User dialog now looks in line with the rest of the desktop client tray

Signed-off-by: Claudio Cambra <claudio.cambra@gmail.com>
This commit is contained in:
Claudio Cambra 2022-06-08 02:30:22 +02:00
parent 73a5aa141b
commit ee3e0d3351
9 changed files with 444 additions and 51 deletions

View File

@ -3,6 +3,9 @@
<file>src/gui/UserStatusSelector.qml</file>
<file>src/gui/UserStatusSelectorDialog.qml</file>
<file>src/gui/EmojiPicker.qml</file>
<file>src/gui/UserStatusSelectorButton.qml</file>
<file>src/gui/PredefinedStatusButton.qml</file>
<file>src/gui/BasicComboBox.qml</file>
<file>src/gui/ErrorBox.qml</file>
<file>src/gui/tray/Window.qml</file>
<file>src/gui/tray/UserLine.qml</file>

99
src/gui/BasicComboBox.qml Normal file
View File

@ -0,0 +1,99 @@
/*
* Copyright (C) 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.Dialogs 1.3
import QtQuick.Layouts 1.15
import QtQuick.Controls 2.15
import QtGraphicalEffects 1.0
import Style 1.0
ComboBox {
id: clearComboBox
padding: Style.standardSpacing
background: Rectangle {
radius: Style.slightlyRoundedButtonRadius
color: Style.buttonBackgroundColor
opacity: clearComboBox.hovered ? Style.hoverOpacity : 1.0
}
contentItem: Label {
leftPadding: 0
rightPadding: clearComboBox.indicator.width + clearComboBox.spacing
text: clearComboBox.displayText
color: Style.ncTextColor
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
}
indicator: ColorOverlay {
x: clearComboBox.width - clearComboBox.rightPadding
y: clearComboBox.topPadding + (clearComboBox.availableHeight - height) / 2
cached: true
color: Style.ncTextColor
width: source.width
height: source.height
source: Image {
horizontalAlignment: Qt.AlignRight
verticalAlignment: Qt.AlignVCenter
source: "qrc:///client/theme/white/caret-down.svg"
sourceSize.width: Style.accountDropDownCaretSize
sourceSize.height: Style.accountDropDownCaretSize
Accessible.role: Accessible.PopupMenu
Accessible.name: qsTr("Clear status message menu")
}
}
popup: Popup {
y: clearComboBox.height - Style.normalBorderWidth
width: clearComboBox.width
implicitHeight: contentItem.implicitHeight
padding: Style.normalBorderWidth
contentItem: ListView {
clip: true
implicitHeight: contentHeight
model: clearComboBox.popup.visible ? clearComboBox.delegateModel : null
currentIndex: clearComboBox.highlightedIndex
ScrollIndicator.vertical: ScrollIndicator { }
}
background: Rectangle {
color: Style.backgroundColor
border.color: Style.menuBorder
radius: Style.slightlyRoundedButtonRadius
}
}
delegate: ItemDelegate {
id: clearStatusDelegate
width: clearComboBox.width
contentItem: Label {
text: modelData
color: Style.ncTextColor
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
}
highlighted: clearComboBox.highlightedIndex === index
background: Rectangle {
color: clearStatusDelegate.highlighted || clearStatusDelegate.hovered ? Style.lightHover : Style.backgroundColor
}
}
}

View File

@ -16,6 +16,7 @@ import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import Style 1.0
import com.nextcloud.desktopclient 1.0 as NC
ColumnLayout {
@ -34,6 +35,7 @@ ColumnLayout {
ListView {
id: headerLayout
Layout.fillWidth: true
Layout.margins: 1
implicitWidth: contentItem.childrenRect.width
implicitHeight: metrics.height * 2
@ -42,24 +44,32 @@ ColumnLayout {
model: emojiModel.emojiCategoriesModel
delegate: ItemDelegate {
id: headerDelegate
width: metrics.height * 2
height: headerLayout.height
contentItem: Text {
background: Rectangle {
color: Style.lightHover
visible: ListView.isCurrentItem || headerDelegate.highlighted || headerDelegate.checked || headerDelegate.down || headerDelegate.hovered
radius: Style.slightlyRoundedButtonRadius
}
contentItem: Label {
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
text: emoji
color: Style.ncTextColor
}
Rectangle {
anchors.bottom: parent.bottom
width: parent.width
height: 2
height: Style.thickBorderWidth
visible: ListView.isCurrentItem
color: "grey"
color: Style.menuBorder
}
@ -71,15 +81,16 @@ ColumnLayout {
}
Rectangle {
height: 1
height: Style.normalBorderWidth
Layout.fillWidth: true
color: "grey"
color: Style.menuBorder
}
GridView {
Layout.fillWidth: true
Layout.fillHeight: true
Layout.preferredHeight: metrics.height * 8
Layout.margins: Style.normalBorderWidth
cellWidth: metrics.height * 2
cellHeight: metrics.height * 2
@ -90,13 +101,22 @@ ColumnLayout {
model: emojiModel.model
delegate: ItemDelegate {
id: emojiDelegate
width: metrics.height * 2
height: metrics.height * 2
contentItem: Text {
anchors.centerIn: parent
background: Rectangle {
color: Style.lightHover
visible: ListView.isCurrentItem || emojiDelegate.highlighted || emojiDelegate.checked || emojiDelegate.down || emojiDelegate.hovered
radius: Style.slightlyRoundedButtonRadius
}
contentItem: Label {
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
text: modelData === undefined ? "" : modelData.unicode
color: Style.ncTextColor
}
onClicked: {

View File

@ -1,4 +1,5 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import Style 1.0
@ -19,7 +20,7 @@ Item {
border.color: errorBox.borderColor
}
Text {
Label {
id: errorMessage
anchors.fill: parent

View File

@ -0,0 +1,51 @@
/*
* Copyright (C) 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.Dialogs 1.3
import QtQuick.Layouts 1.15
import QtQuick.Controls 2.15
import Style 1.0
AbstractButton {
id: root
hoverEnabled: true
topPadding: Style.standardSpacing
bottomPadding: Style.standardSpacing
leftPadding: Style.standardSpacing / 2
rightPadding: Style.standardSpacing / 2
property real internalSpacing: Style.standardSpacing
property string emoji: ""
background: Rectangle {
color: root.hovered || root.checked ? Style.lightHover : "transparent"
radius: Style.slightlyRoundedButtonRadius
}
contentItem: Row {
spacing: internalSpacing
Label {
text: emoji
}
Label {
text: root.text
color: Style.ncTextColor
}
}
}

View File

@ -19,103 +19,182 @@ import QtQuick.Controls 2.15
import QtQuick.Window 2.15
import com.nextcloud.desktopclient 1.0 as NC
import Style 1.0
ColumnLayout {
id: rootLayout
spacing: 0
property NC.UserStatusSelectorModel userStatusSelectorModel
Label {
Layout.topMargin: 16
Layout.leftMargin: 8
Layout.rightMargin: 8
Layout.bottomMargin: 8
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 {
Layout.margins: 8
id: topButtonsLayout
Layout.margins: Style.standardSpacing
Layout.alignment: Qt.AlignTop
columns: 2
rows: 2
columnSpacing: 8
rowSpacing: 8
columnSpacing: Style.standardSpacing
rowSpacing: Style.standardSpacing
Button {
Layout.fillWidth: true
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.setOnlineStatus(NC.UserStatus.Online)
implicitWidth: 100
}
Button {
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.setOnlineStatus(NC.UserStatus.Away)
implicitWidth: 100
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)
}
Button {
Layout.fillWidth: true
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.setOnlineStatus(NC.UserStatus.DoNotDisturb)
implicitWidth: 100
}
Button {
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.setOnlineStatus(NC.UserStatus.Invisible)
implicitWidth: 100
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: 16
Layout.leftMargin: 8
Layout.rightMargin: 8
Layout.bottomMargin: 8
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: 8
Layout.leftMargin: 8
Layout.rightMargin: 8
Layout.bottomMargin: 16
Layout.topMargin: Style.standardSpacing
Layout.leftMargin: Style.standardSpacing
Layout.rightMargin: Style.standardSpacing
Layout.bottomMargin: Style.standardSpacing * 2
Layout.alignment: Qt.AlignTop
Layout.fillWidth: true
Button {
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
@ -131,37 +210,74 @@ ColumnLayout {
id: userStatusMessageTextField
Layout.fillWidth: true
placeholderText: qsTr("What is your status?")
placeholderTextColor: Style.ncSecondaryTextColor
text: userStatusSelectorModel.userStatusMessage
color: Style.ncTextColor
selectByMouse: true
onEditingFinished: userStatusSelectorModel.setUserStatusMessage(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
}
}
}
}
Repeater {
model: userStatusSelectorModel.predefinedStatusesCount
Button {
PredefinedStatusButton {
id: control
Layout.fillWidth: true
flat: !hovered
hoverEnabled: true
text: userStatusSelectorModel.predefinedStatus(index).icon + " <b>" + userStatusSelectorModel.predefinedStatus(index).message + "</b> - " + userStatusSelectorModel.predefinedStatusClearAt(index)
Layout.leftMargin: Style.standardSpacing
Layout.rightMargin: Style.standardSpacing
internalSpacing: Style.standardSpacing + fieldButton.padding + userStatusMessageTextField.padding
emoji: userStatusSelectorModel.predefinedStatus(index).icon
text: "<b>" + userStatusSelectorModel.predefinedStatus(index).message + "</b> - " + userStatusSelectorModel.predefinedStatusClearAt(index)
onClicked: userStatusSelectorModel.setPredefinedStatus(index)
}
}
RowLayout {
Layout.topMargin: 16
Layout.leftMargin: 8
Layout.rightMargin: 8
Layout.bottomMargin: 8
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
}
ComboBox {
BasicComboBox {
id: clearComboBox
Layout.fillWidth: true
model: userStatusSelectorModel.clearAtValues
displayText: userStatusSelectorModel.clearAt
@ -170,16 +286,18 @@ ColumnLayout {
}
RowLayout {
Layout.margins: 8
Layout.margins: Style.standardSpacing
Layout.alignment: Qt.AlignTop
Button {
UserStatusSelectorButton {
Layout.fillWidth: true
primary: true
text: qsTr("Clear status message")
onClicked: userStatusSelectorModel.clearUserStatus()
}
Button {
highlighted: true
UserStatusSelectorButton {
primary: true
colored: true
Layout.fillWidth: true
text: qsTr("Set status message")
onClicked: userStatusSelectorModel.setUserStatus()
@ -187,7 +305,7 @@ ColumnLayout {
}
ErrorBox {
Layout.margins: 8
Layout.margins: Style.standardSpacing
Layout.fillWidth: true
visible: userStatusSelectorModel.errorMessage != ""

View File

@ -0,0 +1,91 @@
/*
* Copyright (C) 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.6
import QtQuick.Dialogs 1.3
import QtQuick.Layouts 1.15
import QtQuick.Controls 2.15
import Style 1.0
AbstractButton {
id: root
property string secondaryText: ""
property bool colored: false
property bool primary: false
property bool highlighted: false
readonly property bool showBorder: hovered || highlighted || checked
hoverEnabled: true
padding: Style.standardSpacing
background: Rectangle {
radius: root.primary ? Style.veryRoundedButtonRadius : Style.mediumRoundedButtonRadius
color: root.colored ? Style.ncBlue : Style.buttonBackgroundColor
opacity: root.colored && root.hovered ? Style.hoverOpacity : 1.0
border.color: Style.ncBlue
border.width: root.showBorder ? root.primary ? Style.normalBorderWidth : Style.thickBorderWidth : 0
}
contentItem: GridLayout {
columns: 2
rows: 2
columnSpacing: Style.standardSpacing
rowSpacing: Style.standardSpacing / 2
Image {
Layout.column: 0
Layout.columnSpan: root.text === "" && root.secondaryText == "" ? 2 : 1
Layout.row: 0
Layout.rowSpan: 2
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
source: root.icon.source
visible: root.icon.source !== ""
}
Label {
Layout.column: root.icon.source === "" ? 0 : 1
Layout.columnSpan: root.icon.source === "" ? 2 : 1
Layout.row: 0
Layout.rowSpan: root.secondaryText === "" ? 2 : 1
Layout.fillWidth: true
horizontalAlignment: root.primary ? Text.AlignHCenter : Text.AlignLeft
verticalAlignment: Text.AlignVCenter
text: root.text
wrapMode: Text.Wrap
color: root.colored ? Style.ncHeaderTextColor : Style.ncTextColor
font.bold: root.primary
}
Label {
Layout.column: root.icon.source === "" ? 0 : 1
Layout.columnSpan: root.icon.source === "" ? 2 : 1
Layout.row: 1
Layout.fillWidth: true
horizontalAlignment: root.primary ? Text.AlignHCenter : Text.AlignLeft
verticalAlignment: Text.AlignVCenter
text: root.secondaryText
wrapMode: Text.Wrap
color: Style.ncSecondaryTextColor
visible: root.secondaryText !== ""
}
}
}

View File

@ -1,4 +1,5 @@
import QtQuick.Window 2.15
import Style 1.0
import com.nextcloud.desktopclient 1.0 as NC
@ -6,6 +7,7 @@ Window {
id: dialog
title: qsTr("Set account status")
color: Style.backgroundColor
property NC.UserStatusSelectorModel model: NC.UserStatusSelectorModel {
onFinished: dialog.close()

View File

@ -13,8 +13,9 @@ QtObject {
readonly property color ncSecondaryTextColor: "#808080"
readonly property color ncHeaderTextColor: "white"
readonly property color lightHover: Theme.darkMode ? Qt.lighter(backgroundColor, 2) : Qt.darker(backgroundColor, 1.05)
readonly property color menuBorder: ncSecondaryTextColor
readonly property color menuBorder: Theme.darkMode ? Qt.lighter(backgroundColor, 2.5) : Qt.darker(backgroundColor, 1.5)
readonly property color backgroundColor: Theme.systemPalette.base
readonly property color buttonBackgroundColor: Theme.systemPalette.button
// ErrorBox colors
readonly property color errorBoxTextColor: Theme.errorBoxTextColor
@ -48,6 +49,13 @@ QtObject {
property int currentAccountButtonRadius: 2
property int currentAccountLabelWidth: 128
property int normalBorderWidth: 1
property int thickBorderWidth: 2
property int veryRoundedButtonRadius: 100
property int mediumRoundedButtonRadius: 8
property int slightlyRoundedButtonRadius: 5
property double hoverOpacity: 0.7
property url stateOnlineImageSource: Theme.stateOnlineImageSource
property url stateOfflineImageSource: Theme.stateOfflineImageSource