<?phpnamespace App\Gui;use App\Chat\Smileys;use JumPHP\Gui\Pointer;use JumPHP\Gui\Theme;use JumPHP\Gui\Widget;use JumPHP\Renderer\Renderer;/** * The applet's smileys panel (net.spray.chat.a.c): the pictures of smiley.gif on white, in cells of at least 38 x 24 (a * 32 x 20 picture and its margins), as many columns as the width holds, spread over the whole width, a scrollbar-less list here (the panel * scrolls with the wheel). A click gives "@NN". Event: change (string $code). */final class SmileyGrid extends Widget{ public const DEFAULTS = ['width' => 'fill', 'height' => 'fill']; private const CELL_W = 38; private const CELL_H = 24; public int $hover = -1; public float $scrollY = 0; public static function events(): array { return ['change', 'click']; } public static function code(int $n): string { return '@' . str_pad((string)$n, 2, '0', STR_PAD_LEFT); } private function columns(Theme $t): int { return max(1, (int)floor(($this->w - $t->px(2)) / $t->px(self::CELL_W))); } protected function content(Theme $t, float $maxWidth, string $breakpoint): array { $cols = max(1, (int)floor(($maxWidth - $t->px(2)) / $t->px(self::CELL_W))); return [$maxWidth, (int)ceil(Smileys::IMAGES / $cols) * $t->px(self::CELL_H) + $t->px(4)]; } /** The cells share the whole width: the columns that fit, the leftover spread between them (no empty strip at the right). */ private function cellWidth(Theme $t): float { return ($this->w - $t->px(2)) / $this->columns($t); } private function contentHeight(Theme $t): float { return (int)ceil(Smileys::IMAGES / $this->columns($t)) * $t->px(self::CELL_H) + $t->px(4); } public function render(Renderer $r, Theme $t): void { $r->fillRect($this->x, $this->y, $this->w, $this->h, '#ffffff'); $cols = $this->columns($t); $cellW = $this->cellWidth($t); $this->scrollY = max(0, min($this->scrollY, $this->contentHeight($t) - $this->h)); for ($i = 0; $i < Smileys::IMAGES; $i++) { $cx = $this->x + $t->px(1) + ($i % $cols) * $cellW + ($cellW - $t->px(self::CELL_W)) / 2; $cy = $this->y + $t->px(1) + intdiv($i, $cols) * $t->px(self::CELL_H) - $this->scrollY; if ($cy < $this->y || $cy + $t->px(self::CELL_H) > $this->y + $this->h) { continue; } SmileyPainter::drawImage($r, $this->form?->sdlOrNull(), $i, $cx + $t->px(3), $cy + $t->px(2), $t->scale); } $r->drawRect($this->x, $this->y, $this->w, $this->h, Awt::SHADOW, Awt::px($t)); } public function wheel(float $notches): bool { if ($this->form === null) { return false; } $before = $this->scrollY; $this->scrollY -= $notches * $this->form->theme->px(self::CELL_H); $this->scrollY = max(0, min($this->scrollY, $this->contentHeight($this->form->theme) - $this->h)); return $this->scrollY !== $before; } private function indexAt(float $px, float $py): int { $t = $this->form?->theme; if ($t === null) { return -1; } $col = (int)floor(($px - $this->x - $t->px(1)) / $this->cellWidth($t)); $row = (int)floor(($py - $this->y - $t->px(1) + $this->scrollY) / $t->px(self::CELL_H)); $i = $row * $this->columns($t) + $col; return ($col >= 0 && $col < $this->columns($t) && $row >= 0 && $i < Smileys::IMAGES) ? $i : -1; } public function pointerUp(Pointer $p, bool $inside): void { $i = $inside ? $this->indexAt($p->x, $p->y) : -1; if ($i >= 0) { $this->emit('change', self::code($i)); } $this->click($p); }}