mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-10-20 02:04:01 -04:00
i18n: keep translations when the selected locale resolves to the same file
fixes #3508
This commit is contained in:
@@ -139,15 +139,22 @@ Singleton {
|
||||
|
||||
function useLocale(localeTag, fileUrl) {
|
||||
_resolvedLocale = localeTag || "en";
|
||||
_selectedPath = fileUrl;
|
||||
translationsLoaded = false;
|
||||
translations = ({});
|
||||
_selectTranslationFile(fileUrl);
|
||||
log.info(`I18n: Using locale '${localeTag}' from ${fileUrl}`);
|
||||
if (commonDir.status === FolderListModel.Ready)
|
||||
_pickCommonTranslation();
|
||||
localeApplied();
|
||||
}
|
||||
|
||||
function _selectTranslationFile(fileUrl) {
|
||||
const previousPath = String(_selectedPath);
|
||||
_selectedPath = fileUrl;
|
||||
if (translationsLoaded && String(_selectedPath) === previousPath)
|
||||
return;
|
||||
translationsLoaded = false;
|
||||
translations = ({});
|
||||
}
|
||||
|
||||
function _fallbackToEnglish() {
|
||||
_resolvedLocale = "en";
|
||||
_selectedPath = "";
|
||||
|
||||
@@ -1,321 +0,0 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import qs.Common
|
||||
import qs.Modules.DankBar
|
||||
import qs.DankCommon.Common as DC
|
||||
|
||||
ShellRoot {
|
||||
id: root
|
||||
|
||||
readonly property var steps: [
|
||||
{
|
||||
position: 0,
|
||||
mode: "fit",
|
||||
goth: true
|
||||
},
|
||||
{
|
||||
position: 0,
|
||||
mode: "fit",
|
||||
goth: false
|
||||
},
|
||||
{
|
||||
position: 2,
|
||||
mode: "fit",
|
||||
goth: true
|
||||
},
|
||||
{
|
||||
position: 1,
|
||||
mode: "fit",
|
||||
goth: true
|
||||
},
|
||||
{
|
||||
position: 3,
|
||||
mode: "fit",
|
||||
goth: true
|
||||
},
|
||||
{
|
||||
position: 0,
|
||||
mode: "fit",
|
||||
goth: true,
|
||||
center: false
|
||||
},
|
||||
{
|
||||
position: 0,
|
||||
mode: "percent",
|
||||
goth: true
|
||||
},
|
||||
{
|
||||
position: 2,
|
||||
mode: "percent",
|
||||
goth: true
|
||||
},
|
||||
{
|
||||
position: 0,
|
||||
mode: "fit",
|
||||
goth: true,
|
||||
wide: true
|
||||
},
|
||||
{
|
||||
position: 0,
|
||||
mode: "percent",
|
||||
goth: false,
|
||||
wide: true,
|
||||
percent: 100
|
||||
},
|
||||
{
|
||||
position: 0,
|
||||
mode: "full",
|
||||
goth: true
|
||||
}
|
||||
]
|
||||
property int step: 0
|
||||
readonly property var current: steps[step]
|
||||
readonly property bool vertical: current.position === SettingsData.Position.Left || current.position === SettingsData.Position.Right
|
||||
readonly property int wingRadius: 16
|
||||
property var barConfig: ({
|
||||
id: "fixture",
|
||||
enabled: true,
|
||||
screenPreferences: ["all"],
|
||||
position: current.position,
|
||||
spacing: 0,
|
||||
innerPadding: 4,
|
||||
barLengthMode: current.mode,
|
||||
wide: current.wide === true,
|
||||
barLengthPercent: current.percent ?? 80,
|
||||
gothCornersEnabled: current.goth,
|
||||
gothCornerRadiusOverride: true,
|
||||
gothCornerRadiusValue: wingRadius
|
||||
})
|
||||
property var hyprlandOverviewLoader: null
|
||||
property bool systemTrayMenuOpen: false
|
||||
|
||||
ScriptModel {
|
||||
id: widgets
|
||||
values: [
|
||||
{
|
||||
widgetId: "clock",
|
||||
id: "clock_0"
|
||||
}
|
||||
]
|
||||
}
|
||||
ScriptModel {
|
||||
id: pairedWidgets
|
||||
values: [
|
||||
{
|
||||
widgetId: "clock",
|
||||
id: "clock_1"
|
||||
},
|
||||
{
|
||||
widgetId: "clock",
|
||||
id: "clock_2"
|
||||
}
|
||||
]
|
||||
}
|
||||
ScriptModel {
|
||||
id: noWidgets
|
||||
values: []
|
||||
}
|
||||
ScriptModel {
|
||||
id: wideWidgets
|
||||
values: [
|
||||
{
|
||||
widgetId: "clock",
|
||||
id: "clock_3"
|
||||
},
|
||||
{
|
||||
widgetId: "clock",
|
||||
id: "clock_4"
|
||||
},
|
||||
{
|
||||
widgetId: "clock",
|
||||
id: "clock_5"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
FloatingWindow {
|
||||
id: window
|
||||
visible: true
|
||||
implicitWidth: 800
|
||||
implicitHeight: 600
|
||||
|
||||
Item {
|
||||
width: root.vertical ? body.surfaceImplicitWidth : 800
|
||||
height: root.vertical ? 600 : body.surfaceImplicitHeight
|
||||
|
||||
DankBarBody {
|
||||
id: body
|
||||
anchors.fill: parent
|
||||
rootWindow: root
|
||||
hostWindow: window
|
||||
modelData: Quickshell.screens[0]
|
||||
barConfig: root.barConfig
|
||||
leftWidgetsModel: root.current.wide ? wideWidgets : widgets
|
||||
centerWidgetsModel: root.current.center === false ? noWidgets : widgets
|
||||
rightWidgetsModel: pairedWidgets
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function check(condition, message) {
|
||||
if (!condition)
|
||||
throw new Error(`step ${step}: ${message}`);
|
||||
}
|
||||
|
||||
function find(item, predicate, out) {
|
||||
if (predicate(item))
|
||||
out.push(item);
|
||||
for (const child of item.children || [])
|
||||
find(child, predicate, out);
|
||||
return out;
|
||||
}
|
||||
|
||||
function rectOf(item) {
|
||||
const point = item.mapToItem(body, 0, 0);
|
||||
return {
|
||||
x: point.x,
|
||||
y: point.y,
|
||||
width: item.width,
|
||||
height: item.height,
|
||||
right: point.x + item.width,
|
||||
bottom: point.y + item.height,
|
||||
discX: item.discRect ? point.x + item.discRect.x + item.discRect.width / 2 : 0,
|
||||
discY: item.discRect ? point.y + item.discRect.y + item.discRect.height / 2 : 0
|
||||
};
|
||||
}
|
||||
|
||||
function near(a, b) {
|
||||
return Math.abs(a - b) <= 1.5;
|
||||
}
|
||||
|
||||
function checkStep() {
|
||||
const content = body._leftSection.barContent;
|
||||
const mask = rectOf(body.inputMaskItem);
|
||||
const surface = rectOf(find(body, item => item.motion !== undefined && item.topLeftRadius !== undefined, [])[0]);
|
||||
const wings = find(body, item => typeof item.corner === "string" && item.discRect !== undefined && item.visible && item.radius > 0, []).map(rectOf);
|
||||
const along = item => root.vertical ? item.height : item.width;
|
||||
const length = root.vertical ? 600 : 800;
|
||||
const instance = ShellLayout.forScreen(Quickshell.screens[0]).instances.find(instance => instance.barId === "fixture");
|
||||
const thickness = body.effectiveBarThickness + body.effectiveSpacing;
|
||||
const sections = [body._leftSection, body._centerSection, body._rightSection].map(rectOf);
|
||||
const startEdge = rect => root.vertical ? rect.y : rect.x;
|
||||
const endEdge = rect => root.vertical ? rect.bottom : rect.right;
|
||||
|
||||
if (current.mode === "full") {
|
||||
check(near(along(mask), length), `full mask spans the edge, got ${along(mask)}`);
|
||||
check(near(instance.rowThickness, thickness + root.wingRadius), `full bar row includes the cross wing, got ${instance.rowThickness}`);
|
||||
check(wings.length === 2 && wings.every(wing => near(wing.y, surface.bottom)), "full bar wings hang below the surface");
|
||||
return;
|
||||
}
|
||||
|
||||
check(near(along(surface), along(mask)) && near(startEdge(surface), startEdge(mask)), "surface matches the mask");
|
||||
check(near(root.vertical ? surface.width : surface.height, thickness), `no cross wing thickens the shortened surface, got ${root.vertical ? surface.width : surface.height}`);
|
||||
check(near(instance.rowThickness, thickness), `shortened row excludes the wing, got ${instance.rowThickness}`);
|
||||
check([sections[0], sections[2]].every(section => section.x >= surface.x - 1 && section.right <= surface.right + 1 && section.y >= surface.y - 1 && section.bottom <= surface.bottom + 1), "side sections stay inside the surface");
|
||||
check(near(startEdge(mask), startEdge(sections[0]) - content.fittedStartMargin) && near(endEdge(mask), endEdge(sections[2]) + content.fittedEndMargin), `mask hugs the outer sections plus the inset, got ${JSON.stringify(mask)}`);
|
||||
|
||||
if (current.mode === "percent") {
|
||||
const share = (current.percent ?? 80) / 100;
|
||||
check(near(along(mask), length * share), `percent mask spans ${share} of the edge, got ${along(mask)}`);
|
||||
check(near(startEdge(mask), length * (1 - share) / 2), `percent mask is centered, got ${startEdge(mask)}`);
|
||||
const center = body._centerSection;
|
||||
const coreStart = startEdge(sections[1]) + center.contentStart;
|
||||
const coreMid = coreStart + center.contentSize / 2;
|
||||
if (current.wide) {
|
||||
check(coreMid > length / 2 + 5 && near(coreStart - endEdge(sections[0]), content.sectionGap), `wide left pushes the center right by one widget spacing, got ${coreMid} and ${coreStart - endEdge(sections[0])}`);
|
||||
check(endEdge(sections[0]) + content.sectionGap <= coreStart + 0.5 && coreStart + center.contentSize + content.sectionGap <= startEdge(sections[2]) + 0.5, "no section overlaps the center");
|
||||
} else {
|
||||
check(near(coreMid, length / 2), `percent keeps the center on the screen center, got ${coreMid}`);
|
||||
}
|
||||
} else {
|
||||
const center = body._centerSection;
|
||||
const coreStart = startEdge(sections[1]) + center.contentStart;
|
||||
const coreEnd = coreStart + center.contentSize;
|
||||
if (current.center === false) {
|
||||
check(center.contentSize === 0, "empty center has no extent");
|
||||
const groupMid = (startEdge(sections[0]) + endEdge(sections[2])) / 2;
|
||||
check(near(groupMid, length / 2), `side sections center on the edge without center widgets, got ${groupMid}`);
|
||||
} else if (current.wide) {
|
||||
check((coreStart + coreEnd) / 2 > length / 2 + 5 && near(startEdge(mask), 0), `wide left pushes the center right and the body hugs the edge, got ${(coreStart + coreEnd) / 2} and ${startEdge(mask)}`);
|
||||
check(near(coreStart - endEdge(sections[0]), content.sectionGap) && near(startEdge(sections[2]) - coreEnd, content.sectionGap), `sides keep the widget spacing around the pushed center, got ${coreStart - endEdge(sections[0])} and ${startEdge(sections[2]) - coreEnd}`);
|
||||
} else {
|
||||
check(center.contentSize > 0 && near((coreStart + coreEnd) / 2, length / 2), `center widgets stay on the screen center, got ${(coreStart + coreEnd) / 2}`);
|
||||
check(near(coreStart - endEdge(sections[0]), content.sectionGap) && near(startEdge(sections[2]) - coreEnd, content.sectionGap), `side sections hug the center content by the widget spacing, got ${coreStart - endEdge(sections[0])} and ${startEdge(sections[2]) - coreEnd}`);
|
||||
check(near(content.sectionGap, body._leftSection.widgetSpacing), "section gap equals the widget spacing");
|
||||
}
|
||||
check(current.wide || along(sections[0]) < along(sections[2]), "asymmetric sides in the fixture");
|
||||
check(current.wide || along(mask) < length - 100, `fitted mask is shorter than the edge, got ${along(mask)}`);
|
||||
}
|
||||
|
||||
if (!current.goth) {
|
||||
check(wings.length === 0, "no wings without goth corners");
|
||||
return;
|
||||
}
|
||||
check(wings.length === 2, `two along wings, got ${wings.length}`);
|
||||
const attached = wing => {
|
||||
switch (current.position) {
|
||||
case SettingsData.Position.Bottom:
|
||||
return near(wing.bottom, surface.bottom);
|
||||
case SettingsData.Position.Left:
|
||||
return near(wing.x, surface.x);
|
||||
case SettingsData.Position.Right:
|
||||
return near(wing.right, surface.right);
|
||||
default:
|
||||
return near(wing.y, surface.y);
|
||||
}
|
||||
};
|
||||
check(wings.every(attached), "wings sit on the attached edge");
|
||||
const leading = root.vertical ? near(wings[0].bottom, surface.y) : near(wings[0].right, surface.x);
|
||||
const trailing = root.vertical ? near(wings[1].y, surface.bottom) : near(wings[1].x, surface.right);
|
||||
check(leading && trailing, `wings flank the surface ends, got ${JSON.stringify(wings)} around ${JSON.stringify(surface)}`);
|
||||
const cutAway = wing => {
|
||||
switch (current.position) {
|
||||
case SettingsData.Position.Bottom:
|
||||
return near(wing.discY, wing.y);
|
||||
case SettingsData.Position.Left:
|
||||
return near(wing.discX, wing.right);
|
||||
case SettingsData.Position.Right:
|
||||
return near(wing.discX, wing.x);
|
||||
default:
|
||||
return near(wing.discY, wing.bottom);
|
||||
}
|
||||
};
|
||||
const cutOutward = root.vertical ? near(wings[0].discY, wings[0].y) && near(wings[1].discY, wings[1].bottom) : near(wings[0].discX, wings[0].x) && near(wings[1].discX, wings[1].right);
|
||||
check(wings.every(cutAway) && cutOutward, `wing discs are cut away from the edge and the surface, got ${JSON.stringify(wings)}`);
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: ticker
|
||||
interval: 200
|
||||
repeat: true
|
||||
running: SettingsData._hasLoaded
|
||||
onTriggered: {
|
||||
try {
|
||||
if (SettingsData.barConfigs.length !== 1 || SettingsData.barConfigs[0].barLengthMode !== root.current.mode || SettingsData.barConfigs[0].wide !== (root.current.wide === true) || SettingsData.barConfigs[0].barLengthPercent !== (root.current.percent ?? 80) || SettingsData.barConfigs[0].position !== root.current.position || SettingsData.barConfigs[0].gothCornersEnabled !== root.current.goth) {
|
||||
SettingsData.barConfigs = [root.barConfig];
|
||||
return;
|
||||
}
|
||||
root.checkStep();
|
||||
if (root.step === root.steps.length - 1) {
|
||||
stop();
|
||||
console.log("FIXTURE_PASS");
|
||||
Qt.quit();
|
||||
return;
|
||||
}
|
||||
root.step++;
|
||||
} catch (error) {
|
||||
stop();
|
||||
console.error("FIXTURE_FAIL", error.message);
|
||||
Qt.quit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
Quickshell.watchFiles = false;
|
||||
DC.Style.theme = Theme;
|
||||
DC.Style.settings = SettingsData;
|
||||
DC.I18n.backend = I18n;
|
||||
SettingsData.reduceMotion = true;
|
||||
}
|
||||
}
|
||||
@@ -51,7 +51,7 @@ def main():
|
||||
parser.error("--jobs must be positive")
|
||||
selected = args.suites or ["widgets", "media", "qt", "logic"]
|
||||
commands = {
|
||||
"widgets": [sys.executable, "quickshell/tests/run-surface-fixture.py", "--suite", "quickshell/tests/qml/bar-content.qml", "quickshell/tests/qml/frame-bar-flip.qml", "quickshell/tests/qml/bar-fit.qml", "quickshell/tests/qml/instance-routing.qml", "quickshell/tests/qml/font-weights.qml", "quickshell/tests/qml/theme-selected-container.qml", "quickshell/tests/qml/theme-accents.qml", "quickshell/tests/qml/clipboard-preview.qml", "quickshell/tests/qml/launcher-plugin-instances.qml", "quickshell/tests/qml/island-launcher-focus.qml", "quickshell/tests/qml/workspace-switcher.qml", "quickshell/tests/qml/focused-app.qml", "quickshell/tests/qml/slider-wheel.qml", "quickshell/tests/qml/control-center-sizes.qml", "quickshell/tests/qml/dash-navigation.qml", "quickshell/tests/qml/grid-edit-layout.qml", "quickshell/tests/qml/settings-scroll.qml", "quickshell/tests/qml/settings-group-layout.qml"],
|
||||
"widgets": [sys.executable, "quickshell/tests/run-surface-fixture.py", "--suite", "quickshell/tests/qml/bar-content.qml", "quickshell/tests/qml/frame-bar-flip.qml", "quickshell/tests/qml/instance-routing.qml", "quickshell/tests/qml/font-weights.qml", "quickshell/tests/qml/theme-selected-container.qml", "quickshell/tests/qml/theme-accents.qml", "quickshell/tests/qml/clipboard-preview.qml", "quickshell/tests/qml/launcher-plugin-instances.qml", "quickshell/tests/qml/island-launcher-focus.qml", "quickshell/tests/qml/workspace-switcher.qml", "quickshell/tests/qml/focused-app.qml", "quickshell/tests/qml/slider-wheel.qml", "quickshell/tests/qml/control-center-sizes.qml", "quickshell/tests/qml/dash-navigation.qml", "quickshell/tests/qml/grid-edit-layout.qml", "quickshell/tests/qml/settings-scroll.qml", "quickshell/tests/qml/settings-group-layout.qml"],
|
||||
"media": [sys.executable, "quickshell/tests/run-surface-fixture.py", "--suite", "quickshell/tests/qml/media-presentation.qml", "quickshell/tests/qml/media-playback.qml", "quickshell/tests/qml/media-artwork.qml", "quickshell/tests/qml/media-lyrics.qml", "--mpris", "--artwork"],
|
||||
"logic": ["node", "--test", *sorted(str(path.relative_to(repo)) for path in (repo / "quickshell/tests").glob("*.test.mjs"))],
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user