Example #1
0
 internal BoxLayoutGroup()
 {
     horizontal     = null;
     layoutPriority = 1;
     parameters     = new BoxLayoutParams();
     vertical       = null;
 }
Example #2
0
        /// <summary>
        /// Calculates the size of the box layout container.
        /// </summary>
        /// <param name="obj">The container to lay out.</param>
        /// <param name="args">The parameters to use for layout.</param>
        /// <param name="direction">The direction which is being calculated.</param>
        /// <returns>The minimum and preferred box layout size.</returns>
        private static BoxLayoutResults Calc(GameObject obj, BoxLayoutParams args,
                                             PanelDirection direction)
        {
            var transform  = obj.AddOrGet <RectTransform>();
            int n          = transform.childCount;
            var result     = new BoxLayoutResults(direction, n);
            var components = ListPool <Component, BoxLayoutGroup> .Allocate();

            for (int i = 0; i < n; i++)
            {
                var child = transform.GetChild(i)?.gameObject;
                if (child != null && child.activeInHierarchy)
                {
                    // Only on active game objects
                    components.Clear();
                    child.GetComponents(components);
                    var hc = PUIUtils.CalcSizes(child, direction, components);
                    if (!hc.ignore)
                    {
                        if (args.Direction == direction)
                        {
                            result.Accum(hc, args.Spacing);
                        }
                        else
                        {
                            result.Expand(hc);
                        }
                        result.children.Add(hc);
                    }
                }
            }
            components.Recycle();
            return(result);
        }
Example #3
0
        /// <summary>
        /// Without adding a BoxLayoutGroup component to the specified object, lays it out
        /// based on its current child and layout element sizes, then updates its preferred
        /// and minimum sizes based on the results. The component will be laid out at a fixed
        /// size equal to its preferred size.
        ///
        /// UI elements should be active before any layouts are added, especially if they are
        /// to be frozen.
        /// </summary>
        /// <param name="obj">The object to lay out immediately.</param>
        /// <param name="parameters">The layout parameters to use.</param>
        /// <param name="size">The minimum component size.</param>
        /// <returns>obj for call chaining</returns>
        public static GameObject LayoutNow(GameObject obj, BoxLayoutParams parameters = null,
                                           Vector2 size = default)
        {
            if (obj == null)
            {
                throw new ArgumentNullException("obj");
            }
            var args   = parameters ?? new BoxLayoutParams();
            var margin = args.Margin ?? new RectOffset();
            // Calculate
            var horizontal = Calc(obj, args, PanelDirection.Horizontal);
            var vertical   = Calc(obj, args, PanelDirection.Vertical);
            // Update or create fixed layout element
            var   layoutElement = obj.AddOrGet <LayoutElement>();
            float hmin = horizontal.total.preferred + margin.left + margin.right,
                  vmin = vertical.total.preferred + margin.top + margin.bottom,
                  hsize = Math.Max(size.x, hmin), vsize = Math.Max(size.y, vmin);

            layoutElement.minWidth        = hsize;
            layoutElement.preferredWidth  = hsize;
            layoutElement.flexibleWidth   = 0.0f;
            layoutElement.minHeight       = vsize;
            layoutElement.preferredHeight = vsize;
            layoutElement.flexibleHeight  = 0.0f;
            // Size the object now
            var rt = obj.rectTransform();

            rt.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, hsize);
            rt.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, vsize);
            // Allocate
            DoLayout(args, horizontal, hsize);
            DoLayout(args, vertical, vsize);
            return(obj);
        }
