O Eclipse tornou-se famoso não apenas por ser uma IDE de primeira qualidade, desenvolvido com a extensibilidade em mente, mas também porque ele tinha uma interface gráfica levíssima e que era igual a do sistema operacional onde ele estivesse executando, diferentemente do conjunto de componentes gráficos do Java, o Swing, que tinha uma “cara” própria e algumas opções que fazem com que ele “imite” a aparência de um sistema operacional qualquer. Mas essa possibilidade de imitar ainda não era a melhor escolha, porque vários sistemas operacionais tem a opção para aplicar-se temas gráficos e as aplicações Swing não tinham como acompanhar estas mudanças.

Esta interface gráfica do Eclipse foi “separada” do código principal da IDE e tornou-se o que se conhece hoje como SWT, o Standard Widget Toolkit. Ele é uma camada de componentes sobre os componentes padrão do sistema operacional, um botão SWT é na verdade um botão do seu sistema operacional, então, se você aplicar um tema que mude a cor ou a forma do seu botão, o botão SWT vai se modificar conforme o novo tema. Aplicações SWT sempre tem a aparência do sistema operacional que elas executam porque elas utilizam diretamente estes componentes para gerar a visualização na tela. Esta abordagem diferente do Swing, faz com que as aplicações possam se relacionar melhor com o ambiente onde elas estão executando, tornando-as mais naturais para os usuários e até mesmo mais leves, já que ações que exigem muito processamento (como desenhar componentes visuais na tela) são feitos diretamente pelas primitivas do SO e não pela máquina virtual do Java.

Preparando o ambiente

Para seguir esse material você precisa fazer o download da biblioteca do SWT da página do projeto em http://www.eclipse.org/swt/. Procure por “Releases” e depois “Stable”, você deve ver as opções de sistemas operacionais com binários compilados, basta selecionar o seu sistema operacional e fazer o download do arquivo para o seu sistema. Depois de feito o download, descompacte o arquivo e você deve encontrar um arquivo chamado “swt.jar”, esse é o arquivo que contém a biblioteca de componentes e deve ser colocado no classpath da sua aplicação para poder acessar os objetos da biblioteca.

Além desse arquivo você deve encontrar também um conjunto de bibliotecas nativas (no Windows são alguns arquivos com a extensão .DLL, no Linux são arquivos .SO) que também devem ser adicionados a ao classpath da sua aplicação. Se você está utilizando o Eclipse para desenvolvimento, não é necessário fazer o download da biblioteca, pois ela já faz parte do “pacote” do Eclipse e pode ser adicionada a projetos dentro da IDE. Para adicionar o SWT em um projeto do Eclipse 3.2 basta clicar na pasta do projeto com o botão direito, ir pra opção “Build Path”, depois selecionar “Add Library” e adicionar a biblioteca “Standard Widget Toolkit (SWT)”.

Dando os primeiros passos

Agora que já estamos com o ambiente pronto para iniciar, vejamos nosso primeiro exemplo de aplicação SWT, é uma simples janela com um botão que conta quantas vezes é clicado, mas que vai nos trazer várias das noções de como é desenvolver uma aplicação real em SWT.

Listagem 1 – org.maujr.ExemploSWT.java

            package org.maujr;


import org.eclipse.swt.SWT;

import org.eclipse.swt.events.SelectionAdapter;

import org.eclipse.swt.events.SelectionEvent;

import org.eclipse.swt.layout.RowLayout;

import org.eclipse.swt.widgets.Button;

import org.eclipse.swt.widgets.Display;

import org.eclipse.swt.widgets.Shell;

 

public class ExemploSWT {


