222 lines
6.9 KiB
PHP
222 lines
6.9 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
require_once __DIR__ . '/../vendor/autoload.php';
|
|
|
|
use PHPNative\Framework\Application;
|
|
use PHPNative\Ui\Widget\Button;
|
|
use PHPNative\Ui\Widget\Container;
|
|
use PHPNative\Ui\Widget\Label;
|
|
use PHPNative\Ui\Widget\TextInput;
|
|
use PHPNative\Ui\Window;
|
|
|
|
$storagePath = __DIR__ . '/todo_data.json';
|
|
|
|
$loadTasks = static function (string $path): array {
|
|
if (!is_file($path)) {
|
|
return [];
|
|
}
|
|
|
|
$raw = file_get_contents($path);
|
|
$data = json_decode($raw ?: '[]', true);
|
|
|
|
if (!is_array($data)) {
|
|
return [];
|
|
}
|
|
|
|
// Normalize task structure
|
|
return array_values(array_map(static function ($task) {
|
|
return [
|
|
'id' => $task['id'] ?? uniqid('task_', true),
|
|
'title' => trim((string) ($task['title'] ?? '')),
|
|
'done' => (bool) ($task['done'] ?? false),
|
|
];
|
|
}, $data));
|
|
};
|
|
|
|
$saveTasks = static function (string $path, array $tasks): void {
|
|
file_put_contents($path, json_encode($tasks, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE), LOCK_EX);
|
|
};
|
|
|
|
$tasks = $loadTasks($storagePath);
|
|
|
|
$app = new Application();
|
|
$window = new Window('Todo Beispiel', 520, 720);
|
|
|
|
$statusLabel = new Label('Bereit.', 'text-sm text-gray-600');
|
|
|
|
$main = new Container('flex flex-col bg-gray-100 gap-4 p-4 h-full w-full');
|
|
$title = new Label('Todo Liste', 'text-2xl font-bold text-black');
|
|
$main->addComponent($title);
|
|
|
|
$input = new TextInput(
|
|
'Neue Aufgabe hinzufügen …',
|
|
'flex-1 border border-gray-300 rounded px-3 py-2 bg-white text-black',
|
|
);
|
|
$addButton = new Button('Hinzufügen', 'px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700');
|
|
$inputRow = new Container('flex flex-row gap-3 w-full');
|
|
$inputRow->addComponent($input);
|
|
$inputRow->addComponent($addButton);
|
|
$main->addComponent($inputRow);
|
|
|
|
$listWrapper = new Container('flex-1 w-full overflow-auto bg-white rounded border border-gray-300 p-2');
|
|
$listContainer = new Container('flex flex-col gap-2 w-full');
|
|
$listWrapper->addComponent($listContainer);
|
|
$main->addComponent($listWrapper);
|
|
$main->addComponent($statusLabel);
|
|
|
|
$renderTasks = null;
|
|
$renderTasks = function () use (&$tasks, $listContainer, $statusLabel, $storagePath, $saveTasks, &$renderTasks) {
|
|
$listContainer->clearChildren();
|
|
|
|
if (empty($tasks)) {
|
|
$emptyLabel = new Label('Keine Aufgaben vorhanden. Erstelle die erste oben im Feld.', 'text-sm text-gray-500');
|
|
$listContainer->addComponent($emptyLabel);
|
|
return;
|
|
}
|
|
|
|
foreach ($tasks as $index => $task) {
|
|
$row = new Container(
|
|
'flex flex-row items-center gap-3 w-full border border-gray-200 rounded px-3 py-2 bg-white shadow-sm',
|
|
);
|
|
$taskLabelStyles = $task['done'] ? 'flex-1 text-gray-500 line-through' : 'flex-1 text-black';
|
|
$taskLabel = new Label($task['title'], $taskLabelStyles);
|
|
$row->addComponent($taskLabel);
|
|
|
|
$toggleButton = new Button(
|
|
$task['done'] ? 'Reaktivieren' : 'Erledigt',
|
|
$task['done']
|
|
? 'px-3 py-1 text-sm bg-amber-500 text-white rounded hover:bg-amber-600'
|
|
: 'px-3 py-1 text-sm bg-emerald-500 text-white rounded hover:bg-emerald-600',
|
|
);
|
|
|
|
$toggleButton->setOnClick(function () use (
|
|
&$tasks,
|
|
$task,
|
|
$storagePath,
|
|
$saveTasks,
|
|
$statusLabel,
|
|
$renderTasks,
|
|
) {
|
|
foreach ($tasks as &$entry) {
|
|
if ($entry['id'] === $task['id']) {
|
|
$entry['done'] = !$entry['done'];
|
|
break;
|
|
}
|
|
}
|
|
|
|
$saveTasks($storagePath, $tasks);
|
|
$statusLabel->setText(($task['done'] ? 'Aufgabe reaktiviert: ' : 'Aufgabe erledigt: ') . $task['title']);
|
|
$renderTasks();
|
|
});
|
|
|
|
$deleteButton = new Button('Löschen', 'px-3 py-1 text-sm bg-red-500 text-white rounded hover:bg-red-600');
|
|
|
|
$deleteButton->setOnClick(function () use (
|
|
&$tasks,
|
|
$index,
|
|
$task,
|
|
$storagePath,
|
|
$saveTasks,
|
|
$statusLabel,
|
|
$renderTasks,
|
|
) {
|
|
array_splice($tasks, $index, 1);
|
|
$saveTasks($storagePath, $tasks);
|
|
$statusLabel->setText('Aufgabe entfernt: ' . $task['title']);
|
|
$renderTasks();
|
|
});
|
|
|
|
$row->addComponent($toggleButton);
|
|
$row->addComponent($deleteButton);
|
|
|
|
$listContainer->addComponent($row);
|
|
}
|
|
};
|
|
|
|
$renderTasks();
|
|
|
|
$addButton->setOnClick(function () use (&$tasks, $input, $saveTasks, $storagePath, $statusLabel, $renderTasks) {
|
|
$title = trim($input->getValue());
|
|
if ($title === '') {
|
|
$statusLabel->setText('Bitte zuerst einen Aufgabentext eingeben.');
|
|
return;
|
|
}
|
|
|
|
$tasks[] = [
|
|
'id' => uniqid('task_', true),
|
|
'title' => $title,
|
|
'done' => false,
|
|
];
|
|
|
|
$saveTasks($storagePath, $tasks);
|
|
$input->setValue('');
|
|
$statusLabel->setText('Aufgabe hinzugefügt: ' . $title);
|
|
$renderTasks();
|
|
});
|
|
|
|
$window->setRoot($main);
|
|
$app->addWindow($window);
|
|
|
|
// Setup Tray with callbacks
|
|
if (function_exists('tray_setup')) {
|
|
try {
|
|
tray_setup('', [
|
|
[
|
|
'label' => 'Neue Aufgabe',
|
|
'callback' => function ($idx) use (
|
|
&$tasks,
|
|
$input,
|
|
$saveTasks,
|
|
$storagePath,
|
|
$statusLabel,
|
|
$renderTasks,
|
|
) {
|
|
try {
|
|
error_log('Neue Aufgabe Callback called!');
|
|
|
|
$title = 'Tray: Neue Aufgabe';
|
|
|
|
error_log("Adding task: {$title}");
|
|
|
|
$tasks[] = [
|
|
'id' => uniqid('task_', true),
|
|
'title' => $title,
|
|
'done' => false,
|
|
];
|
|
|
|
error_log('Saving tasks...');
|
|
$saveTasks($storagePath, $tasks);
|
|
|
|
error_log('Updating UI...');
|
|
$statusLabel->setText('Aufgabe hinzugefügt: ' . $title);
|
|
$renderTasks();
|
|
|
|
error_log('Done!');
|
|
} catch (\Throwable $e) {
|
|
error_log('ERROR in Neue Aufgabe callback: ' . $e->getMessage());
|
|
error_log('Trace: ' . $e->getTraceAsString());
|
|
}
|
|
},
|
|
],
|
|
[
|
|
'label' => 'Fenster anzeigen',
|
|
'callback' => function ($idx) use ($window) {
|
|
// Show/focus window (TODO: implement window focus/show API)
|
|
},
|
|
],
|
|
[
|
|
'label' => 'Beenden',
|
|
'callback' => function ($idx) use ($app) {
|
|
$app->quit();
|
|
},
|
|
],
|
|
]);
|
|
} catch (\Throwable $e) {
|
|
error_log('Tray setup failed: ' . $e->getMessage());
|
|
}
|
|
}
|
|
|
|
$app->run();
|