Panel
#Cabecera
#include <gui/panel.h>
#Funciones
| 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 (...) |
Un Panel es un control dentro de una ventana que agrupa otros controles. Define su propio sistema de referencia, es decir, si desplazamos un panel todos sus descendientes se moverán al unísono ya que sus ubicaciones serán relativas al origen del mismo. Admitirá otros (sub)-paneles como descendientes, lo que permite conformar una Jerarquía de ventana (Figura 1). En pro de la portabilidad, esta librería Gui no permite indicar directamente coordenadas ni tamaños concretos para los elementos dentro de un panel, sino que la asociación se lleva a cabo mediante un objeto Layout que se encarga de calcular en tiempo de ejecución las ubicaciones finales de los controles en función de la plataforma y gestor de ventanas. En ¡Hola Subpanel! tienes un ejemplo elemental del uso de paneles.
- Utiliza panel_create para crear un nuevo panel.
- Utiliza panel_layout para añadir controles al panel.
#1. Panel multi-layout
- Utiliza panel_visible_layout para alternar entre layouts.
Cada panel admite varios layouts y permite alternar entre ellos en tiempo de ejecución (Figura 2). Esto permite crear interfaces dinámicas responsivas con muy poco esfuerzo, ya que el propio panel se encarga de vincular y dimensionar los controles en función del layout activo en cada caso. En ¡Hola Multi-layout! tienes un ejemplo.
Debido a que los layout son estructuras lógicas fuera de la jerarquía de ventana, pueden compartir controles al estar vinculados con el mismo panel (Figura 3). Lo que no está permitido es utilizar los mismos objetos en diferentes paneles, por el propio concepto de jerarquía.
Al contrario que los paneles, los layouts no crean ningún control nativo (GtkWidget, HWND, NSView, etc).
#2. Entendiendo el dimensionado de paneles
- Utiliza panel_size para establecer el tamaño por defecto del panel.
Vamos a mostrar, por medio de un ejemplo, la lógica que se esconde detrás de la composición y dimensionado de paneles. Empezamos con (Listado 1) donde creamos un panel relativamente extenso en altura.
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; } |
- La línea 3 declara las variables de control y las líneas 4-6 crean la ventana, el panel y el layout.
- El bucle 8-19 añade varias etiquetas y cajas de edición al layout.
- El bucle 21-22 establece una pequeña separación entre filas.
- Las líneas 24-25 establecen una separación entre columnas y un margen en el borde.
- Las líneas 26-27 vinculan el layout con el panel y este con la ventana.
El resultado de este código es el Dimensionado natural del panel (Figura 4), que establece por defecto una anchura de 100 puntos para los controles de edición. Las etiquetas se ajustan al texto que contienen. También se han aplicado las separaciones y márgenes.
En este caso es posible redimensionar la ventana, ya que hemos utilizado el flag ekWINDOW_STDRES al crearla (Figura 5).
Este comportamiento puede que no sea el más apropiado para el caso que nos ocupa. Por defecto, el layout realiza la Expansión de celdas de manera proporcional. Pero lo que realmente buscamos es "estirar" los controles de edición y que las filas mantengan su altura por defecto (Listado 2).
Layout *layout = layout_create(2, n + 1); ... layout_hexpand(layout, 1); layout_vexpand(layout, n); |
Las líneas anteriores provocan que la expansión horizontal recaiga exclusivamente sobre la columna 1 (la de los EditBox). Por otro lado, se ha creado una fila extra vacía, volcando en ella toda la expansión vertical (Figura 6).
Si bien el panel se comporta ahora correctamente ante el crecimiento de la ventana, tenemos dificultades cuando queremos "encogerla" por debajo de cierto límite (Figura 7). Esto es debido a que el dimensionado natural impone un tamaño mínimo, ya que llega un momento que es imposible reducir los controles asociados al layout.
Esto puede suponer un problema, ya que es posible que tengamos paneles suficientemente grandes que excedan incluso el tamaño del monitor y no puedan mostrarse en su totalidad. Para solucionarlo podemos establecer un tamaño por defecto para todo el panel (Listado 3), que será el que muestre al iniciar la ventana (Figura 8).
... panel_size(panel, s2df(400, 300)); ... |
Este comando desvincula, en cierta manera, el tamaño del panel del tamaño de su contenido. De esta forma, el Layout tiene libertad para reducir el tamaño de la vista, independientemente de si puede o no mostrar la totalidad del contenido (Figura 9).
Y ya por último, si queremos, podemos crear el panel con barras de scroll (Listado 4) y desplazarnos por el contenido no visible (Figura 10).
... Panel *panel = panel_scroll(TRUE, TRUE); ... |
Y, por supuesto, todo lo dicho funcionará de la misma forma en cualquier plataforma (Figura 11).
#3. Barras de scroll en paneles
- Utiliza panel_scroll para crear un nuevo panel con barras de scroll.
- Utiliza panel_custom si además quieres resaltar el borde del panel.
- Utiliza panel_scroll_mode para definir el comportamiento de las barras de scroll.
Como hemos visto en Entendiendo el dimensionado de paneles, panel_size desvincula el tamaño de un panel del de su contenido, y panel_scroll añade barras para desplazarnos por la parte no visible. Pero en cuanto aparece una barra de scroll surge una pregunta: ¿quién se hace cargo del espacio físico que ocupa esa barra? La respuesta depende de si el sistema la dibuja en estilo legacy o overlay.
- Las barras legacy (o "sólidas") ocupan espacio real de forma permanente — el área de contenido se reduce para dejarles sitio, estén o no siendo usadas en ese momento. Es el único estilo disponible en Windows, y el que usan macOS y Linux cuando el usuario fija esa preferencia en el sistema.
- Las barras overlay flotan sobre el contenido sin reservar ningún espacio — permanecen ocultas o casi transparentes hasta que el usuario interactúa con ellas (mover el ratón sobre el área de dibujo, hacer scroll...), momento en el que aparecen brevemente encima del contenido. Es el estilo por defecto en macOS y en la mayoría de distribuciones Linux modernas (GNOME/GTK3) con trackpad.
- La elección entre uno u otro estilo no la hace la aplicación, sino el propio sistema operativo — en macOS, Ajustes del Sistema > Trackpad > Desplazamiento y zoom > Mostrar barras de desplazamiento; en Linux/GNOME, el ajuste equivalente de accesibilidad del entorno de escritorio activo. NAppGUI detecta el cambio de estilo en caliente, sin necesidad de reiniciar la aplicación ni recrear la ventana.
La diferencia entre ambos estilos no es solo visual — también determina si el espacio de la barra se resta o no del área disponible para el contenido (Figura 12).
Si no queremos (o no podemos) agrandar la ventana, panel_scroll_mode permite decidir qué hacer con ese hueco cuando la barra es legacy: reservar siempre el mismo espacio fijo (modo estable), o dejar que el contenido se estreche dinámicamente para cederle sitio a la barra sin que el tamaño exterior del panel cambie (Figura 13).
panel_scroll_mode(panel, TRUE, TRUE); |
- Con
hshrink/vshrinkaFALSE(comportamiento estable), el panel reserva siempre el margen de la barra legacy, incluso cuando esta no está visible en ese momento — evitando el "salto" de contenido cada vez que la barra aparece o desaparece. - Con
hshrink/vshrinkaTRUE(valor por defecto), el contenido se redimensiona para aprovechar el espacio completo cuando no hace falta la barra, y se encoge justo lo necesario cuando sí hace falta — sin dejar nunca un hueco en blanco permanente. - En overlay, ninguno de los dos modos reserva espacio — la barra flota sobre el contenido sin afectar al layout, así que panel_scroll_mode no tiene ningún efecto visible en ese estilo.
Por último, la navegación no depende únicamente de arrastrar la propia barra. El contenido se puede desplazar con la rueda del ratón (vertical) y, en la mayoría de sistemas, con Mayús + rueda para desplazamiento horizontal — el mismo gesto estándar que usan aplicaciones como VS Code o Microsoft 365. En trackpads y ratones táctiles (como el Magic Mouse de Apple), el deslizamiento lateral de dos dedos ya produce scroll horizontal de forma nativa, sin necesidad de ninguna tecla modificadora.
#panel_create ()
Crea un panel.
Panel* panel_create(void);
Retorna
El nuevo panel.
#panel_scroll ()
Crea un panel con barras de scroll.
Panel* panel_scroll(const bool_t hscroll, const bool_t vscroll);
| hscroll |
|
| vscroll |
|
Retorna
El nuevo panel.
Observaciones
Ver Entendiendo el dimensionado de paneles y Barras de scroll en paneles.
#panel_custom ()
Crea un panel totalmente configurable.
Panel* panel_custom(const bool_t hscroll, const bool_t vscroll, const bool_t border);
| hscroll |
|
| vscroll |
|
| border |
|
Retorna
El nuevo panel.
Observaciones
Ver Entendiendo el dimensionado de paneles y Barras de scroll en paneles.
#panel_data ()
Asocia datos de usuario con el panel.
void panel_data(Panel *panel, type **data, FPtr_destroy func_destroy_data, type);
| panel | El panel. |
| data | Datos de usuario. |
| func_destroy_data | Destructor de los datos de usuario. Será llamado al destruir el panel. |
| type | Tipo de datos de usuario. |
#panel_get_data ()
Obtiene los datos de usuario asociados con el panel.
type* panel_get_data(const Panel *panel, type);
| panel | El panel. |
| type | Tipo de datos de usuario. |
Retorna
Los datos de usuario.
#panel_size ()
Establece el tamaño por defecto del área visible de un panel.
void panel_size(Panel *panel, const S2Df size);
| panel | El panel. |
| size | El tamaño por defecto. |
Observaciones
Ver Entendiendo el dimensionado de paneles.
#panel_scroll_mode ()
Establece cómo reacciona el contenido de un panel al espacio ocupado por sus barras de scroll legacy.
void panel_scroll_mode(Panel *panel, const bool_t hshrink, const bool_t vshrink);
| panel | El panel. |
| hshrink |
|
| vshrink |
|
Observaciones
Solo tiene efecto con barras legacy — en overlay no hay espacio que reservar ni del que estrecharse. Ver Barras de scroll en paneles.
#panel_layout ()
Añade un layout a un panel.
uint32_t panel_layout(Panel *panel, Layout *layout);
| panel | El panel. |
| layout | El layout. |
Retorna
El índice del layout recién añadido.
#panel_get_layout ()
Obtiene un layout de un panel.
Layout* panel_get_layout(Panel *panel, const uint32_t index);
| panel | El panel. |
| index | El índice del layout. |
Retorna
El layout.
#panel_visible_layout ()
Establece el layout activo dentro del panel.
void panel_visible_layout(Panel *panel, const uint32_t index);
| panel | El panel. |
| index | El índice del layout. |
Observaciones
Para el cambio se haga efectivo, hay que llamar a panel_update.
#panel_update ()
Refresca el contenido de la ventana que contiene el panel.
void panel_update(Panel *panel);
| panel | El panel. |
Observaciones
Es equivalente a llamar a window_update.
#panel_viewport ()
Obtiene las dimensiones del área visible del panel.
void panel_viewport(const Panel *panel, V2Df *pos, S2Df *size);
| panel | El panel. |
| pos | La posición del viewport. Puede ser |
| size | El tamaño del viewport. Puede ser |
Observaciones
Si el panel no tiene barras de scroll, pos será (0,0). Ver Barras de scroll en paneles.


