first try
This commit is contained in:
parent
11767487ef
commit
899499f2e5
189
examples/advanced_multi_window_example.php
Normal file
189
examples/advanced_multi_window_example.php
Normal file
@ -0,0 +1,189 @@
|
||||
<?php
|
||||
|
||||
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;
|
||||
|
||||
// Create application
|
||||
$app = new Application();
|
||||
|
||||
// Create main window
|
||||
$mainWindow = $app->createWindow('Advanced Multi-Window Demo', 700, 500, 100, 100);
|
||||
|
||||
$windowCounter = 1;
|
||||
|
||||
// Main UI
|
||||
$mainContainer = new Container('flex flex-col p-6 gap-4 bg-gray-50');
|
||||
|
||||
$title = new Label(
|
||||
text: 'Advanced Multi-Window Demo',
|
||||
style: 'text-2xl text-gray-900'
|
||||
);
|
||||
|
||||
$subtitle = new Label(
|
||||
text: 'Windows mit asynchronen Operationen',
|
||||
style: 'text-base text-gray-600'
|
||||
);
|
||||
|
||||
// Button to create window with async data loading
|
||||
$createAsyncWindowButton = new Button(
|
||||
text: 'Window mit Async-Datenladung',
|
||||
style: 'bg-purple-500 hover:bg-purple-700 text-white p-4 rounded-lg'
|
||||
);
|
||||
|
||||
$createAsyncWindowButton->setOnClickAsync(
|
||||
onClickAsync: function() use (&$windowCounter) {
|
||||
// Simulate loading data in background
|
||||
sleep(1);
|
||||
|
||||
return [
|
||||
'windowId' => $windowCounter++,
|
||||
'data' => 'Daten erfolgreich geladen!',
|
||||
'timestamp' => date('H:i:s'),
|
||||
'items' => ['Item 1', 'Item 2', 'Item 3']
|
||||
];
|
||||
},
|
||||
|
||||
onComplete: function($result) use ($app) {
|
||||
// Create window with loaded data
|
||||
$windowId = $result['windowId'];
|
||||
$newWindow = $app->createWindow(
|
||||
"Data Window #$windowId",
|
||||
500,
|
||||
350,
|
||||
150 + ($windowId * 30),
|
||||
150 + ($windowId * 30)
|
||||
);
|
||||
|
||||
$container = new Container('flex flex-col p-6 gap-3 bg-green-50');
|
||||
|
||||
$titleLabel = new Label(
|
||||
text: "Data Window #$windowId",
|
||||
style: 'text-xl text-gray-900'
|
||||
);
|
||||
|
||||
$dataLabel = new Label(
|
||||
text: $result['data'],
|
||||
style: 'text-base text-green-700 p-2 bg-white rounded'
|
||||
);
|
||||
|
||||
$timeLabel = new Label(
|
||||
text: "Geladen um: " . $result['timestamp'],
|
||||
style: 'text-sm text-gray-600'
|
||||
);
|
||||
|
||||
$itemsLabel = new Label(
|
||||
text: "Items:\n" . implode("\n", $result['items']),
|
||||
style: 'text-sm text-gray-700 p-2 bg-white rounded'
|
||||
);
|
||||
|
||||
$closeButton = new Button(
|
||||
text: 'Schließen',
|
||||
style: 'bg-red-500 hover:bg-red-700 text-white p-3 rounded'
|
||||
);
|
||||
|
||||
$closeButton->setOnClick(function() use ($newWindow) {
|
||||
$newWindow->close();
|
||||
});
|
||||
|
||||
$container->addComponent($titleLabel);
|
||||
$container->addComponent($dataLabel);
|
||||
$container->addComponent($timeLabel);
|
||||
$container->addComponent($itemsLabel);
|
||||
$container->addComponent($closeButton);
|
||||
|
||||
$newWindow->setRoot($container);
|
||||
},
|
||||
|
||||
onError: function($error) {
|
||||
error_log("Error creating window: " . $error->getMessage());
|
||||
}
|
||||
);
|
||||
|
||||
// Button to create multiple windows at once
|
||||
$createMultipleButton = new Button(
|
||||
text: '3 Windows auf einmal erstellen',
|
||||
style: 'bg-orange-500 hover:bg-orange-700 text-white p-4 rounded-lg'
|
||||
);
|
||||
|
||||
$createMultipleButton->setOnClick(function() use ($app, &$windowCounter) {
|
||||
for ($i = 0; $i < 3; $i++) {
|
||||
$currentId = $windowCounter++;
|
||||
$offset = $currentId * 40;
|
||||
|
||||
$newWindow = $app->createWindow(
|
||||
"Batch Window #$currentId",
|
||||
400,
|
||||
250,
|
||||
200 + $offset,
|
||||
200 + ($i * 50)
|
||||
);
|
||||
|
||||
$container = new Container('flex flex-col p-4 gap-2 bg-yellow-50');
|
||||
|
||||
$label = new Label(
|
||||
text: "Batch Window #$currentId",
|
||||
style: 'text-lg text-gray-900'
|
||||
);
|
||||
|
||||
$info = new Label(
|
||||
text: "Teil einer Batch-Erstellung ($i/3)",
|
||||
style: 'text-sm text-gray-600'
|
||||
);
|
||||
|
||||
$closeButton = new Button(
|
||||
text: 'X',
|
||||
style: 'bg-red-500 hover:bg-red-700 text-white p-2 rounded'
|
||||
);
|
||||
|
||||
$closeButton->setOnClick(function() use ($newWindow) {
|
||||
$newWindow->close();
|
||||
});
|
||||
|
||||
$container->addComponent($label);
|
||||
$container->addComponent($info);
|
||||
$container->addComponent($closeButton);
|
||||
|
||||
$newWindow->setRoot($container);
|
||||
}
|
||||
});
|
||||
|
||||
// Window count label
|
||||
$windowCountLabel = new Label(
|
||||
text: 'Offene Windows: 1',
|
||||
style: 'text-sm text-blue-600 p-2 bg-white rounded'
|
||||
);
|
||||
|
||||
// Update window count periodically
|
||||
$mainContainer->addComponent($title);
|
||||
$mainContainer->addComponent($subtitle);
|
||||
$mainContainer->addComponent($windowCountLabel);
|
||||
$mainContainer->addComponent($createAsyncWindowButton);
|
||||
$mainContainer->addComponent($createMultipleButton);
|
||||
|
||||
// Info
|
||||
$info = new Label(
|
||||
text: 'Die UI bleibt responsive während des Ladens!',
|
||||
style: 'text-xs text-gray-500 italic mt-4'
|
||||
);
|
||||
$mainContainer->addComponent($info);
|
||||
|
||||
// Quit button
|
||||
$quitButton = new Button(
|
||||
text: 'Beenden',
|
||||
style: 'bg-red-600 hover:bg-red-800 text-white p-4 rounded-lg'
|
||||
);
|
||||
|
||||
$quitButton->setOnClick(function() use ($app) {
|
||||
$app->quit();
|
||||
});
|
||||
|
||||
$mainContainer->addComponent($quitButton);
|
||||
|
||||
$mainWindow->setRoot($mainContainer);
|
||||
|
||||
// Run application
|
||||
$app->run();
|
||||
161
examples/multi_window_example.php
Normal file
161
examples/multi_window_example.php
Normal file
@ -0,0 +1,161 @@
|
||||
<?php
|
||||
|
||||
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;
|
||||
|
||||
// Create application
|
||||
$app = new Application();
|
||||
|
||||
// Create main window
|
||||
$mainWindow = $app->createWindow('Main Window - Multi-Window Demo', 600, 400, 100, 100);
|
||||
|
||||
// Counter for new windows
|
||||
$windowCounter = 1;
|
||||
|
||||
// Create main window UI
|
||||
$mainContainer = new Container('flex flex-col p-6 gap-4 bg-gray-100');
|
||||
|
||||
$title = new Label(
|
||||
text: 'Multi-Window Demo',
|
||||
style: 'text-2xl text-gray-900'
|
||||
);
|
||||
|
||||
$description = new Label(
|
||||
text: 'Klicken Sie auf die Buttons, um neue Windows zu erstellen!',
|
||||
style: 'text-base text-gray-700'
|
||||
);
|
||||
|
||||
$windowCountLabel = new Label(
|
||||
text: 'Offene Windows: 1',
|
||||
style: 'text-sm text-blue-600 p-2 bg-white rounded'
|
||||
);
|
||||
|
||||
// Button to create a simple new window
|
||||
$createSimpleButton = new Button(
|
||||
text: 'Neues Fenster erstellen',
|
||||
style: 'bg-blue-500 hover:bg-blue-700 text-white p-4 rounded-lg'
|
||||
);
|
||||
|
||||
$createSimpleButton->setOnClick(function() use ($app, &$windowCounter, $windowCountLabel) {
|
||||
// Create new window with offset position
|
||||
$offset = $windowCounter * 30;
|
||||
$newWindow = $app->createWindow(
|
||||
"Window #$windowCounter",
|
||||
500,
|
||||
300,
|
||||
150 + $offset,
|
||||
150 + $offset
|
||||
);
|
||||
|
||||
// Create UI for new window
|
||||
$container = new Container('flex flex-col p-6 gap-3 bg-gradient-to-br from-purple-400 to-pink-400');
|
||||
|
||||
$label = new Label(
|
||||
text: "Dies ist Window #$windowCounter",
|
||||
style: 'text-xl text-white'
|
||||
);
|
||||
|
||||
$closeButton = new Button(
|
||||
text: 'Fenster schließen',
|
||||
style: 'bg-red-500 hover:bg-red-700 text-white p-3 rounded'
|
||||
);
|
||||
|
||||
$closeButton->setOnClick(function() use ($newWindow) {
|
||||
$newWindow->close();
|
||||
});
|
||||
|
||||
$container->addComponent($label);
|
||||
$container->addComponent($closeButton);
|
||||
$newWindow->setRoot($container);
|
||||
|
||||
$windowCounter++;
|
||||
|
||||
// Update counter
|
||||
$windowCountLabel->setText('Offene Windows: ' . $app->getWindowCount());
|
||||
});
|
||||
|
||||
// Button to create a window with interactive content
|
||||
$createInteractiveButton = new Button(
|
||||
text: 'Interaktives Fenster erstellen',
|
||||
style: 'bg-green-500 hover:bg-green-700 text-white p-4 rounded-lg'
|
||||
);
|
||||
|
||||
$createInteractiveButton->setOnClick(function() use ($app, &$windowCounter, $windowCountLabel) {
|
||||
$offset = $windowCounter * 30;
|
||||
$newWindow = $app->createWindow(
|
||||
"Interactive Window #$windowCounter",
|
||||
600,
|
||||
400,
|
||||
150 + $offset,
|
||||
150 + $offset
|
||||
);
|
||||
|
||||
// Create interactive UI
|
||||
$container = new Container('flex flex-col p-6 gap-3 bg-blue-50');
|
||||
|
||||
$title = new Label(
|
||||
text: "Interaktives Window #$windowCounter",
|
||||
style: 'text-xl text-gray-900'
|
||||
);
|
||||
|
||||
$counter = 0;
|
||||
$counterLabel = new Label(
|
||||
text: "Zähler: $counter",
|
||||
style: 'text-lg text-blue-600 p-2 bg-white rounded'
|
||||
);
|
||||
|
||||
$incrementButton = new Button(
|
||||
text: 'Zähler erhöhen',
|
||||
style: 'bg-blue-500 hover:bg-blue-700 text-white p-3 rounded'
|
||||
);
|
||||
|
||||
$incrementButton->setOnClick(function() use (&$counter, $counterLabel) {
|
||||
$counter++;
|
||||
$counterLabel->setText("Zähler: $counter");
|
||||
});
|
||||
|
||||
$closeButton = new Button(
|
||||
text: 'Fenster schließen',
|
||||
style: 'bg-red-500 hover:bg-red-700 text-white p-3 rounded'
|
||||
);
|
||||
|
||||
$closeButton->setOnClick(function() use ($newWindow) {
|
||||
$newWindow->close();
|
||||
});
|
||||
|
||||
$container->addComponent($title);
|
||||
$container->addComponent($counterLabel);
|
||||
$container->addComponent($incrementButton);
|
||||
$container->addComponent($closeButton);
|
||||
$newWindow->setRoot($container);
|
||||
|
||||
$windowCounter++;
|
||||
$windowCountLabel->setText('Offene Windows: ' . $app->getWindowCount());
|
||||
});
|
||||
|
||||
// Button to quit application
|
||||
$quitButton = new Button(
|
||||
text: 'Alle Fenster schließen und beenden',
|
||||
style: 'bg-red-600 hover:bg-red-800 text-white p-4 rounded-lg mt-4'
|
||||
);
|
||||
|
||||
$quitButton->setOnClick(function() use ($app) {
|
||||
$app->quit();
|
||||
});
|
||||
|
||||
// Add all components to main container
|
||||
$mainContainer->addComponent($title);
|
||||
$mainContainer->addComponent($description);
|
||||
$mainContainer->addComponent($windowCountLabel);
|
||||
$mainContainer->addComponent($createSimpleButton);
|
||||
$mainContainer->addComponent($createInteractiveButton);
|
||||
$mainContainer->addComponent($quitButton);
|
||||
|
||||
$mainWindow->setRoot($mainContainer);
|
||||
|
||||
// Run application
|
||||
$app->run();
|
||||
65
examples/simple_window_example.php
Normal file
65
examples/simple_window_example.php
Normal file
@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
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;
|
||||
|
||||
// Create application
|
||||
$app = new Application();
|
||||
|
||||
// Create a single window (simple usage)
|
||||
$window = $app->createWindow('Simple Window Example', 800, 600);
|
||||
|
||||
// Create UI
|
||||
$container = new Container('flex flex-col p-6 gap-4 bg-gradient-to-br from-blue-400 to-purple-500');
|
||||
|
||||
$title = new Label(
|
||||
text: 'Willkommen zu PHPNative!',
|
||||
style: 'text-3xl text-white'
|
||||
);
|
||||
|
||||
$description = new Label(
|
||||
text: 'Dies ist ein einfaches Beispiel mit einem Window.',
|
||||
style: 'text-lg text-white'
|
||||
);
|
||||
|
||||
$button = new Button(
|
||||
text: 'Klick mich!',
|
||||
style: 'bg-white hover:bg-gray-100 text-blue-600 p-4 rounded-lg'
|
||||
);
|
||||
|
||||
$statusLabel = new Label(
|
||||
text: '',
|
||||
style: 'text-base text-white'
|
||||
);
|
||||
|
||||
$clickCount = 0;
|
||||
$button->setOnClick(function() use ($statusLabel, &$clickCount) {
|
||||
$clickCount++;
|
||||
$statusLabel->setText("Button wurde $clickCount mal geklickt!");
|
||||
});
|
||||
|
||||
$quitButton = new Button(
|
||||
text: 'Beenden',
|
||||
style: 'bg-red-500 hover:bg-red-700 text-white p-4 rounded-lg mt-4'
|
||||
);
|
||||
|
||||
$quitButton->setOnClick(function() use ($app) {
|
||||
$app->quit();
|
||||
});
|
||||
|
||||
// Add components
|
||||
$container->addComponent($title);
|
||||
$container->addComponent($description);
|
||||
$container->addComponent($button);
|
||||
$container->addComponent($statusLabel);
|
||||
$container->addComponent($quitButton);
|
||||
|
||||
// Set window root
|
||||
$window->setRoot($container);
|
||||
|
||||
// Run application
|
||||
$app->run();
|
||||
@ -4,229 +4,129 @@ namespace PHPNative\Framework;
|
||||
|
||||
use PHPNative\Async\TaskManager;
|
||||
use PHPNative\Ui\Component;
|
||||
use PHPNative\Ui\Viewport;
|
||||
use PHPNative\Ui\Window;
|
||||
|
||||
class Application
|
||||
{
|
||||
private static ?Application $instance = null;
|
||||
protected array $windows = [];
|
||||
protected int $nextWindowId = 0;
|
||||
protected bool $running = true;
|
||||
|
||||
protected $window;
|
||||
protected $windowWidth;
|
||||
protected $windowHeight;
|
||||
protected $title;
|
||||
protected $running = true;
|
||||
protected $rootComponent = null;
|
||||
private TextRenderer $textRenderer;
|
||||
private $mouseX;
|
||||
private $mouseY;
|
||||
private Viewport $viewport;
|
||||
private bool $shouldBeReLayouted = true;
|
||||
private float $pixelRatio = 2;
|
||||
|
||||
public function __construct(string $title, int $width = 800, int $height = 600)
|
||||
public function __construct()
|
||||
{
|
||||
$this->title = $title;
|
||||
$this->windowWidth = $width;
|
||||
$this->windowHeight = $height;
|
||||
|
||||
// Create window
|
||||
$this->window = rgfw_createWindow($title, 100, 100, $width, $height, RGFW_CENTER);
|
||||
if (!$this->window) {
|
||||
throw new \Exception('Failed to create window');
|
||||
self::$instance = $this;
|
||||
}
|
||||
|
||||
// Initialize RSGL renderer
|
||||
if (!rsgl_init($this->window)) {
|
||||
throw new \Exception('Failed to initialize RSGL renderer');
|
||||
}
|
||||
|
||||
// Initialize text renderer
|
||||
$this->textRenderer = new TextRenderer($this->window);
|
||||
if (!$this->textRenderer->init()) {
|
||||
error_log('Warning: Failed to initialize text renderer. Text rendering will not be available.');
|
||||
}
|
||||
|
||||
// Get actual window size
|
||||
$size = rgfw_window_getSize($this->window);
|
||||
$this->windowWidth = $size[0];
|
||||
$this->windowHeight = $size[1];
|
||||
$this->viewport = new Viewport(
|
||||
windowWidth: $this->windowWidth,
|
||||
windowHeight: $this->windowHeight,
|
||||
width: $this->windowWidth,
|
||||
height: $this->windowHeight,
|
||||
);
|
||||
}
|
||||
|
||||
public function createWindow(): void
|
||||
/**
|
||||
* Get singleton instance
|
||||
*/
|
||||
public static function getInstance(): ?Application
|
||||
{
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new window
|
||||
*
|
||||
* @param string $title Window title
|
||||
* @param int $width Window width
|
||||
* @param int $height Window height
|
||||
* @param int $x Window X position (default: 100)
|
||||
* @param int $y Window Y position (default: 100)
|
||||
* @return Window The created window instance
|
||||
*/
|
||||
public function createWindow(
|
||||
string $title,
|
||||
int $width = 800,
|
||||
int $height = 600,
|
||||
int $x = 100,
|
||||
int $y = 100
|
||||
): Window {
|
||||
$window = new Window($title, $width, $height, $x, $y);
|
||||
$windowId = $this->nextWindowId++;
|
||||
$this->windows[$windowId] = $window;
|
||||
|
||||
return $window;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all windows
|
||||
*/
|
||||
public function getWindows(): array
|
||||
{
|
||||
return $this->windows;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get window count
|
||||
*/
|
||||
public function getWindowCount(): int
|
||||
{
|
||||
return count($this->windows);
|
||||
}
|
||||
|
||||
/**
|
||||
* Main application loop
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
while ($this->running && !rgfw_window_shouldClose($this->window)) {
|
||||
$this->handleEvents();
|
||||
$this->update();
|
||||
$this->layout();
|
||||
$this->render();
|
||||
while ($this->running && count($this->windows) > 0) {
|
||||
// Handle events for all windows
|
||||
foreach ($this->windows as $windowId => $window) {
|
||||
$window->handleEvents();
|
||||
}
|
||||
|
||||
// Update async tasks (global)
|
||||
TaskManager::getInstance()->update();
|
||||
|
||||
// Update all windows
|
||||
foreach ($this->windows as $windowId => $window) {
|
||||
$window->update();
|
||||
}
|
||||
|
||||
// Layout all windows
|
||||
foreach ($this->windows as $windowId => $window) {
|
||||
$window->layout();
|
||||
}
|
||||
|
||||
// Render all windows
|
||||
foreach ($this->windows as $windowId => $window) {
|
||||
$window->render();
|
||||
}
|
||||
|
||||
// Remove closed windows
|
||||
foreach ($this->windows as $windowId => $window) {
|
||||
if ($window->shouldClose()) {
|
||||
$window->cleanup();
|
||||
unset($this->windows[$windowId]);
|
||||
}
|
||||
}
|
||||
|
||||
// Limit frame rate to ~60 FPS
|
||||
usleep(16666);
|
||||
}
|
||||
|
||||
// Cleanup remaining windows
|
||||
$this->cleanup();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle window and input events
|
||||
*/
|
||||
protected function handleEvents(): void
|
||||
{
|
||||
while ($event = rgfw_window_checkEvent($this->window)) {
|
||||
switch ($event['type']) {
|
||||
case RGFW_quit:
|
||||
$this->running = false;
|
||||
break;
|
||||
|
||||
case RGFW_keyPressed:
|
||||
$keyCode = $event['keyCode'] ?? 0;
|
||||
if ($keyCode == RGFW_Escape) {
|
||||
$this->running = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case RGFW_windowResized:
|
||||
// Update window dimensions (from event data)
|
||||
$newWidth = $event[0] ?? $this->windowWidth;
|
||||
$newHeight = $event[1] ?? $this->windowHeight;
|
||||
|
||||
$this->windowWidth = $newWidth;
|
||||
$this->windowHeight = $newHeight;
|
||||
|
||||
// Update RSGL renderer size and viewport
|
||||
// This ensures the renderer is properly configured for the new window size
|
||||
rsgl_updateRendererSize($this->window);
|
||||
|
||||
// Update text renderer framebuffer
|
||||
if ($this->textRenderer && $this->textRenderer->isInitialized()) {
|
||||
$this->textRenderer->updateFramebuffer($newWidth, $newHeight);
|
||||
}
|
||||
|
||||
$this->viewport->x = 0;
|
||||
$this->viewport->y = 0;
|
||||
$this->viewport->windowWidth = $newWidth;
|
||||
$this->viewport->width = $newWidth;
|
||||
$this->viewport->height = $newHeight;
|
||||
$this->viewport->windowHeight = $newHeight;
|
||||
$this->shouldBeReLayouted = true;
|
||||
break;
|
||||
|
||||
case RGFW_mousePosChanged:
|
||||
$this->mouseX = $event[0] ?? 0;
|
||||
$this->mouseY = $event[1] ?? 0;
|
||||
|
||||
// Propagate mouse move to root component
|
||||
if ($this->rootComponent) {
|
||||
$this->rootComponent->handleMouseMove($this->mouseX, $this->mouseY);
|
||||
}
|
||||
break;
|
||||
|
||||
case RGFW_mouseButtonPressed:
|
||||
$button = $event['button'] ?? 0;
|
||||
// Propagate click to root component
|
||||
if ($this->rootComponent) {
|
||||
$this->rootComponent->handleMouseClick($this->mouseX, $this->mouseY, $button);
|
||||
}
|
||||
break;
|
||||
|
||||
case RGFW_mouseButtonReleased:
|
||||
$button = $event['button'] ?? 0;
|
||||
|
||||
// Propagate release to root component
|
||||
if ($this->rootComponent) {
|
||||
$this->rootComponent->handleMouseRelease($this->mouseX, $this->mouseY, $button);
|
||||
}
|
||||
break;
|
||||
|
||||
case RGFW_mouseScroll:
|
||||
$deltaY = $event[1] ?? 0;
|
||||
|
||||
// Propagate wheel to root component
|
||||
if ($this->rootComponent) {
|
||||
$this->rootComponent->handleMouseWheel($this->mouseX, $this->mouseY, $deltaY);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update application state
|
||||
*/
|
||||
protected function update(): void
|
||||
{
|
||||
// Update async tasks
|
||||
TaskManager::getInstance()->update();
|
||||
|
||||
if ($this->rootComponent) {
|
||||
$this->rootComponent->update();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the application
|
||||
*/
|
||||
protected function render(): void
|
||||
{
|
||||
rsgl_clear($this->window, 255, 255, 255, 0);
|
||||
|
||||
// Render root component tree
|
||||
if ($this->rootComponent) {
|
||||
$this->rootComponent->render($this->textRenderer);
|
||||
$this->rootComponent->renderContent($this->textRenderer);
|
||||
}
|
||||
|
||||
// Render to screen
|
||||
rsgl_render($this->window);
|
||||
rgfw_window_swapBuffers($this->window);
|
||||
}
|
||||
|
||||
protected function layout(): void
|
||||
{
|
||||
// Render root component tree
|
||||
|
||||
if ($this->rootComponent && $this->shouldBeReLayouted) {
|
||||
$this->rootComponent->setViewport($this->viewport);
|
||||
$this->rootComponent->setWindow($this->window);
|
||||
$this->rootComponent->layout($this->textRenderer);
|
||||
$this->shouldBeReLayouted = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up resources
|
||||
* Clean up all resources
|
||||
*/
|
||||
protected function cleanup(): void
|
||||
{
|
||||
if ($this->textRenderer) {
|
||||
$this->textRenderer->free();
|
||||
foreach ($this->windows as $window) {
|
||||
$window->cleanup();
|
||||
}
|
||||
rsgl_close($this->window);
|
||||
rgfw_window_close($this->window);
|
||||
$this->windows = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the application
|
||||
* Stop the application (close all windows)
|
||||
*/
|
||||
public function quit(): void
|
||||
{
|
||||
$this->running = false;
|
||||
}
|
||||
|
||||
public function setRoot(Component $component): self
|
||||
{
|
||||
$this->rootComponent = $component;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
@ -22,6 +22,12 @@ class StyleParser
|
||||
$styleStr = trim($styleStr);
|
||||
|
||||
$style = self::parseSimpleStyle($styleStr);
|
||||
|
||||
// Skip if style is not recognized
|
||||
if($style === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$s = new \PHPNative\Tailwind\Model\Style($style);
|
||||
$mq = \PHPNative\Tailwind\Parser\MediaQuery::parse($styleStr);
|
||||
if($mq) {
|
||||
|
||||
@ -2,6 +2,230 @@
|
||||
|
||||
namespace PHPNative\Ui;
|
||||
|
||||
use PHPNative\Framework\TextRenderer;
|
||||
|
||||
class Window
|
||||
{
|
||||
private mixed $window = null;
|
||||
private ?Component $rootComponent = null;
|
||||
private TextRenderer $textRenderer;
|
||||
private float $mouseX = 0;
|
||||
private float $mouseY = 0;
|
||||
private Viewport $viewport;
|
||||
private bool $shouldBeReLayouted = true;
|
||||
private float $pixelRatio = 2;
|
||||
private bool $shouldClose = false;
|
||||
|
||||
public function __construct(
|
||||
private string $title,
|
||||
private int $width = 800,
|
||||
private int $height = 600,
|
||||
private int $x = 100,
|
||||
private int $y = 100
|
||||
) {
|
||||
// Create window
|
||||
$this->window = rgfw_createWindow($title, $x, $y, $width, $height, 0);
|
||||
if (!$this->window) {
|
||||
throw new \Exception('Failed to create window');
|
||||
}
|
||||
|
||||
// Initialize RSGL renderer
|
||||
if (!rsgl_init($this->window)) {
|
||||
throw new \Exception('Failed to initialize RSGL renderer');
|
||||
}
|
||||
|
||||
// Initialize text renderer
|
||||
$this->textRenderer = new TextRenderer($this->window);
|
||||
if (!$this->textRenderer->init()) {
|
||||
error_log('Warning: Failed to initialize text renderer. Text rendering will not be available.');
|
||||
}
|
||||
|
||||
// Get actual window size
|
||||
$size = rgfw_window_getSize($this->window);
|
||||
$this->width = $size[0];
|
||||
$this->height = $size[1];
|
||||
$this->viewport = new Viewport(
|
||||
windowWidth: $this->width,
|
||||
windowHeight: $this->height,
|
||||
width: $this->width,
|
||||
height: $this->height,
|
||||
);
|
||||
}
|
||||
|
||||
public function setRoot(Component $component): self
|
||||
{
|
||||
$this->rootComponent = $component;
|
||||
$this->shouldBeReLayouted = true;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getRoot(): ?Component
|
||||
{
|
||||
return $this->rootComponent;
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
public function getWidth(): int
|
||||
{
|
||||
return $this->width;
|
||||
}
|
||||
|
||||
public function getHeight(): int
|
||||
{
|
||||
return $this->height;
|
||||
}
|
||||
|
||||
public function shouldClose(): bool
|
||||
{
|
||||
return $this->shouldClose || rgfw_window_shouldClose($this->window);
|
||||
}
|
||||
|
||||
public function close(): void
|
||||
{
|
||||
$this->shouldClose = true;
|
||||
}
|
||||
|
||||
public function getWindowResource(): mixed
|
||||
{
|
||||
return $this->window;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle window and input events
|
||||
*/
|
||||
public function handleEvents(): void
|
||||
{
|
||||
while ($event = rgfw_window_checkEvent($this->window)) {
|
||||
switch ($event['type']) {
|
||||
case RGFW_quit:
|
||||
$this->shouldClose = true;
|
||||
break;
|
||||
|
||||
case RGFW_keyPressed:
|
||||
$keyCode = $event['keyCode'] ?? 0;
|
||||
if ($keyCode == RGFW_Escape) {
|
||||
$this->shouldClose = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case RGFW_windowResized:
|
||||
// Update window dimensions (from event data)
|
||||
$newWidth = $event[0] ?? $this->width;
|
||||
$newHeight = $event[1] ?? $this->height;
|
||||
|
||||
$this->width = $newWidth;
|
||||
$this->height = $newHeight;
|
||||
|
||||
// Update RSGL renderer size and viewport
|
||||
rsgl_updateRendererSize($this->window);
|
||||
|
||||
// Update text renderer framebuffer
|
||||
if ($this->textRenderer && $this->textRenderer->isInitialized()) {
|
||||
$this->textRenderer->updateFramebuffer($newWidth, $newHeight);
|
||||
}
|
||||
|
||||
$this->viewport->x = 0;
|
||||
$this->viewport->y = 0;
|
||||
$this->viewport->windowWidth = $newWidth;
|
||||
$this->viewport->width = $newWidth;
|
||||
$this->viewport->height = $newHeight;
|
||||
$this->viewport->windowHeight = $newHeight;
|
||||
$this->shouldBeReLayouted = true;
|
||||
break;
|
||||
|
||||
case RGFW_mousePosChanged:
|
||||
$this->mouseX = $event[0] ?? 0;
|
||||
$this->mouseY = $event[1] ?? 0;
|
||||
|
||||
// Propagate mouse move to root component
|
||||
if ($this->rootComponent) {
|
||||
$this->rootComponent->handleMouseMove($this->mouseX, $this->mouseY);
|
||||
}
|
||||
break;
|
||||
|
||||
case RGFW_mouseButtonPressed:
|
||||
$button = $event['button'] ?? 0;
|
||||
// Propagate click to root component
|
||||
if ($this->rootComponent) {
|
||||
$this->rootComponent->handleMouseClick($this->mouseX, $this->mouseY, $button);
|
||||
}
|
||||
break;
|
||||
|
||||
case RGFW_mouseButtonReleased:
|
||||
$button = $event['button'] ?? 0;
|
||||
|
||||
// Propagate release to root component
|
||||
if ($this->rootComponent) {
|
||||
$this->rootComponent->handleMouseRelease($this->mouseX, $this->mouseY, $button);
|
||||
}
|
||||
break;
|
||||
|
||||
case RGFW_mouseScroll:
|
||||
$deltaY = $event[1] ?? 0;
|
||||
|
||||
// Propagate wheel to root component
|
||||
if ($this->rootComponent) {
|
||||
$this->rootComponent->handleMouseWheel($this->mouseX, $this->mouseY, $deltaY);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update window state
|
||||
*/
|
||||
public function update(): void
|
||||
{
|
||||
if ($this->rootComponent) {
|
||||
$this->rootComponent->update();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Layout components
|
||||
*/
|
||||
public function layout(): void
|
||||
{
|
||||
if ($this->rootComponent && $this->shouldBeReLayouted) {
|
||||
$this->rootComponent->setViewport($this->viewport);
|
||||
$this->rootComponent->setWindow($this->window);
|
||||
$this->rootComponent->layout($this->textRenderer);
|
||||
$this->shouldBeReLayouted = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the window
|
||||
*/
|
||||
public function render(): void
|
||||
{
|
||||
rsgl_clear($this->window, 255, 255, 255, 0);
|
||||
|
||||
// Render root component tree
|
||||
if ($this->rootComponent) {
|
||||
$this->rootComponent->render($this->textRenderer);
|
||||
$this->rootComponent->renderContent($this->textRenderer);
|
||||
}
|
||||
|
||||
// Render to screen
|
||||
rsgl_render($this->window);
|
||||
rgfw_window_swapBuffers($this->window);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up resources
|
||||
*/
|
||||
public function cleanup(): void
|
||||
{
|
||||
if ($this->textRenderer) {
|
||||
$this->textRenderer->free();
|
||||
}
|
||||
rsgl_close($this->window);
|
||||
rgfw_window_close($this->window);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user