sdl3/examples/windows_app_example.php
2025-11-09 20:24:05 +01:00

222 lines
7.7 KiB
PHP

<?php
require_once __DIR__ . '/../vendor/autoload.php';
use PHPNative\Framework\Application;
use PHPNative\Framework\IconFontRegistry;
use PHPNative\Tailwind\Data\Icon as IconName;
use PHPNative\Ui\Widget\Button;
use PHPNative\Ui\Widget\Container;
use PHPNative\Ui\Widget\Icon;
use PHPNative\Ui\Widget\Label;
use PHPNative\Ui\Widget\Menu;
use PHPNative\Ui\Widget\MenuBar;
use PHPNative\Ui\Widget\Modal;
use PHPNative\Ui\Widget\StatusBar;
use PHPNative\Ui\Widget\TabContainer;
use PHPNative\Ui\Widget\Table;
use PHPNative\Ui\Widget\TextInput;
use PHPNative\Ui\Window;
$iconFontCandidates = [
__DIR__ . '/../assets/fonts/fa-solid-900.ttf',
__DIR__ . '/../assets/fonts/fontawesome/fa7_freesolid_900.otf',
'/usr/share/fonts/truetype/fontawesome-webfont.ttf',
'/usr/share/fonts/truetype/fontawesome/fa-solid-900.ttf',
'/usr/share/fonts/truetype/fa-solid-900.ttf',
];
$iconFontPath = null;
foreach ($iconFontCandidates as $candidate) {
if (is_file($candidate)) {
$iconFontPath = $candidate;
break;
}
}
if ($iconFontPath !== null) {
IconFontRegistry::setDefaultFontPath($iconFontPath);
} else {
echo "Hinweis: FontAwesome Font nicht gefunden. Icons werden ohne Symbol dargestellt.\n";
}
$app = new Application();
$window = new Window('Windows Application Example', 800, 600);
$currentApiKey = '';
/** @var Label|null $statusLabel */
$statusLabel = null;
// Main container (flex-col: menu, content, status)
$mainContainer = new Container('flex flex-col bg-gray-100');
// Modal dialog setup (hidden by default)
$apiKeyInput = new TextInput('API Key', 'w-full border border-gray-300 rounded px-3 py-2 bg-white text-black');
$modalDialog = new Container('bg-white border border-gray-300 rounded p-6 flex flex-col w-96 gap-3 shadow-lg');
$modalDialog->addComponent(new Label('API Einstellungen', 'text-xl font-bold text-black'));
$modalDialog->addComponent(new Label(
'Bitte gib deinen API Key ein, um externe Dienste zu verbinden.',
'text-sm text-gray-700',
));
$fieldContainer = new Container('flex flex-col gap-1');
$fieldContainer->addComponent(new Label('API Key', 'text-sm text-gray-600'));
$fieldContainer->addComponent($apiKeyInput);
$modalDialog->addComponent($fieldContainer);
$buttonRow = new Container('flex flex-row justify-end gap-2');
$cancelButton = new Button('Abbrechen', 'px-4 py-2 bg-gray-200 text-black rounded hover:bg-gray-300');
$saveButton = new Button('Speichern', 'px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 flex items-center');
$saveIcon = new Icon(IconName::save, 18, 'text-white');
$saveButton->setIcon($saveIcon);
$buttonRow->addComponent($cancelButton);
$buttonRow->addComponent($saveButton);
$modalDialog->addComponent($buttonRow);
$modal = new Modal($modalDialog);
$modal->setBackdropAlpha(180);
// === 1. MenuBar ===
$menuBar = new MenuBar();
// File Menu
$fileMenu = new Menu(title: 'Datei');
$fileMenu->addItem('Neu', function () {
echo "Neu clicked\n";
});
$fileMenu->addItem('Öffnen', function () {
echo "Öffnen clicked\n";
});
$fileMenu->addSeparator();
$fileMenu->addItem('Beenden', function () use ($app) {
echo "Beenden clicked\n";
exit(0);
});
// Settings Menu
$settingsMenu = new Menu(title: 'Einstellungen');
$settingsMenu->addItem('Optionen', function () use ($menuBar, $modal, $apiKeyInput, &$currentApiKey) {
$menuBar->closeAllMenus();
$apiKeyInput->setValue($currentApiKey);
$modal->setVisible(true);
});
$settingsMenu->addItem('Sprache', function () {
echo "Sprache clicked\n";
});
$menuBar->addMenu($fileMenu);
$menuBar->addMenu($settingsMenu);
$mainContainer->addComponent($menuBar);
// === 2. Tab Container (flex-1) ===
$tabContainer = new TabContainer('flex-1');
// Tab 1: Table with data
$tab1 = new Container('flex flex-col p-4');
$table = new Table(style: 'bg-lime-200');
$table->setColumns([
['key' => 'id', 'title' => 'ID', 'width' => 80],
['key' => 'name', 'title' => 'Name'],
['key' => 'email', 'title' => 'E-Mail'],
['key' => 'status', 'title' => 'Status', 'width' => 120],
]);
$table->setData([
['id' => 1, 'name' => 'Max Mustermann', 'email' => 'max@example.com', 'status' => 'Aktiv'],
['id' => 2, 'name' => 'Anna Schmidt', 'email' => 'anna@example.com', 'status' => 'Aktiv'],
['id' => 3, 'name' => 'Peter Weber', 'email' => 'peter@example.com', 'status' => 'Inaktiv'],
['id' => 4, 'name' => 'Lisa Müller', 'email' => 'lisa@example.com', 'status' => 'Aktiv'],
['id' => 5, 'name' => 'Tom Klein', 'email' => 'tom@example.com', 'status' => 'Aktiv'],
['id' => 6, 'name' => 'Sarah Wagner', 'email' => 'sarah@example.com', 'status' => 'Inaktiv'],
['id' => 7, 'name' => 'Michael Becker', 'email' => 'michael@example.com', 'status' => 'Aktiv'],
['id' => 8, 'name' => 'Julia Fischer', 'email' => 'julia@example.com', 'status' => 'Aktiv'],
['id' => 9, 'name' => 'Daniel Schneider', 'email' => 'daniel@example.com', 'status' => 'Inaktiv'],
['id' => 10, 'name' => 'Laura Hoffmann', 'email' => 'laura@example.com', 'status' => 'Aktiv'],
]);
// Row selection handler
$statusLabel = new Label(
text: 'Fenster: ' . $window->getViewport()->windowWidth . 'x' . $window->getViewport()->windowHeight,
style: 'basis-4/8 text-black',
);
$table->setOnRowSelect(function ($index, $row) use (&$statusLabel) {
if ($row) {
$statusLabel->setText("Selected: {$row['name']} ({$row['email']})");
}
});
$tab1->addComponent($table);
$tabContainer->addTab('Daten', $tab1);
// Tab 2: Some info
$tab2 = new Container('flex flex-col p-4');
$tab2->addComponent(new Label('Dies ist Tab 2', 'text-xl font-bold mb-4'));
$tab2->addComponent(new Label('Hier könnte weiterer Inhalt stehen...', ''));
$tabContainer->addTab('Info', $tab2);
// Tab 3: Settings
$tab3 = new Container('flex flex-col p-4');
$tab3->addComponent(new Label('Einstellungen', 'text-xl font-bold mb-4'));
$tab3->addComponent(new Label('Konfigurationsoptionen...', ''));
$tabContainer->addTab('Einstellungen', $tab3);
$mainContainer->addComponent($tabContainer);
// === 3. StatusBar ===
$statusBar = new StatusBar();
$statusBar->addSegment($statusLabel);
$fpsLabel = new Label(
text: 'FPS: --',
style: 'basis-1/8 text-black border-l',
);
$statusBar->addSegment(new Label(
text: 'Zeilen: 10',
style: 'basis-2/8 text-black border-l',
)); // Fixed width
$statusBar->addSegment($fpsLabel);
$statusBar->addSegment(new Label(
text: 'Version 1.0',
style: 'border-l text-black basis-2/8',
));
$mainContainer->addComponent($statusBar);
$cancelButton->setOnClick(function () use ($menuBar, $modal) {
$menuBar->closeAllMenus();
$modal->setVisible(false);
});
$saveButton->setOnClick(function () use (&$currentApiKey, $apiKeyInput, $menuBar, $modal, &$statusLabel) {
$currentApiKey = trim($apiKeyInput->getValue());
if ($statusLabel !== null) {
$masked = strlen($currentApiKey) > 4
? (str_repeat('*', max(0, strlen($currentApiKey) - 4)) . substr($currentApiKey, -4))
: $currentApiKey;
$statusLabel->setText('API-Key gespeichert: ' . $masked);
}
$menuBar->closeAllMenus();
$modal->setVisible(false);
});
$mainContainer->addComponent($modal);
$window->setOnResize(function (Window $window) use (&$statusLabel) {
$statusLabel->setText(
'Fenster: ' . $window->getViewport()->windowWidth . 'x' . $window->getViewport()->windowHeight,
);
});
$window->setOnFpsChange(function (float $fps) use ($fpsLabel) {
$fpsLabel->setText(sprintf('FPS: %d', max(0, (int) round($fps))));
});
// Set root and run
$window->setRoot($mainContainer);
$app->addWindow($window);
echo "Windows Application Example started!\n";
echo "Features:\n";
echo "- MenuBar with 'Datei' and 'Einstellungen'\n";
echo "- Tab Container with 3 tabs\n";
echo "- Scrollable Table in first tab\n";
echo "- StatusBar at the bottom\n\n";
$app->run();