Alchemy icon indicating copy to clipboard operation
Alchemy copied to clipboard

HorizontalGroupとLabelTextを組み合わせると、左側の文字の幅が無視されて見えなくなってしまう

Open KurisuJuha opened this issue 2 years ago • 6 comments

なるときとならないときがある 下はLowとHighと書いてますが、High部分が消えている例です。 image

KurisuJuha avatar Dec 12 '23 14:12 KurisuJuha

この不具合はおそらくUnityのPropertyFieldクラスの挙動に起因するものであり、ライブラリ側から修正することはやや難しいと思われます。 対処法として、#34 で追加されたLabelWidth属性を使用してラベルの横幅を上書きすることが可能です。こちらを使用して適切な横幅を設定してみてください。

nuskey8 avatar Feb 17 '24 13:02 nuskey8

3回スケジュールすることで対応できました。

[CustomGroupDrawer(typeof(HorizontalGroupAttribute))]
public sealed class HorizontalGroupDrawer : AlchemyGroupDrawer
{
  
    public override VisualElement CreateRootElement(string label)
    {
        var root = new VisualElement()
        {
            style =
            {
                width = Length.Percent(100f),
                flexDirection = FlexDirection.Row
            }
        };

        static void AdjustLabel(PropertyField element, VisualElement inspector, int childCount)
        {
            if (element.childCount == 0) return;
            if (element.Q<Foldout>() != null) return;

            var field = element[0];
            field.RemoveFromClassList("unity-base-field__aligned");

            var labelElement = field.Q<Label>();
            if (labelElement != null)
            {
                labelElement.style.minWidth = 0f;
                labelElement.style.width = GUIHelper.CalculateLabelWidth(element, inspector) * 0.8f / childCount;
            }
        }

        var count = 2;
        root.schedule.Execute(() =>
        {
            count--;
            if (root.childCount <= 1) return;

            var visualTree = root.panel.visualTree;

            foreach (var field in root.Query<PropertyField>().Build())
            {
                AdjustLabel(field, visualTree, root.childCount);
            }

            foreach (var field in root.Query<GenericField>().Children<PropertyField>().Build())
            {
                AdjustLabel(field, visualTree, root.childCount);
            }
        }).Until(() => count <= 0);

        return root;
    }
}

[CustomAttributeDrawer(typeof(LabelWidthAttribute))]
public sealed class LabelWidthDrawer : AlchemyAttributeDrawer
{
    public override void OnCreateElement()
    {
        var width = ((LabelWidthAttribute)Attribute).Width;

        if (Element is AlchemyPropertyField field && field.FieldElement is PropertyField)
        {
            var executed = false;
            field.schedule.Execute(() =>
            {
                var label = field.Q<Label>();
                if (label == null) return;
                if(label.style.minWidth.value == width&& label.style.width.value == width)
                {
                    executed = true;
                    return;
                }
                GUIHelper.SetMinAndCurrentWidth(label, width);
                
            }).Until(() => executed);
            
            return;
        }

        Debug.LogWarning("The LabelWidth attribute currently only supports PropertyField and is ignored for other visual elements.");
    }
}

akeit0 avatar Feb 17 '24 15:02 akeit0

PropertyFieldはBind時に内部のコンポーネントを構築するため、scheduleによる複数回の遅延実行は対処として不確実です。また、HorizontalGroup内で独自のレイアウト処理をスケジュールすることはかなり強引な処理であり、将来的に他の問題を引き起こす可能性があります。(現在のLabelWidthDrawerも同様の問題を抱えているため、適用対象を特定のPropertyFieldに限定しています。)

nuskey8 avatar Feb 17 '24 23:02 nuskey8

LabelWidthを設定してもAdd/RemoveComponent時に同様の問題が発生するので、Until(() => false)のようにするしかないかもしれないですね、、

akeit0 avatar Feb 18 '24 01:02 akeit0

PropertyFieldからLabelを取り出してしまう方法でもうまくいきました。

//AlchemyPropertyFieldに以下を追加し、widthをいじるときはこれを用いる。
Label labelElement;
public Label OverridenLabel
{
    get
    {
        if (labelElement != null) return labelElement;
        var label = element.Q<Label>();
        if (label == null)
        {
            return null;
        }
        labelElement = new Label(label.text);
        
        var labelStyle = labelElement.style;
        labelStyle.marginTop = 3f;
        labelStyle.marginBottom = 3f;
        labelStyle.marginLeft = 3f;
        labelStyle.marginRight = 3f;
        label.RemoveFromHierarchy();

        style.flexDirection = FlexDirection.Row;
        Insert(0, labelElement);
        element.style.flexGrow = 1;
        return labelElement;
    }
}

akeit0 avatar Feb 18 '24 07:02 akeit0

Unfortunatly, (though I haven't checked) I think that'd break properties that use the label as a handle. I've managed to stabilize things with this.

        public override VisualElement CreateRootElement(string label)
        {
            var root = new VisualElement();
            
            root.styleSheets.Add(_styleSheet);
            root.AddToClassList("horizontal-group__main-element");

            static void AdjustLabel(PropertyField element, VisualElement inspector, int childCount)
            {
                if (element.childCount == 0) return;
                if (element.Q<Foldout>() != null) return;
              
                var field = element[0];
                field.RemoveFromClassList("unity-base-field__aligned");
                
                var labelElement = field.Q<Label>();
                if (labelElement != null && !labelElement.ClassListContains("horizontal-group__property-field__label"))
                {
                    labelElement.AddToClassList("horizontal-group__property-field__label");
                    labelElement.RegisterCallback<GeometryChangedEvent>(_ =>
                    {
                        //I'd like to use stylesheets here, but it seems values are set inline somewhere.
                        //Therefore we too must inline, aggressively.
                        labelElement.style.minWidth = new Length(50, LengthUnit.Percent);
                        labelElement.style.maxWidth = new Length(50, LengthUnit.Percent);
                        labelElement.style.width = GUIHelper.CalculateLabelWidth(element, inspector) * 0.8f / childCount;
                    });
                }
            }
            
            root.RegisterCallback<GeometryChangedEvent>(_ =>
            {
                if (root.childCount <= 1) return;

                var visualTree = root.panel.visualTree;
                
                foreach (var field in root.Query<PropertyField>().Build())
                {
                    AdjustLabel(field, visualTree, root.childCount);
                }
                foreach (var field in root.Query<GenericField>().Children<PropertyField>().Build())
                {
                    AdjustLabel(field, visualTree, root.childCount);
                }
            });
            
            return root;
        }

Though its quite heavy handed. I'm gonna keep on trying.

Level6Rogue avatar Aug 14 '24 15:08 Level6Rogue