No projeto que estou desenvolvendo, criei um gerenciador de layout que
organiza os controles na tela de acordo com o espaço disponível nela e
a disposição do layout (vertical ou horizontal). Agora estou começando
a desenvolver a parte da geração da tela efetivamente, na qual
adiciono os controles em função das propriedades do objeto.
Pois bem, após adicionar os controles no layout precisaria definir o
tamanho da tela, mas aí vem a questão: como definir isso de forma que
a tela fique com um tamanho adequado?
É uma questão, digamos, um pouco recursiva, já que preciso definir um
tamanho adequado para a tela, a fim de que o gerenciador de layout
organize os controles, mas para determinar o tamanho da tela é preciso
que os controles estejam organizados...
Um exemplo disso: se insiro 10 controles de 200px de largura em uma
tela de 300px de largura, os controles ficarão um abaixo do outro, mas
se eu aumento a tela para 500px, os controles podem ficam em 2 colunas
tranquilamente. A questão é, como determinar a largura e o comprimento
da tela?
Como o Merlin dimensiona as telas geradas? Ele mesmo determina a
posição dos controles?
Diogo
Desculpa a demora. E por enquanto, também desculpa a resposta vaga,
pois código mesmo só posso mostrar depois que eu entregar a
dissertação....
Bem, Os algortitmos plugáveis que falo nada mais são que um "monte de
código escrito em qualquer e que pode fazer qualquer coisa em qualquer
parte do sistema". Na prática, a única exigência é a clássica
implementação de uma interface comum, que possui um método "doLayout",
que recebe como parâmetro o "root" do grafo de objetos que a tela de
cadastro está vinculada. É tarefa do algoritmo extrair as meta-
informações do grafo recebido e gerar o posicionamento e distribuição
dos controles como achar melhor. Essas meta-informações são as
anotações (JSR 175) do Java. Para .Net, uma outra empresa parceira
nossa deve estar assumindo a implementação, e daí não tenho detalhes
ainda (mas parece que o conceito é o mesmo). Aqui, Groovy e BeanShell
são uma mão na roda.
GUIs diferentes para uma mesma classe ainda não está estável e a
solução que tenho acho que pode ser melhorada. Na prática, o que faço
nesses casos é declarar interfaces "ocas" no mesmo nível da classe de
base. E usar essas interfaces no lugar das classes originais na hora
da geração. Porém, não é uma solução "bonita", por assim dizer. Outra
alternativa não descartada é usar o pattern Wrapper, onde a classe
original é empacotada noutra classe (o Wrapper). Daí, no wrapper
usando o padrão delegate, eu poderia sobreescrever somente aquilo que
o wrapper tem de diferente da classe original. Nesse caso, o wrapper
deveria ter um marcador (uma anotação), de forma que o Merlin saiba
que, ao recebê-lo, ele deve buscar as informações para geração ora da
classe/objeto empacotado e ora do wrapper. Essas abordagens contrastam
de outras soluções que vi na minha pesquisa, onde os caras usam
herança para resolver isso. Não gosto de herança para isso, pois
muitas vezes as telas podem estar operando sobre classes final, e daí
já era...
As heurísticas são feitas durante a montagem da tela, em tempo de
execução. Nada existe nada em tempo de compilação. Mas, toda a
execução pode ser prevista, ou seja, saberemos o que vai acontecer em
tempo de execução. Assim, não tem perigo de uma tela mudar a cada vez
que executa o sistema. Isso é garantido por um complexo mecanismo de
gerência de histórico (a parte mais complicada do sistema, pra dizer a
verdade) que já tem uma implementação básica, mas que deve ser
refinado muito ainda (na prática, acho que isso vai ser tema do meu
Doutorado no futuro...). É esse mecanismo que garante coisas como, se
um sistema S1 mudar alguma coisa, essa mudança possa ser propagada de
forma gerenciada para os sistemas S2, S3, S4, etc. ao longo da
cadeia...E note que isso é realimentado, pois a mudança em S2, nesse
caso implicaria novamente em mudanças em S3, S4 e assim, um ciclo
recursivo. Por isso é complexo :)
Bem, enchi de historinhas. Não sei se ajudei :)