treste 2.4.8 → 2.4.9
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/INSTALL.md +35 -4
- package/README.md +2 -2
- package/dist/ast.d.ts +1 -1
- package/dist/ast.d.ts.map +1 -1
- package/dist/cli.js +178 -0
- package/dist/cli.js.map +1 -1
- package/dist/compiler/exe.d.ts.map +1 -1
- package/dist/compiler/exe.js +130 -18
- package/dist/compiler/exe.js.map +1 -1
- package/dist/compiler/web.d.ts +1 -0
- package/dist/compiler/web.d.ts.map +1 -1
- package/dist/compiler/web.js +31 -0
- package/dist/compiler/web.js.map +1 -1
- package/dist/interpreter.d.ts.map +1 -1
- package/dist/interpreter.js +84 -1
- package/dist/interpreter.js.map +1 -1
- package/dist/lexer.d.ts.map +1 -1
- package/dist/lexer.js +3 -0
- package/dist/lexer.js.map +1 -1
- package/dist/parser.d.ts.map +1 -1
- package/dist/parser.js +43 -13
- package/dist/parser.js.map +1 -1
- package/dist/scripts/create-app.js +408 -0
- package/dist/std/gui.trest +5 -0
- package/dist/std-native.d.ts +57 -9
- package/dist/std-native.d.ts.map +1 -1
- package/dist/std-native.js +710 -35
- package/dist/std-native.js.map +1 -1
- package/package.json +98 -95
- package/src/std/gui.trest +5 -0
package/dist/std-native.js
CHANGED
|
@@ -408,10 +408,73 @@ class StdAsync {
|
|
|
408
408
|
exports.StdAsync = StdAsync;
|
|
409
409
|
/**
|
|
410
410
|
* ========================================
|
|
411
|
-
* GUI Module - Interface Gráfica
|
|
411
|
+
* GUI Module - Interface Gráfica Desktop com Electron
|
|
412
412
|
* ========================================
|
|
413
413
|
*/
|
|
414
|
+
// Variáveis globais para Electron
|
|
415
|
+
let electronModule = null;
|
|
416
|
+
let BrowserWindow = null;
|
|
417
|
+
let app = null;
|
|
418
|
+
function loadElectron() {
|
|
419
|
+
// Se já carregado e disponível, retornar true
|
|
420
|
+
if (electronModule && app && BrowserWindow) {
|
|
421
|
+
// Verificar se app.isReady() ou se está disponível
|
|
422
|
+
if (app && typeof app.isReady === 'function') {
|
|
423
|
+
return true;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
try {
|
|
427
|
+
// Tentar carregar Electron
|
|
428
|
+
electronModule = require('electron');
|
|
429
|
+
if (electronModule) {
|
|
430
|
+
// Tentar acessar app e BrowserWindow
|
|
431
|
+
// Quando executado através do Node.js (não Electron main process),
|
|
432
|
+
// app e BrowserWindow podem não estar disponíveis diretamente
|
|
433
|
+
if (electronModule.app) {
|
|
434
|
+
app = electronModule.app;
|
|
435
|
+
}
|
|
436
|
+
if (electronModule.BrowserWindow) {
|
|
437
|
+
BrowserWindow = electronModule.BrowserWindow;
|
|
438
|
+
}
|
|
439
|
+
// Se temos Electron instalado mas app/BrowserWindow não estão disponíveis,
|
|
440
|
+
// ainda podemos tentar usá-los (pode funcionar em contexto Electron)
|
|
441
|
+
if (electronModule && electronModule.app && electronModule.BrowserWindow) {
|
|
442
|
+
app = electronModule.app;
|
|
443
|
+
BrowserWindow = electronModule.BrowserWindow;
|
|
444
|
+
return true;
|
|
445
|
+
}
|
|
446
|
+
// Se app ou BrowserWindow não estão disponíveis, retornar false
|
|
447
|
+
// Isso significa que não estamos no contexto Electron main process
|
|
448
|
+
return false;
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
catch (e) {
|
|
452
|
+
// Electron não instalado
|
|
453
|
+
return false;
|
|
454
|
+
}
|
|
455
|
+
return false;
|
|
456
|
+
}
|
|
414
457
|
class StdGUI {
|
|
458
|
+
/**
|
|
459
|
+
* Initialize Electron app
|
|
460
|
+
*/
|
|
461
|
+
static ensureAppReady() {
|
|
462
|
+
if (!loadElectron()) {
|
|
463
|
+
console.error('❌ Electron não está instalado. Para usar GUI desktop, instale: npm install electron');
|
|
464
|
+
return Promise.resolve(false);
|
|
465
|
+
}
|
|
466
|
+
if (this.appReady) {
|
|
467
|
+
return Promise.resolve(true);
|
|
468
|
+
}
|
|
469
|
+
if (app.isReady()) {
|
|
470
|
+
this.appReady = true;
|
|
471
|
+
return Promise.resolve(true);
|
|
472
|
+
}
|
|
473
|
+
return app.whenReady().then(() => {
|
|
474
|
+
this.appReady = true;
|
|
475
|
+
return true;
|
|
476
|
+
});
|
|
477
|
+
}
|
|
415
478
|
/**
|
|
416
479
|
* Create Terminal
|
|
417
480
|
*/
|
|
@@ -421,7 +484,6 @@ class StdGUI {
|
|
|
421
484
|
console.clear();
|
|
422
485
|
},
|
|
423
486
|
printAt: (x, y, text) => {
|
|
424
|
-
// Basic terminal positioning
|
|
425
487
|
process.stdout.write(`\x1b[${y};${x}H${text}`);
|
|
426
488
|
},
|
|
427
489
|
getHeight: () => process.stdout.rows || 24,
|
|
@@ -429,51 +491,254 @@ class StdGUI {
|
|
|
429
491
|
};
|
|
430
492
|
}
|
|
431
493
|
/**
|
|
432
|
-
* Create Window
|
|
494
|
+
* Create Desktop Window with Electron
|
|
433
495
|
*/
|
|
434
496
|
static createWindow(options) {
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
497
|
+
const title = options.title || options.título || 'Janela Trest';
|
|
498
|
+
const width = options.width || options.largura || 800;
|
|
499
|
+
const height = options.height || options.altura || 600;
|
|
500
|
+
const resizable = options.resizable !== false;
|
|
501
|
+
const center = options.center !== false;
|
|
502
|
+
const minWidth = options.minWidth || options.larguraMinima || undefined;
|
|
503
|
+
const minHeight = options.minHeight || options.alturaMinima || undefined;
|
|
504
|
+
const maxWidth = options.maxWidth || options.larguraMaxima || undefined;
|
|
505
|
+
const maxHeight = options.maxHeight || options.alturaMaxima || undefined;
|
|
506
|
+
const fullscreen = options.fullscreen || options.telaCheia || false;
|
|
507
|
+
const alwaysOnTop = options.alwaysOnTop || options.sempreNoTopo || false;
|
|
508
|
+
const winId = ++this.windowCounter;
|
|
509
|
+
let browserWindow = null;
|
|
510
|
+
// Check if Electron is available
|
|
511
|
+
if (!loadElectron()) {
|
|
512
|
+
return this.createFallbackWindow(options);
|
|
513
|
+
}
|
|
514
|
+
// Create window wrapper object immediately (window will be created when app is ready)
|
|
515
|
+
const windowWrapper = {
|
|
516
|
+
id: winId,
|
|
517
|
+
_pending: true,
|
|
518
|
+
_options: options,
|
|
519
|
+
show: () => {
|
|
520
|
+
if (browserWindow) {
|
|
521
|
+
browserWindow.show();
|
|
522
|
+
}
|
|
523
|
+
else if (windowWrapper._pending) {
|
|
524
|
+
windowWrapper._pendingShow = true;
|
|
525
|
+
}
|
|
526
|
+
},
|
|
527
|
+
hide: () => {
|
|
528
|
+
if (browserWindow) {
|
|
529
|
+
browserWindow.hide();
|
|
530
|
+
}
|
|
531
|
+
},
|
|
532
|
+
close: () => {
|
|
533
|
+
if (browserWindow) {
|
|
534
|
+
browserWindow.close();
|
|
535
|
+
this.windows.delete(winId);
|
|
536
|
+
}
|
|
537
|
+
},
|
|
538
|
+
loadHTML: (html) => {
|
|
539
|
+
if (browserWindow) {
|
|
540
|
+
browserWindow.loadURL(`data:text/html;charset=utf-8,${encodeURIComponent(html)}`);
|
|
541
|
+
}
|
|
542
|
+
else {
|
|
543
|
+
windowWrapper._pendingHTML = html;
|
|
544
|
+
}
|
|
545
|
+
},
|
|
546
|
+
loadFile: (filePath) => {
|
|
547
|
+
if (browserWindow) {
|
|
548
|
+
browserWindow.loadFile(filePath);
|
|
549
|
+
}
|
|
550
|
+
else {
|
|
551
|
+
windowWrapper._pendingFile = filePath;
|
|
552
|
+
}
|
|
553
|
+
},
|
|
554
|
+
loadURL: (url) => {
|
|
555
|
+
if (browserWindow) {
|
|
556
|
+
browserWindow.loadURL(url);
|
|
557
|
+
}
|
|
558
|
+
else {
|
|
559
|
+
windowWrapper._pendingURL = url;
|
|
560
|
+
}
|
|
561
|
+
},
|
|
562
|
+
setTitle: (newTitle) => {
|
|
563
|
+
if (browserWindow) {
|
|
564
|
+
browserWindow.setTitle(newTitle);
|
|
565
|
+
}
|
|
566
|
+
else {
|
|
567
|
+
windowWrapper._pendingTitle = newTitle;
|
|
568
|
+
}
|
|
569
|
+
},
|
|
570
|
+
setSize: (w, h) => {
|
|
571
|
+
if (browserWindow) {
|
|
572
|
+
browserWindow.setSize(w, h);
|
|
573
|
+
}
|
|
574
|
+
},
|
|
575
|
+
setMinimumSize: (w, h) => {
|
|
576
|
+
if (browserWindow) {
|
|
577
|
+
browserWindow.setMinimumSize(w, h);
|
|
578
|
+
}
|
|
579
|
+
},
|
|
580
|
+
setMaximumSize: (w, h) => {
|
|
581
|
+
if (browserWindow) {
|
|
582
|
+
browserWindow.setMaximumSize(w, h);
|
|
583
|
+
}
|
|
584
|
+
},
|
|
585
|
+
minimize: () => {
|
|
586
|
+
if (browserWindow)
|
|
587
|
+
browserWindow.minimize();
|
|
588
|
+
},
|
|
589
|
+
maximize: () => {
|
|
590
|
+
if (browserWindow)
|
|
591
|
+
browserWindow.maximize();
|
|
592
|
+
},
|
|
593
|
+
restore: () => {
|
|
594
|
+
if (browserWindow)
|
|
595
|
+
browserWindow.restore();
|
|
442
596
|
},
|
|
597
|
+
focus: () => {
|
|
598
|
+
if (browserWindow)
|
|
599
|
+
browserWindow.focus();
|
|
600
|
+
},
|
|
601
|
+
on: (event, callback) => {
|
|
602
|
+
if (browserWindow) {
|
|
603
|
+
browserWindow.on(event, callback);
|
|
604
|
+
}
|
|
605
|
+
},
|
|
606
|
+
webContents: null,
|
|
607
|
+
};
|
|
608
|
+
// Initialize app and create window in background
|
|
609
|
+
this.ensureAppReady().then((isReady) => {
|
|
610
|
+
if (!isReady) {
|
|
611
|
+
return;
|
|
612
|
+
}
|
|
613
|
+
try {
|
|
614
|
+
browserWindow = new BrowserWindow({
|
|
615
|
+
width: width,
|
|
616
|
+
height: height,
|
|
617
|
+
title: title,
|
|
618
|
+
resizable: resizable,
|
|
619
|
+
center: center,
|
|
620
|
+
minWidth: minWidth,
|
|
621
|
+
minHeight: minHeight,
|
|
622
|
+
maxWidth: maxWidth,
|
|
623
|
+
maxHeight: maxHeight,
|
|
624
|
+
fullscreen: fullscreen,
|
|
625
|
+
alwaysOnTop: alwaysOnTop,
|
|
626
|
+
webPreferences: {
|
|
627
|
+
nodeIntegration: true,
|
|
628
|
+
contextIsolation: false,
|
|
629
|
+
webSecurity: false,
|
|
630
|
+
},
|
|
631
|
+
show: false,
|
|
632
|
+
});
|
|
633
|
+
this.windows.set(winId, browserWindow);
|
|
634
|
+
windowWrapper._pending = false;
|
|
635
|
+
windowWrapper.webContents = browserWindow.webContents;
|
|
636
|
+
browserWindow.on('closed', () => {
|
|
637
|
+
this.windows.delete(winId);
|
|
638
|
+
browserWindow = null;
|
|
639
|
+
});
|
|
640
|
+
// Apply pending operations
|
|
641
|
+
if (windowWrapper._pendingHTML) {
|
|
642
|
+
browserWindow.loadURL(`data:text/html;charset=utf-8,${encodeURIComponent(windowWrapper._pendingHTML)}`);
|
|
643
|
+
}
|
|
644
|
+
else if (windowWrapper._pendingFile) {
|
|
645
|
+
browserWindow.loadFile(windowWrapper._pendingFile);
|
|
646
|
+
}
|
|
647
|
+
else if (windowWrapper._pendingURL) {
|
|
648
|
+
browserWindow.loadURL(windowWrapper._pendingURL);
|
|
649
|
+
}
|
|
650
|
+
if (windowWrapper._pendingTitle) {
|
|
651
|
+
browserWindow.setTitle(windowWrapper._pendingTitle);
|
|
652
|
+
}
|
|
653
|
+
if (windowWrapper._pendingShow) {
|
|
654
|
+
browserWindow.show();
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
catch (error) {
|
|
658
|
+
console.error('❌ Erro ao criar janela Electron:', error.message);
|
|
659
|
+
}
|
|
660
|
+
});
|
|
661
|
+
return windowWrapper;
|
|
662
|
+
}
|
|
663
|
+
/**
|
|
664
|
+
* Fallback window (quando Electron não está disponível)
|
|
665
|
+
*/
|
|
666
|
+
static createFallbackWindow(options) {
|
|
667
|
+
console.warn('⚠️ Electron não disponível - usando modo fallback');
|
|
668
|
+
return {
|
|
669
|
+
id: ++this.windowCounter,
|
|
670
|
+
show: () => console.log('Window shown (fallback mode)'),
|
|
671
|
+
hide: () => console.log('Window hidden (fallback mode)'),
|
|
672
|
+
close: () => console.log('Window closed (fallback mode)'),
|
|
673
|
+
loadHTML: (html) => console.log('HTML loaded (fallback mode)'),
|
|
674
|
+
loadFile: (filePath) => console.log(`File loaded: ${filePath} (fallback mode)`),
|
|
675
|
+
loadURL: (url) => console.log(`URL loaded: ${url} (fallback mode)`),
|
|
676
|
+
setTitle: (title) => console.log(`Title set: ${title} (fallback mode)`),
|
|
677
|
+
setSize: () => { },
|
|
678
|
+
setMinimumSize: () => { },
|
|
679
|
+
setMaximumSize: () => { },
|
|
680
|
+
minimize: () => { },
|
|
681
|
+
maximize: () => { },
|
|
682
|
+
restore: () => { },
|
|
683
|
+
focus: () => { },
|
|
684
|
+
on: () => { },
|
|
685
|
+
webContents: null,
|
|
443
686
|
};
|
|
444
687
|
}
|
|
445
688
|
/**
|
|
446
|
-
* Create Button
|
|
689
|
+
* Create Button (HTML-based)
|
|
447
690
|
*/
|
|
448
691
|
static createButton(text, onClick) {
|
|
449
692
|
return {
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
693
|
+
type: 'button',
|
|
694
|
+
text: text,
|
|
695
|
+
onClick: onClick,
|
|
696
|
+
disabled: false,
|
|
697
|
+
disable: function () {
|
|
698
|
+
this.disabled = true;
|
|
699
|
+
},
|
|
700
|
+
enable: function () {
|
|
701
|
+
this.disabled = false;
|
|
702
|
+
},
|
|
703
|
+
setText: function (newText) {
|
|
704
|
+
this.text = newText;
|
|
705
|
+
},
|
|
454
706
|
};
|
|
455
707
|
}
|
|
456
708
|
/**
|
|
457
|
-
* Create Text Input
|
|
709
|
+
* Create Text Input (HTML-based)
|
|
458
710
|
*/
|
|
459
711
|
static createText(placeholder, onChange) {
|
|
460
712
|
return {
|
|
713
|
+
type: 'text',
|
|
714
|
+
placeholder: placeholder,
|
|
461
715
|
value: '',
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
716
|
+
onChange: onChange,
|
|
717
|
+
setValue: function (newValue) {
|
|
718
|
+
this.value = newValue;
|
|
719
|
+
},
|
|
720
|
+
focus: function () { },
|
|
721
|
+
blur: function () { },
|
|
465
722
|
};
|
|
466
723
|
}
|
|
467
724
|
/**
|
|
468
|
-
* Create List
|
|
725
|
+
* Create List (HTML-based)
|
|
469
726
|
*/
|
|
470
727
|
static createList(data, onSelect) {
|
|
471
728
|
return {
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
729
|
+
type: 'list',
|
|
730
|
+
data: data || [],
|
|
731
|
+
onSelect: onSelect,
|
|
732
|
+
update: function (newData) {
|
|
733
|
+
this.data = newData || [];
|
|
734
|
+
},
|
|
735
|
+
add: function (item) {
|
|
736
|
+
this.data.push(item);
|
|
737
|
+
},
|
|
738
|
+
remove: function (index) {
|
|
739
|
+
if (index >= 0 && index < this.data.length) {
|
|
740
|
+
this.data.splice(index, 1);
|
|
741
|
+
}
|
|
477
742
|
},
|
|
478
743
|
};
|
|
479
744
|
}
|
|
@@ -482,14 +747,18 @@ class StdGUI {
|
|
|
482
747
|
*/
|
|
483
748
|
static componentContainer(children) {
|
|
484
749
|
return {
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
750
|
+
type: 'container',
|
|
751
|
+
children: children || [],
|
|
752
|
+
add: function (component) {
|
|
753
|
+
this.children.push(component);
|
|
488
754
|
},
|
|
489
|
-
remove: (component)
|
|
490
|
-
const index = children.indexOf(component);
|
|
755
|
+
remove: function (component) {
|
|
756
|
+
const index = this.children.indexOf(component);
|
|
491
757
|
if (index > -1)
|
|
492
|
-
children.splice(index, 1);
|
|
758
|
+
this.children.splice(index, 1);
|
|
759
|
+
},
|
|
760
|
+
clear: function () {
|
|
761
|
+
this.children = [];
|
|
493
762
|
},
|
|
494
763
|
};
|
|
495
764
|
}
|
|
@@ -497,20 +766,426 @@ class StdGUI {
|
|
|
497
766
|
* Component Label
|
|
498
767
|
*/
|
|
499
768
|
static componentLabel(text) {
|
|
500
|
-
return {
|
|
769
|
+
return {
|
|
770
|
+
type: 'label',
|
|
771
|
+
text: text,
|
|
772
|
+
setText: function (newText) {
|
|
773
|
+
this.text = newText;
|
|
774
|
+
},
|
|
775
|
+
};
|
|
501
776
|
}
|
|
502
777
|
/**
|
|
503
778
|
* Component Image
|
|
504
779
|
*/
|
|
505
780
|
static componentImage(src) {
|
|
506
781
|
return {
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
782
|
+
type: 'image',
|
|
783
|
+
src: src,
|
|
784
|
+
visible: true,
|
|
785
|
+
show: function () {
|
|
786
|
+
this.visible = true;
|
|
787
|
+
},
|
|
788
|
+
hide: function () {
|
|
789
|
+
this.visible = false;
|
|
790
|
+
},
|
|
791
|
+
setSrc: function (newSrc) {
|
|
792
|
+
this.src = newSrc;
|
|
793
|
+
},
|
|
794
|
+
};
|
|
795
|
+
}
|
|
796
|
+
/**
|
|
797
|
+
* Helper: Render components to HTML
|
|
798
|
+
*/
|
|
799
|
+
static renderComponentToHTML(component) {
|
|
800
|
+
if (!component)
|
|
801
|
+
return '';
|
|
802
|
+
switch (component.type) {
|
|
803
|
+
case 'button':
|
|
804
|
+
return `<button onclick="window.trestGuiClickHandler(${JSON.stringify(component.text)})" ${component.disabled ? 'disabled' : ''}>${component.text}</button>`;
|
|
805
|
+
case 'text':
|
|
806
|
+
return `<input type="text" placeholder="${component.placeholder || ''}" value="${component.value || ''}" onchange="window.trestGuiChangeHandler(this.value)" />`;
|
|
807
|
+
case 'label':
|
|
808
|
+
return `<label>${component.text || ''}</label>`;
|
|
809
|
+
case 'image':
|
|
810
|
+
if (!component.visible)
|
|
811
|
+
return '';
|
|
812
|
+
return `<img src="${component.src || ''}" alt="" />`;
|
|
813
|
+
case 'list':
|
|
814
|
+
const items = (component.data || []).map((item, index) => `<li onclick="window.trestGuiSelectHandler(${index})">${item}</li>`).join('');
|
|
815
|
+
return `<ul>${items}</ul>`;
|
|
816
|
+
case 'container':
|
|
817
|
+
const childrenHTML = (component.children || []).map((child) => this.renderComponentToHTML(child)).join('');
|
|
818
|
+
return `<div>${childrenHTML}</div>`;
|
|
819
|
+
default:
|
|
820
|
+
return '';
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
/**
|
|
824
|
+
* Keep app running (prevents Electron from closing)
|
|
825
|
+
*/
|
|
826
|
+
static keepRunning() {
|
|
827
|
+
if (!loadElectron() || !app) {
|
|
828
|
+
console.warn('⚠️ Electron não disponível para manterRodando()');
|
|
829
|
+
return;
|
|
830
|
+
}
|
|
831
|
+
app.on('window-all-closed', () => {
|
|
832
|
+
// On macOS, apps typically stay active
|
|
833
|
+
if (process.platform !== 'darwin') {
|
|
834
|
+
// Keep running - don't quit automatically
|
|
835
|
+
}
|
|
836
|
+
});
|
|
837
|
+
}
|
|
838
|
+
/**
|
|
839
|
+
* Create Widget (base class for all GUI components)
|
|
840
|
+
* Allows creating GUI programmatically without HTML
|
|
841
|
+
*/
|
|
842
|
+
static Widget(type, props = {}) {
|
|
843
|
+
return {
|
|
844
|
+
type: type,
|
|
845
|
+
props: props || {},
|
|
846
|
+
children: [],
|
|
847
|
+
id: `widget_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
|
|
848
|
+
setText: function (text) {
|
|
849
|
+
this.props.text = text;
|
|
850
|
+
this.update();
|
|
851
|
+
},
|
|
852
|
+
setVisible: function (visible) {
|
|
853
|
+
this.props.visible = visible !== false;
|
|
854
|
+
this.update();
|
|
855
|
+
},
|
|
856
|
+
setEnabled: function (enabled) {
|
|
857
|
+
this.props.enabled = enabled !== false;
|
|
858
|
+
this.update();
|
|
859
|
+
},
|
|
860
|
+
addChild: function (child) {
|
|
861
|
+
this.children.push(child);
|
|
862
|
+
this.update();
|
|
863
|
+
},
|
|
864
|
+
removeChild: function (child) {
|
|
865
|
+
const index = this.children.indexOf(child);
|
|
866
|
+
if (index > -1) {
|
|
867
|
+
this.children.splice(index, 1);
|
|
868
|
+
this.update();
|
|
869
|
+
}
|
|
870
|
+
},
|
|
871
|
+
update: function () {
|
|
872
|
+
// Update será implementado quando widget for adicionado a uma janela
|
|
873
|
+
if (this._parentWindow) {
|
|
874
|
+
this._parentWindow._updateGUI();
|
|
875
|
+
}
|
|
876
|
+
},
|
|
877
|
+
toHTML: function () {
|
|
878
|
+
return this._renderToHTML();
|
|
879
|
+
},
|
|
880
|
+
_renderToHTML: function () {
|
|
881
|
+
// Implementação base - será sobrescrita por widgets específicos
|
|
882
|
+
return '';
|
|
883
|
+
}
|
|
884
|
+
};
|
|
885
|
+
}
|
|
886
|
+
/**
|
|
887
|
+
* Create Button Widget (programmatic)
|
|
888
|
+
*/
|
|
889
|
+
static Button(text, onClick) {
|
|
890
|
+
const widget = this.Widget('button', {
|
|
891
|
+
text: text || '',
|
|
892
|
+
onClick: onClick,
|
|
893
|
+
enabled: true,
|
|
894
|
+
visible: true,
|
|
895
|
+
style: {}
|
|
896
|
+
});
|
|
897
|
+
widget._renderToHTML = function () {
|
|
898
|
+
const style = this.props.style || {};
|
|
899
|
+
const styleStr = Object.keys(style).map(k => `${k}:${style[k]}`).join(';');
|
|
900
|
+
const disabled = this.props.enabled === false ? 'disabled' : '';
|
|
901
|
+
const onclick = this.props.onClick ? `onclick="window.trestGuiButtonClick('${this.id}')"` : '';
|
|
902
|
+
return `<button id="${this.id}" ${onclick} ${disabled} style="${styleStr}">${this.props.text || ''}</button>`;
|
|
903
|
+
};
|
|
904
|
+
return widget;
|
|
905
|
+
}
|
|
906
|
+
/**
|
|
907
|
+
* Create Label Widget (programmatic)
|
|
908
|
+
*/
|
|
909
|
+
static Label(text) {
|
|
910
|
+
const widget = this.Widget('label', {
|
|
911
|
+
text: text || '',
|
|
912
|
+
visible: true,
|
|
913
|
+
style: {}
|
|
914
|
+
});
|
|
915
|
+
widget._renderToHTML = function () {
|
|
916
|
+
const style = this.props.style || {};
|
|
917
|
+
const styleStr = Object.keys(style).map(k => `${k}:${style[k]}`).join(';');
|
|
918
|
+
return `<label id="${this.id}" style="${styleStr}">${this.props.text || ''}</label>`;
|
|
919
|
+
};
|
|
920
|
+
return widget;
|
|
921
|
+
}
|
|
922
|
+
/**
|
|
923
|
+
* Create Input Widget (programmatic)
|
|
924
|
+
*/
|
|
925
|
+
static Input(placeholder = '', onChange) {
|
|
926
|
+
const widget = this.Widget('input', {
|
|
927
|
+
placeholder: placeholder || '',
|
|
928
|
+
value: '',
|
|
929
|
+
onChange: onChange,
|
|
930
|
+
enabled: true,
|
|
931
|
+
visible: true,
|
|
932
|
+
type: 'text',
|
|
933
|
+
style: {}
|
|
934
|
+
});
|
|
935
|
+
widget.setValue = function (value) {
|
|
936
|
+
this.props.value = value;
|
|
937
|
+
this.update();
|
|
938
|
+
};
|
|
939
|
+
widget.getValue = function () {
|
|
940
|
+
return this.props.value || '';
|
|
941
|
+
};
|
|
942
|
+
widget._renderToHTML = function () {
|
|
943
|
+
const style = this.props.style || {};
|
|
944
|
+
const styleStr = Object.keys(style).map(k => `${k}:${style[k]}`).join(';');
|
|
945
|
+
const disabled = this.props.enabled === false ? 'disabled' : '';
|
|
946
|
+
const onchange = this.props.onChange ? `onchange="window.trestGuiInputChange('${this.id}', this.value)"` : '';
|
|
947
|
+
return `<input id="${this.id}" type="${this.props.type || 'text'}" placeholder="${this.props.placeholder || ''}" value="${this.props.value || ''}" ${onchange} ${disabled} style="${styleStr}">`;
|
|
948
|
+
};
|
|
949
|
+
return widget;
|
|
950
|
+
}
|
|
951
|
+
/**
|
|
952
|
+
* Create VBox Layout (vertical box - like PySide6)
|
|
953
|
+
*/
|
|
954
|
+
static VBox(children = [], spacing = 0) {
|
|
955
|
+
const widget = this.Widget('vbox', {
|
|
956
|
+
spacing: spacing || 0,
|
|
957
|
+
alignment: 'top'
|
|
958
|
+
});
|
|
959
|
+
widget.children = children || [];
|
|
960
|
+
widget._renderToHTML = function () {
|
|
961
|
+
const spacing = this.props.spacing || 0;
|
|
962
|
+
const style = `display: flex; flex-direction: column; gap: ${spacing}px;`;
|
|
963
|
+
const childrenHTML = this.children.map((child) => {
|
|
964
|
+
if (child && typeof child.toHTML === 'function') {
|
|
965
|
+
return child.toHTML();
|
|
966
|
+
}
|
|
967
|
+
return typeof child === 'string' ? child : '';
|
|
968
|
+
}).join('');
|
|
969
|
+
return `<div id="${this.id}" style="${style}">${childrenHTML}</div>`;
|
|
970
|
+
};
|
|
971
|
+
return widget;
|
|
972
|
+
}
|
|
973
|
+
/**
|
|
974
|
+
* Create HBox Layout (horizontal box - like PySide6)
|
|
975
|
+
*/
|
|
976
|
+
static HBox(children = [], spacing = 0) {
|
|
977
|
+
const widget = this.Widget('hbox', {
|
|
978
|
+
spacing: spacing || 0,
|
|
979
|
+
alignment: 'left'
|
|
980
|
+
});
|
|
981
|
+
widget.children = children || [];
|
|
982
|
+
widget._renderToHTML = function () {
|
|
983
|
+
const spacing = this.props.spacing || 0;
|
|
984
|
+
const style = `display: flex; flex-direction: row; gap: ${spacing}px;`;
|
|
985
|
+
const childrenHTML = this.children.map((child) => {
|
|
986
|
+
if (child && typeof child.toHTML === 'function') {
|
|
987
|
+
return child.toHTML();
|
|
988
|
+
}
|
|
989
|
+
return typeof child === 'string' ? child : '';
|
|
990
|
+
}).join('');
|
|
991
|
+
return `<div id="${this.id}" style="${style}">${childrenHTML}</div>`;
|
|
992
|
+
};
|
|
993
|
+
return widget;
|
|
994
|
+
}
|
|
995
|
+
/**
|
|
996
|
+
* Create Grid Layout (like PySide6)
|
|
997
|
+
*/
|
|
998
|
+
static Grid(rows = 1, cols = 1, spacing = 0) {
|
|
999
|
+
const widget = this.Widget('grid', {
|
|
1000
|
+
rows: rows || 1,
|
|
1001
|
+
cols: cols || 1,
|
|
1002
|
+
spacing: spacing || 0
|
|
1003
|
+
});
|
|
1004
|
+
widget.children = [];
|
|
1005
|
+
widget._grid = Array(rows).fill(null).map(() => Array(cols).fill(null));
|
|
1006
|
+
widget.addWidget = function (child, row, col, rowSpan = 1, colSpan = 1) {
|
|
1007
|
+
if (row >= 0 && row < this.props.rows && col >= 0 && col < this.props.cols) {
|
|
1008
|
+
this.children.push({ widget: child, row, col, rowSpan, colSpan });
|
|
1009
|
+
this._grid[row][col] = child;
|
|
1010
|
+
this.update();
|
|
1011
|
+
}
|
|
1012
|
+
};
|
|
1013
|
+
widget._renderToHTML = function () {
|
|
1014
|
+
const spacing = this.props.spacing || 0;
|
|
1015
|
+
const style = `display: grid; grid-template-rows: repeat(${this.props.rows}, 1fr); grid-template-columns: repeat(${this.props.cols}, 1fr); gap: ${spacing}px;`;
|
|
1016
|
+
// Renderizar widgets nos lugares corretos do grid
|
|
1017
|
+
const gridItems = Array(this.props.rows * this.props.cols).fill('<div></div>');
|
|
1018
|
+
this.children.forEach((item) => {
|
|
1019
|
+
const index = item.row * this.props.cols + item.col;
|
|
1020
|
+
const rowSpan = item.rowSpan || 1;
|
|
1021
|
+
const colSpan = item.colSpan || 1;
|
|
1022
|
+
const gridArea = `${item.row + 1} / ${item.col + 1} / ${item.row + rowSpan + 1} / ${item.col + colSpan + 1}`;
|
|
1023
|
+
const childHTML = item.widget && typeof item.widget.toHTML === 'function'
|
|
1024
|
+
? item.widget.toHTML()
|
|
1025
|
+
: '';
|
|
1026
|
+
gridItems[index] = `<div style="grid-area: ${gridArea}">${childHTML}</div>`;
|
|
1027
|
+
});
|
|
1028
|
+
return `<div id="${this.id}" style="${style}">${gridItems.join('')}</div>`;
|
|
1029
|
+
};
|
|
1030
|
+
return widget;
|
|
1031
|
+
}
|
|
1032
|
+
/**
|
|
1033
|
+
* Create Programmatic Window (without HTML dependency)
|
|
1034
|
+
* This allows creating windows and adding widgets programmatically
|
|
1035
|
+
*/
|
|
1036
|
+
static createProgrammaticWindow(options) {
|
|
1037
|
+
const title = options.title || options.título || 'Janela Trest';
|
|
1038
|
+
const width = options.width || options.largura || 800;
|
|
1039
|
+
const height = options.height || options.altura || 600;
|
|
1040
|
+
// Create window normally
|
|
1041
|
+
const window = this.createWindow(options);
|
|
1042
|
+
// Add programmatic methods
|
|
1043
|
+
window._widgets = [];
|
|
1044
|
+
window._layout = null;
|
|
1045
|
+
window._useProgrammatic = true;
|
|
1046
|
+
window.setLayout = function (layout) {
|
|
1047
|
+
this._layout = layout;
|
|
1048
|
+
if (layout && layout._parentWindow) {
|
|
1049
|
+
layout._parentWindow = this;
|
|
1050
|
+
}
|
|
1051
|
+
this._updateGUI();
|
|
1052
|
+
};
|
|
1053
|
+
window.addWidget = function (widget) {
|
|
1054
|
+
this._widgets.push(widget);
|
|
1055
|
+
if (widget && widget._parentWindow) {
|
|
1056
|
+
widget._parentWindow = this;
|
|
1057
|
+
}
|
|
1058
|
+
this._updateGUI();
|
|
1059
|
+
};
|
|
1060
|
+
window.removeWidget = function (widget) {
|
|
1061
|
+
const index = this._widgets.indexOf(widget);
|
|
1062
|
+
if (index > -1) {
|
|
1063
|
+
this._widgets.splice(index, 1);
|
|
1064
|
+
this._updateGUI();
|
|
1065
|
+
}
|
|
1066
|
+
};
|
|
1067
|
+
window.clear = function () {
|
|
1068
|
+
this._widgets = [];
|
|
1069
|
+
this._layout = null;
|
|
1070
|
+
this._updateGUI();
|
|
1071
|
+
};
|
|
1072
|
+
window._updateGUI = function () {
|
|
1073
|
+
// Gerar HTML automaticamente dos widgets
|
|
1074
|
+
let contentHTML = '';
|
|
1075
|
+
if (this._layout) {
|
|
1076
|
+
// Se há um layout, renderizar o layout
|
|
1077
|
+
if (this._layout.toHTML) {
|
|
1078
|
+
contentHTML = this._layout.toHTML();
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
else {
|
|
1082
|
+
// Senão, renderizar widgets diretamente
|
|
1083
|
+
contentHTML = this._widgets.map((w) => {
|
|
1084
|
+
if (w && typeof w.toHTML === 'function') {
|
|
1085
|
+
return w.toHTML();
|
|
1086
|
+
}
|
|
1087
|
+
return '';
|
|
1088
|
+
}).join('');
|
|
1089
|
+
}
|
|
1090
|
+
// Criar HTML completo
|
|
1091
|
+
const html = `<!DOCTYPE html>
|
|
1092
|
+
<html lang="pt-BR">
|
|
1093
|
+
<head>
|
|
1094
|
+
<meta charset="UTF-8">
|
|
1095
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
1096
|
+
<title>${title}</title>
|
|
1097
|
+
<style>
|
|
1098
|
+
* {
|
|
1099
|
+
margin: 0;
|
|
1100
|
+
padding: 0;
|
|
1101
|
+
box-sizing: border-box;
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
body {
|
|
1105
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
|
1106
|
+
padding: 20px;
|
|
1107
|
+
background: #f5f5f5;
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
.container {
|
|
1111
|
+
background: white;
|
|
1112
|
+
border-radius: 8px;
|
|
1113
|
+
padding: 20px;
|
|
1114
|
+
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
|
1115
|
+
min-height: calc(100vh - 40px);
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
button {
|
|
1119
|
+
padding: 10px 20px;
|
|
1120
|
+
border: none;
|
|
1121
|
+
border-radius: 4px;
|
|
1122
|
+
background: #007bff;
|
|
1123
|
+
color: white;
|
|
1124
|
+
cursor: pointer;
|
|
1125
|
+
font-size: 14px;
|
|
1126
|
+
transition: background 0.3s;
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
button:hover:not(:disabled) {
|
|
1130
|
+
background: #0056b3;
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
button:disabled {
|
|
1134
|
+
background: #ccc;
|
|
1135
|
+
cursor: not-allowed;
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
input {
|
|
1139
|
+
padding: 10px;
|
|
1140
|
+
border: 1px solid #ddd;
|
|
1141
|
+
border-radius: 4px;
|
|
1142
|
+
font-size: 14px;
|
|
1143
|
+
width: 100%;
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
input:focus {
|
|
1147
|
+
outline: none;
|
|
1148
|
+
border-color: #007bff;
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
label {
|
|
1152
|
+
display: block;
|
|
1153
|
+
margin-bottom: 5px;
|
|
1154
|
+
font-weight: 500;
|
|
1155
|
+
color: #333;
|
|
1156
|
+
}
|
|
1157
|
+
</style>
|
|
1158
|
+
</head>
|
|
1159
|
+
<body>
|
|
1160
|
+
<div class="container">
|
|
1161
|
+
${contentHTML}
|
|
1162
|
+
</div>
|
|
1163
|
+
|
|
1164
|
+
<script>
|
|
1165
|
+
// Handlers para widgets programáticos
|
|
1166
|
+
window.trestGuiButtonClick = function(widgetId) {
|
|
1167
|
+
// Callback será implementado quando widget for criado
|
|
1168
|
+
console.log('Button clicked:', widgetId);
|
|
1169
|
+
};
|
|
1170
|
+
|
|
1171
|
+
window.trestGuiInputChange = function(widgetId, value) {
|
|
1172
|
+
console.log('Input changed:', widgetId, value);
|
|
1173
|
+
};
|
|
1174
|
+
</script>
|
|
1175
|
+
</body>
|
|
1176
|
+
</html>`;
|
|
1177
|
+
// Atualizar janela com HTML gerado
|
|
1178
|
+
if (this.loadHTML) {
|
|
1179
|
+
this.loadHTML(html);
|
|
1180
|
+
}
|
|
510
1181
|
};
|
|
1182
|
+
return window;
|
|
511
1183
|
}
|
|
512
1184
|
}
|
|
513
1185
|
exports.StdGUI = StdGUI;
|
|
1186
|
+
StdGUI.windows = new Map();
|
|
1187
|
+
StdGUI.windowCounter = 0;
|
|
1188
|
+
StdGUI.appReady = false;
|
|
514
1189
|
/**
|
|
515
1190
|
* ========================================
|
|
516
1191
|
* Database Module - SQLite
|