Cómo programar componentes en Gambas

Introducción

Toda la información a continuación es para Gambas 3.

  1. En la pestaña "General" del propieades de proyecto, debe cambiar el "project type" a "Component".

  2. Entonces la pestaña "Information" se hace disponible.

    Debe llenarse con los siguientes detalles:
    • El progreso del componente (es decir, si es estable o no).

    • Las características requeridas.

    • Los componentes requeridos.

    • Los componentes excluidos.

  3. Algunas de las clases de proyectos se exportan.

  4. Las clases exportadas que son controles deben declarar algunas constantes públicas especiales que le dirán al IDE toda la información necesaria para administrar el control.

Muchos componentes oficiales de gambas ya están escritos en gambas, y puedes usarlos como ejemplos. El código fuente de estos componentes se encuentra en el

Esta es la lista de dichos componentes: Algunos componentes escritos en C/C++ tienen también partes escritas en gambas: El código fuente de la parte de gambas está dentro del directorio fuente de ese componente. Por ejemplo, la parte de gambas de gb.qt4 está en /gb.qt4/src.

Clases Exportadas

El propósito de un componente es proporcionar nuevas clases al intérprete de Gambas.

Las clases que queremos proveer deben ser exportadas desde nuestro proyecto, de otro modo el usuario de tu proyecto no podrá verlas o usarlas.

Para marcar una clase como exportada, sólo hay que añadir la palabra clave EXPORT al principio del código de la clase.

Si un método o una propiedad de una clase exportada devuelve un objeto de otra clase declarada en tu componente, entonces esa otra clase debería de ser exportada igualmente. De otro modo, el usuario no podrá guardar una referencia a ella, a menos que use el tipo de dato Object.

Si una clase exportada hereda de otra clase declarada en tu componente, esta otra clase debería de ser exportada también. De otro modo, se producirá un error cuando el intérprete intente cargar el componente.

Un buen ejemplo de un componente sencillo con clases exportadas es gb.settings y su clase Settings.

Clases Exportadas Ocultas

Si tienes que exportar algunas clases que no quieres que sean visibles al usuario, puedes ocultarlas simplemente comenzando su nombre con un guión bajo.

Por ejemplo, mira el código fuente de gb.desktop y verás muchas clases exportadas que no son visibles explícitamente.

Con esto, podemos reemplazar el concepto de "clase " existente sólo en componentes escritos en C/C++.

Controles

Los controles son clases exportadas especiales que aparecen en la caja de herramientas de controles del IDE.

Los controles son generalmente elementos gráficos que muestran algo y que sirven para interactuar con el usuario. Pero también pueden ser clases normales y no tener una parte gráfica, como el control Timer. En estos casos los llamamos controles es.

Los componentes gráficos (gb.qt4 o gb.gtk) proveen dos clases que deberías usar como "clases padre" para crear tus propio controles: UserControl y UserContainer son realmente clases hijas de la clase Container. De esta forma, puedes crear nuevos controles o nuevos contenedores mezclando controles o contenedores ya existentes.
Para un buen ejemplo de uso de UserControl, mira el código fuente del control FileView. Este control contiene un TreeView y un IconView dentro de un UserControl, mostrando uno de los dos, según el tipo de vista requerido por el usuario.

Para otro ejemplo de uso de UserContainer, mira el código fuente del control ListContainer. Este control es un contenedor que permite crear un ListBox cuyos ítems son otros controles.

Los controles tienen que declarar algunos tipos de constantes ocultas que permiten al IDE gestionar el control:

