From 3d3b2726c9070550c043152cf12dcbe7b7e68091 Mon Sep 17 00:00:00 2001 From: Eduardo Barreto Alexandre Date: Fri, 5 Sep 2025 19:01:12 -0300 Subject: [PATCH 1/6] feat: Add support for multiple systems in flake --- flake.nix | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/flake.nix b/flake.nix index 66024d8d..809c6eaa 100644 --- a/flake.nix +++ b/flake.nix @@ -9,10 +9,12 @@ outputs = { self, nixpkgs, quickshell, ... }: let - system = "x86_64-linux"; - pkgs = nixpkgs.legacyPackages.${system}; + forEachSystem = fn: + nixpkgs.lib.genAttrs + nixpkgs.lib.platforms.linux + (system: fn system nixpkgs.legacyPackages.${system}); in { - packages.${system} = { + packages = forEachSystem (system: pkgs: rec { dankMaterialShell = pkgs.stdenvNoCC.mkDerivation { name = "dankMaterialShell"; src = ./.; @@ -24,7 +26,7 @@ }; default = self.packages.${system}.dankMaterialShell; - }; + }); homeModules.dankMaterialShell = { config, pkgs, lib, ... }: let cfg = config.programs.dankMaterialShell; @@ -43,9 +45,9 @@ config = lib.mkIf cfg.enable { programs.quickshell = { enable = true; - package = quickshell.packages.${system}.quickshell; + package = quickshell.packages.${pkgs.system}.quickshell; configs.DankMaterialShell = "${ - self.packages.${system}.dankMaterialShell + self.packages.${pkgs.system}.dankMaterialShell }/etc/xdg/quickshell/DankMaterialShell"; activeConfig = lib.mkIf cfg.enableSystemd "DankMaterialShell"; systemd = lib.mkIf cfg.enableSystemd { From 79c21e67dc88cb17229feb2254a72964c3decb15 Mon Sep 17 00:00:00 2001 From: purian23 Date: Fri, 5 Sep 2025 20:57:35 -0400 Subject: [PATCH 2/6] Add additional savings opts to notepad --- Modules/NotepadSlideout.qml | 262 +++++++++++++++++++++++++++++++++--- 1 file changed, 241 insertions(+), 21 deletions(-) diff --git a/Modules/NotepadSlideout.qml b/Modules/NotepadSlideout.qml index efa7df81..b92d3b45 100644 --- a/Modules/NotepadSlideout.qml +++ b/Modules/NotepadSlideout.qml @@ -5,6 +5,7 @@ import Quickshell import Quickshell.Wayland import Quickshell.Io import qs.Common +import qs.Modals.Common import qs.Modals.FileBrowser import qs.Services import qs.Widgets @@ -22,6 +23,16 @@ PanelWindow { property var targetScreen: null property var modelData: null property bool animatingOut: false + property bool confirmationDialogOpen: false + property string pendingAction: "" + property string lastSavedFileContent: "" + + function hasFileChanges() { + if (!root.currentFileUrl.toString()) { + return root.hasUnsavedChanges || SessionData.notepadContent.length > 0 + } + return SessionData.notepadContent !== root.lastSavedFileContent + } function show() { notepadVisible = true @@ -111,10 +122,10 @@ PanelWindow { } StyledText { - text: (root.hasUnsavedChanges ? "● " : "") + (root.currentFileName || qsTr("Untitled")) + text: (hasFileChanges() ? "● " : "") + (root.currentFileName || qsTr("Untitled")) font.pixelSize: Theme.fontSizeSmall - color: root.hasUnsavedChanges ? Theme.primary : Theme.surfaceTextMedium - visible: root.currentFileName !== "" || root.hasUnsavedChanges + color: hasFileChanges() ? Theme.primary : Theme.surfaceTextMedium + visible: root.currentFileName !== "" || hasFileChanges() elide: Text.ElideMiddle maximumLineCount: 1 width: parent.width - Theme.spacingM @@ -203,16 +214,29 @@ PanelWindow { break case Qt.Key_O: event.accepted = true - root.fileDialogOpen = true - loadBrowser.open() + if (hasFileChanges()) { + root.pendingAction = "open" + root.confirmationDialogOpen = true + confirmationDialog.open() + } else { + root.fileDialogOpen = true + loadBrowser.open() + } break case Qt.Key_N: event.accepted = true - textArea.text = "" - SessionData.notepadContent = "" - root.currentFileName = "" - root.currentFileUrl = "" - root.hasUnsavedChanges = false + if (hasFileChanges()) { + root.pendingAction = "new" + root.confirmationDialogOpen = true + confirmationDialog.open() + } else { + textArea.text = "" + SessionData.notepadContent = "" + root.currentFileName = "" + root.currentFileUrl = "" + root.hasUnsavedChanges = false + root.lastSavedFileContent = "" + } break case Qt.Key_A: event.accepted = true @@ -244,7 +268,7 @@ PanelWindow { iconName: "save" iconSize: Theme.iconSize - 2 iconColor: Theme.primary - enabled: root.hasUnsavedChanges || SessionData.notepadContent.length > 0 + enabled: hasFileChanges() || SessionData.notepadContent.length > 0 onClicked: { root.fileDialogOpen = true saveBrowser.open() @@ -265,8 +289,14 @@ PanelWindow { iconSize: Theme.iconSize - 2 iconColor: Theme.secondary onClicked: { - root.fileDialogOpen = true - loadBrowser.open() + if (hasFileChanges()) { + root.pendingAction = "open" + root.confirmationDialogOpen = true + confirmationDialog.open() + } else { + root.fileDialogOpen = true + loadBrowser.open() + } } } StyledText { @@ -284,11 +314,18 @@ PanelWindow { iconSize: Theme.iconSize - 2 iconColor: Theme.surfaceText onClicked: { - textArea.text = "" - SessionData.notepadContent = "" - root.currentFileName = "" - root.currentFileUrl = "" - root.hasUnsavedChanges = false + if (hasFileChanges()) { + root.pendingAction = "new" + root.confirmationDialogOpen = true + confirmationDialog.open() + } else { + textArea.text = "" + SessionData.notepadContent = "" + root.currentFileName = "" + root.currentFileUrl = "" + root.hasUnsavedChanges = false + root.lastSavedFileContent = "" + } } } StyledText { @@ -318,9 +355,9 @@ PanelWindow { } StyledText { - text: saveTimer.running ? qsTr("Auto-saving...") : (root.hasUnsavedChanges ? qsTr("Unsaved changes") : qsTr("Auto-saved")) + text: saveTimer.running ? qsTr("Auto-saving...") : (hasFileChanges() ? qsTr("Unsaved changes") : qsTr("Auto-saved")) font.pixelSize: Theme.fontSizeSmall - color: root.hasUnsavedChanges ? Theme.warning : (saveTimer.running ? Theme.primary : Theme.surfaceTextMedium) + color: hasFileChanges() ? Theme.warning : (saveTimer.running ? Theme.primary : Theme.surfaceTextMedium) opacity: SessionData.notepadContent.length > 0 ? 1 : 0 } } @@ -372,6 +409,7 @@ PanelWindow { onExited: (exitCode) => { if (exitCode === 0) { root.hasUnsavedChanges = false + root.lastSavedFileContent = SessionData.notepadContent } else { console.warn("Notepad: Failed to save file, exit code:", exitCode) } @@ -385,6 +423,7 @@ PanelWindow { onStreamFinished: { SessionData.notepadContent = text root.hasUnsavedChanges = false + root.lastSavedFileContent = text } } @@ -404,7 +443,9 @@ PanelWindow { fileExtensions: ["*.txt", "*.md", "*.*"] allowStacking: true saveMode: true - defaultFileName: "note.txt" + defaultFileName: root.currentFileName || "note.txt" + + WlrLayershell.layer: WlrLayershell.Overlay onFileSelected: (path) => { root.fileDialogOpen = false @@ -416,6 +457,25 @@ PanelWindow { root.currentFileUrl = fileUrl saveToFile(fileUrl) + + // Handle pending action after save + if (root.pendingAction === "new") { + Qt.callLater(() => { + textArea.text = "" + SessionData.notepadContent = "" + root.currentFileName = "" + root.currentFileUrl = "" + root.hasUnsavedChanges = false + root.lastSavedFileContent = "" + }) + } else if (root.pendingAction === "open") { + Qt.callLater(() => { + root.fileDialogOpen = true + loadBrowser.open() + }) + } + root.pendingAction = "" + close() } @@ -433,6 +493,8 @@ PanelWindow { fileExtensions: ["*.txt", "*.md", "*.*"] allowStacking: true + WlrLayershell.layer: WlrLayershell.Overlay + onFileSelected: (path) => { root.fileDialogOpen = false const cleanPath = path.toString().replace(/^file:\/\//, '') @@ -450,4 +512,162 @@ PanelWindow { root.fileDialogOpen = false } } + + DankModal { + id: confirmationDialog + + width: 400 + height: 180 + shouldBeVisible: false + allowStacking: true + + onBackgroundClicked: { + close() + root.confirmationDialogOpen = false + } + + content: Component { + FocusScope { + anchors.fill: parent + focus: true + + Keys.onEscapePressed: event => { + confirmationDialog.close() + root.confirmationDialogOpen = false + event.accepted = true + } + + Column { + anchors.centerIn: parent + width: parent.width - Theme.spacingM * 2 + spacing: Theme.spacingM + + Row { + width: parent.width + + Column { + width: parent.width - 40 + spacing: Theme.spacingXS + + StyledText { + text: qsTr("Unsaved Changes") + font.pixelSize: Theme.fontSizeLarge + color: Theme.surfaceText + font.weight: Font.Medium + } + + StyledText { + text: root.pendingAction === "new" ? + qsTr("You have unsaved changes. Save before creating a new file?") : + qsTr("You have unsaved changes. Save before opening a file?") + font.pixelSize: Theme.fontSizeMedium + color: Theme.surfaceTextMedium + width: parent.width + wrapMode: Text.Wrap + } + } + + DankActionButton { + iconName: "close" + iconSize: Theme.iconSize - 4 + iconColor: Theme.surfaceText + onClicked: { + confirmationDialog.close() + root.confirmationDialogOpen = false + } + } + } + + Item { + width: parent.width + height: 40 + + Row { + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + spacing: Theme.spacingM + + Rectangle { + width: Math.max(80, discardText.contentWidth + Theme.spacingM * 2) + height: 36 + radius: Theme.cornerRadius + color: discardArea.containsMouse ? Theme.surfaceTextHover : "transparent" + border.color: Theme.surfaceVariantAlpha + border.width: 1 + + StyledText { + id: discardText + anchors.centerIn: parent + text: qsTr("Don't Save") + font.pixelSize: Theme.fontSizeMedium + color: Theme.surfaceText + font.weight: Font.Medium + } + + MouseArea { + id: discardArea + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: { + confirmationDialog.close() + root.confirmationDialogOpen = false + if (root.pendingAction === "new") { + textArea.text = "" + SessionData.notepadContent = "" + root.currentFileName = "" + root.currentFileUrl = "" + root.hasUnsavedChanges = false + root.lastSavedFileContent = "" + } else if (root.pendingAction === "open") { + root.fileDialogOpen = true + loadBrowser.open() + } + root.pendingAction = "" + } + } + } + + Rectangle { + width: Math.max(70, saveAsText.contentWidth + Theme.spacingM * 2) + height: 36 + radius: Theme.cornerRadius + color: saveAsArea.containsMouse ? Qt.darker(Theme.primary, 1.1) : Theme.primary + + StyledText { + id: saveAsText + anchors.centerIn: parent + text: qsTr("Save") + font.pixelSize: Theme.fontSizeMedium + color: Theme.background + font.weight: Font.Medium + } + + MouseArea { + id: saveAsArea + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: { + confirmationDialog.close() + root.confirmationDialogOpen = false + root.fileDialogOpen = true + saveBrowser.open() + } + } + + Behavior on color { + ColorAnimation { + duration: Theme.shortDuration + easing.type: Theme.standardEasing + } + } + } + + } + } + } + } + } + } } \ No newline at end of file From 353ee355a369ab01c064c2da6d4c9c8831351dff Mon Sep 17 00:00:00 2001 From: "Eduardo B. A." <279828+sezaru@users.noreply.github.com> Date: Fri, 5 Sep 2025 22:38:01 -0300 Subject: [PATCH 3/6] Allow setting quickshell package (#165) * feat: Add config option to set quickshell package * refactor: Use configured quickshell package for ipc calls --------- Co-authored-by: Eduardo Barreto Alexandre --- flake.nix | 107 +++++++++++------------------------------------------- 1 file changed, 21 insertions(+), 86 deletions(-) diff --git a/flake.nix b/flake.nix index 238984a3..159c4ddd 100644 --- a/flake.nix +++ b/flake.nix @@ -42,12 +42,19 @@ lib.mkEnableOption "DankMaterialShell systemd startup"; enableSpawn = lib.mkEnableOption "DankMaterialShell Niri spawn-at-startup"; + + quickshell = { + package = lib.mkPackageOption pkgs "quickshell" { + default = quickshell.packages.${pkgs.system}.quickshell; + nullable = false; + }; + }; }; config = lib.mkIf cfg.enable { programs.quickshell = { enable = true; - package = quickshell.packages.${pkgs.system}.quickshell; + package = cfg.quickshell.package; configs.DankMaterialShell = "${ self.packages.${pkgs.system}.dankMaterialShell }/etc/xdg/quickshell/DankMaterialShell"; @@ -58,109 +65,37 @@ }; }; - programs.niri.settings = lib.mkMerge [ + programs.niri.settings = lib.mkMerge let + quickShellIpc = spawn "${cfg.quickshell.package}/bin/qs" "-c" "DankMaterialShell" "ipc" "call"; + in [ (lib.mkIf cfg.enableKeybinds { binds = { - "Mod+Space".action.spawn = [ - "qs" - "-c" - "DankMaterialShell" - "ipc" - "call" - "spotlight" - "toggle" - ]; - "Mod+V".action.spawn = [ - "qs" - "-c" - "DankMaterialShell" - "ipc" - "call" - "clipboard" - "toggle" - ]; - "Mod+M".action.spawn = [ - "qs" - "-c" - "DankMaterialShell" - "ipc" - "call" - "processlist" - "toggle" - ]; - "Mod+Comma".action.spawn = [ - "qs" - "-c" - "DankMaterialShell" - "ipc" - "call" - "settings" - "toggle" - ]; - "Super+Alt+L".action.spawn = [ - "qs" - "-c" - "DankMaterialShell" - "ipc" - "call" - "lock" - "lock" - ]; + "Mod+Space".action = quickShellIpc "spotlight" "toggle"; + "Mod+V".action = quickShellIpc "clipboard" "toggle"; + "Mod+M".action = quickShellIpc "processlist" "toggle"; + "Mod+Comma".action = quickShellIpc "settings" "toggle"; + "Super+Alt+L".action = quickShellIpc "lock" "lock"; "XF86AudioRaiseVolume" = { allow-when-locked = true; - action.spawn = [ - "qs" - "-c" - "DankMaterialShell" - "ipc" - "call" - "audio" - "increment" - "3" - ]; + action = quickShellIpc "audio" "increment" "3"; }; "XF86AudioLowerVolume" = { allow-when-locked = true; - action.spawn = [ - "qs" - "-c" - "DankMaterialShell" - "ipc" - "call" - "audio" - "decrement" - "3" - ]; + action = quickShellIpc "audio" "decrement" "3"; }; "XF86AudioMute" = { allow-when-locked = true; - action.spawn = [ - "qs" - "-c" - "DankMaterialShell" - "ipc" - "call" - "audio" - "mute" - ]; + action = quickShellIpc "audio" "mute"; }; "XF86AudioMicMute" = { allow-when-locked = true; - action.spawn = [ - "qs" - "-c" - "DankMaterialShell" - "ipc" - "call" - "audio" - "micmute" - ]; + action = quickShellIpc "audio" "micmute"; }; }; }) (lib.mkIf (cfg.enableSpawn) { spawn-at-startup = - [{ command = [ "qs" "-c" "DankMaterialShell" ]; }]; + [{ command = [ "${cfg.quickshell.package}/bin/qs" "-c" "DankMaterialShell" ]; }]; }) ]; From 9ba3bfb4dba9ff02c3e69c53c213836b0d2070b4 Mon Sep 17 00:00:00 2001 From: "Eduardo B. A." <279828+sezaru@users.noreply.github.com> Date: Fri, 5 Sep 2025 23:24:07 -0300 Subject: [PATCH 4/6] [FIX] Allow setting quickshell package (#166) * feat: Add config option to set quickshell package * refactor: Use configured quickshell package for ipc calls * fix: Fixes niri binds --------- Co-authored-by: Eduardo Barreto Alexandre --- flake.nix | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/flake.nix b/flake.nix index 159c4ddd..7cc5a517 100644 --- a/flake.nix +++ b/flake.nix @@ -65,11 +65,11 @@ }; }; - programs.niri.settings = lib.mkMerge let - quickShellIpc = spawn "${cfg.quickshell.package}/bin/qs" "-c" "DankMaterialShell" "ipc" "call"; - in [ + programs.niri.settings = lib.mkMerge [ (lib.mkIf cfg.enableKeybinds { - binds = { + binds = with config.lib.niri.actions; let + quickShellIpc = spawn "${cfg.quickshell.package}/bin/qs" "-c" "DankMaterialShell" "ipc" "call"; + in { "Mod+Space".action = quickShellIpc "spotlight" "toggle"; "Mod+V".action = quickShellIpc "clipboard" "toggle"; "Mod+M".action = quickShellIpc "processlist" "toggle"; From 5f97fd4f7cd8f9be97f80526f4ac7ac292f372df Mon Sep 17 00:00:00 2001 From: "Eduardo B. A." <279828+sezaru@users.noreply.github.com> Date: Fri, 5 Sep 2025 23:57:58 -0300 Subject: [PATCH 5/6] Add config options to features (#167) * feat: Add config option to set quickshell package * refactor: Use configured quickshell package for ipc calls * feat: Add config for all shell features in flake Also add missing dependencies * fix: Fixes niri binds * fix: Fixes dgop package path * fix: Fixes pkgs not being set * fix: Use ${pkgs.system} for dms-cli package --------- Co-authored-by: Eduardo Barreto Alexandre --- flake.nix | 90 +++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 71 insertions(+), 19 deletions(-) diff --git a/flake.nix b/flake.nix index 7cc5a517..3e533828 100644 --- a/flake.nix +++ b/flake.nix @@ -3,13 +3,21 @@ inputs = { nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable"; - quickshell.url = "git+https://git.outfoxxed.me/quickshell/quickshell"; - quickshell.inputs.nixpkgs.follows = "nixpkgs"; - dms-cli.url = "github:AvengeMedia/danklinux"; - dms-cli.inputs.nixpkgs.follows = "nixpkgs"; + quickshell = { + url = "git+https://git.outfoxxed.me/quickshell/quickshell"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + dgop = { + url = "github:AvengeMedia/dgop"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + dms-cli = { + url = "github:AvengeMedia/danklinux"; + inputs.nixpkgs.follows = "nixpkgs"; + }; }; - outputs = { self, nixpkgs, quickshell, dms-cli, ... }: + outputs = { self, nixpkgs, quickshell, dgop, dms-cli, ... }: let forEachSystem = fn: nixpkgs.lib.genAttrs @@ -42,7 +50,46 @@ lib.mkEnableOption "DankMaterialShell systemd startup"; enableSpawn = lib.mkEnableOption "DankMaterialShell Niri spawn-at-startup"; - + enableSystemMonitoring = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Add needed dependencies to use system monitoring widgets"; + }; + enableClipboard = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Add needed dependencies to use the clipboard widget"; + }; + enableVPN = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Add needed dependencies to use the VPN widget"; + }; + enableBrigthnessControl = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Add needed dependencies to have brightness/backlight support"; + }; + enableNightMode = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Add needed dependencies to have night mode support"; + }; + enableDynamicTheming = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Add needed dependencies to have dynamic theming support"; + }; + enableAudioWavelenght = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Add needed dependencies to have audio wavelenght support"; + }; + enableCalendarEvents = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Add calendar events support via khal"; + }; quickshell = { package = lib.mkPackageOption pkgs "quickshell" { default = quickshell.packages.${pkgs.system}.quickshell; @@ -99,19 +146,24 @@ }) ]; - home.packages = with pkgs; [ - material-symbols - inter - fira-code - cava - wl-clipboard - cliphist - ddcutil - libsForQt5.qt5ct - kdePackages.qt6ct - matugen - dms-cli.packages.${system}.default - ]; + home.packages = [ + pkgs.material-symbols + pkgs.inter + pkgs.fira-code + + pkgs.ddcutil + pkgs.libsForQt5.qt5ct + pkgs.kdePackages.qt6ct + dms-cli.packages.${pkgs.system}.default + ] + ++ lib.optional cfg.enableSystemMonitoring dgop.packages.${pkgs.system}.dgop + ++ lib.optionals cfg.enableClipboard [pkgs.cliphist pkgs.wl-clipboard] + ++ lib.optionals cfg.enableVPN [pkgs.glib pkgs.networkmanager] + ++ lib.optional cfg.enableBrigthnessControl pkgs.brightnessctl + ++ lib.optional cfg.enableNightMode pkgs.gammastep + ++ lib.optional cfg.enableDynamicTheming pkgs.matugen + ++ lib.optional cfg.enableAudioWavelenght pkgs.cava + ++ lib.optional cfg.enableCalendarEvents pkgs.khal; }; }; }; From c9d7641d86861d5f233cc73a264b9151e6ad580c Mon Sep 17 00:00:00 2001 From: purian23 Date: Fri, 5 Sep 2025 23:40:09 -0400 Subject: [PATCH 6/6] Update initial focus on IPC Notifications & Notepad --- Modals/NotificationModal.qml | 11 +++++++++++ Modules/NotepadSlideout.qml | 4 +--- .../Center/NotificationKeyboardController.qml | 19 +++++++++++++++++++ 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/Modals/NotificationModal.qml b/Modals/NotificationModal.qml index cf381fb4..3867992f 100644 --- a/Modals/NotificationModal.qml +++ b/Modals/NotificationModal.qml @@ -20,6 +20,17 @@ DankModal { if (modalKeyboardController && notificationListRef) { modalKeyboardController.listView = notificationListRef modalKeyboardController.rebuildFlatNavigation() + + Qt.callLater(() => { + modalKeyboardController.keyboardNavigationActive = true + modalKeyboardController.selectedFlatIndex = 0 + modalKeyboardController.updateSelectedIdFromIndex() + if (notificationListRef) { + notificationListRef.keyboardActive = true + } + modalKeyboardController.selectionVersion++ + modalKeyboardController.ensureVisible() + }) } } diff --git a/Modules/NotepadSlideout.qml b/Modules/NotepadSlideout.qml index b92d3b45..975dbcef 100644 --- a/Modules/NotepadSlideout.qml +++ b/Modules/NotepadSlideout.qml @@ -36,6 +36,7 @@ PanelWindow { function show() { notepadVisible = true + Qt.callLater(() => textArea.forceActiveFocus()) } function hide() { @@ -381,9 +382,6 @@ PanelWindow { repeat: false onTriggered: { animatingOut = false - currentFileName = "" - currentFileUrl = "" - hasUnsavedChanges = false } } diff --git a/Modules/Notifications/Center/NotificationKeyboardController.qml b/Modules/Notifications/Center/NotificationKeyboardController.qml index 6b04924a..928ffd7a 100644 --- a/Modules/Notifications/Center/NotificationKeyboardController.qml +++ b/Modules/Notifications/Center/NotificationKeyboardController.qml @@ -138,6 +138,22 @@ QtObject { ensureVisible() } + function selectNextWrapping() { + keyboardNavigationActive = true + if (flatNavigation.length === 0) + return + + // Re-enable auto-scrolling when arrow keys are used + if (listView && listView.enableAutoScroll) { + listView.enableAutoScroll() + } + + selectedFlatIndex = (selectedFlatIndex + 1) % flatNavigation.length + updateSelectedIdFromIndex() + selectionVersion++ + ensureVisible() + } + function selectPrevious() { keyboardNavigationActive = true if (flatNavigation.length === 0) @@ -408,6 +424,9 @@ QtObject { } else if (event.key === Qt.Key_Delete || event.key === Qt.Key_Backspace) { clearSelected() event.accepted = true + } else if (event.key === Qt.Key_Tab) { + selectNextWrapping() + event.accepted = true } else if (event.key >= Qt.Key_1 && event.key <= Qt.Key_9) { const actionIndex = event.key - Qt.Key_1 executeAction(actionIndex)