quinta-feira, 1 de maio de 2008

Criando layouts malucos com o Merlin

Nenhum renderizador (ops, gerador) de tela é completo se não suportar uma customização total das telas que ele gerar. E não estou apenas falando de customizações simples, como a reordenação de campos ou a aplicação de CSS sobre um formulário.

Estou falando de revolucionar (adoro essa palavra) as coisas e criar um layout totalmente piscodélico, incluindo span horizontal ou vertical, disposição circular de elementos, transparências e mesmo sobreposições (z-order) de controles.

O Merlin suporta tudo isso, e a feature é chamada simplesmente de Layout Maluco. Porém, antes de falar dele, vai uma breve introdução sobre os tipos de customização de layout suportados pelo Merlin.

Tipos de Customização Suportadas
Desde o princípio, o Merlin foi projetado para suportar 3 tipos de customização de layout:

1. Simples (use anotações): São aquelas corriqueiras, onde o objetivo é apenas dar uma “aparada” no que o renderizador de layout atrelado produz. Isso inclui reordenação de campos, subtituição de tipos de controles, reposicionamento de labels descritivos, definição de resizing (vertical ou horizontal) e algumas outras pequenas features que, na prática, dependem muito do renderizador de layout associado ao bean.
O Merlin disponibiliza alguns renderizadores “built-in”, sendo que essas features que comentei são suportadas por eles.
Na prática, customizações simples são feitas apenas através de anotações sobre os beans e suas propriedades.

2. Complexas (crie anotações e renderizadores): Quando você deseja algo a mais, e as anotações built-in oferecidas podem não ser suficientes, e você pode precisar criar novas anotações ou modificar (ou adicionar, estender, ou remover) os renderizadores existentes.
Por exemplo, digamos que você tem um controle de tela um pouco diferente, como um StackPanel ( do GWT, que agrupa controles em uma barra vertical) e queira que as propriedades de beans que são coleções tenham seus elementos exibidos dentro desse controle na tela. Solução? Marque o controle com @RenderAs(value=StackPanel.class,binder=MyStackPanelBinder), onde MyStackPanelBinder é a classe que você criou e que se responsabiliza por traduzir a coleção de objetos no StackPanel e vice-versa (saiba mais ou assista o demo).

3. Malucas (eu quero como eu quero!): Noutras (existe essa palavra?) vezes, a coisa encrespa mesmo, e a tela de cadastro que tem de ser criada é totalmente pirada (usuários têm disso). Nesses casos, a solução mais rápida ou efetiva é não criar nada de novo, e sim reusar coisas prontas. O restante do post mostra mais detalhes sobre esse tipo de coisa.

Criando um Layout (ainda nem tanto) Maluco
No primeiro release do Merlin, que deve incluir o suporte para o pacote gráfico Swing, a engine de renderização padrão é o JGoodies Forms. Essa biblioteca é muito prática, de rápida assimilação e, principalmente, projetada para suportar diversos dispositivos gráficos (MacOS, Windows, JavaME, etc.) sem degradação de qualidade.

O exemplo que vou mostrar é em cima dela, mas lembre-se que essas regras valem para qualquer pacote gráfico que você escolher.

Vamos supor que o usuário quer uma tela como essa:



Essa tela apresenta 2 labels e 3 controles, sendo duas caixas de texto e um botão. Nos controles de texto, as setas indicam o redimensionamento que irá ocorrer no caso do usuário modificar o tamanho da tela.

Para fazer isso no Merlin, temos duas formas.

A Primeira Forma: Usando Posicionamento Manual
Usar posicionamento manual significa que, para cada controle a ser exibido na tela, uma anotação @Pos é colocada sobre uma propriedade do bean. Nesse exemplo, o bean associado seria como:

@Layout(impl=JGoodies.class, init="'p, 4dlu, 40dlu, 15dlu, p, 4dlu, 25dlu:g', '30dlu, 20dlu, 15dlu:g'")
class Bean {

@Pos(value=“3,1,1,3”, captionAt=”1,1”)
String nome;

@Pos(“7,2”, captionAt=”5,2”)
String historico;

@Pos(“5,1”)
@RenderAs(JButton.class)
String importar;
}

