Improve navigation cpp (#1061)
* add focusController class * add more key handlers * add focus navigation to qml * fixed language selector * add reverse focus change to FocusController * add default focus item * update transitions * update pages * add ListViewFocusController * fix ListView navigation * update CardType for using with focus navigation * remove useless key navigation * remove useless slots, logs, Drawer open and close * fix reverse focus move on listView * fix drawer radio buttons selection * fix drawer layout and focus move * fix PageSetupWizardProtocolSettings focus move * fix back navigation on default focus item * fix crashes after ListView navigation * fix protocol settings focus move * fix focus on users on page share * clean up page share * fix server rename * fix page share default server selection * refactor about page for correct focus move * fix focus move on list views with header and-or footer * minor fixes * fix server list back button handler * fix spawn signals on switch * fix share details drawer * fix drawer open close usage * refactor listViewFocusController * refactor focusController to make the logic more straightforward * fix focus on notification * update config page for scrolling with tab * fix crash on return with esc key * fix focus navigation in dynamic delegate of list view * fix focus move on qr code on share page * refactor page logging settings for focus navigation * update popup * Bump version * Add mandatory requirement for android.software.leanback. * Fix importing files on TVs * fix: add separate method for reading files to fix file reading on Android TV * fix(android): add CHANGE_NETWORK_STATE permission for all Android versions * Fix connection check for AWG/WG * chore: minor fixes (#1235) * fix: add a workaround to open files on Android TV due to lack of SAF * fix: change the banner format for TV * refactor: make TvFilePicker activity more sustainable * fix: add the touch emulation method for Android TV * fix: null uri processing * fix: add the touch emulation method for Android TV * fix: hide UI elements that use file saving * chore: bump version code * add `ScrollBarType` * update initial config page * refactor credentials setup page to handle the focus navigation * add `setDelegateIndex` method to `listViewFocusController` * fix focus behavior on new page/popup * make minor fixes and clean up * fix: get rid of the assign function call * Scrollbar is on if the content is larger than a screen * Fix selection in language change list * Update select language list * update logging settings page * fix checked item in lists * fix split tunneling settings * make unchangable properties readonly * refactor SwitcherType * fix hide/unhide password * `PageShare` readonly properties * Fix list view focus moving on `PageShare` * remove manual focus control on `PageShare` * format `ListViewFocusController` * format `FocusController` * add `focusControl` with utility functions for focus control * refactor `listViewFocusController` acoording to `focusControl` * refactor `focusConroller` according to `focusControl` * add `printSectionName` method to `listViewController` * remove arrow from `Close application` item * fix focus movement in `ServersListView` * `Restore from backup` is visible only on start screen * `I have nothing` is visible only on start screen * fix back button on `SelectLanguageDrawer` * rename `focusControl` to `qmlUtils` * fix `CMakeLists.txt` * fix `ScrollBarType` * fix `PageSetupWizardApiServicesList` * fix focus movement on dynamic delegates in listView * refactor `PageSetupWizardProtocols` * remove comments and clean up * fix `ListViewWithLabelsType` * fix `PageProtocolCloakSettings` * fix `PageSettingsAppSplitTunneling` * fix `PageDevMenu` * remove debug output from `FocusController` * remove debug output from `ListViewFocusController` * remove debug output from `focusControl` * `focusControl` => `FocusControl` --------- Co-authored-by: albexk <albexk@proton.me> Co-authored-by: Nethius <nethiuswork@gmail.com>
This commit is contained in:
parent
212e9b3a91
commit
6acaab0ffa
109 changed files with 4036 additions and 3700 deletions
|
@ -19,13 +19,13 @@ import "../Components"
|
|||
PageType {
|
||||
id: root
|
||||
|
||||
defaultActiveFocusItem: focusItem
|
||||
|
||||
Connections {
|
||||
objectName: "pageControllerConnections"
|
||||
|
||||
target: PageController
|
||||
|
||||
function onRestorePageHomeState(isContainerInstalled) {
|
||||
drawer.open()
|
||||
drawer.openTriggered()
|
||||
if (isContainerInstalled) {
|
||||
containersDropDown.rootButtonClickedFunction()
|
||||
}
|
||||
|
@ -33,23 +33,22 @@ PageType {
|
|||
}
|
||||
|
||||
Item {
|
||||
objectName: "homeColumnItem"
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.bottomMargin: drawer.collapsedHeight
|
||||
|
||||
ColumnLayout {
|
||||
objectName: "homeColumnLayout"
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.topMargin: 34
|
||||
anchors.bottomMargin: 34
|
||||
|
||||
Item {
|
||||
id: focusItem
|
||||
KeyNavigation.tab: loggingButton.visible ?
|
||||
loggingButton :
|
||||
connectButton
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
id: loggingButton
|
||||
objectName: "loggingButton"
|
||||
|
||||
property bool isLoggingEnabled: SettingsController.isLoggingEnabled
|
||||
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
|
@ -69,8 +68,6 @@ PageType {
|
|||
Keys.onEnterPressed: loggingButton.clicked()
|
||||
Keys.onReturnPressed: loggingButton.clicked()
|
||||
|
||||
KeyNavigation.tab: connectButton
|
||||
|
||||
onClicked: {
|
||||
PageController.goToPage(PageEnum.PageSettingsLogging)
|
||||
}
|
||||
|
@ -78,13 +75,15 @@ PageType {
|
|||
|
||||
ConnectButton {
|
||||
id: connectButton
|
||||
objectName: "connectButton"
|
||||
|
||||
Layout.fillHeight: true
|
||||
Layout.alignment: Qt.AlignCenter
|
||||
KeyNavigation.tab: splitTunnelingButton
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
id: splitTunnelingButton
|
||||
objectName: "splitTunnelingButton"
|
||||
|
||||
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
|
||||
Layout.bottomMargin: 34
|
||||
|
@ -116,53 +115,37 @@ PageType {
|
|||
Keys.onEnterPressed: splitTunnelingButton.clicked()
|
||||
Keys.onReturnPressed: splitTunnelingButton.clicked()
|
||||
|
||||
KeyNavigation.tab: drawer
|
||||
|
||||
onClicked: {
|
||||
homeSplitTunnelingDrawer.open()
|
||||
homeSplitTunnelingDrawer.openTriggered()
|
||||
}
|
||||
|
||||
HomeSplitTunnelingDrawer {
|
||||
id: homeSplitTunnelingDrawer
|
||||
parent: root
|
||||
objectName: "homeSplitTunnelingDrawer"
|
||||
|
||||
onClosed: {
|
||||
if (!GC.isMobile()) {
|
||||
focusItem.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
parent: root
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DrawerType2 {
|
||||
id: drawer
|
||||
objectName: "drawerProtocol"
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
onClosed: {
|
||||
if (!GC.isMobile()) {
|
||||
focusItem.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
collapsedStateContent: Item {
|
||||
objectName: "ProtocolDrawerCollapsedContent"
|
||||
|
||||
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
|
||||
objectName: "collapsedColumnLayout"
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
@ -181,6 +164,8 @@ PageType {
|
|||
}
|
||||
|
||||
RowLayout {
|
||||
objectName: "rowLayout"
|
||||
|
||||
Layout.topMargin: 14
|
||||
Layout.leftMargin: 24
|
||||
Layout.rightMargin: 24
|
||||
|
@ -189,9 +174,11 @@ PageType {
|
|||
spacing: 0
|
||||
|
||||
Connections {
|
||||
objectName: "drawerConnections"
|
||||
|
||||
target: drawer
|
||||
function onEntered() {
|
||||
if (drawer.isCollapsed) {
|
||||
function onCursorEntered() {
|
||||
if (drawer.isCollapsedStateActive) {
|
||||
collapsedButtonChevron.backgroundColor = collapsedButtonChevron.hoveredColor
|
||||
collapsedButtonHeader.opacity = 0.8
|
||||
} else {
|
||||
|
@ -199,8 +186,8 @@ PageType {
|
|||
}
|
||||
}
|
||||
|
||||
function onExited() {
|
||||
if (drawer.isCollapsed) {
|
||||
function onCursorExited() {
|
||||
if (drawer.isCollapsedStateActive) {
|
||||
collapsedButtonChevron.backgroundColor = collapsedButtonChevron.defaultColor
|
||||
collapsedButtonHeader.opacity = 1
|
||||
} else {
|
||||
|
@ -209,7 +196,7 @@ PageType {
|
|||
}
|
||||
|
||||
function onPressed(pressed, entered) {
|
||||
if (drawer.isCollapsed) {
|
||||
if (drawer.isCollapsedStateActive) {
|
||||
collapsedButtonChevron.backgroundColor = pressed ? collapsedButtonChevron.pressedColor : entered ? collapsedButtonChevron.hoveredColor : collapsedButtonChevron.defaultColor
|
||||
collapsedButtonHeader.opacity = 0.7
|
||||
} else {
|
||||
|
@ -220,6 +207,8 @@ PageType {
|
|||
|
||||
Header1TextType {
|
||||
id: collapsedButtonHeader
|
||||
objectName: "collapsedButtonHeader"
|
||||
|
||||
Layout.maximumWidth: drawer.width - 48 - 18 - 12
|
||||
|
||||
maximumLineCount: 2
|
||||
|
@ -228,8 +217,6 @@ PageType {
|
|||
text: ServersModel.defaultServerName
|
||||
horizontalAlignment: Qt.AlignHCenter
|
||||
|
||||
KeyNavigation.tab: tabBar
|
||||
|
||||
Behavior on opacity {
|
||||
PropertyAnimation { duration: 200 }
|
||||
}
|
||||
|
@ -237,10 +224,11 @@ PageType {
|
|||
|
||||
ImageButtonType {
|
||||
id: collapsedButtonChevron
|
||||
objectName: "collapsedButtonChevron"
|
||||
|
||||
Layout.leftMargin: 8
|
||||
|
||||
visible: drawer.isCollapsed
|
||||
visible: drawer.isCollapsedStateActive()
|
||||
|
||||
hoverEnabled: false
|
||||
image: "qrc:/images/controls/chevron-down.svg"
|
||||
|
@ -255,18 +243,17 @@ PageType {
|
|||
|
||||
Keys.onEnterPressed: collapsedButtonChevron.clicked()
|
||||
Keys.onReturnPressed: collapsedButtonChevron.clicked()
|
||||
Keys.onTabPressed: lastItemTabClicked()
|
||||
|
||||
|
||||
onClicked: {
|
||||
if (drawer.isCollapsed) {
|
||||
drawer.open()
|
||||
if (drawer.isCollapsedStateActive()) {
|
||||
drawer.openTriggered()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
objectName: "rowLayoutLabel"
|
||||
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
|
||||
Layout.topMargin: 8
|
||||
Layout.bottomMargin: drawer.isCollapsed ? 44 : ServersModel.isDefaultServerFromApi ? 61 : 16
|
||||
|
@ -305,18 +292,9 @@ PageType {
|
|||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: drawer
|
||||
enabled: !GC.isMobile()
|
||||
function onIsCollapsedChanged() {
|
||||
if (!drawer.isCollapsed) {
|
||||
focusItem1.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: serversMenuHeader
|
||||
objectName: "serversMenuHeader"
|
||||
|
||||
anchors.top: collapsed.bottom
|
||||
anchors.right: parent.right
|
||||
|
@ -328,13 +306,9 @@ PageType {
|
|||
|
||||
visible: !ServersModel.isDefaultServerFromApi
|
||||
|
||||
Item {
|
||||
id: focusItem1
|
||||
KeyNavigation.tab: containersDropDown
|
||||
}
|
||||
|
||||
DropDownType {
|
||||
id: containersDropDown
|
||||
objectName: "containersDropDown"
|
||||
|
||||
rootButtonImageColor: AmneziaStyle.color.midnightBlack
|
||||
rootButtonBackgroundColor: AmneziaStyle.color.paleGray
|
||||
|
@ -345,28 +319,29 @@ PageType {
|
|||
rootButtonTextTopMargin: 8
|
||||
rootButtonTextBottomMargin: 8
|
||||
|
||||
enabled: drawer.isOpened
|
||||
|
||||
text: ServersModel.defaultServerDefaultContainerName
|
||||
textColor: AmneziaStyle.color.midnightBlack
|
||||
headerText: qsTr("VPN protocol")
|
||||
headerBackButtonImage: "qrc:/images/controls/arrow-left.svg"
|
||||
|
||||
rootButtonClickedFunction: function() {
|
||||
containersDropDown.open()
|
||||
containersDropDown.openTriggered()
|
||||
}
|
||||
|
||||
drawerParent: root
|
||||
KeyNavigation.tab: serversMenuContent
|
||||
|
||||
listView: HomeContainersListView {
|
||||
id: containersListView
|
||||
objectName: "containersListView"
|
||||
|
||||
rootWidth: root.width
|
||||
onVisibleChanged: {
|
||||
if (containersDropDown.visible && !GC.isMobile()) {
|
||||
focusItem1.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
height: 500 // TODO: make calculated
|
||||
|
||||
Connections {
|
||||
objectName: "rowLayoutConnections"
|
||||
|
||||
target: ServersModel
|
||||
|
||||
function onDefaultServerIndexChanged() {
|
||||
|
@ -408,167 +383,21 @@ PageType {
|
|||
|
||||
ButtonGroup {
|
||||
id: serversRadioButtonGroup
|
||||
objectName: "serversRadioButtonGroup"
|
||||
}
|
||||
|
||||
ListView {
|
||||
ServersListView {
|
||||
id: serversMenuContent
|
||||
objectName: "serversMenuContent"
|
||||
|
||||
anchors.top: serversMenuHeader.bottom
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent.left
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.topMargin: 16
|
||||
|
||||
model: ServersModel
|
||||
currentIndex: ServersModel.defaultIndex
|
||||
|
||||
ScrollBar.vertical: ScrollBar {
|
||||
id: scrollBar
|
||||
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()
|
||||
isFocusable: false
|
||||
|
||||
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) {
|
||||
serversMenuContent.currentIndex = serverIndex
|
||||
}
|
||||
}
|
||||
|
||||
clip: true
|
||||
|
||||
delegate: Item {
|
||||
id: menuContentDelegate
|
||||
|
||||
property variant delegateData: model
|
||||
property VerticalRadioButton serverRadioButtonProperty: serverRadioButton
|
||||
|
||||
implicitWidth: serversMenuContent.width
|
||||
implicitHeight: serverRadioButtonContent.implicitHeight
|
||||
|
||||
onActiveFocusChanged: {
|
||||
if (activeFocus) {
|
||||
serverRadioButton.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: serverRadioButtonContent
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.rightMargin: 16
|
||||
anchors.leftMargin: 16
|
||||
|
||||
spacing: 0
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
VerticalRadioButton {
|
||||
id: serverRadioButton
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: name
|
||||
descriptionText: serverDescription
|
||||
|
||||
checked: index === serversMenuContent.currentIndex
|
||||
checkable: !ConnectionController.isConnected
|
||||
|
||||
ButtonGroup.group: serversRadioButtonGroup
|
||||
|
||||
onClicked: {
|
||||
if (ConnectionController.isConnected) {
|
||||
PageController.showNotificationMessage(qsTr("Unable change server while there is an active connection"))
|
||||
return
|
||||
}
|
||||
|
||||
serversMenuContent.currentIndex = index
|
||||
|
||||
ServersModel.defaultIndex = index
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: serverRadioButton
|
||||
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: AmneziaStyle.color.paleGray
|
||||
|
||||
implicitWidth: 56
|
||||
implicitHeight: 56
|
||||
|
||||
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)
|
||||
drawer.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 0
|
||||
Layout.rightMargin: 0
|
||||
}
|
||||
// this item shouldn't be focused when drawer is closed
|
||||
function onIsOpenedChanged() {
|
||||
serversMenuContent.isFocusable = drawer.isOpened
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue