diff --git a/Panel.qml b/Panel.qml
index 5e42cf0..2ccac43 100644
--- a/Panel.qml
+++ b/Panel.qml
@@ -76,6 +76,44 @@ Panel {
return -1
}
+ function isOsdEnabled() {
+ var v = setting("showOsd", true)
+ return v === true || v === "true" || v === 1 || v === "1"
+ }
+
+ property string currentDisplayMode: String(setting("displayMode", "flag"))
+ property bool currentShowOsd: isOsdEnabled()
+
+ onSettingsChanged: {
+ currentDisplayMode = String(setting("displayMode", "flag"))
+ currentShowOsd = isOsdEnabled()
+ }
+
+ function updateDisplayMode(mode) {
+ currentDisplayMode = String(mode)
+ if (root.bar) root.bar.run("omarchy bar set glafeara.languages displayMode " + mode)
+ }
+
+ function updateShowOsd(enabled) {
+ var boolVal = (enabled === true || enabled === "true" || enabled === 1 || enabled === "1")
+ currentShowOsd = boolVal
+ if (root.bar) root.bar.run("omarchy bar set glafeara.languages showOsd " + (boolVal ? "true" : "false") + " --json")
+ }
+
+ readonly property string activeCode: {
+ if (activeIndex >= 0) return String(layouts[activeIndex].code).toLowerCase()
+ var key = codesByName[String(activeKeymap)]
+ if (key) return String(key).replace(/\(.*$/, "").toLowerCase()
+ return fallbackAbbrev(activeKeymap).toLowerCase()
+ }
+
+ function flagPathFor(code) {
+ if (!code) return ""
+ var c = String(code).toLowerCase().trim().replace(/[^a-z0-9_]/g, "")
+ if (c === "en") c = "us"
+ return Qt.resolvedUrl("flags/" + c + ".svg")
+ }
+
readonly property string barLabel: activeIndex >= 0
? abbrevFor(layouts[activeIndex].code)
: (activeKeymap !== "" ? fallbackAbbrev(activeKeymap) : "")
@@ -208,7 +246,7 @@ Panel {
function showOsd(label) {
if (!root.primaryInstance) return
- if (setting("showOsd", true) !== true || label === "") return
+ if (!root.currentShowOsd || label === "") return
root.osdText = label
root.osdVisible = true
osdTimer.restart()
@@ -439,30 +477,61 @@ Panel {
id: button
anchors.fill: parent
bar: root.bar
- text: root.barLabel
+ text: root.currentDisplayMode === "flag" ? " " : (root.currentDisplayMode === "both" ? " " + root.barLabel : root.barLabel)
fontSize: Style.font.body
- horizontalMargin: 7
+ horizontalMargin: root.currentDisplayMode === "flag" ? 6 : 7
onPressed: function(buttonCode) {
if (buttonCode === Qt.RightButton) root.cycleLayout()
else root.toggle()
}
- // The stock label is 10px regular — too faint to read at a glance next to
- // the bar's 13px icons. Hide it (it still sizes the slot) and paint the
- // abbreviation bold at body size instead.
labelVisible: false
- Text {
+ Row {
anchors.centerIn: parent
- // Box-centering puts cap-height ink a hair above the optical center the
- // bar icons sit on; one pixel down lines the label up with them.
- anchors.verticalCenterOffset: 1
- text: root.barLabel
- color: button.active && button.useActiveColor ? button.activeColor : button.foreground
- font.family: root.fontFamily
- font.pixelSize: Style.font.body
- font.bold: true
- renderType: Text.NativeRendering
+ anchors.verticalCenterOffset: 0
+ spacing: Style.space(6)
+
+ Rectangle {
+ id: flagBadge
+ visible: root.currentDisplayMode !== "text" && flagImg.status === Image.Ready
+ width: Style.space(19)
+ height: Style.space(13)
+ anchors.verticalCenter: parent.verticalCenter
+ radius: Style.space(2)
+ color: "transparent"
+ clip: true
+
+ Image {
+ id: flagImg
+ anchors.fill: parent
+ source: root.flagPathFor(root.activeCode)
+ sourceSize.width: 38
+ sourceSize.height: 26
+ fillMode: Image.PreserveAspectCrop
+ smooth: true
+ }
+
+ Rectangle {
+ anchors.fill: parent
+ radius: parent.radius
+ color: "transparent"
+ border.color: Qt.rgba(1, 1, 1, 0.18)
+ border.width: 1
+ }
+ }
+
+ Text {
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.verticalCenterOffset: 1
+ visible: root.currentDisplayMode !== "flag" || flagImg.status !== Image.Ready
+ text: root.barLabel
+ color: button.active && button.useActiveColor ? button.activeColor : button.foreground
+ font.family: root.fontFamily
+ font.pixelSize: Style.font.body
+ font.bold: true
+ renderType: Text.NativeRendering
+ }
}
}
@@ -558,9 +627,39 @@ Panel {
}
}
+ Rectangle {
+ id: rowFlagBadge
+ anchors.left: parent.left
+ anchors.leftMargin: Style.space(8)
+ anchors.verticalCenter: parent.verticalCenter
+ width: Style.space(20)
+ height: Style.space(14)
+ radius: Style.space(2)
+ color: "transparent"
+ clip: true
+
+ Image {
+ id: rowFlagImg
+ anchors.fill: parent
+ source: root.flagPathFor(row.modelData.code)
+ sourceSize.width: 40
+ sourceSize.height: 28
+ fillMode: Image.PreserveAspectCrop
+ smooth: true
+ }
+
+ Rectangle {
+ anchors.fill: parent
+ radius: parent.radius
+ color: "transparent"
+ border.color: Qt.rgba(1, 1, 1, 0.15)
+ border.width: 1
+ }
+ }
+
Text {
id: rowAbbrev
- anchors.left: parent.left
+ anchors.left: rowFlagBadge.right
anchors.leftMargin: Style.space(8)
anchors.verticalCenter: parent.verticalCenter
text: root.abbrevFor(row.modelData.code)
@@ -661,6 +760,109 @@ Panel {
}
}
+ PanelSeparator {
+ foreground: root.foreground
+ }
+
+ Row {
+ width: column.width
+ spacing: Style.space(6)
+
+ Text {
+ width: Style.space(78)
+ anchors.verticalCenter: parent.verticalCenter
+ text: "Display:"
+ color: root.dim
+ font.family: root.fontFamily
+ font.pixelSize: Style.font.caption
+ }
+
+ Repeater {
+ model: [
+ { id: "flag", label: "Flag" },
+ { id: "text", label: "Text" },
+ { id: "both", label: "Both" }
+ ]
+
+ delegate: CursorSurface {
+ id: modeChip
+ required property var modelData
+ readonly property bool isSelected: root.currentDisplayMode === modelData.id
+ width: (column.width - Style.space(78) - Style.space(18)) / 3
+ height: Style.space(26)
+ radius: Style.space(4)
+ color: isSelected ? Qt.rgba(1, 1, 1, 0.12) : "transparent"
+ border.color: isSelected ? Color.accent : Qt.rgba(1, 1, 1, 0.1)
+ border.width: 1
+
+ MouseArea {
+ anchors.fill: parent
+ hoverEnabled: true
+ cursorShape: Qt.PointingHandCursor
+ onClicked: root.updateDisplayMode(modeChip.modelData.id)
+ }
+
+ Text {
+ anchors.centerIn: parent
+ text: modeChip.modelData.label
+ color: modeChip.isSelected ? Color.accent : root.foreground
+ font.family: root.fontFamily
+ font.pixelSize: Style.font.caption
+ font.bold: modeChip.isSelected
+ }
+ }
+ }
+ }
+
+ Row {
+ width: column.width
+ spacing: Style.space(6)
+
+ Text {
+ width: Style.space(78)
+ anchors.verticalCenter: parent.verticalCenter
+ text: "OSD Popup:"
+ color: root.dim
+ font.family: root.fontFamily
+ font.pixelSize: Style.font.caption
+ }
+
+ Repeater {
+ model: [
+ { val: true, label: "Enabled" },
+ { val: false, label: "Disabled" }
+ ]
+
+ delegate: CursorSurface {
+ id: osdChip
+ required property var modelData
+ readonly property bool isSelected: root.currentShowOsd === modelData.val
+ width: (column.width - Style.space(78) - Style.space(12)) / 2
+ height: Style.space(26)
+ radius: Style.space(4)
+ color: isSelected ? Qt.rgba(1, 1, 1, 0.12) : "transparent"
+ border.color: isSelected ? Color.accent : Qt.rgba(1, 1, 1, 0.1)
+ border.width: 1
+
+ MouseArea {
+ anchors.fill: parent
+ hoverEnabled: true
+ cursorShape: Qt.PointingHandCursor
+ onClicked: root.updateShowOsd(osdChip.modelData.val)
+ }
+
+ Text {
+ anchors.centerIn: parent
+ text: osdChip.modelData.label
+ color: osdChip.isSelected ? Color.accent : root.foreground
+ font.family: root.fontFamily
+ font.pixelSize: Style.font.caption
+ font.bold: osdChip.isSelected
+ }
+ }
+ }
+ }
+
Text {
width: column.width
visible: root.lastError !== ""
@@ -699,6 +901,8 @@ Panel {
// whatever the user is typing into.
mask: Region {}
+ readonly property bool osdIsFlagOnly: root.currentDisplayMode === "flag" && osdFlagImg.status === Image.Ready
+
BorderSurface {
id: osdCard
anchors.centerIn: parent
@@ -706,11 +910,41 @@ Panel {
// Mirrors the theme's decoration:rounding, like the stock OSD card.
radius: Style.cornerRadius
borderSpec: Border.surfaceSpec("popups", "border", Color.popups.border, Math.max(1, Style.space(2)))
- width: Math.max(osdLabel.implicitHeight, osdLabel.implicitWidth) + Style.space(48)
- height: osdLabel.implicitHeight + Style.space(48)
+ width: osdWindow.osdIsFlagOnly ? Style.space(136) : (Math.max(osdLabel.implicitHeight, osdLabel.implicitWidth) + Style.space(48))
+ height: osdWindow.osdIsFlagOnly ? Style.space(96) : (osdLabel.implicitHeight + Style.space(48))
+
+ Rectangle {
+ id: osdFlagBadge
+ visible: osdWindow.osdIsFlagOnly
+ anchors.centerIn: parent
+ width: Style.space(96)
+ height: Style.space(64)
+ radius: Style.space(6)
+ color: "transparent"
+ clip: true
+
+ Image {
+ id: osdFlagImg
+ anchors.fill: parent
+ source: root.flagPathFor(root.osdText)
+ sourceSize.width: 192
+ sourceSize.height: 128
+ fillMode: Image.PreserveAspectCrop
+ smooth: true
+ }
+
+ Rectangle {
+ anchors.fill: parent
+ radius: parent.radius
+ color: "transparent"
+ border.color: Qt.rgba(1, 1, 1, 0.22)
+ border.width: 1
+ }
+ }
Text {
id: osdLabel
+ visible: !osdWindow.osdIsFlagOnly
anchors.centerIn: parent
text: root.osdText
color: Color.popups.text
diff --git a/flags/am.svg b/flags/am.svg
new file mode 100644
index 0000000..d51219b
--- /dev/null
+++ b/flags/am.svg
@@ -0,0 +1,5 @@
+
diff --git a/flags/by.svg b/flags/by.svg
new file mode 100644
index 0000000..bf96fa9
--- /dev/null
+++ b/flags/by.svg
@@ -0,0 +1,6 @@
+
diff --git a/flags/ca.svg b/flags/ca.svg
new file mode 100644
index 0000000..4521509
--- /dev/null
+++ b/flags/ca.svg
@@ -0,0 +1,5 @@
+
diff --git a/flags/cn.svg b/flags/cn.svg
new file mode 100644
index 0000000..eecc923
--- /dev/null
+++ b/flags/cn.svg
@@ -0,0 +1,4 @@
+
diff --git a/flags/de.svg b/flags/de.svg
new file mode 100644
index 0000000..b6d60e3
--- /dev/null
+++ b/flags/de.svg
@@ -0,0 +1,5 @@
+
diff --git a/flags/ee.svg b/flags/ee.svg
new file mode 100644
index 0000000..3e6f465
--- /dev/null
+++ b/flags/ee.svg
@@ -0,0 +1,5 @@
+
diff --git a/flags/es.svg b/flags/es.svg
new file mode 100644
index 0000000..03b489a
--- /dev/null
+++ b/flags/es.svg
@@ -0,0 +1,4 @@
+
diff --git a/flags/fi.svg b/flags/fi.svg
new file mode 100644
index 0000000..7f1f271
--- /dev/null
+++ b/flags/fi.svg
@@ -0,0 +1,5 @@
+
diff --git a/flags/fr.svg b/flags/fr.svg
new file mode 100644
index 0000000..5582acb
--- /dev/null
+++ b/flags/fr.svg
@@ -0,0 +1,5 @@
+
diff --git a/flags/gb.svg b/flags/gb.svg
new file mode 100644
index 0000000..edcf98d
--- /dev/null
+++ b/flags/gb.svg
@@ -0,0 +1,7 @@
+
diff --git a/flags/ge.svg b/flags/ge.svg
new file mode 100644
index 0000000..dd460f1
--- /dev/null
+++ b/flags/ge.svg
@@ -0,0 +1,4 @@
+
diff --git a/flags/it.svg b/flags/it.svg
new file mode 100644
index 0000000..60e5a51
--- /dev/null
+++ b/flags/it.svg
@@ -0,0 +1,5 @@
+
diff --git a/flags/jp.svg b/flags/jp.svg
new file mode 100644
index 0000000..1b0d1c7
--- /dev/null
+++ b/flags/jp.svg
@@ -0,0 +1,4 @@
+
diff --git a/flags/kz.svg b/flags/kz.svg
new file mode 100644
index 0000000..bf3666e
--- /dev/null
+++ b/flags/kz.svg
@@ -0,0 +1,4 @@
+
diff --git a/flags/pl.svg b/flags/pl.svg
new file mode 100644
index 0000000..f2c5863
--- /dev/null
+++ b/flags/pl.svg
@@ -0,0 +1,4 @@
+
diff --git a/flags/ru.svg b/flags/ru.svg
new file mode 100644
index 0000000..5c6314d
--- /dev/null
+++ b/flags/ru.svg
@@ -0,0 +1,7 @@
+
diff --git a/flags/tr.svg b/flags/tr.svg
new file mode 100644
index 0000000..918a0d7
--- /dev/null
+++ b/flags/tr.svg
@@ -0,0 +1,5 @@
+
diff --git a/flags/ua.svg b/flags/ua.svg
new file mode 100644
index 0000000..8ae40fb
--- /dev/null
+++ b/flags/ua.svg
@@ -0,0 +1,4 @@
+
diff --git a/flags/us.svg b/flags/us.svg
new file mode 100644
index 0000000..e9188b1
--- /dev/null
+++ b/flags/us.svg
@@ -0,0 +1,20 @@
+
diff --git a/manifest.json b/manifest.json
index a4df406..3a37db6 100644
--- a/manifest.json
+++ b/manifest.json
@@ -19,10 +19,17 @@
"allowMultiple": false,
"defaultSection": "right",
"defaults": {
+ "displayMode": "flag",
"osdDurationMs": 750,
"showOsd": true
},
"schema": [
+ {
+ "key": "displayMode",
+ "type": "string",
+ "label": "Display mode (flag, text, both)",
+ "defaultValue": "flag"
+ },
{
"key": "osdDurationMs",
"type": "integer",