diff --git a/qml/AppControls2/AppItem.qml b/qml/AppControls2/AppItem.qml index cd19cc2..b71d5f2 100644 --- a/qml/AppControls2/AppItem.qml +++ b/qml/AppControls2/AppItem.qml @@ -6,8 +6,15 @@ import org.ukui.menu.core 1.0 MouseArea { id: control hoverEnabled: true - + states: State { + when: control.activeFocus + PropertyChanges { + target: controlBase + alpha: 0.55 + } + } StyleBackground { + id: controlBase anchors.fill: parent radius: 4 useStyleTransparent: false diff --git a/qml/AppControls2/FolderItem.qml b/qml/AppControls2/FolderItem.qml index 06a3e9e..5f9f45b 100644 --- a/qml/AppControls2/FolderItem.qml +++ b/qml/AppControls2/FolderItem.qml @@ -11,8 +11,15 @@ MouseArea { hoverEnabled: true ToolTip.visible: editStatus && truncate && control.containsMouse ToolTip.text: name - + states: State { + when: control.activeFocus + PropertyChanges { + target: controlBase + alpha: 0.55 + } + } StyleBackground { + id: controlBase anchors.fill: parent radius: 4 useStyleTransparent: false diff --git a/qml/AppControls2/LabelItem.qml b/qml/AppControls2/LabelItem.qml index 2294a46..dcbfc72 100644 --- a/qml/AppControls2/LabelItem.qml +++ b/qml/AppControls2/LabelItem.qml @@ -7,8 +7,15 @@ MouseArea { hoverEnabled: true ToolTip.text: qsTr("Open the label selection interface") ToolTip.visible: control.containsMouse - + states: State { + when: control.activeFocus + PropertyChanges { + target: controlBase + alpha: 0.55 + } + } StyleBackground { + id: controlBase anchors.fill: parent radius: 4 useStyleTransparent: false diff --git a/qml/AppControls2/RoundButton.qml b/qml/AppControls2/RoundButton.qml index 72cc3b1..7ee2d08 100644 --- a/qml/AppControls2/RoundButton.qml +++ b/qml/AppControls2/RoundButton.qml @@ -1,4 +1,4 @@ -import QtQuick 2.0 +import QtQuick 2.15 import org.ukui.menu.core 1.0 MouseArea { @@ -9,6 +9,7 @@ MouseArea { property alias autoHighLight: themeIcon.autoHighLight StyleBackground { + id: buttonBase useStyleTransparent: false paletteRole: mainWindow.isFullScreen ? Palette.Light : Palette.Text anchors.fill: parent @@ -22,4 +23,12 @@ MouseArea { width: 16; height: width source: buttonIcon } + states: State { + when: buttonMouseArea.activeFocus + PropertyChanges { + target: buttonBase + borderColor: Palette.Highlight + border.width: 2 + } + } } diff --git a/qml/AppControls2/StyleBackground.qml b/qml/AppControls2/StyleBackground.qml index f301abf..e74d019 100644 --- a/qml/AppControls2/StyleBackground.qml +++ b/qml/AppControls2/StyleBackground.qml @@ -1,4 +1,4 @@ -import QtQuick 2.0 +import QtQuick 2.12 import org.ukui.menu.core 1.0 Rectangle { diff --git a/qml/AppUI/AppList.qml b/qml/AppUI/AppList.qml index 5d645f5..6766ff4 100644 --- a/qml/AppUI/AppList.qml +++ b/qml/AppUI/AppList.qml @@ -32,14 +32,20 @@ Item { appListView.view.positionViewAtIndex(appListView.model.getLabelIndex(labelId), ListView.Beginning) } + function resetListFocus() { + appListView.listFocus = true; + } + AppListView { id: appListView anchors.fill: parent + model: modelManager.getAppModel() delegate: Component { Loader { width: ListView.view ? ListView.view.width : 0 height: 40 + focus: true property int index: model.index property int type: model.type property string id: model.id @@ -65,6 +71,7 @@ Item { Component { id: appItem AppControls2.AppItem { + focus: true acceptedButtons: Qt.LeftButton | Qt.RightButton onClicked: { if (mouse.button === Qt.RightButton) { @@ -79,12 +86,21 @@ Item { return; } } + Keys.onPressed: { + if (event.key === Qt.Key_Enter || event.key === Qt.Key_Return) { + appManager.launchApp(id); + if (recentInstall) { + appManager.appLaunched(id); + } + } + } } } Component { id: labelItem AppControls2.LabelItem { + focus: true onClicked: labelItemClicked(); } } @@ -92,6 +108,7 @@ Item { Component { id: folderItem AppControls2.FolderItem { + focus: true acceptedButtons: Qt.LeftButton | Qt.RightButton onClicked: { if (mouse.button === Qt.RightButton) { @@ -103,6 +120,11 @@ Item { return; } } + Keys.onPressed: { + if (event.key === Qt.Key_Enter || event.key === Qt.Key_Return) { + openFolderPageSignal(id, name); + } + } Component.onCompleted: { appListView.model.renameText.connect(toEditText); } diff --git a/qml/AppUI/AppListView.qml b/qml/AppUI/AppListView.qml index 7d975ea..9495d99 100644 --- a/qml/AppUI/AppListView.qml +++ b/qml/AppUI/AppListView.qml @@ -28,10 +28,10 @@ MouseArea { readonly property alias view: listView property alias model: listView.model property alias delegate: listView.delegate - + property alias listFocus: listView.focus hoverEnabled: true clip: true - + onContainsMouseChanged: listView.focus = false AppControls2.StyleBackground { anchors.top: parent.top width: parent.width @@ -49,13 +49,16 @@ MouseArea { ListView { id: listView - spacing: 4 Layout.fillHeight: true Layout.fillWidth: true highlightMoveDuration: 0 boundsBehavior: Flickable.StopAtBounds ScrollBar.vertical: listViewScrollBar + // 焦点切换后,listView按键导航重新开始 + onActiveFocusChanged: currentIndex = 0 + onCountChanged: currentIndex = 0 + keyNavigationWraps: true } AppControls2.ScrollBar { diff --git a/qml/AppUI/AppPage.qml b/qml/AppUI/AppPage.qml index 95f114f..1de6cf3 100644 --- a/qml/AppUI/AppPage.qml +++ b/qml/AppUI/AppPage.qml @@ -25,6 +25,8 @@ import org.ukui.menu.core 1.0 AppControls2.StyleBackground { paletteRole: Palette.Window radius: 12 + property alias header: appPageHeader + property alias content: appPageContent ColumnLayout { anchors.fill: parent @@ -35,12 +37,15 @@ AppControls2.StyleBackground { id: appPageHeader Layout.fillWidth: true Layout.preferredHeight: 40 + focusToListView: appPageContent } AppPageContent { + id: appPageContent appPageHeader: appPageHeader Layout.fillWidth: true Layout.fillHeight: true + activeFocusOnTab: false } } } diff --git a/qml/AppUI/AppPageContent.qml b/qml/AppUI/AppPageContent.qml index 3239804..609a24b 100644 --- a/qml/AppUI/AppPageContent.qml +++ b/qml/AppUI/AppPageContent.qml @@ -30,6 +30,10 @@ SwipeView { // 5.12 property AppPageHeader appPageHeader: null + function resetFocus() { + appList.resetListFocus(); + } + Item { id: appListBase AppList { diff --git a/qml/AppUI/AppPageHeader.qml b/qml/AppUI/AppPageHeader.qml index f93d1d6..970ed71 100644 --- a/qml/AppUI/AppPageHeader.qml +++ b/qml/AppUI/AppPageHeader.qml @@ -26,10 +26,18 @@ import org.ukui.menu.utils 1.0 Item { id: appPageHeaderRoot property string title: "" + required property Item focusToListView property Component content: null clip: true + function changeToSearch() { + if (appPageHeaderRoot.content === null) { + loader.item.state = "search"; + } + } + Loader { + id: loader anchors.fill: parent property string title: appPageHeaderRoot.title sourceComponent: { @@ -41,6 +49,7 @@ Item { id: defaultComponent Item { id: root + state: "normal" states: [ State { @@ -60,6 +69,12 @@ Item { Transition { to: "normal" SequentialAnimation { + ScriptAction { + script: { + pluginSelectionBar.visible = true; + pluginSelectMenu.visible = true; + } + } NumberAnimation { easing.type: Easing.InOutQuad; properties: "scale,y"; duration: 300 } ScriptAction { script: searchBar.visible = false } } @@ -70,10 +85,17 @@ Item { ScriptAction { script: { searchBar.visible = true; + searchInputBar.providerId = "search"; searchInputBar.textInputFocus(); } } NumberAnimation { easing.type: Easing.InOutQuad; properties: "scale,y"; duration: 300} + ScriptAction { + script: { + pluginSelectionBar.visible = false; + pluginSelectMenu.visible = false; + } + } } } ] @@ -106,6 +128,7 @@ Item { Layout.fillWidth: true Layout.fillHeight: true radius: 16 + keyDownTarget: focusToListView onTextChanged: { if (text === "") { pluginSelectMenu.model.reactivateProvider(); @@ -119,11 +142,18 @@ Item { AppControls2.RoundButton { Layout.preferredWidth: parent.height; Layout.preferredHeight: parent.height buttonIcon: "image://appicon/edit-clear-symbolic" + activeFocusOnTab: true onClicked: { searchInputBar.clear(); root.state = "normal"; } + Keys.onPressed: { + if (event.key === Qt.Key_Enter || event.key === Qt.Key_Return) { + searchInputBar.clear(); + root.state = "normal"; + } + } } } } @@ -145,6 +175,7 @@ Item { } ListView { + id: searchListView Layout.preferredWidth: childrenRect.width Layout.preferredHeight: 32 Layout.maximumWidth: 68 @@ -153,6 +184,8 @@ Item { clip: true spacing: 4 orientation: ListView.Horizontal + interactive: false + activeFocusOnTab: true model: appPageHeaderUtils.model(PluginGroup.Button) delegate: AppControls2.RoundButton { @@ -160,7 +193,13 @@ Item { buttonIcon: model.icon onClicked: { root.state = "search"; - searchInputBar.providerId = model.id; + // 后续添加需求,可以用model数据设置 + // searchInputBar.providerId = model.id; + } + Keys.onPressed: { + if (event.key === Qt.Key_Enter || event.key === Qt.Key_Return) { + root.state = "search"; + } } } } diff --git a/qml/AppUI/FullScreenContent.qml b/qml/AppUI/FullScreenContent.qml index 485f9b1..e3f53c1 100644 --- a/qml/AppUI/FullScreenContent.qml +++ b/qml/AppUI/FullScreenContent.qml @@ -127,7 +127,6 @@ RowLayout { id: contentViewBase Layout.fillWidth: true Layout.fillHeight: true - property int maxColumns: 8 property int columns: { let c = Math.floor(width / cellWidth); diff --git a/qml/AppUI/FullScreenFooter.qml b/qml/AppUI/FullScreenFooter.qml index 54de039..0e91eee 100644 --- a/qml/AppUI/FullScreenFooter.qml +++ b/qml/AppUI/FullScreenFooter.qml @@ -16,7 +16,7 @@ * */ -import QtQuick 2.0 +import QtQuick 2.12 import QtQuick.Layouts 1.12 import QtQuick.Controls 2.5 @@ -35,6 +35,8 @@ Row { paletteRole: Palette.Light alpha: powerButtonArea.containsPress ? 0.25 : powerButtonArea.containsMouse ? 0.12 : 0 radius: height / 2 + borderColor: Palette.Highlight + border.width: powerButtonArea.activeFocus ? 2 : 0 PowerButton { id: powerButtonBase @@ -55,11 +57,17 @@ Row { ToolTip.text: powerButtonBase.toolTip acceptedButtons: Qt.LeftButton | Qt.RightButton property int spacingFromMenu: 8 + activeFocusOnTab: true onClicked: { var buttonPosition = powerButtonArea.mapToGlobal(width, height); powerButtonBase.clicked(mouse.button === Qt.LeftButton, buttonPosition.x - width - spacingFromMenu, buttonPosition.y + spacingFromMenu, mainWindow.isFullScreen); } + Keys.onPressed: { + if (event.key === Qt.Key_Enter || event.key === Qt.Key_Return) { + powerButtonBase.clicked(true, 0, 0, mainWindow.isFullScreen); + } + } } } } diff --git a/qml/AppUI/FullScreenHeader.qml b/qml/AppUI/FullScreenHeader.qml index 333a4f0..ed2b769 100644 --- a/qml/AppUI/FullScreenHeader.qml +++ b/qml/AppUI/FullScreenHeader.qml @@ -1,4 +1,4 @@ -import QtQuick 2.0 +import QtQuick 2.12 import QtQuick.Layouts 1.12 import QtQuick.Controls 2.12 import AppControls2 1.0 as AppControls2 @@ -45,6 +45,8 @@ Item { alpha: buttonMouseArea.containsPress ? 0.30 : buttonMouseArea.containsMouse ? 0.20 : 0.00 anchors.right: parent.right anchors.verticalCenter: parent.verticalCenter + borderColor: Palette.Highlight + border.width: buttonMouseArea.activeFocus ? 2 : 0 ThemeIcon { anchors.centerIn: parent @@ -58,6 +60,12 @@ Item { hoverEnabled: true anchors.fill: parent onClicked: mainWindow.exitFullScreen() + activeFocusOnTab: true + Keys.onPressed: { + if (event.key === Qt.Key_Enter || event.key === Qt.Key_Return) { + mainWindow.exitFullScreen(); + } + } } } } diff --git a/qml/AppUI/NormalUI.qml b/qml/AppUI/NormalUI.qml index d7edef0..17b0630 100644 --- a/qml/AppUI/NormalUI.qml +++ b/qml/AppUI/NormalUI.qml @@ -3,11 +3,30 @@ import QtQuick.Controls 2.0 as QQC2 import AppControls2 1.0 as AppControls2 import org.ukui.menu.core 1.0 -Item { +FocusScope { + anchors.fill: parent + function focusToFalse() { + focus = false; + } + function keyPressed(event) { + // 任意字符键焦点切换到搜索(0-9 a-z) + if ((0x2f < event.key && event.key < 0x3a )||(0x40 < event.key && event.key < 0x5b)) { + focus = true; + appPage.header.changeToSearch(); + // 任意方向键切换至搜索结果 + } else if ( 0x01000011 < event.key && event.key < 0x01000016) { + focus = true; + appPage.content.resetFocus(); + appPage.content.focus = true; + } + } + + Component.onCompleted: mainWindow.visibleChanged.connect(focusToFalse) + Component.onDestruction: mainWindow.visibleChanged.disconnect(focusToFalse) Row { anchors.fill: parent; - AppPage { + id: appPage width: 300 height: parent.height; } diff --git a/qml/AppUI/PluginSelectMenu.qml b/qml/AppUI/PluginSelectMenu.qml index 9f1df14..16ba4f8 100644 --- a/qml/AppUI/PluginSelectMenu.qml +++ b/qml/AppUI/PluginSelectMenu.qml @@ -16,7 +16,7 @@ * */ -import QtQuick 2.0 +import QtQuick 2.12 import QtQuick.Layouts 1.12 import QtQuick.Controls 2.12 @@ -34,25 +34,52 @@ RowLayout { Layout.fillHeight: true Layout.fillWidth: true Layout.maximumWidth: height + activeFocusOnTab: true highlight: mainWindow.isFullScreen autoHighLight: !mainWindow.isFullScreen buttonIcon: "image://appicon/ukui-selected" - onClicked: pluginSelectMenuRoot.model.autoSwitchProvider(); + onClicked: pluginSelectMenuRoot.model.autoSwitchProvider() + Keys.onPressed: { + if (event.key === Qt.Key_Enter || event.key === Qt.Key_Return) { + pluginSelectMenuRoot.model.autoSwitchProvider(); + } + } } MouseArea { + id: sortMenuMouseArea Layout.alignment: Qt.AlignVCenter Layout.fillWidth: true Layout.fillHeight: true Layout.maximumWidth: 16 Layout.maximumHeight: 16 + activeFocusOnTab: true + Keys.onPressed: { + if (event.key === Qt.Key_Enter || event.key === Qt.Key_Return) { + sortMenuClicked(); + } + } onClicked: { + sortMenuClicked(); + } + function sortMenuClicked() { if(sortMenuSelector.state === "sortMenuClose") { sortMenuSelector.state = "sortMenuOpen"; } - pluginSelectMenuRoot.model.openMenu(pluginSelectButton.height - y - mouseY + 8); + var pressY = sortMenuMouseArea.mapToGlobal(0, 0).y; + var pressX = sortMenuMouseArea.mapToGlobal(0, 0).x; + pluginSelectMenuRoot.model.openMenu(pluginSelectButton.height - y - mouseY + 8, pressX, pressY); + } + + AppControls2.StyleBackground { + anchors.fill: parent + useStyleTransparent: false + paletteRole: Palette.Highlight + alpha: 0 + borderColor: Palette.Highlight + border.width: sortMenuMouseArea.activeFocus ? 2 : 0 } ThemeIcon { diff --git a/qml/AppUI/SearchInputBar.qml b/qml/AppUI/SearchInputBar.qml index 9c1a6f2..a47a2ef 100644 --- a/qml/AppUI/SearchInputBar.qml +++ b/qml/AppUI/SearchInputBar.qml @@ -23,10 +23,9 @@ import org.ukui.menu.core 1.0 import AppControls2 1.0 as AppControls2 AppControls2.StyleBackground { + property Item keyDownTarget readonly property string text: textInput.text; - function clear() { - textInput.clear(); - } + alpha: 0.04 useStyleTransparent: false paletteRole: Palette.Text @@ -37,7 +36,9 @@ AppControls2.StyleBackground { function textInputFocus() { textInput.forceActiveFocus(); } - + function clear() { + textInput.clear(); + } Item { id: defaultSearch width: searchIcon.width + defaultText.contentWidth; height: parent.height @@ -96,23 +97,25 @@ AppControls2.StyleBackground { selectByMouse: true verticalAlignment: TextInput.AlignVCenter font.pixelSize: 14 - focus: parent.visible || mainWindow.isFullScreen - onActiveFocusChanged: clear() + focus: parent.visible || mainWindow.isFullScreen + KeyNavigation.down: keyDownTarget + onEditingFinished: KeyNavigation.down = keyDownTarget + + //字体选中跟随主题高亮 property int textColor: mainWindow.isFullScreen ? Palette.HighlightedText : Palette.Text - function updateTextInputColor() { color = themePalette.paletteColor(textColor) selectionColor = themePalette.paletteColor(Palette.Highlight) } - Component.onCompleted: { updateTextInputColor(); themePalette.styleColorChanged.connect(updateTextInputColor); } - onTextColorChanged: updateTextInputColor() Component.onDestruction: themePalette.styleColorChanged.disconnect(updateTextInputColor) + + onFocusChanged: clear() } AppControls2.RoundButton { diff --git a/qml/AppUI/Sidebar.qml b/qml/AppUI/Sidebar.qml index 050d455..c4fa80a 100644 --- a/qml/AppUI/Sidebar.qml +++ b/qml/AppUI/Sidebar.qml @@ -17,7 +17,7 @@ */ import QtQml 2.12 -import QtQuick 2.0 +import QtQuick 2.12 import QtQuick.Layouts 1.12 import QtQuick.Controls 2.5 @@ -66,6 +66,8 @@ Item { radius: 4 useStyleTransparent: false alpha: buttonMouseArea.containsPress ? 0.65 : buttonMouseArea.containsMouse ? 0.40 : 0.00 + borderColor: Palette.Highlight + border.width: buttonMouseArea.activeFocus ? 2 : 0 ThemeIcon { anchors.centerIn: parent width: parent.width / 2; height: width @@ -75,6 +77,12 @@ Item { hoverEnabled: true anchors.fill: parent onClicked: mainWindow.isFullScreen = true + activeFocusOnTab: true + Keys.onPressed: { + if (event.key === Qt.Key_Enter || event.key === Qt.Key_Return) { + mainWindow.isFullScreen = true; + } + } } } } @@ -88,7 +96,6 @@ Item { id: extensionLoader anchors.fill: parent clip: true - focus: true onLoaded: { item.send.connect(extensionListView.send); } @@ -106,9 +113,10 @@ Item { height: 32 paletteRole: Palette.Base useStyleTransparent: false - alpha: powerButtonArea.pressed ? 0.85 : powerButtonArea.hovered ? 0.65 : 0 + alpha: powerButtonArea.containsPress ? 0.85 : powerButtonArea.containsMouse ? 0.65 : 0 radius: height / 2 - + borderColor: Palette.Highlight + border.width: powerButtonArea.activeFocus ? 2 : 0 PowerButton { id: powerButtonBase } @@ -122,24 +130,24 @@ Item { MouseArea { id: powerButtonArea - property bool hovered: false anchors.fill: parent hoverEnabled: true - ToolTip.visible: hovered + ToolTip.visible: containsMouse ToolTip.text: powerButtonBase.toolTip acceptedButtons: Qt.LeftButton | Qt.RightButton property int spacingFromMenu: 16 + activeFocusOnTab: true + Keys.onPressed: { + if (event.key === Qt.Key_Enter || event.key === Qt.Key_Return) { + powerButtonBase.clicked(true, 0, 0, mainWindow.isFullScreen); + } + } + onClicked: { var buttonPosition = powerButtonArea.mapToGlobal(width, height); powerButtonBase.clicked(mouse.button === Qt.LeftButton, buttonPosition.x + spacingFromMenu, buttonPosition.y + spacingFromMenu, mainWindow.isFullScreen); } - onEntered: { - hovered = true - } - onExited: { - hovered = false - } } } } @@ -148,11 +156,26 @@ Item { Component { id: headerDelegate - Item { + AppControls2.StyleBackground { + id: headerDelegateItem + useStyleTransparent: false + paletteRole: Palette.Highlight + alpha: 0 + radius: 4 + borderColor: Palette.Highlight + border.width: headerDelegateItem.activeFocus ? 2 : 0 + property var extensionData: model.data width: styleText.width height: ListView.view ? ListView.view.height : 0 + activeFocusOnTab: true + Keys.onPressed: { + if (event.key === Qt.Key_Enter || event.key === Qt.Key_Return) { + ListView.view.currentIndex = model.index; + } + } + onExtensionDataChanged: { if (extensionLoader.source === model.url) { extensionLoader.item.extensionData = extensionData; diff --git a/qml/main.qml b/qml/main.qml index 61453ea..1a2aab4 100644 --- a/qml/main.qml +++ b/qml/main.qml @@ -221,15 +221,18 @@ Item { Loader { id: normalScreenLoader + focus: !mainWindow.isFullScreen height: normalScreenGeometry.normalGeometry.height; width: normalScreenGeometry.normalGeometry.width sourceComponent: normalComponent NumberAnimation { id: normalShow; target: normalScreenLoader; properties: "opacity"; from: 0; to: 1; duration: root.animationDuration; easing.type: Easing.InQuint } NumberAnimation { id: normalHide; target: normalScreenLoader; properties: "opacity"; from: 1; to: 0; duration: root.animationDuration; easing.type: Easing.OutQuint } - Component.onCompleted: { active = mainWindow.isFullScreen ? false : true; opacity = mainWindow.isFullScreen ? 0 : 1; } + Keys.onPressed: { + item.keyPressed(event); + } } Loader { diff --git a/src/utils/app-page-header-utils.cpp b/src/utils/app-page-header-utils.cpp index 4e07957..4ee6940 100644 --- a/src/utils/app-page-header-utils.cpp +++ b/src/utils/app-page-header-utils.cpp @@ -43,7 +43,7 @@ public: Q_INVOKABLE void autoSwitchProvider(); Q_INVOKABLE QString currentProviderIcon(); - Q_INVOKABLE void openMenu(int offset); + Q_INVOKABLE void openMenu(int offset, int mouseX, int mouseY); Q_SIGNALS: void currentIndexChanged(); @@ -146,7 +146,7 @@ QString ProviderModel::currentProviderIcon() return data(createIndex(m_currentIndex, 0), Icon).toString(); } -void ProviderModel::openMenu(int offset) +void ProviderModel::openMenu(int offset, int mouseX, int mouseY) { QMenu menu; @@ -164,9 +164,8 @@ void ProviderModel::openMenu(int offset) } menu.actions().at(m_currentIndex)->setChecked(true); - - int x = QCursor::pos().x() - menu.sizeHint().width(); - int y = QCursor::pos().y() + offset; + int x = mouseX - menu.sizeHint().width(); + int y = mouseY + offset; menu.exec(QPoint(x, y)); Q_EMIT menuClosed();