220 lines
6.8 KiB
PHP
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;
|
|
}
|
|
}
|