Example #4
0
        /// <summary>
        /// Lays out components in the box layout container parallel to the layout axis.
        /// </summary>
        /// <param name="required">The calculated minimum and preferred sizes.</param>
        /// <param name="args">The parameters to use for layout.</param>
        /// <param name="status">The current status of layout.</param>
        private static void DoLayoutLinear(BoxLayoutResults required, BoxLayoutParams args,
                                           BoxLayoutStatus status)
        {
            var total      = required.total;
            var components = ListPool <ILayoutController, BoxLayoutGroup> .Allocate();

            var direction = args.Direction;
            // Determine flex size ratio
            float size = status.size, prefRatio = 0.0f, minSize = total.min, prefSize =
                total.preferred, excess = Math.Max(0.0f, size - prefSize), flexTotal = total.
                                                                                       flexible, offset = status.offset, spacing = args.Spacing;

            if (size > minSize && prefSize > minSize)
            {
                // Do not divide by 0
                prefRatio = Math.Min(1.0f, (size - minSize) / (prefSize - minSize));
            }
            if (excess > 0.0f && flexTotal == 0.0f)
            {
                // If no components can be expanded, offset all
                offset += PUIUtils.GetOffset(args.Alignment, status.direction, excess);
            }
            foreach (var child in required.children)
            {
                var obj = child.source;
                // Active objects only
                if (obj != null && obj.activeInHierarchy)
                {
                    float compSize = child.min;
                    if (prefRatio > 0.0f)
                    {
                        compSize += (child.preferred - child.min) * prefRatio;
                    }
                    if (excess > 0.0f && flexTotal > 0.0f)
                    {
                        compSize += excess * child.flexible / flexTotal;
                    }
                    // Place and size component
                    obj.AddOrGet <RectTransform>().SetInsetAndSizeFromParentEdge(status.edge,
                                                                                 offset, compSize);
                    offset += compSize + ((compSize > 0.0f) ? spacing : 0.0f);
                    // Invoke SetLayout on dependents
                    components.Clear();
                    obj.GetComponents(components);
                    foreach (var component in components)
                    {
                        if (direction == PanelDirection.Horizontal)
                        {
                            component.SetLayoutHorizontal();
                        }
                        else                         // if (direction == PanelDirection.Vertical)
                        {
                            component.SetLayoutVertical();
                        }
                    }
                }
            }
            components.Recycle();
        }
Example #5
0
        public static GameObject LayoutNow(GameObject obj, BoxLayoutParams parameters = null,
                                           Vector2 size = default)
        {
            if (obj == null)
            {
                throw new ArgumentNullException("obj");
            }
            var layout = obj.AddOrGet <BoxLayoutGroup>();

            layout.Params = parameters;
            layout.LockLayout();
            obj.SetMinUISize(size);
            return(obj);
        }
Example #6
0
        /// <summary>
        /// Gets the offset required for a component in its box.
        /// </summary>
        /// <param name="args">The parameters to use for layout.</param>
        /// <param name="direction">The direction of layout.</param>
        /// <param name="delta">The remaining space.</param>
        /// <returns>The offset from the edge.</returns>
        private static float GetOffset(BoxLayoutParams args, PanelDirection direction,
                                       float delta)
        {
            float offset = 0.0f;

            // Based on alignment, offset component
            if (direction == PanelDirection.Horizontal)
            {
                switch (args.Alignment)
                {
                case TextAnchor.LowerCenter:
                case TextAnchor.MiddleCenter:
                case TextAnchor.UpperCenter:
                    offset = delta * 0.5f;
                    break;

                case TextAnchor.LowerRight:
                case TextAnchor.MiddleRight:
                case TextAnchor.UpperRight:
                    offset = delta;
                    break;

                default:
                    break;
                }
            }
            else
            {
                switch (args.Alignment)
                {
                case TextAnchor.MiddleLeft:
                case TextAnchor.MiddleCenter:
                case TextAnchor.MiddleRight:
                    offset = delta * 0.5f;
                    break;

                case TextAnchor.LowerLeft:
                case TextAnchor.LowerCenter:
                case TextAnchor.LowerRight:
                    offset = delta;
                    break;

                default:
                    break;
                }
            }
            return(offset);
        }
