diff --git a/CMakeLists.txt b/CMakeLists.txt index bb1f31f..6037207 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -118,6 +118,7 @@ set(SOURCE_FILES src/utils/app-page-header-utils.cpp src/utils/app-page-header-utils.h src/utils/power-button.cpp src/utils/power-button.h src/utils/app-manager.cpp src/utils/app-manager.h + src/menu/menu-provider.h src/menu/menu-manager.cpp src/menu/menu-manager.h src/items/theme-icon.h src/items/theme-icon.cpp ) diff --git a/qml/AppControls2/AppItem.qml b/qml/AppControls2/AppItem.qml index cd19cc2..7eadc34 100644 --- a/qml/AppControls2/AppItem.qml +++ b/qml/AppControls2/AppItem.qml @@ -5,13 +5,21 @@ import org.ukui.menu.core 1.0 MouseArea { id: control + property bool isSelect: false hoverEnabled: true - + states: State { + when: control.activeFocus + PropertyChanges { + target: controlBase + alpha: 0.55 + } + } StyleBackground { + id: controlBase anchors.fill: parent radius: 4 useStyleTransparent: false - alpha: control.containsPress ? 0.82 : control.containsMouse ? 0.55 : 0.00 + alpha: isSelect ? 1.00 : control.containsPress ? 0.82 : control.containsMouse ? 0.55 : 0.00 ToolTip.visible: content.textTruncated && control.containsMouse ToolTip.text: name diff --git a/qml/AppControls2/FolderItem.qml b/qml/AppControls2/FolderItem.qml index 06a3e9e..92c7dc7 100644 --- a/qml/AppControls2/FolderItem.qml +++ b/qml/AppControls2/FolderItem.qml @@ -8,15 +8,23 @@ MouseArea { id: control property bool editStatus: false property bool truncate: false + property bool isSelect: false 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 - alpha: control.containsPress ? 0.82 : control.containsMouse ? 0.55 : 0.00 + alpha: isSelect ? 0.55 : control.containsPress ? 0.82 : control.containsMouse ? 0.55 : 0.00 RowLayout { anchors.fill: parent 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..9505500 100644 --- a/qml/AppUI/AppList.qml +++ b/qml/AppUI/AppList.qml @@ -16,7 +16,7 @@ * */ -import QtQuick 2.0 +import QtQuick 2.15 import QtQml 2.12 import QtQuick.Controls 2.5 import QtQuick.Layouts 1.12 @@ -25,6 +25,7 @@ import org.ukui.menu.core 1.0 Item { property string title: "" + property string idSelect: "" signal labelItemClicked() signal openFolderPageSignal(string folderId, string folderName); @@ -32,14 +33,21 @@ 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 { + id: loaderView + focus: true width: ListView.view ? ListView.view.width : 0 - height: 40 + height: appListView.itemHeight property int index: model.index property int type: model.type property string id: model.id @@ -49,13 +57,13 @@ Item { property bool recentInstall: model.recentInstall sourceComponent: { if (type === DataType.Normal) { - return appItem; + return appItemComponent; } if (type === DataType.Folder) { - return folderItem; + return folderItemComponent; } if (type === DataType.Label) { - return labelItem; + return labelItemComponent; } } } @@ -63,56 +71,115 @@ Item { } Component { - id: appItem - AppControls2.AppItem { - acceptedButtons: Qt.LeftButton | Qt.RightButton - onClicked: { - if (mouse.button === Qt.RightButton) { - appListView.model.openMenu(index); - return; + id: appItemComponent + Item { + id: appItemBase + AppControls2.AppItem { + id: appItem + focus: true + width: appListView.view ? appListView.view.width : 0 + height: appListView.itemHeight + acceptedButtons: Qt.LeftButton | Qt.RightButton + Drag.active: drag.active + Drag.hotSpot.x: width / 2 + Drag.hotSpot.y: height / 2 + onClicked: { + if (mouse.button === Qt.RightButton) { + appListView.model.openMenu(index, MenuInfo.AppList); + return; + } + if (mouse.button === Qt.LeftButton) { + appManager.launchApp(id); + if (recentInstall) { + appManager.appLaunched(id); + } + return; + } } - if (mouse.button === Qt.LeftButton) { + onPressAndHold: { + if (mouse.button === Qt.LeftButton) { + x = appItem.mapToItem(appListView,0,0).x; + y = appItem.mapToItem(appListView,0,0).y; + drag.target = appItem; + appItem.parent = appListView; + appItem.isSelect = true; + } + } + onReleased: { + idSelect = id; + Drag.drop(); + drag.target = null; + appItem.parent = appItemBase; + x = 0; + y = 0; + appItem.isSelect = false; + } + } + Keys.onPressed: { + if (event.key === Qt.Key_Enter || event.key === Qt.Key_Return) { appManager.launchApp(id); if (recentInstall) { appManager.appLaunched(id); } - return; } } } } Component { - id: labelItem + id: labelItemComponent AppControls2.LabelItem { + focus: true; onClicked: labelItemClicked(); } } Component { - id: folderItem - AppControls2.FolderItem { - acceptedButtons: Qt.LeftButton | Qt.RightButton - onClicked: { - if (mouse.button === Qt.RightButton) { - appListView.model.openMenu(index); - return; - } - if (mouse.button === Qt.LeftButton) { - openFolderPageSignal(id, name); - return; + id: folderItemComponent + DropArea { + onEntered: { + folderItem.isSelect = true; + } + onExited: { + folderItem.isSelect = false; + } + onDropped: { + folderItem.isSelect = false; + if (id !== idSelect) { + appListView.model.addAppToFolder(idSelect, id); } } - Component.onCompleted: { - appListView.model.renameText.connect(toEditText); - } - Component.onDestruction: { - appListView.model.renameText.disconnect(toEditText); - } + AppControls2.FolderItem { + id: folderItem + focus: true + anchors.fill: parent + acceptedButtons: Qt.LeftButton | Qt.RightButton + onClicked: { + if (mouse.button === Qt.RightButton) { + appListView.model.openMenu(index, MenuInfo.AppList); + return; + } + if (mouse.button === Qt.LeftButton) { + openFolderPageSignal(id, name); + return; + } + } + Keys.onPressed: { + if (event.key === Qt.Key_Enter || event.key === Qt.Key_Return) { + openFolderPageSignal(id, name); + } + } + Component.onCompleted: { + appListView.model.renameText.connect(toEditText); + } + Component.onDestruction: { + appListView.model.renameText.disconnect(toEditText); + } - function toEditText(idOfIndex){ - if (id === idOfIndex) { - editStatus = true; + function toEditText(idOfIndex){ + if (id === idOfIndex) { + editStatus = true; + } } } } diff --git a/qml/AppUI/AppListView.qml b/qml/AppUI/AppListView.qml index 7d975ea..05dd6a6 100644 --- a/qml/AppUI/AppListView.qml +++ b/qml/AppUI/AppListView.qml @@ -28,10 +28,11 @@ MouseArea { readonly property alias view: listView property alias model: listView.model property alias delegate: listView.delegate - + property alias listFocus: listView.focus + property int itemHeight: 40 hoverEnabled: true clip: true - + onContainsMouseChanged: listView.focus = false AppControls2.StyleBackground { anchors.top: parent.top width: parent.width @@ -49,13 +50,17 @@ MouseArea { ListView { id: listView - + cacheBuffer: itemHeight * listView.count 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..fc658da 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 { @@ -68,7 +72,7 @@ SwipeView { acceptedButtons: Qt.LeftButton | Qt.RightButton onClicked: { if (mouse.button === Qt.RightButton) { - menuManager.showMenu(id); + menuManager.showMenu(id, MenuInfo.FolderPage); return; } if (mouse.button === Qt.LeftButton) { 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..c56736b 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); @@ -163,11 +162,14 @@ RowLayout { Component { id: appViewComponent GridView { + id: appGridView + property string selectId: "" ScrollBar.vertical: fullScreenScrollBar cellWidth: contentViewBase.cellWidth cellHeight: contentViewBase.cellHeight boundsBehavior: Flickable.StopAtBounds model: modelManager.getAppModel() + cacheBuffer: cellHeight * appGridView.count / 6 delegate: Loader { width: GridView.view.cellWidth @@ -189,46 +191,134 @@ RowLayout { Component { id: appComponent - Item { - MouseArea { - anchors.centerIn: parent - hoverEnabled: true - width: 170; height: width - acceptedButtons: Qt.LeftButton | Qt.RightButton + DropArea { + id: dropArea + property int originalX + property int originalY - AppControls2.StyleBackground { - anchors.fill: parent - useStyleTransparent: false - paletteRole: Palette.Light - radius: 16 - alpha: parent.containsPress ? 0.25 : parent.containsMouse ? 0.15 : 0.00 - AppControls2.IconLabel { - anchors.fill: parent - iconWidth: 96; iconHeight: 96 - appName: name; appIcon: icon - spacing: 8 - textHighLight: true - } + onEntered: { + if (appGridView.selectId !== id) { + imageBase.visible = true; } + } + onExited: { + imageBase.visible = false; + } + onDropped: { + if (appGridView.selectId !== id) { + appGridView.model.addAppsToFolder(appGridView.selectId, id, ""); + } + } + Item { + id: appItem + property bool isSelect: false + property bool isEnterd: false + width: contentViewBase.cellWidth + height: contentViewBase.cellHeight + Drag.active: appItemMouseArea.drag.active + Drag.hotSpot.x: width / 2 + Drag.hotSpot.y: height / 2 + MouseArea { + id: appItemMouseArea + anchors.centerIn: parent + hoverEnabled: true + width: 170; height: width + acceptedButtons: Qt.LeftButton | Qt.RightButton - onClicked: { - if (mouse.button === Qt.RightButton) { - parent.parent.GridView.view.model.openMenu(index); - return; + AppControls2.StyleBackground { + anchors.fill: parent + useStyleTransparent: false + paletteRole: Palette.Light + radius: 16 + alpha: appItem.isSelect ? 0.00 : parent.containsPress ? 0.25 : parent.containsMouse ? 0.15 : 0.00 + + Item { + anchors.top: parent.top + anchors.topMargin: 14 + anchors.horizontalCenter: parent.horizontalCenter + height: 108 + width: 108 + + AppControls2.StyleBackground { + id: imageBase + anchors.fill: parent + paletteRole: Palette.Text + useStyleTransparent: false + alpha: 0.25 + radius: 24 + visible: false + } + Image { + id: iconImage + anchors.centerIn: parent + height: 96 + width: 96 + source: icon + } + } + + AppControls2.StyleText { + id: iconText + visible: !appItem.isSelect + width: parent.width + horizontalAlignment: Text.AlignHCenter + anchors.bottom: parent.bottom + anchors.bottomMargin: 20 + anchors.horizontalCenter: parent.horizontalCenter + text: name + font.pixelSize: 14 + elide: Text.ElideRight + paletteRole: Palette.HighlightedText + } } - if (mouse.button === Qt.LeftButton) { - parent.parent.GridView.view.model.appClicked(index); - return; + + onClicked: { + if (mouse.button === Qt.RightButton) { + appGridView.model.openMenu(index, MenuInfo.FullScreen); + return; + } + if (mouse.button === Qt.LeftButton) { + appGridView.model.appClicked(index); + return; + } + } + onPressAndHold: { + if (mouse.button === Qt.LeftButton) { + originalX = appItem.x; + originalY = appItem.y; + appItem.x = appItem.mapToItem(contentViewBase,0,0).x; + appItem.y = appItem.mapToItem(contentViewBase,0,0).y; + drag.target = appItem; + appItem.parent = contentViewBase; + appItem.isSelect = true; + appGridView.selectId = id; + } + } + onReleased: { + parent.Drag.drop(); + appItem.isSelect = false; + drag.target = null; + appItem.parent = dropArea; + appItem.x = originalX; + appItem.y = originalY; } } } } - } Component { id: normalFolderComponent - Item { + DropArea { + onEntered: { + folderItem.isSelect = true; + } + onExited: { + folderItem.isSelect = false; + } + onDropped: { + appGridView.model.addAppToFolder(appGridView.selectId, id); + } MouseArea { anchors.centerIn: parent hoverEnabled: true @@ -242,34 +332,51 @@ RowLayout { radius: 16 alpha: parent.containsPress ? 0.25 : parent.containsMouse ? 0.15 : 0.00 - ColumnLayout { - height: 128; width: parent.width - anchors.centerIn: parent - spacing: 8 + Item { + id: folderItem + property bool isSelect: false + anchors.horizontalCenter: parent.horizontalCenter + height: 108; width: 108 + anchors.top: parent.top + anchors.topMargin: 14 + + AppControls2.StyleBackground { + anchors.fill: parent + paletteRole: Palette.Text + useStyleTransparent: false + alpha: 0.25 + radius: 24 + visible: folderItem.isSelect + } + AppControls2.FolderIcon { id: folderIcon - Layout.margins: 5 + width: 96 + height: 96 rows: 4; columns: 4 spacing: 2; padding: 8 icons: icon - radius: 16; alpha: 0.25 - Layout.alignment: Qt.AlignHCenter - Layout.preferredWidth: 86; Layout.preferredHeight: 86 - } - AppControls2.StyleText { - Layout.fillWidth: true; Layout.fillHeight: true - horizontalAlignment: Text.AlignHCenter - elide: Text.ElideRight - text: name - font.pixelSize: 14 - paletteRole: Palette.HighlightedText + radius: 16; alpha: folderItem.isSelect ? 0 : 0.25 + anchors.centerIn: parent } } + + AppControls2.StyleText { + anchors.bottom: parent.bottom + anchors.bottomMargin: 20 + width: parent.width + horizontalAlignment: Text.AlignHCenter + anchors.horizontalCenter: parent.horizontalCenter + elide: Text.ElideRight + text: name + font.pixelSize: 14 + paletteRole: Palette.HighlightedText + } } onClicked: { if (mouse.button === Qt.RightButton) { - parent.parent.GridView.view.model.openMenu(index); + modelManager.getAppModel().openMenu(index, MenuInfo.FullScreen); return; } if (mouse.button === Qt.LeftButton) { @@ -363,6 +470,7 @@ RowLayout { anchors.centerIn: parent hoverEnabled: true width: 170; height: width + acceptedButtons: Qt.LeftButton | Qt.RightButton AppControls2.StyleBackground { anchors.fill: parent @@ -380,9 +488,15 @@ RowLayout { } } - onClicked: { - //console.log("clicked", Object.keys(model)) - labelRepeater.model.openApp(labelAppsGridView.labelIndex, model.index); + onClicked: function (mouse) { + if (mouse.button === Qt.RightButton) { + labelRepeater.model.openMenu(labelAppsGridView.labelIndex, model.index); + return; + } + if (mouse.button === Qt.LeftButton) { + labelRepeater.model.openApp(labelAppsGridView.labelIndex, model.index); + return; + } } } } diff --git a/qml/AppUI/FullScreenFolder.qml b/qml/AppUI/FullScreenFolder.qml index 6f793c6..4b1c40a 100644 --- a/qml/AppUI/FullScreenFolder.qml +++ b/qml/AppUI/FullScreenFolder.qml @@ -275,7 +275,7 @@ Loader { } onClicked: { if (mouse.button === Qt.RightButton) { - menuManager.showMenu(id); + menuManager.showMenu(id, MenuInfo.FullScreenFolder); return; } if (mouse.button === Qt.LeftButton) { @@ -305,4 +305,3 @@ Loader { } } - 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/extensions/FavoriteExtension.qml b/qml/extensions/FavoriteExtension.qml index cf6564f..663a841 100644 --- a/qml/extensions/FavoriteExtension.qml +++ b/qml/extensions/FavoriteExtension.qml @@ -49,7 +49,6 @@ UkuiMenuExtension { anchors.leftMargin: 16 anchors.topMargin: 12 anchors.bottomMargin: 6 - clip: true cellWidth: itemHeight + spacing; cellHeight: cellWidth property int exchangedStartIndex: 0 property int spacing: 4 @@ -71,7 +70,9 @@ UkuiMenuExtension { id: delegateRoot property int visualIndex: DelegateModel.itemsIndex width: favoriteView.cellWidth; height: favoriteView.cellHeight - onEntered: visualModel.items.move(drag.source.visualIndex, icon.visualIndex) + onEntered: { + visualModel.items.move(drag.source.visualIndex, icon.visualIndex) + } Binding { target: icon; property: "visualIndex"; value: visualIndex } @@ -112,11 +113,6 @@ UkuiMenuExtension { pressAndHoldInterval: 300 acceptedButtons: Qt.LeftButton | Qt.RightButton - onPressed: { - if (mouse.button === Qt.RightButton) { - console.log("RightButtononPressed:",mouse.x,mouse.y); - } - } onClicked: { if (mouse.button === Qt.RightButton) { visualModel.model.openMenu(index) @@ -161,8 +157,20 @@ UkuiMenuExtension { } ParallelAnimation { id: iconResetAnimation - NumberAnimation { target: icon; property: "x"; to: delegateRoot.x; easing.type: Easing.OutQuad; duration: 300 } - NumberAnimation { target: icon; property: "y"; to: delegateRoot.y; easing.type: Easing.OutQuad; duration: 300 } + NumberAnimation { + target: icon + property: "x" + to: delegateRoot.x + easing.type: Easing.OutQuad + duration: 300 + } + NumberAnimation { + target: icon + property: "y" + to: delegateRoot.y - favoriteView.contentY + easing.type: Easing.OutQuad + duration: 300 + } onFinished: { icon.parent = delegateRoot; 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/appdata/app-data-manager.cpp b/src/appdata/app-data-manager.cpp index ac763b4..e1d2c98 100644 --- a/src/appdata/app-data-manager.cpp +++ b/src/appdata/app-data-manager.cpp @@ -146,6 +146,9 @@ void AppDataWorker::onAppAdded(const QStringList &infos) QList apps; appendApps(infos, apps); + if (apps.isEmpty()) { + return; + } Q_EMIT appAdded(apps); updateFavoriteApps(); @@ -188,6 +191,9 @@ void AppDataWorker::onAppUpdated(const UkuiSearch::ApplicationInfoMap &infos) QList apps; updateApps(infos, apps); + if (apps.isEmpty()) { + return; + } Q_EMIT appUpdated(apps); updateFavoriteApps(); @@ -201,6 +207,9 @@ void AppDataWorker::onAppUpdatedAll(const QStringList &infos) QList apps; updateAppsAll(infos, apps); + if (apps.isEmpty()) { + return; + } Q_EMIT appUpdated(apps); updateFavoriteApps(); @@ -281,6 +290,9 @@ void AppDataWorker::onAppDeleted(QStringList infos) } QStringList removedIdList; removeApps(infos, removedIdList); + if (removedIdList.isEmpty()) { + return; + } Q_EMIT appDeleted(removedIdList); updateFavoriteApps(); diff --git a/src/appdata/app-folder-helper.cpp b/src/appdata/app-folder-helper.cpp index 16ec4f2..68dc1e6 100644 --- a/src/appdata/app-folder-helper.cpp +++ b/src/appdata/app-folder-helper.cpp @@ -40,11 +40,12 @@ class FolderMenuProvider : public MenuProvider public: FolderMenuProvider(); int index() override { return 256; } - QList generateActions(QObject *parent, const RequestType &type, const QVariant &data) override; - + bool isSupport(const RequestType &type) override; + QList generateActions(QObject *parent, const QVariant &data, const MenuInfo::Location &location, const QString &locationId) override; private: - void folderMenuForNormal(QObject *parent, const QString &appId, QList &list); - void folderMenuForFolder(QObject *parent, const QString &folderId, QList &list); + static void folderMenuForNormal(QObject *parent, const QString &appId, QList &list); + static void dissolveFolder(QObject *parent, const QString &folderId, QList &list); + static void renameFolder(QObject *parent, const QString &folderId, QList &list); }; FolderMenuProvider::FolderMenuProvider() @@ -52,24 +53,52 @@ FolderMenuProvider::FolderMenuProvider() } -QList FolderMenuProvider::generateActions(QObject *parent, const MenuProvider::RequestType &type, const QVariant &data) +bool FolderMenuProvider::isSupport(const MenuProvider::RequestType &type) { - if (!parent || (type != DataType)) { + return type == DataType; +} + +QList +FolderMenuProvider::generateActions(QObject *parent, const QVariant &data, const MenuInfo::Location &location, + const QString &locationId) +{ + if (!parent) { return {}; } DataEntity app = data.value(); QList list; - if (app.type() == DataType::Normal) { - // 应用菜单 - folderMenuForNormal(parent, app.id(), list); - return list; - } - if (app.type() == DataType::Folder) { - //文件夹菜单 - folderMenuForFolder(parent, app.id(), list); - return list; + + switch (location) { + case MenuInfo::FullScreen: { + if (app.type() == DataType::Folder) { + dissolveFolder(parent, app.id(), list); + break; + } + } + case MenuInfo::AppList: { + if (app.type() == DataType::Folder) { + renameFolder(parent, app.id(), list); + dissolveFolder(parent, app.id(), list); + break; + } + + if (app.type() != DataType::Normal || locationId != "all") { + break; + } + } + case MenuInfo::FullScreenFolder: + case MenuInfo::FolderPage: { + if (app.type() == DataType::Normal) { + folderMenuForNormal(parent, app.id(), list); + } + break; + } + case MenuInfo::Extension: + default: + break; } + return list; } @@ -98,24 +127,19 @@ void FolderMenuProvider::folderMenuForNormal(QObject *parent, const QString &app subMenu->addAction(newAct); QList folders = AppFolderHelper::instance()->folderData(); - for (const Folder &folder : folders) { - QString name = QObject::tr("Add to \"%1\"").arg(folder.getName()); + for (const Folder &f : folders) { + QString name = QObject::tr("Add to \"%1\"").arg(f.getName()); QAction* act = new QAction(name, subMenu); - QObject::connect(act, &QAction::triggered, parent, [appId, folder] { - AppFolderHelper::instance()->addAppToFolder(appId, folder.getId()); + QObject::connect(act, &QAction::triggered, parent, [appId, f] { + AppFolderHelper::instance()->addAppToFolder(appId, f.getId()); }); subMenu->addAction(act); } menu->addMenu(subMenu); } -void FolderMenuProvider::folderMenuForFolder(QObject *parent, const QString &folderId, QList &list) +void FolderMenuProvider::dissolveFolder(QObject *parent, const QString &folderId, QList &list) { - list << new QAction(QObject::tr("Rename"), parent); - QObject::connect(list.last(), &QAction::triggered, parent, [folderId] { - ModelManager::instance()->getAppModel()->toRenameFolder(folderId); - }); - list << new QAction(QObject::tr("Dissolve folder"), parent); QObject::connect(list.last(), &QAction::triggered, parent, [folderId] { AppFolderHelper::instance()->deleteFolder(folderId.toInt()); @@ -123,6 +147,14 @@ void FolderMenuProvider::folderMenuForFolder(QObject *parent, const QString &fol }); } +void FolderMenuProvider::renameFolder(QObject *parent, const QString &folderId, QList &list) +{ + list << new QAction(QObject::tr("Rename"), parent); + QObject::connect(list.last(), &QAction::triggered, parent, [folderId] { + ModelManager::instance()->getAppModel()->toRenameFolder(folderId); + }); +} + QString AppFolderHelper::s_folderConfigFile = QDir::homePath() + "/" + FOLDER_FILE_PATH + FOLDER_FILE_NAME; AppFolderHelper *AppFolderHelper::instance() @@ -211,6 +243,30 @@ void AppFolderHelper::addAppToNewFolder(const QString &appId, const QString &fol Q_EMIT folderAdded(folder.id); } +void AppFolderHelper::addAppsToNewFolder(const QString &appIdA, const QString &appIdB, const QString &folderName) +{ + if (appIdA.isEmpty() || appIdB.isEmpty()) { + return; + } + + // TODO: max越界处理 + int max = m_folders.isEmpty() ? -1 : m_folders.lastKey(); + QString name = folderName; + if (name.isEmpty()) { + name = tr("New Folder %1").arg(m_folders.size() + 1); + } + + Folder folder; + folder.id = ++max; + folder.name = name; + folder.apps.append(appIdA); + folder.apps.append(appIdB); + + insertFolder(folder); + Q_EMIT folderAdded(folder.id); + forceSync(); +} + void AppFolderHelper::removeAppFromFolder(const QString &appId, const int &folderId) { if (appId.isEmpty()) { diff --git a/src/appdata/app-folder-helper.h b/src/appdata/app-folder-helper.h index 8bddb5d..2c3502d 100644 --- a/src/appdata/app-folder-helper.h +++ b/src/appdata/app-folder-helper.h @@ -74,6 +74,7 @@ public: void addAppToFolder(const QString& appId, const int& folderId); void addAppToNewFolder(const QString& appId, const QString& folderName); + void addAppsToNewFolder(const QString& appIdA, const QString& appIdB, const QString& folderName); void removeAppFromFolder(const QString& appId, const int& folderId); bool deleteFolder(const int& folderId); void renameFolder(const int& folderId, const QString& folderName); diff --git a/src/appdata/plugin/all-app-data-provider.cpp b/src/appdata/plugin/all-app-data-provider.cpp index 9ba77c9..bdf2986 100644 --- a/src/appdata/plugin/all-app-data-provider.cpp +++ b/src/appdata/plugin/all-app-data-provider.cpp @@ -158,15 +158,24 @@ bool AllAppDataProvider::appDataSort(const DataEntity &a, const DataEntity &b) } else if ((a.top() == 0) && (b.top() == 0)) { if (a.isRecentInstall()) { if (b.isRecentInstall()) { - return QDateTime::fromString(a.insertTime(), "yyyy-MM-dd hh:mm:ss") - > QDateTime::fromString(b.insertTime(), "yyyy-MM-dd hh:mm:ss"); + if (QDateTime::fromString(a.insertTime(), "yyyy-MM-dd hh:mm:ss") + != QDateTime::fromString(b.insertTime(), "yyyy-MM-dd hh:mm:ss")) { + return QDateTime::fromString(a.insertTime(), "yyyy-MM-dd hh:mm:ss") + > QDateTime::fromString(b.insertTime(), "yyyy-MM-dd hh:mm:ss"); + } else { + return letterSort(a.firstLetter(), b.firstLetter()); + } } else { return true; } } else if (b.isRecentInstall()) { return false; } else { - return a.launchTimes() > b.launchTimes(); + if (a.launchTimes() == b.launchTimes()) { + return letterSort(a.firstLetter(), b.firstLetter()); + } else { + return a.launchTimes() > b.launchTimes(); + } } } else { return a.top() > b.top(); @@ -188,6 +197,14 @@ void AllAppDataProvider::setRecentState(DataEntity &app) app.setRecentInstall(false); } +bool AllAppDataProvider::letterSort(const QString &a, const QString &b) +{ + if (QString::compare(a, b, Qt::CaseInsensitive) == 0) { + return false; + } + return QString::compare(a, b, Qt::CaseInsensitive) > 0 ? false : true; +} + void AllAppDataProvider::sendData() { QVector data; diff --git a/src/appdata/plugin/all-app-data-provider.h b/src/appdata/plugin/all-app-data-provider.h index 8b5f190..90ef01a 100644 --- a/src/appdata/plugin/all-app-data-provider.h +++ b/src/appdata/plugin/all-app-data-provider.h @@ -55,6 +55,7 @@ private: void updateData(const QList& apps); static bool appDataSort(const DataEntity &a, const DataEntity &b); static void setRecentState(DataEntity &app); + static bool letterSort(const QString &a, const QString &b); private: QMutex m_mutex; diff --git a/src/extension/extensions/favorite-extension.cpp b/src/extension/extensions/favorite-extension.cpp index 98d9e7f..9a1b5a8 100644 --- a/src/extension/extensions/favorite-extension.cpp +++ b/src/extension/extensions/favorite-extension.cpp @@ -31,7 +31,8 @@ class FavoriteMenuProvider : public MenuProvider { public: int index() override { return 512; } - QList generateActions(QObject *parent, const RequestType &type, const QVariant &data) override; + bool isSupport(const RequestType &type) override; + QList generateActions(QObject *parent, const QVariant &data, const MenuInfo::Location &location, const QString &locationId) override; }; class FavoriteAppsModel : public QAbstractListModel @@ -170,7 +171,7 @@ void FavoriteAppsModel::openMenu(const int &index) return; } - MenuManager::instance()->showMenu(m_favoriteAppsData.at(index)); + MenuManager::instance()->showMenu(m_favoriteAppsData.at(index), MenuInfo::Extension, "favorite"); } void FavoriteAppsModel::exchangedAppsOrder(int startIndex, int endIndex) @@ -180,9 +181,16 @@ void FavoriteAppsModel::exchangedAppsOrder(int startIndex, int endIndex) AppDataManager::instance()->changedFavoriteOrderSignal(startId, endNum); } -QList FavoriteMenuProvider::generateActions(QObject *parent, const MenuProvider::RequestType &type, const QVariant &data) +bool FavoriteMenuProvider::isSupport(const MenuProvider::RequestType &type) { - if (!parent || (type != DataType)) { + return type == DataType; +} + +QList +FavoriteMenuProvider::generateActions(QObject *parent, const QVariant &data, const MenuInfo::Location &location, + const QString &locationId) +{ + if (!parent) { return {}; } @@ -193,19 +201,29 @@ QList FavoriteMenuProvider::generateActions(QObject *parent, const Me QList list; - if (app.favorite() == 0) { - list << new QAction(QObject::tr("Fix to favorite"), parent); - QObject::connect(list.last(), &QAction::triggered, parent, [app] { - Q_EMIT AppDataManager::instance()->fixToFavoriteSignal(app.id(), 1); - }); - } else { - list << new QAction(QObject::tr("Remove from favorite"), parent); - QObject::connect(list.last(), &QAction::triggered, parent, [app] { - Q_EMIT AppDataManager::instance()->fixToFavoriteSignal(app.id(), 0); - }); + switch (location) { + case MenuInfo::AppList: + case MenuInfo::FolderPage: + case MenuInfo::Extension: { + if (app.favorite() == 0) { + list << new QAction(QObject::tr("Fix to favorite"), parent); + QObject::connect(list.last(), &QAction::triggered, parent, [app] { + Q_EMIT AppDataManager::instance()->fixToFavoriteSignal(app.id(), 1); + }); + } else if (locationId == "favorite") { + list << new QAction(QObject::tr("Remove from favorite"), parent); + QObject::connect(list.last(), &QAction::triggered, parent, [app] { + Q_EMIT AppDataManager::instance()->fixToFavoriteSignal(app.id(), 0); + }); + } + break; + } + case MenuInfo::FullScreen: + case MenuInfo::FullScreenFolder: + default: + break; } - return list; } diff --git a/src/menu/menu-manager.cpp b/src/menu/menu-manager.cpp index 9b758d5..ebc0737 100644 --- a/src/menu/menu-manager.cpp +++ b/src/menu/menu-manager.cpp @@ -41,7 +41,8 @@ class AppMenuProvider : public MenuProvider public: AppMenuProvider(); int index() override { return 1024; } - QList generateActions(QObject *parent, const RequestType &type, const QVariant &data) override; + bool isSupport(const RequestType &type) override; + QList generateActions(QObject *parent, const QVariant &data, const MenuInfo::Location &location, const QString &locationId) override; private: void addToTop(QObject *parent, const QString &appId, const int &appTop, QList &list); @@ -62,10 +63,16 @@ AppMenuProvider::AppMenuProvider() } } -QList -AppMenuProvider::generateActions(QObject *parent, const MenuProvider::RequestType &type, const QVariant &data) +bool AppMenuProvider::isSupport(const MenuProvider::RequestType &type) { - if (!parent || (type != DataType)) { + return type == DataType; +} + +QList +AppMenuProvider::generateActions(QObject *parent, const QVariant &data, const MenuInfo::Location &location, + const QString &locationId) +{ + if (!parent) { return {}; } @@ -78,17 +85,27 @@ AppMenuProvider::generateActions(QObject *parent, const MenuProvider::RequestTyp QString appId = app.id(); int appTop = app.top(); - //置顶 - addToTop(parent, appId, appTop ,list); - - // 添加到任务栏 - addToPanelAction(parent, appId, list); - - //添加到桌面快捷方式 - addToDesktopAction(parent, appId, list); - - // 卸载 - addUninstall(parent, appId, list); + switch (location) { + case MenuInfo::AppList: { + //置顶 + if (locationId == "all") { + addToTop(parent, appId, appTop, list); + } + } + case MenuInfo::Extension: + case MenuInfo::FolderPage: + case MenuInfo::FullScreen: + case MenuInfo::FullScreenFolder: + // 卸载 + addToPanelAction(parent, appId, list); + // 添加到任务栏 + addUninstall(parent, appId, list); + //添加到桌面快捷方式 + addToDesktopAction(parent, appId, list); + break; + default: + break; + } return list; } @@ -213,26 +230,29 @@ void MenuManager::registerMenuProvider(MenuProvider *provider) }); } -void MenuManager::showMenu(const QString &appid, const QPoint &point) +void MenuManager::showMenu(const QString &appid, const MenuInfo::Location location, const QString &lid, const QPoint &point) { DataEntity app; if (AppDataManager::instance()->getApp(appid, app)) { - showMenu(MenuProvider::DataType, QVariant::fromValue(app), point); + showMenu(MenuProvider::DataType, QVariant::fromValue(app), location, lid, point); } } -void MenuManager::showMenu(const DataEntity &entity, const QPoint &point) +void MenuManager::showMenu(const DataEntity &entity, const MenuInfo::Location location, const QString &lid, + const QPoint &point) { - showMenu(MenuProvider::DataType, QVariant::fromValue(entity), point); + showMenu(MenuProvider::DataType, QVariant::fromValue(entity), location, lid, point); } -void MenuManager::showMenu(const MenuProvider::RequestType &type, const QVariant &data, const QPoint &point) +void MenuManager::showMenu(const MenuProvider::RequestType &type, QVariant data, const MenuInfo::Location location, const QString &lid, const QPoint &point) { QMenu menu; QList actions; for (const auto &provider : d->providers) { - actions.append(provider->generateActions(&menu, type, data)); + if (provider->isSupport(type)) { + actions.append(provider->generateActions(&menu, data, location, lid)); + } } if (actions.isEmpty() && menu.isEmpty()) { diff --git a/src/menu/menu-manager.h b/src/menu/menu-manager.h index 935e26e..2a11552 100644 --- a/src/menu/menu-manager.h +++ b/src/menu/menu-manager.h @@ -36,9 +36,9 @@ public: static MenuManager *instance(); ~MenuManager() override; void registerMenuProvider(MenuProvider *provider); - Q_INVOKABLE void showMenu(const QString &appid, const QPoint &point = QPoint()); - void showMenu(const DataEntity &entity, const QPoint &point = QPoint()); - void showMenu(const MenuProvider::RequestType &type, const QVariant &data, const QPoint &point = QPoint()); + Q_INVOKABLE void showMenu(const QString &appid, MenuInfo::Location, const QString& lid = QString(), const QPoint &point = QPoint()); + void showMenu(const DataEntity &entity, MenuInfo::Location location, const QString& lid = QString(), const QPoint &point = QPoint()); + void showMenu(const MenuProvider::RequestType &type, QVariant data, MenuInfo::Location location, const QString& lid = QString(), const QPoint &point = QPoint()); private: MenuManager(); diff --git a/src/menu/menu-provider.h b/src/menu/menu-provider.h index 4c9a7c5..45273a9 100644 --- a/src/menu/menu-provider.h +++ b/src/menu/menu-provider.h @@ -28,6 +28,20 @@ namespace UkuiMenu { +class MenuInfo +{ + Q_GADGET +public: + enum Location { + AppList = 0, + Extension, + FolderPage, + FullScreen, + FullScreenFolder, + }; + Q_ENUM(Location) +}; + class MenuProvider { public: @@ -37,7 +51,8 @@ public: }; virtual ~MenuProvider() = default; virtual int index() = 0; - virtual QList generateActions(QObject *parent, const RequestType &type, const QVariant &data) = 0; + virtual bool isSupport(const RequestType &type) = 0; + virtual QList generateActions(QObject *parent, const QVariant &data, const MenuInfo::Location &location, const QString &locationId) = 0; }; } // UkuiMenu diff --git a/src/model/app-group-model.cpp b/src/model/app-group-model.cpp index 712a48b..5d35255 100644 --- a/src/model/app-group-model.cpp +++ b/src/model/app-group-model.cpp @@ -177,4 +177,10 @@ inline void AppGroupModel::resetModel(QVector &labels) d->labelIndex = QVector(d->labels.size(), -1); } +void AppGroupModel::openMenu(int labelIndex, int appIndex) +{ + int index = d->labelIndex.at(labelIndex); + d->appModel->openMenu(++index + appIndex, MenuInfo::FullScreen); +} + } // UkuiMenu diff --git a/src/model/app-group-model.h b/src/model/app-group-model.h index dd03c9a..ce772e9 100644 --- a/src/model/app-group-model.h +++ b/src/model/app-group-model.h @@ -40,6 +40,7 @@ public: bool containLabel(); + Q_INVOKABLE void openMenu(int labelIndex, int appIndex); Q_INVOKABLE void openApp(int labelIndex, int appIndex); Q_INVOKABLE int getLabelIndex(const QString &labelId); diff --git a/src/model/app-model.cpp b/src/model/app-model.cpp index 2e56065..27f127e 100644 --- a/src/model/app-model.cpp +++ b/src/model/app-model.cpp @@ -187,13 +187,13 @@ void AppModel::insertData(QVector &data, int index) Q_EMIT endInsertRows(); } -void AppModel::openMenu(const int &index) +void AppModel::openMenu(const int &index, MenuInfo::Location location) { if (index < 0 || index >= m_apps.size()) { return; } - MenuManager::instance()->showMenu(m_apps.at(index)); + MenuManager::instance()->showMenu(m_apps.at(index), location, DataProviderManager::instance()->activatedProvider()); } void AppModel::renameFolder(const QString &index, const QString &folderName) @@ -201,6 +201,16 @@ void AppModel::renameFolder(const QString &index, const QString &folderName) AppFolderHelper::instance()->renameFolder(index.toInt(), folderName); } +void AppModel::addAppsToFolder(const QString &appIdA, const QString &appIdB, const QString &folderName) +{ + AppFolderHelper::instance()->addAppsToNewFolder(appIdA, appIdB, folderName); +} + +void AppModel::addAppToFolder(const QString &appId, const QString &folderId) +{ + AppFolderHelper::instance()->addAppToFolder(appId, folderId.toInt()); +} + QVariantList AppModel::getApps(int start, int end) { if (start < 0 || start >= m_apps.size() || end < 0 || end > m_apps.size()) { diff --git a/src/model/app-model.h b/src/model/app-model.h index 7299730..61e63c6 100644 --- a/src/model/app-model.h +++ b/src/model/app-model.h @@ -24,6 +24,7 @@ #include "commons.h" #include "data-provider-manager.h" +#include "menu-provider.h" namespace UkuiMenu { @@ -41,8 +42,10 @@ public: Q_INVOKABLE int getLabelIndex(const QString &id); Q_INVOKABLE QVariantList folderApps(const QString &folderName); Q_INVOKABLE void appClicked(const int &index); - Q_INVOKABLE void openMenu(const int &index); + Q_INVOKABLE void openMenu(const int &index, MenuInfo::Location location); Q_INVOKABLE void renameFolder(const QString &index, const QString &folderName); + Q_INVOKABLE void addAppsToFolder(const QString &appIdA, const QString &appIdB, const QString &folderName); + Q_INVOKABLE void addAppToFolder(const QString &appId, const QString &folderId); public Q_SLOTS: void toRenameFolder(QString id); diff --git a/src/ukui-menu-application.cpp b/src/ukui-menu-application.cpp index d8f6d89..1284136 100644 --- a/src/ukui-menu-application.cpp +++ b/src/ukui-menu-application.cpp @@ -66,8 +66,10 @@ void UkuiMenuApplication::registerQmlTypes() // commons qRegisterMetaType("DataType::Type"); qRegisterMetaType("DataEntity"); + qRegisterMetaType("MenuInfo::Location"); qmlRegisterUncreatableType(uri, versionMajor, versionMinor, "Display", "Use enums only."); qmlRegisterUncreatableType(uri, versionMajor, versionMinor, "DataType", "Use enums only"); + qmlRegisterUncreatableType(uri, versionMajor, versionMinor, "MenuInfo", "Use enums only."); // qmlRegisterUncreatableType(uri, versionMajor, versionMinor, "DataEntity", "unknown"); // vis colors @@ -103,6 +105,8 @@ void UkuiMenuApplication::initQmlEngine() // }, Qt::QueuedConnection); // // m_applicationEngine->load(url); + + connect(AppManager::instance(), &AppManager::request, this, &UkuiMenuApplication::execCommand); } void UkuiMenuApplication::loadMenuUI() @@ -148,6 +152,12 @@ void UkuiMenuApplication::execCommand(Command command) QCoreApplication::quit(); break; } + case Hide: { + if (m_mainWindow) { + m_mainWindow->setVisible(false); + } + break; + } default: break; } diff --git a/src/ukui-menu-application.h b/src/ukui-menu-application.h index da1d71e..2b419f0 100644 --- a/src/ukui-menu-application.h +++ b/src/ukui-menu-application.h @@ -37,7 +37,8 @@ public: enum Command { Active = 0, Show, - Quit + Quit, + Hide }; explicit UkuiMenuApplication(MenuMessageProcessor *processor); ~UkuiMenuApplication() override; diff --git a/src/utils/app-manager.cpp b/src/utils/app-manager.cpp index 147fd60..72502fc 100644 --- a/src/utils/app-manager.cpp +++ b/src/utils/app-manager.cpp @@ -49,6 +49,7 @@ AppManager::AppManager(QObject *parent) : QObject(parent) bool AppManager::launchApp(const QString &desktopFilePath) { + Q_EMIT request(UkuiMenuApplication::Hide); if (launchAppWithDBus(desktopFilePath)) { return true; } diff --git a/src/utils/app-manager.h b/src/utils/app-manager.h index aa39852..7a26d85 100644 --- a/src/utils/app-manager.h +++ b/src/utils/app-manager.h @@ -19,6 +19,7 @@ #ifndef UKUI_MENU_APP_MANAGER_H #define UKUI_MENU_APP_MANAGER_H #include +#include "ukui-menu-application.h" class QDBusInterface; @@ -43,6 +44,9 @@ private: private: QDBusInterface *m_appManagerDbusInterface = nullptr; + +Q_SIGNALS: + void request(UkuiMenuApplication::Command command); }; } // UkuiMenu 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(); diff --git a/src/windows/menu-main-window.cpp b/src/windows/menu-main-window.cpp index abf5102..c292e6f 100644 --- a/src/windows/menu-main-window.cpp +++ b/src/windows/menu-main-window.cpp @@ -395,7 +395,7 @@ void MenuWindow::init() setColor("transparent"); // setFlags(Qt::FramelessWindowHint); - setFlag(Qt::WindowStaysOnTopHint); + setFlags(Qt::Window | Qt::WindowStaysOnTopHint); WindowHelper::setWindowAttribute(this); WindowHelper::removeHeaderBar(this); WindowHelper::windowBlur(this, true); @@ -429,6 +429,12 @@ void MenuWindow::updateGeometry() return; } + if (m_isFullScreen) { + setMaximumSize(rect.size()); + } else { + setMinimumSize(rect.size()); + } + WindowHelper::setWindowGeometry(this, rect); setMinimumSize(geometry().size()); setMaximumSize(geometry().size()); @@ -486,7 +492,13 @@ void MenuWindow::enableWindowBlur(bool enable) void MenuWindow::exposeEvent(QExposeEvent *event) { if (isExposed()) { - requestActivate(); + if (QX11Info::isPlatformX11()) { + requestActivate(); + } else { + WindowHelper::setWindowAttribute(this); +// WindowHelper::removeHeaderBar(this); + updateGeometry(); + } } QQuickView::exposeEvent(event); }