feat(GODT-2586): Two-columns layout for account details.

This commit is contained in:
Xavier Michelon
2023-04-21 10:20:32 +02:00
parent 3b297fa37b
commit ce5a559926
3 changed files with 97 additions and 108 deletions

View File

@ -27,67 +27,55 @@ Item {
property var notifications property var notifications
property var user property var user
signal showSignIn() signal showSignIn
signal showSetupGuide(var user, string address) signal showSetupGuide(var user, string address)
property int _leftMargin: 64 property int _contentWidth: 640
property int _rightMargin: 64
property int _topMargin: 32 property int _topMargin: 32
property int _detailsTopMargin: 25 property int _detailsMargin: 25
property int _bottomMargin: 12
property int _spacing: 20 property int _spacing: 20
property int _lineWidth: 1 property int _lineThickness: 1
property bool _connected: root.user ? root.user.state === EUserState.Connected : false
ScrollView {
id: scrollView
clip: true
Rectangle {
anchors.fill: parent anchors.fill: parent
Component.onCompleted: contentItem.boundsBehavior = Flickable.StopAtBounds // Disable the springy effect when scroll reaches top/bottom. color: root.colorScheme.background_weak
Item { ScrollView {
// can't use parent here because parent is not ScrollView (Flickable inside contentItem inside ScrollView) id: scrollView
width: scrollView.availableWidth anchors.fill: parent
height: scrollView.availableHeight Component.onCompleted: contentItem.boundsBehavior = Flickable.StopAtBounds
implicitHeight: children[0].implicitHeight + children[0].anchors.topMargin + children[0].anchors.bottomMargin
// do not set implicitWidth because implicit width of ColumnLayout will be equal to maximum implicit width of
// internal items. And if one of internal items would be a Text or Label - implicit width of those is always
// equal to non-wrapped text (i.e. one line only). That will lead to enabling horizontal scroll when not needed
implicitWidth: width
ColumnLayout { ColumnLayout {
id: topLevelColumnLayout
anchors.fill: parent
spacing: 0 spacing: 0
anchors.fill: parent
Rectangle { Rectangle {
id: topRectangle id: topArea
color: root.colorScheme.background_norm color: root.colorScheme.background_norm
clip: true
implicitHeight: children[0].implicitHeight + children[0].anchors.topMargin + children[0].anchors.bottomMargin
implicitWidth: children[0].implicitWidth + children[0].anchors.leftMargin + children[0].anchors.rightMargin
Layout.fillWidth: true Layout.fillWidth: true
implicitHeight: childrenRect.height
ColumnLayout { ColumnLayout {
spacing: root._spacing id: topLayout
width: _contentWidth
anchors.horizontalCenter: parent.horizontalCenter
spacing: _spacing
anchors.fill: parent RowLayout {
anchors.leftMargin: root._leftMargin // account delegate with action buttons
anchors.rightMargin: root._rightMargin
anchors.topMargin: root._topMargin
anchors.bottomMargin: root._bottomMargin
RowLayout { // account delegate with action buttons
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: _topMargin
AccountDelegate { AccountDelegate {
Layout.fillWidth: true Layout.fillWidth: true
colorScheme: root.colorScheme colorScheme: root.colorScheme
user: root.user user: root.user
type: AccountDelegate.LargeView type: AccountDelegate.LargeView
enabled: root.user ? (root.user.state === EUserState.Connected) : false enabled: _connected
} }
Button { Button {
@ -95,10 +83,11 @@ Item {
colorScheme: root.colorScheme colorScheme: root.colorScheme
text: qsTr("Sign out") text: qsTr("Sign out")
secondary: true secondary: true
visible: root.user ? (root.user.state === EUserState.Connected) : false visible: _connected
onClicked: { onClicked: {
if (!root.user) return if (!root.user)
root.user.logout() return;
root.user.logout();
} }
} }
@ -109,8 +98,9 @@ Item {
secondary: true secondary: true
visible: root.user ? (root.user.state === EUserState.SignedOut) : false visible: root.user ? (root.user.state === EUserState.SignedOut) : false
onClicked: { onClicked: {
if (!root.user) return if (!root.user)
root.showSignIn() return;
root.showSignIn();
} }
} }
@ -120,8 +110,9 @@ Item {
icon.source: "/qml/icons/ic-trash.svg" icon.source: "/qml/icons/ic-trash.svg"
secondary: true secondary: true
onClicked: { onClicked: {
if (!root.user) return if (!root.user)
root.notifications.askDeleteAccount(root.user) return;
root.notifications.askDeleteAccount(root.user);
} }
visible: root.user ? root.user.state !== EUserState.Locked : false visible: root.user ? root.user.state !== EUserState.Locked : false
} }
@ -129,7 +120,7 @@ Item {
Rectangle { Rectangle {
Layout.fillWidth: true Layout.fillWidth: true
height: root._lineWidth height: root._lineThickness
color: root.colorScheme.border_weak color: root.colorScheme.border_weak
} }
@ -139,12 +130,12 @@ Item {
actionText: qsTr("Configure") actionText: qsTr("Configure")
description: qsTr("Using the mailbox details below (re)configure your client.") description: qsTr("Using the mailbox details below (re)configure your client.")
type: SettingsItem.Button type: SettingsItem.Button
enabled: root.user ? root.user.state === EUserState.Connected : false visible: _connected && (!root.user.splitMode) || (root.user.addresses.length === 1)
visible: root.user ? !root.user.splitMode || root.user.addresses.length==1 : false
showSeparator: splitMode.visible showSeparator: splitMode.visible
onClicked: { onClicked: {
if (!root.user) return if (!root.user)
root.showSetupGuide(root.user, user.addresses[0]) return;
root.showSetupGuide(root.user, user.addresses[0]);
} }
Layout.fillWidth: true Layout.fillWidth: true
@ -157,15 +148,14 @@ Item {
description: qsTr("Setup multiple email addresses individually.") description: qsTr("Setup multiple email addresses individually.")
type: SettingsItem.Toggle type: SettingsItem.Toggle
checked: root.user ? root.user.splitMode : false checked: root.user ? root.user.splitMode : false
visible: root.user ? root.user.addresses.length > 1 : false visible: _connected && root.user.addresses.length > 1
enabled: root.user ? (root.user.state === EUserState.Connected) : false
showSeparator: addressSelector.visible showSeparator: addressSelector.visible
onClicked: { onClicked: {
if (!splitMode.checked){ if (!splitMode.checked) {
root.notifications.askEnableSplitMode(user) root.notifications.askEnableSplitMode(user);
} else { } else {
addressSelector.currentIndex = 0 addressSelector.currentIndex = 0;
root.user.toggleSplitMode(!splitMode.checked) root.user.toggleSplitMode(!splitMode.checked);
} }
} }
@ -174,8 +164,8 @@ Item {
RowLayout { RowLayout {
Layout.fillWidth: true Layout.fillWidth: true
enabled: root.user ? (root.user.state === EUserState.Connected) : false Layout.bottomMargin: _spacing
visible: root.user ? root.user.splitMode : false visible: _connected && root.user.splitMode
ComboBox { ComboBox {
id: addressSelector id: addressSelector
@ -189,60 +179,68 @@ Item {
text: qsTr("Configure") text: qsTr("Configure")
secondary: true secondary: true
onClicked: { onClicked: {
if (!root.user) return if (!root.user)
root.showSetupGuide(root.user, addressSelector.displayText) return;
root.showSetupGuide(root.user, addressSelector.displayText);
} }
} }
} }
Rectangle {
height: 0
} // just for some extra space before separator
} }
} }
Rectangle { Rectangle {
id: bottomArea
Layout.fillWidth: true
implicitHeight: bottomLayout.implicitHeight
color: root.colorScheme.background_weak color: root.colorScheme.background_weak
implicitHeight: children[0].implicitHeight + children[0].anchors.topMargin + children[0].anchors.bottomMargin
implicitWidth: children[0].implicitWidth + children[0].anchors.leftMargin + children[0].anchors.rightMargin
Layout.fillWidth: true
ColumnLayout { ColumnLayout {
id: configuration id: bottomLayout
width: _contentWidth
anchors.fill: parent anchors.horizontalCenter: parent.horizontalCenter
anchors.leftMargin: root._leftMargin spacing: _spacing
anchors.rightMargin: root._rightMargin visible: _connected
anchors.topMargin: root._detailsTopMargin
anchors.bottomMargin: root._spacing
spacing: root._spacing
visible: root.user ? (root.user.state === EUserState.Connected) : false
property string currentAddress: addressSelector.displayText
Label { Label {
Layout.topMargin: _detailsMargin
colorScheme: root.colorScheme colorScheme: root.colorScheme
text: qsTr("Mailbox details") text: qsTr("Mailbox details")
type: Label.Body_semibold type: Label.Body_semibold
} }
Configuration { RowLayout {
colorScheme: root.colorScheme id: configuration
title: qsTr("IMAP") spacing: _spacing
hostname: Backend.hostname Layout.fillWidth: true
port: Backend.imapPort.toString() Layout.fillHeight: true
username: configuration.currentAddress
password: root.user ? root.user.password : ""
security : Backend.useSSLForIMAP ? "SSL" : "STARTTLS"
}
Configuration { property string currentAddress: addressSelector.displayText
colorScheme: root.colorScheme
title: qsTr("SMTP") Configuration {
hostname : Backend.hostname Layout.fillWidth: true
port : Backend.smtpPort.toString() colorScheme: root.colorScheme
username : configuration.currentAddress title: qsTr("IMAP")
password : root.user ? root.user.password : "" hostname: Backend.hostname
security : Backend.useSSLForSMTP ? "SSL" : "STARTTLS" port: Backend.imapPort.toString()
username: configuration.currentAddress
password: root.user ? root.user.password : ""
security: Backend.useSSLForIMAP ? "SSL" : "STARTTLS"
}
Configuration {
Layout.fillWidth: true
colorScheme: root.colorScheme
title: qsTr("SMTP")
hostname: Backend.hostname
port: Backend.smtpPort.toString()
username: configuration.currentAddress
password: root.user ? root.user.password : ""
security: Backend.useSSLForSMTP ? "SSL" : "STARTTLS"
}
} }
} }
} }

View File

@ -61,8 +61,6 @@ Rectangle {
type: Label.Body_semibold type: Label.Body_semibold
} }
Item{}
ConfigurationItem{ colorScheme: root.colorScheme; label: qsTr("Hostname") ; value: root.hostname } ConfigurationItem{ colorScheme: root.colorScheme; label: qsTr("Hostname") ; value: root.hostname }
ConfigurationItem{ colorScheme: root.colorScheme; label: qsTr("Port") ; value: root.port } ConfigurationItem{ colorScheme: root.colorScheme; label: qsTr("Port") ; value: root.port }
ConfigurationItem{ colorScheme: root.colorScheme; label: qsTr("Username") ; value: root.username } ConfigurationItem{ colorScheme: root.colorScheme; label: qsTr("Username") ; value: root.username }

View File

@ -28,25 +28,18 @@ import "tests"
ApplicationWindow { ApplicationWindow {
id: root id: root
colorScheme: ProtonStyle.currentStyle
width: 960
height: 576
visible: true visible: true
minimumHeight: contentLayout.implicitHeight
minimumWidth: contentLayout.implicitWidth
colorScheme: ProtonStyle.currentStyle property int _defaultWidth: 1080
property int _defaultHeight: 780
width: _defaultWidth
height: _defaultHeight
minimumWidth: _defaultWidth
property var notifications property var notifications
// This is needed because on MacOS if first window shown is not transparent -
// all other windows of application will not have transparent background (black
// instead of transparency). In our case that mean that if MainWindow will be
// shown before StatusWindow - StatusWindow will not have transparent corners.
color: "transparent"
// show Setup Guide on every new user // show Setup Guide on every new user
Connections { Connections {
target: Backend.users target: Backend.users