From 02bbcd3a31393f910825e66cc16790f1008ab3bc Mon Sep 17 00:00:00 2001 From: Cyril Anisimov Date: Sat, 14 Sep 2024 19:42:55 +0200 Subject: [PATCH 01/40] add focusController class --- client/amnezia_application.cpp | 3 + client/amnezia_application.h | 2 + client/resources.qrc | 383 +++++++------- client/ui/controllers/focusController.cpp | 476 ++++++++++++++++++ client/ui/controllers/focusController.h | 52 ++ client/ui/controllers/pageController.cpp | 18 +- client/ui/controllers/pageController.h | 7 +- client/ui/qml/Components/ConnectButton.qml | 10 + .../ConnectionTypeSelectionDrawer.qml | 19 +- .../qml/Components/HomeContainersListView.qml | 99 ++-- .../Components/HomeSplitTunnelingDrawer.qml | 29 +- .../ui/qml/Components/InstalledAppsDrawer.qml | 2 +- client/ui/qml/Components/QuestionDrawer.qml | 17 +- .../qml/Components/SelectLanguageDrawer.qml | 34 +- client/ui/qml/Components/ServersListView.qml | 208 ++++++++ .../qml/Components/ShareConnectionDrawer.qml | 27 +- .../qml/Components/TransportProtoSelector.qml | 2 - client/ui/qml/Controls2/BackButtonType.qml | 20 +- client/ui/qml/Controls2/BasicButtonType.qml | 13 + client/ui/qml/Controls2/CardWithIconsType.qml | 12 +- client/ui/qml/Controls2/DrawerType2.qml | 217 +++++--- client/ui/qml/Controls2/DropDownType.qml | 145 +++--- client/ui/qml/Controls2/FlickableType.qml | 2 +- .../qml/Controls2/HorizontalRadioButton.qml | 12 + client/ui/qml/Controls2/ImageButtonType.qml | 19 +- .../ui/qml/Controls2/LabelWithButtonType.qml | 44 +- .../Controls2/ListViewWithRadioButtonType.qml | 40 +- client/ui/qml/Controls2/PageType.qml | 25 +- client/ui/qml/Controls2/PopupType.qml | 10 +- client/ui/qml/Controls2/SwitcherType.qml | 10 + client/ui/qml/Controls2/TabButtonType.qml | 11 +- .../ui/qml/Controls2/TabImageButtonType.qml | 15 +- .../qml/Controls2/TextAreaWithFooterType.qml | 3 - .../qml/Controls2/TextFieldWithHeaderType.qml | 23 +- .../ui/qml/Controls2/VerticalRadioButton.qml | 8 +- client/ui/qml/Pages2/PageDevMenu.qml | 11 - client/ui/qml/Pages2/PageHome.qml | 270 ++-------- .../ui/qml/Pages2/PageProtocolAwgSettings.qml | 53 +- .../qml/Pages2/PageProtocolCloakSettings.qml | 13 - .../Pages2/PageProtocolOpenVpnSettings.qml | 40 +- client/ui/qml/Pages2/PageProtocolRaw.qml | 19 +- .../PageProtocolShadowSocksSettings.qml | 16 +- .../Pages2/PageProtocolWireGuardSettings.qml | 42 +- .../qml/Pages2/PageProtocolXraySettings.qml | 22 +- .../ui/qml/Pages2/PageServiceDnsSettings.qml | 8 - .../ui/qml/Pages2/PageServiceSftpSettings.qml | 14 +- .../Pages2/PageServiceSocksProxySettings.qml | 26 +- .../Pages2/PageServiceTorWebsiteSettings.qml | 8 - client/ui/qml/Pages2/PageSettings.qml | 23 +- client/ui/qml/Pages2/PageSettingsAbout.qml | 27 +- .../qml/Pages2/PageSettingsApiServerInfo.qml | 7 - .../Pages2/PageSettingsAppSplitTunneling.qml | 14 - .../ui/qml/Pages2/PageSettingsApplication.qml | 48 +- client/ui/qml/Pages2/PageSettingsBackup.qml | 11 +- .../ui/qml/Pages2/PageSettingsConnection.qml | 13 - client/ui/qml/Pages2/PageSettingsDns.qml | 19 +- client/ui/qml/Pages2/PageSettingsLogging.qml | 57 ++- .../ui/qml/Pages2/PageSettingsServerData.qml | 4 - .../ui/qml/Pages2/PageSettingsServerInfo.qml | 26 +- .../qml/Pages2/PageSettingsServerProtocol.qml | 9 - .../ui/qml/Pages2/PageSettingsServersList.qml | 143 +++--- .../qml/Pages2/PageSettingsSplitTunneling.qml | 62 +-- .../Pages2/PageSetupWizardApiServiceInfo.qml | 8 - .../Pages2/PageSetupWizardApiServicesList.qml | 8 - .../Pages2/PageSetupWizardConfigSource.qml | 89 +++- .../qml/Pages2/PageSetupWizardCredentials.qml | 17 +- client/ui/qml/Pages2/PageSetupWizardEasy.qml | 13 +- .../PageSetupWizardProtocolSettings.qml | 43 +- .../qml/Pages2/PageSetupWizardProtocols.qml | 8 - client/ui/qml/Pages2/PageSetupWizardStart.qml | 9 - .../ui/qml/Pages2/PageSetupWizardTextKey.qml | 11 - .../qml/Pages2/PageSetupWizardViewConfig.qml | 10 - client/ui/qml/Pages2/PageShare.qml | 146 +++--- client/ui/qml/Pages2/PageShareFullAccess.qml | 18 +- client/ui/qml/Pages2/PageStart.qml | 52 +- client/ui/qml/main2.qml | 28 +- 76 files changed, 1906 insertions(+), 1576 deletions(-) create mode 100644 client/ui/controllers/focusController.cpp create mode 100644 client/ui/controllers/focusController.h create mode 100644 client/ui/qml/Components/ServersListView.qml diff --git a/client/amnezia_application.cpp b/client/amnezia_application.cpp index 4e25097d..aeed439b 100644 --- a/client/amnezia_application.cpp +++ b/client/amnezia_application.cpp @@ -404,6 +404,9 @@ void AmneziaApplication::initControllers() m_pageController.reset(new PageController(m_serversModel, m_settings)); m_engine->rootContext()->setContextProperty("PageController", m_pageController.get()); + m_focusController.reset(new FocusController(m_engine, this)); + m_engine->rootContext()->setContextProperty("FocusController", m_focusController.get()); + m_installController.reset(new InstallController(m_serversModel, m_containersModel, m_protocolsModel, m_clientManagementModel, m_apiServicesModel, m_settings)); m_engine->rootContext()->setContextProperty("InstallController", m_installController.get()); diff --git a/client/amnezia_application.h b/client/amnezia_application.h index 64566216..cfeac0d1 100644 --- a/client/amnezia_application.h +++ b/client/amnezia_application.h @@ -19,6 +19,7 @@ #include "ui/controllers/exportController.h" #include "ui/controllers/importController.h" #include "ui/controllers/installController.h" +#include "ui/controllers/focusController.h" #include "ui/controllers/pageController.h" #include "ui/controllers/settingsController.h" #include "ui/controllers/sitesController.h" @@ -124,6 +125,7 @@ private: #endif QScopedPointer m_connectionController; + QScopedPointer m_focusController; QScopedPointer m_pageController; QScopedPointer m_installController; QScopedPointer m_importController; diff --git a/client/resources.qrc b/client/resources.qrc index a10a784d..5edd1e8a 100644 --- a/client/resources.qrc +++ b/client/resources.qrc @@ -1,225 +1,226 @@ + fonts/pt-root-ui_vf.ttf + images/amneziaBigLogo.png + images/AmneziaVPN.png + images/controls/alert-circle.svg + images/controls/amnezia.svg + images/controls/app.svg + images/controls/archive-restore.svg + images/controls/arrow-left.svg + images/controls/arrow-right.svg + images/controls/bug.svg + images/controls/check.svg + images/controls/chevron-down.svg + images/controls/chevron-right.svg + images/controls/chevron-up.svg + images/controls/close.svg + images/controls/copy.svg + images/controls/delete.svg + images/controls/download.svg + images/controls/edit-3.svg + images/controls/eye-off.svg + images/controls/eye.svg + images/controls/file-check-2.svg + images/controls/file-cog-2.svg + images/controls/folder-open.svg + images/controls/folder-search-2.svg + images/controls/gauge.svg + images/controls/github.svg + images/controls/help-circle.svg + images/controls/history.svg + images/controls/home.svg + images/controls/info.svg + images/controls/mail.svg + images/controls/map-pin.svg + images/controls/more-vertical.svg + images/controls/plus.svg + images/controls/qr-code.svg + images/controls/radio-button-inner-circle-pressed.png + images/controls/radio-button-inner-circle.png + images/controls/radio-button-pressed.svg + images/controls/radio-button.svg + images/controls/radio.svg + images/controls/refresh-cw.svg + images/controls/save.svg + images/controls/scan-line.svg + images/controls/search.svg + images/controls/server.svg + images/controls/settings-2.svg + images/controls/settings.svg + images/controls/share-2.svg + images/controls/split-tunneling.svg + images/controls/tag.svg + images/controls/telegram.svg + images/controls/text-cursor.svg + images/controls/trash.svg + images/controls/x-circle.svg images/tray/active.png images/tray/default.png images/tray/error.png - images/AmneziaVPN.png - server_scripts/remove_container.sh - server_scripts/setup_host_firewall.sh - server_scripts/openvpn_cloak/Dockerfile + server_scripts/awg/configure_container.sh + server_scripts/awg/Dockerfile + server_scripts/awg/run_container.sh + server_scripts/awg/start.sh + server_scripts/awg/template.conf + server_scripts/build_container.sh + server_scripts/check_connection.sh + server_scripts/check_server_is_busy.sh + server_scripts/check_user_in_sudo.sh + server_scripts/dns/configure_container.sh + server_scripts/dns/Dockerfile + server_scripts/dns/run_container.sh + server_scripts/install_docker.sh + server_scripts/ipsec/configure_container.sh + server_scripts/ipsec/Dockerfile + server_scripts/ipsec/mobileconfig.plist + server_scripts/ipsec/run_container.sh + server_scripts/ipsec/start.sh + server_scripts/ipsec/strongswan.profile server_scripts/openvpn_cloak/configure_container.sh + server_scripts/openvpn_cloak/Dockerfile + server_scripts/openvpn_cloak/run_container.sh server_scripts/openvpn_cloak/start.sh server_scripts/openvpn_cloak/template.ovpn - server_scripts/install_docker.sh - server_scripts/build_container.sh - server_scripts/prepare_host.sh - server_scripts/check_connection.sh - server_scripts/remove_all_containers.sh - server_scripts/openvpn_cloak/run_container.sh - server_scripts/openvpn/configure_container.sh - server_scripts/openvpn/run_container.sh - server_scripts/openvpn/template.ovpn - server_scripts/openvpn/Dockerfile - server_scripts/openvpn/start.sh server_scripts/openvpn_shadowsocks/configure_container.sh server_scripts/openvpn_shadowsocks/Dockerfile server_scripts/openvpn_shadowsocks/run_container.sh server_scripts/openvpn_shadowsocks/start.sh server_scripts/openvpn_shadowsocks/template.ovpn + server_scripts/openvpn/configure_container.sh + server_scripts/openvpn/Dockerfile + server_scripts/openvpn/run_container.sh + server_scripts/openvpn/start.sh + server_scripts/openvpn/template.ovpn + server_scripts/prepare_host.sh + server_scripts/remove_all_containers.sh + server_scripts/remove_container.sh + server_scripts/setup_host_firewall.sh + server_scripts/sftp/configure_container.sh + server_scripts/sftp/Dockerfile + server_scripts/sftp/run_container.sh + server_scripts/socks5_proxy/configure_container.sh + server_scripts/socks5_proxy/Dockerfile + server_scripts/socks5_proxy/run_container.sh + server_scripts/socks5_proxy/start.sh + server_scripts/website_tor/configure_container.sh + server_scripts/website_tor/Dockerfile + server_scripts/website_tor/run_container.sh server_scripts/wireguard/configure_container.sh server_scripts/wireguard/Dockerfile server_scripts/wireguard/run_container.sh server_scripts/wireguard/start.sh server_scripts/wireguard/template.conf - server_scripts/website_tor/configure_container.sh - server_scripts/website_tor/run_container.sh - ui/qml/Config/GlobalConfig.qml - ui/qml/Config/qmldir - server_scripts/check_server_is_busy.sh - server_scripts/dns/configure_container.sh - server_scripts/dns/Dockerfile - server_scripts/dns/run_container.sh - server_scripts/sftp/configure_container.sh - server_scripts/sftp/Dockerfile - server_scripts/sftp/run_container.sh - server_scripts/ipsec/configure_container.sh - server_scripts/ipsec/Dockerfile - server_scripts/ipsec/run_container.sh - server_scripts/ipsec/start.sh - server_scripts/ipsec/mobileconfig.plist - server_scripts/ipsec/strongswan.profile - server_scripts/website_tor/Dockerfile - server_scripts/check_user_in_sudo.sh - ui/qml/Controls2/BasicButtonType.qml - ui/qml/Controls2/TextFieldWithHeaderType.qml - ui/qml/Controls2/LabelWithButtonType.qml - images/controls/arrow-right.svg - images/controls/chevron-right.svg - ui/qml/Controls2/ImageButtonType.qml - ui/qml/Controls2/CardType.qml - ui/qml/Controls2/CheckBoxType.qml - images/controls/check.svg - ui/qml/Controls2/DropDownType.qml - ui/qml/Pages2/PageSetupWizardStart.qml - ui/qml/main2.qml - images/amneziaBigLogo.png - ui/qml/Controls2/FlickableType.qml - ui/qml/Pages2/PageSetupWizardCredentials.qml - ui/qml/Controls2/HeaderType.qml - images/controls/arrow-left.svg - ui/qml/Pages2/PageSetupWizardProtocols.qml - ui/qml/Pages2/PageSetupWizardEasy.qml - images/controls/chevron-down.svg - images/controls/chevron-up.svg - ui/qml/Controls2/TextTypes/ParagraphTextType.qml - ui/qml/Controls2/TextTypes/Header2TextType.qml - ui/qml/Controls2/HorizontalRadioButton.qml - ui/qml/Controls2/VerticalRadioButton.qml - ui/qml/Controls2/SwitcherType.qml - ui/qml/Controls2/TabButtonType.qml - ui/qml/Pages2/PageSetupWizardProtocolSettings.qml - ui/qml/Pages2/PageSetupWizardInstalling.qml - ui/qml/Pages2/PageSetupWizardConfigSource.qml - images/controls/folder-open.svg - images/controls/qr-code.svg - images/controls/text-cursor.svg - ui/qml/Pages2/PageSetupWizardTextKey.qml - ui/qml/Pages2/PageStart.qml - ui/qml/Controls2/TabImageButtonType.qml - images/controls/home.svg - images/controls/settings-2.svg - images/controls/share-2.svg - ui/qml/Pages2/PageHome.qml - ui/qml/Pages2/PageSettingsServersList.qml - ui/qml/Pages2/PageShare.qml - ui/qml/Controls2/TextTypes/Header1TextType.qml - ui/qml/Controls2/TextTypes/LabelTextType.qml - ui/qml/Controls2/TextTypes/ButtonTextType.qml - ui/qml/Controls2/Header2Type.qml - images/controls/plus.svg - ui/qml/Components/ConnectButton.qml - images/controls/download.svg - ui/qml/Controls2/ProgressBarType.qml - ui/qml/Components/ConnectionTypeSelectionDrawer.qml - ui/qml/Components/HomeContainersListView.qml - ui/qml/Controls2/TextTypes/CaptionTextType.qml - images/controls/settings.svg - ui/qml/Pages2/PageSettingsServerInfo.qml - ui/qml/Controls2/PageType.qml - ui/qml/Controls2/PopupType.qml - images/controls/edit-3.svg - ui/qml/Pages2/PageSettingsServerData.qml - ui/qml/Components/SettingsContainersListView.qml - ui/qml/Controls2/TextTypes/ListItemTitleType.qml - ui/qml/Controls2/DividerType.qml - ui/qml/Controls2/StackViewType.qml - ui/qml/Pages2/PageSettings.qml - images/controls/amnezia.svg - images/controls/app.svg - images/controls/radio.svg - images/controls/save.svg - images/controls/server.svg - ui/qml/Pages2/PageSettingsServerProtocols.qml - ui/qml/Pages2/PageSettingsServerServices.qml - ui/qml/Pages2/PageSetupWizardViewConfig.qml - images/controls/file-cog-2.svg - ui/qml/Components/QuestionDrawer.qml - ui/qml/Pages2/PageDeinstalling.qml - ui/qml/Controls2/BackButtonType.qml - ui/qml/Pages2/PageSettingsServerProtocol.qml - ui/qml/Components/TransportProtoSelector.qml - ui/qml/Controls2/ListViewWithRadioButtonType.qml - images/controls/radio-button.svg - images/controls/radio-button-inner-circle.png - images/controls/radio-button-pressed.svg - images/controls/radio-button-inner-circle-pressed.png - ui/qml/Components/ShareConnectionDrawer.qml - ui/qml/Pages2/PageSettingsConnection.qml - ui/qml/Pages2/PageSettingsDns.qml - ui/qml/Pages2/PageSettingsApplication.qml - ui/qml/Pages2/PageSettingsBackup.qml - images/controls/delete.svg - ui/qml/Pages2/PageSettingsAbout.qml - images/controls/github.svg - images/controls/mail.svg - images/controls/telegram.svg - ui/qml/Controls2/TextTypes/SmallTextType.qml - ui/qml/Filters/ContainersModelFilters.qml - ui/qml/Components/SelectLanguageDrawer.qml - ui/qml/Controls2/BusyIndicatorType.qml - ui/qml/Pages2/PageProtocolOpenVpnSettings.qml - ui/qml/Pages2/PageProtocolShadowSocksSettings.qml - ui/qml/Pages2/PageProtocolCloakSettings.qml - ui/qml/Pages2/PageProtocolXraySettings.qml - ui/qml/Pages2/PageProtocolRaw.qml - ui/qml/Pages2/PageSettingsLogging.qml - ui/qml/Pages2/PageServiceSftpSettings.qml - images/controls/copy.svg - ui/qml/Pages2/PageServiceTorWebsiteSettings.qml - ui/qml/Pages2/PageSetupWizardQrReader.qml - images/controls/eye.svg - images/controls/eye-off.svg - ui/qml/Pages2/PageSettingsSplitTunneling.qml - ui/qml/Controls2/ContextMenuType.qml - ui/qml/Controls2/TextAreaType.qml - images/controls/trash.svg - images/controls/more-vertical.svg - ui/qml/Controls2/ListViewWithLabelsType.qml - ui/qml/Pages2/PageServiceDnsSettings.qml - ui/qml/Controls2/TopCloseButtonType.qml - images/controls/x-circle.svg - ui/qml/Pages2/PageProtocolAwgSettings.qml - server_scripts/awg/template.conf - server_scripts/awg/start.sh - server_scripts/awg/configure_container.sh - server_scripts/awg/run_container.sh - server_scripts/awg/Dockerfile - ui/qml/Pages2/PageShareFullAccess.qml - images/controls/close.svg - images/controls/search.svg server_scripts/xray/configure_container.sh server_scripts/xray/Dockerfile server_scripts/xray/run_container.sh server_scripts/xray/start.sh server_scripts/xray/template.json - ui/qml/Pages2/PageProtocolWireGuardSettings.qml + ui/qml/Components/ConnectButton.qml + ui/qml/Components/ConnectionTypeSelectionDrawer.qml + ui/qml/Components/HomeContainersListView.qml ui/qml/Components/HomeSplitTunnelingDrawer.qml - images/controls/split-tunneling.svg - ui/qml/Controls2/DrawerType2.qml - ui/qml/Pages2/PageSettingsAppSplitTunneling.qml ui/qml/Components/InstalledAppsDrawer.qml - images/controls/alert-circle.svg - images/controls/file-check-2.svg + ui/qml/Components/QuestionDrawer.qml + ui/qml/Components/SelectLanguageDrawer.qml + ui/qml/Components/ServersListView.qml + ui/qml/Components/SettingsContainersListView.qml + ui/qml/Components/ShareConnectionDrawer.qml + ui/qml/Components/TransportProtoSelector.qml + ui/qml/Config/GlobalConfig.qml + ui/qml/Config/qmldir + ui/qml/Controls2/BackButtonType.qml + ui/qml/Controls2/BasicButtonType.qml + ui/qml/Controls2/BusyIndicatorType.qml + ui/qml/Controls2/CardType.qml + ui/qml/Controls2/CardWithIconsType.qml + ui/qml/Controls2/CheckBoxType.qml + ui/qml/Controls2/ContextMenuType.qml + ui/qml/Controls2/DividerType.qml + ui/qml/Controls2/DrawerType2.qml + ui/qml/Controls2/DropDownType.qml + ui/qml/Controls2/FlickableType.qml + ui/qml/Controls2/Header2Type.qml + ui/qml/Controls2/HeaderType.qml + ui/qml/Controls2/HorizontalRadioButton.qml + ui/qml/Controls2/ImageButtonType.qml + ui/qml/Controls2/LabelWithButtonType.qml + ui/qml/Controls2/LabelWithImageType.qml + ui/qml/Controls2/ListViewWithLabelsType.qml + ui/qml/Controls2/ListViewWithRadioButtonType.qml + ui/qml/Controls2/PageType.qml + ui/qml/Controls2/PopupType.qml + ui/qml/Controls2/ProgressBarType.qml + ui/qml/Controls2/StackViewType.qml + ui/qml/Controls2/SwitcherType.qml + ui/qml/Controls2/TabButtonType.qml + ui/qml/Controls2/TabImageButtonType.qml + ui/qml/Controls2/TextAreaType.qml + ui/qml/Controls2/TextAreaWithFooterType.qml + ui/qml/Controls2/TextFieldWithHeaderType.qml + ui/qml/Controls2/TextTypes/ButtonTextType.qml + ui/qml/Controls2/TextTypes/CaptionTextType.qml + ui/qml/Controls2/TextTypes/Header1TextType.qml + ui/qml/Controls2/TextTypes/Header2TextType.qml + ui/qml/Controls2/TextTypes/LabelTextType.qml + ui/qml/Controls2/TextTypes/ListItemTitleType.qml + ui/qml/Controls2/TextTypes/ParagraphTextType.qml + ui/qml/Controls2/TextTypes/SmallTextType.qml + ui/qml/Controls2/TopCloseButtonType.qml + ui/qml/Controls2/VerticalRadioButton.qml ui/qml/Controls2/WarningType.qml - fonts/pt-root-ui_vf.ttf - ui/qml/Modules/Style/qmldir + ui/qml/Filters/ContainersModelFilters.qml + ui/qml/main2.qml ui/qml/Modules/Style/AmneziaStyle.qml + ui/qml/Modules/Style/qmldir + ui/qml/Pages2/PageDeinstalling.qml + ui/qml/Pages2/PageDevMenu.qml + ui/qml/Pages2/PageHome.qml + ui/qml/Pages2/PageProtocolAwgSettings.qml + ui/qml/Pages2/PageProtocolCloakSettings.qml + ui/qml/Pages2/PageProtocolOpenVpnSettings.qml + ui/qml/Pages2/PageProtocolRaw.qml + ui/qml/Pages2/PageProtocolShadowSocksSettings.qml + ui/qml/Pages2/PageProtocolWireGuardSettings.qml + ui/qml/Pages2/PageProtocolXraySettings.qml + ui/qml/Pages2/PageServiceDnsSettings.qml + ui/qml/Pages2/PageServiceSftpSettings.qml ui/qml/Pages2/PageServiceSocksProxySettings.qml - server_scripts/socks5_proxy/run_container.sh - server_scripts/socks5_proxy/Dockerfile - server_scripts/socks5_proxy/configure_container.sh - server_scripts/socks5_proxy/start.sh + ui/qml/Pages2/PageServiceTorWebsiteSettings.qml + ui/qml/Pages2/PageSettings.qml + ui/qml/Pages2/PageSettingsAbout.qml + ui/qml/Pages2/PageSettingsApiLanguageList.qml + ui/qml/Pages2/PageSettingsApiServerInfo.qml + ui/qml/Pages2/PageSettingsApplication.qml + ui/qml/Pages2/PageSettingsAppSplitTunneling.qml + ui/qml/Pages2/PageSettingsBackup.qml + ui/qml/Pages2/PageSettingsConnection.qml + ui/qml/Pages2/PageSettingsDns.qml + ui/qml/Pages2/PageSettingsLogging.qml + ui/qml/Pages2/PageSettingsServerData.qml + ui/qml/Pages2/PageSettingsServerInfo.qml + ui/qml/Pages2/PageSettingsServerProtocol.qml + ui/qml/Pages2/PageSettingsServerProtocols.qml + ui/qml/Pages2/PageSettingsServerServices.qml + ui/qml/Pages2/PageSettingsServersList.qml + ui/qml/Pages2/PageSettingsSplitTunneling.qml ui/qml/Pages2/PageProtocolAwgClientSettings.qml ui/qml/Pages2/PageProtocolWireGuardClientSettings.qml - ui/qml/Pages2/PageSetupWizardApiServicesList.qml ui/qml/Pages2/PageSetupWizardApiServiceInfo.qml - ui/qml/Controls2/CardWithIconsType.qml - images/controls/tag.svg - images/controls/history.svg - images/controls/gauge.svg - images/controls/map-pin.svg - ui/qml/Controls2/LabelWithImageType.qml - images/controls/info.svg - ui/qml/Controls2/TextAreaWithFooterType.qml - images/controls/scan-line.svg - images/controls/folder-search-2.svg - ui/qml/Pages2/PageSettingsApiServerInfo.qml - images/controls/bug.svg - ui/qml/Pages2/PageDevMenu.qml - images/controls/refresh-cw.svg - ui/qml/Pages2/PageSettingsApiLanguageList.qml - images/controls/archive-restore.svg - images/controls/help-circle.svg + ui/qml/Pages2/PageSetupWizardApiServicesList.qml + ui/qml/Pages2/PageSetupWizardConfigSource.qml + ui/qml/Pages2/PageSetupWizardCredentials.qml + ui/qml/Pages2/PageSetupWizardEasy.qml + ui/qml/Pages2/PageSetupWizardInstalling.qml + ui/qml/Pages2/PageSetupWizardProtocols.qml + ui/qml/Pages2/PageSetupWizardProtocolSettings.qml + ui/qml/Pages2/PageSetupWizardQrReader.qml + ui/qml/Pages2/PageSetupWizardStart.qml + ui/qml/Pages2/PageSetupWizardTextKey.qml + ui/qml/Pages2/PageSetupWizardViewConfig.qml + ui/qml/Pages2/PageShare.qml + ui/qml/Pages2/PageShareFullAccess.qml + ui/qml/Pages2/PageStart.qml images/flagKit/ZW.svg diff --git a/client/ui/controllers/focusController.cpp b/client/ui/controllers/focusController.cpp new file mode 100644 index 00000000..58e33c7e --- /dev/null +++ b/client/ui/controllers/focusController.cpp @@ -0,0 +1,476 @@ +#include "focusController.h" + +#include +#include +#include +#include +#include +#include + + +bool isVisible(QObject* item) +{ + const auto res = item->property("visible").toBool(); + // qDebug() << "==>> " << (res ? "VISIBLE" : "NOT visible") << item; + return res; +} + +bool isFocusable(QObject* item) +{ + const auto res = item->property("isFocusable").toBool(); + // qDebug() << "==>> " << (res ? "FOCUSABLE" : "NOT focusable") << item; + return res; +} + +QRectF getItemCoordsOnScene(QQuickItem* item) // TODO: remove? +{ + if (!item) return {}; + return item->mapRectToScene(item->childrenRect()); +} + +QPointF getItemCenterPointOnScene(QQuickItem* item) +{ + const auto x0 = item->x() + (item->width() / 2); + const auto y0 = item->y() + (item->height() / 2); + return item->parentItem()->mapToScene(QPointF{x0, y0}); +} + +bool isLess(QObject* item1, QObject* item2) +{ + const auto p1 = getItemCenterPointOnScene(qobject_cast(item1)); + const auto p2 = getItemCenterPointOnScene(qobject_cast(item2)); + return (p1.y() == p2.y()) ? (p1.x() < p2.x()) : (p1.y() < p2.y()); +} + +bool isListView(QObject* item) +{ + return item->inherits("QQuickListView"); +} + +bool isOnTheScene(QObject* object) +{ + QQuickItem* item = qobject_cast(object); + if (!item) { + qWarning() << "Couldn't recognize object as item"; + return false; + } + + if (!item->isVisible()) { + qInfo() << "The item is not visible: " << item; + return false; + } + + QRectF itemRect{}; // TODO: ListView couln't get into list because it's children's rect is too large + // if (isListView(item)) { + // itemRect = QRectF(item->x(), item->y(), item->width(), item->height()); + // } else { + itemRect = item->mapRectToScene(item->childrenRect()); + // } + QQuickWindow* window = item->window(); + if (!window) { + qWarning() << "Couldn't get the window on the Scene check"; + return false; + } + + // const auto contentItem = window->contentItem(); + // if (!contentItem) { + // qWarning() << "Couldn't get the content item on the Scene check"; + // return false; + // } + // QRectF windowRect = contentItem->childrenRect(); + // const auto res = (windowRect.contains(itemRect) || isListView(item)); + // // qDebug() << (res ? "===>> item is inside the Scene" : "===>> ITEM IS OUTSIDE THE SCENE") << " itemRect: " << itemRect << "; windowRect: " << windowRect; + // return res; + return true; +} + +bool isEnabled(QObject* obj) +{ + const auto item = qobject_cast(obj); + return item && item->isEnabled(); +} + +QQuickItem* getPageOfItem(QQuickItem* item) // TODO: remove? +{ + if(!item) { + qWarning() << "item is null"; + return {}; + } + const auto pagePattern = QString::fromLatin1("Page"); + QString className{item->metaObject()->className()}; + qDebug() << "=====================>> Item: " << item << " with name: " << item->metaObject()->className(); + const auto isPage = className.contains(pagePattern, Qt::CaseSensitive); + if(isPage) { + return item; + } else { + return getPageOfItem(item->parentItem()); + } +} + +QList getSubChain(QObject* item) +{ + QList res; + if (!item) { + qDebug() << "null top item"; + return res; + } + const auto children = item->children(); + for(const auto child : children) { + if (child + && isFocusable(child) + && (isOnTheScene(child)) + && isEnabled(child) + ) { + res.append(child); + // qDebug() << "==>> [*** added ***] " << qobject_cast(child); + } else { + // qDebug() << "==>> [** skipped **] " << qobject_cast(child); + res.append(getSubChain(child)); + } + } + return res; +} + +template +void printItems(const T& items, QObject* current_item) +{ + qDebug() << "**********************************************"; + for(const auto& item : items) { + QQuickItem* i = qobject_cast(item); + QPointF coords {getItemCenterPointOnScene(i)}; + QString prefix = current_item == i ? "==>" : " "; + qDebug() << prefix << " Item: " << i << " with coords: " << coords; + } + qDebug() << "**********************************************"; +} + +/*! + * \brief The ListViewFocusController class manages the focus of elements in ListView + * \details This class object moving focus to ListView's controls since ListView stores + * it's data implicitly and it could be got one by one. + * + * This class was made to store as less as possible data getting it from QML + * when it's needed. + */ +class ListViewFocusController : public QObject +{ +public: + explicit ListViewFocusController(QQuickItem* listView, QObject* parent = nullptr); + ~ListViewFocusController(); + + void incrementIndex(); + void decrementCurrentIndex(); + void positionViewAtIndex(); + void focusNextItem(); + void focusPreviousItem(); + void resetFocusChain(); + bool isListViewLastFocusItem(); + bool isDelegateLastFocusItem(); + +private: + int size() const; + int currentIndex() const; + QQuickItem* itemAtIndex(const int index); + QQuickItem* currentDelegate(); + QQuickItem* focusedItem(); + + QQuickItem* m_listView; + QList m_focusChain; + QQuickItem* m_focusedItem; + qsizetype m_focusedItemIndex; + qsizetype m_delegateIndex; +}; + +ListViewFocusController::ListViewFocusController(QQuickItem* listView, QObject* parent) + : QObject{parent} + , m_listView{listView} + , m_focusChain{} + , m_focusedItem{nullptr} + , m_focusedItemIndex{-1} + , m_delegateIndex{0} +{ +} + +ListViewFocusController::~ListViewFocusController() +{ + +} + +void ListViewFocusController::positionViewAtIndex() +{ + QMetaObject::invokeMethod(m_listView, "positionViewAtIndex", + Q_ARG(int, m_delegateIndex), // Index + Q_ARG(int, 2)); // PositionMode (0 = Visible) +} + +int ListViewFocusController::size() const +{ + return m_listView->property("count").toInt(); +} + +int ListViewFocusController::currentIndex() const +{ + return m_delegateIndex; +} + +void ListViewFocusController::incrementIndex() +{ + m_delegateIndex++; +} + +void ListViewFocusController::decrementCurrentIndex() +{ + m_delegateIndex--; +} + +QQuickItem* ListViewFocusController::itemAtIndex(const int index) +{ + QQuickItem* item{nullptr}; + + QMetaObject::invokeMethod(m_listView, "itemAtIndex", + Q_RETURN_ARG(QQuickItem*, item), + Q_ARG(int, index)); + + return item; +} + +QQuickItem* ListViewFocusController::currentDelegate() +{ + return itemAtIndex(m_delegateIndex); +} + +QQuickItem* ListViewFocusController::focusedItem() +{ + return m_focusedItem; +} + +void ListViewFocusController::focusNextItem() +{ + if (m_focusChain.empty()) { + qWarning() << "Empty focusChain with current delegate: " << currentDelegate(); + m_focusChain = getSubChain(currentDelegate()); + } + m_focusedItemIndex++; + m_focusedItem = qobject_cast(m_focusChain.at(m_focusedItemIndex)); + m_focusedItem->forceActiveFocus(); +} + +void ListViewFocusController::focusPreviousItem() +{ + // TODO: implement +} + +void ListViewFocusController::resetFocusChain() +{ + m_focusChain.clear(); + m_focusedItem = nullptr; + m_focusedItemIndex = -1; +} + +bool ListViewFocusController::isDelegateLastFocusItem() +{ + return m_focusedItem && (m_focusedItem == m_focusChain.last()); +} + +bool ListViewFocusController::isListViewLastFocusItem() +{ + return (m_delegateIndex == size() - 1) && isDelegateLastFocusItem(); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +FocusController::FocusController(QQmlApplicationEngine* engine, QObject *parent) + : QObject{parent} + , m_engine{engine} + , m_focusChain{} + , m_focusedItem{nullptr} + , m_focusedItemIndex{-1} + , m_rootItem{nullptr} + , m_lvfc{nullptr} +{ + connect(this, &FocusController::rootItemChanged, this, &FocusController::reload); +} + +void FocusController::resetFocus() +{ + reload(); + if (m_focusChain.empty()) { + qWarning() << "There is no focusable elements"; + return; + } + if(m_focusedItemIndex == -1) { + m_focusedItemIndex = 0; + m_focusedItem = qobject_cast(m_focusChain.at(m_focusedItemIndex)); + m_focusedItem->forceActiveFocus(); + } +} + +void FocusController::nextKeyTabItem() +{ + if (m_lvfc) { + focusNextListViewItem(); // Need to go on first element by default? + return; + } + + reload(); + + if(m_focusChain.empty()) { + qWarning() << "There are no items to navigate"; + return; + } + + if (m_focusedItemIndex == (m_focusChain.size() - 1)) { + qDebug() << "Last focus index. Making it zero"; + m_focusedItemIndex = 0; + } else { + qDebug() << "Incrementing focus index"; + m_focusedItemIndex++; + } + + m_focusedItem = qobject_cast(m_focusChain.at(m_focusedItemIndex)); + + if(m_focusedItem == nullptr) { + qWarning() << "Failed to get item to focus on"; + return; + } + + if(isListView(m_focusedItem)) { + qDebug() << "===>> Found ListView Item: " << m_focusedItem; // TODO: remove? + m_lvfc = new ListViewFocusController(m_focusedItem, this); + focusNextListViewItem(); + return; + } + + m_focusedItem->forceActiveFocus(Qt::TabFocusReason); + + printItems(m_focusChain, m_focusedItem); +} + +void FocusController::focusNextListViewItem() +{ + m_lvfc->focusNextItem(); + + if (m_lvfc->isListViewLastFocusItem()) { + delete m_lvfc; + m_lvfc = nullptr; + } else if (m_lvfc->isDelegateLastFocusItem()) { + m_lvfc->resetFocusChain(); + m_lvfc->incrementIndex(); + m_lvfc->positionViewAtIndex(); + } +} + +void FocusController::focusPreviousListViewItem() +{ + // TODO: implement +} + +void FocusController::previousKeyTabItem() +{ + reload(); + + if(m_focusChain.empty()) { + return; + } + + if (m_focusedItemIndex <= 0) { + m_focusedItemIndex = m_focusChain.size() - 1; + } else { + m_focusedItemIndex--; + } + + m_focusedItem = qobject_cast(m_focusChain.at(m_focusedItemIndex)); + m_focusedItem->forceActiveFocus(Qt::TabFocusReason); + + qDebug() << "--> Current focus was changed to " << m_focusedItem; +} + +void FocusController::nextKeyUpItem() +{ + qDebug() << "nextKeyUpItem" << "triggered"; +} + +void FocusController::nextKeyDownItem() +{ + qDebug() << "nextKeyDownItem" << "triggered"; +} + +void FocusController::nextKeyLeftItem() +{ + qDebug() << "nextKeyLeftItem" << "triggered"; +} + +void FocusController::nextKeyRightItem() +{ + qDebug() << "nextKeyRightItem" << "triggered"; +} + +void FocusController::reload() +{ + m_focusChain.clear(); + + QObjectList rootObjects; + + const auto rootItem = m_rootItem; + + if (rootItem != nullptr) { + qDebug() << "*** root item: " << rootItem; + rootObjects << qobject_cast(rootItem); + } else { + qDebug() << "*** root item is null"; + rootObjects = m_engine->rootObjects(); + } + + if(rootObjects.empty()) { + qWarning() << "Empty focus chain detected!"; + emit focusChainChanged(); + return; + } + + for(const auto object : rootObjects) { + m_focusChain.append(getSubChain(object)); + } + + std::sort(m_focusChain.begin(), m_focusChain.end(), isLess); + + printItems(m_focusChain, m_focusedItem); + + emit focusChainChanged(); + + if (m_focusChain.empty()) { + m_focusedItemIndex = -1; + qWarning() << "reloaded to empty focus chain"; + return; + } + + QQuickWindow* window = qobject_cast(rootObjects[0]); + if (!window) { + window = qobject_cast(rootObjects[0])->window(); + } + + if (!window) { + qCritical() << "Couldn't get the current window"; + return; + } + + // qDebug() << "==> Active Focused Item: " << window->activeFocusItem(); + // qDebug() << "--> Active Focused Object: " << window->focusObject(); + // qDebug() << ">>> Current Focused Item: " << m_focused_item; + + m_focusedItemIndex = m_focusChain.indexOf(window->activeFocusItem()); + + if(m_focusedItemIndex == -1) { + qDebug() << "===>> No focus item in chain. Moving focus to begin..."; + // m_focused_item_index = 0; // if not in focus chain current + return; + } + + m_focusedItem = qobject_cast(m_focusChain.at(m_focusedItemIndex)); + + m_focusedItem->forceActiveFocus(); +} + +void FocusController::setRootItem(QQuickItem* item) +{ + m_rootItem = item; +} diff --git a/client/ui/controllers/focusController.h b/client/ui/controllers/focusController.h new file mode 100644 index 00000000..f593fc8d --- /dev/null +++ b/client/ui/controllers/focusController.h @@ -0,0 +1,52 @@ +#ifndef FOCUSCONTROLLER_H +#define FOCUSCONTROLLER_H + +#include + +class QQuickItem; +class QQmlApplicationEngine; +class ListViewFocusController; + +class FocusController : public QObject +{ + Q_OBJECT +public: + explicit FocusController(QQmlApplicationEngine* engine, QObject *parent = nullptr); + ~FocusController() override = default; + + Q_INVOKABLE void nextKeyTabItem(); + Q_INVOKABLE void previousKeyTabItem(); + Q_INVOKABLE void nextKeyUpItem(); + Q_INVOKABLE void nextKeyDownItem(); + Q_INVOKABLE void nextKeyLeftItem(); + Q_INVOKABLE void nextKeyRightItem(); + +signals: + void nextTabItemChanged(QObject* item); + void previousTabItemChanged(QObject* item); + void nextKeyUpItemChanged(QObject* item); + void nextKeyDownItemChanged(QObject* item); + void nextKeyLeftItemChanged(QObject* item); + void nextKeyRightItemChanged(QObject* item); + void focusChainChanged(); + void rootItemChanged(); + +public slots: + void resetFocus(); + void reload(); + void setRootItem(QQuickItem* item); + +private: + void focusNextListViewItem(); + void focusPreviousListViewItem(); + + QQmlApplicationEngine* m_engine; // Pointer to engine to get root object + QList m_focusChain; // List of current objects to be focused + QQuickItem* m_focusedItem; // Pointer to the active focus item + qsizetype m_focusedItemIndex; // Active focus item's index in focus chain + QQuickItem* m_rootItem; + + ListViewFocusController* m_lvfc; // ListView focus manager +}; + +#endif // FOCUSCONTROLLER_H diff --git a/client/ui/controllers/pageController.cpp b/client/ui/controllers/pageController.cpp index bbcc55a1..d515df49 100644 --- a/client/ui/controllers/pageController.cpp +++ b/client/ui/controllers/pageController.cpp @@ -81,7 +81,7 @@ void PageController::keyPressEvent(Qt::Key key) case Qt::Key_Escape: { if (m_drawerDepth) { emit closeTopDrawer(); - setDrawerDepth(getDrawerDepth() - 1); + decrementDrawerDepth(); } else { emit escapePressed(); } @@ -142,11 +142,25 @@ void PageController::setDrawerDepth(const int depth) } } -int PageController::getDrawerDepth() +int PageController::getDrawerDepth() const { return m_drawerDepth; } +int PageController::incrementDrawerDepth() +{ + return ++m_drawerDepth; +} + +int PageController::decrementDrawerDepth() +{ + if (m_drawerDepth == 0) { + return m_drawerDepth; + } else { + return --m_drawerDepth; + } +} + void PageController::onShowErrorMessage(ErrorCode errorCode) { const auto fullErrorMessage = errorString(errorCode); diff --git a/client/ui/controllers/pageController.h b/client/ui/controllers/pageController.h index f89d39a1..ffbdd3a1 100644 --- a/client/ui/controllers/pageController.h +++ b/client/ui/controllers/pageController.h @@ -100,7 +100,9 @@ public slots: void closeApplication(); void setDrawerDepth(const int depth); - int getDrawerDepth(); + int getDrawerDepth() const; + int incrementDrawerDepth(); + int decrementDrawerDepth(); private slots: void onShowErrorMessage(amnezia::ErrorCode errorCode); @@ -135,9 +137,6 @@ signals: void escapePressed(); void closeTopDrawer(); - void forceTabBarActiveFocus(); - void forceStackActiveFocus(); - private: QSharedPointer m_serversModel; diff --git a/client/ui/qml/Components/ConnectButton.qml b/client/ui/qml/Components/ConnectButton.qml index fa18703b..c3032eab 100644 --- a/client/ui/qml/Components/ConnectButton.qml +++ b/client/ui/qml/Components/ConnectButton.qml @@ -16,6 +16,16 @@ Button { property string connectedButtonColor: AmneziaStyle.color.goldenApricot property bool buttonActiveFocus: activeFocus && (Qt.platform.os !== "android" || SettingsController.isOnTv()) + property bool isFocusable: true + + Keys.onTabPressed: { + FocusController.nextKeyTabItem() + } + + Keys.onBacktabPressed: { + FocusController.previousKeyTabItem() + } + implicitWidth: 190 implicitHeight: 190 diff --git a/client/ui/qml/Components/ConnectionTypeSelectionDrawer.qml b/client/ui/qml/Components/ConnectionTypeSelectionDrawer.qml index 23fe0d2a..1b43a628 100644 --- a/client/ui/qml/Components/ConnectionTypeSelectionDrawer.qml +++ b/client/ui/qml/Components/ConnectionTypeSelectionDrawer.qml @@ -14,7 +14,7 @@ DrawerType2 { width: parent.width height: parent.height - expandedContent: ColumnLayout { + expandedStateContent: ColumnLayout { id: content anchors.top: parent.top @@ -26,14 +26,6 @@ DrawerType2 { root.expandedHeight = content.implicitHeight + 32 } - Connections { - target: root - enabled: !GC.isMobile() - function onOpened() { - focusItem.forceActiveFocus() - } - } - Header2Type { Layout.fillWidth: true Layout.topMargin: 24 @@ -44,11 +36,6 @@ DrawerType2 { headerText: qsTr("Add new connection") } - Item { - id: focusItem - KeyNavigation.tab: ip.rightButton - } - LabelWithButtonType { id: ip Layout.fillWidth: true @@ -61,8 +48,6 @@ DrawerType2 { PageController.goToPage(PageEnum.PageSetupWizardCredentials) root.close() } - - KeyNavigation.tab: qrCode.rightButton } DividerType {} @@ -78,8 +63,6 @@ DrawerType2 { PageController.goToPage(PageEnum.PageSetupWizardConfigSource) root.close() } - - KeyNavigation.tab: focusItem } DividerType {} diff --git a/client/ui/qml/Components/HomeContainersListView.qml b/client/ui/qml/Components/HomeContainersListView.qml index b0e074d0..9c3d3857 100644 --- a/client/ui/qml/Components/HomeContainersListView.qml +++ b/client/ui/qml/Components/HomeContainersListView.qml @@ -20,52 +20,71 @@ ListView { property bool a: true width: rootWidth - height: menuContent.contentItem.height + height: contentItem.height // TODO: It should be fixed size, not content item height clip: true - interactive: false + // interactive: false - property FlickableType parentFlickable - property var lastItemTabClicked + // property FlickableType parentFlickable + // property var lastItemTabClicked - property int currentFocusIndex: 0 + // property int currentFocusIndex: 0 - activeFocusOnTab: true - onActiveFocusChanged: { - if (activeFocus) { - this.currentFocusIndex = 0 - this.itemAtIndex(currentFocusIndex).forceActiveFocus() - } - } + snapMode: ListView.SnapToItem + + // ScrollBar.vertical: ScrollBar {} + + property bool isFocusable: true Keys.onTabPressed: { - if (currentFocusIndex < this.count - 1) { - currentFocusIndex += 1 - this.itemAtIndex(currentFocusIndex).forceActiveFocus() - } else { - currentFocusIndex = 0 - if (lastItemTabClicked && typeof lastItemTabClicked === "function") { - lastItemTabClicked() - } - } + console.debug("--> Tab is pressed on HomeContainersListView: ", objectName) + FocusController.nextKeyTabItem() } - onVisibleChanged: { - if (visible) { - currentFocusIndex = 0 - focusItem.forceActiveFocus() - } - } - - Item { - id: focusItem + Keys.onBacktabPressed: { + console.debug("--> Shift+Tab is pressed on HomeContainersListView: ", objectName) + FocusController.previousKeyTabItem() } - onCurrentFocusIndexChanged: { - if (parentFlickable) { - parentFlickable.ensureVisible(this.itemAtIndex(currentFocusIndex)) - } - } + // activeFocusOnTab: true + // onActiveFocusChanged: { + // console.log("===========================") + // positionViewAtEnd() + // parentFlickable.ensureVisible(this.itemAtIndex(6)) + // 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 @@ -75,12 +94,6 @@ ListView { implicitWidth: rootWidth implicitHeight: content.implicitHeight - onActiveFocusChanged: { - if (activeFocus) { - containerRadioButton.forceActiveFocus() - } - } - ColumnLayout { id: content @@ -111,13 +124,13 @@ ListView { } if (checked) { - containersDropDown.close() + containersDropDown.closeTriggered() // TODO: containersDropDown is outside this file ServersModel.setDefaultContainer(ServersModel.defaultIndex, proxyDefaultServerContainersModel.mapToSource(index)) } else { ContainersModel.setProcessedContainerIndex(proxyDefaultServerContainersModel.mapToSource(index)) InstallController.setShouldCreateServer(false) PageController.goToPage(PageEnum.PageSetupWizardProtocolSettings) - containersDropDown.close() + containersDropDown.closeTriggered() } } diff --git a/client/ui/qml/Components/HomeSplitTunnelingDrawer.qml b/client/ui/qml/Components/HomeSplitTunnelingDrawer.qml index 405d4eda..b37b0b81 100644 --- a/client/ui/qml/Components/HomeSplitTunnelingDrawer.qml +++ b/client/ui/qml/Components/HomeSplitTunnelingDrawer.qml @@ -16,7 +16,7 @@ DrawerType2 { anchors.fill: parent expandedHeight: parent.height * 0.9 - expandedContent: ColumnLayout { + expandedStateContent: ColumnLayout { id: content anchors.top: parent.top @@ -24,14 +24,6 @@ DrawerType2 { anchors.right: parent.right spacing: 0 - Connections { - target: root - enabled: !GC.isMobile() - function onOpened() { - focusItem.forceActiveFocus() - } - } - Header2Type { Layout.fillWidth: true Layout.topMargin: 24 @@ -43,11 +35,6 @@ 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 @@ -59,7 +46,7 @@ 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 + // KeyNavigation.tab: siteBasedSplitTunnelingSwitch.visible ? siteBasedSplitTunnelingSwitch.rightButton : focusItem clickedFunction: function() { // PageController.goToPage(PageEnum.PageSettingsSplitTunneling) @@ -80,13 +67,13 @@ DrawerType2 { descriptionText: enabled && SitesModel.isTunnelingEnabled ? qsTr("Enabled") : qsTr("Disabled") rightImageSource: "qrc:/images/controls/chevron-right.svg" - KeyNavigation.tab: appSplitTunnelingSwitch.visible ? - appSplitTunnelingSwitch.rightButton : - focusItem + // KeyNavigation.tab: appSplitTunnelingSwitch.visible ? + // appSplitTunnelingSwitch.rightButton : + // focusItem clickedFunction: function() { PageController.goToPage(PageEnum.PageSettingsSplitTunneling) - root.close() + root.closeTriggered() } } @@ -103,11 +90,11 @@ DrawerType2 { descriptionText: AppSplitTunnelingModel.isTunnelingEnabled ? qsTr("Enabled") : qsTr("Disabled") rightImageSource: "qrc:/images/controls/chevron-right.svg" - KeyNavigation.tab: focusItem + // KeyNavigation.tab: focusItem clickedFunction: function() { PageController.goToPage(PageEnum.PageSettingsAppSplitTunneling) - root.close() + root.closeTriggered() } } diff --git a/client/ui/qml/Components/InstalledAppsDrawer.qml b/client/ui/qml/Components/InstalledAppsDrawer.qml index e5d10055..0ee48100 100644 --- a/client/ui/qml/Components/InstalledAppsDrawer.qml +++ b/client/ui/qml/Components/InstalledAppsDrawer.qml @@ -26,7 +26,7 @@ DrawerType2 { id: installedAppsModel } - expandedContent: Item { + expandedStateContent: Item { id: container implicitHeight: expandedHeight diff --git a/client/ui/qml/Components/QuestionDrawer.qml b/client/ui/qml/Components/QuestionDrawer.qml index a0e86dbc..09e8638a 100644 --- a/client/ui/qml/Components/QuestionDrawer.qml +++ b/client/ui/qml/Components/QuestionDrawer.qml @@ -20,7 +20,7 @@ DrawerType2 { property var yesButtonFunction property var noButtonFunction - expandedContent: ColumnLayout { + expandedStateContent: ColumnLayout { id: content anchors.top: parent.top @@ -37,7 +37,11 @@ DrawerType2 { target: root enabled: !GC.isMobile() function onOpened() { - focusItem.forceActiveFocus() + FocusController.setRoot(root) + } + + function onClosed() { + FocusController.setRoot(null) } } @@ -59,11 +63,6 @@ DrawerType2 { text: descriptionText } - Item { - id: focusItem - KeyNavigation.tab: yesButton - } - BasicButtonType { id: yesButton Layout.fillWidth: true @@ -78,8 +77,6 @@ DrawerType2 { yesButtonFunction() } } - - KeyNavigation.tab: noButton } BasicButtonType { @@ -102,8 +99,6 @@ DrawerType2 { noButtonFunction() } } - - KeyNavigation.tab: focusItem } } } diff --git a/client/ui/qml/Components/SelectLanguageDrawer.qml b/client/ui/qml/Components/SelectLanguageDrawer.qml index 4d9d7f0e..3124445e 100644 --- a/client/ui/qml/Components/SelectLanguageDrawer.qml +++ b/client/ui/qml/Components/SelectLanguageDrawer.qml @@ -11,7 +11,7 @@ import "../Config" DrawerType2 { id: root - expandedContent: Item { + expandedStateContent: Item { id: container implicitHeight: root.height * 0.9 @@ -24,13 +24,12 @@ DrawerType2 { target: root enabled: !GC.isMobile() function onOpened() { - focusItem.forceActiveFocus() + FocusController.setRoot(root) } - } - Item { - id: focusItem - KeyNavigation.tab: backButton + function onClosed() { + FocusController.setRoot(null) + } } ColumnLayout { @@ -44,8 +43,7 @@ DrawerType2 { BackButtonType { id: backButton backButtonImage: "qrc:/images/controls/arrow-left.svg" - backButtonFunction: function() { root.close() } - KeyNavigation.tab: listView + backButtonFunction: function() { root.closeTriggered() } } } @@ -97,15 +95,15 @@ DrawerType2 { } } - Keys.onTabPressed: { - if (currentFocusIndex < this.count - 1) { - currentFocusIndex += 1 - this.itemAtIndex(currentFocusIndex).forceActiveFocus() - } else { - listViewFocusItem.forceActiveFocus() - focusItem.forceActiveFocus() - } - } + // Keys.onTabPressed: { + // if (currentFocusIndex < this.count - 1) { + // currentFocusIndex += 1 + // this.itemAtIndex(currentFocusIndex).forceActiveFocus() + // } else { + // listViewFocusItem.forceActiveFocus() + // focusItem.forceActiveFocus() + // } + // } Item { id: listViewFocusItem @@ -195,7 +193,7 @@ DrawerType2 { onClicked: { listView.currentIndex = index LanguageModel.changeLanguage(languageIndex) - root.close() + root.closeTriggered() } } } diff --git a/client/ui/qml/Components/ServersListView.qml b/client/ui/qml/Components/ServersListView.qml new file mode 100644 index 00000000..76f3f267 --- /dev/null +++ b/client/ui/qml/Components/ServersListView.qml @@ -0,0 +1,208 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +import SortFilterProxyModel 0.2 + +import PageEnum 1.0 +import ProtocolEnum 1.0 +import ContainerProps 1.0 +import ContainersModelFilters 1.0 +import Style 1.0 + +import "./" +import "../Controls2" +import "../Controls2/TextTypes" +import "../Config" + +ListView { + id: root + + 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 + objectName: "scrollBar" + policy: root.height >= root.contentHeight ? ScrollBar.AlwaysOff : ScrollBar.AlwaysOn + } + + readonly property bool isFocusable: true + + // Keys.onTabPressed: { + // FocusController.nextKeyTabItem() + // } + + // activeFocusOnTab: true + // focus: true + + property int focusItemIndex: 0 + + // onFocusItemIndexChanged: { + // console.debug("===>> root onFocusItemIndexChanged") + + // // const focusedElement = root.itemAtIndex(focusItemIndex) + // // if (focusedElement) { + // // if (focusedElement.y + focusedElement.height > root.height) { + // // root.contentY = focusedElement.y + focusedElement.height - root.height + // // } else { + // // root.contentY = 0 + // // } + // // } + // } + + Keys.onUpPressed: scrollBar.decrease() + Keys.onDownPressed: scrollBar.increase() + + // Connections { + // target: drawer + // enabled: !GC.isMobile() + // function onIsCollapsedChanged() { + // if (drawer.isCollapsedStateActive) { + // const item = root.itemAtIndex(root.focusItemIndex) + // if (item) { item.serverRadioButtonProperty.focus = false } + // } + // } + // } + + Connections { + target: ServersModel + function onDefaultServerIndexChanged(serverIndex) { + root.currentIndex = serverIndex + } + } + + clip: true + + delegate: Item { + id: menuContentDelegate + objectName: "menuContentDelegate" + + property variant delegateData: model + property VerticalRadioButton serverRadioButtonProperty: serverRadioButton + + implicitWidth: root.width + implicitHeight: serverRadioButtonContent.implicitHeight + + ColumnLayout { + id: serverRadioButtonContent + objectName: "serverRadioButtonContent" + + anchors.fill: parent + anchors.rightMargin: 16 + anchors.leftMargin: 16 + + spacing: 0 + + RowLayout { + objectName: "serverRadioButtonRowLayout" + + Layout.fillWidth: true + VerticalRadioButton { + id: serverRadioButton + objectName: "serverRadioButton" + + Layout.fillWidth: true + focus: true + text: name + descriptionText: serverDescription + + checked: index === root.currentIndex + checkable: !ConnectionController.isConnected + + ButtonGroup.group: serversRadioButtonGroup + + onClicked: { + if (ConnectionController.isConnected) { + PageController.showNotificationMessage(qsTr("Unable change server while there is an active connection")) + return + } + + root.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 + objectName: "serverInfoButton" + + // signal keyTabOnLastElement + + // isFocusable: false + + image: "qrc:/images/controls/settings.svg" + imageColor: AmneziaStyle.color.paleGray + + implicitWidth: 56 + implicitHeight: 56 + + z: 1 + + // onActiveFocusChanged: { + // console.debug("===>> serverInfoButton::activeFocusChanged") + + // if (activeFocus) { + // if (currentIndex === root.count - 1) { + // console.log("---> Latest element") + // keyTabOnLastElement() + // } + + // console.log("--->>", currentIndex) + // // serverRadioButton.forceActiveFocus() + // } + // } + + // onKeyTabOnLastElement: { + // console.log("*** Signal emmited! ***") + // FocusController.nextKeyTabItem() + // } + + // Keys.onTabPressed: { + // console.log("===>> serverInfoButton::Keys.onTabPressed") + // if (root.focusItemIndex < root.count - 1) { + // root.focusItemIndex++ + // root.itemAtIndex(root.focusItemIndex).forceActiveFocus() + // } else { + // FocusController.nextKeyTabItem() + // root.contentY = 0 + // } + // } + Keys.onEnterPressed: serverInfoButton.clicked() + Keys.onReturnPressed: serverInfoButton.clicked() + + onClicked: function() { + console.debug("===>> onClicked serverInfoButton") + + ServersModel.processedIndex = index + PageController.goToPage(PageEnum.PageSettingsServerInfo) + drawer.closeTriggered() + } + } + } + + DividerType { + Layout.fillWidth: true + Layout.leftMargin: 0 + Layout.rightMargin: 0 + } + } + } +} diff --git a/client/ui/qml/Components/ShareConnectionDrawer.qml b/client/ui/qml/Components/ShareConnectionDrawer.qml index d2bf28ab..261cd742 100644 --- a/client/ui/qml/Components/ShareConnectionDrawer.qml +++ b/client/ui/qml/Components/ShareConnectionDrawer.qml @@ -36,7 +36,7 @@ DrawerType2 { configFileName = "amnezia_config" } - expandedContent: Item { + expandedStateContent: Item { implicitHeight: root.expandedHeight Connections { @@ -57,8 +57,6 @@ DrawerType2 { anchors.rightMargin: 16 headerText: root.headerText - - KeyNavigation.tab: shareButton } FlickableType { @@ -86,8 +84,6 @@ DrawerType2 { text: qsTr("Share") leftImageSource: "qrc:/images/controls/share-2.svg" - KeyNavigation.tab: copyConfigTextButton - clickedFunc: function() { var fileName = "" if (GC.isMobile()) { @@ -124,8 +120,6 @@ DrawerType2 { Keys.onReturnPressed: { copyConfigTextButton.clicked() } Keys.onEnterPressed: { copyConfigTextButton.clicked() } - - KeyNavigation.tab: copyNativeConfigStringButton.visible ? copyNativeConfigStringButton : showSettingsButton } BasicButtonType { @@ -166,8 +160,6 @@ DrawerType2 { clickedFunc: function() { configContentDrawer.open() } - - KeyNavigation.tab: header } DrawerType2 { @@ -184,24 +176,11 @@ DrawerType2 { } } - expandedContent: Item { + expandedStateContent: 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() { @@ -232,8 +211,6 @@ DrawerType2 { anchors.topMargin: 16 backButtonFunction: function() { configContentDrawer.close() } - - KeyNavigation.tab: focusItem } FlickableType { diff --git a/client/ui/qml/Components/TransportProtoSelector.qml b/client/ui/qml/Components/TransportProtoSelector.qml index e40dd4bb..323892fa 100644 --- a/client/ui/qml/Components/TransportProtoSelector.qml +++ b/client/ui/qml/Components/TransportProtoSelector.qml @@ -39,8 +39,6 @@ Rectangle { implicitWidth: (rootWidth - 32) / 2 text: "UDP" - KeyNavigation.tab: tcpButton - onClicked: { root.currentIndex = 0 } diff --git a/client/ui/qml/Controls2/BackButtonType.qml b/client/ui/qml/Controls2/BackButtonType.qml index 86fc86a2..41aac9e7 100644 --- a/client/ui/qml/Controls2/BackButtonType.qml +++ b/client/ui/qml/Controls2/BackButtonType.qml @@ -4,23 +4,27 @@ import Qt5Compat.GraphicalEffects import Style 1.0 -Item { +FocusScope { id: root property string backButtonImage: "qrc:/images/controls/arrow-left.svg" property var backButtonFunction + // property bool isFocusable: true + + // Keys.onTabPressed: { + // FocusController.nextKeyTabItem() + // } + + // Keys.onBacktabPressed: { + // FocusController.previousKeyTabItem() + // } + implicitWidth: content.implicitWidth implicitHeight: content.implicitHeight visible: backButtonImage !== "" - onActiveFocusChanged: { - if (activeFocus) { - backButton.forceActiveFocus() - } - } - RowLayout { id: content @@ -35,6 +39,8 @@ Item { implicitWidth: 40 implicitHeight: 40 + // focus: true + onClicked: { if (backButtonFunction && typeof backButtonFunction === "function") { backButtonFunction() diff --git a/client/ui/qml/Controls2/BasicButtonType.qml b/client/ui/qml/Controls2/BasicButtonType.qml index 828c32bc..e925035b 100644 --- a/client/ui/qml/Controls2/BasicButtonType.qml +++ b/client/ui/qml/Controls2/BasicButtonType.qml @@ -35,12 +35,25 @@ Button { property alias buttonTextLabel: buttonText + property bool isFocusable: true + + Keys.onTabPressed: { + console.debug("--> Tab is pressed on BasicButtonType: ", objectName) + FocusController.nextKeyTabItem() + } + + Keys.onBacktabPressed: { + console.debug("--> Shift+Tab is pressed on ", objectName) + FocusController.previousKeyTabItem() + } + implicitHeight: 56 hoverEnabled: true focusPolicy: Qt.TabFocus onFocusChanged: { + console.debug("===>> BUTTON: active.focus: ", root.activeFocus, " parentFlickable: ", root.parentFlickable ) if (root.activeFocus) { if (root.parentFlickable) { root.parentFlickable.ensureVisible(this) diff --git a/client/ui/qml/Controls2/CardWithIconsType.qml b/client/ui/qml/Controls2/CardWithIconsType.qml index fea65116..f5d2bea2 100644 --- a/client/ui/qml/Controls2/CardWithIconsType.qml +++ b/client/ui/qml/Controls2/CardWithIconsType.qml @@ -25,10 +25,13 @@ Button { property real textOpacity: 1.0 + property alias focusItem: rightImage + hoverEnabled: true background: Rectangle { id: backgroundRect + anchors.fill: parent radius: 16 @@ -44,8 +47,10 @@ Button { anchors.right: parent.right implicitHeight: content.implicitHeight + RowLayout { id: content + anchors.fill: parent Image { @@ -61,6 +66,7 @@ Button { } ColumnLayout { + ListItemTitleType { text: root.headerText visible: text !== "" @@ -123,6 +129,7 @@ Button { Rectangle { id: rightImageBackground + anchors.fill: parent radius: 12 color: "transparent" @@ -131,10 +138,9 @@ Button { PropertyAnimation { duration: 200 } } } + onClicked: { - if (clickedFunction && typeof clickedFunction === "function") { - clickedFunction() - } + root.clicked() } } } diff --git a/client/ui/qml/Controls2/DrawerType2.qml b/client/ui/qml/Controls2/DrawerType2.qml index c4b584c1..df07c52f 100644 --- a/client/ui/qml/Controls2/DrawerType2.qml +++ b/client/ui/qml/Controls2/DrawerType2.qml @@ -9,17 +9,17 @@ import "TextTypes" Item { id: root - readonly property string drawerExpanded: "expanded" - readonly property string drawerCollapsed: "collapsed" + readonly property string drawerExpandedStateName: "expanded" + readonly property string drawerCollapsedStateName: "collapsed" - readonly property bool isOpened: drawerContent.state === root.drawerExpanded || (drawerContent.state === root.drawerCollapsed && dragArea.drag.active === true) - readonly property bool isClosed: drawerContent.state === root.drawerCollapsed && dragArea.drag.active === false + // readonly property bool isExpanded: isExpandedStateActive() + // readonly property bool isCollapsed: isCollapsedStateActive() - readonly property bool isExpanded: drawerContent.state === root.drawerExpanded - readonly property bool isCollapsed: drawerContent.state === root.drawerCollapsed + readonly property bool isOpened: isExpandedStateActive() || (isCollapsedStateActive && (dragArea.drag.active === true)) + readonly property bool isClosed: isCollapsedStateActive() && (dragArea.drag.active === false) - property Component collapsedContent - property Component expandedContent + property Component collapsedStateContent + property Component expandedStateContent property string defaultColor: AmneziaStyle.color.onyxBlack property string borderColor: AmneziaStyle.color.slateGray @@ -29,29 +29,43 @@ Item { property int depthIndex: 0 - signal entered - signal exited + signal cursorEntered + signal cursorExited signal pressed(bool pressed, bool entered) signal aboutToHide signal aboutToShow - signal close - signal open + signal closeTriggered + signal openTriggered signal closed signal opened + function isExpandedStateActive() { + return isStateActive(drawerExpandedStateName) + } + + function isCollapsedStateActive() { + return isStateActive(drawerCollapsedStateName) + } + + function isStateActive(stateName) { + return drawerContent.state === stateName + } + Connections { target: PageController function onCloseTopDrawer() { + console.debug("===>> onCloseTopDrawer function") + if (depthIndex === PageController.getDrawerDepth()) { - if (isCollapsed) { + if (isCollapsedStateActive()) { return } aboutToHide() - drawerContent.state = root.drawerCollapsed + drawerContent.state = root.drawerCollapsedStateName depthIndex = 0 closed() } @@ -61,30 +75,62 @@ Item { Connections { target: root - function onClose() { - if (isCollapsed) { + function onCloseTriggered() { + console.debug("***>> onClose root connection") + + if (isCollapsedStateActive()) { return } aboutToHide() - drawerContent.state = root.drawerCollapsed - depthIndex = 0 - PageController.setDrawerDepth(PageController.getDrawerDepth() - 1) closed() } - function onOpen() { - if (isExpanded) { + function onClosed() { + console.debug("***>> onClosed root connection") + + drawerContent.state = root.drawerCollapsedStateName + + if (root.isCollapsedStateActive()) { + var initialPageNavigationBarColor = PageController.getInitialPageNavigationBarColor() + if (initialPageNavigationBarColor !== 0xFF1C1D21) { + PageController.updateNavigationBarColor(initialPageNavigationBarColor) + } + } + + depthIndex = 0 + PageController.decrementDrawerDepth() + FocusController.setRootItem(null) + } + + function onOpenTriggered() { + console.debug("===>> onOpen root connection") + + if (root.isExpandedStateActive()) { return } - aboutToShow() + root.aboutToShow() - drawerContent.state = root.drawerExpanded - depthIndex = PageController.getDrawerDepth() + 1 - PageController.setDrawerDepth(depthIndex) - opened() + root.opened() + } + + function onOpened() { + drawerContent.state = root.drawerExpandedStateName + + console.debug("===>> onOpened root connection") + + if (isExpandedStateActive()) { + console.error("new state - extended") + if (PageController.getInitialPageNavigationBarColor() !== 0xFF1C1D21) { + PageController.updateNavigationBarColor(0xFF1C1D21) + } + } + + depthIndex = PageController.incrementDrawerDepth() + FocusController.setRootItem(root) + console.debug("===>> Root item has changed to ", root) } } @@ -102,18 +148,18 @@ Item { MouseArea { id: emptyArea anchors.fill: parent - enabled: root.isExpanded - visible: enabled + onClicked: { - root.close() + console.debug("===>> onClicked emptyArea") + root.closeTriggered() } } MouseArea { id: dragArea + objectName: "dragArea" anchors.fill: drawerContentBackground - cursorShape: root.isCollapsed ? Qt.PointingHandCursor : Qt.ArrowCursor hoverEnabled: true enabled: drawerContent.implicitHeight > 0 @@ -125,35 +171,46 @@ Item { /** If drag area is released at any point other than min or max y, transition to the other state */ onReleased: { - if (root.isCollapsed && drawerContent.y < dragArea.drag.maximumY) { - root.open() + console.debug("===>> onReleased dragArea") + + if (isCollapsedStateActive() && drawerContent.y < dragArea.drag.maximumY) { + root.openTriggered() return } - if (root.isExpanded && drawerContent.y > dragArea.drag.minimumY) { - root.close() + if (isExpandedStateActive() && drawerContent.y > dragArea.drag.minimumY) { + root.closeTriggered() return } } onEntered: { - root.entered() + console.debug("===>> onEntered dragArea") + + root.cursorEntered() } onExited: { - root.exited() + console.debug("===>> onExited dragArea") + + root.cursorExited() } onPressedChanged: { + console.debug("===>> onPressedChanged dragArea") + root.pressed(pressed, entered) } onClicked: { - if (root.isCollapsed) { - root.open() + console.debug("===>> onClicked dragArea") + + if (isCollapsedStateActive()) { + root.openTriggered() } } } Rectangle { id: drawerContentBackground + objectName: "drawerContentBackground" anchors { left: drawerContent.left; right: drawerContent.right; top: drawerContent.top } height: root.height @@ -174,53 +231,80 @@ Item { Item { id: drawerContent + objectName: "drawerContent" Drag.active: dragArea.drag.active anchors.right: root.right anchors.left: root.left - y: root.height - drawerContent.height - state: root.drawerCollapsed - implicitHeight: root.isCollapsed ? collapsedHeight : expandedHeight - - onStateChanged: { - if (root.isCollapsed) { - var initialPageNavigationBarColor = PageController.getInitialPageNavigationBarColor() - if (initialPageNavigationBarColor !== 0xFF1C1D21) { - PageController.updateNavigationBarColor(initialPageNavigationBarColor) - } - return - } - if (root.isExpanded) { - if (PageController.getInitialPageNavigationBarColor() !== 0xFF1C1D21) { - PageController.updateNavigationBarColor(0xFF1C1D21) - } - return - } - } + state: root.drawerCollapsedStateName states: [ State { - name: root.drawerCollapsed + name: root.drawerCollapsedStateName PropertyChanges { target: drawerContent + implicitHeight: collapsedHeight y: root.height - root.collapsedHeight } + PropertyChanges { + target: background + color: AmneziaStyle.color.transparent + } + PropertyChanges { + target: dragArea + cursorShape: Qt.PointingHandCursor + } + PropertyChanges { + target: emptyArea + enabled: false + visible: false + } + PropertyChanges { + target: collapsedLoader + // visible: true + } + PropertyChanges { + target: expandedLoader + visible: false + + } }, State { - name: root.drawerExpanded + name: root.drawerExpandedStateName PropertyChanges { target: drawerContent + implicitHeight: expandedHeight y: dragArea.drag.minimumY - + } + PropertyChanges { + target: background + color: Qt.rgba(14/255, 14/255, 17/255, 0.8) + } + PropertyChanges { + target: dragArea + cursorShape: Qt.ArrowCursor + } + PropertyChanges { + target: emptyArea + enabled: true + visible: true + } + PropertyChanges { + target: collapsedLoader + // visible: false + } + PropertyChanges { + target: expandedLoader + visible: true } } ] transitions: [ Transition { - from: root.drawerCollapsed - to: root.drawerExpanded + from: root.drawerCollapsedStateName + to: root.drawerExpandedStateName PropertyAnimation { target: drawerContent properties: "y" @@ -228,8 +312,8 @@ Item { } }, Transition { - from: root.drawerExpanded - to: root.drawerCollapsed + from: root.drawerExpandedStateName + to: root.drawerCollapsedStateName PropertyAnimation { target: drawerContent properties: "y" @@ -241,7 +325,7 @@ Item { Loader { id: collapsedLoader - sourceComponent: root.collapsedContent + sourceComponent: root.collapsedStateContent anchors.right: parent.right anchors.left: parent.left @@ -250,8 +334,7 @@ Item { Loader { id: expandedLoader - visible: root.isExpanded - sourceComponent: root.expandedContent + sourceComponent: root.expandedStateContent anchors.right: parent.right anchors.left: parent.left diff --git a/client/ui/qml/Controls2/DropDownType.qml b/client/ui/qml/Controls2/DropDownType.qml index 906cfffe..8b683d94 100644 --- a/client/ui/qml/Controls2/DropDownType.qml +++ b/client/ui/qml/Controls2/DropDownType.qml @@ -45,33 +45,51 @@ Item { property Item drawerParent property Component listView - signal open - signal close + signal openTriggered + signal closeTriggered - function popupClosedFunc() { - if (!GC.isMobile()) { - this.forceActiveFocus() - } + readonly property bool isFocusable: true + + Keys.onTabPressed: { + FocusController.nextKeyTabItem() } - property var parentFlickable - onFocusChanged: { - if (root.activeFocus) { - if (root.parentFlickable) { - root.parentFlickable.ensureVisible(root) - } - } - } + // 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() + onOpenTriggered: { + menu.openTriggered() } - onClose: { - menu.close() + onCloseTriggered: { + menu.closeTriggered() + } + + Keys.onEnterPressed: { + if (menu.isClosed) { + menu.openTriggered() + } + } + + Keys.onReturnPressed: { + if (menu.isClosed) { + menu.openTriggered() + } } Rectangle { @@ -153,6 +171,8 @@ Item { } ImageButtonType { + // isFocusable: false + Layout.rightMargin: 16 implicitWidth: 40 @@ -173,7 +193,7 @@ Item { if (rootButtonClickedFunction && typeof rootButtonClickedFunction === "function") { rootButtonClickedFunction() } else { - menu.open() + menu.openTriggered() } } } @@ -186,27 +206,14 @@ Item { anchors.fill: parent expandedHeight: drawerParent.height * drawerHeight - onClosed: { - root.popupClosedFunc() - } + // onClosed: { + // root.popupClosedFunc() + // } - expandedContent: Item { + expandedStateContent: 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 @@ -218,61 +225,43 @@ Item { BackButtonType { id: backButton backButtonImage: root.headerBackButtonImage - backButtonFunction: function() { menu.close() } - KeyNavigation.tab: listViewLoader.item + backButtonFunction: function() { menu.closeTriggered() } + // KeyNavigation.tab: listViewLoader.item } } - FlickableType { - id: flickable - anchors.top: header.bottom - anchors.topMargin: 16 - contentHeight: col.implicitHeight + Column { + id: col + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right - Column { - id: col - anchors.top: parent.top + spacing: 16 + + Header2Type { anchors.left: parent.left anchors.right: parent.right + anchors.leftMargin: 16 + anchors.rightMargin: 16 - spacing: 16 + headerText: root.headerText - Header2Type { - anchors.left: parent.left - anchors.right: parent.right - anchors.leftMargin: 16 - anchors.rightMargin: 16 + width: parent.width + } - headerText: root.headerText + Loader { + id: listViewLoader + sourceComponent: root.listView - width: parent.width - } - - Loader { - id: listViewLoader - sourceComponent: root.listView - - onLoaded: { - listViewLoader.item.parentFlickable = flickable - listViewLoader.item.lastItemTabClicked = function() { - focusItem.forceActiveFocus() - } - } + onLoaded: { + // listViewLoader.item.parentFlickable = flickable + // FocusController.reload() + // listViewLoader.item.lastItemTabClicked = function() { + // focusItem.forceActiveFocus() + // } } } } } } - - Keys.onEnterPressed: { - if (menu.isClosed) { - menu.open() - } - } - - Keys.onReturnPressed: { - if (menu.isClosed) { - menu.open() - } - } } diff --git a/client/ui/qml/Controls2/FlickableType.qml b/client/ui/qml/Controls2/FlickableType.qml index bcd14487..db857ea9 100644 --- a/client/ui/qml/Controls2/FlickableType.qml +++ b/client/ui/qml/Controls2/FlickableType.qml @@ -7,7 +7,7 @@ Flickable { function ensureVisible(item) { if (item.y < fl.contentY) { - fl.contentY = item.y + fl.contentY = item.y - 40 // 40 is a top margin } else if (item.y + item.height > fl.contentY + fl.height) { fl.contentY = item.y + item.height - fl.height + 40 // 40 is a bottom margin } diff --git a/client/ui/qml/Controls2/HorizontalRadioButton.qml b/client/ui/qml/Controls2/HorizontalRadioButton.qml index 1ac1cd30..b87d9b84 100644 --- a/client/ui/qml/Controls2/HorizontalRadioButton.qml +++ b/client/ui/qml/Controls2/HorizontalRadioButton.qml @@ -27,6 +27,18 @@ RadioButton { implicitWidth: content.implicitWidth implicitHeight: content.implicitHeight + property bool isFocusable: true + + Keys.onTabPressed: { + console.debug("--> Tab is pressed on BasicButtonType: ", objectName) + FocusController.nextKeyTabItem() + } + + Keys.onBacktabPressed: { + console.debug("--> Shift+Tab is pressed on ", objectName) + FocusController.previousKeyTabItem() + } + indicator: Rectangle { anchors.fill: parent radius: 16 diff --git a/client/ui/qml/Controls2/ImageButtonType.qml b/client/ui/qml/Controls2/ImageButtonType.qml index fffb6d84..8770f454 100644 --- a/client/ui/qml/Controls2/ImageButtonType.qml +++ b/client/ui/qml/Controls2/ImageButtonType.qml @@ -24,22 +24,23 @@ Button { property int borderFocusedWidth: 1 hoverEnabled: true - focus: true - focusPolicy: Qt.TabFocus icon.source: image icon.color: root.enabled ? imageColor : disableImageColor - property Flickable parentFlickable + property bool isFocusable: true - onFocusChanged: { - if (root.activeFocus) { - if (root.parentFlickable) { - root.parentFlickable.ensureVisible(this) - } - } + Keys.onTabPressed: { + FocusController.nextKeyTabItem() } + Keys.onBacktabPressed: { + FocusController.previousKeyTabItem() + } + + Keys.onEnterPressed: root.clicked() + Keys.onReturnPressed: root.clicked() + Behavior on icon.color { PropertyAnimation { duration: 200 } } diff --git a/client/ui/qml/Controls2/LabelWithButtonType.qml b/client/ui/qml/Controls2/LabelWithButtonType.qml index 41faf108..5e05c30e 100644 --- a/client/ui/qml/Controls2/LabelWithButtonType.qml +++ b/client/ui/qml/Controls2/LabelWithButtonType.qml @@ -41,27 +41,37 @@ Item { property bool descriptionOnTop: false property bool hideDescription: true + property bool isFocusable: !(eyeImage.visible || rightImage.visible) // TODO: this component already has focusable items + + Keys.onTabPressed: { + FocusController.nextKeyTabItem() + } + + Keys.onBacktabPressed: { + FocusController.previousKeyTabItem() + } + 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) - } - } - } + // 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) - } - } - } - } + // Connections { + // target: rightImage + // function onFocusChanged() { + // if (rightImage.activeFocus) { + // if (root.parentFlickable) { + // root.parentFlickable.ensureVisible(root) + // } + // } + // } + // } MouseArea { anchors.fill: parent diff --git a/client/ui/qml/Controls2/ListViewWithRadioButtonType.qml b/client/ui/qml/Controls2/ListViewWithRadioButtonType.qml index f7b777a7..ed5fa6d9 100644 --- a/client/ui/qml/Controls2/ListViewWithRadioButtonType.qml +++ b/client/ui/qml/Controls2/ListViewWithRadioButtonType.qml @@ -33,40 +33,14 @@ ListView { property int currentFocusIndex: 0 - activeFocusOnTab: true - onActiveFocusChanged: { - if (activeFocus) { - this.currentFocusIndex = 0 - this.itemAtIndex(currentFocusIndex).forceActiveFocus() - } - } + property bool isFocusable: true Keys.onTabPressed: { - if (currentFocusIndex < this.count - 1) { - currentFocusIndex += 1 - } else { - currentFocusIndex = 0 - } - this.itemAtIndex(currentFocusIndex).forceActiveFocus() + FocusController.nextKeyTabItem() } - Item { - id: focusItem - Keys.onTabPressed: { - root.forceActiveFocus() - } - } - - onVisibleChanged: { - if (visible) { - focusItem.forceActiveFocus() - } - } - - onCurrentFocusIndexChanged: { - if (parentFlickable) { - parentFlickable.ensureVisible(this.itemAtIndex(currentFocusIndex)) - } + Keys.onBacktabPressed: { + FocusController.previousKeyTabItem() } ButtonGroup { @@ -83,12 +57,6 @@ ListView { implicitWidth: rootWidth implicitHeight: content.implicitHeight - onActiveFocusChanged: { - if (activeFocus) { - radioButton.forceActiveFocus() - } - } - ColumnLayout { id: content diff --git a/client/ui/qml/Controls2/PageType.qml b/client/ui/qml/Controls2/PageType.qml index 0a2a2998..abe7c8d6 100644 --- a/client/ui/qml/Controls2/PageType.qml +++ b/client/ui/qml/Controls2/PageType.qml @@ -9,30 +9,12 @@ Item { property StackView stackView: StackView.view - 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 @@ -49,11 +31,10 @@ Item { // Set a timer to set focus after a short delay Timer { id: timer - interval: 100 // Milliseconds + interval: 1000 // Milliseconds // TODO: return to 500 onTriggered: { - if (defaultActiveFocusItem) { - defaultActiveFocusItem.forceActiveFocus() - } + console.debug("===>> Page creation completed") + FocusController.resetFocus() } repeat: false // Stop the timer after one trigger running: !GC.isMobile() // Start the timer diff --git a/client/ui/qml/Controls2/PopupType.qml b/client/ui/qml/Controls2/PopupType.qml index 7a6a770e..e5019fe5 100644 --- a/client/ui/qml/Controls2/PopupType.qml +++ b/client/ui/qml/Controls2/PopupType.qml @@ -28,11 +28,11 @@ Popup { } onOpened: { - focusItem.forceActiveFocus() + FocusController.setRoot(root) } onClosed: { - PageController.forceStackActiveFocus() + FocusController.setRoot(null) } background: Rectangle { @@ -72,11 +72,6 @@ Popup { } } - Item { - id: focusItem - KeyNavigation.tab: closeButton - } - BasicButtonType { id: closeButton visible: closeButtonVisible @@ -92,7 +87,6 @@ Popup { borderWidth: 0 text: qsTr("Close") - KeyNavigation.tab: focusItem clickedFunc: function() { root.close() diff --git a/client/ui/qml/Controls2/SwitcherType.qml b/client/ui/qml/Controls2/SwitcherType.qml index 43c35778..b96f40e4 100644 --- a/client/ui/qml/Controls2/SwitcherType.qml +++ b/client/ui/qml/Controls2/SwitcherType.qml @@ -35,6 +35,16 @@ Switch { property string hoveredIndicatorBackgroundColor: AmneziaStyle.color.translucentWhite property string defaultIndicatorBackgroundColor: AmneziaStyle.color.transparent + property bool isFocusable: true + + Keys.onTabPressed: { + FocusController.nextKeyTabItem() + } + + Keys.onBacktabPressed: { + FocusController.previousKeyTabItem() + } + hoverEnabled: enabled ? true : false focusPolicy: Qt.TabFocus diff --git a/client/ui/qml/Controls2/TabButtonType.qml b/client/ui/qml/Controls2/TabButtonType.qml index d57ff3a0..d3dc0d1b 100644 --- a/client/ui/qml/Controls2/TabButtonType.qml +++ b/client/ui/qml/Controls2/TabButtonType.qml @@ -17,10 +17,19 @@ TabButton { property bool isSelected: false + property bool isFocusable: true + + Keys.onTabPressed: { + FocusController.nextKeyTabItem() + } + + Keys.onBacktabPressed: { + FocusController.previousKeyTabItem() + } + implicitHeight: 48 hoverEnabled: true - focusPolicy: Qt.TabFocus background: Rectangle { id: background diff --git a/client/ui/qml/Controls2/TabImageButtonType.qml b/client/ui/qml/Controls2/TabImageButtonType.qml index abe544aa..3e0b5aed 100644 --- a/client/ui/qml/Controls2/TabImageButtonType.qml +++ b/client/ui/qml/Controls2/TabImageButtonType.qml @@ -14,13 +14,23 @@ TabButton { property bool isSelected: false + property bool isFocusable: true + + Keys.onTabPressed: { + FocusController.nextKeyTabItem() + } + + Keys.onBacktabPressed: { + FocusController.previousKeyTabItem() + } + property string borderFocusedColor: AmneziaStyle.color.paleGray property int borderFocusedWidth: 1 property var clickedFunc hoverEnabled: true - focusPolicy: Qt.TabFocus + // focusPolicy: Qt.TabFocus icon.source: image icon.color: isSelected ? selectedColor : defaultColor @@ -41,8 +51,9 @@ TabButton { cursorShape: Qt.PointingHandCursor enabled: false } - + Keys.onEnterPressed: { + console.log("$$$$$$$$$ ENTER PRESSED INSIDE TABIMAGEBUTTONTYPE") if (root.clickedFunc && typeof root.clickedFunc === "function") { root.clickedFunc() } diff --git a/client/ui/qml/Controls2/TextAreaWithFooterType.qml b/client/ui/qml/Controls2/TextAreaWithFooterType.qml index 102929e2..cf7b9146 100644 --- a/client/ui/qml/Controls2/TextAreaWithFooterType.qml +++ b/client/ui/qml/Controls2/TextAreaWithFooterType.qml @@ -78,9 +78,6 @@ Rectangle { placeholderText: root.placeholderText text: root.text - - KeyNavigation.tab: firstButton - onCursorVisibleChanged: { if (textArea.cursorVisible) { fl.interactive = true diff --git a/client/ui/qml/Controls2/TextFieldWithHeaderType.qml b/client/ui/qml/Controls2/TextFieldWithHeaderType.qml index 365faa94..875eed9f 100644 --- a/client/ui/qml/Controls2/TextFieldWithHeaderType.qml +++ b/client/ui/qml/Controls2/TextFieldWithHeaderType.qml @@ -84,7 +84,16 @@ Item { TextField { id: textField - activeFocusOnTab: false + // activeFocusOnTab: false + property bool isFocusable: true + + Keys.onTabPressed: { + FocusController.nextKeyTabItem() + } + + Keys.onBacktabPressed: { + FocusController.previousKeyTabItem() + } enabled: root.textFieldEditable color: root.enabled ? root.textFieldTextColor : root.textFieldTextDisabledColor @@ -209,9 +218,9 @@ Item { clickedFunc() } - if (KeyNavigation.tab) { - KeyNavigation.tab.forceActiveFocus(); - } + // if (KeyNavigation.tab) { + // KeyNavigation.tab.forceActiveFocus(); + // } } Keys.onReturnPressed: { @@ -219,8 +228,8 @@ Item { clickedFunc() } - if (KeyNavigation.tab) { - KeyNavigation.tab.forceActiveFocus(); - } + // if (KeyNavigation.tab) { + // KeyNavigation.tab.forceActiveFocus(); + // } } } diff --git a/client/ui/qml/Controls2/VerticalRadioButton.qml b/client/ui/qml/Controls2/VerticalRadioButton.qml index 1a781f20..4320103b 100644 --- a/client/ui/qml/Controls2/VerticalRadioButton.qml +++ b/client/ui/qml/Controls2/VerticalRadioButton.qml @@ -28,8 +28,14 @@ RadioButton { property string imageSource property bool showImage + property bool isFocusable: true + + Keys.onTabPressed: { + FocusController.nextKeyTabItem() + } + hoverEnabled: true - focusPolicy: Qt.TabFocus + // focusPolicy: Qt.TabFocus indicator: Rectangle { id: background diff --git a/client/ui/qml/Pages2/PageDevMenu.qml b/client/ui/qml/Pages2/PageDevMenu.qml index 5da40eff..7f3ff1f0 100644 --- a/client/ui/qml/Pages2/PageDevMenu.qml +++ b/client/ui/qml/Pages2/PageDevMenu.qml @@ -16,13 +16,6 @@ import "../Components" PageType { id: root - defaultActiveFocusItem: focusItem - - Item { - id: focusItem - KeyNavigation.tab: backButton - } - ColumnLayout { id: backButtonLayout @@ -34,7 +27,6 @@ PageType { BackButtonType { id: backButton - // KeyNavigation.tab: removeButton } } @@ -61,7 +53,6 @@ PageType { headerText: "Dev menu" } - TextFieldWithHeaderType { id: passwordTextField @@ -86,8 +77,6 @@ PageType { SettingsController.gatewayEndpoint = textFieldText } } - - // KeyNavigation.tab: saveButton } SwitcherType { diff --git a/client/ui/qml/Pages2/PageHome.qml b/client/ui/qml/Pages2/PageHome.qml index 8422a10f..aa2f0501 100644 --- a/client/ui/qml/Pages2/PageHome.qml +++ b/client/ui/qml/Pages2/PageHome.qml @@ -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 @@ -115,53 +114,47 @@ PageType { Keys.onEnterPressed: splitTunnelingButton.clicked() Keys.onReturnPressed: splitTunnelingButton.clicked() - KeyNavigation.tab: drawer - onClicked: { - homeSplitTunnelingDrawer.open() + homeSplitTunnelingDrawer.openTriggered() } HomeSplitTunnelingDrawer { id: homeSplitTunnelingDrawer + objectName: "homeSplitTunnelingDrawer" + parent: root onClosed: { - if (!GC.isMobile()) { - focusItem.forceActiveFocus() - } + console.log(objectName, " was closed...") + FocusController.setRootItem(null) } } } } } - 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 { + objectName: "drawerConnections" + 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 @@ -180,6 +173,8 @@ PageType { } RowLayout { + objectName: "rowLayout" + Layout.topMargin: 14 Layout.leftMargin: 24 Layout.rightMargin: 24 @@ -188,9 +183,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 { @@ -198,8 +195,8 @@ PageType { } } - function onExited() { - if (drawer.isCollapsed) { + function onCursorExited() { + if (drawer.isCollapsedStateActive) { collapsedButtonChevron.backgroundColor = collapsedButtonChevron.defaultColor collapsedButtonHeader.opacity = 1 } else { @@ -208,7 +205,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 { @@ -219,6 +216,8 @@ PageType { Header1TextType { id: collapsedButtonHeader + objectName: "collapsedButtonHeader" + Layout.maximumWidth: drawer.width - 48 - 18 - 12 maximumLineCount: 2 @@ -227,8 +226,6 @@ PageType { text: ServersModel.defaultServerName horizontalAlignment: Qt.AlignHCenter - KeyNavigation.tab: tabBar - Behavior on opacity { PropertyAnimation { duration: 200 } } @@ -236,10 +233,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" @@ -254,18 +252,19 @@ PageType { Keys.onEnterPressed: collapsedButtonChevron.clicked() Keys.onReturnPressed: collapsedButtonChevron.clicked() - Keys.onTabPressed: lastItemTabClicked() - onClicked: { - if (drawer.isCollapsed) { - drawer.open() + console.debug("onClicked collapsedButtonChevron") + if (drawer.isCollapsedStateActive()) { + drawer.openTriggered() + FocusController.setRootItem(drawer) } } } } RowLayout { + objectName: "rowLayoutLabel" Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter Layout.topMargin: 8 Layout.bottomMargin: drawer.isCollapsed ? 44 : ServersModel.isDefaultServerFromApi ? 61 : 16 @@ -316,6 +315,7 @@ PageType { ColumnLayout { id: serversMenuHeader + objectName: "serversMenuHeader" anchors.top: collapsed.bottom anchors.right: parent.right @@ -327,13 +327,9 @@ PageType { visible: !ServersModel.isDefaultServerFromApi - Item { - id: focusItem1 - KeyNavigation.tab: containersDropDown - } - DropDownType { id: containersDropDown + objectName: "containersDropDown" rootButtonImageColor: AmneziaStyle.color.midnightBlack rootButtonBackgroundColor: AmneziaStyle.color.paleGray @@ -350,22 +346,23 @@ PageType { 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 + + // isFocusable: false // TODO: this is a workaround. Need to remove it Connections { + objectName: "rowLayoutConnections" + target: ServersModel function onDefaultServerIndexChanged() { @@ -407,169 +404,12 @@ PageType { ButtonGroup { id: serversRadioButtonGroup + objectName: "serversRadioButtonGroup" } - ListView { + ServersListView { id: 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() - - 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 - } - } - } + objectName: "serversMenuContent" } } } diff --git a/client/ui/qml/Pages2/PageProtocolAwgSettings.qml b/client/ui/qml/Pages2/PageProtocolAwgSettings.qml index 27ea66f9..d03b3580 100644 --- a/client/ui/qml/Pages2/PageProtocolAwgSettings.qml +++ b/client/ui/qml/Pages2/PageProtocolAwgSettings.qml @@ -17,18 +17,6 @@ import "../Components" PageType { id: root - defaultActiveFocusItem: listview.currentItem.portTextField.textField - - Item { - id: focusItem - onFocusChanged: { - if (activeFocus) { - fl.ensureVisible(focusItem) - } - } - KeyNavigation.tab: backButton - } - ColumnLayout { id: backButtonLayout @@ -40,7 +28,6 @@ PageType { BackButtonType { id: backButton - KeyNavigation.tab: listview.currentItem.portTextField.textField } } @@ -114,8 +101,26 @@ PageType { } checkEmptyText: true + } - KeyNavigation.tab: junkPacketCountTextField.textField + TextFieldWithHeaderType { + id: mtuTextField + Layout.fillWidth: true + Layout.topMargin: 16 + + headerText: qsTr("MTU") + textFieldText: mtu + textField.validator: IntValidator { bottom: 576; top: 65535 } + + textField.onEditingFinished: { + if (textFieldText === "") { + textFieldText = "0" + } + if (textFieldText !== mtu) { + mtu = textFieldText + } + } + checkEmptyText: true } TextFieldWithHeaderType { @@ -139,8 +144,6 @@ PageType { } checkEmptyText: true - - KeyNavigation.tab: junkPacketMinSizeTextField.textField } TextFieldWithHeaderType { @@ -160,8 +163,6 @@ PageType { } checkEmptyText: true - - KeyNavigation.tab: junkPacketMaxSizeTextField.textField } TextFieldWithHeaderType { @@ -181,8 +182,6 @@ PageType { } checkEmptyText: true - - KeyNavigation.tab: initPacketJunkSizeTextField.textField } TextFieldWithHeaderType { @@ -202,8 +201,6 @@ PageType { } checkEmptyText: true - - KeyNavigation.tab: responsePacketJunkSizeTextField.textField } TextFieldWithHeaderType { @@ -223,8 +220,6 @@ PageType { } checkEmptyText: true - - KeyNavigation.tab: initPacketMagicHeaderTextField.textField } TextFieldWithHeaderType { @@ -244,8 +239,6 @@ PageType { } checkEmptyText: true - - KeyNavigation.tab: responsePacketMagicHeaderTextField.textField } TextFieldWithHeaderType { @@ -265,8 +258,6 @@ PageType { } checkEmptyText: true - - KeyNavigation.tab: transportPacketMagicHeaderTextField.textField } TextFieldWithHeaderType { @@ -286,8 +277,6 @@ PageType { } checkEmptyText: true - - KeyNavigation.tab: underloadPacketMagicHeaderTextField.textField } TextFieldWithHeaderType { @@ -307,8 +296,6 @@ PageType { } checkEmptyText: true - - KeyNavigation.tab: saveRestartButton } BasicButtonType { @@ -332,8 +319,6 @@ PageType { text: qsTr("Save") - Keys.onTabPressed: lastItemTabClicked(focusItem) - clickedFunc: function() { forceActiveFocus() diff --git a/client/ui/qml/Pages2/PageProtocolCloakSettings.qml b/client/ui/qml/Pages2/PageProtocolCloakSettings.qml index 5089a764..29df03ef 100644 --- a/client/ui/qml/Pages2/PageProtocolCloakSettings.qml +++ b/client/ui/qml/Pages2/PageProtocolCloakSettings.qml @@ -16,13 +16,6 @@ import "../Components" PageType { id: root - defaultActiveFocusItem: listview.currentItem.trafficFromField.textField - - Item { - id: focusItem - KeyNavigation.tab: backButton - } - ColumnLayout { id: backButtonLayout @@ -34,7 +27,6 @@ PageType { BackButtonType { id: backButton - KeyNavigation.tab: listview.currentItem.trafficFromField.textField } } @@ -110,8 +102,6 @@ PageType { } } } - - KeyNavigation.tab: portTextField.textField } TextFieldWithHeaderType { @@ -130,8 +120,6 @@ PageType { port = textFieldText } } - - KeyNavigation.tab: cipherDropDown } DropDownType { @@ -143,7 +131,6 @@ PageType { headerText: qsTr("Cipher") drawerParent: root - KeyNavigation.tab: saveRestartButton listView: ListViewWithRadioButtonType { id: cipherListView diff --git a/client/ui/qml/Pages2/PageProtocolOpenVpnSettings.qml b/client/ui/qml/Pages2/PageProtocolOpenVpnSettings.qml index 30540a93..75fdd18d 100644 --- a/client/ui/qml/Pages2/PageProtocolOpenVpnSettings.qml +++ b/client/ui/qml/Pages2/PageProtocolOpenVpnSettings.qml @@ -17,18 +17,6 @@ import "../Components" PageType { id: root - defaultActiveFocusItem: listview.currentItem.vpnAddressSubnetTextField.textField - - Item { - id: focusItem - KeyNavigation.tab: backButton - onActiveFocusChanged: { - if (activeFocus) { - fl.ensureVisible(focusItem) - } - } - } - ColumnLayout { id: backButtonLayout @@ -40,7 +28,6 @@ PageType { BackButtonType { id: backButton - KeyNavigation.tab: listview.currentItem.vpnAddressSubnetTextField.textField } } @@ -104,7 +91,6 @@ PageType { textFieldText: subnetAddress parentFlickable: fl - KeyNavigation.tab: transportProtoSelector textField.onEditingFinished: { if (textFieldText !== subnetAddress) { @@ -132,8 +118,6 @@ PageType { return transportProto === "tcp" ? 1 : 0 } - KeyNavigation.tab: portTextField.enabled ? portTextField.textField : autoNegotiateEncryprionSwitcher - onCurrentIndexChanged: { if (transportProto === "tcp" && currentIndex === 0) { transportProto = "udp" @@ -162,8 +146,6 @@ PageType { port = textFieldText } } - - KeyNavigation.tab: autoNegotiateEncryprionSwitcher } SwitcherType { @@ -181,10 +163,6 @@ PageType { autoNegotiateEncryprion = checked } } - - KeyNavigation.tab: hashDropDown.enabled ? - hashDropDown : - tlsAuthCheckBox } DropDownType { @@ -199,9 +177,6 @@ PageType { drawerParent: root parentFlickable: fl - KeyNavigation.tab: cipherDropDown.enabled ? - cipherDropDown : - tlsAuthCheckBox listView: ListViewWithRadioButtonType { id: hashListView @@ -252,8 +227,6 @@ PageType { drawerParent: root parentFlickable: fl - KeyNavigation.tab: tlsAuthCheckBox - listView: ListViewWithRadioButtonType { id: cipherListView @@ -320,8 +293,6 @@ PageType { text: qsTr("TLS auth") checked: tlsAuth - KeyNavigation.tab: blockDnsCheckBox - onCheckedChanged: { if (checked !== tlsAuth) { console.log("tlsAuth changed to: " + checked) @@ -339,8 +310,6 @@ PageType { text: qsTr("Block DNS requests outside of VPN") checked: blockDns - KeyNavigation.tab: additionalClientCommandsSwitcher - onCheckedChanged: { if (checked !== blockDns) { blockDns = checked @@ -355,9 +324,6 @@ PageType { Layout.fillWidth: true Layout.topMargin: 32 parentFlickable: fl - KeyNavigation.tab: additionalClientCommandsTextArea.visible ? - additionalClientCommandsTextArea.textArea : - additionalServerCommandsSwitcher checked: additionalClientCommands !== "" @@ -376,7 +342,7 @@ PageType { Layout.topMargin: 16 visible: additionalClientCommandsSwitcher.checked - KeyNavigation.tab: additionalServerCommandsSwitcher + parentFlickable: fl textAreaText: additionalClientCommands @@ -394,9 +360,6 @@ PageType { Layout.fillWidth: true Layout.topMargin: 16 parentFlickable: fl - KeyNavigation.tab: additionalServerCommandsTextArea.visible ? - additionalServerCommandsTextArea.textArea : - saveRestartButton checked: additionalServerCommands !== "" @@ -419,7 +382,6 @@ PageType { textAreaText: additionalServerCommands placeholderText: qsTr("Commands:") parentFlickable: fl - KeyNavigation.tab: saveRestartButton textArea.onEditingFinished: { if (additionalServerCommands !== textAreaText) { additionalServerCommands = textAreaText diff --git a/client/ui/qml/Pages2/PageProtocolRaw.qml b/client/ui/qml/Pages2/PageProtocolRaw.qml index 24853afd..3cb02435 100644 --- a/client/ui/qml/Pages2/PageProtocolRaw.qml +++ b/client/ui/qml/Pages2/PageProtocolRaw.qml @@ -19,13 +19,6 @@ import "../Components" PageType { id: root - defaultActiveFocusItem: focusItem - - Item { - id: focusItem - KeyNavigation.tab: backButton - } - ColumnLayout { id: header @@ -37,7 +30,6 @@ PageType { BackButtonType { id: backButton - KeyNavigation.tab: listView } HeaderType { @@ -104,8 +96,6 @@ PageType { configContentDrawer.open() } - KeyNavigation.tab: removeButton - MouseArea { anchors.fill: button cursorShape: Qt.PointingHandCursor @@ -129,7 +119,7 @@ PageType { parent: root anchors.fill: parent - expandedContent: Item { + expandedStateContent: Item { implicitHeight: configContentDrawer.expandedHeight Connections { @@ -140,11 +130,6 @@ PageType { } } - Item { - id: focusItem1 - KeyNavigation.tab: backButton1 - } - BackButtonType { id: backButton1 @@ -156,8 +141,6 @@ PageType { backButtonFunction: function() { configContentDrawer.close() } - - KeyNavigation.tab: focusItem1 } FlickableType { diff --git a/client/ui/qml/Pages2/PageProtocolShadowSocksSettings.qml b/client/ui/qml/Pages2/PageProtocolShadowSocksSettings.qml index 4d3b2c4e..dae96b5a 100644 --- a/client/ui/qml/Pages2/PageProtocolShadowSocksSettings.qml +++ b/client/ui/qml/Pages2/PageProtocolShadowSocksSettings.qml @@ -16,15 +16,6 @@ import "../Components" PageType { id: root - defaultActiveFocusItem: listview.currentItem.focusItemId.enabled ? - listview.currentItem.focusItemId.textField : - focusItem - - Item { - id: focusItem - KeyNavigation.tab: backButton - } - ColumnLayout { id: backButtonLayout @@ -36,9 +27,6 @@ PageType { BackButtonType { id: backButton - KeyNavigation.tab: listview.currentItem.focusItemId.enabled ? - listview.currentItem.focusItemId.textField : - focusItem } } @@ -114,8 +102,6 @@ PageType { port = textFieldText } } - - KeyNavigation.tab: cipherDropDown } DropDownType { @@ -129,9 +115,9 @@ PageType { headerText: qsTr("Cipher") drawerParent: root - KeyNavigation.tab: saveRestartButton listView: ListViewWithRadioButtonType { + id: cipherListView rootWidth: root.width diff --git a/client/ui/qml/Pages2/PageProtocolWireGuardSettings.qml b/client/ui/qml/Pages2/PageProtocolWireGuardSettings.qml index b5d08132..ab50f444 100644 --- a/client/ui/qml/Pages2/PageProtocolWireGuardSettings.qml +++ b/client/ui/qml/Pages2/PageProtocolWireGuardSettings.qml @@ -16,13 +16,6 @@ import "../Components" PageType { id: root - defaultActiveFocusItem: listview - - Item { - id: focusItem - KeyNavigation.tab: backButton - } - ColumnLayout { id: backButtonLayout @@ -34,7 +27,6 @@ PageType { BackButtonType { id: backButton - KeyNavigation.tab: listview } } @@ -64,12 +56,12 @@ PageType { model: WireGuardConfigModel - activeFocusOnTab: true - onActiveFocusChanged: { - if (activeFocus) { - listview.itemAtIndex(0)?.focusItemId.forceActiveFocus() - } - } + // activeFocusOnTab: true + // onActiveFocusChanged: { + // if (activeFocus) { + // listview.itemAtIndex(0)?.focusItemId.forceActiveFocus() + // } + // } delegate: Item { id: delegateItem @@ -109,8 +101,6 @@ PageType { textField.maximumLength: 5 textField.validator: IntValidator { bottom: 1; top: 65535 } - KeyNavigation.tab: saveButton - textField.onEditingFinished: { if (textFieldText !== port) { port = textFieldText @@ -120,6 +110,26 @@ PageType { checkEmptyText: true } + TextFieldWithHeaderType { + id: mtuTextField + Layout.fillWidth: true + Layout.topMargin: 16 + + headerText: qsTr("MTU") + textFieldText: mtu + textField.validator: IntValidator { bottom: 576; top: 65535 } + + textField.onEditingFinished: { + if (textFieldText === "") { + textFieldText = "0" + } + if (textFieldText !== mtu) { + mtu = textFieldText + } + } + checkEmptyText: true + } + BasicButtonType { id: saveButton Layout.fillWidth: true diff --git a/client/ui/qml/Pages2/PageProtocolXraySettings.qml b/client/ui/qml/Pages2/PageProtocolXraySettings.qml index 20ee1da6..d74aabad 100644 --- a/client/ui/qml/Pages2/PageProtocolXraySettings.qml +++ b/client/ui/qml/Pages2/PageProtocolXraySettings.qml @@ -17,13 +17,6 @@ import "../Components" PageType { id: root - defaultActiveFocusItem: listview - - Item { - id: focusItem - KeyNavigation.tab: backButton - } - ColumnLayout { id: backButtonLayout @@ -35,7 +28,6 @@ PageType { BackButtonType { id: backButton - KeyNavigation.tab: listview } } @@ -65,12 +57,12 @@ PageType { model: XrayConfigModel - activeFocusOnTab: true - onActiveFocusChanged: { - if (activeFocus) { - listview.itemAtIndex(0)?.focusItemId.forceActiveFocus() - } - } + // activeFocusOnTab: true + // onActiveFocusChanged: { + // if (activeFocus) { + // listview.itemAtIndex(0)?.focusItemId.forceActiveFocus() + // } + // } delegate: Item { property alias focusItemId: textFieldWithHeaderType.textField @@ -103,8 +95,6 @@ PageType { headerText: qsTr("Disguised as traffic from") textFieldText: site - KeyNavigation.tab: basicButton - textField.onEditingFinished: { if (textFieldText !== site) { var tmpText = textFieldText diff --git a/client/ui/qml/Pages2/PageServiceDnsSettings.qml b/client/ui/qml/Pages2/PageServiceDnsSettings.qml index bb3cbf96..c50f9a9b 100644 --- a/client/ui/qml/Pages2/PageServiceDnsSettings.qml +++ b/client/ui/qml/Pages2/PageServiceDnsSettings.qml @@ -16,13 +16,6 @@ import "../Components" PageType { id: root - defaultActiveFocusItem: focusItem - - Item { - id: focusItem - KeyNavigation.tab: backButton - } - ColumnLayout { id: backButtonLayout @@ -34,7 +27,6 @@ PageType { BackButtonType { id: backButton - KeyNavigation.tab: removeButton } } diff --git a/client/ui/qml/Pages2/PageServiceSftpSettings.qml b/client/ui/qml/Pages2/PageServiceSftpSettings.qml index 9bdbf2db..8513f111 100644 --- a/client/ui/qml/Pages2/PageServiceSftpSettings.qml +++ b/client/ui/qml/Pages2/PageServiceSftpSettings.qml @@ -16,8 +16,6 @@ import "../Components" PageType { id: root - defaultActiveFocusItem: focusItem - Connections { target: InstallController @@ -26,11 +24,6 @@ PageType { } } - Item { - id: focusItem - KeyNavigation.tab: backButton - } - ColumnLayout { id: backButtonLayout @@ -42,7 +35,6 @@ PageType { BackButtonType { id: backButton - KeyNavigation.tab: listview } } @@ -107,7 +99,6 @@ PageType { Layout.topMargin: 32 parentFlickable: fl - KeyNavigation.tab: portLabel.rightButton text: qsTr("Host") descriptionText: ServersModel.getProcessedServerData("hostName") @@ -136,7 +127,6 @@ PageType { descriptionOnTop: true parentFlickable: fl - KeyNavigation.tab: usernameLabel.rightButton rightImageSource: "qrc:/images/controls/copy.svg" rightImageColor: AmneziaStyle.color.paleGray @@ -160,7 +150,6 @@ PageType { descriptionOnTop: true parentFlickable: fl - KeyNavigation.tab: passwordLabel.eyeButton rightImageSource: "qrc:/images/controls/copy.svg" rightImageColor: AmneziaStyle.color.paleGray @@ -184,7 +173,7 @@ PageType { descriptionOnTop: true parentFlickable: fl - eyeButton.KeyNavigation.tab: passwordLabel.rightButton + rightButton.Keys.onTabPressed: { if (mountButton.visible) { mountButton.forceActiveFocus() @@ -225,7 +214,6 @@ PageType { borderWidth: 1 parentFlickable: fl - KeyNavigation.tab: detailedInstructionsButton text: qsTr("Mount folder on device") diff --git a/client/ui/qml/Pages2/PageServiceSocksProxySettings.qml b/client/ui/qml/Pages2/PageServiceSocksProxySettings.qml index 9d21963d..cadfee09 100644 --- a/client/ui/qml/Pages2/PageServiceSocksProxySettings.qml +++ b/client/ui/qml/Pages2/PageServiceSocksProxySettings.qml @@ -17,8 +17,6 @@ import "../Components" PageType { id: root - defaultActiveFocusItem: listview - Connections { target: InstallController @@ -27,11 +25,6 @@ PageType { } } - Item { - id: focusItem - KeyNavigation.tab: backButton - } - ColumnLayout { id: backButtonLayout @@ -43,7 +36,6 @@ PageType { BackButtonType { id: backButton - KeyNavigation.tab: listview } } @@ -99,7 +91,6 @@ PageType { Layout.topMargin: 32 parentFlickable: fl - KeyNavigation.tab: portLabel.rightButton text: qsTr("Host") descriptionText: ServersModel.getProcessedServerData("hostName") @@ -128,7 +119,6 @@ PageType { descriptionOnTop: true parentFlickable: fl - KeyNavigation.tab: usernameLabel.rightButton rightImageSource: "qrc:/images/controls/copy.svg" rightImageColor: AmneziaStyle.color.paleGray @@ -152,7 +142,6 @@ PageType { descriptionOnTop: true parentFlickable: fl - KeyNavigation.tab: passwordLabel.eyeButton rightImageSource: "qrc:/images/controls/copy.svg" rightImageColor: AmneziaStyle.color.paleGray @@ -176,8 +165,6 @@ PageType { descriptionOnTop: true parentFlickable: fl - eyeButton.KeyNavigation.tab: passwordLabel.rightButton - rightButton.KeyNavigation.tab: changeSettingsButton rightImageSource: "qrc:/images/controls/copy.svg" rightImageColor: AmneziaStyle.color.paleGray @@ -206,7 +193,7 @@ PageType { } } - expandedContent: ColumnLayout { + expandedStateContent: ColumnLayout { property string tempPort: port property string tempUsername: username property string tempPassword: password @@ -239,11 +226,6 @@ PageType { } } - Item { - id: drawerFocusItem - KeyNavigation.tab: portTextField.textField - } - HeaderType { Layout.fillWidth: true @@ -268,8 +250,6 @@ PageType { port = textFieldText } } - - KeyNavigation.tab: usernameTextField.textField } TextFieldWithHeaderType { @@ -290,8 +270,6 @@ PageType { username = textFieldText } } - - KeyNavigation.tab: passwordTextField.textField } TextFieldWithHeaderType { @@ -322,8 +300,6 @@ PageType { password = textFieldText } } - - KeyNavigation.tab: saveButton } BasicButtonType { diff --git a/client/ui/qml/Pages2/PageServiceTorWebsiteSettings.qml b/client/ui/qml/Pages2/PageServiceTorWebsiteSettings.qml index 946a77bb..a1e41f29 100644 --- a/client/ui/qml/Pages2/PageServiceTorWebsiteSettings.qml +++ b/client/ui/qml/Pages2/PageServiceTorWebsiteSettings.qml @@ -17,8 +17,6 @@ import "../Components" PageType { id: root - defaultActiveFocusItem: focusItem - Connections { target: InstallController @@ -27,11 +25,6 @@ PageType { } } - Item { - id: focusItem - KeyNavigation.tab: backButton - } - ColumnLayout { id: backButtonLayout @@ -43,7 +36,6 @@ PageType { BackButtonType { id: backButton - KeyNavigation.tab: websiteName.rightButton } } diff --git a/client/ui/qml/Pages2/PageSettings.qml b/client/ui/qml/Pages2/PageSettings.qml index bb5ca766..6e291ff0 100644 --- a/client/ui/qml/Pages2/PageSettings.qml +++ b/client/ui/qml/Pages2/PageSettings.qml @@ -14,9 +14,7 @@ import "../Config" PageType { id: root - defaultActiveFocusItem: header - - FlickableType { + FlickableType { // TODO: refactor either replace with ListView or Repeater id: fl anchors.top: parent.top anchors.bottom: parent.bottom @@ -39,8 +37,6 @@ PageType { Layout.leftMargin: 16 headerText: qsTr("Settings") - - KeyNavigation.tab: account.rightButton } LabelWithButtonType { @@ -55,8 +51,6 @@ PageType { clickedFunction: function() { PageController.goToPage(PageEnum.PageSettingsServersList) } - - KeyNavigation.tab: connection.rightButton } DividerType {} @@ -72,8 +66,6 @@ PageType { clickedFunction: function() { PageController.goToPage(PageEnum.PageSettingsConnection) } - - KeyNavigation.tab: application.rightButton } DividerType {} @@ -89,8 +81,6 @@ PageType { clickedFunction: function() { PageController.goToPage(PageEnum.PageSettingsApplication) } - - KeyNavigation.tab: backup.rightButton } DividerType {} @@ -106,8 +96,6 @@ PageType { clickedFunction: function() { PageController.goToPage(PageEnum.PageSettingsBackup) } - - KeyNavigation.tab: about.rightButton } DividerType {} @@ -123,8 +111,6 @@ PageType { clickedFunction: function() { PageController.goToPage(PageEnum.PageSettingsAbout) } - KeyNavigation.tab: close - } DividerType {} @@ -138,8 +124,6 @@ PageType { rightImageSource: "qrc:/images/controls/chevron-right.svg" leftImageSource: "qrc:/images/controls/bug.svg" - // Keys.onTabPressed: lastItemTabClicked(header) - clickedFunction: function() { PageController.goToPage(PageEnum.PageDevMenu) } @@ -156,10 +140,9 @@ PageType { Layout.preferredHeight: about.height text: qsTr("Close application") + rightImageSource: "qrc:/images/controls/chevron-right.svg" leftImageSource: "qrc:/images/controls/x-circle.svg" - isLeftImageHoverEnabled: false - - Keys.onTabPressed: lastItemTabClicked(header) + // isLeftImageHoverEnabled: false clickedFunction: function() { PageController.closeApplication() diff --git a/client/ui/qml/Pages2/PageSettingsAbout.qml b/client/ui/qml/Pages2/PageSettingsAbout.qml index cde9ee20..5407e75e 100644 --- a/client/ui/qml/Pages2/PageSettingsAbout.qml +++ b/client/ui/qml/Pages2/PageSettingsAbout.qml @@ -14,18 +14,16 @@ import "../Components" PageType { id: root - defaultActiveFocusItem: focusItem + // Item { + // id: focusItem + // KeyNavigation.tab: backButton - Item { - id: focusItem - KeyNavigation.tab: backButton - - onFocusChanged: { - if (focusItem.activeFocus) { - fl.contentY = 0 - } - } - } + // onFocusChanged: { + // if (focusItem.activeFocus) { + // fl.contentY = 0 + // } + // } + // } BackButtonType { id: backButton @@ -34,8 +32,6 @@ PageType { anchors.left: parent.left anchors.right: parent.right anchors.topMargin: 20 - - KeyNavigation.tab: telegramButton } FlickableType { @@ -106,7 +102,6 @@ PageType { descriptionText: qsTr("To discuss features") leftImageSource: "qrc:/images/controls/telegram.svg" - KeyNavigation.tab: mailButton parentFlickable: fl clickedFunction: function() { @@ -124,7 +119,6 @@ PageType { descriptionText: qsTr("For reviews and bug reports") leftImageSource: "qrc:/images/controls/mail.svg" - KeyNavigation.tab: githubButton parentFlickable: fl clickedFunction: function() { @@ -143,7 +137,6 @@ PageType { text: qsTr("GitHub") leftImageSource: "qrc:/images/controls/github.svg" - KeyNavigation.tab: websiteButton parentFlickable: fl clickedFunction: function() { @@ -161,7 +154,6 @@ PageType { text: qsTr("Website") leftImageSource: "qrc:/images/controls/amnezia.svg" - KeyNavigation.tab: checkUpdatesButton parentFlickable: fl clickedFunction: function() { @@ -209,7 +201,6 @@ PageType { text: qsTr("Check for updates") - KeyNavigation.tab: privacyPolicyButton parentFlickable: fl clickedFunc: function() { diff --git a/client/ui/qml/Pages2/PageSettingsApiServerInfo.qml b/client/ui/qml/Pages2/PageSettingsApiServerInfo.qml index 2d6c1d9b..73fdc551 100644 --- a/client/ui/qml/Pages2/PageSettingsApiServerInfo.qml +++ b/client/ui/qml/Pages2/PageSettingsApiServerInfo.qml @@ -15,8 +15,6 @@ import "../Components" PageType { id: root - defaultActiveFocusItem: focusItem - FlickableType { id: fl anchors.top: parent.top @@ -32,11 +30,6 @@ PageType { spacing: 0 - Item { - id: focusItem -// KeyNavigation.tab: backButton - } - LabelWithImageType { Layout.fillWidth: true Layout.margins: 16 diff --git a/client/ui/qml/Pages2/PageSettingsAppSplitTunneling.qml b/client/ui/qml/Pages2/PageSettingsAppSplitTunneling.qml index 2e8bda2f..4751aa71 100644 --- a/client/ui/qml/Pages2/PageSettingsAppSplitTunneling.qml +++ b/client/ui/qml/Pages2/PageSettingsAppSplitTunneling.qml @@ -21,8 +21,6 @@ import "../Components" PageType { id: root - defaultActiveFocusItem: focusItem - property bool pageEnabled Component.onCompleted: { @@ -66,11 +64,6 @@ PageType { } } - Item { - id: focusItem - KeyNavigation.tab: backButton - } - ColumnLayout { id: header @@ -82,7 +75,6 @@ PageType { BackButtonType { id: backButton - KeyNavigation.tab: switcher } RowLayout { @@ -103,10 +95,6 @@ PageType { enabled: root.pageEnabled - KeyNavigation.tab: selector.enabled ? - selector : - searchField.textField - checked: AppSplitTunnelingModel.isTunnelingEnabled onToggled: { AppSplitTunnelingModel.toggleSplitTunneling(checked) @@ -130,8 +118,6 @@ PageType { enabled: Qt.platform.os === "android" && root.pageEnabled - KeyNavigation.tab: searchField.textField - listView: ListViewWithRadioButtonType { rootWidth: root.width diff --git a/client/ui/qml/Pages2/PageSettingsApplication.qml b/client/ui/qml/Pages2/PageSettingsApplication.qml index 0f85ac30..dfccabcf 100644 --- a/client/ui/qml/Pages2/PageSettingsApplication.qml +++ b/client/ui/qml/Pages2/PageSettingsApplication.qml @@ -14,18 +14,15 @@ import "../Components" PageType { id: root - defaultActiveFocusItem: focusItem + // Item { + // id: focusItem - Item { - id: focusItem - KeyNavigation.tab: backButton - - onFocusChanged: { - if (focusItem.activeFocus) { - fl.contentY = 0 - } - } - } + // onFocusChanged: { + // if (focusItem.activeFocus) { + // fl.contentY = 0 + // } + // } + // } BackButtonType { id: backButton @@ -35,7 +32,7 @@ PageType { anchors.right: parent.right anchors.topMargin: 20 - KeyNavigation.tab: GC.isMobile() ? switcher : switcherAutoStart + // KeyNavigation.tab: GC.isMobile() ? switcher : switcherAutoStart } FlickableType { @@ -77,8 +74,8 @@ PageType { } } - KeyNavigation.tab: Qt.platform.os === "android" && !SettingsController.isNotificationPermissionGranted ? - labelWithButtonNotification.rightButton : labelWithButtonLanguage.rightButton + // KeyNavigation.tab: Qt.platform.os === "android" && !SettingsController.isNotificationPermissionGranted ? + // labelWithButtonNotification.rightButton : labelWithButtonLanguage.rightButton parentFlickable: fl } @@ -95,7 +92,7 @@ PageType { descriptionText: qsTr("Enable notifications to show the VPN state in the status bar") rightImageSource: "qrc:/images/controls/chevron-right.svg" - KeyNavigation.tab: labelWithButtonLanguage.rightButton + // KeyNavigation.tab: labelWithButtonLanguage.rightButton parentFlickable: fl clickedFunction: function() { @@ -117,7 +114,6 @@ PageType { text: qsTr("Auto start") descriptionText: qsTr("Launch the application every time the device is starts") - KeyNavigation.tab: switcherAutoConnect parentFlickable: fl checked: SettingsController.isAutoStartEnabled() @@ -142,7 +138,6 @@ PageType { text: qsTr("Auto connect") descriptionText: qsTr("Connect to VPN on app start") - KeyNavigation.tab: switcherStartMinimized parentFlickable: fl checked: SettingsController.isAutoConnectEnabled() @@ -167,7 +162,6 @@ PageType { text: qsTr("Start minimized") descriptionText: qsTr("Launch application minimized") - KeyNavigation.tab: labelWithButtonLanguage.rightButton parentFlickable: fl checked: SettingsController.isStartMinimizedEnabled() @@ -190,11 +184,10 @@ PageType { descriptionText: LanguageModel.currentLanguageName rightImageSource: "qrc:/images/controls/chevron-right.svg" - KeyNavigation.tab: labelWithButtonLogging.rightButton parentFlickable: fl clickedFunction: function() { - selectLanguageDrawer.open() + selectLanguageDrawer.openTriggered() } } @@ -208,7 +201,6 @@ PageType { descriptionText: SettingsController.isLoggingEnabled ? qsTr("Enabled") : qsTr("Disabled") rightImageSource: "qrc:/images/controls/chevron-right.svg" - KeyNavigation.tab: labelWithButtonReset.rightButton parentFlickable: fl clickedFunction: function() { @@ -245,12 +237,12 @@ PageType { } if (!GC.isMobile()) { - root.defaultActiveFocusItem.forceActiveFocus() + // root.defaultActiveFocusItem.forceActiveFocus() } } var noButtonFunction = function() { if (!GC.isMobile()) { - root.defaultActiveFocusItem.forceActiveFocus() + // root.defaultActiveFocusItem.forceActiveFocus() } } @@ -268,10 +260,10 @@ PageType { width: root.width height: root.height - onClosed: { - if (!GC.isMobile()) { - focusItem.forceActiveFocus() - } - } + // onClosed: { + // if (!GC.isMobile()) { + // focusItem.forceActiveFocus() + // } + // } } } diff --git a/client/ui/qml/Pages2/PageSettingsBackup.qml b/client/ui/qml/Pages2/PageSettingsBackup.qml index abede9b3..35d45589 100644 --- a/client/ui/qml/Pages2/PageSettingsBackup.qml +++ b/client/ui/qml/Pages2/PageSettingsBackup.qml @@ -17,7 +17,7 @@ import "../Controls2/TextTypes" PageType { id: root - defaultActiveFocusItem: focusItem + // defaultActiveFocusItem: focusItem Connections { target: SettingsController @@ -36,11 +36,6 @@ PageType { } } - Item { - id: focusItem - KeyNavigation.tab: backButton - } - BackButtonType { id: backButton @@ -48,8 +43,6 @@ PageType { anchors.left: parent.left anchors.right: parent.right anchors.topMargin: 20 - - KeyNavigation.tab: makeBackupButton } FlickableType { @@ -111,8 +104,6 @@ PageType { PageController.showNotificationMessage(qsTr("Backup file saved")) } } - - KeyNavigation.tab: restoreBackupButton } BasicButtonType { diff --git a/client/ui/qml/Pages2/PageSettingsConnection.qml b/client/ui/qml/Pages2/PageSettingsConnection.qml index 31b1c764..5991b713 100644 --- a/client/ui/qml/Pages2/PageSettingsConnection.qml +++ b/client/ui/qml/Pages2/PageSettingsConnection.qml @@ -12,15 +12,8 @@ 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 @@ -28,8 +21,6 @@ PageType { anchors.left: parent.left anchors.right: parent.right anchors.topMargin: 20 - - KeyNavigation.tab: amneziaDnsSwitch } FlickableType { @@ -67,8 +58,6 @@ PageType { SettingsController.toggleAmneziaDns(checked) } } - - KeyNavigation.tab: dnsServersButton.rightButton } DividerType {} @@ -84,8 +73,6 @@ PageType { clickedFunction: function() { PageController.goToPage(PageEnum.PageSettingsDns) } - - KeyNavigation.tab: splitTunnelingButton.rightButton } DividerType {} diff --git a/client/ui/qml/Pages2/PageSettingsDns.qml b/client/ui/qml/Pages2/PageSettingsDns.qml index 1d7517d9..cede6c74 100644 --- a/client/ui/qml/Pages2/PageSettingsDns.qml +++ b/client/ui/qml/Pages2/PageSettingsDns.qml @@ -14,13 +14,6 @@ import "../Components" PageType { id: root - defaultActiveFocusItem: primaryDns.textField - - Item { - id: focusItem - KeyNavigation.tab: backButton - } - BackButtonType { id: backButton @@ -28,8 +21,6 @@ PageType { anchors.left: parent.left anchors.right: parent.right anchors.topMargin: 20 - - KeyNavigation.tab: root.defaultActiveFocusItem } FlickableType { @@ -80,8 +71,6 @@ PageType { textField.validator: RegularExpressionValidator { regularExpression: InstallController.ipAddressRegExp() } - - KeyNavigation.tab: secondaryDns.textField } TextFieldWithHeaderType { @@ -94,8 +83,6 @@ PageType { textField.validator: RegularExpressionValidator { regularExpression: InstallController.ipAddressRegExp() } - - KeyNavigation.tab: restoreDefaultButton } BasicButtonType { @@ -124,19 +111,17 @@ PageType { PageController.showNotificationMessage(qsTr("Settings have been reset")) if (!GC.isMobile()) { - defaultActiveFocusItem.forceActiveFocus() + // defaultActiveFocusItem.forceActiveFocus() } } var noButtonFunction = function() { if (!GC.isMobile()) { - defaultActiveFocusItem.forceActiveFocus() + // defaultActiveFocusItem.forceActiveFocus() } } showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) } - - KeyNavigation.tab: saveButton } BasicButtonType { diff --git a/client/ui/qml/Pages2/PageSettingsLogging.qml b/client/ui/qml/Pages2/PageSettingsLogging.qml index 9abfc453..bea366a6 100644 --- a/client/ui/qml/Pages2/PageSettingsLogging.qml +++ b/client/ui/qml/Pages2/PageSettingsLogging.qml @@ -16,13 +16,6 @@ import "../Controls2/TextTypes" PageType { id: root - defaultActiveFocusItem: focusItem - - Item { - id: focusItem - KeyNavigation.tab: backButton - } - BackButtonType { id: backButton @@ -30,8 +23,6 @@ PageType { anchors.left: parent.left anchors.right: parent.right anchors.topMargin: 20 - - KeyNavigation.tab: switcher } FlickableType { @@ -68,12 +59,21 @@ PageType { text: qsTr("Enable logs") checked: SettingsController.isLoggingEnabled - //KeyNavigation.tab: openFolderButton + onCheckedChanged: { if (checked !== SettingsController.isLoggingEnabled) { SettingsController.isLoggingEnabled = checked } } + + onFocusChanged: { + console.debug("MOVE THIS LOGIC TO CPP!") + if (activeFocus) { + if (fl) { + fl.ensureVisible(this) + } + } + } } DividerType {} @@ -87,8 +87,6 @@ PageType { leftImageSource: "qrc:/images/controls/trash.svg" isSmallLeftImage: true - // KeyNavigation.tab: labelWithButton3 - clickedFunction: function() { var headerText = qsTr("Clear logs?") var yesButtonText = qsTr("Continue") @@ -104,9 +102,9 @@ PageType { } } var noButtonFunction = function() { - if (!GC.isMobile()) { - focusItem.forceActiveFocus() - } + // if (!GC.isMobile()) { + // focusItem.forceActiveFocus() + // } } showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) @@ -142,8 +140,6 @@ PageType { leftImageSource: "qrc:/images/controls/folder-open.svg" isSmallLeftImage: true - // KeyNavigation.tab: labelWithButton3 - clickedFunction: function() { SettingsController.openLogsFolder() } @@ -161,7 +157,14 @@ PageType { leftImageSource: "qrc:/images/controls/save.svg" isSmallLeftImage: true - // KeyNavigation.tab: labelWithButton3 + onFocusChanged: { + console.debug("MOVE THIS LOGIC TO CPP!") + if (activeFocus) { + if (fl) { + fl.ensureVisible(this) + } + } + } clickedFunction: function() { var fileName = "" @@ -221,7 +224,14 @@ PageType { leftImageSource: "qrc:/images/controls/folder-open.svg" isSmallLeftImage: true - // KeyNavigation.tab: labelWithButton3 + onFocusChanged: { + console.debug("MOVE THIS LOGIC TO CPP!") + if (activeFocus) { + if (fl) { + fl.ensureVisible(this) + } + } + } clickedFunction: function() { SettingsController.openServiceLogsFolder() @@ -245,7 +255,14 @@ PageType { leftImageSource: "qrc:/images/controls/save.svg" isSmallLeftImage: true - // KeyNavigation.tab: labelWithButton3 + onFocusChanged: { + console.debug("MOVE THIS LOGIC TO CPP!") + if (activeFocus) { + if (fl) { + fl.ensureVisible(this) + } + } + } clickedFunction: function() { var fileName = "" diff --git a/client/ui/qml/Pages2/PageSettingsServerData.qml b/client/ui/qml/Pages2/PageSettingsServerData.qml index e170a351..0568c5f4 100644 --- a/client/ui/qml/Pages2/PageSettingsServerData.qml +++ b/client/ui/qml/Pages2/PageSettingsServerData.qml @@ -100,8 +100,6 @@ PageType { 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() @@ -121,8 +119,6 @@ PageType { text: qsTr("Reboot server") textColor: AmneziaStyle.color.vibrantRed - 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?") diff --git a/client/ui/qml/Pages2/PageSettingsServerInfo.qml b/client/ui/qml/Pages2/PageSettingsServerInfo.qml index 95ae5c8a..4f6ab934 100644 --- a/client/ui/qml/Pages2/PageSettingsServerInfo.qml +++ b/client/ui/qml/Pages2/PageSettingsServerInfo.qml @@ -25,7 +25,7 @@ PageType { property int pageSettingsApiServerInfo: 3 property int pageSettingsApiLanguageList: 4 - defaultActiveFocusItem: focusItem + // defaultActiveFocusItem: focusItem Connections { target: PageController @@ -46,11 +46,6 @@ PageType { ] } - Item { - id: focusItem - KeyNavigation.tab: header - } - ColumnLayout { anchors.fill: parent @@ -75,7 +70,6 @@ PageType { BackButtonType { id: backButton - KeyNavigation.tab: headerContent.actionButton backButtonFunction: function() { if (nestedStackView.currentIndex === root.pageSettingsApiServerInfo && @@ -108,13 +102,11 @@ PageType { } } - KeyNavigation.tab: tabBar - actionButtonFunction: function() { if (nestedStackView.currentIndex === root.pageSettingsApiLanguageList) { nestedStackView.currentIndex = root.pageSettingsApiServerInfo } else { - serverNameEditDrawer.open() + serverNameEditDrawer.openTriggered() } } } @@ -133,7 +125,7 @@ PageType { } } - expandedContent: ColumnLayout { + expandedStateContent: ColumnLayout { anchors.top: parent.top anchors.left: parent.left anchors.right: parent.right @@ -149,11 +141,6 @@ PageType { } } - Item { - id: focusItem1 - KeyNavigation.tab: serverName.textField - } - TextFieldWithHeaderType { id: serverName @@ -162,8 +149,6 @@ PageType { textFieldText: name textField.maximumLength: 30 checkEmptyText: true - - KeyNavigation.tab: saveButton } BasicButtonType { @@ -172,7 +157,6 @@ PageType { Layout.fillWidth: true text: qsTr("Save") - KeyNavigation.tab: focusItem1 clickedFunc: function() { if (serverName.textFieldText === "") { @@ -182,7 +166,7 @@ PageType { if (serverName.textFieldText !== name) { name = serverName.textFieldText } - serverNameEditDrawer.close() + serverNameEditDrawer.closeTriggered() } } } @@ -219,7 +203,6 @@ PageType { isSelected: tabBar.currentIndex === root.pageSettingsServerProtocols text: qsTr("Protocols") - KeyNavigation.tab: servicesTab Keys.onReturnPressed: tabBar.currentIndex = root.pageSettingsServerProtocols Keys.onEnterPressed: tabBar.currentIndex = root.pageSettingsServerProtocols } @@ -231,7 +214,6 @@ PageType { isSelected: tabBar.currentIndex === root.pageSettingsServerServices text: qsTr("Services") - KeyNavigation.tab: dataTab Keys.onReturnPressed: tabBar.currentIndex = root.pageSettingsServerServices Keys.onEnterPressed: tabBar.currentIndex = root.pageSettingsServerServices } diff --git a/client/ui/qml/Pages2/PageSettingsServerProtocol.qml b/client/ui/qml/Pages2/PageSettingsServerProtocol.qml index dcdf01af..73e1e2c6 100644 --- a/client/ui/qml/Pages2/PageSettingsServerProtocol.qml +++ b/client/ui/qml/Pages2/PageSettingsServerProtocol.qml @@ -21,13 +21,6 @@ PageType { property bool isClearCacheVisible: ServersModel.isProcessedServerHasWriteAccess() && !ContainersModel.isServiceContainer(ContainersModel.getProcessedContainerIndex()) - defaultActiveFocusItem: focusItem - - Item { - id: focusItem - KeyNavigation.tab: backButton - } - ColumnLayout { id: header @@ -39,7 +32,6 @@ PageType { BackButtonType { id: backButton - KeyNavigation.tab: protocols } HeaderType { @@ -168,7 +160,6 @@ PageType { Layout.fillWidth: true visible: root.isClearCacheVisible - KeyNavigation.tab: removeButton text: qsTr("Clear profile") diff --git a/client/ui/qml/Pages2/PageSettingsServersList.qml b/client/ui/qml/Pages2/PageSettingsServersList.qml index 102dd46f..6f43c3fe 100644 --- a/client/ui/qml/Pages2/PageSettingsServersList.qml +++ b/client/ui/qml/Pages2/PageSettingsServersList.qml @@ -18,13 +18,6 @@ import "../Components" PageType { id: root - defaultActiveFocusItem: focusItem - - Item { - id: focusItem - KeyNavigation.tab: backButton - } - ColumnLayout { id: header @@ -36,7 +29,6 @@ PageType { BackButtonType { id: backButton - KeyNavigation.tab: servers } HeaderType { @@ -48,95 +40,88 @@ PageType { } } - FlickableType { - id: fl + ListView { + id: servers + objectName: "servers" + + width: parent.width anchors.top: header.bottom anchors.topMargin: 16 - contentHeight: col.implicitHeight + anchors.left: parent.left + anchors.right: parent.right - Column { - id: col - anchors.top: parent.top - anchors.left: parent.left - anchors.right: parent.right + height: 500 // servers.contentItem.height // TODO: calculate height - ListView { - id: servers - width: parent.width - height: servers.contentItem.height + model: ServersModel - model: ServersModel + clip: true + interactive: false - 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() + // } - 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) + // } - fl.ensureVisible(this.currentItem) - } + onVisibleChanged: { + if (visible) { + currentIndex = 0 + } + } - onVisibleChanged: { - if (visible) { - currentIndex = 0 - } - } + delegate: Item { + implicitWidth: servers.width + implicitHeight: delegateContent.implicitHeight - delegate: Item { - implicitWidth: servers.width - implicitHeight: delegateContent.implicitHeight + // onFocusChanged: { + // if (focus) { + // server.rightButton.forceActiveFocus() + // } + // } - onFocusChanged: { - if (focus) { - server.rightButton.forceActiveFocus() - } - } + ColumnLayout { + id: delegateContent - ColumnLayout { - id: delegateContent + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right - anchors.top: parent.top - anchors.left: parent.left - anchors.right: parent.right + LabelWithButtonType { + id: server + Layout.fillWidth: true - LabelWithButtonType { - id: server - Layout.fillWidth: true - - text: name - parentFlickable: fl - descriptionText: { - var servicesNameString = "" - var servicesName = ServersModel.getAllInstalledServicesName(index) - for (var i = 0; i < servicesName.length; i++) { - servicesNameString += servicesName[i] + " · " - } - - if (ServersModel.isServerFromApi(index)) { - return servicesNameString + serverDescription - } else { - return servicesNameString + hostName - } - } - rightImageSource: "qrc:/images/controls/chevron-right.svg" - - clickedFunction: function() { - ServersModel.processedIndex = index - PageController.goToPage(PageEnum.PageSettingsServerInfo) - } + text: name + // parentFlickable: fl + descriptionText: { + var servicesNameString = "" + var servicesName = ServersModel.getAllInstalledServicesName(index) + for (var i = 0; i < servicesName.length; i++) { + servicesNameString += servicesName[i] + " · " } - DividerType {} + if (ServersModel.isServerFromApi(index)) { + return servicesNameString + serverDescription + } else { + return servicesNameString + hostName + } + } + rightImageSource: "qrc:/images/controls/chevron-right.svg" + + clickedFunction: function() { + ServersModel.processedIndex = index + PageController.goToPage(PageEnum.PageSettingsServerInfo) } } + + DividerType {} } } } diff --git a/client/ui/qml/Pages2/PageSettingsSplitTunneling.qml b/client/ui/qml/Pages2/PageSettingsSplitTunneling.qml index f5fe285a..4bd4d0a4 100644 --- a/client/ui/qml/Pages2/PageSettingsSplitTunneling.qml +++ b/client/ui/qml/Pages2/PageSettingsSplitTunneling.qml @@ -23,13 +23,6 @@ PageType { property var isServerFromTelegramApi: ServersModel.getDefaultServerData("isServerFromTelegramApi") - defaultActiveFocusItem: searchField.textField - - Item { - id: focusItem - KeyNavigation.tab: backButton - } - property bool pageEnabled Component.onCompleted: { @@ -99,7 +92,6 @@ PageType { BackButtonType { id: backButton - KeyNavigation.tab: switcher } RowLayout { @@ -129,8 +121,6 @@ PageType { onToggled: { onToggledFunc() } Keys.onEnterPressed: { onToggledFunc() } Keys.onReturnPressed: { onToggledFunc() } - - KeyNavigation.tab: selector } } @@ -180,11 +170,11 @@ PageType { } } - KeyNavigation.tab: { - return sites.count > 0 ? - sites : - searchField.textField - } + // KeyNavigation.tab: { + // return sites.count > 0 ? + // sites : + // searchField.textField + // } } } @@ -325,7 +315,7 @@ PageType { textFieldPlaceholderText: qsTr("website or IP") buttonImageSource: "qrc:/images/controls/plus.svg" - KeyNavigation.tab: GC.isMobile() ? focusItem : addSiteButtonImage + // KeyNavigation.tab: GC.isMobile() ? focusItem : addSiteButtonImage clickedFunc: function() { PageController.showBusyIndicator(true) @@ -344,7 +334,7 @@ PageType { imageColor: AmneziaStyle.color.paleGray onClicked: function () { - moreActionsDrawer.open() + moreActionsDrawer.openTriggered() } Keys.onReturnPressed: addSiteButtonImage.clicked() @@ -361,12 +351,12 @@ PageType { expandedHeight: parent.height * 0.4375 onClosed: { - if (root.defaultActiveFocusItem && !GC.isMobile()) { - root.defaultActiveFocusItem.forceActiveFocus() - } + // if (root.defaultActiveFocusItem && !GC.isMobile()) { + // root.defaultActiveFocusItem.forceActiveFocus() + // } } - expandedContent: ColumnLayout { + expandedStateContent: ColumnLayout { id: moreActionsDrawerContent anchors.top: parent.top @@ -387,11 +377,6 @@ PageType { } } - Item { - id: focusItem1 - KeyNavigation.tab: importSitesButton.rightButton - } - Header2Type { Layout.fillWidth: true Layout.margins: 16 @@ -407,10 +392,8 @@ PageType { rightImageSource: "qrc:/images/controls/chevron-right.svg" clickedFunction: function() { - importSitesDrawer.open() + importSitesDrawer.openTriggered() } - - KeyNavigation.tab: exportSitesButton } DividerType {} @@ -420,8 +403,6 @@ PageType { Layout.fillWidth: true text: qsTr("Save site list") - KeyNavigation.tab: focusItem1 - clickedFunction: function() { var fileName = "" if (GC.isMobile()) { @@ -436,7 +417,7 @@ PageType { if (fileName !== "") { PageController.showBusyIndicator(true) SitesController.exportSites(fileName) - moreActionsDrawer.close() + moreActionsDrawer.closeTriggered() PageController.showBusyIndicator(false) } } @@ -458,7 +439,7 @@ PageType { } } - expandedContent: Item { + expandedStateContent: Item { implicitHeight: importSitesDrawer.expandedHeight Connections { @@ -469,11 +450,6 @@ PageType { } } - Item { - id: focusItem2 - KeyNavigation.tab: importSitesDrawerBackButton - } - BackButtonType { id: importSitesDrawerBackButton @@ -482,10 +458,8 @@ PageType { anchors.right: parent.right anchors.topMargin: 16 - KeyNavigation.tab: importSitesButton2 - backButtonFunction: function() { - importSitesDrawer.close() + importSitesDrawer.closeTriggered() } } @@ -516,7 +490,6 @@ PageType { Layout.fillWidth: true text: qsTr("Replace site list") - KeyNavigation.tab: importSitesButton3 clickedFunction: function() { var fileName = SystemController.getFileName(qsTr("Open sites file"), @@ -533,7 +506,6 @@ PageType { 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"), @@ -548,8 +520,8 @@ PageType { PageController.showBusyIndicator(true) SitesController.importSites(fileName, replaceExistingSites) PageController.showBusyIndicator(false) - importSitesDrawer.close() - moreActionsDrawer.close() + importSitesDrawer.closeTriggered() + moreActionsDrawer.closeTriggered() } DividerType {} diff --git a/client/ui/qml/Pages2/PageSetupWizardApiServiceInfo.qml b/client/ui/qml/Pages2/PageSetupWizardApiServiceInfo.qml index 75fd3d47..9e541ded 100644 --- a/client/ui/qml/Pages2/PageSetupWizardApiServiceInfo.qml +++ b/client/ui/qml/Pages2/PageSetupWizardApiServiceInfo.qml @@ -15,8 +15,6 @@ import "../Components" PageType { id: root - defaultActiveFocusItem: focusItem - FlickableType { id: fl anchors.top: parent.top @@ -32,15 +30,9 @@ PageType { spacing: 0 - Item { - id: focusItem - KeyNavigation.tab: backButton - } - BackButtonType { id: backButton Layout.topMargin: 20 -// KeyNavigation.tab: fileButton.rightButton } HeaderType { diff --git a/client/ui/qml/Pages2/PageSetupWizardApiServicesList.qml b/client/ui/qml/Pages2/PageSetupWizardApiServicesList.qml index 85a50393..82e2e7e1 100644 --- a/client/ui/qml/Pages2/PageSetupWizardApiServicesList.qml +++ b/client/ui/qml/Pages2/PageSetupWizardApiServicesList.qml @@ -14,8 +14,6 @@ import "../Config" PageType { id: root - defaultActiveFocusItem: focusItem - FlickableType { id: fl anchors.top: parent.top @@ -31,15 +29,9 @@ PageType { spacing: 0 - Item { - id: focusItem - KeyNavigation.tab: backButton - } - BackButtonType { id: backButton Layout.topMargin: 20 -// KeyNavigation.tab: fileButton.rightButton } HeaderType { diff --git a/client/ui/qml/Pages2/PageSetupWizardConfigSource.qml b/client/ui/qml/Pages2/PageSetupWizardConfigSource.qml index 7c031997..e5030336 100644 --- a/client/ui/qml/Pages2/PageSetupWizardConfigSource.qml +++ b/client/ui/qml/Pages2/PageSetupWizardConfigSource.qml @@ -25,7 +25,7 @@ PageType { } } - defaultActiveFocusItem: focusItem + // defaultActiveFocusItem: focusItem FlickableType { id: fl @@ -42,15 +42,11 @@ PageType { spacing: 0 - Item { - id: focusItem - KeyNavigation.tab: textKey.textField - } - - HeaderType { - property bool isVisible: SettingsController.getInstallationUuid() !== "" || PageController.isStartPageVisible() + id: moreButton + property bool isVisible: SettingsController.getInstallationUuid() !== "" || PageController.isStartPageVisible() + Layout.fillWidth: true Layout.topMargin: 24 Layout.rightMargin: 16 @@ -60,7 +56,16 @@ PageType { actionButtonImage: isVisible ? "qrc:/images/controls/more-vertical.svg" : "" actionButtonFunction: function() { - moreActionsDrawer.open() + moreActionsDrawer.openTriggered() + } + + actionButton.onFocusChanged: { + console.debug("MOVE THIS LOGIC TO CPP!") + if (actionButton.activeFocus) { + if (fl) { + fl.ensureVisible(moreButton) + } + } } DrawerType2 { @@ -71,7 +76,7 @@ PageType { anchors.fill: parent expandedHeight: root.height * 0.5 - expandedContent: ColumnLayout { + expandedStateContent: ColumnLayout { anchors.top: parent.top anchors.left: parent.left anchors.right: parent.right @@ -154,8 +159,6 @@ PageType { textField.text = "" textField.paste() } - - KeyNavigation.tab: continueButton } BasicButtonType { @@ -166,10 +169,18 @@ PageType { Layout.rightMargin: 16 Layout.leftMargin: 16 + onFocusChanged: { + console.debug("MOVE THIS LOGIC TO CPP!") + if (activeFocus) { + if (fl) { + fl.ensureVisible(this) + } + } + } + visible: textKey.textFieldText !== "" text: qsTr("Continue") - Keys.onTabPressed: lastItemTabClicked(focusItem) clickedFunc: function() { if (ImportController.extractConfigFromData(textKey.textFieldText)) { @@ -203,6 +214,15 @@ PageType { rightImageSource: "qrc:/images/controls/chevron-right.svg" leftImageSource: "qrc:/images/controls/amnezia.svg" + focusItem.onFocusChanged: { + console.debug("MOVE THIS LOGIC TO CPP!") + if (focusItem.activeFocus) { + if (fl) { + fl.ensureVisible(apiInstalling) + } + } + } + onClicked: function() { PageController.showBusyIndicator(true) var result = InstallController.fillAvailableServices() @@ -227,6 +247,15 @@ PageType { rightImageSource: "qrc:/images/controls/chevron-right.svg" leftImageSource: "qrc:/images/controls/server.svg" + focusItem.onFocusChanged: { + console.debug("MOVE THIS LOGIC TO CPP!") + if (focusItem.activeFocus) { + if (fl) { + fl.ensureVisible(manualInstalling) + } + } + } + onClicked: { PageController.goToPage(PageEnum.PageSetupWizardCredentials) } @@ -247,6 +276,15 @@ PageType { rightImageSource: "qrc:/images/controls/chevron-right.svg" leftImageSource: "qrc:/images/controls/archive-restore.svg" + focusItem.onFocusChanged: { + console.debug("MOVE THIS LOGIC TO CPP!") + if (focusItem.activeFocus) { + if (fl) { + fl.ensureVisible(backupRestore) + } + } + } + onClicked: { var filePath = SystemController.getFileName(qsTr("Open backup file"), qsTr("Backup files (*.backup)")) @@ -271,6 +309,13 @@ PageType { rightImageSource: "qrc:/images/controls/chevron-right.svg" leftImageSource: "qrc:/images/controls/folder-search-2.svg" + focusItem.onFocusChanged: { + console.debug("MOVE THIS LOGIC TO CPP!") + if (fl) { + fl.ensureVisible(openFile) + } + } + onClicked: { var nameFilter = !ServersModel.getServersCount() ? "Config or backup files (*.vpn *.ovpn *.conf *.json *.backup)" : "Config files (*.vpn *.ovpn *.conf *.json)" @@ -298,6 +343,15 @@ PageType { rightImageSource: "qrc:/images/controls/chevron-right.svg" leftImageSource: "qrc:/images/controls/scan-line.svg" + focusItem.onFocusChanged: { + console.debug("MOVE THIS LOGIC TO CPP!") + if (focusItem.activeFocus) { + if (fl) { + fl.ensureVisible(scanQr) + } + } + } + onClicked: { ImportController.startDecodingQr() if (Qt.platform.os === "ios") { @@ -321,6 +375,15 @@ PageType { rightImageSource: "qrc:/images/controls/chevron-right.svg" leftImageSource: "qrc:/images/controls/help-circle.svg" + focusItem.onFocusChanged: { + console.debug("MOVE THIS LOGIC TO CPP!") + if (focusItem.activeFocus) { + if (fl) { + fl.ensureVisible(siteLink) + } + } + } + onClicked: { Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl()) } diff --git a/client/ui/qml/Pages2/PageSetupWizardCredentials.qml b/client/ui/qml/Pages2/PageSetupWizardCredentials.qml index aced12b1..5139ad5a 100644 --- a/client/ui/qml/Pages2/PageSetupWizardCredentials.qml +++ b/client/ui/qml/Pages2/PageSetupWizardCredentials.qml @@ -13,12 +13,7 @@ import "../Controls2/TextTypes" PageType { id: root - defaultActiveFocusItem: hostname.textField - - Item { - id: focusItem - KeyNavigation.tab: backButton - } + // defaultActiveFocusItem: hostname.textField BackButtonType { id: backButton @@ -27,8 +22,6 @@ PageType { anchors.left: parent.left anchors.right: parent.right anchors.topMargin: 20 - - KeyNavigation.tab: hostname.textField } FlickableType { @@ -64,8 +57,6 @@ PageType { textField.onFocusChanged: { textField.text = textField.text.replace(/^\s+|\s+$/g, '') } - - KeyNavigation.tab: username.textField } TextFieldWithHeaderType { @@ -78,8 +69,6 @@ PageType { textField.onFocusChanged: { textField.text = textField.text.replace(/^\s+|\s+$/g, '') } - - KeyNavigation.tab: secretData.textField } TextFieldWithHeaderType { @@ -100,8 +89,6 @@ PageType { textField.onFocusChanged: { textField.text = textField.text.replace(/^\s+|\s+$/g, '') } - - KeyNavigation.tab: continueButton } BasicButtonType { @@ -112,8 +99,6 @@ PageType { text: qsTr("Continue") - Keys.onTabPressed: lastItemTabClicked(focusItem) - clickedFunc: function() { forceActiveFocus() if (!isCredentialsFilled()) { diff --git a/client/ui/qml/Pages2/PageSetupWizardEasy.qml b/client/ui/qml/Pages2/PageSetupWizardEasy.qml index 02a7c928..27df72b0 100644 --- a/client/ui/qml/Pages2/PageSetupWizardEasy.qml +++ b/client/ui/qml/Pages2/PageSetupWizardEasy.qml @@ -17,7 +17,6 @@ PageType { id: root property bool isEasySetup: true - defaultActiveFocusItem: focusItem SortFilterProxyModel { id: proxyContainersModel @@ -34,14 +33,6 @@ PageType { } } - Item { - id: focusItem - implicitWidth: 1 - implicitHeight: 54 - - KeyNavigation.tab: backButton - } - BackButtonType { id: backButton @@ -49,8 +40,6 @@ PageType { anchors.left: parent.left anchors.right: parent.right anchors.topMargin: 20 - - KeyNavigation.tab: continueButton } FlickableType { @@ -163,7 +152,7 @@ PageType { implicitWidth: parent.width text: qsTr("Continue") - KeyNavigation.tab: setupLaterButton + parentFlickable: fl clickedFunc: function() { diff --git a/client/ui/qml/Pages2/PageSetupWizardProtocolSettings.qml b/client/ui/qml/Pages2/PageSetupWizardProtocolSettings.qml index 6b4c0a1c..de8275f1 100644 --- a/client/ui/qml/Pages2/PageSetupWizardProtocolSettings.qml +++ b/client/ui/qml/Pages2/PageSetupWizardProtocolSettings.qml @@ -62,19 +62,12 @@ 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 { @@ -113,13 +106,13 @@ PageType { parent: root onClosed: { if (!GC.isMobile()) { - defaultActiveFocusItem.forceActiveFocus() + // defaultActiveFocusItem.forceActiveFocus() } } anchors.fill: parent expandedHeight: parent.height * 0.9 - expandedContent: Item { + expandedStateContent: Item { Connections { target: showDetailsDrawer enabled: !GC.isMobile() @@ -130,15 +123,15 @@ PageType { implicitHeight: showDetailsDrawer.expandedHeight - Item { - id: focusItem2 - KeyNavigation.tab: showDetailsBackButton - onFocusChanged: { - if (focusItem2.activeFocus) { - fl.contentY = 0 - } - } - } + // Item { + // id: focusItem2 + // KeyNavigation.tab: showDetailsBackButton + // onFocusChanged: { + // if (focusItem2.activeFocus) { + // fl.contentY = 0 + // } + // } + // } BackButtonType { id: showDetailsBackButton @@ -148,8 +141,6 @@ PageType { anchors.right: parent.right anchors.topMargin: 16 - KeyNavigation.tab: showDetailsCloseButton - backButtonFunction: function() { showDetailsDrawer.close() } @@ -230,7 +221,7 @@ PageType { Layout.fillWidth: true rootWidth: root.width - KeyNavigation.tab: (port.visible && port.enabled) ? port.textField : installButton + // KeyNavigation.tab: (port.visible && port.enabled) ? port.textField : installButton } TextFieldWithHeaderType { @@ -242,8 +233,6 @@ PageType { headerText: qsTr("Port") textField.maximumLength: 5 textField.validator: IntValidator { bottom: 1; top: 65535 } - - KeyNavigation.tab: installButton } Rectangle { @@ -289,10 +278,10 @@ PageType { transportProtoSelector.visible = protocolSelectorVisible transportProtoHeader.visible = protocolSelectorVisible - if (port.visible && port.enabled) - defaultActiveFocusItem = port.textField - else - defaultActiveFocusItem = focusItem + // if (port.visible && port.enabled) + // defaultActiveFocusItem = port.textField + // else + // defaultActiveFocusItem = focusItem } } } diff --git a/client/ui/qml/Pages2/PageSetupWizardProtocols.qml b/client/ui/qml/Pages2/PageSetupWizardProtocols.qml index 48265f66..15c1170f 100644 --- a/client/ui/qml/Pages2/PageSetupWizardProtocols.qml +++ b/client/ui/qml/Pages2/PageSetupWizardProtocols.qml @@ -15,13 +15,6 @@ import "../Config" PageType { id: root - defaultActiveFocusItem: focusItem - - Item { - id: focusItem - KeyNavigation.tab: backButton - } - SortFilterProxyModel { id: proxyContainersModel sourceModel: ContainersModel @@ -52,7 +45,6 @@ PageType { BackButtonType { id: backButton - KeyNavigation.tab: containers } } diff --git a/client/ui/qml/Pages2/PageSetupWizardStart.qml b/client/ui/qml/Pages2/PageSetupWizardStart.qml index b12c7830..2d6790ba 100644 --- a/client/ui/qml/Pages2/PageSetupWizardStart.qml +++ b/client/ui/qml/Pages2/PageSetupWizardStart.qml @@ -14,8 +14,6 @@ import "../Components" PageType { id: root - defaultActiveFocusItem: focusItem - ColumnLayout { id: content @@ -32,11 +30,6 @@ PageType { Layout.preferredHeight: 287 } - Item { - id: focusItem - KeyNavigation.tab: startButton - } - BasicButtonType { id: startButton Layout.fillWidth: true @@ -50,8 +43,6 @@ PageType { clickedFunc: function() { PageController.goToPage(PageEnum.PageSetupWizardConfigSource) } - - Keys.onTabPressed: lastItemTabClicked(focusItem) } } } diff --git a/client/ui/qml/Pages2/PageSetupWizardTextKey.qml b/client/ui/qml/Pages2/PageSetupWizardTextKey.qml index c4227df1..86880713 100644 --- a/client/ui/qml/Pages2/PageSetupWizardTextKey.qml +++ b/client/ui/qml/Pages2/PageSetupWizardTextKey.qml @@ -13,14 +13,6 @@ import "../Config" PageType { id: root - defaultActiveFocusItem: textKey.textField - - - Item { - id: focusItem - KeyNavigation.tab: backButton - } - FlickableType { id: fl anchors.top: parent.top @@ -39,7 +31,6 @@ PageType { BackButtonType { id: backButton Layout.topMargin: 20 - KeyNavigation.tab: textKey.textField } HeaderType { @@ -67,8 +58,6 @@ PageType { textField.text = "" textField.paste() } - - KeyNavigation.tab: continueButton } } } diff --git a/client/ui/qml/Pages2/PageSetupWizardViewConfig.qml b/client/ui/qml/Pages2/PageSetupWizardViewConfig.qml index 92048f36..dffaa8c6 100644 --- a/client/ui/qml/Pages2/PageSetupWizardViewConfig.qml +++ b/client/ui/qml/Pages2/PageSetupWizardViewConfig.qml @@ -16,13 +16,6 @@ PageType { property bool showContent: false - defaultActiveFocusItem: focusItem - - Item { - id: focusItem - KeyNavigation.tab: backButton - } - BackButtonType { id: backButton @@ -30,8 +23,6 @@ PageType { anchors.left: parent.left anchors.right: parent.right anchors.topMargin: 20 - - KeyNavigation.tab: showContentButton } Connections { @@ -107,7 +98,6 @@ PageType { textColor: AmneziaStyle.color.goldenApricot text: showContent ? qsTr("Collapse content") : qsTr("Show content") - KeyNavigation.tab: connectButton clickedFunc: function() { showContent = !showContent diff --git a/client/ui/qml/Pages2/PageShare.qml b/client/ui/qml/Pages2/PageShare.qml index 995fa3e7..cef516df 100644 --- a/client/ui/qml/Pages2/PageShare.qml +++ b/client/ui/qml/Pages2/PageShare.qml @@ -18,8 +18,6 @@ import "../Config" PageType { id: root - defaultActiveFocusItem: clientNameTextField.textField - enum ConfigType { AmneziaConnection, OpenVpn, @@ -47,7 +45,7 @@ PageType { shareConnectionDrawer.headerText = qsTr("Connection to ") + serverSelector.text shareConnectionDrawer.configContentHeaderText = qsTr("File with connection settings to ") + serverSelector.text - shareConnectionDrawer.open() + shareConnectionDrawer. shareConnectionDrawer.contentVisible = false PageController.showBusyIndicator(true) @@ -104,7 +102,7 @@ PageType { } function onExportErrorOccurred(error) { - shareConnectionDrawer.close() + shareConnectionDrawer.closeTriggered() PageController.showErrorMessage(error) } @@ -172,16 +170,6 @@ PageType { spacing: 0 - Item { - id: focusItem - KeyNavigation.tab: header.actionButton - onFocusChanged: { - if (focusItem.activeFocus) { - a.contentY = 0 - } - } - } - HeaderType { id: header Layout.fillWidth: true @@ -191,10 +179,18 @@ PageType { actionButtonImage: "qrc:/images/controls/more-vertical.svg" actionButtonFunction: function() { - shareFullAccessDrawer.open() + shareFullAccessDrawer.openTriggered() } - KeyNavigation.tab: connectionRadioButton + // KeyNavigation.tab: connectionRadioButton + actionButton.onFocusChanged: { + console.debug("MOVE THIS LOGIC TO CPP!") + if (actionButton.activeFocus) { + if (fl) { + fl.ensureVisible(moreButton) + } + } + } DrawerType2 { id: shareFullAccessDrawer @@ -205,11 +201,11 @@ PageType { expandedHeight: root.height onClosed: { if (!GC.isMobile()) { - clientNameTextField.textField.forceActiveFocus() + // clientNameTextField.textField.forceActiveFocus() } } - expandedContent: ColumnLayout { + expandedStateContent: ColumnLayout { id: shareFullAccessDrawerContent anchors.top: parent.top anchors.left: parent.left @@ -226,7 +222,7 @@ PageType { target: shareFullAccessDrawer enabled: !GC.isMobile() function onOpened() { - focusItem.forceActiveFocus() + // focusItem.forceActiveFocus() } } @@ -240,10 +236,10 @@ 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 - } + // Item { + // id: focusItem + // // KeyNavigation.tab: shareFullAccessButton.rightButton + // } LabelWithButtonType { id: shareFullAccessButton @@ -251,11 +247,11 @@ PageType { text: qsTr("Share") rightImageSource: "qrc:/images/controls/chevron-right.svg" - KeyNavigation.tab: focusItem + // KeyNavigation.tab: focusItem clickedFunction: function() { PageController.goToPage(PageEnum.PageShareFullAccess) - shareFullAccessDrawer.close() + shareFullAccessDrawer.closeTriggered() } } @@ -288,12 +284,12 @@ PageType { implicitWidth: (root.width - 32) / 2 text: qsTr("Connection") - KeyNavigation.tab: usersRadioButton + // KeyNavigation.tab: usersRadioButton onClicked: { accessTypeSelector.currentIndex = 0 if (!GC.isMobile()) { - clientNameTextField.textField.forceActiveFocus() + // clientNameTextField.textField.forceActiveFocus() } } } @@ -305,7 +301,7 @@ PageType { implicitWidth: (root.width - 32) / 2 text: qsTr("Users") - KeyNavigation.tab: accessTypeSelector.currentIndex === 0 ? clientNameTextField.textField : serverSelector + // KeyNavigation.tab: accessTypeSelector.currentIndex === 0 ? clientNameTextField.textField : serverSelector onClicked: { accessTypeSelector.currentIndex = 1 @@ -313,7 +309,7 @@ PageType { ExportController.updateClientManagementModel(ContainersModel.getProcessedContainerIndex(), ServersModel.getProcessedServerCredentials()) PageController.showBusyIndicator(false) - focusItem.forceActiveFocus() + // focusItem.forceActiveFocus() } } } @@ -343,7 +339,7 @@ PageType { checkEmptyText: true - KeyNavigation.tab: serverSelector + // KeyNavigation.tab: serverSelector } @@ -390,7 +386,7 @@ PageType { serverSelector.severSelectorIndexChanged() } - serverSelector.close() + serverSelector.closeTriggered() } Component.onCompleted: { @@ -409,7 +405,7 @@ PageType { } } - KeyNavigation.tab: protocolSelector + // KeyNavigation.tab: protocolSelector } DropDownType { @@ -450,7 +446,7 @@ PageType { clickedFunction: function() { handler() - protocolSelector.close() + protocolSelector.closeTriggered() } Connections { @@ -509,11 +505,11 @@ PageType { } } - KeyNavigation.tab: accessTypeSelector.currentIndex === 0 ? - exportTypeSelector : - isSearchBarVisible ? - searchTextField.textField : - usersHeader.actionButton + // KeyNavigation.tab: accessTypeSelector.currentIndex === 0 ? + // exportTypeSelector : searchTextField.textField + // isSearchBarVisible ? + // searchTextField.textField : + // usersHeader.actionButton } DropDownType { @@ -549,7 +545,7 @@ PageType { clickedFunction: function() { exportTypeSelector.text = selectedText exportTypeSelector.currentIndex = currentIndex - exportTypeSelector.close() + exportTypeSelector.closeTriggered() } Component.onCompleted: { @@ -558,7 +554,7 @@ PageType { } } - KeyNavigation.tab: shareButton + // KeyNavigation.tab: shareButton } @@ -622,11 +618,11 @@ PageType { target: root function onIsSearchBarVisibleChanged() { if (root.isSearchBarVisible) { - searchTextField.textField.forceActiveFocus() + // searchTextField.textField.forceActiveFocus() } else { searchTextField.textFieldText = "" if (!GC.isMobile()) { - usersHeader.actionButton.forceActiveFocus() + // usersHeader.actionButton.forceActiveFocus() } } } @@ -638,15 +634,15 @@ PageType { function navigateTo() { if (GC.isMobile()) { - focusItem.forceActiveFocus() + // focusItem.forceActiveFocus() return; } if (searchTextField.textFieldText === "") { root.isSearchBarVisible = false - usersHeader.actionButton.forceActiveFocus() + // usersHeader.actionButton.forceActiveFocus() } else { - closeSearchButton.forceActiveFocus() + // closeSearchButton.forceActiveFocus() } } @@ -663,9 +659,9 @@ PageType { Keys.onTabPressed: { if (!GC.isMobile()) { if (clientsListView.model.count > 0) { - clientsListView.forceActiveFocus() + // clientsListView.forceActiveFocus() } else { - lastItemTabClicked(focusItem) + // lastItemTabClicked(focusItem) } } } @@ -706,10 +702,10 @@ PageType { if (!GC.isMobile()) { if (currentIndex < this.count - 1) { this.incrementCurrentIndex() - currentItem.focusItem.forceActiveFocus() + // currentItem.focusItem.forceActiveFocus() } else { this.currentIndex = 0 - lastItemTabClicked(focusItem) + // lastItemTabClicked(focusItem) } } } @@ -717,7 +713,7 @@ PageType { onActiveFocusChanged: { if (focus && !GC.isMobile()) { currentIndex = 0 - currentItem.focusItem.forceActiveFocus() + // currentItem.focusItem.forceActiveFocus() } } @@ -735,7 +731,7 @@ PageType { implicitWidth: clientsListView.width implicitHeight: delegateContent.implicitHeight - property alias focusItem: clientFocusItem.rightButton + // property alias focusItem: clientFocusItem.rightButton ColumnLayout { id: delegateContent @@ -755,7 +751,7 @@ PageType { rightImageSource: "qrc:/images/controls/chevron-right.svg" clickedFunction: function() { - clientInfoDrawer.open() + clientInfoDrawer.openTriggered() } } @@ -768,15 +764,15 @@ PageType { onClosed: { if (!GC.isMobile()) { - focusItem.forceActiveFocus() + // focusItem.forceActiveFocus() } } width: root.width height: root.height - expandedContent: ColumnLayout { - id: expandedContent + expandedStateContent: ColumnLayout { + id: expandedStateContent anchors.top: parent.top anchors.left: parent.left anchors.right: parent.right @@ -785,14 +781,14 @@ PageType { anchors.rightMargin: 16 onImplicitHeightChanged: { - clientInfoDrawer.expandedHeight = expandedContent.implicitHeight + 32 + clientInfoDrawer.expandedHeight = expandedStateContent.implicitHeight + 32 } Connections { target: clientInfoDrawer enabled: !GC.isMobile() function onOpened() { - focusItem1.forceActiveFocus() + // focusItem1.forceActiveFocus() } } @@ -846,11 +842,6 @@ PageType { text: qsTr("Allowed IPs: %1").arg(allowedIps) } - Item { - id: focusItem1 - KeyNavigation.tab: renameButton - } - BasicButtonType { id: renameButton Layout.fillWidth: true @@ -865,10 +856,10 @@ PageType { text: qsTr("Rename") - KeyNavigation.tab: revokeButton + // KeyNavigation.tab: revokeButton clickedFunc: function() { - clientNameEditDrawer.open() + clientNameEditDrawer.openTriggered() } DrawerType2 { @@ -881,11 +872,11 @@ PageType { onClosed: { if (!GC.isMobile()) { - focusItem1.forceActiveFocus() + // focusItem1.forceActiveFocus() } } - expandedContent: ColumnLayout { + expandedStateContent: ColumnLayout { anchors.top: parent.top anchors.left: parent.left anchors.right: parent.right @@ -897,15 +888,10 @@ PageType { target: clientNameEditDrawer enabled: !GC.isMobile() function onOpened() { - clientNameEditor.textField.forceActiveFocus() + // clientNameEditor.textField.forceActiveFocus() } } - Item { - id: focusItem2 - KeyNavigation.tab: clientNameEditor.textField - } - TextFieldWithHeaderType { id: clientNameEditor Layout.fillWidth: true @@ -914,7 +900,7 @@ PageType { textField.maximumLength: 20 checkEmptyText: true - KeyNavigation.tab: saveButton + // KeyNavigation.tab: saveButton } BasicButtonType { @@ -923,7 +909,7 @@ PageType { Layout.fillWidth: true text: qsTr("Save") - KeyNavigation.tab: focusItem2 + // KeyNavigation.tab: focusItem2 clickedFunc: function() { if (clientNameEditor.textFieldText === "") { @@ -937,7 +923,7 @@ PageType { ContainersModel.getProcessedContainerIndex(), ServersModel.getProcessedServerCredentials()) PageController.showBusyIndicator(false) - clientNameEditDrawer.close() + clientNameEditDrawer.closeTriggered() } } } @@ -958,7 +944,7 @@ PageType { borderWidth: 1 text: qsTr("Revoke") - KeyNavigation.tab: focusItem1 + // KeyNavigation.tab: focusItem1 clickedFunc: function() { var headerText = qsTr("Revoke the config for a user - %1?").arg(clientName) @@ -967,12 +953,12 @@ PageType { var noButtonText = qsTr("Cancel") var yesButtonFunction = function() { - clientInfoDrawer.close() + clientInfoDrawer.closeTriggered() root.revokeConfig(index) } var noButtonFunction = function() { if (!GC.isMobile()) { - focusItem1.forceActiveFocus() + // focusItem1.forceActiveFocus() } } @@ -993,7 +979,7 @@ PageType { anchors.fill: parent onClosed: { if (!GC.isMobile()) { - clientNameTextField.textField.forceActiveFocus() + // clientNameTextField.textField.forceActiveFocus() } } } @@ -1001,7 +987,7 @@ PageType { MouseArea { anchors.fill: parent onPressed: function(mouse) { - forceActiveFocus() + // forceActiveFocus() mouse.accepted = false } } diff --git a/client/ui/qml/Pages2/PageShareFullAccess.qml b/client/ui/qml/Pages2/PageShareFullAccess.qml index 404ba563..70451b83 100644 --- a/client/ui/qml/Pages2/PageShareFullAccess.qml +++ b/client/ui/qml/Pages2/PageShareFullAccess.qml @@ -18,12 +18,7 @@ import "../Config" PageType { id: root - defaultActiveFocusItem: focusItem - - Item { - id: focusItem - KeyNavigation.tab: backButton - } + // defaultActiveFocusItem: focusItem BackButtonType { id: backButton @@ -32,8 +27,6 @@ PageType { anchors.left: parent.left anchors.right: parent.right anchors.topMargin: 20 - - KeyNavigation.tab: serverSelector } FlickableType { @@ -85,8 +78,6 @@ PageType { descriptionText: qsTr("Server") headerText: qsTr("Server") - KeyNavigation.tab: shareButton - listView: ListViewWithRadioButtonType { id: serverSelectorListView @@ -137,8 +128,6 @@ PageType { text: qsTr("Share") leftImageSource: "qrc:/images/controls/share-2.svg" - Keys.onTabPressed: lastItemTabClicked(focusItem) - clickedFunc: function() { PageController.showBusyIndicator(true) @@ -166,10 +155,5 @@ PageType { id: shareConnectionDrawer anchors.fill: parent - onClosed: { - if (!GC.isMobile()) { - focusItem.forceActiveFocus() - } - } } } diff --git a/client/ui/qml/Pages2/PageStart.qml b/client/ui/qml/Pages2/PageStart.qml index 640c61ef..9f37458c 100644 --- a/client/ui/qml/Pages2/PageStart.qml +++ b/client/ui/qml/Pages2/PageStart.qml @@ -15,12 +15,12 @@ import "../Components" PageType { id: root - defaultActiveFocusItem: homeTabButton - property bool isControlsDisabled: false property bool isTabBarDisabled: false Connections { + objectName: "pageControllerConnection" + target: PageController function onGoToPageHome() { @@ -91,18 +91,20 @@ PageType { } } - function onForceTabBarActiveFocus() { - homeTabButton.focus = true - tabBar.forceActiveFocus() - } + // function onForceTabBarActiveFocus() { + // homeTabButton.focus = true + // tabBar.forceActiveFocus() + // } - function onForceStackActiveFocus() { - homeTabButton.focus = true - tabBarStackView.forceActiveFocus() - } + // function onForceStackActiveFocus() { + // homeTabButton.focus = true + // tabBarStackView.forceActiveFocus() + // } } Connections { + objectName: "installControllerConnections" + target: InstallController function onInstallationErrorOccurred(error) { @@ -165,6 +167,8 @@ PageType { } Connections { + objectName: "connectionControllerConnections" + target: ConnectionController function onReconnectWithUpdatedContainer(message) { @@ -182,6 +186,8 @@ PageType { } Connections { + objectName: "importControllerConnections" + target: ImportController function onImportErrorOccurred(error, goToPageHome) { @@ -196,6 +202,8 @@ PageType { } Connections { + objectName: "settingsControllerConnections" + target: SettingsController function onLoggingDisableByWatcher() { @@ -218,6 +226,7 @@ PageType { StackViewType { id: tabBarStackView + objectName: "tabBarStackView" anchors.top: parent.top anchors.right: parent.right @@ -254,6 +263,7 @@ PageType { TabBar { id: tabBar + objectName: "tabBar" anchors.right: parent.right anchors.left: parent.left @@ -269,6 +279,8 @@ PageType { enabled: !root.isControlsDisabled && !root.isTabBarDisabled background: Shape { + objectName: "backgroundShape" + width: parent.width height: parent.height @@ -289,21 +301,25 @@ PageType { TabImageButtonType { id: homeTabButton + objectName: "homeTabButton" + isSelected: tabBar.currentIndex === 0 image: "qrc:/images/controls/home.svg" clickedFunc: function () { tabBarStackView.goToTabBarPage(PageEnum.PageHome) ServersModel.processedIndex = ServersModel.defaultIndex tabBar.currentIndex = 0 + FocusController.setRootItem(null) // TODO: move to do it automaticaly } - KeyNavigation.tab: shareTabButton - Keys.onEnterPressed: this.clicked() - Keys.onReturnPressed: this.clicked() + // KeyNavigation.tab: shareTabButton + // Keys.onEnterPressed: this.clicked() + // Keys.onReturnPressed: this.clicked() } TabImageButtonType { id: shareTabButton + objectName: "shareTabButton" Connections { target: ServersModel @@ -325,11 +341,13 @@ PageType { tabBar.currentIndex = 1 } - KeyNavigation.tab: settingsTabButton + // KeyNavigation.tab: settingsTabButton } TabImageButtonType { id: settingsTabButton + objectName: "settingsTabButton" + isSelected: tabBar.currentIndex === 2 image: "qrc:/images/controls/settings-2.svg" clickedFunc: function () { @@ -337,11 +355,13 @@ PageType { tabBar.currentIndex = 2 } - KeyNavigation.tab: plusTabButton + // KeyNavigation.tab: plusTabButton } TabImageButtonType { id: plusTabButton + objectName: "plusTabButton" + isSelected: tabBar.currentIndex === 3 image: "qrc:/images/controls/plus.svg" clickedFunc: function () { @@ -349,7 +369,7 @@ PageType { tabBar.currentIndex = 3 } - Keys.onTabPressed: PageController.forceStackActiveFocus() + // Keys.onTabPressed: PageController.forceStackActiveFocus() } } } diff --git a/client/ui/qml/main2.qml b/client/ui/qml/main2.qml index fb99559f..18b69795 100644 --- a/client/ui/qml/main2.qml +++ b/client/ui/qml/main2.qml @@ -33,6 +33,8 @@ Window { title: "AmneziaVPN" Connections { + objectName: "pageControllerConnections" + target: PageController function onRaiseMainWindow() { @@ -72,6 +74,8 @@ Window { } Connections { + objectName: "settingsControllerConnections" + target: SettingsController function onChangeSettingsFinished(finishedMessage) { @@ -80,11 +84,15 @@ Window { } PageStart { + objectName: "pageStart" + width: root.width height: root.height } Item { + objectName: "popupNotificationItem" + anchors.right: parent.right anchors.left: parent.left anchors.bottom: parent.bottom @@ -108,6 +116,8 @@ Window { } Item { + objectName: "popupErrorMessageItem" + anchors.right: parent.right anchors.left: parent.left anchors.bottom: parent.bottom @@ -120,6 +130,8 @@ Window { } Item { + objectName: "privateKeyPassphraseDrawerItem" + anchors.fill: parent DrawerType2 { @@ -128,7 +140,7 @@ Window { anchors.fill: parent expandedHeight: root.height * 0.35 - expandedContent: ColumnLayout { + expandedStateContent: ColumnLayout { anchors.top: parent.top anchors.left: parent.left anchors.right: parent.right @@ -168,7 +180,7 @@ Window { hidePassword = !hidePassword } - KeyNavigation.tab: saveButton + // KeyNavigation.tab: saveButton } BasicButtonType { @@ -195,6 +207,8 @@ Window { } Item { + objectName: "questionDrawerItem" + anchors.fill: parent QuestionDrawer { @@ -205,6 +219,8 @@ Window { } Item { + objectName: "busyIndicatorItem" + anchors.fill: parent BusyIndicatorType { @@ -221,26 +237,26 @@ Window { questionDrawer.noButtonText = noButtonText questionDrawer.yesButtonFunction = function() { - questionDrawer.close() + questionDrawer.closeTriggered() if (yesButtonFunction && typeof yesButtonFunction === "function") { yesButtonFunction() } } questionDrawer.noButtonFunction = function() { - questionDrawer.close() + questionDrawer.closeTriggered() if (noButtonFunction && typeof noButtonFunction === "function") { noButtonFunction() } } - questionDrawer.open() + questionDrawer.openTriggered() } FileDialog { id: mainFileDialog + objectName: "mainFileDialog" property bool isSaveMode: false - objectName: "mainFileDialog" fileMode: isSaveMode ? FileDialog.SaveFile : FileDialog.OpenFile onAccepted: SystemController.fileDialogClosed(true) From cecee3769e20379ca6959a7900fa7b63cb51f0e8 Mon Sep 17 00:00:00 2001 From: Cyril Anisimov Date: Thu, 19 Sep 2024 15:00:01 +0200 Subject: [PATCH 02/40] add more key handlers --- client/ui/controllers/focusController.cpp | 54 +++++++------------ client/ui/qml/Components/ConnectButton.qml | 16 ++++++ .../qml/Components/HomeContainersListView.qml | 8 +++ .../Components/HomeSplitTunnelingDrawer.qml | 12 +---- client/ui/qml/Controls2/BasicButtonType.qml | 20 +++++-- client/ui/qml/Controls2/FlickableType.qml | 1 + .../qml/Controls2/HorizontalRadioButton.qml | 18 ++++++- client/ui/qml/Controls2/ImageButtonType.qml | 16 ++++++ .../ui/qml/Controls2/LabelWithButtonType.qml | 50 +++++++++++------ .../Controls2/ListViewWithRadioButtonType.qml | 16 ++++++ client/ui/qml/Controls2/SwitcherType.qml | 17 ++++++ client/ui/qml/Controls2/TabButtonType.qml | 16 ++++++ .../ui/qml/Controls2/TabImageButtonType.qml | 30 ++++++++--- .../ui/qml/Controls2/VerticalRadioButton.qml | 20 +++++++ client/ui/qml/Pages2/PageHome.qml | 10 ++++ 15 files changed, 228 insertions(+), 76 deletions(-) diff --git a/client/ui/controllers/focusController.cpp b/client/ui/controllers/focusController.cpp index 58e33c7e..7e2f79d3 100644 --- a/client/ui/controllers/focusController.cpp +++ b/client/ui/controllers/focusController.cpp @@ -18,7 +18,6 @@ bool isVisible(QObject* item) bool isFocusable(QObject* item) { const auto res = item->property("isFocusable").toBool(); - // qDebug() << "==>> " << (res ? "FOCUSABLE" : "NOT focusable") << item; return res; } @@ -60,28 +59,23 @@ bool isOnTheScene(QObject* object) return false; } - QRectF itemRect{}; // TODO: ListView couln't get into list because it's children's rect is too large - // if (isListView(item)) { - // itemRect = QRectF(item->x(), item->y(), item->width(), item->height()); - // } else { - itemRect = item->mapRectToScene(item->childrenRect()); - // } + QRectF itemRect = item->mapRectToScene(item->childrenRect()); + QQuickWindow* window = item->window(); if (!window) { qWarning() << "Couldn't get the window on the Scene check"; return false; } - // const auto contentItem = window->contentItem(); - // if (!contentItem) { - // qWarning() << "Couldn't get the content item on the Scene check"; - // return false; - // } - // QRectF windowRect = contentItem->childrenRect(); - // const auto res = (windowRect.contains(itemRect) || isListView(item)); - // // qDebug() << (res ? "===>> item is inside the Scene" : "===>> ITEM IS OUTSIDE THE SCENE") << " itemRect: " << itemRect << "; windowRect: " << windowRect; - // return res; - return true; + const auto contentItem = window->contentItem(); + if (!contentItem) { + qWarning() << "Couldn't get the content item on the Scene check"; + return false; + } + QRectF windowRect = contentItem->childrenRect(); + const auto res = (windowRect.contains(itemRect) || isListView(item)); + // qDebug() << (res ? "===>> item is inside the Scene" : "===>> ITEM IS OUTSIDE THE SCENE") << " itemRect: " << itemRect << "; windowRect: " << windowRect; + return res; } bool isEnabled(QObject* obj) @@ -98,7 +92,6 @@ QQuickItem* getPageOfItem(QQuickItem* item) // TODO: remove? } const auto pagePattern = QString::fromLatin1("Page"); QString className{item->metaObject()->className()}; - qDebug() << "=====================>> Item: " << item << " with name: " << item->metaObject()->className(); const auto isPage = className.contains(pagePattern, Qt::CaseSensitive); if(isPage) { return item; @@ -122,9 +115,7 @@ QList getSubChain(QObject* item) && isEnabled(child) ) { res.append(child); - // qDebug() << "==>> [*** added ***] " << qobject_cast(child); } else { - // qDebug() << "==>> [** skipped **] " << qobject_cast(child); res.append(getSubChain(child)); } } @@ -134,14 +125,12 @@ QList getSubChain(QObject* item) template void printItems(const T& items, QObject* current_item) { - qDebug() << "**********************************************"; for(const auto& item : items) { QQuickItem* i = qobject_cast(item); QPointF coords {getItemCenterPointOnScene(i)}; QString prefix = current_item == i ? "==>" : " "; - qDebug() << prefix << " Item: " << i << " with coords: " << coords; + // qDebug() << prefix << " Item: " << i << " with coords: " << coords; // Uncomment to visualize tab transitions } - qDebug() << "**********************************************"; } /*! @@ -308,7 +297,7 @@ void FocusController::resetFocus() void FocusController::nextKeyTabItem() { if (m_lvfc) { - focusNextListViewItem(); // Need to go on first element by default? + focusNextListViewItem(); return; } @@ -335,7 +324,6 @@ void FocusController::nextKeyTabItem() } if(isListView(m_focusedItem)) { - qDebug() << "===>> Found ListView Item: " << m_focusedItem; // TODO: remove? m_lvfc = new ListViewFocusController(m_focusedItem, this); focusNextListViewItem(); return; @@ -387,22 +375,22 @@ void FocusController::previousKeyTabItem() void FocusController::nextKeyUpItem() { - qDebug() << "nextKeyUpItem" << "triggered"; + previousKeyTabItem(); } void FocusController::nextKeyDownItem() { - qDebug() << "nextKeyDownItem" << "triggered"; + nextKeyTabItem(); } void FocusController::nextKeyLeftItem() { - qDebug() << "nextKeyLeftItem" << "triggered"; + previousKeyTabItem(); } void FocusController::nextKeyRightItem() { - qDebug() << "nextKeyRightItem" << "triggered"; + nextKeyTabItem(); } void FocusController::reload() @@ -414,10 +402,8 @@ void FocusController::reload() const auto rootItem = m_rootItem; if (rootItem != nullptr) { - qDebug() << "*** root item: " << rootItem; rootObjects << qobject_cast(rootItem); } else { - qDebug() << "*** root item is null"; rootObjects = m_engine->rootObjects(); } @@ -453,14 +439,10 @@ void FocusController::reload() return; } - // qDebug() << "==> Active Focused Item: " << window->activeFocusItem(); - // qDebug() << "--> Active Focused Object: " << window->focusObject(); - // qDebug() << ">>> Current Focused Item: " << m_focused_item; - m_focusedItemIndex = m_focusChain.indexOf(window->activeFocusItem()); if(m_focusedItemIndex == -1) { - qDebug() << "===>> No focus item in chain. Moving focus to begin..."; + qInfo() << "No focus item in chain. Moving focus to begin..."; // m_focused_item_index = 0; // if not in focus chain current return; } diff --git a/client/ui/qml/Components/ConnectButton.qml b/client/ui/qml/Components/ConnectButton.qml index c3032eab..b90891a0 100644 --- a/client/ui/qml/Components/ConnectButton.qml +++ b/client/ui/qml/Components/ConnectButton.qml @@ -25,7 +25,23 @@ Button { Keys.onBacktabPressed: { FocusController.previousKeyTabItem() } + + Keys.onUpPressed: { + FocusController.nextKeyUpItem() + } + Keys.onDownPressed: { + FocusController.nextKeyDownItem() + } + + Keys.onLeftPressed: { + FocusController.nextKeyLeftItem() + } + + Keys.onRightPressed: { + FocusController.nextKeyRightItem() + } + implicitWidth: 190 implicitHeight: 190 diff --git a/client/ui/qml/Components/HomeContainersListView.qml b/client/ui/qml/Components/HomeContainersListView.qml index 9c3d3857..5d3569f5 100644 --- a/client/ui/qml/Components/HomeContainersListView.qml +++ b/client/ui/qml/Components/HomeContainersListView.qml @@ -46,6 +46,14 @@ ListView { FocusController.previousKeyTabItem() } + Keys.onRightPressed: { + FocusController.nextKeyTabItem() + } + + Keys.onLeftPressed: { + FocusController.previousKeyTabItem() + } + // activeFocusOnTab: true // onActiveFocusChanged: { // console.log("===========================") diff --git a/client/ui/qml/Components/HomeSplitTunnelingDrawer.qml b/client/ui/qml/Components/HomeSplitTunnelingDrawer.qml index b37b0b81..67696c33 100644 --- a/client/ui/qml/Components/HomeSplitTunnelingDrawer.qml +++ b/client/ui/qml/Components/HomeSplitTunnelingDrawer.qml @@ -46,11 +46,9 @@ 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() + PageController.goToPage(PageEnum.PageSettingsSplitTunneling) + root.close() } } @@ -67,10 +65,6 @@ 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.closeTriggered() @@ -90,8 +84,6 @@ 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.closeTriggered() diff --git a/client/ui/qml/Controls2/BasicButtonType.qml b/client/ui/qml/Controls2/BasicButtonType.qml index e925035b..2f1e3fbb 100644 --- a/client/ui/qml/Controls2/BasicButtonType.qml +++ b/client/ui/qml/Controls2/BasicButtonType.qml @@ -38,14 +38,28 @@ Button { property bool isFocusable: true Keys.onTabPressed: { - console.debug("--> Tab is pressed on BasicButtonType: ", objectName) FocusController.nextKeyTabItem() } - + Keys.onBacktabPressed: { - console.debug("--> Shift+Tab is pressed on ", objectName) FocusController.previousKeyTabItem() } + + Keys.onUpPressed: { + FocusController.nextKeyUpItem() + } + + Keys.onDownPressed: { + FocusController.nextKeyDownItem() + } + + Keys.onLeftPressed: { + FocusController.nextKeyLeftItem() + } + + Keys.onRightPressed: { + FocusController.nextKeyRightItem() + } implicitHeight: 56 diff --git a/client/ui/qml/Controls2/FlickableType.qml b/client/ui/qml/Controls2/FlickableType.qml index db857ea9..2151e520 100644 --- a/client/ui/qml/Controls2/FlickableType.qml +++ b/client/ui/qml/Controls2/FlickableType.qml @@ -11,6 +11,7 @@ Flickable { } else if (item.y + item.height > fl.contentY + fl.height) { fl.contentY = item.y + item.height - fl.height + 40 // 40 is a bottom margin } + fl.returnToBounds() } clip: true diff --git a/client/ui/qml/Controls2/HorizontalRadioButton.qml b/client/ui/qml/Controls2/HorizontalRadioButton.qml index b87d9b84..89cc1658 100644 --- a/client/ui/qml/Controls2/HorizontalRadioButton.qml +++ b/client/ui/qml/Controls2/HorizontalRadioButton.qml @@ -30,15 +30,29 @@ RadioButton { property bool isFocusable: true Keys.onTabPressed: { - console.debug("--> Tab is pressed on BasicButtonType: ", objectName) FocusController.nextKeyTabItem() } Keys.onBacktabPressed: { - console.debug("--> Shift+Tab is pressed on ", objectName) FocusController.previousKeyTabItem() } + Keys.onUpPressed: { + FocusController.nextKeyUpItem() + } + + Keys.onDownPressed: { + FocusController.nextKeyDownItem() + } + + Keys.onLeftPressed: { + FocusController.nextKeyLeftItem() + } + + Keys.onRightPressed: { + FocusController.nextKeyRightItem() + } + indicator: Rectangle { anchors.fill: parent radius: 16 diff --git a/client/ui/qml/Controls2/ImageButtonType.qml b/client/ui/qml/Controls2/ImageButtonType.qml index 8770f454..d5f646a7 100644 --- a/client/ui/qml/Controls2/ImageButtonType.qml +++ b/client/ui/qml/Controls2/ImageButtonType.qml @@ -38,6 +38,22 @@ Button { FocusController.previousKeyTabItem() } + Keys.onUpPressed: { + FocusController.nextKeyUpItem() + } + + Keys.onDownPressed: { + FocusController.nextKeyDownItem() + } + + Keys.onLeftPressed: { + FocusController.nextKeyLeftItem() + } + + Keys.onRightPressed: { + FocusController.nextKeyRightItem() + } + Keys.onEnterPressed: root.clicked() Keys.onReturnPressed: root.clicked() diff --git a/client/ui/qml/Controls2/LabelWithButtonType.qml b/client/ui/qml/Controls2/LabelWithButtonType.qml index 5e05c30e..087415f7 100644 --- a/client/ui/qml/Controls2/LabelWithButtonType.qml +++ b/client/ui/qml/Controls2/LabelWithButtonType.qml @@ -50,28 +50,44 @@ Item { Keys.onBacktabPressed: { FocusController.previousKeyTabItem() } + + Keys.onUpPressed: { + FocusController.nextKeyUpItem() + } + + Keys.onDownPressed: { + FocusController.nextKeyDownItem() + } + + Keys.onLeftPressed: { + FocusController.nextKeyLeftItem() + } + + Keys.onRightPressed: { + FocusController.nextKeyRightItem() + } 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) - // } - // } - // } + 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) - // } - // } - // } - // } + Connections { + target: rightImage + function onFocusChanged() { + if (rightImage.activeFocus) { + if (root.parentFlickable) { + root.parentFlickable.ensureVisible(root) + } + } + } + } MouseArea { anchors.fill: parent diff --git a/client/ui/qml/Controls2/ListViewWithRadioButtonType.qml b/client/ui/qml/Controls2/ListViewWithRadioButtonType.qml index ed5fa6d9..9024af91 100644 --- a/client/ui/qml/Controls2/ListViewWithRadioButtonType.qml +++ b/client/ui/qml/Controls2/ListViewWithRadioButtonType.qml @@ -43,6 +43,22 @@ ListView { FocusController.previousKeyTabItem() } + Keys.onUpPressed: { + FocusController.nextKeyUpItem() + } + + Keys.onDownPressed: { + FocusController.nextKeyDownItem() + } + + Keys.onLeftPressed: { + FocusController.nextKeyLeftItem() + } + + Keys.onRightPressed: { + FocusController.nextKeyRightItem() + } + ButtonGroup { id: buttonGroup } diff --git a/client/ui/qml/Controls2/SwitcherType.qml b/client/ui/qml/Controls2/SwitcherType.qml index b96f40e4..3569a7d0 100644 --- a/client/ui/qml/Controls2/SwitcherType.qml +++ b/client/ui/qml/Controls2/SwitcherType.qml @@ -45,10 +45,27 @@ Switch { FocusController.previousKeyTabItem() } + Keys.onUpPressed: { + FocusController.nextKeyUpItem() + } + + Keys.onDownPressed: { + FocusController.nextKeyDownItem() + } + + Keys.onLeftPressed: { + FocusController.nextKeyLeftItem() + } + + Keys.onRightPressed: { + FocusController.nextKeyRightItem() + } + hoverEnabled: enabled ? true : false focusPolicy: Qt.TabFocus property FlickableType parentFlickable: null + onFocusChanged: { if (root.activeFocus) { if (root.parentFlickable) { diff --git a/client/ui/qml/Controls2/TabButtonType.qml b/client/ui/qml/Controls2/TabButtonType.qml index d3dc0d1b..0e48d975 100644 --- a/client/ui/qml/Controls2/TabButtonType.qml +++ b/client/ui/qml/Controls2/TabButtonType.qml @@ -26,6 +26,22 @@ TabButton { Keys.onBacktabPressed: { FocusController.previousKeyTabItem() } + + Keys.onUpPressed: { + FocusController.nextKeyUpItem() + } + + Keys.onDownPressed: { + FocusController.nextKeyDownItem() + } + + Keys.onLeftPressed: { + FocusController.nextKeyLeftItem() + } + + Keys.onRightPressed: { + FocusController.nextKeyRightItem() + } implicitHeight: 48 diff --git a/client/ui/qml/Controls2/TabImageButtonType.qml b/client/ui/qml/Controls2/TabImageButtonType.qml index 3e0b5aed..b49ad8eb 100644 --- a/client/ui/qml/Controls2/TabImageButtonType.qml +++ b/client/ui/qml/Controls2/TabImageButtonType.qml @@ -16,13 +16,29 @@ TabButton { property bool isFocusable: true - Keys.onTabPressed: { - FocusController.nextKeyTabItem() - } + Keys.onTabPressed: { + FocusController.nextKeyTabItem() + } - Keys.onBacktabPressed: { - FocusController.previousKeyTabItem() - } + Keys.onBacktabPressed: { + FocusController.previousKeyTabItem() + } + + Keys.onUpPressed: { + FocusController.nextKeyUpItem() + } + + Keys.onDownPressed: { + FocusController.nextKeyDownItem() + } + + Keys.onLeftPressed: { + FocusController.nextKeyLeftItem() + } + + Keys.onRightPressed: { + FocusController.nextKeyRightItem() + } property string borderFocusedColor: AmneziaStyle.color.paleGray property int borderFocusedWidth: 1 @@ -30,7 +46,6 @@ TabButton { property var clickedFunc hoverEnabled: true - // focusPolicy: Qt.TabFocus icon.source: image icon.color: isSelected ? selectedColor : defaultColor @@ -53,7 +68,6 @@ TabButton { } Keys.onEnterPressed: { - console.log("$$$$$$$$$ ENTER PRESSED INSIDE TABIMAGEBUTTONTYPE") if (root.clickedFunc && typeof root.clickedFunc === "function") { root.clickedFunc() } diff --git a/client/ui/qml/Controls2/VerticalRadioButton.qml b/client/ui/qml/Controls2/VerticalRadioButton.qml index 4320103b..dcf0f1d9 100644 --- a/client/ui/qml/Controls2/VerticalRadioButton.qml +++ b/client/ui/qml/Controls2/VerticalRadioButton.qml @@ -34,6 +34,26 @@ RadioButton { FocusController.nextKeyTabItem() } + Keys.onBacktabPressed: { + FocusController.previousKeyTabItem() + } + + Keys.onUpPressed: { + FocusController.nextKeyUpItem() + } + + Keys.onDownPressed: { + FocusController.nextKeyDownItem() + } + + Keys.onLeftPressed: { + FocusController.nextKeyLeftItem() + } + + Keys.onRightPressed: { + FocusController.nextKeyRightItem() + } + hoverEnabled: true // focusPolicy: Qt.TabFocus diff --git a/client/ui/qml/Pages2/PageHome.qml b/client/ui/qml/Pages2/PageHome.qml index aa2f0501..2d52b044 100644 --- a/client/ui/qml/Pages2/PageHome.qml +++ b/client/ui/qml/Pages2/PageHome.qml @@ -410,6 +410,16 @@ PageType { ServersListView { id: serversMenuContent objectName: "serversMenuContent" + + isFocusable: false + + Connections { + target: drawer + + function onIsOpenedChanged() { + serversMenuContent.isFocusable = drawer.isOpened + } + } } } } From 01e31b4b4d6e137653307cc95a15b213f8dc79ba Mon Sep 17 00:00:00 2001 From: Cyril Anisimov Date: Thu, 19 Sep 2024 20:27:48 +0200 Subject: [PATCH 03/40] add focus navigation to qml --- client/ui/controllers/focusController.cpp | 21 ++++-- client/ui/qml/Components/QuestionDrawer.qml | 4 +- .../Components/SettingsContainersListView.qml | 16 ++--- client/ui/qml/Controls2/BackButtonType.qml | 12 ---- client/ui/qml/Controls2/BasicButtonType.qml | 1 - client/ui/qml/Controls2/CardWithIconsType.qml | 18 +++++ client/ui/qml/Controls2/PageType.qml | 3 +- client/ui/qml/Controls2/PopupType.qml | 4 +- .../qml/Controls2/TextFieldWithHeaderType.qml | 3 +- .../Pages2/PageProtocolAwgClientSettings.qml | 2 - .../qml/Pages2/PageProtocolCloakSettings.qml | 1 - client/ui/qml/Pages2/PageSettings.qml | 3 +- client/ui/qml/Pages2/PageSettingsAbout.qml | 12 ---- .../ui/qml/Pages2/PageSettingsApplication.qml | 13 ---- client/ui/qml/Pages2/PageSettingsBackup.qml | 8 +-- .../ui/qml/Pages2/PageSettingsConnection.qml | 28 +++----- client/ui/qml/Pages2/PageSettingsLogging.qml | 20 +++--- .../ui/qml/Pages2/PageSettingsServersList.qml | 24 +------ .../Pages2/PageSetupWizardApiServicesList.qml | 5 ++ .../Pages2/PageSetupWizardConfigSource.qml | 67 +++---------------- .../qml/Pages2/PageSetupWizardCredentials.qml | 12 +++- .../qml/Pages2/PageSetupWizardViewConfig.qml | 2 + client/ui/qml/Pages2/PageStart.qml | 20 ------ 23 files changed, 103 insertions(+), 196 deletions(-) diff --git a/client/ui/controllers/focusController.cpp b/client/ui/controllers/focusController.cpp index 7e2f79d3..3f5d042f 100644 --- a/client/ui/controllers/focusController.cpp +++ b/client/ui/controllers/focusController.cpp @@ -41,6 +41,11 @@ bool isLess(QObject* item1, QObject* item2) return (p1.y() == p2.y()) ? (p1.x() < p2.x()) : (p1.y() < p2.y()); } +bool isMore(QObject* item1, QObject* item2) +{ + return !isLess(item1, item2); +} + bool isListView(QObject* item) { return item->inherits("QQuickListView"); @@ -129,7 +134,7 @@ void printItems(const T& items, QObject* current_item) QQuickItem* i = qobject_cast(item); QPointF coords {getItemCenterPointOnScene(i)}; QString prefix = current_item == i ? "==>" : " "; - // qDebug() << prefix << " Item: " << i << " with coords: " << coords; // Uncomment to visualize tab transitions + qDebug() << prefix << " Item: " << i << " with coords: " << coords; } } @@ -236,12 +241,12 @@ QQuickItem* ListViewFocusController::focusedItem() void ListViewFocusController::focusNextItem() { if (m_focusChain.empty()) { - qWarning() << "Empty focusChain with current delegate: " << currentDelegate(); + qDebug() << "Empty focusChain with current delegate: " << currentDelegate(); m_focusChain = getSubChain(currentDelegate()); } m_focusedItemIndex++; m_focusedItem = qobject_cast(m_focusChain.at(m_focusedItemIndex)); - m_focusedItem->forceActiveFocus(); + m_focusedItem->forceActiveFocus(); } void ListViewFocusController::focusPreviousItem() @@ -287,6 +292,7 @@ void FocusController::resetFocus() qWarning() << "There is no focusable elements"; return; } + if(m_focusedItemIndex == -1) { m_focusedItemIndex = 0; m_focusedItem = qobject_cast(m_focusChain.at(m_focusedItemIndex)); @@ -370,7 +376,7 @@ void FocusController::previousKeyTabItem() m_focusedItem = qobject_cast(m_focusChain.at(m_focusedItemIndex)); m_focusedItem->forceActiveFocus(Qt::TabFocusReason); - qDebug() << "--> Current focus was changed to " << m_focusedItem; + qDebug() << "===>> Current focus was changed to " << m_focusedItem; } void FocusController::nextKeyUpItem() @@ -439,7 +445,8 @@ void FocusController::reload() return; } - m_focusedItemIndex = m_focusChain.indexOf(window->activeFocusItem()); + m_focusedItemIndex = m_focusChain.indexOf(m_focusedItem); + // m_focusedItemIndex = m_focusChain.indexOf(window->activeFocusItem()); if(m_focusedItemIndex == -1) { qInfo() << "No focus item in chain. Moving focus to begin..."; @@ -447,9 +454,9 @@ void FocusController::reload() return; } - m_focusedItem = qobject_cast(m_focusChain.at(m_focusedItemIndex)); + // m_focusedItem = qobject_cast(m_focusChain.at(m_focusedItemIndex)); - m_focusedItem->forceActiveFocus(); + // m_focusedItem->forceActiveFocus(); } void FocusController::setRootItem(QQuickItem* item) diff --git a/client/ui/qml/Components/QuestionDrawer.qml b/client/ui/qml/Components/QuestionDrawer.qml index 09e8638a..0dbd2eab 100644 --- a/client/ui/qml/Components/QuestionDrawer.qml +++ b/client/ui/qml/Components/QuestionDrawer.qml @@ -37,11 +37,11 @@ DrawerType2 { target: root enabled: !GC.isMobile() function onOpened() { - FocusController.setRoot(root) + FocusController.setRootItem(root) } function onClosed() { - FocusController.setRoot(null) + FocusController.setRootItem(null) } } diff --git a/client/ui/qml/Components/SettingsContainersListView.qml b/client/ui/qml/Components/SettingsContainersListView.qml index 769e1abd..d6db36af 100644 --- a/client/ui/qml/Components/SettingsContainersListView.qml +++ b/client/ui/qml/Components/SettingsContainersListView.qml @@ -23,14 +23,14 @@ ListView { interactive: false activeFocusOnTab: true - Keys.onTabPressed: { - if (currentIndex < this.count - 1) { - this.incrementCurrentIndex() - } else { - currentIndex = 0 - lastItemTabClickedSignal() - } - } + // Keys.onTabPressed: { + // if (currentIndex < this.count - 1) { + // this.incrementCurrentIndex() + // } else { + // currentIndex = 0 + // lastItemTabClickedSignal() + // } + // } onCurrentIndexChanged: { if (visible) { diff --git a/client/ui/qml/Controls2/BackButtonType.qml b/client/ui/qml/Controls2/BackButtonType.qml index 41aac9e7..40136ad5 100644 --- a/client/ui/qml/Controls2/BackButtonType.qml +++ b/client/ui/qml/Controls2/BackButtonType.qml @@ -10,16 +10,6 @@ FocusScope { property string backButtonImage: "qrc:/images/controls/arrow-left.svg" property var backButtonFunction - // property bool isFocusable: true - - // Keys.onTabPressed: { - // FocusController.nextKeyTabItem() - // } - - // Keys.onBacktabPressed: { - // FocusController.previousKeyTabItem() - // } - implicitWidth: content.implicitWidth implicitHeight: content.implicitHeight @@ -39,8 +29,6 @@ FocusScope { implicitWidth: 40 implicitHeight: 40 - // focus: true - onClicked: { if (backButtonFunction && typeof backButtonFunction === "function") { backButtonFunction() diff --git a/client/ui/qml/Controls2/BasicButtonType.qml b/client/ui/qml/Controls2/BasicButtonType.qml index 2f1e3fbb..aa8103e9 100644 --- a/client/ui/qml/Controls2/BasicButtonType.qml +++ b/client/ui/qml/Controls2/BasicButtonType.qml @@ -67,7 +67,6 @@ Button { focusPolicy: Qt.TabFocus onFocusChanged: { - console.debug("===>> BUTTON: active.focus: ", root.activeFocus, " parentFlickable: ", root.parentFlickable ) if (root.activeFocus) { if (root.parentFlickable) { root.parentFlickable.ensureVisible(this) diff --git a/client/ui/qml/Controls2/CardWithIconsType.qml b/client/ui/qml/Controls2/CardWithIconsType.qml index f5d2bea2..482b5217 100644 --- a/client/ui/qml/Controls2/CardWithIconsType.qml +++ b/client/ui/qml/Controls2/CardWithIconsType.qml @@ -27,6 +27,8 @@ Button { property alias focusItem: rightImage + property FlickableType parentFlickable + hoverEnabled: true background: Rectangle { @@ -42,6 +44,22 @@ Button { } } + function ensureVisible(item) { + if (item.activeFocus) { + if (root.parentFlickable) { + root.parentFlickable.ensureVisible(root) + } + } + } + + onFocusChanged: { + ensureVisible(root) + } + + focusItem.onFocusChanged: { + root.ensureVisible(focusItem) + } + contentItem: Item { anchors.left: parent.left anchors.right: parent.right diff --git a/client/ui/qml/Controls2/PageType.qml b/client/ui/qml/Controls2/PageType.qml index abe7c8d6..977c18ba 100644 --- a/client/ui/qml/Controls2/PageType.qml +++ b/client/ui/qml/Controls2/PageType.qml @@ -31,9 +31,8 @@ Item { // Set a timer to set focus after a short delay Timer { id: timer - interval: 1000 // Milliseconds // TODO: return to 500 + interval: 500 // Milliseconds onTriggered: { - console.debug("===>> Page creation completed") FocusController.resetFocus() } repeat: false // Stop the timer after one trigger diff --git a/client/ui/qml/Controls2/PopupType.qml b/client/ui/qml/Controls2/PopupType.qml index e5019fe5..5bed7350 100644 --- a/client/ui/qml/Controls2/PopupType.qml +++ b/client/ui/qml/Controls2/PopupType.qml @@ -28,11 +28,11 @@ Popup { } onOpened: { - FocusController.setRoot(root) + FocusController.setRootItem(root) } onClosed: { - FocusController.setRoot(null) + FocusController.setRootItem(null) } background: Rectangle { diff --git a/client/ui/qml/Controls2/TextFieldWithHeaderType.qml b/client/ui/qml/Controls2/TextFieldWithHeaderType.qml index 875eed9f..fbea618b 100644 --- a/client/ui/qml/Controls2/TextFieldWithHeaderType.qml +++ b/client/ui/qml/Controls2/TextFieldWithHeaderType.qml @@ -40,6 +40,7 @@ Item { implicitHeight: content.implicitHeight property FlickableType parentFlickable + Connections { target: textField function onFocusChanged() { @@ -84,7 +85,7 @@ Item { TextField { id: textField - // activeFocusOnTab: false + property bool isFocusable: true Keys.onTabPressed: { diff --git a/client/ui/qml/Pages2/PageProtocolAwgClientSettings.qml b/client/ui/qml/Pages2/PageProtocolAwgClientSettings.qml index 2b912f18..8685a954 100644 --- a/client/ui/qml/Pages2/PageProtocolAwgClientSettings.qml +++ b/client/ui/qml/Pages2/PageProtocolAwgClientSettings.qml @@ -174,7 +174,6 @@ PageType { checkEmptyText: true - Keys.onTabPressed: saveButton.forceActiveFocus() } Header2TextType { @@ -283,7 +282,6 @@ PageType { text: qsTr("Save") - Keys.onTabPressed: lastItemTabClicked(focusItem) clickedFunc: function() { forceActiveFocus() diff --git a/client/ui/qml/Pages2/PageProtocolCloakSettings.qml b/client/ui/qml/Pages2/PageProtocolCloakSettings.qml index 29df03ef..c0dbc2ee 100644 --- a/client/ui/qml/Pages2/PageProtocolCloakSettings.qml +++ b/client/ui/qml/Pages2/PageProtocolCloakSettings.qml @@ -171,7 +171,6 @@ PageType { Layout.bottomMargin: 24 text: qsTr("Save") - Keys.onTabPressed: lastItemTabClicked(focusItem) clickedFunc: function() { forceActiveFocus() diff --git a/client/ui/qml/Pages2/PageSettings.qml b/client/ui/qml/Pages2/PageSettings.qml index 6e291ff0..65c696c7 100644 --- a/client/ui/qml/Pages2/PageSettings.qml +++ b/client/ui/qml/Pages2/PageSettings.qml @@ -14,7 +14,7 @@ import "../Config" PageType { id: root - FlickableType { // TODO: refactor either replace with ListView or Repeater + FlickableType { id: fl anchors.top: parent.top anchors.bottom: parent.bottom @@ -142,7 +142,6 @@ PageType { text: qsTr("Close application") rightImageSource: "qrc:/images/controls/chevron-right.svg" leftImageSource: "qrc:/images/controls/x-circle.svg" - // isLeftImageHoverEnabled: false clickedFunction: function() { PageController.closeApplication() diff --git a/client/ui/qml/Pages2/PageSettingsAbout.qml b/client/ui/qml/Pages2/PageSettingsAbout.qml index 5407e75e..7de813e3 100644 --- a/client/ui/qml/Pages2/PageSettingsAbout.qml +++ b/client/ui/qml/Pages2/PageSettingsAbout.qml @@ -14,17 +14,6 @@ import "../Components" PageType { id: root - // Item { - // id: focusItem - // KeyNavigation.tab: backButton - - // onFocusChanged: { - // if (focusItem.activeFocus) { - // fl.contentY = 0 - // } - // } - // } - BackButtonType { id: backButton @@ -223,7 +212,6 @@ PageType { text: qsTr("Privacy Policy") - Keys.onTabPressed: lastItemTabClicked() parentFlickable: fl clickedFunc: function() { diff --git a/client/ui/qml/Pages2/PageSettingsApplication.qml b/client/ui/qml/Pages2/PageSettingsApplication.qml index dfccabcf..4dea8c65 100644 --- a/client/ui/qml/Pages2/PageSettingsApplication.qml +++ b/client/ui/qml/Pages2/PageSettingsApplication.qml @@ -14,16 +14,6 @@ import "../Components" PageType { id: root - // Item { - // id: focusItem - - // onFocusChanged: { - // if (focusItem.activeFocus) { - // fl.contentY = 0 - // } - // } - // } - BackButtonType { id: backButton @@ -31,8 +21,6 @@ PageType { anchors.left: parent.left anchors.right: parent.right anchors.topMargin: 20 - - // KeyNavigation.tab: GC.isMobile() ? switcher : switcherAutoStart } FlickableType { @@ -92,7 +80,6 @@ PageType { descriptionText: qsTr("Enable notifications to show the VPN state in the status bar") rightImageSource: "qrc:/images/controls/chevron-right.svg" - // KeyNavigation.tab: labelWithButtonLanguage.rightButton parentFlickable: fl clickedFunction: function() { diff --git a/client/ui/qml/Pages2/PageSettingsBackup.qml b/client/ui/qml/Pages2/PageSettingsBackup.qml index 35d45589..d2dd4f2a 100644 --- a/client/ui/qml/Pages2/PageSettingsBackup.qml +++ b/client/ui/qml/Pages2/PageSettingsBackup.qml @@ -17,8 +17,6 @@ import "../Controls2/TextTypes" PageType { id: root - // defaultActiveFocusItem: focusItem - Connections { target: SettingsController @@ -86,6 +84,8 @@ PageType { text: qsTr("Make a backup") + parentFlickable: fl + clickedFunc: function() { var fileName = "" if (GC.isMobile()) { @@ -120,6 +120,8 @@ PageType { text: qsTr("Restore from backup") + parentFlickable: fl + clickedFunc: function() { var filePath = SystemController.getFileName(qsTr("Open backup file"), qsTr("Backup files (*.backup)")) @@ -127,8 +129,6 @@ PageType { restoreBackup(filePath) } } - - Keys.onTabPressed: lastItemTabClicked() } } } diff --git a/client/ui/qml/Pages2/PageSettingsConnection.qml b/client/ui/qml/Pages2/PageSettingsConnection.qml index 5991b713..d3743b96 100644 --- a/client/ui/qml/Pages2/PageSettingsConnection.qml +++ b/client/ui/qml/Pages2/PageSettingsConnection.qml @@ -70,6 +70,8 @@ PageType { descriptionText: qsTr("When AmneziaDNS is not used or installed") rightImageSource: "qrc:/images/controls/chevron-right.svg" + parentFlickable: fl + clickedFunction: function() { PageController.goToPage(PageEnum.PageSettingsDns) } @@ -85,19 +87,11 @@ PageType { descriptionText: qsTr("Allows you to select which sites you want to access through the VPN") rightImageSource: "qrc:/images/controls/chevron-right.svg" + parentFlickable: fl + clickedFunction: function() { PageController.goToPage(PageEnum.PageSettingsSplitTunneling) } - - Keys.onTabPressed: { - if (splitTunnelingButton2.visible) { - return splitTunnelingButton2.rightButton.forceActiveFocus() - } else if (killSwitchSwitcher.visible) { - return killSwitchSwitcher.forceActiveFocus() - } else { - lastItemTabClicked() - } - } } DividerType { @@ -114,17 +108,11 @@ PageType { descriptionText: qsTr("Allows you to use the VPN only for certain Apps") rightImageSource: "qrc:/images/controls/chevron-right.svg" + parentFlickable: fl + clickedFunction: function() { PageController.goToPage(PageEnum.PageSettingsAppSplitTunneling) } - - Keys.onTabPressed: { - if (killSwitchSwitcher.visible) { - return killSwitchSwitcher.forceActiveFocus() - } else { - lastItemTabClicked() - } - } } DividerType { @@ -141,6 +129,8 @@ PageType { text: qsTr("KillSwitch") descriptionText: qsTr("Disables your internet if your encrypted VPN connection drops out for any reason.") + parentFlickable: fl + checked: SettingsController.isKillSwitchEnabled() checkable: !ConnectionController.isConnected onCheckedChanged: { @@ -153,8 +143,6 @@ PageType { PageController.showNotificationMessage(qsTr("Cannot change killSwitch settings during active connection")) } } - - Keys.onTabPressed: lastItemTabClicked() } DividerType { diff --git a/client/ui/qml/Pages2/PageSettingsLogging.qml b/client/ui/qml/Pages2/PageSettingsLogging.qml index bea366a6..33074dfa 100644 --- a/client/ui/qml/Pages2/PageSettingsLogging.qml +++ b/client/ui/qml/Pages2/PageSettingsLogging.qml @@ -23,10 +23,20 @@ PageType { anchors.left: parent.left anchors.right: parent.right anchors.topMargin: 20 + + onFocusChanged: { + console.debug("MOVE THIS LOGIC TO CPP!") + if (activeFocus) { + if (fl) { + fl.ensureVisible(this) + } + } + } } FlickableType { id: fl + anchors.top: backButton.bottom anchors.bottom: parent.bottom contentHeight: content.height @@ -51,6 +61,7 @@ PageType { SwitcherType { id: switcher + Layout.fillWidth: true Layout.topMargin: 16 Layout.leftMargin: 16 @@ -66,14 +77,7 @@ PageType { } } - onFocusChanged: { - console.debug("MOVE THIS LOGIC TO CPP!") - if (activeFocus) { - if (fl) { - fl.ensureVisible(this) - } - } - } + parentFlickable: fl } DividerType {} diff --git a/client/ui/qml/Pages2/PageSettingsServersList.qml b/client/ui/qml/Pages2/PageSettingsServersList.qml index 6f43c3fe..1852f9c3 100644 --- a/client/ui/qml/Pages2/PageSettingsServersList.qml +++ b/client/ui/qml/Pages2/PageSettingsServersList.qml @@ -52,25 +52,13 @@ PageType { height: 500 // servers.contentItem.height // TODO: calculate height + property bool isFocusable: true + model: ServersModel 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 @@ -81,12 +69,6 @@ PageType { implicitWidth: servers.width implicitHeight: delegateContent.implicitHeight - // onFocusChanged: { - // if (focus) { - // server.rightButton.forceActiveFocus() - // } - // } - ColumnLayout { id: delegateContent @@ -99,7 +81,7 @@ PageType { Layout.fillWidth: true text: name - // parentFlickable: fl + descriptionText: { var servicesNameString = "" var servicesName = ServersModel.getAllInstalledServicesName(index) diff --git a/client/ui/qml/Pages2/PageSetupWizardApiServicesList.qml b/client/ui/qml/Pages2/PageSetupWizardApiServicesList.qml index 82e2e7e1..6ecfdc99 100644 --- a/client/ui/qml/Pages2/PageSetupWizardApiServicesList.qml +++ b/client/ui/qml/Pages2/PageSetupWizardApiServicesList.qml @@ -51,6 +51,8 @@ PageType { height: containers.contentItem.height spacing: 16 + property bool isFocusable: true + currentIndex: 1 interactive: false model: ApiServicesModel @@ -85,6 +87,9 @@ PageType { PageController.goToPage(PageEnum.PageSetupWizardApiServiceInfo) } } + + Keys.onEnterPressed: clicked() + Keys.onReturnPressed: clicked() } } } diff --git a/client/ui/qml/Pages2/PageSetupWizardConfigSource.qml b/client/ui/qml/Pages2/PageSetupWizardConfigSource.qml index e5030336..e98b8055 100644 --- a/client/ui/qml/Pages2/PageSetupWizardConfigSource.qml +++ b/client/ui/qml/Pages2/PageSetupWizardConfigSource.qml @@ -25,8 +25,6 @@ PageType { } } - // defaultActiveFocusItem: focusItem - FlickableType { id: fl anchors.top: parent.top @@ -136,6 +134,8 @@ PageType { } ParagraphTextType { + objectName: "insertKeyLabel" + Layout.fillWidth: true Layout.topMargin: 32 Layout.rightMargin: 16 @@ -155,6 +155,8 @@ PageType { headerText: qsTr("Insert key") buttonText: qsTr("Insert") + parentFlickable: fl + clickedFunc: function() { textField.text = "" textField.paste() @@ -169,14 +171,7 @@ PageType { Layout.rightMargin: 16 Layout.leftMargin: 16 - onFocusChanged: { - console.debug("MOVE THIS LOGIC TO CPP!") - if (activeFocus) { - if (fl) { - fl.ensureVisible(this) - } - } - } + parentFlickable: fl visible: textKey.textFieldText !== "" @@ -214,14 +209,7 @@ PageType { rightImageSource: "qrc:/images/controls/chevron-right.svg" leftImageSource: "qrc:/images/controls/amnezia.svg" - focusItem.onFocusChanged: { - console.debug("MOVE THIS LOGIC TO CPP!") - if (focusItem.activeFocus) { - if (fl) { - fl.ensureVisible(apiInstalling) - } - } - } + parentFlickable: fl onClicked: function() { PageController.showBusyIndicator(true) @@ -247,14 +235,7 @@ PageType { rightImageSource: "qrc:/images/controls/chevron-right.svg" leftImageSource: "qrc:/images/controls/server.svg" - focusItem.onFocusChanged: { - console.debug("MOVE THIS LOGIC TO CPP!") - if (focusItem.activeFocus) { - if (fl) { - fl.ensureVisible(manualInstalling) - } - } - } + parentFlickable: fl onClicked: { PageController.goToPage(PageEnum.PageSetupWizardCredentials) @@ -276,14 +257,7 @@ PageType { rightImageSource: "qrc:/images/controls/chevron-right.svg" leftImageSource: "qrc:/images/controls/archive-restore.svg" - focusItem.onFocusChanged: { - console.debug("MOVE THIS LOGIC TO CPP!") - if (focusItem.activeFocus) { - if (fl) { - fl.ensureVisible(backupRestore) - } - } - } + parentFlickable: fl onClicked: { var filePath = SystemController.getFileName(qsTr("Open backup file"), @@ -309,12 +283,7 @@ PageType { rightImageSource: "qrc:/images/controls/chevron-right.svg" leftImageSource: "qrc:/images/controls/folder-search-2.svg" - focusItem.onFocusChanged: { - console.debug("MOVE THIS LOGIC TO CPP!") - if (fl) { - fl.ensureVisible(openFile) - } - } + parentFlickable: fl onClicked: { var nameFilter = !ServersModel.getServersCount() ? "Config or backup files (*.vpn *.ovpn *.conf *.json *.backup)" : @@ -343,14 +312,7 @@ PageType { rightImageSource: "qrc:/images/controls/chevron-right.svg" leftImageSource: "qrc:/images/controls/scan-line.svg" - focusItem.onFocusChanged: { - console.debug("MOVE THIS LOGIC TO CPP!") - if (focusItem.activeFocus) { - if (fl) { - fl.ensureVisible(scanQr) - } - } - } + parentFlickable: fl onClicked: { ImportController.startDecodingQr() @@ -375,14 +337,7 @@ PageType { rightImageSource: "qrc:/images/controls/chevron-right.svg" leftImageSource: "qrc:/images/controls/help-circle.svg" - focusItem.onFocusChanged: { - console.debug("MOVE THIS LOGIC TO CPP!") - if (focusItem.activeFocus) { - if (fl) { - fl.ensureVisible(siteLink) - } - } - } + parentFlickable: fl onClicked: { Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl()) diff --git a/client/ui/qml/Pages2/PageSetupWizardCredentials.qml b/client/ui/qml/Pages2/PageSetupWizardCredentials.qml index 5139ad5a..aa0b935f 100644 --- a/client/ui/qml/Pages2/PageSetupWizardCredentials.qml +++ b/client/ui/qml/Pages2/PageSetupWizardCredentials.qml @@ -13,8 +13,6 @@ import "../Controls2/TextTypes" PageType { id: root - // defaultActiveFocusItem: hostname.textField - BackButtonType { id: backButton @@ -54,6 +52,8 @@ PageType { headerText: qsTr("Server IP address [:port]") textFieldPlaceholderText: qsTr("255.255.255.255:22") + parentFlickable: fl + textField.onFocusChanged: { textField.text = textField.text.replace(/^\s+|\s+$/g, '') } @@ -66,6 +66,8 @@ PageType { headerText: qsTr("SSH Username") textFieldPlaceholderText: "root" + parentFlickable: fl + textField.onFocusChanged: { textField.text = textField.text.replace(/^\s+|\s+$/g, '') } @@ -82,6 +84,8 @@ PageType { buttonImageSource: textFieldText !== "" ? (hidePassword ? "qrc:/images/controls/eye.svg" : "qrc:/images/controls/eye-off.svg") : "" + parentFlickable: fl + clickedFunc: function() { hidePassword = !hidePassword } @@ -99,6 +103,8 @@ PageType { text: qsTr("Continue") + parentFlickable: fl + clickedFunc: function() { forceActiveFocus() if (!isCredentialsFilled()) { @@ -138,6 +144,8 @@ PageType { rightImageSource: "qrc:/images/controls/chevron-right.svg" leftImageSource: "qrc:/images/controls/help-circle.svg" + parentFlickable: fl + onClicked: { Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl() + "/starter-guide") } diff --git a/client/ui/qml/Pages2/PageSetupWizardViewConfig.qml b/client/ui/qml/Pages2/PageSetupWizardViewConfig.qml index dffaa8c6..52c12c56 100644 --- a/client/ui/qml/Pages2/PageSetupWizardViewConfig.qml +++ b/client/ui/qml/Pages2/PageSetupWizardViewConfig.qml @@ -99,6 +99,8 @@ PageType { text: showContent ? qsTr("Collapse content") : qsTr("Show content") + parentFlickable: fl + clickedFunc: function() { showContent = !showContent } diff --git a/client/ui/qml/Pages2/PageStart.qml b/client/ui/qml/Pages2/PageStart.qml index 9f37458c..abd03809 100644 --- a/client/ui/qml/Pages2/PageStart.qml +++ b/client/ui/qml/Pages2/PageStart.qml @@ -90,16 +90,6 @@ PageType { PageController.closePage() } } - - // function onForceTabBarActiveFocus() { - // homeTabButton.focus = true - // tabBar.forceActiveFocus() - // } - - // function onForceStackActiveFocus() { - // homeTabButton.focus = true - // tabBarStackView.forceActiveFocus() - // } } Connections { @@ -311,10 +301,6 @@ PageType { tabBar.currentIndex = 0 FocusController.setRootItem(null) // TODO: move to do it automaticaly } - - // KeyNavigation.tab: shareTabButton - // Keys.onEnterPressed: this.clicked() - // Keys.onReturnPressed: this.clicked() } TabImageButtonType { @@ -340,8 +326,6 @@ PageType { tabBarStackView.goToTabBarPage(PageEnum.PageShare) tabBar.currentIndex = 1 } - - // KeyNavigation.tab: settingsTabButton } TabImageButtonType { @@ -354,8 +338,6 @@ PageType { tabBarStackView.goToTabBarPage(PageEnum.PageSettings) tabBar.currentIndex = 2 } - - // KeyNavigation.tab: plusTabButton } TabImageButtonType { @@ -368,8 +350,6 @@ PageType { tabBarStackView.goToTabBarPage(PageEnum.PageSetupWizardConfigSource) tabBar.currentIndex = 3 } - - // Keys.onTabPressed: PageController.forceStackActiveFocus() } } } From 78a4caa47eb6cbb819f0d650864861cc6b268645 Mon Sep 17 00:00:00 2001 From: Cyril Anisimov Date: Fri, 20 Sep 2024 00:22:14 +0200 Subject: [PATCH 04/40] fixed language selector --- .../qml/Components/SelectLanguageDrawer.qml | 255 ++++++++++-------- client/ui/qml/Controls2/DrawerType2.qml | 23 -- 2 files changed, 138 insertions(+), 140 deletions(-) diff --git a/client/ui/qml/Components/SelectLanguageDrawer.qml b/client/ui/qml/Components/SelectLanguageDrawer.qml index 3124445e..337c5987 100644 --- a/client/ui/qml/Components/SelectLanguageDrawer.qml +++ b/client/ui/qml/Components/SelectLanguageDrawer.qml @@ -24,11 +24,11 @@ DrawerType2 { target: root enabled: !GC.isMobile() function onOpened() { - FocusController.setRoot(root) + FocusController.setRootItem(root) } function onClosed() { - FocusController.setRoot(null) + FocusController.setRootItem(null) } } @@ -42,166 +42,187 @@ DrawerType2 { BackButtonType { id: backButton + + Layout.fillWidth: true + Layout.topMargin: 16 + Layout.rightMargin: 16 + Layout.leftMargin: 16 + backButtonImage: "qrc:/images/controls/arrow-left.svg" backButtonFunction: function() { root.closeTriggered() } } + + Header2Type { + id: header + + Layout.fillWidth: true + Layout.topMargin: 16 + Layout.rightMargin: 16 + Layout.leftMargin: 16 + + headerText: qsTr("Choose language") + } } - FlickableType { + ListView { + id: listView + anchors.top: backButtonLayout.bottom anchors.left: parent.left anchors.right: parent.right anchors.bottom: parent.bottom - contentHeight: content.implicitHeight - ColumnLayout { - id: content + clip: true + interactive: true - anchors.fill: parent + property bool isFocusable: true - Header2Type { - id: header - Layout.fillWidth: true - Layout.topMargin: 16 - Layout.rightMargin: 16 - Layout.leftMargin: 16 + model: LanguageModel + currentIndex: LanguageModel.currentLanguageIndex - headerText: qsTr("Choose language") - } + ButtonGroup { + id: buttonGroup + } - ListView { - id: listView + // activeFocusOnTab: true + // onActiveFocusChanged: { + // if (activeFocus) { + // this.currentFocusIndex = 0 + // this.itemAtIndex(currentFocusIndex).forceActiveFocus() + // } + // } - Layout.fillWidth: true - height: listView.contentItem.height + // Keys.onTabPressed: { + // if (currentFocusIndex < this.count - 1) { + // currentFocusIndex += 1 + // this.itemAtIndex(currentFocusIndex).forceActiveFocus() + // } else { + // listViewFocusItem.forceActiveFocus() + // focusItem.forceActiveFocus() + // } + // } - clip: true - interactive: false + // Item { + // id: listViewFocusItem // TODO: delete? + // Keys.onTabPressed: { + // root.forceActiveFocus() + // } + // } - model: LanguageModel - currentIndex: LanguageModel.currentLanguageIndex + // onVisibleChanged: { + // if (visible) { + // listViewFocusItem.forceActiveFocus() + // focusItem.forceActiveFocus() + // } + // } - ButtonGroup { - id: buttonGroup - } + delegate: Item { + implicitWidth: root.width + implicitHeight: delegateContent.implicitHeight - property int currentFocusIndex: 0 + // onActiveFocusChanged: { + // if (activeFocus) { + // radioButton.forceActiveFocus() + // } + // } - activeFocusOnTab: true - onActiveFocusChanged: { - if (activeFocus) { - this.currentFocusIndex = 0 - this.itemAtIndex(currentFocusIndex).forceActiveFocus() - } - } + ColumnLayout { + id: delegateContent - // Keys.onTabPressed: { - // if (currentFocusIndex < this.count - 1) { - // currentFocusIndex += 1 - // this.itemAtIndex(currentFocusIndex).forceActiveFocus() - // } else { - // listViewFocusItem.forceActiveFocus() - // focusItem.forceActiveFocus() - // } - // } + anchors.fill: parent + + RadioButton { + id: radioButton + + implicitWidth: parent.width + implicitHeight: radioButtonContent.implicitHeight + + hoverEnabled: true + + property bool isFocusable: true - Item { - id: listViewFocusItem Keys.onTabPressed: { - root.forceActiveFocus() + FocusController.nextKeyTabItem() } - } - onVisibleChanged: { - if (visible) { - listViewFocusItem.forceActiveFocus() - focusItem.forceActiveFocus() + Keys.onBacktabPressed: { + FocusController.previousKeyTabItem() } - } - delegate: Item { - implicitWidth: root.width - implicitHeight: delegateContent.implicitHeight + Keys.onUpPressed: { + FocusController.nextKeyUpItem() + } - onActiveFocusChanged: { - if (activeFocus) { - radioButton.forceActiveFocus() + Keys.onDownPressed: { + FocusController.nextKeyDownItem() + } + + Keys.onLeftPressed: { + FocusController.nextKeyLeftItem() + } + + Keys.onRightPressed: { + FocusController.nextKeyRightItem() + } + + indicator: Rectangle { + width: parent.width - 1 + height: parent.height + color: radioButton.hovered ? AmneziaStyle.color.slateGray : AmneziaStyle.color.onyxBlack + border.color: radioButton.focus ? AmneziaStyle.color.paleGray : AmneziaStyle.color.transparent + border.width: radioButton.focus ? 1 : 0 + + Behavior on color { + PropertyAnimation { duration: 200 } + } + Behavior on border.color { + PropertyAnimation { duration: 200 } } } - ColumnLayout { - id: delegateContent - + RowLayout { + id: radioButtonContent anchors.fill: parent - RadioButton { - id: radioButton + anchors.rightMargin: 16 + anchors.leftMargin: 16 - implicitWidth: parent.width - implicitHeight: radioButtonContent.implicitHeight + spacing: 0 - hoverEnabled: true + z: 1 - indicator: Rectangle { - width: parent.width - 1 - height: parent.height - color: radioButton.hovered ? AmneziaStyle.color.slateGray : AmneziaStyle.color.onyxBlack - border.color: radioButton.focus ? AmneziaStyle.color.paleGray : AmneziaStyle.color.transparent - border.width: radioButton.focus ? 1 : 0 + ParagraphTextType { + Layout.fillWidth: true + Layout.topMargin: 20 + Layout.bottomMargin: 20 - Behavior on color { - PropertyAnimation { duration: 200 } - } - Behavior on border.color { - PropertyAnimation { duration: 200 } - } - } + text: languageName + } - RowLayout { - id: radioButtonContent - anchors.fill: parent + Image { + source: "qrc:/images/controls/check.svg" + visible: radioButton.checked - anchors.rightMargin: 16 - anchors.leftMargin: 16 + width: 24 + height: 24 - spacing: 0 - - z: 1 - - ParagraphTextType { - Layout.fillWidth: true - Layout.topMargin: 20 - Layout.bottomMargin: 20 - - text: languageName - } - - Image { - source: "qrc:/images/controls/check.svg" - visible: radioButton.checked - - width: 24 - height: 24 - - Layout.rightMargin: 8 - } - } - - ButtonGroup.group: buttonGroup - checked: listView.currentIndex === index - - onClicked: { - listView.currentIndex = index - LanguageModel.changeLanguage(languageIndex) - root.closeTriggered() - } + Layout.rightMargin: 8 } } - Keys.onEnterPressed: radioButton.clicked() - Keys.onReturnPressed: radioButton.clicked() + ButtonGroup.group: buttonGroup + checked: listView.currentIndex === index + + onClicked: { + listView.currentIndex = index + LanguageModel.changeLanguage(languageIndex) + root.closeTriggered() + } } } + + Keys.onEnterPressed: radioButton.clicked() + Keys.onReturnPressed: radioButton.clicked() } } } diff --git a/client/ui/qml/Controls2/DrawerType2.qml b/client/ui/qml/Controls2/DrawerType2.qml index df07c52f..5e480327 100644 --- a/client/ui/qml/Controls2/DrawerType2.qml +++ b/client/ui/qml/Controls2/DrawerType2.qml @@ -56,8 +56,6 @@ Item { target: PageController function onCloseTopDrawer() { - console.debug("===>> onCloseTopDrawer function") - if (depthIndex === PageController.getDrawerDepth()) { if (isCollapsedStateActive()) { return @@ -76,8 +74,6 @@ Item { target: root function onCloseTriggered() { - console.debug("***>> onClose root connection") - if (isCollapsedStateActive()) { return } @@ -88,8 +84,6 @@ Item { } function onClosed() { - console.debug("***>> onClosed root connection") - drawerContent.state = root.drawerCollapsedStateName if (root.isCollapsedStateActive()) { @@ -105,8 +99,6 @@ Item { } function onOpenTriggered() { - console.debug("===>> onOpen root connection") - if (root.isExpandedStateActive()) { return } @@ -119,10 +111,7 @@ Item { function onOpened() { drawerContent.state = root.drawerExpandedStateName - console.debug("===>> onOpened root connection") - if (isExpandedStateActive()) { - console.error("new state - extended") if (PageController.getInitialPageNavigationBarColor() !== 0xFF1C1D21) { PageController.updateNavigationBarColor(0xFF1C1D21) } @@ -130,7 +119,6 @@ Item { depthIndex = PageController.incrementDrawerDepth() FocusController.setRootItem(root) - console.debug("===>> Root item has changed to ", root) } } @@ -150,7 +138,6 @@ Item { anchors.fill: parent onClicked: { - console.debug("===>> onClicked emptyArea") root.closeTriggered() } } @@ -171,8 +158,6 @@ Item { /** If drag area is released at any point other than min or max y, transition to the other state */ onReleased: { - console.debug("===>> onReleased dragArea") - if (isCollapsedStateActive() && drawerContent.y < dragArea.drag.maximumY) { root.openTriggered() return @@ -184,24 +169,16 @@ Item { } onEntered: { - console.debug("===>> onEntered dragArea") - root.cursorEntered() } onExited: { - console.debug("===>> onExited dragArea") - root.cursorExited() } onPressedChanged: { - console.debug("===>> onPressedChanged dragArea") - root.pressed(pressed, entered) } onClicked: { - console.debug("===>> onClicked dragArea") - if (isCollapsedStateActive()) { root.openTriggered() } From f189f7b6af3d96ab07617e8eceb3ce33b779467c Mon Sep 17 00:00:00 2001 From: Cyril Anisimov Date: Tue, 24 Sep 2024 20:08:36 +0200 Subject: [PATCH 05/40] add reverse focus change to FocusController --- client/ui/controllers/focusController.cpp | 237 ++++++++++++++-------- client/ui/controllers/focusController.h | 29 ++- 2 files changed, 160 insertions(+), 106 deletions(-) diff --git a/client/ui/controllers/focusController.cpp b/client/ui/controllers/focusController.cpp index 3f5d042f..16133341 100644 --- a/client/ui/controllers/focusController.cpp +++ b/client/ui/controllers/focusController.cpp @@ -60,12 +60,12 @@ bool isOnTheScene(QObject* object) } if (!item->isVisible()) { - qInfo() << "The item is not visible: " << item; + // qDebug() << "===>> The item is not visible: " << item; return false; } QRectF itemRect = item->mapRectToScene(item->childrenRect()); - + QQuickWindow* window = item->window(); if (!window) { qWarning() << "Couldn't get the window on the Scene check"; @@ -118,7 +118,7 @@ QList getSubChain(QObject* item) && isFocusable(child) && (isOnTheScene(child)) && isEnabled(child) - ) { + ) { res.append(child); } else { res.append(getSubChain(child)); @@ -153,13 +153,17 @@ public: ~ListViewFocusController(); void incrementIndex(); - void decrementCurrentIndex(); + void decrementIndex(); void positionViewAtIndex(); void focusNextItem(); void focusPreviousItem(); void resetFocusChain(); + bool isListViewFirstFocusItem(); + bool isDelegateFirstFocusItem(); bool isListViewLastFocusItem(); bool isDelegateLastFocusItem(); + bool isReturnNeeded(); + void viewToBegin(); private: int size() const; @@ -173,6 +177,7 @@ private: QQuickItem* m_focusedItem; qsizetype m_focusedItemIndex; qsizetype m_delegateIndex; + bool m_isReturnNeeded; }; ListViewFocusController::ListViewFocusController(QQuickItem* listView, QObject* parent) @@ -182,6 +187,7 @@ ListViewFocusController::ListViewFocusController(QQuickItem* listView, QObject* , m_focusedItem{nullptr} , m_focusedItemIndex{-1} , m_delegateIndex{0} + , m_isReturnNeeded{false} { } @@ -212,7 +218,7 @@ void ListViewFocusController::incrementIndex() m_delegateIndex++; } -void ListViewFocusController::decrementCurrentIndex() +void ListViewFocusController::decrementIndex() { m_delegateIndex--; } @@ -241,11 +247,17 @@ QQuickItem* ListViewFocusController::focusedItem() void ListViewFocusController::focusNextItem() { if (m_focusChain.empty()) { - qDebug() << "Empty focusChain with current delegate: " << currentDelegate(); + qDebug() << "Empty focusChain with current delegate: " << currentDelegate() << "Scanning for elements..."; m_focusChain = getSubChain(currentDelegate()); } + if (m_focusChain.empty()) { + qWarning() << "No elements found. Returning from ListView..."; + m_isReturnNeeded = true; + return; + } m_focusedItemIndex++; m_focusedItem = qobject_cast(m_focusChain.at(m_focusedItemIndex)); + qDebug() << "==>> Focused Item: " << m_focusedItem << " with Index: " << m_focusedItemIndex; m_focusedItem->forceActiveFocus(); } @@ -261,16 +273,36 @@ void ListViewFocusController::resetFocusChain() m_focusedItemIndex = -1; } +bool ListViewFocusController::isDelegateFirstFocusItem() +{ + return m_focusedItem && (m_focusedItem == m_focusChain.first()); +} + bool ListViewFocusController::isDelegateLastFocusItem() { return m_focusedItem && (m_focusedItem == m_focusChain.last()); } +bool ListViewFocusController::isListViewFirstFocusItem() +{ + return (m_delegateIndex == 0) && isDelegateFirstFocusItem(); +} + bool ListViewFocusController::isListViewLastFocusItem() { return (m_delegateIndex == size() - 1) && isDelegateLastFocusItem(); } +bool ListViewFocusController::isReturnNeeded() +{ + return m_isReturnNeeded; +} + +void ListViewFocusController::viewToBegin() +{ + QMetaObject::invokeMethod(m_listView, "positionViewAtBeginning", Qt::AutoConnection); +} + //////////////////////////////////////////////////////////////////////////////////////////////////// FocusController::FocusController(QQmlApplicationEngine* engine, QObject *parent) @@ -279,35 +311,31 @@ FocusController::FocusController(QQmlApplicationEngine* engine, QObject *parent) , m_focusChain{} , m_focusedItem{nullptr} , m_focusedItemIndex{-1} - , m_rootItem{nullptr} + , m_rootObjects{} + , m_defaultFocusItem{QSharedPointer()} , m_lvfc{nullptr} { - connect(this, &FocusController::rootItemChanged, this, &FocusController::reload); + // connect(this, &FocusController::rootItemChanged, this, &FocusController::onReload); + QObject::connect(m_engine.get(), &QQmlApplicationEngine::objectCreated, this, [this](QObject *object, const QUrl &url){ + qDebug() << "===>> () CREATED " << object << " : " << url; + QQuickItem* newDefaultFocusItem = object->findChild("defaultFocusItem"); + if(newDefaultFocusItem && m_defaultFocusItem != newDefaultFocusItem) { + m_defaultFocusItem.reset(newDefaultFocusItem); + qDebug() << "===>> [] NEW DEFAULT FOCUS ITEM " << m_defaultFocusItem; + } + }); } -void FocusController::resetFocus() -{ - reload(); - if (m_focusChain.empty()) { - qWarning() << "There is no focusable elements"; - return; - } - - if(m_focusedItemIndex == -1) { - m_focusedItemIndex = 0; - m_focusedItem = qobject_cast(m_focusChain.at(m_focusedItemIndex)); - m_focusedItem->forceActiveFocus(); - } -} - -void FocusController::nextKeyTabItem() +void FocusController::nextItem(bool isForwardOrder) { if (m_lvfc) { - focusNextListViewItem(); + isForwardOrder ? focusNextListViewItem() : focusPreviousListViewItem(); + qDebug() << "===>> [handling the ListView]"; + return; } - reload(); + reload(isForwardOrder); if(m_focusChain.empty()) { qWarning() << "There are no items to navigate"; @@ -325,29 +353,49 @@ void FocusController::nextKeyTabItem() m_focusedItem = qobject_cast(m_focusChain.at(m_focusedItemIndex)); if(m_focusedItem == nullptr) { - qWarning() << "Failed to get item to focus on"; + qWarning() << "Failed to get item to focus on. Setting focus on default"; + m_focusedItem = m_defaultFocusItem.get(); return; } if(isListView(m_focusedItem)) { + qDebug() << "===>> [Found ListView]"; m_lvfc = new ListViewFocusController(m_focusedItem, this); - focusNextListViewItem(); + if(isForwardOrder) { + m_lvfc->viewToBegin(); + focusNextListViewItem(); + } else { + focusPreviousListViewItem(); + } return; } + qDebug() << "===>> Focused Item: " << m_focusedItem; m_focusedItem->forceActiveFocus(Qt::TabFocusReason); printItems(m_focusChain, m_focusedItem); + + const auto w = m_defaultFocusItem->window(); + + qDebug() << "===>> CURRENT ACTIVE ITEM: " << w->activeFocusItem(); + qDebug() << "===>> CURRENT FOCUS OBJECT: " << w->focusObject(); + if(m_rootObjects.empty()) { + qDebug() << "===>> ROOT OBJECT IS DEFAULT"; + } else { + qDebug() << "===>> ROOT OBJECT: " << m_rootObjects.top(); + } } void FocusController::focusNextListViewItem() { m_lvfc->focusNextItem(); - if (m_lvfc->isListViewLastFocusItem()) { + if (m_lvfc->isListViewLastFocusItem() || m_lvfc->isReturnNeeded()) { + qDebug() << "===>> [Last item in ListView was reached]"; delete m_lvfc; m_lvfc = nullptr; } else if (m_lvfc->isDelegateLastFocusItem()) { + qDebug() << "===>> [End of delegate elements was reached. Going to the next delegate]"; m_lvfc->resetFocusChain(); m_lvfc->incrementIndex(); m_lvfc->positionViewAtIndex(); @@ -356,110 +404,121 @@ void FocusController::focusNextListViewItem() void FocusController::focusPreviousListViewItem() { - // TODO: implement + m_lvfc->focusPreviousItem(); + + if (m_lvfc->isListViewFirstFocusItem() || m_lvfc->isReturnNeeded()) { + delete m_lvfc; + m_lvfc = nullptr; + } else if (m_lvfc->isDelegateFirstFocusItem()) { + m_lvfc->resetFocusChain(); + m_lvfc->decrementIndex(); + m_lvfc->positionViewAtIndex(); + } +} + +void FocusController::nextKeyTabItem() +{ + nextItem(true); } void FocusController::previousKeyTabItem() { - reload(); - - if(m_focusChain.empty()) { - return; - } - - if (m_focusedItemIndex <= 0) { - m_focusedItemIndex = m_focusChain.size() - 1; - } else { - m_focusedItemIndex--; - } - - m_focusedItem = qobject_cast(m_focusChain.at(m_focusedItemIndex)); - m_focusedItem->forceActiveFocus(Qt::TabFocusReason); - - qDebug() << "===>> Current focus was changed to " << m_focusedItem; + nextItem(false); } void FocusController::nextKeyUpItem() { - previousKeyTabItem(); + nextItem(false); } void FocusController::nextKeyDownItem() { - nextKeyTabItem(); + nextItem(true); } void FocusController::nextKeyLeftItem() { - previousKeyTabItem(); + nextItem(false); } void FocusController::nextKeyRightItem() { - nextKeyTabItem(); + nextItem(true); } -void FocusController::reload() +void FocusController::setFocusOnDefaultItem() { - m_focusChain.clear(); + qDebug() << "===>> Setting focus on DEFAULT FOCUS ITEM..."; + m_defaultFocusItem->forceActiveFocus(); +} - QObjectList rootObjects; +void FocusController::reload(bool isForwardOrder) +{ + m_focusChain.clear(); - const auto rootItem = m_rootItem; + QObject* rootObject = (m_rootObjects.empty() + ? m_engine->rootObjects().value(0) + : m_rootObjects.top()); - if (rootItem != nullptr) { - rootObjects << qobject_cast(rootItem); - } else { - rootObjects = m_engine->rootObjects(); - } - - if(rootObjects.empty()) { - qWarning() << "Empty focus chain detected!"; - emit focusChainChanged(); + if(!rootObject) { + qCritical() << "No ROOT OBJECT found!"; + m_focusedItemIndex = -1; + resetRootObject(); + setFocusOnDefaultItem(); return; } - for(const auto object : rootObjects) { - m_focusChain.append(getSubChain(object)); - } + qDebug() << "===>> ROOT OBJECTS: " << rootObject; - std::sort(m_focusChain.begin(), m_focusChain.end(), isLess); + m_focusChain.append(getSubChain(rootObject)); - printItems(m_focusChain, m_focusedItem); - - emit focusChainChanged(); + std::sort(m_focusChain.begin(), m_focusChain.end(), isForwardOrder? isLess : isMore); if (m_focusChain.empty()) { + qWarning() << "Focus chain is empty!"; m_focusedItemIndex = -1; - qWarning() << "reloaded to empty focus chain"; - return; - } - - QQuickWindow* window = qobject_cast(rootObjects[0]); - if (!window) { - window = qobject_cast(rootObjects[0])->window(); - } - - if (!window) { - qCritical() << "Couldn't get the current window"; + resetRootObject(); + setFocusOnDefaultItem(); return; } m_focusedItemIndex = m_focusChain.indexOf(m_focusedItem); - // m_focusedItemIndex = m_focusChain.indexOf(window->activeFocusItem()); if(m_focusedItemIndex == -1) { - qInfo() << "No focus item in chain. Moving focus to begin..."; - // m_focused_item_index = 0; // if not in focus chain current + qInfo() << "No focus item in chain."; + setFocusOnDefaultItem(); + return; + } +} + +void FocusController::pushRootObject(QObject* object) +{ + m_rootObjects.push(object); + qDebug() << "===>> ROOT OBJECT is changed to: " << m_rootObjects.top(); +} + +void FocusController::dropRootObject(QObject* object) +{ + if (m_rootObjects.empty()) { + qDebug() << "ROOT OBJECT is already NULL"; + return; } - // m_focusedItem = qobject_cast(m_focusChain.at(m_focusedItemIndex)); - - // m_focusedItem->forceActiveFocus(); + if (m_rootObjects.top() == object) { + m_rootObjects.pop(); + if(m_rootObjects.size()) { + qDebug() << "===>> ROOT OBJECT is changed to: " << m_rootObjects.top(); + } else { + qDebug() << "===>> ROOT OBJECT is changed to NULL"; + } + } else { + qWarning() << "===>> TRY TO DROP WRONG ROOT OBJECT: " << m_rootObjects.top() << " SHOULD BE: " << object; + } } -void FocusController::setRootItem(QQuickItem* item) +void FocusController::resetRootObject() { - m_rootItem = item; + m_rootObjects.clear(); + qDebug() << "===>> ROOT OBJECT IS RESETED"; } diff --git a/client/ui/controllers/focusController.h b/client/ui/controllers/focusController.h index f593fc8d..38869a69 100644 --- a/client/ui/controllers/focusController.h +++ b/client/ui/controllers/focusController.h @@ -2,6 +2,9 @@ #define FOCUSCONTROLLER_H #include +#include +#include + class QQuickItem; class QQmlApplicationEngine; @@ -20,31 +23,23 @@ public: Q_INVOKABLE void nextKeyDownItem(); Q_INVOKABLE void nextKeyLeftItem(); Q_INVOKABLE void nextKeyRightItem(); - -signals: - void nextTabItemChanged(QObject* item); - void previousTabItemChanged(QObject* item); - void nextKeyUpItemChanged(QObject* item); - void nextKeyDownItemChanged(QObject* item); - void nextKeyLeftItemChanged(QObject* item); - void nextKeyRightItemChanged(QObject* item); - void focusChainChanged(); - void rootItemChanged(); - -public slots: - void resetFocus(); - void reload(); - void setRootItem(QQuickItem* item); + Q_INVOKABLE void setFocusOnDefaultItem(); + Q_INVOKABLE void resetRootObject(); + Q_INVOKABLE void pushRootObject(QObject* object); + Q_INVOKABLE void dropRootObject(QObject* object); private: + void nextItem(bool isForwardOrder); void focusNextListViewItem(); void focusPreviousListViewItem(); + void reload(bool isForwardOrder); - QQmlApplicationEngine* m_engine; // Pointer to engine to get root object + QSharedPointer m_engine; // Pointer to engine to get root object QList m_focusChain; // List of current objects to be focused QQuickItem* m_focusedItem; // Pointer to the active focus item qsizetype m_focusedItemIndex; // Active focus item's index in focus chain - QQuickItem* m_rootItem; + QStack m_rootObjects; + QSharedPointer m_defaultFocusItem; ListViewFocusController* m_lvfc; // ListView focus manager }; From b4f4ec4ac96c0db3ea18e0819c76c7aafffc364c Mon Sep 17 00:00:00 2001 From: Cyril Anisimov Date: Sun, 29 Sep 2024 23:47:20 +0200 Subject: [PATCH 06/40] add default focus item --- client/ui/qml/main2.qml | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/client/ui/qml/main2.qml b/client/ui/qml/main2.qml index 18b69795..83a6667f 100644 --- a/client/ui/qml/main2.qml +++ b/client/ui/qml/main2.qml @@ -32,6 +32,37 @@ Window { title: "AmneziaVPN" + Item { + id: defaultFocusItem + objectName: "defaultFocusItem" + + focus: true + + Keys.onTabPressed: { + FocusController.nextKeyTabItem() + } + + Keys.onBacktabPressed: { + FocusController.previousKeyTabItem() + } + + Keys.onUpPressed: { + FocusController.nextKeyUpItem() + } + + Keys.onDownPressed: { + FocusController.nextKeyDownItem() + } + + Keys.onLeftPressed: { + FocusController.nextKeyLeftItem() + } + + Keys.onRightPressed: { + FocusController.nextKeyRightItem() + } + } + Connections { objectName: "pageControllerConnections" From 3c655d0051444e81a71e4b2eacdaafa04b127573 Mon Sep 17 00:00:00 2001 From: Cyril Anisimov Date: Sun, 29 Sep 2024 23:50:58 +0200 Subject: [PATCH 07/40] update transitions --- client/ui/qml/Components/QuestionDrawer.qml | 4 ++-- .../ui/qml/Components/SelectLanguageDrawer.qml | 4 ++-- client/ui/qml/Controls2/DrawerType2.qml | 4 ++-- client/ui/qml/Controls2/PageType.qml | 16 ++-------------- client/ui/qml/Controls2/PopupType.qml | 4 ++-- client/ui/qml/Pages2/PageStart.qml | 1 - 6 files changed, 10 insertions(+), 23 deletions(-) diff --git a/client/ui/qml/Components/QuestionDrawer.qml b/client/ui/qml/Components/QuestionDrawer.qml index 0dbd2eab..0a49358a 100644 --- a/client/ui/qml/Components/QuestionDrawer.qml +++ b/client/ui/qml/Components/QuestionDrawer.qml @@ -37,11 +37,11 @@ DrawerType2 { target: root enabled: !GC.isMobile() function onOpened() { - FocusController.setRootItem(root) + FocusController.pushRootObject(root) } function onClosed() { - FocusController.setRootItem(null) + FocusController.dropRootObject(root) } } diff --git a/client/ui/qml/Components/SelectLanguageDrawer.qml b/client/ui/qml/Components/SelectLanguageDrawer.qml index 337c5987..4ca472cf 100644 --- a/client/ui/qml/Components/SelectLanguageDrawer.qml +++ b/client/ui/qml/Components/SelectLanguageDrawer.qml @@ -24,11 +24,11 @@ DrawerType2 { target: root enabled: !GC.isMobile() function onOpened() { - FocusController.setRootItem(root) + FocusController.pushRootObject(root) } function onClosed() { - FocusController.setRootItem(null) + FocusController.dropRootObject(root) } } diff --git a/client/ui/qml/Controls2/DrawerType2.qml b/client/ui/qml/Controls2/DrawerType2.qml index 5e480327..04a7635c 100644 --- a/client/ui/qml/Controls2/DrawerType2.qml +++ b/client/ui/qml/Controls2/DrawerType2.qml @@ -95,7 +95,7 @@ Item { depthIndex = 0 PageController.decrementDrawerDepth() - FocusController.setRootItem(null) + FocusController.dropRootObject(root) } function onOpenTriggered() { @@ -118,7 +118,7 @@ Item { } depthIndex = PageController.incrementDrawerDepth() - FocusController.setRootItem(root) + FocusController.pushRootObject(root) } } diff --git a/client/ui/qml/Controls2/PageType.qml b/client/ui/qml/Controls2/PageType.qml index 977c18ba..724d3b0d 100644 --- a/client/ui/qml/Controls2/PageType.qml +++ b/client/ui/qml/Controls2/PageType.qml @@ -15,25 +15,13 @@ Item { } } -// MouseArea { -// id: globalMouseArea -// z: 99 -// anchors.fill: parent - -// enabled: true - -// onPressed: function(mouse) { -// forceActiveFocus() -// mouse.accepted = false -// } -// } - // Set a timer to set focus after a short delay Timer { id: timer interval: 500 // Milliseconds onTriggered: { - FocusController.resetFocus() + FocusController.resetRootObject() + FocusController.setFocusOnDefaultItem() } repeat: false // Stop the timer after one trigger running: !GC.isMobile() // Start the timer diff --git a/client/ui/qml/Controls2/PopupType.qml b/client/ui/qml/Controls2/PopupType.qml index 5bed7350..61221567 100644 --- a/client/ui/qml/Controls2/PopupType.qml +++ b/client/ui/qml/Controls2/PopupType.qml @@ -28,11 +28,11 @@ Popup { } onOpened: { - FocusController.setRootItem(root) + FocusController.pushRootObject(root) } onClosed: { - FocusController.setRootItem(null) + FocusController.dropRootObject(root) } background: Rectangle { diff --git a/client/ui/qml/Pages2/PageStart.qml b/client/ui/qml/Pages2/PageStart.qml index abd03809..01e623b6 100644 --- a/client/ui/qml/Pages2/PageStart.qml +++ b/client/ui/qml/Pages2/PageStart.qml @@ -299,7 +299,6 @@ PageType { tabBarStackView.goToTabBarPage(PageEnum.PageHome) ServersModel.processedIndex = ServersModel.defaultIndex tabBar.currentIndex = 0 - FocusController.setRootItem(null) // TODO: move to do it automaticaly } } From 75f189e25694e0a6563eca09b8e696324321a23c Mon Sep 17 00:00:00 2001 From: Cyril Anisimov Date: Sun, 29 Sep 2024 23:54:04 +0200 Subject: [PATCH 08/40] update pages --- .../qml/Components/HomeContainersListView.qml | 22 +- .../ui/qml/Components/InstalledAppsDrawer.qml | 2 + client/ui/qml/Components/ServersListView.qml | 74 +----- .../Components/SettingsContainersListView.qml | 29 +- client/ui/qml/Controls2/DropDownType.qml | 51 ++-- .../Controls2/ListViewWithRadioButtonType.qml | 122 ++++----- client/ui/qml/Pages2/PageHome.qml | 6 +- .../Pages2/PageSettingsApiLanguageList.qml | 116 ++++---- .../ui/qml/Pages2/PageSettingsServerInfo.qml | 250 ++++++++---------- .../Pages2/PageSettingsServerProtocols.qml | 76 +++--- .../qml/Pages2/PageSettingsServerServices.qml | 72 +++-- .../qml/Pages2/PageSettingsSplitTunneling.qml | 227 +++++++--------- client/ui/qml/Pages2/PageShare.qml | 64 ++--- 13 files changed, 443 insertions(+), 668 deletions(-) diff --git a/client/ui/qml/Components/HomeContainersListView.qml b/client/ui/qml/Components/HomeContainersListView.qml index 5d3569f5..8ddccb5a 100644 --- a/client/ui/qml/Components/HomeContainersListView.qml +++ b/client/ui/qml/Components/HomeContainersListView.qml @@ -17,8 +17,6 @@ ListView { property var rootWidth property var selectedText - property bool a: true - width: rootWidth height: contentItem.height // TODO: It should be fixed size, not content item height @@ -30,28 +28,34 @@ ListView { // property int currentFocusIndex: 0 - snapMode: ListView.SnapToItem + // snapMode: ListView.SnapToItem // ScrollBar.vertical: ScrollBar {} property bool isFocusable: true Keys.onTabPressed: { - console.debug("--> Tab is pressed on HomeContainersListView: ", objectName) FocusController.nextKeyTabItem() } Keys.onBacktabPressed: { - console.debug("--> Shift+Tab is pressed on HomeContainersListView: ", objectName) FocusController.previousKeyTabItem() } - Keys.onRightPressed: { - FocusController.nextKeyTabItem() + Keys.onUpPressed: { + FocusController.nextKeyUpItem() } - + + Keys.onDownPressed: { + FocusController.nextKeyDownItem() + } + Keys.onLeftPressed: { - FocusController.previousKeyTabItem() + FocusController.nextKeyLeftItem() + } + + Keys.onRightPressed: { + FocusController.nextKeyRightItem() } // activeFocusOnTab: true diff --git a/client/ui/qml/Components/InstalledAppsDrawer.qml b/client/ui/qml/Components/InstalledAppsDrawer.qml index 0ee48100..cb8186a9 100644 --- a/client/ui/qml/Components/InstalledAppsDrawer.qml +++ b/client/ui/qml/Components/InstalledAppsDrawer.qml @@ -69,6 +69,8 @@ DrawerType2 { clip: true interactive: true + property bool isFocusable: true + model: SortFilterProxyModel { id: proxyInstalledAppsModel sourceModel: installedAppsModel diff --git a/client/ui/qml/Components/ServersListView.qml b/client/ui/qml/Components/ServersListView.qml index 76f3f267..b3663cbd 100644 --- a/client/ui/qml/Components/ServersListView.qml +++ b/client/ui/qml/Components/ServersListView.qml @@ -33,43 +33,7 @@ ListView { policy: root.height >= root.contentHeight ? ScrollBar.AlwaysOff : ScrollBar.AlwaysOn } - readonly property bool isFocusable: true - - // Keys.onTabPressed: { - // FocusController.nextKeyTabItem() - // } - - // activeFocusOnTab: true - // focus: true - - property int focusItemIndex: 0 - - // onFocusItemIndexChanged: { - // console.debug("===>> root onFocusItemIndexChanged") - - // // const focusedElement = root.itemAtIndex(focusItemIndex) - // // if (focusedElement) { - // // if (focusedElement.y + focusedElement.height > root.height) { - // // root.contentY = focusedElement.y + focusedElement.height - root.height - // // } else { - // // root.contentY = 0 - // // } - // // } - // } - - Keys.onUpPressed: scrollBar.decrease() - Keys.onDownPressed: scrollBar.increase() - - // Connections { - // target: drawer - // enabled: !GC.isMobile() - // function onIsCollapsedChanged() { - // if (drawer.isCollapsedStateActive) { - // const item = root.itemAtIndex(root.focusItemIndex) - // if (item) { item.serverRadioButtonProperty.focus = false } - // } - // } - // } + property bool isFocusable: true Connections { target: ServersModel @@ -135,7 +99,6 @@ ListView { enabled: false } - // Keys.onTabPressed: serverInfoButton.forceActiveFocus() Keys.onEnterPressed: serverRadioButton.clicked() Keys.onReturnPressed: serverRadioButton.clicked() } @@ -144,10 +107,6 @@ ListView { id: serverInfoButton objectName: "serverInfoButton" - // signal keyTabOnLastElement - - // isFocusable: false - image: "qrc:/images/controls/settings.svg" imageColor: AmneziaStyle.color.paleGray @@ -156,41 +115,10 @@ ListView { z: 1 - // onActiveFocusChanged: { - // console.debug("===>> serverInfoButton::activeFocusChanged") - - // if (activeFocus) { - // if (currentIndex === root.count - 1) { - // console.log("---> Latest element") - // keyTabOnLastElement() - // } - - // console.log("--->>", currentIndex) - // // serverRadioButton.forceActiveFocus() - // } - // } - - // onKeyTabOnLastElement: { - // console.log("*** Signal emmited! ***") - // FocusController.nextKeyTabItem() - // } - - // Keys.onTabPressed: { - // console.log("===>> serverInfoButton::Keys.onTabPressed") - // if (root.focusItemIndex < root.count - 1) { - // root.focusItemIndex++ - // root.itemAtIndex(root.focusItemIndex).forceActiveFocus() - // } else { - // FocusController.nextKeyTabItem() - // root.contentY = 0 - // } - // } Keys.onEnterPressed: serverInfoButton.clicked() Keys.onReturnPressed: serverInfoButton.clicked() onClicked: function() { - console.debug("===>> onClicked serverInfoButton") - ServersModel.processedIndex = index PageController.goToPage(PageEnum.PageSettingsServerInfo) drawer.closeTriggered() diff --git a/client/ui/qml/Components/SettingsContainersListView.qml b/client/ui/qml/Components/SettingsContainersListView.qml index d6db36af..b3357b9c 100644 --- a/client/ui/qml/Components/SettingsContainersListView.qml +++ b/client/ui/qml/Components/SettingsContainersListView.qml @@ -22,28 +22,7 @@ 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 - } - } - } - } + property bool isFocusable: false onVisibleChanged: { if (visible) { @@ -55,12 +34,6 @@ ListView { implicitWidth: root.width implicitHeight: delegateContent.implicitHeight - onActiveFocusChanged: { - if (activeFocus) { - containerRadioButton.rightButton.forceActiveFocus() - } - } - ColumnLayout { id: delegateContent diff --git a/client/ui/qml/Controls2/DropDownType.qml b/client/ui/qml/Controls2/DropDownType.qml index 8b683d94..48471873 100644 --- a/client/ui/qml/Controls2/DropDownType.qml +++ b/client/ui/qml/Controls2/DropDownType.qml @@ -54,20 +54,25 @@ Item { FocusController.nextKeyTabItem() } - // function popupClosedFunc() { - // if (!GC.isMobile()) { - // this.forceActiveFocus() - // } - // } + Keys.onBacktabPressed: { + FocusController.previousKeyTabItem() + } - // property var parentFlickable - // onFocusChanged: { - // if (root.activeFocus) { - // if (root.parentFlickable) { - // root.parentFlickable.ensureVisible(root) - // } - // } - // } + Keys.onUpPressed: { + FocusController.nextKeyUpItem() + } + + Keys.onDownPressed: { + FocusController.nextKeyDownItem() + } + + Keys.onLeftPressed: { + FocusController.nextKeyLeftItem() + } + + Keys.onRightPressed: { + FocusController.nextKeyRightItem() + } implicitWidth: rootButtonContent.implicitWidth implicitHeight: rootButtonContent.implicitHeight @@ -171,8 +176,6 @@ Item { } ImageButtonType { - // isFocusable: false - Layout.rightMargin: 16 implicitWidth: 40 @@ -206,10 +209,6 @@ Item { anchors.fill: parent expandedHeight: drawerParent.height * drawerHeight - // onClosed: { - // root.popupClosedFunc() - // } - expandedStateContent: Item { id: container implicitHeight: menu.expandedHeight @@ -226,7 +225,11 @@ Item { id: backButton backButtonImage: root.headerBackButtonImage backButtonFunction: function() { menu.closeTriggered() } - // KeyNavigation.tab: listViewLoader.item + onActiveFocusChanged: { + if(activeFocus) { + root.listView.positionViewAtBeginning() + } + } } } @@ -252,14 +255,6 @@ Item { Loader { id: listViewLoader sourceComponent: root.listView - - onLoaded: { - // listViewLoader.item.parentFlickable = flickable - // FocusController.reload() - // listViewLoader.item.lastItemTabClicked = function() { - // focusItem.forceActiveFocus() - // } - } } } } diff --git a/client/ui/qml/Controls2/ListViewWithRadioButtonType.qml b/client/ui/qml/Controls2/ListViewWithRadioButtonType.qml index 9024af91..230f37eb 100644 --- a/client/ui/qml/Controls2/ListViewWithRadioButtonType.qml +++ b/client/ui/qml/Controls2/ListViewWithRadioButtonType.qml @@ -26,12 +26,6 @@ ListView { height: root.contentItem.height clip: true - interactive: false - - property FlickableType parentFlickable - property var lastItemTabClicked - - property int currentFocusIndex: 0 property bool isFocusable: true @@ -69,84 +63,80 @@ ListView { radioButton.clicked() } - delegate: Item { + delegate: ColumnLayout { + id: content + implicitWidth: rootWidth - implicitHeight: content.implicitHeight + // implicitHeight: content.implicitHeight - ColumnLayout { - id: content + RadioButton { + id: radioButton - anchors.fill: parent + implicitWidth: parent.width + implicitHeight: radioButtonContent.implicitHeight - RadioButton { - id: radioButton + hoverEnabled: true - implicitWidth: parent.width - implicitHeight: radioButtonContent.implicitHeight + indicator: Rectangle { + width: parent.width - 1 + height: parent.height + color: radioButton.hovered ? AmneziaStyle.color.slateGray : AmneziaStyle.color.onyxBlack + border.color: radioButton.focus ? AmneziaStyle.color.paleGray : AmneziaStyle.color.transparent + border.width: radioButton.focus ? 1 : 0 - hoverEnabled: true - - indicator: Rectangle { - width: parent.width - 1 - height: parent.height - color: radioButton.hovered ? AmneziaStyle.color.slateGray : AmneziaStyle.color.onyxBlack - border.color: radioButton.focus ? AmneziaStyle.color.paleGray : AmneziaStyle.color.transparent - border.width: radioButton.focus ? 1 : 0 - - Behavior on color { - PropertyAnimation { duration: 200 } - } - Behavior on border.color { - PropertyAnimation { duration: 200 } - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - enabled: false - } + Behavior on color { + PropertyAnimation { duration: 200 } + } + Behavior on border.color { + PropertyAnimation { duration: 200 } } - RowLayout { - id: radioButtonContent + MouseArea { anchors.fill: parent + cursorShape: Qt.PointingHandCursor + enabled: false + } + } - anchors.rightMargin: 16 - anchors.leftMargin: 16 + RowLayout { + id: radioButtonContent + anchors.fill: parent - z: 1 + anchors.rightMargin: 16 + anchors.leftMargin: 16 - ParagraphTextType { - Layout.fillWidth: true - Layout.topMargin: 20 - Layout.bottomMargin: 20 + z: 1 - text: name - maximumLineCount: root.textMaximumLineCount - elide: root.textElide + ParagraphTextType { + Layout.fillWidth: true + Layout.topMargin: 20 + Layout.bottomMargin: 20 - } + text: name + maximumLineCount: root.textMaximumLineCount + elide: root.textElide - Image { - source: imageSource - visible: radioButton.checked - - width: 24 - height: 24 - - Layout.rightMargin: 8 - } } - ButtonGroup.group: buttonGroup - checked: root.currentIndex === index + Image { + source: imageSource + visible: radioButton.checked - onClicked: { - root.currentIndex = index - root.selectedText = name - if (clickedFunction && typeof clickedFunction === "function") { - clickedFunction() - } + width: 24 + height: 24 + + Layout.rightMargin: 8 + } + } + + ButtonGroup.group: buttonGroup + checked: root.currentIndex === index + + onClicked: { + root.currentIndex = index + root.selectedText = name + if (clickedFunction && typeof clickedFunction === "function") { + clickedFunction() } } } diff --git a/client/ui/qml/Pages2/PageHome.qml b/client/ui/qml/Pages2/PageHome.qml index 2d52b044..5ef33ded 100644 --- a/client/ui/qml/Pages2/PageHome.qml +++ b/client/ui/qml/Pages2/PageHome.qml @@ -126,7 +126,6 @@ PageType { onClosed: { console.log(objectName, " was closed...") - FocusController.setRootItem(null) } } } @@ -257,7 +256,6 @@ PageType { console.debug("onClicked collapsedButtonChevron") if (drawer.isCollapsedStateActive()) { drawer.openTriggered() - FocusController.setRootItem(drawer) } } } @@ -340,6 +338,8 @@ PageType { rootButtonTextTopMargin: 8 rootButtonTextBottomMargin: 8 + enabled: drawer.isOpened + text: ServersModel.defaultServerDefaultContainerName textColor: AmneziaStyle.color.midnightBlack headerText: qsTr("VPN protocol") @@ -358,8 +358,6 @@ PageType { rootWidth: root.width height: 500 // TODO: make calculated - // isFocusable: false // TODO: this is a workaround. Need to remove it - Connections { objectName: "rowLayoutConnections" diff --git a/client/ui/qml/Pages2/PageSettingsApiLanguageList.qml b/client/ui/qml/Pages2/PageSettingsApiLanguageList.qml index 120313cd..dd38b0f4 100644 --- a/client/ui/qml/Pages2/PageSettingsApiLanguageList.qml +++ b/client/ui/qml/Pages2/PageSettingsApiLanguageList.qml @@ -31,72 +31,68 @@ PageType { id: containersRadioButtonGroup } - delegate: Item { + delegate: ColumnLayout { + id: content + implicitWidth: parent.width implicitHeight: content.implicitHeight - ColumnLayout { - id: content + RowLayout { + VerticalRadioButton { + id: containerRadioButton - anchors.fill: parent - - RowLayout { - VerticalRadioButton { - id: containerRadioButton - - Layout.fillWidth: true - Layout.leftMargin: 16 - - text: countryName - - ButtonGroup.group: containersRadioButtonGroup - - imageSource: "qrc:/images/controls/download.svg" - - checked: index === ApiCountryModel.currentIndex - - onClicked: { - if (index !== ApiCountryModel.currentIndex) { - PageController.showBusyIndicator(true) - var prevIndex = ApiCountryModel.currentIndex - ApiCountryModel.currentIndex = index - if (!InstallController.updateServiceFromApi(ServersModel.defaultIndex, countryCode, countryName)) { - ApiCountryModel.currentIndex = prevIndex - } - } - } - - MouseArea { - anchors.fill: containerRadioButton - cursorShape: Qt.PointingHandCursor - enabled: false - } - - Keys.onEnterPressed: { - if (checkable) { - checked = true - } - containerRadioButton.clicked() - } - Keys.onReturnPressed: { - if (checkable) { - checked = true - } - containerRadioButton.clicked() - } - } - - Image { - Layout.rightMargin: 32 - Layout.alignment: Qt.AlignRight - - source: "qrc:/countriesFlags/images/flagKit/" + countryImageCode + ".svg" - } - } - - DividerType { Layout.fillWidth: true + Layout.leftMargin: 16 + + text: countryName + + ButtonGroup.group: containersRadioButtonGroup + + imageSource: "qrc:/images/controls/download.svg" + + checked: index === ApiCountryModel.currentIndex + + onClicked: { + if (index !== ApiCountryModel.currentIndex) { + PageController.showBusyIndicator(true) + var prevIndex = ApiCountryModel.currentIndex + ApiCountryModel.currentIndex = index + if (!InstallController.updateServiceFromApi(ServersModel.defaultIndex, countryCode, countryName)) { + ApiCountryModel.currentIndex = prevIndex + } + } + } + + MouseArea { + anchors.fill: containerRadioButton + cursorShape: Qt.PointingHandCursor + enabled: false + } + + Keys.onEnterPressed: { + if (checkable) { + checked = true + } + containerRadioButton.clicked() + } + Keys.onReturnPressed: { + if (checkable) { + checked = true + } + containerRadioButton.clicked() + } } + + Image { + Layout.rightMargin: 32 + Layout.alignment: Qt.AlignRight + + source: "qrc:/countriesFlags/images/flagKit/" + countryImageCode + ".svg" + } + } + + DividerType { + Layout.fillWidth: true } } } diff --git a/client/ui/qml/Pages2/PageSettingsServerInfo.qml b/client/ui/qml/Pages2/PageSettingsServerInfo.qml index 4f6ab934..01d559f8 100644 --- a/client/ui/qml/Pages2/PageSettingsServerInfo.qml +++ b/client/ui/qml/Pages2/PageSettingsServerInfo.qml @@ -19,24 +19,26 @@ import "../Components" PageType { id: root - property int pageSettingsServerProtocols: 0 - property int pageSettingsServerServices: 1 - property int pageSettingsServerData: 2 - property int pageSettingsApiServerInfo: 3 - property int pageSettingsApiLanguageList: 4 + readonly property int pageSettingsServerProtocols: 0 + readonly property int pageSettingsServerServices: 1 + readonly property int pageSettingsServerData: 2 + readonly property int pageSettingsApiServerInfo: 3 + readonly property int pageSettingsApiLanguageList: 4 - // defaultActiveFocusItem: focusItem + property var server Connections { target: PageController function onGoToPageSettingsServerServices() { - tabBar.currentIndex = root.pageSettingsServerServices + tabBar.setCurrentIndex(root.pageSettingsServerServices) } } SortFilterProxyModel { id: proxyServersModel + objectName: "proxyServersModel" + sourceModel: ServersModel filters: [ ValueFilter { @@ -44,131 +46,110 @@ PageType { value: true } ] + + Component.onCompleted: { + root.server = proxyServersModel.get(0) + } } ColumnLayout { + objectName: "mainLayout" + anchors.fill: parent + anchors.topMargin: 20 - spacing: 16 + spacing: 4 - Repeater { - id: header - model: proxyServersModel + BackButtonType { + id: backButton + objectName: "backButton" - activeFocusOnTab: true - onFocusChanged: { - header.itemAt(0).focusItem.forceActiveFocus() + backButtonFunction: function() { + if (nestedStackView.currentIndex === root.pageSettingsApiServerInfo + && ServersModel.getProcessedServerData("isCountrySelectionAvailable")) { + nestedStackView.currentIndex = root.pageSettingsApiLanguageList + } else { + PageController.closePage() + } + } + } + + HeaderType { + id: headerContent + objectName: "headerContent" + + Layout.fillWidth: true + Layout.leftMargin: 16 + Layout.rightMargin: 16 + Layout.bottomMargin: 10 + + actionButtonImage: nestedStackView.currentIndex === root.pageSettingsApiLanguageList ? "qrc:/images/controls/settings.svg" : "qrc:/images/controls/edit-3.svg" + + headerText: root.server.name + descriptionText: { + if (ServersModel.getProcessedServerData("isServerFromGatewayApi")) { + return ApiServicesModel.getSelectedServiceData("serviceDescription") + } else if (ServersModel.getProcessedServerData("isServerFromTelegramApi")) { + return root.server.serverDescription + } else if (ServersModel.isProcessedServerHasWriteAccess()) { + return root.server.credentialsLogin + " · " + root.server.hostName + } else { + return root.server.hostName + } } - delegate: ColumnLayout { - - property alias focusItem: backButton - - id: content - - Layout.topMargin: 20 - - BackButtonType { - id: backButton - - backButtonFunction: function() { - if (nestedStackView.currentIndex === root.pageSettingsApiServerInfo && - ServersModel.getProcessedServerData("isCountrySelectionAvailable")) { - nestedStackView.currentIndex = root.pageSettingsApiLanguageList - } else { - PageController.closePage() - } - } + actionButtonFunction: function() { + if (nestedStackView.currentIndex === root.pageSettingsApiLanguageList) { + nestedStackView.currentIndex = root.pageSettingsApiServerInfo + } else { + serverNameEditDrawer.openTriggered() } + } + } + + DrawerType2 { + id: serverNameEditDrawer + objectName: "serverNameEditDrawer" + + parent: root + + anchors.fill: parent + expandedHeight: root.height * 0.35 + + expandedStateContent: ColumnLayout { + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.topMargin: 32 + anchors.leftMargin: 16 + anchors.rightMargin: 16 + + TextFieldWithHeaderType { + id: serverName - HeaderType { - id: headerContent Layout.fillWidth: true - Layout.leftMargin: 16 - Layout.rightMargin: 16 - - actionButtonImage: nestedStackView.currentIndex === root.pageSettingsApiLanguageList ? "qrc:/images/controls/settings.svg" : "qrc:/images/controls/edit-3.svg" - - headerText: name - descriptionText: { - if (ServersModel.getProcessedServerData("isServerFromGatewayApi")) { - return ApiServicesModel.getSelectedServiceData("serviceDescription") - } else if (ServersModel.getProcessedServerData("isServerFromTelegramApi")) { - return serverDescription - } else if (ServersModel.isProcessedServerHasWriteAccess()) { - return credentialsLogin + " · " + hostName - } else { - return hostName - } - } - - actionButtonFunction: function() { - if (nestedStackView.currentIndex === root.pageSettingsApiLanguageList) { - nestedStackView.currentIndex = root.pageSettingsApiServerInfo - } else { - serverNameEditDrawer.openTriggered() - } - } + headerText: qsTr("Server name") + textFieldText: root.server.name + textField.maximumLength: 30 + checkEmptyText: true } - DrawerType2 { - id: serverNameEditDrawer + BasicButtonType { + id: saveButton - parent: root + Layout.fillWidth: true - anchors.fill: parent - expandedHeight: root.height * 0.35 + text: qsTr("Save") - onClosed: { - if (!GC.isMobile()) { - headerContent.actionButton.forceActiveFocus() - } - } - - expandedStateContent: ColumnLayout { - anchors.top: parent.top - anchors.left: parent.left - anchors.right: parent.right - anchors.topMargin: 32 - anchors.leftMargin: 16 - anchors.rightMargin: 16 - - Connections { - target: serverNameEditDrawer - enabled: !GC.isMobile() - function onOpened() { - serverName.textField.forceActiveFocus() - } + clickedFunc: function() { + if (serverName.textFieldText === "") { + return } - TextFieldWithHeaderType { - id: serverName - - Layout.fillWidth: true - headerText: qsTr("Server name") - textFieldText: name - textField.maximumLength: 30 - checkEmptyText: true - } - - BasicButtonType { - id: saveButton - - Layout.fillWidth: true - - text: qsTr("Save") - - clickedFunc: function() { - if (serverName.textFieldText === "") { - return - } - - if (serverName.textFieldText !== name) { - name = serverName.textFieldText - } - serverNameEditDrawer.closeTriggered() - } + if (serverName.textFieldText !== root.server.name) { + root.server.name = serverName.textFieldText // TODO(CyAn84): set value to the model } + serverNameEditDrawer.closeTriggered() } } } @@ -189,33 +170,27 @@ PageType { visible: !ServersModel.getProcessedServerData("isServerFromGatewayApi") - activeFocusOnTab: true - onFocusChanged: { - if (activeFocus) { - protocolsTab.forceActiveFocus() - } - } TabButtonType { id: protocolsTab visible: protocolsPage.installedProtocolsCount width: protocolsPage.installedProtocolsCount ? undefined : 0 - isSelected: tabBar.currentIndex === root.pageSettingsServerProtocols + isSelected: TabBar.tabBar.currentIndex === root.pageSettingsServerProtocols text: qsTr("Protocols") - Keys.onReturnPressed: tabBar.currentIndex = root.pageSettingsServerProtocols - Keys.onEnterPressed: tabBar.currentIndex = root.pageSettingsServerProtocols + Keys.onReturnPressed: TabBar.tabBar.setCurrentIndex(root.pageSettingsServerProtocols) + Keys.onEnterPressed: TabBar.tabBar.setCurrentIndex(root.pageSettingsServerProtocols) } TabButtonType { id: servicesTab visible: servicesPage.installedServicesCount width: servicesPage.installedServicesCount ? undefined : 0 - isSelected: tabBar.currentIndex === root.pageSettingsServerServices + isSelected: TabBar.tabBar.currentIndex === root.pageSettingsServerServices text: qsTr("Services") - Keys.onReturnPressed: tabBar.currentIndex = root.pageSettingsServerServices - Keys.onEnterPressed: tabBar.currentIndex = root.pageSettingsServerServices + Keys.onReturnPressed: TabBar.tabBar.setCurrentIndex(root.pageSettingsServerServices) + Keys.onEnterPressed: TabBar.tabBar.setCurrentIndex(root.pageSettingsServerServices) } TabButtonType { @@ -223,24 +198,15 @@ PageType { isSelected: tabBar.currentIndex === root.pageSettingsServerData text: qsTr("Management") - Keys.onReturnPressed: tabBar.currentIndex = root.pageSettingsServerData - Keys.onEnterPressed: tabBar.currentIndex = root.pageSettingsServerData - Keys.onTabPressed: function() { - if (nestedStackView.currentIndex === root.pageSettingsServerProtocols) { - return protocolsPage - } else if (nestedStackView.currentIndex === root.pageSettingsServerProtocols) { - return servicesPage - } else { - return dataPage - } - } + Keys.onReturnPressed: TabBar.tabBar.setCurrentIndex(root.pageSettingsServerData) + Keys.onEnterPressed: TabBar.tabBar.setCurrentIndex(root.pageSettingsServerData) } } StackLayout { id: nestedStackView - Layout.preferredWidth: root.width - Layout.preferredHeight: root.height - tabBar.implicitHeight - header.implicitHeight + + Layout.fillWidth: true currentIndex: ServersModel.getProcessedServerData("isServerFromGatewayApi") ? (ServersModel.getProcessedServerData("isCountrySelectionAvailable") ? @@ -249,36 +215,26 @@ 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) } PageSettingsApiServerInfo { id: apiInfoPage stackView: root.stackView - -// onLastItemTabClickedSignal: lastItemTabClicked(focusItem) } PageSettingsApiLanguageList { id: apiLanguageListPage stackView: root.stackView - -// onLastItemTabClickedSignal: lastItemTabClicked(focusItem) } } diff --git a/client/ui/qml/Pages2/PageSettingsServerProtocols.qml b/client/ui/qml/Pages2/PageSettingsServerProtocols.qml index 0bc1fc7d..ba72957e 100644 --- a/client/ui/qml/Pages2/PageSettingsServerProtocols.qml +++ b/client/ui/qml/Pages2/PageSettingsServerProtocols.qml @@ -21,53 +21,45 @@ PageType { property var installedProtocolsCount - onFocusChanged: settingsContainersListView.forceActiveFocus() - signal lastItemTabClickedSignal() + function resetView() { + settingsContainersListView.positionViewAtBeginning() + } - FlickableType { - id: fl - anchors.top: parent.top - anchors.bottom: parent.bottom - contentHeight: content.implicitHeight + SettingsContainersListView { + id: settingsContainersListView - Column { - id: content + anchors.fill: parent - anchors.top: parent.top - anchors.left: parent.left - anchors.right: parent.right + Connections { + target: ServersModel - SettingsContainersListView { - id: settingsContainersListView - - Connections { - target: ServersModel - - function onProcessedServerIndexChanged() { - settingsContainersListView.updateContainersModelFilters() - } - } - - function updateContainersModelFilters() { - if (ServersModel.isProcessedServerHasWriteAccess()) { - proxyContainersModel.filters = ContainersModelFilters.getWriteAccessProtocolsListFilters() - } else { - proxyContainersModel.filters = ContainersModelFilters.getReadAccessProtocolsListFilters() - } - root.installedProtocolsCount = proxyContainersModel.count - } - - model: SortFilterProxyModel { - id: proxyContainersModel - sourceModel: ContainersModel - sorters: [ - RoleSorter { roleName: "isInstalled"; sortOrder: Qt.DescendingOrder }, - RoleSorter { roleName: "installPageOrder"; sortOrder: Qt.AscendingOrder } - ] - } - - Component.onCompleted: updateContainersModelFilters() + function onProcessedServerIndexChanged() { + settingsContainersListView.updateContainersModelFilters() } } + + function updateContainersModelFilters() { + if (ServersModel.isProcessedServerHasWriteAccess()) { + proxyContainersModel.filters = ContainersModelFilters.getWriteAccessProtocolsListFilters() + } else { + proxyContainersModel.filters = ContainersModelFilters.getReadAccessProtocolsListFilters() + } + root.installedProtocolsCount = proxyContainersModel.count + } + + model: SortFilterProxyModel { + id: proxyContainersModel + sourceModel: ContainersModel + sorters: [ + RoleSorter { roleName: "isInstalled"; sortOrder: Qt.DescendingOrder }, + RoleSorter { roleName: "installPageOrder"; sortOrder: Qt.AscendingOrder } + ] + } + + Component.onCompleted: { + settingsContainersListView.isFocusable = true + settingsContainersListView.interactive = true + updateContainersModelFilters() + } } } diff --git a/client/ui/qml/Pages2/PageSettingsServerServices.qml b/client/ui/qml/Pages2/PageSettingsServerServices.qml index 440fd8db..a46d4051 100644 --- a/client/ui/qml/Pages2/PageSettingsServerServices.qml +++ b/client/ui/qml/Pages2/PageSettingsServerServices.qml @@ -21,52 +21,40 @@ PageType { property var installedServicesCount - onFocusChanged: settingsContainersListView.forceActiveFocus() - signal lastItemTabClickedSignal() + SettingsContainersListView { + id: settingsContainersListView - FlickableType { - id: fl - anchors.top: parent.top - anchors.bottom: parent.bottom - contentHeight: content.implicitHeight + anchors.fill: parent - Column { - id: content + Connections { + target: ServersModel - anchors.top: parent.top - anchors.left: parent.left - anchors.right: parent.right - - SettingsContainersListView { - id: settingsContainersListView - - Connections { - target: ServersModel - - function onProcessedServerIndexChanged() { - settingsContainersListView.updateContainersModelFilters() - } - } - - function updateContainersModelFilters() { - if (ServersModel.isProcessedServerHasWriteAccess()) { - proxyContainersModel.filters = ContainersModelFilters.getWriteAccessServicesListFilters() - } else { - proxyContainersModel.filters = ContainersModelFilters.getReadAccessServicesListFilters() - } - root.installedServicesCount = proxyContainersModel.count - } - - model: SortFilterProxyModel { - id: proxyContainersModel - sourceModel: ContainersModel - sorters: [ - RoleSorter { roleName: "isInstalled"; sortOrder: Qt.DescendingOrder } - ] - } - - Component.onCompleted: updateContainersModelFilters() + function onProcessedServerIndexChanged() { + settingsContainersListView.updateContainersModelFilters() } } + + function updateContainersModelFilters() { + if (ServersModel.isProcessedServerHasWriteAccess()) { + proxyContainersModel.filters = ContainersModelFilters.getWriteAccessServicesListFilters() + } else { + proxyContainersModel.filters = ContainersModelFilters.getReadAccessServicesListFilters() + } + root.installedServicesCount = proxyContainersModel.count + } + + model: SortFilterProxyModel { + id: proxyContainersModel + sourceModel: ContainersModel + sorters: [ + RoleSorter { roleName: "isInstalled"; sortOrder: Qt.DescendingOrder } + ] + } + + Component.onCompleted: { + settingsContainersListView.isFocusable = true + settingsContainersListView.interactive = true + updateContainersModelFilters() + } } } diff --git a/client/ui/qml/Pages2/PageSettingsSplitTunneling.qml b/client/ui/qml/Pages2/PageSettingsSplitTunneling.qml index 4bd4d0a4..f3ae3227 100644 --- a/client/ui/qml/Pages2/PageSettingsSplitTunneling.qml +++ b/client/ui/qml/Pages2/PageSettingsSplitTunneling.qml @@ -169,124 +169,120 @@ PageType { } } } - - // KeyNavigation.tab: { - // return sites.count > 0 ? - // sites : - // searchField.textField - // } } } - FlickableType { - id: fl + ListView { + id: sites + anchors.top: header.bottom anchors.topMargin: 16 - contentHeight: col.implicitHeight + addSiteButton.implicitHeight + addSiteButton.anchors.bottomMargin + addSiteButton.anchors.topMargin + width: parent.width + + height: 200 // TODO: Change to correct height enabled: root.pageEnabled - Column { - id: col - anchors.top: parent.top - anchors.left: parent.left - anchors.right: parent.right + property bool isFocusable: true - ListView { - id: sites - width: parent.width - height: sites.contentItem.height + Keys.onTabPressed: { + FocusController.nextKeyTabItem() + } - model: SortFilterProxyModel { - id: proxySitesModel - sourceModel: SitesModel - filters: [ - AnyOf { - RegExpFilter { - roleName: "url" - pattern: ".*" + searchField.textField.text + ".*" - caseSensitivity: Qt.CaseInsensitive - } - RegExpFilter { - roleName: "ip" - pattern: ".*" + searchField.textField.text + ".*" - caseSensitivity: Qt.CaseInsensitive + Keys.onBacktabPressed: { + FocusController.previousKeyTabItem() + } + + Keys.onUpPressed: { + FocusController.nextKeyUpItem() + } + + Keys.onDownPressed: { + FocusController.nextKeyDownItem() + } + + Keys.onLeftPressed: { + FocusController.nextKeyLeftItem() + } + + Keys.onRightPressed: { + FocusController.nextKeyRightItem() + } + + model: SortFilterProxyModel { + id: proxySitesModel + sourceModel: SitesModel + filters: [ + AnyOf { + RegExpFilter { + roleName: "url" + pattern: ".*" + searchField.textField.text + ".*" + caseSensitivity: Qt.CaseInsensitive + } + RegExpFilter { + roleName: "ip" + pattern: ".*" + searchField.textField.text + ".*" + caseSensitivity: Qt.CaseInsensitive + } + } + ] + } + + clip: true + + delegate: Item { + implicitWidth: sites.width + implicitHeight: delegateContent.implicitHeight + + // onActiveFocusChanged: { + // if (activeFocus) { + // site.rightButton.forceActiveFocus() + // } + // } + + ColumnLayout { + id: delegateContent + + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + + LabelWithButtonType { + id: site + Layout.fillWidth: true + + text: url + descriptionText: ip + rightImageSource: "qrc:/images/controls/trash.svg" + rightImageColor: AmneziaStyle.color.paleGray + + clickedFunction: function() { + var headerText = qsTr("Remove ") + url + "?" + var yesButtonText = qsTr("Continue") + var noButtonText = qsTr("Cancel") + + var yesButtonFunction = function() { + SitesController.removeSite(proxySitesModel.mapToSource(index)) + if (!GC.isMobile()) { + site.rightButton.forceActiveFocus() } } - ] - } - - 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 - - anchors.top: parent.top - anchors.left: parent.left - anchors.right: parent.right - - LabelWithButtonType { - id: site - Layout.fillWidth: true - - text: url - descriptionText: ip - rightImageSource: "qrc:/images/controls/trash.svg" - rightImageColor: AmneziaStyle.color.paleGray - - clickedFunction: function() { - var headerText = qsTr("Remove ") + url + "?" - var yesButtonText = qsTr("Continue") - var noButtonText = qsTr("Cancel") - - 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) + var noButtonFunction = function() { + if (!GC.isMobile()) { + site.rightButton.forceActiveFocus() } } - DividerType {} + showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) } } + + DividerType {} } - } } + Rectangle { anchors.fill: addSiteButton anchors.bottomMargin: -24 @@ -315,7 +311,6 @@ PageType { textFieldPlaceholderText: qsTr("website or IP") buttonImageSource: "qrc:/images/controls/plus.svg" - // KeyNavigation.tab: GC.isMobile() ? focusItem : addSiteButtonImage clickedFunc: function() { PageController.showBusyIndicator(true) @@ -339,8 +334,6 @@ PageType { Keys.onReturnPressed: addSiteButtonImage.clicked() Keys.onEnterPressed: addSiteButtonImage.clicked() - - Keys.onTabPressed: lastItemTabClicked(focusItem) } } @@ -350,12 +343,6 @@ PageType { anchors.fill: parent expandedHeight: parent.height * 0.4375 - onClosed: { - // if (root.defaultActiveFocusItem && !GC.isMobile()) { - // root.defaultActiveFocusItem.forceActiveFocus() - // } - } - expandedStateContent: ColumnLayout { id: moreActionsDrawerContent @@ -363,20 +350,6 @@ PageType { anchors.left: parent.left anchors.right: parent.right - Connections { - target: moreActionsDrawer - - function onOpened() { - focusItem1.forceActiveFocus() - } - - function onActiveFocusChanged() { - if (!GC.isMobile()) { - focusItem1.forceActiveFocus() - } - } - } - Header2Type { Layout.fillWidth: true Layout.margins: 16 @@ -433,23 +406,9 @@ PageType { anchors.fill: parent expandedHeight: parent.height * 0.4375 - onClosed: { - if (!GC.isMobile()) { - moreActionsDrawer.forceActiveFocus() - } - } - expandedStateContent: Item { implicitHeight: importSitesDrawer.expandedHeight - Connections { - target: importSitesDrawer - enabled: !GC.isMobile() - function onOpened() { - focusItem2.forceActiveFocus() - } - } - BackButtonType { id: importSitesDrawerBackButton diff --git a/client/ui/qml/Pages2/PageShare.qml b/client/ui/qml/Pages2/PageShare.qml index cef516df..3cb8f660 100644 --- a/client/ui/qml/Pages2/PageShare.qml +++ b/client/ui/qml/Pages2/PageShare.qml @@ -236,11 +236,6 @@ 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 @@ -253,7 +248,6 @@ PageType { PageController.goToPage(PageEnum.PageShareFullAccess) shareFullAccessDrawer.closeTriggered() } - } } } @@ -694,38 +688,38 @@ PageType { } clip: true - interactive: false + // 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) - } - } - } + // 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() - } - } + // 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 - } - } - } + // 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 From f3df9eb5f51697550eed8d4282db68dc30c1d36f Mon Sep 17 00:00:00 2001 From: Cyril Anisimov Date: Sun, 13 Oct 2024 21:19:15 +0200 Subject: [PATCH 09/40] add ListViewFocusController --- client/ui/controllers/focusController.cpp | 324 +-------------- client/ui/controllers/focusController.h | 6 + .../controllers/listViewFocusController.cpp | 385 ++++++++++++++++++ .../ui/controllers/listViewFocusController.h | 74 ++++ 4 files changed, 478 insertions(+), 311 deletions(-) create mode 100644 client/ui/controllers/listViewFocusController.cpp create mode 100644 client/ui/controllers/listViewFocusController.h diff --git a/client/ui/controllers/focusController.cpp b/client/ui/controllers/focusController.cpp index 16133341..27be7d2a 100644 --- a/client/ui/controllers/focusController.cpp +++ b/client/ui/controllers/focusController.cpp @@ -1,310 +1,11 @@ #include "focusController.h" +#include "listViewFocusController.h" + #include #include -#include -#include -#include -#include -bool isVisible(QObject* item) -{ - const auto res = item->property("visible").toBool(); - // qDebug() << "==>> " << (res ? "VISIBLE" : "NOT visible") << item; - return res; -} - -bool isFocusable(QObject* item) -{ - const auto res = item->property("isFocusable").toBool(); - return res; -} - -QRectF getItemCoordsOnScene(QQuickItem* item) // TODO: remove? -{ - if (!item) return {}; - return item->mapRectToScene(item->childrenRect()); -} - -QPointF getItemCenterPointOnScene(QQuickItem* item) -{ - const auto x0 = item->x() + (item->width() / 2); - const auto y0 = item->y() + (item->height() / 2); - return item->parentItem()->mapToScene(QPointF{x0, y0}); -} - -bool isLess(QObject* item1, QObject* item2) -{ - const auto p1 = getItemCenterPointOnScene(qobject_cast(item1)); - const auto p2 = getItemCenterPointOnScene(qobject_cast(item2)); - return (p1.y() == p2.y()) ? (p1.x() < p2.x()) : (p1.y() < p2.y()); -} - -bool isMore(QObject* item1, QObject* item2) -{ - return !isLess(item1, item2); -} - -bool isListView(QObject* item) -{ - return item->inherits("QQuickListView"); -} - -bool isOnTheScene(QObject* object) -{ - QQuickItem* item = qobject_cast(object); - if (!item) { - qWarning() << "Couldn't recognize object as item"; - return false; - } - - if (!item->isVisible()) { - // qDebug() << "===>> The item is not visible: " << item; - return false; - } - - QRectF itemRect = item->mapRectToScene(item->childrenRect()); - - QQuickWindow* window = item->window(); - if (!window) { - qWarning() << "Couldn't get the window on the Scene check"; - return false; - } - - const auto contentItem = window->contentItem(); - if (!contentItem) { - qWarning() << "Couldn't get the content item on the Scene check"; - return false; - } - QRectF windowRect = contentItem->childrenRect(); - const auto res = (windowRect.contains(itemRect) || isListView(item)); - // qDebug() << (res ? "===>> item is inside the Scene" : "===>> ITEM IS OUTSIDE THE SCENE") << " itemRect: " << itemRect << "; windowRect: " << windowRect; - return res; -} - -bool isEnabled(QObject* obj) -{ - const auto item = qobject_cast(obj); - return item && item->isEnabled(); -} - -QQuickItem* getPageOfItem(QQuickItem* item) // TODO: remove? -{ - if(!item) { - qWarning() << "item is null"; - return {}; - } - const auto pagePattern = QString::fromLatin1("Page"); - QString className{item->metaObject()->className()}; - const auto isPage = className.contains(pagePattern, Qt::CaseSensitive); - if(isPage) { - return item; - } else { - return getPageOfItem(item->parentItem()); - } -} - -QList getSubChain(QObject* item) -{ - QList res; - if (!item) { - qDebug() << "null top item"; - return res; - } - const auto children = item->children(); - for(const auto child : children) { - if (child - && isFocusable(child) - && (isOnTheScene(child)) - && isEnabled(child) - ) { - res.append(child); - } else { - res.append(getSubChain(child)); - } - } - return res; -} - -template -void printItems(const T& items, QObject* current_item) -{ - for(const auto& item : items) { - QQuickItem* i = qobject_cast(item); - QPointF coords {getItemCenterPointOnScene(i)}; - QString prefix = current_item == i ? "==>" : " "; - qDebug() << prefix << " Item: " << i << " with coords: " << coords; - } -} - -/*! - * \brief The ListViewFocusController class manages the focus of elements in ListView - * \details This class object moving focus to ListView's controls since ListView stores - * it's data implicitly and it could be got one by one. - * - * This class was made to store as less as possible data getting it from QML - * when it's needed. - */ -class ListViewFocusController : public QObject -{ -public: - explicit ListViewFocusController(QQuickItem* listView, QObject* parent = nullptr); - ~ListViewFocusController(); - - void incrementIndex(); - void decrementIndex(); - void positionViewAtIndex(); - void focusNextItem(); - void focusPreviousItem(); - void resetFocusChain(); - bool isListViewFirstFocusItem(); - bool isDelegateFirstFocusItem(); - bool isListViewLastFocusItem(); - bool isDelegateLastFocusItem(); - bool isReturnNeeded(); - void viewToBegin(); - -private: - int size() const; - int currentIndex() const; - QQuickItem* itemAtIndex(const int index); - QQuickItem* currentDelegate(); - QQuickItem* focusedItem(); - - QQuickItem* m_listView; - QList m_focusChain; - QQuickItem* m_focusedItem; - qsizetype m_focusedItemIndex; - qsizetype m_delegateIndex; - bool m_isReturnNeeded; -}; - -ListViewFocusController::ListViewFocusController(QQuickItem* listView, QObject* parent) - : QObject{parent} - , m_listView{listView} - , m_focusChain{} - , m_focusedItem{nullptr} - , m_focusedItemIndex{-1} - , m_delegateIndex{0} - , m_isReturnNeeded{false} -{ -} - -ListViewFocusController::~ListViewFocusController() -{ - -} - -void ListViewFocusController::positionViewAtIndex() -{ - QMetaObject::invokeMethod(m_listView, "positionViewAtIndex", - Q_ARG(int, m_delegateIndex), // Index - Q_ARG(int, 2)); // PositionMode (0 = Visible) -} - -int ListViewFocusController::size() const -{ - return m_listView->property("count").toInt(); -} - -int ListViewFocusController::currentIndex() const -{ - return m_delegateIndex; -} - -void ListViewFocusController::incrementIndex() -{ - m_delegateIndex++; -} - -void ListViewFocusController::decrementIndex() -{ - m_delegateIndex--; -} - -QQuickItem* ListViewFocusController::itemAtIndex(const int index) -{ - QQuickItem* item{nullptr}; - - QMetaObject::invokeMethod(m_listView, "itemAtIndex", - Q_RETURN_ARG(QQuickItem*, item), - Q_ARG(int, index)); - - return item; -} - -QQuickItem* ListViewFocusController::currentDelegate() -{ - return itemAtIndex(m_delegateIndex); -} - -QQuickItem* ListViewFocusController::focusedItem() -{ - return m_focusedItem; -} - -void ListViewFocusController::focusNextItem() -{ - if (m_focusChain.empty()) { - qDebug() << "Empty focusChain with current delegate: " << currentDelegate() << "Scanning for elements..."; - m_focusChain = getSubChain(currentDelegate()); - } - if (m_focusChain.empty()) { - qWarning() << "No elements found. Returning from ListView..."; - m_isReturnNeeded = true; - return; - } - m_focusedItemIndex++; - m_focusedItem = qobject_cast(m_focusChain.at(m_focusedItemIndex)); - qDebug() << "==>> Focused Item: " << m_focusedItem << " with Index: " << m_focusedItemIndex; - m_focusedItem->forceActiveFocus(); -} - -void ListViewFocusController::focusPreviousItem() -{ - // TODO: implement -} - -void ListViewFocusController::resetFocusChain() -{ - m_focusChain.clear(); - m_focusedItem = nullptr; - m_focusedItemIndex = -1; -} - -bool ListViewFocusController::isDelegateFirstFocusItem() -{ - return m_focusedItem && (m_focusedItem == m_focusChain.first()); -} - -bool ListViewFocusController::isDelegateLastFocusItem() -{ - return m_focusedItem && (m_focusedItem == m_focusChain.last()); -} - -bool ListViewFocusController::isListViewFirstFocusItem() -{ - return (m_delegateIndex == 0) && isDelegateFirstFocusItem(); -} - -bool ListViewFocusController::isListViewLastFocusItem() -{ - return (m_delegateIndex == size() - 1) && isDelegateLastFocusItem(); -} - -bool ListViewFocusController::isReturnNeeded() -{ - return m_isReturnNeeded; -} - -void ListViewFocusController::viewToBegin() -{ - QMetaObject::invokeMethod(m_listView, "positionViewAtBeginning", Qt::AutoConnection); -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// - FocusController::FocusController(QQmlApplicationEngine* engine, QObject *parent) : QObject{parent} , m_engine{engine} @@ -315,13 +16,11 @@ FocusController::FocusController(QQmlApplicationEngine* engine, QObject *parent) , m_defaultFocusItem{QSharedPointer()} , m_lvfc{nullptr} { - // connect(this, &FocusController::rootItemChanged, this, &FocusController::onReload); QObject::connect(m_engine.get(), &QQmlApplicationEngine::objectCreated, this, [this](QObject *object, const QUrl &url){ - qDebug() << "===>> () CREATED " << object << " : " << url; QQuickItem* newDefaultFocusItem = object->findChild("defaultFocusItem"); if(newDefaultFocusItem && m_defaultFocusItem != newDefaultFocusItem) { m_defaultFocusItem.reset(newDefaultFocusItem); - qDebug() << "===>> [] NEW DEFAULT FOCUS ITEM " << m_defaultFocusItem; + qDebug() << "===>> NEW DEFAULT FOCUS ITEM " << m_defaultFocusItem; } }); } @@ -363,8 +62,11 @@ void FocusController::nextItem(bool isForwardOrder) m_lvfc = new ListViewFocusController(m_focusedItem, this); if(isForwardOrder) { m_lvfc->viewToBegin(); + m_lvfc->nextElement(); focusNextListViewItem(); } else { + m_lvfc->viewToEnd(); + m_lvfc->previousElement(); focusPreviousListViewItem(); } return; @@ -390,15 +92,15 @@ void FocusController::focusNextListViewItem() { m_lvfc->focusNextItem(); - if (m_lvfc->isListViewLastFocusItem() || m_lvfc->isReturnNeeded()) { + if (m_lvfc->isLastFocusItemInListView() || m_lvfc->isReturnNeeded()) { qDebug() << "===>> [Last item in ListView was reached]"; delete m_lvfc; m_lvfc = nullptr; - } else if (m_lvfc->isDelegateLastFocusItem()) { + } else if (m_lvfc->isLastFocusItemInDelegate()) { qDebug() << "===>> [End of delegate elements was reached. Going to the next delegate]"; m_lvfc->resetFocusChain(); - m_lvfc->incrementIndex(); - m_lvfc->positionViewAtIndex(); + m_lvfc->nextElement(); + m_lvfc->viewAtCurrentIndex(); } } @@ -406,13 +108,13 @@ void FocusController::focusPreviousListViewItem() { m_lvfc->focusPreviousItem(); - if (m_lvfc->isListViewFirstFocusItem() || m_lvfc->isReturnNeeded()) { + if (m_lvfc->isFirstFocusItemInListView() || m_lvfc->isReturnNeeded()) { delete m_lvfc; m_lvfc = nullptr; - } else if (m_lvfc->isDelegateFirstFocusItem()) { + } else if (m_lvfc->isFirstFocusItemInDelegate()) { m_lvfc->resetFocusChain(); m_lvfc->decrementIndex(); - m_lvfc->positionViewAtIndex(); + m_lvfc->viewAtCurrentIndex(); } } diff --git a/client/ui/controllers/focusController.h b/client/ui/controllers/focusController.h index 38869a69..e6368e98 100644 --- a/client/ui/controllers/focusController.h +++ b/client/ui/controllers/focusController.h @@ -10,6 +10,12 @@ class QQuickItem; class QQmlApplicationEngine; class ListViewFocusController; +/*! + * \brief The FocusController class makes focus control more straightforward + * \details Focus is handled only for visible and enabled items which have + * `isFocused` property from top left to bottom right. + * \note There are items handled differently (e.g. ListView) + */ class FocusController : public QObject { Q_OBJECT diff --git a/client/ui/controllers/listViewFocusController.cpp b/client/ui/controllers/listViewFocusController.cpp new file mode 100644 index 00000000..a4b13fb4 --- /dev/null +++ b/client/ui/controllers/listViewFocusController.cpp @@ -0,0 +1,385 @@ +#include "listViewFocusController.h" + +#include +#include +#include +#include +#include + + +bool isVisible(QObject* item) +{ + const auto res = item->property("visible").toBool(); + // qDebug() << "==>> " << (res ? "VISIBLE" : "NOT visible") << item; + return res; +} + +bool isFocusable(QObject* item) +{ + const auto res = item->property("isFocusable").toBool(); + return res; +} + +QPointF getItemCenterPointOnScene(QQuickItem* item) +{ + const auto x0 = item->x() + (item->width() / 2); + const auto y0 = item->y() + (item->height() / 2); + return item->parentItem()->mapToScene(QPointF{x0, y0}); +} + +bool isLess(QObject* item1, QObject* item2) +{ + const auto p1 = getItemCenterPointOnScene(qobject_cast(item1)); + const auto p2 = getItemCenterPointOnScene(qobject_cast(item2)); + return (p1.y() == p2.y()) ? (p1.x() < p2.x()) : (p1.y() < p2.y()); +} + +bool isMore(QObject* item1, QObject* item2) +{ + return !isLess(item1, item2); +} + +bool isListView(QObject* item) +{ + return item->inherits("QQuickListView"); +} + +bool isOnTheScene(QObject* object) +{ + QQuickItem* item = qobject_cast(object); + if (!item) { + qWarning() << "Couldn't recognize object as item"; + return false; + } + + if (!item->isVisible()) { + // qDebug() << "===>> The item is not visible: " << item; + return false; + } + + QRectF itemRect = item->mapRectToScene(item->childrenRect()); + + QQuickWindow* window = item->window(); + if (!window) { + qWarning() << "Couldn't get the window on the Scene check"; + return false; + } + + const auto contentItem = window->contentItem(); + if (!contentItem) { + qWarning() << "Couldn't get the content item on the Scene check"; + return false; + } + QRectF windowRect = contentItem->childrenRect(); + const auto res = (windowRect.contains(itemRect) || isListView(item)); + // qDebug() << (res ? "===>> item is inside the Scene" : "===>> ITEM IS OUTSIDE THE SCENE") << " itemRect: " << itemRect << "; windowRect: " << windowRect; + return res; +} + +bool isEnabled(QObject* obj) +{ + const auto item = qobject_cast(obj); + return item && item->isEnabled(); +} + +QList getSubChain(QObject* object) +{ + QList res; + if (!object) { + qDebug() << "The object is NULL"; + return res; + } + + const auto children = object->children(); + + for(const auto child : children) { + if (child + && isFocusable(child) + && isOnTheScene(child) + && isEnabled(child) + ) { + res.append(child); + } else { + res.append(getSubChain(child)); + } + } + return res; +} + +void printItems(const QList& items, QObject* current_item) +{ + for(const auto& item : items) { + QQuickItem* i = qobject_cast(item); + QPointF coords {getItemCenterPointOnScene(i)}; + QString prefix = current_item == i ? "==>" : " "; + qDebug() << prefix << " Item: " << i << " with coords: " << coords; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +ListViewFocusController::ListViewFocusController(QQuickItem* listView, QObject* parent) + : QObject{parent} + , m_listView{listView} + , m_focusChain{} + , m_currentSection{Section::Default} + , m_header{nullptr} + , m_footer{nullptr} + , m_focusedItem{nullptr} + , m_focusedItemIndex{-1} + , m_delegateIndex{0} + , m_isReturnNeeded{false} + , m_currentSectionString {"Default", "Header", "Delegate", "Footer"} +{ + QVariant headerItemProperty = m_listView->property("headerItem"); + m_header = headerItemProperty.canConvert() ? headerItemProperty.value() : nullptr; + + QVariant footerItemProperty = m_listView->property("footerItem"); + m_footer = footerItemProperty.canConvert() ? footerItemProperty.value() : nullptr; +} + +ListViewFocusController::~ListViewFocusController() +{ + +} + +void ListViewFocusController::viewAtCurrentIndex() +{ + switch(m_currentSection) { + case Section::Default: + case Section::Header: { + QMetaObject::invokeMethod(m_listView, "positionViewAtBeginning"); + break; + } + case Section::Delegate: { + QMetaObject::invokeMethod(m_listView, "positionViewAtIndex", + Q_ARG(int, m_delegateIndex), // Index + Q_ARG(int, 2)); // PositionMode (0 = Visible) + break; + } + case Section::Footer: { + QMetaObject::invokeMethod(m_listView, "positionViewAtEnd"); + break; + } + } + +} + +int ListViewFocusController::size() const +{ + return m_listView->property("count").toInt(); +} + +int ListViewFocusController::currentIndex() const +{ + return m_delegateIndex; +} + +void ListViewFocusController::nextElement() +{ + qDebug() << "===>> Current section: " << m_currentSectionString.at(static_cast(m_currentSection)); + switch(m_currentSection) { + case Section::Default: { + if(m_header) { + m_currentSection = Section::Header; + break; + } + } + case Section::Header: { + if (size() > 0) { + m_currentSection = Section::Delegate; + break; + } + } + case Section::Delegate: + if (m_delegateIndex < (size() - 1)) { + m_delegateIndex++; + break; + } else if (m_footer) { + m_currentSection = Section::Footer; + break; + } + case Section::Footer: { + m_isReturnNeeded = true; + m_currentSection = Section::Default; + break; + } + default: { + qCritical() << "Current section is invalid!"; + break; + } + } + +} + +void ListViewFocusController::previousElement() +{ + switch(m_currentSection) { + case Section::Default: { + if(m_footer) { + m_currentSection = Section::Footer; + break; + } + } + case Section::Footer: { + if (size() > 0) { + m_currentSection = Section::Delegate; + m_focusedItemIndex = size() - 1; // workarount to default value == -1 + break; + } + } + case Section::Delegate: { + if (m_delegateIndex > 0) { + m_delegateIndex--; + break; + } else if (m_header) { + m_currentSection = Section::Header; + break; + } + } + case Section::Header: { + m_isReturnNeeded = true; + m_currentSection = Section::Default; + break; + } + default: { + qCritical() << "Current section is invalid!"; + break; + } + } +} + +void ListViewFocusController::decrementIndex() +{ + m_delegateIndex--; +} + +QQuickItem* ListViewFocusController::itemAtIndex(const int index) +{ + QQuickItem* item{nullptr}; + + QMetaObject::invokeMethod(m_listView, "itemAtIndex", + Q_RETURN_ARG(QQuickItem*, item), + Q_ARG(int, index)); + + return item; +} + +QQuickItem* ListViewFocusController::currentDelegate() +{ + QQuickItem* result{nullptr}; + + switch(m_currentSection) { + case Section::Default: { + qWarning() << "No elements..."; + break; + } + case Section::Header: { + result = m_header; + break; + } + case Section::Delegate: { + result = itemAtIndex(m_delegateIndex); + break; + } + case Section::Footer: { + result = m_footer; + break; + } + } + return result; +} + +QQuickItem* ListViewFocusController::focusedItem() +{ + return m_focusedItem; +} + +void ListViewFocusController::focusNextItem() +{ + if (m_isReturnNeeded) { + return; + } + + if (m_focusChain.empty()) { + qDebug() << "Empty focusChain with current delegate: " << currentDelegate() << "Scanning for elements..."; + m_focusChain = getSubChain(currentDelegate()); + } + if (m_focusChain.empty()) { + qWarning() << "No elements found. Returning from ListView..."; + nextElement(); + focusNextItem(); + return; + } + m_focusedItemIndex++; + m_focusedItem = qobject_cast(m_focusChain.at(m_focusedItemIndex)); + qDebug() << "==>> Focused Item: " << m_focusedItem << " with Index: " << m_focusedItemIndex; + m_focusedItem->forceActiveFocus(); +} + +void ListViewFocusController::focusPreviousItem() +{ + if (m_isReturnNeeded) { + return; + } + + if (m_focusChain.empty()) { + qDebug() << "Empty focusChain with current delegate: " << currentDelegate() << "Scanning for elements..."; + m_focusChain = getSubChain(currentDelegate()); + } + if (m_focusChain.empty()) { + qWarning() << "No elements found. Returning from ListView..."; + previousElement(); + focusPreviousItem(); + return; + } + m_focusedItemIndex--; + m_focusedItem = qobject_cast(m_focusChain.at(m_focusedItemIndex)); + qDebug() << "==>> Focused Item: " << m_focusedItem << " with Index: " << m_focusedItemIndex; + m_focusedItem->forceActiveFocus(); +} + +void ListViewFocusController::resetFocusChain() +{ + m_focusChain.clear(); + m_focusedItem = nullptr; + m_focusedItemIndex = -1; +} + +bool ListViewFocusController::isFirstFocusItemInDelegate() +{ + return m_focusedItem && (m_focusedItem == m_focusChain.first()); +} + +bool ListViewFocusController::isLastFocusItemInDelegate() +{ + return m_focusedItem && (m_focusedItem == m_focusChain.last()); +} + +bool ListViewFocusController::isFirstFocusItemInListView() +{ + return (m_delegateIndex == 0) && isFirstFocusItemInDelegate(); +} + +bool ListViewFocusController::isLastFocusItemInListView() +{ + bool isLastSection = (m_footer && m_currentSection == Section::Footer) + || (!m_footer && (m_currentSection == Section::Delegate) && (m_delegateIndex == size() - 1)) + || (m_header && (m_currentSection == Section::Header) && (size() <= 0) && !m_footer); + return isLastSection && isLastFocusItemInDelegate(); +} + +bool ListViewFocusController::isReturnNeeded() +{ + return m_isReturnNeeded; +} + +void ListViewFocusController::viewToBegin() +{ + QMetaObject::invokeMethod(m_listView, "positionViewAtBeginning", Qt::AutoConnection); +} + +void ListViewFocusController::viewToEnd() +{ + QMetaObject::invokeMethod(m_listView, "positionViewAtEnd", Qt::AutoConnection); +} diff --git a/client/ui/controllers/listViewFocusController.h b/client/ui/controllers/listViewFocusController.h new file mode 100644 index 00000000..e7d83bbf --- /dev/null +++ b/client/ui/controllers/listViewFocusController.h @@ -0,0 +1,74 @@ +#ifndef LISTVIEWFOCUSCONTROLLER_H +#define LISTVIEWFOCUSCONTROLLER_H + +#include +#include +#include +#include + + +bool isListView(QObject* item); +bool isMore(QObject* item1, QObject* item2); +bool isLess(QObject* item1, QObject* item2); +QList getSubChain(QObject* object); + +void printItems(const QList& items, QObject* current_item); + +/*! + * \brief The ListViewFocusController class manages the focus of elements in ListView + * \details This class object moving focus to ListView's controls since ListView stores + * it's data implicitly and it could be got one by one. + * + * This class was made to store as less as possible data getting it from QML + * when it's needed. + */ +class ListViewFocusController : public QObject +{ + // Q_OBJECT +public: + explicit ListViewFocusController(QQuickItem* listView, QObject* parent = nullptr); + ~ListViewFocusController(); + + void nextElement(); + void previousElement(); + void decrementIndex(); + void focusNextItem(); + void focusPreviousItem(); + void resetFocusChain(); + bool isFirstFocusItemInListView(); + bool isFirstFocusItemInDelegate(); + bool isLastFocusItemInListView(); + bool isLastFocusItemInDelegate(); + bool isReturnNeeded(); + void viewToBegin(); + void viewToEnd(); + void viewAtCurrentIndex(); + +private: + enum class Section { + Default, + Header, + Delegate, + Footer, + }; + + int size() const; + int currentIndex() const; + QQuickItem* itemAtIndex(const int index); + QQuickItem* currentDelegate(); + QQuickItem* focusedItem(); + + QQuickItem* m_listView; + QList m_focusChain; + Section m_currentSection; + QQuickItem* m_header; + QQuickItem* m_footer; + QQuickItem* m_focusedItem; // Pointer to focused item on Delegate + qsizetype m_focusedItemIndex; + qsizetype m_delegateIndex; + bool m_isReturnNeeded; + + QList m_currentSectionString; +}; + +#endif // LISTVIEWFOCUSCONTROLLER_H From 89ac585e07ddab7a2f7512eefefbc8dc77c030bd Mon Sep 17 00:00:00 2001 From: Cyril Anisimov Date: Thu, 17 Oct 2024 03:52:57 +0200 Subject: [PATCH 10/40] fix ListView navigation --- client/ui/controllers/focusController.cpp | 108 ++++++++++++++---- client/ui/controllers/focusController.h | 9 +- .../controllers/listViewFocusController.cpp | 69 +++-------- .../ui/controllers/listViewFocusController.h | 7 +- client/ui/qml/main2.qml | 5 +- 5 files changed, 117 insertions(+), 81 deletions(-) diff --git a/client/ui/controllers/focusController.cpp b/client/ui/controllers/focusController.cpp index 27be7d2a..e70391a8 100644 --- a/client/ui/controllers/focusController.cpp +++ b/client/ui/controllers/focusController.cpp @@ -6,6 +6,67 @@ #include +bool isListView(QObject* item) +{ + return item->inherits("QQuickListView"); +} + +bool isOnTheScene(QObject* object) +{ + QQuickItem* item = qobject_cast(object); + if (!item) { + qWarning() << "Couldn't recognize object as item"; + return false; + } + + if (!item->isVisible()) { + // qDebug() << "===>> The item is not visible: " << item; + return false; + } + + QRectF itemRect = item->mapRectToScene(item->childrenRect()); + + QQuickWindow* window = item->window(); + if (!window) { + qWarning() << "Couldn't get the window on the Scene check"; + return false; + } + + const auto contentItem = window->contentItem(); + if (!contentItem) { + qWarning() << "Couldn't get the content item on the Scene check"; + return false; + } + QRectF windowRect = contentItem->childrenRect(); + const auto res = (windowRect.contains(itemRect) || isListView(item)); + // qDebug() << (res ? "===>> item is inside the Scene" : "===>> ITEM IS OUTSIDE THE SCENE") << " itemRect: " << itemRect << "; windowRect: " << windowRect; + return res; +} + +QList getSubChain(QObject* object) +{ + QList res; + if (!object) { + qDebug() << "The object is NULL"; + return res; + } + + const auto children = object->children(); + + for(const auto child : children) { + if (child + && isFocusable(child) + && isOnTheScene(child) + && isEnabled(child) + ) { + res.append(child); + } else { + res.append(getSubChain(child)); + } + } + return res; +} + FocusController::FocusController(QQmlApplicationEngine* engine, QObject *parent) : QObject{parent} , m_engine{engine} @@ -25,16 +86,16 @@ FocusController::FocusController(QQmlApplicationEngine* engine, QObject *parent) }); } -void FocusController::nextItem(bool isForwardOrder) +void FocusController::nextItem(Direction direction) { if (m_lvfc) { - isForwardOrder ? focusNextListViewItem() : focusPreviousListViewItem(); + direction == Direction::Forward ? focusNextListViewItem() : focusPreviousListViewItem(); qDebug() << "===>> [handling the ListView]"; return; } - reload(isForwardOrder); + reload(direction); if(m_focusChain.empty()) { qWarning() << "There are no items to navigate"; @@ -60,13 +121,13 @@ void FocusController::nextItem(bool isForwardOrder) if(isListView(m_focusedItem)) { qDebug() << "===>> [Found ListView]"; m_lvfc = new ListViewFocusController(m_focusedItem, this); - if(isForwardOrder) { + if(direction == Direction::Forward) { m_lvfc->viewToBegin(); - m_lvfc->nextElement(); + m_lvfc->nextDelegate(); focusNextListViewItem(); } else { m_lvfc->viewToEnd(); - m_lvfc->previousElement(); + m_lvfc->previousDelegate(); focusPreviousListViewItem(); } return; @@ -90,62 +151,67 @@ void FocusController::nextItem(bool isForwardOrder) void FocusController::focusNextListViewItem() { - m_lvfc->focusNextItem(); - if (m_lvfc->isLastFocusItemInListView() || m_lvfc->isReturnNeeded()) { - qDebug() << "===>> [Last item in ListView was reached]"; + qDebug() << "===>> [Last item in ListView was reached. Going to the NEXT element after ListView]"; delete m_lvfc; m_lvfc = nullptr; + nextItem(Direction::Forward); + return; } else if (m_lvfc->isLastFocusItemInDelegate()) { qDebug() << "===>> [End of delegate elements was reached. Going to the next delegate]"; m_lvfc->resetFocusChain(); - m_lvfc->nextElement(); + m_lvfc->nextDelegate(); m_lvfc->viewAtCurrentIndex(); } + + m_lvfc->focusNextItem(); } void FocusController::focusPreviousListViewItem() { - m_lvfc->focusPreviousItem(); - if (m_lvfc->isFirstFocusItemInListView() || m_lvfc->isReturnNeeded()) { + qDebug() << "===>> [First item in ListView was reached. Going to the PREVIOUS element after ListView]"; delete m_lvfc; m_lvfc = nullptr; + nextItem(Direction::Backward); + return; } else if (m_lvfc->isFirstFocusItemInDelegate()) { m_lvfc->resetFocusChain(); - m_lvfc->decrementIndex(); + m_lvfc->previousDelegate(); m_lvfc->viewAtCurrentIndex(); } + + m_lvfc->focusPreviousItem(); } void FocusController::nextKeyTabItem() { - nextItem(true); + nextItem(Direction::Forward); } void FocusController::previousKeyTabItem() { - nextItem(false); + nextItem(Direction::Backward); } void FocusController::nextKeyUpItem() { - nextItem(false); + nextItem(Direction::Backward); } void FocusController::nextKeyDownItem() { - nextItem(true); + nextItem(Direction::Forward); } void FocusController::nextKeyLeftItem() { - nextItem(false); + nextItem(Direction::Backward); } void FocusController::nextKeyRightItem() { - nextItem(true); + nextItem(Direction::Forward); } void FocusController::setFocusOnDefaultItem() @@ -154,7 +220,7 @@ void FocusController::setFocusOnDefaultItem() m_defaultFocusItem->forceActiveFocus(); } -void FocusController::reload(bool isForwardOrder) +void FocusController::reload(Direction direction) { m_focusChain.clear(); @@ -174,7 +240,7 @@ void FocusController::reload(bool isForwardOrder) m_focusChain.append(getSubChain(rootObject)); - std::sort(m_focusChain.begin(), m_focusChain.end(), isForwardOrder? isLess : isMore); + std::sort(m_focusChain.begin(), m_focusChain.end(), direction == Direction::Forward ? isLess : isMore); if (m_focusChain.empty()) { qWarning() << "Focus chain is empty!"; diff --git a/client/ui/controllers/focusController.h b/client/ui/controllers/focusController.h index e6368e98..a4851032 100644 --- a/client/ui/controllers/focusController.h +++ b/client/ui/controllers/focusController.h @@ -35,10 +35,15 @@ public: Q_INVOKABLE void dropRootObject(QObject* object); private: - void nextItem(bool isForwardOrder); + enum class Direction { + Forward, + Backward, + }; + + void nextItem(Direction direction); void focusNextListViewItem(); void focusPreviousListViewItem(); - void reload(bool isForwardOrder); + void reload(Direction direction); QSharedPointer m_engine; // Pointer to engine to get root object QList m_focusChain; // List of current objects to be focused diff --git a/client/ui/controllers/listViewFocusController.cpp b/client/ui/controllers/listViewFocusController.cpp index a4b13fb4..b899dfe6 100644 --- a/client/ui/controllers/listViewFocusController.cpp +++ b/client/ui/controllers/listViewFocusController.cpp @@ -10,7 +10,6 @@ bool isVisible(QObject* item) { const auto res = item->property("visible").toBool(); - // qDebug() << "==>> " << (res ? "VISIBLE" : "NOT visible") << item; return res; } @@ -39,50 +38,13 @@ bool isMore(QObject* item1, QObject* item2) return !isLess(item1, item2); } -bool isListView(QObject* item) -{ - return item->inherits("QQuickListView"); -} - -bool isOnTheScene(QObject* object) -{ - QQuickItem* item = qobject_cast(object); - if (!item) { - qWarning() << "Couldn't recognize object as item"; - return false; - } - - if (!item->isVisible()) { - // qDebug() << "===>> The item is not visible: " << item; - return false; - } - - QRectF itemRect = item->mapRectToScene(item->childrenRect()); - - QQuickWindow* window = item->window(); - if (!window) { - qWarning() << "Couldn't get the window on the Scene check"; - return false; - } - - const auto contentItem = window->contentItem(); - if (!contentItem) { - qWarning() << "Couldn't get the content item on the Scene check"; - return false; - } - QRectF windowRect = contentItem->childrenRect(); - const auto res = (windowRect.contains(itemRect) || isListView(item)); - // qDebug() << (res ? "===>> item is inside the Scene" : "===>> ITEM IS OUTSIDE THE SCENE") << " itemRect: " << itemRect << "; windowRect: " << windowRect; - return res; -} - bool isEnabled(QObject* obj) { const auto item = qobject_cast(obj); return item && item->isEnabled(); } -QList getSubChain(QObject* object) +QList getItemsChain(QObject* object) { QList res; if (!object) { @@ -95,12 +57,12 @@ QList getSubChain(QObject* object) for(const auto child : children) { if (child && isFocusable(child) - && isOnTheScene(child) && isEnabled(child) + && isVisible(child) ) { res.append(child); } else { - res.append(getSubChain(child)); + res.append(getItemsChain(child)); } } return res; @@ -175,13 +137,13 @@ int ListViewFocusController::currentIndex() const return m_delegateIndex; } -void ListViewFocusController::nextElement() +void ListViewFocusController::nextDelegate() { - qDebug() << "===>> Current section: " << m_currentSectionString.at(static_cast(m_currentSection)); switch(m_currentSection) { case Section::Default: { if(m_header) { m_currentSection = Section::Header; + viewToBegin(); break; } } @@ -197,6 +159,7 @@ void ListViewFocusController::nextElement() break; } else if (m_footer) { m_currentSection = Section::Footer; + viewToEnd(); break; } case Section::Footer: { @@ -209,10 +172,9 @@ void ListViewFocusController::nextElement() break; } } - } -void ListViewFocusController::previousElement() +void ListViewFocusController::previousDelegate() { switch(m_currentSection) { case Section::Default: { @@ -224,7 +186,6 @@ void ListViewFocusController::previousElement() case Section::Footer: { if (size() > 0) { m_currentSection = Section::Delegate; - m_focusedItemIndex = size() - 1; // workarount to default value == -1 break; } } @@ -298,16 +259,17 @@ QQuickItem* ListViewFocusController::focusedItem() void ListViewFocusController::focusNextItem() { if (m_isReturnNeeded) { + qDebug() << "===>> RETURN IS NEEDED..."; return; } if (m_focusChain.empty()) { qDebug() << "Empty focusChain with current delegate: " << currentDelegate() << "Scanning for elements..."; - m_focusChain = getSubChain(currentDelegate()); + m_focusChain = getItemsChain(currentDelegate()); } if (m_focusChain.empty()) { - qWarning() << "No elements found. Returning from ListView..."; - nextElement(); + qWarning() << "No elements found in the delegate. Going to next delegate..."; + nextDelegate(); focusNextItem(); return; } @@ -325,14 +287,17 @@ void ListViewFocusController::focusPreviousItem() if (m_focusChain.empty()) { qDebug() << "Empty focusChain with current delegate: " << currentDelegate() << "Scanning for elements..."; - m_focusChain = getSubChain(currentDelegate()); + m_focusChain = getItemsChain(currentDelegate()); } if (m_focusChain.empty()) { - qWarning() << "No elements found. Returning from ListView..."; - previousElement(); + qWarning() << "No elements found in the delegate. Going to next delegate..."; + previousDelegate(); focusPreviousItem(); return; } + if (m_focusedItemIndex == -1) { + m_focusedItemIndex = m_focusChain.size(); + } m_focusedItemIndex--; m_focusedItem = qobject_cast(m_focusChain.at(m_focusedItemIndex)); qDebug() << "==>> Focused Item: " << m_focusedItem << " with Index: " << m_focusedItemIndex; diff --git a/client/ui/controllers/listViewFocusController.h b/client/ui/controllers/listViewFocusController.h index e7d83bbf..66c31228 100644 --- a/client/ui/controllers/listViewFocusController.h +++ b/client/ui/controllers/listViewFocusController.h @@ -7,7 +7,8 @@ #include -bool isListView(QObject* item); +bool isEnabled(QObject* item); +bool isFocusable(QObject* item); bool isMore(QObject* item1, QObject* item2); bool isLess(QObject* item1, QObject* item2); QList getSubChain(QObject* object); @@ -29,8 +30,8 @@ public: explicit ListViewFocusController(QQuickItem* listView, QObject* parent = nullptr); ~ListViewFocusController(); - void nextElement(); - void previousElement(); + void nextDelegate(); + void previousDelegate(); void decrementIndex(); void focusNextItem(); void focusPreviousItem(); diff --git a/client/ui/qml/main2.qml b/client/ui/qml/main2.qml index 83a6667f..f1a87ba9 100644 --- a/client/ui/qml/main2.qml +++ b/client/ui/qml/main2.qml @@ -15,6 +15,7 @@ import "Pages2" Window { id: root objectName: "mainWindow" + visible: true width: GC.screenWidth height: GC.screenHeight @@ -32,7 +33,7 @@ Window { title: "AmneziaVPN" - Item { + Item { // This item is needed for focus handling id: defaultFocusItem objectName: "defaultFocusItem" @@ -210,8 +211,6 @@ Window { clickedFunc: function() { hidePassword = !hidePassword } - - // KeyNavigation.tab: saveButton } BasicButtonType { From 852e90e3175d640e4967453534bb48aa0440e7d1 Mon Sep 17 00:00:00 2001 From: Cyril Anisimov Date: Thu, 17 Oct 2024 04:10:40 +0200 Subject: [PATCH 11/40] update CardType for using with focus navigation --- client/ui/qml/Controls2/CardType.qml | 31 +++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/client/ui/qml/Controls2/CardType.qml b/client/ui/qml/Controls2/CardType.qml index f584a8fc..8e689541 100644 --- a/client/ui/qml/Controls2/CardType.qml +++ b/client/ui/qml/Controls2/CardType.qml @@ -22,6 +22,7 @@ RadioButton { property string pressedBorderColor: AmneziaStyle.color.softGoldenApricot property string selectedBorderColor: AmneziaStyle.color.goldenApricot property string defaultBodredColor: AmneziaStyle.color.transparent + property string focusBorderColor: AmneziaStyle.color.paleGray property int borderWidth: 0 implicitWidth: content.implicitWidth @@ -29,6 +30,32 @@ RadioButton { hoverEnabled: true + property bool isFocusable: true + + Keys.onTabPressed: { + FocusController.nextKeyTabItem() + } + + Keys.onBacktabPressed: { + FocusController.previousKeyTabItem() + } + + Keys.onUpPressed: { + FocusController.nextKeyUpItem() + } + + Keys.onDownPressed: { + FocusController.nextKeyDownItem() + } + + Keys.onLeftPressed: { + FocusController.nextKeyLeftItem() + } + + Keys.onRightPressed: { + FocusController.nextKeyRightItem() + } + indicator: Rectangle { anchors.fill: parent radius: 16 @@ -52,6 +79,8 @@ RadioButton { return pressedBorderColor } else if (root.checked) { return selectedBorderColor + } else if (root.activeFocus) { + return focusBorderColor } } return defaultBodredColor @@ -59,7 +88,7 @@ RadioButton { border.width: { if (root.enabled) { - if(root.checked) { + if(root.checked || root.activeFocus) { return 1 } return root.pressed ? 1 : 0 From 063851445a597cce996ad4515a4efb173dbe13fa Mon Sep 17 00:00:00 2001 From: Cyril Anisimov Date: Thu, 17 Oct 2024 04:14:18 +0200 Subject: [PATCH 12/40] remove useless key navigation --- client/ui/qml/Controls2/HeaderType.qml | 2 -- .../Pages2/PageProtocolShadowSocksSettings.qml | 1 - .../ui/qml/Pages2/PageProtocolXraySettings.qml | 2 -- .../Pages2/PageSettingsAppSplitTunneling.qml | 1 - .../ui/qml/Pages2/PageSettingsApplication.qml | 7 ------- .../Pages2/PageSetupWizardProtocolSettings.qml | 17 ++--------------- client/ui/qml/Pages2/PageSetupWizardTextKey.qml | 1 - .../ui/qml/Pages2/PageSetupWizardViewConfig.qml | 2 -- 8 files changed, 2 insertions(+), 31 deletions(-) diff --git a/client/ui/qml/Controls2/HeaderType.qml b/client/ui/qml/Controls2/HeaderType.qml index f1cafbff..1366148d 100644 --- a/client/ui/qml/Controls2/HeaderType.qml +++ b/client/ui/qml/Controls2/HeaderType.qml @@ -19,8 +19,6 @@ Item { property string descriptionText - focus: true - implicitWidth: content.implicitWidth implicitHeight: content.implicitHeight diff --git a/client/ui/qml/Pages2/PageProtocolShadowSocksSettings.qml b/client/ui/qml/Pages2/PageProtocolShadowSocksSettings.qml index dae96b5a..aa04eb29 100644 --- a/client/ui/qml/Pages2/PageProtocolShadowSocksSettings.qml +++ b/client/ui/qml/Pages2/PageProtocolShadowSocksSettings.qml @@ -158,7 +158,6 @@ PageType { enabled: isPortEditable | isCipherEditable text: qsTr("Save") - Keys.onTabPressed: lastItemTabClicked(focusItem) clickedFunc: function() { forceActiveFocus() diff --git a/client/ui/qml/Pages2/PageProtocolXraySettings.qml b/client/ui/qml/Pages2/PageProtocolXraySettings.qml index d74aabad..6d53fdd3 100644 --- a/client/ui/qml/Pages2/PageProtocolXraySettings.qml +++ b/client/ui/qml/Pages2/PageProtocolXraySettings.qml @@ -118,8 +118,6 @@ PageType { text: qsTr("Save") - Keys.onTabPressed: lastItemTabClicked(focusItem) - onClicked: { forceActiveFocus() diff --git a/client/ui/qml/Pages2/PageSettingsAppSplitTunneling.qml b/client/ui/qml/Pages2/PageSettingsAppSplitTunneling.qml index 4751aa71..2e86a0dc 100644 --- a/client/ui/qml/Pages2/PageSettingsAppSplitTunneling.qml +++ b/client/ui/qml/Pages2/PageSettingsAppSplitTunneling.qml @@ -253,7 +253,6 @@ PageType { textFieldPlaceholderText: qsTr("application name") buttonImageSource: "qrc:/images/controls/plus.svg" - Keys.onTabPressed: lastItemTabClicked(focusItem) rightButtonClickedOnEnter: true clickedFunc: function() { diff --git a/client/ui/qml/Pages2/PageSettingsApplication.qml b/client/ui/qml/Pages2/PageSettingsApplication.qml index 4dea8c65..c5fc0bc1 100644 --- a/client/ui/qml/Pages2/PageSettingsApplication.qml +++ b/client/ui/qml/Pages2/PageSettingsApplication.qml @@ -205,7 +205,6 @@ PageType { rightImageSource: "qrc:/images/controls/chevron-right.svg" textColor: AmneziaStyle.color.vibrantRed - Keys.onTabPressed: lastItemTabClicked() parentFlickable: fl clickedFunction: function() { @@ -246,11 +245,5 @@ PageType { width: root.width height: root.height - - // onClosed: { - // if (!GC.isMobile()) { - // focusItem.forceActiveFocus() - // } - // } } } diff --git a/client/ui/qml/Pages2/PageSetupWizardProtocolSettings.qml b/client/ui/qml/Pages2/PageSetupWizardProtocolSettings.qml index de8275f1..3cf52c3f 100644 --- a/client/ui/qml/Pages2/PageSetupWizardProtocolSettings.qml +++ b/client/ui/qml/Pages2/PageSetupWizardProtocolSettings.qml @@ -104,23 +104,10 @@ PageType { DrawerType2 { id: showDetailsDrawer parent: root - onClosed: { - if (!GC.isMobile()) { - // defaultActiveFocusItem.forceActiveFocus() - } - } anchors.fill: parent expandedHeight: parent.height * 0.9 expandedStateContent: Item { - Connections { - target: showDetailsDrawer - enabled: !GC.isMobile() - function onOpened() { - focusItem2.forceActiveFocus() - } - } - implicitHeight: showDetailsDrawer.expandedHeight // Item { @@ -196,7 +183,7 @@ PageType { parentFlickable: fl text: qsTr("Close") - Keys.onTabPressed: lastItemTabClicked(focusItem2) + // Keys.onTabPressed: lastItemTabClicked(focusItem2) clickedFunc: function() { showDetailsDrawer.close() @@ -248,7 +235,7 @@ PageType { text: qsTr("Install") - Keys.onTabPressed: lastItemTabClicked(focusItem) + // Keys.onTabPressed: lastItemTabClicked(focusItem) clickedFunc: function() { if (!port.textField.acceptableInput && diff --git a/client/ui/qml/Pages2/PageSetupWizardTextKey.qml b/client/ui/qml/Pages2/PageSetupWizardTextKey.qml index 86880713..126a7c91 100644 --- a/client/ui/qml/Pages2/PageSetupWizardTextKey.qml +++ b/client/ui/qml/Pages2/PageSetupWizardTextKey.qml @@ -73,7 +73,6 @@ PageType { anchors.bottomMargin: 32 text: qsTr("Continue") - Keys.onTabPressed: lastItemTabClicked(focusItem) clickedFunc: function() { if (ImportController.extractConfigFromData(textKey.textFieldText)) { diff --git a/client/ui/qml/Pages2/PageSetupWizardViewConfig.qml b/client/ui/qml/Pages2/PageSetupWizardViewConfig.qml index 52c12c56..14096742 100644 --- a/client/ui/qml/Pages2/PageSetupWizardViewConfig.qml +++ b/client/ui/qml/Pages2/PageSetupWizardViewConfig.qml @@ -178,8 +178,6 @@ PageType { anchors.rightMargin: 16 anchors.leftMargin: 16 - Keys.onTabPressed: lastItemTabClicked(focusItem) - BasicButtonType { id: connectButton Layout.fillWidth: true From ada3f9a7faa5de13a23c7fa8479f6caf8014432b Mon Sep 17 00:00:00 2001 From: Cyril Anisimov Date: Sat, 19 Oct 2024 17:09:57 +0200 Subject: [PATCH 13/40] remove useless slots, logs, Drawer open and close --- client/ui/qml/Components/QuestionDrawer.qml | 12 ------------ .../ui/qml/Components/SelectLanguageDrawer.qml | 12 ------------ .../ui/qml/Components/ShareConnectionDrawer.qml | 14 -------------- client/ui/qml/Pages2/PageHome.qml | 8 +++----- .../qml/Pages2/PageProtocolOpenVpnSettings.qml | 1 - client/ui/qml/Pages2/PageProtocolRaw.qml | 15 --------------- .../PageProtocolWireGuardClientSettings.qml | 2 -- .../qml/Pages2/PageProtocolWireGuardSettings.qml | 2 -- client/ui/qml/Pages2/PageServiceDnsSettings.qml | 2 -- client/ui/qml/Pages2/PageServiceSftpSettings.qml | 9 --------- .../qml/Pages2/PageServiceSocksProxySettings.qml | 11 ----------- .../qml/Pages2/PageServiceTorWebsiteSettings.qml | 2 -- client/ui/qml/Pages2/PageSettingsDns.qml | 2 -- client/ui/qml/Pages2/PageSettingsServerData.qml | 16 ---------------- .../ui/qml/Pages2/PageSettingsServerProtocol.qml | 10 ---------- client/ui/qml/Pages2/PageShare.qml | 16 ---------------- client/ui/qml/main2.qml | 1 - 17 files changed, 3 insertions(+), 132 deletions(-) diff --git a/client/ui/qml/Components/QuestionDrawer.qml b/client/ui/qml/Components/QuestionDrawer.qml index 0a49358a..0c14e52d 100644 --- a/client/ui/qml/Components/QuestionDrawer.qml +++ b/client/ui/qml/Components/QuestionDrawer.qml @@ -33,18 +33,6 @@ DrawerType2 { root.expandedHeight = content.implicitHeight + 32 } - Connections { - target: root - enabled: !GC.isMobile() - function onOpened() { - FocusController.pushRootObject(root) - } - - function onClosed() { - FocusController.dropRootObject(root) - } - } - Header2TextType { Layout.fillWidth: true Layout.topMargin: 16 diff --git a/client/ui/qml/Components/SelectLanguageDrawer.qml b/client/ui/qml/Components/SelectLanguageDrawer.qml index 4ca472cf..678ecf64 100644 --- a/client/ui/qml/Components/SelectLanguageDrawer.qml +++ b/client/ui/qml/Components/SelectLanguageDrawer.qml @@ -20,18 +20,6 @@ DrawerType2 { root.expandedHeight = container.implicitHeight } - Connections { - target: root - enabled: !GC.isMobile() - function onOpened() { - FocusController.pushRootObject(root) - } - - function onClosed() { - FocusController.dropRootObject(root) - } - } - ColumnLayout { id: backButtonLayout diff --git a/client/ui/qml/Components/ShareConnectionDrawer.qml b/client/ui/qml/Components/ShareConnectionDrawer.qml index 261cd742..dd794a03 100644 --- a/client/ui/qml/Components/ShareConnectionDrawer.qml +++ b/client/ui/qml/Components/ShareConnectionDrawer.qml @@ -39,14 +39,6 @@ DrawerType2 { expandedStateContent: Item { implicitHeight: root.expandedHeight - Connections { - target: root - enabled: !GC.isMobile() - function onOpened() { - header.forceActiveFocus() - } - } - Header2Type { id: header anchors.top: parent.top @@ -170,12 +162,6 @@ DrawerType2 { anchors.fill: parent expandedHeight: parent.height * 0.9 - onClosed: { - if (!GC.isMobile()) { - header.forceActiveFocus() - } - } - expandedStateContent: Item { id: configContentContainer diff --git a/client/ui/qml/Pages2/PageHome.qml b/client/ui/qml/Pages2/PageHome.qml index 5ef33ded..f5ac8ae3 100644 --- a/client/ui/qml/Pages2/PageHome.qml +++ b/client/ui/qml/Pages2/PageHome.qml @@ -123,10 +123,6 @@ PageType { objectName: "homeSplitTunnelingDrawer" parent: root - - onClosed: { - console.log(objectName, " was closed...") - } } } } @@ -145,12 +141,14 @@ PageType { Component.onCompleted: { drawer.expandedHeight = implicitHeight } + Connections { objectName: "drawerConnections" target: drawer enabled: !GC.isMobile() } + ColumnLayout { id: collapsed objectName: "collapsedColumnLayout" @@ -253,7 +251,6 @@ PageType { Keys.onReturnPressed: collapsedButtonChevron.clicked() onClicked: { - console.debug("onClicked collapsedButtonChevron") if (drawer.isCollapsedStateActive()) { drawer.openTriggered() } @@ -414,6 +411,7 @@ PageType { Connections { target: drawer + // this item shouldn't be focused when drawer is closed function onIsOpenedChanged() { serversMenuContent.isFocusable = drawer.isOpened } diff --git a/client/ui/qml/Pages2/PageProtocolOpenVpnSettings.qml b/client/ui/qml/Pages2/PageProtocolOpenVpnSettings.qml index 75fdd18d..3df23ac0 100644 --- a/client/ui/qml/Pages2/PageProtocolOpenVpnSettings.qml +++ b/client/ui/qml/Pages2/PageProtocolOpenVpnSettings.qml @@ -398,7 +398,6 @@ PageType { text: qsTr("Save") parentFlickable: fl - Keys.onTabPressed: lastItemTabClicked(focusItem) clickedFunc: function() { forceActiveFocus() diff --git a/client/ui/qml/Pages2/PageProtocolRaw.qml b/client/ui/qml/Pages2/PageProtocolRaw.qml index 3cb02435..14aa5194 100644 --- a/client/ui/qml/Pages2/PageProtocolRaw.qml +++ b/client/ui/qml/Pages2/PageProtocolRaw.qml @@ -110,26 +110,12 @@ PageType { expandedHeight: root.height * 0.9 - onClosed: { - if (!GC.isMobile()) { - defaultActiveFocusItem.forceActiveFocus() - } - } - parent: root anchors.fill: parent expandedStateContent: Item { implicitHeight: configContentDrawer.expandedHeight - Connections { - target: configContentDrawer - enabled: !GC.isMobile() - function onOpened() { - focusItem1.forceActiveFocus() - } - } - BackButtonType { id: backButton1 @@ -209,7 +195,6 @@ PageType { text: qsTr("Remove ") + ContainersModel.getProcessedContainerName() textColor: AmneziaStyle.color.vibrantRed - 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.") diff --git a/client/ui/qml/Pages2/PageProtocolWireGuardClientSettings.qml b/client/ui/qml/Pages2/PageProtocolWireGuardClientSettings.qml index 007de5ca..4f0f474c 100644 --- a/client/ui/qml/Pages2/PageProtocolWireGuardClientSettings.qml +++ b/client/ui/qml/Pages2/PageProtocolWireGuardClientSettings.qml @@ -150,8 +150,6 @@ PageType { text: qsTr("Save") - Keys.onTabPressed: lastItemTabClicked(focusItem) - clickedFunc: function() { forceActiveFocus() var headerText = qsTr("Save settings?") diff --git a/client/ui/qml/Pages2/PageProtocolWireGuardSettings.qml b/client/ui/qml/Pages2/PageProtocolWireGuardSettings.qml index ab50f444..257bc675 100644 --- a/client/ui/qml/Pages2/PageProtocolWireGuardSettings.qml +++ b/client/ui/qml/Pages2/PageProtocolWireGuardSettings.qml @@ -140,8 +140,6 @@ PageType { text: qsTr("Save") - Keys.onTabPressed: lastItemTabClicked(focusItem) - onClicked: function() { forceActiveFocus() diff --git a/client/ui/qml/Pages2/PageServiceDnsSettings.qml b/client/ui/qml/Pages2/PageServiceDnsSettings.qml index c50f9a9b..cef29813 100644 --- a/client/ui/qml/Pages2/PageServiceDnsSettings.qml +++ b/client/ui/qml/Pages2/PageServiceDnsSettings.qml @@ -64,8 +64,6 @@ PageType { text: qsTr("Remove ") + ContainersModel.getProcessedContainerName() textColor: AmneziaStyle.color.vibrantRed - Keys.onTabPressed: root.lastItemTabClicked() - clickedFunction: function() { var headerText = qsTr("Remove %1 from server?").arg(ContainersModel.getProcessedContainerName()) var yesButtonText = qsTr("Continue") diff --git a/client/ui/qml/Pages2/PageServiceSftpSettings.qml b/client/ui/qml/Pages2/PageServiceSftpSettings.qml index 8513f111..2deb315c 100644 --- a/client/ui/qml/Pages2/PageServiceSftpSettings.qml +++ b/client/ui/qml/Pages2/PageServiceSftpSettings.qml @@ -174,14 +174,6 @@ PageType { parentFlickable: fl - rightButton.Keys.onTabPressed: { - if (mountButton.visible) { - mountButton.forceActiveFocus() - } else { - detailedInstructionsButton.forceActiveFocus() - } - } - rightImageSource: "qrc:/images/controls/copy.svg" rightImageColor: AmneziaStyle.color.paleGray @@ -278,7 +270,6 @@ PageType { text: qsTr("Detailed instructions") parentFlickable: fl - Keys.onTabPressed: lastItemTabClicked(focusItem) clickedFunc: function() { // Qt.openUrlExternally("https://github.com/amnezia-vpn/desktop-client/releases/latest") diff --git a/client/ui/qml/Pages2/PageServiceSocksProxySettings.qml b/client/ui/qml/Pages2/PageServiceSocksProxySettings.qml index cadfee09..4c94913e 100644 --- a/client/ui/qml/Pages2/PageServiceSocksProxySettings.qml +++ b/client/ui/qml/Pages2/PageServiceSocksProxySettings.qml @@ -187,12 +187,6 @@ PageType { anchors.fill: parent expandedHeight: root.height * 0.9 - onClosed: { - if (!GC.isMobile()) { - focusItem.forceActiveFocus() - } - } - expandedStateContent: ColumnLayout { property string tempPort: port property string tempUsername: username @@ -209,9 +203,6 @@ PageType { Connections { target: changeSettingsDrawer function onOpened() { - if (!GC.isMobile()) { - drawerFocusItem.forceActiveFocus() - } tempPort = port tempUsername = username tempPassword = password @@ -310,7 +301,6 @@ PageType { Layout.bottomMargin: 24 text: qsTr("Change connection settings") - Keys.onTabPressed: lastItemTabClicked(drawerFocusItem) clickedFunc: function() { forceActiveFocus() @@ -348,7 +338,6 @@ PageType { Layout.rightMargin: 16 text: qsTr("Change connection settings") - Keys.onTabPressed: lastItemTabClicked(focusItem) clickedFunc: function() { forceActiveFocus() diff --git a/client/ui/qml/Pages2/PageServiceTorWebsiteSettings.qml b/client/ui/qml/Pages2/PageServiceTorWebsiteSettings.qml index a1e41f29..249c70c7 100644 --- a/client/ui/qml/Pages2/PageServiceTorWebsiteSettings.qml +++ b/client/ui/qml/Pages2/PageServiceTorWebsiteSettings.qml @@ -80,8 +80,6 @@ PageType { rightImageSource: "qrc:/images/controls/copy.svg" rightImageColor: AmneziaStyle.color.paleGray - Keys.onTabPressed: lastItemTabClicked(focusItem) - clickedFunction: function() { GC.copyToClipBoard(descriptionText) PageController.showNotificationMessage(qsTr("Copied")) diff --git a/client/ui/qml/Pages2/PageSettingsDns.qml b/client/ui/qml/Pages2/PageSettingsDns.qml index cede6c74..b6ade37a 100644 --- a/client/ui/qml/Pages2/PageSettingsDns.qml +++ b/client/ui/qml/Pages2/PageSettingsDns.qml @@ -140,8 +140,6 @@ PageType { } PageController.showNotificationMessage(qsTr("Settings saved")) } - - Keys.onTabPressed: lastItemTabClicked(focusItem) } } } diff --git a/client/ui/qml/Pages2/PageSettingsServerData.qml b/client/ui/qml/Pages2/PageSettingsServerData.qml index 0568c5f4..cd736d39 100644 --- a/client/ui/qml/Pages2/PageSettingsServerData.qml +++ b/client/ui/qml/Pages2/PageSettingsServerData.qml @@ -158,16 +158,6 @@ PageType { text: qsTr("Remove server from application") textColor: AmneziaStyle.color.vibrantRed - 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.") @@ -206,10 +196,6 @@ PageType { text: qsTr("Clear server from Amnezia software") textColor: AmneziaStyle.color.vibrantRed - 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.") @@ -249,8 +235,6 @@ PageType { text: qsTr("Reset API config") textColor: AmneziaStyle.color.vibrantRed - Keys.onTabPressed: root.lastItemTabClickedSignal() - clickedFunction: function() { var headerText = qsTr("Do you want to reset API config?") var descriptionText = "" diff --git a/client/ui/qml/Pages2/PageSettingsServerProtocol.qml b/client/ui/qml/Pages2/PageSettingsServerProtocol.qml index 73e1e2c6..ab0cd291 100644 --- a/client/ui/qml/Pages2/PageSettingsServerProtocol.qml +++ b/client/ui/qml/Pages2/PageSettingsServerProtocol.qml @@ -61,15 +61,6 @@ PageType { } } - Keys.onTabPressed: { - if (currentFocusIndex < this.count - 1) { - currentFocusIndex += 1 - protocols.itemAtIndex(currentFocusIndex).focusItem.forceActiveFocus() - } else { - clearCacheButton.forceActiveFocus() - } - } - delegate: Item { property var focusItem: clientSettings.rightButton @@ -210,7 +201,6 @@ PageType { Layout.fillWidth: true visible: ServersModel.isProcessedServerHasWriteAccess() - Keys.onTabPressed: lastItemTabClicked(focusItem) text: qsTr("Remove ") textColor: AmneziaStyle.color.vibrantRed diff --git a/client/ui/qml/Pages2/PageShare.qml b/client/ui/qml/Pages2/PageShare.qml index 3cb8f660..7bf31067 100644 --- a/client/ui/qml/Pages2/PageShare.qml +++ b/client/ui/qml/Pages2/PageShare.qml @@ -199,11 +199,6 @@ PageType { anchors.fill: parent expandedHeight: root.height - onClosed: { - if (!GC.isMobile()) { - // clientNameTextField.textField.forceActiveFocus() - } - } expandedStateContent: ColumnLayout { id: shareFullAccessDrawerContent @@ -218,14 +213,6 @@ PageType { shareFullAccessDrawer.expandedHeight = shareFullAccessDrawerContent.implicitHeight + 32 } - Connections { - target: shareFullAccessDrawer - enabled: !GC.isMobile() - function onOpened() { - // focusItem.forceActiveFocus() - } - } - Header2Type { Layout.fillWidth: true Layout.bottomMargin: 16 @@ -295,8 +282,6 @@ PageType { implicitWidth: (root.width - 32) / 2 text: qsTr("Users") - // KeyNavigation.tab: accessTypeSelector.currentIndex === 0 ? clientNameTextField.textField : serverSelector - onClicked: { accessTypeSelector.currentIndex = 1 PageController.showBusyIndicator(true) @@ -565,7 +550,6 @@ PageType { text: qsTr("Share") leftImageSource: "qrc:/images/controls/share-2.svg" - Keys.onTabPressed: lastItemTabClicked(focusItem) parentFlickable: a diff --git a/client/ui/qml/main2.qml b/client/ui/qml/main2.qml index f1a87ba9..e71fae37 100644 --- a/client/ui/qml/main2.qml +++ b/client/ui/qml/main2.qml @@ -27,7 +27,6 @@ Window { color: AmneziaStyle.color.midnightBlack onClosing: function() { - console.debug("QML onClosing signal") PageController.closeWindow() } From 626b9e1e763e8b9e6b7ab94c41b85c25f5eb1da7 Mon Sep 17 00:00:00 2001 From: Cyril Anisimov Date: Sat, 19 Oct 2024 22:20:35 +0200 Subject: [PATCH 14/40] fix reverse focus move on listView --- client/ui/controllers/listViewFocusController.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/client/ui/controllers/listViewFocusController.cpp b/client/ui/controllers/listViewFocusController.cpp index b899dfe6..f07c069c 100644 --- a/client/ui/controllers/listViewFocusController.cpp +++ b/client/ui/controllers/listViewFocusController.cpp @@ -109,6 +109,7 @@ void ListViewFocusController::viewAtCurrentIndex() { switch(m_currentSection) { case Section::Default: + [[fallthrough]]; case Section::Header: { QMetaObject::invokeMethod(m_listView, "positionViewAtBeginning"); break; @@ -146,12 +147,14 @@ void ListViewFocusController::nextDelegate() viewToBegin(); break; } + [[fallthrough]]; } case Section::Header: { if (size() > 0) { m_currentSection = Section::Delegate; break; } + [[fallthrough]]; } case Section::Delegate: if (m_delegateIndex < (size() - 1)) { @@ -162,6 +165,7 @@ void ListViewFocusController::nextDelegate() viewToEnd(); break; } + [[fallthrough]]; case Section::Footer: { m_isReturnNeeded = true; m_currentSection = Section::Default; @@ -182,12 +186,15 @@ void ListViewFocusController::previousDelegate() m_currentSection = Section::Footer; break; } + [[fallthrough]]; } case Section::Footer: { if (size() > 0) { m_currentSection = Section::Delegate; + m_delegateIndex = size() - 1; break; } + [[fallthrough]]; } case Section::Delegate: { if (m_delegateIndex > 0) { @@ -197,6 +204,7 @@ void ListViewFocusController::previousDelegate() m_currentSection = Section::Header; break; } + [[fallthrough]]; } case Section::Header: { m_isReturnNeeded = true; From 2c9fa10b8b02db3c54b9449dda060bcb4de09427 Mon Sep 17 00:00:00 2001 From: Cyril Anisimov Date: Sun, 20 Oct 2024 01:27:02 +0200 Subject: [PATCH 15/40] fix drawer radio buttons selection --- .../qml/Components/SelectLanguageDrawer.qml | 62 +++++++------------ .../Controls2/ListViewWithRadioButtonType.qml | 2 + client/ui/qml/Pages2/PageStart.qml | 4 ++ 3 files changed, 30 insertions(+), 38 deletions(-) diff --git a/client/ui/qml/Components/SelectLanguageDrawer.qml b/client/ui/qml/Components/SelectLanguageDrawer.qml index 678ecf64..a48515cc 100644 --- a/client/ui/qml/Components/SelectLanguageDrawer.qml +++ b/client/ui/qml/Components/SelectLanguageDrawer.qml @@ -65,6 +65,30 @@ DrawerType2 { property bool isFocusable: true + Keys.onTabPressed: { + FocusController.nextKeyTabItem() + } + + Keys.onBacktabPressed: { + FocusController.previousKeyTabItem() + } + + Keys.onUpPressed: { + FocusController.nextKeyUpItem() + } + + Keys.onDownPressed: { + FocusController.nextKeyDownItem() + } + + Keys.onLeftPressed: { + FocusController.nextKeyLeftItem() + } + + Keys.onRightPressed: { + FocusController.nextKeyRightItem() + } + model: LanguageModel currentIndex: LanguageModel.currentLanguageIndex @@ -72,48 +96,10 @@ DrawerType2 { id: buttonGroup } - // 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 // TODO: delete? - // 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 diff --git a/client/ui/qml/Controls2/ListViewWithRadioButtonType.qml b/client/ui/qml/Controls2/ListViewWithRadioButtonType.qml index 230f37eb..dda20313 100644 --- a/client/ui/qml/Controls2/ListViewWithRadioButtonType.qml +++ b/client/ui/qml/Controls2/ListViewWithRadioButtonType.qml @@ -77,6 +77,8 @@ ListView { hoverEnabled: true + property bool isFocusable: true + indicator: Rectangle { width: parent.width - 1 height: parent.height diff --git a/client/ui/qml/Pages2/PageStart.qml b/client/ui/qml/Pages2/PageStart.qml index 01e623b6..14cc6896 100644 --- a/client/ui/qml/Pages2/PageStart.qml +++ b/client/ui/qml/Pages2/PageStart.qml @@ -246,6 +246,10 @@ PageType { } Keys.onPressed: function(event) { + if(event.key === Qt.Key_Tab) { + FocusController.nextKeyTabItem() + } + PageController.keyPressEvent(event.key) event.accepted = true } From 9cfa4c1389ca088c8608af36698a28b4e430c465 Mon Sep 17 00:00:00 2001 From: Cyril Anisimov Date: Sun, 20 Oct 2024 21:44:44 +0200 Subject: [PATCH 16/40] fix drawer layout and focus move --- client/ui/qml/Controls2/DropDownType.qml | 5 ++-- .../Controls2/ListViewWithRadioButtonType.qml | 24 +++++++++++++++++++ .../qml/Pages2/PageSettingsSplitTunneling.qml | 2 +- 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/client/ui/qml/Controls2/DropDownType.qml b/client/ui/qml/Controls2/DropDownType.qml index 48471873..787890cb 100644 --- a/client/ui/qml/Controls2/DropDownType.qml +++ b/client/ui/qml/Controls2/DropDownType.qml @@ -226,7 +226,7 @@ Item { backButtonImage: root.headerBackButtonImage backButtonFunction: function() { menu.closeTriggered() } onActiveFocusChanged: { - if(activeFocus) { + if(backButton.enabled && backButton.activeFocus) { root.listView.positionViewAtBeginning() } } @@ -235,9 +235,10 @@ Item { Column { id: col - anchors.top: parent.top + anchors.top: header.bottom anchors.left: parent.left anchors.right: parent.right + anchors.topMargin: 16 spacing: 16 diff --git a/client/ui/qml/Controls2/ListViewWithRadioButtonType.qml b/client/ui/qml/Controls2/ListViewWithRadioButtonType.qml index dda20313..2ced4335 100644 --- a/client/ui/qml/Controls2/ListViewWithRadioButtonType.qml +++ b/client/ui/qml/Controls2/ListViewWithRadioButtonType.qml @@ -79,6 +79,30 @@ ListView { property bool isFocusable: true + Keys.onTabPressed: { + FocusController.nextKeyTabItem() + } + + Keys.onBacktabPressed: { + FocusController.previousKeyTabItem() + } + + Keys.onUpPressed: { + FocusController.nextKeyUpItem() + } + + Keys.onDownPressed: { + FocusController.nextKeyDownItem() + } + + Keys.onLeftPressed: { + FocusController.nextKeyLeftItem() + } + + Keys.onRightPressed: { + FocusController.nextKeyRightItem() + } + indicator: Rectangle { width: parent.width - 1 height: parent.height diff --git a/client/ui/qml/Pages2/PageSettingsSplitTunneling.qml b/client/ui/qml/Pages2/PageSettingsSplitTunneling.qml index f3ae3227..e61dc9f6 100644 --- a/client/ui/qml/Pages2/PageSettingsSplitTunneling.qml +++ b/client/ui/qml/Pages2/PageSettingsSplitTunneling.qml @@ -148,7 +148,7 @@ PageType { clickedFunction: function() { selector.text = selectedText - selector.close() + selector.closeTriggered() if (SitesModel.routeMode !== root.routeModesModel[currentIndex].type) { SitesModel.routeMode = root.routeModesModel[currentIndex].type } From c9622110682e6c2241d18d6a31aebe8f12b8b0c7 Mon Sep 17 00:00:00 2001 From: Cyril Anisimov Date: Mon, 21 Oct 2024 16:19:57 +0200 Subject: [PATCH 17/40] fix PageSetupWizardProtocolSettings focus move --- .../PageSetupWizardProtocolSettings.qml | 52 +++++++++++-------- 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/client/ui/qml/Pages2/PageSetupWizardProtocolSettings.qml b/client/ui/qml/Pages2/PageSetupWizardProtocolSettings.qml index 3cf52c3f..0de4da6b 100644 --- a/client/ui/qml/Pages2/PageSetupWizardProtocolSettings.qml +++ b/client/ui/qml/Pages2/PageSetupWizardProtocolSettings.qml @@ -49,6 +49,32 @@ PageType { interactive: false model: proxyContainersModel + property bool isFocusable: true + + Keys.onTabPressed: { + FocusController.nextKeyTabItem() + } + + Keys.onBacktabPressed: { + FocusController.previousKeyTabItem() + } + + Keys.onUpPressed: { + FocusController.nextKeyUpItem() + } + + Keys.onDownPressed: { + FocusController.nextKeyDownItem() + } + + Keys.onLeftPressed: { + FocusController.nextKeyLeftItem() + } + + Keys.onRightPressed: { + FocusController.nextKeyRightItem() + } + delegate: Item { implicitWidth: processedContainerListView.width implicitHeight: (delegateContent.implicitHeight > root.height) ? delegateContent.implicitHeight : root.height @@ -97,7 +123,7 @@ PageType { KeyNavigation.tab: transportProtoSelector clickedFunc: function() { - showDetailsDrawer.open() + showDetailsDrawer.openTriggered() } } @@ -110,16 +136,6 @@ PageType { expandedStateContent: Item { implicitHeight: showDetailsDrawer.expandedHeight - // Item { - // id: focusItem2 - // KeyNavigation.tab: showDetailsBackButton - // onFocusChanged: { - // if (focusItem2.activeFocus) { - // fl.contentY = 0 - // } - // } - // } - BackButtonType { id: showDetailsBackButton @@ -129,7 +145,7 @@ PageType { anchors.topMargin: 16 backButtonFunction: function() { - showDetailsDrawer.close() + showDetailsDrawer.closeTriggered() } } @@ -183,10 +199,9 @@ PageType { parentFlickable: fl text: qsTr("Close") - // Keys.onTabPressed: lastItemTabClicked(focusItem2) clickedFunc: function() { - showDetailsDrawer.close() + showDetailsDrawer.closeTriggered() } } } @@ -207,8 +222,6 @@ PageType { Layout.fillWidth: true rootWidth: root.width - - // KeyNavigation.tab: (port.visible && port.enabled) ? port.textField : installButton } TextFieldWithHeaderType { @@ -235,8 +248,6 @@ PageType { text: qsTr("Install") - // Keys.onTabPressed: lastItemTabClicked(focusItem) - clickedFunc: function() { if (!port.textField.acceptableInput && ContainerProps.containerTypeToString(dockerContainer) !== "torwebsite" && @@ -264,11 +275,6 @@ PageType { var protocolSelectorVisible = ProtocolProps.defaultTransportProtoChangeable(defaultContainerProto) transportProtoSelector.visible = protocolSelectorVisible transportProtoHeader.visible = protocolSelectorVisible - - // if (port.visible && port.enabled) - // defaultActiveFocusItem = port.textField - // else - // defaultActiveFocusItem = focusItem } } } From db5d289edcbf560ec94dad84da7c29decb5371fc Mon Sep 17 00:00:00 2001 From: Cyril Anisimov Date: Mon, 21 Oct 2024 16:47:46 +0200 Subject: [PATCH 18/40] fix back navigation on default focus item --- client/ui/qml/main2.qml | 38 ++++++++++++++++---------------------- 1 file changed, 16 insertions(+), 22 deletions(-) diff --git a/client/ui/qml/main2.qml b/client/ui/qml/main2.qml index e71fae37..2ab4db69 100644 --- a/client/ui/qml/main2.qml +++ b/client/ui/qml/main2.qml @@ -38,28 +38,22 @@ Window { focus: true - Keys.onTabPressed: { - FocusController.nextKeyTabItem() - } - - Keys.onBacktabPressed: { - FocusController.previousKeyTabItem() - } - - Keys.onUpPressed: { - FocusController.nextKeyUpItem() - } - - Keys.onDownPressed: { - FocusController.nextKeyDownItem() - } - - Keys.onLeftPressed: { - FocusController.nextKeyLeftItem() - } - - Keys.onRightPressed: { - FocusController.nextKeyRightItem() + Keys.onPressed: function(event) { + switch (event.key) { + case Qt.Key_Tab: + case Qt.Key_Down: + case Qt.Key_Right: + FocusController.nextKeyTabItem() + break + case Qt.Key_Backtab: + case Qt.Key_Up: + case Qt.Key_Left: + FocusController.previousKeyTabItem() + break + default: + PageController.keyPressEvent(event.key) + event.accepted = true + } } } From dac45a9f7f37c6ff517eedd798896759ac463d45 Mon Sep 17 00:00:00 2001 From: Cyril Anisimov Date: Mon, 21 Oct 2024 20:43:05 +0200 Subject: [PATCH 19/40] fix crashes after ListView navigation --- client/ui/controllers/focusController.cpp | 19 +++++++++++++++---- client/ui/controllers/focusController.h | 1 + 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/client/ui/controllers/focusController.cpp b/client/ui/controllers/focusController.cpp index e70391a8..682f4b0e 100644 --- a/client/ui/controllers/focusController.cpp +++ b/client/ui/controllers/focusController.cpp @@ -88,6 +88,8 @@ FocusController::FocusController(QQmlApplicationEngine* engine, QObject *parent) void FocusController::nextItem(Direction direction) { + reload(direction); + if (m_lvfc) { direction == Direction::Forward ? focusNextListViewItem() : focusPreviousListViewItem(); qDebug() << "===>> [handling the ListView]"; @@ -95,15 +97,13 @@ void FocusController::nextItem(Direction direction) return; } - reload(direction); - if(m_focusChain.empty()) { qWarning() << "There are no items to navigate"; return; } if (m_focusedItemIndex == (m_focusChain.size() - 1)) { - qDebug() << "Last focus index. Making it zero"; + qDebug() << "Last focus index. Making it zero..."; m_focusedItemIndex = 0; } else { qDebug() << "Incrementing focus index"; @@ -222,7 +222,7 @@ void FocusController::setFocusOnDefaultItem() void FocusController::reload(Direction direction) { - m_focusChain.clear(); + m_focusChain.clear(); QObject* rootObject = (m_rootObjects.empty() ? m_engine->rootObjects().value(0) @@ -232,6 +232,7 @@ void FocusController::reload(Direction direction) qCritical() << "No ROOT OBJECT found!"; m_focusedItemIndex = -1; resetRootObject(); + resetListView(); setFocusOnDefaultItem(); return; } @@ -246,6 +247,7 @@ void FocusController::reload(Direction direction) qWarning() << "Focus chain is empty!"; m_focusedItemIndex = -1; resetRootObject(); + resetListView(); setFocusOnDefaultItem(); return; } @@ -254,11 +256,20 @@ void FocusController::reload(Direction direction) if(m_focusedItemIndex == -1) { qInfo() << "No focus item in chain."; + resetListView(); setFocusOnDefaultItem(); return; } } +void FocusController::resetListView() +{ + if(m_lvfc) { + delete m_lvfc; + m_lvfc = nullptr; + } +} + void FocusController::pushRootObject(QObject* object) { m_rootObjects.push(object); diff --git a/client/ui/controllers/focusController.h b/client/ui/controllers/focusController.h index a4851032..472c328b 100644 --- a/client/ui/controllers/focusController.h +++ b/client/ui/controllers/focusController.h @@ -44,6 +44,7 @@ private: void focusNextListViewItem(); void focusPreviousListViewItem(); void reload(Direction direction); + void resetListView(); QSharedPointer m_engine; // Pointer to engine to get root object QList m_focusChain; // List of current objects to be focused From 21755cbd5404c04df895e9a716379ea205d8c980 Mon Sep 17 00:00:00 2001 From: Cyril Anisimov Date: Tue, 22 Oct 2024 00:25:05 +0200 Subject: [PATCH 20/40] fix protocol settings focus move --- .../Pages2/PageProtocolAwgClientSettings.qml | 415 ++++++------ .../ui/qml/Pages2/PageProtocolAwgSettings.qml | 629 +++++++++--------- .../qml/Pages2/PageSettingsServerProtocol.qml | 202 +++--- 3 files changed, 644 insertions(+), 602 deletions(-) diff --git a/client/ui/qml/Pages2/PageProtocolAwgClientSettings.qml b/client/ui/qml/Pages2/PageProtocolAwgClientSettings.qml index 8685a954..859d155e 100644 --- a/client/ui/qml/Pages2/PageProtocolAwgClientSettings.qml +++ b/client/ui/qml/Pages2/PageProtocolAwgClientSettings.qml @@ -16,18 +16,6 @@ import "../Components" PageType { id: root - defaultActiveFocusItem: listview.currentItem.mtuTextField.textField - - Item { - id: focusItem - onFocusChanged: { - if (activeFocus) { - fl.ensureVisible(focusItem) - } - } - KeyNavigation.tab: backButton - } - ColumnLayout { id: backButtonLayout @@ -39,228 +27,236 @@ PageType { BackButtonType { id: backButton - KeyNavigation.tab: listview.currentItem.mtuTextField.textField } } - FlickableType { - id: fl + ListView { + id: listview + anchors.top: backButtonLayout.bottom - anchors.bottom: parent.bottom - contentHeight: content.implicitHeight + saveButton.implicitHeight + saveButton.anchors.bottomMargin + saveButton.anchors.topMargin + anchors.bottom: saveButton.top - Column { - id: content + width: parent.width - anchors.top: parent.top - anchors.left: parent.left - anchors.right: parent.right + clip: true - ListView { - id: listview + property bool isFocusable: true - width: parent.width - height: listview.contentItem.height + Keys.onTabPressed: { + FocusController.nextKeyTabItem() + } - clip: true - interactive: false + Keys.onBacktabPressed: { + FocusController.previousKeyTabItem() + } - model: AwgConfigModel + Keys.onUpPressed: { + FocusController.nextKeyUpItem() + } - delegate: Item { - id: delegateItem - implicitWidth: listview.width - implicitHeight: col.implicitHeight + Keys.onDownPressed: { + FocusController.nextKeyDownItem() + } - property alias mtuTextField: mtuTextField - property bool isSaveButtonEnabled: mtuTextField.errorText === "" && - junkPacketMaxSizeTextField.errorText === "" && - junkPacketMinSizeTextField.errorText === "" && - junkPacketCountTextField.errorText === "" + Keys.onLeftPressed: { + FocusController.nextKeyLeftItem() + } - ColumnLayout { - id: col + Keys.onRightPressed: { + FocusController.nextKeyRightItem() + } - anchors.top: parent.top - anchors.left: parent.left - anchors.right: parent.right + model: AwgConfigModel - anchors.leftMargin: 16 - anchors.rightMargin: 16 + delegate: Item { + id: delegateItem + implicitWidth: listview.width + implicitHeight: col.implicitHeight - spacing: 0 + property alias mtuTextField: mtuTextField + property bool isSaveButtonEnabled: mtuTextField.errorText === "" && + junkPacketMaxSizeTextField.errorText === "" && + junkPacketMinSizeTextField.errorText === "" && + junkPacketCountTextField.errorText === "" - HeaderType { - Layout.fillWidth: true + ColumnLayout { + id: col - headerText: qsTr("AmneziaWG settings") - } + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right - TextFieldWithHeaderType { - id: mtuTextField - Layout.fillWidth: true - Layout.topMargin: 40 + anchors.leftMargin: 16 + anchors.rightMargin: 16 - headerText: qsTr("MTU") - textFieldText: clientMtu - textField.validator: IntValidator { bottom: 576; top: 65535 } + spacing: 0 - textField.onEditingFinished: { - if (textFieldText !== clientMtu) { - clientMtu = textFieldText - } - } - checkEmptyText: true - KeyNavigation.tab: junkPacketCountTextField.textField - } + HeaderType { + Layout.fillWidth: true - TextFieldWithHeaderType { - id: junkPacketCountTextField - Layout.fillWidth: true - Layout.topMargin: 16 + headerText: qsTr("AmneziaWG settings") + } - headerText: "Jc - Junk packet count" - textFieldText: clientJunkPacketCount - textField.validator: IntValidator { bottom: 0 } - parentFlickable: fl + TextFieldWithHeaderType { + id: mtuTextField + Layout.fillWidth: true + Layout.topMargin: 40 - textField.onEditingFinished: { - if (textFieldText !== clientJunkPacketCount) { - clientJunkPacketCount = textFieldText - } - } + headerText: qsTr("MTU") + textFieldText: clientMtu + textField.validator: IntValidator { bottom: 576; top: 65535 } - checkEmptyText: true - - KeyNavigation.tab: junkPacketMinSizeTextField.textField - } - - TextFieldWithHeaderType { - id: junkPacketMinSizeTextField - Layout.fillWidth: true - Layout.topMargin: 16 - - headerText: "Jmin - Junk packet minimum size" - textFieldText: clientJunkPacketMinSize - textField.validator: IntValidator { bottom: 0 } - parentFlickable: fl - - textField.onEditingFinished: { - if (textFieldText !== clientJunkPacketMinSize) { - clientJunkPacketMinSize = textFieldText - } - } - - checkEmptyText: true - - KeyNavigation.tab: junkPacketMaxSizeTextField.textField - } - - TextFieldWithHeaderType { - id: junkPacketMaxSizeTextField - Layout.fillWidth: true - Layout.topMargin: 16 - - headerText: "Jmax - Junk packet maximum size" - textFieldText: clientJunkPacketMaxSize - textField.validator: IntValidator { bottom: 0 } - parentFlickable: fl - - textField.onEditingFinished: { - if (textFieldText !== clientJunkPacketMaxSize) { - clientJunkPacketMaxSize = textFieldText - } - } - - checkEmptyText: true - - } - - Header2TextType { - Layout.fillWidth: true - Layout.topMargin: 16 - - text: qsTr("Server settings") - } - - TextFieldWithHeaderType { - id: portTextField - Layout.fillWidth: true - Layout.topMargin: 8 - - enabled: false - - headerText: qsTr("Port") - textFieldText: port - } - - TextFieldWithHeaderType { - id: initPacketJunkSizeTextField - Layout.fillWidth: true - Layout.topMargin: 16 - - enabled: false - - headerText: "S1 - Init packet junk size" - textFieldText: serverInitPacketJunkSize - } - - TextFieldWithHeaderType { - id: responsePacketJunkSizeTextField - Layout.fillWidth: true - Layout.topMargin: 16 - - enabled: false - - headerText: "S2 - Response packet junk size" - textFieldText: serverResponsePacketJunkSize - } - - TextFieldWithHeaderType { - id: initPacketMagicHeaderTextField - Layout.fillWidth: true - Layout.topMargin: 16 - - enabled: false - - headerText: "H1 - Init packet magic header" - textFieldText: serverInitPacketMagicHeader - } - - TextFieldWithHeaderType { - id: responsePacketMagicHeaderTextField - Layout.fillWidth: true - Layout.topMargin: 16 - - enabled: false - - headerText: "H2 - Response packet magic header" - textFieldText: serverResponsePacketMagicHeader - } - - TextFieldWithHeaderType { - id: underloadPacketMagicHeaderTextField - Layout.fillWidth: true - Layout.topMargin: 16 - parentFlickable: fl - - enabled: false - - headerText: "H3 - Underload packet magic header" - textFieldText: serverUnderloadPacketMagicHeader - } - - TextFieldWithHeaderType { - id: transportPacketMagicHeaderTextField - Layout.fillWidth: true - Layout.topMargin: 16 - - enabled: false - - headerText: "H4 - Transport packet magic header" - textFieldText: serverTransportPacketMagicHeader + textField.onEditingFinished: { + if (textFieldText !== clientMtu) { + clientMtu = textFieldText } } + checkEmptyText: true + KeyNavigation.tab: junkPacketCountTextField.textField + } + + TextFieldWithHeaderType { + id: junkPacketCountTextField + Layout.fillWidth: true + Layout.topMargin: 16 + + headerText: "Jc - Junk packet count" + textFieldText: clientJunkPacketCount + textField.validator: IntValidator { bottom: 0 } + + textField.onEditingFinished: { + if (textFieldText !== clientJunkPacketCount) { + clientJunkPacketCount = textFieldText + } + } + + checkEmptyText: true + + KeyNavigation.tab: junkPacketMinSizeTextField.textField + } + + TextFieldWithHeaderType { + id: junkPacketMinSizeTextField + Layout.fillWidth: true + Layout.topMargin: 16 + + headerText: "Jmin - Junk packet minimum size" + textFieldText: clientJunkPacketMinSize + textField.validator: IntValidator { bottom: 0 } + + textField.onEditingFinished: { + if (textFieldText !== clientJunkPacketMinSize) { + clientJunkPacketMinSize = textFieldText + } + } + + checkEmptyText: true + + KeyNavigation.tab: junkPacketMaxSizeTextField.textField + } + + TextFieldWithHeaderType { + id: junkPacketMaxSizeTextField + Layout.fillWidth: true + Layout.topMargin: 16 + + headerText: "Jmax - Junk packet maximum size" + textFieldText: clientJunkPacketMaxSize + textField.validator: IntValidator { bottom: 0 } + + textField.onEditingFinished: { + if (textFieldText !== clientJunkPacketMaxSize) { + clientJunkPacketMaxSize = textFieldText + } + } + + checkEmptyText: true + + } + + Header2TextType { + Layout.fillWidth: true + Layout.topMargin: 16 + + text: qsTr("Server settings") + } + + TextFieldWithHeaderType { + id: portTextField + Layout.fillWidth: true + Layout.topMargin: 8 + + enabled: false + + headerText: qsTr("Port") + textFieldText: port + } + + TextFieldWithHeaderType { + id: initPacketJunkSizeTextField + Layout.fillWidth: true + Layout.topMargin: 16 + + enabled: false + + headerText: "S1 - Init packet junk size" + textFieldText: serverInitPacketJunkSize + } + + TextFieldWithHeaderType { + id: responsePacketJunkSizeTextField + Layout.fillWidth: true + Layout.topMargin: 16 + + enabled: false + + headerText: "S2 - Response packet junk size" + textFieldText: serverResponsePacketJunkSize + } + + TextFieldWithHeaderType { + id: initPacketMagicHeaderTextField + Layout.fillWidth: true + Layout.topMargin: 16 + + enabled: false + + headerText: "H1 - Init packet magic header" + textFieldText: serverInitPacketMagicHeader + } + + TextFieldWithHeaderType { + id: responsePacketMagicHeaderTextField + Layout.fillWidth: true + Layout.topMargin: 16 + + enabled: false + + headerText: "H2 - Response packet magic header" + textFieldText: serverResponsePacketMagicHeader + } + + TextFieldWithHeaderType { + id: underloadPacketMagicHeaderTextField + Layout.fillWidth: true + Layout.topMargin: 16 + parentFlickable: fl + + enabled: false + + headerText: "H3 - Underload packet magic header" + textFieldText: serverUnderloadPacketMagicHeader + } + + TextFieldWithHeaderType { + id: transportPacketMagicHeaderTextField + Layout.fillWidth: true + Layout.topMargin: 16 + + enabled: false + + headerText: "H4 - Transport packet magic header" + textFieldText: serverTransportPacketMagicHeader } } } @@ -282,6 +278,11 @@ PageType { text: qsTr("Save") + onActiveFocusChanged: { + if(activeFocus) { + listview.positionViewAtEnd() + } + } clickedFunc: function() { forceActiveFocus() diff --git a/client/ui/qml/Pages2/PageProtocolAwgSettings.qml b/client/ui/qml/Pages2/PageProtocolAwgSettings.qml index d03b3580..3093e134 100644 --- a/client/ui/qml/Pages2/PageProtocolAwgSettings.qml +++ b/client/ui/qml/Pages2/PageProtocolAwgSettings.qml @@ -2,6 +2,8 @@ import QtQuick import QtQuick.Controls import QtQuick.Layouts +import QtCore + import SortFilterProxyModel 0.2 import PageEnum 1.0 @@ -31,335 +33,354 @@ PageType { } } - FlickableType { - id: fl + ListView { + id: listview + anchors.top: backButtonLayout.bottom anchors.bottom: parent.bottom - contentHeight: content.implicitHeight - Column { - id: content + width: parent.width - anchors.top: parent.top - anchors.left: parent.left - anchors.right: parent.right + property bool isFocusable: true - ListView { - id: listview + Keys.onTabPressed: { + FocusController.nextKeyTabItem() + } - width: parent.width - height: listview.contentItem.height + Keys.onBacktabPressed: { + FocusController.previousKeyTabItem() + } - clip: true - interactive: false + Keys.onUpPressed: { + FocusController.nextKeyUpItem() + } - model: AwgConfigModel + Keys.onDownPressed: { + FocusController.nextKeyDownItem() + } - delegate: Item { - id: delegateItem - implicitWidth: listview.width - implicitHeight: col.implicitHeight + Keys.onLeftPressed: { + FocusController.nextKeyLeftItem() + } - property alias portTextField: portTextField - property bool isEnabled: ServersModel.isProcessedServerHasWriteAccess() + Keys.onRightPressed: { + FocusController.nextKeyRightItem() + } - ColumnLayout { - id: col + clip: true - anchors.top: parent.top - anchors.left: parent.left - anchors.right: parent.right + model: AwgConfigModel - anchors.leftMargin: 16 - anchors.rightMargin: 16 + delegate: Item { + id: delegateItem + implicitWidth: listview.width + implicitHeight: col.implicitHeight - spacing: 0 + property alias portTextField: portTextField + property bool isEnabled: ServersModel.isProcessedServerHasWriteAccess() - HeaderType { - Layout.fillWidth: true + ColumnLayout { + id: col - headerText: qsTr("AmneziaWG settings") + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + + anchors.leftMargin: 16 + anchors.rightMargin: 16 + + spacing: 0 + + HeaderType { + Layout.fillWidth: true + + headerText: qsTr("AmneziaWG settings") + } + + TextFieldWithHeaderType { + id: portTextField + Layout.fillWidth: true + Layout.topMargin: 40 + + enabled: delegateItem.isEnabled + + headerText: qsTr("Port") + textFieldText: port + textField.maximumLength: 5 + textField.validator: IntValidator { bottom: 1; top: 65535 } + + textField.onEditingFinished: { + if (textFieldText !== port) { + port = textFieldText + } + } + + checkEmptyText: true + } + + TextFieldWithHeaderType { + id: mtuTextField + Layout.fillWidth: true + Layout.topMargin: 16 + + headerText: qsTr("MTU") + textFieldText: mtu + textField.validator: IntValidator { bottom: 576; top: 65535 } + + textField.onEditingFinished: { + if (textFieldText === "") { + textFieldText = "0" + } + if (textFieldText !== mtu) { + mtu = textFieldText + } + } + checkEmptyText: true + } + + TextFieldWithHeaderType { + id: junkPacketCountTextField + Layout.fillWidth: true + Layout.topMargin: 16 + + headerText: qsTr("Jc - Junk packet count") + textFieldText: serverJunkPacketCount + textField.validator: IntValidator { bottom: 0 } + + textField.onEditingFinished: { + if (textFieldText === "") { + textFieldText = "0" } - TextFieldWithHeaderType { - id: portTextField - Layout.fillWidth: true - Layout.topMargin: 40 + if (textFieldText !== serverJunkPacketCount) { + serverJunkPacketCount = textFieldText + } + } - enabled: delegateItem.isEnabled + checkEmptyText: true + } - headerText: qsTr("Port") - textFieldText: port - textField.maximumLength: 5 - textField.validator: IntValidator { bottom: 1; top: 65535 } - parentFlickable: fl + TextFieldWithHeaderType { + id: junkPacketMinSizeTextField + Layout.fillWidth: true + Layout.topMargin: 16 - textField.onEditingFinished: { - if (textFieldText !== port) { - port = textFieldText - } + headerText: qsTr("Jmin - Junk packet minimum size") + textFieldText: serverJunkPacketMinSize + textField.validator: IntValidator { bottom: 0 } + + textField.onEditingFinished: { + if (textFieldText !== serverJunkPacketMinSize) { + serverJunkPacketMinSize = textFieldText + } + } + + checkEmptyText: true + } + + TextFieldWithHeaderType { + id: junkPacketMaxSizeTextField + Layout.fillWidth: true + Layout.topMargin: 16 + + headerText: qsTr("Jmax - Junk packet maximum size") + textFieldText: serverJunkPacketMaxSize + textField.validator: IntValidator { bottom: 0 } + + textField.onEditingFinished: { + if (textFieldText !== serverJunkPacketMaxSize) { + serverJunkPacketMaxSize = textFieldText + } + } + + checkEmptyText: true + } + + TextFieldWithHeaderType { + id: initPacketJunkSizeTextField + Layout.fillWidth: true + Layout.topMargin: 16 + + headerText: qsTr("S1 - Init packet junk size") + textFieldText: serverInitPacketJunkSize + textField.validator: IntValidator { bottom: 0 } + + textField.onEditingFinished: { + if (textFieldText !== serverInitPacketJunkSize) { + serverInitPacketJunkSize = textFieldText + } + } + + checkEmptyText: true + + onActiveFocusChanged: { + if(activeFocus) { + listview.positionViewAtEnd() + } + } + } + + TextFieldWithHeaderType { + id: responsePacketJunkSizeTextField + Layout.fillWidth: true + Layout.topMargin: 16 + + headerText: qsTr("S2 - Response packet junk size") + textFieldText: serverResponsePacketJunkSize + textField.validator: IntValidator { bottom: 0 } + + textField.onEditingFinished: { + if (textFieldText !== serverResponsePacketJunkSize) { + serverResponsePacketJunkSize = textFieldText + } + } + + checkEmptyText: true + + onActiveFocusChanged: { + if(activeFocus) { + listview.positionViewAtEnd() + } + } + } + + TextFieldWithHeaderType { + id: initPacketMagicHeaderTextField + Layout.fillWidth: true + Layout.topMargin: 16 + + headerText: qsTr("H1 - Init packet magic header") + textFieldText: serverInitPacketMagicHeader + textField.validator: IntValidator { bottom: 0 } + + textField.onEditingFinished: { + if (textFieldText !== serverInitPacketMagicHeader) { + serverInitPacketMagicHeader = textFieldText + } + } + + checkEmptyText: true + } + + TextFieldWithHeaderType { + id: responsePacketMagicHeaderTextField + Layout.fillWidth: true + Layout.topMargin: 16 + + headerText: qsTr("H2 - Response packet magic header") + textFieldText: serverResponsePacketMagicHeader + textField.validator: IntValidator { bottom: 0 } + + textField.onEditingFinished: { + if (textFieldText !== serverResponsePacketMagicHeader) { + serverResponsePacketMagicHeader = textFieldText + } + } + + checkEmptyText: true + } + + TextFieldWithHeaderType { + id: transportPacketMagicHeaderTextField + Layout.fillWidth: true + Layout.topMargin: 16 + + headerText: qsTr("H4 - Transport packet magic header") + textFieldText: serverTransportPacketMagicHeader + textField.validator: IntValidator { bottom: 0 } + + textField.onEditingFinished: { + if (textFieldText !== serverTransportPacketMagicHeader) { + serverTransportPacketMagicHeader = textFieldText + } + } + + checkEmptyText: true + } + + TextFieldWithHeaderType { + id: underloadPacketMagicHeaderTextField + Layout.fillWidth: true + Layout.topMargin: 16 + + headerText: qsTr("H3 - Underload packet magic header") + textFieldText: serverUnderloadPacketMagicHeader + textField.validator: IntValidator { bottom: 0 } + + textField.onEditingFinished: { + if (textFieldText !== serverUnderloadPacketMagicHeader) { + serverUnderloadPacketMagicHeader = textFieldText + } + } + + checkEmptyText: true + } + + BasicButtonType { + id: saveRestartButton + + Layout.fillWidth: true + Layout.topMargin: 24 + Layout.bottomMargin: 24 + + enabled: underloadPacketMagicHeaderTextField.errorText === "" && + transportPacketMagicHeaderTextField.errorText === "" && + responsePacketMagicHeaderTextField.errorText === "" && + initPacketMagicHeaderTextField.errorText === "" && + responsePacketJunkSizeTextField.errorText === "" && + initPacketJunkSizeTextField.errorText === "" && + junkPacketMaxSizeTextField.errorText === "" && + junkPacketMinSizeTextField.errorText === "" && + junkPacketCountTextField.errorText === "" && + portTextField.errorText === "" + + text: qsTr("Save") + + onActiveFocusChanged: { + if(activeFocus) { + listview.positionViewAtEnd() + } + } + + clickedFunc: function() { + forceActiveFocus() + + if (delegateItem.isEnabled) { + if (AwgConfigModel.isHeadersEqual(underloadPacketMagicHeaderTextField.textField.text, + transportPacketMagicHeaderTextField.textField.text, + responsePacketMagicHeaderTextField.textField.text, + initPacketMagicHeaderTextField.textField.text)) { + PageController.showErrorMessage(qsTr("The values of the H1-H4 fields must be unique")) + return } - checkEmptyText: true - } - - TextFieldWithHeaderType { - id: mtuTextField - Layout.fillWidth: true - Layout.topMargin: 16 - - headerText: qsTr("MTU") - textFieldText: mtu - textField.validator: IntValidator { bottom: 576; top: 65535 } - - textField.onEditingFinished: { - if (textFieldText === "") { - textFieldText = "0" - } - if (textFieldText !== mtu) { - mtu = textFieldText - } - } - checkEmptyText: true - } - - TextFieldWithHeaderType { - id: junkPacketCountTextField - Layout.fillWidth: true - Layout.topMargin: 16 - - headerText: qsTr("Jc - Junk packet count") - textFieldText: serverJunkPacketCount - textField.validator: IntValidator { bottom: 0 } - parentFlickable: fl - - textField.onEditingFinished: { - if (textFieldText === "") { - textFieldText = "0" - } - - if (textFieldText !== serverJunkPacketCount) { - serverJunkPacketCount = textFieldText - } - } - - checkEmptyText: true - } - - TextFieldWithHeaderType { - id: junkPacketMinSizeTextField - Layout.fillWidth: true - Layout.topMargin: 16 - - headerText: qsTr("Jmin - Junk packet minimum size") - textFieldText: serverJunkPacketMinSize - textField.validator: IntValidator { bottom: 0 } - parentFlickable: fl - - textField.onEditingFinished: { - if (textFieldText !== serverJunkPacketMinSize) { - serverJunkPacketMinSize = textFieldText - } - } - - checkEmptyText: true - } - - TextFieldWithHeaderType { - id: junkPacketMaxSizeTextField - Layout.fillWidth: true - Layout.topMargin: 16 - - headerText: qsTr("Jmax - Junk packet maximum size") - textFieldText: serverJunkPacketMaxSize - textField.validator: IntValidator { bottom: 0 } - parentFlickable: fl - - textField.onEditingFinished: { - if (textFieldText !== serverJunkPacketMaxSize) { - serverJunkPacketMaxSize = textFieldText - } - } - - checkEmptyText: true - } - - TextFieldWithHeaderType { - id: initPacketJunkSizeTextField - Layout.fillWidth: true - Layout.topMargin: 16 - - headerText: qsTr("S1 - Init packet junk size") - textFieldText: serverInitPacketJunkSize - textField.validator: IntValidator { bottom: 0 } - parentFlickable: fl - - textField.onEditingFinished: { - if (textFieldText !== serverInitPacketJunkSize) { - serverInitPacketJunkSize = textFieldText - } - } - - checkEmptyText: true - } - - TextFieldWithHeaderType { - id: responsePacketJunkSizeTextField - Layout.fillWidth: true - Layout.topMargin: 16 - - headerText: qsTr("S2 - Response packet junk size") - textFieldText: serverResponsePacketJunkSize - textField.validator: IntValidator { bottom: 0 } - parentFlickable: fl - - textField.onEditingFinished: { - if (textFieldText !== serverResponsePacketJunkSize) { - serverResponsePacketJunkSize = textFieldText - } - } - - checkEmptyText: true - } - - TextFieldWithHeaderType { - id: initPacketMagicHeaderTextField - Layout.fillWidth: true - Layout.topMargin: 16 - - headerText: qsTr("H1 - Init packet magic header") - textFieldText: serverInitPacketMagicHeader - textField.validator: IntValidator { bottom: 0 } - parentFlickable: fl - - textField.onEditingFinished: { - if (textFieldText !== serverInitPacketMagicHeader) { - serverInitPacketMagicHeader = textFieldText - } - } - - checkEmptyText: true - } - - TextFieldWithHeaderType { - id: responsePacketMagicHeaderTextField - Layout.fillWidth: true - Layout.topMargin: 16 - - headerText: qsTr("H2 - Response packet magic header") - textFieldText: serverResponsePacketMagicHeader - textField.validator: IntValidator { bottom: 0 } - parentFlickable: fl - - textField.onEditingFinished: { - if (textFieldText !== serverResponsePacketMagicHeader) { - serverResponsePacketMagicHeader = textFieldText - } - } - - checkEmptyText: true - } - - TextFieldWithHeaderType { - id: transportPacketMagicHeaderTextField - Layout.fillWidth: true - Layout.topMargin: 16 - - headerText: qsTr("H4 - Transport packet magic header") - textFieldText: serverTransportPacketMagicHeader - textField.validator: IntValidator { bottom: 0 } - parentFlickable: fl - - textField.onEditingFinished: { - if (textFieldText !== serverTransportPacketMagicHeader) { - serverTransportPacketMagicHeader = textFieldText - } - } - - checkEmptyText: true - } - - TextFieldWithHeaderType { - id: underloadPacketMagicHeaderTextField - Layout.fillWidth: true - Layout.topMargin: 16 - parentFlickable: fl - - headerText: qsTr("H3 - Underload packet magic header") - textFieldText: serverUnderloadPacketMagicHeader - textField.validator: IntValidator { bottom: 0 } - - textField.onEditingFinished: { - if (textFieldText !== serverUnderloadPacketMagicHeader) { - serverUnderloadPacketMagicHeader = textFieldText - } - } - - checkEmptyText: true - } - - BasicButtonType { - id: saveRestartButton - parentFlickable: fl - - Layout.fillWidth: true - Layout.topMargin: 24 - Layout.bottomMargin: 24 - - enabled: underloadPacketMagicHeaderTextField.errorText === "" && - transportPacketMagicHeaderTextField.errorText === "" && - responsePacketMagicHeaderTextField.errorText === "" && - initPacketMagicHeaderTextField.errorText === "" && - responsePacketJunkSizeTextField.errorText === "" && - initPacketJunkSizeTextField.errorText === "" && - junkPacketMaxSizeTextField.errorText === "" && - junkPacketMinSizeTextField.errorText === "" && - junkPacketCountTextField.errorText === "" && - portTextField.errorText === "" - - text: qsTr("Save") - - clickedFunc: function() { - forceActiveFocus() - - if (delegateItem.isEnabled) { - if (AwgConfigModel.isHeadersEqual(underloadPacketMagicHeaderTextField.textField.text, - transportPacketMagicHeaderTextField.textField.text, - responsePacketMagicHeaderTextField.textField.text, - initPacketMagicHeaderTextField.textField.text)) { - PageController.showErrorMessage(qsTr("The values of the H1-H4 fields must be unique")) - return - } - - if (AwgConfigModel.isPacketSizeEqual(parseInt(initPacketJunkSizeTextField.textField.text), - parseInt(responsePacketJunkSizeTextField.textField.text))) { - PageController.showErrorMessage(qsTr("The value of the field S1 + message initiation size (148) must not equal S2 + message response size (92)")) - return - } - } - - var headerText = qsTr("Save settings?") - var descriptionText = qsTr("All users with whom you shared a connection with will no longer be able to connect to it.") - var yesButtonText = qsTr("Continue") - var noButtonText = qsTr("Cancel") - - var yesButtonFunction = function() { - if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) { - PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection")) - return - } - - PageController.goToPage(PageEnum.PageSetupWizardInstalling); - InstallController.updateContainer(AwgConfigModel.getConfig()) - } - var noButtonFunction = function() { - if (!GC.isMobile()) { - saveRestartButton.forceActiveFocus() - } - } - showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) + if (AwgConfigModel.isPacketSizeEqual(parseInt(initPacketJunkSizeTextField.textField.text), + parseInt(responsePacketJunkSizeTextField.textField.text))) { + PageController.showErrorMessage(qsTr("The value of the field S1 + message initiation size (148) must not equal S2 + message response size (92)")) + return } } + + var headerText = qsTr("Save settings?") + var descriptionText = qsTr("All users with whom you shared a connection with will no longer be able to connect to it.") + var yesButtonText = qsTr("Continue") + var noButtonText = qsTr("Cancel") + + var yesButtonFunction = function() { + if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) { + PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection")) + return + } + + PageController.goToPage(PageEnum.PageSetupWizardInstalling); + InstallController.updateContainer(AwgConfigModel.getConfig()) + } + var noButtonFunction = function() { + if (!GC.isMobile()) { + saveRestartButton.forceActiveFocus() + } + } + showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) } } } diff --git a/client/ui/qml/Pages2/PageSettingsServerProtocol.qml b/client/ui/qml/Pages2/PageSettingsServerProtocol.qml index ab0cd291..ade94ebb 100644 --- a/client/ui/qml/Pages2/PageSettingsServerProtocol.qml +++ b/client/ui/qml/Pages2/PageSettingsServerProtocol.qml @@ -49,21 +49,36 @@ PageType { height: protocols.contentItem.height clip: true interactive: true - model: ProtocolsModel - property int currentFocusIndex: 0 + property bool isFocusable: true - activeFocusOnTab: true - onActiveFocusChanged: { - if (activeFocus) { - this.currentFocusIndex = 0 - protocols.itemAtIndex(currentFocusIndex).focusItem.forceActiveFocus() - } + Keys.onTabPressed: { + FocusController.nextKeyTabItem() } - delegate: Item { - property var focusItem: clientSettings.rightButton + Keys.onBacktabPressed: { + FocusController.previousKeyTabItem() + } + Keys.onUpPressed: { + FocusController.nextKeyUpItem() + } + + Keys.onDownPressed: { + FocusController.nextKeyDownItem() + } + + Keys.onLeftPressed: { + FocusController.nextKeyLeftItem() + } + + Keys.onRightPressed: { + FocusController.nextKeyRightItem() + } + + model: ProtocolsModel + + delegate: Item { implicitWidth: protocols.width implicitHeight: delegateContent.implicitHeight @@ -143,107 +158,112 @@ PageType { } } } - } - LabelWithButtonType { - id: clearCacheButton + footer: ColumnLayout { + width: header.width - Layout.fillWidth: true + LabelWithButtonType { + id: clearCacheButton - visible: root.isClearCacheVisible + Layout.fillWidth: true - text: qsTr("Clear profile") + visible: root.isClearCacheVisible - clickedFunction: function() { - var headerText = qsTr("Clear %1 profile?").arg(ContainersModel.getProcessedContainerName()) - var descriptionText = qsTr("The connection configuration will be deleted for this device only") - var yesButtonText = qsTr("Continue") - var noButtonText = qsTr("Cancel") + text: qsTr("Clear profile") - var yesButtonFunction = function() { - if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) { - var message = qsTr("Unable to clear %1 profile while there is an active connection").arg(ContainersModel.getProcessedContainerName()) - PageController.showNotificationMessage(message) - return + clickedFunction: function() { + var headerText = qsTr("Clear %1 profile?").arg(ContainersModel.getProcessedContainerName()) + var descriptionText = qsTr("The connection configuration will be deleted for this device only") + var yesButtonText = qsTr("Continue") + var noButtonText = qsTr("Cancel") + + var yesButtonFunction = function() { + if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) { + var message = qsTr("Unable to clear %1 profile while there is an active connection").arg(ContainersModel.getProcessedContainerName()) + PageController.showNotificationMessage(message) + return + } + + PageController.showBusyIndicator(true) + InstallController.clearCachedProfile() + PageController.showBusyIndicator(false) + } + var noButtonFunction = function() { + // if (!GC.isMobile()) { + // focusItem.forceActiveFocus() + // } + } + + showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) } - PageController.showBusyIndicator(true) - InstallController.clearCachedProfile() - PageController.showBusyIndicator(false) - } - var noButtonFunction = function() { - if (!GC.isMobile()) { - focusItem.forceActiveFocus() + MouseArea { + anchors.fill: clearCacheButton + cursorShape: Qt.PointingHandCursor + enabled: false } } - showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) - } + DividerType { + Layout.fillWidth: true + Layout.leftMargin: 16 + Layout.rightMargin: 16 - MouseArea { - anchors.fill: clearCacheButton - cursorShape: Qt.PointingHandCursor - enabled: false - } - } - - DividerType { - Layout.fillWidth: true - Layout.leftMargin: 16 - Layout.rightMargin: 16 - - visible: root.isClearCacheVisible - } - - LabelWithButtonType { - id: removeButton - - Layout.fillWidth: true - - visible: ServersModel.isProcessedServerHasWriteAccess() - - text: qsTr("Remove ") - textColor: AmneziaStyle.color.vibrantRed - - clickedFunction: function() { - var headerText = qsTr("Remove %1 from server?").arg(ContainersModel.getProcessedContainerName()) - var descriptionText = qsTr("All users with whom you shared a connection will no longer be able to connect to it.") - var yesButtonText = qsTr("Continue") - var noButtonText = qsTr("Cancel") - - var yesButtonFunction = function() { - if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected - && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) { - PageController.showNotificationMessage(qsTr("Cannot remove active container")) - } else - { - PageController.goToPage(PageEnum.PageDeinstalling) - InstallController.removeProcessedContainer() - } + visible: root.isClearCacheVisible } - var noButtonFunction = function() { - if (!GC.isMobile()) { - focusItem.forceActiveFocus() + + LabelWithButtonType { + id: removeButton + + Layout.fillWidth: true + + visible: ServersModel.isProcessedServerHasWriteAccess() + + text: qsTr("Remove ") + textColor: AmneziaStyle.color.vibrantRed + + clickedFunction: function() { + var headerText = qsTr("Remove %1 from server?").arg(ContainersModel.getProcessedContainerName()) + var descriptionText = qsTr("All users with whom you shared a connection will no longer be able to connect to it.") + var yesButtonText = qsTr("Continue") + var noButtonText = qsTr("Cancel") + + var yesButtonFunction = function() { + if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected + && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) { + PageController.showNotificationMessage(qsTr("Cannot remove active container")) + } else + { + PageController.goToPage(PageEnum.PageDeinstalling) + InstallController.removeProcessedContainer() + } + } + var noButtonFunction = function() { + if (!GC.isMobile()) { + focusItem.forceActiveFocus() + } + } + + showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) + } + + MouseArea { + anchors.fill: removeButton + cursorShape: Qt.PointingHandCursor + enabled: false } } - showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) - } + DividerType { + Layout.fillWidth: true + Layout.leftMargin: 16 + Layout.rightMargin: 16 - MouseArea { - anchors.fill: removeButton - cursorShape: Qt.PointingHandCursor - enabled: false + visible: ServersModel.isProcessedServerHasWriteAccess() + } } } - DividerType { - Layout.fillWidth: true - Layout.leftMargin: 16 - Layout.rightMargin: 16 - - visible: ServersModel.isProcessedServerHasWriteAccess() - } } } From 766e1c92fcf610498a7afcb543257509cb961020 Mon Sep 17 00:00:00 2001 From: Cyril Anisimov Date: Tue, 22 Oct 2024 23:58:53 +0200 Subject: [PATCH 21/40] fix focus on users on page share --- client/ui/qml/Pages2/PageShare.qml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/client/ui/qml/Pages2/PageShare.qml b/client/ui/qml/Pages2/PageShare.qml index 7bf31067..47ca08f4 100644 --- a/client/ui/qml/Pages2/PageShare.qml +++ b/client/ui/qml/Pages2/PageShare.qml @@ -661,6 +661,32 @@ PageType { visible: accessTypeSelector.currentIndex === 1 + property bool isFocusable: true + + Keys.onTabPressed: { + FocusController.nextKeyTabItem() + } + + Keys.onBacktabPressed: { + FocusController.previousKeyTabItem() + } + + Keys.onUpPressed: { + FocusController.nextKeyUpItem() + } + + Keys.onDownPressed: { + FocusController.nextKeyDownItem() + } + + Keys.onLeftPressed: { + FocusController.nextKeyLeftItem() + } + + Keys.onRightPressed: { + FocusController.nextKeyRightItem() + } + model: SortFilterProxyModel { id: proxyClientManagementModel sourceModel: ClientManagementModel From b6c59b08a1654ff9e8834fc4053dc7aef29edc1b Mon Sep 17 00:00:00 2001 From: Cyril Anisimov Date: Tue, 22 Oct 2024 23:59:12 +0200 Subject: [PATCH 22/40] clean up page share --- client/ui/qml/Pages2/PageShare.qml | 31 ++---------------------------- 1 file changed, 2 insertions(+), 29 deletions(-) diff --git a/client/ui/qml/Pages2/PageShare.qml b/client/ui/qml/Pages2/PageShare.qml index 47ca08f4..c238d6c4 100644 --- a/client/ui/qml/Pages2/PageShare.qml +++ b/client/ui/qml/Pages2/PageShare.qml @@ -45,7 +45,7 @@ PageType { shareConnectionDrawer.headerText = qsTr("Connection to ") + serverSelector.text shareConnectionDrawer.configContentHeaderText = qsTr("File with connection settings to ") + serverSelector.text - shareConnectionDrawer. + shareConnectionDrawer.openTriggered() shareConnectionDrawer.contentVisible = false PageController.showBusyIndicator(true) @@ -182,7 +182,6 @@ PageType { shareFullAccessDrawer.openTriggered() } - // KeyNavigation.tab: connectionRadioButton actionButton.onFocusChanged: { console.debug("MOVE THIS LOGIC TO CPP!") if (actionButton.activeFocus) { @@ -229,7 +228,6 @@ PageType { text: qsTr("Share") rightImageSource: "qrc:/images/controls/chevron-right.svg" - // KeyNavigation.tab: focusItem clickedFunction: function() { PageController.goToPage(PageEnum.PageShareFullAccess) @@ -265,12 +263,10 @@ PageType { implicitWidth: (root.width - 32) / 2 text: qsTr("Connection") - // KeyNavigation.tab: usersRadioButton - onClicked: { accessTypeSelector.currentIndex = 0 if (!GC.isMobile()) { - // clientNameTextField.textField.forceActiveFocus() + clientNameTextField.textField.forceActiveFocus() } } } @@ -288,7 +284,6 @@ PageType { ExportController.updateClientManagementModel(ContainersModel.getProcessedContainerIndex(), ServersModel.getProcessedServerCredentials()) PageController.showBusyIndicator(false) - // focusItem.forceActiveFocus() } } } @@ -317,9 +312,6 @@ PageType { textField.maximumLength: 20 checkEmptyText: true - - // KeyNavigation.tab: serverSelector - } DropDownType { @@ -383,8 +375,6 @@ PageType { ServersModel.processedIndex = proxyServersModel.mapToSource(currentIndex) } } - - // KeyNavigation.tab: protocolSelector } DropDownType { @@ -483,12 +473,6 @@ PageType { } } } - - // KeyNavigation.tab: accessTypeSelector.currentIndex === 0 ? - // exportTypeSelector : searchTextField.textField - // isSearchBarVisible ? - // searchTextField.textField : - // usersHeader.actionButton } DropDownType { @@ -532,9 +516,6 @@ PageType { exportTypeSelector.currentIndex = currentIndex } } - - // KeyNavigation.tab: shareButton - } BasicButtonType { @@ -558,7 +539,6 @@ PageType { ExportController.generateConfig(root.connectionTypesModel[exportTypeSelector.currentIndex].type) } } - } Header2Type { @@ -578,7 +558,6 @@ PageType { Keys.onTabPressed: clientsListView.model.count > 0 ? clientsListView.forceActiveFocus() : lastItemTabClicked(focusItem) - } RowLayout { @@ -860,8 +839,6 @@ PageType { text: qsTr("Rename") - // KeyNavigation.tab: revokeButton - clickedFunc: function() { clientNameEditDrawer.openTriggered() } @@ -903,8 +880,6 @@ PageType { textFieldText: clientName textField.maximumLength: 20 checkEmptyText: true - - // KeyNavigation.tab: saveButton } BasicButtonType { @@ -913,7 +888,6 @@ PageType { Layout.fillWidth: true text: qsTr("Save") - // KeyNavigation.tab: focusItem2 clickedFunc: function() { if (clientNameEditor.textFieldText === "") { @@ -948,7 +922,6 @@ PageType { borderWidth: 1 text: qsTr("Revoke") - // KeyNavigation.tab: focusItem1 clickedFunc: function() { var headerText = qsTr("Revoke the config for a user - %1?").arg(clientName) From c77e01fb37547cea98843916ecb7a1e8805afc50 Mon Sep 17 00:00:00 2001 From: Cyril Anisimov Date: Thu, 24 Oct 2024 07:33:10 +0200 Subject: [PATCH 23/40] fix server rename --- client/ui/models/servers_model.cpp | 18 ++++++++++++++++++ client/ui/models/servers_model.h | 2 ++ .../ui/qml/Pages2/PageSettingsServerInfo.qml | 3 ++- 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/client/ui/models/servers_model.cpp b/client/ui/models/servers_model.cpp index c87499a7..7dd76b84 100644 --- a/client/ui/models/servers_model.cpp +++ b/client/ui/models/servers_model.cpp @@ -79,6 +79,12 @@ bool ServersModel::setData(const QModelIndex &index, const QVariant &value, int return true; } +bool ServersModel::setData(const int index, const QVariant &value, int role) +{ + QModelIndex modelIndex = this->index(index); + return setData(modelIndex, value, role); +} + QVariant ServersModel::data(const QModelIndex &index, int role) const { if (!index.isValid() || index.row() < 0 || index.row() >= static_cast(m_servers.size())) { @@ -679,6 +685,18 @@ QVariant ServersModel::getProcessedServerData(const QString roleString) return {}; } +bool ServersModel::setProcessedServerData(const QString& roleString, const QVariant& value) +{ + const auto roles = roleNames(); + for (auto it = roles.begin(); it != roles.end(); it++) { + if (QString(it.value()) == roleString) { + return setData(m_processedServerIndex, value, it.key()); + } + } + + return false; +} + bool ServersModel::isDefaultServerDefaultContainerHasSplitTunneling() { auto server = m_servers.at(m_defaultServerIndex).toObject(); diff --git a/client/ui/models/servers_model.h b/client/ui/models/servers_model.h index 0f18ea30..c15a5b51 100644 --- a/client/ui/models/servers_model.h +++ b/client/ui/models/servers_model.h @@ -46,6 +46,7 @@ public: int rowCount(const QModelIndex &parent = QModelIndex()) const override; bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; + bool setData(const int index, const QVariant &value, int role = Qt::EditRole); QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; QVariant data(const int index, int role = Qt::DisplayRole) const; @@ -115,6 +116,7 @@ public slots: QVariant getDefaultServerData(const QString roleString); QVariant getProcessedServerData(const QString roleString); + bool setProcessedServerData(const QString &roleString, const QVariant &value); bool isDefaultServerDefaultContainerHasSplitTunneling(); diff --git a/client/ui/qml/Pages2/PageSettingsServerInfo.qml b/client/ui/qml/Pages2/PageSettingsServerInfo.qml index 01d559f8..e17cec8f 100644 --- a/client/ui/qml/Pages2/PageSettingsServerInfo.qml +++ b/client/ui/qml/Pages2/PageSettingsServerInfo.qml @@ -147,7 +147,8 @@ PageType { } if (serverName.textFieldText !== root.server.name) { - root.server.name = serverName.textFieldText // TODO(CyAn84): set value to the model + ServersModel.setProcessedServerData("name", serverName.textFieldText); + root.server = proxyServersModel.get(0); } serverNameEditDrawer.closeTriggered() } From 7c3d08d80e632e9f58dd406f1e49c77d7447cabc Mon Sep 17 00:00:00 2001 From: Cyril Anisimov Date: Thu, 24 Oct 2024 14:36:22 +0200 Subject: [PATCH 24/40] fix page share default server selection --- client/ui/qml/Controls2/ListViewWithRadioButtonType.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ui/qml/Controls2/ListViewWithRadioButtonType.qml b/client/ui/qml/Controls2/ListViewWithRadioButtonType.qml index 2ced4335..8409e964 100644 --- a/client/ui/qml/Controls2/ListViewWithRadioButtonType.qml +++ b/client/ui/qml/Controls2/ListViewWithRadioButtonType.qml @@ -59,7 +59,7 @@ ListView { function triggerCurrentItem() { var item = root.itemAtIndex(currentIndex) - var radioButton = item.children[0].children[0] + var radioButton = item.children[0] radioButton.clicked() } From 42645a98f80001abaacf80f8297ac44c3dfd6fc0 Mon Sep 17 00:00:00 2001 From: Cyril Anisimov Date: Thu, 24 Oct 2024 22:09:59 +0200 Subject: [PATCH 25/40] refactor about page for correct focus move --- client/ui/qml/Pages2/PageSettingsAbout.qml | 209 ++++++++++++--------- 1 file changed, 123 insertions(+), 86 deletions(-) diff --git a/client/ui/qml/Pages2/PageSettingsAbout.qml b/client/ui/qml/Pages2/PageSettingsAbout.qml index 7de813e3..86034ae9 100644 --- a/client/ui/qml/Pages2/PageSettingsAbout.qml +++ b/client/ui/qml/Pages2/PageSettingsAbout.qml @@ -21,20 +21,109 @@ PageType { anchors.left: parent.left anchors.right: parent.right anchors.topMargin: 20 + + onActiveFocusChanged: { + if(backButton.enabled && backButton.activeFocus) { + listView.positionViewAtBeginning() + } + } } - FlickableType { - id: fl + QtObject { + id: telegramGroup + + property string title: qsTr("Telegram group") + property string description: qsTr("To discuss features") + property string imageSource: "qrc:/images/controls/telegram.svg" + property var handler: function() { + Qt.openUrlExternally(qsTr("https://t.me/amnezia_vpn_en")) + } + } + + QtObject { + id: mail + + property string title: qsTr("support@amnezia.org") + property string description: qsTr("For reviews and bug reports") + property string imageSource: "qrc:/images/controls/mail.svg" + property var handler: function() { + GC.copyToClipBoard(title) + PageController.showNotificationMessage(qsTr("Copied")) + } + } + + QtObject { + id: github + + property string title: qsTr("GitHub") + property string description: qsTr("Discover the source code") + property string imageSource: "qrc:/images/controls/github.svg" + property var handler: function() { + Qt.openUrlExternally(qsTr("https://github.com/amnezia-vpn/amnezia-client")) + } + } + + QtObject { + id: website + + property string title: qsTr("Website") + property string description: qsTr("Visit official website") + property string imageSource: "qrc:/images/controls/amnezia.svg" + property var handler: function() { + Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl()) + } + } + + property list contacts: [ + telegramGroup, + mail, + github, + website + ] + + ListView { + id: listView anchors.top: backButton.bottom anchors.bottom: parent.bottom - contentHeight: content.height + anchors.right: parent.right + anchors.left: parent.left - ColumnLayout { - id: content + property bool isFocusable: true - anchors.top: parent.top - anchors.left: parent.left - anchors.right: parent.right + Keys.onTabPressed: { + FocusController.nextKeyTabItem() + } + + Keys.onBacktabPressed: { + FocusController.previousKeyTabItem() + } + + Keys.onUpPressed: { + FocusController.nextKeyUpItem() + } + + Keys.onDownPressed: { + FocusController.nextKeyDownItem() + } + + Keys.onLeftPressed: { + FocusController.nextKeyLeftItem() + } + + Keys.onRightPressed: { + FocusController.nextKeyRightItem() + } + + ScrollBar.vertical: ScrollBar { + policy: ScrollBar.AsNeeded + } + + model: contacts + + clip: true + + header: ColumnLayout { + width: listView.width Image { id: image @@ -81,77 +170,29 @@ PageType { text: qsTr("Contacts") } + } + + delegate: ColumnLayout { + width: listView.width LabelWithButtonType { id: telegramButton Layout.fillWidth: true - Layout.topMargin: 16 + Layout.topMargin: 6 - text: qsTr("Telegram group") - descriptionText: qsTr("To discuss features") - leftImageSource: "qrc:/images/controls/telegram.svg" + text: title + descriptionText: description + leftImageSource: imageSource - parentFlickable: fl - - clickedFunction: function() { - Qt.openUrlExternally(qsTr("https://t.me/amnezia_vpn_en")) - } + clickedFunction: handler } DividerType {} - LabelWithButtonType { - id: mailButton - Layout.fillWidth: true + } - text: qsTr("support@amnezia.org") - descriptionText: qsTr("For reviews and bug reports") - leftImageSource: "qrc:/images/controls/mail.svg" - - parentFlickable: fl - - clickedFunction: function() { - GC.copyToClipBoard(text) - PageController.showNotificationMessage(qsTr("Copied")) - } - - } - - DividerType {} - - LabelWithButtonType { - id: githubButton - Layout.fillWidth: true - - text: qsTr("GitHub") - leftImageSource: "qrc:/images/controls/github.svg" - - 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" - - parentFlickable: fl - - clickedFunction: function() { - Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl()) - } - - } - - DividerType {} + footer: ColumnLayout { + width: listView.width CaptionTextType { Layout.fillWidth: true @@ -190,33 +231,29 @@ PageType { text: qsTr("Check for updates") - 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 - implicitHeight: 25 + id: privacyPolicyButton + Layout.alignment: Qt.AlignHCenter + Layout.bottomMargin: 16 + Layout.topMargin: -15 + implicitHeight: 25 - defaultColor: AmneziaStyle.color.transparent - hoveredColor: AmneziaStyle.color.translucentWhite - pressedColor: AmneziaStyle.color.sheerWhite - disabledColor: AmneziaStyle.color.mutedGray - textColor: AmneziaStyle.color.goldenApricot + defaultColor: AmneziaStyle.color.transparent + hoveredColor: AmneziaStyle.color.translucentWhite + pressedColor: AmneziaStyle.color.sheerWhite + disabledColor: AmneziaStyle.color.mutedGray + textColor: AmneziaStyle.color.goldenApricot - text: qsTr("Privacy Policy") + text: qsTr("Privacy Policy") - parentFlickable: fl - - clickedFunc: function() { - Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl() + "/policy") - } + clickedFunc: function() { + Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl() + "/policy") + } } } } From 3d7209ee7a43d3bb271f78b26ddeaab78f8efe9b Mon Sep 17 00:00:00 2001 From: Cyril Anisimov Date: Fri, 25 Oct 2024 03:42:22 +0200 Subject: [PATCH 26/40] fix focus move on list views with header and-or footer --- .../controllers/listViewFocusController.cpp | 57 ++++++++++++++++--- .../ui/controllers/listViewFocusController.h | 3 + 2 files changed, 51 insertions(+), 9 deletions(-) diff --git a/client/ui/controllers/listViewFocusController.cpp b/client/ui/controllers/listViewFocusController.cpp index f07c069c..70801a65 100644 --- a/client/ui/controllers/listViewFocusController.cpp +++ b/client/ui/controllers/listViewFocusController.cpp @@ -142,7 +142,7 @@ void ListViewFocusController::nextDelegate() { switch(m_currentSection) { case Section::Default: { - if(m_header) { + if(hasHeader()) { m_currentSection = Section::Header; viewToBegin(); break; @@ -160,7 +160,7 @@ void ListViewFocusController::nextDelegate() if (m_delegateIndex < (size() - 1)) { m_delegateIndex++; break; - } else if (m_footer) { + } else if (hasFooter()) { m_currentSection = Section::Footer; viewToEnd(); break; @@ -182,7 +182,7 @@ void ListViewFocusController::previousDelegate() { switch(m_currentSection) { case Section::Default: { - if(m_footer) { + if(hasFooter()) { m_currentSection = Section::Footer; break; } @@ -200,7 +200,7 @@ void ListViewFocusController::previousDelegate() if (m_delegateIndex > 0) { m_delegateIndex--; break; - } else if (m_header) { + } else if (hasHeader()) { m_currentSection = Section::Header; break; } @@ -331,15 +331,54 @@ bool ListViewFocusController::isLastFocusItemInDelegate() bool ListViewFocusController::isFirstFocusItemInListView() { - return (m_delegateIndex == 0) && isFirstFocusItemInDelegate(); + switch (m_currentSection) { + case Section::Footer: { + return isFirstFocusItemInDelegate() && !hasHeader() && (size() == 0); + } + case Section::Delegate: { + return isFirstFocusItemInDelegate() && (m_delegateIndex == 0) && !hasHeader(); + } + case Section::Header: { + isFirstFocusItemInDelegate(); + } + case Section::Default: { + return true; + } + default: + qWarning() << "Wrong section"; + return true; + } +} + +bool ListViewFocusController::hasHeader() +{ + return m_header && !getItemsChain(m_header).isEmpty(); +} + +bool ListViewFocusController::hasFooter() +{ + return m_footer && !getItemsChain(m_footer).isEmpty(); } bool ListViewFocusController::isLastFocusItemInListView() { - bool isLastSection = (m_footer && m_currentSection == Section::Footer) - || (!m_footer && (m_currentSection == Section::Delegate) && (m_delegateIndex == size() - 1)) - || (m_header && (m_currentSection == Section::Header) && (size() <= 0) && !m_footer); - return isLastSection && isLastFocusItemInDelegate(); + switch (m_currentSection) { + case Section::Default: { + return !hasHeader() && (size() == 0) && !hasFooter(); + } + case Section::Header: { + return isLastFocusItemInDelegate() && (size() == 0) && !hasFooter(); + } + case Section::Delegate: { + return isLastFocusItemInDelegate() && (m_delegateIndex == size() - 1) && !hasFooter(); + } + case Section::Footer: { + return isLastFocusItemInDelegate(); + } + default: + qWarning() << "Wrong section"; + return true; + } } bool ListViewFocusController::isReturnNeeded() diff --git a/client/ui/controllers/listViewFocusController.h b/client/ui/controllers/listViewFocusController.h index 66c31228..508efa6e 100644 --- a/client/ui/controllers/listViewFocusController.h +++ b/client/ui/controllers/listViewFocusController.h @@ -59,6 +59,9 @@ private: QQuickItem* currentDelegate(); QQuickItem* focusedItem(); + bool hasHeader(); + bool hasFooter(); + QQuickItem* m_listView; QList m_focusChain; Section m_currentSection; From 163399816f77827ed1baf4f64d31880f15d54644 Mon Sep 17 00:00:00 2001 From: Cyril Anisimov Date: Fri, 25 Oct 2024 16:55:46 +0200 Subject: [PATCH 27/40] minor fixes --- client/ui/controllers/focusController.cpp | 10 ++++------ client/ui/qml/Pages2/PageSettingsAbout.qml | 3 +++ client/ui/qml/Pages2/PageShareFullAccess.qml | 4 ++-- client/ui/qml/Pages2/PageStart.qml | 18 ++++++++++++++---- 4 files changed, 23 insertions(+), 12 deletions(-) diff --git a/client/ui/controllers/focusController.cpp b/client/ui/controllers/focusController.cpp index 682f4b0e..66738030 100644 --- a/client/ui/controllers/focusController.cpp +++ b/client/ui/controllers/focusController.cpp @@ -153,8 +153,7 @@ void FocusController::focusNextListViewItem() { if (m_lvfc->isLastFocusItemInListView() || m_lvfc->isReturnNeeded()) { qDebug() << "===>> [Last item in ListView was reached. Going to the NEXT element after ListView]"; - delete m_lvfc; - m_lvfc = nullptr; + resetListView(); nextItem(Direction::Forward); return; } else if (m_lvfc->isLastFocusItemInDelegate()) { @@ -171,8 +170,7 @@ void FocusController::focusPreviousListViewItem() { if (m_lvfc->isFirstFocusItemInListView() || m_lvfc->isReturnNeeded()) { qDebug() << "===>> [First item in ListView was reached. Going to the PREVIOUS element after ListView]"; - delete m_lvfc; - m_lvfc = nullptr; + resetListView(); nextItem(Direction::Backward); return; } else if (m_lvfc->isFirstFocusItemInDelegate()) { @@ -279,7 +277,7 @@ void FocusController::pushRootObject(QObject* object) void FocusController::dropRootObject(QObject* object) { if (m_rootObjects.empty()) { - qDebug() << "ROOT OBJECT is already NULL"; + qDebug() << "ROOT OBJECT is already DEFAULT"; return; } @@ -289,7 +287,7 @@ void FocusController::dropRootObject(QObject* object) if(m_rootObjects.size()) { qDebug() << "===>> ROOT OBJECT is changed to: " << m_rootObjects.top(); } else { - qDebug() << "===>> ROOT OBJECT is changed to NULL"; + qDebug() << "===>> ROOT OBJECT is changed to DEFAULT"; } } else { qWarning() << "===>> TRY TO DROP WRONG ROOT OBJECT: " << m_rootObjects.top() << " SHOULD BE: " << object; diff --git a/client/ui/qml/Pages2/PageSettingsAbout.qml b/client/ui/qml/Pages2/PageSettingsAbout.qml index 86034ae9..8501d2a4 100644 --- a/client/ui/qml/Pages2/PageSettingsAbout.qml +++ b/client/ui/qml/Pages2/PageSettingsAbout.qml @@ -83,6 +83,7 @@ PageType { ListView { id: listView + anchors.top: backButton.bottom anchors.bottom: parent.bottom anchors.right: parent.right @@ -218,6 +219,7 @@ PageType { BasicButtonType { id: checkUpdatesButton + Layout.alignment: Qt.AlignHCenter Layout.topMargin: 8 Layout.bottomMargin: 16 @@ -238,6 +240,7 @@ PageType { BasicButtonType { id: privacyPolicyButton + Layout.alignment: Qt.AlignHCenter Layout.bottomMargin: 16 Layout.topMargin: -15 diff --git a/client/ui/qml/Pages2/PageShareFullAccess.qml b/client/ui/qml/Pages2/PageShareFullAccess.qml index 70451b83..8451835c 100644 --- a/client/ui/qml/Pages2/PageShareFullAccess.qml +++ b/client/ui/qml/Pages2/PageShareFullAccess.qml @@ -104,7 +104,7 @@ PageType { shareConnectionDrawer.headerText = qsTr("Accessing ") + serverSelector.text shareConnectionDrawer.configContentHeaderText = qsTr("File with accessing settings to ") + serverSelector.text - serverSelector.close() + serverSelector.closeTriggered() } Component.onCompleted: { @@ -142,7 +142,7 @@ PageType { shareConnectionDrawer.headerText = qsTr("Connection to ") + serverSelector.text shareConnectionDrawer.configContentHeaderText = qsTr("File with connection settings to ") + serverSelector.text - shareConnectionDrawer.open() + shareConnectionDrawer.openTriggered() shareConnectionDrawer.contentVisible = true PageController.showBusyIndicator(false) diff --git a/client/ui/qml/Pages2/PageStart.qml b/client/ui/qml/Pages2/PageStart.qml index 14cc6896..c08acb7b 100644 --- a/client/ui/qml/Pages2/PageStart.qml +++ b/client/ui/qml/Pages2/PageStart.qml @@ -246,12 +246,22 @@ PageType { } Keys.onPressed: function(event) { - if(event.key === Qt.Key_Tab) { + console.debug(">>>> ", event.key, " Event is caught by StartPage") + switch (event.key) { + case Qt.Key_Tab: + case Qt.Key_Down: + case Qt.Key_Right: FocusController.nextKeyTabItem() + break + case Qt.Key_Backtab: + case Qt.Key_Up: + case Qt.Key_Left: + FocusController.previousKeyTabItem() + break + default: + PageController.keyPressEvent(event.key) + event.accepted = true } - - PageController.keyPressEvent(event.key) - event.accepted = true } } From 2e896ed34f67770f8a510636f6347d7833e53a29 Mon Sep 17 00:00:00 2001 From: Cyril Anisimov Date: Sat, 26 Oct 2024 18:01:14 +0200 Subject: [PATCH 28/40] fix server list back button handler --- client/ui/qml/Components/ServersListView.qml | 3 --- 1 file changed, 3 deletions(-) diff --git a/client/ui/qml/Components/ServersListView.qml b/client/ui/qml/Components/ServersListView.qml index b3663cbd..9270c0e6 100644 --- a/client/ui/qml/Components/ServersListView.qml +++ b/client/ui/qml/Components/ServersListView.qml @@ -115,9 +115,6 @@ ListView { z: 1 - Keys.onEnterPressed: serverInfoButton.clicked() - Keys.onReturnPressed: serverInfoButton.clicked() - onClicked: function() { ServersModel.processedIndex = index PageController.goToPage(PageEnum.PageSettingsServerInfo) From e4d21dc4d7fb1ea8194782b5a1d1b88f0d797f50 Mon Sep 17 00:00:00 2001 From: Cyril Anisimov Date: Sun, 27 Oct 2024 20:16:11 +0100 Subject: [PATCH 29/40] fix spawn signals on switch --- client/ui/qml/Controls2/SwitcherType.qml | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/client/ui/qml/Controls2/SwitcherType.qml b/client/ui/qml/Controls2/SwitcherType.qml index 3569a7d0..77b657e7 100644 --- a/client/ui/qml/Controls2/SwitcherType.qml +++ b/client/ui/qml/Controls2/SwitcherType.qml @@ -159,12 +159,26 @@ Switch { } Keys.onEnterPressed: { - root.checked = !root.checked - root.checkedChanged() + if (!event.isAutoRepeat) { + root.checked = !root.checked + root.checkedChanged() + } + event.accepted = true } Keys.onReturnPressed: { - root.checked = !root.checked - root.checkedChanged() + if (!event.isAutoRepeat) { + root.checked = !root.checked + root.checkedChanged() + } + event.accepted = true + } + + Keys.onSpacePressed: { + if (!event.isAutoRepeat) { + root.checked = !root.checked + root.checkedChanged() + } + event.accepted = true } } From 88958c042f715270a3674cedb7be857904737b2a Mon Sep 17 00:00:00 2001 From: Cyril Anisimov Date: Mon, 28 Oct 2024 18:41:22 +0100 Subject: [PATCH 30/40] fix share details drawer --- client/ui/qml/Components/ShareConnectionDrawer.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ui/qml/Components/ShareConnectionDrawer.qml b/client/ui/qml/Components/ShareConnectionDrawer.qml index dd794a03..eb746d37 100644 --- a/client/ui/qml/Components/ShareConnectionDrawer.qml +++ b/client/ui/qml/Components/ShareConnectionDrawer.qml @@ -150,7 +150,7 @@ DrawerType2 { text: qsTr("Show connection settings") clickedFunc: function() { - configContentDrawer.open() + configContentDrawer.openTriggered() } } @@ -196,7 +196,7 @@ DrawerType2 { anchors.right: parent.right anchors.topMargin: 16 - backButtonFunction: function() { configContentDrawer.close() } + backButtonFunction: function() { configContentDrawer.closeTriggered() } } FlickableType { From f020bdb6e8cea8a6fb7c18133e100438c0578770 Mon Sep 17 00:00:00 2001 From: Cyril Anisimov Date: Wed, 30 Oct 2024 05:58:54 +0100 Subject: [PATCH 31/40] fix drawer open close usage --- client/ui/qml/Components/ConnectionTypeSelectionDrawer.qml | 4 ++-- client/ui/qml/Components/HomeSplitTunnelingDrawer.qml | 2 +- client/ui/qml/Components/InstalledAppsDrawer.qml | 4 ++-- client/ui/qml/Pages2/PageProtocolCloakSettings.qml | 2 +- client/ui/qml/Pages2/PageProtocolOpenVpnSettings.qml | 4 ++-- client/ui/qml/Pages2/PageProtocolRaw.qml | 4 ++-- client/ui/qml/Pages2/PageProtocolShadowSocksSettings.qml | 2 +- client/ui/qml/Pages2/PageServiceSocksProxySettings.qml | 4 ++-- client/ui/qml/Pages2/PageSettingsAppSplitTunneling.qml | 4 ++-- client/ui/qml/main2.qml | 4 ++-- 10 files changed, 17 insertions(+), 17 deletions(-) diff --git a/client/ui/qml/Components/ConnectionTypeSelectionDrawer.qml b/client/ui/qml/Components/ConnectionTypeSelectionDrawer.qml index 1b43a628..c9124d81 100644 --- a/client/ui/qml/Components/ConnectionTypeSelectionDrawer.qml +++ b/client/ui/qml/Components/ConnectionTypeSelectionDrawer.qml @@ -46,7 +46,7 @@ DrawerType2 { clickedFunction: function() { PageController.goToPage(PageEnum.PageSetupWizardCredentials) - root.close() + root.closeTriggered() } } @@ -61,7 +61,7 @@ DrawerType2 { clickedFunction: function() { PageController.goToPage(PageEnum.PageSetupWizardConfigSource) - root.close() + root.closeTriggered() } } diff --git a/client/ui/qml/Components/HomeSplitTunnelingDrawer.qml b/client/ui/qml/Components/HomeSplitTunnelingDrawer.qml index 67696c33..097274a4 100644 --- a/client/ui/qml/Components/HomeSplitTunnelingDrawer.qml +++ b/client/ui/qml/Components/HomeSplitTunnelingDrawer.qml @@ -48,7 +48,7 @@ DrawerType2 { clickedFunction: function() { PageController.goToPage(PageEnum.PageSettingsSplitTunneling) - root.close() + root.closeTriggered() } } diff --git a/client/ui/qml/Components/InstalledAppsDrawer.qml b/client/ui/qml/Components/InstalledAppsDrawer.qml index cb8186a9..ca579491 100644 --- a/client/ui/qml/Components/InstalledAppsDrawer.qml +++ b/client/ui/qml/Components/InstalledAppsDrawer.qml @@ -43,7 +43,7 @@ DrawerType2 { BackButtonType { backButtonImage: "qrc:/images/controls/arrow-left.svg" backButtonFunction: function() { - root.close() + root.closeTriggered() } } @@ -157,7 +157,7 @@ DrawerType2 { PageController.showBusyIndicator(true) AppSplitTunnelingController.addApps(installedAppsModel.getSelectedAppsInfo()) PageController.showBusyIndicator(false) - root.close() + root.closeTriggered() } } } diff --git a/client/ui/qml/Pages2/PageProtocolCloakSettings.qml b/client/ui/qml/Pages2/PageProtocolCloakSettings.qml index c0dbc2ee..fc43bd8e 100644 --- a/client/ui/qml/Pages2/PageProtocolCloakSettings.qml +++ b/client/ui/qml/Pages2/PageProtocolCloakSettings.qml @@ -148,7 +148,7 @@ PageType { clickedFunction: function() { cipherDropDown.text = selectedText cipher = cipherDropDown.text - cipherDropDown.close() + cipherDropDown.closeTriggered() } Component.onCompleted: { diff --git a/client/ui/qml/Pages2/PageProtocolOpenVpnSettings.qml b/client/ui/qml/Pages2/PageProtocolOpenVpnSettings.qml index 3df23ac0..2b1f3f44 100644 --- a/client/ui/qml/Pages2/PageProtocolOpenVpnSettings.qml +++ b/client/ui/qml/Pages2/PageProtocolOpenVpnSettings.qml @@ -199,7 +199,7 @@ PageType { clickedFunction: function() { hashDropDown.text = selectedText hash = hashDropDown.text - hashDropDown.close() + hashDropDown.closeTriggered() } Component.onCompleted: { @@ -248,7 +248,7 @@ PageType { clickedFunction: function() { cipherDropDown.text = selectedText cipher = cipherDropDown.text - cipherDropDown.close() + cipherDropDown.closeTriggered() } Component.onCompleted: { diff --git a/client/ui/qml/Pages2/PageProtocolRaw.qml b/client/ui/qml/Pages2/PageProtocolRaw.qml index 14aa5194..1a530780 100644 --- a/client/ui/qml/Pages2/PageProtocolRaw.qml +++ b/client/ui/qml/Pages2/PageProtocolRaw.qml @@ -93,7 +93,7 @@ PageType { text: qsTr("Show connection options") clickedFunction: function() { - configContentDrawer.open() + configContentDrawer.openTriggered() } MouseArea { @@ -125,7 +125,7 @@ PageType { anchors.topMargin: 16 backButtonFunction: function() { - configContentDrawer.close() + configContentDrawer.closeTriggered() } } diff --git a/client/ui/qml/Pages2/PageProtocolShadowSocksSettings.qml b/client/ui/qml/Pages2/PageProtocolShadowSocksSettings.qml index aa04eb29..44cbd1ce 100644 --- a/client/ui/qml/Pages2/PageProtocolShadowSocksSettings.qml +++ b/client/ui/qml/Pages2/PageProtocolShadowSocksSettings.qml @@ -133,7 +133,7 @@ PageType { clickedFunction: function() { cipherDropDown.text = selectedText cipher = cipherDropDown.text - cipherDropDown.close() + cipherDropDown.closeTriggered() } Component.onCompleted: { diff --git a/client/ui/qml/Pages2/PageServiceSocksProxySettings.qml b/client/ui/qml/Pages2/PageServiceSocksProxySettings.qml index 4c94913e..5eee9a1e 100644 --- a/client/ui/qml/Pages2/PageServiceSocksProxySettings.qml +++ b/client/ui/qml/Pages2/PageServiceSocksProxySettings.qml @@ -322,7 +322,7 @@ PageType { tempPort = portTextField.textFieldText tempUsername = usernameTextField.textFieldText tempPassword = passwordTextField.textFieldText - changeSettingsDrawer.close() + changeSettingsDrawer.closeTriggered() } } } @@ -341,7 +341,7 @@ PageType { clickedFunc: function() { forceActiveFocus() - changeSettingsDrawer.open() + changeSettingsDrawer.openTriggered() } } } diff --git a/client/ui/qml/Pages2/PageSettingsAppSplitTunneling.qml b/client/ui/qml/Pages2/PageSettingsAppSplitTunneling.qml index 2e86a0dc..91501224 100644 --- a/client/ui/qml/Pages2/PageSettingsAppSplitTunneling.qml +++ b/client/ui/qml/Pages2/PageSettingsAppSplitTunneling.qml @@ -127,7 +127,7 @@ PageType { clickedFunction: function() { selector.text = selectedText - selector.close() + selector.closeTriggered() if (AppSplitTunnelingModel.routeMode !== root.routeModesModel[currentIndex].type) { AppSplitTunnelingModel.routeMode = root.routeModesModel[currentIndex].type } @@ -266,7 +266,7 @@ PageType { AppSplitTunnelingController.addApp(fileName) } } else if (Qt.platform.os === "android"){ - installedAppDrawer.open() + installedAppDrawer.openTriggered() } PageController.showBusyIndicator(false) diff --git a/client/ui/qml/main2.qml b/client/ui/qml/main2.qml index 2ab4db69..8b73e62d 100644 --- a/client/ui/qml/main2.qml +++ b/client/ui/qml/main2.qml @@ -85,7 +85,7 @@ Window { } function onShowPassphraseRequestDrawer() { - privateKeyPassphraseDrawer.open() + privateKeyPassphraseDrawer.openTriggered() } function onGoToPageSettingsBackup() { @@ -221,7 +221,7 @@ Window { text: qsTr("Save") clickedFunc: function() { - privateKeyPassphraseDrawer.close() + privateKeyPassphraseDrawer.closeTriggered() PageController.passphraseRequestDrawerClosed(passphrase.textFieldText) } } From dc6c1cdf49c51a64da5f44cdfe4db3d78a5c1d1b Mon Sep 17 00:00:00 2001 From: Cyril Anisimov Date: Wed, 30 Oct 2024 07:46:22 +0100 Subject: [PATCH 32/40] refactor listViewFocusController --- .../controllers/listViewFocusController.cpp | 70 +++++++++---------- .../ui/controllers/listViewFocusController.h | 26 ++++--- 2 files changed, 46 insertions(+), 50 deletions(-) diff --git a/client/ui/controllers/listViewFocusController.cpp b/client/ui/controllers/listViewFocusController.cpp index 70801a65..38e7acc0 100644 --- a/client/ui/controllers/listViewFocusController.cpp +++ b/client/ui/controllers/listViewFocusController.cpp @@ -105,22 +105,25 @@ ListViewFocusController::~ListViewFocusController() } -void ListViewFocusController::viewAtCurrentIndex() +void ListViewFocusController::viewAtCurrentIndex() const { switch(m_currentSection) { case Section::Default: [[fallthrough]]; case Section::Header: { + qDebug() << "===>> [FOCUS ON BEGINNING...]"; QMetaObject::invokeMethod(m_listView, "positionViewAtBeginning"); break; } case Section::Delegate: { + qDebug() << "===>> [FOCUS ON INDEX...]"; QMetaObject::invokeMethod(m_listView, "positionViewAtIndex", Q_ARG(int, m_delegateIndex), // Index Q_ARG(int, 2)); // PositionMode (0 = Visible) break; } case Section::Footer: { + qDebug() << "===>> [FOCUS ON END...]"; QMetaObject::invokeMethod(m_listView, "positionViewAtEnd"); break; } @@ -140,11 +143,13 @@ int ListViewFocusController::currentIndex() const void ListViewFocusController::nextDelegate() { + const auto sectionName = m_currentSectionString[static_cast(m_currentSection)]; + qDebug() << "===>> [nextDelegate... current section: " << sectionName << " ]"; switch(m_currentSection) { case Section::Default: { if(hasHeader()) { m_currentSection = Section::Header; - viewToBegin(); + viewAtCurrentIndex(); break; } [[fallthrough]]; @@ -152,6 +157,7 @@ void ListViewFocusController::nextDelegate() case Section::Header: { if (size() > 0) { m_currentSection = Section::Delegate; + viewAtCurrentIndex(); break; } [[fallthrough]]; @@ -159,16 +165,18 @@ void ListViewFocusController::nextDelegate() case Section::Delegate: if (m_delegateIndex < (size() - 1)) { m_delegateIndex++; + viewAtCurrentIndex(); break; } else if (hasFooter()) { m_currentSection = Section::Footer; - viewToEnd(); + viewAtCurrentIndex(); break; } [[fallthrough]]; case Section::Footer: { m_isReturnNeeded = true; m_currentSection = Section::Default; + viewAtCurrentIndex(); break; } default: { @@ -223,7 +231,7 @@ void ListViewFocusController::decrementIndex() m_delegateIndex--; } -QQuickItem* ListViewFocusController::itemAtIndex(const int index) +QQuickItem* ListViewFocusController::itemAtIndex(const int index) const { QQuickItem* item{nullptr}; @@ -234,7 +242,7 @@ QQuickItem* ListViewFocusController::itemAtIndex(const int index) return item; } -QQuickItem* ListViewFocusController::currentDelegate() +QQuickItem* ListViewFocusController::currentDelegate() const { QQuickItem* result{nullptr}; @@ -259,7 +267,7 @@ QQuickItem* ListViewFocusController::currentDelegate() return result; } -QQuickItem* ListViewFocusController::focusedItem() +QQuickItem* ListViewFocusController::focusedItem() const { return m_focusedItem; } @@ -267,7 +275,7 @@ QQuickItem* ListViewFocusController::focusedItem() void ListViewFocusController::focusNextItem() { if (m_isReturnNeeded) { - qDebug() << "===>> RETURN IS NEEDED..."; + qDebug() << "===>> [ RETURN IS NEEDED... ]"; return; } @@ -283,8 +291,8 @@ void ListViewFocusController::focusNextItem() } m_focusedItemIndex++; m_focusedItem = qobject_cast(m_focusChain.at(m_focusedItemIndex)); - qDebug() << "==>> Focused Item: " << m_focusedItem << " with Index: " << m_focusedItemIndex; - m_focusedItem->forceActiveFocus(); + qDebug() << "==>> [ Focused Item: " << m_focusedItem << " with Index: " << m_focusedItemIndex << " ]"; + m_focusedItem->forceActiveFocus(Qt::TabFocusReason); } void ListViewFocusController::focusPreviousItem() @@ -308,8 +316,8 @@ void ListViewFocusController::focusPreviousItem() } m_focusedItemIndex--; m_focusedItem = qobject_cast(m_focusChain.at(m_focusedItemIndex)); - qDebug() << "==>> Focused Item: " << m_focusedItem << " with Index: " << m_focusedItemIndex; - m_focusedItem->forceActiveFocus(); + qDebug() << "==>> [ Focused Item: " << m_focusedItem << " with Index: " << m_focusedItemIndex << " ]"; + m_focusedItem->forceActiveFocus(Qt::TabFocusReason); } void ListViewFocusController::resetFocusChain() @@ -319,17 +327,27 @@ void ListViewFocusController::resetFocusChain() m_focusedItemIndex = -1; } -bool ListViewFocusController::isFirstFocusItemInDelegate() +bool ListViewFocusController::isFirstFocusItemInDelegate() const { return m_focusedItem && (m_focusedItem == m_focusChain.first()); } -bool ListViewFocusController::isLastFocusItemInDelegate() +bool ListViewFocusController::isLastFocusItemInDelegate() const { return m_focusedItem && (m_focusedItem == m_focusChain.last()); } -bool ListViewFocusController::isFirstFocusItemInListView() +bool ListViewFocusController::hasHeader() const +{ + return m_header && !getItemsChain(m_header).isEmpty(); +} + +bool ListViewFocusController::hasFooter() const +{ + return m_footer && !getItemsChain(m_footer).isEmpty(); +} + +bool ListViewFocusController::isFirstFocusItemInListView() const { switch (m_currentSection) { case Section::Footer: { @@ -350,17 +368,7 @@ bool ListViewFocusController::isFirstFocusItemInListView() } } -bool ListViewFocusController::hasHeader() -{ - return m_header && !getItemsChain(m_header).isEmpty(); -} - -bool ListViewFocusController::hasFooter() -{ - return m_footer && !getItemsChain(m_footer).isEmpty(); -} - -bool ListViewFocusController::isLastFocusItemInListView() +bool ListViewFocusController::isLastFocusItemInListView() const { switch (m_currentSection) { case Section::Default: { @@ -381,17 +389,7 @@ bool ListViewFocusController::isLastFocusItemInListView() } } -bool ListViewFocusController::isReturnNeeded() +bool ListViewFocusController::isReturnNeeded() const { return m_isReturnNeeded; } - -void ListViewFocusController::viewToBegin() -{ - QMetaObject::invokeMethod(m_listView, "positionViewAtBeginning", Qt::AutoConnection); -} - -void ListViewFocusController::viewToEnd() -{ - QMetaObject::invokeMethod(m_listView, "positionViewAtEnd", Qt::AutoConnection); -} diff --git a/client/ui/controllers/listViewFocusController.h b/client/ui/controllers/listViewFocusController.h index 508efa6e..a94b4237 100644 --- a/client/ui/controllers/listViewFocusController.h +++ b/client/ui/controllers/listViewFocusController.h @@ -25,7 +25,7 @@ void printItems(const QList& items, QObject* current_item); */ class ListViewFocusController : public QObject { - // Q_OBJECT + Q_OBJECT public: explicit ListViewFocusController(QQuickItem* listView, QObject* parent = nullptr); ~ListViewFocusController(); @@ -36,14 +36,11 @@ public: void focusNextItem(); void focusPreviousItem(); void resetFocusChain(); - bool isFirstFocusItemInListView(); - bool isFirstFocusItemInDelegate(); - bool isLastFocusItemInListView(); - bool isLastFocusItemInDelegate(); - bool isReturnNeeded(); - void viewToBegin(); - void viewToEnd(); - void viewAtCurrentIndex(); + bool isFirstFocusItemInListView() const; + bool isFirstFocusItemInDelegate() const; + bool isLastFocusItemInListView() const; + bool isLastFocusItemInDelegate() const; + bool isReturnNeeded() const; private: enum class Section { @@ -55,12 +52,13 @@ private: int size() const; int currentIndex() const; - QQuickItem* itemAtIndex(const int index); - QQuickItem* currentDelegate(); - QQuickItem* focusedItem(); + void viewAtCurrentIndex() const; + QQuickItem* itemAtIndex(const int index) const; + QQuickItem* currentDelegate() const; + QQuickItem* focusedItem() const; - bool hasHeader(); - bool hasFooter(); + bool hasHeader() const; + bool hasFooter() const; QQuickItem* m_listView; QList m_focusChain; From 5e9202f6acc2de18ac6c6be3ff802d39e946894c Mon Sep 17 00:00:00 2001 From: Cyril Anisimov Date: Wed, 30 Oct 2024 17:00:57 +0100 Subject: [PATCH 33/40] refactor focusController to make the logic more straightforward --- client/ui/controllers/focusController.cpp | 319 ++++++++++++---------- client/ui/controllers/focusController.h | 11 +- 2 files changed, 178 insertions(+), 152 deletions(-) diff --git a/client/ui/controllers/focusController.cpp b/client/ui/controllers/focusController.cpp index 66738030..b5558f33 100644 --- a/client/ui/controllers/focusController.cpp +++ b/client/ui/controllers/focusController.cpp @@ -72,7 +72,6 @@ FocusController::FocusController(QQmlApplicationEngine* engine, QObject *parent) , m_engine{engine} , m_focusChain{} , m_focusedItem{nullptr} - , m_focusedItemIndex{-1} , m_rootObjects{} , m_defaultFocusItem{QSharedPointer()} , m_lvfc{nullptr} @@ -81,106 +80,15 @@ FocusController::FocusController(QQmlApplicationEngine* engine, QObject *parent) QQuickItem* newDefaultFocusItem = object->findChild("defaultFocusItem"); if(newDefaultFocusItem && m_defaultFocusItem != newDefaultFocusItem) { m_defaultFocusItem.reset(newDefaultFocusItem); - qDebug() << "===>> NEW DEFAULT FOCUS ITEM " << m_defaultFocusItem; + qDebug() << "===>> NEW DEFAULT FOCUS ITEM: " << m_defaultFocusItem; } }); + + QObject::connect(this, &FocusController::focusedItemChanged, this, [this]() { + m_focusedItem->forceActiveFocus(Qt::TabFocusReason); + }); } -void FocusController::nextItem(Direction direction) -{ - reload(direction); - - if (m_lvfc) { - direction == Direction::Forward ? focusNextListViewItem() : focusPreviousListViewItem(); - qDebug() << "===>> [handling the ListView]"; - - return; - } - - if(m_focusChain.empty()) { - qWarning() << "There are no items to navigate"; - return; - } - - if (m_focusedItemIndex == (m_focusChain.size() - 1)) { - qDebug() << "Last focus index. Making it zero..."; - m_focusedItemIndex = 0; - } else { - qDebug() << "Incrementing focus index"; - m_focusedItemIndex++; - } - - m_focusedItem = qobject_cast(m_focusChain.at(m_focusedItemIndex)); - - if(m_focusedItem == nullptr) { - qWarning() << "Failed to get item to focus on. Setting focus on default"; - m_focusedItem = m_defaultFocusItem.get(); - return; - } - - if(isListView(m_focusedItem)) { - qDebug() << "===>> [Found ListView]"; - m_lvfc = new ListViewFocusController(m_focusedItem, this); - if(direction == Direction::Forward) { - m_lvfc->viewToBegin(); - m_lvfc->nextDelegate(); - focusNextListViewItem(); - } else { - m_lvfc->viewToEnd(); - m_lvfc->previousDelegate(); - focusPreviousListViewItem(); - } - return; - } - - qDebug() << "===>> Focused Item: " << m_focusedItem; - m_focusedItem->forceActiveFocus(Qt::TabFocusReason); - - printItems(m_focusChain, m_focusedItem); - - const auto w = m_defaultFocusItem->window(); - - qDebug() << "===>> CURRENT ACTIVE ITEM: " << w->activeFocusItem(); - qDebug() << "===>> CURRENT FOCUS OBJECT: " << w->focusObject(); - if(m_rootObjects.empty()) { - qDebug() << "===>> ROOT OBJECT IS DEFAULT"; - } else { - qDebug() << "===>> ROOT OBJECT: " << m_rootObjects.top(); - } -} - -void FocusController::focusNextListViewItem() -{ - if (m_lvfc->isLastFocusItemInListView() || m_lvfc->isReturnNeeded()) { - qDebug() << "===>> [Last item in ListView was reached. Going to the NEXT element after ListView]"; - resetListView(); - nextItem(Direction::Forward); - return; - } else if (m_lvfc->isLastFocusItemInDelegate()) { - qDebug() << "===>> [End of delegate elements was reached. Going to the next delegate]"; - m_lvfc->resetFocusChain(); - m_lvfc->nextDelegate(); - m_lvfc->viewAtCurrentIndex(); - } - - m_lvfc->focusNextItem(); -} - -void FocusController::focusPreviousListViewItem() -{ - if (m_lvfc->isFirstFocusItemInListView() || m_lvfc->isReturnNeeded()) { - qDebug() << "===>> [First item in ListView was reached. Going to the PREVIOUS element after ListView]"; - resetListView(); - nextItem(Direction::Backward); - return; - } else if (m_lvfc->isFirstFocusItemInDelegate()) { - m_lvfc->resetFocusChain(); - m_lvfc->previousDelegate(); - m_lvfc->viewAtCurrentIndex(); - } - - m_lvfc->focusPreviousItem(); -} void FocusController::nextKeyTabItem() { @@ -212,70 +120,36 @@ void FocusController::nextKeyRightItem() nextItem(Direction::Forward); } +void FocusController::setFocusItem(QQuickItem* item) +{ + if (m_focusedItem != item) { + m_focusedItem = item; + emit focusedItemChanged(); + qDebug() << "===>> FocusItem is changed to " << item << "!"; + } else { + qDebug() << "===>> FocusItem is is the same: " << item << "!"; + } +} + void FocusController::setFocusOnDefaultItem() { qDebug() << "===>> Setting focus on DEFAULT FOCUS ITEM..."; - m_defaultFocusItem->forceActiveFocus(); -} - -void FocusController::reload(Direction direction) -{ - m_focusChain.clear(); - - QObject* rootObject = (m_rootObjects.empty() - ? m_engine->rootObjects().value(0) - : m_rootObjects.top()); - - if(!rootObject) { - qCritical() << "No ROOT OBJECT found!"; - m_focusedItemIndex = -1; - resetRootObject(); - resetListView(); - setFocusOnDefaultItem(); - return; - } - - qDebug() << "===>> ROOT OBJECTS: " << rootObject; - - m_focusChain.append(getSubChain(rootObject)); - - std::sort(m_focusChain.begin(), m_focusChain.end(), direction == Direction::Forward ? isLess : isMore); - - if (m_focusChain.empty()) { - qWarning() << "Focus chain is empty!"; - m_focusedItemIndex = -1; - resetRootObject(); - resetListView(); - setFocusOnDefaultItem(); - return; - } - - m_focusedItemIndex = m_focusChain.indexOf(m_focusedItem); - - if(m_focusedItemIndex == -1) { - qInfo() << "No focus item in chain."; - resetListView(); - setFocusOnDefaultItem(); - return; - } -} - -void FocusController::resetListView() -{ - if(m_lvfc) { - delete m_lvfc; - m_lvfc = nullptr; - } + setFocusItem(m_defaultFocusItem.get()); } void FocusController::pushRootObject(QObject* object) { + qDebug() << "===>> Calling < pushRootObject >..."; m_rootObjects.push(object); + dropListView(); + // setFocusOnDefaultItem(); qDebug() << "===>> ROOT OBJECT is changed to: " << m_rootObjects.top(); + qDebug() << "===>> ROOT OBJECTS: " << m_rootObjects; } void FocusController::dropRootObject(QObject* object) { + qDebug() << "===>> Calling < dropRootObject >..."; if (m_rootObjects.empty()) { qDebug() << "ROOT OBJECT is already DEFAULT"; @@ -284,6 +158,8 @@ void FocusController::dropRootObject(QObject* object) if (m_rootObjects.top() == object) { m_rootObjects.pop(); + dropListView(); + setFocusOnDefaultItem(); if(m_rootObjects.size()) { qDebug() << "===>> ROOT OBJECT is changed to: " << m_rootObjects.top(); } else { @@ -296,6 +172,153 @@ void FocusController::dropRootObject(QObject* object) void FocusController::resetRootObject() { + qDebug() << "===>> Calling < resetRootObject >..."; m_rootObjects.clear(); qDebug() << "===>> ROOT OBJECT IS RESETED"; } + +void FocusController::reload(Direction direction) +{ + qDebug() << "===>> Calling < reload >..."; + m_focusChain.clear(); + + QObject* rootObject = (m_rootObjects.empty() + ? m_engine->rootObjects().value(0) + : m_rootObjects.top()); + + if(!rootObject) { + qCritical() << "No ROOT OBJECT found!"; + resetRootObject(); + dropListView(); + return; + } + + qDebug() << "===>> ROOT OBJECTS: " << rootObject; + + m_focusChain.append(getSubChain(rootObject)); + + std::sort(m_focusChain.begin(), m_focusChain.end(), direction == Direction::Forward ? isLess : isMore); + + if (m_focusChain.empty()) { + qWarning() << "Focus chain is empty!"; + resetRootObject(); + dropListView(); + return; + } +} + +void FocusController::nextItem(Direction direction) +{ + qDebug() << "===>> Calling < nextItem >..."; + + reload(direction); + + if (m_lvfc) { + direction == Direction::Forward ? focusNextListViewItem() : focusPreviousListViewItem(); + qDebug() << "===>> Handling the [ ListView ]..."; + + return; + } + + if(m_focusChain.empty()) { + qWarning() << "There are no items to navigate"; + setFocusOnDefaultItem(); + return; + } + + auto focusedItemIndex = m_focusChain.indexOf(m_focusedItem); + + if (focusedItemIndex == -1) { + qDebug() << "Current FocusItem is not in chain, switch to first in chain..."; + focusedItemIndex = 0; + } else if (focusedItemIndex == (m_focusChain.size() - 1)) { + qDebug() << "Last focus index. Starting from the beginning..."; + focusedItemIndex = 0; + } else { + qDebug() << "Incrementing focus index..."; + focusedItemIndex++; + } + + const auto focusedItem = qobject_cast(m_focusChain.at(focusedItemIndex)); + + if(focusedItem == nullptr) { + qWarning() << "Failed to get item to focus on. Setting focus on default"; + setFocusOnDefaultItem(); + return; + } + + if(isListView(focusedItem)) { + qDebug() << "===>> Found [ListView]"; + m_lvfc = new ListViewFocusController(focusedItem, this); + m_focusedItem = focusedItem; + if(direction == Direction::Forward) { + m_lvfc->nextDelegate(); + focusNextListViewItem(); + } else { + m_lvfc->previousDelegate(); + focusPreviousListViewItem(); + } + return; + } + + setFocusItem(focusedItem); + + printItems(m_focusChain, focusedItem); + + /////////////////////////////////////////////////////////// + + const auto w = m_defaultFocusItem->window(); + + qDebug() << "===>> CURRENT ACTIVE ITEM: " << w->activeFocusItem(); + qDebug() << "===>> CURRENT FOCUS OBJECT: " << w->focusObject(); + if(m_rootObjects.empty()) { + qDebug() << "===>> ROOT OBJECT IS DEFAULT"; + } else { + qDebug() << "===>> ROOT OBJECT: " << m_rootObjects.top(); + } +} + +void FocusController::focusNextListViewItem() +{ + qDebug() << "===>> Calling < focusNextListViewItem >..."; + + if (m_lvfc->isLastFocusItemInListView() || m_lvfc->isReturnNeeded()) { + qDebug() << "===>> Last item in [ ListView ] was reached. Going to the NEXT element after [ ListView ]"; + dropListView(); + nextItem(Direction::Forward); + return; + } else if (m_lvfc->isLastFocusItemInDelegate()) { + qDebug() << "===>> End of delegate elements was reached. Going to the next delegate"; + m_lvfc->resetFocusChain(); + m_lvfc->nextDelegate(); + } + + m_lvfc->focusNextItem(); +} + +void FocusController::focusPreviousListViewItem() +{ + qDebug() << "===>> Calling < focusPreviousListViewItem >..."; + + if (m_lvfc->isFirstFocusItemInListView() || m_lvfc->isReturnNeeded()) { + qDebug() << "===>> First item in [ ListView ] was reached. Going to the PREVIOUS element after [ ListView ]"; + dropListView(); + nextItem(Direction::Backward); + return; + } else if (m_lvfc->isFirstFocusItemInDelegate()) { + m_lvfc->resetFocusChain(); + m_lvfc->previousDelegate(); + } + + m_lvfc->focusPreviousItem(); +} + +void FocusController::dropListView() +{ + qDebug() << "===>> Calling < dropListView >..."; + + if(m_lvfc) { + delete m_lvfc; + m_lvfc = nullptr; + } +} diff --git a/client/ui/controllers/focusController.h b/client/ui/controllers/focusController.h index 472c328b..54e0a05c 100644 --- a/client/ui/controllers/focusController.h +++ b/client/ui/controllers/focusController.h @@ -29,10 +29,11 @@ public: Q_INVOKABLE void nextKeyDownItem(); Q_INVOKABLE void nextKeyLeftItem(); Q_INVOKABLE void nextKeyRightItem(); + Q_INVOKABLE void setFocusItem(QQuickItem* item); Q_INVOKABLE void setFocusOnDefaultItem(); - Q_INVOKABLE void resetRootObject(); Q_INVOKABLE void pushRootObject(QObject* object); Q_INVOKABLE void dropRootObject(QObject* object); + Q_INVOKABLE void resetRootObject(); private: enum class Direction { @@ -40,20 +41,22 @@ private: Backward, }; + void reload(Direction direction); void nextItem(Direction direction); void focusNextListViewItem(); void focusPreviousListViewItem(); - void reload(Direction direction); - void resetListView(); + void dropListView(); QSharedPointer m_engine; // Pointer to engine to get root object QList m_focusChain; // List of current objects to be focused QQuickItem* m_focusedItem; // Pointer to the active focus item - qsizetype m_focusedItemIndex; // Active focus item's index in focus chain QStack m_rootObjects; QSharedPointer m_defaultFocusItem; ListViewFocusController* m_lvfc; // ListView focus manager + +signals: + void focusedItemChanged(); }; #endif // FOCUSCONTROLLER_H From a92f706524b28c6579270eb485ead2762ee87df5 Mon Sep 17 00:00:00 2001 From: Cyril Anisimov Date: Fri, 1 Nov 2024 08:28:27 +0100 Subject: [PATCH 34/40] fix focus on notification --- client/ui/qml/Controls2/PageType.qml | 2 +- client/ui/qml/Controls2/PopupType.qml | 14 +++++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/client/ui/qml/Controls2/PageType.qml b/client/ui/qml/Controls2/PageType.qml index 724d3b0d..edeb07fe 100644 --- a/client/ui/qml/Controls2/PageType.qml +++ b/client/ui/qml/Controls2/PageType.qml @@ -18,7 +18,7 @@ Item { // Set a timer to set focus after a short delay Timer { id: timer - interval: 500 // Milliseconds + interval: 200 // Milliseconds onTriggered: { FocusController.resetRootObject() FocusController.setFocusOnDefaultItem() diff --git a/client/ui/qml/Controls2/PopupType.qml b/client/ui/qml/Controls2/PopupType.qml index 61221567..5f635593 100644 --- a/client/ui/qml/Controls2/PopupType.qml +++ b/client/ui/qml/Controls2/PopupType.qml @@ -5,6 +5,7 @@ import QtQuick.Layouts import Style 1.0 import "TextTypes" +import "../Config" Popup { id: root @@ -28,7 +29,7 @@ Popup { } onOpened: { - FocusController.pushRootObject(root) + timer.start() } onClosed: { @@ -42,6 +43,17 @@ Popup { radius: 4 } + Timer { + id: timer + interval: 400 // Milliseconds + onTriggered: { + FocusController.pushRootObject(root) + FocusController.setFocusItem(closeButton) + } + repeat: false // Stop the timer after one trigger + running: !GC.isMobile() // Start the timer + } + contentItem: Item { implicitWidth: content.implicitWidth implicitHeight: content.implicitHeight From 45b8235a819a84a06cee6440217c6e6bfa88944f Mon Sep 17 00:00:00 2001 From: Cyril Anisimov Date: Fri, 1 Nov 2024 22:32:57 +0100 Subject: [PATCH 35/40] update config page for scrolling with tab --- .../Pages2/PageSetupWizardConfigSource.qml | 312 +++++++++--------- 1 file changed, 151 insertions(+), 161 deletions(-) diff --git a/client/ui/qml/Pages2/PageSetupWizardConfigSource.qml b/client/ui/qml/Pages2/PageSetupWizardConfigSource.qml index e98b8055..ca0556a1 100644 --- a/client/ui/qml/Pages2/PageSetupWizardConfigSource.qml +++ b/client/ui/qml/Pages2/PageSetupWizardConfigSource.qml @@ -25,20 +25,149 @@ PageType { } } - FlickableType { - id: fl - anchors.top: parent.top - anchors.bottom: parent.bottom - contentHeight: content.height + QtObject { + id: amneziaVpn - ColumnLayout { - id: content + property string title: qsTr("VPN by Amnezia") + property string description: qsTr("Connect to classic paid and free VPN services from Amnezia") + property string imageSource: "qrc:/images/controls/amnezia.svg" + property bool isVisible: true + property var handler: function() { + PageController.showBusyIndicator(true) + var result = InstallController.fillAvailableServices() + PageController.showBusyIndicator(false) + if (result) { + PageController.goToPage(PageEnum.PageSetupWizardApiServicesList) + } + } + } - anchors.top: parent.top - anchors.left: parent.left - anchors.right: parent.right + QtObject { + id: selfHostVpn - spacing: 0 + property string title: qsTr("Self-hosted VPN") + property string description: qsTr("Configure Amnezia VPN on your own server") + property string imageSource: "qrc:/images/controls/server.svg" + property bool isVisible: true + property var handler: function() { + PageController.goToPage(PageEnum.PageSetupWizardCredentials) + } + } + + QtObject { + id: backupRestore + + property string title: qsTr("Restore from backup") + property string description: qsTr("") + property string imageSource: "qrc:/images/controls/archive-restore.svg" + property bool isVisible: true + property var handler: function() { + var filePath = SystemController.getFileName(qsTr("Open backup file"), + qsTr("Backup files (*.backup)")) + if (filePath !== "") { + PageController.showBusyIndicator(true) + SettingsController.restoreAppConfig(filePath) + PageController.showBusyIndicator(false) + } + } + } + + QtObject { + id: fileOpen + + property string title: qsTr("File with connection settings") + property string description: qsTr("") + property string imageSource: "qrc:/images/controls/folder-search-2.svg" + property bool isVisible: true + property var handler: function() { + var nameFilter = !ServersModel.getServersCount() ? "Config or backup files (*.vpn *.ovpn *.conf *.json *.backup)" : + "Config files (*.vpn *.ovpn *.conf *.json)" + var fileName = SystemController.getFileName(qsTr("Open config file"), nameFilter) + if (fileName !== "") { + if (ImportController.extractConfigFromFile(fileName)) { + PageController.goToPage(PageEnum.PageSetupWizardViewConfig) + } + } + } + } + + QtObject { + id: qrScan + + property string title: qsTr("QR code") + property string description: qsTr("") + property string imageSource: "qrc:/images/controls/scan-line.svg" + property bool isVisible: SettingsController.isCameraPresent() + property var handler: function() { + ImportController.startDecodingQr() + if (Qt.platform.os === "ios") { + PageController.goToPage(PageEnum.PageSetupWizardQrReader) + } + } + } + + QtObject { + id: siteLink + + property string title: qsTr("I have nothing") + property string description: qsTr("") + property string imageSource: "qrc:/images/controls/help-circle.svg" + property bool isVisible: true + property var handler: function() { + Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl()) + } + } + + property list variants: [ + amneziaVpn, + selfHostVpn, + backupRestore, + fileOpen, + qrScan, + siteLink + ] + + ListView { + id: listView + + anchors.fill: parent + + property bool isFocusable: true + + Keys.onTabPressed: { + FocusController.nextKeyTabItem() + } + + Keys.onBacktabPressed: { + FocusController.previousKeyTabItem() + } + + Keys.onUpPressed: { + FocusController.nextKeyUpItem() + } + + Keys.onDownPressed: { + FocusController.nextKeyDownItem() + } + + Keys.onLeftPressed: { + FocusController.nextKeyLeftItem() + } + + Keys.onRightPressed: { + FocusController.nextKeyRightItem() + } + + ScrollBar.vertical: ScrollBar { + policy: ScrollBar.AsNeeded + } + + model: variants + + clip: true + + header: ColumnLayout { + width: listView.width HeaderType { id: moreButton @@ -57,15 +186,6 @@ PageType { moreActionsDrawer.openTriggered() } - actionButton.onFocusChanged: { - console.debug("MOVE THIS LOGIC TO CPP!") - if (actionButton.activeFocus) { - if (fl) { - fl.ensureVisible(moreButton) - } - } - } - DrawerType2 { id: moreActionsDrawer @@ -155,8 +275,6 @@ PageType { headerText: qsTr("Insert key") buttonText: qsTr("Insert") - parentFlickable: fl - clickedFunc: function() { textField.text = "" textField.paste() @@ -171,8 +289,6 @@ PageType { Layout.rightMargin: 16 Layout.leftMargin: 16 - parentFlickable: fl - visible: textKey.textFieldText !== "" text: qsTr("Continue") @@ -194,154 +310,28 @@ PageType { color: AmneziaStyle.color.charcoalGray text: qsTr("Other connection options") } + } + + delegate: ColumnLayout { + width: listView.width CardWithIconsType { - id: apiInstalling + id: entry Layout.fillWidth: true Layout.rightMargin: 16 Layout.leftMargin: 16 Layout.bottomMargin: 16 - headerText: qsTr("VPN by Amnezia") - bodyText: qsTr("Connect to classic paid and free VPN services from Amnezia") + visible: isVisible + + headerText: title + bodyText: description rightImageSource: "qrc:/images/controls/chevron-right.svg" - leftImageSource: "qrc:/images/controls/amnezia.svg" + leftImageSource: imageSource - parentFlickable: fl - - onClicked: function() { - PageController.showBusyIndicator(true) - var result = InstallController.fillAvailableServices() - PageController.showBusyIndicator(false) - if (result) { - PageController.goToPage(PageEnum.PageSetupWizardApiServicesList) - } - } - } - - CardWithIconsType { - id: manualInstalling - - Layout.fillWidth: true - Layout.rightMargin: 16 - Layout.leftMargin: 16 - Layout.bottomMargin: 16 - - headerText: qsTr("Self-hosted VPN") - bodyText: qsTr("Configure Amnezia VPN on your own server") - - rightImageSource: "qrc:/images/controls/chevron-right.svg" - leftImageSource: "qrc:/images/controls/server.svg" - - parentFlickable: fl - - onClicked: { - PageController.goToPage(PageEnum.PageSetupWizardCredentials) - } - } - - CardWithIconsType { - id: backupRestore - - Layout.fillWidth: true - Layout.rightMargin: 16 - Layout.leftMargin: 16 - Layout.bottomMargin: 16 - - visible: PageController.isStartPageVisible() - - headerText: qsTr("Restore from backup") - - rightImageSource: "qrc:/images/controls/chevron-right.svg" - leftImageSource: "qrc:/images/controls/archive-restore.svg" - - parentFlickable: fl - - onClicked: { - var filePath = SystemController.getFileName(qsTr("Open backup file"), - qsTr("Backup files (*.backup)")) - if (filePath !== "") { - PageController.showBusyIndicator(true) - SettingsController.restoreAppConfig(filePath) - PageController.showBusyIndicator(false) - } - } - } - - CardWithIconsType { - id: openFile - - Layout.fillWidth: true - Layout.rightMargin: 16 - Layout.leftMargin: 16 - Layout.bottomMargin: 16 - - headerText: qsTr("File with connection settings") - - rightImageSource: "qrc:/images/controls/chevron-right.svg" - leftImageSource: "qrc:/images/controls/folder-search-2.svg" - - parentFlickable: fl - - onClicked: { - var nameFilter = !ServersModel.getServersCount() ? "Config or backup files (*.vpn *.ovpn *.conf *.json *.backup)" : - "Config files (*.vpn *.ovpn *.conf *.json)" - var fileName = SystemController.getFileName(qsTr("Open config file"), nameFilter) - if (fileName !== "") { - if (ImportController.extractConfigFromFile(fileName)) { - PageController.goToPage(PageEnum.PageSetupWizardViewConfig) - } - } - } - } - - CardWithIconsType { - id: scanQr - - Layout.fillWidth: true - Layout.rightMargin: 16 - Layout.leftMargin: 16 - Layout.bottomMargin: 16 - - visible: SettingsController.isCameraPresent() - - headerText: qsTr("QR code") - - rightImageSource: "qrc:/images/controls/chevron-right.svg" - leftImageSource: "qrc:/images/controls/scan-line.svg" - - parentFlickable: fl - - onClicked: { - ImportController.startDecodingQr() - if (Qt.platform.os === "ios") { - PageController.goToPage(PageEnum.PageSetupWizardQrReader) - } - } - } - - CardWithIconsType { - id: siteLink - - Layout.fillWidth: true - Layout.rightMargin: 16 - Layout.leftMargin: 16 - Layout.bottomMargin: 16 - - visible: PageController.isStartPageVisible() - - headerText: qsTr("I have nothing") - - rightImageSource: "qrc:/images/controls/chevron-right.svg" - leftImageSource: "qrc:/images/controls/help-circle.svg" - - parentFlickable: fl - - onClicked: { - Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl()) - } + onClicked: { handler() } } } } From 416421cba7c1123ea4a7ebf1c00968d0a131bf99 Mon Sep 17 00:00:00 2001 From: Cyril Anisimov Date: Fri, 1 Nov 2024 22:33:28 +0100 Subject: [PATCH 36/40] fix crash on return with esc key --- client/ui/controllers/focusController.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ui/controllers/focusController.cpp b/client/ui/controllers/focusController.cpp index b5558f33..f206620d 100644 --- a/client/ui/controllers/focusController.cpp +++ b/client/ui/controllers/focusController.cpp @@ -213,7 +213,7 @@ void FocusController::nextItem(Direction direction) reload(direction); - if (m_lvfc) { + if (m_lvfc && isListView(m_focusedItem)) { direction == Direction::Forward ? focusNextListViewItem() : focusPreviousListViewItem(); qDebug() << "===>> Handling the [ ListView ]..."; From ed6fc27e522fbdacb7cbf6264a80f6128a1963a8 Mon Sep 17 00:00:00 2001 From: Cyril Anisimov Date: Fri, 1 Nov 2024 23:10:01 +0100 Subject: [PATCH 37/40] fix focus navigation in dynamic delegate of list view --- client/ui/controllers/listViewFocusController.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/client/ui/controllers/listViewFocusController.cpp b/client/ui/controllers/listViewFocusController.cpp index 38e7acc0..21326597 100644 --- a/client/ui/controllers/listViewFocusController.cpp +++ b/client/ui/controllers/listViewFocusController.cpp @@ -279,10 +279,8 @@ void ListViewFocusController::focusNextItem() return; } - if (m_focusChain.empty()) { - qDebug() << "Empty focusChain with current delegate: " << currentDelegate() << "Scanning for elements..."; - m_focusChain = getItemsChain(currentDelegate()); - } + m_focusChain = getItemsChain(currentDelegate()); + if (m_focusChain.empty()) { qWarning() << "No elements found in the delegate. Going to next delegate..."; nextDelegate(); From 942805cca20b49657edab05db676a3f19701e6eb Mon Sep 17 00:00:00 2001 From: Cyril Anisimov Date: Sat, 2 Nov 2024 01:44:49 +0100 Subject: [PATCH 38/40] fix focus move on qr code on share page --- .../qml/Components/ShareConnectionDrawer.qml | 90 +++++++++++++++++-- 1 file changed, 81 insertions(+), 9 deletions(-) diff --git a/client/ui/qml/Components/ShareConnectionDrawer.qml b/client/ui/qml/Components/ShareConnectionDrawer.qml index eb746d37..7e8db993 100644 --- a/client/ui/qml/Components/ShareConnectionDrawer.qml +++ b/client/ui/qml/Components/ShareConnectionDrawer.qml @@ -51,20 +51,50 @@ DrawerType2 { headerText: root.headerText } - FlickableType { + ListView { + id: listView + anchors.top: header.bottom anchors.bottom: parent.bottom - contentHeight: content.height + 32 + anchors.left: parent.left + anchors.right: parent.right - ColumnLayout { - id: content + property bool isFocusable: true - anchors.top: parent.top - anchors.left: parent.left - anchors.right: parent.right + Keys.onTabPressed: { + FocusController.nextKeyTabItem() + } - anchors.leftMargin: 16 - anchors.rightMargin: 16 + Keys.onBacktabPressed: { + FocusController.previousKeyTabItem() + } + + Keys.onUpPressed: { + FocusController.nextKeyUpItem() + } + + Keys.onDownPressed: { + FocusController.nextKeyDownItem() + } + + Keys.onLeftPressed: { + FocusController.nextKeyLeftItem() + } + + Keys.onRightPressed: { + FocusController.nextKeyRightItem() + } + + ScrollBar.vertical: ScrollBar { + policy: ScrollBar.AsNeeded + } + + model: 1 + + clip: true + + header: ColumnLayout { + width: listView.width visible: root.contentVisible @@ -72,6 +102,8 @@ DrawerType2 { id: shareButton Layout.fillWidth: true Layout.topMargin: 16 + Layout.leftMargin: 16 + Layout.rightMargin: 16 text: qsTr("Share") leftImageSource: "qrc:/images/controls/share-2.svg" @@ -99,6 +131,8 @@ DrawerType2 { id: copyConfigTextButton Layout.fillWidth: true Layout.topMargin: 8 + Layout.leftMargin: 16 + Layout.rightMargin: 16 defaultColor: AmneziaStyle.color.transparent hoveredColor: AmneziaStyle.color.translucentWhite @@ -118,6 +152,8 @@ DrawerType2 { id: copyNativeConfigStringButton Layout.fillWidth: true Layout.topMargin: 8 + Layout.leftMargin: 16 + Layout.rightMargin: 16 visible: false @@ -139,6 +175,8 @@ DrawerType2 { Layout.fillWidth: true Layout.topMargin: 24 + Layout.leftMargin: 16 + Layout.rightMargin: 16 defaultColor: AmneziaStyle.color.transparent hoveredColor: AmneziaStyle.color.translucentWhite @@ -265,6 +303,10 @@ DrawerType2 { } } } + } + + delegate: ColumnLayout { + width: listView.width Rectangle { id: qrCodeContainer @@ -272,6 +314,8 @@ DrawerType2 { Layout.fillWidth: true Layout.preferredHeight: width Layout.topMargin: 20 + Layout.leftMargin: 16 + Layout.rightMargin: 16 visible: ExportController.qrCodesCount > 0 @@ -283,6 +327,32 @@ DrawerType2 { source: ExportController.qrCodesCount ? ExportController.qrCodes[0] : "" + property bool isFocusable: true + + Keys.onTabPressed: { + FocusController.nextKeyTabItem() + } + + Keys.onBacktabPressed: { + FocusController.previousKeyTabItem() + } + + Keys.onUpPressed: { + FocusController.nextKeyUpItem() + } + + Keys.onDownPressed: { + FocusController.nextKeyDownItem() + } + + Keys.onLeftPressed: { + FocusController.nextKeyLeftItem() + } + + Keys.onRightPressed: { + FocusController.nextKeyRightItem() + } + Timer { property int index: 0 interval: 1000 @@ -309,6 +379,8 @@ DrawerType2 { Layout.fillWidth: true Layout.topMargin: 24 Layout.bottomMargin: 32 + Layout.leftMargin: 16 + Layout.rightMargin: 16 visible: ExportController.qrCodesCount > 0 From d956be901dff2eab21decb1be368646bd4ebd092 Mon Sep 17 00:00:00 2001 From: Cyril Anisimov Date: Sun, 3 Nov 2024 17:48:51 +0100 Subject: [PATCH 39/40] refactor page logging settings for focus navigation --- client/ui/qml/Pages2/PageSettingsLogging.qml | 280 ++++++++----------- 1 file changed, 123 insertions(+), 157 deletions(-) diff --git a/client/ui/qml/Pages2/PageSettingsLogging.qml b/client/ui/qml/Pages2/PageSettingsLogging.qml index 33074dfa..4b2d7d42 100644 --- a/client/ui/qml/Pages2/PageSettingsLogging.qml +++ b/client/ui/qml/Pages2/PageSettingsLogging.qml @@ -23,30 +23,122 @@ PageType { anchors.left: parent.left anchors.right: parent.right anchors.topMargin: 20 + } - onFocusChanged: { - console.debug("MOVE THIS LOGIC TO CPP!") - if (activeFocus) { - if (fl) { - fl.ensureVisible(this) - } + QtObject { + id: clientLogs + + property string title: qsTr("Client logs") + property string description: qsTr("AmneziaVPN logs") + property bool isVisible: true + property var openLogsHandler: function() { + SettingsController.openLogsFolder() + } + property var exportLogsHandler: function() { + var fileName = "" + if (GC.isMobile()) { + fileName = "AmneziaVPN.log" + } else { + fileName = SystemController.getFileName(qsTr("Save"), + qsTr("Logs files (*.log)"), + StandardPaths.standardLocations(StandardPaths.DocumentsLocation) + "/AmneziaVPN", + true, + ".log") + } + if (fileName !== "") { + PageController.showBusyIndicator(true) + SettingsController.exportLogsFile(fileName) + PageController.showBusyIndicator(false) + PageController.showNotificationMessage(qsTr("Logs file saved")) } } } - FlickableType { - id: fl + QtObject { + id: serviceLogs + + property string title: qsTr("Service logs") + property string description: qsTr("AmneziaVPN-service logs") + property bool isVisible: !GC.isMobile() + property var openLogsHandler: function() { + SettingsController.openServiceLogsFolder() + } + property var exportLogsHandler: function() { + var fileName = "" + if (GC.isMobile()) { + fileName = "AmneziaVPN-service.log" + } else { + fileName = SystemController.getFileName(qsTr("Save"), + qsTr("Logs files (*.log)"), + StandardPaths.standardLocations(StandardPaths.DocumentsLocation) + "/AmneziaVPN-service", + true, + ".log") + } + if (fileName !== "") { + PageController.showBusyIndicator(true) + SettingsController.exportServiceLogsFile(fileName) + PageController.showBusyIndicator(false) + PageController.showNotificationMessage(qsTr("Logs file saved")) + } + } + } + + property list logTypes: [ + clientLogs, + serviceLogs + ] + + ListView { + id: listView anchors.top: backButton.bottom anchors.bottom: parent.bottom - contentHeight: content.height + anchors.right: parent.right + anchors.left: parent.left - ColumnLayout { - id: content + property bool isFocusable: true + + Keys.onTabPressed: { + FocusController.nextKeyTabItem() + } + + Keys.onBacktabPressed: { + FocusController.previousKeyTabItem() + } + + Keys.onUpPressed: { + FocusController.nextKeyUpItem() + } + + Keys.onDownPressed: { + FocusController.nextKeyDownItem() + } + + Keys.onLeftPressed: { + FocusController.nextKeyLeftItem() + } + + Keys.onRightPressed: { + FocusController.nextKeyRightItem() + } + + ScrollBar.vertical: ScrollBar { + policy: ScrollBar.AsNeeded + } + + model: logTypes + spacing: 24 + snapMode: ListView.SnapOneItem + + reuseItems: true + + clip: true + + header: ColumnLayout { + id: headerContent + + width: listView.width - anchors.top: parent.top - anchors.left: parent.left - anchors.right: parent.right spacing: 0 HeaderType { @@ -76,14 +168,11 @@ PageType { SettingsController.isLoggingEnabled = checked } } - - parentFlickable: fl } DividerType {} LabelWithButtonType { - // id: labelWithButton2 Layout.fillWidth: true Layout.topMargin: -8 @@ -101,19 +190,25 @@ PageType { 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) } } + } + + delegate: ColumnLayout { + id: delegateContent + + width: listView.width + + spacing: 0 + + visible: isVisible ListItemTitleType { Layout.fillWidth: true @@ -121,7 +216,7 @@ PageType { Layout.leftMargin: 16 Layout.rightMargin: 16 - text: qsTr("Client logs") + text: title } ParagraphTextType { @@ -131,11 +226,11 @@ PageType { Layout.rightMargin: 16 color: AmneziaStyle.color.mutedGray - text: qsTr("AmneziaVPN logs") + + text: description } LabelWithButtonType { - // id: labelWithButton2 Layout.fillWidth: true Layout.topMargin: -8 Layout.bottomMargin: -8 @@ -144,15 +239,12 @@ PageType { leftImageSource: "qrc:/images/controls/folder-open.svg" isSmallLeftImage: true - clickedFunction: function() { - SettingsController.openLogsFolder() - } + clickedFunction: openLogsHandler } DividerType {} LabelWithButtonType { - // id: labelWithButton2 Layout.fillWidth: true Layout.topMargin: -8 Layout.bottomMargin: -8 @@ -161,136 +253,10 @@ PageType { leftImageSource: "qrc:/images/controls/save.svg" isSmallLeftImage: true - onFocusChanged: { - console.debug("MOVE THIS LOGIC TO CPP!") - if (activeFocus) { - if (fl) { - fl.ensureVisible(this) - } - } - } - - clickedFunction: function() { - var fileName = "" - if (GC.isMobile()) { - fileName = "AmneziaVPN.log" - } else { - fileName = SystemController.getFileName(qsTr("Save"), - qsTr("Logs files (*.log)"), - StandardPaths.standardLocations(StandardPaths.DocumentsLocation) + "/AmneziaVPN", - true, - ".log") - } - if (fileName !== "") { - PageController.showBusyIndicator(true) - SettingsController.exportLogsFile(fileName) - PageController.showBusyIndicator(false) - PageController.showNotificationMessage(qsTr("Logs file saved")) - } - } + clickedFunction: exportLogsHandler } DividerType {} - - ListItemTitleType { - visible: !GC.isMobile() - - Layout.fillWidth: true - Layout.topMargin: 32 - Layout.leftMargin: 16 - Layout.rightMargin: 16 - - text: qsTr("Service logs") - } - - ParagraphTextType { - visible: !GC.isMobile() - - Layout.fillWidth: true - Layout.topMargin: 8 - Layout.leftMargin: 16 - Layout.rightMargin: 16 - - color: AmneziaStyle.color.mutedGray - text: qsTr("AmneziaVPN-service logs") - } - - LabelWithButtonType { - // id: labelWithButton2 - - visible: !GC.isMobile() - - Layout.fillWidth: true - Layout.topMargin: -8 - Layout.bottomMargin: -8 - - text: qsTr("Open logs folder") - leftImageSource: "qrc:/images/controls/folder-open.svg" - isSmallLeftImage: true - - onFocusChanged: { - console.debug("MOVE THIS LOGIC TO CPP!") - if (activeFocus) { - if (fl) { - fl.ensureVisible(this) - } - } - } - - clickedFunction: function() { - SettingsController.openServiceLogsFolder() - } - } - - DividerType { - visible: !GC.isMobile() - } - - LabelWithButtonType { - // id: labelWithButton2 - - visible: !GC.isMobile() - - Layout.fillWidth: true - Layout.topMargin: -8 - Layout.bottomMargin: -8 - - text: qsTr("Export logs") - leftImageSource: "qrc:/images/controls/save.svg" - isSmallLeftImage: true - - onFocusChanged: { - console.debug("MOVE THIS LOGIC TO CPP!") - if (activeFocus) { - if (fl) { - fl.ensureVisible(this) - } - } - } - - clickedFunction: function() { - var fileName = "" - if (GC.isMobile()) { - fileName = "AmneziaVPN-service.log" - } else { - fileName = SystemController.getFileName(qsTr("Save"), - qsTr("Logs files (*.log)"), - StandardPaths.standardLocations(StandardPaths.DocumentsLocation) + "/AmneziaVPN-service", - true, - ".log") - } - if (fileName !== "") { - PageController.showBusyIndicator(true) - SettingsController.exportServiceLogsFile(fileName) - PageController.showBusyIndicator(false) - PageController.showNotificationMessage(qsTr("Logs file saved")) - } - } - } - - DividerType { - visible: !GC.isMobile() - } } } } From 0620b4536a88d3b59b270dea445cecef9e12d59e Mon Sep 17 00:00:00 2001 From: Cyril Anisimov Date: Mon, 4 Nov 2024 00:11:01 +0100 Subject: [PATCH 40/40] update popup --- client/ui/qml/Controls2/PopupType.qml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/client/ui/qml/Controls2/PopupType.qml b/client/ui/qml/Controls2/PopupType.qml index 5f635593..d067fc12 100644 --- a/client/ui/qml/Controls2/PopupType.qml +++ b/client/ui/qml/Controls2/PopupType.qml @@ -47,8 +47,10 @@ Popup { id: timer interval: 400 // Milliseconds onTriggered: { - FocusController.pushRootObject(root) - FocusController.setFocusItem(closeButton) + if (!GC.isMobile()) { + FocusController.setFocusItem(closeButton) + FocusController.pushRootObject(root) + } } repeat: false // Stop the timer after one trigger running: !GC.isMobile() // Start the timer