From 5aa42c3c20337422a966416af5ee9042414fd197 Mon Sep 17 00:00:00 2001 From: memurats Date: Mon, 27 Oct 2025 10:54:50 +0100 Subject: [PATCH 01/13] main window changes --- resources.qrc | 3 + src/gui/CMakeLists.txt | 7 + src/gui/accountsettings.cpp | 31 +- src/gui/nmcgui/NMCHeaderButton.qml | 65 ++++ src/gui/nmcgui/NMCMenuItem.qml | 37 ++ src/gui/nmcgui/qmldir.txt | 3 + src/gui/tray/ActivityItemActions.qml | 20 ++ src/gui/tray/ActivityItemContent.qml | 30 +- src/gui/tray/ActivityList.qml | 5 +- src/gui/tray/CurrentAccountHeaderButton.qml | 359 ++++++++----------- src/gui/tray/MainWindow.qml | 117 ++---- src/gui/tray/NCProgressBar.qml | 10 +- src/gui/tray/SyncStatus.qml | 60 +++- src/gui/tray/TrayWindowHeader.qml | 179 ++++----- src/gui/tray/UnifiedSearchInputContainer.qml | 2 +- src/gui/tray/UserLine.qml | 87 +++-- theme.qrc.in | 8 + theme/NMCIcons/accountAvatarIcon-white.svg | 7 + theme/NMCIcons/logout-white.svg | 7 + theme/NMCIcons/pause-white.svg | 7 + theme/NMCIcons/remove-white.svg | 7 + theme/NMCIcons/website-white.svg | 7 + theme/Style/Style.qml | 52 ++- theme/black/folder.svg | 16 +- theme/close-white.svg | 7 + theme/close.svg | 8 +- theme/more-white.svg | 7 + theme/more.svg | 8 +- theme/settings-white.svg | 7 + theme/settings.svg | 12 +- theme/white/folder.svg | 18 +- 31 files changed, 710 insertions(+), 483 deletions(-) create mode 100644 src/gui/nmcgui/NMCHeaderButton.qml create mode 100644 src/gui/nmcgui/NMCMenuItem.qml create mode 100644 src/gui/nmcgui/qmldir.txt create mode 100644 theme/NMCIcons/accountAvatarIcon-white.svg create mode 100644 theme/NMCIcons/logout-white.svg create mode 100644 theme/NMCIcons/pause-white.svg create mode 100644 theme/NMCIcons/remove-white.svg create mode 100644 theme/NMCIcons/website-white.svg create mode 100644 theme/close-white.svg create mode 100644 theme/more-white.svg create mode 100644 theme/settings-white.svg diff --git a/resources.qrc b/resources.qrc index f26f90192acfe..c43621f8847f1 100644 --- a/resources.qrc +++ b/resources.qrc @@ -1,5 +1,8 @@ + src/gui/nmcgui/qmldir + src/gui/nmcgui/NMCHeaderButton.qml + src/gui/nmcgui/NMCMenuItem.qml src/gui/UserStatusMessageView.qml src/gui/UserStatusSelectorPage.qml src/gui/EmojiPicker.qml diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 736d8bc3cc622..32b5bf7a93b8c 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -6,6 +6,9 @@ find_package(Qt${QT_MAJOR_VERSION} REQUIRED COMPONENTS Widgets Svg Qml Quick Qui find_package(KF6Archive REQUIRED) find_package(KF6GuiAddons) +#NMC customization: needed to find the ui file in a different location than the header file +set(CMAKE_AUTOUIC_SEARCH_PATHS "${CMAKE_SOURCE_DIR}/src/gui") + if(CMAKE_BUILD_TYPE MATCHES Debug) add_definitions(-DQT_QML_DEBUG) endif() @@ -259,6 +262,10 @@ set(client_SRCS integration/fileactionsmodel.cpp ) +file(GLOB NMC_FILES "nmcgui/*") +set(NMC_SRCS ${NMC_FILES}) +list(APPEND client_SRCS ${NMC_SRCS}) + if (NOT DISABLE_ACCOUNT_MIGRATION) list(APPEND client_SRCS legacyaccountselectiondialog.h diff --git a/src/gui/accountsettings.cpp b/src/gui/accountsettings.cpp index fe7c66e315bbc..f409863294775 100644 --- a/src/gui/accountsettings.cpp +++ b/src/gui/accountsettings.cpp @@ -207,6 +207,8 @@ AccountSettings::AccountSettings(AccountState *accountState, QWidget *parent) new ToolTipUpdater(_ui->_folderList); +const auto tabWidget = _ui->tabWidget; + #if defined(BUILD_FILE_PROVIDER_MODULE) if (Mac::FileProvider::available()) { const auto fileProviderPanelContents = _ui->fileProviderPanelContents; @@ -216,21 +218,19 @@ AccountSettings::AccountSettings(AccountState *accountState, QWidget *parent) const auto fpSettingsWidget = fpSettingsController->settingsViewWidget(fpAccountUserIdAtHost, fileProviderPanelContents, QQuickWidget::SizeRootObjectToView); fpSettingsLayout->setContentsMargins(0, 0, 0, 0); - fpSettingsLayout->setSpacing(0); - - fpSettingsWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - if (const auto fpSettingsWidgetLayout = fpSettingsWidget->layout()) { - fpSettingsWidgetLayout->setContentsMargins(0, 0, 0, 0); - } - fpSettingsLayout->addWidget(fpSettingsWidget, 1); - fileProviderPanelContents->setLayout(fpSettingsLayout); + fpSettingsLayout->addWidget(fpSettingsWidget); + fileProviderTab->setLayout(fpSettingsLayout); } else { // macOS 13 Ventura: the file provider feature is unsupported there. // This branch can be removed once Ventura is no longer supported. _ui->fileProviderPanel->setVisible(false); } #else - _ui->fileProviderPanel->setVisible(false); + const auto fileProviderTab = _ui->fileProviderTab; + if (const auto fileProviderWidgetTabIndex = tabWidget->indexOf(fileProviderTab); fileProviderWidgetTabIndex >= 0) { + tabWidget->removeTab(fileProviderWidgetTabIndex); + } + tabWidget->setCurrentIndex(0); #endif const auto connectionSettingsPanelContents = _ui->connectionSettingsPanelContents; @@ -240,10 +240,15 @@ AccountSettings::AccountSettings(AccountState *accountState, QWidget *parent) networkSettingsLayout->setContentsMargins(0, 0, 0, 0); } connectionSettingsLayout->setContentsMargins(0, 0, 0, 0); - connectionSettingsLayout->setSpacing(0); - connectionSettingsLayout->addWidget(networkSettings, 1); - connectionSettingsPanelContents->setLayout(connectionSettingsLayout); - + connectionSettingsLayout->addWidget(networkSettings); + connectionSettingsTab->setLayout(connectionSettingsLayout); + + if (const auto connectionSettingsTabIndex = tabWidget->indexOf(connectionSettingsTab); connectionSettingsTabIndex >= 0) { + tabWidget->removeTab(connectionSettingsTabIndex); + } + tabWidget->setCurrentIndex(0); + tabWidget->tabBar()->hide(); + const auto mouseCursorChanger = new MouseCursorChanger(this); mouseCursorChanger->folderList = _ui->_folderList; mouseCursorChanger->model = _model; diff --git a/src/gui/nmcgui/NMCHeaderButton.qml b/src/gui/nmcgui/NMCHeaderButton.qml new file mode 100644 index 0000000000000..560019eb014a2 --- /dev/null +++ b/src/gui/nmcgui/NMCHeaderButton.qml @@ -0,0 +1,65 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +import Style +import com.nextcloud.desktopclient + +Item { + id: rec + + width: 92 + height: Style.nmcTrayWindowHeaderHeight + + signal clickedButton + + property string iconText: "" + property string iconSource: "" + property bool iconHovered: false + + ColumnLayout { + spacing: 0 + anchors.centerIn: parent + + Button { + id: button + flat: true + focusPolicy: Qt.NoFocus + Layout.alignment: Qt.AlignHCenter + + contentItem: Image { + source: rec.iconSource + width: Style.nmcTrayWindowIconWidth + height: Style.nmcTrayWindowIconWidth + fillMode: Image.PreserveAspectFit + anchors.centerIn: parent + } + + background: Rectangle { + color: rec.iconHovered || button.visualFocus ? "black" : "transparent" + opacity: 0.05 + radius: 4 + } + + MouseArea { + id: buttonArea + anchors.fill: parent + onClicked: rec.clickedButton() // Trigger the button click signal + } + + // Optional: Handle hover on icon to change its state + onClicked: rec.clickedButton() + } + + Text { + width: rec.width + text: rec.iconText + elide: Text.ElideRight + color: Style.nmcTrayWindowHeaderTextColor + font.pixelSize: Style.nmcFontSizeIconText + horizontalAlignment: Text.AlignHCenter + leftPadding: 8 + rightPadding: 8 + } + } +} diff --git a/src/gui/nmcgui/NMCMenuItem.qml b/src/gui/nmcgui/NMCMenuItem.qml new file mode 100644 index 0000000000000..76c18dbb53992 --- /dev/null +++ b/src/gui/nmcgui/NMCMenuItem.qml @@ -0,0 +1,37 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +import Style + +MenuItem { + id: root + + contentItem: RowLayout { + spacing: 8 + anchors.fill: parent + anchors.leftMargin: 12 + + Image { + source: root.icon.source + visible: root.icon.source !== "" + width: Style.nmcTrayWindowIconWidth + height: Style.nmcTrayWindowIconWidth + fillMode: Image.PreserveAspectFit + } + + Text { + text: root.text + color: hovered ? Style.nmcTrayWindowHeaderTextColor : Style.nmcTrayWindowHeaderTextColor + font.pixelSize: Style.topLinePixelSize + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignLeft + elide: Text.ElideRight + Layout.fillWidth: true + } + } + + background: Rectangle { + color: root.hovered ? Style.nmcTrayWindowHeaderHighlightColor : "transparent" + } +} diff --git a/src/gui/nmcgui/qmldir.txt b/src/gui/nmcgui/qmldir.txt new file mode 100644 index 0000000000000..9bc50ef6383cc --- /dev/null +++ b/src/gui/nmcgui/qmldir.txt @@ -0,0 +1,3 @@ +module NMCGui +NMCHeaderButton 1.0 NMCHeaderButton.qml +NMCMenuItem 1.0 NMCMenuItem.qml \ No newline at end of file diff --git a/src/gui/tray/ActivityItemActions.qml b/src/gui/tray/ActivityItemActions.qml index 3997f9250f6e8..7ddaad27e2ee1 100644 --- a/src/gui/tray/ActivityItemActions.qml +++ b/src/gui/tray/ActivityItemActions.qml @@ -50,5 +50,25 @@ Repeater { onClicked: isTalkReplyButton ? root.showReplyField() : root.triggerAction(model.index) visible: verb !== "REPLY" || (verb === "REPLY" && root.talkReplyButtonVisible) + + HoverHandler { + id: mouse + acceptedDevices: PointerDevice.AllPointerTypes + } + + background: Rectangle { + color: mouse.hovered ? Style.nmcConflictHoverColor : Style.nmcConflictColor + radius: Style.nmcStandardRadius + height: Style.nmcTraySyncButtonHeight + } + + contentItem: Text { + text: activityActionButton.text + color: mouse.hovered ? Style.nmcTextInButtonColor : Style.nmcTextInButtonColor + font.pixelSize: Style.fontSizeSmall + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } } } diff --git a/src/gui/tray/ActivityItemContent.qml b/src/gui/tray/ActivityItemContent.qml index f1b09c765e0ef..b22f457684b50 100644 --- a/src/gui/tray/ActivityItemContent.qml +++ b/src/gui/tray/ActivityItemContent.qml @@ -33,8 +33,8 @@ RowLayout { Item { id: thumbnailItem - readonly property int imageWidth: width * (1 - Style.thumbnailImageSizeReduction) - readonly property int imageHeight: height * (1 - Style.thumbnailImageSizeReduction) + readonly property int imageWidth: width + readonly property int imageHeight: height readonly property int thumbnailRadius: model.thumbnail && model.thumbnail.isUserAvatar ? width / 2 : 3 implicitWidth: root.iconSize @@ -184,30 +184,8 @@ RowLayout { } display: Button.IconOnly - visible: model.showFileDetails - onClicked: fileMoreButtonMenu.visible ? fileMoreButtonMenu.close() : fileMoreButtonMenu.popup() - - AutoSizingMenu { - 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) - } - } + visible: false + onClicked: Systray.presentShareViewInTray(model.openablePath) } Button { diff --git a/src/gui/tray/ActivityList.qml b/src/gui/tray/ActivityList.qml index e2e43c3492707..5840ceb69f0f0 100644 --- a/src/gui/tray/ActivityList.qml +++ b/src/gui/tray/ActivityList.qml @@ -98,7 +98,8 @@ ScrollView { width: activityList.contentItem.width isFileActivityList: controlRoot.isFileActivityList - iconSize: controlRoot.iconSize + iconSize: Style.nmcListViewIconSize + leftPadding: Style.nmcListViewLeftPadding flickable: activityList onHoveredChanged: if (hovered) { // When we set the currentIndex the list view will scroll... @@ -150,7 +151,7 @@ ScrollView { Column { id: placeholderColumn - width: parent.width * 0.8 + width: parent.width * 0.75 anchors.centerIn: parent visible: activityList.count === 0 spacing: Style.standardSpacing diff --git a/src/gui/tray/CurrentAccountHeaderButton.qml b/src/gui/tray/CurrentAccountHeaderButton.qml index c296c8fbab0d4..f278a2432a039 100644 --- a/src/gui/tray/CurrentAccountHeaderButton.qml +++ b/src/gui/tray/CurrentAccountHeaderButton.qml @@ -9,6 +9,7 @@ import QtQuick.Layouts import "../" import "../filedetails/" +import "qrc:/qml/NMCGui" import Style import com.nextcloud.desktopclient @@ -23,18 +24,20 @@ Button { display: AbstractButton.IconOnly flat: true + hoverEnabled: true + + background: Rectangle { + color: root.hovered ? Style.nmcTrayWindowHeaderHighlightColor : "transparent" + radius: 4 + } + + Layout.preferredWidth: Style.nmcCurrentAccountButtonWidth + Layout.preferredHeight: Style.nmcTrayWindowHeaderHeight Accessible.role: Accessible.ButtonMenu Accessible.name: qsTr("Current account") Accessible.onPressAction: root.clicked() - palette { - text: Style.currentUserHeaderTextColor - windowText: Style.currentUserHeaderTextColor - buttonText: Style.currentUserHeaderTextColor - button: Style.adjustedCurrentUserHeaderColor - } - // We call open() instead of popup() because we want to position it // exactly below the dropdown button, not the mouse onClicked: { @@ -51,18 +54,15 @@ Button { // x coordinate grows towards the right // y coordinate grows towards the bottom - x: (root.x + 2) - y: (root.y + Style.trayWindowHeaderHeight + 2) + x: (0 - Style.nmcTrayWindowLogoWidth) + y: (root.y + Style.nmcTrayWindowHeaderHeight - Style.nmcTrayWindowMenuOverlayMargin) - property real widestMenuItemWidth: 0 - property real maximumWidthAllowed: trayWindowHeader.width - (root.x + 4) - property real extraMenuWidth: Style.userLineSpacing - width: Math.min(widestMenuItemWidth + leftPadding + rightPadding + extraMenuWidth, maximumWidthAllowed) + width: (Style.nmcCurrentAccountButtonWidth + Style.nmcTrayWindowLogoWidth + 64) height: Math.min(implicitHeight, maxMenuHeight) closePolicy: Menu.CloseOnPressOutsideParent | Menu.CloseOnEscape onClosed: { - // HACK: reload account Instantiator immediately by resetting it - could be done better I guess + // HACK: reload account Instantiator immediately by restting it - could be done better I guess // see also onVisibleChanged above userLineInstantiator.active = false; userLineInstantiator.active = true; @@ -75,175 +75,106 @@ Button { implicitHeight: instantiatedUserLine.height UserLine { id: instantiatedUserLine - width: Math.min(accountMenu.widestMenuItemWidth, accountMenu.maximumWidthAllowed) - parentBackgroundColor: Style.colorWithoutTransparency(accountMenu.palette.window) - - Component.onCompleted: { - instantiatedUserLine.updateMenuWidth() - } - - onImplicitWidthChanged: { - instantiatedUserLine.updateMenuWidth() - } - - Connections { - target: model - function onNameChanged() { - instantiatedUserLine.updateMenuWidth() - } - - function onStatusChanged() { - instantiatedUserLine.updateMenuWidth() - } - } - + width: parent.width onShowUserStatusSelector: { userStatusDrawer.openUserStatusDrawer(model.index); accountMenu.close(); } - onShowUserStatusMessageSelector: { - userStatusDrawer.openUserStatusMessageDrawer(model.index); - accountMenu.close(); - } onClicked: UserModel.currentUserId = model.index; - - function updateMenuWidth() - { - accountMenu.widestMenuItemWidth = Math.max( instantiatedUserLine.implicitWidth, accountMenu.widestMenuItemWidth ) - } } } - onObjectAdded: function(index, object) { - accountMenu.insertItem(index, object) - } - onObjectRemoved: function(index, object) { - accountMenu.removeItem(object) - } + onObjectAdded: accountMenu.insertItem(index, object) + onObjectRemoved: accountMenu.removeItem(object) } - MenuItem { - id: addAccountButton - hoverEnabled: true - visible: Systray.enableAddAccount - implicitHeight: Style.trayWindowHeaderHeight - - readonly property real addAccountIconSize: Style.accountAvatarSize * Style.smallIconScaleFactor - readonly property real addAccountHorizontalOffset: ( (Style.accountAvatarSize - addAccountIconSize) / 2 ) + Style.accountIconsMenuMargin - property var iconColor: !addAccountButton.enabled - ? addAccountButton.palette.mid - : ((addAccountButton.highlighted || addAccountButton.down) && Qt.platform.os !== "windows" - ? addAccountButton.palette.highlightedText - : addAccountButton.palette.text) - - icon.source: "image://svgimage-custom-color/add.svg/" + iconColor - icon.width: addAccountIconSize - icon.height: addAccountIconSize - leftPadding: addAccountHorizontalOffset - spacing: Style.userLineSpacing - text: qsTr("Add account") - onClicked: UserModel.addAccount() - - Accessible.role: Accessible.MenuItem - Accessible.name: qsTr("Add new account") - Accessible.onPressAction: addAccountButton.clicked() + MenuSeparator { + padding: 0 + topPadding: 6 + bottomPadding: 6 + contentItem: Rectangle { + implicitHeight: 1 + color: Style.nmcTrayWindowHeaderSeparatorColor + } } - MenuSeparator {} - - MenuItem { + NMCMenuItem { id: syncPauseButton - height: Systray.anySyncFolders ? implicitHeight : 0 - font.pixelSize: Style.topLinePixelSize - hoverEnabled: true + + icon.source: Style.nmcPauseIcon + icon.height: Style.nmcTrayWindowIconWidth + icon.width: Style.nmcTrayWindowIconWidth + leftPadding: Style.nmcMenuSubItemLeftPadding + height: Style.nmcMenuSubItemHeight + enabled: Systray.anySyncFolders visible: Systray.anySyncFolders onClicked: Systray.syncIsPaused = !Systray.syncIsPaused Accessible.role: Accessible.MenuItem Accessible.name: Systray.syncIsPaused ? qsTr("Resume sync for all") : qsTr("Pause sync for all") Accessible.onPressAction: syncPauseButton.clicked() - - contentItem: Text { - text: parent.text - horizontalAlignment: Text.AlignLeft - verticalAlignment: Text.AlignVCenter - leftPadding: Style.userLineSpacing - elide: Text.ElideRight - color: !parent.enabled - ? parent.palette.mid - : ((parent.highlighted || parent.down) && Qt.platform.os !== "windows" - ? parent.palette.highlightedText - : parent.palette.text) - } } - MenuItem { + NMCMenuItem { id: settingsButton text: qsTr("Settings") - font.pixelSize: Style.topLinePixelSize - hoverEnabled: true + + icon.source: Style.nmcSettingsIcon + icon.height: Style.nmcTrayWindowIconWidth + icon.width: Style.nmcTrayWindowIconWidth + leftPadding: Style.nmcMenuSubItemLeftPadding + height: Style.nmcMenuSubItemHeight + onClicked: Systray.openSettings() Accessible.role: Accessible.MenuItem Accessible.name: text Accessible.onPressAction: settingsButton.clicked() - - contentItem: Text { - text: parent.text - horizontalAlignment: Text.AlignLeft - verticalAlignment: Text.AlignVCenter - leftPadding: Style.userLineSpacing - elide: Text.ElideRight - color: !parent.enabled - ? parent.palette.mid - : ((parent.highlighted || parent.down) && Qt.platform.os !== "windows" - ? parent.palette.highlightedText - : parent.palette.text) - } } - MenuItem { + NMCMenuItem { id: exitButton text: qsTr("Exit"); - font.pixelSize: Style.topLinePixelSize - hoverEnabled: true + + icon.source: Style.nmcCloseIcon + icon.height: Style.nmcTrayWindowIconWidth + icon.width: Style.nmcTrayWindowIconWidth + leftPadding: Style.nmcMenuSubItemLeftPadding + height: Style.nmcMenuSubItemHeight + onClicked: Systray.shutdown() Accessible.role: Accessible.MenuItem Accessible.name: text - Accessible.onPressAction: exitButton.clicked() - - contentItem: Text { - text: parent.text - horizontalAlignment: Text.AlignLeft - verticalAlignment: Text.AlignVCenter - leftPadding: Style.userLineSpacing - elide: Text.ElideRight - color: !parent.enabled - ? parent.palette.mid - : ((parent.highlighted || parent.down) && Qt.platform.os !== "windows" - ? parent.palette.highlightedText - : parent.palette.text) - } + Accessible.onPressAction: exitButton.clicked() + } + + // NMC customization: spacer at the bottom of the menu + Rectangle { + width: parent.width + height: 8 + color: "transparent" + anchors.horizontalCenter: parent.horizontalCenter } } - RowLayout { - id: accountControlRowLayout + Item { + anchors.fill: parent - height: Style.trayWindowHeaderHeight - width: Style.rootWidth - spacing: 0 + RowLayout { + id: accountControlRowLayout - Image { - id: currentAccountAvatar + anchors.verticalCenter: parent.verticalCenter + width: Style.nmcCurrentAccountButtonWidth + spacing: 0 - Layout.leftMargin: Style.trayHorizontalMargin - verticalAlignment: Qt.AlignCenter - cache: false - source: (UserModel.currentUser && UserModel.currentUser.avatar !== "") ? UserModel.currentUser.avatar : "image://avatars/fallbackWhite" - Layout.preferredHeight: Style.accountAvatarSize - Layout.preferredWidth: Style.accountAvatarSize + Image { + id: currentAccountAvatar - Accessible.role: Accessible.Graphic - Accessible.name: qsTr("Current account avatar") + Layout.leftMargin: Style.trayHorizontalMargin + verticalAlignment: Qt.AlignCenter + cache: false + source: Style.nmcAccountAvatarIcon + + Accessible.role: Accessible.Graphic + Accessible.name: qsTr("Current account avatar") Rectangle { id: currentAccountStatusIndicatorBackground @@ -253,7 +184,7 @@ Button { && UserModel.currentUser.status !== NC.userStatus.Offline width: Style.accountAvatarStateIndicatorSize + Style.trayFolderStatusIndicatorSizeOffset height: width - color: "white" + color: root.parentBackgroundColor anchors.bottom: currentAccountAvatar.bottom anchors.right: currentAccountAvatar.right radius: width * Style.trayFolderStatusIndicatorRadiusFactor @@ -271,84 +202,88 @@ Button { sourceSize.width: Style.accountAvatarStateIndicatorSize sourceSize.height: Style.accountAvatarStateIndicatorSize - Accessible.role: Accessible.Indicator - Accessible.name: UserModel.desktopNotificationsAllowed ? qsTr("Current account status is online") : qsTr("Current account status is do not disturb") - } - } - - Column { - id: accountLabels - spacing: Style.extraExtraSmallSpacing - Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter - Layout.leftMargin: Style.userStatusSpacing - Layout.fillWidth: true - Layout.maximumWidth: parent.width - - EnforcedPlainTextLabel { - id: currentAccountUser - Layout.alignment: Qt.AlignLeft | Qt.AlignBottom - width: Style.currentAccountLabelWidth - color: Style.currentUserHeaderTextColor - text: UserModel.currentUser ? UserModel.currentUser.name : "" - elide: Text.ElideRight - - font.pixelSize: Style.topLinePixelSize - font.bold: true - } - - EnforcedPlainTextLabel { - id: currentAccountServer - Layout.alignment: Qt.AlignLeft | Qt.AlignTop - width: Style.currentAccountLabelWidth - color: Style.currentUserHeaderTextColor - text: UserModel.currentUser ? UserModel.currentUser.server : "" - font.pixelSize: Style.subLinePixelSize - elide: Text.ElideRight - visible: UserModel.numUsers() > 1 + Accessible.role: Accessible.Indicator + Accessible.name: UserModel.desktopNotificationsAllowed ? qsTr("Current account status is online") : qsTr("Current account status is do not disturb") + } } - RowLayout { - id: currentUserStatus - visible: UserModel.currentUser && UserModel.currentUser.isConnected && - UserModel.currentUser.serverHasUserStatus - width: parent.width + Column { + id: accountLabels + spacing: 0 + Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter + Layout.leftMargin: Style.userStatusSpacing + Layout.fillWidth: true EnforcedPlainTextLabel { - id: emoji - Layout.alignment: Qt.AlignLeft | Qt.AlignTop - visible: UserModel.currentUser && UserModel.currentUser.statusEmoji !== "" - color: Style.currentUserHeaderTextColor - text: UserModel.currentUser ? UserModel.currentUser.statusEmoji : "" - font.pixelSize: Style.subLinePixelSize + id: currentAccountUser + Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter + Layout.fillWidth: true + width: Style.currentAccountLabelWidth + color: Style.nmcTrayWindowHeaderTextColor + text: UserModel.currentUser ? UserModel.currentUser.name : "" + elide: Text.ElideRight + + font.pixelSize: Style.topLinePixelSize + font.bold: false + palette.windowText: Style.nmcTrayWindowHeaderTextColor } + EnforcedPlainTextLabel { - id: message - Layout.alignment: Qt.AlignLeft | Qt.AlignTop - Layout.fillWidth: true - visible: UserModel.currentUser && UserModel.currentUser.statusMessage !== "" + id: currentAccountServer + visible: false + Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter + width: Style.currentAccountLabelWidth color: Style.currentUserHeaderTextColor - text: UserModel.currentUser && UserModel.currentUser.statusMessage !== "" - ? UserModel.currentUser.statusMessage - : UserModel.currentUser ? UserModel.currentUser.server : "" - font.pixelSize: Style.subLinePixelSize + text: UserModel.currentUser ? UserModel.currentUser.server : "" elide: Text.ElideRight } - } - } - Loader { - active: root.indicator === null - sourceComponent: Image { - Layout.alignment: Qt.AlignRight - verticalAlignment: Qt.AlignCenter - horizontalAlignment: Qt.AlignRight - Layout.leftMargin: Style.accountDropDownCaretMargin - source: "image://svgimage-custom-color/caret-down.svg/" + palette.windowText - sourceSize.width: Style.accountDropDownCaretSize - sourceSize.height: Style.accountDropDownCaretSize - Accessible.role: Accessible.PopupMenu - Accessible.name: qsTr("Account switcher and settings menu") + RowLayout { + id: currentUserStatus + visible: UserModel.currentUser && UserModel.currentUser.isConnected && + UserModel.currentUser.serverHasUserStatus + spacing: Style.accountLabelsSpacing + width: parent.width + + EnforcedPlainTextLabel { + id: emoji + visible: UserModel.currentUser && UserModel.currentUser.statusEmoji !== "" + width: Style.userStatusEmojiSize + color: Style.currentUserHeaderTextColor + text: UserModel.currentUser ? UserModel.currentUser.statusEmoji : "" + } + EnforcedPlainTextLabel { + id: message + Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter + Layout.fillWidth: true + visible: UserModel.currentUser && UserModel.currentUser.statusMessage !== "" + width: Style.currentAccountLabelWidth + color: Style.currentUserHeaderTextColor + text: UserModel.currentUser && UserModel.currentUser.statusMessage !== "" + ? UserModel.currentUser.statusMessage + : UserModel.currentUser ? UserModel.currentUser.server : "" + elide: Text.ElideRight + font.pixelSize: Style.subLinePixelSize + } + } } } } -} + + Loader { + id: caretLoader + anchors.verticalCenter: parent.verticalCenter + anchors.right: root.right + anchors.rightMargin: 12 + active: root.indicator === null + + sourceComponent: Image { + source: "image://svgimage-custom-color/caret-down.svg/" + Style.nmcTrayWindowHeaderTextColor + sourceSize.width: Style.accountDropDownCaretSize + sourceSize.height: Style.accountDropDownCaretSize + + Accessible.role: Accessible.PopupMenu + Accessible.name: qsTr("Account switcher and settings menu") + } + } +} \ No newline at end of file diff --git a/src/gui/tray/MainWindow.qml b/src/gui/tray/MainWindow.qml index e0c70e93c7a23..88b19f054f86a 100644 --- a/src/gui/tray/MainWindow.qml +++ b/src/gui/tray/MainWindow.qml @@ -13,6 +13,7 @@ import Qt.labs.platform as NativeDialogs import "../" import "../filedetails/" +import "qrc:/qml/NMCGui" // Custom qml modules are in /theme (and included by resources.qrc) import Style @@ -27,12 +28,12 @@ ApplicationWindow { title: Systray.windowTitle // If the main dialog is displayed as a regular window we want it to be quadratic - width: Systray.useNormalWindow ? Style.trayWindowHeight : Style.trayWindowWidth - height: Style.trayWindowHeight + width: Systray.useNormalWindow ? Style.nmcTrayWindowHeight : Style.nmcTrayWindowWidth + height: Style.nmcTrayWindowHeight flags: Systray.useNormalWindow ? Qt.Window : Qt.Dialog | Qt.FramelessWindowHint color: "transparent" - readonly property int maxMenuHeight: Style.trayWindowHeight - Style.trayWindowHeaderHeight - 2 * Style.trayWindowBorderWidth + readonly property int maxMenuHeight: Style.nmcTrayWindowHeight - Style.trayWindowHeaderHeight - 2 * Style.trayWindowBorderWidth Component.onCompleted: Systray.forceWindowInit(trayWindow) @@ -254,91 +255,24 @@ ApplicationWindow { TrayWindowHeader { id: trayWindowHeader + height: Style.nmcTrayWindowHeaderHeight - anchors.top: parent.top - anchors.left: parent.left - anchors.right: parent.right - height: Style.trayWindowHeaderHeight - - onFeaturedAppButtonClicked: { - if (UserModel.currentUser.isAssistantEnabled) { - trayWindowMainItem.showAssistantPanel = !trayWindowMainItem.showAssistantPanel - if (trayWindowMainItem.showAssistantPanel) { - assistantQuestionInput.forceActiveFocus() - } - } else { - UserModel.openCurrentAccountFeaturedApp() - } + anchors { + top: parent.top + left: parent.left + right: parent.right } } - Button { - id: trayWindowSyncWarning - - readonly property color warningIconColor: Style.errorBoxBackgroundColor - - anchors.top: trayWindowHeader.bottom - anchors.left: trayWindowMainItem.left - anchors.right: trayWindowMainItem.right - anchors.topMargin: Style.trayHorizontalMargin - anchors.leftMargin: Style.trayHorizontalMargin - anchors.rightMargin: Style.trayHorizontalMargin - - visible: UserModel.hasSyncErrors - && !(UserModel.syncErrorUserCount === 1 - && UserModel.firstSyncErrorUserId === UserModel.currentUserId) - && !trayWindowMainItem.isAssistantActive - padding: 0 - background: Rectangle { - radius: Style.slightlyRoundedButtonRadius - color: Qt.rgba(trayWindowSyncWarning.warningIconColor.r, - trayWindowSyncWarning.warningIconColor.g, - trayWindowSyncWarning.warningIconColor.b, - 0.2) - border.width: Style.normalBorderWidth - border.color: Qt.rgba(trayWindowSyncWarning.warningIconColor.r, - trayWindowSyncWarning.warningIconColor.g, - trayWindowSyncWarning.warningIconColor.b, - 0.6) - } - - Accessible.name: syncWarningText.text - Accessible.role: Accessible.Button - - contentItem: RowLayout { - anchors.fill: parent - spacing: 0 - - ColumnLayout { - Layout.fillWidth: true - Layout.alignment: Qt.AlignVCenter - Layout.topMargin: 4 - Layout.leftMargin: Style.trayHorizontalMargin - Layout.rightMargin: Style.trayHorizontalMargin - Layout.bottomMargin: 4 - - EnforcedPlainTextLabel { - id: syncWarningText - - Layout.fillWidth: true - font.pixelSize: Style.topLinePixelSize - font.bold: true - wrapMode: Text.WordWrap - horizontalAlignment: Text.AlignHCenter - text: { - if (UserModel.syncErrorUserCount <= 1) { - return qsTr("Issue with account %1").arg(UserModel.firstSyncErrorUser ? UserModel.firstSyncErrorUser.name : ""); - } - return qsTr("Issues with several accounts"); - } - } - } - } + Rectangle { + id: separator + height: 1 + color: Style.nmcTrayWindowHeaderSeparatorColor - onClicked: { - if (UserModel.firstSyncErrorUserId >= 0) { - UserModel.currentUserId = UserModel.firstSyncErrorUserId - } + anchors { + top: trayWindowHeader.bottom + left: trayWindowMainItem.left + right: trayWindowMainItem.right } } @@ -346,6 +280,7 @@ ApplicationWindow { id: trayWindowUnifiedSearchInputContainer visible: !trayWindowMainItem.showAssistantPanel + visible: false property bool activateSearchFocus: activeFocus anchors.top: trayWindowSyncWarning.visible @@ -677,7 +612,7 @@ ApplicationWindow { Rectangle { id: bottomUnifiedSearchInputSeparator - anchors.top: trayWindowMainItem.showAssistantPanel ? assistantInputContainer.bottom : trayWindowUnifiedSearchInputContainer.bottom + anchors.top: trayWindowUnifiedSearchInputContainer.visible ? trayWindowUnifiedSearchInputContainer.bottom : separator.bottom anchors.left: parent.left anchors.right: parent.right anchors.topMargin: Style.trayHorizontalMargin @@ -691,7 +626,7 @@ ApplicationWindow { id: unifiedSearchResultsErrorLabel visible: UserModel.currentUser.unifiedSearchResultsListModel.errorString && !unifiedSearchResultsListView.visible && ! UserModel.currentUser.unifiedSearchResultsListModel.isSearchInProgress && ! UserModel.currentUser.unifiedSearchResultsListModel.currentFetchMoreInProgressProviderId text: UserModel.currentUser.unifiedSearchResultsListModel.errorString - anchors.top: bottomUnifiedSearchInputSeparator.bottom + anchors.top: trayWindowUnifiedSearchInputContainer.visible ? bottomUnifiedSearchInputSeparator.bottom : separator.bottom anchors.left: trayWindowMainItem.left anchors.right: trayWindowMainItem.right anchors.margins: Style.trayHorizontalMargin @@ -700,7 +635,7 @@ ApplicationWindow { UnifiedSearchPlaceholderView { id: unifiedSearchPlaceholderView - anchors.top: bottomUnifiedSearchInputSeparator.bottom + anchors.top: trayWindowUnifiedSearchInputContainer.visible ? bottomUnifiedSearchInputSeparator.bottom : separator.bottom anchors.left: trayWindowMainItem.left anchors.right: trayWindowMainItem.right anchors.bottom: trayWindowMainItem.bottom @@ -712,7 +647,7 @@ ApplicationWindow { UnifiedSearchResultNothingFound { id: unifiedSearchResultNothingFound - anchors.top: bottomUnifiedSearchInputSeparator.bottom + anchors.top: trayWindowUnifiedSearchInputContainer.visible ? bottomUnifiedSearchInputSeparator.bottom : separator.bottom anchors.left: trayWindowMainItem.left anchors.right: trayWindowMainItem.right anchors.topMargin: Style.trayHorizontalMargin @@ -730,7 +665,7 @@ ApplicationWindow { Loader { id: unifiedSearchResultsListViewSkeletonLoader - anchors.top: bottomUnifiedSearchInputSeparator.bottom + anchors.top: trayWindowUnifiedSearchInputContainer.visible ? bottomUnifiedSearchInputSeparator.bottom : separator.bottom anchors.left: trayWindowMainItem.left anchors.right: trayWindowMainItem.right anchors.bottom: trayWindowMainItem.bottom @@ -759,7 +694,7 @@ ApplicationWindow { } visible: unifiedSearchResultsListView.count > 0 - anchors.top: bottomUnifiedSearchInputSeparator.bottom + anchors.top: trayWindowUnifiedSearchInputContainer.visible ? bottomUnifiedSearchInputSeparator.bottom : separator.bottom anchors.left: trayWindowMainItem.left anchors.right: trayWindowMainItem.right anchors.bottom: trayWindowMainItem.bottom @@ -802,7 +737,7 @@ ApplicationWindow { accentColor: Style.accentColor visible: !trayWindowMainItem.isUnifiedSearchActive && !trayWindowMainItem.showAssistantPanel - anchors.top: trayWindowMainItem.showAssistantPanel ? assistantInputContainer.bottom : trayWindowUnifiedSearchInputContainer.bottom + anchors.top: trayWindowUnifiedSearchInputContainer.visible ? trayWindowUnifiedSearchInputContainer.bottom : separator.bottom anchors.left: trayWindowMainItem.left anchors.right: trayWindowMainItem.right } @@ -878,6 +813,8 @@ ApplicationWindow { anchors.right: trayWindowMainItem.right anchors.bottom: trayWindowMainItem.bottom + ScrollBar.vertical.visible: contentHeight > activityList.height + activeFocusOnTab: true model: activityModel onOpenFile: Qt.openUrlExternally(filePath); diff --git a/src/gui/tray/NCProgressBar.qml b/src/gui/tray/NCProgressBar.qml index 839073da4b084..21cd9a7dffdc6 100644 --- a/src/gui/tray/NCProgressBar.qml +++ b/src/gui/tray/NCProgressBar.qml @@ -16,10 +16,10 @@ BasicControls.ProgressBar { background: Rectangle { implicitWidth: Style.progressBarWidth implicitHeight: Style.progressBarBackgroundHeight - radius: Style.progressBarRadius - color: palette.base + radius: Style.nmcStandardRadius + color: Style.nmcTrayWindowHeaderHighlightColor border.color: palette.dark - border.width: Style.progressBarBackgroundBorderWidth + border.width: 0 } contentItem: Item { @@ -30,9 +30,9 @@ BasicControls.ProgressBar { width: control.visualPosition * parent.width height: parent.height radius: Style.progressBarRadius - color: control.fillColor + color: Style.nmcTelekomMagentaColor border.color: palette.dark - border.width: Style.progressBarContentBorderWidth + border.width: 0 } } } diff --git a/src/gui/tray/SyncStatus.qml b/src/gui/tray/SyncStatus.qml index 8827d8a842991..486dc27523045 100644 --- a/src/gui/tray/SyncStatus.qml +++ b/src/gui/tray/SyncStatus.qml @@ -18,6 +18,8 @@ RowLayout { property color accentColor: Style.ncBlue spacing: Style.trayHorizontalMargin + Layout.alignment: Qt.AlignVCenter + Layout.preferredHeight: Style.nmcTraySyncButtonHeight + 8 NC.SyncStatusSummary { id: syncStatus @@ -31,11 +33,11 @@ RowLayout { Layout.preferredWidth: size Layout.preferredHeight: size - Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter + Layout.alignment: Qt.AlignVCenter Layout.topMargin: Style.trayHorizontalMargin Layout.rightMargin: whiteSpace * (0.5 + Style.thumbnailImageSizeReduction) Layout.bottomMargin: Style.trayHorizontalMargin - Layout.leftMargin: Style.trayHorizontalMargin + (whiteSpace * (0.5 - Style.thumbnailImageSizeReduction)) + Layout.leftMargin: Style.nmcListViewLeftPadding padding: 0 @@ -50,6 +52,7 @@ RowLayout { Layout.topMargin: 8 Layout.rightMargin: Style.trayHorizontalMargin Layout.bottomMargin: 8 + Layout.leftMargin: Style.nmcProgressFieldTextOffset Layout.fillWidth: true Layout.fillHeight: true @@ -60,7 +63,7 @@ RowLayout { text: syncStatus.syncStatusString verticalAlignment: Text.AlignVCenter - font.pixelSize: Style.topLinePixelSize + font.pixelSize: Style.nmcFontSizeSyncText font.bold: true wrapMode: Text.Wrap } @@ -85,7 +88,7 @@ RowLayout { Layout.fillWidth: true text: syncStatus.syncStatusDetailString - visible: syncStatus.syncStatusDetailString !== "" + visible: false font.pixelSize: Style.subLinePixelSize wrapMode: Text.Wrap } @@ -94,6 +97,7 @@ RowLayout { Button { id: syncNowButton + Layout.alignment: Qt.AlignVCenter Layout.rightMargin: Style.trayHorizontalMargin text: qsTr("Sync now") @@ -107,6 +111,27 @@ RowLayout { (Qt.platform.os === "osx" && NC.UserModel.currentUser.hasFileProvider)) && NC.UserModel.currentUser.isConnected enabled: visible + + HoverHandler { + id: mouseSync + acceptedDevices: PointerDevice.AllPointerTypes + } + + background: Rectangle { + color: mouseSync.hovered ? Style.nmcSyncHoverColor : Style.nmcTelekomMagentaColor + radius: Style.nmcStandardRadius + height: Style.nmcTraySyncButtonHeight + } + + contentItem: Text { + text: syncNowButton.text + color: "white" + font.pixelSize: Style.nmcFontSizeAccountName + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + onClicked: { if(!syncStatus.syncing) { NC.UserModel.currentUser.forceSyncNow(); @@ -115,8 +140,11 @@ RowLayout { } Button { + Layout.alignment: Qt.AlignVCenter Layout.rightMargin: Style.trayHorizontalMargin + padding: Style.smallSpacing + text: qsTr("Resolve conflicts") visible: activityModel.hasSyncConflicts && @@ -124,10 +152,34 @@ RowLayout { NC.UserModel.currentUser.hasLocalFolder && NC.UserModel.currentUser.isConnected enabled: visible + + HoverHandler { + id: mouseConflict + acceptedDevices: PointerDevice.Mouse + } + + contentItem: Text { + text: parent.text + color: Style.nmcTextInButtonColor + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + anchors.fill: parent + } + + background: Rectangle { + color: mouseConflict.hovered + ? Style.nmcConflictHoverColor + : Style.nmcConflictColor + radius: Style.nmcStandardRadius + height: Style.nmcTraySyncButtonHeight + width: parent.width + } + onClicked: NC.Systray.createResolveConflictsDialog(activityModel.allConflicts); } Button { + Layout.alignment: Qt.AlignVCenter Layout.rightMargin: Style.trayHorizontalMargin text: qsTr("Open browser") diff --git a/src/gui/tray/TrayWindowHeader.qml b/src/gui/tray/TrayWindowHeader.qml index a7a6aad89cbc3..897e346d3fafa 100644 --- a/src/gui/tray/TrayWindowHeader.qml +++ b/src/gui/tray/TrayWindowHeader.qml @@ -9,6 +9,7 @@ import QtQuick.Layouts import "../" import "../filedetails/" +import "qrc:/qml/NMCGui" import Style import com.nextcloud.desktopclient @@ -19,10 +20,9 @@ Rectangle { signal featuredAppButtonClicked readonly property alias currentAccountHeaderButton: currentAccountHeaderButton - readonly property alias openLocalFolderButton: openLocalFolderButton - readonly property alias appsMenu: appsMenu - color: Style.currentUserHeaderColor + color: Style.nmcTrayWindowHeaderBackgroundColor + height: Style.nmcTrayWindowHeaderHeight palette { text: Style.currentUserHeaderTextColor @@ -31,11 +31,54 @@ Rectangle { button: Style.adjustedCurrentUserHeaderColor } + Rectangle { + id: whiteMargin + width: 10 + height: Style.nmcTrayWindowHeaderHeight + color: Style.nmcTrayWindowHeaderBackgroundColor + + anchors { + top: parent.top + left: parent.left + } + } + + Rectangle { + id: tLogo + width: Style.nmcTrayWindowLogoWidth + height: Style.nmcTrayWindowHeaderHeight + + anchors { + top: parent.top + left: whiteMargin.right + } + + Image { + anchors.fill: parent + source: Style.nmcTLogoPath + fillMode: Image.Stretch + cache: false + } + } + RowLayout { id: trayWindowHeaderLayout spacing: 0 - anchors.fill: parent + height: Style.nmcTrayWindowHeaderHeight + + anchors { + top: parent.top + left: tLogo.right + right: parent.right + } + + Rectangle { + id: whiteMarginLeft1 + Layout.preferredWidth: 10 + Layout.fillHeight: true + color: Style.nmcTrayWindowHeaderBackgroundColor + } CurrentAccountHeaderButton { id: currentAccountHeaderButton @@ -49,101 +92,69 @@ Rectangle { Layout.fillWidth: true } - TrayFoldersMenuButton { - id: openLocalFolderButton - - Layout.alignment: Qt.AlignRight - Layout.preferredWidth: Style.trayWindowHeaderHeight + Rectangle { + id: trayWindowWebsiteButtonContainer + Layout.preferredWidth: 92 Layout.fillHeight: true - - visible: currentUser.hasLocalFolder || (Qt.platform.os === "osx" && currentUser.hasFileProvider) - currentUser: UserModel.currentUser - parentBackgroundColor: root.color - - onClicked: { - if (openLocalFolderButton.userHasGroupFolders) { - openLocalFolderButton.toggleMenuOpen() - } else if (currentUser.hasLocalFolder) { - UserModel.openCurrentAccountLocalFolder() - } else if (Qt.platform.os === "osx") { - UserModel.openCurrentAccountFileProviderDomain() + color: websiteHover.hovered ? Style.nmcTrayWindowHeaderHighlightColor : "transparent" + + NMCHeaderButton { + id: trayWindowWebsiteButton + iconSource: Style.darkMode + ? "qrc:///client/theme/NMCIcons/website-white.svg" + : "qrc:///client/theme/NMCIcons/website.svg" + iconText: qsTranslate("", "OPEN_WEBSITE") + + MouseArea { + anchors.fill: parent + onClicked: UserModel.openCurrentAccountServer() } } - onFolderEntryTriggered: isGroupFolder ? UserModel.openCurrentAccountFolderFromTrayInfo(fullFolderPath) : UserModel.openCurrentAccountLocalFolder() - - Accessible.role: Accessible.Graphic - Accessible.name: qsTr("Open local or team folders") - Accessible.onPressAction: { - if (openLocalFolderButton.userHasGroupFolders) { - openLocalFolderButton.toggleMenuOpen() - } else if (currentUser.hasLocalFolder) { - UserModel.openCurrentAccountLocalFolder() - } else if (Qt.platform.os === "osx") { - UserModel.openCurrentAccountFileProviderDomain() - } + HoverHandler { + id: websiteHover + acceptedDevices: PointerDevice.Mouse } } - HeaderButton { - id: trayWindowFeaturedAppButton - - Layout.alignment: Qt.AlignRight - Layout.preferredWidth: Style.trayWindowHeaderHeight + Rectangle { + id: whiteMarginRight1 + Layout.preferredWidth: 10 Layout.fillHeight: true - visible: UserModel.currentUser.isAssistantEnabled - icon.source: UserModel.currentUser.featuredAppIcon + "/" + palette.windowText - onClicked: root.featuredAppButtonClicked() - - Accessible.role: Accessible.Button - Accessible.name: UserModel.currentUser.featuredAppAccessibleName - Accessible.onPressAction: trayWindowFeaturedAppButton.clicked() + color: Style.nmcTrayWindowHeaderBackgroundColor } - HeaderButton { - id: trayWindowAppsButton - icon.source: "image://svgimage-custom-color/more-apps.svg/" + palette.windowText - - onClicked: { - if(appsMenu.count <= 0) { - UserModel.openCurrentAccountServer() - } else if (appsMenu.visible) { - appsMenu.close() - } else { - appsMenu.open() + Rectangle { + id: trayWindowLocalButtonContainer + Layout.preferredWidth: 92 + Layout.fillHeight: true + color: localHover.hovered ? Style.nmcTrayWindowHeaderHighlightColor : "transparent" + + NMCHeaderButton { + id: trayWindowLocalButton + iconSource: Style.darkMode + ? "qrc:///client/theme/white/folder.svg" + : "qrc:///client/theme/black/folder.svg" + iconText: qsTranslate("", "LOCAL_FOLDER") + + MouseArea { + anchors.fill: parent + onClicked: UserModel.openCurrentAccountLocalFolder() } } - Accessible.role: Accessible.ButtonMenu - Accessible.name: qsTr("More apps") - Accessible.onPressAction: trayWindowAppsButton.clicked() - - Menu { - id: appsMenu - x: Style.trayWindowMenuOffsetX - y: (trayWindowAppsButton.y + trayWindowAppsButton.height + Style.trayWindowMenuOffsetY) - width: Style.trayWindowWidth * Style.trayWindowMenuWidthFactor - height: implicitHeight + y > Style.trayWindowHeight ? Style.trayWindowHeight - y : implicitHeight - closePolicy: Menu.CloseOnPressOutsideParent | Menu.CloseOnEscape - - Repeater { - model: UserAppsModel - delegate: MenuItem { - id: appEntry - // HACK: Without creating our own component (and killing native styling) - // HACK: we do not have a way to adjust the text and icon spacing. - text: " " + model.appName - font.pixelSize: Style.topLinePixelSize - icon.source: "image://tray-image-provider/" + model.appIconUrl - icon.color: palette.windowText - onTriggered: UserAppsModel.openAppUrl(appUrl) - Accessible.role: Accessible.MenuItem - Accessible.name: qsTr("Open %1 in browser").arg(model.appName) - Accessible.onPressAction: appEntry.triggered() - } - } + HoverHandler { + id: localHover + acceptedDevices: PointerDevice.Mouse } } + + Rectangle { + id: whiteMarginRight2 + Layout.preferredWidth: 10 + Layout.fillHeight: true + color: Style.nmcTrayWindowHeaderBackgroundColor + } } } diff --git a/src/gui/tray/UnifiedSearchInputContainer.qml b/src/gui/tray/UnifiedSearchInputContainer.qml index 4fd6ac4f1d927..535026ca9daeb 100644 --- a/src/gui/tray/UnifiedSearchInputContainer.qml +++ b/src/gui/tray/UnifiedSearchInputContainer.qml @@ -28,7 +28,7 @@ TextField { topPadding: topInset bottomPadding: bottomInset leftPadding: iconInset + leadingControlWidth + Style.smallSpacing - rightPadding: iconInset + trailingControlWidth + Style.smallSpacing + rightPadding: clearTextButton.width + iconInset + Style.smallSpacing verticalAlignment: Qt.AlignVCenter placeholderText: qsTr("Search files, messages, events …") diff --git a/src/gui/tray/UserLine.qml b/src/gui/tray/UserLine.qml index 2491fd3c5be79..97f02e7392505 100644 --- a/src/gui/tray/UserLine.qml +++ b/src/gui/tray/UserLine.qml @@ -7,6 +7,7 @@ import QtQuick import QtQuick.Window import QtQuick.Controls import QtQuick.Layouts +import "qrc:/qml/NMCGui" // Custom qml modules are in /theme (and included by resources.qrc) import Style @@ -23,14 +24,15 @@ AbstractButton { Accessible.role: Accessible.MenuItem Accessible.name: qsTr("Switch to account") + " " + model.name - height: Style.trayWindowHeaderHeight + height: Style.nmcMenuSubItemHeight contentItem: RowLayout { id: userLineLayout - spacing: Style.userLineSpacing + spacing: 8 Image { id: accountAvatar + visible: false Layout.leftMargin: Style.accountIconsMenuMargin verticalAlignment: Qt.AlignCenter cache: false @@ -68,21 +70,33 @@ AbstractButton { } } - ColumnLayout { + RowLayout { id: accountLabels Layout.fillWidth: true Layout.fillHeight: true - spacing: Style.extraExtraSmallSpacing + Layout.alignment: Qt.AlignVCenter + spacing: 8 + anchors.leftMargin: 12 + + Image { + id: accountIcon + source: Style.nmcAccountAvatarIcon + visible: true + width: Style.nmcTrayWindowIconWidth + height: Style.nmcTrayWindowIconWidth + fillMode: Image.PreserveAspectFit + Layout.alignment: Qt.AlignVCenter + } EnforcedPlainTextLabel { id: accountUser Layout.fillWidth: true - Layout.alignment: Qt.AlignLeft | Qt.AlignBottom - verticalAlignment: Text.AlignBottom + Layout.alignment: Qt.AlignVCenter + verticalAlignment: Text.AlignVCenter text: name elide: Text.ElideRight font.pixelSize: Style.topLinePixelSize - font.bold: true + font.bold: false color: !userLine.parent.enabled ? userLine.parent.palette.mid @@ -110,7 +124,7 @@ AbstractButton { RowLayout { id: statusLayout Layout.fillWidth: true - height: visible ? implicitHeight : 0 + Layout.preferredHeight: visible ? implicitHeight : 0 visible: model.isConnected && model.serverHasUserStatus && (model.statusEmoji !== "" || model.statusMessage !== "") @@ -142,6 +156,23 @@ AbstractButton { : userLine.parent.palette.text) } } + + EnforcedPlainTextLabel { + id: accountServer + visible: false + Layout.fillWidth: true + Layout.alignment: Qt.AlignLeft | Qt.AlignTop + verticalAlignment: Text.AlignTop + text: server + elide: Text.ElideRight + font.pixelSize: Style.subLinePixelSize + + color: !userLine.parent.enabled + ? userLine.parent.palette.mid + : ((userLine.parent.highlighted || userLine.parent.down) && Qt.platform.os !== "windows" + ? userLine.parent.palette.highlightedText + : userLine.parent.palette.text) + } } Item { // Spacer @@ -169,9 +200,12 @@ AbstractButton { Button { id: userMoreButton - Layout.preferredWidth: Style.headerButtonIconSize + Layout.preferredWidth: Style.iconButtonWidth Layout.fillHeight: true + Layout.rightMargin: Style.accountIconsMenuMargin flat: true + visible: true + opacity: 1 Accessible.role: Accessible.ButtonMenu Accessible.name: qsTr("Account actions") @@ -179,12 +213,9 @@ AbstractButton { onClicked: userMoreButtonMenu.visible ? userMoreButtonMenu.close() : userMoreButtonMenu.popup() - property var iconColor: !userLine.parent.enabled - ? userLine.parent.palette.mid - : (!hovered && ((userLine.parent.highlighted || userLine.parent.down) && Qt.platform.os !== "windows") - ? userLine.parent.palette.highlightedText - : userLine.parent.palette.text) - icon.source: "image://svgimage-custom-color/more.svg/" + iconColor + icon.source: Style.darkMode + ? "qrc:///client/theme/more-white.svg" + : "qrc:///client/theme/more.svg" AutoSizingMenu { id: userMoreButtonMenu @@ -193,6 +224,7 @@ AbstractButton { MenuItem { id: setStatusButton + visible: false enabled: model.isConnected && model.serverHasUserStatus text: qsTr("Set status") font.pixelSize: Style.topLinePixelSize @@ -207,6 +239,7 @@ AbstractButton { MenuItem { id: statusMessageButton + visible: false enabled: model.isConnected && model.serverHasUserStatus text: qsTr("Status message") font.pixelSize: Style.topLinePixelSize @@ -219,13 +252,15 @@ AbstractButton { Accessible.onPressAction: statusMessageButton.clicked() } - MenuItem { + NMCMenuItem { id: logInOutButton enabled: model.canLogout text: model.isConnected ? qsTr("Log out") : qsTr("Log in") - width: parent.width - font.pixelSize: Style.topLinePixelSize - hoverEnabled: true + height: Style.nmcMenuSubItemHeight + icon.source: Style.nmcLogOutIcon + icon.height: Style.nmcTrayWindowIconWidth + icon.width: Style.nmcTrayWindowIconWidth + leftPadding: Style.nmcMenuSubItemLeftPadding onClicked: { if (model.isConnected) { @@ -238,14 +273,18 @@ AbstractButton { Accessible.role: Accessible.Button Accessible.name: text - Accessible.onPressAction: logInOutButton.clicked() + Accessible.onPressAction: clicked() } - MenuItem { + NMCMenuItem { id: removeAccountButton text: model.removeAccountText - font.pixelSize: Style.topLinePixelSize - hoverEnabled: true + height: Style.nmcMenuSubItemHeight + icon.source: Style.nmcLogOutIcon + icon.height: Style.nmcTrayWindowIconWidth + icon.width: Style.nmcTrayWindowIconWidth + leftPadding: Style.nmcMenuSubItemLeftPadding + onClicked: { UserModel.removeAccount(index) accountMenu.close() @@ -253,7 +292,7 @@ AbstractButton { Accessible.role: Accessible.Button Accessible.name: text - Accessible.onPressAction: removeAccountButton.clicked() + Accessible.onPressAction: clicked() } } } diff --git a/theme.qrc.in b/theme.qrc.in index 8ebb6e3d7b590..8f88cfc526498 100644 --- a/theme.qrc.in +++ b/theme.qrc.in @@ -295,5 +295,13 @@ theme/file-open.svg theme/backup.svg theme/convert_to_text.svg + theme/NMCIcons/accountAvatarIcon-white.svg + theme/NMCIcons/logout-white.svg + theme/NMCIcons/pause-white.svg + theme/NMCIcons/remove-white.svg + theme/NMCIcons/website-white.svg + theme/close-white.svg + theme/more-white.svg + theme/settings-white.svg diff --git a/theme/NMCIcons/accountAvatarIcon-white.svg b/theme/NMCIcons/accountAvatarIcon-white.svg new file mode 100644 index 0000000000000..1071c83ea6ef8 --- /dev/null +++ b/theme/NMCIcons/accountAvatarIcon-white.svg @@ -0,0 +1,7 @@ + + + icon/user_file/user/default@svg + + + + \ No newline at end of file diff --git a/theme/NMCIcons/logout-white.svg b/theme/NMCIcons/logout-white.svg new file mode 100644 index 0000000000000..f449764e1a279 --- /dev/null +++ b/theme/NMCIcons/logout-white.svg @@ -0,0 +1,7 @@ + + + /svg/icon/action/logout/default + + + + \ No newline at end of file diff --git a/theme/NMCIcons/pause-white.svg b/theme/NMCIcons/pause-white.svg new file mode 100644 index 0000000000000..41fd92da98393 --- /dev/null +++ b/theme/NMCIcons/pause-white.svg @@ -0,0 +1,7 @@ + + + icon/action/pause/default@svg + + + + \ No newline at end of file diff --git a/theme/NMCIcons/remove-white.svg b/theme/NMCIcons/remove-white.svg new file mode 100644 index 0000000000000..9a71790bcff30 --- /dev/null +++ b/theme/NMCIcons/remove-white.svg @@ -0,0 +1,7 @@ + + + /svg/icon/action/remove/default + + + + \ No newline at end of file diff --git a/theme/NMCIcons/website-white.svg b/theme/NMCIcons/website-white.svg new file mode 100644 index 0000000000000..4db891a1afe17 --- /dev/null +++ b/theme/NMCIcons/website-white.svg @@ -0,0 +1,7 @@ + + + icon/content/news/default@svg + + + + \ No newline at end of file diff --git a/theme/Style/Style.qml b/theme/Style/Style.qml index 07d80f19e53f7..26398df3c1b32 100644 --- a/theme/Style/Style.qml +++ b/theme/Style/Style.qml @@ -7,6 +7,52 @@ import QtQuick import com.nextcloud.desktopclient QtObject { + // NMC customization + readonly property int nmcFontSizeAccountName: 13 + readonly property int nmcFontSizeIconText: 10 + readonly property int nmcFontSizeSyncText: 15 + + // NMC icons + readonly property url nmcTLogoPath: "qrc:///client/theme/NMCIcons/tlogocarrier.svg" + readonly property url nmcAccountAvatarIcon: Theme.darkMode ? "qrc:///client/theme/NMCIcons/accountAvatarIcon-white.svg" : "qrc:///client/theme/NMCIcons/accountAvatarIcon.svg" + readonly property url nmcSettingsIcon: Theme.darkMode ? "qrc:///client/theme/settings-white.svg" : "qrc:///client/theme/settings.svg" + readonly property url nmcCloseIcon: Theme.darkMode ? "qrc:///client/theme/close-white.svg" : "qrc:///client/theme/close.svg" + readonly property url nmcPauseIcon: Theme.darkMode ? "qrc:///client/theme/NMCIcons/pause-white.svg" : "qrc:///client/theme/NMCIcons/pause.svg" + readonly property url nmcLogOutIcon: Theme.darkMode ? "qrc:///client/theme/NMCIcons/logout-white.svg" : "qrc:///client/theme/NMCIcons/logout.svg" + readonly property url nmcRemoveIcon: Theme.darkMode ? "qrc:///client/theme/NMCIcons/remove-white.svg" : "qrc:///client/theme/NMCIcons/remove.svg" + + // NMC colors + readonly property color nmcTelekomMagentaColor: "#E20074" + readonly property color nmcConflictColor: Theme.darkMode ? "#FFA14A" : "#F97012" + readonly property color nmcConflictHoverColor: Theme.darkMode ? "#f0802e" : "#de6310" + readonly property color nmcSyncHoverColor: Theme.darkMode ? "#ff4d94" : "#c00063" + readonly property color nmcSyncRefreshColor: Theme.darkMode ? "#6677ff" : "#2238df" + readonly property color nmcTrayWindowHeaderBackgroundColor: Theme.darkMode ? "#2b2b2b" : "white" + readonly property color nmcTrayWindowHeaderTextColor: Theme.darkMode ? "white" : "black" + readonly property color nmcTrayWindowHeaderSeparatorColor: Theme.darkMode ? "#3c3c3c" : "#ededed" + readonly property color nmcTrayWindowHeaderHighlightColor: Theme.darkMode ? "#444444" : "#ededed" + readonly property color nmcMenuMoreItemsColor: Theme.darkMode ? "#3a3a3a" : "#E5E5E5" + readonly property color nmcTextInButtonColor: Theme.darkMode ? "black" : "white" + + // NMC measurements + readonly property int nmcTrayWindowWidth: variableSize(480) + readonly property int nmcTrayWindowHeight: variableSize(520) + readonly property int nmcTrayWindowAddAccountButtonHeight: 56 + readonly property int nmcTrayWindowHeaderHeight: 64 + readonly property int nmcTrayWindowItemsHeight: 48 + readonly property int nmcTrayWindowIconWidth: 20 + readonly property int nmcTrayWindowLogoWidth: 64 + readonly property int nmcTrayWindowStandardMargin: 16 + readonly property int nmcTrayWindowMenuOverlayMargin: 8 + readonly property int nmcTraySyncButtonHeight: 28 + readonly property int nmcCurrentAccountButtonWidth: 180 + readonly property int nmcMenuSubItemHeight: 42 + readonly property int nmcMenuSubItemLeftPadding: 16 + readonly property int nmcStandardRadius: 4 + readonly property int nmcListViewLeftPadding: 22 + readonly property int nmcListViewIconSize: 32 + readonly property int nmcProgressFieldTextOffset: -12 + readonly property int pixelSize: fontMetrics.font.pixelSize readonly property bool darkMode: Theme.darkMode @@ -93,7 +139,7 @@ QtObject { property int folderStateIndicatorSize: 16 property int accountLabelWidth: 128 - property int accountDropDownCaretSize: 10 + property int accountDropDownCaretSize: 18 property int accountDropDownCaretMargin: 8 property int trayFoldersMenuButtonStateIndicatorBottomOffset: 5 @@ -106,7 +152,7 @@ QtObject { property int headerButtonIconSize: 48 property int minimumActivityItemHeight: 24 - property int accountIconsMenuMargin: 7 + property int accountIconsMenuMargin: 4 property int activityLabelBaseWidth: 240 @@ -129,7 +175,7 @@ QtObject { property int accountServerAnchorsMargin: 10 property int accountLabelsSpacing: 4 property int accountsServerMargin: 6 - property int accountLabelsAnchorsMargin: 5 + property int accountLabelsAnchorsMargin: 4 property int accountLabelsLayoutMargin: 12 property int accountLabelsLayoutTopMargin: 10 diff --git a/theme/black/folder.svg b/theme/black/folder.svg index 265162634208a..b4ca23cca78ac 100644 --- a/theme/black/folder.svg +++ b/theme/black/folder.svg @@ -1,7 +1,13 @@ - - - - - + + + icon/content/folder/default@svg + + + + + + + + diff --git a/theme/close-white.svg b/theme/close-white.svg new file mode 100644 index 0000000000000..f4f3c0c23cc5e --- /dev/null +++ b/theme/close-white.svg @@ -0,0 +1,7 @@ + + + icon/action/circle-close/default@svg + + + + \ No newline at end of file diff --git a/theme/close.svg b/theme/close.svg index d4102db766da4..6949d3c95049b 100644 --- a/theme/close.svg +++ b/theme/close.svg @@ -1 +1,7 @@ - \ No newline at end of file + + + icon/action/circle-close/default@svg + + + + diff --git a/theme/more-white.svg b/theme/more-white.svg new file mode 100644 index 0000000000000..7efdfcbc6c514 --- /dev/null +++ b/theme/more-white.svg @@ -0,0 +1,7 @@ + + + /svg/icon/action/more/default + + + + \ No newline at end of file diff --git a/theme/more.svg b/theme/more.svg index 164a1b70345f8..e2f50c80c7483 100644 --- a/theme/more.svg +++ b/theme/more.svg @@ -1 +1,7 @@ - \ No newline at end of file + + + /svg/icon/action/more/default + + + + \ No newline at end of file diff --git a/theme/settings-white.svg b/theme/settings-white.svg new file mode 100644 index 0000000000000..9004d38106663 --- /dev/null +++ b/theme/settings-white.svg @@ -0,0 +1,7 @@ + + + /svg/icon/service/settings/default + + + + \ No newline at end of file diff --git a/theme/settings.svg b/theme/settings.svg index a9c68e4067248..434caf275d458 100644 --- a/theme/settings.svg +++ b/theme/settings.svg @@ -1,7 +1,7 @@ - - - - - + + + /svg/icon/service/settings/default + + - + \ No newline at end of file diff --git a/theme/white/folder.svg b/theme/white/folder.svg index 68606b40cf8e7..393127d142233 100644 --- a/theme/white/folder.svg +++ b/theme/white/folder.svg @@ -1,7 +1,13 @@ - - - - - + + + icon/content/folder/default@svg + + + + + + + + - + \ No newline at end of file From bfd9a9a19603e5eeb659e0efe5a76b525676d6c6 Mon Sep 17 00:00:00 2001 From: memurats Date: Mon, 27 Oct 2025 12:26:49 +0100 Subject: [PATCH 02/13] renamed qmldir --- src/gui/nmcgui/{qmldir.txt => qmldir} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/gui/nmcgui/{qmldir.txt => qmldir} (100%) diff --git a/src/gui/nmcgui/qmldir.txt b/src/gui/nmcgui/qmldir similarity index 100% rename from src/gui/nmcgui/qmldir.txt rename to src/gui/nmcgui/qmldir From 7eb197fee3bbfe4675047f8ab086f7e384227ed6 Mon Sep 17 00:00:00 2001 From: memurats Date: Mon, 27 Oct 2025 14:50:00 +0100 Subject: [PATCH 03/13] updated gui cmake file --- src/gui/CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 32b5bf7a93b8c..7233e45df8f81 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -6,9 +6,6 @@ find_package(Qt${QT_MAJOR_VERSION} REQUIRED COMPONENTS Widgets Svg Qml Quick Qui find_package(KF6Archive REQUIRED) find_package(KF6GuiAddons) -#NMC customization: needed to find the ui file in a different location than the header file -set(CMAKE_AUTOUIC_SEARCH_PATHS "${CMAKE_SOURCE_DIR}/src/gui") - if(CMAKE_BUILD_TYPE MATCHES Debug) add_definitions(-DQT_QML_DEBUG) endif() @@ -26,6 +23,9 @@ endif() configure_file(${CMAKE_SOURCE_DIR}/theme.qrc.in ${CMAKE_SOURCE_DIR}/theme.qrc) set(theme_dir ${CMAKE_SOURCE_DIR}/theme) +#NMC customization: needed to find the ui file in a different location than the header file +set(CMAKE_AUTOUIC_SEARCH_PATHS "${CMAKE_SOURCE_DIR}/src/gui") + set(client_UI_SRCS accountsettings.ui conflictdialog.ui From b934238731582e80d9248301dd0bd6736770b747 Mon Sep 17 00:00:00 2001 From: memurats Date: Mon, 27 Oct 2025 14:59:39 +0100 Subject: [PATCH 04/13] updated theme icons --- theme.qrc.in | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/theme.qrc.in b/theme.qrc.in index 8f88cfc526498..49aaf8bd995bd 100644 --- a/theme.qrc.in +++ b/theme.qrc.in @@ -1,5 +1,16 @@ + + + theme/NMCIcons/accountAvatarIcon-white.svg + theme/NMCIcons/logout-white.svg + theme/NMCIcons/pause-white.svg + theme/NMCIcons/remove-white.svg + theme/NMCIcons/website-white.svg + theme/close-white.svg + theme/more-white.svg + theme/settings-white.svg + theme/colored/16-@APPLICATION_ICON_NAME@-icon.png theme/colored/24-@APPLICATION_ICON_NAME@-icon.png theme/colored/32-@APPLICATION_ICON_NAME@-icon.png From de783946c7bde48d975a20a6b5515029b11265c9 Mon Sep 17 00:00:00 2001 From: memurats Date: Mon, 27 Oct 2025 17:37:06 +0100 Subject: [PATCH 05/13] solved merge conflict --- theme.qrc.in | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/theme.qrc.in b/theme.qrc.in index 49aaf8bd995bd..8f88cfc526498 100644 --- a/theme.qrc.in +++ b/theme.qrc.in @@ -1,16 +1,5 @@ - - - theme/NMCIcons/accountAvatarIcon-white.svg - theme/NMCIcons/logout-white.svg - theme/NMCIcons/pause-white.svg - theme/NMCIcons/remove-white.svg - theme/NMCIcons/website-white.svg - theme/close-white.svg - theme/more-white.svg - theme/settings-white.svg - theme/colored/16-@APPLICATION_ICON_NAME@-icon.png theme/colored/24-@APPLICATION_ICON_NAME@-icon.png theme/colored/32-@APPLICATION_ICON_NAME@-icon.png From b55994c773ded41c32975095b675ec7e3debabf1 Mon Sep 17 00:00:00 2001 From: memurats Date: Tue, 28 Oct 2025 09:15:30 +0100 Subject: [PATCH 06/13] fixed user menu --- src/gui/tray/UserLine.qml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/gui/tray/UserLine.qml b/src/gui/tray/UserLine.qml index 97f02e7392505..3ede602c3b124 100644 --- a/src/gui/tray/UserLine.qml +++ b/src/gui/tray/UserLine.qml @@ -76,7 +76,8 @@ AbstractButton { Layout.fillHeight: true Layout.alignment: Qt.AlignVCenter spacing: 8 - anchors.leftMargin: 12 + + Item { width: Style.nmcMenuSubItemLeftPadding } Image { id: accountIcon @@ -229,6 +230,7 @@ AbstractButton { text: qsTr("Set status") font.pixelSize: Style.topLinePixelSize hoverEnabled: true + height: visible ? implicitHeight : 0 onClicked: showUserStatusSelector(index) @@ -244,6 +246,7 @@ AbstractButton { text: qsTr("Status message") font.pixelSize: Style.topLinePixelSize hoverEnabled: true + height: visible ? implicitHeight : 0 onClicked: showUserStatusMessageSelector(index) From 2ac9236326a7eb54dfa942e633bf32d2914f2a0d Mon Sep 17 00:00:00 2001 From: memurats Date: Fri, 14 Nov 2025 12:36:26 +0100 Subject: [PATCH 07/13] reintroduced tabs --- src/gui/accountsettings.cpp | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/gui/accountsettings.cpp b/src/gui/accountsettings.cpp index f409863294775..3c8a469742c5b 100644 --- a/src/gui/accountsettings.cpp +++ b/src/gui/accountsettings.cpp @@ -207,8 +207,6 @@ AccountSettings::AccountSettings(AccountState *accountState, QWidget *parent) new ToolTipUpdater(_ui->_folderList); -const auto tabWidget = _ui->tabWidget; - #if defined(BUILD_FILE_PROVIDER_MODULE) if (Mac::FileProvider::available()) { const auto fileProviderPanelContents = _ui->fileProviderPanelContents; @@ -226,6 +224,7 @@ const auto tabWidget = _ui->tabWidget; _ui->fileProviderPanel->setVisible(false); } #else + const auto tabWidget = _ui->tabWidget; const auto fileProviderTab = _ui->fileProviderTab; if (const auto fileProviderWidgetTabIndex = tabWidget->indexOf(fileProviderTab); fileProviderWidgetTabIndex >= 0) { tabWidget->removeTab(fileProviderWidgetTabIndex); @@ -243,12 +242,6 @@ const auto tabWidget = _ui->tabWidget; connectionSettingsLayout->addWidget(networkSettings); connectionSettingsTab->setLayout(connectionSettingsLayout); - if (const auto connectionSettingsTabIndex = tabWidget->indexOf(connectionSettingsTab); connectionSettingsTabIndex >= 0) { - tabWidget->removeTab(connectionSettingsTabIndex); - } - tabWidget->setCurrentIndex(0); - tabWidget->tabBar()->hide(); - const auto mouseCursorChanger = new MouseCursorChanger(this); mouseCursorChanger->folderList = _ui->_folderList; mouseCursorChanger->model = _model; From 1a439f67ed3930232dd03b0feca88115abff378c Mon Sep 17 00:00:00 2001 From: Mauro Mura Date: Fri, 14 Nov 2025 15:14:58 +0100 Subject: [PATCH 08/13] changed remove account icon --- src/gui/tray/UserLine.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/tray/UserLine.qml b/src/gui/tray/UserLine.qml index 3ede602c3b124..6b5e3f18e0aed 100644 --- a/src/gui/tray/UserLine.qml +++ b/src/gui/tray/UserLine.qml @@ -283,7 +283,7 @@ AbstractButton { id: removeAccountButton text: model.removeAccountText height: Style.nmcMenuSubItemHeight - icon.source: Style.nmcLogOutIcon + icon.source: Style.nmcRemoveIcon icon.height: Style.nmcTrayWindowIconWidth icon.width: Style.nmcTrayWindowIconWidth leftPadding: Style.nmcMenuSubItemLeftPadding From 9af8827d08c2e5eb0ccd8301e2a000c0828ee210 Mon Sep 17 00:00:00 2001 From: memurats Date: Mon, 16 Feb 2026 10:35:02 +0100 Subject: [PATCH 09/13] update to main window --- src/gui/tray/MainWindow.qml | 79 +++++++++++++++++++++++++++++++++---- 1 file changed, 72 insertions(+), 7 deletions(-) diff --git a/src/gui/tray/MainWindow.qml b/src/gui/tray/MainWindow.qml index 88b19f054f86a..a17bb89dea3c3 100644 --- a/src/gui/tray/MainWindow.qml +++ b/src/gui/tray/MainWindow.qml @@ -255,12 +255,79 @@ ApplicationWindow { TrayWindowHeader { id: trayWindowHeader + + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right height: Style.nmcTrayWindowHeaderHeight + } - anchors { - top: parent.top - left: parent.left - right: parent.right + Button { + id: trayWindowSyncWarning + + readonly property color warningIconColor: Style.errorBoxBackgroundColor + + anchors.top: trayWindowHeader.bottom + anchors.left: trayWindowMainItem.left + anchors.right: trayWindowMainItem.right + anchors.topMargin: Style.trayHorizontalMargin + anchors.leftMargin: Style.trayHorizontalMargin + anchors.rightMargin: Style.trayHorizontalMargin + + visible: UserModel.hasSyncErrors + && !(UserModel.syncErrorUserCount === 1 + && UserModel.firstSyncErrorUserId === UserModel.currentUserId) + padding: 0 + background: Rectangle { + radius: Style.slightlyRoundedButtonRadius + color: Qt.rgba(trayWindowSyncWarning.warningIconColor.r, + trayWindowSyncWarning.warningIconColor.g, + trayWindowSyncWarning.warningIconColor.b, + 0.2) + border.width: Style.normalBorderWidth + border.color: Qt.rgba(trayWindowSyncWarning.warningIconColor.r, + trayWindowSyncWarning.warningIconColor.g, + trayWindowSyncWarning.warningIconColor.b, + 0.6) + } + + Accessible.name: syncWarningText.text + Accessible.role: Accessible.Button + + contentItem: RowLayout { + anchors.fill: parent + spacing: 0 + + ColumnLayout { + Layout.fillWidth: true + Layout.alignment: Qt.AlignVCenter + Layout.topMargin: 4 + Layout.leftMargin: Style.trayHorizontalMargin + Layout.rightMargin: Style.trayHorizontalMargin + Layout.bottomMargin: 4 + + EnforcedPlainTextLabel { + id: syncWarningText + + Layout.fillWidth: true + font.pixelSize: Style.topLinePixelSize + font.bold: true + wrapMode: Text.WordWrap + horizontalAlignment: Text.AlignHCenter + text: { + if (UserModel.syncErrorUserCount <= 1) { + return qsTr("Issue with account %1").arg(UserModel.firstSyncErrorUser ? UserModel.firstSyncErrorUser.name : ""); + } + return qsTr("Issues with several accounts"); + } + } + } + } + + onClicked: { + if (UserModel.firstSyncErrorUserId >= 0) { + UserModel.currentUserId = UserModel.firstSyncErrorUserId + } } } @@ -813,8 +880,6 @@ ApplicationWindow { anchors.right: trayWindowMainItem.right anchors.bottom: trayWindowMainItem.bottom - ScrollBar.vertical.visible: contentHeight > activityList.height - activeFocusOnTab: true model: activityModel onOpenFile: Qt.openUrlExternally(filePath); @@ -831,4 +896,4 @@ ApplicationWindow { } } } // Item trayWindowMainItem -} +} \ No newline at end of file From 4de99858f6823ca9d6aac640379f17a71b1cb37f Mon Sep 17 00:00:00 2001 From: memurats Date: Tue, 26 May 2026 11:42:08 +0200 Subject: [PATCH 10/13] fix error --- src/gui/accountsettings.cpp | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/gui/accountsettings.cpp b/src/gui/accountsettings.cpp index 3c8a469742c5b..d7d00b72ebe66 100644 --- a/src/gui/accountsettings.cpp +++ b/src/gui/accountsettings.cpp @@ -217,19 +217,11 @@ AccountSettings::AccountSettings(AccountState *accountState, QWidget *parent) QQuickWidget::SizeRootObjectToView); fpSettingsLayout->setContentsMargins(0, 0, 0, 0); fpSettingsLayout->addWidget(fpSettingsWidget); - fileProviderTab->setLayout(fpSettingsLayout); } else { - // macOS 13 Ventura: the file provider feature is unsupported there. - // This branch can be removed once Ventura is no longer supported. _ui->fileProviderPanel->setVisible(false); } #else - const auto tabWidget = _ui->tabWidget; - const auto fileProviderTab = _ui->fileProviderTab; - if (const auto fileProviderWidgetTabIndex = tabWidget->indexOf(fileProviderTab); fileProviderWidgetTabIndex >= 0) { - tabWidget->removeTab(fileProviderWidgetTabIndex); - } - tabWidget->setCurrentIndex(0); + _ui->fileProviderPanel->setVisible(false); #endif const auto connectionSettingsPanelContents = _ui->connectionSettingsPanelContents; @@ -240,7 +232,6 @@ AccountSettings::AccountSettings(AccountState *accountState, QWidget *parent) } connectionSettingsLayout->setContentsMargins(0, 0, 0, 0); connectionSettingsLayout->addWidget(networkSettings); - connectionSettingsTab->setLayout(connectionSettingsLayout); const auto mouseCursorChanger = new MouseCursorChanger(this); mouseCursorChanger->folderList = _ui->_folderList; From b8ef147c59b504d7461155556971d23db0eb3d02 Mon Sep 17 00:00:00 2001 From: memurats Date: Wed, 27 May 2026 11:47:10 +0200 Subject: [PATCH 11/13] fix runtime error --- src/gui/tray/MainWindow.qml | 16 ++++++---------- src/gui/tray/TrayWindowHeader.qml | 2 +- src/gui/tray/asyncimageresponse.cpp | 4 ++++ 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/gui/tray/MainWindow.qml b/src/gui/tray/MainWindow.qml index a17bb89dea3c3..b6a80c13e8b28 100644 --- a/src/gui/tray/MainWindow.qml +++ b/src/gui/tray/MainWindow.qml @@ -49,9 +49,9 @@ ApplicationWindow { onVisibleChanged: { // HACK: reload account Instantiator immediately by restting it - could be done better I guess - // see also id:trayWindowHeader.currentAccountHeaderButton.accountMenu below - trayWindowHeader.currentAccountHeaderButton.userLineInstantiator.active = false; - trayWindowHeader.currentAccountHeaderButton.userLineInstantiator.active = true; + // see also id:trayWindowHeader.accountHeaderButton.accountMenu below + trayWindowHeader.accountHeaderButton.userLineInstantiator.active = false; + trayWindowHeader.accountHeaderButton.userLineInstantiator.active = true; syncStatus.model.load(); } @@ -65,7 +65,7 @@ ApplicationWindow { Connections { target: UserModel function onCurrentUserChanged() { - trayWindowHeader.currentAccountHeaderButton.accountMenu.close(); + trayWindowHeader.accountHeaderButton.accountMenu.close(); syncStatus.model.load(); } } @@ -91,10 +91,8 @@ ApplicationWindow { fileDetailsDrawer.close(); if (Systray.isOpen) { - trayWindowHeader.currentAccountHeaderButton.accountMenu.close(); - trayWindowHeader.appsMenu.close(); - trayWindowHeader.openLocalFolderButton.closeMenu() - UserModel.refreshSyncErrorUsers() + trayWindowHeader.accountHeaderButton.accountMenu.close(); + UserModel.refreshSyncErrorUsers(); } } @@ -345,8 +343,6 @@ ApplicationWindow { UnifiedSearchInputContainer { id: trayWindowUnifiedSearchInputContainer - visible: !trayWindowMainItem.showAssistantPanel - visible: false property bool activateSearchFocus: activeFocus diff --git a/src/gui/tray/TrayWindowHeader.qml b/src/gui/tray/TrayWindowHeader.qml index 897e346d3fafa..45c811c789c8e 100644 --- a/src/gui/tray/TrayWindowHeader.qml +++ b/src/gui/tray/TrayWindowHeader.qml @@ -19,7 +19,7 @@ Rectangle { signal featuredAppButtonClicked - readonly property alias currentAccountHeaderButton: currentAccountHeaderButton + readonly property alias accountHeaderButton: currentAccountHeaderButton color: Style.nmcTrayWindowHeaderBackgroundColor height: Style.nmcTrayWindowHeaderHeight diff --git a/src/gui/tray/asyncimageresponse.cpp b/src/gui/tray/asyncimageresponse.cpp index 875a2d16eb755..d6603173676b2 100644 --- a/src/gui/tray/asyncimageresponse.cpp +++ b/src/gui/tray/asyncimageresponse.cpp @@ -137,6 +137,7 @@ void AsyncImageResponse::processNetworkReply(QNetworkReply *reply) scaledSvg.fill("transparent"); QPainter painterForSvg(&scaledSvg); svgRenderer.render(&painterForSvg); + painterForSvg.end(); if (!_svgRecolor.isValid()) { setImageAndEmitFinished(scaledSvg); @@ -145,9 +146,12 @@ void AsyncImageResponse::processNetworkReply(QNetworkReply *reply) QImage image(_requestedImageSize, QImage::Format_ARGB32); image.fill(_svgRecolor); + QPainter imagePainter(&image); imagePainter.setCompositionMode(QPainter::CompositionMode_DestinationIn); imagePainter.drawImage(0, 0, scaledSvg); + imagePainter.end(); + setImageAndEmitFinished(image); } From 42b1a350d4ee130c1b5e5b0e45ac3484fdbd6031 Mon Sep 17 00:00:00 2001 From: Mauro Mura Date: Fri, 29 May 2026 10:30:10 +0200 Subject: [PATCH 12/13] Restore UserLine.qml with original content --- src/gui/tray/UserLine.qml | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/src/gui/tray/UserLine.qml b/src/gui/tray/UserLine.qml index 6b5e3f18e0aed..b643b96fda956 100644 --- a/src/gui/tray/UserLine.qml +++ b/src/gui/tray/UserLine.qml @@ -114,6 +114,7 @@ AbstractButton { text: server elide: Text.ElideRight font.pixelSize: Style.subLinePixelSize + visible: false color: !userLine.parent.enabled ? userLine.parent.palette.mid @@ -157,23 +158,6 @@ AbstractButton { : userLine.parent.palette.text) } } - - EnforcedPlainTextLabel { - id: accountServer - visible: false - Layout.fillWidth: true - Layout.alignment: Qt.AlignLeft | Qt.AlignTop - verticalAlignment: Text.AlignTop - text: server - elide: Text.ElideRight - font.pixelSize: Style.subLinePixelSize - - color: !userLine.parent.enabled - ? userLine.parent.palette.mid - : ((userLine.parent.highlighted || userLine.parent.down) && Qt.platform.os !== "windows" - ? userLine.parent.palette.highlightedText - : userLine.parent.palette.text) - } } Item { // Spacer From 500dfb63887b365f333e1bf0a641223572b77fde Mon Sep 17 00:00:00 2001 From: memurats Date: Wed, 3 Jun 2026 12:30:32 +0200 Subject: [PATCH 13/13] fix hover state --- src/gui/tray/ActivityItem.qml | 16 ++++++--- src/gui/tray/ActivityList.qml | 61 +++++++++++++---------------------- 2 files changed, 35 insertions(+), 42 deletions(-) diff --git a/src/gui/tray/ActivityItem.qml b/src/gui/tray/ActivityItem.qml index 6af9ec3c16522..d406dc30d0ecd 100644 --- a/src/gui/tray/ActivityItem.qml +++ b/src/gui/tray/ActivityItem.qml @@ -7,6 +7,7 @@ import QtQml import QtQuick import QtQuick.Controls import QtQuick.Layouts + import Style import com.nextcloud.desktopclient @@ -24,25 +25,31 @@ ItemDelegate { property bool isTalkReplyOptionVisible: model.messageSent !== "" padding: Style.standardSpacing + hoverEnabled: true Accessible.role: Accessible.ListItem Accessible.name: (model.path !== "" && model.displayPath !== "") ? qsTr("Open %1 locally").arg(model.displayPath) : model.message Accessible.onPressAction: root.clicked() + background: Rectangle { + color: root.hovered || root.activeFocus ? palette.highlight : "transparent" + radius: Style.mediumRoundedButtonRadius + border.width: 0 + } + ToolTip { popupType: Qt.platform.os === "windows" ? Popup.Item : Popup.Native visible: root.hovered && !activityContent.childHovered && model.displayLocation !== "" text: qsTr("In %1").arg(model.displayLocation) } - // TODO: the current style does not support customization of this control contentItem: ColumnLayout { spacing: Style.smallSpacing ActivityItemContent { id: activityContent - adaptiveTextColor: root.activeFocus ? palette.highlightedText : palette.text + adaptiveTextColor: root.hovered || root.activeFocus ? palette.highlightedText : palette.text Layout.fillWidth: true Layout.minimumHeight: Style.minActivityHeight @@ -60,6 +67,7 @@ ItemDelegate { Loader { id: talkReplyTextFieldLoader + active: root.isChatActivity && root.isTalkReplyPossible && model.messageSent === "" visible: root.isTalkReplyOptionVisible @@ -69,8 +77,8 @@ ItemDelegate { sourceComponent: TalkReplyTextField { onSendReply: { - UserModel.currentUser.sendReplyMessage(model.activityIndex, model.conversationToken, reply, model.messageId); - talkReplyTextFieldLoader.visible = false; + UserModel.currentUser.sendReplyMessage(model.activityIndex, model.conversationToken, reply, model.messageId) + talkReplyTextFieldLoader.visible = false } } } diff --git a/src/gui/tray/ActivityList.qml b/src/gui/tray/ActivityList.qml index 5840ceb69f0f0..404a39c3f7854 100644 --- a/src/gui/tray/ActivityList.qml +++ b/src/gui/tray/ActivityList.qml @@ -11,9 +11,10 @@ import com.nextcloud.desktopclient as NC ScrollView { id: controlRoot + property alias model: sortedActivityList.sourceModel property alias count: activityList.count - property alias atYBeginning : activityList.atYBeginning + property alias atYBeginning: activityList.atYBeginning property bool isFileActivityList: false property int iconSize: Style.trayListItemIconSize property int delegateHorizontalPadding: 0 @@ -21,7 +22,6 @@ ScrollView { property bool scrollingToTop: false function scrollToTop() { - // Triggers activation of repeating upward flick timer scrollingToTop = true } @@ -33,7 +33,7 @@ ScrollView { topPadding: 0 bottomPadding: 0 rightPadding: ScrollBar.vertical.width - + focus: false ScrollBar.horizontal.policy: ScrollBar.AlwaysOff @@ -60,6 +60,9 @@ ScrollView { currentIndex: -1 interactive: true + highlight: null + highlightFollowsCurrentItem: false + Timer { id: repeatUpFlickTimer interval: Style.activityListScrollToTopTimerInterval @@ -74,53 +77,34 @@ ScrollView { } } - highlight: Rectangle { - id: activityHover - anchors.fill: activityList.currentItem - color: palette.highlight - radius: Style.mediumRoundedButtonRadius - visible: activityList.activeFocus - } - - highlightFollowsCurrentItem: true - highlightMoveDuration: 0 - highlightResizeDuration: 0 - highlightRangeMode: ListView.ApplyRange - preferredHighlightBegin: 0 - preferredHighlightEnd: controlRoot.height - model: NC.SortedActivityListModel { id: sortedActivityList } delegate: ActivityItem { - background: null width: activityList.contentItem.width isFileActivityList: controlRoot.isFileActivityList iconSize: Style.nmcListViewIconSize leftPadding: Style.nmcListViewLeftPadding flickable: activityList + onHoveredChanged: if (hovered) { - // When we set the currentIndex the list view will scroll... - // unless we tamper with the preferred highlight points to stop this. - const savedPreferredHighlightBegin = activityList.preferredHighlightBegin; - const savedPreferredHighlightEnd = activityList.preferredHighlightEnd; - // Set overkill values to make sure no scroll happens when we hover with mouse - activityList.preferredHighlightBegin = -controlRoot.height; - activityList.preferredHighlightEnd = controlRoot.height * 2; + const savedPreferredHighlightBegin = activityList.preferredHighlightBegin + const savedPreferredHighlightEnd = activityList.preferredHighlightEnd - activityList.currentIndex = index + activityList.preferredHighlightBegin = -controlRoot.height + activityList.preferredHighlightEnd = controlRoot.height * 2 - // Reset original values so keyboard navigation makes list view scroll - activityList.preferredHighlightBegin = savedPreferredHighlightBegin; - activityList.preferredHighlightEnd = savedPreferredHighlightEnd; + activityList.currentIndex = index - forceActiveFocus(); + activityList.preferredHighlightBegin = savedPreferredHighlightBegin + activityList.preferredHighlightEnd = savedPreferredHighlightEnd } + onClicked: { if (model.isCurrentUserFileActivity && model.openablePath) { - openFile("file://" + model.openablePath); + openFile("file://" + model.openablePath) } else { activityItemClicked(model.activityIndex) } @@ -151,6 +135,7 @@ ScrollView { Column { id: placeholderColumn + width: parent.width * 0.75 anchors.centerIn: parent visible: activityList.count === 0 @@ -165,12 +150,12 @@ ScrollView { } EnforcedPlainTextLabel { - width: parent.width - text: qsTr("No activities yet") - font.bold: true - wrapMode: Text.Wrap - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter + width: parent.width + text: qsTr("No activities yet") + font.bold: true + wrapMode: Text.Wrap + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter } } }