<?phpnamespace App\Gui;use JumPHP\Gui\Pointer;use JumPHP\Gui\Theme;use JumPHP\Gui\Widget;use JumPHP\Renderer\Renderer;/** * The tabs over the right pane (General, Memory, Storage, Networking, Console, Performance, Snapshots...). * Events: change (int $index, string $tab). * * @property list<string> $tabs * @property int $selected */final class TabStrip extends Widget{ public const DEFAULTS = ['tabs' => [], 'selected' => 0, 'width' => 'fill']; public static function events(): array { return ['change', 'click']; } protected function content(Theme $t, float $maxWidth, string $breakpoint): array { $lines = 1; foreach ($this->boxes($t, $maxWidth, 0.0) as [, , $line]) { $lines = max($lines, $line + 1); } return [$maxWidth, $t->px(40) * $lines]; } public function value(): ?string { return $this->props['tabs'][(int)$this->props['selected']] ?? null; } /** * x, width, line and y of each tab: the tabs that do not fit (a phone) go on more lines. * * @return list<array{0: float, 1: float, 2: int, 3: float}> */ private function boxes(Theme $t, ?float $width = null, ?float $left = null): array { $font = $t->font(); $left ??= $this->x; $width ??= $this->w; $x = $left + $t->px(10); $line = 0; $out = []; foreach ((array)$this->props['tabs'] as $tab) { $w = $font->width(\JumPHP\Util\Lang::t((string)$tab)) + $t->px(28); if ($x + $w > $left + $width - $t->px(4) && $x > $left + $t->px(10)) { $x = $left + $t->px(10); $line++; } $out[] = [$x, $w, $line, $this->y + $line * $t->px(40)]; $x += $w + $t->px(2); } return $out; } public function render(Renderer $r, Theme $t): void { $r->fillRect($this->x, $this->y, $this->w, $this->h, '#ffffff'); $r->fillRect($this->x, $this->y + $this->h - 1, $this->w, 1, '#e1e5eb'); $font = $t->font(); $bold = $t->font(null, true); $lh = $t->px(40); foreach ($this->boxes($t) as $i => [$x, $w, , $y]) { $selected = $i === (int)$this->props['selected']; $hover = $this->hovered && $this->hoverTab === $i; if ($hover && !$selected) { $t->box($r, $x, $y + $t->px(6), $w, $lh - $t->px(12), '#f0f3f8', $t->px(6)); } if ($selected) { $t->box($r, $x + $t->px(6), $y + $lh - $t->px(3), $w - $t->px(12), $t->px(3), '#2f6fd6', $t->px(1.5)); } $f = $selected ? $bold : $font; $f->draw($r, \JumPHP\Util\Lang::t((string)$this->props['tabs'][$i]), $x + $w / 2, $y + ($lh - $f->height()) / 2, 1, $selected ? '#1d3c6e' : '#57606a', 'center'); } } private int $hoverTab = -1; public function update(float $dt): void { $engine = $this->form?->engineOrNull(); if ($engine === null || !$this->hovered || $this->form === null) { $this->hoverTab = -1; return; } $mx = $engine->input->getMouse()->x; $my = $engine->input->getMouse()->y; $this->hoverTab = -1; $lh = $this->form->theme->px(40); foreach ($this->boxes($this->form->theme) as $i => [$x, $w, , $y]) { if ($mx >= $x && $mx < $x + $w && $my >= $y && $my < $y + $lh) { $this->hoverTab = $i; } } } public function select(int $index): void { $tabs = (array)$this->props['tabs']; if (!isset($tabs[$index])) { return; } if ($index !== (int)$this->props['selected']) { $this->selected = $index; $this->emit('change', $index, (string)$tabs[$index]); } } public function selectTab(string $tab): void { $index = array_search($tab, (array)$this->props['tabs'], true); if ($index !== false) { $this->select((int)$index); } } public function pointerUp(Pointer $p, bool $inside): void { if (!$inside || $this->form === null) { return; } $lh = $this->form->theme->px(40); foreach ($this->boxes($this->form->theme) as $i => [$x, $w, , $y]) { if ($p->x >= $x && $p->x < $x + $w && $p->y >= $y && $p->y < $y + $lh) { $this->select($i); } } $this->emit('click'); }}