Xojo incluye de serie una buena biblioteca de controles gráficos listos para usar. Ya sabes, se trata de las piezas mediante las cuales puedes diseñar las interfaces de usuario de tus aplicaciones, repletas de propiedades, métodos y eventos que te permiten añadir el comportamiento específico que esperas de ellos para tus proyectos. Pero si lo que necesitas es crear tus propios controles, la respuesta está en el control Canvas.
Además, al tratarse de un entorno de desarrollo multiplataforma, verás que Xojo adapta en muchos casos cada uno de los controles a su función específica sobre la plataforma de despliegue; tal y como ocurre, por ejemplo, con el ListBox, los botones o incluso los campos de texto. Pero, ¿qué ocurre cuando lo que deseamos es crear nuestros propios controles, desde cero? Pues bien, a continuación veremos como hacerlo basándonos en dicho control Canvas.
Como su propio nombre indica, el control Canvas de Xojo —disponible en las ediciones Desktop, Web, iOS y Raspberry Pi— es un verdadero lienzo en el que puedes dibujar su aspecto gráfico sobre la marcha, utilizando para ello el evento Paint y el conjunto de clases gráficas disponibles en Xojo, o bien asignando simplemente una imagen como fondo del control y que puedes cambiar en función de cada uno de los posibles estados, como por ejemplo cuando el apuntador del ratón está sobre el área del control, el apuntador ha abandonado dicha área, o bien se ha hecho clic sobre el control propiamente dicho.
Canvas: cuestión de eventos
Es por ello que el control Canvas proporciona todo tipo de eventos, de modo que tu control personalizado pueda implementarlos para reaccionar antes las acciones realizadas por los usuarios; solo has de echar un vistazo a la documentación disponible para darte cuenta de ello.
Ahora bien, si bien puedes arrastrar controles Canvas directamente sobre tu proyecto, implementar los eventos que deseas capturar y escribir el código que ha de ejecutar en respuesta cada uno de ellos, lo más razonable es que si estás utilizando un Canvas es porque quieras crear un control personalizado… que utilices en más de un proyecto, ¿cierto? Y esto nos lleva a la creación de tu propia subclase a partir del Canvas.
Tal y como hemos visto en otras entradas, la creación de una subclase responde a la necesidad de implementar un comportamiento específico para un control o clase ya existente, y en el caso de los Canvas no es distinto.
Crear una subclase en Xojo no puede ser más sencillo. Puedes arrastrar un elemento Canvas desde el panel Library sobre el Navegador de Proyecto (la columna situada más a la izquierda en el IDE) o bien elegir
desde la barra de herramientas del IDE o desde el menú Insert > Class
.Insert
Puntos, no píxeles: HiDPI y Retina
En cualquiera de los casos, una vez añadida la nueva clase al proyecto, has de seleccionar el elemento recién añadido, cambiar al panel Inspector, asignar el nombre que tendrá tu nueva clase y definir la clase superior a Canvas. De este modo nuestra nueva clase heredará todas las propiedades, métodos y eventos definidos sobre dicha clase.
Esto es lo que haremos en este tutorial para crear un botón personalizado que varía su imagen en función de que el ratón entre sobre el área del botón, salga de dicha área o bien se haga clic sobre el control, cambiando en este caso no solo la imagen sino también el estado interno del botón almacenado mediante una propiedad de tipo Booleano.
Dado que nuestro control personalizado no va a dibujarse sobre la marcha mediante el evento Paint, tendremos que diseñar las imágenes de forma concreta al tamaño exacto que tendrá el control que diseñemos (en este ejemplo 32 x 32 puntos).
Y sí, hay que hablar de puntos en vez de píxeles dado que las aplicaciones Xojo soportan HiDPI sobre Windows y Retina en macOS; es decir, gráficos de alta resolución. Por ello es importante que a la hora de diseñar las imágenes a utilizar en tus controles personalizados emplees una herramienta de diseño gráfico que simplifique el proceso. Personalmente utilizo Sketch para macOS, dado que es vectorial y permite generar automáticamente las versiones de doble densidad de píxeles que utilizará automáticamente Xojo cuando se ejecute la app sobre equipos compatibles HiDPI/Retina. (Dicha capacidad está disponible para cualquier aplicación en la que se active el soporte HiDPI/Retina desde los ajustes de Compilación en el IDE de Xojo.)
¿Qué ocurriría con el soporte HiDPI/Retina en el caso de que fuésemos nosotros mismos los responsables de dibujar los gráficos sobre la marcha? Pues bien, en tal caso estaríamos dibujando siempre en puntos de modo que Xojo continuaría realizando los cálculos de dibujado correctamente sobre el evento Paint, y en el caso de que necesitásemos acceder realmente a cada uno de los píxeles de las imágenes, entonces tendríamos que utilizar la clase RGBSurface, para lo cual también nos vendrá de perlas implementar el evento ScaleFactorChanged de la clase Canvas.
A eventos consumidos… eventos definidos
Dado que la clase en cuestión consume los eventos Open, MouseEnter, MouseExit y MouseDown, hemos de asegurarnos de definir esos mismos eventos sobre la clase, replicando los parámetros pasados y también los tipos devueltos (cuando proceda), de modo que los consumidores de nuestra clase puedan utilizarlos también para ajustar aun más el comportamiento del control a las necesidades específicas de la aplicación que estén creando.
Por tanto, una vez definidos los eventos consumidos, hemos de asegurarnos de llamarlos mediante la palabra clave RaiseEvent al final del código que ejecutaremos sobre el evento consumido. Por ejemplo, así quedaría la implementación del evento MouseDown en nuestra subclase de Canvas:
me.Backdrop = if(me.state = false, ShellRunSelected, ShellRun)
me.state = not me.state
return RaiseEvent mousedown(X, y)
Observa que en este caso, nuestra implementación del evento MouseDown devolverá el valor especificado por quien vaya a implementar nuestro control sobre el proyecto. Es decir, por omisión el evento MouseDown de la clase Canvas espera que se devuelva un valor booleano, del cual dependerá de la gestión del mismo evento en la cadena de eventos formada por la disposición de los controles sobre la ventana. Por tanto, nuestra responsabilidad será la de devolver el valor más neutro que podamos, y este es el que decida el programador que utilice nuestra subclase en cada uno de los proyectos. Quizá en algunos casos precise devolver ‘True’ (el control captura y trata el evento), mientras que en otros puede devolver ‘False’ (deja en manos del sistema el evento para que de la oportunidad de su tratamiento en el resto de los controles).
Unir todas las piezas
Con la anterior información en nuestras manos, y los conceptos básicos ya indicados, en el siguiente vídeo puedes ver como unimos todas las piezas: gráficos, eventos, definición de eventos y propiedades, para crear un control desde cero. Obviamente, a partir de aquí te sugiero que consultes la documentación sobre el control Canvas para ver en qué medida puedas emplear las Propiedades, Eventos y Métodos de la clase en el diseño de tus propios controles. Por ejemplo, desde los más sencillos botones personalizados a la creación de gráficas que representen de forma dinámica los valores recibidos. ¡Las posibilidades son infinitas!
*Esta entrada ha sido escrita en Markdown y exportada como HTML para este blog con Snippery