fedora
This commit is contained in:
@@ -0,0 +1,319 @@
|
||||
pragma Singleton
|
||||
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import qs.Commons
|
||||
import qs.Services.Compositor
|
||||
import qs.Services.UI
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
property string currentLayout: I18n.tr("common.unknown")
|
||||
property string fullLayoutName: I18n.tr("common.unknown")
|
||||
property string previousLayout: ""
|
||||
property bool isInitialized: false
|
||||
|
||||
// Updates current layout from various format strings. Called by compositors
|
||||
function setCurrentLayout(layoutString) {
|
||||
root.fullLayoutName = layoutString || I18n.tr("common.unknown");
|
||||
root.currentLayout = extractLayoutCode(layoutString);
|
||||
}
|
||||
|
||||
// Extract layout code from various format strings
|
||||
// Priority: variant > country code > language lookup > fallback
|
||||
function extractLayoutCode(layoutString) {
|
||||
if (!layoutString)
|
||||
return I18n.tr("common.unknown");
|
||||
|
||||
const str = layoutString.toLowerCase();
|
||||
|
||||
// If it's already a short code (2-3 chars), return uppercase
|
||||
if (/^[a-z]{2,3}(\+.*)?$/.test(str)) {
|
||||
return str.split('+')[0].toUpperCase();
|
||||
}
|
||||
|
||||
// Check for layout variants first - these are more meaningful than country codes
|
||||
// when distinguishing between multiple layouts of the same language
|
||||
for (const [pattern, display] of Object.entries(variantMap)) {
|
||||
if (str.includes(pattern)) {
|
||||
return display;
|
||||
}
|
||||
}
|
||||
|
||||
// Extract short code from parentheses like "English (US)"
|
||||
const shortCodeMatch = str.match(/\(([a-z]{2,3})\)/i);
|
||||
if (shortCodeMatch) {
|
||||
return shortCodeMatch[1].toUpperCase();
|
||||
}
|
||||
|
||||
// Check for language/country names in the language map
|
||||
// First, try to match at the start of the string (the primary language)
|
||||
for (const [lang, code] of Object.entries(languageMap)) {
|
||||
if (str.startsWith(lang)) {
|
||||
return code.toUpperCase();
|
||||
}
|
||||
}
|
||||
// Then try word boundary matching anywhere in the string
|
||||
for (const [lang, code] of Object.entries(languageMap)) {
|
||||
const regex = new RegExp(`\\b${lang}\\b`);
|
||||
if (regex.test(str)) {
|
||||
return code.toUpperCase();
|
||||
}
|
||||
}
|
||||
|
||||
// If nothing matches, try first 2-3 characters if they look like a code
|
||||
const codeMatch = str.match(/^([a-z]{2,3})/);
|
||||
return codeMatch ? codeMatch[1].toUpperCase() : I18n.tr("common.unknown");
|
||||
}
|
||||
|
||||
// Watch for layout changes and show toast
|
||||
onCurrentLayoutChanged: {
|
||||
// Update previousLayout after checking for changes
|
||||
const layoutChanged = isInitialized && currentLayout !== previousLayout && currentLayout !== I18n.tr("common.unknown") && previousLayout !== "" && previousLayout !== I18n.tr("common.unknown");
|
||||
|
||||
if (layoutChanged) {
|
||||
if (Settings.data.notifications.enableKeyboardLayoutToast) {
|
||||
const message = I18n.tr("toast.keyboard-layout.changed", {
|
||||
"layout": fullLayoutName
|
||||
});
|
||||
ToastService.showNotice(I18n.tr("toast.keyboard-layout.title"), message, "", 2000);
|
||||
}
|
||||
Logger.d("KeyboardLayout", "Layout changed from", previousLayout, "to", currentLayout);
|
||||
}
|
||||
|
||||
// Update previousLayout for next comparison
|
||||
previousLayout = currentLayout;
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
Logger.i("KeyboardLayout", "Service started");
|
||||
// Mark as initialized after a delay to allow first layout update to complete
|
||||
// This prevents showing a toast on the initial load
|
||||
initializationTimer.start();
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: initializationTimer
|
||||
interval: 2000 // Wait 2 seconds for first layout update to complete
|
||||
onTriggered: {
|
||||
isInitialized = true;
|
||||
// Set previousLayout to current value after initialization
|
||||
previousLayout = currentLayout;
|
||||
Logger.d("KeyboardLayout", "Service initialized, current layout:", currentLayout);
|
||||
}
|
||||
}
|
||||
|
||||
// Layout variants - checked BEFORE country codes
|
||||
// These display the variant name when it's more meaningful than the country
|
||||
property var variantMap: {
|
||||
// Alternative keyboard layouts
|
||||
"colemak": "Colemak",
|
||||
"dvorak": "Dvorak",
|
||||
"workman": "Workman",
|
||||
"programmer dvorak": "Dvk-P",
|
||||
"norman": "Norman",
|
||||
// International variants
|
||||
"intl": "Intl",
|
||||
"international": "Intl",
|
||||
"altgr-intl": "Intl",
|
||||
"with dead keys": "Dead",
|
||||
// Common variants
|
||||
"phonetic": "Phon",
|
||||
"extended": "Ext",
|
||||
"ergonomic": "Ergo",
|
||||
"legacy": "Legacy",
|
||||
// Input methods
|
||||
"pinyin": "Pinyin",
|
||||
"cangjie": "Cangjie",
|
||||
"romaji": "Romaji",
|
||||
"kana": "Kana"
|
||||
}
|
||||
|
||||
// Language/country name to ISO code mapping
|
||||
property var languageMap: {
|
||||
// English variants
|
||||
"english": "us",
|
||||
"american": "us",
|
||||
"united states": "us",
|
||||
"us english": "us",
|
||||
"british": "gb",
|
||||
"united kingdom": "gb",
|
||||
"english (uk)": "gb",
|
||||
"canadian": "ca",
|
||||
"canada": "ca",
|
||||
"canadian english": "ca",
|
||||
"australian": "au",
|
||||
"australia": "au",
|
||||
// Nordic countries
|
||||
"swedish": "se",
|
||||
"svenska": "se",
|
||||
"sweden": "se",
|
||||
"norwegian": "no",
|
||||
"norsk": "no",
|
||||
"norway": "no",
|
||||
"danish": "dk",
|
||||
"dansk": "dk",
|
||||
"denmark": "dk",
|
||||
"finnish": "fi",
|
||||
"suomi": "fi",
|
||||
"finland": "fi",
|
||||
"icelandic": "is",
|
||||
"íslenska": "is",
|
||||
"iceland": "is",
|
||||
// Western/Central European Germanic
|
||||
"german": "de",
|
||||
"deutsch": "de",
|
||||
"germany": "de",
|
||||
"austrian": "at",
|
||||
"austria": "at",
|
||||
"österreich": "at",
|
||||
"swiss": "ch",
|
||||
"switzerland": "ch",
|
||||
"schweiz": "ch",
|
||||
"suisse": "ch",
|
||||
"dutch": "nl",
|
||||
"nederlands": "nl",
|
||||
"netherlands": "nl",
|
||||
"holland": "nl",
|
||||
"belgian": "be",
|
||||
"belgium": "be",
|
||||
"belgië": "be",
|
||||
"belgique": "be",
|
||||
// Romance languages (Western/Southern Europe)
|
||||
"french": "fr",
|
||||
"français": "fr",
|
||||
"france": "fr",
|
||||
"canadian french": "ca",
|
||||
"spanish": "es",
|
||||
"español": "es",
|
||||
"spain": "es",
|
||||
"castilian": "es",
|
||||
"italian": "it",
|
||||
"italiano": "it",
|
||||
"italy": "it",
|
||||
"portuguese": "pt",
|
||||
"português": "pt",
|
||||
"portugal": "pt",
|
||||
"catalan": "ad",
|
||||
"català": "ad",
|
||||
"andorra": "ad",
|
||||
// Eastern European Romance
|
||||
"romanian": "ro",
|
||||
"română": "ro",
|
||||
"romania": "ro",
|
||||
// Slavic languages (Eastern Europe)
|
||||
"russian": "ru",
|
||||
"русский": "ru",
|
||||
"russia": "ru",
|
||||
"polish": "pl",
|
||||
"polski": "pl",
|
||||
"poland": "pl",
|
||||
"czech": "cz",
|
||||
"čeština": "cz",
|
||||
"czech republic": "cz",
|
||||
"slovak": "sk",
|
||||
"slovenčina": "sk",
|
||||
"slovakia": "sk",
|
||||
// Ukrainian
|
||||
"ukraine": "ua",
|
||||
"ukrainian": "ua",
|
||||
"українська": "ua",
|
||||
"bulgarian": "bg",
|
||||
"български": "bg",
|
||||
"bulgaria": "bg",
|
||||
"serbian": "rs",
|
||||
"srpski": "rs",
|
||||
"serbia": "rs",
|
||||
"croatian": "hr",
|
||||
"hrvatski": "hr",
|
||||
"croatia": "hr",
|
||||
"slovenian": "si",
|
||||
"slovenščina": "si",
|
||||
"slovenia": "si",
|
||||
"bosnian": "ba",
|
||||
"bosanski": "ba",
|
||||
"bosnia": "ba",
|
||||
"macedonian": "mk",
|
||||
"македонски": "mk",
|
||||
"macedonia": "mk",
|
||||
// Celtic languages (Western Europe)
|
||||
"irish": "ie",
|
||||
"gaeilge": "ie",
|
||||
"ireland": "ie",
|
||||
"welsh": "gb",
|
||||
"cymraeg": "gb",
|
||||
"wales": "gb",
|
||||
"scottish": "gb",
|
||||
"gàidhlig": "gb",
|
||||
"scotland": "gb",
|
||||
// Baltic languages (Northern Europe)
|
||||
"estonian": "ee",
|
||||
"eesti": "ee",
|
||||
"estonia": "ee",
|
||||
"latvian": "lv",
|
||||
"latviešu": "lv",
|
||||
"latvia": "lv",
|
||||
"lithuanian": "lt",
|
||||
"lietuvių": "lt",
|
||||
"lithuania": "lt",
|
||||
// Other European languages
|
||||
"hungarian": "hu",
|
||||
"magyar": "hu",
|
||||
"hungary": "hu",
|
||||
"greek": "gr",
|
||||
"ελληνικά": "gr",
|
||||
"greece": "gr",
|
||||
"albanian": "al",
|
||||
"shqip": "al",
|
||||
"albania": "al",
|
||||
"maltese": "mt",
|
||||
"malti": "mt",
|
||||
"malta": "mt",
|
||||
// West/Southwest Asian languages
|
||||
"turkish": "tr",
|
||||
"türkçe": "tr",
|
||||
"turkey": "tr",
|
||||
"arabic": "ar",
|
||||
"العربية": "ar",
|
||||
"arab": "ar",
|
||||
"hebrew": "il",
|
||||
"עברית": "il",
|
||||
"israel": "il",
|
||||
// South American languages
|
||||
"brazilian": "br",
|
||||
"brazilian portuguese": "br",
|
||||
"brasil": "br",
|
||||
"brazil": "br",
|
||||
// East Asian languages
|
||||
"japanese": "jp",
|
||||
"日本語": "jp",
|
||||
"japan": "jp",
|
||||
"korean": "kr",
|
||||
"한국어": "kr",
|
||||
"korea": "kr",
|
||||
"south korea": "kr",
|
||||
"chinese": "cn",
|
||||
"中文": "cn",
|
||||
"china": "cn",
|
||||
"simplified chinese": "cn",
|
||||
"traditional chinese": "tw",
|
||||
"taiwan": "tw",
|
||||
"繁體中文": "tw",
|
||||
// Southeast Asian languages
|
||||
"thai": "th",
|
||||
"ไทย": "th",
|
||||
"thailand": "th",
|
||||
"vietnamese": "vn",
|
||||
"tiếng việt": "vn",
|
||||
"vietnam": "vn",
|
||||
// South Asian languages
|
||||
"hindi": "in",
|
||||
"हिन्दी": "in",
|
||||
"india": "in",
|
||||
// African languages
|
||||
"afrikaans": "za",
|
||||
"south africa": "za",
|
||||
"south african": "za"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user