Example #7
0
        /// <summary>
        /// Lays out components in the box layout container against the layout axis.
        /// </summary>
        /// <param name="required">The calculated minimum and preferred sizes.</param>
        /// <param name="args">The parameters to use for layout.</param>
        /// <param name="status">The current status of layout.</param>
        private static void DoLayoutPerp(LayoutResults required, BoxLayoutParams args,
                                         LayoutStatus status)
        {
            var components = ListPool <ILayoutController, BoxLayoutGroup> .Allocate();

            var   direction = args.Direction;
            float size      = status.size;

            foreach (var child in required.children)
            {
                var obj = child.source;
                // Active objects only
                if (obj != null && obj.activeInHierarchy)
                {
                    float compSize = size;
                    if (child.flexible <= 0.0f)
                    {
                        // Does not expand to all
                        compSize = Math.Min(compSize, child.preferred);
                    }
                    float offset = (size > compSize) ? GetOffset(args, status.direction,
                                                                 size - compSize) : 0.0f;
                    // Place and size component
                    obj.AddOrGet <RectTransform>().SetInsetAndSizeFromParentEdge(status.edge,
                                                                                 offset + status.offset, compSize);
                    // Invoke SetLayout on dependents
                    components.Clear();
                    obj.GetComponents(components);
                    foreach (var component in components)
                    {
                        if (!PUIUtils.IgnoreLayout(component))
                        {
                            if (direction == PanelDirection.Horizontal)
                            {
                                component.SetLayoutVertical();
                            }
                            else                             // if (direction == PanelDirection.Vertical)
                            {
                                component.SetLayoutHorizontal();
                            }
                        }
                    }
                }
            }
            components.Recycle();
        }
Example #8
0
        /// <summary>
        /// Lays out components in the box layout container.
        /// </summary>
        /// <param name="args">The parameters to use for layout.</param>
        /// <param name="required">The calculated minimum and preferred sizes.</param>
        /// <param name="size">The total available size in this dimension.</param>
        private static void DoLayout(BoxLayoutParams args, LayoutResults required, float size)
        {
            if (required == null)
            {
                throw new ArgumentNullException("required");
            }
            var direction = required.direction;
            var status    = new LayoutStatus(direction, args.Margin ?? new RectOffset(), size);

            if (args.Direction == direction)
            {
                DoLayoutLinear(required, args, status);
            }
            else
            {
                DoLayoutPerp(required, args, status);
            }
        }
Example #9
0
        public override GameObject Build()
        {
            var label = PUIElements.CreateUI(null, Name);

            // Background
            if (BackColor.a > 0)
            {
                label.AddComponent <Image>().color = BackColor;
            }
            // Add foreground image
            if (Sprite != null)
            {
                ImageChildHelper(label, this);
            }
            // Add text
            if (!string.IsNullOrEmpty(Text))
            {
                TextChildHelper(label, TextStyle ?? PUITuning.Fonts.UILightStyle, Text);
            }
            // Add tooltip
            if (!string.IsNullOrEmpty(ToolTip))
            {
                label.AddComponent <ToolTip>().toolTip = ToolTip;
            }
            label.SetActive(true);
            // Icon and text are side by side
            var lp = new BoxLayoutParams()
            {
                Spacing   = IconSpacing, Direction = PanelDirection.Horizontal, Margin = Margin,
                Alignment = TextAlignment
            };

            if (DynamicSize)
            {
                label.AddComponent <BoxLayoutGroup>().Params = lp;
            }
            else
            {
                BoxLayoutGroup.LayoutNow(label, lp);
            }
            label.SetFlexUISize(FlexSize);
            InvokeRealize(label);
            return(label);
        }
Example #10
0
        public GameObject Build()
        {
            var panel = PUIElements.CreateUI(Name);

            if (BackColor.a > 0.0f)
            {
                panel.AddComponent <Image>().color = BackColor;
            }
            panel.layer = LayerMask.NameToLayer("UI");
            // Add children
            foreach (var child in children)
            {
                PUIElements.SetParent(child.Build(), panel);
            }
            // Add layout component
            var args = new BoxLayoutParams()
            {
                Direction = Direction, Alignment = Alignment, Spacing = Spacing,
                Margin    = Margin
            };

            // Gotta love freezable layouts
            if (DynamicSize)
            {
                var lg = panel.AddComponent <BoxLayoutGroup>();
                lg.Params         = args;
                lg.flexibleWidth  = FlexSize.x;
                lg.flexibleHeight = FlexSize.y;
            }
            else
            {
                BoxLayoutGroup.LayoutNow(panel, args).SetFlexUISize(FlexSize);
            }
            OnRealize?.Invoke(panel);
            return(panel);
        }
