feat: input mgr

This commit is contained in:
2026-01-24 14:35:16 +02:00
parent 1a37e76c4a
commit e5d5b2e086
14 changed files with 418 additions and 67 deletions

View File

@@ -28,6 +28,8 @@ top bar:
color: #04047C
settings:
offset from top + 9slice: 180
with 9slice borders:
x: 1217
y: 767

View File

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

65
Shell/InputShortcuts.qml Normal file
View File

@@ -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);
}
}

73
Shell/Overlay.qml Normal file
View File

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

View File

@@ -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);
}
}

View File

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

View File

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

View File

@@ -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,14 +64,6 @@ 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
@@ -63,12 +84,17 @@ Item {
anchors.centerIn: parent
selected: topbar.selectedIndex == repeatitem.index
iconSource: topbar.iconSources[repeatitem.index]
showSoul: !topbar.manager.quickSettingsOpen
}
MouseArea {
anchors.fill: parent
onClicked: {
topbar.selectedIndex = repeatitem.index;
if (topbar.iconSources[repeatitem.index] === "config.png") {
ShellStateManager.openQuickSettings({
source: "Topbar"
});
}
}
}

View File

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

View File

@@ -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")
}
}

53
hyprland.conf Normal file
View File

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

View File

@@ -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"