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 {
id: focusItem
} }
onCurrentFocusIndexChanged: { Keys.onUpPressed: {
if (parentFlickable) { FocusController.nextKeyUpItem()
parentFlickable.ensureVisible(this.itemAtIndex(currentFocusIndex))
}
} }
Keys.onDownPressed: {
FocusController.nextKeyDownItem()
}
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,167 +30,173 @@ 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 }
Header2Type {
id: header
Layout.fillWidth: true
Layout.topMargin: 16
Layout.rightMargin: 16
Layout.leftMargin: 16
headerText: qsTr("Choose language")
} }
} }
FlickableType { ListView {
id: listView
anchors.top: backButtonLayout.bottom anchors.top: backButtonLayout.bottom
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
contentHeight: content.implicitHeight
ColumnLayout { clip: true
id: content interactive: true
anchors.fill: parent property bool isFocusable: true
Header2Type { Keys.onTabPressed: {
id: header FocusController.nextKeyTabItem()
Layout.fillWidth: true }
Layout.topMargin: 16
Layout.rightMargin: 16
Layout.leftMargin: 16
headerText: qsTr("Choose language") Keys.onBacktabPressed: {
} FocusController.previousKeyTabItem()
}
ListView { Keys.onUpPressed: {
id: listView FocusController.nextKeyUpItem()
}
Layout.fillWidth: true Keys.onDownPressed: {
height: listView.contentItem.height FocusController.nextKeyDownItem()
}
clip: true Keys.onLeftPressed: {
interactive: false FocusController.nextKeyLeftItem()
}
model: LanguageModel Keys.onRightPressed: {
currentIndex: LanguageModel.currentLanguageIndex FocusController.nextKeyRightItem()
}
ButtonGroup { model: LanguageModel
id: buttonGroup currentIndex: LanguageModel.currentLanguageIndex
}
property int currentFocusIndex: 0 ButtonGroup {
id: buttonGroup
}
activeFocusOnTab: true delegate: Item {
onActiveFocusChanged: { implicitWidth: root.width
if (activeFocus) { implicitHeight: delegateContent.implicitHeight
this.currentFocusIndex = 0
this.itemAtIndex(currentFocusIndex).forceActiveFocus()
}
}
Keys.onTabPressed: { ColumnLayout {
if (currentFocusIndex < this.count - 1) { id: delegateContent
currentFocusIndex += 1
this.itemAtIndex(currentFocusIndex).forceActiveFocus() anchors.fill: parent
} else {
listViewFocusItem.forceActiveFocus() RadioButton {
focusItem.forceActiveFocus() id: radioButton
}
} implicitWidth: parent.width
implicitHeight: radioButtonContent.implicitHeight
hoverEnabled: true
property bool isFocusable: true
Item {
id: listViewFocusItem
Keys.onTabPressed: { Keys.onTabPressed: {
root.forceActiveFocus() FocusController.nextKeyTabItem()
} }
}
onVisibleChanged: { Keys.onBacktabPressed: {
if (visible) { FocusController.previousKeyTabItem()
listViewFocusItem.forceActiveFocus()
focusItem.forceActiveFocus()
} }
}
delegate: Item { Keys.onUpPressed: {
implicitWidth: root.width FocusController.nextKeyUpItem()
implicitHeight: delegateContent.implicitHeight }
onActiveFocusChanged: { Keys.onDownPressed: {
if (activeFocus) { FocusController.nextKeyDownItem()
radioButton.forceActiveFocus() }
Keys.onLeftPressed: {
FocusController.nextKeyLeftItem()
}
Keys.onRightPressed: {
FocusController.nextKeyRightItem()
}
indicator: Rectangle {
width: parent.width - 1
height: parent.height
color: radioButton.hovered ? AmneziaStyle.color.slateGray : AmneziaStyle.color.onyxBlack
border.color: radioButton.focus ? AmneziaStyle.color.paleGray : AmneziaStyle.color.transparent
border.width: radioButton.focus ? 1 : 0
Behavior on color {
PropertyAnimation { duration: 200 }
}
Behavior on border.color {
PropertyAnimation { duration: 200 }
} }
} }
ColumnLayout { RowLayout {
id: delegateContent id: radioButtonContent
anchors.fill: parent anchors.fill: parent
RadioButton { anchors.rightMargin: 16
id: radioButton anchors.leftMargin: 16
implicitWidth: parent.width spacing: 0
implicitHeight: radioButtonContent.implicitHeight
hoverEnabled: true z: 1
indicator: Rectangle { ParagraphTextType {
width: parent.width - 1 Layout.fillWidth: true
height: parent.height Layout.topMargin: 20
color: radioButton.hovered ? AmneziaStyle.color.slateGray : AmneziaStyle.color.onyxBlack Layout.bottomMargin: 20
border.color: radioButton.focus ? AmneziaStyle.color.paleGray : AmneziaStyle.color.transparent
border.width: radioButton.focus ? 1 : 0
Behavior on color { text: languageName
PropertyAnimation { duration: 200 } }
}
Behavior on border.color {
PropertyAnimation { duration: 200 }
}
}
RowLayout { Image {
id: radioButtonContent source: "qrc:/images/controls/check.svg"
anchors.fill: parent visible: radioButton.checked
anchors.rightMargin: 16 width: 24
anchors.leftMargin: 16 height: 24
spacing: 0 Layout.rightMargin: 8
z: 1
ParagraphTextType {
Layout.fillWidth: true
Layout.topMargin: 20
Layout.bottomMargin: 20
text: languageName
}
Image {
source: "qrc:/images/controls/check.svg"
visible: radioButton.checked
width: 24
height: 24
Layout.rightMargin: 8
}
}
ButtonGroup.group: buttonGroup
checked: listView.currentIndex === index
onClicked: {
listView.currentIndex = index
LanguageModel.changeLanguage(languageIndex)
root.close()
}
} }
} }
Keys.onEnterPressed: radioButton.clicked() ButtonGroup.group: buttonGroup
Keys.onReturnPressed: radioButton.clicked() checked: listView.currentIndex === index
onClicked: {
listView.currentIndex = index
LanguageModel.changeLanguage(languageIndex)
root.closeTriggered()
}
} }
} }
Keys.onEnterPressed: radioButton.clicked()
Keys.onReturnPressed: radioButton.clicked()
} }
} }
} }

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 anchors.left: parent.left
anchors.right: parent.right
ColumnLayout { property bool isFocusable: true
id: content
anchors.top: parent.top Keys.onTabPressed: {
anchors.left: parent.left FocusController.nextKeyTabItem()
anchors.right: parent.right }
anchors.leftMargin: 16 Keys.onBacktabPressed: {
anchors.rightMargin: 16 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,61 +224,40 @@ 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 {
id: col
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
spacing: 16
Header2Type {
anchors.left: parent.left
anchors.right: parent.right
anchors.leftMargin: 16
anchors.rightMargin: 16
headerText: root.headerText
width: parent.width
}
Loader {
id: listViewLoader
sourceComponent: root.listView
onLoaded: {
listViewLoader.item.parentFlickable = flickable
listViewLoader.item.lastItemTabClicked = function() {
focusItem.forceActiveFocus()
}
} }
} }
} }
} }
}
}
Keys.onEnterPressed: { Column {
if (menu.isClosed) { id: col
menu.open() anchors.top: header.bottom
} anchors.left: parent.left
} anchors.right: parent.right
anchors.topMargin: 16
Keys.onReturnPressed: { spacing: 16
if (menu.isClosed) {
menu.open() Header2Type {
anchors.left: parent.left
anchors.right: parent.right
anchors.leftMargin: 16
anchors.rightMargin: 16
headerText: root.headerText
width: parent.width
}
Loader {
id: listViewLoader
sourceComponent: root.listView
}
}
} }
} }
} }

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,94 +59,110 @@ 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 {
id: content
implicitWidth: rootWidth implicitWidth: rootWidth
implicitHeight: content.implicitHeight // implicitHeight: content.implicitHeight
onActiveFocusChanged: { RadioButton {
if (activeFocus) { id: radioButton
radioButton.forceActiveFocus()
implicitWidth: parent.width
implicitHeight: radioButtonContent.implicitHeight
hoverEnabled: true
property bool isFocusable: true
Keys.onTabPressed: {
FocusController.nextKeyTabItem()
} }
}
ColumnLayout { Keys.onBacktabPressed: {
id: content FocusController.previousKeyTabItem()
}
anchors.fill: parent Keys.onUpPressed: {
FocusController.nextKeyUpItem()
}
RadioButton { Keys.onDownPressed: {
id: radioButton FocusController.nextKeyDownItem()
}
implicitWidth: parent.width Keys.onLeftPressed: {
implicitHeight: radioButtonContent.implicitHeight FocusController.nextKeyLeftItem()
}
hoverEnabled: true Keys.onRightPressed: {
FocusController.nextKeyRightItem()
}
indicator: Rectangle { indicator: Rectangle {
width: parent.width - 1 width: parent.width - 1
height: parent.height height: parent.height
color: radioButton.hovered ? AmneziaStyle.color.slateGray : AmneziaStyle.color.onyxBlack color: radioButton.hovered ? AmneziaStyle.color.slateGray : AmneziaStyle.color.onyxBlack
border.color: radioButton.focus ? AmneziaStyle.color.paleGray : AmneziaStyle.color.transparent border.color: radioButton.focus ? AmneziaStyle.color.paleGray : AmneziaStyle.color.transparent
border.width: radioButton.focus ? 1 : 0 border.width: radioButton.focus ? 1 : 0
Behavior on color { Behavior on color {
PropertyAnimation { duration: 200 } PropertyAnimation { duration: 200 }
} }
Behavior on border.color { Behavior on border.color {
PropertyAnimation { duration: 200 } PropertyAnimation { duration: 200 }
}
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
enabled: false
}
} }
RowLayout { MouseArea {
id: radioButtonContent
anchors.fill: parent anchors.fill: parent
cursorShape: Qt.PointingHandCursor
enabled: false
}
}
anchors.rightMargin: 16 RowLayout {
anchors.leftMargin: 16 id: radioButtonContent
anchors.fill: parent
z: 1 anchors.rightMargin: 16
anchors.leftMargin: 16
ParagraphTextType { z: 1
Layout.fillWidth: true
Layout.topMargin: 20
Layout.bottomMargin: 20
text: name ParagraphTextType {
maximumLineCount: root.textMaximumLineCount Layout.fillWidth: true
elide: root.textElide Layout.topMargin: 20
Layout.bottomMargin: 20
} text: name
maximumLineCount: root.textMaximumLineCount
elide: root.textElide
Image {
source: imageSource
visible: radioButton.checked
width: 24
height: 24
Layout.rightMargin: 8
}
} }
ButtonGroup.group: buttonGroup Image {
checked: root.currentIndex === index source: imageSource
visible: radioButton.checked
onClicked: { width: 24
root.currentIndex = index height: 24
root.selectedText = name
if (clickedFunction && typeof clickedFunction === "function") { Layout.rightMargin: 8
clickedFunction() }
} }
ButtonGroup.group: buttonGroup
checked: root.currentIndex === index
onClicked: {
root.currentIndex = index
root.selectedText = name
if (clickedFunction && typeof clickedFunction === "function") {
clickedFunction()
} }
} }
} }

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: {
root.checked = !root.checked if (!event.isAutoRepeat) {
root.checkedChanged() root.checked = !root.checked
root.checkedChanged()
}
event.accepted = true
} }
Keys.onReturnPressed: { Keys.onReturnPressed: {
root.checked = !root.checked if (!event.isAutoRepeat) {
root.checkedChanged() root.checked = !root.checked
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
parent: root objectName: "homeSplitTunnelingDrawer"
onClosed: { parent: root
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,229 +27,236 @@ PageType {
BackButtonType { BackButtonType {
id: backButton id: backButton
KeyNavigation.tab: listview.currentItem.mtuTextField.textField
} }
} }
FlickableType { ListView {
id: fl id: listview
anchors.top: backButtonLayout.bottom anchors.top: backButtonLayout.bottom
anchors.bottom: parent.bottom anchors.bottom: saveButton.top
contentHeight: content.implicitHeight + saveButton.implicitHeight + saveButton.anchors.bottomMargin + saveButton.anchors.topMargin
Column { width: parent.width
id: content
anchors.top: parent.top clip: true
anchors.left: parent.left
anchors.right: parent.right
ListView { property bool isFocusable: true
id: listview
width: parent.width Keys.onTabPressed: {
height: listview.contentItem.height FocusController.nextKeyTabItem()
}
clip: true Keys.onBacktabPressed: {
interactive: false FocusController.previousKeyTabItem()
}
model: AwgConfigModel Keys.onUpPressed: {
FocusController.nextKeyUpItem()
}
delegate: Item { Keys.onDownPressed: {
id: delegateItem FocusController.nextKeyDownItem()
implicitWidth: listview.width }
implicitHeight: col.implicitHeight
property alias mtuTextField: mtuTextField Keys.onLeftPressed: {
property bool isSaveButtonEnabled: mtuTextField.errorText === "" && FocusController.nextKeyLeftItem()
junkPacketMaxSizeTextField.errorText === "" && }
junkPacketMinSizeTextField.errorText === "" &&
junkPacketCountTextField.errorText === ""
ColumnLayout { Keys.onRightPressed: {
id: col FocusController.nextKeyRightItem()
}
anchors.top: parent.top model: AwgConfigModel
anchors.left: parent.left
anchors.right: parent.right
anchors.leftMargin: 16 delegate: Item {
anchors.rightMargin: 16 id: delegateItem
implicitWidth: listview.width
implicitHeight: col.implicitHeight
spacing: 0 property alias mtuTextField: mtuTextField
property bool isSaveButtonEnabled: mtuTextField.errorText === "" &&
junkPacketMaxSizeTextField.errorText === "" &&
junkPacketMinSizeTextField.errorText === "" &&
junkPacketCountTextField.errorText === ""
HeaderType { ColumnLayout {
Layout.fillWidth: true id: col
headerText: qsTr("AmneziaWG settings") anchors.top: parent.top
} anchors.left: parent.left
anchors.right: parent.right
TextFieldWithHeaderType { anchors.leftMargin: 16
id: mtuTextField anchors.rightMargin: 16
Layout.fillWidth: true
Layout.topMargin: 40
headerText: qsTr("MTU") spacing: 0
textFieldText: clientMtu
textField.validator: IntValidator { bottom: 576; top: 65535 }
textField.onEditingFinished: { HeaderType {
if (textFieldText !== clientMtu) { Layout.fillWidth: true
clientMtu = textFieldText
}
}
checkEmptyText: true
KeyNavigation.tab: junkPacketCountTextField.textField
}
TextFieldWithHeaderType { headerText: qsTr("AmneziaWG settings")
id: junkPacketCountTextField }
Layout.fillWidth: true
Layout.topMargin: 16
headerText: "Jc - Junk packet count" TextFieldWithHeaderType {
textFieldText: clientJunkPacketCount id: mtuTextField
textField.validator: IntValidator { bottom: 0 } Layout.fillWidth: true
parentFlickable: fl Layout.topMargin: 40
textField.onEditingFinished: { headerText: qsTr("MTU")
if (textFieldText !== clientJunkPacketCount) { textFieldText: clientMtu
clientJunkPacketCount = textFieldText textField.validator: IntValidator { bottom: 576; top: 65535 }
}
}
checkEmptyText: true textField.onEditingFinished: {
if (textFieldText !== clientMtu) {
KeyNavigation.tab: junkPacketMinSizeTextField.textField clientMtu = textFieldText
}
TextFieldWithHeaderType {
id: junkPacketMinSizeTextField
Layout.fillWidth: true
Layout.topMargin: 16
headerText: "Jmin - Junk packet minimum size"
textFieldText: clientJunkPacketMinSize
textField.validator: IntValidator { bottom: 0 }
parentFlickable: fl
textField.onEditingFinished: {
if (textFieldText !== clientJunkPacketMinSize) {
clientJunkPacketMinSize = textFieldText
}
}
checkEmptyText: true
KeyNavigation.tab: junkPacketMaxSizeTextField.textField
}
TextFieldWithHeaderType {
id: junkPacketMaxSizeTextField
Layout.fillWidth: true
Layout.topMargin: 16
headerText: "Jmax - Junk packet maximum size"
textFieldText: clientJunkPacketMaxSize
textField.validator: IntValidator { bottom: 0 }
parentFlickable: fl
textField.onEditingFinished: {
if (textFieldText !== clientJunkPacketMaxSize) {
clientJunkPacketMaxSize = textFieldText
}
}
checkEmptyText: true
Keys.onTabPressed: saveButton.forceActiveFocus()
}
Header2TextType {
Layout.fillWidth: true
Layout.topMargin: 16
text: qsTr("Server settings")
}
TextFieldWithHeaderType {
id: portTextField
Layout.fillWidth: true
Layout.topMargin: 8
enabled: false
headerText: qsTr("Port")
textFieldText: port
}
TextFieldWithHeaderType {
id: initPacketJunkSizeTextField
Layout.fillWidth: true
Layout.topMargin: 16
enabled: false
headerText: "S1 - Init packet junk size"
textFieldText: serverInitPacketJunkSize
}
TextFieldWithHeaderType {
id: responsePacketJunkSizeTextField
Layout.fillWidth: true
Layout.topMargin: 16
enabled: false
headerText: "S2 - Response packet junk size"
textFieldText: serverResponsePacketJunkSize
}
TextFieldWithHeaderType {
id: initPacketMagicHeaderTextField
Layout.fillWidth: true
Layout.topMargin: 16
enabled: false
headerText: "H1 - Init packet magic header"
textFieldText: serverInitPacketMagicHeader
}
TextFieldWithHeaderType {
id: responsePacketMagicHeaderTextField
Layout.fillWidth: true
Layout.topMargin: 16
enabled: false
headerText: "H2 - Response packet magic header"
textFieldText: serverResponsePacketMagicHeader
}
TextFieldWithHeaderType {
id: underloadPacketMagicHeaderTextField
Layout.fillWidth: true
Layout.topMargin: 16
parentFlickable: fl
enabled: false
headerText: "H3 - Underload packet magic header"
textFieldText: serverUnderloadPacketMagicHeader
}
TextFieldWithHeaderType {
id: transportPacketMagicHeaderTextField
Layout.fillWidth: true
Layout.topMargin: 16
enabled: false
headerText: "H4 - Transport packet magic header"
textFieldText: serverTransportPacketMagicHeader
} }
} }
checkEmptyText: true
KeyNavigation.tab: junkPacketCountTextField.textField
}
TextFieldWithHeaderType {
id: junkPacketCountTextField
Layout.fillWidth: true
Layout.topMargin: 16
headerText: "Jc - Junk packet count"
textFieldText: clientJunkPacketCount
textField.validator: IntValidator { bottom: 0 }
textField.onEditingFinished: {
if (textFieldText !== clientJunkPacketCount) {
clientJunkPacketCount = textFieldText
}
}
checkEmptyText: true
KeyNavigation.tab: junkPacketMinSizeTextField.textField
}
TextFieldWithHeaderType {
id: junkPacketMinSizeTextField
Layout.fillWidth: true
Layout.topMargin: 16
headerText: "Jmin - Junk packet minimum size"
textFieldText: clientJunkPacketMinSize
textField.validator: IntValidator { bottom: 0 }
textField.onEditingFinished: {
if (textFieldText !== clientJunkPacketMinSize) {
clientJunkPacketMinSize = textFieldText
}
}
checkEmptyText: true
KeyNavigation.tab: junkPacketMaxSizeTextField.textField
}
TextFieldWithHeaderType {
id: junkPacketMaxSizeTextField
Layout.fillWidth: true
Layout.topMargin: 16
headerText: "Jmax - Junk packet maximum size"
textFieldText: clientJunkPacketMaxSize
textField.validator: IntValidator { bottom: 0 }
textField.onEditingFinished: {
if (textFieldText !== clientJunkPacketMaxSize) {
clientJunkPacketMaxSize = textFieldText
}
}
checkEmptyText: true
}
Header2TextType {
Layout.fillWidth: true
Layout.topMargin: 16
text: qsTr("Server settings")
}
TextFieldWithHeaderType {
id: portTextField
Layout.fillWidth: true
Layout.topMargin: 8
enabled: false
headerText: qsTr("Port")
textFieldText: port
}
TextFieldWithHeaderType {
id: initPacketJunkSizeTextField
Layout.fillWidth: true
Layout.topMargin: 16
enabled: false
headerText: "S1 - Init packet junk size"
textFieldText: serverInitPacketJunkSize
}
TextFieldWithHeaderType {
id: responsePacketJunkSizeTextField
Layout.fillWidth: true
Layout.topMargin: 16
enabled: false
headerText: "S2 - Response packet junk size"
textFieldText: serverResponsePacketJunkSize
}
TextFieldWithHeaderType {
id: initPacketMagicHeaderTextField
Layout.fillWidth: true
Layout.topMargin: 16
enabled: false
headerText: "H1 - Init packet magic header"
textFieldText: serverInitPacketMagicHeader
}
TextFieldWithHeaderType {
id: responsePacketMagicHeaderTextField
Layout.fillWidth: true
Layout.topMargin: 16
enabled: false
headerText: "H2 - Response packet magic header"
textFieldText: serverResponsePacketMagicHeader
}
TextFieldWithHeaderType {
id: underloadPacketMagicHeaderTextField
Layout.fillWidth: true
Layout.topMargin: 16
parentFlickable: fl
enabled: false
headerText: "H3 - Underload packet magic header"
textFieldText: serverUnderloadPacketMagicHeader
}
TextFieldWithHeaderType {
id: transportPacketMagicHeaderTextField
Layout.fillWidth: true
Layout.topMargin: 16
enabled: false
headerText: "H4 - Transport packet magic header"
textFieldText: serverTransportPacketMagicHeader
} }
} }
} }
@ -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,341 +30,357 @@ PageType {
BackButtonType { BackButtonType {
id: backButton id: backButton
KeyNavigation.tab: listview.currentItem.portTextField.textField
} }
} }
FlickableType { ListView {
id: fl id: listview
anchors.top: backButtonLayout.bottom anchors.top: backButtonLayout.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
contentHeight: content.implicitHeight
Column { width: parent.width
id: content
anchors.top: parent.top property bool isFocusable: true
anchors.left: parent.left
anchors.right: parent.right
ListView { Keys.onTabPressed: {
id: listview FocusController.nextKeyTabItem()
}
width: parent.width Keys.onBacktabPressed: {
height: listview.contentItem.height FocusController.previousKeyTabItem()
}
clip: true Keys.onUpPressed: {
interactive: false FocusController.nextKeyUpItem()
}
model: AwgConfigModel Keys.onDownPressed: {
FocusController.nextKeyDownItem()
}
delegate: Item { Keys.onLeftPressed: {
id: delegateItem FocusController.nextKeyLeftItem()
implicitWidth: listview.width }
implicitHeight: col.implicitHeight
property alias portTextField: portTextField Keys.onRightPressed: {
property bool isEnabled: ServersModel.isProcessedServerHasWriteAccess() FocusController.nextKeyRightItem()
}
ColumnLayout { clip: true
id: col
anchors.top: parent.top model: AwgConfigModel
anchors.left: parent.left
anchors.right: parent.right
anchors.leftMargin: 16 delegate: Item {
anchors.rightMargin: 16 id: delegateItem
implicitWidth: listview.width
implicitHeight: col.implicitHeight
spacing: 0 property alias portTextField: portTextField
property bool isEnabled: ServersModel.isProcessedServerHasWriteAccess()
HeaderType { ColumnLayout {
Layout.fillWidth: true id: col
headerText: qsTr("AmneziaWG settings") anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.leftMargin: 16
anchors.rightMargin: 16
spacing: 0
HeaderType {
Layout.fillWidth: true
headerText: qsTr("AmneziaWG settings")
}
TextFieldWithHeaderType {
id: portTextField
Layout.fillWidth: true
Layout.topMargin: 40
enabled: delegateItem.isEnabled
headerText: qsTr("Port")
textFieldText: port
textField.maximumLength: 5
textField.validator: IntValidator { bottom: 1; top: 65535 }
textField.onEditingFinished: {
if (textFieldText !== port) {
port = textFieldText
}
}
checkEmptyText: true
}
TextFieldWithHeaderType {
id: mtuTextField
Layout.fillWidth: true
Layout.topMargin: 16
headerText: qsTr("MTU")
textFieldText: mtu
textField.validator: IntValidator { bottom: 576; top: 65535 }
textField.onEditingFinished: {
if (textFieldText === "") {
textFieldText = "0"
}
if (textFieldText !== mtu) {
mtu = textFieldText
}
}
checkEmptyText: true
}
TextFieldWithHeaderType {
id: junkPacketCountTextField
Layout.fillWidth: true
Layout.topMargin: 16
headerText: qsTr("Jc - Junk packet count")
textFieldText: serverJunkPacketCount
textField.validator: IntValidator { bottom: 0 }
textField.onEditingFinished: {
if (textFieldText === "") {
textFieldText = "0"
} }
TextFieldWithHeaderType { if (textFieldText !== serverJunkPacketCount) {
id: portTextField serverJunkPacketCount = textFieldText
Layout.fillWidth: true }
Layout.topMargin: 40 }
enabled: delegateItem.isEnabled checkEmptyText: true
}
headerText: qsTr("Port") TextFieldWithHeaderType {
textFieldText: port id: junkPacketMinSizeTextField
textField.maximumLength: 5 Layout.fillWidth: true
textField.validator: IntValidator { bottom: 1; top: 65535 } Layout.topMargin: 16
parentFlickable: fl
textField.onEditingFinished: { headerText: qsTr("Jmin - Junk packet minimum size")
if (textFieldText !== port) { textFieldText: serverJunkPacketMinSize
port = textFieldText textField.validator: IntValidator { bottom: 0 }
}
textField.onEditingFinished: {
if (textFieldText !== serverJunkPacketMinSize) {
serverJunkPacketMinSize = textFieldText
}
}
checkEmptyText: true
}
TextFieldWithHeaderType {
id: junkPacketMaxSizeTextField
Layout.fillWidth: true
Layout.topMargin: 16
headerText: qsTr("Jmax - Junk packet maximum size")
textFieldText: serverJunkPacketMaxSize
textField.validator: IntValidator { bottom: 0 }
textField.onEditingFinished: {
if (textFieldText !== serverJunkPacketMaxSize) {
serverJunkPacketMaxSize = textFieldText
}
}
checkEmptyText: true
}
TextFieldWithHeaderType {
id: initPacketJunkSizeTextField
Layout.fillWidth: true
Layout.topMargin: 16
headerText: qsTr("S1 - Init packet junk size")
textFieldText: serverInitPacketJunkSize
textField.validator: IntValidator { bottom: 0 }
textField.onEditingFinished: {
if (textFieldText !== serverInitPacketJunkSize) {
serverInitPacketJunkSize = textFieldText
}
}
checkEmptyText: true
onActiveFocusChanged: {
if(activeFocus) {
listview.positionViewAtEnd()
}
}
}
TextFieldWithHeaderType {
id: responsePacketJunkSizeTextField
Layout.fillWidth: true
Layout.topMargin: 16
headerText: qsTr("S2 - Response packet junk size")
textFieldText: serverResponsePacketJunkSize
textField.validator: IntValidator { bottom: 0 }
textField.onEditingFinished: {
if (textFieldText !== serverResponsePacketJunkSize) {
serverResponsePacketJunkSize = textFieldText
}
}
checkEmptyText: true
onActiveFocusChanged: {
if(activeFocus) {
listview.positionViewAtEnd()
}
}
}
TextFieldWithHeaderType {
id: initPacketMagicHeaderTextField
Layout.fillWidth: true
Layout.topMargin: 16
headerText: qsTr("H1 - Init packet magic header")
textFieldText: serverInitPacketMagicHeader
textField.validator: IntValidator { bottom: 0 }
textField.onEditingFinished: {
if (textFieldText !== serverInitPacketMagicHeader) {
serverInitPacketMagicHeader = textFieldText
}
}
checkEmptyText: true
}
TextFieldWithHeaderType {
id: responsePacketMagicHeaderTextField
Layout.fillWidth: true
Layout.topMargin: 16
headerText: qsTr("H2 - Response packet magic header")
textFieldText: serverResponsePacketMagicHeader
textField.validator: IntValidator { bottom: 0 }
textField.onEditingFinished: {
if (textFieldText !== serverResponsePacketMagicHeader) {
serverResponsePacketMagicHeader = textFieldText
}
}
checkEmptyText: true
}
TextFieldWithHeaderType {
id: transportPacketMagicHeaderTextField
Layout.fillWidth: true
Layout.topMargin: 16
headerText: qsTr("H4 - Transport packet magic header")
textFieldText: serverTransportPacketMagicHeader
textField.validator: IntValidator { bottom: 0 }
textField.onEditingFinished: {
if (textFieldText !== serverTransportPacketMagicHeader) {
serverTransportPacketMagicHeader = textFieldText
}
}
checkEmptyText: true
}
TextFieldWithHeaderType {
id: underloadPacketMagicHeaderTextField
Layout.fillWidth: true
Layout.topMargin: 16
headerText: qsTr("H3 - Underload packet magic header")
textFieldText: serverUnderloadPacketMagicHeader
textField.validator: IntValidator { bottom: 0 }
textField.onEditingFinished: {
if (textFieldText !== serverUnderloadPacketMagicHeader) {
serverUnderloadPacketMagicHeader = textFieldText
}
}
checkEmptyText: true
}
BasicButtonType {
id: saveRestartButton
Layout.fillWidth: true
Layout.topMargin: 24
Layout.bottomMargin: 24
enabled: underloadPacketMagicHeaderTextField.errorText === "" &&
transportPacketMagicHeaderTextField.errorText === "" &&
responsePacketMagicHeaderTextField.errorText === "" &&
initPacketMagicHeaderTextField.errorText === "" &&
responsePacketJunkSizeTextField.errorText === "" &&
initPacketJunkSizeTextField.errorText === "" &&
junkPacketMaxSizeTextField.errorText === "" &&
junkPacketMinSizeTextField.errorText === "" &&
junkPacketCountTextField.errorText === "" &&
portTextField.errorText === ""
text: qsTr("Save")
onActiveFocusChanged: {
if(activeFocus) {
listview.positionViewAtEnd()
}
}
clickedFunc: function() {
forceActiveFocus()
if (delegateItem.isEnabled) {
if (AwgConfigModel.isHeadersEqual(underloadPacketMagicHeaderTextField.textField.text,
transportPacketMagicHeaderTextField.textField.text,
responsePacketMagicHeaderTextField.textField.text,
initPacketMagicHeaderTextField.textField.text)) {
PageController.showErrorMessage(qsTr("The values of the H1-H4 fields must be unique"))
return
} }
checkEmptyText: true if (AwgConfigModel.isPacketSizeEqual(parseInt(initPacketJunkSizeTextField.textField.text),
parseInt(responsePacketJunkSizeTextField.textField.text))) {
KeyNavigation.tab: junkPacketCountTextField.textField PageController.showErrorMessage(qsTr("The value of the field S1 + message initiation size (148) must not equal S2 + message response size (92)"))
} return
TextFieldWithHeaderType {
id: junkPacketCountTextField
Layout.fillWidth: true
Layout.topMargin: 16
headerText: qsTr("Jc - Junk packet count")
textFieldText: serverJunkPacketCount
textField.validator: IntValidator { bottom: 0 }
parentFlickable: fl
textField.onEditingFinished: {
if (textFieldText === "") {
textFieldText = "0"
}
if (textFieldText !== serverJunkPacketCount) {
serverJunkPacketCount = textFieldText
}
}
checkEmptyText: true
KeyNavigation.tab: junkPacketMinSizeTextField.textField
}
TextFieldWithHeaderType {
id: junkPacketMinSizeTextField
Layout.fillWidth: true
Layout.topMargin: 16
headerText: qsTr("Jmin - Junk packet minimum size")
textFieldText: serverJunkPacketMinSize
textField.validator: IntValidator { bottom: 0 }
parentFlickable: fl
textField.onEditingFinished: {
if (textFieldText !== serverJunkPacketMinSize) {
serverJunkPacketMinSize = textFieldText
}
}
checkEmptyText: true
KeyNavigation.tab: junkPacketMaxSizeTextField.textField
}
TextFieldWithHeaderType {
id: junkPacketMaxSizeTextField
Layout.fillWidth: true
Layout.topMargin: 16
headerText: qsTr("Jmax - Junk packet maximum size")
textFieldText: serverJunkPacketMaxSize
textField.validator: IntValidator { bottom: 0 }
parentFlickable: fl
textField.onEditingFinished: {
if (textFieldText !== serverJunkPacketMaxSize) {
serverJunkPacketMaxSize = textFieldText
}
}
checkEmptyText: true
KeyNavigation.tab: initPacketJunkSizeTextField.textField
}
TextFieldWithHeaderType {
id: initPacketJunkSizeTextField
Layout.fillWidth: true
Layout.topMargin: 16
headerText: qsTr("S1 - Init packet junk size")
textFieldText: serverInitPacketJunkSize
textField.validator: IntValidator { bottom: 0 }
parentFlickable: fl
textField.onEditingFinished: {
if (textFieldText !== serverInitPacketJunkSize) {
serverInitPacketJunkSize = textFieldText
}
}
checkEmptyText: true
KeyNavigation.tab: responsePacketJunkSizeTextField.textField
}
TextFieldWithHeaderType {
id: responsePacketJunkSizeTextField
Layout.fillWidth: true
Layout.topMargin: 16
headerText: qsTr("S2 - Response packet junk size")
textFieldText: serverResponsePacketJunkSize
textField.validator: IntValidator { bottom: 0 }
parentFlickable: fl
textField.onEditingFinished: {
if (textFieldText !== serverResponsePacketJunkSize) {
serverResponsePacketJunkSize = textFieldText
}
}
checkEmptyText: true
KeyNavigation.tab: initPacketMagicHeaderTextField.textField
}
TextFieldWithHeaderType {
id: initPacketMagicHeaderTextField
Layout.fillWidth: true
Layout.topMargin: 16
headerText: qsTr("H1 - Init packet magic header")
textFieldText: serverInitPacketMagicHeader
textField.validator: IntValidator { bottom: 0 }
parentFlickable: fl
textField.onEditingFinished: {
if (textFieldText !== serverInitPacketMagicHeader) {
serverInitPacketMagicHeader = textFieldText
}
}
checkEmptyText: true
KeyNavigation.tab: responsePacketMagicHeaderTextField.textField
}
TextFieldWithHeaderType {
id: responsePacketMagicHeaderTextField
Layout.fillWidth: true
Layout.topMargin: 16
headerText: qsTr("H2 - Response packet magic header")
textFieldText: serverResponsePacketMagicHeader
textField.validator: IntValidator { bottom: 0 }
parentFlickable: fl
textField.onEditingFinished: {
if (textFieldText !== serverResponsePacketMagicHeader) {
serverResponsePacketMagicHeader = textFieldText
}
}
checkEmptyText: true
KeyNavigation.tab: transportPacketMagicHeaderTextField.textField
}
TextFieldWithHeaderType {
id: transportPacketMagicHeaderTextField
Layout.fillWidth: true
Layout.topMargin: 16
headerText: qsTr("H4 - Transport packet magic header")
textFieldText: serverTransportPacketMagicHeader
textField.validator: IntValidator { bottom: 0 }
parentFlickable: fl
textField.onEditingFinished: {
if (textFieldText !== serverTransportPacketMagicHeader) {
serverTransportPacketMagicHeader = textFieldText
}
}
checkEmptyText: true
KeyNavigation.tab: underloadPacketMagicHeaderTextField.textField
}
TextFieldWithHeaderType {
id: underloadPacketMagicHeaderTextField
Layout.fillWidth: true
Layout.topMargin: 16
parentFlickable: fl
headerText: qsTr("H3 - Underload packet magic header")
textFieldText: serverUnderloadPacketMagicHeader
textField.validator: IntValidator { bottom: 0 }
textField.onEditingFinished: {
if (textFieldText !== serverUnderloadPacketMagicHeader) {
serverUnderloadPacketMagicHeader = textFieldText
}
}
checkEmptyText: true
KeyNavigation.tab: saveRestartButton
}
BasicButtonType {
id: saveRestartButton
parentFlickable: fl
Layout.fillWidth: true
Layout.topMargin: 24
Layout.bottomMargin: 24
enabled: underloadPacketMagicHeaderTextField.errorText === "" &&
transportPacketMagicHeaderTextField.errorText === "" &&
responsePacketMagicHeaderTextField.errorText === "" &&
initPacketMagicHeaderTextField.errorText === "" &&
responsePacketJunkSizeTextField.errorText === "" &&
initPacketJunkSizeTextField.errorText === "" &&
junkPacketMaxSizeTextField.errorText === "" &&
junkPacketMinSizeTextField.errorText === "" &&
junkPacketCountTextField.errorText === "" &&
portTextField.errorText === ""
text: qsTr("Save")
Keys.onTabPressed: lastItemTabClicked(focusItem)
clickedFunc: function() {
forceActiveFocus()
if (delegateItem.isEnabled) {
if (AwgConfigModel.isHeadersEqual(underloadPacketMagicHeaderTextField.textField.text,
transportPacketMagicHeaderTextField.textField.text,
responsePacketMagicHeaderTextField.textField.text,
initPacketMagicHeaderTextField.textField.text)) {
PageController.showErrorMessage(qsTr("The values of the H1-H4 fields must be unique"))
return
}
if (AwgConfigModel.isPacketSizeEqual(parseInt(initPacketJunkSizeTextField.textField.text),
parseInt(responsePacketJunkSizeTextField.textField.text))) {
PageController.showErrorMessage(qsTr("The value of the field S1 + message initiation size (148) must not equal S2 + message response size (92)"))
return
}
}
var headerText = qsTr("Save settings?")
var descriptionText = qsTr("All users with whom you shared a connection with will no longer be able to connect to it.")
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() {
if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) {
PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection"))
return
}
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
InstallController.updateContainer(AwgConfigModel.getConfig())
}
var noButtonFunction = function() {
if (!GC.isMobile()) {
saveRestartButton.forceActiveFocus()
}
}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
} }
} }
var headerText = qsTr("Save settings?")
var descriptionText = qsTr("All users with whom you shared a connection with will no longer be able to connect to it.")
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() {
if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) {
PageController.showNotificationMessage(qsTr("Unable change settings while there is an active connection"))
return
}
PageController.goToPage(PageEnum.PageSetupWizardInstalling);
InstallController.updateContainer(AwgConfigModel.getConfig())
}
var noButtonFunction = function() {
if (!GC.isMobile()) {
saveRestartButton.forceActiveFocus()
}
}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
} }
} }
} }

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 anchors.right: parent.right
anchors.left: parent.left
ColumnLayout { property bool isFocusable: true
id: content
anchors.top: parent.top Keys.onTabPressed: {
anchors.left: parent.left FocusController.nextKeyTabItem()
anchors.right: parent.right }
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") footer: ColumnLayout {
descriptionText: qsTr("For reviews and bug reports") width: listView.width
leftImageSource: "qrc:/images/controls/mail.svg"
KeyNavigation.tab: githubButton
parentFlickable: fl
clickedFunction: function() {
GC.copyToClipBoard(text)
PageController.showNotificationMessage(qsTr("Copied"))
}
}
DividerType {}
LabelWithButtonType {
id: githubButton
Layout.fillWidth: true
text: qsTr("GitHub")
leftImageSource: "qrc:/images/controls/github.svg"
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,35 +233,30 @@ 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")
} }
} }
BasicButtonType { BasicButtonType {
id: privacyPolicyButton id: privacyPolicyButton
Layout.alignment: Qt.AlignHCenter
Layout.bottomMargin: 16
Layout.topMargin: -15
implicitHeight: 25
defaultColor: AmneziaStyle.color.transparent Layout.alignment: Qt.AlignHCenter
hoveredColor: AmneziaStyle.color.translucentWhite Layout.bottomMargin: 16
pressedColor: AmneziaStyle.color.sheerWhite Layout.topMargin: -15
disabledColor: AmneziaStyle.color.mutedGray implicitHeight: 25
textColor: AmneziaStyle.color.goldenApricot
text: qsTr("Privacy Policy") defaultColor: AmneziaStyle.color.transparent
hoveredColor: AmneziaStyle.color.translucentWhite
pressedColor: AmneziaStyle.color.sheerWhite
disabledColor: AmneziaStyle.color.mutedGray
textColor: AmneziaStyle.color.goldenApricot
Keys.onTabPressed: lastItemTabClicked() text: qsTr("Privacy Policy")
parentFlickable: fl
clickedFunc: function() { clickedFunc: function() {
Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl() + "/policy") Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl() + "/policy")
} }
} }
} }
} }

