diff options
| author | Danilo M. <danix@danix.xyz> | 2026-10-17 13:12:36 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-10-17 13:12:36 +0200 |
| commit | d3717bc4b42c5b0b6c7e8b5055585c1a1ba061aa (patch) | |
| tree | fcc0b12d189143f9cd5f58be53c04d043ca72fb5 /desktop/island/SceneIcons.js | |
| parent | e39df38860ba10b4611572ec721a8be39cf8bcae (diff) | |
| download | quickshell-d3717bc4b42c5b0b6c7e8b5055585c1a1ba061aa.tar.gz quickshell-d3717bc4b42c5b0b6c7e8b5055585c1a1ba061aa.zip | |
island: scene icon map logic and its tests
Diffstat (limited to 'desktop/island/SceneIcons.js')
| -rw-r--r-- | desktop/island/SceneIcons.js | 77 |
1 files changed, 77 insertions, 0 deletions
diff --git a/desktop/island/SceneIcons.js b/desktop/island/SceneIcons.js new file mode 100644 index 0000000..28118b7 --- /dev/null +++ b/desktop/island/SceneIcons.js @@ -0,0 +1,77 @@ +// Copyright (C) 2026 Danilo M. <danix@danix.xyz> +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +.pragma library + +// The starter map, used when the config file is missing or unreadable, and the +// seed for the settings page. The md-* glyphs are supplementary-plane, so they +// are surrogate pairs; a five-digit \u escape would be a different character. +var defaults = { + generic: "\udb80\uded7", // md-help_circle + scenes: { + "Desktop": "\udb80\udf79", // md-monitor + "Webcam Only": "\udb81\udd67", // md-video + "Window Only": "\uf2d0", // fa-window_maximize + "Intervallo": "\udb80\udd76", // md-coffee + }, +}; + +// Exactly one code point; a surrogate pair counts as one. +function validGlyph(s) { + if (typeof s !== "string" || s === "") return false; + let n = 0; + for (let i = 0; i < s.length; i++) { + const c = s.charCodeAt(i); + if (c >= 0xD800 && c <= 0xDBFF && i + 1 < s.length) { + const d = s.charCodeAt(i + 1); + if (d >= 0xDC00 && d <= 0xDFFF) i++; + } + n++; + } + return n === 1; +} + +// The map as stored: an invalid generic falls back, invalid scenes drop. +function normalize(map) { + const src = map || {}; + const out = { + generic: validGlyph(src.generic) ? src.generic : defaults.generic, + scenes: {}, + }; + const sc = src.scenes || {}; + for (const k in sc) if (validGlyph(sc[k])) out.scenes[k] = sc[k]; + return out; +} + +// A missing or unparseable file is the starter map; a valid file is used as-is. +function parse(text) { + let v = null; + try { v = JSON.parse(text); } catch (e) {} + if (!v || typeof v !== "object" || Array.isArray(v)) return normalize(defaults); + return normalize(v); +} + +function serialize(map) { + return JSON.stringify({ generic: map.generic, scenes: map.scenes }, null, 2) + "\n"; +} + +function iconFor(map, name) { + return (map && map.scenes && map.scenes[name]) || (map && map.generic) || defaults.generic; +} + +// A draft may leave the generic empty; Save substitutes the default. +function validMap(map) { + if (!map || typeof map !== "object") return false; + if (map.generic !== "" && !validGlyph(map.generic)) return false; + const sc = map.scenes || {}; + for (const k in sc) if (!validGlyph(sc[k])) return false; + return true; +} |
