Merge branch 'improve_navigation_cpp' into feature/android-tv

This commit is contained in:
albexk 2024-11-25 12:20:31 +03:00
commit c72d76aec7
88 changed files with 3939 additions and 3369 deletions

View file

@ -404,6 +404,9 @@ void AmneziaApplication::initControllers()
m_pageController.reset(new PageController(m_serversModel, m_settings)); m_pageController.reset(new PageController(m_serversModel, m_settings));
m_engine->rootContext()->setContextProperty("PageController", m_pageController.get()); 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_installController.reset(new InstallController(m_serversModel, m_containersModel, m_protocolsModel, m_clientManagementModel,
m_apiServicesModel, m_settings)); m_apiServicesModel, m_settings));
m_engine->rootContext()->setContextProperty("InstallController", m_installController.get()); m_engine->rootContext()->setContextProperty("InstallController", m_installController.get());

View file

@ -19,6 +19,7 @@
#include "ui/controllers/exportController.h" #include "ui/controllers/exportController.h"
#include "ui/controllers/importController.h" #include "ui/controllers/importController.h"
#include "ui/controllers/installController.h" #include "ui/controllers/installController.h"
#include "ui/controllers/focusController.h"
#include "ui/controllers/pageController.h" #include "ui/controllers/pageController.h"
#include "ui/controllers/settingsController.h" #include "ui/controllers/settingsController.h"
#include "ui/controllers/sitesController.h" #include "ui/controllers/sitesController.h"
@ -124,6 +125,7 @@ private:
#endif #endif
QScopedPointer<ConnectionController> m_connectionController; QScopedPointer<ConnectionController> m_connectionController;
QScopedPointer<FocusController> m_focusController;
QScopedPointer<PageController> m_pageController; QScopedPointer<PageController> m_pageController;
QScopedPointer<InstallController> m_installController; QScopedPointer<InstallController> m_installController;
QScopedPointer<ImportController> m_importController; QScopedPointer<ImportController> m_importController;

View file

@ -1,225 +1,226 @@
<RCC> <RCC>
<qresource prefix="/"> <qresource prefix="/">
<file>fonts/pt-root-ui_vf.ttf</file>
<file>images/amneziaBigLogo.png</file>
<file>images/AmneziaVPN.png</file>
<file>images/controls/alert-circle.svg</file>
<file>images/controls/amnezia.svg</file>
<file>images/controls/app.svg</file>
<file>images/controls/archive-restore.svg</file>
<file>images/controls/arrow-left.svg</file>
<file>images/controls/arrow-right.svg</file>
<file>images/controls/bug.svg</file>
<file>images/controls/check.svg</file>
<file>images/controls/chevron-down.svg</file>
<file>images/controls/chevron-right.svg</file>
<file>images/controls/chevron-up.svg</file>
<file>images/controls/close.svg</file>
<file>images/controls/copy.svg</file>
<file>images/controls/delete.svg</file>
<file>images/controls/download.svg</file>
<file>images/controls/edit-3.svg</file>
<file>images/controls/eye-off.svg</file>
<file>images/controls/eye.svg</file>
<file>images/controls/file-check-2.svg</file>
<file>images/controls/file-cog-2.svg</file>
<file>images/controls/folder-open.svg</file>
<file>images/controls/folder-search-2.svg</file>
<file>images/controls/gauge.svg</file>
<file>images/controls/github.svg</file>
<file>images/controls/help-circle.svg</file>
<file>images/controls/history.svg</file>
<file>images/controls/home.svg</file>
<file>images/controls/info.svg</file>
<file>images/controls/mail.svg</file>
<file>images/controls/map-pin.svg</file>
<file>images/controls/more-vertical.svg</file>
<file>images/controls/plus.svg</file>
<file>images/controls/qr-code.svg</file>
<file>images/controls/radio-button-inner-circle-pressed.png</file>
<file>images/controls/radio-button-inner-circle.png</file>
<file>images/controls/radio-button-pressed.svg</file>
<file>images/controls/radio-button.svg</file>
<file>images/controls/radio.svg</file>
<file>images/controls/refresh-cw.svg</file>
<file>images/controls/save.svg</file>
<file>images/controls/scan-line.svg</file>
<file>images/controls/search.svg</file>
<file>images/controls/server.svg</file>
<file>images/controls/settings-2.svg</file>
<file>images/controls/settings.svg</file>
<file>images/controls/share-2.svg</file>
<file>images/controls/split-tunneling.svg</file>
<file>images/controls/tag.svg</file>
<file>images/controls/telegram.svg</file>
<file>images/controls/text-cursor.svg</file>
<file>images/controls/trash.svg</file>
<file>images/controls/x-circle.svg</file>
<file>images/tray/active.png</file> <file>images/tray/active.png</file>
<file>images/tray/default.png</file> <file>images/tray/default.png</file>
<file>images/tray/error.png</file> <file>images/tray/error.png</file>
<file>images/AmneziaVPN.png</file> <file>server_scripts/awg/configure_container.sh</file>
<file>server_scripts/remove_container.sh</file> <file>server_scripts/awg/Dockerfile</file>
<file>server_scripts/setup_host_firewall.sh</file> <file>server_scripts/awg/run_container.sh</file>
<file>server_scripts/openvpn_cloak/Dockerfile</file> <file>server_scripts/awg/start.sh</file>
<file>server_scripts/awg/template.conf</file>
<file>server_scripts/build_container.sh</file>
<file>server_scripts/check_connection.sh</file>
<file>server_scripts/check_server_is_busy.sh</file>
<file>server_scripts/check_user_in_sudo.sh</file>
<file>server_scripts/dns/configure_container.sh</file>
<file>server_scripts/dns/Dockerfile</file>
<file>server_scripts/dns/run_container.sh</file>
<file>server_scripts/install_docker.sh</file>
<file>server_scripts/ipsec/configure_container.sh</file>
<file>server_scripts/ipsec/Dockerfile</file>
<file>server_scripts/ipsec/mobileconfig.plist</file>
<file>server_scripts/ipsec/run_container.sh</file>
<file>server_scripts/ipsec/start.sh</file>
<file>server_scripts/ipsec/strongswan.profile</file>
<file>server_scripts/openvpn_cloak/configure_container.sh</file> <file>server_scripts/openvpn_cloak/configure_container.sh</file>
<file>server_scripts/openvpn_cloak/Dockerfile</file>
<file>server_scripts/openvpn_cloak/run_container.sh</file>
<file>server_scripts/openvpn_cloak/start.sh</file> <file>server_scripts/openvpn_cloak/start.sh</file>
<file>server_scripts/openvpn_cloak/template.ovpn</file> <file>server_scripts/openvpn_cloak/template.ovpn</file>
<file>server_scripts/install_docker.sh</file>
<file>server_scripts/build_container.sh</file>
<file>server_scripts/prepare_host.sh</file>
<file>server_scripts/check_connection.sh</file>
<file>server_scripts/remove_all_containers.sh</file>
<file>server_scripts/openvpn_cloak/run_container.sh</file>
<file>server_scripts/openvpn/configure_container.sh</file>
<file>server_scripts/openvpn/run_container.sh</file>
<file>server_scripts/openvpn/template.ovpn</file>
<file>server_scripts/openvpn/Dockerfile</file>
<file>server_scripts/openvpn/start.sh</file>
<file>server_scripts/openvpn_shadowsocks/configure_container.sh</file> <file>server_scripts/openvpn_shadowsocks/configure_container.sh</file>
<file>server_scripts/openvpn_shadowsocks/Dockerfile</file> <file>server_scripts/openvpn_shadowsocks/Dockerfile</file>
<file>server_scripts/openvpn_shadowsocks/run_container.sh</file> <file>server_scripts/openvpn_shadowsocks/run_container.sh</file>
<file>server_scripts/openvpn_shadowsocks/start.sh</file> <file>server_scripts/openvpn_shadowsocks/start.sh</file>
<file>server_scripts/openvpn_shadowsocks/template.ovpn</file> <file>server_scripts/openvpn_shadowsocks/template.ovpn</file>
<file>server_scripts/openvpn/configure_container.sh</file>
<file>server_scripts/openvpn/Dockerfile</file>
<file>server_scripts/openvpn/run_container.sh</file>
<file>server_scripts/openvpn/start.sh</file>
<file>server_scripts/openvpn/template.ovpn</file>
<file>server_scripts/prepare_host.sh</file>
<file>server_scripts/remove_all_containers.sh</file>
<file>server_scripts/remove_container.sh</file>
<file>server_scripts/setup_host_firewall.sh</file>
<file>server_scripts/sftp/configure_container.sh</file>
<file>server_scripts/sftp/Dockerfile</file>
<file>server_scripts/sftp/run_container.sh</file>
<file>server_scripts/socks5_proxy/configure_container.sh</file>
<file>server_scripts/socks5_proxy/Dockerfile</file>
<file>server_scripts/socks5_proxy/run_container.sh</file>
<file>server_scripts/socks5_proxy/start.sh</file>
<file>server_scripts/website_tor/configure_container.sh</file>
<file>server_scripts/website_tor/Dockerfile</file>
<file>server_scripts/website_tor/run_container.sh</file>
<file>server_scripts/wireguard/configure_container.sh</file> <file>server_scripts/wireguard/configure_container.sh</file>
<file>server_scripts/wireguard/Dockerfile</file> <file>server_scripts/wireguard/Dockerfile</file>
<file>server_scripts/wireguard/run_container.sh</file> <file>server_scripts/wireguard/run_container.sh</file>
<file>server_scripts/wireguard/start.sh</file> <file>server_scripts/wireguard/start.sh</file>
<file>server_scripts/wireguard/template.conf</file> <file>server_scripts/wireguard/template.conf</file>
<file>server_scripts/website_tor/configure_container.sh</file>
<file>server_scripts/website_tor/run_container.sh</file>
<file>ui/qml/Config/GlobalConfig.qml</file>
<file>ui/qml/Config/qmldir</file>
<file>server_scripts/check_server_is_busy.sh</file>
<file>server_scripts/dns/configure_container.sh</file>
<file>server_scripts/dns/Dockerfile</file>
<file>server_scripts/dns/run_container.sh</file>
<file>server_scripts/sftp/configure_container.sh</file>
<file>server_scripts/sftp/Dockerfile</file>
<file>server_scripts/sftp/run_container.sh</file>
<file>server_scripts/ipsec/configure_container.sh</file>
<file>server_scripts/ipsec/Dockerfile</file>
<file>server_scripts/ipsec/run_container.sh</file>
<file>server_scripts/ipsec/start.sh</file>
<file>server_scripts/ipsec/mobileconfig.plist</file>
<file>server_scripts/ipsec/strongswan.profile</file>
<file>server_scripts/website_tor/Dockerfile</file>
<file>server_scripts/check_user_in_sudo.sh</file>
<file>ui/qml/Controls2/BasicButtonType.qml</file>
<file>ui/qml/Controls2/TextFieldWithHeaderType.qml</file>
<file>ui/qml/Controls2/LabelWithButtonType.qml</file>
<file>images/controls/arrow-right.svg</file>
<file>images/controls/chevron-right.svg</file>
<file>ui/qml/Controls2/ImageButtonType.qml</file>
<file>ui/qml/Controls2/CardType.qml</file>
<file>ui/qml/Controls2/CheckBoxType.qml</file>
<file>images/controls/check.svg</file>
<file>ui/qml/Controls2/DropDownType.qml</file>
<file>ui/qml/Pages2/PageSetupWizardStart.qml</file>
<file>ui/qml/main2.qml</file>
<file>images/amneziaBigLogo.png</file>
<file>ui/qml/Controls2/FlickableType.qml</file>
<file>ui/qml/Pages2/PageSetupWizardCredentials.qml</file>
<file>ui/qml/Controls2/HeaderType.qml</file>
<file>images/controls/arrow-left.svg</file>
<file>ui/qml/Pages2/PageSetupWizardProtocols.qml</file>
<file>ui/qml/Pages2/PageSetupWizardEasy.qml</file>
<file>images/controls/chevron-down.svg</file>
<file>images/controls/chevron-up.svg</file>
<file>ui/qml/Controls2/TextTypes/ParagraphTextType.qml</file>
<file>ui/qml/Controls2/TextTypes/Header2TextType.qml</file>
<file>ui/qml/Controls2/HorizontalRadioButton.qml</file>
<file>ui/qml/Controls2/VerticalRadioButton.qml</file>
<file>ui/qml/Controls2/SwitcherType.qml</file>
<file>ui/qml/Controls2/TabButtonType.qml</file>
<file>ui/qml/Pages2/PageSetupWizardProtocolSettings.qml</file>
<file>ui/qml/Pages2/PageSetupWizardInstalling.qml</file>
<file>ui/qml/Pages2/PageSetupWizardConfigSource.qml</file>
<file>images/controls/folder-open.svg</file>
<file>images/controls/qr-code.svg</file>
<file>images/controls/text-cursor.svg</file>
<file>ui/qml/Pages2/PageSetupWizardTextKey.qml</file>
<file>ui/qml/Pages2/PageStart.qml</file>
<file>ui/qml/Controls2/TabImageButtonType.qml</file>
<file>images/controls/home.svg</file>
<file>images/controls/settings-2.svg</file>
<file>images/controls/share-2.svg</file>
<file>ui/qml/Pages2/PageHome.qml</file>
<file>ui/qml/Pages2/PageSettingsServersList.qml</file>
<file>ui/qml/Pages2/PageShare.qml</file>
<file>ui/qml/Controls2/TextTypes/Header1TextType.qml</file>
<file>ui/qml/Controls2/TextTypes/LabelTextType.qml</file>
<file>ui/qml/Controls2/TextTypes/ButtonTextType.qml</file>
<file>ui/qml/Controls2/Header2Type.qml</file>
<file>images/controls/plus.svg</file>
<file>ui/qml/Components/ConnectButton.qml</file>
<file>images/controls/download.svg</file>
<file>ui/qml/Controls2/ProgressBarType.qml</file>
<file>ui/qml/Components/ConnectionTypeSelectionDrawer.qml</file>
<file>ui/qml/Components/HomeContainersListView.qml</file>
<file>ui/qml/Controls2/TextTypes/CaptionTextType.qml</file>
<file>images/controls/settings.svg</file>
<file>ui/qml/Pages2/PageSettingsServerInfo.qml</file>
<file>ui/qml/Controls2/PageType.qml</file>
<file>ui/qml/Controls2/PopupType.qml</file>
<file>images/controls/edit-3.svg</file>
<file>ui/qml/Pages2/PageSettingsServerData.qml</file>
<file>ui/qml/Components/SettingsContainersListView.qml</file>
<file>ui/qml/Controls2/TextTypes/ListItemTitleType.qml</file>
<file>ui/qml/Controls2/DividerType.qml</file>
<file>ui/qml/Controls2/StackViewType.qml</file>
<file>ui/qml/Pages2/PageSettings.qml</file>
<file>images/controls/amnezia.svg</file>
<file>images/controls/app.svg</file>
<file>images/controls/radio.svg</file>
<file>images/controls/save.svg</file>
<file>images/controls/server.svg</file>
<file>ui/qml/Pages2/PageSettingsServerProtocols.qml</file>
<file>ui/qml/Pages2/PageSettingsServerServices.qml</file>
<file>ui/qml/Pages2/PageSetupWizardViewConfig.qml</file>
<file>images/controls/file-cog-2.svg</file>
<file>ui/qml/Components/QuestionDrawer.qml</file>
<file>ui/qml/Pages2/PageDeinstalling.qml</file>
<file>ui/qml/Controls2/BackButtonType.qml</file>
<file>ui/qml/Pages2/PageSettingsServerProtocol.qml</file>
<file>ui/qml/Components/TransportProtoSelector.qml</file>
<file>ui/qml/Controls2/ListViewWithRadioButtonType.qml</file>
<file>images/controls/radio-button.svg</file>
<file>images/controls/radio-button-inner-circle.png</file>
<file>images/controls/radio-button-pressed.svg</file>
<file>images/controls/radio-button-inner-circle-pressed.png</file>
<file>ui/qml/Components/ShareConnectionDrawer.qml</file>
<file>ui/qml/Pages2/PageSettingsConnection.qml</file>
<file>ui/qml/Pages2/PageSettingsDns.qml</file>
<file>ui/qml/Pages2/PageSettingsApplication.qml</file>
<file>ui/qml/Pages2/PageSettingsBackup.qml</file>
<file>images/controls/delete.svg</file>
<file>ui/qml/Pages2/PageSettingsAbout.qml</file>
<file>images/controls/github.svg</file>
<file>images/controls/mail.svg</file>
<file>images/controls/telegram.svg</file>
<file>ui/qml/Controls2/TextTypes/SmallTextType.qml</file>
<file>ui/qml/Filters/ContainersModelFilters.qml</file>
<file>ui/qml/Components/SelectLanguageDrawer.qml</file>
<file>ui/qml/Controls2/BusyIndicatorType.qml</file>
<file>ui/qml/Pages2/PageProtocolOpenVpnSettings.qml</file>
<file>ui/qml/Pages2/PageProtocolShadowSocksSettings.qml</file>
<file>ui/qml/Pages2/PageProtocolCloakSettings.qml</file>
<file>ui/qml/Pages2/PageProtocolXraySettings.qml</file>
<file>ui/qml/Pages2/PageProtocolRaw.qml</file>
<file>ui/qml/Pages2/PageSettingsLogging.qml</file>
<file>ui/qml/Pages2/PageServiceSftpSettings.qml</file>
<file>images/controls/copy.svg</file>
<file>ui/qml/Pages2/PageServiceTorWebsiteSettings.qml</file>
<file>ui/qml/Pages2/PageSetupWizardQrReader.qml</file>
<file>images/controls/eye.svg</file>
<file>images/controls/eye-off.svg</file>
<file>ui/qml/Pages2/PageSettingsSplitTunneling.qml</file>
<file>ui/qml/Controls2/ContextMenuType.qml</file>
<file>ui/qml/Controls2/TextAreaType.qml</file>
<file>images/controls/trash.svg</file>
<file>images/controls/more-vertical.svg</file>
<file>ui/qml/Controls2/ListViewWithLabelsType.qml</file>
<file>ui/qml/Pages2/PageServiceDnsSettings.qml</file>
<file>ui/qml/Controls2/TopCloseButtonType.qml</file>
<file>images/controls/x-circle.svg</file>
<file>ui/qml/Pages2/PageProtocolAwgSettings.qml</file>
<file>server_scripts/awg/template.conf</file>
<file>server_scripts/awg/start.sh</file>
<file>server_scripts/awg/configure_container.sh</file>
<file>server_scripts/awg/run_container.sh</file>
<file>server_scripts/awg/Dockerfile</file>
<file>ui/qml/Pages2/PageShareFullAccess.qml</file>
<file>images/controls/close.svg</file>
<file>images/controls/search.svg</file>
<file>server_scripts/xray/configure_container.sh</file> <file>server_scripts/xray/configure_container.sh</file>
<file>server_scripts/xray/Dockerfile</file> <file>server_scripts/xray/Dockerfile</file>
<file>server_scripts/xray/run_container.sh</file> <file>server_scripts/xray/run_container.sh</file>
<file>server_scripts/xray/start.sh</file> <file>server_scripts/xray/start.sh</file>
<file>server_scripts/xray/template.json</file> <file>server_scripts/xray/template.json</file>
<file>ui/qml/Pages2/PageProtocolWireGuardSettings.qml</file> <file>ui/qml/Components/ConnectButton.qml</file>
<file>ui/qml/Components/ConnectionTypeSelectionDrawer.qml</file>
<file>ui/qml/Components/HomeContainersListView.qml</file>
<file>ui/qml/Components/HomeSplitTunnelingDrawer.qml</file> <file>ui/qml/Components/HomeSplitTunnelingDrawer.qml</file>
<file>images/controls/split-tunneling.svg</file>
<file>ui/qml/Controls2/DrawerType2.qml</file>
<file>ui/qml/Pages2/PageSettingsAppSplitTunneling.qml</file>
<file>ui/qml/Components/InstalledAppsDrawer.qml</file> <file>ui/qml/Components/InstalledAppsDrawer.qml</file>
<file>images/controls/alert-circle.svg</file> <file>ui/qml/Components/QuestionDrawer.qml</file>
<file>images/controls/file-check-2.svg</file> <file>ui/qml/Components/SelectLanguageDrawer.qml</file>
<file>ui/qml/Components/ServersListView.qml</file>
<file>ui/qml/Components/SettingsContainersListView.qml</file>
<file>ui/qml/Components/ShareConnectionDrawer.qml</file>
<file>ui/qml/Components/TransportProtoSelector.qml</file>
<file>ui/qml/Config/GlobalConfig.qml</file>
<file>ui/qml/Config/qmldir</file>
<file>ui/qml/Controls2/BackButtonType.qml</file>
<file>ui/qml/Controls2/BasicButtonType.qml</file>
<file>ui/qml/Controls2/BusyIndicatorType.qml</file>
<file>ui/qml/Controls2/CardType.qml</file>
<file>ui/qml/Controls2/CardWithIconsType.qml</file>
<file>ui/qml/Controls2/CheckBoxType.qml</file>
<file>ui/qml/Controls2/ContextMenuType.qml</file>
<file>ui/qml/Controls2/DividerType.qml</file>
<file>ui/qml/Controls2/DrawerType2.qml</file>
<file>ui/qml/Controls2/DropDownType.qml</file>
<file>ui/qml/Controls2/FlickableType.qml</file>
<file>ui/qml/Controls2/Header2Type.qml</file>
<file>ui/qml/Controls2/HeaderType.qml</file>
<file>ui/qml/Controls2/HorizontalRadioButton.qml</file>
<file>ui/qml/Controls2/ImageButtonType.qml</file>
<file>ui/qml/Controls2/LabelWithButtonType.qml</file>
<file>ui/qml/Controls2/LabelWithImageType.qml</file>
<file>ui/qml/Controls2/ListViewWithLabelsType.qml</file>
<file>ui/qml/Controls2/ListViewWithRadioButtonType.qml</file>
<file>ui/qml/Controls2/PageType.qml</file>
<file>ui/qml/Controls2/PopupType.qml</file>
<file>ui/qml/Controls2/ProgressBarType.qml</file>
<file>ui/qml/Controls2/StackViewType.qml</file>
<file>ui/qml/Controls2/SwitcherType.qml</file>
<file>ui/qml/Controls2/TabButtonType.qml</file>
<file>ui/qml/Controls2/TabImageButtonType.qml</file>
<file>ui/qml/Controls2/TextAreaType.qml</file>
<file>ui/qml/Controls2/TextAreaWithFooterType.qml</file>
<file>ui/qml/Controls2/TextFieldWithHeaderType.qml</file>
<file>ui/qml/Controls2/TextTypes/ButtonTextType.qml</file>
<file>ui/qml/Controls2/TextTypes/CaptionTextType.qml</file>
<file>ui/qml/Controls2/TextTypes/Header1TextType.qml</file>
<file>ui/qml/Controls2/TextTypes/Header2TextType.qml</file>
<file>ui/qml/Controls2/TextTypes/LabelTextType.qml</file>
<file>ui/qml/Controls2/TextTypes/ListItemTitleType.qml</file>
<file>ui/qml/Controls2/TextTypes/ParagraphTextType.qml</file>
<file>ui/qml/Controls2/TextTypes/SmallTextType.qml</file>
<file>ui/qml/Controls2/TopCloseButtonType.qml</file>
<file>ui/qml/Controls2/VerticalRadioButton.qml</file>
<file>ui/qml/Controls2/WarningType.qml</file> <file>ui/qml/Controls2/WarningType.qml</file>
<file>fonts/pt-root-ui_vf.ttf</file> <file>ui/qml/Filters/ContainersModelFilters.qml</file>
<file>ui/qml/Modules/Style/qmldir</file> <file>ui/qml/main2.qml</file>
<file>ui/qml/Modules/Style/AmneziaStyle.qml</file> <file>ui/qml/Modules/Style/AmneziaStyle.qml</file>
<file>ui/qml/Modules/Style/qmldir</file>
<file>ui/qml/Pages2/PageDeinstalling.qml</file>
<file>ui/qml/Pages2/PageDevMenu.qml</file>
<file>ui/qml/Pages2/PageHome.qml</file>
<file>ui/qml/Pages2/PageProtocolAwgSettings.qml</file>
<file>ui/qml/Pages2/PageProtocolCloakSettings.qml</file>
<file>ui/qml/Pages2/PageProtocolOpenVpnSettings.qml</file>
<file>ui/qml/Pages2/PageProtocolRaw.qml</file>
<file>ui/qml/Pages2/PageProtocolShadowSocksSettings.qml</file>
<file>ui/qml/Pages2/PageProtocolWireGuardSettings.qml</file>
<file>ui/qml/Pages2/PageProtocolXraySettings.qml</file>
<file>ui/qml/Pages2/PageServiceDnsSettings.qml</file>
<file>ui/qml/Pages2/PageServiceSftpSettings.qml</file>
<file>ui/qml/Pages2/PageServiceSocksProxySettings.qml</file> <file>ui/qml/Pages2/PageServiceSocksProxySettings.qml</file>
<file>server_scripts/socks5_proxy/run_container.sh</file> <file>ui/qml/Pages2/PageServiceTorWebsiteSettings.qml</file>
<file>server_scripts/socks5_proxy/Dockerfile</file> <file>ui/qml/Pages2/PageSettings.qml</file>
<file>server_scripts/socks5_proxy/configure_container.sh</file> <file>ui/qml/Pages2/PageSettingsAbout.qml</file>
<file>server_scripts/socks5_proxy/start.sh</file> <file>ui/qml/Pages2/PageSettingsApiLanguageList.qml</file>
<file>ui/qml/Pages2/PageSettingsApiServerInfo.qml</file>
<file>ui/qml/Pages2/PageSettingsApplication.qml</file>
<file>ui/qml/Pages2/PageSettingsAppSplitTunneling.qml</file>
<file>ui/qml/Pages2/PageSettingsBackup.qml</file>
<file>ui/qml/Pages2/PageSettingsConnection.qml</file>
<file>ui/qml/Pages2/PageSettingsDns.qml</file>
<file>ui/qml/Pages2/PageSettingsLogging.qml</file>
<file>ui/qml/Pages2/PageSettingsServerData.qml</file>
<file>ui/qml/Pages2/PageSettingsServerInfo.qml</file>
<file>ui/qml/Pages2/PageSettingsServerProtocol.qml</file>
<file>ui/qml/Pages2/PageSettingsServerProtocols.qml</file>
<file>ui/qml/Pages2/PageSettingsServerServices.qml</file>
<file>ui/qml/Pages2/PageSettingsServersList.qml</file>
<file>ui/qml/Pages2/PageSettingsSplitTunneling.qml</file>
<file>ui/qml/Pages2/PageProtocolAwgClientSettings.qml</file> <file>ui/qml/Pages2/PageProtocolAwgClientSettings.qml</file>
<file>ui/qml/Pages2/PageProtocolWireGuardClientSettings.qml</file> <file>ui/qml/Pages2/PageProtocolWireGuardClientSettings.qml</file>
<file>ui/qml/Pages2/PageSetupWizardApiServicesList.qml</file>
<file>ui/qml/Pages2/PageSetupWizardApiServiceInfo.qml</file> <file>ui/qml/Pages2/PageSetupWizardApiServiceInfo.qml</file>
<file>ui/qml/Controls2/CardWithIconsType.qml</file> <file>ui/qml/Pages2/PageSetupWizardApiServicesList.qml</file>
<file>images/controls/tag.svg</file> <file>ui/qml/Pages2/PageSetupWizardConfigSource.qml</file>
<file>images/controls/history.svg</file> <file>ui/qml/Pages2/PageSetupWizardCredentials.qml</file>
<file>images/controls/gauge.svg</file> <file>ui/qml/Pages2/PageSetupWizardEasy.qml</file>
<file>images/controls/map-pin.svg</file> <file>ui/qml/Pages2/PageSetupWizardInstalling.qml</file>
<file>ui/qml/Controls2/LabelWithImageType.qml</file> <file>ui/qml/Pages2/PageSetupWizardProtocols.qml</file>
<file>images/controls/info.svg</file> <file>ui/qml/Pages2/PageSetupWizardProtocolSettings.qml</file>
<file>ui/qml/Controls2/TextAreaWithFooterType.qml</file> <file>ui/qml/Pages2/PageSetupWizardQrReader.qml</file>
<file>images/controls/scan-line.svg</file> <file>ui/qml/Pages2/PageSetupWizardStart.qml</file>
<file>images/controls/folder-search-2.svg</file> <file>ui/qml/Pages2/PageSetupWizardTextKey.qml</file>
<file>ui/qml/Pages2/PageSettingsApiServerInfo.qml</file> <file>ui/qml/Pages2/PageSetupWizardViewConfig.qml</file>
<file>images/controls/bug.svg</file> <file>ui/qml/Pages2/PageShare.qml</file>
<file>ui/qml/Pages2/PageDevMenu.qml</file> <file>ui/qml/Pages2/PageShareFullAccess.qml</file>
<file>images/controls/refresh-cw.svg</file> <file>ui/qml/Pages2/PageStart.qml</file>
<file>ui/qml/Pages2/PageSettingsApiLanguageList.qml</file>
<file>images/controls/archive-restore.svg</file>
<file>images/controls/help-circle.svg</file>
</qresource> </qresource>
<qresource prefix="/countriesFlags"> <qresource prefix="/countriesFlags">
<file>images/flagKit/ZW.svg</file> <file>images/flagKit/ZW.svg</file>

View file

@ -0,0 +1,324 @@
#include "focusController.h"
#include "listViewFocusController.h"
#include <QQuickWindow>
#include <QQmlApplicationEngine>
bool isListView(QObject* item)
{
return item->inherits("QQuickListView");
}
bool isOnTheScene(QObject* object)
{
QQuickItem* item = qobject_cast<QQuickItem*>(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<QObject*> getSubChain(QObject* object)
{
QList<QObject*> 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}
, m_focusChain{}
, m_focusedItem{nullptr}
, m_rootObjects{}
, m_defaultFocusItem{QSharedPointer<QQuickItem>()}
, m_lvfc{nullptr}
{
QObject::connect(m_engine.get(), &QQmlApplicationEngine::objectCreated, this, [this](QObject *object, const QUrl &url){
QQuickItem* newDefaultFocusItem = object->findChild<QQuickItem*>("defaultFocusItem");
if(newDefaultFocusItem && m_defaultFocusItem != newDefaultFocusItem) {
m_defaultFocusItem.reset(newDefaultFocusItem);
qDebug() << "===>> NEW DEFAULT FOCUS ITEM: " << m_defaultFocusItem;
}
});
QObject::connect(this, &FocusController::focusedItemChanged, this, [this]() {
m_focusedItem->forceActiveFocus(Qt::TabFocusReason);
});
}
void FocusController::nextKeyTabItem()
{
nextItem(Direction::Forward);
}
void FocusController::previousKeyTabItem()
{
nextItem(Direction::Backward);
}
void FocusController::nextKeyUpItem()
{
nextItem(Direction::Backward);
}
void FocusController::nextKeyDownItem()
{
nextItem(Direction::Forward);
}
void FocusController::nextKeyLeftItem()
{
nextItem(Direction::Backward);
}
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...";
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";
return;
}
if (m_rootObjects.top() == object) {
m_rootObjects.pop();
dropListView();
setFocusOnDefaultItem();
if(m_rootObjects.size()) {
qDebug() << "===>> ROOT OBJECT is changed to: " << m_rootObjects.top();
} else {
qDebug() << "===>> ROOT OBJECT is changed to DEFAULT";
}
} else {
qWarning() << "===>> TRY TO DROP WRONG ROOT OBJECT: " << m_rootObjects.top() << " SHOULD BE: " << 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 && isListView(m_focusedItem)) {
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<QQuickItem*>(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;
}
}

View file

@ -0,0 +1,62 @@
#ifndef FOCUSCONTROLLER_H
#define FOCUSCONTROLLER_H
#include <QObject>
#include <QStack>
#include <QSharedPointer>
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
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();
Q_INVOKABLE void setFocusItem(QQuickItem* item);
Q_INVOKABLE void setFocusOnDefaultItem();
Q_INVOKABLE void pushRootObject(QObject* object);
Q_INVOKABLE void dropRootObject(QObject* object);
Q_INVOKABLE void resetRootObject();
private:
enum class Direction {
Forward,
Backward,
};
void reload(Direction direction);
void nextItem(Direction direction);
void focusNextListViewItem();
void focusPreviousListViewItem();
void dropListView();
QSharedPointer<QQmlApplicationEngine> m_engine; // Pointer to engine to get root object
QList<QObject*> m_focusChain; // List of current objects to be focused
QQuickItem* m_focusedItem; // Pointer to the active focus item
QStack<QObject*> m_rootObjects;
QSharedPointer<QQuickItem> m_defaultFocusItem;
ListViewFocusController* m_lvfc; // ListView focus manager
signals:
void focusedItemChanged();
};
#endif // FOCUSCONTROLLER_H