      private static int selecoes = 0;

     
      public static void main(String[] args) {

           
            Display display = Display.getDefault();

            Shell shell = new Shell(display, SWT.CLOSE | SWT.TITLE | SWT.MIN |

                                    SWT.MAX | SWT.RESIZE);

            shell.setLayout(new RowLayout());

            shell.setText("Aprendendo SWT");


            Button button = new Button(shell, SWT.PUSH);

            button.setText("Clique Aqui Agora!!!");
          

            button.addSelectionListener(new SelectionAdapter() {


                  @Override

                  public void widgetSelected(SelectionEvent event) {

                        Button buttonClicked = (Button) event.getSource();

                        selecoes += 1;

                        buttonClicked.setText(String.format("Fui clicado %s

                                              vezes!", selecoes));

                  }
         

            });


            shell.setSize(300, 100);

            shell.open();


            while (!shell.isDisposed()) {

                  if (!display.readAndDispatch()) {

                        display.sleep();

                  }

            }

            display.dispose();

      }

}
            

Como você já percebeu, o código do nosso exemplo é curto e simples, inicialmente, nós pegamos o objeto Display padrão para a aplicação, que é o objeto que faz a ponte entre o sistema de envio de eventos do sistema operacional para os objetos da sua aplicação Java. Ele é o objeto responsável por transformar as chamadas a métodos em objetos Java normais em chamadas a primitivas do sistema operacional, através de uma referência ao objeto OS, que representa o sistema operacional no qual aplicação está sendo executada. Este objeto OS contém diversas implementações em métodos nativos para as chamadas nos componentes visuais reais.

É no Display que também se encontram informações sobre o ambiente onde a aplicação está executando, como a quantidade de monitores disponíveis, os tamanhos recomendados para certos componentes visuais e outras informações mais avançadas, que no momento não nos interessam.

Não é obrigatório utilizar o método estático “getDefault()” para pegar o Display padrão da aplicação, até porque nós poderíamos querer que houvesse outros Displays acessando diferentes monitores e fazendo diferentes trabalhos, então nós poderíamos simplesmente ter criado um objeto Display utilizando o seu construtor vazio (“new Display()”), mas como nós só queremos um único Display para toda a aplicação é uma boa prática utilizar o método “getDefault()” para evitar problemas como a criação de mais de um objeto do tipo.

Outro detalhe importante é que se você deseja ter mais do que um Display em uma mesma aplicação, tem que criar cada um deles em uma Thread separada, pois cada um precisa de sua própria Thread para organizar a execução da fila de eventos enviados pelo usuário da interface gráfica.

Janelas em SWT – O objeto Shell

Terminando com o Display, nós chegamos ao nosso primeiro “componente visual” do SWT, o Shell, que representa uma janela no sistema operacional. Essa janela pode ser qualquer modelo de janela que os vários sistemas operacionais para os quais o SWT tem versões compatíveis, desde a janela principal da aplicação, com um título, os botões para maximizar, minimizar e fechar até telas de diálogo sem bordas de título.

Para definir qual o tipo da janela, nós utilizamos um modo comum para criar objetos em toda a biblioteca SWT, os “style bits” (algo como “bits de estilo”) que informam ao componente visual como ele deve se mostrar. Os “style bits” são conjuntos de constantes que podem ser aplicadas sobre os componentes visuais para que eles assumam características variadas e estão todos declarados na classe SWT. Nela você encontra todos os “style bits” utilizados na biblioteca. Para saber quais são e o que fazem os “style bits” de um componente visual, você deve ver o “Javadoc” do componente, na documentação sempre existe a informação completa de quais são os estilos aplicáveis a um componente e o que cada estilo destes faz.

No caso da nossa janela, nós queríamos que ela fosse uma janela principal do sistema (aquela com os botões de maximizar, minimizar, fechar e uma barra de título), então criamos uma nova janela (passando o Display onde a janela deve aparecer como parâmetro) com os seguintes “style bits”: SWT.CLOSE, SWT.TITLE, SWT.MIN, SWT.MAX, SWT.RESIZE.

Os operadores podem ser combinados com o uso do operador “ou” ( “|”) em Java. Se todas as opções forem suportadas e não entrarem em conflito nenhum, todas elas são aplicadas ao componente em questão. Como você viu, no nosso exemplo da criação do Shell, nós definimos os seguintes estilos:

  • SWT.CLOSE - Para adicionar o botão de fechar a janela.
  • SWT.TITLE - Para adicionar uma barra de título a janela.
  • SWT.MIN - Para adicionar o botão de minimizar a janela.
  • SWT.MAX - Para adicionar o botão de maximizar a janela.
  • SWT.RESIZE - Para que o usuário possa redimensionar o tamanho da janela conforme a sua necessidade.

Reunindo os vários estilos usando o operador “|” (or/ou), nós fazemos a janela principal padrão dos programas, que tem os botões de maximizar, minimizar, fechar, uma barra de título e ainda pode ser redimensionada.

Outro detalhe importante do modo de trabalho do SWT, é que, diferentemente de outras bibliotecas de componentes (como o Swing), os objetos não são adicionados diretamente aos seus pais (como o método “addComponent()”), os pais de cada componente devem ser passados nos seus construtores. Estranho? Nem tanto. Como o SWT é baseado nas bibliotecas nativas do sistema operacional ele está sempre fazendo acesso a recursos de baixo nível e assim que um componente é instanciado também é criada a referência para o componente real da plataforma nativa, então, para não ter que se preocupar com o ciclo de vida dos componentes nativos, só é possível criar componentes SWT passando a referência do seu componente pai, o que você percebe olhando para o código de instanciação do botão:

Button button = new Button(shell, SWT.PUSH);

O botão é instanciado passando uma referência para o componente que o contém, o objeto Shell, que é a janela na qual o botão vai ser mostrado.

Um ponto que não foi tocado, foi a chamada ao método “setLayout()”, onde é passada uma referência a um objeto do tipo RowLayout. Esse objeto contém as informações de como posicionar os componentes dentro da janela, ele é um “gerenciador de layout”. Mas este é um tema mais avançado que vai ficar pra uma próxima conversa, por enquanto, basta que você saiba que é através desse objeto que os objetos são organizados no componente no qual eles são mostrados.

Botões e eventos

Já próximo do fim do nosso exemplo, nós vemos a implementação do evento que responde ao clique no nosso botão. Os objetos responsáveis por responder por eventos de clique e seleção em SWT são os objetos do tipo org.eclipse.swt.events.SelectionListener, que definem dois métodos, “widgetSelected()” e “widgetDefaultSelected()”. No nosso exemplo, como nós queríamos apenas responder ao clique do botão, utilizamos a classe utilitária org.eclipse.swt.events.SelectionAdapter, que contém uma implementação vazia para ambos os métodos da interface SelectionListener, então basta apenas implementar o método que nos interessa, que é o “widgetSelected()”.

A implementação do nosso método é extremamente simples, ele simplesmente aciona um contador para saber quantos cliques já foram feitos no botão.

Fechando o exemplo

Mas ainda não acabamos, chegamos a parte mais estranha do exemplo, as últimas linhas de código:


            while (!shell.isDisposed()) {

                if (!display.readAndDispatch()) {

                      display.sleep();

                }

          }

          display.dispose();
            

Esta última parte é a que vai controlar a execução do loop de tratamento de eventos da aplicação. Veja que é um “while” onde a condição é a janela principal (nosso objeto Shell que acabou de ser aberto) estar em estado “disposed”, que é quando o usuário clicar no botão de fechar a janela. Então, enquanto a janela não for fechada, ele vai executar o código dentro dele, que é o loop de execução de eventos do SWT. Quando a janela for fechada, ele sai do “while” e libera o objeto display.

É muito importante lembrar que os objetos em SWT devem ser sempre liberados com a chamada do método “dispose()” (se o objeto tem vários objetos filhos, basta fazer o “dispose()” no pai, ele automaticamente faz o mesmo em todos os seus filhos), por isto que assim que nós liberamos a janela, temos que chamar “dispose()” no display para liberar os recursos que ele havia alocado.

Imagem do exemplo executando
Figura 1 – Imagem do exemplo executando.

Conclusão

O SWT é uma incrível biblioteca de componentes visuais para aplicações desktop que garantem ter a mesma aparência dos outros componentes visuais do sistema operacional, diferentemente do Swing que mesmo parecendo ser igual, não consegue ter o mesmo feeling dos componentes padrão do sistema. Você viu um exemplo simples, mas que já traz algumas das características principais do SWT, como o uso de “style bits” e os componentes sendo instanciados passando como referência o seu componente pai.

Referências

  • Clayberg, Eric; Rubel, Dan. 2006. Eclipse – Building Commercial-Quality Plugins. The Eclipse Series. 2º Edition. Addison-Wesley - Pearson Education.
  • McAffer, Jeff; Lemieux, Jean-Michel; 2006. Eclipse Rich Client Platform – Designing, Coding, and Packaging Java Applications. The Eclipse Series. Addison-Wesley – Pearson Education.
  • D’Anjou, Jim; Fairbrother, Scott; Kehn, Dan; Kellerman, John; McCarthy, Pat; 2005. The Java Developer’s Guide to Eclipse. Second Edition. Addison-Wesley – Pearson Education.
  • Scarpino, Matthew; Holder, Stephen; NG, Stanford; Mihalkovic, Laurent; 2005. SWT/JFace in Action – How to design graphical applications with Eclipse 3.0. Manning Publications.
  • SWT Website