Cross-platform C SDK logo
EspañolEnglish
Cross-platform C SDK

Panel

❮ Back
Next ❯

#Header

#include <gui/panel.h>


#Functions

Panel*panel_create (void)
Panel*panel_scroll (...)
Panel*panel_custom (...)
voidpanel_data (...)
type*panel_get_data (...)
voidpanel_size (...)
voidpanel_scroll_mode (...)
uint32_tpanel_layout (...)
Layout*panel_get_layout (...)
voidpanel_visible_layout (...)
voidpanel_update (...)
voidpanel_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.
  • Figure 1: Window hierarchy.

#1. Multi-layout panel

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.

Figure 2: Panel with two different organizations for the same controls.

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.).
Figure 3: It is possible to reuse the same components between layouts of the same panel.

#2. Understanding panel sizing

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.

Listing 1: Composition of a panel with multiple edit rows.
 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.

Panel with a list of edit controls.
Figure 4: Natural sizing of the panel defined in (Listing 1).

In this case it is possible to resize the window, since we have used the ekWINDOW_STDRES flag when creating it (Figure 5).

Window with extended size edit controls.
Figure 5: Behavior of the panel when the window grows.

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).

Listing 2: Change in horizontal and vertical expansion.
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).

Window with extended size editing controls, improving the layout.
Figure 6: Desired behavior, when the window expands.

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.

Window with editing controls and reduced size.
Figure 7: Minimum panel size.

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).

Listing 3: Panel default size.
...
panel_size(panel, s2df(400, 300));
...
Window with editing controls and default size.
Figure 8: Natural sizing, forced to 400x300.

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).

Window with very small size and editing controls.
Figure 9: Panel boundary reduction.

And finally, if we want, we can create the panel with scroll bars (Listing 4) and scroll through the non-visible content (Figure 10).

Listing 4: Panel with scroll bars.
...
Panel *panel = panel_scroll(TRUE, TRUE);
...
Window with edit controls and scroll bars.
Figure 10: Panel with scroll bars.

And, of course, everything said will work the same on any platform (Figure 11).

Window with edit controls and scroll bars, macOS version. Window with edit controls and scroll bars, Linux version.
Figure 11: Our panel running on macOS and Linux.

#3. Scroll bars in panels

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).

Animation showing the scrollbar style change when expanding the panel.
Figure 12: Switching from an overlay bar to a legacy one, enlarging the window to make room for the bar without invading the content.

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).

Listing 5: Enabling shrink mode on both axes.
Animation showing
Figure 13: Switching from an overlay bar to a legacy one, shrinking the content so the panel's size doesn't change.
  • With hshrink/vshrink set to FALSE (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/vshrink set to TRUE (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.

❮ Back
Next ❯

#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

TRUE if we want horizontal scroll bar.

vscroll

TRUE if we want vertical scroll bar.

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

TRUE if we want horizontal scroll bar.

vscroll

TRUE if we want vertical scroll bar.

border

TRUE if we want to highlight the 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

TRUE for the content to shrink in height when the horizontal bar appears, instead of always reserving it a fixed margin.

vshrink

TRUE for the content to shrink in width when the vertical bar appears, instead of always reserving it a fixed margin.

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 NULL.

size

The size of the viewport. It can be NULL.

Remarks

If the panel does not have scroll bars, pos will be (0,0). See Scroll bars in panels.

❮ Back
Next ❯