[verified] feat(quickshell): add cyber dock launcher

This commit is contained in:
2026-08-30 16:54:19 +00:00
parent 6406e06330
commit abccf536f6
5 changed files with 413 additions and 1 deletions
@@ -0,0 +1,88 @@
pragma ComponentBehavior: Bound
import Quickshell
import Quickshell.Hyprland
import Quickshell.Wayland
import QtQuick
import qs.widgets.theme
// Variant 11 — cyberpunk bottom dock inspired by V5's rising carousel.
Scope {
id: root
property bool active: false
function toggle() { root.active = !root.active; }
onActiveChanged: {
LauncherState.dockOpen = root.active;
if (root.active)
win.prepareOpen();
}
GlobalShortcut {
name: "launcher11"
description: "Toggle app launcher (Cyber Dock)"
onPressed: root.toggle()
}
PanelWindow {
id: win
visible: root.active || deck.y < height
WlrLayershell.layer: WlrLayer.Overlay
WlrLayershell.keyboardFocus: root.active ? WlrKeyboardFocus.Exclusive : WlrKeyboardFocus.None
exclusionMode: ExclusionMode.Ignore
color: Theme.textAlpha(0)
anchors { bottom: true; left: true; right: true }
margins { bottom: 10; left: 12; right: 12 }
implicitHeight: 290
property int selectedIndex: 0
function prepareOpen() {
deck.clearSearch();
selectedIndex = 0;
deck.focusSearch();
}
function clampSelection(index) {
return model.apps.length === 0 ? 0 : Math.max(0, Math.min(model.apps.length - 1, index));
}
function move(delta) {
const count = model.apps.length;
if (count === 0)
return;
selectedIndex = ((selectedIndex + delta) % count + count) % count;
}
function launch(index) {
if (model.launch(index))
root.active = false;
}
AppModel { id: model; search: deck.query }
CyberDockContent {
id: deck
width: 1440
height: 272
x: (parent.width - width) / 2
y: root.active ? parent.height - height : parent.height + 4
scale: Math.min(1, (parent.width - 16) / width)
transformOrigin: Item.Bottom
apps: model.apps
selectedIndex: win.selectedIndex
Behavior on y {
NumberAnimation {
duration: 240
easing.type: root.active ? Easing.OutCubic : Easing.InCubic
}
}
onSelectionRequested: index => win.selectedIndex = win.clampSelection(index)
onMoveRequested: delta => win.move(delta)
onLaunchRequested: index => win.launch(index)
onDismissRequested: root.active = false
}
}
}