mirror of
https://github.com/ProtonMail/proton-bridge.git
synced 2025-12-17 15:46:44 +00:00
chore: use qmlformat on qml files, and removed deprecated tests.
This commit is contained in:
@ -1,251 +1,252 @@
|
|||||||
// Copyright (c) 2023 Proton AG
|
// Copyright (c) 2023 Proton AG
|
||||||
//
|
|
||||||
// This file is part of Proton Mail Bridge.
|
// This file is part of Proton Mail Bridge.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
// 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
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
// 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/>.
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
|
|
||||||
import Proton
|
import Proton
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
|
enum ViewType {
|
||||||
property ColorScheme colorScheme
|
SmallView,
|
||||||
property var user
|
LargeView
|
||||||
|
}
|
||||||
|
|
||||||
property var _spacing: 12 * ProtonStyle.px
|
property var _spacing: 12 * ProtonStyle.px
|
||||||
|
property ColorScheme colorScheme
|
||||||
property color progressColor : {
|
property color progressColor: {
|
||||||
if (!root.enabled) return root.colorScheme.text_weak
|
if (!root.enabled)
|
||||||
if (root.type == AccountDelegate.SmallView) return root.colorScheme.text_weak
|
return root.colorScheme.text_weak;
|
||||||
if (root.user && root.user.isSyncing) return root.colorScheme.text_weak
|
if (root.type === AccountDelegate.SmallView)
|
||||||
if (root.progressRatio < .50) return root.colorScheme.signal_success
|
return root.colorScheme.text_weak;
|
||||||
if (root.progressRatio < .75) return root.colorScheme.signal_warning
|
if (root.user && root.user.isSyncing)
|
||||||
return root.colorScheme.signal_danger
|
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: {
|
property real progressRatio: {
|
||||||
if (!root.user)
|
if (!root.user)
|
||||||
return 0
|
return 0;
|
||||||
return root.user.isSyncing ? root.user.syncProgress : reasonableFraction(root.user.usedBytes, root.user.totalBytes)
|
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 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)
|
property string usedSpace: root.spaceWithUnits(root.user ? root.reasonableBytes(root.user.usedBytes) : 0)
|
||||||
|
property var user
|
||||||
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]
|
|
||||||
}
|
|
||||||
|
|
||||||
function primaryEmail() {
|
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
|
// width expected to be set by parent object
|
||||||
implicitHeight : children[0].implicitHeight
|
implicitHeight: children[0].implicitHeight
|
||||||
|
|
||||||
enum ViewType{
|
|
||||||
SmallView, LargeView
|
|
||||||
}
|
|
||||||
property var type : AccountDelegate.SmallView
|
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
spacing: root._spacing
|
spacing: root._spacing
|
||||||
|
|
||||||
anchors {
|
anchors {
|
||||||
top: root.top
|
|
||||||
left: root.left
|
left: root.left
|
||||||
right: root.right
|
right: root.right
|
||||||
|
top: root.top
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: avatar
|
id: avatar
|
||||||
|
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
Layout.preferredWidth: height
|
Layout.preferredWidth: height
|
||||||
|
color: root.colorScheme.background_avatar
|
||||||
radius: ProtonStyle.avatar_radius
|
radius: ProtonStyle.avatar_radius
|
||||||
|
|
||||||
color: root.colorScheme.background_avatar
|
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
colorScheme: root.colorScheme
|
|
||||||
anchors.fill: parent
|
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: {
|
type: {
|
||||||
switch (root.type) {
|
switch (root.type) {
|
||||||
case AccountDelegate.SmallView: return Label.Body
|
case AccountDelegate.SmallView:
|
||||||
case AccountDelegate.LargeView: return Label.Title
|
return Label.Body;
|
||||||
|
case AccountDelegate.LargeView:
|
||||||
|
return Label.Title;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
font.weight: Font.Normal
|
|
||||||
color: "#FFFFFF"
|
|
||||||
horizontalAlignment: Qt.AlignHCenter
|
|
||||||
verticalAlignment: Qt.AlignVCenter
|
verticalAlignment: Qt.AlignVCenter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: account
|
id: account
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
id: labelEmail
|
id: labelEmail
|
||||||
Layout.maximumWidth: root.width - (root._spacing + avatar.width)
|
Layout.maximumWidth: root.width - (root._spacing + avatar.width)
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
|
elide: Text.ElideMiddle
|
||||||
text: primaryEmail()
|
text: primaryEmail()
|
||||||
type: {
|
type: {
|
||||||
switch (root.type) {
|
switch (root.type) {
|
||||||
case AccountDelegate.SmallView: return Label.Body
|
case AccountDelegate.SmallView:
|
||||||
case AccountDelegate.LargeView: return Label.Title
|
return Label.Body;
|
||||||
|
case AccountDelegate.LargeView:
|
||||||
|
return Label.Title;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
elide: Text.ElideMiddle
|
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: labelArea
|
id: labelArea
|
||||||
anchors.fill:parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
}
|
}
|
||||||
|
|
||||||
ToolTip {
|
ToolTip {
|
||||||
id: toolTipEmail
|
id: toolTipEmail
|
||||||
visible: labelArea.containsMouse && labelEmail.truncated
|
|
||||||
text: primaryEmail()
|
|
||||||
delay: 1000
|
delay: 1000
|
||||||
|
text: primaryEmail()
|
||||||
|
visible: labelArea.containsMouse && labelEmail.truncated
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
border.color: root.colorScheme.background_strong
|
border.color: root.colorScheme.background_strong
|
||||||
color: root.colorScheme.background_norm
|
color: root.colorScheme.background_norm
|
||||||
}
|
}
|
||||||
|
|
||||||
contentItem: Text {
|
contentItem: Text {
|
||||||
color: root.colorScheme.text_norm
|
color: root.colorScheme.text_norm
|
||||||
text: toolTipEmail.text
|
text: toolTipEmail.text
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Item {
|
||||||
Item { implicitHeight: root.type == AccountDelegate.LargeView ? 6 * ProtonStyle.px : 0 }
|
implicitHeight: root.type === AccountDelegate.LargeView ? 6 * ProtonStyle.px : 0
|
||||||
|
}
|
||||||
RowLayout {
|
RowLayout {
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
|
color: root.progressColor
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
text: {
|
text: {
|
||||||
if (!root.user)
|
if (!root.user)
|
||||||
return qsTr("Signed out")
|
return qsTr("Signed out");
|
||||||
switch (root.user.state) {
|
switch (root.user.state) {
|
||||||
case EUserState.SignedOut:
|
case EUserState.SignedOut:
|
||||||
default:
|
default:
|
||||||
return qsTr("Signed out")
|
return qsTr("Signed out");
|
||||||
case EUserState.Locked:
|
case EUserState.Locked:
|
||||||
return qsTr("Connecting") + dotsTimer.dots
|
return qsTr("Connecting") + dotsTimer.dots;
|
||||||
case EUserState.Connected:
|
case EUserState.Connected:
|
||||||
if (root.user.isSyncing)
|
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
|
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: {
|
type: {
|
||||||
switch (root.type) {
|
switch (root.type) {
|
||||||
case AccountDelegate.SmallView: return Label.Caption
|
case AccountDelegate.SmallView:
|
||||||
case AccountDelegate.LargeView: return Label.Body
|
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 {
|
Label {
|
||||||
colorScheme: root.colorScheme
|
|
||||||
text: root.user && root.user.state == EUserState.Connected && !root.user.isSyncing ? " / " + root.totalSpace : ""
|
|
||||||
color: root.colorScheme.text_weak
|
color: root.colorScheme.text_weak
|
||||||
|
colorScheme: root.colorScheme
|
||||||
|
text: root.user && root.user.state === EUserState.Connected && !root.user.isSyncing ? " / " + root.totalSpace : ""
|
||||||
type: {
|
type: {
|
||||||
switch (root.type) {
|
switch (root.type) {
|
||||||
case AccountDelegate.SmallView: return Label.Caption
|
case AccountDelegate.SmallView:
|
||||||
case AccountDelegate.LargeView: return Label.Body
|
return Label.Caption;
|
||||||
|
case AccountDelegate.LargeView:
|
||||||
|
return Label.Body;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Item {
|
||||||
Item { implicitHeight: root.type == AccountDelegate.LargeView ? 3 * ProtonStyle.px : 0 }
|
implicitHeight: root.type === AccountDelegate.LargeView ? 3 * ProtonStyle.px : 0
|
||||||
|
}
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: progress_bar
|
id: progress_bar
|
||||||
visible: root.user ? root.type == AccountDelegate.LargeView : false
|
color: root.colorScheme.border_weak
|
||||||
width: 140 * ProtonStyle.px
|
|
||||||
height: 4 * ProtonStyle.px
|
height: 4 * ProtonStyle.px
|
||||||
radius: ProtonStyle.progress_bar_radius
|
radius: ProtonStyle.progress_bar_radius
|
||||||
color: root.colorScheme.border_weak
|
visible: root.user ? root.type === AccountDelegate.LargeView : false
|
||||||
|
width: 140 * ProtonStyle.px
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: progress_bar_filled
|
id: progress_bar_filled
|
||||||
radius: ProtonStyle.progress_bar_radius
|
|
||||||
color: root.progressColor
|
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 {
|
anchors {
|
||||||
top : parent.top
|
bottom: parent.bottom
|
||||||
bottom : parent.bottom
|
left: parent.left
|
||||||
left : parent.left
|
top: parent.top
|
||||||
}
|
}
|
||||||
width: Math.min(1,Math.max(0.02,root.progressRatio)) * parent.width
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,42 +1,35 @@
|
|||||||
// Copyright (c) 2023 Proton AG
|
// Copyright (c) 2023 Proton AG
|
||||||
//
|
|
||||||
// This file is part of Proton Mail Bridge.
|
// This file is part of Proton Mail Bridge.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
// 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
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
// 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/>.
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
|
|
||||||
import Proton
|
import Proton
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: root
|
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 ColorScheme colorScheme
|
||||||
property var notifications
|
property var notifications
|
||||||
property var user
|
property var user
|
||||||
|
|
||||||
signal showSignIn
|
|
||||||
|
|
||||||
signal showSetupGuide(var user, string address)
|
signal showSetupGuide(var user, string address)
|
||||||
|
signal showSignIn
|
||||||
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
|
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@ -45,6 +38,7 @@ Item {
|
|||||||
ScrollView {
|
ScrollView {
|
||||||
id: scrollView
|
id: scrollView
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
||||||
Component.onCompleted: contentItem.boundsBehavior = Flickable.StopAtBounds
|
Component.onCompleted: contentItem.boundsBehavior = Flickable.StopAtBounds
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
@ -54,16 +48,16 @@ Item {
|
|||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: topArea
|
id: topArea
|
||||||
color: root.colorScheme.background_norm
|
|
||||||
clip: true
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
clip: true
|
||||||
|
color: root.colorScheme.background_norm
|
||||||
implicitHeight: childrenRect.height
|
implicitHeight: childrenRect.height
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: topLayout
|
id: topLayout
|
||||||
width: _contentWidth
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
spacing: _spacing
|
spacing: _spacing
|
||||||
|
width: _contentWidth
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
// account delegate with action buttons
|
// account delegate with action buttons
|
||||||
@ -73,83 +67,82 @@ Item {
|
|||||||
AccountDelegate {
|
AccountDelegate {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
user: root.user
|
|
||||||
type: AccountDelegate.LargeView
|
|
||||||
enabled: _connected
|
enabled: _connected
|
||||||
|
type: AccountDelegate.LargeView
|
||||||
|
user: root.user
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
Layout.alignment: Qt.AlignTop
|
Layout.alignment: Qt.AlignTop
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
text: qsTr("Sign out")
|
|
||||||
secondary: true
|
secondary: true
|
||||||
|
text: qsTr("Sign out")
|
||||||
visible: _connected
|
visible: _connected
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (!root.user)
|
if (!root.user)
|
||||||
return;
|
return;
|
||||||
root.user.logout();
|
root.user.logout();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
Layout.alignment: Qt.AlignTop
|
Layout.alignment: Qt.AlignTop
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
text: qsTr("Sign in")
|
|
||||||
secondary: true
|
secondary: true
|
||||||
|
text: qsTr("Sign in")
|
||||||
visible: root.user ? (root.user.state === EUserState.SignedOut) : false
|
visible: root.user ? (root.user.state === EUserState.SignedOut) : false
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (!root.user)
|
if (!root.user)
|
||||||
return;
|
return;
|
||||||
root.showSignIn();
|
root.showSignIn();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
Layout.alignment: Qt.AlignTop
|
Layout.alignment: Qt.AlignTop
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
icon.source: "/qml/icons/ic-trash.svg"
|
icon.source: "/qml/icons/ic-trash.svg"
|
||||||
secondary: true
|
secondary: true
|
||||||
|
visible: root.user ? root.user.state !== EUserState.Locked : false
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (!root.user)
|
if (!root.user)
|
||||||
return;
|
return;
|
||||||
root.notifications.askDeleteAccount(root.user);
|
root.notifications.askDeleteAccount(root.user);
|
||||||
}
|
}
|
||||||
visible: root.user ? root.user.state !== EUserState.Locked : false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
height: root._lineThickness
|
|
||||||
color: root.colorScheme.border_weak
|
color: root.colorScheme.border_weak
|
||||||
|
height: root._lineThickness
|
||||||
}
|
}
|
||||||
|
|
||||||
SettingsItem {
|
SettingsItem {
|
||||||
colorScheme: root.colorScheme
|
Layout.fillWidth: true
|
||||||
text: qsTr("Email clients")
|
|
||||||
actionText: qsTr("Configure")
|
actionText: qsTr("Configure")
|
||||||
|
colorScheme: root.colorScheme
|
||||||
description: qsTr("Using the mailbox details below (re)configure your client.")
|
description: qsTr("Using the mailbox details below (re)configure your client.")
|
||||||
|
showSeparator: splitMode.visible
|
||||||
|
text: qsTr("Email clients")
|
||||||
type: SettingsItem.Button
|
type: SettingsItem.Button
|
||||||
visible: _connected && (!root.user.splitMode) || (root.user.addresses.length === 1)
|
visible: _connected && (!root.user.splitMode) || (root.user.addresses.length === 1)
|
||||||
showSeparator: splitMode.visible
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (!root.user)
|
if (!root.user)
|
||||||
return;
|
return;
|
||||||
root.showSetupGuide(root.user, user.addresses[0]);
|
root.showSetupGuide(root.user, user.addresses[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SettingsItem {
|
SettingsItem {
|
||||||
id: splitMode
|
id: splitMode
|
||||||
colorScheme: root.colorScheme
|
Layout.fillWidth: true
|
||||||
text: qsTr("Split addresses")
|
|
||||||
description: qsTr("Setup multiple email addresses individually.")
|
|
||||||
type: SettingsItem.Toggle
|
|
||||||
checked: root.user ? root.user.splitMode : false
|
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
|
showSeparator: addressSelector.visible
|
||||||
|
text: qsTr("Split addresses")
|
||||||
|
type: SettingsItem.Toggle
|
||||||
|
visible: _connected && root.user.addresses.length > 1
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (!splitMode.checked) {
|
if (!splitMode.checked) {
|
||||||
root.notifications.askEnableSplitMode(user);
|
root.notifications.askEnableSplitMode(user);
|
||||||
@ -158,26 +151,23 @@ Item {
|
|||||||
root.user.toggleSplitMode(!splitMode.checked);
|
root.user.toggleSplitMode(!splitMode.checked);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.bottomMargin: _spacing
|
Layout.bottomMargin: _spacing
|
||||||
|
Layout.fillWidth: true
|
||||||
visible: _connected && root.user.splitMode
|
visible: _connected && root.user.splitMode
|
||||||
|
|
||||||
ComboBox {
|
ComboBox {
|
||||||
id: addressSelector
|
id: addressSelector
|
||||||
colorScheme: root.colorScheme
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
colorScheme: root.colorScheme
|
||||||
model: root.user ? root.user.addresses : null
|
model: root.user ? root.user.addresses : null
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
text: qsTr("Configure")
|
|
||||||
secondary: true
|
secondary: true
|
||||||
|
text: qsTr("Configure")
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (!root.user)
|
if (!root.user)
|
||||||
return;
|
return;
|
||||||
@ -185,25 +175,23 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
height: 0
|
height: 0
|
||||||
} // just for some extra space before separator
|
} // just for some extra space before separator
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: bottomArea
|
id: bottomArea
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
implicitHeight: bottomLayout.implicitHeight
|
|
||||||
color: root.colorScheme.background_weak
|
color: root.colorScheme.background_weak
|
||||||
|
implicitHeight: bottomLayout.implicitHeight
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: bottomLayout
|
id: bottomLayout
|
||||||
width: _contentWidth
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
spacing: _spacing
|
spacing: _spacing
|
||||||
visible: _connected
|
visible: _connected
|
||||||
|
width: _contentWidth
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
Layout.topMargin: _detailsMargin
|
Layout.topMargin: _detailsMargin
|
||||||
@ -211,35 +199,34 @@ Item {
|
|||||||
text: qsTr("Mailbox details")
|
text: qsTr("Mailbox details")
|
||||||
type: Label.Body_semibold
|
type: Label.Body_semibold
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
id: configuration
|
id: configuration
|
||||||
spacing: _spacing
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.fillHeight: true
|
|
||||||
|
|
||||||
property string currentAddress: addressSelector.displayText
|
property string currentAddress: addressSelector.displayText
|
||||||
|
|
||||||
Configuration {
|
Layout.fillHeight: true
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
colorScheme: root.colorScheme
|
spacing: _spacing
|
||||||
title: qsTr("IMAP")
|
|
||||||
hostname: Backend.hostname
|
|
||||||
port: Backend.imapPort.toString()
|
|
||||||
username: configuration.currentAddress
|
|
||||||
password: root.user ? root.user.password : ""
|
|
||||||
security: Backend.useSSLForIMAP ? "SSL" : "STARTTLS"
|
|
||||||
}
|
|
||||||
|
|
||||||
Configuration {
|
Configuration {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
title: qsTr("SMTP")
|
|
||||||
hostname: Backend.hostname
|
hostname: Backend.hostname
|
||||||
port: Backend.smtpPort.toString()
|
|
||||||
username: configuration.currentAddress
|
|
||||||
password: root.user ? root.user.password : ""
|
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"
|
security: Backend.useSSLForSMTP ? "SSL" : "STARTTLS"
|
||||||
|
title: qsTr("SMTP")
|
||||||
|
username: configuration.currentAddress
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,25 +1,19 @@
|
|||||||
// Copyright (c) 2023 Proton AG
|
// Copyright (c) 2023 Proton AG
|
||||||
//
|
|
||||||
// This file is part of Proton Mail Bridge.
|
// This file is part of Proton Mail Bridge.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
// 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
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
// 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/>.
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Controls.impl
|
import QtQuick.Controls.impl
|
||||||
|
|
||||||
import Proton
|
import Proton
|
||||||
import Notifications
|
import Notifications
|
||||||
|
|
||||||
@ -27,34 +21,28 @@ Popup {
|
|||||||
id: root
|
id: root
|
||||||
|
|
||||||
property ColorScheme colorScheme
|
property ColorScheme colorScheme
|
||||||
property Notification notification
|
|
||||||
property var mainWindow
|
property var mainWindow
|
||||||
|
property Notification notification
|
||||||
topMargin: 37
|
|
||||||
leftMargin: (mainWindow.width - root.implicitWidth)/2
|
|
||||||
|
|
||||||
implicitHeight: contentLayout.implicitHeight + contentLayout.anchors.topMargin + contentLayout.anchors.bottomMargin
|
implicitHeight: contentLayout.implicitHeight + contentLayout.anchors.topMargin + contentLayout.anchors.bottomMargin
|
||||||
implicitWidth: 600 // contentLayout.implicitWidth + contentLayout.anchors.leftMargin + contentLayout.anchors.rightMargin
|
implicitWidth: 600 // contentLayout.implicitWidth + contentLayout.anchors.leftMargin + contentLayout.anchors.rightMargin
|
||||||
|
leftMargin: (mainWindow.width - root.implicitWidth) / 2
|
||||||
popupType: ApplicationWindow.PopupType.Banner
|
|
||||||
|
|
||||||
shouldShow: notification ? (notification.active && !notification.dismissed) : false
|
|
||||||
|
|
||||||
modal: false
|
modal: false
|
||||||
|
popupType: ApplicationWindow.PopupType.Banner
|
||||||
|
shouldShow: notification ? (notification.active && !notification.dismissed) : false
|
||||||
|
topMargin: 37
|
||||||
|
|
||||||
Action {
|
Action {
|
||||||
id: defaultDismissAction
|
id: defaultDismissAction
|
||||||
|
|
||||||
text: qsTr("OK")
|
text: qsTr("OK")
|
||||||
|
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
if (!root.notification) {
|
if (!root.notification) {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
root.notification.dismissed = true;
|
||||||
root.notification.dismissed = true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
id: contentLayout
|
id: contentLayout
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@ -63,170 +51,148 @@ Popup {
|
|||||||
Item {
|
Item {
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
clip: true
|
clip: true
|
||||||
implicitHeight: children[1].implicitHeight + children[1].anchors.topMargin + children[1].anchors.bottomMargin
|
implicitHeight: children[1].implicitHeight + children[1].anchors.topMargin + children[1].anchors.bottomMargin
|
||||||
implicitWidth: children[1].implicitWidth + children[1].anchors.leftMargin + children[1].anchors.rightMargin
|
implicitWidth: children[1].implicitWidth + children[1].anchors.leftMargin + children[1].anchors.rightMargin
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
width: parent.width + 10
|
anchors.top: parent.top
|
||||||
radius: ProtonStyle.banner_radius
|
|
||||||
color: {
|
color: {
|
||||||
if (!root.notification) {
|
if (!root.notification) {
|
||||||
return "transparent"
|
return "transparent";
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (root.notification.type) {
|
switch (root.notification.type) {
|
||||||
case Notification.NotificationType.Info:
|
case Notification.NotificationType.Info:
|
||||||
return root.colorScheme.signal_info
|
return root.colorScheme.signal_info;
|
||||||
case Notification.NotificationType.Success:
|
case Notification.NotificationType.Success:
|
||||||
return root.colorScheme.signal_success
|
return root.colorScheme.signal_success;
|
||||||
case Notification.NotificationType.Warning:
|
case Notification.NotificationType.Warning:
|
||||||
return root.colorScheme.signal_warning
|
return root.colorScheme.signal_warning;
|
||||||
case Notification.NotificationType.Danger:
|
case Notification.NotificationType.Danger:
|
||||||
return root.colorScheme.signal_danger
|
return root.colorScheme.signal_danger;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
radius: ProtonStyle.banner_radius
|
||||||
|
width: parent.width + 10
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
anchors.topMargin: 14
|
|
||||||
anchors.bottomMargin: 14
|
anchors.bottomMargin: 14
|
||||||
|
anchors.fill: parent
|
||||||
anchors.leftMargin: 16
|
anchors.leftMargin: 16
|
||||||
|
anchors.topMargin: 14
|
||||||
spacing: 8
|
spacing: 8
|
||||||
|
|
||||||
ColorImage {
|
ColorImage {
|
||||||
color: root.colorScheme.text_invert
|
|
||||||
width: 24
|
|
||||||
height: 24
|
|
||||||
|
|
||||||
sourceSize.width: 24
|
|
||||||
sourceSize.height: 24
|
|
||||||
|
|
||||||
Layout.preferredHeight: 24
|
Layout.preferredHeight: 24
|
||||||
Layout.preferredWidth: 24
|
Layout.preferredWidth: 24
|
||||||
|
color: root.colorScheme.text_invert
|
||||||
|
height: 24
|
||||||
source: {
|
source: {
|
||||||
if (!root.notification) {
|
if (!root.notification) {
|
||||||
return ""
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (root.notification.type) {
|
switch (root.notification.type) {
|
||||||
case Notification.NotificationType.Info:
|
case Notification.NotificationType.Info:
|
||||||
return "/qml/icons/ic-info-circle-filled.svg"
|
return "/qml/icons/ic-info-circle-filled.svg";
|
||||||
case Notification.NotificationType.Success:
|
case Notification.NotificationType.Success:
|
||||||
return "/qml/icons/ic-info-circle-filled.svg"
|
return "/qml/icons/ic-info-circle-filled.svg";
|
||||||
case Notification.NotificationType.Warning:
|
case Notification.NotificationType.Warning:
|
||||||
return "/qml/icons/ic-exclamation-circle-filled.svg"
|
return "/qml/icons/ic-exclamation-circle-filled.svg";
|
||||||
case Notification.NotificationType.Danger:
|
case Notification.NotificationType.Danger:
|
||||||
return "/qml/icons/ic-exclamation-circle-filled.svg"
|
return "/qml/icons/ic-exclamation-circle-filled.svg";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
sourceSize.height: 24
|
||||||
|
sourceSize.width: 24
|
||||||
|
width: 24
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
colorScheme: root.colorScheme
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
Layout.fillWidth: true
|
||||||
Layout.leftMargin: 16
|
Layout.leftMargin: 16
|
||||||
|
|
||||||
color: root.colorScheme.text_invert
|
color: root.colorScheme.text_invert
|
||||||
|
colorScheme: root.colorScheme
|
||||||
text: root.notification ? root.notification.description : ""
|
text: root.notification ? root.notification.description : ""
|
||||||
|
|
||||||
wrapMode: Text.WordWrap
|
wrapMode: Text.WordWrap
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
width: 1
|
|
||||||
color: {
|
color: {
|
||||||
if (!root.notification) {
|
if (!root.notification) {
|
||||||
return "transparent"
|
return "transparent";
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (root.notification.type) {
|
switch (root.notification.type) {
|
||||||
case Notification.NotificationType.Info:
|
case Notification.NotificationType.Info:
|
||||||
return root.colorScheme.signal_info_active
|
return root.colorScheme.signal_info_active;
|
||||||
case Notification.NotificationType.Success:
|
case Notification.NotificationType.Success:
|
||||||
return root.colorScheme.signal_success_active
|
return root.colorScheme.signal_success_active;
|
||||||
case Notification.NotificationType.Warning:
|
case Notification.NotificationType.Warning:
|
||||||
return root.colorScheme.signal_warning_active
|
return root.colorScheme.signal_warning_active;
|
||||||
case Notification.NotificationType.Danger:
|
case Notification.NotificationType.Danger:
|
||||||
return root.colorScheme.signal_danger_active
|
return root.colorScheme.signal_danger_active;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
width: 1
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
colorScheme: root.colorScheme
|
|
||||||
Layout.fillHeight: true
|
|
||||||
|
|
||||||
id: actionButton
|
id: actionButton
|
||||||
|
Layout.fillHeight: true
|
||||||
action: (root.notification && root.notification.action.length > 0) ? root.notification.action[0] : defaultDismissAction
|
action: (root.notification && root.notification.action.length > 0) ? root.notification.action[0] : defaultDismissAction
|
||||||
|
colorScheme: root.colorScheme
|
||||||
|
|
||||||
background: Item {
|
background: Item {
|
||||||
clip: true
|
clip: true
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
width: parent.width + 10
|
anchors.top: parent.top
|
||||||
radius: ProtonStyle.banner_radius
|
|
||||||
color: {
|
color: {
|
||||||
if (!root.notification) {
|
if (!root.notification) {
|
||||||
return "transparent"
|
return "transparent";
|
||||||
}
|
}
|
||||||
|
let norm;
|
||||||
var norm
|
let hover;
|
||||||
var hover
|
let active;
|
||||||
var active
|
|
||||||
|
|
||||||
switch (root.notification.type) {
|
switch (root.notification.type) {
|
||||||
case Notification.NotificationType.Info:
|
case Notification.NotificationType.Info:
|
||||||
norm = root.colorScheme.signal_info
|
norm = root.colorScheme.signal_info;
|
||||||
hover = root.colorScheme.signal_info_hover
|
hover = root.colorScheme.signal_info_hover;
|
||||||
active = root.colorScheme.signal_info_active
|
active = root.colorScheme.signal_info_active;
|
||||||
break;
|
break;
|
||||||
case Notification.NotificationType.Success:
|
case Notification.NotificationType.Success:
|
||||||
norm = root.colorScheme.signal_success
|
norm = root.colorScheme.signal_success;
|
||||||
hover = root.colorScheme.signal_success_hover
|
hover = root.colorScheme.signal_success_hover;
|
||||||
active = root.colorScheme.signal_success_active
|
active = root.colorScheme.signal_success_active;
|
||||||
break;
|
break;
|
||||||
case Notification.NotificationType.Warning:
|
case Notification.NotificationType.Warning:
|
||||||
norm = root.colorScheme.signal_warning
|
norm = root.colorScheme.signal_warning;
|
||||||
hover = root.colorScheme.signal_warning_hover
|
hover = root.colorScheme.signal_warning_hover;
|
||||||
active = root.colorScheme.signal_warning_active
|
active = root.colorScheme.signal_warning_active;
|
||||||
break;
|
break;
|
||||||
case Notification.NotificationType.Danger:
|
case Notification.NotificationType.Danger:
|
||||||
norm = root.colorScheme.signal_danger
|
norm = root.colorScheme.signal_danger;
|
||||||
hover = root.colorScheme.signal_danger_hover
|
hover = root.colorScheme.signal_danger_hover;
|
||||||
active = root.colorScheme.signal_danger_active
|
active = root.colorScheme.signal_danger_active;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (actionButton.down) {
|
if (actionButton.down) {
|
||||||
return active
|
return active;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (actionButton.enabled && (actionButton.highlighted || actionButton.hovered || actionButton.checked)) {
|
if (actionButton.enabled && (actionButton.highlighted || actionButton.hovered || actionButton.checked)) {
|
||||||
return hover
|
return hover;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (actionButton.loading) {
|
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
|
// Copyright (c) 2023 Proton AG
|
||||||
//
|
|
||||||
// This file is part of Proton Mail Bridge.
|
// This file is part of Proton Mail Bridge.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
// 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
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
// 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/>.
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import QtQml
|
import QtQml
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Window
|
import QtQuick.Window
|
||||||
import Qt.labs.platform
|
import Qt.labs.platform
|
||||||
|
|
||||||
import Proton
|
import Proton
|
||||||
import Notifications
|
import Notifications
|
||||||
|
|
||||||
QtObject {
|
QtObject {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
function bound(num, lowerLimit, upperLimit) {
|
property MainWindow _mainWindow: MainWindow {
|
||||||
return Math.max(lowerLimit, Math.min(upperLimit, num))
|
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 {
|
property Notifications _notifications: Notifications {
|
||||||
id: notifications
|
id: notifications
|
||||||
frontendMain: mainWindow
|
frontendMain: mainWindow
|
||||||
}
|
}
|
||||||
|
|
||||||
property NotificationFilter _trayNotificationFilter: NotificationFilter {
|
property NotificationFilter _trayNotificationFilter: NotificationFilter {
|
||||||
id: trayNotificationFilter
|
id: trayNotificationFilter
|
||||||
source: root._notifications ? root._notifications.all : undefined
|
source: root._notifications ? root._notifications.all : undefined
|
||||||
onTopmostChanged: {
|
|
||||||
if (topmost) {
|
onTopmostChanged: {
|
||||||
switch (topmost.type) {
|
if (topmost) {
|
||||||
case Notification.NotificationType.Danger:
|
switch (topmost.type) {
|
||||||
Backend.setErrorTrayIcon(topmost.brief, topmost.icon)
|
case Notification.NotificationType.Danger:
|
||||||
return
|
Backend.setErrorTrayIcon(topmost.brief, topmost.icon);
|
||||||
case Notification.NotificationType.Warning:
|
return;
|
||||||
Backend.setWarnTrayIcon(topmost.brief, topmost.icon)
|
case Notification.NotificationType.Warning:
|
||||||
return
|
Backend.setWarnTrayIcon(topmost.brief, topmost.icon);
|
||||||
case Notification.NotificationType.Info:
|
return;
|
||||||
Backend.setUpdateTrayIcon(topmost.brief, topmost.icon)
|
case Notification.NotificationType.Info:
|
||||||
return
|
Backend.setUpdateTrayIcon(topmost.brief, topmost.icon);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Backend.setNormalTrayIcon()
|
Backend.setNormalTrayIcon();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
property var title: Backend.appname
|
||||||
|
|
||||||
|
function bound(num, lowerLimit, upperLimit) {
|
||||||
property MainWindow _mainWindow: MainWindow {
|
return Math.max(lowerLimit, Math.min(upperLimit, num));
|
||||||
id: mainWindow
|
}
|
||||||
visible: false
|
function setColorScheme() {
|
||||||
|
if (Backend.colorSchemeName === "light")
|
||||||
title: root.title
|
ProtonStyle.currentStyle = ProtonStyle.lightStyle;
|
||||||
notifications: root._notifications
|
if (Backend.colorSchemeName === "dark")
|
||||||
|
ProtonStyle.currentStyle = ProtonStyle.darkStyle;
|
||||||
onVisibleChanged: {
|
|
||||||
Backend.dockIconVisible = visible
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: Backend
|
|
||||||
function onDiskCacheUnavailable() {
|
|
||||||
mainWindow.showAndRise()
|
|
||||||
}
|
|
||||||
function onColorSchemeNameChanged(scheme) { root.setColorScheme() }
|
|
||||||
|
|
||||||
function onHideMainWindow() {
|
|
||||||
mainWindow.hide();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
if (!Backend) {
|
if (!Backend) {
|
||||||
console.log("Backend not loaded")
|
console.log("Backend not loaded");
|
||||||
}
|
}
|
||||||
|
root.setColorScheme();
|
||||||
root.setColorScheme()
|
|
||||||
|
|
||||||
|
|
||||||
if (!Backend.users) {
|
if (!Backend.users) {
|
||||||
console.log("users not loaded")
|
console.log("users not loaded");
|
||||||
}
|
}
|
||||||
|
const c = Backend.users.count;
|
||||||
var c = Backend.users.count
|
const u = Backend.users.get(0);
|
||||||
var u = Backend.users.get(0)
|
|
||||||
// DEBUG
|
// DEBUG
|
||||||
if (c !== 0) {
|
if (c !== 0) {
|
||||||
console.log("users non zero", c)
|
console.log("users non zero", c);
|
||||||
console.log("first user", u )
|
console.log("first user", u);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (c === 0) {
|
if (c === 0) {
|
||||||
mainWindow.showAndRise()
|
mainWindow.showAndRise();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (u) {
|
if (u) {
|
||||||
if (c === 1 && (u.state === EUserState.SignedOut)) {
|
if (c === 1 && (u.state === EUserState.SignedOut)) {
|
||||||
mainWindow.showAndRise()
|
mainWindow.showAndRise();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Backend.guiReady();
|
||||||
Backend.guiReady()
|
if (Backend.showOnStartup || Backend.showSplashScreen) {
|
||||||
|
mainWindow.showAndRise();
|
||||||
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
|
// Copyright (c) 2023 Proton AG
|
||||||
//
|
|
||||||
// This file is part of Proton Mail Bridge.
|
// This file is part of Proton Mail Bridge.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
// 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
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
// 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/>.
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
|
|
||||||
import Proton
|
import Proton
|
||||||
|
|
||||||
SettingsView {
|
SettingsView {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
fillHeight: true
|
|
||||||
|
|
||||||
property var selectedAddress
|
property var selectedAddress
|
||||||
|
|
||||||
signal bugReportWasSent()
|
signal bugReportWasSent
|
||||||
|
|
||||||
Label {
|
function isValidEmail(text) {
|
||||||
text: qsTr("Report a problem")
|
const reEmail = /^[^@]+@[^@]+\.[A-Za-z]+\s*$/;
|
||||||
colorScheme: root.colorScheme
|
return reEmail.test(text);
|
||||||
type: Label.Heading
|
}
|
||||||
|
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 {
|
TextArea {
|
||||||
id: description
|
id: description
|
||||||
property int _minLength: 150
|
|
||||||
property int _maxLength: 800
|
property int _maxLength: 800
|
||||||
|
property int _minLength: 150
|
||||||
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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
KeyNavigation.priority: KeyNavigation.BeforeItem
|
KeyNavigation.priority: KeyNavigation.BeforeItem
|
||||||
KeyNavigation.tab: address
|
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
|
// set implicitHeight to explicit height because se don't
|
||||||
// want TextArea implicitHeight (which is height of all text)
|
// want TextArea implicitHeight (which is height of all text)
|
||||||
// to be considered in SettingsView internal scroll view
|
// to be considered in SettingsView internal scroll view
|
||||||
implicitHeight: height
|
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 {
|
TextField {
|
||||||
id: address
|
id: address
|
||||||
|
|
||||||
label: qsTr("Your contact email")
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
colorScheme: root.colorScheme
|
||||||
|
label: qsTr("Your contact email")
|
||||||
placeholderText: qsTr("e.g. jane.doe@protonmail.com")
|
placeholderText: qsTr("e.g. jane.doe@protonmail.com")
|
||||||
|
validator: function (str) {
|
||||||
validator: function(str) {
|
|
||||||
if (!isValidEmail(str)) {
|
if (!isValidEmail(str)) {
|
||||||
return qsTr("Enter valid email address")
|
return qsTr("Enter valid email address");
|
||||||
}
|
}
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TextField {
|
TextField {
|
||||||
id: emailClient
|
id: emailClient
|
||||||
|
|
||||||
label: qsTr("Your email client (including version)")
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
colorScheme: root.colorScheme
|
||||||
|
label: qsTr("Your email client (including version)")
|
||||||
placeholderText: qsTr("e.g. Apple Mail 14.0")
|
placeholderText: qsTr("e.g. Apple Mail 14.0")
|
||||||
|
validator: function (str) {
|
||||||
validator: function(str) {
|
|
||||||
if (str.length === 0) {
|
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 {
|
RowLayout {
|
||||||
CheckBox {
|
CheckBox {
|
||||||
id: includeLogs
|
id: includeLogs
|
||||||
text: qsTr("Include my recent logs")
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
checked: true
|
checked: true
|
||||||
|
colorScheme: root.colorScheme
|
||||||
|
text: qsTr("Include my recent logs")
|
||||||
}
|
}
|
||||||
Button {
|
Button {
|
||||||
Layout.leftMargin: 12
|
Layout.leftMargin: 12
|
||||||
text: qsTr("View logs")
|
|
||||||
secondary: true
|
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
|
secondary: true
|
||||||
|
text: qsTr("View logs")
|
||||||
|
|
||||||
onClicked: Qt.openUrlExternally(Backend.logsPath)
|
onClicked: Qt.openUrlExternally(Backend.logsPath)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TextEdit {
|
TextEdit {
|
||||||
text: qsTr("Reports are not end-to-end encrypted, please do not send any sensitive information.")
|
|
||||||
|
|
||||||
readOnly: true
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
color: root.colorScheme.text_weak
|
color: root.colorScheme.text_weak
|
||||||
font.family: ProtonStyle.font_family
|
font.family: ProtonStyle.font_family
|
||||||
font.weight: ProtonStyle.fontWeight_400
|
|
||||||
font.pixelSize: ProtonStyle.caption_font_size
|
|
||||||
font.letterSpacing: ProtonStyle.caption_letter_spacing
|
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
|
// No way to set lineHeight: ProtonStyle.caption_line_height
|
||||||
selectionColor: root.colorScheme.interaction_norm
|
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
|
wrapMode: Text.WordWrap
|
||||||
selectByMouse: true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
id: sendButton
|
id: sendButton
|
||||||
text: qsTr("Send")
|
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
enabled: !loading
|
enabled: !loading
|
||||||
|
text: qsTr("Send")
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
description.validate()
|
description.validate();
|
||||||
address.validate()
|
address.validate();
|
||||||
emailClient.validate()
|
emailClient.validate();
|
||||||
|
|
||||||
if (description.error || address.error || emailClient.error) {
|
if (description.error || address.error || emailClient.error) {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
submit();
|
||||||
submit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
|
function onBugReportSendSuccess() {
|
||||||
|
root.bugReportWasSent();
|
||||||
|
}
|
||||||
|
function onReportBugFinished() {
|
||||||
|
sendButton.loading = false;
|
||||||
|
}
|
||||||
|
|
||||||
target: Backend
|
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
|
// Copyright (c) 2023 Proton AG
|
||||||
//
|
|
||||||
// This file is part of Proton Mail Bridge.
|
// This file is part of Proton Mail Bridge.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
// 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
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
// 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/>.
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Controls.impl
|
import QtQuick.Controls.impl
|
||||||
|
|
||||||
import Proton
|
import Proton
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
|
property int _margin: 24
|
||||||
property ColorScheme colorScheme
|
property ColorScheme colorScheme
|
||||||
property string title
|
|
||||||
property string hostname
|
property string hostname
|
||||||
property string port
|
|
||||||
property string username
|
|
||||||
property string password
|
property string password
|
||||||
|
property string port
|
||||||
property string security
|
property string security
|
||||||
|
property string title
|
||||||
implicitWidth: 304
|
property string username
|
||||||
implicitHeight: content.height + 2*root._margin
|
|
||||||
|
|
||||||
color: root.colorScheme.background_norm
|
color: root.colorScheme.background_norm
|
||||||
|
implicitHeight: content.height + 2 * root._margin
|
||||||
|
implicitWidth: 304
|
||||||
radius: ProtonStyle.card_radius
|
radius: ProtonStyle.card_radius
|
||||||
|
|
||||||
property int _margin: 24
|
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: content
|
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
|
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 {
|
Label {
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
text: root.title
|
text: root.title
|
||||||
type: Label.Body_semibold
|
type: Label.Body_semibold
|
||||||
}
|
}
|
||||||
|
ConfigurationItem {
|
||||||
ConfigurationItem{ colorScheme: root.colorScheme; label: qsTr("Hostname") ; value: root.hostname }
|
colorScheme: root.colorScheme
|
||||||
ConfigurationItem{ colorScheme: root.colorScheme; label: qsTr("Port") ; value: root.port }
|
label: qsTr("Hostname")
|
||||||
ConfigurationItem{ colorScheme: root.colorScheme; label: qsTr("Username") ; value: root.username }
|
value: root.hostname
|
||||||
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("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
|
// Copyright (c) 2023 Proton AG
|
||||||
//
|
|
||||||
// This file is part of Proton Mail Bridge.
|
// This file is part of Proton Mail Bridge.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
// 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
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
// 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/>.
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Controls.impl
|
import QtQuick.Controls.impl
|
||||||
|
|
||||||
import Proton
|
import Proton
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
property var colorScheme
|
property var colorScheme
|
||||||
property string label
|
property string label
|
||||||
property string value
|
property string value
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
implicitHeight: children[0].implicitHeight
|
implicitHeight: children[0].implicitHeight
|
||||||
implicitWidth: children[0].implicitWidth
|
implicitWidth: children[0].implicitWidth
|
||||||
|
|
||||||
@ -47,45 +41,42 @@ Item {
|
|||||||
}
|
}
|
||||||
TextEdit {
|
TextEdit {
|
||||||
id: valueText
|
id: valueText
|
||||||
text: root.value
|
Layout.fillWidth: true
|
||||||
color: root.colorScheme.text_weak
|
color: root.colorScheme.text_weak
|
||||||
readOnly: true
|
readOnly: true
|
||||||
selectByMouse: true
|
|
||||||
selectByKeyboard: true
|
selectByKeyboard: true
|
||||||
|
selectByMouse: true
|
||||||
selectionColor: root.colorScheme.text_weak
|
selectionColor: root.colorScheme.text_weak
|
||||||
|
text: root.value
|
||||||
wrapMode: Text.WrapAnywhere
|
wrapMode: Text.WrapAnywhere
|
||||||
Layout.fillWidth: true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
|
|
||||||
ColorImage {
|
ColorImage {
|
||||||
source: "/qml/icons/ic-copy.svg"
|
|
||||||
color: root.colorScheme.text_norm
|
color: root.colorScheme.text_norm
|
||||||
height: root.colorScheme.body_font_size
|
height: root.colorScheme.body_font_size
|
||||||
|
source: "/qml/icons/ic-copy.svg"
|
||||||
sourceSize.height: root.colorScheme.body_font_size
|
sourceSize.height: root.colorScheme.body_font_size
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
onClicked : {
|
|
||||||
valueText.select(0, valueText.length)
|
onClicked: {
|
||||||
valueText.copy()
|
valueText.select(0, valueText.length);
|
||||||
valueText.deselect()
|
valueText.copy();
|
||||||
|
valueText.deselect();
|
||||||
}
|
}
|
||||||
onPressed: parent.scale = 0.90
|
onPressed: parent.scale = 0.90
|
||||||
onReleased: parent.scale = 1
|
onReleased: parent.scale = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
height: 1
|
|
||||||
color: root.colorScheme.border_norm
|
color: root.colorScheme.border_norm
|
||||||
|
height: 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,155 +1,138 @@
|
|||||||
// Copyright (c) 2023 Proton AG
|
// Copyright (c) 2023 Proton AG
|
||||||
//
|
|
||||||
// This file is part of Proton Mail Bridge.
|
// This file is part of Proton Mail Bridge.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
// 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
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
// 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/>.
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Controls.impl
|
import QtQuick.Controls.impl
|
||||||
|
|
||||||
import Proton
|
import Proton
|
||||||
|
|
||||||
SettingsView {
|
SettingsView {
|
||||||
id: root
|
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
|
fillHeight: false
|
||||||
|
|
||||||
|
onVisibleChanged: {
|
||||||
|
root.setDefaultValues();
|
||||||
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
|
Layout.fillWidth: true
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
text: qsTr("Connection mode")
|
text: qsTr("Connection mode")
|
||||||
type: Label.Heading
|
type: Label.Heading
|
||||||
Layout.fillWidth: true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
color: root.colorScheme.text_weak
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
text: qsTr("Change the protocol Bridge and the email client use to connect for IMAP and SMTP.")
|
text: qsTr("Change the protocol Bridge and the email client use to connect for IMAP and SMTP.")
|
||||||
type: Label.Body
|
type: Label.Body
|
||||||
color: root.colorScheme.text_weak
|
|
||||||
Layout.fillWidth: true
|
|
||||||
wrapMode: Text.WordWrap
|
wrapMode: Text.WordWrap
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
spacing: 16
|
spacing: 16
|
||||||
|
|
||||||
ButtonGroup{ id: imapProtocolSelection }
|
ButtonGroup {
|
||||||
|
id: imapProtocolSelection
|
||||||
|
}
|
||||||
Label {
|
Label {
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
text: qsTr("IMAP connection")
|
text: qsTr("IMAP connection")
|
||||||
}
|
}
|
||||||
|
|
||||||
RadioButton {
|
RadioButton {
|
||||||
id: imapSSLButton
|
id: imapSSLButton
|
||||||
colorScheme: root.colorScheme
|
|
||||||
ButtonGroup.group: imapProtocolSelection
|
ButtonGroup.group: imapProtocolSelection
|
||||||
|
colorScheme: root.colorScheme
|
||||||
text: qsTr("SSL")
|
text: qsTr("SSL")
|
||||||
}
|
}
|
||||||
|
|
||||||
RadioButton {
|
RadioButton {
|
||||||
id: imapSTARTTLSButton
|
id: imapSTARTTLSButton
|
||||||
colorScheme: root.colorScheme
|
|
||||||
ButtonGroup.group: imapProtocolSelection
|
ButtonGroup.group: imapProtocolSelection
|
||||||
|
colorScheme: root.colorScheme
|
||||||
text: qsTr("STARTTLS")
|
text: qsTr("STARTTLS")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
height: 1
|
|
||||||
color: root.colorScheme.border_weak
|
color: root.colorScheme.border_weak
|
||||||
|
height: 1
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
spacing: 16
|
spacing: 16
|
||||||
|
|
||||||
ButtonGroup{ id: smtpProtocolSelection }
|
ButtonGroup {
|
||||||
|
id: smtpProtocolSelection
|
||||||
|
}
|
||||||
Label {
|
Label {
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
text: qsTr("SMTP connection")
|
text: qsTr("SMTP connection")
|
||||||
}
|
}
|
||||||
|
|
||||||
RadioButton {
|
RadioButton {
|
||||||
id: smtpSSLButton
|
id: smtpSSLButton
|
||||||
colorScheme: root.colorScheme
|
|
||||||
ButtonGroup.group: smtpProtocolSelection
|
ButtonGroup.group: smtpProtocolSelection
|
||||||
|
colorScheme: root.colorScheme
|
||||||
text: qsTr("SSL")
|
text: qsTr("SSL")
|
||||||
}
|
}
|
||||||
|
|
||||||
RadioButton {
|
RadioButton {
|
||||||
id: smtpSTARTTLSButton
|
id: smtpSTARTTLSButton
|
||||||
colorScheme: root.colorScheme
|
|
||||||
ButtonGroup.group: smtpProtocolSelection
|
ButtonGroup.group: smtpProtocolSelection
|
||||||
|
colorScheme: root.colorScheme
|
||||||
text: qsTr("STARTTLS")
|
text: qsTr("STARTTLS")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
height: 1
|
|
||||||
color: root.colorScheme.border_weak
|
color: root.colorScheme.border_weak
|
||||||
|
height: 1
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
spacing: 12
|
spacing: 12
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
id: submitButton
|
id: submitButton
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
text: qsTr("Save")
|
|
||||||
onClicked: {
|
|
||||||
submitButton.loading = true
|
|
||||||
root.submit()
|
|
||||||
}
|
|
||||||
|
|
||||||
enabled: (!loading) && ((imapSSLButton.checked !== Backend.useSSLForIMAP) || (smtpSSLButton.checked !== Backend.useSSLForSMTP))
|
enabled: (!loading) && ((imapSSLButton.checked !== Backend.useSSLForIMAP) || (smtpSSLButton.checked !== Backend.useSSLForSMTP))
|
||||||
}
|
text: qsTr("Save")
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
submitButton.loading = true;
|
||||||
|
root.submit();
|
||||||
|
}
|
||||||
|
}
|
||||||
Button {
|
Button {
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
text: qsTr("Cancel")
|
|
||||||
onClicked: root.back()
|
|
||||||
secondary: true
|
secondary: true
|
||||||
}
|
text: qsTr("Cancel")
|
||||||
|
|
||||||
|
onClicked: root.back()
|
||||||
|
}
|
||||||
Connections {
|
Connections {
|
||||||
target: Backend
|
|
||||||
|
|
||||||
function onChangeMailServerSettingsFinished() {
|
function onChangeMailServerSettingsFinished() {
|
||||||
submitButton.loading = false
|
submitButton.loading = false;
|
||||||
root.back()
|
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
|
// Copyright (c) 2023 Proton AG
|
||||||
//
|
|
||||||
// This file is part of Proton Mail Bridge.
|
// This file is part of Proton Mail Bridge.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
// 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
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
// 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/>.
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
|
|
||||||
import Proton
|
import Proton
|
||||||
import Notifications
|
import Notifications
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
property ColorScheme colorScheme
|
|
||||||
|
|
||||||
|
property ColorScheme colorScheme
|
||||||
property var notifications
|
property var notifications
|
||||||
|
|
||||||
|
signal closeWindow
|
||||||
|
signal quitBridge
|
||||||
signal showSetupGuide(var user, string address)
|
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 {
|
RowLayout {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@ -38,13 +64,13 @@ Item {
|
|||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: leftBar
|
id: leftBar
|
||||||
|
|
||||||
property ColorScheme colorScheme: root.colorScheme.prominent
|
property ColorScheme colorScheme: root.colorScheme.prominent
|
||||||
|
|
||||||
Layout.minimumWidth: 264
|
|
||||||
Layout.maximumWidth: 320
|
|
||||||
Layout.preferredWidth: 320
|
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
|
Layout.maximumWidth: 320
|
||||||
|
Layout.minimumWidth: 264
|
||||||
|
Layout.preferredWidth: 320
|
||||||
color: colorScheme.background_norm
|
color: colorScheme.background_norm
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
@ -52,24 +78,21 @@ Item {
|
|||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
id:topLeftBar
|
id: topLeftBar
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.minimumHeight: 60
|
|
||||||
Layout.maximumHeight: 60
|
Layout.maximumHeight: 60
|
||||||
|
Layout.minimumHeight: 60
|
||||||
Layout.preferredHeight: 60
|
Layout.preferredHeight: 60
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
Status {
|
Status {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
Layout.bottomMargin: 17
|
||||||
Layout.leftMargin: 16
|
Layout.leftMargin: 16
|
||||||
Layout.topMargin: 24
|
Layout.topMargin: 24
|
||||||
Layout.bottomMargin: 17
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
|
|
||||||
colorScheme: leftBar.colorScheme
|
colorScheme: leftBar.colorScheme
|
||||||
notifications: root.notifications
|
|
||||||
|
|
||||||
notificationWhitelist: Notifications.Group.Connection | Notifications.Group.ForceUpdate
|
notificationWhitelist: Notifications.Group.Connection | Notifications.Group.ForceUpdate
|
||||||
|
notifications: root.notifications
|
||||||
}
|
}
|
||||||
|
|
||||||
// just a placeholder
|
// just a placeholder
|
||||||
@ -77,47 +100,38 @@ Item {
|
|||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
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.bottomMargin: 9
|
||||||
|
Layout.maximumHeight: 36
|
||||||
|
Layout.maximumWidth: 36
|
||||||
|
Layout.minimumHeight: 36
|
||||||
|
Layout.minimumWidth: 36
|
||||||
|
Layout.preferredHeight: 36
|
||||||
|
Layout.preferredWidth: 36
|
||||||
Layout.rightMargin: 4
|
Layout.rightMargin: 4
|
||||||
|
Layout.topMargin: 16
|
||||||
|
colorScheme: leftBar.colorScheme
|
||||||
horizontalPadding: 0
|
horizontalPadding: 0
|
||||||
|
|
||||||
icon.source: "/qml/icons/ic-question-circle.svg"
|
icon.source: "/qml/icons/ic-question-circle.svg"
|
||||||
|
|
||||||
onClicked: rightContent.showHelpView()
|
onClicked: rightContent.showHelpView()
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
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.bottomMargin: 9
|
||||||
|
Layout.maximumHeight: 36
|
||||||
|
Layout.maximumWidth: 36
|
||||||
|
Layout.minimumHeight: 36
|
||||||
|
Layout.minimumWidth: 36
|
||||||
|
Layout.preferredHeight: 36
|
||||||
|
Layout.preferredWidth: 36
|
||||||
Layout.rightMargin: 4
|
Layout.rightMargin: 4
|
||||||
|
Layout.topMargin: 16
|
||||||
|
colorScheme: leftBar.colorScheme
|
||||||
horizontalPadding: 0
|
horizontalPadding: 0
|
||||||
|
|
||||||
icon.source: "/qml/icons/ic-cog-wheel.svg"
|
icon.source: "/qml/icons/ic-cog-wheel.svg"
|
||||||
|
|
||||||
onClicked: rightContent.showGeneralSettings()
|
onClicked: rightContent.showGeneralSettings()
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
id: dotMenuButton
|
id: dotMenuButton
|
||||||
Layout.bottomMargin: 9
|
Layout.bottomMargin: 9
|
||||||
@ -134,7 +148,7 @@ Item {
|
|||||||
icon.source: "/qml/icons/ic-three-dots-vertical.svg"
|
icon.source: "/qml/icons/ic-three-dots-vertical.svg"
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
dotMenu.open()
|
dotMenu.open();
|
||||||
}
|
}
|
||||||
|
|
||||||
Menu {
|
Menu {
|
||||||
@ -143,332 +157,319 @@ Item {
|
|||||||
modal: true
|
modal: true
|
||||||
y: dotMenuButton.Layout.preferredHeight + dotMenuButton.Layout.bottomMargin
|
y: dotMenuButton.Layout.preferredHeight + dotMenuButton.Layout.bottomMargin
|
||||||
|
|
||||||
|
onClosed: {
|
||||||
|
parent.checked = false;
|
||||||
|
}
|
||||||
|
onOpened: {
|
||||||
|
parent.checked = true;
|
||||||
|
}
|
||||||
|
|
||||||
MenuItem {
|
MenuItem {
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
text: qsTr("Close window")
|
text: qsTr("Close window")
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
root.closeWindow()
|
root.closeWindow();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MenuItem {
|
MenuItem {
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
text: qsTr("Quit Bridge")
|
text: qsTr("Quit Bridge")
|
||||||
onClicked: {
|
|
||||||
root.quitBridge()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onClosed: {
|
onClicked: {
|
||||||
parent.checked = false
|
root.quitBridge();
|
||||||
}
|
}
|
||||||
onOpened: {
|
|
||||||
parent.checked = true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Item {
|
||||||
Item {implicitHeight:10}
|
implicitHeight: 10
|
||||||
|
}
|
||||||
|
|
||||||
// Separator line
|
// Separator line
|
||||||
Rectangle {
|
Rectangle {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.minimumHeight: 1
|
|
||||||
Layout.maximumHeight: 1
|
Layout.maximumHeight: 1
|
||||||
|
Layout.minimumHeight: 1
|
||||||
color: leftBar.colorScheme.border_weak
|
color: leftBar.colorScheme.border_weak
|
||||||
}
|
}
|
||||||
|
|
||||||
ListView {
|
ListView {
|
||||||
id: accounts
|
id: accounts
|
||||||
|
|
||||||
property var _topBottomMargins: 24
|
|
||||||
property var _leftRightMargins: 16
|
property var _leftRightMargins: 16
|
||||||
|
property var _topBottomMargins: 24
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.bottomMargin: accounts._topBottomMargins
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
|
Layout.fillWidth: true
|
||||||
Layout.leftMargin: accounts._leftRightMargins
|
Layout.leftMargin: accounts._leftRightMargins
|
||||||
Layout.rightMargin: accounts._leftRightMargins
|
Layout.rightMargin: accounts._leftRightMargins
|
||||||
Layout.topMargin: accounts._topBottomMargins
|
Layout.topMargin: accounts._topBottomMargins
|
||||||
Layout.bottomMargin: accounts._topBottomMargins
|
|
||||||
|
|
||||||
spacing: 12
|
|
||||||
clip: true
|
|
||||||
boundsBehavior: Flickable.StopAtBounds
|
boundsBehavior: Flickable.StopAtBounds
|
||||||
|
clip: true
|
||||||
|
model: Backend.users
|
||||||
|
spacing: 12
|
||||||
|
|
||||||
header: Rectangle {
|
delegate: Item {
|
||||||
height: headerLabel.height+16
|
implicitHeight: children[0].implicitHeight + children[0].anchors.topMargin + children[0].anchors.bottomMargin
|
||||||
// color: ProtonStyle.transparent
|
implicitWidth: children[0].implicitWidth + children[0].anchors.leftMargin + children[0].anchors.rightMargin
|
||||||
Label{
|
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
|
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
|
id: headerLabel
|
||||||
|
colorScheme: leftBar.colorScheme
|
||||||
text: qsTr("Accounts")
|
text: qsTr("Accounts")
|
||||||
type: Label.LabelType.Body
|
type: Label.LabelType.Body
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
highlight: Rectangle {
|
highlight: Rectangle {
|
||||||
color: leftBar.colorScheme.interaction_default_active
|
color: leftBar.colorScheme.interaction_default_active
|
||||||
radius: ProtonStyle.account_row_radius
|
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
|
// Separator
|
||||||
Rectangle {
|
Rectangle {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.minimumHeight: 1
|
|
||||||
Layout.maximumHeight: 1
|
Layout.maximumHeight: 1
|
||||||
|
Layout.minimumHeight: 1
|
||||||
color: leftBar.colorScheme.border_weak
|
color: leftBar.colorScheme.border_weak
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: bottomLeftBar
|
id: bottomLeftBar
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.minimumHeight: 52
|
|
||||||
Layout.maximumHeight: 52
|
Layout.maximumHeight: 52
|
||||||
|
Layout.minimumHeight: 52
|
||||||
Layout.preferredHeight: 52
|
Layout.preferredHeight: 52
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
colorScheme: leftBar.colorScheme
|
|
||||||
width: 36
|
|
||||||
height: 36
|
|
||||||
|
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.top: parent.top
|
|
||||||
|
|
||||||
anchors.leftMargin: 16
|
anchors.leftMargin: 16
|
||||||
|
anchors.top: parent.top
|
||||||
anchors.topMargin: 7
|
anchors.topMargin: 7
|
||||||
|
colorScheme: leftBar.colorScheme
|
||||||
|
height: 36
|
||||||
horizontalPadding: 0
|
horizontalPadding: 0
|
||||||
|
|
||||||
icon.source: "/qml/icons/ic-plus.svg"
|
icon.source: "/qml/icons/ic-plus.svg"
|
||||||
|
width: 36
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
signIn.username = ""
|
signIn.username = "";
|
||||||
rightContent.showSignIn()
|
rightContent.showSignIn();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Rectangle {
|
||||||
Rectangle { // right content background
|
Layout.fillHeight: true // right content background
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.fillHeight: true
|
|
||||||
|
|
||||||
color: colorScheme.background_norm
|
color: colorScheme.background_norm
|
||||||
|
|
||||||
StackLayout {
|
StackLayout {
|
||||||
id: rightContent
|
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
|
anchors.fill: parent
|
||||||
|
|
||||||
AccountView { // 0
|
AccountView {
|
||||||
|
// 0
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
notifications: root.notifications
|
notifications: root.notifications
|
||||||
user: {
|
user: {
|
||||||
if (accounts.currentIndex < 0) return undefined
|
if (accounts.currentIndex < 0)
|
||||||
if (Backend.users.count == 0) return undefined
|
return undefined;
|
||||||
return Backend.users.get(accounts.currentIndex)
|
if (Backend.users.count === 0)
|
||||||
|
return undefined;
|
||||||
|
return Backend.users.get(accounts.currentIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
onShowSetupGuide: function (user, address) {
|
||||||
|
root.showSetupGuide(user, address);
|
||||||
}
|
}
|
||||||
onShowSignIn: {
|
onShowSignIn: {
|
||||||
var user = this.user
|
const user = this.user;
|
||||||
signIn.username = user ? user.primaryEmailOrUsername() : ""
|
signIn.username = user ? user.primaryEmailOrUsername() : "";
|
||||||
rightContent.showSignIn()
|
rightContent.showSignIn();
|
||||||
}
|
|
||||||
onShowSetupGuide: function(user, address) {
|
|
||||||
root.showSetupGuide(user,address)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
GridLayout {
|
||||||
GridLayout { // 1 Sign In
|
// 1 Sign In
|
||||||
columns: 2
|
columns: 2
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
id: backButton
|
id: backButton
|
||||||
|
Layout.alignment: Qt.AlignTop
|
||||||
Layout.leftMargin: 18
|
Layout.leftMargin: 18
|
||||||
Layout.topMargin: 10
|
Layout.topMargin: 10
|
||||||
Layout.alignment: Qt.AlignTop
|
|
||||||
|
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
onClicked: {
|
horizontalPadding: 8
|
||||||
signIn.abort()
|
|
||||||
rightContent.showAccount()
|
|
||||||
}
|
|
||||||
icon.source: "/qml/icons/ic-arrow-left.svg"
|
icon.source: "/qml/icons/ic-arrow-left.svg"
|
||||||
secondary: true
|
secondary: true
|
||||||
horizontalPadding: 8
|
|
||||||
}
|
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
signIn.abort();
|
||||||
|
rightContent.showAccount();
|
||||||
|
}
|
||||||
|
}
|
||||||
SignIn {
|
SignIn {
|
||||||
id: signIn
|
id: signIn
|
||||||
Layout.topMargin: 68
|
|
||||||
Layout.leftMargin: 80 - backButton.width - 18
|
|
||||||
Layout.rightMargin: 80
|
|
||||||
Layout.bottomMargin: 68
|
Layout.bottomMargin: 68
|
||||||
Layout.preferredWidth: 320
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.fillHeight: 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
|
colorScheme: root.colorScheme
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
GeneralSettings {
|
||||||
GeneralSettings { // 2
|
// 2
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
notifications: root.notifications
|
notifications: root.notifications
|
||||||
|
|
||||||
onBack: {
|
onBack: {
|
||||||
rightContent.showAccount()
|
rightContent.showAccount();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
KeychainSettings {
|
||||||
KeychainSettings { // 3
|
// 3
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
|
|
||||||
onBack: {
|
onBack: {
|
||||||
rightContent.showGeneralSettings()
|
rightContent.showGeneralSettings();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
PortSettings {
|
||||||
PortSettings { // 4
|
// 4
|
||||||
colorScheme: root.colorScheme
|
|
||||||
notifications: root.notifications
|
|
||||||
onBack: {
|
|
||||||
rightContent.showGeneralSettings()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ConnectionModeSettings { // 5
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
|
|
||||||
onBack: {
|
|
||||||
rightContent.showGeneralSettings()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LocalCacheSettings { // 6
|
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
notifications: root.notifications
|
notifications: root.notifications
|
||||||
|
|
||||||
onBack: {
|
onBack: {
|
||||||
rightContent.showGeneralSettings()
|
rightContent.showGeneralSettings();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ConnectionModeSettings {
|
||||||
HelpView { // 7
|
// 5
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
|
|
||||||
onBack: {
|
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
|
id: bugReport
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
selectedAddress: {
|
selectedAddress: {
|
||||||
if (accounts.currentIndex < 0) return ""
|
if (accounts.currentIndex < 0)
|
||||||
if (Backend.users.count == 0) return ""
|
return "";
|
||||||
var user = Backend.users.get(accounts.currentIndex)
|
if (Backend.users.count === 0)
|
||||||
if (!user) return ""
|
return "";
|
||||||
return user.addresses[0]
|
const user = Backend.users.get(accounts.currentIndex);
|
||||||
|
if (!user)
|
||||||
|
return "";
|
||||||
|
return user.addresses[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
onBack: {
|
onBack: {
|
||||||
rightContent.showHelpView()
|
rightContent.showHelpView();
|
||||||
}
|
}
|
||||||
|
|
||||||
onBugReportWasSent: {
|
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 {
|
Connections {
|
||||||
target: Backend
|
function onLoginAlreadyLoggedIn(index) {
|
||||||
|
rightContent.showAccount(index);
|
||||||
|
}
|
||||||
|
function onLoginFinished(index) {
|
||||||
|
rightContent.showAccount(index);
|
||||||
|
}
|
||||||
|
|
||||||
function onLoginFinished(index) { rightContent.showAccount(index) }
|
target: Backend
|
||||||
function onLoginAlreadyLoggedIn(index) { rightContent.showAccount(index) }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
// Copyright (c) 2023 Proton AG
|
||||||
//
|
|
||||||
// This file is part of Proton Mail Bridge.
|
// This file is part of Proton Mail Bridge.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
// 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
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
// 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/>.
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
|
|
||||||
import "."
|
import "."
|
||||||
import "./Proton"
|
import "Proton"
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
property var target: parent
|
property var target: parent
|
||||||
|
|
||||||
x: target.x
|
|
||||||
y: target.y
|
|
||||||
width: target.width
|
|
||||||
height: target.height
|
|
||||||
|
|
||||||
color: "transparent"
|
|
||||||
border.color: "red"
|
border.color: "red"
|
||||||
border.width: 1
|
border.width: 1
|
||||||
|
color: "transparent"
|
||||||
|
height: target.height
|
||||||
|
width: target.width
|
||||||
|
x: target.x
|
||||||
|
y: target.y
|
||||||
//z: parent.z - 1
|
//z: parent.z - 1
|
||||||
z: 10000000
|
z: 10000000
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
text: parent.width + "x" + parent.height
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
color: "black"
|
color: "black"
|
||||||
colorScheme: ProtonStyle.currentStyle
|
colorScheme: ProtonStyle.currentStyle
|
||||||
|
text: parent.width + "x" + parent.height
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: target.implicitWidth
|
|
||||||
height: target.implicitHeight
|
|
||||||
|
|
||||||
color: "transparent"
|
|
||||||
border.color: "green"
|
border.color: "green"
|
||||||
border.width: 1
|
border.width: 1
|
||||||
|
color: "transparent"
|
||||||
|
height: target.implicitHeight
|
||||||
|
width: target.implicitWidth
|
||||||
//z: parent.z - 1
|
//z: parent.z - 1
|
||||||
z: 10000000
|
z: 10000000
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,25 +1,19 @@
|
|||||||
// Copyright (c) 2023 Proton AG
|
// Copyright (c) 2023 Proton AG
|
||||||
//
|
|
||||||
// This file is part of Proton Mail Bridge.
|
// This file is part of Proton Mail Bridge.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
// 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
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
// 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/>.
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Controls.impl
|
import QtQuick.Controls.impl
|
||||||
|
|
||||||
import Proton
|
import Proton
|
||||||
|
|
||||||
SettingsView {
|
SettingsView {
|
||||||
@ -31,144 +25,138 @@ SettingsView {
|
|||||||
fillHeight: false
|
fillHeight: false
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
|
Layout.fillWidth: true
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
text: qsTr("Settings")
|
text: qsTr("Settings")
|
||||||
type: Label.Heading
|
type: Label.Heading
|
||||||
Layout.fillWidth: true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SettingsItem {
|
SettingsItem {
|
||||||
id: autoUpdate
|
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
|
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 {
|
SettingsItem {
|
||||||
id: autostart
|
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
|
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 {
|
SettingsItem {
|
||||||
id: beta
|
id: beta
|
||||||
colorScheme: root.colorScheme
|
Layout.fillWidth: true
|
||||||
text: qsTr("Beta access")
|
|
||||||
description: qsTr("Be among the first to try new features.")
|
|
||||||
type: SettingsItem.Toggle
|
|
||||||
checked: Backend.isBetaEnabled
|
checked: Backend.isBetaEnabled
|
||||||
|
colorScheme: root.colorScheme
|
||||||
|
description: qsTr("Be among the first to try new features.")
|
||||||
|
text: qsTr("Beta access")
|
||||||
|
type: SettingsItem.Toggle
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (!beta.checked) {
|
if (!beta.checked) {
|
||||||
root.notifications.askEnableBeta()
|
root.notifications.askEnableBeta();
|
||||||
} else {
|
} else {
|
||||||
Backend.toggleBeta(false)
|
Backend.toggleBeta(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
ColorImage {
|
ColorImage {
|
||||||
Layout.alignment: Qt.AlignCenter
|
Layout.alignment: Qt.AlignCenter
|
||||||
|
|
||||||
source: root._isAdvancedShown ? "/qml/icons/ic-chevron-down.svg" : "/qml/icons/ic-chevron-right.svg"
|
|
||||||
color: root.colorScheme.interaction_norm
|
color: root.colorScheme.interaction_norm
|
||||||
height: root.colorScheme.body_font_size
|
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
|
sourceSize.height: root.colorScheme.body_font_size
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
||||||
onClicked: root._isAdvancedShown = !root._isAdvancedShown
|
onClicked: root._isAdvancedShown = !root._isAdvancedShown
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
id: advSettLabel
|
id: advSettLabel
|
||||||
|
color: root.colorScheme.interaction_norm
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
text: qsTr("Advanced settings")
|
text: qsTr("Advanced settings")
|
||||||
color: root.colorScheme.interaction_norm
|
|
||||||
type: Label.Body
|
type: Label.Body
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
||||||
onClicked: root._isAdvancedShown = !root._isAdvancedShown
|
onClicked: root._isAdvancedShown = !root._isAdvancedShown
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SettingsItem {
|
SettingsItem {
|
||||||
id: keychains
|
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
|
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 {
|
SettingsItem {
|
||||||
id: doh
|
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
|
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 {
|
SettingsItem {
|
||||||
id: darkMode
|
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
|
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 {
|
SettingsItem {
|
||||||
id: allMail
|
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
|
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 {
|
SettingsItem {
|
||||||
id: telemetry
|
id: telemetry
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
@ -181,73 +169,68 @@ SettingsView {
|
|||||||
|
|
||||||
onClicked: Backend.toggleIsTelemetryDisabled(telemetry.checked)
|
onClicked: Backend.toggleIsTelemetryDisabled(telemetry.checked)
|
||||||
}
|
}
|
||||||
|
|
||||||
SettingsItem {
|
SettingsItem {
|
||||||
id: ports
|
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
|
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 {
|
SettingsItem {
|
||||||
id: imap
|
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
|
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 {
|
SettingsItem {
|
||||||
id: cache
|
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
|
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 {
|
SettingsItem {
|
||||||
id: exportTLSCertificates
|
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
|
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 {
|
SettingsItem {
|
||||||
id: reset
|
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
|
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
|
// Copyright (c) 2023 Proton AG
|
||||||
//
|
|
||||||
// This file is part of Proton Mail Bridge.
|
// This file is part of Proton Mail Bridge.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
// 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
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
// 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/>.
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
|
|
||||||
import Proton
|
import Proton
|
||||||
|
|
||||||
SettingsView {
|
SettingsView {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
fillHeight: true
|
fillHeight: true
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
|
Layout.fillWidth: true
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
text: qsTr("Help")
|
text: qsTr("Help")
|
||||||
type: Label.Heading
|
type: Label.Heading
|
||||||
Layout.fillWidth: true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SettingsItem {
|
SettingsItem {
|
||||||
id: setupPage
|
id: setupPage
|
||||||
colorScheme: root.colorScheme
|
Layout.fillWidth: true
|
||||||
text: qsTr("Installation and setup")
|
|
||||||
actionText: qsTr("Go to help topics")
|
|
||||||
actionIcon: "/qml/icons/ic-external-link.svg"
|
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.")
|
description: qsTr("Get help setting up your client with our instructions and FAQs.")
|
||||||
|
text: qsTr("Installation and setup")
|
||||||
type: SettingsItem.PrimaryButton
|
type: SettingsItem.PrimaryButton
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
Backend.notifyKBArticleClicked("https://proton.me/support/bridge");
|
Backend.notifyKBArticleClicked("https://proton.me/support/bridge");
|
||||||
Qt.openUrlExternally("https://proton.me/support/bridge")}
|
Qt.openUrlExternally("https://proton.me/support/bridge");
|
||||||
|
}
|
||||||
Layout.fillWidth: true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SettingsItem {
|
SettingsItem {
|
||||||
id: checkUpdates
|
id: checkUpdates
|
||||||
colorScheme: root.colorScheme
|
Layout.fillWidth: true
|
||||||
text: qsTr("Updates")
|
|
||||||
actionText: qsTr("Check now")
|
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.")
|
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
|
type: SettingsItem.Button
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
checkUpdates.loading = true
|
checkUpdates.loading = true;
|
||||||
Backend.checkUpdates()
|
Backend.checkUpdates();
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
|
function onCheckUpdatesFinished() {
|
||||||
|
checkUpdates.loading = false;
|
||||||
|
}
|
||||||
|
|
||||||
target: Backend
|
target: Backend
|
||||||
function onCheckUpdatesFinished() { checkUpdates.loading = false }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SettingsItem {
|
SettingsItem {
|
||||||
id: logs
|
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
|
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 {
|
SettingsItem {
|
||||||
id: reportBug
|
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
|
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 {
|
Item {
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
colorScheme: root.colorScheme
|
|
||||||
type: Label.Caption
|
|
||||||
color: root.colorScheme.text_weak
|
color: root.colorScheme.text_weak
|
||||||
textFormat: Text.StyledText
|
colorScheme: root.colorScheme
|
||||||
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
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").
|
onLinkActivated: function (link) {
|
||||||
arg(Backend.appname).
|
Qt.openUrlExternally(link);
|
||||||
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) }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,116 +1,105 @@
|
|||||||
// Copyright (c) 2023 Proton AG
|
// Copyright (c) 2023 Proton AG
|
||||||
//
|
|
||||||
// This file is part of Proton Mail Bridge.
|
// This file is part of Proton Mail Bridge.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
// 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
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
// 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/>.
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Controls.impl
|
import QtQuick.Controls.impl
|
||||||
|
|
||||||
import Proton
|
import Proton
|
||||||
|
|
||||||
SettingsView {
|
SettingsView {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
fillHeight: false
|
property bool _valuesChanged: keychainSelection.checkedButton && keychainSelection.checkedButton.text !== Backend.currentKeychain
|
||||||
|
|
||||||
property bool _valuesChanged: keychainSelection.checkedButton && keychainSelection.checkedButton.text != Backend.currentKeychain
|
function setDefaultValues() {
|
||||||
|
for (const bi in keychainSelection.buttons) {
|
||||||
Label {
|
const button = keychainSelection.buttons[bi];
|
||||||
colorScheme: root.colorScheme
|
if (button.text === Backend.currentKeychain) {
|
||||||
text: qsTr("Default keychain")
|
button.checked = true;
|
||||||
type: Label.Heading
|
break;
|
||||||
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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fillHeight: false
|
||||||
|
|
||||||
Rectangle {
|
Component.onCompleted: root.setDefaultValues()
|
||||||
Layout.fillWidth: true
|
onBack: {
|
||||||
height: 1
|
root.setDefaultValues();
|
||||||
color: root.colorScheme.border_weak
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
RowLayout {
|
||||||
spacing: 12
|
spacing: 12
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
id: submitButton
|
id: submitButton
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
text: qsTr("Save and restart")
|
|
||||||
enabled: root._valuesChanged
|
enabled: root._valuesChanged
|
||||||
|
text: qsTr("Save and restart")
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
Backend.changeKeychain(keychainSelection.checkedButton.text)
|
Backend.changeKeychain(keychainSelection.checkedButton.text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
text: qsTr("Cancel")
|
|
||||||
onClicked: root.back()
|
|
||||||
secondary: true
|
secondary: true
|
||||||
}
|
text: qsTr("Cancel")
|
||||||
|
|
||||||
|
onClicked: root.back()
|
||||||
|
}
|
||||||
Connections {
|
Connections {
|
||||||
target: Backend
|
|
||||||
|
|
||||||
function onChangeKeychainFinished() {
|
function onChangeKeychainFinished() {
|
||||||
submitButton.loading = false
|
submitButton.loading = false;
|
||||||
root.back()
|
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
|
// Copyright (c) 2023 Proton AG
|
||||||
//
|
|
||||||
// This file is part of Proton Mail Bridge.
|
// This file is part of Proton Mail Bridge.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
// 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
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
// 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/>.
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Controls.impl
|
import QtQuick.Controls.impl
|
||||||
import QtQuick.Dialogs
|
import QtQuick.Dialogs
|
||||||
|
|
||||||
import Proton
|
import Proton
|
||||||
|
|
||||||
SettingsView {
|
SettingsView {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
fillHeight: false
|
|
||||||
|
|
||||||
property var notifications
|
|
||||||
property url diskCachePath: pathDialog.shortcuts.home
|
property url diskCachePath: pathDialog.shortcuts.home
|
||||||
|
property var notifications
|
||||||
|
|
||||||
function refresh() {
|
function refresh() {
|
||||||
diskCacheSetting.description = Backend.nativePath(root.diskCachePath)
|
diskCacheSetting.description = Backend.nativePath(root.diskCachePath);
|
||||||
submitButton.enabled = (!submitButton.loading) && !Backend.areSameFileOrFolder(Backend.diskCachePath, 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 {
|
Label {
|
||||||
|
Layout.fillWidth: true
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
text: qsTr("Local cache")
|
text: qsTr("Local cache")
|
||||||
type: Label.Heading
|
type: Label.Heading
|
||||||
Layout.fillWidth: true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
color: root.colorScheme.text_weak
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
text: qsTr("Bridge stores your encrypted messages locally to optimize communication with your client.")
|
text: qsTr("Bridge stores your encrypted messages locally to optimize communication with your client.")
|
||||||
type: Label.Body
|
type: Label.Body
|
||||||
color: root.colorScheme.text_weak
|
|
||||||
Layout.fillWidth: true
|
|
||||||
wrapMode: Text.WordWrap
|
wrapMode: Text.WordWrap
|
||||||
}
|
}
|
||||||
|
|
||||||
SettingsItem {
|
SettingsItem {
|
||||||
id: diskCacheSetting
|
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
|
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 {
|
FolderDialog {
|
||||||
id: pathDialog
|
id: pathDialog
|
||||||
title: qsTr("Select cache location")
|
|
||||||
currentFolder: root.diskCachePath
|
currentFolder: root.diskCachePath
|
||||||
onAccepted: {
|
title: qsTr("Select cache location")
|
||||||
root.diskCachePath = pathDialog.selectedFolder
|
|
||||||
root.refresh()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
onAccepted: {
|
||||||
|
root.diskCachePath = pathDialog.selectedFolder;
|
||||||
|
root.refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
RowLayout {
|
RowLayout {
|
||||||
spacing: 12
|
spacing: 12
|
||||||
|
|
||||||
@ -83,43 +90,25 @@ SettingsView {
|
|||||||
id: submitButton
|
id: submitButton
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
text: qsTr("Save")
|
text: qsTr("Save")
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
root.submit()
|
root.submit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
text: qsTr("Cancel")
|
|
||||||
onClicked: root.back()
|
|
||||||
secondary: true
|
secondary: true
|
||||||
}
|
text: qsTr("Cancel")
|
||||||
|
|
||||||
|
onClicked: root.back()
|
||||||
|
}
|
||||||
Connections {
|
Connections {
|
||||||
target: Backend
|
|
||||||
|
|
||||||
function onDiskCachePathChangeFinished() {
|
function onDiskCachePathChangeFinished() {
|
||||||
submitButton.loading = false
|
submitButton.loading = false;
|
||||||
root.setDefaultValues()
|
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
|
// Copyright (c) 2023 Proton AG
|
||||||
//
|
|
||||||
// This file is part of Proton Mail Bridge.
|
// This file is part of Proton Mail Bridge.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
// 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
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
// 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/>.
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import QtQml
|
import QtQml
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Window
|
import QtQuick.Window
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
|
|
||||||
import Proton
|
import Proton
|
||||||
import Notifications
|
import Notifications
|
||||||
|
|
||||||
ApplicationWindow {
|
ApplicationWindow {
|
||||||
id: root
|
id: root
|
||||||
colorScheme: ProtonStyle.currentStyle
|
|
||||||
visible: true
|
|
||||||
|
|
||||||
|
|
||||||
property int _defaultWidth: 1080
|
|
||||||
property int _defaultHeight: 780
|
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
|
height: _defaultHeight
|
||||||
minimumWidth: _defaultWidth
|
minimumWidth: _defaultWidth
|
||||||
|
visible: true
|
||||||
property var notifications
|
width: _defaultWidth
|
||||||
|
|
||||||
// show Setup Guide on every new user
|
// show Setup Guide on every new user
|
||||||
Connections {
|
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) {
|
function onRowsAboutToBeRemoved(parent, first, last) {
|
||||||
for (var i = first; i <= last; i++ ) {
|
for (let i = first; i <= last; i++) {
|
||||||
var user = Backend.users.get(i)
|
const user = Backend.users.get(i);
|
||||||
|
|
||||||
if (setupGuide.user === user) {
|
if (setupGuide.user === user) {
|
||||||
setupGuide.user = null
|
setupGuide.user = null;
|
||||||
contentLayout._showSetup = false
|
contentLayout._showSetup = false;
|
||||||
return
|
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 {
|
Connections {
|
||||||
target: Backend
|
|
||||||
|
|
||||||
function onShowMainWindow() {
|
|
||||||
root.showAndRise()
|
|
||||||
}
|
|
||||||
|
|
||||||
function onLoginFinished(index, wasSignedOut) {
|
function onLoginFinished(index, wasSignedOut) {
|
||||||
var user = Backend.users.get(index)
|
const user = Backend.users.get(index);
|
||||||
if (user && !wasSignedOut) {
|
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) {
|
function onSelectUser(userID, forceShowWindow) {
|
||||||
contentWrapper.selectUser(userID)
|
contentWrapper.selectUser(userID);
|
||||||
if (forceShowWindow) {
|
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 {
|
StackLayout {
|
||||||
id: contentLayout
|
id: contentLayout
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
property bool _showSetup: false
|
property bool _showSetup: false
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
currentIndex: {
|
currentIndex: {
|
||||||
// show welcome when there are no users
|
// show welcome when there are no users
|
||||||
if (Backend.users.count === 0) {
|
if (Backend.users.count === 0) {
|
||||||
return 1
|
return 1;
|
||||||
}
|
}
|
||||||
|
const u = Backend.users.get(0);
|
||||||
var u = Backend.users.get(0)
|
|
||||||
|
|
||||||
if (!u) {
|
if (!u) {
|
||||||
console.trace()
|
console.trace();
|
||||||
console.log("empty user")
|
console.log("empty user");
|
||||||
return 1
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((Backend.users.count === 1) && (u.state === EUserState.SignedOut)) {
|
if ((Backend.users.count === 1) && (u.state === EUserState.SignedOut)) {
|
||||||
showSignIn(u.primaryEmailOrUsername())
|
showSignIn(u.primaryEmailOrUsername());
|
||||||
return 0
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (contentLayout._showSetup) {
|
if (contentLayout._showSetup) {
|
||||||
return 2
|
return 2;
|
||||||
}
|
}
|
||||||
|
return 0;
|
||||||
return 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ContentWrapper { // 0
|
ContentWrapper {
|
||||||
|
// 0
|
||||||
id: contentWrapper
|
id: contentWrapper
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.fillWidth: true
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
notifications: root.notifications
|
notifications: root.notifications
|
||||||
|
|
||||||
Layout.fillHeight: true
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
onShowSetupGuide: function(user, address) {
|
|
||||||
root.showSetup(user,address)
|
|
||||||
}
|
|
||||||
|
|
||||||
onCloseWindow: {
|
onCloseWindow: {
|
||||||
root.close()
|
root.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
onQuitBridge: {
|
onQuitBridge: {
|
||||||
// If we ever want to add a confirmation dialog before quitting:
|
// 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.notifications.askQuestion("Quit Bridge", "Insert warning message here.", "Quit", "Cancel", Backend.quit, null)
|
||||||
root.close()
|
root.close();
|
||||||
Backend.quit()
|
Backend.quit();
|
||||||
|
}
|
||||||
|
onShowSetupGuide: function (user, address) {
|
||||||
|
root.showSetup(user, address);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
WelcomeGuide {
|
||||||
WelcomeGuide { // 1
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
|
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true // 1
|
||||||
|
colorScheme: root.colorScheme
|
||||||
}
|
}
|
||||||
|
SetupGuide {
|
||||||
SetupGuide { // 2
|
// 2
|
||||||
id: setupGuide
|
id: setupGuide
|
||||||
colorScheme: root.colorScheme
|
|
||||||
|
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
colorScheme: root.colorScheme
|
||||||
|
|
||||||
onDismissed: {
|
onDismissed: {
|
||||||
root.showSetup(null,"")
|
root.showSetup(null, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
onFinished: {
|
onFinished: {
|
||||||
// TODO: Do not close window. Trigger Backend to check that
|
// TODO: Do not close window. Trigger Backend to check that
|
||||||
// there is a successfully connected client. Then Backend
|
// there is a successfully connected client. Then Backend
|
||||||
// should send another signal to close the setup guide.
|
// should send another signal to close the setup guide.
|
||||||
root.showSetup(null,"")
|
root.showSetup(null, "");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NotificationPopups {
|
NotificationPopups {
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
notifications: root.notifications
|
|
||||||
mainWindow: root
|
mainWindow: root
|
||||||
|
notifications: root.notifications
|
||||||
}
|
}
|
||||||
|
|
||||||
SplashScreen {
|
SplashScreen {
|
||||||
id: splashScreen
|
id: splashScreen
|
||||||
colorScheme: root.colorScheme
|
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
|
// Copyright (c) 2023 Proton AG
|
||||||
//
|
|
||||||
// This file is part of Proton Mail Bridge.
|
// This file is part of Proton Mail Bridge.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
// 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
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
// 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/>.
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import QtQml
|
import QtQml
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
|
|
||||||
import Proton
|
import Proton
|
||||||
import Notifications
|
import Notifications
|
||||||
|
|
||||||
Dialog {
|
Dialog {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
|
default property alias data: additionalChildrenContainer.children
|
||||||
property var notification
|
property var notification
|
||||||
|
|
||||||
shouldShow: notification && notification.active && !notification.dismissed
|
|
||||||
modal: true
|
modal: true
|
||||||
|
shouldShow: notification && notification.active && !notification.dismissed
|
||||||
default property alias data: additionalChildrenContainer.children
|
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
Layout.bottomMargin: 16
|
||||||
sourceSize.width: 64
|
|
||||||
sourceSize.height: 64
|
|
||||||
|
|
||||||
Layout.preferredHeight: 64
|
Layout.preferredHeight: 64
|
||||||
Layout.preferredWidth: 64
|
Layout.preferredWidth: 64
|
||||||
|
|
||||||
Layout.bottomMargin: 16
|
|
||||||
|
|
||||||
visible: source != ""
|
|
||||||
|
|
||||||
source: {
|
source: {
|
||||||
if (!root.notification) {
|
if (!root.notification) {
|
||||||
return ""
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (root.notification.type) {
|
switch (root.notification.type) {
|
||||||
case Notification.NotificationType.Info:
|
case Notification.NotificationType.Info:
|
||||||
return "/qml/icons/ic-info.svg"
|
return "/qml/icons/ic-info.svg";
|
||||||
case Notification.NotificationType.Success:
|
case Notification.NotificationType.Success:
|
||||||
return "/qml/icons/ic-success.svg"
|
return "/qml/icons/ic-success.svg";
|
||||||
case Notification.NotificationType.Warning:
|
case Notification.NotificationType.Warning:
|
||||||
case Notification.NotificationType.Danger:
|
case Notification.NotificationType.Danger:
|
||||||
return "/qml/icons/ic-alert.svg"
|
return "/qml/icons/ic-alert.svg";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
sourceSize.height: 64
|
||||||
|
sourceSize.width: 64
|
||||||
|
visible: source != ""
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
Layout.bottomMargin: 8
|
Layout.bottomMargin: 8
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
text: root.notification.title
|
text: root.notification.title
|
||||||
type: Label.LabelType.Title
|
type: Label.LabelType.Title
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
|
Layout.bottomMargin: 16
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.preferredWidth: 240
|
Layout.preferredWidth: 240
|
||||||
Layout.bottomMargin: 16
|
|
||||||
|
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
text: root.notification.description
|
|
||||||
wrapMode: Text.WordWrap
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
text: root.notification.description
|
||||||
type: Label.LabelType.Body
|
type: Label.LabelType.Body
|
||||||
onLinkActivated: function(link) { Qt.openUrlExternally(link) }
|
wrapMode: Text.WordWrap
|
||||||
}
|
|
||||||
|
|
||||||
|
onLinkActivated: function (link) {
|
||||||
|
Qt.openUrlExternally(link);
|
||||||
|
}
|
||||||
|
}
|
||||||
Item {
|
Item {
|
||||||
id: additionalChildrenContainer
|
id: additionalChildrenContainer
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.bottomMargin: 16
|
Layout.bottomMargin: 16
|
||||||
|
Layout.fillWidth: true
|
||||||
visible: children.length > 0
|
|
||||||
|
|
||||||
implicitHeight: additionalChildrenContainer.childrenRect.height
|
implicitHeight: additionalChildrenContainer.childrenRect.height
|
||||||
implicitWidth: additionalChildrenContainer.childrenRect.width
|
implicitWidth: additionalChildrenContainer.childrenRect.width
|
||||||
|
visible: children.length > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
spacing: 8
|
spacing: 8
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
model: root.notification.action
|
model: root.notification.action
|
||||||
|
|
||||||
delegate: Button {
|
delegate: Button {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
action: modelData
|
action: modelData
|
||||||
|
colorScheme: root.colorScheme
|
||||||
secondary: index > 0
|
|
||||||
|
|
||||||
loading: modelData.loading
|
loading: modelData.loading
|
||||||
|
secondary: index > 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,25 +1,19 @@
|
|||||||
// Copyright (c) 2023 Proton AG
|
// Copyright (c) 2023 Proton AG
|
||||||
//
|
|
||||||
// This file is part of Proton Mail Bridge.
|
// This file is part of Proton Mail Bridge.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
// 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
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
// 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/>.
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import QtQml
|
import QtQml
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
|
|
||||||
import Proton
|
import Proton
|
||||||
import Notifications
|
import Notifications
|
||||||
|
|
||||||
@ -27,118 +21,98 @@ Item {
|
|||||||
id: root
|
id: root
|
||||||
|
|
||||||
property ColorScheme colorScheme
|
property ColorScheme colorScheme
|
||||||
property var notifications
|
|
||||||
property var mainWindow
|
property var mainWindow
|
||||||
|
|
||||||
property int notificationWhitelist: NotificationFilter.FilterConsts.All
|
|
||||||
property int notificationBlacklist: NotificationFilter.FilterConsts.None
|
property int notificationBlacklist: NotificationFilter.FilterConsts.None
|
||||||
|
property int notificationWhitelist: NotificationFilter.FilterConsts.All
|
||||||
|
property var notifications
|
||||||
|
|
||||||
NotificationFilter {
|
NotificationFilter {
|
||||||
id: bannerNotificationFilter
|
id: bannerNotificationFilter
|
||||||
|
|
||||||
source: root.notifications.all
|
|
||||||
blacklist: Notifications.Group.Dialogs
|
blacklist: Notifications.Group.Dialogs
|
||||||
|
source: root.notifications.all
|
||||||
}
|
}
|
||||||
|
|
||||||
Banner {
|
Banner {
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
notification: bannerNotificationFilter.topmost
|
|
||||||
mainWindow: root.mainWindow
|
mainWindow: root.mainWindow
|
||||||
|
notification: bannerNotificationFilter.topmost
|
||||||
}
|
}
|
||||||
|
|
||||||
NotificationDialog {
|
NotificationDialog {
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
notification: root.notifications.updateManualReady
|
notification: root.notifications.updateManualReady
|
||||||
|
|
||||||
Switch {
|
Switch {
|
||||||
id:autoUpdate
|
id: autoUpdate
|
||||||
|
checked: Backend.isAutomaticUpdateOn
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
text: qsTr("Update automatically in the future")
|
text: qsTr("Update automatically in the future")
|
||||||
checked: Backend.isAutomaticUpdateOn
|
|
||||||
onClicked: Backend.toggleAutomaticUpdate(autoUpdate.checked)
|
onClicked: Backend.toggleAutomaticUpdate(autoUpdate.checked)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NotificationDialog {
|
NotificationDialog {
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
notification: root.notifications.updateForce
|
notification: root.notifications.updateForce
|
||||||
}
|
}
|
||||||
|
|
||||||
NotificationDialog {
|
NotificationDialog {
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
notification: root.notifications.updateForceError
|
notification: root.notifications.updateForceError
|
||||||
}
|
}
|
||||||
|
|
||||||
NotificationDialog {
|
NotificationDialog {
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
notification: root.notifications.enableBeta
|
notification: root.notifications.enableBeta
|
||||||
}
|
}
|
||||||
|
|
||||||
NotificationDialog {
|
NotificationDialog {
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
notification: root.notifications.cacheUnavailable
|
notification: root.notifications.cacheUnavailable
|
||||||
}
|
}
|
||||||
|
|
||||||
NotificationDialog {
|
NotificationDialog {
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
notification: root.notifications.cacheCantMove
|
notification: root.notifications.cacheCantMove
|
||||||
}
|
}
|
||||||
|
|
||||||
NotificationDialog {
|
NotificationDialog {
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
notification: root.notifications.diskFull
|
notification: root.notifications.diskFull
|
||||||
}
|
}
|
||||||
|
|
||||||
NotificationDialog {
|
NotificationDialog {
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
notification: root.notifications.enableSplitMode
|
notification: root.notifications.enableSplitMode
|
||||||
}
|
}
|
||||||
|
|
||||||
NotificationDialog {
|
NotificationDialog {
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
notification: root.notifications.resetBridge
|
notification: root.notifications.resetBridge
|
||||||
}
|
}
|
||||||
|
|
||||||
NotificationDialog {
|
NotificationDialog {
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
notification: root.notifications.changeAllMailVisibility
|
notification: root.notifications.changeAllMailVisibility
|
||||||
}
|
}
|
||||||
|
|
||||||
NotificationDialog {
|
NotificationDialog {
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
notification: root.notifications.deleteAccount
|
notification: root.notifications.deleteAccount
|
||||||
}
|
}
|
||||||
|
|
||||||
NotificationDialog {
|
NotificationDialog {
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
notification: root.notifications.noKeychain
|
notification: root.notifications.noKeychain
|
||||||
}
|
}
|
||||||
|
|
||||||
NotificationDialog {
|
NotificationDialog {
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
notification: root.notifications.rebuildKeychain
|
notification: root.notifications.rebuildKeychain
|
||||||
}
|
}
|
||||||
|
|
||||||
NotificationDialog {
|
NotificationDialog {
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
notification: root.notifications.apiCertIssue
|
notification: root.notifications.apiCertIssue
|
||||||
}
|
}
|
||||||
|
|
||||||
NotificationDialog {
|
NotificationDialog {
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
notification: root.notifications.noActiveKeyForRecipient
|
notification: root.notifications.noActiveKeyForRecipient
|
||||||
}
|
}
|
||||||
|
|
||||||
NotificationDialog {
|
NotificationDialog {
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
notification: root.notifications.userBadEvent
|
notification: root.notifications.userBadEvent
|
||||||
}
|
}
|
||||||
|
|
||||||
NotificationDialog {
|
NotificationDialog {
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
notification: root.notifications.genericError
|
notification: root.notifications.genericError
|
||||||
}
|
}
|
||||||
|
|
||||||
NotificationDialog {
|
NotificationDialog {
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
notification: root.notifications.genericQuestion
|
notification: root.notifications.genericQuestion
|
||||||
|
|||||||
@ -1,54 +1,45 @@
|
|||||||
// Copyright (c) 2023 Proton AG
|
// Copyright (c) 2023 Proton AG
|
||||||
//
|
|
||||||
// This file is part of Proton Mail Bridge.
|
// This file is part of Proton Mail Bridge.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
// 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
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
// 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/>.
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import QtQml
|
import QtQml
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
|
|
||||||
QtObject {
|
QtObject {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
default property var children
|
|
||||||
|
|
||||||
enum NotificationType {
|
enum NotificationType {
|
||||||
Info = 0,
|
Info,
|
||||||
Success = 1,
|
Success,
|
||||||
Warning = 2,
|
Warning,
|
||||||
Danger = 3
|
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
|
// title is used in dialogs only
|
||||||
property string title
|
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 type
|
||||||
property int group
|
|
||||||
|
|
||||||
property bool dismissed: false
|
|
||||||
property bool active: false
|
|
||||||
readonly property var occurred: active ? new Date() : undefined
|
|
||||||
|
|
||||||
property var data
|
|
||||||
|
|
||||||
onActiveChanged: {
|
onActiveChanged: {
|
||||||
dismissed = false
|
dismissed = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,114 +1,95 @@
|
|||||||
// Copyright (c) 2023 Proton AG
|
// Copyright (c) 2023 Proton AG
|
||||||
//
|
|
||||||
// This file is part of Proton Mail Bridge.
|
// This file is part of Proton Mail Bridge.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
// 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
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
// 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/>.
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import QtQml
|
import QtQml
|
||||||
import QtQml.Models
|
import QtQml.Models
|
||||||
|
|
||||||
// contains notifications that satisfy black- and whitelist and are sorted in time-occurred order
|
// contains notifications that satisfy black- and whitelist and are sorted in time-occurred order
|
||||||
ListModel {
|
ListModel {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
enum FilterConsts {
|
enum FilterConsts {
|
||||||
None = 0,
|
None,
|
||||||
All = 255
|
All = 255
|
||||||
}
|
}
|
||||||
|
|
||||||
property int whitelist: NotificationFilter.FilterConsts.All
|
|
||||||
property int blacklist: NotificationFilter.FilterConsts.None
|
property int blacklist: NotificationFilter.FilterConsts.None
|
||||||
|
|
||||||
property Notification topmost
|
|
||||||
property var source
|
|
||||||
|
|
||||||
property bool componentCompleted: false
|
property bool componentCompleted: false
|
||||||
Component.onCompleted: {
|
property var source
|
||||||
root.componentCompleted = true
|
property Notification topmost
|
||||||
root.rebuildList()
|
property int whitelist: NotificationFilter.FilterConsts.All
|
||||||
}
|
|
||||||
|
|
||||||
// overriding get method to ignore any role and return directly object itself
|
// overriding get method to ignore any role and return directly object itself
|
||||||
function get(row) {
|
function get(row) {
|
||||||
if (row < 0 || row >= count) {
|
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() {
|
function rebuildList() {
|
||||||
|
let i;
|
||||||
// avoid evaluation of the list before Component.onCompleted
|
// avoid evaluation of the list before Component.onCompleted
|
||||||
if (!root.componentCompleted) {
|
if (!root.componentCompleted) {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
for (i = 0; i < root.count; i++) {
|
||||||
for (var i = 0; i < root.count; i++) {
|
root.get(i).onActiveChanged.disconnect(root.updateList);
|
||||||
root.get(i).onActiveChanged.disconnect( root.updateList )
|
|
||||||
}
|
}
|
||||||
|
root.clear();
|
||||||
root.clear()
|
|
||||||
|
|
||||||
if (!root.source) {
|
if (!root.source) {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < root.source.length; i++) {
|
for (i = 0; i < root.source.length; i++) {
|
||||||
var obj = root.source[i]
|
const obj = root.source[i];
|
||||||
if (obj.group & root.blacklist) {
|
if (obj.group & root.blacklist) {
|
||||||
continue
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(obj.group & root.whitelist)) {
|
if (!(obj.group & root.whitelist)) {
|
||||||
continue
|
continue;
|
||||||
}
|
}
|
||||||
|
root.append({
|
||||||
root.append({obj})
|
"obj": obj
|
||||||
obj.onActiveChanged.connect( root.updateList )
|
});
|
||||||
|
obj.onActiveChanged.connect(root.updateList);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateList() {
|
function updateList() {
|
||||||
var topmost = null
|
let topmost = null;
|
||||||
|
for (let i = 0; i < root.count; i++) {
|
||||||
for (var i = 0; i < root.count; i++) {
|
const obj = root.get(i);
|
||||||
var obj = root.get(i)
|
|
||||||
|
|
||||||
if (!obj.active) {
|
if (!obj.active) {
|
||||||
continue
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (topmost && (topmost.type > obj.type)) {
|
if (topmost && (topmost.type > obj.type)) {
|
||||||
continue
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (topmost && (topmost.type === obj.type) && (topmost.occurred > obj.occurred)) {
|
if (topmost && (topmost.type === obj.type) && (topmost.occurred > obj.occurred)) {
|
||||||
continue
|
continue;
|
||||||
}
|
}
|
||||||
|
topmost = obj;
|
||||||
topmost = obj
|
|
||||||
}
|
}
|
||||||
|
root.topmost = topmost;
|
||||||
root.topmost = topmost
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onWhitelistChanged: {
|
Component.onCompleted: {
|
||||||
root.rebuildList()
|
root.componentCompleted = true;
|
||||||
|
root.rebuildList();
|
||||||
}
|
}
|
||||||
onBlacklistChanged: {
|
onBlacklistChanged: {
|
||||||
root.rebuildList()
|
root.rebuildList();
|
||||||
}
|
}
|
||||||
onSourceChanged: {
|
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
|
// Copyright (c) 2023 Proton AG
|
||||||
//
|
|
||||||
// This file is part of Proton Mail Bridge.
|
// This file is part of Proton Mail Bridge.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
// 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
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
// 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/>.
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Controls.impl
|
import QtQuick.Controls.impl
|
||||||
|
|
||||||
import Proton
|
import Proton
|
||||||
|
|
||||||
SettingsView {
|
SettingsView {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
fillHeight: false
|
property bool _valuesChanged: (imapField.text * 1 !== Backend.imapPort || smtpField.text * 1 !== Backend.smtpPort)
|
||||||
|
|
||||||
property var notifications
|
property var notifications
|
||||||
|
|
||||||
property bool _valuesChanged: (
|
function isPortFree(field) {
|
||||||
imapField.text*1 !== Backend.imapPort ||
|
const num = field.text * 1;
|
||||||
smtpField.text*1 !== Backend.smtpPort
|
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 {
|
Label {
|
||||||
|
Layout.fillWidth: true
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
text: qsTr("Default ports")
|
text: qsTr("Default ports")
|
||||||
type: Label.Heading
|
type: Label.Heading
|
||||||
Layout.fillWidth: true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
color: root.colorScheme.text_weak
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
text: qsTr("Changes require reconfiguration of your email client.")
|
text: qsTr("Changes require reconfiguration of your email client.")
|
||||||
type: Label.Body
|
type: Label.Body
|
||||||
color: root.colorScheme.text_weak
|
|
||||||
Layout.fillWidth: true
|
|
||||||
wrapMode: Text.WordWrap
|
wrapMode: Text.WordWrap
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
spacing: 16
|
spacing: 16
|
||||||
|
|
||||||
TextField {
|
TextField {
|
||||||
id: imapField
|
id: imapField
|
||||||
|
Layout.alignment: Qt.AlignTop | Qt.AlignLeft
|
||||||
|
Layout.preferredWidth: 160
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
label: qsTr("IMAP port")
|
label: qsTr("IMAP port")
|
||||||
Layout.preferredWidth: 160
|
|
||||||
Layout.alignment: Qt.AlignTop | Qt.AlignLeft
|
|
||||||
validator: root.validate
|
validator: root.validate
|
||||||
}
|
}
|
||||||
TextField {
|
TextField {
|
||||||
id: smtpField
|
id: smtpField
|
||||||
|
Layout.alignment: Qt.AlignTop | Qt.AlignLeft
|
||||||
|
Layout.preferredWidth: 160
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
label: qsTr("SMTP port")
|
label: qsTr("SMTP port")
|
||||||
Layout.preferredWidth: 160
|
|
||||||
Layout.alignment: Qt.AlignTop | Qt.AlignLeft
|
|
||||||
validator: root.validate
|
validator: root.validate
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
height: 1
|
|
||||||
color: root.colorScheme.border_weak
|
color: root.colorScheme.border_weak
|
||||||
|
height: 1
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
spacing: 12
|
spacing: 12
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
id: submitButton
|
id: submitButton
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
text: qsTr("Save")
|
|
||||||
enabled: (!loading) && root._valuesChanged
|
enabled: (!loading) && root._valuesChanged
|
||||||
|
text: qsTr("Save")
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
// removing error here because we may have set it manually (port occupied)
|
// removing error here because we may have set it manually (port occupied)
|
||||||
imapField.error = false
|
imapField.error = false;
|
||||||
smtpField.error = false
|
smtpField.error = false;
|
||||||
|
|
||||||
// checking errors separately because we want to display "same port" error only once
|
// checking errors separately because we want to display "same port" error only once
|
||||||
imapField.validate()
|
imapField.validate();
|
||||||
if (imapField.error) {
|
if (imapField.error) {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
smtpField.validate()
|
smtpField.validate();
|
||||||
if (smtpField.error) {
|
if (smtpField.error) {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
submitButton.loading = true;
|
||||||
submitButton.loading = true
|
|
||||||
|
|
||||||
// check both ports before returning an error
|
// check both ports before returning an error
|
||||||
var err = false
|
let err = false;
|
||||||
err |= !isPortFree(imapField)
|
err |= !isPortFree(imapField);
|
||||||
err |= !isPortFree(smtpField)
|
err |= !isPortFree(smtpField);
|
||||||
if (err) {
|
if (err) {
|
||||||
submitButton.loading = false
|
submitButton.loading = false;
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We turn off all port error notification. They well be restored if problems persist
|
// We turn off all port error notification. They will be restored if problems persist
|
||||||
root.notifications.imapPortStartupError.active = false
|
root.notifications.imapPortStartupError.active = false;
|
||||||
root.notifications.smtpPortStartupError.active = false
|
root.notifications.smtpPortStartupError.active = false;
|
||||||
root.notifications.imapPortChangeError.active = false
|
root.notifications.imapPortChangeError.active = false;
|
||||||
root.notifications.smtpPortChangeError.active = false
|
root.notifications.smtpPortChangeError.active = false;
|
||||||
|
Backend.setMailServerSettings(imapField.text, smtpField.text, Backend.useSSLForIMAP, Backend.useSSLForSMTP);
|
||||||
Backend.setMailServerSettings(imapField.text, smtpField.text, Backend.useSSLForIMAP, Backend.useSSLForSMTP)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
text: qsTr("Cancel")
|
|
||||||
onClicked: root.back()
|
|
||||||
secondary: true
|
secondary: true
|
||||||
}
|
text: qsTr("Cancel")
|
||||||
|
|
||||||
|
onClicked: root.back()
|
||||||
|
}
|
||||||
Connections {
|
Connections {
|
||||||
target: Backend
|
|
||||||
|
|
||||||
function onChangeMailServerSettingsFinished() {
|
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
|
// Copyright (c) 2023 Proton AG
|
||||||
//
|
|
||||||
// This file is part of Proton Mail Bridge.
|
// This file is part of Proton Mail Bridge.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
// 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
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
// 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/>.
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Templates as T
|
import QtQuick.Templates as T
|
||||||
|
|
||||||
|
|||||||
@ -1,20 +1,15 @@
|
|||||||
// Copyright (c) 2023 Proton AG
|
// Copyright (c) 2023 Proton AG
|
||||||
//
|
|
||||||
// This file is part of Proton Mail Bridge.
|
// This file is part of Proton Mail Bridge.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
// 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
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
// 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/>.
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import QtQml
|
import QtQml
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Window
|
import QtQuick.Window
|
||||||
@ -25,14 +20,14 @@ import QtQuick.Templates as T
|
|||||||
T.ApplicationWindow {
|
T.ApplicationWindow {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property ColorScheme colorScheme
|
|
||||||
|
|
||||||
// popup priority based on types
|
// popup priority based on types
|
||||||
enum PopupType {
|
enum PopupType {
|
||||||
Banner = 0,
|
Banner,
|
||||||
Dialog = 1
|
Dialog
|
||||||
}
|
}
|
||||||
|
|
||||||
|
property ColorScheme colorScheme
|
||||||
|
|
||||||
// contains currently visible popup
|
// contains currently visible popup
|
||||||
property var popupVisible: null
|
property var popupVisible: null
|
||||||
|
|
||||||
@ -41,85 +36,61 @@ T.ApplicationWindow {
|
|||||||
// overriding get method to ignore any role and return directly object itself
|
// overriding get method to ignore any role and return directly object itself
|
||||||
function get(row) {
|
function get(row) {
|
||||||
if (row < 0 || row >= count) {
|
if (row < 0 || row >= count) {
|
||||||
return undefined
|
return undefined;
|
||||||
}
|
}
|
||||||
return data(index(row, 0), Qt.DisplayRole)
|
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()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onRowsAboutToBeRemoved: function (parent, first, last) {
|
onRowsAboutToBeRemoved: function (parent, first, last) {
|
||||||
for (var i = first; i <= last; i++ ) {
|
for (let i = first; i <= last; i++) {
|
||||||
var obj = popups.get(i)
|
const obj = popups.get(i);
|
||||||
obj.onShouldShowChanged.disconnect( root.processPopups )
|
obj.onShouldShowChanged.disconnect(root.processPopups);
|
||||||
|
|
||||||
// if currently visible popup was removed
|
// if currently visible popup was removed
|
||||||
if (root.popupVisible === obj) {
|
if (root.popupVisible === obj) {
|
||||||
root.popupVisible.visible = false
|
root.popupVisible.visible = false;
|
||||||
root.popupVisible = null
|
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() {
|
function processPopups() {
|
||||||
if ((root.popupVisible) && (!root.popupVisible.shouldShow)) {
|
if ((root.popupVisible) && (!root.popupVisible.shouldShow)) {
|
||||||
root.popupVisible.visible = false
|
root.popupVisible.visible = false;
|
||||||
}
|
}
|
||||||
|
let topmost = null;
|
||||||
var topmost = null
|
for (let i = 0; i < popups.count; i++) {
|
||||||
for (var i = 0; i < popups.count; i++) {
|
const obj = popups.get(i);
|
||||||
var obj = popups.get(i)
|
|
||||||
|
|
||||||
if (obj.shouldShow === false) {
|
if (obj.shouldShow === false) {
|
||||||
continue
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (topmost && (topmost.popupType > obj.popupType)) {
|
if (topmost && (topmost.popupType > obj.popupType)) {
|
||||||
continue
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (topmost && (topmost.popupType === obj.popupType) && (topmost.occurred > obj.occurred)) {
|
if (topmost && (topmost.popupType === obj.popupType) && (topmost.occurred > obj.occurred)) {
|
||||||
continue
|
continue;
|
||||||
}
|
}
|
||||||
|
topmost = obj;
|
||||||
topmost = obj
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (root.popupVisible !== topmost) {
|
if (root.popupVisible !== topmost) {
|
||||||
if (root.popupVisible) {
|
if (root.popupVisible) {
|
||||||
root.popupVisible.visible = false
|
root.popupVisible.visible = false;
|
||||||
}
|
}
|
||||||
root.popupVisible = topmost
|
root.popupVisible = topmost;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!root.popupVisible) {
|
if (!root.popupVisible) {
|
||||||
return
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
root.popupVisible.visible = true
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: root.popupVisible
|
|
||||||
|
|
||||||
function onVisibleChanged() {
|
|
||||||
if (root.popupVisible.visible) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
root.popupVisible = null
|
|
||||||
root.processPopups()
|
|
||||||
}
|
}
|
||||||
|
root.popupVisible.visible = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
color: root.colorScheme.background_norm
|
color: root.colorScheme.background_norm
|
||||||
@ -127,8 +98,19 @@ T.ApplicationWindow {
|
|||||||
Overlay.modal: Rectangle {
|
Overlay.modal: Rectangle {
|
||||||
color: root.colorScheme.backdrop_norm
|
color: root.colorScheme.backdrop_norm
|
||||||
}
|
}
|
||||||
|
|
||||||
Overlay.modeless: Rectangle {
|
Overlay.modeless: Rectangle {
|
||||||
color: "transparent"
|
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
|
// Copyright (c) 2023 Proton AG
|
||||||
//
|
|
||||||
// This file is part of Proton Mail Bridge.
|
// This file is part of Proton Mail Bridge.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
// 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
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
// 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/>.
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Controls.impl
|
import QtQuick.Controls.impl
|
||||||
@ -23,212 +18,169 @@ import QtQuick.Layouts
|
|||||||
import "." as Proton
|
import "." as Proton
|
||||||
|
|
||||||
T.Button {
|
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
|
id: control
|
||||||
|
|
||||||
implicitWidth: Math.max(
|
property bool borderless: false
|
||||||
implicitBackgroundWidth + leftInset + rightInset,
|
property ColorScheme colorScheme
|
||||||
implicitContentWidth + leftPadding + rightPadding
|
readonly property bool hasTextAndIcon: (control.text !== "") && (iconImage.source.toString().length > 0)
|
||||||
)
|
readonly property bool isIcon: control.text === ""
|
||||||
implicitHeight: Math.max(
|
property int labelType: Proton.Label.LabelType.Body
|
||||||
implicitBackgroundHeight + topInset + bottomInset,
|
property bool loading: false
|
||||||
implicitContentHeight + topPadding + bottomPadding
|
readonly property bool primary: !secondary
|
||||||
)
|
property alias secondary: control.flat
|
||||||
|
property alias textHorizontalAlignment: label.horizontalAlignment
|
||||||
padding: 8
|
property alias textVerticalAlignment: label.verticalAlignment
|
||||||
horizontalPadding: 16
|
|
||||||
spacing: 10
|
|
||||||
|
|
||||||
font: label.font
|
font: label.font
|
||||||
|
horizontalPadding: 16
|
||||||
icon.width: 16
|
|
||||||
icon.height: 16
|
|
||||||
icon.color: {
|
icon.color: {
|
||||||
if (primary && !isIcon) {
|
if (primary && !isIcon) {
|
||||||
return "#FFFFFF"
|
return "#FFFFFF";
|
||||||
} else {
|
} 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 {
|
contentItem: RowLayout {
|
||||||
id: _contentItem
|
id: _contentItem
|
||||||
spacing: control.hasTextAndIcon ? control.spacing : 0
|
spacing: control.hasTextAndIcon ? control.spacing : 0
|
||||||
|
|
||||||
Proton.Label {
|
Proton.Label {
|
||||||
colorScheme: root.colorScheme
|
|
||||||
id: label
|
id: label
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
Layout.fillWidth: true
|
||||||
elide: Text.ElideRight
|
|
||||||
horizontalAlignment: Qt.AlignHCenter
|
|
||||||
|
|
||||||
visible: !control.isIcon
|
|
||||||
text: control.text
|
|
||||||
color: {
|
color: {
|
||||||
if (primary && !isIcon) {
|
if (primary && !isIcon) {
|
||||||
return "#FFFFFF"
|
return "#FFFFFF";
|
||||||
} else {
|
} 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
|
opacity: control.enabled || control.loading ? 1.0 : 0.5
|
||||||
|
text: control.text
|
||||||
type: labelType
|
type: labelType
|
||||||
|
visible: !control.isIcon
|
||||||
}
|
}
|
||||||
|
|
||||||
ColorImage {
|
ColorImage {
|
||||||
id: iconImage
|
id: iconImage
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignCenter
|
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: {
|
width: {
|
||||||
// special case for loading since we want icon to be square for rotation animation
|
// special case for loading since we want icon to be square for rotation animation
|
||||||
if (control.loading) {
|
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 {
|
RotationAnimation {
|
||||||
target: iconImage
|
direction: RotationAnimation.Clockwise
|
||||||
loops: Animation.Infinite
|
|
||||||
duration: 1000
|
duration: 1000
|
||||||
from: 0
|
from: 0
|
||||||
to: 360
|
loops: Animation.Infinite
|
||||||
direction: RotationAnimation.Clockwise
|
|
||||||
running: control.loading
|
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: {
|
Component.onCompleted: {
|
||||||
if (!control.colorScheme) {
|
if (!control.colorScheme) {
|
||||||
console.trace()
|
console.trace();
|
||||||
var next = root
|
let next = root;
|
||||||
for (var i = 0; i<1000; i++) {
|
for (let i = 0; i < 1000; i++) {
|
||||||
console.log(i, next, "colorscheme", next.colorScheme)
|
console.log(i, next, "colorscheme", next.colorScheme);
|
||||||
next = next.parent
|
next = next.parent;
|
||||||
if (!next) break
|
if (!next)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
console.error("ColorScheme not defined")
|
console.error("ColorScheme not defined");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,97 +1,96 @@
|
|||||||
// Copyright (c) 2023 Proton AG
|
// Copyright (c) 2023 Proton AG
|
||||||
//
|
|
||||||
// This file is part of Proton Mail Bridge.
|
// This file is part of Proton Mail Bridge.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
// 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
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
// 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/>.
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Controls.impl
|
import QtQuick.Controls.impl
|
||||||
import QtQuick.Templates as T
|
import QtQuick.Templates as T
|
||||||
|
|
||||||
T.CheckBox {
|
T.CheckBox {
|
||||||
property ColorScheme colorScheme
|
|
||||||
|
|
||||||
property bool error: false
|
|
||||||
|
|
||||||
id: control
|
id: control
|
||||||
|
|
||||||
implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
|
property ColorScheme colorScheme
|
||||||
implicitContentWidth + leftPadding + rightPadding)
|
property bool error: false
|
||||||
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
|
|
||||||
implicitContentHeight + topPadding + bottomPadding,
|
|
||||||
implicitIndicatorHeight + topPadding + bottomPadding)
|
|
||||||
|
|
||||||
|
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, implicitContentHeight + topPadding + bottomPadding, implicitIndicatorHeight + topPadding + bottomPadding)
|
||||||
|
implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, implicitContentWidth + leftPadding + rightPadding)
|
||||||
padding: 0
|
padding: 0
|
||||||
spacing: 8
|
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 {
|
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
|
implicitHeight: 20
|
||||||
|
implicitWidth: 20
|
||||||
radius: ProtonStyle.checkbox_radius
|
radius: ProtonStyle.checkbox_radius
|
||||||
|
|
||||||
x: text ? (control.mirrored ? control.width - width - control.rightPadding : control.leftPadding) : control.leftPadding + (control.availableWidth - 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
|
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 {
|
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
|
x: (parent.width - width) / 2
|
||||||
y: (parent.height - height) / 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?
|
// TODO: do we need PartiallyChecked state?
|
||||||
@ -105,30 +104,4 @@ T.CheckBox {
|
|||||||
// visible: control.checkState === Qt.PartiallyChecked
|
// 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
|
// Copyright (c) 2023 Proton AG
|
||||||
//
|
|
||||||
// This file is part of Proton Mail Bridge.
|
// This file is part of Proton Mail Bridge.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
// 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
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
// 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/>.
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQml
|
import QtQml
|
||||||
|
|
||||||
QtObject {
|
QtObject {
|
||||||
// should be a pointer to ColorScheme object
|
|
||||||
property var prominent
|
|
||||||
|
|
||||||
// Primary
|
// Backdrop
|
||||||
property color primary_norm
|
property color backdrop_norm
|
||||||
|
property color background_avatar
|
||||||
|
|
||||||
// Interaction-norm
|
// Background
|
||||||
property color interaction_norm
|
property color background_norm
|
||||||
property color interaction_norm_hover
|
property color background_strong
|
||||||
property color interaction_norm_active
|
property color background_weak
|
||||||
|
|
||||||
// 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
|
|
||||||
|
|
||||||
// Border
|
// Border
|
||||||
property color border_norm
|
property color border_norm
|
||||||
property color border_weak
|
property color border_weak
|
||||||
|
property color field_disabled
|
||||||
|
property color field_hover
|
||||||
|
|
||||||
// Background
|
// Field
|
||||||
property color background_norm
|
property color field_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
|
|
||||||
|
|
||||||
// Interaction-default
|
// Interaction-default
|
||||||
property color interaction_default
|
property color interaction_default
|
||||||
property color interaction_default_hover
|
|
||||||
property color interaction_default_active
|
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
|
// Scrollbar
|
||||||
property color scrollbar_norm
|
property color scrollbar_norm
|
||||||
property color scrollbar_hover
|
property color shadow_lifted
|
||||||
|
|
||||||
// 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
|
|
||||||
|
|
||||||
// Shadows
|
// Shadows
|
||||||
property color shadow_norm
|
property color shadow_norm
|
||||||
property color shadow_lifted
|
|
||||||
|
|
||||||
// Backdrop
|
// Signal
|
||||||
property color backdrop_norm
|
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
|
// Images
|
||||||
property string welcome_img
|
property string welcome_img
|
||||||
property string logo_img
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,20 +1,15 @@
|
|||||||
// Copyright (c) 2023 Proton AG
|
// Copyright (c) 2023 Proton AG
|
||||||
//
|
|
||||||
// This file is part of Proton Mail Bridge.
|
// This file is part of Proton Mail Bridge.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
// 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
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
// 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/>.
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Window
|
import QtQuick.Window
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
@ -26,148 +21,124 @@ T.ComboBox {
|
|||||||
|
|
||||||
property ColorScheme colorScheme
|
property ColorScheme colorScheme
|
||||||
|
|
||||||
implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
|
bottomPadding: 5
|
||||||
implicitContentWidth + leftPadding + rightPadding)
|
font.family: ProtonStyle.font_family
|
||||||
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
|
font.letterSpacing: ProtonStyle.body_letter_spacing
|
||||||
implicitContentHeight + topPadding + bottomPadding,
|
font.pixelSize: ProtonStyle.body_font_size
|
||||||
implicitIndicatorHeight + topPadding + bottomPadding)
|
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)
|
leftPadding: 12 + (!root.mirrored || !indicator || !indicator.visible ? 0 : indicator.width + spacing)
|
||||||
rightPadding: 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
|
spacing: 8
|
||||||
|
topPadding: 5
|
||||||
|
|
||||||
font.family: ProtonStyle.font_family
|
background: Rectangle {
|
||||||
font.weight: ProtonStyle.fontWeight_400
|
border.color: root.colorScheme.border_norm
|
||||||
font.pixelSize: ProtonStyle.body_font_size
|
border.width: 1
|
||||||
font.letterSpacing: ProtonStyle.body_letter_spacing
|
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 {
|
contentItem: T.TextField {
|
||||||
padding: 5
|
|
||||||
|
|
||||||
text: root.editable ? root.editText : root.displayText
|
|
||||||
font: root.font
|
|
||||||
|
|
||||||
enabled: root.editable
|
|
||||||
autoScroll: 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
|
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
|
validator: root.validator
|
||||||
verticalAlignment: TextInput.AlignVCenter
|
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 {
|
background: Rectangle {
|
||||||
radius: ProtonStyle.context_item_radius
|
|
||||||
visible: root.enabled && root.editable && !root.flat
|
|
||||||
border.color: {
|
border.color: {
|
||||||
if (root.activeFocus) {
|
if (root.activeFocus) {
|
||||||
return root.colorScheme.interaction_norm
|
return root.colorScheme.interaction_norm;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (root.hovered || root.activeFocus) {
|
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
|
border.width: 1
|
||||||
color: root.colorScheme.background_norm
|
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 {
|
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
|
property bool selected: root.currentIndex === index
|
||||||
|
|
||||||
|
font: root.font
|
||||||
highlighted: root.highlightedIndex === index
|
highlighted: root.highlightedIndex === index
|
||||||
|
hoverEnabled: root.hoverEnabled
|
||||||
palette.highlightedText: selected ? root.colorScheme.text_invert : root.colorScheme.text_norm
|
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 {
|
background: PaddedRectangle {
|
||||||
radius: ProtonStyle.context_item_radius
|
|
||||||
color: {
|
color: {
|
||||||
if (parent.down) {
|
if (parent.down) {
|
||||||
return root.colorScheme.interaction_default_active
|
return root.colorScheme.interaction_default_active;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (parent.selected) {
|
if (parent.selected) {
|
||||||
return root.colorScheme.interaction_norm
|
return root.colorScheme.interaction_norm;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (parent.hovered || parent.highlighted) {
|
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 {
|
popup: T.Popup {
|
||||||
y: root.height
|
bottomMargin: 8
|
||||||
width: root.width
|
|
||||||
height: Math.min(contentItem.implicitHeight, root.Window.height - topMargin - bottomMargin)
|
height: Math.min(contentItem.implicitHeight, root.Window.height - topMargin - bottomMargin)
|
||||||
topMargin: 8
|
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 {
|
contentItem: Item {
|
||||||
implicitHeight: children[0].implicitHeight + children[0].anchors.topMargin + children[0].anchors.bottomMargin
|
implicitHeight: children[0].implicitHeight + children[0].anchors.topMargin + children[0].anchors.bottomMargin
|
||||||
implicitWidth: children[0].implicitWidth + children[0].anchors.leftMargin + children[0].anchors.rightMargin
|
implicitWidth: children[0].implicitWidth + children[0].anchors.leftMargin + children[0].anchors.rightMargin
|
||||||
@ -175,21 +146,14 @@ T.ComboBox {
|
|||||||
ListView {
|
ListView {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: 8
|
anchors.margins: 8
|
||||||
|
currentIndex: root.highlightedIndex
|
||||||
implicitHeight: contentHeight
|
implicitHeight: contentHeight
|
||||||
model: root.delegateModel
|
model: root.delegateModel
|
||||||
currentIndex: root.highlightedIndex
|
|
||||||
spacing: 4
|
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
|
// Copyright (c) 2023 Proton AG
|
||||||
//
|
|
||||||
// This file is part of Proton Mail Bridge.
|
// This file is part of Proton Mail Bridge.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
// 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
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
// 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/>.
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import QtQml
|
import QtQml
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Templates as T
|
import QtQuick.Templates as T
|
||||||
@ -23,58 +18,46 @@ import QtQuick.Controls.impl
|
|||||||
|
|
||||||
T.Dialog {
|
T.Dialog {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property ColorScheme colorScheme
|
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
|
readonly property var occurred: shouldShow ? new Date() : undefined
|
||||||
function open() {
|
readonly property int popupType: ApplicationWindow.PopupType.Dialog
|
||||||
root.shouldShow = true
|
property bool shouldShow: false
|
||||||
}
|
|
||||||
|
|
||||||
function close() {
|
function close() {
|
||||||
root.shouldShow = false
|
root.shouldShow = false;
|
||||||
|
}
|
||||||
|
function open() {
|
||||||
|
root.shouldShow = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
anchors.centerIn: Overlay.overlay
|
anchors.centerIn: Overlay.overlay
|
||||||
|
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,
|
implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, contentWidth + leftPadding + rightPadding, implicitHeaderWidth, implicitFooterWidth)
|
||||||
contentWidth + leftPadding + rightPadding,
|
|
||||||
implicitHeaderWidth,
|
|
||||||
implicitFooterWidth)
|
|
||||||
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
|
|
||||||
contentHeight + topPadding + bottomPadding
|
|
||||||
+ (implicitHeaderHeight > 0 ? implicitHeaderHeight + spacing : 0)
|
|
||||||
+ (implicitFooterHeight > 0 ? implicitFooterHeight + spacing : 0))
|
|
||||||
|
|
||||||
padding: 24
|
padding: 24
|
||||||
|
|
||||||
|
// TODO: Add DropShadow here
|
||||||
|
T.Overlay.modal: Rectangle {
|
||||||
|
color: root.colorScheme.backdrop_norm
|
||||||
|
}
|
||||||
|
T.Overlay.modeless: Rectangle {
|
||||||
|
color: "transparent"
|
||||||
|
}
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
color: root.colorScheme.background_norm
|
color: root.colorScheme.background_norm
|
||||||
radius: ProtonStyle.dialog_radius
|
radius: ProtonStyle.dialog_radius
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Add DropShadow here
|
Component.onCompleted: {
|
||||||
|
if (!ApplicationWindow.window) {
|
||||||
T.Overlay.modal: Rectangle {
|
return;
|
||||||
color: root.colorScheme.backdrop_norm
|
}
|
||||||
}
|
if (ApplicationWindow.window.popups === undefined) {
|
||||||
|
return;
|
||||||
T.Overlay.modeless: Rectangle {
|
}
|
||||||
color: "transparent"
|
const obj = this;
|
||||||
|
ApplicationWindow.window.popups.append({
|
||||||
|
"obj": obj
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,32 +1,23 @@
|
|||||||
// Copyright (c) 2023 Proton AG
|
// Copyright (c) 2023 Proton AG
|
||||||
//
|
|
||||||
// This file is part of Proton Mail Bridge.
|
// This file is part of Proton Mail Bridge.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
// 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
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
// 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/>.
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Controls.impl
|
import QtQuick.Controls.impl
|
||||||
import QtQuick.Templates as T
|
import QtQuick.Templates as T
|
||||||
|
|
||||||
import "." as Proton
|
import "." as Proton
|
||||||
|
|
||||||
T.Label {
|
T.Label {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property ColorScheme colorScheme
|
|
||||||
|
|
||||||
enum LabelType {
|
enum LabelType {
|
||||||
// weight 700, size 28, height 36
|
// weight 700, size 28, height 36
|
||||||
Heading,
|
Heading,
|
||||||
@ -47,96 +38,92 @@ T.Label {
|
|||||||
// weight 700, size 12, height 16, spacing 0.4
|
// weight 700, size 12, height 16, spacing 0.4
|
||||||
Caption_bold
|
Caption_bold
|
||||||
}
|
}
|
||||||
|
|
||||||
|
property ColorScheme colorScheme
|
||||||
property int type: Proton.Label.LabelType.Body
|
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
|
color: root.enabled ? root.colorScheme.text_norm : root.colorScheme.text_disabled
|
||||||
linkColor: root.colorScheme.interaction_norm
|
|
||||||
palette.link: linkColor
|
|
||||||
|
|
||||||
font.family: ProtonStyle.font_family
|
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: {
|
font.letterSpacing: {
|
||||||
switch (root.type) {
|
switch (root.type) {
|
||||||
case Proton.Label.LabelType.Heading:
|
case Proton.Label.LabelType.Heading:
|
||||||
case Proton.Label.LabelType.Title:
|
case Proton.Label.LabelType.Title:
|
||||||
case Proton.Label.LabelType.Lead:
|
case Proton.Label.LabelType.Lead:
|
||||||
return 0
|
return 0;
|
||||||
case Proton.Label.LabelType.Body:
|
case Proton.Label.LabelType.Body:
|
||||||
case Proton.Label.LabelType.Body_semibold:
|
case Proton.Label.LabelType.Body_semibold:
|
||||||
case Proton.Label.LabelType.Body_bold:
|
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:
|
||||||
case Proton.Label.LabelType.Caption_semibold:
|
case Proton.Label.LabelType.Caption_semibold:
|
||||||
case Proton.Label.LabelType.Caption_bold:
|
case Proton.Label.LabelType.Caption_bold:
|
||||||
return ProtonStyle.caption_letter_spacing
|
return ProtonStyle.caption_letter_spacing;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
font.pixelSize: {
|
||||||
verticalAlignment: Text.AlignBottom
|
switch (root.type) {
|
||||||
|
case Proton.Label.LabelType.Heading:
|
||||||
function link(url, text) {
|
return ProtonStyle.heading_font_size;
|
||||||
return `<a href="${url}">${text}</a>`
|
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
|
// Copyright (c) 2023 Proton AG
|
||||||
//
|
|
||||||
// This file is part of Proton Mail Bridge.
|
// This file is part of Proton Mail Bridge.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
// 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
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
// 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/>.
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Controls.impl
|
import QtQuick.Controls.impl
|
||||||
@ -27,22 +22,19 @@ T.Menu {
|
|||||||
|
|
||||||
property ColorScheme colorScheme
|
property ColorScheme colorScheme
|
||||||
|
|
||||||
implicitWidth: Math.max(
|
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, contentHeight + topPadding + bottomPadding)
|
||||||
implicitBackgroundWidth + leftInset + rightInset,
|
implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, contentWidth + leftPadding + rightPadding)
|
||||||
contentWidth + leftPadding + rightPadding
|
|
||||||
)
|
|
||||||
implicitHeight: Math.max(
|
|
||||||
implicitBackgroundHeight + topInset + bottomInset,
|
|
||||||
contentHeight + topPadding + bottomPadding
|
|
||||||
)
|
|
||||||
|
|
||||||
margins: 0
|
margins: 0
|
||||||
overlap: 1
|
overlap: 1
|
||||||
|
|
||||||
delegate: MenuItem {
|
background: Rectangle {
|
||||||
colorScheme: control.colorScheme
|
border.color: colorScheme.border_weak
|
||||||
|
border.width: 1
|
||||||
|
color: colorScheme.background_norm
|
||||||
|
implicitHeight: 40
|
||||||
|
implicitWidth: 200
|
||||||
|
radius: ProtonStyle.account_row_radius
|
||||||
}
|
}
|
||||||
|
|
||||||
contentItem: Item {
|
contentItem: Item {
|
||||||
implicitHeight: children[0].implicitHeight + children[0].anchors.topMargin + children[0].anchors.bottomMargin
|
implicitHeight: children[0].implicitHeight + children[0].anchors.topMargin + children[0].anchors.bottomMargin
|
||||||
implicitWidth: children[0].implicitWidth + children[0].anchors.leftMargin + children[0].anchors.rightMargin
|
implicitWidth: children[0].implicitWidth + children[0].anchors.leftMargin + children[0].anchors.rightMargin
|
||||||
@ -50,23 +42,17 @@ T.Menu {
|
|||||||
ListView {
|
ListView {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: 8
|
anchors.margins: 8
|
||||||
|
|
||||||
implicitHeight: contentHeight
|
|
||||||
model: control.contentModel
|
|
||||||
interactive: Window.window ? contentHeight > Window.window.height : false
|
|
||||||
clip: true
|
clip: true
|
||||||
currentIndex: control.currentIndex
|
currentIndex: control.currentIndex
|
||||||
|
implicitHeight: contentHeight
|
||||||
|
interactive: Window.window ? contentHeight > Window.window.height : false
|
||||||
|
model: control.contentModel
|
||||||
|
|
||||||
ScrollIndicator.vertical: ScrollIndicator {}
|
ScrollIndicator.vertical: ScrollIndicator {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
delegate: MenuItem {
|
||||||
background: Rectangle {
|
colorScheme: control.colorScheme
|
||||||
implicitWidth: 200
|
|
||||||
implicitHeight: 40
|
|
||||||
color: colorScheme.background_norm
|
|
||||||
border.width: 1
|
|
||||||
border.color: colorScheme.border_weak
|
|
||||||
radius: ProtonStyle.account_row_radius
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,20 +1,15 @@
|
|||||||
// Copyright (c) 2023 Proton AG
|
// Copyright (c) 2023 Proton AG
|
||||||
//
|
|
||||||
// This file is part of Proton Mail Bridge.
|
// This file is part of Proton Mail Bridge.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
// 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
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
// 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/>.
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Controls.impl
|
import QtQuick.Controls.impl
|
||||||
@ -26,46 +21,39 @@ T.MenuItem {
|
|||||||
|
|
||||||
property ColorScheme colorScheme
|
property ColorScheme colorScheme
|
||||||
|
|
||||||
width: parent.width // required. Other item overflows to the right of the menu and get clipped.
|
font.family: ProtonStyle.font_family
|
||||||
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
|
font.letterSpacing: ProtonStyle.body_letter_spacing
|
||||||
implicitContentHeight + topPadding + bottomPadding,
|
font.pixelSize: ProtonStyle.body_font_size
|
||||||
implicitIndicatorHeight + topPadding + bottomPadding)
|
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
|
padding: 12
|
||||||
spacing: 6
|
spacing: 6
|
||||||
|
width: parent.width // required. Other item overflows to the right of the menu and get clipped.
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
background: Rectangle {
|
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
|
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
|
// Copyright (c) 2023 Proton AG
|
||||||
//
|
|
||||||
// This file is part of Proton Mail Bridge.
|
// This file is part of Proton Mail Bridge.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
// 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
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
// 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/>.
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import QtQml
|
import QtQml
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
@ -23,45 +18,40 @@ import QtQuick.Templates as T
|
|||||||
|
|
||||||
T.Popup {
|
T.Popup {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property ColorScheme colorScheme
|
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
|
readonly property var occurred: shouldShow ? new Date() : undefined
|
||||||
function open() {
|
property int popupType: ApplicationWindow.PopupType.Banner
|
||||||
root.shouldShow = true
|
property bool shouldShow: false
|
||||||
}
|
|
||||||
|
|
||||||
function close() {
|
function close() {
|
||||||
root.shouldShow = false
|
root.shouldShow = false;
|
||||||
|
}
|
||||||
|
function open() {
|
||||||
|
root.shouldShow = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
|
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, contentHeight + topPadding + bottomPadding)
|
||||||
contentWidth + leftPadding + rightPadding)
|
implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, contentWidth + leftPadding + rightPadding)
|
||||||
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
|
|
||||||
contentHeight + topPadding + bottomPadding)
|
|
||||||
|
|
||||||
// TODO: Add DropShadow here
|
// TODO: Add DropShadow here
|
||||||
|
|
||||||
T.Overlay.modal: Rectangle {
|
T.Overlay.modal: Rectangle {
|
||||||
color: root.colorScheme.backdrop_norm
|
color: root.colorScheme.backdrop_norm
|
||||||
}
|
}
|
||||||
|
|
||||||
T.Overlay.modeless: Rectangle {
|
T.Overlay.modeless: Rectangle {
|
||||||
color: "transparent"
|
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
|
// Copyright (c) 2023 Proton AG
|
||||||
//
|
|
||||||
// This file is part of Proton Mail Bridge.
|
// This file is part of Proton Mail Bridge.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
// 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
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
// 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/>.
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Controls.impl
|
import QtQuick.Controls.impl
|
||||||
import QtQuick.Templates as T
|
import QtQuick.Templates as T
|
||||||
|
|
||||||
T.RadioButton {
|
T.RadioButton {
|
||||||
property ColorScheme colorScheme
|
|
||||||
|
|
||||||
property bool error: false
|
|
||||||
|
|
||||||
id: control
|
id: control
|
||||||
|
|
||||||
implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
|
property ColorScheme colorScheme
|
||||||
implicitContentWidth + leftPadding + rightPadding)
|
property bool error: false
|
||||||
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
|
|
||||||
implicitContentHeight + topPadding + bottomPadding,
|
|
||||||
implicitIndicatorHeight + topPadding + bottomPadding)
|
|
||||||
|
|
||||||
|
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, implicitContentHeight + topPadding + bottomPadding, implicitIndicatorHeight + topPadding + bottomPadding)
|
||||||
|
implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, implicitContentWidth + leftPadding + rightPadding)
|
||||||
padding: 0
|
padding: 0
|
||||||
spacing: 8
|
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 {
|
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
|
implicitHeight: 20
|
||||||
|
implicitWidth: 20
|
||||||
radius: width / 2
|
radius: width / 2
|
||||||
|
|
||||||
x: text ? (control.mirrored ? control.width - width - control.rightPadding : control.leftPadding) : control.leftPadding + (control.availableWidth - 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
|
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 {
|
Rectangle {
|
||||||
x: (parent.width - width) / 2
|
|
||||||
y: (parent.height - height) / 2
|
|
||||||
width: 8
|
|
||||||
height: 8
|
|
||||||
radius: width / 2
|
|
||||||
color: {
|
color: {
|
||||||
if (!control.enabled) {
|
if (!control.enabled) {
|
||||||
return control.colorScheme.field_disabled
|
return control.colorScheme.field_disabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (control.error) {
|
if (control.error) {
|
||||||
return control.colorScheme.signal_danger
|
return control.colorScheme.signal_danger;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (control.hovered || control.activeFocus) {
|
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
|
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
|
// Copyright (c) 2023 Proton AG
|
||||||
//
|
|
||||||
// This file is part of Proton Mail Bridge.
|
// This file is part of Proton Mail Bridge.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
// 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
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
// 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/>.
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
pragma Singleton
|
pragma Singleton
|
||||||
import QtQml
|
import QtQml
|
||||||
import QtQuick
|
import QtQuick
|
||||||
|
import "."
|
||||||
import "./"
|
|
||||||
|
|
||||||
// https://wiki.qt.io/Qml_Styling
|
// https://wiki.qt.io/Qml_Styling
|
||||||
// http://imaginativethinking.ca/make-qml-component-singleton/
|
// http://imaginativethinking.ca/make-qml-component-singleton/
|
||||||
|
|
||||||
QtObject {
|
QtObject {
|
||||||
id: root
|
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 {
|
property ColorScheme darkProminentStyle: ColorScheme {
|
||||||
id: _darkProminentStyle
|
id: _darkProminentStyle
|
||||||
|
|
||||||
prominent: this
|
// Backdrop
|
||||||
|
backdrop_norm: Qt.rgba(0, 0, 0, 0.32)
|
||||||
|
background_avatar: "#6D4AFF"
|
||||||
|
|
||||||
// Primary
|
// Background
|
||||||
primary_norm: "#8A6EFF"
|
background_norm: "#16141c"
|
||||||
|
background_strong: "#3F3B4C"
|
||||||
// Interaction-norm
|
background_weak: "#292733"
|
||||||
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
|
||||||
border_norm: "#4A4658"
|
border_norm: "#4A4658"
|
||||||
border_weak: "#343140"
|
border_weak: "#343140"
|
||||||
|
field_disabled: "#3F3B4C"
|
||||||
|
field_hover: "#6D697D"
|
||||||
|
|
||||||
// Background
|
// Field
|
||||||
background_norm: "#16141c"
|
field_norm: "#5B576B"
|
||||||
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
|
||||||
interaction_default: "#00000000"
|
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
|
||||||
scrollbar_norm: "#4A4658"
|
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
|
||||||
signal_danger: "#F5385A"
|
signal_danger: "#F5385A"
|
||||||
signal_danger_hover: "#FF5473"
|
|
||||||
signal_danger_active: "#DC3251"
|
signal_danger_active: "#DC3251"
|
||||||
signal_warning: "#FF9900"
|
signal_danger_hover: "#FF5473"
|
||||||
signal_warning_hover: "#FFB800"
|
|
||||||
signal_warning_active: "#FF8419"
|
|
||||||
signal_success: "#1EA885"
|
|
||||||
signal_success_hover: "#23C299"
|
|
||||||
signal_success_active: "#198F71"
|
|
||||||
signal_info: "#239ECE"
|
signal_info: "#239ECE"
|
||||||
signal_info_hover: "#27B1E8"
|
|
||||||
signal_info_active: "#1F83B5"
|
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
|
// Text
|
||||||
shadow_norm: Qt.rgba(0,0,0,0.4) // #000000 40% x+0 y+1 blur:4
|
text_norm: "#FFFFFF"
|
||||||
shadow_lifted: Qt.rgba(0,0,0,0.48) // #000000 48% x+0 y+8 blur:24
|
text_weak: "#A7A4B5"
|
||||||
|
|
||||||
// Backdrop
|
|
||||||
backdrop_norm: Qt.rgba(0,0,0,0.32)
|
|
||||||
|
|
||||||
// Images
|
// Images
|
||||||
welcome_img: "/qml/icons/img-welcome-dark.png"
|
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: {
|
// Background
|
||||||
switch (Qt.platform.os) {
|
background_norm: "#1C1B24"
|
||||||
case "windows":
|
background_strong: "#3F3B4C"
|
||||||
return "Segoe UI"
|
background_weak: "#292733"
|
||||||
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.
|
// Border
|
||||||
case "linux":
|
border_norm: "#4A4658"
|
||||||
return "Ubuntu"
|
border_weak: "#343140"
|
||||||
default:
|
field_disabled: "#3F3B4C"
|
||||||
console.error("Unknown platform")
|
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 dialog_radius: 12 * root.px // px
|
||||||
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 int fontWeight_100: Font.Thin
|
property int fontWeight_100: Font.Thin
|
||||||
property int fontWeight_200: Font.Light
|
property int fontWeight_200: Font.Light
|
||||||
property int fontWeight_300: Font.ExtraLight
|
property int fontWeight_300: Font.ExtraLight
|
||||||
@ -391,4 +192,179 @@ QtObject {
|
|||||||
property int fontWeight_700: Font.Bold
|
property int fontWeight_700: Font.Bold
|
||||||
property int fontWeight_800: Font.ExtraBold
|
property int fontWeight_800: Font.ExtraBold
|
||||||
property int fontWeight_900: Font.Black
|
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
|
// Copyright (c) 2023 Proton AG
|
||||||
//
|
|
||||||
// This file is part of Proton Mail Bridge.
|
// This file is part of Proton Mail Bridge.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
// 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
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
// 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/>.
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Templates as T
|
import QtQuick.Templates as T
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Controls.impl
|
import QtQuick.Controls.impl
|
||||||
|
|
||||||
T.Switch {
|
T.Switch {
|
||||||
property ColorScheme colorScheme
|
id: control
|
||||||
|
|
||||||
|
property ColorScheme colorScheme
|
||||||
property bool loading: false
|
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?
|
// TODO: store previous enabled state and restore it?
|
||||||
// For now assuming that only enabled buttons could have loading state
|
// For now assuming that only enabled buttons could have loading state
|
||||||
onLoadingChanged: {
|
onLoadingChanged: {
|
||||||
if (loading) {
|
enabled = !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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,54 +1,37 @@
|
|||||||
// Copyright (c) 2023 Proton AG
|
// Copyright (c) 2023 Proton AG
|
||||||
//
|
|
||||||
// This file is part of Proton Mail Bridge.
|
// This file is part of Proton Mail Bridge.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
// 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
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
// 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/>.
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import QtQml
|
import QtQml
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Controls.impl
|
import QtQuick.Controls.impl
|
||||||
import QtQuick.Templates as T
|
import QtQuick.Templates as T
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
|
|
||||||
import "." as Proton
|
import "." as Proton
|
||||||
|
|
||||||
FocusScope {
|
FocusScope {
|
||||||
id: root
|
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 alias activeFocusOnPress: control.activeFocusOnPress
|
||||||
|
property string assistiveText
|
||||||
|
property alias background: control.background
|
||||||
property alias baseUrl: control.baseUrl
|
property alias baseUrl: control.baseUrl
|
||||||
|
property alias bottomInset: control.bottomInset
|
||||||
property alias bottomPadding: control.bottomPadding
|
property alias bottomPadding: control.bottomPadding
|
||||||
property alias canPaste: control.canPaste
|
property alias canPaste: control.canPaste
|
||||||
property alias canRedo: control.canRedo
|
property alias canRedo: control.canRedo
|
||||||
property alias canUndo: control.canUndo
|
property alias canUndo: control.canUndo
|
||||||
property alias color: control.color
|
property alias color: control.color
|
||||||
|
property ColorScheme colorScheme
|
||||||
property alias contentHeight: control.contentHeight
|
property alias contentHeight: control.contentHeight
|
||||||
property alias contentWidth: control.contentWidth
|
property alias contentWidth: control.contentWidth
|
||||||
property alias cursorDelegate: control.cursorDelegate
|
property alias cursorDelegate: control.cursorDelegate
|
||||||
@ -56,21 +39,36 @@ FocusScope {
|
|||||||
property alias cursorRectangle: control.cursorRectangle
|
property alias cursorRectangle: control.cursorRectangle
|
||||||
property alias cursorVisible: control.cursorVisible
|
property alias cursorVisible: control.cursorVisible
|
||||||
property alias effectiveHorizontalAlignment: control.effectiveHorizontalAlignment
|
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 font: control.font
|
||||||
|
property alias hint: hint.text
|
||||||
property alias horizontalAlignment: control.horizontalAlignment
|
property alias horizontalAlignment: control.horizontalAlignment
|
||||||
|
property alias hoverEnabled: control.hoverEnabled
|
||||||
|
property alias hovered: control.hovered
|
||||||
property alias hoveredLink: control.hoveredLink
|
property alias hoveredLink: control.hoveredLink
|
||||||
|
property alias implicitBackgroundHeight: control.implicitBackgroundHeight
|
||||||
|
property alias implicitBackgroundWidth: control.implicitBackgroundWidth
|
||||||
property alias inputMethodComposing: control.inputMethodComposing
|
property alias inputMethodComposing: control.inputMethodComposing
|
||||||
property alias inputMethodHints: control.inputMethodHints
|
property alias inputMethodHints: control.inputMethodHints
|
||||||
|
property alias label: label.text
|
||||||
|
property alias leftInset: control.leftInset
|
||||||
property alias leftPadding: control.leftPadding
|
property alias leftPadding: control.leftPadding
|
||||||
property alias length: control.length
|
property alias length: control.length
|
||||||
property alias lineCount: control.lineCount
|
property alias lineCount: control.lineCount
|
||||||
property alias mouseSelectionMode: control.mouseSelectionMode
|
property alias mouseSelectionMode: control.mouseSelectionMode
|
||||||
property alias overwriteMode: control.overwriteMode
|
property alias overwriteMode: control.overwriteMode
|
||||||
property alias padding: control.padding
|
property alias padding: control.padding
|
||||||
|
property alias palette: control.palette
|
||||||
property alias persistentSelection: control.persistentSelection
|
property alias persistentSelection: control.persistentSelection
|
||||||
|
property alias placeholderText: control.placeholderText
|
||||||
|
property alias placeholderTextColor: control.placeholderTextColor
|
||||||
property alias preeditText: control.preeditText
|
property alias preeditText: control.preeditText
|
||||||
property alias readOnly: control.readOnly
|
property alias readOnly: control.readOnly
|
||||||
property alias renderType: control.renderType
|
property alias renderType: control.renderType
|
||||||
|
property alias rightInset: control.rightInset
|
||||||
property alias rightPadding: control.rightPadding
|
property alias rightPadding: control.rightPadding
|
||||||
property alias selectByKeyboard: control.selectByKeyboard
|
property alias selectByKeyboard: control.selectByKeyboard
|
||||||
property alias selectByMouse: control.selectByMouse
|
property alias selectByMouse: control.selectByMouse
|
||||||
@ -84,61 +82,119 @@ FocusScope {
|
|||||||
property alias textDocument: control.textDocument
|
property alias textDocument: control.textDocument
|
||||||
property alias textFormat: control.textFormat
|
property alias textFormat: control.textFormat
|
||||||
property alias textMargin: control.textMargin
|
property alias textMargin: control.textMargin
|
||||||
|
property alias topInset: control.topInset
|
||||||
property alias topPadding: control.topPadding
|
property alias topPadding: control.topPadding
|
||||||
|
property bool validateOnEditingFinished: true
|
||||||
// We are using our own type of validators. It should be a function
|
// 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
|
// returning an error string in case of error and undefined if no error
|
||||||
property var validator
|
property var validator
|
||||||
property alias verticalAlignment: control.verticalAlignment
|
property alias verticalAlignment: control.verticalAlignment
|
||||||
property alias wrapMode: control.wrapMode
|
property alias wrapMode: control.wrapMode
|
||||||
|
|
||||||
implicitWidth: children[0].implicitWidth
|
signal editingFinished
|
||||||
implicitHeight: children[0].implicitHeight
|
|
||||||
|
|
||||||
property alias label: label.text
|
function append(text) {
|
||||||
property alias hint: hint.text
|
return control.append(text);
|
||||||
property string assistiveText
|
}
|
||||||
property string errorString
|
function clear() {
|
||||||
|
return control.clear();
|
||||||
property bool error: false
|
}
|
||||||
|
function copy() {
|
||||||
signal editingFinished()
|
return control.copy();
|
||||||
|
}
|
||||||
function append(text) { return control.append(text) }
|
function cut() {
|
||||||
function clear() { return control.clear() }
|
return control.cut();
|
||||||
function copy() { return control.copy() }
|
}
|
||||||
function cut() { return control.cut() }
|
function deselect() {
|
||||||
function deselect() { return control.deselect() }
|
return control.deselect();
|
||||||
function getFormattedText(start, end) { return control.getFormattedText(start, end) }
|
}
|
||||||
function getText(start, end) { return control.getText(start, end) }
|
function getFormattedText(start, end) {
|
||||||
function insert(position, text) { return control.insert(position, text) }
|
return control.getFormattedText(start, end);
|
||||||
function isRightToLeft(start, end) { return control.isRightToLeft(start, end) }
|
}
|
||||||
function linkAt(x, y) { return control.linkAt(x, y) }
|
function getText(start, end) {
|
||||||
function moveCursorSelection(position, mode) { return control.moveCursorSelection(position, mode) }
|
return control.getText(start, end);
|
||||||
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() }
|
|
||||||
|
|
||||||
// Calculates the height of the component to make exactly lineNum visible in edit area
|
// Calculates the height of the component to make exactly lineNum visible in edit area
|
||||||
function heightForLinesVisible(lineNum) {
|
function heightForLinesVisible(lineNum) {
|
||||||
var totalHeight = 0
|
let totalHeight = 0;
|
||||||
totalHeight += headerLayout.height
|
totalHeight += headerLayout.height;
|
||||||
totalHeight += footerLayout.height
|
totalHeight += footerLayout.height;
|
||||||
totalHeight += control.topPadding + control.bottomPadding
|
totalHeight += control.topPadding + control.bottomPadding;
|
||||||
totalHeight += lineNum * fontMetrics.height
|
totalHeight += lineNum * fontMetrics.height;
|
||||||
return totalHeight
|
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 {
|
FontMetrics {
|
||||||
id: fontMetrics
|
id: fontMetrics
|
||||||
font: control.font
|
font: control.font
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
spacing: 0
|
spacing: 0
|
||||||
@ -149,154 +205,123 @@ FocusScope {
|
|||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
Proton.Label {
|
Proton.Label {
|
||||||
colorScheme: root.colorScheme
|
|
||||||
id: label
|
id: label
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
color: root.enabled ? root.colorScheme.text_norm : root.colorScheme.text_disabled
|
color: root.enabled ? root.colorScheme.text_norm : root.colorScheme.text_disabled
|
||||||
|
colorScheme: root.colorScheme
|
||||||
type: Proton.Label.LabelType.Body_semibold
|
type: Proton.Label.LabelType.Body_semibold
|
||||||
}
|
}
|
||||||
|
|
||||||
Proton.Label {
|
Proton.Label {
|
||||||
colorScheme: root.colorScheme
|
|
||||||
id: hint
|
id: hint
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
color: root.enabled ? root.colorScheme.text_weak : root.colorScheme.text_disabled
|
color: root.enabled ? root.colorScheme.text_weak : root.colorScheme.text_disabled
|
||||||
|
colorScheme: root.colorScheme
|
||||||
horizontalAlignment: Text.AlignRight
|
horizontalAlignment: Text.AlignRight
|
||||||
type: Proton.Label.LabelType.Caption
|
type: Proton.Label.LabelType.Caption
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ScrollView {
|
ScrollView {
|
||||||
id: controlView
|
id: controlView
|
||||||
|
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
clip: true
|
clip: true
|
||||||
|
|
||||||
T.TextArea {
|
T.TextArea {
|
||||||
id: control
|
id: control
|
||||||
|
KeyNavigation.backtab: root.KeyNavigation.backtab
|
||||||
implicitWidth: Math.max(
|
KeyNavigation.down: root.KeyNavigation.down
|
||||||
contentWidth + leftPadding + rightPadding,
|
KeyNavigation.left: root.KeyNavigation.left
|
||||||
implicitBackgroundWidth + leftInset + rightInset,
|
KeyNavigation.priority: root.KeyNavigation.priority
|
||||||
placeholder.implicitWidth + leftPadding + rightPadding
|
KeyNavigation.right: root.KeyNavigation.right
|
||||||
)
|
KeyNavigation.tab: root.KeyNavigation.tab
|
||||||
implicitHeight: Math.max(
|
KeyNavigation.up: root.KeyNavigation.up
|
||||||
contentHeight + topPadding + bottomPadding,
|
|
||||||
implicitBackgroundHeight + topInset + bottomInset,
|
|
||||||
placeholder.implicitHeight + topPadding + bottomPadding
|
|
||||||
)
|
|
||||||
|
|
||||||
topPadding: 8
|
|
||||||
bottomPadding: 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
|
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
|
// enforcing default focus here within component
|
||||||
focus: root.focus
|
focus: root.focus
|
||||||
|
font.family: ProtonStyle.font_family
|
||||||
KeyNavigation.priority: root.KeyNavigation.priority
|
font.letterSpacing: ProtonStyle.body_letter_spacing
|
||||||
KeyNavigation.backtab: root.KeyNavigation.backtab
|
font.pixelSize: ProtonStyle.body_font_size
|
||||||
KeyNavigation.tab: root.KeyNavigation.tab
|
font.weight: ProtonStyle.fontWeight_400
|
||||||
KeyNavigation.up: root.KeyNavigation.up
|
implicitHeight: Math.max(contentHeight + topPadding + bottomPadding, implicitBackgroundHeight + topInset + bottomInset, placeholder.implicitHeight + topPadding + bottomPadding)
|
||||||
KeyNavigation.down: root.KeyNavigation.down
|
implicitWidth: Math.max(contentWidth + leftPadding + rightPadding, implicitBackgroundWidth + leftInset + rightInset, placeholder.implicitWidth + leftPadding + rightPadding)
|
||||||
KeyNavigation.left: root.KeyNavigation.left
|
leftPadding: 12
|
||||||
KeyNavigation.right: root.KeyNavigation.right
|
placeholderTextColor: control.enabled ? root.colorScheme.text_hint : root.colorScheme.text_disabled
|
||||||
|
rightPadding: 12
|
||||||
selectByMouse: true
|
selectByMouse: true
|
||||||
|
selectedTextColor: control.palette.highlightedText
|
||||||
cursorDelegate: Rectangle {
|
selectionColor: control.palette.highlight
|
||||||
id: cursor
|
topPadding: 8
|
||||||
width: 1
|
wrapMode: TextInput.Wrap
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
||||||
radius: ProtonStyle.input_radius
|
|
||||||
visible: true
|
|
||||||
color: root.colorScheme.background_norm
|
|
||||||
border.color: {
|
border.color: {
|
||||||
if (!control.enabled) {
|
if (!control.enabled) {
|
||||||
return root.colorScheme.field_disabled
|
return root.colorScheme.field_disabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (control.activeFocus) {
|
if (control.activeFocus) {
|
||||||
return root.colorScheme.interaction_norm
|
return root.colorScheme.interaction_norm;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (root.error) {
|
if (root.error) {
|
||||||
return root.colorScheme.signal_danger
|
return root.colorScheme.signal_danger;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (control.hovered) {
|
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
|
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 {
|
RowLayout {
|
||||||
id: footerLayout
|
id: footerLayout
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
@ -304,67 +329,29 @@ FocusScope {
|
|||||||
|
|
||||||
ColorImage {
|
ColorImage {
|
||||||
id: errorIcon
|
id: errorIcon
|
||||||
|
|
||||||
Layout.rightMargin: 4
|
Layout.rightMargin: 4
|
||||||
|
|
||||||
visible: root.error && (assistiveText.text.length > 0)
|
|
||||||
source: "/qml/icons/ic-exclamation-circle-filled.svg"
|
|
||||||
color: root.colorScheme.signal_danger
|
color: root.colorScheme.signal_danger
|
||||||
height: assistiveText.height
|
height: assistiveText.height
|
||||||
|
source: "/qml/icons/ic-exclamation-circle-filled.svg"
|
||||||
sourceSize.height: assistiveText.height
|
sourceSize.height: assistiveText.height
|
||||||
|
visible: root.error && (assistiveText.text.length > 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
Proton.Label {
|
Proton.Label {
|
||||||
colorScheme: root.colorScheme
|
|
||||||
id: assistiveText
|
id: assistiveText
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
text: root.error ? root.errorString : root.assistiveText
|
|
||||||
|
|
||||||
color: {
|
color: {
|
||||||
if (!root.enabled) {
|
if (!root.enabled) {
|
||||||
return root.colorScheme.text_disabled
|
return root.colorScheme.text_disabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (root.error) {
|
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
|
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
|
// Copyright (c) 2023 Proton AG
|
||||||
//
|
|
||||||
// This file is part of Proton Mail Bridge.
|
// This file is part of Proton Mail Bridge.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
// 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
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
// 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/>.
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import QtQml
|
import QtQml
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Controls.impl
|
import QtQuick.Controls.impl
|
||||||
import QtQuick.Templates as T
|
import QtQuick.Templates as T
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
|
|
||||||
import "." as Proton
|
import "." as Proton
|
||||||
|
|
||||||
FocusScope {
|
FocusScope {
|
||||||
id: root
|
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 acceptableInput: control.acceptableInput
|
||||||
property alias activeFocusOnPress: control.activeFocusOnPress
|
property alias activeFocusOnPress: control.activeFocusOnPress
|
||||||
|
property string assistiveText
|
||||||
property alias autoScroll: control.autoScroll
|
property alias autoScroll: control.autoScroll
|
||||||
|
property alias background: control.background
|
||||||
|
property alias bottomInset: control.bottomInset
|
||||||
property alias bottomPadding: control.bottomPadding
|
property alias bottomPadding: control.bottomPadding
|
||||||
property alias canPaste: control.canPaste
|
property alias canPaste: control.canPaste
|
||||||
property alias canRedo: control.canRedo
|
property alias canRedo: control.canRedo
|
||||||
property alias canUndo: control.canUndo
|
property alias canUndo: control.canUndo
|
||||||
property alias color: control.color
|
property alias color: control.color
|
||||||
|
property ColorScheme colorScheme
|
||||||
//property alias contentHeight: control.contentHeight
|
//property alias contentHeight: control.contentHeight
|
||||||
//property alias contentWidth: control.contentWidth
|
//property alias contentWidth: control.contentWidth
|
||||||
property alias cursorDelegate: control.cursorDelegate
|
property alias cursorDelegate: control.cursorDelegate
|
||||||
@ -56,24 +40,39 @@ FocusScope {
|
|||||||
property alias cursorRectangle: control.cursorRectangle
|
property alias cursorRectangle: control.cursorRectangle
|
||||||
property alias cursorVisible: control.cursorVisible
|
property alias cursorVisible: control.cursorVisible
|
||||||
property alias displayText: control.displayText
|
property alias displayText: control.displayText
|
||||||
|
property int echoMode: TextInput.Normal
|
||||||
property alias effectiveHorizontalAlignment: control.effectiveHorizontalAlignment
|
property alias effectiveHorizontalAlignment: control.effectiveHorizontalAlignment
|
||||||
|
property bool error: false
|
||||||
|
property string errorString
|
||||||
|
property alias focusReason: control.focusReason
|
||||||
property alias font: control.font
|
property alias font: control.font
|
||||||
|
property alias hint: hint.text
|
||||||
property alias horizontalAlignment: control.horizontalAlignment
|
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 inputMask: control.inputMask
|
||||||
property alias inputMethodComposing: control.inputMethodComposing
|
property alias inputMethodComposing: control.inputMethodComposing
|
||||||
property alias inputMethodHints: control.inputMethodHints
|
property alias inputMethodHints: control.inputMethodHints
|
||||||
|
property alias label: label.text
|
||||||
|
property alias leftInset: control.leftInset
|
||||||
property alias leftPadding: control.leftPadding
|
property alias leftPadding: control.leftPadding
|
||||||
property alias length: control.length
|
property alias length: control.length
|
||||||
property alias maximumLength: control.maximumLength
|
property alias maximumLength: control.maximumLength
|
||||||
property alias mouseSelectionMode: control.mouseSelectionMode
|
property alias mouseSelectionMode: control.mouseSelectionMode
|
||||||
property alias overwriteMode: control.overwriteMode
|
property alias overwriteMode: control.overwriteMode
|
||||||
property alias padding: control.padding
|
property alias padding: control.padding
|
||||||
|
property alias palette: control.palette
|
||||||
property alias passwordCharacter: control.passwordCharacter
|
property alias passwordCharacter: control.passwordCharacter
|
||||||
property alias passwordMaskDelay: control.passwordMaskDelay
|
property alias passwordMaskDelay: control.passwordMaskDelay
|
||||||
property alias persistentSelection: control.persistentSelection
|
property alias persistentSelection: control.persistentSelection
|
||||||
|
property alias placeholderText: control.placeholderText
|
||||||
|
property alias placeholderTextColor: control.placeholderTextColor
|
||||||
property alias preeditText: control.preeditText
|
property alias preeditText: control.preeditText
|
||||||
property alias readOnly: control.readOnly
|
property alias readOnly: control.readOnly
|
||||||
property alias renderType: control.renderType
|
property alias renderType: control.renderType
|
||||||
|
property alias rightInset: control.rightInset
|
||||||
property alias rightPadding: control.rightPadding
|
property alias rightPadding: control.rightPadding
|
||||||
property alias selectByMouse: control.selectByMouse
|
property alias selectByMouse: control.selectByMouse
|
||||||
property alias selectedText: control.selectedText
|
property alias selectedText: control.selectedText
|
||||||
@ -82,47 +81,102 @@ FocusScope {
|
|||||||
property alias selectionEnd: control.selectionEnd
|
property alias selectionEnd: control.selectionEnd
|
||||||
property alias selectionStart: control.selectionStart
|
property alias selectionStart: control.selectionStart
|
||||||
property alias text: control.text
|
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
|
// 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
|
// returning an error string in case of error and undefined if no error
|
||||||
property var validator
|
property var validator
|
||||||
property alias verticalAlignment: control.verticalAlignment
|
property alias verticalAlignment: control.verticalAlignment
|
||||||
property alias wrapMode: control.wrapMode
|
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
|
implicitHeight: children[0].implicitHeight
|
||||||
|
implicitWidth: children[0].implicitWidth
|
||||||
|
|
||||||
property alias label: label.text
|
onEditingFinished: {
|
||||||
property alias hint: hint.text
|
if (!validateOnEditingFinished) {
|
||||||
property string assistiveText
|
return;
|
||||||
property string errorString
|
}
|
||||||
|
validate();
|
||||||
property int echoMode: TextInput.Normal
|
}
|
||||||
|
onTextChanged: {
|
||||||
property bool error: false
|
root.error = false;
|
||||||
|
root.errorString = "";
|
||||||
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() }
|
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@ -133,19 +187,18 @@ FocusScope {
|
|||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
Proton.Label {
|
Proton.Label {
|
||||||
colorScheme: root.colorScheme
|
|
||||||
id: label
|
id: label
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
colorScheme: root.colorScheme
|
||||||
type: Proton.Label.LabelType.Body_semibold
|
type: Proton.Label.LabelType.Body_semibold
|
||||||
}
|
}
|
||||||
|
|
||||||
Proton.Label {
|
Proton.Label {
|
||||||
colorScheme: root.colorScheme
|
|
||||||
id: hint
|
id: hint
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
color: root.enabled ? root.colorScheme.text_weak : root.colorScheme.text_disabled
|
color: root.enabled ? root.colorScheme.text_weak : root.colorScheme.text_disabled
|
||||||
|
colorScheme: root.colorScheme
|
||||||
horizontalAlignment: Text.AlignRight
|
horizontalAlignment: Text.AlignRight
|
||||||
type: Proton.Label.LabelType.Caption
|
type: Proton.Label.LabelType.Caption
|
||||||
}
|
}
|
||||||
@ -156,36 +209,29 @@ FocusScope {
|
|||||||
// will be adjusted to background's width making text field and eye button overlap
|
// will be adjusted to background's width making text field and eye button overlap
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: background
|
id: background
|
||||||
|
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
radius: ProtonStyle.input_radius
|
|
||||||
visible: true
|
|
||||||
color: root.colorScheme.background_norm
|
|
||||||
border.color: {
|
border.color: {
|
||||||
if (!control.enabled) {
|
if (!control.enabled) {
|
||||||
return root.colorScheme.field_disabled
|
return root.colorScheme.field_disabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (control.activeFocus) {
|
if (control.activeFocus) {
|
||||||
return root.colorScheme.interaction_norm
|
return root.colorScheme.interaction_norm;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (root.error) {
|
if (root.error) {
|
||||||
return root.colorScheme.signal_danger
|
return root.colorScheme.signal_danger;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (control.hovered) {
|
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
|
border.width: 1
|
||||||
|
color: root.colorScheme.background_norm
|
||||||
implicitWidth: children[0].implicitWidth
|
|
||||||
implicitHeight: children[0].implicitHeight
|
implicitHeight: children[0].implicitHeight
|
||||||
|
implicitWidth: children[0].implicitWidth
|
||||||
|
radius: ProtonStyle.input_radius
|
||||||
|
visible: true
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@ -193,190 +239,135 @@ FocusScope {
|
|||||||
|
|
||||||
T.TextField {
|
T.TextField {
|
||||||
id: control
|
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.fillHeight: true
|
||||||
Layout.fillWidth: 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
|
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
|
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
|
echoMode: eyeButton.checked ? TextInput.Normal : root.echoMode
|
||||||
|
|
||||||
// enforcing default focus here within component
|
// enforcing default focus here within component
|
||||||
focus: true
|
focus: true
|
||||||
|
font.family: ProtonStyle.font_family
|
||||||
KeyNavigation.priority: root.KeyNavigation.priority
|
font.letterSpacing: ProtonStyle.body_letter_spacing
|
||||||
KeyNavigation.backtab: root.KeyNavigation.backtab
|
font.pixelSize: ProtonStyle.body_font_size
|
||||||
KeyNavigation.tab: root.KeyNavigation.tab
|
font.weight: ProtonStyle.fontWeight_400
|
||||||
KeyNavigation.up: root.KeyNavigation.up
|
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, contentHeight + topPadding + bottomPadding, placeholder.implicitHeight + topPadding + bottomPadding)
|
||||||
KeyNavigation.down: root.KeyNavigation.down
|
implicitWidth: implicitBackgroundWidth + leftInset + rightInset || Math.max(contentWidth, placeholder.implicitWidth) + leftPadding + rightPadding
|
||||||
KeyNavigation.left: root.KeyNavigation.left
|
leftPadding: 12
|
||||||
KeyNavigation.right: root.KeyNavigation.right
|
placeholderTextColor: control.enabled ? root.colorScheme.text_hint : root.colorScheme.text_disabled
|
||||||
|
rightPadding: 12
|
||||||
selectByMouse: true
|
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 {
|
cursorDelegate: Rectangle {
|
||||||
id: cursor
|
id: cursor
|
||||||
width: 1
|
|
||||||
color: root.colorScheme.interaction_norm
|
color: root.colorScheme.interaction_norm
|
||||||
visible: control.activeFocus && !control.readOnly && control.selectionStart === control.selectionEnd
|
visible: control.activeFocus && !control.readOnly && control.selectionStart === control.selectionEnd
|
||||||
|
width: 1
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: control
|
|
||||||
function onCursorPositionChanged() {
|
function onCursorPositionChanged() {
|
||||||
// keep a moving cursor visible
|
// keep a moving cursor visible
|
||||||
cursor.opacity = 1
|
cursor.opacity = 1;
|
||||||
timer.restart()
|
timer.restart();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
target: control
|
||||||
|
}
|
||||||
Timer {
|
Timer {
|
||||||
id: timer
|
id: timer
|
||||||
running: control.activeFocus && !control.readOnly
|
|
||||||
repeat: true
|
|
||||||
interval: Qt.styleHints.cursorFlashTime / 2
|
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
|
// force the cursor visible when gaining focus
|
||||||
onRunningChanged: cursor.opacity = 1
|
onRunningChanged: cursor.opacity = 1
|
||||||
|
onTriggered: cursor.opacity = !cursor.opacity ? 1 : 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onAccepted: {
|
||||||
|
root.accepted();
|
||||||
|
}
|
||||||
|
onEditingFinished: {
|
||||||
|
root.editingFinished();
|
||||||
|
}
|
||||||
|
onTextEdited: {
|
||||||
|
root.textEdited();
|
||||||
|
}
|
||||||
|
|
||||||
PlaceholderText {
|
PlaceholderText {
|
||||||
id: placeholder
|
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
|
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
|
verticalAlignment: control.verticalAlignment
|
||||||
visible: !control.length && !control.preeditText && (!control.activeFocus || control.horizontalAlignment !== Qt.AlignHCenter)
|
visible: !control.length && !control.preeditText && (!control.activeFocus || control.horizontalAlignment !== Qt.AlignHCenter)
|
||||||
elide: Text.ElideRight
|
width: control.width - (control.leftPadding + control.rightPadding)
|
||||||
renderType: control.renderType
|
x: control.leftPadding
|
||||||
}
|
y: control.topPadding
|
||||||
|
|
||||||
background: Item {
|
|
||||||
implicitWidth: 80
|
|
||||||
implicitHeight: 36
|
|
||||||
visible: false
|
|
||||||
}
|
|
||||||
|
|
||||||
onAccepted: {
|
|
||||||
root.accepted()
|
|
||||||
}
|
|
||||||
onEditingFinished: {
|
|
||||||
root.editingFinished()
|
|
||||||
}
|
|
||||||
onTextEdited: {
|
|
||||||
root.textEdited()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Proton.Button {
|
Proton.Button {
|
||||||
colorScheme: root.colorScheme
|
|
||||||
id: eyeButton
|
id: eyeButton
|
||||||
|
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
|
|
||||||
visible: root.echoMode === TextInput.Password
|
|
||||||
icon.color: control.color
|
|
||||||
checkable: true
|
checkable: true
|
||||||
|
colorScheme: root.colorScheme
|
||||||
|
icon.color: control.color
|
||||||
icon.source: checked ? "../icons/ic-eye-slash.svg" : "../icons/ic-eye.svg"
|
icon.source: checked ? "../icons/ic-eye-slash.svg" : "../icons/ic-eye.svg"
|
||||||
|
visible: root.echoMode === TextInput.Password
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
ColorImage {
|
ColorImage {
|
||||||
id: errorIcon
|
id: errorIcon
|
||||||
|
|
||||||
Layout.rightMargin: 4
|
Layout.rightMargin: 4
|
||||||
|
|
||||||
visible: root.error && (assistiveText.text.length > 0)
|
|
||||||
source: "../icons/ic-exclamation-circle-filled.svg"
|
|
||||||
color: root.colorScheme.signal_danger
|
color: root.colorScheme.signal_danger
|
||||||
height: assistiveText.lineHeight
|
height: assistiveText.lineHeight
|
||||||
|
source: "../icons/ic-exclamation-circle-filled.svg"
|
||||||
sourceSize.height: assistiveText.lineHeight
|
sourceSize.height: assistiveText.lineHeight
|
||||||
|
visible: root.error && (assistiveText.text.length > 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
Proton.Label {
|
Proton.Label {
|
||||||
colorScheme: root.colorScheme
|
|
||||||
id: assistiveText
|
id: assistiveText
|
||||||
|
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
wrapMode: Text.WordWrap
|
|
||||||
|
|
||||||
text: root.error ? root.errorString : root.assistiveText
|
|
||||||
|
|
||||||
color: {
|
color: {
|
||||||
if (!root.enabled) {
|
if (!root.enabled) {
|
||||||
return root.colorScheme.text_disabled
|
return root.colorScheme.text_disabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (root.error) {
|
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
|
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
|
// Copyright (c) 2023 Proton AG
|
||||||
//
|
|
||||||
// This file is part of Proton Mail Bridge.
|
// This file is part of Proton Mail Bridge.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
// 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
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
// 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/>.
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
@ -22,92 +17,106 @@ import QtQuick.Controls.impl
|
|||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
property var colorScheme
|
|
||||||
|
property bool _disabled: !enabled
|
||||||
property bool checked
|
property bool checked
|
||||||
|
property var colorScheme
|
||||||
property bool hovered
|
property bool hovered
|
||||||
property bool loading
|
property bool loading
|
||||||
|
|
||||||
signal clicked
|
signal clicked
|
||||||
|
|
||||||
property bool _disabled: !enabled
|
|
||||||
|
|
||||||
implicitHeight: children[0].implicitHeight
|
implicitHeight: children[0].implicitHeight
|
||||||
implicitWidth: children[0].implicitWidth
|
implicitWidth: children[0].implicitWidth
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: indicator
|
id: indicator
|
||||||
implicitWidth: 40
|
|
||||||
implicitHeight: 24
|
|
||||||
|
|
||||||
radius: width/2
|
|
||||||
color: {
|
color: {
|
||||||
if (root.loading) return "transparent"
|
if (root.loading)
|
||||||
if (root._disabled) return root.colorScheme.background_strong
|
return "transparent";
|
||||||
return root.colorScheme.background_norm
|
if (root._disabled)
|
||||||
}
|
return root.colorScheme.background_strong;
|
||||||
border {
|
return root.colorScheme.background_norm;
|
||||||
width: 1
|
|
||||||
color: (root._disabled || root.loading) ? "transparent" : colorScheme.field_norm
|
|
||||||
}
|
}
|
||||||
|
implicitHeight: 24
|
||||||
|
implicitWidth: 40
|
||||||
|
radius: width / 2
|
||||||
|
|
||||||
|
border {
|
||||||
|
color: (root._disabled || root.loading) ? "transparent" : colorScheme.field_norm
|
||||||
|
width: 1
|
||||||
|
}
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.verticalCenter: indicator.verticalCenter
|
|
||||||
anchors.left: indicator.left
|
anchors.left: indicator.left
|
||||||
anchors.leftMargin: root.checked ? 16 : 0
|
anchors.leftMargin: root.checked ? 16 : 0
|
||||||
width: 24
|
anchors.verticalCenter: indicator.verticalCenter
|
||||||
height: 24
|
|
||||||
radius: width/2
|
|
||||||
color: {
|
color: {
|
||||||
if (root.loading) return "transparent"
|
if (root.loading)
|
||||||
if (root._disabled) return root.colorScheme.field_disabled
|
return "transparent";
|
||||||
|
if (root._disabled)
|
||||||
|
return root.colorScheme.field_disabled;
|
||||||
if (root.checked) {
|
if (root.checked) {
|
||||||
if (root.hovered) return root.colorScheme.interaction_norm_hover
|
if (root.hovered)
|
||||||
return root.colorScheme.interaction_norm
|
return root.colorScheme.interaction_norm_hover;
|
||||||
|
return root.colorScheme.interaction_norm;
|
||||||
} else {
|
} else {
|
||||||
if (root.hovered) return root.colorScheme.field_hover
|
if (root.hovered)
|
||||||
return root.colorScheme.field_norm
|
return root.colorScheme.field_hover;
|
||||||
|
return root.colorScheme.field_norm;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
height: 24
|
||||||
|
radius: width / 2
|
||||||
|
width: 24
|
||||||
|
|
||||||
ColorImage {
|
ColorImage {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
source: "/qml/icons/ic-check.svg"
|
|
||||||
color: root.colorScheme.background_norm
|
color: root.colorScheme.background_norm
|
||||||
height: root.colorScheme.body_font_size
|
height: root.colorScheme.body_font_size
|
||||||
|
source: "/qml/icons/ic-check.svg"
|
||||||
sourceSize.height: root.colorScheme.body_font_size
|
sourceSize.height: root.colorScheme.body_font_size
|
||||||
visible: root.checked
|
visible: root.checked
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ColorImage {
|
ColorImage {
|
||||||
id: loader
|
id: loader
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
source: "/qml/icons/Loader_16.svg"
|
|
||||||
color: root.colorScheme.text_norm
|
color: root.colorScheme.text_norm
|
||||||
height: root.colorScheme.body_font_size
|
height: root.colorScheme.body_font_size
|
||||||
|
source: "/qml/icons/Loader_16.svg"
|
||||||
sourceSize.height: root.colorScheme.body_font_size
|
sourceSize.height: root.colorScheme.body_font_size
|
||||||
visible: root.loading
|
visible: root.loading
|
||||||
|
|
||||||
RotationAnimation {
|
RotationAnimation {
|
||||||
target: loader
|
direction: RotationAnimation.Clockwise
|
||||||
loops: Animation.Infinite
|
|
||||||
duration: 1000
|
duration: 1000
|
||||||
from: 0
|
from: 0
|
||||||
to: 360
|
loops: Animation.Infinite
|
||||||
direction: RotationAnimation.Clockwise
|
|
||||||
running: root.loading
|
running: root.loading
|
||||||
|
target: loader
|
||||||
|
to: 360
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: indicator
|
anchors.fill: indicator
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
onEntered: {root.hovered = true }
|
|
||||||
onExited: {root.hovered = false }
|
onClicked: {
|
||||||
onClicked: { if (root.enabled) root.clicked();}
|
if (root.enabled)
|
||||||
onPressed: {root.hovered = true }
|
root.clicked();
|
||||||
onReleased: { root.hovered = containsMouse }
|
}
|
||||||
|
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
|
// Copyright (c) 2023 Proton AG
|
||||||
//
|
|
||||||
// This file is part of Proton Mail Bridge.
|
// This file is part of Proton Mail Bridge.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
// 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
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
// 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/>.
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
|
|
||||||
import Proton
|
import Proton
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
property var colorScheme
|
enum ActionType {
|
||||||
|
Toggle = 1,
|
||||||
property string text: "Text"
|
Button,
|
||||||
property string actionText: "Action"
|
PrimaryButton
|
||||||
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
|
|
||||||
|
|
||||||
property var _bottomMargin: 20
|
property var _bottomMargin: 20
|
||||||
property var _lineWidth: 1
|
property var _lineWidth: 1
|
||||||
property var _toggleTopMargin: 6
|
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
|
signal clicked
|
||||||
|
|
||||||
enum ActionType {
|
|
||||||
Toggle = 1, Button = 2, PrimaryButton = 3
|
|
||||||
}
|
|
||||||
|
|
||||||
implicitHeight: children[0].implicitHeight + children[0].anchors.topMargin + children[0].anchors.bottomMargin
|
implicitHeight: children[0].implicitHeight + children[0].anchors.topMargin + children[0].anchors.bottomMargin
|
||||||
implicitWidth: children[0].implicitWidth + children[0].anchors.leftMargin + children[0].anchors.rightMargin
|
implicitWidth: children[0].implicitWidth + children[0].anchors.leftMargin + children[0].anchors.rightMargin
|
||||||
|
|
||||||
@ -54,10 +47,9 @@ Item {
|
|||||||
spacing: 16
|
spacing: 16
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
|
Layout.bottomMargin: root._bottomMargin
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.bottomMargin: root._bottomMargin
|
|
||||||
|
|
||||||
spacing: 4
|
spacing: 4
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
@ -66,51 +58,51 @@ Item {
|
|||||||
text: root.text
|
text: root.text
|
||||||
type: Label.Body_semibold
|
type: Label.Body_semibold
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
id: descriptionLabel
|
id: descriptionLabel
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
Layout.preferredWidth: parent.width
|
Layout.preferredWidth: parent.width
|
||||||
|
color: root.colorScheme.text_weak
|
||||||
wrapMode: Text.WordWrap
|
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
text: root.description
|
text: root.description
|
||||||
color: root.colorScheme.text_weak
|
wrapMode: Text.WordWrap
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Toggle {
|
Toggle {
|
||||||
|
id: toggle
|
||||||
Layout.alignment: Qt.AlignTop
|
Layout.alignment: Qt.AlignTop
|
||||||
Layout.topMargin: root._toggleTopMargin
|
Layout.topMargin: root._toggleTopMargin
|
||||||
id: toggle
|
checked: root.checked
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
|
loading: root.loading
|
||||||
visible: root.type === SettingsItem.ActionType.Toggle
|
visible: root.type === SettingsItem.ActionType.Toggle
|
||||||
|
|
||||||
checked: root.checked
|
onClicked: {
|
||||||
loading: root.loading
|
if (!root.loading)
|
||||||
onClicked: { if (!root.loading) root.clicked() }
|
root.clicked();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
Layout.alignment: Qt.AlignTop
|
|
||||||
|
|
||||||
id: button
|
id: button
|
||||||
|
Layout.alignment: Qt.AlignTop
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
visible: root.type === SettingsItem.Button || root.type === SettingsItem.PrimaryButton
|
|
||||||
text: root.actionText + (root.actionIcon != "" ? " " : "")
|
|
||||||
loading: root.loading
|
|
||||||
icon.source: root.actionIcon
|
icon.source: root.actionIcon
|
||||||
onClicked: { if (!root.loading) root.clicked() }
|
loading: root.loading
|
||||||
secondary: root.type !== SettingsItem.PrimaryButton
|
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 {
|
Rectangle {
|
||||||
|
anchors.bottom: root.bottom
|
||||||
anchors.left: root.left
|
anchors.left: root.left
|
||||||
anchors.right: root.right
|
anchors.right: root.right
|
||||||
anchors.bottom: root.bottom
|
|
||||||
color: colorScheme.border_weak
|
color: colorScheme.border_weak
|
||||||
height: root._lineWidth
|
height: root._lineWidth
|
||||||
visible: root.showSeparator
|
visible: root.showSeparator
|
||||||
|
|||||||
@ -1,61 +1,53 @@
|
|||||||
// Copyright (c) 2023 Proton AG
|
// Copyright (c) 2023 Proton AG
|
||||||
//
|
|
||||||
// This file is part of Proton Mail Bridge.
|
// This file is part of Proton Mail Bridge.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
// 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
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
// 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/>.
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Controls.impl
|
import QtQuick.Controls.impl
|
||||||
|
|
||||||
import Proton
|
import Proton
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property var colorScheme
|
property int _bottomMargin: 32
|
||||||
default property alias items: content.children
|
|
||||||
|
|
||||||
signal back()
|
|
||||||
|
|
||||||
property int _leftMargin: 64
|
property int _leftMargin: 64
|
||||||
property int _rightMargin: 64
|
property int _rightMargin: 64
|
||||||
property int _topMargin: 32
|
|
||||||
property int _bottomMargin: 32
|
|
||||||
property int _spacing: 20
|
property int _spacing: 20
|
||||||
|
property int _topMargin: 32
|
||||||
|
property var colorScheme
|
||||||
|
|
||||||
// fillHeight indicates whether the SettingsView should fill all available explicit height set
|
// fillHeight indicates whether the SettingsView should fill all available explicit height set
|
||||||
property bool fillHeight: false
|
property bool fillHeight: false
|
||||||
|
default property alias items: content.children
|
||||||
|
|
||||||
|
signal back
|
||||||
|
|
||||||
ScrollView {
|
ScrollView {
|
||||||
id: scrollView
|
id: scrollView
|
||||||
|
anchors.fill: parent
|
||||||
clip: true
|
clip: true
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
Component.onCompleted: contentItem.boundsBehavior = Flickable.StopAtBounds // Disable the springy effect when scroll reaches top/bottom.
|
Component.onCompleted: contentItem.boundsBehavior = Flickable.StopAtBounds // Disable the springy effect when scroll reaches top/bottom.
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
// can't use parent here because parent is not ScrollView (Flickable inside contentItem inside ScrollView)
|
|
||||||
width: scrollView.availableWidth
|
|
||||||
height: scrollView.availableHeight
|
height: scrollView.availableHeight
|
||||||
|
|
||||||
implicitHeight: children[0].implicitHeight + children[0].anchors.topMargin + children[0].anchors.bottomMargin
|
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
|
// 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
|
// 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
|
// equal to non-wrapped text (i.e. one line only). That will lead to enabling horizontal scroll when not needed
|
||||||
implicitWidth: width
|
implicitWidth: width
|
||||||
|
// can't use parent here because parent is not ScrollView (Flickable inside contentItem inside ScrollView)
|
||||||
|
width: scrollView.availableWidth
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@ -63,16 +55,13 @@ Item {
|
|||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: content
|
id: content
|
||||||
spacing: root._spacing
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
Layout.topMargin: root._topMargin
|
|
||||||
Layout.bottomMargin: root._bottomMargin
|
Layout.bottomMargin: root._bottomMargin
|
||||||
|
Layout.fillWidth: true
|
||||||
Layout.leftMargin: root._leftMargin
|
Layout.leftMargin: root._leftMargin
|
||||||
Layout.rightMargin: root._rightMargin
|
Layout.rightMargin: root._rightMargin
|
||||||
|
Layout.topMargin: root._topMargin
|
||||||
|
spacing: root._spacing
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: filler
|
id: filler
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
@ -81,19 +70,20 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
id: backButton
|
id: backButton
|
||||||
anchors {
|
|
||||||
top: parent.top
|
|
||||||
left: parent.left
|
|
||||||
topMargin: root._topMargin
|
|
||||||
leftMargin: (root._leftMargin-backButton.width) / 2
|
|
||||||
}
|
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
onClicked: root.back()
|
horizontalPadding: 8
|
||||||
icon.source: "/qml/icons/ic-arrow-left.svg"
|
icon.source: "/qml/icons/ic-arrow-left.svg"
|
||||||
secondary: true
|
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
|
// Copyright (c) 2023 Proton AG
|
||||||
//
|
|
||||||
// This file is part of Proton Mail Bridge.
|
// This file is part of Proton Mail Bridge.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
// 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
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
// 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/>.
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Controls.impl
|
import QtQuick.Controls.impl
|
||||||
|
|
||||||
import Proton
|
import Proton
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id:root
|
id: root
|
||||||
|
|
||||||
|
property string address
|
||||||
property ColorScheme colorScheme
|
property ColorScheme colorScheme
|
||||||
property var user
|
property var user
|
||||||
property string address
|
|
||||||
|
|
||||||
signal dismissed()
|
signal dismissed
|
||||||
signal finished()
|
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
|
implicitHeight: children[0].implicitHeight
|
||||||
implicitWidth: children[0].implicitWidth
|
implicitWidth: children[0].implicitWidth
|
||||||
|
|
||||||
|
|
||||||
ListModel {
|
ListModel {
|
||||||
id: clients
|
id: clients
|
||||||
property string name : "Apple Mail"
|
|
||||||
property string iconSource : "/qml/icons/ic-apple-mail.svg"
|
|
||||||
property bool haveAutoSetup: true
|
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 link: "https://proton.me/support/protonmail-bridge-clients-apple-mail"
|
||||||
|
property string name: "Apple Mail"
|
||||||
|
|
||||||
Component.onCompleted : {
|
Component.onCompleted: {
|
||||||
if (Backend.goos == "darwin") {
|
if (Backend.goos === "darwin") {
|
||||||
append({
|
append({
|
||||||
"name" : "Apple Mail",
|
"name": "Apple Mail",
|
||||||
"iconSource" : "/qml/icons/ic-apple-mail.svg",
|
"iconSource": "/qml/icons/ic-apple-mail.svg",
|
||||||
"haveAutoSetup" : true,
|
"haveAutoSetup": true,
|
||||||
"link" : "https://proton.me/support/protonmail-bridge-clients-apple-mail"
|
"link": "https://proton.me/support/protonmail-bridge-clients-apple-mail"
|
||||||
})
|
});
|
||||||
append({
|
append({
|
||||||
"name" : "Microsoft Outlook",
|
"name": "Microsoft Outlook",
|
||||||
"iconSource" : "/qml/icons/ic-microsoft-outlook.svg",
|
"iconSource": "/qml/icons/ic-microsoft-outlook.svg",
|
||||||
"haveAutoSetup" : false,
|
"haveAutoSetup": false,
|
||||||
"link" : "https://proton.me/support/protonmail-bridge-clients-macos-outlook-2019"
|
"link": "https://proton.me/support/protonmail-bridge-clients-macos-outlook-2019"
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
if (Backend.goos == "windows") {
|
if (Backend.goos === "windows") {
|
||||||
append({
|
append({
|
||||||
"name" : "Microsoft Outlook",
|
"name": "Microsoft Outlook",
|
||||||
"iconSource" : "/qml/icons/ic-microsoft-outlook.svg",
|
"iconSource": "/qml/icons/ic-microsoft-outlook.svg",
|
||||||
"haveAutoSetup" : false,
|
"haveAutoSetup": false,
|
||||||
"link" : "https://proton.me/support/protonmail-bridge-clients-windows-outlook-2019"
|
"link": "https://proton.me/support/protonmail-bridge-clients-windows-outlook-2019"
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
append({
|
append({
|
||||||
"name" : "Mozilla Thunderbird",
|
"name": "Mozilla Thunderbird",
|
||||||
"iconSource" : "/qml/icons/ic-mozilla-thunderbird.svg",
|
"iconSource": "/qml/icons/ic-mozilla-thunderbird.svg",
|
||||||
"haveAutoSetup" : false,
|
"haveAutoSetup": false,
|
||||||
"link" : "https://proton.me/support/protonmail-bridge-clients-windows-thunderbird"
|
"link": "https://proton.me/support/protonmail-bridge-clients-windows-thunderbird"
|
||||||
})
|
});
|
||||||
|
|
||||||
append({
|
append({
|
||||||
"name" : "Other",
|
"name": "Other",
|
||||||
"iconSource" : "/qml/icons/ic-other-mail-clients.svg",
|
"iconSource": "/qml/icons/ic-other-mail-clients.svg",
|
||||||
"haveAutoSetup" : false,
|
"haveAutoSetup": false,
|
||||||
"link" : "https://proton.me/support/protonmail-bridge-configure-client"
|
"link": "https://proton.me/support/protonmail-bridge-configure-client"
|
||||||
})
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.fill: root
|
anchors.fill: root
|
||||||
color: root.colorScheme.background_norm
|
color: root.colorScheme.background_norm
|
||||||
}
|
}
|
||||||
|
|
||||||
StackLayout {
|
StackLayout {
|
||||||
id: guidePages
|
id: guidePages
|
||||||
|
anchors.bottomMargin: 70
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.leftMargin: 80
|
anchors.leftMargin: 80
|
||||||
anchors.rightMargin: 80
|
anchors.rightMargin: 80
|
||||||
anchors.topMargin: 30
|
anchors.topMargin: 30
|
||||||
anchors.bottomMargin: 70
|
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
ColumnLayout { // 0: Client selection
|
// 0: Client selection
|
||||||
id: clientView
|
id: clientView
|
||||||
Layout.fillHeight: true
|
|
||||||
|
|
||||||
property int columnWidth: 268
|
property int columnWidth: 268
|
||||||
|
|
||||||
|
Layout.fillHeight: true
|
||||||
spacing: 8
|
spacing: 8
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
@ -112,16 +141,14 @@ Item {
|
|||||||
text: qsTr("Setting up email client")
|
text: qsTr("Setting up email client")
|
||||||
type: Label.LabelType.Heading
|
type: Label.LabelType.Heading
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
|
color: root.colorScheme.text_weak
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
text: address
|
text: address
|
||||||
color: root.colorScheme.text_weak
|
|
||||||
type: Label.LabelType.Lead
|
type: Label.LabelType.Lead
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Layout.topMargin: 32-clientView.spacing
|
Layout.topMargin: 32 - clientView.spacing
|
||||||
spacing: 24
|
spacing: 24
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
@ -134,185 +161,133 @@ Item {
|
|||||||
text: qsTr("Choose an email client")
|
text: qsTr("Choose an email client")
|
||||||
type: Label.LabelType.Body_semibold
|
type: Label.LabelType.Body_semibold
|
||||||
}
|
}
|
||||||
|
|
||||||
ListView {
|
ListView {
|
||||||
id: clientList
|
id: clientList
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
|
model: clients
|
||||||
width: clientView.columnWidth
|
width: clientView.columnWidth
|
||||||
|
|
||||||
model: clients
|
|
||||||
|
|
||||||
highlight: Rectangle {
|
|
||||||
color: root.colorScheme.interaction_default_active
|
|
||||||
radius: ProtonStyle.context_item_radius
|
|
||||||
}
|
|
||||||
|
|
||||||
delegate: Item {
|
delegate: Item {
|
||||||
implicitWidth: clientRow.width
|
|
||||||
implicitHeight: clientRow.height
|
implicitHeight: clientRow.height
|
||||||
|
implicitWidth: clientRow.width
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: clientRow
|
id: clientRow
|
||||||
width: clientList.width
|
width: clientList.width
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Layout.topMargin: 12
|
|
||||||
Layout.bottomMargin: 12
|
Layout.bottomMargin: 12
|
||||||
Layout.leftMargin: 16
|
Layout.leftMargin: 16
|
||||||
Layout.rightMargin: 16
|
Layout.rightMargin: 16
|
||||||
|
Layout.topMargin: 12
|
||||||
|
|
||||||
ColorImage {
|
ColorImage {
|
||||||
source: model.iconSource
|
|
||||||
height: 36
|
height: 36
|
||||||
|
source: model.iconSource
|
||||||
sourceSize.height: 36
|
sourceSize.height: 36
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
colorScheme: root.colorScheme
|
|
||||||
Layout.leftMargin: 12
|
Layout.leftMargin: 12
|
||||||
|
colorScheme: root.colorScheme
|
||||||
text: model.name
|
text: model.name
|
||||||
type: Label.LabelType.Body
|
type: Label.LabelType.Body
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.preferredHeight: 1
|
Layout.preferredHeight: 1
|
||||||
color: root.colorScheme.border_weak
|
color: root.colorScheme.border_weak
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
clientList.currentIndex = index
|
clientList.currentIndex = index;
|
||||||
if (!model.haveAutoSetup) {
|
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 {
|
ColumnLayout {
|
||||||
id: actionColumn
|
id: actionColumn
|
||||||
visible: clientList.currentIndex >= 0 && clients.get(clientList.currentIndex).haveAutoSetup
|
|
||||||
Layout.alignment: Qt.AlignTop
|
Layout.alignment: Qt.AlignTop
|
||||||
|
visible: clientList.currentIndex >= 0 && clients.get(clientList.currentIndex).haveAutoSetup
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
text: qsTr("Choose configuration mode")
|
text: qsTr("Choose configuration mode")
|
||||||
type: Label.LabelType.Body_semibold
|
type: Label.LabelType.Body_semibold
|
||||||
}
|
}
|
||||||
|
|
||||||
ListView {
|
ListView {
|
||||||
id: actionList
|
id: actionList
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
|
model: [qsTr("Configure automatically"), qsTr("Configure manually")]
|
||||||
width: clientView.columnWidth
|
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 {
|
delegate: Item {
|
||||||
implicitWidth: children[0].width
|
|
||||||
implicitHeight: children[0].height
|
implicitHeight: children[0].height
|
||||||
|
implicitWidth: children[0].width
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
width: actionList.width
|
width: actionList.width
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
Layout.topMargin: 20
|
|
||||||
Layout.bottomMargin: 20
|
Layout.bottomMargin: 20
|
||||||
Layout.leftMargin: 16
|
Layout.leftMargin: 16
|
||||||
Layout.rightMargin: 16
|
Layout.rightMargin: 16
|
||||||
|
Layout.topMargin: 20
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
text: modelData
|
text: modelData
|
||||||
type: Label.LabelType.Body
|
type: Label.LabelType.Body
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.preferredHeight: 1
|
Layout.preferredHeight: 1
|
||||||
color: root.colorScheme.border_weak
|
color: root.colorScheme.border_weak
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
actionList.currentIndex = index
|
actionList.currentIndex = index;
|
||||||
root.setupAction(index,clientList.currentIndex)
|
root.setupAction(index, clientList.currentIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
highlight: Rectangle {
|
||||||
|
color: root.colorScheme.interaction_default_active
|
||||||
|
radius: ProtonStyle.context_item_radius
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Item {
|
||||||
Item { Layout.fillHeight: true }
|
Layout.fillHeight: true
|
||||||
|
}
|
||||||
Button {
|
Button {
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
text: qsTr("Set up later")
|
|
||||||
flat: true
|
flat: true
|
||||||
|
text: qsTr("Set up later")
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
root.setupAction(-1,-1)
|
root.setupAction(-1, -1);
|
||||||
if (user) {
|
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
|
// Copyright (c) 2023 Proton AG
|
||||||
//
|
|
||||||
// This file is part of Proton Mail Bridge.
|
// This file is part of Proton Mail Bridge.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
// 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
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
// 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/>.
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import QtQml
|
import QtQml
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Controls.impl
|
import QtQuick.Controls.impl
|
||||||
|
|
||||||
import Proton
|
import Proton
|
||||||
|
|
||||||
FocusScope {
|
FocusScope {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property ColorScheme colorScheme
|
property ColorScheme colorScheme
|
||||||
|
property alias currentIndex: stackLayout.currentIndex
|
||||||
function reset() {
|
property alias username: usernameTextField.text
|
||||||
stackLayout.currentIndex = 0
|
|
||||||
loginNormalLayout.reset()
|
|
||||||
login2FALayout.reset()
|
|
||||||
login2PasswordLayout.reset()
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function abort() {
|
function abort() {
|
||||||
root.reset()
|
root.reset();
|
||||||
Backend.loginAbort(usernameTextField.text)
|
Backend.loginAbort(usernameTextField.text);
|
||||||
|
}
|
||||||
|
function reset() {
|
||||||
|
stackLayout.currentIndex = 0;
|
||||||
|
loginNormalLayout.reset();
|
||||||
|
login2FALayout.reset();
|
||||||
|
login2PasswordLayout.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
implicitHeight: children[0].implicitHeight
|
implicitHeight: children[0].implicitHeight
|
||||||
implicitWidth: children[0].implicitWidth
|
implicitWidth: children[0].implicitWidth
|
||||||
|
|
||||||
property alias username: usernameTextField.text
|
|
||||||
state: "Page 1"
|
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 {
|
StackLayout {
|
||||||
id: stackLayout
|
id: stackLayout
|
||||||
|
function loginFailed() {
|
||||||
|
signInButton.loading = false;
|
||||||
|
usernameTextField.enabled = true;
|
||||||
|
usernameTextField.error = true;
|
||||||
|
passwordTextField.enabled = true;
|
||||||
|
passwordTextField.error = true;
|
||||||
|
}
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
||||||
function loginFailed() {
|
|
||||||
signInButton.loading = false
|
|
||||||
|
|
||||||
usernameTextField.enabled = true
|
|
||||||
usernameTextField.error = true
|
|
||||||
|
|
||||||
passwordTextField.enabled = true
|
|
||||||
passwordTextField.error = true
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: Backend
|
function onLogin2FAError(_) {
|
||||||
|
console.assert(stackLayout.currentIndex === 1, "Unexpected login2FAError");
|
||||||
function onLoginUsernamePasswordError(errorMsg) {
|
twoFAButton.loading = false;
|
||||||
console.assert(stackLayout.currentIndex == 0, "Unexpected loginUsernamePasswordError")
|
twoFactorPasswordTextField.enabled = true;
|
||||||
console.assert(signInButton.loading == true, "Unexpected loginUsernamePasswordError")
|
twoFactorPasswordTextField.error = true;
|
||||||
|
twoFactorPasswordTextField.errorString = qsTr("Your code is incorrect");
|
||||||
stackLayout.loginFailed()
|
twoFactorPasswordTextField.focus = true;
|
||||||
if (errorMsg!="") errorLabel.text = errorMsg
|
|
||||||
else errorLabel.text = qsTr("Incorrect login credentials")
|
|
||||||
}
|
}
|
||||||
|
function onLogin2FAErrorAbort(_) {
|
||||||
function onLoginFreeUserError() {
|
console.assert(stackLayout.currentIndex === 1, "Unexpected login2FAErrorAbort");
|
||||||
console.assert(stackLayout.currentIndex == 0, "Unexpected loginFreeUserError")
|
root.reset();
|
||||||
stackLayout.loginFailed()
|
errorLabel.text = qsTr("Incorrect login credentials. Please try again.");
|
||||||
}
|
}
|
||||||
|
function onLogin2FARequested(username) {
|
||||||
function onLoginConnectionError(errorMsg) {
|
console.assert(stackLayout.currentIndex === 0, "Unexpected login2FARequested");
|
||||||
if (stackLayout.currentIndex == 0 ) {
|
twoFactorUsernameLabel.text = username;
|
||||||
stackLayout.loginFailed()
|
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 onLoginFinished(_) {
|
||||||
function onLogin2FARequested(username) {
|
stackLayout.currentIndex = 0;
|
||||||
console.assert(stackLayout.currentIndex == 0, "Unexpected login2FARequested")
|
root.reset();
|
||||||
twoFactorUsernameLabel.text = username
|
}
|
||||||
stackLayout.currentIndex = 1
|
function onLoginFreeUserError() {
|
||||||
twoFactorPasswordTextField.focus = true
|
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) {
|
target: Backend
|
||||||
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()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: loginNormalLayout
|
id: loginNormalLayout
|
||||||
|
|
||||||
function reset() {
|
function reset() {
|
||||||
signInButton.loading = false
|
signInButton.loading = false;
|
||||||
|
errorLabel.text = "";
|
||||||
errorLabel.text = ""
|
usernameTextField.enabled = true;
|
||||||
|
usernameTextField.error = false;
|
||||||
usernameTextField.enabled = true
|
usernameTextField.errorString = "";
|
||||||
usernameTextField.error = false
|
usernameTextField.focus = true;
|
||||||
usernameTextField.errorString = ""
|
passwordTextField.enabled = true;
|
||||||
usernameTextField.focus = true
|
passwordTextField.error = false;
|
||||||
|
passwordTextField.errorString = "";
|
||||||
passwordTextField.enabled = true
|
passwordTextField.text = "";
|
||||||
passwordTextField.error = false
|
|
||||||
passwordTextField.errorString = ""
|
|
||||||
passwordTextField.text = ""
|
|
||||||
}
|
}
|
||||||
|
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
colorScheme: root.colorScheme
|
|
||||||
text: qsTr("Sign in")
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
Layout.topMargin: 16
|
Layout.topMargin: 16
|
||||||
|
colorScheme: root.colorScheme
|
||||||
|
text: qsTr("Sign in")
|
||||||
type: Label.LabelType.Title
|
type: Label.LabelType.Title
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
colorScheme: root.colorScheme
|
|
||||||
id: subTitle
|
id: subTitle
|
||||||
text: qsTr("Enter your Proton Account details.")
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
Layout.topMargin: 8
|
Layout.topMargin: 8
|
||||||
color: root.colorScheme.text_weak
|
color: root.colorScheme.text_weak
|
||||||
|
colorScheme: root.colorScheme
|
||||||
|
text: qsTr("Enter your Proton Account details.")
|
||||||
type: Label.LabelType.Body
|
type: Label.LabelType.Body
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: 36
|
Layout.topMargin: 36
|
||||||
|
|
||||||
spacing: 0
|
spacing: 0
|
||||||
visible: errorLabel.text.length > 0
|
visible: errorLabel.text.length > 0
|
||||||
|
|
||||||
ColorImage {
|
ColorImage {
|
||||||
color: root.colorScheme.signal_danger
|
color: root.colorScheme.signal_danger
|
||||||
source: "/qml/icons/ic-exclamation-circle-filled.svg"
|
|
||||||
height: errorLabel.lineHeight
|
height: errorLabel.lineHeight
|
||||||
|
source: "/qml/icons/ic-exclamation-circle-filled.svg"
|
||||||
sourceSize.height: errorLabel.lineHeight
|
sourceSize.height: errorLabel.lineHeight
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
colorScheme: root.colorScheme
|
|
||||||
id: errorLabel
|
id: errorLabel
|
||||||
wrapMode: Text.WordWrap
|
Layout.fillWidth: true
|
||||||
Layout.fillWidth: true;
|
|
||||||
Layout.leftMargin: 4
|
Layout.leftMargin: 4
|
||||||
color: root.colorScheme.signal_danger
|
color: root.colorScheme.signal_danger
|
||||||
|
colorScheme: root.colorScheme
|
||||||
type: root.error ? Label.LabelType.Caption_semibold : Label.LabelType.Caption
|
type: root.error ? Label.LabelType.Caption_semibold : Label.LabelType.Caption
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TextField {
|
TextField {
|
||||||
colorScheme: root.colorScheme
|
|
||||||
id: usernameTextField
|
id: usernameTextField
|
||||||
label: qsTr("Email or username")
|
|
||||||
focus: true
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: 24
|
Layout.topMargin: 24
|
||||||
|
colorScheme: root.colorScheme
|
||||||
|
focus: true
|
||||||
|
label: qsTr("Email or username")
|
||||||
validateOnEditingFinished: false
|
validateOnEditingFinished: false
|
||||||
|
validator: function (str) {
|
||||||
onTextChanged: {
|
|
||||||
// remove "invalid username / password error"
|
|
||||||
if (error || errorLabel.text.length > 0) {
|
|
||||||
errorLabel.text = ""
|
|
||||||
usernameTextField.error = false
|
|
||||||
passwordTextField.error = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
validator: function(str) {
|
|
||||||
if (str.length === 0) {
|
if (str.length === 0) {
|
||||||
return qsTr("Enter email or username")
|
return qsTr("Enter email or username");
|
||||||
}
|
}
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onAccepted: passwordTextField.forceActiveFocus()
|
onAccepted: passwordTextField.forceActiveFocus()
|
||||||
}
|
|
||||||
|
|
||||||
TextField {
|
|
||||||
colorScheme: root.colorScheme
|
|
||||||
id: passwordTextField
|
|
||||||
label: qsTr("Password")
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.topMargin: 8
|
|
||||||
echoMode: TextInput.Password
|
|
||||||
validateOnEditingFinished: false
|
|
||||||
|
|
||||||
onTextChanged: {
|
onTextChanged: {
|
||||||
// remove "invalid username / password error"
|
// remove "invalid username / password error"
|
||||||
if (error || errorLabel.text.length > 0) {
|
if (error || errorLabel.text.length > 0) {
|
||||||
errorLabel.text = ""
|
errorLabel.text = "";
|
||||||
usernameTextField.error = false
|
usernameTextField.error = false;
|
||||||
passwordTextField.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) {
|
if (str.length === 0) {
|
||||||
return qsTr("Enter password")
|
return qsTr("Enter password");
|
||||||
}
|
}
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onAccepted: signInButton.checkAndSignIn()
|
onAccepted: signInButton.checkAndSignIn()
|
||||||
}
|
onTextChanged: {
|
||||||
|
// remove "invalid username / password error"
|
||||||
Button {
|
if (error || errorLabel.text.length > 0) {
|
||||||
colorScheme: root.colorScheme
|
errorLabel.text = "";
|
||||||
id: signInButton
|
usernameTextField.error = false;
|
||||||
text: loading ? qsTr("Signing in") : qsTr("Sign in")
|
passwordTextField.error = false;
|
||||||
enabled: !loading
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.topMargin: 24
|
|
||||||
|
|
||||||
|
|
||||||
onClicked: checkAndSignIn()
|
|
||||||
|
|
||||||
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))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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
|
colorScheme: root.colorScheme
|
||||||
textFormat: Text.StyledText
|
enabled: !loading
|
||||||
text: link("https://proton.me/mail/pricing", qsTr("Create or upgrade your account"))
|
text: loading ? qsTr("Signing in") : qsTr("Sign in")
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
checkAndSignIn();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Label {
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
Layout.topMargin: 24
|
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
|
type: Label.LabelType.Body
|
||||||
|
|
||||||
onLinkActivated: {
|
onLinkActivated: {
|
||||||
Qt.openUrlExternally(link)
|
Qt.openUrlExternally(link);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: login2FALayout
|
id: login2FALayout
|
||||||
|
|
||||||
function reset() {
|
function reset() {
|
||||||
twoFAButton.loading = false
|
twoFAButton.loading = false;
|
||||||
|
twoFactorPasswordTextField.enabled = true;
|
||||||
twoFactorPasswordTextField.enabled = true
|
twoFactorPasswordTextField.error = false;
|
||||||
twoFactorPasswordTextField.error = false
|
twoFactorPasswordTextField.errorString = "";
|
||||||
twoFactorPasswordTextField.errorString = ""
|
twoFactorPasswordTextField.text = "";
|
||||||
twoFactorPasswordTextField.text = ""
|
|
||||||
}
|
}
|
||||||
|
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
|
Layout.alignment: Qt.AlignCenter
|
||||||
|
Layout.topMargin: 16
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
text: qsTr("Two-factor authentication")
|
text: qsTr("Two-factor authentication")
|
||||||
Layout.topMargin: 16
|
|
||||||
Layout.alignment: Qt.AlignCenter
|
|
||||||
type: Label.LabelType.Heading
|
type: Label.LabelType.Heading
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
colorScheme: root.colorScheme
|
|
||||||
id: twoFactorUsernameLabel
|
id: twoFactorUsernameLabel
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignCenter
|
Layout.alignment: Qt.AlignCenter
|
||||||
Layout.topMargin: 8
|
Layout.topMargin: 8
|
||||||
type: Label.LabelType.Lead
|
|
||||||
color: root.colorScheme.text_weak
|
color: root.colorScheme.text_weak
|
||||||
}
|
|
||||||
|
|
||||||
TextField {
|
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
|
type: Label.LabelType.Lead
|
||||||
|
}
|
||||||
|
TextField {
|
||||||
id: twoFactorPasswordTextField
|
id: twoFactorPasswordTextField
|
||||||
label: qsTr("Two-factor code")
|
|
||||||
assistiveText: qsTr("Enter the 6-digit code")
|
|
||||||
validateOnEditingFinished: false
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: 32
|
Layout.topMargin: 32
|
||||||
|
assistiveText: qsTr("Enter the 6-digit code")
|
||||||
validator: function(str) {
|
colorScheme: root.colorScheme
|
||||||
|
label: qsTr("Two-factor code")
|
||||||
|
validateOnEditingFinished: false
|
||||||
|
validator: function (str) {
|
||||||
if (str.length === 0) {
|
if (str.length === 0) {
|
||||||
return qsTr("Enter the 6-digit code")
|
return qsTr("Enter the 6-digit code");
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onTextChanged: {
|
|
||||||
if (text.length >= 6) {
|
|
||||||
twoFAButton.onClicked()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onAccepted: {
|
onAccepted: {
|
||||||
twoFAButton.onClicked()
|
twoFAButton.onClicked();
|
||||||
|
}
|
||||||
|
onTextChanged: {
|
||||||
|
if (text.length >= 6) {
|
||||||
|
twoFAButton.onClicked();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
colorScheme: root.colorScheme
|
|
||||||
id: twoFAButton
|
id: twoFAButton
|
||||||
text: loading ? qsTr("Authenticating") : qsTr("Authenticate")
|
|
||||||
enabled: !loading
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: 24
|
Layout.topMargin: 24
|
||||||
|
colorScheme: root.colorScheme
|
||||||
|
enabled: !loading
|
||||||
|
text: loading ? qsTr("Authenticating") : qsTr("Authenticate")
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
twoFactorPasswordTextField.validate()
|
twoFactorPasswordTextField.validate();
|
||||||
|
|
||||||
if (twoFactorPasswordTextField.error) {
|
if (twoFactorPasswordTextField.error) {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
twoFactorPasswordTextField.enabled = false;
|
||||||
twoFactorPasswordTextField.enabled = false
|
loading = true;
|
||||||
loading = true
|
Backend.login2FA(usernameTextField.text, Qt.btoa(twoFactorPasswordTextField.text));
|
||||||
Backend.login2FA(usernameTextField.text, Qt.btoa(twoFactorPasswordTextField.text))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: login2PasswordLayout
|
id: login2PasswordLayout
|
||||||
|
|
||||||
function reset() {
|
function reset() {
|
||||||
secondPasswordButton.loading = false
|
secondPasswordButton.loading = false;
|
||||||
|
secondPasswordTextField.enabled = true;
|
||||||
secondPasswordTextField.enabled = true
|
secondPasswordTextField.error = false;
|
||||||
secondPasswordTextField.error = false
|
secondPasswordTextField.errorString = "";
|
||||||
secondPasswordTextField.errorString = ""
|
secondPasswordTextField.text = "";
|
||||||
secondPasswordTextField.text = ""
|
|
||||||
}
|
}
|
||||||
|
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
|
Layout.alignment: Qt.AlignCenter
|
||||||
|
Layout.topMargin: 16
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
text: qsTr("Unlock your mailbox")
|
text: qsTr("Unlock your mailbox")
|
||||||
Layout.topMargin: 16
|
|
||||||
Layout.alignment: Qt.AlignCenter
|
|
||||||
type: Label.LabelType.Heading
|
type: Label.LabelType.Heading
|
||||||
}
|
}
|
||||||
|
|
||||||
TextField {
|
TextField {
|
||||||
colorScheme: root.colorScheme
|
|
||||||
id: secondPasswordTextField
|
id: secondPasswordTextField
|
||||||
label: qsTr("Mailbox password")
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: 8 + implicitHeight + 24 + subTitle.implicitHeight
|
Layout.topMargin: 8 + implicitHeight + 24 + subTitle.implicitHeight
|
||||||
|
colorScheme: root.colorScheme
|
||||||
echoMode: TextInput.Password
|
echoMode: TextInput.Password
|
||||||
|
label: qsTr("Mailbox password")
|
||||||
validateOnEditingFinished: false
|
validateOnEditingFinished: false
|
||||||
|
validator: function (str) {
|
||||||
validator: function(str) {
|
|
||||||
if (str.length === 0) {
|
if (str.length === 0) {
|
||||||
return qsTr("Enter password")
|
return qsTr("Enter password");
|
||||||
}
|
}
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onAccepted: {
|
onAccepted: {
|
||||||
secondPasswordButton.onClicked()
|
secondPasswordButton.onClicked();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
colorScheme: root.colorScheme
|
|
||||||
id: secondPasswordButton
|
id: secondPasswordButton
|
||||||
text: loading ? qsTr("Unlocking") : qsTr("Unlock")
|
|
||||||
enabled: !loading
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: 24
|
Layout.topMargin: 24
|
||||||
|
colorScheme: root.colorScheme
|
||||||
|
enabled: !loading
|
||||||
|
text: loading ? qsTr("Unlocking") : qsTr("Unlock")
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
secondPasswordTextField.validate()
|
secondPasswordTextField.validate();
|
||||||
|
|
||||||
if (secondPasswordTextField.error) {
|
if (secondPasswordTextField.error) {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
secondPasswordTextField.enabled = false;
|
||||||
secondPasswordTextField.enabled = false
|
loading = true;
|
||||||
loading = true
|
Backend.login2Password(usernameTextField.text, Qt.btoa(secondPasswordTextField.text));
|
||||||
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
|
// Copyright (c) 2023 Proton AG
|
||||||
//
|
|
||||||
// This file is part of Proton Mail Bridge.
|
// This file is part of Proton Mail Bridge.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
// 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
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
// 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/>.
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import QtQml
|
import QtQml
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Controls.impl
|
import QtQuick.Controls.impl
|
||||||
|
|
||||||
import Proton
|
import Proton
|
||||||
|
|
||||||
Dialog {
|
Dialog {
|
||||||
id: root
|
id: root
|
||||||
|
leftPadding: 0
|
||||||
shouldShow: Backend.showSplashScreen
|
|
||||||
modal: true
|
modal: true
|
||||||
|
rightPadding: 0
|
||||||
topPadding : 0
|
shouldShow: Backend.showSplashScreen
|
||||||
leftPadding : 0
|
topPadding: 0
|
||||||
rightPadding : 0
|
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
spacing: 20
|
spacing: 20
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
|
||||||
sourceSize.width: 384
|
|
||||||
sourceSize.height: 144
|
|
||||||
|
|
||||||
Layout.preferredWidth: 384
|
|
||||||
Layout.preferredHeight: 144
|
Layout.preferredHeight: 144
|
||||||
|
Layout.preferredWidth: 384
|
||||||
source: "./icons/img-splash.png"
|
source: "./icons/img-splash.png"
|
||||||
|
sourceSize.height: 144
|
||||||
|
sourceSize.width: 384
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
colorScheme: root.colorScheme;
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignHCenter;
|
|
||||||
Layout.leftMargin: 24
|
Layout.leftMargin: 24
|
||||||
Layout.rightMargin: 24
|
|
||||||
Layout.preferredWidth: 336
|
Layout.preferredWidth: 336
|
||||||
|
Layout.rightMargin: 24
|
||||||
type: Label.Title
|
colorScheme: root.colorScheme
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
text: qsTr("What's new in Bridge")
|
text: qsTr("What's new in Bridge")
|
||||||
|
type: Label.Title
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
width: root.width
|
width: root.width
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
width: 24
|
|
||||||
Layout.leftMargin: 32
|
Layout.leftMargin: 32
|
||||||
Layout.rightMargin: 16
|
Layout.rightMargin: 16
|
||||||
|
width: 24
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
sourceSize.width: 24
|
|
||||||
sourceSize.height: 24
|
|
||||||
source: "./icons/ic-splash-check.svg"
|
source: "./icons/ic-splash-check.svg"
|
||||||
|
sourceSize.height: 24
|
||||||
|
sourceSize.width: 24
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
colorScheme: root.colorScheme
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.alignment: Qt.AlignHCenter;
|
|
||||||
Layout.preferredWidth: 264
|
|
||||||
Layout.leftMargin: 0
|
Layout.leftMargin: 0
|
||||||
|
Layout.preferredWidth: 264
|
||||||
Layout.rightMargin: 24
|
Layout.rightMargin: 24
|
||||||
wrapMode: Text.WordWrap
|
colorScheme: root.colorScheme
|
||||||
|
|
||||||
type: Label.Body
|
|
||||||
horizontalAlignment: Text.AlignLeft
|
horizontalAlignment: Text.AlignLeft
|
||||||
textFormat: Text.StyledText
|
|
||||||
text: qsTr("<b>New IMAP engine</b><br/>For improved stability and performance.")
|
text: qsTr("<b>New IMAP engine</b><br/>For improved stability and performance.")
|
||||||
|
textFormat: Text.StyledText
|
||||||
|
type: Label.Body
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
width: root.width
|
width: root.width
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
width: 24
|
|
||||||
Layout.leftMargin: 32
|
Layout.leftMargin: 32
|
||||||
Layout.rightMargin: 16
|
Layout.rightMargin: 16
|
||||||
|
width: 24
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
sourceSize.width: 24
|
|
||||||
sourceSize.height: 24
|
|
||||||
source: "./icons/ic-splash-check.svg"
|
source: "./icons/ic-splash-check.svg"
|
||||||
|
sourceSize.height: 24
|
||||||
|
sourceSize.width: 24
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
colorScheme: root.colorScheme
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.alignment: Qt.AlignHCenter;
|
|
||||||
Layout.preferredWidth: 264
|
|
||||||
Layout.leftMargin: 0
|
Layout.leftMargin: 0
|
||||||
|
Layout.preferredWidth: 264
|
||||||
Layout.rightMargin: 24
|
Layout.rightMargin: 24
|
||||||
wrapMode: Text.WordWrap
|
colorScheme: root.colorScheme
|
||||||
|
|
||||||
type: Label.Body
|
|
||||||
horizontalAlignment: Text.AlignLeft
|
horizontalAlignment: Text.AlignLeft
|
||||||
textFormat: Text.StyledText
|
|
||||||
text: qsTr("<b>Faster than ever</b><br/>Up to 10x faster syncing and receiving.")
|
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 {
|
RowLayout {
|
||||||
width: root.width
|
width: root.width
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
width: 24
|
|
||||||
Layout.leftMargin: 32
|
Layout.leftMargin: 32
|
||||||
Layout.rightMargin: 16
|
Layout.rightMargin: 16
|
||||||
|
width: 24
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
sourceSize.width: 24
|
|
||||||
sourceSize.height: 24
|
|
||||||
source: "./icons/ic-splash-check.svg"
|
source: "./icons/ic-splash-check.svg"
|
||||||
|
sourceSize.height: 24
|
||||||
|
sourceSize.width: 24
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
colorScheme: root.colorScheme
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.alignment: Qt.AlignHCenter;
|
|
||||||
Layout.preferredWidth: 264
|
|
||||||
Layout.leftMargin: 0
|
Layout.leftMargin: 0
|
||||||
|
Layout.preferredWidth: 264
|
||||||
Layout.rightMargin: 24
|
Layout.rightMargin: 24
|
||||||
wrapMode: Text.WordWrap
|
colorScheme: root.colorScheme
|
||||||
|
|
||||||
type: Label.Body
|
|
||||||
horizontalAlignment: Text.AlignLeft
|
horizontalAlignment: Text.AlignLeft
|
||||||
textFormat: Text.StyledText
|
|
||||||
text: qsTr("<b>Extra security</b><br/>New, encrypted local database and keychain improvements.")
|
text: qsTr("<b>Extra security</b><br/>New, encrypted local database and keychain improvements.")
|
||||||
|
textFormat: Text.StyledText
|
||||||
|
type: Label.Body
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.leftMargin: 24
|
Layout.leftMargin: 24
|
||||||
Layout.rightMargin: 24
|
Layout.rightMargin: 24
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
text: "Got it"
|
text: "Got it"
|
||||||
|
|
||||||
onClicked: Backend.showSplashScreen = false
|
onClicked: Backend.showSplashScreen = false
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
colorScheme: root.colorScheme
|
Layout.alignment: Qt.AlignHCenter
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.alignment: Qt.AlignHCenter;
|
|
||||||
Layout.preferredWidth: 336
|
|
||||||
Layout.leftMargin: 24
|
Layout.leftMargin: 24
|
||||||
|
Layout.preferredWidth: 336
|
||||||
Layout.rightMargin: 24
|
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
|
wrapMode: Text.WordWrap
|
||||||
|
|
||||||
type: Label.Body
|
onLinkActivated: function (link) {
|
||||||
horizontalAlignment: Text.AlignHCenter
|
Qt.openUrlExternally(link);
|
||||||
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) }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,111 +1,93 @@
|
|||||||
|
|
||||||
// Copyright (c) 2023 Proton AG
|
// Copyright (c) 2023 Proton AG
|
||||||
//
|
|
||||||
// This file is part of Proton Mail Bridge.
|
// This file is part of Proton Mail Bridge.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
// 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
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
// 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/>.
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Controls.impl
|
import QtQuick.Controls.impl
|
||||||
|
|
||||||
import Proton
|
import Proton
|
||||||
import Notifications
|
import Notifications
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: root
|
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
|
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
|
implicitHeight: children[0].implicitHeight + children[0].anchors.topMargin + children[0].anchors.bottomMargin
|
||||||
implicitWidth: children[0].implicitWidth + children[0].anchors.leftMargin + children[0].anchors.rightMargin
|
implicitWidth: children[0].implicitWidth + children[0].anchors.leftMargin + children[0].anchors.rightMargin
|
||||||
|
|
||||||
NotificationFilter {
|
NotificationFilter {
|
||||||
id: notificationFilter
|
id: notificationFilter
|
||||||
|
blacklist: root.notificationBlacklist
|
||||||
source: root.notifications ? root.notifications.all : undefined
|
source: root.notifications ? root.notifications.all : undefined
|
||||||
whitelist: root.notificationWhitelist
|
whitelist: root.notificationWhitelist
|
||||||
blacklist: root.notificationBlacklist
|
|
||||||
|
|
||||||
onTopmostChanged: {
|
onTopmostChanged: {
|
||||||
if (!topmost) {
|
if (!topmost) {
|
||||||
image.source = "/qml/icons/ic-connected.svg"
|
image.source = "/qml/icons/ic-connected.svg";
|
||||||
image.color = root.colorScheme.signal_success
|
image.color = root.colorScheme.signal_success;
|
||||||
label.text = qsTr("Connected")
|
label.text = qsTr("Connected");
|
||||||
label.color = root.colorScheme.signal_success
|
label.color = root.colorScheme.signal_success;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
image.source = topmost.icon;
|
||||||
image.source = topmost.icon
|
label.text = topmost.brief;
|
||||||
label.text = topmost.brief
|
|
||||||
|
|
||||||
switch (topmost.type) {
|
switch (topmost.type) {
|
||||||
case Notification.NotificationType.Danger:
|
case Notification.NotificationType.Danger:
|
||||||
image.color = root.colorScheme.signal_danger
|
image.color = root.colorScheme.signal_danger;
|
||||||
label.color = root.colorScheme.signal_danger
|
label.color = root.colorScheme.signal_danger;
|
||||||
break;
|
break;
|
||||||
case Notification.NotificationType.Warning:
|
case Notification.NotificationType.Warning:
|
||||||
image.color = root.colorScheme.signal_warning
|
image.color = root.colorScheme.signal_warning;
|
||||||
label.color = root.colorScheme.signal_warning
|
label.color = root.colorScheme.signal_warning;
|
||||||
break;
|
break;
|
||||||
case Notification.NotificationType.Success:
|
case Notification.NotificationType.Success:
|
||||||
image.color = root.colorScheme.signal_success
|
image.color = root.colorScheme.signal_success;
|
||||||
label.color = root.colorScheme.signal_success
|
label.color = root.colorScheme.signal_success;
|
||||||
break;
|
break;
|
||||||
case Notification.NotificationType.Info:
|
case Notification.NotificationType.Info:
|
||||||
image.color = root.colorScheme.signal_info
|
image.color = root.colorScheme.signal_info;
|
||||||
label.color = root.colorScheme.signal_info
|
label.color = root.colorScheme.signal_info;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
spacing: 8
|
spacing: 8
|
||||||
|
|
||||||
ColorImage {
|
ColorImage {
|
||||||
id: image
|
id: image
|
||||||
width: 16
|
|
||||||
height: 16
|
|
||||||
sourceSize.width: width
|
|
||||||
sourceSize.height: height
|
|
||||||
source: "/qml/icons/ic-connected.svg"
|
|
||||||
color: root.colorScheme.signal_success
|
color: root.colorScheme.signal_success
|
||||||
|
height: 16
|
||||||
|
source: "/qml/icons/ic-connected.svg"
|
||||||
|
sourceSize.height: height
|
||||||
|
sourceSize.width: width
|
||||||
|
width: 16
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
colorScheme: root.colorScheme
|
|
||||||
id: label
|
id: label
|
||||||
|
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
wrapMode: Text.WordWrap
|
|
||||||
|
|
||||||
horizontalAlignment: Text.AlignLeft
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
|
|
||||||
text: qsTr("Connected")
|
|
||||||
color: root.colorScheme.signal_success
|
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
|
// Copyright (c) 2023 Proton AG
|
||||||
//
|
|
||||||
// This file is part of Proton Mail Bridge.
|
// This file is part of Proton Mail Bridge.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is free software: you can redistribute it and/or modify
|
// 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
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
//
|
|
||||||
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
// Proton Mail Bridge is distributed in the hope that it will be useful,
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
// 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/>.
|
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import QtQml
|
import QtQml
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
|
|
||||||
import Proton
|
import Proton
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
@ -34,24 +28,46 @@ Item {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
Rectangle {
|
states: [
|
||||||
color: root.colorScheme.background_norm
|
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.fillHeight: true
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
color: root.colorScheme.background_norm
|
||||||
implicitHeight: children[0].implicitHeight
|
implicitHeight: children[0].implicitHeight
|
||||||
implicitWidth: children[0].implicitWidth
|
implicitWidth: children[0].implicitWidth
|
||||||
|
visible: signInItem.currentIndex === 0
|
||||||
visible: signInItem.currentIndex == 0
|
|
||||||
|
|
||||||
GridLayout {
|
GridLayout {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
||||||
columnSpacing: 0
|
columnSpacing: 0
|
||||||
rowSpacing: 0
|
|
||||||
|
|
||||||
columns: 3
|
columns: 3
|
||||||
|
rowSpacing: 0
|
||||||
|
|
||||||
// top margin
|
// top margin
|
||||||
Item {
|
Item {
|
||||||
@ -59,141 +75,123 @@ Item {
|
|||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
// Using binding component here instead of direct binding to avoid binding loop during construction of element
|
// 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
|
value: (parent.height - welcomeContentItem.height) / 4
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// left margin
|
// left margin
|
||||||
Item {
|
Item {
|
||||||
Layout.minimumWidth: 48
|
|
||||||
Layout.maximumWidth: 80
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
Layout.maximumWidth: 80
|
||||||
|
Layout.minimumWidth: 48
|
||||||
Layout.preferredHeight: welcomeContentItem.height
|
Layout.preferredHeight: welcomeContentItem.height
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: welcomeContentItem
|
id: welcomeContentItem
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
source: colorScheme.welcome_img
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
Layout.topMargin: 16
|
Layout.topMargin: 16
|
||||||
|
source: colorScheme.welcome_img
|
||||||
sourceSize.height: 148
|
sourceSize.height: 148
|
||||||
sourceSize.width: 264
|
sourceSize.width: 264
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
colorScheme: root.colorScheme
|
|
||||||
text: qsTr("Welcome to\nProton Mail Bridge")
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: 16
|
Layout.topMargin: 16
|
||||||
|
colorScheme: root.colorScheme
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
text: qsTr("Welcome to\nProton Mail Bridge")
|
||||||
type: Label.LabelType.Heading
|
type: Label.LabelType.Heading
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
colorScheme: root.colorScheme
|
|
||||||
id: longTextLabel
|
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.alignment: Qt.AlignHCenter
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: 16
|
|
||||||
Layout.preferredWidth: 320
|
Layout.preferredWidth: 320
|
||||||
|
Layout.topMargin: 16
|
||||||
wrapMode: Text.WordWrap
|
colorScheme: root.colorScheme
|
||||||
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
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
|
type: Label.LabelType.Body
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Right margin
|
// Right margin
|
||||||
Item {
|
Item {
|
||||||
Layout.minimumWidth: 48
|
|
||||||
Layout.maximumWidth: 80
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
Layout.maximumWidth: 80
|
||||||
|
Layout.minimumWidth: 48
|
||||||
Layout.preferredHeight: welcomeContentItem.height
|
Layout.preferredHeight: welcomeContentItem.height
|
||||||
}
|
}
|
||||||
|
|
||||||
// bottom margin
|
// bottom margin
|
||||||
Item {
|
Item {
|
||||||
Layout.columnSpan: 3
|
Layout.columnSpan: 3
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
|
Layout.fillWidth: true
|
||||||
implicitHeight: children[0].implicitHeight + children[0].anchors.bottomMargin + children[0].anchors.topMargin
|
implicitHeight: children[0].implicitHeight + children[0].anchors.bottomMargin + children[0].anchors.topMargin
|
||||||
implicitWidth: children[0].implicitWidth
|
implicitWidth: children[0].implicitWidth
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
id: logoImage
|
id: logoImage
|
||||||
source: colorScheme.logo_img
|
|
||||||
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
anchors.topMargin: 48
|
|
||||||
anchors.bottomMargin: 48
|
anchors.bottomMargin: 48
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
anchors.topMargin: 48
|
||||||
|
source: colorScheme.logo_img
|
||||||
sourceSize.height: 25
|
sourceSize.height: 25
|
||||||
sourceSize.width: 200
|
sourceSize.width: 200
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
color: (signInItem.currentIndex == 0) ? root.colorScheme.background_weak : root.colorScheme.background_norm
|
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
color: (signInItem.currentIndex == 0) ? root.colorScheme.background_weak : root.colorScheme.background_norm
|
||||||
implicitHeight: children[0].implicitHeight
|
implicitHeight: children[0].implicitHeight
|
||||||
implicitWidth: children[0].implicitWidth
|
implicitWidth: children[0].implicitWidth
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.preferredWidth: signInItem.currentIndex == 0 ? 0 : parent.width / 4
|
Layout.preferredWidth: signInItem.currentIndex == 0 ? 0 : parent.width / 4
|
||||||
|
|
||||||
implicitHeight: children[0].implicitHeight + children[0].anchors.topMargin + children[0].anchors.bottomMargin
|
implicitHeight: children[0].implicitHeight + children[0].anchors.topMargin + children[0].anchors.bottomMargin
|
||||||
implicitWidth: children[0].implicitWidth + children[0].anchors.leftMargin + children[0].anchors.rightMargin
|
implicitWidth: children[0].implicitWidth + children[0].anchors.leftMargin + children[0].anchors.rightMargin
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
colorScheme: root.colorScheme
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.bottomMargin: 80
|
||||||
|
anchors.left: parent.left
|
||||||
anchors.leftMargin: 80
|
anchors.leftMargin: 80
|
||||||
anchors.rightMargin: 80
|
anchors.rightMargin: 80
|
||||||
anchors.topMargin: 80
|
anchors.topMargin: 80
|
||||||
anchors.bottomMargin: 80
|
colorScheme: root.colorScheme
|
||||||
|
|
||||||
visible: signInItem.currentIndex != 0
|
|
||||||
|
|
||||||
secondary: true
|
secondary: true
|
||||||
text: qsTr("Back")
|
text: qsTr("Back")
|
||||||
|
visible: signInItem.currentIndex != 0
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
signInItem.abort()
|
signInItem.abort();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GridLayout {
|
GridLayout {
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
columnSpacing: 0
|
columnSpacing: 0
|
||||||
rowSpacing: 0
|
|
||||||
|
|
||||||
columns: 3
|
columns: 3
|
||||||
|
rowSpacing: 0
|
||||||
|
|
||||||
// top margin
|
// top margin
|
||||||
Item {
|
Item {
|
||||||
@ -201,76 +199,47 @@ Item {
|
|||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
// Using binding component here instead of direct binding to avoid binding loop during construction of element
|
// 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
|
value: (parent.height - signInItem.height) / 4
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// left margin
|
// left margin
|
||||||
Item {
|
Item {
|
||||||
Layout.minimumWidth: 48
|
|
||||||
Layout.maximumWidth: 80
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
Layout.maximumWidth: 80
|
||||||
|
Layout.minimumWidth: 48
|
||||||
Layout.preferredHeight: signInItem.height
|
Layout.preferredHeight: signInItem.height
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
SignIn {
|
SignIn {
|
||||||
id: signInItem
|
id: signInItem
|
||||||
colorScheme: root.colorScheme
|
|
||||||
|
|
||||||
Layout.preferredWidth: 320
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredWidth: 320
|
||||||
|
colorScheme: root.colorScheme
|
||||||
focus: true
|
focus: true
|
||||||
username: Backend.users.count === 1 && Backend.users.get(0) && (Backend.users.get(0).state === EUserState.SignedOut) ? Backend.users.get(0).username : ""
|
username: Backend.users.count === 1 && Backend.users.get(0) && (Backend.users.get(0).state === EUserState.SignedOut) ? Backend.users.get(0).username : ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// Right margin
|
// Right margin
|
||||||
Item {
|
Item {
|
||||||
Layout.minimumWidth: 48
|
|
||||||
Layout.maximumWidth: 80
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
Layout.maximumWidth: 80
|
||||||
|
Layout.minimumWidth: 48
|
||||||
Layout.preferredHeight: signInItem.height
|
Layout.preferredHeight: signInItem.height
|
||||||
}
|
}
|
||||||
|
|
||||||
// bottom margin
|
// bottom margin
|
||||||
Item {
|
Item {
|
||||||
Layout.columnSpan: 3
|
Layout.columnSpan: 3
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
Layout.fillHeight: true
|
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