diff options
author | Claudio Cambra <claudio.cambra@gmail.com> | 2022-07-20 20:56:57 +0300 |
---|---|---|
committer | Claudio Cambra <claudio.cambra@gmail.com> | 2022-08-05 22:56:40 +0300 |
commit | 02909e4a9b9738c9ec98050724de0ce6c6c68e0d (patch) | |
tree | 3a1d294a8e3e2c5386eb14d6e2e8f8661027cb57 | |
parent | 40951974cc3a21c1abed559435b93c3ab7eedfef (diff) |
Make UserStatusSelector a dismissible page pushed onto the tray windowfeature/modal-userstatusselector
Signed-off-by: Claudio Cambra <claudio.cambra@gmail.com>
-rw-r--r-- | resources.qrc | 2 | ||||
-rw-r--r-- | src/gui/UserStatusSelector.qml | 497 | ||||
-rw-r--r-- | src/gui/UserStatusSelectorDialog.qml | 33 | ||||
-rw-r--r-- | src/gui/UserStatusSelectorPage.qml | 45 | ||||
-rw-r--r-- | src/gui/tray/UserLine.qml | 4 | ||||
-rw-r--r-- | src/gui/tray/Window.qml | 137 | ||||
-rw-r--r-- | src/gui/userstatusselectormodel.cpp | 29 | ||||
-rw-r--r-- | src/gui/userstatusselectormodel.h | 10 | ||||
-rw-r--r-- | test/testsetuserstatusdialog.cpp | 10 | ||||
-rw-r--r-- | theme/Style/Style.qml | 3 |
10 files changed, 417 insertions, 353 deletions
diff --git a/resources.qrc b/resources.qrc index 6aeb159b3..d9db7bc05 100644 --- a/resources.qrc +++ b/resources.qrc @@ -1,7 +1,7 @@ <RCC> <qresource prefix="/qml"> <file>src/gui/UserStatusSelector.qml</file> - <file>src/gui/UserStatusSelectorDialog.qml</file> + <file>src/gui/UserStatusSelectorPage.qml</file> <file>src/gui/EmojiPicker.qml</file> <file>src/gui/UserStatusSelectorButton.qml</file> <file>src/gui/PredefinedStatusButton.qml</file> diff --git a/src/gui/UserStatusSelector.qml b/src/gui/UserStatusSelector.qml index f480597cb..68809ae4b 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: "<b>%1</b> – %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: "<b>%1</b> – %2".arg(modelData.message).arg(userStatusSelectorModel.clearAtReadable(modelData)) - onClicked: userStatusSelectorModel.setPredefinedStatus(modelData) + visible: userStatusSelectorModel.errorMessage != "" + text: "<b>Error:</b> " + 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: "<b>Error:</b> " + userStatusSelectorModel.errorMessage - } } diff --git a/src/gui/UserStatusSelectorDialog.qml b/src/gui/UserStatusSelectorDialog.qml deleted file mode 100644 index 938eae547..000000000 --- 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 000000000..c89d821d9 --- /dev/null +++ b/src/gui/UserStatusSelectorPage.qml @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2022 by Claudio Cambra <claudio.cambra@nextcloud.com> + * + * 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 e3d88888d..4429aaba9 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 22c92f648..f782d6dc3 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 onNewUserSelected() {
@@ -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 857c71305..227d7bd42 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 9d7e39364..913798641 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<OCC::UserStatus> predefinedStatuses READ predefinedStatuses NOTIFY predefinedStatusesChanged) Q_PROPERTY(QVariantList clearStageTypes READ clearStageTypes CONSTANT) Q_PROPERTY(QString clearAtDisplayString READ clearAtDisplayString NOTIFY clearAtDisplayStringChanged) @@ -73,6 +74,8 @@ public: 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 @@ public: 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 @@ private: void setError(const QString &reason); void clearError(); + int _userIndex = -1; std::shared_ptr<UserStatusConnector> _userStatusConnector {}; QVector<UserStatus> _predefinedStatuses; UserStatus _userStatus; diff --git a/test/testsetuserstatusdialog.cpp b/test/testsetuserstatusdialog.cpp index 5ea487e43..d529f1a16 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<FakeUserStatusConnector>(); 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 06970bae0..6d1adb5ec 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)
|