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:
parent
d50e7dd3f4
commit
0e4ae26bae
66 changed files with 2269 additions and 143 deletions
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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 {}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue