<?phpnamespace App\Gui;use JumPHP\Renderer\Renderer;/** * The little pictures of the tree, the tabs and the toolbar, drawn with rectangles and circles (no image file): XenCenter's * meanings kept: a VM is a screen whose badge says its power state (green = running, red = halted, a pause = paused / suspended), * a host is a server box, a pool a stack of them, an SR a disk, a network a plug. */final class Icons{ public const RUNNING = '#2ea043'; public const HALTED = '#d1242f'; public const PAUSED = '#d29922'; /** $kind: xencenter, pool, host, host-off, host-maint, vm-running, vm-halted, vm-paused, vm-suspended, template, snapshot, sr, sr-iso, network, folder, disconnected, connecting... */ public static function draw(Renderer $r, string $kind, float $x, float $y, float $s): void { $u = $s / 16; $rect = fn (float $a, float $b, float $w, float $h, string $c) => $r->fillRect($x + $a * $u, $y + $b * $u, max(1, $w * $u), max(1, $h * $u), $c); $dot = fn (float $a, float $b, float $rad, string $c) => $r->fillCircle($x + $a * $u, $y + $b * $u, max(1, $rad * $u), $c); switch ($kind) { case 'xencenter': $rect(1, 2, 14, 12, '#1b4f8a'); $rect(2, 3, 12, 10, '#3a7bd5'); $rect(4, 5, 3, 6, '#ffffff'); $rect(9, 5, 3, 6, '#ffffff'); $rect(6, 7, 4, 2, '#ffffff'); return; case 'pool': case 'pool-off': foreach ([0, 4, 8] as $k => $dy) { $rect(2 + $k, 1 + $dy, 11, 5, '#5b6b7f'); $rect(3 + $k, 2 + $dy, 9, 3, $kind === 'pool' ? '#a9c2e0' : '#c9ced6'); $rect(9 + $k, 3 + $dy, 2, 1, $kind === 'pool' ? self::RUNNING : '#888888'); } return; case 'host': case 'host-off': case 'host-maint': case 'disconnected': case 'connecting': $rect(2, 2, 12, 12, '#46566b'); $rect(3, 3, 10, 3, '#b8c9de'); $rect(3, 7, 10, 3, '#b8c9de'); $rect(3, 11, 10, 2, '#8da2bb'); $rect(10, 4, 2, 1, '#2b3a4c'); $rect(10, 8, 2, 1, '#2b3a4c'); $badge = match ($kind) {'host' => self::RUNNING, 'host-off', 'disconnected' => self::HALTED, 'host-maint' => self::PAUSED, default => '#3a7bd5'}; if ($kind === 'host') { $dot(13, 13, 2.6, '#ffffff'); $dot(13, 13, 1.9, $badge); } elseif ($kind === 'host-maint') { $rect(10, 10, 6, 6, '#ffffff'); $rect(11, 11, 4, 4, $badge); $rect(12.5, 11.5, 1, 3, '#ffffff'); } else { $dot(13, 13, 2.6, '#ffffff'); $dot(13, 13, 1.9, $badge); } return; case 'vm-running': case 'vm-halted': case 'vm-paused': case 'vm-suspended': case 'template': case 'snapshot': case 'vm': $frame = $kind === 'template' ? '#7a6a9e' : ($kind === 'snapshot' ? '#6b8f71' : '#3d5a80'); $rect(1, 2, 13, 10, $frame); $rect(2, 3, 11, 8, $kind === 'template' ? '#d7cef0' : ($kind === 'snapshot' ? '#cfe8d3' : '#cfe0f5')); $rect(5, 12, 5, 1.5, $frame); $rect(3.5, 13.5, 8, 1.5, $frame); if ($kind === 'vm-running') { $dot(12.5, 12.5, 3.2, '#ffffff'); $r->fillRect($x + 11.2 * $u, $y + 10.6 * $u, 1.2 * $u, 3.8 * $u, self::RUNNING); $r->fillRect($x + 12.4 * $u, $y + 11.2 * $u, 1.2 * $u, 2.6 * $u, self::RUNNING); $r->fillRect($x + 13.6 * $u, $y + 11.9 * $u, 1.0 * $u, 1.2 * $u, self::RUNNING); } elseif ($kind === 'vm-halted') { $dot(12.5, 12.5, 3.2, '#ffffff'); $rect(10.7, 10.7, 3.6, 3.6, self::HALTED); } elseif ($kind === 'vm-paused' || $kind === 'vm-suspended') { $dot(12.5, 12.5, 3.2, '#ffffff'); $rect(10.8, 10.6, 1.3, 3.8, $kind === 'vm-paused' ? self::PAUSED : '#3a7bd5'); $rect(12.9, 10.6, 1.3, 3.8, $kind === 'vm-paused' ? self::PAUSED : '#3a7bd5'); } elseif ($kind === 'snapshot') { $dot(12.5, 12.5, 3.2, '#ffffff'); $rect(10.3, 11.3, 4.4, 3, '#3f6b47'); $rect(11.6, 10.4, 1.8, 1, '#3f6b47'); } return; case 'sr': case 'sr-iso': case 'sr-shared': case 'sr-broken': $rect(2, 4, 12, 9, '#6c757d'); $rect(3, 5, 10, 7, $kind === 'sr-iso' ? '#f1e3b6' : '#dfe4ea'); $rect(3, 9, 10, 1, '#9aa3ad'); $rect(10, 10.5, 2, 1, $kind === 'sr-broken' ? self::HALTED : self::RUNNING); if ($kind === 'sr-iso') { $dot(6, 7.5, 2, '#b8902b'); $dot(6, 7.5, 0.7, '#f1e3b6'); } if ($kind === 'sr-shared') { $rect(5, 13, 6, 1, '#6c757d'); $rect(7.5, 13, 1, 2, '#6c757d'); } return; case 'network': $rect(3, 2, 10, 7, '#2f6f9f'); $rect(4, 3, 8, 5, '#8cc4ea'); $rect(7.5, 9, 1, 3, '#2f6f9f'); $rect(2, 12, 12, 2, '#2f6f9f'); return; case 'nic': $rect(1, 4, 14, 8, '#2c6e49'); $rect(3, 6, 4, 4, '#d8f3dc'); $rect(9, 6, 4, 4, '#d8f3dc'); $rect(3, 12, 1, 2, '#c9a227'); $rect(6, 12, 1, 2, '#c9a227'); $rect(9, 12, 1, 2, '#c9a227'); $rect(12, 12, 1, 2, '#c9a227'); return; case 'disk': $rect(2, 3, 12, 10, '#5a6474'); $dot(8, 8, 3.8, '#c3cad4'); $dot(8, 8, 1.2, '#5a6474'); return; case 'folder': $rect(1, 4, 6, 2, '#d8a31a'); $rect(1, 5, 14, 9, '#f2c14e'); return; case 'tag': // a label: a body and a pointed end, its hole $rect(1, 5, 9, 7, '#2f8f6f'); for ($k = 0; $k < 4; $k++) { $rect(10 + $k, 5 + $k * 0.9, 1, 7 - $k * 1.8, '#2f8f6f'); } $dot(4, 8.5, 1.3, '#ffffff'); return; case 'alert': $dot(8, 8, 7, '#d1242f'); $rect(7, 3.5, 2, 6, '#ffffff'); $rect(7, 11, 2, 2, '#ffffff'); return; case 'warning': $dot(8, 8, 7, '#d29922'); $rect(7, 3.5, 2, 6, '#ffffff'); $rect(7, 11, 2, 2, '#ffffff'); return; case 'info': $dot(8, 8, 7, '#3a7bd5'); $rect(7, 3, 2, 2, '#ffffff'); $rect(7, 6.5, 2, 6.5, '#ffffff'); return; case 'ok': $dot(8, 8, 7, self::RUNNING); $rect(4, 8, 2, 2, '#ffffff'); $rect(6, 10, 2, 2, '#ffffff'); $rect(8, 8, 2, 2, '#ffffff'); $rect(10, 6, 2, 2, '#ffffff'); $rect(12, 4, 2, 2, '#ffffff'); return; // toolbar case 'back': case 'forward': $dot(8, 8, 7, '#3a7bd5'); $dir = $kind === 'back' ? -1 : 1; for ($i = 0; $i < 5; $i++) { $rect(8 - 2.5 * $dir + $i * $dir - 0.5, 8 - (4 - $i) - 0.5 + ($dir < 0 ? 0 : 0), 1.2, 2 * (4 - $i) + 1, '#ffffff'); } return; case 'add-server': self::draw($r, 'host', $x, $y, $s * 0.85); $rect(10, 1, 6, 2, self::RUNNING); $rect(12, -1, 2, 6, self::RUNNING); return; case 'new-pool': self::draw($r, 'pool', $x, $y, $s * 0.85); $rect(10, 1, 6, 2, self::RUNNING); $rect(12, -1, 2, 6, self::RUNNING); return; case 'new-sr': self::draw($r, 'sr', $x, $y, $s * 0.85); $rect(10, 1, 6, 2, self::RUNNING); $rect(12, -1, 2, 6, self::RUNNING); return; case 'new-vm': self::draw($r, 'vm', $x, $y, $s * 0.85); $rect(10, 1, 6, 2, self::RUNNING); $rect(12, -1, 2, 6, self::RUNNING); return; case 'power-off': $dot(8, 8, 7, self::HALTED); $dot(8, 8, 4.5, '#ffffff'); $dot(8, 8, 3, self::HALTED); $rect(7, 1.5, 2, 6, '#ffffff'); $rect(7.4, 2, 1.2, 5, self::HALTED); return; case 'power-on': $dot(8, 8, 7, self::RUNNING); for ($i = 0; $i < 6; $i++) { $rect(5.5 + $i, 4 + $i * 0.67, 1.2, 8 - $i * 1.33, '#ffffff'); } return; case 'reboot': $dot(8, 8, 7, '#d29922'); $dot(8, 8, 4.5, '#ffffff'); $dot(8, 8, 3, '#d29922'); $rect(8, 2.5, 5, 4, '#d29922'); $rect(10, 3, 3, 1.5, '#ffffff'); $rect(11.5, 3, 1.5, 3, '#ffffff'); return; case 'suspend': $dot(8, 8, 7, '#3a7bd5'); $rect(5, 4.5, 2, 7, '#ffffff'); $rect(9, 4.5, 2, 7, '#ffffff'); return; case 'resume': $dot(8, 8, 7, '#3a7bd5'); for ($i = 0; $i < 6; $i++) { $rect(5.5 + $i, 4 + $i * 0.67, 1.2, 8 - $i * 1.33, '#ffffff'); } return; case 'migrate': $dot(8, 8, 7, '#7a6a9e'); $rect(3, 7, 8, 2, '#ffffff'); for ($i = 0; $i < 4; $i++) { $rect(10 + $i, 4.5 + $i, 1, 7 - 2 * $i, '#ffffff'); } return; case 'console': $rect(1, 2, 14, 11, '#22272e'); $rect(2, 3, 12, 9, '#0d1117'); $rect(3, 5, 2, 1, '#3fb950'); $rect(5, 6, 2, 1, '#3fb950'); $rect(3, 7, 2, 1, '#3fb950'); $rect(8, 8, 4, 1, '#c9d1d9'); return; default: $rect(3, 3, 10, 10, '#8a94a3'); } } /** The icon of an XAPI object of a cache. @param array<string, mixed> $record */ public static function of(string $class, array $record, ?\App\Model\Cache $cache = null): string { return match ($class) { 'pool' => 'pool', 'host' => !($record['enabled'] ?? true) ? 'host-maint' : (($cache?->get((string)($record['metrics'] ?? ''))['live'] ?? true) ? 'host' : 'host-off'), 'vm' => !empty($record['is_a_snapshot']) ? 'snapshot' : (!empty($record['is_a_template']) ? 'template' : match ((string)($record['power_state'] ?? '')) { 'Running' => 'vm-running', 'Paused' => 'vm-paused', 'Suspended' => 'vm-suspended', default => 'vm-halted'}), 'sr' => ($record['content_type'] ?? '') === 'iso' ? 'sr-iso' : (!empty($record['shared']) ? 'sr-shared' : 'sr'), 'network' => 'network', 'pif' => 'nic', 'vdi' => 'disk', default => 'folder', }; }}