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:
Alexander Bilyak
2021-08-04 14:00:31 +02:00
committed by Jakub
parent 6bd0739013
commit 0a9748a15d
51 changed files with 3277 additions and 1056 deletions

View 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"
}
}

View File

@ -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"}}
]
}

View File

@ -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
}

View File

@ -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
}
}

View 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"
}
}

View 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>`
}
}

View 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
}
}

View 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
}
}

View 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"
}
}

View File

@ -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 } }
]
}

View File

@ -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
}
}

View File

@ -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
}

View File

@ -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
}
}

View File

@ -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 {

View File

@ -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
}
}
}

View File

@ -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