PL
apps/caramail-chat / src / Gui / SmileyGrid.php · commit 6eebcdb
<?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);    }}