From 9117b108be3181cab7134209dfe55566a7a96957 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Wed, 29 Jul 2026 17:29:27 +0800 Subject: [PATCH 1/9] fix(activity): Add ActivityFileMenu Signed-off-by: Claudio Cambra --- resources.qrc | 1 + src/gui/activity/qml/ActivityFileMenu.qml | 50 +++++++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 src/gui/activity/qml/ActivityFileMenu.qml diff --git a/resources.qrc b/resources.qrc index a9e58cda6e896..2e61ab5fc39b0 100644 --- a/resources.qrc +++ b/resources.qrc @@ -40,6 +40,7 @@ src/gui/tray/TrayWindowHeader.qml src/gui/activity/qml/ActivityItemContextMenu.qml src/gui/activity/qml/ActivityItemActions.qml + src/gui/activity/qml/ActivityFileMenu.qml src/gui/activity/qml/ActivityItemContent.qml src/gui/activity/qml/TalkReplyTextField.qml src/gui/tray/CallNotificationDialog.qml diff --git a/src/gui/activity/qml/ActivityFileMenu.qml b/src/gui/activity/qml/ActivityFileMenu.qml new file mode 100644 index 0000000000000..d87a31b447a0a --- /dev/null +++ b/src/gui/activity/qml/ActivityFileMenu.qml @@ -0,0 +1,50 @@ +/* + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +import QtQuick +import QtQuick.Controls +import "../../tray" + +AutoSizingMenu { + id: root + + required property string filePath + required property bool serverHasIntegration + required property int itemFontPixelSize + + signal fileDetailsRequested(string filePath) + signal fileActionsRequested(string filePath) + + closePolicy: Menu.CloseOnPressOutsideParent | Menu.CloseOnEscape + height: implicitHeight + + Action { + id: fileDetailsAction + objectName: "fileDetailsAction" + text: qsTr("File details") + onTriggered: root.fileDetailsRequested(root.filePath) + } + + Action { + id: fileActionsAction + objectName: "fileActionsAction" + text: qsTr("File actions") + onTriggered: root.fileActionsRequested(root.filePath) + } + + MenuItem { + action: fileDetailsAction + font.pixelSize: root.itemFontPixelSize + hoverEnabled: true + } + + MenuItem { + action: fileActionsAction + visible: root.serverHasIntegration + height: visible ? implicitHeight : 0 + font.pixelSize: root.itemFontPixelSize + hoverEnabled: true + } +} From 6464fde4d9c721706da68c9cc97de2ea79c07c54 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Wed, 29 Jul 2026 17:30:01 +0800 Subject: [PATCH 2/9] fix(activity): Use ActivityFileMenu within ActivityItemContent Signed-off-by: Claudio Cambra --- src/gui/activity/qml/ActivityItemContent.qml | 27 ++++++-------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/src/gui/activity/qml/ActivityItemContent.qml b/src/gui/activity/qml/ActivityItemContent.qml index 822259d97d734..33e2944989aed 100644 --- a/src/gui/activity/qml/ActivityItemContent.qml +++ b/src/gui/activity/qml/ActivityItemContent.qml @@ -191,26 +191,15 @@ RowLayout { visible: model.showFileDetails onClicked: fileMoreButtonMenu.visible ? fileMoreButtonMenu.close() : fileMoreButtonMenu.popup() - AutoSizingMenu { + ActivityFileMenu { id: fileMoreButtonMenu - closePolicy: Menu.CloseOnPressOutsideParent | Menu.CloseOnEscape - - MenuItem { - height: visible ? implicitHeight : 0 - text: qsTr("File details") - font.pixelSize: Style.topLinePixelSize - hoverEnabled: true - onClicked: Systray.presentShareViewInTray(model.openablePath) - } - - MenuItem { - visible: model.serverHasIntegration - height: visible ? implicitHeight : 0 - text: qsTr("File actions") - font.pixelSize: Style.topLinePixelSize - hoverEnabled: true - onClicked: Systray.presentFileActionsViewInSystray(model.openablePath) - } + + filePath: root.activityData.openablePath + serverHasIntegration: root.activityData.serverHasIntegration + itemFontPixelSize: Style.topLinePixelSize + + onFileDetailsRequested: path => Systray.presentShareViewInTray(path) + onFileActionsRequested: path => Systray.presentFileActionsViewInSystray(path) } } From 1616235531df3a38b0bcb4560178dd665465fa34 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Wed, 29 Jul 2026 17:31:06 +0800 Subject: [PATCH 3/9] chore(activity): Remove unused Style import in AutoSizingMenu Signed-off-by: Claudio Cambra --- src/gui/tray/AutoSizingMenu.qml | 1 - 1 file changed, 1 deletion(-) diff --git a/src/gui/tray/AutoSizingMenu.qml b/src/gui/tray/AutoSizingMenu.qml index e3cc79871d459..835bcaa1bdd57 100644 --- a/src/gui/tray/AutoSizingMenu.qml +++ b/src/gui/tray/AutoSizingMenu.qml @@ -5,7 +5,6 @@ import QtQuick import QtQuick.Controls -import Style Menu { popupType: Popup.Window From 3843cbec8d51dc9701f4e10b2beec7b8c66ac0ae Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Wed, 29 Jul 2026 17:45:22 +0800 Subject: [PATCH 4/9] fix(activity): Add activity file menu test Signed-off-by: Claudio Cambra --- test/CMakeLists.txt | 4 + .../activityfilemenu/testactivityfilemenu.qml | 78 +++++++++++++++++++ 2 files changed, 82 insertions(+) create mode 100644 test/qml/activityfilemenu/testactivityfilemenu.qml diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index cfa3a89a95d0e..6092b21965af3 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -101,6 +101,10 @@ nextcloud_add_test(UnifiedSearchListmodel) nextcloud_add_test(ActivityListModel) nextcloud_add_test(SortedActivityListModel) nextcloud_add_test(ActivityData) +add_test(NAME ActivityFileMenuQmlTest + COMMAND Qt6::qmltestrunner + -input "${CMAKE_CURRENT_SOURCE_DIR}/qml/activityfilemenu/testactivityfilemenu.qml" +) nextcloud_add_test(TalkReply) nextcloud_add_test(LockFile) nextcloud_add_test(ShareModel) diff --git a/test/qml/activityfilemenu/testactivityfilemenu.qml b/test/qml/activityfilemenu/testactivityfilemenu.qml new file mode 100644 index 0000000000000..73cd06d348b86 --- /dev/null +++ b/test/qml/activityfilemenu/testactivityfilemenu.qml @@ -0,0 +1,78 @@ +/* + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +import QtQuick +import QtTest +import "../../../src/gui/activity/qml" + +TestCase { + id: testCase + + name: "ActivityFileMenu" + when: windowShown + + property ActivityFileMenu menu + + SignalSpy { + id: fileDetailsRequestedSpy + signalName: "fileDetailsRequested" + } + + SignalSpy { + id: fileActionsRequestedSpy + signalName: "fileActionsRequested" + } + + Component { + id: menuComponent + + ActivityFileMenu { + filePath: "/sync/folder/file.txt" + serverHasIntegration: true + itemFontPixelSize: 14 + } + } + + function init() + { + menu = createTemporaryObject(menuComponent, testCase); + verify(menu); + + fileDetailsRequestedSpy.target = menu; + fileActionsRequestedSpy.target = menu; + } + + function test_usesFullImplicitHeight() + { + compare(menu.height, menu.implicitHeight); + verify(menu.height > 0); + } + + function test_fileDetailsItemEmitsCapturedPath() + { + menu.popup(); + tryCompare(menu, "opened", true); + + const fileDetailsItem = menu.itemAt(0); + verify(fileDetailsItem); + mouseClick(fileDetailsItem); + + compare(fileDetailsRequestedSpy.count, 1); + compare(fileDetailsRequestedSpy.signalArguments[0][0], "/sync/folder/file.txt"); + } + + function test_fileActionsItemEmitsCapturedPath() + { + menu.popup(); + tryCompare(menu, "opened", true); + + const fileActionsItem = menu.itemAt(1); + verify(fileActionsItem); + mouseClick(fileActionsItem); + + compare(fileActionsRequestedSpy.count, 1); + compare(fileActionsRequestedSpy.signalArguments[0][0], "/sync/folder/file.txt"); + } +} From 42c68b29957c9f864f920fe6d690b860b620bb4a Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Wed, 29 Jul 2026 17:54:32 +0800 Subject: [PATCH 5/9] fix(activity): Add ActivityFileMenuButton Signed-off-by: Claudio Cambra --- resources.qrc | 1 + .../activity/qml/ActivityFileMenuButton.qml | 51 +++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 src/gui/activity/qml/ActivityFileMenuButton.qml diff --git a/resources.qrc b/resources.qrc index 2e61ab5fc39b0..cb4cc6218d7ff 100644 --- a/resources.qrc +++ b/resources.qrc @@ -41,6 +41,7 @@ src/gui/activity/qml/ActivityItemContextMenu.qml src/gui/activity/qml/ActivityItemActions.qml src/gui/activity/qml/ActivityFileMenu.qml + src/gui/activity/qml/ActivityFileMenuButton.qml src/gui/activity/qml/ActivityItemContent.qml src/gui/activity/qml/TalkReplyTextField.qml src/gui/tray/CallNotificationDialog.qml diff --git a/src/gui/activity/qml/ActivityFileMenuButton.qml b/src/gui/activity/qml/ActivityFileMenuButton.qml new file mode 100644 index 0000000000000..5e6d16afd1fef --- /dev/null +++ b/src/gui/activity/qml/ActivityFileMenuButton.qml @@ -0,0 +1,51 @@ +/* + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +import QtQuick +import QtQuick.Controls + +Button { + id: root + + required property string filePath + required property bool serverHasIntegration + required property int itemFontPixelSize + required property int buttonWidth + required property int buttonHeight + required property int buttonIconSize + + readonly property alias menu: fileMenu + + signal fileDetailsRequested(string filePath) + signal fileActionsRequested(string filePath) + + width: buttonWidth + height: buttonHeight + + icon.name: "view-more-symbolic" + icon.source: "image://svgimage-custom-color/more.svg/" + palette.buttonText + icon.width: buttonIconSize + icon.height: buttonIconSize + + ToolTip { + popupType: Qt.platform.os === "windows" ? Popup.Item : Popup.Native + text: qsTr("Open file details") + visible: parent.hovered + } + + display: Button.IconOnly + onClicked: fileMenu.visible ? fileMenu.close() : fileMenu.popup() + + ActivityFileMenu { + id: fileMenu + + filePath: root.filePath + serverHasIntegration: root.serverHasIntegration + itemFontPixelSize: root.itemFontPixelSize + + onFileDetailsRequested: path => root.fileDetailsRequested(path) + onFileActionsRequested: path => root.fileActionsRequested(path) + } +} From b43cb87a120d1f991f6653dde75e79c442d88265 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Wed, 29 Jul 2026 17:55:36 +0800 Subject: [PATCH 6/9] fix(activity): Use file button in activity item content Signed-off-by: Claudio Cambra --- src/gui/activity/qml/ActivityItemContent.qml | 36 +++++--------------- 1 file changed, 9 insertions(+), 27 deletions(-) diff --git a/src/gui/activity/qml/ActivityItemContent.qml b/src/gui/activity/qml/ActivityItemContent.qml index 33e2944989aed..253c164e7c522 100644 --- a/src/gui/activity/qml/ActivityItemContent.qml +++ b/src/gui/activity/qml/ActivityItemContent.qml @@ -170,37 +170,19 @@ RowLayout { Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter spacing: Style.extraSmallSpacing - Button { + ActivityFileMenuButton { id: fileDetailsButton - width: Style.activityListButtonWidth - height: Style.activityListButtonHeight - - icon.name: 'view-more-symbolic' - icon.source: "image://svgimage-custom-color/more.svg/" + palette.buttonText - icon.width: Style.activityListButtonIconSize - icon.height: Style.activityListButtonIconSize - - ToolTip { - popupType: Qt.platform.os === "windows" ? Popup.Item : Qt.platform.os === "windows" ? Popup.Item : Popup.Native - text: qsTr("Open file details") - visible: parent.hovered - } - - display: Button.IconOnly + buttonWidth: Style.activityListButtonWidth + buttonHeight: Style.activityListButtonHeight + buttonIconSize: Style.activityListButtonIconSize + itemFontPixelSize: Style.topLinePixelSize + filePath: root.activityData.openablePath + serverHasIntegration: root.activityData.serverHasIntegration visible: model.showFileDetails - onClicked: fileMoreButtonMenu.visible ? fileMoreButtonMenu.close() : fileMoreButtonMenu.popup() - - ActivityFileMenu { - id: fileMoreButtonMenu - filePath: root.activityData.openablePath - serverHasIntegration: root.activityData.serverHasIntegration - itemFontPixelSize: Style.topLinePixelSize - - onFileDetailsRequested: path => Systray.presentShareViewInTray(path) - onFileActionsRequested: path => Systray.presentFileActionsViewInSystray(path) - } + onFileDetailsRequested: path => Systray.presentShareViewInTray(path) + onFileActionsRequested: path => Systray.presentFileActionsViewInSystray(path) } Button { From 4e5a07c9bf736908782ffe06593c4cccf9a581e7 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Wed, 29 Jul 2026 18:15:03 +0800 Subject: [PATCH 7/9] fix(activity): Include button in UI test Signed-off-by: Claudio Cambra --- .../activityfilemenu/testactivityfilemenu.qml | 123 ++++++++++-------- 1 file changed, 72 insertions(+), 51 deletions(-) diff --git a/test/qml/activityfilemenu/testactivityfilemenu.qml b/test/qml/activityfilemenu/testactivityfilemenu.qml index 73cd06d348b86..6274c0106c2bc 100644 --- a/test/qml/activityfilemenu/testactivityfilemenu.qml +++ b/test/qml/activityfilemenu/testactivityfilemenu.qml @@ -7,72 +7,93 @@ import QtQuick import QtTest import "../../../src/gui/activity/qml" -TestCase { - id: testCase +Item { + id: testRoot - name: "ActivityFileMenu" - when: windowShown + width: 200 + height: 100 - property ActivityFileMenu menu + TestCase { + id: testCase - SignalSpy { - id: fileDetailsRequestedSpy - signalName: "fileDetailsRequested" - } + name: "ActivityFileMenu" + when: windowShown - SignalSpy { - id: fileActionsRequestedSpy - signalName: "fileActionsRequested" - } + property ActivityFileMenuButton button - Component { - id: menuComponent + SignalSpy { + id: fileDetailsRequestedSpy + signalName: "fileDetailsRequested" + } - ActivityFileMenu { - filePath: "/sync/folder/file.txt" - serverHasIntegration: true - itemFontPixelSize: 14 + SignalSpy { + id: fileActionsRequestedSpy + signalName: "fileActionsRequested" } - } - function init() - { - menu = createTemporaryObject(menuComponent, testCase); - verify(menu); + Component { + id: buttonComponent + + ActivityFileMenuButton { + filePath: "/sync/folder/file.txt" + serverHasIntegration: true + itemFontPixelSize: 14 + buttonWidth: 44 + buttonHeight: 32 + buttonIconSize: 16 + } + } - fileDetailsRequestedSpy.target = menu; - fileActionsRequestedSpy.target = menu; - } + function init() + { + button = createTemporaryObject(buttonComponent, testRoot); + verify(button); - function test_usesFullImplicitHeight() - { - compare(menu.height, menu.implicitHeight); - verify(menu.height > 0); - } + fileDetailsRequestedSpy.target = button; + fileActionsRequestedSpy.target = button; + } - function test_fileDetailsItemEmitsCapturedPath() - { - menu.popup(); - tryCompare(menu, "opened", true); + function test_usesFullImplicitHeight() + { + compare(button.menu.height, button.menu.implicitHeight); + verify(button.menu.height > 0); + } - const fileDetailsItem = menu.itemAt(0); - verify(fileDetailsItem); - mouseClick(fileDetailsItem); + function openMenu() + { + mouseClick(button); + tryCompare(button.menu, "opened", true); + } - compare(fileDetailsRequestedSpy.count, 1); - compare(fileDetailsRequestedSpy.signalArguments[0][0], "/sync/folder/file.txt"); - } + function test_buttonOpensMenu() + { + verify(!button.menu.opened); + openMenu(); + verify(button.menu.opened); + } + + function test_fileDetailsItemEmitsCapturedPath() + { + openMenu(); - function test_fileActionsItemEmitsCapturedPath() - { - menu.popup(); - tryCompare(menu, "opened", true); + const fileDetailsItem = button.menu.itemAt(0); + verify(fileDetailsItem); + mouseClick(fileDetailsItem); - const fileActionsItem = menu.itemAt(1); - verify(fileActionsItem); - mouseClick(fileActionsItem); + compare(fileDetailsRequestedSpy.count, 1); + compare(fileDetailsRequestedSpy.signalArguments[0][0], "/sync/folder/file.txt"); + } + + function test_fileActionsItemEmitsCapturedPath() + { + openMenu(); - compare(fileActionsRequestedSpy.count, 1); - compare(fileActionsRequestedSpy.signalArguments[0][0], "/sync/folder/file.txt"); + const fileActionsItem = button.menu.itemAt(1); + verify(fileActionsItem); + mouseClick(fileActionsItem); + + compare(fileActionsRequestedSpy.count, 1); + compare(fileActionsRequestedSpy.signalArguments[0][0], "/sync/folder/file.txt"); + } } } From a6dadc1d077c35b9054219e28552c2f832b1361a Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Wed, 29 Jul 2026 19:21:26 +0800 Subject: [PATCH 8/9] fix(activity): Fix activity file menu sizing Signed-off-by: Claudio Cambra --- src/gui/activity/qml/ActivityFileMenu.qml | 18 +++++++----------- .../activityfilemenu/testactivityfilemenu.qml | 10 +++++++--- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/gui/activity/qml/ActivityFileMenu.qml b/src/gui/activity/qml/ActivityFileMenu.qml index d87a31b447a0a..465648d92e05b 100644 --- a/src/gui/activity/qml/ActivityFileMenu.qml +++ b/src/gui/activity/qml/ActivityFileMenu.qml @@ -7,7 +7,7 @@ import QtQuick import QtQuick.Controls import "../../tray" -AutoSizingMenu { +Menu { id: root required property string filePath @@ -18,16 +18,15 @@ AutoSizingMenu { signal fileActionsRequested(string filePath) closePolicy: Menu.CloseOnPressOutsideParent | Menu.CloseOnEscape - height: implicitHeight - Action { + property Action fileDetailsAction: Action { id: fileDetailsAction objectName: "fileDetailsAction" text: qsTr("File details") onTriggered: root.fileDetailsRequested(root.filePath) } - Action { + property Action fileActionsAction: Action { id: fileActionsAction objectName: "fileActionsAction" text: qsTr("File actions") @@ -35,16 +34,13 @@ AutoSizingMenu { } MenuItem { - action: fileDetailsAction - font.pixelSize: root.itemFontPixelSize - hoverEnabled: true + id: fileDetailsMenuItem + action: root.fileDetailsAction } MenuItem { - action: fileActionsAction + id: fileActionsMenuItem + action: root.fileActionsAction visible: root.serverHasIntegration - height: visible ? implicitHeight : 0 - font.pixelSize: root.itemFontPixelSize - hoverEnabled: true } } diff --git a/test/qml/activityfilemenu/testactivityfilemenu.qml b/test/qml/activityfilemenu/testactivityfilemenu.qml index 6274c0106c2bc..84dc2db2b2770 100644 --- a/test/qml/activityfilemenu/testactivityfilemenu.qml +++ b/test/qml/activityfilemenu/testactivityfilemenu.qml @@ -53,10 +53,14 @@ Item { fileActionsRequestedSpy.target = button; } - function test_usesFullImplicitHeight() + function test_contentFitsWithoutScrolling() { - compare(button.menu.height, button.menu.implicitHeight); - verify(button.menu.height > 0); + button.menu.popup(); + tryCompare(button.menu, "opened", true); + compare(button.menu.count, 2); + const lastItem = button.menu.itemAt(button.menu.count - 1); + verify(lastItem); + verify(button.menu.availableHeight >= lastItem.y + lastItem.height); } function openMenu() From 5cfd3117132eeb5fe23271c1116bfbdd53e1be7e Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Thu, 30 Jul 2026 12:33:57 +0800 Subject: [PATCH 9/9] ci(macOS): checkout like on Windows Signed-off-by: Claudio Cambra --- .github/workflows/macos-build-and-test.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/macos-build-and-test.yml b/.github/workflows/macos-build-and-test.yml index 2005faef02bba..e1ed01a20e01f 100644 --- a/.github/workflows/macos-build-and-test.yml +++ b/.github/workflows/macos-build-and-test.yml @@ -78,6 +78,10 @@ jobs: timeout-minutes: 60 runs-on: macos-26 steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + fetch-depth: 1 + - name: Restore cached Craft directories containing the built client uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: