@designliquido/delegua-interface-grafica 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.release-it.json +15 -0
- package/LICENSE +21 -0
- package/README.md +90 -0
- package/fontes/delegua-modulo.ts +238 -0
- package/fontes/index.ts +3 -0
- package/fontes/infraestruturas/electron/README.md +92 -0
- package/fontes/infraestruturas/electron/infraestrutura-electron.ts +284 -0
- package/fontes/infraestruturas/index.ts +2 -0
- package/fontes/infraestruturas/vazia/README.md +53 -0
- package/fontes/infraestruturas/vazia/infraestrutura-vazia.ts +61 -0
- package/fontes/interface-grafica.ts +172 -0
- package/fontes/interfaces/componente-interface-grafica-interface.ts +8 -0
- package/fontes/interfaces/index.ts +2 -0
- package/fontes/interfaces/infraestrutura-grafica-interface.ts +46 -0
- package/jest.config.ts +21 -0
- package/package.json +41 -0
- package/testes/interface-grafica.test.ts +159 -0
- package/tsconfig.json +22 -0
- package/tsconfig.spec.json +21 -0
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
import { InfraestruturaGraficaInterface } from '../../interfaces/infraestrutura-grafica-interface';
|
|
2
|
+
import { ComponenteInterfaceGraficaInterface } from '../../interfaces/componente-interface-grafica-interface';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Mapeamento de nomes de eventos Delégua para eventos DOM correspondentes.
|
|
6
|
+
*/
|
|
7
|
+
const MAPA_EVENTOS: Record<string, string> = {
|
|
8
|
+
clique: 'click',
|
|
9
|
+
alterado: 'input',
|
|
10
|
+
tecla: 'keydown',
|
|
11
|
+
foco: 'focus',
|
|
12
|
+
desfoco: 'blur',
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Infraestrutura de interface gráfica para o ambiente Electron.
|
|
17
|
+
* Cria elementos DOM no `document.body` da janela do Electron.
|
|
18
|
+
*
|
|
19
|
+
* O programa Delégua enxerga cada componente como um identificador opaco
|
|
20
|
+
* (`ComponenteInterfaceGraficaInterface`). Internamente, este infraestrutura mantém um mapa entre esses
|
|
21
|
+
* identificadores e os `HTMLElement`s correspondentes.
|
|
22
|
+
*
|
|
23
|
+
* Cada instância de `InfraestruturaElectron` gerencia uma janela independente:
|
|
24
|
+
* um `div` flutuante criado sobre o conteúdo existente da página.
|
|
25
|
+
*/
|
|
26
|
+
export class InfraestruturaElectron implements InfraestruturaGraficaInterface {
|
|
27
|
+
private readonly elementosPorId: Map<string, HTMLElement> = new Map();
|
|
28
|
+
private contadorIds = 0;
|
|
29
|
+
private sobreposicao: HTMLElement | null = null;
|
|
30
|
+
private resolverLaco: (() => void) | null = null;
|
|
31
|
+
|
|
32
|
+
// -------------------------------------------------------------------------
|
|
33
|
+
// Helpers internos
|
|
34
|
+
// -------------------------------------------------------------------------
|
|
35
|
+
|
|
36
|
+
private proximoId(): string {
|
|
37
|
+
return `delegua-gui-${++this.contadorIds}`;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
private registrar(elemento: HTMLElement): ComponenteInterfaceGraficaInterface {
|
|
41
|
+
const idComponente = this.proximoId();
|
|
42
|
+
this.elementosPorId.set(idComponente, elemento);
|
|
43
|
+
return { idComponente };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
private obterElemento(componente: ComponenteInterfaceGraficaInterface): HTMLElement {
|
|
47
|
+
const elemento = this.elementosPorId.get(componente.idComponente);
|
|
48
|
+
if (!elemento) {
|
|
49
|
+
throw new Error(
|
|
50
|
+
`Componente '${componente.idComponente}' não encontrado. ` +
|
|
51
|
+
`Verifique se ele foi criado pela mesma instância de InfraestruturaElectron.`
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
return elemento;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// -------------------------------------------------------------------------
|
|
58
|
+
// Criação de componentes
|
|
59
|
+
// -------------------------------------------------------------------------
|
|
60
|
+
|
|
61
|
+
criarJanela(largura: number, altura: number, titulo: string): ComponenteInterfaceGraficaInterface {
|
|
62
|
+
this.sobreposicao = document.createElement('div');
|
|
63
|
+
Object.assign(this.sobreposicao.style, {
|
|
64
|
+
position: 'fixed',
|
|
65
|
+
top: '0',
|
|
66
|
+
left: '0',
|
|
67
|
+
width: '100%',
|
|
68
|
+
height: '100%',
|
|
69
|
+
background: 'rgba(0, 0, 0, 0.4)',
|
|
70
|
+
display: 'flex',
|
|
71
|
+
alignItems: 'center',
|
|
72
|
+
justifyContent: 'center',
|
|
73
|
+
zIndex: '9999',
|
|
74
|
+
fontFamily: 'system-ui, -apple-system, sans-serif',
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
const moldura = document.createElement('div');
|
|
78
|
+
Object.assign(moldura.style, {
|
|
79
|
+
background: '#f0f0f0',
|
|
80
|
+
border: '1px solid #aaa',
|
|
81
|
+
borderRadius: '5px',
|
|
82
|
+
width: `${largura}px`,
|
|
83
|
+
height: `${altura}px`,
|
|
84
|
+
display: 'flex',
|
|
85
|
+
flexDirection: 'column',
|
|
86
|
+
boxShadow: '0 6px 24px rgba(0, 0, 0, 0.4)',
|
|
87
|
+
overflow: 'hidden',
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// Barra de título
|
|
91
|
+
const barraTitulo = document.createElement('div');
|
|
92
|
+
Object.assign(barraTitulo.style, {
|
|
93
|
+
background: '#3c3c3c',
|
|
94
|
+
color: 'white',
|
|
95
|
+
padding: '6px 10px',
|
|
96
|
+
fontSize: '13px',
|
|
97
|
+
display: 'flex',
|
|
98
|
+
alignItems: 'center',
|
|
99
|
+
justifyContent: 'space-between',
|
|
100
|
+
userSelect: 'none',
|
|
101
|
+
flexShrink: '0',
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
const textoTitulo = document.createElement('span');
|
|
105
|
+
textoTitulo.textContent = titulo;
|
|
106
|
+
|
|
107
|
+
const botaoFechar = document.createElement('button');
|
|
108
|
+
botaoFechar.textContent = '×';
|
|
109
|
+
Object.assign(botaoFechar.style, {
|
|
110
|
+
background: 'none',
|
|
111
|
+
border: 'none',
|
|
112
|
+
color: 'white',
|
|
113
|
+
fontSize: '20px',
|
|
114
|
+
lineHeight: '1',
|
|
115
|
+
cursor: 'pointer',
|
|
116
|
+
padding: '0 2px',
|
|
117
|
+
});
|
|
118
|
+
botaoFechar.addEventListener('click', () => this.encerrar());
|
|
119
|
+
|
|
120
|
+
barraTitulo.appendChild(textoTitulo);
|
|
121
|
+
barraTitulo.appendChild(botaoFechar);
|
|
122
|
+
|
|
123
|
+
// Área de conteúdo — é aqui que os filhos da janela são inseridos
|
|
124
|
+
const areaConteudo = document.createElement('div');
|
|
125
|
+
Object.assign(areaConteudo.style, {
|
|
126
|
+
flex: '1',
|
|
127
|
+
overflow: 'auto',
|
|
128
|
+
padding: '10px',
|
|
129
|
+
display: 'flex',
|
|
130
|
+
flexDirection: 'column',
|
|
131
|
+
gap: '8px',
|
|
132
|
+
boxSizing: 'border-box',
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
moldura.appendChild(barraTitulo);
|
|
136
|
+
moldura.appendChild(areaConteudo);
|
|
137
|
+
this.sobreposicao.appendChild(moldura);
|
|
138
|
+
document.body.appendChild(this.sobreposicao);
|
|
139
|
+
|
|
140
|
+
// O idComponente da janela aponta para areaConteudo,
|
|
141
|
+
// onde os filhos serão inseridos diretamente.
|
|
142
|
+
return this.registrar(areaConteudo);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
criarBotao(pai: ComponenteInterfaceGraficaInterface, rotulo: string): ComponenteInterfaceGraficaInterface {
|
|
146
|
+
const elementoPai = this.obterElemento(pai);
|
|
147
|
+
const botao = document.createElement('button');
|
|
148
|
+
botao.textContent = rotulo;
|
|
149
|
+
Object.assign(botao.style, {
|
|
150
|
+
padding: '6px 16px',
|
|
151
|
+
fontSize: '13px',
|
|
152
|
+
cursor: 'pointer',
|
|
153
|
+
border: '1px solid #999',
|
|
154
|
+
borderRadius: '3px',
|
|
155
|
+
background: '#e8e8e8',
|
|
156
|
+
alignSelf: 'flex-start',
|
|
157
|
+
});
|
|
158
|
+
elementoPai.appendChild(botao);
|
|
159
|
+
return this.registrar(botao);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
criarRotulo(pai: ComponenteInterfaceGraficaInterface, texto: string): ComponenteInterfaceGraficaInterface {
|
|
163
|
+
const elementoPai = this.obterElemento(pai);
|
|
164
|
+
const rotulo = document.createElement('label');
|
|
165
|
+
rotulo.textContent = texto;
|
|
166
|
+
Object.assign(rotulo.style, {
|
|
167
|
+
fontSize: '13px',
|
|
168
|
+
color: '#222',
|
|
169
|
+
});
|
|
170
|
+
elementoPai.appendChild(rotulo);
|
|
171
|
+
return this.registrar(rotulo);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
criarCaixaTexto(pai: ComponenteInterfaceGraficaInterface, textoInicial: string): ComponenteInterfaceGraficaInterface {
|
|
175
|
+
const elementoPai = this.obterElemento(pai);
|
|
176
|
+
const entrada = document.createElement('input');
|
|
177
|
+
entrada.type = 'text';
|
|
178
|
+
entrada.value = textoInicial;
|
|
179
|
+
Object.assign(entrada.style, {
|
|
180
|
+
fontSize: '13px',
|
|
181
|
+
padding: '5px 8px',
|
|
182
|
+
border: '1px solid #999',
|
|
183
|
+
borderRadius: '3px',
|
|
184
|
+
boxSizing: 'border-box',
|
|
185
|
+
});
|
|
186
|
+
elementoPai.appendChild(entrada);
|
|
187
|
+
return this.registrar(entrada);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
criarCaixaVertical(pai: ComponenteInterfaceGraficaInterface): ComponenteInterfaceGraficaInterface {
|
|
191
|
+
const elementoPai = this.obterElemento(pai);
|
|
192
|
+
const caixa = document.createElement('div');
|
|
193
|
+
Object.assign(caixa.style, {
|
|
194
|
+
display: 'flex',
|
|
195
|
+
flexDirection: 'column',
|
|
196
|
+
gap: '8px',
|
|
197
|
+
});
|
|
198
|
+
elementoPai.appendChild(caixa);
|
|
199
|
+
return this.registrar(caixa);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
criarCaixaHorizontal(pai: ComponenteInterfaceGraficaInterface): ComponenteInterfaceGraficaInterface {
|
|
203
|
+
const elementoPai = this.obterElemento(pai);
|
|
204
|
+
const caixa = document.createElement('div');
|
|
205
|
+
Object.assign(caixa.style, {
|
|
206
|
+
display: 'flex',
|
|
207
|
+
flexDirection: 'row',
|
|
208
|
+
gap: '8px',
|
|
209
|
+
alignItems: 'center',
|
|
210
|
+
});
|
|
211
|
+
elementoPai.appendChild(caixa);
|
|
212
|
+
return this.registrar(caixa);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// -------------------------------------------------------------------------
|
|
216
|
+
// Leitura e escrita de propriedades
|
|
217
|
+
// -------------------------------------------------------------------------
|
|
218
|
+
|
|
219
|
+
definirTexto(componente: ComponenteInterfaceGraficaInterface, texto: string): void {
|
|
220
|
+
const elemento = this.obterElemento(componente);
|
|
221
|
+
if (elemento instanceof HTMLInputElement || elemento instanceof HTMLTextAreaElement) {
|
|
222
|
+
elemento.value = texto;
|
|
223
|
+
} else {
|
|
224
|
+
elemento.textContent = texto;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
obterTexto(componente: ComponenteInterfaceGraficaInterface): string {
|
|
229
|
+
const elemento = this.obterElemento(componente);
|
|
230
|
+
if (elemento instanceof HTMLInputElement || elemento instanceof HTMLTextAreaElement) {
|
|
231
|
+
return elemento.value;
|
|
232
|
+
}
|
|
233
|
+
return elemento.textContent ?? '';
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// -------------------------------------------------------------------------
|
|
237
|
+
// Eventos
|
|
238
|
+
// -------------------------------------------------------------------------
|
|
239
|
+
|
|
240
|
+
conectarEvento(
|
|
241
|
+
componente: ComponenteInterfaceGraficaInterface,
|
|
242
|
+
evento: string,
|
|
243
|
+
callback: (...argumentos: any[]) => Promise<void>
|
|
244
|
+
): void {
|
|
245
|
+
const elemento = this.obterElemento(componente);
|
|
246
|
+
const eventoDOM = MAPA_EVENTOS[evento] ?? evento;
|
|
247
|
+
|
|
248
|
+
elemento.addEventListener(eventoDOM, (e: Event) => {
|
|
249
|
+
// Para o evento 'alterado' em campos de texto, passa o valor atual.
|
|
250
|
+
// Para demais eventos, não passa argumentos ao callback.
|
|
251
|
+
if (
|
|
252
|
+
evento === 'alterado' &&
|
|
253
|
+
(elemento instanceof HTMLInputElement || elemento instanceof HTMLTextAreaElement)
|
|
254
|
+
) {
|
|
255
|
+
callback(elemento.value).catch(console.error);
|
|
256
|
+
} else {
|
|
257
|
+
callback().catch(console.error);
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// -------------------------------------------------------------------------
|
|
263
|
+
// Ciclo de vida
|
|
264
|
+
// -------------------------------------------------------------------------
|
|
265
|
+
|
|
266
|
+
async iniciarLaco(): Promise<void> {
|
|
267
|
+
return new Promise<void>((resolve) => {
|
|
268
|
+
this.resolverLaco = resolve;
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
encerrar(): void {
|
|
273
|
+
if (this.sobreposicao?.parentNode) {
|
|
274
|
+
this.sobreposicao.parentNode.removeChild(this.sobreposicao);
|
|
275
|
+
this.sobreposicao = null;
|
|
276
|
+
}
|
|
277
|
+
this.elementosPorId.clear();
|
|
278
|
+
|
|
279
|
+
if (this.resolverLaco) {
|
|
280
|
+
this.resolverLaco();
|
|
281
|
+
this.resolverLaco = null;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# InfraestruturaVazia
|
|
2
|
+
|
|
3
|
+
Implementação de `InfraestruturaGraficaInterface` sem operações visuais. Nenhum elemento
|
|
4
|
+
DOM é criado e nenhuma janela é exibida. Destinada exclusivamente a **testes unitários** e
|
|
5
|
+
a ambientes sem interface gráfica (CI, Node.js puro).
|
|
6
|
+
|
|
7
|
+
## Comportamento
|
|
8
|
+
|
|
9
|
+
### Criação de componentes
|
|
10
|
+
|
|
11
|
+
Todos os métodos `criar*` retornam objetos `ComponenteInterfaceGraficaInterface` com IDs
|
|
12
|
+
únicos gerados por um contador de instância (e.g. `componente-1`, `componente-2`, …).
|
|
13
|
+
Parâmetros como `pai`, `largura`, `altura` e `titulo` são ignorados.
|
|
14
|
+
|
|
15
|
+
### Estado de texto
|
|
16
|
+
|
|
17
|
+
`criarRotulo` e `criarCaixaTexto` armazenam o texto inicial em um
|
|
18
|
+
`Map<string, string>` indexado pelo `idComponente`. `definirTexto` atualiza essa entrada;
|
|
19
|
+
`obterTexto` a lê (retorna `''` caso o componente não tenha texto registrado).
|
|
20
|
+
|
|
21
|
+
Componentes que não carregam texto (janela, botão, caixas de layout) não têm entrada no
|
|
22
|
+
mapa — `obterTexto` devolve `''` para eles.
|
|
23
|
+
|
|
24
|
+
### Eventos
|
|
25
|
+
|
|
26
|
+
`conectarEvento` aceita o registro sem lançar exceção, mas descarta o callback. Nenhum
|
|
27
|
+
evento é disparado internamente.
|
|
28
|
+
|
|
29
|
+
### Ciclo de vida
|
|
30
|
+
|
|
31
|
+
| Método | Comportamento |
|
|
32
|
+
|---------------|----------------------------------------|
|
|
33
|
+
| `iniciarLaco` | Resolve imediatamente (`async` vazio). |
|
|
34
|
+
| `encerrar` | Não faz nada. |
|
|
35
|
+
|
|
36
|
+
## Uso em testes
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
import { InfraestruturaVazia } from './infraestrutura-vazia';
|
|
40
|
+
import { InterfaceGrafica } from '../../interface-grafica';
|
|
41
|
+
|
|
42
|
+
const infraestrutura = new InfraestruturaVazia();
|
|
43
|
+
const ig = new InterfaceGrafica(infraestrutura);
|
|
44
|
+
|
|
45
|
+
const janela = ig.janela(interpretador, 800, 600, 'Teste');
|
|
46
|
+
const rotulo = ig.rotulo(interpretador, janela, 'Olá');
|
|
47
|
+
|
|
48
|
+
ig.definirTexto(interpretador, rotulo, 'Mundo');
|
|
49
|
+
expect(ig.obterTexto(interpretador, rotulo)).toBe('Mundo');
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Crie uma nova instância de `InfraestruturaVazia` em cada `beforeEach` para evitar
|
|
53
|
+
vazamento de estado entre testes.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { InfraestruturaGraficaInterface } from '../../interfaces/infraestrutura-grafica-interface';
|
|
2
|
+
import { ComponenteInterfaceGraficaInterface } from '../../interfaces/componente-interface-grafica-interface';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Infraestrutura sem operações: não cria janelas nem elementos visuais.
|
|
6
|
+
* Útil para testes unitários e ambientes sem interface gráfica.
|
|
7
|
+
*/
|
|
8
|
+
export class InfraestruturaVazia implements InfraestruturaGraficaInterface {
|
|
9
|
+
private contadorIds = 0;
|
|
10
|
+
private textos: Map<string, string> = new Map();
|
|
11
|
+
|
|
12
|
+
private novoComponente(): ComponenteInterfaceGraficaInterface {
|
|
13
|
+
return { idComponente: `componente-${++this.contadorIds}` };
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
criarJanela(_largura: number, _altura: number, _titulo: string): ComponenteInterfaceGraficaInterface {
|
|
17
|
+
return this.novoComponente();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
criarBotao(_pai: ComponenteInterfaceGraficaInterface, _rotulo: string): ComponenteInterfaceGraficaInterface {
|
|
21
|
+
return this.novoComponente();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
criarRotulo(_pai: ComponenteInterfaceGraficaInterface, texto: string): ComponenteInterfaceGraficaInterface {
|
|
25
|
+
const componente = this.novoComponente();
|
|
26
|
+
this.textos.set(componente.idComponente, texto);
|
|
27
|
+
return componente;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
criarCaixaTexto(_pai: ComponenteInterfaceGraficaInterface, textoInicial: string): ComponenteInterfaceGraficaInterface {
|
|
31
|
+
const componente = this.novoComponente();
|
|
32
|
+
this.textos.set(componente.idComponente, textoInicial);
|
|
33
|
+
return componente;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
criarCaixaVertical(_pai: ComponenteInterfaceGraficaInterface): ComponenteInterfaceGraficaInterface {
|
|
37
|
+
return this.novoComponente();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
criarCaixaHorizontal(_pai: ComponenteInterfaceGraficaInterface): ComponenteInterfaceGraficaInterface {
|
|
41
|
+
return this.novoComponente();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
definirTexto(componente: ComponenteInterfaceGraficaInterface, texto: string): void {
|
|
45
|
+
this.textos.set(componente.idComponente, texto);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
obterTexto(componente: ComponenteInterfaceGraficaInterface): string {
|
|
49
|
+
return this.textos.get(componente.idComponente) ?? '';
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
conectarEvento(
|
|
53
|
+
_componente: ComponenteInterfaceGraficaInterface,
|
|
54
|
+
_evento: string,
|
|
55
|
+
_callback: (...argumentos: any[]) => Promise<void>
|
|
56
|
+
): void {}
|
|
57
|
+
|
|
58
|
+
async iniciarLaco(): Promise<void> {}
|
|
59
|
+
|
|
60
|
+
encerrar(): void {}
|
|
61
|
+
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { InterpretadorInterface } from '@designliquido/delegua';
|
|
2
|
+
import { InfraestruturaGraficaInterface } from './interfaces/infraestrutura-grafica-interface';
|
|
3
|
+
import { ComponenteInterfaceGraficaInterface } from './interfaces/componente-interface-grafica-interface';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Biblioteca de interface gráfica para Delégua.
|
|
7
|
+
*
|
|
8
|
+
* Cada método público recebe o interpretador como primeiro argumento
|
|
9
|
+
* porque são registrados como `FuncaoPadrao`, que repassa o visitante
|
|
10
|
+
* automaticamente. Os argumentos seguintes são os fornecidos pelo código Delégua.
|
|
11
|
+
*/
|
|
12
|
+
export class InterfaceGrafica {
|
|
13
|
+
constructor(private readonly backend: InfraestruturaGraficaInterface) {}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Cria a janela principal do programa.
|
|
17
|
+
* @param interpretador Passado automaticamente pelo interpretador Delégua.
|
|
18
|
+
* @param largura Largura em pixels.
|
|
19
|
+
* @param altura Altura em pixels.
|
|
20
|
+
* @param titulo Título exibido na barra da janela.
|
|
21
|
+
*/
|
|
22
|
+
janela(
|
|
23
|
+
_interpretador: InterpretadorInterface,
|
|
24
|
+
largura: number,
|
|
25
|
+
altura: number,
|
|
26
|
+
titulo: string
|
|
27
|
+
): ComponenteInterfaceGraficaInterface {
|
|
28
|
+
return this.backend.criarJanela(largura, altura, titulo);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Cria um botão dentro do componente pai.
|
|
33
|
+
* @param interpretador Passado automaticamente pelo interpretador Delégua.
|
|
34
|
+
* @param pai Componente que conterá o botão.
|
|
35
|
+
* @param rotulo Texto exibido no botão.
|
|
36
|
+
*/
|
|
37
|
+
botao(
|
|
38
|
+
_interpretador: InterpretadorInterface,
|
|
39
|
+
pai: ComponenteInterfaceGraficaInterface,
|
|
40
|
+
rotulo: string
|
|
41
|
+
): ComponenteInterfaceGraficaInterface {
|
|
42
|
+
return this.backend.criarBotao(pai, rotulo);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Cria um rótulo de texto dentro do componente pai.
|
|
47
|
+
* @param interpretador Passado automaticamente pelo interpretador Delégua.
|
|
48
|
+
* @param pai Componente que conterá o rótulo.
|
|
49
|
+
* @param texto Texto exibido.
|
|
50
|
+
*/
|
|
51
|
+
rotulo(
|
|
52
|
+
_interpretador: InterpretadorInterface,
|
|
53
|
+
pai: ComponenteInterfaceGraficaInterface,
|
|
54
|
+
texto: string
|
|
55
|
+
): ComponenteInterfaceGraficaInterface {
|
|
56
|
+
return this.backend.criarRotulo(pai, texto);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Cria uma caixa de texto editável dentro do componente pai.
|
|
61
|
+
* @param interpretador Passado automaticamente pelo interpretador Delégua.
|
|
62
|
+
* @param pai Componente que conterá a caixa de texto.
|
|
63
|
+
* @param textoInicial Valor inicial da caixa.
|
|
64
|
+
*/
|
|
65
|
+
caixaTexto(
|
|
66
|
+
_interpretador: InterpretadorInterface,
|
|
67
|
+
pai: ComponenteInterfaceGraficaInterface,
|
|
68
|
+
textoInicial: string = ''
|
|
69
|
+
): ComponenteInterfaceGraficaInterface {
|
|
70
|
+
return this.backend.criarCaixaTexto(pai, textoInicial ?? '');
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Cria um contêiner com layout vertical (empilhamento de cima para baixo).
|
|
75
|
+
* @param interpretador Passado automaticamente pelo interpretador Delégua.
|
|
76
|
+
* @param pai Componente pai do contêiner.
|
|
77
|
+
*/
|
|
78
|
+
caixaVertical(
|
|
79
|
+
_interpretador: InterpretadorInterface,
|
|
80
|
+
pai: ComponenteInterfaceGraficaInterface
|
|
81
|
+
): ComponenteInterfaceGraficaInterface {
|
|
82
|
+
return this.backend.criarCaixaVertical(pai);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Cria um contêiner com layout horizontal (empilhamento da esquerda para direita).
|
|
87
|
+
* @param interpretador Passado automaticamente pelo interpretador Delégua.
|
|
88
|
+
* @param pai Componente pai do contêiner.
|
|
89
|
+
*/
|
|
90
|
+
caixaHorizontal(
|
|
91
|
+
_interpretador: InterpretadorInterface,
|
|
92
|
+
pai: ComponenteInterfaceGraficaInterface
|
|
93
|
+
): ComponenteInterfaceGraficaInterface {
|
|
94
|
+
return this.backend.criarCaixaHorizontal(pai);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Altera o texto de um componente (rótulo ou caixa de texto).
|
|
99
|
+
* @param interpretador Passado automaticamente pelo interpretador Delégua.
|
|
100
|
+
* @param componente Componente a ser alterado.
|
|
101
|
+
* @param texto Novo texto.
|
|
102
|
+
*/
|
|
103
|
+
definirTexto(
|
|
104
|
+
_interpretador: InterpretadorInterface,
|
|
105
|
+
componente: ComponenteInterfaceGraficaInterface,
|
|
106
|
+
texto: string
|
|
107
|
+
): void {
|
|
108
|
+
this.backend.definirTexto(componente, texto);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Lê o texto atual de um componente (rótulo ou caixa de texto).
|
|
113
|
+
* @param interpretador Passado automaticamente pelo interpretador Delégua.
|
|
114
|
+
* @param componente Componente a ser lido.
|
|
115
|
+
*/
|
|
116
|
+
obterTexto(
|
|
117
|
+
_interpretador: InterpretadorInterface,
|
|
118
|
+
componente: ComponenteInterfaceGraficaInterface
|
|
119
|
+
): string {
|
|
120
|
+
return this.backend.obterTexto(componente);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Registra uma função Delégua para ser chamada ao clicar no componente.
|
|
125
|
+
* @param interpretador Passado automaticamente pelo interpretador Delégua.
|
|
126
|
+
* @param componente Componente que receberá o evento.
|
|
127
|
+
* @param funcaoDelegua Função declarada em Delégua a ser invocada no clique.
|
|
128
|
+
*/
|
|
129
|
+
aoClicar(
|
|
130
|
+
interpretador: InterpretadorInterface,
|
|
131
|
+
componente: ComponenteInterfaceGraficaInterface,
|
|
132
|
+
funcaoDelegua: any
|
|
133
|
+
): void {
|
|
134
|
+
this.backend.conectarEvento(componente, 'clique', async () => {
|
|
135
|
+
await interpretador.executarChamavel(funcaoDelegua, []);
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Registra uma função Delégua para ser chamada quando o texto do componente mudar.
|
|
141
|
+
* @param interpretador Passado automaticamente pelo interpretador Delégua.
|
|
142
|
+
* @param componente Componente que receberá o evento.
|
|
143
|
+
* @param funcaoDelegua Função declarada em Delégua a ser invocada na mudança.
|
|
144
|
+
*/
|
|
145
|
+
aoAlterar(
|
|
146
|
+
interpretador: InterpretadorInterface,
|
|
147
|
+
componente: ComponenteInterfaceGraficaInterface,
|
|
148
|
+
funcaoDelegua: any
|
|
149
|
+
): void {
|
|
150
|
+
this.backend.conectarEvento(componente, 'alterado', async (novoTexto: string) => {
|
|
151
|
+
await interpretador.executarChamavel(funcaoDelegua, [novoTexto]);
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Inicia o laço de eventos da interface gráfica. Este método bloqueia
|
|
157
|
+
* a execução do programa Delégua até que a janela seja fechada.
|
|
158
|
+
* Deve ser chamado após criar e configurar todos os componentes.
|
|
159
|
+
* @param interpretador Passado automaticamente pelo interpretador Delégua.
|
|
160
|
+
*/
|
|
161
|
+
async iniciar(_interpretador: InterpretadorInterface): Promise<void> {
|
|
162
|
+
await this.backend.iniciarLaco();
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Encerra a interface gráfica e fecha todas as janelas.
|
|
167
|
+
* @param interpretador Passado automaticamente pelo interpretador Delégua.
|
|
168
|
+
*/
|
|
169
|
+
encerrar(_interpretador: InterpretadorInterface): void {
|
|
170
|
+
this.backend.encerrar();
|
|
171
|
+
}
|
|
172
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Identificador opaco de um componente de interface gráfica.
|
|
3
|
+
* Delégua recebe e devolve este objeto às funções da biblioteca.
|
|
4
|
+
* Cada infraestrutura é responsável por implementar e interpretar o conteúdo interno.
|
|
5
|
+
*/
|
|
6
|
+
export interface ComponenteInterfaceGraficaInterface {
|
|
7
|
+
readonly idComponente: string;
|
|
8
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { ComponenteInterfaceGraficaInterface } from './componente-interface-grafica-interface';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Contrato que todo infraestrutura de interface gráfica deve implementar.
|
|
5
|
+
* O infraestrutura é responsável por criar e manipular os elementos visuais
|
|
6
|
+
* no ambiente de execução (Electron, navegador, etc.).
|
|
7
|
+
*
|
|
8
|
+
* As chamadas de eventos recebem um callback já resolvido: o backend
|
|
9
|
+
* não precisa conhecer o interpretador nem os tipos de Delégua.
|
|
10
|
+
*/
|
|
11
|
+
export interface InfraestruturaGraficaInterface {
|
|
12
|
+
// Criação de componentes
|
|
13
|
+
criarJanela(largura: number, altura: number, titulo: string): ComponenteInterfaceGraficaInterface;
|
|
14
|
+
criarBotao(pai: ComponenteInterfaceGraficaInterface, rotulo: string): ComponenteInterfaceGraficaInterface;
|
|
15
|
+
criarRotulo(pai: ComponenteInterfaceGraficaInterface, texto: string): ComponenteInterfaceGraficaInterface;
|
|
16
|
+
criarCaixaTexto(pai: ComponenteInterfaceGraficaInterface, textoInicial: string): ComponenteInterfaceGraficaInterface;
|
|
17
|
+
criarCaixaVertical(pai: ComponenteInterfaceGraficaInterface): ComponenteInterfaceGraficaInterface;
|
|
18
|
+
criarCaixaHorizontal(pai: ComponenteInterfaceGraficaInterface): ComponenteInterfaceGraficaInterface;
|
|
19
|
+
|
|
20
|
+
// Leitura e escrita de propriedades
|
|
21
|
+
definirTexto(componente: ComponenteInterfaceGraficaInterface, texto: string): void;
|
|
22
|
+
obterTexto(componente: ComponenteInterfaceGraficaInterface): string;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Registra um callback nativo a ser invocado quando o evento ocorrer.
|
|
26
|
+
* O callback já encapsula a chamada ao interpretador de Delégua,
|
|
27
|
+
* portanto o infraestrutura só precisa chamá-lo no momento certo.
|
|
28
|
+
* @param componente O componente-alvo do evento.
|
|
29
|
+
* @param evento Nome do evento: 'clique', 'alterado', 'tecla', etc.
|
|
30
|
+
* @param callback Função assíncrona a ser chamada quando o evento ocorrer.
|
|
31
|
+
*/
|
|
32
|
+
conectarEvento(
|
|
33
|
+
componente: ComponenteInterfaceGraficaInterface,
|
|
34
|
+
evento: string,
|
|
35
|
+
callback: (...argumentos: any[]) => Promise<void>
|
|
36
|
+
): void;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Inicia o laço de eventos. Resolve quando a janela principal for fechada
|
|
40
|
+
* ou quando `encerrar()` for chamado.
|
|
41
|
+
*/
|
|
42
|
+
iniciarLaco(): Promise<void>;
|
|
43
|
+
|
|
44
|
+
/** Encerra o laço de eventos e fecha todas as janelas. */
|
|
45
|
+
encerrar(): void;
|
|
46
|
+
}
|
package/jest.config.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Config } from '@jest/types';
|
|
2
|
+
|
|
3
|
+
export default async (): Promise<Config.InitialOptions> => {
|
|
4
|
+
return {
|
|
5
|
+
verbose: true,
|
|
6
|
+
modulePathIgnorePatterns: ['<rootDir>/dist/'],
|
|
7
|
+
preset: 'ts-jest',
|
|
8
|
+
testEnvironment: 'node',
|
|
9
|
+
transform: {
|
|
10
|
+
'^.+\\.tsx?$': ['ts-jest', {
|
|
11
|
+
isolatedModules: false,
|
|
12
|
+
tsconfig: {
|
|
13
|
+
sourceMap: true,
|
|
14
|
+
inlineSourceMap: true,
|
|
15
|
+
inlineSources: true
|
|
16
|
+
}
|
|
17
|
+
}]
|
|
18
|
+
},
|
|
19
|
+
coverageReporters: ['json-summary', 'lcov', 'text', 'text-summary']
|
|
20
|
+
};
|
|
21
|
+
};
|