proton-bridge/internal/frontend/qml/BridgeUI/MainWindow.qml

396 lines
12 KiB
QML

// 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/>.
// This is main window
import QtQuick 2.8
import QtQuick.Window 2.2
import QtQuick.Controls 2.1
import QtQuick.Layouts 1.3
import BridgeUI 1.0
import ProtonUI 1.0
// Main Window
Window {
id: root
property alias tabbar : tabbar
property alias viewContent : viewContent
property alias viewAccount : viewAccount
property alias dialogAddUser : dialogAddUser
property alias dialogChangePort : dialogChangePort
property alias dialogCredits : dialogCredits
property alias dialogTlsCert : dialogTlsCert
property alias dialogUpdate : dialogUpdate
property alias dialogFirstStart : dialogFirstStart
property alias dialogGlobal : dialogGlobal
property alias dialogConnectionTroubleshoot : dialogConnectionTroubleshoot
property alias bubbleNote : bubbleNote
property alias addAccountTip : addAccountTip
property alias updateState : infoBar.state
property alias tlsBarState : tlsBar.state
property int heightContent : height-titleBar.height
// main window appeareance
width : Style.main.width
height : Style.main.height
flags : Qt.Window | Qt.FramelessWindowHint
color: go.goos=="windows" ? "black" : "transparent"
title: go.programTitle
minimumWidth: Style.main.width
minimumHeight: Style.main.height
maximumWidth: Style.main.width
property bool isOutdateVersion : root.updateState == "forceUpdate"
property bool activeContent :
!dialogAddUser .visible &&
!dialogChangePort .visible &&
!dialogCredits .visible &&
!dialogTlsCert .visible &&
!dialogUpdate .visible &&
!dialogFirstStart .visible &&
!dialogGlobal .visible &&
!bubbleNote .visible
Accessible.role: Accessible.Grouping
Accessible.description: qsTr("Window %1").arg(title)
Accessible.name: Accessible.description
Component.onCompleted : {
gui.winMain = root
console.log("GraphicsInfo of", titleBar,
"api" , titleBar.GraphicsInfo.api ,
"majorVersion" , titleBar.GraphicsInfo.majorVersion ,
"minorVersion" , titleBar.GraphicsInfo.minorVersion ,
"profile" , titleBar.GraphicsInfo.profile ,
"renderableType" , titleBar.GraphicsInfo.renderableType ,
"shaderCompilationType" , titleBar.GraphicsInfo.shaderCompilationType ,
"shaderSourceType" , titleBar.GraphicsInfo.shaderSourceType ,
"shaderType" , titleBar.GraphicsInfo.shaderType)
tabbar.focusButton()
}
WindowTitleBar {
id: titleBar
window: root
}
Rectangle {
anchors {
top : titleBar.bottom
left : parent.left
right : parent.right
bottom : parent.bottom
}
color: Style.title.background
}
TLSCertPinIssueBar {
id: tlsBar
anchors {
left : parent.left
right : parent.right
top : titleBar.bottom
leftMargin: Style.main.border
rightMargin: Style.main.border
}
enabled : root.activeContent
}
InformationBar {
id: infoBar
anchors {
left : parent.left
right : parent.right
top : tlsBar.bottom
leftMargin: Style.main.border
rightMargin: Style.main.border
}
enabled : root.activeContent
}
TabLabels {
id: tabbar
currentIndex : 0
enabled: root.activeContent
anchors {
top : infoBar.bottom
right : parent.right
left : parent.left
leftMargin: Style.main.border
rightMargin: Style.main.border
}
model: [
{ "title" : qsTr("Accounts" , "title of tab that shows account list" ), "iconText": Style.fa.user_circle_o },
{ "title" : qsTr("Settings" , "title of tab that allows user to change settings" ), "iconText": Style.fa.cog },
{ "title" : qsTr("Help" , "title of tab that shows the help menu" ), "iconText": Style.fa.life_ring }
]
}
// Content of tabs
StackLayout {
id: viewContent
enabled: root.activeContent
// dimensions
anchors {
left : parent.left
right : parent.right
top : tabbar.bottom
bottom : parent.bottom
leftMargin: Style.main.border
rightMargin: Style.main.border
bottomMargin: Style.main.border
}
// attributes
currentIndex : { return root.tabbar.currentIndex}
clip : true
// content
AccountView {
id: viewAccount
onAddAccount: dialogAddUser.show()
model: accountsModel
delegate: AccountDelegate {
row_width: viewContent.width
}
}
SettingsView { id: viewSettings; }
HelpView { id: viewHelp; }
}
// Floating things
// Triangle
Rectangle {
id: tabtriangle
visible: false
property int margin : Style.main.leftMargin+ Style.tabbar.widthButton/2
anchors {
top : tabbar.bottom
left : tabbar.left
leftMargin : tabtriangle.margin - tabtriangle.width/2 + tabbar.currentIndex * tabbar.spacing
}
width: 2*Style.tabbar.heightTriangle
height: Style.tabbar.heightTriangle
color: "transparent"
Canvas {
anchors.fill: parent
onPaint: {
var ctx = getContext("2d")
ctx.fillStyle = Style.tabbar.background
ctx.moveTo(0 , 0)
ctx.lineTo(width/2, height)
ctx.lineTo(width , 0)
ctx.closePath()
ctx.fill()
}
}
}
// Bubble prevent action
Rectangle {
anchors {
left: parent.left
right: parent.right
top: titleBar.bottom
bottom: parent.bottom
}
visible: bubbleNote.visible
color: "#aa222222"
MouseArea {
anchors.fill: parent
hoverEnabled: true
}
}
BubbleNote {
id : bubbleNote
visible : false
Component.onCompleted : {
bubbleNote.place(0)
}
}
BubbleNote {
id:addAccountTip
anchors.topMargin: viewAccount.separatorNoAccount - 2*Style.main.fontSize
text : qsTr("Click here to start", "on first launch, this is displayed above the Add Account button to tell the user what to do first")
state: (go.isFirstStart && viewAccount.numAccounts==0 && root.viewContent.currentIndex==0) ? "Visible" : "Invisible"
bubbleColor: Style.main.textBlue
Component.onCompleted : {
addAccountTip.place(-1)
}
enabled: false
states: [
State {
name: "Visible"
// hack: opacity 100% makes buttons dialog windows quit wrong color
PropertyChanges{target: addAccountTip; opacity: 0.999; visible: true}
},
State {
name: "Invisible"
PropertyChanges{target: addAccountTip; opacity: 0.0; visible: false}
}
]
transitions: [
Transition {
from: "Visible"
to: "Invisible"
SequentialAnimation{
NumberAnimation {
target: addAccountTip
property: "opacity"
duration: 0
easing.type: Easing.InOutQuad
}
NumberAnimation {
target: addAccountTip
property: "visible"
duration: 0
}
}
},
Transition {
from: "Invisible"
to: "Visible"
SequentialAnimation{
NumberAnimation {
target: addAccountTip
property: "visible"
duration: 300
}
NumberAnimation {
target: addAccountTip
property: "opacity"
duration: 500
easing.type: Easing.InOutQuad
}
}
}
]
}
// Dialogs
DialogFirstStart {
id: dialogFirstStart
visible: go.isFirstStart && gui.isFirstWindow && !dialogGlobal.visible
}
// Dialogs
DialogPortChange {
id: dialogChangePort
}
DialogKeychainChange {
id: dialogChangeKeychain
}
DialogConnectionTroubleshoot {
id: dialogConnectionTroubleshoot
}
DialogAddUser {
id: dialogAddUser
onCreateAccount: Qt.openUrlExternally("https://protonmail.com/signup")
}
DialogUpdate {
id: dialogUpdate
forceUpdate: root.isOutdateVersion
}
Dialog {
id: dialogCredits
title: qsTr("Credits", "link to click on to view list of credited libraries")
Credits { }
}
DialogTLSCertInfo {
id: dialogTlsCert
}
DialogYesNo {
id: dialogGlobal
question : ""
answer : ""
z: 100
}
// resize
MouseArea {
property int diff: 0
anchors {
bottom : parent.bottom
left : parent.left
right : parent.right
}
cursorShape: Qt.SizeVerCursor
height: Style.main.fontSize
onPressed: {
var globPos = mapToGlobal(mouse.x, mouse.y)
diff = root.height
diff -= globPos.y
}
onMouseYChanged : {
var globPos = mapToGlobal(mouse.x, mouse.y)
root.height = Math.max(root.minimumHeight, globPos.y + diff)
}
}
function showAndRise(){
go.loadAccounts()
root.show()
root.raise()
if (!root.active) {
root.requestActivate()
}
}
// Toggle window
function toggle() {
go.loadAccounts()
if (root.visible) {
if (!root.active) {
root.raise()
root.requestActivate()
} else {
root.hide()
}
} else {
root.show()
root.raise()
}
}
onClosing: {
close.accepted = false
// NOTE: In order to make an initial accounts load
root.hide()
gui.closeMainWindow()
}
}