From 835d5962d01c707cdb7ad5bf9dcb76c0f1ee7b1e Mon Sep 17 00:00:00 2001 From: reionwong Date: Tue, 1 Sep 2026 07:06:15 -0400 Subject: [PATCH] feat(menu): use shared context menu on desktop --- model/foldermodel.cpp | 5 + model/foldermodel.h | 1 + qml.qrc | 1 + qml/Desktop/Main.qml | 8 ++ qml/FolderContextMenu.qml | 248 ++++++++++++++++++++++++++++++++++++++ qml/FolderGridView.qml | 15 ++- qml/FolderListView.qml | 15 ++- qml/FolderPage.qml | 19 ++- qml/OptionsMenu.qml | 14 +-- qml/SideBar.qml | 10 +- 10 files changed, 309 insertions(+), 27 deletions(-) create mode 100644 qml/FolderContextMenu.qml diff --git a/model/foldermodel.cpp b/model/foldermodel.cpp index 8e0d5b8..e2c1dd0 100644 --- a/model/foldermodel.cpp +++ b/model/foldermodel.cpp @@ -1499,6 +1499,11 @@ void FolderModel::setWallpaperSelected() iface.call("setWallpaper", url.toLocalFile()); } +void FolderModel::prepareContextMenu() +{ + updateActions(); +} + void FolderModel::openContextMenu(QQuickItem *visualParent, Qt::KeyboardModifiers modifiers) { Q_UNUSED(modifiers); diff --git a/model/foldermodel.h b/model/foldermodel.h index a5ef858..28b3423 100644 --- a/model/foldermodel.h +++ b/model/foldermodel.h @@ -217,6 +217,7 @@ public: Q_INVOKABLE void setWallpaperSelected(); + Q_INVOKABLE void prepareContextMenu(); Q_INVOKABLE void openContextMenu(QQuickItem *visualParent = nullptr, Qt::KeyboardModifiers modifiers = Qt::NoModifier); Q_INVOKABLE void openPropertiesDialog(); Q_INVOKABLE void openInTerminal(); diff --git a/qml.qrc b/qml.qrc index db0b3ac..95f358f 100644 --- a/qml.qrc +++ b/qml.qrc @@ -2,6 +2,7 @@ qml/main.qml qml/FolderPage.qml + qml/FolderContextMenu.qml qml/ArchiveProgressDialog.qml qml/SideBar.qml qml/FolderListItem.qml diff --git a/qml/Desktop/Main.qml b/qml/Desktop/Main.qml index 09edc00..3fd9855 100644 --- a/qml/Desktop/Main.qml +++ b/qml/Desktop/Main.qml @@ -62,6 +62,11 @@ Item { _folderView.contentWidth, _folderView.contentHeight) } + FolderContextMenu { + id: desktopMenu + folderModel: dirModel + } + ArchiveProgressDialog { id: archiveProgressDialog archiveModel: dirModel @@ -97,6 +102,9 @@ Item { flow: GridView.FlowTopToBottom delegate: FolderGridItem {} + useCustomContextMenu: true + + onContextMenuRequested: desktopMenu.popup() onIconSizeChanged: { globalSettings.desktopIconSize = _folderView.iconSize diff --git a/qml/FolderContextMenu.qml b/qml/FolderContextMenu.qml new file mode 100644 index 0000000..55faa19 --- /dev/null +++ b/qml/FolderContextMenu.qml @@ -0,0 +1,248 @@ +import QtQuick 2.12 + +import FishUI 1.0 as FishUI + +FishUI.DesktopMenu { + id: control + + property var folderModel: null + property bool hasSelection: folderModel ? folderModel.selectionCount > 0 : false + + FishUI.MenuItem { + id: newFolderItem + property var modelAction: control.folderModel ? control.folderModel.action("newFolder") : null + text: modelAction ? modelAction.text : qsTr("New Folder") + visible: !control.hasSelection && modelAction && modelAction.visible + enabled: modelAction ? modelAction.enabled : false + onTriggered: modelAction.trigger() + } + + FishUI.MenuItem { + id: newDocumentsItem + text: qsTr("New Documents") + visible: !control.hasSelection && newTextFileItem.visible + + childMenu: FishUI.DesktopMenu { + id: newDocumentsMenu + + FishUI.MenuItem { + id: newTextFileItem + property var modelAction: control.folderModel ? control.folderModel.action("newTextFile") : null + text: modelAction ? modelAction.text : qsTr("New Text") + visible: modelAction && modelAction.visible + enabled: modelAction ? modelAction.enabled : false + onTriggered: modelAction.trigger() + } + } + } + + FishUI.MenuSeparator { + visible: !control.hasSelection && + (newFolderItem.visible || newDocumentsItem.visible) + } + + FishUI.MenuItem { + id: pasteItem + property var modelAction: control.folderModel ? control.folderModel.action("paste") : null + text: modelAction ? modelAction.text : qsTr("Paste") + visible: !control.hasSelection && modelAction && modelAction.visible + enabled: modelAction ? modelAction.enabled : false + onTriggered: modelAction.trigger() + } + + FishUI.MenuItem { + id: selectAllItem + text: qsTr("Select All") + visible: !control.hasSelection + onTriggered: control.folderModel.selectAll() + } + + FishUI.MenuSeparator { + visible: !control.hasSelection && + (terminalItem.visible || changeBackgroundItem.visible) + } + + FishUI.MenuItem { + id: terminalItem + property var modelAction: control.folderModel ? control.folderModel.action("terminal") : null + text: modelAction ? modelAction.text : qsTr("Open in Terminal") + visible: !control.hasSelection && modelAction && modelAction.visible + enabled: modelAction ? modelAction.enabled : false + onTriggered: modelAction.trigger() + } + + FishUI.MenuItem { + id: changeBackgroundItem + property var modelAction: control.folderModel ? control.folderModel.action("changeBackground") : null + text: modelAction ? modelAction.text : qsTr("Change background") + visible: !control.hasSelection && control.folderModel && control.folderModel.isDesktop && + modelAction && modelAction.visible + enabled: modelAction ? modelAction.enabled : false + onTriggered: modelAction.trigger() + } + + FishUI.MenuSeparator { + visible: !control.hasSelection && showHiddenItem.visible + } + + FishUI.MenuItem { + id: showHiddenItem + property var modelAction: control.folderModel ? control.folderModel.action("showHidden") : null + text: modelAction ? modelAction.text : qsTr("Show hidden files") + visible: !control.hasSelection && modelAction && modelAction.visible + enabled: modelAction ? modelAction.enabled : false + checkable: true + checked: modelAction ? modelAction.checked : false + onTriggered: modelAction.trigger() + } + + FishUI.MenuSeparator { + visible: !control.hasSelection && emptyTrashItem.visible + } + + FishUI.MenuItem { + id: emptyTrashItem + property var modelAction: control.folderModel ? control.folderModel.action("emptyTrash") : null + text: modelAction ? modelAction.text : qsTr("Empty Trash") + visible: !control.hasSelection && modelAction && modelAction.visible + enabled: modelAction ? modelAction.enabled : false + onTriggered: modelAction.trigger() + } + + FishUI.MenuItem { + id: restoreItem + property var modelAction: control.folderModel ? control.folderModel.action("restore") : null + text: modelAction ? modelAction.text : qsTr("Restore") + visible: control.hasSelection && modelAction && modelAction.visible + enabled: modelAction ? modelAction.enabled : false + onTriggered: modelAction.trigger() + } + + FishUI.MenuItem { + id: openItem + property var modelAction: control.folderModel ? control.folderModel.action("open") : null + text: modelAction ? modelAction.text : qsTr("Open") + visible: control.hasSelection && modelAction && modelAction.visible + enabled: modelAction ? modelAction.enabled : false + onTriggered: modelAction.trigger() + } + + FishUI.MenuItem { + id: openInNewWindowItem + property var modelAction: control.folderModel ? control.folderModel.action("openInNewWindow") : null + text: modelAction ? modelAction.text : qsTr("Open in new window") + visible: control.hasSelection && modelAction && modelAction.visible + enabled: modelAction ? modelAction.enabled : false + onTriggered: modelAction.trigger() + } + + FishUI.MenuItem { + id: openWithItem + property var modelAction: control.folderModel ? control.folderModel.action("openWith") : null + text: modelAction ? modelAction.text : qsTr("Open with") + visible: control.hasSelection && modelAction && modelAction.visible + enabled: modelAction ? modelAction.enabled : false + onTriggered: modelAction.trigger() + } + + FishUI.MenuItem { + id: compressItem + property var modelAction: control.folderModel ? control.folderModel.action("compress") : null + text: modelAction ? modelAction.text : qsTr("Compress") + visible: control.hasSelection && modelAction && modelAction.visible + enabled: modelAction ? modelAction.enabled : false + onTriggered: modelAction.trigger() + } + + FishUI.MenuSeparator { + visible: control.hasSelection && + (cutItem.visible || copyItem.visible || trashItem.visible || + deleteItem.visible || renameItem.visible) + } + + FishUI.MenuItem { + id: cutItem + property var modelAction: control.folderModel ? control.folderModel.action("cut") : null + text: modelAction ? modelAction.text : qsTr("Cut") + visible: control.hasSelection && modelAction && modelAction.visible + enabled: modelAction ? modelAction.enabled : false + onTriggered: modelAction.trigger() + } + + FishUI.MenuItem { + id: copyItem + property var modelAction: control.folderModel ? control.folderModel.action("copy") : null + text: modelAction ? modelAction.text : qsTr("Copy") + visible: control.hasSelection && modelAction && modelAction.visible + enabled: modelAction ? modelAction.enabled : false + onTriggered: modelAction.trigger() + } + + FishUI.MenuItem { + id: trashItem + property var modelAction: control.folderModel ? control.folderModel.action("trash") : null + text: modelAction ? modelAction.text : qsTr("Move To Trash") + visible: control.hasSelection && modelAction && modelAction.visible + enabled: modelAction ? modelAction.enabled : false + onTriggered: modelAction.trigger() + } + + FishUI.MenuItem { + id: deleteItem + property var modelAction: control.folderModel ? control.folderModel.action("del") : null + text: modelAction ? modelAction.text : qsTr("Delete") + visible: control.hasSelection && modelAction && modelAction.visible + enabled: modelAction ? modelAction.enabled : false + onTriggered: modelAction.trigger() + } + + FishUI.MenuItem { + id: renameItem + property var modelAction: control.folderModel ? control.folderModel.action("rename") : null + text: modelAction ? modelAction.text : qsTr("Rename") + visible: control.hasSelection && modelAction && modelAction.visible + enabled: modelAction ? modelAction.enabled : false + onTriggered: modelAction.trigger() + } + + FishUI.MenuSeparator { + visible: control.hasSelection && + (terminalSelectedItem.visible || wallpaperItem.visible) + } + + FishUI.MenuItem { + id: terminalSelectedItem + property var modelAction: control.folderModel ? control.folderModel.action("terminal") : null + text: modelAction ? modelAction.text : qsTr("Open in Terminal") + visible: control.hasSelection && modelAction && modelAction.visible + enabled: modelAction ? modelAction.enabled : false + onTriggered: modelAction.trigger() + } + + FishUI.MenuItem { + id: wallpaperItem + property var modelAction: control.folderModel ? control.folderModel.action("wallpaper") : null + text: modelAction ? modelAction.text : qsTr("Set as Wallpaper") + visible: control.hasSelection && modelAction && modelAction.visible + enabled: modelAction ? modelAction.enabled : false + onTriggered: modelAction.trigger() + } + + FishUI.MenuSeparator { + visible: propertiesItem.visible + } + + FishUI.MenuItem { + id: propertiesItem + property var modelAction: control.folderModel ? control.folderModel.action("properties") : null + text: modelAction ? modelAction.text : qsTr("Properties") + visible: modelAction && modelAction.visible + enabled: modelAction ? modelAction.enabled : false + onTriggered: modelAction.trigger() + } + + onVisibleChanged: { + if (visible && folderModel) + folderModel.prepareContextMenu() + } +} diff --git a/qml/FolderGridView.qml b/qml/FolderGridView.qml index 247e8fd..a18d323 100644 --- a/qml/FolderGridView.qml +++ b/qml/FolderGridView.qml @@ -30,6 +30,7 @@ GridView { objectName: "FolderGridView" property bool isDesktopView: false + property bool useCustomContextMenu: false property Item rubberBand: null property Item hoveredItem: null @@ -65,6 +66,7 @@ GridView { property bool scrollDown: false signal keyPress(var event) + signal contextMenuRequested() cacheBuffer: Math.max(0, control.height * 1.5) reuseItems: true @@ -142,6 +144,15 @@ GridView { } } + function openContextMenu(modifiers) { + if (control.useCustomContextMenu) { + dirModel.prepareContextMenu() + control.contextMenuRequested() + } else { + dirModel.openContextMenu(null, modifiers) + } + } + function reset() { currentIndex = -1 anchorIndex = 0 @@ -373,7 +384,7 @@ GridView { // 弹出 Item 菜单 if (mouse.buttons & Qt.RightButton) { clearPressState() - dirModel.openContextMenu(null, mouse.modifiers) + control.openContextMenu(mouse.modifiers) mouse.accepted = true } } else { @@ -386,7 +397,7 @@ GridView { // 弹出文件夹菜单 if (mouse.buttons & Qt.RightButton) { clearPressState() - dirModel.openContextMenu(null, mouse.modifiers) + control.openContextMenu(mouse.modifiers) mouse.accepted = true } } diff --git a/qml/FolderListView.qml b/qml/FolderListView.qml index 25a6882..c6fbabe 100644 --- a/qml/FolderListView.qml +++ b/qml/FolderListView.qml @@ -46,12 +46,14 @@ ListView { property variant cPress: null property Item editor: null property int anchorIndex: 0 + property bool useCustomContextMenu: false property var itemHeight: FishUI.Units.fontMetrics.height * 2 + FishUI.Units.smallSpacing property variant cachedRectangleSelection: null signal keyPress(var event) + signal contextMenuRequested() clip: true cacheBuffer: width @@ -88,6 +90,15 @@ ListView { } } + function openContextMenu(modifiers) { + if (control.useCustomContextMenu) { + dirModel.prepareContextMenu() + control.contextMenuRequested() + } else { + dirModel.openContextMenu(null, modifiers) + } + } + function reset() { currentIndex = -1 anchorIndex = 0 @@ -264,7 +275,7 @@ ListView { if (mouse.buttons & Qt.RightButton) { clearPressState() - dirModel.openContextMenu(null, mouse.modifiers) + control.openContextMenu(mouse.modifiers) mouse.accepted = true } } else { @@ -288,7 +299,7 @@ ListView { if (mouse.buttons & Qt.RightButton) { clearPressState() - dirModel.openContextMenu(null, mouse.modifiers) + control.openContextMenu(mouse.modifiers) mouse.accepted = true } } diff --git a/qml/FolderPage.qml b/qml/FolderPage.qml index d3cfdf7..7c66f3b 100644 --- a/qml/FolderPage.qml +++ b/qml/FolderPage.qml @@ -193,18 +193,9 @@ Item { _viewLoader.item.contentWidth, _viewLoader.item.contentHeight) } - FishUI.DesktopMenu { + FolderContextMenu { id: folderMenu - - MenuItem { - text: qsTr("Open") - onTriggered: dirModel.openSelected() - } - - MenuItem { - text: qsTr("Properties") - onTriggered: dirModel.openPropertiesDialog() - } + folderModel: dirModel } ColumnLayout { @@ -323,6 +314,9 @@ Item { id: _gridView model: dirModel delegate: FolderGridItem {} + useCustomContextMenu: true + + onContextMenuRequested: folderMenu.popup() leftMargin: FishUI.Units.smallSpacing rightMargin: FishUI.Units.largeSpacing @@ -346,6 +340,9 @@ Item { FolderListView { id: _folderListView model: dirModel + useCustomContextMenu: true + + onContextMenuRequested: folderMenu.popup() topMargin: FishUI.Units.smallSpacing leftMargin: FishUI.Units.largeSpacing diff --git a/qml/OptionsMenu.qml b/qml/OptionsMenu.qml index 5e63f34..71b875f 100644 --- a/qml/OptionsMenu.qml +++ b/qml/OptionsMenu.qml @@ -25,7 +25,7 @@ import FishUI 1.0 as FishUI FishUI.DesktopMenu { id: control - MenuItem { + FishUI.MenuItem { Layout.fillWidth: true Image { @@ -63,7 +63,7 @@ FishUI.DesktopMenu { onTriggered: settings.viewMethod = 1 } - MenuItem { + FishUI.MenuItem { Layout.fillWidth: true Image { @@ -101,11 +101,11 @@ FishUI.DesktopMenu { onTriggered: settings.viewMethod = 0 } - MenuSeparator { + FishUI.MenuSeparator { Layout.fillWidth: true } - MenuItem { + FishUI.MenuItem { Layout.fillWidth: true // Image { @@ -143,7 +143,7 @@ FishUI.DesktopMenu { onTriggered: settings.sortMode = 0 } - MenuItem { + FishUI.MenuItem { Layout.fillWidth: true // Image { @@ -181,7 +181,7 @@ FishUI.DesktopMenu { onTriggered: settings.sortMode = 2 } - MenuItem { + FishUI.MenuItem { Text { anchors.left: parent.left anchors.verticalCenter: parent.verticalCenter @@ -205,7 +205,7 @@ FishUI.DesktopMenu { onTriggered: settings.sortMode = 6 } - MenuItem { + FishUI.MenuItem { Layout.fillWidth: true // Image { diff --git a/qml/SideBar.qml b/qml/SideBar.qml index c374e5d..a5ce6e3 100644 --- a/qml/SideBar.qml +++ b/qml/SideBar.qml @@ -116,7 +116,7 @@ ListView { FishUI.DesktopMenu { id: _menu - MenuItem { + FishUI.MenuItem { text: qsTr("Open") onTriggered: { @@ -127,7 +127,7 @@ ListView { } } - MenuItem { + FishUI.MenuItem { text: qsTr("Open in new window") onTriggered: { @@ -135,12 +135,12 @@ ListView { } } - MenuSeparator { + FishUI.MenuSeparator { Layout.fillWidth: true visible: _ejectMenuItem.visible || _umountMenuItem.visible } - MenuItem { + FishUI.MenuItem { id: _ejectMenuItem text: qsTr("Eject") visible: model.isDevice && @@ -153,7 +153,7 @@ ListView { } } - MenuItem { + FishUI.MenuItem { id: _umountMenuItem text: qsTr("Unmount") visible: model.isDevice &&