quinta-feira, 24 de abril de 2008

Customizando telas com o Merlin

Este é o primeiro de uma série de posts que devem vir sobre como o Merlin suporta a customização das telas de cadastro.

Muitos desses posts são idéias que podem se concretizar ou não. Enfim, o que vale é o espaço para discussões, objetivando chegar à melhor solução.

Uma tela simples

Supomos que temos um bean muito simples, como:

class Pessoa {

String nome;

String historico;

}

Para esse bean, ao fazer a geração (ops, renderização!) da Tela de Cadastro com o Merlin, chegamos a isso:

Como não existe informação de contexto alguma, esta tela é o melhor que podemos fazer nesse momento.

Evoluindo as telas simples

Porém, o usuário espera que o campo historico seja um campo textual para escrever bastante coisa, e assim, usando uma anotação do próprio Hibernate, podemos fazer a evolução:

class {

String nome;

@Length(max=5000)

String historico;

}

Com isso, o resultado da renderização da tela de cadastro é algo como (as setas indicam o redimensionamento):

Mais evoluções

Mas, mesmo assim, o usuário não está satisfeito. Ele quer um resultado como:

Nessa tela, o campo historico continua sendo renderizado como uma caixa de texto simples e possui um botão extra que, ao ser clicado, abre uma outra tela para então, aí sim, termos todo o texto de histórico do cliente!

Como conseguir isso usando o Merlin?

class Pessoa {

String nome;

@Length(max=5000)

@RenderAs(JtextField.class)

String historico;

@RenderAs(value=JButton.class, init=”text='...'”)

@Agent(event=”onClick”, event=”showPopup”)

@Merge(“historico”)

String botao;

}

E pronto. E pronto? Mas como assim?

Explicação

A primeira anotação @RenderAs, indica que o campo historico deve ser mostrado como uma caixa de texto, que será redimensionável horizontalmente (devido o tamanho de 5000), mas que terá apenas uma linha (pois é uma caixa de texto, e não um “textArea”).

A segunda anotação @RenderAs faz com que o campo botao seja renderizado na tela de cadastro como um botão com a propriedade text inicializada com uma String de reticências.

A anotação @Agent adiciona um tratador de eventos ao controle, indicando que ao ser clicado, o método showPopup seja executado (vide mais informações ao final do post).

A anotação @Merge é o kernel da coisa. Ela implica que o botão renderizado seja combinado ao texto do histórico. Em outras palavras, é como se o controle de histórico fosse encapsulado em um painel e nesse painel também fosse adicionado o botão. Essa anotação suporta mais coisas, como a especificação de um layout diferenciado para esse painel, eventos, etc. Mas não precisamos disso aqui. Na prática, essa anotação é muito parecida com @Group, a qual devemos falar em outras oportunidades...

Conclusões

Essa tela de cadastro é bem simples, mas a idéia é essa mesmo.

Como estamos iniciando o nosso framework, nada melhor do que pensar em situações que podem ocorrer e, conforme conseguirmos resolvê-las, irmos incrementando-as em complexidade.

A cada passo, devemos olhar o passado e tentar manter a compatibilidade ou, então, repensar as coisas. É assim mesmo.

Estamos fazendo várias simulações “em casa” e, tão logo tenhamos tempo para postar, bem, é isso que faremos :)

Mais explicações

O leitor atento vai questionar: Mas o Swing (que é o pacote UI inferido no exemplo devido o uso do Jbutton e do JTextField) não possui um evento onClick. Como se explica isso? Como funciona?

Rapidamente, a resposta é: O Merlin suporta aliases de nomes (e não só para eventos, mas para tudo – classes, métodos, etc.). Logo, tanto faz se escrever onClic, doClick, actionPerformed, etc. E o melhor de tudo, é que esses aliases são criados conforme o gosto do usuário e de forma facilitada, propagando-se (ou não) por todo o contexto da aplicação e mesmo de várias aplicações, caso seja de interesse. E claro, são intercambiáveis entre pacotes gráficos diferentes. Uma loucura, como diria um amigo meu...

A segunda pergunta é: E o método showPopup? Este método é obtido por inferência de proximidade. Primeiro, o Merlin tenta localizá-lo no próprio bean vinculado à tela. Não encontrando, procura métodos de aliases no contexto do formulário (por exemplo, se usado Web Beans ou Seam, seria um método de EJB ou análigo que tivesse a anotação @WebMethod) ou códigos de scripting (groovy, beanshel, etc) associados ao formulário. Se não encontrado, ele procura na hierarquia de classes do bean. Se encontrado, ele instancia esse bean e executa o método. Não encontrando, ele procura pelo escopo (primeiro) da aplicação – partindo dos pacotes mais próximos ao bean, e (depois) por todas as aplicações atingíveis pelo classloader da aplicação corrente. Nesse caso de busca por escopo, ele procura primeiro por classes com métodos públicos anotados com @Action (em classes ou métodos) e depois por métodos públicos somente. Toda essa busca é built-in da ferramenta, podendo ser customizada, sobreescrita ou desabilitada.

E ao leitor mais “bicudo” (ahah, lembrei o apelido que eu tinha na época que eu fazia curso de computação antigamente...) vai questionar? E como os parâmetros são passados para o método showPopup? No caso onde o método está diponível no próprio bean, não tem mistério: é acesso direto. Para os outros casos, é possível implementar uma interface InvocationContext, tal como no mundo dos EJBs ou usar a anotação @In, como no mundo do Seam/Web Beans. Outra forma é usar a API da ferramenta.