Depois de desenvolvida a aplicação, é tempo de desenvolver a interface. Normalmente, uma aplicação será composta por uma classe relativa à aplicação (o Modelo, neste caso EstacaoServico) e uma classe que definirá a interface. A esta última vamos chamar ESView (ver apêndice H).
ESView é a classe da janela principal da nossa aplicação.
Para ser janela principal de uma aplicação deverá ser subclasse de ViewManager.
Uma janela típica em Smalltalk é composta por uma série de subpanes, cada uma das subpanes poderá ser de um tipo diferente, texto, uma lista, um botão, etc
.
Por convenção, as subclasses de ViewManager definem o método open ou openOn: para a inicialização das instâncias.
Neste caso usamos open.
O método começa por criar uma instancia de EstacaoServico e indicar que a label da janela deverá ser "E. S. Boa Viagem"
.
De seguida, são criadas as subpanes que constituem a janela.
Vamos definir seis subpanes, organizadas numa matriz de 2
3 (ver figura 1).
As panes da primeira linha são utilizadas como títulos das panes da segunda linha, que contêm as matrículas dos carros que estão em cada uma das listas de espera.
Começando pelo canto superior esquerdo, definimos então uma pane da classe StaticText (texto que não pode ser editado):
subpane1 := StaticText new.
subpane1 owner: self.
subpane1 centered.
subpane1 contents: 'Alinhamento'.
subpane1 framingRatio: (0@0 extent: (1/3)@(1/10)).
self addSubpane: subpane1.
Com
subpane1 owner: selfdeclaramos o dono da pane, neste caso a própria janela
Com
subpane1 framingRatio: (0@0 extent: (1/3)@(1/10))declaramos o tamanho da pane em coordenadas relativas, indicando o ponto superior esquerdo e o ponto inferior direito. Neste caso, a pane começa em 0@0 e vai até 1/3 da extensão horizontal e 1/10 da extensão vertical. Deste modo, se as dimensões da janela forem alteradas as dimensões da subpane ajustam-se automaticamente.
Finalmente, com
self addSubpane: subpane1a pane é adicionada à janela.
De seguida vamos definir a lista relativa ao alinhamento:
subpane2 := ListBox new
owner: self;
when: #getContents perform: #listAlinhamento:;
when: #select perform: #infoA:;
framingRatio: (0@(1/10) extent: (1/3)@(9/10)).
self addSubpane: subpane2.
Neste caso trata-se de uma ListBox.
Aqui, há que chamar a atenção para o método when:perform::
este método permite declarar que quando (when) um dado evento ocorre a pane deve executar (perform) um dado métodoDe seguida são adicionadas as subpanes restantes. Apenas a título de exemplo, uma das panes é declarada com um dono diferente de self:
self addSubpane:
( ListBox new
owner: (estacao caixa);
when: #getContents perform: #listCaixa:;
when: #select perform: #info:;
framingRatio: ((2/3)@(1/10) extent: (1/3)@(9/10))
).
Esta pane é declarada com dono (estacao caixa) (a caixa da estação de serviço), deste modo os métodos listCaixa: e info: deverão ser métodos da classe CaixaFinalmente, é enviada a self a mensagem openWindow e adicionado um menu à janela. O menu é criado pela método menuES:
menuES
"Menu da ES"
| menu |
menu := Menu labels: ('&Chega Carro\Regista &Lavagem\Regista
&Alinhamento\Regista &Pagamento' withCrs
)
lines: #(1 3)
selectors: #(chegaCarro regLav regAl regCaixa).
menu owner: self;
title: '&Gestao'.
^menu!
O método labels:lines:selectors: cria um menu.
Em labels indicam-se os nomes das opções, o & indica o caracter que servirá de atalho para a opção.
Em lines indicam-se as posições de linhas separadoras no menu.
Em selectors lista-se, para cada opção, o método que deverá ser utilizado quando a opção é seleccionada.
O método chegaCarro:
chegaCarro
"chegar um carro"
| car |
car := LeCarro new ler.
( car = nil ) ifFalse: [
estacao chegaCarro: car.
self changed:#listAlinhamento:.
self changed:#listLavagem:]
começa por criar um automóvel e, de seguida, coloca-o na estação de serviço.
Como a chegada de um carro à estação pode alterar as listas de espera de alinhamento e de lavagem, é enviada a self (a janela) a mensagem changed com listAlinhamento (para actualizar a lista de alinhamentos) e com listLavagem (para actualizar a lista de lavagens)Para criar um automóvel vamos utilizar uma janela de diálogo para ler as informações relativas ao mesmo (ver fig. 2).
Para tal criou-se a classe LeCarro (ver apêndice I) como subclasse de WindowDialog.
O método ler é enviado a uma instância desta classe e cria as subpanes necessárias para a leitura da informação relativa ao automóvel. São utilizadas panes de tipo EntryField para leitura de valores:
self addSubpane:
(EntryField new
setName: #matricula;
contents: '';
framingBlock:
[:box | (box leftTop rightAndDown: 40@lineHeight)
extentFromLeftTop: 90@lineHeight ]
).
A mensagem setName: indica um nome para a pane e permite depois "ir buscar" o seu valor (ver utilização do método query: em ok:).
As coordenadas são, agora, dadas de modo diferente: neste caso são dadas de modo absoluto e não relativo, utilizando framingBlock:.
A partir de uma caixa que é passada como parâmetro (box)Para a indicação do estado do automóvel são utilizadas check boxes:
self addSubpane:
(CheckBox new
owner: self;
contents: 'para Lavagem';
when: #clicked perform: #setLavagem:;
framingBlock:
[:box | (box leftTop rightAndDown: 5@(8*lineHeight))
extentFromLeftTop: 90@lineHeight ]
).
O método deverá ser de fácil interpretação.
Nota-se ainda a utilização de botões:
self addSubpane:
(Button new
defaultPushButton;
contents: 'OK';
idOK;
when: #clicked perform: #ok:;
framingBlock:
[:box | (box leftTop rightAndDown: (17@10)*charSize)
extentFromLeftTop: (8@2)*charSize]
).
A mensagem defaultPushButton serve para indicar que este é o botão selccionado por defeito.
A mensagem idOK serve para indicar que este botão deverá ser activado quando o utilizador prime Enter, existe também a mensagem idCancel que faz com que o botão seja activado quando o utilizador carrega em Escape e que é utilizada no botão Cancel.