mirror of
https://invent.kde.org/marcoa/shift-shell.git
synced 2026-04-29 07:03:08 +00:00
homescreens/folio: Add keyboard navigation to app drawer and fix on
search This commit is a subset of https://invent.kde.org/plasma/plasma-mobile/-/merge_requests/694 to add keyboard navigation to the app drawer. Pressing the arrow keys while in the app drawer will allow you to navigate between the apps and the search bar, and going up will exit the app drawer. Escape/Back is also supported for unfocusing the search bar and exiting the view. This also fixes an issue in the keyboard navigation on the search screen where it wouldn't close when there are no search results.
This commit is contained in:
parent
04bcbd9f8d
commit
0bcab0ae3a
7 changed files with 156 additions and 10 deletions
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
|
||||
// SPDX-FileCopyrightText: 2025 Devin Lin <devin@kde.org>
|
||||
// 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)
|
||||
}
|
||||
Loading…
Reference in a new issue