Improve navigation cpp (#1061)
* add focusController class * add more key handlers * add focus navigation to qml * fixed language selector * add reverse focus change to FocusController * add default focus item * update transitions * update pages * add ListViewFocusController * fix ListView navigation * update CardType for using with focus navigation * remove useless key navigation * remove useless slots, logs, Drawer open and close * fix reverse focus move on listView * fix drawer radio buttons selection * fix drawer layout and focus move * fix PageSetupWizardProtocolSettings focus move * fix back navigation on default focus item * fix crashes after ListView navigation * fix protocol settings focus move * fix focus on users on page share * clean up page share * fix server rename * fix page share default server selection * refactor about page for correct focus move * fix focus move on list views with header and-or footer * minor fixes * fix server list back button handler * fix spawn signals on switch * fix share details drawer * fix drawer open close usage * refactor listViewFocusController * refactor focusController to make the logic more straightforward * fix focus on notification * update config page for scrolling with tab * fix crash on return with esc key * fix focus navigation in dynamic delegate of list view * fix focus move on qr code on share page * refactor page logging settings for focus navigation * update popup * Bump version * Add mandatory requirement for android.software.leanback. * Fix importing files on TVs * fix: add separate method for reading files to fix file reading on Android TV * fix(android): add CHANGE_NETWORK_STATE permission for all Android versions * Fix connection check for AWG/WG * chore: minor fixes (#1235) * fix: add a workaround to open files on Android TV due to lack of SAF * fix: change the banner format for TV * refactor: make TvFilePicker activity more sustainable * fix: add the touch emulation method for Android TV * fix: null uri processing * fix: add the touch emulation method for Android TV * fix: hide UI elements that use file saving * chore: bump version code * add `ScrollBarType` * update initial config page * refactor credentials setup page to handle the focus navigation * add `setDelegateIndex` method to `listViewFocusController` * fix focus behavior on new page/popup * make minor fixes and clean up * fix: get rid of the assign function call * Scrollbar is on if the content is larger than a screen * Fix selection in language change list * Update select language list * update logging settings page * fix checked item in lists * fix split tunneling settings * make unchangable properties readonly * refactor SwitcherType * fix hide/unhide password * `PageShare` readonly properties * Fix list view focus moving on `PageShare` * remove manual focus control on `PageShare` * format `ListViewFocusController` * format `FocusController` * add `focusControl` with utility functions for focus control * refactor `listViewFocusController` acoording to `focusControl` * refactor `focusConroller` according to `focusControl` * add `printSectionName` method to `listViewController` * remove arrow from `Close application` item * fix focus movement in `ServersListView` * `Restore from backup` is visible only on start screen * `I have nothing` is visible only on start screen * fix back button on `SelectLanguageDrawer` * rename `focusControl` to `qmlUtils` * fix `CMakeLists.txt` * fix `ScrollBarType` * fix `PageSetupWizardApiServicesList` * fix focus movement on dynamic delegates in listView * refactor `PageSetupWizardProtocols` * remove comments and clean up * fix `ListViewWithLabelsType` * fix `PageProtocolCloakSettings` * fix `PageSettingsAppSplitTunneling` * fix `PageDevMenu` * remove debug output from `FocusController` * remove debug output from `ListViewFocusController` * remove debug output from `focusControl` * `focusControl` => `FocusControl` --------- Co-authored-by: albexk <albexk@proton.me> Co-authored-by: Nethius <nethiuswork@gmail.com>
This commit is contained in:
parent
212e9b3a91
commit
6acaab0ffa
109 changed files with 4036 additions and 3700 deletions
|
@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
|
||||||
|
|
||||||
set(PROJECT AmneziaVPN)
|
set(PROJECT AmneziaVPN)
|
||||||
|
|
||||||
project(${PROJECT} VERSION 4.8.2.4
|
project(${PROJECT} VERSION 4.8.3.0
|
||||||
DESCRIPTION "AmneziaVPN"
|
DESCRIPTION "AmneziaVPN"
|
||||||
HOMEPAGE_URL "https://amnezia.org/"
|
HOMEPAGE_URL "https://amnezia.org/"
|
||||||
)
|
)
|
||||||
|
@ -11,7 +11,7 @@ string(TIMESTAMP CURRENT_DATE "%Y-%m-%d")
|
||||||
set(RELEASE_DATE "${CURRENT_DATE}")
|
set(RELEASE_DATE "${CURRENT_DATE}")
|
||||||
|
|
||||||
set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
|
set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
|
||||||
set(APP_ANDROID_VERSION_CODE 2071)
|
set(APP_ANDROID_VERSION_CODE 2072)
|
||||||
|
|
||||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||||
set(MZ_PLATFORM_NAME "linux")
|
set(MZ_PLATFORM_NAME "linux")
|
||||||
|
|
|
@ -146,6 +146,7 @@ set(HEADERS ${HEADERS}
|
||||||
${CMAKE_CURRENT_LIST_DIR}/core/serialization/transfer.h
|
${CMAKE_CURRENT_LIST_DIR}/core/serialization/transfer.h
|
||||||
${CMAKE_CURRENT_LIST_DIR}/core/enums/apiEnums.h
|
${CMAKE_CURRENT_LIST_DIR}/core/enums/apiEnums.h
|
||||||
${CMAKE_CURRENT_LIST_DIR}/../common/logger/logger.h
|
${CMAKE_CURRENT_LIST_DIR}/../common/logger/logger.h
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/utils/qmlUtils.h
|
||||||
)
|
)
|
||||||
|
|
||||||
# Mozilla headres
|
# Mozilla headres
|
||||||
|
@ -197,6 +198,7 @@ set(SOURCES ${SOURCES}
|
||||||
${CMAKE_CURRENT_LIST_DIR}/core/serialization/vmess.cpp
|
${CMAKE_CURRENT_LIST_DIR}/core/serialization/vmess.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/core/serialization/vmess_new.cpp
|
${CMAKE_CURRENT_LIST_DIR}/core/serialization/vmess_new.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/../common/logger/logger.cpp
|
${CMAKE_CURRENT_LIST_DIR}/../common/logger/logger.cpp
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/utils/qmlUtils.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
# Mozilla sources
|
# Mozilla sources
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
<uses-feature android:name="android.hardware.camera.any" android:required="false" />
|
<uses-feature android:name="android.hardware.camera.any" android:required="false" />
|
||||||
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
|
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
|
||||||
<!-- for TV -->
|
<!-- for TV -->
|
||||||
<uses-feature android:name="android.software.leanback" android:required="false" />
|
<uses-feature android:name="android.software.leanback" android:required="true" />
|
||||||
<uses-feature android:name="android.hardware.touchscreen" android:required="false" />
|
<uses-feature android:name="android.hardware.touchscreen" android:required="false" />
|
||||||
|
|
||||||
<!-- The following comment will be replaced upon deployment with default features based on the dependencies
|
<!-- The following comment will be replaced upon deployment with default features based on the dependencies
|
||||||
|
@ -91,6 +91,13 @@
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:theme="@style/Translucent" />
|
android:theme="@style/Translucent" />
|
||||||
|
|
||||||
|
<activity android:name=".TvFilePicker"
|
||||||
|
android:excludeFromRecents="true"
|
||||||
|
android:launchMode="singleTask"
|
||||||
|
android:taskAffinity=""
|
||||||
|
android:exported="false"
|
||||||
|
android:theme="@style/Translucent" />
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".ImportConfigActivity"
|
android:name=".ImportConfigActivity"
|
||||||
android:excludeFromRecents="true"
|
android:excludeFromRecents="true"
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<background android:drawable="@color/ic_banner_background"/>
|
|
||||||
<foreground android:drawable="@mipmap/ic_banner_foreground"/>
|
|
||||||
</adaptive-icon>
|
|
BIN
client/android/res/mipmap-hdpi/ic_banner.png
Normal file
BIN
client/android/res/mipmap-hdpi/ic_banner.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
BIN
client/android/res/mipmap-mdpi/ic_banner.png
Normal file
BIN
client/android/res/mipmap-mdpi/ic_banner.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 9.9 KiB |
Binary file not shown.
Before Width: | Height: | Size: 12 KiB |
|
@ -23,4 +23,6 @@
|
||||||
<string name="notificationSettingsDialogTitle">Настройки уведомлений</string>
|
<string name="notificationSettingsDialogTitle">Настройки уведомлений</string>
|
||||||
<string name="notificationSettingsDialogMessage">Для показа уведомлений необходимо включить уведомления в системных настройках</string>
|
<string name="notificationSettingsDialogMessage">Для показа уведомлений необходимо включить уведомления в системных настройках</string>
|
||||||
<string name="openNotificationSettings">Открыть настройки уведомлений</string>
|
<string name="openNotificationSettings">Открыть настройки уведомлений</string>
|
||||||
|
|
||||||
|
<string name="tvNoFileBrowser">Пожалуйста, установите приложение для просмотра файлов</string>
|
||||||
</resources>
|
</resources>
|
|
@ -1,4 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<resources>
|
|
||||||
<color name="ic_banner_background">#1E1E1F</color>
|
|
||||||
</resources>
|
|
|
@ -23,4 +23,6 @@
|
||||||
<string name="notificationSettingsDialogTitle">Notification settings</string>
|
<string name="notificationSettingsDialogTitle">Notification settings</string>
|
||||||
<string name="notificationSettingsDialogMessage">To show notifications, you must enable notifications in the system settings</string>
|
<string name="notificationSettingsDialogMessage">To show notifications, you must enable notifications in the system settings</string>
|
||||||
<string name="openNotificationSettings">Open notification settings</string>
|
<string name="openNotificationSettings">Open notification settings</string>
|
||||||
|
|
||||||
|
<string name="tvNoFileBrowser">Please install a file management utility to browse files</string>
|
||||||
</resources>
|
</resources>
|
|
@ -4,6 +4,7 @@ import android.Manifest
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.app.AlertDialog
|
import android.app.AlertDialog
|
||||||
import android.app.NotificationManager
|
import android.app.NotificationManager
|
||||||
|
import android.content.ActivityNotFoundException
|
||||||
import android.content.BroadcastReceiver
|
import android.content.BroadcastReceiver
|
||||||
import android.content.ComponentName
|
import android.content.ComponentName
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
@ -12,6 +13,7 @@ import android.content.Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
|
||||||
import android.content.ServiceConnection
|
import android.content.ServiceConnection
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
|
import android.net.Uri
|
||||||
import android.net.VpnService
|
import android.net.VpnService
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
@ -20,8 +22,13 @@ import android.os.IBinder
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
import android.os.Message
|
import android.os.Message
|
||||||
import android.os.Messenger
|
import android.os.Messenger
|
||||||
|
import android.os.ParcelFileDescriptor
|
||||||
|
import android.os.SystemClock
|
||||||
|
import android.provider.OpenableColumns
|
||||||
import android.provider.Settings
|
import android.provider.Settings
|
||||||
import android.view.MotionEvent
|
import android.view.MotionEvent
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
import android.view.WindowManager.LayoutParams
|
import android.view.WindowManager.LayoutParams
|
||||||
import android.webkit.MimeTypeMap
|
import android.webkit.MimeTypeMap
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
@ -30,6 +37,7 @@ import androidx.annotation.RequiresApi
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import kotlin.LazyThreadSafetyMode.NONE
|
import kotlin.LazyThreadSafetyMode.NONE
|
||||||
|
import kotlin.coroutines.CoroutineContext
|
||||||
import kotlin.text.RegexOption.IGNORE_CASE
|
import kotlin.text.RegexOption.IGNORE_CASE
|
||||||
import AppListProvider
|
import AppListProvider
|
||||||
import kotlinx.coroutines.CompletableDeferred
|
import kotlinx.coroutines.CompletableDeferred
|
||||||
|
@ -71,6 +79,7 @@ class AmneziaActivity : QtActivity() {
|
||||||
private var isInBoundState = false
|
private var isInBoundState = false
|
||||||
private var notificationStateReceiver: BroadcastReceiver? = null
|
private var notificationStateReceiver: BroadcastReceiver? = null
|
||||||
private lateinit var vpnServiceMessenger: IpcMessenger
|
private lateinit var vpnServiceMessenger: IpcMessenger
|
||||||
|
private var pfd: ParcelFileDescriptor? = null
|
||||||
|
|
||||||
private val actionResultHandlers = mutableMapOf<Int, ActivityResultHandler>()
|
private val actionResultHandlers = mutableMapOf<Int, ActivityResultHandler>()
|
||||||
private val permissionRequestHandlers = mutableMapOf<Int, PermissionRequestHandler>()
|
private val permissionRequestHandlers = mutableMapOf<Int, PermissionRequestHandler>()
|
||||||
|
@ -514,6 +523,7 @@ class AmneziaActivity : QtActivity() {
|
||||||
type = "text/*"
|
type = "text/*"
|
||||||
putExtra(Intent.EXTRA_TITLE, fileName)
|
putExtra(Intent.EXTRA_TITLE, fileName)
|
||||||
}.also {
|
}.also {
|
||||||
|
try {
|
||||||
startActivityForResult(it, CREATE_FILE_ACTION_CODE, ActivityResultHandler(
|
startActivityForResult(it, CREATE_FILE_ACTION_CODE, ActivityResultHandler(
|
||||||
onSuccess = {
|
onSuccess = {
|
||||||
it?.data?.let { uri ->
|
it?.data?.let { uri ->
|
||||||
|
@ -529,6 +539,9 @@ class AmneziaActivity : QtActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
))
|
))
|
||||||
|
} catch (_: ActivityNotFoundException) {
|
||||||
|
Toast.makeText(this@AmneziaActivity, "Unsupported", Toast.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -537,6 +550,7 @@ class AmneziaActivity : QtActivity() {
|
||||||
fun openFile(filter: String?) {
|
fun openFile(filter: String?) {
|
||||||
Log.v(TAG, "Open file with filter: $filter")
|
Log.v(TAG, "Open file with filter: $filter")
|
||||||
mainScope.launch {
|
mainScope.launch {
|
||||||
|
val intent = if (!isOnTv()) {
|
||||||
val mimeTypes = if (!filter.isNullOrEmpty()) {
|
val mimeTypes = if (!filter.isNullOrEmpty()) {
|
||||||
val extensionRegex = "\\*\\.([a-z0-9]+)".toRegex(IGNORE_CASE)
|
val extensionRegex = "\\*\\.([a-z0-9]+)".toRegex(IGNORE_CASE)
|
||||||
val mime = MimeTypeMap.getSingleton()
|
val mime = MimeTypeMap.getSingleton()
|
||||||
|
@ -562,10 +576,20 @@ class AmneziaActivity : QtActivity() {
|
||||||
else -> type = "*/*"
|
else -> type = "*/*"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.also {
|
}
|
||||||
startActivityForResult(it, OPEN_FILE_ACTION_CODE, ActivityResultHandler(
|
} else {
|
||||||
|
Intent(this@AmneziaActivity, TvFilePicker::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
startActivityForResult(intent, OPEN_FILE_ACTION_CODE, ActivityResultHandler(
|
||||||
onAny = {
|
onAny = {
|
||||||
val uri = it?.data?.toString() ?: ""
|
if (isOnTv() && it?.hasExtra("activityNotFound") == true) {
|
||||||
|
showNoFileBrowserAlertDialog()
|
||||||
|
}
|
||||||
|
val uri = it?.data?.apply {
|
||||||
|
grantUriPermission(packageName, this, Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||||
|
}?.toString() ?: ""
|
||||||
Log.v(TAG, "Open file: $uri")
|
Log.v(TAG, "Open file: $uri")
|
||||||
mainScope.launch {
|
mainScope.launch {
|
||||||
qtInitialized.await()
|
qtInitialized.await()
|
||||||
|
@ -573,9 +597,67 @@ class AmneziaActivity : QtActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
))
|
))
|
||||||
|
} catch (_: ActivityNotFoundException) {
|
||||||
|
showNoFileBrowserAlertDialog()
|
||||||
|
mainScope.launch {
|
||||||
|
qtInitialized.await()
|
||||||
|
QtAndroidController.onFileOpened("")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showNoFileBrowserAlertDialog() {
|
||||||
|
AlertDialog.Builder(this)
|
||||||
|
.setMessage(R.string.tvNoFileBrowser)
|
||||||
|
.setCancelable(false)
|
||||||
|
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||||
|
try {
|
||||||
|
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse("market://webstoreredirect")))
|
||||||
|
} catch (_: Throwable) {}
|
||||||
|
}
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
|
fun getFd(fileName: String): Int {
|
||||||
|
Log.v(TAG, "Get fd for $fileName")
|
||||||
|
return blockingCall {
|
||||||
|
try {
|
||||||
|
pfd = contentResolver.openFileDescriptor(Uri.parse(fileName), "r")
|
||||||
|
pfd?.fd ?: -1
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Failed to get fd: $e")
|
||||||
|
-1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
|
fun closeFd() {
|
||||||
|
Log.v(TAG, "Close fd")
|
||||||
|
mainScope.launch {
|
||||||
|
pfd?.close()
|
||||||
|
pfd = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
|
fun getFileName(uri: String): String {
|
||||||
|
Log.v(TAG, "Get file name for uri: $uri")
|
||||||
|
return blockingCall {
|
||||||
|
try {
|
||||||
|
contentResolver.query(Uri.parse(uri), arrayOf(OpenableColumns.DISPLAY_NAME), null, null, null)?.use { cursor ->
|
||||||
|
if (cursor.moveToFirst() && !cursor.isNull(0)) {
|
||||||
|
return@blockingCall cursor.getString(0) ?: ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Failed to get file name: $e")
|
||||||
|
}
|
||||||
|
""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
@SuppressLint("UnsupportedChromeOsCameraSystemFeature")
|
@SuppressLint("UnsupportedChromeOsCameraSystemFeature")
|
||||||
|
@ -721,6 +803,50 @@ class AmneziaActivity : QtActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// method to workaround Qt's problem with calling the keyboard on TVs
|
||||||
|
@Suppress("unused")
|
||||||
|
fun sendTouch(x: Float, y: Float) {
|
||||||
|
Log.v(TAG, "Send touch: $x, $y")
|
||||||
|
blockingCall {
|
||||||
|
findQtWindow(window.decorView)?.let {
|
||||||
|
Log.v(TAG, "Send touch to $it")
|
||||||
|
it.dispatchTouchEvent(createEvent(x, y, SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN))
|
||||||
|
it.dispatchTouchEvent(createEvent(x, y, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun findQtWindow(view: View): View? {
|
||||||
|
Log.v(TAG, "findQtWindow: process $view")
|
||||||
|
if (view::class.simpleName == "QtWindow") return view
|
||||||
|
else if (view is ViewGroup) {
|
||||||
|
for (i in 0 until view.childCount) {
|
||||||
|
val result = findQtWindow(view.getChildAt(i))
|
||||||
|
if (result != null) return result
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
} else return null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createEvent(x: Float, y: Float, eventTime: Long, action: Int): MotionEvent =
|
||||||
|
MotionEvent.obtain(
|
||||||
|
eventTime,
|
||||||
|
eventTime,
|
||||||
|
action,
|
||||||
|
1,
|
||||||
|
arrayOf(MotionEvent.PointerProperties().apply {
|
||||||
|
id = 0
|
||||||
|
toolType = MotionEvent.TOOL_TYPE_FINGER
|
||||||
|
}),
|
||||||
|
arrayOf(MotionEvent.PointerCoords().apply {
|
||||||
|
this.x = x
|
||||||
|
this.y = y
|
||||||
|
pressure = 1f
|
||||||
|
size = 1f
|
||||||
|
}),
|
||||||
|
0, 0, 1.0f, 1.0f, 0, 0, 0,0
|
||||||
|
)
|
||||||
|
|
||||||
// workaround for a bug in Qt that causes the mouse click event not to be handled
|
// workaround for a bug in Qt that causes the mouse click event not to be handled
|
||||||
// also disable right-click, as it causes the application to crash
|
// also disable right-click, as it causes the application to crash
|
||||||
private var lastButtonState = 0
|
private var lastButtonState = 0
|
||||||
|
@ -770,6 +896,7 @@ class AmneziaActivity : QtActivity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
|
override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
|
||||||
|
Log.v(TAG, "dispatchTouch: $ev")
|
||||||
if (ev != null && ev.getToolType(0) == MotionEvent.TOOL_TYPE_MOUSE) {
|
if (ev != null && ev.getToolType(0) == MotionEvent.TOOL_TYPE_MOUSE) {
|
||||||
return handleMouseEvent(ev) { super.dispatchTouchEvent(it) }
|
return handleMouseEvent(ev) { super.dispatchTouchEvent(it) }
|
||||||
}
|
}
|
||||||
|
@ -784,6 +911,13 @@ class AmneziaActivity : QtActivity() {
|
||||||
/**
|
/**
|
||||||
* Utils methods
|
* Utils methods
|
||||||
*/
|
*/
|
||||||
|
private fun <T> blockingCall(
|
||||||
|
context: CoroutineContext = Dispatchers.Main.immediate,
|
||||||
|
block: suspend () -> T
|
||||||
|
) = runBlocking {
|
||||||
|
mainScope.async(context) { block() }.await()
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private fun actionCodeToString(actionCode: Int): String =
|
private fun actionCodeToString(actionCode: Int): String =
|
||||||
when (actionCode) {
|
when (actionCode) {
|
||||||
|
|
45
client/android/src/org/amnezia/vpn/TvFilePicker.kt
Normal file
45
client/android/src/org/amnezia/vpn/TvFilePicker.kt
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
package org.amnezia.vpn
|
||||||
|
|
||||||
|
import android.content.ActivityNotFoundException
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.Bundle
|
||||||
|
import androidx.activity.ComponentActivity
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
|
import org.amnezia.vpn.util.Log
|
||||||
|
|
||||||
|
private const val TAG = "TvFilePicker"
|
||||||
|
|
||||||
|
class TvFilePicker : ComponentActivity() {
|
||||||
|
|
||||||
|
private val fileChooseResultLauncher = registerForActivityResult(ActivityResultContracts.GetContent()) {
|
||||||
|
setResult(RESULT_OK, Intent().apply { data = it })
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
Log.v(TAG, "onCreate")
|
||||||
|
getFile()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onNewIntent(intent: Intent) {
|
||||||
|
super.onNewIntent(intent)
|
||||||
|
Log.v(TAG, "onNewIntent")
|
||||||
|
getFile()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getFile() {
|
||||||
|
try {
|
||||||
|
Log.v(TAG, "getFile")
|
||||||
|
fileChooseResultLauncher.launch("*/*")
|
||||||
|
} catch (_: ActivityNotFoundException) {
|
||||||
|
Log.w(TAG, "Activity not found")
|
||||||
|
setResult(RESULT_CANCELED, Intent().apply { putExtra("activityNotFound", true) })
|
||||||
|
finish()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Failed to get file: $e")
|
||||||
|
setResult(RESULT_CANCELED)
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -163,9 +163,7 @@ QString AndroidController::openFile(const QString &filter)
|
||||||
QString fileName;
|
QString fileName;
|
||||||
connect(this, &AndroidController::fileOpened, this,
|
connect(this, &AndroidController::fileOpened, this,
|
||||||
[&fileName, &wait](const QString &uri) {
|
[&fileName, &wait](const QString &uri) {
|
||||||
qDebug() << "Android event: file opened; uri:" << uri;
|
fileName = uri;
|
||||||
fileName = QQmlFile::urlToLocalFileOrQrc(uri);
|
|
||||||
qDebug() << "Android opened filename:" << fileName;
|
|
||||||
wait.quit();
|
wait.quit();
|
||||||
},
|
},
|
||||||
static_cast<Qt::ConnectionType>(Qt::QueuedConnection | Qt::SingleShotConnection));
|
static_cast<Qt::ConnectionType>(Qt::QueuedConnection | Qt::SingleShotConnection));
|
||||||
|
@ -175,6 +173,25 @@ QString AndroidController::openFile(const QString &filter)
|
||||||
return fileName;
|
return fileName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int AndroidController::getFd(const QString &fileName)
|
||||||
|
{
|
||||||
|
return callActivityMethod<jint>("getFd", "(Ljava/lang/String;)I",
|
||||||
|
QJniObject::fromString(fileName).object<jstring>());
|
||||||
|
}
|
||||||
|
|
||||||
|
void AndroidController::closeFd()
|
||||||
|
{
|
||||||
|
callActivityMethod("closeFd", "()V");
|
||||||
|
}
|
||||||
|
|
||||||
|
QString AndroidController::getFileName(const QString &uri)
|
||||||
|
{
|
||||||
|
auto fileName = callActivityMethod<jstring, jstring>("getFileName", "(Ljava/lang/String;)Ljava/lang/String;",
|
||||||
|
QJniObject::fromString(uri).object<jstring>());
|
||||||
|
QJniEnvironment env;
|
||||||
|
return AndroidUtils::convertJString(env.jniEnv(), fileName.object<jstring>());
|
||||||
|
}
|
||||||
|
|
||||||
bool AndroidController::isCameraPresent()
|
bool AndroidController::isCameraPresent()
|
||||||
{
|
{
|
||||||
return callActivityMethod<jboolean>("isCameraPresent", "()Z");
|
return callActivityMethod<jboolean>("isCameraPresent", "()Z");
|
||||||
|
@ -287,6 +304,11 @@ bool AndroidController::requestAuthentication()
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AndroidController::sendTouch(float x, float y)
|
||||||
|
{
|
||||||
|
callActivityMethod("sendTouch", "(FF)V", x, y);
|
||||||
|
}
|
||||||
|
|
||||||
// Moving log processing to the Android side
|
// Moving log processing to the Android side
|
||||||
jclass AndroidController::log;
|
jclass AndroidController::log;
|
||||||
jmethodID AndroidController::logDebug;
|
jmethodID AndroidController::logDebug;
|
||||||
|
|
|
@ -34,6 +34,9 @@ public:
|
||||||
void resetLastServer(int serverIndex);
|
void resetLastServer(int serverIndex);
|
||||||
void saveFile(const QString &fileName, const QString &data);
|
void saveFile(const QString &fileName, const QString &data);
|
||||||
QString openFile(const QString &filter);
|
QString openFile(const QString &filter);
|
||||||
|
int getFd(const QString &fileName);
|
||||||
|
void closeFd();
|
||||||
|
QString getFileName(const QString &uri);
|
||||||
bool isCameraPresent();
|
bool isCameraPresent();
|
||||||
bool isOnTv();
|
bool isOnTv();
|
||||||
void startQrReaderActivity();
|
void startQrReaderActivity();
|
||||||
|
@ -48,6 +51,7 @@ public:
|
||||||
bool isNotificationPermissionGranted();
|
bool isNotificationPermissionGranted();
|
||||||
void requestNotificationPermission();
|
void requestNotificationPermission();
|
||||||
bool requestAuthentication();
|
bool requestAuthentication();
|
||||||
|
void sendTouch(float x, float y);
|
||||||
|
|
||||||
static bool initLogging();
|
static bool initLogging();
|
||||||
static void messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &message);
|
static void messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &message);
|
||||||
|
|
|
@ -1,225 +1,227 @@
|
||||||
<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/ScrollBarType.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>
|
||||||
|
|
210
client/ui/controllers/focusController.cpp
Normal file
210
client/ui/controllers/focusController.cpp
Normal file
|
@ -0,0 +1,210 @@
|
||||||
|
#include "focusController.h"
|
||||||
|
#include "utils/qmlUtils.h"
|
||||||
|
|
||||||
|
#include <QQmlApplicationEngine>
|
||||||
|
#include <QQuickWindow>
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FocusController::setFocusOnDefaultItem()
|
||||||
|
{
|
||||||
|
setFocusItem(m_defaultFocusItem.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
void FocusController::pushRootObject(QObject *object)
|
||||||
|
{
|
||||||
|
m_rootObjects.push(object);
|
||||||
|
dropListView();
|
||||||
|
// setFocusOnDefaultItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FocusController::dropRootObject(QObject *object)
|
||||||
|
{
|
||||||
|
if (m_rootObjects.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_rootObjects.top() == object) {
|
||||||
|
m_rootObjects.pop();
|
||||||
|
dropListView();
|
||||||
|
setFocusOnDefaultItem();
|
||||||
|
} else {
|
||||||
|
qWarning() << "===>> TRY TO DROP WRONG ROOT OBJECT: " << m_rootObjects.top() << " SHOULD BE: " << object;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FocusController::resetRootObject()
|
||||||
|
{
|
||||||
|
m_rootObjects.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FocusController::reload(Direction direction)
|
||||||
|
{
|
||||||
|
m_focusChain.clear();
|
||||||
|
|
||||||
|
QObject *rootObject = (m_rootObjects.empty() ? m_engine->rootObjects().value(0) : m_rootObjects.top());
|
||||||
|
|
||||||
|
if (!rootObject) {
|
||||||
|
qCritical() << "No ROOT OBJECT found!";
|
||||||
|
resetRootObject();
|
||||||
|
dropListView();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_focusChain.append(FocusControl::getSubChain(rootObject));
|
||||||
|
|
||||||
|
std::sort(m_focusChain.begin(), m_focusChain.end(),
|
||||||
|
direction == Direction::Forward ? FocusControl::isLess : FocusControl::isMore);
|
||||||
|
|
||||||
|
if (m_focusChain.empty()) {
|
||||||
|
qWarning() << "Focus chain is empty!";
|
||||||
|
resetRootObject();
|
||||||
|
dropListView();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FocusController::nextItem(Direction direction)
|
||||||
|
{
|
||||||
|
reload(direction);
|
||||||
|
|
||||||
|
if (m_lvfc && FocusControl::isListView(m_focusedItem)) {
|
||||||
|
direction == Direction::Forward ? focusNextListViewItem() : focusPreviousListViewItem();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_focusChain.empty()) {
|
||||||
|
qWarning() << "There are no items to navigate";
|
||||||
|
setFocusOnDefaultItem();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto focusedItemIndex = m_focusChain.indexOf(m_focusedItem);
|
||||||
|
|
||||||
|
if (focusedItemIndex == -1) {
|
||||||
|
focusedItemIndex = 0;
|
||||||
|
} else if (focusedItemIndex == (m_focusChain.size() - 1)) {
|
||||||
|
focusedItemIndex = 0;
|
||||||
|
} else {
|
||||||
|
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 (FocusControl::isListView(focusedItem)) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FocusController::focusNextListViewItem()
|
||||||
|
{
|
||||||
|
m_lvfc->reloadFocusChain();
|
||||||
|
if (m_lvfc->isLastFocusItemInListView() || m_lvfc->isReturnNeeded()) {
|
||||||
|
dropListView();
|
||||||
|
nextItem(Direction::Forward);
|
||||||
|
return;
|
||||||
|
} else if (m_lvfc->isLastFocusItemInDelegate()) {
|
||||||
|
m_lvfc->resetFocusChain();
|
||||||
|
m_lvfc->nextDelegate();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_lvfc->focusNextItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FocusController::focusPreviousListViewItem()
|
||||||
|
{
|
||||||
|
m_lvfc->reloadFocusChain();
|
||||||
|
if (m_lvfc->isFirstFocusItemInListView() || m_lvfc->isReturnNeeded()) {
|
||||||
|
dropListView();
|
||||||
|
nextItem(Direction::Backward);
|
||||||
|
return;
|
||||||
|
} else if (m_lvfc->isFirstFocusItemInDelegate()) {
|
||||||
|
m_lvfc->resetFocusChain();
|
||||||
|
m_lvfc->previousDelegate();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_lvfc->focusPreviousItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FocusController::dropListView()
|
||||||
|
{
|
||||||
|
if (m_lvfc) {
|
||||||
|
delete m_lvfc;
|
||||||
|
m_lvfc = nullptr;
|
||||||
|
}
|
||||||
|
}
|
57
client/ui/controllers/focusController.h
Normal file
57
client/ui/controllers/focusController.h
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
#ifndef FOCUSCONTROLLER_H
|
||||||
|
#define FOCUSCONTROLLER_H
|
||||||
|
|
||||||
|
#include "ui/controllers/listViewFocusController.h"
|
||||||
|
|
||||||
|
#include <QQmlApplicationEngine>
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \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
|
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
#include "core/errorstrings.h"
|
#include "core/errorstrings.h"
|
||||||
#include "core/serialization/serialization.h"
|
#include "core/serialization/serialization.h"
|
||||||
|
#include "systemController.h"
|
||||||
#include "utilities.h"
|
#include "utilities.h"
|
||||||
|
|
||||||
#ifdef Q_OS_ANDROID
|
#ifdef Q_OS_ANDROID
|
||||||
|
@ -76,18 +77,19 @@ ImportController::ImportController(const QSharedPointer<ServersModel> &serversMo
|
||||||
|
|
||||||
bool ImportController::extractConfigFromFile(const QString &fileName)
|
bool ImportController::extractConfigFromFile(const QString &fileName)
|
||||||
{
|
{
|
||||||
QFile file(fileName);
|
QString data;
|
||||||
|
if (!SystemController::readFile(fileName, data)) {
|
||||||
if (file.open(QIODevice::ReadOnly)) {
|
|
||||||
QString data = file.readAll();
|
|
||||||
|
|
||||||
m_configFileName = QFileInfo(file.fileName()).fileName();
|
|
||||||
return extractConfigFromData(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
emit importErrorOccurred(ErrorCode::ImportOpenConfigError, false);
|
emit importErrorOccurred(ErrorCode::ImportOpenConfigError, false);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
m_configFileName = QFileInfo(QFile(fileName).fileName()).fileName();
|
||||||
|
#ifdef Q_OS_ANDROID
|
||||||
|
if (m_configFileName.isEmpty()) {
|
||||||
|
m_configFileName = AndroidController::instance()->getFileName(fileName);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return extractConfigFromData(data);
|
||||||
|
}
|
||||||
|
|
||||||
bool ImportController::extractConfigFromData(QString data)
|
bool ImportController::extractConfigFromData(QString data)
|
||||||
{
|
{
|
||||||
|
|
309
client/ui/controllers/listViewFocusController.cpp
Normal file
309
client/ui/controllers/listViewFocusController.cpp
Normal file
|
@ -0,0 +1,309 @@
|
||||||
|
#include "listViewFocusController.h"
|
||||||
|
#include "utils/qmlUtils.h"
|
||||||
|
|
||||||
|
#include <QQuickWindow>
|
||||||
|
|
||||||
|
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: {
|
||||||
|
QMetaObject::invokeMethod(m_listView, "positionViewAtBeginning");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Section::Delegate: {
|
||||||
|
QMetaObject::invokeMethod(m_listView, "positionViewAtIndex", Q_ARG(int, m_delegateIndex), // Index
|
||||||
|
Q_ARG(int, 2)); // PositionMode (0 = Visible)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Section::Footer: {
|
||||||
|
QMetaObject::invokeMethod(m_listView, "positionViewAtEnd");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int ListViewFocusController::size() const
|
||||||
|
{
|
||||||
|
return m_listView->property("count").toInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
int ListViewFocusController::currentIndex() const
|
||||||
|
{
|
||||||
|
return m_delegateIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ListViewFocusController::setDelegateIndex(int index)
|
||||||
|
{
|
||||||
|
m_delegateIndex = index;
|
||||||
|
m_listView->setProperty("currentIndex", index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ListViewFocusController::nextDelegate()
|
||||||
|
{
|
||||||
|
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)) {
|
||||||
|
setDelegateIndex(m_delegateIndex + 1);
|
||||||
|
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;
|
||||||
|
setDelegateIndex(size() - 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
[[fallthrough]];
|
||||||
|
}
|
||||||
|
case Section::Delegate: {
|
||||||
|
if (m_delegateIndex > 0) {
|
||||||
|
setDelegateIndex(m_delegateIndex - 1);
|
||||||
|
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) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
reloadFocusChain();
|
||||||
|
|
||||||
|
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));
|
||||||
|
m_focusedItem->forceActiveFocus(Qt::TabFocusReason);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ListViewFocusController::focusPreviousItem()
|
||||||
|
{
|
||||||
|
if (m_isReturnNeeded) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_focusChain.empty()) {
|
||||||
|
qInfo() << "Empty focusChain with current delegate: " << currentDelegate() << "Scanning for elements...";
|
||||||
|
reloadFocusChain();
|
||||||
|
}
|
||||||
|
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));
|
||||||
|
m_focusedItem->forceActiveFocus(Qt::TabFocusReason);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ListViewFocusController::resetFocusChain()
|
||||||
|
{
|
||||||
|
m_focusChain.clear();
|
||||||
|
m_focusedItem = nullptr;
|
||||||
|
m_focusedItemIndex = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ListViewFocusController::reloadFocusChain()
|
||||||
|
{
|
||||||
|
m_focusChain = FocusControl::getItemsChain(currentDelegate());
|
||||||
|
}
|
||||||
|
|
||||||
|
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 && !FocusControl::getItemsChain(m_header).isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ListViewFocusController::hasFooter() const
|
||||||
|
{
|
||||||
|
return m_footer && !FocusControl::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;
|
||||||
|
}
|
70
client/ui/controllers/listViewFocusController.h
Normal file
70
client/ui/controllers/listViewFocusController.h
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
#ifndef LISTVIEWFOCUSCONTROLLER_H
|
||||||
|
#define LISTVIEWFOCUSCONTROLLER_H
|
||||||
|
|
||||||
|
#include <QList>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QQuickItem>
|
||||||
|
#include <QSharedPointer>
|
||||||
|
#include <QStack>
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \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();
|
||||||
|
void reloadFocusChain();
|
||||||
|
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 setDelegateIndex(int index);
|
||||||
|
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
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -131,12 +131,8 @@ void SettingsController::backupAppConfig(const QString &fileName)
|
||||||
|
|
||||||
void SettingsController::restoreAppConfig(const QString &fileName)
|
void SettingsController::restoreAppConfig(const QString &fileName)
|
||||||
{
|
{
|
||||||
QFile file(fileName);
|
QByteArray data;
|
||||||
|
SystemController::readFile(fileName, data);
|
||||||
file.open(QIODevice::ReadOnly);
|
|
||||||
|
|
||||||
QByteArray data = file.readAll();
|
|
||||||
|
|
||||||
restoreAppConfigFromData(data);
|
restoreAppConfigFromData(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -82,14 +82,12 @@ void SitesController::removeSite(int index)
|
||||||
|
|
||||||
void SitesController::importSites(const QString &fileName, bool replaceExisting)
|
void SitesController::importSites(const QString &fileName, bool replaceExisting)
|
||||||
{
|
{
|
||||||
QFile file(fileName);
|
QByteArray jsonData;
|
||||||
|
if (!SystemController::readFile(fileName, jsonData)) {
|
||||||
if (!file.open(QIODevice::ReadOnly)) {
|
|
||||||
emit errorOccurred(tr("Can't open file: %1").arg(fileName));
|
emit errorOccurred(tr("Can't open file: %1").arg(fileName));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray jsonData = file.readAll();
|
|
||||||
QJsonDocument jsonDocument = QJsonDocument::fromJson(jsonData);
|
QJsonDocument jsonDocument = QJsonDocument::fromJson(jsonData);
|
||||||
if (jsonDocument.isNull()) {
|
if (jsonDocument.isNull()) {
|
||||||
emit errorOccurred(tr("Failed to parse JSON data from file: %1").arg(fileName));
|
emit errorOccurred(tr("Failed to parse JSON data from file: %1").arg(fileName));
|
||||||
|
|
|
@ -24,7 +24,7 @@ SystemController::SystemController(const std::shared_ptr<Settings> &settings, QO
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void SystemController::saveFile(QString fileName, const QString &data)
|
void SystemController::saveFile(const QString &fileName, const QString &data)
|
||||||
{
|
{
|
||||||
#if defined Q_OS_ANDROID
|
#if defined Q_OS_ANDROID
|
||||||
AndroidController::instance()->saveFile(fileName, data);
|
AndroidController::instance()->saveFile(fileName, data);
|
||||||
|
@ -62,6 +62,31 @@ void SystemController::saveFile(QString fileName, const QString &data)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SystemController::readFile(const QString &fileName, QByteArray &data)
|
||||||
|
{
|
||||||
|
#ifdef Q_OS_ANDROID
|
||||||
|
int fd = AndroidController::instance()->getFd(fileName);
|
||||||
|
if (fd == -1) return false;
|
||||||
|
QFile file;
|
||||||
|
if(!file.open(fd, QIODevice::ReadOnly)) return false;
|
||||||
|
data = file.readAll();
|
||||||
|
AndroidController::instance()->closeFd();
|
||||||
|
#else
|
||||||
|
QFile file(fileName);
|
||||||
|
if (!file.open(QIODevice::ReadOnly)) return false;
|
||||||
|
data = file.readAll();
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SystemController::readFile(const QString &fileName, QString &data)
|
||||||
|
{
|
||||||
|
QByteArray byteArray;
|
||||||
|
if(!readFile(fileName, byteArray)) return false;
|
||||||
|
data = byteArray;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
QString SystemController::getFileName(const QString &acceptLabel, const QString &nameFilter,
|
QString SystemController::getFileName(const QString &acceptLabel, const QString &nameFilter,
|
||||||
const QString &selectedFile, const bool isSaveMode, const QString &defaultSuffix)
|
const QString &selectedFile, const bool isSaveMode, const QString &defaultSuffix)
|
||||||
{
|
{
|
||||||
|
@ -134,3 +159,10 @@ bool SystemController::isAuthenticated()
|
||||||
return true;
|
return true;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SystemController::sendTouch(float x, float y)
|
||||||
|
{
|
||||||
|
#ifdef Q_OS_ANDROID
|
||||||
|
AndroidController::instance()->sendTouch(x, y);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
|
@ -11,7 +11,9 @@ class SystemController : public QObject
|
||||||
public:
|
public:
|
||||||
explicit SystemController(const std::shared_ptr<Settings> &setting, QObject *parent = nullptr);
|
explicit SystemController(const std::shared_ptr<Settings> &setting, QObject *parent = nullptr);
|
||||||
|
|
||||||
static void saveFile(QString fileName, const QString &data);
|
static void saveFile(const QString &fileName, const QString &data);
|
||||||
|
static bool readFile(const QString &fileName, QByteArray &data);
|
||||||
|
static bool readFile(const QString &fileName, QString &data);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
QString getFileName(const QString &acceptLabel, const QString &nameFilter, const QString &selectedFile = "",
|
QString getFileName(const QString &acceptLabel, const QString &nameFilter, const QString &selectedFile = "",
|
||||||
|
@ -20,6 +22,8 @@ public slots:
|
||||||
void setQmlRoot(QObject *qmlRoot);
|
void setQmlRoot(QObject *qmlRoot);
|
||||||
|
|
||||||
bool isAuthenticated();
|
bool isAuthenticated();
|
||||||
|
void sendTouch(float x, float y);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void fileDialogClosed(const bool isAccepted);
|
void fileDialogClosed(const bool isAccepted);
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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 {}
|
||||||
|
|
|
@ -17,55 +17,15 @@ 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
|
||||||
|
|
||||||
clip: true
|
clip: true
|
||||||
interactive: false
|
snapMode: ListView.SnapToItem
|
||||||
|
|
||||||
property FlickableType parentFlickable
|
ScrollBar.vertical: ScrollBarType {}
|
||||||
property var lastItemTabClicked
|
|
||||||
|
|
||||||
property int currentFocusIndex: 0
|
property bool isFocusable: true
|
||||||
|
|
||||||
activeFocusOnTab: true
|
|
||||||
onActiveFocusChanged: {
|
|
||||||
if (activeFocus) {
|
|
||||||
this.currentFocusIndex = 0
|
|
||||||
this.itemAtIndex(currentFocusIndex).forceActiveFocus()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onTabPressed: {
|
|
||||||
if (currentFocusIndex < this.count - 1) {
|
|
||||||
currentFocusIndex += 1
|
|
||||||
this.itemAtIndex(currentFocusIndex).forceActiveFocus()
|
|
||||||
} else {
|
|
||||||
currentFocusIndex = 0
|
|
||||||
if (lastItemTabClicked && typeof lastItemTabClicked === "function") {
|
|
||||||
lastItemTabClicked()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onVisibleChanged: {
|
|
||||||
if (visible) {
|
|
||||||
currentFocusIndex = 0
|
|
||||||
focusItem.forceActiveFocus()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: focusItem
|
|
||||||
}
|
|
||||||
|
|
||||||
onCurrentFocusIndexChanged: {
|
|
||||||
if (parentFlickable) {
|
|
||||||
parentFlickable.ensureVisible(this.itemAtIndex(currentFocusIndex))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ButtonGroup {
|
ButtonGroup {
|
||||||
id: containersRadioButtonGroup
|
id: containersRadioButtonGroup
|
||||||
|
@ -75,12 +35,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 +65,13 @@ ListView {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (checked) {
|
if (checked) {
|
||||||
containersDropDown.close()
|
containersDropDown.closeTriggered()
|
||||||
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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
@ -79,10 +81,7 @@ DrawerType2 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ScrollBar.vertical: ScrollBar {
|
ScrollBar.vertical: ScrollBarType {}
|
||||||
id: scrollBar
|
|
||||||
policy: ScrollBar.AlwaysOn
|
|
||||||
}
|
|
||||||
|
|
||||||
ButtonGroup {
|
ButtonGroup {
|
||||||
id: buttonGroup
|
id: buttonGroup
|
||||||
|
@ -155,7 +154,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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ import "../Config"
|
||||||
DrawerType2 {
|
DrawerType2 {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
expandedContent: Item {
|
expandedStateContent: Item {
|
||||||
id: container
|
id: container
|
||||||
|
|
||||||
implicitHeight: root.height * 0.9
|
implicitHeight: root.height * 0.9
|
||||||
|
@ -20,19 +20,6 @@ DrawerType2 {
|
||||||
root.expandedHeight = container.implicitHeight
|
root.expandedHeight = container.implicitHeight
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: root
|
|
||||||
enabled: !GC.isMobile()
|
|
||||||
function onOpened() {
|
|
||||||
focusItem.forceActiveFocus()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: focusItem
|
|
||||||
KeyNavigation.tab: backButton
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: backButtonLayout
|
id: backButtonLayout
|
||||||
|
|
||||||
|
@ -43,26 +30,16 @@ DrawerType2 {
|
||||||
|
|
||||||
BackButtonType {
|
BackButtonType {
|
||||||
id: backButton
|
id: backButton
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
backButtonImage: "qrc:/images/controls/arrow-left.svg"
|
backButtonImage: "qrc:/images/controls/arrow-left.svg"
|
||||||
backButtonFunction: function() { root.close() }
|
backButtonFunction: function() { root.closeTriggered() }
|
||||||
KeyNavigation.tab: listView
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
FlickableType {
|
|
||||||
anchors.top: backButtonLayout.bottom
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
contentHeight: content.implicitHeight
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
id: content
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
Header2Type {
|
Header2Type {
|
||||||
id: header
|
id: header
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: 16
|
Layout.topMargin: 16
|
||||||
Layout.rightMargin: 16
|
Layout.rightMargin: 16
|
||||||
|
@ -70,67 +47,34 @@ DrawerType2 {
|
||||||
|
|
||||||
headerText: qsTr("Choose language")
|
headerText: qsTr("Choose language")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ListView {
|
ListView {
|
||||||
id: listView
|
id: listView
|
||||||
|
|
||||||
Layout.fillWidth: true
|
anchors.top: backButtonLayout.bottom
|
||||||
height: listView.contentItem.height
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
|
||||||
|
property bool isFocusable: true
|
||||||
|
property int selectedIndex: LanguageModel.currentLanguageIndex
|
||||||
|
|
||||||
clip: true
|
clip: true
|
||||||
interactive: false
|
reuseItems: true
|
||||||
|
|
||||||
|
ScrollBar.vertical: ScrollBarType {}
|
||||||
|
|
||||||
model: LanguageModel
|
model: LanguageModel
|
||||||
currentIndex: LanguageModel.currentLanguageIndex
|
|
||||||
|
|
||||||
ButtonGroup {
|
ButtonGroup {
|
||||||
id: buttonGroup
|
id: buttonGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
property int currentFocusIndex: 0
|
|
||||||
|
|
||||||
activeFocusOnTab: true
|
|
||||||
onActiveFocusChanged: {
|
|
||||||
if (activeFocus) {
|
|
||||||
this.currentFocusIndex = 0
|
|
||||||
this.itemAtIndex(currentFocusIndex).forceActiveFocus()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onTabPressed: {
|
|
||||||
if (currentFocusIndex < this.count - 1) {
|
|
||||||
currentFocusIndex += 1
|
|
||||||
this.itemAtIndex(currentFocusIndex).forceActiveFocus()
|
|
||||||
} else {
|
|
||||||
listViewFocusItem.forceActiveFocus()
|
|
||||||
focusItem.forceActiveFocus()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: listViewFocusItem
|
|
||||||
Keys.onTabPressed: {
|
|
||||||
root.forceActiveFocus()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onVisibleChanged: {
|
|
||||||
if (visible) {
|
|
||||||
listViewFocusItem.forceActiveFocus()
|
|
||||||
focusItem.forceActiveFocus()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
delegate: Item {
|
delegate: Item {
|
||||||
implicitWidth: root.width
|
implicitWidth: root.width
|
||||||
implicitHeight: delegateContent.implicitHeight
|
implicitHeight: delegateContent.implicitHeight
|
||||||
|
|
||||||
onActiveFocusChanged: {
|
|
||||||
if (activeFocus) {
|
|
||||||
radioButton.forceActiveFocus()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: delegateContent
|
id: delegateContent
|
||||||
|
|
||||||
|
@ -144,6 +88,32 @@ DrawerType2 {
|
||||||
|
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
|
|
||||||
|
property bool isFocusable: true
|
||||||
|
|
||||||
|
Keys.onTabPressed: {
|
||||||
|
FocusController.nextKeyTabItem()
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.onBacktabPressed: {
|
||||||
|
FocusController.previousKeyTabItem()
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.onUpPressed: {
|
||||||
|
FocusController.nextKeyUpItem()
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.onDownPressed: {
|
||||||
|
FocusController.nextKeyDownItem()
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.onLeftPressed: {
|
||||||
|
FocusController.nextKeyLeftItem()
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.onRightPressed: {
|
||||||
|
FocusController.nextKeyRightItem()
|
||||||
|
}
|
||||||
|
|
||||||
indicator: Rectangle {
|
indicator: Rectangle {
|
||||||
width: parent.width - 1
|
width: parent.width - 1
|
||||||
height: parent.height
|
height: parent.height
|
||||||
|
@ -190,12 +160,12 @@ DrawerType2 {
|
||||||
}
|
}
|
||||||
|
|
||||||
ButtonGroup.group: buttonGroup
|
ButtonGroup.group: buttonGroup
|
||||||
checked: listView.currentIndex === index
|
checked: listView.selectedIndex === index
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
listView.currentIndex = index
|
listView.selectedIndex = index
|
||||||
LanguageModel.changeLanguage(languageIndex)
|
LanguageModel.changeLanguage(languageIndex)
|
||||||
root.close()
|
root.closeTriggered()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -206,5 +176,3 @@ DrawerType2 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
126
client/ui/qml/Components/ServersListView.qml
Normal file
126
client/ui/qml/Components/ServersListView.qml
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
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
|
||||||
|
|
||||||
|
property int selectedIndex: ServersModel.defaultIndex
|
||||||
|
|
||||||
|
anchors.top: serversMenuHeader.bottom
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.topMargin: 16
|
||||||
|
|
||||||
|
model: ServersModel
|
||||||
|
|
||||||
|
ScrollBar.vertical: ScrollBarType {}
|
||||||
|
|
||||||
|
property bool isFocusable: true
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: ServersModel
|
||||||
|
function onDefaultServerIndexChanged(serverIndex) {
|
||||||
|
root.selectedIndex = serverIndex
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
clip: true
|
||||||
|
reuseItems: 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
|
||||||
|
|
||||||
|
text: name
|
||||||
|
descriptionText: serverDescription
|
||||||
|
|
||||||
|
checked: index === root.selectedIndex
|
||||||
|
checkable: !ConnectionController.isConnected
|
||||||
|
|
||||||
|
ButtonGroup.group: serversRadioButtonGroup
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
if (ConnectionController.isConnected) {
|
||||||
|
PageController.showNotificationMessage(qsTr("Unable change server while there is an active connection"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
root.selectedIndex = index
|
||||||
|
|
||||||
|
ServersModel.defaultIndex = index
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,47 +20,14 @@ ListView {
|
||||||
height: root.contentItem.height
|
height: root.contentItem.height
|
||||||
|
|
||||||
clip: true
|
clip: true
|
||||||
interactive: false
|
reuseItems: true
|
||||||
|
|
||||||
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: {
|
|
||||||
if (visible) {
|
|
||||||
this.currentIndex = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
delegate: Item {
|
delegate: Item {
|
||||||
implicitWidth: root.width
|
implicitWidth: root.width
|
||||||
implicitHeight: delegateContent.implicitHeight
|
implicitHeight: delegateContent.implicitHeight
|
||||||
|
|
||||||
onActiveFocusChanged: {
|
|
||||||
if (activeFocus) {
|
|
||||||
containerRadioButton.rightButton.forceActiveFocus()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: delegateContent
|
id: delegateContent
|
||||||
|
|
||||||
|
|
|
@ -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,27 @@ DrawerType2 {
|
||||||
anchors.rightMargin: 16
|
anchors.rightMargin: 16
|
||||||
|
|
||||||
headerText: root.headerText
|
headerText: root.headerText
|
||||||
|
|
||||||
KeyNavigation.tab: shareButton
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FlickableType {
|
ListView {
|
||||||
|
id: listView
|
||||||
|
|
||||||
anchors.top: header.bottom
|
anchors.top: header.bottom
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
contentHeight: content.height + 32
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
id: content
|
|
||||||
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
|
|
||||||
anchors.leftMargin: 16
|
property bool isFocusable: true
|
||||||
anchors.rightMargin: 16
|
|
||||||
|
ScrollBar.vertical: ScrollBarType {}
|
||||||
|
|
||||||
|
model: 1
|
||||||
|
|
||||||
|
clip: true
|
||||||
|
reuseItems: true
|
||||||
|
|
||||||
|
header: ColumnLayout {
|
||||||
|
width: listView.width
|
||||||
|
|
||||||
visible: root.contentVisible
|
visible: root.contentVisible
|
||||||
|
|
||||||
|
@ -82,12 +77,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 +106,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 +121,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 +150,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 +163,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 +175,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 +209,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 +278,10 @@ DrawerType2 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delegate: ColumnLayout {
|
||||||
|
width: listView.width
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: qrCodeContainer
|
id: qrCodeContainer
|
||||||
|
@ -309,6 +289,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 +302,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 +354,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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -35,10 +35,35 @@ 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
|
||||||
focusPolicy: Qt.TabFocus
|
|
||||||
|
|
||||||
onFocusChanged: {
|
onFocusChanged: {
|
||||||
if (root.activeFocus) {
|
if (root.activeFocus) {
|
||||||
|
@ -150,7 +175,7 @@ Button {
|
||||||
ButtonTextType {
|
ButtonTextType {
|
||||||
id: buttonText
|
id: buttonText
|
||||||
|
|
||||||
color: textColor
|
color: root.textColor
|
||||||
text: root.text
|
text: root.text
|
||||||
visible: root.text === "" ? false : true
|
visible: root.text === "" ? false : true
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,17 +9,14 @@ 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 isOpened: isExpandedStateActive() || (isCollapsedStateActive && (dragArea.drag.active === true))
|
||||||
readonly property bool isClosed: drawerContent.state === root.drawerCollapsed && dragArea.drag.active === false
|
readonly property bool isClosed: isCollapsedStateActive() && (dragArea.drag.active === false)
|
||||||
|
|
||||||
readonly property bool isExpanded: drawerContent.state === root.drawerExpanded
|
property Component collapsedStateContent
|
||||||
readonly property bool isCollapsed: drawerContent.state === root.drawerCollapsed
|
property Component expandedStateContent
|
||||||
|
|
||||||
property Component collapsedContent
|
|
||||||
property Component expandedContent
|
|
||||||
|
|
||||||
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 +26,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 +70,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 +133,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 +155,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 +205,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 +286,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 +299,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 +308,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
|
||||||
|
|
|
@ -45,33 +45,56 @@ Item {
|
||||||
property Item drawerParent
|
property Item drawerParent
|
||||||
property Component listView
|
property Component listView
|
||||||
|
|
||||||
signal open
|
signal openTriggered
|
||||||
signal close
|
signal closeTriggered
|
||||||
|
|
||||||
function popupClosedFunc() {
|
readonly property bool isFocusable: true
|
||||||
if (!GC.isMobile()) {
|
|
||||||
this.forceActiveFocus()
|
Keys.onTabPressed: {
|
||||||
}
|
FocusController.nextKeyTabItem()
|
||||||
}
|
}
|
||||||
|
|
||||||
property var parentFlickable
|
Keys.onBacktabPressed: {
|
||||||
onFocusChanged: {
|
FocusController.previousKeyTabItem()
|
||||||
if (root.activeFocus) {
|
|
||||||
if (root.parentFlickable) {
|
|
||||||
root.parentFlickable.ensureVisible(root)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Keys.onUpPressed: {
|
||||||
|
FocusController.nextKeyUpItem()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Keys.onDownPressed: {
|
||||||
|
FocusController.nextKeyDownItem()
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.onLeftPressed: {
|
||||||
|
FocusController.nextKeyLeftItem()
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.onRightPressed: {
|
||||||
|
FocusController.nextKeyRightItem()
|
||||||
}
|
}
|
||||||
|
|
||||||
implicitWidth: rootButtonContent.implicitWidth
|
implicitWidth: rootButtonContent.implicitWidth
|
||||||
implicitHeight: rootButtonContent.implicitHeight
|
implicitHeight: rootButtonContent.implicitHeight
|
||||||
|
|
||||||
onOpen: {
|
onOpenTriggered: {
|
||||||
menu.open()
|
menu.openTriggered()
|
||||||
}
|
}
|
||||||
|
|
||||||
onClose: {
|
onCloseTriggered: {
|
||||||
menu.close()
|
menu.closeTriggered()
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.onEnterPressed: {
|
||||||
|
if (menu.isClosed) {
|
||||||
|
menu.openTriggered()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.onReturnPressed: {
|
||||||
|
if (menu.isClosed) {
|
||||||
|
menu.openTriggered()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
|
@ -173,7 +196,7 @@ Item {
|
||||||
if (rootButtonClickedFunction && typeof rootButtonClickedFunction === "function") {
|
if (rootButtonClickedFunction && typeof rootButtonClickedFunction === "function") {
|
||||||
rootButtonClickedFunction()
|
rootButtonClickedFunction()
|
||||||
} else {
|
} else {
|
||||||
menu.open()
|
menu.openTriggered()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -186,27 +209,10 @@ Item {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
expandedHeight: drawerParent.height * drawerHeight
|
expandedHeight: drawerParent.height * drawerHeight
|
||||||
|
|
||||||
onClosed: {
|
expandedStateContent: Item {
|
||||||
root.popupClosedFunc()
|
|
||||||
}
|
|
||||||
|
|
||||||
expandedContent: Item {
|
|
||||||
id: container
|
id: container
|
||||||
implicitHeight: menu.expandedHeight
|
implicitHeight: menu.expandedHeight
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: menu
|
|
||||||
enabled: !GC.isMobile()
|
|
||||||
function onOpened() {
|
|
||||||
focusItem.forceActiveFocus()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: focusItem
|
|
||||||
KeyNavigation.tab: backButton
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: header
|
id: header
|
||||||
|
|
||||||
|
@ -218,22 +224,16 @@ 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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FlickableType {
|
|
||||||
id: flickable
|
|
||||||
anchors.top: header.bottom
|
|
||||||
anchors.topMargin: 16
|
|
||||||
contentHeight: col.implicitHeight
|
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
id: col
|
id: col
|
||||||
anchors.top: parent.top
|
anchors.top: header.bottom
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
|
anchors.topMargin: 16
|
||||||
|
|
||||||
spacing: 16
|
spacing: 16
|
||||||
|
|
||||||
|
@ -251,28 +251,8 @@ Item {
|
||||||
Loader {
|
Loader {
|
||||||
id: listViewLoader
|
id: listViewLoader
|
||||||
sourceComponent: root.listView
|
sourceComponent: root.listView
|
||||||
|
|
||||||
onLoaded: {
|
|
||||||
listViewLoader.item.parentFlickable = flickable
|
|
||||||
listViewLoader.item.lastItemTabClicked = function() {
|
|
||||||
focusItem.forceActiveFocus()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onEnterPressed: {
|
|
||||||
if (menu.isClosed) {
|
|
||||||
menu.open()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onReturnPressed: {
|
|
||||||
if (menu.isClosed) {
|
|
||||||
menu.open()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -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
|
||||||
|
@ -24,7 +25,7 @@ Flickable {
|
||||||
Keys.onUpPressed: scrollBar.decrease()
|
Keys.onUpPressed: scrollBar.decrease()
|
||||||
Keys.onDownPressed: scrollBar.increase()
|
Keys.onDownPressed: scrollBar.increase()
|
||||||
|
|
||||||
ScrollBar.vertical: ScrollBar {
|
ScrollBar.vertical: ScrollBarType {
|
||||||
id: scrollBar
|
id: scrollBar
|
||||||
policy: fl.height >= fl.contentHeight ? ScrollBar.AlwaysOff : ScrollBar.AlwaysOn
|
policy: fl.height >= fl.contentHeight ? ScrollBar.AlwaysOff : ScrollBar.AlwaysOn
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 }
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ ListView {
|
||||||
|
|
||||||
property bool dividerVisible: false
|
property bool dividerVisible: false
|
||||||
|
|
||||||
currentIndex: 0
|
property int selectedIndex: 0
|
||||||
|
|
||||||
width: rootWidth
|
width: rootWidth
|
||||||
height: menuContent.contentItem.height
|
height: menuContent.contentItem.height
|
||||||
|
@ -45,7 +45,7 @@ ListView {
|
||||||
rightImageSource: imageSource
|
rightImageSource: imageSource
|
||||||
|
|
||||||
clickedFunction: function() {
|
clickedFunction: function() {
|
||||||
menuContent.currentIndex = index
|
menuContent.selectedIndex = index
|
||||||
menuContent.selectedText = name
|
menuContent.selectedText = name
|
||||||
if (menuContent.clickedFunction && typeof menuContent.clickedFunction === "function") {
|
if (menuContent.clickedFunction && typeof menuContent.clickedFunction === "function") {
|
||||||
menuContent.clickedFunction()
|
menuContent.clickedFunction()
|
||||||
|
@ -62,7 +62,7 @@ ListView {
|
||||||
}
|
}
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
if (menuContent.currentIndex === index) {
|
if (menuContent.selectedIndex === index) {
|
||||||
menuContent.selectedText = name
|
menuContent.selectedText = name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,79 +20,31 @@ ListView {
|
||||||
|
|
||||||
property var clickedFunction
|
property var clickedFunction
|
||||||
|
|
||||||
currentIndex: 0
|
property int selectedIndex: 0
|
||||||
|
|
||||||
width: rootWidth
|
width: rootWidth
|
||||||
height: root.contentItem.height
|
height: root.contentItem.height
|
||||||
|
|
||||||
clip: true
|
clip: true
|
||||||
interactive: false
|
reuseItems: true
|
||||||
|
|
||||||
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: {
|
|
||||||
if (currentFocusIndex < this.count - 1) {
|
|
||||||
currentFocusIndex += 1
|
|
||||||
} else {
|
|
||||||
currentFocusIndex = 0
|
|
||||||
}
|
|
||||||
this.itemAtIndex(currentFocusIndex).forceActiveFocus()
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: focusItem
|
|
||||||
Keys.onTabPressed: {
|
|
||||||
root.forceActiveFocus()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onVisibleChanged: {
|
|
||||||
if (visible) {
|
|
||||||
focusItem.forceActiveFocus()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onCurrentFocusIndexChanged: {
|
|
||||||
if (parentFlickable) {
|
|
||||||
parentFlickable.ensureVisible(this.itemAtIndex(currentFocusIndex))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ButtonGroup {
|
ButtonGroup {
|
||||||
id: buttonGroup
|
id: buttonGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
function triggerCurrentItem() {
|
function triggerCurrentItem() {
|
||||||
var item = root.itemAtIndex(currentIndex)
|
var item = root.itemAtIndex(selectedIndex)
|
||||||
var radioButton = item.children[0].children[0]
|
item.selectable.clicked()
|
||||||
radioButton.clicked()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
delegate: Item {
|
delegate: ColumnLayout {
|
||||||
implicitWidth: rootWidth
|
|
||||||
implicitHeight: content.implicitHeight
|
|
||||||
|
|
||||||
onActiveFocusChanged: {
|
|
||||||
if (activeFocus) {
|
|
||||||
radioButton.forceActiveFocus()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
id: content
|
id: content
|
||||||
|
|
||||||
anchors.fill: parent
|
property alias selectable: radioButton
|
||||||
|
|
||||||
|
implicitWidth: rootWidth
|
||||||
|
|
||||||
RadioButton {
|
RadioButton {
|
||||||
id: radioButton
|
id: radioButton
|
||||||
|
@ -102,6 +54,32 @@ ListView {
|
||||||
|
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
|
|
||||||
|
property bool isFocusable: true
|
||||||
|
|
||||||
|
Keys.onTabPressed: {
|
||||||
|
FocusController.nextKeyTabItem()
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.onBacktabPressed: {
|
||||||
|
FocusController.previousKeyTabItem()
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.onUpPressed: {
|
||||||
|
FocusController.nextKeyUpItem()
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.onDownPressed: {
|
||||||
|
FocusController.nextKeyDownItem()
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.onLeftPressed: {
|
||||||
|
FocusController.nextKeyLeftItem()
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.onRightPressed: {
|
||||||
|
FocusController.nextKeyRightItem()
|
||||||
|
}
|
||||||
|
|
||||||
indicator: Rectangle {
|
indicator: Rectangle {
|
||||||
width: parent.width - 1
|
width: parent.width - 1
|
||||||
height: parent.height
|
height: parent.height
|
||||||
|
@ -155,20 +133,19 @@ ListView {
|
||||||
}
|
}
|
||||||
|
|
||||||
ButtonGroup.group: buttonGroup
|
ButtonGroup.group: buttonGroup
|
||||||
checked: root.currentIndex === index
|
checked: root.selectedIndex === index
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
root.currentIndex = index
|
root.selectedIndex = index
|
||||||
root.selectedText = name
|
root.selectedText = name
|
||||||
if (clickedFunction && typeof clickedFunction === "function") {
|
if (clickedFunction && typeof clickedFunction === "function") {
|
||||||
clickedFunction()
|
clickedFunction()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
if (root.currentIndex === index) {
|
if (root.selectedIndex === index) {
|
||||||
root.selectedText = name
|
root.selectedText = name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,53 +9,22 @@ Item {
|
||||||
|
|
||||||
property StackView stackView: StackView.view
|
property StackView stackView: StackView.view
|
||||||
|
|
||||||
property var defaultActiveFocusItem: null
|
|
||||||
|
|
||||||
onVisibleChanged: {
|
onVisibleChanged: {
|
||||||
if (visible && !GC.isMobile()) {
|
if (visible) {
|
||||||
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) {
|
console.debug(">>> PageType timer triggered")
|
||||||
defaultActiveFocusItem.forceActiveFocus()
|
FocusController.resetRootObject()
|
||||||
}
|
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: true // Start the timer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,17 @@ Popup {
|
||||||
radius: 4
|
radius: 4
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: timer
|
||||||
|
interval: 200 // Milliseconds
|
||||||
|
onTriggered: {
|
||||||
|
FocusController.pushRootObject(root)
|
||||||
|
FocusController.setFocusItem(closeButton)
|
||||||
|
}
|
||||||
|
repeat: false // Stop the timer after one trigger
|
||||||
|
running: true // Start the timer
|
||||||
|
}
|
||||||
|
|
||||||
contentItem: Item {
|
contentItem: Item {
|
||||||
implicitWidth: content.implicitWidth
|
implicitWidth: content.implicitWidth
|
||||||
implicitHeight: content.implicitHeight
|
implicitHeight: content.implicitHeight
|
||||||
|
@ -72,11 +84,6 @@ Popup {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
|
||||||
id: focusItem
|
|
||||||
KeyNavigation.tab: closeButton
|
|
||||||
}
|
|
||||||
|
|
||||||
BasicButtonType {
|
BasicButtonType {
|
||||||
id: closeButton
|
id: closeButton
|
||||||
visible: closeButtonVisible
|
visible: closeButtonVisible
|
||||||
|
@ -92,7 +99,6 @@ Popup {
|
||||||
borderWidth: 0
|
borderWidth: 0
|
||||||
|
|
||||||
text: qsTr("Close")
|
text: qsTr("Close")
|
||||||
KeyNavigation.tab: focusItem
|
|
||||||
|
|
||||||
clickedFunc: function() {
|
clickedFunc: function() {
|
||||||
root.close()
|
root.close()
|
||||||
|
|
11
client/ui/qml/Controls2/ScrollBarType.qml
Normal file
11
client/ui/qml/Controls2/ScrollBarType.qml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
|
||||||
|
import "./"
|
||||||
|
import "../Controls2"
|
||||||
|
|
||||||
|
ScrollBar {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
policy: parent.height >= parent.contentHeight ? ScrollBar.AlwaysOff : ScrollBar.AlwaysOn
|
||||||
|
}
|
|
@ -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) {
|
||||||
|
@ -131,13 +158,15 @@ Switch {
|
||||||
enabled: false
|
enabled: false
|
||||||
}
|
}
|
||||||
|
|
||||||
Keys.onEnterPressed: {
|
Keys.onEnterPressed: event => handleSwitch(event)
|
||||||
root.checked = !root.checked
|
Keys.onReturnPressed: event => handleSwitch(event)
|
||||||
root.checkedChanged()
|
Keys.onSpacePressed: event => handleSwitch(event)
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onReturnPressed: {
|
function handleSwitch(event) {
|
||||||
|
if (!event.isAutoRepeat) {
|
||||||
root.checked = !root.checked
|
root.checked = !root.checked
|
||||||
root.checkedChanged()
|
root.checkedChanged()
|
||||||
}
|
}
|
||||||
|
event.accepted = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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();
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,8 +28,33 @@ 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
|
|
||||||
|
|
||||||
indicator: Rectangle {
|
indicator: Rectangle {
|
||||||
id: background
|
id: background
|
||||||
|
|
|
@ -16,40 +16,28 @@ import "../Components"
|
||||||
PageType {
|
PageType {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
defaultActiveFocusItem: focusItem
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: focusItem
|
|
||||||
KeyNavigation.tab: backButton
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
id: backButtonLayout
|
|
||||||
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
|
|
||||||
anchors.topMargin: 20
|
|
||||||
|
|
||||||
BackButtonType {
|
BackButtonType {
|
||||||
id: backButton
|
id: backButton
|
||||||
// KeyNavigation.tab: removeButton
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
FlickableType {
|
|
||||||
id: fl
|
|
||||||
anchors.top: backButtonLayout.bottom
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
contentHeight: content.implicitHeight
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
id: content
|
|
||||||
|
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
|
anchors.topMargin: 20
|
||||||
|
}
|
||||||
|
|
||||||
|
ListView {
|
||||||
|
id: listView
|
||||||
|
anchors.top: backButton.bottom
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.left: parent.left
|
||||||
|
|
||||||
|
property bool isFocusable: true
|
||||||
|
|
||||||
|
ScrollBar.vertical: ScrollBarType {}
|
||||||
|
|
||||||
|
header: ColumnLayout {
|
||||||
|
width: listView.width
|
||||||
|
|
||||||
HeaderType {
|
HeaderType {
|
||||||
id: header
|
id: header
|
||||||
|
@ -60,7 +48,14 @@ PageType {
|
||||||
|
|
||||||
headerText: "Dev menu"
|
headerText: "Dev menu"
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
model: 1
|
||||||
|
clip: true
|
||||||
|
spacing: 16
|
||||||
|
|
||||||
|
delegate: ColumnLayout {
|
||||||
|
width: listView.width
|
||||||
|
|
||||||
TextFieldWithHeaderType {
|
TextFieldWithHeaderType {
|
||||||
id: passwordTextField
|
id: passwordTextField
|
||||||
|
@ -69,7 +64,6 @@ PageType {
|
||||||
Layout.topMargin: 16
|
Layout.topMargin: 16
|
||||||
Layout.rightMargin: 16
|
Layout.rightMargin: 16
|
||||||
Layout.leftMargin: 16
|
Layout.leftMargin: 16
|
||||||
parentFlickable: fl
|
|
||||||
|
|
||||||
headerText: qsTr("Gateway endpoint")
|
headerText: qsTr("Gateway endpoint")
|
||||||
textFieldText: SettingsController.gatewayEndpoint
|
textFieldText: SettingsController.gatewayEndpoint
|
||||||
|
@ -86,17 +80,19 @@ PageType {
|
||||||
SettingsController.gatewayEndpoint = textFieldText
|
SettingsController.gatewayEndpoint = textFieldText
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// KeyNavigation.tab: saveButton
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
footer: ColumnLayout {
|
||||||
|
width: listView.width
|
||||||
|
|
||||||
SwitcherType {
|
SwitcherType {
|
||||||
id: switcher
|
id: switcher
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: 24
|
||||||
Layout.rightMargin: 16
|
Layout.rightMargin: 16
|
||||||
Layout.leftMargin: 16
|
Layout.leftMargin: 16
|
||||||
Layout.topMargin: 16
|
|
||||||
|
|
||||||
text: qsTr("Dev gateway environment")
|
text: qsTr("Dev gateway environment")
|
||||||
checked: SettingsController.isDevGatewayEnv
|
checked: SettingsController.isDevGatewayEnv
|
||||||
|
|
|
@ -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
|
||||||
|
@ -116,53 +115,37 @@ PageType {
|
||||||
Keys.onEnterPressed: splitTunnelingButton.clicked()
|
Keys.onEnterPressed: splitTunnelingButton.clicked()
|
||||||
Keys.onReturnPressed: splitTunnelingButton.clicked()
|
Keys.onReturnPressed: splitTunnelingButton.clicked()
|
||||||
|
|
||||||
KeyNavigation.tab: drawer
|
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
homeSplitTunnelingDrawer.open()
|
homeSplitTunnelingDrawer.openTriggered()
|
||||||
}
|
}
|
||||||
|
|
||||||
HomeSplitTunnelingDrawer {
|
HomeSplitTunnelingDrawer {
|
||||||
id: homeSplitTunnelingDrawer
|
id: homeSplitTunnelingDrawer
|
||||||
|
objectName: "homeSplitTunnelingDrawer"
|
||||||
|
|
||||||
parent: root
|
parent: root
|
||||||
|
|
||||||
onClosed: {
|
|
||||||
if (!GC.isMobile()) {
|
|
||||||
focusItem.forceActiveFocus()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
DrawerType2 {
|
DrawerType2 {
|
||||||
id: drawer
|
id: drawer
|
||||||
|
objectName: "drawerProtocol"
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
||||||
onClosed: {
|
collapsedStateContent: Item {
|
||||||
if (!GC.isMobile()) {
|
objectName: "ProtocolDrawerCollapsedContent"
|
||||||
focusItem.forceActiveFocus()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
collapsedContent: Item {
|
|
||||||
implicitHeight: Qt.platform.os !== "ios" ? root.height * 0.9 : screen.height * 0.77
|
implicitHeight: Qt.platform.os !== "ios" ? root.height * 0.9 : screen.height * 0.77
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
drawer.expandedHeight = implicitHeight
|
drawer.expandedHeight = implicitHeight
|
||||||
}
|
}
|
||||||
Connections {
|
|
||||||
target: drawer
|
|
||||||
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
|
||||||
|
@ -181,6 +164,8 @@ PageType {
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
|
objectName: "rowLayout"
|
||||||
|
|
||||||
Layout.topMargin: 14
|
Layout.topMargin: 14
|
||||||
Layout.leftMargin: 24
|
Layout.leftMargin: 24
|
||||||
Layout.rightMargin: 24
|
Layout.rightMargin: 24
|
||||||
|
@ -189,9 +174,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 {
|
||||||
|
@ -199,8 +186,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 {
|
||||||
|
@ -209,7 +196,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 {
|
||||||
|
@ -220,6 +207,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
|
||||||
|
@ -228,8 +217,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 }
|
||||||
}
|
}
|
||||||
|
@ -237,10 +224,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"
|
||||||
|
@ -255,18 +243,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
|
||||||
|
@ -305,18 +292,9 @@ PageType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: drawer
|
|
||||||
enabled: !GC.isMobile()
|
|
||||||
function onIsCollapsedChanged() {
|
|
||||||
if (!drawer.isCollapsed) {
|
|
||||||
focusItem1.forceActiveFocus()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: serversMenuHeader
|
id: serversMenuHeader
|
||||||
|
objectName: "serversMenuHeader"
|
||||||
|
|
||||||
anchors.top: collapsed.bottom
|
anchors.top: collapsed.bottom
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
|
@ -328,13 +306,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
|
||||||
|
@ -345,28 +319,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() {
|
||||||
|
@ -408,167 +383,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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,18 +16,6 @@ import "../Components"
|
||||||
PageType {
|
PageType {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
defaultActiveFocusItem: listview.currentItem.mtuTextField.textField
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: focusItem
|
|
||||||
onFocusChanged: {
|
|
||||||
if (activeFocus) {
|
|
||||||
fl.ensureVisible(focusItem)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
KeyNavigation.tab: backButton
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: backButtonLayout
|
id: backButtonLayout
|
||||||
|
|
||||||
|
@ -39,31 +27,44 @@ PageType {
|
||||||
|
|
||||||
BackButtonType {
|
BackButtonType {
|
||||||
id: backButton
|
id: backButton
|
||||||
KeyNavigation.tab: listview.currentItem.mtuTextField.textField
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FlickableType {
|
|
||||||
id: fl
|
|
||||||
anchors.top: backButtonLayout.bottom
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
contentHeight: content.implicitHeight + saveButton.implicitHeight + saveButton.anchors.bottomMargin + saveButton.anchors.topMargin
|
|
||||||
|
|
||||||
Column {
|
|
||||||
id: content
|
|
||||||
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
|
|
||||||
ListView {
|
ListView {
|
||||||
id: listview
|
id: listview
|
||||||
|
|
||||||
|
anchors.top: backButtonLayout.bottom
|
||||||
|
anchors.bottom: saveButton.top
|
||||||
|
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: listview.contentItem.height
|
|
||||||
|
|
||||||
clip: true
|
clip: true
|
||||||
interactive: false
|
|
||||||
|
property bool isFocusable: true
|
||||||
|
|
||||||
|
Keys.onTabPressed: {
|
||||||
|
FocusController.nextKeyTabItem()
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.onBacktabPressed: {
|
||||||
|
FocusController.previousKeyTabItem()
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.onUpPressed: {
|
||||||
|
FocusController.nextKeyUpItem()
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.onDownPressed: {
|
||||||
|
FocusController.nextKeyDownItem()
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.onLeftPressed: {
|
||||||
|
FocusController.nextKeyLeftItem()
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.onRightPressed: {
|
||||||
|
FocusController.nextKeyRightItem()
|
||||||
|
}
|
||||||
|
|
||||||
model: AwgConfigModel
|
model: AwgConfigModel
|
||||||
|
|
||||||
|
@ -122,7 +123,6 @@ PageType {
|
||||||
headerText: "Jc - Junk packet count"
|
headerText: "Jc - Junk packet count"
|
||||||
textFieldText: clientJunkPacketCount
|
textFieldText: clientJunkPacketCount
|
||||||
textField.validator: IntValidator { bottom: 0 }
|
textField.validator: IntValidator { bottom: 0 }
|
||||||
parentFlickable: fl
|
|
||||||
|
|
||||||
textField.onEditingFinished: {
|
textField.onEditingFinished: {
|
||||||
if (textFieldText !== clientJunkPacketCount) {
|
if (textFieldText !== clientJunkPacketCount) {
|
||||||
|
@ -143,7 +143,6 @@ PageType {
|
||||||
headerText: "Jmin - Junk packet minimum size"
|
headerText: "Jmin - Junk packet minimum size"
|
||||||
textFieldText: clientJunkPacketMinSize
|
textFieldText: clientJunkPacketMinSize
|
||||||
textField.validator: IntValidator { bottom: 0 }
|
textField.validator: IntValidator { bottom: 0 }
|
||||||
parentFlickable: fl
|
|
||||||
|
|
||||||
textField.onEditingFinished: {
|
textField.onEditingFinished: {
|
||||||
if (textFieldText !== clientJunkPacketMinSize) {
|
if (textFieldText !== clientJunkPacketMinSize) {
|
||||||
|
@ -164,7 +163,6 @@ PageType {
|
||||||
headerText: "Jmax - Junk packet maximum size"
|
headerText: "Jmax - Junk packet maximum size"
|
||||||
textFieldText: clientJunkPacketMaxSize
|
textFieldText: clientJunkPacketMaxSize
|
||||||
textField.validator: IntValidator { bottom: 0 }
|
textField.validator: IntValidator { bottom: 0 }
|
||||||
parentFlickable: fl
|
|
||||||
|
|
||||||
textField.onEditingFinished: {
|
textField.onEditingFinished: {
|
||||||
if (textFieldText !== clientJunkPacketMaxSize) {
|
if (textFieldText !== clientJunkPacketMaxSize) {
|
||||||
|
@ -174,7 +172,6 @@ PageType {
|
||||||
|
|
||||||
checkEmptyText: true
|
checkEmptyText: true
|
||||||
|
|
||||||
Keys.onTabPressed: saveButton.forceActiveFocus()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Header2TextType {
|
Header2TextType {
|
||||||
|
@ -243,7 +240,6 @@ PageType {
|
||||||
id: underloadPacketMagicHeaderTextField
|
id: underloadPacketMagicHeaderTextField
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: 16
|
Layout.topMargin: 16
|
||||||
parentFlickable: fl
|
|
||||||
|
|
||||||
enabled: false
|
enabled: false
|
||||||
|
|
||||||
|
@ -264,8 +260,6 @@ PageType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BasicButtonType {
|
BasicButtonType {
|
||||||
id: saveButton
|
id: saveButton
|
||||||
|
@ -283,7 +277,11 @@ PageType {
|
||||||
|
|
||||||
text: qsTr("Save")
|
text: qsTr("Save")
|
||||||
|
|
||||||
Keys.onTabPressed: lastItemTabClicked(focusItem)
|
onActiveFocusChanged: {
|
||||||
|
if(activeFocus) {
|
||||||
|
listview.positionViewAtEnd()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
clickedFunc: function() {
|
clickedFunc: function() {
|
||||||
forceActiveFocus()
|
forceActiveFocus()
|
||||||
|
|
|
@ -2,6 +2,8 @@ import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
import QtCore
|
||||||
|
|
||||||
import SortFilterProxyModel 0.2
|
import SortFilterProxyModel 0.2
|
||||||
|
|
||||||
import PageEnum 1.0
|
import PageEnum 1.0
|
||||||
|
@ -17,18 +19,6 @@ import "../Components"
|
||||||
PageType {
|
PageType {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
defaultActiveFocusItem: listview.currentItem.portTextField.textField
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: focusItem
|
|
||||||
onFocusChanged: {
|
|
||||||
if (activeFocus) {
|
|
||||||
fl.ensureVisible(focusItem)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
KeyNavigation.tab: backButton
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: backButtonLayout
|
id: backButtonLayout
|
||||||
|
|
||||||
|
@ -40,31 +30,44 @@ PageType {
|
||||||
|
|
||||||
BackButtonType {
|
BackButtonType {
|
||||||
id: backButton
|
id: backButton
|
||||||
KeyNavigation.tab: listview.currentItem.portTextField.textField
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FlickableType {
|
|
||||||
id: fl
|
|
||||||
anchors.top: backButtonLayout.bottom
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
contentHeight: content.implicitHeight
|
|
||||||
|
|
||||||
Column {
|
|
||||||
id: content
|
|
||||||
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
|
|
||||||
ListView {
|
ListView {
|
||||||
id: listview
|
id: listview
|
||||||
|
|
||||||
|
anchors.top: backButtonLayout.bottom
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: listview.contentItem.height
|
|
||||||
|
property bool isFocusable: true
|
||||||
|
|
||||||
|
Keys.onTabPressed: {
|
||||||
|
FocusController.nextKeyTabItem()
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.onBacktabPressed: {
|
||||||
|
FocusController.previousKeyTabItem()
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.onUpPressed: {
|
||||||
|
FocusController.nextKeyUpItem()
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.onDownPressed: {
|
||||||
|
FocusController.nextKeyDownItem()
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.onLeftPressed: {
|
||||||
|
FocusController.nextKeyLeftItem()
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.onRightPressed: {
|
||||||
|
FocusController.nextKeyRightItem()
|
||||||
|
}
|
||||||
|
|
||||||
clip: true
|
clip: true
|
||||||
interactive: false
|
|
||||||
|
|
||||||
model: AwgConfigModel
|
model: AwgConfigModel
|
||||||
|
|
||||||
|
@ -105,7 +108,6 @@ PageType {
|
||||||
textFieldText: port
|
textFieldText: port
|
||||||
textField.maximumLength: 5
|
textField.maximumLength: 5
|
||||||
textField.validator: IntValidator { bottom: 1; top: 65535 }
|
textField.validator: IntValidator { bottom: 1; top: 65535 }
|
||||||
parentFlickable: fl
|
|
||||||
|
|
||||||
textField.onEditingFinished: {
|
textField.onEditingFinished: {
|
||||||
if (textFieldText !== port) {
|
if (textFieldText !== port) {
|
||||||
|
@ -114,8 +116,26 @@ PageType {
|
||||||
}
|
}
|
||||||
|
|
||||||
checkEmptyText: true
|
checkEmptyText: true
|
||||||
|
}
|
||||||
|
|
||||||
KeyNavigation.tab: junkPacketCountTextField.textField
|
TextFieldWithHeaderType {
|
||||||
|
id: mtuTextField
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: 16
|
||||||
|
|
||||||
|
headerText: qsTr("MTU")
|
||||||
|
textFieldText: mtu
|
||||||
|
textField.validator: IntValidator { bottom: 576; top: 65535 }
|
||||||
|
|
||||||
|
textField.onEditingFinished: {
|
||||||
|
if (textFieldText === "") {
|
||||||
|
textFieldText = "0"
|
||||||
|
}
|
||||||
|
if (textFieldText !== mtu) {
|
||||||
|
mtu = textFieldText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
checkEmptyText: true
|
||||||
}
|
}
|
||||||
|
|
||||||
TextFieldWithHeaderType {
|
TextFieldWithHeaderType {
|
||||||
|
@ -126,7 +146,6 @@ PageType {
|
||||||
headerText: qsTr("Jc - Junk packet count")
|
headerText: qsTr("Jc - Junk packet count")
|
||||||
textFieldText: serverJunkPacketCount
|
textFieldText: serverJunkPacketCount
|
||||||
textField.validator: IntValidator { bottom: 0 }
|
textField.validator: IntValidator { bottom: 0 }
|
||||||
parentFlickable: fl
|
|
||||||
|
|
||||||
textField.onEditingFinished: {
|
textField.onEditingFinished: {
|
||||||
if (textFieldText === "") {
|
if (textFieldText === "") {
|
||||||
|
@ -139,8 +158,6 @@ PageType {
|
||||||
}
|
}
|
||||||
|
|
||||||
checkEmptyText: true
|
checkEmptyText: true
|
||||||
|
|
||||||
KeyNavigation.tab: junkPacketMinSizeTextField.textField
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TextFieldWithHeaderType {
|
TextFieldWithHeaderType {
|
||||||
|
@ -151,7 +168,6 @@ PageType {
|
||||||
headerText: qsTr("Jmin - Junk packet minimum size")
|
headerText: qsTr("Jmin - Junk packet minimum size")
|
||||||
textFieldText: serverJunkPacketMinSize
|
textFieldText: serverJunkPacketMinSize
|
||||||
textField.validator: IntValidator { bottom: 0 }
|
textField.validator: IntValidator { bottom: 0 }
|
||||||
parentFlickable: fl
|
|
||||||
|
|
||||||
textField.onEditingFinished: {
|
textField.onEditingFinished: {
|
||||||
if (textFieldText !== serverJunkPacketMinSize) {
|
if (textFieldText !== serverJunkPacketMinSize) {
|
||||||
|
@ -160,8 +176,6 @@ PageType {
|
||||||
}
|
}
|
||||||
|
|
||||||
checkEmptyText: true
|
checkEmptyText: true
|
||||||
|
|
||||||
KeyNavigation.tab: junkPacketMaxSizeTextField.textField
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TextFieldWithHeaderType {
|
TextFieldWithHeaderType {
|
||||||
|
@ -172,7 +186,6 @@ PageType {
|
||||||
headerText: qsTr("Jmax - Junk packet maximum size")
|
headerText: qsTr("Jmax - Junk packet maximum size")
|
||||||
textFieldText: serverJunkPacketMaxSize
|
textFieldText: serverJunkPacketMaxSize
|
||||||
textField.validator: IntValidator { bottom: 0 }
|
textField.validator: IntValidator { bottom: 0 }
|
||||||
parentFlickable: fl
|
|
||||||
|
|
||||||
textField.onEditingFinished: {
|
textField.onEditingFinished: {
|
||||||
if (textFieldText !== serverJunkPacketMaxSize) {
|
if (textFieldText !== serverJunkPacketMaxSize) {
|
||||||
|
@ -181,8 +194,6 @@ PageType {
|
||||||
}
|
}
|
||||||
|
|
||||||
checkEmptyText: true
|
checkEmptyText: true
|
||||||
|
|
||||||
KeyNavigation.tab: initPacketJunkSizeTextField.textField
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TextFieldWithHeaderType {
|
TextFieldWithHeaderType {
|
||||||
|
@ -193,7 +204,6 @@ PageType {
|
||||||
headerText: qsTr("S1 - Init packet junk size")
|
headerText: qsTr("S1 - Init packet junk size")
|
||||||
textFieldText: serverInitPacketJunkSize
|
textFieldText: serverInitPacketJunkSize
|
||||||
textField.validator: IntValidator { bottom: 0 }
|
textField.validator: IntValidator { bottom: 0 }
|
||||||
parentFlickable: fl
|
|
||||||
|
|
||||||
textField.onEditingFinished: {
|
textField.onEditingFinished: {
|
||||||
if (textFieldText !== serverInitPacketJunkSize) {
|
if (textFieldText !== serverInitPacketJunkSize) {
|
||||||
|
@ -203,7 +213,11 @@ PageType {
|
||||||
|
|
||||||
checkEmptyText: true
|
checkEmptyText: true
|
||||||
|
|
||||||
KeyNavigation.tab: responsePacketJunkSizeTextField.textField
|
onActiveFocusChanged: {
|
||||||
|
if(activeFocus) {
|
||||||
|
listview.positionViewAtEnd()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TextFieldWithHeaderType {
|
TextFieldWithHeaderType {
|
||||||
|
@ -214,7 +228,6 @@ PageType {
|
||||||
headerText: qsTr("S2 - Response packet junk size")
|
headerText: qsTr("S2 - Response packet junk size")
|
||||||
textFieldText: serverResponsePacketJunkSize
|
textFieldText: serverResponsePacketJunkSize
|
||||||
textField.validator: IntValidator { bottom: 0 }
|
textField.validator: IntValidator { bottom: 0 }
|
||||||
parentFlickable: fl
|
|
||||||
|
|
||||||
textField.onEditingFinished: {
|
textField.onEditingFinished: {
|
||||||
if (textFieldText !== serverResponsePacketJunkSize) {
|
if (textFieldText !== serverResponsePacketJunkSize) {
|
||||||
|
@ -224,7 +237,11 @@ PageType {
|
||||||
|
|
||||||
checkEmptyText: true
|
checkEmptyText: true
|
||||||
|
|
||||||
KeyNavigation.tab: initPacketMagicHeaderTextField.textField
|
onActiveFocusChanged: {
|
||||||
|
if(activeFocus) {
|
||||||
|
listview.positionViewAtEnd()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TextFieldWithHeaderType {
|
TextFieldWithHeaderType {
|
||||||
|
@ -235,7 +252,6 @@ PageType {
|
||||||
headerText: qsTr("H1 - Init packet magic header")
|
headerText: qsTr("H1 - Init packet magic header")
|
||||||
textFieldText: serverInitPacketMagicHeader
|
textFieldText: serverInitPacketMagicHeader
|
||||||
textField.validator: IntValidator { bottom: 0 }
|
textField.validator: IntValidator { bottom: 0 }
|
||||||
parentFlickable: fl
|
|
||||||
|
|
||||||
textField.onEditingFinished: {
|
textField.onEditingFinished: {
|
||||||
if (textFieldText !== serverInitPacketMagicHeader) {
|
if (textFieldText !== serverInitPacketMagicHeader) {
|
||||||
|
@ -244,8 +260,6 @@ PageType {
|
||||||
}
|
}
|
||||||
|
|
||||||
checkEmptyText: true
|
checkEmptyText: true
|
||||||
|
|
||||||
KeyNavigation.tab: responsePacketMagicHeaderTextField.textField
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TextFieldWithHeaderType {
|
TextFieldWithHeaderType {
|
||||||
|
@ -256,7 +270,6 @@ PageType {
|
||||||
headerText: qsTr("H2 - Response packet magic header")
|
headerText: qsTr("H2 - Response packet magic header")
|
||||||
textFieldText: serverResponsePacketMagicHeader
|
textFieldText: serverResponsePacketMagicHeader
|
||||||
textField.validator: IntValidator { bottom: 0 }
|
textField.validator: IntValidator { bottom: 0 }
|
||||||
parentFlickable: fl
|
|
||||||
|
|
||||||
textField.onEditingFinished: {
|
textField.onEditingFinished: {
|
||||||
if (textFieldText !== serverResponsePacketMagicHeader) {
|
if (textFieldText !== serverResponsePacketMagicHeader) {
|
||||||
|
@ -265,8 +278,6 @@ PageType {
|
||||||
}
|
}
|
||||||
|
|
||||||
checkEmptyText: true
|
checkEmptyText: true
|
||||||
|
|
||||||
KeyNavigation.tab: transportPacketMagicHeaderTextField.textField
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TextFieldWithHeaderType {
|
TextFieldWithHeaderType {
|
||||||
|
@ -277,7 +288,6 @@ PageType {
|
||||||
headerText: qsTr("H4 - Transport packet magic header")
|
headerText: qsTr("H4 - Transport packet magic header")
|
||||||
textFieldText: serverTransportPacketMagicHeader
|
textFieldText: serverTransportPacketMagicHeader
|
||||||
textField.validator: IntValidator { bottom: 0 }
|
textField.validator: IntValidator { bottom: 0 }
|
||||||
parentFlickable: fl
|
|
||||||
|
|
||||||
textField.onEditingFinished: {
|
textField.onEditingFinished: {
|
||||||
if (textFieldText !== serverTransportPacketMagicHeader) {
|
if (textFieldText !== serverTransportPacketMagicHeader) {
|
||||||
|
@ -286,15 +296,12 @@ PageType {
|
||||||
}
|
}
|
||||||
|
|
||||||
checkEmptyText: true
|
checkEmptyText: true
|
||||||
|
|
||||||
KeyNavigation.tab: underloadPacketMagicHeaderTextField.textField
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TextFieldWithHeaderType {
|
TextFieldWithHeaderType {
|
||||||
id: underloadPacketMagicHeaderTextField
|
id: underloadPacketMagicHeaderTextField
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: 16
|
Layout.topMargin: 16
|
||||||
parentFlickable: fl
|
|
||||||
|
|
||||||
headerText: qsTr("H3 - Underload packet magic header")
|
headerText: qsTr("H3 - Underload packet magic header")
|
||||||
textFieldText: serverUnderloadPacketMagicHeader
|
textFieldText: serverUnderloadPacketMagicHeader
|
||||||
|
@ -307,13 +314,10 @@ PageType {
|
||||||
}
|
}
|
||||||
|
|
||||||
checkEmptyText: true
|
checkEmptyText: true
|
||||||
|
|
||||||
KeyNavigation.tab: saveRestartButton
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BasicButtonType {
|
BasicButtonType {
|
||||||
id: saveRestartButton
|
id: saveRestartButton
|
||||||
parentFlickable: fl
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: 24
|
Layout.topMargin: 24
|
||||||
|
@ -332,7 +336,11 @@ PageType {
|
||||||
|
|
||||||
text: qsTr("Save")
|
text: qsTr("Save")
|
||||||
|
|
||||||
Keys.onTabPressed: lastItemTabClicked(focusItem)
|
onActiveFocusChanged: {
|
||||||
|
if(activeFocus) {
|
||||||
|
listview.positionViewAtEnd()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
clickedFunc: function() {
|
clickedFunc: function() {
|
||||||
forceActiveFocus()
|
forceActiveFocus()
|
||||||
|
@ -379,5 +387,3 @@ PageType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,11 +48,13 @@ PageType {
|
||||||
ListView {
|
ListView {
|
||||||
id: listview
|
id: listview
|
||||||
|
|
||||||
|
property int selectedIndex: 0
|
||||||
|
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: listview.contentItem.height
|
height: listview.contentItem.height
|
||||||
|
|
||||||
clip: true
|
clip: true
|
||||||
interactive: false
|
reuseItems: true
|
||||||
|
|
||||||
model: CloakConfigModel
|
model: CloakConfigModel
|
||||||
|
|
||||||
|
@ -110,8 +104,6 @@ PageType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
KeyNavigation.tab: portTextField.textField
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TextFieldWithHeaderType {
|
TextFieldWithHeaderType {
|
||||||
|
@ -130,8 +122,6 @@ PageType {
|
||||||
port = textFieldText
|
port = textFieldText
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
KeyNavigation.tab: cipherDropDown
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DropDownType {
|
DropDownType {
|
||||||
|
@ -143,7 +133,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 +150,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: {
|
||||||
|
@ -169,7 +158,7 @@ PageType {
|
||||||
|
|
||||||
for (var i = 0; i < cipherListView.model.count; i++) {
|
for (var i = 0; i < cipherListView.model.count; i++) {
|
||||||
if (cipherListView.model.get(i).name === cipherDropDown.text) {
|
if (cipherListView.model.get(i).name === cipherDropDown.text) {
|
||||||
currentIndex = i
|
selectedIndex = i
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -184,7 +173,6 @@ PageType {
|
||||||
Layout.bottomMargin: 24
|
Layout.bottomMargin: 24
|
||||||
|
|
||||||
text: qsTr("Save")
|
text: qsTr("Save")
|
||||||
Keys.onTabPressed: lastItemTabClicked(focusItem)
|
|
||||||
|
|
||||||
clickedFunc: function() {
|
clickedFunc: function() {
|
||||||
forceActiveFocus()
|
forceActiveFocus()
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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 {
|
||||||
|
@ -75,13 +67,6 @@ PageType {
|
||||||
activeFocusOnTab: true
|
activeFocusOnTab: true
|
||||||
focus: true
|
focus: true
|
||||||
|
|
||||||
onActiveFocusChanged: {
|
|
||||||
if (focus) {
|
|
||||||
listView.currentIndex = 0
|
|
||||||
listView.currentItem.focusItem.forceActiveFocus()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
delegate: Item {
|
delegate: Item {
|
||||||
implicitWidth: parent.width
|
implicitWidth: parent.width
|
||||||
implicitHeight: delegateContent.implicitHeight
|
implicitHeight: delegateContent.implicitHeight
|
||||||
|
@ -101,11 +86,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 +103,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 +118,8 @@ PageType {
|
||||||
anchors.topMargin: 16
|
anchors.topMargin: 16
|
||||||
|
|
||||||
backButtonFunction: function() {
|
backButtonFunction: function() {
|
||||||
configContentDrawer.close()
|
configContentDrawer.closeTriggered()
|
||||||
}
|
}
|
||||||
|
|
||||||
KeyNavigation.tab: focusItem1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FlickableType {
|
FlickableType {
|
||||||
|
@ -226,7 +188,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.")
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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?")
|
||||||
|
|
|
@ -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,13 +56,6 @@ PageType {
|
||||||
|
|
||||||
model: WireGuardConfigModel
|
model: WireGuardConfigModel
|
||||||
|
|
||||||
activeFocusOnTab: true
|
|
||||||
onActiveFocusChanged: {
|
|
||||||
if (activeFocus) {
|
|
||||||
listview.itemAtIndex(0)?.focusItemId.forceActiveFocus()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
delegate: Item {
|
delegate: Item {
|
||||||
id: delegateItem
|
id: delegateItem
|
||||||
|
|
||||||
|
@ -109,8 +94,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 +103,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 +133,6 @@ PageType {
|
||||||
|
|
||||||
text: qsTr("Save")
|
text: qsTr("Save")
|
||||||
|
|
||||||
Keys.onTabPressed: lastItemTabClicked(focusItem)
|
|
||||||
|
|
||||||
onClicked: function() {
|
onClicked: function() {
|
||||||
forceActiveFocus()
|
forceActiveFocus()
|
||||||
|
|
||||||
|
|
|
@ -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,13 +57,6 @@ PageType {
|
||||||
|
|
||||||
model: XrayConfigModel
|
model: XrayConfigModel
|
||||||
|
|
||||||
activeFocusOnTab: true
|
|
||||||
onActiveFocusChanged: {
|
|
||||||
if (activeFocus) {
|
|
||||||
listview.itemAtIndex(0)?.focusItemId.forceActiveFocus()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
delegate: Item {
|
delegate: Item {
|
||||||
property alias focusItemId: textFieldWithHeaderType.textField
|
property alias focusItemId: textFieldWithHeaderType.textField
|
||||||
|
|
||||||
|
@ -103,8 +88,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 +111,6 @@ PageType {
|
||||||
|
|
||||||
text: qsTr("Save")
|
text: qsTr("Save")
|
||||||
|
|
||||||
Keys.onTabPressed: lastItemTabClicked(focusItem)
|
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
forceActiveFocus()
|
forceActiveFocus()
|
||||||
|
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"))
|
||||||
|
|
|
@ -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,14 +81,13 @@ PageType {
|
||||||
clickedFunction: function() {
|
clickedFunction: function() {
|
||||||
PageController.goToPage(PageEnum.PageSettingsApplication)
|
PageController.goToPage(PageEnum.PageSettingsApplication)
|
||||||
}
|
}
|
||||||
|
|
||||||
KeyNavigation.tab: backup.rightButton
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DividerType {}
|
DividerType {}
|
||||||
|
|
||||||
LabelWithButtonType {
|
LabelWithButtonType {
|
||||||
id: backup
|
id: backup
|
||||||
|
visible: !SettingsController.isOnTv()
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
text: qsTr("Backup")
|
text: qsTr("Backup")
|
||||||
|
@ -106,11 +97,11 @@ PageType {
|
||||||
clickedFunction: function() {
|
clickedFunction: function() {
|
||||||
PageController.goToPage(PageEnum.PageSettingsBackup)
|
PageController.goToPage(PageEnum.PageSettingsBackup)
|
||||||
}
|
}
|
||||||
|
|
||||||
KeyNavigation.tab: about.rightButton
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DividerType {}
|
DividerType {
|
||||||
|
visible: !SettingsController.isOnTv()
|
||||||
|
}
|
||||||
|
|
||||||
LabelWithButtonType {
|
LabelWithButtonType {
|
||||||
id: about
|
id: about
|
||||||
|
@ -123,8 +114,6 @@ PageType {
|
||||||
clickedFunction: function() {
|
clickedFunction: function() {
|
||||||
PageController.goToPage(PageEnum.PageSettingsAbout)
|
PageController.goToPage(PageEnum.PageSettingsAbout)
|
||||||
}
|
}
|
||||||
KeyNavigation.tab: close
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DividerType {}
|
DividerType {}
|
||||||
|
@ -138,8 +127,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)
|
||||||
}
|
}
|
||||||
|
@ -159,8 +146,6 @@ PageType {
|
||||||
leftImageSource: "qrc:/images/controls/x-circle.svg"
|
leftImageSource: "qrc:/images/controls/x-circle.svg"
|
||||||
isLeftImageHoverEnabled: false
|
isLeftImageHoverEnabled: false
|
||||||
|
|
||||||
Keys.onTabPressed: lastItemTabClicked(header)
|
|
||||||
|
|
||||||
clickedFunction: function() {
|
clickedFunction: function() {
|
||||||
PageController.closeApplication()
|
PageController.closeApplication()
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,107 @@ 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
|
||||||
|
|
||||||
|
readonly property string title: qsTr("Telegram group")
|
||||||
|
readonly property string description: qsTr("To discuss features")
|
||||||
|
readonly property string imageSource: "qrc:/images/controls/telegram.svg"
|
||||||
|
readonly property var handler: function() {
|
||||||
|
Qt.openUrlExternally(qsTr("https://t.me/amnezia_vpn_en"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QtObject {
|
||||||
|
id: mail
|
||||||
|
|
||||||
|
readonly property string title: qsTr("support@amnezia.org")
|
||||||
|
readonly property string description: qsTr("For reviews and bug reports")
|
||||||
|
readonly property string imageSource: "qrc:/images/controls/mail.svg"
|
||||||
|
readonly property var handler: function() {
|
||||||
|
GC.copyToClipBoard(title)
|
||||||
|
PageController.showNotificationMessage(qsTr("Copied"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QtObject {
|
||||||
|
id: github
|
||||||
|
|
||||||
|
readonly property string title: qsTr("GitHub")
|
||||||
|
readonly property string description: qsTr("Discover the source code")
|
||||||
|
readonly property string imageSource: "qrc:/images/controls/github.svg"
|
||||||
|
readonly property var handler: function() {
|
||||||
|
Qt.openUrlExternally(qsTr("https://github.com/amnezia-vpn/amnezia-client"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QtObject {
|
||||||
|
id: website
|
||||||
|
|
||||||
|
readonly property string title: qsTr("Website")
|
||||||
|
readonly property string description: qsTr("Visit official website")
|
||||||
|
readonly property string imageSource: "qrc:/images/controls/amnezia.svg"
|
||||||
|
readonly property var handler: function() {
|
||||||
|
Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
property list<QtObject> contacts: [
|
||||||
|
telegramGroup,
|
||||||
|
mail,
|
||||||
|
github,
|
||||||
|
website
|
||||||
|
]
|
||||||
|
|
||||||
|
ListView {
|
||||||
|
id: listView
|
||||||
|
|
||||||
anchors.top: backButton.bottom
|
anchors.top: backButton.bottom
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
contentHeight: content.height
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
id: content
|
|
||||||
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
|
anchors.left: parent.left
|
||||||
|
|
||||||
|
property bool isFocusable: true
|
||||||
|
|
||||||
|
Keys.onTabPressed: {
|
||||||
|
FocusController.nextKeyTabItem()
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.onBacktabPressed: {
|
||||||
|
FocusController.previousKeyTabItem()
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.onUpPressed: {
|
||||||
|
FocusController.nextKeyUpItem()
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.onDownPressed: {
|
||||||
|
FocusController.nextKeyDownItem()
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.onLeftPressed: {
|
||||||
|
FocusController.nextKeyLeftItem()
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.onRightPressed: {
|
||||||
|
FocusController.nextKeyRightItem()
|
||||||
|
}
|
||||||
|
|
||||||
|
ScrollBar.vertical: ScrollBarType {}
|
||||||
|
|
||||||
|
model: contacts
|
||||||
|
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
header: ColumnLayout {
|
||||||
|
width: listView.width
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
id: image
|
id: image
|
||||||
|
@ -96,81 +169,29 @@ PageType {
|
||||||
|
|
||||||
text: qsTr("Contacts")
|
text: qsTr("Contacts")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delegate: ColumnLayout {
|
||||||
|
width: listView.width
|
||||||
|
|
||||||
LabelWithButtonType {
|
LabelWithButtonType {
|
||||||
id: telegramButton
|
id: telegramButton
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: 16
|
Layout.topMargin: 6
|
||||||
|
|
||||||
text: qsTr("Telegram group")
|
text: title
|
||||||
descriptionText: qsTr("To discuss features")
|
descriptionText: description
|
||||||
leftImageSource: "qrc:/images/controls/telegram.svg"
|
leftImageSource: imageSource
|
||||||
|
|
||||||
KeyNavigation.tab: mailButton
|
clickedFunction: handler
|
||||||
parentFlickable: fl
|
|
||||||
|
|
||||||
clickedFunction: function() {
|
|
||||||
Qt.openUrlExternally(qsTr("https://t.me/amnezia_vpn_en"))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DividerType {}
|
DividerType {}
|
||||||
|
|
||||||
LabelWithButtonType {
|
|
||||||
id: mailButton
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
text: qsTr("support@amnezia.org")
|
|
||||||
descriptionText: qsTr("For reviews and bug reports")
|
|
||||||
leftImageSource: "qrc:/images/controls/mail.svg"
|
|
||||||
|
|
||||||
KeyNavigation.tab: githubButton
|
|
||||||
parentFlickable: fl
|
|
||||||
|
|
||||||
clickedFunction: function() {
|
|
||||||
GC.copyToClipBoard(text)
|
|
||||||
PageController.showNotificationMessage(qsTr("Copied"))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
footer: ColumnLayout {
|
||||||
|
width: listView.width
|
||||||
DividerType {}
|
|
||||||
|
|
||||||
LabelWithButtonType {
|
|
||||||
id: githubButton
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
text: qsTr("GitHub")
|
|
||||||
leftImageSource: "qrc:/images/controls/github.svg"
|
|
||||||
|
|
||||||
KeyNavigation.tab: websiteButton
|
|
||||||
parentFlickable: fl
|
|
||||||
|
|
||||||
clickedFunction: function() {
|
|
||||||
Qt.openUrlExternally(qsTr("https://github.com/amnezia-vpn/amnezia-client"))
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
DividerType {}
|
|
||||||
|
|
||||||
LabelWithButtonType {
|
|
||||||
id: websiteButton
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
text: qsTr("Website")
|
|
||||||
leftImageSource: "qrc:/images/controls/amnezia.svg"
|
|
||||||
|
|
||||||
KeyNavigation.tab: checkUpdatesButton
|
|
||||||
parentFlickable: fl
|
|
||||||
|
|
||||||
clickedFunction: function() {
|
|
||||||
Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl())
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
DividerType {}
|
|
||||||
|
|
||||||
CaptionTextType {
|
CaptionTextType {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
@ -196,6 +217,7 @@ PageType {
|
||||||
|
|
||||||
BasicButtonType {
|
BasicButtonType {
|
||||||
id: checkUpdatesButton
|
id: checkUpdatesButton
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
Layout.topMargin: 8
|
Layout.topMargin: 8
|
||||||
Layout.bottomMargin: 16
|
Layout.bottomMargin: 16
|
||||||
|
@ -209,9 +231,6 @@ PageType {
|
||||||
|
|
||||||
text: qsTr("Check for updates")
|
text: qsTr("Check for updates")
|
||||||
|
|
||||||
KeyNavigation.tab: privacyPolicyButton
|
|
||||||
parentFlickable: fl
|
|
||||||
|
|
||||||
clickedFunc: function() {
|
clickedFunc: function() {
|
||||||
Qt.openUrlExternally("https://github.com/amnezia-vpn/desktop-client/releases/latest")
|
Qt.openUrlExternally("https://github.com/amnezia-vpn/desktop-client/releases/latest")
|
||||||
}
|
}
|
||||||
|
@ -219,6 +238,7 @@ PageType {
|
||||||
|
|
||||||
BasicButtonType {
|
BasicButtonType {
|
||||||
id: privacyPolicyButton
|
id: privacyPolicyButton
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
Layout.bottomMargin: 16
|
Layout.bottomMargin: 16
|
||||||
Layout.topMargin: -15
|
Layout.topMargin: -15
|
||||||
|
@ -232,9 +252,6 @@ PageType {
|
||||||
|
|
||||||
text: qsTr("Privacy Policy")
|
text: qsTr("Privacy Policy")
|
||||||
|
|
||||||
Keys.onTabPressed: lastItemTabClicked()
|
|
||||||
parentFlickable: fl
|
|
||||||
|
|
||||||
clickedFunc: function() {
|
clickedFunc: function() {
|
||||||
Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl() + "/policy")
|
Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl() + "/policy")
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,14 +31,11 @@ PageType {
|
||||||
id: containersRadioButtonGroup
|
id: containersRadioButtonGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
delegate: Item {
|
delegate: ColumnLayout {
|
||||||
implicitWidth: parent.width
|
|
||||||
implicitHeight: content.implicitHeight
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
id: content
|
id: content
|
||||||
|
|
||||||
anchors.fill: parent
|
implicitWidth: parent.width
|
||||||
|
implicitHeight: content.implicitHeight
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
VerticalRadioButton {
|
VerticalRadioButton {
|
||||||
|
@ -106,4 +103,3 @@ PageType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
|
@ -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
|
||||||
|
@ -111,9 +104,6 @@ PageType {
|
||||||
|
|
||||||
descriptionOnTop: true
|
descriptionOnTop: true
|
||||||
|
|
||||||
// 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
|
||||||
|
|
||||||
|
@ -141,8 +131,6 @@ PageType {
|
||||||
|
|
||||||
text: qsTr("Reload API config")
|
text: qsTr("Reload API config")
|
||||||
|
|
||||||
// Keys.onTabPressed: lastItemTabClicked(focusItem)
|
|
||||||
|
|
||||||
clickedFunc: function() {
|
clickedFunc: function() {
|
||||||
var headerText = qsTr("Reload API config?")
|
var headerText = qsTr("Reload API config?")
|
||||||
var yesButtonText = qsTr("Continue")
|
var yesButtonText = qsTr("Continue")
|
||||||
|
@ -181,8 +169,6 @@ PageType {
|
||||||
|
|
||||||
text: qsTr("Remove from application")
|
text: qsTr("Remove from application")
|
||||||
|
|
||||||
// Keys.onTabPressed: lastItemTabClicked(focusItem)
|
|
||||||
|
|
||||||
clickedFunc: function() {
|
clickedFunc: function() {
|
||||||
var headerText = qsTr("Remove from application?")
|
var headerText = qsTr("Remove from application?")
|
||||||
var yesButtonText = qsTr("Continue")
|
var yesButtonText = qsTr("Continue")
|
||||||
|
|
|
@ -21,8 +21,6 @@ import "../Components"
|
||||||
PageType {
|
PageType {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
defaultActiveFocusItem: focusItem
|
|
||||||
|
|
||||||
property bool pageEnabled
|
property bool pageEnabled
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
|
@ -48,13 +46,15 @@ PageType {
|
||||||
|
|
||||||
QtObject {
|
QtObject {
|
||||||
id: onlyForwardApps
|
id: onlyForwardApps
|
||||||
property string name: qsTr("Only the apps from the list should have access via VPN")
|
|
||||||
property int type: routeMode.onlyForwardApps
|
readonly property string name: qsTr("Only the apps from the list should have access via VPN")
|
||||||
|
readonly property int type: routeMode.onlyForwardApps
|
||||||
}
|
}
|
||||||
QtObject {
|
QtObject {
|
||||||
id: allExceptApps
|
id: allExceptApps
|
||||||
property string name: qsTr("Apps from the list should not have access via VPN")
|
|
||||||
property int type: routeMode.allExceptApps
|
readonly property string name: qsTr("Apps from the list should not have access via VPN")
|
||||||
|
readonly property int type: routeMode.allExceptApps
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRouteModesModelIndex() {
|
function getRouteModesModelIndex() {
|
||||||
|
@ -66,11 +66,6 @@ PageType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
|
||||||
id: focusItem
|
|
||||||
KeyNavigation.tab: backButton
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: header
|
id: header
|
||||||
|
|
||||||
|
@ -82,7 +77,6 @@ PageType {
|
||||||
|
|
||||||
BackButtonType {
|
BackButtonType {
|
||||||
id: backButton
|
id: backButton
|
||||||
KeyNavigation.tab: switcher
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
|
@ -103,10 +97,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,25 +120,23 @@ 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
|
||||||
|
|
||||||
model: root.routeModesModel
|
model: root.routeModesModel
|
||||||
|
|
||||||
currentIndex: getRouteModesModelIndex()
|
selectedIndex: getRouteModesModelIndex()
|
||||||
|
|
||||||
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[selectedIndex].type) {
|
||||||
AppSplitTunnelingModel.routeMode = root.routeModesModel[currentIndex].type
|
AppSplitTunnelingModel.routeMode = root.routeModesModel[selectedIndex].type
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
if (root.routeModesModel[currentIndex].type === AppSplitTunnelingModel.routeMode) {
|
if (root.routeModesModel[selectedIndex].type === AppSplitTunnelingModel.routeMode) {
|
||||||
selector.text = selectedText
|
selector.text = selectedText
|
||||||
} else {
|
} else {
|
||||||
selector.text = root.routeModesModel[0].name
|
selector.text = root.routeModesModel[0].name
|
||||||
|
@ -158,7 +146,7 @@ PageType {
|
||||||
Connections {
|
Connections {
|
||||||
target: AppSplitTunnelingModel
|
target: AppSplitTunnelingModel
|
||||||
function onRouteModeChanged() {
|
function onRouteModeChanged() {
|
||||||
currentIndex = getRouteModesModelIndex()
|
selectedIndex = getRouteModesModelIndex()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -267,7 +255,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 +268,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)
|
||||||
|
|
|
@ -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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,23 +23,22 @@ 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 {
|
ListView {
|
||||||
id: fl
|
id: listView
|
||||||
|
|
||||||
anchors.top: backButton.bottom
|
anchors.top: backButton.bottom
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
contentHeight: content.height
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
id: content
|
|
||||||
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
spacing: 0
|
anchors.left: parent.left
|
||||||
|
|
||||||
|
property bool isFocusable: true
|
||||||
|
|
||||||
|
ScrollBar.vertical: ScrollBarType {}
|
||||||
|
|
||||||
|
header: ColumnLayout {
|
||||||
|
width: listView.width
|
||||||
|
|
||||||
HeaderType {
|
HeaderType {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
@ -60,6 +52,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 +61,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 +72,6 @@ PageType {
|
||||||
DividerType {}
|
DividerType {}
|
||||||
|
|
||||||
LabelWithButtonType {
|
LabelWithButtonType {
|
||||||
// id: labelWithButton2
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: -8
|
Layout.topMargin: -8
|
||||||
|
|
||||||
|
@ -87,8 +79,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 +89,28 @@ 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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
model: logTypes
|
||||||
|
clip: true
|
||||||
|
reuseItems: true
|
||||||
|
snapMode: ListView.SnapOneItem
|
||||||
|
|
||||||
|
delegate: ColumnLayout {
|
||||||
|
id: delegateContent
|
||||||
|
|
||||||
|
width: listView.width
|
||||||
|
|
||||||
|
enabled: isVisible
|
||||||
|
|
||||||
ListItemTitleType {
|
ListItemTitleType {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
@ -119,7 +118,7 @@ PageType {
|
||||||
Layout.leftMargin: 16
|
Layout.leftMargin: 16
|
||||||
Layout.rightMargin: 16
|
Layout.rightMargin: 16
|
||||||
|
|
||||||
text: qsTr("Client logs")
|
text: title
|
||||||
}
|
}
|
||||||
|
|
||||||
ParagraphTextType {
|
ParagraphTextType {
|
||||||
|
@ -129,11 +128,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 +141,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,9 +155,28 @@ PageType {
|
||||||
leftImageSource: "qrc:/images/controls/save.svg"
|
leftImageSource: "qrc:/images/controls/save.svg"
|
||||||
isSmallLeftImage: true
|
isSmallLeftImage: true
|
||||||
|
|
||||||
// KeyNavigation.tab: labelWithButton3
|
clickedFunction: exportLogsHandler
|
||||||
|
}
|
||||||
|
|
||||||
clickedFunction: function() {
|
DividerType {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
property list<QtObject> logTypes: [
|
||||||
|
clientLogs,
|
||||||
|
serviceLogs
|
||||||
|
]
|
||||||
|
|
||||||
|
QtObject {
|
||||||
|
id: clientLogs
|
||||||
|
|
||||||
|
readonly property string title: qsTr("Client logs")
|
||||||
|
readonly property string description: qsTr("AmneziaVPN logs")
|
||||||
|
readonly property bool isVisible: true
|
||||||
|
readonly property var openLogsHandler: function() {
|
||||||
|
SettingsController.openLogsFolder()
|
||||||
|
}
|
||||||
|
readonly property var exportLogsHandler: function() {
|
||||||
var fileName = ""
|
var fileName = ""
|
||||||
if (GC.isMobile()) {
|
if (GC.isMobile()) {
|
||||||
fileName = "AmneziaVPN.log"
|
fileName = "AmneziaVPN.log"
|
||||||
|
@ -183,71 +196,16 @@ PageType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DividerType {}
|
QtObject {
|
||||||
|
id: serviceLogs
|
||||||
|
|
||||||
ListItemTitleType {
|
readonly property string title: qsTr("Service logs")
|
||||||
visible: !GC.isMobile()
|
readonly property string description: qsTr("AmneziaVPN-service logs")
|
||||||
|
readonly property bool isVisible: !GC.isMobile()
|
||||||
Layout.fillWidth: true
|
readonly property var openLogsHandler: function() {
|
||||||
Layout.topMargin: 32
|
|
||||||
Layout.leftMargin: 16
|
|
||||||
Layout.rightMargin: 16
|
|
||||||
|
|
||||||
text: qsTr("Service logs")
|
|
||||||
}
|
|
||||||
|
|
||||||
ParagraphTextType {
|
|
||||||
visible: !GC.isMobile()
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.topMargin: 8
|
|
||||||
Layout.leftMargin: 16
|
|
||||||
Layout.rightMargin: 16
|
|
||||||
|
|
||||||
color: AmneziaStyle.color.mutedGray
|
|
||||||
text: qsTr("AmneziaVPN-service logs")
|
|
||||||
}
|
|
||||||
|
|
||||||
LabelWithButtonType {
|
|
||||||
// id: labelWithButton2
|
|
||||||
|
|
||||||
visible: !GC.isMobile()
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.topMargin: -8
|
|
||||||
Layout.bottomMargin: -8
|
|
||||||
|
|
||||||
text: qsTr("Open logs folder")
|
|
||||||
leftImageSource: "qrc:/images/controls/folder-open.svg"
|
|
||||||
isSmallLeftImage: true
|
|
||||||
|
|
||||||
// KeyNavigation.tab: labelWithButton3
|
|
||||||
|
|
||||||
clickedFunction: function() {
|
|
||||||
SettingsController.openServiceLogsFolder()
|
SettingsController.openServiceLogsFolder()
|
||||||
}
|
}
|
||||||
}
|
readonly property var exportLogsHandler: function() {
|
||||||
|
|
||||||
DividerType {
|
|
||||||
visible: !GC.isMobile()
|
|
||||||
}
|
|
||||||
|
|
||||||
LabelWithButtonType {
|
|
||||||
// id: labelWithButton2
|
|
||||||
|
|
||||||
visible: !GC.isMobile()
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.topMargin: -8
|
|
||||||
Layout.bottomMargin: -8
|
|
||||||
|
|
||||||
text: qsTr("Export logs")
|
|
||||||
leftImageSource: "qrc:/images/controls/save.svg"
|
|
||||||
isSmallLeftImage: true
|
|
||||||
|
|
||||||
// KeyNavigation.tab: labelWithButton3
|
|
||||||
|
|
||||||
clickedFunction: function() {
|
|
||||||
var fileName = ""
|
var fileName = ""
|
||||||
if (GC.isMobile()) {
|
if (GC.isMobile()) {
|
||||||
fileName = "AmneziaVPN-service.log"
|
fileName = "AmneziaVPN-service.log"
|
||||||
|
@ -266,10 +224,4 @@ PageType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DividerType {
|
|
||||||
visible: !GC.isMobile()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 = ""
|
||||||
|
|
|
@ -19,21 +19,19 @@ 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
|
||||||
|
|
||||||
property var processedServer
|
property var processedServer
|
||||||
|
|
||||||
defaultActiveFocusItem: focusItem
|
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: PageController
|
target: PageController
|
||||||
|
|
||||||
function onGoToPageSettingsServerServices() {
|
function onGoToPageSettingsServerServices() {
|
||||||
tabBar.currentIndex = root.pageSettingsServerServices
|
tabBar.setCurrentIndex(root.pageSettingsServerServices)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,21 +60,17 @@ PageType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
|
||||||
id: focusItem
|
|
||||||
//KeyNavigation.tab: header
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
|
objectName: "mainLayout"
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
anchors.topMargin: 20
|
||||||
|
|
||||||
spacing: 4
|
spacing: 4
|
||||||
|
|
||||||
BackButtonType {
|
BackButtonType {
|
||||||
id: backButton
|
id: backButton
|
||||||
|
objectName: "backButton"
|
||||||
Layout.topMargin: 20
|
|
||||||
KeyNavigation.tab: headerContent.actionButton
|
|
||||||
|
|
||||||
backButtonFunction: function() {
|
backButtonFunction: function() {
|
||||||
if (nestedStackView.currentIndex === root.pageSettingsApiServerInfo &&
|
if (nestedStackView.currentIndex === root.pageSettingsApiServerInfo &&
|
||||||
|
@ -90,9 +84,12 @@ PageType {
|
||||||
|
|
||||||
HeaderType {
|
HeaderType {
|
||||||
id: headerContent
|
id: headerContent
|
||||||
|
objectName: "headerContent"
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.leftMargin: 16
|
Layout.leftMargin: 16
|
||||||
Layout.rightMargin: 16
|
Layout.rightMargin: 16
|
||||||
|
Layout.bottomMargin: 10
|
||||||
|
|
||||||
actionButtonImage: nestedStackView.currentIndex === root.pageSettingsApiLanguageList ? "qrc:/images/controls/settings.svg"
|
actionButtonImage: nestedStackView.currentIndex === root.pageSettingsApiLanguageList ? "qrc:/images/controls/settings.svg"
|
||||||
: "qrc:/images/controls/edit-3.svg"
|
: "qrc:/images/controls/edit-3.svg"
|
||||||
|
@ -114,32 +111,25 @@ PageType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
KeyNavigation.tab: tabBar
|
|
||||||
|
|
||||||
actionButtonFunction: function() {
|
actionButtonFunction: function() {
|
||||||
if (nestedStackView.currentIndex === root.pageSettingsApiLanguageList) {
|
if (nestedStackView.currentIndex === root.pageSettingsApiLanguageList) {
|
||||||
nestedStackView.currentIndex = root.pageSettingsApiServerInfo
|
nestedStackView.currentIndex = root.pageSettingsApiServerInfo
|
||||||
} else {
|
} else {
|
||||||
serverNameEditDrawer.open()
|
serverNameEditDrawer.openTriggered()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DrawerType2 {
|
DrawerType2 {
|
||||||
id: serverNameEditDrawer
|
id: serverNameEditDrawer
|
||||||
|
objectName: "serverNameEditDrawer"
|
||||||
|
|
||||||
parent: root
|
parent: root
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
expandedHeight: root.height * 0.35
|
expandedHeight: root.height * 0.35
|
||||||
|
|
||||||
onClosed: {
|
expandedStateContent: ColumnLayout {
|
||||||
if (!GC.isMobile()) {
|
|
||||||
headerContent.actionButton.forceActiveFocus()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
expandedContent: ColumnLayout {
|
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
|
@ -147,19 +137,6 @@ PageType {
|
||||||
anchors.leftMargin: 16
|
anchors.leftMargin: 16
|
||||||
anchors.rightMargin: 16
|
anchors.rightMargin: 16
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: serverNameEditDrawer
|
|
||||||
enabled: !GC.isMobile()
|
|
||||||
function onOpened() {
|
|
||||||
serverName.textField.forceActiveFocus()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: focusItem1
|
|
||||||
KeyNavigation.tab: serverName.textField
|
|
||||||
}
|
|
||||||
|
|
||||||
TextFieldWithHeaderType {
|
TextFieldWithHeaderType {
|
||||||
id: serverName
|
id: serverName
|
||||||
|
|
||||||
|
@ -168,8 +145,6 @@ PageType {
|
||||||
textFieldText: root.processedServer.name
|
textFieldText: root.processedServer.name
|
||||||
textField.maximumLength: 30
|
textField.maximumLength: 30
|
||||||
checkEmptyText: true
|
checkEmptyText: true
|
||||||
|
|
||||||
KeyNavigation.tab: saveButton
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BasicButtonType {
|
BasicButtonType {
|
||||||
|
@ -178,7 +153,6 @@ PageType {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
text: qsTr("Save")
|
text: qsTr("Save")
|
||||||
KeyNavigation.tab: focusItem1
|
|
||||||
|
|
||||||
clickedFunc: function() {
|
clickedFunc: function() {
|
||||||
if (serverName.textFieldText === "") {
|
if (serverName.textFieldText === "") {
|
||||||
|
@ -188,7 +162,7 @@ PageType {
|
||||||
if (serverName.textFieldText !== root.processedServer.name) {
|
if (serverName.textFieldText !== root.processedServer.name) {
|
||||||
ServersModel.setProcessedServerData("name", serverName.textFieldText);
|
ServersModel.setProcessedServerData("name", serverName.textFieldText);
|
||||||
}
|
}
|
||||||
serverNameEditDrawer.close()
|
serverNameEditDrawer.closeTriggered()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -209,35 +183,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 {
|
||||||
|
@ -245,22 +211,14 @@ 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.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
currentIndex: ServersModel.getProcessedServerData("isServerFromGatewayApi") ?
|
currentIndex: ServersModel.getProcessedServerData("isServerFromGatewayApi") ?
|
||||||
|
@ -270,38 +228,27 @@ 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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,13 +21,6 @@ PageType {
|
||||||
|
|
||||||
property bool isClearCacheVisible: ServersModel.isProcessedServerHasWriteAccess() && !ContainersModel.isServiceContainer(ContainersModel.getProcessedContainerIndex())
|
property bool isClearCacheVisible: ServersModel.isProcessedServerHasWriteAccess() && !ContainersModel.isServiceContainer(ContainersModel.getProcessedContainerIndex())
|
||||||
|
|
||||||
defaultActiveFocusItem: focusItem
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: focusItem
|
|
||||||
KeyNavigation.tab: backButton
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: header
|
id: header
|
||||||
|
|
||||||
|
@ -39,7 +32,6 @@ PageType {
|
||||||
|
|
||||||
BackButtonType {
|
BackButtonType {
|
||||||
id: backButton
|
id: backButton
|
||||||
KeyNavigation.tab: protocols
|
|
||||||
}
|
}
|
||||||
|
|
||||||
HeaderType {
|
HeaderType {
|
||||||
|
@ -57,30 +49,36 @@ PageType {
|
||||||
height: protocols.contentItem.height
|
height: protocols.contentItem.height
|
||||||
clip: true
|
clip: true
|
||||||
interactive: true
|
interactive: true
|
||||||
model: ProtocolsModel
|
|
||||||
|
|
||||||
property int currentFocusIndex: 0
|
property bool isFocusable: true
|
||||||
|
|
||||||
activeFocusOnTab: true
|
|
||||||
onActiveFocusChanged: {
|
|
||||||
if (activeFocus) {
|
|
||||||
this.currentFocusIndex = 0
|
|
||||||
protocols.itemAtIndex(currentFocusIndex).focusItem.forceActiveFocus()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onTabPressed: {
|
Keys.onTabPressed: {
|
||||||
if (currentFocusIndex < this.count - 1) {
|
FocusController.nextKeyTabItem()
|
||||||
currentFocusIndex += 1
|
|
||||||
protocols.itemAtIndex(currentFocusIndex).focusItem.forceActiveFocus()
|
|
||||||
} else {
|
|
||||||
clearCacheButton.forceActiveFocus()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Keys.onBacktabPressed: {
|
||||||
|
FocusController.previousKeyTabItem()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Keys.onUpPressed: {
|
||||||
|
FocusController.nextKeyUpItem()
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.onDownPressed: {
|
||||||
|
FocusController.nextKeyDownItem()
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.onLeftPressed: {
|
||||||
|
FocusController.nextKeyLeftItem()
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.onRightPressed: {
|
||||||
|
FocusController.nextKeyRightItem()
|
||||||
|
}
|
||||||
|
|
||||||
|
model: ProtocolsModel
|
||||||
|
|
||||||
delegate: Item {
|
delegate: Item {
|
||||||
property var focusItem: clientSettings.rightButton
|
|
||||||
|
|
||||||
implicitWidth: protocols.width
|
implicitWidth: protocols.width
|
||||||
implicitHeight: delegateContent.implicitHeight
|
implicitHeight: delegateContent.implicitHeight
|
||||||
|
|
||||||
|
@ -160,7 +158,9 @@ PageType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
footer: ColumnLayout {
|
||||||
|
width: header.width
|
||||||
|
|
||||||
LabelWithButtonType {
|
LabelWithButtonType {
|
||||||
id: clearCacheButton
|
id: clearCacheButton
|
||||||
|
@ -168,7 +168,6 @@ PageType {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
visible: root.isClearCacheVisible
|
visible: root.isClearCacheVisible
|
||||||
KeyNavigation.tab: removeButton
|
|
||||||
|
|
||||||
text: qsTr("Clear profile")
|
text: qsTr("Clear profile")
|
||||||
|
|
||||||
|
@ -190,9 +189,9 @@ PageType {
|
||||||
PageController.showBusyIndicator(false)
|
PageController.showBusyIndicator(false)
|
||||||
}
|
}
|
||||||
var noButtonFunction = function() {
|
var noButtonFunction = function() {
|
||||||
if (!GC.isMobile()) {
|
// if (!GC.isMobile()) {
|
||||||
focusItem.forceActiveFocus()
|
// focusItem.forceActiveFocus()
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
showQuestionDrawer(headerText, descriptionText, yesButtonText, noButtonText, yesButtonFunction, noButtonFunction)
|
||||||
|
@ -219,7 +218,6 @@ PageType {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
visible: ServersModel.isProcessedServerHasWriteAccess()
|
visible: ServersModel.isProcessedServerHasWriteAccess()
|
||||||
Keys.onTabPressed: lastItemTabClicked(focusItem)
|
|
||||||
|
|
||||||
text: qsTr("Remove ")
|
text: qsTr("Remove ")
|
||||||
textColor: AmneziaStyle.color.vibrantRed
|
textColor: AmneziaStyle.color.vibrantRed
|
||||||
|
@ -266,3 +264,6 @@ PageType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,25 +21,15 @@ PageType {
|
||||||
|
|
||||||
property var installedProtocolsCount
|
property var installedProtocolsCount
|
||||||
|
|
||||||
onFocusChanged: settingsContainersListView.forceActiveFocus()
|
function resetView() {
|
||||||
signal lastItemTabClickedSignal()
|
settingsContainersListView.positionViewAtBeginning()
|
||||||
|
}
|
||||||
FlickableType {
|
|
||||||
id: fl
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
contentHeight: content.implicitHeight
|
|
||||||
|
|
||||||
Column {
|
|
||||||
id: content
|
|
||||||
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
|
|
||||||
SettingsContainersListView {
|
SettingsContainersListView {
|
||||||
id: settingsContainersListView
|
id: settingsContainersListView
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: ServersModel
|
target: ServersModel
|
||||||
|
|
||||||
|
@ -66,8 +56,10 @@ PageType {
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
Component.onCompleted: updateContainersModelFilters()
|
Component.onCompleted: {
|
||||||
}
|
settingsContainersListView.isFocusable = true
|
||||||
|
settingsContainersListView.interactive = true
|
||||||
|
updateContainersModelFilters()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,25 +21,11 @@ PageType {
|
||||||
|
|
||||||
property var installedServicesCount
|
property var installedServicesCount
|
||||||
|
|
||||||
onFocusChanged: settingsContainersListView.forceActiveFocus()
|
|
||||||
signal lastItemTabClickedSignal()
|
|
||||||
|
|
||||||
FlickableType {
|
|
||||||
id: fl
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
contentHeight: content.implicitHeight
|
|
||||||
|
|
||||||
Column {
|
|
||||||
id: content
|
|
||||||
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
|
|
||||||
SettingsContainersListView {
|
SettingsContainersListView {
|
||||||
id: settingsContainersListView
|
id: settingsContainersListView
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: ServersModel
|
target: ServersModel
|
||||||
|
|
||||||
|
@ -65,8 +51,10 @@ PageType {
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
Component.onCompleted: updateContainersModelFilters()
|
Component.onCompleted: {
|
||||||
}
|
settingsContainersListView.isFocusable = true
|
||||||
|
settingsContainersListView.interactive = true
|
||||||
|
updateContainersModelFilters()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,58 +40,29 @@ PageType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FlickableType {
|
ListView {
|
||||||
id: fl
|
id: servers
|
||||||
|
objectName: "servers"
|
||||||
|
|
||||||
|
width: parent.width
|
||||||
anchors.top: header.bottom
|
anchors.top: header.bottom
|
||||||
anchors.topMargin: 16
|
anchors.topMargin: 16
|
||||||
contentHeight: col.implicitHeight
|
|
||||||
|
|
||||||
Column {
|
|
||||||
id: col
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
|
|
||||||
ListView {
|
height: 500
|
||||||
id: servers
|
|
||||||
width: parent.width
|
property bool isFocusable: true
|
||||||
height: servers.contentItem.height
|
|
||||||
|
|
||||||
model: ServersModel
|
model: ServersModel
|
||||||
|
|
||||||
clip: true
|
clip: true
|
||||||
interactive: false
|
reuseItems: true
|
||||||
|
|
||||||
activeFocusOnTab: true
|
|
||||||
focus: true
|
|
||||||
Keys.onTabPressed: {
|
|
||||||
if (currentIndex < servers.count - 1) {
|
|
||||||
servers.incrementCurrentIndex()
|
|
||||||
} else {
|
|
||||||
servers.currentIndex = 0
|
|
||||||
focusItem.forceActiveFocus()
|
|
||||||
root.lastItemTabClicked()
|
|
||||||
}
|
|
||||||
|
|
||||||
fl.ensureVisible(this.currentItem)
|
|
||||||
}
|
|
||||||
|
|
||||||
onVisibleChanged: {
|
|
||||||
if (visible) {
|
|
||||||
currentIndex = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
delegate: Item {
|
delegate: Item {
|
||||||
implicitWidth: servers.width
|
implicitWidth: servers.width
|
||||||
implicitHeight: delegateContent.implicitHeight
|
implicitHeight: delegateContent.implicitHeight
|
||||||
|
|
||||||
onFocusChanged: {
|
|
||||||
if (focus) {
|
|
||||||
server.rightButton.forceActiveFocus()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: delegateContent
|
id: delegateContent
|
||||||
|
|
||||||
|
@ -112,7 +75,7 @@ PageType {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
text: name
|
text: name
|
||||||
parentFlickable: fl
|
|
||||||
descriptionText: {
|
descriptionText: {
|
||||||
var servicesNameString = ""
|
var servicesNameString = ""
|
||||||
var servicesName = ServersModel.getAllInstalledServicesName(index)
|
var servicesName = ServersModel.getAllInstalledServicesName(index)
|
||||||
|
@ -139,5 +102,3 @@ PageType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,18 +144,17 @@ PageType {
|
||||||
|
|
||||||
model: root.routeModesModel
|
model: root.routeModesModel
|
||||||
|
|
||||||
currentIndex: getRouteModesModelIndex()
|
|
||||||
|
|
||||||
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[selectedIndex].type) {
|
||||||
SitesModel.routeMode = root.routeModesModel[currentIndex].type
|
SitesModel.routeMode = root.routeModesModel[selectedIndex].type
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
if (root.routeModesModel[currentIndex].type === SitesModel.routeMode) {
|
if (root.routeModesModel[selectedIndex].type === SitesModel.routeMode) {
|
||||||
selector.text = selectedText
|
selector.text = selectedText
|
||||||
} else {
|
} else {
|
||||||
selector.text = root.routeModesModel[0].name
|
selector.text = root.routeModesModel[0].name
|
||||||
|
@ -175,37 +164,25 @@ PageType {
|
||||||
Connections {
|
Connections {
|
||||||
target: SitesModel
|
target: SitesModel
|
||||||
function onRouteModeChanged() {
|
function onRouteModeChanged() {
|
||||||
currentIndex = getRouteModesModelIndex()
|
selectedIndex = getRouteModesModelIndex()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
KeyNavigation.tab: {
|
ListView {
|
||||||
return sites.count > 0 ?
|
id: listView
|
||||||
sites :
|
|
||||||
searchField.textField
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
FlickableType {
|
|
||||||
id: fl
|
|
||||||
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
|
anchors.bottom: addSiteButton.top
|
||||||
|
|
||||||
|
width: parent.width
|
||||||
|
|
||||||
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 {
|
|
||||||
id: sites
|
|
||||||
width: parent.width
|
|
||||||
height: sites.contentItem.height
|
|
||||||
|
|
||||||
model: SortFilterProxyModel {
|
model: SortFilterProxyModel {
|
||||||
id: proxySitesModel
|
id: proxySitesModel
|
||||||
|
@ -227,37 +204,13 @@ PageType {
|
||||||
}
|
}
|
||||||
|
|
||||||
clip: true
|
clip: true
|
||||||
interactive: false
|
|
||||||
|
|
||||||
activeFocusOnTab: true
|
reuseItems: true
|
||||||
focus: true
|
|
||||||
Keys.onTabPressed: {
|
|
||||||
if (currentIndex < this.count - 1) {
|
|
||||||
this.incrementCurrentIndex()
|
|
||||||
} else {
|
|
||||||
currentIndex = 0
|
|
||||||
searchField.textField.forceActiveFocus()
|
|
||||||
}
|
|
||||||
|
|
||||||
fl.ensureVisible(currentItem)
|
delegate: ColumnLayout {
|
||||||
}
|
|
||||||
|
|
||||||
delegate: Item {
|
|
||||||
implicitWidth: sites.width
|
|
||||||
implicitHeight: delegateContent.implicitHeight
|
|
||||||
|
|
||||||
onActiveFocusChanged: {
|
|
||||||
if (activeFocus) {
|
|
||||||
site.rightButton.forceActiveFocus()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
id: delegateContent
|
id: delegateContent
|
||||||
|
|
||||||
anchors.top: parent.top
|
width: listView.width
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
|
|
||||||
LabelWithButtonType {
|
LabelWithButtonType {
|
||||||
id: site
|
id: site
|
||||||
|
@ -292,10 +245,7 @@ PageType {
|
||||||
DividerType {}
|
DividerType {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.fill: addSiteButton
|
anchors.fill: addSiteButton
|
||||||
|
@ -325,7 +275,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 +293,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 +307,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,21 +329,18 @@ 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 {}
|
||||||
|
|
||||||
LabelWithButtonType {
|
LabelWithButtonType {
|
||||||
id: exportSitesButton
|
id: exportSitesButton
|
||||||
|
enabled: !SettingsController.isOnTv()
|
||||||
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,13 +355,15 @@ 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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DividerType {}
|
DividerType {
|
||||||
|
enabled: !SettingsController.isOnTv()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -452,28 +373,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 +384,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 +416,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 +432,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 +446,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 {}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -14,8 +14,6 @@ import "../Config"
|
||||||
PageType {
|
PageType {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
defaultActiveFocusItem: focusItem
|
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: header
|
id: header
|
||||||
|
|
||||||
|
@ -25,15 +23,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 {
|
||||||
|
@ -50,6 +42,7 @@ PageType {
|
||||||
|
|
||||||
ListView {
|
ListView {
|
||||||
id: servicesListView
|
id: servicesListView
|
||||||
|
|
||||||
anchors.top: header.bottom
|
anchors.top: header.bottom
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
|
@ -57,16 +50,21 @@ PageType {
|
||||||
anchors.topMargin: 16
|
anchors.topMargin: 16
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
currentIndex: 1
|
property bool isFocusable: true
|
||||||
|
|
||||||
clip: true
|
clip: true
|
||||||
|
reuseItems: true
|
||||||
|
|
||||||
model: ApiServicesModel
|
model: ApiServicesModel
|
||||||
|
|
||||||
ScrollBar.vertical: ScrollBar {}
|
ScrollBar.vertical: ScrollBarType {}
|
||||||
|
|
||||||
delegate: Item {
|
delegate: Item {
|
||||||
implicitWidth: servicesListView.width
|
implicitWidth: servicesListView.width
|
||||||
implicitHeight: delegateContent.implicitHeight
|
implicitHeight: delegateContent.implicitHeight
|
||||||
|
|
||||||
|
enabled: isServiceAvailable
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: delegateContent
|
id: delegateContent
|
||||||
|
|
||||||
|
@ -86,14 +84,15 @@ PageType {
|
||||||
|
|
||||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||||
|
|
||||||
enabled: isServiceAvailable
|
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (isServiceAvailable) {
|
if (isServiceAvailable) {
|
||||||
ApiServicesModel.setServiceIndex(index)
|
ApiServicesModel.setServiceIndex(index)
|
||||||
PageController.goToPage(PageEnum.PageSetupWizardApiServiceInfo)
|
PageController.goToPage(PageEnum.PageSetupWizardApiServiceInfo)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Keys.onEnterPressed: clicked()
|
||||||
|
Keys.onReturnPressed: clicked()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,29 +25,27 @@ PageType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultActiveFocusItem: focusItem
|
ListView {
|
||||||
|
id: listView
|
||||||
|
|
||||||
FlickableType {
|
anchors.fill: parent
|
||||||
id: fl
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
contentHeight: content.height
|
|
||||||
|
|
||||||
ColumnLayout {
|
property bool isFocusable: true
|
||||||
id: content
|
|
||||||
|
|
||||||
anchors.top: parent.top
|
ScrollBar.vertical: ScrollBarType {}
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
|
|
||||||
spacing: 0
|
model: variants
|
||||||
|
|
||||||
Item {
|
clip: true
|
||||||
id: focusItem
|
|
||||||
KeyNavigation.tab: textKey.textField
|
reuseItems: 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
|
||||||
|
@ -59,7 +57,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 {
|
||||||
|
@ -70,7 +68,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
|
||||||
|
@ -130,6 +128,8 @@ PageType {
|
||||||
}
|
}
|
||||||
|
|
||||||
ParagraphTextType {
|
ParagraphTextType {
|
||||||
|
objectName: "insertKeyLabel"
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: 32
|
Layout.topMargin: 32
|
||||||
Layout.rightMargin: 16
|
Layout.rightMargin: 16
|
||||||
|
@ -153,8 +153,6 @@ PageType {
|
||||||
textField.text = ""
|
textField.text = ""
|
||||||
textField.paste()
|
textField.paste()
|
||||||
}
|
}
|
||||||
|
|
||||||
KeyNavigation.tab: continueButton
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BasicButtonType {
|
BasicButtonType {
|
||||||
|
@ -168,7 +166,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)) {
|
||||||
|
@ -187,22 +184,47 @@ 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
|
|
||||||
|
|
||||||
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() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
property list<QtObject> variants: [
|
||||||
|
amneziaVpn,
|
||||||
|
selfHostVpn,
|
||||||
|
backupRestore,
|
||||||
|
fileOpen,
|
||||||
|
qrScan,
|
||||||
|
siteLink
|
||||||
|
]
|
||||||
|
|
||||||
|
QtObject {
|
||||||
|
id: amneziaVpn
|
||||||
|
|
||||||
|
property string title: qsTr("VPN by Amnezia")
|
||||||
|
property string description: qsTr("Connect to classic paid and free VPN services from Amnezia")
|
||||||
|
property string imageSource: "qrc:/images/controls/amnezia.svg"
|
||||||
|
property bool isVisible: true
|
||||||
|
property var handler: function() {
|
||||||
PageController.showBusyIndicator(true)
|
PageController.showBusyIndicator(true)
|
||||||
var result = InstallController.fillAvailableServices()
|
var result = InstallController.fillAvailableServices()
|
||||||
PageController.showBusyIndicator(false)
|
PageController.showBusyIndicator(false)
|
||||||
|
@ -212,41 +234,26 @@ PageType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CardWithIconsType {
|
QtObject {
|
||||||
id: manualInstalling
|
id: selfHostVpn
|
||||||
|
|
||||||
Layout.fillWidth: true
|
property string title: qsTr("Self-hosted VPN")
|
||||||
Layout.rightMargin: 16
|
property string description: qsTr("Configure Amnezia VPN on your own server")
|
||||||
Layout.leftMargin: 16
|
property string imageSource: "qrc:/images/controls/server.svg"
|
||||||
Layout.bottomMargin: 16
|
property bool isVisible: true
|
||||||
|
property var handler: function() {
|
||||||
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)
|
PageController.goToPage(PageEnum.PageSetupWizardCredentials)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CardWithIconsType {
|
QtObject {
|
||||||
id: backupRestore
|
id: backupRestore
|
||||||
|
|
||||||
Layout.fillWidth: true
|
property string title: qsTr("Restore from backup")
|
||||||
Layout.rightMargin: 16
|
property string description: qsTr("")
|
||||||
Layout.leftMargin: 16
|
property string imageSource: "qrc:/images/controls/archive-restore.svg"
|
||||||
Layout.bottomMargin: 16
|
property bool isVisible: PageController.isStartPageVisible()
|
||||||
|
property var handler: function() {
|
||||||
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"),
|
var filePath = SystemController.getFileName(qsTr("Open backup file"),
|
||||||
qsTr("Backup files (*.backup)"))
|
qsTr("Backup files (*.backup)"))
|
||||||
if (filePath !== "") {
|
if (filePath !== "") {
|
||||||
|
@ -257,20 +264,14 @@ PageType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CardWithIconsType {
|
QtObject {
|
||||||
id: openFile
|
id: fileOpen
|
||||||
|
|
||||||
Layout.fillWidth: true
|
property string title: qsTr("File with connection settings")
|
||||||
Layout.rightMargin: 16
|
property string description: qsTr("")
|
||||||
Layout.leftMargin: 16
|
property string imageSource: "qrc:/images/controls/folder-search-2.svg"
|
||||||
Layout.bottomMargin: 16
|
property bool isVisible: true
|
||||||
|
property var handler: function() {
|
||||||
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)" :
|
var nameFilter = !ServersModel.getServersCount() ? "Config or backup files (*.vpn *.ovpn *.conf *.json *.backup)" :
|
||||||
"Config files (*.vpn *.ovpn *.conf *.json)"
|
"Config files (*.vpn *.ovpn *.conf *.json)"
|
||||||
var fileName = SystemController.getFileName(qsTr("Open config file"), nameFilter)
|
var fileName = SystemController.getFileName(qsTr("Open config file"), nameFilter)
|
||||||
|
@ -282,22 +283,14 @@ PageType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CardWithIconsType {
|
QtObject {
|
||||||
id: scanQr
|
id: qrScan
|
||||||
|
|
||||||
Layout.fillWidth: true
|
property string title: qsTr("QR code")
|
||||||
Layout.rightMargin: 16
|
property string description: qsTr("")
|
||||||
Layout.leftMargin: 16
|
property string imageSource: "qrc:/images/controls/scan-line.svg"
|
||||||
Layout.bottomMargin: 16
|
property bool isVisible: SettingsController.isCameraPresent()
|
||||||
|
property var handler: function() {
|
||||||
visible: SettingsController.isCameraPresent()
|
|
||||||
|
|
||||||
headerText: qsTr("QR code")
|
|
||||||
|
|
||||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
|
||||||
leftImageSource: "qrc:/images/controls/scan-line.svg"
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
ImportController.startDecodingQr()
|
ImportController.startDecodingQr()
|
||||||
if (Qt.platform.os === "ios") {
|
if (Qt.platform.os === "ios") {
|
||||||
PageController.goToPage(PageEnum.PageSetupWizardQrReader)
|
PageController.goToPage(PageEnum.PageSetupWizardQrReader)
|
||||||
|
@ -305,25 +298,15 @@ PageType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CardWithIconsType {
|
QtObject {
|
||||||
id: siteLink
|
id: siteLink
|
||||||
|
|
||||||
Layout.fillWidth: true
|
property string title: qsTr("I have nothing")
|
||||||
Layout.rightMargin: 16
|
property string description: qsTr("")
|
||||||
Layout.leftMargin: 16
|
property string imageSource: "qrc:/images/controls/help-circle.svg"
|
||||||
Layout.bottomMargin: 16
|
property bool isVisible: PageController.isStartPageVisible()
|
||||||
|
property var handler: function() {
|
||||||
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())
|
Qt.openUrlExternally(LanguageModel.getCurrentSiteUrl())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
@ -28,100 +21,133 @@ PageType {
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.topMargin: 20
|
anchors.topMargin: 20
|
||||||
|
|
||||||
KeyNavigation.tab: hostname.textField
|
onFocusChanged: {
|
||||||
|
if (this.activeFocus) {
|
||||||
|
listView.positionViewAtBeginning()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FlickableType {
|
ListView {
|
||||||
id: fl
|
id: listView
|
||||||
anchors.top: backButton.bottom
|
anchors.top: backButton.bottom
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
contentHeight: content.height
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
id: content
|
|
||||||
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.rightMargin: 16
|
anchors.left: parent.left
|
||||||
anchors.leftMargin: 16
|
|
||||||
|
|
||||||
spacing: 16
|
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: ScrollBarType {}
|
||||||
|
|
||||||
|
header: ColumnLayout {
|
||||||
|
width: listView.width
|
||||||
|
|
||||||
HeaderType {
|
HeaderType {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
Layout.leftMargin: 16
|
||||||
|
Layout.rightMargin: 16
|
||||||
|
Layout.bottomMargin: 16
|
||||||
|
|
||||||
headerText: qsTr("Configure your server")
|
headerText: qsTr("Configure your server")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
model: inputFields
|
||||||
|
spacing: 16
|
||||||
|
clip: true
|
||||||
|
reuseItems: true
|
||||||
|
|
||||||
|
delegate: ColumnLayout {
|
||||||
|
property alias textField: _textField.textField
|
||||||
|
|
||||||
|
width: listView.width
|
||||||
|
|
||||||
TextFieldWithHeaderType {
|
TextFieldWithHeaderType {
|
||||||
id: hostname
|
id: _textField
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
headerText: qsTr("Server IP address [:port]")
|
Layout.leftMargin: 16
|
||||||
textFieldPlaceholderText: qsTr("255.255.255.255:22")
|
Layout.rightMargin: 16
|
||||||
|
|
||||||
textField.onFocusChanged: {
|
property bool hidePassword: hideText
|
||||||
textField.text = textField.text.replace(/^\s+|\s+$/g, '')
|
|
||||||
}
|
|
||||||
|
|
||||||
KeyNavigation.tab: username.textField
|
headerText: title
|
||||||
}
|
textField.echoMode: hideText ? TextInput.Password : TextInput.Normal
|
||||||
|
buttonImageSource: imageSource
|
||||||
|
textFieldPlaceholderText: placeholderText
|
||||||
|
textField.text: textFieldText
|
||||||
|
|
||||||
TextFieldWithHeaderType {
|
rightButtonClickedOnEnter: true
|
||||||
id: username
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
headerText: qsTr("SSH Username")
|
|
||||||
textFieldPlaceholderText: "root"
|
|
||||||
|
|
||||||
textField.onFocusChanged: {
|
|
||||||
textField.text = textField.text.replace(/^\s+|\s+$/g, '')
|
|
||||||
}
|
|
||||||
|
|
||||||
KeyNavigation.tab: secretData.textField
|
|
||||||
}
|
|
||||||
|
|
||||||
TextFieldWithHeaderType {
|
|
||||||
id: secretData
|
|
||||||
|
|
||||||
property bool hidePassword: true
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
headerText: qsTr("Password or SSH private key")
|
|
||||||
textField.echoMode: hidePassword ? TextInput.Password : TextInput.Normal
|
|
||||||
buttonImageSource: textFieldText !== "" ? (hidePassword ? "qrc:/images/controls/eye.svg" : "qrc:/images/controls/eye-off.svg")
|
|
||||||
: ""
|
|
||||||
|
|
||||||
clickedFunc: function () {
|
clickedFunc: function () {
|
||||||
hidePassword = !hidePassword
|
clickedHandler()
|
||||||
}
|
}
|
||||||
|
|
||||||
textField.onFocusChanged: {
|
textField.onFocusChanged: {
|
||||||
textField.text = textField.text.replace(/^\s+|\s+$/g, '')
|
var _currentIndex = listView.currentIndex
|
||||||
|
var _currentItem = listView.itemAtIndex(_currentIndex).children[0]
|
||||||
|
listView.model[_currentIndex].textFieldText = _currentItem.textFieldText.replace(/^\s+|\s+$/g, '')
|
||||||
}
|
}
|
||||||
|
|
||||||
KeyNavigation.tab: continueButton
|
textField.onTextChanged: {
|
||||||
|
var _currentIndex = listView.currentIndex
|
||||||
|
textFieldText = textField.text
|
||||||
|
|
||||||
|
if (_currentIndex === vars.secretDataIndex) {
|
||||||
|
buttonImageSource = textFieldText !== "" ? (hideText ? "qrc:/images/controls/eye.svg" : "qrc:/images/controls/eye-off.svg") : ""
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
footer: ColumnLayout {
|
||||||
|
width: listView.width
|
||||||
|
|
||||||
BasicButtonType {
|
BasicButtonType {
|
||||||
id: continueButton
|
id: continueButton
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: 24
|
Layout.topMargin: 32
|
||||||
|
Layout.leftMargin: 16
|
||||||
|
Layout.rightMargin: 16
|
||||||
|
|
||||||
text: qsTr("Continue")
|
text: qsTr("Continue")
|
||||||
|
|
||||||
Keys.onTabPressed: lastItemTabClicked(focusItem)
|
|
||||||
|
|
||||||
clickedFunc: function() {
|
clickedFunc: function() {
|
||||||
forceActiveFocus()
|
if (!root.isCredentialsFilled()) {
|
||||||
if (!isCredentialsFilled()) {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
InstallController.setShouldCreateServer(true)
|
InstallController.setShouldCreateServer(true)
|
||||||
InstallController.setProcessedServerCredentials(hostname.textField.text, username.textField.text, secretData.textField.text)
|
var _hostname = listView.itemAtIndex(vars.hostnameIndex).children[0].textFieldText
|
||||||
|
var _username = listView.itemAtIndex(vars.usernameIndex).children[0].textFieldText
|
||||||
|
var _secretData = listView.itemAtIndex(vars.secretDataIndex).children[0].textFieldText
|
||||||
|
|
||||||
|
InstallController.setProcessedServerCredentials(_hostname, _username, _secretData)
|
||||||
|
|
||||||
PageController.showBusyIndicator(true)
|
PageController.showBusyIndicator(true)
|
||||||
var isConnectionOpened = InstallController.checkSshConnection()
|
var isConnectionOpened = InstallController.checkSshConnection()
|
||||||
|
@ -136,7 +162,10 @@ PageType {
|
||||||
|
|
||||||
LabelTextType {
|
LabelTextType {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: 12
|
Layout.topMargin: 24
|
||||||
|
Layout.leftMargin: 16
|
||||||
|
Layout.rightMargin: 16
|
||||||
|
Layout.bottomMargin: 16
|
||||||
|
|
||||||
text: qsTr("All data you enter will remain strictly confidential and will not be shared or disclosed to the Amnezia or any third parties")
|
text: qsTr("All data you enter will remain strictly confidential and will not be shared or disclosed to the Amnezia or any third parties")
|
||||||
}
|
}
|
||||||
|
@ -145,6 +174,8 @@ PageType {
|
||||||
id: siteLink
|
id: siteLink
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
Layout.leftMargin: 16
|
||||||
|
Layout.rightMargin: 16
|
||||||
Layout.bottomMargin: 16
|
Layout.bottomMargin: 16
|
||||||
|
|
||||||
headerText: qsTr("How to run your VPN server")
|
headerText: qsTr("How to run your VPN server")
|
||||||
|
@ -163,21 +194,78 @@ PageType {
|
||||||
function isCredentialsFilled() {
|
function isCredentialsFilled() {
|
||||||
var hasEmptyField = false
|
var hasEmptyField = false
|
||||||
|
|
||||||
if (hostname.textFieldText === "") {
|
var _hostname = listView.itemAtIndex(vars.hostnameIndex).children[0]
|
||||||
hostname.errorText = qsTr("Ip address cannot be empty")
|
if (_hostname.textFieldText === "") {
|
||||||
|
_hostname.errorText = qsTr("Ip address cannot be empty")
|
||||||
hasEmptyField = true
|
hasEmptyField = true
|
||||||
} else if (!hostname.textField.acceptableInput) {
|
} else if (!_hostname.textField.acceptableInput) {
|
||||||
hostname.errorText = qsTr("Enter the address in the format 255.255.255.255:88")
|
_hostname.errorText = qsTr("Enter the address in the format 255.255.255.255:88")
|
||||||
}
|
}
|
||||||
|
|
||||||
if (username.textFieldText === "") {
|
var _username = listView.itemAtIndex(vars.usernameIndex).children[0]
|
||||||
username.errorText = qsTr("Login cannot be empty")
|
if (_username.textFieldText === "") {
|
||||||
|
_username.errorText = qsTr("Login cannot be empty")
|
||||||
hasEmptyField = true
|
hasEmptyField = true
|
||||||
}
|
}
|
||||||
if (secretData.textFieldText === "") {
|
|
||||||
secretData.errorText = qsTr("Password/private key cannot be empty")
|
var _secretData = listView.itemAtIndex(vars.secretDataIndex).children[0]
|
||||||
|
if (_secretData.textFieldText === "") {
|
||||||
|
_secretData.errorText = qsTr("Password/private key cannot be empty")
|
||||||
hasEmptyField = true
|
hasEmptyField = true
|
||||||
}
|
}
|
||||||
|
|
||||||
return !hasEmptyField
|
return !hasEmptyField
|
||||||
}
|
}
|
||||||
|
|
||||||
|
property list<QtObject> inputFields: [
|
||||||
|
hostname,
|
||||||
|
username,
|
||||||
|
secretData
|
||||||
|
]
|
||||||
|
|
||||||
|
QtObject {
|
||||||
|
id: hostname
|
||||||
|
|
||||||
|
property string title: qsTr("Server IP address [:port]")
|
||||||
|
readonly property string placeholderText: qsTr("255.255.255.255:22")
|
||||||
|
property string textFieldText: ""
|
||||||
|
property bool hideText: false
|
||||||
|
property string imageSource: ""
|
||||||
|
readonly property var clickedHandler: function() {
|
||||||
|
console.debug(">>> Server IP address text field was clicked!!!")
|
||||||
|
clicked()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QtObject {
|
||||||
|
id: username
|
||||||
|
|
||||||
|
property string title: qsTr("SSH Username")
|
||||||
|
readonly property string placeholderText: "root"
|
||||||
|
property string textFieldText: ""
|
||||||
|
property bool hideText: false
|
||||||
|
property string imageSource: ""
|
||||||
|
readonly property var clickedHandler: undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
QtObject {
|
||||||
|
id: secretData
|
||||||
|
|
||||||
|
property string title: qsTr("Password or SSH private key")
|
||||||
|
readonly property string placeholderText: ""
|
||||||
|
property string textFieldText: ""
|
||||||
|
property bool hideText: true
|
||||||
|
property string imageSource: textFieldText !== "" ? (hideText ? "qrc:/images/controls/eye.svg" : "qrc:/images/controls/eye-off.svg") : ""
|
||||||
|
readonly property var clickedHandler: function() {
|
||||||
|
hideText = !hideText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QtObject {
|
||||||
|
id: vars
|
||||||
|
|
||||||
|
readonly property int hostnameIndex: 0
|
||||||
|
readonly property int usernameIndex: 1
|
||||||
|
readonly property int secretDataIndex: 2
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
@ -98,6 +87,8 @@ PageType {
|
||||||
property int containerDefaultPort
|
property int containerDefaultPort
|
||||||
property int containerDefaultTransportProto
|
property int containerDefaultTransportProto
|
||||||
|
|
||||||
|
property bool isFocusable: true
|
||||||
|
|
||||||
delegate: Item {
|
delegate: Item {
|
||||||
implicitWidth: containers.width
|
implicitWidth: containers.width
|
||||||
implicitHeight: delegateContent.implicitHeight
|
implicitHeight: delegateContent.implicitHeight
|
||||||
|
@ -163,7 +154,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() {
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
@ -41,110 +34,53 @@ PageType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
BackButtonType {
|
||||||
id: backButtonLayout
|
id: backButton
|
||||||
|
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
|
|
||||||
anchors.topMargin: 20
|
anchors.topMargin: 20
|
||||||
|
|
||||||
BackButtonType {
|
|
||||||
id: backButton
|
|
||||||
KeyNavigation.tab: containers
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FlickableType {
|
ListView {
|
||||||
id: fl
|
id: listView
|
||||||
anchors.top: backButtonLayout.bottom
|
anchors.top: backButton.bottom
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
contentHeight: content.implicitHeight + content.anchors.topMargin + content.anchors.bottomMargin
|
|
||||||
|
|
||||||
Column {
|
|
||||||
id: content
|
|
||||||
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.bottomMargin: 20
|
anchors.left: parent.left
|
||||||
|
|
||||||
Item {
|
property bool isFocusable: true
|
||||||
width: parent.width
|
|
||||||
height: header.implicitHeight
|
ScrollBar.vertical: ScrollBarType {}
|
||||||
|
|
||||||
|
header: ColumnLayout {
|
||||||
|
width: listView.width
|
||||||
|
|
||||||
HeaderType {
|
HeaderType {
|
||||||
id: header
|
id: header
|
||||||
|
|
||||||
anchors.fill: parent
|
Layout.fillWidth: true
|
||||||
|
Layout.leftMargin: 16
|
||||||
anchors.leftMargin: 16
|
Layout.rightMargin: 16
|
||||||
anchors.rightMargin: 16
|
Layout.bottomMargin: 16
|
||||||
|
|
||||||
width: parent.width
|
|
||||||
|
|
||||||
headerText: qsTr("VPN protocol")
|
headerText: qsTr("VPN protocol")
|
||||||
descriptionText: qsTr("Choose the one with the highest priority for you. Later, you can install other protocols and additional services, such as DNS proxy and SFTP.")
|
descriptionText: qsTr("Choose the one with the highest priority for you. Later, you can install other protocols and additional services, such as DNS proxy and SFTP.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ListView {
|
|
||||||
id: containers
|
|
||||||
width: parent.width
|
|
||||||
height: containers.contentItem.height
|
|
||||||
// currentIndex: -1
|
|
||||||
clip: true
|
|
||||||
interactive: false
|
|
||||||
model: proxyContainersModel
|
model: proxyContainersModel
|
||||||
|
clip: true
|
||||||
|
spacing: 0
|
||||||
|
reuseItems: true
|
||||||
|
snapMode: ListView.SnapToItem
|
||||||
|
|
||||||
function ensureCurrentItemVisible() {
|
delegate: ColumnLayout {
|
||||||
if (currentIndex >= 0) {
|
width: listView.width
|
||||||
if (currentItem.y < fl.contentY) {
|
|
||||||
fl.contentY = currentItem.y
|
|
||||||
} else if (currentItem.y + currentItem.height + header.height > fl.contentY + fl.height) {
|
|
||||||
fl.contentY = currentItem.y + currentItem.height + header.height - fl.height + 40 // 40 is a bottom margin
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
activeFocusOnTab: true
|
|
||||||
Keys.onTabPressed: {
|
|
||||||
if (currentIndex < this.count - 1) {
|
|
||||||
this.incrementCurrentIndex()
|
|
||||||
} else {
|
|
||||||
this.currentIndex = 0
|
|
||||||
focusItem.forceActiveFocus()
|
|
||||||
}
|
|
||||||
|
|
||||||
ensureCurrentItemVisible()
|
|
||||||
}
|
|
||||||
|
|
||||||
onVisibleChanged: {
|
|
||||||
if (visible) {
|
|
||||||
currentIndex = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
delegate: Item {
|
|
||||||
implicitWidth: containers.width
|
|
||||||
implicitHeight: delegateContent.implicitHeight
|
|
||||||
|
|
||||||
onActiveFocusChanged: {
|
|
||||||
if (activeFocus) {
|
|
||||||
container.rightButton.forceActiveFocus()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
id: delegateContent
|
|
||||||
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
|
|
||||||
LabelWithButtonType {
|
LabelWithButtonType {
|
||||||
id: container
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
text: name
|
text: name
|
||||||
|
@ -161,6 +97,3 @@ PageType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue