forked from Silverfish/proton-bridge
chore: use qmlformat on qml files, and removed deprecated tests.
This commit is contained in:
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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)<br>© 2017-%4 %5<br>%6 %7<br>%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)<br>© 2017-%4 %5<br>%6 %7<br>%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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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
|
||||
|
||||
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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> 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> 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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Templates as T
|
||||
|
||||
|
||||
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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 `<a href="${url}">${text}</a>`;
|
||||
}
|
||||
|
||||
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 `<a href="${url}">${text}</a>`
|
||||
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
|
||||
}
|
||||
|
||||
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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 = ""
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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 = ""
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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
|
||||
|
||||
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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("<b>New IMAP engine</b><br/>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("<b>Faster than ever</b><br/>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("<b>Extra security</b><br/>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.<br/>") + 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.<br/>") + 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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"
|
||||
}
|
||||
}
|
||||
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
import QtQuick.Window
|
||||
|
||||
import "../Proton"
|
||||
|
||||
Window {
|
||||
width: 800
|
||||
height: 600
|
||||
visible: true
|
||||
TestComponents {
|
||||
anchors.fill: parent
|
||||
colorScheme: ProtonStyle.currentStyle
|
||||
}
|
||||
onClosing: {
|
||||
Qt.quit()
|
||||
}
|
||||
}
|
||||
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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"
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user