forked from Silverfish/proton-bridge
GODT-22: Facelift
- GODT-1199: Add menu to status window - GODT-22: use ColorImage instead of IconLabel - GODT-22: remove banners from MainWindow - GODT-1199: Fix separator width - GODT-1199: Fix StatusWindow button position - GODT-1198: Open main window on startup if no users - GODT-1199: Fix avatar text color - GODT-1198: refactor main window layout - GODT-22: add missing components to qmldir - GODT-22: refactor components having Layout as root item - GODT-22: add more user controls - GODT-1199: Add status window resize and maximum height - GODT-22: WIP: notification arch - GODT-22: Notifications WIP - GODT-22: Fix notification filter, topmost notification - GODT-1199: Add notifications to status window - GODT-22: Add strict typization to colorScheme variable - GODT-1198: WIP Notifications, dialogs and banners - GODT-22: Add backend notifications (Banners & Dialogs) D
This commit is contained in:
@ -21,49 +21,58 @@ import QtQuick.Controls 2.12
|
||||
|
||||
import Proton 4.0
|
||||
|
||||
RowLayout {
|
||||
Item {
|
||||
id: root
|
||||
property var colorScheme: parent.colorScheme
|
||||
|
||||
property var text: "janedoe@protonmail.com"
|
||||
property var avatarText: "jd"
|
||||
property var captionText: "50.5 MB / 20 GB"
|
||||
property ColorScheme colorScheme
|
||||
property var user
|
||||
|
||||
spacing: 16
|
||||
implicitHeight: children[0].implicitHeight
|
||||
implicitWidth: children[0].implicitWidth
|
||||
|
||||
Rectangle {
|
||||
id: avatar
|
||||
Layout.preferredHeight: account.height
|
||||
Layout.preferredWidth: account.height
|
||||
radius: 4
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
spacing: 12
|
||||
|
||||
color: root.colorScheme.background_avatar
|
||||
Rectangle {
|
||||
id: avatar
|
||||
|
||||
ProtonLabel {
|
||||
anchors.centerIn: avatar
|
||||
color: root.colorScheme.text_norm
|
||||
text: root.avatarText.toUpperCase()
|
||||
state: "body"
|
||||
horizontalAlignment: Qt.AlignHCenter
|
||||
verticalAlignment: Qt.AlignVCenter
|
||||
}
|
||||
}
|
||||
Layout.fillHeight: true
|
||||
Layout.preferredWidth: height
|
||||
|
||||
ColumnLayout {
|
||||
id: account
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
radius: 4
|
||||
|
||||
ProtonLabel {
|
||||
text: root.text
|
||||
color: root.colorScheme.text_norm
|
||||
state: "body"
|
||||
color: root.colorScheme.background_avatar
|
||||
|
||||
Label {
|
||||
colorScheme: root.colorScheme
|
||||
anchors.fill: parent
|
||||
text: root.user.avatarText.toUpperCase()
|
||||
type: Label.LabelType.Body
|
||||
color: root.colorScheme.text_invert
|
||||
horizontalAlignment: Qt.AlignHCenter
|
||||
verticalAlignment: Qt.AlignVCenter
|
||||
}
|
||||
}
|
||||
|
||||
ProtonLabel {
|
||||
text: root.captionText
|
||||
color: root.colorScheme.text_weak
|
||||
state: "caption"
|
||||
ColumnLayout {
|
||||
id: account
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
|
||||
spacing: 0
|
||||
|
||||
Label {
|
||||
colorScheme: root.colorScheme
|
||||
text: user.username
|
||||
type: Label.LabelType.Body
|
||||
}
|
||||
|
||||
Label {
|
||||
colorScheme: root.colorScheme
|
||||
text: user.captionText
|
||||
type: Label.LabelType.Caption
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -21,28 +21,34 @@ import QtQuick.Controls 2.12
|
||||
|
||||
import Proton 4.0
|
||||
|
||||
ColumnLayout {
|
||||
Item {
|
||||
id: root
|
||||
property var colorScheme: parent.colorScheme
|
||||
property ColorScheme colorScheme
|
||||
|
||||
spacing: 0
|
||||
implicitHeight: children[0].implicitHeight
|
||||
implicitWidth: children[0].implicitWidth
|
||||
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.minimumHeight: 277
|
||||
Layout.maximumHeight: 277
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
spacing: 0
|
||||
|
||||
color: root.colorScheme.background_norm
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.minimumHeight: 277
|
||||
Layout.maximumHeight: 277
|
||||
|
||||
ColumnLayout {
|
||||
color: root.colorScheme.background_norm
|
||||
|
||||
ColumnLayout {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
color: root.colorScheme.background_weak
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
color: root.colorScheme.background_weak
|
||||
}
|
||||
}
|
||||
|
||||
226
internal/frontend/qml/Banner.qml
Normal file
226
internal/frontend/qml/Banner.qml
Normal file
@ -0,0 +1,226 @@
|
||||
// Copyright (c) 2021 Proton Technologies AG
|
||||
//
|
||||
// This file is part of ProtonMail Bridge.
|
||||
//
|
||||
// ProtonMail 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.
|
||||
//
|
||||
// ProtonMail 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 ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import QtQuick 2.13
|
||||
import QtQuick.Layouts 1.12
|
||||
import QtQuick.Controls 2.12
|
||||
import QtQuick.Controls.impl 2.12
|
||||
|
||||
import Proton 4.0
|
||||
import Notifications 1.0
|
||||
|
||||
Popup {
|
||||
id: root
|
||||
|
||||
property ColorScheme colorScheme
|
||||
property Notification notification
|
||||
|
||||
implicitHeight: contentLayout.implicitHeight + contentLayout.anchors.topMargin + contentLayout.anchors.bottomMargin
|
||||
implicitWidth: contentLayout.implicitWidth + contentLayout.anchors.leftMargin + contentLayout.anchors.rightMargin
|
||||
|
||||
popupType: ApplicationWindow.PopupType.Banner
|
||||
|
||||
shouldShow: notification ? (notification.active && !notification.dismissed) : false
|
||||
|
||||
Action {
|
||||
id: defaultDismissAction
|
||||
|
||||
text: qsTr("OK")
|
||||
onTriggered: {
|
||||
if (!root.notification) {
|
||||
return
|
||||
}
|
||||
|
||||
root.notification.dismissed = true
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: contentLayout
|
||||
anchors.fill: parent
|
||||
spacing: 0
|
||||
|
||||
Item {
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
|
||||
clip: true
|
||||
implicitHeight: children[1].implicitHeight + children[1].anchors.topMargin + children[1].anchors.bottomMargin
|
||||
implicitWidth: children[1].implicitWidth + children[1].anchors.leftMargin + children[1].anchors.rightMargin
|
||||
|
||||
Rectangle {
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
width: parent.width + 10
|
||||
radius: 10
|
||||
color: {
|
||||
if (!root.notification) {
|
||||
return "transparent"
|
||||
}
|
||||
|
||||
switch (root.notification.type) {
|
||||
case Notification.NotificationType.Info:
|
||||
return root.colorScheme.signal_info
|
||||
case Notification.NotificationType.Success:
|
||||
return root.colorScheme.signal_success
|
||||
case Notification.NotificationType.Warning:
|
||||
return root.colorScheme.signal_warning
|
||||
case Notification.NotificationType.Danger:
|
||||
return root.colorScheme.signal_danger
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 12
|
||||
|
||||
spacing: 10
|
||||
|
||||
ColorImage {
|
||||
color: root.colorScheme.text_invert
|
||||
width: 24
|
||||
height: 24
|
||||
|
||||
sourceSize.width: 24
|
||||
sourceSize.height: 24
|
||||
|
||||
Layout.preferredHeight: 24
|
||||
Layout.preferredWidth: 24
|
||||
|
||||
source: {
|
||||
if (!root.notification) {
|
||||
return ""
|
||||
}
|
||||
|
||||
switch (root.notification.type) {
|
||||
case Notification.NotificationType.Info:
|
||||
return "./icons/ic-info-circle-filled.svg"
|
||||
case Notification.NotificationType.Success:
|
||||
return "./icons/ic-info-circle-filled.svg"
|
||||
case Notification.NotificationType.Warning:
|
||||
return "./icons/ic-exclamation-circle-filled.svg"
|
||||
case Notification.NotificationType.Danger:
|
||||
return "./icons/ic-exclamation-circle-filled.svg"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
colorScheme: root.colorScheme
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
color: root.colorScheme.text_invert
|
||||
text: root.notification ? root.notification.text : ""
|
||||
|
||||
wrapMode: Text.WordWrap
|
||||
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.fillHeight: true
|
||||
width: 1
|
||||
color: {
|
||||
if (!root.notification) {
|
||||
return "transparent"
|
||||
}
|
||||
|
||||
switch (root.notification.type) {
|
||||
case Notification.NotificationType.Info:
|
||||
return root.colorScheme.signal_info_active
|
||||
case Notification.NotificationType.Success:
|
||||
return root.colorScheme.signal_success_active
|
||||
case Notification.NotificationType.Warning:
|
||||
return root.colorScheme.signal_warning_active
|
||||
case Notification.NotificationType.Danger:
|
||||
return root.colorScheme.signal_danger_active
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
colorScheme: root.colorScheme
|
||||
Layout.fillHeight: true
|
||||
|
||||
id: actionButton
|
||||
|
||||
action: (root.notification && root.notification.action.length > 0) ? root.notification.action[0] : defaultDismissAction
|
||||
|
||||
background: Item {
|
||||
clip: true
|
||||
Rectangle {
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
width: parent.width + 10
|
||||
radius: 10
|
||||
color: {
|
||||
if (!root.notification) {
|
||||
return "transparent"
|
||||
}
|
||||
|
||||
var norm
|
||||
var hover
|
||||
var active
|
||||
|
||||
switch (root.notification.type) {
|
||||
case Notification.NotificationType.Info:
|
||||
norm = root.colorScheme.signal_info
|
||||
hover = root.colorScheme.signal_info_hover
|
||||
active = root.colorScheme.signal_info_active
|
||||
break;
|
||||
case Notification.NotificationType.Success:
|
||||
norm = root.colorScheme.signal_success
|
||||
hover = root.colorScheme.signal_success_hover
|
||||
active = root.colorScheme.signal_success_active
|
||||
break;
|
||||
case Notification.NotificationType.Warning:
|
||||
norm = root.colorScheme.signal_warning
|
||||
hover = root.colorScheme.signal_warning_hover
|
||||
active = root.colorScheme.signal_warning_active
|
||||
break;
|
||||
case Notification.NotificationType.Danger:
|
||||
norm = root.colorScheme.signal_danger
|
||||
hover = root.colorScheme.signal_danger_hover
|
||||
active = root.colorScheme.signal_danger_active
|
||||
break;
|
||||
}
|
||||
|
||||
if (actionButton.down) {
|
||||
return active
|
||||
}
|
||||
|
||||
if (actionButton.enabled && (actionButton.highlighted || actionButton.hovered || actionButton.checked)) {
|
||||
return hover
|
||||
}
|
||||
|
||||
if (actionButton.loading) {
|
||||
return hover
|
||||
}
|
||||
|
||||
return norm
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,121 +0,0 @@
|
||||
// Copyright (c) 2021 Proton Technologies AG
|
||||
//
|
||||
// This file is part of ProtonMail Bridge.
|
||||
//
|
||||
// ProtonMail 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.
|
||||
//
|
||||
// ProtonMail 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 ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import QtQml 2.12
|
||||
import QtQuick 2.13
|
||||
import Proton 4.0
|
||||
import QtQuick.Controls 2.13
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
property var window
|
||||
|
||||
property bool onTop: false
|
||||
property bool blocking: root.nDangers != 0
|
||||
property int nDangers: 0
|
||||
|
||||
color: root.getTransparentVersion(window.colorScheme.text_norm,root.blocking ? 0.5 : 0)
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: root
|
||||
acceptedButtons: root.blocking ? Qt.AllButtons : Qt.NoButton
|
||||
enabled: root.blocking
|
||||
}
|
||||
|
||||
ListModel {
|
||||
id: notifications
|
||||
}
|
||||
|
||||
ListView {
|
||||
id: view
|
||||
anchors.top : root.top
|
||||
anchors.bottom : root.bottom
|
||||
anchors.horizontalCenter : root.horizontalCenter
|
||||
anchors.topMargin : root.height/20
|
||||
anchors.bottomMargin : root.height/20
|
||||
|
||||
layoutDirection: ListView.Vertical
|
||||
verticalLayoutDirection: root.onTop ? ListView.TopToBottom : ListView.BottomToTop
|
||||
|
||||
spacing: 5
|
||||
|
||||
model: notifications
|
||||
delegate: Banner {
|
||||
id: bannerDelegate
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
text: model.text
|
||||
actionText: model.buttonText
|
||||
state: model.state
|
||||
|
||||
onAccepted: {
|
||||
switch (model.submitAction) {
|
||||
case "update":
|
||||
console.log("I am updating now")
|
||||
break;
|
||||
default:
|
||||
console.log("NOOP")
|
||||
}
|
||||
if (model.state == "danger") root.nDangers-=1
|
||||
anchors.horizontalCenter = undefined
|
||||
notifications.remove(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function notify(descriptionText, buttonText, type = "info", submitAction = "noop") {
|
||||
if (type === "danger") root.nDangers+=1
|
||||
notifications.append({
|
||||
"text": descriptionText,
|
||||
"buttonText": buttonText,
|
||||
"state": type,
|
||||
"submitAction": submitAction
|
||||
})
|
||||
}
|
||||
|
||||
function notifyOnlyPaidUsers(){
|
||||
root.notify(
|
||||
qsTr("Bridge is exclusive to our paid plans. Upgrade your account to use Bridge."),
|
||||
qsTr("ok"), "danger"
|
||||
)
|
||||
}
|
||||
|
||||
function notifyConnectionLostWhileLogin(){
|
||||
root.notify(
|
||||
qsTr("Can't connect to the server. Check your internet connection and try again."),
|
||||
qsTr("ok"), "danger"
|
||||
)
|
||||
}
|
||||
|
||||
function notifyUpdateManually(){
|
||||
root.notify(
|
||||
qsTr("Bridge could not update automatically."),
|
||||
qsTr("update"), "warning", "update"
|
||||
)
|
||||
}
|
||||
|
||||
function notifyUserAdded(){
|
||||
root.notify(
|
||||
qsTr("Your account has been added to Bridge and you are now signed in."),
|
||||
qsTr("ok"), "success"
|
||||
)
|
||||
}
|
||||
|
||||
function getTransparentVersion(original, transparency){
|
||||
return Qt.rgba(original.r, original.g, original.b, transparency)
|
||||
}
|
||||
}
|
||||
@ -18,25 +18,34 @@
|
||||
import QtQml 2.12
|
||||
import QtQuick 2.13
|
||||
import QtQuick.Window 2.13
|
||||
import Qt.labs.platform 1.0
|
||||
import Qt.labs.platform 1.1
|
||||
|
||||
import Notifications 1.0
|
||||
|
||||
QtObject {
|
||||
id: root
|
||||
|
||||
property var backend
|
||||
property var users
|
||||
|
||||
signal login(string username, string password)
|
||||
signal login2FA(string username, string code)
|
||||
signal login2Password(string username, string password)
|
||||
signal loginAbort(string username)
|
||||
|
||||
property var mainWindow: MainWindow {
|
||||
property Notifications _notifications: Notifications {
|
||||
id: notifications
|
||||
backend: root.backend
|
||||
frontendMain: mainWindow
|
||||
frontendStatus: statusWindow
|
||||
frontendTray: trayIcon
|
||||
}
|
||||
|
||||
property MainWindow _mainWindow: MainWindow {
|
||||
id: mainWindow
|
||||
visible: true
|
||||
visible: false
|
||||
|
||||
backend: root.backend
|
||||
users: root.users
|
||||
notifications: notifications
|
||||
|
||||
onLogin: {
|
||||
root.login(username, password)
|
||||
@ -52,38 +61,142 @@ QtObject {
|
||||
}
|
||||
}
|
||||
|
||||
property var _trayMenu: Window {
|
||||
id: trayMenu
|
||||
title: "window 2"
|
||||
property StatusWindow _statusWindow: StatusWindow {
|
||||
id: statusWindow
|
||||
visible: false
|
||||
flags: Qt.Dialog
|
||||
|
||||
backend: root.backend
|
||||
notifications: notifications
|
||||
|
||||
onShowMainWindow: {
|
||||
mainWindow.visible = true
|
||||
}
|
||||
onShowHelp: {
|
||||
|
||||
}
|
||||
onShowSettings: {
|
||||
|
||||
}
|
||||
onQuit: {
|
||||
backend.quit()
|
||||
}
|
||||
}
|
||||
|
||||
property var _trayIcon: SystemTrayIcon {
|
||||
property SystemTrayIcon _trayIcon: SystemTrayIcon {
|
||||
id: trayIcon
|
||||
visible: true
|
||||
iconSource: "./icons/ic-systray.svg"
|
||||
onActivated: {
|
||||
function calcStatusWindowPosition(statusWidth, statusHeight) {
|
||||
function bound(num, lower_limit, upper_limit) {
|
||||
return Math.max(lower_limit, Math.min(upper_limit, num))
|
||||
}
|
||||
// checks if rect1 fits within rect2
|
||||
function isRectFit(rect1, rect2) {
|
||||
//if (rect2.)
|
||||
if ((rect2.left > rect1.left) ||
|
||||
(rect2.right < rect1.right) ||
|
||||
(rect2.top > rect1.top) ||
|
||||
(rect2.bottom < rect1.bottom)) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// First we get icon center position.
|
||||
// On some platforms (X11 / Wayland) Qt does not provide icon geometry info.
|
||||
// In this case we rely on cursor position
|
||||
var iconCenter = Qt.point(geometry.x + (geometry.width / 2), geometry.y + (geometry.height / 2))
|
||||
|
||||
if (geometry.width == 0 && geometry.height == 0) {
|
||||
iconCenter = backend.getCursorPos()
|
||||
}
|
||||
|
||||
// Now bound this position to virtual screen available rect
|
||||
// TODO: here we should detect which screen mouse is on and use that screen available geometry to bound
|
||||
iconCenter.x = bound(iconCenter.x, 0, Qt.application.screens[0].desktopAvailableWidth)
|
||||
iconCenter.y = bound(iconCenter.y, 0, Qt.application.screens[0].desktopAvailableHeight)
|
||||
|
||||
var x = 0
|
||||
var y = 0
|
||||
|
||||
// Check if window may fit above
|
||||
x = iconCenter.x - statusWidth / 2
|
||||
y = iconCenter.y - statusHeight
|
||||
if (isRectFit(
|
||||
Qt.rect(x, y, statusWidth, statusHeight),
|
||||
// TODO: we should detect which screen mouse is on and use that screen available geometry to bound
|
||||
Qt.rect(0, 0, Qt.application.screens[0].desktopAvailableWidth, Qt.application.screens[0].desktopAvailableHeight)
|
||||
)) {
|
||||
return Qt.point(x, y)
|
||||
}
|
||||
|
||||
// Check if window may fit below
|
||||
x = iconCenter.x - statusWidth / 2
|
||||
y = iconCenter.y
|
||||
if (isRectFit(
|
||||
Qt.rect(x, y, statusWidth, statusHeight),
|
||||
// TODO: we should detect which screen mouse is on and use that screen available geometry to bound
|
||||
Qt.rect(0, 0, Qt.application.screens[0].desktopAvailableWidth, Qt.application.screens[0].desktopAvailableHeight)
|
||||
)) {
|
||||
return Qt.point(x, y)
|
||||
}
|
||||
|
||||
// Check if window may fit to the left
|
||||
x = iconCenter.x - statusWidth
|
||||
y = iconCenter.y - statusHeight / 2
|
||||
if (isRectFit(
|
||||
Qt.rect(x, y, statusWidth, statusHeight),
|
||||
// TODO: we should detect which screen mouse is on and use that screen available geometry to bound
|
||||
Qt.rect(0, 0, Qt.application.screens[0].desktopAvailableWidth, Qt.application.screens[0].desktopAvailableHeight)
|
||||
)) {
|
||||
return Qt.point(x, y)
|
||||
}
|
||||
|
||||
// Check if window may fit to the right
|
||||
x = iconCenter.x
|
||||
y = iconCenter.y - statusHeight / 2
|
||||
if (isRectFit(
|
||||
Qt.rect(x, y, statusWidth, statusHeight),
|
||||
// TODO: we should detect which screen mouse is on and use that screen available geometry to bound
|
||||
Qt.rect(0, 0, Qt.application.screens[0].desktopAvailableWidth, Qt.application.screens[0].desktopAvailableHeight)
|
||||
)) {
|
||||
return Qt.point(x, y)
|
||||
}
|
||||
|
||||
// TODO: add fallback
|
||||
}
|
||||
|
||||
switch (reason) {
|
||||
case SystemTrayIcon.Unknown:
|
||||
case SystemTrayIcon.Unknown:
|
||||
break;
|
||||
case SystemTrayIcon.Context:
|
||||
trayMenu.x = (Screen.desktopAvailableWidth - trayMenu.width) / 2
|
||||
trayMenu.visible = !trayMenu.visible
|
||||
case SystemTrayIcon.Context:
|
||||
case SystemTrayIcon.Trigger:!statusWindow.visible
|
||||
if (!statusWindow.visible) {
|
||||
var point = calcStatusWindowPosition(statusWindow.width, statusWindow.height)
|
||||
statusWindow.x = point.x
|
||||
statusWindow.y = point.y
|
||||
}
|
||||
statusWindow.visible = !statusWindow.visible
|
||||
break
|
||||
case SystemTrayIcon.DoubleClick:
|
||||
case SystemTrayIcon.DoubleClick:
|
||||
case SystemTrayIcon.MiddleClick:
|
||||
mainWindow.visible = !mainWindow.visible
|
||||
break;
|
||||
case SystemTrayIcon.Trigger:
|
||||
trayMenu.x = (Screen.desktopAvailableWidth - trayMenu.width) / 2
|
||||
trayMenu.visible = !trayMenu.visible
|
||||
break;
|
||||
case SystemTrayIcon.MiddleClick:
|
||||
mainWindow.visible = !mainWindow.visible
|
||||
break;
|
||||
default:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
if (root.backend.users.count === 0) {
|
||||
mainWindow.show()
|
||||
}
|
||||
|
||||
if (root.backend.users.count === 1 && root.backend.users.get(0).loggedIn === false) {
|
||||
mainWindow.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -20,7 +20,11 @@ import QtQuick 2.13
|
||||
import QtQuick.Layouts 1.12
|
||||
import QtQuick.Controls 2.13
|
||||
|
||||
import Proton 4.0
|
||||
|
||||
ColumnLayout {
|
||||
id: root
|
||||
|
||||
property var user
|
||||
property var backend
|
||||
|
||||
@ -29,9 +33,10 @@ ColumnLayout {
|
||||
Layout.fillHeight: true
|
||||
//Layout.fillWidth: true
|
||||
|
||||
property var colorScheme
|
||||
property ColorScheme colorScheme
|
||||
|
||||
TextField {
|
||||
colorScheme: root.colorScheme
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: user !== undefined ? user.username : ""
|
||||
@ -41,39 +46,57 @@ ColumnLayout {
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
Button {
|
||||
//Layout.fillWidth: true
|
||||
Switch {
|
||||
id: userLoginSwitch
|
||||
colorScheme: root.colorScheme
|
||||
|
||||
text: "Login"
|
||||
enabled: user !== undefined && !user.loggedIn && user.username.length > 0
|
||||
text: "LoggedIn"
|
||||
enabled: user !== undefined && user.username.length > 0
|
||||
|
||||
onClicked: {
|
||||
if (user === backend.loginUser) {
|
||||
var newUserObject = backend.userComponent.createObject(backend, {username: user.username, loggedIn: true})
|
||||
backend.users.append( { object: newUserObject } )
|
||||
checked: user ? user.loggedIn : false
|
||||
|
||||
user.username = ""
|
||||
user.resetLoginRequests()
|
||||
onCheckedChanged: {
|
||||
if (!user) {
|
||||
return
|
||||
}
|
||||
|
||||
user.loggedIn = true
|
||||
user.resetLoginRequests()
|
||||
if (checked) {
|
||||
if (user === backend.loginUser) {
|
||||
var newUserObject = backend.userComponent.createObject(backend, {username: user.username, loggedIn: true, setupGuideSeen: user.setupGuideSeen})
|
||||
backend.users.append( { object: newUserObject } )
|
||||
|
||||
user.username = ""
|
||||
user.resetLoginRequests()
|
||||
return
|
||||
}
|
||||
|
||||
user.loggedIn = true
|
||||
user.resetLoginRequests()
|
||||
return
|
||||
} else {
|
||||
user.loggedIn = false
|
||||
user.resetLoginRequests()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
//Layout.fillWidth: true
|
||||
Switch {
|
||||
colorScheme: root.colorScheme
|
||||
|
||||
text: "Logout"
|
||||
enabled: user !== undefined && user.loggedIn && user.username.length > 0
|
||||
text: "Setup guide seen"
|
||||
enabled: user !== undefined && user.username.length > 0
|
||||
|
||||
onClicked: {
|
||||
user.loggedIn = false
|
||||
user.resetLoginRequests()
|
||||
checked: user ? user.setupGuideSeen : false
|
||||
|
||||
onCheckedChanged: {
|
||||
if (!user) {
|
||||
return
|
||||
}
|
||||
|
||||
user.setupGuideSeen = checked
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -83,6 +106,7 @@ ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
Label {
|
||||
colorScheme: root.colorScheme
|
||||
id: loginLabel
|
||||
text: "Login:"
|
||||
|
||||
@ -90,6 +114,7 @@ ColumnLayout {
|
||||
}
|
||||
|
||||
Button {
|
||||
colorScheme: root.colorScheme
|
||||
text: "name/pass error"
|
||||
enabled: user !== undefined && user.isLoginRequested && !user.isLogin2FARequested && !user.isLogin2PasswordProvided
|
||||
|
||||
@ -100,6 +125,7 @@ ColumnLayout {
|
||||
}
|
||||
|
||||
Button {
|
||||
colorScheme: root.colorScheme
|
||||
text: "free user error"
|
||||
enabled: user !== undefined && user.isLoginRequested
|
||||
onClicked: {
|
||||
@ -109,6 +135,7 @@ ColumnLayout {
|
||||
}
|
||||
|
||||
Button {
|
||||
colorScheme: root.colorScheme
|
||||
text: "connection error"
|
||||
enabled: user !== undefined && user.isLoginRequested
|
||||
onClicked: {
|
||||
@ -122,6 +149,7 @@ ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
Label {
|
||||
colorScheme: root.colorScheme
|
||||
id: faLabel
|
||||
text: "2FA:"
|
||||
|
||||
@ -129,6 +157,7 @@ ColumnLayout {
|
||||
}
|
||||
|
||||
Button {
|
||||
colorScheme: root.colorScheme
|
||||
text: "request"
|
||||
|
||||
enabled: user !== undefined && user.isLoginRequested && !user.isLogin2FARequested && !user.isLogin2PasswordRequested
|
||||
@ -139,6 +168,7 @@ ColumnLayout {
|
||||
}
|
||||
|
||||
Button {
|
||||
colorScheme: root.colorScheme
|
||||
text: "error"
|
||||
|
||||
enabled: user !== undefined && user.isLogin2FAProvided && !(user.isLogin2PasswordRequested && !user.isLogin2PasswordProvided)
|
||||
@ -149,6 +179,7 @@ ColumnLayout {
|
||||
}
|
||||
|
||||
Button {
|
||||
colorScheme: root.colorScheme
|
||||
text: "Abort"
|
||||
|
||||
enabled: user !== undefined && user.isLogin2FAProvided && !(user.isLogin2PasswordRequested && !user.isLogin2PasswordProvided)
|
||||
@ -163,6 +194,7 @@ ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
Label {
|
||||
colorScheme: root.colorScheme
|
||||
id: passLabel
|
||||
text: "2 Password:"
|
||||
|
||||
@ -170,6 +202,7 @@ ColumnLayout {
|
||||
}
|
||||
|
||||
Button {
|
||||
colorScheme: root.colorScheme
|
||||
text: "request"
|
||||
|
||||
enabled: user !== undefined && user.isLoginRequested && !user.isLogin2PasswordRequested && !(user.isLogin2FARequested && !user.isLogin2FAProvided)
|
||||
@ -180,6 +213,7 @@ ColumnLayout {
|
||||
}
|
||||
|
||||
Button {
|
||||
colorScheme: root.colorScheme
|
||||
text: "error"
|
||||
|
||||
enabled: user !== undefined && user.isLogin2PasswordProvided && !(user.isLogin2FARequested && !user.isLogin2FAProvided)
|
||||
@ -191,6 +225,7 @@ ColumnLayout {
|
||||
}
|
||||
|
||||
Button {
|
||||
colorScheme: root.colorScheme
|
||||
text: "Abort"
|
||||
|
||||
enabled: user !== undefined && user.isLogin2PasswordProvided && !(user.isLogin2FARequested && !user.isLogin2FAProvided)
|
||||
|
||||
@ -24,7 +24,7 @@ import Proton 4.0
|
||||
ColumnLayout {
|
||||
id: root
|
||||
|
||||
property var colorScheme
|
||||
property ColorScheme colorScheme
|
||||
property var backend
|
||||
|
||||
property alias currentIndex: usersListView.currentIndex
|
||||
@ -46,10 +46,10 @@ ColumnLayout {
|
||||
anchors.margins: 10
|
||||
|
||||
Label {
|
||||
colorScheme: root.colorScheme
|
||||
text: modelData.username
|
||||
anchors.margins: 10
|
||||
anchors.fill: parent
|
||||
color: root.colorScheme.text_norm
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
@ -69,14 +69,25 @@ ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
Button {
|
||||
colorScheme: root.colorScheme
|
||||
|
||||
text: "+"
|
||||
|
||||
onClicked: {
|
||||
var newUserObject = backend.userComponent.createObject(backend, { username: "test@protonmail.com", loggedIn: false } )
|
||||
var newUserObject = backend.userComponent.createObject(backend)
|
||||
newUserObject.username = backend.loginUser.username.length > 0 ? backend.loginUser.username : "test@protonmail.com"
|
||||
newUserObject.loggedIn = true
|
||||
newUserObject.setupGuideSeen = true // backend.loginUser.setupGuideSeen
|
||||
|
||||
backend.loginUser.username = ""
|
||||
backend.loginUser.loggedIn = false
|
||||
backend.loginUser.setupGuideSeen = false
|
||||
|
||||
backend.users.append( { object: newUserObject } )
|
||||
}
|
||||
}
|
||||
Button {
|
||||
colorScheme: root.colorScheme
|
||||
text: "-"
|
||||
|
||||
enabled: usersListView.currentIndex != 0
|
||||
|
||||
@ -26,22 +26,32 @@ import QtQml.Models 2.12
|
||||
import Proton 4.0
|
||||
|
||||
import "./BridgeTest"
|
||||
import BridgePreview 1.0
|
||||
|
||||
import Notifications 1.0
|
||||
|
||||
Window {
|
||||
id: root
|
||||
|
||||
width: 640
|
||||
height: 480
|
||||
x: 100
|
||||
y: 100
|
||||
|
||||
property var colorScheme: ProtonStyle.darkStyle
|
||||
property ColorScheme colorScheme: ProtonStyle.darkStyle
|
||||
|
||||
flags : Qt.Window | Qt.Dialog
|
||||
visible : true
|
||||
title : "Bridge Test GUI"
|
||||
color : colorScheme.background_norm
|
||||
|
||||
function getCursorPos() {
|
||||
return BridgePreview.getCursorPos()
|
||||
}
|
||||
function quit() {
|
||||
if (bridge !== undefined && bridge !== null) {
|
||||
bridge.destroy()
|
||||
}
|
||||
}
|
||||
|
||||
function _log(msg, color) {
|
||||
logTextArea.text += "<p style='color: " + color + ";'>" + msg + "</p>"
|
||||
logTextArea.text += "\n"
|
||||
@ -94,6 +104,11 @@ Window {
|
||||
property string username: ""
|
||||
property bool loggedIn: false
|
||||
|
||||
property bool setupGuideSeen: true
|
||||
|
||||
property string captionText: "50.3 MB / 20 GB"
|
||||
property string avatarText: "jd"
|
||||
|
||||
signal loginUsernamePasswordError()
|
||||
signal loginFreeUserError()
|
||||
signal loginConnectionError()
|
||||
@ -166,6 +181,7 @@ Window {
|
||||
Component.onCompleted: {
|
||||
var newLoginUser = _userComponent.createObject()
|
||||
root.loginUser = newLoginUser
|
||||
root.loginUser.setupGuideSeen = false
|
||||
_usersTest.append({object: newLoginUser})
|
||||
|
||||
newLoginUser.loginUsernamePasswordError.connect(root.loginUsernamePasswordError)
|
||||
@ -194,7 +210,7 @@ Window {
|
||||
}
|
||||
|
||||
TabButton {
|
||||
text: "Playground"
|
||||
text: "Notifications"
|
||||
}
|
||||
|
||||
TabButton {
|
||||
@ -215,16 +231,13 @@ Window {
|
||||
RowLayout {
|
||||
id: globalTab
|
||||
spacing : 5
|
||||
property alias colorScheme: root.colorScheme
|
||||
|
||||
ColumnLayout {
|
||||
spacing : 5
|
||||
|
||||
property alias colorScheme: globalTab.colorScheme
|
||||
|
||||
ProtonLabel {
|
||||
Label {
|
||||
colorScheme: root.colorScheme
|
||||
text: "Global settings"
|
||||
color: globalTab.colorScheme.text_norm
|
||||
}
|
||||
|
||||
ButtonGroup {
|
||||
@ -232,6 +245,7 @@ Window {
|
||||
}
|
||||
|
||||
RadioButton {
|
||||
colorScheme: root.colorScheme
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: "Light UI"
|
||||
@ -246,6 +260,7 @@ Window {
|
||||
}
|
||||
|
||||
RadioButton {
|
||||
colorScheme: root.colorScheme
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: "Dark UI"
|
||||
@ -262,6 +277,7 @@ Window {
|
||||
}
|
||||
|
||||
Button {
|
||||
colorScheme: root.colorScheme
|
||||
//Layout.fillWidth: true
|
||||
|
||||
text: "Open Bridge"
|
||||
@ -272,6 +288,7 @@ Window {
|
||||
}
|
||||
|
||||
Button {
|
||||
colorScheme: root.colorScheme
|
||||
//Layout.fillWidth: true
|
||||
|
||||
text: "Close Bridge"
|
||||
@ -289,14 +306,13 @@ Window {
|
||||
ColumnLayout {
|
||||
spacing : 5
|
||||
|
||||
property alias colorScheme: globalTab.colorScheme
|
||||
|
||||
ProtonLabel {
|
||||
Label {
|
||||
colorScheme: root.colorScheme
|
||||
text: "Notifications"
|
||||
color: globalTab.colorScheme.text_norm
|
||||
}
|
||||
|
||||
Button {
|
||||
colorScheme: root.colorScheme
|
||||
text: "Notify: danger"
|
||||
enabled: bridge !== undefined && bridge !== null
|
||||
onClicked: {
|
||||
@ -305,6 +321,7 @@ Window {
|
||||
}
|
||||
|
||||
Button {
|
||||
colorScheme: root.colorScheme
|
||||
text: "Notify: warning"
|
||||
enabled: bridge !== undefined && bridge !== null
|
||||
onClicked: {
|
||||
@ -313,6 +330,7 @@ Window {
|
||||
}
|
||||
|
||||
Button {
|
||||
colorScheme: root.colorScheme
|
||||
text: "Notify: success"
|
||||
enabled: bridge !== undefined && bridge !== null
|
||||
onClicked: {
|
||||
@ -320,8 +338,6 @@ Window {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Item {
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
@ -345,14 +361,131 @@ Window {
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: playgroundTab
|
||||
id: notificationsTab
|
||||
spacing: 5
|
||||
|
||||
property var colorScheme: root.colorScheme
|
||||
ColumnLayout {
|
||||
spacing: 5
|
||||
|
||||
AccountDelegate{}
|
||||
Switch {
|
||||
colorScheme: root.colorScheme
|
||||
|
||||
text: "Internet connection"
|
||||
checked: true
|
||||
onCheckedChanged: {
|
||||
checked ? root.internetOn() : root.internetOff()
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
colorScheme: root.colorScheme
|
||||
|
||||
text: "Update manual ready"
|
||||
onClicked: {
|
||||
root.updateManualReady("3.14.1592")
|
||||
}
|
||||
}
|
||||
Button {
|
||||
colorScheme: root.colorScheme
|
||||
|
||||
text: "Update manual done"
|
||||
onClicked: {
|
||||
root.updateManualRestartNeeded()
|
||||
}
|
||||
}
|
||||
Button {
|
||||
colorScheme: root.colorScheme
|
||||
|
||||
text: "Update manual error"
|
||||
onClicked: {
|
||||
root.updateManualError()
|
||||
}
|
||||
}
|
||||
Button {
|
||||
colorScheme: root.colorScheme
|
||||
|
||||
text: "Update force"
|
||||
onClicked: {
|
||||
root.updateForce("3.14.1592")
|
||||
}
|
||||
}
|
||||
Button {
|
||||
colorScheme: root.colorScheme
|
||||
|
||||
text: "Update force error"
|
||||
onClicked: {
|
||||
root.updateForceError()
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
colorScheme: root.colorScheme
|
||||
|
||||
text: "Update silent done"
|
||||
onClicked: {
|
||||
root.updateSilentRestartNeeded()
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
colorScheme: root.colorScheme
|
||||
|
||||
text: "Update solent error"
|
||||
onClicked: {
|
||||
root.updateSilentError()
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
colorScheme: root.colorScheme
|
||||
|
||||
text: "Bug report send OK"
|
||||
onClicked: {
|
||||
root.bugReportSendSuccess()
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
colorScheme: root.colorScheme
|
||||
|
||||
text: "Bug report send error"
|
||||
onClicked: {
|
||||
root.bugReportSendError()
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
colorScheme: root.colorScheme
|
||||
|
||||
text: "Cache anavailable"
|
||||
onClicked: {
|
||||
root.cacheAnavailable()
|
||||
}
|
||||
}
|
||||
Button {
|
||||
colorScheme: root.colorScheme
|
||||
|
||||
text: "Cache can't move"
|
||||
onClicked: {
|
||||
root.cacheCantMove()
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
colorScheme: root.colorScheme
|
||||
|
||||
text: "Disk full"
|
||||
onClicked: {
|
||||
root.diskFull()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
TextArea {
|
||||
colorScheme: root.colorScheme
|
||||
id: logTextArea
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
@ -365,7 +498,7 @@ Window {
|
||||
}
|
||||
}
|
||||
|
||||
property var bridge
|
||||
property Bridge bridge
|
||||
|
||||
// this signals are used only when trying to login with new user (i.e. not in users model)
|
||||
signal loginUsernamePasswordError()
|
||||
@ -378,6 +511,25 @@ Window {
|
||||
signal login2PasswordError()
|
||||
signal login2PasswordErrorAbort()
|
||||
|
||||
signal internetOff()
|
||||
signal internetOn()
|
||||
|
||||
signal updateManualReady(var version)
|
||||
signal updateManualRestartNeeded()
|
||||
signal updateManualError()
|
||||
signal updateForce(var version)
|
||||
signal updateForceError()
|
||||
signal updateSilentRestartNeeded()
|
||||
signal updateSilentError()
|
||||
|
||||
signal bugReportSendSuccess()
|
||||
signal bugReportSendError()
|
||||
|
||||
signal cacheAnavailable()
|
||||
signal cacheCantMove()
|
||||
|
||||
signal diskFull()
|
||||
|
||||
onLoginUsernamePasswordError: {
|
||||
console.debug("<- loginUsernamePasswordError")
|
||||
}
|
||||
@ -406,6 +558,13 @@ Window {
|
||||
console.debug("<- login2PasswordErrorAbort")
|
||||
}
|
||||
|
||||
onInternetOff: {
|
||||
console.debug("<- internetOff")
|
||||
}
|
||||
onInternetOn: {
|
||||
console.debug("<- internetOn")
|
||||
}
|
||||
|
||||
Component {
|
||||
id: bridgeComponent
|
||||
|
||||
|
||||
@ -23,9 +23,14 @@ import Proton 4.0
|
||||
|
||||
Item {
|
||||
id: root
|
||||
property var colorScheme: parent.colorScheme
|
||||
property ColorScheme colorScheme
|
||||
|
||||
property var window
|
||||
property var backend
|
||||
|
||||
signal login(string username, string password)
|
||||
signal login2FA(string username, string code)
|
||||
signal login2Password(string username, string password)
|
||||
signal loginAbort(string username)
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
@ -33,7 +38,7 @@ Item {
|
||||
|
||||
Rectangle {
|
||||
id: leftBar
|
||||
property var colorScheme: ProtonStyle.prominentStyle
|
||||
property ColorScheme colorScheme: root.colorScheme.prominent
|
||||
|
||||
Layout.minimumWidth: 264
|
||||
Layout.maximumWidth: 320
|
||||
@ -55,9 +60,8 @@ Item {
|
||||
Layout.preferredHeight: 60
|
||||
spacing: 0
|
||||
|
||||
property var colorScheme: leftBar.colorScheme
|
||||
|
||||
Status {
|
||||
colorScheme: leftBar.colorScheme
|
||||
Layout.leftMargin: 16
|
||||
Layout.topMargin: 24
|
||||
Layout.bottomMargin: 17
|
||||
@ -72,6 +76,7 @@ Item {
|
||||
}
|
||||
|
||||
Button {
|
||||
colorScheme: leftBar.colorScheme
|
||||
Layout.minimumHeight: 36
|
||||
Layout.maximumHeight: 36
|
||||
Layout.preferredHeight: 36
|
||||
@ -89,6 +94,7 @@ Item {
|
||||
}
|
||||
|
||||
Button {
|
||||
colorScheme: leftBar.colorScheme
|
||||
Layout.minimumHeight: 36
|
||||
Layout.maximumHeight: 36
|
||||
Layout.preferredHeight: 36
|
||||
@ -127,20 +133,20 @@ Item {
|
||||
|
||||
header: Rectangle {
|
||||
height: headerLabel.height+16
|
||||
color: ProtonStyle.transparent
|
||||
ProtonLabel{
|
||||
id:headerLabel
|
||||
// color: ProtonStyle.transparent
|
||||
Label{
|
||||
colorScheme: leftBar.colorScheme
|
||||
id: headerLabel
|
||||
text: qsTr("Accounts")
|
||||
color: leftBar.colorScheme.text_norm
|
||||
state: "body"
|
||||
type: Label.LabelType.Body
|
||||
}
|
||||
}
|
||||
|
||||
model: window.backend.users
|
||||
model: root.backend.users
|
||||
delegate: AccountDelegate{
|
||||
id: accountDelegate
|
||||
colorScheme: leftBar.colorScheme
|
||||
text: modelData.username
|
||||
user: modelData
|
||||
}
|
||||
}
|
||||
|
||||
@ -160,9 +166,8 @@ Item {
|
||||
Layout.maximumHeight: 52
|
||||
Layout.preferredHeight: 52
|
||||
|
||||
property var colorScheme: leftBar.colorScheme
|
||||
|
||||
Button {
|
||||
colorScheme: leftBar.colorScheme
|
||||
width: 36
|
||||
height: 36
|
||||
|
||||
@ -209,14 +214,13 @@ Item {
|
||||
Layout.fillHeight: true
|
||||
|
||||
colorScheme: root.colorScheme
|
||||
user: (root.window.backend.users.count === 1 && root.window.backend.users.get(0).loggedIn === false) ? root.window.backend.users.get(0) : undefined
|
||||
backend: root.window.backend
|
||||
window: root.window
|
||||
user: (root.backend.users.count === 1 && root.backend.users.get(0).loggedIn === false) ? root.backend.users.get(0) : undefined
|
||||
backend: root.backend
|
||||
|
||||
onLogin : { root.window.login ( username , password ) }
|
||||
onLogin2FA : { root.window.login2FA ( username , code ) }
|
||||
onLogin2Password : { root.window.login2Password ( username , password ) }
|
||||
onLoginAbort : { root.window.loginAbort ( username ) }
|
||||
onLogin : { root.login ( username , password ) }
|
||||
onLogin2FA : { root.login2FA ( username , code ) }
|
||||
onLogin2Password : { root.login2Password ( username , password ) }
|
||||
onLoginAbort : { root.loginAbort ( username ) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -18,16 +18,20 @@
|
||||
import QtQuick 2.13
|
||||
import QtQuick.Controls 2.12
|
||||
|
||||
import Proton 4.0
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: "transparent"
|
||||
border.color: "red"
|
||||
border.width: 1
|
||||
z: parent.z - 1
|
||||
//z: parent.z - 1
|
||||
z: 10000000
|
||||
|
||||
Label {
|
||||
text: parent.width + "x" + parent.height
|
||||
anchors.centerIn: parent
|
||||
color: "black"
|
||||
colorScheme: ProtonStyle.currentStyle
|
||||
}
|
||||
}
|
||||
|
||||
@ -22,10 +22,11 @@ import QtQuick.Layouts 1.12
|
||||
import QtQuick.Controls 2.12
|
||||
|
||||
import Proton 4.0
|
||||
import Notifications 1.0
|
||||
|
||||
import "tests"
|
||||
|
||||
Window {
|
||||
ApplicationWindow {
|
||||
id: root
|
||||
title: "ProtonMail Bridge"
|
||||
|
||||
@ -35,33 +36,104 @@ Window {
|
||||
minimumHeight: contentLayout.implicitHeight
|
||||
minimumWidth: contentLayout.implicitWidth
|
||||
|
||||
property var colorScheme: ProtonStyle.currentStyle
|
||||
colorScheme: ProtonStyle.currentStyle
|
||||
|
||||
property var backend
|
||||
property var users
|
||||
|
||||
|
||||
property bool isNoUser: backend.users.count === 0
|
||||
property bool isNoLoggedUser: backend.users.count === 1 && backend.users.get(0).loggedIn === false
|
||||
property bool showSetup: true
|
||||
property var notifications
|
||||
|
||||
signal login(string username, string password)
|
||||
signal login2FA(string username, string code)
|
||||
signal login2Password(string username, string password)
|
||||
signal loginAbort(string username)
|
||||
|
||||
// show Setup Guide on every new user
|
||||
Connections {
|
||||
target: root.backend.users
|
||||
|
||||
onRowsInserted: {
|
||||
// considerring that users are added one-by-one
|
||||
var user = root.backend.users.get(first)
|
||||
|
||||
if (!user.loggedIn) {
|
||||
return
|
||||
}
|
||||
|
||||
if (user.setupGuideSeen) {
|
||||
return
|
||||
}
|
||||
|
||||
root.showSetup(user)
|
||||
}
|
||||
|
||||
onRowsAboutToBeRemoved: {
|
||||
for (var i = first; i <= last; i++ ) {
|
||||
var user = root.backend.users.get(i)
|
||||
|
||||
if (setupGuide.user === user) {
|
||||
setupGuide.user = null
|
||||
contentLayout._showSetup = false
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function showSetup(user) {
|
||||
setupGuide.user = user
|
||||
if (setupGuide.user) {
|
||||
contentLayout._showSetup = true
|
||||
} else {
|
||||
contentLayout._showSetup = false
|
||||
}
|
||||
}
|
||||
|
||||
StackLayout {
|
||||
id: contentLayout
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
currentIndex: (root.isNoUser || root.isNoLoggedUser) ? 0 : ( root.showSetup ? 1 : 2)
|
||||
property bool _showSetup: false
|
||||
currentIndex: {
|
||||
// show welcome when there are no users or only one non-logged-in user is present
|
||||
if (backend.users.count === 0) {
|
||||
return 1
|
||||
}
|
||||
|
||||
WelcomeWindow {
|
||||
if (backend.users.count === 1 && backend.users.get(0).loggedIn === false) {
|
||||
return 1
|
||||
}
|
||||
|
||||
if (contentLayout._showSetup) {
|
||||
return 2
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
ContentWrapper {
|
||||
colorScheme: root.colorScheme
|
||||
backend: root.backend
|
||||
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
|
||||
onLogin: {
|
||||
root.login(username, password)
|
||||
}
|
||||
onLogin2FA: {
|
||||
root.login2FA(username, code)
|
||||
}
|
||||
onLogin2Password: {
|
||||
root.login2Password(username, password)
|
||||
}
|
||||
onLoginAbort: {
|
||||
root.loginAbort(username)
|
||||
}
|
||||
}
|
||||
|
||||
WelcomeGuide {
|
||||
colorScheme: root.colorScheme
|
||||
backend: root.backend
|
||||
window: root
|
||||
enabled: !banners.blocking
|
||||
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
@ -81,38 +153,21 @@ Window {
|
||||
}
|
||||
|
||||
SetupGuide {
|
||||
id: setupGuide
|
||||
colorScheme: root.colorScheme
|
||||
window: root
|
||||
enabled: !banners.blocking
|
||||
backend: root.backend
|
||||
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
ContentWrapper {
|
||||
colorScheme: root.colorScheme
|
||||
window: root
|
||||
enabled: !banners.blocking
|
||||
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
onDismissed: {
|
||||
root.showSetup(null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Banners {
|
||||
id: banners
|
||||
anchors.fill: parent
|
||||
window: root
|
||||
onTop: contentLayout.currentIndex == 0
|
||||
}
|
||||
|
||||
function notifyOnlyPaidUsers() { banners.notifyOnlyPaidUsers() }
|
||||
function notifyConnectionLostWhileLogin() { banners.notifyConnectionLostWhileLogin() }
|
||||
function notifyUpdateManually() { banners.notifyUpdateManually() }
|
||||
function notifyUserAdded() { banners.notifyUserAdded() }
|
||||
|
||||
function showSetupGuide(user) {
|
||||
setupGuide.user = user
|
||||
root.showSetup = true
|
||||
NotificationPopups {
|
||||
colorScheme: root.colorScheme
|
||||
notifications: root.notifications
|
||||
}
|
||||
}
|
||||
|
||||
117
internal/frontend/qml/NotificationDialog.qml
Normal file
117
internal/frontend/qml/NotificationDialog.qml
Normal file
@ -0,0 +1,117 @@
|
||||
// Copyright (c) 2021 Proton Technologies AG
|
||||
//
|
||||
// This file is part of ProtonMail Bridge.
|
||||
//
|
||||
// ProtonMail 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.
|
||||
//
|
||||
// ProtonMail 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 ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import QtQml 2.12
|
||||
import QtQuick 2.12
|
||||
import QtQuick.Layouts 1.12
|
||||
import QtQuick.Controls 2.12
|
||||
|
||||
import Proton 4.0
|
||||
import Notifications 1.0
|
||||
|
||||
Dialog {
|
||||
id: root
|
||||
|
||||
property var notification
|
||||
|
||||
shouldShow: notification && notification.active && !notification.dismissed
|
||||
modal: true
|
||||
|
||||
default property alias data: additionalChildrenContainer.children
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 0
|
||||
|
||||
Image {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
|
||||
sourceSize.width: 64
|
||||
sourceSize.height: 64
|
||||
|
||||
Layout.preferredHeight: 64
|
||||
Layout.preferredWidth: 64
|
||||
|
||||
Layout.bottomMargin: 16
|
||||
|
||||
visible: source != ""
|
||||
|
||||
source: {
|
||||
if (!root.notification) {
|
||||
return ""
|
||||
}
|
||||
|
||||
switch (root.notification.type) {
|
||||
case Notification.NotificationType.Info:
|
||||
// TODO: Add info icon?
|
||||
return ""
|
||||
case Notification.NotificationType.Success:
|
||||
return "./icons/ic-success.svg"
|
||||
case Notification.NotificationType.Warning:
|
||||
case Notification.NotificationType.Danger:
|
||||
return "./icons/ic-alert.svg"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.bottomMargin: 8
|
||||
colorScheme: root.colorScheme
|
||||
text: root.notification.text
|
||||
type: Label.LabelType.Title
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredWidth: 240
|
||||
Layout.bottomMargin: 16
|
||||
|
||||
colorScheme: root.colorScheme
|
||||
text: root.notification.description
|
||||
wrapMode: Text.WordWrap
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
type: Label.LabelType.Body
|
||||
}
|
||||
|
||||
Item {
|
||||
id: additionalChildrenContainer
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.bottomMargin: 16
|
||||
|
||||
visible: children.length > 0
|
||||
|
||||
implicitHeight: additionalChildrenContainer.childrenRect.height
|
||||
implicitWidth: additionalChildrenContainer.childrenRect.width
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 8
|
||||
Repeater {
|
||||
model: root.notification.action
|
||||
delegate: Button {
|
||||
Layout.fillWidth: true
|
||||
|
||||
colorScheme: root.colorScheme
|
||||
action: modelData
|
||||
|
||||
secondary: index > 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
91
internal/frontend/qml/NotificationPopups.qml
Normal file
91
internal/frontend/qml/NotificationPopups.qml
Normal file
@ -0,0 +1,91 @@
|
||||
// Copyright (c) 2021 Proton Technologies AG
|
||||
//
|
||||
// This file is part of ProtonMail Bridge.
|
||||
//
|
||||
// ProtonMail 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.
|
||||
//
|
||||
// ProtonMail 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 ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import QtQml 2.12
|
||||
import QtQuick 2.12
|
||||
import QtQuick.Layouts 1.12
|
||||
import QtQuick.Controls 2.12
|
||||
|
||||
import Proton 4.0
|
||||
import Notifications 1.0
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property ColorScheme colorScheme
|
||||
property var notifications
|
||||
|
||||
property int notificationWhitelist: NotificationFilter.FilterConsts.All
|
||||
property int notificationBlacklist: NotificationFilter.FilterConsts.None
|
||||
|
||||
NotificationFilter {
|
||||
id: bannerNotificationFilter
|
||||
|
||||
source: root.notifications.all
|
||||
blacklist: Notifications.Group.Dialogs
|
||||
}
|
||||
|
||||
Banner {
|
||||
colorScheme: root.colorScheme
|
||||
notification: bannerNotificationFilter.topmost
|
||||
}
|
||||
|
||||
NotificationDialog {
|
||||
colorScheme: root.colorScheme
|
||||
notification: root.notifications.updateManualReady
|
||||
|
||||
Switch {
|
||||
colorScheme: root.colorScheme
|
||||
text: qsTr("Update automatically in the future")
|
||||
}
|
||||
}
|
||||
|
||||
NotificationDialog {
|
||||
colorScheme: root.colorScheme
|
||||
notification: root.notifications.updateForce
|
||||
}
|
||||
|
||||
NotificationDialog {
|
||||
colorScheme: root.colorScheme
|
||||
notification: root.notifications.updateForceError
|
||||
}
|
||||
|
||||
NotificationDialog {
|
||||
colorScheme: root.colorScheme
|
||||
notification: root.notifications.bugReportSendSuccess
|
||||
}
|
||||
|
||||
NotificationDialog {
|
||||
colorScheme: root.colorScheme
|
||||
notification: root.notifications.bugReportSendError
|
||||
}
|
||||
|
||||
NotificationDialog {
|
||||
colorScheme: root.colorScheme
|
||||
notification: root.notifications.cacheAnavailable
|
||||
}
|
||||
|
||||
NotificationDialog {
|
||||
colorScheme: root.colorScheme
|
||||
notification: root.notifications.cacheCantMove
|
||||
}
|
||||
|
||||
NotificationDialog {
|
||||
colorScheme: root.colorScheme
|
||||
notification: root.notifications.diskFull
|
||||
}
|
||||
}
|
||||
49
internal/frontend/qml/Notifications/Notification.qml
Normal file
49
internal/frontend/qml/Notifications/Notification.qml
Normal file
@ -0,0 +1,49 @@
|
||||
// Copyright (c) 2021 Proton Technologies AG
|
||||
//
|
||||
// This file is part of ProtonMail Bridge.
|
||||
//
|
||||
// ProtonMail 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.
|
||||
//
|
||||
// ProtonMail 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 ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import QtQml 2.12
|
||||
import QtQuick.Controls 2.12
|
||||
|
||||
QtObject {
|
||||
id: root
|
||||
|
||||
default property var children
|
||||
|
||||
enum NotificationType {
|
||||
Info = 0,
|
||||
Success = 1,
|
||||
Warning = 2,
|
||||
Danger = 3
|
||||
}
|
||||
|
||||
property string text
|
||||
property string description
|
||||
property string icon
|
||||
property list<Action> action
|
||||
property int type
|
||||
property int group
|
||||
|
||||
property bool dismissed: false
|
||||
property bool active: false
|
||||
readonly property var occurred: active ? new Date() : undefined
|
||||
|
||||
property var data
|
||||
|
||||
onActiveChanged: {
|
||||
dismissed = false
|
||||
}
|
||||
}
|
||||
114
internal/frontend/qml/Notifications/NotificationFilter.qml
Normal file
114
internal/frontend/qml/Notifications/NotificationFilter.qml
Normal file
@ -0,0 +1,114 @@
|
||||
// Copyright (c) 2021 Proton Technologies AG
|
||||
//
|
||||
// This file is part of ProtonMail Bridge.
|
||||
//
|
||||
// ProtonMail 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.
|
||||
//
|
||||
// ProtonMail 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 ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import QtQml 2.12
|
||||
import QtQml.Models 2.12
|
||||
|
||||
// contains notifications that satisfy black- and whitelist and are sorted in time-occurred order
|
||||
ListModel {
|
||||
id: root
|
||||
|
||||
enum FilterConsts {
|
||||
None = 0,
|
||||
All = 255
|
||||
}
|
||||
|
||||
property int whitelist: NotificationFilter.FilterConsts.All
|
||||
property int blacklist: NotificationFilter.FilterConsts.None
|
||||
|
||||
property Notification topmost
|
||||
property var source
|
||||
|
||||
property bool componentCompleted: false
|
||||
Component.onCompleted: {
|
||||
root.componentCompleted = true
|
||||
root.rebuildList()
|
||||
}
|
||||
|
||||
// overriding get method to ignore any role and return directly object itself
|
||||
function get(row) {
|
||||
if (row < 0 || row >= count) {
|
||||
return undefined
|
||||
}
|
||||
return data(index(row, 0), Qt.DisplayRole)
|
||||
}
|
||||
|
||||
function rebuildList() {
|
||||
// avoid evaluation of the list before Component.onCompleted
|
||||
if (!root.componentCompleted) {
|
||||
return
|
||||
}
|
||||
|
||||
for (var i = 0; i < root.count; i++) {
|
||||
root.get(i).onActiveChanged.disconnect( root.updateList )
|
||||
}
|
||||
|
||||
root.clear()
|
||||
|
||||
if (!root.source) {
|
||||
return
|
||||
}
|
||||
|
||||
for (i = 0; i < root.source.length; i++) {
|
||||
var obj = root.source[i]
|
||||
if (obj.group & root.blacklist) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (!(obj.group & root.whitelist)) {
|
||||
continue
|
||||
}
|
||||
|
||||
root.append({obj})
|
||||
obj.onActiveChanged.connect( root.updateList )
|
||||
}
|
||||
}
|
||||
|
||||
function updateList() {
|
||||
var topmost = null
|
||||
|
||||
for (var i = 0; i < root.count; i++) {
|
||||
var obj = root.get(i)
|
||||
|
||||
if (!obj.active) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (topmost && (topmost.type > obj.type)) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (topmost && (topmost.type === obj.type) && (topmost.occurred > obj.occurred)) {
|
||||
continue
|
||||
}
|
||||
|
||||
topmost = obj
|
||||
}
|
||||
|
||||
root.topmost = topmost
|
||||
}
|
||||
|
||||
onWhitelistChanged: {
|
||||
root.rebuildList()
|
||||
}
|
||||
onBlacklistChanged: {
|
||||
root.rebuildList()
|
||||
}
|
||||
onSourceChanged: {
|
||||
root.rebuildList()
|
||||
}
|
||||
}
|
||||
428
internal/frontend/qml/Notifications/Notifications.qml
Normal file
428
internal/frontend/qml/Notifications/Notifications.qml
Normal file
@ -0,0 +1,428 @@
|
||||
// Copyright (c) 2021 Proton Technologies AG
|
||||
//
|
||||
// This file is part of ProtonMail Bridge.
|
||||
//
|
||||
// ProtonMail 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.
|
||||
//
|
||||
// ProtonMail 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 ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import QtQml 2.12
|
||||
import Qt.labs.platform 1.1
|
||||
import QtQuick.Controls 2.12
|
||||
import ".."
|
||||
|
||||
QtObject {
|
||||
id: root
|
||||
|
||||
property var backend
|
||||
|
||||
property MainWindow frontendMain
|
||||
property StatusWindow frontendStatus
|
||||
property SystemTrayIcon frontendTray
|
||||
|
||||
enum Group {
|
||||
Connection = 1,
|
||||
Update = 2,
|
||||
Configuration = 4,
|
||||
API = 32,
|
||||
|
||||
// Special group for notifications that require dialog popup instead of banner
|
||||
Dialogs = 64
|
||||
}
|
||||
|
||||
property var all: [
|
||||
root.noInternet,
|
||||
root.updateManualReady,
|
||||
root.updateManualRestartNeeded,
|
||||
root.updateManualError,
|
||||
root.updateForce,
|
||||
root.updateForceError,
|
||||
root.updateSilentRestartNeeded,
|
||||
root.updateSilentError,
|
||||
root.bugReportSendSuccess,
|
||||
root.bugReportSendError,
|
||||
root.cacheAnavailable,
|
||||
root.cacheCantMove,
|
||||
root.accountChanged,
|
||||
root.diskFull
|
||||
]
|
||||
|
||||
// Connection
|
||||
property Notification noInternet: Notification {
|
||||
text: qsTr("No connection")
|
||||
icon: "./icons/ic-no-connection.svg"
|
||||
type: Notification.NotificationType.Danger
|
||||
group: Notifications.Group.Connection
|
||||
|
||||
Connections {
|
||||
target: root.backend
|
||||
|
||||
onInternetOff: {
|
||||
root.noInternet.active = true
|
||||
}
|
||||
onInternetOn: {
|
||||
root.noInternet.active = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Updates
|
||||
property Notification updateManualReady: Notification {
|
||||
text: qsTr("Update to Bridge") + " " + (data ? data.version : "")
|
||||
description: qsTr("A new version of ProtonMail Bridge is available. See what's changed.")
|
||||
icon: "./icons/ic-info-circle-filled.svg"
|
||||
type: Notification.NotificationType.Info
|
||||
group: Notifications.Group.Update | Notifications.Group.Dialogs
|
||||
|
||||
Connections {
|
||||
target: root.backend
|
||||
onUpdateManualReady: {
|
||||
root.updateManualReady.data = { version: version }
|
||||
root.updateManualReady.active = true
|
||||
}
|
||||
}
|
||||
|
||||
action: [
|
||||
Action {
|
||||
text: qsTr("Update")
|
||||
|
||||
onTriggered: {
|
||||
// TODO: call update from backend
|
||||
root.updateManualReady.active = false
|
||||
}
|
||||
},
|
||||
Action {
|
||||
text: qsTr("Remind me later")
|
||||
|
||||
onTriggered: {
|
||||
// TODO: start timer here
|
||||
root.updateManualReady.active = false
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
property Notification updateManualRestartNeeded: Notification {
|
||||
text: qsTr("Bridge update is ready")
|
||||
icon: "./icons/ic-info-circle-filled.svg"
|
||||
type: Notification.NotificationType.Info
|
||||
group: Notifications.Group.Update
|
||||
|
||||
Connections {
|
||||
target: root.backend
|
||||
onUpdateManualRestartNeeded: {
|
||||
root.updateManualRestartNeeded.active = true
|
||||
}
|
||||
}
|
||||
|
||||
action: Action {
|
||||
text: qsTr("Restart Bridge")
|
||||
|
||||
onTriggered: {
|
||||
// TODO
|
||||
root.updateManualRestartNeeded.active = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
property Notification updateManualError: Notification {
|
||||
text: qsTr("Bridge couldn’t update")
|
||||
icon: "./icons/ic-exclamation-circle-filled.svg"
|
||||
type: Notification.NotificationType.Warning
|
||||
group: Notifications.Group.Update
|
||||
|
||||
Connections {
|
||||
target: root.backend
|
||||
onUpdateManualError: {
|
||||
root.updateManualError.active = true
|
||||
}
|
||||
}
|
||||
|
||||
action: Action {
|
||||
text: qsTr("Update manually")
|
||||
|
||||
onTriggered: {
|
||||
// TODO
|
||||
root.updateManualError.active = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
property Notification updateForce: Notification {
|
||||
text: qsTr("Update to ProtonMail Bridge") + " " + (data ? data.version : "")
|
||||
description: qsTr("This version of Bridge is no longer supported, please update. Learn why. To update manually, go to: https:/protonmail.com/bridge/download")
|
||||
icon: "./icons/ic-exclamation-circle-filled.svg"
|
||||
type: Notification.NotificationType.Danger
|
||||
group: Notifications.Group.Update | Notifications.Group.Dialogs
|
||||
|
||||
Connections {
|
||||
target: root.backend
|
||||
|
||||
onUpdateForce: {
|
||||
root.updateForce.data = { version: version }
|
||||
root.updateForce.active = true
|
||||
}
|
||||
}
|
||||
|
||||
action: [
|
||||
Action {
|
||||
text: qsTr("Update")
|
||||
|
||||
onTriggered: {
|
||||
// TODO: trigger update here
|
||||
root.updateForce.active = false
|
||||
}
|
||||
},
|
||||
Action {
|
||||
text: qsTr("Quite Bridge")
|
||||
|
||||
onTriggered: {
|
||||
// TODO: quit Bridge here
|
||||
root.updateForce.active = false
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
property Notification updateForceError: Notification {
|
||||
text: qsTr("Bridge coudn’t update")
|
||||
description: qsTr("You must update manually. Go to: https:/protonmail.com/bridge/download")
|
||||
icon: "./icons/ic-exclamation-circle-filled.svg"
|
||||
type: Notification.NotificationType.Danger
|
||||
group: Notifications.Group.Update | Notifications.Group.Dialogs
|
||||
|
||||
Connections {
|
||||
target: root.backend
|
||||
|
||||
onUpdateForceError: {
|
||||
root.updateForceError.active = true
|
||||
}
|
||||
}
|
||||
|
||||
action: [
|
||||
Action {
|
||||
text: qsTr("Update manually")
|
||||
|
||||
onTriggered: {
|
||||
// TODO: trigger update here
|
||||
root.updateForceError.active = false
|
||||
}
|
||||
},
|
||||
Action {
|
||||
text: qsTr("Quite Bridge")
|
||||
|
||||
onTriggered: {
|
||||
// TODO: quit Bridge here
|
||||
root.updateForce.active = false
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
property Notification updateSilentRestartNeeded: Notification {
|
||||
text: qsTr("Bridge update is ready")
|
||||
icon: "./icons/ic-info-circle-filled.svg"
|
||||
type: Notification.NotificationType.Info
|
||||
group: Notifications.Group.Update
|
||||
|
||||
Connections {
|
||||
target: root.backend
|
||||
onUpdateSilentRestartNeeded: {
|
||||
root.updateSilentRestartNeeded.active = true
|
||||
}
|
||||
}
|
||||
|
||||
action: Action {
|
||||
text: qsTr("Restart Bridge")
|
||||
|
||||
onTriggered: {
|
||||
// TODO
|
||||
root.updateSilentRestartNeeded.active = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
property Notification updateSilentError: Notification {
|
||||
text: qsTr("Bridge couldn’t update")
|
||||
icon: "./icons/ic-exclamation-circle-filled.svg"
|
||||
type: Notification.NotificationType.Warning
|
||||
group: Notifications.Group.Update
|
||||
|
||||
Connections {
|
||||
target: root.backend
|
||||
onUpdateSilentError: {
|
||||
root.updateSilentError.active = true
|
||||
}
|
||||
}
|
||||
|
||||
action: Action {
|
||||
text: qsTr("Update manually")
|
||||
|
||||
onTriggered: {
|
||||
// TODO
|
||||
root.updateSilentError.active = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Bug reports
|
||||
property Notification bugReportSendSuccess: Notification {
|
||||
text: qsTr("Bug report sent")
|
||||
description: qsTr("We’ve received your report, thank you! Our team will get back to you as soon as we can.")
|
||||
type: Notification.NotificationType.Success
|
||||
group: Notifications.Group.Configuration | Notifications.Group.Dialogs
|
||||
|
||||
Connections {
|
||||
target: root.backend
|
||||
onBugReportSendSuccess: {
|
||||
root.bugReportSendSuccess.active = true
|
||||
}
|
||||
}
|
||||
|
||||
action: [
|
||||
Action {
|
||||
text: qsTr("Ok")
|
||||
onTriggered: {
|
||||
root.bugReportSendSuccess.active = false
|
||||
}
|
||||
},
|
||||
Action {
|
||||
text: "test"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
property Notification bugReportSendError: Notification {
|
||||
text: qsTr("There was a problem")
|
||||
description: qsTr("There was a problem with sending your report. Please try again later or contact us directly at security@protonmail.com")
|
||||
type: Notification.NotificationType.Warning
|
||||
group: Notifications.Group.Configuration | Notifications.Group.Dialogs
|
||||
|
||||
Connections {
|
||||
target: root.backend
|
||||
onBugReportSendError: {
|
||||
root.bugReportSendError.active = true
|
||||
}
|
||||
}
|
||||
|
||||
action: Action {
|
||||
text: qsTr("Ok")
|
||||
onTriggered: {
|
||||
root.bugReportSendError.active = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Cache
|
||||
property Notification cacheAnavailable: Notification {
|
||||
text: qsTr("Cache location is unavailable")
|
||||
description: qsTr("Check the directory or change it in your settings.")
|
||||
type: Notification.NotificationType.Warning
|
||||
group: Notifications.Group.Configuration | Notifications.Group.Dialogs
|
||||
|
||||
Connections {
|
||||
target: root.backend
|
||||
onCacheAnavailable: {
|
||||
root.cacheAnavailable.active = true
|
||||
}
|
||||
}
|
||||
|
||||
action: [
|
||||
Action {
|
||||
text: qsTr("Quit Bridge")
|
||||
onTriggered: {
|
||||
root.cacheAnavailable.active = false
|
||||
}
|
||||
},
|
||||
Action {
|
||||
text: qsTr("Change location")
|
||||
onTriggered: {
|
||||
root.cacheAnavailable.active = false
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
property Notification cacheCantMove: Notification {
|
||||
text: qsTr("Can’t move cache")
|
||||
description: qsTr("The location you have selected is not available. Make sure you have enough free space or choose another location.")
|
||||
type: Notification.NotificationType.Warning
|
||||
group: Notifications.Group.Configuration | Notifications.Group.Dialogs
|
||||
|
||||
Connections {
|
||||
target: root.backend
|
||||
onCacheCantMove: {
|
||||
root.cacheCantMove.active = true
|
||||
}
|
||||
}
|
||||
|
||||
action: [
|
||||
Action {
|
||||
text: qsTr("Cancel")
|
||||
onTriggered: {
|
||||
root.cacheCantMove.active = false
|
||||
}
|
||||
},
|
||||
Action {
|
||||
text: qsTr("Change location")
|
||||
onTriggered: {
|
||||
root.cacheCantMove.active = false
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
// Other
|
||||
property Notification accountChanged: Notification {
|
||||
text: qsTr("The address list for your account has changed")
|
||||
icon: "./icons/ic-exclamation-circle-filled.svg"
|
||||
type: Notification.NotificationType.Danger
|
||||
group: Notifications.Group.Configuration
|
||||
|
||||
action: Action {
|
||||
text: qsTr("Reconfigure")
|
||||
|
||||
onTriggered: {
|
||||
// TODO: open configuration window here
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
property Notification diskFull: Notification {
|
||||
text: qsTr("Your disk is almost full")
|
||||
description: qsTr("Quit Bridge and free disk space or disable the local cache (not recommended).")
|
||||
type: Notification.NotificationType.Warning
|
||||
group: Notifications.Group.Configuration | Notifications.Group.Dialogs
|
||||
|
||||
Connections {
|
||||
target: root.backend
|
||||
onDiskFull: {
|
||||
root.diskFull.active = true
|
||||
}
|
||||
}
|
||||
|
||||
action: [
|
||||
Action {
|
||||
text: qsTr("Quit Bridge")
|
||||
onTriggered: {
|
||||
root.diskFull.active = false
|
||||
}
|
||||
},
|
||||
Action {
|
||||
text: qsTr("Settings")
|
||||
onTriggered: {
|
||||
root.diskFull.active = false
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
6
internal/frontend/qml/Notifications/qmldir
Normal file
6
internal/frontend/qml/Notifications/qmldir
Normal file
@ -0,0 +1,6 @@
|
||||
module Notifications
|
||||
depends QtQml 2.12
|
||||
|
||||
Notifications 1.0 Notifications.qml
|
||||
NotificationFilter 1.0 NotificationFilter.qml
|
||||
Notification 1.0 Notification.qml
|
||||
136
internal/frontend/qml/Proton/ApplicationWindow.qml
Normal file
136
internal/frontend/qml/Proton/ApplicationWindow.qml
Normal file
@ -0,0 +1,136 @@
|
||||
// Copyright (c) 2021 Proton Technologies AG
|
||||
//
|
||||
// This file is part of ProtonMail Bridge.
|
||||
//
|
||||
// ProtonMail 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.
|
||||
//
|
||||
// ProtonMail 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 ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import QtQml 2.12
|
||||
import QtQuick 2.12
|
||||
import QtQuick.Window 2.12
|
||||
import QtQuick.Controls 2.12
|
||||
import QtQuick.Controls.impl 2.12
|
||||
import QtQuick.Templates 2.12 as T
|
||||
|
||||
import "."
|
||||
|
||||
T.ApplicationWindow {
|
||||
id: root
|
||||
|
||||
property ColorScheme colorScheme
|
||||
|
||||
// popup priority based on types
|
||||
enum PopupType {
|
||||
Banner = 0,
|
||||
Dialog = 1
|
||||
}
|
||||
|
||||
// contains currently visible popup
|
||||
property var popupVisible: null
|
||||
|
||||
// list of all popups within ApplicationWindow
|
||||
property ListModel popups: ListModel {
|
||||
// overriding get method to ignore any role and return directly object itself
|
||||
function get(row) {
|
||||
if (row < 0 || row >= count) {
|
||||
return undefined
|
||||
}
|
||||
return data(index(row, 0), Qt.DisplayRole)
|
||||
}
|
||||
|
||||
onRowsInserted: {
|
||||
for (var i = first; i <= last; i++) {
|
||||
var obj = popups.get(i)
|
||||
obj.onShouldShowChanged.connect( root.processPopups )
|
||||
}
|
||||
|
||||
processPopups()
|
||||
}
|
||||
|
||||
onRowsAboutToBeRemoved: {
|
||||
for (var i = first; i <= last; i++ ) {
|
||||
var obj = popups.get(i)
|
||||
obj.onShouldShowChanged.disconnect( root.processPopups )
|
||||
|
||||
// if currently visible popup was removed
|
||||
if (root.popupVisible === obj) {
|
||||
root.popupVisible.visible = false
|
||||
root.popupVisible = null
|
||||
}
|
||||
}
|
||||
|
||||
processPopups()
|
||||
}
|
||||
}
|
||||
|
||||
function processPopups() {
|
||||
if ((root.popupVisible) && (!root.popupVisible.shouldShow)) {
|
||||
root.popupVisible.visible = false
|
||||
}
|
||||
|
||||
// do nothing if there is already visible popup
|
||||
if (root.popupVisible) {
|
||||
return
|
||||
}
|
||||
|
||||
var topmost = null
|
||||
for (var i = 0; i < popups.count; i++) {
|
||||
var obj = popups.get(i)
|
||||
|
||||
if (obj.shouldShow === false) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (topmost && (topmost.popupType > obj.popupType)) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (topmost && (topmost.popupType === obj.popupType) && (topmost.occurred > obj.occurred)) {
|
||||
continue
|
||||
}
|
||||
|
||||
topmost = obj
|
||||
}
|
||||
|
||||
root.popupVisible = topmost
|
||||
|
||||
if (!topmost) {
|
||||
return
|
||||
}
|
||||
|
||||
topmost.visible = true
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: root.popupVisible
|
||||
|
||||
onVisibleChanged: {
|
||||
if (root.popupVisible.visible) {
|
||||
return
|
||||
}
|
||||
|
||||
root.popupVisible = null
|
||||
root.processPopups()
|
||||
}
|
||||
}
|
||||
|
||||
color: root.colorScheme.background_norm
|
||||
|
||||
overlay.modal: Rectangle {
|
||||
color: root.colorScheme.backdrop_norm
|
||||
}
|
||||
|
||||
overlay.modeless: Rectangle {
|
||||
color: "transparent"
|
||||
}
|
||||
}
|
||||
@ -1,117 +0,0 @@
|
||||
// Copyright (c) 2021 Proton Technologies AG
|
||||
//
|
||||
// This file is part of ProtonMail Bridge.
|
||||
//
|
||||
// ProtonMail 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.
|
||||
//
|
||||
// ProtonMail 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 ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
import QtQml 2.12
|
||||
import QtQuick 2.13
|
||||
import QtQuick.Layouts 1.12
|
||||
import QtQuick.Controls.impl 2.12
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
width: layout.width
|
||||
height: layout.height
|
||||
|
||||
radius: 10
|
||||
|
||||
signal accepted()
|
||||
|
||||
|
||||
property alias text: description.text
|
||||
property var actionText: ""
|
||||
|
||||
property var colorText: Style.currentStyle.text_invert
|
||||
property var colorMain: "#000"
|
||||
property var colorHover: "#000"
|
||||
property var colorActive: "#000"
|
||||
property var iconSource: "../icons/ic-exclamation-circle-filled.svg"
|
||||
|
||||
color: root.colorMain
|
||||
border.color: root.colorActive
|
||||
border.width: 1
|
||||
|
||||
property var maxWidth: 600
|
||||
property var minWidth: 400
|
||||
property var usedWidth: button.width + icon.width
|
||||
|
||||
RowLayout {
|
||||
id: layout
|
||||
|
||||
IconLabel {
|
||||
id:icon
|
||||
Layout.alignment: Qt.AlignCenter
|
||||
Layout.leftMargin: 17.5
|
||||
Layout.topMargin: 15.5
|
||||
Layout.bottomMargin: 15.5
|
||||
color: root.colorText
|
||||
icon.source: root.iconSource
|
||||
icon.color: root.colorText
|
||||
icon.height: Style.title_line_height
|
||||
}
|
||||
|
||||
ProtonLabel {
|
||||
id: description
|
||||
Layout.alignment: Qt.AlignCenter
|
||||
Layout.leftMargin: 9.5
|
||||
Layout.minimumWidth: root.minWidth - root.usedWidth
|
||||
Layout.maximumWidth: root.maxWidth - root.usedWidth
|
||||
|
||||
color: root.colorText
|
||||
state: "body"
|
||||
|
||||
wrapMode: Text.WordWrap
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
|
||||
Button {
|
||||
id:button
|
||||
Layout.fillHeight: true
|
||||
|
||||
hoverEnabled: true
|
||||
|
||||
text: root.actionText.toUpperCase()
|
||||
|
||||
onClicked: root.accepted()
|
||||
|
||||
background: RoundedRectangle {
|
||||
width:parent.width
|
||||
height:parent.height
|
||||
strokeColor: root.colorActive
|
||||
strokeWidth: root.border.width
|
||||
|
||||
radiusTopRight : root.radius
|
||||
radiusBottomRight : root.radius
|
||||
radiusTopLeft : 0
|
||||
radiusBottomLeft : 0
|
||||
|
||||
fillColor: button.down ? root.colorActive : (
|
||||
button.hovered ? root.colorHover :
|
||||
root.colorMain
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
state: "info"
|
||||
states: [
|
||||
State{ name : "danger" ; PropertyChanges{ target : root ; colorMain : Style.currentStyle.signal_danger ; colorHover : Style.currentStyle.signal_danger_hover ; colorActive : Style.currentStyle.signal_danger_active ; iconSource: "../icons/ic-exclamation-circle-filled.svg"}} ,
|
||||
State{ name : "warning" ; PropertyChanges{ target : root ; colorMain : Style.currentStyle.signal_warning ; colorHover : Style.currentStyle.signal_warning_hover ; colorActive : Style.currentStyle.signal_warning_active ; iconSource: "../icons/ic-exclamation-circle-filled.svg"}} ,
|
||||
State{ name : "success" ; PropertyChanges{ target : root ; colorMain : Style.currentStyle.signal_success ; colorHover : Style.currentStyle.signal_success_hover ; colorActive : Style.currentStyle.signal_success_active ; iconSource: "../icons/ic-info-circle-filled.svg"}} ,
|
||||
State{ name : "info" ; PropertyChanges{ target : root ; colorMain : Style.currentStyle.signal_info ; colorHover : Style.currentStyle.signal_info_hover ; colorActive : Style.currentStyle.signal_info_active ; iconSource: "../icons/ic-info-circle-filled.svg"}}
|
||||
]
|
||||
}
|
||||
@ -22,7 +22,7 @@ import QtQuick.Templates 2.12 as T
|
||||
import "."
|
||||
|
||||
T.Button {
|
||||
property var colorScheme: parent.colorScheme ? parent.colorScheme : Style.currentStyle
|
||||
property ColorScheme colorScheme
|
||||
|
||||
property alias secondary: control.flat
|
||||
readonly property bool primary: !secondary
|
||||
@ -30,6 +30,10 @@ T.Button {
|
||||
|
||||
property bool loading: false
|
||||
|
||||
property bool borderless: false
|
||||
|
||||
property int labelType: Label.LabelType.Body
|
||||
|
||||
// TODO: store previous enabled state and restore it?
|
||||
// For now assuming that only enabled buttons could have loading state
|
||||
onLoadingChanged: {
|
||||
@ -55,9 +59,7 @@ T.Button {
|
||||
horizontalPadding: 16
|
||||
spacing: 10
|
||||
|
||||
font.family: Style.font_family
|
||||
font.pixelSize: Style.body_font_size
|
||||
font.letterSpacing: Style.body_letter_spacing
|
||||
font: label.font
|
||||
|
||||
icon.width: 16
|
||||
icon.height: 16
|
||||
@ -65,7 +67,7 @@ T.Button {
|
||||
if (primary && !isIcon) {
|
||||
return "#FFFFFF"
|
||||
} else {
|
||||
return colorScheme.text_norm
|
||||
return control.colorScheme.text_norm
|
||||
}
|
||||
}
|
||||
|
||||
@ -103,6 +105,7 @@ T.Button {
|
||||
}
|
||||
|
||||
Label {
|
||||
colorScheme: root.colorScheme
|
||||
id: label
|
||||
anchors.left: labelIcon.left
|
||||
anchors.top: labelIcon.top
|
||||
@ -115,15 +118,16 @@ T.Button {
|
||||
verticalAlignment: Qt.AlignVCenter
|
||||
|
||||
text: control.text
|
||||
font: control.font
|
||||
color: {
|
||||
if (primary && !isIcon) {
|
||||
return "#FFFFFF"
|
||||
} else {
|
||||
return colorScheme.text_norm
|
||||
return control.colorScheme.text_norm
|
||||
}
|
||||
}
|
||||
opacity: control.enabled || control.loading ? 1.0 : 0.5
|
||||
|
||||
type: labelType
|
||||
}
|
||||
|
||||
ColorImage {
|
||||
@ -176,74 +180,74 @@ T.Button {
|
||||
// Primary colors
|
||||
|
||||
if (control.down) {
|
||||
return colorScheme.interaction_norm_active
|
||||
return control.colorScheme.interaction_norm_active
|
||||
}
|
||||
|
||||
if (control.enabled && (control.highlighted || control.hovered || control.checked)) {
|
||||
return colorScheme.interaction_norm_hover
|
||||
return control.colorScheme.interaction_norm_hover
|
||||
}
|
||||
|
||||
if (control.loading) {
|
||||
return colorScheme.interaction_norm_hover
|
||||
return control.colorScheme.interaction_norm_hover
|
||||
}
|
||||
|
||||
return colorScheme.interaction_norm
|
||||
return control.colorScheme.interaction_norm
|
||||
} else {
|
||||
// Secondary colors
|
||||
|
||||
if (control.down) {
|
||||
return colorScheme.interaction_default_active
|
||||
return control.colorScheme.interaction_default_active
|
||||
}
|
||||
|
||||
if (control.enabled && (control.highlighted || control.hovered || control.checked)) {
|
||||
return colorScheme.interaction_default_hover
|
||||
return control.colorScheme.interaction_default_hover
|
||||
}
|
||||
|
||||
if (control.loading) {
|
||||
return colorScheme.interaction_default_hover
|
||||
return control.colorScheme.interaction_default_hover
|
||||
}
|
||||
|
||||
return colorScheme.interaction_default
|
||||
return control.colorScheme.interaction_default
|
||||
}
|
||||
} else {
|
||||
if (primary) {
|
||||
// Primary icon colors
|
||||
|
||||
if (control.down) {
|
||||
return colorScheme.interaction_default_active
|
||||
return control.colorScheme.interaction_default_active
|
||||
}
|
||||
|
||||
if (control.enabled && (control.highlighted || control.hovered || control.checked)) {
|
||||
return colorScheme.interaction_default_hover
|
||||
return control.colorScheme.interaction_default_hover
|
||||
}
|
||||
|
||||
if (control.loading) {
|
||||
return colorScheme.interaction_default_hover
|
||||
return control.colorScheme.interaction_default_hover
|
||||
}
|
||||
|
||||
return colorScheme.interaction_default
|
||||
return control.colorScheme.interaction_default
|
||||
} else {
|
||||
// Secondary icon colors
|
||||
|
||||
if (control.down) {
|
||||
return colorScheme.interaction_default_active
|
||||
return control.colorScheme.interaction_default_active
|
||||
}
|
||||
|
||||
if (control.enabled && (control.highlighted || control.hovered || control.checked)) {
|
||||
return colorScheme.interaction_default_hover
|
||||
return control.colorScheme.interaction_default_hover
|
||||
}
|
||||
|
||||
if (control.loading) {
|
||||
return colorScheme.interaction_default_hover
|
||||
return control.colorScheme.interaction_default_hover
|
||||
}
|
||||
|
||||
return colorScheme.interaction_default
|
||||
return control.colorScheme.interaction_default
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
border.color: colorScheme.border_norm
|
||||
border.width: secondary ? 1 : 0
|
||||
border.color: control.colorScheme.border_norm
|
||||
border.width: secondary && !borderless ? 1 : 0
|
||||
|
||||
opacity: control.enabled || control.loading ? 1.0 : 0.5
|
||||
}
|
||||
|
||||
@ -21,7 +21,7 @@ import QtQuick.Controls.impl 2.12
|
||||
import QtQuick.Templates 2.12 as T
|
||||
|
||||
T.CheckBox {
|
||||
property var colorScheme: parent.colorScheme ? parent.colorScheme : Style.currentStyle
|
||||
property ColorScheme colorScheme
|
||||
|
||||
property bool error: false
|
||||
|
||||
@ -46,39 +46,39 @@ T.CheckBox {
|
||||
|
||||
color: {
|
||||
if (!checked) {
|
||||
return colorScheme.background_norm
|
||||
return control.colorScheme.background_norm
|
||||
}
|
||||
|
||||
if (!control.enabled) {
|
||||
return colorScheme.field_disabled
|
||||
return control.colorScheme.field_disabled
|
||||
}
|
||||
|
||||
if (control.error) {
|
||||
return colorScheme.signal_danger
|
||||
return control.colorScheme.signal_danger
|
||||
}
|
||||
|
||||
if (control.hovered) {
|
||||
return colorScheme.interaction_norm_hover
|
||||
return control.colorScheme.interaction_norm_hover
|
||||
}
|
||||
|
||||
return colorScheme.interaction_norm
|
||||
return control.colorScheme.interaction_norm
|
||||
}
|
||||
|
||||
border.width: control.checked ? 0 : 1
|
||||
border.color: {
|
||||
if (!control.enabled) {
|
||||
return colorScheme.field_disabled
|
||||
return control.colorScheme.field_disabled
|
||||
}
|
||||
|
||||
if (control.error) {
|
||||
return colorScheme.signal_danger
|
||||
return control.colorScheme.signal_danger
|
||||
}
|
||||
|
||||
if (control.hovered) {
|
||||
return colorScheme.interaction_norm_hover
|
||||
return control.colorScheme.interaction_norm_hover
|
||||
}
|
||||
|
||||
return colorScheme.field_norm
|
||||
return control.colorScheme.field_norm
|
||||
}
|
||||
|
||||
ColorImage {
|
||||
@ -112,21 +112,21 @@ T.CheckBox {
|
||||
|
||||
color: {
|
||||
if (!enabled) {
|
||||
return colorScheme.text_disabled
|
||||
return control.colorScheme.text_disabled
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return colorScheme.signal_danger
|
||||
return control.colorScheme.signal_danger
|
||||
}
|
||||
|
||||
return colorScheme.text_norm
|
||||
return control.colorScheme.text_norm
|
||||
}
|
||||
|
||||
font.family: Style.font_family
|
||||
font.weight: Style.fontWidth_400
|
||||
font.pixelSize: 14
|
||||
lineHeight: 20
|
||||
font.weight: Style.fontWeight_400
|
||||
font.pixelSize: Style.body_font_size
|
||||
lineHeight: Style.body_line_height
|
||||
lineHeightMode: Text.FixedHeight
|
||||
font.letterSpacing: 0.2
|
||||
font.letterSpacing: Style.body_letter_spacing
|
||||
}
|
||||
}
|
||||
|
||||
82
internal/frontend/qml/Proton/Dialog.qml
Normal file
82
internal/frontend/qml/Proton/Dialog.qml
Normal file
@ -0,0 +1,82 @@
|
||||
// Copyright (c) 2021 Proton Technologies AG
|
||||
//
|
||||
// This file is part of ProtonMail Bridge.
|
||||
//
|
||||
// ProtonMail 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.
|
||||
//
|
||||
// ProtonMail 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 ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import QtQml 2.12
|
||||
import QtQuick 2.12
|
||||
import QtQuick.Templates 2.12 as T
|
||||
import QtQuick.Controls 2.12
|
||||
import QtQuick.Controls.impl 2.12
|
||||
|
||||
import "."
|
||||
|
||||
T.Dialog {
|
||||
id: root
|
||||
property ColorScheme colorScheme
|
||||
|
||||
Component.onCompleted: {
|
||||
if (!ApplicationWindow.window) {
|
||||
return
|
||||
}
|
||||
|
||||
if (ApplicationWindow.window.popups === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
var obj = this
|
||||
ApplicationWindow.window.popups.append( { obj } )
|
||||
}
|
||||
|
||||
readonly property int popupType: ApplicationWindow.PopupType.Dialog
|
||||
|
||||
property bool shouldShow: false
|
||||
readonly property var occurred: shouldShow ? new Date() : undefined
|
||||
function open() {
|
||||
root.shouldShow = true
|
||||
}
|
||||
|
||||
function close() {
|
||||
root.shouldShow = false
|
||||
}
|
||||
|
||||
anchors.centerIn: Overlay.overlay
|
||||
|
||||
implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
|
||||
contentWidth + leftPadding + rightPadding,
|
||||
implicitHeaderWidth,
|
||||
implicitFooterWidth)
|
||||
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
|
||||
contentHeight + topPadding + bottomPadding
|
||||
+ (implicitHeaderHeight > 0 ? implicitHeaderHeight + spacing : 0)
|
||||
+ (implicitFooterHeight > 0 ? implicitFooterHeight + spacing : 0))
|
||||
|
||||
padding: 24
|
||||
|
||||
background: Rectangle {
|
||||
color: root.colorScheme.background_norm
|
||||
radius: 10
|
||||
}
|
||||
|
||||
// TODO: Add DropShadow here
|
||||
|
||||
T.Overlay.modal: Rectangle {
|
||||
color: root.colorScheme.backdrop_norm
|
||||
}
|
||||
|
||||
T.Overlay.modeless: Rectangle {
|
||||
color: "transparent"
|
||||
}
|
||||
}
|
||||
138
internal/frontend/qml/Proton/Label.qml
Normal file
138
internal/frontend/qml/Proton/Label.qml
Normal file
@ -0,0 +1,138 @@
|
||||
// Copyright (c) 2021 Proton Technologies AG
|
||||
//
|
||||
// This file is part of ProtonMail Bridge.
|
||||
//
|
||||
// ProtonMail 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.
|
||||
//
|
||||
// ProtonMail 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 ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import QtQuick 2.13
|
||||
import QtQuick.Controls 2.12
|
||||
import QtQuick.Controls.impl 2.12
|
||||
import QtQuick.Templates 2.12 as T
|
||||
import "."
|
||||
|
||||
T.Label {
|
||||
id: root
|
||||
|
||||
property ColorScheme colorScheme
|
||||
|
||||
enum LabelType {
|
||||
// weight 700, size 28, height 36
|
||||
Heading,
|
||||
// weight 700, size 20, height 24
|
||||
Title,
|
||||
// weight 400, size 18, height 26
|
||||
Lead,
|
||||
// weight 400, size 14, height 20, spacing 0.2
|
||||
Body,
|
||||
// weight 600, size 14, height 20, spacing 0.2
|
||||
Body_semibold,
|
||||
// weight 700, size 14, height 20, spacing 0.2
|
||||
Body_bold,
|
||||
// weight 400, size 12, height 16, spacing 0.4
|
||||
Caption,
|
||||
// weight 600, size 12, height 16, spacing 0.4
|
||||
Caption_semibold,
|
||||
// weight 700, size 12, height 16, spacing 0.4
|
||||
Caption_bold
|
||||
}
|
||||
property int type: Label.LabelType.Body
|
||||
|
||||
color: root.enabled ? root.colorScheme.text_norm : root.colorScheme.text_disabled
|
||||
palette.link: root.colorScheme.interaction_norm
|
||||
|
||||
font.family: Style.font_family
|
||||
lineHeightMode: Text.FixedHeight
|
||||
|
||||
font.weight: {
|
||||
switch (root.type) {
|
||||
case Label.LabelType.Heading:
|
||||
return Style.fontWeight_700
|
||||
case Label.LabelType.Title:
|
||||
return Style.fontWeight_700
|
||||
case Label.LabelType.Lead:
|
||||
return Style.fontWeight_400
|
||||
case Label.LabelType.Body:
|
||||
return Style.fontWeight_400
|
||||
case Label.LabelType.Body_semibold:
|
||||
return Style.fontWeight_600
|
||||
case Label.LabelType.Body_bold:
|
||||
return Style.fontWeight_700
|
||||
case Label.LabelType.Caption:
|
||||
return Style.fontWeight_400
|
||||
case Label.LabelType.Caption_semibold:
|
||||
return Style.fontWeight_600
|
||||
case Label.LabelType.Caption_bold:
|
||||
return Style.fontWeight_700
|
||||
}
|
||||
}
|
||||
|
||||
font.pixelSize: {
|
||||
switch (root.type) {
|
||||
case Label.LabelType.Heading:
|
||||
return Style.heading_font_size
|
||||
case Label.LabelType.Title:
|
||||
return Style.title_font_size
|
||||
case Label.LabelType.Lead:
|
||||
return Style.lead_font_size
|
||||
case Label.LabelType.Body:
|
||||
case Label.LabelType.Body_semibold:
|
||||
case Label.LabelType.Body_bold:
|
||||
return Style.body_font_size
|
||||
case Label.LabelType.Caption:
|
||||
case Label.LabelType.Caption_semibold:
|
||||
case Label.LabelType.Caption_bold:
|
||||
return Style.caption_font_size
|
||||
}
|
||||
}
|
||||
|
||||
lineHeight: {
|
||||
switch (root.type) {
|
||||
case Label.LabelType.Heading:
|
||||
return Style.heading_line_height
|
||||
case Label.LabelType.Title:
|
||||
return Style.title_line_height
|
||||
case Label.LabelType.Lead:
|
||||
return Style.lead_line_height
|
||||
case Label.LabelType.Body:
|
||||
case Label.LabelType.Body_semibold:
|
||||
case Label.LabelType.Body_bold:
|
||||
return Style.body_line_height
|
||||
case Label.LabelType.Caption:
|
||||
case Label.LabelType.Caption_semibold:
|
||||
case Label.LabelType.Caption_bold:
|
||||
return Style.caption_line_height
|
||||
}
|
||||
}
|
||||
|
||||
font.letterSpacing: {
|
||||
switch (root.type) {
|
||||
case Label.LabelType.Heading:
|
||||
case Label.LabelType.Title:
|
||||
case Label.LabelType.Lead:
|
||||
return 0
|
||||
case Label.LabelType.Body:
|
||||
case Label.LabelType.Body_semibold:
|
||||
case Label.LabelType.Body_bold:
|
||||
return Style.body_letter_spacing
|
||||
case Label.LabelType.Caption:
|
||||
case Label.LabelType.Caption_semibold:
|
||||
case Label.LabelType.Caption_bold:
|
||||
return Style.caption_letter_spacing
|
||||
}
|
||||
}
|
||||
|
||||
function link(url, text) {
|
||||
return `<a href="${url}">${text}</a>`
|
||||
}
|
||||
}
|
||||
68
internal/frontend/qml/Proton/Menu.qml
Normal file
68
internal/frontend/qml/Proton/Menu.qml
Normal file
@ -0,0 +1,68 @@
|
||||
// Copyright (c) 2021 Proton Technologies AG
|
||||
//
|
||||
// This file is part of ProtonMail Bridge.
|
||||
//
|
||||
// ProtonMail 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.
|
||||
//
|
||||
// ProtonMail 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 ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import QtQuick 2.12
|
||||
import QtQuick.Controls 2.12
|
||||
import QtQuick.Controls.impl 2.12
|
||||
import QtQuick.Templates 2.12 as T
|
||||
import QtQuick.Window 2.12
|
||||
import "."
|
||||
|
||||
T.Menu {
|
||||
id: control
|
||||
|
||||
property ColorScheme colorScheme
|
||||
|
||||
implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
|
||||
contentWidth + leftPadding + rightPadding)
|
||||
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
|
||||
contentHeight + topPadding + bottomPadding)
|
||||
|
||||
margins: 0
|
||||
overlap: 1
|
||||
|
||||
delegate: MenuItem {
|
||||
colorScheme: control.colorScheme
|
||||
}
|
||||
|
||||
contentItem: Item {
|
||||
implicitHeight: children[0].implicitHeight + children[0].anchors.topMargin + children[0].anchors.bottomMargin
|
||||
implicitWidth: children[0].implicitWidth + children[0].anchors.leftMargin + children[0].anchors.rightMargin
|
||||
|
||||
ListView {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 8
|
||||
|
||||
implicitHeight: contentHeight
|
||||
model: control.contentModel
|
||||
interactive: Window.window ? contentHeight > Window.window.height : false
|
||||
clip: true
|
||||
currentIndex: control.currentIndex
|
||||
|
||||
ScrollIndicator.vertical: ScrollIndicator {}
|
||||
}
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
implicitWidth: 200
|
||||
implicitHeight: 40
|
||||
color: colorScheme.background_norm
|
||||
border.width: 1
|
||||
border.color: colorScheme.border_weak
|
||||
radius: 10
|
||||
}
|
||||
}
|
||||
72
internal/frontend/qml/Proton/MenuItem.qml
Normal file
72
internal/frontend/qml/Proton/MenuItem.qml
Normal file
@ -0,0 +1,72 @@
|
||||
// Copyright (c) 2021 Proton Technologies AG
|
||||
//
|
||||
// This file is part of ProtonMail Bridge.
|
||||
//
|
||||
// ProtonMail 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.
|
||||
//
|
||||
// ProtonMail 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 ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import QtQuick 2.12
|
||||
import QtQuick.Controls 2.12
|
||||
import QtQuick.Controls.impl 2.12
|
||||
import QtQuick.Templates 2.12 as T
|
||||
import "."
|
||||
|
||||
T.MenuItem {
|
||||
id: control
|
||||
|
||||
property ColorScheme colorScheme
|
||||
|
||||
implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
|
||||
implicitContentWidth + leftPadding + rightPadding)
|
||||
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
|
||||
implicitContentHeight + topPadding + bottomPadding,
|
||||
implicitIndicatorHeight + topPadding + bottomPadding)
|
||||
|
||||
padding: 6
|
||||
spacing: 6
|
||||
|
||||
icon.width: 24
|
||||
icon.height: 24
|
||||
icon.color: control.enabled ? control.colorScheme.text_norm : control.colorScheme.text_disabled
|
||||
|
||||
font.family: Style.font_family
|
||||
font.weight: Style.fontWeight_400
|
||||
font.pixelSize: Style.body_font_size
|
||||
font.letterSpacing: Style.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 {
|
||||
implicitWidth: 164
|
||||
implicitHeight: 36
|
||||
radius: 4
|
||||
color: control.down ? control.colorScheme.interaction_default_active : control.highlighted ? control.colorScheme.interaction_default_hover : control.colorScheme.interaction_default
|
||||
}
|
||||
}
|
||||
67
internal/frontend/qml/Proton/Popup.qml
Normal file
67
internal/frontend/qml/Proton/Popup.qml
Normal file
@ -0,0 +1,67 @@
|
||||
// Copyright (c) 2021 Proton Technologies AG
|
||||
//
|
||||
// This file is part of ProtonMail Bridge.
|
||||
//
|
||||
// ProtonMail 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.
|
||||
//
|
||||
// ProtonMail 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 ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import QtQml 2.12
|
||||
import QtQuick 2.12
|
||||
import QtQuick.Controls 2.12
|
||||
import QtQuick.Controls.impl 2.12
|
||||
import QtQuick.Templates 2.12 as T
|
||||
|
||||
T.Popup {
|
||||
id: root
|
||||
property ColorScheme colorScheme
|
||||
|
||||
Component.onCompleted: {
|
||||
if (!ApplicationWindow.window) {
|
||||
return
|
||||
}
|
||||
|
||||
if (ApplicationWindow.window.popups === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
var obj = this
|
||||
ApplicationWindow.window.popups.append( { obj } )
|
||||
}
|
||||
|
||||
property int popupType: ApplicationWindow.PopupType.Banner
|
||||
|
||||
property bool shouldShow: false
|
||||
readonly property var occurred: shouldShow ? new Date() : undefined
|
||||
function open() {
|
||||
root.shouldShow = true
|
||||
}
|
||||
|
||||
function close() {
|
||||
root.shouldShow = false
|
||||
}
|
||||
|
||||
implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
|
||||
contentWidth + leftPadding + rightPadding)
|
||||
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
|
||||
contentHeight + topPadding + bottomPadding)
|
||||
|
||||
// TODO: Add DropShadow here
|
||||
|
||||
T.Overlay.modal: Rectangle {
|
||||
color: root.colorScheme.backdrop_norm
|
||||
}
|
||||
|
||||
T.Overlay.modeless: Rectangle {
|
||||
color: "transparent"
|
||||
}
|
||||
}
|
||||
@ -1,43 +0,0 @@
|
||||
// Copyright (c) 2021 Proton Technologies AG
|
||||
//
|
||||
// This file is part of ProtonMail Bridge.
|
||||
//
|
||||
// ProtonMail 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.
|
||||
//
|
||||
// ProtonMail 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 ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import QtQuick 2.13
|
||||
import QtQuick.Controls 2.12
|
||||
|
||||
Label {
|
||||
id: root
|
||||
|
||||
color: Style.currentStyle.text_norm
|
||||
palette.link: Style.currentStyle.interaction_norm
|
||||
|
||||
font.family: ProtonStyle.font_family
|
||||
font.weight: ProtonStyle.fontWidth_400
|
||||
lineHeightMode: Text.FixedHeight
|
||||
|
||||
function putLink(linkURL,linkText) {
|
||||
return `<a href="${linkURL}">${linkText}</a>`
|
||||
}
|
||||
|
||||
state: "title"
|
||||
states: [
|
||||
State { name : "heading" ; PropertyChanges { target : root ; font.pixelSize : Style.heading_font_size ; lineHeight : Style.heading_line_height } },
|
||||
State { name : "title" ; PropertyChanges { target : root ; font.pixelSize : Style.title_font_size ; lineHeight : Style.title_line_height } },
|
||||
State { name : "lead" ; PropertyChanges { target : root ; font.pixelSize : Style.lead_font_size ; lineHeight : Style.lead_line_height } },
|
||||
State { name : "body" ; PropertyChanges { target : root ; font.pixelSize : Style.body_font_size ; lineHeight : Style.body_line_height ; font.letterSpacing : Style.body_letter_spacing } },
|
||||
State { name : "caption" ; PropertyChanges { target : root ; font.pixelSize : Style.caption_font_size ; lineHeight : Style.caption_line_height ; font.letterSpacing : Style.caption_letter_spacing } }
|
||||
]
|
||||
}
|
||||
@ -21,7 +21,7 @@ import QtQuick.Controls.impl 2.12
|
||||
import QtQuick.Templates 2.12 as T
|
||||
|
||||
T.RadioButton {
|
||||
property var colorScheme: parent.colorScheme ? parent.colorScheme : Style.currentStyle
|
||||
property ColorScheme colorScheme
|
||||
|
||||
property bool error: false
|
||||
|
||||
@ -44,22 +44,22 @@ T.RadioButton {
|
||||
x: text ? (control.mirrored ? control.width - width - control.rightPadding : control.leftPadding) : control.leftPadding + (control.availableWidth - width) / 2
|
||||
y: control.topPadding + (control.availableHeight - height) / 2
|
||||
|
||||
color: colorScheme.background_norm
|
||||
color: control.colorScheme.background_norm
|
||||
border.width: 1
|
||||
border.color: {
|
||||
if (!control.enabled) {
|
||||
return colorScheme.field_disabled
|
||||
return control.colorScheme.field_disabled
|
||||
}
|
||||
|
||||
if (control.error) {
|
||||
return colorScheme.signal_danger
|
||||
return control.colorScheme.signal_danger
|
||||
}
|
||||
|
||||
if (control.hovered) {
|
||||
return colorScheme.interaction_norm_hover
|
||||
return control.colorScheme.interaction_norm_hover
|
||||
}
|
||||
|
||||
return colorScheme.field_norm
|
||||
return control.colorScheme.field_norm
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
@ -70,18 +70,18 @@ T.RadioButton {
|
||||
radius: width / 2
|
||||
color: {
|
||||
if (!control.enabled) {
|
||||
return colorScheme.field_disabled
|
||||
return control.colorScheme.field_disabled
|
||||
}
|
||||
|
||||
if (control.error) {
|
||||
return colorScheme.signal_danger
|
||||
return control.colorScheme.signal_danger
|
||||
}
|
||||
|
||||
if (control.hovered) {
|
||||
return colorScheme.interaction_norm_hover
|
||||
return control.colorScheme.interaction_norm_hover
|
||||
}
|
||||
|
||||
return colorScheme.interaction_norm
|
||||
return control.colorScheme.interaction_norm
|
||||
}
|
||||
visible: control.checked
|
||||
}
|
||||
@ -95,21 +95,21 @@ T.RadioButton {
|
||||
|
||||
color: {
|
||||
if (!enabled) {
|
||||
return colorScheme.text_disabled
|
||||
return control.colorScheme.text_disabled
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return colorScheme.signal_danger
|
||||
return control.colorScheme.signal_danger
|
||||
}
|
||||
|
||||
return colorScheme.text_norm
|
||||
return control.colorScheme.text_norm
|
||||
}
|
||||
|
||||
font.family: Style.font_family
|
||||
font.weight: Style.fontWidth_400
|
||||
font.pixelSize: 14
|
||||
lineHeight: 20
|
||||
font.weight: Style.fontWeight_400
|
||||
font.pixelSize: Style.body_font_size
|
||||
lineHeight: Style.body_line_height
|
||||
lineHeightMode: Text.FixedHeight
|
||||
font.letterSpacing: 0.2
|
||||
font.letterSpacing: Style.body_letter_spacing
|
||||
}
|
||||
}
|
||||
|
||||
@ -32,9 +32,8 @@ QtObject {
|
||||
// property color primay_norm
|
||||
// ...
|
||||
// }
|
||||
// and instead of "var" later on "ColorScheme" should be used (also in each component)
|
||||
|
||||
property var lightStyle: ColorScheme {
|
||||
property ColorScheme lightStyle: ColorScheme {
|
||||
id: _lightStyle
|
||||
|
||||
prominent: prominentStyle
|
||||
@ -105,7 +104,7 @@ QtObject {
|
||||
backdrop_norm: "#7A262A33"
|
||||
}
|
||||
|
||||
property var prominentStyle: ColorScheme {
|
||||
property ColorScheme prominentStyle: ColorScheme {
|
||||
id: _prominentStyle
|
||||
|
||||
prominent: this
|
||||
@ -176,7 +175,7 @@ QtObject {
|
||||
backdrop_norm: "#52000000"
|
||||
}
|
||||
|
||||
property var darkStyle: ColorScheme {
|
||||
property ColorScheme darkStyle: ColorScheme {
|
||||
id: _darkStyle
|
||||
|
||||
prominent: prominentStyle
|
||||
@ -249,7 +248,7 @@ QtObject {
|
||||
|
||||
// TODO: if default style should be loaded from somewhere
|
||||
// (i.e. from preferencies file) - it should be loaded here
|
||||
property var currentStyle: lightStyle
|
||||
property ColorScheme currentStyle: lightStyle
|
||||
|
||||
property string font_family: {
|
||||
switch (Qt.platform.os) {
|
||||
@ -281,15 +280,13 @@ QtObject {
|
||||
property int caption_line_height: 16
|
||||
property real caption_letter_spacing: 0.4
|
||||
|
||||
property int fontWidth_100: Font.Thin
|
||||
property int fontWidth_200: Font.Light
|
||||
property int fontWidth_300: Font.ExtraLight
|
||||
property int fontWidth_400: Font.Normal
|
||||
property int fontWidth_500: Font.Medium
|
||||
property int fontWidth_600: Font.DemiBold
|
||||
property int fontWidth_700: Font.Bold
|
||||
property int fontWidth_800: Font.ExtraBold
|
||||
property int fontWidth_900: Font.Black
|
||||
|
||||
property var transparent: "#00000000"
|
||||
property int fontWeight_100: Font.Thin
|
||||
property int fontWeight_200: Font.Light
|
||||
property int fontWeight_300: Font.ExtraLight
|
||||
property int fontWeight_400: Font.Normal
|
||||
property int fontWeight_500: Font.Medium
|
||||
property int fontWeight_600: Font.DemiBold
|
||||
property int fontWeight_700: Font.Bold
|
||||
property int fontWeight_800: Font.ExtraBold
|
||||
property int fontWeight_900: Font.Black
|
||||
}
|
||||
|
||||
@ -21,7 +21,7 @@ import QtQuick.Controls 2.12
|
||||
import QtQuick.Controls.impl 2.12
|
||||
|
||||
T.Switch {
|
||||
property var colorScheme: parent.colorScheme ? parent.colorScheme : Style.currentStyle
|
||||
property ColorScheme colorScheme
|
||||
|
||||
property bool loading: false
|
||||
|
||||
@ -57,9 +57,9 @@ T.Switch {
|
||||
leftPadding: 0
|
||||
rightPadding: 0
|
||||
padding: 0
|
||||
color: control.enabled || control.loading ? colorScheme.background_norm : colorScheme.background_strong
|
||||
color: control.enabled || control.loading ? control.colorScheme.background_norm : control.colorScheme.background_strong
|
||||
border.width: control.enabled && !loading ? 1 : 0
|
||||
border.color: control.hovered ? colorScheme.field_hover : colorScheme.field_norm
|
||||
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)))
|
||||
@ -72,22 +72,22 @@ T.Switch {
|
||||
|
||||
color: {
|
||||
if (!control.enabled) {
|
||||
return colorScheme.field_disabled
|
||||
return control.colorScheme.field_disabled
|
||||
}
|
||||
|
||||
if (control.checked) {
|
||||
if (control.hovered) {
|
||||
return colorScheme.interaction_norm_hover
|
||||
return control.colorScheme.interaction_norm_hover
|
||||
}
|
||||
|
||||
return colorScheme.interaction_norm
|
||||
return control.colorScheme.interaction_norm
|
||||
}
|
||||
|
||||
if (control.hovered) {
|
||||
return colorScheme.field_hover
|
||||
return control.colorScheme.field_hover
|
||||
}
|
||||
|
||||
return colorScheme.field_norm
|
||||
return control.colorScheme.field_norm
|
||||
}
|
||||
|
||||
ColorImage {
|
||||
@ -114,7 +114,7 @@ T.Switch {
|
||||
|
||||
width: 18
|
||||
height: 18
|
||||
color: colorScheme.interaction_norm_hover
|
||||
color: control.colorScheme.interaction_norm_hover
|
||||
source: "../icons/Loader_16.svg"
|
||||
visible: control.loading
|
||||
|
||||
@ -137,13 +137,13 @@ T.Switch {
|
||||
|
||||
text: control.text
|
||||
|
||||
color: control.enabled || control.loading ? colorScheme.text_norm : colorScheme.text_disabled
|
||||
color: control.enabled || control.loading ? control.colorScheme.text_norm : control.colorScheme.text_disabled
|
||||
|
||||
font.family: Style.font_family
|
||||
font.weight: Style.fontWidth_400
|
||||
font.pixelSize: 14
|
||||
lineHeight: 20
|
||||
font.weight: Style.fontWeight_400
|
||||
font.pixelSize: Style.body_font_size
|
||||
lineHeight: Style.body_line_height
|
||||
lineHeightMode: Text.FixedHeight
|
||||
font.letterSpacing: 0.2
|
||||
font.letterSpacing: Style.body_letter_spacing
|
||||
}
|
||||
}
|
||||
|
||||
@ -23,7 +23,7 @@ import QtQuick.Templates 2.12 as T
|
||||
|
||||
Item {
|
||||
id: root
|
||||
property var colorScheme: parent.colorScheme ? parent.colorScheme : Style.currentStyle
|
||||
property ColorScheme colorScheme
|
||||
|
||||
property alias background: control.background
|
||||
property alias bottomInset: control.bottomInset
|
||||
@ -104,61 +104,53 @@ Item {
|
||||
|
||||
radius: 4
|
||||
visible: true
|
||||
color: colorScheme.background_norm
|
||||
color: root.colorScheme.background_norm
|
||||
border.color: {
|
||||
if (!control.enabled) {
|
||||
return colorScheme.field_disabled
|
||||
return root.colorScheme.field_disabled
|
||||
}
|
||||
|
||||
if (control.activeFocus) {
|
||||
return colorScheme.interaction_norm
|
||||
return root.colorScheme.interaction_norm
|
||||
}
|
||||
|
||||
if (root.error) {
|
||||
return colorScheme.signal_danger
|
||||
return root.colorScheme.signal_danger
|
||||
}
|
||||
|
||||
if (control.hovered) {
|
||||
return colorScheme.field_hover
|
||||
return root.colorScheme.field_hover
|
||||
}
|
||||
|
||||
return colorScheme.field_norm
|
||||
return root.colorScheme.field_norm
|
||||
}
|
||||
border.width: 1
|
||||
}
|
||||
|
||||
Label {
|
||||
colorScheme: root.colorScheme
|
||||
id: label
|
||||
|
||||
anchors.top: root.top
|
||||
anchors.left: root.left
|
||||
anchors.bottomMargin: 4
|
||||
|
||||
color: root.enabled ? colorScheme.text_norm : colorScheme.text_disabled
|
||||
color: root.enabled ? root.colorScheme.text_norm : root.colorScheme.text_disabled
|
||||
|
||||
font.family: Style.font_family
|
||||
font.weight: Style.fontWidth_600
|
||||
font.pixelSize: 14
|
||||
lineHeight: 20
|
||||
lineHeightMode: Text.FixedHeight
|
||||
font.letterSpacing: 0.2
|
||||
type: Label.LabelType.Body_semibold
|
||||
}
|
||||
|
||||
Label {
|
||||
colorScheme: root.colorScheme
|
||||
id: hint
|
||||
|
||||
anchors.right: root.right
|
||||
anchors.bottom: controlView.top
|
||||
anchors.bottomMargin: 5
|
||||
|
||||
color: root.enabled ? colorScheme.text_weak : colorScheme.text_disabled
|
||||
color: root.enabled ? root.colorScheme.text_weak : root.colorScheme.text_disabled
|
||||
|
||||
font.family: Style.font_family
|
||||
font.weight: Style.fontWidth_400
|
||||
font.pixelSize: 12
|
||||
lineHeight: 16
|
||||
lineHeightMode: Text.FixedHeight
|
||||
font.letterSpacing: 0.4
|
||||
type: Label.LabelType.Caption
|
||||
}
|
||||
|
||||
ColorImage {
|
||||
@ -168,10 +160,11 @@ Item {
|
||||
anchors.top: assistiveText.top
|
||||
anchors.bottom: assistiveText.bottom
|
||||
source: "../icons/ic-exclamation-circle-filled.svg"
|
||||
color: colorScheme.signal_danger
|
||||
color: root.colorScheme.signal_danger
|
||||
}
|
||||
|
||||
Label {
|
||||
colorScheme: root.colorScheme
|
||||
id: assistiveText
|
||||
|
||||
anchors.left: root.error ? errorIcon.right : parent.left
|
||||
@ -181,22 +174,17 @@ Item {
|
||||
|
||||
color: {
|
||||
if (!root.enabled) {
|
||||
return colorScheme.text_disabled
|
||||
return root.colorScheme.text_disabled
|
||||
}
|
||||
|
||||
if (root.error) {
|
||||
return colorScheme.signal_danger
|
||||
return root.colorScheme.signal_danger
|
||||
}
|
||||
|
||||
return colorScheme.text_weak
|
||||
return root.colorScheme.text_weak
|
||||
}
|
||||
|
||||
font.family: Style.font_family
|
||||
font.weight: root.error ? Style.fontWidth_600 : Style.fontWidth_400
|
||||
font.pixelSize: 12
|
||||
lineHeight: 16
|
||||
lineHeightMode: Text.FixedHeight
|
||||
font.letterSpacing: 0.4
|
||||
type: root.error ? Label.LabelType.Caption_semibold : Label.LabelType.Caption
|
||||
}
|
||||
|
||||
ScrollView {
|
||||
@ -222,8 +210,8 @@ Item {
|
||||
padding: 8
|
||||
leftPadding: 12
|
||||
|
||||
color: control.enabled ? colorScheme.text_norm : colorScheme.text_disabled
|
||||
placeholderTextColor: control.enabled ? colorScheme.text_hint : 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
|
||||
@ -231,7 +219,7 @@ Item {
|
||||
cursorDelegate: Rectangle {
|
||||
id: cursor
|
||||
width: 1
|
||||
color: colorScheme.interaction_norm
|
||||
color: root.colorScheme.interaction_norm
|
||||
visible: control.activeFocus && !control.readOnly && control.selectionStart === control.selectionEnd
|
||||
|
||||
Connections {
|
||||
|
||||
@ -21,10 +21,11 @@ import QtQuick.Controls 2.12
|
||||
import QtQuick.Controls.impl 2.12
|
||||
import QtQuick.Templates 2.12 as T
|
||||
import QtQuick.Layouts 1.12
|
||||
import "."
|
||||
|
||||
Item {
|
||||
id: root
|
||||
property var colorScheme: parent.colorScheme ? parent.colorScheme : Style.currentStyle
|
||||
property ColorScheme colorScheme
|
||||
|
||||
property alias background: control.background
|
||||
property alias bottomInset: control.bottomInset
|
||||
@ -91,7 +92,7 @@ Item {
|
||||
property alias hint: hint.text
|
||||
property alias assistiveText: assistiveText.text
|
||||
|
||||
property var echoMode: TextInput.Normal
|
||||
property int echoMode: TextInput.Normal
|
||||
|
||||
property bool error: false
|
||||
|
||||
@ -117,7 +118,7 @@ Item {
|
||||
function selectAll() { control.selectAll() }
|
||||
function selectWord() { control.selectWord() }
|
||||
function undo() { control.undo() }
|
||||
function forceActiveFocus() {control.forceActiveFocus()}
|
||||
function forceActiveFocus() { control.forceActiveFocus() }
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
@ -127,22 +128,22 @@ Item {
|
||||
Layout.fillWidth: true
|
||||
spacing: 0
|
||||
|
||||
ProtonLabel {
|
||||
Label {
|
||||
colorScheme: root.colorScheme
|
||||
id: label
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
color: root.enabled ? colorScheme.text_norm : colorScheme.text_disabled
|
||||
font.weight: Style.fontWidth_600
|
||||
state: "body"
|
||||
type: Label.LabelType.Body_semibold
|
||||
}
|
||||
|
||||
ProtonLabel {
|
||||
Label {
|
||||
colorScheme: root.colorScheme
|
||||
id: hint
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
color: root.enabled ? colorScheme.text_weak : colorScheme.text_disabled
|
||||
color: root.enabled ? root.colorScheme.text_weak : root.colorScheme.text_disabled
|
||||
horizontalAlignment: Text.AlignRight
|
||||
state: "caption"
|
||||
type: Label.LabelType.Caption
|
||||
}
|
||||
}
|
||||
|
||||
@ -157,25 +158,25 @@ Item {
|
||||
|
||||
radius: 4
|
||||
visible: true
|
||||
color: colorScheme.background_norm
|
||||
color: root.colorScheme.background_norm
|
||||
border.color: {
|
||||
if (!control.enabled) {
|
||||
return colorScheme.field_disabled
|
||||
return root.colorScheme.field_disabled
|
||||
}
|
||||
|
||||
if (control.activeFocus) {
|
||||
return colorScheme.interaction_norm
|
||||
return root.colorScheme.interaction_norm
|
||||
}
|
||||
|
||||
if (root.error) {
|
||||
return colorScheme.signal_danger
|
||||
return root.colorScheme.signal_danger
|
||||
}
|
||||
|
||||
if (control.hovered) {
|
||||
return colorScheme.field_hover
|
||||
return root.colorScheme.field_hover
|
||||
}
|
||||
|
||||
return colorScheme.field_norm
|
||||
return root.colorScheme.field_norm
|
||||
}
|
||||
border.width: 1
|
||||
|
||||
@ -193,25 +194,27 @@ Item {
|
||||
Layout.fillWidth: true
|
||||
|
||||
implicitWidth: implicitBackgroundWidth + leftInset + rightInset
|
||||
|| Math.max(contentWidth, placeholder.implicitWidth) + leftPadding + rightPadding
|
||||
|| Math.max(contentWidth, placeholder.implicitWidth) + leftPadding + rightPadding
|
||||
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
|
||||
contentHeight + topPadding + bottomPadding,
|
||||
placeholder.implicitHeight + topPadding + bottomPadding)
|
||||
contentHeight + topPadding + bottomPadding,
|
||||
placeholder.implicitHeight + topPadding + bottomPadding)
|
||||
|
||||
padding: 8
|
||||
leftPadding: 12
|
||||
|
||||
color: control.enabled ? colorScheme.text_norm : colorScheme.text_disabled
|
||||
color: control.enabled ? root.colorScheme.text_norm : root.colorScheme.text_disabled
|
||||
|
||||
selectionColor: control.palette.highlight
|
||||
selectedTextColor: control.palette.highlightedText
|
||||
placeholderTextColor: control.enabled ? colorScheme.text_hint : colorScheme.text_disabled
|
||||
placeholderTextColor: control.enabled ? root.colorScheme.text_hint : root.colorScheme.text_disabled
|
||||
verticalAlignment: TextInput.AlignVCenter
|
||||
|
||||
echoMode: eyeButton.checked ? TextInput.Normal : root.echoMode
|
||||
|
||||
cursorDelegate: Rectangle {
|
||||
id: cursor
|
||||
width: 1
|
||||
color: colorScheme.interaction_norm
|
||||
color: root.colorScheme.interaction_norm
|
||||
visible: control.activeFocus && !control.readOnly && control.selectionStart === control.selectionEnd
|
||||
|
||||
Connections {
|
||||
@ -268,22 +271,16 @@ Item {
|
||||
}
|
||||
|
||||
Button {
|
||||
colorScheme: root.colorScheme
|
||||
id: eyeButton
|
||||
|
||||
Layout.fillHeight: true
|
||||
|
||||
visible: root.echoMode === TextInput.Password
|
||||
icon.source: control.echoMode == TextInput.Password ? "../icons/ic-eye.svg" : "../icons/ic-eye-slash.svg"
|
||||
icon.color: control.color
|
||||
background: Rectangle{color: "#00000000"}
|
||||
onClicked: {
|
||||
if (control.echoMode == TextInput.Password) {
|
||||
control.echoMode = TextInput.Normal
|
||||
} else {
|
||||
control.echoMode = TextInput.Password
|
||||
}
|
||||
}
|
||||
Component.onCompleted: control.echoMode = root.echoMode
|
||||
background: Item { }
|
||||
checkable: true
|
||||
icon.source: checked ? "../icons/ic-eye-slash.svg" : "../icons/ic-eye.svg"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -292,17 +289,16 @@ Item {
|
||||
Layout.fillWidth: true
|
||||
spacing: 0
|
||||
|
||||
// FIXME: maybe somewhere in the future there will be an Icon component capable of setting color to the icon
|
||||
// but before that moment we need to use IconLabel
|
||||
IconLabel {
|
||||
ColorImage {
|
||||
id: errorIcon
|
||||
|
||||
visible: root.error && (assistiveText.text.length > 0)
|
||||
icon.source: "../icons/ic-exclamation-circle-filled.svg"
|
||||
icon.color: colorScheme.signal_danger
|
||||
source: "../icons/ic-exclamation-circle-filled.svg"
|
||||
color: root.colorScheme.signal_danger
|
||||
}
|
||||
|
||||
ProtonLabel {
|
||||
Label {
|
||||
colorScheme: root.colorScheme
|
||||
id: assistiveText
|
||||
|
||||
Layout.fillHeight: true
|
||||
@ -311,18 +307,17 @@ Item {
|
||||
|
||||
color: {
|
||||
if (!root.enabled) {
|
||||
return colorScheme.text_disabled
|
||||
return root.colorScheme.text_disabled
|
||||
}
|
||||
|
||||
if (root.error) {
|
||||
return colorScheme.signal_danger
|
||||
return root.colorScheme.signal_danger
|
||||
}
|
||||
|
||||
return colorScheme.text_weak
|
||||
return root.colorScheme.text_weak
|
||||
}
|
||||
|
||||
font.weight: root.error ? Style.fontWidth_600 : Style.fontWidth_400
|
||||
state: "caption"
|
||||
type: root.error ? Label.LabelType.Caption_semibold : Label.LabelType.Caption
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,13 +1,36 @@
|
||||
# Copyright (c) 2021 Proton Technologies AG
|
||||
#
|
||||
# This file is part of ProtonMail Bridge.
|
||||
#
|
||||
# ProtonMail 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.
|
||||
#
|
||||
# ProtonMail 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 ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
module QQtQuick.Controls.Proton
|
||||
depends QtQuick.Controls 2.12
|
||||
|
||||
singleton ProtonStyle 4.0 Style.qml
|
||||
Banner 4.0 Banner.qml
|
||||
ColorScheme 4.0 ColorScheme.qml
|
||||
|
||||
ApplicationWindow 4.0 ApplicationWindow.qml
|
||||
Button 4.0 Button.qml
|
||||
CheckBox 4.0 CheckBox.qml
|
||||
ProtonLabel 4.0 ProtonLabel.qml
|
||||
RoundedRectangle 4.0 RoundedRectangle.qml
|
||||
Dialog 4.0 Dialog.qml
|
||||
Label 4.0 Label.qml
|
||||
Menu 4.0 Menu.qml
|
||||
MenuItem 4.0 MenuItem.qml
|
||||
Popup 4.0 Popup.qml
|
||||
RadioButton 4.0 RadioButton.qml
|
||||
RoundedRectangle 4.0 RoundedRectangle.qml
|
||||
Switch 4.0 Switch.qml
|
||||
TextArea 4.0 TextArea.qml
|
||||
TextField 4.0 TextField.qml
|
||||
|
||||
@ -18,92 +18,107 @@
|
||||
|
||||
import QtQuick 2.13
|
||||
import QtQuick.Layouts 1.12
|
||||
import QtQuick.Controls 2.12
|
||||
import QtQuick.Controls.impl 2.12
|
||||
|
||||
import Proton 4.0
|
||||
|
||||
RowLayout {
|
||||
Item {
|
||||
id:root
|
||||
|
||||
property var colorScheme
|
||||
property var window
|
||||
property ColorScheme colorScheme
|
||||
property var backend
|
||||
|
||||
property var user: { "username": "janedoe@protonmail.com" }
|
||||
property var user
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillHeight: true
|
||||
Layout.leftMargin: 80
|
||||
Layout.rightMargin: 80
|
||||
Layout.topMargin: 30
|
||||
Layout.bottomMargin: 70
|
||||
signal dismissed()
|
||||
|
||||
ProtonLabel {
|
||||
text: qsTr("Set up email client")
|
||||
font.weight: ProtonStyle.fontWidth_700
|
||||
state: "heading"
|
||||
}
|
||||
implicitHeight: children[0].implicitHeight
|
||||
implicitWidth: children[0].implicitWidth
|
||||
|
||||
ProtonLabel {
|
||||
text: user.username
|
||||
color: root.colorScheme.text_weak
|
||||
state: "lead"
|
||||
}
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
spacing: 0
|
||||
|
||||
ProtonLabel {
|
||||
Layout.topMargin: 32
|
||||
text: qsTr("Choose an email client")
|
||||
font.weight: ProtonStyle.fontWidth_600
|
||||
state: "body"
|
||||
}
|
||||
ColumnLayout {
|
||||
Layout.fillHeight: true
|
||||
Layout.leftMargin: 80
|
||||
Layout.rightMargin: 80
|
||||
Layout.topMargin: 30
|
||||
Layout.bottomMargin: 70
|
||||
spacing: 0
|
||||
|
||||
ListModel {
|
||||
id: clients
|
||||
ListElement{name : "Apple Mail" ; iconSource : "./icons/ic-apple-mail.svg" }
|
||||
ListElement{name : "Microsoft Outlook" ; iconSource : "./icons/ic-microsoft-outlook.svg" }
|
||||
ListElement{name : "Mozilla Thunderbird" ; iconSource : "./icons/ic-mozilla-thunderbird.svg" }
|
||||
ListElement{name : "Other" ; iconSource : "./icons/ic-other-mail-clients.svg" }
|
||||
}
|
||||
Label {
|
||||
colorScheme: root.colorScheme
|
||||
text: qsTr("Set up email client")
|
||||
type: Label.LabelType.Heading
|
||||
}
|
||||
|
||||
Label {
|
||||
colorScheme: root.colorScheme
|
||||
text: user ? user.username : ""
|
||||
color: root.colorScheme.text_weak
|
||||
type: Label.LabelType.Lead
|
||||
}
|
||||
|
||||
Label {
|
||||
colorScheme: root.colorScheme
|
||||
Layout.topMargin: 32
|
||||
text: qsTr("Choose an email client")
|
||||
type: Label.LabelType.Body_semibold
|
||||
}
|
||||
|
||||
ListModel {
|
||||
id: clients
|
||||
ListElement{name : "Apple Mail" ; iconSource : "./icons/ic-apple-mail.svg" }
|
||||
ListElement{name : "Microsoft Outlook" ; iconSource : "./icons/ic-microsoft-outlook.svg" }
|
||||
ListElement{name : "Mozilla Thunderbird" ; iconSource : "./icons/ic-mozilla-thunderbird.svg" }
|
||||
ListElement{name : "Other" ; iconSource : "./icons/ic-other-mail-clients.svg" }
|
||||
}
|
||||
|
||||
|
||||
Repeater {
|
||||
model: clients
|
||||
Repeater {
|
||||
model: clients
|
||||
|
||||
ColumnLayout {
|
||||
RowLayout {
|
||||
Layout.topMargin: 12
|
||||
Layout.bottomMargin: 12
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
ColumnLayout {
|
||||
RowLayout {
|
||||
Layout.topMargin: 12
|
||||
Layout.bottomMargin: 12
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
|
||||
IconLabel {
|
||||
icon.source: model.iconSource
|
||||
icon.height: 36
|
||||
ColorImage {
|
||||
source: model.iconSource
|
||||
height: 36
|
||||
}
|
||||
|
||||
Label {
|
||||
colorScheme: root.colorScheme
|
||||
Layout.leftMargin: 12
|
||||
text: model.name
|
||||
type: Label.LabelType.Body
|
||||
}
|
||||
}
|
||||
|
||||
ProtonLabel {
|
||||
Layout.leftMargin: 12
|
||||
text: model.name
|
||||
state: "body"
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 1
|
||||
color: root.colorScheme.border_weak
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 1
|
||||
color: root.colorScheme.border_weak
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item { Layout.fillHeight: true }
|
||||
Item { Layout.fillHeight: true }
|
||||
|
||||
Button {
|
||||
text: qsTr("Set up later")
|
||||
flat: true
|
||||
Button {
|
||||
colorScheme: root.colorScheme
|
||||
text: qsTr("Set up later")
|
||||
flat: true
|
||||
|
||||
onClicked: {
|
||||
root.window.showSetup = false
|
||||
root.reset()
|
||||
onClicked: {
|
||||
user.setupGuideSeen = true
|
||||
root.dismissed()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -25,7 +25,7 @@ import Proton 4.0
|
||||
|
||||
Item {
|
||||
id: root
|
||||
property var colorScheme: parent.colorScheme
|
||||
property ColorScheme colorScheme
|
||||
|
||||
function abort() {
|
||||
root.loginAbort(usernameTextField.text)
|
||||
@ -173,20 +173,22 @@ Item {
|
||||
|
||||
spacing: 0
|
||||
|
||||
ProtonLabel {
|
||||
Label {
|
||||
colorScheme: root.colorScheme
|
||||
text: qsTr("Sign in")
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.topMargin: 16
|
||||
font.weight: ProtonStyle.fontWidth_700
|
||||
type: Label.LabelType.Title
|
||||
}
|
||||
|
||||
ProtonLabel {
|
||||
Label {
|
||||
colorScheme: root.colorScheme
|
||||
id: subTitle
|
||||
text: qsTr("Enter your Proton Account details.")
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.topMargin: 8
|
||||
color: root.colorScheme.text_weak
|
||||
state: "body"
|
||||
type: Label.LabelType.Body
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
@ -201,17 +203,18 @@ Item {
|
||||
source: "./icons/ic-exclamation-circle-filled.svg"
|
||||
}
|
||||
|
||||
ProtonLabel {
|
||||
Label {
|
||||
colorScheme: root.colorScheme
|
||||
id: errorLabel
|
||||
Layout.leftMargin: 4
|
||||
color: root.colorScheme.signal_danger
|
||||
|
||||
font.weight: root.error ? ProtonStyle.fontWidth_600 : ProtonStyle.fontWidth_400
|
||||
state: "caption"
|
||||
type: root.error ? Label.LabelType.Caption_semibold : Label.LabelType.Caption
|
||||
}
|
||||
}
|
||||
|
||||
TextField {
|
||||
colorScheme: root.colorScheme
|
||||
id: usernameTextField
|
||||
label: qsTr("Username or email")
|
||||
|
||||
@ -236,6 +239,7 @@ Item {
|
||||
}
|
||||
|
||||
TextField {
|
||||
colorScheme: root.colorScheme
|
||||
id: passwordTextField
|
||||
label: qsTr("Password")
|
||||
|
||||
@ -259,6 +263,7 @@ Item {
|
||||
}
|
||||
|
||||
Button {
|
||||
colorScheme: root.colorScheme
|
||||
id: signInButton
|
||||
text: qsTr("Sign in")
|
||||
|
||||
@ -308,12 +313,13 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
ProtonLabel {
|
||||
Label {
|
||||
colorScheme: root.colorScheme
|
||||
textFormat: Text.StyledText
|
||||
text: putLink("https://protonmail.com/upgrade", qsTr("Create or upgrade your account"))
|
||||
text: link("https://protonmail.com/upgrade", qsTr("Create or upgrade your account"))
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.topMargin: 24
|
||||
state: "body"
|
||||
type: Label.LabelType.Body
|
||||
|
||||
onLinkActivated: {
|
||||
Qt.openUrlExternally(link)
|
||||
@ -335,16 +341,16 @@ Item {
|
||||
|
||||
spacing: 0
|
||||
|
||||
ProtonLabel {
|
||||
Label {
|
||||
colorScheme: root.colorScheme
|
||||
text: qsTr("Two-factor authentication")
|
||||
Layout.topMargin: 16
|
||||
Layout.alignment: Qt.AlignCenter
|
||||
font.weight: ProtonStyle.fontWidth_700
|
||||
|
||||
|
||||
type: Label.LabelType.Heading
|
||||
}
|
||||
|
||||
TextField {
|
||||
colorScheme: root.colorScheme
|
||||
id: twoFactorPasswordTextField
|
||||
label: qsTr("Two-factor authentication code")
|
||||
|
||||
@ -360,6 +366,7 @@ Item {
|
||||
}
|
||||
|
||||
Button {
|
||||
colorScheme: root.colorScheme
|
||||
id: twoFAButton
|
||||
text: loading ? qsTr("Authenticating") : qsTr("Authenticate")
|
||||
|
||||
@ -410,19 +417,16 @@ Item {
|
||||
|
||||
spacing: 0
|
||||
|
||||
ProtonLabel {
|
||||
Label {
|
||||
colorScheme: root.colorScheme
|
||||
text: qsTr("Unlock your mailbox")
|
||||
Layout.topMargin: 16
|
||||
Layout.alignment: Qt.AlignCenter
|
||||
font.weight: ProtonStyle.fontWidth_700
|
||||
type: Label.LabelType.Heading
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
TextField {
|
||||
colorScheme: root.colorScheme
|
||||
id: secondPasswordTextField
|
||||
label: qsTr("Mailbox password")
|
||||
|
||||
@ -439,6 +443,7 @@ Item {
|
||||
}
|
||||
|
||||
Button {
|
||||
colorScheme: root.colorScheme
|
||||
id: secondPasswordButton
|
||||
text: loading ? qsTr("Unlocking") : qsTr("Unlock")
|
||||
|
||||
|
||||
@ -22,20 +22,165 @@ import QtQuick.Controls 2.12
|
||||
import QtQuick.Controls.impl 2.12
|
||||
|
||||
import Proton 4.0
|
||||
import Notifications 1.0
|
||||
|
||||
RowLayout {
|
||||
id: layout
|
||||
spacing: 8
|
||||
Item {
|
||||
id: root
|
||||
|
||||
ColorImage {
|
||||
id: image
|
||||
source: "./icons/ic-connected.svg"
|
||||
color: ProtonStyle.currentStyle.signal_success
|
||||
property var backend
|
||||
property var notifications
|
||||
property ColorScheme colorScheme
|
||||
|
||||
property int notificationWhitelist: NotificationFilter.FilterConsts.All
|
||||
property int notificationBlacklist: NotificationFilter.FilterConsts.None
|
||||
|
||||
readonly property Notification activeNotification: notificationFilter.topmost
|
||||
|
||||
implicitHeight: children[0].implicitHeight + children[0].anchors.topMargin + children[0].anchors.bottomMargin
|
||||
implicitWidth: children[0].implicitWidth + children[0].anchors.leftMargin + children[0].anchors.rightMargin
|
||||
|
||||
NotificationFilter {
|
||||
id: notificationFilter
|
||||
|
||||
source: root.notifications.all
|
||||
whitelist: root.notificationWhitelist
|
||||
blacklist: root.notificationBlacklist
|
||||
|
||||
onTopmostChanged: {
|
||||
if (!topmost) {
|
||||
image.source = "./icons/ic-connected.svg"
|
||||
image.color = root.colorScheme.signal_success
|
||||
label.text = qsTr("Connected")
|
||||
label.color = root.colorScheme.signal_success
|
||||
return;
|
||||
}
|
||||
|
||||
image.source = topmost.icon
|
||||
label.text = topmost.text
|
||||
|
||||
switch (topmost.type) {
|
||||
case Notification.NotificationType.Danger:
|
||||
image.color = root.colorScheme.signal_danger
|
||||
label.color = root.colorScheme.signal_danger
|
||||
break;
|
||||
case Notification.NotificationType.Warning:
|
||||
image.color = root.colorScheme.signal_warning
|
||||
label.color = root.colorScheme.signal_warning
|
||||
break;
|
||||
case Notification.NotificationType.Success:
|
||||
image.color = root.colorScheme.signal_success
|
||||
label.color = root.colorScheme.signal_success
|
||||
break;
|
||||
case Notification.NotificationType.Info:
|
||||
image.color = root.colorScheme.signal_info
|
||||
label.color = root.colorScheme.signal_info
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
id: label
|
||||
text: "Connected"
|
||||
color: ProtonStyle.currentStyle.signal_success
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
spacing: 8
|
||||
anchors.margins: 12
|
||||
|
||||
ColorImage {
|
||||
id: image
|
||||
}
|
||||
|
||||
Label {
|
||||
colorScheme: root.colorScheme
|
||||
id: label
|
||||
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
|
||||
wrapMode: Text.WordWrap
|
||||
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
}
|
||||
|
||||
state: "Connected"
|
||||
states: [
|
||||
State {
|
||||
name: "Connected"
|
||||
PropertyChanges {
|
||||
target: image
|
||||
source: "./icons/ic-connected.svg"
|
||||
color: ProtonStyle.currentStyle.signal_success
|
||||
}
|
||||
PropertyChanges {
|
||||
target: label
|
||||
text: qsTr("Connected")
|
||||
color: ProtonStyle.currentStyle.signal_success
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "No connection"
|
||||
PropertyChanges {
|
||||
target: image
|
||||
source: "./icons/ic-no-connection.svg"
|
||||
color: ProtonStyle.currentStyle.signal_danger
|
||||
}
|
||||
PropertyChanges {
|
||||
target: label
|
||||
text: qsTr("No connection")
|
||||
color: ProtonStyle.currentStyle.signal_danger
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "Outdated"
|
||||
PropertyChanges {
|
||||
target: image
|
||||
source: "./icons/ic-exclamation-circle-filled.svg"
|
||||
color: ProtonStyle.currentStyle.signal_danger
|
||||
}
|
||||
PropertyChanges {
|
||||
target: label
|
||||
text: qsTr("Bridge is outdated")
|
||||
color: ProtonStyle.currentStyle.signal_danger
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "Account changed"
|
||||
PropertyChanges {
|
||||
target: image
|
||||
source: "./icons/ic-exclamation-circle-filled.svg"
|
||||
color: ProtonStyle.currentStyle.signal_danger
|
||||
}
|
||||
PropertyChanges {
|
||||
target: label
|
||||
text: qsTr("The address list for your account has changed")
|
||||
color: ProtonStyle.currentStyle.signal_danger
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "Auto update failed"
|
||||
PropertyChanges {
|
||||
target: image
|
||||
source: "./icons/ic-info-circle-filled.svg"
|
||||
color: ProtonStyle.currentStyle.signal_info
|
||||
}
|
||||
PropertyChanges {
|
||||
target: label
|
||||
text: qsTr("Bridge couldn’t update automatically")
|
||||
color: ProtonStyle.currentStyle.signal_info
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "Update ready"
|
||||
PropertyChanges {
|
||||
target: image
|
||||
source: "./icons/ic-info-circle-filled.svg"
|
||||
color: ProtonStyle.currentStyle.signal_info
|
||||
}
|
||||
PropertyChanges {
|
||||
target: label
|
||||
text: qsTr("Bridge update is ready")
|
||||
color: ProtonStyle.currentStyle.signal_info
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
300
internal/frontend/qml/StatusWindow.qml
Normal file
300
internal/frontend/qml/StatusWindow.qml
Normal file
@ -0,0 +1,300 @@
|
||||
// Copyright (c) 2021 Proton Technologies AG
|
||||
//
|
||||
// This file is part of ProtonMail Bridge.
|
||||
//
|
||||
// ProtonMail 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.
|
||||
//
|
||||
// ProtonMail 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 ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import QtQml 2.12
|
||||
import QtQuick 2.13
|
||||
import QtQuick.Window 2.13
|
||||
import QtQuick.Layouts 1.12
|
||||
import QtQuick.Controls 2.13
|
||||
|
||||
import Proton 4.0
|
||||
import ProtonBackend 1.0
|
||||
import Notifications 1.0
|
||||
|
||||
// Because of https://bugreports.qt.io/browse/QTBUG-69777 and other bugs alike it is impossible
|
||||
// to use Window with flags: Qt.Popup here since it won't close by it's own on click outside.
|
||||
PopupWindow {
|
||||
id: root
|
||||
title: "ProtonMail Bridge"
|
||||
|
||||
height: contentLayout.implicitHeight
|
||||
width: contentLayout.implicitWidth
|
||||
|
||||
minimumHeight: 201
|
||||
minimumWidth: 448
|
||||
|
||||
property ColorScheme colorScheme: ProtonStyle.currentStyle
|
||||
|
||||
property var backend
|
||||
property var notifications
|
||||
|
||||
color: "transparent"
|
||||
|
||||
signal showMainWindow()
|
||||
signal showHelp()
|
||||
signal showSettings()
|
||||
signal quit()
|
||||
|
||||
ColumnLayout {
|
||||
id: contentLayout
|
||||
|
||||
anchors.fill: parent
|
||||
spacing: 0
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 0
|
||||
|
||||
Item {
|
||||
implicitHeight: 12
|
||||
Layout.fillWidth: true
|
||||
clip: true
|
||||
Rectangle {
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
height: parent.height * 2
|
||||
radius: 10
|
||||
|
||||
color: {
|
||||
if (!statusItem.activeNotification) {
|
||||
return root.colorScheme.signal_success
|
||||
}
|
||||
|
||||
switch (statusItem.activeNotification.type) {
|
||||
case Notification.NotificationType.Danger:
|
||||
return root.colorScheme.signal_danger
|
||||
case Notification.NotificationType.Warning:
|
||||
return root.colorScheme.signal_warning
|
||||
case Notification.NotificationType.Success:
|
||||
return root.colorScheme.signal_success
|
||||
case Notification.NotificationType.Info:
|
||||
return root.colorScheme.signal_info
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.fillWidth: 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
|
||||
|
||||
color: colorScheme.background_norm
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
|
||||
anchors.topMargin: 8
|
||||
anchors.bottomMargin: 8
|
||||
anchors.leftMargin: 24
|
||||
anchors.rightMargin: 24
|
||||
|
||||
spacing: 8
|
||||
|
||||
Status {
|
||||
id: statusItem
|
||||
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
|
||||
colorScheme: root.colorScheme
|
||||
backend: root.backend
|
||||
notifications: root.notifications
|
||||
|
||||
notificationWhitelist: Notifications.Group.Connection | Notifications.Group.Update | Notifications.Group.Configuration
|
||||
}
|
||||
|
||||
Button {
|
||||
colorScheme: root.colorScheme
|
||||
secondary: true
|
||||
|
||||
visible: (statusItem.activeNotification && statusItem.activeNotification.action) ? true : false
|
||||
action: statusItem.activeNotification && statusItem.activeNotification.action.length > 0 ? statusItem.activeNotification.action[0] : null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
height: 1
|
||||
color: root.colorScheme.background_norm
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: 24
|
||||
anchors.rightMargin: 24
|
||||
color: root.colorScheme.border_norm
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
Layout.maximumHeight: accountListView.count ?
|
||||
accountListView.contentHeight / accountListView.count * 3 + accountListView.anchors.topMargin + accountListView.anchors.bottomMargin :
|
||||
Number.POSITIVE_INFINITY
|
||||
|
||||
color: root.colorScheme.background_norm
|
||||
clip: true
|
||||
|
||||
implicitHeight: children[0].contentHeight + children[0].anchors.topMargin + children[0].anchors.bottomMargin
|
||||
implicitWidth: children[0].contentWidth + children[0].anchors.leftMargin + children[0].anchors.rightMargin
|
||||
|
||||
ListView {
|
||||
id: accountListView
|
||||
|
||||
model: root.backend.users
|
||||
anchors.fill: parent
|
||||
|
||||
anchors.topMargin: 8
|
||||
anchors.bottomMargin: 8
|
||||
anchors.leftMargin: 24
|
||||
anchors.rightMargin: 24
|
||||
|
||||
interactive: contentHeight > parent.height
|
||||
snapMode: ListView.SnapToItem
|
||||
|
||||
delegate: Item {
|
||||
width: ListView.view.width
|
||||
|
||||
implicitHeight: children[0].implicitHeight
|
||||
implicitWidth: children[0].implicitWidth
|
||||
|
||||
RowLayout {
|
||||
spacing: 0
|
||||
anchors.fill: parent
|
||||
|
||||
AccountDelegate {
|
||||
Layout.fillWidth: true
|
||||
|
||||
Layout.margins: 12
|
||||
|
||||
user: modelData
|
||||
colorScheme: root.colorScheme
|
||||
|
||||
}
|
||||
Button {
|
||||
Layout.margins: 12
|
||||
colorScheme: root.colorScheme
|
||||
visible: true
|
||||
text: "test"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
|
||||
implicitHeight: children[1].implicitHeight + children[1].anchors.topMargin + children[1].anchors.bottomMargin
|
||||
implicitWidth: children[1].implicitWidth + children[1].anchors.leftMargin + children[1].anchors.rightMargin
|
||||
|
||||
// background:
|
||||
clip: true
|
||||
Rectangle {
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
height: parent.height * 2
|
||||
radius: 10
|
||||
|
||||
color: root.colorScheme.background_weak
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 8
|
||||
spacing: 0
|
||||
|
||||
Button {
|
||||
colorScheme: root.colorScheme
|
||||
secondary: true
|
||||
text: qsTr("Open ProtonBridge")
|
||||
|
||||
borderless: true
|
||||
labelType: Label.LabelType.Caption_semibold
|
||||
|
||||
onClicked: {
|
||||
root.showMainWindow()
|
||||
root.visible = false
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Button {
|
||||
colorScheme: root.colorScheme
|
||||
secondary: true
|
||||
icon.source: "./icons/ic-three-dots-vertical.svg"
|
||||
borderless: true
|
||||
checkable: true
|
||||
|
||||
onClicked: {
|
||||
menu.open()
|
||||
}
|
||||
|
||||
Menu {
|
||||
id: menu
|
||||
colorScheme: root.colorScheme
|
||||
modal: true
|
||||
|
||||
y: 0 - height
|
||||
|
||||
MenuItem {
|
||||
colorScheme: root.colorScheme
|
||||
text: qsTr("Help")
|
||||
onClicked: {
|
||||
root.showHelp()
|
||||
root.visible = false
|
||||
}
|
||||
}
|
||||
MenuItem {
|
||||
colorScheme: root.colorScheme
|
||||
text: qsTr("Settings")
|
||||
onClicked: {
|
||||
root.showSettings()
|
||||
root.visible = false
|
||||
}
|
||||
}
|
||||
MenuItem {
|
||||
colorScheme: root.colorScheme
|
||||
text: qsTr("Quit ProtonBridge")
|
||||
onClicked: {
|
||||
root.quit()
|
||||
root.visible = false
|
||||
}
|
||||
}
|
||||
|
||||
onClosed: {
|
||||
parent.checked = false
|
||||
}
|
||||
onOpened: {
|
||||
parent.checked = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
294
internal/frontend/qml/WelcomeGuide.qml
Normal file
294
internal/frontend/qml/WelcomeGuide.qml
Normal file
@ -0,0 +1,294 @@
|
||||
// Copyright (c) 2021 Proton Technologies AG
|
||||
//
|
||||
// This file is part of ProtonMail Bridge.
|
||||
//
|
||||
// ProtonMail 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.
|
||||
//
|
||||
// ProtonMail 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 ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import QtQml 2.12
|
||||
import QtQuick 2.13
|
||||
import QtQuick.Layouts 1.12
|
||||
import QtQuick.Controls 2.12
|
||||
|
||||
import Proton 4.0
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property ColorScheme colorScheme
|
||||
|
||||
property var backend
|
||||
property var window
|
||||
|
||||
signal login(string username, string password)
|
||||
signal login2FA(string username, string code)
|
||||
signal login2Password(string username, string password)
|
||||
signal loginAbort(string username)
|
||||
|
||||
implicitHeight: children[0].implicitHeight
|
||||
implicitWidth: children[0].implicitWidth
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
spacing: 0
|
||||
|
||||
Rectangle {
|
||||
color: root.colorScheme.background_norm
|
||||
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
|
||||
implicitHeight: children[0].implicitHeight
|
||||
implicitWidth: children[0].implicitWidth
|
||||
|
||||
visible: signInItem.currentIndex == 0
|
||||
|
||||
GridLayout {
|
||||
anchors.fill: parent
|
||||
|
||||
columnSpacing: 0
|
||||
rowSpacing: 0
|
||||
|
||||
columns: 3
|
||||
|
||||
// top margin
|
||||
Item {
|
||||
Layout.columnSpan: 3
|
||||
Layout.fillWidth: true
|
||||
|
||||
// Using binding component here instead of direct binding to avoid binding loop during construction of element
|
||||
Binding on Layout.preferredHeight {
|
||||
value: (parent.height - welcomeContentItem.height) / 4
|
||||
}
|
||||
}
|
||||
|
||||
// left margin
|
||||
Item {
|
||||
Layout.minimumWidth: 48
|
||||
Layout.maximumWidth: 80
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: welcomeContentItem.height
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: welcomeContentItem
|
||||
Layout.fillWidth: true
|
||||
spacing: 0
|
||||
|
||||
Image {
|
||||
source: "icons/img-welcome.svg"
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.topMargin: 16
|
||||
}
|
||||
|
||||
Label {
|
||||
colorScheme: root.colorScheme
|
||||
text: qsTr("Welcome to\nProtonMail Bridge")
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
|
||||
type: Label.LabelType.Heading
|
||||
}
|
||||
|
||||
Label {
|
||||
colorScheme: root.colorScheme
|
||||
id: longTextLabel
|
||||
text: qsTr("Now you can securely access and manage ProtonMail messages in your favorite email client. Bridge runs in the background and encrypts and decrypts your messages seamlessly.")
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
Layout.preferredWidth: 320
|
||||
|
||||
wrapMode: Text.WordWrap
|
||||
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
|
||||
type: Label.LabelType.Body
|
||||
}
|
||||
}
|
||||
|
||||
// Right margin
|
||||
Item {
|
||||
Layout.minimumWidth: 48
|
||||
Layout.maximumWidth: 80
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: welcomeContentItem.height
|
||||
}
|
||||
|
||||
// bottom margin
|
||||
Item {
|
||||
Layout.columnSpan: 3
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
implicitHeight: children[0].implicitHeight + children[0].anchors.bottomMargin + children[0].anchors.topMargin
|
||||
implicitWidth: children[0].implicitWidth
|
||||
|
||||
Image {
|
||||
id: logoImage
|
||||
source: "icons/product_logos.svg"
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.topMargin: 48
|
||||
anchors.bottomMargin: 48
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
color: (signInItem.currentIndex == 0) ? root.colorScheme.background_weak : root.colorScheme.background_norm
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
|
||||
implicitHeight: children[0].implicitHeight
|
||||
implicitWidth: children[0].implicitWidth
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
spacing: 0
|
||||
Item {
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredWidth: signInItem.currentIndex == 0 ? 0 : parent.width / 4
|
||||
|
||||
implicitHeight: children[0].implicitHeight + children[0].anchors.topMargin + children[0].anchors.bottomMargin
|
||||
implicitWidth: children[0].implicitWidth + children[0].anchors.leftMargin + children[0].anchors.rightMargin
|
||||
|
||||
Button {
|
||||
colorScheme: root.colorScheme
|
||||
anchors.left: parent.left
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
anchors.leftMargin: 80
|
||||
anchors.rightMargin: 80
|
||||
anchors.topMargin: 80
|
||||
anchors.bottomMargin: 80
|
||||
|
||||
visible: signInItem.currentIndex != 0
|
||||
|
||||
secondary: true
|
||||
text: qsTr("Back")
|
||||
|
||||
onClicked: {
|
||||
signInItem.abort()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GridLayout {
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
|
||||
columnSpacing: 0
|
||||
rowSpacing: 0
|
||||
|
||||
columns: 3
|
||||
|
||||
// top margin
|
||||
Item {
|
||||
Layout.columnSpan: 3
|
||||
Layout.fillWidth: true
|
||||
|
||||
// Using binding component here instead of direct binding to avoid binding loop during construction of element
|
||||
Binding on Layout.preferredHeight {
|
||||
value: (parent.height - signInItem.height) / 4
|
||||
}
|
||||
}
|
||||
|
||||
// left margin
|
||||
Item {
|
||||
Layout.minimumWidth: 48
|
||||
Layout.maximumWidth: 80
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: signInItem.height
|
||||
}
|
||||
|
||||
|
||||
SignIn {
|
||||
id: signInItem
|
||||
colorScheme: root.colorScheme
|
||||
|
||||
Layout.preferredWidth: 320
|
||||
Layout.fillWidth: true
|
||||
|
||||
onLogin: {
|
||||
root.login(username, password)
|
||||
}
|
||||
onLogin2FA: {
|
||||
root.login2FA(username, code)
|
||||
}
|
||||
onLogin2Password: {
|
||||
root.login2Password(username, password)
|
||||
}
|
||||
onLoginAbort: {
|
||||
root.loginAbort(username)
|
||||
}
|
||||
|
||||
user: (backend.users.count === 1 && backend.users.get(0).loggedIn === false) ? backend.users.get(0) : undefined
|
||||
backend: root.backend
|
||||
window: root.window
|
||||
}
|
||||
|
||||
// Right margin
|
||||
Item {
|
||||
Layout.minimumWidth: 48
|
||||
Layout.maximumWidth: 80
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: signInItem.height
|
||||
}
|
||||
|
||||
// bottom margin
|
||||
Item {
|
||||
Layout.columnSpan: 3
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillHeight: true
|
||||
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,297 +0,0 @@
|
||||
// Copyright (c) 2021 Proton Technologies AG
|
||||
//
|
||||
// This file is part of ProtonMail Bridge.
|
||||
//
|
||||
// ProtonMail 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.
|
||||
//
|
||||
// ProtonMail 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 ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import QtQml 2.12
|
||||
import QtQuick 2.13
|
||||
import QtQuick.Layouts 1.12
|
||||
import QtQuick.Controls 2.12
|
||||
|
||||
import Proton 4.0
|
||||
|
||||
RowLayout {
|
||||
id: root
|
||||
|
||||
property var colorScheme: parent.colorScheme
|
||||
|
||||
property var backend
|
||||
property var window
|
||||
|
||||
signal login(string username, string password)
|
||||
signal login2FA(string username, string code)
|
||||
signal login2Password(string username, string password)
|
||||
signal loginAbort(string username)
|
||||
|
||||
spacing: 0
|
||||
|
||||
Rectangle {
|
||||
color: root.colorScheme.background_norm
|
||||
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
|
||||
implicitHeight: children[0].implicitHeight
|
||||
implicitWidth: children[0].implicitWidth
|
||||
|
||||
visible: signInItem.currentIndex == 0
|
||||
|
||||
GridLayout {
|
||||
anchors.fill: parent
|
||||
|
||||
columnSpacing: 0
|
||||
rowSpacing: 0
|
||||
|
||||
columns: 3
|
||||
|
||||
// top margin
|
||||
Item {
|
||||
Layout.columnSpan: 3
|
||||
Layout.fillWidth: true
|
||||
|
||||
// Using binding component here instead of direct binding to avoid binding loop during construction of element
|
||||
Binding on Layout.preferredHeight {
|
||||
value: (parent.height - welcomeContentItem.height) / 4
|
||||
}
|
||||
}
|
||||
|
||||
// left margin
|
||||
Item {
|
||||
Layout.minimumWidth: 48
|
||||
Layout.maximumWidth: 80
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: welcomeContentItem.height
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: welcomeContentItem
|
||||
Layout.fillWidth: true
|
||||
spacing: 0
|
||||
|
||||
Image {
|
||||
source: "icons/img-welcome.svg"
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.topMargin: 16
|
||||
}
|
||||
|
||||
Label {
|
||||
text: qsTr("Welcome to\nProtonMail Bridge")
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
|
||||
color: root.colorScheme.text_norm
|
||||
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
|
||||
font.family: ProtonStyle.font_family
|
||||
font.weight: ProtonStyle.fontWidth_700
|
||||
font.pixelSize: 28
|
||||
lineHeight: 36
|
||||
lineHeightMode: Text.FixedHeight
|
||||
}
|
||||
|
||||
Label {
|
||||
id: longTextLabel
|
||||
text: qsTr("Now you can securely access and manage ProtonMail messages in your favorite email client. Bridge runs in the background and encrypts and decrypts your messages seamlessly.")
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
Layout.preferredWidth: 320
|
||||
|
||||
color: root.colorScheme.text_norm
|
||||
wrapMode: Text.WordWrap
|
||||
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
|
||||
font.family: ProtonStyle.font_family
|
||||
font.weight: ProtonStyle.fontWidth_400
|
||||
font.pixelSize: 14
|
||||
lineHeight: 20
|
||||
lineHeightMode: Text.FixedHeight
|
||||
font.letterSpacing: 0.2
|
||||
}
|
||||
}
|
||||
|
||||
// Right margin
|
||||
Item {
|
||||
Layout.minimumWidth: 48
|
||||
Layout.maximumWidth: 80
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: welcomeContentItem.height
|
||||
}
|
||||
|
||||
// bottom margin
|
||||
Item {
|
||||
Layout.columnSpan: 3
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
implicitHeight: children[0].implicitHeight + children[0].anchors.bottomMargin + children[0].anchors.topMargin
|
||||
implicitWidth: children[0].implicitWidth
|
||||
|
||||
Image {
|
||||
id: logoImage
|
||||
source: "icons/product_logos.svg"
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.topMargin: 48
|
||||
anchors.bottomMargin: 48
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
color: (signInItem.currentIndex == 0) ? root.colorScheme.background_weak : root.colorScheme.background_norm
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
|
||||
implicitHeight: children[0].implicitHeight
|
||||
implicitWidth: children[0].implicitWidth
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
spacing: 0
|
||||
Item {
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredWidth: signInItem.currentIndex == 0 ? 0 : parent.width / 4
|
||||
|
||||
implicitHeight: children[0].implicitHeight + children[0].anchors.topMargin + children[0].anchors.bottomMargin
|
||||
implicitWidth: children[0].implicitWidth + children[0].anchors.leftMargin + children[0].anchors.rightMargin
|
||||
|
||||
Button {
|
||||
anchors.left: parent.left
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
anchors.leftMargin: 80
|
||||
anchors.rightMargin: 80
|
||||
anchors.topMargin: 80
|
||||
anchors.bottomMargin: 80
|
||||
|
||||
visible: signInItem.currentIndex != 0
|
||||
|
||||
secondary: true
|
||||
text: qsTr("Back")
|
||||
|
||||
onClicked: {
|
||||
signInItem.abort()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GridLayout {
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
|
||||
columnSpacing: 0
|
||||
rowSpacing: 0
|
||||
|
||||
columns: 3
|
||||
|
||||
// top margin
|
||||
Item {
|
||||
Layout.columnSpan: 3
|
||||
Layout.fillWidth: true
|
||||
|
||||
// Using binding component here instead of direct binding to avoid binding loop during construction of element
|
||||
Binding on Layout.preferredHeight {
|
||||
value: (parent.height - signInItem.height) / 4
|
||||
}
|
||||
}
|
||||
|
||||
// left margin
|
||||
Item {
|
||||
Layout.minimumWidth: 48
|
||||
Layout.maximumWidth: 80
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: signInItem.height
|
||||
}
|
||||
|
||||
|
||||
SignIn {
|
||||
id: signInItem
|
||||
colorScheme: root.colorScheme
|
||||
|
||||
Layout.preferredWidth: 320
|
||||
Layout.fillWidth: true
|
||||
|
||||
onLogin: {
|
||||
root.login(username, password)
|
||||
}
|
||||
onLogin2FA: {
|
||||
root.login2FA(username, code)
|
||||
}
|
||||
onLogin2Password: {
|
||||
root.login2Password(username, password)
|
||||
}
|
||||
onLoginAbort: {
|
||||
root.loginAbort(username)
|
||||
}
|
||||
|
||||
user: (backend.users.count === 1 && backend.users.get(0).loggedIn === false) ? backend.users.get(0) : undefined
|
||||
backend: root.backend
|
||||
window: root.window
|
||||
}
|
||||
|
||||
// Right margin
|
||||
Item {
|
||||
Layout.minimumWidth: 48
|
||||
Layout.maximumWidth: 80
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: signInItem.height
|
||||
}
|
||||
|
||||
// bottom margin
|
||||
Item {
|
||||
Layout.columnSpan: 3
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillHeight: true
|
||||
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
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
11
internal/frontend/qml/icons/ic-alert.svg
Normal file
11
internal/frontend/qml/icons/ic-alert.svg
Normal file
@ -0,0 +1,11 @@
|
||||
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M25.9729 8.06214C28.6889 3.51461 35.2762 3.51461 37.9923 8.06214L62.464 49.0356C65.2508 53.7015 61.8891 59.625 56.4543 59.625H7.51078C2.07602 59.625 -1.28569 53.7015 1.50106 49.0356L25.9729 8.06214Z" fill="url(#paint0_linear)"/>
|
||||
<path d="M29.0775 24.3888C29.0864 22.7908 30.3843 21.5 31.9824 21.5C33.5805 21.5 34.8785 22.7908 34.8874 24.3888L34.9658 38.5C34.9749 40.1542 33.6366 41.5 31.9824 41.5C30.3283 41.5 28.9899 40.1542 28.9991 38.5L29.0775 24.3888Z" fill="white"/>
|
||||
<path d="M34.9824 47.5C34.9824 49.1569 33.6393 50.5 31.9824 50.5C30.3256 50.5 28.9824 49.1569 28.9824 47.5C28.9824 45.8431 30.3256 44.5 31.9824 44.5C33.6393 44.5 34.9824 45.8431 34.9824 47.5Z" fill="white"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear" x1="31.9827" y1="4.18421" x2="31.9827" y2="84.2072" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FF7171"/>
|
||||
<stop offset="1" stop-color="#E63757"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1013 B |
3
internal/frontend/qml/icons/ic-no-connection.svg
Normal file
3
internal/frontend/qml/icons/ic-no-connection.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M1 13L2 14L6.60951 9.39049L9.42104 6.57896L10.7224 5.27756L12.8435 3.15654L15 1L14 0L11.4338 2.56619C10.3363 2.19528 9.1768 2 8 2C4.99776 2 2.10791 3.271 0.0743047 5.48656C-0.0273754 5.59626 -0.0246996 5.76752 0.0823321 5.87455L1.67175 7.4637C1.78146 7.57341 1.95806 7.57341 2.06777 7.4637C2.0865 7.44524 2.09988 7.42383 2.11058 7.39975C3.61705 5.74584 5.76036 4.7962 8 4.7962C8.37819 4.7962 8.75363 4.82329 9.12372 4.87628L7.56734 6.43266C5.90389 6.54608 4.35089 7.31026 3.24238 8.56633L3.2354 8.57044C3.22366 8.5773 3.21069 8.58487 3.19957 8.59577C3.08986 8.70547 3.08986 8.88208 3.19957 8.99152L4.10403 9.89597L1 13ZM7.99994 9.11969C7.9593 9.11969 7.91877 9.12034 7.87836 9.12164L10.1916 6.80845C11.1689 7.16857 12.052 7.76806 12.7548 8.56633C12.7709 8.57436 12.7869 8.58265 12.8003 8.59577C12.91 8.70547 12.91 8.88208 12.8003 8.99152L11.2724 10.5194C11.2457 10.5462 11.2162 10.5676 11.1815 10.5809C11.1467 10.5943 11.1119 10.6023 11.0744 10.6023H11.0584C11.0519 10.6023 11.0455 10.5995 11.0391 10.5966C11.0366 10.5954 11.0341 10.5943 11.0317 10.5934C11.0299 10.5927 11.0281 10.5921 11.0263 10.5916C11.0022 10.5892 10.9781 10.5836 10.9567 10.5729C10.9436 10.5685 10.9341 10.5606 10.9238 10.5521L10.9238 10.5521L10.9166 10.5462L10.9095 10.5421L10.9045 10.5395C10.8944 10.5342 10.884 10.5288 10.8764 10.5191C10.8734 10.5176 10.872 10.5153 10.8705 10.5125C10.8693 10.5105 10.868 10.5082 10.8657 10.5057C10.8644 10.5033 10.8625 10.502 10.8606 10.5007C10.8585 10.4994 10.8564 10.498 10.855 10.495C10.1647 9.62006 9.12377 9.11969 7.99994 9.11969ZM13.8894 7.39975C13.1883 6.63018 12.3493 6.01303 11.4269 5.5731L13.4895 3.51054C14.3862 4.0424 15.2091 4.70584 15.9257 5.48656C16.0274 5.59653 16.0247 5.76752 15.9177 5.87455L14.3282 7.4637C14.2747 7.51989 14.2025 7.54692 14.1302 7.54692C14.058 7.54692 13.9857 7.51989 13.9322 7.4637C13.9135 7.44524 13.9001 7.42383 13.8894 7.39975ZM7.99998 10.2626C7.17317 10.2626 6.50154 10.9371 6.50154 11.7637C6.50154 12.5903 7.17317 13.2621 7.99998 13.2621C8.8268 13.2621 9.49843 12.5905 9.49843 11.7637C9.49843 10.9369 8.8268 10.2626 7.99998 10.2626Z" fill="#D42F34"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
10
internal/frontend/qml/icons/ic-success.svg
Normal file
10
internal/frontend/qml/icons/ic-success.svg
Normal file
@ -0,0 +1,10 @@
|
||||
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="32" cy="32" r="30" fill="url(#paint0_linear)"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M45.6652 22.2252C46.6453 23.1977 46.6514 24.7806 45.6789 25.7607L29.8028 41.7606C29.3334 42.2337 28.6946 42.4997 28.0282 42.4997C27.3618 42.4998 26.723 42.2337 26.2536 41.7606L19.1297 34.5735C18.1572 33.5935 18.1633 32.0106 19.1434 31.038C20.1235 30.0655 21.7064 30.0717 22.6789 31.0517L28.0282 36.4504L42.1297 22.2389C43.1022 21.2588 44.6851 21.2527 45.6652 22.2252Z" fill="white"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear" x1="32" y1="2" x2="32" y2="62" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#5BCB6A"/>
|
||||
<stop offset="1" stop-color="#1CB78F"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 796 B |
9
internal/frontend/qml/icons/ic-three-dots-vertical.svg
Normal file
9
internal/frontend/qml/icons/ic-three-dots-vertical.svg
Normal file
@ -0,0 +1,9 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="ic-three-dots-vertical">
|
||||
<g id="icon">
|
||||
<path d="M7 2C7 1.44772 7.44772 1 8 1C8.55228 1 9 1.44772 9 2C9 2.55228 8.55228 3 8 3C7.44772 3 7 2.55228 7 2Z" fill="#17181C"/>
|
||||
<path d="M7 14C7 13.4477 7.44772 13 8 13C8.55228 13 9 13.4477 9 14C9 14.5523 8.55228 15 8 15C7.44772 15 7 14.5523 7 14Z" fill="#17181C"/>
|
||||
<path d="M8 7C7.44772 7 7 7.44772 7 8C7 8.55228 7.44772 9 8 9C8.55228 9 9 8.55228 9 8C9 7.44772 8.55228 7 8 7Z" fill="#17181C"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 555 B |
@ -23,10 +23,11 @@ import QtQuick.Controls 2.12
|
||||
import Proton 4.0
|
||||
|
||||
RowLayout {
|
||||
property var colorScheme: parent.colorScheme
|
||||
property ColorScheme colorScheme
|
||||
|
||||
// Primary buttons
|
||||
ButtonsColumn {
|
||||
colorScheme: root.colorScheme
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
@ -35,6 +36,7 @@ RowLayout {
|
||||
|
||||
// Secondary buttons
|
||||
ButtonsColumn {
|
||||
colorScheme: root.colorScheme
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
@ -44,6 +46,7 @@ RowLayout {
|
||||
|
||||
// Secondary icons
|
||||
ButtonsColumn {
|
||||
colorScheme: root.colorScheme
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
@ -58,6 +61,7 @@ RowLayout {
|
||||
|
||||
// Icons
|
||||
ButtonsColumn {
|
||||
colorScheme: root.colorScheme
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
|
||||
@ -22,7 +22,7 @@ import Proton 4.0
|
||||
|
||||
ColumnLayout {
|
||||
id: root
|
||||
property var colorScheme: parent.colorScheme
|
||||
property ColorScheme colorScheme
|
||||
|
||||
property string textNormal: "Button"
|
||||
property string iconNormal: ""
|
||||
@ -33,6 +33,7 @@ ColumnLayout {
|
||||
property bool secondary: false
|
||||
|
||||
Button {
|
||||
colorScheme: root.colorScheme
|
||||
Layout.fillWidth: true
|
||||
|
||||
Layout.minimumHeight: implicitHeight
|
||||
@ -45,6 +46,7 @@ ColumnLayout {
|
||||
|
||||
|
||||
Button {
|
||||
colorScheme: root.colorScheme
|
||||
Layout.fillWidth: true
|
||||
|
||||
Layout.minimumHeight: implicitHeight
|
||||
@ -58,6 +60,7 @@ ColumnLayout {
|
||||
}
|
||||
|
||||
Button {
|
||||
colorScheme: root.colorScheme
|
||||
Layout.fillWidth: true
|
||||
|
||||
Layout.minimumHeight: implicitHeight
|
||||
|
||||
@ -23,11 +23,11 @@ import QtQuick.Controls 2.12
|
||||
import Proton 4.0
|
||||
|
||||
RowLayout {
|
||||
property var colorScheme: parent.colorScheme
|
||||
id: root
|
||||
property ColorScheme colorScheme
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
property var colorScheme: parent.colorScheme
|
||||
|
||||
spacing: parent.spacing
|
||||
|
||||
@ -61,7 +61,6 @@ RowLayout {
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
property var colorScheme: parent.colorScheme
|
||||
|
||||
spacing: parent.spacing
|
||||
|
||||
|
||||
@ -23,37 +23,42 @@ import QtQuick.Controls 2.12
|
||||
import Proton 4.0
|
||||
|
||||
RowLayout {
|
||||
property var colorScheme: parent.colorScheme
|
||||
property ColorScheme colorScheme
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
property var colorScheme: parent.colorScheme
|
||||
|
||||
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
|
||||
}
|
||||
@ -61,38 +66,43 @@ RowLayout {
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
property var colorScheme: parent.colorScheme
|
||||
|
||||
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
|
||||
|
||||
@ -23,11 +23,10 @@ import QtQuick.Controls 2.12
|
||||
import Proton 4.0
|
||||
|
||||
RowLayout {
|
||||
property var colorScheme: parent.colorScheme
|
||||
property ColorScheme colorScheme
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
property var colorScheme: parent.colorScheme
|
||||
|
||||
spacing: parent.spacing
|
||||
|
||||
@ -62,7 +61,6 @@ RowLayout {
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
property var colorScheme: parent.colorScheme
|
||||
|
||||
spacing: parent.spacing
|
||||
|
||||
|
||||
@ -22,42 +22,47 @@ import QtQuick.Controls 2.12
|
||||
import Proton 4.0
|
||||
|
||||
Rectangle {
|
||||
property var colorScheme
|
||||
property ColorScheme colorScheme
|
||||
color: colorScheme.background_norm
|
||||
clip: true
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
property var colorScheme: parent.colorScheme
|
||||
|
||||
spacing: 5
|
||||
|
||||
Buttons {
|
||||
colorScheme: root.colorScheme
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: 20
|
||||
}
|
||||
|
||||
TextFields {
|
||||
colorScheme: root.colorScheme
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: 20
|
||||
}
|
||||
|
||||
TextAreas {
|
||||
colorScheme: root.colorScheme
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: 20
|
||||
}
|
||||
|
||||
CheckBoxes {
|
||||
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
|
||||
}
|
||||
|
||||
@ -23,15 +23,16 @@ import QtQuick.Controls 2.12
|
||||
import Proton 4.0
|
||||
|
||||
RowLayout {
|
||||
property var colorScheme: parent.colorScheme
|
||||
id: root
|
||||
property ColorScheme colorScheme
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
property var colorScheme: parent.colorScheme
|
||||
|
||||
spacing: parent.spacing
|
||||
|
||||
TextArea {
|
||||
colorScheme: root.colorScheme
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 100
|
||||
|
||||
@ -42,6 +43,7 @@ RowLayout {
|
||||
}
|
||||
|
||||
TextArea {
|
||||
colorScheme: root.colorScheme
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 100
|
||||
|
||||
@ -54,6 +56,7 @@ RowLayout {
|
||||
|
||||
|
||||
TextArea {
|
||||
colorScheme: root.colorScheme
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 100
|
||||
|
||||
@ -68,6 +71,7 @@ RowLayout {
|
||||
|
||||
|
||||
TextArea {
|
||||
colorScheme: root.colorScheme
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 100
|
||||
|
||||
|
||||
@ -23,16 +23,16 @@ import QtQuick.Controls 2.12
|
||||
import Proton 4.0
|
||||
|
||||
RowLayout {
|
||||
property var colorScheme: parent.colorScheme
|
||||
property ColorScheme colorScheme
|
||||
|
||||
// Norm
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
property var colorScheme: parent.colorScheme
|
||||
|
||||
spacing: parent.spacing
|
||||
|
||||
TextField {
|
||||
colorScheme: root.colorScheme
|
||||
Layout.fillWidth: true
|
||||
|
||||
placeholderText: "Placeholder"
|
||||
@ -42,6 +42,7 @@ RowLayout {
|
||||
}
|
||||
|
||||
TextField {
|
||||
colorScheme: root.colorScheme
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: "Value"
|
||||
@ -53,6 +54,7 @@ RowLayout {
|
||||
|
||||
|
||||
TextField {
|
||||
colorScheme: root.colorScheme
|
||||
Layout.fillWidth: true
|
||||
error: true
|
||||
|
||||
@ -65,6 +67,7 @@ RowLayout {
|
||||
|
||||
|
||||
TextField {
|
||||
colorScheme: root.colorScheme
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: "Value"
|
||||
@ -80,11 +83,11 @@ RowLayout {
|
||||
// Masked
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
property var colorScheme: parent.colorScheme
|
||||
|
||||
spacing: parent.spacing
|
||||
|
||||
TextField {
|
||||
colorScheme: root.colorScheme
|
||||
Layout.fillWidth: true
|
||||
echoMode: TextInput.Password
|
||||
placeholderText: "Password"
|
||||
@ -94,6 +97,7 @@ RowLayout {
|
||||
}
|
||||
|
||||
TextField {
|
||||
colorScheme: root.colorScheme
|
||||
Layout.fillWidth: true
|
||||
text: "Password"
|
||||
|
||||
@ -105,6 +109,7 @@ RowLayout {
|
||||
}
|
||||
|
||||
TextField {
|
||||
colorScheme: root.colorScheme
|
||||
Layout.fillWidth: true
|
||||
text: "Password"
|
||||
error: true
|
||||
@ -117,6 +122,7 @@ RowLayout {
|
||||
}
|
||||
|
||||
TextField {
|
||||
colorScheme: root.colorScheme
|
||||
Layout.fillWidth: true
|
||||
text: "Password"
|
||||
enabled: false
|
||||
@ -132,50 +138,31 @@ RowLayout {
|
||||
// Varia
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
property var colorScheme: parent.colorScheme
|
||||
|
||||
spacing: parent.spacing
|
||||
|
||||
TextField {
|
||||
colorScheme: root.colorScheme
|
||||
Layout.fillWidth: true
|
||||
|
||||
placeholderText: "Placeholder"
|
||||
label: "Label"
|
||||
hint: "Hint"
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
border.color: "red"
|
||||
border.width: 1
|
||||
z: parent.z - 1
|
||||
}
|
||||
}
|
||||
|
||||
TextField {
|
||||
colorScheme: root.colorScheme
|
||||
Layout.fillWidth: true
|
||||
|
||||
placeholderText: "Placeholder"
|
||||
assistiveText: "Assistive text"
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
border.color: "red"
|
||||
border.width: 1
|
||||
z: parent.z - 1
|
||||
}
|
||||
}
|
||||
|
||||
TextField {
|
||||
colorScheme: root.colorScheme
|
||||
Layout.fillWidth: true
|
||||
|
||||
placeholderText: "Placeholder"
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
border.color: "red"
|
||||
border.width: 1
|
||||
z: parent.z - 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user