Es difícil construir el interfaz gráfico de una aplicación programando a nivel de Xlib. X Toolkit intenta facilitar esta labor pero, siguiendo la filosofía de incluir mecanismos pero no políticas, no define un conjunto fijo de componentes de diálogo sino un mecanismo general para manejar objetos de diálogo (widgets) construidos por la aplicación o que forman parte de una biblioteca predefinida como Motif.
X Toolkit presenta un modelo de programación dirigido por los eventos (por el usuario) frente al modelo dirigido por la aplicación de Xlib.
Las widgets están organizadas en clases siguiendo un modelo orientado a objetos. Cada clase hereda todas las características de la superclase de la que deriva definiendo únicamente sus características específicas. Cuando un programa crea una widget (XtCreateWidget) de una clase está creando una instancia de esa clase.
Cada clase tiene asociados un conjunto de recursos (algunos propios otros heredados) cuyos valores deben especificarse cuando se crea una instancia. Esta especificación puede estar en el programa usando XtSetValues (vector de argumentos) o XtVaSetValues (lista de argumentos), o puede haber sido definida por el usuario usando el mecanismo general de especificación de recursos.
Un tipo de recurso que incluye la mayoría de las clases de widgets son las listas de callback. La aplicación puede asociar una función propia con una lista de callback usando XtAddCallback. Cuando se cumplen determinadas condiciones en la widget se invocará a la función especificada por la aplicación.
En Xt existen algunas clases predefinidas (metaclases) que generalmente no se instancian:
Todas las clases no son normalmente instanciadas sino que sirven como superclase para el resto de clases, ya sean programadas por la aplicación o, más típicamente, contenidas en una librería predefinida como Motif.
A continuación veremos el modo de operación de las widgets y, como consecuencia de éste, la típica estructura de la aplicación que usa Xt:
Generalmente, las aplicaciones que usan Xt no necesitan tratar con eventos sino con listas de callbacks. Sin embargo, Xt proporciona facilidades para manejar directamente eventos permitiendo que la aplicación pueda asociar una función a la ocurrencia de uno o varios eventos. Para ello se usa XtAddEventHandler en la que se especifica la función que se invocará cuando suceda en la ventana asociada a la widget alguno de los eventos que se seleccionan en una máscara. Cuando el programa invoque XtAppMainLoop la ocurrencia de alguno de estos eventos dará lugar a la ejecución de la función asociada que recibirá como parámetros, entre otros, la widget donde se produjo el evento y el tipo de evento.
Normalmente las aplicaciones no manejan directamente eventos sino listas de callbacks que proporcionan una abstracción de más alto nivel. La activación de un callback está vinculada a la ocurrencia de uno o varios eventos, pero esta asociación puede ser modificada permitiendo que el código de la aplicación sea independiente de los eventos que maneja (ver traducciones). Cada clase de widget define varios recursos de tipo callback. Cuando un programa crea una instancia de esa clase puede asociar funciones a dichos recursos usando XtAddCallback. Durante el bucle de tratamiento de eventos (XtAppMainLoop), la activación del callback causará que se ejecute la función.
Por ejemplo, la clase PushButton de Motif incluye, entre otros, el recurso XmNactivateCallback. Este callback indica la activación del botón de diálogo que, por defecto, está asociado a la ocurrencia del evento que indica que el botón 1 del ratón se ha soltado en la ventana asociada a la widget. Sin embargo, esta asociación puede ser modificada.
Aunque hasta ahora hemos hablado de asociaciones entre eventos y callbacks, realmente existe una asociación entre eventos y acciones mediante la tabla de traducciones.
Una acción es una función interna de la widget que se invocará cuando se produzcan los eventos especificados. Normalmente, las acciones, además de realizar los cambios pertinentes sobre la widget, acaban activando algún callback. Por ejemplo, la clase PushButton de Motif incluye la acción Activate que después de modificar el dibujo del botón en pantalla, activa XmNactivateCallback. En algunos casos, sin embargo, la acción no activa ningún callback. Por ejemplo, la clase Text de Motif incluye una acción forward-character que sólo avanza el cursor un carácter sin invocar ningún callback.
Adicionalmente, un programa, con el objetivo de aumentar la funcionalidad de una widget, puede definir nuevas acciones asociadas a funciones del programa que, como ocurre con las acciones predefinidas, estarán asociadas a eventos de forma configurable.
El siguiente ejemplo tomado del fichero de recursos del netscape muestra un fragmento de la definición de una tabla de traducciones que modifica el comportamiento por defecto de las widgets de la clase XmTextField que se usen en este programa.
*XmTextField.translations: #override \n\ ~Meta ~Alt Ctrl<Key>a: beginning-of-line() \n\ ~Meta ~Alt Ctrl<Key>b: backward-character() \n\ ~Meta ~Alt Ctrl<Key>d: delete-next-character() \n\ ~Meta ~Alt Ctrl<Key>e: end-of-line() \n\ ~Meta ~Alt Ctrl<Key>f: forward-character() \n\ ~Meta ~Alt Ctrl<Key>g: process-cancel() \n\ ~Meta ~Alt Ctrl<Key>h: delete-previous-character() \n\ ~Meta ~Alt Ctrl<Key>k: delete-to-end-of-line() \n\ ~Meta ~Alt Ctrl<Key>u: beginning-of-line() \ delete-to-end-of-line() \n\ ~Meta ~Alt Ctrl<Key>w: cut-clipboard() \n\ Meta ~Ctrl<Key>b: backward-word() \n\ Alt ~Ctrl<Key>b: backward-word() \n\ Meta ~Ctrl<Key>d: delete-next-word() \n\ Alt ~Ctrl<Key>d: delete-next-word() \n\ Meta ~Ctrl<Key>f: forward-word() \n\ Alt ~Ctrl<Key>f: forward-word() \n\ Meta ~Ctrl<Key>w: copy-clipboard() \n\ Alt ~Ctrl<Key>w: copy-clipboard() \n\ ....................................................................La tabla comienza con una directiva (#override) que indica cómo se mezclará esta tabla con la que actualmente está activa. En este caso indica que cuando una traducción entre en conflicto con una ya existente, prevalecerá la nueva. A continuación existe una línea por cada traducción. Cada línea asocia una secuencia de eventos (por ejemplo, <Key>w indica el evento de la tecla correspondiente a la w pulsada) asociados a una acción (por ejemplo cut-clipboard()). Cada línea puede comenzar con una lista de modificadores que tendrán que estar activados (o desactivados si tienen el carácter ~ delante) para que se cumpla la regla (por ejemplo, ~Meta ~Alt Ctrl indica que deben estar desactivados los modificadores meta y alt pero activado control).
Cada widget tiene un recurso XtNaccelerators heredado de la clase Core que contiene la tabla de definición de aceleradores. El formato y la forma de especificar esta tabla es idéntico a la de traducciones. Una vez definido este recurso en una widget, se dede llamar a XtInstallAccelerators para especificar otra widget en la que la ocurrencia de los eventos establecidos activará las correspondientes acciones de la primera widget.
Por ejemplo, supongamos una widget contenedora que incluye un botón (clase PushButton en Motif) y queremos que éste se active, además de con el ratón, cuando se pulse la tecla de retorno de carro tanto si el puntero está en la ventana del botón como si está en la ventana contenedora. En este caso, se debería definir un acelerador asociado a la widget del botón e "instalarlo" en la widget contenedora.
Debido a la estructura de un programa que usa Xt, que después de llamar a XtAppMainLoop se convierte en pasivo, es difícil manejar otros eventos no relacionados con X. Para facilitar esta labor, Xt permite que algunos eventos externos se puedan tratar de una forma similar a los propios de X, esto es, dejando que el programa pueda asociar funciones con la ocurrencia de dichos eventos.
La principal diferencia entre la gestión de los recursos en Xt respecto a Xlib es que en este nivel superior no se trata de una convención del programador sino que es una labor de Xt. Los principales aspectos de la gestión de recursos en Xt son los siguientes:
Volver al índice ; Próximo: Motif ; Previo: Usuario de X