From 9f2b88e0eb4107928aaff2b44522a2b05d145df4 Mon Sep 17 00:00:00 2001 From: Thomas Peterson Date: Thu, 23 Oct 2025 10:14:41 +0200 Subject: [PATCH] State --- examples/ButtonExample.php | 2 +- examples/SimpleButtonExample.php | 26 ++++++++++++++++++++ src/Ui/Component.php | 32 ++++++++++++++++++++---- src/Ui/Widget/Button.php | 3 +-- src/Ui/Widget/Container.php | 42 ++++++++++++++++++-------------- 5 files changed, 79 insertions(+), 26 deletions(-) create mode 100644 examples/SimpleButtonExample.php diff --git a/examples/ButtonExample.php b/examples/ButtonExample.php index a3712f9..5d0c9b9 100644 --- a/examples/ButtonExample.php +++ b/examples/ButtonExample.php @@ -34,7 +34,7 @@ $mainContainer->addComponent($button1); // Button with different padding $button2 = new Button( text: 'Another Button', - style: 'm-5 p-15 bg-green-500 rounded-lg', + style: 'm-5 p-15 bg-green-500 hover:bg-green-200 rounded-lg', onClick: function () { echo 'test2'; }, diff --git a/examples/SimpleButtonExample.php b/examples/SimpleButtonExample.php new file mode 100644 index 0000000..161b277 --- /dev/null +++ b/examples/SimpleButtonExample.php @@ -0,0 +1,26 @@ +setRoot($button2); +$app->run(); diff --git a/src/Ui/Component.php b/src/Ui/Component.php index 4ede79f..10af138 100644 --- a/src/Ui/Component.php +++ b/src/Ui/Component.php @@ -16,15 +16,19 @@ abstract class Component protected $window; protected $pixelRatio; - protected string $styles = ''; - protected bool $visible = true; + protected StateEnum $currentState = StateEnum::normal; + protected Viewport $viewport; protected array $computedStyles = []; protected Viewport $contentViewport; + public function __construct( + protected string $style = '', + ) {} + public function setViewport(Viewport $viewport): void { $this->viewport = $viewport; @@ -83,9 +87,8 @@ abstract class Component { $this->computedStyles = StyleParser::parse($this->style)->getValidStyles( MediaQueryEnum::normal, - StateEnum::normal, + $this->currentState, ); - 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); @@ -185,7 +188,26 @@ abstract class Component */ public function handleMouseMove(float $mouseX, float $mouseY): void { - // Default implementation: propagate to children + // Check if mouse is over this component + $isMouseOver = + $mouseX >= $this->viewport->x && + $mouseX <= ($this->viewport->x + $this->viewport->width) && + $mouseY >= $this->viewport->y && + $mouseY <= ($this->viewport->y + $this->viewport->height); + + // Update state based on mouse position + $previousState = $this->currentState; + $this->currentState = $isMouseOver ? StateEnum::hover : StateEnum::normal; + + // Recompute styles if state changed + if ($previousState !== $this->currentState) { + $this->computedStyles = StyleParser::parse($this->style)->getValidStyles( + MediaQueryEnum::normal, + $this->currentState, + ); + } + + // Propagate to children foreach ($this->children as $child) { $child->handleMouseMove($mouseX, $mouseY); } diff --git a/src/Ui/Widget/Button.php b/src/Ui/Widget/Button.php index fcc359e..0d02620 100644 --- a/src/Ui/Widget/Button.php +++ b/src/Ui/Widget/Button.php @@ -11,7 +11,7 @@ class Button extends Container public function __construct( public string $text = '', - public string $style = '', + string $style = '', null|callable $onClick = null, ) { parent::__construct($style); @@ -57,7 +57,6 @@ class Button extends Container } return true; } - // Propagate to parent if click was outside button return parent::handleMouseClick($mouseX, $mouseY, $button); } diff --git a/src/Ui/Widget/Container.php b/src/Ui/Widget/Container.php index 5e49c79..205488f 100644 --- a/src/Ui/Widget/Container.php +++ b/src/Ui/Widget/Container.php @@ -33,9 +33,10 @@ class Container extends Component private const SCROLLBAR_WIDTH = 12; private const SCROLLBAR_MIN_SIZE = 20; - public function __construct( - public string $style = '', - ) {} + public function __construct(string $style = '') + { + parent::__construct($style); + } public function layout(null|TextRenderer $textRenderer = null): void { @@ -47,14 +48,18 @@ class Container extends Component // Check if overflow is set (if yes, container should not auto-expand) $overflow = $this->computedStyles[\PHPNative\Tailwind\Style\Overflow::class] ?? null; - $hasOverflowX = $overflow && in_array($overflow->x, [ - \PHPNative\Tailwind\Style\OverflowEnum::scroll, - \PHPNative\Tailwind\Style\OverflowEnum::auto, - ]); - $hasOverflowY = $overflow && in_array($overflow->y, [ - \PHPNative\Tailwind\Style\OverflowEnum::scroll, - \PHPNative\Tailwind\Style\OverflowEnum::auto, - ]); + $hasOverflowX = + $overflow && + in_array($overflow->x, [ + \PHPNative\Tailwind\Style\OverflowEnum::scroll, + \PHPNative\Tailwind\Style\OverflowEnum::auto, + ]); + $hasOverflowY = + $overflow && + in_array($overflow->y, [ + \PHPNative\Tailwind\Style\OverflowEnum::scroll, + \PHPNative\Tailwind\Style\OverflowEnum::auto, + ]); // A container is a flex container if it has flex/flex-row/flex-col in style // (but not flex-1, flex-auto, flex-none which make it a flex child) @@ -93,7 +98,7 @@ class Container extends Component width: $this->contentViewport->width, height: $this->contentViewport->height, windowWidth: $this->contentViewport->windowWidth, - windowHeight: $this->contentViewport->windowHeight + windowHeight: $this->contentViewport->windowHeight, ); $child->setViewport($childViewport); @@ -200,7 +205,7 @@ class Container extends Component width: $size, height: $this->contentViewport->height, windowWidth: $this->contentViewport->windowWidth, - windowHeight: $this->contentViewport->windowHeight + windowHeight: $this->contentViewport->windowHeight, ); $currentPosition += $size; } else { @@ -211,7 +216,7 @@ class Container extends Component width: $this->contentViewport->width, height: $size, windowWidth: $this->contentViewport->windowWidth, - windowHeight: $this->contentViewport->windowHeight + windowHeight: $this->contentViewport->windowHeight, ); $currentPosition += $size; } @@ -324,10 +329,10 @@ class Container extends Component // Performance optimization: skip completely invisible children $childViewport = $child->getViewport(); $isVisible = - $childViewport->x + $childViewport->width > $scissorX && - $childViewport->x < $scissorX + $scissorW && - $childViewport->y + $childViewport->height > $scissorY && - $childViewport->y < $scissorY + $scissorH; + ($childViewport->x + $childViewport->width) > $scissorX && + $childViewport->x < ($scissorX + $scissorW) && + ($childViewport->y + $childViewport->height) > $scissorY && + $childViewport->y < ($scissorY + $scissorH); if ($isVisible) { // Render - batches draw calls @@ -489,6 +494,7 @@ class Container extends Component public function handleMouseMove(float $mouseX, float $mouseY): void { + parent::handleMouseMove($mouseX, $mouseY); if ($this->isDraggingScrollbarY) { $deltaY = $mouseY - $this->dragStartY; $scrollbarHeight = $this->contentViewport->height;