Example #11
0
        public GameObject Build()
        {
            var textField = PUIElements.CreateUI(Name);
            // Background
            var style = TextStyle ?? PUITuning.Fonts.TextLightStyle;

            textField.AddComponent <Image>().color = style.textColor;
            // Text box with rectangular clipping area
            var textArea = PUIElements.CreateUI("Text Area", true, false);

            textArea.AddComponent <Image>().color = BackColor;
            PUIElements.SetParent(textArea, textField);
            var mask = textArea.AddComponent <RectMask2D>();
            // Scrollable text
            var textBox = PUIElements.CreateUI("Text");

            PUIElements.SetParent(textBox, textArea);
            // Text to display
            var textDisplay = textBox.AddComponent <TextMeshProUGUI>();

            textDisplay.alignment             = TextAlignment;
            textDisplay.autoSizeTextContainer = false;
            textDisplay.enabled         = true;
            textDisplay.color           = style.textColor;
            textDisplay.font            = style.sdfFont;
            textDisplay.fontSize        = style.fontSize;
            textDisplay.fontStyle       = style.style;
            textDisplay.maxVisibleLines = 1;
            // Text field itself
            var onChange = OnTextChanged;

            textField.SetActive(false);
            var textEntry = textField.AddComponent <TMP_InputField>();

            textEntry.textComponent = textDisplay;
            textEntry.textViewport  = textArea.rectTransform();
            textField.SetActive(true);
            textEntry.text   = Text ?? "";
            textDisplay.text = Text ?? "";
            ConfigureTextEntry(textEntry).onDeselect.AddListener((text) => {
                onChange?.Invoke(textField, (text ?? "").TrimEnd());
            });
            // Add tooltip
            if (!string.IsNullOrEmpty(ToolTip))
            {
                textField.AddComponent <ToolTip>().toolTip = ToolTip;
            }
            mask.enabled = true;
            // Lay out - TMP_InputField does not support auto layout so we have to do a hack
            var minSize = new Vector2(MinWidth, LayoutUtility.GetPreferredHeight(textBox.
                                                                                 rectTransform()));

            textArea.SetMinUISize(minSize).SetFlexUISize(Vector2.one);
            var lp = new BoxLayoutParams()
            {
                Direction = PanelDirection.Horizontal, Alignment = TextAnchor.MiddleLeft,
                Margin    = new RectOffset(1, 1, 1, 1)
            };

            if (DynamicSize)
            {
                var layout = textField.AddComponent <BoxLayoutGroup>();
                layout.Params         = lp;
                layout.flexibleWidth  = FlexSize.x;
                layout.flexibleHeight = FlexSize.y;
                textField.SetMinUISize(minSize);
            }
            else
            {
                BoxLayoutGroup.LayoutNow(textField, lp, new Vector2(MinWidth, 0.0f)).
                SetFlexUISize(FlexSize);
            }
            OnRealize?.Invoke(textField);
            return(textField);
        }
