diff --git a/Services/PreferencesService.qml b/Services/PreferencesService.qml deleted file mode 100644 index 386ae118..00000000 --- a/Services/PreferencesService.qml +++ /dev/null @@ -1,105 +0,0 @@ -import QtQuick -import QtCore -import Quickshell -import Quickshell.Io -pragma Singleton -pragma ComponentBehavior: Bound - -Singleton { - id: root - - property string configDir: StandardPaths.writableLocation(StandardPaths.ConfigLocation) + "/DankMaterialShell" - property string recentAppsFile: configDir + "/recentApps.json" - property int maxRecentApps: 10 - property var recentApps: [] - - // Create config directory on startup - Process { - id: mkdirProcess - command: ["mkdir", "-p", root.configDir] - running: true - onExited: { - loadRecentApps() - } - } - - FileView { - id: recentAppsFileView - path: root.recentAppsFile - - onTextChanged: { - if (text && text.length > 0) { - try { - var data = JSON.parse(text) - if (Array.isArray(data)) { - root.recentApps = data - } - } catch (e) { - console.log("PreferencesService: Invalid recent apps format") - root.recentApps = [] - } - } - } - } - - function loadRecentApps() { - // FileView will automatically load and trigger onTextChanged - if (!recentAppsFileView.text || recentAppsFileView.text.length === 0) { - recentApps = [] - } - } - - function saveRecentApps() { - var jsonData = JSON.stringify(recentApps, null, 2) - var process = Qt.createQmlObject(' - import Quickshell.Io - Process { - command: ["sh", "-c", "echo \'' + jsonData.replace(/'/g, "'\"'\"'") + '\' > \'' + root.recentAppsFile + '\'"] - running: true - onExited: { - if (exitCode !== 0) { - console.warn("Failed to save recent apps:", exitCode) - } - destroy() - } - } - ', root) - } - - function addRecentApp(app) { - if (!app) return - - var execProp = app.execString || "" - if (!execProp) return - - // Create a minimal app object to store - var appData = { - name: app.name, - exec: execProp, - icon: app.icon || "application-x-executable", - comment: app.comment || "" - } - - // Remove existing entry if present - recentApps = recentApps.filter(a => a.exec !== execProp) - - // Add to front - recentApps.unshift(appData) - - // Limit size - if (recentApps.length > maxRecentApps) { - recentApps = recentApps.slice(0, maxRecentApps) - } - - saveRecentApps() - } - - function getRecentApps() { - return recentApps - } - - function clearRecentApps() { - recentApps = [] - saveRecentApps() - } -} \ No newline at end of file diff --git a/Services/qmldir b/Services/qmldir index e1a6ccc8..d8080f2a 100644 --- a/Services/qmldir +++ b/Services/qmldir @@ -9,7 +9,6 @@ singleton BrightnessService 1.0 BrightnessService.qml singleton BatteryService 1.0 BatteryService.qml singleton SystemMonitorService 1.0 SystemMonitorService.qml singleton AppSearchService 1.0 AppSearchService.qml -singleton PreferencesService 1.0 PreferencesService.qml singleton LauncherService 1.0 LauncherService.qml singleton NiriWorkspaceService 1.0 NiriWorkspaceService.qml singleton CalendarService 1.0 CalendarService.qml diff --git a/Widgets/AppLauncher.qml b/Widgets/AppLauncher.qml index 09fd380d..4e5158e5 100644 --- a/Widgets/AppLauncher.qml +++ b/Widgets/AppLauncher.qml @@ -37,6 +37,7 @@ PanelWindow { property var pinnedApps: ["firefox", "code", "terminal", "file-manager"] property bool showCategories: false property string viewMode: "list" // "list" or "grid" + property int selectedIndex: 0 ListModel { id: filteredModel } @@ -95,6 +96,7 @@ PanelWindow { function updateFilteredModel() { filteredModel.clear() + selectedIndex = 0 var apps = [] @@ -131,6 +133,57 @@ PanelWindow { }) }) } + + function selectNext() { + if (filteredModel.count > 0) { + if (viewMode === "grid") { + // Grid navigation: move by columns + var columnsCount = appGrid.columnsCount || 8 + selectedIndex = Math.min(selectedIndex + columnsCount, filteredModel.count - 1) + } else { + // List navigation: next item + selectedIndex = (selectedIndex + 1) % filteredModel.count + } + } + } + + function selectPrevious() { + if (filteredModel.count > 0) { + if (viewMode === "grid") { + // Grid navigation: move by columns + var columnsCount = appGrid.columnsCount || 8 + selectedIndex = Math.max(selectedIndex - columnsCount, 0) + } else { + // List navigation: previous item + selectedIndex = selectedIndex > 0 ? selectedIndex - 1 : filteredModel.count - 1 + } + } + } + + function selectNextInRow() { + if (filteredModel.count > 0 && viewMode === "grid") { + selectedIndex = Math.min(selectedIndex + 1, filteredModel.count - 1) + } + } + + function selectPreviousInRow() { + if (filteredModel.count > 0 && viewMode === "grid") { + selectedIndex = Math.max(selectedIndex - 1, 0) + } + } + + function launchSelected() { + if (filteredModel.count > 0 && selectedIndex >= 0 && selectedIndex < filteredModel.count) { + var selectedApp = filteredModel.get(selectedIndex) + if (selectedApp.desktopEntry) { + Prefs.addRecentApp(selectedApp.desktopEntry) + AppSearchService.launchApp(selectedApp.desktopEntry) + } else { + launcher.launchApp(selectedApp.exec) + } + launcher.hide() + } + } Component { id: iconComponent @@ -277,6 +330,21 @@ PanelWindow { if (event.key === Qt.Key_Escape) { launcher.hide() event.accepted = true + } else if (event.key === Qt.Key_Down) { + selectNext() + event.accepted = true + } else if (event.key === Qt.Key_Up) { + selectPrevious() + event.accepted = true + } else if (event.key === Qt.Key_Right && viewMode === "grid") { + selectNextInRow() + event.accepted = true + } else if (event.key === Qt.Key_Left && viewMode === "grid") { + selectPreviousInRow() + event.accepted = true + } else if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) { + launchSelected() + event.accepted = true } } @@ -559,6 +627,7 @@ PanelWindow { model: filteredModel delegate: listDelegate + currentIndex: selectedIndex // Make mouse wheel scrolling more responsive property real wheelStepSize: 60 @@ -566,6 +635,8 @@ PanelWindow { MouseArea { anchors.fill: parent acceptedButtons: Qt.NoButton + propagateComposedEvents: true + z: -1 onWheel: (wheel) => { var delta = wheel.angleDelta.y @@ -618,6 +689,8 @@ PanelWindow { MouseArea { anchors.fill: parent acceptedButtons: Qt.NoButton + propagateComposedEvents: true + z: -1 onWheel: (wheel) => { var delta = wheel.angleDelta.y @@ -689,6 +762,8 @@ PanelWindow { MouseArea { anchors.fill: parent acceptedButtons: Qt.NoButton + propagateComposedEvents: true + z: -1 onWheel: (wheel) => { var delta = wheel.angleDelta.y @@ -746,10 +821,12 @@ PanelWindow { width: appList.width height: 72 radius: Theme.cornerRadiusLarge - color: appMouseArea.hovered ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) - : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.03) - border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08) - border.width: 1 + color: ListView.isCurrentItem ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.16) : + appMouseArea.hovered ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : + Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.03) + border.color: ListView.isCurrentItem ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.3) : + Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08) + border.width: ListView.isCurrentItem ? 2 : 1 Row { anchors.fill: parent @@ -799,6 +876,9 @@ PanelWindow { anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor + z: 10 + property bool hovered: containsMouse + onEntered: selectedIndex = index onClicked: { if (model.desktopEntry) { Prefs.addRecentApp(model.desktopEntry) @@ -819,10 +899,12 @@ PanelWindow { width: appGrid.cellWidth - 8 height: appGrid.cellHeight - 8 radius: Theme.cornerRadiusLarge - color: gridAppArea.hovered ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) - : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.03) - border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08) - border.width: 1 + color: selectedIndex === index ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.16) : + gridAppArea.hovered ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : + Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.03) + border.color: selectedIndex === index ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.3) : + Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08) + border.width: selectedIndex === index ? 2 : 1 Column { anchors.centerIn: parent @@ -861,6 +943,9 @@ PanelWindow { anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor + z: 10 + property bool hovered: containsMouse + onEntered: selectedIndex = index onClicked: { if (model.desktopEntry) { Prefs.addRecentApp(model.desktopEntry) diff --git a/Widgets/BatteryWidget.qml b/Widgets/BatteryWidget.qml index 3749ea97..0335dabd 100644 --- a/Widgets/BatteryWidget.qml +++ b/Widgets/BatteryWidget.qml @@ -1,5 +1,4 @@ import QtQuick -import QtQuick.Controls import "../Common" import "../Services" @@ -65,7 +64,6 @@ Rectangle { onClicked: { batteryPopupVisible = !batteryPopupVisible - root.batteryPopupVisible = batteryPopupVisible } } diff --git a/Widgets/PowerButton.qml b/Widgets/PowerButton.qml deleted file mode 100644 index 423b94c2..00000000 --- a/Widgets/PowerButton.qml +++ /dev/null @@ -1,74 +0,0 @@ -import QtQuick -import QtQuick.Controls -import "../Common" - -Rectangle { - id: powerButton - - width: 48 - height: 30 - radius: Theme.cornerRadius - color: powerArea.containsMouse || root.powerMenuVisible ? - Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.16) : - Qt.rgba(Theme.secondary.r, Theme.secondary.g, Theme.secondary.b, 0.08) - - // Power icon - Text { - text: "power_settings_new" - font.family: Theme.iconFont - font.pixelSize: Theme.iconSize - 6 - color: powerArea.containsMouse || root.powerMenuVisible ? Theme.error : Theme.surfaceText - anchors.centerIn: parent - } - - MouseArea { - id: powerArea - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - - onClicked: { - root.powerMenuVisible = !root.powerMenuVisible - } - } - - // Tooltip on hover - Rectangle { - id: powerTooltip - width: tooltipText.contentWidth + Theme.spacingM * 2 - height: tooltipText.contentHeight + Theme.spacingS * 2 - radius: Theme.cornerRadius - color: Theme.surfaceContainer - border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2) - border.width: 1 - visible: powerArea.containsMouse && !root.powerMenuVisible - - anchors.bottom: parent.top - anchors.bottomMargin: Theme.spacingS - anchors.horizontalCenter: parent.horizontalCenter - - opacity: powerArea.containsMouse ? 1.0 : 0.0 - - Behavior on opacity { - NumberAnimation { - duration: Theme.shortDuration - easing.type: Theme.standardEasing - } - } - - Text { - id: tooltipText - text: "Power Menu" - font.pixelSize: Theme.fontSizeSmall - color: Theme.surfaceText - anchors.centerIn: parent - } - } - - Behavior on color { - ColorAnimation { - duration: Theme.shortDuration - easing.type: Theme.standardEasing - } - } -} \ No newline at end of file diff --git a/Widgets/SpotlightLauncher.qml b/Widgets/SpotlightLauncher.qml index e80a21a2..c99281a3 100644 --- a/Widgets/SpotlightLauncher.qml +++ b/Widgets/SpotlightLauncher.qml @@ -499,6 +499,8 @@ PanelWindow { MouseArea { anchors.fill: parent acceptedButtons: Qt.NoButton + propagateComposedEvents: true + z: -1 onWheel: (wheel) => { var delta = wheel.angleDelta.y @@ -569,6 +571,7 @@ PanelWindow { anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor + z: 10 onEntered: selectedIndex = index onClicked: launchApp(model) } @@ -611,6 +614,8 @@ PanelWindow { MouseArea { anchors.fill: parent acceptedButtons: Qt.NoButton + propagateComposedEvents: true + z: -1 onWheel: (wheel) => { var delta = wheel.angleDelta.y @@ -679,6 +684,7 @@ PanelWindow { anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor + z: 10 onEntered: selectedIndex = index onClicked: launchApp(model) } diff --git a/Widgets/TopBar/TopBar.qml b/Widgets/TopBar/TopBar.qml index f7e97577..49234f6a 100644 --- a/Widgets/TopBar/TopBar.qml +++ b/Widgets/TopBar/TopBar.qml @@ -68,17 +68,6 @@ PanelWindow { property int count: 0 } - // Battery widget and other widgets are imported from their original locations - // These will be handled by the parent shell - property alias batteryWidget: batteryWidgetProxy - property alias cpuMonitorWidget: cpuMonitorWidgetProxy - property alias ramMonitorWidget: ramMonitorWidgetProxy - property alias powerButton: powerButtonProxy - - QtObject { id: batteryWidgetProxy } - QtObject { id: cpuMonitorWidgetProxy } - QtObject { id: ramMonitorWidgetProxy } - QtObject { id: powerButtonProxy } anchors { top: true diff --git a/Widgets/qmldir b/Widgets/qmldir index 5e6e5ba6..820633e9 100644 --- a/Widgets/qmldir +++ b/Widgets/qmldir @@ -9,7 +9,6 @@ CustomSlider 1.0 CustomSlider.qml InputDialog 1.0 InputDialog.qml BatteryWidget 1.0 BatteryWidget.qml BatteryControlPopup 1.0 BatteryControlPopup.qml -PowerButton 1.0 PowerButton.qml PowerMenuPopup 1.0 PowerMenuPopup.qml PowerConfirmDialog 1.0 PowerConfirmDialog.qml ThemePicker 1.0 ThemePicker.qml