Window
Funciones
Window* | window_create (...) |
void | window_destroy (...) |
void | window_panel (...) |
void | window_OnClose (...) |
void | window_OnMoved (...) |
void | window_OnResize (...) |
void | window_title (...) |
void | window_show (...) |
void | window_hide (...) |
void | window_overlay (...) |
uint32_t | window_modal (...) |
void | window_stop_modal (...) |
void | window_hotkey (...) |
void | window_clear_hotkeys (...) |
void | window_cycle_tabstop (...) |
gui_focus_t | window_next_tabstop (...) |
gui_focus_t | window_previous_tabstop (...) |
gui_focus_t | window_focus (...) |
GuiControl* | window_get_focus (...) |
void | window_focus_info (...) |
void | window_update (...) |
void | window_origin (...) |
void | window_size (...) |
V2Df | window_get_origin (...) |
S2Df | window_get_size (...) |
S2Df | window_get_client_size (...) |
R2Df | window_control_frame (...) |
V2Df | window_client_to_screen (...) |
void | window_defbutton (...) |
void | window_cursor (...) |
Los objetos Window son los contenedores de más alto nivel dentro de la interfaz de usuario (Figura 1). Están compuestos por la barra de título, donde se ubican los botones de cerrar, maximizar y minimizar, la zona interior y el marco. Si la ventana admite redimensionado, dicho marco podrá ser arrastrado con el ratón para cambiar su tamaño. La zona interior o área cliente de la ventana (Figura 2) es donde residen los controles que conforman la interfaz propiamente dicha y se configura mediante un panel principal. En ¡Hola Mundo! tienes un ejemplo sencillo de composición y muestra de una ventana.
- Utiliza window_create para crear una ventana.
- Utiliza window_panel para asignar el panel principal.
- Utiliza window_show para mostrar una ventana.
- Utiliza el flag ekWINDOW_TITLE para incluir barra de título.
- Utiliza window_title para asignar un título.
NAppGUI no distingue entre ventana, cuadro de diálogo, caja de mensaje, etc. El rol de cada ventana irá en función de los controles que contenga, su ubicación y su comportamiento.
1. Tamaño de la ventana
En principio, el tamaño de la ventana se calcula automáticamente en función del Dimensionado natural de su panel principal, pero puede ser alterado en cualquier momento.
- Utiliza window_size para cambiar el tamaño del panel principal.
- Utiliza el flag ekWINDOW_MAX para incluir el botón de maximizar en la barra de título.
- Utiliza el flag ekWINDOW_MIN para incluir el botón de minimizar en la barra de título.
- Utiliza el flag ekWINDOW_RESIZE para crear una ventana con bordes redimensionables.
El cambio en las dimensiones del área cliente lleva implícita una re-ubicación y re-dimensionado de los controles interiores. Esto se gestiona automáticamente por medio de los objetos layout, en función de como se haya configurado su Expansión de celdas y que se propagará de manera recursiva por todos los sublayouts. En Die tienes un ejemplo del redimensionado de una ventana (Figura 3).
2. Cierre de la ventana
Normalmente una ventana se cierra pulsando el botón [X]
ubicado a la derecha de la barra de título. Pero, en ocasiones, puede ser útil cerrarla también mediante las teclas [ENTER]
o [ESC]
. Cerrar una ventana implica ocultarla, pero no destruirla. Es decir, podemos volver a mostrar una ventana ya cerrada utilizando window_show. En el caso de que el cierre esté condicionado a un estado de la aplicación, como guardar un archivo por ejemplo, deberemos asignar un manejador mediante window_OnClose y decidir ahí si se cierra o no.
- Utiliza window_hide para ocultar una ventana.
- Utiliza window_destroy para destruir definitivamente una ventana.
- Utiliza el flag ekWINDOW_CLOSE para incluir el botón de cierre en la barra de título.
- Utiliza el flag ekWINDOW_RETURN para habilitar el cierre mediante
[ENTER]
. - Utiliza el flag ekWINDOW_ESC para habilitar el cierre mediante
[ESC]
. - Utiliza el flag window_OnClose para evitar el cierre de una ventana (Listado 1).
1 2 3 4 5 6 7 8 9 10 11 |
static void i_OnClose(App *app, Event *e) { const EvWinClose *params = event_params(e, EvWinClose); if (can_close(app, params->origin) == FALSE) { bool_t *result = event_result(e, bool_t); *result = FALSE; } } ... window_OnClose(window, listener(app, i_OnClose, App)); |
Al destruir una ventana se realiza, implícitamente, la destrucción de todos sus elementos y controles internos.
3. Ventanas modales
Son aquellas que, al ser lanzadas, bloquean a la ventana anterior (o padre) hasta que no se produzca el cierre de la misma (Figura 4). El ser o no "modal" no es una característica de la ventana en sí, si no del modo de lanzarla. En ¡Hola Modal Window! tienes un ejemplo de uso.
- Utiliza window_modal para mostrar una ventana en modo modal.
- Utiliza window_stop_modal para ocultarla y detener el ciclo modal.
Tras la llamada a window_modal, el programa queda detenido en este punto, a la espera del cierre de la ventana, que podrá realizarse mediante [X]
, [ENTER]
, [ESC]
o llamando a window_stop_modal (Listado 2). El valor retornado por esta función será:
- ekGUI_CLOSE_ESC (1). Si la ventana modal se cerró pulsando
[ESC]
. - ekGUI_CLOSE_INTRO (2). Si la ventana modal se cerró pulsando
[ENTER]
. - ekGUI_CLOSE_BUTTON (3). Si la ventana modal se cerró pulsando
[X]
. - El valor indicado en window_stop_modal.
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 |
static void i_OnAcceptClick(Window *window, Event *e) { window_stop_modal(window, 300); } Window *window = i_create_window_with_accept_button(); // The program will stop HERE until window is closed uint32_t ret = window_modal(window); if (ret == 1) { // Closed by ESC } else if (ret == 2) { // Closed by INTRO } else if (ret == 3) { // Closed by [X] } else if (ret == 300) { // Closed by window_stop_modal } window_destroy(&window); |
Por defecto, la ventana modal se ocultará tras recibir la llamada a window_stop_modal, pero no se destruirá como ya hemos indicado anteriormente. En determinadas ocasiones (aunque no muy habituales), es posible que queramos relanzar la ventana tras acabar el ciclo modal sin que se produzca un anti-estético "parpadeo" debido a una nueva (y rápida) visualización tras el cierre de la ventana.
- Utiliza el flag ekWINDOW_MODAL_NOHIDE al crear la ventana para evitar que se oculte tras el ciclo modal.
4. Ventanas superpuestas
- Utiliza window_overlay para lanzar una ventana superpuesta.
En ocasiones puede ser útil mostrar pequeñas ventanas sobre la principal que incluyan controles adicionales de forma temporal. Es un caso similar a las ventanas modales, con la diferencia de que la ventana "padre" no será desactivada y seguirá recibiendo eventos desde el sistema operativo, mientras la secundaria sigue visible. Normalmente no incluyen borde ni barra de título. En ¡Hola Overlay Window! tienes el código fuente de (Figura 5).
- Utiliza window_control_frame para obtener las coordenadas de ventana de un control interior.
- Utiliza window_client_to_screen para transformar coordenadas de ventana en coordenadas de pantalla.
Es habitual que tengamos que posicionar las ventanas secundarias tomando como referencia algún control interior de la ventana principal, pero el origen de la ventana se debe proporcionar en coordenadas de pantalla. En (Listado 3) se muestra como realizar correctamente la transformación de coordenadas de (Figura 5).
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 30 31 |
static void i_OnIdleLaunch(FlyOut *flyout, Event *e) { /* Edit control bounds in window coordinates */ R2Df frame = window_control_frame(flyout->parent, flyout->edit); /* Top-Left edit control in screen coordinates */ V2Df pos = window_client_to_screen(flyout->parent, frame.pos); /* Flyout window size */ S2Df size = window_get_size(flyout->flywin); switch (flyout->align) { case 0: pos.y += frame.size.height; break; case 1: pos.y -= size.height; break; case 2: pos.x -= size.width - frame.size.width; pos.y += frame.size.height; break; case 3: pos.x -= size.width - frame.size.width; pos.y -= size.height; break; } /* Position in screen coordinates */ window_origin(flyout->flywin, pos); window_overlay(flyout->flywin, flyout->parent); unref(e); } |
Si hacemos clic sobre la ventana padre, la secundaria será desactivada y ocultada de forma automática. Deberemos llamar de nuevo a window_overlay
para visualizarla. Si queremos evitar el cierre por desactivación, deberemos contemplar el valor ekGUI_CLOSE_DEACT en window_OnClose
. Por supuesto, también podremos incluir los flags ekGUI_CLOSE_ESC y ekGUI_CLOSE_INTRO para cerrar la ventana mediante el teclado.
5. Foco del teclado
Determinadas ventanas, como los cuadros de diálogo, hacen un uso intensivo del teclado. Incluso es posible que el usuario deba gestionar la entrada de datos prescindiendo del ratón. Es por esto que tenemos que tener muy claro como se comportan los diferentes elementos ante las pulsaciones de teclas. Se denomina foco del teclado al único control que recibe los eventos de teclas dentro de una ventana determinada. Normalmente este control aparece con el borde resaltado (Figura 6).
- Utiliza window_get_focus para obtener el control que dispone del foco del teclado.
5.1. Cambio del foco
El foco del teclado se asigna automáticamente al primer control de la tab-list cuando se activa la ventana y se puede cambiar de diferentes maneras:
- Mediante
[TAB]
o[SHIFT]+[TAB]
nos desplazaremos por los controles incluidos en la tab-list, como ya vimos en Tabstops. - Haciendo clic sobre el control al que queremos conectar el teclado.
- Utilizando window_focus, que lo establecerá en el control deseado mediante código.
- Utilizando window_next_tabstop que equivale a pulsar
[TAB]
. En ¡Hola IP-Input! tienes variosEdit
que pasan al siguiente control cuando se introducen exactamente tres números. - Utilizando window_previous_tabstop que equivale a pulsar
[SHIFT]+[TAB]
.
Estas funciones devolverán un gui_focus_t para indicar si el cambio de foco ha tenido éxito o no.
5.2. Protocolo del foco
El cambio del foco entre controles no es directo, mas bien sigue un protocolo (Figura 7). Por lo general no nos tenemos que preocupar por esto, ya que cada control presenta un comportamiento por defecto a la hora de liberar o aceptar el foco. Los puntos a tener presentes son los siguientes:
- Los controles
Edit
pueden retener el foco como respuesta a un eventoOnChange
, como vimos en Validar textos. - Las vistas personalizadas permiten tomar una decisión en tiempo de ejecución mediante los eventos
OnResignFocus
yOnAcceptFocus
, como también vimos en Uso del teclado. Por defecto, aceptarán ambos casos. - Utiliza window_focus_info dentro de edit_OnChange o view_OnResignFocus para obtener información adicional sobre la operación de cambio de foco.
Por ejemplo, si pulsamos [TAB]
sobre un Edit
, se lanzará el evento OnChange
, que puede devolver FALSE
como respuesta. En este caso, el foco del teclado permanecerá en dicho Edit
y no saltará al siguiente control.
5.3. Tablist sin ciclos
Volviendo a la navegación mediante la tecla [TAB]
, lo habitual será que los tabstops funcionen de manera cíclica (por defecto). Es decir, si el último control de la tab-list tiene el foco y pulsamos [TAB]
, el foco pasará de nuevo al primer control de la tab-list. Es posible deshabilitar este comportamiento, quedando el foco fijo en el último control aunque pulsemos repetidamente la tecla [TAB]
. De igual forma, el foco quedará fijo en el primer control aunque pulsemos [SHIFT]+[TAB]
.
- Utiliza window_cycle_tabstop para activar/desactivar el ciclo de tabstops.
6. Botón por defecto
El botón por defecto es aquel que aparece resaltado dentro de la ventana y que recibirá un evento OnClick
cada vez que se pulse la tecla [RETURN]
, independientemente de que control tenga el foco del teclado. En principio, no existe ningún botón por defecto, hay que indicarlo explícitamente en la ventana.
- Utiliza window_defbutton para establecer el botón por defecto.
7. Atajos de teclado
Como ya hemos indicado, el foco del teclado estará fijado en algún control del interior de la ventana ya sea un Edit
, Button
, View
, etc. Pero es posible que queramos definir acciones globales asociadas a alguna tecla concreta.
- Utiliza window_hotkey para asignar una acción a una tecla.
- Utiliza window_clear_hotkeys para eliminar todos los atajos asociados a la ventana.
Las hotkeys tendrán prioridad sobre el foco del teclado (Figura 9). Es decir, si tenemos una acción vinculada con la tecla [F9]
, la ventana capturará el evento ekGUI_EVENT_KEYDOWN (F9) y este no llegará al control que tiene actualmente el foco del teclado.
Para concluir, resumimos todos los puntos a tener en cuenta a la hora de realizar una correcta gestión del teclado.
- Cierre de la ventana con
[RETURN]
o[ESC
. - Gestionar correctamente la tab-list y el foco del teclado.
- Definir un botón por defecto, que se active al pulsar
[RETURN]
. - Definir los atajos de teclado oportunos.
window_create ()
Crea una nueva ventana.
Window* window_create(const uint32_t flags);
flags | Combinación de valores window_flag_t. |
Retorna
La ventana recién creada.
window_destroy ()
Destruye la ventana y todo su contenido.
void window_destroy(Window **window);
window | La ventana. Será puesto a |
Observaciones
Se destruirán recursivamente paneles, layouts y componentes.
window_panel ()
Asocia el panel principal a una ventana.
void window_panel(Window *window, Panel *panel);
window | La ventana. |
panel | Panel principal, que integra todo el contenido de la ventana (vistas, controles, etc). |
Observaciones
El tamaño de la ventana se ajustará en función del Dimensionado natural del panel principal.
window_OnClose ()
Establece un manejador para el evento de cierre de la ventana.
void window_OnClose(Window *window, Listener *listener);
window | La ventana. |
listener | Función callback que se llamará antes de cerrar una ventana. |
Observaciones
Ver Cierre de la ventana.
window_OnMoved ()
Establece un manejador para el desplazamiento de la ventana por el escritorio.
void window_OnMoved(Window *window, Listener *listener);
window | La ventana. |
listener | Función callback que se llamará a medida que se arrastre la barra de título y se mueva la ventana por el escritorio. |
Observaciones
Ver Eventos GUI.
window_OnResize ()
Establece un manejador para el redimensionado de la ventana.
void window_OnResize(Window *window, Listener *listener);
window | La ventana. |
listener | Función callback que se llamará a medida que se arrastren los bordes externos de la ventana para cambiar su tamaño. |
Observaciones
El redimensionado y rehubicación de elementos se realiza de forma automática en función del Layout principal, por lo que no suele ser necesario que la aplicación responda a este evento. Ver Eventos GUI.
window_title ()
Establece el texto que mostrará la ventana en la barra de título.
void window_title(Window *window, const char_t *text);
window | La ventana. |
text | Cadena C UTF8 terminada en carácter nulo |
window_show ()
Muestra la ventana. Por defecto las ventanas se crean ocultas. Hay que mostrarlas explícitamente.
void window_show(Window *window);
window | La ventana. |
window_hide ()
Oculta la ventana.
void window_hide(Window *window);
window | La ventana. |
window_overlay ()
Lanza una ventana superpuesta.
void window_overlay(Window *window, Window *parent);
window | La ventana. |
parent | La ventana principal. |
Observaciones
window_modal ()
Lanza una ventana en modo modal.
uint32_t window_modal(Window *window, Window *parent);
window | La ventana. |
parent | La ventana bloqueada. |
Retorna
Valor retornado por window_stop_modal.
Observaciones
parent
dejará de recibir eventos hasta que no se llame a window_stop_modal. Ver Ventanas modales
window_stop_modal ()
Termina el ciclo modal de una ventana.
void window_stop_modal(Window *window, const uint32_t return_value);
window | La ventana lanzada previamente con window_modal. |
return_value | Valor que deberá devolver window_modal. |
Observaciones
Ver Ventanas modales.
window_hotkey ()
Establece una acción asociada a la pulsación de una tecla.
void window_hotkey(Window *window, const vkey_t key, const uint32_t modifiers, Listener *listener);
window | La ventana. |
key | La tecla. |
modifiers | Modificadores. |
listener | Manejador del evento asociado a la pulsación de la tecla. Si |
Observaciones
Ver Atajos de teclado.
window_clear_hotkeys ()
Elimina todos los atajos de teclado asociados con la ventana.
void window_clear_hotkeys(Window *window);
window | La ventana. |
Observaciones
Ver Atajos de teclado.
window_cycle_tabstop ()
Activa o desactiva el comportamiento cíclico de los tabstops.
void window_cycle_tabstop(Window *window, const bool_t cycle);
window | La ventana. |
cycle |
|
Observaciones
Ver Tablist sin ciclos.
window_next_tabstop ()
Mueve el foco del teclado al siguiente control en la tab-list. Produce el mismo efecto que pulsar [TAB]
.
gui_focus_t window_next_tabstop(Window *window);
window | La ventana. |
Retorna
Resultado de la operación de cambio del foco.
Observaciones
Ver Cambio del foco.
window_previous_tabstop ()
Mueve el foco del teclado al anterior control en la tab-list. Produce el mismo efecto que pulsar [SHIFT]+[TAB]
.
gui_focus_t window_previous_tabstop(Window *window);
window | La ventana. |
Retorna
Resultado de la operación de cambio del foco.
Observaciones
Ver Cambio del foco.
window_focus ()
Establece el foco del teclado en un control concreto.
gui_focus_t window_focus(Window *window, GuiControl *control);
window | La ventana. |
control | El control que recibirá el foco. |
Retorna
Resultado de la operación de cambio del foco.
Observaciones
Ver Cambio del foco.
window_get_focus ()
Obtiene el control que posee el foco del teclado.
GuiControl* window_get_focus(Window *window);
window | La ventana. |
Retorna
El control.
window_focus_info ()
Obtiene información adicional sobre una operación de cambio del foco del teclado.
void window_focus_info(Window *window, FocusInfo *info);
window | La ventana. |
info | Estructura donde se retornarán los datos de la operación. |
Observaciones
En ocasiones, la decisión de liberar el foco del teclado para un control requiere información de contexto. Por ejemplo que acción originó el cambio (pulsar [TAB]
, clic sobre otro control) o cual será el control que recibirá el foco. Ver Protocolo del foco.
window_update ()
Recalcula la posición y tamaño de los controles tras modificar cualquier Layout.
void window_update(Window *window);
window | La ventana. |
window_origin ()
Mueve la ventana a unas coordenadas concretas del escritorio.
void window_origin(Window *window, const V2Df origin);
window | La ventana. |
origin | Posición |
window_size ()
Establece el tamaño del área cliente de la ventana.
void window_size(Window *window, const S2Df size);
window | La ventana. |
size | Tamaño del panel principal. |
Observaciones
El tamaño final dependerá de la configuración del marco de la ventana y del tema de escritorio. Esta medida solo hace referencia al área interior.
window_get_origin ()
Obtiene la posición de la ventana.
V2Df window_get_origin(const Window *window);
window | La ventana. |
Retorna
Posición (x,y)
de la esquina superior-izquierda de la ventana.
window_get_size ()
Obtiene las dimensiones totales de la ventana.
S2Df window_get_size(const Window *window);
window | La ventana. |
Retorna
Tamaño de la ventana.
Observaciones
Se tiene en cuenta el marco y barra de título.
window_get_client_size ()
Obtiene las dimensiones del área cliente de la ventana.
S2Df window_get_client_size(const Window *window);
window | La ventana. |
Retorna
Tamaño del panel principal.
window_control_frame ()
Obtiene la posición y tamaño de un control en coordenadas de ventana.
R2Df window_control_frame(const Window *window, const GuiControl *control);
window | La ventana. |
control | El control. |
Retorna
Rectángulo en coordenadas de ventana.
Observaciones
control
debe pertenecer a la ventana, estar activo y visible. El punto (0,0) corresponde con el vértice superior izquierdo del área cliente de la ventana. Ver Ventanas superpuestas.
window_client_to_screen ()
Transforma un punto expresado en coordenadas de ventana a coordenadas de pantalla.
V2Df window_client_to_screen(const Window *window, const V2Df point);
window | La ventana. |
point | El punto en coordenadas de ventana. |
Retorna
El punto en coordenadas de pantalla.
Observaciones
point
es un punto interior, donde (0,0) corresponde con el vértice superior izquierdo del área cliente de la ventana. Ver Ventanas superpuestas.
window_defbutton ()
Establece el botón por defecto de la ventana. Será activado cuando se pulse [Intro]
.
void window_defbutton(Window *window, Button *button);
window | La ventana. |
button | El botón. |
Observaciones
Esta función deshabilita el posible anterior botón por defecto. Para que se establezca el nuevo botón, este debe existir en el layout activo, lo que exige que esta función se llame después de window_panel
. Ver Botón por defecto.
window_cursor ()
Cambia el cursor del ratón.
void window_cursor(Window *window, const gui_cursor_t cursor, const Image *image, const real32_t hot_x, const real32_t hot_y);
window | La ventana. |
cursor | Identificador del nuevo cursor. |
image | Imagen personalizada. Solo válido en ekGUI_CURSOR_USER. |
hot_x | Coordenada x del punto de clic. Solo válido en ekGUI_CURSOR_USER. |
hot_y | Coordenada y del punto de clic. Solo válido en ekGUI_CURSOR_USER. |
Observaciones
hot_x, hot_y
indican el punto "sensible" dentro de la imagen, que indicará la posición exacta del ratón.