Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/desktop.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorClaudio Cambra <claudio.cambra@gmail.com>2022-07-20 20:56:57 +0300
committerClaudio Cambra <claudio.cambra@gmail.com>2022-08-05 22:56:40 +0300
commit02909e4a9b9738c9ec98050724de0ce6c6c68e0d (patch)
tree3a1d294a8e3e2c5386eb14d6e2e8f8661027cb57
parent40951974cc3a21c1abed559435b93c3ab7eedfef (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.qrc2
-rw-r--r--src/gui/UserStatusSelector.qml497
-rw-r--r--src/gui/UserStatusSelectorDialog.qml33
-rw-r--r--src/gui/UserStatusSelectorPage.qml45
-rw-r--r--src/gui/tray/UserLine.qml4
-rw-r--r--src/gui/tray/Window.qml137
-rw-r--r--src/gui/userstatusselectormodel.cpp29
-rw-r--r--src/gui/userstatusselectormodel.h10
-rw-r--r--test/testsetuserstatusdialog.cpp10
-rw-r--r--theme/Style/Style.qml3
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)