Introduce resolution-aware UI scaling to keep fonts, glyphs, and panel chrome readable and aligned above 1920×1080. Adds a new components/layout.js that sets --co-ui-scale (baseline 1920×1080) on load/resize and exposes CO_Layout.update; index.html now loads it and app.js triggers updates onBrowserShown and on window resize. styles.css converts many fixed px values to calc()/em driven by --co-ui-scale, removes the old pixel-floor media queries and clamp caps, and scales borders, spacing, and font sizes. controller-glyphs.js multiplies glyph dimensions by the UI scale so controller icons grow correctly. Changes address small/drifting server browser content on high-res and ultrawide displays.
231 lines
5.6 KiB
JavaScript
231 lines
5.6 KiB
JavaScript
(function () {
|
|
"use strict";
|
|
|
|
var KEY_ALIASES = {
|
|
LB: "lb",
|
|
RB: "rb",
|
|
A: "a",
|
|
B: "b",
|
|
X: "x",
|
|
Y: "y"
|
|
};
|
|
|
|
var GLYPH_SIZES = {
|
|
a: { w: 28, h: 28 },
|
|
b: { w: 28, h: 28 },
|
|
x: { w: 28, h: 28 },
|
|
y: { w: 28, h: 28 },
|
|
lb: { w: 34, h: 34 },
|
|
rb: { w: 34, h: 34 }
|
|
};
|
|
|
|
var PLATFORMS = {
|
|
Xbox: {
|
|
base: "./assets/icons/Xbox/",
|
|
files: {
|
|
a: "T_X_A_White_Alt.png",
|
|
b: "T_X_B_White_Alt.png",
|
|
x: "T_X_X_White_Alt.png",
|
|
y: "T_X_Y_White_Alt.png",
|
|
lb: "T_X_LB_Alt.png",
|
|
rb: "T_X_RB_Alt.png"
|
|
}
|
|
},
|
|
Playstation: {
|
|
base: "./assets/icons/Playstation/",
|
|
files: {
|
|
a: "T_P5_Cross_Alt.png",
|
|
b: "T_P5_Circle_Alt.png",
|
|
x: "T_P5_Square_Alt.png",
|
|
y: "T_P5_Triangle_Alt.png",
|
|
lb: "T_P5_L1_Alt.png",
|
|
rb: "T_P5_R1_Alt.png"
|
|
}
|
|
},
|
|
Switch: {
|
|
base: "./assets/icons/Switch/",
|
|
files: {
|
|
a: "T_S_A_Alt.png",
|
|
b: "T_S_B_Alt.png",
|
|
x: "T_S_X_Alt.png",
|
|
y: "T_S_Y_Alt.png",
|
|
lb: "T_S_LB_Alt.png",
|
|
rb: "T_S_RB_Alt.png"
|
|
}
|
|
}
|
|
};
|
|
|
|
var activePlatform = "Xbox";
|
|
var tintCache = {};
|
|
var WHITE_THRESHOLD = 200;
|
|
var BLACK_THRESHOLD = 40;
|
|
|
|
function glyphUrl(glyphId) {
|
|
var platform = PLATFORMS[activePlatform] || PLATFORMS.Xbox;
|
|
var file = platform.files[glyphId];
|
|
if (!file) {
|
|
return null;
|
|
}
|
|
return platform.base + file;
|
|
}
|
|
|
|
function getTintColor() {
|
|
var style = getComputedStyle(document.documentElement);
|
|
return {
|
|
r: parseInt(style.getPropertyValue("--co-r"), 10) || 18,
|
|
g: parseInt(style.getPropertyValue("--co-g"), 10) || 255,
|
|
b: parseInt(style.getPropertyValue("--co-b"), 10) || 21
|
|
};
|
|
}
|
|
|
|
function getUiScale() {
|
|
var style = getComputedStyle(document.documentElement);
|
|
var scale = parseFloat(style.getPropertyValue("--co-ui-scale"));
|
|
return isFinite(scale) && scale > 0 ? scale : 1;
|
|
}
|
|
|
|
function tintCacheKey(url, color) {
|
|
return url + "|" + color.r + "," + color.g + "," + color.b;
|
|
}
|
|
|
|
function classifyPixel(r, g, b) {
|
|
if (r >= WHITE_THRESHOLD && g >= WHITE_THRESHOLD && b >= WHITE_THRESHOLD) {
|
|
return "white";
|
|
}
|
|
if (r <= BLACK_THRESHOLD && g <= BLACK_THRESHOLD && b <= BLACK_THRESHOLD) {
|
|
return "black";
|
|
}
|
|
return "edge";
|
|
}
|
|
|
|
function tintImageData(imageData, color) {
|
|
var data = imageData.data;
|
|
var tr = color.r;
|
|
var tg = color.g;
|
|
var tb = color.b;
|
|
|
|
for (var i = 0; i < data.length; i += 4) {
|
|
var alpha = data[i + 3];
|
|
if (alpha < 4) {
|
|
data[i + 3] = 0;
|
|
continue;
|
|
}
|
|
|
|
var r = data[i];
|
|
var g = data[i + 1];
|
|
var b = data[i + 2];
|
|
var kind = classifyPixel(r, g, b);
|
|
|
|
if (kind === "white") {
|
|
data[i] = tr;
|
|
data[i + 1] = tg;
|
|
data[i + 2] = tb;
|
|
} else if (kind === "black") {
|
|
data[i] = 0;
|
|
data[i + 1] = 0;
|
|
data[i + 2] = 0;
|
|
} else {
|
|
var lum = (r + g + b) / (3 * 255);
|
|
data[i] = Math.round(lum * tr);
|
|
data[i + 1] = Math.round(lum * tg);
|
|
data[i + 2] = Math.round(lum * tb);
|
|
}
|
|
}
|
|
|
|
return imageData;
|
|
}
|
|
|
|
function tintImage(url, color, done) {
|
|
var key = tintCacheKey(url, color);
|
|
if (tintCache[key]) {
|
|
done(tintCache[key]);
|
|
return;
|
|
}
|
|
|
|
var img = new Image();
|
|
img.onload = function () {
|
|
var canvas = document.createElement("canvas");
|
|
canvas.width = img.naturalWidth;
|
|
canvas.height = img.naturalHeight;
|
|
var ctx = canvas.getContext("2d");
|
|
if (!ctx) {
|
|
done(null);
|
|
return;
|
|
}
|
|
|
|
ctx.drawImage(img, 0, 0);
|
|
var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
|
ctx.putImageData(tintImageData(imageData, color), 0, 0);
|
|
var dataUrl = canvas.toDataURL("image/png");
|
|
tintCache[key] = dataUrl;
|
|
done(dataUrl);
|
|
};
|
|
img.onerror = function () {
|
|
done(null);
|
|
};
|
|
img.src = url;
|
|
}
|
|
|
|
function hydrateGlyphs(root) {
|
|
var scope = root || document;
|
|
var color = getTintColor();
|
|
var imgs = scope.querySelectorAll(".co-glyph-img[data-glyph-src]");
|
|
|
|
for (var i = 0; i < imgs.length; i++) {
|
|
(function (img) {
|
|
var src = img.getAttribute("data-glyph-src");
|
|
if (!src) {
|
|
return;
|
|
}
|
|
tintImage(src, color, function (dataUrl) {
|
|
if (dataUrl) {
|
|
img.src = dataUrl;
|
|
}
|
|
});
|
|
})(imgs[i]);
|
|
}
|
|
}
|
|
|
|
window.CO_ControllerGlyphs = {
|
|
setPlatform: function (platformName) {
|
|
if (PLATFORMS[platformName]) {
|
|
activePlatform = platformName;
|
|
tintCache = {};
|
|
}
|
|
},
|
|
|
|
getPlatform: function () {
|
|
return activePlatform;
|
|
},
|
|
|
|
hydrate: hydrateGlyphs,
|
|
|
|
onThemeApplied: function () {
|
|
tintCache = {};
|
|
hydrateGlyphs(document);
|
|
},
|
|
|
|
render: function (key) {
|
|
var glyphId = KEY_ALIASES[String(key || "").toUpperCase()];
|
|
if (!glyphId) {
|
|
return '<span class="co-prompt-key co-prompt-key--fallback">' + key + "</span>";
|
|
}
|
|
|
|
var size = GLYPH_SIZES[glyphId] || GLYPH_SIZES.a;
|
|
var scale = getUiScale();
|
|
var url = glyphUrl(glyphId);
|
|
if (!url) {
|
|
return '<span class="co-prompt-key co-prompt-key--fallback">' + key + "</span>";
|
|
}
|
|
|
|
return (
|
|
'<span class="co-prompt-key co-prompt-key--glyph co-prompt-key--' + glyphId + '"' +
|
|
' style="width:' + (size.w * scale) + "px;height:" + (size.h * scale) + 'px"' +
|
|
' aria-hidden="true">' +
|
|
'<img class="co-glyph-img" data-glyph-src="' + url + '" src="' + url + '" alt="" draggable="false" />' +
|
|
"</span>"
|
|
);
|
|
}
|
|
};
|
|
})();
|