diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/AccountDelegate.qml b/internal/frontend/bridge-gui/bridge-gui/qml/AccountDelegate.qml
index a0671e76..3f4bdfea 100644
--- a/internal/frontend/bridge-gui/bridge-gui/qml/AccountDelegate.qml
+++ b/internal/frontend/bridge-gui/bridge-gui/qml/AccountDelegate.qml
@@ -1,251 +1,252 @@
// Copyright (c) 2023 Proton AG
-//
// This file is part of Proton Mail Bridge.
-//
// Proton Mail Bridge 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 3 of the License, or
// (at your option) any later version.
-//
// Proton Mail Bridge 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.
-//
// You should have received a copy of the GNU General Public License
// along with Proton Mail Bridge. If not, see .
-
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
-
import Proton
Item {
id: root
-
- property ColorScheme colorScheme
- property var user
+ enum ViewType {
+ SmallView,
+ LargeView
+ }
property var _spacing: 12 * ProtonStyle.px
-
- property color progressColor : {
- if (!root.enabled) return root.colorScheme.text_weak
- if (root.type == AccountDelegate.SmallView) return root.colorScheme.text_weak
- if (root.user && root.user.isSyncing) return root.colorScheme.text_weak
- if (root.progressRatio < .50) return root.colorScheme.signal_success
- if (root.progressRatio < .75) return root.colorScheme.signal_warning
- return root.colorScheme.signal_danger
+ property ColorScheme colorScheme
+ property color progressColor: {
+ if (!root.enabled)
+ return root.colorScheme.text_weak;
+ if (root.type === AccountDelegate.SmallView)
+ return root.colorScheme.text_weak;
+ if (root.user && root.user.isSyncing)
+ return root.colorScheme.text_weak;
+ if (root.progressRatio < .50)
+ return root.colorScheme.signal_success;
+ if (root.progressRatio < .75)
+ return root.colorScheme.signal_warning;
+ return root.colorScheme.signal_danger;
}
property real progressRatio: {
if (!root.user)
- return 0
- return root.user.isSyncing ? root.user.syncProgress : reasonableFraction(root.user.usedBytes, root.user.totalBytes)
+ return 0;
+ return root.user.isSyncing ? root.user.syncProgress : reasonableFraction(root.user.usedBytes, root.user.totalBytes);
}
property string totalSpace: root.spaceWithUnits(root.user ? root.reasonableBytes(root.user.totalBytes) : 0)
+ property var type: AccountDelegate.SmallView
property string usedSpace: root.spaceWithUnits(root.user ? root.reasonableBytes(root.user.usedBytes) : 0)
-
- function reasonableFraction(used, total){
- var usedSafe = root.reasonableBytes(used)
- var totalSafe = root.reasonableBytes(total)
- if (totalSafe == 0 || usedSafe == 0) return 0
- if (totalSafe <= usedSafe) return 1
- return usedSafe / totalSafe
- }
-
- function reasonableBytes(bytes){
- var safeBytes = bytes+0
- if (safeBytes != bytes) return 0
- if (safeBytes < 0) return 0
- return Math.ceil(safeBytes)
- }
-
- function spaceWithUnits(bytes){
- if (bytes*1 !== bytes || bytes == 0 ) return "0 kB"
- var units = ['B',"kB", "MB", "GB", "TB"];
- var i = parseInt(Math.floor(Math.log(bytes)/Math.log(1024)));
-
- return Math.round(bytes*10 / Math.pow(1024, i))/10 + " " + units[i]
- }
+ property var user
function primaryEmail() {
- return root.user ? root.user.primaryEmailOrUsername() : ""
+ return root.user ? root.user.primaryEmailOrUsername() : "";
+ }
+ function reasonableBytes(bytes) {
+ const safeBytes = bytes + 0;
+ if (safeBytes !== bytes)
+ return 0;
+ if (safeBytes < 0)
+ return 0;
+ return Math.ceil(safeBytes);
+ }
+ function reasonableFraction(used, total) {
+ const usedSafe = root.reasonableBytes(used);
+ const totalSafe = root.reasonableBytes(total);
+ if (totalSafe === 0 || usedSafe === 0)
+ return 0;
+ if (totalSafe <= usedSafe)
+ return 1;
+ return usedSafe / totalSafe;
+ }
+ function spaceWithUnits(bytes) {
+ if (bytes * 1 !== bytes || bytes === 0)
+ return "0 kB";
+ const units = ['B', "kB", "MB", "GB", "TB"];
+ const i = Math.floor(Math.log(bytes) / Math.log(1024));
+ return Math.round(bytes * 10 / Math.pow(1024, i)) / 10 + " " + units[i];
}
// width expected to be set by parent object
- implicitHeight : children[0].implicitHeight
-
- enum ViewType{
- SmallView, LargeView
- }
- property var type : AccountDelegate.SmallView
+ implicitHeight: children[0].implicitHeight
RowLayout {
spacing: root._spacing
anchors {
- top: root.top
left: root.left
right: root.right
+ top: root.top
}
-
Rectangle {
id: avatar
-
Layout.fillHeight: true
Layout.preferredWidth: height
-
+ color: root.colorScheme.background_avatar
radius: ProtonStyle.avatar_radius
- color: root.colorScheme.background_avatar
-
Label {
- colorScheme: root.colorScheme
anchors.fill: parent
- text: root.user ? root.user.avatarText.toUpperCase(): ""
+ color: "#FFFFFF"
+ colorScheme: root.colorScheme
+ font.weight: Font.Normal
+ horizontalAlignment: Qt.AlignHCenter
+ text: root.user ? root.user.avatarText.toUpperCase() : ""
type: {
switch (root.type) {
- case AccountDelegate.SmallView: return Label.Body
- case AccountDelegate.LargeView: return Label.Title
+ case AccountDelegate.SmallView:
+ return Label.Body;
+ case AccountDelegate.LargeView:
+ return Label.Title;
}
}
- font.weight: Font.Normal
- color: "#FFFFFF"
- horizontalAlignment: Qt.AlignHCenter
verticalAlignment: Qt.AlignVCenter
}
}
-
ColumnLayout {
id: account
Layout.fillHeight: true
Layout.fillWidth: true
-
spacing: 0
Label {
id: labelEmail
Layout.maximumWidth: root.width - (root._spacing + avatar.width)
colorScheme: root.colorScheme
+ elide: Text.ElideMiddle
text: primaryEmail()
type: {
switch (root.type) {
- case AccountDelegate.SmallView: return Label.Body
- case AccountDelegate.LargeView: return Label.Title
+ case AccountDelegate.SmallView:
+ return Label.Body;
+ case AccountDelegate.LargeView:
+ return Label.Title;
}
}
- elide: Text.ElideMiddle
MouseArea {
id: labelArea
- anchors.fill:parent
+ anchors.fill: parent
hoverEnabled: true
}
-
ToolTip {
id: toolTipEmail
- visible: labelArea.containsMouse && labelEmail.truncated
- text: primaryEmail()
delay: 1000
+ text: primaryEmail()
+ visible: labelArea.containsMouse && labelEmail.truncated
background: Rectangle {
border.color: root.colorScheme.background_strong
color: root.colorScheme.background_norm
}
-
contentItem: Text {
color: root.colorScheme.text_norm
text: toolTipEmail.text
}
}
}
-
- Item { implicitHeight: root.type == AccountDelegate.LargeView ? 6 * ProtonStyle.px : 0 }
-
+ Item {
+ implicitHeight: root.type === AccountDelegate.LargeView ? 6 * ProtonStyle.px : 0
+ }
RowLayout {
spacing: 0
+
Label {
+ color: root.progressColor
colorScheme: root.colorScheme
text: {
if (!root.user)
- return qsTr("Signed out")
+ return qsTr("Signed out");
switch (root.user.state) {
case EUserState.SignedOut:
default:
- return qsTr("Signed out")
+ return qsTr("Signed out");
case EUserState.Locked:
- return qsTr("Connecting") + dotsTimer.dots
+ return qsTr("Connecting") + dotsTimer.dots;
case EUserState.Connected:
if (root.user.isSyncing)
- return qsTr("Synchronizing (%1%)").arg(Math.floor(root.user.syncProgress * 100)) + dotsTimer.dots
+ return qsTr("Synchronizing (%1%)").arg(Math.floor(root.user.syncProgress * 100)) + dotsTimer.dots;
else
- return root.usedSpace
+ return root.usedSpace;
}
}
-
- Timer { // dots animation while connecting & syncing.
- id:dotsTimer
- property string dots: ""
- interval: 500;
- repeat: true;
- running: (root.user != null) && ((root.user.state === EUserState.Locked) || (root.user.isSyncing))
- onTriggered: {
- dots += "."
- if (dots.length > 3)
- dots = ""
- }
- onRunningChanged: {
- dots = ""
- }
- }
-
- color: root.progressColor
type: {
switch (root.type) {
- case AccountDelegate.SmallView: return Label.Caption
- case AccountDelegate.LargeView: return Label.Body
+ case AccountDelegate.SmallView:
+ return Label.Caption;
+ case AccountDelegate.LargeView:
+ return Label.Body;
+ }
+ }
+
+ Timer {
+ // dots animation while connecting & syncing.
+ id: dotsTimer
+
+ property string dots: ""
+
+ interval: 500
+ repeat: true
+ running: (root.user != null) && ((root.user.state === EUserState.Locked) || (root.user.isSyncing))
+
+ onRunningChanged: {
+ dots = "";
+ }
+ onTriggered: {
+ dots += ".";
+ if (dots.length > 3)
+ dots = "";
}
}
}
-
Label {
- colorScheme: root.colorScheme
- text: root.user && root.user.state == EUserState.Connected && !root.user.isSyncing ? " / " + root.totalSpace : ""
color: root.colorScheme.text_weak
+ colorScheme: root.colorScheme
+ text: root.user && root.user.state === EUserState.Connected && !root.user.isSyncing ? " / " + root.totalSpace : ""
type: {
switch (root.type) {
- case AccountDelegate.SmallView: return Label.Caption
- case AccountDelegate.LargeView: return Label.Body
+ case AccountDelegate.SmallView:
+ return Label.Caption;
+ case AccountDelegate.LargeView:
+ return Label.Body;
}
}
}
}
-
- Item { implicitHeight: root.type == AccountDelegate.LargeView ? 3 * ProtonStyle.px : 0 }
-
+ Item {
+ implicitHeight: root.type === AccountDelegate.LargeView ? 3 * ProtonStyle.px : 0
+ }
Rectangle {
id: progress_bar
- visible: root.user ? root.type == AccountDelegate.LargeView : false
- width: 140 * ProtonStyle.px
+ color: root.colorScheme.border_weak
height: 4 * ProtonStyle.px
radius: ProtonStyle.progress_bar_radius
- color: root.colorScheme.border_weak
+ visible: root.user ? root.type === AccountDelegate.LargeView : false
+ width: 140 * ProtonStyle.px
Rectangle {
id: progress_bar_filled
- radius: ProtonStyle.progress_bar_radius
color: root.progressColor
- visible: root.user ? parent.visible && (root.user.state == EUserState.Connected): false
+ radius: ProtonStyle.progress_bar_radius
+ visible: root.user ? parent.visible && (root.user.state === EUserState.Connected) : false
+ width: Math.min(1, Math.max(0.02, root.progressRatio)) * parent.width
+
anchors {
- top : parent.top
- bottom : parent.bottom
- left : parent.left
+ bottom: parent.bottom
+ left: parent.left
+ top: parent.top
}
- width: Math.min(1,Math.max(0.02,root.progressRatio)) * parent.width
}
}
}
-
Item {
Layout.fillWidth: true
}
diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/AccountView.qml b/internal/frontend/bridge-gui/bridge-gui/qml/AccountView.qml
index fa9702a8..ff196d43 100644
--- a/internal/frontend/bridge-gui/bridge-gui/qml/AccountView.qml
+++ b/internal/frontend/bridge-gui/bridge-gui/qml/AccountView.qml
@@ -1,42 +1,35 @@
// Copyright (c) 2023 Proton AG
-//
// This file is part of Proton Mail Bridge.
-//
// Proton Mail Bridge 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 3 of the License, or
// (at your option) any later version.
-//
// Proton Mail Bridge 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.
-//
// You should have received a copy of the GNU General Public License
// along with Proton Mail Bridge. If not, see .
-
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
-
import Proton
Item {
id: root
+
+ property bool _connected: root.user ? root.user.state === EUserState.Connected : false
+ property int _contentWidth: 640
+ property int _detailsMargin: 25
+ property int _lineThickness: 1
+ property int _spacing: 20
+ property int _topMargin: 32
property ColorScheme colorScheme
property var notifications
property var user
- signal showSignIn
-
signal showSetupGuide(var user, string address)
-
- property int _contentWidth: 640
- property int _topMargin: 32
- property int _detailsMargin: 25
- property int _spacing: 20
- property int _lineThickness: 1
- property bool _connected: root.user ? root.user.state === EUserState.Connected : false
+ signal showSignIn
Rectangle {
anchors.fill: parent
@@ -45,6 +38,7 @@ Item {
ScrollView {
id: scrollView
anchors.fill: parent
+
Component.onCompleted: contentItem.boundsBehavior = Flickable.StopAtBounds
ColumnLayout {
@@ -54,16 +48,16 @@ Item {
Rectangle {
id: topArea
- color: root.colorScheme.background_norm
- clip: true
Layout.fillWidth: true
+ clip: true
+ color: root.colorScheme.background_norm
implicitHeight: childrenRect.height
ColumnLayout {
id: topLayout
- width: _contentWidth
anchors.horizontalCenter: parent.horizontalCenter
spacing: _spacing
+ width: _contentWidth
RowLayout {
// account delegate with action buttons
@@ -73,83 +67,82 @@ Item {
AccountDelegate {
Layout.fillWidth: true
colorScheme: root.colorScheme
- user: root.user
- type: AccountDelegate.LargeView
enabled: _connected
+ type: AccountDelegate.LargeView
+ user: root.user
}
-
Button {
Layout.alignment: Qt.AlignTop
colorScheme: root.colorScheme
- text: qsTr("Sign out")
secondary: true
+ text: qsTr("Sign out")
visible: _connected
+
onClicked: {
if (!root.user)
return;
root.user.logout();
}
}
-
Button {
Layout.alignment: Qt.AlignTop
colorScheme: root.colorScheme
- text: qsTr("Sign in")
secondary: true
+ text: qsTr("Sign in")
visible: root.user ? (root.user.state === EUserState.SignedOut) : false
+
onClicked: {
if (!root.user)
return;
root.showSignIn();
}
}
-
Button {
Layout.alignment: Qt.AlignTop
colorScheme: root.colorScheme
icon.source: "/qml/icons/ic-trash.svg"
secondary: true
+ visible: root.user ? root.user.state !== EUserState.Locked : false
+
onClicked: {
if (!root.user)
return;
root.notifications.askDeleteAccount(root.user);
}
- visible: root.user ? root.user.state !== EUserState.Locked : false
}
}
-
Rectangle {
Layout.fillWidth: true
- height: root._lineThickness
color: root.colorScheme.border_weak
+ height: root._lineThickness
}
-
SettingsItem {
- colorScheme: root.colorScheme
- text: qsTr("Email clients")
+ Layout.fillWidth: true
actionText: qsTr("Configure")
+ colorScheme: root.colorScheme
description: qsTr("Using the mailbox details below (re)configure your client.")
+ showSeparator: splitMode.visible
+ text: qsTr("Email clients")
type: SettingsItem.Button
visible: _connected && (!root.user.splitMode) || (root.user.addresses.length === 1)
- showSeparator: splitMode.visible
+
onClicked: {
if (!root.user)
return;
root.showSetupGuide(root.user, user.addresses[0]);
}
-
- Layout.fillWidth: true
}
-
SettingsItem {
id: splitMode
- colorScheme: root.colorScheme
- text: qsTr("Split addresses")
- description: qsTr("Setup multiple email addresses individually.")
- type: SettingsItem.Toggle
+ Layout.fillWidth: true
checked: root.user ? root.user.splitMode : false
- visible: _connected && root.user.addresses.length > 1
+ colorScheme: root.colorScheme
+ description: qsTr("Setup multiple email addresses individually.")
showSeparator: addressSelector.visible
+ text: qsTr("Split addresses")
+ type: SettingsItem.Toggle
+ visible: _connected && root.user.addresses.length > 1
+
onClicked: {
if (!splitMode.checked) {
root.notifications.askEnableSplitMode(user);
@@ -158,26 +151,23 @@ Item {
root.user.toggleSplitMode(!splitMode.checked);
}
}
-
- Layout.fillWidth: true
}
-
RowLayout {
- Layout.fillWidth: true
Layout.bottomMargin: _spacing
+ Layout.fillWidth: true
visible: _connected && root.user.splitMode
ComboBox {
id: addressSelector
- colorScheme: root.colorScheme
Layout.fillWidth: true
+ colorScheme: root.colorScheme
model: root.user ? root.user.addresses : null
}
-
Button {
colorScheme: root.colorScheme
- text: qsTr("Configure")
secondary: true
+ text: qsTr("Configure")
+
onClicked: {
if (!root.user)
return;
@@ -185,25 +175,23 @@ Item {
}
}
}
-
Rectangle {
height: 0
} // just for some extra space before separator
}
}
-
Rectangle {
id: bottomArea
Layout.fillWidth: true
- implicitHeight: bottomLayout.implicitHeight
color: root.colorScheme.background_weak
+ implicitHeight: bottomLayout.implicitHeight
ColumnLayout {
id: bottomLayout
- width: _contentWidth
anchors.horizontalCenter: parent.horizontalCenter
spacing: _spacing
visible: _connected
+ width: _contentWidth
Label {
Layout.topMargin: _detailsMargin
@@ -211,35 +199,34 @@ Item {
text: qsTr("Mailbox details")
type: Label.Body_semibold
}
-
RowLayout {
id: configuration
- spacing: _spacing
- Layout.fillWidth: true
- Layout.fillHeight: true
property string currentAddress: addressSelector.displayText
- Configuration {
- Layout.fillWidth: true
- colorScheme: root.colorScheme
- title: qsTr("IMAP")
- hostname: Backend.hostname
- port: Backend.imapPort.toString()
- username: configuration.currentAddress
- password: root.user ? root.user.password : ""
- security: Backend.useSSLForIMAP ? "SSL" : "STARTTLS"
- }
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+ spacing: _spacing
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 : ""
+ port: Backend.imapPort.toString()
+ security: Backend.useSSLForIMAP ? "SSL" : "STARTTLS"
+ title: qsTr("IMAP")
+ username: configuration.currentAddress
+ }
+ Configuration {
+ Layout.fillWidth: true
+ colorScheme: root.colorScheme
+ hostname: Backend.hostname
+ password: root.user ? root.user.password : ""
+ port: Backend.smtpPort.toString()
security: Backend.useSSLForSMTP ? "SSL" : "STARTTLS"
+ title: qsTr("SMTP")
+ username: configuration.currentAddress
}
}
}
diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/Banner.qml b/internal/frontend/bridge-gui/bridge-gui/qml/Banner.qml
index 18c5d856..47a80893 100644
--- a/internal/frontend/bridge-gui/bridge-gui/qml/Banner.qml
+++ b/internal/frontend/bridge-gui/bridge-gui/qml/Banner.qml
@@ -1,25 +1,19 @@
// Copyright (c) 2023 Proton AG
-//
// This file is part of Proton Mail Bridge.
-//
// Proton Mail Bridge 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 3 of the License, or
// (at your option) any later version.
-//
// Proton Mail Bridge 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.
-//
// You should have received a copy of the GNU General Public License
// along with Proton Mail Bridge. If not, see .
-
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import QtQuick.Controls.impl
-
import Proton
import Notifications
@@ -27,34 +21,28 @@ Popup {
id: root
property ColorScheme colorScheme
- property Notification notification
property var mainWindow
-
- topMargin: 37
- leftMargin: (mainWindow.width - root.implicitWidth)/2
+ property Notification notification
implicitHeight: contentLayout.implicitHeight + contentLayout.anchors.topMargin + contentLayout.anchors.bottomMargin
implicitWidth: 600 // contentLayout.implicitWidth + contentLayout.anchors.leftMargin + contentLayout.anchors.rightMargin
-
- popupType: ApplicationWindow.PopupType.Banner
-
- shouldShow: notification ? (notification.active && !notification.dismissed) : false
-
+ leftMargin: (mainWindow.width - root.implicitWidth) / 2
modal: false
+ popupType: ApplicationWindow.PopupType.Banner
+ shouldShow: notification ? (notification.active && !notification.dismissed) : false
+ topMargin: 37
Action {
id: defaultDismissAction
-
text: qsTr("OK")
+
onTriggered: {
if (!root.notification) {
- return
+ return;
}
-
- root.notification.dismissed = true
+ root.notification.dismissed = true;
}
}
-
RowLayout {
id: contentLayout
anchors.fill: parent
@@ -63,170 +51,148 @@ Popup {
Item {
Layout.fillHeight: true
Layout.fillWidth: true
-
clip: true
implicitHeight: children[1].implicitHeight + children[1].anchors.topMargin + children[1].anchors.bottomMargin
implicitWidth: children[1].implicitWidth + children[1].anchors.leftMargin + children[1].anchors.rightMargin
Rectangle {
- anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: parent.left
- width: parent.width + 10
- radius: ProtonStyle.banner_radius
+ anchors.top: parent.top
color: {
if (!root.notification) {
- return "transparent"
+ return "transparent";
}
-
switch (root.notification.type) {
- case Notification.NotificationType.Info:
- return root.colorScheme.signal_info
- case Notification.NotificationType.Success:
- return root.colorScheme.signal_success
- case Notification.NotificationType.Warning:
- return root.colorScheme.signal_warning
- case Notification.NotificationType.Danger:
- return root.colorScheme.signal_danger
+ case Notification.NotificationType.Info:
+ return root.colorScheme.signal_info;
+ case Notification.NotificationType.Success:
+ return root.colorScheme.signal_success;
+ case Notification.NotificationType.Warning:
+ return root.colorScheme.signal_warning;
+ case Notification.NotificationType.Danger:
+ return root.colorScheme.signal_danger;
}
}
+ radius: ProtonStyle.banner_radius
+ width: parent.width + 10
}
-
RowLayout {
- anchors.fill: parent
-
- anchors.topMargin: 14
anchors.bottomMargin: 14
+ anchors.fill: parent
anchors.leftMargin: 16
-
+ anchors.topMargin: 14
spacing: 8
ColorImage {
- color: root.colorScheme.text_invert
- width: 24
- height: 24
-
- sourceSize.width: 24
- sourceSize.height: 24
-
Layout.preferredHeight: 24
Layout.preferredWidth: 24
-
+ color: root.colorScheme.text_invert
+ height: 24
source: {
if (!root.notification) {
- return ""
+ return "";
}
-
switch (root.notification.type) {
- case Notification.NotificationType.Info:
- return "/qml/icons/ic-info-circle-filled.svg"
- case Notification.NotificationType.Success:
- return "/qml/icons/ic-info-circle-filled.svg"
- case Notification.NotificationType.Warning:
- return "/qml/icons/ic-exclamation-circle-filled.svg"
- case Notification.NotificationType.Danger:
- return "/qml/icons/ic-exclamation-circle-filled.svg"
+ case Notification.NotificationType.Info:
+ return "/qml/icons/ic-info-circle-filled.svg";
+ case Notification.NotificationType.Success:
+ return "/qml/icons/ic-info-circle-filled.svg";
+ case Notification.NotificationType.Warning:
+ return "/qml/icons/ic-exclamation-circle-filled.svg";
+ case Notification.NotificationType.Danger:
+ return "/qml/icons/ic-exclamation-circle-filled.svg";
}
}
+ sourceSize.height: 24
+ sourceSize.width: 24
+ width: 24
}
-
Label {
- colorScheme: root.colorScheme
- Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter
+ Layout.fillWidth: true
Layout.leftMargin: 16
-
color: root.colorScheme.text_invert
+ colorScheme: root.colorScheme
text: root.notification ? root.notification.description : ""
-
wrapMode: Text.WordWrap
}
}
}
-
Rectangle {
Layout.fillHeight: true
- width: 1
color: {
if (!root.notification) {
- return "transparent"
+ return "transparent";
}
-
switch (root.notification.type) {
- case Notification.NotificationType.Info:
- return root.colorScheme.signal_info_active
- case Notification.NotificationType.Success:
- return root.colorScheme.signal_success_active
- case Notification.NotificationType.Warning:
- return root.colorScheme.signal_warning_active
- case Notification.NotificationType.Danger:
- return root.colorScheme.signal_danger_active
+ case Notification.NotificationType.Info:
+ return root.colorScheme.signal_info_active;
+ case Notification.NotificationType.Success:
+ return root.colorScheme.signal_success_active;
+ case Notification.NotificationType.Warning:
+ return root.colorScheme.signal_warning_active;
+ case Notification.NotificationType.Danger:
+ return root.colorScheme.signal_danger_active;
}
}
+ width: 1
}
-
Button {
- colorScheme: root.colorScheme
- Layout.fillHeight: true
-
id: actionButton
-
+ Layout.fillHeight: true
action: (root.notification && root.notification.action.length > 0) ? root.notification.action[0] : defaultDismissAction
+ colorScheme: root.colorScheme
background: Item {
clip: true
+
Rectangle {
- anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.right: parent.right
- width: parent.width + 10
- radius: ProtonStyle.banner_radius
+ anchors.top: parent.top
color: {
if (!root.notification) {
- return "transparent"
+ return "transparent";
}
-
- var norm
- var hover
- var active
-
+ let norm;
+ let hover;
+ let active;
switch (root.notification.type) {
- case Notification.NotificationType.Info:
- norm = root.colorScheme.signal_info
- hover = root.colorScheme.signal_info_hover
- active = root.colorScheme.signal_info_active
+ case Notification.NotificationType.Info:
+ norm = root.colorScheme.signal_info;
+ hover = root.colorScheme.signal_info_hover;
+ active = root.colorScheme.signal_info_active;
break;
- case Notification.NotificationType.Success:
- norm = root.colorScheme.signal_success
- hover = root.colorScheme.signal_success_hover
- active = root.colorScheme.signal_success_active
+ case Notification.NotificationType.Success:
+ norm = root.colorScheme.signal_success;
+ hover = root.colorScheme.signal_success_hover;
+ active = root.colorScheme.signal_success_active;
break;
- case Notification.NotificationType.Warning:
- norm = root.colorScheme.signal_warning
- hover = root.colorScheme.signal_warning_hover
- active = root.colorScheme.signal_warning_active
+ case Notification.NotificationType.Warning:
+ norm = root.colorScheme.signal_warning;
+ hover = root.colorScheme.signal_warning_hover;
+ active = root.colorScheme.signal_warning_active;
break;
- case Notification.NotificationType.Danger:
- norm = root.colorScheme.signal_danger
- hover = root.colorScheme.signal_danger_hover
- active = root.colorScheme.signal_danger_active
+ case Notification.NotificationType.Danger:
+ norm = root.colorScheme.signal_danger;
+ hover = root.colorScheme.signal_danger_hover;
+ active = root.colorScheme.signal_danger_active;
break;
}
-
if (actionButton.down) {
- return active
+ return active;
}
-
if (actionButton.enabled && (actionButton.highlighted || actionButton.hovered || actionButton.checked)) {
- return hover
+ return hover;
}
-
if (actionButton.loading) {
- return hover
+ return hover;
}
-
- return norm
+ return norm;
}
+ radius: ProtonStyle.banner_radius
+ width: parent.width + 10
}
}
}
diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/Bridge.qml b/internal/frontend/bridge-gui/bridge-gui/qml/Bridge.qml
index 3e377ad7..1f1a6c4c 100644
--- a/internal/frontend/bridge-gui/bridge-gui/qml/Bridge.qml
+++ b/internal/frontend/bridge-gui/bridge-gui/qml/Bridge.qml
@@ -1,129 +1,112 @@
// Copyright (c) 2023 Proton AG
-//
// This file is part of Proton Mail Bridge.
-//
// Proton Mail Bridge 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 3 of the License, or
// (at your option) any later version.
-//
// Proton Mail Bridge 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.
-//
// You should have received a copy of the GNU General Public License
// along with Proton Mail Bridge. If not, see .
-
import QtQml
import QtQuick
import QtQuick.Window
import Qt.labs.platform
-
import Proton
import Notifications
QtObject {
id: root
- function bound(num, lowerLimit, upperLimit) {
- return Math.max(lowerLimit, Math.min(upperLimit, num))
+ property MainWindow _mainWindow: MainWindow {
+ id: mainWindow
+ notifications: root._notifications
+ title: root.title
+ visible: false
+
+ onVisibleChanged: {
+ Backend.dockIconVisible = visible;
+ }
+
+ Connections {
+ function onColorSchemeNameChanged(scheme) {
+ root.setColorScheme();
+ }
+ function onDiskCacheUnavailable() {
+ mainWindow.showAndRise();
+ }
+ function onHideMainWindow() {
+ mainWindow.hide();
+ }
+
+ target: Backend
+ }
}
-
- property var title: Backend.appname
-
property Notifications _notifications: Notifications {
id: notifications
frontendMain: mainWindow
}
-
property NotificationFilter _trayNotificationFilter: NotificationFilter {
- id: trayNotificationFilter
- source: root._notifications ? root._notifications.all : undefined
- onTopmostChanged: {
- if (topmost) {
- switch (topmost.type) {
- case Notification.NotificationType.Danger:
- Backend.setErrorTrayIcon(topmost.brief, topmost.icon)
- return
- case Notification.NotificationType.Warning:
- Backend.setWarnTrayIcon(topmost.brief, topmost.icon)
- return
- case Notification.NotificationType.Info:
- Backend.setUpdateTrayIcon(topmost.brief, topmost.icon)
- return
+ id: trayNotificationFilter
+ source: root._notifications ? root._notifications.all : undefined
+
+ onTopmostChanged: {
+ if (topmost) {
+ switch (topmost.type) {
+ case Notification.NotificationType.Danger:
+ Backend.setErrorTrayIcon(topmost.brief, topmost.icon);
+ return;
+ case Notification.NotificationType.Warning:
+ Backend.setWarnTrayIcon(topmost.brief, topmost.icon);
+ return;
+ case Notification.NotificationType.Info:
+ Backend.setUpdateTrayIcon(topmost.brief, topmost.icon);
+ return;
}
}
- Backend.setNormalTrayIcon()
+ Backend.setNormalTrayIcon();
}
}
+ property var title: Backend.appname
-
- property MainWindow _mainWindow: MainWindow {
- id: mainWindow
- visible: false
-
- title: root.title
- notifications: root._notifications
-
- onVisibleChanged: {
- Backend.dockIconVisible = visible
- }
-
- Connections {
- target: Backend
- function onDiskCacheUnavailable() {
- mainWindow.showAndRise()
- }
- function onColorSchemeNameChanged(scheme) { root.setColorScheme() }
-
- function onHideMainWindow() {
- mainWindow.hide();
- }
- }
+ function bound(num, lowerLimit, upperLimit) {
+ return Math.max(lowerLimit, Math.min(upperLimit, num));
+ }
+ function setColorScheme() {
+ if (Backend.colorSchemeName === "light")
+ ProtonStyle.currentStyle = ProtonStyle.lightStyle;
+ if (Backend.colorSchemeName === "dark")
+ ProtonStyle.currentStyle = ProtonStyle.darkStyle;
}
-
Component.onCompleted: {
if (!Backend) {
- console.log("Backend not loaded")
+ console.log("Backend not loaded");
}
-
- root.setColorScheme()
-
-
+ root.setColorScheme();
if (!Backend.users) {
- console.log("users not loaded")
+ console.log("users not loaded");
}
-
- var c = Backend.users.count
- var u = Backend.users.get(0)
+ const c = Backend.users.count;
+ const u = Backend.users.get(0);
// DEBUG
if (c !== 0) {
- console.log("users non zero", c)
- console.log("first user", u )
+ console.log("users non zero", c);
+ console.log("first user", u);
}
-
if (c === 0) {
- mainWindow.showAndRise()
+ mainWindow.showAndRise();
}
-
if (u) {
if (c === 1 && (u.state === EUserState.SignedOut)) {
- mainWindow.showAndRise()
+ mainWindow.showAndRise();
}
}
-
- Backend.guiReady()
-
- if (Backend.showOnStartup || Backend.showSplashScreen) {
- mainWindow.showAndRise()
+ Backend.guiReady();
+ if (Backend.showOnStartup || Backend.showSplashScreen) {
+ mainWindow.showAndRise();
}
-
- }
-
- function setColorScheme() {
- if (Backend.colorSchemeName === "light") ProtonStyle.currentStyle = ProtonStyle.lightStyle
- if (Backend.colorSchemeName === "dark") ProtonStyle.currentStyle = ProtonStyle.darkStyle
}
}
diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/BugReportView.qml b/internal/frontend/bridge-gui/bridge-gui/qml/BugReportView.qml
index fc566eec..1f7abc73 100644
--- a/internal/frontend/bridge-gui/bridge-gui/qml/BugReportView.qml
+++ b/internal/frontend/bridge-gui/bridge-gui/qml/BugReportView.qml
@@ -1,202 +1,175 @@
// Copyright (c) 2023 Proton AG
-//
// This file is part of Proton Mail Bridge.
-//
// Proton Mail Bridge 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 3 of the License, or
// (at your option) any later version.
-//
// Proton Mail Bridge 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.
-//
// You should have received a copy of the GNU General Public License
// along with Proton Mail Bridge. If not, see .
-
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
-
import Proton
SettingsView {
id: root
- fillHeight: true
-
property var selectedAddress
- signal bugReportWasSent()
+ signal bugReportWasSent
- Label {
- text: qsTr("Report a problem")
- colorScheme: root.colorScheme
- type: Label.Heading
+ function isValidEmail(text) {
+ const reEmail = /^[^@]+@[^@]+\.[A-Za-z]+\s*$/;
+ return reEmail.test(text);
+ }
+ function setDefaultValue() {
+ description.text = "";
+ address.text = root.selectedAddress;
+ emailClient.text = Backend.currentEmailClient;
+ includeLogs.checked = true;
+ }
+ function setDescription(message) {
+ description.text = message;
+ }
+ function submit() {
+ sendButton.loading = true;
+ Backend.reportBug(description.text, address.text, emailClient.text, includeLogs.checked);
}
+ fillHeight: true
+ onVisibleChanged: {
+ root.setDefaultValue();
+ }
+
+ Label {
+ colorScheme: root.colorScheme
+ text: qsTr("Report a problem")
+ type: Label.Heading
+ }
TextArea {
id: description
- property int _minLength: 150
+
property int _maxLength: 800
-
- label: qsTr("Description")
- colorScheme: root.colorScheme
- Layout.fillWidth: true
- Layout.fillHeight: true
- Layout.minimumHeight: heightForLinesVisible(4)
- hint: description.text.length + "/" + _maxLength
- placeholderText: qsTr("Tell us what went wrong or isn't working (min. %1 characters).").arg(_minLength)
-
- validator: function(text) {
- if (description.text.length < description._minLength) {
- return qsTr("Enter a problem description (min. %1 characters).").arg(_minLength)
- }
-
- if (description.text.length > description._maxLength) {
- return qsTr("Enter a problem description (max. %1 characters).").arg(_maxLength)
- }
-
- return
- }
-
- onTextChanged: {
- // Rise max length error immediately while typing
- if (description.text.length > description._maxLength) {
- validate()
- }
- }
+ property int _minLength: 150
KeyNavigation.priority: KeyNavigation.BeforeItem
KeyNavigation.tab: address
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+ Layout.minimumHeight: heightForLinesVisible(4)
+ colorScheme: root.colorScheme
+ hint: description.text.length + "/" + _maxLength
// set implicitHeight to explicit height because se don't
// want TextArea implicitHeight (which is height of all text)
// to be considered in SettingsView internal scroll view
implicitHeight: height
+ label: qsTr("Description")
+ placeholderText: qsTr("Tell us what went wrong or isn't working (min. %1 characters).").arg(_minLength)
+ validator: function (text) {
+ if (description.text.length < description._minLength) {
+ return qsTr("Enter a problem description (min. %1 characters).").arg(_minLength);
+ }
+ if (description.text.length > description._maxLength) {
+ return qsTr("Enter a problem description (max. %1 characters).").arg(_maxLength);
+ }
+ return;
+ }
+
+ onTextChanged: {
+ // Rise max length error immediately while typing
+ if (description.text.length > description._maxLength) {
+ validate();
+ }
+ }
}
-
-
TextField {
id: address
-
- label: qsTr("Your contact email")
- colorScheme: root.colorScheme
Layout.fillWidth: true
+ colorScheme: root.colorScheme
+ label: qsTr("Your contact email")
placeholderText: qsTr("e.g. jane.doe@protonmail.com")
-
- validator: function(str) {
+ validator: function (str) {
if (!isValidEmail(str)) {
- return qsTr("Enter valid email address")
+ return qsTr("Enter valid email address");
}
- return
+ return;
}
}
-
TextField {
id: emailClient
-
- label: qsTr("Your email client (including version)")
- colorScheme: root.colorScheme
Layout.fillWidth: true
+ colorScheme: root.colorScheme
+ label: qsTr("Your email client (including version)")
placeholderText: qsTr("e.g. Apple Mail 14.0")
-
- validator: function(str) {
+ validator: function (str) {
if (str.length === 0) {
- return qsTr("Enter an email client name and version")
+ return qsTr("Enter an email client name and version");
}
- return
+ return;
}
}
-
-
RowLayout {
CheckBox {
id: includeLogs
- text: qsTr("Include my recent logs")
- colorScheme: root.colorScheme
checked: true
+ colorScheme: root.colorScheme
+ text: qsTr("Include my recent logs")
}
Button {
Layout.leftMargin: 12
- text: qsTr("View logs")
- secondary: true
colorScheme: root.colorScheme
+ secondary: true
+ text: qsTr("View logs")
+
onClicked: Qt.openUrlExternally(Backend.logsPath)
}
}
-
TextEdit {
- text: qsTr("Reports are not end-to-end encrypted, please do not send any sensitive information.")
-
- readOnly: true
-
Layout.fillWidth: true
color: root.colorScheme.text_weak
font.family: ProtonStyle.font_family
- font.weight: ProtonStyle.fontWeight_400
- font.pixelSize: ProtonStyle.caption_font_size
font.letterSpacing: ProtonStyle.caption_letter_spacing
+ font.pixelSize: ProtonStyle.caption_font_size
+ font.weight: ProtonStyle.fontWeight_400
+ readOnly: true
+ selectByMouse: true
+ selectedTextColor: root.colorScheme.text_invert
// No way to set lineHeight: ProtonStyle.caption_line_height
selectionColor: root.colorScheme.interaction_norm
- selectedTextColor: root.colorScheme.text_invert
+ text: qsTr("Reports are not end-to-end encrypted, please do not send any sensitive information.")
wrapMode: Text.WordWrap
- selectByMouse: true
}
-
Button {
id: sendButton
- text: qsTr("Send")
colorScheme: root.colorScheme
enabled: !loading
+ text: qsTr("Send")
+
onClicked: {
- description.validate()
- address.validate()
- emailClient.validate()
-
+ description.validate();
+ address.validate();
+ emailClient.validate();
if (description.error || address.error || emailClient.error) {
- return
+ return;
}
-
- submit()
+ submit();
}
Connections {
+ function onBugReportSendSuccess() {
+ root.bugReportWasSent();
+ }
+ function onReportBugFinished() {
+ sendButton.loading = false;
+ }
+
target: Backend
- function onReportBugFinished() { sendButton.loading = false }
- function onBugReportSendSuccess() { root.bugReportWasSent() }
}
}
-
- function setDescription(message) {
- description.text = message
- }
-
- function setDefaultValue() {
- description.text = ""
- address.text = root.selectedAddress
- emailClient.text = Backend.currentEmailClient
- includeLogs.checked = true
- }
-
- function isValidEmail(text){
- var reEmail = /^[^@]+@[^@]+\.[A-Za-z]+\s*$/
- return reEmail.test(text)
- }
-
- function submit() {
- sendButton.loading = true
- Backend.reportBug(
- description.text,
- address.text,
- emailClient.text,
- includeLogs.checked
- )
- }
-
- onVisibleChanged: {
- root.setDefaultValue()
- }
}
diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/Configuration.qml b/internal/frontend/bridge-gui/bridge-gui/qml/Configuration.qml
index 84121e81..c162dcfc 100644
--- a/internal/frontend/bridge-gui/bridge-gui/qml/Configuration.qml
+++ b/internal/frontend/bridge-gui/bridge-gui/qml/Configuration.qml
@@ -1,71 +1,80 @@
// Copyright (c) 2023 Proton AG
-//
// This file is part of Proton Mail Bridge.
-//
// Proton Mail Bridge 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 3 of the License, or
// (at your option) any later version.
-//
// Proton Mail Bridge 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.
-//
// You should have received a copy of the GNU General Public License
// along with Proton Mail Bridge. If not, see .
-
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import QtQuick.Controls.impl
-
import Proton
Rectangle {
id: root
+ property int _margin: 24
property ColorScheme colorScheme
- property string title
property string hostname
- property string port
- property string username
property string password
+ property string port
property string security
-
- implicitWidth: 304
- implicitHeight: content.height + 2*root._margin
+ property string title
+ property string username
color: root.colorScheme.background_norm
+ implicitHeight: content.height + 2 * root._margin
+ implicitWidth: 304
radius: ProtonStyle.card_radius
- property int _margin: 24
-
ColumnLayout {
id: content
- width: root.width - 2*root._margin
- anchors{
- top: root.top
- left: root.left
- leftMargin : root._margin
- rightMargin : root._margin
- topMargin : root._margin
- bottomMargin : root._margin
- }
-
spacing: 12
+ width: root.width - 2 * root._margin
+ anchors {
+ bottomMargin: root._margin
+ left: root.left
+ leftMargin: root._margin
+ rightMargin: root._margin
+ top: root.top
+ topMargin: root._margin
+ }
Label {
colorScheme: root.colorScheme
text: root.title
type: Label.Body_semibold
}
-
- 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("Username") ; value: root.username }
- ConfigurationItem{ colorScheme: root.colorScheme; label: qsTr("Password") ; value: root.password }
- ConfigurationItem{ colorScheme: root.colorScheme; label: qsTr("Security") ; value: root.security }
+ 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("Username")
+ value: root.username
+ }
+ ConfigurationItem {
+ colorScheme: root.colorScheme
+ label: qsTr("Password")
+ value: root.password
+ }
+ ConfigurationItem {
+ colorScheme: root.colorScheme
+ label: qsTr("Security")
+ value: root.security
+ }
}
}
-
diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/ConfigurationItem.qml b/internal/frontend/bridge-gui/bridge-gui/qml/ConfigurationItem.qml
index 80732855..55cf2634 100644
--- a/internal/frontend/bridge-gui/bridge-gui/qml/ConfigurationItem.qml
+++ b/internal/frontend/bridge-gui/bridge-gui/qml/ConfigurationItem.qml
@@ -1,35 +1,29 @@
// Copyright (c) 2023 Proton AG
-//
// This file is part of Proton Mail Bridge.
-//
// Proton Mail Bridge 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 3 of the License, or
// (at your option) any later version.
-//
// Proton Mail Bridge 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.
-//
// You should have received a copy of the GNU General Public License
// along with Proton Mail Bridge. If not, see .
-
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import QtQuick.Controls.impl
-
import Proton
Item {
id: root
- Layout.fillWidth: true
property var colorScheme
property string label
property string value
+ Layout.fillWidth: true
implicitHeight: children[0].implicitHeight
implicitWidth: children[0].implicitWidth
@@ -47,45 +41,42 @@ Item {
}
TextEdit {
id: valueText
- text: root.value
+ Layout.fillWidth: true
color: root.colorScheme.text_weak
readOnly: true
- selectByMouse: true
selectByKeyboard: true
+ selectByMouse: true
selectionColor: root.colorScheme.text_weak
+ text: root.value
wrapMode: Text.WrapAnywhere
- Layout.fillWidth: true
}
}
-
Item {
Layout.fillWidth: true
}
-
ColorImage {
- source: "/qml/icons/ic-copy.svg"
color: root.colorScheme.text_norm
height: root.colorScheme.body_font_size
+ source: "/qml/icons/ic-copy.svg"
sourceSize.height: root.colorScheme.body_font_size
MouseArea {
anchors.fill: parent
- onClicked : {
- valueText.select(0, valueText.length)
- valueText.copy()
- valueText.deselect()
+
+ onClicked: {
+ valueText.select(0, valueText.length);
+ valueText.copy();
+ valueText.deselect();
}
onPressed: parent.scale = 0.90
onReleased: parent.scale = 1
}
-
}
}
-
Rectangle {
Layout.fillWidth: true
- height: 1
color: root.colorScheme.border_norm
+ height: 1
}
}
}
diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/ConnectionModeSettings.qml b/internal/frontend/bridge-gui/bridge-gui/qml/ConnectionModeSettings.qml
index 5ab2e8f2..40daf623 100644
--- a/internal/frontend/bridge-gui/bridge-gui/qml/ConnectionModeSettings.qml
+++ b/internal/frontend/bridge-gui/bridge-gui/qml/ConnectionModeSettings.qml
@@ -1,155 +1,138 @@
// Copyright (c) 2023 Proton AG
-//
// This file is part of Proton Mail Bridge.
-//
// Proton Mail Bridge 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 3 of the License, or
// (at your option) any later version.
-//
// Proton Mail Bridge 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.
-//
// You should have received a copy of the GNU General Public License
// along with Proton Mail Bridge. If not, see .
-
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import QtQuick.Controls.impl
-
import Proton
SettingsView {
id: root
+ function setDefaultValues() {
+ imapSSLButton.checked = Backend.useSSLForIMAP;
+ imapSTARTTLSButton.checked = !Backend.useSSLForIMAP;
+ smtpSSLButton.checked = Backend.useSSLForSMTP;
+ smtpSTARTTLSButton.checked = !Backend.useSSLForSMTP;
+ }
+ function submit() {
+ submitButton.loading = true;
+ Backend.setMailServerSettings(Backend.imapPort, Backend.smtpPort, imapSSLButton.checked, smtpSSLButton.checked);
+ }
fillHeight: false
+ onVisibleChanged: {
+ root.setDefaultValues();
+ }
+
Label {
+ Layout.fillWidth: true
colorScheme: root.colorScheme
text: qsTr("Connection mode")
type: Label.Heading
- Layout.fillWidth: true
}
-
Label {
+ Layout.fillWidth: true
+ color: root.colorScheme.text_weak
colorScheme: root.colorScheme
text: qsTr("Change the protocol Bridge and the email client use to connect for IMAP and SMTP.")
type: Label.Body
- color: root.colorScheme.text_weak
- Layout.fillWidth: true
wrapMode: Text.WordWrap
}
-
ColumnLayout {
spacing: 16
- ButtonGroup{ id: imapProtocolSelection }
-
+ ButtonGroup {
+ id: imapProtocolSelection
+ }
Label {
colorScheme: root.colorScheme
text: qsTr("IMAP connection")
}
-
RadioButton {
id: imapSSLButton
- colorScheme: root.colorScheme
ButtonGroup.group: imapProtocolSelection
+ colorScheme: root.colorScheme
text: qsTr("SSL")
}
-
RadioButton {
id: imapSTARTTLSButton
- colorScheme: root.colorScheme
ButtonGroup.group: imapProtocolSelection
+ colorScheme: root.colorScheme
text: qsTr("STARTTLS")
}
}
-
Rectangle {
Layout.fillWidth: true
- height: 1
color: root.colorScheme.border_weak
+ height: 1
}
-
ColumnLayout {
spacing: 16
- ButtonGroup{ id: smtpProtocolSelection }
-
+ ButtonGroup {
+ id: smtpProtocolSelection
+ }
Label {
colorScheme: root.colorScheme
text: qsTr("SMTP connection")
}
-
RadioButton {
id: smtpSSLButton
- colorScheme: root.colorScheme
ButtonGroup.group: smtpProtocolSelection
+ colorScheme: root.colorScheme
text: qsTr("SSL")
}
-
RadioButton {
id: smtpSTARTTLSButton
- colorScheme: root.colorScheme
ButtonGroup.group: smtpProtocolSelection
+ colorScheme: root.colorScheme
text: qsTr("STARTTLS")
}
}
-
Rectangle {
Layout.fillWidth: true
- height: 1
color: root.colorScheme.border_weak
+ height: 1
}
-
RowLayout {
spacing: 12
Button {
id: submitButton
colorScheme: root.colorScheme
- text: qsTr("Save")
- onClicked: {
- submitButton.loading = true
- root.submit()
- }
-
enabled: (!loading) && ((imapSSLButton.checked !== Backend.useSSLForIMAP) || (smtpSSLButton.checked !== Backend.useSSLForSMTP))
- }
+ text: qsTr("Save")
+ onClicked: {
+ submitButton.loading = true;
+ root.submit();
+ }
+ }
Button {
colorScheme: root.colorScheme
- text: qsTr("Cancel")
- onClicked: root.back()
secondary: true
- }
+ text: qsTr("Cancel")
+ onClicked: root.back()
+ }
Connections {
- target: Backend
-
function onChangeMailServerSettingsFinished() {
- submitButton.loading = false
- root.back()
+ submitButton.loading = false;
+ root.back();
}
+
+ target: Backend
}
}
-
- function submit(){
- submitButton.loading = true
- Backend.setMailServerSettings(Backend.imapPort, Backend.smtpPort, imapSSLButton.checked, smtpSSLButton.checked)
- }
-
- function setDefaultValues(){
- imapSSLButton.checked = Backend.useSSLForIMAP
- imapSTARTTLSButton.checked = !Backend.useSSLForIMAP
- smtpSSLButton.checked = Backend.useSSLForSMTP
- smtpSTARTTLSButton.checked = !Backend.useSSLForSMTP
- }
-
- onVisibleChanged: {
- root.setDefaultValues()
- }
}
diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/ContentWrapper.qml b/internal/frontend/bridge-gui/bridge-gui/qml/ContentWrapper.qml
index 6dc46d79..eca31c53 100644
--- a/internal/frontend/bridge-gui/bridge-gui/qml/ContentWrapper.qml
+++ b/internal/frontend/bridge-gui/bridge-gui/qml/ContentWrapper.qml
@@ -1,36 +1,62 @@
// Copyright (c) 2023 Proton AG
-//
// This file is part of Proton Mail Bridge.
-//
// Proton Mail Bridge 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 3 of the License, or
// (at your option) any later version.
-//
// Proton Mail Bridge 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.
-//
// You should have received a copy of the GNU General Public License
// along with Proton Mail Bridge. If not, see .
-
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
-
import Proton
import Notifications
Item {
id: root
- property ColorScheme colorScheme
+ property ColorScheme colorScheme
property var notifications
+ signal closeWindow
+ signal quitBridge
signal showSetupGuide(var user, string address)
- signal closeWindow()
- signal quitBridge()
+
+ function selectUser(userID) {
+ const users = Backend.users;
+ for (let i = 0; i < users.count; i++) {
+ const user = users.get(i);
+ if (user.id !== userID) {
+ continue;
+ }
+ accounts.currentIndex = i;
+ if (user.state === EUserState.SignedOut)
+ showSignIn(user.primaryEmailOrUsername());
+ return;
+ }
+ console.error("User with ID ", userID, " was not found in the account list");
+ }
+ function showBugReportAndPrefill(description) {
+ rightContent.showBugReport();
+ bugReport.setDescription(description);
+ }
+ function showHelp() {
+ rightContent.showHelpView();
+ }
+ function showLocalCacheSettings() {
+ rightContent.showLocalCacheSettings();
+ }
+ function showSettings() {
+ rightContent.showGeneralSettings();
+ }
+ function showSignIn(username) {
+ signIn.username = username;
+ rightContent.showSignIn();
+ }
RowLayout {
anchors.fill: parent
@@ -38,13 +64,13 @@ Item {
Rectangle {
id: leftBar
+
property ColorScheme colorScheme: root.colorScheme.prominent
- Layout.minimumWidth: 264
- Layout.maximumWidth: 320
- Layout.preferredWidth: 320
Layout.fillHeight: true
-
+ Layout.maximumWidth: 320
+ Layout.minimumWidth: 264
+ Layout.preferredWidth: 320
color: colorScheme.background_norm
ColumnLayout {
@@ -52,24 +78,21 @@ Item {
spacing: 0
RowLayout {
- id:topLeftBar
-
+ id: topLeftBar
Layout.fillWidth: true
- Layout.minimumHeight: 60
Layout.maximumHeight: 60
+ Layout.minimumHeight: 60
Layout.preferredHeight: 60
spacing: 0
Status {
+ Layout.alignment: Qt.AlignHCenter
+ Layout.bottomMargin: 17
Layout.leftMargin: 16
Layout.topMargin: 24
- Layout.bottomMargin: 17
- Layout.alignment: Qt.AlignHCenter
-
colorScheme: leftBar.colorScheme
- notifications: root.notifications
-
notificationWhitelist: Notifications.Group.Connection | Notifications.Group.ForceUpdate
+ notifications: root.notifications
}
// just a placeholder
@@ -77,47 +100,38 @@ Item {
Layout.fillHeight: true
Layout.fillWidth: true
}
-
Button {
- colorScheme: leftBar.colorScheme
- Layout.minimumHeight: 36
- Layout.maximumHeight: 36
- Layout.preferredHeight: 36
- Layout.minimumWidth: 36
- Layout.maximumWidth: 36
- Layout.preferredWidth: 36
-
- Layout.topMargin: 16
Layout.bottomMargin: 9
+ Layout.maximumHeight: 36
+ Layout.maximumWidth: 36
+ Layout.minimumHeight: 36
+ Layout.minimumWidth: 36
+ Layout.preferredHeight: 36
+ Layout.preferredWidth: 36
Layout.rightMargin: 4
-
+ Layout.topMargin: 16
+ colorScheme: leftBar.colorScheme
horizontalPadding: 0
-
icon.source: "/qml/icons/ic-question-circle.svg"
onClicked: rightContent.showHelpView()
}
-
Button {
- colorScheme: leftBar.colorScheme
- Layout.minimumHeight: 36
- Layout.maximumHeight: 36
- Layout.preferredHeight: 36
- Layout.minimumWidth: 36
- Layout.maximumWidth: 36
- Layout.preferredWidth: 36
-
- Layout.topMargin: 16
Layout.bottomMargin: 9
+ Layout.maximumHeight: 36
+ Layout.maximumWidth: 36
+ Layout.minimumHeight: 36
+ Layout.minimumWidth: 36
+ Layout.preferredHeight: 36
+ Layout.preferredWidth: 36
Layout.rightMargin: 4
-
+ Layout.topMargin: 16
+ colorScheme: leftBar.colorScheme
horizontalPadding: 0
-
icon.source: "/qml/icons/ic-cog-wheel.svg"
onClicked: rightContent.showGeneralSettings()
}
-
Button {
id: dotMenuButton
Layout.bottomMargin: 9
@@ -134,7 +148,7 @@ Item {
icon.source: "/qml/icons/ic-three-dots-vertical.svg"
onClicked: {
- dotMenu.open()
+ dotMenu.open();
}
Menu {
@@ -143,332 +157,319 @@ Item {
modal: true
y: dotMenuButton.Layout.preferredHeight + dotMenuButton.Layout.bottomMargin
+ onClosed: {
+ parent.checked = false;
+ }
+ onOpened: {
+ parent.checked = true;
+ }
+
MenuItem {
colorScheme: root.colorScheme
text: qsTr("Close window")
+
onClicked: {
- root.closeWindow()
+ root.closeWindow();
}
}
MenuItem {
colorScheme: root.colorScheme
text: qsTr("Quit Bridge")
- onClicked: {
- root.quitBridge()
- }
- }
- onClosed: {
- parent.checked = false
- }
- onOpened: {
- parent.checked = true
+ onClicked: {
+ root.quitBridge();
+ }
}
}
}
}
-
- Item {implicitHeight:10}
+ Item {
+ implicitHeight: 10
+ }
// Separator line
Rectangle {
Layout.fillWidth: true
- Layout.minimumHeight: 1
Layout.maximumHeight: 1
+ Layout.minimumHeight: 1
color: leftBar.colorScheme.border_weak
}
-
ListView {
id: accounts
- property var _topBottomMargins: 24
property var _leftRightMargins: 16
+ property var _topBottomMargins: 24
- Layout.fillWidth: true
+ Layout.bottomMargin: accounts._topBottomMargins
Layout.fillHeight: true
+ Layout.fillWidth: true
Layout.leftMargin: accounts._leftRightMargins
Layout.rightMargin: accounts._leftRightMargins
Layout.topMargin: accounts._topBottomMargins
- Layout.bottomMargin: accounts._topBottomMargins
-
- spacing: 12
- clip: true
boundsBehavior: Flickable.StopAtBounds
+ clip: true
+ model: Backend.users
+ spacing: 12
- header: Rectangle {
- height: headerLabel.height+16
- // color: ProtonStyle.transparent
- Label{
+ delegate: Item {
+ implicitHeight: children[0].implicitHeight + children[0].anchors.topMargin + children[0].anchors.bottomMargin
+ implicitWidth: children[0].implicitWidth + children[0].anchors.leftMargin + children[0].anchors.rightMargin
+ width: leftBar.width - 2 * accounts._leftRightMargins
+
+ AccountDelegate {
+ id: accountDelegate
+ anchors.bottomMargin: 8
+ anchors.fill: parent
+ anchors.leftMargin: 12
+ anchors.rightMargin: 12
+ anchors.topMargin: 8
colorScheme: leftBar.colorScheme
+ user: Backend.users.get(index)
+ }
+ MouseArea {
+ anchors.fill: parent
+
+ onClicked: {
+ const user = Backend.users.get(index);
+ accounts.currentIndex = index;
+ if (!user)
+ return;
+ if (user.state !== EUserState.SignedOut) {
+ rightContent.showAccount();
+ } else {
+ signIn.username = user.primaryEmailOrUsername();
+ rightContent.showSignIn();
+ }
+ }
+ }
+ }
+ header: Rectangle {
+ height: headerLabel.height + 16
+
+ // color: ProtonStyle.transparent
+ Label {
id: headerLabel
+ colorScheme: leftBar.colorScheme
text: qsTr("Accounts")
type: Label.LabelType.Body
}
}
-
highlight: Rectangle {
color: leftBar.colorScheme.interaction_default_active
radius: ProtonStyle.account_row_radius
}
-
- model: Backend.users
- delegate: Item {
- width: leftBar.width - 2*accounts._leftRightMargins
- implicitHeight: children[0].implicitHeight + children[0].anchors.topMargin + children[0].anchors.bottomMargin
- implicitWidth: children[0].implicitWidth + children[0].anchors.leftMargin + children[0].anchors.rightMargin
-
- AccountDelegate {
- id: accountDelegate
-
- anchors.fill: parent
- anchors.topMargin: 8
- anchors.bottomMargin: 8
- anchors.leftMargin: 12
- anchors.rightMargin: 12
-
- colorScheme: leftBar.colorScheme
- user: Backend.users.get(index)
- }
-
- MouseArea {
- anchors.fill: parent
- onClicked: {
- var user = Backend.users.get(index)
- accounts.currentIndex = index
- if (!user) return
- if (user.state !== EUserState.SignedOut) {
- rightContent.showAccount()
- } else {
- signIn.username = user.primaryEmailOrUsername()
- rightContent.showSignIn()
- }
- }
- }
- }
}
// Separator
Rectangle {
Layout.fillWidth: true
- Layout.minimumHeight: 1
Layout.maximumHeight: 1
+ Layout.minimumHeight: 1
color: leftBar.colorScheme.border_weak
}
-
Item {
id: bottomLeftBar
-
Layout.fillWidth: true
- Layout.minimumHeight: 52
Layout.maximumHeight: 52
+ Layout.minimumHeight: 52
Layout.preferredHeight: 52
Button {
- colorScheme: leftBar.colorScheme
- width: 36
- height: 36
-
anchors.left: parent.left
- anchors.top: parent.top
-
anchors.leftMargin: 16
+ anchors.top: parent.top
anchors.topMargin: 7
-
+ colorScheme: leftBar.colorScheme
+ height: 36
horizontalPadding: 0
-
icon.source: "/qml/icons/ic-plus.svg"
+ width: 36
onClicked: {
- signIn.username = ""
- rightContent.showSignIn()
+ signIn.username = "";
+ rightContent.showSignIn();
}
}
}
}
}
-
- Rectangle { // right content background
+ Rectangle {
+ Layout.fillHeight: true // right content background
Layout.fillWidth: true
- Layout.fillHeight: true
-
color: colorScheme.background_norm
StackLayout {
id: rightContent
+ function showAccount(index) {
+ if (index !== undefined && index >= 0) {
+ accounts.currentIndex = index;
+ }
+ rightContent.currentIndex = 0;
+ }
+ function showBugReport() {
+ rightContent.currentIndex = 8;
+ }
+ function showConnectionModeSettings() {
+ rightContent.currentIndex = 5;
+ }
+ function showGeneralSettings() {
+ rightContent.currentIndex = 2;
+ }
+ function showHelpView() {
+ rightContent.currentIndex = 7;
+ }
+ function showKeychainSettings() {
+ rightContent.currentIndex = 3;
+ }
+ function showLocalCacheSettings() {
+ rightContent.currentIndex = 6;
+ }
+ function showPortSettings() {
+ rightContent.currentIndex = 4;
+ }
+ function showSignIn() {
+ rightContent.currentIndex = 1;
+ signIn.focus = true;
+ }
+
anchors.fill: parent
- AccountView { // 0
+ AccountView {
+ // 0
colorScheme: root.colorScheme
notifications: root.notifications
user: {
- if (accounts.currentIndex < 0) return undefined
- if (Backend.users.count == 0) return undefined
- return Backend.users.get(accounts.currentIndex)
+ if (accounts.currentIndex < 0)
+ return undefined;
+ if (Backend.users.count === 0)
+ return undefined;
+ return Backend.users.get(accounts.currentIndex);
+ }
+
+ onShowSetupGuide: function (user, address) {
+ root.showSetupGuide(user, address);
}
onShowSignIn: {
- var user = this.user
- signIn.username = user ? user.primaryEmailOrUsername() : ""
- rightContent.showSignIn()
- }
- onShowSetupGuide: function(user, address) {
- root.showSetupGuide(user,address)
+ const user = this.user;
+ signIn.username = user ? user.primaryEmailOrUsername() : "";
+ rightContent.showSignIn();
}
}
-
- GridLayout { // 1 Sign In
+ GridLayout {
+ // 1 Sign In
columns: 2
Button {
id: backButton
+ Layout.alignment: Qt.AlignTop
Layout.leftMargin: 18
Layout.topMargin: 10
- Layout.alignment: Qt.AlignTop
-
colorScheme: root.colorScheme
- onClicked: {
- signIn.abort()
- rightContent.showAccount()
- }
+ horizontalPadding: 8
icon.source: "/qml/icons/ic-arrow-left.svg"
secondary: true
- horizontalPadding: 8
- }
+ onClicked: {
+ signIn.abort();
+ rightContent.showAccount();
+ }
+ }
SignIn {
id: signIn
- Layout.topMargin: 68
- Layout.leftMargin: 80 - backButton.width - 18
- Layout.rightMargin: 80
Layout.bottomMargin: 68
- Layout.preferredWidth: 320
- Layout.fillWidth: true
Layout.fillHeight: true
-
+ Layout.fillWidth: true
+ Layout.leftMargin: 80 - backButton.width - 18
+ Layout.preferredWidth: 320
+ Layout.rightMargin: 80
+ Layout.topMargin: 68
colorScheme: root.colorScheme
}
}
-
- GeneralSettings { // 2
+ GeneralSettings {
+ // 2
colorScheme: root.colorScheme
notifications: root.notifications
onBack: {
- rightContent.showAccount()
+ rightContent.showAccount();
}
}
-
- KeychainSettings { // 3
+ KeychainSettings {
+ // 3
colorScheme: root.colorScheme
onBack: {
- rightContent.showGeneralSettings()
+ rightContent.showGeneralSettings();
}
}
-
- PortSettings { // 4
- colorScheme: root.colorScheme
- notifications: root.notifications
- onBack: {
- rightContent.showGeneralSettings()
- }
- }
-
- ConnectionModeSettings { // 5
- colorScheme: root.colorScheme
-
- onBack: {
- rightContent.showGeneralSettings()
- }
- }
-
- LocalCacheSettings { // 6
+ PortSettings {
+ // 4
colorScheme: root.colorScheme
notifications: root.notifications
onBack: {
- rightContent.showGeneralSettings()
+ rightContent.showGeneralSettings();
}
}
-
- HelpView { // 7
+ ConnectionModeSettings {
+ // 5
colorScheme: root.colorScheme
onBack: {
- rightContent.showAccount()
+ rightContent.showGeneralSettings();
}
}
+ LocalCacheSettings {
+ // 6
+ colorScheme: root.colorScheme
+ notifications: root.notifications
- BugReportView { // 8
+ onBack: {
+ rightContent.showGeneralSettings();
+ }
+ }
+ HelpView {
+ // 7
+ colorScheme: root.colorScheme
+
+ onBack: {
+ rightContent.showAccount();
+ }
+ }
+ BugReportView {
+ // 8
id: bugReport
colorScheme: root.colorScheme
selectedAddress: {
- if (accounts.currentIndex < 0) return ""
- if (Backend.users.count == 0) return ""
- var user = Backend.users.get(accounts.currentIndex)
- if (!user) return ""
- return user.addresses[0]
+ if (accounts.currentIndex < 0)
+ return "";
+ if (Backend.users.count === 0)
+ return "";
+ const user = Backend.users.get(accounts.currentIndex);
+ if (!user)
+ return "";
+ return user.addresses[0];
}
onBack: {
- rightContent.showHelpView()
+ rightContent.showHelpView();
}
-
onBugReportWasSent: {
- rightContent.showAccount()
+ rightContent.showAccount();
}
}
-
- function showAccount(index) {
- if (index !== undefined && index >= 0){
- accounts.currentIndex = index
- }
- rightContent.currentIndex = 0
- }
-
- function showSignIn () { rightContent.currentIndex = 1; signIn.focus = true }
- function showGeneralSettings () { rightContent.currentIndex = 2 }
- function showKeychainSettings () { rightContent.currentIndex = 3 }
- function showPortSettings () { rightContent.currentIndex = 4 }
- function showConnectionModeSettings() { rightContent.currentIndex = 5 }
- function showLocalCacheSettings () { rightContent.currentIndex = 6 }
- function showHelpView () { rightContent.currentIndex = 7 }
- function showBugReport () { rightContent.currentIndex = 8 }
-
Connections {
- target: Backend
+ function onLoginAlreadyLoggedIn(index) {
+ rightContent.showAccount(index);
+ }
+ function onLoginFinished(index) {
+ rightContent.showAccount(index);
+ }
- function onLoginFinished(index) { rightContent.showAccount(index) }
- function onLoginAlreadyLoggedIn(index) { rightContent.showAccount(index) }
+ target: Backend
}
}
}
}
-
- function showLocalCacheSettings(){rightContent.showLocalCacheSettings() }
- function showSettings(){rightContent.showGeneralSettings() }
- function showHelp(){rightContent.showHelpView() }
- function showSignIn(username){
- signIn.username = username
- rightContent.showSignIn()
- }
-
- function selectUser(userID) {
- var users = Backend.users;
- for (var i = 0; i < users.count; i++) {
- var user = users.get(i)
- if (user.id !== userID) {
- continue;
- }
- accounts.currentIndex = i;
- if (user.state === EUserState.SignedOut)
- showSignIn(user.primaryEmailOrUsername())
- return;
- }
- console.error("User with ID ", userID, " was not found in the account list")
- }
-
- function showBugReportAndPrefill(description) {
- rightContent.showBugReport()
- bugReport.setDescription(description)
- }
}
diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/DebugWrapper.qml b/internal/frontend/bridge-gui/bridge-gui/qml/DebugWrapper.qml
index fafb9a5d..eb8bd15c 100644
--- a/internal/frontend/bridge-gui/bridge-gui/qml/DebugWrapper.qml
+++ b/internal/frontend/bridge-gui/bridge-gui/qml/DebugWrapper.qml
@@ -1,54 +1,45 @@
// Copyright (c) 2023 Proton AG
-//
// This file is part of Proton Mail Bridge.
-//
// Proton Mail Bridge 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 3 of the License, or
// (at your option) any later version.
-//
// Proton Mail Bridge 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.
-//
// You should have received a copy of the GNU General Public License
// along with Proton Mail Bridge. If not, see .
-
import QtQuick
import QtQuick.Controls
-
import "."
-import "./Proton"
+import "Proton"
Rectangle {
property var target: parent
- x: target.x
- y: target.y
- width: target.width
- height: target.height
-
- color: "transparent"
border.color: "red"
border.width: 1
+ color: "transparent"
+ height: target.height
+ width: target.width
+ x: target.x
+ y: target.y
//z: parent.z - 1
z: 10000000
Label {
- text: parent.width + "x" + parent.height
anchors.centerIn: parent
color: "black"
colorScheme: ProtonStyle.currentStyle
+ text: parent.width + "x" + parent.height
}
-
Rectangle {
- width: target.implicitWidth
- height: target.implicitHeight
-
- color: "transparent"
border.color: "green"
border.width: 1
+ color: "transparent"
+ height: target.implicitHeight
+ width: target.implicitWidth
//z: parent.z - 1
z: 10000000
}
diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/GeneralSettings.qml b/internal/frontend/bridge-gui/bridge-gui/qml/GeneralSettings.qml
index 63be1c42..c29bc896 100644
--- a/internal/frontend/bridge-gui/bridge-gui/qml/GeneralSettings.qml
+++ b/internal/frontend/bridge-gui/bridge-gui/qml/GeneralSettings.qml
@@ -1,25 +1,19 @@
// Copyright (c) 2023 Proton AG
-//
// This file is part of Proton Mail Bridge.
-//
// Proton Mail Bridge 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 3 of the License, or
// (at your option) any later version.
-//
// Proton Mail Bridge 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.
-//
// You should have received a copy of the GNU General Public License
// along with Proton Mail Bridge. If not, see .
-
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import QtQuick.Controls.impl
-
import Proton
SettingsView {
@@ -31,144 +25,138 @@ SettingsView {
fillHeight: false
Label {
+ Layout.fillWidth: true
colorScheme: root.colorScheme
text: qsTr("Settings")
type: Label.Heading
- Layout.fillWidth: true
}
-
SettingsItem {
id: autoUpdate
- colorScheme: root.colorScheme
- text: qsTr("Automatic updates")
- description: qsTr("Bridge will automatically update in the background.")
- type: SettingsItem.Toggle
- checked: Backend.isAutomaticUpdateOn
- onClicked: Backend.toggleAutomaticUpdate(!autoUpdate.checked)
-
Layout.fillWidth: true
- }
+ checked: Backend.isAutomaticUpdateOn
+ colorScheme: root.colorScheme
+ description: qsTr("Bridge will automatically update in the background.")
+ text: qsTr("Automatic updates")
+ type: SettingsItem.Toggle
+ onClicked: Backend.toggleAutomaticUpdate(!autoUpdate.checked)
+ }
SettingsItem {
id: autostart
- colorScheme: root.colorScheme
- text: qsTr("Open on startup")
- description: qsTr("Bridge will open upon startup.")
- type: SettingsItem.Toggle
- checked: Backend.isAutostartOn
- onClicked: {
- autostart.loading = true
- Backend.toggleAutostart(!autostart.checked)
- }
- Connections{
- target: Backend
- function onToggleAutostartFinished() {
- autostart.loading = false
- }
- }
-
Layout.fillWidth: true
- }
+ checked: Backend.isAutostartOn
+ colorScheme: root.colorScheme
+ description: qsTr("Bridge will open upon startup.")
+ text: qsTr("Open on startup")
+ type: SettingsItem.Toggle
+ onClicked: {
+ autostart.loading = true;
+ Backend.toggleAutostart(!autostart.checked);
+ }
+
+ Connections {
+ function onToggleAutostartFinished() {
+ autostart.loading = false;
+ }
+
+ target: Backend
+ }
+ }
SettingsItem {
id: beta
- colorScheme: root.colorScheme
- text: qsTr("Beta access")
- description: qsTr("Be among the first to try new features.")
- type: SettingsItem.Toggle
+ Layout.fillWidth: true
checked: Backend.isBetaEnabled
+ colorScheme: root.colorScheme
+ description: qsTr("Be among the first to try new features.")
+ text: qsTr("Beta access")
+ type: SettingsItem.Toggle
+
onClicked: {
if (!beta.checked) {
- root.notifications.askEnableBeta()
+ root.notifications.askEnableBeta();
} else {
- Backend.toggleBeta(false)
+ Backend.toggleBeta(false);
}
}
-
- Layout.fillWidth: true
}
-
RowLayout {
ColorImage {
Layout.alignment: Qt.AlignCenter
-
- source: root._isAdvancedShown ? "/qml/icons/ic-chevron-down.svg" : "/qml/icons/ic-chevron-right.svg"
color: root.colorScheme.interaction_norm
height: root.colorScheme.body_font_size
+ source: root._isAdvancedShown ? "/qml/icons/ic-chevron-down.svg" : "/qml/icons/ic-chevron-right.svg"
sourceSize.height: root.colorScheme.body_font_size
+
MouseArea {
anchors.fill: parent
+
onClicked: root._isAdvancedShown = !root._isAdvancedShown
}
}
-
Label {
id: advSettLabel
+ color: root.colorScheme.interaction_norm
colorScheme: root.colorScheme
text: qsTr("Advanced settings")
- color: root.colorScheme.interaction_norm
type: Label.Body
MouseArea {
anchors.fill: parent
+
onClicked: root._isAdvancedShown = !root._isAdvancedShown
}
}
}
-
SettingsItem {
id: keychains
- visible: root._isAdvancedShown && Backend.availableKeychain.length > 1
- colorScheme: root.colorScheme
- text: qsTr("Change keychain")
- description: qsTr("Change which keychain Bridge uses as default")
- actionText: qsTr("Change")
- type: SettingsItem.Button
- checked: Backend.isDoHEnabled
- onClicked: root.parent.showKeychainSettings()
-
Layout.fillWidth: true
- }
+ actionText: qsTr("Change")
+ checked: Backend.isDoHEnabled
+ colorScheme: root.colorScheme
+ description: qsTr("Change which keychain Bridge uses as default")
+ text: qsTr("Change keychain")
+ type: SettingsItem.Button
+ visible: root._isAdvancedShown && Backend.availableKeychain.length > 1
+ onClicked: root.parent.showKeychainSettings()
+ }
SettingsItem {
id: doh
- visible: root._isAdvancedShown
- colorScheme: root.colorScheme
- text: qsTr("Alternative routing")
- description: qsTr("If Proton’s servers are blocked in your location, alternative network routing will be used to reach Proton.")
- type: SettingsItem.Toggle
- checked: Backend.isDoHEnabled
- onClicked: Backend.toggleDoH(!doh.checked)
-
Layout.fillWidth: true
- }
+ checked: Backend.isDoHEnabled
+ colorScheme: root.colorScheme
+ description: qsTr("If Proton’s servers are blocked in your location, alternative network routing will be used to reach Proton.")
+ text: qsTr("Alternative routing")
+ type: SettingsItem.Toggle
+ visible: root._isAdvancedShown
+ onClicked: Backend.toggleDoH(!doh.checked)
+ }
SettingsItem {
id: darkMode
- visible: root._isAdvancedShown
- colorScheme: root.colorScheme
- text: qsTr("Dark mode")
- description: qsTr("Choose dark color theme.")
- type: SettingsItem.Toggle
- checked: Backend.colorSchemeName == "dark"
- onClicked: Backend.changeColorScheme( darkMode.checked ? "light" : "dark")
-
Layout.fillWidth: true
- }
+ checked: Backend.colorSchemeName === "dark"
+ colorScheme: root.colorScheme
+ description: qsTr("Choose dark color theme.")
+ text: qsTr("Dark mode")
+ type: SettingsItem.Toggle
+ visible: root._isAdvancedShown
+ onClicked: Backend.changeColorScheme(darkMode.checked ? "light" : "dark")
+ }
SettingsItem {
id: allMail
- visible: root._isAdvancedShown
- colorScheme: root.colorScheme
- text: qsTr("Show All Mail")
- description: qsTr("Choose to list the All Mail folder in your local client.")
- type: SettingsItem.Toggle
- checked: Backend.isAllMailVisible
- onClicked: root.notifications.askChangeAllMailVisibility(Backend.isAllMailVisible)
-
Layout.fillWidth: true
- }
+ checked: Backend.isAllMailVisible
+ colorScheme: root.colorScheme
+ description: qsTr("Choose to list the All Mail folder in your local client.")
+ text: qsTr("Show All Mail")
+ type: SettingsItem.Toggle
+ visible: root._isAdvancedShown
+ onClicked: root.notifications.askChangeAllMailVisibility(Backend.isAllMailVisible)
+ }
SettingsItem {
id: telemetry
Layout.fillWidth: true
@@ -181,73 +169,68 @@ SettingsView {
onClicked: Backend.toggleIsTelemetryDisabled(telemetry.checked)
}
-
SettingsItem {
id: ports
- visible: root._isAdvancedShown
- colorScheme: root.colorScheme
- text: qsTr("Default ports")
- actionText: qsTr("Change")
- description: qsTr("Choose which ports are used by default.")
- type: SettingsItem.Button
- onClicked: root.parent.showPortSettings()
-
Layout.fillWidth: true
- }
+ actionText: qsTr("Change")
+ colorScheme: root.colorScheme
+ description: qsTr("Choose which ports are used by default.")
+ text: qsTr("Default ports")
+ type: SettingsItem.Button
+ visible: root._isAdvancedShown
+ onClicked: root.parent.showPortSettings()
+ }
SettingsItem {
id: imap
- visible: root._isAdvancedShown
- colorScheme: root.colorScheme
- text: qsTr("Connection mode")
- actionText: qsTr("Change")
- description: qsTr("Change the protocol Bridge and the email client use to connect for IMAP and SMTP.")
- type: SettingsItem.Button
- onClicked: root.parent.showConnectionModeSettings()
-
Layout.fillWidth: true
- }
+ actionText: qsTr("Change")
+ colorScheme: root.colorScheme
+ description: qsTr("Change the protocol Bridge and the email client use to connect for IMAP and SMTP.")
+ text: qsTr("Connection mode")
+ type: SettingsItem.Button
+ visible: root._isAdvancedShown
+ onClicked: root.parent.showConnectionModeSettings()
+ }
SettingsItem {
id: cache
- visible: root._isAdvancedShown
- colorScheme: root.colorScheme
- text: qsTr("Local cache")
- actionText: qsTr("Configure")
- description: qsTr("Configure Bridge's local cache.")
- type: SettingsItem.Button
- onClicked: root.parent.showLocalCacheSettings()
-
Layout.fillWidth: true
- }
+ actionText: qsTr("Configure")
+ colorScheme: root.colorScheme
+ description: qsTr("Configure Bridge's local cache.")
+ text: qsTr("Local cache")
+ type: SettingsItem.Button
+ visible: root._isAdvancedShown
+ onClicked: root.parent.showLocalCacheSettings()
+ }
SettingsItem {
id: exportTLSCertificates
- visible: root._isAdvancedShown
- colorScheme: root.colorScheme
- text: qsTr("Export TLS certificates")
- actionText: qsTr("Export")
- description: qsTr("Export the TLS private key and certificate used by the IMAP and SMTP servers.")
- type: SettingsItem.Button
- onClicked: {
- Backend.exportTLSCertificates()
- }
Layout.fillWidth: true
+ actionText: qsTr("Export")
+ colorScheme: root.colorScheme
+ description: qsTr("Export the TLS private key and certificate used by the IMAP and SMTP servers.")
+ text: qsTr("Export TLS certificates")
+ type: SettingsItem.Button
+ visible: root._isAdvancedShown
+ onClicked: {
+ Backend.exportTLSCertificates();
+ }
}
-
SettingsItem {
id: reset
- visible: root._isAdvancedShown
- colorScheme: root.colorScheme
- text: qsTr("Reset Bridge")
- actionText: qsTr("Reset")
- description: qsTr("Remove all accounts, clear cached data, and restore the original settings.")
- type: SettingsItem.Button
- onClicked: {
- root.notifications.askResetBridge()
- }
-
Layout.fillWidth: true
+ actionText: qsTr("Reset")
+ colorScheme: root.colorScheme
+ description: qsTr("Remove all accounts, clear cached data, and restore the original settings.")
+ text: qsTr("Reset Bridge")
+ type: SettingsItem.Button
+ visible: root._isAdvancedShown
+
+ onClicked: {
+ root.notifications.askResetBridge();
+ }
}
}
diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/HelpView.qml b/internal/frontend/bridge-gui/bridge-gui/qml/HelpView.qml
index beac6533..969567ba 100644
--- a/internal/frontend/bridge-gui/bridge-gui/qml/HelpView.qml
+++ b/internal/frontend/bridge-gui/bridge-gui/qml/HelpView.qml
@@ -1,126 +1,110 @@
// Copyright (c) 2023 Proton AG
-//
// This file is part of Proton Mail Bridge.
-//
// Proton Mail Bridge 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 3 of the License, or
// (at your option) any later version.
-//
// Proton Mail Bridge 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.
-//
// You should have received a copy of the GNU General Public License
// along with Proton Mail Bridge. If not, see .
-
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
-
import Proton
SettingsView {
id: root
-
fillHeight: true
Label {
+ Layout.fillWidth: true
colorScheme: root.colorScheme
text: qsTr("Help")
type: Label.Heading
- Layout.fillWidth: true
}
-
SettingsItem {
id: setupPage
- colorScheme: root.colorScheme
- text: qsTr("Installation and setup")
- actionText: qsTr("Go to help topics")
+ Layout.fillWidth: true
actionIcon: "/qml/icons/ic-external-link.svg"
+ actionText: qsTr("Go to help topics")
+ colorScheme: root.colorScheme
description: qsTr("Get help setting up your client with our instructions and FAQs.")
+ text: qsTr("Installation and setup")
type: SettingsItem.PrimaryButton
+
onClicked: {
Backend.notifyKBArticleClicked("https://proton.me/support/bridge");
- Qt.openUrlExternally("https://proton.me/support/bridge")}
-
- Layout.fillWidth: true
+ Qt.openUrlExternally("https://proton.me/support/bridge");
+ }
}
-
SettingsItem {
id: checkUpdates
- colorScheme: root.colorScheme
- text: qsTr("Updates")
+ Layout.fillWidth: true
actionText: qsTr("Check now")
+ colorScheme: root.colorScheme
description: qsTr("Check that you're using the latest version of Bridge. To stay up to date, enable auto-updates in settings.")
+ text: qsTr("Updates")
type: SettingsItem.Button
+
onClicked: {
- checkUpdates.loading = true
- Backend.checkUpdates()
+ checkUpdates.loading = true;
+ Backend.checkUpdates();
}
Connections {
+ function onCheckUpdatesFinished() {
+ checkUpdates.loading = false;
+ }
+
target: Backend
- function onCheckUpdatesFinished() { checkUpdates.loading = false }
}
-
- Layout.fillWidth: true
}
-
SettingsItem {
id: logs
- colorScheme: root.colorScheme
- text: qsTr("Logs")
- actionText: qsTr("View logs")
- description: qsTr("Open and review logs to troubleshoot.")
- type: SettingsItem.Button
- onClicked: Qt.openUrlExternally(Backend.logsPath)
-
Layout.fillWidth: true
- }
+ actionText: qsTr("View logs")
+ colorScheme: root.colorScheme
+ description: qsTr("Open and review logs to troubleshoot.")
+ text: qsTr("Logs")
+ type: SettingsItem.Button
+ onClicked: Qt.openUrlExternally(Backend.logsPath)
+ }
SettingsItem {
id: reportBug
- colorScheme: root.colorScheme
- text: qsTr("Report a problem")
- actionText: qsTr("Report a problem")
- description: qsTr("Something not working as expected? Let us know.")
- type: SettingsItem.Button
- onClicked: {
- Backend.updateCurrentMailClient()
- Backend.notifyReportBugClicked()
- root.parent.showBugReport()
- }
-
Layout.fillWidth: true
+ actionText: qsTr("Report a problem")
+ colorScheme: root.colorScheme
+ description: qsTr("Something not working as expected? Let us know.")
+ text: qsTr("Report a problem")
+ type: SettingsItem.Button
+
+ onClicked: {
+ Backend.updateCurrentMailClient();
+ Backend.notifyReportBugClicked();
+ root.parent.showBugReport();
+ }
}
- // fill height so the footer label will be always attached to the bottom
+ // fill height so the footer label will always be attached to the bottom
Item {
Layout.fillHeight: true
Layout.fillWidth: true
}
-
Label {
Layout.alignment: Qt.AlignHCenter
- colorScheme: root.colorScheme
- type: Label.Caption
color: root.colorScheme.text_weak
- textFormat: Text.StyledText
-
+ colorScheme: root.colorScheme
horizontalAlignment: Text.AlignHCenter
+ text: qsTr("%1 v%2 (%3)
© 2017-%4 %5
%6 %7
%8").arg(Backend.appname).arg(Backend.version).arg(Backend.tag).arg(Backend.buildYear()).arg(Backend.vendor).arg(link(Backend.licensePath, qsTr("License"))).arg(link(Backend.dependencyLicensesLink, qsTr("Dependencies"))).arg(link(Backend.releaseNotesLink, qsTr("Release notes")))
+ textFormat: Text.StyledText
+ type: Label.Caption
- text: qsTr("%1 v%2 (%3)
© 2017-%4 %5
%6 %7
%8").
- arg(Backend.appname).
- arg(Backend.version).
- arg(Backend.tag).
- arg(Backend.buildYear()).
- arg(Backend.vendor).
- arg(link(Backend.licensePath, qsTr("License"))).
- arg(link(Backend.dependencyLicensesLink, qsTr("Dependencies"))).
- arg(link(Backend.releaseNotesLink, qsTr("Release notes")))
-
- onLinkActivated: function(link) { Qt.openUrlExternally(link) }
+ onLinkActivated: function (link) {
+ Qt.openUrlExternally(link);
+ }
}
}
diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/KeychainSettings.qml b/internal/frontend/bridge-gui/bridge-gui/qml/KeychainSettings.qml
index 06af8379..fec873cc 100644
--- a/internal/frontend/bridge-gui/bridge-gui/qml/KeychainSettings.qml
+++ b/internal/frontend/bridge-gui/bridge-gui/qml/KeychainSettings.qml
@@ -1,116 +1,105 @@
// Copyright (c) 2023 Proton AG
-//
// This file is part of Proton Mail Bridge.
-//
// Proton Mail Bridge 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 3 of the License, or
// (at your option) any later version.
-//
// Proton Mail Bridge 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.
-//
// You should have received a copy of the GNU General Public License
// along with Proton Mail Bridge. If not, see .
-
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import QtQuick.Controls.impl
-
import Proton
SettingsView {
id: root
- fillHeight: false
+ property bool _valuesChanged: keychainSelection.checkedButton && keychainSelection.checkedButton.text !== Backend.currentKeychain
- property bool _valuesChanged: keychainSelection.checkedButton && keychainSelection.checkedButton.text != Backend.currentKeychain
-
- Label {
- colorScheme: root.colorScheme
- text: qsTr("Default keychain")
- type: Label.Heading
- Layout.fillWidth: true
- }
-
- Label {
- colorScheme: root.colorScheme
- text: qsTr("Change which keychain Bridge uses as default")
- type: Label.Body
- color: root.colorScheme.text_weak
- Layout.fillWidth: true
- wrapMode: Text.WordWrap
- }
-
- ColumnLayout {
- spacing: 16
-
- ButtonGroup{ id: keychainSelection }
-
- Repeater {
- model: Backend.availableKeychain
-
- RadioButton {
- colorScheme: root.colorScheme
- ButtonGroup.group: keychainSelection
- text: modelData
+ function setDefaultValues() {
+ for (const bi in keychainSelection.buttons) {
+ const button = keychainSelection.buttons[bi];
+ if (button.text === Backend.currentKeychain) {
+ button.checked = true;
+ break;
}
}
}
+ fillHeight: false
- Rectangle {
- Layout.fillWidth: true
- height: 1
- color: root.colorScheme.border_weak
+ Component.onCompleted: root.setDefaultValues()
+ onBack: {
+ root.setDefaultValues();
}
+ Label {
+ Layout.fillWidth: true
+ colorScheme: root.colorScheme
+ text: qsTr("Default keychain")
+ type: Label.Heading
+ }
+ Label {
+ Layout.fillWidth: true
+ color: root.colorScheme.text_weak
+ colorScheme: root.colorScheme
+ text: qsTr("Change which keychain Bridge uses as default")
+ type: Label.Body
+ wrapMode: Text.WordWrap
+ }
+ ColumnLayout {
+ spacing: 16
+
+ ButtonGroup {
+ id: keychainSelection
+ }
+ Repeater {
+ model: Backend.availableKeychain
+
+ RadioButton {
+ ButtonGroup.group: keychainSelection
+ colorScheme: root.colorScheme
+ text: modelData
+ }
+ }
+ }
+ Rectangle {
+ Layout.fillWidth: true
+ color: root.colorScheme.border_weak
+ height: 1
+ }
RowLayout {
spacing: 12
Button {
id: submitButton
colorScheme: root.colorScheme
- text: qsTr("Save and restart")
enabled: root._valuesChanged
+ text: qsTr("Save and restart")
+
onClicked: {
- Backend.changeKeychain(keychainSelection.checkedButton.text)
+ Backend.changeKeychain(keychainSelection.checkedButton.text);
}
}
-
Button {
colorScheme: root.colorScheme
- text: qsTr("Cancel")
- onClicked: root.back()
secondary: true
- }
+ text: qsTr("Cancel")
+ onClicked: root.back()
+ }
Connections {
- target: Backend
-
function onChangeKeychainFinished() {
- submitButton.loading = false
- root.back()
+ submitButton.loading = false;
+ root.back();
}
+
+ target: Backend
}
}
-
- onBack: {
- root.setDefaultValues()
- }
-
- function setDefaultValues(){
- for (var bi in keychainSelection.buttons){
- var button = keychainSelection.buttons[bi]
- if (button.text == Backend.currentKeychain) {
- button.checked = true
- break;
- }
- }
- }
-
- Component.onCompleted: root.setDefaultValues()
}
diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/LocalCacheSettings.qml b/internal/frontend/bridge-gui/bridge-gui/qml/LocalCacheSettings.qml
index 5dee608e..a319fd4d 100644
--- a/internal/frontend/bridge-gui/bridge-gui/qml/LocalCacheSettings.qml
+++ b/internal/frontend/bridge-gui/bridge-gui/qml/LocalCacheSettings.qml
@@ -1,81 +1,88 @@
// Copyright (c) 2023 Proton AG
-//
// This file is part of Proton Mail Bridge.
-//
// Proton Mail Bridge 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 3 of the License, or
// (at your option) any later version.
-//
// Proton Mail Bridge 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.
-//
// You should have received a copy of the GNU General Public License
// along with Proton Mail Bridge. If not, see .
-
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import QtQuick.Controls.impl
import QtQuick.Dialogs
-
import Proton
SettingsView {
id: root
- fillHeight: false
-
- property var notifications
property url diskCachePath: pathDialog.shortcuts.home
+ property var notifications
function refresh() {
- diskCacheSetting.description = Backend.nativePath(root.diskCachePath)
- submitButton.enabled = (!submitButton.loading) && !Backend.areSameFileOrFolder(Backend.diskCachePath, root.diskCachePath)
+ diskCacheSetting.description = Backend.nativePath(root.diskCachePath);
+ submitButton.enabled = (!submitButton.loading) && !Backend.areSameFileOrFolder(Backend.diskCachePath, root.diskCachePath);
+ }
+ function setDefaultValues() {
+ root.diskCachePath = Backend.diskCachePath;
+ root.refresh();
+ }
+ function submit() {
+ submitButton.loading = true;
+ Backend.setDiskCachePath(root.diskCachePath);
+ }
+
+ fillHeight: false
+
+ onBack: {
+ root.setDefaultValues();
+ }
+ onVisibleChanged: {
+ root.setDefaultValues();
}
Label {
+ Layout.fillWidth: true
colorScheme: root.colorScheme
text: qsTr("Local cache")
type: Label.Heading
- Layout.fillWidth: true
}
-
Label {
+ Layout.fillWidth: true
+ color: root.colorScheme.text_weak
colorScheme: root.colorScheme
text: qsTr("Bridge stores your encrypted messages locally to optimize communication with your client.")
type: Label.Body
- color: root.colorScheme.text_weak
- Layout.fillWidth: true
wrapMode: Text.WordWrap
}
-
SettingsItem {
id: diskCacheSetting
- colorScheme: root.colorScheme
- text: qsTr("Current cache location")
- actionText: qsTr("Change location")
- descriptionWrap: Text.WrapAnywhere
- type: SettingsItem.Button
- onClicked: {
- pathDialog.open()
- }
-
Layout.fillWidth: true
+ actionText: qsTr("Change location")
+ colorScheme: root.colorScheme
+ descriptionWrap: Text.WrapAnywhere
+ text: qsTr("Current cache location")
+ type: SettingsItem.Button
+
+ onClicked: {
+ pathDialog.open();
+ }
FolderDialog {
id: pathDialog
- title: qsTr("Select cache location")
currentFolder: root.diskCachePath
- onAccepted: {
- root.diskCachePath = pathDialog.selectedFolder
- root.refresh()
- }
- }
- }
+ title: qsTr("Select cache location")
+ onAccepted: {
+ root.diskCachePath = pathDialog.selectedFolder;
+ root.refresh();
+ }
+ }
+ }
RowLayout {
spacing: 12
@@ -83,43 +90,25 @@ SettingsView {
id: submitButton
colorScheme: root.colorScheme
text: qsTr("Save")
+
onClicked: {
- root.submit()
+ root.submit();
}
}
-
Button {
colorScheme: root.colorScheme
- text: qsTr("Cancel")
- onClicked: root.back()
secondary: true
- }
+ text: qsTr("Cancel")
+ onClicked: root.back()
+ }
Connections {
- target: Backend
-
function onDiskCachePathChangeFinished() {
- submitButton.loading = false
- root.setDefaultValues()
+ submitButton.loading = false;
+ root.setDefaultValues();
}
+
+ target: Backend
}
}
-
- onBack: {
- root.setDefaultValues()
- }
-
- function submit() {
- submitButton.loading = true
- Backend.setDiskCachePath(root.diskCachePath)
- }
-
- function setDefaultValues(){
- root.diskCachePath = Backend.diskCachePath
- root.refresh();
- }
-
- onVisibleChanged: {
- root.setDefaultValues()
- }
}
diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/MainWindow.qml b/internal/frontend/bridge-gui/bridge-gui/qml/MainWindow.qml
index 3de94f99..a76c1a6a 100644
--- a/internal/frontend/bridge-gui/bridge-gui/qml/MainWindow.qml
+++ b/internal/frontend/bridge-gui/bridge-gui/qml/MainWindow.qml
@@ -1,232 +1,202 @@
// Copyright (c) 2023 Proton AG
-//
// This file is part of Proton Mail Bridge.
-//
// Proton Mail Bridge 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 3 of the License, or
// (at your option) any later version.
-//
// Proton Mail Bridge 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.
-//
// You should have received a copy of the GNU General Public License
// along with Proton Mail Bridge. If not, see .
-
import QtQml
import QtQuick
import QtQuick.Window
import QtQuick.Layouts
import QtQuick.Controls
-
import Proton
import Notifications
ApplicationWindow {
id: root
- colorScheme: ProtonStyle.currentStyle
- visible: true
-
- property int _defaultWidth: 1080
property int _defaultHeight: 780
- width: _defaultWidth
+ property int _defaultWidth: 1080
+ property var notifications
+
+ function selectUser(userID) {
+ contentWrapper.selectUser(userID);
+ }
+ function showAndRise() {
+ root.show();
+ root.raise();
+ if (!root.active) {
+ root.requestActivate();
+ }
+ }
+ function showBugReportAndPrefill(message) {
+ contentWrapper.showBugReportAndPrefill(message);
+ }
+ function showHelp() {
+ contentWrapper.showHelp();
+ }
+ function showLocalCacheSettings() {
+ contentWrapper.showLocalCacheSettings();
+ }
+ function showSettings() {
+ contentWrapper.showSettings();
+ }
+ function showSetup(user, address) {
+ setupGuide.user = user;
+ setupGuide.address = address;
+ setupGuide.reset();
+ contentLayout._showSetup = !!setupGuide.user;
+ }
+ function showSignIn(username) {
+ if (contentLayout.currentIndex === 1)
+ return;
+ contentWrapper.showSignIn(username);
+ }
+
+ colorScheme: ProtonStyle.currentStyle
height: _defaultHeight
minimumWidth: _defaultWidth
-
- property var notifications
+ visible: true
+ width: _defaultWidth
// show Setup Guide on every new user
Connections {
- target: Backend.users
-
- function onRowsInserted(parent, first, last) {
- // considering that users are added one-by-one
- var user = Backend.users.get(first)
-
- if (user.state === EUserState.SignedOut) {
- return
- }
-
- if (user.setupGuideSeen) {
- return
- }
-
- root.showSetup(user,user.addresses[0])
- }
-
function onRowsAboutToBeRemoved(parent, first, last) {
- for (var i = first; i <= last; i++ ) {
- var user = Backend.users.get(i)
-
+ for (let i = first; i <= last; i++) {
+ const user = Backend.users.get(i);
if (setupGuide.user === user) {
- setupGuide.user = null
- contentLayout._showSetup = false
- return
+ setupGuide.user = null;
+ contentLayout._showSetup = false;
+ return;
}
}
}
- }
+ function onRowsInserted(parent, first, _) {
+ // considering that users are added one-by-one
+ const user = Backend.users.get(first);
+ if (user.state === EUserState.SignedOut) {
+ return;
+ }
+ if (user.setupGuideSeen) {
+ return;
+ }
+ root.showSetup(user, user.addresses[0]);
+ }
+ target: Backend.users
+ }
Connections {
- target: Backend
-
- function onShowMainWindow() {
- root.showAndRise()
- }
-
function onLoginFinished(index, wasSignedOut) {
- var user = Backend.users.get(index)
+ const user = Backend.users.get(index);
if (user && !wasSignedOut) {
- root.showSetup(user, user.addresses[0])
+ root.showSetup(user, user.addresses[0]);
}
- console.debug("Login finished", index)
+ console.debug("Login finished", index);
}
-
- function onShowHelp() {
- root.showHelp()
- root.showAndRise()
- }
-
- function onShowSettings() {
- root.showSettings()
- root.showAndRise()
- }
-
function onSelectUser(userID, forceShowWindow) {
- contentWrapper.selectUser(userID)
+ contentWrapper.selectUser(userID);
if (forceShowWindow) {
- root.showAndRise()
+ root.showAndRise();
}
}
- }
+ function onShowHelp() {
+ root.showHelp();
+ root.showAndRise();
+ }
+ function onShowMainWindow() {
+ root.showAndRise();
+ }
+ function onShowSettings() {
+ root.showSettings();
+ root.showAndRise();
+ }
+ target: Backend
+ }
StackLayout {
id: contentLayout
- anchors.fill: parent
-
property bool _showSetup: false
+
+ anchors.fill: parent
currentIndex: {
// show welcome when there are no users
if (Backend.users.count === 0) {
- return 1
+ return 1;
}
-
- var u = Backend.users.get(0)
-
+ const u = Backend.users.get(0);
if (!u) {
- console.trace()
- console.log("empty user")
- return 1
+ console.trace();
+ console.log("empty user");
+ return 1;
}
-
if ((Backend.users.count === 1) && (u.state === EUserState.SignedOut)) {
- showSignIn(u.primaryEmailOrUsername())
- return 0
+ showSignIn(u.primaryEmailOrUsername());
+ return 0;
}
-
if (contentLayout._showSetup) {
- return 2
+ return 2;
}
-
- return 0
+ return 0;
}
- ContentWrapper { // 0
+ ContentWrapper {
+ // 0
id: contentWrapper
+ Layout.fillHeight: true
+ Layout.fillWidth: true
colorScheme: root.colorScheme
notifications: root.notifications
- Layout.fillHeight: true
- Layout.fillWidth: true
-
- onShowSetupGuide: function(user, address) {
- root.showSetup(user,address)
- }
-
onCloseWindow: {
- root.close()
+ root.close();
}
-
onQuitBridge: {
// If we ever want to add a confirmation dialog before quitting:
//root.notifications.askQuestion("Quit Bridge", "Insert warning message here.", "Quit", "Cancel", Backend.quit, null)
- root.close()
- Backend.quit()
+ root.close();
+ Backend.quit();
+ }
+ onShowSetupGuide: function (user, address) {
+ root.showSetup(user, address);
}
}
-
- WelcomeGuide { // 1
- colorScheme: root.colorScheme
-
+ WelcomeGuide {
Layout.fillHeight: true
- Layout.fillWidth: true
+ Layout.fillWidth: true // 1
+ colorScheme: root.colorScheme
}
-
- SetupGuide { // 2
+ SetupGuide {
+ // 2
id: setupGuide
- colorScheme: root.colorScheme
-
Layout.fillHeight: true
Layout.fillWidth: true
+ colorScheme: root.colorScheme
onDismissed: {
- root.showSetup(null,"")
+ root.showSetup(null, "");
}
-
onFinished: {
// TODO: Do not close window. Trigger Backend to check that
// there is a successfully connected client. Then Backend
// should send another signal to close the setup guide.
- root.showSetup(null,"")
+ root.showSetup(null, "");
}
}
}
-
NotificationPopups {
colorScheme: root.colorScheme
- notifications: root.notifications
mainWindow: root
+ notifications: root.notifications
}
-
SplashScreen {
id: splashScreen
colorScheme: root.colorScheme
}
-
- function showLocalCacheSettings() { contentWrapper.showLocalCacheSettings() }
- function showSettings() { contentWrapper.showSettings() }
- function showHelp() { contentWrapper.showHelp() }
- function selectUser(userID) { contentWrapper.selectUser(userID) }
-
- function showBugReportAndPrefill(message) {
- contentWrapper.showBugReportAndPrefill(message)
- }
-
- function showSignIn(username) {
- if (contentLayout.currentIndex == 1) return
- contentWrapper.showSignIn(username)
- }
-
- function showSetup(user, address) {
- setupGuide.user = user
- setupGuide.address = address
- setupGuide.reset()
- if (setupGuide.user) {
- contentLayout._showSetup = true
- } else {
- contentLayout._showSetup = false
- }
- }
-
- function showAndRise() {
- root.show()
- root.raise()
- if (!root.active) {
- root.requestActivate()
- }
- }
}
diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/NotificationDialog.qml b/internal/frontend/bridge-gui/bridge-gui/qml/NotificationDialog.qml
index bd075a2e..a1031ad2 100644
--- a/internal/frontend/bridge-gui/bridge-gui/qml/NotificationDialog.qml
+++ b/internal/frontend/bridge-gui/bridge-gui/qml/NotificationDialog.qml
@@ -1,118 +1,99 @@
// Copyright (c) 2023 Proton AG
-//
// This file is part of Proton Mail Bridge.
-//
// Proton Mail Bridge 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 3 of the License, or
// (at your option) any later version.
-//
// Proton Mail Bridge 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.
-//
// You should have received a copy of the GNU General Public License
// along with Proton Mail Bridge. If not, see .
-
import QtQml
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
-
import Proton
import Notifications
Dialog {
id: root
+ default property alias data: additionalChildrenContainer.children
property var notification
- shouldShow: notification && notification.active && !notification.dismissed
modal: true
-
- default property alias data: additionalChildrenContainer.children
+ shouldShow: notification && notification.active && !notification.dismissed
ColumnLayout {
spacing: 0
Image {
Layout.alignment: Qt.AlignHCenter
-
- sourceSize.width: 64
- sourceSize.height: 64
-
+ Layout.bottomMargin: 16
Layout.preferredHeight: 64
Layout.preferredWidth: 64
-
- Layout.bottomMargin: 16
-
- visible: source != ""
-
source: {
if (!root.notification) {
- return ""
+ return "";
}
-
switch (root.notification.type) {
- case Notification.NotificationType.Info:
- return "/qml/icons/ic-info.svg"
- case Notification.NotificationType.Success:
- return "/qml/icons/ic-success.svg"
- case Notification.NotificationType.Warning:
- case Notification.NotificationType.Danger:
- return "/qml/icons/ic-alert.svg"
+ case Notification.NotificationType.Info:
+ return "/qml/icons/ic-info.svg";
+ case Notification.NotificationType.Success:
+ return "/qml/icons/ic-success.svg";
+ case Notification.NotificationType.Warning:
+ case Notification.NotificationType.Danger:
+ return "/qml/icons/ic-alert.svg";
}
}
+ sourceSize.height: 64
+ sourceSize.width: 64
+ visible: source != ""
}
-
Label {
Layout.alignment: Qt.AlignHCenter
- horizontalAlignment: Text.AlignHCenter
Layout.bottomMargin: 8
colorScheme: root.colorScheme
+ horizontalAlignment: Text.AlignHCenter
text: root.notification.title
type: Label.LabelType.Title
}
-
Label {
+ Layout.bottomMargin: 16
Layout.fillWidth: true
Layout.preferredWidth: 240
- Layout.bottomMargin: 16
-
colorScheme: root.colorScheme
- text: root.notification.description
- wrapMode: Text.WordWrap
horizontalAlignment: Text.AlignHCenter
+ text: root.notification.description
type: Label.LabelType.Body
- onLinkActivated: function(link) { Qt.openUrlExternally(link) }
- }
+ wrapMode: Text.WordWrap
+ onLinkActivated: function (link) {
+ Qt.openUrlExternally(link);
+ }
+ }
Item {
id: additionalChildrenContainer
-
- Layout.fillWidth: true
Layout.bottomMargin: 16
-
- visible: children.length > 0
-
+ Layout.fillWidth: true
implicitHeight: additionalChildrenContainer.childrenRect.height
implicitWidth: additionalChildrenContainer.childrenRect.width
+ visible: children.length > 0
}
-
ColumnLayout {
spacing: 8
+
Repeater {
model: root.notification.action
+
delegate: Button {
Layout.fillWidth: true
-
- colorScheme: root.colorScheme
action: modelData
-
- secondary: index > 0
-
+ colorScheme: root.colorScheme
loading: modelData.loading
+ secondary: index > 0
}
}
}
diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/NotificationPopups.qml b/internal/frontend/bridge-gui/bridge-gui/qml/NotificationPopups.qml
index bf6d45bb..08b2cc3a 100644
--- a/internal/frontend/bridge-gui/bridge-gui/qml/NotificationPopups.qml
+++ b/internal/frontend/bridge-gui/bridge-gui/qml/NotificationPopups.qml
@@ -1,25 +1,19 @@
// Copyright (c) 2023 Proton AG
-//
// This file is part of Proton Mail Bridge.
-//
// Proton Mail Bridge 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 3 of the License, or
// (at your option) any later version.
-//
// Proton Mail Bridge 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.
-//
// You should have received a copy of the GNU General Public License
// along with Proton Mail Bridge. If not, see .
-
import QtQml
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
-
import Proton
import Notifications
@@ -27,118 +21,98 @@ Item {
id: root
property ColorScheme colorScheme
- property var notifications
property var mainWindow
-
- property int notificationWhitelist: NotificationFilter.FilterConsts.All
property int notificationBlacklist: NotificationFilter.FilterConsts.None
+ property int notificationWhitelist: NotificationFilter.FilterConsts.All
+ property var notifications
NotificationFilter {
id: bannerNotificationFilter
-
- source: root.notifications.all
blacklist: Notifications.Group.Dialogs
+ source: root.notifications.all
}
-
Banner {
colorScheme: root.colorScheme
- notification: bannerNotificationFilter.topmost
mainWindow: root.mainWindow
+ notification: bannerNotificationFilter.topmost
}
-
NotificationDialog {
colorScheme: root.colorScheme
notification: root.notifications.updateManualReady
Switch {
- id:autoUpdate
+ id: autoUpdate
+ checked: Backend.isAutomaticUpdateOn
colorScheme: root.colorScheme
text: qsTr("Update automatically in the future")
- checked: Backend.isAutomaticUpdateOn
+
onClicked: Backend.toggleAutomaticUpdate(autoUpdate.checked)
}
}
-
NotificationDialog {
colorScheme: root.colorScheme
notification: root.notifications.updateForce
}
-
NotificationDialog {
colorScheme: root.colorScheme
notification: root.notifications.updateForceError
}
-
NotificationDialog {
colorScheme: root.colorScheme
notification: root.notifications.enableBeta
}
-
NotificationDialog {
colorScheme: root.colorScheme
notification: root.notifications.cacheUnavailable
}
-
NotificationDialog {
colorScheme: root.colorScheme
notification: root.notifications.cacheCantMove
}
-
NotificationDialog {
colorScheme: root.colorScheme
notification: root.notifications.diskFull
}
-
NotificationDialog {
colorScheme: root.colorScheme
notification: root.notifications.enableSplitMode
}
-
NotificationDialog {
colorScheme: root.colorScheme
notification: root.notifications.resetBridge
}
-
NotificationDialog {
colorScheme: root.colorScheme
notification: root.notifications.changeAllMailVisibility
}
-
NotificationDialog {
colorScheme: root.colorScheme
notification: root.notifications.deleteAccount
}
-
NotificationDialog {
colorScheme: root.colorScheme
notification: root.notifications.noKeychain
}
-
NotificationDialog {
colorScheme: root.colorScheme
notification: root.notifications.rebuildKeychain
}
-
NotificationDialog {
colorScheme: root.colorScheme
notification: root.notifications.apiCertIssue
}
-
NotificationDialog {
colorScheme: root.colorScheme
notification: root.notifications.noActiveKeyForRecipient
}
-
NotificationDialog {
colorScheme: root.colorScheme
notification: root.notifications.userBadEvent
}
-
NotificationDialog {
colorScheme: root.colorScheme
notification: root.notifications.genericError
}
-
NotificationDialog {
colorScheme: root.colorScheme
notification: root.notifications.genericQuestion
diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/Notifications/Notification.qml b/internal/frontend/bridge-gui/bridge-gui/qml/Notifications/Notification.qml
index d8fd41d9..202a250c 100644
--- a/internal/frontend/bridge-gui/bridge-gui/qml/Notifications/Notification.qml
+++ b/internal/frontend/bridge-gui/bridge-gui/qml/Notifications/Notification.qml
@@ -1,54 +1,45 @@
// Copyright (c) 2023 Proton AG
-//
// This file is part of Proton Mail Bridge.
-//
// Proton Mail Bridge 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 3 of the License, or
// (at your option) any later version.
-//
// Proton Mail Bridge 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.
-//
// You should have received a copy of the GNU General Public License
// along with Proton Mail Bridge. If not, see .
-
import QtQml
import QtQuick.Controls
QtObject {
id: root
-
- default property var children
-
enum NotificationType {
- Info = 0,
- Success = 1,
- Warning = 2,
- Danger = 3
+ Info,
+ Success,
+ Warning,
+ Danger
}
+ property list action
+ property bool active: false
+ // brief is used in status view only
+ property string brief
+ default property var children
+ property var data
+ // description is used in banners and in dialogs as description
+ property string description
+ property bool dismissed: false
+ property int group
+ property string icon
+ readonly property var occurred: active ? new Date() : undefined
+
// title is used in dialogs only
property string title
- // description is used in banners and in dialogs as description
- property string description
- // brief is used in status view only
- property string brief
-
- property string icon
- property list action
property int type
- property int group
-
- property bool dismissed: false
- property bool active: false
- readonly property var occurred: active ? new Date() : undefined
-
- property var data
onActiveChanged: {
- dismissed = false
+ dismissed = false;
}
}
diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/Notifications/NotificationFilter.qml b/internal/frontend/bridge-gui/bridge-gui/qml/Notifications/NotificationFilter.qml
index 91b49996..d74abc8c 100644
--- a/internal/frontend/bridge-gui/bridge-gui/qml/Notifications/NotificationFilter.qml
+++ b/internal/frontend/bridge-gui/bridge-gui/qml/Notifications/NotificationFilter.qml
@@ -1,114 +1,95 @@
// Copyright (c) 2023 Proton AG
-//
// This file is part of Proton Mail Bridge.
-//
// Proton Mail Bridge 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 3 of the License, or
// (at your option) any later version.
-//
// Proton Mail Bridge 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.
-//
// You should have received a copy of the GNU General Public License
// along with Proton Mail Bridge. If not, see .
-
import QtQml
import QtQml.Models
// contains notifications that satisfy black- and whitelist and are sorted in time-occurred order
ListModel {
id: root
-
enum FilterConsts {
- None = 0,
+ None,
All = 255
}
- property int whitelist: NotificationFilter.FilterConsts.All
property int blacklist: NotificationFilter.FilterConsts.None
-
- property Notification topmost
- property var source
-
property bool componentCompleted: false
- Component.onCompleted: {
- root.componentCompleted = true
- root.rebuildList()
- }
+ property var source
+ property Notification topmost
+ property int whitelist: NotificationFilter.FilterConsts.All
// overriding get method to ignore any role and return directly object itself
function get(row) {
if (row < 0 || row >= count) {
- return undefined
+ return undefined;
}
- return data(index(row, 0), Qt.DisplayRole)
+ return data(index(row, 0), Qt.DisplayRole);
}
-
function rebuildList() {
+ let i;
// avoid evaluation of the list before Component.onCompleted
if (!root.componentCompleted) {
- return
+ return;
}
-
- for (var i = 0; i < root.count; i++) {
- root.get(i).onActiveChanged.disconnect( root.updateList )
+ for (i = 0; i < root.count; i++) {
+ root.get(i).onActiveChanged.disconnect(root.updateList);
}
-
- root.clear()
-
+ root.clear();
if (!root.source) {
- return
+ return;
}
-
for (i = 0; i < root.source.length; i++) {
- var obj = root.source[i]
+ const obj = root.source[i];
if (obj.group & root.blacklist) {
- continue
+ continue;
}
-
if (!(obj.group & root.whitelist)) {
- continue
+ continue;
}
-
- root.append({obj})
- obj.onActiveChanged.connect( root.updateList )
+ root.append({
+ "obj": obj
+ });
+ obj.onActiveChanged.connect(root.updateList);
}
}
-
function updateList() {
- var topmost = null
-
- for (var i = 0; i < root.count; i++) {
- var obj = root.get(i)
-
+ let topmost = null;
+ for (let i = 0; i < root.count; i++) {
+ const obj = root.get(i);
if (!obj.active) {
- continue
+ continue;
}
-
if (topmost && (topmost.type > obj.type)) {
- continue
+ continue;
}
-
if (topmost && (topmost.type === obj.type) && (topmost.occurred > obj.occurred)) {
- continue
+ continue;
}
-
- topmost = obj
+ topmost = obj;
}
-
- root.topmost = topmost
+ root.topmost = topmost;
}
- onWhitelistChanged: {
- root.rebuildList()
+ Component.onCompleted: {
+ root.componentCompleted = true;
+ root.rebuildList();
}
onBlacklistChanged: {
- root.rebuildList()
+ root.rebuildList();
}
onSourceChanged: {
- root.rebuildList()
+ root.rebuildList();
+ }
+ onWhitelistChanged: {
+ root.rebuildList();
}
}
diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/Notifications/Notifications.qml b/internal/frontend/bridge-gui/bridge-gui/qml/Notifications/Notifications.qml
index ac05169d..22589019 100644
--- a/internal/frontend/bridge-gui/bridge-gui/qml/Notifications/Notifications.qml
+++ b/internal/frontend/bridge-gui/bridge-gui/qml/Notifications/Notifications.qml
@@ -1,1069 +1,114 @@
// Copyright (c) 2023 Proton AG
-//
// This file is part of Proton Mail Bridge.
-//
// Proton Mail Bridge 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 3 of the License, or
// (at your option) any later version.
-//
// Proton Mail Bridge 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.
-//
// You should have received a copy of the GNU General Public License
// along with Proton Mail Bridge. If not, see .
-
import QtQml
import Qt.labs.platform
import QtQuick.Controls
-import ".."
+import "../"
QtObject {
id: root
-
- property MainWindow frontendMain
-
- signal askEnableBeta()
- signal askEnableSplitMode(var user)
- signal askResetBridge()
- signal askChangeAllMailVisibility(var isVisibleNow)
- signal askDeleteAccount(var user)
- signal askQuestion(var title, var description, var option1, var option2, var action1, var action2)
enum Group {
- Connection = 1,
- Update = 2,
+ Connection = 1,
+ Update,
Configuration = 4,
- ForceUpdate = 8,
- API = 32,
+ ForceUpdate = 8,
+ API = 32,
// Special group for notifications that require dialog popup instead of banner
Dialogs = 64
}
- property var all: [
- root.noInternet,
- root.imapPortStartupError,
- root.smtpPortStartupError,
- root.imapPortChangeError,
- root.smtpPortChangeError,
- root.imapConnectionModeChangeError,
- root.smtpConnectionModeChangeError,
- root.updateManualReady,
- root.updateManualRestartNeeded,
- root.updateManualError,
- root.updateForce,
- root.updateForceError,
- root.updateSilentRestartNeeded,
- root.updateSilentError,
- root.updateIsLatestVersion,
- root.loginConnectionError,
- root.onlyPaidUsers,
- root.alreadyLoggedIn,
- root.enableBeta,
- root.bugReportSendSuccess,
- root.bugReportSendError,
- root.cacheUnavailable,
- root.cacheCantMove,
- root.accountChanged,
- root.diskFull,
- root.cacheLocationChangeSuccess,
- root.enableSplitMode,
- root.resetBridge,
- root.changeAllMailVisibility,
- root.deleteAccount,
- root.noKeychain,
- root.rebuildKeychain,
- root.addressChanged,
- root.apiCertIssue,
- root.noActiveKeyForRecipient,
- root.userBadEvent,
- root.imapLoginWhileSignedOut,
- root.genericError,
- root.genericQuestion,
- ]
-
- // Connection
- property Notification noInternet: Notification {
- description: qsTr("Bridge is not able to contact the server, please check your internet connection.")
- brief: qsTr("No connection")
- icon: "./icons/ic-no-connection.svg"
- type: Notification.NotificationType.Danger
- group: Notifications.Group.Connection
-
- Connections {
- target: Backend
-
- function onInternetOff() {
- root.noInternet.active = true
- }
- function onInternetOn() {
- root.noInternet.active = false
- }
- }
- }
-
- property Notification imapPortStartupError: Notification {
- description: qsTr("The IMAP server could not be started. Please check or change the IMAP port.")
- brief: qsTr("IMAP port error")
- icon: "./icons/ic-alert.svg"
- type: Notification.NotificationType.Danger
- group: Notifications.Group.Connection
-
- Connections {
- target: Backend
-
- function onImapPortStartupError() {
- root.imapPortStartupError.active = true
- }
- }
- }
-
- property Notification smtpPortStartupError: Notification {
- description: qsTr("The SMTP server could not be started. Please check or change the SMTP port.")
- brief: qsTr("SMTP port error")
- icon: "./icons/ic-alert.svg"
- type: Notification.NotificationType.Danger
- group: Notifications.Group.Connection
-
- Connections {
- target: Backend
-
- function onSmtpPortStartupError() {
- root.smtpPortStartupError.active = true
- }
- }
- }
-
- property Notification imapPortChangeError: Notification {
- description: qsTr("The IMAP port could not be changed.")
- brief: qsTr("IMAP port error")
- icon: "./icons/ic-alert.svg"
- type: Notification.NotificationType.Danger
- group: Notifications.Group.Connection
-
- Connections {
- target: Backend
-
- function onImapPortChangeError() {
- root.imapPortChangeError.active = true
- }
- }
- }
-
- property Notification smtpPortChangeError: Notification {
- description: qsTr("The SMTP port could not be changed.")
- brief: qsTr("SMTP port error")
- icon: "./icons/ic-alert.svg"
- type: Notification.NotificationType.Danger
- group: Notifications.Group.Connection
-
- Connections {
- target: Backend
-
- function onSmtpPortChangeError() {
- root.smtpPortChangeError.active = true
- }
- }
- }
-
- property Notification imapConnectionModeChangeError: Notification {
- description: qsTr("The IMAP connection mode could not be changed.")
- brief: qsTr("IMAP Connection mode error")
- icon: "./icons/ic-alert.svg"
- type: Notification.NotificationType.Danger
- group: Notifications.Group.Connection
-
- Connections {
- target: Backend
-
- function onImapConnectionModeChangeError() {
- root.imapConnectionModeChangeError.active = true
- }
- }
-
- action: Action {
- text: qsTr("OK")
-
- onTriggered: {
- root.imapConnectionModeChangeError.active= false
- }
- }
- }
-
- property Notification smtpConnectionModeChangeError: Notification {
- description: qsTr("The SMTP connection mode could not be changed.")
- brief: qsTr("SMTP Connection mode error")
- icon: "./icons/ic-alert.svg"
- type: Notification.NotificationType.Danger
- group: Notifications.Group.Connection
-
- Connections {
- target: Backend
-
- function onSmtpConnectionModeChangeError() {
- root.smtpConnectionModeChangeError.active = true
- }
- }
-
- action: Action {
- text: qsTr("OK")
-
- onTriggered: {
- root.smtpConnectionModeChangeError.active= false
- }
- }
- }
-
- // Updates
- property Notification updateManualReady: Notification {
- title: qsTr("Update to Bridge %1").arg(data ? data.version : "")
- description: {
- var descr = qsTr("A new version of Proton Mail Bridge is available.")
- var text = qsTr("See what's changed.")
- var link = Backend.releaseNotesLink
- return `${descr} ${text}`
- }
- brief: qsTr("Update available")
- icon: "./icons/ic-info-circle-filled.svg"
- type: Notification.NotificationType.Info
- group: Notifications.Group.Update | Notifications.Group.Dialogs
-
- Connections {
- target: Backend
- function onUpdateManualReady(version) {
- root.updateManualReady.data = { version: version }
- root.updateManualReady.active = true
- }
- }
-
- action: [
- Action {
- text: qsTr("Install update")
-
- onTriggered: {
- Backend.installUpdate()
- root.updateManualReady.active = false
- }
- },
- Action {
- text: qsTr("Update manually")
-
- onTriggered: {
- Qt.openUrlExternally(Backend.landingPageLink)
- root.updateManualReady.active = false
- }
- },
- Action {
- text: qsTr("Remind me later")
-
- onTriggered: {
- root.updateManualReady.active = false
- }
- }
- ]
- }
-
- property Notification updateManualRestartNeeded: Notification {
- description: qsTr("Bridge update is ready")
- brief: description
- icon: "./icons/ic-info-circle-filled.svg"
- type: Notification.NotificationType.Info
- group: Notifications.Group.Update
-
- Connections {
- target: Backend
- function onUpdateManualRestartNeeded() {
- root.updateManualRestartNeeded.active = true
- }
- }
-
- action: Action {
- text: qsTr("Restart Bridge")
-
- onTriggered: {
- Backend.restart()
- root.updateManualRestartNeeded.active = false
- }
- }
- }
-
- property Notification updateManualError: Notification {
- title: qsTr("Bridge couldn’t update")
- brief: title
- description: qsTr("Please follow manual installation in order to update Bridge.")
- icon: "./icons/ic-exclamation-circle-filled.svg"
- type: Notification.NotificationType.Warning
- group: Notifications.Group.Update
-
- Connections {
- target: Backend
- function onUpdateManualError() {
- root.updateManualError.active = true
- }
- }
-
- action: [
- Action {
- text: qsTr("Update manually")
-
- onTriggered: {
- Qt.openUrlExternally(Backend.landingPageLink)
- root.updateManualError.active = false
- Backend.quit()
- }
- },
- Action {
- text: qsTr("Remind me later")
-
- onTriggered: {
- root.updateManualError.active = false
- }
- }
- ]
- }
-
- property Notification updateForce: Notification {
- title: qsTr("Update to Bridge %1").arg(data ? data.version : "")
- description: qsTr("This version of Bridge is no longer supported, please update.")
- brief: qsTr("Bridge is outdated")
- icon: "./icons/ic-exclamation-circle-filled.svg"
- type: Notification.NotificationType.Danger
- group: Notifications.Group.Update | Notifications.Group.ForceUpdate | Notifications.Group.Dialogs
-
- Connections {
- target: Backend
-
- function onUpdateForce(version) {
- root.updateForce.data = { version: version }
- root.updateForce.active = true
- }
- }
-
- action: [
- Action {
- text: qsTr("Install update")
-
- onTriggered: {
- Backend.installUpdate()
- root.updateForce.active = false
- }
- },
- Action {
- text: qsTr("Update manually")
-
- onTriggered: {
- Qt.openUrlExternally(Backend.landingPageLink)
- root.updateForce.active = false
- }
- },
- Action {
- text: qsTr("Quit Bridge")
-
- onTriggered: {
- Backend.quit()
- root.updateForce.active = false
- }
- }
- ]
- }
-
- property Notification updateForceError: Notification {
- title: qsTr("Bridge couldn't update")
- description: qsTr("You must update manually. Go to: https://proton.me/mail/bridge#download")
- brief: title
- icon: "./icons/ic-exclamation-circle-filled.svg"
- type: Notification.NotificationType.Danger
- group: Notifications.Group.Update | Notifications.Group.Dialogs
-
- Connections {
- target: Backend
-
- function onUpdateForceError() {
- root.updateForceError.active = true
- }
- }
-
- action: [
- Action {
- text: qsTr("Update manually")
-
- onTriggered: {
- Qt.openUrlExternally(Backend.landingPageLink)
- root.updateForceError.active = false
- }
- },
- Action {
- text: qsTr("Quit Bridge")
-
- onTriggered: {
- Backend.quit()
- root.updateForceError.active = false
- }
- }
- ]
- }
-
- property Notification updateSilentRestartNeeded: Notification {
- description: qsTr("Bridge update is ready")
- brief: description
- icon: "./icons/ic-info-circle-filled.svg"
- type: Notification.NotificationType.Info
- group: Notifications.Group.Update
-
- Connections {
- target: Backend
- function onUpdateSilentRestartNeeded() {
- root.updateSilentRestartNeeded.active = true
- }
- }
-
- action: Action {
- text: qsTr("Restart Bridge")
-
- onTriggered: {
- Backend.restart()
- root.updateSilentRestartNeeded.active = false
- }
- }
- }
-
- property Notification updateSilentError: Notification {
- description: qsTr("Bridge couldn't update")
- brief: description
- icon: "./icons/ic-exclamation-circle-filled.svg"
- type: Notification.NotificationType.Warning
- group: Notifications.Group.Update
-
- Connections {
- target: Backend
- function onUpdateSilentError() {
- root.updateSilentError.active = true
- }
- }
-
- action: Action {
- text: qsTr("Update manually")
-
- onTriggered: {
- Qt.openUrlExternally(Backend.landingPageLink)
- root.updateSilentError.active = false
- }
- }
- }
-
- property Notification updateIsLatestVersion: Notification {
- description: qsTr("Bridge is up to date")
- brief: description
- icon: "./icons/ic-info-circle-filled.svg"
- type: Notification.NotificationType.Info
- group: Notifications.Group.Update
-
- Connections {
- target: Backend
- function onUpdateIsLatestVersion() {
- root.updateIsLatestVersion.active = true
- }
- }
-
- action: Action {
- text: qsTr("OK")
-
- onTriggered: {
- root.updateIsLatestVersion.active = false
- }
- }
- }
-
- property Notification enableBeta: Notification {
- title: qsTr("Enable Beta access")
- brief: title
- description: qsTr("Be the first to get new updates and use new features. Bridge will update to the latest beta version.")
- icon: "./icons/ic-info-circle-filled.svg"
- type: Notification.NotificationType.Info
- group: Notifications.Group.Update | Notifications.Group.Dialogs
-
- Connections {
- target: root
- function onAskEnableBeta() {
- root.enableBeta.active = true
- }
- }
-
- action: [
- Action {
- text: qsTr("Enable")
- onTriggered: {
- Backend.toggleBeta(true)
- root.enableBeta.active = false
- }
- },
- Action {
- text: qsTr("Cancel")
-
- onTriggered: {
- root.enableBeta.active = false
- }
- }
- ]
- }
-
- // login
- property Notification loginConnectionError: Notification {
- description: qsTr("Bridge is not able to contact the server, please check your internet connection.")
- brief: qsTr("Connection error")
- icon: "./icons/ic-exclamation-circle-filled.svg"
- type: Notification.NotificationType.Danger
- group: Notifications.Group.Configuration
-
- Connections {
- target: Backend
- function onLoginConnectionError(errorMsg) {
- root.loginConnectionError.active = true
- }
- }
-
- action: [
- Action {
- text: qsTr("OK")
- onTriggered: {
- root.loginConnectionError.active = false
- }
- }
- ]
- }
-
- property Notification onlyPaidUsers: Notification {
- description: qsTr("Bridge is exclusive to our mail paid plans. Upgrade your account to use Bridge.")
- brief: qsTr("Upgrade your account")
- icon: "./icons/ic-exclamation-circle-filled.svg"
- type: Notification.NotificationType.Danger
- group: Notifications.Group.Configuration
- property var pricingLink: "https://proton.me/mail/pricing"
-
- Connections {
- target: Backend
- function onLoginFreeUserError() {
- root.onlyPaidUsers.active = true
- }
- }
-
- action: [
- Action {
- text: qsTr("Upgrade")
- onTriggered: {
- Qt.openUrlExternally(root.onlyPaidUsers.pricingLink)
- root.onlyPaidUsers.active = false
- }
- }
- ]
- }
-
- property Notification alreadyLoggedIn: Notification {
- description: qsTr("This account is already signed in.")
- brief: qsTr("Already signed in")
- icon: "./icons/ic-exclamation-circle-filled.svg"
- type: Notification.NotificationType.Info
- group: Notifications.Group.Configuration
-
- Connections {
- target: Backend
- function onLoginAlreadyLoggedIn(index) {
- root.alreadyLoggedIn.active = true
- }
- }
-
- action: [
- Action {
- text: qsTr("OK")
- onTriggered: {
- root.alreadyLoggedIn.active = false
- }
- }
- ]
- }
-
- // Bug reports
- property Notification bugReportSendSuccess: Notification {
- description: qsTr("Thank you for the report. We'll get back to you as soon as we can.")
- brief: qsTr("Report sent")
- icon: "./icons/ic-info-circle-filled.svg"
- type: Notification.NotificationType.Success
- group: Notifications.Group.Configuration
-
- Connections {
- target: Backend
- function onBugReportSendSuccess() {
- root.bugReportSendSuccess.active = true
- }
- }
-
- action: [
- Action {
- text: qsTr("OK")
- onTriggered: {
- root.bugReportSendSuccess.active = false
- }
- }
- ]
- }
-
- property Notification bugReportSendError: Notification {
- description: qsTr("Report could not be sent. Try again or email us directly.")
- brief: qsTr("Error sending report")
- icon: "./icons/ic-exclamation-circle-filled.svg"
- type: Notification.NotificationType.Danger
- group: Notifications.Group.Configuration
-
- Connections {
- target: Backend
- function onBugReportSendError() {
- root.bugReportSendError.active = true
- }
- }
-
- action: Action {
- text: qsTr("OK")
- onTriggered: {
- root.bugReportSendError.active = false
- }
- }
- }
-
- // Cache
- property Notification cacheUnavailable: Notification {
- title: qsTr("Cache location is unavailable")
- description: qsTr("The current cache location is unavailable. Check the directory or change it in your settings.")
- brief: title
- icon: "./icons/ic-exclamation-circle-filled.svg"
- type: Notification.NotificationType.Warning
- group: Notifications.Group.Configuration | Notifications.Group.Dialogs
-
- Connections {
- target: Backend
- function onDiskCacheUnavailable() {
- root.cacheUnavailable.active = true
- }
- }
-
- action: [
- Action {
- text: qsTr("Quit Bridge")
- onTriggered: {
- Backend.quit()
- root.cacheUnavailable.active = false
- }
- },
- Action {
- text: qsTr("Change location")
- onTriggered: {
- root.cacheUnavailable.active = false
- root.frontendMain.showLocalCacheSettings()
- }
- }
- ]
- }
-
- property Notification cacheCantMove: Notification {
- title: qsTr("Can’t move cache")
- brief: title
- description: qsTr("The location you have selected is not available. Make sure you have enough free space or choose another location.")
- icon: "./icons/ic-exclamation-circle-filled.svg"
- type: Notification.NotificationType.Warning
- group: Notifications.Group.Configuration | Notifications.Group.Dialogs
-
- Connections {
- target: Backend
- function onCantMoveDiskCache() {
- root.cacheCantMove.active = true
- }
- }
-
- action: [
- Action {
- text: qsTr("Cancel")
- onTriggered: {
- root.cacheCantMove.active = false
- }
- },
- Action {
- text: qsTr("Change location")
- onTriggered: {
- root.cacheCantMove.active = false
- root.frontendMain.showLocalCacheSettings()
- }
- }
- ]
- }
-
- property Notification cacheLocationChangeSuccess: Notification {
- description: qsTr("Cache location successfully changed")
- brief: description
- icon: "./icons/ic-info-circle-filled.svg"
- type: Notification.NotificationType.Success
- group: Notifications.Group.Configuration
-
- Connections {
- target: Backend
- function onDiskCachePathChanged() {
- console.log("notify location changed successfully")
- root.cacheLocationChangeSuccess.active = true
- }
- }
-
- action: [
- Action {
- text: qsTr("OK")
- onTriggered: {
- root.cacheLocationChangeSuccess.active = false
- }
- }
- ]
- }
-
// Other
property Notification accountChanged: Notification {
- description: qsTr("The address list for .... account has changed. You need to reconfigure your email client.")
brief: qsTr("Address list changed")
+ description: qsTr("The address list for .... account has changed. You need to reconfigure your email client.")
+ group: Notifications.Group.Configuration
icon: "./icons/ic-exclamation-circle-filled.svg"
type: Notification.NotificationType.Danger
- group: Notifications.Group.Configuration
action: Action {
text: qsTr("Reconfigure")
- onTriggered: {
- // TODO: open configuration window here
+ onTriggered:
+ // TODO: open configuration window here
+ {
}
}
}
-
- property Notification diskFull: Notification {
- title: qsTr("Your disk is almost full")
- description: qsTr("Quit Bridge and free disk space or disable the local cache (not recommended).")
- brief: title
- icon: "./icons/ic-exclamation-circle-filled.svg"
- type: Notification.NotificationType.Warning
- group: Notifications.Group.Configuration | Notifications.Group.Dialogs
-
- Connections {
- target: Backend
- function onDiskFull() {
- root.diskFull.active = true
- }
- }
-
- action: [
- Action {
- text: qsTr("Quit Bridge")
- onTriggered: {
- Backend.quit()
- root.diskFull.active = false
- }
- },
- Action {
- text: qsTr("Settings")
- onTriggered: {
- root.diskFull.active = false
- root.frontendMain.showLocalCacheSettings()
- }
- }
- ]
- }
-
- property Notification enableSplitMode: Notification {
- title: qsTr("Enable split mode?")
- brief: title
- description: qsTr("Changing between split and combined address mode will require you to delete your account(s) from your email client and begin the setup process from scratch.")
- icon: "/qml/icons/ic-question-circle.svg"
- type: Notification.NotificationType.Warning
- group: Notifications.Group.Configuration | Notifications.Group.Dialogs
-
- property var user
-
- Connections {
- target: root
- function onAskEnableSplitMode(user) {
- root.enableSplitMode.user = user
- root.enableSplitMode.active = true
- }
- }
-
- Connections {
- target: (root && root.enableSplitMode && root.enableSplitMode.user ) ? root.enableSplitMode.user : null
- function onToggleSplitModeFinished() {
- root.enableSplitMode.active = false
-
- enableSplitMode_enable.loading = false
- enableSplitMode_cancel.enabled = true
- }
- }
-
- action: [
- Action {
- id: enableSplitMode_cancel
- text: qsTr("Cancel")
- onTriggered: {
- root.enableSplitMode.active = false
- }
- },
- Action {
- id: enableSplitMode_enable
- text: qsTr("Enable split mode")
- onTriggered: {
- enableSplitMode_enable.loading = true
- enableSplitMode_cancel.enabled = false
- root.enableSplitMode.user.toggleSplitMode(true)
- }
- }
- ]
- }
-
- property Notification resetBridge: Notification {
- title: qsTr("Reset Bridge?")
- brief: title
- icon: "./icons/ic-exclamation-circle-filled.svg"
- description: qsTr("This will clear your accounts, preferences, and cached data. You will need to reconfigure your email client. Bridge will automatically restart.")
- type: Notification.NotificationType.Danger
- group: Notifications.Group.Configuration | Notifications.Group.Dialogs
-
- property var user
-
- Connections {
- target: root
- function onAskResetBridge() {
- root.resetBridge.active = true
- }
- }
-
- Connections {
- target: Backend
- function onResetFinished() {
- root.resetBridge.active = false
-
- resetBridge_reset.loading = false
- resetBridge_cancel.enabled = true
- }
- }
-
- action: [
- Action {
- id: resetBridge_cancel
- text: qsTr("Cancel")
- onTriggered: {
- root.resetBridge.active = false
- }
- },
- Action {
- id: resetBridge_reset
- text: qsTr("Reset and restart")
- onTriggered: {
- resetBridge_reset.loading = true
- resetBridge_cancel.enabled = false
- Backend.triggerReset()
- }
- }
- ]
- }
-
- property Notification changeAllMailVisibility: Notification {
- title: root.changeAllMailVisibility.isVisibleNow ?
- qsTr("Hide All Mail folder?") :
- qsTr("Show All Mail folder?")
- brief: title
- icon: "./icons/ic-info-circle-filled.svg"
- description: qsTr("Switching between showing and hiding the All Mail folder will require you to restart your client.")
- type: Notification.NotificationType.Info
- group: Notifications.Group.Configuration | Notifications.Group.Dialogs
-
- property var isVisibleNow
-
- Connections {
- target: root
-
- function onAskChangeAllMailVisibility(isVisibleNow) {
- root.changeAllMailVisibility.isVisibleNow = isVisibleNow
- root.changeAllMailVisibility.active = true
- }
- }
-
- action: [
- Action {
- id: allMail_change
- text: root.changeAllMailVisibility.isVisibleNow ?
- qsTr("Hide All Mail folder") :
- qsTr("Show All Mail folder")
- onTriggered: {
- Backend.changeIsAllMailVisible(!root.changeAllMailVisibility.isVisibleNow)
- root.changeAllMailVisibility.active = false
- }
- },
- Action {
- id: allMail_cancel
- text: qsTr("Cancel")
- onTriggered: {
- root.changeAllMailVisibility.active = false
- }
- }
- ]
- }
-
- property Notification deleteAccount: Notification {
- title: qsTr("Remove this account?")
- brief: title
- icon: "./icons/ic-exclamation-circle-filled.svg"
- description: qsTr("Are you sure you want to remove this account from Bridge and delete locally stored preferences and data?")
- type: Notification.NotificationType.Danger
- group: Notifications.Group.Configuration | Notifications.Group.Dialogs
-
- property var user
-
- Connections {
- target: root
- function onAskDeleteAccount(user) {
- root.deleteAccount.user = user
- root.deleteAccount.active = true
- }
- }
-
- action: [
- Action {
- id: deleteAccount_cancel
- text: qsTr("Cancel")
- onTriggered: {
- root.deleteAccount.active = false
- }
- },
- Action {
- id: deleteAccount_delete
- text: qsTr("Remove this account")
- onTriggered: {
- root.deleteAccount.user.remove()
- root.deleteAccount.active = false
- }
- }
- ]
- }
-
- property Notification noKeychain: Notification {
- title: qsTr("No keychain available")
- brief: title
- description: qsTr("Bridge is not able to detect a supported password manager (pass or secret-service). Please install and setup supported password manager and restart the application.")
- icon: "./icons/ic-exclamation-circle-filled.svg"
- type: Notification.NotificationType.Danger
- group: Notifications.Group.Dialogs | Notifications.Group.Configuration
-
- Connections {
- target: Backend
-
- function onNotifyHasNoKeychain() {
- root.noKeychain.active = true
- }
- }
-
- action: [
- Action {
- text: qsTr("Quit Bridge")
-
- onTriggered: {
- Backend.quit()
- }
- },
- Action {
- text: qsTr("Restart Bridge")
-
- onTriggered: {
- Backend.restart()
- }
- }
- ]
- }
-
- property Notification rebuildKeychain: Notification {
- title: qsTr("Your macOS keychain might be corrupted")
- brief: title
- description: qsTr("Bridge is not able to access your macOS keychain. Please consult the instructions on our support page.")
- icon: "./icons/ic-exclamation-circle-filled.svg"
- type: Notification.NotificationType.Danger
- group: Notifications.Group.Dialogs | Notifications.Group.Configuration
-
- property var supportLink: "https://proton.me/support/bridge"
-
-
- Connections {
- target: Backend
-
- function onNotifyRebuildKeychain() {
- console.log("notifications")
- root.rebuildKeychain.active = true
- }
- }
-
- action: [
- Action {
- text: qsTr("Open the support page")
-
- onTriggered: {
- Backend.notifyKBArticleClicked(root.rebuildKeychain.supportLink);
- Qt.openUrlExternally(root.rebuildKeychain.supportLink)
- Backend.quit()
- }
- }
- ]
- }
-
property Notification addressChanged: Notification {
- title: qsTr("Address list changes")
brief: title
description: qsTr("The address list for your account has changed. You might need to reconfigure your email client.")
- icon: "./icons/ic-exclamation-circle-filled.svg"
- type: Notification.NotificationType.Warning
group: Notifications.Group.Configuration
-
- Connections {
- target: Backend
-
- function onAddressChanged(address) {
- root.addressChanged.description = qsTr("The address list for your account %1 has changed. You might need to reconfigure your email client.").arg(address)
- root.addressChanged.active = true
- }
-
- function onAddressChangedLogout(address) {
- root.addressChanged.description = qsTr("The address list for your account %1 has changed. You have to reconfigure your email client.").arg(address)
- root.addressChanged.active = true
- }
- }
+ icon: "./icons/ic-exclamation-circle-filled.svg"
+ title: qsTr("Address list changes")
+ type: Notification.NotificationType.Warning
action: [
Action {
text: qsTr("OK")
onTriggered: {
- root.addressChanged.active = false
+ root.addressChanged.active = false;
}
}
]
- }
-
- property Notification apiCertIssue: Notification {
- title: qsTr("Unable to establish a \nsecure connection to \nProton servers")
- brief: qsTr("Cannot establish secure connection")
- description: qsTr("Bridge cannot verify the authenticity of Proton servers on your current network due to a TLS certificate error. " +
- "Start Bridge again after ensuring your connection is secure and/or connecting to a VPN. Learn more about TLS pinning " +
- "here.")
-
- icon: "./icons/ic-exclamation-circle-filled.svg"
- type: Notification.NotificationType.Danger
- group: Notifications.Group.Dialogs | Notifications.Group.Connection
Connections {
- target: Backend
-
- function onApiCertIssue() {
- root.apiCertIssue.active = true
+ function onAddressChanged(address) {
+ root.addressChanged.description = qsTr("The address list for your account %1 has changed. You might need to reconfigure your email client.").arg(address);
+ root.addressChanged.active = true;
}
+ function onAddressChangedLogout(address) {
+ root.addressChanged.description = qsTr("The address list for your account %1 has changed. You have to reconfigure your email client.").arg(address);
+ root.addressChanged.active = true;
+ }
+
+ target: Backend
}
+ }
+ property var all: [root.noInternet, root.imapPortStartupError, root.smtpPortStartupError, root.imapPortChangeError, root.smtpPortChangeError, root.imapConnectionModeChangeError, root.smtpConnectionModeChangeError, root.updateManualReady, root.updateManualRestartNeeded, root.updateManualError, root.updateForce, root.updateForceError, root.updateSilentRestartNeeded, root.updateSilentError, root.updateIsLatestVersion, root.loginConnectionError, root.onlyPaidUsers, root.alreadyLoggedIn, root.enableBeta, root.bugReportSendSuccess, root.bugReportSendError, root.cacheUnavailable, root.cacheCantMove, root.accountChanged, root.diskFull, root.cacheLocationChangeSuccess, root.enableSplitMode, root.resetBridge, root.changeAllMailVisibility, root.deleteAccount, root.noKeychain, root.rebuildKeychain, root.addressChanged, root.apiCertIssue, root.noActiveKeyForRecipient, root.userBadEvent, root.imapLoginWhileSignedOut, root.genericError, root.genericQuestion]
+ property Notification alreadyLoggedIn: Notification {
+ brief: qsTr("Already signed in")
+ description: qsTr("This account is already signed in.")
+ group: Notifications.Group.Configuration
+ icon: "./icons/ic-exclamation-circle-filled.svg"
+ type: Notification.NotificationType.Info
+
+ action: [
+ Action {
+ text: qsTr("OK")
+
+ onTriggered: {
+ root.alreadyLoggedIn.active = false;
+ }
+ }
+ ]
+
+ Connections {
+ function onLoginAlreadyLoggedIn(_) {
+ root.alreadyLoggedIn.active = true;
+ }
+
+ target: Backend
+ }
+ }
+ property Notification apiCertIssue: Notification {
+ brief: qsTr("Cannot establish secure connection")
+ description: qsTr("Bridge cannot verify the authenticity of Proton servers on your current network due to a TLS certificate error. " + "Start Bridge again after ensuring your connection is secure and/or connecting to a VPN. Learn more about TLS pinning " + "here.")
+ group: Notifications.Group.Dialogs | Notifications.Group.Connection
+ icon: "./icons/ic-exclamation-circle-filled.svg"
+ title: qsTr("Unable to establish a \nsecure connection to \nProton servers")
+ type: Notification.NotificationType.Danger
action: [
Action {
@@ -1071,180 +116,1092 @@ QtObject {
onTriggered: {
root.apiCertIssue.active = false;
- Backend.quit()
+ Backend.quit();
}
}
]
- }
-
- property Notification noActiveKeyForRecipient: Notification {
- title: qsTr("Unable to send \nencrypted message")
- brief: title
- description: "#PlaceholderText#"
- icon: "./icons/ic-exclamation-circle-filled.svg"
- type: Notification.NotificationType.Danger
- group: Notifications.Group.Dialogs | Notifications.Group.Connection
Connections {
- target: Backend
+ function onApiCertIssue() {
+ root.apiCertIssue.active = true;
+ }
- function onNoActiveKeyForRecipient(email) {
- root.noActiveKeyForRecipient.description = qsTr("There are no active keys to encrypt your message to %1. "+
- "Please update the setting for this contact.").arg(email)
- root.noActiveKeyForRecipient.active = true
+ target: Backend
+ }
+ }
+ property Notification bugReportSendError: Notification {
+ brief: qsTr("Error sending report")
+ description: qsTr("Report could not be sent. Try again or email us directly.")
+ group: Notifications.Group.Configuration
+ icon: "./icons/ic-exclamation-circle-filled.svg"
+ type: Notification.NotificationType.Danger
+
+ action: Action {
+ text: qsTr("OK")
+
+ onTriggered: {
+ root.bugReportSendError.active = false;
}
}
+ Connections {
+ function onBugReportSendError() {
+ root.bugReportSendError.active = true;
+ }
+
+ target: Backend
+ }
+ }
+
+ // Bug reports
+ property Notification bugReportSendSuccess: Notification {
+ brief: qsTr("Report sent")
+ description: qsTr("Thank you for the report. We'll get back to you as soon as we can.")
+ group: Notifications.Group.Configuration
+ icon: "./icons/ic-info-circle-filled.svg"
+ type: Notification.NotificationType.Success
+
action: [
Action {
text: qsTr("OK")
onTriggered: {
- root.noActiveKeyForRecipient.active = false
+ root.bugReportSendSuccess.active = false;
}
}
]
- }
-
- property Notification userBadEvent: Notification {
- title: qsTr("Internal error")
- brief: title
- description: "#PlaceHolderText"
- icon: "./icons/ic-exclamation-circle-filled.svg"
- type: Notification.NotificationType.Danger
- group: Notifications.Group.Connection | Notifications.Group.Dialogs
-
- property var userID: ""
Connections {
- target: Backend
- function onUserBadEvent(userID, errorMessage) {
- root.userBadEvent.userID = userID
- root.userBadEvent.description = errorMessage
- root.userBadEvent.active = true
+ function onBugReportSendSuccess() {
+ root.bugReportSendSuccess.active = true;
}
+
+ target: Backend
}
+ }
+ property Notification cacheCantMove: Notification {
+ brief: title
+ description: qsTr("The location you have selected is not available. Make sure you have enough free space or choose another location.")
+ group: Notifications.Group.Configuration | Notifications.Group.Dialogs
+ icon: "./icons/ic-exclamation-circle-filled.svg"
+ title: qsTr("Can’t move cache")
+ type: Notification.NotificationType.Warning
action: [
Action {
- text: qsTr("Synchronize")
+ text: qsTr("Cancel")
onTriggered: {
- root.userBadEvent.active = false
- Backend.sendBadEventUserFeedback(root.userBadEvent.userID, true)
+ root.cacheCantMove.active = false;
}
},
-
Action {
- text: qsTr("Logout")
+ text: qsTr("Change location")
onTriggered: {
- root.userBadEvent.active = false
- Backend.sendBadEventUserFeedback(root.userBadEvent.userID, false)
+ root.cacheCantMove.active = false;
+ root.frontendMain.showLocalCacheSettings();
}
}
]
- }
-
- property Notification imapLoginWhileSignedOut: Notification {
- title: qsTr("IMAP Login failed")
- brief: title
- description: "#PlaceHolderText"
- icon: "./icons/ic-exclamation-circle-filled.svg"
- type: Notification.NotificationType.Danger
- group: Notifications.Group.Connection
-
Connections {
- target: Backend
- function onImapLoginWhileSignedOut(username) {
- root.imapLoginWhileSignedOut.description = qsTr("An email client tried to connect to the account %1, but this account is signed " +
- "out. Please sign-in to continue.").arg(username)
- root.imapLoginWhileSignedOut.active = true
+ function onCantMoveDiskCache() {
+ root.cacheCantMove.active = true;
}
+
+ target: Backend
}
+ }
+ property Notification cacheLocationChangeSuccess: Notification {
+ brief: description
+ description: qsTr("Cache location successfully changed")
+ group: Notifications.Group.Configuration
+ icon: "./icons/ic-info-circle-filled.svg"
+ type: Notification.NotificationType.Success
action: [
Action {
text: qsTr("OK")
onTriggered: {
- root.imapLoginWhileSignedOut.active = false
+ root.cacheLocationChangeSuccess.active = false;
}
}
]
+
+ Connections {
+ function onDiskCachePathChanged() {
+ console.log("notify location changed successfully");
+ root.cacheLocationChangeSuccess.active = true;
+ }
+
+ target: Backend
+ }
}
+ // Cache
+ property Notification cacheUnavailable: Notification {
+ brief: title
+ description: qsTr("The current cache location is unavailable. Check the directory or change it in your settings.")
+ group: Notifications.Group.Configuration | Notifications.Group.Dialogs
+ icon: "./icons/ic-exclamation-circle-filled.svg"
+ title: qsTr("Cache location is unavailable")
+ type: Notification.NotificationType.Warning
+
+ action: [
+ Action {
+ text: qsTr("Quit Bridge")
+
+ onTriggered: {
+ Backend.quit();
+ root.cacheUnavailable.active = false;
+ }
+ },
+ Action {
+ text: qsTr("Change location")
+
+ onTriggered: {
+ root.cacheUnavailable.active = false;
+ root.frontendMain.showLocalCacheSettings();
+ }
+ }
+ ]
+
+ Connections {
+ function onDiskCacheUnavailable() {
+ root.cacheUnavailable.active = true;
+ }
+
+ target: Backend
+ }
+ }
+ property Notification changeAllMailVisibility: Notification {
+ property var isVisibleNow
+
+ brief: title
+ description: qsTr("Switching between showing and hiding the All Mail folder will require you to restart your client.")
+ group: Notifications.Group.Configuration | Notifications.Group.Dialogs
+ icon: "./icons/ic-info-circle-filled.svg"
+ title: root.changeAllMailVisibility.isVisibleNow ? qsTr("Hide All Mail folder?") : qsTr("Show All Mail folder?")
+ type: Notification.NotificationType.Info
+
+ action: [
+ Action {
+ id: allMail_change
+ text: root.changeAllMailVisibility.isVisibleNow ? qsTr("Hide All Mail folder") : qsTr("Show All Mail folder")
+
+ onTriggered: {
+ Backend.changeIsAllMailVisible(!root.changeAllMailVisibility.isVisibleNow);
+ root.changeAllMailVisibility.active = false;
+ }
+ },
+ Action {
+ id: allMail_cancel
+ text: qsTr("Cancel")
+
+ onTriggered: {
+ root.changeAllMailVisibility.active = false;
+ }
+ }
+ ]
+
+ Connections {
+ function onAskChangeAllMailVisibility(isVisibleNow) {
+ root.changeAllMailVisibility.isVisibleNow = isVisibleNow;
+ root.changeAllMailVisibility.active = true;
+ }
+
+ target: root
+ }
+ }
+ property Notification deleteAccount: Notification {
+ property var user
+
+ brief: title
+ description: qsTr("Are you sure you want to remove this account from Bridge and delete locally stored preferences and data?")
+ group: Notifications.Group.Configuration | Notifications.Group.Dialogs
+ icon: "./icons/ic-exclamation-circle-filled.svg"
+ title: qsTr("Remove this account?")
+ type: Notification.NotificationType.Danger
+
+ action: [
+ Action {
+ id: deleteAccount_cancel
+ text: qsTr("Cancel")
+
+ onTriggered: {
+ root.deleteAccount.active = false;
+ }
+ },
+ Action {
+ id: deleteAccount_delete
+ text: qsTr("Remove this account")
+
+ onTriggered: {
+ root.deleteAccount.user.remove();
+ root.deleteAccount.active = false;
+ }
+ }
+ ]
+
+ Connections {
+ function onAskDeleteAccount(user) {
+ root.deleteAccount.user = user;
+ root.deleteAccount.active = true;
+ }
+
+ target: root
+ }
+ }
+ property Notification diskFull: Notification {
+ brief: title
+ description: qsTr("Quit Bridge and free disk space or disable the local cache (not recommended).")
+ group: Notifications.Group.Configuration | Notifications.Group.Dialogs
+ icon: "./icons/ic-exclamation-circle-filled.svg"
+ title: qsTr("Your disk is almost full")
+ type: Notification.NotificationType.Warning
+
+ action: [
+ Action {
+ text: qsTr("Quit Bridge")
+
+ onTriggered: {
+ Backend.quit();
+ root.diskFull.active = false;
+ }
+ },
+ Action {
+ text: qsTr("Settings")
+
+ onTriggered: {
+ root.diskFull.active = false;
+ root.frontendMain.showLocalCacheSettings();
+ }
+ }
+ ]
+
+ Connections {
+ function onDiskFull() {
+ root.diskFull.active = true;
+ }
+
+ target: Backend
+ }
+ }
+ property Notification enableBeta: Notification {
+ brief: title
+ description: qsTr("Be the first to get new updates and use new features. Bridge will update to the latest beta version.")
+ group: Notifications.Group.Update | Notifications.Group.Dialogs
+ icon: "./icons/ic-info-circle-filled.svg"
+ title: qsTr("Enable Beta access")
+ type: Notification.NotificationType.Info
+
+ action: [
+ Action {
+ text: qsTr("Enable")
+
+ onTriggered: {
+ Backend.toggleBeta(true);
+ root.enableBeta.active = false;
+ }
+ },
+ Action {
+ text: qsTr("Cancel")
+
+ onTriggered: {
+ root.enableBeta.active = false;
+ }
+ }
+ ]
+
+ Connections {
+ function onAskEnableBeta() {
+ root.enableBeta.active = true;
+ }
+
+ target: root
+ }
+ }
+ property Notification enableSplitMode: Notification {
+ property var user
+
+ brief: title
+ description: qsTr("Changing between split and combined address mode will require you to delete your account(s) from your email client and begin the setup process from scratch.")
+ group: Notifications.Group.Configuration | Notifications.Group.Dialogs
+ icon: "/qml/icons/ic-question-circle.svg"
+ title: qsTr("Enable split mode?")
+ type: Notification.NotificationType.Warning
+
+ action: [
+ Action {
+ id: enableSplitMode_cancel
+ text: qsTr("Cancel")
+
+ onTriggered: {
+ root.enableSplitMode.active = false;
+ }
+ },
+ Action {
+ id: enableSplitMode_enable
+ text: qsTr("Enable split mode")
+
+ onTriggered: {
+ enableSplitMode_enable.loading = true;
+ enableSplitMode_cancel.enabled = false;
+ root.enableSplitMode.user.toggleSplitMode(true);
+ }
+ }
+ ]
+
+ Connections {
+ function onAskEnableSplitMode(user) {
+ root.enableSplitMode.user = user;
+ root.enableSplitMode.active = true;
+ }
+
+ target: root
+ }
+ Connections {
+ function onToggleSplitModeFinished() {
+ root.enableSplitMode.active = false;
+ enableSplitMode_enable.loading = false;
+ enableSplitMode_cancel.enabled = true;
+ }
+
+ target: (root && root.enableSplitMode && root.enableSplitMode.user) ? root.enableSplitMode.user : null
+ }
+ }
+ property MainWindow frontendMain
property Notification genericError: Notification {
- title: ""
brief: title
description: ""
- icon: "./icons/ic-exclamation-circle-filled.svg"
- type: Notification.NotificationType.Danger
group: Notifications.Group.Dialogs
+ icon: "./icons/ic-exclamation-circle-filled.svg"
+ title: ""
+ type: Notification.NotificationType.Danger
+
+ action: [
+ Action {
+ text: qsTr("OK")
+
+ onTriggered: {
+ root.genericError.active = false;
+ }
+ }
+ ]
+
Connections {
- target: Backend
function onGenericError(title, description) {
- root.genericError.title = title
- root.genericError.description = description
+ root.genericError.title = title;
+ root.genericError.description = description;
root.genericError.active = true;
}
+
+ target: Backend
}
-
- action: [
- Action {
- text: qsTr("OK")
-
- onTriggered: {
- root.genericError.active = false
- }
- }
- ]
}
-
property Notification genericQuestion: Notification {
- title: ""
- brief: title
- description: ""
- type: Notification.NotificationType.Warning
- group: Notifications.Group.Dialogs
- property var option1: ""
- property var option2: ""
property variant action1: null
property variant action2: null
+ property var option1: ""
+ property var option2: ""
- Connections {
- target: root
- function onAskQuestion(title, description, option1, option2, action1, action2) {
- root.genericQuestion.title = title
- root.genericQuestion.description = description
- root.genericQuestion.option1 = option1
- root.genericQuestion.option2 = option2
- root.genericQuestion.action1 = action1
- root.genericQuestion.action2 = action2
- root.genericQuestion.active = true
- }
- }
+ brief: title
+ description: ""
+ group: Notifications.Group.Dialogs
+ title: ""
+ type: Notification.NotificationType.Warning
action: [
Action {
text: root.genericQuestion.option1
onTriggered: {
- root.genericQuestion.active = false
+ root.genericQuestion.active = false;
if (root.genericQuestion.action1)
- root.genericQuestion.action1()
+ root.genericQuestion.action1();
}
},
Action {
text: root.genericQuestion.option2
onTriggered: {
- root.genericQuestion.active = false
+ root.genericQuestion.active = false;
if (root.genericQuestion.action2)
- root.genericQuestion.action2()
+ root.genericQuestion.action2();
}
}
]
+
+ Connections {
+ function onAskQuestion(title, description, option1, option2, action1, action2) {
+ root.genericQuestion.title = title;
+ root.genericQuestion.description = description;
+ root.genericQuestion.option1 = option1;
+ root.genericQuestion.option2 = option2;
+ root.genericQuestion.action1 = action1;
+ root.genericQuestion.action2 = action2;
+ root.genericQuestion.active = true;
+ }
+
+ target: root
+ }
}
+ property Notification imapConnectionModeChangeError: Notification {
+ brief: qsTr("IMAP Connection mode error")
+ description: qsTr("The IMAP connection mode could not be changed.")
+ group: Notifications.Group.Connection
+ icon: "./icons/ic-alert.svg"
+ type: Notification.NotificationType.Danger
+
+ action: Action {
+ text: qsTr("OK")
+
+ onTriggered: {
+ root.imapConnectionModeChangeError.active = false;
+ }
+ }
+
+ Connections {
+ function onImapConnectionModeChangeError() {
+ root.imapConnectionModeChangeError.active = true;
+ }
+
+ target: Backend
+ }
+ }
+ property Notification imapLoginWhileSignedOut: Notification {
+ brief: title
+ description: "#PlaceHolderText"
+ group: Notifications.Group.Connection
+ icon: "./icons/ic-exclamation-circle-filled.svg"
+ title: qsTr("IMAP Login failed")
+ type: Notification.NotificationType.Danger
+
+ action: [
+ Action {
+ text: qsTr("OK")
+
+ onTriggered: {
+ root.imapLoginWhileSignedOut.active = false;
+ }
+ }
+ ]
+
+ Connections {
+ function onImapLoginWhileSignedOut(username) {
+ root.imapLoginWhileSignedOut.description = qsTr("An email client tried to connect to the account %1, but this account is signed " + "out. Please sign-in to continue.").arg(username);
+ root.imapLoginWhileSignedOut.active = true;
+ }
+
+ target: Backend
+ }
+ }
+ property Notification imapPortChangeError: Notification {
+ brief: qsTr("IMAP port error")
+ description: qsTr("The IMAP port could not be changed.")
+ group: Notifications.Group.Connection
+ icon: "./icons/ic-alert.svg"
+ type: Notification.NotificationType.Danger
+
+ Connections {
+ function onImapPortChangeError() {
+ root.imapPortChangeError.active = true;
+ }
+
+ target: Backend
+ }
+ }
+ property Notification imapPortStartupError: Notification {
+ brief: qsTr("IMAP port error")
+ description: qsTr("The IMAP server could not be started. Please check or change the IMAP port.")
+ group: Notifications.Group.Connection
+ icon: "./icons/ic-alert.svg"
+ type: Notification.NotificationType.Danger
+
+ Connections {
+ function onImapPortStartupError() {
+ root.imapPortStartupError.active = true;
+ }
+
+ target: Backend
+ }
+ }
+
+ // login
+ property Notification loginConnectionError: Notification {
+ brief: qsTr("Connection error")
+ description: qsTr("Bridge is not able to contact the server, please check your internet connection.")
+ group: Notifications.Group.Configuration
+ icon: "./icons/ic-exclamation-circle-filled.svg"
+ type: Notification.NotificationType.Danger
+
+ action: [
+ Action {
+ text: qsTr("OK")
+
+ onTriggered: {
+ root.loginConnectionError.active = false;
+ }
+ }
+ ]
+
+ Connections {
+ function onLoginConnectionError(_) {
+ root.loginConnectionError.active = true;
+ }
+
+ target: Backend
+ }
+ }
+ property Notification noActiveKeyForRecipient: Notification {
+ brief: title
+ description: "#PlaceholderText#"
+ group: Notifications.Group.Dialogs | Notifications.Group.Connection
+ icon: "./icons/ic-exclamation-circle-filled.svg"
+ title: qsTr("Unable to send \nencrypted message")
+ type: Notification.NotificationType.Danger
+
+ action: [
+ Action {
+ text: qsTr("OK")
+
+ onTriggered: {
+ root.noActiveKeyForRecipient.active = false;
+ }
+ }
+ ]
+
+ Connections {
+ function onNoActiveKeyForRecipient(email) {
+ root.noActiveKeyForRecipient.description = qsTr("There are no active keys to encrypt your message to %1. " + "Please update the setting for this contact.").arg(email);
+ root.noActiveKeyForRecipient.active = true;
+ }
+
+ target: Backend
+ }
+ }
+
+ // Connection
+ property Notification noInternet: Notification {
+ brief: qsTr("No connection")
+ description: qsTr("Bridge is not able to contact the server, please check your internet connection.")
+ group: Notifications.Group.Connection
+ icon: "./icons/ic-no-connection.svg"
+ type: Notification.NotificationType.Danger
+
+ Connections {
+ function onInternetOff() {
+ root.noInternet.active = true;
+ }
+ function onInternetOn() {
+ root.noInternet.active = false;
+ }
+
+ target: Backend
+ }
+ }
+ property Notification noKeychain: Notification {
+ brief: title
+ description: qsTr("Bridge is not able to detect a supported password manager (pass or secret-service). Please install and setup supported password manager and restart the application.")
+ group: Notifications.Group.Dialogs | Notifications.Group.Configuration
+ icon: "./icons/ic-exclamation-circle-filled.svg"
+ title: qsTr("No keychain available")
+ type: Notification.NotificationType.Danger
+
+ action: [
+ Action {
+ text: qsTr("Quit Bridge")
+
+ onTriggered: {
+ Backend.quit();
+ }
+ },
+ Action {
+ text: qsTr("Restart Bridge")
+
+ onTriggered: {
+ Backend.restart();
+ }
+ }
+ ]
+
+ Connections {
+ function onNotifyHasNoKeychain() {
+ root.noKeychain.active = true;
+ }
+
+ target: Backend
+ }
+ }
+ property Notification onlyPaidUsers: Notification {
+ property var pricingLink: "https://proton.me/mail/pricing"
+
+ brief: qsTr("Upgrade your account")
+ description: qsTr("Bridge is exclusive to our mail paid plans. Upgrade your account to use Bridge.")
+ group: Notifications.Group.Configuration
+ icon: "./icons/ic-exclamation-circle-filled.svg"
+ type: Notification.NotificationType.Danger
+
+ action: [
+ Action {
+ text: qsTr("Upgrade")
+
+ onTriggered: {
+ Qt.openUrlExternally(root.onlyPaidUsers.pricingLink);
+ root.onlyPaidUsers.active = false;
+ }
+ }
+ ]
+
+ Connections {
+ function onLoginFreeUserError() {
+ root.onlyPaidUsers.active = true;
+ }
+
+ target: Backend
+ }
+ }
+ property Notification rebuildKeychain: Notification {
+ property var supportLink: "https://proton.me/support/bridge"
+
+ brief: title
+ description: qsTr("Bridge is not able to access your macOS keychain. Please consult the instructions on our support page.")
+ group: Notifications.Group.Dialogs | Notifications.Group.Configuration
+ icon: "./icons/ic-exclamation-circle-filled.svg"
+ title: qsTr("Your macOS keychain might be corrupted")
+ type: Notification.NotificationType.Danger
+
+ action: [
+ Action {
+ text: qsTr("Open the support page")
+
+ onTriggered: {
+ Backend.notifyKBArticleClicked(root.rebuildKeychain.supportLink);
+ Qt.openUrlExternally(root.rebuildKeychain.supportLink);
+ Backend.quit();
+ }
+ }
+ ]
+
+ Connections {
+ function onNotifyRebuildKeychain() {
+ console.log("notifications");
+ root.rebuildKeychain.active = true;
+ }
+
+ target: Backend
+ }
+ }
+ property Notification resetBridge: Notification {
+ property var user
+
+ brief: title
+ description: qsTr("This will clear your accounts, preferences, and cached data. You will need to reconfigure your email client. Bridge will automatically restart.")
+ group: Notifications.Group.Configuration | Notifications.Group.Dialogs
+ icon: "./icons/ic-exclamation-circle-filled.svg"
+ title: qsTr("Reset Bridge?")
+ type: Notification.NotificationType.Danger
+
+ action: [
+ Action {
+ id: resetBridge_cancel
+ text: qsTr("Cancel")
+
+ onTriggered: {
+ root.resetBridge.active = false;
+ }
+ },
+ Action {
+ id: resetBridge_reset
+ text: qsTr("Reset and restart")
+
+ onTriggered: {
+ resetBridge_reset.loading = true;
+ resetBridge_cancel.enabled = false;
+ Backend.triggerReset();
+ }
+ }
+ ]
+
+ Connections {
+ function onAskResetBridge() {
+ root.resetBridge.active = true;
+ }
+
+ target: root
+ }
+ Connections {
+ function onResetFinished() {
+ root.resetBridge.active = false;
+ resetBridge_reset.loading = false;
+ resetBridge_cancel.enabled = true;
+ }
+
+ target: Backend
+ }
+ }
+ property Notification smtpConnectionModeChangeError: Notification {
+ brief: qsTr("SMTP Connection mode error")
+ description: qsTr("The SMTP connection mode could not be changed.")
+ group: Notifications.Group.Connection
+ icon: "./icons/ic-alert.svg"
+ type: Notification.NotificationType.Danger
+
+ action: Action {
+ text: qsTr("OK")
+
+ onTriggered: {
+ root.smtpConnectionModeChangeError.active = false;
+ }
+ }
+
+ Connections {
+ function onSmtpConnectionModeChangeError() {
+ root.smtpConnectionModeChangeError.active = true;
+ }
+
+ target: Backend
+ }
+ }
+ property Notification smtpPortChangeError: Notification {
+ brief: qsTr("SMTP port error")
+ description: qsTr("The SMTP port could not be changed.")
+ group: Notifications.Group.Connection
+ icon: "./icons/ic-alert.svg"
+ type: Notification.NotificationType.Danger
+
+ Connections {
+ function onSmtpPortChangeError() {
+ root.smtpPortChangeError.active = true;
+ }
+
+ target: Backend
+ }
+ }
+ property Notification smtpPortStartupError: Notification {
+ brief: qsTr("SMTP port error")
+ description: qsTr("The SMTP server could not be started. Please check or change the SMTP port.")
+ group: Notifications.Group.Connection
+ icon: "./icons/ic-alert.svg"
+ type: Notification.NotificationType.Danger
+
+ Connections {
+ function onSmtpPortStartupError() {
+ root.smtpPortStartupError.active = true;
+ }
+
+ target: Backend
+ }
+ }
+ property Notification updateForce: Notification {
+ brief: qsTr("Bridge is outdated")
+ description: qsTr("This version of Bridge is no longer supported, please update.")
+ group: Notifications.Group.Update | Notifications.Group.ForceUpdate | Notifications.Group.Dialogs
+ icon: "./icons/ic-exclamation-circle-filled.svg"
+ title: qsTr("Update to Bridge %1").arg(data ? data.version : "")
+ type: Notification.NotificationType.Danger
+
+ action: [
+ Action {
+ text: qsTr("Install update")
+
+ onTriggered: {
+ Backend.installUpdate();
+ root.updateForce.active = false;
+ }
+ },
+ Action {
+ text: qsTr("Update manually")
+
+ onTriggered: {
+ Qt.openUrlExternally(Backend.landingPageLink);
+ root.updateForce.active = false;
+ }
+ },
+ Action {
+ text: qsTr("Quit Bridge")
+
+ onTriggered: {
+ Backend.quit();
+ root.updateForce.active = false;
+ }
+ }
+ ]
+
+ Connections {
+ function onUpdateForce(version) {
+ root.updateForce.data = {
+ "version": version
+ };
+ root.updateForce.active = true;
+ }
+
+ target: Backend
+ }
+ }
+ property Notification updateForceError: Notification {
+ brief: title
+ description: qsTr("You must update manually. Go to: https://proton.me/mail/bridge#download")
+ group: Notifications.Group.Update | Notifications.Group.Dialogs
+ icon: "./icons/ic-exclamation-circle-filled.svg"
+ title: qsTr("Bridge couldn't update")
+ type: Notification.NotificationType.Danger
+
+ action: [
+ Action {
+ text: qsTr("Update manually")
+
+ onTriggered: {
+ Qt.openUrlExternally(Backend.landingPageLink);
+ root.updateForceError.active = false;
+ }
+ },
+ Action {
+ text: qsTr("Quit Bridge")
+
+ onTriggered: {
+ Backend.quit();
+ root.updateForceError.active = false;
+ }
+ }
+ ]
+
+ Connections {
+ function onUpdateForceError() {
+ root.updateForceError.active = true;
+ }
+
+ target: Backend
+ }
+ }
+ property Notification updateIsLatestVersion: Notification {
+ brief: description
+ description: qsTr("Bridge is up to date")
+ group: Notifications.Group.Update
+ icon: "./icons/ic-info-circle-filled.svg"
+ type: Notification.NotificationType.Info
+
+ action: Action {
+ text: qsTr("OK")
+
+ onTriggered: {
+ root.updateIsLatestVersion.active = false;
+ }
+ }
+
+ Connections {
+ function onUpdateIsLatestVersion() {
+ root.updateIsLatestVersion.active = true;
+ }
+
+ target: Backend
+ }
+ }
+ property Notification updateManualError: Notification {
+ brief: title
+ description: qsTr("Please follow manual installation in order to update Bridge.")
+ group: Notifications.Group.Update
+ icon: "./icons/ic-exclamation-circle-filled.svg"
+ title: qsTr("Bridge couldn’t update")
+ type: Notification.NotificationType.Warning
+
+ action: [
+ Action {
+ text: qsTr("Update manually")
+
+ onTriggered: {
+ Qt.openUrlExternally(Backend.landingPageLink);
+ root.updateManualError.active = false;
+ Backend.quit();
+ }
+ },
+ Action {
+ text: qsTr("Remind me later")
+
+ onTriggered: {
+ root.updateManualError.active = false;
+ }
+ }
+ ]
+
+ Connections {
+ function onUpdateManualError() {
+ root.updateManualError.active = true;
+ }
+
+ target: Backend
+ }
+ }
+
+ // Updates
+ property Notification updateManualReady: Notification {
+ brief: qsTr("Update available")
+ description: {
+ const descr = qsTr("A new version of Proton Mail Bridge is available.");
+ const text = qsTr("See what's changed.");
+ const link = Backend.releaseNotesLink;
+ return `${descr} ${text}`;
+ }
+ group: Notifications.Group.Update | Notifications.Group.Dialogs
+ icon: "./icons/ic-info-circle-filled.svg"
+ title: qsTr("Update to Bridge %1").arg(data ? data.version : "")
+ type: Notification.NotificationType.Info
+
+ action: [
+ Action {
+ text: qsTr("Install update")
+
+ onTriggered: {
+ Backend.installUpdate();
+ root.updateManualReady.active = false;
+ }
+ },
+ Action {
+ text: qsTr("Update manually")
+
+ onTriggered: {
+ Qt.openUrlExternally(Backend.landingPageLink);
+ root.updateManualReady.active = false;
+ }
+ },
+ Action {
+ text: qsTr("Remind me later")
+
+ onTriggered: {
+ root.updateManualReady.active = false;
+ }
+ }
+ ]
+
+ Connections {
+ function onUpdateManualReady(version) {
+ root.updateManualReady.data = {
+ "version": version
+ };
+ root.updateManualReady.active = true;
+ }
+
+ target: Backend
+ }
+ }
+ property Notification updateManualRestartNeeded: Notification {
+ brief: description
+ description: qsTr("Bridge update is ready")
+ group: Notifications.Group.Update
+ icon: "./icons/ic-info-circle-filled.svg"
+ type: Notification.NotificationType.Info
+
+ action: Action {
+ text: qsTr("Restart Bridge")
+
+ onTriggered: {
+ Backend.restart();
+ root.updateManualRestartNeeded.active = false;
+ }
+ }
+
+ Connections {
+ function onUpdateManualRestartNeeded() {
+ root.updateManualRestartNeeded.active = true;
+ }
+
+ target: Backend
+ }
+ }
+ property Notification updateSilentError: Notification {
+ brief: description
+ description: qsTr("Bridge couldn't update")
+ group: Notifications.Group.Update
+ icon: "./icons/ic-exclamation-circle-filled.svg"
+ type: Notification.NotificationType.Warning
+
+ action: Action {
+ text: qsTr("Update manually")
+
+ onTriggered: {
+ Qt.openUrlExternally(Backend.landingPageLink);
+ root.updateSilentError.active = false;
+ }
+ }
+
+ Connections {
+ function onUpdateSilentError() {
+ root.updateSilentError.active = true;
+ }
+
+ target: Backend
+ }
+ }
+ property Notification updateSilentRestartNeeded: Notification {
+ brief: description
+ description: qsTr("Bridge update is ready")
+ group: Notifications.Group.Update
+ icon: "./icons/ic-info-circle-filled.svg"
+ type: Notification.NotificationType.Info
+
+ action: Action {
+ text: qsTr("Restart Bridge")
+
+ onTriggered: {
+ Backend.restart();
+ root.updateSilentRestartNeeded.active = false;
+ }
+ }
+
+ Connections {
+ function onUpdateSilentRestartNeeded() {
+ root.updateSilentRestartNeeded.active = true;
+ }
+
+ target: Backend
+ }
+ }
+ property Notification userBadEvent: Notification {
+ property var userID: ""
+
+ brief: title
+ description: "#PlaceHolderText"
+ group: Notifications.Group.Connection | Notifications.Group.Dialogs
+ icon: "./icons/ic-exclamation-circle-filled.svg"
+ title: qsTr("Internal error")
+ type: Notification.NotificationType.Danger
+
+ action: [
+ Action {
+ text: qsTr("Synchronize")
+
+ onTriggered: {
+ root.userBadEvent.active = false;
+ Backend.sendBadEventUserFeedback(root.userBadEvent.userID, true);
+ }
+ },
+ Action {
+ text: qsTr("Logout")
+
+ onTriggered: {
+ root.userBadEvent.active = false;
+ Backend.sendBadEventUserFeedback(root.userBadEvent.userID, false);
+ }
+ }
+ ]
+
+ Connections {
+ function onUserBadEvent(userID, errorMessage) {
+ root.userBadEvent.userID = userID;
+ root.userBadEvent.description = errorMessage;
+ root.userBadEvent.active = true;
+ }
+
+ target: Backend
+ }
+ }
+
+ signal askChangeAllMailVisibility(var isVisibleNow)
+ signal askDeleteAccount(var user)
+ signal askEnableBeta
+ signal askEnableSplitMode(var user)
+ signal askQuestion(var title, var description, var option1, var option2, var action1, var action2)
+ signal askResetBridge
}
diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/PortSettings.qml b/internal/frontend/bridge-gui/bridge-gui/qml/PortSettings.qml
index 6e10b8d8..cd524ecf 100644
--- a/internal/frontend/bridge-gui/bridge-gui/qml/PortSettings.qml
+++ b/internal/frontend/bridge-gui/bridge-gui/qml/PortSettings.qml
@@ -1,178 +1,157 @@
// Copyright (c) 2023 Proton AG
-//
// This file is part of Proton Mail Bridge.
-//
// Proton Mail Bridge 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 3 of the License, or
// (at your option) any later version.
-//
// Proton Mail Bridge 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.
-//
// You should have received a copy of the GNU General Public License
// along with Proton Mail Bridge. If not, see .
-
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import QtQuick.Controls.impl
-
import Proton
SettingsView {
id: root
- fillHeight: false
-
+ property bool _valuesChanged: (imapField.text * 1 !== Backend.imapPort || smtpField.text * 1 !== Backend.smtpPort)
property var notifications
- property bool _valuesChanged: (
- imapField.text*1 !== Backend.imapPort ||
- smtpField.text*1 !== Backend.smtpPort
- )
+ function isPortFree(field) {
+ const num = field.text * 1;
+ if (num === Backend.imapPort)
+ return true;
+ if (num === Backend.smtpPort)
+ return true;
+ if (!Backend.isPortFree(num)) {
+ field.error = true;
+ field.errorString = qsTr("Port occupied");
+ return false;
+ }
+ return true;
+ }
+ function setDefaultValues() {
+ imapField.text = Backend.imapPort;
+ smtpField.text = Backend.smtpPort;
+ imapField.error = false;
+ smtpField.error = false;
+ }
+ function validate(port) {
+ const num = port * 1;
+ if (!(num > 1 && num < 65536)) {
+ return qsTr("Invalid port number");
+ }
+ if (imapField.text === smtpField.text) {
+ return qsTr("Port numbers must be different");
+ }
+ }
+
+ fillHeight: false
+
+ Component.onCompleted: root.setDefaultValues()
+ onBack: {
+ root.setDefaultValues();
+ }
Label {
+ Layout.fillWidth: true
colorScheme: root.colorScheme
text: qsTr("Default ports")
type: Label.Heading
- Layout.fillWidth: true
}
-
Label {
+ Layout.fillWidth: true
+ color: root.colorScheme.text_weak
colorScheme: root.colorScheme
text: qsTr("Changes require reconfiguration of your email client.")
type: Label.Body
- color: root.colorScheme.text_weak
- Layout.fillWidth: true
wrapMode: Text.WordWrap
}
-
RowLayout {
spacing: 16
TextField {
id: imapField
+ Layout.alignment: Qt.AlignTop | Qt.AlignLeft
+ Layout.preferredWidth: 160
colorScheme: root.colorScheme
label: qsTr("IMAP port")
- Layout.preferredWidth: 160
- Layout.alignment: Qt.AlignTop | Qt.AlignLeft
validator: root.validate
}
TextField {
id: smtpField
+ Layout.alignment: Qt.AlignTop | Qt.AlignLeft
+ Layout.preferredWidth: 160
colorScheme: root.colorScheme
label: qsTr("SMTP port")
- Layout.preferredWidth: 160
- Layout.alignment: Qt.AlignTop | Qt.AlignLeft
validator: root.validate
}
}
-
Rectangle {
Layout.fillWidth: true
- height: 1
color: root.colorScheme.border_weak
+ height: 1
}
-
RowLayout {
spacing: 12
Button {
id: submitButton
colorScheme: root.colorScheme
- text: qsTr("Save")
enabled: (!loading) && root._valuesChanged
+ text: qsTr("Save")
+
onClicked: {
// removing error here because we may have set it manually (port occupied)
- imapField.error = false
- smtpField.error = false
+ imapField.error = false;
+ smtpField.error = false;
// checking errors separately because we want to display "same port" error only once
- imapField.validate()
+ imapField.validate();
if (imapField.error) {
- return
+ return;
}
- smtpField.validate()
+ smtpField.validate();
if (smtpField.error) {
- return
+ return;
}
-
- submitButton.loading = true
+ submitButton.loading = true;
// check both ports before returning an error
- var err = false
- err |= !isPortFree(imapField)
- err |= !isPortFree(smtpField)
+ let err = false;
+ err |= !isPortFree(imapField);
+ err |= !isPortFree(smtpField);
if (err) {
- submitButton.loading = false
- return
+ submitButton.loading = false;
+ return;
}
- // We turn off all port error notification. They well be restored if problems persist
- root.notifications.imapPortStartupError.active = false
- root.notifications.smtpPortStartupError.active = false
- root.notifications.imapPortChangeError.active = false
- root.notifications.smtpPortChangeError.active = false
-
- Backend.setMailServerSettings(imapField.text, smtpField.text, Backend.useSSLForIMAP, Backend.useSSLForSMTP)
+ // We turn off all port error notification. They will be restored if problems persist
+ root.notifications.imapPortStartupError.active = false;
+ root.notifications.smtpPortStartupError.active = false;
+ root.notifications.imapPortChangeError.active = false;
+ root.notifications.smtpPortChangeError.active = false;
+ Backend.setMailServerSettings(imapField.text, smtpField.text, Backend.useSSLForIMAP, Backend.useSSLForSMTP);
}
}
-
Button {
colorScheme: root.colorScheme
- text: qsTr("Cancel")
- onClicked: root.back()
secondary: true
- }
+ text: qsTr("Cancel")
+ onClicked: root.back()
+ }
Connections {
- target: Backend
-
function onChangeMailServerSettingsFinished() {
- submitButton.loading = false
+ submitButton.loading = false;
}
+
+ target: Backend
}
}
-
- onBack: {
- root.setDefaultValues()
- }
-
- function validate(port) {
- var num = port*1
- if (! (num > 1 && num < 65536) ) {
- return qsTr("Invalid port number")
- }
-
- if (imapField.text == smtpField.text) {
- return qsTr("Port numbers must be different")
- }
-
- return
- }
-
- function isPortFree(field) {
- var num = field.text*1
- if (num === Backend.imapPort) return true
- if (num === Backend.smtpPort) return true
- if (!Backend.isPortFree(num)) {
- field.error = true
- field.errorString = qsTr("Port occupied")
- return false
- }
-
- return true
- }
-
- function setDefaultValues(){
- imapField.text = Backend.imapPort
- smtpField.text = Backend.smtpPort
- imapField.error = false
- smtpField.error = false
- }
-
- Component.onCompleted: root.setDefaultValues()
}
diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/Proton/Action.qml b/internal/frontend/bridge-gui/bridge-gui/qml/Proton/Action.qml
index 49af2ede..9f982d18 100644
--- a/internal/frontend/bridge-gui/bridge-gui/qml/Proton/Action.qml
+++ b/internal/frontend/bridge-gui/bridge-gui/qml/Proton/Action.qml
@@ -1,20 +1,15 @@
// Copyright (c) 2023 Proton AG
-//
// This file is part of Proton Mail Bridge.
-//
// Proton Mail Bridge 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 3 of the License, or
// (at your option) any later version.
-//
// Proton Mail Bridge 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.
-//
// You should have received a copy of the GNU General Public License
// along with Proton Mail Bridge. If not, see .
-
import QtQuick
import QtQuick.Templates as T
diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/Proton/ApplicationWindow.qml b/internal/frontend/bridge-gui/bridge-gui/qml/Proton/ApplicationWindow.qml
index 70b56fa0..788aaf89 100644
--- a/internal/frontend/bridge-gui/bridge-gui/qml/Proton/ApplicationWindow.qml
+++ b/internal/frontend/bridge-gui/bridge-gui/qml/Proton/ApplicationWindow.qml
@@ -1,20 +1,15 @@
// Copyright (c) 2023 Proton AG
-//
// This file is part of Proton Mail Bridge.
-//
// Proton Mail Bridge 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 3 of the License, or
// (at your option) any later version.
-//
// Proton Mail Bridge 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.
-//
// You should have received a copy of the GNU General Public License
// along with Proton Mail Bridge. If not, see .
-
import QtQml
import QtQuick
import QtQuick.Window
@@ -25,14 +20,14 @@ import QtQuick.Templates as T
T.ApplicationWindow {
id: root
- property ColorScheme colorScheme
-
// popup priority based on types
enum PopupType {
- Banner = 0,
- Dialog = 1
+ Banner,
+ Dialog
}
+ property ColorScheme colorScheme
+
// contains currently visible popup
property var popupVisible: null
@@ -41,85 +36,61 @@ T.ApplicationWindow {
// overriding get method to ignore any role and return directly object itself
function get(row) {
if (row < 0 || row >= count) {
- return undefined
+ return undefined;
}
- return data(index(row, 0), Qt.DisplayRole)
- }
-
- onRowsInserted: function(parent, first, last) {
- for (var i = first; i <= last; i++) {
- var obj = popups.get(i)
- obj.onShouldShowChanged.connect( root.processPopups )
- }
-
- processPopups()
+ return data(index(row, 0), Qt.DisplayRole);
}
onRowsAboutToBeRemoved: function (parent, first, last) {
- for (var i = first; i <= last; i++ ) {
- var obj = popups.get(i)
- obj.onShouldShowChanged.disconnect( root.processPopups )
+ for (let i = first; i <= last; i++) {
+ const obj = popups.get(i);
+ obj.onShouldShowChanged.disconnect(root.processPopups);
// if currently visible popup was removed
if (root.popupVisible === obj) {
- root.popupVisible.visible = false
- root.popupVisible = null
+ root.popupVisible.visible = false;
+ root.popupVisible = null;
}
}
-
- processPopups()
+ processPopups();
+ }
+ onRowsInserted: function (parent, first, last) {
+ for (let i = first; i <= last; i++) {
+ const obj = popups.get(i);
+ obj.onShouldShowChanged.connect(root.processPopups);
+ }
+ processPopups();
}
}
function processPopups() {
if ((root.popupVisible) && (!root.popupVisible.shouldShow)) {
- root.popupVisible.visible = false
+ root.popupVisible.visible = false;
}
-
- var topmost = null
- for (var i = 0; i < popups.count; i++) {
- var obj = popups.get(i)
-
+ let topmost = null;
+ for (let i = 0; i < popups.count; i++) {
+ const obj = popups.get(i);
if (obj.shouldShow === false) {
- continue
+ continue;
}
-
if (topmost && (topmost.popupType > obj.popupType)) {
- continue
+ continue;
}
-
if (topmost && (topmost.popupType === obj.popupType) && (topmost.occurred > obj.occurred)) {
- continue
+ continue;
}
-
- topmost = obj
+ topmost = obj;
}
-
if (root.popupVisible !== topmost) {
if (root.popupVisible) {
- root.popupVisible.visible = false
+ root.popupVisible.visible = false;
}
- root.popupVisible = topmost
+ root.popupVisible = topmost;
}
-
if (!root.popupVisible) {
- return
- }
-
- root.popupVisible.visible = true
- }
-
- Connections {
- target: root.popupVisible
-
- function onVisibleChanged() {
- if (root.popupVisible.visible) {
- return
- }
-
- root.popupVisible = null
- root.processPopups()
+ return;
}
+ root.popupVisible.visible = true;
}
color: root.colorScheme.background_norm
@@ -127,8 +98,19 @@ T.ApplicationWindow {
Overlay.modal: Rectangle {
color: root.colorScheme.backdrop_norm
}
-
Overlay.modeless: Rectangle {
color: "transparent"
}
+
+ Connections {
+ function onVisibleChanged() {
+ if (root.popupVisible.visible) {
+ return;
+ }
+ root.popupVisible = null;
+ root.processPopups();
+ }
+
+ target: root.popupVisible
+ }
}
diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/Proton/Button.qml b/internal/frontend/bridge-gui/bridge-gui/qml/Proton/Button.qml
index 34f7c2e9..af085093 100644
--- a/internal/frontend/bridge-gui/bridge-gui/qml/Proton/Button.qml
+++ b/internal/frontend/bridge-gui/bridge-gui/qml/Proton/Button.qml
@@ -1,20 +1,15 @@
// Copyright (c) 2023 Proton AG
-//
// This file is part of Proton Mail Bridge.
-//
// Proton Mail Bridge 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 3 of the License, or
// (at your option) any later version.
-//
// Proton Mail Bridge 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.
-//
// You should have received a copy of the GNU General Public License
// along with Proton Mail Bridge. If not, see .
-
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.impl
@@ -23,212 +18,169 @@ import QtQuick.Layouts
import "." as Proton
T.Button {
- property ColorScheme colorScheme
-
- property alias secondary: control.flat
- readonly property bool primary: !secondary
- readonly property bool isIcon: control.text === ""
- readonly property bool hasTextAndIcon: (control.text !== "") && (iconImage.source.toString().length > 0)
- property bool loading: false
-
- property bool borderless: false
-
- property int labelType: Proton.Label.LabelType.Body
-
- property alias textVerticalAlignment: label.verticalAlignment
- property alias textHorizontalAlignment: label.horizontalAlignment
-
id: control
- implicitWidth: Math.max(
- implicitBackgroundWidth + leftInset + rightInset,
- implicitContentWidth + leftPadding + rightPadding
- )
- implicitHeight: Math.max(
- implicitBackgroundHeight + topInset + bottomInset,
- implicitContentHeight + topPadding + bottomPadding
- )
-
- padding: 8
- horizontalPadding: 16
- spacing: 10
+ property bool borderless: false
+ property ColorScheme colorScheme
+ readonly property bool hasTextAndIcon: (control.text !== "") && (iconImage.source.toString().length > 0)
+ readonly property bool isIcon: control.text === ""
+ property int labelType: Proton.Label.LabelType.Body
+ property bool loading: false
+ readonly property bool primary: !secondary
+ property alias secondary: control.flat
+ property alias textHorizontalAlignment: label.horizontalAlignment
+ property alias textVerticalAlignment: label.verticalAlignment
font: label.font
-
- icon.width: 16
- icon.height: 16
+ horizontalPadding: 16
icon.color: {
if (primary && !isIcon) {
- return "#FFFFFF"
+ return "#FFFFFF";
} else {
- return control.colorScheme.text_norm
+ return control.colorScheme.text_norm;
}
}
+ icon.height: 16
+ icon.width: 16
+ implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, implicitContentHeight + topPadding + bottomPadding)
+ implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, implicitContentWidth + leftPadding + rightPadding)
+ padding: 8
+ spacing: 10
+ background: Rectangle {
+ border.color: {
+ return control.colorScheme.border_norm;
+ }
+ border.width: secondary && !borderless ? 1 : 0
+ color: {
+ if (!isIcon) {
+ if (primary) {
+ // Primary colors
+ if (control.down) {
+ return control.colorScheme.interaction_norm_active;
+ }
+ if (control.enabled && (control.highlighted || control.hovered || control.checked || control.activeFocus)) {
+ return control.colorScheme.interaction_norm_hover;
+ }
+ if (control.loading) {
+ return control.colorScheme.interaction_norm_hover;
+ }
+ return control.colorScheme.interaction_norm;
+ } else {
+ // Secondary colors
+ if (control.down) {
+ return control.colorScheme.interaction_default_active;
+ }
+ if (control.enabled && (control.highlighted || control.hovered || control.checked || control.activeFocus)) {
+ return control.colorScheme.interaction_default_hover;
+ }
+ if (control.loading) {
+ return control.colorScheme.interaction_default_hover;
+ }
+ return control.colorScheme.interaction_default;
+ }
+ } else {
+ if (primary) {
+ // Primary icon colors
+ if (control.down) {
+ return control.colorScheme.interaction_default_active;
+ }
+ if (control.enabled && (control.highlighted || control.hovered || control.checked || control.activeFocus)) {
+ return control.colorScheme.interaction_default_hover;
+ }
+ if (control.loading) {
+ return control.colorScheme.interaction_default_hover;
+ }
+ return control.colorScheme.interaction_default;
+ } else {
+ // Secondary icon colors
+ if (control.down) {
+ return control.colorScheme.interaction_default_active;
+ }
+ if (control.enabled && (control.highlighted || control.hovered || control.checked || control.activeFocus)) {
+ return control.colorScheme.interaction_default_hover;
+ }
+ if (control.loading) {
+ return control.colorScheme.interaction_default_hover;
+ }
+ return control.colorScheme.interaction_default;
+ }
+ }
+ }
+ implicitHeight: 36
+ implicitWidth: 36
+ opacity: control.enabled || control.loading ? 1.0 : 0.5
+ radius: ProtonStyle.button_radius
+ visible: true
+ }
contentItem: RowLayout {
id: _contentItem
spacing: control.hasTextAndIcon ? control.spacing : 0
Proton.Label {
- colorScheme: root.colorScheme
id: label
-
- Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter
-
- elide: Text.ElideRight
- horizontalAlignment: Qt.AlignHCenter
-
- visible: !control.isIcon
- text: control.text
+ Layout.fillWidth: true
color: {
if (primary && !isIcon) {
- return "#FFFFFF"
+ return "#FFFFFF";
} else {
- return control.colorScheme.text_norm
+ return control.colorScheme.text_norm;
}
}
+ colorScheme: root.colorScheme
+ elide: Text.ElideRight
+ horizontalAlignment: Qt.AlignHCenter
opacity: control.enabled || control.loading ? 1.0 : 0.5
-
+ text: control.text
type: labelType
+ visible: !control.isIcon
}
-
ColorImage {
id: iconImage
-
Layout.alignment: Qt.AlignCenter
-
+ color: control.icon.color
+ height: {
+ if (control.loading) {
+ return width;
+ }
+ Math.min(control.icon.height, availableHeight);
+ }
+ source: control.loading ? "/qml/icons/Loader_16.svg" : control.icon.source
+ sourceSize.height: control.icon.height
+ sourceSize.width: control.icon.width
+ visible: control.loading || control.icon.source
width: {
// special case for loading since we want icon to be square for rotation animation
if (control.loading) {
- return Math.min(control.icon.width, availableWidth, control.icon.height, availableHeight)
+ return Math.min(control.icon.width, availableWidth, control.icon.height, availableHeight);
}
-
- return Math.min(control.icon.width, availableWidth)
+ return Math.min(control.icon.width, availableWidth);
}
- height: {
- if (control.loading) {
- return width
- }
-
- Math.min(control.icon.height, availableHeight)
- }
-
- sourceSize.width: control.icon.width
- sourceSize.height: control.icon.height
-
- color: control.icon.color
- source: control.loading ? "/qml/icons/Loader_16.svg" : control.icon.source
- visible: control.loading || control.icon.source
RotationAnimation {
- target: iconImage
- loops: Animation.Infinite
+ direction: RotationAnimation.Clockwise
duration: 1000
from: 0
- to: 360
- direction: RotationAnimation.Clockwise
+ loops: Animation.Infinite
running: control.loading
+ target: iconImage
+ to: 360
}
}
}
- background: Rectangle {
- implicitWidth: 36
- implicitHeight: 36
- radius: ProtonStyle.button_radius
- visible: true
- color: {
- if (!isIcon) {
- if (primary) {
- // Primary colors
-
- if (control.down) {
- return control.colorScheme.interaction_norm_active
- }
-
- if (control.enabled && (control.highlighted || control.hovered || control.checked || control.activeFocus)) {
- return control.colorScheme.interaction_norm_hover
- }
-
- if (control.loading) {
- return control.colorScheme.interaction_norm_hover
- }
-
- return control.colorScheme.interaction_norm
- } else {
- // Secondary colors
-
- if (control.down) {
- return control.colorScheme.interaction_default_active
- }
-
- if (control.enabled && (control.highlighted || control.hovered || control.checked || control.activeFocus)) {
- return control.colorScheme.interaction_default_hover
- }
-
- if (control.loading) {
- return control.colorScheme.interaction_default_hover
- }
-
- return control.colorScheme.interaction_default
- }
- } else {
- if (primary) {
- // Primary icon colors
-
- if (control.down) {
- return control.colorScheme.interaction_default_active
- }
-
- if (control.enabled && (control.highlighted || control.hovered || control.checked || control.activeFocus)) {
- return control.colorScheme.interaction_default_hover
- }
-
- if (control.loading) {
- return control.colorScheme.interaction_default_hover
- }
-
- return control.colorScheme.interaction_default
- } else {
- // Secondary icon colors
-
- if (control.down) {
- return control.colorScheme.interaction_default_active
- }
-
- if (control.enabled && (control.highlighted || control.hovered || control.checked || control.activeFocus)) {
- return control.colorScheme.interaction_default_hover
- }
-
- if (control.loading) {
- return control.colorScheme.interaction_default_hover
- }
-
- return control.colorScheme.interaction_default
- }
- }
- }
-
- border.color: {
- return control.colorScheme.border_norm
- }
- border.width: secondary && !borderless ? 1 : 0
-
- opacity: control.enabled || control.loading ? 1.0 : 0.5
- }
-
-
Component.onCompleted: {
if (!control.colorScheme) {
- console.trace()
- var next = root
- for (var i = 0; i<1000; i++) {
- console.log(i, next, "colorscheme", next.colorScheme)
- next = next.parent
- if (!next) break
+ console.trace();
+ let next = root;
+ for (let i = 0; i < 1000; i++) {
+ console.log(i, next, "colorscheme", next.colorScheme);
+ next = next.parent;
+ if (!next)
+ break;
}
- console.error("ColorScheme not defined")
+ console.error("ColorScheme not defined");
}
}
}
diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/Proton/CheckBox.qml b/internal/frontend/bridge-gui/bridge-gui/qml/Proton/CheckBox.qml
index 5c6b82ad..2dc7551d 100644
--- a/internal/frontend/bridge-gui/bridge-gui/qml/Proton/CheckBox.qml
+++ b/internal/frontend/bridge-gui/bridge-gui/qml/Proton/CheckBox.qml
@@ -1,97 +1,96 @@
// Copyright (c) 2023 Proton AG
-//
// This file is part of Proton Mail Bridge.
-//
// Proton Mail Bridge 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 3 of the License, or
// (at your option) any later version.
-//
// Proton Mail Bridge 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.
-//
// You should have received a copy of the GNU General Public License
// along with Proton Mail Bridge. If not, see .
-
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.impl
import QtQuick.Templates as T
T.CheckBox {
- property ColorScheme colorScheme
-
- property bool error: false
-
id: control
- implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
- implicitContentWidth + leftPadding + rightPadding)
- implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
- implicitContentHeight + topPadding + bottomPadding,
- implicitIndicatorHeight + topPadding + bottomPadding)
+ property ColorScheme colorScheme
+ property bool error: false
+ implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, implicitContentHeight + topPadding + bottomPadding, implicitIndicatorHeight + topPadding + bottomPadding)
+ implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, implicitContentWidth + leftPadding + rightPadding)
padding: 0
spacing: 8
+ contentItem: CheckLabel {
+ color: {
+ if (!enabled) {
+ return control.colorScheme.text_disabled;
+ }
+ if (error) {
+ return control.colorScheme.signal_danger;
+ }
+ return control.colorScheme.text_norm;
+ }
+ font.family: ProtonStyle.font_family
+ font.letterSpacing: ProtonStyle.body_letter_spacing
+ font.pixelSize: ProtonStyle.body_font_size
+ font.weight: ProtonStyle.fontWeight_400
+ leftPadding: control.indicator && !control.mirrored ? control.indicator.width + control.spacing : 0
+ lineHeight: ProtonStyle.body_line_height
+ lineHeightMode: Text.FixedHeight
+ rightPadding: control.indicator && control.mirrored ? control.indicator.width + control.spacing : 0
+ text: control.text
+ }
indicator: Rectangle {
- implicitWidth: 20
+ border.color: {
+ if (!control.enabled) {
+ return control.colorScheme.field_disabled;
+ }
+ if (control.error) {
+ return control.colorScheme.signal_danger;
+ }
+ if (control.hovered || control.activeFocus) {
+ return control.colorScheme.interaction_norm_hover;
+ }
+ return control.colorScheme.field_norm;
+ }
+ border.width: control.checked ? 0 : 1
+ color: {
+ if (!checked) {
+ return control.colorScheme.background_norm;
+ }
+ if (!control.enabled) {
+ return control.colorScheme.field_disabled;
+ }
+ if (control.error) {
+ return control.colorScheme.signal_danger;
+ }
+ if (control.hovered || control.activeFocus) {
+ return control.colorScheme.interaction_norm_hover;
+ }
+ return control.colorScheme.interaction_norm;
+ }
implicitHeight: 20
+ implicitWidth: 20
radius: ProtonStyle.checkbox_radius
-
x: text ? (control.mirrored ? control.width - width - control.rightPadding : control.leftPadding) : control.leftPadding + (control.availableWidth - width) / 2
y: control.topPadding + (control.availableHeight - height) / 2
- color: {
- if (!checked) {
- return control.colorScheme.background_norm
- }
-
- if (!control.enabled) {
- return control.colorScheme.field_disabled
- }
-
- if (control.error) {
- return control.colorScheme.signal_danger
- }
-
- if (control.hovered || control.activeFocus) {
- return control.colorScheme.interaction_norm_hover
- }
-
- return control.colorScheme.interaction_norm
- }
-
- border.width: control.checked ? 0 : 1
- border.color: {
- if (!control.enabled) {
- return control.colorScheme.field_disabled
- }
-
- if (control.error) {
- return control.colorScheme.signal_danger
- }
-
- if (control.hovered || control.activeFocus) {
- return control.colorScheme.interaction_norm_hover
- }
-
- return control.colorScheme.field_norm
- }
-
ColorImage {
+ color: "#FFFFFF"
+ height: parent.height - 4
+ source: "/qml/icons/ic-check.svg"
+ sourceSize.height: parent.height - 4
+ sourceSize.width: parent.width - 4
+ visible: control.checkState === Qt.Checked
+ width: parent.width - 4
x: (parent.width - width) / 2
y: (parent.height - height) / 2
-
- width: parent.width - 4
- height: parent.height - 4
- sourceSize.width: parent.width - 4
- sourceSize.height: parent.height - 4
- color: "#FFFFFF"
- source: "/qml/icons/ic-check.svg"
- visible: control.checkState === Qt.Checked
}
// TODO: do we need PartiallyChecked state?
@@ -105,30 +104,4 @@ T.CheckBox {
// visible: control.checkState === Qt.PartiallyChecked
//}
}
-
- contentItem: CheckLabel {
- leftPadding: control.indicator && !control.mirrored ? control.indicator.width + control.spacing : 0
- rightPadding: control.indicator && control.mirrored ? control.indicator.width + control.spacing : 0
-
- text: control.text
-
- color: {
- if (!enabled) {
- return control.colorScheme.text_disabled
- }
-
- if (error) {
- return control.colorScheme.signal_danger
- }
-
- return control.colorScheme.text_norm
- }
-
- font.family: ProtonStyle.font_family
- font.weight: ProtonStyle.fontWeight_400
- font.pixelSize: ProtonStyle.body_font_size
- lineHeight: ProtonStyle.body_line_height
- lineHeightMode: Text.FixedHeight
- font.letterSpacing: ProtonStyle.body_letter_spacing
- }
}
diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/Proton/ColorScheme.qml b/internal/frontend/bridge-gui/bridge-gui/qml/Proton/ColorScheme.qml
index 89585091..d795b7f4 100644
--- a/internal/frontend/bridge-gui/bridge-gui/qml/Proton/ColorScheme.qml
+++ b/internal/frontend/bridge-gui/bridge-gui/qml/Proton/ColorScheme.qml
@@ -1,93 +1,88 @@
// Copyright (c) 2023 Proton AG
-//
// This file is part of Proton Mail Bridge.
-//
// Proton Mail Bridge 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 3 of the License, or
// (at your option) any later version.
-//
// Proton Mail Bridge 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.
-//
// You should have received a copy of the GNU General Public License
// along with Proton Mail Bridge. If not, see .
-
import QtQuick
import QtQml
QtObject {
- // should be a pointer to ColorScheme object
- property var prominent
- // Primary
- property color primary_norm
+ // Backdrop
+ property color backdrop_norm
+ property color background_avatar
- // Interaction-norm
- property color interaction_norm
- property color interaction_norm_hover
- property color interaction_norm_active
-
- // Text
- property color text_norm
- property color text_weak
- property color text_hint
- property color text_disabled
- property color text_invert
-
- // Field
- property color field_norm
- property color field_hover
- property color field_disabled
+ // Background
+ property color background_norm
+ property color background_strong
+ property color background_weak
// Border
property color border_norm
property color border_weak
+ property color field_disabled
+ property color field_hover
- // Background
- property color background_norm
- property color background_weak
- property color background_strong
- property color background_avatar
-
- // Interaction-weak
- property color interaction_weak
- property color interaction_weak_hover
- property color interaction_weak_active
+ // Field
+ property color field_norm
// Interaction-default
property color interaction_default
- property color interaction_default_hover
property color interaction_default_active
+ property color interaction_default_hover
+
+ // Interaction-norm
+ property color interaction_norm
+ property color interaction_norm_active
+ property color interaction_norm_hover
+
+ // Interaction-weak
+ property color interaction_weak
+ property color interaction_weak_active
+ property color interaction_weak_hover
+ property string logo_img
+
+ // Primary
+ property color primary_norm
+ // should be a pointer to ColorScheme object
+ property var prominent
+ property color scrollbar_hover
// Scrollbar
property color scrollbar_norm
- property color scrollbar_hover
-
- // Signal
- property color signal_danger
- property color signal_danger_hover
- property color signal_danger_active
- property color signal_warning
- property color signal_warning_hover
- property color signal_warning_active
- property color signal_success
- property color signal_success_hover
- property color signal_success_active
- property color signal_info
- property color signal_info_hover
- property color signal_info_active
+ property color shadow_lifted
// Shadows
property color shadow_norm
- property color shadow_lifted
- // Backdrop
- property color backdrop_norm
+ // Signal
+ property color signal_danger
+ property color signal_danger_active
+ property color signal_danger_hover
+ property color signal_info
+ property color signal_info_active
+ property color signal_info_hover
+ property color signal_success
+ property color signal_success_active
+ property color signal_success_hover
+ property color signal_warning
+ property color signal_warning_active
+ property color signal_warning_hover
+ property color text_disabled
+ property color text_hint
+ property color text_invert
+
+ // Text
+ property color text_norm
+ property color text_weak
// Images
property string welcome_img
- property string logo_img
}
diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/Proton/ComboBox.qml b/internal/frontend/bridge-gui/bridge-gui/qml/Proton/ComboBox.qml
index 988abeb0..0d904f86 100644
--- a/internal/frontend/bridge-gui/bridge-gui/qml/Proton/ComboBox.qml
+++ b/internal/frontend/bridge-gui/bridge-gui/qml/Proton/ComboBox.qml
@@ -1,20 +1,15 @@
// Copyright (c) 2023 Proton AG
-//
// This file is part of Proton Mail Bridge.
-//
// Proton Mail Bridge 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 3 of the License, or
// (at your option) any later version.
-//
// Proton Mail Bridge 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.
-//
// You should have received a copy of the GNU General Public License
// along with Proton Mail Bridge. If not, see .
-
import QtQuick
import QtQuick.Window
import QtQuick.Controls
@@ -26,148 +21,124 @@ T.ComboBox {
property ColorScheme colorScheme
- implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
- implicitContentWidth + leftPadding + rightPadding)
- implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
- implicitContentHeight + topPadding + bottomPadding,
- implicitIndicatorHeight + topPadding + bottomPadding)
-
+ bottomPadding: 5
+ font.family: ProtonStyle.font_family
+ font.letterSpacing: ProtonStyle.body_letter_spacing
+ font.pixelSize: ProtonStyle.body_font_size
+ font.weight: ProtonStyle.fontWeight_400
+ implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, implicitContentHeight + topPadding + bottomPadding, implicitIndicatorHeight + topPadding + bottomPadding)
+ implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, implicitContentWidth + leftPadding + rightPadding)
leftPadding: 12 + (!root.mirrored || !indicator || !indicator.visible ? 0 : indicator.width + spacing)
rightPadding: 12 + (root.mirrored || !indicator || !indicator.visible ? 0 : indicator.width + spacing)
-
- topPadding: 5
- bottomPadding: 5
-
spacing: 8
+ topPadding: 5
- font.family: ProtonStyle.font_family
- font.weight: ProtonStyle.fontWeight_400
- font.pixelSize: ProtonStyle.body_font_size
- font.letterSpacing: ProtonStyle.body_letter_spacing
-
+ background: Rectangle {
+ border.color: root.colorScheme.border_norm
+ border.width: 1
+ color: {
+ if (root.down) {
+ return root.colorScheme.interaction_default_active;
+ }
+ if (root.enabled && root.hovered || root.activeFocus) {
+ return root.colorScheme.interaction_default_hover;
+ }
+ if (!root.enabled) {
+ return root.colorScheme.interaction_default;
+ }
+ return root.colorScheme.background_norm;
+ }
+ implicitHeight: 36
+ implicitWidth: 140
+ radius: ProtonStyle.context_item_radius
+ }
contentItem: T.TextField {
- padding: 5
-
- text: root.editable ? root.editText : root.displayText
- font: root.font
-
- enabled: root.editable
autoScroll: root.editable
- readOnly: root.down
+ color: root.enabled ? root.colorScheme.text_norm : root.colorScheme.text_disabled
+ enabled: root.editable
+ font: root.font
inputMethodHints: root.inputMethodHints
+ padding: 5
+ placeholderTextColor: root.enabled ? root.colorScheme.text_hint : root.colorScheme.text_disabled
+ readOnly: root.down
+ selectedTextColor: root.colorScheme.text_invert
+ selectionColor: root.colorScheme.interaction_norm
+ text: root.editable ? root.editText : root.displayText
validator: root.validator
verticalAlignment: TextInput.AlignVCenter
- color: root.enabled ? root.colorScheme.text_norm : root.colorScheme.text_disabled
- selectionColor: root.colorScheme.interaction_norm
- selectedTextColor: root.colorScheme.text_invert
- placeholderTextColor: root.enabled ? root.colorScheme.text_hint : root.colorScheme.text_disabled
-
background: Rectangle {
- radius: ProtonStyle.context_item_radius
- visible: root.enabled && root.editable && !root.flat
border.color: {
if (root.activeFocus) {
- return root.colorScheme.interaction_norm
+ return root.colorScheme.interaction_norm;
}
-
if (root.hovered || root.activeFocus) {
- return root.colorScheme.field_hover
+ return root.colorScheme.field_hover;
}
-
- return root.colorScheme.field_norm
+ return root.colorScheme.field_norm;
}
border.width: 1
color: root.colorScheme.background_norm
+ radius: ProtonStyle.context_item_radius
+ visible: root.enabled && root.editable && !root.flat
}
}
-
- background: Rectangle {
- implicitWidth: 140
- implicitHeight: 36
- radius: ProtonStyle.context_item_radius
- color: {
- if (root.down) {
- return root.colorScheme.interaction_default_active
- }
-
- if (root.enabled && root.hovered || root.activeFocus) {
- return root.colorScheme.interaction_default_hover
- }
-
- if (!root.enabled) {
- return root.colorScheme.interaction_default
- }
-
- return root.colorScheme.background_norm
- }
-
- border.color: root.colorScheme.border_norm
- border.width: 1
- }
-
- indicator: ColorImage {
- x: root.mirrored ? 12 : root.width - width - 12
- y: root.topPadding + (root.availableHeight - height) / 2
- color: root.enabled ? root.colorScheme.text_norm : root.colorScheme.text_disabled
- source: popup.visible ? "/qml/icons/ic-chevron-up.svg" : "/qml/icons/ic-chevron-down.svg"
-
- sourceSize.width: 16
- sourceSize.height: 16
- }
-
-
delegate: ItemDelegate {
- width: parent.width
- text: root.textRole ? (Array.isArray(root.model) ? modelData[root.textRole] : model[root.textRole]) : modelData
-
- palette.text: {
- if (!root.enabled) {
- return root.colorScheme.text_disabled
- }
-
- if (selected) {
- return root.colorScheme.text_invert
- }
-
- return root.colorScheme.text_norm
- }
- font: root.font
-
- hoverEnabled: root.hoverEnabled
-
property bool selected: root.currentIndex === index
+ font: root.font
highlighted: root.highlightedIndex === index
+ hoverEnabled: root.hoverEnabled
palette.highlightedText: selected ? root.colorScheme.text_invert : root.colorScheme.text_norm
+ palette.text: {
+ if (!root.enabled) {
+ return root.colorScheme.text_disabled;
+ }
+ if (selected) {
+ return root.colorScheme.text_invert;
+ }
+ return root.colorScheme.text_norm;
+ }
+ text: root.textRole ? (Array.isArray(root.model) ? modelData[root.textRole] : model[root.textRole]) : modelData
+ width: parent.width
background: PaddedRectangle {
- radius: ProtonStyle.context_item_radius
color: {
if (parent.down) {
- return root.colorScheme.interaction_default_active
+ return root.colorScheme.interaction_default_active;
}
-
if (parent.selected) {
- return root.colorScheme.interaction_norm
+ return root.colorScheme.interaction_norm;
}
-
if (parent.hovered || parent.highlighted) {
- return root.colorScheme.interaction_default_hover
+ return root.colorScheme.interaction_default_hover;
}
-
- return root.colorScheme.interaction_default
+ return root.colorScheme.interaction_default;
}
+ radius: ProtonStyle.context_item_radius
}
}
-
+ indicator: ColorImage {
+ color: root.enabled ? root.colorScheme.text_norm : root.colorScheme.text_disabled
+ source: popup.visible ? "/qml/icons/ic-chevron-up.svg" : "/qml/icons/ic-chevron-down.svg"
+ sourceSize.height: 16
+ sourceSize.width: 16
+ x: root.mirrored ? 12 : root.width - width - 12
+ y: root.topPadding + (root.availableHeight - height) / 2
+ }
popup: T.Popup {
- y: root.height
- width: root.width
+ bottomMargin: 8
height: Math.min(contentItem.implicitHeight, root.Window.height - topMargin - bottomMargin)
topMargin: 8
- bottomMargin: 8
+ width: root.width
+ y: root.height
+ background: Rectangle {
+ border.color: root.colorScheme.border_weak
+ border.width: 1
+ color: root.colorScheme.background_norm
+ radius: ProtonStyle.dialog_radius
+ }
contentItem: Item {
implicitHeight: children[0].implicitHeight + children[0].anchors.topMargin + children[0].anchors.bottomMargin
implicitWidth: children[0].implicitWidth + children[0].anchors.leftMargin + children[0].anchors.rightMargin
@@ -175,21 +146,14 @@ T.ComboBox {
ListView {
anchors.fill: parent
anchors.margins: 8
-
+ currentIndex: root.highlightedIndex
implicitHeight: contentHeight
model: root.delegateModel
- currentIndex: root.highlightedIndex
spacing: 4
- T.ScrollIndicator.vertical: ScrollIndicator { }
+ T.ScrollIndicator.vertical: ScrollIndicator {
+ }
}
}
-
- background: Rectangle {
- color: root.colorScheme.background_norm
- radius: ProtonStyle.dialog_radius
- border.color: root.colorScheme.border_weak
- border.width: 1
- }
}
}
diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/Proton/Dialog.qml b/internal/frontend/bridge-gui/bridge-gui/qml/Proton/Dialog.qml
index edbfc8bf..368bcb30 100644
--- a/internal/frontend/bridge-gui/bridge-gui/qml/Proton/Dialog.qml
+++ b/internal/frontend/bridge-gui/bridge-gui/qml/Proton/Dialog.qml
@@ -1,20 +1,15 @@
// Copyright (c) 2023 Proton AG
-//
// This file is part of Proton Mail Bridge.
-//
// Proton Mail Bridge 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 3 of the License, or
// (at your option) any later version.
-//
// Proton Mail Bridge 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.
-//
// You should have received a copy of the GNU General Public License
// along with Proton Mail Bridge. If not, see .
-
import QtQml
import QtQuick
import QtQuick.Templates as T
@@ -23,58 +18,46 @@ import QtQuick.Controls.impl
T.Dialog {
id: root
+
property ColorScheme colorScheme
-
- Component.onCompleted: {
- if (!ApplicationWindow.window) {
- return
- }
-
- if (ApplicationWindow.window.popups === undefined) {
- return
- }
-
- var obj = this
- ApplicationWindow.window.popups.append( { obj } )
- }
-
- readonly property int popupType: ApplicationWindow.PopupType.Dialog
-
- property bool shouldShow: false
readonly property var occurred: shouldShow ? new Date() : undefined
- function open() {
- root.shouldShow = true
- }
+ readonly property int popupType: ApplicationWindow.PopupType.Dialog
+ property bool shouldShow: false
function close() {
- root.shouldShow = false
+ root.shouldShow = false;
+ }
+ function open() {
+ root.shouldShow = true;
}
anchors.centerIn: Overlay.overlay
-
- implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
- contentWidth + leftPadding + rightPadding,
- implicitHeaderWidth,
- implicitFooterWidth)
- implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
- contentHeight + topPadding + bottomPadding
- + (implicitHeaderHeight > 0 ? implicitHeaderHeight + spacing : 0)
- + (implicitFooterHeight > 0 ? implicitFooterHeight + spacing : 0))
-
+ implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, contentHeight + topPadding + bottomPadding + (implicitHeaderHeight > 0 ? implicitHeaderHeight + spacing : 0) + (implicitFooterHeight > 0 ? implicitFooterHeight + spacing : 0))
+ implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, contentWidth + leftPadding + rightPadding, implicitHeaderWidth, implicitFooterWidth)
padding: 24
+ // TODO: Add DropShadow here
+ T.Overlay.modal: Rectangle {
+ color: root.colorScheme.backdrop_norm
+ }
+ T.Overlay.modeless: Rectangle {
+ color: "transparent"
+ }
background: Rectangle {
color: root.colorScheme.background_norm
radius: ProtonStyle.dialog_radius
}
- // TODO: Add DropShadow here
-
- T.Overlay.modal: Rectangle {
- color: root.colorScheme.backdrop_norm
- }
-
- T.Overlay.modeless: Rectangle {
- color: "transparent"
+ Component.onCompleted: {
+ if (!ApplicationWindow.window) {
+ return;
+ }
+ if (ApplicationWindow.window.popups === undefined) {
+ return;
+ }
+ const obj = this;
+ ApplicationWindow.window.popups.append({
+ "obj": obj
+ });
}
}
diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/Proton/Label.qml b/internal/frontend/bridge-gui/bridge-gui/qml/Proton/Label.qml
index 052d530e..da4de95a 100644
--- a/internal/frontend/bridge-gui/bridge-gui/qml/Proton/Label.qml
+++ b/internal/frontend/bridge-gui/bridge-gui/qml/Proton/Label.qml
@@ -1,32 +1,23 @@
// Copyright (c) 2023 Proton AG
-//
// This file is part of Proton Mail Bridge.
-//
// Proton Mail Bridge 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 3 of the License, or
// (at your option) any later version.
-//
// Proton Mail Bridge 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.
-//
// You should have received a copy of the GNU General Public License
// along with Proton Mail Bridge. If not, see .
-
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.impl
import QtQuick.Templates as T
-
import "." as Proton
T.Label {
id: root
-
- property ColorScheme colorScheme
-
enum LabelType {
// weight 700, size 28, height 36
Heading,
@@ -47,96 +38,92 @@ T.Label {
// weight 700, size 12, height 16, spacing 0.4
Caption_bold
}
+
+ property ColorScheme colorScheme
property int type: Proton.Label.LabelType.Body
+ function link(url, text) {
+ return `${text}`;
+ }
+
color: root.enabled ? root.colorScheme.text_norm : root.colorScheme.text_disabled
- linkColor: root.colorScheme.interaction_norm
- palette.link: linkColor
-
font.family: ProtonStyle.font_family
- lineHeightMode: Text.FixedHeight
-
- font.weight: {
- switch (root.type) {
- case Proton.Label.LabelType.Heading:
- return ProtonStyle.fontWeight_700
- case Proton.Label.LabelType.Title:
- return ProtonStyle.fontWeight_700
- case Proton.Label.LabelType.Lead:
- return ProtonStyle.fontWeight_400
- case Proton.Label.LabelType.Body:
- return ProtonStyle.fontWeight_400
- case Proton.Label.LabelType.Body_semibold:
- return ProtonStyle.fontWeight_600
- case Proton.Label.LabelType.Body_bold:
- return ProtonStyle.fontWeight_700
- case Proton.Label.LabelType.Caption:
- return ProtonStyle.fontWeight_400
- case Proton.Label.LabelType.Caption_semibold:
- return ProtonStyle.fontWeight_600
- case Proton.Label.LabelType.Caption_bold:
- return ProtonStyle.fontWeight_700
- }
- }
-
- font.pixelSize: {
- switch (root.type) {
- case Proton.Label.LabelType.Heading:
- return ProtonStyle.heading_font_size
- case Proton.Label.LabelType.Title:
- return ProtonStyle.title_font_size
- case Proton.Label.LabelType.Lead:
- return ProtonStyle.lead_font_size
- case Proton.Label.LabelType.Body:
- case Proton.Label.LabelType.Body_semibold:
- case Proton.Label.LabelType.Body_bold:
- return ProtonStyle.body_font_size
- case Proton.Label.LabelType.Caption:
- case Proton.Label.LabelType.Caption_semibold:
- case Proton.Label.LabelType.Caption_bold:
- return ProtonStyle.caption_font_size
- }
- }
-
- lineHeight: {
- switch (root.type) {
- case Proton.Label.LabelType.Heading:
- return ProtonStyle.heading_line_height
- case Proton.Label.LabelType.Title:
- return ProtonStyle.title_line_height
- case Proton.Label.LabelType.Lead:
- return ProtonStyle.lead_line_height
- case Proton.Label.LabelType.Body:
- case Proton.Label.LabelType.Body_semibold:
- case Proton.Label.LabelType.Body_bold:
- return ProtonStyle.body_line_height
- case Proton.Label.LabelType.Caption:
- case Proton.Label.LabelType.Caption_semibold:
- case Proton.Label.LabelType.Caption_bold:
- return ProtonStyle.caption_line_height
- }
- }
-
font.letterSpacing: {
switch (root.type) {
case Proton.Label.LabelType.Heading:
case Proton.Label.LabelType.Title:
case Proton.Label.LabelType.Lead:
- return 0
+ return 0;
case Proton.Label.LabelType.Body:
case Proton.Label.LabelType.Body_semibold:
case Proton.Label.LabelType.Body_bold:
- return ProtonStyle.body_letter_spacing
+ return ProtonStyle.body_letter_spacing;
case Proton.Label.LabelType.Caption:
case Proton.Label.LabelType.Caption_semibold:
case Proton.Label.LabelType.Caption_bold:
- return ProtonStyle.caption_letter_spacing
+ return ProtonStyle.caption_letter_spacing;
}
}
-
- verticalAlignment: Text.AlignBottom
-
- function link(url, text) {
- return `${text}`
+ font.pixelSize: {
+ switch (root.type) {
+ case Proton.Label.LabelType.Heading:
+ return ProtonStyle.heading_font_size;
+ case Proton.Label.LabelType.Title:
+ return ProtonStyle.title_font_size;
+ case Proton.Label.LabelType.Lead:
+ return ProtonStyle.lead_font_size;
+ case Proton.Label.LabelType.Body:
+ case Proton.Label.LabelType.Body_semibold:
+ case Proton.Label.LabelType.Body_bold:
+ return ProtonStyle.body_font_size;
+ case Proton.Label.LabelType.Caption:
+ case Proton.Label.LabelType.Caption_semibold:
+ case Proton.Label.LabelType.Caption_bold:
+ return ProtonStyle.caption_font_size;
+ }
}
+ font.weight: {
+ switch (root.type) {
+ case Proton.Label.LabelType.Heading:
+ return ProtonStyle.fontWeight_700;
+ case Proton.Label.LabelType.Title:
+ return ProtonStyle.fontWeight_700;
+ case Proton.Label.LabelType.Lead:
+ return ProtonStyle.fontWeight_400;
+ case Proton.Label.LabelType.Body:
+ return ProtonStyle.fontWeight_400;
+ case Proton.Label.LabelType.Body_semibold:
+ return ProtonStyle.fontWeight_600;
+ case Proton.Label.LabelType.Body_bold:
+ return ProtonStyle.fontWeight_700;
+ case Proton.Label.LabelType.Caption:
+ return ProtonStyle.fontWeight_400;
+ case Proton.Label.LabelType.Caption_semibold:
+ return ProtonStyle.fontWeight_600;
+ case Proton.Label.LabelType.Caption_bold:
+ return ProtonStyle.fontWeight_700;
+ }
+ }
+ lineHeight: {
+ switch (root.type) {
+ case Proton.Label.LabelType.Heading:
+ return ProtonStyle.heading_line_height;
+ case Proton.Label.LabelType.Title:
+ return ProtonStyle.title_line_height;
+ case Proton.Label.LabelType.Lead:
+ return ProtonStyle.lead_line_height;
+ case Proton.Label.LabelType.Body:
+ case Proton.Label.LabelType.Body_semibold:
+ case Proton.Label.LabelType.Body_bold:
+ return ProtonStyle.body_line_height;
+ case Proton.Label.LabelType.Caption:
+ case Proton.Label.LabelType.Caption_semibold:
+ case Proton.Label.LabelType.Caption_bold:
+ return ProtonStyle.caption_line_height;
+ }
+ }
+ lineHeightMode: Text.FixedHeight
+ linkColor: root.colorScheme.interaction_norm
+ palette.link: linkColor
+ verticalAlignment: Text.AlignBottom
}
diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/Proton/Menu.qml b/internal/frontend/bridge-gui/bridge-gui/qml/Proton/Menu.qml
index 02026509..0655f1de 100644
--- a/internal/frontend/bridge-gui/bridge-gui/qml/Proton/Menu.qml
+++ b/internal/frontend/bridge-gui/bridge-gui/qml/Proton/Menu.qml
@@ -1,20 +1,15 @@
// Copyright (c) 2023 Proton AG
-//
// This file is part of Proton Mail Bridge.
-//
// Proton Mail Bridge 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 3 of the License, or
// (at your option) any later version.
-//
// Proton Mail Bridge 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.
-//
// You should have received a copy of the GNU General Public License
// along with Proton Mail Bridge. If not, see .
-
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.impl
@@ -27,22 +22,19 @@ T.Menu {
property ColorScheme colorScheme
- implicitWidth: Math.max(
- implicitBackgroundWidth + leftInset + rightInset,
- contentWidth + leftPadding + rightPadding
- )
- implicitHeight: Math.max(
- implicitBackgroundHeight + topInset + bottomInset,
- contentHeight + topPadding + bottomPadding
- )
-
+ implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, contentHeight + topPadding + bottomPadding)
+ implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, contentWidth + leftPadding + rightPadding)
margins: 0
overlap: 1
- delegate: MenuItem {
- colorScheme: control.colorScheme
+ background: Rectangle {
+ border.color: colorScheme.border_weak
+ border.width: 1
+ color: colorScheme.background_norm
+ implicitHeight: 40
+ implicitWidth: 200
+ radius: ProtonStyle.account_row_radius
}
-
contentItem: Item {
implicitHeight: children[0].implicitHeight + children[0].anchors.topMargin + children[0].anchors.bottomMargin
implicitWidth: children[0].implicitWidth + children[0].anchors.leftMargin + children[0].anchors.rightMargin
@@ -50,23 +42,17 @@ T.Menu {
ListView {
anchors.fill: parent
anchors.margins: 8
-
- implicitHeight: contentHeight
- model: control.contentModel
- interactive: Window.window ? contentHeight > Window.window.height : false
clip: true
currentIndex: control.currentIndex
+ implicitHeight: contentHeight
+ interactive: Window.window ? contentHeight > Window.window.height : false
+ model: control.contentModel
- ScrollIndicator.vertical: ScrollIndicator {}
+ ScrollIndicator.vertical: ScrollIndicator {
+ }
}
}
-
- background: Rectangle {
- implicitWidth: 200
- implicitHeight: 40
- color: colorScheme.background_norm
- border.width: 1
- border.color: colorScheme.border_weak
- radius: ProtonStyle.account_row_radius
+ delegate: MenuItem {
+ colorScheme: control.colorScheme
}
}
diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/Proton/MenuItem.qml b/internal/frontend/bridge-gui/bridge-gui/qml/Proton/MenuItem.qml
index 7c30d353..3d19cbf8 100644
--- a/internal/frontend/bridge-gui/bridge-gui/qml/Proton/MenuItem.qml
+++ b/internal/frontend/bridge-gui/bridge-gui/qml/Proton/MenuItem.qml
@@ -1,20 +1,15 @@
// Copyright (c) 2023 Proton AG
-//
// This file is part of Proton Mail Bridge.
-//
// Proton Mail Bridge 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 3 of the License, or
// (at your option) any later version.
-//
// Proton Mail Bridge 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.
-//
// You should have received a copy of the GNU General Public License
// along with Proton Mail Bridge. If not, see .
-
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.impl
@@ -26,46 +21,39 @@ T.MenuItem {
property ColorScheme colorScheme
- width: parent.width // required. Other item overflows to the right of the menu and get clipped.
- implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
- implicitContentHeight + topPadding + bottomPadding,
- implicitIndicatorHeight + topPadding + bottomPadding)
-
+ font.family: ProtonStyle.font_family
+ font.letterSpacing: ProtonStyle.body_letter_spacing
+ font.pixelSize: ProtonStyle.body_font_size
+ font.weight: ProtonStyle.fontWeight_400
+ icon.color: control.enabled ? control.colorScheme.text_norm : control.colorScheme.text_disabled
+ icon.height: 24
+ icon.width: 24
+ implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, implicitContentHeight + topPadding + bottomPadding, implicitIndicatorHeight + topPadding + bottomPadding)
padding: 12
spacing: 6
-
- icon.width: 24
- icon.height: 24
- icon.color: control.enabled ? control.colorScheme.text_norm : control.colorScheme.text_disabled
-
- font.family: ProtonStyle.font_family
- font.weight: ProtonStyle.fontWeight_400
- font.pixelSize: ProtonStyle.body_font_size
- font.letterSpacing: ProtonStyle.body_letter_spacing
-
- contentItem: IconLabel {
- id: iconLabel
- readonly property real arrowPadding: control.subMenu && control.arrow ? control.arrow.width + control.spacing : 0
- readonly property real indicatorPadding: control.checkable && control.indicator ? control.indicator.width + control.spacing : 0
- leftPadding: !control.mirrored ? indicatorPadding : arrowPadding
- rightPadding: control.mirrored ? indicatorPadding : arrowPadding
-
- spacing: control.spacing
- mirrored: control.mirrored
- display: control.display
- alignment: Qt.AlignLeft
-
- icon: control.icon
- text: control.text
- font: control.font
-
- color: control.enabled ? control.colorScheme.text_norm : control.colorScheme.text_disabled
- }
+ width: parent.width // required. Other item overflows to the right of the menu and get clipped.
background: Rectangle {
- implicitWidth: 164
- implicitHeight: 36
- radius: ProtonStyle.button_radius
color: control.down ? control.colorScheme.interaction_default_active : control.highlighted ? control.colorScheme.interaction_default_hover : control.colorScheme.interaction_default
+ implicitHeight: 36
+ implicitWidth: 164
+ radius: ProtonStyle.button_radius
+ }
+ contentItem: IconLabel {
+ id: iconLabel
+
+ readonly property real arrowPadding: control.subMenu && control.arrow ? control.arrow.width + control.spacing : 0
+ readonly property real indicatorPadding: control.checkable && control.indicator ? control.indicator.width + control.spacing : 0
+
+ alignment: Qt.AlignLeft
+ color: control.enabled ? control.colorScheme.text_norm : control.colorScheme.text_disabled
+ display: control.display
+ font: control.font
+ icon: control.icon
+ leftPadding: !control.mirrored ? indicatorPadding : arrowPadding
+ mirrored: control.mirrored
+ rightPadding: control.mirrored ? indicatorPadding : arrowPadding
+ spacing: control.spacing
+ text: control.text
}
}
diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/Proton/Popup.qml b/internal/frontend/bridge-gui/bridge-gui/qml/Proton/Popup.qml
index bf980b8d..b2be86c0 100644
--- a/internal/frontend/bridge-gui/bridge-gui/qml/Proton/Popup.qml
+++ b/internal/frontend/bridge-gui/bridge-gui/qml/Proton/Popup.qml
@@ -1,20 +1,15 @@
// Copyright (c) 2023 Proton AG
-//
// This file is part of Proton Mail Bridge.
-//
// Proton Mail Bridge 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 3 of the License, or
// (at your option) any later version.
-//
// Proton Mail Bridge 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.
-//
// You should have received a copy of the GNU General Public License
// along with Proton Mail Bridge. If not, see .
-
import QtQml
import QtQuick
import QtQuick.Controls
@@ -23,45 +18,40 @@ import QtQuick.Templates as T
T.Popup {
id: root
+
property ColorScheme colorScheme
-
- Component.onCompleted: {
- if (!ApplicationWindow.window) {
- return
- }
-
- if (ApplicationWindow.window.popups === undefined) {
- return
- }
-
- var obj = this
- ApplicationWindow.window.popups.append( { obj } )
- }
-
- property int popupType: ApplicationWindow.PopupType.Banner
-
- property bool shouldShow: false
readonly property var occurred: shouldShow ? new Date() : undefined
- function open() {
- root.shouldShow = true
- }
+ property int popupType: ApplicationWindow.PopupType.Banner
+ property bool shouldShow: false
function close() {
- root.shouldShow = false
+ root.shouldShow = false;
+ }
+ function open() {
+ root.shouldShow = true;
}
- implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
- contentWidth + leftPadding + rightPadding)
- implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
- contentHeight + topPadding + bottomPadding)
+ implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, contentHeight + topPadding + bottomPadding)
+ implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, contentWidth + leftPadding + rightPadding)
// TODO: Add DropShadow here
-
T.Overlay.modal: Rectangle {
color: root.colorScheme.backdrop_norm
}
-
T.Overlay.modeless: Rectangle {
color: "transparent"
}
+
+ Component.onCompleted: {
+ if (!ApplicationWindow.window) {
+ return;
+ }
+ if (ApplicationWindow.window.popups === undefined) {
+ return;
+ }
+ const obj = this;
+ ApplicationWindow.window.popups.append({
+ "obj": obj
+ });
+ }
}
diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/Proton/RadioButton.qml b/internal/frontend/bridge-gui/bridge-gui/qml/Proton/RadioButton.qml
index 2c31863e..833d409b 100644
--- a/internal/frontend/bridge-gui/bridge-gui/qml/Proton/RadioButton.qml
+++ b/internal/frontend/bridge-gui/bridge-gui/qml/Proton/RadioButton.qml
@@ -1,115 +1,91 @@
// Copyright (c) 2023 Proton AG
-//
// This file is part of Proton Mail Bridge.
-//
// Proton Mail Bridge 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 3 of the License, or
// (at your option) any later version.
-//
// Proton Mail Bridge 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.
-//
// You should have received a copy of the GNU General Public License
// along with Proton Mail Bridge. If not, see .
-
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.impl
import QtQuick.Templates as T
T.RadioButton {
- property ColorScheme colorScheme
-
- property bool error: false
-
id: control
- implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
- implicitContentWidth + leftPadding + rightPadding)
- implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
- implicitContentHeight + topPadding + bottomPadding,
- implicitIndicatorHeight + topPadding + bottomPadding)
+ property ColorScheme colorScheme
+ property bool error: false
+ implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, implicitContentHeight + topPadding + bottomPadding, implicitIndicatorHeight + topPadding + bottomPadding)
+ implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, implicitContentWidth + leftPadding + rightPadding)
padding: 0
spacing: 8
+ contentItem: CheckLabel {
+ color: {
+ if (!enabled) {
+ return control.colorScheme.text_disabled;
+ }
+ if (error) {
+ return control.colorScheme.signal_danger;
+ }
+ return control.colorScheme.text_norm;
+ }
+ font.family: ProtonStyle.font_family
+ font.letterSpacing: ProtonStyle.body_letter_spacing
+ font.pixelSize: ProtonStyle.body_font_size
+ font.weight: ProtonStyle.fontWeight_400
+ leftPadding: control.indicator && !control.mirrored ? control.indicator.width + control.spacing : 0
+ lineHeight: ProtonStyle.body_line_height
+ lineHeightMode: Text.FixedHeight
+ rightPadding: control.indicator && control.mirrored ? control.indicator.width + control.spacing : 0
+ text: control.text
+ }
indicator: Rectangle {
- implicitWidth: 20
+ border.color: {
+ if (!control.enabled) {
+ return control.colorScheme.field_disabled;
+ }
+ if (control.error) {
+ return control.colorScheme.signal_danger;
+ }
+ if (control.hovered || control.activeFocus) {
+ return control.colorScheme.interaction_norm_hover;
+ }
+ return control.colorScheme.field_norm;
+ }
+ border.width: 1
+ color: control.colorScheme.background_norm
implicitHeight: 20
+ implicitWidth: 20
radius: width / 2
-
x: text ? (control.mirrored ? control.width - width - control.rightPadding : control.leftPadding) : control.leftPadding + (control.availableWidth - width) / 2
y: control.topPadding + (control.availableHeight - height) / 2
- color: control.colorScheme.background_norm
- border.width: 1
- border.color: {
- if (!control.enabled) {
- return control.colorScheme.field_disabled
- }
-
- if (control.error) {
- return control.colorScheme.signal_danger
- }
-
- if (control.hovered || control.activeFocus) {
- return control.colorScheme.interaction_norm_hover
- }
-
- return control.colorScheme.field_norm
- }
-
Rectangle {
- x: (parent.width - width) / 2
- y: (parent.height - height) / 2
- width: 8
- height: 8
- radius: width / 2
color: {
if (!control.enabled) {
- return control.colorScheme.field_disabled
+ return control.colorScheme.field_disabled;
}
-
if (control.error) {
- return control.colorScheme.signal_danger
+ return control.colorScheme.signal_danger;
}
-
if (control.hovered || control.activeFocus) {
- return control.colorScheme.interaction_norm_hover
+ return control.colorScheme.interaction_norm_hover;
}
-
- return control.colorScheme.interaction_norm
+ return control.colorScheme.interaction_norm;
}
+ height: 8
+ radius: width / 2
visible: control.checked
+ width: 8
+ x: (parent.width - width) / 2
+ y: (parent.height - height) / 2
}
}
-
- contentItem: CheckLabel {
- leftPadding: control.indicator && !control.mirrored ? control.indicator.width + control.spacing : 0
- rightPadding: control.indicator && control.mirrored ? control.indicator.width + control.spacing : 0
-
- text: control.text
-
- color: {
- if (!enabled) {
- return control.colorScheme.text_disabled
- }
-
- if (error) {
- return control.colorScheme.signal_danger
- }
-
- return control.colorScheme.text_norm
- }
-
- font.family: ProtonStyle.font_family
- font.weight: ProtonStyle.fontWeight_400
- font.pixelSize: ProtonStyle.body_font_size
- lineHeight: ProtonStyle.body_line_height
- lineHeightMode: Text.FixedHeight
- font.letterSpacing: ProtonStyle.body_letter_spacing
- }
}
diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/Proton/Style.qml b/internal/frontend/bridge-gui/bridge-gui/qml/Proton/Style.qml
index 4d542822..3e6379a3 100644
--- a/internal/frontend/bridge-gui/bridge-gui/qml/Proton/Style.qml
+++ b/internal/frontend/bridge-gui/bridge-gui/qml/Proton/Style.qml
@@ -1,387 +1,188 @@
// Copyright (c) 2023 Proton AG
-//
// This file is part of Proton Mail Bridge.
-//
// Proton Mail Bridge 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 3 of the License, or
// (at your option) any later version.
-//
// Proton Mail Bridge 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.
-//
// You should have received a copy of the GNU General Public License
// along with Proton Mail Bridge. If not, see .
-
pragma Singleton
import QtQml
import QtQuick
-
-import "./"
+import "."
// https://wiki.qt.io/Qml_Styling
// http://imaginativethinking.ca/make-qml-component-singleton/
-
QtObject {
id: root
- // TODO: Once we will use Qt >=5.15 this should be refactored with inline components as follows:
- // https://doc.qt.io/qt-5/qtqml-documents-definetypes.html#inline-components
-
- // component ColorScheme: QtObject {
- // property color primary_norm
- // ...
- // }
-
- property ColorScheme lightStyle: ColorScheme {
- id: _lightStyle
-
- prominent: lightProminentStyle
-
- // Primary
- primary_norm: "#6D4AFF"
-
- // Interaction-norm
- interaction_norm: "#6D4AFF"
- interaction_norm_hover: "#4D34B3"
- interaction_norm_active: "#372580"
-
- // Text
- text_norm: "#0C0C14"
- text_weak: "#706D6B"
- text_hint: "#8F8D8A"
- text_disabled: "#C2BFBC"
- text_invert: "#FFFFFF"
-
- // Field
- field_norm: "#ADABA8"
- field_hover: "#8F8D8A"
- field_disabled: "#D1CFCD"
-
- // Border
- border_norm: "#D1CFCD"
- border_weak: "#EAE7E4"
-
- // Background
- background_norm: "#FFFFFF"
- background_weak: "#F5F4F2"
- background_strong: "#EAE7E4"
- background_avatar: "#C2BFBC"
-
- // Interaction-weak
- interaction_weak: "#D1CFCD"
- interaction_weak_hover: "#C2BFBC"
- interaction_weak_active: "#A8A6A3"
-
- // Interaction-default
- interaction_default: Qt.rgba(0,0,0,0)
- interaction_default_hover: Qt.rgba(194./255., 191./255., 188./255., 0.2)
- interaction_default_active: Qt.rgba(194./255., 191./255., 188./255., 0.4)
-
- // Scrollbar
- scrollbar_norm: "#D1CFCD"
- scrollbar_hover: "#C2BFBC"
-
- // Signal
- signal_danger: "#DC3251"
- signal_danger_hover: "#F74F6D"
- signal_danger_active: "#B72346"
- signal_warning: "#FF9900"
- signal_warning_hover: "#FFB800"
- signal_warning_active: "#FF851A"
- signal_success: "#1EA885"
- signal_success_hover: "#23C299"
- signal_success_active: "#198F71"
- signal_info: "#239ECE"
- signal_info_hover: "#27B1E8"
- signal_info_active: "#1F83B5"
-
- // Shadows
- shadow_norm: Qt.rgba(0,0,0, 0.1) // #000000 10% x:0 y:1 blur:4
- shadow_lifted: Qt.rgba(0,0,0, 0.16) // #000000 16% x:0 y:8 blur:24
-
- // Backdrop
- backdrop_norm: Qt.rgba(12./255., 12./255., 20./255., 0.32)
-
- // Images
- welcome_img: "/qml/icons/img-welcome.png"
- logo_img: "/qml/icons/product_logos.svg"
- }
-
- property ColorScheme lightProminentStyle: ColorScheme {
- id: _lightProminentStyle
-
- prominent: this
-
- // Primary
- primary_norm: "#8A6EFF"
-
- // Interaction-norm
- interaction_norm: "#6D4AFF"
- interaction_norm_hover: "#7C5CFF"
- interaction_norm_active: "#8A6EFF"
-
- // Text
- text_norm: "#FFFFFF"
- text_weak: "#9282D4"
- text_hint: "#544399"
- text_disabled: "#4A398F"
- text_invert: "#1B1340"
-
- // Field
- field_norm: "#9282D4"
- field_hover: "#7C5CFF"
- field_disabled: "#38277A"
-
- // Border
- border_norm: "#413085"
- border_weak: "#3C2B80"
-
- // Background
- background_norm: "#1B1340"
- background_weak: "#271C57"
- background_strong: "#38277A"
- background_avatar: "#6D4AFF"
-
- // Interaction-weak
- interaction_weak: "#4A398F"
- interaction_weak_hover: "#6D4AFF"
- interaction_weak_active: "#8A6EFF"
-
- // Interaction-default
- interaction_default: Qt.rgba(0,0,0,0)
- interaction_default_hover: Qt.rgba(68./255., 78./255., 114./255., 0.2)
- interaction_default_active: Qt.rgba(68./255., 78./255., 114./255., 0.3)
-
- // Scrollbar
- scrollbar_norm: "#413085"
- scrollbar_hover: "#4A398F"
-
- // Signal
- signal_danger: "#F5385A"
- signal_danger_hover: "#FF5473"
- signal_danger_active: "#DC3251"
- signal_warning: "#FF9900"
- signal_warning_hover: "#FFB800"
- signal_warning_active: "#FF8419"
- signal_success: "#1EA885"
- signal_success_hover: "#23C299"
- signal_success_active: "#198F71"
- signal_info: "#2C89DB"
- signal_info_hover: "#3491E3"
- signal_info_active: "#1F83B5"
-
- // Shadows
- shadow_norm: Qt.rgba(0,0,0, 0.32) // #000000 32% x:0 y:1 blur:4
- shadow_lifted: Qt.rgba(0,0,0, 0.40) // #000000 40% x:0 y:8 blur:24
-
- // Backdrop
- backdrop_norm: Qt.rgba(0,0,0, 0.32)
-
- // Images
- welcome_img: "/qml/icons/img-welcome-dark.png"
- logo_img: "/qml/icons/product_logos_dark.svg"
- }
-
- property ColorScheme darkStyle: ColorScheme {
- id: _darkStyle
-
- prominent: darkProminentStyle
-
- // Primary
- primary_norm: "#8A6EFF"
-
- // Interaction-norm
- interaction_norm: "#6D4AFF"
- interaction_norm_hover: "#7C5CFF"
- interaction_norm_active: "#8A6EFF"
-
- // Text
- text_norm: "#FFFFFF"
- text_weak: "#A7A4B5"
- text_hint: "#6D697D"
- text_disabled: "#5B576B"
- text_invert: "#1C1B24"
-
- // Field
- field_norm: "#5B576B"
- field_hover: "#6D697D"
- field_disabled: "#3F3B4C"
-
- // Border
- border_norm: "#4A4658"
- border_weak: "#343140"
-
- // Background
- background_norm: "#1C1B24"
- background_weak: "#292733"
- background_strong: "#3F3B4C"
- background_avatar: "#6D4AFF"
-
- // Interaction-weak
- interaction_weak: "#4A4658"
- interaction_weak_hover: "#5B576B"
- interaction_weak_active: "#6D697D"
-
- // Interaction-default
- interaction_default: "#00000000"
- interaction_default_hover: Qt.rgba(91./255.,87./255.,107./255.,0.2)
- interaction_default_active: Qt.rgba(91./255.,87./255.,107./255.,0.4)
-
- // Scrollbar
- scrollbar_norm: "#4A4658"
- scrollbar_hover: "#5B576B"
-
- // Signal
- signal_danger: "#F5385A"
- signal_danger_hover: "#FF5473"
- signal_danger_active: "#DC3251"
- signal_warning: "#FF9900"
- signal_warning_hover: "#FFB800"
- signal_warning_active: "#FF8419"
- signal_success: "#1EA885"
- signal_success_hover: "#23C299"
- signal_success_active: "#198F71"
- signal_info: "#239ECE"
- signal_info_hover: "#27B1E8"
- signal_info_active: "#1F83B5"
-
- // Shadows
- shadow_norm: Qt.rgba(0,0,0,0.4) // #000000 40% x+0 y+1 blur:4
- shadow_lifted: Qt.rgba(0,0,0,0.48) // #000000 48% x+0 y+8 blur:24
-
- // Backdrop
- backdrop_norm: Qt.rgba(0,0,0,0.32)
-
- // Images
- welcome_img: "/qml/icons/img-welcome-dark.png"
- logo_img: "/qml/icons/product_logos_dark.svg"
- }
+ property real account_hover_radius: 12 * root.px // px
+ property real account_row_radius: 12 * root.px // px
+ property real avatar_radius: 8 * root.px // px
+ property real banner_radius: 12 * root.px // px
+ property real big_avatar_radius: 12 * root.px // px
+ property int body_font_size: 14
+ property real body_letter_spacing: 0.2 * root.px
+ property int body_line_height: 20
+ property real button_radius: 8 * root.px // px
+ property int caption_font_size: 12
+ property real caption_letter_spacing: 0.4 * root.px
+ property int caption_line_height: 16
+ property real card_radius: 12 * root.px // px
+ property real checkbox_radius: 4 * root.px // px
+ property real context_item_radius: 8 * root.px // px
+ property ColorScheme currentStyle: lightStyle
property ColorScheme darkProminentStyle: ColorScheme {
id: _darkProminentStyle
- prominent: this
+ // Backdrop
+ backdrop_norm: Qt.rgba(0, 0, 0, 0.32)
+ background_avatar: "#6D4AFF"
- // Primary
- primary_norm: "#8A6EFF"
-
- // Interaction-norm
- interaction_norm: "#6D4AFF"
- interaction_norm_hover: "#7C5CFF"
- interaction_norm_active: "#8A6EFF"
-
- // Text
- text_norm: "#FFFFFF"
- text_weak: "#A7A4B5"
- text_hint: "#6D697D"
- text_disabled: "#5B576B"
- text_invert: "#1C1B24"
-
- // Field
- field_norm: "#5B576B"
- field_hover: "#6D697D"
- field_disabled: "#3F3B4C"
+ // Background
+ background_norm: "#16141c"
+ background_strong: "#3F3B4C"
+ background_weak: "#292733"
// Border
border_norm: "#4A4658"
border_weak: "#343140"
+ field_disabled: "#3F3B4C"
+ field_hover: "#6D697D"
- // Background
- background_norm: "#16141c"
- background_weak: "#292733"
- background_strong: "#3F3B4C"
- background_avatar: "#6D4AFF"
-
- // Interaction-weak
- interaction_weak: "#4A4658"
- interaction_weak_hover: "#5B576B"
- interaction_weak_active: "#6D697D"
+ // Field
+ field_norm: "#5B576B"
// Interaction-default
interaction_default: "#00000000"
- interaction_default_hover: Qt.rgba(91./255.,87./255.,107./255.,0.2)
- interaction_default_active: Qt.rgba(91./255.,87./255.,107./255.,0.4)
+ interaction_default_active: Qt.rgba(91. / 255., 87. / 255., 107. / 255., 0.4)
+ interaction_default_hover: Qt.rgba(91. / 255., 87. / 255., 107. / 255., 0.2)
+
+ // Interaction-norm
+ interaction_norm: "#6D4AFF"
+ interaction_norm_active: "#8A6EFF"
+ interaction_norm_hover: "#7C5CFF"
+
+ // Interaction-weak
+ interaction_weak: "#4A4658"
+ interaction_weak_active: "#6D697D"
+ interaction_weak_hover: "#5B576B"
+ logo_img: "/qml/icons/product_logos_dark.svg"
+
+ // Primary
+ primary_norm: "#8A6EFF"
+ prominent: this
+ scrollbar_hover: "#5B576B"
// Scrollbar
scrollbar_norm: "#4A4658"
- scrollbar_hover: "#5B576B"
+ shadow_lifted: Qt.rgba(0, 0, 0, 0.48) // #000000 48% x+0 y+8 blur:24
+
+ // Shadows
+ shadow_norm: Qt.rgba(0, 0, 0, 0.4) // #000000 40% x+0 y+1 blur:4
// Signal
signal_danger: "#F5385A"
- signal_danger_hover: "#FF5473"
signal_danger_active: "#DC3251"
- signal_warning: "#FF9900"
- signal_warning_hover: "#FFB800"
- signal_warning_active: "#FF8419"
- signal_success: "#1EA885"
- signal_success_hover: "#23C299"
- signal_success_active: "#198F71"
+ signal_danger_hover: "#FF5473"
signal_info: "#239ECE"
- signal_info_hover: "#27B1E8"
signal_info_active: "#1F83B5"
+ signal_info_hover: "#27B1E8"
+ signal_success: "#1EA885"
+ signal_success_active: "#198F71"
+ signal_success_hover: "#23C299"
+ signal_warning: "#FF9900"
+ signal_warning_active: "#FF8419"
+ signal_warning_hover: "#FFB800"
+ text_disabled: "#5B576B"
+ text_hint: "#6D697D"
+ text_invert: "#1C1B24"
- // Shadows
- shadow_norm: Qt.rgba(0,0,0,0.4) // #000000 40% x+0 y+1 blur:4
- shadow_lifted: Qt.rgba(0,0,0,0.48) // #000000 48% x+0 y+8 blur:24
-
- // Backdrop
- backdrop_norm: Qt.rgba(0,0,0,0.32)
+ // Text
+ text_norm: "#FFFFFF"
+ text_weak: "#A7A4B5"
// Images
welcome_img: "/qml/icons/img-welcome-dark.png"
- logo_img: "/qml/icons/product_logos_dark.svg"
}
+ property ColorScheme darkStyle: ColorScheme {
+ id: _darkStyle
- property ColorScheme currentStyle: lightStyle
+ // Backdrop
+ backdrop_norm: Qt.rgba(0, 0, 0, 0.32)
+ background_avatar: "#6D4AFF"
- property string font_family: {
- switch (Qt.platform.os) {
- case "windows":
- return "Segoe UI"
- case "osx":
- return ".AppleSystemUIFont" // should be SF Pro for the foreseeable future. Using "SF Pro Display" directly here is not allowed by the font's license.
- case "linux":
- return "Ubuntu"
- default:
- console.error("Unknown platform")
- }
+ // Background
+ background_norm: "#1C1B24"
+ background_strong: "#3F3B4C"
+ background_weak: "#292733"
+
+ // Border
+ border_norm: "#4A4658"
+ border_weak: "#343140"
+ field_disabled: "#3F3B4C"
+ field_hover: "#6D697D"
+
+ // Field
+ field_norm: "#5B576B"
+
+ // Interaction-default
+ interaction_default: "#00000000"
+ interaction_default_active: Qt.rgba(91. / 255., 87. / 255., 107. / 255., 0.4)
+ interaction_default_hover: Qt.rgba(91. / 255., 87. / 255., 107. / 255., 0.2)
+
+ // Interaction-norm
+ interaction_norm: "#6D4AFF"
+ interaction_norm_active: "#8A6EFF"
+ interaction_norm_hover: "#7C5CFF"
+
+ // Interaction-weak
+ interaction_weak: "#4A4658"
+ interaction_weak_active: "#6D697D"
+ interaction_weak_hover: "#5B576B"
+ logo_img: "/qml/icons/product_logos_dark.svg"
+
+ // Primary
+ primary_norm: "#8A6EFF"
+ prominent: darkProminentStyle
+ scrollbar_hover: "#5B576B"
+
+ // Scrollbar
+ scrollbar_norm: "#4A4658"
+ shadow_lifted: Qt.rgba(0, 0, 0, 0.48) // #000000 48% x+0 y+8 blur:24
+
+ // Shadows
+ shadow_norm: Qt.rgba(0, 0, 0, 0.4) // #000000 40% x+0 y+1 blur:4
+
+ // Signal
+ signal_danger: "#F5385A"
+ signal_danger_active: "#DC3251"
+ signal_danger_hover: "#FF5473"
+ signal_info: "#239ECE"
+ signal_info_active: "#1F83B5"
+ signal_info_hover: "#27B1E8"
+ signal_success: "#1EA885"
+ signal_success_active: "#198F71"
+ signal_success_hover: "#23C299"
+ signal_warning: "#FF9900"
+ signal_warning_active: "#FF8419"
+ signal_warning_hover: "#FFB800"
+ text_disabled: "#5B576B"
+ text_hint: "#6D697D"
+ text_invert: "#1C1B24"
+
+ // Text
+ text_norm: "#FFFFFF"
+ text_weak: "#A7A4B5"
+
+ // Images
+ welcome_img: "/qml/icons/img-welcome-dark.png"
}
-
- property real px : 1.00 // px
-
- property real input_radius : 8 * root.px // px
- property real button_radius : 8 * root.px // px
- property real checkbox_radius : 4 * root.px // px
- property real avatar_radius : 8 * root.px // px
- property real big_avatar_radius : 12 * root.px // px
- property real account_hover_radius : 12 * root.px // px
- property real account_row_radius : 12 * root.px // px
- property real context_item_radius : 8 * root.px // px
- property real banner_radius : 12 * root.px // px
- property real dialog_radius : 12 * root.px // px
- property real card_radius : 12 * root.px // px
- property real progress_bar_radius : 3 * root.px // px
- property real tooltip_radius : 8 * root.px // px
-
- property int heading_font_size: 28
- property int heading_line_height: 36
-
- property int title_font_size: 20
- property int title_line_height: 24
-
- property int lead_font_size: 18
- property int lead_line_height: 26
-
- property int body_font_size: 14
- property int body_line_height: 20
- property real body_letter_spacing: 0.2 * root.px
-
- property int caption_font_size: 12
- property int caption_line_height: 16
- property real caption_letter_spacing: 0.4 * root.px
-
+ property real dialog_radius: 12 * root.px // px
property int fontWeight_100: Font.Thin
property int fontWeight_200: Font.Light
property int fontWeight_300: Font.ExtraLight
@@ -391,4 +192,179 @@ QtObject {
property int fontWeight_700: Font.Bold
property int fontWeight_800: Font.ExtraBold
property int fontWeight_900: Font.Black
+ property string font_family: {
+ switch (Qt.platform.os) {
+ case "windows":
+ return "Segoe UI";
+ case "osx":
+ return ".AppleSystemUIFont"; // should be SF Pro for the foreseeable future. Using "SF Pro Display" directly here is not allowed by the font's license.
+ case "linux":
+ return "Ubuntu";
+ default:
+ console.error("Unknown platform");
+ }
+ }
+ property int heading_font_size: 28
+ property int heading_line_height: 36
+ property real input_radius: 8 * root.px // px
+ property int lead_font_size: 18
+ property int lead_line_height: 26
+ property ColorScheme lightProminentStyle: ColorScheme {
+ id: _lightProminentStyle
+
+ // Backdrop
+ backdrop_norm: Qt.rgba(0, 0, 0, 0.32)
+ background_avatar: "#6D4AFF"
+
+ // Background
+ background_norm: "#1B1340"
+ background_strong: "#38277A"
+ background_weak: "#271C57"
+
+ // Border
+ border_norm: "#413085"
+ border_weak: "#3C2B80"
+ field_disabled: "#38277A"
+ field_hover: "#7C5CFF"
+
+ // Field
+ field_norm: "#9282D4"
+
+ // Interaction-default
+ interaction_default: Qt.rgba(0, 0, 0, 0)
+ interaction_default_active: Qt.rgba(68. / 255., 78. / 255., 114. / 255., 0.3)
+ interaction_default_hover: Qt.rgba(68. / 255., 78. / 255., 114. / 255., 0.2)
+
+ // Interaction-norm
+ interaction_norm: "#6D4AFF"
+ interaction_norm_active: "#8A6EFF"
+ interaction_norm_hover: "#7C5CFF"
+
+ // Interaction-weak
+ interaction_weak: "#4A398F"
+ interaction_weak_active: "#8A6EFF"
+ interaction_weak_hover: "#6D4AFF"
+ logo_img: "/qml/icons/product_logos_dark.svg"
+
+ // Primary
+ primary_norm: "#8A6EFF"
+ prominent: this
+ scrollbar_hover: "#4A398F"
+
+ // Scrollbar
+ scrollbar_norm: "#413085"
+ shadow_lifted: Qt.rgba(0, 0, 0, 0.40) // #000000 40% x:0 y:8 blur:24
+
+ // Shadows
+ shadow_norm: Qt.rgba(0, 0, 0, 0.32) // #000000 32% x:0 y:1 blur:4
+
+ // Signal
+ signal_danger: "#F5385A"
+ signal_danger_active: "#DC3251"
+ signal_danger_hover: "#FF5473"
+ signal_info: "#2C89DB"
+ signal_info_active: "#1F83B5"
+ signal_info_hover: "#3491E3"
+ signal_success: "#1EA885"
+ signal_success_active: "#198F71"
+ signal_success_hover: "#23C299"
+ signal_warning: "#FF9900"
+ signal_warning_active: "#FF8419"
+ signal_warning_hover: "#FFB800"
+ text_disabled: "#4A398F"
+ text_hint: "#544399"
+ text_invert: "#1B1340"
+
+ // Text
+ text_norm: "#FFFFFF"
+ text_weak: "#9282D4"
+
+ // Images
+ welcome_img: "/qml/icons/img-welcome-dark.png"
+ }
+ // TODO: Once we will use Qt >=5.15 this should be refactored with inline components as follows:
+ // https://doc.qt.io/qt-5/qtqml-documents-definetypes.html#inline-components
+
+ // component ColorScheme: QtObject {
+ // property color primary_norm
+ // ...
+ // }
+ property ColorScheme lightStyle: ColorScheme {
+ id: _lightStyle
+
+ // Backdrop
+ backdrop_norm: Qt.rgba(12. / 255., 12. / 255., 20. / 255., 0.32)
+ background_avatar: "#C2BFBC"
+
+ // Background
+ background_norm: "#FFFFFF"
+ background_strong: "#EAE7E4"
+ background_weak: "#F5F4F2"
+
+ // Border
+ border_norm: "#D1CFCD"
+ border_weak: "#EAE7E4"
+ field_disabled: "#D1CFCD"
+ field_hover: "#8F8D8A"
+
+ // Field
+ field_norm: "#ADABA8"
+
+ // Interaction-default
+ interaction_default: Qt.rgba(0, 0, 0, 0)
+ interaction_default_active: Qt.rgba(194. / 255., 191. / 255., 188. / 255., 0.4)
+ interaction_default_hover: Qt.rgba(194. / 255., 191. / 255., 188. / 255., 0.2)
+
+ // Interaction-norm
+ interaction_norm: "#6D4AFF"
+ interaction_norm_active: "#372580"
+ interaction_norm_hover: "#4D34B3"
+
+ // Interaction-weak
+ interaction_weak: "#D1CFCD"
+ interaction_weak_active: "#A8A6A3"
+ interaction_weak_hover: "#C2BFBC"
+ logo_img: "/qml/icons/product_logos.svg"
+
+ // Primary
+ primary_norm: "#6D4AFF"
+ prominent: lightProminentStyle
+ scrollbar_hover: "#C2BFBC"
+
+ // Scrollbar
+ scrollbar_norm: "#D1CFCD"
+ shadow_lifted: Qt.rgba(0, 0, 0, 0.16) // #000000 16% x:0 y:8 blur:24
+
+ // Shadows
+ shadow_norm: Qt.rgba(0, 0, 0, 0.1) // #000000 10% x:0 y:1 blur:4
+
+ // Signal
+ signal_danger: "#DC3251"
+ signal_danger_active: "#B72346"
+ signal_danger_hover: "#F74F6D"
+ signal_info: "#239ECE"
+ signal_info_active: "#1F83B5"
+ signal_info_hover: "#27B1E8"
+ signal_success: "#1EA885"
+ signal_success_active: "#198F71"
+ signal_success_hover: "#23C299"
+ signal_warning: "#FF9900"
+ signal_warning_active: "#FF851A"
+ signal_warning_hover: "#FFB800"
+ text_disabled: "#C2BFBC"
+ text_hint: "#8F8D8A"
+ text_invert: "#FFFFFF"
+
+ // Text
+ text_norm: "#0C0C14"
+ text_weak: "#706D6B"
+
+ // Images
+ welcome_img: "/qml/icons/img-welcome.png"
+ }
+ property real progress_bar_radius: 3 * root.px // px
+ property real px: 1.00 // px
+ property int title_font_size: 20
+ property int title_line_height: 24
+ property real tooltip_radius: 8 * root.px // px
}
diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/Proton/Switch.qml b/internal/frontend/bridge-gui/bridge-gui/qml/Proton/Switch.qml
index 567f5c7f..4d633b8d 100644
--- a/internal/frontend/bridge-gui/bridge-gui/qml/Proton/Switch.qml
+++ b/internal/frontend/bridge-gui/bridge-gui/qml/Proton/Switch.qml
@@ -1,150 +1,124 @@
// Copyright (c) 2023 Proton AG
-//
// This file is part of Proton Mail Bridge.
-//
// Proton Mail Bridge 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 3 of the License, or
// (at your option) any later version.
-//
// Proton Mail Bridge 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.
-//
// You should have received a copy of the GNU General Public License
// along with Proton Mail Bridge. If not, see .
-
import QtQuick
import QtQuick.Templates as T
import QtQuick.Controls
import QtQuick.Controls.impl
T.Switch {
- property ColorScheme colorScheme
+ id: control
+ property ColorScheme colorScheme
property bool loading: false
+ implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, implicitContentHeight + topPadding + bottomPadding, implicitIndicatorHeight + topPadding + bottomPadding)
+ implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, implicitContentWidth + leftPadding + rightPadding)
+ padding: 0
+ spacing: 7
+
+ contentItem: CheckLabel {
+ id: label
+ color: control.enabled || control.loading ? control.colorScheme.text_norm : control.colorScheme.text_disabled
+ font.family: ProtonStyle.font_family
+ font.letterSpacing: ProtonStyle.body_letter_spacing
+ font.pixelSize: ProtonStyle.body_font_size
+ font.weight: ProtonStyle.fontWeight_400
+ leftPadding: control.indicator && !control.mirrored ? control.indicator.width + control.spacing : 0
+ lineHeight: ProtonStyle.body_line_height
+ lineHeightMode: Text.FixedHeight
+ rightPadding: control.indicator && control.mirrored ? control.indicator.width + control.spacing : 0
+ text: control.text
+ }
+ indicator: Rectangle {
+ border.color: control.hovered ? control.colorScheme.field_hover : control.colorScheme.field_norm
+ border.width: control.enabled && !loading ? 1 : 0
+ color: control.enabled || control.loading ? control.colorScheme.background_norm : control.colorScheme.background_strong
+ implicitHeight: 24
+ implicitWidth: 40
+ radius: height / 2.
+ x: text ? (control.mirrored ? control.width - width - control.rightPadding : control.leftPadding) : control.leftPadding + (control.availableWidth - width) / 2
+ y: control.topPadding + (control.availableHeight - height) / 2
+
+ Rectangle {
+ color: {
+ if (!control.enabled) {
+ return control.colorScheme.field_disabled;
+ }
+ if (control.checked) {
+ if (control.hovered || control.activeFocus) {
+ return control.colorScheme.interaction_norm_hover;
+ }
+ return control.colorScheme.interaction_norm;
+ }
+ if (control.hovered || control.activeFocus) {
+ return control.colorScheme.field_hover;
+ }
+ return control.colorScheme.field_norm;
+ }
+ height: 24
+ radius: parent.radius
+ visible: !loading
+ width: 24
+ x: Math.max(0, Math.min(parent.width - width, control.visualPosition * parent.width - (width / 2)))
+ y: (parent.height - height) / 2
+
+ Behavior on x {
+ enabled: !control.down
+
+ SmoothedAnimation {
+ velocity: 200
+ }
+ }
+
+ ColorImage {
+ color: "#FFFFFF"
+ height: 16
+ source: "/qml/icons/ic-check.svg"
+ sourceSize.height: 16
+ sourceSize.width: 16
+ visible: control.checked
+ width: 16
+ x: (parent.width - width) / 2
+ y: (parent.height - height) / 2
+ }
+ }
+ ColorImage {
+ id: loadingImage
+ color: control.colorScheme.interaction_norm_hover
+ height: 18
+ source: "/qml/icons/Loader_16.svg"
+ sourceSize.height: 18
+ sourceSize.width: 18
+ visible: control.loading
+ width: 18
+ x: parent.width - width
+ y: (parent.height - height) / 2
+
+ RotationAnimation {
+ direction: RotationAnimation.Clockwise
+ duration: 1000
+ from: 0
+ loops: Animation.Infinite
+ running: control.loading
+ target: loadingImage
+ to: 360
+ }
+ }
+ }
+
// TODO: store previous enabled state and restore it?
// For now assuming that only enabled buttons could have loading state
onLoadingChanged: {
- if (loading) {
- enabled = false
- } else {
- enabled = true
- }
- }
-
- id: control
-
- implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
- implicitContentWidth + leftPadding + rightPadding)
- implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
- implicitContentHeight + topPadding + bottomPadding,
- implicitIndicatorHeight + topPadding + bottomPadding)
-
- padding: 0
- spacing: 7
-
- indicator: Rectangle {
- implicitWidth: 40
- implicitHeight: 24
-
- x: text ? (control.mirrored ? control.width - width - control.rightPadding : control.leftPadding) : control.leftPadding + (control.availableWidth - width) / 2
- y: control.topPadding + (control.availableHeight - height) / 2
-
- radius: height / 2.
- color: control.enabled || control.loading ? control.colorScheme.background_norm : control.colorScheme.background_strong
- border.width: control.enabled && !loading ? 1 : 0
- border.color: control.hovered ? control.colorScheme.field_hover : control.colorScheme.field_norm
-
- Rectangle {
- x: Math.max(0, Math.min(parent.width - width, control.visualPosition * parent.width - (width / 2)))
- y: (parent.height - height) / 2
- width: 24
- height: 24
- radius: parent.radius
-
- visible: !loading
-
- color: {
- if (!control.enabled) {
- return control.colorScheme.field_disabled
- }
-
- if (control.checked) {
- if (control.hovered || control.activeFocus) {
- return control.colorScheme.interaction_norm_hover
- }
-
- return control.colorScheme.interaction_norm
- }
-
- if (control.hovered || control.activeFocus) {
- return control.colorScheme.field_hover
- }
-
- return control.colorScheme.field_norm
- }
-
- ColorImage {
- x: (parent.width - width) / 2
- y: (parent.height - height) / 2
-
- width: 16
- height: 16
- sourceSize.width: 16
- sourceSize.height: 16
- color: "#FFFFFF"
- source: "/qml/icons/ic-check.svg"
- visible: control.checked
- }
-
- Behavior on x {
- enabled: !control.down
- SmoothedAnimation { velocity: 200 }
- }
- }
-
- ColorImage {
- id: loadingImage
- x: parent.width - width
- y: (parent.height - height) / 2
-
- width: 18
- height: 18
- sourceSize.width: 18
- sourceSize.height: 18
- color: control.colorScheme.interaction_norm_hover
- source: "/qml/icons/Loader_16.svg"
- visible: control.loading
-
- RotationAnimation {
- target: loadingImage
- loops: Animation.Infinite
- duration: 1000
- from: 0
- to: 360
- direction: RotationAnimation.Clockwise
- running: control.loading
- }
- }
- }
-
- contentItem: CheckLabel {
- id: label
- leftPadding: control.indicator && !control.mirrored ? control.indicator.width + control.spacing : 0
- rightPadding: control.indicator && control.mirrored ? control.indicator.width + control.spacing : 0
-
- text: control.text
-
- color: control.enabled || control.loading ? control.colorScheme.text_norm : control.colorScheme.text_disabled
-
- font.family: ProtonStyle.font_family
- font.weight: ProtonStyle.fontWeight_400
- font.pixelSize: ProtonStyle.body_font_size
- lineHeight: ProtonStyle.body_line_height
- lineHeightMode: Text.FixedHeight
- font.letterSpacing: ProtonStyle.body_letter_spacing
+ enabled = !loading;
}
}
diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/Proton/TextArea.qml b/internal/frontend/bridge-gui/bridge-gui/qml/Proton/TextArea.qml
index 3742e5cb..a5422280 100644
--- a/internal/frontend/bridge-gui/bridge-gui/qml/Proton/TextArea.qml
+++ b/internal/frontend/bridge-gui/bridge-gui/qml/Proton/TextArea.qml
@@ -1,54 +1,37 @@
// Copyright (c) 2023 Proton AG
-//
// This file is part of Proton Mail Bridge.
-//
// Proton Mail Bridge 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 3 of the License, or
// (at your option) any later version.
-//
// Proton Mail Bridge 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.
-//
// You should have received a copy of the GNU General Public License
// along with Proton Mail Bridge. If not, see .
-
import QtQml
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.impl
import QtQuick.Templates as T
import QtQuick.Layouts
-
import "." as Proton
FocusScope {
id: root
- property ColorScheme colorScheme
- property alias background: control.background
- property alias bottomInset: control.bottomInset
- //property alias flickable: control.flickable
- property alias focusReason: control.focusReason
- property alias hoverEnabled: control.hoverEnabled
- property alias hovered: control.hovered
- property alias implicitBackgroundHeight: control.implicitBackgroundHeight
- property alias implicitBackgroundWidth: control.implicitBackgroundWidth
- property alias leftInset: control.leftInset
- property alias palette: control.palette
- property alias placeholderText: control.placeholderText
- property alias placeholderTextColor: control.placeholderTextColor
- property alias rightInset: control.rightInset
- property alias topInset: control.topInset
property alias activeFocusOnPress: control.activeFocusOnPress
+ property string assistiveText
+ property alias background: control.background
property alias baseUrl: control.baseUrl
+ property alias bottomInset: control.bottomInset
property alias bottomPadding: control.bottomPadding
property alias canPaste: control.canPaste
property alias canRedo: control.canRedo
property alias canUndo: control.canUndo
property alias color: control.color
+ property ColorScheme colorScheme
property alias contentHeight: control.contentHeight
property alias contentWidth: control.contentWidth
property alias cursorDelegate: control.cursorDelegate
@@ -56,21 +39,36 @@ FocusScope {
property alias cursorRectangle: control.cursorRectangle
property alias cursorVisible: control.cursorVisible
property alias effectiveHorizontalAlignment: control.effectiveHorizontalAlignment
+ property bool error: false
+ property string errorString
+ //property alias flickable: control.flickable
+ property alias focusReason: control.focusReason
property alias font: control.font
+ property alias hint: hint.text
property alias horizontalAlignment: control.horizontalAlignment
+ property alias hoverEnabled: control.hoverEnabled
+ property alias hovered: control.hovered
property alias hoveredLink: control.hoveredLink
+ property alias implicitBackgroundHeight: control.implicitBackgroundHeight
+ property alias implicitBackgroundWidth: control.implicitBackgroundWidth
property alias inputMethodComposing: control.inputMethodComposing
property alias inputMethodHints: control.inputMethodHints
+ property alias label: label.text
+ property alias leftInset: control.leftInset
property alias leftPadding: control.leftPadding
property alias length: control.length
property alias lineCount: control.lineCount
property alias mouseSelectionMode: control.mouseSelectionMode
property alias overwriteMode: control.overwriteMode
property alias padding: control.padding
+ property alias palette: control.palette
property alias persistentSelection: control.persistentSelection
+ property alias placeholderText: control.placeholderText
+ property alias placeholderTextColor: control.placeholderTextColor
property alias preeditText: control.preeditText
property alias readOnly: control.readOnly
property alias renderType: control.renderType
+ property alias rightInset: control.rightInset
property alias rightPadding: control.rightPadding
property alias selectByKeyboard: control.selectByKeyboard
property alias selectByMouse: control.selectByMouse
@@ -84,61 +82,119 @@ FocusScope {
property alias textDocument: control.textDocument
property alias textFormat: control.textFormat
property alias textMargin: control.textMargin
+ property alias topInset: control.topInset
property alias topPadding: control.topPadding
+ property bool validateOnEditingFinished: true
// We are using our own type of validators. It should be a function
// returning an error string in case of error and undefined if no error
property var validator
property alias verticalAlignment: control.verticalAlignment
property alias wrapMode: control.wrapMode
- implicitWidth: children[0].implicitWidth
- implicitHeight: children[0].implicitHeight
+ signal editingFinished
- property alias label: label.text
- property alias hint: hint.text
- property string assistiveText
- property string errorString
-
- property bool error: false
-
- signal editingFinished()
-
- function append(text) { return control.append(text) }
- function clear() { return control.clear() }
- function copy() { return control.copy() }
- function cut() { return control.cut() }
- function deselect() { return control.deselect() }
- function getFormattedText(start, end) { return control.getFormattedText(start, end) }
- function getText(start, end) { return control.getText(start, end) }
- function insert(position, text) { return control.insert(position, text) }
- function isRightToLeft(start, end) { return control.isRightToLeft(start, end) }
- function linkAt(x, y) { return control.linkAt(x, y) }
- function moveCursorSelection(position, mode) { return control.moveCursorSelection(position, mode) }
- function paste() { return control.paste() }
- function positionAt(x, y) { return control.positionAt(x, y) }
- function positionToRectangle(position) { return control.positionToRectangle(position) }
- function redo() { return control.redo() }
- function remove(start, end) { return control.remove(start, end) }
- function select(start, end) { return control.select(start, end) }
- function selectAll() { return control.selectAll() }
- function selectWord() { return control.selectWord() }
- function undo() { return control.undo() }
+ function append(text) {
+ return control.append(text);
+ }
+ function clear() {
+ return control.clear();
+ }
+ function copy() {
+ return control.copy();
+ }
+ function cut() {
+ return control.cut();
+ }
+ function deselect() {
+ return control.deselect();
+ }
+ function getFormattedText(start, end) {
+ return control.getFormattedText(start, end);
+ }
+ function getText(start, end) {
+ return control.getText(start, end);
+ }
// Calculates the height of the component to make exactly lineNum visible in edit area
function heightForLinesVisible(lineNum) {
- var totalHeight = 0
- totalHeight += headerLayout.height
- totalHeight += footerLayout.height
- totalHeight += control.topPadding + control.bottomPadding
- totalHeight += lineNum * fontMetrics.height
- return totalHeight
+ let totalHeight = 0;
+ totalHeight += headerLayout.height;
+ totalHeight += footerLayout.height;
+ totalHeight += control.topPadding + control.bottomPadding;
+ totalHeight += lineNum * fontMetrics.height;
+ return totalHeight;
+ }
+ function insert(position, text) {
+ return control.insert(position, text);
+ }
+ function isRightToLeft(start, end) {
+ return control.isRightToLeft(start, end);
+ }
+ function linkAt(x, y) {
+ return control.linkAt(x, y);
+ }
+ function moveCursorSelection(position, mode) {
+ return control.moveCursorSelection(position, mode);
+ }
+ function paste() {
+ return control.paste();
+ }
+ function positionAt(x, y) {
+ return control.positionAt(x, y);
+ }
+ function positionToRectangle(position) {
+ return control.positionToRectangle(position);
+ }
+ function redo() {
+ return control.redo();
+ }
+ function remove(start, end) {
+ return control.remove(start, end);
+ }
+ function select(start, end) {
+ return control.select(start, end);
+ }
+ function selectAll() {
+ return control.selectAll();
+ }
+ function selectWord() {
+ return control.selectWord();
+ }
+ function undo() {
+ return control.undo();
+ }
+ function validate() {
+ if (validator === undefined) {
+ return;
+ }
+ const error = validator(text);
+ if (error) {
+ root.error = true;
+ root.errorString = error;
+ } else {
+ root.error = false;
+ root.errorString = "";
+ }
+ }
+
+ implicitHeight: children[0].implicitHeight
+ implicitWidth: children[0].implicitWidth
+
+ onEditingFinished: {
+ if (!validateOnEditingFinished) {
+ return;
+ }
+ validate();
+ }
+ onTextChanged: {
+ root.error = false;
+ root.errorString = "";
}
FontMetrics {
id: fontMetrics
font: control.font
}
-
ColumnLayout {
anchors.fill: parent
spacing: 0
@@ -149,154 +205,123 @@ FocusScope {
spacing: 0
Proton.Label {
- colorScheme: root.colorScheme
id: label
-
Layout.fillWidth: true
-
color: root.enabled ? root.colorScheme.text_norm : root.colorScheme.text_disabled
-
+ colorScheme: root.colorScheme
type: Proton.Label.LabelType.Body_semibold
}
-
Proton.Label {
- colorScheme: root.colorScheme
id: hint
-
Layout.fillWidth: true
-
color: root.enabled ? root.colorScheme.text_weak : root.colorScheme.text_disabled
+ colorScheme: root.colorScheme
horizontalAlignment: Text.AlignRight
type: Proton.Label.LabelType.Caption
}
}
-
ScrollView {
id: controlView
-
Layout.fillHeight: true
Layout.fillWidth: true
-
clip: true
T.TextArea {
id: control
-
- implicitWidth: Math.max(
- contentWidth + leftPadding + rightPadding,
- implicitBackgroundWidth + leftInset + rightInset,
- placeholder.implicitWidth + leftPadding + rightPadding
- )
- implicitHeight: Math.max(
- contentHeight + topPadding + bottomPadding,
- implicitBackgroundHeight + topInset + bottomInset,
- placeholder.implicitHeight + topPadding + bottomPadding
- )
-
- topPadding: 8
+ KeyNavigation.backtab: root.KeyNavigation.backtab
+ KeyNavigation.down: root.KeyNavigation.down
+ KeyNavigation.left: root.KeyNavigation.left
+ KeyNavigation.priority: root.KeyNavigation.priority
+ KeyNavigation.right: root.KeyNavigation.right
+ KeyNavigation.tab: root.KeyNavigation.tab
+ KeyNavigation.up: root.KeyNavigation.up
bottomPadding: 8
- leftPadding: 12
- rightPadding: 12
-
- font.family: ProtonStyle.font_family
- font.weight: ProtonStyle.fontWeight_400
- font.pixelSize: ProtonStyle.body_font_size
- font.letterSpacing: ProtonStyle.body_letter_spacing
-
color: control.enabled ? root.colorScheme.text_norm : root.colorScheme.text_disabled
- placeholderTextColor: control.enabled ? root.colorScheme.text_hint : root.colorScheme.text_disabled
- selectionColor: control.palette.highlight
- selectedTextColor: control.palette.highlightedText
-
- onEditingFinished: root.editingFinished()
-
- wrapMode: TextInput.Wrap
// enforcing default focus here within component
focus: root.focus
-
- KeyNavigation.priority: root.KeyNavigation.priority
- KeyNavigation.backtab: root.KeyNavigation.backtab
- KeyNavigation.tab: root.KeyNavigation.tab
- KeyNavigation.up: root.KeyNavigation.up
- KeyNavigation.down: root.KeyNavigation.down
- KeyNavigation.left: root.KeyNavigation.left
- KeyNavigation.right: root.KeyNavigation.right
-
+ font.family: ProtonStyle.font_family
+ font.letterSpacing: ProtonStyle.body_letter_spacing
+ font.pixelSize: ProtonStyle.body_font_size
+ font.weight: ProtonStyle.fontWeight_400
+ implicitHeight: Math.max(contentHeight + topPadding + bottomPadding, implicitBackgroundHeight + topInset + bottomInset, placeholder.implicitHeight + topPadding + bottomPadding)
+ implicitWidth: Math.max(contentWidth + leftPadding + rightPadding, implicitBackgroundWidth + leftInset + rightInset, placeholder.implicitWidth + leftPadding + rightPadding)
+ leftPadding: 12
+ placeholderTextColor: control.enabled ? root.colorScheme.text_hint : root.colorScheme.text_disabled
+ rightPadding: 12
selectByMouse: true
-
- cursorDelegate: Rectangle {
- id: cursor
- width: 1
- color: root.colorScheme.interaction_norm
- visible: control.activeFocus && !control.readOnly && control.selectionStart === control.selectionEnd
-
- Connections {
- target: control
- function onCursorPositionChanged() {
- // keep a moving cursor visible
- cursor.opacity = 1
- timer.restart()
- }
- }
-
- Timer {
- id: timer
- running: control.activeFocus && !control.readOnly
- repeat: true
- interval: Qt.styleHints.cursorFlashTime / 2
- onTriggered: cursor.opacity = !cursor.opacity ? 1 : 0
- // force the cursor visible when gaining focus
- onRunningChanged: cursor.opacity = 1
- }
- }
-
- PlaceholderText {
- id: placeholder
- x: control.leftPadding
- y: control.topPadding
- width: control.width - (control.leftPadding + control.rightPadding)
- height: control.height - (control.topPadding + control.bottomPadding)
-
- text: control.placeholderText
- font: control.font
- color: control.placeholderTextColor
- verticalAlignment: control.verticalAlignment
- visible: !control.length && !control.preeditText && (!control.activeFocus || control.horizontalAlignment !== Qt.AlignHCenter)
- elide: Text.ElideRight
- renderType: control.renderType
- }
+ selectedTextColor: control.palette.highlightedText
+ selectionColor: control.palette.highlight
+ topPadding: 8
+ wrapMode: TextInput.Wrap
background: Rectangle {
anchors.fill: parent
-
- radius: ProtonStyle.input_radius
- visible: true
- color: root.colorScheme.background_norm
border.color: {
if (!control.enabled) {
- return root.colorScheme.field_disabled
+ return root.colorScheme.field_disabled;
}
-
if (control.activeFocus) {
- return root.colorScheme.interaction_norm
+ return root.colorScheme.interaction_norm;
}
-
if (root.error) {
- return root.colorScheme.signal_danger
+ return root.colorScheme.signal_danger;
}
-
if (control.hovered) {
- return root.colorScheme.field_hover
+ return root.colorScheme.field_hover;
}
-
- return root.colorScheme.field_norm
+ return root.colorScheme.field_norm;
}
border.width: 1
+ color: root.colorScheme.background_norm
+ radius: ProtonStyle.input_radius
+ visible: true
+ }
+ cursorDelegate: Rectangle {
+ id: cursor
+ color: root.colorScheme.interaction_norm
+ visible: control.activeFocus && !control.readOnly && control.selectionStart === control.selectionEnd
+ width: 1
+
+ Connections {
+ function onCursorPositionChanged() {
+ // keep a moving cursor visible
+ cursor.opacity = 1;
+ timer.restart();
+ }
+
+ target: control
+ }
+ Timer {
+ id: timer
+ interval: Qt.styleHints.cursorFlashTime / 2
+ repeat: true
+ running: control.activeFocus && !control.readOnly
+
+ // force the cursor visible when gaining focus
+ onRunningChanged: cursor.opacity = 1
+ onTriggered: cursor.opacity = !cursor.opacity ? 1 : 0
+ }
+ }
+
+ onEditingFinished: root.editingFinished()
+
+ PlaceholderText {
+ id: placeholder
+ color: control.placeholderTextColor
+ elide: Text.ElideRight
+ font: control.font
+ height: control.height - (control.topPadding + control.bottomPadding)
+ renderType: control.renderType
+ text: control.placeholderText
+ verticalAlignment: control.verticalAlignment
+ visible: !control.length && !control.preeditText && (!control.activeFocus || control.horizontalAlignment !== Qt.AlignHCenter)
+ width: control.width - (control.leftPadding + control.rightPadding)
+ x: control.leftPadding
+ y: control.topPadding
}
}
}
-
RowLayout {
id: footerLayout
Layout.fillWidth: true
@@ -304,67 +329,29 @@ FocusScope {
ColorImage {
id: errorIcon
-
Layout.rightMargin: 4
-
- visible: root.error && (assistiveText.text.length > 0)
- source: "/qml/icons/ic-exclamation-circle-filled.svg"
color: root.colorScheme.signal_danger
height: assistiveText.height
+ source: "/qml/icons/ic-exclamation-circle-filled.svg"
sourceSize.height: assistiveText.height
+ visible: root.error && (assistiveText.text.length > 0)
}
-
Proton.Label {
- colorScheme: root.colorScheme
id: assistiveText
-
Layout.fillWidth: true
-
- text: root.error ? root.errorString : root.assistiveText
-
color: {
if (!root.enabled) {
- return root.colorScheme.text_disabled
+ return root.colorScheme.text_disabled;
}
-
if (root.error) {
- return root.colorScheme.signal_danger
+ return root.colorScheme.signal_danger;
}
-
- return root.colorScheme.text_weak
+ return root.colorScheme.text_weak;
}
-
+ colorScheme: root.colorScheme
+ text: root.error ? root.errorString : root.assistiveText
type: root.error ? Proton.Label.LabelType.Caption_semibold : Proton.Label.LabelType.Caption
}
}
}
-
- property bool validateOnEditingFinished: true
- onEditingFinished: {
- if (!validateOnEditingFinished) {
- return
- }
- validate()
- }
-
- function validate() {
- if (validator === undefined) {
- return
- }
-
- var error = validator(text)
-
- if (error) {
- root.error = true
- root.errorString = error
- } else {
- root.error = false
- root.errorString = ""
- }
- }
-
- onTextChanged: {
- root.error = false
- root.errorString = ""
- }
}
diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/Proton/TextField.qml b/internal/frontend/bridge-gui/bridge-gui/qml/Proton/TextField.qml
index ff20326b..1e630ac5 100644
--- a/internal/frontend/bridge-gui/bridge-gui/qml/Proton/TextField.qml
+++ b/internal/frontend/bridge-gui/bridge-gui/qml/Proton/TextField.qml
@@ -1,54 +1,38 @@
// Copyright (c) 2023 Proton AG
-//
// This file is part of Proton Mail Bridge.
-//
// Proton Mail Bridge 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 3 of the License, or
// (at your option) any later version.
-//
// Proton Mail Bridge 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.
-//
// You should have received a copy of the GNU General Public License
// along with Proton Mail Bridge. If not, see .
-
import QtQml
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.impl
import QtQuick.Templates as T
import QtQuick.Layouts
-
import "." as Proton
FocusScope {
id: root
- property ColorScheme colorScheme
- property alias background: control.background
- property alias bottomInset: control.bottomInset
- property alias focusReason: control.focusReason
- property alias hoverEnabled: control.hoverEnabled
- property alias hovered: control.hovered
- property alias implicitBackgroundHeight: control.implicitBackgroundHeight
- property alias implicitBackgroundWidth: control.implicitBackgroundWidth
- property alias leftInset: control.leftInset
- property alias palette: control.palette
- property alias placeholderText: control.placeholderText
- property alias placeholderTextColor: control.placeholderTextColor
- property alias rightInset: control.rightInset
- property alias topInset: control.topInset
property alias acceptableInput: control.acceptableInput
property alias activeFocusOnPress: control.activeFocusOnPress
+ property string assistiveText
property alias autoScroll: control.autoScroll
+ property alias background: control.background
+ property alias bottomInset: control.bottomInset
property alias bottomPadding: control.bottomPadding
property alias canPaste: control.canPaste
property alias canRedo: control.canRedo
property alias canUndo: control.canUndo
property alias color: control.color
+ property ColorScheme colorScheme
//property alias contentHeight: control.contentHeight
//property alias contentWidth: control.contentWidth
property alias cursorDelegate: control.cursorDelegate
@@ -56,24 +40,39 @@ FocusScope {
property alias cursorRectangle: control.cursorRectangle
property alias cursorVisible: control.cursorVisible
property alias displayText: control.displayText
+ property int echoMode: TextInput.Normal
property alias effectiveHorizontalAlignment: control.effectiveHorizontalAlignment
+ property bool error: false
+ property string errorString
+ property alias focusReason: control.focusReason
property alias font: control.font
+ property alias hint: hint.text
property alias horizontalAlignment: control.horizontalAlignment
+ property alias hoverEnabled: control.hoverEnabled
+ property alias hovered: control.hovered
+ property alias implicitBackgroundHeight: control.implicitBackgroundHeight
+ property alias implicitBackgroundWidth: control.implicitBackgroundWidth
property alias inputMask: control.inputMask
property alias inputMethodComposing: control.inputMethodComposing
property alias inputMethodHints: control.inputMethodHints
+ property alias label: label.text
+ property alias leftInset: control.leftInset
property alias leftPadding: control.leftPadding
property alias length: control.length
property alias maximumLength: control.maximumLength
property alias mouseSelectionMode: control.mouseSelectionMode
property alias overwriteMode: control.overwriteMode
property alias padding: control.padding
+ property alias palette: control.palette
property alias passwordCharacter: control.passwordCharacter
property alias passwordMaskDelay: control.passwordMaskDelay
property alias persistentSelection: control.persistentSelection
+ property alias placeholderText: control.placeholderText
+ property alias placeholderTextColor: control.placeholderTextColor
property alias preeditText: control.preeditText
property alias readOnly: control.readOnly
property alias renderType: control.renderType
+ property alias rightInset: control.rightInset
property alias rightPadding: control.rightPadding
property alias selectByMouse: control.selectByMouse
property alias selectedText: control.selectedText
@@ -82,47 +81,102 @@ FocusScope {
property alias selectionEnd: control.selectionEnd
property alias selectionStart: control.selectionStart
property alias text: control.text
+ property alias topInset: control.topInset
+ property bool validateOnEditingFinished: true
// We are using our own type of validators. It should be a function
// returning an error string in case of error and undefined if no error
property var validator
property alias verticalAlignment: control.verticalAlignment
property alias wrapMode: control.wrapMode
- implicitWidth: children[0].implicitWidth
+ signal accepted
+ signal editingFinished
+ signal textEdited
+
+ function clear() {
+ control.clear();
+ }
+ function copy() {
+ control.copy();
+ }
+ function cut() {
+ control.cut();
+ }
+ function deselect() {
+ control.deselect();
+ }
+ function ensureVisible(position) {
+ control.ensureVisible(position);
+ }
+ function forceActiveFocus() {
+ control.forceActiveFocus();
+ }
+ function getText(start, end) {
+ control.getText(start, end);
+ }
+ function insert(position, text) {
+ control.insert(position, text);
+ }
+ function isRightToLeft(start, end) {
+ control.isRightToLeft(start, end);
+ }
+ function moveCursorSelection(position, mode) {
+ control.moveCursorSelection(position, mode);
+ }
+ function paste() {
+ control.paste();
+ }
+ function positionAt(x, y, position) {
+ control.positionAt(x, y, position);
+ }
+ function positionToRectangle(pos) {
+ control.positionToRectangle(pos);
+ }
+ function redo() {
+ control.redo();
+ }
+ function remove(start, end) {
+ control.remove(start, end);
+ }
+ function select(start, end) {
+ control.select(start, end);
+ }
+ function selectAll() {
+ control.selectAll();
+ }
+ function selectWord() {
+ control.selectWord();
+ }
+ function undo() {
+ control.undo();
+ }
+ function validate() {
+ if (validator === undefined) {
+ return;
+ }
+ const error = validator(text);
+ if (error) {
+ root.error = true;
+ root.errorString = error;
+ } else {
+ root.error = false;
+ root.errorString = "";
+ }
+ }
+
implicitHeight: children[0].implicitHeight
+ implicitWidth: children[0].implicitWidth
- property alias label: label.text
- property alias hint: hint.text
- property string assistiveText
- property string errorString
-
- property int echoMode: TextInput.Normal
-
- property bool error: false
-
- signal accepted()
- signal editingFinished()
- signal textEdited()
-
- function clear() { control.clear() }
- function copy() { control.copy() }
- function cut() { control.cut() }
- function deselect() { control.deselect() }
- function ensureVisible(position) { control.ensureVisible(position) }
- function getText(start, end) { control.getText(start, end) }
- function insert(position, text) { control.insert(position, text) }
- function isRightToLeft(start, end) { control.isRightToLeft(start, end) }
- function moveCursorSelection(position, mode) { control.moveCursorSelection(position, mode) }
- function paste() { control.paste() }
- function positionAt(x, y, position) { control.positionAt(x, y, position) }
- function positionToRectangle(pos) { control.positionToRectangle(pos) }
- function redo() { control.redo() }
- function remove(start, end) { control.remove(start, end) }
- function select(start, end) { control.select(start, end) }
- function selectAll() { control.selectAll() }
- function selectWord() { control.selectWord() }
- function undo() { control.undo() }
- function forceActiveFocus() { control.forceActiveFocus() }
+ onEditingFinished: {
+ if (!validateOnEditingFinished) {
+ return;
+ }
+ validate();
+ }
+ onTextChanged: {
+ root.error = false;
+ root.errorString = "";
+ }
ColumnLayout {
anchors.fill: parent
@@ -133,19 +187,18 @@ FocusScope {
spacing: 0
Proton.Label {
- colorScheme: root.colorScheme
id: label
Layout.fillHeight: true
Layout.fillWidth: true
+ colorScheme: root.colorScheme
type: Proton.Label.LabelType.Body_semibold
}
-
Proton.Label {
- colorScheme: root.colorScheme
id: hint
Layout.fillHeight: true
Layout.fillWidth: true
color: root.enabled ? root.colorScheme.text_weak : root.colorScheme.text_disabled
+ colorScheme: root.colorScheme
horizontalAlignment: Text.AlignRight
type: Proton.Label.LabelType.Caption
}
@@ -156,36 +209,29 @@ FocusScope {
// will be adjusted to background's width making text field and eye button overlap
Rectangle {
id: background
-
Layout.fillHeight: true
Layout.fillWidth: true
-
- radius: ProtonStyle.input_radius
- visible: true
- color: root.colorScheme.background_norm
border.color: {
if (!control.enabled) {
- return root.colorScheme.field_disabled
+ return root.colorScheme.field_disabled;
}
-
if (control.activeFocus) {
- return root.colorScheme.interaction_norm
+ return root.colorScheme.interaction_norm;
}
-
if (root.error) {
- return root.colorScheme.signal_danger
+ return root.colorScheme.signal_danger;
}
-
if (control.hovered) {
- return root.colorScheme.field_hover
+ return root.colorScheme.field_hover;
}
-
- return root.colorScheme.field_norm
+ return root.colorScheme.field_norm;
}
border.width: 1
-
- implicitWidth: children[0].implicitWidth
+ color: root.colorScheme.background_norm
implicitHeight: children[0].implicitHeight
+ implicitWidth: children[0].implicitWidth
+ radius: ProtonStyle.input_radius
+ visible: true
RowLayout {
anchors.fill: parent
@@ -193,190 +239,135 @@ FocusScope {
T.TextField {
id: control
-
+ KeyNavigation.backtab: root.KeyNavigation.backtab
+ KeyNavigation.down: root.KeyNavigation.down
+ KeyNavigation.left: root.KeyNavigation.left
+ KeyNavigation.priority: root.KeyNavigation.priority
+ KeyNavigation.right: root.KeyNavigation.right
+ KeyNavigation.tab: root.KeyNavigation.tab
+ KeyNavigation.up: root.KeyNavigation.up
Layout.fillHeight: true
Layout.fillWidth: true
-
- implicitWidth: implicitBackgroundWidth + leftInset + rightInset
- || Math.max(contentWidth, placeholder.implicitWidth) + leftPadding + rightPadding
- implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
- contentHeight + topPadding + bottomPadding,
- placeholder.implicitHeight + topPadding + bottomPadding)
-
- topPadding: 8
bottomPadding: 8
- leftPadding: 12
- rightPadding: 12
-
- font.family: ProtonStyle.font_family
- font.weight: ProtonStyle.fontWeight_400
- font.pixelSize: ProtonStyle.body_font_size
- font.letterSpacing: ProtonStyle.body_letter_spacing
-
color: control.enabled ? root.colorScheme.text_norm : root.colorScheme.text_disabled
- placeholderTextColor: control.enabled ? root.colorScheme.text_hint : root.colorScheme.text_disabled
- selectionColor: control.palette.highlight
- selectedTextColor: control.palette.highlightedText
-
- verticalAlignment: TextInput.AlignVCenter
-
echoMode: eyeButton.checked ? TextInput.Normal : root.echoMode
// enforcing default focus here within component
focus: true
-
- KeyNavigation.priority: root.KeyNavigation.priority
- KeyNavigation.backtab: root.KeyNavigation.backtab
- KeyNavigation.tab: root.KeyNavigation.tab
- KeyNavigation.up: root.KeyNavigation.up
- KeyNavigation.down: root.KeyNavigation.down
- KeyNavigation.left: root.KeyNavigation.left
- KeyNavigation.right: root.KeyNavigation.right
-
+ font.family: ProtonStyle.font_family
+ font.letterSpacing: ProtonStyle.body_letter_spacing
+ font.pixelSize: ProtonStyle.body_font_size
+ font.weight: ProtonStyle.fontWeight_400
+ implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, contentHeight + topPadding + bottomPadding, placeholder.implicitHeight + topPadding + bottomPadding)
+ implicitWidth: implicitBackgroundWidth + leftInset + rightInset || Math.max(contentWidth, placeholder.implicitWidth) + leftPadding + rightPadding
+ leftPadding: 12
+ placeholderTextColor: control.enabled ? root.colorScheme.text_hint : root.colorScheme.text_disabled
+ rightPadding: 12
selectByMouse: true
+ selectedTextColor: control.palette.highlightedText
+ selectionColor: control.palette.highlight
+ topPadding: 8
+ verticalAlignment: TextInput.AlignVCenter
+ background: Item {
+ implicitHeight: 36
+ implicitWidth: 80
+ visible: false
+ }
cursorDelegate: Rectangle {
id: cursor
- width: 1
color: root.colorScheme.interaction_norm
visible: control.activeFocus && !control.readOnly && control.selectionStart === control.selectionEnd
+ width: 1
Connections {
- target: control
function onCursorPositionChanged() {
// keep a moving cursor visible
- cursor.opacity = 1
- timer.restart()
+ cursor.opacity = 1;
+ timer.restart();
}
- }
+ target: control
+ }
Timer {
id: timer
- running: control.activeFocus && !control.readOnly
- repeat: true
interval: Qt.styleHints.cursorFlashTime / 2
- onTriggered: cursor.opacity = !cursor.opacity ? 1 : 0
+ repeat: true
+ running: control.activeFocus && !control.readOnly
+
// force the cursor visible when gaining focus
onRunningChanged: cursor.opacity = 1
+ onTriggered: cursor.opacity = !cursor.opacity ? 1 : 0
}
}
+ onAccepted: {
+ root.accepted();
+ }
+ onEditingFinished: {
+ root.editingFinished();
+ }
+ onTextEdited: {
+ root.textEdited();
+ }
+
PlaceholderText {
id: placeholder
- x: control.leftPadding
- y: control.topPadding
- width: control.width - (control.leftPadding + control.rightPadding)
- height: control.height - (control.topPadding + control.bottomPadding)
-
- text: control.placeholderText
- font: control.font
color: control.placeholderTextColor
+ elide: Text.ElideRight
+ font: control.font
+ height: control.height - (control.topPadding + control.bottomPadding)
+ renderType: control.renderType
+ text: control.placeholderText
verticalAlignment: control.verticalAlignment
visible: !control.length && !control.preeditText && (!control.activeFocus || control.horizontalAlignment !== Qt.AlignHCenter)
- elide: Text.ElideRight
- renderType: control.renderType
- }
-
- background: Item {
- implicitWidth: 80
- implicitHeight: 36
- visible: false
- }
-
- onAccepted: {
- root.accepted()
- }
- onEditingFinished: {
- root.editingFinished()
- }
- onTextEdited: {
- root.textEdited()
+ width: control.width - (control.leftPadding + control.rightPadding)
+ x: control.leftPadding
+ y: control.topPadding
}
}
-
Proton.Button {
- colorScheme: root.colorScheme
id: eyeButton
-
Layout.fillHeight: true
-
- visible: root.echoMode === TextInput.Password
- icon.color: control.color
checkable: true
+ colorScheme: root.colorScheme
+ icon.color: control.color
icon.source: checked ? "../icons/ic-eye-slash.svg" : "../icons/ic-eye.svg"
+ visible: root.echoMode === TextInput.Password
}
}
}
-
RowLayout {
Layout.fillWidth: true
spacing: 0
ColorImage {
id: errorIcon
-
Layout.rightMargin: 4
-
- visible: root.error && (assistiveText.text.length > 0)
- source: "../icons/ic-exclamation-circle-filled.svg"
color: root.colorScheme.signal_danger
height: assistiveText.lineHeight
+ source: "../icons/ic-exclamation-circle-filled.svg"
sourceSize.height: assistiveText.lineHeight
+ visible: root.error && (assistiveText.text.length > 0)
}
-
Proton.Label {
- colorScheme: root.colorScheme
id: assistiveText
-
Layout.fillHeight: true
Layout.fillWidth: true
- wrapMode: Text.WordWrap
-
- text: root.error ? root.errorString : root.assistiveText
-
color: {
if (!root.enabled) {
- return root.colorScheme.text_disabled
+ return root.colorScheme.text_disabled;
}
-
if (root.error) {
- return root.colorScheme.signal_danger
+ return root.colorScheme.signal_danger;
}
-
- return root.colorScheme.text_weak
+ return root.colorScheme.text_weak;
}
-
+ colorScheme: root.colorScheme
+ text: root.error ? root.errorString : root.assistiveText
type: root.error ? Proton.Label.LabelType.Caption_semibold : Proton.Label.LabelType.Caption
+ wrapMode: Text.WordWrap
}
}
}
-
- property bool validateOnEditingFinished: true
- onEditingFinished: {
- if (!validateOnEditingFinished) {
- return
- }
- validate()
- }
-
- function validate() {
- if (validator === undefined) {
- return
- }
-
- var error = validator(text)
-
- if (error) {
- root.error = true
- root.errorString = error
- } else {
- root.error = false
- root.errorString = ""
- }
- }
-
- onTextChanged: {
- root.error = false
- root.errorString = ""
- }
}
diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/Proton/Toggle.qml b/internal/frontend/bridge-gui/bridge-gui/qml/Proton/Toggle.qml
index f2c52541..a9d3207b 100644
--- a/internal/frontend/bridge-gui/bridge-gui/qml/Proton/Toggle.qml
+++ b/internal/frontend/bridge-gui/bridge-gui/qml/Proton/Toggle.qml
@@ -1,20 +1,15 @@
// Copyright (c) 2023 Proton AG
-//
// This file is part of Proton Mail Bridge.
-//
// Proton Mail Bridge 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 3 of the License, or
// (at your option) any later version.
-//
// Proton Mail Bridge 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.
-//
// You should have received a copy of the GNU General Public License
// along with Proton Mail Bridge. If not, see .
-
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
@@ -22,92 +17,106 @@ import QtQuick.Controls.impl
Item {
id: root
- property var colorScheme
+
+ property bool _disabled: !enabled
property bool checked
+ property var colorScheme
property bool hovered
property bool loading
signal clicked
- property bool _disabled: !enabled
-
implicitHeight: children[0].implicitHeight
implicitWidth: children[0].implicitWidth
Rectangle {
id: indicator
- implicitWidth: 40
- implicitHeight: 24
-
- radius: width/2
color: {
- if (root.loading) return "transparent"
- if (root._disabled) return root.colorScheme.background_strong
- return root.colorScheme.background_norm
- }
- border {
- width: 1
- color: (root._disabled || root.loading) ? "transparent" : colorScheme.field_norm
+ if (root.loading)
+ return "transparent";
+ if (root._disabled)
+ return root.colorScheme.background_strong;
+ return root.colorScheme.background_norm;
}
+ implicitHeight: 24
+ implicitWidth: 40
+ radius: width / 2
+ border {
+ color: (root._disabled || root.loading) ? "transparent" : colorScheme.field_norm
+ width: 1
+ }
Rectangle {
- anchors.verticalCenter: indicator.verticalCenter
anchors.left: indicator.left
anchors.leftMargin: root.checked ? 16 : 0
- width: 24
- height: 24
- radius: width/2
+ anchors.verticalCenter: indicator.verticalCenter
color: {
- if (root.loading) return "transparent"
- if (root._disabled) return root.colorScheme.field_disabled
-
+ if (root.loading)
+ return "transparent";
+ if (root._disabled)
+ return root.colorScheme.field_disabled;
if (root.checked) {
- if (root.hovered) return root.colorScheme.interaction_norm_hover
- return root.colorScheme.interaction_norm
+ if (root.hovered)
+ return root.colorScheme.interaction_norm_hover;
+ return root.colorScheme.interaction_norm;
} else {
- if (root.hovered) return root.colorScheme.field_hover
- return root.colorScheme.field_norm
+ if (root.hovered)
+ return root.colorScheme.field_hover;
+ return root.colorScheme.field_norm;
}
}
+ height: 24
+ radius: width / 2
+ width: 24
ColorImage {
anchors.centerIn: parent
- source: "/qml/icons/ic-check.svg"
color: root.colorScheme.background_norm
height: root.colorScheme.body_font_size
+ source: "/qml/icons/ic-check.svg"
sourceSize.height: root.colorScheme.body_font_size
visible: root.checked
}
}
-
ColorImage {
id: loader
anchors.centerIn: parent
- source: "/qml/icons/Loader_16.svg"
color: root.colorScheme.text_norm
height: root.colorScheme.body_font_size
+ source: "/qml/icons/Loader_16.svg"
sourceSize.height: root.colorScheme.body_font_size
visible: root.loading
RotationAnimation {
- target: loader
- loops: Animation.Infinite
+ direction: RotationAnimation.Clockwise
duration: 1000
from: 0
- to: 360
- direction: RotationAnimation.Clockwise
+ loops: Animation.Infinite
running: root.loading
+ target: loader
+ to: 360
}
}
-
MouseArea {
anchors.fill: indicator
hoverEnabled: true
- onEntered: {root.hovered = true }
- onExited: {root.hovered = false }
- onClicked: { if (root.enabled) root.clicked();}
- onPressed: {root.hovered = true }
- onReleased: { root.hovered = containsMouse }
+
+ onClicked: {
+ if (root.enabled)
+ root.clicked();
+ }
+ onEntered: {
+ root.hovered = true;
+ }
+ onExited: {
+ root.hovered = false;
+ }
+ onPressed: {
+ root.hovered = true;
+ }
+ onReleased: {
+ root.hovered = containsMouse;
+ }
}
}
}
diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/SettingsItem.qml b/internal/frontend/bridge-gui/bridge-gui/qml/SettingsItem.qml
index 8a3aafaa..ea0e6fc0 100644
--- a/internal/frontend/bridge-gui/bridge-gui/qml/SettingsItem.qml
+++ b/internal/frontend/bridge-gui/bridge-gui/qml/SettingsItem.qml
@@ -1,51 +1,44 @@
// Copyright (c) 2023 Proton AG
-//
// This file is part of Proton Mail Bridge.
-//
// Proton Mail Bridge 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 3 of the License, or
// (at your option) any later version.
-//
// Proton Mail Bridge 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.
-//
// You should have received a copy of the GNU General Public License
// along with Proton Mail Bridge. If not, see .
-
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
-
import Proton
Item {
id: root
- property var colorScheme
-
- property string text: "Text"
- property string actionText: "Action"
- property string actionIcon: ""
- property string description: "Lorem ipsum dolor sit amet"
- property alias descriptionWrap: descriptionLabel.wrapMode
- property var type: SettingsItem.ActionType.Toggle
-
- property bool checked: true
- property bool loading: false
- property bool showSeparator: true
+ enum ActionType {
+ Toggle = 1,
+ Button,
+ PrimaryButton
+ }
property var _bottomMargin: 20
property var _lineWidth: 1
property var _toggleTopMargin: 6
+ property string actionIcon: ""
+ property string actionText: "Action"
+ property bool checked: true
+ property var colorScheme
+ property string description: "Lorem ipsum dolor sit amet"
+ property alias descriptionWrap: descriptionLabel.wrapMode
+ property bool loading: false
+ property bool showSeparator: true
+ property string text: "Text"
+ property var type: SettingsItem.ActionType.Toggle
signal clicked
- enum ActionType {
- Toggle = 1, Button = 2, PrimaryButton = 3
- }
-
implicitHeight: children[0].implicitHeight + children[0].anchors.topMargin + children[0].anchors.bottomMargin
implicitWidth: children[0].implicitWidth + children[0].anchors.leftMargin + children[0].anchors.rightMargin
@@ -54,10 +47,9 @@ Item {
spacing: 16
ColumnLayout {
+ Layout.bottomMargin: root._bottomMargin
Layout.fillHeight: true
Layout.fillWidth: true
- Layout.bottomMargin: root._bottomMargin
-
spacing: 4
Label {
@@ -66,51 +58,51 @@ Item {
text: root.text
type: Label.Body_semibold
}
-
Label {
id: descriptionLabel
Layout.fillHeight: true
Layout.fillWidth: true
-
Layout.preferredWidth: parent.width
-
- wrapMode: Text.WordWrap
+ color: root.colorScheme.text_weak
colorScheme: root.colorScheme
text: root.description
- color: root.colorScheme.text_weak
+ wrapMode: Text.WordWrap
}
}
-
Toggle {
+ id: toggle
Layout.alignment: Qt.AlignTop
Layout.topMargin: root._toggleTopMargin
- id: toggle
+ checked: root.checked
colorScheme: root.colorScheme
+ loading: root.loading
visible: root.type === SettingsItem.ActionType.Toggle
- checked: root.checked
- loading: root.loading
- onClicked: { if (!root.loading) root.clicked() }
+ onClicked: {
+ if (!root.loading)
+ root.clicked();
+ }
}
-
Button {
- Layout.alignment: Qt.AlignTop
-
id: button
+ Layout.alignment: Qt.AlignTop
colorScheme: root.colorScheme
- visible: root.type === SettingsItem.Button || root.type === SettingsItem.PrimaryButton
- text: root.actionText + (root.actionIcon != "" ? " " : "")
- loading: root.loading
icon.source: root.actionIcon
- onClicked: { if (!root.loading) root.clicked() }
+ loading: root.loading
secondary: root.type !== SettingsItem.PrimaryButton
+ text: root.actionText + (root.actionIcon !== "" ? " " : "")
+ visible: root.type === SettingsItem.Button || root.type === SettingsItem.PrimaryButton
+
+ onClicked: {
+ if (!root.loading)
+ root.clicked();
+ }
}
}
-
Rectangle {
+ anchors.bottom: root.bottom
anchors.left: root.left
anchors.right: root.right
- anchors.bottom: root.bottom
color: colorScheme.border_weak
height: root._lineWidth
visible: root.showSeparator
diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/SettingsView.qml b/internal/frontend/bridge-gui/bridge-gui/qml/SettingsView.qml
index e202b49c..e4619052 100644
--- a/internal/frontend/bridge-gui/bridge-gui/qml/SettingsView.qml
+++ b/internal/frontend/bridge-gui/bridge-gui/qml/SettingsView.qml
@@ -1,61 +1,53 @@
// Copyright (c) 2023 Proton AG
-//
// This file is part of Proton Mail Bridge.
-//
// Proton Mail Bridge 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 3 of the License, or
// (at your option) any later version.
-//
// Proton Mail Bridge 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.
-//
// You should have received a copy of the GNU General Public License
// along with Proton Mail Bridge. If not, see .
-
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import QtQuick.Controls.impl
-
import Proton
Item {
id: root
- property var colorScheme
- default property alias items: content.children
-
- signal back()
-
+ property int _bottomMargin: 32
property int _leftMargin: 64
property int _rightMargin: 64
- property int _topMargin: 32
- property int _bottomMargin: 32
property int _spacing: 20
+ property int _topMargin: 32
+ property var colorScheme
// fillHeight indicates whether the SettingsView should fill all available explicit height set
property bool fillHeight: false
+ default property alias items: content.children
+
+ signal back
ScrollView {
id: scrollView
+ anchors.fill: parent
clip: true
- anchors.fill: parent
Component.onCompleted: contentItem.boundsBehavior = Flickable.StopAtBounds // Disable the springy effect when scroll reaches top/bottom.
Item {
- // can't use parent here because parent is not ScrollView (Flickable inside contentItem inside ScrollView)
- width: scrollView.availableWidth
height: scrollView.availableHeight
-
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
+ // can't use parent here because parent is not ScrollView (Flickable inside contentItem inside ScrollView)
+ width: scrollView.availableWidth
ColumnLayout {
anchors.fill: parent
@@ -63,16 +55,13 @@ Item {
ColumnLayout {
id: content
- spacing: root._spacing
-
- Layout.fillWidth: true
-
- Layout.topMargin: root._topMargin
Layout.bottomMargin: root._bottomMargin
+ Layout.fillWidth: true
Layout.leftMargin: root._leftMargin
Layout.rightMargin: root._rightMargin
+ Layout.topMargin: root._topMargin
+ spacing: root._spacing
}
-
Item {
id: filler
Layout.fillHeight: true
@@ -81,19 +70,20 @@ Item {
}
}
}
-
Button {
id: backButton
- anchors {
- top: parent.top
- left: parent.left
- topMargin: root._topMargin
- leftMargin: (root._leftMargin-backButton.width) / 2
- }
colorScheme: root.colorScheme
- onClicked: root.back()
+ horizontalPadding: 8
icon.source: "/qml/icons/ic-arrow-left.svg"
secondary: true
- horizontalPadding: 8
+
+ onClicked: root.back()
+
+ anchors {
+ left: parent.left
+ leftMargin: (root._leftMargin - backButton.width) / 2
+ top: parent.top
+ topMargin: root._topMargin
+ }
}
}
diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/SetupGuide.qml b/internal/frontend/bridge-gui/bridge-gui/qml/SetupGuide.qml
index 972a7f70..4cdfe43f 100644
--- a/internal/frontend/bridge-gui/bridge-gui/qml/SetupGuide.qml
+++ b/internal/frontend/bridge-gui/bridge-gui/qml/SetupGuide.qml
@@ -1,110 +1,139 @@
// Copyright (c) 2023 Proton AG
-//
// This file is part of Proton Mail Bridge.
-//
// Proton Mail Bridge 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 3 of the License, or
// (at your option) any later version.
-//
// Proton Mail Bridge 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.
-//
// You should have received a copy of the GNU General Public License
// along with Proton Mail Bridge. If not, see .
-
-
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import QtQuick.Controls.impl
-
import Proton
Item {
- id:root
+ id: root
+ property string address
property ColorScheme colorScheme
property var user
- property string address
- signal dismissed()
- signal finished()
+ signal dismissed
+ signal finished
+
+ function reset() {
+ guidePages.currentIndex = 0;
+ clientList.currentIndex = -1;
+ actionList.currentIndex = -1;
+ }
+ function setupAction(actionID, clientID) {
+ if (user) {
+ user.setupGuideSeen = true;
+ }
+ switch (actionID) {
+ case -1:
+ root.dismissed();
+ break; // dismiss
+ case 0 // automatic
+ :
+ if (user) {
+ switch (clientID) {
+ case 0:
+ root.user.configureAppleMail(root.address);
+ Backend.notifyAutoconfigClicked("AppleMail");
+ break;
+ }
+ }
+ root.finished();
+ break;
+ case 1 // manual
+ :
+ let clientObj = clients.get(clientID);
+ if (clientObj !== undefined && clientObj.link !== "") {
+ Qt.openUrlExternally(clientObj.link);
+ Backend.notifyKBArticleClicked(clientObj.link);
+ } else {
+ console.log("unexpected client index", actionID, clientID);
+ }
+ root.finished();
+ break;
+ default:
+ console.log("unexpected client setup action", actionID, clientID);
+ }
+ }
implicitHeight: children[0].implicitHeight
implicitWidth: children[0].implicitWidth
-
ListModel {
id: clients
- property string name : "Apple Mail"
- property string iconSource : "/qml/icons/ic-apple-mail.svg"
+
property bool haveAutoSetup: true
+ property string iconSource: "/qml/icons/ic-apple-mail.svg"
property string link: "https://proton.me/support/protonmail-bridge-clients-apple-mail"
+ property string name: "Apple Mail"
- Component.onCompleted : {
- if (Backend.goos == "darwin") {
+ Component.onCompleted: {
+ if (Backend.goos === "darwin") {
append({
- "name" : "Apple Mail",
- "iconSource" : "/qml/icons/ic-apple-mail.svg",
- "haveAutoSetup" : true,
- "link" : "https://proton.me/support/protonmail-bridge-clients-apple-mail"
- })
+ "name": "Apple Mail",
+ "iconSource": "/qml/icons/ic-apple-mail.svg",
+ "haveAutoSetup": true,
+ "link": "https://proton.me/support/protonmail-bridge-clients-apple-mail"
+ });
append({
- "name" : "Microsoft Outlook",
- "iconSource" : "/qml/icons/ic-microsoft-outlook.svg",
- "haveAutoSetup" : false,
- "link" : "https://proton.me/support/protonmail-bridge-clients-macos-outlook-2019"
- })
+ "name": "Microsoft Outlook",
+ "iconSource": "/qml/icons/ic-microsoft-outlook.svg",
+ "haveAutoSetup": false,
+ "link": "https://proton.me/support/protonmail-bridge-clients-macos-outlook-2019"
+ });
}
- if (Backend.goos == "windows") {
+ if (Backend.goos === "windows") {
append({
- "name" : "Microsoft Outlook",
- "iconSource" : "/qml/icons/ic-microsoft-outlook.svg",
- "haveAutoSetup" : false,
- "link" : "https://proton.me/support/protonmail-bridge-clients-windows-outlook-2019"
- })
+ "name": "Microsoft Outlook",
+ "iconSource": "/qml/icons/ic-microsoft-outlook.svg",
+ "haveAutoSetup": false,
+ "link": "https://proton.me/support/protonmail-bridge-clients-windows-outlook-2019"
+ });
}
-
append({
- "name" : "Mozilla Thunderbird",
- "iconSource" : "/qml/icons/ic-mozilla-thunderbird.svg",
- "haveAutoSetup" : false,
- "link" : "https://proton.me/support/protonmail-bridge-clients-windows-thunderbird"
- })
-
+ "name": "Mozilla Thunderbird",
+ "iconSource": "/qml/icons/ic-mozilla-thunderbird.svg",
+ "haveAutoSetup": false,
+ "link": "https://proton.me/support/protonmail-bridge-clients-windows-thunderbird"
+ });
append({
- "name" : "Other",
- "iconSource" : "/qml/icons/ic-other-mail-clients.svg",
- "haveAutoSetup" : false,
- "link" : "https://proton.me/support/protonmail-bridge-configure-client"
- })
-
+ "name": "Other",
+ "iconSource": "/qml/icons/ic-other-mail-clients.svg",
+ "haveAutoSetup": false,
+ "link": "https://proton.me/support/protonmail-bridge-configure-client"
+ });
}
}
-
Rectangle {
anchors.fill: root
color: root.colorScheme.background_norm
}
-
StackLayout {
id: guidePages
+ anchors.bottomMargin: 70
anchors.fill: parent
anchors.leftMargin: 80
anchors.rightMargin: 80
anchors.topMargin: 30
- anchors.bottomMargin: 70
-
- ColumnLayout { // 0: Client selection
+ ColumnLayout {
+ // 0: Client selection
id: clientView
- Layout.fillHeight: true
property int columnWidth: 268
+ Layout.fillHeight: true
spacing: 8
Label {
@@ -112,16 +141,14 @@ Item {
text: qsTr("Setting up email client")
type: Label.LabelType.Heading
}
-
Label {
+ color: root.colorScheme.text_weak
colorScheme: root.colorScheme
text: address
- color: root.colorScheme.text_weak
type: Label.LabelType.Lead
}
-
RowLayout {
- Layout.topMargin: 32-clientView.spacing
+ Layout.topMargin: 32 - clientView.spacing
spacing: 24
ColumnLayout {
@@ -134,185 +161,133 @@ Item {
text: qsTr("Choose an email client")
type: Label.LabelType.Body_semibold
}
-
ListView {
id: clientList
Layout.fillHeight: true
+ model: clients
width: clientView.columnWidth
- model: clients
-
- highlight: Rectangle {
- color: root.colorScheme.interaction_default_active
- radius: ProtonStyle.context_item_radius
- }
-
delegate: Item {
- implicitWidth: clientRow.width
implicitHeight: clientRow.height
+ implicitWidth: clientRow.width
ColumnLayout {
id: clientRow
width: clientList.width
RowLayout {
- Layout.topMargin: 12
Layout.bottomMargin: 12
Layout.leftMargin: 16
Layout.rightMargin: 16
+ Layout.topMargin: 12
ColorImage {
- source: model.iconSource
height: 36
+ source: model.iconSource
sourceSize.height: 36
}
-
Label {
- colorScheme: root.colorScheme
Layout.leftMargin: 12
+ colorScheme: root.colorScheme
text: model.name
type: Label.LabelType.Body
}
}
-
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 1
color: root.colorScheme.border_weak
}
}
-
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
+
onClicked: {
- clientList.currentIndex = index
+ clientList.currentIndex = index;
if (!model.haveAutoSetup) {
- root.setupAction(1,index)
+ root.setupAction(1, index);
}
}
}
}
+ highlight: Rectangle {
+ color: root.colorScheme.interaction_default_active
+ radius: ProtonStyle.context_item_radius
+ }
}
}
-
ColumnLayout {
id: actionColumn
- visible: clientList.currentIndex >= 0 && clients.get(clientList.currentIndex).haveAutoSetup
Layout.alignment: Qt.AlignTop
+ visible: clientList.currentIndex >= 0 && clients.get(clientList.currentIndex).haveAutoSetup
Label {
colorScheme: root.colorScheme
text: qsTr("Choose configuration mode")
type: Label.LabelType.Body_semibold
}
-
ListView {
id: actionList
Layout.fillHeight: true
+ model: [qsTr("Configure automatically"), qsTr("Configure manually")]
width: clientView.columnWidth
- model: [
- qsTr("Configure automatically"),
- qsTr("Configure manually"),
- ]
-
- highlight: Rectangle {
- color: root.colorScheme.interaction_default_active
- radius: ProtonStyle.context_item_radius
- }
-
delegate: Item {
- implicitWidth: children[0].width
implicitHeight: children[0].height
+ implicitWidth: children[0].width
ColumnLayout {
width: actionList.width
Label {
- Layout.topMargin: 20
Layout.bottomMargin: 20
Layout.leftMargin: 16
Layout.rightMargin: 16
+ Layout.topMargin: 20
colorScheme: root.colorScheme
text: modelData
type: Label.LabelType.Body
}
-
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 1
color: root.colorScheme.border_weak
}
}
-
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
+
onClicked: {
- actionList.currentIndex = index
- root.setupAction(index,clientList.currentIndex)
+ actionList.currentIndex = index;
+ root.setupAction(index, clientList.currentIndex);
}
}
}
+ highlight: Rectangle {
+ color: root.colorScheme.interaction_default_active
+ radius: ProtonStyle.context_item_radius
+ }
}
}
}
-
- Item { Layout.fillHeight: true }
-
+ Item {
+ Layout.fillHeight: true
+ }
Button {
colorScheme: root.colorScheme
- text: qsTr("Set up later")
flat: true
+ text: qsTr("Set up later")
onClicked: {
- root.setupAction(-1,-1)
+ root.setupAction(-1, -1);
if (user) {
- user.setupGuideSeen = true
+ user.setupGuideSeen = true;
}
- root.dismissed()
+ root.dismissed();
}
}
}
}
-
- function setupAction(actionID,clientID){
- if (user) {
- user.setupGuideSeen = true
- }
-
- switch (actionID) {
- case -1: root.dismissed(); break; // dismiss
- case 0: // automatic
- if (user) {
- switch (clientID) {
- case 0:
- root.user.configureAppleMail(root.address)
- Backend.notifyAutoconfigClicked("AppleMail");
- break;
- }
- }
- root.finished()
- break;
- case 1: // manual
- var clientObj = clients.get(clientID)
- if (clientObj != undefined && clientObj.link != "" ) {
- Qt.openUrlExternally(clientObj.link)
- Backend.notifyKBArticleClicked(clientObj.link);
- } else {
- console.log("unexpected client index", actionID, clientID)
- }
- root.finished();
- break;
- default:
- console.log("unexpected client setup action", actionID, clientID)
- }
- }
-
- function reset(){
- guidePages.currentIndex = 0
- clientList.currentIndex = -1
- actionList.currentIndex = -1
- }
}
diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/SignIn.qml b/internal/frontend/bridge-gui/bridge-gui/qml/SignIn.qml
index 221affab..35278df0 100644
--- a/internal/frontend/bridge-gui/bridge-gui/qml/SignIn.qml
+++ b/internal/frontend/bridge-gui/bridge-gui/qml/SignIn.qml
@@ -1,478 +1,413 @@
// Copyright (c) 2023 Proton AG
-//
// This file is part of Proton Mail Bridge.
-//
// Proton Mail Bridge 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 3 of the License, or
// (at your option) any later version.
-//
// Proton Mail Bridge 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.
-//
// You should have received a copy of the GNU General Public License
// along with Proton Mail Bridge. If not, see .
-
import QtQml
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import QtQuick.Controls.impl
-
import Proton
FocusScope {
id: root
+
property ColorScheme colorScheme
-
- function reset() {
- stackLayout.currentIndex = 0
- loginNormalLayout.reset()
- login2FALayout.reset()
- login2PasswordLayout.reset()
-
- }
+ property alias currentIndex: stackLayout.currentIndex
+ property alias username: usernameTextField.text
function abort() {
- root.reset()
- Backend.loginAbort(usernameTextField.text)
+ root.reset();
+ Backend.loginAbort(usernameTextField.text);
+ }
+ function reset() {
+ stackLayout.currentIndex = 0;
+ loginNormalLayout.reset();
+ login2FALayout.reset();
+ login2PasswordLayout.reset();
}
implicitHeight: children[0].implicitHeight
implicitWidth: children[0].implicitWidth
-
- property alias username: usernameTextField.text
state: "Page 1"
- property alias currentIndex: stackLayout.currentIndex
+ states: [
+ State {
+ name: "Page 1"
+
+ PropertyChanges {
+ currentIndex: 0
+ target: stackLayout
+ }
+ },
+ State {
+ name: "Page 2"
+
+ PropertyChanges {
+ currentIndex: 1
+ target: stackLayout
+ }
+ },
+ State {
+ name: "Page 3"
+
+ PropertyChanges {
+ currentIndex: 2
+ target: stackLayout
+ }
+ }
+ ]
StackLayout {
id: stackLayout
+ function loginFailed() {
+ signInButton.loading = false;
+ usernameTextField.enabled = true;
+ usernameTextField.error = true;
+ passwordTextField.enabled = true;
+ passwordTextField.error = true;
+ }
+
anchors.fill: parent
- function loginFailed() {
- signInButton.loading = false
-
- usernameTextField.enabled = true
- usernameTextField.error = true
-
- passwordTextField.enabled = true
- passwordTextField.error = true
- }
-
Connections {
- target: Backend
-
- function onLoginUsernamePasswordError(errorMsg) {
- console.assert(stackLayout.currentIndex == 0, "Unexpected loginUsernamePasswordError")
- console.assert(signInButton.loading == true, "Unexpected loginUsernamePasswordError")
-
- stackLayout.loginFailed()
- if (errorMsg!="") errorLabel.text = errorMsg
- else errorLabel.text = qsTr("Incorrect login credentials")
+ function onLogin2FAError(_) {
+ console.assert(stackLayout.currentIndex === 1, "Unexpected login2FAError");
+ twoFAButton.loading = false;
+ twoFactorPasswordTextField.enabled = true;
+ twoFactorPasswordTextField.error = true;
+ twoFactorPasswordTextField.errorString = qsTr("Your code is incorrect");
+ twoFactorPasswordTextField.focus = true;
}
-
- function onLoginFreeUserError() {
- console.assert(stackLayout.currentIndex == 0, "Unexpected loginFreeUserError")
- stackLayout.loginFailed()
+ function onLogin2FAErrorAbort(_) {
+ console.assert(stackLayout.currentIndex === 1, "Unexpected login2FAErrorAbort");
+ root.reset();
+ errorLabel.text = qsTr("Incorrect login credentials. Please try again.");
}
-
- function onLoginConnectionError(errorMsg) {
- if (stackLayout.currentIndex == 0 ) {
- stackLayout.loginFailed()
+ function onLogin2FARequested(username) {
+ console.assert(stackLayout.currentIndex === 0, "Unexpected login2FARequested");
+ twoFactorUsernameLabel.text = username;
+ stackLayout.currentIndex = 1;
+ twoFactorPasswordTextField.focus = true;
+ }
+ function onLogin2PasswordError(_) {
+ console.assert(stackLayout.currentIndex === 2, "Unexpected login2PasswordError");
+ secondPasswordButton.loading = false;
+ secondPasswordTextField.enabled = true;
+ secondPasswordTextField.error = true;
+ secondPasswordTextField.errorString = qsTr("Your mailbox password is incorrect");
+ secondPasswordTextField.focus = true;
+ }
+ function onLogin2PasswordErrorAbort(_) {
+ console.assert(stackLayout.currentIndex === 2, "Unexpected login2PasswordErrorAbort");
+ root.reset();
+ errorLabel.text = qsTr("Incorrect login credentials. Please try again.");
+ }
+ function onLogin2PasswordRequested() {
+ console.assert(stackLayout.currentIndex === 0 || stackLayout.currentIndex === 1, "Unexpected login2PasswordRequested");
+ stackLayout.currentIndex = 2;
+ secondPasswordTextField.focus = true;
+ }
+ function onLoginAlreadyLoggedIn(_) {
+ stackLayout.currentIndex = 0;
+ root.reset();
+ }
+ function onLoginConnectionError(_) {
+ if (stackLayout.currentIndex === 0) {
+ stackLayout.loginFailed();
}
}
-
- function onLogin2FARequested(username) {
- console.assert(stackLayout.currentIndex == 0, "Unexpected login2FARequested")
- twoFactorUsernameLabel.text = username
- stackLayout.currentIndex = 1
- twoFactorPasswordTextField.focus = true
+ function onLoginFinished(_) {
+ stackLayout.currentIndex = 0;
+ root.reset();
+ }
+ function onLoginFreeUserError() {
+ console.assert(stackLayout.currentIndex === 0, "Unexpected loginFreeUserError");
+ stackLayout.loginFailed();
+ }
+ function onLoginUsernamePasswordError(errorMsg) {
+ console.assert(stackLayout.currentIndex === 0, "Unexpected loginUsernamePasswordError");
+ stackLayout.loginFailed();
+ if (errorMsg !== "")
+ errorLabel.text = errorMsg;
+ else
+ errorLabel.text = qsTr("Incorrect login credentials");
}
- function onLogin2FAError(errorMsg) {
- console.assert(stackLayout.currentIndex == 1, "Unexpected login2FAError")
-
- twoFAButton.loading = false
-
- twoFactorPasswordTextField.enabled = true
- twoFactorPasswordTextField.error = true
- twoFactorPasswordTextField.errorString = qsTr("Your code is incorrect")
- twoFactorPasswordTextField.focus = true
- }
-
- function onLogin2FAErrorAbort(errorMsg) {
- console.assert(stackLayout.currentIndex == 1, "Unexpected login2FAErrorAbort")
- root.reset()
- errorLabel.text = qsTr("Incorrect login credentials. Please try again.")
- }
-
- function onLogin2PasswordRequested() {
- console.assert(stackLayout.currentIndex == 0 || stackLayout.currentIndex == 1, "Unexpected login2PasswordRequested")
- stackLayout.currentIndex = 2
- secondPasswordTextField.focus = true
- }
- function onLogin2PasswordError(errorMsg) {
- console.assert(stackLayout.currentIndex == 2, "Unexpected login2PasswordError")
-
- secondPasswordButton.loading = false
-
- secondPasswordTextField.enabled = true
- secondPasswordTextField.error = true
- secondPasswordTextField.errorString = qsTr("Your mailbox password is incorrect")
- secondPasswordTextField.focus = true
- }
- function onLogin2PasswordErrorAbort(errorMsg) {
- console.assert(stackLayout.currentIndex == 2, "Unexpected login2PasswordErrorAbort")
- root.reset()
- errorLabel.text = qsTr("Incorrect login credentials. Please try again.")
- }
-
- function onLoginFinished(index) {
- stackLayout.currentIndex = 0
- root.reset()
- }
-
- function onLoginAlreadyLoggedIn(index) {
- stackLayout.currentIndex = 0
- root.reset()
- }
+ target: Backend
}
-
ColumnLayout {
id: loginNormalLayout
-
function reset() {
- signInButton.loading = false
-
- errorLabel.text = ""
-
- usernameTextField.enabled = true
- usernameTextField.error = false
- usernameTextField.errorString = ""
- usernameTextField.focus = true
-
- passwordTextField.enabled = true
- passwordTextField.error = false
- passwordTextField.errorString = ""
- passwordTextField.text = ""
+ signInButton.loading = false;
+ errorLabel.text = "";
+ usernameTextField.enabled = true;
+ usernameTextField.error = false;
+ usernameTextField.errorString = "";
+ usernameTextField.focus = true;
+ passwordTextField.enabled = true;
+ passwordTextField.error = false;
+ passwordTextField.errorString = "";
+ passwordTextField.text = "";
}
spacing: 0
Label {
- colorScheme: root.colorScheme
- text: qsTr("Sign in")
Layout.alignment: Qt.AlignHCenter
Layout.topMargin: 16
+ colorScheme: root.colorScheme
+ text: qsTr("Sign in")
type: Label.LabelType.Title
}
-
Label {
- colorScheme: root.colorScheme
id: subTitle
- text: qsTr("Enter your Proton Account details.")
Layout.alignment: Qt.AlignHCenter
Layout.topMargin: 8
color: root.colorScheme.text_weak
+ colorScheme: root.colorScheme
+ text: qsTr("Enter your Proton Account details.")
type: Label.LabelType.Body
}
-
RowLayout {
Layout.fillWidth: true
Layout.topMargin: 36
-
spacing: 0
visible: errorLabel.text.length > 0
ColorImage {
color: root.colorScheme.signal_danger
- source: "/qml/icons/ic-exclamation-circle-filled.svg"
height: errorLabel.lineHeight
+ source: "/qml/icons/ic-exclamation-circle-filled.svg"
sourceSize.height: errorLabel.lineHeight
}
-
Label {
- colorScheme: root.colorScheme
id: errorLabel
- wrapMode: Text.WordWrap
- Layout.fillWidth: true;
+ Layout.fillWidth: true
Layout.leftMargin: 4
color: root.colorScheme.signal_danger
-
+ colorScheme: root.colorScheme
type: root.error ? Label.LabelType.Caption_semibold : Label.LabelType.Caption
+ wrapMode: Text.WordWrap
}
}
-
TextField {
- colorScheme: root.colorScheme
id: usernameTextField
- label: qsTr("Email or username")
- focus: true
Layout.fillWidth: true
Layout.topMargin: 24
+ colorScheme: root.colorScheme
+ focus: true
+ label: qsTr("Email or username")
validateOnEditingFinished: false
-
- onTextChanged: {
- // remove "invalid username / password error"
- if (error || errorLabel.text.length > 0) {
- errorLabel.text = ""
- usernameTextField.error = false
- passwordTextField.error = false
- }
- }
-
- validator: function(str) {
+ validator: function (str) {
if (str.length === 0) {
- return qsTr("Enter email or username")
+ return qsTr("Enter email or username");
}
- return
}
onAccepted: passwordTextField.forceActiveFocus()
- }
-
- TextField {
- colorScheme: root.colorScheme
- id: passwordTextField
- label: qsTr("Password")
- Layout.fillWidth: true
- Layout.topMargin: 8
- echoMode: TextInput.Password
- validateOnEditingFinished: false
-
onTextChanged: {
// remove "invalid username / password error"
if (error || errorLabel.text.length > 0) {
- errorLabel.text = ""
- usernameTextField.error = false
- passwordTextField.error = false
+ errorLabel.text = "";
+ usernameTextField.error = false;
+ passwordTextField.error = false;
}
}
-
- validator: function(str) {
+ }
+ TextField {
+ id: passwordTextField
+ Layout.fillWidth: true
+ Layout.topMargin: 8
+ colorScheme: root.colorScheme
+ echoMode: TextInput.Password
+ label: qsTr("Password")
+ validateOnEditingFinished: false
+ validator: function (str) {
if (str.length === 0) {
- return qsTr("Enter password")
+ return qsTr("Enter password");
}
- return
}
onAccepted: signInButton.checkAndSignIn()
- }
-
- Button {
- colorScheme: root.colorScheme
- id: signInButton
- text: loading ? qsTr("Signing in") : qsTr("Sign in")
- enabled: !loading
- Layout.fillWidth: true
- Layout.topMargin: 24
-
-
- onClicked: checkAndSignIn()
-
- function checkAndSignIn() {
- usernameTextField.validate()
- passwordTextField.validate()
-
- if (usernameTextField.error || passwordTextField.error) {
- return
+ onTextChanged: {
+ // remove "invalid username / password error"
+ if (error || errorLabel.text.length > 0) {
+ errorLabel.text = "";
+ usernameTextField.error = false;
+ passwordTextField.error = false;
}
-
- usernameTextField.enabled = false
- passwordTextField.enabled = false
-
- loading = true
-
- Backend.login(usernameTextField.text, Qt.btoa(passwordTextField.text))
}
}
+ Button {
+ id: signInButton
+ function checkAndSignIn() {
+ usernameTextField.validate();
+ passwordTextField.validate();
+ if (usernameTextField.error || passwordTextField.error) {
+ return;
+ }
+ usernameTextField.enabled = false;
+ passwordTextField.enabled = false;
+ loading = true;
+ Backend.login(usernameTextField.text, Qt.btoa(passwordTextField.text));
+ }
- Label {
+ Layout.fillWidth: true
+ Layout.topMargin: 24
colorScheme: root.colorScheme
- textFormat: Text.StyledText
- text: link("https://proton.me/mail/pricing", qsTr("Create or upgrade your account"))
+ enabled: !loading
+ text: loading ? qsTr("Signing in") : qsTr("Sign in")
+
+ onClicked: {
+ checkAndSignIn();
+ }
+ }
+ Label {
Layout.alignment: Qt.AlignHCenter
Layout.topMargin: 24
+ colorScheme: root.colorScheme
+ text: link("https://proton.me/mail/pricing", qsTr("Create or upgrade your account"))
+ textFormat: Text.StyledText
type: Label.LabelType.Body
onLinkActivated: {
- Qt.openUrlExternally(link)
+ Qt.openUrlExternally(link);
}
}
}
-
ColumnLayout {
id: login2FALayout
-
function reset() {
- twoFAButton.loading = false
-
- twoFactorPasswordTextField.enabled = true
- twoFactorPasswordTextField.error = false
- twoFactorPasswordTextField.errorString = ""
- twoFactorPasswordTextField.text = ""
+ twoFAButton.loading = false;
+ twoFactorPasswordTextField.enabled = true;
+ twoFactorPasswordTextField.error = false;
+ twoFactorPasswordTextField.errorString = "";
+ twoFactorPasswordTextField.text = "";
}
spacing: 0
Label {
+ Layout.alignment: Qt.AlignCenter
+ Layout.topMargin: 16
colorScheme: root.colorScheme
text: qsTr("Two-factor authentication")
- Layout.topMargin: 16
- Layout.alignment: Qt.AlignCenter
type: Label.LabelType.Heading
}
-
Label {
- colorScheme: root.colorScheme
id: twoFactorUsernameLabel
-
Layout.alignment: Qt.AlignCenter
Layout.topMargin: 8
- type: Label.LabelType.Lead
color: root.colorScheme.text_weak
- }
-
- TextField {
colorScheme: root.colorScheme
+ type: Label.LabelType.Lead
+ }
+ TextField {
id: twoFactorPasswordTextField
- label: qsTr("Two-factor code")
- assistiveText: qsTr("Enter the 6-digit code")
- validateOnEditingFinished: false
Layout.fillWidth: true
Layout.topMargin: 32
-
- validator: function(str) {
+ assistiveText: qsTr("Enter the 6-digit code")
+ colorScheme: root.colorScheme
+ label: qsTr("Two-factor code")
+ validateOnEditingFinished: false
+ validator: function (str) {
if (str.length === 0) {
- return qsTr("Enter the 6-digit code")
- }
- }
-
- onTextChanged: {
- if (text.length >= 6) {
- twoFAButton.onClicked()
+ return qsTr("Enter the 6-digit code");
}
}
onAccepted: {
- twoFAButton.onClicked()
+ twoFAButton.onClicked();
+ }
+ onTextChanged: {
+ if (text.length >= 6) {
+ twoFAButton.onClicked();
+ }
}
-
}
-
Button {
- colorScheme: root.colorScheme
id: twoFAButton
- text: loading ? qsTr("Authenticating") : qsTr("Authenticate")
- enabled: !loading
Layout.fillWidth: true
Layout.topMargin: 24
+ colorScheme: root.colorScheme
+ enabled: !loading
+ text: loading ? qsTr("Authenticating") : qsTr("Authenticate")
onClicked: {
- twoFactorPasswordTextField.validate()
-
+ twoFactorPasswordTextField.validate();
if (twoFactorPasswordTextField.error) {
- return
+ return;
}
-
- twoFactorPasswordTextField.enabled = false
- loading = true
- Backend.login2FA(usernameTextField.text, Qt.btoa(twoFactorPasswordTextField.text))
+ twoFactorPasswordTextField.enabled = false;
+ loading = true;
+ Backend.login2FA(usernameTextField.text, Qt.btoa(twoFactorPasswordTextField.text));
}
}
}
-
ColumnLayout {
id: login2PasswordLayout
-
function reset() {
- secondPasswordButton.loading = false
-
- secondPasswordTextField.enabled = true
- secondPasswordTextField.error = false
- secondPasswordTextField.errorString = ""
- secondPasswordTextField.text = ""
+ secondPasswordButton.loading = false;
+ secondPasswordTextField.enabled = true;
+ secondPasswordTextField.error = false;
+ secondPasswordTextField.errorString = "";
+ secondPasswordTextField.text = "";
}
spacing: 0
Label {
+ Layout.alignment: Qt.AlignCenter
+ Layout.topMargin: 16
colorScheme: root.colorScheme
text: qsTr("Unlock your mailbox")
- Layout.topMargin: 16
- Layout.alignment: Qt.AlignCenter
type: Label.LabelType.Heading
}
-
TextField {
- colorScheme: root.colorScheme
id: secondPasswordTextField
- label: qsTr("Mailbox password")
Layout.fillWidth: true
Layout.topMargin: 8 + implicitHeight + 24 + subTitle.implicitHeight
+ colorScheme: root.colorScheme
echoMode: TextInput.Password
+ label: qsTr("Mailbox password")
validateOnEditingFinished: false
-
- validator: function(str) {
+ validator: function (str) {
if (str.length === 0) {
- return qsTr("Enter password")
+ return qsTr("Enter password");
}
- return
}
onAccepted: {
- secondPasswordButton.onClicked()
+ secondPasswordButton.onClicked();
}
}
-
Button {
- colorScheme: root.colorScheme
id: secondPasswordButton
- text: loading ? qsTr("Unlocking") : qsTr("Unlock")
- enabled: !loading
-
Layout.fillWidth: true
Layout.topMargin: 24
+ colorScheme: root.colorScheme
+ enabled: !loading
+ text: loading ? qsTr("Unlocking") : qsTr("Unlock")
onClicked: {
- secondPasswordTextField.validate()
-
+ secondPasswordTextField.validate();
if (secondPasswordTextField.error) {
- return
+ return;
}
-
- secondPasswordTextField.enabled = false
- loading = true
- Backend.login2Password(usernameTextField.text, Qt.btoa(secondPasswordTextField.text))
+ secondPasswordTextField.enabled = false;
+ loading = true;
+ Backend.login2Password(usernameTextField.text, Qt.btoa(secondPasswordTextField.text));
}
}
}
}
-
- states: [
- State {
- name: "Page 1"
- PropertyChanges {
- target: stackLayout
- currentIndex: 0
- }
- },
- State {
- name: "Page 2"
- PropertyChanges {
- target: stackLayout
- currentIndex: 1
- }
- },
- State {
- name: "Page 3"
- PropertyChanges {
- target: stackLayout
- currentIndex: 2
- }
- }
- ]
}
diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/SplashScreen.qml b/internal/frontend/bridge-gui/bridge-gui/qml/SplashScreen.qml
index 9490b09c..753f5d25 100644
--- a/internal/frontend/bridge-gui/bridge-gui/qml/SplashScreen.qml
+++ b/internal/frontend/bridge-gui/bridge-gui/qml/SplashScreen.qml
@@ -1,194 +1,169 @@
// Copyright (c) 2023 Proton AG
-//
// This file is part of Proton Mail Bridge.
-//
// Proton Mail Bridge 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 3 of the License, or
// (at your option) any later version.
-//
// Proton Mail Bridge 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.
-//
// You should have received a copy of the GNU General Public License
// along with Proton Mail Bridge. If not, see .
-
import QtQml
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import QtQuick.Controls.impl
-
import Proton
Dialog {
id: root
-
- shouldShow: Backend.showSplashScreen
+ leftPadding: 0
modal: true
-
- topPadding : 0
- leftPadding : 0
- rightPadding : 0
+ rightPadding: 0
+ shouldShow: Backend.showSplashScreen
+ topPadding: 0
ColumnLayout {
spacing: 20
Image {
Layout.alignment: Qt.AlignHCenter
-
- sourceSize.width: 384
- sourceSize.height: 144
-
- Layout.preferredWidth: 384
Layout.preferredHeight: 144
-
+ Layout.preferredWidth: 384
source: "./icons/img-splash.png"
+ sourceSize.height: 144
+ sourceSize.width: 384
}
-
Label {
- colorScheme: root.colorScheme;
-
- Layout.alignment: Qt.AlignHCenter;
+ Layout.alignment: Qt.AlignHCenter
Layout.leftMargin: 24
- Layout.rightMargin: 24
Layout.preferredWidth: 336
-
- type: Label.Title
+ Layout.rightMargin: 24
+ colorScheme: root.colorScheme
horizontalAlignment: Text.AlignHCenter
text: qsTr("What's new in Bridge")
+ type: Label.Title
}
-
RowLayout {
width: root.width
Item {
Layout.fillHeight: true
- width: 24
Layout.leftMargin: 32
Layout.rightMargin: 16
+ width: 24
+
Image {
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
- sourceSize.width: 24
- sourceSize.height: 24
source: "./icons/ic-splash-check.svg"
+ sourceSize.height: 24
+ sourceSize.width: 24
}
}
-
Label {
- colorScheme: root.colorScheme
-
+ Layout.alignment: Qt.AlignHCenter
Layout.fillWidth: true
- Layout.alignment: Qt.AlignHCenter;
- Layout.preferredWidth: 264
Layout.leftMargin: 0
+ Layout.preferredWidth: 264
Layout.rightMargin: 24
- wrapMode: Text.WordWrap
-
- type: Label.Body
+ colorScheme: root.colorScheme
horizontalAlignment: Text.AlignLeft
- textFormat: Text.StyledText
text: qsTr("New IMAP engine
For improved stability and performance.")
+ textFormat: Text.StyledText
+ type: Label.Body
+ wrapMode: Text.WordWrap
}
}
-
RowLayout {
width: root.width
Item {
Layout.fillHeight: true
- width: 24
Layout.leftMargin: 32
Layout.rightMargin: 16
+ width: 24
+
Image {
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
- sourceSize.width: 24
- sourceSize.height: 24
source: "./icons/ic-splash-check.svg"
+ sourceSize.height: 24
+ sourceSize.width: 24
}
}
-
Label {
- colorScheme: root.colorScheme
-
+ Layout.alignment: Qt.AlignHCenter
Layout.fillWidth: true
- Layout.alignment: Qt.AlignHCenter;
- Layout.preferredWidth: 264
Layout.leftMargin: 0
+ Layout.preferredWidth: 264
Layout.rightMargin: 24
- wrapMode: Text.WordWrap
-
- type: Label.Body
+ colorScheme: root.colorScheme
horizontalAlignment: Text.AlignLeft
- textFormat: Text.StyledText
text: qsTr("Faster than ever
Up to 10x faster syncing and receiving.")
+ textFormat: Text.StyledText
+ type: Label.Body
+ wrapMode: Text.WordWrap
}
}
-
RowLayout {
width: root.width
Item {
Layout.fillHeight: true
- width: 24
Layout.leftMargin: 32
Layout.rightMargin: 16
+ width: 24
+
Image {
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
- sourceSize.width: 24
- sourceSize.height: 24
source: "./icons/ic-splash-check.svg"
+ sourceSize.height: 24
+ sourceSize.width: 24
}
}
-
-
Label {
- colorScheme: root.colorScheme
-
+ Layout.alignment: Qt.AlignHCenter
Layout.fillWidth: true
- Layout.alignment: Qt.AlignHCenter;
- Layout.preferredWidth: 264
Layout.leftMargin: 0
+ Layout.preferredWidth: 264
Layout.rightMargin: 24
- wrapMode: Text.WordWrap
-
- type: Label.Body
+ colorScheme: root.colorScheme
horizontalAlignment: Text.AlignLeft
- textFormat: Text.StyledText
text: qsTr("Extra security
New, encrypted local database and keychain improvements.")
+ textFormat: Text.StyledText
+ type: Label.Body
+ wrapMode: Text.WordWrap
}
}
-
Button {
Layout.fillWidth: true
Layout.leftMargin: 24
Layout.rightMargin: 24
colorScheme: root.colorScheme
text: "Got it"
+
onClicked: Backend.showSplashScreen = false
}
-
Label {
- colorScheme: root.colorScheme
+ Layout.alignment: Qt.AlignHCenter
Layout.fillWidth: true
- Layout.alignment: Qt.AlignHCenter;
- Layout.preferredWidth: 336
Layout.leftMargin: 24
+ Layout.preferredWidth: 336
Layout.rightMargin: 24
+ colorScheme: root.colorScheme
+ horizontalAlignment: Text.AlignHCenter
+ text: qsTr("Note that your client will redownload all the emails.
") + link("https://proton.me/blog/new-proton-mail-bridge", qsTr("Learn more about new Bridge."))
+ textFormat: Text.StyledText
+ type: Label.Body
wrapMode: Text.WordWrap
- type: Label.Body
- horizontalAlignment: Text.AlignHCenter
- textFormat: Text.StyledText
- text: qsTr("Note that your client will redownload all the emails.
") + link("https://proton.me/blog/new-proton-mail-bridge", qsTr("Learn more about new Bridge."))
-
- onLinkActivated: function(link) { Qt.openUrlExternally(link) }
+ onLinkActivated: function (link) {
+ Qt.openUrlExternally(link);
+ }
}
}
}
-
diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/Status.qml b/internal/frontend/bridge-gui/bridge-gui/qml/Status.qml
index dfe53d8d..b64c8027 100644
--- a/internal/frontend/bridge-gui/bridge-gui/qml/Status.qml
+++ b/internal/frontend/bridge-gui/bridge-gui/qml/Status.qml
@@ -1,111 +1,93 @@
-
// Copyright (c) 2023 Proton AG
-//
// This file is part of Proton Mail Bridge.
-//
// Proton Mail Bridge 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 3 of the License, or
// (at your option) any later version.
-//
// Proton Mail Bridge 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.
-//
// You should have received a copy of the GNU General Public License
// along with Proton Mail Bridge. If not, see .
-
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import QtQuick.Controls.impl
-
import Proton
import Notifications
Item {
id: root
- property var notifications
- property ColorScheme colorScheme
-
- property int notificationWhitelist: NotificationFilter.FilterConsts.All
- property int notificationBlacklist: NotificationFilter.FilterConsts.None
-
readonly property Notification activeNotification: notificationFilter.topmost
+ property ColorScheme colorScheme
+ property int notificationBlacklist: NotificationFilter.FilterConsts.None
+ property int notificationWhitelist: NotificationFilter.FilterConsts.All
+ property var notifications
implicitHeight: children[0].implicitHeight + children[0].anchors.topMargin + children[0].anchors.bottomMargin
implicitWidth: children[0].implicitWidth + children[0].anchors.leftMargin + children[0].anchors.rightMargin
NotificationFilter {
id: notificationFilter
-
+ blacklist: root.notificationBlacklist
source: root.notifications ? root.notifications.all : undefined
whitelist: root.notificationWhitelist
- blacklist: root.notificationBlacklist
onTopmostChanged: {
if (!topmost) {
- image.source = "/qml/icons/ic-connected.svg"
- image.color = root.colorScheme.signal_success
- label.text = qsTr("Connected")
- label.color = root.colorScheme.signal_success
+ image.source = "/qml/icons/ic-connected.svg";
+ image.color = root.colorScheme.signal_success;
+ label.text = qsTr("Connected");
+ label.color = root.colorScheme.signal_success;
return;
}
-
- image.source = topmost.icon
- label.text = topmost.brief
-
+ image.source = topmost.icon;
+ label.text = topmost.brief;
switch (topmost.type) {
- case Notification.NotificationType.Danger:
- image.color = root.colorScheme.signal_danger
- label.color = root.colorScheme.signal_danger
+ case Notification.NotificationType.Danger:
+ image.color = root.colorScheme.signal_danger;
+ label.color = root.colorScheme.signal_danger;
break;
- case Notification.NotificationType.Warning:
- image.color = root.colorScheme.signal_warning
- label.color = root.colorScheme.signal_warning
+ case Notification.NotificationType.Warning:
+ image.color = root.colorScheme.signal_warning;
+ label.color = root.colorScheme.signal_warning;
break;
- case Notification.NotificationType.Success:
- image.color = root.colorScheme.signal_success
- label.color = root.colorScheme.signal_success
+ case Notification.NotificationType.Success:
+ image.color = root.colorScheme.signal_success;
+ label.color = root.colorScheme.signal_success;
break;
- case Notification.NotificationType.Info:
- image.color = root.colorScheme.signal_info
- label.color = root.colorScheme.signal_info
+ case Notification.NotificationType.Info:
+ image.color = root.colorScheme.signal_info;
+ label.color = root.colorScheme.signal_info;
break;
}
}
}
-
RowLayout {
anchors.fill: parent
spacing: 8
ColorImage {
id: image
- width: 16
- height: 16
- sourceSize.width: width
- sourceSize.height: height
- source: "/qml/icons/ic-connected.svg"
color: root.colorScheme.signal_success
+ height: 16
+ source: "/qml/icons/ic-connected.svg"
+ sourceSize.height: height
+ sourceSize.width: width
+ width: 16
}
-
Label {
- colorScheme: root.colorScheme
id: label
-
Layout.fillHeight: true
Layout.fillWidth: true
-
- wrapMode: Text.WordWrap
-
- horizontalAlignment: Text.AlignLeft
- verticalAlignment: Text.AlignVCenter
-
- text: qsTr("Connected")
color: root.colorScheme.signal_success
+ colorScheme: root.colorScheme
+ horizontalAlignment: Text.AlignLeft
+ text: qsTr("Connected")
+ verticalAlignment: Text.AlignVCenter
+ wrapMode: Text.WordWrap
}
}
}
diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/WelcomeGuide.qml b/internal/frontend/bridge-gui/bridge-gui/qml/WelcomeGuide.qml
index 88c331bb..8be55925 100644
--- a/internal/frontend/bridge-gui/bridge-gui/qml/WelcomeGuide.qml
+++ b/internal/frontend/bridge-gui/bridge-gui/qml/WelcomeGuide.qml
@@ -1,25 +1,19 @@
// Copyright (c) 2023 Proton AG
-//
// This file is part of Proton Mail Bridge.
-//
// Proton Mail Bridge 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 3 of the License, or
// (at your option) any later version.
-//
// Proton Mail Bridge 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.
-//
// You should have received a copy of the GNU General Public License
// along with Proton Mail Bridge. If not, see .
-
import QtQml
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
-
import Proton
Item {
@@ -34,24 +28,46 @@ Item {
anchors.fill: parent
spacing: 0
- Rectangle {
- color: root.colorScheme.background_norm
+ states: [
+ State {
+ name: "Page 1"
+ PropertyChanges {
+ currentIndex: 0
+ target: signInItem
+ }
+ },
+ State {
+ name: "Page 2"
+
+ PropertyChanges {
+ currentIndex: 1
+ target: signInItem
+ }
+ },
+ State {
+ name: "Page 3"
+
+ PropertyChanges {
+ currentIndex: 2
+ target: signInItem
+ }
+ }
+ ]
+
+ Rectangle {
Layout.fillHeight: true
Layout.fillWidth: true
-
+ color: root.colorScheme.background_norm
implicitHeight: children[0].implicitHeight
implicitWidth: children[0].implicitWidth
-
- visible: signInItem.currentIndex == 0
+ visible: signInItem.currentIndex === 0
GridLayout {
anchors.fill: parent
-
columnSpacing: 0
- rowSpacing: 0
-
columns: 3
+ rowSpacing: 0
// top margin
Item {
@@ -59,141 +75,123 @@ Item {
Layout.fillWidth: true
// Using binding component here instead of direct binding to avoid binding loop during construction of element
- Binding on Layout.preferredHeight {
+ Binding on Layout.preferredHeight {
value: (parent.height - welcomeContentItem.height) / 4
}
}
// left margin
Item {
- Layout.minimumWidth: 48
- Layout.maximumWidth: 80
Layout.fillWidth: true
+ Layout.maximumWidth: 80
+ Layout.minimumWidth: 48
Layout.preferredHeight: welcomeContentItem.height
}
-
ColumnLayout {
id: welcomeContentItem
Layout.fillWidth: true
spacing: 0
Image {
- source: colorScheme.welcome_img
Layout.alignment: Qt.AlignHCenter
Layout.topMargin: 16
+ source: colorScheme.welcome_img
sourceSize.height: 148
sourceSize.width: 264
}
-
Label {
- colorScheme: root.colorScheme
- text: qsTr("Welcome to\nProton Mail Bridge")
Layout.alignment: Qt.AlignHCenter
Layout.fillWidth: true
Layout.topMargin: 16
-
+ colorScheme: root.colorScheme
horizontalAlignment: Text.AlignHCenter
-
+ text: qsTr("Welcome to\nProton Mail Bridge")
type: Label.LabelType.Heading
}
-
Label {
- colorScheme: root.colorScheme
id: longTextLabel
- text: qsTr("Add your Proton Mail account to securely access and manage your messages in your favorite email client. Bridge runs in the background and encrypts and decrypts your messages seamlessly.")
Layout.alignment: Qt.AlignHCenter
Layout.fillWidth: true
- Layout.topMargin: 16
Layout.preferredWidth: 320
-
- wrapMode: Text.WordWrap
-
+ Layout.topMargin: 16
+ colorScheme: root.colorScheme
horizontalAlignment: Text.AlignHCenter
-
+ text: qsTr("Add your Proton Mail account to securely access and manage your messages in your favorite email client. Bridge runs in the background and encrypts and decrypts your messages seamlessly.")
type: Label.LabelType.Body
+ wrapMode: Text.WordWrap
}
}
// Right margin
Item {
- Layout.minimumWidth: 48
- Layout.maximumWidth: 80
Layout.fillWidth: true
+ Layout.maximumWidth: 80
+ Layout.minimumWidth: 48
Layout.preferredHeight: welcomeContentItem.height
}
// bottom margin
Item {
Layout.columnSpan: 3
- Layout.fillWidth: true
Layout.fillHeight: true
-
+ Layout.fillWidth: true
implicitHeight: children[0].implicitHeight + children[0].anchors.bottomMargin + children[0].anchors.topMargin
implicitWidth: children[0].implicitWidth
Image {
id: logoImage
- source: colorScheme.logo_img
-
- anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom
- anchors.topMargin: 48
anchors.bottomMargin: 48
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.topMargin: 48
+ source: colorScheme.logo_img
sourceSize.height: 25
sourceSize.width: 200
}
}
}
}
-
Rectangle {
- color: (signInItem.currentIndex == 0) ? root.colorScheme.background_weak : root.colorScheme.background_norm
Layout.fillHeight: true
Layout.fillWidth: true
-
+ color: (signInItem.currentIndex == 0) ? root.colorScheme.background_weak : root.colorScheme.background_norm
implicitHeight: children[0].implicitHeight
implicitWidth: children[0].implicitWidth
RowLayout {
anchors.fill: parent
spacing: 0
+
Item {
Layout.fillHeight: true
Layout.fillWidth: true
Layout.preferredWidth: signInItem.currentIndex == 0 ? 0 : parent.width / 4
-
implicitHeight: children[0].implicitHeight + children[0].anchors.topMargin + children[0].anchors.bottomMargin
implicitWidth: children[0].implicitWidth + children[0].anchors.leftMargin + children[0].anchors.rightMargin
Button {
- colorScheme: root.colorScheme
- anchors.left: parent.left
anchors.bottom: parent.bottom
-
+ anchors.bottomMargin: 80
+ anchors.left: parent.left
anchors.leftMargin: 80
anchors.rightMargin: 80
anchors.topMargin: 80
- anchors.bottomMargin: 80
-
- visible: signInItem.currentIndex != 0
-
+ colorScheme: root.colorScheme
secondary: true
text: qsTr("Back")
+ visible: signInItem.currentIndex != 0
onClicked: {
- signInItem.abort()
+ signInItem.abort();
}
}
}
-
GridLayout {
Layout.fillHeight: true
Layout.fillWidth: true
-
columnSpacing: 0
- rowSpacing: 0
-
columns: 3
+ rowSpacing: 0
// top margin
Item {
@@ -201,76 +199,47 @@ Item {
Layout.fillWidth: true
// Using binding component here instead of direct binding to avoid binding loop during construction of element
- Binding on Layout.preferredHeight {
+ Binding on Layout.preferredHeight {
value: (parent.height - signInItem.height) / 4
}
}
// left margin
Item {
- Layout.minimumWidth: 48
- Layout.maximumWidth: 80
Layout.fillWidth: true
+ Layout.maximumWidth: 80
+ Layout.minimumWidth: 48
Layout.preferredHeight: signInItem.height
}
-
-
SignIn {
id: signInItem
- colorScheme: root.colorScheme
-
- Layout.preferredWidth: 320
Layout.fillWidth: true
-
+ Layout.preferredWidth: 320
+ colorScheme: root.colorScheme
focus: true
username: Backend.users.count === 1 && Backend.users.get(0) && (Backend.users.get(0).state === EUserState.SignedOut) ? Backend.users.get(0).username : ""
}
// Right margin
Item {
- Layout.minimumWidth: 48
- Layout.maximumWidth: 80
Layout.fillWidth: true
+ Layout.maximumWidth: 80
+ Layout.minimumWidth: 48
Layout.preferredHeight: signInItem.height
}
// bottom margin
Item {
Layout.columnSpan: 3
- Layout.fillWidth: true
Layout.fillHeight: true
+ Layout.fillWidth: true
}
}
-
Item {
Layout.fillHeight: true
- Layout.preferredWidth: signInItem.currentIndex == 0 ? 0 : parent.width / 4
+ Layout.preferredWidth: signInItem.currentIndex === 0 ? 0 : parent.width / 4
}
}
}
-
- states: [
- State {
- name: "Page 1"
- PropertyChanges {
- target: signInItem
- currentIndex: 0
- }
- },
- State {
- name: "Page 2"
- PropertyChanges {
- target: signInItem
- currentIndex: 1
- }
- },
- State {
- name: "Page 3"
- PropertyChanges {
- target: signInItem
- currentIndex: 2
- }
- }
- ]
}
}
diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/tests/Buttons.qml b/internal/frontend/bridge-gui/bridge-gui/qml/tests/Buttons.qml
deleted file mode 100644
index 1b82a3a2..00000000
--- a/internal/frontend/bridge-gui/bridge-gui/qml/tests/Buttons.qml
+++ /dev/null
@@ -1,76 +0,0 @@
-// Copyright (c) 2023 Proton AG
-//
-// This file is part of Proton Mail Bridge.
-//
-// Proton Mail Bridge 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 3 of the License, or
-// (at your option) any later version.
-//
-// Proton Mail Bridge 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.
-//
-// You should have received a copy of the GNU General Public License
-// along with Proton Mail Bridge. If not, see .
-
-import QtQuick
-import QtQuick.Window
-import QtQuick.Layouts
-import QtQuick.Controls
-
-import "../Proton"
-
-RowLayout {
- id: root
- property ColorScheme colorScheme
-
- // Primary buttons
- ButtonsColumn {
- colorScheme: root.colorScheme
- Layout.fillWidth: true
- Layout.fillHeight: true
-
- iconLoading: "/qml/icons/Loader_16.svg"
- }
-
- // Secondary buttons
- ButtonsColumn {
- colorScheme: root.colorScheme
- Layout.fillWidth: true
- Layout.fillHeight: true
-
- secondary: true
- iconLoading: "/qml/icons/Loader_16.svg"
- }
-
- // Secondary icons
- ButtonsColumn {
- colorScheme: root.colorScheme
- Layout.fillWidth: true
- Layout.fillHeight: true
-
- secondary: true
- textNormal: ""
- iconNormal: "/qml/icons/ic-cross-close.svg"
- textDisabled: ""
- iconDisabled: "/qml/icons/ic-cross-close.svg"
- textLoading: ""
- iconLoading: "/qml/icons/Loader_16.svg"
- }
-
- // Icons
- ButtonsColumn {
- colorScheme: root.colorScheme
- Layout.fillWidth: true
- Layout.fillHeight: true
-
- textNormal: ""
- iconNormal: "/qml/icons/ic-cross-close.svg"
- textDisabled: ""
- iconDisabled: "/qml/icons/ic-cross-close.svg"
- textLoading: ""
- iconLoading: "/qml/icons/Loader_16.svg"
- }
-}
diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/tests/ButtonsColumn.qml b/internal/frontend/bridge-gui/bridge-gui/qml/tests/ButtonsColumn.qml
deleted file mode 100644
index f4079c48..00000000
--- a/internal/frontend/bridge-gui/bridge-gui/qml/tests/ButtonsColumn.qml
+++ /dev/null
@@ -1,76 +0,0 @@
-// Copyright (c) 2023 Proton AG
-//
-// This file is part of Proton Mail Bridge.
-//
-// Proton Mail Bridge 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 3 of the License, or
-// (at your option) any later version.
-//
-// Proton Mail Bridge 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.
-//
-// You should have received a copy of the GNU General Public License
-// along with Proton Mail Bridge. If not, see .
-
-import QtQuick.Layouts
-import QtQuick
-import QtQuick.Controls
-
-import "../Proton"
-
-ColumnLayout {
- id: root
- property ColorScheme colorScheme
-
- property string textNormal: "Button"
- property string iconNormal: ""
- property string textDisabled: "Disabled"
- property string iconDisabled: ""
- property string textLoading: "Loading"
- property string iconLoading: ""
- property bool secondary: false
-
- Button {
- colorScheme: root.colorScheme
- Layout.fillWidth: true
-
- Layout.minimumHeight: implicitHeight
- Layout.minimumWidth: implicitWidth
-
- text: root.textNormal
- icon.source: iconNormal
- secondary: root.secondary
- }
-
-
- Button {
- colorScheme: root.colorScheme
- Layout.fillWidth: true
-
- Layout.minimumHeight: implicitHeight
- Layout.minimumWidth: implicitWidth
-
- text: root.textDisabled
- icon.source: iconDisabled
- secondary: root.secondary
-
- enabled: false
- }
-
- Button {
- colorScheme: root.colorScheme
- Layout.fillWidth: true
-
- Layout.minimumHeight: implicitHeight
- Layout.minimumWidth: implicitWidth
-
- text: root.textLoading
- icon.source: iconLoading
- secondary: root.secondary
-
- loading: true
- }
-}
diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/tests/CheckBoxes.qml b/internal/frontend/bridge-gui/bridge-gui/qml/tests/CheckBoxes.qml
deleted file mode 100644
index 12f18b66..00000000
--- a/internal/frontend/bridge-gui/bridge-gui/qml/tests/CheckBoxes.qml
+++ /dev/null
@@ -1,112 +0,0 @@
-// Copyright (c) 2023 Proton AG
-//
-// This file is part of Proton Mail Bridge.
-//
-// Proton Mail Bridge 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 3 of the License, or
-// (at your option) any later version.
-//
-// Proton Mail Bridge 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.
-//
-// You should have received a copy of the GNU General Public License
-// along with Proton Mail Bridge. If not, see .
-
-import QtQuick
-import QtQuick.Window
-import QtQuick.Layouts
-import QtQuick.Controls
-
-import "../Proton"
-
-RowLayout {
- id: root
- property ColorScheme colorScheme
-
- ColumnLayout {
- Layout.fillWidth: true
-
- spacing: parent.spacing
-
- CheckBox {
- text: "Checkbox"
- colorScheme: root.colorScheme
- }
-
- CheckBox {
- text: "Checkbox"
- error: true
- colorScheme: root.colorScheme
- }
-
- CheckBox {
- text: "Checkbox"
- enabled: false
- colorScheme: root.colorScheme
- }
- CheckBox {
- text: ""
- colorScheme: root.colorScheme
- }
-
- CheckBox {
- text: ""
- error: true
- colorScheme: root.colorScheme
- }
-
- CheckBox {
- text: ""
- enabled: false
- colorScheme: root.colorScheme
- }
- }
-
- ColumnLayout {
- Layout.fillWidth: true
-
- spacing: parent.spacing
-
- CheckBox {
- text: "Checkbox"
- checked: true
- colorScheme: root.colorScheme
- }
-
- CheckBox {
- text: "Checkbox"
- checked: true
- error: true
- colorScheme: root.colorScheme
- }
-
- CheckBox {
- text: "Checkbox"
- checked: true
- enabled: false
- colorScheme: root.colorScheme
- }
- CheckBox {
- text: ""
- checked: true
- colorScheme: root.colorScheme
- }
-
- CheckBox {
- text: ""
- checked: true
- error: true
- colorScheme: root.colorScheme
- }
-
- CheckBox {
- text: ""
- checked: true
- enabled: false
- colorScheme: root.colorScheme
- }
- }
-}
diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/tests/ComboBoxes.qml b/internal/frontend/bridge-gui/bridge-gui/qml/tests/ComboBoxes.qml
deleted file mode 100644
index 437622d5..00000000
--- a/internal/frontend/bridge-gui/bridge-gui/qml/tests/ComboBoxes.qml
+++ /dev/null
@@ -1,100 +0,0 @@
-// Copyright (c) 2023 Proton AG
-//
-// This file is part of Proton Mail Bridge.
-//
-// Proton Mail Bridge 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 3 of the License, or
-// (at your option) any later version.
-//
-// Proton Mail Bridge 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.
-//
-// You should have received a copy of the GNU General Public License
-// along with Proton Mail Bridge. If not, see .
-
-import QtQuick
-import QtQuick.Window
-import QtQuick.Layouts
-import QtQuick.Controls
-
-import "../Proton"
-
-RowLayout {
- id: root
- property ColorScheme colorScheme
-
- ColumnLayout {
- Layout.fillWidth: true
- ComboBox {
- Layout.fillWidth: true
- model: ["First", "Second", "Third"]
- colorScheme: root.colorScheme
- }
-
- ComboBox {
- Layout.fillWidth: true
- model: ["First", "Second", "Third"]
- editable: true
- colorScheme: root.colorScheme
- }
- }
-
- ColumnLayout {
- Layout.fillWidth: true
- ComboBox {
- Layout.fillWidth: true
- model: ["First", "Second", "Third"]
- colorScheme: root.colorScheme
- enabled: false
- }
-
- ComboBox {
- Layout.fillWidth: true
- model: ["First", "Second", "Third"]
- editable: true
- colorScheme: root.colorScheme
- enabled: false
- }
- }
-
- ColumnLayout {
- Layout.fillWidth: true
- ComboBox {
- Layout.fillWidth: true
- model: ["First", "Second", "Third"]
- colorScheme: root.colorScheme
- LayoutMirroring.enabled: true
- }
-
- ComboBox {
- Layout.fillWidth: true
- model: ["First", "Second", "Third"]
- editable: true
- colorScheme: root.colorScheme
- LayoutMirroring.enabled: true
- }
- }
-
- ColumnLayout {
- Layout.fillWidth: true
- ComboBox {
- Layout.fillWidth: true
- model: ["First", "Second", "Third"]
- colorScheme: root.colorScheme
- enabled: false
- LayoutMirroring.enabled: true
- }
-
- ComboBox {
- Layout.fillWidth: true
- model: ["First", "Second", "Third"]
- editable: true
- colorScheme: root.colorScheme
- enabled: false
- LayoutMirroring.enabled: true
- }
- }
-}
diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/tests/RadioButtons.qml b/internal/frontend/bridge-gui/bridge-gui/qml/tests/RadioButtons.qml
deleted file mode 100644
index 11a704f2..00000000
--- a/internal/frontend/bridge-gui/bridge-gui/qml/tests/RadioButtons.qml
+++ /dev/null
@@ -1,112 +0,0 @@
-// Copyright (c) 2023 Proton AG
-//
-// This file is part of Proton Mail Bridge.
-//
-// Proton Mail Bridge 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 3 of the License, or
-// (at your option) any later version.
-//
-// Proton Mail Bridge 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.
-//
-// You should have received a copy of the GNU General Public License
-// along with Proton Mail Bridge. If not, see .
-
-import QtQuick
-import QtQuick.Window
-import QtQuick.Layouts
-import QtQuick.Controls
-
-import "../Proton"
-
-RowLayout {
- id: root
- property ColorScheme colorScheme
-
- ColumnLayout {
- Layout.fillWidth: true
-
- spacing: parent.spacing
-
- RadioButton {
- colorScheme: root.colorScheme
- text: "Radio"
- }
-
- RadioButton {
- colorScheme: root.colorScheme
- text: "Radio"
- error: true
- }
-
- RadioButton {
- colorScheme: root.colorScheme
- text: "Radio"
- enabled: false
- }
- RadioButton {
- colorScheme: root.colorScheme
- text: ""
- }
-
- RadioButton {
- colorScheme: root.colorScheme
- text: ""
- error: true
- }
-
- RadioButton {
- colorScheme: root.colorScheme
- text: ""
- enabled: false
- }
- }
-
- ColumnLayout {
- Layout.fillWidth: true
-
- spacing: parent.spacing
-
- RadioButton {
- colorScheme: root.colorScheme
- text: "Radio"
- checked: true
- }
-
- RadioButton {
- colorScheme: root.colorScheme
- text: "Radio"
- checked: true
- error: true
- }
-
- RadioButton {
- colorScheme: root.colorScheme
- text: "Radio"
- checked: true
- enabled: false
- }
- RadioButton {
- colorScheme: root.colorScheme
- text: ""
- checked: true
- }
-
- RadioButton {
- colorScheme: root.colorScheme
- text: ""
- checked: true
- error: true
- }
-
- RadioButton {
- colorScheme: root.colorScheme
- text: ""
- checked: true
- enabled: false
- }
- }
-}
diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/tests/Switches.qml b/internal/frontend/bridge-gui/bridge-gui/qml/tests/Switches.qml
deleted file mode 100644
index 4b664e1e..00000000
--- a/internal/frontend/bridge-gui/bridge-gui/qml/tests/Switches.qml
+++ /dev/null
@@ -1,114 +0,0 @@
-// Copyright (c) 2023 Proton AG
-//
-// This file is part of Proton Mail Bridge.
-//
-// Proton Mail Bridge 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 3 of the License, or
-// (at your option) any later version.
-//
-// Proton Mail Bridge 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.
-//
-// You should have received a copy of the GNU General Public License
-// along with Proton Mail Bridge. If not, see .
-
-import QtQuick
-import QtQuick.Window
-import QtQuick.Layouts
-import QtQuick.Controls
-
-import "../Proton"
-
-RowLayout {
- id: root
- property ColorScheme colorScheme
-
- ColumnLayout {
- Layout.fillWidth: true
-
- spacing: parent.spacing
-
- Switch {
- text: "Toggle"
- colorScheme: root.colorScheme
- }
-
- Switch {
- text: "Toggle"
- enabled: false
- colorScheme: root.colorScheme
- }
-
- Switch {
- text: "Toggle"
- loading: true
- colorScheme: root.colorScheme
- }
-
- Switch {
- text: ""
- colorScheme: root.colorScheme
- }
-
- Switch {
- text: ""
- enabled: false
- colorScheme: root.colorScheme
- }
-
- Switch {
- text: ""
- loading: true
- colorScheme: root.colorScheme
- }
- }
-
- ColumnLayout {
- Layout.fillWidth: true
-
- spacing: parent.spacing
-
- Switch {
- text: "Toggle"
- checked: true
- colorScheme: root.colorScheme
- }
-
- Switch {
- text: "Toggle"
- checked: true
- enabled: false
- colorScheme: root.colorScheme
- }
-
- Switch {
- text: "Toggle"
- checked: true
- loading: true
- colorScheme: root.colorScheme
- }
-
- Switch {
- text: ""
- checked: true
- colorScheme: root.colorScheme
- }
-
- Switch {
- text: ""
- checked: true
- enabled: false
- colorScheme: root.colorScheme
- }
-
- Switch {
- text: ""
- checked: true
- loading: true
- colorScheme: root.colorScheme
- }
- }
-}
diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/tests/Test.qml b/internal/frontend/bridge-gui/bridge-gui/qml/tests/Test.qml
deleted file mode 100644
index c6027148..00000000
--- a/internal/frontend/bridge-gui/bridge-gui/qml/tests/Test.qml
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright (c) 2023 Proton AG
-//
-// This file is part of Proton Mail Bridge.
-//
-// Proton Mail Bridge 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 3 of the License, or
-// (at your option) any later version.
-//
-// Proton Mail Bridge 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.
-//
-// You should have received a copy of the GNU General Public License
-// along with Proton Mail Bridge. If not, see .
-
-import QtQuick.Window
-
-import "../Proton"
-
-Window {
- width: 800
- height: 600
- visible: true
- TestComponents {
- anchors.fill: parent
- colorScheme: ProtonStyle.currentStyle
- }
- onClosing: {
- Qt.quit()
- }
-}
diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/tests/TestComponents.qml b/internal/frontend/bridge-gui/bridge-gui/qml/tests/TestComponents.qml
deleted file mode 100644
index a4fd5738..00000000
--- a/internal/frontend/bridge-gui/bridge-gui/qml/tests/TestComponents.qml
+++ /dev/null
@@ -1,86 +0,0 @@
-// Copyright (c) 2023 Proton AG
-//
-// This file is part of Proton Mail Bridge.
-//
-// Proton Mail Bridge 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 3 of the License, or
-// (at your option) any later version.
-//
-// Proton Mail Bridge 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.
-//
-// You should have received a copy of the GNU General Public License
-// along with Proton Mail Bridge. If not, see .
-
-import QtQuick
-import QtQuick.Layouts
-import QtQuick.Controls
-
-import "../Proton"
-
-Rectangle {
- id: root
- property ColorScheme colorScheme
- color: 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
-
- ScrollView {
- anchors.fill: parent
-
- ColumnLayout {
- anchors.margins: 20
-
- width: root.width
-
- spacing: 5
-
- Buttons {
- colorScheme: root.colorScheme
- Layout.fillWidth: true
- Layout.margins: 20
- }
-
- CheckBoxes {
- colorScheme: root.colorScheme
- Layout.fillWidth: true
- Layout.margins: 20
- }
-
- ComboBoxes {
- colorScheme: root.colorScheme
- Layout.fillWidth: true
- Layout.margins: 20
- }
-
- RadioButtons {
- colorScheme: root.colorScheme
- Layout.fillWidth: true
- Layout.margins: 20
- }
-
- Switches {
- colorScheme: root.colorScheme
- Layout.fillWidth: true
- Layout.margins: 20
- }
-
- TextAreas {
- colorScheme: root.colorScheme
- Layout.fillWidth: true
- Layout.margins: 20
- }
-
- TextFields {
- colorScheme: root.colorScheme
- Layout.fillWidth: true
- Layout.margins: 20
- }
- }
- }
-}
diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/tests/TextAreas.qml b/internal/frontend/bridge-gui/bridge-gui/qml/tests/TextAreas.qml
deleted file mode 100644
index e6146d9a..00000000
--- a/internal/frontend/bridge-gui/bridge-gui/qml/tests/TextAreas.qml
+++ /dev/null
@@ -1,113 +0,0 @@
-// Copyright (c) 2023 Proton AG
-//
-// This file is part of Proton Mail Bridge.
-//
-// Proton Mail Bridge 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 3 of the License, or
-// (at your option) any later version.
-//
-// Proton Mail Bridge 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.
-//
-// You should have received a copy of the GNU General Public License
-// along with Proton Mail Bridge. If not, see .
-
-import QtQuick
-import QtQuick.Window
-import QtQuick.Layouts
-import QtQuick.Controls
-
-import "../Proton"
-
-ColumnLayout {
- id: root
- property ColorScheme colorScheme
-
- spacing: 10
-
- TextArea {
- colorScheme: root.colorScheme
- Layout.fillWidth: true
- Layout.preferredHeight: 100
-
- placeholderText: "Placeholder"
- label: "Label"
- hint: "Hint"
- assistiveText: "Assistive text"
-
- wrapMode: TextInput.Wrap
- }
-
- TextArea {
- colorScheme: root.colorScheme
- Layout.fillWidth: true
- Layout.preferredHeight: 100
-
- text: "Value"
- placeholderText: "Placeholder"
- label: "Label"
- hint: "Hint"
- assistiveText: "Assistive text"
-
- wrapMode: TextInput.Wrap
- }
-
-
- TextArea {
- colorScheme: root.colorScheme
- Layout.fillWidth: true
- Layout.preferredHeight: 100
-
- error: true
-
- text: "Value"
- placeholderText: "Placeholder"
- label: "Label"
- hint: "Hint"
- errorString: "Error message"
-
- wrapMode: TextInput.Wrap
- }
-
-
- TextArea {
- colorScheme: root.colorScheme
- Layout.fillWidth: true
- Layout.preferredHeight: 100
-
- enabled: false
-
- text: "Value"
- placeholderText: "Placeholder"
- label: "Label"
- hint: "Hint"
- assistiveText: "Assistive text"
-
- wrapMode: TextInput.Wrap
- }
-
- TextArea {
- colorScheme: root.colorScheme
- Layout.fillWidth: true
- Layout.preferredHeight: 100
-
- placeholderText: "Type 42 here"
- label: "42 Validator"
- hint: "Accepts only \"42\""
- assistiveText: "Type something here, preferably 42"
-
- wrapMode: TextInput.Wrap
-
- validator: function(str) {
- if (str === "42") {
- return
- }
-
- return "Not 42"
- }
- }
-}
-
diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/tests/TextFields.qml b/internal/frontend/bridge-gui/bridge-gui/qml/tests/TextFields.qml
deleted file mode 100644
index d3618660..00000000
--- a/internal/frontend/bridge-gui/bridge-gui/qml/tests/TextFields.qml
+++ /dev/null
@@ -1,187 +0,0 @@
-// Copyright (c) 2023 Proton AG
-//
-// This file is part of Proton Mail Bridge.
-//
-// Proton Mail Bridge 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 3 of the License, or
-// (at your option) any later version.
-//
-// Proton Mail Bridge 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.
-//
-// You should have received a copy of the GNU General Public License
-// along with Proton Mail Bridge. If not, see .
-
-import QtQuick
-import QtQuick.Window
-import QtQuick.Layouts
-import QtQuick.Controls
-
-import "../Proton"
-
-RowLayout {
- id: root
- property ColorScheme colorScheme
-
- // Norm
- ColumnLayout {
- Layout.fillWidth: true
-
- spacing: parent.spacing
-
- TextField {
- colorScheme: root.colorScheme
- Layout.fillWidth: true
-
- placeholderText: "Placeholder"
- label: "Label"
- hint: "Hint"
- assistiveText: "Assistive text"
- }
-
- TextField {
- colorScheme: root.colorScheme
- Layout.fillWidth: true
-
- text: "Value"
- placeholderText: "Placeholder"
- label: "Label"
- hint: "Hint"
- assistiveText: "Assistive text"
- }
-
-
- TextField {
- colorScheme: root.colorScheme
- Layout.fillWidth: true
- error: true
-
- text: "Value"
- placeholderText: "Placeholder"
- label: "Label"
- hint: "Hint"
- errorString: "Error message"
- }
-
-
- TextField {
- colorScheme: root.colorScheme
- Layout.fillWidth: true
-
- text: "Value"
- placeholderText: "Placeholder"
- label: "Label"
- hint: "Hint"
- assistiveText: "Assistive text"
-
- enabled: false
- }
- }
-
- // Masked
- ColumnLayout {
- Layout.fillWidth: true
-
- spacing: parent.spacing
-
- TextField {
- colorScheme: root.colorScheme
- Layout.fillWidth: true
- echoMode: TextInput.Password
- placeholderText: "Password"
- label: "Label"
- hint: "Hint"
- assistiveText: "Assistive text"
- }
-
- TextField {
- colorScheme: root.colorScheme
- Layout.fillWidth: true
- text: "Password"
-
- echoMode: TextInput.Password
- placeholderText: "Password"
- label: "Label"
- hint: "Hint"
- assistiveText: "Assistive text"
- }
-
- TextField {
- colorScheme: root.colorScheme
- Layout.fillWidth: true
- text: "Password"
- error: true
-
- echoMode: TextInput.Password
- placeholderText: "Password"
- label: "Label"
- hint: "Hint"
- errorString: "Error message"
- }
-
- TextField {
- colorScheme: root.colorScheme
- Layout.fillWidth: true
- text: "Password"
- enabled: false
-
- echoMode: TextInput.Password
- placeholderText: "Password"
- label: "Label"
- hint: "Hint"
- assistiveText: "Assistive text"
- }
- }
-
- // Varia
- ColumnLayout {
- Layout.fillWidth: true
-
- spacing: parent.spacing
-
- TextField {
- colorScheme: root.colorScheme
- Layout.fillWidth: true
-
- placeholderText: "Type 42 here"
- label: "42 Validator"
- hint: "Accepts only \"42\""
- assistiveText: "Type something here, preferably 42"
-
- validator: function(str) {
- if (str === "42") {
- return
- }
-
- return "Not 42"
- }
- }
-
- TextField {
- colorScheme: root.colorScheme
- Layout.fillWidth: true
-
- placeholderText: "Placeholder"
- label: "Label"
- }
-
- TextField {
- colorScheme: root.colorScheme
- Layout.fillWidth: true
-
- placeholderText: "Placeholder"
- hint: "Hint"
- }
-
- TextField {
- colorScheme: root.colorScheme
- Layout.fillWidth: true
-
- placeholderText: "Placeholder"
- assistiveText: "Assistive text"
- }
- }
-}