quinta-feira, 9 de abril de 2009

Tutorial: Janela simples

De posse de um ambiente de desenvolvimento todo configurado, passemos à parte mais interessante: o código.

Neste tutorial, ensinarei a criar uma janela simples, vazia. Essa janela será, naturalmente, a nossa janela principal, e será incrementada a cada tutorial.

Sendo essa janela a principal, ela dever ser derivada da classe FXMainWindow.

Obs.: Toda classe no FOX Toolkit tem o prefixo FX.


FoxTutorialMainWindow.h
 1 /*
2 * FoxTutorialMainWindow.h
3 *
4 * Created on: 07/04/2009
5 * Author: edgar
6 */

7
8 #ifndef FOXTUTORIALMAINWINDOW_H
9 #define FOXTUTORIALMAINWINDOW_H
10
11 #ifndef FXMAINWINDOW_H
12 #include <fox-1.6/FXMainWindow.h>
13 #endif
14
15 class FoxTutorialMainWindow : public FXMainWindow {
16 FXDECLARE(FoxTutorialMainWindow)
17
18 private:
19 FoxTutorialMainWindow() {}
20
21 public:
22 FoxTutorialMainWindow(FXApp *a);
23
24 virtual void create();
25
26 virtual ~FoxTutorialMainWindow();
27 };
28
29 #endif /* FOXTUTORIALMAINWINDOW_H */

Analisando linha por linha:

15: A classe FoxTutorialMainWindow é derivada de FXMainWindow.

16: Macro para declaração de hierarquia. Obrigatório para todas as classes FOX.

19: Construtor default privado sem corpo. Obrigatório também.

22: Construtor público. É o que vamos utilizar. Recebe como parâmetro um ponteiro para FXApp, classe que representa uma aplicação FOX.

24: Todos os componentes FOX têm um método virtual void create(); aqui vamos sobrescrevê-lo.

26: Nosso destrutor.


FoxTutorialMainWindow.cpp

1 /*
2 * FoxTutorialMainWindow.cpp
3 *
4 * Created on: 07/04/2009
5 * Author: edgar
6 */

7
8 #include <fox-1.6/fx.h>
9
10 #include "FoxTutorialMainWindow.h"
11
12 FXDEFMAP(FoxTutorialMainWindow) FoxTutorialMainWindowMap[] = {
13 };
14
15 FXIMPLEMENT(FoxTutorialMainWindow, FXMainWindow,
16 FoxTutorialMainWindowMap,
ARRAYNUMBER(FoxTutorialMainWindowMap))
17
18 FoxTutorialMainWindow::FoxTutorialMainWindow(FXApp *a)
19 : FXMainWindow(a, "Tutorial FOX", NULL, NULL, DECOR_ALL,
0, 0, 200, 200) {
20 }
21
22 void FoxTutorialMainWindow::create() {
23 FXMainWindow::create();
24
25 show(PLACEMENT_SCREEN);
26 }
27
28 FoxTutorialMainWindow::~FoxTutorialMainWindow() {
29 }

Analisando linha por linha:

8: fx.h é um arquivo de fachada que inclui quase todos os arquivos de cabeçalho do FOX Toolkit. Por enquanto, basta saber que ele inclui o suficiente para a interface gráfica de um programa comum.

Outro ponto que merece destaque é que todas as classes FOX estão dentro do namespace FX. O arquivo fx.h automaticamente instrui o compilador a usar esse namespace (using namespace FX;), a menos que a macro FX_NO_GLOBAL_NAMESPACE esteja definida. Mas cá entre nós, quem iria querer definir essa macro e usar FX::FXMainWindow? Detalhe: isso para todas as classes.

12-13: Definição do mapa de mensagens. Aqui é definido o tratamento de eventos, ou seja, a resposta da janela diante das ações do usuário. Como nossa janela não vai tratar nenhum evento por enquanto, ele fica vazio mesmo. Quando falarmos de tratamento de eventos, explicarei o mapa de mensagens com mais detalhes.

