summaryrefslogtreecommitdiff
path: root/gui/components
diff options
context:
space:
mode:
authorkali kaneko (leap communications) <kali@leap.se>2021-09-06 21:08:14 +0200
committerkali kaneko (leap communications) <kali@leap.se>2021-11-23 21:51:01 +0100
commit8543125fa656ddc2c114072adfc27e4e7c461695 (patch)
tree176f90c03f64c2645932dbaf3d87f2ad22b61489 /gui/components
parentb8e0fe3b5003d22043042110e8036f4383602545 (diff)
[ui] transient connecting state
Diffstat (limited to 'gui/components')
-rw-r--r--gui/components/Footer.qml121
-rw-r--r--gui/components/Header.qml5
-rw-r--r--gui/components/Home.qml4
-rw-r--r--gui/components/Locations.qml284
-rw-r--r--gui/components/MainView.qml42
-rw-r--r--gui/components/Preferences.qml50
-rw-r--r--gui/components/SignalIcon.qml62
-rw-r--r--gui/components/Splash.qml15
-rw-r--r--gui/components/StatusBox.qml9
-rw-r--r--gui/components/ThemedPage.qml11
-rw-r--r--gui/components/VPNState.qml55
-rw-r--r--gui/components/WrappedRadioButton.qml21
12 files changed, 563 insertions, 116 deletions
diff --git a/gui/components/Footer.qml b/gui/components/Footer.qml
index 2c7c875..7658fa1 100644
--- a/gui/components/Footer.qml
+++ b/gui/components/Footer.qml
@@ -2,10 +2,13 @@ import QtQuick 2.0
import QtQuick.Controls 2.4
import QtQuick.Controls.Material 2.1
import QtQuick.Layouts 1.14
+import QtGraphicalEffects 1.0
+
+import "../themes/themes.js" as Theme
ToolBar {
- Material.background: Material.backgroundColor
+ Material.background: Theme.bgColor
Material.foreground: "black"
Material.elevation: 0
visible: stackView.depth > 1 && ctx !== undefined ? false : true
@@ -17,10 +20,13 @@ ToolBar {
ToolButton {
id: gwButton
- anchors.verticalCenter: parent.verticalCenter
- anchors.leftMargin: 10
- anchors.left: parent.left
- anchors.verticalCenterOffset: 5
+ visible: hasMultipleGateways()
+ anchors {
+ verticalCenter: parent.verticalCenter
+ leftMargin: 10
+ left: parent.left
+ verticalCenterOffset: 5
+ }
icon.source: stackView.depth > 1 ? "" : "../resources/globe.svg"
onClicked: stackView.push("Locations.qml")
}
@@ -30,7 +36,8 @@ ToolBar {
anchors.left: gwButton.right
anchors.verticalCenter: parent.verticalCenter
anchors.verticalCenterOffset: 5
- text: "Seattle"
+ text: locationStr()
+ color: getLocationColor()
}
Item {
@@ -40,6 +47,7 @@ ToolBar {
Image {
id: bridge
+ visible: isBridgeSelected()
height: 24
width: 24
source: "../resources/bridge.png"
@@ -58,6 +66,107 @@ ToolBar {
anchors.rightMargin: 20
anchors.verticalCenter: parent.verticalCenter
anchors.verticalCenterOffset: 5
+ // TODO refactor with SignalIcon
+ ColorOverlay{
+ anchors.fill: gwQuality
+ source: gwQuality
+ color: getSignalColor()
+ antialiasing: true
+ }
+ }
+ }
+
+ function getSignalColor() {
+ if (ctx && ctx.status == "on") {
+ return "green"
+ } else {
+ return "black"
+ }
+ }
+
+ StateGroup {
+ state: ctx ? ctx.status : "off"
+ states: [
+ State {
+ name: "on"
+ PropertyChanges {
+ target: gwQuality
+ source: "../resources/reception-4.svg"
+ }
+ },
+ State {
+ name: "off"
+ PropertyChanges {
+ target: gwQuality
+ source: "../resources/reception-0.svg"
+ }
+ }
+ ]
+ }
+
+ function locationStr() {
+ if (ctx && ctx.status == "on") {
+ if (ctx.currentLocation && ctx.currentCountry) {
+ let s = ctx.currentLocation + ", " + ctx.currentCountry
+ if (root.selectedGateway == "auto") {
+ s = "🗲 " + s
+ }
+ return s
+ }
+ }
+ if (root.selectedGateway == "auto") {
+ if (ctx && ctx.locations && ctx.bestLocation) {
+ return "🗲 " + getCanonicalLocation(ctx.bestLocation)
+ } else {
+ return qsTr("Recommended")
+ }
+ }
+ if (ctx && ctx.locations && ctx.locationLabels) {
+ return getCanonicalLocation(root.selectedGateway)
+ }
+ }
+
+ // returns the composite of Location, CC
+ function getCanonicalLocation(label) {
+ try {
+ let loc = ctx.locationLabels[label]
+ return loc[0] + ", " + loc[1]
+ } catch(e) {
+ return "unknown"
+ }
+ }
+
+ function getLocationColor() {
+ if (ctx && ctx.status == "on") {
+ return "black"
+ } else {
+ // TODO darker gray
+ return "gray"
+ }
+ }
+
+ function hasMultipleGateways() {
+ let provider = getSelectedProvider(providers)
+ if (provider == "riseup") {
+ return true
+ } else {
+ if (!ctx) {
+ return false
+ }
+ return ctx.locations.length > 0
+ }
+ }
+
+ function getSelectedProvider(providers) {
+ let obj = JSON.parse(providers.getJson())
+ return obj['default']
+ }
+
+ function isBridgeSelected() {
+ if (ctx && ctx.transport == "obfs4") {
+ return true
+ } else {
+ return false
}
}
}
diff --git a/gui/components/Header.qml b/gui/components/Header.qml
index 92f4bdd..6682a28 100644
--- a/gui/components/Header.qml
+++ b/gui/components/Header.qml
@@ -3,10 +3,12 @@ import QtQuick.Controls 2.4
import QtQuick.Dialogs 1.2
import QtQuick.Controls.Material 2.1
+import "../themes/themes.js" as Theme
+
ToolBar {
visible: stackView.depth > 1
Material.foreground: Material.Black
- Material.background: "#ffffff"
+ Material.background: Theme.bgColor
Material.elevation: 0
contentHeight: settingsButton.implicitHeight
@@ -27,6 +29,7 @@ ToolBar {
Label {
text: stackView.currentItem.title
+ font.bold: true
anchors.centerIn: parent
}
}
diff --git a/gui/components/Home.qml b/gui/components/Home.qml
index c9eab2a..099bc23 100644
--- a/gui/components/Home.qml
+++ b/gui/components/Home.qml
@@ -3,6 +3,6 @@ import QtQuick.Controls 2.2
import QtGraphicalEffects 1.0
Page {
-
- StatusBox {}
+ StatusBox {
+ }
}
diff --git a/gui/components/Locations.qml b/gui/components/Locations.qml
index d3e0f5a..955da26 100644
--- a/gui/components/Locations.qml
+++ b/gui/components/Locations.qml
@@ -1,62 +1,256 @@
import QtQuick 2.9
import QtQuick.Controls 2.2
+import QtQuick.Layouts 1.14
+import QtGraphicalEffects 1.0
import "../themes/themes.js" as Theme
-Page {
+/* TODO
+ [ ] build a better gateway object list (location, user-friendly, country, bridge, etc)
+ [ ] ui: mark manual override icon somehow?
+ [ ] ui: auto radiobutton should use also the bridge icon
+ [ ] corner case: manual override, not full list yet
+ [ ] persist bridges
+ [ ] persist manual selection
+ [ ] display the location we know
+ [ ] corner case: user selects bridges with manual selection
+ (I think the backend should discard any manual selection when selecting bridges...
+ unless the current selection provides the bridge, in which case we can maintain it)
+ */
+
+ThemedPage {
+
+ id: locationPage
title: qsTr("Select Location")
- ListView {
- id: gwList
- focus: true
- currentIndex: -1
- anchors.fill: parent
- spacing: 1
+ // TODO add ScrollIndicator
+ // https://doc.qt.io/qt-5.12//qml-qtquick-controls2-scrollindicator.html
- delegate: ItemDelegate {
- id: loc
- Rectangle {
- width: parent.width
- height: 1
- color: Theme.borderColor
- }
- width: parent.width
- text: model.text
- highlighted: ListView.isCurrentItem
- icon.color: "transparent"
- icon.source: model.icon
- onClicked: {
- model.triggered()
- stackView.pop()
- }
- MouseArea {
- property var onMouseAreaClicked: function () {
- parent.clicked()
- }
- id: mouseArea
- anchors.fill: loc
- cursorShape: Qt.PointingHandCursor
- onReleased: {
- onMouseAreaClicked()
+ //: this is in the radio button for the auto selection
+ property var autoSelectionLabel: qsTr("Automatically use best connection")
+ //: Location Selection: label for radio buttons that selects manually
+ property var manualSelectionLabel: qsTr("Manually select")
+ //: A little display to signal that the clicked gateway is being switched to
+ property var switchingLocationLabel: qsTr("Switching gateways...")
+ //: Subtitle to explain that only bridge locations are shown in the selector
+ property var onlyBridgesWarning: qsTr("Only locations with bridges")
+
+ property bool switching: false
+
+ ButtonGroup {
+ id: locsel
+ }
+
+ Rectangle {
+ id: autoBox
+ width: root.width * 0.90
+ height: 100
+ radius: 10
+ color: "white"
+ anchors {
+ horizontalCenter: parent.horizontalCenter
+ top: parent.top
+ margins: 10
+ }
+ Rectangle {
+ anchors {
+ fill: parent
+ margins: 10
+ }
+ Label {
+ id: recommendedLabel
+ //: Location Selection: label for radio button that selects automatically
+ text: qsTr("Recommended")
+ font.bold: true
+ }
+ WrappedRadioButton {
+ id: autoRadioButton
+ anchors.top: recommendedLabel.bottom
+ text: getAutoLabel()
+ ButtonGroup.group: locsel
+ checked: false
+ onClicked: {
+ root.selectedGateway = "auto"
+ console.debug("Selected gateway: auto")
+ backend.useAutomaticGateway()
}
}
}
+ }
- model: ListModel {
- ListElement {
- text: qsTr("Paris")
- triggered: function () {}
- icon: "../resources/reception-4.svg"
+ Rectangle {
+ id: manualBox
+ visible: root.locationsModel.length > 0
+ width: root.width * 0.90
+ height: getManualBoxHeight()
+ radius: 10
+ color: Theme.fgColor
+ anchors {
+ horizontalCenter: parent.horizontalCenter
+ top: autoBox.bottom
+ margins: 10
+ }
+ Rectangle {
+ anchors {
+ fill: parent
+ margins: 10
+ }
+ Label {
+ id: manualLabel
+ text: manualSelectionLabel
+ font.bold: true
+ }
+ Label {
+ id: bridgeWarning
+ text: onlyBridgesWarning
+ color: "gray"
+ visible: isBridgeSelected()
+ wrapMode: Text.Wrap
+ anchors {
+ topMargin: 5
+ top: manualLabel.bottom
+ }
+ font.pixelSize: Theme.fontSize - 3
}
- ListElement {
- text: qsTr("Montreal")
- triggered: function () {}
- icon: "../resources/reception-4.svg"
+ ColumnLayout {
+ id: gatewayListColumn
+ width: parent.width
+ spacing: 1
+ anchors.top: getManualAnchor()
+
+ Repeater {
+ id: gwManualSelectorList
+ width: parent.width
+ model: root.locationsModel
+
+ RowLayout {
+ width: parent.width
+ WrappedRadioButton {
+ text: getLocationLabel(modelData)
+ location: modelData
+ ButtonGroup.group: locsel
+ checked: false
+ enabled: locationPage.switching ? false : true
+ onClicked: {
+ if (ctx.status == "on") {
+ locationPage.switching = true
+ }
+ root.selectedGateway = location
+ backend.useLocation(location)
+ }
+ }
+ Item {
+ Layout.fillWidth: true
+ }
+ Image {
+ height: 16
+ width: 16
+ visible: isBridgeSelected()
+ source: "../resources/bridge.png"
+ Layout.alignment: Qt.AlignRight
+ Layout.rightMargin: 10
+ }
+ SignalIcon {
+ // TODO mocked!
+ quality: getSignalFor(modelData)
+ Layout.alignment: Qt.AlignRight
+ Layout.rightMargin: 20
+ }
+ }
+ }
}
- ListElement {
- text: qsTr("Seattle")
- triggered: function () {}
- icon: "../resources/reception-2.svg"
+ }
+ }
+
+ StateGroup {
+ states: [
+ State {
+ when: locationPage.switching && ctx.status != "on"
+ PropertyChanges {
+ target: manualLabel
+ text: switchingLocationLabel
+ }
+ },
+ State {
+ when: ctx && ctx.status == "on"
+ PropertyChanges {
+ target: manualLabel
+ text: manualSelectionLabel
+ }
+ StateChangeScript {
+ script: {
+ locationPage.switching = false
+ }
+ }
+ }
+ ]
+ }
+
+ function getAutoLabel() {
+ let l = autoSelectionLabel
+ if (ctx && ctx.locations && ctx.bestLocation) {
+ let best = ctx.locationLabels[ctx.bestLocation]
+ let label = best[0] + ", " + best[1]
+ l += " (" + label + ")"
+ }
+ return l
+ }
+
+ function getLocationLabel(location) {
+ if (!ctx) { return ""}
+ let l = ctx.locationLabels[location]
+ return l[0] + ", " + l[1]
+ }
+
+ function getManualBoxHeight() {
+ let h = gatewayListColumn.height + manualLabel.height
+ if (bridgeWarning.visible) {
+ h += bridgeWarning.height
+ }
+ return h + 15
+ }
+
+ function getSignalFor(location) {
+ switch(location) {
+ case "amsterdam":
+ case "paris":
+ return "good"
+ case "newyork":
+ return "medium"
+ case "montreal":
+ return "medium"
+ default:
+ return "low"
+ }
+ }
+
+ function isBridgeSelected() {
+ if (ctx && ctx.transport == "obfs4") {
+ return true
+ } else {
+ return false
+ }
+ }
+
+ function getManualAnchor() {
+ if (isBridgeSelected()) {
+ return bridgeWarning.bottom
+ } else {
+ return manualLabel.bottom
+ }
+ }
+
+ Component.onCompleted: {
+ if (root.selectedGateway == "auto") {
+ autoRadioButton.checked = true;
+ } else {
+ let match = false
+ for (var i=1; i<locsel.buttons.length; i++) {
+ let b = locsel.buttons[i]
+ if (b.location == root.selectedGateway) {
+ match = true;
+ b.checked = true;
+ }
}
}
}
diff --git a/gui/components/MainView.qml b/gui/components/MainView.qml
index 1918c7f..5407178 100644
--- a/gui/components/MainView.qml
+++ b/gui/components/MainView.qml
@@ -82,44 +82,12 @@ Page {
}
}
- Drawer {
- id: locationsDrawer
-
- width: root.width
- height: root.height
-
- ListView {
- focus: true
- currentIndex: -1
- anchors.fill: parent
-
- delegate: ItemDelegate {
- width: parent.width
- text: model.text
- highlighted: ListView.isCurrentItem
- onClicked: {
- locationsDrawer.close()
- model.triggered()
- }
- }
-
- model: ListModel {
- ListElement {
- text: qsTr("Montreal, CA")
- triggered: function () {}
- }
- ListElement {
- text: qsTr("Paris, FR")
- triggered: function () {}
- }
- }
-
- ScrollIndicator.vertical: ScrollIndicator {}
- }
+ header: Header {
+ id: header
+ }
+ footer: Footer {
+ id: footer
}
-
- header: Header {}
- footer: Footer {}
Dialog {
id: aboutDialog
diff --git a/gui/components/Preferences.qml b/gui/components/Preferences.qml
index 481444a..5c708a3 100644
--- a/gui/components/Preferences.qml
+++ b/gui/components/Preferences.qml
@@ -2,15 +2,17 @@ import QtQuick 2.9
import QtQuick.Controls 2.2
import QtQuick.Controls.Material 2.1
-Page {
+import "../themes/themes.js" as Theme
+
+ThemedPage {
title: qsTr("Preferences")
Column {
id: prefCol
// FIXME the checkboxes seem to have a bigger lineHeight themselves, need to pack more.
spacing: 1
- topPadding: root.width * 0.1
- leftPadding: root.width * 0.15
+ topPadding: root.width * 0.05
+ leftPadding: root.width * 0.1
rightPadding: root.width * 0.15
Rectangle {
@@ -18,6 +20,7 @@ Page {
visible: false
height: 40
width: 300
+ color: Theme.bgColor
anchors.horizontalCenter: parent.horizontalCenter
@@ -38,12 +41,20 @@ Page {
id: useBridgesCheckBox
checked: false
text: qsTr("Use obfs4 bridges")
+ onClicked: {
+ // TODO there's a corner case that needs to be dealt with in the backend,
+ // if an user has a manual location selected and switches to bridges:
+ // we need to fallback to "auto" selection if such location does not
+ // offer bridges
+ useBridges(checked)
+ }
}
CheckBox {
id: useSnowflake
- checked: false
text: qsTr("Use Snowflake (experimental)")
+ enabled: false
+ checked: false
}
Label {
@@ -53,8 +64,9 @@ Page {
CheckBox {
id: useUDP
- checked: false
text: qsTr("UDP")
+ enabled: false
+ checked: false
}
}
@@ -69,11 +81,11 @@ Page {
}
PropertyChanges {
target: useBridgesCheckBox
- checkable: false
+ enabled: false
}
PropertyChanges {
target: useUDP
- checkable: false
+ enabled: false
}
},
State {
@@ -84,11 +96,11 @@ Page {
}
PropertyChanges {
target: useBridgesCheckBox
- checkable: false
+ enabled: false
}
PropertyChanges {
target: useUDP
- checkable: false
+ enabled: false
}
},
State {
@@ -99,13 +111,29 @@ Page {
}
PropertyChanges {
target: useBridgesCheckBox
- checkable: true
+ enabled: true
}
PropertyChanges {
target: useUDP
- checkable: true
+ enabled: true
}
}
]
}
+
+ function useBridges(value) {
+ if (value == true) {
+ console.debug("use obfs4")
+ backend.setTransport("obfs4")
+ } else {
+ console.debug("use regular")
+ backend.setTransport("openvpn")
+ }
+ }
+
+ Component.onCompleted: {
+ if (ctx && ctx.transport == "obfs4") {
+ useBridgesCheckBox.checked = true
+ }
+ }
}
diff --git a/gui/components/SignalIcon.qml b/gui/components/SignalIcon.qml
new file mode 100644
index 0000000..63bde5c
--- /dev/null
+++ b/gui/components/SignalIcon.qml
@@ -0,0 +1,62 @@
+import QtQuick 2.9
+import QtQuick.Controls 2.2
+import QtQuick.Layouts 1.14
+import QtGraphicalEffects 1.0
+
+import "../themes/themes.js" as Theme
+
+Image {
+ id: icon
+ height: 16
+ width: 16
+ // one of: good, medium, low
+ property var quality: "good"
+
+ ColorOverlay{
+ anchors.fill: icon
+ source: icon
+ color: getQualityColor()
+ antialiasing: true
+ }
+
+ StateGroup {
+ state: quality
+ states: [
+ State {
+ name: "good"
+ PropertyChanges {
+ target: icon
+ source: "../resources/reception-4.svg"
+ }
+ },
+ State {
+ name: "medium"
+ PropertyChanges {
+ target: icon
+ source: "../resources/reception-2.svg"
+ }
+ },
+ State {
+ name: "low"
+ PropertyChanges {
+ target: icon
+ source: "../resources/reception-0.svg"
+ }
+ }
+ ]
+ }
+
+ function getQualityColor() {
+ // I like this better than with states
+ switch (quality) {
+ case "good":
+ return Theme.signalGood
+ case "medium":
+ return Theme.signalMedium
+ case "low":
+ return Theme.signalLow
+ default:
+ return Theme.signalGood
+ }
+ }
+}
diff --git a/gui/components/Splash.qml b/gui/components/Splash.qml
index 6bdd3ab..15acf48 100644
--- a/gui/components/Splash.qml
+++ b/gui/components/Splash.qml
@@ -4,7 +4,8 @@ import QtGraphicalEffects 1.0
Page {
id: splash
- property int timeoutInterval: 1600
+ property int timeoutInterval: 200
+ //property int timeoutInterval: 1600
property alias errors: splashErrorBox
Column {
@@ -40,7 +41,7 @@ Page {
function delay(delayTime, cb) {
splashTimer.interval = delayTime
- splashTimer.repeat = false
+ splashTimer.repeat = true
splashTimer.triggered.connect(cb)
splashTimer.start()
}
@@ -50,9 +51,13 @@ Page {
return
}
if (ctx && ctx.isReady) {
+ splashTimer.stop()
loader.source = "MainView.qml"
} else {
- delay(100, loadMainViewWhenReady)
+ if (!splashTimer.running) {
+ console.debug('delay...')
+ delay(500, loadMainViewWhenReady)
+ }
}
}
@@ -65,7 +70,5 @@ Page {
}
}
- Component.onCompleted: {
-
- }
+ Component.onCompleted: {}
}
diff --git a/gui/components/StatusBox.qml b/gui/components/StatusBox.qml
index a20b930..a3a5c18 100644
--- a/gui/components/StatusBox.qml
+++ b/gui/components/StatusBox.qml
@@ -7,6 +7,7 @@ import QtQuick.Templates 2.12 as T
import QtQuick.Controls.impl 2.12
import QtQuick.Controls.Material 2.12
import QtQuick.Controls.Material.impl 2.12
+
import "../themes/themes.js" as Theme
Item {
@@ -18,10 +19,15 @@ Item {
}
Rectangle {
+ color: Theme.bgColor
+ anchors.fill: parent
+ }
+
+ Rectangle {
id: statusBoxBackground
+ color: Theme.fgColor
height: 300
radius: 10
- color: Theme.bgColor
antialiasing: true
anchors {
fill: parent
@@ -124,6 +130,7 @@ Item {
if (vpn.state === "on") {
backend.switchOff()
} else if (vpn.state === "off") {
+ vpn.startingUI = true
backend.switchOn()
} else {
console.debug("unknown state")
diff --git a/gui/components/ThemedPage.qml b/gui/components/ThemedPage.qml
new file mode 100644
index 0000000..f7ee647
--- /dev/null
+++ b/gui/components/ThemedPage.qml
@@ -0,0 +1,11 @@
+import QtQuick 2.9
+import QtQuick.Controls 2.2
+
+import "../themes/themes.js" as Theme
+
+Page {
+ Rectangle {
+ color: Theme.bgColor
+ anchors.fill: parent
+ }
+}
diff --git a/gui/components/VPNState.qml b/gui/components/VPNState.qml
index 9d443ce..5e659a9 100644
--- a/gui/components/VPNState.qml
+++ b/gui/components/VPNState.qml
@@ -11,17 +11,49 @@ StateGroup {
property var stopping: "stopping"
property var failed: "failed"
- state: ctx ? ctx.status : vpnStates.off
+ property bool startingUI: false
+
+ state: ctx ? ctx.status : off
states: [
State {
name: initializing
},
State {
- name: off
+ when: ctx && ctx.status == "off" && startingUI == true
+ PropertyChanges {
+ target: connectionState
+ text: qsTr("Connecting")
+ }
+ PropertyChanges {
+ target: statusBoxBackground
+ border.color: Theme.accentConnecting
+ }
+ PropertyChanges {
+ target: connectionImage
+ source: "../resources/birds.svg"
+ anchors.horizontalCenter: parent.horizontalCenter
+ }
+ PropertyChanges {
+ target: toggleVPN
+ enabled: false
+ text: ("...")
+ }
+ PropertyChanges {
+ target: systray
+ tooltip: toHuman("connecting")
+ icon.source: icons["wait"]
+ }
+ PropertyChanges {
+ target: systray.statusItem
+ text: toHuman("connecting")
+ }
+ },
+ State {
+ name: "off"
PropertyChanges {
target: connectionState
- text: qsTr("Connection\nUnsecured")
+ text: qsTr("Unsecured\nConnection")
}
PropertyChanges {
target: statusBoxBackground
@@ -33,6 +65,7 @@ StateGroup {
}
PropertyChanges {
target: toggleVPN
+ enabled: true
text: qsTr("Turn on")
}
PropertyChanges {
@@ -44,14 +77,16 @@ StateGroup {
text: toHuman("off")
}
StateChangeScript {
- script: {}
+ script: {
+ console.debug("status off")
+ }
}
},
State {
name: on
PropertyChanges {
target: connectionState
- text: qsTr("Connection\nSecured")
+ text: qsTr("Secured\nConnection")
}
PropertyChanges {
target: statusBoxBackground
@@ -63,6 +98,7 @@ StateGroup {
}
PropertyChanges {
target: toggleVPN
+ enabled: true
text: qsTr("Turn off")
}
PropertyChanges {
@@ -75,7 +111,9 @@ StateGroup {
text: toHuman("on")
}
StateChangeScript {
- script: {}
+ script: {
+ vpn.startingUI = false
+ }
}
},
State {
@@ -95,6 +133,7 @@ StateGroup {
}
PropertyChanges {
target: toggleVPN
+ enabled: true
text: qsTr("Cancel")
}
PropertyChanges {
@@ -107,7 +146,9 @@ StateGroup {
text: toHuman("connecting")
}
StateChangeScript {
- script: {}
+ script: {
+ vpn.startingUI = false
+ }
}
},
State {
diff --git a/gui/components/WrappedRadioButton.qml b/gui/components/WrappedRadioButton.qml
new file mode 100644
index 0000000..04643b1
--- /dev/null
+++ b/gui/components/WrappedRadioButton.qml
@@ -0,0 +1,21 @@
+import QtQuick 2.9
+import QtQuick.Controls 2.2
+import QtQuick.Controls.Material 2.12
+import QtQuick.Controls.Material.impl 2.12
+
+import "../themes/themes.js" as Theme
+
+RadioButton {
+ id: control
+ width: parent.width
+ property var location
+
+ contentItem: Label {
+ text: control.text
+ font: control.font
+ horizontalAlignment: Text.AlignLeft
+ verticalAlignment: Text.AlignVCenter
+ leftPadding: control.indicator.width + control.spacing
+ wrapMode: Label.Wrap
+ }
+}