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
|
- **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.
|
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.
|
player, the CPU/MEM/GPU readout and the clock on the right.
|
||||||
- **rail** — network, bluetooth, audio, mic, backlight, battery, tray,
|
- **rail** — network, bluetooth, audio, mic, backlight, battery, tray,
|
||||||
notifications, power. Popouts grow leftward out of it, into the screen.
|
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
|
services/ data sources, all singletons
|
||||||
bar/ the two surfaces and their modules
|
bar/ the two surfaces and their modules
|
||||||
popouts/ hover panels hanging off rail and bar 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
|
lock/ session lock
|
||||||
overview/ every monitor, workspace and window at once
|
overview/ every monitor, workspace and window at once
|
||||||
backdrop/ the wallpaper, drawn by the shell
|
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 QtQuick
|
||||||
import Quickshell
|
import Quickshell
|
||||||
|
import Quickshell.Hyprland
|
||||||
import Quickshell.Wayland
|
import Quickshell.Wayland
|
||||||
import "root:/config"
|
import "root:/config"
|
||||||
import "root:/components"
|
import "root:/components"
|
||||||
@@ -44,6 +45,7 @@ PanelWindow {
|
|||||||
switch (root.kind) {
|
switch (root.kind) {
|
||||||
case "mic": return Audio.micIcon;
|
case "mic": return Audio.micIcon;
|
||||||
case "brightness": return Brightness.icon;
|
case "brightness": return Brightness.icon;
|
||||||
|
case "layout": return "";
|
||||||
default: return Audio.icon;
|
default: return Audio.icon;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -52,6 +54,7 @@ PanelWindow {
|
|||||||
switch (root.kind) {
|
switch (root.kind) {
|
||||||
case "mic": return Audio.micMuted ? "Mic muted" : "Microphone";
|
case "mic": return Audio.micMuted ? "Mic muted" : "Microphone";
|
||||||
case "brightness": return "Brightness";
|
case "brightness": return "Brightness";
|
||||||
|
case "layout": return "Keyboard";
|
||||||
default: return Audio.muted ? "Muted" : "Volume";
|
default: return Audio.muted ? "Muted" : "Volume";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -71,6 +74,7 @@ PanelWindow {
|
|||||||
// silenced speaker flashed the same colour as a live one.
|
// silenced speaker flashed the same colour as a live one.
|
||||||
case "mic": return Audio.micMuted ? Theme.treatmentOffText : Theme.accent;
|
case "mic": return Audio.micMuted ? Theme.treatmentOffText : Theme.accent;
|
||||||
case "brightness": return Theme.accent;
|
case "brightness": return Theme.accent;
|
||||||
|
case "layout": return Theme.accent;
|
||||||
default: return Audio.muted ? Theme.treatmentOffText : Theme.accent;
|
default: return Audio.muted ? Theme.treatmentOffText : Theme.accent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -100,6 +104,26 @@ PanelWindow {
|
|||||||
function onBumped() { root.flash("brightness"); }
|
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 {
|
P5Panel {
|
||||||
id: panel
|
id: panel
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@@ -178,12 +202,26 @@ PanelWindow {
|
|||||||
// width and right-aligning the text would flatten the
|
// width and right-aligning the text would flatten the
|
||||||
// glitch flicker away. The title's left edge is anchored, so a
|
// glitch flicker away. The title's left edge is anchored, so a
|
||||||
// widening readout only moves its own left edge.
|
// 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
|
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 {
|
MeterBar {
|
||||||
|
visible: root.kind !== "layout"
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: 4
|
height: 4
|
||||||
segments: 20
|
segments: 20
|
||||||
|
|||||||
Reference in new issue
Block a user