Constante Tipo Por defecto Descripción
_IsControl Boolean FALSE Esta propiedad debe establecerse a TRUE, para que una clase exportada se convierta en un control.
_IsContainer Boolean FALSE Si un control es un contenedor, es decir, si puedes poner otros controles dentro usando el editor de formularios del IDE.
_IsMultiContainer Boolean FALSE si un control es un contenedor múltiple, como el TabStrip.
_IsVirtual Boolean FALSE Si un control es virtual, es decir, una clase normal que puedes poner en el editor de formularios, como el control Timer o la clase Printer.
_IsForm Boolean FALSE Si un control es realmente un formulario, por ejemplo el contenedor de nivel superior de todos los controles de la misma familia.
_Family String "*" La familia del control, esto es, el tipo de objeto de nivel superior donde se ha de colocar el control. Por ejemplo: "Form" para un control Form, "Report" para un control Report, and "*" para un control que se puede colocar en cualquier lugar.
_Group String _Family El nombre de la pestaña de la caja de herramientas del IDE donde se mostrará el icono del control. Por defecto se usará el nombre de la familia, pero si no se ha establecido se usará el grupo "Special".
_Similar String "" Una lista separada por comas de grupos similares. El editor de formularios del IDE permite reemplazar un control por cualquier otro que comparta los mismos grupos de similaridad. Normalmente un grupo de similaridad es el nombre de un control.
_Properties String "" La lista de propiedades del control. Ver más abajo.
_DefaultEvent String "" El evento por defecto. Ver más abajo.
_DefaultSize String "" El tamaño por defecto del control. Ver más abajo.
_DrawWith String "" El control gráfico real empleado para dibujar el control dentro del editor de formularios. Ver más abajo.
_Arrangement Integer 0 La disposición automática del control cuando éste es un contenedor y si éste dispone la posición de sus elementos hijos automáticamente. Ver más abajo.

Sólo las constantes _IsControl y _Properties son obligatorias.

Estas constantes ocultas se heredan como cualquier otra constante. Así que no necesitas declararlas en cada control, ya que hay una relación hereditaria entre ellas.

La constante _Properties

Esta constante es la más importante y además es obligatoria. Describe todas las propiedades que aparecerán en la hoja de propiedades del IDE para ese control, su tipo, sus valores por defecto y otra información dependiente del tipo de propiedad.

Sintaxis

Esta constante especial es una cadena con la siguiente sintaxis:

PUBLIC CONST _Properties AS String = " [ * , ] Propiedad1 , Propiedad2 , ... "

Cada propiedad tiene la siguiente sintaxis:

[-] Nombre [ { Tipo [ Argumentos ] } ] = Default

  • Nombre es el nombre de la propiedad. Por supuesto, la propiedad debe ser declarada e implementada en el código del control.

  • Tipo es el tipo de propiedad (N. del T.: Tipo como sinónimo de "especie", no como tipo de dato o datatype). Puede ser más exacto que el tipo de dato de la propiedad. Por ejemplo, "Color" significará que la propiedad es un entero, pero la hoja de propiedades del control en el IDE abrirá un selector de colores para definir su valor. Si no se define, el tipo de propiedad es su tipo de dato (datatype).

  • Argumentos son argumentos opcionales que dependen del valor de Tipo.

  • Default es el valor por defecto de la propiedad. La sintaxis aquí depende del tipo de propiedad.

La primera propiedad puede ser un asterisco, de forma que el control adquiere automáticamente todas las propiedades declaradas en la constante _Properties de su clase ascendiente. En ese caso, un nombre de propiedad puede comenzar con un guión medio (-) para que no aparezca en la lista de propiedades heredada de la clase padre.

Tipos de Propiedades

Aquí están los diferentes valores de Tipo soportados hasta el momento:

Tipo Propiedad Descripción Argumentos
Color Un entero que representa un color.

El IDE mostrará un selector de color emergente para editar el valor de esta propiedad.
Font Una fuente de caracteres.

El IDE mostrará un selector de fuentes emergente para editar el valor de esta propiedad.
Font [ :Fixed ]
Usar Font:Fixed para permitir fuentes fijas solamente.
Path Una ruta a un fichero.

El IDE mostrará un selector de ficheror emergente para editar el valor de esta propiedad.
Picture Una imagen localizada en el el directorio del proyecto o un icono de stock. El IDE mostrará un selector de imágenes para editar el valor de esta propiedad.

Range Un rango de números enteros con un mínimo y un máximo.

