No primeiro tópico sobre tratamento de eventos (aqui), eu criei um botão que apenas fechava o aplicativo, ou seja, o botão enviava uma mensagem pré-definida.
Neste tópico, mostrarei como definir suas próprias mensagens a serem enviadas, assim como tratar os eventos a elas relacionados.
Pois bem, ponhamos a mão na massa.
Declaração das mensagens
O primeiro passo é declarar as mensagens que serão enviadas. Isto é feito na declaração da classe através de uma enumeração, que deve estar na interface pública. Assim:
Aqui, estou chamando minha mensagem de ID_HELLO, seguindo a convenção de prefixar as mensagens com ID_. É importante também que o nome seja bem descritivo.
Em se tratando de uma enumeração, por padrão os itens são numerados seqüencialmente começando em 0. Porém essa é uma característica indesejável nesse caso, pois isso proporcionaria conflito de mensagens. Para garantir que cada mensagem tenha um valor diferente, todos os widgets FOX declaram a "mensagem" ID_LAST, cujo único propósito é garantir a seqüência de mensagens e evitar colisão. Então, deve-se atribuir à primeira mensagem o valor da última mensagem da superclasse direta, neste caso FXMainWindow.
Eu costumo definir também a minha própria ID_LAST, apesar de nunca ter derivado uma classe minha. Se for garantido que sua classe não será derivada, não é necessário.
Declaração dos callbacks
Além das mensagens, é preciso definir as funções que irão tratar as mensagens. Quando se trata de interação com o usuário, essas funções são chamadas callbacks, exatamente porque elas não têm um momento certo para serem chamadas, mas são executadas em resposta às ações do usuário. Em outro tópico discutirei um tema relevante com relação a essa questão dos callbacks.
Eles devem estar na interface pública da classe. Como C++ permite definir o escopo dos componentes da classe diversas vezes, eu gosto de "agrupar" a declaração das mensagens e dos callbacks em uma "seção pública" separada, conforme pode-se observar no código.
No FOX, todos os callbacks têm um protótipo igual:
ou seja, sempre retornam um long e recebem três parâmetros: FXObject*, FXSelector, void*.
Além disso, existe uma convenção de nomenclatura, que será tratada em outro tópico.
Neste exemplo, definimos o seguinte callback:
Aqui terminamos as declarações necessárias. Agora partimos para a implementação.
Mapa de mensagens
O mapa de mensagens é definido com auxílio da macro FXDEFMAP, já apresentada aqui. Porém, estava vazio, pois a janela não tratava nenhum evento. Agora vamos colocar um mapeamento nele.
Cada mapeamento é definido pela macro FXMAPFUNC, recebe três parâmetros: tipo da mensagem, ID da mensagem e um ponteiro para o callback. No nosso exemplo, temos:
Obs.: Por limitações de espaço, quebrei a linha em três, mas normalmente deixo a declaração inteira em uma linha só.
Isso quer dizer que sempre que a nossa janela receber uma mensagem do tipo SEL_COMMAND (um botão clicado, um menu selecionado etc.), associado à mensagem ID_HELLO, ela deve executar o callback onCmdHello.
A linha contendo a chamada à macro FXIMPLEMENT permanece intacta, pois a macro auxiliar ARRAYNUMBER garante a atualização automática do tamanho do mapa de mensagens.
Conectando os objetos
Em seguida, eu preciso dizer quem vai enviar essa mensagem. Para isso, eu criei um botão:
Quando o usuário clica no botão, ele lança um evento do tipo SEL_COMMAND, associado à sua mensagem, que neste caso é ID_HELLO. Esse evento é enviado para o alvo do botão, que neste caso é a própria janela (this).
Em seguida, o alvo (nossa janela) consulta seu mapa de mensagens e descobre que um evento SEL_COMMAND, associado à mensagem ID_HELLO, resulta em uma chamada a onCmdHello.
Implementando o callback
Naturalmente, o callback deve ser implementado. Neste exemplo, será exibida uma mensagem de saudação:
Note o valor de retorno: 1. Callbacks, no FOX Toolkit, retornam basicamente 1 para informar que o evento foi tratado, ou 0, indicando que não foi tratado.
Essa convenção é utilizada para, por exemplo, tratar a atualização da interface, especialmente em eventos do mouse: se o evento foi tratado, agenda uma atualização da interface; senão, nada mais acontece.
Na prática, o valor de retorno é quase sempre 1 (pelo menos, eu nunca retornei 0, mas já andei fuçando os fontes e isso acontece, portanto é bom saber).
Com isso, temos um botão que, ao ser clicado, exibe uma mensagem ao usuário.
Resultado
Confira no vídeo a seguir como ficou a nossa janela:
---
Código-fonte para este tutorial.
Baixar vídeo.
Neste tópico, mostrarei como definir suas próprias mensagens a serem enviadas, assim como tratar os eventos a elas relacionados.
Pois bem, ponhamos a mão na massa.
Declaração das mensagens
O primeiro passo é declarar as mensagens que serão enviadas. Isto é feito na declaração da classe através de uma enumeração, que deve estar na interface pública. Assim:
30 public:
31 enum {
32 ID_HELLO = FXMainWindow::ID_LAST,
33
34 ID_LAST,
35 };
Aqui, estou chamando minha mensagem de ID_HELLO, seguindo a convenção de prefixar as mensagens com ID_. É importante também que o nome seja bem descritivo.
Em se tratando de uma enumeração, por padrão os itens são numerados seqüencialmente começando em 0. Porém essa é uma característica indesejável nesse caso, pois isso proporcionaria conflito de mensagens. Para garantir que cada mensagem tenha um valor diferente, todos os widgets FOX declaram a "mensagem" ID_LAST, cujo único propósito é garantir a seqüência de mensagens e evitar colisão. Então, deve-se atribuir à primeira mensagem o valor da última mensagem da superclasse direta, neste caso FXMainWindow.
Eu costumo definir também a minha própria ID_LAST, apesar de nunca ter derivado uma classe minha. Se for garantido que sua classe não será derivada, não é necessário.
Declaração dos callbacks
Além das mensagens, é preciso definir as funções que irão tratar as mensagens. Quando se trata de interação com o usuário, essas funções são chamadas callbacks, exatamente porque elas não têm um momento certo para serem chamadas, mas são executadas em resposta às ações do usuário. Em outro tópico discutirei um tema relevante com relação a essa questão dos callbacks.
Eles devem estar na interface pública da classe. Como C++ permite definir o escopo dos componentes da classe diversas vezes, eu gosto de "agrupar" a declaração das mensagens e dos callbacks em uma "seção pública" separada, conforme pode-se observar no código.
No FOX, todos os callbacks têm um protótipo igual:
long callback(FXObject*, FXSelector, void*);
ou seja, sempre retornam um long e recebem três parâmetros: FXObject*, FXSelector, void*.
Além disso, existe uma convenção de nomenclatura, que será tratada em outro tópico.
Neste exemplo, definimos o seguinte callback:
37 long onCmdHello(FXObject*, FXSelector, void*);
Aqui terminamos as declarações necessárias. Agora partimos para a implementação.
Mapa de mensagens
O mapa de mensagens é definido com auxílio da macro FXDEFMAP, já apresentada aqui. Porém, estava vazio, pois a janela não tratava nenhum evento. Agora vamos colocar um mapeamento nele.
Cada mapeamento é definido pela macro FXMAPFUNC, recebe três parâmetros: tipo da mensagem, ID da mensagem e um ponteiro para o callback. No nosso exemplo, temos:
13 FXMAPFUNC(SEL_COMMAND,
FoxTutorialMainWindow::ID_HELLO,
FoxTutorialMainWindow::onCmdHello),
Obs.: Por limitações de espaço, quebrei a linha em três, mas normalmente deixo a declaração inteira em uma linha só.
Isso quer dizer que sempre que a nossa janela receber uma mensagem do tipo SEL_COMMAND (um botão clicado, um menu selecionado etc.), associado à mensagem ID_HELLO, ela deve executar o callback onCmdHello.
A linha contendo a chamada à macro FXIMPLEMENT permanece intacta, pois a macro auxiliar ARRAYNUMBER garante a atualização automática do tamanho do mapa de mensagens.
Conectando os objetos
Em seguida, eu preciso dizer quem vai enviar essa mensagem. Para isso, eu criei um botão:
26 new FXButton(contents, "&Hello FOX!", NULL, this, ID_HELLO,
27 BUTTON_NORMAL|LAYOUT_FILL_X);
Quando o usuário clica no botão, ele lança um evento do tipo SEL_COMMAND, associado à sua mensagem, que neste caso é ID_HELLO. Esse evento é enviado para o alvo do botão, que neste caso é a própria janela (this).
Em seguida, o alvo (nossa janela) consulta seu mapa de mensagens e descobre que um evento SEL_COMMAND, associado à mensagem ID_HELLO, resulta em uma chamada a onCmdHello.
Implementando o callback
Naturalmente, o callback deve ser implementado. Neste exemplo, será exibida uma mensagem de saudação:
48 long FoxTutorialMainWindow::onCmdHello(FXObject*, FXSelector, void*) {
49 FXMessageBox::information(this, MBOX_OK, "Olá", "Olá, FOX Toolkit");
50
51 return 1;
52 }
Note o valor de retorno: 1. Callbacks, no FOX Toolkit, retornam basicamente 1 para informar que o evento foi tratado, ou 0, indicando que não foi tratado.
Essa convenção é utilizada para, por exemplo, tratar a atualização da interface, especialmente em eventos do mouse: se o evento foi tratado, agenda uma atualização da interface; senão, nada mais acontece.
Na prática, o valor de retorno é quase sempre 1 (pelo menos, eu nunca retornei 0, mas já andei fuçando os fontes e isso acontece, portanto é bom saber).
Com isso, temos um botão que, ao ser clicado, exibe uma mensagem ao usuário.
Resultado
Confira no vídeo a seguir como ficou a nossa janela:
---
Código-fonte para este tutorial.
Baixar vídeo.
2 comentários:
Olá Edgar,
Estou com uma duvida e meia:
->A meia duvida pode parecer até idiota, mas é que não tenho certeza:
-Qual é a função dessa enumeração? Serve apenas para as mensagens criadas? ID_QUIT, não contaria na enumeração?
->A outra duvida, menos estúpida, é:
- o callback foi implementado dessa forma:
long FoxTutorialMainWindow::onCmdHello(FXObject*, FXSelector, void*) {
FXMessageBox::information(this, MBOX_OK, "Olá", "Olá, FOX Toolkit");
return 1;
}
- Eles são herdados de alguma coisa, pergunto isso porque na implementação não vi nada relacionado com os parâmetros passados a essa função.
Essa enumeração são as mensagens que o objeto vai tratar. ID_QUIT não conta, a menos que sua classe seja derivada de FXApp (onde ela é definida). Mas é aí que entra a questão do ID_LAST, para que uma classe derivada possa aproveitar as mensagens das classes da hierarquia e continuar a contagem.
Quanto ao callback, neste caso, não há herança, mas é possível também estender/redefinir um callback que já foi definido na classe base.
Por exemplo, imagine que é preciso verificar se o arquivo foi salvo antes de fechar o aplicativo. Bastaria estender o callback onCmdQuit de FXApp.
Quanto aos parâmetros, são sempre esses. FXObject é a base da hierarquia dos objetos FOX, sendo que todos os objetos deste tipo podem enviar/receber mensagens (algumas classes utilitárias não derivam de FXObjet, como FXString). E como eles não são utilizados nessa função, podem ficar anônimos.
Espero ter sanado as dúvidas.
Postar um comentário