sdl3/src/Ui/Component.php
2025-10-23 08:56:22 +02:00

220 lines
6.8 KiB
PHP

<?php
namespace PHPNative\Ui;
use PHPNative\Framework\TextRenderer;
use PHPNative\Tailwind\Style\Margin;
use PHPNative\Tailwind\Style\MediaQueryEnum;
use PHPNative\Tailwind\Style\Padding;
use PHPNative\Tailwind\Style\StateEnum;
use PHPNative\Tailwind\StyleParser;
abstract class Component
{
protected $children = [];
protected $window;
protected $pixelRatio;
protected string $styles = '';
protected bool $visible = true;
protected Viewport $viewport;
protected array $computedStyles = [];
protected Viewport $contentViewport;
public function setViewport(Viewport $viewport): void
{
$this->viewport = $viewport;
}
public function getViewport(): Viewport
{
return $this->viewport;
}
public function getContentViewport(): Viewport
{
return $this->contentViewport;
}
public function setContentViewport(Viewport $viewport): void
{
$this->contentViewport = $viewport;
}
/**
* Apply offset to viewport and all child viewports recursively
*/
public function applyScrollOffset(int $offsetX, int $offsetY): void
{
// Offset this component's viewports
$this->viewport->x = (int) ($this->viewport->x - $offsetX);
$this->viewport->y = (int) ($this->viewport->y - $offsetY);
$this->contentViewport->x = (int) ($this->contentViewport->x - $offsetX);
$this->contentViewport->y = (int) ($this->contentViewport->y - $offsetY);
// Recursively apply to all children
foreach ($this->children as $child) {
$child->applyScrollOffset($offsetX, $offsetY);
}
}
public function setWindow($window): void
{
$this->window = $window;
}
public function setPixelRatio($pixelRatio): void
{
$this->pixelRatio = $pixelRatio;
}
public function update(): void
{
foreach ($this->children as $child) {
$child->update();
}
}
public function layout(null|TextRenderer $textRenderer = null): void
{
$this->computedStyles = StyleParser::parse($this->style)->getValidStyles(
MediaQueryEnum::normal,
StateEnum::normal,
);
if (isset($this->computedStyles[Margin::class]) && ($m = $this->computedStyles[Margin::class])) {
$this->viewport->x = (int) ($this->viewport->x + $m->left);
$this->viewport->width = max(0, ($this->viewport->width - $m->right) - $m->left);
$this->viewport->y = (int) ($this->viewport->y + $m->top);
$this->viewport->height = max(0, ($this->viewport->height - $m->bottom) - $m->top);
}
$this->contentViewport = clone $this->viewport;
if (isset($this->computedStyles[Padding::class]) && ($p = $this->computedStyles[Padding::class])) {
$this->contentViewport->x = (int) ($this->contentViewport->x + $p->left);
$this->contentViewport->width = max(0, ($this->contentViewport->width - $p->right) - $p->left);
$this->contentViewport->y = (int) ($this->contentViewport->y + $p->top);
$this->contentViewport->height = max(0, ($this->contentViewport->height - $p->bottom) - $p->top);
}
}
public function render(null|TextRenderer $textRenderer = null): void
{
if (!$this->visible) {
return;
}
if (
isset($this->computedStyles[\PHPNative\Tailwind\Style\Background::class]) &&
($bg = $this->computedStyles[\PHPNative\Tailwind\Style\Background::class])
) {
rsgl_setColor($this->window, $bg->color->red, $bg->color->green, $bg->color->blue, $bg->color->alpha);
if (
isset($this->computedStyles[\PHPNative\Tailwind\Style\Border::class]) &&
($border = $this->computedStyles[\PHPNative\Tailwind\Style\Border::class])
) {
rsgl_drawRoundRectExF(
$this->window,
$this->viewport->x,
$this->viewport->y,
$this->viewport->width,
$this->viewport->height,
$border->roundTopLeft ?? 0,
$border->roundTopLeft ?? 0,
$border->roundTopRight ?? 0,
$border->roundTopRight ?? 0,
$border->roundBottomLeft ?? 0,
$border->roundBottomLeft ?? 0,
$border->roundBottomRight ?? 0,
$border->roundBottomRight ?? 0,
);
} else {
rsgl_drawRectF(
$this->window,
$this->viewport->x,
$this->viewport->y,
$this->viewport->width,
$this->viewport->height,
);
}
}
}
public function renderContent(null|TextRenderer $textRenderer = null): void
{
if (!$this->visible) {
return;
}
// Render children
foreach ($this->children as $child) {
$child->setWindow($this->window);
$child->render($textRenderer);
$child->renderContent($textRenderer);
}
}
public function addComponent(Component $component): void
{
$this->children[] = $component;
}
/**
* Handle mouse click event
* @return bool True if event was handled
*/
public function handleMouseClick(float $mouseX, float $mouseY, int $button): bool
{
// Default implementation: propagate to children
foreach ($this->children as $child) {
if ($child->handleMouseClick($mouseX, $mouseY, $button)) {
return true;
}
}
return false;
}
/**
* Handle mouse move event
*/
public function handleMouseMove(float $mouseX, float $mouseY): void
{
// Default implementation: propagate to children
foreach ($this->children as $child) {
$child->handleMouseMove($mouseX, $mouseY);
}
}
/**
* Handle mouse button release event
*/
public function handleMouseRelease(float $mouseX, float $mouseY, int $button): void
{
// Default implementation: propagate to children
foreach ($this->children as $child) {
$child->handleMouseRelease($mouseX, $mouseY, $button);
}
}
/**
* Handle mouse wheel event
* @return bool True if event was handled
*/
public function handleMouseWheel(float $mouseX, float $mouseY, float $deltaY): bool
{
// Default implementation: propagate to children
foreach ($this->children as $child) {
if ($child->handleMouseWheel($mouseX, $mouseY, $deltaY)) {
return true;
}
}
return false;
}
}