Bootstrap Chameleon Logo

Directional Layout

Vertical Layout SVerticalBox, Horizontal Layout SHorizontalBox

SVerticalBox and SHorizontalBox are the two most commonly used widget layouts. They enable child widgets and layouts to be arranged sequentially, and can be found in almost every tool. For example, the following image uses nested SVerticalBox and SHorizontalBox. For a specific video, see here.

Buttons arranged in a Fibonacci layout

This article will clarify the layout and alignment logic of SVerticalBox and SHorizontalBox, involving keywords:

  • Slots
  • AutoWidth, AutoHeight
  • HAlign, VAlign

Rules

Rule 1. By default, each child widget will occupy as much available space as possible

In the following image, when there is only one widget (SBorder), it will fill the entire window

"Root":{
    "SBorder": {
        "BorderImage": { "Style": "FEditorStyle", "Brush": "DashedBorder"},
        "BorderBackgroundColor": [0, 1, 0, 1]
    }
}

Occupying all the space:

One widget filling the tab

When replaced with SButton, a large button filling the entire window is created:

One Button filling the tab

Rule 2. Child widgets in the same layout will equally share all available space

"Root":{
    "SHorizontalBox":
    {
        "Slots": [
            {
                "SBorder": { "BorderBackgroundColor": [0, 1, 0, 1], "BorderImage": { "Style": "FEditorStyle", "Brush": "DashedBorder"}}
            },
            {
                "SBorder": { "BorderBackgroundColor": [1, 1, 1, 1], "BorderImage": { "Style": "FEditorStyle", "Brush": "DashedBorder"}}
            }
        ]
    }
}

Both sides are equally divided:

Two buttons sharing the tab space

Rule 3. When AutoWidth or AutoHeight is set for a Slot, Rule 2 is invalidated, and the child widget will only occupy the space it needs

In the following image, the left green box has no other widgets inside, so the size it needs is just the 4 pixels occupied by its own border

"Root":{
    "SHorizontalBox":
    {
        "Slots": [
            {
                "AutoWidth": true,
                "SBorder": { "BorderBackgroundColor": [0, 1, 0, 1], "BorderImage": { "Style": "FEditorStyle", "Brush": "DashedBorder"}}
            },
            {
                "SBorder": { "BorderBackgroundColor": [1, 1, 1, 1], "BorderImage": { "Style": "FEditorStyle", "Brush": "DashedBorder"}}
            }
        ]
    }
}

Left side has AutoWidth, but no content:

The left widget taking no space as it contains no child widget

If an SButton is placed inside the first SBorder, the size needed by the SBorder is only the size needed by the SButton

"Root":{
    "SHorizontalBox":
    {
        "Slots": [
            {
                "AutoWidth": true,
                "SBorder": { "BorderBackgroundColor": [0, 1, 0, 1], "BorderImage": { "Style": "FEditorStyle", "Brush": "DashedBorder"},
                    "Content": {
                        "SButton": { "Text": "PlaceHolder Button" }
                    }
                }
            },
            {
                "SBorder": { "BorderBackgroundColor": [1, 1, 1, 1], "BorderImage": { "Style": "FEditorStyle", "Brush": "DashedBorder"}}
            }
        ]
    }
}

The left side has AutoWidth, and an SButton is placed inside:

The left widget fitting to the button's size

Rule 4. 4. When setting HAlign or VAlign, AutoWidth or AutoHeight cannot be used simultaneously

In the following image, the right white box is set to align to the right. At this point, AutoWidth cannot be used. The widget will align to the right and only occupy the space it needs

"Root":
{
    "SHorizontalBox":
    {
        "Slots": [
            {
                "AutoWidth": true,
                "SBorder": { "BorderBackgroundColor": [0, 1, 0, 1], "BorderImage": { "Style": "FEditorStyle", "Brush": "DashedBorder"},
                    "Content": {
                        "SButton": { "Text": "PlaceHolder Button" }
                    }
                }
            },
            {
                "HAlign": "Right",
                "SBorder": { "BorderBackgroundColor": [1, 1, 1, 1], "BorderImage": { "Style": "FEditorStyle", "Brush": "DashedBorder"}}
            }
        ]
    }
}

