Traducido desde: https://blog.leahhanson.us/post/haiku-first-gui-program.html
Leah Hanson
Este es un recorrido para crear su primer programa GUI para Haiku. Estamos escribiendo una aplicación súper simple usando solo la API incorporada.
Suposiciones acerca de usted
- Haiku y PaladinSupongo que ha instalado Haiku y ha usado
installoptionalpackages
deinstalloptionalpackages
para obtener el IDE de Paladin. - OOP y punterosEstamos escribiendo el programa en C ++ porque eso es lo que ha escrito Haiku y sus API. Si estás familiarizado con la POO basada en clases y con los punteros C, deberías estar muy bien equipado para seguir esto.
Documentación API Haiku
La principal documentación de la API parece ser el BeBook . Encontrarás un PDF de él en tu escritorio en Haiku.Me resulta un poco difícil de seguir, pero es muy útil para buscar la API específica disponible.
Para averiguar cómo usar realmente la API descrita en el BeBook, estoy usando las lecciones 14 y 15 deAprender a programar con Haiku de DarkWyrm. Me salté la mayor parte del libro, pero hice la Revisión de la Unidad para verificar que no me estaba perdiendo mucho. Por lo que puedo decir, las cosas específicas de Haiku no comienzan hasta la Lección 14.
Primero, daré algunas instrucciones breves sobre la configuración del proyecto en Paladin y proporcionaré el código real. Puedes compilar y ejecutar el código para probar que funciona. Luego, explicaré mi comprensión de lo que sucede cuando se ejecuta el código.
La parte real de hacer cosas
Para empezar, abre el IDE de Paladín.
- Crear un nuevo proyecto (vacío) en Paladin
- Desde el menú "Proyecto" en la ventana de Paladín, seleccione "Agregar nuevo archivo".
- Cree un archivo llamado
App.cpp
y deje que Paladin cree un archivo de encabezado para usted también. - Abra App.cpp en el editor de Paladin (haga doble clic en el nombre del archivo). Escriba lo siguiente en el archivo:
#include "App.h" #include <Window.h> #include <Button.h> #include <View.h> #include <String.h> enum { M_BUTTON_CLICKED = ' btcl ' }; App :: App( void ) : BApplication( "application/x-vnd.lh-MyFirstApp" ) { //initialize counter fCount = 0 ; //create window BRect frame ( 100 , 100 , 500 , 400 ); myWindow = new BWindow(frame, "My First App" , B_TITLED_WINDOW , B_ASYNCHRONOUS_CONTROLS | B_QUIT_ON_WINDOW_CLOSE); //set up button and add to window BButton * button = new BButton(BRect( 10 , 10 , 11 , 11 ), "button" , "Click Me!" , new BMessage(M_BUTTON_CLICKED)); button -> SetTarget( this ); button -> ResizeToPreferred(); myWindow -> AddChild(button); //actually display the window myWindow -> Show(); } void App :: MessageReceived(BMessage * msg) { switch (msg -> what) { case M_BUTTON_CLICKED: { fCount ++ ; BString labelString ( "Clicks: " ); labelString << fCount; myWindow -> SetTitle(labelString.String()); break ; } default : { BApplication :: MessageReceived(msg); break ; } } } int main ( void ) { App * app = new App(); app -> Run(); delete app; return 0 ; }
- Mientras se encuentra en la ventana del editor de App.cpp, presione
Alt+Tab
para abrir el archivo de encabezado de App.h. Edita App.h para que contenga lo siguiente:#ifndef APP_H #define APP_H #include <Application.h> class App : public BApplication { public : App( void ); void MessageReceived (BMessage * msg); private : int32 fCount; BWindow * myWindow; }; #endif
- Desde cualquiera de las ventanas del editor, use
Alt+R
para compilar y ejecutar el programa. - Suponiendo que no haya errores tipográficos, debería ver aparecer una nueva ventana. El título de la ventana será "Mi primera aplicación" y tendrá un botón "Haga clic en mí". Si hace clic en el botón, la barra de título cambiará a "Clics: 1". Los clics repetidos aumentarán el número en la barra de título.
Cómo creo que funciona
Este ejemplo se basa en el ejemplo de la Lección 14. Luego lo edité para tener la misma funcionalidad en el ejemplo de la Lección 15. No se parece mucho a la Lección 15 porque me cansé de seguir exactamente las instrucciones y pensé que hacer un archivo completamente nuevo solo para la ventana principal parecía una tontería.
El archivo de encabezado
En el archivo de encabezado, hacemos algunas cosas importantes.
La línea
#include <Application.h>
Es como importamos las cosas en el kit de aplicaciones de Haiku. Esto incluye el manejo de mensajes y la clase de aplicación de
BApplication
. class App : public BApplication
Esta definición de clase dice que estamos creando una clase, llamada
App
, que es una subclase de la aplicación BApplication
. Ser una subclase de BApplication
nos proporciona implementaciones predeterminadas de varios métodos útiles, como MessageReceived
y Run
. public : App( void ); void MessageReceived (BMessage * msg);
Estamos definiendo dos métodos públicos
App(void)
y MessageReceived(BMessage *msg)
. App
es el constructor, que no tiene argumentos. MessageReceived
es cómo nuestra aplicación puede recibir mensajes (de sí misma, de otras aplicaciones, del sistema operativo). Está sobrecargando una función del mismo nombre en la clase de aplicación BApplication
. Un BMessage
tiene algunos campos, pero la única parte que usaremos hoy es el campo que. Este es un int
que se usa como el tema de un correo electrónico. private : int32 fCount; BWindow * myWindow;
Nuestros miembros privados,
fCount
y myWindow
, son los datos que nuestro constructor y la función de manejo de mensajes necesitarán compartir, y que nuestra función de manejo de mensajería deberá guardar entre invocaciones. fCount
es el contador de "cuántas veces se ha hecho clic en el botón". myWindow
es solo un puntero a nuestra ventana principal.El archivo de clase
El archivo de clase es donde cumplimos con el contrato que hicimos en el archivo de encabezado. Definiremos una constante, nuestro constructor y controlador de mensajes, y una función
main
para orquestar todo.
La constante M_BUTTON_CLICKED
En App.cpp, comenzamos por definir una enumeración.
enum { M_BUTTON_CLICKED = ' btcl ' };
DarkWyrm dice que es mejor usar una enumeración en lugar de
#define
aquí, pero no explica por qué. La enumeración que definimos anteriormente es equivalente a esta línea: #define M_BUTTON_CLICKED 'btcl'
'btcl'
es una constante de múltiples caracteres, como aprendí de esta Respuesta de StackOverflow . Las constantes de múltiples caracteres son una característica específica de la implementación que implementa gcc / g ++. En nuestro código, M_BUTTON_CLICKED
es un valor de 32 bits. Cada carácter contribuye con un dígito de 8 bits al valor. Para ser más concretos: M_BUTTON_CLICKED = ( 256 ^ 3 ) * b + ( 256 ^ 2 ) * t + 256 * c + l = 16777216 * 98 + 65536 * 116 + 256 * 99 + 108 = 1651794796
Principal
Comencemos nuestra explicación donde comienza la ejecución, en
main
. int main ( void ) { App * app = new App(); app -> Run(); delete app; return 0 ; }
Lo primero que sucede es que creamos una instancia de la
App
. Esto significa que el constructor que definimos se ejecuta.
Entonces, llamamos a este misterioso método
Run()
. Como no definimos uno, esto se hereda de la aplicación BApplication
.
La entrada BApplication de BeBook resume lo que
Run
hace:Al igual que con todos los BLoopers, para usar una aplicación BA construyes el objeto y luego le dices que comience su ciclo de mensajes llamando a la función Ejecutar (). Sin embargo, a diferencia de otros loopers, Run () de BApplication no regresa hasta que se le indica a la aplicación que se cierre. Y después de que Run () regresa, eliminas el objeto que no se ha eliminado.
El resto de los
main
es solo nosotros limpiando, eliminando la instancia de la App
y proporcionando un valor de retorno.
####Constructor
El constructor se define con
App :: App( void ) : BApplication( "application/x-vnd.lh-MyFirstApp" )
Lo primero que hacemos es llamar a uno de los constructores de nuestra superclase,
BApplication
. Le pasamos una cadena, que es la firma de nuestra aplicación. En lo que respecta a las convenciones, DarkWyrm informa que la parte lh
debe ser las iniciales del autor y la parte MyFirstApp
debe ser el nombre de su aplicación.
Supongo que te estás preguntando qué es la firma de una aplicación. Todavía estoy un poco confundido.
La entrada de la aplicación de la descripción general del sistema de BeBook dice que esta cadena es "la firma de la aplicación":
La cadena pasada al constructor establece la firma de la aplicación. Esta es una medida de precaución, es mejor agregar la firma como un recurso que definirla aquí (una firma de recurso anula la firma del constructor). Utilice la aplicación FileTypes para establecer la firma como un recurso.
Por lo tanto, hay otros lugares donde podríamos establecer la firma. Esto también apunta a que la firma de la aplicación está relacionada con el tipo de archivo del ejecutable.
La entrada de aplicación de clases y métodos BA proporciona más información sobre este tema de firma, pero no explica para qué se utiliza.
Los constructores de firmas asignan el argumento como la firma de la aplicación. El argumento se ignora si ya se ha especificado una firma en un recurso o atributo del ejecutable de la aplicación (las aplicaciones serias siempre deben establecer la firma como un atributo y un recurso). La firma es una cadena de tipo MIME que debe tener el supertipo "aplicación". Para obtener más información sobre las firmas de la aplicación y cómo configurarlas, consulte TODO.
Sí, realmente dice TODO. El sistema de archivos de Haiku (BFS) no usa extensiones de nombre de archivo (.jpg, .gif, etc.). En su lugar, utiliza tipos MIME. Los tipos MIME también se utilizan en Internet al transferir archivos. En cuanto al supertipo de "aplicación", es por eso que nuestra cadena comienza con "aplicación".
Los usos de las firmas de aplicaciones se indican en este hilo de la lista de correo :
Además, esto facilitará el envío de mensajes entre aplicaciones o su uso sin tener que buscar la firma de la aplicación.
También hay una mención de ellos en la Guía del usuario de Haiku :
En la parte superior, verá, en lugar de una cadena MIME estandarizada, la firma única de la aplicación. Con él, el sistema encuentra el programa donde está instalado.
Parece que la firma de su aplicación se usa como nombre único. Esto permite que otros programas inicien nuestro programa o le envíen mensajes. Todavía no estoy seguro de qué significa la parte
x-vnd
, pero parece ser una parte estándar de la firma para las aplicaciones.
Cuando
main
llama a nuestro desarrollador de App
, nosotros:- Inicializar nuestro contador,
fCount
.fCount = 0 ;
- Crear una ventana
BRect frame ( 100 , 100 , 500 , 400 );
BRect
proviene del kit de interfaz y se utiliza para crear varios botones, ventanas y widgets. El resumen de BeBook de BRect tiene una excelente explicación con imágenes. Para ser breve, estamos llamando a una función que toma coordenadas(left,top,right,bottom)
. El origen está en la esquina superior izquierda de la pantalla. Las coordenadas enteras están ubicadas en los centros de píxeles. Nuestroframe
es un rectángulo de 400x300 píxeles.myWindow = new BWindow(frame , "My First App" , B_TITLED_WINDOW , B_ASYNCHRONOUS_CONTROLS | B_QUIT_ON_WINDOW_CLOSE);
Estamos creando un BWindow usando este constructor:BWindow(BRect frame, const char * title, window_type type, uint32 flags, uint32 workspaces = B_CURRENT_WORKSPACE);
"Mi primera aplicación" es el título de la ventana.B_TITLED_WINDOW
significa que esta es solo una ventana de estilo Haiku. De acuerdo con BeBook ,B_QUIT_ON_WINDOW_CLOSE
no hace nada yB_ASYNCHRONOUS_CONTROLS
debe usar en cualquier ventana que contenga controles:Le dice a la ventana que permita que los controles se ejecuten de forma asíncrona. Todas las ventanas que contienen controles deben incluir este indicador (está desactivado de forma predeterminada debido a la compatibilidad con versiones anteriores). - Crea un botón y ponlo en la ventana.
BButton * button = new BButton(BRect( 10 , 10 , 11 , 11 ), "button" , "Click Me!" , new BMessage(M_BUTTON_CLICKED));
Esto crea un nuevo botón. Todavía no se ha agregado a la ventana, por lo que esta es solo la idea de un botón. El constructor que estamos usando es:BButton(BRect frame, const char * name, const char * label, BMessage * message, uint32 resizingMode = B_FOLLOW_LEFT | B_FOLLOW_TOP, uint32 flags = B_WILL_DRAW | B_NAVIGABLE);
ElBRect frame
le dice al botón qué tan grande y dónde debería estar."button"
es el nombre."Click Me!"
Es el texto que aparecerá en el botón. CadaBMessage
what
seM_BUTTON_CLICKED
clic en este botón, seBMessage
unBMessage
cuyoBMessage
esM_BUTTON_CLICKED
. No nos metemos con los argumentos opcionalesresizingMode
yflags
.El nombre no parece importar para los propósitos de este programa. Podemos proporcionar un nombre porque BButton es un BControl , que es un BView , que es un BHandler , que usa un nombre para registrarse con el sistema de mensajería.Según BView , no necesitamos especificar un nombre:asigna a BView un nombre de identificación, que puede ser NULL.button -> SetTarget( this );
Lo primero que haremos es decirle al botón a dónde enviar sus mensajes. Por defecto, esa sería su ventana. En este caso, eso significaríamyWindow
. Sin embargo, no estamos definiendo el controlador de mensajes demyWindow
, por lo que no podremos ver los mensajes. Queremos recibir los clics en nuestro controlador en su lugar. Al llamar aSetTarget(this)
el botón envía los mensajes a esta instancia de laApp
lugar de a la ventana. El botón enviará un mensaje cada vez que se haga clic.button -> ResizeToPreferred();
Este es un método de BView . Calcula el tamaño preferido:Por ejemplo, un objeto BButton informa el tamaño óptimo para mostrar el borde del botón y la etiqueta dada la fuente actual.Y entonces:cambia el tamaño del rectángulo del marco del BView al tamaño preferido, manteniendo constantes sus lados izquierdo y superior.Para resumir, esta línea le dice al botón que olvide el tamaño delframe
original y que cambie su tamaño al espacio suficiente para "Click Me!" Y un borde.myWindow -> AddChild(button);
- Mostrar la ventana
myWindow -> Show();
Esto es lo que hace que la ventana se muestre en la pantalla y comienza a recibir mensajes.
La parte de esto que importa a nuestro manejador de mensajes es el botón. En el constructor de botones, describimos qué tipo de mensaje enviará cuando se haga clic. Esto es donde
M_BUTTON_CLICKED
cosa viene también.BMessage
son un concepto clave en Haiku. Se utilizan para comunicarse entre aplicaciones y entre subprocesos en una aplicación. Un mensaje tiene un what
y datos. Por el momento, todo lo que nos importa es el what
. Este es un número y se usa como el asunto de un correo electrónico. Al utilizar M_BUTTON_CLICKED como el asunto del mensaje, solo podemos tratar los mensajes de nuestro botón en nuestro controlador de mensajes, y luego pasar todo lo demás al Mensaje mucho más sabio Recibido de la aplicación BA.Manejador de mensajes
Cuando recibimos un mensaje, se llama a MessageReceived con el mensaje.
void App :: MessageReceived(BMessage * msg)
El
void
en la firma de tipo significa que no estamos tratando de producir nada. Solo estamos reaccionando al mensaje. switch (msg -> what)
La mayor parte de esta función está ocupada por una instrucción
switch
. Todo lo que nos importa es filtrar los mensajes de nuestro botón.
Si el botón nos está enviando un mensaje, entonces nosotros:
- Incremento
fCount
fCount ++ ;
- Averiguar cómo queremos que sea el nuevo título de la ventana.
BString labelString ( "Clicks: " ); labelString << fCount;
- Establecer el título de la ventana
myWindow -> SetTitle(labelString.String());
Por otro lado, si recibimos algún otro mensaje, simplemente lo pasamos a nuestra aplicación de clase
BApplication
ya que no tenemos idea de qué hacer. default : { BApplication :: MessageReceived(msg); break ; }