Make UserStatusSelector a dismissible page pushed onto the tray window

Signed-off-by: Claudio Cambra <claudio.cambra@gmail.com>
This commit is contained in:
Claudio Cambra 2022-07-20 19:56:57 +02:00 committed by Camila (Rebase PR Action)
parent b90e79a6a0
commit d86f25d215
10 changed files with 426 additions and 362 deletions

View file

@ -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>

View file

@ -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
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.
Layout.margins: Style.standardSpacing
Layout.alignment: Qt.AlignTop
columns: 2
rows: 2
columnSpacing: Style.standardSpacing
rowSpacing: Style.standardSpacing
id: mainContentsLayout
spacing: rootLayout.spacing
property int maxButtonHeight: 0
function updateMaxButtonHeight(newHeight) {
maxButtonHeight = Math.max(maxButtonHeight, newHeight)
}
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
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
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
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)
}
}
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
}
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
Layout.fillHeight: true
Layout.alignment: Qt.AlignTop
spacing: 0
ColumnLayout {
id: statusButtonsLayout
width: parent.width
spacing: Style.smallSpacing
UserStatusSelectorButton {
id: fieldButton
Label {
Layout.fillWidth: true
Layout.bottomMargin: Style.smallSpacing
horizontalAlignment: Text.AlignHCenter
font.bold: true
text: qsTr("Online status")
color: Style.ncTextColor
}
Layout.preferredWidth: userStatusMessageTextField.height
Layout.preferredHeight: userStatusMessageTextField.height
GridLayout {
id: topButtonsLayout
columns: 2
rows: 2
columnSpacing: statusButtonsLayout.spacing
rowSpacing: statusButtonsLayout.spacing
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
property int maxButtonHeight: 0
function updateMaxButtonHeight(newHeight) {
maxButtonHeight = Math.max(maxButtonHeight, newHeight)
}
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
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
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
Layout.fillWidth: true
implicitWidth: 200 // Pretty much a hack to ensure all the buttons are equal in width
}
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)
}
}
}
Popup {
id: emojiDialog
padding: 0
margins: 0
clip: true
ColumnLayout {
id: userStatusMessageLayout
width: parent.width
spacing: Style.smallSpacing
anchors.centerIn: Overlay.overlay
background: Rectangle {
color: Style.backgroundColor
border.width: Style.normalBorderWidth
border.color: Style.menuBorder
radius: Style.slightlyRoundedButtonRadius
Label {
Layout.fillWidth: true
Layout.bottomMargin: Style.smallSpacing
horizontalAlignment: Text.AlignHCenter
font.bold: true
text: qsTr("Status message")
color: Style.ncTextColor
}
EmojiPicker {
id: emojiPicker
onChosen: {
userStatusSelectorModel.userStatusEmoji = emoji
emojiDialog.close()
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
}
}
}
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()
}
}
}
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
}
}
}
}
ColumnLayout {
Layout.fillWidth: true
spacing: 0
Repeater {
model: userStatusSelectorModel.predefinedStatuses
PredefinedStatusButton {
id: control
Layout.fillWidth: true
internalSpacing: Style.standardSpacing + fieldButton.padding + userStatusMessageTextField.padding
emoji: modelData.icon
text: "<b>%1</b> %2".arg(modelData.message).arg(userStatusSelectorModel.clearAtReadable(modelData))
onClicked: userStatusSelectorModel.setPredefinedStatus(modelData)
}
}
}
RowLayout {
Layout.fillWidth: true
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)
}
}
}
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
ErrorBox {
width: parent.width
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
}
}
visible: userStatusSelectorModel.errorMessage != ""
text: "<b>Error:</b> " + userStatusSelectorModel.errorMessage
}
}
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
emoji: modelData.icon
text: "<b>%1</b> %2".arg(modelData.message).arg(userStatusSelectorModel.clearAtReadable(modelData))
onClicked: userStatusSelectorModel.setPredefinedStatus(modelData)
}
}
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
}
}

View file

@ -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
}
}

View file

@ -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
}
}

View file

@ -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

View file

@ -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
}

View file

@ -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();
}

View file

@ -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;

View file

@ -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()

View file

@ -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)