View file

@ -31,72 +31,68 @@ PageType {
id: containersRadioButtonGroup id: containersRadioButtonGroup
} }
delegate: Item { delegate: ColumnLayout {
id: content
implicitWidth: parent.width implicitWidth: parent.width
implicitHeight: content.implicitHeight implicitHeight: content.implicitHeight
ColumnLayout { RowLayout {
id: content VerticalRadioButton {
id: containerRadioButton
anchors.fill: parent
RowLayout {
VerticalRadioButton {
id: containerRadioButton
Layout.fillWidth: true
Layout.leftMargin: 16
text: countryName
ButtonGroup.group: containersRadioButtonGroup
imageSource: "qrc:/images/controls/download.svg"
checked: index === ApiCountryModel.currentIndex
onClicked: {
if (index !== ApiCountryModel.currentIndex) {
PageController.showBusyIndicator(true)
var prevIndex = ApiCountryModel.currentIndex
ApiCountryModel.currentIndex = index
if (!InstallController.updateServiceFromApi(ServersModel.defaultIndex, countryCode, countryName)) {
ApiCountryModel.currentIndex = prevIndex
}
}
}
MouseArea {
anchors.fill: containerRadioButton
cursorShape: Qt.PointingHandCursor
enabled: false
}
Keys.onEnterPressed: {
if (checkable) {
checked = true
}
containerRadioButton.clicked()
}
Keys.onReturnPressed: {
if (checkable) {
checked = true
}
containerRadioButton.clicked()
}
}
Image {
Layout.rightMargin: 32
Layout.alignment: Qt.AlignRight
source: "qrc:/countriesFlags/images/flagKit/" + countryImageCode + ".svg"
}
}
DividerType {
Layout.fillWidth: true Layout.fillWidth: true
Layout.leftMargin: 16
text: countryName
ButtonGroup.group: containersRadioButtonGroup
imageSource: "qrc:/images/controls/download.svg"
checked: index === ApiCountryModel.currentIndex
onClicked: {
if (index !== ApiCountryModel.currentIndex) {
PageController.showBusyIndicator(true)
var prevIndex = ApiCountryModel.currentIndex
ApiCountryModel.currentIndex = index
if (!InstallController.updateServiceFromApi(ServersModel.defaultIndex, countryCode, countryName)) {
ApiCountryModel.currentIndex = prevIndex
}
}
}
MouseArea {
anchors.fill: containerRadioButton
cursorShape: Qt.PointingHandCursor
enabled: false
}
Keys.onEnterPressed: {
if (checkable) {
checked = true
}
containerRadioButton.clicked()
}
Keys.onReturnPressed: {
if (checkable) {
checked = true
}
containerRadioButton.clicked()
}
} }
Image {
Layout.rightMargin: 32
Layout.alignment: Qt.AlignRight
source: "qrc:/countriesFlags/images/flagKit/" + countryImageCode + ".svg"
}
}
DividerType {
Layout.fillWidth: true
} }
} }
} }

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,22 +23,122 @@ 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
property string title: qsTr("Client logs")
property string description: qsTr("AmneziaVPN logs")
property bool isVisible: true
property var openLogsHandler: function() {
SettingsController.openLogsFolder()
}
property var exportLogsHandler: function() {
var fileName = ""
if (GC.isMobile()) {
fileName = "AmneziaVPN.log"
} else {
fileName = SystemController.getFileName(qsTr("Save"),
qsTr("Logs files (*.log)"),
StandardPaths.standardLocations(StandardPaths.DocumentsLocation) + "/AmneziaVPN",
true,
".log")
}
if (fileName !== "") {
PageController.showBusyIndicator(true)
SettingsController.exportLogsFile(fileName)
PageController.showBusyIndicator(false)
PageController.showNotificationMessage(qsTr("Logs file saved"))
}
}
}
QtObject {
id: serviceLogs
property string title: qsTr("Service logs")
property string description: qsTr("AmneziaVPN-service logs")
property bool isVisible: !GC.isMobile()
property var openLogsHandler: function() {
SettingsController.openServiceLogsFolder()
}
property var exportLogsHandler: function() {
var fileName = ""
if (GC.isMobile()) {
fileName = "AmneziaVPN-service.log"
} else {
fileName = SystemController.getFileName(qsTr("Save"),
qsTr("Logs files (*.log)"),
StandardPaths.standardLocations(StandardPaths.DocumentsLocation) + "/AmneziaVPN-service",
true,
".log")
}
if (fileName !== "") {
PageController.showBusyIndicator(true)
SettingsController.exportServiceLogsFile(fileName)
PageController.showBusyIndicator(false)
PageController.showNotificationMessage(qsTr("Logs file saved"))
}
}
}
property list<QtObject> logTypes: [
clientLogs,
serviceLogs
]
ListView {
id: listView
anchors.top: backButton.bottom anchors.top: backButton.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
contentHeight: content.height anchors.right: parent.right
anchors.left: parent.left
ColumnLayout { property bool isFocusable: true
id: content
Keys.onTabPressed: {
FocusController.nextKeyTabItem()
}
Keys.onBacktabPressed: {
FocusController.previousKeyTabItem()
}
Keys.onUpPressed: {
FocusController.nextKeyUpItem()
}
Keys.onDownPressed: {
FocusController.nextKeyDownItem()
}
Keys.onLeftPressed: {
FocusController.nextKeyLeftItem()
}
Keys.onRightPressed: {
FocusController.nextKeyRightItem()
}
ScrollBar.vertical: ScrollBar {
policy: ScrollBar.AsNeeded
}
model: logTypes
spacing: 24
snapMode: ListView.SnapOneItem
reuseItems: true
clip: true
header: ColumnLayout {
id: headerContent
width: listView.width
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
spacing: 0 spacing: 0
HeaderType { HeaderType {
@ -60,6 +153,7 @@ PageType {
SwitcherType { SwitcherType {
id: switcher id: switcher
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 16 Layout.topMargin: 16
Layout.leftMargin: 16 Layout.leftMargin: 16
@ -68,7 +162,7 @@ PageType {
text: qsTr("Enable logs") text: qsTr("Enable logs")
checked: SettingsController.isLoggingEnabled checked: SettingsController.isLoggingEnabled
//KeyNavigation.tab: openFolderButton
onCheckedChanged: { onCheckedChanged: {
if (checked !== SettingsController.isLoggingEnabled) { if (checked !== SettingsController.isLoggingEnabled) {
SettingsController.isLoggingEnabled = checked SettingsController.isLoggingEnabled = checked
@ -79,7 +173,6 @@ PageType {
DividerType {} DividerType {}
LabelWithButtonType { LabelWithButtonType {
// id: labelWithButton2
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: -8 Layout.topMargin: -8
@ -87,8 +180,6 @@ PageType {
leftImageSource: "qrc:/images/controls/trash.svg" leftImageSource: "qrc:/images/controls/trash.svg"
isSmallLeftImage: true isSmallLeftImage: true
// KeyNavigation.tab: labelWithButton3
clickedFunction: function() { clickedFunction: function() {
var headerText = qsTr("Clear logs?") var headerText = qsTr("Clear logs?")
var yesButtonText = qsTr("Continue") var yesButtonText = qsTr("Continue")
@ -99,19 +190,25 @@ PageType {
SettingsController.clearLogs() SettingsController.clearLogs()
PageController.showBusyIndicator(false) PageController.showBusyIndicator(false)
PageController.showNotificationMessage(qsTr("Logs have been cleaned up")) PageController.showNotificationMessage(qsTr("Logs have been cleaned up"))
if (!GC.isMobile()) {
focusItem.forceActiveFocus()
}
} }
var noButtonFunction = function() { var noButtonFunction = function() {
if (!GC.isMobile()) {
focusItem.forceActiveFocus()
}
} }
showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
} }
} }
}
delegate: ColumnLayout {
id: delegateContent
width: listView.width
spacing: 0
visible: isVisible
ListItemTitleType { ListItemTitleType {
Layout.fillWidth: true Layout.fillWidth: true
@ -119,7 +216,7 @@ PageType {
Layout.leftMargin: 16 Layout.leftMargin: 16
Layout.rightMargin: 16 Layout.rightMargin: 16
text: qsTr("Client logs") text: title
} }
ParagraphTextType { ParagraphTextType {
@ -129,11 +226,11 @@ PageType {
Layout.rightMargin: 16 Layout.rightMargin: 16
color: AmneziaStyle.color.mutedGray color: AmneziaStyle.color.mutedGray
text: qsTr("AmneziaVPN logs")
text: description
} }
LabelWithButtonType { LabelWithButtonType {
// id: labelWithButton2
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: -8 Layout.topMargin: -8
Layout.bottomMargin: -8 Layout.bottomMargin: -8
@ -142,17 +239,12 @@ PageType {
leftImageSource: "qrc:/images/controls/folder-open.svg" leftImageSource: "qrc:/images/controls/folder-open.svg"
isSmallLeftImage: true isSmallLeftImage: true
// KeyNavigation.tab: labelWithButton3 clickedFunction: openLogsHandler
clickedFunction: function() {
SettingsController.openLogsFolder()
}
} }
DividerType {} DividerType {}
LabelWithButtonType { LabelWithButtonType {
// id: labelWithButton2
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: -8 Layout.topMargin: -8
Layout.bottomMargin: -8 Layout.bottomMargin: -8
@ -161,115 +253,10 @@ PageType {
leftImageSource: "qrc:/images/controls/save.svg" leftImageSource: "qrc:/images/controls/save.svg"
isSmallLeftImage: true isSmallLeftImage: true
// KeyNavigation.tab: labelWithButton3 clickedFunction: exportLogsHandler
clickedFunction: function() {
var fileName = ""
if (GC.isMobile()) {
fileName = "AmneziaVPN.log"
} else {
fileName = SystemController.getFileName(qsTr("Save"),
qsTr("Logs files (*.log)"),
StandardPaths.standardLocations(StandardPaths.DocumentsLocation) + "/AmneziaVPN",
true,
".log")
}
if (fileName !== "") {
PageController.showBusyIndicator(true)
SettingsController.exportLogsFile(fileName)
PageController.showBusyIndicator(false)
PageController.showNotificationMessage(qsTr("Logs file saved"))
}
}
} }
DividerType {} DividerType {}
ListItemTitleType {
visible: !GC.isMobile()
Layout.fillWidth: true
Layout.topMargin: 32
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("Service logs")
}
ParagraphTextType {
visible: !GC.isMobile()
Layout.fillWidth: true
Layout.topMargin: 8
Layout.leftMargin: 16
Layout.rightMargin: 16
color: AmneziaStyle.color.mutedGray
text: qsTr("AmneziaVPN-service logs")
}
LabelWithButtonType {
// id: labelWithButton2
visible: !GC.isMobile()
Layout.fillWidth: true
Layout.topMargin: -8
Layout.bottomMargin: -8
text: qsTr("Open logs folder")
leftImageSource: "qrc:/images/controls/folder-open.svg"
isSmallLeftImage: true
// KeyNavigation.tab: labelWithButton3
clickedFunction: function() {
SettingsController.openServiceLogsFolder()
}
}
DividerType {
visible: !GC.isMobile()
}
LabelWithButtonType {
// id: labelWithButton2
visible: !GC.isMobile()
Layout.fillWidth: true
Layout.topMargin: -8
Layout.bottomMargin: -8
text: qsTr("Export logs")
leftImageSource: "qrc:/images/controls/save.svg"
isSmallLeftImage: true
// KeyNavigation.tab: labelWithButton3
clickedFunction: function() {
var fileName = ""
if (GC.isMobile()) {
fileName = "AmneziaVPN-service.log"
} else {
fileName = SystemController.getFileName(qsTr("Save"),
qsTr("Logs files (*.log)"),
StandardPaths.standardLocations(StandardPaths.DocumentsLocation) + "/AmneziaVPN-service",
true,
".log")
}
if (fileName !== "") {
PageController.showBusyIndicator(true)
SettingsController.exportServiceLogsFile(fileName)
PageController.showBusyIndicator(false)
PageController.showNotificationMessage(qsTr("Logs file saved"))
}
}
}
DividerType {
visible: !GC.isMobile()
}
} }
} }
} }

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,147 +46,111 @@ 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 { BackButtonType {
id: header id: backButton
model: proxyServersModel objectName: "backButton"
activeFocusOnTab: true backButtonFunction: function() {
onFocusChanged: { if (nestedStackView.currentIndex === root.pageSettingsApiServerInfo
header.itemAt(0).focusItem.forceActiveFocus() && ServersModel.getProcessedServerData("isCountrySelectionAvailable")) {
nestedStackView.currentIndex = root.pageSettingsApiLanguageList
} else {
PageController.closePage()
}
}
}
HeaderType {
id: headerContent
objectName: "headerContent"
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
Layout.bottomMargin: 10
actionButtonImage: nestedStackView.currentIndex === root.pageSettingsApiLanguageList ? "qrc:/images/controls/settings.svg" : "qrc:/images/controls/edit-3.svg"
headerText: root.server.name
descriptionText: {
if (ServersModel.getProcessedServerData("isServerFromGatewayApi")) {
return ApiServicesModel.getSelectedServiceData("serviceDescription")
} else if (ServersModel.getProcessedServerData("isServerFromTelegramApi")) {
return root.server.serverDescription
} else if (ServersModel.isProcessedServerHasWriteAccess()) {
return root.server.credentialsLogin + " · " + root.server.hostName
} else {
return root.server.hostName
}
} }
delegate: ColumnLayout { actionButtonFunction: function() {
if (nestedStackView.currentIndex === root.pageSettingsApiLanguageList) {
property alias focusItem: backButton nestedStackView.currentIndex = root.pageSettingsApiServerInfo
} else {
id: content serverNameEditDrawer.openTriggered()
Layout.topMargin: 20
BackButtonType {
id: backButton
KeyNavigation.tab: headerContent.actionButton
backButtonFunction: function() {
if (nestedStackView.currentIndex === root.pageSettingsApiServerInfo &&
ServersModel.getProcessedServerData("isCountrySelectionAvailable")) {
nestedStackView.currentIndex = root.pageSettingsApiLanguageList
} else {
PageController.closePage()
}
}
} }
}
}
DrawerType2 {
id: serverNameEditDrawer
objectName: "serverNameEditDrawer"
parent: root
anchors.fill: parent
expandedHeight: root.height * 0.35
expandedStateContent: ColumnLayout {
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 32
anchors.leftMargin: 16
anchors.rightMargin: 16
TextFieldWithHeaderType {
id: serverName
HeaderType {
id: headerContent
Layout.fillWidth: true Layout.fillWidth: true
Layout.leftMargin: 16 headerText: qsTr("Server name")
Layout.rightMargin: 16 textFieldText: root.server.name
textField.maximumLength: 30
actionButtonImage: nestedStackView.currentIndex === root.pageSettingsApiLanguageList ? "qrc:/images/controls/settings.svg" : "qrc:/images/controls/edit-3.svg" checkEmptyText: true
headerText: name
descriptionText: {
if (ServersModel.getProcessedServerData("isServerFromGatewayApi")) {
return ApiServicesModel.getSelectedServiceData("serviceDescription")
} else if (ServersModel.getProcessedServerData("isServerFromTelegramApi")) {
return serverDescription
} else if (ServersModel.isProcessedServerHasWriteAccess()) {
return credentialsLogin + " · " + hostName
} else {
return hostName
}
}
KeyNavigation.tab: tabBar
actionButtonFunction: function() {
if (nestedStackView.currentIndex === root.pageSettingsApiLanguageList) {
nestedStackView.currentIndex = root.pageSettingsApiServerInfo
} else {
serverNameEditDrawer.open()
}
}
} }
DrawerType2 { BasicButtonType {
id: serverNameEditDrawer id: saveButton
parent: root Layout.fillWidth: true
anchors.fill: parent text: qsTr("Save")
expandedHeight: root.height * 0.35
onClosed: { clickedFunc: function() {
if (!GC.isMobile()) { if (serverName.textFieldText === "") {
headerContent.actionButton.forceActiveFocus() return
}
}
expandedContent: ColumnLayout {
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 32
anchors.leftMargin: 16
anchors.rightMargin: 16
Connections {
target: serverNameEditDrawer
enabled: !GC.isMobile()
function onOpened() {
serverName.textField.forceActiveFocus()
}
} }
Item { if (serverName.textFieldText !== root.server.name) {
id: focusItem1 ServersModel.setProcessedServerData("name", serverName.textFieldText);
KeyNavigation.tab: serverName.textField root.server = proxyServersModel.get(0);
}
TextFieldWithHeaderType {
id: serverName
Layout.fillWidth: true
headerText: qsTr("Server name")
textFieldText: name
textField.maximumLength: 30
checkEmptyText: true
KeyNavigation.tab: saveButton
}
BasicButtonType {
id: saveButton
Layout.fillWidth: true
text: qsTr("Save")
KeyNavigation.tab: focusItem1
clickedFunc: function() {
if (serverName.textFieldText === "") {
return
}
if (serverName.textFieldText !== name) {
name = serverName.textFieldText
}
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()
}
} }
delegate: Item { Keys.onBacktabPressed: {
property var focusItem: clientSettings.rightButton FocusController.previousKeyTabItem()
}
Keys.onUpPressed: {
FocusController.nextKeyUpItem()
}
Keys.onDownPressed: {
FocusController.nextKeyDownItem()
}
Keys.onLeftPressed: {
FocusController.nextKeyLeftItem()
}
Keys.onRightPressed: {
FocusController.nextKeyRightItem()
}
model: ProtocolsModel
delegate: Item {
implicitWidth: protocols.width implicitWidth: protocols.width
implicitHeight: delegateContent.implicitHeight implicitHeight: delegateContent.implicitHeight
@ -160,109 +158,112 @@ PageType {
} }
} }
} }
}
LabelWithButtonType { footer: ColumnLayout {
id: clearCacheButton width: header.width
Layout.fillWidth: true LabelWithButtonType {
id: clearCacheButton
visible: root.isClearCacheVisible Layout.fillWidth: true
KeyNavigation.tab: removeButton
text: qsTr("Clear profile") visible: root.isClearCacheVisible
clickedFunction: function() { text: qsTr("Clear profile")
var headerText = qsTr("Clear %1 profile?").arg(ContainersModel.getProcessedContainerName())
var descriptionText = qsTr("The connection configuration will be deleted for this device only")
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() { clickedFunction: function() {
if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) { var headerText = qsTr("Clear %1 profile?").arg(ContainersModel.getProcessedContainerName())
var message = qsTr("Unable to clear %1 profile while there is an active connection").arg(ContainersModel.getProcessedContainerName()) var descriptionText = qsTr("The connection configuration will be deleted for this device only")
PageController.showNotificationMessage(message) var yesButtonText = qsTr("Continue")
return var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() {
if (ConnectionController.isConnected && ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) {
var message = qsTr("Unable to clear %1 profile while there is an active connection").arg(ContainersModel.getProcessedContainerName())
PageController.showNotificationMessage(message)
return
}
PageController.showBusyIndicator(true)
InstallController.clearCachedProfile()
PageController.showBusyIndicator(false)
}
var noButtonFunction = function() {
// if (!GC.isMobile()) {
// focusItem.forceActiveFocus()
// }
}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
} }
PageController.showBusyIndicator(true) MouseArea {
InstallController.clearCachedProfile() anchors.fill: clearCacheButton
PageController.showBusyIndicator(false) cursorShape: Qt.PointingHandCursor
} enabled: false
var noButtonFunction = function() {
if (!GC.isMobile()) {
focusItem.forceActiveFocus()
} }
} }
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) DividerType {
} Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
MouseArea { visible: root.isClearCacheVisible
anchors.fill: clearCacheButton
cursorShape: Qt.PointingHandCursor
enabled: false
}
}
DividerType {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
visible: root.isClearCacheVisible
}
LabelWithButtonType {
id: removeButton
Layout.fillWidth: true
visible: ServersModel.isProcessedServerHasWriteAccess()
Keys.onTabPressed: lastItemTabClicked(focusItem)
text: qsTr("Remove ")
textColor: AmneziaStyle.color.vibrantRed
clickedFunction: function() {
var headerText = qsTr("Remove %1 from server?").arg(ContainersModel.getProcessedContainerName())
var descriptionText = qsTr("All users with whom you shared a connection will no longer be able to connect to it.")
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() {
if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected
&& ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) {
PageController.showNotificationMessage(qsTr("Cannot remove active container"))
} else
{
PageController.goToPage(PageEnum.PageDeinstalling)
InstallController.removeProcessedContainer()
}
} }
var noButtonFunction = function() {
if (!GC.isMobile()) { LabelWithButtonType {
focusItem.forceActiveFocus() id: removeButton
Layout.fillWidth: true
visible: ServersModel.isProcessedServerHasWriteAccess()
text: qsTr("Remove ")
textColor: AmneziaStyle.color.vibrantRed
clickedFunction: function() {
var headerText = qsTr("Remove %1 from server?").arg(ContainersModel.getProcessedContainerName())
var descriptionText = qsTr("All users with whom you shared a connection will no longer be able to connect to it.")
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() {
if (ServersModel.isDefaultServerCurrentlyProcessed() && ConnectionController.isConnected
&& ServersModel.getDefaultServerData("defaultContainer") === ContainersModel.getProcessedContainerIndex()) {
PageController.showNotificationMessage(qsTr("Cannot remove active container"))
} else
{
PageController.goToPage(PageEnum.PageDeinstalling)
InstallController.removeProcessedContainer()
}
}
var noButtonFunction = function() {
if (!GC.isMobile()) {
focusItem.forceActiveFocus()
}
}
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
}
MouseArea {
anchors.fill: removeButton
cursorShape: Qt.PointingHandCursor
enabled: false
} }
} }
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction) DividerType {
} Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
MouseArea { visible: ServersModel.isProcessedServerHasWriteAccess()
anchors.fill: removeButton }
cursorShape: Qt.PointingHandCursor
enabled: false
} }
} }
DividerType {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
visible: ServersModel.isProcessedServerHasWriteAccess()
}
} }
} }

View file

@ -21,53 +21,45 @@ PageType {
property var installedProtocolsCount property var installedProtocolsCount
onFocusChanged: settingsContainersListView.forceActiveFocus() function resetView() {
signal lastItemTabClickedSignal() settingsContainersListView.positionViewAtBeginning()
}
FlickableType { SettingsContainersListView {
id: fl id: settingsContainersListView
anchors.top: parent.top
anchors.bottom: parent.bottom
contentHeight: content.implicitHeight
Column { anchors.fill: parent
id: content
anchors.top: parent.top Connections {
anchors.left: parent.left target: ServersModel
anchors.right: parent.right
SettingsContainersListView { function onProcessedServerIndexChanged() {
id: settingsContainersListView settingsContainersListView.updateContainersModelFilters()
Connections {
target: ServersModel
function onProcessedServerIndexChanged() {
settingsContainersListView.updateContainersModelFilters()
}
}
function updateContainersModelFilters() {
if (ServersModel.isProcessedServerHasWriteAccess()) {
proxyContainersModel.filters = ContainersModelFilters.getWriteAccessProtocolsListFilters()
} else {
proxyContainersModel.filters = ContainersModelFilters.getReadAccessProtocolsListFilters()
}
root.installedProtocolsCount = proxyContainersModel.count
}
model: SortFilterProxyModel {
id: proxyContainersModel
sourceModel: ContainersModel
sorters: [
RoleSorter { roleName: "isInstalled"; sortOrder: Qt.DescendingOrder },
RoleSorter { roleName: "installPageOrder"; sortOrder: Qt.AscendingOrder }
]
}
Component.onCompleted: updateContainersModelFilters()
} }
} }
function updateContainersModelFilters() {
if (ServersModel.isProcessedServerHasWriteAccess()) {
proxyContainersModel.filters = ContainersModelFilters.getWriteAccessProtocolsListFilters()
} else {
proxyContainersModel.filters = ContainersModelFilters.getReadAccessProtocolsListFilters()
}
root.installedProtocolsCount = proxyContainersModel.count
}
model: SortFilterProxyModel {
id: proxyContainersModel
sourceModel: ContainersModel
sorters: [
RoleSorter { roleName: "isInstalled"; sortOrder: Qt.DescendingOrder },
RoleSorter { roleName: "installPageOrder"; sortOrder: Qt.AscendingOrder }
]
}
Component.onCompleted: {
settingsContainersListView.isFocusable = true
settingsContainersListView.interactive = true
updateContainersModelFilters()
}
} }
} }

View file

@ -21,52 +21,40 @@ PageType {
property var installedServicesCount property var installedServicesCount
onFocusChanged: settingsContainersListView.forceActiveFocus() SettingsContainersListView {
signal lastItemTabClickedSignal() id: settingsContainersListView
FlickableType { anchors.fill: parent
id: fl
anchors.top: parent.top
anchors.bottom: parent.bottom
contentHeight: content.implicitHeight
Column { Connections {
id: content target: ServersModel
anchors.top: parent.top function onProcessedServerIndexChanged() {
anchors.left: parent.left settingsContainersListView.updateContainersModelFilters()
anchors.right: parent.right
SettingsContainersListView {
id: settingsContainersListView
Connections {
target: ServersModel
function onProcessedServerIndexChanged() {
settingsContainersListView.updateContainersModelFilters()
}
}
function updateContainersModelFilters() {
if (ServersModel.isProcessedServerHasWriteAccess()) {
proxyContainersModel.filters = ContainersModelFilters.getWriteAccessServicesListFilters()
} else {
proxyContainersModel.filters = ContainersModelFilters.getReadAccessServicesListFilters()
}
root.installedServicesCount = proxyContainersModel.count
}
model: SortFilterProxyModel {
id: proxyContainersModel
sourceModel: ContainersModel
sorters: [
RoleSorter { roleName: "isInstalled"; sortOrder: Qt.DescendingOrder }
]
}
Component.onCompleted: updateContainersModelFilters()
} }
} }
function updateContainersModelFilters() {
if (ServersModel.isProcessedServerHasWriteAccess()) {
proxyContainersModel.filters = ContainersModelFilters.getWriteAccessServicesListFilters()
} else {
proxyContainersModel.filters = ContainersModelFilters.getReadAccessServicesListFilters()
}
root.installedServicesCount = proxyContainersModel.count
}
model: SortFilterProxyModel {
id: proxyContainersModel
sourceModel: ContainersModel
sorters: [
RoleSorter { roleName: "isInstalled"; sortOrder: Qt.DescendingOrder }
]
}
Component.onCompleted: {
settingsContainersListView.isFocusable = true
settingsContainersListView.interactive = true
updateContainersModelFilters()
}
} }
} }

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,95 +40,70 @@ 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 anchors.left: parent.left
anchors.right: parent.right
Column { height: 500 // servers.contentItem.height // TODO: calculate height
id: col
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
ListView { property bool isFocusable: true
id: servers
width: parent.width
height: servers.contentItem.height
model: ServersModel model: ServersModel
clip: true clip: true
interactive: false interactive: false
activeFocusOnTab: true onVisibleChanged: {
focus: true if (visible) {
Keys.onTabPressed: { currentIndex = 0
if (currentIndex < servers.count - 1) { }
servers.incrementCurrentIndex() }
} else {
servers.currentIndex = 0
focusItem.forceActiveFocus()
root.lastItemTabClicked()
}
fl.ensureVisible(this.currentItem) delegate: Item {
} implicitWidth: servers.width
implicitHeight: delegateContent.implicitHeight
onVisibleChanged: { ColumnLayout {
if (visible) { id: delegateContent
currentIndex = 0
}
}
delegate: Item { anchors.top: parent.top
implicitWidth: servers.width anchors.left: parent.left
implicitHeight: delegateContent.implicitHeight anchors.right: parent.right
onFocusChanged: { LabelWithButtonType {
if (focus) { id: server
server.rightButton.forceActiveFocus() Layout.fillWidth: true
}
}
ColumnLayout { text: name
id: delegateContent
anchors.top: parent.top descriptionText: {
anchors.left: parent.left var servicesNameString = ""
anchors.right: parent.right var servicesName = ServersModel.getAllInstalledServicesName(index)
for (var i = 0; i < servicesName.length; i++) {
LabelWithButtonType { servicesNameString += servicesName[i] + " · "
id: server
Layout.fillWidth: true
text: name
parentFlickable: fl
descriptionText: {
var servicesNameString = ""
var servicesName = ServersModel.getAllInstalledServicesName(index)
for (var i = 0; i < servicesName.length; i++) {
servicesNameString += servicesName[i] + " · "
}
if (ServersModel.isServerFromApi(index)) {
return servicesNameString + serverDescription
} else {
return servicesNameString + hostName
}
}
rightImageSource: "qrc:/images/controls/chevron-right.svg"
clickedFunction: function() {
ServersModel.processedIndex = index
PageController.goToPage(PageEnum.PageSettingsServerInfo)
}
} }
DividerType {} if (ServersModel.isServerFromApi(index)) {
return servicesNameString + serverDescription
} else {
return servicesNameString + hostName
}
}
rightImageSource: "qrc:/images/controls/chevron-right.svg"
clickedFunction: function() {
ServersModel.processedIndex = index
PageController.goToPage(PageEnum.PageSettingsServerInfo)
} }
} }
DividerType {}
} }
} }
} }

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,124 +169,120 @@ PageType {
} }
} }
} }
KeyNavigation.tab: {
return sites.count > 0 ?
sites :
searchField.textField
}
} }
} }
FlickableType { ListView {
id: fl id: sites
anchors.top: header.bottom anchors.top: header.bottom
anchors.topMargin: 16 anchors.topMargin: 16
contentHeight: col.implicitHeight + addSiteButton.implicitHeight + addSiteButton.anchors.bottomMargin + addSiteButton.anchors.topMargin width: parent.width
height: 200 // TODO: Change to correct height
enabled: root.pageEnabled enabled: root.pageEnabled
Column { property bool isFocusable: true
id: col
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
ListView { Keys.onTabPressed: {
id: sites FocusController.nextKeyTabItem()
width: parent.width }
height: sites.contentItem.height
model: SortFilterProxyModel { Keys.onBacktabPressed: {
id: proxySitesModel FocusController.previousKeyTabItem()
sourceModel: SitesModel }
filters: [
AnyOf { Keys.onUpPressed: {
RegExpFilter { FocusController.nextKeyUpItem()
roleName: "url" }
pattern: ".*" + searchField.textField.text + ".*"
caseSensitivity: Qt.CaseInsensitive Keys.onDownPressed: {
} FocusController.nextKeyDownItem()
RegExpFilter { }
roleName: "ip"
pattern: ".*" + searchField.textField.text + ".*" Keys.onLeftPressed: {
caseSensitivity: Qt.CaseInsensitive FocusController.nextKeyLeftItem()
}
Keys.onRightPressed: {
FocusController.nextKeyRightItem()
}
model: SortFilterProxyModel {
id: proxySitesModel
sourceModel: SitesModel
filters: [
AnyOf {
RegExpFilter {
roleName: "url"
pattern: ".*" + searchField.textField.text + ".*"
caseSensitivity: Qt.CaseInsensitive
}
RegExpFilter {
roleName: "ip"
pattern: ".*" + searchField.textField.text + ".*"
caseSensitivity: Qt.CaseInsensitive
}
}
]
}
clip: true
delegate: Item {
implicitWidth: sites.width
implicitHeight: delegateContent.implicitHeight
// onActiveFocusChanged: {
// if (activeFocus) {
// site.rightButton.forceActiveFocus()
// }
// }
ColumnLayout {
id: delegateContent
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
LabelWithButtonType {
id: site
Layout.fillWidth: true
text: url
descriptionText: ip
rightImageSource: "qrc:/images/controls/trash.svg"
rightImageColor: AmneziaStyle.color.paleGray
clickedFunction: function() {
var headerText = qsTr("Remove ") + url + "?"
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() {
SitesController.removeSite(proxySitesModel.mapToSource(index))
if (!GC.isMobile()) {
site.rightButton.forceActiveFocus()
} }
} }
] var noButtonFunction = function() {
} if (!GC.isMobile()) {
site.rightButton.forceActiveFocus()
clip: true
interactive: false
activeFocusOnTab: true
focus: true
Keys.onTabPressed: {
if (currentIndex < this.count - 1) {
this.incrementCurrentIndex()
} else {
currentIndex = 0
searchField.textField.forceActiveFocus()
}
fl.ensureVisible(currentItem)
}
delegate: Item {
implicitWidth: sites.width
implicitHeight: delegateContent.implicitHeight
onActiveFocusChanged: {
if (activeFocus) {
site.rightButton.forceActiveFocus()
}
}
ColumnLayout {
id: delegateContent
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
LabelWithButtonType {
id: site
Layout.fillWidth: true
text: url
descriptionText: ip
rightImageSource: "qrc:/images/controls/trash.svg"
rightImageColor: AmneziaStyle.color.paleGray
clickedFunction: function() {
var headerText = qsTr("Remove ") + url + "?"
var yesButtonText = qsTr("Continue")
var noButtonText = qsTr("Cancel")
var yesButtonFunction = function() {
SitesController.removeSite(proxySitesModel.mapToSource(index))
if (!GC.isMobile()) {
site.rightButton.forceActiveFocus()
}
}
var noButtonFunction = function() {
if (!GC.isMobile()) {
site.rightButton.forceActiveFocus()
}
}
showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
} }
} }
DividerType {} showQuestionDrawer(headerText, "", yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
} }
} }
DividerType {}
} }
} }
} }
Rectangle { Rectangle {
anchors.fill: addSiteButton anchors.fill: addSiteButton
anchors.bottomMargin: -24 anchors.bottomMargin: -24
@ -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) {
PageController.keyPressEvent(event.key) console.debug(">>>> ", event.key, " Event is caught by StartPage")
event.accepted = true 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
}
} }
} }
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)