Não entendeu nada?

Bem, primeiro o que fizemos foi informar para o Merlin utilizar um gerenciador de layout diferente nesse bean. Isso foi feito com a anotação @Layout. Nessa anotação, passamos como parâmetro a classe JGoodies.class, que é uma classe utilitária (nesse caso built-in do Merlin) que representa a implementação de gerenciador de layout a ser utilizada. Na prática, ela é uma classe Delegate, que encarrega-se de receber alguns parâmetros (pelo atributo init) e repassá-los para o gerenciador de layout, no caso, o próprio JGoodies Form Layout.

Depois disso, informamos que dentro desse layout, os controles devem ser posicionados conforme a anotação @Pos. Essa anotação nada mais faz do que informar as posições exatas em que as propriedades devem ser exibidas, tal como em layouts na web: em outras palavras, Linha, Coluna, Span Horizontal e Span Vertical. O atributo #captionAt indica a posição em que deve ser colocado o label descritivo associado ao controle.

Para o caso da propriedade “importar” (que já vai ser renderizada como um botão devido a presença da anotação @RenderAs), não precisamos de um label descritivo e, assim, simplesmente não usamos a propriedade #captionAt.

A Segunda Forma: Usando Ferramentas WYSIWYG
WYSIWYG é acrônimo de What You See Is What You Get, e significa literalmente isso: Você simplesmente usa seu editor WYSIWYG preferido, como o Netbeans ou o Visual Editor do Eclipse e cria a sua classe visual. Feito isso, você passa ela como parâmentro para o Merlin no método createIU.

A única coisa que você deve ter em mente é que os controles criados no editor WYSIWYG devem ter o mesmo nome daqueles que serão renderizados pelo merlin.

Usando essa abordagem, o bean pode ser “oco”, como:

class Bean {

String nome;
String historico;
String importar;

}

Mas como funciona isso?

OK, você tem o bean oco. Ao ser renderizado esse bean, o Merlin vai gerar (por padrão, e o que pode ser customizado também) os seguintes nomes de controles: bean.nome, bean.historico, bean.importar. Assim, quanto voce estiver criando sua classe visual com layout realmente psicodélico (usando o SpringLayout do Netbeans, por exemplo), você coloca na propriedade #name dos componentes os nomes indicados acima.

É isso, simples assim? Quase, o último passo é extremamente complexo: você deve chamar o método createIU com um parâmetro extra, que é justamente a sua classe criada no Netbeans.

Por exemplo: merlin.createIU(Bean.class, TesteLayoutMaluco.class).

O Merlin simplesmente “varre” cada propriedade do bean e tenta localizar um controle equivalente na classe de template (o seu form). Quando encontra, ele faz a substituição, ou seja, ele renderiza a propriedade do bean na exata posição em que se encontra o controle equivalente no form. O tipo do controle a ser renderizado vai depender do tipo de controle que você colocou no seu form, ou, existindo a propriedade @RenderAs na propriedade do bean, usa-se essa.

Qual usar?
Bom, se considerarmos o que Karsten Lentzsch afirma (que o JGoodies abrange 90% dos layouts para cadastros – e eu concordo veementemente com ele), a resposta é justamente esta: em 90% dos casos você vai criar layouts malucos usando a abordagem do @Layout + @Pos. E lembro, que o mesmo esquema de posicionamento vale para outros frameworks gráficos, como o GWT ou JSF

Agora, se você realmente não quer se preocupar com cálculos de posicionamento (o que é uma pena, pois a experiência mostra que a maioria dos layouts feitos em IDEs não suporta redimensionamento, escalonamento de fonte, troca de ambiente – Windows, Mac, etc. ou um simples ajuste de resolução de tela…) e quer usar ferramentas WYSIWYG, vá em frente. Você não precisa usar nenhuma anotação e pode desenhar as telas livremente, usando o Merlin para fazer o trabalho sujo de binding, validação, tratamento de eventos, etc.