로그인
KO
apps/caramail-chat / src / Gui / Awt.php · commit 6eebcdb
<?phpnamespace App\Gui;use JumPHP\Gui\Form;use JumPHP\Gui\Theme;use JumPHP\Renderer\Renderer;/** * The look of AWT 1.1 on Windows (what the 2002 applet ran in): grey #c0c0c0, 3D bevels drawn with white, #808080 * and black lines, Helvetica 12. Shared by the widgets of this folder. */final class Awt{    public const FACE = '#c0c0c0';    public const LIGHT = '#ffffff';    public const SHADOW = '#808080';    public const DARK = '#000000';    public const ORANGE = '#ffc800';   // java.awt.Color.orange: the "warning" buttons of the applet    /** The colour of the panels and buttons: the applet's `color` parameter (grey by default), the colour chosen in the Options' picker. */    public static string $face = self::FACE;    /** The theme of every window of the app: AWT colours, small square controls. */    public static function theme(Form $form): void    {        $t = $form->theme;        $t->colors = [            'background' => self::$face, 'surface' => '#ffffff', 'text' => '#000000', 'muted' => '#404040', 'border' => self::SHADOW,            'accent' => '#000080', 'accentText' => '#ffffff', 'hover' => '#d4d0c8', 'pressed' => '#a0a0a0', 'focus' => self::SHADOW,            'selection' => '#a6caf0', 'danger' => '#ff0000', 'disabled' => self::SHADOW, 'track' => self::$face,        ];        $t->radius = 0;        $t->fontSize = 12;        $t->controlHeight = 22;        $t->padding = 4;    }    /** The sizes (dp) the interface writes in: 10 the choices and tooltips, 12 everything else, 14 / 16 the talk's big fonts. */    private const SIZES = [10, 11, 12, 13, 14, 16];    /**     * The theme's cache is filled with Liberation Sans (Arial's metrics) smoothed with light hinting, at the exact     * pixel sizes used (again when the screen's scale changes): the look of the applet as it shows today.     */    /** Languages whose letters are not in Liberation Sans: Droid Sans Fallback (assets/fonts/cjk.ttf) instead. */    public const CJK = ['ja', 'zh', 'ko'];    public static function crisp(Form $form): void    {        $sdl = $form->sdlOrNull();        $t = $form->theme;        $lang = $form->engineOrNull()?->lang->getLang() ?? 'fr';        $cjk = in_array(substr($lang, 0, 2), self::CJK, true);        if ($sdl === null || !function_exists('TTF_OpenFont') && !extension_loaded('sdl_ttf')) {            return;        }        $put = \Closure::bind(function (string $key, \JumPHP\Renderer\TextDrawer $font): void {            $this->fonts[$key] = $font;        }, $t, Theme::class);        foreach (self::SIZES as $dp) {            foreach ([false, true] as $bold) {                $px = max(6, (int)round($dp * $t->scale));                try {                    $file = $cjk ? 'assets/fonts/cjk.ttf' : ($bold ? 'assets/fonts/ui-bold.ttf' : 'assets/fonts/ui.ttf');                    $put($px . ($bold ? 'b' : ''), self::lightHinted(new \JumPHP\Renderer\TtfFont($sdl, $file, $px, true, $bold && $cjk ? \JumPHP\Renderer\TtfFont::BOLD : 0)));                } catch (\Throwable) {                    return;                }            }        }    }    /**     * The applet as it shows today (a 2002 Java applet in a browser): Arial 12 px smoothed in grey levels, its stems a     * little soft. FreeType's light hinting (TTF_HINTING_LIGHT) gives that; its normal hinting is sharper and darker.     */    public static function lightHinted(\JumPHP\Renderer\TtfFont $font): \JumPHP\Renderer\TtfFont    {        if (function_exists('TTF_SetFontHinting')) {            \Closure::bind(function (): void {                if ($this->font) {                    \TTF_SetFontHinting($this->font, \defined('TTF_HINTING_LIGHT') ? \TTF_HINTING_LIGHT : 1);                }            }, $font, \JumPHP\Renderer\TtfFont::class)();        }        return $font;    }    /** Calls crisp() when the scale changed since $seen (each frame from the forms' tick()). */    public static function keepCrisp(Form $form, float &$seen): void    {        if ($form->theme->scale !== $seen) {            $seen = $form->theme->scale;            self::crisp($form);            $form->invalidate();      // measured again with these fonts (another language: other widths)        }    }    /** A raised bevel (a button, a scrollbar's thumb); $down: pressed in. */    public static function raised(Renderer $r, float $x, float $y, float $w, float $h, ?string $fill = null, bool $down = false, float $px = 1): void    {        $r->fillRect($x, $y, $w, $h, $fill ?? self::$face);        $x2 = $x + $w - $px;        $y2 = $y + $h - $px;        if ($down) {            $r->fillRect($x, $y, $w, $px, self::DARK);            $r->fillRect($x, $y, $px, $h, self::DARK);            $r->fillRect($x + $px, $y + $px, $w - 2 * $px, $px, self::SHADOW);            $r->fillRect($x + $px, $y + $px, $px, $h - 2 * $px, self::SHADOW);            $r->fillRect($x, $y2, $w, $px, self::LIGHT);            $r->fillRect($x2, $y, $px, $h, self::LIGHT);            return;        }        $r->fillRect($x, $y, $w - $px, $px, self::LIGHT);        $r->fillRect($x, $y, $px, $h - $px, self::LIGHT);        $r->fillRect($x, $y2, $w, $px, self::DARK);        $r->fillRect($x2, $y, $px, $h, self::DARK);        $r->fillRect($x + $px, $y2 - $px, $w - 2 * $px, $px, self::SHADOW);        $r->fillRect($x2 - $px, $y + $px, $px, $h - 2 * $px, self::SHADOW);    }    /** A sunken bevel around a field (a text field, a list, a check box): drawn on its edges. */    public static function sunken(Renderer $r, float $x, float $y, float $w, float $h, float $px = 1): void    {        $r->fillRect($x, $y, $w - $px, $px, self::SHADOW);        $r->fillRect($x, $y, $px, $h - $px, self::SHADOW);        $r->fillRect($x + $px, $y + $px, $w - 3 * $px, $px, self::DARK);        $r->fillRect($x + $px, $y + $px, $px, $h - 3 * $px, self::DARK);        $r->fillRect($x, $y + $h - $px, $w, $px, self::LIGHT);        $r->fillRect($x + $w - $px, $y, $px, $h, self::LIGHT);        $r->fillRect($x + $px, $y + $h - 2 * $px, $w - 2 * $px, $px, self::$face);        $r->fillRect($x + $w - 2 * $px, $y + $px, $px, $h - 2 * $px, self::$face);    }    /** The etched line of the applet's titled panels (net.spray.chat.a.a): grey then white, one pixel apart. */    public static function etched(Renderer $r, float $x, float $y, float $w, float $h, float $px = 1): void    {        $r->drawRect($x + $px, $y + $px, $w - $px, $h - $px, self::LIGHT, (int)$px);        $r->drawRect($x, $y, $w - $px, $h - $px, self::SHADOW, (int)$px);    }    /** A small black triangle (the arrows of the scrollbars and drop-down lists). $dir: up, down, left, right. */    public static function arrow(Renderer $r, float $cx, float $cy, string $dir, float $px = 1, string $color = self::DARK): void    {        for ($i = 0; $i < 4; $i++) {            $len = (7 - 2 * $i) * $px;            match ($dir) {                'up' => $r->fillRect($cx - $len / 2, $cy + (1 - $i) * $px, $len, $px, $color),                'down' => $r->fillRect($cx - $len / 2, $cy + ($i - 2) * $px, $len, $px, $color),                'left' => $r->fillRect($cx + (1 - $i) * $px, $cy - $len / 2, $px, $len, $color),                default => $r->fillRect($cx + ($i - 2) * $px, $cy - $len / 2, $px, $len, $color),            };        }    }    /** A text greyed out the AWT way: white, then grey one pixel up-left. */    public static function disabledText(Renderer $r, Theme $t, \JumPHP\Renderer\TextDrawer $font, string $text, float $x, float $y): void    {        $font->draw($r, $text, $x + $t->px(1), $y + $t->px(1), 1, self::LIGHT);        $font->draw($r, $text, $x, $y, 1, self::SHADOW);    }    public static function px(Theme $t): int    {        return max(1, (int)round($t->scale));    }}