diff --git a/components/mobileshell/qml/widgets/krunner/KRunnerScreen.qml b/components/mobileshell/qml/widgets/krunner/KRunnerScreen.qml index 86ad71bc..7420d590 100644 --- a/components/mobileshell/qml/widgets/krunner/KRunnerScreen.qml +++ b/components/mobileshell/qml/widgets/krunner/KRunnerScreen.qml @@ -72,7 +72,21 @@ MouseArea { font.weight: Font.Bold - KeyNavigation.down: listView + // Keyboard navigation + Keys.onPressed: (event) => { + if (event.key === Qt.Key_Down) { + if (listView.count === 0) { + // Close if listview has no elements + root.requestedClose(); + } else { + // Focus on listview if there are elements + listView.forceActiveFocus(); + listView.currentIndex = 0; + } + event.accepted = true; + } + } + } QQC2.ScrollView { diff --git a/containments/homescreens/folio/package/contents/ui/AppDrawer.qml b/containments/homescreens/folio/package/contents/ui/AppDrawer.qml index d479b505..0f016142 100644 --- a/containments/homescreens/folio/package/contents/ui/AppDrawer.qml +++ b/containments/homescreens/folio/package/contents/ui/AppDrawer.qml @@ -28,11 +28,21 @@ Item { required property int headerHeight required property var headerItem - // height from top of screen that the drawer starts + // Height from top of screen that the drawer starts readonly property real drawerTopMargin: height - topPadding - bottomPadding property alias flickable: appDrawerGrid + // Keyboard navigation + Keys.onPressed: (event) => { + if (event.key === Qt.Key_Escape || event.key === Qt.Key_Back) { + // Close drawer if "back" action + folio.HomeScreenState.closeAppDrawer(); + event.accepted = true; + } + } + + // App drawer container Item { anchors.fill: parent @@ -41,7 +51,7 @@ Item { anchors.rightMargin: root.rightPadding anchors.bottomMargin: root.bottomPadding - // drawer header + // Drawer header MobileShell.BaseItem { id: drawerHeader z: 1 @@ -52,8 +62,23 @@ Item { anchors.right: parent.right contentItem: root.headerItem + + // Keyboard navigation for header (search bar) + Keys.onPressed: (event) => { + if (event.key === Qt.Key_Down || event.key === Qt.Key_Tab || event.key === Qt.Key_Backtab) { + // Go from search bar to app grid + appDrawerGrid.forceActiveFocus(); + appDrawerGrid.currentIndex = 0; + event.accepted = true; + } else if (event.key === Qt.Key_Up) { + // Go to homescreen pages + folio.HomeScreenState.closeAppDrawer(); + event.accepted = true; + } + } } + // App list AppDrawerGrid { id: appDrawerGrid folio: root.folio @@ -64,9 +89,22 @@ Item { anchors.bottom: parent.bottom opacity: 0 // we display with the opacity gradient below headerHeight: root.headerHeight + + // Keyboard navigation + topEdgeCallback: () => { + drawerHeader.contentItem.forceActiveFocus(); + currentIndex = -1; + } + + Keys.onPressed: (event) => { + if (event.key === Qt.Key_Tab || event.key === Qt.Key_Backtab) { + topEdgeCallback(); + event.accepted = true; + } + } } - // opacity gradient at grid edges + // Opacity gradient at grid edges MobileShell.FlickableOpacityGradient { anchors.fill: appDrawerGrid flickable: appDrawerGrid diff --git a/containments/homescreens/folio/package/contents/ui/AppDrawerGrid.qml b/containments/homescreens/folio/package/contents/ui/AppDrawerGrid.qml index d526222b..54632b5d 100644 --- a/containments/homescreens/folio/package/contents/ui/AppDrawerGrid.qml +++ b/containments/homescreens/folio/package/contents/ui/AppDrawerGrid.qml @@ -23,6 +23,10 @@ MobileShell.GridView { reuseItems: true layer.enabled: true + keyNavigationEnabled: true + highlightMoveDuration: 0 + highlight: null // We supply our own highlight from the delegate + property var homeScreen property real headerHeight @@ -84,6 +88,13 @@ MobileShell.GridView { model: folio.ApplicationListSearchModel + // Keyboard focus on app delegate when it is the selected item + onCurrentItemChanged: { + if (currentItem) { + currentItem.keyboardFocus(); + } + } + delegate: AppDelegate { id: appDelegate diff --git a/containments/homescreens/folio/package/contents/ui/AppDrawerHeader.qml b/containments/homescreens/folio/package/contents/ui/AppDrawerHeader.qml index abf9f707..80921fd1 100644 --- a/containments/homescreens/folio/package/contents/ui/AppDrawerHeader.qml +++ b/containments/homescreens/folio/package/contents/ui/AppDrawerHeader.qml @@ -18,10 +18,31 @@ Item { Kirigami.Theme.colorSet: Kirigami.Theme.Complementary Kirigami.Theme.inherit: false + function addSearchText(text: string) { + searchField.text += text; + } + function clearSearchText(): void { searchField.text = ''; } + // Request to not focus on the search bar + signal releaseFocusRequested() + + onFocusChanged: { + if (focus) { + searchField.focus = true; + } + } + + // Keyboard navigation + Keys.onPressed: (event) => { + if (event.key === Qt.Key_Escape || event.key === Qt.Key_Back) { + root.releaseFocusRequested(); + event.accepted = true; + } + } + RowLayout { anchors.topMargin: Kirigami.Units.largeSpacing anchors.leftMargin: Kirigami.Units.gridUnit + Kirigami.Units.largeSpacing diff --git a/containments/homescreens/folio/package/contents/ui/HomeScreen.qml b/containments/homescreens/folio/package/contents/ui/HomeScreen.qml index 490d79f4..3bddeeef 100644 --- a/containments/homescreens/folio/package/contents/ui/HomeScreen.qml +++ b/containments/homescreens/folio/package/contents/ui/HomeScreen.qml @@ -437,17 +437,38 @@ Item { y: (opacity > 0) ? animationY : parent.height headerHeight: Math.round(Kirigami.Units.gridUnit * 4) - headerItem: AppDrawerHeader { folio: root.folio } + headerItem: AppDrawerHeader { + id: appDrawerHeader + folio: root.folio - // account for panels + onReleaseFocusRequested: appDrawer.forceActiveFocus() + } + + // Account for panels topPadding: root.topMargin bottomPadding: root.bottomMargin leftPadding: root.leftMargin rightPadding: root.rightMargin + // Forward keyboard text to the search bar + Keys.onPressed: (event) => { + if (event.text.trim().length > 0) { + appDrawerHeader.addSearchText(event.text); + appDrawerHeader.forceActiveFocus(); + event.accepted = true; + } else if (event.key === Qt.Key_Left || event.key === Qt.Key_Right || event.key === Qt.Key_Up || event.key === Qt.Key_Down) { + appDrawerHeader.forceActiveFocus(); + event.accepted = true; + } + } + Connections { target: folio.HomeScreenState + function onAppDrawerOpened() { + appDrawer.forceActiveFocus(); + } + function onAppDrawerClosed() { // reset app drawer position when closed appDrawer.flickable.contentY = 0; diff --git a/containments/homescreens/folio/package/contents/ui/delegate/AbstractDelegate.qml b/containments/homescreens/folio/package/contents/ui/delegate/AbstractDelegate.qml index 593499f4..27fecab5 100644 --- a/containments/homescreens/folio/package/contents/ui/delegate/AbstractDelegate.qml +++ b/containments/homescreens/folio/package/contents/ui/delegate/AbstractDelegate.qml @@ -33,6 +33,10 @@ Folio.DelegateTouchArea { property real scaleAmount: 1 property bool clickRequested: false + function keyboardFocus() { + delegateWrapper.forceActiveFocus(); + } + NumberAnimation on scaleAmount { id: shrinkAnim running: false @@ -70,17 +74,36 @@ Folio.DelegateTouchArea { // trigger handled by press animation onClicked: clickRequested = true; - layer.enabled: root.shadow - layer.effect: DelegateShadow {} - - Item { + FocusScope { id: delegateWrapper anchors.fill: parent + // Select keyboard navigation + Keys.onPressed: (event) => { + switch (event.key) { + case Qt.Key_Enter: + case Qt.Key_Return: + case Qt.Key_Space: + root.afterClickAnimation(); + event.accepted = true; + break; + default: + break; + } + } + + KeyboardHighlight { + anchors.fill: parent + visible: delegateWrapper.activeFocus + } + ColumnLayout { anchors.fill: parent spacing: 0 + layer.enabled: root.shadow + layer.effect: DelegateShadow {} + // transform is not on delegateWrapper because when it's zoomed in, it apparently // affects the delegate's x and y position, which messes up the starting drag and drop // position (for mapFromItem in HomeScreen.qml) diff --git a/containments/homescreens/folio/package/contents/ui/delegate/KeyboardHighlight.qml b/containments/homescreens/folio/package/contents/ui/delegate/KeyboardHighlight.qml new file mode 100644 index 00000000..2e0ebb6f --- /dev/null +++ b/containments/homescreens/folio/package/contents/ui/delegate/KeyboardHighlight.qml @@ -0,0 +1,18 @@ + +// SPDX-FileCopyrightText: 2025 Devin Lin +// SPDX-License-Identifier: LGPL-2.0-or-later + +import QtQuick +import org.kde.ksvg as KSvg +import org.kde.kirigami as Kirigami + +Rectangle { + id: background + radius: Kirigami.Units.cornerRadius + + border.width: 1 + border.color: Kirigami.Theme.highlightColor + border.pixelAligned: false + + color: Qt.rgba(Kirigami.Theme.highlightColor.r, Kirigami.Theme.highlightColor.g, Kirigami.Theme.highlightColor.b, 0.2) +}