Example #12
0
        public override GameObject Build()
        {
            var button = PUIElements.CreateUI(null, Name);
            // Background
            var kImage    = button.AddComponent <KImage>();
            var trueColor = Color ?? PUITuning.Colors.ButtonPinkStyle;

            kImage.colorStyleSetting = trueColor;
            kImage.color             = trueColor.inactiveColor;
            kImage.sprite            = PUITuning.Images.ButtonBorder;
            kImage.type = Image.Type.Sliced;
            // Set on click event
            var kButton = button.AddComponent <KButton>();
            var evt     = OnClick;

            if (evt != null)
            {
                kButton.onClick += () => {
                    evt?.Invoke(button);
                }
            }
            ;
            kButton.additionalKImages = new KImage[0];
            kButton.soundPlayer       = PUITuning.ButtonSounds;
            kButton.bgImage           = kImage;
            // Add foreground image since the background already has one
            if (Sprite != null)
            {
                kButton.fgImage = ImageChildHelper(button, this);
            }
            // Set colors
            kButton.colorStyleSetting = trueColor;
            // Add text
            if (!string.IsNullOrEmpty(Text))
            {
                TextChildHelper(button, TextStyle ?? PUITuning.Fonts.UILightStyle, Text);
            }
            // Add tooltip
            if (!string.IsNullOrEmpty(ToolTip))
            {
                button.AddComponent <ToolTip>().toolTip = ToolTip;
            }
            button.SetActive(true);
            // Icon and text are side by side
            var lp = new BoxLayoutParams()
            {
                Spacing   = IconSpacing, Direction = PanelDirection.Horizontal, Margin = Margin,
                Alignment = TextAlignment
            };

            if (DynamicSize)
            {
                button.AddComponent <BoxLayoutGroup>().Params = lp;
            }
            else
            {
                BoxLayoutGroup.LayoutNow(button, lp);
            }
            button.SetFlexUISize(FlexSize);
            InvokeRealize(button);
            return(button);
        }
Example #13
0
        public override GameObject Build()
        {
            var checkbox = PUIElements.CreateUI(null, Name);
            // Background
            var trueColor = CheckColor ?? PUITuning.Colors.ComponentLightStyle;
            // Checkbox background
            var checkBack = PUIElements.CreateUI(checkbox, "CheckBox");

            checkBack.AddComponent <Image>().color = BackColor;
            // Checkbox border
            var checkBorder = PUIElements.CreateUI(checkBack, "CheckBorder");
            var borderImg   = checkBorder.AddComponent <Image>();

            borderImg.sprite = PUITuning.Images.CheckBorder;
            borderImg.color  = trueColor.activeColor;
            borderImg.type   = Image.Type.Sliced;
            // Checkbox foreground
            var imageChild = PUIElements.CreateUI(checkBorder, "CheckMark", true, PUIAnchoring.
                                                  Center, PUIAnchoring.Center);
            var img = imageChild.AddComponent <Image>();

            img.sprite         = PUITuning.Images.Checked;
            img.preserveAspect = true;
            // Determine the checkbox size
            var actualSize = CheckSize;

            if (actualSize.x <= 0.0f || actualSize.y <= 0.0f)
            {
                var rt = imageChild.rectTransform();
                actualSize.x = LayoutUtility.GetPreferredWidth(rt);
                actualSize.y = LayoutUtility.GetPreferredHeight(rt);
            }
            imageChild.SetUISize(CheckSize, false);
            // Add foreground image since the background already has one
            if (Sprite != null)
            {
                ImageChildHelper(checkbox, this);
            }
            // Add text
            if (!string.IsNullOrEmpty(Text))
            {
                TextChildHelper(checkbox, TextStyle ?? PUITuning.Fonts.UILightStyle, Text);
            }
            // Add tooltip
            if (!string.IsNullOrEmpty(ToolTip))
            {
                checkbox.AddComponent <ToolTip>().toolTip = ToolTip;
            }
            // Toggle
            var mToggle = checkbox.AddComponent <MultiToggle>();
            var evt     = OnChecked;

            if (evt != null)
            {
                mToggle.onClick += () => {
                    evt?.Invoke(checkbox, mToggle.CurrentState);
                }
            }
            ;
            mToggle.play_sound_on_click   = true;
            mToggle.play_sound_on_release = false;
            mToggle.states       = GenerateStates(trueColor);
            mToggle.toggle_image = img;
            mToggle.ChangeState(InitialState);
            checkbox.SetActive(true);
            // Lay out the checkbox using anchors only
            checkBack.SetUISize(new Vector2(CheckSize.x + 2.0f * CHECKBOX_MARGIN, CheckSize.y +
                                            2.0f * CHECKBOX_MARGIN), true);
            imageChild.SetUISize(CheckSize);
            // Icon and text are side by side
            var lp = new BoxLayoutParams()
            {
                Margin = Margin, Spacing = Math.Max(IconSpacing, 0), Alignment = TextAnchor.
                                                                                 MiddleLeft
            };

            if (DynamicSize)
            {
                checkbox.AddComponent <BoxLayoutGroup>().Params = lp;
            }
            else
            {
                BoxLayoutGroup.LayoutNow(checkbox, lp);
            }
            checkbox.SetFlexUISize(FlexSize);
            InvokeRealize(checkbox);
            return(checkbox);
        }
