Finish the wonderland: System told truthfully, in eight tabs instead of ten
Claude-Session: https://claude.ai/code/session_01Ms2FbjQy31TVf3CEvQhGM8
This commit is contained in:
@@ -33,16 +33,100 @@ Singleton {
|
||||
// so the UI can stop claiming the new locale is already in use.
|
||||
property bool pendingRestart: false
|
||||
|
||||
// Guards read the Process objects directly; a derived binding is stale
|
||||
// inside the handler that changes it. See DefaultApps.qml.
|
||||
readonly property bool busy: apply.running || applyCategory.running || root.scanning
|
||||
|
||||
readonly property string currentLabel: {
|
||||
const match = root.locales.find(locale => locale.value === root.current);
|
||||
return match ? match.label : root.current;
|
||||
}
|
||||
|
||||
// ── Per-category formats ────────────────────────────────────────────────
|
||||
//
|
||||
// Reading in one language while writing dates, numbers and currency the way
|
||||
// your country does is the ordinary case. Each category is an OVERRIDE, and
|
||||
// its absence -- "" here -- means "match language", which is not the same
|
||||
// as an override that happens to equal LANG today: the two diverge the
|
||||
// moment the language changes.
|
||||
//
|
||||
// Same restart discipline as the language itself. localectl only affects
|
||||
// programs started afterwards, so an accepted change sets pendingRestart
|
||||
// and the page says a sign-out is needed rather than claiming the new
|
||||
// format is already in use.
|
||||
|
||||
readonly property var categories:
|
||||
["LC_TIME", "LC_NUMERIC", "LC_MONETARY", "LC_MEASUREMENT", "LC_PAPER"]
|
||||
|
||||
property var categoryValues: ({})
|
||||
|
||||
// Bumped whenever an override is read or accepted, and read at the top of
|
||||
// categoryValue() so a binding built on that call has something to
|
||||
// invalidate. A bare function call captures no dependency and every reader
|
||||
// would go stale -- see DesktopPreferences.get() for the same reason.
|
||||
property int categoryRevision: 0
|
||||
|
||||
// "" means match language. Anything else is an installed locale name.
|
||||
function categoryValue(category: string): string {
|
||||
root.categoryRevision;
|
||||
return String(root.categoryValues[category] ?? "");
|
||||
}
|
||||
|
||||
function categoryLabel(category: string): string {
|
||||
const value = root.categoryValue(category);
|
||||
if (value === "")
|
||||
return "Match language";
|
||||
const match = root.locales.find(locale => locale.value === value);
|
||||
return match ? match.label : value;
|
||||
}
|
||||
|
||||
// Pass "" to remove the override. Refused for anything that is not one of
|
||||
// the five categories: this reaches a privileged command, and the caller
|
||||
// does not get to name the variable being written.
|
||||
function setCategory(category: string, locale: string): bool {
|
||||
if (root.categories.indexOf(category) < 0)
|
||||
return false;
|
||||
if (locale !== "" && !root.locales.some(entry => entry.value === locale))
|
||||
return false;
|
||||
if (root.categoryValue(category) === locale)
|
||||
return true;
|
||||
|
||||
// A click while a change is still applying is queued rather than
|
||||
// fired: assigning running = true to a running Process is a no-op, so
|
||||
// a second setCategory would be silently dropped and applyCategory's
|
||||
// handler would then adopt it as though it had been applied. Same
|
||||
// queue discipline as set() above.
|
||||
root.requestedCategories = Object.assign({}, root.requestedCategories);
|
||||
root.requestedCategories[category] = locale;
|
||||
if (!applyCategory.running)
|
||||
root._applyNextCategory();
|
||||
return true;
|
||||
}
|
||||
|
||||
property var requestedCategories: ({})
|
||||
|
||||
function _applyNextCategory(): void {
|
||||
const keys = Object.keys(root.requestedCategories);
|
||||
if (keys.length === 0)
|
||||
return;
|
||||
const category = keys[0];
|
||||
const value = String(root.requestedCategories[category]);
|
||||
const remaining = Object.assign({}, root.requestedCategories);
|
||||
delete remaining[category];
|
||||
root.requestedCategories = remaining;
|
||||
|
||||
applyCategory.pendingCategory = category;
|
||||
applyCategory.pendingValue = value;
|
||||
applyCategory.command = [root.helperPath, "set", category, value];
|
||||
applyCategory.running = true;
|
||||
}
|
||||
|
||||
function refresh(): void {
|
||||
if (root.scanning)
|
||||
return;
|
||||
root.scanning = true;
|
||||
readCurrent.running = true;
|
||||
readCategories.running = true;
|
||||
list.running = true;
|
||||
}
|
||||
|
||||
@@ -95,6 +179,49 @@ Singleton {
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: readCategories
|
||||
command: [root.helperPath, "overrides"]
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
try {
|
||||
const parsed = JSON.parse(this.text);
|
||||
root.categoryValues = (parsed && typeof parsed === "object") ? parsed : ({});
|
||||
} catch (error) {
|
||||
root.categoryValues = ({});
|
||||
console.warn("SystemLocale: could not parse the format overrides:", error);
|
||||
}
|
||||
root.categoryRevision += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: applyCategory
|
||||
|
||||
property string pendingCategory: ""
|
||||
property string pendingValue: ""
|
||||
|
||||
// A refused change -- polkit dismissed, or a locale the system does not
|
||||
// have -- must not move the UI. The value is only adopted on a zero
|
||||
// exit, exactly as the language apply above does.
|
||||
onExited: code => {
|
||||
if (code === 0) {
|
||||
const next = Object.assign({}, root.categoryValues);
|
||||
next[applyCategory.pendingCategory] = applyCategory.pendingValue;
|
||||
root.categoryValues = next;
|
||||
root.categoryRevision += 1;
|
||||
root.pendingRestart = true;
|
||||
root.lastError = "";
|
||||
} else {
|
||||
root.lastError = "The system did not accept that format. It may have needed a password.";
|
||||
}
|
||||
applyCategory.pendingCategory = "";
|
||||
applyCategory.pendingValue = "";
|
||||
root._applyNextCategory();
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: apply
|
||||
|
||||
|
||||
Reference in New Issue
Block a user