View file

@ -0,0 +1,393 @@
#include "listViewFocusController.h"
#include <QQuickItem>
#include <QQueue>
#include <QPointF>
#include <QRectF>
#include <QQuickWindow>
bool isVisible(QObject* item)
{
const auto res = item->property("visible").toBool();
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<QQuickItem*>(item1));
const auto p2 = getItemCenterPointOnScene(qobject_cast<QQuickItem*>(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 isEnabled(QObject* obj)
{
const auto item = qobject_cast<QQuickItem*>(obj);
return item && item->isEnabled();
}
QList<QObject*> getItemsChain(QObject* object)
{
QList<QObject*> res;
if (!object) {
qDebug() << "The object is NULL";
return res;
}
const auto children = object->children();
for(const auto child : children) {
if (child
&& isFocusable(child)
&& isEnabled(child)
&& isVisible(child)
) {
res.append(child);
} else {
res.append(getItemsChain(child));
}
}
return res;
}
void printItems(const QList<QObject*>& items, QObject* current_item)
{
for(const auto& item : items) {
QQuickItem* i = qobject_cast<QQuickItem*>(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<QQuickItem*>() ? headerItemProperty.value<QQuickItem*>() : nullptr;
QVariant footerItemProperty = m_listView->property("footerItem");
m_footer = footerItemProperty.canConvert<QQuickItem*>() ? footerItemProperty.value<QQuickItem*>() : nullptr;
}
ListViewFocusController::~ListViewFocusController()
{
}
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;
}
}
}
int ListViewFocusController::size() const
{
return m_listView->property("count").toInt();
}
int ListViewFocusController::currentIndex() const
{
return m_delegateIndex;
}
void ListViewFocusController::nextDelegate()
{
const auto sectionName = m_currentSectionString[static_cast<int>(m_currentSection)];
qDebug() << "===>> [nextDelegate... current section: " << sectionName << " ]";
switch(m_currentSection) {
case Section::Default: {
if(hasHeader()) {
m_currentSection = Section::Header;
viewAtCurrentIndex();
break;
}
[[fallthrough]];
}
case Section::Header: {
if (size() > 0) {
m_currentSection = Section::Delegate;
viewAtCurrentIndex();
break;
}
[[fallthrough]];
}
case Section::Delegate:
if (m_delegateIndex < (size() - 1)) {
m_delegateIndex++;
viewAtCurrentIndex();
break;
} else if (hasFooter()) {
m_currentSection = Section::Footer;
viewAtCurrentIndex();
break;
}
[[fallthrough]];
case Section::Footer: {
m_isReturnNeeded = true;
m_currentSection = Section::Default;
viewAtCurrentIndex();
break;
}
default: {
qCritical() << "Current section is invalid!";
break;
}
}
}
void ListViewFocusController::previousDelegate()
{
switch(m_currentSection) {
case Section::Default: {
if(hasFooter()) {
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) {
m_delegateIndex--;
break;
} else if (hasHeader()) {
m_currentSection = Section::Header;
break;
}
[[fallthrough]];
}
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) const
{
QQuickItem* item{nullptr};
QMetaObject::invokeMethod(m_listView, "itemAtIndex",
Q_RETURN_ARG(QQuickItem*, item),
Q_ARG(int, index));
return item;
}
QQuickItem* ListViewFocusController::currentDelegate() const
{
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() const
{
return m_focusedItem;
}
void ListViewFocusController::focusNextItem()
{
if (m_isReturnNeeded) {
qDebug() << "===>> [ RETURN IS NEEDED... ]";
return;
}
m_focusChain = getItemsChain(currentDelegate());
if (m_focusChain.empty()) {
qWarning() << "No elements found in the delegate. Going to next delegate...";
nextDelegate();
focusNextItem();
return;
}
m_focusedItemIndex++;
m_focusedItem = qobject_cast<QQuickItem*>(m_focusChain.at(m_focusedItemIndex));
qDebug() << "==>> [ Focused Item: " << m_focusedItem << " with Index: " << m_focusedItemIndex << " ]";
m_focusedItem->forceActiveFocus(Qt::TabFocusReason);
}
void ListViewFocusController::focusPreviousItem()
{
if (m_isReturnNeeded) {
return;
}
if (m_focusChain.empty()) {
qDebug() << "Empty focusChain with current delegate: " << currentDelegate() << "Scanning for elements...";
m_focusChain = getItemsChain(currentDelegate());
}
if (m_focusChain.empty()) {
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<QQuickItem*>(m_focusChain.at(m_focusedItemIndex));
qDebug() << "==>> [ Focused Item: " << m_focusedItem << " with Index: " << m_focusedItemIndex << " ]";
m_focusedItem->forceActiveFocus(Qt::TabFocusReason);
}
void ListViewFocusController::resetFocusChain()
{
m_focusChain.clear();
m_focusedItem = nullptr;
m_focusedItemIndex = -1;
}
bool ListViewFocusController::isFirstFocusItemInDelegate() const
{
return m_focusedItem && (m_focusedItem == m_focusChain.first());
}
bool ListViewFocusController::isLastFocusItemInDelegate() const
{
return m_focusedItem && (m_focusedItem == m_focusChain.last());
}
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: {
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::isLastFocusItemInListView() const
{
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() const
{
return m_isReturnNeeded;
}

View file

@ -0,0 +1,76 @@
#ifndef LISTVIEWFOCUSCONTROLLER_H
#define LISTVIEWFOCUSCONTROLLER_H
#include <QObject>
#include <QStack>
#include <QSharedPointer>
#include <QQuickItem>
bool isEnabled(QObject* item);
bool isFocusable(QObject* item);
bool isMore(QObject* item1, QObject* item2);
bool isLess(QObject* item1, QObject* item2);
QList<QObject*> getSubChain(QObject* object);
void printItems(const QList<QObject*>& 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 nextDelegate();
void previousDelegate();
void decrementIndex();
void focusNextItem();
void focusPreviousItem();
void resetFocusChain();
bool isFirstFocusItemInListView() const;
bool isFirstFocusItemInDelegate() const;
bool isLastFocusItemInListView() const;
bool isLastFocusItemInDelegate() const;
bool isReturnNeeded() const;
private:
enum class Section {
Default,
Header,
Delegate,
Footer,
};
int size() const;
int currentIndex() const;
void viewAtCurrentIndex() const;
QQuickItem* itemAtIndex(const int index) const;
QQuickItem* currentDelegate() const;
QQuickItem* focusedItem() const;
bool hasHeader() const;
bool hasFooter() const;
QQuickItem* m_listView;
QList<QObject*> 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<QString> m_currentSectionString;
};
#endif // LISTVIEWFOCUSCONTROLLER_H

View file

@ -81,7 +81,7 @@ void PageController::keyPressEvent(Qt::Key key)
case Qt::Key_Escape: { case Qt::Key_Escape: {
if (m_drawerDepth) { if (m_drawerDepth) {
emit closeTopDrawer(); emit closeTopDrawer();
setDrawerDepth(getDrawerDepth() - 1); decrementDrawerDepth();
} else { } else {
emit escapePressed(); emit escapePressed();
} }
@ -142,11 +142,25 @@ void PageController::setDrawerDepth(const int depth)
} }
} }
int PageController::getDrawerDepth() int PageController::getDrawerDepth() const
{ {
return m_drawerDepth; 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) void PageController::onShowErrorMessage(ErrorCode errorCode)
{ {
const auto fullErrorMessage = errorString(errorCode); const auto fullErrorMessage = errorString(errorCode);

View file

@ -100,7 +100,9 @@ public slots:
void closeApplication(); void closeApplication();
void setDrawerDepth(const int depth); void setDrawerDepth(const int depth);
int getDrawerDepth(); int getDrawerDepth() const;
int incrementDrawerDepth();
int decrementDrawerDepth();
private slots: private slots:
void onShowErrorMessage(amnezia::ErrorCode errorCode); void onShowErrorMessage(amnezia::ErrorCode errorCode);
@ -135,9 +137,6 @@ signals:
void escapePressed(); void escapePressed();
void closeTopDrawer(); void closeTopDrawer();
void forceTabBarActiveFocus();
void forceStackActiveFocus();
private: private:
QSharedPointer<ServersModel> m_serversModel; QSharedPointer<ServersModel> m_serversModel;

View file

@ -79,6 +79,12 @@ bool ServersModel::setData(const QModelIndex &index, const QVariant &value, int
return true; 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 QVariant ServersModel::data(const QModelIndex &index, int role) const
{ {
if (!index.isValid() || index.row() < 0 || index.row() >= static_cast<int>(m_servers.size())) { if (!index.isValid() || index.row() < 0 || index.row() >= static_cast<int>(m_servers.size())) {
@ -679,6 +685,18 @@ QVariant ServersModel::getProcessedServerData(const QString roleString)
return {}; 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() bool ServersModel::isDefaultServerDefaultContainerHasSplitTunneling()
{ {
auto server = m_servers.at(m_defaultServerIndex).toObject(); auto server = m_servers.at(m_defaultServerIndex).toObject();

View file

@ -46,6 +46,7 @@ public:
int rowCount(const QModelIndex &parent = QModelIndex()) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) 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 QModelIndex &index, int role = Qt::DisplayRole) const override;
QVariant data(const int index, int role = Qt::DisplayRole) const; QVariant data(const int index, int role = Qt::DisplayRole) const;
@ -115,6 +116,7 @@ public slots:
QVariant getDefaultServerData(const QString roleString); QVariant getDefaultServerData(const QString roleString);
QVariant getProcessedServerData(const QString roleString); QVariant getProcessedServerData(const QString roleString);
bool setProcessedServerData(const QString &roleString, const QVariant &value);
bool isDefaultServerDefaultContainerHasSplitTunneling(); bool isDefaultServerDefaultContainerHasSplitTunneling();

View file

@ -16,6 +16,32 @@ Button {
property string connectedButtonColor: AmneziaStyle.color.goldenApricot property string connectedButtonColor: AmneziaStyle.color.goldenApricot
property bool buttonActiveFocus: activeFocus && (Qt.platform.os !== "android" || SettingsController.isOnTv()) property bool buttonActiveFocus: activeFocus && (Qt.platform.os !== "android" || SettingsController.isOnTv())
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()
}
implicitWidth: 190 implicitWidth: 190
implicitHeight: 190 implicitHeight: 190

View file

@ -14,7 +14,7 @@ DrawerType2 {
width: parent.width width: parent.width
height: parent.height height: parent.height
expandedContent: ColumnLayout { expandedStateContent: ColumnLayout {
id: content id: content
anchors.top: parent.top anchors.top: parent.top
@ -26,14 +26,6 @@ DrawerType2 {
root.expandedHeight = content.implicitHeight + 32 root.expandedHeight = content.implicitHeight + 32
} }
Connections {
target: root
enabled: !GC.isMobile()
function onOpened() {
focusItem.forceActiveFocus()
}
}
Header2Type { Header2Type {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 24 Layout.topMargin: 24
@ -44,11 +36,6 @@ DrawerType2 {
headerText: qsTr("Add new connection") headerText: qsTr("Add new connection")
} }
Item {
id: focusItem
KeyNavigation.tab: ip.rightButton
}
LabelWithButtonType { LabelWithButtonType {
id: ip id: ip
Layout.fillWidth: true Layout.fillWidth: true
@ -59,10 +46,8 @@ DrawerType2 {
clickedFunction: function() { clickedFunction: function() {
PageController.goToPage(PageEnum.PageSetupWizardCredentials) PageController.goToPage(PageEnum.PageSetupWizardCredentials)
root.close() root.closeTriggered()
} }
KeyNavigation.tab: qrCode.rightButton
} }
DividerType {} DividerType {}
@ -76,10 +61,8 @@ DrawerType2 {
clickedFunction: function() { clickedFunction: function() {
PageController.goToPage(PageEnum.PageSetupWizardConfigSource) PageController.goToPage(PageEnum.PageSetupWizardConfigSource)
root.close() root.closeTriggered()
} }
KeyNavigation.tab: focusItem
} }
DividerType {} DividerType {}

View file

@ -17,56 +17,87 @@ ListView {
property var rootWidth property var rootWidth
property var selectedText property var selectedText
property bool a: true
width: rootWidth width: rootWidth
height: menuContent.contentItem.height height: contentItem.height // TODO: It should be fixed size, not content item height
clip: true clip: true
interactive: false // interactive: false
property FlickableType parentFlickable // property FlickableType parentFlickable
property var lastItemTabClicked // property var lastItemTabClicked
property int currentFocusIndex: 0 // property int currentFocusIndex: 0
activeFocusOnTab: true // snapMode: ListView.SnapToItem
onActiveFocusChanged: {
if (activeFocus) { // ScrollBar.vertical: ScrollBar {}
this.currentFocusIndex = 0
this.itemAtIndex(currentFocusIndex).forceActiveFocus() property bool isFocusable: true
}
}
Keys.onTabPressed: { Keys.onTabPressed: {
if (currentFocusIndex < this.count - 1) { FocusController.nextKeyTabItem()
currentFocusIndex += 1
this.itemAtIndex(currentFocusIndex).forceActiveFocus()
} else {
currentFocusIndex = 0
if (lastItemTabClicked && typeof lastItemTabClicked === "function") {
lastItemTabClicked()
}
}
} }
onVisibleChanged: { Keys.onBacktabPressed: {
if (visible) { FocusController.previousKeyTabItem()
currentFocusIndex = 0
focusItem.forceActiveFocus()
}
} }
Item { Keys.onUpPressed: {
id: focusItem FocusController.nextKeyUpItem()
} }
onCurrentFocusIndexChanged: { Keys.onDownPressed: {
if (parentFlickable) { FocusController.nextKeyDownItem()
parentFlickable.ensureVisible(this.itemAtIndex(currentFocusIndex))
} }
Keys.onLeftPressed: {
FocusController.nextKeyLeftItem()
} }
Keys.onRightPressed: {
FocusController.nextKeyRightItem()
}
// 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 { ButtonGroup {
id: containersRadioButtonGroup id: containersRadioButtonGroup
} }
@ -75,12 +106,6 @@ ListView {
implicitWidth: rootWidth implicitWidth: rootWidth
implicitHeight: content.implicitHeight implicitHeight: content.implicitHeight
onActiveFocusChanged: {
if (activeFocus) {
containerRadioButton.forceActiveFocus()
}
}
ColumnLayout { ColumnLayout {
id: content id: content
@ -111,13 +136,13 @@ ListView {
} }
if (checked) { if (checked) {
containersDropDown.close() containersDropDown.closeTriggered() // TODO: containersDropDown is outside this file
ServersModel.setDefaultContainer(ServersModel.defaultIndex, proxyDefaultServerContainersModel.mapToSource(index)) ServersModel.setDefaultContainer(ServersModel.defaultIndex, proxyDefaultServerContainersModel.mapToSource(index))
} else { } else {
ContainersModel.setProcessedContainerIndex(proxyDefaultServerContainersModel.mapToSource(index)) ContainersModel.setProcessedContainerIndex(proxyDefaultServerContainersModel.mapToSource(index))
InstallController.setShouldCreateServer(false) InstallController.setShouldCreateServer(false)
PageController.goToPage(PageEnum.PageSetupWizardProtocolSettings) PageController.goToPage(PageEnum.PageSetupWizardProtocolSettings)
containersDropDown.close() containersDropDown.closeTriggered()
} }
} }

View file

@ -16,7 +16,7 @@ DrawerType2 {
anchors.fill: parent anchors.fill: parent
expandedHeight: parent.height * 0.9 expandedHeight: parent.height * 0.9
expandedContent: ColumnLayout { expandedStateContent: ColumnLayout {
id: content id: content
anchors.top: parent.top anchors.top: parent.top
@ -24,14 +24,6 @@ DrawerType2 {
anchors.right: parent.right anchors.right: parent.right
spacing: 0 spacing: 0
Connections {
target: root
enabled: !GC.isMobile()
function onOpened() {
focusItem.forceActiveFocus()
}
}
Header2Type { Header2Type {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 24 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") 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 { LabelWithButtonType {
id: splitTunnelingSwitch id: splitTunnelingSwitch
Layout.fillWidth: true Layout.fillWidth: true
@ -59,11 +46,9 @@ DrawerType2 {
descriptionText: qsTr("Enabled \nCan't be disabled for current server") descriptionText: qsTr("Enabled \nCan't be disabled for current server")
rightImageSource: "qrc:/images/controls/chevron-right.svg" rightImageSource: "qrc:/images/controls/chevron-right.svg"
KeyNavigation.tab: siteBasedSplitTunnelingSwitch.visible ? siteBasedSplitTunnelingSwitch.rightButton : focusItem
clickedFunction: function() { clickedFunction: function() {
// PageController.goToPage(PageEnum.PageSettingsSplitTunneling) PageController.goToPage(PageEnum.PageSettingsSplitTunneling)
// root.close() root.closeTriggered()
} }
} }
@ -80,13 +65,9 @@ DrawerType2 {
descriptionText: enabled && SitesModel.isTunnelingEnabled ? qsTr("Enabled") : qsTr("Disabled") descriptionText: enabled && SitesModel.isTunnelingEnabled ? qsTr("Enabled") : qsTr("Disabled")
rightImageSource: "qrc:/images/controls/chevron-right.svg" rightImageSource: "qrc:/images/controls/chevron-right.svg"
KeyNavigation.tab: appSplitTunnelingSwitch.visible ?
appSplitTunnelingSwitch.rightButton :
focusItem
clickedFunction: function() { clickedFunction: function() {
PageController.goToPage(PageEnum.PageSettingsSplitTunneling) PageController.goToPage(PageEnum.PageSettingsSplitTunneling)
root.close() root.closeTriggered()
} }
} }
@ -103,11 +84,9 @@ DrawerType2 {
descriptionText: AppSplitTunnelingModel.isTunnelingEnabled ? qsTr("Enabled") : qsTr("Disabled") descriptionText: AppSplitTunnelingModel.isTunnelingEnabled ? qsTr("Enabled") : qsTr("Disabled")
rightImageSource: "qrc:/images/controls/chevron-right.svg" rightImageSource: "qrc:/images/controls/chevron-right.svg"
KeyNavigation.tab: focusItem
clickedFunction: function() { clickedFunction: function() {
PageController.goToPage(PageEnum.PageSettingsAppSplitTunneling) PageController.goToPage(PageEnum.PageSettingsAppSplitTunneling)
root.close() root.closeTriggered()
} }
} }

View file

@ -26,7 +26,7 @@ DrawerType2 {
id: installedAppsModel id: installedAppsModel
} }
expandedContent: Item { expandedStateContent: Item {
id: container id: container
implicitHeight: expandedHeight implicitHeight: expandedHeight
@ -43,7 +43,7 @@ DrawerType2 {
BackButtonType { BackButtonType {
backButtonImage: "qrc:/images/controls/arrow-left.svg" backButtonImage: "qrc:/images/controls/arrow-left.svg"
backButtonFunction: function() { backButtonFunction: function() {
root.close() root.closeTriggered()
} }
} }
@ -69,6 +69,8 @@ DrawerType2 {
clip: true clip: true
interactive: true interactive: true
property bool isFocusable: true
model: SortFilterProxyModel { model: SortFilterProxyModel {
id: proxyInstalledAppsModel id: proxyInstalledAppsModel
sourceModel: installedAppsModel sourceModel: installedAppsModel
@ -155,7 +157,7 @@ DrawerType2 {
PageController.showBusyIndicator(true) PageController.showBusyIndicator(true)
AppSplitTunnelingController.addApps(installedAppsModel.getSelectedAppsInfo()) AppSplitTunnelingController.addApps(installedAppsModel.getSelectedAppsInfo())
PageController.showBusyIndicator(false) PageController.showBusyIndicator(false)
root.close() root.closeTriggered()
} }
} }
} }

View file

@ -20,7 +20,7 @@ DrawerType2 {
property var yesButtonFunction property var yesButtonFunction
property var noButtonFunction property var noButtonFunction
expandedContent: ColumnLayout { expandedStateContent: ColumnLayout {
id: content id: content
anchors.top: parent.top anchors.top: parent.top
@ -33,14 +33,6 @@ DrawerType2 {
root.expandedHeight = content.implicitHeight + 32 root.expandedHeight = content.implicitHeight + 32
} }
Connections {
target: root
enabled: !GC.isMobile()
function onOpened() {
focusItem.forceActiveFocus()
}
}
Header2TextType { Header2TextType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 16 Layout.topMargin: 16
@ -59,11 +51,6 @@ DrawerType2 {
text: descriptionText text: descriptionText
} }
Item {
id: focusItem
KeyNavigation.tab: yesButton
}
BasicButtonType { BasicButtonType {
id: yesButton id: yesButton
Layout.fillWidth: true Layout.fillWidth: true
@ -78,8 +65,6 @@ DrawerType2 {
yesButtonFunction() yesButtonFunction()
} }
} }
KeyNavigation.tab: noButton
} }
BasicButtonType { BasicButtonType {
@ -102,8 +87,6 @@ DrawerType2 {
noButtonFunction() noButtonFunction()
} }
} }
KeyNavigation.tab: focusItem
} }
} }
} }

View file

@ -11,7 +11,7 @@ import "../Config"
DrawerType2 { DrawerType2 {
id: root id: root
expandedContent: Item { expandedStateContent: Item {
id: container id: container
implicitHeight: root.height * 0.9 implicitHeight: root.height * 0.9
@ -20,19 +20,6 @@ DrawerType2 {
root.expandedHeight = container.implicitHeight root.expandedHeight = container.implicitHeight
} }
Connections {
target: root
enabled: !GC.isMobile()
function onOpened() {
focusItem.forceActiveFocus()
}
}
Item {
id: focusItem
KeyNavigation.tab: backButton
}
ColumnLayout { ColumnLayout {
id: backButtonLayout id: backButtonLayout
@ -43,26 +30,19 @@ DrawerType2 {
BackButtonType { BackButtonType {
id: backButton id: backButton
Layout.fillWidth: true
Layout.topMargin: 16
Layout.rightMargin: 16
Layout.leftMargin: 16
backButtonImage: "qrc:/images/controls/arrow-left.svg" backButtonImage: "qrc:/images/controls/arrow-left.svg"
backButtonFunction: function() { root.close() } backButtonFunction: function() { root.closeTriggered() }
KeyNavigation.tab: listView
} }
}
FlickableType {
anchors.top: backButtonLayout.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
contentHeight: content.implicitHeight
ColumnLayout {
id: content
anchors.fill: parent
Header2Type { Header2Type {
id: header id: header
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 16 Layout.topMargin: 16
Layout.rightMargin: 16 Layout.rightMargin: 16
@ -70,15 +50,44 @@ DrawerType2 {
headerText: qsTr("Choose language") headerText: qsTr("Choose language")
} }
}
ListView { ListView {
id: listView id: listView
Layout.fillWidth: true anchors.top: backButtonLayout.bottom
height: listView.contentItem.height anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
clip: true clip: true
interactive: false interactive: 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()
}
model: LanguageModel model: LanguageModel
currentIndex: LanguageModel.currentLanguageIndex currentIndex: LanguageModel.currentLanguageIndex
@ -87,50 +96,10 @@ DrawerType2 {
id: buttonGroup id: buttonGroup
} }
property int currentFocusIndex: 0
activeFocusOnTab: true
onActiveFocusChanged: {
if (activeFocus) {
this.currentFocusIndex = 0
this.itemAtIndex(currentFocusIndex).forceActiveFocus()
}
}
Keys.onTabPressed: {
if (currentFocusIndex < this.count - 1) {
currentFocusIndex += 1
this.itemAtIndex(currentFocusIndex).forceActiveFocus()
} else {
listViewFocusItem.forceActiveFocus()
focusItem.forceActiveFocus()
}
}
Item {
id: listViewFocusItem
Keys.onTabPressed: {
root.forceActiveFocus()
}
}
onVisibleChanged: {
if (visible) {
listViewFocusItem.forceActiveFocus()
focusItem.forceActiveFocus()
}
}
delegate: Item { delegate: Item {
implicitWidth: root.width implicitWidth: root.width
implicitHeight: delegateContent.implicitHeight implicitHeight: delegateContent.implicitHeight
onActiveFocusChanged: {
if (activeFocus) {
radioButton.forceActiveFocus()
}
}
ColumnLayout { ColumnLayout {
id: delegateContent id: delegateContent
@ -144,6 +113,32 @@ DrawerType2 {
hoverEnabled: true 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 { indicator: Rectangle {
width: parent.width - 1 width: parent.width - 1
height: parent.height height: parent.height
@ -195,7 +190,7 @@ DrawerType2 {
onClicked: { onClicked: {
listView.currentIndex = index listView.currentIndex = index
LanguageModel.changeLanguage(languageIndex) LanguageModel.changeLanguage(languageIndex)
root.close() root.closeTriggered()
} }
} }
} }
@ -205,6 +200,4 @@ DrawerType2 {
} }
} }
} }
}
}
} }

View file

@ -0,0 +1,133 @@
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
}
property bool isFocusable: true
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.onEnterPressed: serverRadioButton.clicked()
Keys.onReturnPressed: serverRadioButton.clicked()
}
ImageButtonType {
id: serverInfoButton
objectName: "serverInfoButton"
image: "qrc:/images/controls/settings.svg"
imageColor: AmneziaStyle.color.paleGray
implicitWidth: 56
implicitHeight: 56
z: 1
onClicked: function() {
ServersModel.processedIndex = index
PageController.goToPage(PageEnum.PageSettingsServerInfo)
drawer.closeTriggered()
}
}
}
DividerType {
Layout.fillWidth: true
Layout.leftMargin: 0
Layout.rightMargin: 0
}
}
}
}

View file

@ -22,28 +22,7 @@ ListView {
clip: true clip: true
interactive: false interactive: false
activeFocusOnTab: true property bool isFocusable: false
Keys.onTabPressed: {
if (currentIndex < this.count - 1) {
this.incrementCurrentIndex()
} else {
currentIndex = 0
lastItemTabClickedSignal()
}
}
onCurrentIndexChanged: {
if (visible) {
if (fl.contentHeight > fl.height) {
var item = this.currentItem
if (item.y < fl.height) {
fl.contentY = item.y
} else if (item.y + item.height > fl.contentY + fl.height) {
fl.contentY = item.y + item.height - fl.height
}
}
}
}
onVisibleChanged: { onVisibleChanged: {
if (visible) { if (visible) {
@ -55,12 +34,6 @@ ListView {
implicitWidth: root.width implicitWidth: root.width
implicitHeight: delegateContent.implicitHeight implicitHeight: delegateContent.implicitHeight
onActiveFocusChanged: {
if (activeFocus) {
containerRadioButton.rightButton.forceActiveFocus()
}
}
ColumnLayout { ColumnLayout {
id: delegateContent id: delegateContent

View file

@ -36,17 +36,9 @@ DrawerType2 {
configFileName = "amnezia_config" configFileName = "amnezia_config"
} }
expandedContent: Item { expandedStateContent: Item {
implicitHeight: root.expandedHeight implicitHeight: root.expandedHeight
Connections {
target: root
enabled: !GC.isMobile()
function onOpened() {
header.forceActiveFocus()
}
}
Header2Type { Header2Type {
id: header id: header
anchors.top: parent.top anchors.top: parent.top
@ -57,24 +49,52 @@ DrawerType2 {
anchors.rightMargin: 16 anchors.rightMargin: 16
headerText: root.headerText headerText: root.headerText
KeyNavigation.tab: shareButton
} }
FlickableType { ListView {
id: listView
anchors.top: header.bottom anchors.top: header.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
contentHeight: content.height + 32
ColumnLayout {
id: content
anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.leftMargin: 16 property bool isFocusable: true
anchors.rightMargin: 16
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: 1
clip: true
header: ColumnLayout {
width: listView.width
visible: root.contentVisible visible: root.contentVisible
@ -82,12 +102,12 @@ DrawerType2 {
id: shareButton id: shareButton
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 16 Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("Share") text: qsTr("Share")
leftImageSource: "qrc:/images/controls/share-2.svg" leftImageSource: "qrc:/images/controls/share-2.svg"
KeyNavigation.tab: copyConfigTextButton
clickedFunc: function() { clickedFunc: function() {
var fileName = "" var fileName = ""
if (GC.isMobile()) { if (GC.isMobile()) {
@ -111,6 +131,8 @@ DrawerType2 {
id: copyConfigTextButton id: copyConfigTextButton
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 8 Layout.topMargin: 8
Layout.leftMargin: 16
Layout.rightMargin: 16
defaultColor: AmneziaStyle.color.transparent defaultColor: AmneziaStyle.color.transparent
hoveredColor: AmneziaStyle.color.translucentWhite hoveredColor: AmneziaStyle.color.translucentWhite
@ -124,14 +146,14 @@ DrawerType2 {
Keys.onReturnPressed: { copyConfigTextButton.clicked() } Keys.onReturnPressed: { copyConfigTextButton.clicked() }
Keys.onEnterPressed: { copyConfigTextButton.clicked() } Keys.onEnterPressed: { copyConfigTextButton.clicked() }
KeyNavigation.tab: copyNativeConfigStringButton.visible ? copyNativeConfigStringButton : showSettingsButton
} }
BasicButtonType { BasicButtonType {
id: copyNativeConfigStringButton id: copyNativeConfigStringButton
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 8 Layout.topMargin: 8
Layout.leftMargin: 16
Layout.rightMargin: 16
visible: false visible: false
@ -153,6 +175,8 @@ DrawerType2 {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 24 Layout.topMargin: 24
Layout.leftMargin: 16
Layout.rightMargin: 16
defaultColor: AmneziaStyle.color.transparent defaultColor: AmneziaStyle.color.transparent
hoveredColor: AmneziaStyle.color.translucentWhite hoveredColor: AmneziaStyle.color.translucentWhite
@ -164,10 +188,8 @@ DrawerType2 {
text: qsTr("Show connection settings") text: qsTr("Show connection settings")
clickedFunc: function() { clickedFunc: function() {
configContentDrawer.open() configContentDrawer.openTriggered()
} }
KeyNavigation.tab: header
} }
DrawerType2 { DrawerType2 {
@ -178,30 +200,11 @@ DrawerType2 {
anchors.fill: parent anchors.fill: parent
expandedHeight: parent.height * 0.9 expandedHeight: parent.height * 0.9
onClosed: { expandedStateContent: Item {
if (!GC.isMobile()) {
header.forceActiveFocus()
}
}
expandedContent: Item {
id: configContentContainer id: configContentContainer
implicitHeight: configContentDrawer.expandedHeight implicitHeight: configContentDrawer.expandedHeight
Connections {
target: configContentDrawer
enabled: !GC.isMobile()
function onOpened() {
focusItem.forceActiveFocus()
}
}
Item {
id: focusItem
KeyNavigation.tab: backButton
}
Connections { Connections {
target: copyNativeConfigStringButton target: copyNativeConfigStringButton
function onClicked() { function onClicked() {
@ -231,9 +234,7 @@ DrawerType2 {
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 16 anchors.topMargin: 16
backButtonFunction: function() { configContentDrawer.close() } backButtonFunction: function() { configContentDrawer.closeTriggered() }
KeyNavigation.tab: focusItem
} }
FlickableType { FlickableType {
@ -302,6 +303,10 @@ DrawerType2 {
} }
} }
} }
}
delegate: ColumnLayout {
width: listView.width
Rectangle { Rectangle {
id: qrCodeContainer id: qrCodeContainer
@ -309,6 +314,8 @@ DrawerType2 {
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredHeight: width Layout.preferredHeight: width
Layout.topMargin: 20 Layout.topMargin: 20
Layout.leftMargin: 16
Layout.rightMargin: 16
visible: ExportController.qrCodesCount > 0 visible: ExportController.qrCodesCount > 0
@ -320,6 +327,32 @@ DrawerType2 {
source: ExportController.qrCodesCount ? ExportController.qrCodes[0] : "" 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 { Timer {
property int index: 0 property int index: 0
interval: 1000 interval: 1000
@ -346,6 +379,8 @@ DrawerType2 {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 24 Layout.topMargin: 24
Layout.bottomMargin: 32 Layout.bottomMargin: 32
Layout.leftMargin: 16
Layout.rightMargin: 16
visible: ExportController.qrCodesCount > 0 visible: ExportController.qrCodesCount > 0

View file

@ -39,8 +39,6 @@ Rectangle {
implicitWidth: (rootWidth - 32) / 2 implicitWidth: (rootWidth - 32) / 2
text: "UDP" text: "UDP"
KeyNavigation.tab: tcpButton
onClicked: { onClicked: {
root.currentIndex = 0 root.currentIndex = 0
} }

View file

@ -4,7 +4,7 @@ import Qt5Compat.GraphicalEffects
import Style 1.0 import Style 1.0
Item { FocusScope {
id: root id: root
property string backButtonImage: "qrc:/images/controls/arrow-left.svg" property string backButtonImage: "qrc:/images/controls/arrow-left.svg"
@ -15,12 +15,6 @@ Item {
visible: backButtonImage !== "" visible: backButtonImage !== ""
onActiveFocusChanged: {
if (activeFocus) {
backButton.forceActiveFocus()
}
}
RowLayout { RowLayout {
id: content id: content

View file

@ -35,6 +35,32 @@ Button {
property alias buttonTextLabel: buttonText property alias buttonTextLabel: buttonText
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()
}
implicitHeight: 56 implicitHeight: 56
hoverEnabled: true hoverEnabled: true

View file

@ -22,6 +22,7 @@ RadioButton {
property string pressedBorderColor: AmneziaStyle.color.softGoldenApricot property string pressedBorderColor: AmneziaStyle.color.softGoldenApricot
property string selectedBorderColor: AmneziaStyle.color.goldenApricot property string selectedBorderColor: AmneziaStyle.color.goldenApricot
property string defaultBodredColor: AmneziaStyle.color.transparent property string defaultBodredColor: AmneziaStyle.color.transparent
property string focusBorderColor: AmneziaStyle.color.paleGray
property int borderWidth: 0 property int borderWidth: 0
implicitWidth: content.implicitWidth implicitWidth: content.implicitWidth
@ -29,6 +30,32 @@ RadioButton {
hoverEnabled: true 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 { indicator: Rectangle {
anchors.fill: parent anchors.fill: parent
radius: 16 radius: 16
@ -52,6 +79,8 @@ RadioButton {
return pressedBorderColor return pressedBorderColor
} else if (root.checked) { } else if (root.checked) {
return selectedBorderColor return selectedBorderColor
} else if (root.activeFocus) {
return focusBorderColor
} }
} }
return defaultBodredColor return defaultBodredColor
@ -59,7 +88,7 @@ RadioButton {
border.width: { border.width: {
if (root.enabled) { if (root.enabled) {
if(root.checked) { if(root.checked || root.activeFocus) {
return 1 return 1
} }
return root.pressed ? 1 : 0 return root.pressed ? 1 : 0

View file

@ -25,10 +25,15 @@ Button {
property real textOpacity: 1.0 property real textOpacity: 1.0
property alias focusItem: rightImage
property FlickableType parentFlickable
hoverEnabled: true hoverEnabled: true
background: Rectangle { background: Rectangle {
id: backgroundRect id: backgroundRect
anchors.fill: parent anchors.fill: parent
radius: 16 radius: 16
@ -39,13 +44,31 @@ Button {
} }
} }
function ensureVisible(item) {
if (item.activeFocus) {
if (root.parentFlickable) {
root.parentFlickable.ensureVisible(root)
}
}
}
onFocusChanged: {
ensureVisible(root)
}
focusItem.onFocusChanged: {
root.ensureVisible(focusItem)
}
contentItem: Item { contentItem: Item {
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
implicitHeight: content.implicitHeight implicitHeight: content.implicitHeight
RowLayout { RowLayout {
id: content id: content
anchors.fill: parent anchors.fill: parent
Image { Image {
@ -61,6 +84,7 @@ Button {
} }
ColumnLayout { ColumnLayout {
ListItemTitleType { ListItemTitleType {
text: root.headerText text: root.headerText
visible: text !== "" visible: text !== ""
@ -123,6 +147,7 @@ Button {
Rectangle { Rectangle {
id: rightImageBackground id: rightImageBackground
anchors.fill: parent anchors.fill: parent
radius: 12 radius: 12
color: "transparent" color: "transparent"
@ -131,10 +156,9 @@ Button {
PropertyAnimation { duration: 200 } PropertyAnimation { duration: 200 }
} }
} }
onClicked: { onClicked: {
if (clickedFunction && typeof clickedFunction === "function") { root.clicked()
clickedFunction()
}
} }
} }
} }

View file

@ -9,17 +9,17 @@ import "TextTypes"
Item { Item {
id: root id: root
readonly property string drawerExpanded: "expanded" readonly property string drawerExpandedStateName: "expanded"
readonly property string drawerCollapsed: "collapsed" readonly property string drawerCollapsedStateName: "collapsed"
readonly property bool isOpened: drawerContent.state === root.drawerExpanded || (drawerContent.state === root.drawerCollapsed && dragArea.drag.active === true) // readonly property bool isExpanded: isExpandedStateActive()
readonly property bool isClosed: drawerContent.state === root.drawerCollapsed && dragArea.drag.active === false // readonly property bool isCollapsed: isCollapsedStateActive()
readonly property bool isExpanded: drawerContent.state === root.drawerExpanded readonly property bool isOpened: isExpandedStateActive() || (isCollapsedStateActive && (dragArea.drag.active === true))
readonly property bool isCollapsed: drawerContent.state === root.drawerCollapsed readonly property bool isClosed: isCollapsedStateActive() && (dragArea.drag.active === false)
property Component collapsedContent property Component collapsedStateContent
property Component expandedContent property Component expandedStateContent
property string defaultColor: AmneziaStyle.color.onyxBlack property string defaultColor: AmneziaStyle.color.onyxBlack
property string borderColor: AmneziaStyle.color.slateGray property string borderColor: AmneziaStyle.color.slateGray
@ -29,29 +29,41 @@ Item {
property int depthIndex: 0 property int depthIndex: 0
signal entered signal cursorEntered
signal exited signal cursorExited
signal pressed(bool pressed, bool entered) signal pressed(bool pressed, bool entered)
signal aboutToHide signal aboutToHide
signal aboutToShow signal aboutToShow
signal close signal closeTriggered
signal open signal openTriggered
signal closed signal closed
signal opened signal opened
function isExpandedStateActive() {
return isStateActive(drawerExpandedStateName)
}
function isCollapsedStateActive() {
return isStateActive(drawerCollapsedStateName)
}
function isStateActive(stateName) {
return drawerContent.state === stateName
}
Connections { Connections {
target: PageController target: PageController
function onCloseTopDrawer() { function onCloseTopDrawer() {
if (depthIndex === PageController.getDrawerDepth()) { if (depthIndex === PageController.getDrawerDepth()) {
if (isCollapsed) { if (isCollapsedStateActive()) {
return return
} }
aboutToHide() aboutToHide()
drawerContent.state = root.drawerCollapsed drawerContent.state = root.drawerCollapsedStateName
depthIndex = 0 depthIndex = 0
closed() closed()
} }
@ -61,30 +73,52 @@ Item {
Connections { Connections {
target: root target: root
function onClose() { function onCloseTriggered() {
if (isCollapsed) { if (isCollapsedStateActive()) {
return return
} }
aboutToHide() aboutToHide()
drawerContent.state = root.drawerCollapsed
depthIndex = 0
PageController.setDrawerDepth(PageController.getDrawerDepth() - 1)
closed() closed()
} }
function onOpen() { function onClosed() {
if (isExpanded) { drawerContent.state = root.drawerCollapsedStateName
if (root.isCollapsedStateActive()) {
var initialPageNavigationBarColor = PageController.getInitialPageNavigationBarColor()
if (initialPageNavigationBarColor !== 0xFF1C1D21) {
PageController.updateNavigationBarColor(initialPageNavigationBarColor)
}
}
depthIndex = 0
PageController.decrementDrawerDepth()
FocusController.dropRootObject(root)
}
function onOpenTriggered() {
if (root.isExpandedStateActive()) {
return return
} }
aboutToShow() root.aboutToShow()
drawerContent.state = root.drawerExpanded root.opened()
depthIndex = PageController.getDrawerDepth() + 1 }
PageController.setDrawerDepth(depthIndex)
opened() function onOpened() {
drawerContent.state = root.drawerExpandedStateName
if (isExpandedStateActive()) {
if (PageController.getInitialPageNavigationBarColor() !== 0xFF1C1D21) {
PageController.updateNavigationBarColor(0xFF1C1D21)
}
}
depthIndex = PageController.incrementDrawerDepth()
FocusController.pushRootObject(root)
} }
} }
@ -102,18 +136,17 @@ Item {
MouseArea { MouseArea {
id: emptyArea id: emptyArea
anchors.fill: parent anchors.fill: parent
enabled: root.isExpanded
visible: enabled
onClicked: { onClicked: {
root.close() root.closeTriggered()
} }
} }
MouseArea { MouseArea {
id: dragArea id: dragArea
objectName: "dragArea"
anchors.fill: drawerContentBackground anchors.fill: drawerContentBackground
cursorShape: root.isCollapsed ? Qt.PointingHandCursor : Qt.ArrowCursor
hoverEnabled: true hoverEnabled: true
enabled: drawerContent.implicitHeight > 0 enabled: drawerContent.implicitHeight > 0
@ -125,35 +158,36 @@ Item {
/** If drag area is released at any point other than min or max y, transition to the other state */ /** If drag area is released at any point other than min or max y, transition to the other state */
onReleased: { onReleased: {
if (root.isCollapsed && drawerContent.y < dragArea.drag.maximumY) { if (isCollapsedStateActive() && drawerContent.y < dragArea.drag.maximumY) {
root.open() root.openTriggered()
return return
} }
if (root.isExpanded && drawerContent.y > dragArea.drag.minimumY) { if (isExpandedStateActive() && drawerContent.y > dragArea.drag.minimumY) {
root.close() root.closeTriggered()
return return
} }
} }
onEntered: { onEntered: {
root.entered() root.cursorEntered()
} }
onExited: { onExited: {
root.exited() root.cursorExited()
} }
onPressedChanged: { onPressedChanged: {
root.pressed(pressed, entered) root.pressed(pressed, entered)
} }
onClicked: { onClicked: {
if (root.isCollapsed) { if (isCollapsedStateActive()) {
root.open() root.openTriggered()
} }
} }
} }
Rectangle { Rectangle {
id: drawerContentBackground id: drawerContentBackground
objectName: "drawerContentBackground"
anchors { left: drawerContent.left; right: drawerContent.right; top: drawerContent.top } anchors { left: drawerContent.left; right: drawerContent.right; top: drawerContent.top }
height: root.height height: root.height
@ -174,53 +208,80 @@ Item {
Item { Item {
id: drawerContent id: drawerContent
objectName: "drawerContent"
Drag.active: dragArea.drag.active Drag.active: dragArea.drag.active
anchors.right: root.right anchors.right: root.right
anchors.left: root.left anchors.left: root.left
y: root.height - drawerContent.height
state: root.drawerCollapsed
implicitHeight: root.isCollapsed ? collapsedHeight : expandedHeight state: root.drawerCollapsedStateName
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
}
}
states: [ states: [
State { State {
name: root.drawerCollapsed name: root.drawerCollapsedStateName
PropertyChanges { PropertyChanges {
target: drawerContent target: drawerContent
implicitHeight: collapsedHeight
y: root.height - root.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 { State {
name: root.drawerExpanded name: root.drawerExpandedStateName
PropertyChanges { PropertyChanges {
target: drawerContent target: drawerContent
implicitHeight: expandedHeight
y: dragArea.drag.minimumY 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: [ transitions: [
Transition { Transition {
from: root.drawerCollapsed from: root.drawerCollapsedStateName
to: root.drawerExpanded to: root.drawerExpandedStateName
PropertyAnimation { PropertyAnimation {
target: drawerContent target: drawerContent
properties: "y" properties: "y"
@ -228,8 +289,8 @@ Item {
} }
}, },
Transition { Transition {
from: root.drawerExpanded from: root.drawerExpandedStateName
to: root.drawerCollapsed to: root.drawerCollapsedStateName
PropertyAnimation { PropertyAnimation {
target: drawerContent target: drawerContent
properties: "y" properties: "y"
@ -241,7 +302,7 @@ Item {
Loader { Loader {
id: collapsedLoader id: collapsedLoader
sourceComponent: root.collapsedContent sourceComponent: root.collapsedStateContent
anchors.right: parent.right anchors.right: parent.right
anchors.left: parent.left anchors.left: parent.left
@ -250,8 +311,7 @@ Item {
Loader { Loader {
id: expandedLoader id: expandedLoader
visible: root.isExpanded sourceComponent: root.expandedStateContent
sourceComponent: root.expandedContent
anchors.right: parent.right anchors.right: parent.right
anchors.left: parent.left anchors.left: parent.left

View file

@ -45,33 +45,56 @@ Item {
property Item drawerParent property Item drawerParent
property Component listView property Component listView
signal open signal openTriggered
signal close signal closeTriggered
function popupClosedFunc() { readonly property bool isFocusable: true
if (!GC.isMobile()) {
this.forceActiveFocus() Keys.onTabPressed: {
} FocusController.nextKeyTabItem()
} }
property var parentFlickable Keys.onBacktabPressed: {
onFocusChanged: { FocusController.previousKeyTabItem()
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 implicitWidth: rootButtonContent.implicitWidth
implicitHeight: rootButtonContent.implicitHeight implicitHeight: rootButtonContent.implicitHeight
onOpen: { onOpenTriggered: {
menu.open() menu.openTriggered()
} }
onClose: { onCloseTriggered: {
menu.close() menu.closeTriggered()
}
Keys.onEnterPressed: {
if (menu.isClosed) {
menu.openTriggered()
}
}
Keys.onReturnPressed: {
if (menu.isClosed) {
menu.openTriggered()
}
} }
Rectangle { Rectangle {
@ -173,7 +196,7 @@ Item {
if (rootButtonClickedFunction && typeof rootButtonClickedFunction === "function") { if (rootButtonClickedFunction && typeof rootButtonClickedFunction === "function") {
rootButtonClickedFunction() rootButtonClickedFunction()
} else { } else {
menu.open() menu.openTriggered()
} }
} }
} }
@ -186,27 +209,10 @@ Item {
anchors.fill: parent anchors.fill: parent
expandedHeight: drawerParent.height * drawerHeight expandedHeight: drawerParent.height * drawerHeight
onClosed: { expandedStateContent: Item {
root.popupClosedFunc()
}
expandedContent: Item {
id: container id: container
implicitHeight: menu.expandedHeight implicitHeight: menu.expandedHeight
Connections {
target: menu
enabled: !GC.isMobile()
function onOpened() {
focusItem.forceActiveFocus()
}
}
Item {
id: focusItem
KeyNavigation.tab: backButton
}
ColumnLayout { ColumnLayout {
id: header id: header
@ -218,22 +224,21 @@ Item {
BackButtonType { BackButtonType {
id: backButton id: backButton
backButtonImage: root.headerBackButtonImage backButtonImage: root.headerBackButtonImage
backButtonFunction: function() { menu.close() } backButtonFunction: function() { menu.closeTriggered() }
KeyNavigation.tab: listViewLoader.item onActiveFocusChanged: {
if(backButton.enabled && backButton.activeFocus) {
root.listView.positionViewAtBeginning()
}
}
} }
} }
FlickableType {
id: flickable
anchors.top: header.bottom
anchors.topMargin: 16
contentHeight: col.implicitHeight
Column { Column {
id: col id: col
anchors.top: parent.top anchors.top: header.bottom
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 16
spacing: 16 spacing: 16
@ -251,28 +256,8 @@ Item {
Loader { Loader {
id: listViewLoader id: listViewLoader
sourceComponent: root.listView sourceComponent: root.listView
onLoaded: {
listViewLoader.item.parentFlickable = flickable
listViewLoader.item.lastItemTabClicked = function() {
focusItem.forceActiveFocus()
} }
} }
} }
} }
}
}
}
Keys.onEnterPressed: {
if (menu.isClosed) {
menu.open()
}
}
Keys.onReturnPressed: {
if (menu.isClosed) {
menu.open()
}
}
} }

View file

@ -7,10 +7,11 @@ Flickable {
function ensureVisible(item) { function ensureVisible(item) {
if (item.y < fl.contentY) { 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) { } 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.contentY = item.y + item.height - fl.height + 40 // 40 is a bottom margin
} }
fl.returnToBounds()
} }
clip: true clip: true

View file

@ -19,8 +19,6 @@ Item {
property string descriptionText property string descriptionText
focus: true
implicitWidth: content.implicitWidth implicitWidth: content.implicitWidth
implicitHeight: content.implicitHeight implicitHeight: content.implicitHeight

View file

@ -27,6 +27,32 @@ RadioButton {
implicitWidth: content.implicitWidth implicitWidth: content.implicitWidth
implicitHeight: content.implicitHeight implicitHeight: content.implicitHeight
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 { indicator: Rectangle {
anchors.fill: parent anchors.fill: parent
radius: 16 radius: 16

View file

@ -24,22 +24,39 @@ Button {
property int borderFocusedWidth: 1 property int borderFocusedWidth: 1
hoverEnabled: true hoverEnabled: true
focus: true
focusPolicy: Qt.TabFocus
icon.source: image icon.source: image
icon.color: root.enabled ? imageColor : disableImageColor icon.color: root.enabled ? imageColor : disableImageColor
property Flickable parentFlickable property bool isFocusable: true
onFocusChanged: { Keys.onTabPressed: {
if (root.activeFocus) { FocusController.nextKeyTabItem()
if (root.parentFlickable) {
root.parentFlickable.ensureVisible(this)
} }
Keys.onBacktabPressed: {
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()
Behavior on icon.color { Behavior on icon.color {
PropertyAnimation { duration: 200 } PropertyAnimation { duration: 200 }
} }

View file

@ -41,6 +41,32 @@ Item {
property bool descriptionOnTop: false property bool descriptionOnTop: false
property bool hideDescription: true 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()
}
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 implicitWidth: content.implicitWidth + content.anchors.topMargin + content.anchors.bottomMargin
implicitHeight: content.implicitHeight + content.anchors.leftMargin + content.anchors.rightMargin implicitHeight: content.implicitHeight + content.anchors.leftMargin + content.anchors.rightMargin

View file

@ -26,47 +26,31 @@ ListView {
height: root.contentItem.height height: root.contentItem.height
clip: true clip: true
interactive: false
property FlickableType parentFlickable property bool isFocusable: true
property var lastItemTabClicked
property int currentFocusIndex: 0
activeFocusOnTab: true
onActiveFocusChanged: {
if (activeFocus) {
this.currentFocusIndex = 0
this.itemAtIndex(currentFocusIndex).forceActiveFocus()
}
}
Keys.onTabPressed: { Keys.onTabPressed: {
if (currentFocusIndex < this.count - 1) { FocusController.nextKeyTabItem()
currentFocusIndex += 1
} else {
currentFocusIndex = 0
}
this.itemAtIndex(currentFocusIndex).forceActiveFocus()
} }
Item { Keys.onBacktabPressed: {
id: focusItem FocusController.previousKeyTabItem()
Keys.onTabPressed: {
root.forceActiveFocus()
}
} }
onVisibleChanged: { Keys.onUpPressed: {
if (visible) { FocusController.nextKeyUpItem()
focusItem.forceActiveFocus()
}
} }
onCurrentFocusIndexChanged: { Keys.onDownPressed: {
if (parentFlickable) { FocusController.nextKeyDownItem()
parentFlickable.ensureVisible(this.itemAtIndex(currentFocusIndex))
} }
Keys.onLeftPressed: {
FocusController.nextKeyLeftItem()
}
Keys.onRightPressed: {
FocusController.nextKeyRightItem()
} }
ButtonGroup { ButtonGroup {
@ -75,24 +59,15 @@ ListView {
function triggerCurrentItem() { function triggerCurrentItem() {
var item = root.itemAtIndex(currentIndex) var item = root.itemAtIndex(currentIndex)
var radioButton = item.children[0].children[0] var radioButton = item.children[0]
radioButton.clicked() radioButton.clicked()
} }
delegate: Item { delegate: ColumnLayout {
implicitWidth: rootWidth
implicitHeight: content.implicitHeight
onActiveFocusChanged: {
if (activeFocus) {
radioButton.forceActiveFocus()
}
}
ColumnLayout {
id: content id: content
anchors.fill: parent implicitWidth: rootWidth
// implicitHeight: content.implicitHeight
RadioButton { RadioButton {
id: radioButton id: radioButton
@ -102,6 +77,32 @@ ListView {
hoverEnabled: true 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 { indicator: Rectangle {
width: parent.width - 1 width: parent.width - 1
height: parent.height height: parent.height
@ -165,7 +166,6 @@ ListView {
} }
} }
} }
}
Component.onCompleted: { Component.onCompleted: {
if (root.currentIndex === index) { if (root.currentIndex === index) {

View file

@ -9,51 +9,19 @@ Item {
property StackView stackView: StackView.view property StackView stackView: StackView.view
property var defaultActiveFocusItem: null
onVisibleChanged: { onVisibleChanged: {
if (visible && !GC.isMobile()) { if (visible && !GC.isMobile()) {
timer.start() 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
// anchors.fill: parent
// enabled: true
// onPressed: function(mouse) {
// forceActiveFocus()
// mouse.accepted = false
// }
// }
// Set a timer to set focus after a short delay // Set a timer to set focus after a short delay
Timer { Timer {
id: timer id: timer
interval: 100 // Milliseconds interval: 200 // Milliseconds
onTriggered: { onTriggered: {
if (defaultActiveFocusItem) { FocusController.resetRootObject()
defaultActiveFocusItem.forceActiveFocus() FocusController.setFocusOnDefaultItem()
}
} }
repeat: false // Stop the timer after one trigger repeat: false // Stop the timer after one trigger
running: !GC.isMobile() // Start the timer running: !GC.isMobile() // Start the timer

View file

@ -5,6 +5,7 @@ import QtQuick.Layouts
import Style 1.0 import Style 1.0
import "TextTypes" import "TextTypes"
import "../Config"
Popup { Popup {
id: root id: root
@ -28,11 +29,11 @@ Popup {
} }
onOpened: { onOpened: {
focusItem.forceActiveFocus() timer.start()
} }
onClosed: { onClosed: {
PageController.forceStackActiveFocus() FocusController.dropRootObject(root)
} }
background: Rectangle { background: Rectangle {
@ -42,6 +43,19 @@ Popup {
radius: 4 radius: 4
} }
Timer {
id: timer
interval: 400 // Milliseconds
onTriggered: {
if (!GC.isMobile()) {
FocusController.setFocusItem(closeButton)
FocusController.pushRootObject(root)
}
}
repeat: false // Stop the timer after one trigger
running: !GC.isMobile() // Start the timer
}
contentItem: Item { contentItem: Item {
implicitWidth: content.implicitWidth implicitWidth: content.implicitWidth
implicitHeight: content.implicitHeight implicitHeight: content.implicitHeight
@ -72,11 +86,6 @@ Popup {
} }
} }
Item {
id: focusItem
KeyNavigation.tab: closeButton
}
BasicButtonType { BasicButtonType {
id: closeButton id: closeButton
visible: closeButtonVisible visible: closeButtonVisible
@ -92,7 +101,6 @@ Popup {
borderWidth: 0 borderWidth: 0
text: qsTr("Close") text: qsTr("Close")
KeyNavigation.tab: focusItem
clickedFunc: function() { clickedFunc: function() {
root.close() root.close()

View file

@ -35,10 +35,37 @@ Switch {
property string hoveredIndicatorBackgroundColor: AmneziaStyle.color.translucentWhite property string hoveredIndicatorBackgroundColor: AmneziaStyle.color.translucentWhite
property string defaultIndicatorBackgroundColor: AmneziaStyle.color.transparent property string defaultIndicatorBackgroundColor: AmneziaStyle.color.transparent
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()
}
hoverEnabled: enabled ? true : false hoverEnabled: enabled ? true : false
focusPolicy: Qt.TabFocus focusPolicy: Qt.TabFocus
property FlickableType parentFlickable: null property FlickableType parentFlickable: null
onFocusChanged: { onFocusChanged: {
if (root.activeFocus) { if (root.activeFocus) {
if (root.parentFlickable) { if (root.parentFlickable) {
@ -132,12 +159,26 @@ Switch {
} }
Keys.onEnterPressed: { Keys.onEnterPressed: {
if (!event.isAutoRepeat) {
root.checked = !root.checked root.checked = !root.checked
root.checkedChanged() root.checkedChanged()
} }
event.accepted = true
}
Keys.onReturnPressed: { Keys.onReturnPressed: {
if (!event.isAutoRepeat) {
root.checked = !root.checked root.checked = !root.checked
root.checkedChanged() root.checkedChanged()
} }
event.accepted = true
}
Keys.onSpacePressed: {
if (!event.isAutoRepeat) {
root.checked = !root.checked
root.checkedChanged()
}
event.accepted = true
}
} }

View file

@ -17,10 +17,35 @@ TabButton {
property bool isSelected: false property bool isSelected: false
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()
}
implicitHeight: 48 implicitHeight: 48
hoverEnabled: true hoverEnabled: true
focusPolicy: Qt.TabFocus
background: Rectangle { background: Rectangle {
id: background id: background

View file

@ -14,13 +14,38 @@ TabButton {
property bool isSelected: false property bool isSelected: false
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()
}
property string borderFocusedColor: AmneziaStyle.color.paleGray property string borderFocusedColor: AmneziaStyle.color.paleGray
property int borderFocusedWidth: 1 property int borderFocusedWidth: 1
property var clickedFunc property var clickedFunc
hoverEnabled: true hoverEnabled: true
focusPolicy: Qt.TabFocus
icon.source: image icon.source: image
icon.color: isSelected ? selectedColor : defaultColor icon.color: isSelected ? selectedColor : defaultColor

View file

@ -78,9 +78,6 @@ Rectangle {
placeholderText: root.placeholderText placeholderText: root.placeholderText
text: root.text text: root.text
KeyNavigation.tab: firstButton
onCursorVisibleChanged: { onCursorVisibleChanged: {
if (textArea.cursorVisible) { if (textArea.cursorVisible) {
fl.interactive = true fl.interactive = true

View file

@ -40,6 +40,7 @@ Item {
implicitHeight: content.implicitHeight implicitHeight: content.implicitHeight
property FlickableType parentFlickable property FlickableType parentFlickable
Connections { Connections {
target: textField target: textField
function onFocusChanged() { function onFocusChanged() {
@ -84,7 +85,16 @@ Item {
TextField { TextField {
id: textField id: textField
activeFocusOnTab: false
property bool isFocusable: true
Keys.onTabPressed: {
FocusController.nextKeyTabItem()
}
Keys.onBacktabPressed: {
FocusController.previousKeyTabItem()
}
enabled: root.textFieldEditable enabled: root.textFieldEditable
color: root.enabled ? root.textFieldTextColor : root.textFieldTextDisabledColor color: root.enabled ? root.textFieldTextColor : root.textFieldTextDisabledColor
@ -209,9 +219,9 @@ Item {
clickedFunc() clickedFunc()
} }
if (KeyNavigation.tab) { // if (KeyNavigation.tab) {
KeyNavigation.tab.forceActiveFocus(); // KeyNavigation.tab.forceActiveFocus();
} // }
} }
Keys.onReturnPressed: { Keys.onReturnPressed: {
@ -219,8 +229,8 @@ Item {
clickedFunc() clickedFunc()
} }
if (KeyNavigation.tab) { // if (KeyNavigation.tab) {
KeyNavigation.tab.forceActiveFocus(); // KeyNavigation.tab.forceActiveFocus();
} // }
} }
} }

View file

@ -28,8 +28,34 @@ RadioButton {
property string imageSource property string imageSource
property bool showImage property bool showImage
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()
}
hoverEnabled: true hoverEnabled: true
focusPolicy: Qt.TabFocus // focusPolicy: Qt.TabFocus
indicator: Rectangle { indicator: Rectangle {
id: background id: background

View file

@ -16,13 +16,6 @@ import "../Components"
PageType { PageType {
id: root id: root
defaultActiveFocusItem: focusItem
Item {
id: focusItem
KeyNavigation.tab: backButton
}
ColumnLayout { ColumnLayout {
id: backButtonLayout id: backButtonLayout
@ -34,7 +27,6 @@ PageType {
BackButtonType { BackButtonType {
id: backButton id: backButton
// KeyNavigation.tab: removeButton
} }
} }
@ -61,7 +53,6 @@ PageType {
headerText: "Dev menu" headerText: "Dev menu"
} }
TextFieldWithHeaderType { TextFieldWithHeaderType {
id: passwordTextField id: passwordTextField
@ -86,8 +77,6 @@ PageType {
SettingsController.gatewayEndpoint = textFieldText SettingsController.gatewayEndpoint = textFieldText
} }
} }
// KeyNavigation.tab: saveButton
} }
SwitcherType { SwitcherType {

View file

@ -19,13 +19,13 @@ import "../Components"
PageType { PageType {
id: root id: root
defaultActiveFocusItem: focusItem
Connections { Connections {
objectName: "pageControllerConnections"
target: PageController target: PageController
function onRestorePageHomeState(isContainerInstalled) { function onRestorePageHomeState(isContainerInstalled) {
drawer.open() drawer.openTriggered()
if (isContainerInstalled) { if (isContainerInstalled) {
containersDropDown.rootButtonClickedFunction() containersDropDown.rootButtonClickedFunction()
} }
@ -33,23 +33,22 @@ PageType {
} }
Item { Item {
objectName: "homeColumnItem"
anchors.fill: parent anchors.fill: parent
anchors.bottomMargin: drawer.collapsedHeight anchors.bottomMargin: drawer.collapsedHeight
ColumnLayout { ColumnLayout {
objectName: "homeColumnLayout"
anchors.fill: parent anchors.fill: parent
anchors.topMargin: 34 anchors.topMargin: 34
anchors.bottomMargin: 34 anchors.bottomMargin: 34
Item {
id: focusItem
KeyNavigation.tab: loggingButton.visible ?
loggingButton :
connectButton
}
BasicButtonType { BasicButtonType {
id: loggingButton id: loggingButton
objectName: "loggingButton"
property bool isLoggingEnabled: SettingsController.isLoggingEnabled property bool isLoggingEnabled: SettingsController.isLoggingEnabled
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
@ -69,8 +68,6 @@ PageType {
Keys.onEnterPressed: loggingButton.clicked() Keys.onEnterPressed: loggingButton.clicked()
Keys.onReturnPressed: loggingButton.clicked() Keys.onReturnPressed: loggingButton.clicked()
KeyNavigation.tab: connectButton
onClicked: { onClicked: {
PageController.goToPage(PageEnum.PageSettingsLogging) PageController.goToPage(PageEnum.PageSettingsLogging)
} }
@ -78,13 +75,15 @@ PageType {
ConnectButton { ConnectButton {
id: connectButton id: connectButton
objectName: "connectButton"
Layout.fillHeight: true Layout.fillHeight: true
Layout.alignment: Qt.AlignCenter Layout.alignment: Qt.AlignCenter
KeyNavigation.tab: splitTunnelingButton
} }
BasicButtonType { BasicButtonType {
id: splitTunnelingButton id: splitTunnelingButton
objectName: "splitTunnelingButton"
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
Layout.bottomMargin: 34 Layout.bottomMargin: 34
@ -115,53 +114,44 @@ PageType {
Keys.onEnterPressed: splitTunnelingButton.clicked() Keys.onEnterPressed: splitTunnelingButton.clicked()
Keys.onReturnPressed: splitTunnelingButton.clicked() Keys.onReturnPressed: splitTunnelingButton.clicked()
KeyNavigation.tab: drawer
onClicked: { onClicked: {
homeSplitTunnelingDrawer.open() homeSplitTunnelingDrawer.openTriggered()
} }
HomeSplitTunnelingDrawer { HomeSplitTunnelingDrawer {
id: homeSplitTunnelingDrawer id: homeSplitTunnelingDrawer
objectName: "homeSplitTunnelingDrawer"
parent: root parent: root
onClosed: {
if (!GC.isMobile()) {
focusItem.forceActiveFocus()
} }
} }
} }
} }
}
}
DrawerType2 { DrawerType2 {
id: drawer id: drawer
objectName: "drawerProtocol"
anchors.fill: parent anchors.fill: parent
onClosed: { collapsedStateContent: Item {
if (!GC.isMobile()) { objectName: "ProtocolDrawerCollapsedContent"
focusItem.forceActiveFocus()
}
}
collapsedContent: Item {
implicitHeight: Qt.platform.os !== "ios" ? root.height * 0.9 : screen.height * 0.77 implicitHeight: Qt.platform.os !== "ios" ? root.height * 0.9 : screen.height * 0.77
Component.onCompleted: { Component.onCompleted: {
drawer.expandedHeight = implicitHeight drawer.expandedHeight = implicitHeight
} }
Connections { Connections {
objectName: "drawerConnections"
target: drawer target: drawer
enabled: !GC.isMobile() enabled: !GC.isMobile()
function onActiveFocusChanged() {
if (drawer.activeFocus && !drawer.isOpened) {
collapsedButtonChevron.forceActiveFocus()
}
}
} }
ColumnLayout { ColumnLayout {
id: collapsed id: collapsed
objectName: "collapsedColumnLayout"
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
@ -180,6 +170,8 @@ PageType {
} }
RowLayout { RowLayout {
objectName: "rowLayout"
Layout.topMargin: 14 Layout.topMargin: 14
Layout.leftMargin: 24 Layout.leftMargin: 24
Layout.rightMargin: 24 Layout.rightMargin: 24
@ -188,9 +180,11 @@ PageType {
spacing: 0 spacing: 0
Connections { Connections {
objectName: "drawerConnections"
target: drawer target: drawer
function onEntered() { function onCursorEntered() {
if (drawer.isCollapsed) { if (drawer.isCollapsedStateActive) {
collapsedButtonChevron.backgroundColor = collapsedButtonChevron.hoveredColor collapsedButtonChevron.backgroundColor = collapsedButtonChevron.hoveredColor
collapsedButtonHeader.opacity = 0.8 collapsedButtonHeader.opacity = 0.8
} else { } else {
@ -198,8 +192,8 @@ PageType {
} }
} }
function onExited() { function onCursorExited() {
if (drawer.isCollapsed) { if (drawer.isCollapsedStateActive) {
collapsedButtonChevron.backgroundColor = collapsedButtonChevron.defaultColor collapsedButtonChevron.backgroundColor = collapsedButtonChevron.defaultColor
collapsedButtonHeader.opacity = 1 collapsedButtonHeader.opacity = 1
} else { } else {
@ -208,7 +202,7 @@ PageType {
} }
function onPressed(pressed, entered) { function onPressed(pressed, entered) {
if (drawer.isCollapsed) { if (drawer.isCollapsedStateActive) {
collapsedButtonChevron.backgroundColor = pressed ? collapsedButtonChevron.pressedColor : entered ? collapsedButtonChevron.hoveredColor : collapsedButtonChevron.defaultColor collapsedButtonChevron.backgroundColor = pressed ? collapsedButtonChevron.pressedColor : entered ? collapsedButtonChevron.hoveredColor : collapsedButtonChevron.defaultColor
collapsedButtonHeader.opacity = 0.7 collapsedButtonHeader.opacity = 0.7
} else { } else {
@ -219,6 +213,8 @@ PageType {
Header1TextType { Header1TextType {
id: collapsedButtonHeader id: collapsedButtonHeader
objectName: "collapsedButtonHeader"
Layout.maximumWidth: drawer.width - 48 - 18 - 12 Layout.maximumWidth: drawer.width - 48 - 18 - 12
maximumLineCount: 2 maximumLineCount: 2
@ -227,8 +223,6 @@ PageType {
text: ServersModel.defaultServerName text: ServersModel.defaultServerName
horizontalAlignment: Qt.AlignHCenter horizontalAlignment: Qt.AlignHCenter
KeyNavigation.tab: tabBar
Behavior on opacity { Behavior on opacity {
PropertyAnimation { duration: 200 } PropertyAnimation { duration: 200 }
} }
@ -236,10 +230,11 @@ PageType {
ImageButtonType { ImageButtonType {
id: collapsedButtonChevron id: collapsedButtonChevron
objectName: "collapsedButtonChevron"
Layout.leftMargin: 8 Layout.leftMargin: 8
visible: drawer.isCollapsed visible: drawer.isCollapsedStateActive()
hoverEnabled: false hoverEnabled: false
image: "qrc:/images/controls/chevron-down.svg" image: "qrc:/images/controls/chevron-down.svg"
@ -254,18 +249,17 @@ PageType {
Keys.onEnterPressed: collapsedButtonChevron.clicked() Keys.onEnterPressed: collapsedButtonChevron.clicked()
Keys.onReturnPressed: collapsedButtonChevron.clicked() Keys.onReturnPressed: collapsedButtonChevron.clicked()
Keys.onTabPressed: lastItemTabClicked()
onClicked: { onClicked: {
if (drawer.isCollapsed) { if (drawer.isCollapsedStateActive()) {
drawer.open() drawer.openTriggered()
} }
} }
} }
} }
RowLayout { RowLayout {
objectName: "rowLayoutLabel"
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
Layout.topMargin: 8 Layout.topMargin: 8
Layout.bottomMargin: drawer.isCollapsed ? 44 : ServersModel.isDefaultServerFromApi ? 61 : 16 Layout.bottomMargin: drawer.isCollapsed ? 44 : ServersModel.isDefaultServerFromApi ? 61 : 16
@ -316,6 +310,7 @@ PageType {
ColumnLayout { ColumnLayout {
id: serversMenuHeader id: serversMenuHeader
objectName: "serversMenuHeader"
anchors.top: collapsed.bottom anchors.top: collapsed.bottom
anchors.right: parent.right anchors.right: parent.right
@ -327,13 +322,9 @@ PageType {
visible: !ServersModel.isDefaultServerFromApi visible: !ServersModel.isDefaultServerFromApi
Item {
id: focusItem1
KeyNavigation.tab: containersDropDown
}
DropDownType { DropDownType {
id: containersDropDown id: containersDropDown
objectName: "containersDropDown"
rootButtonImageColor: AmneziaStyle.color.midnightBlack rootButtonImageColor: AmneziaStyle.color.midnightBlack
rootButtonBackgroundColor: AmneziaStyle.color.paleGray rootButtonBackgroundColor: AmneziaStyle.color.paleGray
@ -344,28 +335,29 @@ PageType {
rootButtonTextTopMargin: 8 rootButtonTextTopMargin: 8
rootButtonTextBottomMargin: 8 rootButtonTextBottomMargin: 8
enabled: drawer.isOpened
text: ServersModel.defaultServerDefaultContainerName text: ServersModel.defaultServerDefaultContainerName
textColor: AmneziaStyle.color.midnightBlack textColor: AmneziaStyle.color.midnightBlack
headerText: qsTr("VPN protocol") headerText: qsTr("VPN protocol")
headerBackButtonImage: "qrc:/images/controls/arrow-left.svg" headerBackButtonImage: "qrc:/images/controls/arrow-left.svg"
rootButtonClickedFunction: function() { rootButtonClickedFunction: function() {
containersDropDown.open() containersDropDown.openTriggered()
} }
drawerParent: root drawerParent: root
KeyNavigation.tab: serversMenuContent
listView: HomeContainersListView { listView: HomeContainersListView {
id: containersListView id: containersListView
objectName: "containersListView"
rootWidth: root.width rootWidth: root.width
onVisibleChanged: { height: 500 // TODO: make calculated
if (containersDropDown.visible && !GC.isMobile()) {
focusItem1.forceActiveFocus()
}
}
Connections { Connections {
objectName: "rowLayoutConnections"
target: ServersModel target: ServersModel
function onDefaultServerIndexChanged() { function onDefaultServerIndexChanged() {
@ -407,167 +399,21 @@ PageType {
ButtonGroup { ButtonGroup {
id: serversRadioButtonGroup id: serversRadioButtonGroup
objectName: "serversRadioButtonGroup"
} }
ListView { ServersListView {
id: serversMenuContent id: serversMenuContent
objectName: "serversMenuContent"
anchors.top: serversMenuHeader.bottom isFocusable: false
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 { Connections {
target: drawer target: drawer
enabled: !GC.isMobile()
function onIsCollapsedChanged() {
if (drawer.isCollapsed) {
const item = serversMenuContent.itemAtIndex(serversMenuContent.focusItemIndex)
if (item) { item.serverRadioButtonProperty.focus = false }
}
}
}
Connections { // this item shouldn't be focused when drawer is closed
target: ServersModel function onIsOpenedChanged() {
function onDefaultServerIndexChanged(serverIndex) { serversMenuContent.isFocusable = drawer.isOpened
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
}
} }
} }
} }

View file

@ -16,18 +16,6 @@ import "../Components"
PageType { PageType {
id: root id: root
defaultActiveFocusItem: listview.currentItem.mtuTextField.textField
Item {
id: focusItem
onFocusChanged: {
if (activeFocus) {
fl.ensureVisible(focusItem)
}
}
KeyNavigation.tab: backButton
}
ColumnLayout { ColumnLayout {
id: backButtonLayout id: backButtonLayout
@ -39,31 +27,44 @@ PageType {
BackButtonType { BackButtonType {
id: backButton id: backButton
KeyNavigation.tab: listview.currentItem.mtuTextField.textField
} }
} }
FlickableType {
id: fl
anchors.top: backButtonLayout.bottom
anchors.bottom: parent.bottom
contentHeight: content.implicitHeight + saveButton.implicitHeight + saveButton.anchors.bottomMargin + saveButton.anchors.topMargin
Column {
id: content
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
ListView { ListView {
id: listview id: listview
anchors.top: backButtonLayout.bottom
anchors.bottom: saveButton.top
width: parent.width width: parent.width
height: listview.contentItem.height
clip: true clip: true
interactive: false
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: AwgConfigModel model: AwgConfigModel
@ -122,7 +123,6 @@ PageType {
headerText: "Jc - Junk packet count" headerText: "Jc - Junk packet count"
textFieldText: clientJunkPacketCount textFieldText: clientJunkPacketCount
textField.validator: IntValidator { bottom: 0 } textField.validator: IntValidator { bottom: 0 }
parentFlickable: fl
textField.onEditingFinished: { textField.onEditingFinished: {
if (textFieldText !== clientJunkPacketCount) { if (textFieldText !== clientJunkPacketCount) {
@ -143,7 +143,6 @@ PageType {
headerText: "Jmin - Junk packet minimum size" headerText: "Jmin - Junk packet minimum size"
textFieldText: clientJunkPacketMinSize textFieldText: clientJunkPacketMinSize
textField.validator: IntValidator { bottom: 0 } textField.validator: IntValidator { bottom: 0 }
parentFlickable: fl
textField.onEditingFinished: { textField.onEditingFinished: {
if (textFieldText !== clientJunkPacketMinSize) { if (textFieldText !== clientJunkPacketMinSize) {
@ -164,7 +163,6 @@ PageType {
headerText: "Jmax - Junk packet maximum size" headerText: "Jmax - Junk packet maximum size"
textFieldText: clientJunkPacketMaxSize textFieldText: clientJunkPacketMaxSize
textField.validator: IntValidator { bottom: 0 } textField.validator: IntValidator { bottom: 0 }
parentFlickable: fl
textField.onEditingFinished: { textField.onEditingFinished: {
if (textFieldText !== clientJunkPacketMaxSize) { if (textFieldText !== clientJunkPacketMaxSize) {
@ -174,7 +172,6 @@ PageType {
checkEmptyText: true checkEmptyText: true
Keys.onTabPressed: saveButton.forceActiveFocus()
} }
Header2TextType { Header2TextType {
@ -264,8 +261,6 @@ PageType {
} }
} }
} }
}
}
BasicButtonType { BasicButtonType {
id: saveButton id: saveButton
@ -283,7 +278,11 @@ PageType {
text: qsTr("Save") text: qsTr("Save")
Keys.onTabPressed: lastItemTabClicked(focusItem) onActiveFocusChanged: {
if(activeFocus) {
listview.positionViewAtEnd()
}
}
clickedFunc: function() { clickedFunc: function() {
forceActiveFocus() forceActiveFocus()

View file

@ -2,6 +2,8 @@ import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import QtCore
import SortFilterProxyModel 0.2 import SortFilterProxyModel 0.2
import PageEnum 1.0 import PageEnum 1.0
@ -17,18 +19,6 @@ import "../Components"
PageType { PageType {
id: root id: root
defaultActiveFocusItem: listview.currentItem.portTextField.textField
Item {
id: focusItem
onFocusChanged: {
if (activeFocus) {
fl.ensureVisible(focusItem)
}
}
KeyNavigation.tab: backButton
}
ColumnLayout { ColumnLayout {
id: backButtonLayout id: backButtonLayout
@ -40,31 +30,44 @@ PageType {
BackButtonType { BackButtonType {
id: backButton id: backButton
KeyNavigation.tab: listview.currentItem.portTextField.textField
} }
} }
FlickableType {
id: fl
anchors.top: backButtonLayout.bottom
anchors.bottom: parent.bottom
contentHeight: content.implicitHeight
Column {
id: content
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
ListView { ListView {
id: listview id: listview
anchors.top: backButtonLayout.bottom
anchors.bottom: parent.bottom
width: parent.width width: parent.width
height: listview.contentItem.height
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()
}
clip: true clip: true
interactive: false
model: AwgConfigModel model: AwgConfigModel
@ -105,7 +108,6 @@ PageType {
textFieldText: port textFieldText: port
textField.maximumLength: 5 textField.maximumLength: 5
textField.validator: IntValidator { bottom: 1; top: 65535 } textField.validator: IntValidator { bottom: 1; top: 65535 }
parentFlickable: fl
textField.onEditingFinished: { textField.onEditingFinished: {
if (textFieldText !== port) { if (textFieldText !== port) {
@ -114,8 +116,26 @@ PageType {
} }
checkEmptyText: true 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 { TextFieldWithHeaderType {
@ -126,7 +146,6 @@ PageType {
headerText: qsTr("Jc - Junk packet count") headerText: qsTr("Jc - Junk packet count")
textFieldText: serverJunkPacketCount textFieldText: serverJunkPacketCount
textField.validator: IntValidator { bottom: 0 } textField.validator: IntValidator { bottom: 0 }
parentFlickable: fl
textField.onEditingFinished: { textField.onEditingFinished: {
if (textFieldText === "") { if (textFieldText === "") {
@ -139,8 +158,6 @@ PageType {
} }
checkEmptyText: true checkEmptyText: true
KeyNavigation.tab: junkPacketMinSizeTextField.textField
} }
TextFieldWithHeaderType { TextFieldWithHeaderType {
@ -151,7 +168,6 @@ PageType {
headerText: qsTr("Jmin - Junk packet minimum size") headerText: qsTr("Jmin - Junk packet minimum size")
textFieldText: serverJunkPacketMinSize textFieldText: serverJunkPacketMinSize
textField.validator: IntValidator { bottom: 0 } textField.validator: IntValidator { bottom: 0 }
parentFlickable: fl
textField.onEditingFinished: { textField.onEditingFinished: {
if (textFieldText !== serverJunkPacketMinSize) { if (textFieldText !== serverJunkPacketMinSize) {
@ -160,8 +176,6 @@ PageType {
} }
checkEmptyText: true checkEmptyText: true
KeyNavigation.tab: junkPacketMaxSizeTextField.textField
} }
TextFieldWithHeaderType { TextFieldWithHeaderType {
@ -172,7 +186,6 @@ PageType {
headerText: qsTr("Jmax - Junk packet maximum size") headerText: qsTr("Jmax - Junk packet maximum size")
textFieldText: serverJunkPacketMaxSize textFieldText: serverJunkPacketMaxSize
textField.validator: IntValidator { bottom: 0 } textField.validator: IntValidator { bottom: 0 }
parentFlickable: fl
textField.onEditingFinished: { textField.onEditingFinished: {
if (textFieldText !== serverJunkPacketMaxSize) { if (textFieldText !== serverJunkPacketMaxSize) {
@ -181,8 +194,6 @@ PageType {
} }
checkEmptyText: true checkEmptyText: true
KeyNavigation.tab: initPacketJunkSizeTextField.textField
} }
TextFieldWithHeaderType { TextFieldWithHeaderType {
@ -193,7 +204,6 @@ PageType {
headerText: qsTr("S1 - Init packet junk size") headerText: qsTr("S1 - Init packet junk size")
textFieldText: serverInitPacketJunkSize textFieldText: serverInitPacketJunkSize
textField.validator: IntValidator { bottom: 0 } textField.validator: IntValidator { bottom: 0 }
parentFlickable: fl
textField.onEditingFinished: { textField.onEditingFinished: {
if (textFieldText !== serverInitPacketJunkSize) { if (textFieldText !== serverInitPacketJunkSize) {
@ -203,7 +213,11 @@ PageType {
checkEmptyText: true checkEmptyText: true
KeyNavigation.tab: responsePacketJunkSizeTextField.textField onActiveFocusChanged: {
if(activeFocus) {
listview.positionViewAtEnd()
}
}
} }
TextFieldWithHeaderType { TextFieldWithHeaderType {
@ -214,7 +228,6 @@ PageType {
headerText: qsTr("S2 - Response packet junk size") headerText: qsTr("S2 - Response packet junk size")
textFieldText: serverResponsePacketJunkSize textFieldText: serverResponsePacketJunkSize
textField.validator: IntValidator { bottom: 0 } textField.validator: IntValidator { bottom: 0 }
parentFlickable: fl
textField.onEditingFinished: { textField.onEditingFinished: {
if (textFieldText !== serverResponsePacketJunkSize) { if (textFieldText !== serverResponsePacketJunkSize) {
@ -224,7 +237,11 @@ PageType {
checkEmptyText: true checkEmptyText: true
KeyNavigation.tab: initPacketMagicHeaderTextField.textField onActiveFocusChanged: {
if(activeFocus) {
listview.positionViewAtEnd()
}
}
} }
TextFieldWithHeaderType { TextFieldWithHeaderType {
@ -235,7 +252,6 @@ PageType {
headerText: qsTr("H1 - Init packet magic header") headerText: qsTr("H1 - Init packet magic header")
textFieldText: serverInitPacketMagicHeader textFieldText: serverInitPacketMagicHeader
textField.validator: IntValidator { bottom: 0 } textField.validator: IntValidator { bottom: 0 }
parentFlickable: fl
textField.onEditingFinished: { textField.onEditingFinished: {
if (textFieldText !== serverInitPacketMagicHeader) { if (textFieldText !== serverInitPacketMagicHeader) {
@ -244,8 +260,6 @@ PageType {
} }
checkEmptyText: true checkEmptyText: true
KeyNavigation.tab: responsePacketMagicHeaderTextField.textField
} }
TextFieldWithHeaderType { TextFieldWithHeaderType {
@ -256,7 +270,6 @@ PageType {
headerText: qsTr("H2 - Response packet magic header") headerText: qsTr("H2 - Response packet magic header")
textFieldText: serverResponsePacketMagicHeader textFieldText: serverResponsePacketMagicHeader
textField.validator: IntValidator { bottom: 0 } textField.validator: IntValidator { bottom: 0 }
parentFlickable: fl
textField.onEditingFinished: { textField.onEditingFinished: {
if (textFieldText !== serverResponsePacketMagicHeader) { if (textFieldText !== serverResponsePacketMagicHeader) {
@ -265,8 +278,6 @@ PageType {
} }
checkEmptyText: true checkEmptyText: true
KeyNavigation.tab: transportPacketMagicHeaderTextField.textField
} }
TextFieldWithHeaderType { TextFieldWithHeaderType {
@ -277,7 +288,6 @@ PageType {
headerText: qsTr("H4 - Transport packet magic header") headerText: qsTr("H4 - Transport packet magic header")
textFieldText: serverTransportPacketMagicHeader textFieldText: serverTransportPacketMagicHeader
textField.validator: IntValidator { bottom: 0 } textField.validator: IntValidator { bottom: 0 }
parentFlickable: fl
textField.onEditingFinished: { textField.onEditingFinished: {
if (textFieldText !== serverTransportPacketMagicHeader) { if (textFieldText !== serverTransportPacketMagicHeader) {
@ -286,15 +296,12 @@ PageType {
} }
checkEmptyText: true checkEmptyText: true
KeyNavigation.tab: underloadPacketMagicHeaderTextField.textField
} }
TextFieldWithHeaderType { TextFieldWithHeaderType {
id: underloadPacketMagicHeaderTextField id: underloadPacketMagicHeaderTextField
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 16 Layout.topMargin: 16
parentFlickable: fl
headerText: qsTr("H3 - Underload packet magic header") headerText: qsTr("H3 - Underload packet magic header")
textFieldText: serverUnderloadPacketMagicHeader textFieldText: serverUnderloadPacketMagicHeader
@ -307,13 +314,10 @@ PageType {
} }
checkEmptyText: true checkEmptyText: true
KeyNavigation.tab: saveRestartButton
} }
BasicButtonType { BasicButtonType {
id: saveRestartButton id: saveRestartButton
parentFlickable: fl
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 24 Layout.topMargin: 24
@ -332,7 +336,11 @@ PageType {
text: qsTr("Save") text: qsTr("Save")
Keys.onTabPressed: lastItemTabClicked(focusItem) onActiveFocusChanged: {
if(activeFocus) {
listview.positionViewAtEnd()
}
}
clickedFunc: function() { clickedFunc: function() {
forceActiveFocus() forceActiveFocus()
@ -378,6 +386,4 @@ PageType {
} }
} }
} }
}
}
} }

View file

@ -16,13 +16,6 @@ import "../Components"
PageType { PageType {
id: root id: root
defaultActiveFocusItem: listview.currentItem.trafficFromField.textField
Item {
id: focusItem
KeyNavigation.tab: backButton
}
ColumnLayout { ColumnLayout {
id: backButtonLayout id: backButtonLayout
@ -34,7 +27,6 @@ PageType {
BackButtonType { BackButtonType {
id: backButton id: backButton
KeyNavigation.tab: listview.currentItem.trafficFromField.textField
} }
} }
@ -110,8 +102,6 @@ PageType {
} }
} }
} }
KeyNavigation.tab: portTextField.textField
} }
TextFieldWithHeaderType { TextFieldWithHeaderType {
@ -130,8 +120,6 @@ PageType {
port = textFieldText port = textFieldText
} }
} }
KeyNavigation.tab: cipherDropDown
} }
DropDownType { DropDownType {
@ -143,7 +131,6 @@ PageType {
headerText: qsTr("Cipher") headerText: qsTr("Cipher")
drawerParent: root drawerParent: root
KeyNavigation.tab: saveRestartButton
listView: ListViewWithRadioButtonType { listView: ListViewWithRadioButtonType {
id: cipherListView id: cipherListView
@ -161,7 +148,7 @@ PageType {
clickedFunction: function() { clickedFunction: function() {
cipherDropDown.text = selectedText cipherDropDown.text = selectedText
cipher = cipherDropDown.text cipher = cipherDropDown.text
cipherDropDown.close() cipherDropDown.closeTriggered()
} }
Component.onCompleted: { Component.onCompleted: {
@ -184,7 +171,6 @@ PageType {
Layout.bottomMargin: 24 Layout.bottomMargin: 24
text: qsTr("Save") text: qsTr("Save")
Keys.onTabPressed: lastItemTabClicked(focusItem)
clickedFunc: function() { clickedFunc: function() {
forceActiveFocus() forceActiveFocus()

View file

@ -17,18 +17,6 @@ import "../Components"
PageType { PageType {
id: root id: root
defaultActiveFocusItem: listview.currentItem.vpnAddressSubnetTextField.textField
Item {
id: focusItem
KeyNavigation.tab: backButton
onActiveFocusChanged: {
if (activeFocus) {
fl.ensureVisible(focusItem)
}
}
}
ColumnLayout { ColumnLayout {
id: backButtonLayout id: backButtonLayout
@ -40,7 +28,6 @@ PageType {
BackButtonType { BackButtonType {
id: backButton id: backButton
KeyNavigation.tab: listview.currentItem.vpnAddressSubnetTextField.textField
} }
} }
@ -104,7 +91,6 @@ PageType {
textFieldText: subnetAddress textFieldText: subnetAddress
parentFlickable: fl parentFlickable: fl
KeyNavigation.tab: transportProtoSelector
textField.onEditingFinished: { textField.onEditingFinished: {
if (textFieldText !== subnetAddress) { if (textFieldText !== subnetAddress) {
@ -132,8 +118,6 @@ PageType {
return transportProto === "tcp" ? 1 : 0 return transportProto === "tcp" ? 1 : 0
} }
KeyNavigation.tab: portTextField.enabled ? portTextField.textField : autoNegotiateEncryprionSwitcher
onCurrentIndexChanged: { onCurrentIndexChanged: {
if (transportProto === "tcp" && currentIndex === 0) { if (transportProto === "tcp" && currentIndex === 0) {
transportProto = "udp" transportProto = "udp"
@ -162,8 +146,6 @@ PageType {
port = textFieldText port = textFieldText
} }
} }
KeyNavigation.tab: autoNegotiateEncryprionSwitcher
} }
SwitcherType { SwitcherType {
@ -181,10 +163,6 @@ PageType {
autoNegotiateEncryprion = checked autoNegotiateEncryprion = checked
} }
} }
KeyNavigation.tab: hashDropDown.enabled ?
hashDropDown :
tlsAuthCheckBox
} }
DropDownType { DropDownType {
@ -199,9 +177,6 @@ PageType {
drawerParent: root drawerParent: root
parentFlickable: fl parentFlickable: fl
KeyNavigation.tab: cipherDropDown.enabled ?
cipherDropDown :
tlsAuthCheckBox
listView: ListViewWithRadioButtonType { listView: ListViewWithRadioButtonType {
id: hashListView id: hashListView
@ -224,7 +199,7 @@ PageType {
clickedFunction: function() { clickedFunction: function() {
hashDropDown.text = selectedText hashDropDown.text = selectedText
hash = hashDropDown.text hash = hashDropDown.text
hashDropDown.close() hashDropDown.closeTriggered()
} }
Component.onCompleted: { Component.onCompleted: {
@ -252,8 +227,6 @@ PageType {
drawerParent: root drawerParent: root
parentFlickable: fl parentFlickable: fl
KeyNavigation.tab: tlsAuthCheckBox
listView: ListViewWithRadioButtonType { listView: ListViewWithRadioButtonType {
id: cipherListView id: cipherListView
@ -275,7 +248,7 @@ PageType {
clickedFunction: function() { clickedFunction: function() {
cipherDropDown.text = selectedText cipherDropDown.text = selectedText
cipher = cipherDropDown.text cipher = cipherDropDown.text
cipherDropDown.close() cipherDropDown.closeTriggered()
} }
Component.onCompleted: { Component.onCompleted: {
@ -320,8 +293,6 @@ PageType {
text: qsTr("TLS auth") text: qsTr("TLS auth")
checked: tlsAuth checked: tlsAuth
KeyNavigation.tab: blockDnsCheckBox
onCheckedChanged: { onCheckedChanged: {
if (checked !== tlsAuth) { if (checked !== tlsAuth) {
console.log("tlsAuth changed to: " + checked) console.log("tlsAuth changed to: " + checked)
@ -339,8 +310,6 @@ PageType {
text: qsTr("Block DNS requests outside of VPN") text: qsTr("Block DNS requests outside of VPN")
checked: blockDns checked: blockDns
KeyNavigation.tab: additionalClientCommandsSwitcher
onCheckedChanged: { onCheckedChanged: {
if (checked !== blockDns) { if (checked !== blockDns) {
blockDns = checked blockDns = checked
@ -355,9 +324,6 @@ PageType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 32 Layout.topMargin: 32
parentFlickable: fl parentFlickable: fl
KeyNavigation.tab: additionalClientCommandsTextArea.visible ?
additionalClientCommandsTextArea.textArea :
additionalServerCommandsSwitcher
checked: additionalClientCommands !== "" checked: additionalClientCommands !== ""
@ -376,7 +342,7 @@ PageType {
Layout.topMargin: 16 Layout.topMargin: 16
visible: additionalClientCommandsSwitcher.checked visible: additionalClientCommandsSwitcher.checked
KeyNavigation.tab: additionalServerCommandsSwitcher
parentFlickable: fl parentFlickable: fl
textAreaText: additionalClientCommands textAreaText: additionalClientCommands
@ -394,9 +360,6 @@ PageType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 16 Layout.topMargin: 16
parentFlickable: fl parentFlickable: fl
KeyNavigation.tab: additionalServerCommandsTextArea.visible ?
additionalServerCommandsTextArea.textArea :
saveRestartButton
checked: additionalServerCommands !== "" checked: additionalServerCommands !== ""
@ -419,7 +382,6 @@ PageType {
textAreaText: additionalServerCommands textAreaText: additionalServerCommands
placeholderText: qsTr("Commands:") placeholderText: qsTr("Commands:")
parentFlickable: fl parentFlickable: fl
KeyNavigation.tab: saveRestartButton
textArea.onEditingFinished: { textArea.onEditingFinished: {
if (additionalServerCommands !== textAreaText) { if (additionalServerCommands !== textAreaText) {
additionalServerCommands = textAreaText additionalServerCommands = textAreaText
@ -436,7 +398,6 @@ PageType {
text: qsTr("Save") text: qsTr("Save")
parentFlickable: fl parentFlickable: fl
Keys.onTabPressed: lastItemTabClicked(focusItem)
clickedFunc: function() { clickedFunc: function() {
forceActiveFocus() forceActiveFocus()

View file

@ -19,13 +19,6 @@ import "../Components"
PageType { PageType {
id: root id: root
defaultActiveFocusItem: focusItem
Item {
id: focusItem
KeyNavigation.tab: backButton
}
ColumnLayout { ColumnLayout {
id: header id: header
@ -37,7 +30,6 @@ PageType {
BackButtonType { BackButtonType {
id: backButton id: backButton
KeyNavigation.tab: listView
} }
HeaderType { HeaderType {
@ -101,11 +93,9 @@ PageType {
text: qsTr("Show connection options") text: qsTr("Show connection options")
clickedFunction: function() { clickedFunction: function() {
configContentDrawer.open() configContentDrawer.openTriggered()
} }
KeyNavigation.tab: removeButton
MouseArea { MouseArea {
anchors.fill: button anchors.fill: button
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
@ -120,31 +110,12 @@ PageType {
expandedHeight: root.height * 0.9 expandedHeight: root.height * 0.9
onClosed: {
if (!GC.isMobile()) {
defaultActiveFocusItem.forceActiveFocus()
}
}
parent: root parent: root
anchors.fill: parent anchors.fill: parent
expandedContent: Item { expandedStateContent: Item {
implicitHeight: configContentDrawer.expandedHeight implicitHeight: configContentDrawer.expandedHeight
Connections {
target: configContentDrawer
enabled: !GC.isMobile()
function onOpened() {
focusItem1.forceActiveFocus()
}
}
Item {
id: focusItem1
KeyNavigation.tab: backButton1
}
BackButtonType { BackButtonType {
id: backButton1 id: backButton1
@ -154,10 +125,8 @@ PageType {
anchors.topMargin: 16 anchors.topMargin: 16
backButtonFunction: function() { backButtonFunction: function() {
configContentDrawer.close() configContentDrawer.closeTriggered()
} }
KeyNavigation.tab: focusItem1
} }
FlickableType { FlickableType {
@ -226,7 +195,6 @@ PageType {
text: qsTr("Remove ") + ContainersModel.getProcessedContainerName() text: qsTr("Remove ") + ContainersModel.getProcessedContainerName()
textColor: AmneziaStyle.color.vibrantRed textColor: AmneziaStyle.color.vibrantRed
Keys.onTabPressed: lastItemTabClicked(focusItem)
clickedFunction: function() { clickedFunction: function() {
var headerText = qsTr("Remove %1 from server?").arg(ContainersModel.getProcessedContainerName()) 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.") var descriptionText = qsTr("All users with whom you shared a connection with will no longer be able to connect to it.")

View file

@ -16,15 +16,6 @@ import "../Components"
PageType { PageType {
id: root id: root
defaultActiveFocusItem: listview.currentItem.focusItemId.enabled ?
listview.currentItem.focusItemId.textField :
focusItem
Item {
id: focusItem
KeyNavigation.tab: backButton
}
ColumnLayout { ColumnLayout {
id: backButtonLayout id: backButtonLayout
@ -36,9 +27,6 @@ PageType {
BackButtonType { BackButtonType {
id: backButton id: backButton
KeyNavigation.tab: listview.currentItem.focusItemId.enabled ?
listview.currentItem.focusItemId.textField :
focusItem
} }
} }
@ -114,8 +102,6 @@ PageType {
port = textFieldText port = textFieldText
} }
} }
KeyNavigation.tab: cipherDropDown
} }
DropDownType { DropDownType {
@ -129,9 +115,9 @@ PageType {
headerText: qsTr("Cipher") headerText: qsTr("Cipher")
drawerParent: root drawerParent: root
KeyNavigation.tab: saveRestartButton
listView: ListViewWithRadioButtonType { listView: ListViewWithRadioButtonType {
id: cipherListView id: cipherListView
rootWidth: root.width rootWidth: root.width
@ -147,7 +133,7 @@ PageType {
clickedFunction: function() { clickedFunction: function() {
cipherDropDown.text = selectedText cipherDropDown.text = selectedText
cipher = cipherDropDown.text cipher = cipherDropDown.text
cipherDropDown.close() cipherDropDown.closeTriggered()
} }
Component.onCompleted: { Component.onCompleted: {
@ -172,7 +158,6 @@ PageType {
enabled: isPortEditable | isCipherEditable enabled: isPortEditable | isCipherEditable
text: qsTr("Save") text: qsTr("Save")
Keys.onTabPressed: lastItemTabClicked(focusItem)
clickedFunc: function() { clickedFunc: function() {
forceActiveFocus() forceActiveFocus()

View file

@ -150,8 +150,6 @@ PageType {
text: qsTr("Save") text: qsTr("Save")
Keys.onTabPressed: lastItemTabClicked(focusItem)
clickedFunc: function() { clickedFunc: function() {
forceActiveFocus() forceActiveFocus()
var headerText = qsTr("Save settings?") var headerText = qsTr("Save settings?")

View file

@ -16,13 +16,6 @@ import "../Components"
PageType { PageType {
id: root id: root
defaultActiveFocusItem: listview
Item {
id: focusItem
KeyNavigation.tab: backButton
}
ColumnLayout { ColumnLayout {
id: backButtonLayout id: backButtonLayout
@ -34,7 +27,6 @@ PageType {
BackButtonType { BackButtonType {
id: backButton id: backButton
KeyNavigation.tab: listview
} }
} }
@ -64,12 +56,12 @@ PageType {
model: WireGuardConfigModel model: WireGuardConfigModel
activeFocusOnTab: true // activeFocusOnTab: true
onActiveFocusChanged: { // onActiveFocusChanged: {
if (activeFocus) { // if (activeFocus) {
listview.itemAtIndex(0)?.focusItemId.forceActiveFocus() // listview.itemAtIndex(0)?.focusItemId.forceActiveFocus()
} // }
} // }
delegate: Item { delegate: Item {
id: delegateItem id: delegateItem
@ -109,8 +101,6 @@ PageType {
textField.maximumLength: 5 textField.maximumLength: 5
textField.validator: IntValidator { bottom: 1; top: 65535 } textField.validator: IntValidator { bottom: 1; top: 65535 }
KeyNavigation.tab: saveButton
textField.onEditingFinished: { textField.onEditingFinished: {
if (textFieldText !== port) { if (textFieldText !== port) {
port = textFieldText port = textFieldText
@ -120,6 +110,26 @@ PageType {
checkEmptyText: true 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 { BasicButtonType {
id: saveButton id: saveButton
Layout.fillWidth: true Layout.fillWidth: true
@ -130,8 +140,6 @@ PageType {
text: qsTr("Save") text: qsTr("Save")
Keys.onTabPressed: lastItemTabClicked(focusItem)
onClicked: function() { onClicked: function() {
forceActiveFocus() forceActiveFocus()

View file

@ -17,13 +17,6 @@ import "../Components"
PageType { PageType {
id: root id: root
defaultActiveFocusItem: listview
Item {
id: focusItem
KeyNavigation.tab: backButton
}
ColumnLayout { ColumnLayout {
id: backButtonLayout id: backButtonLayout
@ -35,7 +28,6 @@ PageType {
BackButtonType { BackButtonType {
id: backButton id: backButton
KeyNavigation.tab: listview
} }
} }
@ -65,12 +57,12 @@ PageType {
model: XrayConfigModel model: XrayConfigModel
activeFocusOnTab: true // activeFocusOnTab: true
onActiveFocusChanged: { // onActiveFocusChanged: {
if (activeFocus) { // if (activeFocus) {
listview.itemAtIndex(0)?.focusItemId.forceActiveFocus() // listview.itemAtIndex(0)?.focusItemId.forceActiveFocus()
} // }
} // }
delegate: Item { delegate: Item {
property alias focusItemId: textFieldWithHeaderType.textField property alias focusItemId: textFieldWithHeaderType.textField
@ -103,8 +95,6 @@ PageType {
headerText: qsTr("Disguised as traffic from") headerText: qsTr("Disguised as traffic from")
textFieldText: site textFieldText: site
KeyNavigation.tab: basicButton
textField.onEditingFinished: { textField.onEditingFinished: {
if (textFieldText !== site) { if (textFieldText !== site) {
var tmpText = textFieldText var tmpText = textFieldText
@ -128,8 +118,6 @@ PageType {
text: qsTr("Save") text: qsTr("Save")
Keys.onTabPressed: lastItemTabClicked(focusItem)
onClicked: { onClicked: {
forceActiveFocus() forceActiveFocus()

View file

@ -16,13 +16,6 @@ import "../Components"
PageType { PageType {
id: root id: root
defaultActiveFocusItem: focusItem
Item {
id: focusItem
KeyNavigation.tab: backButton
}
ColumnLayout { ColumnLayout {
id: backButtonLayout id: backButtonLayout
@ -34,7 +27,6 @@ PageType {
BackButtonType { BackButtonType {
id: backButton id: backButton
KeyNavigation.tab: removeButton
} }
} }
@ -72,8 +64,6 @@ PageType {
text: qsTr("Remove ") + ContainersModel.getProcessedContainerName() text: qsTr("Remove ") + ContainersModel.getProcessedContainerName()
textColor: AmneziaStyle.color.vibrantRed textColor: AmneziaStyle.color.vibrantRed
Keys.onTabPressed: root.lastItemTabClicked()
clickedFunction: function() { clickedFunction: function() {
var headerText = qsTr("Remove %1 from server?").arg(ContainersModel.getProcessedContainerName()) var headerText = qsTr("Remove %1 from server?").arg(ContainersModel.getProcessedContainerName())
var yesButtonText = qsTr("Continue") var yesButtonText = qsTr("Continue")

View file

@ -16,8 +16,6 @@ import "../Components"
PageType { PageType {
id: root id: root
defaultActiveFocusItem: focusItem
Connections { Connections {
target: InstallController target: InstallController
@ -26,11 +24,6 @@ PageType {
} }
} }
Item {
id: focusItem
KeyNavigation.tab: backButton
}
ColumnLayout { ColumnLayout {
id: backButtonLayout id: backButtonLayout
@ -42,7 +35,6 @@ PageType {
BackButtonType { BackButtonType {
id: backButton id: backButton
KeyNavigation.tab: listview
} }
} }
@ -107,7 +99,6 @@ PageType {
Layout.topMargin: 32 Layout.topMargin: 32
parentFlickable: fl parentFlickable: fl
KeyNavigation.tab: portLabel.rightButton
text: qsTr("Host") text: qsTr("Host")
descriptionText: ServersModel.getProcessedServerData("hostName") descriptionText: ServersModel.getProcessedServerData("hostName")
@ -136,7 +127,6 @@ PageType {
descriptionOnTop: true descriptionOnTop: true
parentFlickable: fl parentFlickable: fl
KeyNavigation.tab: usernameLabel.rightButton
rightImageSource: "qrc:/images/controls/copy.svg" rightImageSource: "qrc:/images/controls/copy.svg"
rightImageColor: AmneziaStyle.color.paleGray rightImageColor: AmneziaStyle.color.paleGray
@ -160,7 +150,6 @@ PageType {
descriptionOnTop: true descriptionOnTop: true
parentFlickable: fl parentFlickable: fl
KeyNavigation.tab: passwordLabel.eyeButton
rightImageSource: "qrc:/images/controls/copy.svg" rightImageSource: "qrc:/images/controls/copy.svg"
rightImageColor: AmneziaStyle.color.paleGray rightImageColor: AmneziaStyle.color.paleGray
@ -184,14 +173,6 @@ PageType {
descriptionOnTop: true descriptionOnTop: true
parentFlickable: fl parentFlickable: fl
eyeButton.KeyNavigation.tab: passwordLabel.rightButton
rightButton.Keys.onTabPressed: {
if (mountButton.visible) {
mountButton.forceActiveFocus()
} else {
detailedInstructionsButton.forceActiveFocus()
}
}
rightImageSource: "qrc:/images/controls/copy.svg" rightImageSource: "qrc:/images/controls/copy.svg"
rightImageColor: AmneziaStyle.color.paleGray rightImageColor: AmneziaStyle.color.paleGray
@ -225,7 +206,6 @@ PageType {
borderWidth: 1 borderWidth: 1
parentFlickable: fl parentFlickable: fl
KeyNavigation.tab: detailedInstructionsButton
text: qsTr("Mount folder on device") text: qsTr("Mount folder on device")
@ -290,7 +270,6 @@ PageType {
text: qsTr("Detailed instructions") text: qsTr("Detailed instructions")
parentFlickable: fl parentFlickable: fl
Keys.onTabPressed: lastItemTabClicked(focusItem)
clickedFunc: function() { clickedFunc: function() {
// Qt.openUrlExternally("https://github.com/amnezia-vpn/desktop-client/releases/latest") // Qt.openUrlExternally("https://github.com/amnezia-vpn/desktop-client/releases/latest")

View file

@ -17,8 +17,6 @@ import "../Components"
PageType { PageType {
id: root id: root
defaultActiveFocusItem: listview
Connections { Connections {
target: InstallController target: InstallController
@ -27,11 +25,6 @@ PageType {
} }
} }
Item {
id: focusItem
KeyNavigation.tab: backButton
}
ColumnLayout { ColumnLayout {
id: backButtonLayout id: backButtonLayout
@ -43,7 +36,6 @@ PageType {
BackButtonType { BackButtonType {
id: backButton id: backButton
KeyNavigation.tab: listview
} }
} }
@ -99,7 +91,6 @@ PageType {
Layout.topMargin: 32 Layout.topMargin: 32
parentFlickable: fl parentFlickable: fl
KeyNavigation.tab: portLabel.rightButton
text: qsTr("Host") text: qsTr("Host")
descriptionText: ServersModel.getProcessedServerData("hostName") descriptionText: ServersModel.getProcessedServerData("hostName")
@ -128,7 +119,6 @@ PageType {
descriptionOnTop: true descriptionOnTop: true
parentFlickable: fl parentFlickable: fl
KeyNavigation.tab: usernameLabel.rightButton
rightImageSource: "qrc:/images/controls/copy.svg" rightImageSource: "qrc:/images/controls/copy.svg"
rightImageColor: AmneziaStyle.color.paleGray rightImageColor: AmneziaStyle.color.paleGray
@ -152,7 +142,6 @@ PageType {
descriptionOnTop: true descriptionOnTop: true
parentFlickable: fl parentFlickable: fl
KeyNavigation.tab: passwordLabel.eyeButton
rightImageSource: "qrc:/images/controls/copy.svg" rightImageSource: "qrc:/images/controls/copy.svg"
rightImageColor: AmneziaStyle.color.paleGray rightImageColor: AmneziaStyle.color.paleGray
@ -176,8 +165,6 @@ PageType {
descriptionOnTop: true descriptionOnTop: true
parentFlickable: fl parentFlickable: fl
eyeButton.KeyNavigation.tab: passwordLabel.rightButton
rightButton.KeyNavigation.tab: changeSettingsButton
rightImageSource: "qrc:/images/controls/copy.svg" rightImageSource: "qrc:/images/controls/copy.svg"
rightImageColor: AmneziaStyle.color.paleGray rightImageColor: AmneziaStyle.color.paleGray
@ -200,13 +187,7 @@ PageType {
anchors.fill: parent anchors.fill: parent
expandedHeight: root.height * 0.9 expandedHeight: root.height * 0.9
onClosed: { expandedStateContent: ColumnLayout {
if (!GC.isMobile()) {
focusItem.forceActiveFocus()
}
}
expandedContent: ColumnLayout {
property string tempPort: port property string tempPort: port
property string tempUsername: username property string tempUsername: username
property string tempPassword: password property string tempPassword: password
@ -222,9 +203,6 @@ PageType {
Connections { Connections {
target: changeSettingsDrawer target: changeSettingsDrawer
function onOpened() { function onOpened() {
if (!GC.isMobile()) {
drawerFocusItem.forceActiveFocus()
}
tempPort = port tempPort = port
tempUsername = username tempUsername = username
tempPassword = password tempPassword = password
@ -239,11 +217,6 @@ PageType {
} }
} }
Item {
id: drawerFocusItem
KeyNavigation.tab: portTextField.textField
}
HeaderType { HeaderType {
Layout.fillWidth: true Layout.fillWidth: true
@ -268,8 +241,6 @@ PageType {
port = textFieldText port = textFieldText
} }
} }
KeyNavigation.tab: usernameTextField.textField
} }
TextFieldWithHeaderType { TextFieldWithHeaderType {
@ -290,8 +261,6 @@ PageType {
username = textFieldText username = textFieldText
} }
} }
KeyNavigation.tab: passwordTextField.textField
} }
TextFieldWithHeaderType { TextFieldWithHeaderType {
@ -322,8 +291,6 @@ PageType {
password = textFieldText password = textFieldText
} }
} }
KeyNavigation.tab: saveButton
} }
BasicButtonType { BasicButtonType {
@ -334,7 +301,6 @@ PageType {
Layout.bottomMargin: 24 Layout.bottomMargin: 24
text: qsTr("Change connection settings") text: qsTr("Change connection settings")
Keys.onTabPressed: lastItemTabClicked(drawerFocusItem)
clickedFunc: function() { clickedFunc: function() {
forceActiveFocus() forceActiveFocus()
@ -356,7 +322,7 @@ PageType {
tempPort = portTextField.textFieldText tempPort = portTextField.textFieldText
tempUsername = usernameTextField.textFieldText tempUsername = usernameTextField.textFieldText
tempPassword = passwordTextField.textFieldText tempPassword = passwordTextField.textFieldText
changeSettingsDrawer.close() changeSettingsDrawer.closeTriggered()
} }
} }
} }
@ -372,11 +338,10 @@ PageType {
Layout.rightMargin: 16 Layout.rightMargin: 16
text: qsTr("Change connection settings") text: qsTr("Change connection settings")
Keys.onTabPressed: lastItemTabClicked(focusItem)
clickedFunc: function() { clickedFunc: function() {
forceActiveFocus() forceActiveFocus()
changeSettingsDrawer.open() changeSettingsDrawer.openTriggered()
} }
} }
} }

View file

@ -17,8 +17,6 @@ import "../Components"
PageType { PageType {
id: root id: root
defaultActiveFocusItem: focusItem
Connections { Connections {
target: InstallController target: InstallController
@ -27,11 +25,6 @@ PageType {
} }
} }
Item {
id: focusItem
KeyNavigation.tab: backButton
}
ColumnLayout { ColumnLayout {
id: backButtonLayout id: backButtonLayout
@ -43,7 +36,6 @@ PageType {
BackButtonType { BackButtonType {
id: backButton id: backButton
KeyNavigation.tab: websiteName.rightButton
} }
} }
@ -88,8 +80,6 @@ PageType {
rightImageSource: "qrc:/images/controls/copy.svg" rightImageSource: "qrc:/images/controls/copy.svg"
rightImageColor: AmneziaStyle.color.paleGray rightImageColor: AmneziaStyle.color.paleGray
Keys.onTabPressed: lastItemTabClicked(focusItem)
clickedFunction: function() { clickedFunction: function() {
GC.copyToClipBoard(descriptionText) GC.copyToClipBoard(descriptionText)
PageController.showNotificationMessage(qsTr("Copied")) PageController.showNotificationMessage(qsTr("Copied"))

View file

@ -14,8 +14,6 @@ import "../Config"
PageType { PageType {
id: root id: root
defaultActiveFocusItem: header
FlickableType { FlickableType {
id: fl id: fl
anchors.top: parent.top anchors.top: parent.top
@ -39,8 +37,6 @@ PageType {
Layout.leftMargin: 16 Layout.leftMargin: 16
headerText: qsTr("Settings") headerText: qsTr("Settings")
KeyNavigation.tab: account.rightButton
} }
LabelWithButtonType { LabelWithButtonType {
@ -55,8 +51,6 @@ PageType {
clickedFunction: function() { clickedFunction: function() {
PageController.goToPage(PageEnum.PageSettingsServersList) PageController.goToPage(PageEnum.PageSettingsServersList)
} }
KeyNavigation.tab: connection.rightButton
} }
DividerType {} DividerType {}
@ -72,8 +66,6 @@ PageType {
clickedFunction: function() { clickedFunction: function() {
PageController.goToPage(PageEnum.PageSettingsConnection) PageController.goToPage(PageEnum.PageSettingsConnection)
} }
KeyNavigation.tab: application.rightButton
} }
DividerType {} DividerType {}
@ -89,8 +81,6 @@ PageType {
clickedFunction: function() { clickedFunction: function() {
PageController.goToPage(PageEnum.PageSettingsApplication) PageController.goToPage(PageEnum.PageSettingsApplication)
} }
KeyNavigation.tab: backup.rightButton
} }
DividerType {} DividerType {}
@ -106,8 +96,6 @@ PageType {
clickedFunction: function() { clickedFunction: function() {
PageController.goToPage(PageEnum.PageSettingsBackup) PageController.goToPage(PageEnum.PageSettingsBackup)
} }
KeyNavigation.tab: about.rightButton
} }
DividerType {} DividerType {}
@ -123,8 +111,6 @@ PageType {
clickedFunction: function() { clickedFunction: function() {
PageController.goToPage(PageEnum.PageSettingsAbout) PageController.goToPage(PageEnum.PageSettingsAbout)
} }
KeyNavigation.tab: close
} }
DividerType {} DividerType {}
@ -138,8 +124,6 @@ PageType {
rightImageSource: "qrc:/images/controls/chevron-right.svg" rightImageSource: "qrc:/images/controls/chevron-right.svg"
leftImageSource: "qrc:/images/controls/bug.svg" leftImageSource: "qrc:/images/controls/bug.svg"
// Keys.onTabPressed: lastItemTabClicked(header)
clickedFunction: function() { clickedFunction: function() {
PageController.goToPage(PageEnum.PageDevMenu) PageController.goToPage(PageEnum.PageDevMenu)
} }
@ -156,10 +140,8 @@ PageType {
Layout.preferredHeight: about.height Layout.preferredHeight: about.height
text: qsTr("Close application") text: qsTr("Close application")
rightImageSource: "qrc:/images/controls/chevron-right.svg"
leftImageSource: "qrc:/images/controls/x-circle.svg" leftImageSource: "qrc:/images/controls/x-circle.svg"
isLeftImageHoverEnabled: false
Keys.onTabPressed: lastItemTabClicked(header)
clickedFunction: function() { clickedFunction: function() {
PageController.closeApplication() PageController.closeApplication()

View file

@ -14,19 +14,6 @@ import "../Components"
PageType { PageType {
id: root id: root
defaultActiveFocusItem: focusItem
Item {
id: focusItem
KeyNavigation.tab: backButton
onFocusChanged: {
if (focusItem.activeFocus) {
fl.contentY = 0
}
}
}
BackButtonType { BackButtonType {
id: backButton id: backButton
@ -35,21 +22,109 @@ PageType {
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 20 anchors.topMargin: 20
KeyNavigation.tab: telegramButton onActiveFocusChanged: {
if(backButton.enabled && backButton.activeFocus) {
listView.positionViewAtBeginning()
}
}
} }
FlickableType { QtObject {
id: fl 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<QtObject> contacts: [
telegramGroup,
mail,
github,
website
]
ListView {
id: listView
anchors.top: backButton.bottom anchors.top: backButton.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
contentHeight: content.height
ColumnLayout {
id: content
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.left: parent.left
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: contacts
clip: true
header: ColumnLayout {
width: listView.width
Image { Image {
id: image id: image
@ -96,81 +171,29 @@ PageType {
text: qsTr("Contacts") text: qsTr("Contacts")
} }
}
delegate: ColumnLayout {
width: listView.width
LabelWithButtonType { LabelWithButtonType {
id: telegramButton id: telegramButton
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 16 Layout.topMargin: 6
text: qsTr("Telegram group") text: title
descriptionText: qsTr("To discuss features") descriptionText: description
leftImageSource: "qrc:/images/controls/telegram.svg" leftImageSource: imageSource
KeyNavigation.tab: mailButton clickedFunction: handler
parentFlickable: fl
clickedFunction: function() {
Qt.openUrlExternally(qsTr("https://t.me/amnezia_vpn_en"))
}
} }
DividerType {} 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"
KeyNavigation.tab: githubButton
parentFlickable: fl
clickedFunction: function() {
GC.copyToClipBoard(text)
PageController.showNotificationMessage(qsTr("Copied"))
} }
} footer: ColumnLayout {
width: listView.width
DividerType {}
LabelWithButtonType {
id: githubButton
Layout.fillWidth: true
text: qsTr("GitHub")
leftImageSource: "qrc:/images/controls/github.svg"
KeyNavigation.tab: websiteButton
parentFlickable: fl
clickedFunction: function() {
Qt.openUrlExternally(qsTr("https://github.com/amnezia-vpn/amnezia-client"))
}
}
DividerType {}
LabelWithButtonType {
id: websiteButton
Layout.fillWidth: true
text: qsTr("Website")
leftImageSource: "qrc:/images/controls/amnezia.svg"
KeyNavigation.tab: checkUpdatesButton
parentFlickable: fl
clickedFunction: function() {
Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl())
}
}
DividerType {}
CaptionTextType { CaptionTextType {
Layout.fillWidth: true Layout.fillWidth: true
@ -196,6 +219,7 @@ PageType {
BasicButtonType { BasicButtonType {
id: checkUpdatesButton id: checkUpdatesButton
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
Layout.topMargin: 8 Layout.topMargin: 8
Layout.bottomMargin: 16 Layout.bottomMargin: 16
@ -209,9 +233,6 @@ PageType {
text: qsTr("Check for updates") text: qsTr("Check for updates")
KeyNavigation.tab: privacyPolicyButton
parentFlickable: fl
clickedFunc: function() { clickedFunc: function() {
Qt.openUrlExternally("https://github.com/amnezia-vpn/desktop-client/releases/latest") Qt.openUrlExternally("https://github.com/amnezia-vpn/desktop-client/releases/latest")
} }
@ -219,6 +240,7 @@ PageType {
BasicButtonType { BasicButtonType {
id: privacyPolicyButton id: privacyPolicyButton
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
Layout.bottomMargin: 16 Layout.bottomMargin: 16
Layout.topMargin: -15 Layout.topMargin: -15
@ -232,9 +254,6 @@ PageType {
text: qsTr("Privacy Policy") text: qsTr("Privacy Policy")
Keys.onTabPressed: lastItemTabClicked()
parentFlickable: fl
clickedFunc: function() { clickedFunc: function() {
Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl() + "/policy") Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl() + "/policy")
} }

View file

@ -31,14 +31,11 @@ PageType {
id: containersRadioButtonGroup id: containersRadioButtonGroup
} }
delegate: Item { delegate: ColumnLayout {
implicitWidth: parent.width
implicitHeight: content.implicitHeight
ColumnLayout {
id: content id: content
anchors.fill: parent implicitWidth: parent.width
implicitHeight: content.implicitHeight
RowLayout { RowLayout {
VerticalRadioButton { VerticalRadioButton {
@ -99,5 +96,4 @@ PageType {
} }
} }
} }
}
} }

View file

@ -15,8 +15,6 @@ import "../Components"
PageType { PageType {
id: root id: root
defaultActiveFocusItem: focusItem
FlickableType { FlickableType {
id: fl id: fl
anchors.top: parent.top anchors.top: parent.top
@ -32,11 +30,6 @@ PageType {
spacing: 0 spacing: 0
Item {
id: focusItem
// KeyNavigation.tab: backButton
}
LabelWithImageType { LabelWithImageType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.margins: 16 Layout.margins: 16

View file

@ -21,8 +21,6 @@ import "../Components"
PageType { PageType {
id: root id: root
defaultActiveFocusItem: focusItem
property bool pageEnabled property bool pageEnabled
Component.onCompleted: { Component.onCompleted: {
@ -66,11 +64,6 @@ PageType {
} }
} }
Item {
id: focusItem
KeyNavigation.tab: backButton
}
ColumnLayout { ColumnLayout {
id: header id: header
@ -82,7 +75,6 @@ PageType {
BackButtonType { BackButtonType {
id: backButton id: backButton
KeyNavigation.tab: switcher
} }
RowLayout { RowLayout {
@ -103,10 +95,6 @@ PageType {
enabled: root.pageEnabled enabled: root.pageEnabled
KeyNavigation.tab: selector.enabled ?
selector :
searchField.textField
checked: AppSplitTunnelingModel.isTunnelingEnabled checked: AppSplitTunnelingModel.isTunnelingEnabled
onToggled: { onToggled: {
AppSplitTunnelingModel.toggleSplitTunneling(checked) AppSplitTunnelingModel.toggleSplitTunneling(checked)
@ -130,8 +118,6 @@ PageType {
enabled: Qt.platform.os === "android" && root.pageEnabled enabled: Qt.platform.os === "android" && root.pageEnabled
KeyNavigation.tab: searchField.textField
listView: ListViewWithRadioButtonType { listView: ListViewWithRadioButtonType {
rootWidth: root.width rootWidth: root.width
@ -141,7 +127,7 @@ PageType {
clickedFunction: function() { clickedFunction: function() {
selector.text = selectedText selector.text = selectedText
selector.close() selector.closeTriggered()
if (AppSplitTunnelingModel.routeMode !== root.routeModesModel[currentIndex].type) { if (AppSplitTunnelingModel.routeMode !== root.routeModesModel[currentIndex].type) {
AppSplitTunnelingModel.routeMode = root.routeModesModel[currentIndex].type AppSplitTunnelingModel.routeMode = root.routeModesModel[currentIndex].type
} }
@ -267,7 +253,6 @@ PageType {
textFieldPlaceholderText: qsTr("application name") textFieldPlaceholderText: qsTr("application name")
buttonImageSource: "qrc:/images/controls/plus.svg" buttonImageSource: "qrc:/images/controls/plus.svg"
Keys.onTabPressed: lastItemTabClicked(focusItem)
rightButtonClickedOnEnter: true rightButtonClickedOnEnter: true
clickedFunc: function() { clickedFunc: function() {
@ -281,7 +266,7 @@ PageType {
AppSplitTunnelingController.addApp(fileName) AppSplitTunnelingController.addApp(fileName)
} }
} else if (Qt.platform.os === "android"){ } else if (Qt.platform.os === "android"){
installedAppDrawer.open() installedAppDrawer.openTriggered()
} }
PageController.showBusyIndicator(false) PageController.showBusyIndicator(false)

View file

@ -14,19 +14,6 @@ import "../Components"
PageType { PageType {
id: root id: root
defaultActiveFocusItem: focusItem
Item {
id: focusItem
KeyNavigation.tab: backButton
onFocusChanged: {
if (focusItem.activeFocus) {
fl.contentY = 0
}
}
}
BackButtonType { BackButtonType {
id: backButton id: backButton
@ -34,8 +21,6 @@ PageType {
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 20 anchors.topMargin: 20
KeyNavigation.tab: GC.isMobile() ? switcher : switcherAutoStart
} }
FlickableType { FlickableType {
@ -77,8 +62,8 @@ PageType {
} }
} }
KeyNavigation.tab: Qt.platform.os === "android" && !SettingsController.isNotificationPermissionGranted ? // KeyNavigation.tab: Qt.platform.os === "android" && !SettingsController.isNotificationPermissionGranted ?
labelWithButtonNotification.rightButton : labelWithButtonLanguage.rightButton // labelWithButtonNotification.rightButton : labelWithButtonLanguage.rightButton
parentFlickable: fl parentFlickable: fl
} }
@ -95,7 +80,6 @@ PageType {
descriptionText: qsTr("Enable notifications to show the VPN state in the status bar") descriptionText: qsTr("Enable notifications to show the VPN state in the status bar")
rightImageSource: "qrc:/images/controls/chevron-right.svg" rightImageSource: "qrc:/images/controls/chevron-right.svg"
KeyNavigation.tab: labelWithButtonLanguage.rightButton
parentFlickable: fl parentFlickable: fl
clickedFunction: function() { clickedFunction: function() {
@ -117,7 +101,6 @@ PageType {
text: qsTr("Auto start") text: qsTr("Auto start")
descriptionText: qsTr("Launch the application every time the device is starts") descriptionText: qsTr("Launch the application every time the device is starts")
KeyNavigation.tab: switcherAutoConnect
parentFlickable: fl parentFlickable: fl
checked: SettingsController.isAutoStartEnabled() checked: SettingsController.isAutoStartEnabled()
@ -142,7 +125,6 @@ PageType {
text: qsTr("Auto connect") text: qsTr("Auto connect")
descriptionText: qsTr("Connect to VPN on app start") descriptionText: qsTr("Connect to VPN on app start")
KeyNavigation.tab: switcherStartMinimized
parentFlickable: fl parentFlickable: fl
checked: SettingsController.isAutoConnectEnabled() checked: SettingsController.isAutoConnectEnabled()
@ -167,7 +149,6 @@ PageType {
text: qsTr("Start minimized") text: qsTr("Start minimized")
descriptionText: qsTr("Launch application minimized") descriptionText: qsTr("Launch application minimized")
KeyNavigation.tab: labelWithButtonLanguage.rightButton
parentFlickable: fl parentFlickable: fl
checked: SettingsController.isStartMinimizedEnabled() checked: SettingsController.isStartMinimizedEnabled()
@ -190,11 +171,10 @@ PageType {
descriptionText: LanguageModel.currentLanguageName descriptionText: LanguageModel.currentLanguageName
rightImageSource: "qrc:/images/controls/chevron-right.svg" rightImageSource: "qrc:/images/controls/chevron-right.svg"
KeyNavigation.tab: labelWithButtonLogging.rightButton
parentFlickable: fl parentFlickable: fl
clickedFunction: function() { clickedFunction: function() {
selectLanguageDrawer.open() selectLanguageDrawer.openTriggered()
} }
} }
@ -208,7 +188,6 @@ PageType {
descriptionText: SettingsController.isLoggingEnabled ? qsTr("Enabled") : qsTr("Disabled") descriptionText: SettingsController.isLoggingEnabled ? qsTr("Enabled") : qsTr("Disabled")
rightImageSource: "qrc:/images/controls/chevron-right.svg" rightImageSource: "qrc:/images/controls/chevron-right.svg"
KeyNavigation.tab: labelWithButtonReset.rightButton
parentFlickable: fl parentFlickable: fl
clickedFunction: function() { clickedFunction: function() {
@ -226,7 +205,6 @@ PageType {
rightImageSource: "qrc:/images/controls/chevron-right.svg" rightImageSource: "qrc:/images/controls/chevron-right.svg"
textColor: AmneziaStyle.color.vibrantRed textColor: AmneziaStyle.color.vibrantRed
Keys.onTabPressed: lastItemTabClicked()
parentFlickable: fl parentFlickable: fl
clickedFunction: function() { clickedFunction: function() {
@ -245,12 +223,12 @@ PageType {
} }
if (!GC.isMobile()) { if (!GC.isMobile()) {
root.defaultActiveFocusItem.forceActiveFocus() // root.defaultActiveFocusItem.forceActiveFocus()
} }
} }
var noButtonFunction = function() { var noButtonFunction = function() {
if (!GC.isMobile()) { if (!GC.isMobile()) {
root.defaultActiveFocusItem.forceActiveFocus() // root.defaultActiveFocusItem.forceActiveFocus()
} }
} }
@ -267,11 +245,5 @@ PageType {
width: root.width width: root.width
height: root.height height: root.height
onClosed: {
if (!GC.isMobile()) {
focusItem.forceActiveFocus()
}
}
} }
} }

View file

@ -17,8 +17,6 @@ import "../Controls2/TextTypes"
PageType { PageType {
id: root id: root
defaultActiveFocusItem: focusItem
Connections { Connections {
target: SettingsController target: SettingsController
@ -36,11 +34,6 @@ PageType {
} }
} }
Item {
id: focusItem
KeyNavigation.tab: backButton
}
BackButtonType { BackButtonType {
id: backButton id: backButton
@ -48,8 +41,6 @@ PageType {
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 20 anchors.topMargin: 20
KeyNavigation.tab: makeBackupButton
} }
FlickableType { FlickableType {
@ -93,6 +84,8 @@ PageType {
text: qsTr("Make a backup") text: qsTr("Make a backup")
parentFlickable: fl
clickedFunc: function() { clickedFunc: function() {
var fileName = "" var fileName = ""
if (GC.isMobile()) { if (GC.isMobile()) {
@ -111,8 +104,6 @@ PageType {
PageController.showNotificationMessage(qsTr("Backup file saved")) PageController.showNotificationMessage(qsTr("Backup file saved"))
} }
} }
KeyNavigation.tab: restoreBackupButton
} }
BasicButtonType { BasicButtonType {
@ -129,6 +120,8 @@ PageType {
text: qsTr("Restore from backup") text: qsTr("Restore from backup")
parentFlickable: fl
clickedFunc: function() { clickedFunc: function() {
var filePath = SystemController.getFileName(qsTr("Open backup file"), var filePath = SystemController.getFileName(qsTr("Open backup file"),
qsTr("Backup files (*.backup)")) qsTr("Backup files (*.backup)"))
@ -136,8 +129,6 @@ PageType {
restoreBackup(filePath) restoreBackup(filePath)
} }
} }
Keys.onTabPressed: lastItemTabClicked()
} }
} }
} }

View file

@ -12,15 +12,8 @@ import "../Config"
PageType { PageType {
id: root id: root
defaultActiveFocusItem: focusItem
property bool isAppSplitTinnelingEnabled: Qt.platform.os === "windows" || Qt.platform.os === "android" property bool isAppSplitTinnelingEnabled: Qt.platform.os === "windows" || Qt.platform.os === "android"
Item {
id: focusItem
KeyNavigation.tab: backButton
}
BackButtonType { BackButtonType {
id: backButton id: backButton
@ -28,8 +21,6 @@ PageType {
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 20 anchors.topMargin: 20
KeyNavigation.tab: amneziaDnsSwitch
} }
FlickableType { FlickableType {
@ -67,8 +58,6 @@ PageType {
SettingsController.toggleAmneziaDns(checked) SettingsController.toggleAmneziaDns(checked)
} }
} }
KeyNavigation.tab: dnsServersButton.rightButton
} }
DividerType {} DividerType {}
@ -81,11 +70,11 @@ PageType {
descriptionText: qsTr("When AmneziaDNS is not used or installed") descriptionText: qsTr("When AmneziaDNS is not used or installed")
rightImageSource: "qrc:/images/controls/chevron-right.svg" rightImageSource: "qrc:/images/controls/chevron-right.svg"
parentFlickable: fl
clickedFunction: function() { clickedFunction: function() {
PageController.goToPage(PageEnum.PageSettingsDns) PageController.goToPage(PageEnum.PageSettingsDns)
} }
KeyNavigation.tab: splitTunnelingButton.rightButton
} }
DividerType {} DividerType {}
@ -98,19 +87,11 @@ PageType {
descriptionText: qsTr("Allows you to select which sites you want to access through the VPN") descriptionText: qsTr("Allows you to select which sites you want to access through the VPN")
rightImageSource: "qrc:/images/controls/chevron-right.svg" rightImageSource: "qrc:/images/controls/chevron-right.svg"
parentFlickable: fl
clickedFunction: function() { clickedFunction: function() {
PageController.goToPage(PageEnum.PageSettingsSplitTunneling) PageController.goToPage(PageEnum.PageSettingsSplitTunneling)
} }
Keys.onTabPressed: {
if (splitTunnelingButton2.visible) {
return splitTunnelingButton2.rightButton.forceActiveFocus()
} else if (killSwitchSwitcher.visible) {
return killSwitchSwitcher.forceActiveFocus()
} else {
lastItemTabClicked()
}
}
} }
DividerType { DividerType {
@ -127,17 +108,11 @@ PageType {
descriptionText: qsTr("Allows you to use the VPN only for certain Apps") descriptionText: qsTr("Allows you to use the VPN only for certain Apps")
rightImageSource: "qrc:/images/controls/chevron-right.svg" rightImageSource: "qrc:/images/controls/chevron-right.svg"
parentFlickable: fl
clickedFunction: function() { clickedFunction: function() {
PageController.goToPage(PageEnum.PageSettingsAppSplitTunneling) PageController.goToPage(PageEnum.PageSettingsAppSplitTunneling)
} }
Keys.onTabPressed: {
if (killSwitchSwitcher.visible) {
return killSwitchSwitcher.forceActiveFocus()
} else {
lastItemTabClicked()
}
}
} }
DividerType { DividerType {
@ -154,6 +129,8 @@ PageType {
text: qsTr("KillSwitch") text: qsTr("KillSwitch")
descriptionText: qsTr("Disables your internet if your encrypted VPN connection drops out for any reason.") descriptionText: qsTr("Disables your internet if your encrypted VPN connection drops out for any reason.")
parentFlickable: fl
checked: SettingsController.isKillSwitchEnabled() checked: SettingsController.isKillSwitchEnabled()
checkable: !ConnectionController.isConnected checkable: !ConnectionController.isConnected
onCheckedChanged: { onCheckedChanged: {
@ -166,8 +143,6 @@ PageType {
PageController.showNotificationMessage(qsTr("Cannot change killSwitch settings during active connection")) PageController.showNotificationMessage(qsTr("Cannot change killSwitch settings during active connection"))
} }
} }
Keys.onTabPressed: lastItemTabClicked()
} }
DividerType { DividerType {

View file

@ -14,13 +14,6 @@ import "../Components"
PageType { PageType {
id: root id: root
defaultActiveFocusItem: primaryDns.textField
Item {
id: focusItem
KeyNavigation.tab: backButton
}
BackButtonType { BackButtonType {
id: backButton id: backButton
@ -28,8 +21,6 @@ PageType {
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 20 anchors.topMargin: 20
KeyNavigation.tab: root.defaultActiveFocusItem
} }
FlickableType { FlickableType {
@ -80,8 +71,6 @@ PageType {
textField.validator: RegularExpressionValidator { textField.validator: RegularExpressionValidator {
regularExpression: InstallController.ipAddressRegExp() regularExpression: InstallController.ipAddressRegExp()
} }
KeyNavigation.tab: secondaryDns.textField
} }
TextFieldWithHeaderType { TextFieldWithHeaderType {
@ -94,8 +83,6 @@ PageType {
textField.validator: RegularExpressionValidator { textField.validator: RegularExpressionValidator {
regularExpression: InstallController.ipAddressRegExp() regularExpression: InstallController.ipAddressRegExp()
} }
KeyNavigation.tab: restoreDefaultButton
} }
BasicButtonType { BasicButtonType {
@ -124,19 +111,17 @@ PageType {
PageController.showNotificationMessage(qsTr("Settings have been reset")) PageController.showNotificationMessage(qsTr("Settings have been reset"))
if (!GC.isMobile()) { if (!GC.isMobile()) {
defaultActiveFocusItem.forceActiveFocus() // defaultActiveFocusItem.forceActiveFocus()
} }
} }
var noButtonFunction = function() { var noButtonFunction = function() {
if (!GC.isMobile()) { if (!GC.isMobile()) {
defaultActiveFocusItem.forceActiveFocus() // defaultActiveFocusItem.forceActiveFocus()
} }
} }
showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
} }
KeyNavigation.tab: saveButton
} }
BasicButtonType { BasicButtonType {
@ -155,8 +140,6 @@ PageType {
} }
PageController.showNotificationMessage(qsTr("Settings saved")) PageController.showNotificationMessage(qsTr("Settings saved"))
} }
Keys.onTabPressed: lastItemTabClicked(focusItem)
} }
} }
} }

View file

@ -16,13 +16,6 @@ import "../Controls2/TextTypes"
PageType { PageType {
id: root id: root
defaultActiveFocusItem: focusItem
Item {
id: focusItem
KeyNavigation.tab: backButton
}
BackButtonType { BackButtonType {
id: backButton id: backButton
@ -30,140 +23,18 @@ PageType {
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 20 anchors.topMargin: 20
KeyNavigation.tab: switcher
} }
FlickableType { QtObject {
id: fl id: clientLogs
anchors.top: backButton.bottom
anchors.bottom: parent.bottom
contentHeight: content.height
ColumnLayout { property string title: qsTr("Client logs")
id: content property string description: qsTr("AmneziaVPN logs")
property bool isVisible: true
anchors.top: parent.top property var openLogsHandler: function() {
anchors.left: parent.left
anchors.right: parent.right
spacing: 0
HeaderType {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("Logging")
descriptionText: qsTr("Enabling this function will save application's logs automatically. " +
"By default, logging functionality is disabled. Enable log saving in case of application malfunction.")
}
SwitcherType {
id: switcher
Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("Enable logs")
checked: SettingsController.isLoggingEnabled
//KeyNavigation.tab: openFolderButton
onCheckedChanged: {
if (checked !== SettingsController.isLoggingEnabled) {
SettingsController.isLoggingEnabled = checked
}
}
}
DividerType {}
LabelWithButtonType {
// id: labelWithButton2
Layout.fillWidth: true
Layout.topMargin: -8
text: qsTr("Clear logs")
leftImageSource: "qrc:/images/controls/trash.svg"
isSmallLeftImage: true
// KeyNavigation.tab: labelWithButton3
clickedFunction: function() {
var headerText = qsTr("Clear logs?")
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() {
PageController.showBusyIndicator(true)
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)
}
}
ListItemTitleType {
Layout.fillWidth: true
Layout.topMargin: 8
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("Client logs")
}
ParagraphTextType {
Layout.fillWidth: true
Layout.topMargin: 8
Layout.leftMargin: 16
Layout.rightMargin: 16
color: AmneziaStyle.color.mutedGray
text: qsTr("AmneziaVPN logs")
}
LabelWithButtonType {
// id: labelWithButton2
Layout.fillWidth: true
Layout.topMargin: -8
Layout.bottomMargin: -8
text: qsTr("Open logs folder")
leftImageSource: "qrc:/images/controls/folder-open.svg"
isSmallLeftImage: true
// KeyNavigation.tab: labelWithButton3
clickedFunction: function() {
SettingsController.openLogsFolder() SettingsController.openLogsFolder()
} }
} property var exportLogsHandler: function() {
DividerType {}
LabelWithButtonType {
// id: labelWithButton2
Layout.fillWidth: true
Layout.topMargin: -8
Layout.bottomMargin: -8
text: qsTr("Export logs")
leftImageSource: "qrc:/images/controls/save.svg"
isSmallLeftImage: true
// KeyNavigation.tab: labelWithButton3
clickedFunction: function() {
var fileName = "" var fileName = ""
if (GC.isMobile()) { if (GC.isMobile()) {
fileName = "AmneziaVPN.log" fileName = "AmneziaVPN.log"
@ -183,71 +54,16 @@ PageType {
} }
} }
DividerType {} QtObject {
id: serviceLogs
ListItemTitleType { property string title: qsTr("Service logs")
visible: !GC.isMobile() property string description: qsTr("AmneziaVPN-service logs")
property bool isVisible: !GC.isMobile()
Layout.fillWidth: true property var openLogsHandler: function() {
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
// KeyNavigation.tab: labelWithButton3
clickedFunction: function() {
SettingsController.openServiceLogsFolder() SettingsController.openServiceLogsFolder()
} }
} property var exportLogsHandler: function() {
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
// KeyNavigation.tab: labelWithButton3
clickedFunction: function() {
var fileName = "" var fileName = ""
if (GC.isMobile()) { if (GC.isMobile()) {
fileName = "AmneziaVPN-service.log" fileName = "AmneziaVPN-service.log"
@ -267,9 +83,180 @@ PageType {
} }
} }
DividerType { property list<QtObject> logTypes: [
visible: !GC.isMobile() clientLogs,
serviceLogs
]
ListView {
id: listView
anchors.top: backButton.bottom
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.left: parent.left
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
spacing: 0
HeaderType {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
headerText: qsTr("Logging")
descriptionText: qsTr("Enabling this function will save application's logs automatically. " +
"By default, logging functionality is disabled. Enable log saving in case of application malfunction.")
}
SwitcherType {
id: switcher
Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("Enable logs")
checked: SettingsController.isLoggingEnabled
onCheckedChanged: {
if (checked !== SettingsController.isLoggingEnabled) {
SettingsController.isLoggingEnabled = checked
}
}
}
DividerType {}
LabelWithButtonType {
Layout.fillWidth: true
Layout.topMargin: -8
text: qsTr("Clear logs")
leftImageSource: "qrc:/images/controls/trash.svg"
isSmallLeftImage: true
clickedFunction: function() {
var headerText = qsTr("Clear logs?")
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() {
PageController.showBusyIndicator(true)
SettingsController.clearLogs()
PageController.showBusyIndicator(false)
PageController.showNotificationMessage(qsTr("Logs have been cleaned up"))
}
var noButtonFunction = function() {
}
showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
}
}
}
delegate: ColumnLayout {
id: delegateContent
width: listView.width
spacing: 0
visible: isVisible
ListItemTitleType {
Layout.fillWidth: true
Layout.topMargin: 8
Layout.leftMargin: 16
Layout.rightMargin: 16
text: title
}
ParagraphTextType {
Layout.fillWidth: true
Layout.topMargin: 8
Layout.leftMargin: 16
Layout.rightMargin: 16
color: AmneziaStyle.color.mutedGray
text: description
}
LabelWithButtonType {
Layout.fillWidth: true
Layout.topMargin: -8
Layout.bottomMargin: -8
text: qsTr("Open logs folder")
leftImageSource: "qrc:/images/controls/folder-open.svg"
isSmallLeftImage: true
clickedFunction: openLogsHandler
}
DividerType {}
LabelWithButtonType {
Layout.fillWidth: true
Layout.topMargin: -8
Layout.bottomMargin: -8
text: qsTr("Export logs")
leftImageSource: "qrc:/images/controls/save.svg"
isSmallLeftImage: true
clickedFunction: exportLogsHandler
}
DividerType {}
} }
} }
} }

View file

@ -100,8 +100,6 @@ PageType {
text: qsTr("Check the server for previously installed Amnezia services") text: qsTr("Check the server for previously installed Amnezia services")
descriptionText: qsTr("Add them to the application if they were not displayed") descriptionText: qsTr("Add them to the application if they were not displayed")
KeyNavigation.tab: labelWithButton2
clickedFunction: function() { clickedFunction: function() {
PageController.showBusyIndicator(true) PageController.showBusyIndicator(true)
InstallController.scanServerForInstalledContainers() InstallController.scanServerForInstalledContainers()
@ -121,8 +119,6 @@ PageType {
text: qsTr("Reboot server") text: qsTr("Reboot server")
textColor: AmneziaStyle.color.vibrantRed textColor: AmneziaStyle.color.vibrantRed
KeyNavigation.tab: labelWithButton3
clickedFunction: function() { clickedFunction: function() {
var headerText = qsTr("Do you want to reboot the server?") 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?") var descriptionText = qsTr("The reboot process may take approximately 30 seconds. Are you sure you wish to proceed?")
@ -162,16 +158,6 @@ PageType {
text: qsTr("Remove server from application") text: qsTr("Remove server from application")
textColor: AmneziaStyle.color.vibrantRed textColor: AmneziaStyle.color.vibrantRed
Keys.onTabPressed: {
if (content.isServerWithWriteAccess) {
labelWithButton4.forceActiveFocus()
} else {
labelWithButton5.visible ?
labelWithButton5.forceActiveFocus() :
lastItemTabClickedSignal()
}
}
clickedFunction: function() { clickedFunction: function() {
var headerText = qsTr("Do you want to remove the server from application?") 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.") var descriptionText = qsTr("All installed AmneziaVPN services will still remain on the server.")
@ -210,10 +196,6 @@ PageType {
text: qsTr("Clear server from Amnezia software") text: qsTr("Clear server from Amnezia software")
textColor: AmneziaStyle.color.vibrantRed textColor: AmneziaStyle.color.vibrantRed
Keys.onTabPressed: labelWithButton5.visible ?
labelWithButton5.forceActiveFocus() :
root.lastItemTabClickedSignal()
clickedFunction: function() { clickedFunction: function() {
var headerText = qsTr("Do you want to clear server from Amnezia software?") 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.") var descriptionText = qsTr("All users whom you shared a connection with will no longer be able to connect to it.")
@ -253,8 +235,6 @@ PageType {
text: qsTr("Reset API config") text: qsTr("Reset API config")
textColor: AmneziaStyle.color.vibrantRed textColor: AmneziaStyle.color.vibrantRed
Keys.onTabPressed: root.lastItemTabClickedSignal()
clickedFunction: function() { clickedFunction: function() {
var headerText = qsTr("Do you want to reset API config?") var headerText = qsTr("Do you want to reset API config?")
var descriptionText = "" var descriptionText = ""

View file

@ -19,24 +19,26 @@ import "../Components"
PageType { PageType {
id: root id: root
property int pageSettingsServerProtocols: 0 readonly property int pageSettingsServerProtocols: 0
property int pageSettingsServerServices: 1 readonly property int pageSettingsServerServices: 1
property int pageSettingsServerData: 2 readonly property int pageSettingsServerData: 2
property int pageSettingsApiServerInfo: 3 readonly property int pageSettingsApiServerInfo: 3
property int pageSettingsApiLanguageList: 4 readonly property int pageSettingsApiLanguageList: 4
defaultActiveFocusItem: focusItem property var server
Connections { Connections {
target: PageController target: PageController
function onGoToPageSettingsServerServices() { function onGoToPageSettingsServerServices() {
tabBar.currentIndex = root.pageSettingsServerServices tabBar.setCurrentIndex(root.pageSettingsServerServices)
} }
} }
SortFilterProxyModel { SortFilterProxyModel {
id: proxyServersModel id: proxyServersModel
objectName: "proxyServersModel"
sourceModel: ServersModel sourceModel: ServersModel
filters: [ filters: [
ValueFilter { ValueFilter {
@ -44,42 +46,27 @@ PageType {
value: true value: true
} }
] ]
}
Item { Component.onCompleted: {
id: focusItem root.server = proxyServersModel.get(0)
KeyNavigation.tab: header }
} }
ColumnLayout { ColumnLayout {
objectName: "mainLayout"
anchors.fill: parent anchors.fill: parent
anchors.topMargin: 20
spacing: 16 spacing: 4
Repeater {
id: header
model: proxyServersModel
activeFocusOnTab: true
onFocusChanged: {
header.itemAt(0).focusItem.forceActiveFocus()
}
delegate: ColumnLayout {
property alias focusItem: backButton
id: content
Layout.topMargin: 20
BackButtonType { BackButtonType {
id: backButton id: backButton
KeyNavigation.tab: headerContent.actionButton objectName: "backButton"
backButtonFunction: function() { backButtonFunction: function() {
if (nestedStackView.currentIndex === root.pageSettingsApiServerInfo && if (nestedStackView.currentIndex === root.pageSettingsApiServerInfo
ServersModel.getProcessedServerData("isCountrySelectionAvailable")) { && ServersModel.getProcessedServerData("isCountrySelectionAvailable")) {
nestedStackView.currentIndex = root.pageSettingsApiLanguageList nestedStackView.currentIndex = root.pageSettingsApiLanguageList
} else { } else {
PageController.closePage() PageController.closePage()
@ -89,51 +76,47 @@ PageType {
HeaderType { HeaderType {
id: headerContent id: headerContent
objectName: "headerContent"
Layout.fillWidth: true Layout.fillWidth: true
Layout.leftMargin: 16 Layout.leftMargin: 16
Layout.rightMargin: 16 Layout.rightMargin: 16
Layout.bottomMargin: 10
actionButtonImage: nestedStackView.currentIndex === root.pageSettingsApiLanguageList ? "qrc:/images/controls/settings.svg" : "qrc:/images/controls/edit-3.svg" actionButtonImage: nestedStackView.currentIndex === root.pageSettingsApiLanguageList ? "qrc:/images/controls/settings.svg" : "qrc:/images/controls/edit-3.svg"
headerText: name headerText: root.server.name
descriptionText: { descriptionText: {
if (ServersModel.getProcessedServerData("isServerFromGatewayApi")) { if (ServersModel.getProcessedServerData("isServerFromGatewayApi")) {
return ApiServicesModel.getSelectedServiceData("serviceDescription") return ApiServicesModel.getSelectedServiceData("serviceDescription")
} else if (ServersModel.getProcessedServerData("isServerFromTelegramApi")) { } else if (ServersModel.getProcessedServerData("isServerFromTelegramApi")) {
return serverDescription return root.server.serverDescription
} else if (ServersModel.isProcessedServerHasWriteAccess()) { } else if (ServersModel.isProcessedServerHasWriteAccess()) {
return credentialsLogin + " · " + hostName return root.server.credentialsLogin + " · " + root.server.hostName
} else { } else {
return hostName return root.server.hostName
} }
} }
KeyNavigation.tab: tabBar
actionButtonFunction: function() { actionButtonFunction: function() {
if (nestedStackView.currentIndex === root.pageSettingsApiLanguageList) { if (nestedStackView.currentIndex === root.pageSettingsApiLanguageList) {
nestedStackView.currentIndex = root.pageSettingsApiServerInfo nestedStackView.currentIndex = root.pageSettingsApiServerInfo
} else { } else {
serverNameEditDrawer.open() serverNameEditDrawer.openTriggered()
} }
} }
} }
DrawerType2 { DrawerType2 {
id: serverNameEditDrawer id: serverNameEditDrawer
objectName: "serverNameEditDrawer"
parent: root parent: root
anchors.fill: parent anchors.fill: parent
expandedHeight: root.height * 0.35 expandedHeight: root.height * 0.35
onClosed: { expandedStateContent: ColumnLayout {
if (!GC.isMobile()) {
headerContent.actionButton.forceActiveFocus()
}
}
expandedContent: ColumnLayout {
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
@ -141,29 +124,14 @@ PageType {
anchors.leftMargin: 16 anchors.leftMargin: 16
anchors.rightMargin: 16 anchors.rightMargin: 16
Connections {
target: serverNameEditDrawer
enabled: !GC.isMobile()
function onOpened() {
serverName.textField.forceActiveFocus()
}
}
Item {
id: focusItem1
KeyNavigation.tab: serverName.textField
}
TextFieldWithHeaderType { TextFieldWithHeaderType {
id: serverName id: serverName
Layout.fillWidth: true Layout.fillWidth: true
headerText: qsTr("Server name") headerText: qsTr("Server name")
textFieldText: name textFieldText: root.server.name
textField.maximumLength: 30 textField.maximumLength: 30
checkEmptyText: true checkEmptyText: true
KeyNavigation.tab: saveButton
} }
BasicButtonType { BasicButtonType {
@ -172,19 +140,17 @@ PageType {
Layout.fillWidth: true Layout.fillWidth: true
text: qsTr("Save") text: qsTr("Save")
KeyNavigation.tab: focusItem1
clickedFunc: function() { clickedFunc: function() {
if (serverName.textFieldText === "") { if (serverName.textFieldText === "") {
return return
} }
if (serverName.textFieldText !== name) { if (serverName.textFieldText !== root.server.name) {
name = serverName.textFieldText ServersModel.setProcessedServerData("name", serverName.textFieldText);
} root.server = proxyServersModel.get(0);
serverNameEditDrawer.close()
}
} }
serverNameEditDrawer.closeTriggered()
} }
} }
} }
@ -205,35 +171,27 @@ PageType {
visible: !ServersModel.getProcessedServerData("isServerFromGatewayApi") visible: !ServersModel.getProcessedServerData("isServerFromGatewayApi")
activeFocusOnTab: true
onFocusChanged: {
if (activeFocus) {
protocolsTab.forceActiveFocus()
}
}
TabButtonType { TabButtonType {
id: protocolsTab id: protocolsTab
visible: protocolsPage.installedProtocolsCount visible: protocolsPage.installedProtocolsCount
width: protocolsPage.installedProtocolsCount ? undefined : 0 width: protocolsPage.installedProtocolsCount ? undefined : 0
isSelected: tabBar.currentIndex === root.pageSettingsServerProtocols isSelected: TabBar.tabBar.currentIndex === root.pageSettingsServerProtocols
text: qsTr("Protocols") text: qsTr("Protocols")
KeyNavigation.tab: servicesTab Keys.onReturnPressed: TabBar.tabBar.setCurrentIndex(root.pageSettingsServerProtocols)
Keys.onReturnPressed: tabBar.currentIndex = root.pageSettingsServerProtocols Keys.onEnterPressed: TabBar.tabBar.setCurrentIndex(root.pageSettingsServerProtocols)
Keys.onEnterPressed: tabBar.currentIndex = root.pageSettingsServerProtocols
} }
TabButtonType { TabButtonType {
id: servicesTab id: servicesTab
visible: servicesPage.installedServicesCount visible: servicesPage.installedServicesCount
width: servicesPage.installedServicesCount ? undefined : 0 width: servicesPage.installedServicesCount ? undefined : 0
isSelected: tabBar.currentIndex === root.pageSettingsServerServices isSelected: TabBar.tabBar.currentIndex === root.pageSettingsServerServices
text: qsTr("Services") text: qsTr("Services")
KeyNavigation.tab: dataTab Keys.onReturnPressed: TabBar.tabBar.setCurrentIndex(root.pageSettingsServerServices)
Keys.onReturnPressed: tabBar.currentIndex = root.pageSettingsServerServices Keys.onEnterPressed: TabBar.tabBar.setCurrentIndex(root.pageSettingsServerServices)
Keys.onEnterPressed: tabBar.currentIndex = root.pageSettingsServerServices
} }
TabButtonType { TabButtonType {
@ -241,24 +199,15 @@ PageType {
isSelected: tabBar.currentIndex === root.pageSettingsServerData isSelected: tabBar.currentIndex === root.pageSettingsServerData
text: qsTr("Management") text: qsTr("Management")
Keys.onReturnPressed: tabBar.currentIndex = root.pageSettingsServerData Keys.onReturnPressed: TabBar.tabBar.setCurrentIndex(root.pageSettingsServerData)
Keys.onEnterPressed: tabBar.currentIndex = root.pageSettingsServerData Keys.onEnterPressed: TabBar.tabBar.setCurrentIndex(root.pageSettingsServerData)
Keys.onTabPressed: function() {
if (nestedStackView.currentIndex === root.pageSettingsServerProtocols) {
return protocolsPage
} else if (nestedStackView.currentIndex === root.pageSettingsServerProtocols) {
return servicesPage
} else {
return dataPage
}
}
} }
} }
StackLayout { StackLayout {
id: nestedStackView id: nestedStackView
Layout.preferredWidth: root.width
Layout.preferredHeight: root.height - tabBar.implicitHeight - header.implicitHeight Layout.fillWidth: true
currentIndex: ServersModel.getProcessedServerData("isServerFromGatewayApi") ? currentIndex: ServersModel.getProcessedServerData("isServerFromGatewayApi") ?
(ServersModel.getProcessedServerData("isCountrySelectionAvailable") ? (ServersModel.getProcessedServerData("isCountrySelectionAvailable") ?
@ -267,36 +216,26 @@ PageType {
PageSettingsServerProtocols { PageSettingsServerProtocols {
id: protocolsPage id: protocolsPage
stackView: root.stackView stackView: root.stackView
onLastItemTabClickedSignal: lastItemTabClicked(focusItem)
} }
PageSettingsServerServices { PageSettingsServerServices {
id: servicesPage id: servicesPage
stackView: root.stackView stackView: root.stackView
onLastItemTabClickedSignal: lastItemTabClicked(focusItem)
} }
PageSettingsServerData { PageSettingsServerData {
id: dataPage id: dataPage
stackView: root.stackView stackView: root.stackView
onLastItemTabClickedSignal: lastItemTabClicked(focusItem)
} }
PageSettingsApiServerInfo { PageSettingsApiServerInfo {
id: apiInfoPage id: apiInfoPage
stackView: root.stackView stackView: root.stackView
// onLastItemTabClickedSignal: lastItemTabClicked(focusItem)
} }
PageSettingsApiLanguageList { PageSettingsApiLanguageList {
id: apiLanguageListPage id: apiLanguageListPage
stackView: root.stackView stackView: root.stackView
// onLastItemTabClickedSignal: lastItemTabClicked(focusItem)
} }
} }

View file

@ -21,13 +21,6 @@ PageType {
property bool isClearCacheVisible: ServersModel.isProcessedServerHasWriteAccess() && !ContainersModel.isServiceContainer(ContainersModel.getProcessedContainerIndex()) property bool isClearCacheVisible: ServersModel.isProcessedServerHasWriteAccess() && !ContainersModel.isServiceContainer(ContainersModel.getProcessedContainerIndex())
defaultActiveFocusItem: focusItem
Item {
id: focusItem
KeyNavigation.tab: backButton
}
ColumnLayout { ColumnLayout {
id: header id: header
@ -39,7 +32,6 @@ PageType {
BackButtonType { BackButtonType {
id: backButton id: backButton
KeyNavigation.tab: protocols
} }
HeaderType { HeaderType {
@ -57,30 +49,36 @@ PageType {
height: protocols.contentItem.height height: protocols.contentItem.height
clip: true clip: true
interactive: 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: { Keys.onTabPressed: {
if (currentFocusIndex < this.count - 1) { FocusController.nextKeyTabItem()
currentFocusIndex += 1
protocols.itemAtIndex(currentFocusIndex).focusItem.forceActiveFocus()
} else {
clearCacheButton.forceActiveFocus()
} }
Keys.onBacktabPressed: {
FocusController.previousKeyTabItem()
} }
Keys.onUpPressed: {
FocusController.nextKeyUpItem()
}
Keys.onDownPressed: {
FocusController.nextKeyDownItem()
}
Keys.onLeftPressed: {
FocusController.nextKeyLeftItem()
}
Keys.onRightPressed: {
FocusController.nextKeyRightItem()
}
model: ProtocolsModel
delegate: Item { delegate: Item {
property var focusItem: clientSettings.rightButton
implicitWidth: protocols.width implicitWidth: protocols.width
implicitHeight: delegateContent.implicitHeight implicitHeight: delegateContent.implicitHeight
@ -160,7 +158,9 @@ PageType {
} }
} }
} }
}
footer: ColumnLayout {
width: header.width
LabelWithButtonType { LabelWithButtonType {
id: clearCacheButton id: clearCacheButton
@ -168,7 +168,6 @@ PageType {
Layout.fillWidth: true Layout.fillWidth: true
visible: root.isClearCacheVisible visible: root.isClearCacheVisible
KeyNavigation.tab: removeButton
text: qsTr("Clear profile") text: qsTr("Clear profile")
@ -190,9 +189,9 @@ PageType {
PageController.showBusyIndicator(false) PageController.showBusyIndicator(false)
} }
var noButtonFunction = function() { var noButtonFunction = function() {
if (!GC.isMobile()) { // if (!GC.isMobile()) {
focusItem.forceActiveFocus() // focusItem.forceActiveFocus()
} // }
} }
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
@ -219,7 +218,6 @@ PageType {
Layout.fillWidth: true Layout.fillWidth: true
visible: ServersModel.isProcessedServerHasWriteAccess() visible: ServersModel.isProcessedServerHasWriteAccess()
Keys.onTabPressed: lastItemTabClicked(focusItem)
text: qsTr("Remove ") text: qsTr("Remove ")
textColor: AmneziaStyle.color.vibrantRed textColor: AmneziaStyle.color.vibrantRed
@ -264,5 +262,8 @@ PageType {
visible: ServersModel.isProcessedServerHasWriteAccess() visible: ServersModel.isProcessedServerHasWriteAccess()
} }
} }
}
}
} }

View file

@ -21,25 +21,15 @@ PageType {
property var installedProtocolsCount property var installedProtocolsCount
onFocusChanged: settingsContainersListView.forceActiveFocus() function resetView() {
signal lastItemTabClickedSignal() settingsContainersListView.positionViewAtBeginning()
}
FlickableType {
id: fl
anchors.top: parent.top
anchors.bottom: parent.bottom
contentHeight: content.implicitHeight
Column {
id: content
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
SettingsContainersListView { SettingsContainersListView {
id: settingsContainersListView id: settingsContainersListView
anchors.fill: parent
Connections { Connections {
target: ServersModel target: ServersModel
@ -66,8 +56,10 @@ PageType {
] ]
} }
Component.onCompleted: updateContainersModelFilters() Component.onCompleted: {
} settingsContainersListView.isFocusable = true
settingsContainersListView.interactive = true
updateContainersModelFilters()
} }
} }
} }

View file

@ -21,25 +21,11 @@ PageType {
property var installedServicesCount property var installedServicesCount
onFocusChanged: settingsContainersListView.forceActiveFocus()
signal lastItemTabClickedSignal()
FlickableType {
id: fl
anchors.top: parent.top
anchors.bottom: parent.bottom
contentHeight: content.implicitHeight
Column {
id: content
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
SettingsContainersListView { SettingsContainersListView {
id: settingsContainersListView id: settingsContainersListView
anchors.fill: parent
Connections { Connections {
target: ServersModel target: ServersModel
@ -65,8 +51,10 @@ PageType {
] ]
} }
Component.onCompleted: updateContainersModelFilters() Component.onCompleted: {
} settingsContainersListView.isFocusable = true
settingsContainersListView.interactive = true
updateContainersModelFilters()
} }
} }
} }

View file

@ -18,13 +18,6 @@ import "../Components"
PageType { PageType {
id: root id: root
defaultActiveFocusItem: focusItem
Item {
id: focusItem
KeyNavigation.tab: backButton
}
ColumnLayout { ColumnLayout {
id: header id: header
@ -36,7 +29,6 @@ PageType {
BackButtonType { BackButtonType {
id: backButton id: backButton
KeyNavigation.tab: servers
} }
HeaderType { HeaderType {
@ -48,42 +40,25 @@ PageType {
} }
} }
FlickableType { ListView {
id: fl id: servers
objectName: "servers"
width: parent.width
anchors.top: header.bottom anchors.top: header.bottom
anchors.topMargin: 16 anchors.topMargin: 16
contentHeight: col.implicitHeight
Column {
id: col
anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
ListView { height: 500 // servers.contentItem.height // TODO: calculate height
id: servers
width: parent.width property bool isFocusable: true
height: servers.contentItem.height
model: ServersModel model: ServersModel
clip: true clip: true
interactive: false 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: { onVisibleChanged: {
if (visible) { if (visible) {
currentIndex = 0 currentIndex = 0
@ -94,12 +69,6 @@ PageType {
implicitWidth: servers.width implicitWidth: servers.width
implicitHeight: delegateContent.implicitHeight implicitHeight: delegateContent.implicitHeight
onFocusChanged: {
if (focus) {
server.rightButton.forceActiveFocus()
}
}
ColumnLayout { ColumnLayout {
id: delegateContent id: delegateContent
@ -112,7 +81,7 @@ PageType {
Layout.fillWidth: true Layout.fillWidth: true
text: name text: name
parentFlickable: fl
descriptionText: { descriptionText: {
var servicesNameString = "" var servicesNameString = ""
var servicesName = ServersModel.getAllInstalledServicesName(index) var servicesName = ServersModel.getAllInstalledServicesName(index)
@ -138,6 +107,4 @@ PageType {
} }
} }
} }
}
}
} }

View file

@ -23,13 +23,6 @@ PageType {
property var isServerFromTelegramApi: ServersModel.getDefaultServerData("isServerFromTelegramApi") property var isServerFromTelegramApi: ServersModel.getDefaultServerData("isServerFromTelegramApi")
defaultActiveFocusItem: searchField.textField
Item {
id: focusItem
KeyNavigation.tab: backButton
}
property bool pageEnabled property bool pageEnabled
Component.onCompleted: { Component.onCompleted: {
@ -99,7 +92,6 @@ PageType {
BackButtonType { BackButtonType {
id: backButton id: backButton
KeyNavigation.tab: switcher
} }
RowLayout { RowLayout {
@ -129,8 +121,6 @@ PageType {
onToggled: { onToggledFunc() } onToggled: { onToggledFunc() }
Keys.onEnterPressed: { onToggledFunc() } Keys.onEnterPressed: { onToggledFunc() }
Keys.onReturnPressed: { onToggledFunc() } Keys.onReturnPressed: { onToggledFunc() }
KeyNavigation.tab: selector
} }
} }
@ -158,7 +148,7 @@ PageType {
clickedFunction: function() { clickedFunction: function() {
selector.text = selectedText selector.text = selectedText
selector.close() selector.closeTriggered()
if (SitesModel.routeMode !== root.routeModesModel[currentIndex].type) { if (SitesModel.routeMode !== root.routeModesModel[currentIndex].type) {
SitesModel.routeMode = root.routeModesModel[currentIndex].type SitesModel.routeMode = root.routeModesModel[currentIndex].type
} }
@ -179,33 +169,45 @@ PageType {
} }
} }
} }
KeyNavigation.tab: {
return sites.count > 0 ?
sites :
searchField.textField
} }
} }
}
FlickableType {
id: fl
anchors.top: header.bottom
anchors.topMargin: 16
contentHeight: col.implicitHeight + addSiteButton.implicitHeight + addSiteButton.anchors.bottomMargin + addSiteButton.anchors.topMargin
enabled: root.pageEnabled
Column {
id: col
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
ListView { ListView {
id: sites id: sites
anchors.top: header.bottom
anchors.topMargin: 16
width: parent.width width: parent.width
height: sites.contentItem.height
height: 200 // TODO: Change to correct height
enabled: root.pageEnabled
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 { model: SortFilterProxyModel {
id: proxySitesModel id: proxySitesModel
@ -227,30 +229,16 @@ PageType {
} }
clip: true 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 { delegate: Item {
implicitWidth: sites.width implicitWidth: sites.width
implicitHeight: delegateContent.implicitHeight implicitHeight: delegateContent.implicitHeight
onActiveFocusChanged: { // onActiveFocusChanged: {
if (activeFocus) { // if (activeFocus) {
site.rightButton.forceActiveFocus() // site.rightButton.forceActiveFocus()
} // }
} // }
ColumnLayout { ColumnLayout {
id: delegateContent id: delegateContent
@ -294,8 +282,6 @@ PageType {
} }
} }
}
}
Rectangle { Rectangle {
anchors.fill: addSiteButton anchors.fill: addSiteButton
@ -325,7 +311,6 @@ PageType {
textFieldPlaceholderText: qsTr("website or IP") textFieldPlaceholderText: qsTr("website or IP")
buttonImageSource: "qrc:/images/controls/plus.svg" buttonImageSource: "qrc:/images/controls/plus.svg"
KeyNavigation.tab: GC.isMobile() ? focusItem : addSiteButtonImage
clickedFunc: function() { clickedFunc: function() {
PageController.showBusyIndicator(true) PageController.showBusyIndicator(true)
@ -344,13 +329,11 @@ PageType {
imageColor: AmneziaStyle.color.paleGray imageColor: AmneziaStyle.color.paleGray
onClicked: function () { onClicked: function () {
moreActionsDrawer.open() moreActionsDrawer.openTriggered()
} }
Keys.onReturnPressed: addSiteButtonImage.clicked() Keys.onReturnPressed: addSiteButtonImage.clicked()
Keys.onEnterPressed: addSiteButtonImage.clicked() Keys.onEnterPressed: addSiteButtonImage.clicked()
Keys.onTabPressed: lastItemTabClicked(focusItem)
} }
} }
@ -360,38 +343,13 @@ PageType {
anchors.fill: parent anchors.fill: parent
expandedHeight: parent.height * 0.4375 expandedHeight: parent.height * 0.4375
onClosed: { expandedStateContent: ColumnLayout {
if (root.defaultActiveFocusItem && !GC.isMobile()) {
root.defaultActiveFocusItem.forceActiveFocus()
}
}
expandedContent: ColumnLayout {
id: moreActionsDrawerContent id: moreActionsDrawerContent
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
Connections {
target: moreActionsDrawer
function onOpened() {
focusItem1.forceActiveFocus()
}
function onActiveFocusChanged() {
if (!GC.isMobile()) {
focusItem1.forceActiveFocus()
}
}
}
Item {
id: focusItem1
KeyNavigation.tab: importSitesButton.rightButton
}
Header2Type { Header2Type {
Layout.fillWidth: true Layout.fillWidth: true
Layout.margins: 16 Layout.margins: 16
@ -407,10 +365,8 @@ PageType {
rightImageSource: "qrc:/images/controls/chevron-right.svg" rightImageSource: "qrc:/images/controls/chevron-right.svg"
clickedFunction: function() { clickedFunction: function() {
importSitesDrawer.open() importSitesDrawer.openTriggered()
} }
KeyNavigation.tab: exportSitesButton
} }
DividerType {} DividerType {}
@ -420,8 +376,6 @@ PageType {
Layout.fillWidth: true Layout.fillWidth: true
text: qsTr("Save site list") text: qsTr("Save site list")
KeyNavigation.tab: focusItem1
clickedFunction: function() { clickedFunction: function() {
var fileName = "" var fileName = ""
if (GC.isMobile()) { if (GC.isMobile()) {
@ -436,7 +390,7 @@ PageType {
if (fileName !== "") { if (fileName !== "") {
PageController.showBusyIndicator(true) PageController.showBusyIndicator(true)
SitesController.exportSites(fileName) SitesController.exportSites(fileName)
moreActionsDrawer.close() moreActionsDrawer.closeTriggered()
PageController.showBusyIndicator(false) PageController.showBusyIndicator(false)
} }
} }
@ -452,28 +406,9 @@ PageType {
anchors.fill: parent anchors.fill: parent
expandedHeight: parent.height * 0.4375 expandedHeight: parent.height * 0.4375
onClosed: { expandedStateContent: Item {
if (!GC.isMobile()) {
moreActionsDrawer.forceActiveFocus()
}
}
expandedContent: Item {
implicitHeight: importSitesDrawer.expandedHeight implicitHeight: importSitesDrawer.expandedHeight
Connections {
target: importSitesDrawer
enabled: !GC.isMobile()
function onOpened() {
focusItem2.forceActiveFocus()
}
}
Item {
id: focusItem2
KeyNavigation.tab: importSitesDrawerBackButton
}
BackButtonType { BackButtonType {
id: importSitesDrawerBackButton id: importSitesDrawerBackButton
@ -482,10 +417,8 @@ PageType {
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 16 anchors.topMargin: 16
KeyNavigation.tab: importSitesButton2
backButtonFunction: function() { backButtonFunction: function() {
importSitesDrawer.close() importSitesDrawer.closeTriggered()
} }
} }
@ -516,7 +449,6 @@ PageType {
Layout.fillWidth: true Layout.fillWidth: true
text: qsTr("Replace site list") text: qsTr("Replace site list")
KeyNavigation.tab: importSitesButton3
clickedFunction: function() { clickedFunction: function() {
var fileName = SystemController.getFileName(qsTr("Open sites file"), var fileName = SystemController.getFileName(qsTr("Open sites file"),
@ -533,7 +465,6 @@ PageType {
id: importSitesButton3 id: importSitesButton3
Layout.fillWidth: true Layout.fillWidth: true
text: qsTr("Add imported sites to existing ones") text: qsTr("Add imported sites to existing ones")
KeyNavigation.tab: focusItem2
clickedFunction: function() { clickedFunction: function() {
var fileName = SystemController.getFileName(qsTr("Open sites file"), var fileName = SystemController.getFileName(qsTr("Open sites file"),
@ -548,8 +479,8 @@ PageType {
PageController.showBusyIndicator(true) PageController.showBusyIndicator(true)
SitesController.importSites(fileName, replaceExistingSites) SitesController.importSites(fileName, replaceExistingSites)
PageController.showBusyIndicator(false) PageController.showBusyIndicator(false)
importSitesDrawer.close() importSitesDrawer.closeTriggered()
moreActionsDrawer.close() moreActionsDrawer.closeTriggered()
} }
DividerType {} DividerType {}

View file

@ -15,8 +15,6 @@ import "../Components"
PageType { PageType {
id: root id: root
defaultActiveFocusItem: focusItem
FlickableType { FlickableType {
id: fl id: fl
anchors.top: parent.top anchors.top: parent.top
@ -32,15 +30,9 @@ PageType {
spacing: 0 spacing: 0
Item {
id: focusItem
KeyNavigation.tab: backButton
}
BackButtonType { BackButtonType {
id: backButton id: backButton
Layout.topMargin: 20 Layout.topMargin: 20
// KeyNavigation.tab: fileButton.rightButton
} }
HeaderType { HeaderType {

View file

@ -14,8 +14,6 @@ import "../Config"
PageType { PageType {
id: root id: root
defaultActiveFocusItem: focusItem
FlickableType { FlickableType {
id: fl id: fl
anchors.top: parent.top anchors.top: parent.top
@ -31,15 +29,9 @@ PageType {
spacing: 0 spacing: 0
Item {
id: focusItem
KeyNavigation.tab: backButton
}
BackButtonType { BackButtonType {
id: backButton id: backButton
Layout.topMargin: 20 Layout.topMargin: 20
// KeyNavigation.tab: fileButton.rightButton
} }
HeaderType { HeaderType {
@ -59,6 +51,8 @@ PageType {
height: containers.contentItem.height height: containers.contentItem.height
spacing: 16 spacing: 16
property bool isFocusable: true
currentIndex: 1 currentIndex: 1
interactive: false interactive: false
model: ApiServicesModel model: ApiServicesModel
@ -93,6 +87,9 @@ PageType {
PageController.goToPage(PageEnum.PageSetupWizardApiServiceInfo) PageController.goToPage(PageEnum.PageSetupWizardApiServiceInfo)
} }
} }
Keys.onEnterPressed: clicked()
Keys.onReturnPressed: clicked()
} }
} }
} }

View file

@ -25,30 +25,153 @@ PageType {
} }
} }
defaultActiveFocusItem: focusItem QtObject {
id: amneziaVpn
FlickableType { property string title: qsTr("VPN by Amnezia")
id: fl property string description: qsTr("Connect to classic paid and free VPN services from Amnezia")
anchors.top: parent.top property string imageSource: "qrc:/images/controls/amnezia.svg"
anchors.bottom: parent.bottom property bool isVisible: true
contentHeight: content.height property var handler: function() {
PageController.showBusyIndicator(true)
ColumnLayout { var result = InstallController.fillAvailableServices()
id: content PageController.showBusyIndicator(false)
if (result) {
anchors.top: parent.top PageController.goToPage(PageEnum.PageSetupWizardApiServicesList)
anchors.left: parent.left }
anchors.right: parent.right }
spacing: 0
Item {
id: focusItem
KeyNavigation.tab: textKey.textField
} }
QtObject {
id: selfHostVpn
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<QtObject> 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 { HeaderType {
id: moreButton
property bool isVisible: SettingsController.getInstallationUuid() !== "" || PageController.isStartPageVisible() property bool isVisible: SettingsController.getInstallationUuid() !== "" || PageController.isStartPageVisible()
Layout.fillWidth: true Layout.fillWidth: true
@ -60,7 +183,7 @@ PageType {
actionButtonImage: isVisible ? "qrc:/images/controls/more-vertical.svg" : "" actionButtonImage: isVisible ? "qrc:/images/controls/more-vertical.svg" : ""
actionButtonFunction: function() { actionButtonFunction: function() {
moreActionsDrawer.open() moreActionsDrawer.openTriggered()
} }
DrawerType2 { DrawerType2 {
@ -71,7 +194,7 @@ PageType {
anchors.fill: parent anchors.fill: parent
expandedHeight: root.height * 0.5 expandedHeight: root.height * 0.5
expandedContent: ColumnLayout { expandedStateContent: ColumnLayout {
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
@ -131,6 +254,8 @@ PageType {
} }
ParagraphTextType { ParagraphTextType {
objectName: "insertKeyLabel"
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 32 Layout.topMargin: 32
Layout.rightMargin: 16 Layout.rightMargin: 16
@ -154,8 +279,6 @@ PageType {
textField.text = "" textField.text = ""
textField.paste() textField.paste()
} }
KeyNavigation.tab: continueButton
} }
BasicButtonType { BasicButtonType {
@ -169,7 +292,6 @@ PageType {
visible: textKey.textFieldText !== "" visible: textKey.textFieldText !== ""
text: qsTr("Continue") text: qsTr("Continue")
Keys.onTabPressed: lastItemTabClicked(focusItem)
clickedFunc: function() { clickedFunc: function() {
if (ImportController.extractConfigFromData(textKey.textFieldText)) { if (ImportController.extractConfigFromData(textKey.textFieldText)) {
@ -188,142 +310,28 @@ PageType {
color: AmneziaStyle.color.charcoalGray color: AmneziaStyle.color.charcoalGray
text: qsTr("Other connection options") text: qsTr("Other connection options")
} }
}
delegate: ColumnLayout {
width: listView.width
CardWithIconsType { CardWithIconsType {
id: apiInstalling id: entry
Layout.fillWidth: true Layout.fillWidth: true
Layout.rightMargin: 16 Layout.rightMargin: 16
Layout.leftMargin: 16 Layout.leftMargin: 16
Layout.bottomMargin: 16 Layout.bottomMargin: 16
headerText: qsTr("VPN by Amnezia") visible: isVisible
bodyText: qsTr("Connect to classic paid and free VPN services from Amnezia")
headerText: title
bodyText: description
rightImageSource: "qrc:/images/controls/chevron-right.svg" rightImageSource: "qrc:/images/controls/chevron-right.svg"
leftImageSource: "qrc:/images/controls/amnezia.svg" leftImageSource: imageSource
onClicked: function() { onClicked: { handler() }
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"
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"
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"
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"
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"
onClicked: {
Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl())
}
} }
} }
} }

View file

@ -13,13 +13,6 @@ import "../Controls2/TextTypes"
PageType { PageType {
id: root id: root
defaultActiveFocusItem: hostname.textField
Item {
id: focusItem
KeyNavigation.tab: backButton
}
BackButtonType { BackButtonType {
id: backButton id: backButton
@ -27,8 +20,6 @@ PageType {
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 20 anchors.topMargin: 20
KeyNavigation.tab: hostname.textField
} }
FlickableType { FlickableType {
@ -61,11 +52,11 @@ PageType {
headerText: qsTr("Server IP address [:port]") headerText: qsTr("Server IP address [:port]")
textFieldPlaceholderText: qsTr("255.255.255.255:22") textFieldPlaceholderText: qsTr("255.255.255.255:22")
parentFlickable: fl
textField.onFocusChanged: { textField.onFocusChanged: {
textField.text = textField.text.replace(/^\s+|\s+$/g, '') textField.text = textField.text.replace(/^\s+|\s+$/g, '')
} }
KeyNavigation.tab: username.textField
} }
TextFieldWithHeaderType { TextFieldWithHeaderType {
@ -75,11 +66,11 @@ PageType {
headerText: qsTr("SSH Username") headerText: qsTr("SSH Username")
textFieldPlaceholderText: "root" textFieldPlaceholderText: "root"
parentFlickable: fl
textField.onFocusChanged: { textField.onFocusChanged: {
textField.text = textField.text.replace(/^\s+|\s+$/g, '') textField.text = textField.text.replace(/^\s+|\s+$/g, '')
} }
KeyNavigation.tab: secretData.textField
} }
TextFieldWithHeaderType { TextFieldWithHeaderType {
@ -93,6 +84,8 @@ PageType {
buttonImageSource: textFieldText !== "" ? (hidePassword ? "qrc:/images/controls/eye.svg" : "qrc:/images/controls/eye-off.svg") buttonImageSource: textFieldText !== "" ? (hidePassword ? "qrc:/images/controls/eye.svg" : "qrc:/images/controls/eye-off.svg")
: "" : ""
parentFlickable: fl
clickedFunc: function() { clickedFunc: function() {
hidePassword = !hidePassword hidePassword = !hidePassword
} }
@ -100,8 +93,6 @@ PageType {
textField.onFocusChanged: { textField.onFocusChanged: {
textField.text = textField.text.replace(/^\s+|\s+$/g, '') textField.text = textField.text.replace(/^\s+|\s+$/g, '')
} }
KeyNavigation.tab: continueButton
} }
BasicButtonType { BasicButtonType {
@ -112,7 +103,7 @@ PageType {
text: qsTr("Continue") text: qsTr("Continue")
Keys.onTabPressed: lastItemTabClicked(focusItem) parentFlickable: fl
clickedFunc: function() { clickedFunc: function() {
forceActiveFocus() forceActiveFocus()
@ -153,6 +144,8 @@ PageType {
rightImageSource: "qrc:/images/controls/chevron-right.svg" rightImageSource: "qrc:/images/controls/chevron-right.svg"
leftImageSource: "qrc:/images/controls/help-circle.svg" leftImageSource: "qrc:/images/controls/help-circle.svg"
parentFlickable: fl
onClicked: { onClicked: {
Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl() + "/starter-guide") Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl() + "/starter-guide")
} }

View file

@ -17,7 +17,6 @@ PageType {
id: root id: root
property bool isEasySetup: true property bool isEasySetup: true
defaultActiveFocusItem: focusItem
SortFilterProxyModel { SortFilterProxyModel {
id: proxyContainersModel id: proxyContainersModel
@ -34,14 +33,6 @@ PageType {
} }
} }
Item {
id: focusItem
implicitWidth: 1
implicitHeight: 54
KeyNavigation.tab: backButton
}
BackButtonType { BackButtonType {
id: backButton id: backButton
@ -49,8 +40,6 @@ PageType {
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 20 anchors.topMargin: 20
KeyNavigation.tab: continueButton
} }
FlickableType { FlickableType {
@ -163,7 +152,7 @@ PageType {
implicitWidth: parent.width implicitWidth: parent.width
text: qsTr("Continue") text: qsTr("Continue")
KeyNavigation.tab: setupLaterButton
parentFlickable: fl parentFlickable: fl
clickedFunc: function() { clickedFunc: function() {

View file

@ -49,6 +49,32 @@ PageType {
interactive: false interactive: false
model: proxyContainersModel 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 { delegate: Item {
implicitWidth: processedContainerListView.width implicitWidth: processedContainerListView.width
implicitHeight: (delegateContent.implicitHeight > root.height) ? delegateContent.implicitHeight : root.height implicitHeight: (delegateContent.implicitHeight > root.height) ? delegateContent.implicitHeight : root.height
@ -62,19 +88,12 @@ PageType {
anchors.rightMargin: 16 anchors.rightMargin: 16
anchors.leftMargin: 16 anchors.leftMargin: 16
Item {
id: focusItem
KeyNavigation.tab: backButton
}
BackButtonType { BackButtonType {
id: backButton id: backButton
Layout.topMargin: 20 Layout.topMargin: 20
Layout.rightMargin: -16 Layout.rightMargin: -16
Layout.leftMargin: -16 Layout.leftMargin: -16
KeyNavigation.tab: showDetailsButton
} }
HeaderType { HeaderType {
@ -104,42 +123,19 @@ PageType {
KeyNavigation.tab: transportProtoSelector KeyNavigation.tab: transportProtoSelector
clickedFunc: function() { clickedFunc: function() {
showDetailsDrawer.open() showDetailsDrawer.openTriggered()
} }
} }
DrawerType2 { DrawerType2 {
id: showDetailsDrawer id: showDetailsDrawer
parent: root parent: root
onClosed: {
if (!GC.isMobile()) {
defaultActiveFocusItem.forceActiveFocus()
}
}
anchors.fill: parent anchors.fill: parent
expandedHeight: parent.height * 0.9 expandedHeight: parent.height * 0.9
expandedContent: Item { expandedStateContent: Item {
Connections {
target: showDetailsDrawer
enabled: !GC.isMobile()
function onOpened() {
focusItem2.forceActiveFocus()
}
}
implicitHeight: showDetailsDrawer.expandedHeight implicitHeight: showDetailsDrawer.expandedHeight
Item {
id: focusItem2
KeyNavigation.tab: showDetailsBackButton
onFocusChanged: {
if (focusItem2.activeFocus) {
fl.contentY = 0
}
}
}
BackButtonType { BackButtonType {
id: showDetailsBackButton id: showDetailsBackButton
@ -148,10 +144,8 @@ PageType {
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 16 anchors.topMargin: 16
KeyNavigation.tab: showDetailsCloseButton
backButtonFunction: function() { backButtonFunction: function() {
showDetailsDrawer.close() showDetailsDrawer.closeTriggered()
} }
} }
@ -205,10 +199,9 @@ PageType {
parentFlickable: fl parentFlickable: fl
text: qsTr("Close") text: qsTr("Close")
Keys.onTabPressed: lastItemTabClicked(focusItem2)
clickedFunc: function() { clickedFunc: function() {
showDetailsDrawer.close() showDetailsDrawer.closeTriggered()
} }
} }
} }
@ -229,8 +222,6 @@ PageType {
Layout.fillWidth: true Layout.fillWidth: true
rootWidth: root.width rootWidth: root.width
KeyNavigation.tab: (port.visible && port.enabled) ? port.textField : installButton
} }
TextFieldWithHeaderType { TextFieldWithHeaderType {
@ -242,8 +233,6 @@ PageType {
headerText: qsTr("Port") headerText: qsTr("Port")
textField.maximumLength: 5 textField.maximumLength: 5
textField.validator: IntValidator { bottom: 1; top: 65535 } textField.validator: IntValidator { bottom: 1; top: 65535 }
KeyNavigation.tab: installButton
} }
Rectangle { Rectangle {
@ -259,8 +248,6 @@ PageType {
text: qsTr("Install") text: qsTr("Install")
Keys.onTabPressed: lastItemTabClicked(focusItem)
clickedFunc: function() { clickedFunc: function() {
if (!port.textField.acceptableInput && if (!port.textField.acceptableInput &&
ContainerProps.containerTypeToString(dockerContainer) !== "torwebsite" && ContainerProps.containerTypeToString(dockerContainer) !== "torwebsite" &&
@ -288,11 +275,6 @@ PageType {
var protocolSelectorVisible = ProtocolProps.defaultTransportProtoChangeable(defaultContainerProto) var protocolSelectorVisible = ProtocolProps.defaultTransportProtoChangeable(defaultContainerProto)
transportProtoSelector.visible = protocolSelectorVisible transportProtoSelector.visible = protocolSelectorVisible
transportProtoHeader.visible = protocolSelectorVisible transportProtoHeader.visible = protocolSelectorVisible
if (port.visible && port.enabled)
defaultActiveFocusItem = port.textField
else
defaultActiveFocusItem = focusItem
} }
} }
} }

View file

@ -15,13 +15,6 @@ import "../Config"
PageType { PageType {
id: root id: root
defaultActiveFocusItem: focusItem
Item {
id: focusItem
KeyNavigation.tab: backButton
}
SortFilterProxyModel { SortFilterProxyModel {
id: proxyContainersModel id: proxyContainersModel
sourceModel: ContainersModel sourceModel: ContainersModel
@ -52,7 +45,6 @@ PageType {
BackButtonType { BackButtonType {
id: backButton id: backButton
KeyNavigation.tab: containers
} }
} }

View file

@ -14,8 +14,6 @@ import "../Components"
PageType { PageType {
id: root id: root
defaultActiveFocusItem: focusItem
ColumnLayout { ColumnLayout {
id: content id: content
@ -32,11 +30,6 @@ PageType {
Layout.preferredHeight: 287 Layout.preferredHeight: 287
} }
Item {
id: focusItem
KeyNavigation.tab: startButton
}
BasicButtonType { BasicButtonType {
id: startButton id: startButton
Layout.fillWidth: true Layout.fillWidth: true
@ -50,8 +43,6 @@ PageType {
clickedFunc: function() { clickedFunc: function() {
PageController.goToPage(PageEnum.PageSetupWizardConfigSource) PageController.goToPage(PageEnum.PageSetupWizardConfigSource)
} }
Keys.onTabPressed: lastItemTabClicked(focusItem)
} }
} }
} }

View file

@ -13,14 +13,6 @@ import "../Config"
PageType { PageType {
id: root id: root
defaultActiveFocusItem: textKey.textField
Item {
id: focusItem
KeyNavigation.tab: backButton
}
FlickableType { FlickableType {
id: fl id: fl
anchors.top: parent.top anchors.top: parent.top
@ -39,7 +31,6 @@ PageType {
BackButtonType { BackButtonType {
id: backButton id: backButton
Layout.topMargin: 20 Layout.topMargin: 20
KeyNavigation.tab: textKey.textField
} }
HeaderType { HeaderType {
@ -67,8 +58,6 @@ PageType {
textField.text = "" textField.text = ""
textField.paste() textField.paste()
} }
KeyNavigation.tab: continueButton
} }
} }
} }
@ -84,7 +73,6 @@ PageType {
anchors.bottomMargin: 32 anchors.bottomMargin: 32
text: qsTr("Continue") text: qsTr("Continue")
Keys.onTabPressed: lastItemTabClicked(focusItem)
clickedFunc: function() { clickedFunc: function() {
if (ImportController.extractConfigFromData(textKey.textFieldText)) { if (ImportController.extractConfigFromData(textKey.textFieldText)) {

View file

@ -16,13 +16,6 @@ PageType {
property bool showContent: false property bool showContent: false
defaultActiveFocusItem: focusItem
Item {
id: focusItem
KeyNavigation.tab: backButton
}
BackButtonType { BackButtonType {
id: backButton id: backButton
@ -30,8 +23,6 @@ PageType {
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 20 anchors.topMargin: 20
KeyNavigation.tab: showContentButton
} }
Connections { Connections {
@ -107,7 +98,8 @@ PageType {
textColor: AmneziaStyle.color.goldenApricot textColor: AmneziaStyle.color.goldenApricot
text: showContent ? qsTr("Collapse content") : qsTr("Show content") text: showContent ? qsTr("Collapse content") : qsTr("Show content")
KeyNavigation.tab: connectButton
parentFlickable: fl
clickedFunc: function() { clickedFunc: function() {
showContent = !showContent showContent = !showContent
@ -186,8 +178,6 @@ PageType {
anchors.rightMargin: 16 anchors.rightMargin: 16
anchors.leftMargin: 16 anchors.leftMargin: 16
Keys.onTabPressed: lastItemTabClicked(focusItem)
BasicButtonType { BasicButtonType {
id: connectButton id: connectButton
Layout.fillWidth: true Layout.fillWidth: true

View file

@ -18,8 +18,6 @@ import "../Config"
PageType { PageType {
id: root id: root
defaultActiveFocusItem: clientNameTextField.textField
enum ConfigType { enum ConfigType {
AmneziaConnection, AmneziaConnection,
OpenVpn, OpenVpn,
@ -47,7 +45,7 @@ PageType {
shareConnectionDrawer.headerText = qsTr("Connection to ") + serverSelector.text shareConnectionDrawer.headerText = qsTr("Connection to ") + serverSelector.text
shareConnectionDrawer.configContentHeaderText = qsTr("File with connection settings to ") + serverSelector.text shareConnectionDrawer.configContentHeaderText = qsTr("File with connection settings to ") + serverSelector.text
shareConnectionDrawer.open() shareConnectionDrawer.openTriggered()
shareConnectionDrawer.contentVisible = false shareConnectionDrawer.contentVisible = false
PageController.showBusyIndicator(true) PageController.showBusyIndicator(true)
@ -104,7 +102,7 @@ PageType {
} }
function onExportErrorOccurred(error) { function onExportErrorOccurred(error) {
shareConnectionDrawer.close() shareConnectionDrawer.closeTriggered()
PageController.showErrorMessage(error) PageController.showErrorMessage(error)
} }
@ -172,16 +170,6 @@ PageType {
spacing: 0 spacing: 0
Item {
id: focusItem
KeyNavigation.tab: header.actionButton
onFocusChanged: {
if (focusItem.activeFocus) {
a.contentY = 0
}
}
}
HeaderType { HeaderType {
id: header id: header
Layout.fillWidth: true Layout.fillWidth: true
@ -191,10 +179,17 @@ PageType {
actionButtonImage: "qrc:/images/controls/more-vertical.svg" actionButtonImage: "qrc:/images/controls/more-vertical.svg"
actionButtonFunction: function() { actionButtonFunction: function() {
shareFullAccessDrawer.open() shareFullAccessDrawer.openTriggered()
} }
KeyNavigation.tab: connectionRadioButton actionButton.onFocusChanged: {
console.debug("MOVE THIS LOGIC TO CPP!")
if (actionButton.activeFocus) {
if (fl) {
fl.ensureVisible(moreButton)
}
}
}
DrawerType2 { DrawerType2 {
id: shareFullAccessDrawer id: shareFullAccessDrawer
@ -203,13 +198,8 @@ PageType {
anchors.fill: parent anchors.fill: parent
expandedHeight: root.height expandedHeight: root.height
onClosed: {
if (!GC.isMobile()) {
clientNameTextField.textField.forceActiveFocus()
}
}
expandedContent: ColumnLayout { expandedStateContent: ColumnLayout {
id: shareFullAccessDrawerContent id: shareFullAccessDrawerContent
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
@ -222,14 +212,6 @@ PageType {
shareFullAccessDrawer.expandedHeight = shareFullAccessDrawerContent.implicitHeight + 32 shareFullAccessDrawer.expandedHeight = shareFullAccessDrawerContent.implicitHeight + 32
} }
Connections {
target: shareFullAccessDrawer
enabled: !GC.isMobile()
function onOpened() {
focusItem.forceActiveFocus()
}
}
Header2Type { Header2Type {
Layout.fillWidth: true Layout.fillWidth: true
Layout.bottomMargin: 16 Layout.bottomMargin: 16
@ -240,24 +222,17 @@ PageType {
descriptionText: qsTr("Use for your own devices, or share with those you trust to manage the server.") 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 { LabelWithButtonType {
id: shareFullAccessButton id: shareFullAccessButton
Layout.fillWidth: true Layout.fillWidth: true
text: qsTr("Share") text: qsTr("Share")
rightImageSource: "qrc:/images/controls/chevron-right.svg" rightImageSource: "qrc:/images/controls/chevron-right.svg"
KeyNavigation.tab: focusItem
clickedFunction: function() { clickedFunction: function() {
PageController.goToPage(PageEnum.PageShareFullAccess) PageController.goToPage(PageEnum.PageShareFullAccess)
shareFullAccessDrawer.close() shareFullAccessDrawer.closeTriggered()
} }
} }
} }
} }
@ -288,8 +263,6 @@ PageType {
implicitWidth: (root.width - 32) / 2 implicitWidth: (root.width - 32) / 2
text: qsTr("Connection") text: qsTr("Connection")
KeyNavigation.tab: usersRadioButton
onClicked: { onClicked: {
accessTypeSelector.currentIndex = 0 accessTypeSelector.currentIndex = 0
if (!GC.isMobile()) { if (!GC.isMobile()) {
@ -305,15 +278,12 @@ PageType {
implicitWidth: (root.width - 32) / 2 implicitWidth: (root.width - 32) / 2
text: qsTr("Users") text: qsTr("Users")
KeyNavigation.tab: accessTypeSelector.currentIndex === 0 ? clientNameTextField.textField : serverSelector
onClicked: { onClicked: {
accessTypeSelector.currentIndex = 1 accessTypeSelector.currentIndex = 1
PageController.showBusyIndicator(true) PageController.showBusyIndicator(true)
ExportController.updateClientManagementModel(ContainersModel.getProcessedContainerIndex(), ExportController.updateClientManagementModel(ContainersModel.getProcessedContainerIndex(),
ServersModel.getProcessedServerCredentials()) ServersModel.getProcessedServerCredentials())
PageController.showBusyIndicator(false) PageController.showBusyIndicator(false)
focusItem.forceActiveFocus()
} }
} }
} }
@ -342,9 +312,6 @@ PageType {
textField.maximumLength: 20 textField.maximumLength: 20
checkEmptyText: true checkEmptyText: true
KeyNavigation.tab: serverSelector
} }
DropDownType { DropDownType {
@ -390,7 +357,7 @@ PageType {
serverSelector.severSelectorIndexChanged() serverSelector.severSelectorIndexChanged()
} }
serverSelector.close() serverSelector.closeTriggered()
} }
Component.onCompleted: { Component.onCompleted: {
@ -408,8 +375,6 @@ PageType {
ServersModel.processedIndex = proxyServersModel.mapToSource(currentIndex) ServersModel.processedIndex = proxyServersModel.mapToSource(currentIndex)
} }
} }
KeyNavigation.tab: protocolSelector
} }
DropDownType { DropDownType {
@ -450,7 +415,7 @@ PageType {
clickedFunction: function() { clickedFunction: function() {
handler() handler()
protocolSelector.close() protocolSelector.closeTriggered()
} }
Connections { Connections {
@ -508,12 +473,6 @@ PageType {
} }
} }
} }
KeyNavigation.tab: accessTypeSelector.currentIndex === 0 ?
exportTypeSelector :
isSearchBarVisible ?
searchTextField.textField :
usersHeader.actionButton
} }
DropDownType { DropDownType {
@ -549,7 +508,7 @@ PageType {
clickedFunction: function() { clickedFunction: function() {
exportTypeSelector.text = selectedText exportTypeSelector.text = selectedText
exportTypeSelector.currentIndex = currentIndex exportTypeSelector.currentIndex = currentIndex
exportTypeSelector.close() exportTypeSelector.closeTriggered()
} }
Component.onCompleted: { Component.onCompleted: {
@ -557,9 +516,6 @@ PageType {
exportTypeSelector.currentIndex = currentIndex exportTypeSelector.currentIndex = currentIndex
} }
} }
KeyNavigation.tab: shareButton
} }
BasicButtonType { BasicButtonType {
@ -575,7 +531,6 @@ PageType {
text: qsTr("Share") text: qsTr("Share")
leftImageSource: "qrc:/images/controls/share-2.svg" leftImageSource: "qrc:/images/controls/share-2.svg"
Keys.onTabPressed: lastItemTabClicked(focusItem)
parentFlickable: a parentFlickable: a
@ -584,7 +539,6 @@ PageType {
ExportController.generateConfig(root.connectionTypesModel[exportTypeSelector.currentIndex].type) ExportController.generateConfig(root.connectionTypesModel[exportTypeSelector.currentIndex].type)
} }
} }
} }
Header2Type { Header2Type {
@ -604,7 +558,6 @@ PageType {
Keys.onTabPressed: clientsListView.model.count > 0 ? Keys.onTabPressed: clientsListView.model.count > 0 ?
clientsListView.forceActiveFocus() : clientsListView.forceActiveFocus() :
lastItemTabClicked(focusItem) lastItemTabClicked(focusItem)
} }
RowLayout { RowLayout {
@ -622,11 +575,11 @@ PageType {
target: root target: root
function onIsSearchBarVisibleChanged() { function onIsSearchBarVisibleChanged() {
if (root.isSearchBarVisible) { if (root.isSearchBarVisible) {
searchTextField.textField.forceActiveFocus() // searchTextField.textField.forceActiveFocus()
} else { } else {
searchTextField.textFieldText = "" searchTextField.textFieldText = ""
if (!GC.isMobile()) { if (!GC.isMobile()) {
usersHeader.actionButton.forceActiveFocus() // usersHeader.actionButton.forceActiveFocus()
} }
} }
} }
@ -638,15 +591,15 @@ PageType {
function navigateTo() { function navigateTo() {
if (GC.isMobile()) { if (GC.isMobile()) {
focusItem.forceActiveFocus() // focusItem.forceActiveFocus()
return; return;
} }
if (searchTextField.textFieldText === "") { if (searchTextField.textFieldText === "") {
root.isSearchBarVisible = false root.isSearchBarVisible = false
usersHeader.actionButton.forceActiveFocus() // usersHeader.actionButton.forceActiveFocus()
} else { } else {
closeSearchButton.forceActiveFocus() // closeSearchButton.forceActiveFocus()
} }
} }
@ -663,9 +616,9 @@ PageType {
Keys.onTabPressed: { Keys.onTabPressed: {
if (!GC.isMobile()) { if (!GC.isMobile()) {
if (clientsListView.model.count > 0) { if (clientsListView.model.count > 0) {
clientsListView.forceActiveFocus() // clientsListView.forceActiveFocus()
} else { } else {
lastItemTabClicked(focusItem) // lastItemTabClicked(focusItem)
} }
} }
} }
@ -687,6 +640,32 @@ PageType {
visible: accessTypeSelector.currentIndex === 1 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 { model: SortFilterProxyModel {
id: proxyClientManagementModel id: proxyClientManagementModel
sourceModel: ClientManagementModel sourceModel: ClientManagementModel
@ -698,44 +677,44 @@ PageType {
} }
clip: true clip: true
interactive: false // interactive: false
activeFocusOnTab: true // activeFocusOnTab: true
focus: true // focus: true
Keys.onTabPressed: { // Keys.onTabPressed: {
if (!GC.isMobile()) { // if (!GC.isMobile()) {
if (currentIndex < this.count - 1) { // if (currentIndex < this.count - 1) {
this.incrementCurrentIndex() // this.incrementCurrentIndex()
currentItem.focusItem.forceActiveFocus() // // currentItem.focusItem.forceActiveFocus()
} else { // } else {
this.currentIndex = 0 // this.currentIndex = 0
lastItemTabClicked(focusItem) // // lastItemTabClicked(focusItem)
} // }
} // }
} // }
onActiveFocusChanged: { // onActiveFocusChanged: {
if (focus && !GC.isMobile()) { // if (focus && !GC.isMobile()) {
currentIndex = 0 // currentIndex = 0
currentItem.focusItem.forceActiveFocus() // // currentItem.focusItem.forceActiveFocus()
} // }
} // }
onCurrentIndexChanged: { // onCurrentIndexChanged: {
if (currentItem) { // if (currentItem) {
if (currentItem.y < a.contentY) { // if (currentItem.y < a.contentY) {
a.contentY = currentItem.y // a.contentY = currentItem.y
} else if (currentItem.y + currentItem.height + clientsListView.y > a.contentY + a.height) { // } else if (currentItem.y + currentItem.height + clientsListView.y > a.contentY + a.height) {
a.contentY = currentItem.y + clientsListView.y + currentItem.height - a.height // a.contentY = currentItem.y + clientsListView.y + currentItem.height - a.height
} // }
} // }
} // }
delegate: Item { delegate: Item {
implicitWidth: clientsListView.width implicitWidth: clientsListView.width
implicitHeight: delegateContent.implicitHeight implicitHeight: delegateContent.implicitHeight
property alias focusItem: clientFocusItem.rightButton // property alias focusItem: clientFocusItem.rightButton
ColumnLayout { ColumnLayout {
id: delegateContent id: delegateContent
@ -755,7 +734,7 @@ PageType {
rightImageSource: "qrc:/images/controls/chevron-right.svg" rightImageSource: "qrc:/images/controls/chevron-right.svg"
clickedFunction: function() { clickedFunction: function() {
clientInfoDrawer.open() clientInfoDrawer.openTriggered()
} }
} }
@ -768,15 +747,15 @@ PageType {
onClosed: { onClosed: {
if (!GC.isMobile()) { if (!GC.isMobile()) {
focusItem.forceActiveFocus() // focusItem.forceActiveFocus()
} }
} }
width: root.width width: root.width
height: root.height height: root.height
expandedContent: ColumnLayout { expandedStateContent: ColumnLayout {
id: expandedContent id: expandedStateContent
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
@ -785,14 +764,14 @@ PageType {
anchors.rightMargin: 16 anchors.rightMargin: 16
onImplicitHeightChanged: { onImplicitHeightChanged: {
clientInfoDrawer.expandedHeight = expandedContent.implicitHeight + 32 clientInfoDrawer.expandedHeight = expandedStateContent.implicitHeight + 32
} }
Connections { Connections {
target: clientInfoDrawer target: clientInfoDrawer
enabled: !GC.isMobile() enabled: !GC.isMobile()
function onOpened() { function onOpened() {
focusItem1.forceActiveFocus() // focusItem1.forceActiveFocus()
} }
} }
@ -846,11 +825,6 @@ PageType {
text: qsTr("Allowed IPs: %1").arg(allowedIps) text: qsTr("Allowed IPs: %1").arg(allowedIps)
} }
Item {
id: focusItem1
KeyNavigation.tab: renameButton
}
BasicButtonType { BasicButtonType {
id: renameButton id: renameButton
Layout.fillWidth: true Layout.fillWidth: true
@ -865,10 +839,8 @@ PageType {
text: qsTr("Rename") text: qsTr("Rename")
KeyNavigation.tab: revokeButton
clickedFunc: function() { clickedFunc: function() {
clientNameEditDrawer.open() clientNameEditDrawer.openTriggered()
} }
DrawerType2 { DrawerType2 {
@ -881,11 +853,11 @@ PageType {
onClosed: { onClosed: {
if (!GC.isMobile()) { if (!GC.isMobile()) {
focusItem1.forceActiveFocus() // focusItem1.forceActiveFocus()
} }
} }
expandedContent: ColumnLayout { expandedStateContent: ColumnLayout {
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
@ -897,15 +869,10 @@ PageType {
target: clientNameEditDrawer target: clientNameEditDrawer
enabled: !GC.isMobile() enabled: !GC.isMobile()
function onOpened() { function onOpened() {
clientNameEditor.textField.forceActiveFocus() // clientNameEditor.textField.forceActiveFocus()
} }
} }
Item {
id: focusItem2
KeyNavigation.tab: clientNameEditor.textField
}
TextFieldWithHeaderType { TextFieldWithHeaderType {
id: clientNameEditor id: clientNameEditor
Layout.fillWidth: true Layout.fillWidth: true
@ -913,8 +880,6 @@ PageType {
textFieldText: clientName textFieldText: clientName
textField.maximumLength: 20 textField.maximumLength: 20
checkEmptyText: true checkEmptyText: true
KeyNavigation.tab: saveButton
} }
BasicButtonType { BasicButtonType {
@ -923,7 +888,6 @@ PageType {
Layout.fillWidth: true Layout.fillWidth: true
text: qsTr("Save") text: qsTr("Save")
KeyNavigation.tab: focusItem2
clickedFunc: function() { clickedFunc: function() {
if (clientNameEditor.textFieldText === "") { if (clientNameEditor.textFieldText === "") {
@ -937,7 +901,7 @@ PageType {
ContainersModel.getProcessedContainerIndex(), ContainersModel.getProcessedContainerIndex(),
ServersModel.getProcessedServerCredentials()) ServersModel.getProcessedServerCredentials())
PageController.showBusyIndicator(false) PageController.showBusyIndicator(false)
clientNameEditDrawer.close() clientNameEditDrawer.closeTriggered()
} }
} }
} }
@ -958,7 +922,6 @@ PageType {
borderWidth: 1 borderWidth: 1
text: qsTr("Revoke") text: qsTr("Revoke")
KeyNavigation.tab: focusItem1
clickedFunc: function() { clickedFunc: function() {
var headerText = qsTr("Revoke the config for a user - %1?").arg(clientName) var headerText = qsTr("Revoke the config for a user - %1?").arg(clientName)
@ -967,12 +930,12 @@ PageType {
var noButtonText = qsTr("Cancel") var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() { var yesButtonFunction = function() {
clientInfoDrawer.close() clientInfoDrawer.closeTriggered()
root.revokeConfig(index) root.revokeConfig(index)
} }
var noButtonFunction = function() { var noButtonFunction = function() {
if (!GC.isMobile()) { if (!GC.isMobile()) {
focusItem1.forceActiveFocus() // focusItem1.forceActiveFocus()
} }
} }
@ -993,7 +956,7 @@ PageType {
anchors.fill: parent anchors.fill: parent
onClosed: { onClosed: {
if (!GC.isMobile()) { if (!GC.isMobile()) {
clientNameTextField.textField.forceActiveFocus() // clientNameTextField.textField.forceActiveFocus()
} }
} }
} }
@ -1001,7 +964,7 @@ PageType {
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
onPressed: function(mouse) { onPressed: function(mouse) {
forceActiveFocus() // forceActiveFocus()
mouse.accepted = false mouse.accepted = false
} }
} }

View file

@ -18,12 +18,7 @@ import "../Config"
PageType { PageType {
id: root id: root
defaultActiveFocusItem: focusItem // defaultActiveFocusItem: focusItem
Item {
id: focusItem
KeyNavigation.tab: backButton
}
BackButtonType { BackButtonType {
id: backButton id: backButton
@ -32,8 +27,6 @@ PageType {
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.topMargin: 20 anchors.topMargin: 20
KeyNavigation.tab: serverSelector
} }
FlickableType { FlickableType {
@ -85,8 +78,6 @@ PageType {
descriptionText: qsTr("Server") descriptionText: qsTr("Server")
headerText: qsTr("Server") headerText: qsTr("Server")
KeyNavigation.tab: shareButton
listView: ListViewWithRadioButtonType { listView: ListViewWithRadioButtonType {
id: serverSelectorListView id: serverSelectorListView
@ -113,7 +104,7 @@ PageType {
shareConnectionDrawer.headerText = qsTr("Accessing ") + serverSelector.text shareConnectionDrawer.headerText = qsTr("Accessing ") + serverSelector.text
shareConnectionDrawer.configContentHeaderText = qsTr("File with accessing settings to ") + serverSelector.text shareConnectionDrawer.configContentHeaderText = qsTr("File with accessing settings to ") + serverSelector.text
serverSelector.close() serverSelector.closeTriggered()
} }
Component.onCompleted: { Component.onCompleted: {
@ -137,8 +128,6 @@ PageType {
text: qsTr("Share") text: qsTr("Share")
leftImageSource: "qrc:/images/controls/share-2.svg" leftImageSource: "qrc:/images/controls/share-2.svg"
Keys.onTabPressed: lastItemTabClicked(focusItem)
clickedFunc: function() { clickedFunc: function() {
PageController.showBusyIndicator(true) PageController.showBusyIndicator(true)
@ -153,7 +142,7 @@ PageType {
shareConnectionDrawer.headerText = qsTr("Connection to ") + serverSelector.text shareConnectionDrawer.headerText = qsTr("Connection to ") + serverSelector.text
shareConnectionDrawer.configContentHeaderText = qsTr("File with connection settings to ") + serverSelector.text shareConnectionDrawer.configContentHeaderText = qsTr("File with connection settings to ") + serverSelector.text
shareConnectionDrawer.open() shareConnectionDrawer.openTriggered()
shareConnectionDrawer.contentVisible = true shareConnectionDrawer.contentVisible = true
PageController.showBusyIndicator(false) PageController.showBusyIndicator(false)
@ -166,10 +155,5 @@ PageType {
id: shareConnectionDrawer id: shareConnectionDrawer
anchors.fill: parent anchors.fill: parent
onClosed: {
if (!GC.isMobile()) {
focusItem.forceActiveFocus()
}
}
} }
} }

View file

@ -15,12 +15,12 @@ import "../Components"
PageType { PageType {
id: root id: root
defaultActiveFocusItem: homeTabButton
property bool isControlsDisabled: false property bool isControlsDisabled: false
property bool isTabBarDisabled: false property bool isTabBarDisabled: false
Connections { Connections {
objectName: "pageControllerConnection"
target: PageController target: PageController
function onGoToPageHome() { function onGoToPageHome() {
@ -90,19 +90,11 @@ PageType {
PageController.closePage() PageController.closePage()
} }
} }
function onForceTabBarActiveFocus() {
homeTabButton.focus = true
tabBar.forceActiveFocus()
}
function onForceStackActiveFocus() {
homeTabButton.focus = true
tabBarStackView.forceActiveFocus()
}
} }
Connections { Connections {
objectName: "installControllerConnections"
target: InstallController target: InstallController
function onInstallationErrorOccurred(error) { function onInstallationErrorOccurred(error) {
@ -165,6 +157,8 @@ PageType {
} }
Connections { Connections {
objectName: "connectionControllerConnections"
target: ConnectionController target: ConnectionController
function onReconnectWithUpdatedContainer(message) { function onReconnectWithUpdatedContainer(message) {
@ -182,6 +176,8 @@ PageType {
} }
Connections { Connections {
objectName: "importControllerConnections"
target: ImportController target: ImportController
function onImportErrorOccurred(error, goToPageHome) { function onImportErrorOccurred(error, goToPageHome) {
@ -196,6 +192,8 @@ PageType {
} }
Connections { Connections {
objectName: "settingsControllerConnections"
target: SettingsController target: SettingsController
function onLoggingDisableByWatcher() { function onLoggingDisableByWatcher() {
@ -218,6 +216,7 @@ PageType {
StackViewType { StackViewType {
id: tabBarStackView id: tabBarStackView
objectName: "tabBarStackView"
anchors.top: parent.top anchors.top: parent.top
anchors.right: parent.right anchors.right: parent.right
@ -247,13 +246,28 @@ PageType {
} }
Keys.onPressed: function(event) { Keys.onPressed: function(event) {
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) PageController.keyPressEvent(event.key)
event.accepted = true event.accepted = true
} }
} }
}
TabBar { TabBar {
id: tabBar id: tabBar
objectName: "tabBar"
anchors.right: parent.right anchors.right: parent.right
anchors.left: parent.left anchors.left: parent.left
@ -269,6 +283,8 @@ PageType {
enabled: !root.isControlsDisabled && !root.isTabBarDisabled enabled: !root.isControlsDisabled && !root.isTabBarDisabled
background: Shape { background: Shape {
objectName: "backgroundShape"
width: parent.width width: parent.width
height: parent.height height: parent.height
@ -289,6 +305,8 @@ PageType {
TabImageButtonType { TabImageButtonType {
id: homeTabButton id: homeTabButton
objectName: "homeTabButton"
isSelected: tabBar.currentIndex === 0 isSelected: tabBar.currentIndex === 0
image: "qrc:/images/controls/home.svg" image: "qrc:/images/controls/home.svg"
clickedFunc: function () { clickedFunc: function () {
@ -296,14 +314,11 @@ PageType {
ServersModel.processedIndex = ServersModel.defaultIndex ServersModel.processedIndex = ServersModel.defaultIndex
tabBar.currentIndex = 0 tabBar.currentIndex = 0
} }
KeyNavigation.tab: shareTabButton
Keys.onEnterPressed: this.clicked()
Keys.onReturnPressed: this.clicked()
} }
TabImageButtonType { TabImageButtonType {
id: shareTabButton id: shareTabButton
objectName: "shareTabButton"
Connections { Connections {
target: ServersModel target: ServersModel
@ -324,32 +339,30 @@ PageType {
tabBarStackView.goToTabBarPage(PageEnum.PageShare) tabBarStackView.goToTabBarPage(PageEnum.PageShare)
tabBar.currentIndex = 1 tabBar.currentIndex = 1
} }
KeyNavigation.tab: settingsTabButton
} }
TabImageButtonType { TabImageButtonType {
id: settingsTabButton id: settingsTabButton
objectName: "settingsTabButton"
isSelected: tabBar.currentIndex === 2 isSelected: tabBar.currentIndex === 2
image: "qrc:/images/controls/settings-2.svg" image: "qrc:/images/controls/settings-2.svg"
clickedFunc: function () { clickedFunc: function () {
tabBarStackView.goToTabBarPage(PageEnum.PageSettings) tabBarStackView.goToTabBarPage(PageEnum.PageSettings)
tabBar.currentIndex = 2 tabBar.currentIndex = 2
} }
KeyNavigation.tab: plusTabButton
} }
TabImageButtonType { TabImageButtonType {
id: plusTabButton id: plusTabButton
objectName: "plusTabButton"
isSelected: tabBar.currentIndex === 3 isSelected: tabBar.currentIndex === 3
image: "qrc:/images/controls/plus.svg" image: "qrc:/images/controls/plus.svg"
clickedFunc: function () { clickedFunc: function () {
tabBarStackView.goToTabBarPage(PageEnum.PageSetupWizardConfigSource) tabBarStackView.goToTabBarPage(PageEnum.PageSetupWizardConfigSource)
tabBar.currentIndex = 3 tabBar.currentIndex = 3
} }
Keys.onTabPressed: PageController.forceStackActiveFocus()
} }
} }
} }

View file

@ -15,6 +15,7 @@ import "Pages2"
Window { Window {
id: root id: root
objectName: "mainWindow" objectName: "mainWindow"
visible: true visible: true
width: GC.screenWidth width: GC.screenWidth
height: GC.screenHeight height: GC.screenHeight
@ -26,13 +27,39 @@ Window {
color: AmneziaStyle.color.midnightBlack color: AmneziaStyle.color.midnightBlack
onClosing: function() { onClosing: function() {
console.debug("QML onClosing signal")
PageController.closeWindow() PageController.closeWindow()
} }
title: "AmneziaVPN" title: "AmneziaVPN"
Item { // This item is needed for focus handling
id: defaultFocusItem
objectName: "defaultFocusItem"
focus: true
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
}
}
}
Connections { Connections {
objectName: "pageControllerConnections"
target: PageController target: PageController
function onRaiseMainWindow() { function onRaiseMainWindow() {
@ -58,7 +85,7 @@ Window {
} }
function onShowPassphraseRequestDrawer() { function onShowPassphraseRequestDrawer() {
privateKeyPassphraseDrawer.open() privateKeyPassphraseDrawer.openTriggered()
} }
function onGoToPageSettingsBackup() { function onGoToPageSettingsBackup() {
@ -72,6 +99,8 @@ Window {
} }
Connections { Connections {
objectName: "settingsControllerConnections"
target: SettingsController target: SettingsController
function onChangeSettingsFinished(finishedMessage) { function onChangeSettingsFinished(finishedMessage) {
@ -80,11 +109,15 @@ Window {
} }
PageStart { PageStart {
objectName: "pageStart"
width: root.width width: root.width
height: root.height height: root.height
} }
Item { Item {
objectName: "popupNotificationItem"
anchors.right: parent.right anchors.right: parent.right
anchors.left: parent.left anchors.left: parent.left
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
@ -108,6 +141,8 @@ Window {
} }
Item { Item {
objectName: "popupErrorMessageItem"
anchors.right: parent.right anchors.right: parent.right
anchors.left: parent.left anchors.left: parent.left
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
@ -120,6 +155,8 @@ Window {
} }
Item { Item {
objectName: "privateKeyPassphraseDrawerItem"
anchors.fill: parent anchors.fill: parent
DrawerType2 { DrawerType2 {
@ -128,7 +165,7 @@ Window {
anchors.fill: parent anchors.fill: parent
expandedHeight: root.height * 0.35 expandedHeight: root.height * 0.35
expandedContent: ColumnLayout { expandedStateContent: ColumnLayout {
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
@ -167,8 +204,6 @@ Window {
clickedFunc: function() { clickedFunc: function() {
hidePassword = !hidePassword hidePassword = !hidePassword
} }
KeyNavigation.tab: saveButton
} }
BasicButtonType { BasicButtonType {
@ -186,7 +221,7 @@ Window {
text: qsTr("Save") text: qsTr("Save")
clickedFunc: function() { clickedFunc: function() {
privateKeyPassphraseDrawer.close() privateKeyPassphraseDrawer.closeTriggered()
PageController.passphraseRequestDrawerClosed(passphrase.textFieldText) PageController.passphraseRequestDrawerClosed(passphrase.textFieldText)
} }
} }
@ -195,6 +230,8 @@ Window {
} }
Item { Item {
objectName: "questionDrawerItem"
anchors.fill: parent anchors.fill: parent
QuestionDrawer { QuestionDrawer {
@ -205,6 +242,8 @@ Window {
} }
Item { Item {
objectName: "busyIndicatorItem"
anchors.fill: parent anchors.fill: parent
BusyIndicatorType { BusyIndicatorType {
@ -221,26 +260,26 @@ Window {
questionDrawer.noButtonText = noButtonText questionDrawer.noButtonText = noButtonText
questionDrawer.yesButtonFunction = function() { questionDrawer.yesButtonFunction = function() {
questionDrawer.close() questionDrawer.closeTriggered()
if (yesButtonFunction && typeof yesButtonFunction === "function") { if (yesButtonFunction && typeof yesButtonFunction === "function") {
yesButtonFunction() yesButtonFunction()
} }
} }
questionDrawer.noButtonFunction = function() { questionDrawer.noButtonFunction = function() {
questionDrawer.close() questionDrawer.closeTriggered()
if (noButtonFunction && typeof noButtonFunction === "function") { if (noButtonFunction && typeof noButtonFunction === "function") {
noButtonFunction() noButtonFunction()
} }
} }
questionDrawer.open() questionDrawer.openTriggered()
} }
FileDialog { FileDialog {
id: mainFileDialog id: mainFileDialog
objectName: "mainFileDialog"
property bool isSaveMode: false property bool isSaveMode: false
objectName: "mainFileDialog"
fileMode: isSaveMode ? FileDialog.SaveFile : FileDialog.OpenFile fileMode: isSaveMode ? FileDialog.SaveFile : FileDialog.OpenFile
onAccepted: SystemController.fileDialogClosed(true) onAccepted: SystemController.fileDialogClosed(true)