O mapa de mensagens é, digamos assim, o cerne do tratamento de eventos no FOX Toolkit. É ele quem determina que callback deve ser chamado em resposta a que tipo de mensagem, enviada por um determinado objeto.
Neste tópico, explicarei com mais detalhes como funciona o mapa. Vamos lá.
Definição do mapa
O mapa de mensagens é definido através da macro FXDEFMAP. Essa macro recebe um parâmetro, que é o nome da classe. Com esta macro, declara-se um vetor onde são armazenados os mapeamentos de mensagens; essa macro expandida nada mais é que uma declaração de uma variável normal. Seguindo o exemplo dos nossos tutoriais, temos o seguinte:
Essa macro (que termina com o parêntese fechado) é expandida na seguinte declaração:
Sendo que FXMapEntry é uma struct interna criada automaticamente pela macro FXDECLARE, chamada ao declararmos a classe FoxTutorialMainWindow (lembram?). Ou seja, estamos declarando um vetor de entradas do mapa de mensagem.
Percebam o nome do vetor de mapeamento. Aqui temos um padrão de nomenclatura: o nome do vetor é composto pelo nome da classe com o sufixo "Map".
Dentro deste vetor, colocamos os mapeamentos em si. Isto é feito com auxílio da macro FXMAPFUNC, que veremos em seguida.
Adicionando mapeamentos
A macro FXMAPFUNC recebe três parâmetros: o tipo da mensagem, o ID da mensagem e um ponteiro para o callback. Tomando como exemplo este tutorial, adicionamos os seguintes mapeamentos:
Tipo de mensagem
Informa qual o tipo de evento que foi disparado: um comando, um clique do mouse, uma tecla pressionada, uma janela redimensionada etc.
Teremos um tópico exclusivo (ou vários tópicos...) para discutir os diferentes tipos de mensagem.
ID da mensagem
De certa maneira, identifica quem enviou a mensagem. Junto com o tipo da mensagem, tem-se uma combinação suficiente para dizer que ação (callback) deve ser executada. Essa combinação deve ser única, portanto uma declaração como a seguinte não é válida:
Neste caso, tentamos fazer com que a mesma mensagem ative duas ações, o que na realidade não ocorre. É possível, entretanto, combinar a mesma mensagem com diferentes tipos, podendo ou não executar a mesma ação. Por exemplo, eu tenho um canvas (área de desenho) que deve executar uma determinada ação quando o mouse for clicado e outra quando for redimensionado. Como é um objeto só, ele vai ter apenas um ID, diga-se ID_CANVAS. No mapa, teremos (para efeito de exemplo, vou omitir o escopo dos identificadores; compare com os exemplos reais acima):
É possível, ainda, que um mesmo ID, com diferentes tipos de mensagem, execute a mesma ação. Digamos que nosso canvas trate igualmente o clique com o botão esquerdo e direito do mouse:
Resumindo, apenas a combinação <tipo de mensagem, id da mensagem> deve ser única.
Ponteiro para o callback
É exatamente isso: um ponteiro para a função membro, que foi declarada na classe (e, naturalmente, deve estar devidamente implementada). Realmente não há muito o que dizer aqui.
Observações finais
Nos exemplos didáticos, eu removi o escopo dos identificadores (id e ponteiros de função) por motivos de espaço. É necessário fornecê-los pelo seguinte motivo: a declaração do mapa de mensagens, na verdade, declara um vetor global, ou seja, fora do escopo da classe. Estando fora do escopo da classe, é necessário informá-lo, qualificando os identificadores através do operador de escopo ( :: ).
Outro motivo, talvez menos comum, é que é possível que um objeto de uma classe execute uma ação de outro objeto de outra classe. Isso acontece, por exemplo, quando um botão de uma caixa de diálogo altera o estado de um componente da janela principal, ou seja, o comando deste botão dispara uma ação da janela principal.
Estes dois motivos explicam, também, porque tanto as IDs das mensagens quanto as ações devem estar localizadas na interface pública da classe.
Conclusão
Neste tópico, apresentei alguns detalhes referentes ao mapa de mensagens. É muito importante entendê-lo, já que ele informa que ações devem ser executadas em resposta às diversas ações do usuário.
Existem ainda alguns outros detalhes, mas que deixarei para outros tópicos, pois acho que este já está extenso e cobriu bem o básico. Até lá.
Neste tópico, explicarei com mais detalhes como funciona o mapa. Vamos lá.
Definição do mapa
O mapa de mensagens é definido através da macro FXDEFMAP. Essa macro recebe um parâmetro, que é o nome da classe. Com esta macro, declara-se um vetor onde são armazenados os mapeamentos de mensagens; essa macro expandida nada mais é que uma declaração de uma variável normal. Seguindo o exemplo dos nossos tutoriais, temos o seguinte:
FXDEFMAP(FoxTutorialMainWindow) FoxTutorialMainWindowMap[] = { };
Essa macro (que termina com o parêntese fechado) é expandida na seguinte declaração:
static const FoxTutorialMainWindowMap::FXMapEntry FoxTutorialMainWindowMap[] = { };
Sendo que FXMapEntry é uma struct interna criada automaticamente pela macro FXDECLARE, chamada ao declararmos a classe FoxTutorialMainWindow (lembram?). Ou seja, estamos declarando um vetor de entradas do mapa de mensagem.
Percebam o nome do vetor de mapeamento. Aqui temos um padrão de nomenclatura: o nome do vetor é composto pelo nome da classe com o sufixo "Map".
Dentro deste vetor, colocamos os mapeamentos em si. Isto é feito com auxílio da macro FXMAPFUNC, que veremos em seguida.
Adicionando mapeamentos
A macro FXMAPFUNC recebe três parâmetros: o tipo da mensagem, o ID da mensagem e um ponteiro para o callback. Tomando como exemplo este tutorial, adicionamos os seguintes mapeamentos:
FXMAPFUNC(SEL_COMMAND, FoxTutorialMainWindow::ID_INFORMATION, FoxTutorialMainWindow::onCmdMessage), FXMAPFUNC(SEL_COMMAND, FoxTutorialMainWindow::ID_QUESTION, FoxTutorialMainWindow::onCmdMessage), FXMAPFUNC(SEL_COMMAND, FoxTutorialMainWindow::ID_WARNING, FoxTutorialMainWindow::onCmdMessage), FXMAPFUNC(SEL_COMMAND, FoxTutorialMainWindow::ID_ERROR, FoxTutorialMainWindow::onCmdMessage),
Tipo de mensagem
Informa qual o tipo de evento que foi disparado: um comando, um clique do mouse, uma tecla pressionada, uma janela redimensionada etc.
Teremos um tópico exclusivo (ou vários tópicos...) para discutir os diferentes tipos de mensagem.
ID da mensagem
De certa maneira, identifica quem enviou a mensagem. Junto com o tipo da mensagem, tem-se uma combinação suficiente para dizer que ação (callback) deve ser executada. Essa combinação deve ser única, portanto uma declaração como a seguinte não é válida:
FXMAPFUNC(SEL_COMMAND, ID_ACTION, onCmdAction1), FXMAPFUNC(SEL_COMMAND, ID_ACTION, onCmdAction2),
Neste caso, tentamos fazer com que a mesma mensagem ative duas ações, o que na realidade não ocorre. É possível, entretanto, combinar a mesma mensagem com diferentes tipos, podendo ou não executar a mesma ação. Por exemplo, eu tenho um canvas (área de desenho) que deve executar uma determinada ação quando o mouse for clicado e outra quando for redimensionado. Como é um objeto só, ele vai ter apenas um ID, diga-se ID_CANVAS. No mapa, teremos (para efeito de exemplo, vou omitir o escopo dos identificadores; compare com os exemplos reais acima):
FXMAPFUNC(SEL_CLICKED, ID_CANVAS, onCanvasClick), FXMAPFUNC(SEL_CONFIGURE, ID_CANVAS, onCanvasConfigure),
É possível, ainda, que um mesmo ID, com diferentes tipos de mensagem, execute a mesma ação. Digamos que nosso canvas trate igualmente o clique com o botão esquerdo e direito do mouse:
FXMAPFUNC(SEL_LEFTBUTTONPRESS, ID_CANVAS, onCanvasClick), FXMAPFUNC(SEL_RIGHTBUTTONPRESS, ID_CANVAS, onCanvasClick),
Resumindo, apenas a combinação <tipo de mensagem, id da mensagem> deve ser única.
Ponteiro para o callback
É exatamente isso: um ponteiro para a função membro, que foi declarada na classe (e, naturalmente, deve estar devidamente implementada). Realmente não há muito o que dizer aqui.
Observações finais
Nos exemplos didáticos, eu removi o escopo dos identificadores (id e ponteiros de função) por motivos de espaço. É necessário fornecê-los pelo seguinte motivo: a declaração do mapa de mensagens, na verdade, declara um vetor global, ou seja, fora do escopo da classe. Estando fora do escopo da classe, é necessário informá-lo, qualificando os identificadores através do operador de escopo ( :: ).
Outro motivo, talvez menos comum, é que é possível que um objeto de uma classe execute uma ação de outro objeto de outra classe. Isso acontece, por exemplo, quando um botão de uma caixa de diálogo altera o estado de um componente da janela principal, ou seja, o comando deste botão dispara uma ação da janela principal.
Estes dois motivos explicam, também, porque tanto as IDs das mensagens quanto as ações devem estar localizadas na interface pública da classe.
Conclusão
Neste tópico, apresentei alguns detalhes referentes ao mapa de mensagens. É muito importante entendê-lo, já que ele informa que ações devem ser executadas em resposta às diversas ações do usuário.
Existem ainda alguns outros detalhes, mas que deixarei para outros tópicos, pois acho que este já está extenso e cobriu bem o básico. Até lá.
Nenhum comentário:
Postar um comentário