Hacer un seguimiento del widget enfocado en el tree de GUI

Estoy diseñando un marco de GUI basado en treees de widgets. Los widgets son estructuras generales de control y contenedores que son todo, desde paneles hasta botones y dialogs de files. Los widgets tienen una variedad de widgets para niños; un button contiene una label y tal vez un ícono. Los widgets son alimentados con events de input donde el widget primero hace su propia lógica con el evento y luego lo pasa a sus hijos. La aplicación alimenta los events de input a un único widget raíz, que puede ser desde un solo button en el medio de la pantalla hasta una window de control completa.

Necesito una forma de manejar qué widget (es) recibe events de keyboard. Decidí que uno de los widgets en un tree de widgets dado sería el widget enfocado. Los widgets tendrían un método gainFocus y un método loseFocus , que se usarían en respuesta a los events de clic del mouse o manualmente por la aplicación. Es poco probable que implemente las tabs. Cuando uno de los artilugios gana enfoque, cualquier artilugio enfocado existente, naturalmente, pierde el foco.

Todavía quiero que cada antecesor del widget enfocado vea sucesivamente un evento de keyboard determinado antes de que se le dé al widget enfocado. Entonces, si tengo un panel con un textbox, el panel vería el evento antes de que lo haga el textbox. Por lo tanto, realmente necesito hacer un seguimiento de la cadena de widgets desde el widget raíz al widget enfocado. También necesito mantener la integridad de los datos cuando cambio el foco para que todos los widgets en un tree puedan decir correctamente si son o no el widget enfocado o un antecesor del widget enfocado.

Hasta ahora he llegado a dar a los widgets un boolean hasFocus y un integer centradoChildIndex . hasFocus se establecerá en verdadero cuando el widget tenga foco. focusedChildIndex se establecería en el índice del elemento secundario enfocado en su matriz secundaria, que podría ser el widget enfocado en sí o el siguiente widget en la cadena que lleva al widget enfocado, o -1 para indicar que no hay un elemento secundario enfocado. Establecer estas variables cuando un widget en el tree obtiene su método gainFocus o loseFocus invocado es algo en lo que estoy perplejo.

Editar

Así que estamos claros, esto es lo que creo que será mi class de widgets:

class Widget private Widget parent private Array<Widget> children // Methods for getters, adding widgets to widget trees, etc. ... public void mouseClick(int x, int y) doMouseClickEvent(x, y) for c in children c.mouseClick(int x, int y) 

Como puede ver en mouseClick en el widget para ver el evento antes que sus hijos. Quiero que las inputs de keyboard funcionen de la misma manera, excepto que esta vez solo quiero tener una sola cadena desde la raíz hasta el widget enfocado para ver el evento. Además, tengo que asegurarme de que cuando un widget gana enfoque, el widget actualmente enfocado en el tree pierde el foco.

Estoy bastante seguro de que todos los widgets tienen información parental (excepto el widget raíz, por supuesto). De modo que puede volver hasta la raíz siempre que se presiona una tecla y pasar events a todos ellos. No estoy seguro si se encuentran campos / methods estáticos, pero se puede implementar fácilmente usando esos.

 class widget { public widget parent; // a referece to it's parent public static widget focusedWidget; public static void keypress(Key k) { stack <widget> widgets; widgets.push(focusedWidget); while(widgest.Top.parent != null) // first create a list of all widgets in the way back to root widgets.push(widgest.Top.parent); while(widgets.count != 0) // then calling all widgets in that list starting from root { widgets.Top.OnKeyPress(k); widgets.pop(); } } } 

también puede cambiar completamente la forma de ver el problema y agregar un widget enfocado a todos sus widgets. De esta forma, tendrá una pequeña ventaja de recordar qué widgets tenían foco cuando el usuario estaba trabajando con otro widget (tal vez haciendo algo en otra window). pero mantener un seguimiento de esto se vuelve un poco más difícil.

 class widget { public widget parent; // a referece to it's parent public widget focusedWidget; public void OnKeyPress(Key k) { // do something here focusedWidget.ObKeyPress(k); } public bool hasFocus{ get { if(parent == null || parent.hasFocus) return parent.focusedWidget == this; } } } 

De lo que estás hablando aquí es un sistema impulsado por events. Esto es lo que usan la mayoría de los sistemas de GUI. Siempre es bueno robarle a los maestros, echemos un vistazo al sistema impulsado por events de Flash.

enter image description here

En este diagtwig, el usuario hace clic en el cuadro rojo. Se genera un evento de clic con el cuadro rojo como destino. A medida que el evento viaja desde el escenario hasta la caja, se llama a cada oyente adjunto, lo que puede evitar que el evento viaje más lejos.

Por ejemplo, si el usuario hace clic en el recuadro rojo, pero el elemento raíz tiene el foco, entonces el elemento raíz puede evitar que el evento avance.

Una vez que el evento alcanza su objective en la fase de captura, se produce una fase de burbujeo opcional. Aquí, el evento viaja desde el elemento objective hasta la raíz. Esto es útil, por ejemplo, para decirle a un elemento padre que un elemento hijo debe tener foco.

El evento toma la siguiente ruta:

enter image description here

Entonces, ¿cómo implementarías esto en C ++? Bueno, eche un vistazo a la excelente libRocket . Han implementado un sistema GUI basado en events en C ++. La esencia de esto es que un evento debe ser un object real y que puede adjuntar detectores de events a widgets. El object de evento realiza un seguimiento de la fase en la que se encuentra y su objective. Los widgets pueden escuchar ciertos events y evitar que el evento progrese más.

El código de libRocket está bien documentado, sugiero echarle un vistazo para get ideas.

Las imágenes se tomaron de Introducción al event handling events en ActionScript 3.0 .

Creo que he encontrado una solución.

Como dije antes, puedo usar un boolean hasFocus para designar el widget enfocado real y un focusedChildIndex para indicar qué widget secundario conduce al widget enfocado.

 class Widget private Widget parent private Array<Widget> children // True if this is the focused widget private bool hasFocus // Index of child that leads to the focused widget private int focusedChildIndex new() ... hasFocus = false focusedChildIndex = -1 // Returns -1 if not an immediate child public int getIndexOfChild(Widget child) int index = -1 for int i = 0, children.size() - 1 if children[i] == child index = i break return index // Root of tree this widget is in, or widget itself if it is the root public Widget getRoot() Widget result = this Widget p = parent while p != null result = p p = p.getParent() return result public bool hasFocus() return hasFocus public int getFocusedChildIndex() return focusedChildIndex public void gainFocus() if not hasFocus getRoot().loseFocus() hasFocus = true onGainFocus() if parent != null parent.focusOnChild(this) // Used by gainFocus public void focusOnChild(Widget child) // This method can be run if // - The widget is not already focused on a child // - The given child is actually a child of this widget // - The child is either the focused widget or has a focused child int index = getIndexOfChild(child) if (focusedChildIndex == -1) and (index > -1) and (child.hasFocus() or (child.getFocusedChildIndex() > -1)) focusedChildIndex = index if parent != null parent.focusOnChild(this) public void loseFocus() if (parent != null) and (parent.getFocusedChildIndex() > -1) parent.loseFocus() else if hasFocus hasFocus = false onLoseFocus() if focusedChildIndex > -1 int oldIndex = focusedChildIndex focusedChildIndex = -1 children[oldIndex].loseFocus() protected virtual void onGainFocus() protected virtual void onLoseFocus()