15-16: Implementação do mapa de mensagens. Esse é obrigatório. Os parâmetros dessa macro são:
  • Nome da classe (FoxTutorialMainWindow)
  • Nome da superclasse (FXMainWindow)
  • Mapa de mensagens (FoxTutorialMainWindowMap)
  • Número de entradas do mapa de mensagens. Para não haver a necessidade de atualizar manualmente esse valor sempre que colocar ou tirar uma entrada do mapa, o FOX provê a macro ARRAYNUMBER, que calcula o número de elementos de um vetor estático.

18-20: Construtor. Como nossa classe é derivada de FXMainWindow, aqui chamamos o construtor da superclasse para a devida inicialização. Mais tarde falarei especificamente da classe FXMainWindow; portanto, apenas saiba que os parâmetros passados são, respectivamente:
  • a aplicação que será dona dessa janela
  • título da janela
  • ícone grande (o ícone que aparece quando se muda de janela com Alt+Tab)
  • ícone pequeno (o que fica no canto da janela, na barra de título)
  • decorações da janela (borda, barra de título, menu da janela etc.)
  • posição X e Y da janela
  • largura e altura da janela

Ou seja, estamos criando uma janela com as seguintes propriedades:
  • chamada "Tutorial FOX"
  • sem ícones (normalmente passa-se NULL para ambos, sendo ajustados dentro do construtor)
  • com todas as decorações disponíveis (mais detalhes sobre isso mais tarde, em outro post)
  • inicialmente posicionada no canto superior esquerdo da tela (0, 0)
  • tamanho inicial de 200x200

22-26: Aqui sobrescrevemos o método create(). Na verdade, apenas o estendemos para que, além de criar a janela, ela possa ser automaticamente exibida. Neste caso, ela será exibida no centro da tela.

28-39: Nosso destrutor. A classe não tem nada de novo, então não há o que destruir explicitamente. Vazio.


main.cpp

É necessário fornecer uma função main() (não WinMain() ou qualquer outra específica de plataforma) para que o programa rode. Normalmente, pode ser implementada da seguinte forma:

1 /*
2 * main.cpp
3 *
4 * Created on: 07/04/2009
5 * Author: edgar
6 */

7
8 #include <fox-1.6/fx.h>
9
10 #include "FoxTutorialMainWindow.h"
11
12 int main(int argc, char *argv[]) {
13 FXApp app("Tutorial FOX", "Edgar Moraes Diniz");
14
15 app.init(argc, argv);
16
17 new FoxTutorialMainWindow(&app);
18
19 app.create();
20
21 return app.run();
22 }

Analisando linha por linha:

12: Por favor, sempre declarem a função main() dessa forma, retornando um inteiro e recebendo dois parâmetros, argc inteiro e argv um vetor de strings. Nada de void main(void). Isso é válido para qualquer aplicativo, mesmo (e principalmente) em linha de comando.

13: Instancia um objeto FXApp, que representa nossa aplicação. Os parâmetros são o nome da aplicação e o fabricante.

15: Inicia o aplicativo, analisando os argumentos de linha de comando. Não adianta tentar burlar a regra da linha 12, passando 0 e NULL. O FOX não deixa.

17: Instancia a nossa janela. Aqui cabe uma discussão interessante sobre o FOX Toolkit.

Percebam que eu não atribuí a nenhum ponteiro. Simplesmente porque não é necessário. O único parâmetro que eu tenho para a janela é a minha aplicação. Essa aplicação vai armazenar o ponteiro para a janela. Quando a aplicação for destruída, ela destrói tudo o que é dela, incluindo essa janela.

Pode parecer estranho no começo (eu levei um certo susto quando vi...), mas com o uso, percebe-se que esse é uma enorme vantagem do FOX. E isso ocorre o tempo inteiro; quando povoarmos nossa janela, isso ficará claro. Resumindo: não preciso ficar me preocupando em destruir um monte de objetos; eles são destruídos por seus donos.

19: Cria a aplicação. Da mesma forma, chamar create() em um objeto FOX faz com que ele chame create() de todos os seus filhos, ou seja, aqui será criada (e, lembrem-se, exibida) nossa janela.

