Table of Contents
Introduction
In our last post we discussed Control Nodes and took a look at the ability to position and size them using the Anchor system. We'll be looking at the Container Nodes and the various types that arrange the children nodes and dictate their sizing and position based on a set of rules.
Using Control and Container Nodes, we can layout our User Interface to meet the needs of our games, by presenting information clearly to our users. We can use these nodes to give us ultimate control over how we structure our views, and compose scenes together.
For example, we may want to present our characters stats; a hot bar for our active items, alerts for things such as leveling up, and prompts for dialogue and other information.
These elements can be broken up into components that are composed of Control and Container Nodes, which do most of the heavy lifting.
Video
Containers
Container
extend the Control
class, which means they also have the ability to position and size using the Anchors system, which we covered previously.
When using a Container
node you have more capability to layout your children nodes based on a set of rules, based on which Container
node you choose.
All of the child Control
nodes within a Container
will have their size and positions automatically updated, which means manually setting the position and size of those children may cause them to be changed when the parent Container
is updated. This behavior allows children to rely on their sizing and positions updating whenever the parent resizes.
This is where the use of a Control
's Sizing Options comes into play. Whenever a Control
is a child of a Container
node, it will have the ability to define it's own Container Sizing Options. These are defined per axis (horizontal and vertical), and their behaviors are implemented per Container
type.
- FILL
- Forces the
Control
to fill the entire area within the container. This is on by default.
- Forces the
- EXPAND
- Expand along the axis to take up as much space is available by the parent node. This will push non expanding siblings away. Expanding
Control
nodes will determine the amount of space they consume based on theStretch Ratio
. This option is only available with certainContainer
Types
- Expand along the axis to take up as much space is available by the parent node. This will push non expanding siblings away. Expanding
- SHRINK_BEGIN
- Positions itself along the start of the axis, while taking as little space in that direction.
- SHRINK_CENTER
- Positions itself along the center of the axis, while taking as little space in that direction.
- SHRINK_END
- Positions itself along the end of the axis, while taking as little space in that direction.
The Stretch Ratio
determines how much expanded Control
nodes will take up relative to one another. The percentage this node will stretch is a ratio of ratio / sum of expanded ratios
. For example a HboxContainer
with 3 children each with a ratio of 1 will take up equal amount of space 1 / 3
, but if the first element has a ratio of 2 it will take up 2x the space as the others, or 2 / 4
. If we instead set the first element to a ratio of .5
it would take up half of the space as the others, or .5 / 2.5
.
Let's discuss each container type and the way they arrange the children within them.
You can also check out Godot's tutorial on UI and Containers over on their manual.
Panel Container
The PanelContainer
keeps of the children Control
nodes within the area of the defined StyleBox
. The Control
's mouse_filter
property is also automatically set to MouseFilter.MOUSE_FILTER_STOP
by default. This allows the PanelContainer
to receive input events and respond to them accordingly.
To modify the style of the PanelContainer
you can override the Theme's panel
property to a custom StyleBox
of your own design.
Margin Container
The MarginContainer
keeps all children within the bounds of the Container
and allows you to customize the additional margin along each edge.
These margins are controlled via the margin_top
, margin_left
, margin_bottom
, and margin_right
properties from the Theme. You can override these individually to determine how much of a margin to add for that specific direction. These are each set to 0 by default.
Aspect Ratio Container
Aspect Ratio Containers on the Godot's Official Documentation.
The Aspect Ratio Container
controls the child Control
nodes to preserve the proportions whenever it is resized and ensures the aspect ratio is retained.
It can have it's alignment set per axis (alignment_horizontal
, alignment_vertical
) with a corresponding AlignmentMode
:
ALIGNMENT_BEGIN
- Aligns children to the start of the axis.
ALIGNMENT_CENTER
- Aligns children to the center of the axis.
ALIGNMENT_END
- Aligns children to the end of the axis.
You can control the aspect ratio with the ratio
property. This ratio works with the stretch_mode
and is width / height
.
The various stretch modes control how to size the children using the stretch_mode
property.
STRETCH_WIDTH_CONTROLS_HEIGHT
- Children height is adjusted based on the container's width.
STRETCH_WIDTH_CONTROLS_WIDTH
- Children width is adjusted based on the container's height.
STRETCH_FIT
- Children bounds are adjusted to fit inside the container while retaining the aspect ratio.
STRETCH_COVER
- Children width and height are adjusted to cover the container while retaining the aspect ratio.
Box Container
The Box Container
types will arrange the children Control
nodes along the axis they are based on. These are popular for creating a stack or list of elements that you can control the spacing and direction of.
You can set the alignment
mode for the given axis with AlignmentMode
:
ALIGNMENT_BEGIN
- Aligns children to the start of the axis.
ALIGNMENT_CENTER
- Aligns children to the center of the axis.
ALIGNMENT_END
- Aligns children to the end of the axis.`
The vertical
bool property of a box container determines if the children should flow along the vertical axis.
The add_spacer(begin: bool)
method adds a spacer Control
child element and accepts a boolean parameter to determine if it should add the spacer before all the children nodes.
For Theme Properties you can override the separation
which determines the space between in elements, in pixels. This is 4 by default.
HBox Container
The HBoxContainer
node extends the BoxContainer
and will arrange the children along the horizontal axis, and expands the children to fill the vertical space of the Container
. This set's the vertical
property to false and cannot be modified.
VBoxContainer
The VBoxContainer
node extends the BoxContainer
and will arrange the children along the vertical axis, and expands the children to fill the horizontal space of the Container
. This set's the vertical
property to true and cannot be modified.
Center Container
The CenterContainer
will keep all of it's children nodes at it's center using their minimum size. This ensures all children are always aligned to the center, without requiring manually setting their positions.
You can set the use_top_left
property to center the children relative to the top left corner of this Container
. This is false by default.
Flow Container
The FlowContainer
will arrange all of the children along the respective axis. When it runs out of space along that axis, it will wrap the children to the next line or column (depending on the axis).
Similar to the BoxContainer
you can set the alignment
mode for the given axis with AlignmentMode
:
ALIGNMENT_BEGIN
- Aligns children to the start of the axis.
ALIGNMENT_CENTER
- Aligns children to the center of the axis.
ALIGNMENT_END
- Aligns children to the end of the axis
The vertical
bool property of the Container
determines if the children should flow along the vertical axis.
The get_line_count()
method will return number of lines this Container
has for the children based on how many times it's wrapped around the axis bounds.
The Theme Properties h_separation
and v_separation
can overridden to determine the amount of separation along the axis for the children nodes.
HFlow Container
The HFlowContainer
node extends the FlowContainer
and will arrange children along the horizontal axis. This will wrap children to a new line if the width of the bounds is exceeded by a child element. This will set the vertical
property to false and cannot be modified.
VFlow Container
The VFlowContainer
node extends the FlowContainer
and will arrange children along the vertical axis. This will wrap children to a new column if the height of the bounds is exceeded by a child element. This will set the vertical
property to true and cannot be modified.
Grid Container
A GridContainer
node arranges it's children along a grid layout. The number of columns
can be defined and has a default of 1.
The number of rows within the grid will be determined based on the number of children within the Container
. This can be calculated as floor(count of children / columns)
.
The Theme Properties h_separation
and v_separation
can overridden to determine the amount of separation along the axis for the children nodes.
Scroll Container
The ScrollContainer
should only contain a single Control
child. When the child is larger than the Container
, it will add scroll bars around the node to allow scrolling along the desired axis. Scroll bars will be added based on the Control.custom_minimum_size
for the child within the Container
.
The Control.clip_contents
property will clip the content of the child if it's size exceeds the Container
bounds. This is set to true by default.
The follow_focus
property will automatically scroll to focused children to make them fully visible. This is set to false by default.
The scroll mode can be defined for each axis using horizontal_scroll_mode
and vertical_scroll_mode
. ScrollMode
SCROLL_MODE_DISABLED
- Scrolling will be disabled for this axis
SCROLL_MODE_AUTO
- Scrolling is enabled, the scrollbar will only be shown if needed.
SCROLL_MODE_SHOW_ALWAYS
- Scrolling is enabled, the scrollbar is always shown.
SCROLL_MODE_SHOW_NEVER
- Scrolling is enabled, the scrollbar is always hidden.
The scroll_deadzone
property controls the deadzone for touch scrolling. A lower deadzone results in more sensitive scrolling.
The scroll_horizontal
and scroll_vertical
properties allows you to control how far into scrolling you are.
NOTE: Setting this within
_ready
will require a deferred call as the scrollbar'sRange.max_value
has not been initialized yet.
func _ready():
set_deferred("scroll_horizontal", 600)
The scroll_horizontal_custom_step
and scroll_vertical_custom_step
properties control the ScrollBar.custom_step
property for when clicking the internal scrollbar's increment and decrement buttons, or when using arrow keys to control the ScrollBar
.
The ensure_control_visible(Control: control)
method allows you to ensure the given control
is visible. The control
must be a direct or indirect child of the ScrollContainer
, as it is used by the follow_focus
.
NOTE: If the node was just added during the same frame, you must wait until the next frame using the
SceneTree.process_frame
.
add_child(child_node)
await get_tree().process_frame
ensure_control_visible(child_node)
The get_h_scroll_bar()
and get_v_scroll_bar()
methods will return the respective HScrollBar
and VScrollBar
for this ScrollContainer
.
WARNING: The
ScrollBar
are internal and required, if you attempt to remove or free them, it may cause a crash. If you want to disable or hide a scrollbar, you can usehorizontal_scroll_mode
.
The Theme Properties panel
may be overridden to give the ScrollContainer
's background some custom styling with a StyleBox
.
Split Container
The SplitContainer
takes two child nodes and places them side by side with a dragger. This respects both horizontal and vertical flags, as well as ratio. You can drag the dragger to change the size ratio between each child.
The dragged
signal is emitted when the dragger is dragged by the user.
The collapsed
property controls if the first Control
will be collapsed and the dragger disabled.
The dragger_visibility
property determines the dragger's visibility based on the DraggerVisbility
enumerator:
DRAGGER_VISIBLE
- The split dragger is visible when the cursor hovers it.
DRAGGER_HIDDEN
- The split dragger is never visible.
DRAGGER_HIDDEN_COLLAPSED
- The split dragger is never visible and it's space is collapsed.
The split_offset
property is the initial offset of the splitting between the two Control
nodes. with 0
being at the end of the first Control
.
The vertical
property determines if the SplitContainer
will arrange it's children vertically, rather than horizontally.
The clamp_split_offset()
method will clamp the split_offset
value to not go outside the currently possible minimal and maximal values.
The Theme Properties you can override are:
autohide
- If
true
the grabber will hide automatically when it isn't under the cursor. Iffalse
, it's always visible.
- If
minimum_grab_thickness
- The minimum thickness of the areas users can click on to grab the splitting line. If
separation
orh_grabber
/v_grabber
thickness are too small, this ensures that the splitting line can still be dragged.
- The minimum thickness of the areas users can click on to grab the splitting line. If
separation
- The space between sides of the
Container
- The space between sides of the
grabber
Texture2D
Icon used for the grabber drawn in the middle area.
h_grabber
Texture2D
Icon used for the grabber drawn in the middle area whenvertical
isfalse
v_grabber
Texture2D
Icon used for the grabber drawn in the middle area whenvertical
istrue
HSplit Container
The HSplitContainer
node extends the SplitContainer
and will split two child Control
nodes along the horizontal axis, and provides a grabber for adjusting the split ratio. This set's the vertical
property to false and cannot be modified.
VSplit Container
The VSplitContainer
node extends the SplitContainer
and will split two child Control
nodes along the vertical axis, and provides a grabber for adjusting the split ratio. This set's the vertical
property to true and cannot be modified.
SubViewport Container
SubViewport Containers on the Godot's Official Documentation.
The SubViewportContainer
is a special Control
that will only accept a single SubViewport
node as a child, and displays it as if it was an image. It's size is based on the SubViewport
size unless it's stretch is enabled.
Note: Changing a SubViewportContainer
's Control.scale
will cause its contents to appear distorted. To change its visual size without causing distortion, adjust the node's margins instead (if it's not already in a container).
Note: The SubViewportContainer
forwards mouse-enter and mouse-exit notifications to its sub-viewports.
The focus_mode
property overrides the Control
's focus mode
and defaults it to 1.
The stretch
property allows the sub-viewport to automatically resize to the Control
's size.
The stretch_shrink
property Dives the sub-viewport's effective resolution by this value while preserving its scale. This can be used to speed up rendering. stretch
must be true for this property to work.
The _propagate_input_event(event: InputEvent)
method is a virtual method that may implemented. If it returns true, the event is propagated to SubViewport
children. Propagation doesn't happen if it returns false. If the function is not implemented, all events are propagated to SubViewport
s
Tab Container
The TabContainer
allows multiple children Control
nodes to be stacked on top of one another. Only a single Control
will be visible at a time.
It will create a tab for each child at the top of the TabContainer
, these tabs have their titles generated via the node's name, but can be modified using the set_tab_title(tab_idx: int, title: String)
method.
The all_tabs_in_front
property determines if the tabs should be drawn in front of the panel. Otherwise they are drawn behind the panel.
The clip_tabs
property will take the overflowing tabs and hide them in favor of navigation buttons. Otherwise it will adjust the minimum size of the container to show all tabs.
The current_tab
property returns the currently visible tab's index. Setting this property will set all other children Control
nodes to visible
of false and this index's Control
node will have it's visible
set to true.
The drag_to_rearrange_enabled
property will enable the ability to drag the tabs with your mouse.
The tab_alignment
property will determine the position that tabs will be placed, using the TabBar.AlignmentMode
:
ALIGNMENT_LEFT
- Places tabs to the left.
ALIGNMENT_CENTER
- Places tabs in the middle.
ALIGNMENT_RIGHT
- Places tabs to the right.
ALIGNMENT_MAX
- Represents the size of the
AlignmentMode
enumerator.
- Represents the size of the
The tab_focus_mode
property controls the focus for the TabBar
using the FocusMode
:
FOCUS_NONE
- The
TabBar
cannot grab focus.
- The
FOCUS_CLICK
- The
TabBar
can only grab focus on mouse clicks.
- The
FOCUS_ALL
- The
TabBar
can grab focus on mouse click, using the arrows and the Tab keys on the keyboard, or using the D-pad buttons on a gamepad.
- The
The tabs_rearrange_group
property determines which group id the TabContainer
tabs can be moved within. Other TabContainer
's with the same group id can freely drag tabs between them, though drag_to_rearrange_enabled
must be true. The default of -1 means rearranging between TabContainer
s is disabled.
The tabs_visible
property determines if a tab and content will be visible.
The use_hidden_tabs_for_min_size
property enables hidden tabs to factor their minimum size affect the total and not only the currently visible tab.
The TabContainer
has several methods:
get_current_tab_control()
- Returns the child
Control
at the current active tab index.
- Returns the child
get_popup()
- Returns the
Popup
node instance if one has been set using theset_popup
method.
- Returns the
get_previous_tab()
- Returns the previously active tab index.
get_tab_bar()
- Returns the
TabBar
contained in thisContainer
.
- Returns the
get_tab_button_icon(tab_idx: int)
- Returns the button icon from the tab at index.
get_tab_control(tab_idx: int)
- Returns the
Control
node from the tab at index.
- Returns the
get_tab_count()
- Returns the number of tabs.
get_tab_icon(tab_idx: int)
- Return the
Texture2D
for the tab at index. Will returnnull
if the tab has noTexture2D
- Return the
get_tab_idx_at_point(point: Vector2)
- Returns the index of the tab at local coordinates
point
. Returns-1
if the point is outside theControl
boundaries or if there's no tab at thepoint
.
- Returns the index of the tab at local coordinates
get_tab_idx_from_control(control: Control)
- Returns the index of the tab tied tot he given
control
. TheControl
must be a child of theTabContainer
.
- Returns the index of the tab tied tot he given
get_tab_metadata(tab_idx: int)
- Returns the metadata value set to the tab at index using the
set_tab_metadata
method. If no metadata was previously set, will returnnull
.
- Returns the metadata value set to the tab at index using the
get_tab_title(tab_idx: int)
- Returns the title of the tab at index. Tab titles default to the name of the indexed child node, but this can be overridden with the
set_tab_title
method.
- Returns the title of the tab at index. Tab titles default to the name of the indexed child node, but this can be overridden with the
is_tab_disabled(tab_idx: int)
- Returns
true
if the tab at index is disabled.
- Returns
is_tab_hidden(tab_idx: int)
- Returns
true
if the tab at index is hidden.
- Returns
select_next_available()
- Selects the first available tab with greater index than the currently selected. Returns
true
if tab selection changed.
- Selects the first available tab with greater index than the currently selected. Returns
select_previous_available()
- Selects the first available tab with lower index than the currently selected. Returns
true
if tab selection changed.
- Selects the first available tab with lower index than the currently selected. Returns
set_popup(popup: Node)
- If set on a
Popup
node instance, a popup menu icon appears in the top-right corner of theTabContainer
. If set tonull
it will make it go away. Clicking it will expand thePopup
node.
- If set on a
set_tab_button_icon(tab_idx: int, icon: Texture2D)
- Sets the button icon from the tab at index.
set_tab_disabled(tab_idx: int, disabled: bool)
- If
disabled
istrue
, disables the tab at index, making it non-interactable.
- If
set_tab_hidden(tab_idx: int, hidden: bool)
- If
hidden
istrue
, hides the tab at index, making it disappear from the tab area.
- If
set_tab_icon(tab_idx: int, icon: Texture2D)
-Sets an icon for the tab at index.set_tab_metadata(tab_idx: int, metadata: Variant)
- Sets the metadata value for the tab at index, which can be retrieved later using the
get_tab_metadata
method.
- Sets the metadata value for the tab at index, which can be retrieved later using the
set_tab_title(tab_idx: int, title: String)
- Sets a custom title for the tab at index. Tab titles default to the name of the indexed child node. Set it back to the child's name to make the tab default to it again.
The Theme Properties you can define are:
drop_mark_color
- Color for the
drop_mark
icon.
- Color for the
font_disabled_color
- Font color for disabled tabs.
font_hovered_color
- Font color for hovered tabs.
font_outline_color
- Tint color for the outline on text of the tab name.
font_selected_color
- Font color of the current tab.
font_unselected_color
- Font color of the other, unselected tabs.
icon_max_width
- The maximum allowed with of the tab's icon. This is applied on top of the default size, but before the value set with
TabBar.set_tab_icon_max_width
. The height is adjusted according tot he icon's ratio.
- The maximum allowed with of the tab's icon. This is applied on top of the default size, but before the value set with
icon_separation
- Space between tab's name and it's icon.
outline_size
- The size of the tab text outline. If using the font with
FontFile.multichannel_signed_distance_field
enabled, it'sFontFile.msdf_pixel_range
must be set to at least twice the value ofoutline_size
for outline rendering to look correct. Otherwise, the outline may appear to be cut off earlier than intended.
- The size of the tab text outline. If using the font with
side_margin
- The space at the left or right edges of the tab bar, accordingly with the current
tab_alignment
. The margin is ignored withTabBar.ALIGNMENT_RIGHT
if the tabs are clipped or a popup has been set. The margin is always ignored withTabBar.ALIGNMENT_CENTER
.
- The space at the left or right edges of the tab bar, accordingly with the current
font
- The font used to draw tab names.
font_size
- The font size of the tab names.
decrement
Texture2D
Icon for the left arrow button that appears when there are too many tabs to fit the container width. When the button is disabled, it appears semi-transparent.
decrement_highlight
Texture2D
Icon for the left arrow button that appears when there are too may tabs to fit in the container width. Used when the button is being hovered with the cursor.
drop_mark
Texture2D
Icon shown to indicate where a dragged tab is going to be dropped.
increment
Texture2D
Icon for the right arrow button that appears when there are too many tabs to fit the container width. When the button is disabled, it appears semi-transparent.
increment_highlight
Texture2D
Icon for the right arrow button that appears when there are too may tabs to fit in the container width. Used when the button is being hovered with the cursor.
menu
Texture2D
Icon for the menu button.
menu_highlight
Texture2D
Icon for the menu button when it's being hovered with the cursor.
panel
- The
StyleBox
for the background fill.
- The
tab_disabled
- The
StyleBox
for disabled tabs.
- The
tab_focus
- The
StyleBox
for when theTabBar
is focused. Thetab_focus
StyleBox
is displayed over the baseStyleBox
of the selected tab, so a partially transparentStyleBox
should be used to ensure the baseStyleBox
remains visible. AStyleBox
that represents an outline or an underline works well for this purpose. To disable the focus visual effect, assign aStyleBoxEmpty
resource. Noe that disabling the focus visual effect will harm keyboard/controller navigation usability, so this is not recommended for accessibility reasons.
- The
tab_hovered
- The
StyleBox
for hovered tabs. Note this style will be drawn with the same width astab_unselected
at minimum.
- The
tab_selected
- The
StyleBox
for currently selected tab.
- The
tab_unselected
- The
StyleBox
for other, unselected tabs.
- The
tabbar_background
- The
StyleBox
for the background fill of theTabBar
area.
- The
Recap
We should now have a better understanding of how the Container nodes work and a good overview of all the functionalities of each based on the documentation. From here you can take what you've learned about container nodes and better create reusable elements for your games and designs.
Stay tuned for future releases of shorter breakdowns with specific examples for creating elements using all of these Nodes we've discussed!
If you want to discuss more about Godot Nodes, or just Game Development in general, be sure to join us over on our growing community Discord page !
I really appreciate all of the support I've been receiving lately. All financial support goes towards helping me continue to produce articles and videos in the future. Thanks to everyone who chooses to do so!
Thanks for reading and I look forward to chatting with you over on discord !
Happy Coding.