diff --git a/assets/symbol.ttf b/assets/symbol.ttf new file mode 100644 index 0000000..0ba7200 Binary files /dev/null and b/assets/symbol.ttf differ diff --git a/composer.json b/composer.json index d8d748f..05c2bc1 100644 --- a/composer.json +++ b/composer.json @@ -11,7 +11,9 @@ "require": { "ext-parallel": "*", "ext-sdl": "*", - "php": "^8.3" + "php": "^8.3", + "symfony/serializer": "^7", + "zumba/json-serializer": "^3" }, "require-dev": { "friendsofphp/php-cs-fixer": "^3.21", @@ -28,6 +30,7 @@ "phpnative/event": "self.version", "phpnative/framework": "self.version", "phpnative/renderer": "self.version", + "phpnative/storage": "self.version", "phpnative/support": "self.version", "phpnative/tailwind": "self.version", "phpnative/ui": "self.version" @@ -39,6 +42,7 @@ "PHPNative\\Event\\": "src/PHPNative/Event/src", "PHPNative\\Framework\\": "src/PHPNative/Framework/src", "PHPNative\\Renderer\\": "src/PHPNative/Renderer/src", + "PHPNative\\Storage\\": "src/PHPNative/Storage/src", "PHPNative\\Support\\": "src/PHPNative/Support/src", "PHPNative\\Tailwind\\": "src/PHPNative/Tailwind/src", "PHPNative\\UI\\": "src/PHPNative/UI/src" @@ -51,6 +55,7 @@ "PHPNative\\Event\\Tests\\": "src/PHPNative/Event/tests", "PHPNative\\Framework\\Tests\\": "src/PHPNative/Framework/tests", "PHPNative\\Renderer\\Tests\\": "src/PHPNative/Renderer/tests", + "PHPNative\\Storage\\Tests\\": "src/PHPNative/Storage/tests", "PHPNative\\Support\\Tests\\": "src/PHPNative/Support/tests", "PHPNative\\Tailwind\\Tests\\": "src/PHPNative/Tailwind/tests", "PHPNative\\UI\\Tests\\": "src/PHPNative/UI/tests" diff --git a/src/PHPNative/Core/src/Collection.php b/src/PHPNative/Core/src/Collection.php index 0e06654..722faa8 100644 --- a/src/PHPNative/Core/src/Collection.php +++ b/src/PHPNative/Core/src/Collection.php @@ -119,5 +119,10 @@ abstract class Collection implements IteratorAggregate $this->elements = $temp->items(); } + + public function clear(): void + { + $this->elements = []; + } } \ No newline at end of file diff --git a/src/PHPNative/Event/src/Driver.php b/src/PHPNative/Event/src/Driver.php index de665d8..06af576 100644 --- a/src/PHPNative/Event/src/Driver.php +++ b/src/PHPNative/Event/src/Driver.php @@ -12,11 +12,17 @@ class Driver public function pollEvent(): Event { \SDL_PollEvent($this->event); + if($this->event->type == \SDL_EVENT_KEY_UP) { + var_dump($this->event->key->key); + } return match($this->event->type) { \SDL_EVENT_QUIT => new \PHPNative\Event\SystemEvent(EventType::QUIT), - \SDL_EVENT_MOUSE_BUTTON_DOWN => new MouseDown(EventType::MOUSEBUTTON_DOWN, $this->event->button->x, $this->event->button->y ), - \SDL_EVENT_MOUSE_BUTTON_UP => new MouseUp(EventType::MOUSEBUTTON_UP, $this->event->button->x, $this->event->button->y ), - \SDL_EVENT_MOUSE_MOTION => new MouseMove(EventType::MOUSEMOVE, $this->event->motion->x, $this->event->motion->y ), + \SDL_EVENT_WINDOW_CLOSE_REQUESTED => new \PHPNative\Event\SystemEvent(EventType::WINDOW_CLOSE, $this->event->window->windowID), + \SDL_EVENT_KEY_UP => new \PHPNative\Event\KeyUp(EventType::KEYUP, $this->event->window->windowID, Key::tryFrom($this->event->key->key)), + \SDL_EVENT_TEXT_INPUT => new \PHPNative\Event\TextInput(EventType::TEXTINPUT, $this->event->window->windowID, mb_convert_encoding($this->event->text->text, 'ISO-8859-1', 'UTF-8')), + \SDL_EVENT_MOUSE_BUTTON_DOWN => new MouseDown(EventType::MOUSEBUTTON_DOWN, $this->event->window->windowID, $this->event->button->x, $this->event->button->y ), + \SDL_EVENT_MOUSE_BUTTON_UP => new MouseUp(EventType::MOUSEBUTTON_UP, $this->event->window->windowID, $this->event->button->x, $this->event->button->y ), + \SDL_EVENT_MOUSE_MOTION => new MouseMove(EventType::MOUSEMOVE, $this->event->window->windowID, $this->event->motion->x, $this->event->motion->y ), default => new \PHPNative\Event\SystemEvent(EventType::NOOP) }; } diff --git a/src/PHPNative/Event/src/EventType.php b/src/PHPNative/Event/src/EventType.php index 7790307..83ce7e7 100644 --- a/src/PHPNative/Event/src/EventType.php +++ b/src/PHPNative/Event/src/EventType.php @@ -6,7 +6,6 @@ enum EventType: int { case NOOP = 0; case QUIT = 1; - case CLOSE_WINDOW = 2; case WINDOW_FOCUS_LOST = 1000; case WINDOW_FOCUS_GAINED = 1001; case WINDOW_RESIZED = 1002; diff --git a/src/PHPNative/Event/src/Key.php b/src/PHPNative/Event/src/Key.php new file mode 100644 index 0000000..d6452b5 --- /dev/null +++ b/src/PHPNative/Event/src/Key.php @@ -0,0 +1,15 @@ +type; + } + +} \ No newline at end of file diff --git a/src/PHPNative/Event/src/MouseDown.php b/src/PHPNative/Event/src/MouseDown.php index 319f6d2..bfdf0cc 100644 --- a/src/PHPNative/Event/src/MouseDown.php +++ b/src/PHPNative/Event/src/MouseDown.php @@ -5,7 +5,7 @@ namespace PHPNative\Event; class MouseDown implements Event { - public function __construct(public EventType $type = EventType::MOUSEBUTTON_DOWN, public int $x = 0, public int $y = 0) + public function __construct(public EventType $type = EventType::MOUSEBUTTON_DOWN, public int $windowId = 0, public int $x = 0, public int $y = 0) { } diff --git a/src/PHPNative/Event/src/MouseMove.php b/src/PHPNative/Event/src/MouseMove.php index a3f26ff..145278d 100644 --- a/src/PHPNative/Event/src/MouseMove.php +++ b/src/PHPNative/Event/src/MouseMove.php @@ -5,7 +5,7 @@ namespace PHPNative\Event; class MouseMove implements Event { - public function __construct(public EventType $type = EventType::MOUSEMOVE, public int $x = 0, public int $y = 0) + public function __construct(public EventType $type = EventType::MOUSEMOVE, public int $windowId = 0, public int $x = 0, public int $y = 0) { } diff --git a/src/PHPNative/Event/src/MouseUp.php b/src/PHPNative/Event/src/MouseUp.php index f0072f4..533c733 100644 --- a/src/PHPNative/Event/src/MouseUp.php +++ b/src/PHPNative/Event/src/MouseUp.php @@ -5,7 +5,7 @@ namespace PHPNative\Event; class MouseUp implements Event { - public function __construct(public EventType $type = EventType::MOUSEBUTTON_UP, public int $x = 0, public int $y = 0) + public function __construct(public EventType $type = EventType::MOUSEBUTTON_UP, public int $windowId = 0, public int $x = 0, public int $y = 0) { } diff --git a/src/PHPNative/Event/src/SystemEvent.php b/src/PHPNative/Event/src/SystemEvent.php index 01eec2f..6ab2202 100644 --- a/src/PHPNative/Event/src/SystemEvent.php +++ b/src/PHPNative/Event/src/SystemEvent.php @@ -5,7 +5,7 @@ namespace PHPNative\Event; class SystemEvent implements Event { - public function __construct(public EventType $type = EventType::NOOP) + public function __construct(public EventType $type = EventType::NOOP, public int $windowId = 0) { } diff --git a/src/PHPNative/Event/src/TextInput.php b/src/PHPNative/Event/src/TextInput.php new file mode 100644 index 0000000..1ac9e5a --- /dev/null +++ b/src/PHPNative/Event/src/TextInput.php @@ -0,0 +1,17 @@ +type; + } + +} \ No newline at end of file diff --git a/src/PHPNative/Framework/src/Application/Application.php b/src/PHPNative/Framework/src/Application/Application.php index d674784..a94d693 100644 --- a/src/PHPNative/Framework/src/Application/Application.php +++ b/src/PHPNative/Framework/src/Application/Application.php @@ -5,6 +5,6 @@ namespace PHPNative\Framework\Application; interface Application { - public function run(string|null $start): void; + public function run(): void; } \ No newline at end of file diff --git a/src/PHPNative/Framework/src/Application/Config.php b/src/PHPNative/Framework/src/Application/Config.php new file mode 100644 index 0000000..b82f92b --- /dev/null +++ b/src/PHPNative/Framework/src/Application/Config.php @@ -0,0 +1,7 @@ +register(App::class, function () use ($app, $container) { + return $container->get($app); + }); $application = $container->get(Gui::class); return $application; } - public function run(string|null $start): void + public function run(): void { try { + $application = $this->container->get(App::class); $lifeCycle = $this->container->get(Lifecycle::class); try { - $lifeCycle->show($this->container->get($start)); + $lifeCycle->show($this->container->get($application->getStartWindow())); $lifeCycle->run(); } catch (ArgumentCountError $e) { var_dump($e->getMessage()); diff --git a/src/PHPNative/Framework/src/Application/Kernel.php b/src/PHPNative/Framework/src/Application/Kernel.php index 06ebbc9..795cf67 100644 --- a/src/PHPNative/Framework/src/Application/Kernel.php +++ b/src/PHPNative/Framework/src/Application/Kernel.php @@ -6,9 +6,9 @@ namespace PHPNative\Framework\Application; use PHPNative\Container\Container; use PHPNative\Container\GenericContainer; -use PHPNative\Framework\Discovery\DiscoveryLocationBootstrap; use PHPNative\Framework\Discovery\LoadDiscoveryClasses; use PHPNative\Framework\Discovery\LoadDiscoveryLocations; +use Zumba\JsonSerializer\JsonSerializer; final class Kernel { @@ -34,6 +34,9 @@ final class Kernel private function registerKernel(): self { $this->container->singleton(self::class, $this); + $this->container->register(JsonSerializer::class, function () { + return new JsonSerializer(); + }); return $this; } diff --git a/src/PHPNative/Framework/src/Lifecycle/Context.php b/src/PHPNative/Framework/src/Lifecycle/Context.php index 4d636a6..f7206ef 100644 --- a/src/PHPNative/Framework/src/Lifecycle/Context.php +++ b/src/PHPNative/Framework/src/Lifecycle/Context.php @@ -2,6 +2,7 @@ namespace PHPNative\Framework\Lifecycle; +use Closure; use PHPNative\Event\Event; use PHPNative\Framework\Application\Window; use PHPNative\Renderer\Thread; @@ -9,7 +10,7 @@ use PHPNative\Renderer\Thread; class Context { - public function __construct(private Thread $thread) + public function __construct(public Thread $thread, public ?Closure $onClose = null) { } @@ -26,7 +27,9 @@ class Context public function event(Event $event): void { - $this->thread->addEvent($event); + if($event->windowId == $this->thread->windowId) { + $this->thread->addEvent($event); + } } public function render(float $delta): void @@ -36,7 +39,10 @@ class Context public function unload(): void { - //TODO: unload + if($this->onClose) { + ($this->onClose)(); + } + $this->thread->close(); } } \ No newline at end of file diff --git a/src/PHPNative/Framework/src/Lifecycle/ContextCollection.php b/src/PHPNative/Framework/src/Lifecycle/ContextCollection.php index 70892ed..7ab96ba 100644 --- a/src/PHPNative/Framework/src/Lifecycle/ContextCollection.php +++ b/src/PHPNative/Framework/src/Lifecycle/ContextCollection.php @@ -12,4 +12,15 @@ class ContextCollection extends TypedCollection { return Context::class; } + + public function removeWindowFrom(string $windowId): void + { + $temp = $this->filter(function ($value, $key) use($windowId){ + if($value->thread->windowId == $windowId) { + $value->unload(); + } + return $value->thread->windowId != $windowId; + }); + $this->setElements($temp->items()); + } } \ No newline at end of file diff --git a/src/PHPNative/Framework/src/Lifecycle/Lifecycle.php b/src/PHPNative/Framework/src/Lifecycle/Lifecycle.php index 71f8bed..ff55033 100644 --- a/src/PHPNative/Framework/src/Lifecycle/Lifecycle.php +++ b/src/PHPNative/Framework/src/Lifecycle/Lifecycle.php @@ -3,6 +3,7 @@ namespace PHPNative\Framework\Lifecycle; use PHPNative\Container\Container; +use PHPNative\Container\Singleton; use PHPNative\Event\Event; use PHPNative\Event\EventType; use PHPNative\Framework\Application\Window; @@ -10,6 +11,7 @@ use PHPNative\Framework\Loop\OrderedEventLoop; use PHPNative\Framework\Loop\WorkerInterface; use PHPNative\Renderer\Thread; +#[Singleton] class Lifecycle implements WorkerInterface { public function __construct( @@ -20,14 +22,15 @@ class Lifecycle implements WorkerInterface $this->loop->use($this); } - public function show(Window $window, array $arguments = []): void + public function show(Window $window, array $arguments = [], ?callable $onClose = null): void { $context = $this->container->get( Context::class, - thread: $this->container->get(Thread::class) + null ); + $context->onClose = $onClose; $context->show($window); $this->contextCollection->add($context); @@ -45,7 +48,9 @@ class Lifecycle implements WorkerInterface public function onRender(float $delta): void { - $this->contextCollection->map(fn(Context $context) => $context->render($delta)); + $this->contextCollection->map( + fn(Context $context) => $context->render($delta) + ); } public function onEvent( $event): void @@ -60,6 +65,9 @@ class Lifecycle implements WorkerInterface case EventType::WINDOW_FOCUS_LOST: $this->loop->pause(); break; + case EventType::WINDOW_CLOSE: + $this->closeWindow($event->windowId); + break; case EventType::WINDOW_FOCUS_GAINED: $this->loop->resume(); break; @@ -67,6 +75,7 @@ class Lifecycle implements WorkerInterface $this->contextCollection->map(fn(Context $context) => $context->unload()); $this->contextCollection = new ContextCollection(); $this->loop->stop(); + \SDL_Quit(); break; } } @@ -84,4 +93,13 @@ class Lifecycle implements WorkerInterface $this->context->resume(); } } + + private function closeWindow($windowId) + { + if($this->contextCollection->count() == 1) { + return; + } + + $this->contextCollection->removeWindowFrom($windowId); + } } \ No newline at end of file diff --git a/src/PHPNative/Framework/src/PHPNative.php b/src/PHPNative/Framework/src/PHPNative.php index 1442966..1c3e201 100644 --- a/src/PHPNative/Framework/src/PHPNative.php +++ b/src/PHPNative/Framework/src/PHPNative.php @@ -15,6 +15,8 @@ final class PHPNative ): Container { $root ??= getcwd(); + \SDL_Init(\SDL_INIT_VIDEO); + \SDL_TTF_Init(); // Kernel return (new Kernel( root: $root, diff --git a/src/PHPNative/Renderer/src/Cache/Image.php b/src/PHPNative/Renderer/src/Cache/Image.php new file mode 100644 index 0000000..83f1b82 --- /dev/null +++ b/src/PHPNative/Renderer/src/Cache/Image.php @@ -0,0 +1,8 @@ +window = $window; - \SDL_Init(\SDL_INIT_VIDEO); - \SDL_TTF_Init(); - $this->windowId = \SDL_CreateWindow($this->window->getTitle(), 800, 600, \SDL_WINDOW_HIGH_PIXEL_DENSITY); - $this->rendererPtr = \SDL_CreateRenderer($this->windowId); + $this->windowPtr = \SDL_CreateWindow($this->window->getTitle(), 1000, 600, \SDL_WINDOW_HIGH_PIXEL_DENSITY); + $this->rendererPtr = \SDL_CreateRenderer($this->windowPtr); + $this->windowId = \SDL_GetWindowID($this->windowPtr); } public function close(): void { + $this->shouldRenderd = false; \SDL_DestroyRenderer($this->rendererPtr); - \SDL_DestroyWindow($this->windowId); - \SDL_Quit(); + \SDL_DestroyWindow($this->windowPtr); + } public function render(): void { + if(!$this->shouldRenderd) { + return; + } $windowWidth = 0; $windowHeight = 0; - \SDL_GetWindowSize($this->windowId, $windowWidth, $windowHeight); + \SDL_GetWindowSize($this->windowPtr, $windowWidth, $windowHeight); $viewPort = new Viewport( windowId: $this->windowId, + windowPtr: $this->windowPtr, renderPtr: $this->rendererPtr, x:0, y: 0, width: $windowWidth, height: $windowHeight, windowWidth: $windowWidth, windowHeight: $windowHeight, @@ -74,6 +81,7 @@ class Thread if($item->clipAt) { $item->renderAt->w = $item->clipAt->w; } + \SDL_RenderTexture($this->rendererPtr, $item->texture, $item->clipAt?? null , $item->renderAt); }); diff --git a/src/PHPNative/Renderer/src/Viewport.php b/src/PHPNative/Renderer/src/Viewport.php index aa0eca3..b5fc0ce 100644 --- a/src/PHPNative/Renderer/src/Viewport.php +++ b/src/PHPNative/Renderer/src/Viewport.php @@ -9,6 +9,7 @@ class Viewport { public function __construct(public $windowId, + public $windowPtr, public $renderPtr, public int $x = 0, public int $y = 0, @@ -16,6 +17,8 @@ class Viewport public $height = 0, public $windowWidth = 0, public $windowHeight = 0, + public $addX = 0, + public $addY = 0, public MediaQueryEnum $windowMediaQuery = MediaQueryEnum::normal) { } diff --git a/src/PHPNative/Renderer/src/Visuals/Background.php b/src/PHPNative/Renderer/src/Visuals/Background.php index 62c3bd3..0f5927b 100644 --- a/src/PHPNative/Renderer/src/Visuals/Background.php +++ b/src/PHPNative/Renderer/src/Visuals/Background.php @@ -2,26 +2,37 @@ namespace PHPNative\Renderer\Visuals; +use PHPNative\Renderer\GFX; use PHPNative\Renderer\Item; use PHPNative\Renderer\Thread; use PHPNative\Renderer\Viewport; class Background { - public static function render(string $widgetId, Thread $thread, array $styles, Viewport $targetViewport): void + public static function render(string $widgetId, Thread $thread, array $styles, Viewport $targetViewport, int $index = 0): void { if(isset($styles[\PHPNative\Tailwind\Style\Background::class]) && $bg = $styles[\PHPNative\Tailwind\Style\Background::class]) { $texture = \SDL_CreateTexture($targetViewport->renderPtr, \SDL_PIXELFORMAT_RGBA8888, \SDL_TEXTUREACCESS_TARGET, $targetViewport->width, $targetViewport->height); \SDL_SetRenderTarget($targetViewport->renderPtr, $texture); - \SDL_SetRenderDrawColor($targetViewport->renderPtr, 255, 255, 255, 255); + \SDL_SetRenderDrawColor($targetViewport->renderPtr, 0, 0, 0, 0); \SDL_RenderClear($targetViewport->renderPtr); - $rect = new \SDL_FRect(0, 0, $targetViewport->width, $targetViewport->height); - \SDL_SetRenderDrawColor($targetViewport->renderPtr, $bg->color->red, $bg->color->green, $bg->color->blue, $bg->color->alpha); - \SDL_RenderFillRect($targetViewport->renderPtr, $rect); - $thread->addToRenderStack(new Item($widgetId, $texture, new \SDL_FRect($targetViewport->x, $targetViewport->y, $targetViewport->width, $targetViewport->height), 2)); + if(isset($styles[\PHPNative\Tailwind\Style\Border::class]) && $border = $styles[\PHPNative\Tailwind\Style\Border::class]) { + if($border->roundTop > 0) { + GFX::roundedBoxRGBA($targetViewport->renderPtr, 0, 0, + $targetViewport->width, $targetViewport->height, $border->roundTop, $bg->color->red, $bg->color->green, $bg->color->blue, $bg->color->alpha); + }else{ + GFX::boxRGBA($targetViewport->renderPtr, 0, 0, + $targetViewport->width, $targetViewport->height, $bg->color->red, $bg->color->green, $bg->color->blue, $bg->color->alpha); + } + }else{ + GFX::boxRGBA($targetViewport->renderPtr, 0, 0, + $targetViewport->width, $targetViewport->height, $bg->color->red, $bg->color->green, $bg->color->blue, $bg->color->alpha); + } + + $thread->addToRenderStack(new Item($widgetId, $texture, new \SDL_FRect($targetViewport->x, $targetViewport->y, $targetViewport->width, $targetViewport->height), $index)); } } } \ No newline at end of file diff --git a/src/PHPNative/Renderer/src/Visuals/Border.php b/src/PHPNative/Renderer/src/Visuals/Border.php index d8e78d8..75faaf8 100644 --- a/src/PHPNative/Renderer/src/Visuals/Border.php +++ b/src/PHPNative/Renderer/src/Visuals/Border.php @@ -10,7 +10,7 @@ use PHPNative\Renderer\Viewport; class Border { - public static function render(string $widgetId, Thread $thread, array $styles, Viewport $targetViewport): void + public static function render(string $widgetId, Thread $thread, array $styles, Viewport $targetViewport, int $index): void { if(isset($styles[\PHPNative\Tailwind\Style\Border::class]) && $b = $styles[\PHPNative\Tailwind\Style\Border::class]) { $texture = \SDL_CreateTexture($targetViewport->renderPtr, \SDL_PIXELFORMAT_RGBA8888, \SDL_TEXTUREACCESS_TARGET, $targetViewport->width, $targetViewport->height); @@ -38,7 +38,7 @@ class Border GFX::boxRGBA($targetViewport->renderPtr, $targetViewport->width - $b->right, 0, $targetViewport->width, $targetViewport->height, $b->color->red, $b->color->green, $b->color->blue, $b->color->alpha); } - $thread->addToRenderStack(new Item($widgetId, $texture, new \SDL_FRect($targetViewport->x, $targetViewport->y, $targetViewport->width, $targetViewport->height), 10)); + $thread->addToRenderStack(new Item($widgetId, $texture, new \SDL_FRect($targetViewport->x, $targetViewport->y, $targetViewport->width, $targetViewport->height), $index)); } } diff --git a/src/PHPNative/Renderer/src/Visuals/Cursor.php b/src/PHPNative/Renderer/src/Visuals/Cursor.php new file mode 100644 index 0000000..a61a459 --- /dev/null +++ b/src/PHPNative/Renderer/src/Visuals/Cursor.php @@ -0,0 +1,23 @@ +renderPtr, \SDL_PIXELFORMAT_RGBA8888, \SDL_TEXTUREACCESS_TARGET, 4, $targetViewport->height); + \SDL_SetRenderTarget($targetViewport->renderPtr, $texture); + \SDL_SetRenderDrawColor($targetViewport->renderPtr, 0, 0, 0, 255); + \SDL_RenderClear($targetViewport->renderPtr); + + GFX::boxRGBA($targetViewport->renderPtr, 0, 0, 4, $targetViewport->height, 128,128,128,255); + + $thread->addToRenderStack(new Item($widgetId, $texture, new \SDL_FRect($targetViewport->x, $targetViewport->y, 4, $targetViewport->height), $index)); + } +} \ No newline at end of file diff --git a/src/PHPNative/Renderer/src/Visuals/Visuals.php b/src/PHPNative/Renderer/src/Visuals/Visuals.php index c57309d..8926553 100644 --- a/src/PHPNative/Renderer/src/Visuals/Visuals.php +++ b/src/PHPNative/Renderer/src/Visuals/Visuals.php @@ -7,10 +7,10 @@ use PHPNative\Renderer\Viewport; class Visuals { - public static function render(string $widgetId, Thread $thread, array $styles, Viewport $targetViewport): void + public static function render(string $widgetId, Thread $thread, array $styles, Viewport $targetViewport, int $index = 0): void { - Background::render($widgetId, $thread, $styles, $targetViewport); - Border::render($widgetId, $thread, $styles, $targetViewport); + Background::render($widgetId, $thread, $styles, $targetViewport, $index); + Border::render($widgetId, $thread, $styles, $targetViewport, $index); } public static function changeViewport(string $widgetId, Thread $thread, array $styles, Viewport $viewport): void diff --git a/src/PHPNative/Renderer/src/Widget.php b/src/PHPNative/Renderer/src/Widget.php index 6a8f7d8..34e4eb9 100644 --- a/src/PHPNative/Renderer/src/Widget.php +++ b/src/PHPNative/Renderer/src/Widget.php @@ -2,29 +2,40 @@ namespace PHPNative\Renderer; +use PHPNative\UI\Widget\Icon; +use PHPNative\UI\Widget\Image; +use PHPNative\UI\Widget\TextEdit; use PHPNative\UI\BaseView; use PHPNative\UI\View; use PHPNative\UI\Widget\Button; use PHPNative\UI\Widget\Container; -use PHPNative\UI\Widget\Label; +use PHPNative\UI\Widget\Text; class Widget { - public static function render(Thread $thread, ViewPort $viewPort, View $view): Viewport { + public static function render(Thread $thread, ViewPort $viewPort, View $view, int $index = 1): Viewport { if($view instanceof BaseView) { - return \PHPNative\Renderer\Widgets\BaseView::render($thread, $viewPort, $view); + return \PHPNative\Renderer\Widgets\BaseView::render($thread, $viewPort, $view, $index); } if($view instanceof Button) { - return \PHPNative\Renderer\Widgets\Button::render($thread, $viewPort, $view); + return \PHPNative\Renderer\Widgets\Container::render($thread, $viewPort, $view, $index); } - if($view instanceof Label) { - return \PHPNative\Renderer\Widgets\Label::render($thread, $viewPort, $view); + if($view instanceof Text) { + return \PHPNative\Renderer\Widgets\Text::render($thread, $viewPort, $view, $index); + } + if($view instanceof TextEdit) { + return \PHPNative\Renderer\Widgets\TextEdit::render($thread, $viewPort, $view, $index); } if($view instanceof Container) { - return \PHPNative\Renderer\Widgets\Container::render($thread, $viewPort, $view); + return \PHPNative\Renderer\Widgets\Container::render($thread, $viewPort, $view, $index); + } + if($view instanceof Image) { + return \PHPNative\Renderer\Widgets\Image::render($thread, $viewPort, $view, $index); + } + if($view instanceof Icon) { + return \PHPNative\Renderer\Widgets\Icon::render($thread, $viewPort, $view, $index); } - } } \ No newline at end of file diff --git a/src/PHPNative/Renderer/src/Widgets/BaseView.php b/src/PHPNative/Renderer/src/Widgets/BaseView.php index 53107e1..ef402e3 100644 --- a/src/PHPNative/Renderer/src/Widgets/BaseView.php +++ b/src/PHPNative/Renderer/src/Widgets/BaseView.php @@ -13,43 +13,7 @@ use PHPNative\Tailwind\Style\Padding; use PHPNative\Tailwind\Style\StateEnum; use PHPNative\Tailwind\StyleParser; -class BaseView +class BaseView extends Container { - public static function render(Thread $thread, Viewport $viewport, \PHPNative\UI\BaseView $view): Viewport - { - $styles = StyleParser::parse($view->style)->getValidStyles($viewport->windowMediaQuery, StateEnum::normal); - if(isset($styles[Margin::class]) && $m = $styles[Margin::class]) { - $viewport->x += $m->left; - $viewport->width -= ($m->right + $m->left); - $viewport->y += $m->top; - $viewport->height -= ($m->bottom + $m->top); - } - - if(isset($styles[Background::class]) && $bg = $styles[Background::class]) { - $texture = \SDL_CreateTexture($viewport->renderPtr, \SDL_PIXELFORMAT_ARGB8888, \SDL_TEXTUREACCESS_TARGET, 200, 100); - \SDL_SetRenderTarget($viewport->renderPtr, $texture); - \SDL_SetRenderDrawColor($viewport->renderPtr, 0, 0, 0, 255); - \SDL_RenderClear($viewport->renderPtr); - $rect = new \SDL_FRect($viewport->x, $viewport->y, $viewport->width, $viewport->height); - - \SDL_SetRenderDrawColor($viewport->renderPtr, $bg->color->red, $bg->color->green, $bg->color->blue, $bg->color->alpha); - \SDL_RenderFillRect($viewport->renderPtr, $rect); - - $thread->addToRenderStack(new Item($view->getId(), $texture, $rect, 0)); - } - - if(isset($styles[Padding::class]) && $m = $styles[Padding::class]) { - $viewport->x += $m->left; - $viewport->width -= ($m->right + $m->left); - $viewport->y += $m->top; - $viewport->height -= ($m->bottom + $m->top); - } - - if($view->getViews()->count() > 0) { - Widget::render($thread, $viewport, $view->getViews()->first()); - } - - return $viewport; - } } \ No newline at end of file diff --git a/src/PHPNative/Renderer/src/Widgets/Button.php b/src/PHPNative/Renderer/src/Widgets/Button.php index 3c447c0..4884901 100644 --- a/src/PHPNative/Renderer/src/Widgets/Button.php +++ b/src/PHPNative/Renderer/src/Widgets/Button.php @@ -8,6 +8,7 @@ use PHPNative\Renderer\Item; use PHPNative\Renderer\Thread; use PHPNative\Renderer\Viewport; use PHPNative\Renderer\Widget; +use PHPNative\Tailwind\Style\AlignEnum; use PHPNative\Tailwind\Style\Background; use PHPNative\Tailwind\Style\Basis; use PHPNative\Tailwind\Style\Border; @@ -23,15 +24,20 @@ use PHPNative\Tailwind\StyleParser; class Button { - public static function render(Thread $thread, Viewport $viewport, \PHPNative\UI\Widget\Button $view): Viewport + public static function render(Thread $thread, Viewport $viewport, \PHPNative\UI\Widget\Button $view, int $index = 0): Viewport { $styles = StyleParser::parse($view->style)->getValidStyles($viewport->windowMediaQuery, $view->state); - $font = \SDL_TTF_OpenFont(__DIR__ . DIRECTORY_SEPARATOR . '../../../../../assets/segoe-ui.ttf' , 20); if(isset($styles[Text::class]) && $t = $styles[Text::class]) { - $color = new \SDL_Color($t->color->red, $t->color->green, $t->color->blue, $t->color->alpha); + $font = \SDL_TTF_OpenFont(__DIR__ . DIRECTORY_SEPARATOR . '../../../../../assets/segoe-ui.ttf' , $t->size); + if($t->color->red != -1) { + $color = new \SDL_Color($t->color->red, $t->color->green, $t->color->blue, $t->color->alpha); + }else{ + $color = new \SDL_Color(0, 0, 0, 255); + } }else{ + $font = \SDL_TTF_OpenFont(__DIR__ . DIRECTORY_SEPARATOR . '../../../../../assets/segoe-ui.ttf' , 16); $color = new \SDL_Color(0, 0, 0, 255); } $surface = \SDL_TTF_RenderText_Blended($font, $view->label, $color); @@ -76,7 +82,7 @@ class Button $backdropViewport->height += ($m->top + $m->bottom); } - \PHPNative\Renderer\Visuals\Visuals::render($view->getId(), $thread, $styles, $backdropViewport); + \PHPNative\Renderer\Visuals\Visuals::render($view->getId(), $thread, $styles, $backdropViewport, $index); if((isset($styles[Width::class]) && $m = $styles[Width::class]) || (isset($styles[Basis::class]) && $m = $styles[Basis::class])) { @@ -87,10 +93,15 @@ class Button new \SDL_FRect(0, 0, $viewport->width, $surface->h) )); }else { - $thread->addToRenderStack(new Item($view->getId(), $textureFont, new \SDL_FRect($viewport->x, $viewport->y, $surface->w, $surface->h), 3)); + if(isset($styles[Text::class]) && $styles[Text::class]->align == AlignEnum::center) { + $thread->addToRenderStack(new Item($view->getId(), $textureFont, new \SDL_FRect($viewport->x + (($viewport->width - $surface->w) /2), $viewport->y, $surface->w, $surface->h), ++$index)); + }elseif(isset($styles[Text::class]) && $styles[Text::class]->align == AlignEnum::right) { + $thread->addToRenderStack(new Item($view->getId(), $textureFont, new \SDL_FRect($viewport->x + ($viewport->width - $surface->w), $viewport->y, $surface->w, $surface->h), ++$index)); + }else{ + $thread->addToRenderStack(new Item($view->getId(), $textureFont, new \SDL_FRect($viewport->x, $viewport->y, $surface->w, $surface->h), ++$index)); + } } - }else{ } diff --git a/src/PHPNative/Renderer/src/Widgets/Container.php b/src/PHPNative/Renderer/src/Widgets/Container.php index 8b675d4..5d617fd 100644 --- a/src/PHPNative/Renderer/src/Widgets/Container.php +++ b/src/PHPNative/Renderer/src/Widgets/Container.php @@ -22,9 +22,9 @@ use PHPNative\Tailwind\StyleParser; class Container { - public static function render(Thread $thread, Viewport $viewport, \PHPNative\UI\Widget\Container $view): Viewport + public static function render(Thread $thread, Viewport $viewport, \PHPNative\UI\Widget\Container $view, int $index = 0): Viewport { - $styles = StyleParser::parse($view->style)->getValidStyles($viewport->windowMediaQuery, StateEnum::normal); + $styles = StyleParser::parse($view->style)->getValidStyles($viewport->windowMediaQuery, $view->state); if(isset($styles[Margin::class]) && $m = $styles[Margin::class]) { $viewport->x += $m->left; @@ -46,6 +46,7 @@ class Container $flexStyles = []; if($view->getViews() != null) { + foreach ($view->getViews() as $subView) { $found = false; $stylesSubView = StyleParser::parse($subView->style)->getValidStyles($viewport->windowMediaQuery, StateEnum::normal); @@ -68,13 +69,13 @@ class Container if ($found) { if (isset($styles[Flex::class]) && $f = $styles[Flex::class] && $styles[Flex::class]->direction == DirectionEnum::column) { - $viepo = new Viewport($viewport->windowId, $viewport->renderPtr, $viewport->x, $viewport->y, $width, $viewport->height, $viewport->windowWidth, $viewport->windowHeight, $viewport->windowMediaQuery); + $viepo = new Viewport($viewport->windowId, $viewport->windowPtr, $viewport->renderPtr, $viewport->x, $viewport->y, $width, $viewport->height, $viewport->windowWidth, $viewport->windowHeight, $viewport->windowMediaQuery); } else { - $viepo = new Viewport($viewport->windowId, $viewport->renderPtr, $viewport->x + (count($flexStyles) === 0 ? 0 : end($flexStyles)->x+end($flexStyles)->width), $viewport->y, $width, $viewport->height, $viewport->windowWidth, $viewport->windowHeight, $viewport->windowMediaQuery); + $viepo = new Viewport($viewport->windowId, $viewport->windowPtr, $viewport->renderPtr, $viewport->x + (count($flexStyles) === 0 ? 0 : end($flexStyles)->x+end($flexStyles)->width), $viewport->y, $width, $viewport->height, $viewport->windowWidth, $viewport->windowHeight, $viewport->windowMediaQuery); } $flexStyles[] = $viepo; } else { - $flexStyles[] = $viewport; + $flexStyles[] = clone $viewport; } } @@ -83,7 +84,7 @@ class Container if (isset($styles[Flex::class]) && $f = $styles[Flex::class] && $styles[Flex::class]->direction == DirectionEnum::column) { $subViewPort = array_shift($flexStyles); $subViewPort->y = $viewport->y - $view->getMoveToY(); - $vp = Widget::render($thread, $subViewPort, $subView); + $vp = Widget::render($thread, $subViewPort, $subView, $index+1); if($vp->y < $targetViewport->y) { $thread->clipFromStack($subView->getId(), y: $targetViewport->y-$vp->y); } @@ -93,9 +94,19 @@ class Container $viewport->y += $vp->height; $viewport->height -= $vp->height; - } else { - $vp = Widget::render($thread, array_shift($flexStyles), $subView); + } elseif(isset($styles[Flex::class]) && $f = $styles[Flex::class] && $styles[Flex::class]->direction == DirectionEnum::row) { + $vp = Widget::render($thread, array_shift($flexStyles), $subView, $index+1); $topHeight = max($topHeight, $vp->height); + } else{ + $sfs = array_shift($flexStyles); + $vp = Widget::render($thread, $sfs, $subView, $index+1); + $topHeight = max($topHeight, $vp->height); + if(count($flexStyles) > 0) { + if($vp->addX > 0) { + $flexStyles[0]->x += $vp->addX; + $flexStyles[0]->width -= $vp->addX; + } + } } } if (isset($styles[Flex::class]) && $f = $styles[Flex::class] && $styles[Flex::class]->direction == DirectionEnum::column) { @@ -104,16 +115,43 @@ class Container $targetViewport->height = $topHeight; $viewport->y += $topHeight; $viewport->height = $topHeight; + if(isset($styles[Padding::class]) && $m = $styles[Padding::class]) { + $targetViewport->height += ($m->bottom + $m->top); + $viewport->height += ($m->bottom + $m->top); + } + if(isset($styles[Margin::class]) && $m = $styles[Margin::class]) { + $viewport->height += ($m->bottom + $m->top); + } } - \PHPNative\Renderer\Visuals\Visuals::render($view->getId(), $thread, $styles, $targetViewport); + if($thread->getEvent() && $thread->getEvent()->getType() === EventType::MOUSEMOVE) { + if( $targetViewport->x <= $thread->getEvent()->x && + $thread->getEvent()->x <= $targetViewport->x + $targetViewport->width && + $targetViewport->y <= $thread->getEvent()->y && + $thread->getEvent()->y <= $targetViewport->y + $targetViewport->height ) { + $view->state = StateEnum::hover; + }else{ + $view->state = StateEnum::normal; + } + } + + if($view instanceof \PHPNative\UI\Widget\Button && $thread->getEvent() && $thread->getEvent()->getType() === EventType::MOUSEBUTTON_UP) { + if( $targetViewport->x <= $thread->getEvent()->x && + $thread->getEvent()->x <= $targetViewport->x + $targetViewport->width && + $targetViewport->y <= $thread->getEvent()->y && $thread->getEvent()->y <= + $targetViewport->y + $targetViewport->height ) { + $view->onClick($thread->worker); + } + } + + \PHPNative\Renderer\Visuals\Visuals::render($view->getId(), $thread, $styles, $targetViewport, $index); if (isset($styles[Overflow::class]) && $f = $styles[Overflow::class] && ( $styles[Overflow::class]->y == OverflowEnum::scroll || ($styles[Overflow::class]->y == OverflowEnum::auto && $viewport->height < 0) )) { - \PHPNative\Renderer\Visuals\Scrollbar::renderBackground($view->getId(), $thread, $styles, $targetViewport); + \PHPNative\Renderer\Visuals\Scrollbar::renderBackground($view->getId(), $thread, $styles, $targetViewport, $index); if ($thread->getEvent() && $thread->getEvent()->getType() === EventType::MOUSEBUTTON_DOWN) { if ($targetViewport->x + $targetViewport->width - 10 <= $thread->getEvent()->x && @@ -154,7 +192,7 @@ class Container $sliderViewport->y = $targetViewport->y + (int)($view->getMoveToY()*$ratio); $sliderViewport->height = (int)($ratio*100 * $ohP); - \PHPNative\Renderer\Visuals\Scrollbar::renderSlider($view->getId(), $thread, $styles, $sliderViewport); + \PHPNative\Renderer\Visuals\Scrollbar::renderSlider($view->getId(), $thread, $styles, $sliderViewport, $index); } } diff --git a/src/PHPNative/Renderer/src/Widgets/Icon.php b/src/PHPNative/Renderer/src/Widgets/Icon.php new file mode 100644 index 0000000..f724e04 --- /dev/null +++ b/src/PHPNative/Renderer/src/Widgets/Icon.php @@ -0,0 +1,103 @@ +style)->getValidStyles($viewport->windowMediaQuery, $view->state); + + if(isset($styles[\PHPNative\Tailwind\Style\Text::class]) && $t = $styles[\PHPNative\Tailwind\Style\Text::class]) { + $font = \SDL_TTF_OpenFont(__DIR__ . DIRECTORY_SEPARATOR . '../../../../../assets/symbol.ttf' , $view->size); + if($t->color->red != -1) { + $color = new \SDL_Color($t->color->red, $t->color->green, $t->color->blue, $t->color->alpha); + }else{ + $color = new \SDL_Color(0, 0, 0, 255); + } + }else{ + $font = \SDL_TTF_OpenFont(__DIR__ . DIRECTORY_SEPARATOR . '../../../../../assets/symbol.ttf' , $view->size); + $color = new \SDL_Color(0, 0, 0, 255); + } + + $surface = \SDL_TTF_RenderGlyph_Blended($font, $view->icon->value, $color); + + if(!isset($styles[Basis::class]) && !isset($styles[Width::class])) { + $viewport->width = $surface->w; + } + $viewport->height = $surface->h; + + if(isset($styles[Margin::class]) && $m = $styles[Margin::class]) { + $viewport->x += $m->left; + $viewport->width -= ($m->right + $m->left); + $viewport->y += $m->top; + $viewport->height += ($m->bottom + $m->top); + } + + $backdropViewport = clone $viewport; + $backdropViewport->height = $surface->h; + + if($thread->getEvent() && $thread->getEvent()->getType() === EventType::MOUSEMOVE) { + if( $viewport->x <= $thread->getEvent()->x && + $thread->getEvent()->x <= $viewport->x + $viewport->width && + $viewport->y <= $thread->getEvent()->y && + $thread->getEvent()->y <= $viewport->y + $viewport->height ) { + $view->state = StateEnum::hover; + }else{ + $view->state = StateEnum::normal; + } + } + + if(isset($styles[Padding::class]) && $m = $styles[Padding::class]) { + $viewport->x += $m->left; + $viewport->width -= ($m->right + $m->left); + $viewport->y += $m->top; + $viewport->height += ($m->bottom + $m->top); + $backdropViewport->height += ($m->bottom + $m->top); + } + + \PHPNative\Renderer\Visuals\Visuals::render($view->getId(), $thread, $styles, $backdropViewport, $index); + + if((isset($styles[Width::class]) && $m = $styles[Width::class]) || (isset($styles[Basis::class]) && $m = $styles[Basis::class])) { + + $textureFont = \SDL_CreateTextureFromSurface($viewport->renderPtr, $surface); + if($surface->w > $viewport->width) { + $thread->addToRenderStack(new Item($textureFont, + new \SDL_FRect($viewport->x, $viewport->y, $surface->w, $surface->h), 3, + new \SDL_FRect(0, 0, $viewport->width, $surface->h) + )); + }else{ + $thread->addToRenderStack(new Item($view->getId(), $textureFont, new \SDL_FRect($viewport->x, $viewport->y, $surface->w, $surface->h), ++$index)); + } + }else{ + $textureFont = \SDL_CreateTextureFromSurface($viewport->renderPtr, $surface); + $thread->addToRenderStack(new Item($view->getId(), $textureFont, new \SDL_FRect($viewport->x, $viewport->y, $surface->w, $surface->h), ++$index)); + } + + if(!isset($styles[Basis::class]) && !isset($styles[Width::class])) { + $viewport->addX = $viewport->width; + } + + return $viewport; + } + +} \ No newline at end of file diff --git a/src/PHPNative/Renderer/src/Widgets/Label.php b/src/PHPNative/Renderer/src/Widgets/Image.php similarity index 83% rename from src/PHPNative/Renderer/src/Widgets/Label.php rename to src/PHPNative/Renderer/src/Widgets/Image.php index 15df5eb..e047b9c 100644 --- a/src/PHPNative/Renderer/src/Widgets/Label.php +++ b/src/PHPNative/Renderer/src/Widgets/Image.php @@ -20,20 +20,17 @@ use PHPNative\Tailwind\Style\Unit; use PHPNative\Tailwind\Style\Width; use PHPNative\Tailwind\StyleParser; -class Label +class Image { - public static function render(Thread $thread, Viewport $viewport, \PHPNative\UI\Widget\Label $view): Viewport + public static function render(Thread $thread, Viewport $viewport, \PHPNative\UI\Widget\Image $view, int $index = 0): Viewport { $styles = StyleParser::parse($view->style)->getValidStyles($viewport->windowMediaQuery, $view->state); - $font = \SDL_TTF_OpenFont(__DIR__ . DIRECTORY_SEPARATOR . '../../../../../assets/segoe-ui.ttf' , 20); - if(isset($styles[Text::class]) && $t = $styles[Text::class]) { - $color = new \SDL_Color($t->color->red, $t->color->green, $t->color->blue, $t->color->alpha); - }else{ - $color = new \SDL_Color(0, 0, 0, 255); + if(!file_exists($view->src)) { + return $viewport; } - $surface = \SDL_TTF_RenderText_Blended($font, $view->label, $color); + $surface = \SDL_IMG_Load($view->src); $viewport->height = $surface->h; @@ -66,7 +63,7 @@ class Label $backdropViewport->height += ($m->bottom + $m->top); } - \PHPNative\Renderer\Visuals\Visuals::render($view->getId(), $thread, $styles, $backdropViewport); + \PHPNative\Renderer\Visuals\Visuals::render($view->getId(), $thread, $styles, $backdropViewport, $index); if((isset($styles[Width::class]) && $m = $styles[Width::class]) || (isset($styles[Basis::class]) && $m = $styles[Basis::class])) { @@ -77,10 +74,11 @@ class Label new \SDL_FRect(0, 0, $viewport->width, $surface->h) )); }else{ - $thread->addToRenderStack(new Item($view->getId(), $textureFont, new \SDL_FRect($viewport->x, $viewport->y, $surface->w, $surface->h), 3)); + $thread->addToRenderStack(new Item($view->getId(), $textureFont, new \SDL_FRect($viewport->x, $viewport->y, $surface->w, $surface->h), ++$index)); } }else{ - + $textureFont = \SDL_CreateTextureFromSurface($viewport->renderPtr, $surface); + $thread->addToRenderStack(new Item($view->getId(), $textureFont, new \SDL_FRect($viewport->x, $viewport->y, $surface->w, $surface->h), ++$index)); } return $viewport; diff --git a/src/PHPNative/Renderer/src/Widgets/Text.php b/src/PHPNative/Renderer/src/Widgets/Text.php new file mode 100644 index 0000000..bf4233e --- /dev/null +++ b/src/PHPNative/Renderer/src/Widgets/Text.php @@ -0,0 +1,119 @@ +style)->getValidStyles($viewport->windowMediaQuery, $view->state); + + if(isset($styles[\PHPNative\Tailwind\Style\Text::class]) && $t = $styles[\PHPNative\Tailwind\Style\Text::class]) { + $font = \SDL_TTF_OpenFont(__DIR__ . DIRECTORY_SEPARATOR . '../../../../../assets/segoe-ui.ttf' , $t->size); + if($t->color->red != -1) { + $color = new \SDL_Color($t->color->red, $t->color->green, $t->color->blue, $t->color->alpha); + }else{ + $color = new \SDL_Color(0, 0, 0, 255); + } + }else{ + $font = \SDL_TTF_OpenFont(__DIR__ . DIRECTORY_SEPARATOR . '../../../../../assets/segoe-ui.ttf' , 16); + $color = new \SDL_Color(0, 0, 0, 255); + } + $surface = \SDL_TTF_RenderText_Blended($font, $view->text, $color); + + if(!isset($styles[Basis::class]) && !isset($styles[Width::class])) { + $viewport->width = $surface->w; + } + $viewport->height = $surface->h; + + if(isset($styles[Margin::class]) && $m = $styles[Margin::class]) { + $viewport->x += $m->left; + if(!isset($styles[Basis::class]) && !isset($styles[Width::class])) { + $viewport->width += ($m->right + $m->left); + }else{ + $viewport->width -= ($m->right + $m->left); + } + $viewport->y += $m->top; + $viewport->height += ($m->bottom + $m->top); + } + + $backdropViewport = clone $viewport; + $backdropViewport->height = $surface->h; + + if($thread->getEvent() && $thread->getEvent()->getType() === EventType::MOUSEMOVE) { + if( $viewport->x <= $thread->getEvent()->x && + $thread->getEvent()->x <= $viewport->x + $viewport->width && + $viewport->y <= $thread->getEvent()->y && + $thread->getEvent()->y <= $viewport->y + $viewport->height ) { + $view->state = StateEnum::hover; + }else{ + $view->state = StateEnum::normal; + } + } + + if(isset($styles[Padding::class]) && $m = $styles[Padding::class]) { + $viewport->x += $m->left; + $viewport->width -= ($m->right + $m->left); + $viewport->y += $m->top; + $viewport->height += ($m->bottom + $m->top); + $backdropViewport->height += ($m->top + $m->bottom); + } + + \PHPNative\Renderer\Visuals\Visuals::render($view->getId(), $thread, $styles, $backdropViewport, $index); + + if((isset($styles[Width::class]) && $m = $styles[Width::class]) || (isset($styles[Basis::class]) && $m = $styles[Basis::class])) { + + $textureFont = \SDL_CreateTextureFromSurface($viewport->renderPtr, $surface); + if($surface->w > $viewport->width) { + $thread->addToRenderStack(new Item($textureFont, + new \SDL_FRect($viewport->x, $viewport->y, $surface->w, $surface->h), 3, + new \SDL_FRect(0, 0, $viewport->width, $surface->h) + )); + }else { + if(isset($styles[\PHPNative\Tailwind\Style\Text::class]) && $styles[\PHPNative\Tailwind\Style\Text::class]->align == AlignEnum::center) { + $thread->addToRenderStack(new Item($view->getId(), $textureFont, new \SDL_FRect($viewport->x + (($viewport->width - $surface->w) /2), $viewport->y, $surface->w, $surface->h), ++$index)); + }elseif(isset($styles[Text::class]) && $styles[Text::class]->align == AlignEnum::right) { + $thread->addToRenderStack(new Item($view->getId(), $textureFont, new \SDL_FRect($viewport->x + ($viewport->width - $surface->w), $viewport->y, $surface->w, $surface->h), ++$index)); + }else{ + $thread->addToRenderStack(new Item($view->getId(), $textureFont, new \SDL_FRect($viewport->x, $viewport->y, $surface->w, $surface->h), ++$index)); + } + } + + }else{ + $textureFont = \SDL_CreateTextureFromSurface($viewport->renderPtr, $surface); + if($surface->w > $viewport->width) { + $thread->addToRenderStack(new Item($view->getId(), $textureFont, + new \SDL_FRect($viewport->x, $viewport->y, $surface->w, $surface->h), 3, + new \SDL_FRect(0, 0, $viewport->width, $surface->h) + )); + }else { + if(isset($styles[\PHPNative\Tailwind\Style\Text::class]) && $styles[\PHPNative\Tailwind\Style\Text::class]->align == AlignEnum::center) { + $thread->addToRenderStack(new Item($view->getId(), $textureFont, new \SDL_FRect($viewport->x + (($viewport->width - $surface->w) /2), $viewport->y, $surface->w, $surface->h), ++$index)); + }elseif(isset($styles[Text::class]) && $styles[Text::class]->align == AlignEnum::right) { + $thread->addToRenderStack(new Item($view->getId(), $textureFont, new \SDL_FRect($viewport->x + ($viewport->width - $surface->w), $viewport->y, $surface->w, $surface->h), ++$index)); + }else{ + $thread->addToRenderStack(new Item($view->getId(), $textureFont, new \SDL_FRect($viewport->x, $viewport->y, $surface->w, $surface->h), ++$index)); + } + } + } + + return $viewport; + } + +} \ No newline at end of file diff --git a/src/PHPNative/Renderer/src/Widgets/TextEdit.php b/src/PHPNative/Renderer/src/Widgets/TextEdit.php new file mode 100644 index 0000000..96b314a --- /dev/null +++ b/src/PHPNative/Renderer/src/Widgets/TextEdit.php @@ -0,0 +1,179 @@ +style)->getValidStyles($viewport->windowMediaQuery, $view->state); + + if(isset($styles[Text::class]) && $t = $styles[Text::class]) { + $font = \SDL_TTF_OpenFont(__DIR__ . DIRECTORY_SEPARATOR . '../../../../../assets/segoe-ui.ttf' , $t->size); + if($t->color->red != -1) { + $color = new \SDL_Color($t->color->red, $t->color->green, $t->color->blue, $t->color->alpha); + }else{ + $color = new \SDL_Color(0, 0, 0, 255); + } + }else{ + $font = \SDL_TTF_OpenFont(__DIR__ . DIRECTORY_SEPARATOR . '../../../../../assets/segoe-ui.ttf' , 16); + $color = new \SDL_Color(0, 0, 0, 255); + } + if($view->value == "" && $view->placeholder != null) { + $color = new \SDL_Color(0, 0, 0, 128); + $surface = \SDL_TTF_RenderText_Blended($font, $view->placeholder, $color); + $cursorWidth = 0; + }else{ + $surface = \SDL_TTF_RenderText_Blended($font, $view->value, $color); + $surfaceCursor = \SDL_TTF_RenderText_Blended($font, mb_substr($view->value, 0, $view->textEditIndex), $color); + $cursorWidth = $surfaceCursor->w; + + } + + $viewport->height = $surface->h; + + if(isset($styles[Margin::class]) && $m = $styles[Margin::class]) { + $viewport->x += $m->left; + $viewport->width -= ($m->right + $m->left); + $viewport->y += $m->top; + $viewport->height += ($m->bottom + $m->top); + } + + $backdropViewport = clone $viewport; + $backdropViewport->height = $surface->h; + + if($thread->getEvent() && $thread->getEvent()->getType() === EventType::MOUSEMOVE) { + if( $viewport->x <= $thread->getEvent()->x && + $thread->getEvent()->x <= $viewport->x + $viewport->width && + $viewport->y <= $thread->getEvent()->y && + $thread->getEvent()->y <= $viewport->y + $viewport->height ) { + if($view->state == StateEnum::focus || $view->state == StateEnum::hoverfocus) { + $view->state = StateEnum::hoverfocus; + }else{ + $view->state = StateEnum::hover; + } + }else{ + if($view->state == StateEnum::hoverfocus ) { + $view->state = StateEnum::focus; + } + if($view->state != StateEnum::focus ) { + $view->state = StateEnum::normal; + } + + } + } + + if($thread->getEvent() && $thread->getEvent()->getType() === EventType::MOUSEBUTTON_UP) { + if( $viewport->x <= $thread->getEvent()->x && + $thread->getEvent()->x <= $viewport->x + $viewport->width && + $viewport->y <= $thread->getEvent()->y && $thread->getEvent()->y <= + $viewport->y + $viewport->height ) { + $view->state = StateEnum::focus; + \SDL_StartTextInput($thread->windowPtr); + }else{ + \SDL_StopTextInput($thread->windowPtr); + $view->state = StateEnum::normal; + } + } + if($thread->getEvent() && $thread->getEvent()->getType() === EventType::KEYUP && $thread->getEvent()->key == Key::BACKSPACE) { + if($view->textEditIndex > 0) { + $view->value = self::str_delete($view->value, $view->textEditIndex); + $view->textEditIndex -= 1; + } + } + if($thread->getEvent() && $thread->getEvent()->getType() === EventType::TEXTINPUT && $view->state === StateEnum::focus) { + if($view->value != "") { + $view->value = self::str_insert($view->value, $thread->getEvent()->text, $view->textEditIndex); + }else { + $view->value = $view->value . $thread->getEvent()->text; + } + $view->textEditIndex += mb_strlen($thread->getEvent()->text); + } + + if($thread->getEvent() && $thread->getEvent()->getType() === EventType::KEYUP && $thread->getEvent()->key == Key::SDLK_LEFT) { + if($view->textEditIndex > 0) { + $view->textEditIndex -= 1; + } + } + if($thread->getEvent() && $thread->getEvent()->getType() === EventType::KEYUP && $thread->getEvent()->key == Key::SDLK_RIGHT) { + if($view->textEditIndex < mb_strlen($view->value)) { + $view->textEditIndex += 1; + } + } + + + if(isset($styles[Padding::class]) && $m = $styles[Padding::class]) { + $viewport->x += $m->left; + $viewport->width -= ($m->right + $m->left); + $viewport->y += $m->top; + $viewport->height += ($m->bottom + $m->top); + $backdropViewport->height += ($m->top + $m->bottom); + } + + \PHPNative\Renderer\Visuals\Visuals::render($view->getId(), $thread, $styles, $backdropViewport, $index); + + $currentTime = \SDL_GetTicks() / 500; + + if (((int)$currentTime & 1) && ($view->state == StateEnum::focus || $view->state == StateEnum::hoverfocus)) { + $viewportCorsor = clone $viewport; + $viewportCorsor->height = $surface->h; + $viewportCorsor->x += $cursorWidth; + Cursor::render($view->getId(), $thread, $styles, $viewportCorsor, $index); + } + + if((isset($styles[Width::class]) && $m = $styles[Width::class]) || (isset($styles[Basis::class]) && $m = $styles[Basis::class])) { + + $textureFont = \SDL_CreateTextureFromSurface($viewport->renderPtr, $surface); + if($surface->w > $viewport->width) { + $thread->addToRenderStack(new Item($view->getId(), $textureFont, + new \SDL_FRect($viewport->x, $viewport->y, $viewport->width, $surface->h), 3, + new \SDL_FRect($surface->w - $viewport->width, 0, $viewport->width, $surface->h) + )); + }else { + if(isset($styles[Text::class]) && $styles[Text::class]->align == AlignEnum::center) { + $thread->addToRenderStack(new Item($view->getId(), $textureFont, new \SDL_FRect($viewport->x + (($viewport->width - $surface->w) /2), $viewport->y, $surface->w, $surface->h), ++$index)); + }elseif(isset($styles[Text::class]) && $styles[Text::class]->align == AlignEnum::right) { + $thread->addToRenderStack(new Item($view->getId(), $textureFont, new \SDL_FRect($viewport->x + ($viewport->width - $surface->w), $viewport->y, $surface->w, $surface->h), ++$index)); + }else{ + $thread->addToRenderStack(new Item($view->getId(), $textureFont, new \SDL_FRect($viewport->x, $viewport->y, $surface->w, $surface->h), ++$index)); + } + } + + }else{ + + } + + return $viewport; + } + + private static function str_delete(string $str, int $pos = 0) { + return mb_substr($str, 0, $pos-1) . mb_substr($str, $pos); + } + + private static function str_insert(string $oldString, string $newString, int $pos = 0) { + return mb_substr($oldString, 0, $pos) . $newString . mb_substr($oldString, $pos); + } +} \ No newline at end of file diff --git a/src/PHPNative/Storage/composer.json b/src/PHPNative/Storage/composer.json new file mode 100644 index 0000000..5375eb3 --- /dev/null +++ b/src/PHPNative/Storage/composer.json @@ -0,0 +1,18 @@ +{ + "name": "phpnative/storage", + "license": "MIT", + "require": { + "php": "^8.3", + "zumba/json-serializer": "^3" + }, + "autoload": { + "psr-4": { + "PHPNative\\Storage\\": "src" + } +}, + "autoload-dev": { + "psr-4": { + "PHPNative\\Storage\\Tests\\": "tests" + } +} +} \ No newline at end of file diff --git a/src/PHPNative/Storage/src/Storage.php b/src/PHPNative/Storage/src/Storage.php new file mode 100644 index 0000000..9d35e73 --- /dev/null +++ b/src/PHPNative/Storage/src/Storage.php @@ -0,0 +1,67 @@ +getLocalAppData() . DIRECTORY_SEPARATOR . md5($model::class). '.json'; + if(!file_exists($this->getLocalAppData())) { + mkdir($this->getLocalAppData(), 0777, true); + } + file_put_contents($path, $this->jsonSerializer->serialize($model)); + } + + /** + * @template T + * @param class-string $className + * @return T + */ + public function loadModel(string $className): object + { + $path = $this->getLocalAppData() . DIRECTORY_SEPARATOR . md5($className). '.json'; + if(file_exists($path)) { + return $this->jsonSerializer->unserialize(file_get_contents($path)); + }else{ + return new $className(); + } + } + + + private function getLocalAppData() { + $homeDir = $_SERVER['HOME'] ?? ''; + + if(empty($homeDir)) { + $homeDir = getenv('HOME'); + } + + if(empty($homeDir) && $this->is_windows()) { + $homeData = $_SERVER['LOCALAPPDATA'] ?? ''; + + $homeDir = $homeData; + } + + if(empty($homeDir) && function_exists('exec')) { + if($this->is_windows()) { + $homeDir = exec('echo %userprofile%'); + } else { + $homeDir = exec('echo ~'); + } + } + + return implode(DIRECTORY_SEPARATOR ,[$homeDir, $this->appConfig->getName()]); + } + + private function is_windows() { + return strncasecmp(PHP_OS, "WIN", 3) === 0; + } + +} \ No newline at end of file diff --git a/src/PHPNative/Tailwind/src/Data/Icon.php b/src/PHPNative/Tailwind/src/Data/Icon.php new file mode 100644 index 0000000..89fdbc9 --- /dev/null +++ b/src/PHPNative/Tailwind/src/Data/Icon.php @@ -0,0 +1,8 @@ +style::class], $style->style); }elseif(isset($tmp[$style->style::class]) && $style->style::class === Border::class) { \PHPNative\Tailwind\Parser\Border::merge($tmp[$style->style::class], $style->style); + }elseif(isset($tmp[$style->style::class]) && $style->style::class === Text::class) { + \PHPNative\Tailwind\Parser\Text::merge($tmp[$style->style::class], $style->style); }else{ $tmp[$style->style::class] = $style->style; } diff --git a/src/PHPNative/Tailwind/src/Parser/Text.php b/src/PHPNative/Tailwind/src/Parser/Text.php index c53f161..209154b 100644 --- a/src/PHPNative/Tailwind/src/Parser/Text.php +++ b/src/PHPNative/Tailwind/src/Parser/Text.php @@ -4,12 +4,43 @@ declare(strict_types=1); namespace PHPNative\Tailwind\Parser; +use PHPNative\Tailwind\Style\AlignEnum; + class Text implements Parser { public static function parse(string $style): ?\PHPNative\Tailwind\Style\Text { $color = new \PHPNative\Tailwind\Style\Color(); + preg_match_all('/text-(xs|sm|base|lg|xl|2xl|3xl|4xl|5xl|6xl|7xl|8xl|9xl)/', $style, $output_array); + if (count($output_array[0]) > 0) { + $size = match ((string)$output_array[1][0]) { + 'xs' => 12, + 'sm' => 14, + 'base' => 16, + 'lg' => 18, + 'xl' => 20, + '2xl' => 24, + '3xl' => 30, + '4xl' => 36, + '5xl' => 48, + '6xl' => 60, + '7xl' => 72, + '8xl' => 96, + '9xl' => 128, + }; + return new \PHPNative\Tailwind\Style\Text(size: $size); + } + + preg_match_all('/text-(center|right|left)/', $style, $output_array); + if (count($output_array[0]) > 0) { + return match ((string)$output_array[1][0]) { + 'left' => new \PHPNative\Tailwind\Style\Text(align: AlignEnum::left), + 'right' => new \PHPNative\Tailwind\Style\Text(align: AlignEnum::right), + 'center' => new \PHPNative\Tailwind\Style\Text(align: AlignEnum::center), + }; + } + preg_match_all('/text-(.*)/', $style, $output_array); if (count($output_array[0]) > 0) { $colorStyle = $output_array[1][0]; @@ -19,4 +50,26 @@ class Text implements Parser return null; } + + public static function merge(\PHPNative\Tailwind\Style\Text $style1, \PHPNative\Tailwind\Style\Text $style2): void + { + if($style2->color->red != -1) { + $style1->color->red = $style2->color->red; + } + if($style2->color->green != -1) { + $style1->color->green = $style2->color->green; + } + if($style2->color->blue != -1) { + $style1->color->blue = $style2->color->blue; + } + if($style2->color->alpha != -1) { + $style1->color->alpha = $style2->color->alpha; + } + if($style2->size != 16) { + $style1->size = $style2->size; + } + if($style2->align != AlignEnum::left) { + $style1->align = $style2->align; + } + } } diff --git a/src/PHPNative/Tailwind/src/Style/AlignEnum.php b/src/PHPNative/Tailwind/src/Style/AlignEnum.php new file mode 100644 index 0000000..c7dce94 --- /dev/null +++ b/src/PHPNative/Tailwind/src/Style/AlignEnum.php @@ -0,0 +1,10 @@ +assertSame(219, $text->color->blue); $this->assertSame(255, $text->color->alpha); } + + public function test_text_align(): void + { + /** @var \PHPNative\Tailwind\Style\Text $text */ + $text = Text::parse("flex text-center"); + $this->assertInstanceOf(\PHPNative\Tailwind\Style\Text::class, $text); + $this->assertSame(AlignEnum::center, $text->align); + $text = Text::parse("flex text-left"); + $this->assertInstanceOf(\PHPNative\Tailwind\Style\Text::class, $text); + $this->assertSame(AlignEnum::left, $text->align); + $text = Text::parse("flex text-right"); + $this->assertInstanceOf(\PHPNative\Tailwind\Style\Text::class, $text); + $this->assertSame(AlignEnum::right, $text->align); + } } diff --git a/src/PHPNative/UI/src/BaseView.php b/src/PHPNative/UI/src/BaseView.php index e2dd38a..0bd63eb 100644 --- a/src/PHPNative/UI/src/BaseView.php +++ b/src/PHPNative/UI/src/BaseView.php @@ -5,18 +5,15 @@ namespace PHPNative\UI; use PHPNative\UI\Collection\Views; use PHPNative\UI\Trait\Id; use PHPNative\UI\Trait\Style; +use PHPNative\UI\Widget\Container; -class BaseView implements View +class BaseView extends Container { use Style; use Id; public ?Views $views = null; - public function __construct() - { - } - public function getViews(): ?Views { return $this->views; diff --git a/src/PHPNative/UI/src/Widget/Button.php b/src/PHPNative/UI/src/Widget/Button.php index 40b3862..07f3910 100644 --- a/src/PHPNative/UI/src/Widget/Button.php +++ b/src/PHPNative/UI/src/Widget/Button.php @@ -4,24 +4,24 @@ declare(strict_types=1); namespace PHPNative\UI\Widget; +use PHPNative\UI\Collection\Views; use PHPNative\UI\Trait\Action\Click; use PHPNative\UI\Trait\Id; use PHPNative\UI\Trait\State; use PHPNative\UI\Trait\Style; use PHPNative\UI\View; -class Button implements View +class Button extends Container implements View { - use Style; use Click; - use State; - use Id; - public function __construct(public string $label) + public function __construct(public ?Views $views = null, string $style = "") { + parent::__construct($style); } public function getViews(): ?\PHPNative\UI\Collection\Views { + return $this->views; } } diff --git a/src/PHPNative/UI/src/Widget/Container.php b/src/PHPNative/UI/src/Widget/Container.php index 823b41f..a8afca2 100644 --- a/src/PHPNative/UI/src/Widget/Container.php +++ b/src/PHPNative/UI/src/Widget/Container.php @@ -7,6 +7,7 @@ namespace PHPNative\UI\Widget; use PHPNative\UI\Collection\Views; use PHPNative\UI\Trait\Action\Scrollbar; use PHPNative\UI\Trait\Id; +use PHPNative\UI\Trait\State; use PHPNative\UI\Trait\Style; use PHPNative\UI\View; @@ -14,6 +15,7 @@ class Container implements View { use Style; use Scrollbar; + use State; use Id; public function __construct(string $style = "") diff --git a/src/PHPNative/UI/src/Widget/Icon.php b/src/PHPNative/UI/src/Widget/Icon.php new file mode 100644 index 0000000..c910d3e --- /dev/null +++ b/src/PHPNative/UI/src/Widget/Icon.php @@ -0,0 +1,26 @@ +style = $style; + } + + public function getViews(): ?Views + { + return null; + } +} \ No newline at end of file diff --git a/src/PHPNative/UI/src/Widget/Image.php b/src/PHPNative/UI/src/Widget/Image.php new file mode 100644 index 0000000..e43245e --- /dev/null +++ b/src/PHPNative/UI/src/Widget/Image.php @@ -0,0 +1,26 @@ +style = $style; + } + + public function getViews(): ?Views + { + return null; + } +} \ No newline at end of file diff --git a/src/PHPNative/UI/src/Widget/Label.php b/src/PHPNative/UI/src/Widget/Text.php similarity index 79% rename from src/PHPNative/UI/src/Widget/Label.php rename to src/PHPNative/UI/src/Widget/Text.php index 50148ea..07b2b7a 100644 --- a/src/PHPNative/UI/src/Widget/Label.php +++ b/src/PHPNative/UI/src/Widget/Text.php @@ -9,13 +9,13 @@ use PHPNative\UI\Trait\State; use PHPNative\UI\Trait\Style; use PHPNative\UI\View; -class Label implements View +class Text implements View { use Style; use State; use Id; - public function __construct(public string $label, string $style = '') + public function __construct(public ?string $text, string $style = '') { $this->style = $style; } diff --git a/src/PHPNative/UI/src/Widget/TextEdit.php b/src/PHPNative/UI/src/Widget/TextEdit.php new file mode 100644 index 0000000..9ddecd0 --- /dev/null +++ b/src/PHPNative/UI/src/Widget/TextEdit.php @@ -0,0 +1,28 @@ +style = $style; + } + + public function getViews(): ?\PHPNative\UI\Collection\Views + { + } +}