From e5d5b2e086e1a3a9ffa9897845fef4f0bd0ded2c Mon Sep 17 00:00:00 2001 From: ocbwoy3 Date: Sat, 24 Jan 2026 14:35:16 +0200 Subject: [PATCH] feat: input mgr --- CONSTANTS.txt | 2 + .../{WLRLayerDialogBox.qml => DialogBox.qml} | 0 .../{WLRLayerHealthbar.qml => Healthbar.qml} | 5 +- Shell/InputShortcuts.qml | 65 ++++++++++++++ Shell/Overlay.qml | 73 ++++++++++++++++ Shell/Settings/SettingsWindow.qml | 0 Shell/ShellInputManager.qml | 58 +++++++++++++ Shell/ShellStateManager.qml | 34 +++++++- Shell/{WLRLayerTopbar.qml => Topbar.qml} | 12 +-- Shell/Topbar/Topbar.qml | 86 ++++++++++++------- Shell/Window/Window.qml | 51 ++++++++--- Shell/Windows/QuickSettings/QuickSettings.qml | 23 +++++ hyprland.conf | 53 ++++++++++++ shell.qml | 23 ++--- 14 files changed, 418 insertions(+), 67 deletions(-) rename Shell/{WLRLayerDialogBox.qml => DialogBox.qml} (100%) rename Shell/{WLRLayerHealthbar.qml => Healthbar.qml} (76%) create mode 100644 Shell/InputShortcuts.qml create mode 100644 Shell/Overlay.qml delete mode 100644 Shell/Settings/SettingsWindow.qml create mode 100644 Shell/ShellInputManager.qml rename Shell/{WLRLayerTopbar.qml => Topbar.qml} (64%) create mode 100644 Shell/Windows/QuickSettings/QuickSettings.qml create mode 100644 hyprland.conf diff --git a/CONSTANTS.txt b/CONSTANTS.txt index ba71d31..4bf7828 100644 --- a/CONSTANTS.txt +++ b/CONSTANTS.txt @@ -28,6 +28,8 @@ top bar: color: #04047C settings: + offset from top + 9slice: 180 + with 9slice borders: x: 1217 y: 767 diff --git a/Shell/WLRLayerDialogBox.qml b/Shell/DialogBox.qml similarity index 100% rename from Shell/WLRLayerDialogBox.qml rename to Shell/DialogBox.qml diff --git a/Shell/WLRLayerHealthbar.qml b/Shell/Healthbar.qml similarity index 76% rename from Shell/WLRLayerHealthbar.qml rename to Shell/Healthbar.qml index 4bed401..e5c4c51 100644 --- a/Shell/WLRLayerHealthbar.qml +++ b/Shell/Healthbar.qml @@ -1,5 +1,6 @@ import Quickshell import Quickshell.Wayland + import "Healthbar" PanelWindow { @@ -10,9 +11,11 @@ PanelWindow { } WlrLayershell.layer: WlrLayer.Top + WlrLayershell.focusable: false + WlrLayershell.keyboardFocus: WlrKeyboardFocus.None WlrLayershell.namespace: "deltarune-quickshell-bottom" - visible: true + visible: ShellStateManager.shellOpen exclusionMode: ExclusionMode.Ignore aboveWindows: true focusable: false diff --git a/Shell/InputShortcuts.qml b/Shell/InputShortcuts.qml new file mode 100644 index 0000000..c0a3150 --- /dev/null +++ b/Shell/InputShortcuts.qml @@ -0,0 +1,65 @@ +import QtQuick +import Quickshell +import Quickshell.Hyprland +import "." + +Item { + id: shortcuts + property var manager: ShellInputManager + + GlobalShortcut { + appid: "deltarune" + triggerDescription: "Close the menu" + name: "shell_close" + onReleased: manager.closeShell(); + } + + GlobalShortcut { + appid: "deltarune" + triggerDescription: "Open the menu" + name: "shell_open" + onReleased: manager.openShell(); + } + + GlobalShortcut { + appid: "deltarune" + triggerDescription: "Input back" + name: "input_back" + onReleased: manager.handleKeyInternal(Qt.Key_Shift); + } + + GlobalShortcut { + appid: "deltarune" + triggerDescription: "Input submit" + name: "input_enter" + onReleased: manager.handleKeyInternal(Qt.Key_Enter); + } + + GlobalShortcut { + appid: "deltarune" + triggerDescription: "Input up" + name: "input_up" + onReleased: manager.handleKeyInternal(Qt.Key_Up); + } + + GlobalShortcut { + appid: "deltarune" + triggerDescription: "Input down" + name: "input_down" + onReleased: manager.handleKeyInternal(Qt.Key_Down); + } + + GlobalShortcut { + appid: "deltarune" + triggerDescription: "Input left" + name: "input_left" + onReleased: manager.handleKeyInternal(Qt.Key_Left); + } + + GlobalShortcut { + appid: "deltarune" + triggerDescription: "Input right" + name: "input_right" + onReleased: manager.handleKeyInternal(Qt.Key_Right); + } +} diff --git a/Shell/Overlay.qml b/Shell/Overlay.qml new file mode 100644 index 0000000..e0b6bd6 --- /dev/null +++ b/Shell/Overlay.qml @@ -0,0 +1,73 @@ +import QtQuick +import Quickshell +import Quickshell.Wayland +import Quickshell.Hyprland + +import "Windows/QuickSettings" + +PanelWindow { + id: overlay + anchors { + top: true + left: true + right: true + bottom: true + } + + WlrLayershell.layer: WlrLayer.Overlay + WlrLayershell.focusable: true + WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive + WlrLayershell.namespace: "deltarune-quickshell-overlay" + + visible: ShellStateManager.shellOpen + exclusionMode: ExclusionMode.Ignore + aboveWindows: true + focusable: ShellStateManager.shellOpen + color: "#00000000" + + HyprlandFocusGrab { + id: grab + active: true + windows: [overlay] + } + + FocusScope { + id: overlayFocus + anchors.fill: parent + focus: ShellStateManager.shellOpen + + Component.onCompleted: { + if (ShellStateManager.shellOpen) + overlayFocus.forceActiveFocus(); + } + + onVisibleChanged: if (visible) + overlayFocus.forceActiveFocus() + + onFocusChanged: { + if (focus && ShellStateManager.shellOpen) + overlayFocus.forceActiveFocus(); + } + + Keys.onReleased: function (event) { + if (!ShellStateManager.shellOpen) + return; + if (ShellInputManager.handleKey(event)) { + event.accepted = true; + } + } + } + + Connections { + target: ShellStateManager + function onShellOpenChanged() { + if (ShellStateManager.shellOpen) + overlayFocus.forceActiveFocus(); + } + } + + QuickSettings { + id: quickSettingsWindow + manager: ShellStateManager + } +} diff --git a/Shell/Settings/SettingsWindow.qml b/Shell/Settings/SettingsWindow.qml deleted file mode 100644 index e69de29..0000000 diff --git a/Shell/ShellInputManager.qml b/Shell/ShellInputManager.qml new file mode 100644 index 0000000..f4a346d --- /dev/null +++ b/Shell/ShellInputManager.qml @@ -0,0 +1,58 @@ +pragma Singleton +import QtQuick +import Quickshell +import Quickshell.Hyprland + +QtObject { + id: inputManager + + property var handlers: ({}) + + function openShell() { + ShellStateManager.openShell(); + } + + function closeShell() { + ShellStateManager.closeShell(); + } + + function registerHandler(context, handler) { + handlers[context] = handler; + } + + function unregisterHandler(context) { + handlers[context] = undefined; + if (handlers.hasOwnProperty(context)) + delete handlers[context]; + } + + function handleKeyInternal(key) { + if (!ShellStateManager.shellOpen) + return false; + + var context = ShellStateManager.quickSettingsOpen ? "quickSettings" : "topbar"; + var handler = handlers[context]; + if (handler) { + var handled = handler(key); + if (handled) + return true; + } + + if (key === Qt.Key_Escape || key === Qt.Key_Shift || key === Qt.Key_X) { + if (context === "quickSettings") { + ShellStateManager.closeQuickSettings(); + } else if (context === "topbar") { + ShellStateManager.closeShell(); + Hyprland.dispatch("submap reset"); + } + return true; + } + + return false; + } + + function handleKey(event) { + handleKeyInternal(event.key); + } + +} diff --git a/Shell/ShellStateManager.qml b/Shell/ShellStateManager.qml index 506323b..a5786bc 100644 --- a/Shell/ShellStateManager.qml +++ b/Shell/ShellStateManager.qml @@ -1,20 +1,26 @@ pragma Singleton import QtQuick +import Quickshell.Hyprland QtObject { id: manager property bool shellOpen: false + property bool quickSettingsOpen: false property var windowRequests: ({}) + property var quickSettingsPayload: ({}) property var globals: ({}) signal shellOpened signal shellClosed + signal quickSettingsOpened + signal quickSettingsClosed signal windowRequested(string name, var payload) function openShell() { if (!shellOpen) { console.log("ShellStateManager: openShell"); + Hyprland.dispatch("submap deltarune"); shellOpen = true; shellOpened(); } @@ -23,13 +29,16 @@ QtObject { function closeShell() { if (shellOpen) { console.log("ShellStateManager: closeShell"); + Hyprland.dispatch("submap reset"); + if (quickSettingsOpen) { + closeQuickSettings(); + } shellOpen = false; shellClosed(); } } function toggleShell() { - console.log("ShellStateManager: toggleShell (isOpen " + shellOpen + ")"); shellOpen ? closeShell() : openShell(); } @@ -42,6 +51,29 @@ QtObject { windowRequested(name, payload); } + function openQuickSettings(payload) { + var normalizedPayload = payload || ({}); + quickSettingsPayload = normalizedPayload; + console.log("ShellStateManager: openQuickSettings", normalizedPayload); + if (!quickSettingsOpen) { + quickSettingsOpen = true; + quickSettingsOpened(); + } + requestWindow("quickSettings", normalizedPayload); + } + + function closeQuickSettings() { + if (quickSettingsOpen) { + console.log("ShellStateManager: closeQuickSettings"); + quickSettingsOpen = false; + quickSettingsClosed(); + } + } + + function toggleQuickSettings(payload) { + quickSettingsOpen ? closeQuickSettings() : openQuickSettings(payload); + } + function setGlobal(key, value) { globals[key] = value; } diff --git a/Shell/WLRLayerTopbar.qml b/Shell/Topbar.qml similarity index 64% rename from Shell/WLRLayerTopbar.qml rename to Shell/Topbar.qml index 1227d66..b184ed5 100644 --- a/Shell/WLRLayerTopbar.qml +++ b/Shell/Topbar.qml @@ -11,19 +11,21 @@ PanelWindow { } WlrLayershell.layer: WlrLayer.Top - WlrLayershell.focusable: true - WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand + WlrLayershell.focusable: false + WlrLayershell.keyboardFocus: WlrKeyboardFocus.None WlrLayershell.namespace: "deltarune-quickshell" mask: Region {} - visible: true + visible: ShellStateManager.shellOpen exclusionMode: ExclusionMode.Ignore aboveWindows: true - focusable: true + focusable: false implicitHeight: 182 color: "#000000" - Topbar {} + Topbar { + manager: ShellStateManager + } } diff --git a/Shell/Topbar/Topbar.qml b/Shell/Topbar/Topbar.qml index 8d5094e..a0a07d2 100644 --- a/Shell/Topbar/Topbar.qml +++ b/Shell/Topbar/Topbar.qml @@ -1,5 +1,6 @@ pragma ComponentBehavior: Bound import QtQuick +import "../" import "topbar" @@ -8,6 +9,8 @@ Item { implicitWidth: 1312 implicitHeight: 182 + property var manager: ShellStateManager + anchors.centerIn: parent property var iconSources: ["item.png", "equip.png", "power.png", "config.png"] @@ -28,6 +31,32 @@ Item { } } + QtObject { + id: overlayKeyHandler + function handle(key) { + if (key === Qt.Key_Left) { + moveSelection(-1); + return true; + } + if (key === Qt.Key_Right) { + moveSelection(1); + return true; + } + if (key === Qt.Key_Return || key === Qt.Key_Enter || key === Qt.Key_Z) { + if (iconSources[selectedIndex] === "config.png") { + ShellStateManager.openQuickSettings({ + source: "Topbar" + }); + return true; + } + } + return false; + } + + Component.onCompleted: ShellInputManager.registerHandler("topbar", handle) + Component.onDestruction: ShellInputManager.unregisterHandler("topbar") + } + DarkDollarsDisplay {} Image { @@ -35,40 +64,37 @@ Item { x: -64 } - FocusScope { - id: focusScope - focus: true - anchors.fill: parent - Keys.onLeftPressed: topbar.moveSelection(-1) - Keys.onRightPressed: topbar.moveSelection(1) - Component.onCompleted: forceActiveFocus() + Row { + id: iconRow + spacing: 76 + anchors.horizontalCenter: parent.horizontalCenter + y: 45 + anchors.horizontalCenterOffset: -38 - Row { - id: iconRow - spacing: 76 - anchors.horizontalCenter: parent.horizontalCenter - y: 45 - anchors.horizontalCenterOffset: -38 + Repeater { + model: topbar.iconSources - Repeater { - model: topbar.iconSources + delegate: Item { + id: repeatitem + required property int index + width: 149 + height: 108 - delegate: Item { - id: repeatitem - required property int index - width: 149 - height: 108 + TopbarSettingIcon { + anchors.centerIn: parent + selected: topbar.selectedIndex == repeatitem.index + iconSource: topbar.iconSources[repeatitem.index] + showSoul: !topbar.manager.quickSettingsOpen + } - TopbarSettingIcon { - anchors.centerIn: parent - selected: topbar.selectedIndex == repeatitem.index - iconSource: topbar.iconSources[repeatitem.index] - } - - MouseArea { - anchors.fill: parent - onClicked: { - topbar.selectedIndex = repeatitem.index; + MouseArea { + anchors.fill: parent + onClicked: { + topbar.selectedIndex = repeatitem.index; + if (topbar.iconSources[repeatitem.index] === "config.png") { + ShellStateManager.openQuickSettings({ + source: "Topbar" + }); } } } diff --git a/Shell/Window/Window.qml b/Shell/Window/Window.qml index 0b34e8f..fcd14a1 100644 --- a/Shell/Window/Window.qml +++ b/Shell/Window/Window.qml @@ -1,19 +1,42 @@ -import Quickshell -import Quickshell.Wayland import QtQuick -PanelWindow { - WlrLayershell.layer: WlrLayer.Top - WlrLayershell.focusable: true - WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand - WlrLayershell.namespace: "deltarune-quickshell" +Item { + id: windowFrame + property color backgroundColor: "#000000" + property int borderSize: 52 + width: 640 + height: 360 + clip: true - visible: true - exclusionMode: ExclusionMode.Ignore - aboveWindows: true - focusable: true + Rectangle { + anchors.fill: parent + anchors.margins: windowFrame.borderSize + color: windowFrame.backgroundColor + } - implicitWidth: 1217 - implicitHeight: 767 - color: "#000000" + BorderImage { + anchors.fill: parent + anchors.margins: 16 + 4 + anchors.centerIn: parent + source: "./border.png" + width: 108 + height: 108 + + border { + left: windowFrame.borderSize + top: windowFrame.borderSize + right: windowFrame.borderSize + bottom: windowFrame.borderSize + } + } + + Item { + id: contentSlot + anchors { + fill: parent + margins: windowFrame.borderSize + } + } + + default property alias windowContent: contentSlot.data } diff --git a/Shell/Windows/QuickSettings/QuickSettings.qml b/Shell/Windows/QuickSettings/QuickSettings.qml new file mode 100644 index 0000000..a85854d --- /dev/null +++ b/Shell/Windows/QuickSettings/QuickSettings.qml @@ -0,0 +1,23 @@ +import QtQuick +import "../.." +import "../../Window" as ShellWindow + +ShellWindow.Window { + id: quickSettingsWindow + property var manager: ShellStateManager + + width: 1217 + 52 + height: 767 + 52 + visible: manager ? manager.quickSettingsOpen : false + anchors.centerIn: parent + + QtObject { + id: quickSettingsKeyHandler + function handle(event) { + return false; + } + + Component.onCompleted: ShellInputManager.registerHandler("quickSettings", handle) + Component.onDestruction: ShellInputManager.unregisterHandler("quickSettings") + } +} diff --git a/hyprland.conf b/hyprland.conf new file mode 100644 index 0000000..944e81b --- /dev/null +++ b/hyprland.conf @@ -0,0 +1,53 @@ +# + +layerrule { + name = deltarune qs mn + match:namespace = deltarune-quickshell + animation = slide top + above_lock = 2 + order = -20 + no_screen_share = off +} + +layerrule { + name = deltarune qs hb + match:namespace = deltarune-quickshell-bottom + animation = slide bottom + above_lock = 2 + order = -20 + no_screen_share = off +} + +layerrule { + name = deltarune qs db + match:namespace = deltarune-quickshell-dialogbox + animation = slide bottom + order = 100 + above_lock = 2 + order = -20 + no_screen_share = off +} + +bind = SUPER, A, global, deltarune:shell_open + +# here goes the dark magic that lets you open this in dletarune (and any other exclusive fullscreen app/game) + +submap = deltarune + +bind = SUPER, A, global, deltarune:shell_close + +bind = , ESCAPE, global, deltarune:input_back +bind = , Shift_L, global, deltarune:input_back +bind = , Shift_R, global, deltarune:input_back +bind = , X, global, deltarune:input_back + +bind = , Z, global, deltarune:input_enter +bind = , ENTER, global, deltarune:input_enter +bind = , RETURN, global, deltarune:input_enter + +bind = , UP, global, deltarune:input_up +bind = , DOWN, global, deltarune:input_down +bind = , LEFT, global, deltarune:input_left +bind = , RIGHT, global, deltarune:input_right + +submap = reset diff --git a/shell.qml b/shell.qml index 31e095d..b8f63c7 100644 --- a/shell.qml +++ b/shell.qml @@ -1,30 +1,21 @@ import Quickshell import Quickshell.Io import Quickshell.Hyprland +import QtQuick import "Shell" ShellRoot { id: baseShell - // Use the singleton directly; it controls the shell state globally. property bool isOpen: ShellStateManager.shellOpen - WLRLayerTopbar { - visible: baseShell.isOpen - } - WLRLayerHealthbar { - visible: baseShell.isOpen - } - WLRLayerDialogBox {} + Overlay {} - GlobalShortcut { - appid: "deltarune" - triggerDescription: "Toggle the menu" - name: "shell_toggle" - onReleased: { - ShellStateManager.toggleShell(); - } - } + Topbar {} + Healthbar {} + DialogBox {} + + InputShortcuts {} IpcHandler { target: "deltarune.shell"