mirror of
https://github.com/ProtonMail/proton-bridge.git
synced 2025-12-16 07:06:45 +00:00
GODT-1179 GODT-658: Components and login flows
This commit is contained in:
117
internal/frontend/qml/Proton/Banner.qml
Normal file
117
internal/frontend/qml/Proton/Banner.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.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"}}
|
||||
]
|
||||
}
|
||||
@ -28,49 +28,151 @@ T.Button {
|
||||
readonly property bool primary: !secondary
|
||||
readonly property bool isIcon: control.text === ""
|
||||
|
||||
property bool loading: false
|
||||
|
||||
// TODO: store previous enabled state and restore it?
|
||||
// For now assuming that only enabled buttons could have loading state
|
||||
onLoadingChanged: {
|
||||
if (loading) {
|
||||
enabled = false
|
||||
} else {
|
||||
enabled = true
|
||||
}
|
||||
}
|
||||
|
||||
id: control
|
||||
|
||||
implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
|
||||
implicitContentWidth + leftPadding + rightPadding)
|
||||
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
|
||||
implicitContentHeight + topPadding + bottomPadding)
|
||||
implicitWidth: Math.max(
|
||||
implicitBackgroundWidth + leftInset + rightInset,
|
||||
implicitContentWidth + leftPadding + rightPadding
|
||||
)
|
||||
implicitHeight: Math.max(
|
||||
implicitBackgroundHeight + topInset + bottomInset,
|
||||
implicitContentHeight + topPadding + bottomPadding
|
||||
)
|
||||
|
||||
padding: 8
|
||||
horizontalPadding: 16
|
||||
spacing: 10
|
||||
|
||||
icon.width: 12
|
||||
icon.height: 12
|
||||
icon.color: control.checked || control.highlighted ? control.palette.brightText :
|
||||
control.flat && !control.down ? (control.visualFocus ? control.palette.highlight : control.palette.windowText) : control.palette.buttonText
|
||||
font.family: Style.font_family
|
||||
font.pixelSize: Style.body_font_size
|
||||
font.letterSpacing: Style.body_letter_spacing
|
||||
|
||||
contentItem: IconLabel {
|
||||
spacing: control.spacing
|
||||
mirrored: control.mirrored
|
||||
display: control.display
|
||||
icon.width: 16
|
||||
icon.height: 16
|
||||
icon.color: {
|
||||
if (primary && !isIcon) {
|
||||
return "#FFFFFF"
|
||||
} else {
|
||||
return colorScheme.text_norm
|
||||
}
|
||||
}
|
||||
|
||||
icon: control.icon
|
||||
text: control.text
|
||||
font: control.font
|
||||
color: {
|
||||
if (!secondary) {
|
||||
// Primary colors
|
||||
return "#FFFFFF"
|
||||
} else {
|
||||
// Secondary colors
|
||||
return colorScheme.text_norm
|
||||
contentItem: Item {
|
||||
id: _contentItem
|
||||
|
||||
// Since contentItem is allways resized to maximum available size - we need to "incapsulate" label
|
||||
// and icon within one single item with calculated fixed implicit size
|
||||
|
||||
implicitHeight: labelIcon.implicitHeight
|
||||
implicitWidth: labelIcon.implicitWidth
|
||||
|
||||
Item {
|
||||
id: labelIcon
|
||||
|
||||
anchors.horizontalCenter: _contentItem.horizontalCenter
|
||||
anchors.verticalCenter: _contentItem.verticalCenter
|
||||
|
||||
width: Math.min(implicitWidth, control.availableWidth)
|
||||
height: Math.min(implicitHeight, control.availableHeight)
|
||||
|
||||
implicitWidth: {
|
||||
var textImplicitWidth = control.text !== "" ? label.implicitWidth : 0
|
||||
var iconImplicitWidth = iconImage.source ? iconImage.implicitWidth : 0
|
||||
var spacing = (control.text !== "" && iconImage.source && control.display === AbstractButton.TextBesideIcon) ? control.spacing : 0
|
||||
|
||||
return control.display === AbstractButton.TextBesideIcon ? textImplicitWidth + iconImplicitWidth + spacing : Math.max(textImplicitWidth, iconImplicitWidth)
|
||||
}
|
||||
implicitHeight: {
|
||||
var textImplicitHeight = control.text !== "" ? label.implicitHeight : 0
|
||||
var iconImplicitHeight = iconImage.source ? iconImage.implicitHeight : 0
|
||||
var spacing = (control.text !== "" && iconImage.source && control.display === AbstractButton.TextUnderIcon) ? control.spacing : 0
|
||||
|
||||
return control.display === AbstractButton.TextUnderIcon ? textImplicitHeight + iconImplicitHeight + spacing : Math.max(textImplicitHeight, iconImplicitHeight)
|
||||
}
|
||||
|
||||
Label {
|
||||
id: label
|
||||
anchors.left: labelIcon.left
|
||||
anchors.top: labelIcon.top
|
||||
anchors.bottom: labelIcon.bottom
|
||||
anchors.right: control.loading ? iconImage.left : labelIcon.right
|
||||
anchors.rightMargin: control.loading ? control.spacing : 0
|
||||
|
||||
elide: Text.ElideRight
|
||||
horizontalAlignment: Qt.AlignHCenter
|
||||
verticalAlignment: Qt.AlignVCenter
|
||||
|
||||
text: control.text
|
||||
font: control.font
|
||||
color: {
|
||||
if (primary && !isIcon) {
|
||||
return "#FFFFFF"
|
||||
} else {
|
||||
return colorScheme.text_norm
|
||||
}
|
||||
}
|
||||
opacity: control.enabled || control.loading ? 1.0 : 0.5
|
||||
}
|
||||
|
||||
ColorImage {
|
||||
id: iconImage
|
||||
|
||||
anchors.verticalCenter: labelIcon.verticalCenter
|
||||
anchors.right: labelIcon.right
|
||||
|
||||
width: {
|
||||
// special case for loading since we want icon to be square for rotation animation
|
||||
if (control.loading) {
|
||||
return Math.min(control.icon.width, availableWidth, control.icon.height, availableHeight)
|
||||
}
|
||||
|
||||
return Math.min(control.icon.width, availableWidth)
|
||||
}
|
||||
height: {
|
||||
if (control.loading) {
|
||||
return width
|
||||
}
|
||||
|
||||
Math.min(control.icon.height, availableHeight)
|
||||
}
|
||||
|
||||
color: control.icon.color
|
||||
source: control.loading ? "../icons/Loader_16.svg" : control.icon.source
|
||||
visible: control.loading || control.icon.source
|
||||
|
||||
RotationAnimation {
|
||||
target: iconImage
|
||||
loops: Animation.Infinite
|
||||
duration: 1000
|
||||
from: 0
|
||||
to: 360
|
||||
direction: RotationAnimation.Clockwise
|
||||
running: control.loading
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
implicitWidth: 72
|
||||
implicitWidth: 36
|
||||
implicitHeight: 36
|
||||
radius: 4
|
||||
visible: !control.flat || control.down || control.checked || control.highlighted
|
||||
visible: true
|
||||
color: {
|
||||
if (!isIcon) {
|
||||
if (!secondary) {
|
||||
if (primary) {
|
||||
// Primary colors
|
||||
|
||||
if (control.down) {
|
||||
@ -81,6 +183,10 @@ T.Button {
|
||||
return colorScheme.interaction_norm_hover
|
||||
}
|
||||
|
||||
if (control.loading) {
|
||||
return colorScheme.interaction_norm_hover
|
||||
}
|
||||
|
||||
return colorScheme.interaction_norm
|
||||
} else {
|
||||
// Secondary colors
|
||||
@ -93,10 +199,14 @@ T.Button {
|
||||
return colorScheme.interaction_default_hover
|
||||
}
|
||||
|
||||
if (control.loading) {
|
||||
return colorScheme.interaction_default_hover
|
||||
}
|
||||
|
||||
return colorScheme.interaction_default
|
||||
}
|
||||
} else {
|
||||
if (!secondary) {
|
||||
if (primary) {
|
||||
// Primary icon colors
|
||||
|
||||
if (control.down) {
|
||||
@ -107,6 +217,10 @@ T.Button {
|
||||
return colorScheme.interaction_default_hover
|
||||
}
|
||||
|
||||
if (control.loading) {
|
||||
return colorScheme.interaction_default_hover
|
||||
}
|
||||
|
||||
return colorScheme.interaction_default
|
||||
} else {
|
||||
// Secondary icon colors
|
||||
@ -119,10 +233,18 @@ T.Button {
|
||||
return colorScheme.interaction_default_hover
|
||||
}
|
||||
|
||||
if (control.loading) {
|
||||
return colorScheme.interaction_default_hover
|
||||
}
|
||||
|
||||
return colorScheme.interaction_default
|
||||
}
|
||||
}
|
||||
}
|
||||
opacity: control.enabled ? 1.0 : 0.5
|
||||
|
||||
border.color: colorScheme.border_norm
|
||||
border.width: secondary ? 1 : 0
|
||||
|
||||
opacity: control.enabled || control.loading ? 1.0 : 0.5
|
||||
}
|
||||
}
|
||||
|
||||
132
internal/frontend/qml/Proton/CheckBox.qml
Normal file
132
internal/frontend/qml/Proton/CheckBox.qml
Normal file
@ -0,0 +1,132 @@
|
||||
// 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
|
||||
|
||||
T.CheckBox {
|
||||
property var colorScheme: parent.colorScheme ? parent.colorScheme : Style.currentStyle
|
||||
|
||||
property bool error: false
|
||||
|
||||
id: control
|
||||
|
||||
implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
|
||||
implicitContentWidth + leftPadding + rightPadding)
|
||||
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
|
||||
implicitContentHeight + topPadding + bottomPadding,
|
||||
implicitIndicatorHeight + topPadding + bottomPadding)
|
||||
|
||||
padding: 0
|
||||
spacing: 8
|
||||
|
||||
indicator: Rectangle {
|
||||
implicitWidth: 20
|
||||
implicitHeight: 20
|
||||
radius: 4
|
||||
|
||||
x: text ? (control.mirrored ? control.width - width - control.rightPadding : control.leftPadding) : control.leftPadding + (control.availableWidth - width) / 2
|
||||
y: control.topPadding + (control.availableHeight - height) / 2
|
||||
|
||||
color: {
|
||||
if (!checked) {
|
||||
return colorScheme.background_norm
|
||||
}
|
||||
|
||||
if (!control.enabled) {
|
||||
return colorScheme.field_disabled
|
||||
}
|
||||
|
||||
if (control.error) {
|
||||
return colorScheme.signal_danger
|
||||
}
|
||||
|
||||
if (control.hovered) {
|
||||
return colorScheme.interaction_norm_hover
|
||||
}
|
||||
|
||||
return colorScheme.interaction_norm
|
||||
}
|
||||
|
||||
border.width: control.checked ? 0 : 1
|
||||
border.color: {
|
||||
if (!control.enabled) {
|
||||
return colorScheme.field_disabled
|
||||
}
|
||||
|
||||
if (control.error) {
|
||||
return colorScheme.signal_danger
|
||||
}
|
||||
|
||||
if (control.hovered) {
|
||||
return colorScheme.interaction_norm_hover
|
||||
}
|
||||
|
||||
return colorScheme.field_norm
|
||||
}
|
||||
|
||||
ColorImage {
|
||||
x: (parent.width - width) / 2
|
||||
y: (parent.height - height) / 2
|
||||
|
||||
width: parent.width - 4
|
||||
height: parent.height - 4
|
||||
color: "#FFFFFF"
|
||||
source: "../icons/ic-check.svg"
|
||||
visible: control.checkState === Qt.Checked
|
||||
}
|
||||
|
||||
// TODO: do we need PartiallyChecked state?
|
||||
|
||||
//Rectangle {
|
||||
// x: (parent.width - width) / 2
|
||||
// y: (parent.height - height) / 2
|
||||
// width: 16
|
||||
// height: 3
|
||||
// color: control.palette.text
|
||||
// visible: control.checkState === Qt.PartiallyChecked
|
||||
//}
|
||||
}
|
||||
|
||||
contentItem: CheckLabel {
|
||||
leftPadding: control.indicator && !control.mirrored ? control.indicator.width + control.spacing : 0
|
||||
rightPadding: control.indicator && control.mirrored ? control.indicator.width + control.spacing : 0
|
||||
|
||||
text: control.text
|
||||
|
||||
color: {
|
||||
if (!enabled) {
|
||||
return colorScheme.text_disabled
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return colorScheme.signal_danger
|
||||
}
|
||||
|
||||
return colorScheme.text_norm
|
||||
}
|
||||
|
||||
font.family: Style.font_family
|
||||
font.weight: Style.fontWidth_400
|
||||
font.pixelSize: 14
|
||||
lineHeight: 20
|
||||
lineHeightMode: Text.FixedHeight
|
||||
font.letterSpacing: 0.2
|
||||
}
|
||||
}
|
||||
@ -17,10 +17,10 @@
|
||||
|
||||
import QtQml 2.13
|
||||
|
||||
// https://wiki.qt.io/Qml_Styling
|
||||
// http://imaginativethinking.ca/make-qml-component-singleton/
|
||||
|
||||
QtObject {
|
||||
// should be a pointer to ColorScheme object
|
||||
property var prominent
|
||||
|
||||
// Primary
|
||||
property color primay_norm
|
||||
|
||||
|
||||
43
internal/frontend/qml/Proton/ProtonLabel.qml
Normal file
43
internal/frontend/qml/Proton/ProtonLabel.qml
Normal file
@ -0,0 +1,43 @@
|
||||
// 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 } }
|
||||
]
|
||||
}
|
||||
115
internal/frontend/qml/Proton/RadioButton.qml
Normal file
115
internal/frontend/qml/Proton/RadioButton.qml
Normal file
@ -0,0 +1,115 @@
|
||||
// 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
|
||||
|
||||
T.RadioButton {
|
||||
property var colorScheme: parent.colorScheme ? parent.colorScheme : Style.currentStyle
|
||||
|
||||
property bool error: false
|
||||
|
||||
id: control
|
||||
|
||||
implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
|
||||
implicitContentWidth + leftPadding + rightPadding)
|
||||
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
|
||||
implicitContentHeight + topPadding + bottomPadding,
|
||||
implicitIndicatorHeight + topPadding + bottomPadding)
|
||||
|
||||
padding: 0
|
||||
spacing: 8
|
||||
|
||||
indicator: Rectangle {
|
||||
implicitWidth: 20
|
||||
implicitHeight: 20
|
||||
radius: width / 2
|
||||
|
||||
x: text ? (control.mirrored ? control.width - width - control.rightPadding : control.leftPadding) : control.leftPadding + (control.availableWidth - width) / 2
|
||||
y: control.topPadding + (control.availableHeight - height) / 2
|
||||
|
||||
color: colorScheme.background_norm
|
||||
border.width: 1
|
||||
border.color: {
|
||||
if (!control.enabled) {
|
||||
return colorScheme.field_disabled
|
||||
}
|
||||
|
||||
if (control.error) {
|
||||
return colorScheme.signal_danger
|
||||
}
|
||||
|
||||
if (control.hovered) {
|
||||
return colorScheme.interaction_norm_hover
|
||||
}
|
||||
|
||||
return colorScheme.field_norm
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
x: (parent.width - width) / 2
|
||||
y: (parent.height - height) / 2
|
||||
width: 8
|
||||
height: 8
|
||||
radius: width / 2
|
||||
color: {
|
||||
if (!control.enabled) {
|
||||
return colorScheme.field_disabled
|
||||
}
|
||||
|
||||
if (control.error) {
|
||||
return colorScheme.signal_danger
|
||||
}
|
||||
|
||||
if (control.hovered) {
|
||||
return colorScheme.interaction_norm_hover
|
||||
}
|
||||
|
||||
return colorScheme.interaction_norm
|
||||
}
|
||||
visible: control.checked
|
||||
}
|
||||
}
|
||||
|
||||
contentItem: CheckLabel {
|
||||
leftPadding: control.indicator && !control.mirrored ? control.indicator.width + control.spacing : 0
|
||||
rightPadding: control.indicator && control.mirrored ? control.indicator.width + control.spacing : 0
|
||||
|
||||
text: control.text
|
||||
|
||||
color: {
|
||||
if (!enabled) {
|
||||
return colorScheme.text_disabled
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return colorScheme.signal_danger
|
||||
}
|
||||
|
||||
return colorScheme.text_norm
|
||||
}
|
||||
|
||||
font.family: Style.font_family
|
||||
font.weight: Style.fontWidth_400
|
||||
font.pixelSize: 14
|
||||
lineHeight: 20
|
||||
lineHeightMode: Text.FixedHeight
|
||||
font.letterSpacing: 0.2
|
||||
}
|
||||
}
|
||||
116
internal/frontend/qml/Proton/RoundedRectangle.qml
Normal file
116
internal/frontend/qml/Proton/RoundedRectangle.qml
Normal file
@ -0,0 +1,116 @@
|
||||
// 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.8
|
||||
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
color: Style.transparent
|
||||
|
||||
property color fillColor : Style.currentStyle.background_norm
|
||||
property color strokeColor : Style.currentStyle.background_strong
|
||||
property real strokeWidth : 1
|
||||
|
||||
property real radiusTopLeft : 10
|
||||
property real radiusBottomLeft : 10
|
||||
property real radiusTopRight : 10
|
||||
property real radiusBottomRight : 10
|
||||
|
||||
function paint() {
|
||||
canvas.requestPaint()
|
||||
}
|
||||
|
||||
onFillColorChanged : root.paint()
|
||||
onStrokeColorChanged : root.paint()
|
||||
onStrokeWidthChanged : root.paint()
|
||||
onRadiusTopLeftChanged : root.paint()
|
||||
onRadiusBottomLeftChanged : root.paint()
|
||||
onRadiusTopRightChanged : root.paint()
|
||||
onRadiusBottomRightChanged : root.paint()
|
||||
|
||||
|
||||
Canvas {
|
||||
id: canvas
|
||||
anchors.fill: root
|
||||
|
||||
onPaint: {
|
||||
var ctx = getContext("2d")
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
ctx.fillStyle = root.fillColor
|
||||
ctx.strokeStyle = root.strokeColor
|
||||
ctx.lineWidth = root.strokeWidth
|
||||
var dimensions = {
|
||||
x: ctx.lineWidth,
|
||||
y: ctx.lineWidth,
|
||||
w: canvas.width-2*ctx.lineWidth,
|
||||
h: canvas.height-2*ctx.lineWidth,
|
||||
}
|
||||
var radius = {
|
||||
tl: root.radiusTopLeft,
|
||||
tr: root.radiusTopRight,
|
||||
bl: root.radiusBottomLeft,
|
||||
br: root.radiusBottomRight,
|
||||
}
|
||||
|
||||
root.roundRect(
|
||||
ctx,
|
||||
dimensions,
|
||||
radius, true, true
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// adapted from: https://stackoverflow.com/questions/1255512/how-to-draw-a-rounded-rectangle-on-html-canvas/3368118#3368118
|
||||
function roundRect(ctx, dim, radius, fill, stroke) {
|
||||
if (typeof stroke == 'undefined') {
|
||||
stroke = true;
|
||||
}
|
||||
if (typeof radius === 'undefined') {
|
||||
radius = 5;
|
||||
}
|
||||
if (typeof radius === 'number') {
|
||||
radius = {tl: radius, tr: radius, br: radius, bl: radius};
|
||||
} else {
|
||||
var defaultRadius = {tl: 0, tr: 0, br: 0, bl: 0};
|
||||
for (var side in defaultRadius) {
|
||||
radius[side] = radius[side] || defaultRadius[side];
|
||||
}
|
||||
}
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(dim.x + radius.tl, dim.y);
|
||||
ctx.lineTo(dim.x + dim.w - radius.tr, dim.y);
|
||||
ctx.quadraticCurveTo(dim.x + dim.w, dim.y, dim.x + dim.w, dim.y + radius.tr);
|
||||
ctx.lineTo(dim.x + dim.w, dim.y + dim.h - radius.br);
|
||||
ctx.quadraticCurveTo(dim.x + dim.w, dim.y + dim.h, dim.x + dim.w - radius.br, dim.y + dim.h);
|
||||
ctx.lineTo(dim.x + radius.bl, dim.y + dim.h);
|
||||
ctx.quadraticCurveTo(dim.x, dim.y + dim.h, dim.x, dim.y + dim.h - radius.bl);
|
||||
ctx.lineTo(dim.x, dim.y + radius.tl);
|
||||
ctx.quadraticCurveTo(dim.x, dim.y, dim.x + radius.tl, dim.y);
|
||||
ctx.closePath();
|
||||
if (fill) {
|
||||
ctx.fill();
|
||||
}
|
||||
if (stroke) {
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: root.paint()
|
||||
}
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
|
||||
pragma Singleton
|
||||
import QtQml 2.13
|
||||
import QtQuick 2.12
|
||||
|
||||
import "./"
|
||||
|
||||
@ -24,279 +25,271 @@ import "./"
|
||||
// http://imaginativethinking.ca/make-qml-component-singleton/
|
||||
|
||||
QtObject {
|
||||
|
||||
// TODO: Once we will use Qt >=5.15 this should be refactored with inline components as follows:
|
||||
// https://doc.qt.io/qt-5/qtqml-documents-definetypes.html#inline-components
|
||||
|
||||
//component ColorScheme: QtObject {
|
||||
// property color primay_norm
|
||||
// ...
|
||||
//}
|
||||
// component ColorScheme: QtObject {
|
||||
// property color primay_norm
|
||||
// ...
|
||||
// }
|
||||
// and instead of "var" later on "ColorScheme" should be used (also in each component)
|
||||
|
||||
// and instead of "var" later on "ColorScheme" should be used
|
||||
property var lightStyle: ColorScheme {
|
||||
id: _lightStyle
|
||||
|
||||
property var _lightStyle: ColorScheme {
|
||||
id: lightStyle
|
||||
prominent: prominentStyle
|
||||
|
||||
// Primary
|
||||
primay_norm: "#657EE4"
|
||||
// Primary
|
||||
primay_norm: "#657EE4"
|
||||
|
||||
// Interaction-norm
|
||||
interaction_norm: "#657EE4"
|
||||
interaction_norm_hover: "#5064B6"
|
||||
interaction_norm_active: "#3C4B88"
|
||||
// Interaction-norm
|
||||
interaction_norm: "#657EE4"
|
||||
interaction_norm_hover: "#5064B6"
|
||||
interaction_norm_active: "#3C4B88"
|
||||
|
||||
// Text
|
||||
text_norm: "#262A33"
|
||||
text_weak: "#696F7D"
|
||||
text_hint: "#A4A9B5"
|
||||
text_disabled: "#BABEC7"
|
||||
text_invert: "#FFFFFF"
|
||||
// Text
|
||||
text_norm: "#262A33"
|
||||
text_weak: "#696F7D"
|
||||
text_hint: "#A4A9B5"
|
||||
text_disabled: "#BABEC7"
|
||||
text_invert: "#FFFFFF"
|
||||
|
||||
// Field
|
||||
field_norm: "#BABEC7"
|
||||
field_hover: "#A4A9B5"
|
||||
field_disabled: "#D0D3DA"
|
||||
// Field
|
||||
field_norm: "#BABEC7"
|
||||
field_hover: "#A4A9B5"
|
||||
field_disabled: "#D0D3DA"
|
||||
|
||||
// Border
|
||||
border_norm: "#D0D3DA"
|
||||
border_weak: "#E7E9EC"
|
||||
// Border
|
||||
border_norm: "#D0D3DA"
|
||||
border_weak: "#E7E9EC"
|
||||
|
||||
// Background
|
||||
background_norm: "#FFFFFF"
|
||||
background_weak: "#F3F4F6"
|
||||
background_strong: "#E7E9EC"
|
||||
background_avatar: "#A4A9B5"
|
||||
// Background
|
||||
background_norm: "#FFFFFF"
|
||||
background_weak: "#F3F4F6"
|
||||
background_strong: "#E7E9EC"
|
||||
background_avatar: "#A4A9B5"
|
||||
|
||||
// Interaction-weak
|
||||
interaction_weak: "#D0D3DA"
|
||||
interaction_weak_hover: "#BABEC7"
|
||||
interaction_weak_active: "#A4A9B5"
|
||||
// Interaction-weak
|
||||
interaction_weak: "#D0D3DA"
|
||||
interaction_weak_hover: "#BABEC7"
|
||||
interaction_weak_active: "#A4A9B5"
|
||||
|
||||
// Interaction-default
|
||||
interaction_default: "#00000000"
|
||||
interaction_default_hover: "#33BABEC7"
|
||||
interaction_default_active: "#4DBABEC7"
|
||||
// Interaction-default
|
||||
interaction_default: "#00000000"
|
||||
interaction_default_hover: "#33BABEC7"
|
||||
interaction_default_active: "#4DBABEC7"
|
||||
|
||||
// Scrollbar
|
||||
scrollbar_norm: "#D0D3DA"
|
||||
scrollbar_hover: "#BABEC7"
|
||||
// Scrollbar
|
||||
scrollbar_norm: "#D0D3DA"
|
||||
scrollbar_hover: "#BABEC7"
|
||||
|
||||
// Signal
|
||||
signal_danger: "#D42F34"
|
||||
signal_danger_hover: "#C7262B"
|
||||
signal_danger_active: "#BA1E23"
|
||||
signal_warning: "#F5830A"
|
||||
signal_warning_hover: "#F5740A"
|
||||
signal_warning_active: "#F5640A"
|
||||
signal_success: "#1B8561"
|
||||
signal_success_hover: "#147857"
|
||||
signal_success_active: "#0F6B4C"
|
||||
signal_info: "#1578CF"
|
||||
signal_info_hover: "#0E6DC2"
|
||||
signal_info_active: "#0764B5"
|
||||
// Signal
|
||||
signal_danger: "#D42F34"
|
||||
signal_danger_hover: "#C7262B"
|
||||
signal_danger_active: "#BA1E23"
|
||||
signal_warning: "#F5830A"
|
||||
signal_warning_hover: "#F5740A"
|
||||
signal_warning_active: "#F5640A"
|
||||
signal_success: "#1B8561"
|
||||
signal_success_hover: "#147857"
|
||||
signal_success_active: "#0F6B4C"
|
||||
signal_info: "#1578CF"
|
||||
signal_info_hover: "#0E6DC2"
|
||||
signal_info_active: "#0764B5"
|
||||
|
||||
// Shadows
|
||||
shadow_norm: "#FFFFFF"
|
||||
shadow_lifted: "#FFFFFF"
|
||||
// Shadows
|
||||
shadow_norm: "#FFFFFF"
|
||||
shadow_lifted: "#FFFFFF"
|
||||
|
||||
// Backdrop
|
||||
backdrop_norm: "#7A262A33"
|
||||
}
|
||||
// Backdrop
|
||||
backdrop_norm: "#7A262A33"
|
||||
}
|
||||
|
||||
property var _prominentStyle: ColorScheme {
|
||||
id: prominentStyle
|
||||
property var prominentStyle: ColorScheme {
|
||||
id: _prominentStyle
|
||||
|
||||
// Primary
|
||||
primay_norm: "#657EE4"
|
||||
prominent: this
|
||||
|
||||
// Interaction-norm
|
||||
interaction_norm: "#657EE4"
|
||||
interaction_norm_hover: "#7D92E8"
|
||||
interaction_norm_active: "#98A9EE"
|
||||
// Primary
|
||||
primay_norm: "#657EE4"
|
||||
|
||||
// Text
|
||||
text_norm: "#FFFFFF"
|
||||
text_weak: "#949BB9"
|
||||
text_hint: "#565F84"
|
||||
text_disabled: "#444E72"
|
||||
text_invert: "#1C223D"
|
||||
// Interaction-norm
|
||||
interaction_norm: "#657EE4"
|
||||
interaction_norm_hover: "#7D92E8"
|
||||
interaction_norm_active: "#98A9EE"
|
||||
|
||||
// Field
|
||||
field_norm: "#565F84"
|
||||
field_hover: "#949BB9"
|
||||
field_disabled: "#353E60"
|
||||
// Text
|
||||
text_norm: "#FFFFFF"
|
||||
text_weak: "#949BB9"
|
||||
text_hint: "#565F84"
|
||||
text_disabled: "#444E72"
|
||||
text_invert: "#1C223D"
|
||||
|
||||
// Border
|
||||
border_norm: "#353E60"
|
||||
border_weak: "#2D3657"
|
||||
// Field
|
||||
field_norm: "#565F84"
|
||||
field_hover: "#949BB9"
|
||||
field_disabled: "#353E60"
|
||||
|
||||
// Background
|
||||
background_norm: "#1C223D"
|
||||
background_weak: "#272F4F"
|
||||
background_strong: "#2D3657"
|
||||
background_avatar: "#444E72"
|
||||
// Border
|
||||
border_norm: "#353E60"
|
||||
border_weak: "#2D3657"
|
||||
|
||||
// Interaction-weak
|
||||
interaction_weak: "#353E60"
|
||||
interaction_weak_hover: "#444E72"
|
||||
interaction_weak_active: "#565F84"
|
||||
// Background
|
||||
background_norm: "#1C223D"
|
||||
background_weak: "#272F4F"
|
||||
background_strong: "#2D3657"
|
||||
background_avatar: "#444E72"
|
||||
|
||||
// Interaction-default
|
||||
interaction_default: "#00000000"
|
||||
interaction_default_hover: "#4D444E72"
|
||||
interaction_default_active: "#66444E72"
|
||||
// Interaction-weak
|
||||
interaction_weak: "#353E60"
|
||||
interaction_weak_hover: "#444E72"
|
||||
interaction_weak_active: "#565F84"
|
||||
|
||||
// Scrollbar
|
||||
scrollbar_norm: "#353E60"
|
||||
scrollbar_hover: "#444E72"
|
||||
// Interaction-default
|
||||
interaction_default: "#00000000"
|
||||
interaction_default_hover: "#4D444E72"
|
||||
interaction_default_active: "#66444E72"
|
||||
|
||||
// Signal
|
||||
signal_danger: "#ED4C51"
|
||||
signal_danger_hover: "#F7595E"
|
||||
signal_danger_active: "#FF666B"
|
||||
signal_warning: "#F5930A"
|
||||
signal_warning_hover: "#F5A716"
|
||||
signal_warning_active: "#F5B922"
|
||||
signal_success: "#349172"
|
||||
signal_success_hover: "#339C79"
|
||||
signal_success_active: "#31A67F"
|
||||
signal_info: "#2C89DB"
|
||||
signal_info_hover: "#3491E3"
|
||||
signal_info_active: "#3D99EB"
|
||||
// Scrollbar
|
||||
scrollbar_norm: "#353E60"
|
||||
scrollbar_hover: "#444E72"
|
||||
|
||||
// Shadows
|
||||
shadow_norm: "#1C223D"
|
||||
shadow_lifted: "#1C223D"
|
||||
// Signal
|
||||
signal_danger: "#ED4C51"
|
||||
signal_danger_hover: "#F7595E"
|
||||
signal_danger_active: "#FF666B"
|
||||
signal_warning: "#F5930A"
|
||||
signal_warning_hover: "#F5A716"
|
||||
signal_warning_active: "#F5B922"
|
||||
signal_success: "#349172"
|
||||
signal_success_hover: "#339C79"
|
||||
signal_success_active: "#31A67F"
|
||||
signal_info: "#2C89DB"
|
||||
signal_info_hover: "#3491E3"
|
||||
signal_info_active: "#3D99EB"
|
||||
|
||||
// Backdrop
|
||||
backdrop_norm: "#52000000"
|
||||
}
|
||||
// Shadows
|
||||
shadow_norm: "#1C223D"
|
||||
shadow_lifted: "#1C223D"
|
||||
|
||||
property var _darkStyle: ColorScheme {
|
||||
id: darkStyle
|
||||
// Backdrop
|
||||
backdrop_norm: "#52000000"
|
||||
}
|
||||
|
||||
// Primary
|
||||
primay_norm: "#657EE4"
|
||||
property var darkStyle: ColorScheme {
|
||||
id: _darkStyle
|
||||
|
||||
// Interaction-norm
|
||||
interaction_norm: "#657EE4"
|
||||
interaction_norm_hover: "#7D92E8"
|
||||
interaction_norm_active: "#98A9EE"
|
||||
prominent: prominentStyle
|
||||
|
||||
// Text
|
||||
text_norm: "#FFFFFF"
|
||||
text_weak: "#A4A9B5"
|
||||
text_hint: "#696F7D"
|
||||
text_disabled: "#575D6B"
|
||||
text_invert: "#262A33"
|
||||
// Primary
|
||||
primay_norm: "#657EE4"
|
||||
|
||||
// Field
|
||||
field_norm: "#575D6B"
|
||||
field_hover: "#696F7D"
|
||||
field_disabled: "#464B58"
|
||||
// Interaction-norm
|
||||
interaction_norm: "#657EE4"
|
||||
interaction_norm_hover: "#7D92E8"
|
||||
interaction_norm_active: "#98A9EE"
|
||||
|
||||
// Border
|
||||
border_norm: "#464B58"
|
||||
border_weak: "#363A46"
|
||||
// Text
|
||||
text_norm: "#FFFFFF"
|
||||
text_weak: "#A4A9B5"
|
||||
text_hint: "#696F7D"
|
||||
text_disabled: "#575D6B"
|
||||
text_invert: "#262A33"
|
||||
|
||||
// Background
|
||||
background_norm: "#262A33"
|
||||
background_weak: "#2E323C"
|
||||
background_strong: "#363A46"
|
||||
background_avatar: "#575D6B"
|
||||
// Field
|
||||
field_norm: "#575D6B"
|
||||
field_hover: "#696F7D"
|
||||
field_disabled: "#464B58"
|
||||
|
||||
// Interaction-weak
|
||||
interaction_weak: "#464B58"
|
||||
interaction_weak_hover: "#575D6B"
|
||||
interaction_weak_active: "#696F7D"
|
||||
// Border
|
||||
border_norm: "#464B58"
|
||||
border_weak: "#363A46"
|
||||
|
||||
// Interaction-default
|
||||
interaction_default: "#00000000"
|
||||
interaction_default_hover: "#33575D6B"
|
||||
interaction_default_active: "#4D575D6B"
|
||||
// Background
|
||||
background_norm: "#262A33"
|
||||
background_weak: "#2E323C"
|
||||
background_strong: "#363A46"
|
||||
background_avatar: "#575D6B"
|
||||
|
||||
// Scrollbar
|
||||
scrollbar_norm: "#464B58"
|
||||
scrollbar_hover: "#575D6B"
|
||||
// Interaction-weak
|
||||
interaction_weak: "#464B58"
|
||||
interaction_weak_hover: "#575D6B"
|
||||
interaction_weak_active: "#696F7D"
|
||||
|
||||
// Signal
|
||||
signal_danger: "#ED4C51"
|
||||
signal_danger_hover: "#F7595E"
|
||||
signal_danger_active: "#FF666B"
|
||||
signal_warning: "#F5930A"
|
||||
signal_warning_hover: "#F5A716"
|
||||
signal_warning_active: "#F5B922"
|
||||
signal_success: "#349172"
|
||||
signal_success_hover: "#339C79"
|
||||
signal_success_active: "#31A67F"
|
||||
signal_info: "#2C89DB"
|
||||
signal_info_hover: "#3491E3"
|
||||
signal_info_active: "#3D99EB"
|
||||
// Interaction-default
|
||||
interaction_default: "#00000000"
|
||||
interaction_default_hover: "#33575D6B"
|
||||
interaction_default_active: "#4D575D6B"
|
||||
|
||||
// Shadows
|
||||
shadow_norm: "#262A33"
|
||||
shadow_lifted: "#262A33"
|
||||
// Scrollbar
|
||||
scrollbar_norm: "#464B58"
|
||||
scrollbar_hover: "#575D6B"
|
||||
|
||||
// Backdrop
|
||||
backdrop_norm: "#52000000"
|
||||
}
|
||||
// Signal
|
||||
signal_danger: "#ED4C51"
|
||||
signal_danger_hover: "#F7595E"
|
||||
signal_danger_active: "#FF666B"
|
||||
signal_warning: "#F5930A"
|
||||
signal_warning_hover: "#F5A716"
|
||||
signal_warning_active: "#F5B922"
|
||||
signal_success: "#349172"
|
||||
signal_success_hover: "#339C79"
|
||||
signal_success_active: "#31A67F"
|
||||
signal_info: "#2C89DB"
|
||||
signal_info_hover: "#3491E3"
|
||||
signal_info_active: "#3D99EB"
|
||||
|
||||
// TODO: if default style should be loaded from somewhere - it should be loaded here
|
||||
property var currentStyle: lightStyle
|
||||
// Shadows
|
||||
shadow_norm: "#262A33"
|
||||
shadow_lifted: "#262A33"
|
||||
|
||||
property var _timer: Timer {
|
||||
interval: 1000
|
||||
repeat: true
|
||||
running: true
|
||||
onTriggered: {
|
||||
switch (currentStyle) {
|
||||
case lightStyle:
|
||||
console.debug("Dark Style")
|
||||
currentStyle = darkStyle
|
||||
return
|
||||
case darkStyle:
|
||||
console.debug("Prominent Style")
|
||||
currentStyle = prominentStyle
|
||||
return
|
||||
case prominentStyle:
|
||||
console.debug("Light Style")
|
||||
currentStyle = lightStyle
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
// Backdrop
|
||||
backdrop_norm: "#52000000"
|
||||
}
|
||||
|
||||
// TODO: if default style should be loaded from somewhere
|
||||
// (i.e. from preferencies file) - it should be loaded here
|
||||
property var currentStyle: lightStyle
|
||||
|
||||
|
||||
property string font: {
|
||||
// TODO: add OS to backend
|
||||
property string font_family: {
|
||||
switch (Qt.platform.os) {
|
||||
case "windows":
|
||||
return "Segoe UI"
|
||||
case "osx":
|
||||
return "SF Pro Display"
|
||||
case "linux":
|
||||
return "Ubuntu"
|
||||
|
||||
//switch (backend.OS) {
|
||||
// case "Windows":
|
||||
// return "Segoe UI"
|
||||
// case "OSX":
|
||||
// return "SF Pro Display"
|
||||
// case "Linux":
|
||||
// return "Ubuntu"
|
||||
//}
|
||||
}
|
||||
|
||||
property int heading_font_size: 28
|
||||
property int heading_line_height: 36
|
||||
|
||||
property int title_font_size: 20
|
||||
property int title_line_height: 24
|
||||
|
||||
property int lead_font_size: 18
|
||||
property int lead_line_height: 26
|
||||
|
||||
property int body_font_size: 14
|
||||
property int body_line_height: 20
|
||||
property real body_letter_spacing: 0.2
|
||||
|
||||
property int caption_font_size: 12
|
||||
property int caption_line_height: 16
|
||||
property real caption_letter_spacing: 0.4
|
||||
default:
|
||||
console.error("Unknown platform")
|
||||
}
|
||||
}
|
||||
|
||||
property int heading_font_size: 28
|
||||
property int heading_line_height: 36
|
||||
|
||||
property int title_font_size: 20
|
||||
property int title_line_height: 24
|
||||
|
||||
property int lead_font_size: 18
|
||||
property int lead_line_height: 26
|
||||
|
||||
property int body_font_size: 14
|
||||
property int body_line_height: 20
|
||||
property real body_letter_spacing: 0.2
|
||||
|
||||
property int caption_font_size: 12
|
||||
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"
|
||||
}
|
||||
|
||||
149
internal/frontend/qml/Proton/Switch.qml
Normal file
149
internal/frontend/qml/Proton/Switch.qml
Normal file
@ -0,0 +1,149 @@
|
||||
// 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.Templates 2.12 as T
|
||||
import QtQuick.Controls 2.12
|
||||
import QtQuick.Controls.impl 2.12
|
||||
|
||||
T.Switch {
|
||||
property var colorScheme: parent.colorScheme ? parent.colorScheme : Style.currentStyle
|
||||
|
||||
property bool loading: false
|
||||
|
||||
// TODO: store previous enabled state and restore it?
|
||||
// For now assuming that only enabled buttons could have loading state
|
||||
onLoadingChanged: {
|
||||
if (loading) {
|
||||
enabled = false
|
||||
} else {
|
||||
enabled = true
|
||||
}
|
||||
}
|
||||
|
||||
id: control
|
||||
|
||||
implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
|
||||
implicitContentWidth + leftPadding + rightPadding)
|
||||
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
|
||||
implicitContentHeight + topPadding + bottomPadding,
|
||||
implicitIndicatorHeight + topPadding + bottomPadding)
|
||||
|
||||
padding: 0
|
||||
spacing: 7
|
||||
|
||||
indicator: PaddedRectangle {
|
||||
implicitWidth: 40
|
||||
implicitHeight: 24
|
||||
|
||||
x: text ? (control.mirrored ? control.width - width - control.rightPadding : control.leftPadding) : control.leftPadding + (control.availableWidth - width) / 2
|
||||
y: control.topPadding + (control.availableHeight - height) / 2
|
||||
|
||||
radius: 12
|
||||
leftPadding: 0
|
||||
rightPadding: 0
|
||||
padding: 0
|
||||
color: control.enabled || control.loading ? colorScheme.background_norm : colorScheme.background_strong
|
||||
border.width: control.enabled && !loading ? 1 : 0
|
||||
border.color: control.hovered ? colorScheme.field_hover : colorScheme.field_norm
|
||||
|
||||
Rectangle {
|
||||
x: Math.max(0, Math.min(parent.width - width, control.visualPosition * parent.width - (width / 2)))
|
||||
y: (parent.height - height) / 2
|
||||
width: 24
|
||||
height: 24
|
||||
radius: 12
|
||||
|
||||
visible: !loading
|
||||
|
||||
color: {
|
||||
if (!control.enabled) {
|
||||
return colorScheme.field_disabled
|
||||
}
|
||||
|
||||
if (control.checked) {
|
||||
if (control.hovered) {
|
||||
return colorScheme.interaction_norm_hover
|
||||
}
|
||||
|
||||
return colorScheme.interaction_norm
|
||||
}
|
||||
|
||||
if (control.hovered) {
|
||||
return colorScheme.field_hover
|
||||
}
|
||||
|
||||
return colorScheme.field_norm
|
||||
}
|
||||
|
||||
ColorImage {
|
||||
x: (parent.width - width) / 2
|
||||
y: (parent.height - height) / 2
|
||||
|
||||
width: 16
|
||||
height: 16
|
||||
color: "#FFFFFF"
|
||||
source: "../icons/ic-check.svg"
|
||||
visible: control.checked
|
||||
}
|
||||
|
||||
Behavior on x {
|
||||
enabled: !control.down
|
||||
SmoothedAnimation { velocity: 200 }
|
||||
}
|
||||
}
|
||||
|
||||
ColorImage {
|
||||
id: loadingImage
|
||||
x: parent.width - width
|
||||
y: (parent.height - height) / 2
|
||||
|
||||
width: 18
|
||||
height: 18
|
||||
color: colorScheme.interaction_norm_hover
|
||||
source: "../icons/Loader_16.svg"
|
||||
visible: control.loading
|
||||
|
||||
RotationAnimation {
|
||||
target: loadingImage
|
||||
loops: Animation.Infinite
|
||||
duration: 1000
|
||||
from: 0
|
||||
to: 360
|
||||
direction: RotationAnimation.Clockwise
|
||||
running: control.loading
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
contentItem: CheckLabel {
|
||||
id: label
|
||||
leftPadding: control.indicator && !control.mirrored ? control.indicator.width + control.spacing : 0
|
||||
rightPadding: control.indicator && control.mirrored ? control.indicator.width + control.spacing : 0
|
||||
|
||||
text: control.text
|
||||
|
||||
color: control.enabled || control.loading ? colorScheme.text_norm : colorScheme.text_disabled
|
||||
|
||||
font.family: Style.font_family
|
||||
font.weight: Style.fontWidth_400
|
||||
font.pixelSize: 14
|
||||
lineHeight: 20
|
||||
lineHeightMode: Text.FixedHeight
|
||||
font.letterSpacing: 0.2
|
||||
}
|
||||
}
|
||||
274
internal/frontend/qml/Proton/TextArea.qml
Normal file
274
internal/frontend/qml/Proton/TextArea.qml
Normal file
@ -0,0 +1,274 @@
|
||||
// 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
|
||||
|
||||
Item {
|
||||
id: root
|
||||
property var colorScheme: parent.colorScheme ? parent.colorScheme : Style.currentStyle
|
||||
|
||||
property alias background: control.background
|
||||
property alias bottomInset: control.bottomInset
|
||||
//property alias flickable: control.flickable
|
||||
property alias focusReason: control.focusReason
|
||||
property alias hoverEnabled: control.hoverEnabled
|
||||
property alias hovered: control.hovered
|
||||
property alias implicitBackgroundHeight: control.implicitBackgroundHeight
|
||||
property alias implicitBackgroundWidth: control.implicitBackgroundWidth
|
||||
property alias leftInset: control.leftInset
|
||||
property alias palette: control.palette
|
||||
property alias placeholderText: control.placeholderText
|
||||
property alias placeholderTextColor: control.placeholderTextColor
|
||||
property alias rightInset: control.rightInset
|
||||
property alias topInset: control.topInset
|
||||
property alias activeFocusOnPress: control.activeFocusOnPress
|
||||
property alias baseUrl: control.baseUrl
|
||||
property alias bottomPadding: control.bottomPadding
|
||||
property alias canPaste: control.canPaste
|
||||
property alias canRedo: control.canRedo
|
||||
property alias canUndo: control.canUndo
|
||||
property alias color: control.color
|
||||
property alias contentHeight: control.contentHeight
|
||||
property alias contentWidth: control.contentWidth
|
||||
property alias cursorDelegate: control.cursorDelegate
|
||||
property alias cursorPosition: control.cursorPosition
|
||||
property alias cursorRectangle: control.cursorRectangle
|
||||
property alias cursorVisible: control.cursorVisible
|
||||
property alias effectiveHorizontalAlignment: control.effectiveHorizontalAlignment
|
||||
property alias font: control.font
|
||||
property alias horizontalAlignment: control.horizontalAlignment
|
||||
property alias hoveredLink: control.hoveredLink
|
||||
property alias inputMethodComposing: control.inputMethodComposing
|
||||
property alias inputMethodHints: control.inputMethodHints
|
||||
property alias leftPadding: control.leftPadding
|
||||
property alias length: control.length
|
||||
property alias lineCount: control.lineCount
|
||||
property alias mouseSelectionMode: control.mouseSelectionMode
|
||||
property alias overwriteMode: control.overwriteMode
|
||||
property alias padding: control.padding
|
||||
property alias persistentSelection: control.persistentSelection
|
||||
property alias preeditText: control.preeditText
|
||||
property alias readOnly: control.readOnly
|
||||
property alias renderType: control.renderType
|
||||
property alias rightPadding: control.rightPadding
|
||||
property alias selectByKeyboard: control.selectByKeyboard
|
||||
property alias selectByMouse: control.selectByMouse
|
||||
property alias selectedText: control.selectedText
|
||||
property alias selectedTextColor: control.selectedTextColor
|
||||
property alias selectionColor: control.selectionColor
|
||||
property alias selectionEnd: control.selectionEnd
|
||||
property alias selectionStart: control.selectionStart
|
||||
property alias tabStopDistance: control.tabStopDistance
|
||||
property alias text: control.text
|
||||
property alias textDocument: control.textDocument
|
||||
property alias textFormat: control.textFormat
|
||||
property alias textMargin: control.textMargin
|
||||
property alias topPadding: control.topPadding
|
||||
property alias verticalAlignment: control.verticalAlignment
|
||||
property alias wrapMode: control.wrapMode
|
||||
|
||||
implicitWidth: background.width
|
||||
implicitHeight: control.implicitHeight +
|
||||
Math.max(label.implicitHeight + label.anchors.topMargin + label.anchors.bottomMargin, hint.implicitHeight + hint.anchors.topMargin + hint.anchors.bottomMargin) +
|
||||
assistiveText.implicitHeight
|
||||
|
||||
property alias label: label.text
|
||||
property alias hint: hint.text
|
||||
property alias assistiveText: assistiveText.text
|
||||
|
||||
property bool error: false
|
||||
|
||||
// Backgroud is moved away from within control as it will be clipped with scrollview
|
||||
Rectangle {
|
||||
id: background
|
||||
|
||||
anchors.fill: controlView
|
||||
|
||||
radius: 4
|
||||
visible: true
|
||||
color: colorScheme.background_norm
|
||||
border.color: {
|
||||
if (!control.enabled) {
|
||||
return colorScheme.field_disabled
|
||||
}
|
||||
|
||||
if (control.activeFocus) {
|
||||
return colorScheme.interaction_norm
|
||||
}
|
||||
|
||||
if (root.error) {
|
||||
return colorScheme.signal_danger
|
||||
}
|
||||
|
||||
if (control.hovered) {
|
||||
return colorScheme.field_hover
|
||||
}
|
||||
|
||||
return colorScheme.field_norm
|
||||
}
|
||||
border.width: 1
|
||||
}
|
||||
|
||||
Label {
|
||||
id: label
|
||||
|
||||
anchors.top: root.top
|
||||
anchors.left: root.left
|
||||
anchors.bottomMargin: 4
|
||||
|
||||
color: root.enabled ? colorScheme.text_norm : 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
|
||||
}
|
||||
|
||||
Label {
|
||||
id: hint
|
||||
|
||||
anchors.right: root.right
|
||||
anchors.bottom: controlView.top
|
||||
anchors.bottomMargin: 5
|
||||
|
||||
color: root.enabled ? colorScheme.text_weak : 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
|
||||
}
|
||||
|
||||
ColorImage {
|
||||
id: errorIcon
|
||||
visible: root.error
|
||||
anchors.left: parent.left
|
||||
anchors.top: assistiveText.top
|
||||
anchors.bottom: assistiveText.bottom
|
||||
source: "../icons/ic-exclamation-circle-filled.svg"
|
||||
color: colorScheme.signal_danger
|
||||
}
|
||||
|
||||
Label {
|
||||
id: assistiveText
|
||||
|
||||
anchors.left: root.error ? errorIcon.right : parent.left
|
||||
anchors.leftMargin: root.error ? 5 : 0
|
||||
anchors.bottom: root.bottom
|
||||
anchors.topMargin: 4
|
||||
|
||||
color: {
|
||||
if (!root.enabled) {
|
||||
return colorScheme.text_disabled
|
||||
}
|
||||
|
||||
if (root.error) {
|
||||
return colorScheme.signal_danger
|
||||
}
|
||||
|
||||
return 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
|
||||
}
|
||||
|
||||
ScrollView {
|
||||
id: controlView
|
||||
|
||||
anchors.top: label.bottom
|
||||
anchors.left: root.left
|
||||
anchors.right: root.right
|
||||
anchors.bottom: assistiveText.top
|
||||
|
||||
clip: true
|
||||
|
||||
T.TextArea {
|
||||
id: control
|
||||
|
||||
implicitWidth: Math.max(contentWidth + leftPadding + rightPadding,
|
||||
implicitBackgroundWidth + leftInset + rightInset,
|
||||
placeholder.implicitWidth + leftPadding + rightPadding)
|
||||
implicitHeight: Math.max(contentHeight + topPadding + bottomPadding,
|
||||
implicitBackgroundHeight + topInset + bottomInset,
|
||||
placeholder.implicitHeight + topPadding + bottomPadding)
|
||||
|
||||
padding: 8
|
||||
leftPadding: 12
|
||||
|
||||
color: control.enabled ? colorScheme.text_norm : colorScheme.text_disabled
|
||||
placeholderTextColor: control.enabled ? colorScheme.text_hint : colorScheme.text_disabled
|
||||
|
||||
selectionColor: control.palette.highlight
|
||||
selectedTextColor: control.palette.highlightedText
|
||||
|
||||
cursorDelegate: Rectangle {
|
||||
id: cursor
|
||||
width: 1
|
||||
color: colorScheme.interaction_norm
|
||||
visible: control.activeFocus && !control.readOnly && control.selectionStart === control.selectionEnd
|
||||
|
||||
Connections {
|
||||
target: control
|
||||
onCursorPositionChanged: {
|
||||
// keep a moving cursor visible
|
||||
cursor.opacity = 1
|
||||
timer.restart()
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: timer
|
||||
running: control.activeFocus && !control.readOnly
|
||||
repeat: true
|
||||
interval: Qt.styleHints.cursorFlashTime / 2
|
||||
onTriggered: cursor.opacity = !cursor.opacity ? 1 : 0
|
||||
// force the cursor visible when gaining focus
|
||||
onRunningChanged: cursor.opacity = 1
|
||||
}
|
||||
}
|
||||
|
||||
PlaceholderText {
|
||||
id: placeholder
|
||||
x: control.leftPadding
|
||||
y: control.topPadding
|
||||
width: control.width - (control.leftPadding + control.rightPadding)
|
||||
height: control.height - (control.topPadding + control.bottomPadding)
|
||||
|
||||
text: control.placeholderText
|
||||
font: control.font
|
||||
color: control.placeholderTextColor
|
||||
verticalAlignment: control.verticalAlignment
|
||||
visible: !control.length && !control.preeditText && (!control.activeFocus || control.horizontalAlignment !== Qt.AlignHCenter)
|
||||
elide: Text.ElideRight
|
||||
renderType: control.renderType
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
329
internal/frontend/qml/Proton/TextField.qml
Normal file
329
internal/frontend/qml/Proton/TextField.qml
Normal file
@ -0,0 +1,329 @@
|
||||
// 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
|
||||
import QtQuick.Layouts 1.12
|
||||
|
||||
Item {
|
||||
id: root
|
||||
property var colorScheme: parent.colorScheme ? parent.colorScheme : Style.currentStyle
|
||||
|
||||
property alias background: control.background
|
||||
property alias bottomInset: control.bottomInset
|
||||
property alias focusReason: control.focusReason
|
||||
property alias hoverEnabled: control.hoverEnabled
|
||||
property alias hovered: control.hovered
|
||||
property alias implicitBackgroundHeight: control.implicitBackgroundHeight
|
||||
property alias implicitBackgroundWidth: control.implicitBackgroundWidth
|
||||
property alias leftInset: control.leftInset
|
||||
property alias palette: control.palette
|
||||
property alias placeholderText: control.placeholderText
|
||||
property alias placeholderTextColor: control.placeholderTextColor
|
||||
property alias rightInset: control.rightInset
|
||||
property alias topInset: control.topInset
|
||||
property alias acceptableInput: control.acceptableInput
|
||||
property alias activeFocusOnPress: control.activeFocusOnPress
|
||||
property alias autoScroll: control.autoScroll
|
||||
property alias bottomPadding: control.bottomPadding
|
||||
property alias canPaste: control.canPaste
|
||||
property alias canRedo: control.canRedo
|
||||
property alias canUndo: control.canUndo
|
||||
property alias color: control.color
|
||||
//property alias contentHeight: control.contentHeight
|
||||
//property alias contentWidth: control.contentWidth
|
||||
property alias cursorDelegate: control.cursorDelegate
|
||||
property alias cursorPosition: control.cursorPosition
|
||||
property alias cursorRectangle: control.cursorRectangle
|
||||
property alias cursorVisible: control.cursorVisible
|
||||
property alias displayText: control.displayText
|
||||
property alias effectiveHorizontalAlignment: control.effectiveHorizontalAlignment
|
||||
property alias font: control.font
|
||||
property alias horizontalAlignment: control.horizontalAlignment
|
||||
property alias inputMask: control.inputMask
|
||||
property alias inputMethodComposing: control.inputMethodComposing
|
||||
property alias inputMethodHints: control.inputMethodHints
|
||||
property alias leftPadding: control.leftPadding
|
||||
property alias length: control.length
|
||||
property alias maximumLength: control.maximumLength
|
||||
property alias mouseSelectionMode: control.mouseSelectionMode
|
||||
property alias overwriteMode: control.overwriteMode
|
||||
property alias padding: control.padding
|
||||
property alias passwordCharacter: control.passwordCharacter
|
||||
property alias passwordMaskDelay: control.passwordMaskDelay
|
||||
property alias persistentSelection: control.persistentSelection
|
||||
property alias preeditText: control.preeditText
|
||||
property alias readOnly: control.readOnly
|
||||
property alias renderType: control.renderType
|
||||
property alias rightPadding: control.rightPadding
|
||||
property alias selectByMouse: control.selectByMouse
|
||||
property alias selectedText: control.selectedText
|
||||
property alias selectedTextColor: control.selectedTextColor
|
||||
property alias selectionColor: control.selectionColor
|
||||
property alias selectionEnd: control.selectionEnd
|
||||
property alias selectionStart: control.selectionStart
|
||||
property alias text: control.text
|
||||
property alias validator: control.validator
|
||||
property alias verticalAlignment: control.verticalAlignment
|
||||
property alias wrapMode: control.wrapMode
|
||||
|
||||
implicitWidth: children[0].implicitWidth
|
||||
implicitHeight: children[0].implicitHeight
|
||||
|
||||
property alias label: label.text
|
||||
property alias hint: hint.text
|
||||
property alias assistiveText: assistiveText.text
|
||||
|
||||
property var echoMode: TextInput.Normal
|
||||
|
||||
property bool error: false
|
||||
|
||||
signal accepted()
|
||||
signal editingFinished()
|
||||
signal textEdited()
|
||||
|
||||
function clear() { control.clear() }
|
||||
function copy() { control.copy() }
|
||||
function cut() { control.cut() }
|
||||
function deselect() { control.deselect() }
|
||||
function ensureVisible(position) { control.ensureVisible(position) }
|
||||
function getText(start, end) { control.getText(start, end) }
|
||||
function insert(position, text) { control.insert(position, text) }
|
||||
function isRightToLeft(start, end) { control.isRightToLeft(start, end) }
|
||||
function moveCursorSelection(position, mode) { control.moveCursorSelection(position, mode) }
|
||||
function paste() { control.paste() }
|
||||
function positionAt(x, y, position) { control.positionAt(x, y, position) }
|
||||
function positionToRectangle(pos) { control.positionToRectangle(pos) }
|
||||
function redo() { control.redo() }
|
||||
function remove(start, end) { control.remove(start, end) }
|
||||
function select(start, end) { control.select(start, end) }
|
||||
function selectAll() { control.selectAll() }
|
||||
function selectWord() { control.selectWord() }
|
||||
function undo() { control.undo() }
|
||||
function forceActiveFocus() {control.forceActiveFocus()}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
spacing: 0
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 0
|
||||
|
||||
ProtonLabel {
|
||||
id: label
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
color: root.enabled ? colorScheme.text_norm : colorScheme.text_disabled
|
||||
font.weight: Style.fontWidth_600
|
||||
state: "body"
|
||||
}
|
||||
|
||||
ProtonLabel {
|
||||
id: hint
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
color: root.enabled ? colorScheme.text_weak : colorScheme.text_disabled
|
||||
horizontalAlignment: Text.AlignRight
|
||||
state: "caption"
|
||||
}
|
||||
}
|
||||
|
||||
// Background is moved away from within control to cover eye button as well.
|
||||
// In case it will remain as control background property - control's width
|
||||
// will be adjusted to background's width making text field and eye button overlap
|
||||
Rectangle {
|
||||
id: background
|
||||
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
|
||||
radius: 4
|
||||
visible: true
|
||||
color: colorScheme.background_norm
|
||||
border.color: {
|
||||
if (!control.enabled) {
|
||||
return colorScheme.field_disabled
|
||||
}
|
||||
|
||||
if (control.activeFocus) {
|
||||
return colorScheme.interaction_norm
|
||||
}
|
||||
|
||||
if (root.error) {
|
||||
return colorScheme.signal_danger
|
||||
}
|
||||
|
||||
if (control.hovered) {
|
||||
return colorScheme.field_hover
|
||||
}
|
||||
|
||||
return colorScheme.field_norm
|
||||
}
|
||||
border.width: 1
|
||||
|
||||
implicitWidth: children[0].implicitWidth
|
||||
implicitHeight: children[0].implicitHeight
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
spacing: 0
|
||||
|
||||
T.TextField {
|
||||
id: control
|
||||
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
|
||||
implicitWidth: implicitBackgroundWidth + leftInset + rightInset
|
||||
|| Math.max(contentWidth, placeholder.implicitWidth) + leftPadding + rightPadding
|
||||
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
|
||||
contentHeight + topPadding + bottomPadding,
|
||||
placeholder.implicitHeight + topPadding + bottomPadding)
|
||||
|
||||
padding: 8
|
||||
leftPadding: 12
|
||||
|
||||
color: control.enabled ? colorScheme.text_norm : colorScheme.text_disabled
|
||||
|
||||
selectionColor: control.palette.highlight
|
||||
selectedTextColor: control.palette.highlightedText
|
||||
placeholderTextColor: control.enabled ? colorScheme.text_hint : colorScheme.text_disabled
|
||||
verticalAlignment: TextInput.AlignVCenter
|
||||
|
||||
cursorDelegate: Rectangle {
|
||||
id: cursor
|
||||
width: 1
|
||||
color: colorScheme.interaction_norm
|
||||
visible: control.activeFocus && !control.readOnly && control.selectionStart === control.selectionEnd
|
||||
|
||||
Connections {
|
||||
target: control
|
||||
onCursorPositionChanged: {
|
||||
// keep a moving cursor visible
|
||||
cursor.opacity = 1
|
||||
timer.restart()
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: timer
|
||||
running: control.activeFocus && !control.readOnly
|
||||
repeat: true
|
||||
interval: Qt.styleHints.cursorFlashTime / 2
|
||||
onTriggered: cursor.opacity = !cursor.opacity ? 1 : 0
|
||||
// force the cursor visible when gaining focus
|
||||
onRunningChanged: cursor.opacity = 1
|
||||
}
|
||||
}
|
||||
|
||||
PlaceholderText {
|
||||
id: placeholder
|
||||
x: control.leftPadding
|
||||
y: control.topPadding
|
||||
width: control.width - (control.leftPadding + control.rightPadding)
|
||||
height: control.height - (control.topPadding + control.bottomPadding)
|
||||
|
||||
text: control.placeholderText
|
||||
font: control.font
|
||||
color: control.placeholderTextColor
|
||||
verticalAlignment: control.verticalAlignment
|
||||
visible: !control.length && !control.preeditText && (!control.activeFocus || control.horizontalAlignment !== Qt.AlignHCenter)
|
||||
elide: Text.ElideRight
|
||||
renderType: control.renderType
|
||||
}
|
||||
|
||||
background: Item {
|
||||
implicitWidth: 80
|
||||
implicitHeight: 36
|
||||
visible: false
|
||||
}
|
||||
|
||||
onAccepted: {
|
||||
root.accepted()
|
||||
}
|
||||
onEditingFinished: {
|
||||
root.editingFinished()
|
||||
}
|
||||
onTextEdited: {
|
||||
root.textEdited()
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
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 {
|
||||
id: errorIcon
|
||||
|
||||
visible: root.error && (assistiveText.text.length > 0)
|
||||
icon.source: "../icons/ic-exclamation-circle-filled.svg"
|
||||
icon.color: colorScheme.signal_danger
|
||||
}
|
||||
|
||||
ProtonLabel {
|
||||
id: assistiveText
|
||||
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 4
|
||||
|
||||
color: {
|
||||
if (!root.enabled) {
|
||||
return colorScheme.text_disabled
|
||||
}
|
||||
|
||||
if (root.error) {
|
||||
return colorScheme.signal_danger
|
||||
}
|
||||
|
||||
return colorScheme.text_weak
|
||||
}
|
||||
|
||||
font.weight: root.error ? Style.fontWidth_600 : Style.fontWidth_400
|
||||
state: "caption"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2,5 +2,12 @@ module QQtQuick.Controls.Proton
|
||||
depends QtQuick.Controls 2.12
|
||||
|
||||
singleton ProtonStyle 4.0 Style.qml
|
||||
Banner 4.0 Banner.qml
|
||||
Button 4.0 Button.qml
|
||||
|
||||
CheckBox 4.0 CheckBox.qml
|
||||
ProtonLabel 4.0 ProtonLabel.qml
|
||||
RoundedRectangle 4.0 RoundedRectangle.qml
|
||||
RadioButton 4.0 RadioButton.qml
|
||||
Switch 4.0 Switch.qml
|
||||
TextArea 4.0 TextArea.qml
|
||||
TextField 4.0 TextField.qml
|
||||
|
||||
Reference in New Issue
Block a user