Panel
#Header
#include <gui/panel.h>
#Functions
| Panel* | panel_create (void) |
| Panel* | panel_scroll (...) |
| Panel* | panel_custom (...) |
| void | panel_data (...) |
| type* | panel_get_data (...) |
| void | panel_size (...) |
| void | panel_scroll_mode (...) |
| uint32_t | panel_layout (...) |
| Layout* | panel_get_layout (...) |
| void | panel_visible_layout (...) |
| void | panel_update (...) |
| void | panel_viewport (...) |
A Panel is a control within a window that groups other controls. It defines its own reference system, that is, if we move a panel all its descendants will move in unison since their locations will be relative to its origin. It will support other (sub)-panels as descendants, which allows to form a Window Hierarchy (Figure 1). For portability, this Gui library does not support specific coordinates and sizes for elements linked to a panel, but the association is carried out by a Layout object which is responsible for calculating at runtime the final locations of controls based on the platform and window manager. In Hello Subpanel! you have an elementary example of using panels.
- Use panel_create to create a new panel.
- Use panel_layout to add child controls to the panel.
#1. Multi-layout panel
- Use panel_visible_layout to change the layout.
Each panel supports several layouts and allows you to switch between them at runtime (Figure 2). This allows to create dynamic responsive interfaces with very little effort, since the panel itself is responsible for linking and sizing the controls according to the active layout in each case. In Hello Multi-layout! you have an example.
Because the layouts are logical structures outside the window hierarchy, they can share controls as they are linked to the same panel (Figure 3). What is not allowed is to use the same objects in different panels, due to the hierarchy concept.
Unlike panels, layouts do not create any native control (GtkWidget, HWND, NSView, etc.).
#2. Understanding panel sizing
- Use panel_size to set the default panel size.
We are going to show, by means of an example, the logic behind the composition and dimensioning of panels. We start with (Listing 1) where we create a relatively large panel in height.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
static Window *i_window(void) { uint32_t i, n = 20; Window *window = window_create(ekWINDOW_STDRES); Panel *panel = panel_create(); Layout *layout = layout_create(2, n); for (i = 0; i < n; ++i) { char_t text[64]; Label *label = label_create(); Edit *edit = edit_create(); bstd_sprintf(text, sizeof(text), "Value %02d", i); label_text(label, text); bstd_sprintf(text, sizeof(text), "Edit here value %02d", i); edit_text(edit, text); layout_label(layout, label, 0, i); layout_edit(layout, edit, 1, i); } for (i = 0; i < n - 1; ++i) layout_vmargin(layout, i, 3); layout_hmargin(layout, 0, 5); layout_margin4(layout, 10, 10, 10, 10); panel_layout(panel, layout); window_panel(window, panel); return window; } |
- Line 3 declares the control variables and lines 4-6 create the window, panel, and layout.
- Loop 8-19 adds various labels and edit boxes to the layout.
- Loop 21-22 establishes a small gap between rows.
- Lines 24-25 establish a column spacing and border margin.
- Lines 26-27 link the layout to the panel and the panel to the window.
The result of this code is the Natural sizing of the panel (Figure 4), which defaults to a width of 100 points for the editing controls. Labels fit to the text they contain. Separations and margins have also been applied.
In this case it is possible to resize the window, since we have used the ekWINDOW_STDRES flag when creating it (Figure 5).
This behavior may not be the most appropriate for the case at hand. By default, the layout performs the Cell expansion proportionally. But what we really want is to "stretch" the editing controls so that the rows keep their default height (Listing 2).
Layout *layout = layout_create(2, n + 1); ... layout_hexpand(layout, 1); layout_vexpand(layout, n); |
The previous lines cause the horizontal expansion to fall exclusively on column 1 (that of the EditBoxes). On the other hand, an extra empty row has been created, pouring all the vertical expansion into it (Figure 6).
Although the panel now behaves correctly when the window grows, we have difficulties when we want to "shrink" it below a certain limit (Figure 7). This is because natural dimensioning imposes a minimum size, since there comes a time when it is impossible to reduce the controls associated with the layout.
This can be a problem as we may have panels large enough that they even exceed the size of the monitor and cannot be fully displayed. To solve this, we can set a default size for the entire panel (Listing 3), which will be the one displayed when the window starts (Figure 8).
... panel_size(panel, s2df(400, 300)); ... |
This command decouples, in a way, the size of the panel from the size of its content. In this way, the Layout is free to reduce the size of the view, regardless of whether or not it can display the entire content (Figure 9).
And finally, if we want, we can create the panel with scroll bars (Listing 4) and scroll through the non-visible content (Figure 10).
... Panel *panel = panel_scroll(TRUE, TRUE); ... |
And, of course, everything said will work the same on any platform (Figure 11).
#3. Scroll bars in panels
- Use panel_scroll to create a new panel with scroll bars.
- Use panel_custom if you also want to highlight the panel's border.
- Use panel_scroll_mode to define the scroll bars' behavior.
As we saw in Understanding panel sizing, panel_size decouples a panel's size from its content's, and panel_scroll adds bars to move through the non-visible part. But as soon as a scroll bar appears, a question comes up: who takes care of the physical space that bar occupies? The answer depends on whether the system draws it in legacy or overlay style.
- Legacy (or "solid") bars occupy real space permanently — the content area shrinks to make room for them, whether or not they're being used at that moment. It's the only style available on Windows, and the one macOS and Linux use when the user sets that preference on the system.
- Overlay bars float over the content without reserving any space — they stay hidden or nearly transparent until the user interacts with them (moving the mouse over the drawing area, scrolling...), at which point they briefly appear on top of the content. It's the default style on macOS and on most modern Linux distributions (GNOME/GTK3) with a trackpad.
- The choice between one style or the other isn't made by the application, but by the operating system itself — on macOS, System Settings > Trackpad > Scroll & Zoom > Show scroll bars; on Linux/GNOME, the equivalent accessibility setting of the active desktop environment. NAppGUI detects the style change on the fly, without needing to restart the application or recreate the window.
The difference between both styles isn't only visual — it also determines whether the bar's space is subtracted from the area available for the content or not (Figure 12).
If we don't want to (or can't) enlarge the window, panel_scroll_mode lets us decide what to do with that gap when the bar is legacy: always reserve the same fixed space (stable mode), or let the content shrink dynamically to make room for the bar without the panel's outer size changing (Figure 13).
panel_scroll_mode(panel, TRUE, TRUE); |
- With
hshrink/vshrinkset toFALSE(stable behavior), the panel always reserves the legacy bar's margin, even when it isn't visible at that moment — avoiding the content "jump" every time the bar appears or disappears. - With
hshrink/vshrinkset toTRUE(default value), the content resizes to use the full space when the bar isn't needed, and shrinks just enough when it is — never leaving a permanent blank gap. - In overlay, neither mode reserves any space — the bar floats over the content without affecting the layout, so panel_scroll_mode has no visible effect in that style.
Finally, navigation doesn't only depend on dragging the bar itself. The content can be scrolled with the mouse wheel (vertical) and, on most systems, with Shift + wheel for horizontal scrolling — the same standard gesture used by applications like VS Code or Microsoft 365. On trackpads and touch mice (like Apple's Magic Mouse), a two-finger sideways swipe already produces horizontal scrolling natively, with no modifier key needed.
#panel_create ()
Create a panel.
Panel* panel_create(void);
Return
The new panel.
#panel_scroll ()
Create a panel with scroll bars.
Panel* panel_scroll(const bool_t hscroll, const bool_t vscroll);
| hscroll |
|
| vscroll |
|
Return
The new panel.
Remarks
See Understanding panel sizing and Scroll bars in panels.
#panel_custom ()
Create a fully configurable panel.
Panel* panel_custom(const bool_t hscroll, const bool_t vscroll, const bool_t border);
| hscroll |
|
| vscroll |
|
| border |
|
Return
The new panel.
Remarks
See Understanding panel sizing and Scroll bars in panels.
#panel_data ()
Associate user data with the panel.
void panel_data(Panel *panel, type **data, FPtr_destroy func_destroy_data, type);
| panel | The panel. |
| data | User data. |
| func_destroy_data | Destructor of user data. It will be called when the panel is destroyed. |
| type | Type of user data. |
#panel_get_data ()
Get the user data associated with the panel.
type* panel_get_data(const Panel *panel, type);
| panel | The panel. |
| type | Type of user data. |
Return
User data.
#panel_size ()
Sets the default size of the visible area of a panel.
void panel_size(Panel *panel, const S2Df size);
| panel | The panel. |
| size | The default size. |
Remarks
See Understanding panel sizing.
#panel_scroll_mode ()
Sets how a panel's content reacts to the space occupied by its legacy scroll bars.
void panel_scroll_mode(Panel *panel, const bool_t hshrink, const bool_t vshrink);
| panel | The panel. |
| hshrink |
|
| vshrink |
|
Remarks
Only has an effect with legacy bars — in overlay there is no space to reserve or shrink from. See Scroll bars in panels.
#panel_layout ()
Add a layout to a panel.
uint32_t panel_layout(Panel *panel, Layout *layout);
| panel | The panel. |
| layout | Layout. |
Return
The newly added layout index.
#panel_get_layout ()
Get a layout of a panel.
Layout* panel_get_layout(Panel *panel, const uint32_t index);
| panel | The panel. |
| index | The layout index. |
Return
Layout.
#panel_visible_layout ()
Set the active layout inside the panel.
void panel_visible_layout(Panel *panel, const uint32_t index);
| panel | The panel. |
| index | The layout index. |
Remarks
To make the change effective, you have to call panel_update.
#panel_update ()
Update the window that contains the panel.
void panel_update(Panel *panel);
| panel | The panel. |
Remarks
It is equivalent to calling window_update.
#panel_viewport ()
Gets the dimensions of the visible area of the panel.
void panel_viewport(const Panel *panel, V2Df *pos, S2Df *size);
| panel | The panel. |
| pos | The position of the viewport. It can be |
| size | The size of the viewport. It can be |
Remarks
If the panel does not have scroll bars, pos will be (0,0). See Scroll bars in panels.