21: Roda a aplicação. É o loop principal do programa, que vai ouvir e tratar eventos.

Resultado

Depois de compilado, basta executar. O resultado:



Lembram do DECOR_ALL? Ele permite que você redimensione, minimize e maximize a janela (entre outros aspectos). Brinquem à vontade com isso. Mais tarde falaremos do poder que o FOX Toolkit oferece com relação a essas três operações.

Obs.: Essa janela não está assim por causa do FOX. O FOX é responsável somente por essa área acinzentada interna. As bordas são de responsabilidade do decorador de janelas (por isso o prefixo DECOR_). Estou usando aqui o Emerald, o decorador de janelas para usar com o Compiz Fusion.

---
Código-fonte para este tutorial.

7 comentários:

Anônimo disse...

Ow to tentando seguir seu tutorial com o visual c++ 2008, faço tudo certin como no exemplo.

Mas... na hora de rodar sai o seguinte erro:
1>.\FoxTutorialMainWindow.cpp(4) : error C2466: cannot allocate an array of constant size 0

=>FXDEFMAP(FoxTutorialMainWindow) FoxTutorialMainWindowMap[] = {};

O que eu faço para poder rodar ser problemas?

Edgar Diniz disse...

Ola, obrigado pelo contato.

Em primeiro lugar, peço desculpas pelo atraso na resposta (inclusive pelo tempo em que nao posto nada aqui).

Experimente:

FXDEFMAP(FoxTutorialMainWindow) FoxTutorialMainWindowMap[] = {
{ 0 },
};

Anônimo disse...

Ow... tentei aki, mas... só fez gerar outros 69 erros( error LNK2001: unresolved external symbol).

Estou errando em algum passo então?

OBS: Estou usando a versão 1.7....

Edgar Diniz disse...

Ola,

Aquele primeiro erro era erro de compilaçao, que agora foi resolvido.

Erros do tipo "unresolved external symbol" sao erros do linker (ou seja, seu programa compilou sem erros, mas nao achou algumas funçoes, portanto nao pode construir o executavel).

Nas opçoes do linker, adicione as bibliotecas libcmtd.lib e comctl32.lib e wsock32.lib (essa ultima pode nao ser necessaria).

Desculpe nao ter avisado isso antes nos tutoriais, eh que faz muito tempo que nao uso produtos Microsoft...

Se ainda houver algum erro desse tipo, por favor poste mais detalhes.

Espero ter ajudado.

T+

Anônimo disse...

Encontrei a solução no próprio site do fox....

Nas FAQs vi isso:

##################################

Compiling FOX as a DLL under VC++ gives me a unresolved external symbol?

If you build a project under VC++ to compile a FOX application, do not forget to specify -DFOXDLL on the compiler command line. Without it, you will get an error like: error LNK2001: unresolved external symbol "public: static struct FXMetaClass const FXApp::metaClass" (?metaClass@FXApp@@2UFXMetaClass@@B). Or words to that effect.

Of course, you can also build FOX as a static library, in which case there is no problem.

##################################

eduardo4jesus disse...

Podes Explicar mais detalhadamente qual é a função do FXIMPLEMENT na lógica do programa?

Edgar Diniz disse...

@Anônimo

Muito obrigado pela sua contribuição.

Peço desculpas por não ter visto seu comentário em tempo hábil. Por algum motivo, não recebi a notificação...

@eduardo

Seguinte:

Na declaração da classe, é preciso chamar FXDECLARE(classe). Como o próprio nome diz, ele vai fazer algumas declarações, como a struct FXMapEntry (que é o mapa de mensagens) e alguns métodos, como handle().

FXIMPLEMENT nada mais faz que implementar esses métodos que foram declarados através de FXDECLARE.

Esses são detalhes internos do FOX, não precisa dedicar muita atenção a eles. Porém são necessários, pois é através disso que ocorre o tratamento de mensagens.

---

Obrigado a todos pelo contato, continuem contribuindo.