Added tab navigation functional. (#721)

- Added tab navigation functional.
- In basic types added parentFlickable property, which will help to ensure, that the item is visible within flickable parent during tab navigation.
- Added focus state for some basic types.
- In PageType qml file added lastItemTabClicked function, which will help to focus tab bar buttons when the last tab on the current page clicked.
- Added Focus for back button for all pages and drawers.
- Added scroll on tab for Servers ListView on PageHome.
This commit is contained in:
Garegin Harutyunyan 2024-04-18 17:54:55 +04:00 committed by GitHub
parent d50e7dd3f4
commit 0e4ae26bae
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
66 changed files with 2269 additions and 143 deletions

View file

@ -49,10 +49,26 @@ Button {
verticalOffset: 0
radius: 10
samples: 25
color: "#FBB26A"
color: root.activeFocus ? "#D7D8DB" : "#FBB26A"
source: backgroundCircle
}
ShapePath {
fillColor: "transparent"
strokeColor: "#D7D8DB"
strokeWidth: root.activeFocus ? 1 : 0
capStyle: ShapePath.RoundCap
PathAngleArc {
centerX: backgroundCircle.width / 2
centerY: backgroundCircle.height / 2
radiusX: 94
radiusY: 94
startAngle: 0
sweepAngle: 360
}
}
ShapePath {
fillColor: "transparent"
strokeColor: {
@ -64,14 +80,14 @@ Button {
return defaultButtonColor
}
}
strokeWidth: 3
strokeWidth: root.activeFocus ? 2 : 3
capStyle: ShapePath.RoundCap
PathAngleArc {
centerX: backgroundCircle.width / 2
centerY: backgroundCircle.height / 2
radiusX: 93
radiusY: 93
radiusX: 93 - (root.activeFocus ? 2 : 0)
radiusY: 93 - (root.activeFocus ? 2 : 0)
startAngle: 0
sweepAngle: 360
}
@ -141,4 +157,7 @@ Button {
ServersModel.setProcessedServerIndex(ServersModel.defaultIndex)
ConnectionController.connectButtonClicked()
}
Keys.onEnterPressed: this.clicked()
Keys.onReturnPressed: this.clicked()
}

View file

@ -26,6 +26,14 @@ DrawerType2 {
root.expandedHeight = content.implicitHeight + 32
}
Connections {
target: root
enabled: !GC.isMobile()
function onOpened() {
focusItem.forceActiveFocus()
}
}
Header2Type {
Layout.fillWidth: true
Layout.topMargin: 24
@ -36,6 +44,11 @@ DrawerType2 {
headerText: qsTr("Add new connection")
}
Item {
id: focusItem
KeyNavigation.tab: ip.rightButton
}
LabelWithButtonType {
id: ip
Layout.fillWidth: true
@ -48,11 +61,14 @@ DrawerType2 {
PageController.goToPage(PageEnum.PageSetupWizardCredentials)
root.close()
}
KeyNavigation.tab: qrCode.rightButton
}
DividerType {}
LabelWithButtonType {
id: qrCode
Layout.fillWidth: true
text: qsTr("Open config file, key or QR code")
@ -62,6 +78,8 @@ DrawerType2 {
PageController.goToPage(PageEnum.PageSetupWizardConfigSource)
root.close()
}
KeyNavigation.tab: focusItem
}
DividerType {}

View file

@ -17,12 +17,56 @@ ListView {
property var rootWidth
property var selectedText
property bool a: true
width: rootWidth
height: menuContent.contentItem.height
clip: true
interactive: false
property FlickableType parentFlickable
property var lastItemTabClicked
property int currentFocusIndex: 0
activeFocusOnTab: true
onActiveFocusChanged: {
if (activeFocus) {
this.currentFocusIndex = 0
this.itemAtIndex(currentFocusIndex).forceActiveFocus()
}
}
Keys.onTabPressed: {
if (currentFocusIndex < this.count - 1) {
currentFocusIndex += 1
this.itemAtIndex(currentFocusIndex).forceActiveFocus()
} else {
currentFocusIndex = 0
if (lastItemTabClicked && typeof lastItemTabClicked === "function") {
lastItemTabClicked()
}
}
}
onVisibleChanged: {
if (visible) {
currentFocusIndex = 0
focusItem.forceActiveFocus()
}
}
Item {
id: focusItem
}
onCurrentFocusIndexChanged: {
if (parentFlickable) {
parentFlickable.ensureVisible(this.itemAtIndex(currentFocusIndex))
}
}
ButtonGroup {
id: containersRadioButtonGroup
}
@ -31,6 +75,12 @@ ListView {
implicitWidth: rootWidth
implicitHeight: content.implicitHeight
onActiveFocusChanged: {
if (activeFocus) {
containerRadioButton.forceActiveFocus()
}
}
ColumnLayout {
id: content
@ -76,6 +126,19 @@ ListView {
cursorShape: Qt.PointingHandCursor
enabled: false
}
Keys.onEnterPressed: {
if (checkable) {
checked = true
}
containerRadioButton.clicked()
}
Keys.onReturnPressed: {
if (checkable) {
checked = true
}
containerRadioButton.clicked()
}
}
DividerType {

View file

@ -24,6 +24,14 @@ DrawerType2 {
anchors.right: parent.right
spacing: 0
Connections {
target: root
enabled: !GC.isMobile()
function onOpened() {
focusItem.forceActiveFocus()
}
}
Header2Type {
Layout.fillWidth: true
Layout.topMargin: 24
@ -35,7 +43,13 @@ DrawerType2 {
descriptionText: qsTr("Allows you to connect to some sites or applications through a VPN connection and bypass others")
}
Item {
id: focusItem
KeyNavigation.tab: splitTunnelingSwitch.visible ? splitTunnelingSwitch : siteBasedSplitTunnelingSwitch.rightButton
}
LabelWithButtonType {
id: splitTunnelingSwitch
Layout.fillWidth: true
Layout.topMargin: 16
@ -45,6 +59,8 @@ DrawerType2 {
descriptionText: qsTr("Enabled \nCan't be disabled for current server")
rightImageSource: "qrc:/images/controls/chevron-right.svg"
KeyNavigation.tab: siteBasedSplitTunnelingSwitch.visible ? siteBasedSplitTunnelingSwitch.rightButton : focusItem
clickedFunction: function() {
// PageController.goToPage(PageEnum.PageSettingsSplitTunneling)
// root.close()
@ -56,6 +72,7 @@ DrawerType2 {
}
LabelWithButtonType {
id: siteBasedSplitTunnelingSwitch
Layout.fillWidth: true
Layout.topMargin: 16
@ -63,6 +80,10 @@ DrawerType2 {
descriptionText: enabled && SitesModel.isTunnelingEnabled ? qsTr("Enabled") : qsTr("Disabled")
rightImageSource: "qrc:/images/controls/chevron-right.svg"
KeyNavigation.tab: appSplitTunnelingSwitch.visible ?
appSplitTunnelingSwitch.rightButton :
focusItem
clickedFunction: function() {
PageController.goToPage(PageEnum.PageSettingsSplitTunneling)
root.close()
@ -73,6 +94,7 @@ DrawerType2 {
}
LabelWithButtonType {
id: appSplitTunnelingSwitch
visible: isAppSplitTinnelingEnabled
Layout.fillWidth: true
@ -81,6 +103,8 @@ DrawerType2 {
descriptionText: AppSplitTunnelingModel.isTunnelingEnabled ? qsTr("Enabled") : qsTr("Disabled")
rightImageSource: "qrc:/images/controls/chevron-right.svg"
KeyNavigation.tab: focusItem
clickedFunction: function() {
PageController.goToPage(PageEnum.PageSettingsAppSplitTunneling)
root.close()

View file

@ -5,6 +5,8 @@ import QtQuick.Layouts
import "../Controls2"
import "../Controls2/TextTypes"
import "../Config"
DrawerType2 {
id: root
@ -29,6 +31,14 @@ DrawerType2 {
root.expandedHeight = content.implicitHeight + 32
}
Connections {
target: root
enabled: !GC.isMobile()
function onOpened() {
focusItem.forceActiveFocus()
}
}
Header2TextType {
Layout.fillWidth: true
Layout.topMargin: 16
@ -47,7 +57,13 @@ DrawerType2 {
text: descriptionText
}
Item {
id: focusItem
KeyNavigation.tab: yesButton
}
BasicButtonType {
id: yesButton
Layout.fillWidth: true
Layout.topMargin: 16
Layout.rightMargin: 16
@ -60,9 +76,12 @@ DrawerType2 {
yesButtonFunction()
}
}
KeyNavigation.tab: noButton
}
BasicButtonType {
id: noButton
Layout.fillWidth: true
Layout.rightMargin: 16
Layout.leftMargin: 16
@ -81,6 +100,8 @@ DrawerType2 {
noButtonFunction()
}
}
KeyNavigation.tab: focusItem
}
}
}

View file

@ -4,6 +4,7 @@ import QtQuick.Layouts
import "../Controls2"
import "../Controls2/TextTypes"
import "../Config"
DrawerType2 {
id: root
@ -17,8 +18,21 @@ DrawerType2 {
root.expandedHeight = container.implicitHeight
}
Connections {
target: root
enabled: !GC.isMobile()
function onOpened() {
focusItem.forceActiveFocus()
}
}
Item {
id: focusItem
KeyNavigation.tab: backButton
}
ColumnLayout {
id: backButton
id: backButtonLayout
anchors.top: parent.top
anchors.left: parent.left
@ -26,15 +40,15 @@ DrawerType2 {
anchors.topMargin: 16
BackButtonType {
id: backButton
backButtonImage: "qrc:/images/controls/arrow-left.svg"
backButtonFunction: function() {
root.close()
}
backButtonFunction: function() { root.close() }
KeyNavigation.tab: listView
}
}
FlickableType {
anchors.top: backButton.bottom
anchors.top: backButtonLayout.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
@ -71,10 +85,50 @@ DrawerType2 {
id: buttonGroup
}
property int currentFocusIndex: 0
activeFocusOnTab: true
onActiveFocusChanged: {
if (activeFocus) {
this.currentFocusIndex = 0
this.itemAtIndex(currentFocusIndex).forceActiveFocus()
}
}
Keys.onTabPressed: {
if (currentFocusIndex < this.count - 1) {
currentFocusIndex += 1
this.itemAtIndex(currentFocusIndex).forceActiveFocus()
} else {
listViewFocusItem.forceActiveFocus()
focusItem.forceActiveFocus()
}
}
Item {
id: listViewFocusItem
Keys.onTabPressed: {
root.forceActiveFocus()
}
}
onVisibleChanged: {
if (visible) {
listViewFocusItem.forceActiveFocus()
focusItem.forceActiveFocus()
}
}
delegate: Item {
implicitWidth: root.width
implicitHeight: delegateContent.implicitHeight
onActiveFocusChanged: {
if (activeFocus) {
radioButton.forceActiveFocus()
}
}
ColumnLayout {
id: delegateContent
@ -89,12 +143,18 @@ DrawerType2 {
hoverEnabled: true
indicator: Rectangle {
anchors.fill: parent
width: parent.width - 1
height: parent.height
color: radioButton.hovered ? "#2C2D30" : "#1C1D21"
border.color: radioButton.focus ? "#D7D8DB" : "transparent"
border.width: radioButton.focus ? 1 : 0
Behavior on color {
PropertyAnimation { duration: 200 }
}
Behavior on border.color {
PropertyAnimation { duration: 200 }
}
}
RowLayout {
@ -137,6 +197,9 @@ DrawerType2 {
}
}
}
Keys.onEnterPressed: radioButton.clicked()
Keys.onReturnPressed: radioButton.clicked()
}
}
}

View file

@ -22,16 +22,52 @@ ListView {
clip: true
interactive: false
activeFocusOnTab: true
Keys.onTabPressed: {
if (currentIndex < this.count - 1) {
this.incrementCurrentIndex()
} else {
currentIndex = 0
lastItemTabClickedSignal()
}
}
onCurrentIndexChanged: {
if (visible) {
if (fl.contentHeight > fl.height) {
var item = this.currentItem
if (item.y < fl.height) {
fl.contentY = item.y
} else if (item.y + item.height > fl.contentY + fl.height) {
fl.contentY = item.y + item.height - fl.height
}
}
}
}
onVisibleChanged: {
if (visible) {
this.currentIndex = 0
}
}
delegate: Item {
implicitWidth: root.width
implicitHeight: delegateContent.implicitHeight
onActiveFocusChanged: {
if (activeFocus) {
containerRadioButton.rightButton.forceActiveFocus()
}
}
ColumnLayout {
id: delegateContent
anchors.fill: parent
LabelWithButtonType {
id: containerRadioButton
implicitWidth: parent.width
text: name

View file

@ -121,6 +121,9 @@ DrawerType2 {
text: qsTr("Copy")
imageSource: "qrc:/images/controls/copy.svg"
Keys.onReturnPressed: { copyConfigTextButton.clicked() }
Keys.onEnterPressed: { copyConfigTextButton.clicked() }
KeyNavigation.tab: copyNativeConfigStringButton.visible ? copyNativeConfigStringButton : showSettingsButton
}
@ -174,11 +177,30 @@ DrawerType2 {
anchors.fill: parent
expandedHeight: parent.height * 0.9
onClosed: {
if (!GC.isMobile()) {
header.forceActiveFocus()
}
}
expandedContent: Item {
id: configContentContainer
implicitHeight: configContentDrawer.expandedHeight
Connections {
target: configContentDrawer
enabled: !GC.isMobile()
function onOpened() {
focusItem.forceActiveFocus()
}
}
Item {
id: focusItem
KeyNavigation.tab: backButton
}
Connections {
target: copyNativeConfigStringButton
function onClicked() {
@ -196,6 +218,7 @@ DrawerType2 {
configText.copy()
configText.select(0, 0)
PageController.showNotificationMessage(qsTr("Copied"))
header.forceActiveFocus()
}
}
@ -207,9 +230,9 @@ DrawerType2 {
anchors.right: parent.right
anchors.topMargin: 16
backButtonFunction: function() {
configContentDrawer.close()
}
backButtonFunction: function() { configContentDrawer.close() }
KeyNavigation.tab: focusItem
}
FlickableType {
@ -256,6 +279,7 @@ DrawerType2 {
height: 24
readOnly: true
activeFocusOnTab: false
color: "#D7D8DB"
selectionColor: "#633303"

View file

@ -17,12 +17,19 @@ Rectangle {
color: "#1C1D21"
radius: 16
onFocusChanged: {
if (focus) {
udpButton.forceActiveFocus()
}
}
RowLayout {
id: transportProtoButtonGroup
spacing: 0
HorizontalRadioButton {
id: udpButton
checked: root.currentIndex === 0
hoverEnabled: root.enabled
@ -30,12 +37,15 @@ Rectangle {
implicitWidth: (rootWidth - 32) / 2
text: "UDP"
KeyNavigation.tab: tcpButton
onClicked: {
root.currentIndex = 0
}
}
HorizontalRadioButton {
id: tcpButton
checked: root.currentIndex === 1
hoverEnabled: root.enabled

View file

@ -13,6 +13,12 @@ Item {
visible: backButtonImage !== ""
onActiveFocusChanged: {
if (activeFocus) {
backButton.forceActiveFocus()
}
}
RowLayout {
id: content
@ -20,6 +26,7 @@ Item {
anchors.leftMargin: 8
ImageButtonType {
id: backButton
image: backButtonImage
imageColor: "#D7D8DB"
@ -42,4 +49,7 @@ Item {
color: "transparent"
}
}
Keys.onEnterPressed: backButton.clicked()
Keys.onReturnPressed: backButton.clicked()
}

View file

@ -26,18 +26,29 @@ Button {
property bool squareLeftSide: false
property FlickableType parentFlickable
property var clickedFunc
implicitHeight: 56
hoverEnabled: true
focusPolicy: Qt.TabFocus
onFocusChanged: {
if (root.activeFocus) {
if (root.parentFlickable) {
root.parentFlickable.ensureVisible(this)
}
}
}
background: Rectangle {
id: focusBorder
color: "transparent"
border.color: root.activeFocus ? root.borderFocusedColor : "transparent"
border.width: root.activeFocus ? root.borderFocusedWidth : "transparent"
border.width: root.activeFocus ? root.borderFocusedWidth : 0
anchors.fill: parent

View file

@ -23,6 +23,8 @@ CheckBox {
property string checkedBorderColor: "#FBB26A"
property string checkedBorderDisabledColor: "#402102"
property string borderFocusedColor: "#D7D8DB"
property string checkedImageColor: "#FBB26A"
property string pressedImageColor: "#A85809"
property string defaultImageColor: "transparent"
@ -30,7 +32,24 @@ CheckBox {
property string imageSource: "qrc:/images/controls/check.svg"
property var parentFlickable
onFocusChanged: {
if (root.activeFocus) {
if (root.parentFlickable) {
root.parentFlickable.ensureVisible(root)
}
}
}
hoverEnabled: enabled ? true : false
focusPolicy: Qt.NoFocus
background: Rectangle {
color: "transparent"
border.color: root.focus ? borderFocusedColor : "transparent"
border.width: 1
radius: 16
}
indicator: Rectangle {
id: background
@ -59,7 +78,11 @@ CheckBox {
width: 24
height: 24
color: "transparent"
border.color: root.checked ? (root.enabled ? checkedBorderColor : checkedBorderDisabledColor) : defaultBorderColor
border.color: root.checked ?
(root.enabled ?
checkedBorderColor :
checkedBorderDisabledColor) :
defaultBorderColor
border.width: 1
radius: 4
@ -130,6 +153,16 @@ CheckBox {
cursorShape: Qt.PointingHandCursor
enabled: false
}
Keys.onEnterPressed: {
root.checked = !root.checked
}
Keys.onReturnPressed: {
root.checked = !root.checked
}
}

View file

@ -3,6 +3,7 @@ import QtQuick.Controls
import QtQuick.Layouts
import "TextTypes"
import "../Config"
Item {
id: root
@ -27,6 +28,9 @@ Item {
property string rootButtonBackgroundHoveredColor: "#1C1D21"
property string rootButtonBackgroundPressedColor: "#1C1D21"
property string borderFocusedColor: "#D7D8DB"
property int borderFocusedWidth: 1
property string rootButtonHoveredBorderColor: "#494B50"
property string rootButtonDefaultBorderColor: "#2C2D30"
property string rootButtonPressedBorderColor: "#D7D8DB"
@ -42,44 +46,70 @@ Item {
signal open
signal close
function popupClosedFunc() {
if (!GC.isMobile()) {
this.forceActiveFocus()
}
}
property var parentFlickable
onFocusChanged: {
if (root.activeFocus) {
if (root.parentFlickable) {
root.parentFlickable.ensureVisible(root)
}
}
}
implicitWidth: rootButtonContent.implicitWidth
implicitHeight: rootButtonContent.implicitHeight
onOpen: {
menu.open()
rootButtonBackground.border.color = rootButtonPressedBorderColor
}
onClose: {
menu.close()
rootButtonBackground.border.color = rootButtonDefaultBorderColor
}
onEnabledChanged: {
if (enabled) {
rootButtonBackground.color = rootButtonBackgroundColor
rootButtonBackground.border.color = rootButtonDefaultBorderColor
} else {
rootButtonBackground.color = "transparent"
rootButtonBackground.border.color = rootButtonHoveredBorderColor
}
}
Rectangle {
id: rootButtonBackground
id: focusBorder
color: "transparent"
border.color: root.activeFocus ? root.borderFocusedColor : "transparent"
border.width: root.activeFocus ? root.borderFocusedWidth : 0
anchors.fill: rootButtonContent
radius: 16
color: root.enabled ? rootButtonBackgroundColor : "transparent"
border.color: root.enabled ? rootButtonDefaultBorderColor : rootButtonHoveredBorderColor
border.width: 1
Behavior on border.color {
PropertyAnimation { duration: 200 }
}
Behavior on color {
PropertyAnimation { duration: 200 }
Rectangle {
id: rootButtonBackground
anchors.fill: focusBorder
anchors.margins: root.activeFocus ? 2 : 0
radius: root.activeFocus ? 14 : 16
color: {
if (root.enabled) {
if (root.pressed) {
return root.rootButtonBackgroundPressedColor
}
return root.hovered ? root.rootButtonBackgroundHoveredColor : root.rootButtonBackgroundColor
} else {
return "transparent"
}
}
border.color: rootButtonDefaultBorderColor
border.width: 1
Behavior on border.color {
PropertyAnimation { duration: 200 }
}
Behavior on color {
PropertyAnimation { duration: 200 }
}
}
}
@ -107,6 +137,7 @@ Item {
}
ButtonTextType {
id: buttonText
Layout.fillWidth: true
horizontalAlignment: Text.AlignLeft
@ -136,26 +167,6 @@ Item {
cursorShape: Qt.PointingHandCursor
hoverEnabled: root.enabled ? true : false
onEntered: {
if (menu.isClosed) {
rootButtonBackground.border.color = rootButtonHoveredBorderColor
rootButtonBackground.color = rootButtonBackgroundHoveredColor
}
}
onExited: {
if (menu.isClosed) {
rootButtonBackground.border.color = rootButtonDefaultBorderColor
rootButtonBackground.color = rootButtonBackgroundColor
}
}
onPressed: {
if (menu.isClosed) {
rootButtonBackground.color = pressed ? rootButtonBackgroundPressedColor : entered ? rootButtonHoveredBorderColor : rootButtonDefaultBorderColor
}
}
onClicked: {
if (rootButtonClickedFunction && typeof rootButtonClickedFunction === "function") {
rootButtonClickedFunction()
@ -173,11 +184,27 @@ Item {
anchors.fill: parent
expandedHeight: drawerParent.height * drawerHeight
onClosed: {
root.popupClosedFunc()
}
expandedContent: Item {
id: container
implicitHeight: menu.expandedHeight
Connections {
target: menu
enabled: !GC.isMobile()
function onOpened() {
focusItem.forceActiveFocus()
}
}
Item {
id: focusItem
KeyNavigation.tab: backButton
}
ColumnLayout {
id: header
@ -187,14 +214,15 @@ Item {
anchors.topMargin: 16
BackButtonType {
id: backButton
backButtonImage: root.headerBackButtonImage
backButtonFunction: function() {
menu.close()
}
backButtonFunction: function() { menu.close() }
KeyNavigation.tab: listViewLoader.item
}
}
FlickableType {
id: flickable
anchors.top: header.bottom
anchors.topMargin: 16
contentHeight: col.implicitHeight
@ -221,9 +249,28 @@ Item {
Loader {
id: listViewLoader
sourceComponent: root.listView
onLoaded: {
listViewLoader.item.parentFlickable = flickable
listViewLoader.item.lastItemTabClicked = function() {
focusItem.forceActiveFocus()
}
}
}
}
}
}
}
Keys.onEnterPressed: {
if (menu.isClosed) {
menu.open()
}
}
Keys.onReturnPressed: {
if (menu.isClosed) {
menu.open()
}
}
}

View file

@ -5,6 +5,14 @@ import "../Config"
Flickable {
id: fl
function ensureVisible(item) {
if (item.y < fl.contentY) {
fl.contentY = item.y
} else if (item.y + item.height > fl.contentY + fl.height) {
fl.contentY = item.y + item.height - fl.height + 40 // 40 is a bottom margin
}
}
clip: true
width: parent.width

View file

@ -9,6 +9,8 @@ Item {
property string actionButtonImage
property var actionButtonFunction
property alias actionButton: headerActionButton
property string headerText
property string descriptionText
@ -60,4 +62,16 @@ Item {
visible: root.descriptionText !== ""
}
}
Keys.onEnterPressed: {
if (actionButtonFunction && typeof actionButtonFunction === "function") {
actionButtonFunction()
}
}
Keys.onReturnPressed: {
if (actionButtonFunction && typeof actionButtonFunction === "function") {
actionButtonFunction()
}
}
}

View file

@ -9,12 +9,16 @@ Item {
property string actionButtonImage
property var actionButtonFunction
property alias actionButton: headerActionButton
property string headerText
property int headerTextMaximumLineCount: 2
property int headerTextElide: Qt.ElideRight
property string descriptionText
focus: true
implicitWidth: content.implicitWidth
implicitHeight: content.implicitHeight
@ -67,4 +71,16 @@ Item {
visible: root.descriptionText !== ""
}
}
Keys.onEnterPressed: {
if (actionButtonFunction && typeof actionButtonFunction === "function") {
actionButtonFunction()
}
}
Keys.onReturnPressed: {
if (actionButtonFunction && typeof actionButtonFunction === "function") {
actionButtonFunction()
}
}
}

View file

@ -19,6 +19,7 @@ RadioButton {
property string checkedBorderColor: "#FBB26A"
property string defaultBodredColor: "transparent"
property string checkedDisabledBorderColor: "#84603D"
property string borderFocusedColor: "#D7D8DB"
property int borderWidth: 0
implicitWidth: content.implicitWidth
@ -47,6 +48,8 @@ RadioButton {
return root.pressedBorderColor
} else if (root.checked) {
return root.checkedBorderColor
} else if (root.activeFocus) {
return root.borderFocusedColor
}
return root.defaultBodredColor
} else {
@ -58,7 +61,7 @@ RadioButton {
}
border.width: {
if(root.checked) {
if(root.checked || root.activeFocus) {
return 1
}
return root.pressed ? 1 : 0
@ -97,4 +100,12 @@ RadioButton {
cursorShape: Qt.PointingHandCursor
enabled: false
}
Keys.onEnterPressed: {
this.clicked()
}
Keys.onReturnPressed: {
this.clicked()
}
}

View file

@ -18,11 +18,26 @@ Button {
property alias backgroundColor: background.color
property alias backgroundRadius: background.radius
property string borderFocusedColor: "#D7D8DB"
property int borderFocusedWidth: 1
hoverEnabled: true
focus: true
focusPolicy: Qt.TabFocus
icon.source: image
icon.color: root.enabled ? imageColor : disableImageColor
property Flickable parentFlickable
onFocusChanged: {
if (root.activeFocus) {
if (root.parentFlickable) {
root.parentFlickable.ensureVisible(this)
}
}
}
Behavior on icon.color {
PropertyAnimation { duration: 200 }
}
@ -31,6 +46,9 @@ Button {
id: background
anchors.fill: parent
border.color: root.activeFocus ? root.borderFocusedColor : "transparent"
border.width: root.activeFocus ? root.borderFocusedWidth : 0
color: {
if (root.enabled) {
if (root.pressed) {
@ -44,6 +62,9 @@ Button {
Behavior on color {
PropertyAnimation { duration: 200 }
}
Behavior on border.color {
PropertyAnimation { duration: 200 }
}
}
MouseArea {

View file

@ -19,12 +19,18 @@ Item {
property string leftImageSource
property bool isLeftImageHoverEnabled: true //todo separete this qml file to 3
property alias rightButton: rightImage
property FlickableType parentFlickable
property string textColor: "#d7d8db"
property string textDisabledColor: "#878B91"
property string descriptionColor: "#878B91"
property string descriptionDisabledColor: "#494B50"
property real textOpacity: 1.0
property string borderFocusedColor: "#D7D8DB"
property int borderFocusedWidth: 1
property string rightImageColor: "#d7d8db"
property bool descriptionOnTop: false
@ -32,6 +38,25 @@ Item {
implicitWidth: content.implicitWidth + content.anchors.topMargin + content.anchors.bottomMargin
implicitHeight: content.implicitHeight + content.anchors.leftMargin + content.anchors.rightMargin
onFocusChanged: {
if (root.activeFocus) {
if (root.parentFlickable) {
root.parentFlickable.ensureVisible(root)
}
}
}
Connections {
target: rightImage
function onFocusChanged() {
if (rightImage.activeFocus) {
if (root.parentFlickable) {
root.parentFlickable.ensureVisible(root)
}
}
}
}
RowLayout {
id: content
anchors.fill: parent
@ -163,6 +188,9 @@ Item {
anchors.fill: root
color: "transparent"
border.color: root.activeFocus ? root.borderFocusedColor : "transparent"
border.width: root.activeFocus ? root.borderFocusedWidth : 0
Behavior on color {
PropertyAnimation { duration: 200 }
@ -207,4 +235,16 @@ Item {
}
}
}
Keys.onEnterPressed: {
if (clickedFunction && typeof clickedFunction === "function") {
clickedFunction()
}
}
Keys.onReturnPressed: {
if (clickedFunction && typeof clickedFunction === "function") {
clickedFunction()
}
}
}

View file

@ -26,6 +26,47 @@ ListView {
clip: true
interactive: false
property FlickableType parentFlickable
property var lastItemTabClicked
property int currentFocusIndex: 0
activeFocusOnTab: true
onActiveFocusChanged: {
if (activeFocus) {
this.currentFocusIndex = 0
this.itemAtIndex(currentFocusIndex).forceActiveFocus()
}
}
Keys.onTabPressed: {
if (currentFocusIndex < this.count - 1) {
currentFocusIndex += 1
} else {
currentFocusIndex = 0
}
this.itemAtIndex(currentFocusIndex).forceActiveFocus()
}
Item {
id: focusItem
Keys.onTabPressed: {
root.forceActiveFocus()
}
}
onVisibleChanged: {
if (visible) {
focusItem.forceActiveFocus()
}
}
onCurrentFocusIndexChanged: {
if (parentFlickable) {
parentFlickable.ensureVisible(this.itemAtIndex(currentFocusIndex))
}
}
ButtonGroup {
id: buttonGroup
}
@ -40,6 +81,12 @@ ListView {
implicitWidth: rootWidth
implicitHeight: content.implicitHeight
onActiveFocusChanged: {
if (activeFocus) {
radioButton.forceActiveFocus()
}
}
ColumnLayout {
id: content
@ -54,12 +101,18 @@ ListView {
hoverEnabled: true
indicator: Rectangle {
anchors.fill: parent
width: parent.width - 1
height: parent.height
color: radioButton.hovered ? "#2C2D30" : "#1C1D21"
border.color: radioButton.focus ? "#D7D8DB" : "transparent"
border.width: radioButton.focus ? 1 : 0
Behavior on color {
PropertyAnimation { duration: 200 }
}
Behavior on border.color {
PropertyAnimation { duration: 200 }
}
MouseArea {
anchors.fill: parent
@ -117,5 +170,13 @@ ListView {
root.selectedText = name
}
}
Keys.onReturnPressed: {
radioButton.clicked()
}
Keys.onEnterPressed: {
radioButton.clicked()
}
}
}

View file

@ -11,6 +11,28 @@ Item {
property var defaultActiveFocusItem: null
onVisibleChanged: {
if (visible && !GC.isMobile()) {
timer.start()
}
}
function lastItemTabClicked(focusItem) {
if (GC.isMobile()) {
return
}
if (focusItem) {
focusItem.forceActiveFocus()
PageController.forceTabBarActiveFocus()
} else {
if (defaultActiveFocusItem) {
defaultActiveFocusItem.forceActiveFocus()
}
PageController.forceTabBarActiveFocus()
}
}
// MouseArea {
// id: globalMouseArea
// z: 99

View file

@ -25,6 +25,14 @@ Popup {
color: Qt.rgba(14/255, 14/255, 17/255, 0.8)
}
onOpened: {
focusItem.forceActiveFocus()
}
onClosed: {
PageController.forceStackActiveFocus()
}
background: Rectangle {
anchors.fill: parent
@ -52,7 +60,13 @@ Popup {
text: root.text
}
Item {
id: focusItem
KeyNavigation.tab: closeButton
}
BasicButtonType {
id: closeButton
visible: closeButtonVisible
implicitHeight: 32
@ -66,6 +80,8 @@ Popup {
borderWidth: 0
text: qsTr("Close")
KeyNavigation.tab: focusItem
clickedFunc: function() {
root.close()
}

View file

@ -18,6 +18,9 @@ Switch {
property string defaultIndicatorColor: "transparent"
property string checkedDisabledIndicatorColor: "#402102"
property string borderFocusedColor: "#D7D8DB"
property int borderFocusedWidth: 1
property string checkedIndicatorBorderColor: "#633303"
property string defaultIndicatorBorderColor: "#494B50"
property string checkedDisabledIndicatorBorderColor: "#402102"
@ -31,6 +34,16 @@ Switch {
property string defaultIndicatorBackgroundColor: "transparent"
hoverEnabled: enabled ? true : false
focusPolicy: Qt.TabFocus
property FlickableType parentFlickable: null
onFocusChanged: {
if (root.activeFocus) {
if (root.parentFlickable) {
root.parentFlickable.ensureVisible(root)
}
}
}
indicator: Rectangle {
id: switcher
@ -44,8 +57,9 @@ Switch {
radius: 16
color: root.checked ? (root.enabled ? root.checkedIndicatorColor : root.checkedDisabledIndicatorColor)
: root.defaultIndicatorColor
border.color: root.checked ? (root.enabled ? root.checkedIndicatorBorderColor : root.checkedDisabledIndicatorBorderColor)
: root.defaultIndicatorBorderColor
border.color: root.activeFocus ? root.borderFocusedColor : (root.checked ? (root.enabled ? root.checkedIndicatorBorderColor : root.checkedDisabledIndicatorBorderColor)
: root.defaultIndicatorBorderColor)
Behavior on color {
PropertyAnimation { duration: 200 }
@ -114,4 +128,14 @@ Switch {
cursorShape: Qt.PointingHandCursor
enabled: false
}
Keys.onEnterPressed: {
root.checked = !root.checked
root.checkedChanged()
}
Keys.onReturnPressed: {
root.checked = !root.checked
root.checkedChanged()
}
}

View file

@ -10,11 +10,15 @@ TabButton {
property string textColor: "#D7D8DB"
property string borderFocusedColor: "#D7D8DB"
property int borderFocusedWidth: 1
property bool isSelected: false
implicitHeight: 48
hoverEnabled: true
focusPolicy: Qt.TabFocus
background: Rectangle {
id: background
@ -22,6 +26,9 @@ TabButton {
anchors.fill: parent
color: "transparent"
border.color: root.activeFocus ? root.borderFocusedColor : "transparent"
border.width: root.activeFocus ? root.borderFocusedWidth : 0
Rectangle {
width: parent.width
height: 1

View file

@ -12,7 +12,13 @@ TabButton {
property bool isSelected: false
property string borderFocusedColor: "#D7D8DB"
property int borderFocusedWidth: 1
property var clickedFunc
hoverEnabled: true
focusPolicy: Qt.TabFocus
icon.source: image
icon.color: isSelected ? selectedColor : defaultColor
@ -21,6 +27,11 @@ TabButton {
id: background
anchors.fill: parent
color: "transparent"
radius: 10
border.color: root.activeFocus ? root.borderFocusedColor : "transparent"
border.width: root.activeFocus ? root.borderFocusedWidth : 0
}
MouseArea {
@ -28,4 +39,22 @@ TabButton {
cursorShape: Qt.PointingHandCursor
enabled: false
}
Keys.onEnterPressed: {
if (root.clickedFunc && typeof root.clickedFunc === "function") {
root.clickedFunc()
}
}
Keys.onReturnPressed: {
if (root.clickedFunc && typeof root.clickedFunc === "function") {
root.clickedFunc()
}
}
onClicked: {
if (root.clickedFunc && typeof root.clickedFunc === "function") {
root.clickedFunc()
}
}
}

View file

@ -19,6 +19,15 @@ Rectangle {
border.color: getBorderColor(borderNormalColor)
radius: 16
property FlickableType parentFlickable: null
onFocusChanged: {
if (root.activeFocus) {
if (root.parentFlickable) {
root.parentFlickable.ensureVisible(root)
}
}
}
MouseArea {
id: parentMouse
anchors.fill: parent

View file

@ -13,6 +13,7 @@ Item {
property alias errorText: errorField.text
property bool checkEmptyText: false
property bool rightButtonClickedOnEnter: false
property string buttonText
property string buttonImageSource
@ -36,6 +37,18 @@ Item {
implicitWidth: content.implicitWidth
implicitHeight: content.implicitHeight
property FlickableType parentFlickable
Connections {
target: textField
function onFocusChanged() {
if (textField.activeFocus) {
if (root.parentFlickable) {
root.parentFlickable.ensureVisible(root)
}
}
}
}
ColumnLayout {
id: content
anchors.fill: parent
@ -188,10 +201,22 @@ Item {
}
Keys.onEnterPressed: {
KeyNavigation.tab.forceActiveFocus();
if (root.rightButtonClickedOnEnter && root.clickedFunc && typeof root.clickedFunc === "function") {
clickedFunc()
}
if (KeyNavigation.tab) {
KeyNavigation.tab.forceActiveFocus();
}
}
Keys.onReturnPressed: {
KeyNavigation.tab.forceActiveFocus();
if (root.rightButtonClickedOnEnter &&root.clickedFunc && typeof root.clickedFunc === "function") {
clickedFunc()
}
if (KeyNavigation.tab) {
KeyNavigation.tab.forceActiveFocus();
}
}
}

View file

@ -20,16 +20,23 @@ RadioButton {
property string textColor: "#D7D8DB"
property string selectedTextColor: "#FBB26A"
property string borderFocusedColor: "#D7D8DB"
property int borderFocusedWidth: 1
property string imageSource
property bool showImage
hoverEnabled: true
focusPolicy: Qt.TabFocus
indicator: Rectangle {
id: background
anchors.verticalCenter: parent.verticalCenter
border.color: root.focus ? root.borderFocusedColor : "transparent"
border.width: root.focus ? root.borderFocusedWidth : 0
implicitWidth: 56
implicitHeight: 56
radius: 16
@ -51,6 +58,10 @@ RadioButton {
PropertyAnimation { duration: 200 }
}
Behavior on border.color {
PropertyAnimation { duration: 200 }
}
Image {
source: {
if (showImage) {

View file

@ -18,6 +18,8 @@ import "../Components"
PageType {
id: root
defaultActiveFocusItem: focusItem
Connections {
target: PageController
@ -38,7 +40,15 @@ PageType {
anchors.topMargin: 34
anchors.bottomMargin: 34
Item {
id: focusItem
KeyNavigation.tab: loggingButton.visible ?
loggingButton :
connectButton
}
BasicButtonType {
id: loggingButton
property bool isLoggingEnabled: SettingsController.isLoggingEnabled
Layout.alignment: Qt.AlignHCenter
@ -55,6 +65,11 @@ PageType {
visible: isLoggingEnabled ? true : false
text: qsTr("Logging enabled")
Keys.onEnterPressed: loggingButton.clicked()
Keys.onReturnPressed: loggingButton.clicked()
KeyNavigation.tab: connectButton
onClicked: {
PageController.goToPage(PageEnum.PageSettingsLogging)
}
@ -64,9 +79,11 @@ PageType {
id: connectButton
Layout.fillHeight: true
Layout.alignment: Qt.AlignCenter
KeyNavigation.tab: splitTunnelingButton
}
BasicButtonType {
id: splitTunnelingButton
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
Layout.bottomMargin: 34
leftPadding: 16
@ -90,6 +107,11 @@ PageType {
imageSource: isSplitTunnelingEnabled ? "qrc:/images/controls/split-tunneling.svg" : ""
rightImageSource: "qrc:/images/controls/chevron-down.svg"
Keys.onEnterPressed: splitTunnelingButton.clicked()
Keys.onReturnPressed: splitTunnelingButton.clicked()
KeyNavigation.tab: drawer
onClicked: {
homeSplitTunnelingDrawer.open()
}
@ -97,6 +119,12 @@ PageType {
HomeSplitTunnelingDrawer {
id: homeSplitTunnelingDrawer
parent: root
onClosed: {
if (!GC.isMobile()) {
focusItem.forceActiveFocus()
}
}
}
}
}
@ -107,11 +135,26 @@ PageType {
id: drawer
anchors.fill: parent
onClosed: {
if (!GC.isMobile()) {
focusItem.forceActiveFocus()
}
}
collapsedContent: Item {
implicitHeight: Qt.platform.os !== "ios" ? root.height * 0.9 : screen.height * 0.77
Component.onCompleted: {
drawer.expandedHeight = implicitHeight
}
Connections {
target: drawer
enabled: !GC.isMobile()
function onActiveFocusChanged() {
if (drawer.activeFocus && !drawer.isOpened) {
collapsedButtonChevron.forceActiveFocus()
}
}
}
ColumnLayout {
id: collapsed
@ -178,6 +221,8 @@ PageType {
text: ServersModel.defaultServerName
horizontalAlignment: Qt.AlignHCenter
KeyNavigation.tab: tabBar
Behavior on opacity {
PropertyAnimation { duration: 200 }
}
@ -201,6 +246,11 @@ PageType {
topPadding: 4
bottomPadding: 3
Keys.onEnterPressed: collapsedButtonChevron.clicked()
Keys.onReturnPressed: collapsedButtonChevron.clicked()
Keys.onTabPressed: lastItemTabClicked()
onClicked: {
if (drawer.isCollapsed) {
drawer.open()
@ -217,6 +267,16 @@ PageType {
}
}
Connections {
target: drawer
enabled: !GC.isMobile()
function onIsCollapsedChanged() {
if (!drawer.isCollapsed) {
focusItem1.forceActiveFocus()
}
}
}
ColumnLayout {
id: serversMenuHeader
@ -230,6 +290,11 @@ PageType {
visible: !ServersModel.isDefaultServerFromApi
Item {
id: focusItem1
KeyNavigation.tab: containersDropDown
}
DropDownType {
id: containersDropDown
@ -252,9 +317,16 @@ PageType {
}
drawerParent: root
KeyNavigation.tab: serversMenuContent
listView: HomeContainersListView {
id: containersListView
rootWidth: root.width
onVisibleChanged: {
if (containersDropDown.visible && !GC.isMobile()) {
focusItem1.forceActiveFocus()
}
}
Connections {
target: ServersModel
@ -317,9 +389,43 @@ PageType {
policy: serversMenuContent.height >= serversMenuContent.contentHeight ? ScrollBar.AlwaysOff : ScrollBar.AlwaysOn
}
activeFocusOnTab: true
focus: true
property int focusItemIndex: 0
onActiveFocusChanged: {
if (activeFocus) {
serversMenuContent.focusItemIndex = 0
serversMenuContent.itemAtIndex(focusItemIndex).forceActiveFocus()
}
}
onFocusItemIndexChanged: {
const focusedElement = serversMenuContent.itemAtIndex(focusItemIndex)
if (focusedElement) {
if (focusedElement.y + focusedElement.height > serversMenuContent.height) {
serversMenuContent.contentY = focusedElement.y + focusedElement.height - serversMenuContent.height
} else {
serversMenuContent.contentY = 0
}
}
}
Keys.onUpPressed: scrollBar.decrease()
Keys.onDownPressed: scrollBar.increase()
Connections {
target: drawer
enabled: !GC.isMobile()
function onIsCollapsedChanged() {
if (drawer.isCollapsed) {
const item = serversMenuContent.itemAtIndex(serversMenuContent.focusItemIndex)
if (item) { item.serverRadioButtonProperty.focus = false }
}
}
}
Connections {
target: ServersModel
function onDefaultServerIndexChanged(serverIndex) {
@ -333,10 +439,17 @@ PageType {
id: menuContentDelegate
property variant delegateData: model
property VerticalRadioButton serverRadioButtonProperty: serverRadioButton
implicitWidth: serversMenuContent.width
implicitHeight: serverRadioButtonContent.implicitHeight
onActiveFocusChanged: {
if (activeFocus) {
serverRadioButton.forceActiveFocus()
}
}
ColumnLayout {
id: serverRadioButtonContent
@ -377,9 +490,14 @@ PageType {
cursorShape: Qt.PointingHandCursor
enabled: false
}
Keys.onTabPressed: serverInfoButton.forceActiveFocus()
Keys.onEnterPressed: serverRadioButton.clicked()
Keys.onReturnPressed: serverRadioButton.clicked()
}
ImageButtonType {
id: serverInfoButton
image: "qrc:/images/controls/settings.svg"
imageColor: "#D7D8DB"
@ -388,6 +506,18 @@ PageType {
z: 1
Keys.onTabPressed: {
if (serversMenuContent.focusItemIndex < serversMenuContent.count - 1) {
serversMenuContent.focusItemIndex++
serversMenuContent.itemAtIndex(serversMenuContent.focusItemIndex).forceActiveFocus()
} else {
focusItem1.forceActiveFocus()
serversMenuContent.contentY = 0
}
}
Keys.onEnterPressed: serverInfoButton.clicked()
Keys.onReturnPressed: serverInfoButton.clicked()
onClicked: function() {
ServersModel.processedIndex = index
PageController.goToPage(PageEnum.PageSettingsServerInfo)

View file

@ -18,8 +18,18 @@ PageType {
defaultActiveFocusItem: listview.currentItem.portTextField.textField
Item {
id: focusItem
onFocusChanged: {
if (activeFocus) {
fl.ensureVisible(focusItem)
}
}
KeyNavigation.tab: backButton
}
ColumnLayout {
id: backButton
id: backButtonLayout
anchors.top: parent.top
anchors.left: parent.left
@ -28,12 +38,14 @@ PageType {
anchors.topMargin: 20
BackButtonType {
id: backButton
KeyNavigation.tab: listview.currentItem.portTextField.textField
}
}
FlickableType {
id: fl
anchors.top: backButton.bottom
anchors.top: backButtonLayout.bottom
anchors.bottom: parent.bottom
contentHeight: content.implicitHeight
@ -47,8 +59,6 @@ PageType {
enabled: ServersModel.isProcessedServerHasWriteAccess()
ListView {
id: listview
width: parent.width
@ -94,6 +104,7 @@ PageType {
textFieldText: port
textField.maximumLength: 5
textField.validator: IntValidator { bottom: 1; top: 65535 }
parentFlickable: fl
textField.onEditingFinished: {
if (textFieldText !== port) {
@ -103,7 +114,7 @@ PageType {
checkEmptyText: true
KeyNavigation.tab: junkPacketCountTextField.textField
KeyNavigation.tab: mtuTextField.textField
}
TextFieldWithHeaderType {
@ -124,6 +135,7 @@ PageType {
}
}
checkEmptyText: true
KeyNavigation.tab: junkPacketCountTextField.textField
}
TextFieldWithHeaderType {
@ -134,6 +146,7 @@ PageType {
headerText: "Jc - Junk packet count"
textFieldText: junkPacketCount
textField.validator: IntValidator { bottom: 0 }
parentFlickable: fl
textField.onEditingFinished: {
if (textFieldText === "") {
@ -158,6 +171,7 @@ PageType {
headerText: "Jmin - Junk packet minimum size"
textFieldText: junkPacketMinSize
textField.validator: IntValidator { bottom: 0 }
parentFlickable: fl
textField.onEditingFinished: {
if (textFieldText !== junkPacketMinSize) {
@ -178,6 +192,7 @@ PageType {
headerText: "Jmax - Junk packet maximum size"
textFieldText: junkPacketMaxSize
textField.validator: IntValidator { bottom: 0 }
parentFlickable: fl
textField.onEditingFinished: {
if (textFieldText !== junkPacketMaxSize) {
@ -198,6 +213,7 @@ PageType {
headerText: "S1 - Init packet junk size"
textFieldText: initPacketJunkSize
textField.validator: IntValidator { bottom: 0 }
parentFlickable: fl
textField.onEditingFinished: {
if (textFieldText !== initPacketJunkSize) {
@ -218,6 +234,7 @@ PageType {
headerText: "S2 - Response packet junk size"
textFieldText: responsePacketJunkSize
textField.validator: IntValidator { bottom: 0 }
parentFlickable: fl
textField.onEditingFinished: {
if (textFieldText !== responsePacketJunkSize) {
@ -238,6 +255,7 @@ PageType {
headerText: "H1 - Init packet magic header"
textFieldText: initPacketMagicHeader
textField.validator: IntValidator { bottom: 0 }
parentFlickable: fl
textField.onEditingFinished: {
if (textFieldText !== initPacketMagicHeader) {
@ -258,6 +276,7 @@ PageType {
headerText: "H2 - Response packet magic header"
textFieldText: responsePacketMagicHeader
textField.validator: IntValidator { bottom: 0 }
parentFlickable: fl
textField.onEditingFinished: {
if (textFieldText !== responsePacketMagicHeader) {
@ -278,6 +297,7 @@ PageType {
headerText: "H4 - Transport packet magic header"
textFieldText: transportPacketMagicHeader
textField.validator: IntValidator { bottom: 0 }
parentFlickable: fl
textField.onEditingFinished: {
if (textFieldText !== transportPacketMagicHeader) {
@ -294,6 +314,7 @@ PageType {
id: underloadPacketMagicHeaderTextField
Layout.fillWidth: true
Layout.topMargin: 16
parentFlickable: fl
headerText: "H3 - Underload packet magic header"
textFieldText: underloadPacketMagicHeader
@ -312,6 +333,7 @@ PageType {
BasicButtonType {
id: saveRestartButton
parentFlickable: fl
Layout.fillWidth: true
Layout.topMargin: 24
@ -330,7 +352,9 @@ PageType {
text: qsTr("Save")
onClicked: {
Keys.onTabPressed: lastItemTabClicked(focusItem)
clickedFunc: function() {
if (AwgConfigModel.isHeadersEqual(underloadPacketMagicHeaderTextField.textField.text,
transportPacketMagicHeaderTextField.textField.text,
responsePacketMagicHeaderTextField.textField.text,
@ -362,6 +386,9 @@ PageType {
InstallController.updateContainer(AwgConfigModel.getConfig())
}
var noButtonFunction = function() {
if (!GC.isMobile()) {
saveRestartButton.forceActiveFocus()
}
}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
}

View file

@ -17,8 +17,13 @@ PageType {
defaultActiveFocusItem: listview.currentItem.trafficFromField.textField
Item {
id: focusItem
KeyNavigation.tab: backButton
}
ColumnLayout {
id: backButton
id: backButtonLayout
anchors.top: parent.top
anchors.left: parent.left
@ -27,12 +32,14 @@ PageType {
anchors.topMargin: 20
BackButtonType {
id: backButton
KeyNavigation.tab: listview.currentItem.trafficFromField.textField
}
}
FlickableType {
id: fl
anchors.top: backButton.bottom
anchors.top: backButtonLayout.bottom
anchors.bottom: parent.bottom
contentHeight: content.implicitHeight
@ -123,7 +130,7 @@ PageType {
}
}
KeyNavigation.tab: saveRestartButton
KeyNavigation.tab: cipherDropDown
}
DropDownType {
@ -135,6 +142,7 @@ PageType {
headerText: qsTr("Cipher")
drawerParent: root
KeyNavigation.tab: saveRestartButton
listView: ListViewWithRadioButtonType {
id: cipherListView
@ -175,6 +183,7 @@ PageType {
Layout.bottomMargin: 24
text: qsTr("Save")
Keys.onTabPressed: lastItemTabClicked(focusItem)
clickedFunc: function() {
forceActiveFocus()

View file

@ -18,8 +18,18 @@ PageType {
defaultActiveFocusItem: listview.currentItem.vpnAddressSubnetTextField.textField
Item {
id: focusItem
KeyNavigation.tab: backButton
onActiveFocusChanged: {
if (activeFocus) {
fl.ensureVisible(focusItem)
}
}
}
ColumnLayout {
id: backButton
id: backButtonLayout
anchors.top: parent.top
anchors.left: parent.left
@ -28,12 +38,14 @@ PageType {
anchors.topMargin: 20
BackButtonType {
id: backButton
KeyNavigation.tab: listview.currentItem.vpnAddressSubnetTextField.textField
}
}
FlickableType {
id: fl
anchors.top: backButton.bottom
anchors.top: backButtonLayout.bottom
anchors.bottom: parent.bottom
contentHeight: content.implicitHeight
@ -90,13 +102,14 @@ PageType {
headerText: qsTr("VPN address subnet")
textFieldText: subnetAddress
parentFlickable: fl
KeyNavigation.tab: transportProtoSelector
textField.onEditingFinished: {
if (textFieldText !== subnetAddress) {
subnetAddress = textFieldText
}
}
KeyNavigation.tab: portTextField.enabled ? portTextField.textField : saveRestartButton
}
ParagraphTextType {
@ -107,6 +120,7 @@ PageType {
}
TransportProtoSelector {
id: transportProtoSelector
Layout.fillWidth: true
Layout.topMargin: 16
rootWidth: root.width
@ -117,6 +131,8 @@ PageType {
return transportProto === "tcp" ? 1 : 0
}
KeyNavigation.tab: portTextField.enabled ? portTextField.textField : autoNegotiateEncryprionSwitcher
onCurrentIndexChanged: {
if (transportProto === "tcp" && currentIndex === 0) {
transportProto = "udp"
@ -131,6 +147,7 @@ PageType {
Layout.fillWidth: true
Layout.topMargin: 40
parentFlickable: fl
enabled: isPortEditable
@ -145,7 +162,7 @@ PageType {
}
}
KeyNavigation.tab: saveRestartButton
KeyNavigation.tab: autoNegotiateEncryprionSwitcher
}
SwitcherType {
@ -153,6 +170,7 @@ PageType {
Layout.fillWidth: true
Layout.topMargin: 24
parentFlickable: fl
text: qsTr("Auto-negotiate encryption")
checked: autoNegotiateEncryprion
@ -162,6 +180,10 @@ PageType {
autoNegotiateEncryprion = checked
}
}
KeyNavigation.tab: hashDropDown.enabled ?
hashDropDown :
tlsAuthCheckBox
}
DropDownType {
@ -175,6 +197,10 @@ PageType {
headerText: qsTr("Hash")
drawerParent: root
parentFlickable: fl
KeyNavigation.tab: cipherDropDown.enabled ?
cipherDropDown :
tlsAuthCheckBox
listView: ListViewWithRadioButtonType {
id: hashListView
@ -223,6 +249,9 @@ PageType {
headerText: qsTr("Cipher")
drawerParent: root
parentFlickable: fl
KeyNavigation.tab: tlsAuthCheckBox
listView: ListViewWithRadioButtonType {
id: cipherListView
@ -261,24 +290,40 @@ PageType {
}
Rectangle {
id: contentRect
Layout.fillWidth: true
Layout.topMargin: 32
Layout.preferredHeight: checkboxLayout.implicitHeight
color: "#1C1D21"
radius: 16
Connections {
target: tlsAuthCheckBox
enabled: !GC.isMobile()
function onFocusChanged() {
if (tlsAuthCheckBox.activeFocus) {
fl.ensureVisible(contentRect)
}
}
}
ColumnLayout {
id: checkboxLayout
anchors.fill: parent
CheckBoxType {
id: tlsAuthCheckBox
Layout.fillWidth: true
text: qsTr("TLS auth")
checked: tlsAuth
KeyNavigation.tab: blockDnsCheckBox
onCheckedChanged: {
if (checked !== tlsAuth) {
console.log("tlsAuth changed to: " + checked)
tlsAuth = checked
}
}
@ -287,11 +332,14 @@ PageType {
DividerType {}
CheckBoxType {
id: blockDnsCheckBox
Layout.fillWidth: true
text: qsTr("Block DNS requests outside of VPN")
checked: blockDns
KeyNavigation.tab: additionalClientCommandsSwitcher
onCheckedChanged: {
if (checked !== blockDns) {
blockDns = checked
@ -305,6 +353,10 @@ PageType {
id: additionalClientCommandsSwitcher
Layout.fillWidth: true
Layout.topMargin: 32
parentFlickable: fl
KeyNavigation.tab: additionalClientCommandsTextArea.visible ?
additionalClientCommandsTextArea.textArea :
additionalServerCommandsSwitcher
checked: additionalClientCommands !== ""
@ -318,10 +370,13 @@ PageType {
}
TextAreaType {
id: additionalClientCommandsTextArea
Layout.fillWidth: true
Layout.topMargin: 16
visible: additionalClientCommandsSwitcher.checked
KeyNavigation.tab: additionalServerCommandsSwitcher
parentFlickable: fl
textAreaText: additionalClientCommands
placeholderText: qsTr("Commands:")
@ -337,6 +392,10 @@ PageType {
id: additionalServerCommandsSwitcher
Layout.fillWidth: true
Layout.topMargin: 16
parentFlickable: fl
KeyNavigation.tab: additionalServerCommandsTextArea.visible ?
additionalServerCommandsTextArea.textArea :
saveRestartButton
checked: additionalServerCommands !== ""
@ -350,6 +409,7 @@ PageType {
}
TextAreaType {
id: additionalServerCommandsTextArea
Layout.fillWidth: true
Layout.topMargin: 16
@ -357,7 +417,8 @@ PageType {
textAreaText: additionalServerCommands
placeholderText: qsTr("Commands:")
parentFlickable: fl
KeyNavigation.tab: saveRestartButton
textArea.onEditingFinished: {
if (additionalServerCommands !== textAreaText) {
additionalServerCommands = textAreaText
@ -373,6 +434,8 @@ PageType {
Layout.bottomMargin: 24
text: qsTr("Save")
parentFlickable: fl
Keys.onTabPressed: lastItemTabClicked(focusItem)
clickedFunc: function() {
forceActiveFocus()

View file

@ -18,6 +18,13 @@ import "../Components"
PageType {
id: root
defaultActiveFocusItem: focusItem
Item {
id: focusItem
KeyNavigation.tab: backButton
}
ColumnLayout {
id: header
@ -28,6 +35,8 @@ PageType {
anchors.topMargin: 20
BackButtonType {
id: backButton
KeyNavigation.tab: listView
}
HeaderType {
@ -55,16 +64,29 @@ PageType {
anchors.topMargin: 32
ListView {
id: listView
width: parent.width
height: contentItem.height
clip: true
interactive: false
model: ProtocolsModel
activeFocusOnTab: true
focus: true
onActiveFocusChanged: {
if (focus) {
listView.currentIndex = 0
listView.currentItem.focusItem.forceActiveFocus()
}
}
delegate: Item {
implicitWidth: parent.width
implicitHeight: delegateContent.implicitHeight
property alias focusItem: button
ColumnLayout {
id: delegateContent
@ -81,6 +103,8 @@ PageType {
configContentDrawer.open()
}
KeyNavigation.tab: removeButton
MouseArea {
anchors.fill: button
cursorShape: Qt.PointingHandCursor
@ -95,14 +119,33 @@ PageType {
expandedHeight: root.height * 0.9
onClosed: {
if (!GC.isMobile()) {
defaultActiveFocusItem.forceActiveFocus()
}
}
parent: root
anchors.fill: parent
expandedContent: Item {
implicitHeight: configContentDrawer.expandedHeight
Connections {
target: configContentDrawer
enabled: !GC.isMobile()
function onOpened() {
focusItem1.forceActiveFocus()
}
}
Item {
id: focusItem1
KeyNavigation.tab: backButton1
}
BackButtonType {
id: backButton
id: backButton1
anchors.top: parent.top
anchors.left: parent.left
@ -112,10 +155,12 @@ PageType {
backButtonFunction: function() {
configContentDrawer.close()
}
KeyNavigation.tab: focusItem1
}
FlickableType {
anchors.top: backButton.bottom
anchors.top: backButton1.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
@ -180,6 +225,7 @@ PageType {
text: qsTr("Remove ") + ContainersModel.getProcessedContainerName()
textColor: "#EB5757"
Keys.onTabPressed: lastItemTabClicked(focusItem)
clickedFunction: function() {
var headerText = qsTr("Remove %1 from server?").arg(ContainersModel.getProcessedContainerName())
var descriptionText = qsTr("All users with whom you shared a connection with will no longer be able to connect to it.")
@ -191,6 +237,9 @@ PageType {
InstallController.removeProcessedContainer()
}
var noButtonFunction = function() {
if (!GC.isMobile()) {
focusItem.forceActiveFocus()
}
}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)

View file

@ -15,10 +15,17 @@ import "../Components"
PageType {
id: root
defaultActiveFocusItem: listview.currentItem.portTextField.textField
defaultActiveFocusItem: listview.currentItem.focusItemId.enabled ?
listview.currentItem.focusItemId.textField :
focusItem
Item {
id: focusItem
KeyNavigation.tab: backButton
}
ColumnLayout {
id: backButton
id: backButtonLayout
anchors.top: parent.top
anchors.left: parent.left
@ -27,12 +34,16 @@ PageType {
anchors.topMargin: 20
BackButtonType {
id: backButton
KeyNavigation.tab: listview.currentItem.focusItemId.enabled ?
listview.currentItem.focusItemId.textField :
focusItem
}
}
FlickableType {
id: fl
anchors.top: backButton.bottom
anchors.top: backButtonLayout.bottom
anchors.bottom: parent.bottom
contentHeight: content.implicitHeight
@ -60,7 +71,11 @@ PageType {
implicitWidth: listview.width
implicitHeight: col.implicitHeight
property alias portTextField: portTextField
property var focusItemId: portTextField.enabled ?
portTextField :
cipherDropDown.enabled ?
cipherDropDown :
saveRestartButton
ColumnLayout {
id: col
@ -99,7 +114,7 @@ PageType {
}
}
KeyNavigation.tab: saveRestartButton
KeyNavigation.tab: cipherDropDown
}
DropDownType {
@ -113,6 +128,7 @@ PageType {
headerText: qsTr("Cipher")
drawerParent: root
KeyNavigation.tab: saveRestartButton
listView: ListViewWithRadioButtonType {
id: cipherListView
@ -155,6 +171,7 @@ PageType {
enabled: isPortEditable | isCipherEditable
text: qsTr("Save")
Keys.onTabPressed: lastItemTabClicked(focusItem)
clickedFunc: function() {
forceActiveFocus()

View file

@ -15,8 +15,15 @@ import "../Components"
PageType {
id: root
defaultActiveFocusItem: listview
Item {
id: focusItem
KeyNavigation.tab: backButton
}
ColumnLayout {
id: backButton
id: backButtonLayout
anchors.top: parent.top
anchors.left: parent.left
@ -25,12 +32,14 @@ PageType {
anchors.topMargin: 20
BackButtonType {
id: backButton
KeyNavigation.tab: listview
}
}
FlickableType {
id: fl
anchors.top: backButton.bottom
anchors.top: backButtonLayout.bottom
anchors.bottom: parent.bottom
contentHeight: content.implicitHeight
@ -54,7 +63,16 @@ PageType {
model: WireGuardConfigModel
activeFocusOnTab: true
onActiveFocusChanged: {
if (activeFocus) {
listview.itemAtIndex(0)?.focusItemId.forceActiveFocus()
}
}
delegate: Item {
property alias focusItemId: portTextField.textField
implicitWidth: listview.width
implicitHeight: col.implicitHeight
@ -85,6 +103,8 @@ PageType {
textField.maximumLength: 5
textField.validator: IntValidator { bottom: 1; top: 65535 }
KeyNavigation.tab: mtuTextField.textField
textField.onEditingFinished: {
if (textFieldText !== port) {
port = textFieldText
@ -103,6 +123,8 @@ PageType {
textFieldText: mtu
textField.validator: IntValidator { bottom: 576; top: 65535 }
KeyNavigation.tab: saveButton
textField.onEditingFinished: {
if (textFieldText === "") {
textFieldText = "0"
@ -115,6 +137,7 @@ PageType {
}
BasicButtonType {
id: saveButton
Layout.fillWidth: true
Layout.topMargin: 24
Layout.bottomMargin: 24
@ -124,6 +147,8 @@ PageType {
text: qsTr("Save")
Keys.onTabPressed: lastItemTabClicked(focusItem)
onClicked: {
forceActiveFocus()
@ -134,7 +159,11 @@ PageType {
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
InstallController.updateContainer(WireGuardConfigModel.getConfig())
focusItem.forceActiveFocus()
}
Keys.onEnterPressed: saveButton.clicked()
Keys.onReturnPressed: saveButton.clicked()
}
}
}

View file

@ -16,8 +16,15 @@ import "../Components"
PageType {
id: root
defaultActiveFocusItem: listview
Item {
id: focusItem
KeyNavigation.tab: backButton
}
ColumnLayout {
id: backButton
id: backButtonLayout
anchors.top: parent.top
anchors.left: parent.left
@ -26,12 +33,14 @@ PageType {
anchors.topMargin: 20
BackButtonType {
id: backButton
KeyNavigation.tab: listview
}
}
FlickableType {
id: fl
anchors.top: backButton.bottom
anchors.top: backButtonLayout.bottom
anchors.bottom: parent.bottom
contentHeight: content.implicitHeight
@ -55,7 +64,16 @@ PageType {
model: XrayConfigModel
activeFocusOnTab: true
onActiveFocusChanged: {
if (activeFocus) {
listview.itemAtIndex(0)?.focusItemId.forceActiveFocus()
}
}
delegate: Item {
property alias focusItemId: textFieldWithHeaderType.textField
implicitWidth: listview.width
implicitHeight: col.implicitHeight
@ -77,12 +95,15 @@ PageType {
}
TextFieldWithHeaderType {
id: textFieldWithHeaderType
Layout.fillWidth: true
Layout.topMargin: 32
headerText: qsTr("Disguised as traffic from")
textFieldText: site
KeyNavigation.tab: basicButton
textField.onEditingFinished: {
if (textFieldText !== site) {
var tmpText = textFieldText
@ -99,12 +120,15 @@ PageType {
}
BasicButtonType {
id: basicButton
Layout.fillWidth: true
Layout.topMargin: 24
Layout.bottomMargin: 24
text: qsTr("Save")
Keys.onTabPressed: lastItemTabClicked(focusItem)
onClicked: {
forceActiveFocus()
@ -115,7 +139,11 @@ PageType {
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
InstallController.updateContainer(XrayConfigModel.getConfig())
focusItem.forceActiveFocus()
}
Keys.onEnterPressed: basicButton.clicked()
Keys.onReturnPressed: basicButton.clicked()
}
}
}

View file

@ -15,8 +15,15 @@ import "../Components"
PageType {
id: root
defaultActiveFocusItem: focusItem
Item {
id: focusItem
KeyNavigation.tab: backButton
}
ColumnLayout {
id: backButton
id: backButtonLayout
anchors.top: parent.top
anchors.left: parent.left
@ -25,12 +32,14 @@ PageType {
anchors.topMargin: 20
BackButtonType {
id: backButton
KeyNavigation.tab: removeButton
}
}
FlickableType {
id: fl
anchors.top: backButton.bottom
anchors.top: backButtonLayout.bottom
anchors.bottom: parent.bottom
contentHeight: content.implicitHeight
@ -62,6 +71,8 @@ PageType {
text: qsTr("Remove ") + ContainersModel.getProcessedContainerName()
textColor: "#EB5757"
Keys.onTabPressed: root.lastItemTabClicked()
clickedFunction: function() {
var headerText = qsTr("Remove %1 from server?").arg(ContainersModel.getProcessedContainerName())
var yesButtonText = qsTr("Continue")
@ -78,6 +89,9 @@ PageType {
}
}
var noButtonFunction = function() {
if (!GC.isMobile()) {
removeButton.rightButton.forceActiveFocus()
}
}
showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)

View file

@ -15,6 +15,8 @@ import "../Components"
PageType {
id: root
defaultActiveFocusItem: focusItem
Connections {
target: InstallController
@ -23,8 +25,13 @@ PageType {
}
}
Item {
id: focusItem
KeyNavigation.tab: backButton
}
ColumnLayout {
id: backButton
id: backButtonLayout
anchors.top: parent.top
anchors.left: parent.left
@ -33,12 +40,14 @@ PageType {
anchors.topMargin: 20
BackButtonType {
id: backButton
KeyNavigation.tab: listview
}
}
FlickableType {
id: fl
anchors.top: backButton.bottom
anchors.top: backButtonLayout.bottom
anchors.bottom: parent.bottom
contentHeight: content.implicitHeight
@ -62,10 +71,18 @@ PageType {
model: SftpConfigModel
onFocusChanged: {
if (focus) {
listview.currentItem.focusItem.forceActiveFocus()
}
}
delegate: Item {
implicitWidth: listview.width
implicitHeight: col.implicitHeight
property alias focusItem: hostLabel.rightButton
ColumnLayout {
id: col
@ -84,9 +101,13 @@ PageType {
}
LabelWithButtonType {
id: hostLabel
Layout.fillWidth: true
Layout.topMargin: 32
parentFlickable: fl
KeyNavigation.tab: portLabel.rightButton
text: qsTr("Host")
descriptionText: ServersModel.getProcessedServerData("hostName")
@ -98,10 +119,14 @@ PageType {
clickedFunction: function() {
GC.copyToClipBoard(descriptionText)
PageController.showNotificationMessage(qsTr("Copied"))
if (!GC.isMobile()) {
this.rightButton.forceActiveFocus()
}
}
}
LabelWithButtonType {
id: portLabel
Layout.fillWidth: true
text: qsTr("Port")
@ -109,16 +134,23 @@ PageType {
descriptionOnTop: true
parentFlickable: fl
KeyNavigation.tab: usernameLabel.rightButton
rightImageSource: "qrc:/images/controls/copy.svg"
rightImageColor: "#D7D8DB"
clickedFunction: function() {
GC.copyToClipBoard(descriptionText)
PageController.showNotificationMessage(qsTr("Copied"))
if (!GC.isMobile()) {
this.rightButton.forceActiveFocus()
}
}
}
LabelWithButtonType {
id: usernameLabel
Layout.fillWidth: true
text: qsTr("User name")
@ -126,16 +158,23 @@ PageType {
descriptionOnTop: true
parentFlickable: fl
KeyNavigation.tab: passwordLabel.rightButton
rightImageSource: "qrc:/images/controls/copy.svg"
rightImageColor: "#D7D8DB"
clickedFunction: function() {
GC.copyToClipBoard(descriptionText)
PageController.showNotificationMessage(qsTr("Copied"))
if (!GC.isMobile()) {
this.rightButton.forceActiveFocus()
}
}
}
LabelWithButtonType {
id: passwordLabel
Layout.fillWidth: true
text: qsTr("Password")
@ -143,16 +182,29 @@ PageType {
descriptionOnTop: true
parentFlickable: fl
Keys.onTabPressed: {
if (mountButton.visible) {
mountButton.forceActiveFocus()
} else {
detailedInstructionsButton.forceActiveFocus()
}
}
rightImageSource: "qrc:/images/controls/copy.svg"
rightImageColor: "#D7D8DB"
clickedFunction: function() {
GC.copyToClipBoard(descriptionText)
PageController.showNotificationMessage(qsTr("Copied"))
if (!GC.isMobile()) {
this.rightButton.forceActiveFocus()
}
}
}
BasicButtonType {
id: mountButton
visible: !GC.isMobile()
Layout.fillWidth: true
@ -168,13 +220,16 @@ PageType {
textColor: "#D7D8DB"
borderWidth: 1
parentFlickable: fl
KeyNavigation.tab: detailedInstructionsButton
text: qsTr("Mount folder on device")
clickedFunc: function() {
PageController.showBusyIndicator(true)
InstallController.mountSftpDrive(port, password, username)
PageController.showBusyIndicator(false)
}
}
}
ParagraphTextType {
@ -216,6 +271,7 @@ PageType {
}
BasicButtonType {
id: detailedInstructionsButton
Layout.topMargin: 16
Layout.bottomMargin: 16
Layout.leftMargin: 8
@ -229,12 +285,16 @@ PageType {
text: qsTr("Detailed instructions")
parentFlickable: fl
KeyNavigation.tab: removeButton
clickedFunc: function() {
// Qt.openUrlExternally("https://github.com/amnezia-vpn/desktop-client/releases/latest")
}
}
BasicButtonType {
id: removeButton
Layout.topMargin: 24
Layout.bottomMargin: 16
Layout.leftMargin: 8
@ -245,6 +305,9 @@ PageType {
pressedColor: Qt.rgba(1, 1, 1, 0.12)
textColor: "#EB5757"
parentFlickable: fl
Keys.onTabPressed: lastItemTabClicked()
text: qsTr("Remove SFTP and all data stored there")
clickedFunc: function() {
@ -257,6 +320,9 @@ PageType {
InstallController.removeProcessedContainer()
}
var noButtonFunction = function() {
if (!GC.isMobile()) {
removeButton.forceActiveFocus()
}
}
showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)

View file

@ -16,6 +16,8 @@ import "../Components"
PageType {
id: root
defaultActiveFocusItem: focusItem
Connections {
target: InstallController
@ -24,8 +26,13 @@ PageType {
}
}
Item {
id: focusItem
KeyNavigation.tab: backButton
}
ColumnLayout {
id: backButton
id: backButtonLayout
anchors.top: parent.top
anchors.left: parent.left
@ -34,12 +41,14 @@ PageType {
anchors.topMargin: 20
BackButtonType {
id: backButton
KeyNavigation.tab: websiteName.rightButton
}
}
FlickableType {
id: fl
anchors.top: backButton.bottom
anchors.top: backButtonLayout.bottom
anchors.bottom: parent.bottom
contentHeight: content.implicitHeight
@ -61,6 +70,7 @@ PageType {
}
LabelWithButtonType {
id: websiteName
Layout.fillWidth: true
Layout.topMargin: 32
@ -77,9 +87,14 @@ PageType {
rightImageSource: "qrc:/images/controls/copy.svg"
rightImageColor: "#D7D8DB"
KeyNavigation.tab: removeButton
clickedFunction: function() {
GC.copyToClipBoard(descriptionText)
PageController.showNotificationMessage(qsTr("Copied"))
if (!GC.isMobile()) {
this.rightButton.forceActiveFocus()
}
}
}
@ -113,6 +128,7 @@ PageType {
}
BasicButtonType {
id: removeButton
Layout.topMargin: 24
Layout.bottomMargin: 16
Layout.leftMargin: 8
@ -125,6 +141,8 @@ PageType {
text: qsTr("Remove website")
Keys.onTabPressed: lastItemTabClicked(focusItem)
clickedFunc: function() {
var headerText = qsTr("The site with all data will be removed from the tor network.")
var yesButtonText = qsTr("Continue")
@ -135,6 +153,9 @@ PageType {
InstallController.removeProcessedContainer()
}
var noButtonFunction = function() {
if (!GC.isMobile()) {
removeButton.forceActiveFocus()
}
}
showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)

View file

@ -13,6 +13,8 @@ import "../Config"
PageType {
id: root
defaultActiveFocusItem: header
FlickableType {
id: fl
anchors.top: parent.top
@ -29,15 +31,19 @@ PageType {
spacing: 0
HeaderType {
id: header
Layout.fillWidth: true
Layout.topMargin: 24
Layout.rightMargin: 16
Layout.leftMargin: 16
headerText: qsTr("Settings")
KeyNavigation.tab: account.rightButton
}
LabelWithButtonType {
id: account
Layout.fillWidth: true
Layout.topMargin: 16
@ -48,11 +54,14 @@ PageType {
clickedFunction: function() {
PageController.goToPage(PageEnum.PageSettingsServersList)
}
KeyNavigation.tab: connection.rightButton
}
DividerType {}
LabelWithButtonType {
id: connection
Layout.fillWidth: true
text: qsTr("Connection")
@ -62,11 +71,14 @@ PageType {
clickedFunction: function() {
PageController.goToPage(PageEnum.PageSettingsConnection)
}
KeyNavigation.tab: application.rightButton
}
DividerType {}
LabelWithButtonType {
id: application
Layout.fillWidth: true
text: qsTr("Application")
@ -76,11 +88,14 @@ PageType {
clickedFunction: function() {
PageController.goToPage(PageEnum.PageSettingsApplication)
}
KeyNavigation.tab: backup.rightButton
}
DividerType {}
LabelWithButtonType {
id: backup
Layout.fillWidth: true
text: qsTr("Backup")
@ -90,6 +105,8 @@ PageType {
clickedFunction: function() {
PageController.goToPage(PageEnum.PageSettingsBackup)
}
KeyNavigation.tab: about.rightButton
}
DividerType {}
@ -105,18 +122,23 @@ PageType {
clickedFunction: function() {
PageController.goToPage(PageEnum.PageSettingsAbout)
}
KeyNavigation.tab: close
}
DividerType {}
LabelWithButtonType {
id: close
visible: GC.isDesktop()
Layout.fillWidth: true
Layout.preferredHeight: about.height
text: qsTr("Close application")
leftImageSource: "qrc:/images/controls/x-circle.svg"
isLeftImageHoverEnabled: false
isLeftImageHoverEnabled: false
Keys.onTabPressed: lastItemTabClicked(header)
clickedFunction: function() {
PageController.closeApplication()

View file

@ -13,6 +13,19 @@ import "../Components"
PageType {
id: root
defaultActiveFocusItem: focusItem
Item {
id: focusItem
KeyNavigation.tab: backButton
onFocusChanged: {
if (focusItem.activeFocus) {
fl.contentY = 0
}
}
}
BackButtonType {
id: backButton
@ -20,6 +33,8 @@ PageType {
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 20
KeyNavigation.tab: telegramButton
}
FlickableType {
@ -82,6 +97,7 @@ PageType {
}
LabelWithButtonType {
id: telegramButton
Layout.fillWidth: true
Layout.topMargin: 16
@ -89,6 +105,9 @@ PageType {
descriptionText: qsTr("To discuss features")
leftImageSource: "qrc:/images/controls/telegram.svg"
KeyNavigation.tab: mailButton
parentFlickable: fl
clickedFunction: function() {
Qt.openUrlExternally(qsTr("https://t.me/amnezia_vpn_en"))
}
@ -97,40 +116,55 @@ PageType {
DividerType {}
LabelWithButtonType {
id: mailButton
Layout.fillWidth: true
text: qsTr("Mail")
descriptionText: qsTr("For reviews and bug reports")
leftImageSource: "qrc:/images/controls/mail.svg"
KeyNavigation.tab: githubButton
parentFlickable: fl
clickedFunction: function() {
}
}
DividerType {}
LabelWithButtonType {
id: githubButton
Layout.fillWidth: true
text: qsTr("Github")
leftImageSource: "qrc:/images/controls/github.svg"
KeyNavigation.tab: websiteButton
parentFlickable: fl
clickedFunction: function() {
Qt.openUrlExternally(qsTr("https://github.com/amnezia-vpn/amnezia-client"))
}
}
DividerType {}
LabelWithButtonType {
id: websiteButton
Layout.fillWidth: true
text: qsTr("Website")
leftImageSource: "qrc:/images/controls/amnezia.svg"
KeyNavigation.tab: checkUpdatesButton
parentFlickable: fl
clickedFunction: function() {
Qt.openUrlExternally(qsTr("https://amnezia.org"))
}
}
DividerType {}
@ -146,6 +180,7 @@ PageType {
}
BasicButtonType {
id: checkUpdatesButton
Layout.alignment: Qt.AlignHCenter
Layout.topMargin: 8
Layout.bottomMargin: 16
@ -159,12 +194,16 @@ PageType {
text: qsTr("Check for updates")
KeyNavigation.tab: privacyPolicyButton
parentFlickable: fl
clickedFunc: function() {
Qt.openUrlExternally("https://github.com/amnezia-vpn/desktop-client/releases/latest")
}
}
BasicButtonType {
id: privacyPolicyButton
Layout.alignment: Qt.AlignHCenter
Layout.bottomMargin: 16
Layout.topMargin: -15
@ -178,6 +217,9 @@ PageType {
text: qsTr("Privacy Policy")
Keys.onTabPressed: lastItemTabClicked()
parentFlickable: fl
clickedFunc: function() {
Qt.openUrlExternally("https://amnezia.org/en/policy")
}

View file

@ -20,6 +20,8 @@ import "../Components"
PageType {
id: root
defaultActiveFocusItem: focusItem
property bool pageEnabled
Component.onCompleted: {
@ -63,6 +65,11 @@ PageType {
}
}
Item {
id: focusItem
KeyNavigation.tab: backButton
}
ColumnLayout {
id: header
@ -73,6 +80,8 @@ PageType {
anchors.topMargin: 20
BackButtonType {
id: backButton
KeyNavigation.tab: switcher
}
RowLayout {
@ -93,6 +102,10 @@ PageType {
enabled: root.pageEnabled
KeyNavigation.tab: selector.enabled ?
selector :
searchField.textField
checked: AppSplitTunnelingModel.isTunnelingEnabled
onToggled: {
AppSplitTunnelingModel.toggleSplitTunneling(checked)
@ -116,6 +129,8 @@ PageType {
enabled: Qt.platform.os === "android" && root.pageEnabled
KeyNavigation.tab: searchField.textField
listView: ListViewWithRadioButtonType {
rootWidth: root.width
@ -251,6 +266,9 @@ PageType {
textFieldPlaceholderText: qsTr("application name")
buttonImageSource: "qrc:/images/controls/plus.svg"
Keys.onTabPressed: lastItemTabClicked(focusItem)
rightButtonClickedOnEnter: true
clickedFunc: function() {
searchField.focus = false
PageController.showBusyIndicator(true)

View file

@ -13,6 +13,53 @@ import "../Components"
PageType {
id: root
defaultActiveFocusItem: focusItem
function getNextComponentInFocusChain(componentId) {
const componentsList = [focusItem,
backButton,
switcher,
switcherAutoStart,
switcherAutoConnect,
switcherStartMinimized,
labelWithButtonLanguage,
labelWithButtonLogging,
labelWithButtonReset,
]
const idx = componentsList.indexOf(componentId)
if (idx === -1) {
return null
}
let nextIndex = idx + 1
if (nextIndex >= componentsList.length) {
nextIndex = 0
}
if (componentsList[nextIndex].visible) {
if ((nextIndex) >= 6) {
return componentsList[nextIndex].rightButton
} else {
return componentsList[nextIndex]
}
} else {
return getNextComponentInFocusChain(componentsList[nextIndex])
}
}
Item {
id: focusItem
KeyNavigation.tab: root.getNextComponentInFocusChain(focusItem)
onFocusChanged: {
if (focusItem.activeFocus) {
fl.contentY = 0
}
}
}
BackButtonType {
id: backButton
@ -20,6 +67,8 @@ PageType {
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 20
KeyNavigation.tab: root.getNextComponentInFocusChain(backButton)
}
FlickableType {
@ -44,6 +93,7 @@ PageType {
}
SwitcherType {
id: switcher
visible: GC.isMobile()
Layout.fillWidth: true
@ -57,6 +107,9 @@ PageType {
SettingsController.toggleScreenshotsEnabled(checked)
}
}
KeyNavigation.tab: root.getNextComponentInFocusChain(switcher)
parentFlickable: fl
}
DividerType {
@ -64,6 +117,7 @@ PageType {
}
SwitcherType {
id: switcherAutoStart
visible: !GC.isMobile()
Layout.fillWidth: true
@ -72,6 +126,9 @@ PageType {
text: qsTr("Auto start")
descriptionText: qsTr("Launch the application every time the device is starts")
KeyNavigation.tab: root.getNextComponentInFocusChain(switcherAutoStart)
parentFlickable: fl
checked: SettingsController.isAutoStartEnabled()
onCheckedChanged: {
if (checked !== SettingsController.isAutoStartEnabled()) {
@ -85,6 +142,7 @@ PageType {
}
SwitcherType {
id: switcherAutoConnect
visible: !GC.isMobile()
Layout.fillWidth: true
@ -93,6 +151,9 @@ PageType {
text: qsTr("Auto connect")
descriptionText: qsTr("Connect to VPN on app start")
KeyNavigation.tab: root.getNextComponentInFocusChain(switcherAutoConnect)
parentFlickable: fl
checked: SettingsController.isAutoConnectEnabled()
onCheckedChanged: {
if (checked !== SettingsController.isAutoConnectEnabled()) {
@ -106,6 +167,7 @@ PageType {
}
SwitcherType {
id: switcherStartMinimized
visible: !GC.isMobile()
Layout.fillWidth: true
@ -114,6 +176,9 @@ PageType {
text: qsTr("Start minimized")
descriptionText: qsTr("Launch application minimized")
KeyNavigation.tab: root.getNextComponentInFocusChain(switcherStartMinimized)
parentFlickable: fl
checked: SettingsController.isStartMinimizedEnabled()
onCheckedChanged: {
if (checked !== SettingsController.isStartMinimizedEnabled()) {
@ -127,12 +192,16 @@ PageType {
}
LabelWithButtonType {
id: labelWithButtonLanguage
Layout.fillWidth: true
text: qsTr("Language")
descriptionText: LanguageModel.currentLanguageName
rightImageSource: "qrc:/images/controls/chevron-right.svg"
KeyNavigation.tab: root.getNextComponentInFocusChain(labelWithButtonLanguage)
parentFlickable: fl
clickedFunction: function() {
selectLanguageDrawer.open()
}
@ -142,12 +211,16 @@ PageType {
DividerType {}
LabelWithButtonType {
id: labelWithButtonLogging
Layout.fillWidth: true
text: qsTr("Logging")
descriptionText: SettingsController.isLoggingEnabled ? qsTr("Enabled") : qsTr("Disabled")
rightImageSource: "qrc:/images/controls/chevron-right.svg"
KeyNavigation.tab: root.getNextComponentInFocusChain(labelWithButtonLogging)
parentFlickable: fl
clickedFunction: function() {
PageController.goToPage(PageEnum.PageSettingsLogging)
}
@ -156,12 +229,16 @@ PageType {
DividerType {}
LabelWithButtonType {
id: labelWithButtonReset
Layout.fillWidth: true
text: qsTr("Reset settings and remove all data from the application")
rightImageSource: "qrc:/images/controls/chevron-right.svg"
textColor: "#EB5757"
Keys.onTabPressed: lastItemTabClicked()
parentFlickable: fl
clickedFunction: function() {
var headerText = qsTr("Reset settings and remove all data from the application?")
var descriptionText = qsTr("All settings will be reset to default. All installed AmneziaVPN services will still remain on the server.")
@ -176,8 +253,15 @@ PageType {
SettingsController.clearSettings()
PageController.replaceStartPage()
}
if (!GC.isMobile()) {
root.defaultActiveFocusItem.forceActiveFocus()
}
}
var noButtonFunction = function() {
if (!GC.isMobile()) {
root.defaultActiveFocusItem.forceActiveFocus()
}
}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
@ -193,5 +277,11 @@ PageType {
width: root.width
height: root.height
onClosed: {
if (!GC.isMobile()) {
focusItem.forceActiveFocus()
}
}
}
}

View file

@ -16,6 +16,8 @@ import "../Controls2/TextTypes"
PageType {
id: root
defaultActiveFocusItem: focusItem
Connections {
target: SettingsController
@ -34,6 +36,11 @@ PageType {
}
}
Item {
id: focusItem
KeyNavigation.tab: backButton
}
BackButtonType {
id: backButton
@ -41,6 +48,8 @@ PageType {
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 20
KeyNavigation.tab: makeBackupButton
}
FlickableType {
@ -102,9 +111,12 @@ PageType {
PageController.showNotificationMessage(qsTr("Backup file saved"))
}
}
KeyNavigation.tab: restoreBackupButton
}
BasicButtonType {
id: restoreBackupButton
Layout.fillWidth: true
Layout.topMargin: -8
@ -124,6 +136,8 @@ PageType {
restoreBackup(filePath)
}
}
Keys.onTabPressed: lastItemTabClicked()
}
}
}

View file

@ -11,8 +11,15 @@ import "../Config"
PageType {
id: root
defaultActiveFocusItem: focusItem
property bool isAppSplitTinnelingEnabled: Qt.platform.os === "windows" || Qt.platform.os === "android"
Item {
id: focusItem
KeyNavigation.tab: backButton
}
BackButtonType {
id: backButton
@ -20,6 +27,8 @@ PageType {
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 20
KeyNavigation.tab: amneziaDnsSwitch
}
FlickableType {
@ -44,6 +53,7 @@ PageType {
}
SwitcherType {
id: amneziaDnsSwitch
Layout.fillWidth: true
Layout.margins: 16
@ -56,11 +66,14 @@ PageType {
SettingsController.toggleAmneziaDns(checked)
}
}
KeyNavigation.tab: dnsServersButton.rightButton
}
DividerType {}
LabelWithButtonType {
id: dnsServersButton
Layout.fillWidth: true
text: qsTr("DNS servers")
@ -70,11 +83,14 @@ PageType {
clickedFunction: function() {
PageController.goToPage(PageEnum.PageSettingsDns)
}
KeyNavigation.tab: splitTunnelingButton.rightButton
}
DividerType {}
LabelWithButtonType {
id: splitTunnelingButton
Layout.fillWidth: true
text: qsTr("Site-based split tunneling")
@ -84,6 +100,10 @@ PageType {
clickedFunction: function() {
PageController.goToPage(PageEnum.PageSettingsSplitTunneling)
}
Keys.onTabPressed: splitTunnelingButton2.visible ?
splitTunnelingButton2.forceActiveFocus() :
lastItemTabClicked()
}
DividerType {
@ -91,6 +111,7 @@ PageType {
}
LabelWithButtonType {
id: splitTunnelingButton2
visible: root.isAppSplitTinnelingEnabled
Layout.fillWidth: true
@ -102,6 +123,8 @@ PageType {
clickedFunction: function() {
PageController.goToPage(PageEnum.PageSettingsAppSplitTunneling)
}
Keys.onTabPressed: lastItemTabClicked()
}
DividerType {

View file

@ -15,6 +15,11 @@ PageType {
defaultActiveFocusItem: primaryDns.textField
Item {
id: focusItem
KeyNavigation.tab: backButton
}
BackButtonType {
id: backButton
@ -22,6 +27,8 @@ PageType {
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 20
KeyNavigation.tab: root.defaultActiveFocusItem
}
FlickableType {
@ -87,10 +94,11 @@ PageType {
regularExpression: InstallController.ipAddressRegExp()
}
KeyNavigation.tab: saveButton
KeyNavigation.tab: restoreDefaultButton
}
BasicButtonType {
id: restoreDefaultButton
Layout.fillWidth: true
defaultColor: "transparent"
@ -113,12 +121,21 @@ PageType {
SettingsController.secondaryDns = "1.0.0.1"
secondaryDns.textFieldText = SettingsController.secondaryDns
PageController.showNotificationMessage(qsTr("Settings have been reset"))
if (!GC.isMobile()) {
defaultActiveFocusItem.forceActiveFocus()
}
}
var noButtonFunction = function() {
if (!GC.isMobile()) {
defaultActiveFocusItem.forceActiveFocus()
}
}
showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
}
KeyNavigation.tab: saveButton
}
BasicButtonType {
@ -137,6 +154,8 @@ PageType {
}
PageController.showNotificationMessage(qsTr("Settings saved"))
}
Keys.onTabPressed: lastItemTabClicked(focusItem)
}
}
}

View file

@ -27,6 +27,13 @@ disabled after 14 days, and all log files will be deleted.")
}
}
defaultActiveFocusItem: focusItem
Item {
id: focusItem
KeyNavigation.tab: backButton
}
BackButtonType {
id: backButton
@ -34,6 +41,8 @@ disabled after 14 days, and all log files will be deleted.")
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 20
KeyNavigation.tab: switcher
}
FlickableType {
@ -62,12 +71,14 @@ disabled after 14 days, and all log files will be deleted.")
}
SwitcherType {
id: switcher
Layout.fillWidth: true
Layout.topMargin: 16
text: qsTr("Save logs")
checked: SettingsController.isLoggingEnabled
KeyNavigation.tab: openFolderButton
onCheckedChanged: {
if (checked !== SettingsController.isLoggingEnabled) {
SettingsController.isLoggingEnabled = checked
@ -84,14 +95,18 @@ disabled after 14 days, and all log files will be deleted.")
visible: !GC.isMobile()
ImageButtonType {
id: openFolderButton
Layout.alignment: Qt.AlignHCenter
implicitWidth: 56
implicitHeight: 56
image: "qrc:/images/controls/folder-open.svg"
KeyNavigation.tab: saveButton
onClicked: SettingsController.openLogsFolder()
Keys.onReturnPressed: openFolderButton.clicked()
Keys.onEnterPressed: openFolderButton.clicked()
}
CaptionTextType {
@ -108,13 +123,17 @@ disabled after 14 days, and all log files will be deleted.")
Layout.preferredWidth: root.width / ( GC.isMobile() ? 2 : 3 )
ImageButtonType {
id: saveButton
Layout.alignment: Qt.AlignHCenter
implicitWidth: 56
implicitHeight: 56
image: "qrc:/images/controls/save.svg"
KeyNavigation.tab: clearButton
Keys.onReturnPressed: saveButton.clicked()
Keys.onEnterPressed: saveButton.clicked()
onClicked: {
var fileName = ""
if (GC.isMobile()) {
@ -149,13 +168,17 @@ disabled after 14 days, and all log files will be deleted.")
Layout.preferredWidth: root.width / ( GC.isMobile() ? 2 : 3 )
ImageButtonType {
id: clearButton
Layout.alignment: Qt.AlignHCenter
implicitWidth: 56
implicitHeight: 56
image: "qrc:/images/controls/delete.svg"
Keys.onTabPressed: lastItemTabClicked(focusItem)
Keys.onReturnPressed: clearButton.clicked()
Keys.onEnterPressed: clearButton.clicked()
onClicked: function() {
var headerText = qsTr("Clear logs?")
var yesButtonText = qsTr("Continue")
@ -166,8 +189,14 @@ disabled after 14 days, and all log files will be deleted.")
SettingsController.clearLogs()
PageController.showBusyIndicator(false)
PageController.showNotificationMessage(qsTr("Logs have been cleaned up"))
if (!GC.isMobile()) {
focusItem.forceActiveFocus()
}
}
var noButtonFunction = function() {
if (!GC.isMobile()) {
focusItem.forceActiveFocus()
}
}
showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)

View file

@ -10,10 +10,17 @@ import ProtocolEnum 1.0
import "../Controls2"
import "../Controls2/TextTypes"
import "../Components"
import "../Config"
PageType {
id: root
signal lastItemTabClickedSignal()
onFocusChanged: content.isServerWithWriteAccess ?
labelWithButton.forceActiveFocus() :
labelWithButton3.forceActiveFocus()
Connections {
target: InstallController
@ -85,12 +92,15 @@ PageType {
property bool isServerWithWriteAccess: ServersModel.isProcessedServerHasWriteAccess()
LabelWithButtonType {
id: labelWithButton
visible: content.isServerWithWriteAccess
Layout.fillWidth: true
text: qsTr("Check the server for previously installed Amnezia services")
descriptionText: qsTr("Add them to the application if they were not displayed")
KeyNavigation.tab: labelWithButton2
clickedFunction: function() {
PageController.showBusyIndicator(true)
InstallController.scanServerForInstalledContainers()
@ -103,12 +113,15 @@ PageType {
}
LabelWithButtonType {
id: labelWithButton2
visible: content.isServerWithWriteAccess
Layout.fillWidth: true
text: qsTr("Reboot server")
textColor: "#EB5757"
KeyNavigation.tab: labelWithButton3
clickedFunction: function() {
var headerText = qsTr("Do you want to reboot the server?")
var descriptionText = qsTr("The reboot process may take approximately 30 seconds. Are you sure you wish to proceed?")
@ -123,8 +136,14 @@ PageType {
InstallController.rebootProcessedServer()
PageController.showBusyIndicator(false)
}
if (!GC.isMobile()) {
labelWithButton5.forceActiveFocus()
}
}
var noButtonFunction = function() {
if (!GC.isMobile()) {
labelWithButton2.forceActiveFocus()
}
}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
@ -136,11 +155,22 @@ PageType {
}
LabelWithButtonType {
id: labelWithButton3
Layout.fillWidth: true
text: qsTr("Remove server from application")
textColor: "#EB5757"
Keys.onTabPressed: {
if (content.isServerWithWriteAccess) {
labelWithButton4.forceActiveFocus()
} else {
labelWithButton5.visible ?
labelWithButton5.forceActiveFocus() :
lastItemTabClickedSignal()
}
}
clickedFunction: function() {
var headerText = qsTr("Do you want to remove the server from application?")
var descriptionText = qsTr("All installed AmneziaVPN services will still remain on the server.")
@ -155,8 +185,14 @@ PageType {
InstallController.removeProcessedServer()
PageController.showBusyIndicator(false)
}
if (!GC.isMobile()) {
labelWithButton5.forceActiveFocus()
}
}
var noButtonFunction = function() {
if (!GC.isMobile()) {
labelWithButton3.forceActiveFocus()
}
}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
@ -166,12 +202,17 @@ PageType {
DividerType {}
LabelWithButtonType {
id: labelWithButton4
visible: content.isServerWithWriteAccess
Layout.fillWidth: true
text: qsTr("Clear server from Amnezia software")
textColor: "#EB5757"
Keys.onTabPressed: labelWithButton5.visible ?
labelWithButton5.forceActiveFocus() :
root.lastItemTabClickedSignal()
clickedFunction: function() {
var headerText = qsTr("Do you want to clear server from Amnezia software?")
var descriptionText = qsTr("All users whom you shared a connection with will no longer be able to connect to it.")
@ -185,8 +226,14 @@ PageType {
PageController.goToPage(PageEnum.PageDeinstalling)
InstallController.removeAllContainers()
}
if (!GC.isMobile()) {
labelWithButton5.forceActiveFocus()
}
}
var noButtonFunction = function() {
if (!GC.isMobile()) {
labelWithButton4.forceActiveFocus()
}
}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
@ -198,12 +245,15 @@ PageType {
}
LabelWithButtonType {
id: labelWithButton5
visible: ServersModel.getProcessedServerData("isServerFromApi")
Layout.fillWidth: true
text: qsTr("Reset API config")
textColor: "#EB5757"
Keys.onTabPressed: root.lastItemTabClickedSignal()
clickedFunction: function() {
var headerText = qsTr("Do you want to reset API config?")
var descriptionText = ""
@ -218,8 +268,15 @@ PageType {
InstallController.removeApiConfig(ServersModel.processedIndex)
PageController.showBusyIndicator(false)
}
if (!GC.isMobile()) {
labelWithButton5.forceActiveFocus()
}
}
var noButtonFunction = function() {
if (!GC.isMobile()) {
labelWithButton5.forceActiveFocus()
}
}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)

View file

@ -18,6 +18,8 @@ import "../Components"
PageType {
id: root
defaultActiveFocusItem: focusItem
Connections {
target: PageController
@ -37,6 +39,11 @@ PageType {
]
}
Item {
id: focusItem
KeyNavigation.tab: header
}
ColumnLayout {
anchors.fill: parent
@ -46,15 +53,26 @@ PageType {
id: header
model: proxyServersModel
activeFocusOnTab: true
onFocusChanged: {
header.itemAt(0).focusItem.forceActiveFocus()
}
delegate: ColumnLayout {
property alias focusItem: backButton
id: content
Layout.topMargin: 20
BackButtonType {
id: backButton
KeyNavigation.tab: headerContent.actionButton
}
HeaderType {
id: headerContent
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
@ -70,6 +88,8 @@ PageType {
}
}
KeyNavigation.tab: tabBar
actionButtonFunction: function() {
serverNameEditDrawer.open()
}
@ -83,6 +103,12 @@ PageType {
anchors.fill: parent
expandedHeight: root.height * 0.35
onClosed: {
if (!GC.isMobile()) {
headerContent.actionButton.forceActiveFocus()
}
}
expandedContent: ColumnLayout {
anchors.top: parent.top
anchors.left: parent.left
@ -99,6 +125,11 @@ PageType {
}
}
Item {
id: focusItem1
KeyNavigation.tab: serverName.textField
}
TextFieldWithHeaderType {
id: serverName
@ -117,6 +148,7 @@ PageType {
Layout.fillWidth: true
text: qsTr("Save")
KeyNavigation.tab: focusItem1
clickedFunc: function() {
if (serverName.textFieldText === "") {
@ -129,12 +161,6 @@ PageType {
serverNameEditDrawer.close()
}
}
Component.onCompleted: {
if (header.itemAt(0) && !GC.isMobile()) {
defaultActiveFocusItem = serverName.textField
}
}
}
}
}
@ -152,25 +178,52 @@ PageType {
color: "transparent"
}
activeFocusOnTab: true
onFocusChanged: {
if (activeFocus) {
protocolsTab.forceActiveFocus()
}
}
TabButtonType {
id: protocolsTab
visible: protocolsPage.installedProtocolsCount
width: protocolsPage.installedProtocolsCount ? undefined : 0
isSelected: tabBar.currentIndex === 0
text: qsTr("Protocols")
KeyNavigation.tab: servicesTab
Keys.onReturnPressed: tabBar.currentIndex = 0
Keys.onEnterPressed: tabBar.currentIndex = 0
}
TabButtonType {
id: servicesTab
visible: servicesPage.installedServicesCount
width: servicesPage.installedServicesCount ? undefined : 0
isSelected: tabBar.currentIndex === 1
text: qsTr("Services")
KeyNavigation.tab: dataTab
Keys.onReturnPressed: tabBar.currentIndex = 1
Keys.onEnterPressed: tabBar.currentIndex = 1
}
TabButtonType {
id: dataTab
isSelected: tabBar.currentIndex === 2
text: qsTr("Management")
Keys.onReturnPressed: tabBar.currentIndex = 2
Keys.onEnterPressed: tabBar.currentIndex = 2
KeyNavigation.tab: stackView.currentIndex === 0 ?
protocolsPage :
stackView.currentIndex === 1 ?
servicesPage :
dataPage
}
}
StackLayout {
id: stackView
Layout.preferredWidth: root.width
Layout.preferredHeight: root.height - tabBar.implicitHeight - header.implicitHeight
@ -179,14 +232,22 @@ PageType {
PageSettingsServerProtocols {
id: protocolsPage
stackView: root.stackView
onLastItemTabClickedSignal: lastItemTabClicked(focusItem)
}
PageSettingsServerServices {
id: servicesPage
stackView: root.stackView
onLastItemTabClickedSignal: lastItemTabClicked(focusItem)
}
PageSettingsServerData {
id: dataPage
stackView: root.stackView
onLastItemTabClickedSignal: lastItemTabClicked(focusItem)
}
}
}
}

View file

@ -18,6 +18,13 @@ import "../Components"
PageType {
id: root
defaultActiveFocusItem: focusItem
Item {
id: focusItem
KeyNavigation.tab: backButton
}
ColumnLayout {
id: header
@ -28,6 +35,8 @@ PageType {
anchors.topMargin: 20
BackButtonType {
id: backButton
KeyNavigation.tab: protocols
}
HeaderType {
@ -47,7 +56,28 @@ PageType {
interactive: true
model: ProtocolsModel
property int currentFocusIndex: 0
activeFocusOnTab: true
onActiveFocusChanged: {
if (activeFocus) {
this.currentFocusIndex = 0
protocols.itemAtIndex(currentFocusIndex).focusItem.forceActiveFocus()
}
}
Keys.onTabPressed: {
if (currentFocusIndex < this.count - 1) {
currentFocusIndex += 1
protocols.itemAtIndex(currentFocusIndex).focusItem.forceActiveFocus()
} else {
clearCacheButton.forceActiveFocus()
}
}
delegate: Item {
property var focusItem: button.rightButton
implicitWidth: protocols.width
implicitHeight: delegateContent.implicitHeight
@ -95,6 +125,7 @@ PageType {
Layout.fillWidth: true
visible: ServersModel.isProcessedServerHasWriteAccess()
KeyNavigation.tab: removeButton
text: qsTr("Clear %1 profile").arg(ContainersModel.getProcessedContainerName())
@ -116,6 +147,9 @@ PageType {
PageController.showBusyIndicator(false)
}
var noButtonFunction = function() {
if (!GC.isMobile()) {
focusItem.forceActiveFocus()
}
}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
@ -142,6 +176,7 @@ PageType {
Layout.fillWidth: true
visible: ServersModel.isProcessedServerHasWriteAccess()
Keys.onTabPressed: lastItemTabClicked(focusItem)
text: qsTr("Remove ") + ContainersModel.getProcessedContainerName()
textColor: "#EB5757"
@ -163,6 +198,9 @@ PageType {
}
}
var noButtonFunction = function() {
if (!GC.isMobile()) {
focusItem.forceActiveFocus()
}
}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
@ -184,3 +222,4 @@ PageType {
}
}
}

View file

@ -20,6 +20,9 @@ PageType {
property var installedProtocolsCount
onFocusChanged: settingsContainersListView.forceActiveFocus()
signal lastItemTabClickedSignal()
FlickableType {
id: fl
anchors.top: parent.top
@ -35,6 +38,7 @@ PageType {
SettingsContainersListView {
id: settingsContainersListView
Connections {
target: ServersModel

View file

@ -20,6 +20,9 @@ PageType {
property var installedServicesCount
onFocusChanged: settingsContainersListView.forceActiveFocus()
signal lastItemTabClickedSignal()
FlickableType {
id: fl
anchors.top: parent.top
@ -35,6 +38,7 @@ PageType {
SettingsContainersListView {
id: settingsContainersListView
Connections {
target: ServersModel

View file

@ -17,6 +17,13 @@ import "../Components"
PageType {
id: root
defaultActiveFocusItem: focusItem
Item {
id: focusItem
KeyNavigation.tab: backButton
}
ColumnLayout {
id: header
@ -27,6 +34,8 @@ PageType {
anchors.topMargin: 20
BackButtonType {
id: backButton
KeyNavigation.tab: servers
}
HeaderType {
@ -39,6 +48,7 @@ PageType {
}
FlickableType {
id: fl
anchors.top: header.bottom
anchors.topMargin: 16
contentHeight: col.implicitHeight
@ -59,10 +69,36 @@ PageType {
clip: true
interactive: false
activeFocusOnTab: true
focus: true
Keys.onTabPressed: {
if (currentIndex < servers.count - 1) {
servers.incrementCurrentIndex()
} else {
servers.currentIndex = 0
focusItem.forceActiveFocus()
root.lastItemTabClicked()
}
fl.ensureVisible(this.currentItem)
}
onVisibleChanged: {
if (visible) {
currentIndex = 0
}
}
delegate: Item {
implicitWidth: servers.width
implicitHeight: delegateContent.implicitHeight
onFocusChanged: {
if (focus) {
server.rightButton.forceActiveFocus()
}
}
ColumnLayout {
id: delegateContent
@ -75,6 +111,7 @@ PageType {
Layout.fillWidth: true
text: name
parentFlickable: fl
descriptionText: {
var servicesNameString = ""
var servicesName = ServersModel.getAllInstalledServicesName(index)

View file

@ -24,6 +24,11 @@ PageType {
defaultActiveFocusItem: searchField.textField
Item {
id: focusItem
KeyNavigation.tab: backButton
}
property bool pageEnabled
Component.onCompleted: {
@ -92,6 +97,8 @@ PageType {
anchors.topMargin: 20
BackButtonType {
id: backButton
KeyNavigation.tab: switcher
}
RowLayout {
@ -112,11 +119,17 @@ PageType {
Layout.fillWidth: true
Layout.rightMargin: 16
checked: SitesModel.isTunnelingEnabled
onToggled: {
SitesModel.toggleSplitTunneling(checked)
function onToggledFunc() {
SitesModel.toggleSplitTunneling(this.checked)
selector.text = root.routeModesModel[getRouteModesModelIndex()].name
}
checked: SitesModel.isTunnelingEnabled
onToggled: { onToggledFunc() }
Keys.onEnterPressed: { onToggledFunc() }
Keys.onReturnPressed: { onToggledFunc() }
KeyNavigation.tab: selector
}
}
@ -165,10 +178,17 @@ PageType {
}
}
}
KeyNavigation.tab: {
return sites.count > 0 ?
sites :
searchField.textField
}
}
}
FlickableType {
id: fl
anchors.top: header.bottom
anchors.topMargin: 16
contentHeight: col.implicitHeight + addSiteButton.implicitHeight + addSiteButton.anchors.bottomMargin + addSiteButton.anchors.topMargin
@ -208,10 +228,29 @@ PageType {
clip: true
interactive: false
activeFocusOnTab: true
focus: true
Keys.onTabPressed: {
if (currentIndex < this.count - 1) {
this.incrementCurrentIndex()
} else {
currentIndex = 0
searchField.textField.forceActiveFocus()
}
fl.ensureVisible(currentItem)
}
delegate: Item {
implicitWidth: sites.width
implicitHeight: delegateContent.implicitHeight
onActiveFocusChanged: {
if (activeFocus) {
site.rightButton.forceActiveFocus()
}
}
ColumnLayout {
id: delegateContent
@ -220,6 +259,7 @@ PageType {
anchors.right: parent.right
LabelWithButtonType {
id: site
Layout.fillWidth: true
text: url
@ -234,8 +274,14 @@ PageType {
var yesButtonFunction = function() {
SitesController.removeSite(proxySitesModel.mapToSource(index))
if (!GC.isMobile()) {
site.rightButton.forceActiveFocus()
}
}
var noButtonFunction = function() {
if (!GC.isMobile()) {
site.rightButton.forceActiveFocus()
}
}
showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
@ -246,6 +292,7 @@ PageType {
}
}
}
}
}
@ -273,9 +320,11 @@ PageType {
id: searchField
Layout.fillWidth: true
rightButtonClickedOnEnter: true
textFieldPlaceholderText: qsTr("website or IP")
buttonImageSource: "qrc:/images/controls/plus.svg"
KeyNavigation.tab: GC.isMobile() ? focusItem : addSiteButtonImage
clickedFunc: function() {
PageController.showBusyIndicator(true)
@ -286,6 +335,7 @@ PageType {
}
ImageButtonType {
id: addSiteButtonImage
implicitWidth: 56
implicitHeight: 56
@ -295,6 +345,11 @@ PageType {
onClicked: function () {
moreActionsDrawer.open()
}
Keys.onReturnPressed: addSiteButtonImage.clicked()
Keys.onEnterPressed: addSiteButtonImage.clicked()
Keys.onTabPressed: lastItemTabClicked(focusItem)
}
}
@ -304,6 +359,12 @@ PageType {
anchors.fill: parent
expandedHeight: parent.height * 0.4375
onClosed: {
if (root.defaultActiveFocusItem && !GC.isMobile()) {
root.defaultActiveFocusItem.forceActiveFocus()
}
}
expandedContent: ColumnLayout {
id: moreActionsDrawerContent
@ -311,6 +372,25 @@ PageType {
anchors.left: parent.left
anchors.right: parent.right
Connections {
target: moreActionsDrawer
function onOpened() {
focusItem1.forceActiveFocus()
}
function onActiveFocusChanged() {
if (!GC.isMobile()) {
focusItem1.forceActiveFocus()
}
}
}
Item {
id: focusItem1
KeyNavigation.tab: importSitesButton.rightButton
}
Header2Type {
Layout.fillWidth: true
Layout.margins: 16
@ -319,6 +399,7 @@ PageType {
}
LabelWithButtonType {
id: importSitesButton
Layout.fillWidth: true
text: qsTr("Import")
@ -327,14 +408,19 @@ PageType {
clickedFunction: function() {
importSitesDrawer.open()
}
KeyNavigation.tab: exportSitesButton
}
DividerType {}
LabelWithButtonType {
id: exportSitesButton
Layout.fillWidth: true
text: qsTr("Save site list")
KeyNavigation.tab: focusItem1
clickedFunction: function() {
var fileName = ""
if (GC.isMobile()) {
@ -365,9 +451,28 @@ PageType {
anchors.fill: parent
expandedHeight: parent.height * 0.4375
onClosed: {
if (!GC.isMobile()) {
moreActionsDrawer.forceActiveFocus()
}
}
expandedContent: Item {
implicitHeight: importSitesDrawer.expandedHeight
Connections {
target: importSitesDrawer
enabled: !GC.isMobile()
function onOpened() {
focusItem2.forceActiveFocus()
}
}
Item {
id: focusItem2
KeyNavigation.tab: importSitesDrawerBackButton
}
BackButtonType {
id: importSitesDrawerBackButton
@ -376,6 +481,8 @@ PageType {
anchors.right: parent.right
anchors.topMargin: 16
KeyNavigation.tab: importSitesButton2
backButtonFunction: function() {
importSitesDrawer.close()
}
@ -404,9 +511,11 @@ PageType {
}
LabelWithButtonType {
id: importSitesButton2
Layout.fillWidth: true
text: qsTr("Replace site list")
KeyNavigation.tab: importSitesButton3
clickedFunction: function() {
var fileName = SystemController.getFileName(qsTr("Open sites file"),
@ -420,8 +529,10 @@ PageType {
DividerType {}
LabelWithButtonType {
id: importSitesButton3
Layout.fillWidth: true
text: qsTr("Add imported sites to existing ones")
KeyNavigation.tab: focusItem2
clickedFunction: function() {
var fileName = SystemController.getFileName(qsTr("Open sites file"),

View file

@ -22,6 +22,8 @@ PageType {
}
}
defaultActiveFocusItem: focusItem
FlickableType {
id: fl
anchors.top: parent.top
@ -37,8 +39,15 @@ PageType {
spacing: 0
Item {
id: focusItem
KeyNavigation.tab: backButton
}
BackButtonType {
id: backButton
Layout.topMargin: 20
KeyNavigation.tab: fileButton.rightButton
}
HeaderType {
@ -61,6 +70,7 @@ PageType {
}
LabelWithButtonType {
id: fileButton
Layout.fillWidth: true
Layout.topMargin: 16
@ -68,6 +78,8 @@ PageType {
rightImageSource: "qrc:/images/controls/chevron-right.svg"
leftImageSource: "qrc:/images/controls/folder-open.svg"
KeyNavigation.tab: qrButton.visible ? qrButton.rightButton : textButton.rightButton
clickedFunction: function() {
var nameFilter = !ServersModel.getServersCount() ? "Config or backup files (*.vpn *.ovpn *.conf *.json *.backup)" :
"Config files (*.vpn *.ovpn *.conf *.json)"
@ -83,6 +95,7 @@ PageType {
DividerType {}
LabelWithButtonType {
id: qrButton
Layout.fillWidth: true
visible: SettingsController.isCameraPresent()
@ -90,6 +103,8 @@ PageType {
rightImageSource: "qrc:/images/controls/chevron-right.svg"
leftImageSource: "qrc:/images/controls/qr-code.svg"
KeyNavigation.tab: textButton.rightButton
clickedFunction: function() {
ImportController.startDecodingQr()
if (Qt.platform.os === "ios") {
@ -103,12 +118,15 @@ PageType {
}
LabelWithButtonType {
id: textButton
Layout.fillWidth: true
text: qsTr("Key as text")
rightImageSource: "qrc:/images/controls/chevron-right.svg"
leftImageSource: "qrc:/images/controls/text-cursor.svg"
Keys.onTabPressed: lastItemTabClicked(focusItem)
clickedFunction: function() {
PageController.goToPage(PageEnum.PageSetupWizardTextKey)
}

View file

@ -14,6 +14,11 @@ PageType {
defaultActiveFocusItem: hostname.textField
Item {
id: focusItem
KeyNavigation.tab: backButton
}
BackButtonType {
id: backButton
@ -21,6 +26,8 @@ PageType {
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 20
KeyNavigation.tab: hostname.textField
}
FlickableType {
@ -107,6 +114,8 @@ PageType {
text: qsTr("Continue")
Keys.onTabPressed: lastItemTabClicked(focusItem)
clickedFunc: function() {
forceActiveFocus()
if (!isCredentialsFilled()) {

View file

@ -16,6 +16,7 @@ PageType {
id: root
property bool isEasySetup: true
defaultActiveFocusItem: focusItem
SortFilterProxyModel {
id: proxyContainersModel
@ -32,6 +33,14 @@ PageType {
}
}
Item {
id: focusItem
implicitWidth: 1
implicitHeight: 54
KeyNavigation.tab: backButton
}
BackButtonType {
id: backButton
@ -39,6 +48,8 @@ PageType {
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 20
KeyNavigation.tab: continueButton
}
FlickableType {
@ -145,17 +156,14 @@ PageType {
}
}
Item {
implicitWidth: 1
implicitHeight: 54
}
BasicButtonType {
id: continueButton
implicitWidth: parent.width
text: qsTr("Continue")
KeyNavigation.tab: setupLaterButton
parentFlickable: fl
clickedFunc: function() {
if (root.isEasySetup) {
@ -184,6 +192,9 @@ PageType {
textColor: "#D7D8DB"
borderWidth: 1
Keys.onTabPressed: lastItemTabClicked(focusItem)
parentFlickable: fl
visible: {
if (PageController.isTriggeredByConnectButton()) {
PageController.setTriggeredByConnectButton(false)

View file

@ -61,12 +61,19 @@ PageType {
anchors.rightMargin: 16
anchors.leftMargin: 16
Item {
id: focusItem
KeyNavigation.tab: backButton
}
BackButtonType {
id: backButton
Layout.topMargin: 20
Layout.rightMargin: -16
Layout.leftMargin: -16
KeyNavigation.tab: showDetailsButton
}
HeaderType {
@ -93,6 +100,7 @@ PageType {
textColor: "#FBB26A"
text: qsTr("More detailed")
KeyNavigation.tab: transportProtoSelector
clickedFunc: function() {
showDetailsDrawer.open()
@ -102,12 +110,35 @@ PageType {
DrawerType2 {
id: showDetailsDrawer
parent: root
onClosed: {
if (!GC.isMobile()) {
defaultActiveFocusItem.forceActiveFocus()
}
}
anchors.fill: parent
expandedHeight: parent.height * 0.9
expandedContent: Item {
Connections {
target: showDetailsDrawer
enabled: !GC.isMobile()
function onOpened() {
focusItem2.forceActiveFocus()
}
}
implicitHeight: showDetailsDrawer.expandedHeight
Item {
id: focusItem2
KeyNavigation.tab: showDetailsBackButton
onFocusChanged: {
if (focusItem2.activeFocus) {
fl.contentY = 0
}
}
}
BackButtonType {
id: showDetailsBackButton
@ -116,12 +147,15 @@ PageType {
anchors.right: parent.right
anchors.topMargin: 16
KeyNavigation.tab: showDetailsCloseButton
backButtonFunction: function() {
showDetailsDrawer.close()
}
}
FlickableType {
id: fl
anchors.top: showDetailsBackButton.bottom
anchors.left: parent.left
anchors.right: parent.right
@ -158,17 +192,19 @@ PageType {
textFormat: Text.MarkdownText
}
Rectangle {
Layout.fillHeight: true
color: "transparent"
}
BasicButtonType {
id: showDetailsCloseButton
Layout.fillWidth: true
Layout.bottomMargin: 32
parentFlickable: fl
text: qsTr("Close")
Keys.onTabPressed: lastItemTabClicked(focusItem2)
clickedFunc: function() {
showDetailsDrawer.close()
@ -192,6 +228,8 @@ PageType {
Layout.fillWidth: true
rootWidth: root.width
KeyNavigation.tab: (port.visible && port.enabled) ? port.textField : installButton
}
TextFieldWithHeaderType {
@ -220,6 +258,8 @@ PageType {
text: qsTr("Install")
Keys.onTabPressed: lastItemTabClicked(focusItem)
clickedFunc: function() {
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
InstallController.install(dockerContainer, port.textFieldText, transportProtoSelector.currentIndex)
@ -241,7 +281,10 @@ PageType {
transportProtoSelector.visible = protocolSelectorVisible
transportProtoHeader.visible = protocolSelectorVisible
defaultActiveFocusItem = port.textField
if (port.visible && port.enabled)
defaultActiveFocusItem = port.textField
else
defaultActiveFocusItem = focusItem
}
}
}

View file

@ -14,6 +14,13 @@ import "../Config"
PageType {
id: root
defaultActiveFocusItem: focusItem
Item {
id: focusItem
KeyNavigation.tab: backButton
}
SortFilterProxyModel {
id: proxyContainersModel
sourceModel: ContainersModel
@ -30,7 +37,7 @@ PageType {
}
ColumnLayout {
id: backButton
id: backButtonLayout
anchors.top: parent.top
anchors.left: parent.left
@ -39,12 +46,14 @@ PageType {
anchors.topMargin: 20
BackButtonType {
id: backButton
KeyNavigation.tab: containers
}
}
FlickableType {
id: fl
anchors.top: backButton.bottom
anchors.top: backButtonLayout.bottom
anchors.bottom: parent.bottom
contentHeight: content.implicitHeight + content.anchors.topMargin + content.anchors.bottomMargin
@ -79,15 +88,49 @@ PageType {
id: containers
width: parent.width
height: containers.contentItem.height
currentIndex: -1
// currentIndex: -1
clip: true
interactive: false
model: proxyContainersModel
function ensureCurrentItemVisible() {
if (currentIndex >= 0) {
if (currentItem.y < fl.contentY) {
fl.contentY = currentItem.y
} else if (currentItem.y + currentItem.height + header.height > fl.contentY + fl.height) {
fl.contentY = currentItem.y + currentItem.height + header.height - fl.height + 40 // 40 is a bottom margin
}
}
}
activeFocusOnTab: true
Keys.onTabPressed: {
if (currentIndex < this.count - 1) {
this.incrementCurrentIndex()
} else {
this.currentIndex = 0
focusItem.forceActiveFocus()
}
ensureCurrentItemVisible()
}
onVisibleChanged: {
if (visible) {
currentIndex = 0
}
}
delegate: Item {
implicitWidth: containers.width
implicitHeight: delegateContent.implicitHeight
onActiveFocusChanged: {
if (activeFocus) {
container.rightButton.forceActiveFocus()
}
}
ColumnLayout {
id: delegateContent

View file

@ -15,6 +15,8 @@ PageType {
property bool isControlsDisabled: false
defaultActiveFocusItem: focusItem
Connections {
target: PageController
@ -136,7 +138,13 @@ PageType {
qsTr(" Helps you access blocked content without revealing your privacy, even to VPN providers.")
}
Item {
id: focusItem
KeyNavigation.tab: startButton
}
BasicButtonType {
id: startButton
Layout.fillWidth: true
Layout.topMargin: 32
Layout.leftMargin: 16
@ -147,9 +155,12 @@ PageType {
clickedFunc: function() {
connectionTypeSelection.open()
}
KeyNavigation.tab: startButton2
}
BasicButtonType {
id: startButton2
Layout.fillWidth: true
Layout.topMargin: 8
Layout.leftMargin: 16
@ -167,11 +178,18 @@ PageType {
clickedFunc: function() {
Qt.openUrlExternally(qsTr("https://amnezia.org/instructions/0_starter-guide"))
}
Keys.onTabPressed: lastItemTabClicked(focusItem)
}
}
}
ConnectionTypeSelectionDrawer {
id: connectionTypeSelection
onClosed: {
PageController.forceTabBarActiveFocus()
root.defaultActiveFocusItem.forceActiveFocus()
}
}
}

View file

@ -14,6 +14,12 @@ PageType {
defaultActiveFocusItem: textKey.textField
Item {
id: focusItem
KeyNavigation.tab: backButton
}
FlickableType {
id: fl
anchors.top: parent.top
@ -30,7 +36,9 @@ PageType {
spacing: 16
BackButtonType {
id: backButton
Layout.topMargin: 20
KeyNavigation.tab: textKey.textField
}
HeaderType {
@ -75,6 +83,7 @@ PageType {
anchors.bottomMargin: 32
text: qsTr("Continue")
Keys.onTabPressed: lastItemTabClicked(focusItem)
clickedFunc: function() {
if (ImportController.extractConfigFromData(textKey.textFieldText)) {

View file

@ -15,6 +15,24 @@ PageType {
property bool showContent: false
defaultActiveFocusItem: focusItem
Item {
id: focusItem
KeyNavigation.tab: backButton
}
BackButtonType {
id: backButton
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 20
KeyNavigation.tab: showContentButton
}
Connections {
target: ImportController
@ -39,15 +57,6 @@ PageType {
}
}
BackButtonType {
id: backButton
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 20
}
FlickableType {
id: fl
anchors.top: backButton.bottom
@ -88,6 +97,7 @@ PageType {
}
BasicButtonType {
id: showContentButton
Layout.topMargin: 16
Layout.leftMargin: -8
implicitHeight: 32
@ -99,6 +109,7 @@ PageType {
textColor: "#FBB26A"
text: showContent ? qsTr("Collapse content") : qsTr("Show content")
KeyNavigation.tab: connectButton
clickedFunc: function() {
showContent = !showContent
@ -138,15 +149,16 @@ PageType {
}
ColumnLayout {
id: connectButton
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.rightMargin: 16
anchors.leftMargin: 16
Keys.onTabPressed: lastItemTabClicked(focusItem)
BasicButtonType {
id: connectButton
Layout.fillWidth: true
Layout.bottomMargin: 32

View file

@ -152,6 +152,8 @@ PageType {
}
FlickableType {
id: a
anchors.top: parent.top
anchors.bottom: parent.bottom
contentHeight: content.height + 10
@ -168,7 +170,18 @@ PageType {
spacing: 0
Item {
id: focusItem
KeyNavigation.tab: header.actionButton
onFocusChanged: {
if (focusItem.activeFocus) {
a.contentY = 0
}
}
}
HeaderType {
id: header
Layout.fillWidth: true
Layout.topMargin: 24
@ -179,6 +192,8 @@ PageType {
shareFullAccessDrawer.open()
}
KeyNavigation.tab: connectionRadioButton
DrawerType2 {
id: shareFullAccessDrawer
@ -186,6 +201,11 @@ PageType {
anchors.fill: parent
expandedHeight: root.height * 0.45
onClosed: {
if (!GC.isMobile()) {
clientNameTextField.textField.forceActiveFocus()
}
}
expandedContent: ColumnLayout {
anchors.top: parent.top
@ -195,6 +215,14 @@ PageType {
spacing: 0
Connections {
target: shareFullAccessDrawer
enabled: !GC.isMobile()
function onOpened() {
focusItem.forceActiveFocus()
}
}
Header2Type {
Layout.fillWidth: true
Layout.bottomMargin: 16
@ -205,17 +233,24 @@ PageType {
descriptionText: qsTr("Use for your own devices, or share with those you trust to manage the server.")
}
Item {
id: focusItem
KeyNavigation.tab: shareFullAccessButton.rightButton
}
LabelWithButtonType {
id: shareFullAccessButton
Layout.fillWidth: true
text: qsTr("Share")
rightImageSource: "qrc:/images/controls/chevron-right.svg"
KeyNavigation.tab: focusItem
clickedFunction: function() {
PageController.goToPage(PageEnum.PageShareFullAccess)
shareFullAccessDrawer.close()
}
}
}
}
@ -240,28 +275,38 @@ PageType {
spacing: 0
HorizontalRadioButton {
id: connectionRadioButton
checked: accessTypeSelector.currentIndex === 0
implicitWidth: (root.width - 32) / 2
text: qsTr("Connection")
KeyNavigation.tab: usersRadioButton
onClicked: {
accessTypeSelector.currentIndex = 0
if (!GC.isMobile()) {
clientNameTextField.textField.forceActiveFocus()
}
}
}
HorizontalRadioButton {
id: usersRadioButton
checked: accessTypeSelector.currentIndex === 1
implicitWidth: (root.width - 32) / 2
text: qsTr("Users")
KeyNavigation.tab: accessTypeSelector.currentIndex === 0 ? clientNameTextField.textField : serverSelector
onClicked: {
accessTypeSelector.currentIndex = 1
PageController.showBusyIndicator(true)
ExportController.updateClientManagementModel(ContainersModel.getProcessedContainerIndex(),
ServersModel.getProcessedServerCredentials())
PageController.showBusyIndicator(false)
focusItem.forceActiveFocus()
}
}
}
@ -291,7 +336,8 @@ PageType {
checkEmptyText: true
KeyNavigation.tab: shareButton
KeyNavigation.tab: serverSelector
}
DropDownType {
@ -311,7 +357,6 @@ PageType {
listView: ListViewWithRadioButtonType {
id: serverSelectorListView
rootWidth: root.width
imageSource: "qrc:/images/controls/check.svg"
@ -356,6 +401,8 @@ PageType {
ServersModel.processedIndex = proxyServersModel.mapToSource(currentIndex)
}
}
KeyNavigation.tab: protocolSelector
}
DropDownType {
@ -454,6 +501,12 @@ PageType {
}
}
}
KeyNavigation.tab: accessTypeSelector.currentIndex === 0 ?
exportTypeSelector :
isSearchBarVisible ?
searchTextField.textField :
usersHeader.actionButton
}
DropDownType {
@ -497,6 +550,9 @@ PageType {
exportTypeSelector.currentIndex = currentIndex
}
}
KeyNavigation.tab: shareButton
}
BasicButtonType {
@ -510,16 +566,22 @@ PageType {
visible: accessTypeSelector.currentIndex === 0
text: qsTr("Share")
imageSource: "qrc:/images/controls/share-2.svg"
imageSource: "qrc:/images/controls/share-2.svg"
Keys.onTabPressed: lastItemTabClicked(focusItem)
parentFlickable: a
clickedFunc: function(){
if (clientNameTextField.textFieldText !== "") {
ExportController.generateConfig(root.connectionTypesModel[exportTypeSelector.currentIndex].type)
}
}
}
Header2Type {
id: usersHeader
Layout.fillWidth: true
Layout.topMargin: 24
Layout.bottomMargin: 16
@ -531,6 +593,11 @@ PageType {
actionButtonFunction: function() {
root.isSearchBarVisible = true
}
Keys.onTabPressed: clientsListView.model.count > 0 ?
clientsListView.forceActiveFocus() :
lastItemTabClicked(focusItem)
}
RowLayout {
@ -543,16 +610,66 @@ PageType {
Layout.fillWidth: true
textFieldPlaceholderText: qsTr("Search")
Connections {
target: root
function onIsSearchBarVisibleChanged() {
if (root.isSearchBarVisible) {
searchTextField.textField.forceActiveFocus()
} else {
searchTextField.textFieldText = ""
if (!GC.isMobile()) {
usersHeader.actionButton.forceActiveFocus()
}
}
}
}
Keys.onEscapePressed: {
root.isSearchBarVisible = false
}
function navigateTo() {
if (GC.isMobile()) {
focusItem.forceActiveFocus()
return;
}
if (searchTextField.textFieldText === "") {
root.isSearchBarVisible = false
usersHeader.actionButton.forceActiveFocus()
} else {
closeSearchButton.forceActiveFocus()
}
}
Keys.onTabPressed: { navigateTo() }
Keys.onEnterPressed: { navigateTo() }
Keys.onReturnPressed: { navigateTo() }
}
ImageButtonType {
id: closeSearchButton
image: "qrc:/images/controls/close.svg"
imageColor: "#D7D8DB"
onClicked: function() {
root.isSearchBarVisible = false
searchTextField.textFieldText = ""
Keys.onTabPressed: {
if (!GC.isMobile()) {
if (clientsListView.model.count > 0) {
clientsListView.forceActiveFocus()
} else {
lastItemTabClicked(focusItem)
}
}
}
function clickedFunc() {
root.isSearchBarVisible = false
}
onClicked: clickedFunc()
Keys.onEnterPressed: clickedFunc()
Keys.onReturnPressed: clickedFunc()
}
}
@ -576,10 +693,43 @@ PageType {
clip: true
interactive: false
activeFocusOnTab: true
focus: true
Keys.onTabPressed: {
if (!GC.isMobile()) {
if (currentIndex < this.count - 1) {
this.incrementCurrentIndex()
currentItem.focusItem.forceActiveFocus()
} else {
this.currentIndex = 0
lastItemTabClicked(focusItem)
}
}
}
onActiveFocusChanged: {
if (focus && !GC.isMobile()) {
currentIndex = 0
currentItem.focusItem.forceActiveFocus()
}
}
onCurrentIndexChanged: {
if (currentItem) {
if (currentItem.y < a.contentY) {
a.contentY = currentItem.y
} else if (currentItem.y + currentItem.height + clientsListView.y > a.contentY + a.height) {
a.contentY = currentItem.y + clientsListView.y + currentItem.height - a.height
}
}
}
delegate: Item {
implicitWidth: clientsListView.width
implicitHeight: delegateContent.implicitHeight
property alias focusItem: clientFocusItem.rightButton
ColumnLayout {
id: delegateContent
@ -591,6 +741,7 @@ PageType {
anchors.leftMargin: -16
LabelWithButtonType {
id: clientFocusItem
Layout.fillWidth: true
text: clientName
@ -608,6 +759,12 @@ PageType {
parent: root
onClosed: {
if (!GC.isMobile()) {
focusItem.forceActiveFocus()
}
}
anchors.fill: parent
expandedHeight: root.height * 0.5
@ -621,6 +778,14 @@ PageType {
spacing: 8
Connections {
target: clientInfoDrawer
enabled: !GC.isMobile()
function onOpened() {
focusItem1.forceActiveFocus()
}
}
Header2Type {
Layout.fillWidth: true
Layout.bottomMargin: 24
@ -629,7 +794,13 @@ PageType {
descriptionText: qsTr("Creation date: ") + creationDate
}
Item {
id: focusItem1
KeyNavigation.tab: renameButton
}
BasicButtonType {
id: renameButton
Layout.fillWidth: true
Layout.topMargin: 24
@ -642,6 +813,8 @@ PageType {
text: qsTr("Rename")
KeyNavigation.tab: revokeButton
clickedFunc: function() {
clientNameEditDrawer.open()
}
@ -654,6 +827,12 @@ PageType {
anchors.fill: parent
expandedHeight: root.height * 0.35
onClosed: {
if (!GC.isMobile()) {
focusItem1.forceActiveFocus()
}
}
expandedContent: ColumnLayout {
anchors.top: parent.top
anchors.left: parent.left
@ -670,6 +849,11 @@ PageType {
}
}
Item {
id: focusItem2
KeyNavigation.tab: clientNameEditor.textField
}
TextFieldWithHeaderType {
id: clientNameEditor
Layout.fillWidth: true
@ -687,6 +871,7 @@ PageType {
Layout.fillWidth: true
text: qsTr("Save")
KeyNavigation.tab: focusItem2
clickedFunc: function() {
if (clientNameEditor.textFieldText === "") {
@ -709,6 +894,7 @@ PageType {
}
BasicButtonType {
id: revokeButton
Layout.fillWidth: true
defaultColor: "transparent"
@ -719,6 +905,7 @@ PageType {
borderWidth: 1
text: qsTr("Revoke")
KeyNavigation.tab: focusItem1
clickedFunc: function() {
var headerText = qsTr("Revoke the config for a user - %1?").arg(clientName)
@ -731,6 +918,9 @@ PageType {
root.revokeConfig(index)
}
var noButtonFunction = function() {
if (!GC.isMobile()) {
focusItem1.forceActiveFocus()
}
}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
@ -748,6 +938,11 @@ PageType {
id: shareConnectionDrawer
anchors.fill: parent
onClosed: {
if (!GC.isMobile()) {
clientNameTextField.textField.forceActiveFocus()
}
}
}
MouseArea {

View file

@ -12,10 +12,18 @@ import "./"
import "../Controls2"
import "../Controls2/TextTypes"
import "../Components"
import "../Config"
PageType {
id: root
defaultActiveFocusItem: focusItem
Item {
id: focusItem
KeyNavigation.tab: backButton
}
BackButtonType {
id: backButton
@ -23,6 +31,8 @@ PageType {
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 20
KeyNavigation.tab: serverSelector
}
FlickableType {
@ -74,6 +84,8 @@ PageType {
descriptionText: qsTr("Server")
headerText: qsTr("Server")
KeyNavigation.tab: shareButton
listView: ListViewWithRadioButtonType {
id: serverSelectorListView
@ -100,7 +112,7 @@ PageType {
shareConnectionDrawer.headerText = qsTr("Accessing ") + serverSelector.text
shareConnectionDrawer.configContentHeaderText = qsTr("File with accessing settings to ") + serverSelector.text
serverSelector.close()
// serverSelector.close()
}
Component.onCompleted: {
@ -117,12 +129,15 @@ PageType {
}
BasicButtonType {
id: shareButton
Layout.fillWidth: true
Layout.topMargin: 40
text: qsTr("Share")
imageSource: "qrc:/images/controls/share-2.svg"
Keys.onTabPressed: lastItemTabClicked(focusItem)
clickedFunc: function() {
shareConnectionDrawer.headerText = qsTr("Connection to ") + serverSelector.text
shareConnectionDrawer.configContentHeaderText = qsTr("File with connection settings to ") + serverSelector.text
@ -149,5 +164,10 @@ PageType {
id: shareConnectionDrawer
anchors.fill: parent
onClosed: {
if (!GC.isMobile()) {
focusItem.forceActiveFocus()
}
}
}
}

View file

@ -14,6 +14,8 @@ import "../Components"
PageType {
id: root
defaultActiveFocusItem: homeTabButton
property bool isControlsDisabled: false
property bool isTabBarDisabled: false
@ -82,6 +84,16 @@ PageType {
PageController.closePage()
}
}
function onForceTabBarActiveFocus() {
homeTabButton.focus = true
tabBar.forceActiveFocus()
}
function onForceStackActiveFocus() {
homeTabButton.focus = true
tabBarStackView.forceActiveFocus()
}
}
Connections {
@ -211,13 +223,19 @@ PageType {
}
TabImageButtonType {
id: homeTabButton
isSelected: tabBar.currentIndex === 0
image: "qrc:/images/controls/home.svg"
onClicked: {
clickedFunc: function () {
tabBarStackView.goToTabBarPage(PageEnum.PageHome)
ServersModel.processedIndex = ServersModel.defaultIndex
tabBar.currentIndex = 0
tabBar.previousIndex = 0
}
KeyNavigation.tab: shareTabButton
Keys.onEnterPressed: this.clicked()
Keys.onReturnPressed: this.clicked()
}
TabImageButtonType {
@ -238,27 +256,37 @@ PageType {
isSelected: tabBar.currentIndex === 1
image: "qrc:/images/controls/share-2.svg"
onClicked: {
clickedFunc: function () {
tabBarStackView.goToTabBarPage(PageEnum.PageShare)
tabBar.currentIndex = 1
tabBar.previousIndex = 1
}
KeyNavigation.tab: settingsTabButton
}
TabImageButtonType {
id: settingsTabButton
isSelected: tabBar.currentIndex === 2
image: "qrc:/images/controls/settings-2.svg"
onClicked: {
clickedFunc: function () {
tabBarStackView.goToTabBarPage(PageEnum.PageSettings)
tabBar.currentIndex = 2
tabBar.previousIndex = 2
}
KeyNavigation.tab: plusTabButton
}
TabImageButtonType {
id: plusTabButton
isSelected: tabBar.currentIndex === 3
image: "qrc:/images/controls/plus.svg"
onClicked: {
clickedFunc: function () {
connectionTypeSelection.open()
}
Keys.onTabPressed: PageController.forceStackActiveFocus()
}
}
@ -266,6 +294,7 @@ PageType {
id: connectionTypeSelection
onAboutToHide: {
PageController.forceTabBarActiveFocus()
tabBar.setCurrentIndex(tabBar.previousIndex)
}
}