diff --git a/Assets/Translations/en.json b/Assets/Translations/en.json index c4ff50ae..d3a3b630 100644 --- a/Assets/Translations/en.json +++ b/Assets/Translations/en.json @@ -354,6 +354,10 @@ "label": "Use App2Unit to launch applications", "description": "Uses an alternative launch method to better manage app processes and prevent issues." }, + "ignore-initial-mouse": { + "label": "Ignore initial mouse position", + "description": "When enabled, the launcher always defaults to the first item, ignoring where your mouse cursor is positioned. Selection only changes when you move the mouse." + }, "terminal-command": { "label": "Terminal command", "description": "Command to launch a terminal. E.g., 'kitty -e' or 'gnome-terminal --'." diff --git a/Assets/settings-default.json b/Assets/settings-default.json index 9b63d3ae..1df70c5a 100644 --- a/Assets/settings-default.json +++ b/Assets/settings-default.json @@ -1,5 +1,5 @@ { - "settingsVersion": 15, + "settingsVersion": 16, "bar": { "position": "top", "backgroundOpacity": 1, @@ -104,7 +104,8 @@ "pinnedExecs": [], "useApp2Unit": false, "sortByMostUsed": true, - "terminalCommand": "xterm -e" + "terminalCommand": "xterm -e", + "ignoreInitialMousePosition": false }, "controlCenter": { "position": "close_to_bar_button", diff --git a/Commons/Settings.qml b/Commons/Settings.qml index d8e8f4bf..355c42e3 100644 --- a/Commons/Settings.qml +++ b/Commons/Settings.qml @@ -125,7 +125,7 @@ Singleton { JsonAdapter { id: adapter - property int settingsVersion: 15 + property int settingsVersion: 16 // bar property JsonObject bar: JsonObject { @@ -233,6 +233,7 @@ Singleton { property bool useApp2Unit: false property bool sortByMostUsed: true property string terminalCommand: "xterm -e" + property bool ignoreInitialMousePosition: true } // control center diff --git a/Modules/Launcher/Launcher.qml b/Modules/Launcher/Launcher.qml index 25bafe37..d31762ea 100644 --- a/Modules/Launcher/Launcher.qml +++ b/Modules/Launcher/Launcher.qml @@ -35,6 +35,7 @@ NPanel { property var plugins: [] property var activePlugin: null property bool resultsReady: false + property bool ignoreMouseHover: false readonly property int badgeSize: Math.round(Style.baseWidgetSize * 1.6 * scaling) readonly property int entryHeight: Math.round(badgeSize + Style.marginM * 2 * scaling) @@ -94,6 +95,7 @@ NPanel { // Lifecycle onOpened: { resultsReady = false + ignoreMouseHover = Settings.data.appLauncher.ignoreInitialMousePosition // Use setting value // Notify plugins for (let plugin of plugins) { @@ -110,6 +112,7 @@ NPanel { onClosed: { // Reset search text searchText = "" + ignoreMouseHover = Settings.data.appLauncher.ignoreInitialMousePosition // Use setting value // Notify plugins for (let plugin of plugins) { @@ -154,6 +157,49 @@ NPanel { color: Color.transparent opacity: resultsReady ? 1.0 : 0.0 + // Global MouseArea to detect mouse movement + MouseArea { + id: mouseMovementDetector + anchors.fill: parent + z: -999 + hoverEnabled: true + propagateComposedEvents: true + acceptedButtons: Qt.NoButton + + property real lastX: 0 + property real lastY: 0 + property bool initialized: false + + onPositionChanged: mouse => { + // Store initial position + if (!initialized) { + lastX = mouse.x + lastY = mouse.y + initialized = true + return + } + + // Check if mouse actually moved + const deltaX = Math.abs(mouse.x - lastX) + const deltaY = Math.abs(mouse.y - lastY) + if (deltaX > 1 || deltaY > 1) { + root.ignoreMouseHover = false + lastX = mouse.x + lastY = mouse.y + } + } + + // Reset when launcher opens + Connections { + target: root + function onOpenedChanged() { + if (root.opened) { + mouseMovementDetector.initialized = false + } + } + } + } + Behavior on opacity { NumberAnimation { duration: Style.animationFast @@ -321,7 +367,7 @@ NPanel { delegate: Rectangle { id: entry - property bool isSelected: mouseArea.containsMouse || (index === selectedIndex) + property bool isSelected: (!root.ignoreMouseHover && mouseArea.containsMouse) || (index === selectedIndex) // Accessor for app id property string appId: (modelData && modelData.appId) ? String(modelData.appId) : "" @@ -523,7 +569,9 @@ NPanel { hoverEnabled: true cursorShape: Qt.PointingHandCursor onEntered: { - selectedIndex = index + if (!root.ignoreMouseHover) { + selectedIndex = index + } } onClicked: mouse => { if (mouse.button === Qt.LeftButton) { diff --git a/Modules/Settings/Tabs/LauncherTab.qml b/Modules/Settings/Tabs/LauncherTab.qml index a01c13fb..1149c811 100644 --- a/Modules/Settings/Tabs/LauncherTab.qml +++ b/Modules/Settings/Tabs/LauncherTab.qml @@ -99,6 +99,13 @@ ColumnLayout { onToggled: checked => Settings.data.appLauncher.useApp2Unit = checked } + NToggle { + label: I18n.tr("settings.launcher.settings.ignore-initial-mouse.label") + description: I18n.tr("settings.launcher.settings.ignore-initial-mouse.description") + checked: Settings.data.appLauncher.ignoreInitialMousePosition + onToggled: checked => Settings.data.appLauncher.ignoreInitialMousePosition = checked + } + NTextInput { label: I18n.tr("settings.launcher.settings.terminal-command.label") description: I18n.tr("settings.launcher.settings.terminal-command.description")