feat(quickshell): scratchpad ring in the bar, keyboard layout in the OSD
The Super+S scratchpad gets a ring after the workspace pips while it holds windows, filled while it is open; clicking it toggles it. Switching keyboard layout flashes the OSD with the short name (US, Dvorak) and the full keymap underneath.
This commit is contained in:
1 parent
b69f4bcf0d
commit
dc2a8fc883
3 files changed
+95
-3
No files matched your search
@@ -39,7 +39,8 @@ cost 4.4% of the height on top of what the bar already takes.
|
||||
|
||||
- **hull** — `bar/Frame.qml`: one surface round the whole screen, with the
|
||||
desktop as a rounded opening. The bar and the rail sit on it.
|
||||
- **bar** — identity and workspaces on the left; the focused window, the
|
||||
- **bar** — identity and workspaces on the left (a ring after them while the
|
||||
`Super+S` scratchpad holds windows, filled while it is open); the focused window, the
|
||||
player, the CPU/MEM/GPU readout and the clock on the right.
|
||||
- **rail** — network, bluetooth, audio, mic, backlight, battery, tray,
|
||||
notifications, power. Popouts grow leftward out of it, into the screen.
|
||||
@@ -64,7 +65,7 @@ components/ the primitives: panels, sliders, list rows, display text
|
||||
services/ data sources, all singletons
|
||||
bar/ the two surfaces and their modules
|
||||
popouts/ hover panels hanging off rail and bar modules
|
||||
overlays/ power menu, notification toasts, volume/brightness OSD
|
||||
overlays/ power menu, notification toasts, the volume/brightness/keyboard OSD, idle dim
|
||||
lock/ session lock
|
||||
overview/ every monitor, workspace and window at once
|
||||
backdrop/ the wallpaper, drawn by the shell
|
||||
|
||||
@@ -155,5 +155,58 @@ Item {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The Super+S scratchpad, after a gap: a ring while it holds windows,
|
||||
// filled with the light while it is open on this monitor. Absent when
|
||||
// it is empty, which is most of the time.
|
||||
Item {
|
||||
id: scratch
|
||||
readonly property var ws: {
|
||||
for (const w of (Hyprland.workspaces?.values ?? []))
|
||||
if (w.name === "special:virtual") return w;
|
||||
return null;
|
||||
}
|
||||
readonly property int count: scratch.ws?.toplevels?.values?.length ?? 0
|
||||
readonly property bool shown: (root.monitor?.lastIpcObject?.specialWorkspace?.name ?? "")
|
||||
=== "special:virtual"
|
||||
|
||||
visible: scratch.count > 0
|
||||
width: 8 + 10
|
||||
height: 14
|
||||
|
||||
Rectangle {
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: 10
|
||||
height: 10
|
||||
radius: 5
|
||||
color: scratch.shown ? Theme.accent : "transparent"
|
||||
border.width: scratch.shown ? 0 : 1
|
||||
border.color: scratchHover.hovered ? Theme.text : Theme.subtext
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation { duration: Theme.durBase }
|
||||
}
|
||||
}
|
||||
|
||||
HoverHandler {
|
||||
id: scratchHover
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
}
|
||||
|
||||
TapHandler {
|
||||
onTapped: Hyprland.dispatch("hl.dsp.workspace.toggle_special(\"virtual\")")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The monitor snapshot carries which special workspace is open; it is only
|
||||
// refreshed on request.
|
||||
Connections {
|
||||
target: Hyprland
|
||||
function onRawEvent(event) {
|
||||
if (event.name === "activespecial" || event.name === "activespecialv2")
|
||||
Hyprland.refreshMonitors();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Hyprland
|
||||
import Quickshell.Wayland
|
||||
import "root:/config"
|
||||
import "root:/components"
|
||||
@@ -44,6 +45,7 @@ PanelWindow {
|
||||
switch (root.kind) {
|
||||
case "mic": return Audio.micIcon;
|
||||
case "brightness": return Brightness.icon;
|
||||
case "layout": return "";
|
||||
default: return Audio.icon;
|
||||
}
|
||||
}
|
||||
@@ -52,6 +54,7 @@ PanelWindow {
|
||||
switch (root.kind) {
|
||||
case "mic": return Audio.micMuted ? "Mic muted" : "Microphone";
|
||||
case "brightness": return "Brightness";
|
||||
case "layout": return "Keyboard";
|
||||
default: return Audio.muted ? "Muted" : "Volume";
|
||||
}
|
||||
}
|
||||
@@ -71,6 +74,7 @@ PanelWindow {
|
||||
// silenced speaker flashed the same colour as a live one.
|
||||
case "mic": return Audio.micMuted ? Theme.treatmentOffText : Theme.accent;
|
||||
case "brightness": return Theme.accent;
|
||||
case "layout": return Theme.accent;
|
||||
default: return Audio.muted ? Theme.treatmentOffText : Theme.accent;
|
||||
}
|
||||
}
|
||||
@@ -100,6 +104,26 @@ PanelWindow {
|
||||
function onBumped() { root.flash("brightness"); }
|
||||
}
|
||||
|
||||
// The keymap Hyprland just switched to, e.g. "English (Dvorak, intl.,
|
||||
// with dead keys)", and a short name for the readout: the variant in the
|
||||
// brackets if there is one, otherwise the language.
|
||||
property string keymap: ""
|
||||
readonly property string keymapShort: {
|
||||
const m = /\(([^,)]+)/.exec(root.keymap);
|
||||
return (m ? m[1] : root.keymap).trim();
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: Hyprland
|
||||
function onRawEvent(event) {
|
||||
if (event.name !== "activelayout") return;
|
||||
// "keyboard-name,Layout name" — the layout name may hold commas.
|
||||
const at = event.data.indexOf(",");
|
||||
root.keymap = at < 0 ? event.data : event.data.slice(at + 1);
|
||||
root.flash("layout");
|
||||
}
|
||||
}
|
||||
|
||||
P5Panel {
|
||||
id: panel
|
||||
anchors.fill: parent
|
||||
@@ -178,12 +202,26 @@ PanelWindow {
|
||||
// width and right-aligning the text would flatten the
|
||||
// glitch flicker away. The title's left edge is anchored, so a
|
||||
// widening readout only moves its own left edge.
|
||||
text: Math.round(root.level * 100) + "%"
|
||||
text: root.kind === "layout" ? root.keymapShort : Math.round(root.level * 100) + "%"
|
||||
pixelSize: Theme.fsBody
|
||||
}
|
||||
}
|
||||
|
||||
// The layout has no level; its full name stands where the meter
|
||||
// would be.
|
||||
Text {
|
||||
visible: root.kind === "layout"
|
||||
width: parent.width
|
||||
text: root.keymap
|
||||
color: Theme.muted
|
||||
elide: Text.ElideRight
|
||||
font.family: Theme.fontMono
|
||||
font.pixelSize: Theme.fsMicro
|
||||
renderType: Text.NativeRendering
|
||||
}
|
||||
|
||||
MeterBar {
|
||||
visible: root.kind !== "layout"
|
||||
width: parent.width
|
||||
height: 4
|
||||
segments: 20
|
||||
|
||||
Reference in new issue
Block a user