Left side has AutoWidth with an SButton inside

one_button_fill

Note

  • AutoWidth is an attribute of SHorizontalBox Slots, while AutoHeight is an attribute of SVerticalBox Slots. They can only be specified in their respective Slots and will not appear together
  • Pay attention to the Output window's tips. When a property name is written incorrectly, or a non-existent property is used, there will be a warning message

Warning message in Output window for 'no supported key names' in json file

The "MinDesiredWidth" and "MinDesiredHeight" in SBox need a parent component with layout functionality like SVerticalBox or SHorizontalBox to work

Practical Use

In practical use, you usually place multiple SHorizontalBox, SScrollBox within an SVerticalBox, and inside each child layout widget, there are other SVerticalBox, SHorizontalBox and so on.

For example, the interface below uses SBorder to simulate a simple interface layout:

  • The green box can be a title
  • The purple box is an option button, always aligned to the right
  • The yellow area below is for status bars, etc.

Note

  • Here, several "SBorders" are used to simulate the space occupied by subspaces. In actual use, there is usually only one "Border" in a tool interface
  • Here, "Padding": 16 is used to make the "SBorder" widget occupy an extra 16 pixels on all sides, simulating the space occupied by child widgets
  • In reality, "Padding" is used to control the spacing between widgets, for a more detailed explanation see here: Padding and Margin

Complex layout example with 'Aligns' and 'Auto Height/Width'

{
    "TabLabel": "Chameleon Sketch",
    "InitTabSize": [400, 240],
    "InitTabPosition": [120, -500],
    "InitPyCmd": "import ChameleonSketch; sketch = ChameleonSketch.ChameleonSketch.ChameleonSketch(%JsonPath)",
    "Root":{
        "SVerticalBox":
        {
            "Slots": [
                {
                    "AutoHeight": true,
                    "SHorizontalBox":
                    {
                        "Slots": [
                            {
                                "SBorder": {
                                    "BorderBackgroundColor": [0, 1, 0, 1], "BorderImage": { "Style": "FEditorStyle", "Brush": "DashedBorder"},
                                    "Padding": 16
                                }
                            },
                            {
                                "HAlign": "Right",
                                "SBorder": {
                                    "BorderBackgroundColor": [1, 0, 1, 1], "BorderImage": { "Style": "FEditorStyle", "Brush": "DashedBorder"},
                                    "Padding": 16
                                }
                            }
                        ]
                    }
                },
                {
                    "AutoHeight": true,
                    "SHorizontalBox":
                    {
                        "Slots": [
                            {
                                "AutoWidth": true,
                                "SBorder": {
                                    "BorderBackgroundColor": [1, 1, 1, 1], "BorderImage": { "Style": "FEditorStyle", "Brush": "DashedBorder"},
                                    "Padding": [120, 60]
                                }
                            },
                            {
                                "SVerticalBox":
                                {
                                    "Slots": [
                                        {
                                            "SBorder": { "BorderBackgroundColor": [0, 0.5, 1, 1], "BorderImage": { "Style": "FEditorStyle", "Brush": "DashedBorder"}}
                                        },
                                        {

                                            "SBorder": { "BorderBackgroundColor": [0, 0.5, 1, 1], "BorderImage": { "Style": "FEditorStyle", "Brush": "DashedBorder"}}
                                        },
                                        {
                                            "SBorder": { "BorderBackgroundColor": [0, 0.5, 1, 1], "BorderImage": { "Style": "FEditorStyle", "Brush": "DashedBorder"}}
                                        }
                                    ]
                                }
                            }
                        ]
                    }
                },
                {
                    "VAlign": "Bottom",
                    "SBorder": {
                        "BorderBackgroundColor": [1, 1, 0, 1], "BorderImage": { "Style": "FEditorStyle", "Brush": "DashedBorder"},
                        "Padding": 10
                    }
                }
            ]
        }
    }
}