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 } } }