diff --git a/resources.qrc b/resources.qrc index 6aeb159b36535..d9db7bc05d87c 100644 --- a/resources.qrc +++ b/resources.qrc @@ -1,7 +1,7 @@ src/gui/UserStatusSelector.qml - src/gui/UserStatusSelectorDialog.qml + src/gui/UserStatusSelectorPage.qml src/gui/EmojiPicker.qml src/gui/UserStatusSelectorButton.qml src/gui/PredefinedStatusButton.qml diff --git a/src/gui/UserStatusSelector.qml b/src/gui/UserStatusSelector.qml index f480597cb47c6..68809ae4bd907 100644 --- a/src/gui/UserStatusSelector.qml +++ b/src/gui/UserStatusSelector.qml @@ -23,274 +23,297 @@ import Style 1.0 ColumnLayout { id: rootLayout - spacing: 0 + spacing: Style.standardSpacing * 2 property NC.UserStatusSelectorModel userStatusSelectorModel - Label { - Layout.topMargin: Style.standardSpacing * 2 - Layout.leftMargin: Style.standardSpacing - Layout.rightMargin: Style.standardSpacing - Layout.bottomMargin: Style.standardSpacing - Layout.alignment: Qt.AlignTop | Qt.AlignHCenter - font.bold: true - text: qsTr("Online status") - color: Style.ncTextColor - } - - GridLayout { - id: topButtonsLayout - - Layout.margins: Style.standardSpacing - Layout.alignment: Qt.AlignTop - columns: 2 - rows: 2 - columnSpacing: Style.standardSpacing - rowSpacing: Style.standardSpacing - - property int maxButtonHeight: 0 - function updateMaxButtonHeight(newHeight) { - maxButtonHeight = Math.max(maxButtonHeight, newHeight) - } + Column { + // We use a normal column here because layouts often don't adjust to any custom + // alignments for each other. If Item 2 is below Item 1, Item 2 will always set + // its alignment in relation to Item 1 being in default alignment of vertically + // centered. So when we set Item 2 to align top, even if Item 1 is aligned top, + // Item 2 will align itself as if Item 1 were vertically centered. + // + // Since in this case we want to set everything to align top, we use the Column + // which does this well, have it fill the height of the parent ColumnLayout, + // pushing the bottom button box down. - UserStatusSelectorButton { - checked: NC.UserStatus.Online === userStatusSelectorModel.onlineStatus - checkable: true - icon.source: userStatusSelectorModel.onlineIcon - icon.color: "transparent" - text: qsTr("Online") - onClicked: userStatusSelectorModel.onlineStatus = NC.UserStatus.Online + id: mainContentsLayout + spacing: rootLayout.spacing - Layout.fillWidth: true - implicitWidth: 200 // Pretty much a hack to ensure all the buttons are equal in width - Layout.preferredHeight: topButtonsLayout.maxButtonHeight - onImplicitHeightChanged: topButtonsLayout.updateMaxButtonHeight(implicitHeight) - Component.onCompleted: topButtonsLayout.updateMaxButtonHeight(implicitHeight) - } - UserStatusSelectorButton { - checked: NC.UserStatus.Away === userStatusSelectorModel.onlineStatus - checkable: true - icon.source: userStatusSelectorModel.awayIcon - icon.color: "transparent" - text: qsTr("Away") - onClicked: userStatusSelectorModel.onlineStatus = NC.UserStatus.Away - - Layout.fillWidth: true - implicitWidth: 200 // Pretty much a hack to ensure all the buttons are equal in width - Layout.preferredHeight: topButtonsLayout.maxButtonHeight - onImplicitHeightChanged: topButtonsLayout.updateMaxButtonHeight(implicitHeight) - Component.onCompleted: topButtonsLayout.updateMaxButtonHeight(implicitHeight) - - } - UserStatusSelectorButton { - checked: NC.UserStatus.DoNotDisturb === userStatusSelectorModel.onlineStatus - checkable: true - icon.source: userStatusSelectorModel.dndIcon - icon.color: "transparent" - text: qsTr("Do not disturb") - secondaryText: qsTr("Mute all notifications") - onClicked: userStatusSelectorModel.onlineStatus = NC.UserStatus.DoNotDisturb + Layout.fillWidth: true + Layout.fillHeight: true + Layout.alignment: Qt.AlignTop - Layout.fillWidth: true - implicitWidth: 200 // Pretty much a hack to ensure all the buttons are equal in width - Layout.preferredHeight: topButtonsLayout.maxButtonHeight - onImplicitHeightChanged: topButtonsLayout.updateMaxButtonHeight(implicitHeight) - Component.onCompleted: topButtonsLayout.updateMaxButtonHeight(implicitHeight) - } - UserStatusSelectorButton { - checked: NC.UserStatus.Invisible === userStatusSelectorModel.onlineStatus - checkable: true - icon.source: userStatusSelectorModel.invisibleIcon - icon.color: "transparent" - text: qsTr("Invisible") - secondaryText: qsTr("Appear offline") - onClicked: userStatusSelectorModel.onlineStatus = NC.UserStatus.Invisible + ColumnLayout { + id: statusButtonsLayout + width: parent.width + spacing: Style.smallSpacing + + Label { + Layout.fillWidth: true + Layout.bottomMargin: Style.smallSpacing + horizontalAlignment: Text.AlignHCenter + font.bold: true + text: qsTr("Online status") + color: Style.ncTextColor + } - Layout.fillWidth: true - implicitWidth: 200 // Pretty much a hack to ensure all the buttons are equal in width - Layout.preferredHeight: topButtonsLayout.maxButtonHeight - onImplicitHeightChanged: topButtonsLayout.updateMaxButtonHeight(implicitHeight) - Component.onCompleted: topButtonsLayout.updateMaxButtonHeight(implicitHeight) - } - } + GridLayout { + id: topButtonsLayout + columns: 2 + rows: 2 + columnSpacing: statusButtonsLayout.spacing + rowSpacing: statusButtonsLayout.spacing - Label { - Layout.topMargin: Style.standardSpacing * 2 - Layout.leftMargin: Style.standardSpacing - Layout.rightMargin: Style.standardSpacing - Layout.bottomMargin: Style.standardSpacing - Layout.alignment: Qt.AlignTop | Qt.AlignHCenter - font.bold: true - text: qsTr("Status message") - color: Style.ncTextColor - } + property int maxButtonHeight: 0 + function updateMaxButtonHeight(newHeight) { + maxButtonHeight = Math.max(maxButtonHeight, newHeight) + } - RowLayout { - Layout.topMargin: Style.standardSpacing - Layout.leftMargin: Style.standardSpacing - Layout.rightMargin: Style.standardSpacing - Layout.bottomMargin: Style.standardSpacing * 2 - Layout.alignment: Qt.AlignTop - Layout.fillWidth: true + UserStatusSelectorButton { + checked: userStatusSelectorModel.onlineStatus === NC.UserStatus.Online + checkable: true + icon.source: userStatusSelectorModel.onlineIcon + icon.color: "transparent" + text: qsTr("Online") + onClicked: userStatusSelectorModel.onlineStatus = NC.UserStatus.Online - spacing: 0 + Layout.fillWidth: true + implicitWidth: 200 // Pretty much a hack to ensure all the buttons are equal in width + } + UserStatusSelectorButton { + checked: userStatusSelectorModel.onlineStatus === NC.UserStatus.Away + checkable: true + icon.source: userStatusSelectorModel.awayIcon + icon.color: "transparent" + text: qsTr("Away") + onClicked: userStatusSelectorModel.onlineStatus = NC.UserStatus.Away - UserStatusSelectorButton { - id: fieldButton + Layout.fillWidth: true + implicitWidth: 200 // Pretty much a hack to ensure all the buttons are equal in width - Layout.preferredWidth: userStatusMessageTextField.height - Layout.preferredHeight: userStatusMessageTextField.height + } + UserStatusSelectorButton { + checked: userStatusSelectorModel.onlineStatus === NC.UserStatus.DoNotDisturb + checkable: true + icon.source: userStatusSelectorModel.dndIcon + icon.color: "transparent" + text: qsTr("Do not disturb") + secondaryText: qsTr("Mute all notifications") + onClicked: userStatusSelectorModel.onlineStatus = NC.UserStatus.DoNotDisturb + + Layout.fillWidth: true + implicitWidth: 200 // Pretty much a hack to ensure all the buttons are equal in width + Layout.preferredHeight: topButtonsLayout.maxButtonHeight + onImplicitHeightChanged: topButtonsLayout.updateMaxButtonHeight(implicitHeight) + Component.onCompleted: topButtonsLayout.updateMaxButtonHeight(implicitHeight) + } + UserStatusSelectorButton { + checked: userStatusSelectorModel.onlineStatus === NC.UserStatus.Invisible + checkable: true + icon.source: userStatusSelectorModel.invisibleIcon + icon.color: "transparent" + text: qsTr("Invisible") + secondaryText: qsTr("Appear offline") + onClicked: userStatusSelectorModel.onlineStatus = NC.UserStatus.Invisible + + Layout.fillWidth: true + implicitWidth: 200 // Pretty much a hack to ensure all the buttons are equal in width + Layout.preferredHeight: topButtonsLayout.maxButtonHeight + onImplicitHeightChanged: topButtonsLayout.updateMaxButtonHeight(implicitHeight) + Component.onCompleted: topButtonsLayout.updateMaxButtonHeight(implicitHeight) + } + } + } - text: userStatusSelectorModel.userStatusEmoji + ColumnLayout { + id: userStatusMessageLayout + width: parent.width + spacing: Style.smallSpacing + + Label { + Layout.fillWidth: true + Layout.bottomMargin: Style.smallSpacing + horizontalAlignment: Text.AlignHCenter + font.bold: true + text: qsTr("Status message") + color: Style.ncTextColor + } - onClicked: emojiDialog.open() - onHeightChanged: topButtonsLayout.maxButtonHeight = Math.max(topButtonsLayout.maxButtonHeight, height) + RowLayout { + Layout.fillWidth: true + spacing: 0 + + UserStatusSelectorButton { + id: fieldButton + + Layout.preferredWidth: userStatusMessageTextField.height + Layout.preferredHeight: userStatusMessageTextField.height + + text: userStatusSelectorModel.userStatusEmoji + + onClicked: emojiDialog.open() + onHeightChanged: topButtonsLayout.maxButtonHeight = Math.max(topButtonsLayout.maxButtonHeight, height) + + primary: true + padding: 0 + z: hovered ? 2 : 0 // Make sure highlight is seen on top of text field + + property color borderColor: showBorder ? Style.ncBlue : Style.menuBorder + + // We create the square with only the top-left and bottom-left rounded corners + // by overlaying different rectangles on top of each other + background: Rectangle { + radius: Style.slightlyRoundedButtonRadius + color: Style.buttonBackgroundColor + border.color: fieldButton.borderColor + border.width: Style.normalBorderWidth + + Rectangle { + anchors.fill: parent + anchors.leftMargin: parent.width / 2 + anchors.rightMargin: -1 + z: 1 + color: Style.buttonBackgroundColor + border.color: fieldButton.borderColor + border.width: Style.normalBorderWidth + } + + Rectangle { // We need to cover the blue border of the non-radiused rectangle + anchors.fill: parent + anchors.leftMargin: parent.width / 4 + anchors.rightMargin: parent.width / 4 + anchors.topMargin: Style.normalBorderWidth + anchors.bottomMargin: Style.normalBorderWidth + z: 2 + color: Style.buttonBackgroundColor + } + } + } - primary: true - padding: 0 - z: hovered ? 2 : 0 // Make sure highlight is seen on top of text field - - property color borderColor: showBorder ? Style.ncBlue : Style.menuBorder - - // We create the square with only the top-left and bottom-left rounded corners - // by overlaying different rectangles on top of each other - background: Rectangle { - radius: Style.slightlyRoundedButtonRadius - color: Style.buttonBackgroundColor - border.color: fieldButton.borderColor - border.width: Style.normalBorderWidth - - Rectangle { - anchors.fill: parent - anchors.leftMargin: parent.width / 2 - anchors.rightMargin: -1 - z: 1 - color: Style.buttonBackgroundColor - border.color: fieldButton.borderColor - border.width: Style.normalBorderWidth + Popup { + id: emojiDialog + padding: 0 + margins: 0 + clip: true + + anchors.centerIn: Overlay.overlay + + background: Rectangle { + color: Style.backgroundColor + border.width: Style.normalBorderWidth + border.color: Style.menuBorder + radius: Style.slightlyRoundedButtonRadius + } + + EmojiPicker { + id: emojiPicker + + onChosen: { + userStatusSelectorModel.userStatusEmoji = emoji + emojiDialog.close() + } + } } - Rectangle { // We need to cover the blue border of the non-radiused rectangle - anchors.fill: parent - anchors.leftMargin: parent.width / 4 - anchors.rightMargin: parent.width / 4 - anchors.topMargin: Style.normalBorderWidth - anchors.bottomMargin: Style.normalBorderWidth - z: 2 - color: Style.buttonBackgroundColor + TextField { + id: userStatusMessageTextField + Layout.fillWidth: true + placeholderText: qsTr("What is your status?") + placeholderTextColor: Style.ncSecondaryTextColor + text: userStatusSelectorModel.userStatusMessage + color: Style.ncTextColor + selectByMouse: true + onEditingFinished: userStatusSelectorModel.userStatusMessage = text + + property color borderColor: activeFocus ? Style.ncBlue : Style.menuBorder + + background: Rectangle { + radius: Style.slightlyRoundedButtonRadius + color: Style.backgroundColor + border.color: userStatusMessageTextField.borderColor + border.width: Style.normalBorderWidth + + Rectangle { + anchors.fill: parent + anchors.rightMargin: parent.width / 2 + z: 1 + color: Style.backgroundColor + border.color: userStatusMessageTextField.borderColor + border.width: Style.normalBorderWidth + } + + Rectangle { // We need to cover the blue border of the non-radiused rectangle + anchors.fill: parent + anchors.leftMargin: parent.width / 4 + anchors.rightMargin: parent.width / 4 + anchors.topMargin: Style.normalBorderWidth + anchors.bottomMargin: Style.normalBorderWidth + z: 2 + color: Style.backgroundColor + } + } } } - } - Popup { - id: emojiDialog - padding: 0 - margins: 0 - clip: true + ColumnLayout { + Layout.fillWidth: true + spacing: 0 - anchors.centerIn: Overlay.overlay + Repeater { + model: userStatusSelectorModel.predefinedStatuses - background: Rectangle { - color: Style.backgroundColor - border.width: Style.normalBorderWidth - border.color: Style.menuBorder - radius: Style.slightlyRoundedButtonRadius - } - - EmojiPicker { - id: emojiPicker + PredefinedStatusButton { + id: control + Layout.fillWidth: true + internalSpacing: Style.standardSpacing + fieldButton.padding + userStatusMessageTextField.padding - onChosen: { - userStatusSelectorModel.userStatusEmoji = emoji - emojiDialog.close() + emoji: modelData.icon + text: "%1 – %2".arg(modelData.message).arg(userStatusSelectorModel.clearAtReadable(modelData)) + onClicked: userStatusSelectorModel.setPredefinedStatus(modelData) + } } } - } - TextField { - id: userStatusMessageTextField - Layout.fillWidth: true - placeholderText: qsTr("What is your status?") - placeholderTextColor: Style.ncSecondaryTextColor - text: userStatusSelectorModel.userStatusMessage - color: Style.ncTextColor - selectByMouse: true - onEditingFinished: userStatusSelectorModel.userStatusMessage = text - - property color borderColor: activeFocus ? Style.ncBlue : Style.menuBorder - - background: Rectangle { - radius: Style.slightlyRoundedButtonRadius - color: Style.backgroundColor - border.color: userStatusMessageTextField.borderColor - border.width: Style.normalBorderWidth - - Rectangle { - anchors.fill: parent - anchors.rightMargin: parent.width / 2 - z: 1 - color: Style.backgroundColor - border.color: userStatusMessageTextField.borderColor - border.width: Style.normalBorderWidth + RowLayout { + Layout.fillWidth: true + spacing: Style.standardSpacing + + Label { + text: qsTr("Clear status message after") + color: Style.ncTextColor } - Rectangle { // We need to cover the blue border of the non-radiused rectangle - anchors.fill: parent - anchors.leftMargin: parent.width / 4 - anchors.rightMargin: parent.width / 4 - anchors.topMargin: Style.normalBorderWidth - anchors.bottomMargin: Style.normalBorderWidth - z: 2 - color: Style.backgroundColor + BasicComboBox { + id: clearComboBox + Layout.fillWidth: true + model: userStatusSelectorModel.clearStageTypes + textRole: "display" + valueRole: "clearStageType" + displayText: userStatusSelectorModel.clearAtDisplayString + onActivated: userStatusSelectorModel.setClearAt(currentValue) } } } - } - - Repeater { - model: userStatusSelectorModel.predefinedStatuses - PredefinedStatusButton { - id: control - Layout.fillWidth: true - Layout.leftMargin: Style.standardSpacing - Layout.rightMargin: Style.standardSpacing - internalSpacing: Style.standardSpacing + fieldButton.padding + userStatusMessageTextField.padding + ErrorBox { + width: parent.width - emoji: modelData.icon - text: "%1 – %2".arg(modelData.message).arg(userStatusSelectorModel.clearAtReadable(modelData)) - onClicked: userStatusSelectorModel.setPredefinedStatus(modelData) + visible: userStatusSelectorModel.errorMessage != "" + text: "Error: " + userStatusSelectorModel.errorMessage } } - RowLayout { - Layout.topMargin: Style.standardSpacing * 2 - Layout.leftMargin: Style.standardSpacing - Layout.rightMargin: Style.standardSpacing - Layout.bottomMargin: Style.standardSpacing - Layout.alignment: Qt.AlignTop - spacing: Style.standardSpacing - - Label { - text: qsTr("Clear status message after") - color: Style.ncTextColor - } - - BasicComboBox { - id: clearComboBox - - Layout.fillWidth: true - model: userStatusSelectorModel.clearStageTypes - textRole: "display" - valueRole: "clearStageType" - displayText: userStatusSelectorModel.clearAtDisplayString - onActivated: userStatusSelectorModel.setClearAt(currentValue) - } - } - RowLayout { - Layout.margins: Style.standardSpacing - Layout.alignment: Qt.AlignTop - + Layout.fillWidth: true + Layout.alignment: Qt.AlignBottom + + UserStatusSelectorButton { + Layout.fillWidth: true + primary: true + text: qsTr("Cancel") + onClicked: finished() + } UserStatusSelectorButton { Layout.fillWidth: true primary: true @@ -298,19 +321,11 @@ ColumnLayout { onClicked: userStatusSelectorModel.clearUserStatus() } UserStatusSelectorButton { + Layout.fillWidth: true primary: true colored: true - Layout.fillWidth: true text: qsTr("Set status message") onClicked: userStatusSelectorModel.setUserStatus() } } - - ErrorBox { - Layout.margins: Style.standardSpacing - Layout.fillWidth: true - - visible: userStatusSelectorModel.errorMessage != "" - text: "Error: " + userStatusSelectorModel.errorMessage - } } diff --git a/src/gui/UserStatusSelectorDialog.qml b/src/gui/UserStatusSelectorDialog.qml deleted file mode 100644 index 938eae547d64a..0000000000000 --- a/src/gui/UserStatusSelectorDialog.qml +++ /dev/null @@ -1,33 +0,0 @@ -import QtQuick.Window 2.15 -import Style 1.0 - -import com.nextcloud.desktopclient 1.0 as NC - -Window { - id: dialog - - title: qsTr("Set account status") - color: Style.backgroundColor - - property NC.UserStatusSelectorModel model: NC.UserStatusSelectorModel { - onFinished: dialog.close() - } - property int userIndex - onUserIndexChanged: model.load(userIndex) - - minimumWidth: view.implicitWidth - minimumHeight: view.implicitHeight - maximumWidth: view.implicitWidth - maximumHeight: view.implicitHeight - width: maximumWidth - height: maximumHeight - - visible: true - - flags: Qt.Dialog - - UserStatusSelector { - id: view - userStatusSelectorModel: model - } -} diff --git a/src/gui/UserStatusSelectorPage.qml b/src/gui/UserStatusSelectorPage.qml new file mode 100644 index 0000000000000..c89d821d91f0d --- /dev/null +++ b/src/gui/UserStatusSelectorPage.qml @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2022 by Claudio Cambra + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import Style 1.0 + +import com.nextcloud.desktopclient 1.0 as NC + +Page { + id: page + + signal finished + + property int userIndex: -1 + property NC.UserStatusSelectorModel model: NC.UserStatusSelectorModel { + userIndex: page.userIndex + onFinished: page.finished() + } + + padding: Style.standardSpacing * 2 + + background: Rectangle { + color: Style.backgroundColor + radius: Style.trayWindowRadius + } + + contentItem: UserStatusSelector { + id: userStatusSelector + userStatusSelectorModel: model + onImplicitHeightChanged: implicitHeight > page.availableHeight ? + spacing = Style.standardSpacing : spacing = Style.standardSpacing * 2 + } +} diff --git a/src/gui/tray/UserLine.qml b/src/gui/tray/UserLine.qml index e3d88888d8d08..4429aaba9d0ea 100644 --- a/src/gui/tray/UserLine.qml +++ b/src/gui/tray/UserLine.qml @@ -18,7 +18,7 @@ MenuItem { property variant comp; activeFocusOnTab: false - signal showUserStatusSelectorDialog(int id) + signal showUserStatusSelector(int id) RowLayout { id: userLineLayout @@ -183,7 +183,7 @@ MenuItem { font.pixelSize: Style.topLinePixelSize palette.windowText: Style.ncTextColor hoverEnabled: true - onClicked: showUserStatusSelectorDialog(index) + onClicked: showUserStatusSelector(index) background: Item { height: parent.height diff --git a/src/gui/tray/Window.qml b/src/gui/tray/Window.qml index 4d9e531bc32cd..bc629d8ee3857 100644 --- a/src/gui/tray/Window.qml +++ b/src/gui/tray/Window.qml @@ -1,5 +1,3 @@ -import QtQml 2.12 -import QtQml.Models 2.1 import QtQuick 2.15 import QtQuick.Window 2.3 import QtQuick.Controls 2.3 @@ -13,7 +11,7 @@ import Style 1.0 import com.nextcloud.desktopclient 1.0 -Window { +ApplicationWindow { id: trayWindow title: Systray.windowTitle @@ -53,6 +51,13 @@ Window { syncStatus.model.load(); } + background: Rectangle { + radius: Systray.useNormalWindow ? 0.0 : Style.trayWindowRadius + border.width: Style.trayWindowBorderWidth + border.color: Style.menuBorder + color: Style.backgroundColor + } + Connections { target: UserModel function onCurrentUserChanged() { @@ -78,6 +83,8 @@ Window { target: Systray function onIsOpenChanged() { + userStatusDrawer.close() + if(Systray.isOpen) { accountMenu.close(); appsMenu.close(); @@ -98,18 +105,54 @@ Window { OpacityMask { anchors.fill: parent source: ShaderEffectSource { - sourceItem: trayWindowBackground + sourceItem: trayWindowMainItem hideSource: true } maskSource: Rectangle { - width: trayWindowBackground.width - height: trayWindowBackground.height + width: trayWindow.width + height: trayWindow.height radius: Systray.useNormalWindow ? 0.0 : Style.trayWindowRadius } } - Rectangle { - id: trayWindowBackground + Drawer { + id: userStatusDrawer + width: parent.width + height: parent.height + padding: 0 + edge: Qt.BottomEdge + modal: false + visible: false + + background: Rectangle { + radius: Systray.useNormalWindow ? 0.0 : Style.trayWindowRadius + border.width: Style.trayWindowBorderWidth + border.color: Style.menuBorder + color: Style.backgroundColor + } + + property int userIndex: 0 + + function openUserStatusDrawer(index) { + console.log(`About to show dialog for user with index ${index}`); + userIndex = index; + open(); + } + + Loader { + id: userStatusContents + anchors.fill: parent + active: userStatusDrawer.visible + sourceComponent: UserStatusSelectorPage { + anchors.fill: parent + userIndex: userStatusDrawer.userIndex + onFinished: userStatusDrawer.close() + } + } + } + + Item { + id: trayWindowMainItem property bool isUnifiedSearchActive: unifiedSearchResultsListViewSkeletonLoader.active || unifiedSearchResultNothingFound.visible @@ -117,10 +160,7 @@ Window { || unifiedSearchResultsListView.visible anchors.fill: parent - radius: Systray.useNormalWindow ? 0.0 : Style.trayWindowRadius - border.width: Style.trayWindowBorderWidth - border.color: Style.menuBorder - color: Style.backgroundColor + clip: true Accessible.role: Accessible.Grouping Accessible.name: qsTr("Nextcloud desktop main dialog") @@ -128,9 +168,9 @@ Window { Rectangle { id: trayWindowHeaderBackground - anchors.left: trayWindowBackground.left - anchors.right: trayWindowBackground.right - anchors.top: trayWindowBackground.top + anchors.left: trayWindowMainItem.left + anchors.right: trayWindowMainItem.right + anchors.top: trayWindowMainItem.top height: Style.trayWindowHeaderHeight color: UserModel.currentUser.headerColor @@ -206,35 +246,12 @@ Window { userLineInstantiator.active = true; } - Loader { - id: userStatusSelectorDialogLoader - - property int userIndex - - function openDialog(newUserIndex) { - console.log(`About to show dialog for user with index ${newUserIndex}`); - userIndex = newUserIndex; - active = true; - item.show(); - } - - active: false - sourceComponent: UserStatusSelectorDialog { - userIndex: userStatusSelectorDialogLoader.userIndex - } - - onLoaded: { - item.model.load(userIndex); - item.show(); - } - } - Instantiator { id: userLineInstantiator model: UserModel delegate: UserLine { - onShowUserStatusSelectorDialog: { - userStatusSelectorDialogLoader.openDialog(model.index); + onShowUserStatusSelector: { + userStatusDrawer.openUserStatusDrawer(model.index); accountMenu.close(); } } @@ -661,8 +678,8 @@ Window { anchors { top: trayWindowHeaderBackground.bottom - left: trayWindowBackground.left - right: trayWindowBackground.right + left: trayWindowMainItem.left + right: trayWindowMainItem.right topMargin: Style.trayHorizontalMargin + controlRoot.padding leftMargin: Style.trayHorizontalMargin + controlRoot.padding @@ -681,8 +698,8 @@ Window { visible: UserModel.currentUser.unifiedSearchResultsListModel.errorString && !unifiedSearchResultsListView.visible && ! UserModel.currentUser.unifiedSearchResultsListModel.isSearchInProgress && ! UserModel.currentUser.unifiedSearchResultsListModel.currentFetchMoreInProgressProviderId text: UserModel.currentUser.unifiedSearchResultsListModel.errorString anchors.top: trayWindowUnifiedSearchInputContainer.bottom - anchors.left: trayWindowBackground.left - anchors.right: trayWindowBackground.right + anchors.left: trayWindowMainItem.left + anchors.right: trayWindowMainItem.right anchors.margins: Style.trayHorizontalMargin } @@ -690,8 +707,8 @@ Window { id: unifiedSearchResultNothingFound visible: false anchors.top: trayWindowUnifiedSearchInputContainer.bottom - anchors.left: trayWindowBackground.left - anchors.right: trayWindowBackground.right + anchors.left: trayWindowMainItem.left + anchors.right: trayWindowMainItem.right anchors.topMargin: Style.trayHorizontalMargin text: UserModel.currentUser.unifiedSearchResultsListModel.searchTerm @@ -724,9 +741,9 @@ Window { Loader { id: unifiedSearchResultsListViewSkeletonLoader anchors.top: trayWindowUnifiedSearchInputContainer.bottom - anchors.left: trayWindowBackground.left - anchors.right: trayWindowBackground.right - anchors.bottom: trayWindowBackground.bottom + anchors.left: trayWindowMainItem.left + anchors.right: trayWindowMainItem.right + anchors.bottom: trayWindowMainItem.bottom active: !unifiedSearchResultNothingFound.visible && !unifiedSearchResultsListView.visible && @@ -752,9 +769,9 @@ Window { visible: unifiedSearchResultsListView.count > 0 anchors.top: trayWindowUnifiedSearchInputContainer.bottom - anchors.left: trayWindowBackground.left - anchors.right: trayWindowBackground.right - anchors.bottom: trayWindowBackground.bottom + anchors.left: trayWindowMainItem.left + anchors.right: trayWindowMainItem.right + anchors.bottom: trayWindowMainItem.bottom ListView { id: unifiedSearchResultsListView @@ -791,19 +808,19 @@ Window { SyncStatus { id: syncStatus - visible: !trayWindowBackground.isUnifiedSearchActive + visible: !trayWindowMainItem.isUnifiedSearchActive anchors.top: trayWindowUnifiedSearchInputContainer.bottom - anchors.left: trayWindowBackground.left - anchors.right: trayWindowBackground.right + anchors.left: trayWindowMainItem.left + anchors.right: trayWindowMainItem.right } ActivityList { - visible: !trayWindowBackground.isUnifiedSearchActive + visible: !trayWindowMainItem.isUnifiedSearchActive anchors.top: syncStatus.bottom - anchors.left: trayWindowBackground.left - anchors.right: trayWindowBackground.right - anchors.bottom: trayWindowBackground.bottom + anchors.left: trayWindowMainItem.left + anchors.right: trayWindowMainItem.right + anchors.bottom: trayWindowMainItem.bottom activeFocusOnTab: true model: activityModel @@ -833,5 +850,5 @@ Window { onLoaded: refresh() } - } // Rectangle trayWindowBackground + } // Item trayWindowMainItem } diff --git a/src/gui/userstatusselectormodel.cpp b/src/gui/userstatusselectormodel.cpp index 857c71305ffd5..227d7bd4249d7 100644 --- a/src/gui/userstatusselectormodel.cpp +++ b/src/gui/userstatusselectormodel.cpp @@ -74,11 +74,26 @@ UserStatusSelectorModel::UserStatusSelectorModel(const UserStatus &userStatus, _userStatus.setIcon("😀"); } -void UserStatusSelectorModel::load(int id) +int UserStatusSelectorModel::userIndex() const { + return _userIndex; +} + +void UserStatusSelectorModel::setUserIndex(const int userIndex) +{ + if(userIndex < 0) { + qCWarning(lcUserStatusDialogModel) << "Invalid user index: " << _userIndex; + return; + } + reset(); - qCDebug(lcUserStatusDialogModel) << "Loading user status connector for user with index: " << id; - _userStatusConnector = UserModel::instance()->userStatusConnector(id); + + _userIndex = userIndex; + emit userIndexChanged(); + + qCDebug(lcUserStatusDialogModel) << "Loading user status connector for user with index: " << _userIndex; + _userStatusConnector = UserModel::instance()->userStatusConnector(_userIndex); + init(); } @@ -102,6 +117,7 @@ void UserStatusSelectorModel::reset() void UserStatusSelectorModel::init() { if (!_userStatusConnector) { + qCWarning(lcUserStatusDialogModel) << "No user status conenctor set"; return; } @@ -182,7 +198,7 @@ void UserStatusSelectorModel::setOnlineStatus(UserStatus::OnlineStatus status) _userStatus.setState(status); _userStatusConnector->setUserStatus(_userStatus); - emit onlineStatusChanged(); + emit userStatusChanged(); } QUrl UserStatusSelectorModel::onlineIcon() const @@ -234,9 +250,7 @@ QString UserStatusSelectorModel::userStatusEmoji() const void UserStatusSelectorModel::onUserStatusFetched(const UserStatus &userStatus) { - if (userStatus.state() != UserStatus::OnlineStatus::Offline) { - _userStatus.setState(userStatus.state()); - } + _userStatus.setState(userStatus.state()); _userStatus.setMessage(userStatus.message()); _userStatus.setMessagePredefined(userStatus.messagePredefined()); _userStatus.setId(userStatus.id()); @@ -247,7 +261,6 @@ void UserStatusSelectorModel::onUserStatusFetched(const UserStatus &userStatus) } emit userStatusChanged(); - emit onlineStatusChanged(); emit clearAtDisplayStringChanged(); } diff --git a/src/gui/userstatusselectormodel.h b/src/gui/userstatusselectormodel.h index 9d7e393649b5e..9137986416b9b 100644 --- a/src/gui/userstatusselectormodel.h +++ b/src/gui/userstatusselectormodel.h @@ -34,9 +34,10 @@ class UserStatusSelectorModel : public QObject { Q_OBJECT + Q_PROPERTY(int userIndex READ userIndex WRITE setUserIndex NOTIFY userIndexChanged) Q_PROPERTY(QString userStatusMessage READ userStatusMessage WRITE setUserStatusMessage NOTIFY userStatusChanged) Q_PROPERTY(QString userStatusEmoji READ userStatusEmoji WRITE setUserStatusEmoji NOTIFY userStatusChanged) - Q_PROPERTY(OCC::UserStatus::OnlineStatus onlineStatus READ onlineStatus WRITE setOnlineStatus NOTIFY onlineStatusChanged) + Q_PROPERTY(OCC::UserStatus::OnlineStatus onlineStatus READ onlineStatus WRITE setOnlineStatus NOTIFY userStatusChanged) Q_PROPERTY(QVector predefinedStatuses READ predefinedStatuses NOTIFY predefinedStatusesChanged) Q_PROPERTY(QVariantList clearStageTypes READ clearStageTypes CONSTANT) Q_PROPERTY(QString clearAtDisplayString READ clearAtDisplayString NOTIFY clearAtDisplayStringChanged) @@ -73,6 +74,8 @@ class UserStatusSelectorModel : public QObject explicit UserStatusSelectorModel(const UserStatus &userStatus, QObject *parent = nullptr); + Q_REQUIRED_RESULT int userIndex() const; + Q_REQUIRED_RESULT UserStatus::OnlineStatus onlineStatus() const; void setOnlineStatus(UserStatus::OnlineStatus status); @@ -95,16 +98,16 @@ class UserStatusSelectorModel : public QObject Q_REQUIRED_RESULT QString errorMessage() const; public slots: - void load(int id); + void setUserIndex(const int userIndex); void setUserStatus(); void clearUserStatus(); void setClearAt(const ClearStageType clearStageType); void setPredefinedStatus(const UserStatus &predefinedStatus); signals: + void userIndexChanged(); void errorMessageChanged(); void userStatusChanged(); - void onlineStatusChanged(); void clearAtDisplayStringChanged(); void predefinedStatusesChanged(); void finished(); @@ -125,6 +128,7 @@ public slots: void setError(const QString &reason); void clearError(); + int _userIndex = -1; std::shared_ptr _userStatusConnector {}; QVector _predefinedStatuses; UserStatus _userStatus; diff --git a/test/testsetuserstatusdialog.cpp b/test/testsetuserstatusdialog.cpp index 5ea487e435173..d529f1a16ee91 100644 --- a/test/testsetuserstatusdialog.cpp +++ b/test/testsetuserstatusdialog.cpp @@ -252,23 +252,23 @@ private slots: OCC::UserStatus::OnlineStatus::Offline, false, {} }); OCC::UserStatusSelectorModel model(fakeUserStatusJob); - QCOMPARE(model.onlineStatus(), OCC::UserStatus::OnlineStatus::Online); + QCOMPARE(model.onlineStatus(), OCC::UserStatus::OnlineStatus::Offline); QCOMPARE(model.userStatusMessage(), ""); QCOMPARE(model.userStatusEmoji(), "😀"); QCOMPARE(model.clearAtDisplayString(), tr("Don't clear")); } - void testSetOnlineStatus_emitOnlineStatusChanged() + void testSetOnlineStatus_emiUserStatusChanged() { const OCC::UserStatus::OnlineStatus onlineStatus(OCC::UserStatus::OnlineStatus::Invisible); auto fakeUserStatusJob = std::make_shared(); OCC::UserStatusSelectorModel model(fakeUserStatusJob); - QSignalSpy onlineStatusChangedSpy(&model, - &OCC::UserStatusSelectorModel::onlineStatusChanged); + QSignalSpy userStatusChangedSpy(&model, + &OCC::UserStatusSelectorModel::userStatusChanged); model.setOnlineStatus(onlineStatus); - QCOMPARE(onlineStatusChangedSpy.count(), 1); + QCOMPARE(userStatusChangedSpy.count(), 1); } void testSetUserStatus_setCustomMessage_userStatusSetCorrect() diff --git a/theme/Style/Style.qml b/theme/Style/Style.qml index 06970bae098de..6d1adb5ec74c5 100644 --- a/theme/Style/Style.qml +++ b/theme/Style/Style.qml @@ -35,6 +35,8 @@ QtObject { property int trayWindowBorderWidth: variableSize(1) property int trayWindowHeaderHeight: variableSize(60) property int trayHorizontalMargin: 10 + property int trayModalWidth: 380 + property int trayModalHeight: 490 property int trayListItemIconSize: accountAvatarSize property real thumbnailImageSizeReduction: 0.2 // We reserve some space within the thumbnail "item", here about 20%. // This is because we need to also add the added/modified icon and we @@ -43,6 +45,7 @@ QtObject { // images, which will work so long as the thumbnails are left aligned property int standardSpacing: 10 + property int smallSpacing: 5 property int minActivityHeight: variableSize(40)