El IDE usará un control SpinBox para editar el valor de esta propiedad.
Range:Mínimo;Máximo

El valor Default value debe coincidir con el tipo de dato de la propiedad. Para propiedades booleanas, se puede especificar "True" o "False" como valor por defecto.

Hay que ser muy prudente cuando definamos (o no) el valor Default de una propiedad.

  • Si no se especifica, el valor por defecto que toma es el valor por defecto asociado con el tipo de dato de la propiedad, (FALSE para una propiedad Boolean, 0 para una numérica, etc.).

  • El valor por defecto debe coincidir con la implementación de la propiedad, porque cuando una propiedad se establece a su valor por defecto en el editor de formularios del IDE, no se genera ningún código para inicializar la propiedad en tiempo de ejecución.

Listas de Constantes

Para propiedades que toman su valor de una lista de constantes predefinidas de la misma clase, puedes especificar una clase en lugar de un tipo de propiedad, con una lista opcional de constantes.

Esta es la sintaxis:

Tipo de Propiedad Descripción Argumentos
Nombre de clase Una lista de constantes.

El IDE usará un control ComboBox para editar el valor de la propiedad, y lo llenará con las constantes especificadas.
Class . ( * | Constante1 ; Constante2 ; ... ) [ = Default ]

Si usamos un asterisco en vez de una lista de propiedades, se usarán todas las constantes de la clase especificada.

Si se especifica, el valor de Default debe ser el nombre de una de las constantes, no su valor real.If specified, the value of _Default must be the name of one of the constants. Not its real value!

Ejemplos

Por ejemplo, el valor de Control._Properties es:
X{Position},Y{Position},Width{Dimension},Height{Dimension},Visible=True,Enabled=True,Font{Font},
Background{Color}=-1,Foreground{Color}=-1,Tag,
Mouse{Mouse.Default;Blank;Arrow;Cross;Wait;Text;SizeAll;SizeH;SizeV;SizeN;SizeS;SizeW;SizeE;SizeNWSE;SizeNESW;SplitH;SplitV;Pointing}=Default,
ToolTip,Drop,Expand,Ignore

Es heredado por todos los otros controles y contenedores.

Este es el valor de ListBox._Properties:
*,List,Mode{Select.*}=Single,Sorted

Las otras Constantes Especiales

La constante _DefaultEvent

Esta constante es una cadena que representa el evento por defecto del control. Este evento por defecto es el que se usa cuando de hace doble clic en un control en el editor de formularios del IDE.

Por ejemplo:
PUBLIC CONST _DefaultEvent AS String = "Click"

Esta constante es opcional, pero es muy conveniente declararla.

La constante _DefaultSize

Esta constante es una cadena que representa el tamaño por defecto del control cuando se suelta encima del editor de formularios al arrastrarlo desde la caja de herramientas de controles. Es su anchura y su altura como un múltiplo de Desktop.Scale, separado por comas.

Un ejemplo:
PUBLIC CONST _DefaultSize AS String = "36,36"

Esta constante es opcional. Si no se declara, el IDE tratará de hacer lo mejor posible.

La constante _DrawWith

Esta constante es una cadena que indica al IDE el control que se va a usar para dibujarlo en el editor de formularios.

Por defecto, los controles que son miembros de gb.qt4, /edit/comp/gb.qt.ext, gb.form y gb.form.mdi se dibujan siendo instanciados con el juego de propiedades Design.

Si un control no es miembro de dichos componentes, entonces el IDE dibujará un marco con el icono del control y su nombre dentro.

Al definir esta constante, el IDE no usará un DrawingArea, sino el control que se especifique.

Por ejemplo:
PUBLIC CONST _DrawWith AS String = "TextBox"

La constante _Arrangement

Esta constante es un valor de tipo cadena que representa el estilo de disposición que usa el IDE para ordenar los controles hijos dentro de un contenedor, antes de salvarlos en el disco duro.

El valor puede ser una de las siguientes cadenas:

Constante Disposición
"H" Arrange.Horizontal
"V" Arrange.Vertical
"R" Arrange.Row
"C" Arrange.Column
"F" Usa el valor real de la propiedad Arrange.

Por ejemplo:
PUBLIC CONST _DefaultArrangement AS Integer = "V" ' Arrange.Vertical

Esa constante se usa solamente si el control es un contenedor y es opcional. Si no se define, no se fuerza su disposición.

Iconos de control

Cada control debe tener un icono para mostrarse en la caja de herramientas del IDE.

Para proporcionar los iconos de los controles de tu componente, debes crear un directorio /control en la raíz de tu proyecto, y colocar un fichero PNG para cada control.

En Gambas 3, el directorio del control se debe crear dentro del directorio .hidden. Esa es la sección "Proyecto" en la vista de árbol del IDE.

El nombre del icono de un control debe ser el nombre de clase del control en minúsculas con la extensión .png.

Por ejemplo:

$ cd gb.db.form
$ cd control
$ ls
databrowser.png  datacombo.png  datacontrol.png  datasource.png  datatree.png  dataview.png

Requerimientos del componente

Se pueden especificar los requerimientos del componente en la pestaña "Requiere" del diálogo de propiedades del proyecto.

Observa que esta pestaña no es visible cuando un proyecto no es un componente.

En esta pestaña se listarán todas las dependencias de tu componente sobre otros componentes.

La sección "Características" tiene cuatro check-boxes, cada una con una característica proporcionada por uno o varios componentes. Esta sección se usa cuando tu componente necesita una característica específica que no depende de un un componente en especial.

Si realmente se necesita algún componente específico, se pueden seleccionar en la sección "_Componentes_" de esta pestaña.

Los componentes seleccionados en la pestaña "Componentes" del diálogo de propiedades del proyecto, no tienen nada que ver con los componentes especificados en la pestaña "Requiere" de las propiedades del componente.

Éstos sólo se usan para correr el proyecto del componente desde el IDE con fines de depuración.

Depuración, instalación y empaquetado del componente

Depuración

Para depurar tu componente, directamente se puede usar y correr el proyecto tal cual.

La clase de inicio del proyecto y los componentes chequeados en la pestaña "Componentes" del diálogo de propiedades de proyecto no interfiere en absoluto con el comportamiento del componente una vez instalado y en uso por otros proyectos.

Empaquetado

El IDE puede crear paquetes binarios de tu componente, igual que haría para cualquier otro proyecto normal.

Sólo hay que definir menos opciones: un componente no tiene entrada de menú, por ejemplo.

El paquete binario instalará el componente a nivel global y se podrá usar igual que cualquier otro componente proporcionado por Gambas.

Si quieres distribuir tu componente, es muy importante nombrarlo siguiendo un esquema muy preciso, para que no haya conflictos entre paquetes.

El nombre de tu componente tiene que ser: gambas3-/vendedor/-/nombre/

  • gambas3 es el prefijo para todos los componentes.

  • vendedor es el nombre de vendedor. Para componentes oficiales de gambas, el nombre de vendedor es simplemente gb.

  • nombre es el nombre del componente.

En caso de que el asistente de empaquetado permita insertar la cadena de vendedor en el nombre del paquete, por favor evita hacerlo.

Coloca el nombre de vendedor dentro del nombre de proyecto del componente, de forma que aparezca en el nombre del componente final y sea visible para el usuario.

Por el momento, hay un fallo en el empaquetador que no tiene en cuenta la versión del proyecto del componente al generar las dependencias del paquete. En consecuencia, hay que establecer la versión de tu componente a la versión de Gambas actual!

Conclusión

He intentado encontrar la forma más fácil de crear componentes usando el IDE. Aún no es perfecta, pero si tienes alguna pregunta o comentario, por favor usa la lista de correo.

Ten en cuenta que algunas cosas cambiarán seguramente antes de la versión final de Gambas 3.

Y por supuesto, si quieres corregir o mejorar este artículo, bienvenido seas!