Example #14
0
        public override GameObject Build()
        {
            var checkbox = PUIElements.CreateUI(Name);
            // Background
            var trueColor = CheckColor ?? PUITuning.Colors.CheckboxWhiteStyle;
            // Checkbox background
            var checkBack = PUIElements.CreateUI("CheckBox");

            checkBack.AddComponent <Image>().color = BackColor;
            PUIElements.SetParent(checkBack, checkbox);
            // Checkbox border
            var checkBorder = PUIElements.CreateUI("CheckBorder");
            var borderImg   = checkBorder.AddComponent <Image>();

            borderImg.sprite = PUITuning.Images.CheckBorder;
            borderImg.color  = trueColor.activeColor;
            borderImg.type   = Image.Type.Sliced;
            PUIElements.SetParent(checkBorder, checkBack);
            // Checkbox foreground
            var imageChild = PUIElements.CreateUI("CheckMark");
            var img        = imageChild.AddComponent <Image>();

            PUIElements.SetParent(imageChild, checkBorder);
            img.sprite         = PUITuning.Images.Checked;
            img.preserveAspect = true;
            // Limit size if needed
            if (CheckSize.x > 0.0f && CheckSize.y > 0.0f)
            {
                PUIElements.SetSizeImmediate(imageChild, CheckSize);
            }
            else
            {
                PUIElements.AddSizeFitter(imageChild, false);
            }
            // Add foreground image since the background already has one
            if (Sprite != null)
            {
                ImageChildHelper(checkbox, Sprite, SpriteTransform, SpriteSize);
            }
            // Add text
            if (!string.IsNullOrEmpty(Text))
            {
                TextChildHelper(checkbox, TextStyle ?? PUITuning.Fonts.UILightStyle, Text);
            }
            // Add tooltip
            if (!string.IsNullOrEmpty(ToolTip))
            {
                checkbox.AddComponent <ToolTip>().toolTip = ToolTip;
            }
            // Toggle
            var kButton = checkbox.AddComponent <MultiToggle>();
            var evt     = OnChecked;

            if (evt != null)
            {
                kButton.onClick += () => {
                    evt?.Invoke(checkbox, kButton.CurrentState);
                }
            }
            ;
            kButton.play_sound_on_click   = true;
            kButton.play_sound_on_release = false;
            kButton.states       = GenerateStates(trueColor, borderImg);
            kButton.toggle_image = img;
            kButton.ChangeState(InitialState);
            checkbox.SetActive(true);
            BoxLayoutGroup.LayoutNow(checkBorder, new BoxLayoutParams()
            {
                Margin = CHECKBOX_MARGIN
            });
            BoxLayoutGroup.LayoutNow(checkBack);
            // Icon and text are side by side
            var lp = new BoxLayoutParams()
            {
                Margin = Margin, Spacing = Math.Max(IconSpacing, 0), Alignment = TextAnchor.
                                                                                 MiddleLeft
            };

            if (DynamicSize)
            {
                checkbox.AddComponent <BoxLayoutGroup>().Params = lp;
            }
            else
            {
                BoxLayoutGroup.LayoutNow(checkbox, lp);
            }
            PUIElements.AddSizeFitter(checkbox, DynamicSize).SetFlexUISize(FlexSize);
            InvokeRealize(checkbox);
            return(checkbox);
        }