mirror of
https://github.com/amnezia-vpn/amnezia-client.git
synced 2026-10-23 03:26:59 +09:00
feat: new design system (#3280)
* refactor: focus controller refactoring * feat: update to new design system * feat: update to new desing system * chore: bump version
This commit is contained in:
1 parent
3e8077f2ca
commit
11355e1de0
241 files changed
+29198
-32524
No files matched your search
+2
-2
@@ -5,7 +5,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|||||||
|
|
||||||
set(PROJECT AmneziaVPN)
|
set(PROJECT AmneziaVPN)
|
||||||
if(NOT AMNEZIAVPN_VERSION)
|
if(NOT AMNEZIAVPN_VERSION)
|
||||||
set(AMNEZIAVPN_VERSION 5.0.3.1 CACHE STRING "Client app version")
|
set(AMNEZIAVPN_VERSION 5.1.0.0 CACHE STRING "Client app version")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(QT_CREATOR_SKIP_PACKAGE_MANAGER_SETUP ON CACHE BOOL "" FORCE)
|
set(QT_CREATOR_SKIP_PACKAGE_MANAGER_SETUP ON CACHE BOOL "" FORCE)
|
||||||
@@ -34,7 +34,7 @@ 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})
|
||||||
|
|
||||||
# bump by 2 on every release, because we're releasing two versions on the play store
|
# bump by 2 on every release, because we're releasing two versions on the play store
|
||||||
set(APP_ANDROID_VERSION_CODE 2171)
|
set(APP_ANDROID_VERSION_CODE 2173)
|
||||||
if(DEFINED APP_ANDROID_VERSION_CODE_OFFSET)
|
if(DEFINED APP_ANDROID_VERSION_CODE_OFFSET)
|
||||||
math(EXPR APP_ANDROID_VERSION_CODE "${APP_ANDROID_VERSION_CODE} + ${APP_ANDROID_VERSION_CODE_OFFSET}")
|
math(EXPR APP_ANDROID_VERSION_CODE "${APP_ANDROID_VERSION_CODE} + ${APP_ANDROID_VERSION_CODE_OFFSET}")
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<color name="black">#FF0E0E11</color>
|
<color name="black">#FF101012</color>
|
||||||
<style name="NoActionBar">
|
<style name="NoActionBar">
|
||||||
<item name="android:windowBackground">@color/black</item>
|
<item name="android:windowBackground">@color/black</item>
|
||||||
<item name="android:colorBackground">@color/black</item>
|
<item name="android:colorBackground">@color/black</item>
|
||||||
|
|||||||
@@ -66,7 +66,6 @@ set(HEADERS ${HEADERS}
|
|||||||
${CLIENT_ROOT_DIR}/core/utils/serialization/transfer.h
|
${CLIENT_ROOT_DIR}/core/utils/serialization/transfer.h
|
||||||
${CLIENT_ROOT_DIR}/../common/logger/logger.h
|
${CLIENT_ROOT_DIR}/../common/logger/logger.h
|
||||||
${CLIENT_ROOT_DIR}/../common/crypto/cryptoUtils.h
|
${CLIENT_ROOT_DIR}/../common/crypto/cryptoUtils.h
|
||||||
${CLIENT_ROOT_DIR}/ui/utils/qmlUtils.h
|
|
||||||
${CLIENT_ROOT_DIR}/core/utils/api/apiUtils.h
|
${CLIENT_ROOT_DIR}/core/utils/api/apiUtils.h
|
||||||
${CLIENT_ROOT_DIR}/core/utils/api/countryCatalog.h
|
${CLIENT_ROOT_DIR}/core/utils/api/countryCatalog.h
|
||||||
${CLIENT_ROOT_DIR}/core/utils/api/gatewayPayloadBuilder.h
|
${CLIENT_ROOT_DIR}/core/utils/api/gatewayPayloadBuilder.h
|
||||||
@@ -155,7 +154,6 @@ set(SOURCES ${SOURCES}
|
|||||||
${CLIENT_ROOT_DIR}/core/utils/serialization/vmess_new.cpp
|
${CLIENT_ROOT_DIR}/core/utils/serialization/vmess_new.cpp
|
||||||
${CLIENT_ROOT_DIR}/../common/logger/logger.cpp
|
${CLIENT_ROOT_DIR}/../common/logger/logger.cpp
|
||||||
${CLIENT_ROOT_DIR}/../common/crypto/cryptoUtils.cpp
|
${CLIENT_ROOT_DIR}/../common/crypto/cryptoUtils.cpp
|
||||||
${CLIENT_ROOT_DIR}/ui/utils/qmlUtils.cpp
|
|
||||||
${CLIENT_ROOT_DIR}/core/utils/api/apiUtils.cpp
|
${CLIENT_ROOT_DIR}/core/utils/api/apiUtils.cpp
|
||||||
${CLIENT_ROOT_DIR}/core/utils/api/countryCatalog.cpp
|
${CLIENT_ROOT_DIR}/core/utils/api/countryCatalog.cpp
|
||||||
${CLIENT_ROOT_DIR}/core/utils/api/gatewayPayloadBuilder.cpp
|
${CLIENT_ROOT_DIR}/core/utils/api/gatewayPayloadBuilder.cpp
|
||||||
|
|||||||
@@ -187,7 +187,6 @@ void CoreController::initControllers()
|
|||||||
|
|
||||||
if (m_engine) {
|
if (m_engine) {
|
||||||
m_focusController = new FocusController(m_engine, this);
|
m_focusController = new FocusController(m_engine, this);
|
||||||
setQmlContextProperty("FocusController", m_focusController);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
m_installUiController = new InstallUiController(m_installController, m_serversController, m_settingsController, m_protocolsModel, m_usersController,
|
m_installUiController = new InstallUiController(m_installController, m_serversController, m_settingsController, m_protocolsModel, m_usersController,
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M20 21C20.5523 21 21 21.4477 21 22V22.0098C21 22.5621 20.5523 23.0098 20 23.0098C19.4477 23.0098 19 22.5621 19 22.0098V22C19 21.4477 19.4477 21 20 21ZM20 3C20.7957 3 21.5585 3.3163 22.1211 3.87891C22.6837 4.44152 23 5.20435 23 6V10.5C23 11.0523 22.5523 11.5 22 11.5C21.4477 11.5 21 11.0523 21 10.5V8.81934L13.5664 13.5439L13.5605 13.5479C13.0928 13.8408 12.5519 13.9961 12 13.9961C11.4481 13.9961 10.9072 13.8408 10.4395 13.5479L10.4336 13.5439L3 8.81934V18C3 18.5477 3.45228 19 4 19H16.5C17.0523 19 17.5 19.4477 17.5 20C17.5 20.5523 17.0523 21 16.5 21H4C2.34772 21 1 19.6523 1 18V6C1 5.20435 1.3163 4.44152 1.87891 3.87891C2.44152 3.3163 3.20435 3 4 3H20ZM20 13C20.5523 13 21 13.4477 21 14V18C21 18.5523 20.5523 19 20 19C19.4477 19 19 18.5523 19 18V14C19 13.4477 19.4477 13 20 13ZM4 5C3.73478 5 3.4805 5.10543 3.29297 5.29297C3.10543 5.4805 3 5.73478 3 6V6.4502L11.5 11.8525C11.6496 11.9463 11.8235 11.9961 12 11.9961C12.1765 11.9961 12.3494 11.9463 12.499 11.8525L21 6.4502V6C21 5.73478 20.8946 5.48051 20.707 5.29297C20.5195 5.10543 20.2652 5 20 5H4Z" fill="#F87171"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -0,0 +1,6 @@
|
|||||||
|
<svg width="298" height="298" viewBox="46 46 298 298" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M129.5 50H64C56.268 50 50 56.268 50 64V129.5" stroke="white" stroke-width="4"/>
|
||||||
|
<path d="M260.5 50H326C333.732 50 340 56.268 340 64V129.5" stroke="white" stroke-width="4"/>
|
||||||
|
<path d="M340 260.5V326C340 333.732 333.732 340 326 340H260.5" stroke="white" stroke-width="4"/>
|
||||||
|
<path d="M129.5 340H64C56.268 340 50 333.732 50 326V260.5" stroke="white" stroke-width="4"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 481 B |
@@ -1,5 +0,0 @@
|
|||||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path d="M14.0001 12C14.0001 10.8954 13.1047 10 12.0001 10C10.8955 10 10.0001 10.8954 10.0001 12C10.0001 13.1046 10.8955 14 12.0001 14C13.1047 14 14.0001 13.1046 14.0001 12ZM16.0001 12C16.0001 14.2091 14.2092 16 12.0001 16C9.79094 16 8.00008 14.2091 8.00008 12C8.00008 9.79086 9.79094 8 12.0001 8C14.2092 8 16.0001 9.79086 16.0001 12Z" fill="#D7D8DB"/>
|
|
||||||
<path d="M20.5001 6C21.8808 6 23.0001 4.88071 23.0001 3.5C23.0001 2.11929 21.8808 1 20.5001 1C19.1194 1 18.0001 2.11929 18.0001 3.5C18.0001 4.88071 19.1194 6 20.5001 6Z" fill="#FBB36A"/>
|
|
||||||
<path d="M12.2198 1C12.9657 1 13.6811 1.28098 14.2315 1.7793C14.0814 2.32745 14.0001 2.90421 14.0001 3.5C14.0001 4.75704 14.3578 5.93021 14.9757 6.9248L14.7198 6.77539C14.2644 6.51244 13.8864 6.13489 13.6231 5.67969C13.3599 5.22423 13.2203 4.70672 13.2198 4.18066V4C13.2198 3.73478 13.1144 3.48051 12.9268 3.29297C12.7393 3.10557 12.4849 3 12.2198 3H11.7803C11.5153 3.00005 11.2608 3.10556 11.0733 3.29297C10.8858 3.4805 10.7803 3.73482 10.7803 4V4.18066C10.7798 4.70675 10.6403 5.22422 10.377 5.67969C10.1138 6.1349 9.73575 6.51343 9.28035 6.77637L9.27937 6.77539L8.85261 7.02441L8.84968 7.02637C8.39376 7.28946 7.87607 7.42773 7.34968 7.42773C6.82327 7.42764 6.30558 7.28958 5.84968 7.02637L5.72956 6.96191L5.70027 6.94629C5.47084 6.81401 5.1983 6.77827 4.94245 6.84668C4.71846 6.90662 4.52295 7.04188 4.38874 7.22852L4.33503 7.31152L4.11531 7.69141C3.98385 7.92039 3.94855 8.19212 4.01667 8.44727C4.07655 8.6712 4.21199 8.86674 4.39851 9.00098L4.48152 9.05469L4.53523 9.08789L4.68464 9.1875C5.11379 9.44747 5.47116 9.8098 5.7237 10.2441C5.98685 10.6968 6.12679 11.2108 6.12995 11.7344V12.2461L6.1241 12.4434C6.09561 12.9034 5.96204 13.3518 5.73152 13.7529C5.47084 14.2065 5.09507 14.5827 4.64363 14.8467L4.6446 14.8477L4.49421 14.9375L4.48152 14.9453C4.25235 15.0781 4.08509 15.2969 4.01667 15.5527C3.94855 15.8079 3.98385 16.0796 4.11531 16.3086L4.33406 16.6885C4.4668 16.9177 4.68662 17.0849 4.94245 17.1533C5.1983 17.2217 5.47084 17.186 5.70027 17.0537L5.72956 17.0381L5.84968 16.9736L6.02351 16.8809C6.43513 16.678 6.88902 16.5724 7.34968 16.5723C7.81025 16.5723 8.26427 16.6781 8.67585 16.8809L8.84968 16.9736L8.85261 16.9756L9.27937 17.2236C9.73477 17.4866 10.1138 17.8651 10.377 18.3203C10.6403 18.7758 10.7798 19.2932 10.7803 19.8193V20C10.7803 20.2652 10.8858 20.5195 11.0733 20.707C11.2608 20.8944 11.5153 21 11.7803 21H12.2198C12.4849 21 12.7393 20.8944 12.9268 20.707C13.1144 20.5195 13.2198 20.2652 13.2198 20V19.8193L13.2266 19.6221C13.2572 19.1646 13.3928 18.7188 13.6231 18.3203C13.8534 17.9221 14.1718 17.5828 14.5528 17.3281L14.7198 17.2236L15.1475 16.9756L15.1505 16.9736L15.3243 16.8809C15.7359 16.6781 16.1899 16.5723 16.6505 16.5723C17.1769 16.5723 17.6946 16.7104 18.1505 16.9736L18.2999 17.0537C18.5293 17.186 18.8018 17.2217 19.0577 17.1533C19.3136 17.0849 19.5323 16.9177 19.6651 16.6885L19.879 16.3086L19.8839 16.3008L19.9288 16.2119C20.0233 16.0026 20.0433 15.7658 19.9835 15.542C19.915 15.2863 19.7477 15.0683 19.5186 14.9355L19.3995 14.8721C19.3898 14.8669 19.3797 14.862 19.3702 14.8564C18.9124 14.5921 18.532 14.2112 18.2686 13.7529C18.0053 13.2946 17.8681 12.7746 17.8702 12.2461V11.75C17.8688 11.2228 18.0059 10.7042 18.2686 10.2471C18.3758 10.0606 18.5033 9.88765 18.6466 9.72949C19.2341 9.90406 19.8558 10 20.5001 10C20.8249 10 21.144 9.97473 21.4561 9.92871C21.2126 10.2693 20.9013 10.5607 20.5333 10.7764L20.5343 10.7773L20.3702 10.876C20.2176 10.9641 20.0908 11.0914 20.003 11.2441C19.9153 11.3968 19.8695 11.57 19.8702 11.7461V12.2539C19.8695 12.43 19.9153 12.6032 20.003 12.7559C20.0894 12.9062 20.214 13.0312 20.3634 13.1191L20.4903 13.1875L20.5216 13.2051L20.7706 13.3662C21.3298 13.7689 21.7365 14.3539 21.9161 15.0254C22.1205 15.7898 22.0131 16.6035 21.6202 17.29L21.6212 17.291L21.4014 17.6816L21.3956 17.6914C20.9972 18.379 20.342 18.8807 19.5743 19.0859C18.817 19.2883 18.0116 19.1847 17.3292 18.8008V18.8027L17.1798 18.7227C17.17 18.7175 17.16 18.7116 17.1505 18.7061C16.9986 18.6184 16.8258 18.5723 16.6505 18.5723C16.4751 18.5723 16.3024 18.6184 16.1505 18.7061L16.1495 18.7051L15.7227 18.9541L15.6114 19.0283C15.5077 19.1078 15.4204 19.2075 15.3546 19.3213C15.2668 19.4731 15.22 19.646 15.2198 19.8213V20C15.2198 20.7955 14.9043 21.5585 14.3419 22.1211C13.7793 22.6836 13.0154 23 12.2198 23H11.7803C10.9848 23 10.2208 22.6837 9.65828 22.1211C9.09594 21.5585 8.78035 20.7954 8.78035 20V19.8213L8.77156 19.6904C8.75435 19.5609 8.71141 19.4352 8.64558 19.3213C8.57971 19.2075 8.49243 19.1078 8.38874 19.0283L8.27742 18.9541L7.84968 18.7051C7.69776 18.6175 7.52506 18.5723 7.34968 18.5723C7.21824 18.5723 7.08852 18.5985 6.96785 18.6484L6.84968 18.7061C6.84011 18.7116 6.83013 18.7175 6.82038 18.7227L6.67097 18.8027L6.66999 18.8008C5.98771 19.1844 5.18286 19.2883 4.42585 19.0859C3.75418 18.9063 3.16836 18.4998 2.7657 17.9404L2.60456 17.6914L2.38484 17.3105L2.38386 17.3096C1.98692 16.6214 1.87897 15.8037 2.08406 15.0361C2.28803 14.2733 2.78502 13.6217 3.46589 13.2227L3.62995 13.124L3.73933 13.0508C3.84344 12.9708 3.93128 12.8704 3.99714 12.7559C4.08484 12.6032 4.13065 12.43 4.12995 12.2539V11.7461L4.12117 11.6162C4.10349 11.4873 4.06003 11.3622 3.99421 11.249C3.92841 11.136 3.84152 11.037 3.73835 10.958L3.62995 10.8857L3.57527 10.8516L3.42585 10.752C2.76614 10.351 2.2839 9.71122 2.08406 8.96387C1.87897 8.19633 1.98692 7.37864 2.38386 6.69043L2.38484 6.68848L2.60456 6.30859L2.7657 6.05957C3.16836 5.50016 3.75418 5.09371 4.42585 4.91406C5.1827 4.71172 5.9878 4.81485 6.66999 5.19824L6.67097 5.19727L6.82038 5.27734L6.84968 5.29395L6.967Line truncated
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 5.9 KiB |
@@ -1,6 +1,3 @@
|
|||||||
<svg width="19" height="18" viewBox="0 0 19 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<rect x="0.5" width="18" height="18" rx="5" fill="white"/>
|
<path d="M19.0275 5C19.0858 5.00093 19.1439 5.0068 19.2014 5.01758C19.2155 5.02023 19.2294 5.02411 19.2434 5.02734C19.2617 5.03158 19.28 5.03576 19.298 5.04102C19.3522 5.05672 19.4046 5.07722 19.4553 5.10156C19.4608 5.10422 19.4664 5.10661 19.4719 5.10938L19.4875 5.11719C19.4978 5.12261 19.5076 5.12899 19.5178 5.13477C19.5626 5.16041 19.6064 5.18867 19.6477 5.22168C19.6746 5.24318 19.701 5.26568 19.7258 5.29004C19.7418 5.30575 19.7559 5.32322 19.7707 5.33984C19.7771 5.3471 19.784 5.35393 19.7902 5.36133C19.9384 5.53804 20.0207 5.76027 20.0207 5.99121L20.0197 12.9941C20.0196 13.5415 19.5679 13.9853 19.0109 13.9854C18.454 13.9852 18.0022 13.5415 18.0021 12.9941V8.41309L12.2385 14.1777V18.4404C12.2382 18.9876 11.7875 19.4315 11.2307 19.4316C10.6737 19.4316 10.2221 18.9877 10.2219 18.4404V13.7715L10.2268 13.6748C10.2492 13.4505 10.3488 13.2393 10.5109 13.0771L16.6037 6.98242H12.0109C11.454 6.98233 11.0023 6.53854 11.0021 5.99121C11.0023 5.44389 11.454 5.00009 12.0109 5H19.0275ZM4.30097 6.0625C4.69824 5.67878 5.33821 5.6847 5.72871 6.0752L9.61836 9.96387C10.0087 10.3543 10.0026 10.9825 9.60566 11.3662C9.20853 11.7496 8.57034 11.7445 8.17988 11.3545L4.28925 7.46387C3.89917 7.07347 3.90418 6.44616 4.30097 6.0625Z" fill="#E4E4E7"/>
|
||||||
<path d="M8.49219 13.5L8.49219 9.44141L14.0191 4.99484" stroke="#0E0E11" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
<path d="M4.47363 5.49805L6.98828 8.0127" stroke="#0E0E11" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
<path d="M14.4727 9.5L14.4727 4.5033L9.50195 4.5033" stroke="#0E0E11" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
</svg>
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 511 B After Width: | Height: | Size: 1.3 KiB |
@@ -42,6 +42,7 @@
|
|||||||
<file>controls/infinity.svg</file>
|
<file>controls/infinity.svg</file>
|
||||||
<file>controls/info.svg</file>
|
<file>controls/info.svg</file>
|
||||||
<file>controls/mail.svg</file>
|
<file>controls/mail.svg</file>
|
||||||
|
<file>controls/mail-warning.svg</file>
|
||||||
<file>controls/map-pin.svg</file>
|
<file>controls/map-pin.svg</file>
|
||||||
<file>controls/more-vertical.svg</file>
|
<file>controls/more-vertical.svg</file>
|
||||||
<file>controls/news.svg</file>
|
<file>controls/news.svg</file>
|
||||||
@@ -49,6 +50,7 @@
|
|||||||
<file>controls/unread-dot.svg</file>
|
<file>controls/unread-dot.svg</file>
|
||||||
<file>controls/plus.svg</file>
|
<file>controls/plus.svg</file>
|
||||||
<file>controls/qr-code.svg</file>
|
<file>controls/qr-code.svg</file>
|
||||||
|
<file>controls/qr-scan-frame.svg</file>
|
||||||
<file>controls/radio-button-inner-circle-pressed.png</file>
|
<file>controls/radio-button-inner-circle-pressed.png</file>
|
||||||
<file>controls/radio-button-inner-circle.png</file>
|
<file>controls/radio-button-inner-circle.png</file>
|
||||||
<file>controls/radio-button-pressed.svg</file>
|
<file>controls/radio-button-pressed.svg</file>
|
||||||
@@ -62,7 +64,6 @@
|
|||||||
<file>controls/server.svg</file>
|
<file>controls/server.svg</file>
|
||||||
<file>controls/settings-2.svg</file>
|
<file>controls/settings-2.svg</file>
|
||||||
<file>controls/settings.svg</file>
|
<file>controls/settings.svg</file>
|
||||||
<file>controls/settings-news.svg</file>
|
|
||||||
<file>controls/share-2.svg</file>
|
<file>controls/share-2.svg</file>
|
||||||
<file>controls/star.svg</file>
|
<file>controls/star.svg</file>
|
||||||
<file>controls/star-filled.svg</file>
|
<file>controls/star-filled.svg</file>
|
||||||
|
|||||||
+1401
-1356
File diff suppressed because it is too large.
Load diff
+1405
-1360
File diff suppressed because it is too large.
Load diff
+1401
-1356
File diff suppressed because it is too large.
Load diff
+1401
-1356
File diff suppressed because it is too large.
Load diff
+1401
-1356
File diff suppressed because it is too large.
Load diff
+1405
-1360
File diff suppressed because it is too large.
Load diff
+1401
-1356
File diff suppressed because it is too large.
Load diff
+1676
-1638
File diff suppressed because it is too large.
Load diff
+1401
-1356
File diff suppressed because it is too large.
Load diff
+1401
-1356
File diff suppressed because it is too large.
Load diff
+1402
-1357
File diff suppressed because it is too large.
Load diff
+1401
-1356
File diff suppressed because it is too large.
Load diff
@@ -1,215 +1,154 @@
|
|||||||
#include "focusController.h"
|
#include "focusController.h"
|
||||||
#include "ui/utils/qmlUtils.h"
|
|
||||||
|
|
||||||
|
#include <QGuiApplication>
|
||||||
|
#include <QKeyEvent>
|
||||||
#include <QQmlApplicationEngine>
|
#include <QQmlApplicationEngine>
|
||||||
|
#include <QQuickItem>
|
||||||
#include <QQuickWindow>
|
#include <QQuickWindow>
|
||||||
|
#include <QStyleHints>
|
||||||
|
|
||||||
#include "logger.h"
|
#include <algorithm>
|
||||||
|
|
||||||
namespace {
|
namespace
|
||||||
Logger logger("FocusController");
|
{
|
||||||
|
constexpr qreal ensureVisibleMargin = 40;
|
||||||
|
|
||||||
|
QQuickItem *focusChainRoot(QQuickItem *item)
|
||||||
|
{
|
||||||
|
for (QQuickItem *it = item; it; it = it->parentItem()) {
|
||||||
|
if (it->inherits("QQuickPopupItem")) {
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return item->window()->contentItem();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FocusController::FocusController(QQmlApplicationEngine *engine, QObject *parent)
|
FocusController::FocusController(QQmlApplicationEngine *engine, QObject *parent) : QObject { parent }
|
||||||
: QObject { parent },
|
|
||||||
m_engine { engine },
|
|
||||||
m_focusChain {},
|
|
||||||
m_focusedItem { nullptr },
|
|
||||||
m_rootObjects {},
|
|
||||||
m_defaultFocusItem { nullptr },
|
|
||||||
m_lvfc { nullptr }
|
|
||||||
{
|
{
|
||||||
QObject::connect(m_engine, &QQmlApplicationEngine::objectCreated, this, [this](QObject *object, const QUrl &url) {
|
QGuiApplication::styleHints()->setTabFocusBehavior(Qt::TabFocusAllControls);
|
||||||
QQuickItem *newDefaultFocusItem = object->findChild<QQuickItem *>("defaultFocusItem");
|
|
||||||
if (newDefaultFocusItem && m_defaultFocusItem != newDefaultFocusItem) {
|
connect(engine, &QQmlApplicationEngine::objectCreated, this, [this](QObject *object) {
|
||||||
m_defaultFocusItem = newDefaultFocusItem;
|
if (auto *window = qobject_cast<QQuickWindow *>(object)) {
|
||||||
|
window->installEventFilter(this);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
QObject::connect(this, &FocusController::focusedItemChanged, this,
|
|
||||||
[this]() { m_focusedItem->forceActiveFocus(Qt::TabFocusReason); });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FocusController::nextKeyTabItem()
|
bool FocusController::eventFilter(QObject *watched, QEvent *event)
|
||||||
{
|
{
|
||||||
nextItem(Direction::Forward);
|
if (event->type() != QEvent::KeyPress) {
|
||||||
}
|
return false;
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
auto *window = qobject_cast<QQuickWindow *>(watched);
|
||||||
m_rootObjects.pop();
|
auto *keyEvent = static_cast<QKeyEvent *>(event);
|
||||||
dropListView();
|
if (!window || (keyEvent->modifiers() & ~(Qt::ShiftModifier | Qt::KeypadModifier))) {
|
||||||
setFocusOnDefaultItem();
|
return false;
|
||||||
} else {
|
|
||||||
logger.warning() << "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) {
|
|
||||||
logger.error() << "No ROOT OBJECT found!";
|
|
||||||
resetRootObject();
|
|
||||||
dropListView();
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
m_focusChain.append(FocusControl::getSubChain(rootObject));
|
QQuickItem *current = window->activeFocusItem() ? window->activeFocusItem() : window->contentItem();
|
||||||
|
|
||||||
std::sort(m_focusChain.begin(), m_focusChain.end(),
|
bool forward = false;
|
||||||
direction == Direction::Forward ? FocusControl::isLess : FocusControl::isMore);
|
switch (keyEvent->key()) {
|
||||||
|
case Qt::Key_Tab: forward = true; break;
|
||||||
if (m_focusChain.empty()) {
|
case Qt::Key_Backtab: forward = false; break;
|
||||||
logger.warning() << "Focus chain is empty!";
|
case Qt::Key_Down:
|
||||||
resetRootObject();
|
case Qt::Key_Right: forward = true; [[fallthrough]];
|
||||||
dropListView();
|
case Qt::Key_Up:
|
||||||
return;
|
case Qt::Key_Left:
|
||||||
}
|
if (wantsArrowKey(current, keyEvent->key())) {
|
||||||
}
|
return false;
|
||||||
|
|
||||||
void FocusController::nextItem(Direction direction)
|
|
||||||
{
|
|
||||||
reload(direction);
|
|
||||||
|
|
||||||
if (m_lvfc && FocusControl::isListView(m_focusedItem)) {
|
|
||||||
direction == Direction::Forward ? focusNextListViewItem() : focusPreviousListViewItem();
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_focusChain.empty()) {
|
|
||||||
logger.warning() << "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) {
|
|
||||||
logger.warning() << "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();
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
default: return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
sortListViewDelegates(focusChainRoot(current));
|
||||||
|
|
||||||
|
QQuickItem *next = current->nextItemInFocusChain(forward);
|
||||||
|
if (next && next != current) {
|
||||||
|
next->forceActiveFocus(forward ? Qt::TabFocusReason : Qt::BacktabFocusReason);
|
||||||
|
ensureVisible(next);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FocusController::wantsArrowKey(const QQuickItem *item, int key)
|
||||||
|
{
|
||||||
|
if (item->inherits("QQuickTextEdit")) {
|
||||||
|
return !item->property("readOnly").toBool();
|
||||||
|
}
|
||||||
|
if (item->inherits("QQuickTextInput")) {
|
||||||
|
return key == Qt::Key_Left || key == Qt::Key_Right;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FocusController::sortListViewDelegates(QQuickItem *root)
|
||||||
|
{
|
||||||
|
if (!root->isVisible()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setFocusItem(focusedItem);
|
if (root->inherits("QQuickListView")) {
|
||||||
}
|
if (auto *contentItem = root->property("contentItem").value<QQuickItem *>()) {
|
||||||
|
QList<QQuickItem *> delegates = contentItem->childItems();
|
||||||
void FocusController::focusNextListViewItem()
|
const auto byPosition = [](const QQuickItem *a, const QQuickItem *b) {
|
||||||
{
|
return a->y() != b->y() ? a->y() < b->y() : a->x() < b->x();
|
||||||
m_lvfc->reloadFocusChain();
|
};
|
||||||
if (m_lvfc->isLastFocusItemInListView() || m_lvfc->isReturnNeeded()) {
|
if (!std::is_sorted(delegates.cbegin(), delegates.cend(), byPosition)) {
|
||||||
dropListView();
|
std::stable_sort(delegates.begin(), delegates.end(), byPosition);
|
||||||
nextItem(Direction::Forward);
|
for (qsizetype i = 1; i < delegates.size(); ++i) {
|
||||||
return;
|
delegates.at(i)->stackAfter(delegates.at(i - 1));
|
||||||
} else if (m_lvfc->isLastFocusItemInDelegate()) {
|
}
|
||||||
m_lvfc->resetFocusChain();
|
}
|
||||||
m_lvfc->nextDelegate();
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_lvfc->focusNextItem();
|
const QList<QQuickItem *> children = root->childItems();
|
||||||
|
for (QQuickItem *child : children) {
|
||||||
|
sortListViewDelegates(child);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FocusController::focusPreviousListViewItem()
|
void FocusController::ensureVisible(QQuickItem *item)
|
||||||
{
|
{
|
||||||
m_lvfc->reloadFocusChain();
|
for (QQuickItem *flickable = item->parentItem(); flickable; flickable = flickable->parentItem()) {
|
||||||
if (m_lvfc->isFirstFocusItemInListView() || m_lvfc->isReturnNeeded()) {
|
if (!flickable->inherits("QQuickFlickable")) {
|
||||||
dropListView();
|
continue;
|
||||||
nextItem(Direction::Backward);
|
}
|
||||||
return;
|
|
||||||
} else if (m_lvfc->isFirstFocusItemInDelegate()) {
|
auto *contentItem = flickable->property("contentItem").value<QQuickItem *>();
|
||||||
m_lvfc->resetFocusChain();
|
if (!contentItem) {
|
||||||
m_lvfc->previousDelegate();
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QRectF rect = item->mapRectToItem(contentItem, item->boundingRect());
|
||||||
|
scrollToRange(flickable, rect.top(), rect.bottom(), flickable->height(), "contentY", "originY",
|
||||||
|
"contentHeight", "topMargin", "bottomMargin");
|
||||||
|
scrollToRange(flickable, rect.left(), rect.right(), flickable->width(), "contentX", "originX",
|
||||||
|
"contentWidth", "leftMargin", "rightMargin");
|
||||||
}
|
}
|
||||||
|
|
||||||
m_lvfc->focusPreviousItem();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FocusController::dropListView()
|
void FocusController::scrollToRange(QQuickItem *flickable, qreal start, qreal end, qreal viewSize, const char *position,
|
||||||
|
const char *origin, const char *contentSize, const char *startMargin,
|
||||||
|
const char *endMargin)
|
||||||
{
|
{
|
||||||
if (m_lvfc) {
|
const qreal current = flickable->property(position).toReal();
|
||||||
delete m_lvfc;
|
|
||||||
m_lvfc = nullptr;
|
qreal target = current;
|
||||||
|
if (start < current) {
|
||||||
|
target = start - ensureVisibleMargin;
|
||||||
|
} else if (end > current + viewSize) {
|
||||||
|
target = std::min(end - viewSize + ensureVisibleMargin, start - ensureVisibleMargin);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target != current) {
|
||||||
|
const qreal min = flickable->property(origin).toReal() - flickable->property(startMargin).toReal();
|
||||||
|
const qreal max = flickable->property(origin).toReal() + flickable->property(contentSize).toReal()
|
||||||
|
+ flickable->property(endMargin).toReal() - viewSize;
|
||||||
|
flickable->setProperty(position, std::clamp(target, min, std::max(min, max)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,57 +1,26 @@
|
|||||||
#ifndef FOCUSCONTROLLER_H
|
#ifndef FOCUSCONTROLLER_H
|
||||||
#define FOCUSCONTROLLER_H
|
#define FOCUSCONTROLLER_H
|
||||||
|
|
||||||
#include "ui/controllers/qml/listViewFocusController.h"
|
|
||||||
|
|
||||||
#include <QQmlApplicationEngine>
|
#include <QQmlApplicationEngine>
|
||||||
|
|
||||||
/*!
|
class QQuickItem;
|
||||||
* \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
|
class FocusController : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit FocusController(QQmlApplicationEngine *engine, QObject *parent = nullptr);
|
explicit FocusController(QQmlApplicationEngine *engine, QObject *parent = nullptr);
|
||||||
~FocusController() override = default;
|
|
||||||
|
|
||||||
Q_INVOKABLE void nextKeyTabItem();
|
protected:
|
||||||
Q_INVOKABLE void previousKeyTabItem();
|
bool eventFilter(QObject *watched, QEvent *event) override;
|
||||||
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:
|
private:
|
||||||
enum class Direction {
|
static bool wantsArrowKey(const QQuickItem *item, int key);
|
||||||
Forward,
|
static void sortListViewDelegates(QQuickItem *root);
|
||||||
Backward,
|
static void ensureVisible(QQuickItem *item);
|
||||||
};
|
static void scrollToRange(QQuickItem *flickable, qreal start, qreal end, qreal viewSize, const char *position,
|
||||||
|
const char *origin, const char *contentSize, const char *startMargin,
|
||||||
void reload(Direction direction);
|
const char *endMargin);
|
||||||
void nextItem(Direction direction);
|
|
||||||
void focusNextListViewItem();
|
|
||||||
void focusPreviousListViewItem();
|
|
||||||
void dropListView();
|
|
||||||
|
|
||||||
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; // Pointer to stack of roots for focus chain
|
|
||||||
QQuickItem *m_defaultFocusItem;
|
|
||||||
|
|
||||||
ListViewFocusController *m_lvfc; // ListView focus manager
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void focusedItemChanged();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // FOCUSCONTROLLER_H
|
#endif // FOCUSCONTROLLER_H
|
||||||
@@ -1,323 +0,0 @@
|
|||||||
#include "listViewFocusController.h"
|
|
||||||
#include "ui/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),
|
|
||||||
Q_ARG(int, 4));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Section::Footer: {
|
|
||||||
QMetaObject::invokeMethod(m_listView, "positionViewAtEnd");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QMetaObject::invokeMethod(m_listView, "forceLayout");
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
viewAtCurrentIndex();
|
|
||||||
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);
|
|
||||||
viewAtCurrentIndex();
|
|
||||||
break;
|
|
||||||
} else if (hasHeader()) {
|
|
||||||
m_currentSection = Section::Header;
|
|
||||||
viewAtCurrentIndex();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
[[fallthrough]];
|
|
||||||
}
|
|
||||||
case Section::Header: {
|
|
||||||
m_isReturnNeeded = true;
|
|
||||||
m_currentSection = Section::Default;
|
|
||||||
viewAtCurrentIndex();
|
|
||||||
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++;
|
|
||||||
if (m_focusedItemIndex < 0 || m_focusedItemIndex >= m_focusChain.size()) {
|
|
||||||
m_focusedItemIndex = 0;
|
|
||||||
}
|
|
||||||
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 < 0 || m_focusedItemIndex > m_focusChain.size()) {
|
|
||||||
m_focusedItemIndex = m_focusChain.size();
|
|
||||||
}
|
|
||||||
m_focusedItemIndex--;
|
|
||||||
if (m_focusedItemIndex < 0) {
|
|
||||||
m_focusedItemIndex = m_focusChain.size() - 1;
|
|
||||||
}
|
|
||||||
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());
|
|
||||||
m_focusedItemIndex = m_focusChain.indexOf(m_focusedItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ListViewFocusController::isFirstFocusItemInDelegate() const
|
|
||||||
{
|
|
||||||
return m_focusedItem && !m_focusChain.isEmpty() && (m_focusedItem == m_focusChain.first());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ListViewFocusController::isLastFocusItemInDelegate() const
|
|
||||||
{
|
|
||||||
return m_focusedItem && !m_focusChain.isEmpty() && (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: {
|
|
||||||
return 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;
|
|
||||||
}
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
#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
|
|
||||||
@@ -89,12 +89,7 @@ void PageController::keyPressEvent(Qt::Key key)
|
|||||||
switch (key) {
|
switch (key) {
|
||||||
case Qt::Key_Back:
|
case Qt::Key_Back:
|
||||||
case Qt::Key_Escape: {
|
case Qt::Key_Escape: {
|
||||||
if (m_drawerDepth) {
|
emit escapePressed();
|
||||||
emit closeTopDrawer();
|
|
||||||
decrementDrawerDepth();
|
|
||||||
} else {
|
|
||||||
emit escapePressed();
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: return;
|
default: return;
|
||||||
@@ -104,9 +99,9 @@ void PageController::keyPressEvent(Qt::Key key)
|
|||||||
unsigned int PageController::getInitialPageNavigationBarColor()
|
unsigned int PageController::getInitialPageNavigationBarColor()
|
||||||
{
|
{
|
||||||
if (m_serversController->getServersCount()) {
|
if (m_serversController->getServersCount()) {
|
||||||
return 0xFF1C1D21;
|
return 0xFF1F1F23;
|
||||||
} else {
|
} else {
|
||||||
return 0xFF0E0E11;
|
return 0xFF101012;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,32 +145,6 @@ void PageController::closeApplication()
|
|||||||
qApp->quit();
|
qApp->quit();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PageController::setDrawerDepth(const int depth)
|
|
||||||
{
|
|
||||||
if (depth >= 0) {
|
|
||||||
m_drawerDepth = depth;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int PageController::getDrawerDepth() const
|
|
||||||
{
|
|
||||||
return m_drawerDepth;
|
|
||||||
}
|
|
||||||
|
|
||||||
int PageController::incrementDrawerDepth()
|
|
||||||
{
|
|
||||||
return ++m_drawerDepth;
|
|
||||||
}
|
|
||||||
|
|
||||||
int PageController::decrementDrawerDepth()
|
|
||||||
{
|
|
||||||
if (m_drawerDepth == 0) {
|
|
||||||
return m_drawerDepth;
|
|
||||||
} else {
|
|
||||||
return --m_drawerDepth;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool PageController::isEdgeToEdgeEnabled()
|
bool PageController::isEdgeToEdgeEnabled()
|
||||||
{
|
{
|
||||||
#ifdef Q_OS_ANDROID
|
#ifdef Q_OS_ANDROID
|
||||||
|
|||||||
@@ -41,11 +41,6 @@ public slots:
|
|||||||
|
|
||||||
void closeApplication();
|
void closeApplication();
|
||||||
|
|
||||||
void setDrawerDepth(const int depth);
|
|
||||||
int getDrawerDepth() const;
|
|
||||||
int incrementDrawerDepth();
|
|
||||||
int decrementDrawerDepth();
|
|
||||||
|
|
||||||
bool isEdgeToEdgeEnabled();
|
bool isEdgeToEdgeEnabled();
|
||||||
int getStatusBarHeight();
|
int getStatusBarHeight();
|
||||||
int getNavigationBarHeight();
|
int getNavigationBarHeight();
|
||||||
@@ -88,7 +83,6 @@ signals:
|
|||||||
void unsupportedConnectDrawerRequested();
|
void unsupportedConnectDrawerRequested();
|
||||||
|
|
||||||
void escapePressed();
|
void escapePressed();
|
||||||
void closeTopDrawer();
|
|
||||||
|
|
||||||
void imeHeightChanged(int height);
|
void imeHeightChanged(int height);
|
||||||
void safeAreaTopMarginChanged();
|
void safeAreaTopMarginChanged();
|
||||||
@@ -100,8 +94,6 @@ private:
|
|||||||
|
|
||||||
bool m_isTriggeredByConnectButton;
|
bool m_isTriggeredByConnectButton;
|
||||||
|
|
||||||
int m_drawerDepth = 0;
|
|
||||||
|
|
||||||
mutable int m_cachedStatusBarHeight = -1;
|
mutable int m_cachedStatusBarHeight = -1;
|
||||||
mutable int m_cachedNavigationBarHeight = -1;
|
mutable int m_cachedNavigationBarHeight = -1;
|
||||||
mutable bool m_cachedEdgeToEdgeEnabled = false;
|
mutable bool m_cachedEdgeToEdgeEnabled = false;
|
||||||
|
|||||||
@@ -1,150 +0,0 @@
|
|||||||
import QtQuick
|
|
||||||
import QtQuick.Controls
|
|
||||||
import QtQuick.Layouts
|
|
||||||
import QtQuick.Shapes
|
|
||||||
|
|
||||||
import Style 1.0
|
|
||||||
|
|
||||||
import "../Config"
|
|
||||||
import "../Controls2"
|
|
||||||
import "../Controls2/TextTypes"
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property real contentHeight: content.implicitHeight + content.anchors.topMargin + content.anchors.bottomMargin
|
|
||||||
property bool isFocusable: true
|
|
||||||
|
|
||||||
gradient: Gradient {
|
|
||||||
orientation: Gradient.Horizontal
|
|
||||||
GradientStop { position: 0.0; color: AmneziaStyle.color.translucentSlateGray }
|
|
||||||
GradientStop { position: 1.0; color: AmneziaStyle.color.translucentOnyxBlack }
|
|
||||||
}
|
|
||||||
border.width: 1
|
|
||||||
border.color: AmneziaStyle.color.onyxBlack
|
|
||||||
radius: 13
|
|
||||||
|
|
||||||
visible: ServersUiController.isAdVisible
|
|
||||||
|
|
||||||
Keys.onTabPressed: {
|
|
||||||
FocusController.nextKeyTabItem()
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onBacktabPressed: {
|
|
||||||
FocusController.previousKeyTabItem()
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onUpPressed: {
|
|
||||||
FocusController.nextKeyUpItem()
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onDownPressed: {
|
|
||||||
FocusController.nextKeyDownItem()
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onLeftPressed: {
|
|
||||||
FocusController.nextKeyLeftItem()
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onRightPressed: {
|
|
||||||
FocusController.nextKeyRightItem()
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onEnterPressed: {
|
|
||||||
Qt.openUrlExternally(ServersUiController.serverAdEndpoint(ServersUiController.defaultServerId))
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onReturnPressed: {
|
|
||||||
Qt.openUrlExternally(ServersUiController.serverAdEndpoint(ServersUiController.defaultServerId))
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
id: content
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.leftMargin: 16
|
|
||||||
anchors.rightMargin: 12
|
|
||||||
anchors.topMargin: 12
|
|
||||||
anchors.bottomMargin: 12
|
|
||||||
spacing: 20
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
spacing: 4
|
|
||||||
|
|
||||||
CaptionTextType {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
text: ServersUiController.adHeader
|
|
||||||
color: AmneziaStyle.color.paleGray
|
|
||||||
font.pixelSize: 14
|
|
||||||
font.weight: 700
|
|
||||||
|
|
||||||
textFormat: Text.RichText
|
|
||||||
}
|
|
||||||
|
|
||||||
CaptionTextType {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
text: ServersUiController.adDescription
|
|
||||||
color: AmneziaStyle.color.mutedGray
|
|
||||||
wrapMode: Text.WordWrap
|
|
||||||
lineHeight: 18
|
|
||||||
lineHeightMode: Text.FixedHeight
|
|
||||||
font.pixelSize: 14
|
|
||||||
|
|
||||||
visible: text !== ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
implicitWidth: 40
|
|
||||||
implicitHeight: 40
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: chevronBackground
|
|
||||||
anchors.fill: parent
|
|
||||||
radius: 12
|
|
||||||
color: AmneziaStyle.color.transparent
|
|
||||||
border.width: root.activeFocus ? 1 : 0
|
|
||||||
border.color: AmneziaStyle.color.paleGray
|
|
||||||
|
|
||||||
Behavior on color {
|
|
||||||
PropertyAnimation { duration: 200 }
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on border.width {
|
|
||||||
PropertyAnimation { duration: 200 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Image {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
source: "qrc:/images/controls/chevron-right.svg"
|
|
||||||
sourceSize: Qt.size(24, 24)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
id: mouseArea
|
|
||||||
anchors.fill: parent
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
hoverEnabled: true
|
|
||||||
|
|
||||||
onEntered: {
|
|
||||||
chevronBackground.color = AmneziaStyle.color.slateGray
|
|
||||||
}
|
|
||||||
|
|
||||||
onExited: {
|
|
||||||
chevronBackground.color = AmneziaStyle.color.transparent
|
|
||||||
}
|
|
||||||
|
|
||||||
onPressedChanged: {
|
|
||||||
chevronBackground.color = pressed ? AmneziaStyle.color.charcoalGray : containsMouse ? AmneziaStyle.color.slateGray : AmneziaStyle.color.transparent
|
|
||||||
}
|
|
||||||
|
|
||||||
onClicked: function() {
|
|
||||||
root.forceActiveFocus()
|
|
||||||
Qt.openUrlExternally(ServersUiController.serverAdEndpoint(ServersUiController.defaultServerId))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,15 +1,13 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
|
|
||||||
import Style 1.0
|
import Style 1.0
|
||||||
|
|
||||||
import "../Controls2"
|
import "../Controls2"
|
||||||
import "../Controls2/TextTypes"
|
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property bool enabled: true
|
|
||||||
property string placeholderText: ""
|
property string placeholderText: ""
|
||||||
property alias textField: searchField.textField
|
property alias textField: searchField.textField
|
||||||
|
|
||||||
@@ -17,57 +15,49 @@ Item {
|
|||||||
signal moreClicked()
|
signal moreClicked()
|
||||||
|
|
||||||
implicitWidth: 360
|
implicitWidth: 360
|
||||||
implicitHeight: 96
|
implicitHeight: content.implicitHeight + 32 + PageController.safeAreaBottomMargin
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: background
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
color: "#0E0F12"
|
|
||||||
|
color: AmneziaStyle.color.bgBase
|
||||||
opacity: 0.85
|
opacity: 0.85
|
||||||
z: -1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
id: addSiteButton
|
id: content
|
||||||
|
|
||||||
enabled: root.enabled
|
anchors.fill: parent
|
||||||
spacing: 2
|
anchors.topMargin: 16
|
||||||
|
anchors.leftMargin: 16
|
||||||
|
anchors.rightMargin: 16
|
||||||
|
anchors.bottomMargin: 16 + PageController.safeAreaBottomMargin
|
||||||
|
|
||||||
anchors {
|
spacing: 8
|
||||||
fill: parent
|
|
||||||
topMargin: 16
|
|
||||||
leftMargin: 16
|
|
||||||
rightMargin: 16
|
|
||||||
bottomMargin: 24
|
|
||||||
}
|
|
||||||
|
|
||||||
TextFieldWithHeaderType {
|
TextFieldType {
|
||||||
id: searchField
|
id: searchField
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
rightButtonClickedOnEnter: true
|
|
||||||
|
|
||||||
textField.placeholderText: root.placeholderText
|
rightButtonClickedOnEnter: true
|
||||||
|
placeholderText: root.placeholderText
|
||||||
buttonImageSource: "qrc:/images/controls/plus.svg"
|
buttonImageSource: "qrc:/images/controls/plus.svg"
|
||||||
|
buttonJoined: true
|
||||||
|
|
||||||
clickedFunc: function() {
|
clickedFunc: function() {
|
||||||
root.addClicked(textField.text)
|
root.addClicked(searchField.textField.text)
|
||||||
textField.text = ""
|
searchField.textField.text = ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageButtonType {
|
IconButtonType {
|
||||||
id: addSiteButtonImage
|
implicitWidth: 64
|
||||||
implicitWidth: 56
|
implicitHeight: 64
|
||||||
implicitHeight: 56
|
|
||||||
|
|
||||||
image: "qrc:/images/controls/more-vertical.svg"
|
icon.source: "qrc:/images/controls/more-vertical.svg"
|
||||||
imageColor: AmneziaStyle.color.paleGray
|
|
||||||
|
|
||||||
onClicked: root.moreClicked()
|
onClicked: root.moreClicked()
|
||||||
|
|
||||||
Keys.onReturnPressed: addSiteButtonImage.clicked()
|
|
||||||
Keys.onEnterPressed: addSiteButtonImage.clicked()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,7 +5,7 @@ import QtQuick.Layouts
|
|||||||
|
|
||||||
import "../Controls2"
|
import "../Controls2"
|
||||||
|
|
||||||
TextFieldWithHeaderType {
|
TextFieldType {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property var scroller: null
|
property var scroller: null
|
||||||
@@ -14,7 +14,6 @@ TextFieldWithHeaderType {
|
|||||||
signal edited(string text)
|
signal edited(string text)
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: 16
|
|
||||||
Layout.leftMargin: 16
|
Layout.leftMargin: 16
|
||||||
Layout.rightMargin: 16
|
Layout.rightMargin: 16
|
||||||
|
|
||||||
|
|||||||
@@ -1,64 +1,58 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
|
import QtQuick.Controls.impl
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
|
|
||||||
import Style 1.0
|
import Style 1.0
|
||||||
|
|
||||||
|
import "../Controls2"
|
||||||
import "../Controls2/TextTypes"
|
import "../Controls2/TextTypes"
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property string iconSource: ""
|
property string iconSource
|
||||||
property string titleText: ""
|
property string title
|
||||||
property string bodyText: ""
|
property string subtitle
|
||||||
property bool link: false
|
property bool isLink: false
|
||||||
|
|
||||||
readonly property string bodyLineText: root.link && root.bodyText.length > 0 ? "@" + root.bodyText : root.bodyText
|
spacing: 16
|
||||||
|
|
||||||
readonly property bool bodyClickable: root.link && root.bodyText.length > 0
|
IconImage {
|
||||||
|
|
||||||
spacing: 12
|
|
||||||
|
|
||||||
Image {
|
|
||||||
Layout.alignment: Qt.AlignTop
|
Layout.alignment: Qt.AlignTop
|
||||||
Layout.preferredWidth: 22
|
Layout.preferredWidth: 24
|
||||||
Layout.preferredHeight: 22
|
Layout.preferredHeight: 24
|
||||||
|
|
||||||
source: root.iconSource
|
source: root.iconSource
|
||||||
fillMode: Image.PreserveAspectFit
|
sourceSize: Qt.size(24, 24)
|
||||||
|
color: AmneziaStyle.color.iconDefault
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
spacing: 4
|
spacing: 4
|
||||||
|
|
||||||
LabelTextType {
|
H3TextType {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
text: root.titleText
|
|
||||||
color: AmneziaStyle.color.paleGray
|
text: root.title
|
||||||
font.pixelSize: 16
|
|
||||||
font.weight: Font.DemiBold
|
|
||||||
wrapMode: Text.Wrap
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
SmallTextType {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
implicitHeight: bodyLabel.implicitHeight
|
|
||||||
|
|
||||||
LabelTextType {
|
visible: !root.isLink && text !== ""
|
||||||
id: bodyLabel
|
text: root.subtitle
|
||||||
width: parent.width
|
}
|
||||||
text: root.bodyLineText
|
|
||||||
color: root.link ? AmneziaStyle.color.goldenApricot : AmneziaStyle.color.mutedGray
|
|
||||||
font.pixelSize: 14
|
|
||||||
wrapMode: Text.Wrap
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
LinkButtonType {
|
||||||
anchors.fill: bodyLabel
|
Layout.alignment: Qt.AlignLeft
|
||||||
visible: root.bodyClickable
|
Layout.maximumWidth: parent.width
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
onClicked: Qt.openUrlExternally("https://t.me/" + root.bodyText)
|
visible: root.isLink && root.subtitle !== ""
|
||||||
}
|
text: "@" + root.subtitle
|
||||||
|
|
||||||
|
onClicked: Qt.openUrlExternally("https://t.me/" + root.subtitle)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,39 +1,36 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
|
|
||||||
import "."
|
|
||||||
|
|
||||||
import Style 1.0
|
import Style 1.0
|
||||||
|
|
||||||
Rectangle {
|
Pane {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property var benefitsModel: null
|
property var benefitsModel: null
|
||||||
|
|
||||||
visible: benefitsModel && benefitsModel.rowCount() > 0
|
visible: root.benefitsModel && root.benefitsModel.rowCount() > 0
|
||||||
|
|
||||||
radius: 16
|
padding: 16
|
||||||
color: AmneziaStyle.color.benefitsPanelBackground
|
|
||||||
implicitHeight: inner.implicitHeight + 24
|
|
||||||
|
|
||||||
ColumnLayout {
|
background: Rectangle {
|
||||||
id: inner
|
radius: 16
|
||||||
|
color: AmneziaStyle.color.bgSurface
|
||||||
|
}
|
||||||
|
|
||||||
anchors.left: parent.left
|
contentItem: ColumnLayout {
|
||||||
anchors.right: parent.right
|
spacing: 16
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.margins: 12
|
|
||||||
spacing: 20
|
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
model: benefitsModel
|
model: root.benefitsModel
|
||||||
|
|
||||||
delegate: BenefitRow {
|
delegate: BenefitRow {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
iconSource: model.icon
|
iconSource: model.icon
|
||||||
titleText: model.title
|
title: model.title
|
||||||
bodyText: model.body
|
subtitle: model.body
|
||||||
link: !!model.link
|
isLink: !!model.link
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,94 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
import Style 1.0
|
||||||
|
|
||||||
|
import "../Controls2"
|
||||||
|
import "../Controls2/TextTypes"
|
||||||
|
|
||||||
|
SheetType {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property string headerText
|
||||||
|
property string contentText
|
||||||
|
|
||||||
|
readonly property alias textArea: contentArea
|
||||||
|
|
||||||
|
expandedHeight: windowHeight * 0.9
|
||||||
|
|
||||||
|
Item {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
IconButtonType {
|
||||||
|
id: backButton
|
||||||
|
|
||||||
|
x: 16
|
||||||
|
y: 8
|
||||||
|
|
||||||
|
size: IconButtonType.Small
|
||||||
|
icon.source: "qrc:/images/controls/arrow-left.svg"
|
||||||
|
|
||||||
|
onClicked: root.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
FlickableType {
|
||||||
|
id: flickable
|
||||||
|
|
||||||
|
anchors.top: backButton.bottom
|
||||||
|
anchors.topMargin: 4
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
|
||||||
|
contentHeight: content.implicitHeight
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: content
|
||||||
|
|
||||||
|
width: flickable.width
|
||||||
|
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
SectionHeaderType {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: 24
|
||||||
|
Layout.leftMargin: 16
|
||||||
|
Layout.rightMargin: 16
|
||||||
|
|
||||||
|
headerText: root.headerText
|
||||||
|
}
|
||||||
|
|
||||||
|
TextArea {
|
||||||
|
id: contentArea
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: 8
|
||||||
|
Layout.leftMargin: 16
|
||||||
|
Layout.rightMargin: 16
|
||||||
|
Layout.bottomMargin: 16 + PageController.safeAreaBottomMargin
|
||||||
|
|
||||||
|
padding: 0
|
||||||
|
leftPadding: 0
|
||||||
|
rightPadding: 0
|
||||||
|
leftInset: 0
|
||||||
|
rightInset: 0
|
||||||
|
readOnly: true
|
||||||
|
activeFocusOnTab: false
|
||||||
|
|
||||||
|
color: AmneziaStyle.color.textSecondary
|
||||||
|
selectionColor: AmneziaStyle.color.bgStateLayerAccent
|
||||||
|
selectedTextColor: AmneziaStyle.color.textSecondary
|
||||||
|
|
||||||
|
font.pixelSize: 16
|
||||||
|
font.weight: 400
|
||||||
|
font.family: "PT Root UI VF"
|
||||||
|
font.letterSpacing: -0.4
|
||||||
|
|
||||||
|
text: root.contentText
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
|
||||||
|
background: Item {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,50 +1,25 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Layouts
|
import QtQuick.Effects
|
||||||
import QtQuick.Shapes
|
import QtQuick.Shapes
|
||||||
import Qt5Compat.GraphicalEffects
|
|
||||||
|
|
||||||
import ConnectionState 1.0
|
|
||||||
import PageEnum 1.0
|
|
||||||
import Style 1.0
|
import Style 1.0
|
||||||
|
|
||||||
|
import "../Controls2"
|
||||||
|
import "../Controls2/TextTypes"
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property string defaultButtonColor: AmneziaStyle.color.paleGray
|
property bool connected: ConnectionController.isConnected
|
||||||
property string progressButtonColor: AmneziaStyle.color.paleGray
|
property bool inProgress: ConnectionController.isConnectionInProgress
|
||||||
property string connectedButtonColor: AmneziaStyle.color.goldenApricot
|
|
||||||
property bool buttonActiveFocus: activeFocus && (Qt.platform.os !== "android" || SettingsController.isOnTv())
|
|
||||||
|
|
||||||
property bool isFocusable: true
|
|
||||||
|
|
||||||
Keys.onTabPressed: {
|
|
||||||
FocusController.nextKeyTabItem()
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onBacktabPressed: {
|
|
||||||
FocusController.previousKeyTabItem()
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onUpPressed: {
|
|
||||||
FocusController.nextKeyUpItem()
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onDownPressed: {
|
|
||||||
FocusController.nextKeyDownItem()
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onLeftPressed: {
|
|
||||||
FocusController.nextKeyLeftItem()
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onRightPressed: {
|
|
||||||
FocusController.nextKeyRightItem()
|
|
||||||
}
|
|
||||||
|
|
||||||
implicitWidth: 190
|
implicitWidth: 190
|
||||||
implicitHeight: 190
|
implicitHeight: 190
|
||||||
|
|
||||||
|
padding: 0
|
||||||
|
hoverEnabled: true
|
||||||
|
|
||||||
text: ConnectionController.connectionStateText
|
text: ConnectionController.connectionStateText
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
@@ -55,136 +30,123 @@ Button {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// enabled: !ConnectionController.isConnectionInProgress
|
QtObject {
|
||||||
|
id: d
|
||||||
|
|
||||||
|
readonly property real ringRadius: (Math.min(root.width, root.height) - 2) / 2
|
||||||
|
readonly property color ringColor: root.connected && !root.inProgress ? AmneziaStyle.color.accentLight
|
||||||
|
: AmneziaStyle.color.borderEmphasis
|
||||||
|
readonly property int glowMargin: 24
|
||||||
|
}
|
||||||
|
|
||||||
background: Item {
|
background: Item {
|
||||||
implicitWidth: parent.width
|
implicitWidth: 190
|
||||||
implicitHeight: parent.height
|
implicitHeight: 190
|
||||||
transformOrigin: Item.Center
|
|
||||||
|
|
||||||
Shape {
|
Shape {
|
||||||
id: backgroundCircle
|
anchors.fill: parent
|
||||||
width: parent.implicitWidth
|
|
||||||
height: parent.implicitHeight
|
visible: root.inProgress
|
||||||
anchors.bottom: parent.bottom
|
opacity: 0.1
|
||||||
anchors.right: parent.right
|
preferredRendererType: Shape.CurveRenderer
|
||||||
layer.enabled: true
|
|
||||||
layer.samples: 4
|
|
||||||
layer.smooth: true
|
|
||||||
layer.effect: DropShadow {
|
|
||||||
anchors.fill: backgroundCircle
|
|
||||||
horizontalOffset: 0
|
|
||||||
verticalOffset: 0
|
|
||||||
radius: 10
|
|
||||||
samples: 25
|
|
||||||
color: root.buttonActiveFocus ? AmneziaStyle.color.paleGray : AmneziaStyle.color.goldenApricot
|
|
||||||
source: backgroundCircle
|
|
||||||
}
|
|
||||||
|
|
||||||
ShapePath {
|
ShapePath {
|
||||||
fillColor: AmneziaStyle.color.transparent
|
fillColor: AmneziaStyle.color.transparent
|
||||||
strokeColor: AmneziaStyle.color.paleGray
|
strokeColor: AmneziaStyle.color.borderEmphasis
|
||||||
strokeWidth: root.buttonActiveFocus ? 1 : 0
|
strokeWidth: 1
|
||||||
capStyle: ShapePath.RoundCap
|
|
||||||
|
|
||||||
PathAngleArc {
|
PathAngleArc {
|
||||||
centerX: backgroundCircle.width / 2
|
centerX: root.width / 2
|
||||||
centerY: backgroundCircle.height / 2
|
centerY: root.height / 2
|
||||||
radiusX: 94
|
radiusX: d.ringRadius
|
||||||
radiusY: 94
|
radiusY: d.ringRadius
|
||||||
startAngle: 0
|
startAngle: 0
|
||||||
sweepAngle: 360
|
sweepAngle: 360
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ShapePath {
|
|
||||||
fillColor: AmneziaStyle.color.transparent
|
|
||||||
strokeColor: {
|
|
||||||
if (ConnectionController.isConnectionInProgress) {
|
|
||||||
return AmneziaStyle.color.darkCharcoal
|
|
||||||
} else if (ConnectionController.isConnected) {
|
|
||||||
return connectedButtonColor
|
|
||||||
} else {
|
|
||||||
return defaultButtonColor
|
|
||||||
}
|
|
||||||
}
|
|
||||||
strokeWidth: root.buttonActiveFocus ? 2 : 3
|
|
||||||
capStyle: ShapePath.RoundCap
|
|
||||||
|
|
||||||
PathAngleArc {
|
|
||||||
centerX: backgroundCircle.width / 2
|
|
||||||
centerY: backgroundCircle.height / 2
|
|
||||||
radiusX: 93 - (root.buttonActiveFocus ? 2 : 0)
|
|
||||||
radiusY: 93 - (root.buttonActiveFocus ? 2 : 0)
|
|
||||||
startAngle: 0
|
|
||||||
sweepAngle: 360
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
enabled: false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Shape {
|
Item {
|
||||||
id: shape
|
anchors.fill: parent
|
||||||
width: parent.implicitWidth
|
|
||||||
height: parent.implicitHeight
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
anchors.right: parent.right
|
|
||||||
layer.enabled: true
|
|
||||||
layer.samples: 4
|
|
||||||
|
|
||||||
visible: ConnectionController.isConnectionInProgress
|
Item {
|
||||||
|
id: ringSource
|
||||||
|
|
||||||
ShapePath {
|
anchors.fill: parent
|
||||||
fillColor: AmneziaStyle.color.transparent
|
anchors.margins: -d.glowMargin
|
||||||
strokeColor: AmneziaStyle.color.paleGray
|
|
||||||
strokeWidth: 3
|
|
||||||
capStyle: ShapePath.RoundCap
|
|
||||||
|
|
||||||
PathAngleArc {
|
Shape {
|
||||||
centerX: shape.width / 2
|
anchors.fill: parent
|
||||||
centerY: shape.height / 2
|
|
||||||
radiusX: 93
|
preferredRendererType: Shape.CurveRenderer
|
||||||
radiusY: 93
|
|
||||||
startAngle: 245
|
ShapePath {
|
||||||
sweepAngle: -180
|
fillColor: AmneziaStyle.color.transparent
|
||||||
|
strokeColor: d.ringColor
|
||||||
|
strokeWidth: 2
|
||||||
|
capStyle: ShapePath.RoundCap
|
||||||
|
|
||||||
|
PathAngleArc {
|
||||||
|
centerX: ringSource.width / 2
|
||||||
|
centerY: ringSource.height / 2
|
||||||
|
radiusX: d.ringRadius
|
||||||
|
radiusY: d.ringRadius
|
||||||
|
startAngle: 100
|
||||||
|
sweepAngle: root.inProgress ? 160 : 360
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RotationAnimator {
|
MultiEffect {
|
||||||
target: shape
|
anchors.fill: ringSource
|
||||||
running: ConnectionController.isConnectionInProgress
|
|
||||||
|
source: ringSource
|
||||||
|
shadowEnabled: true
|
||||||
|
shadowColor: d.ringColor
|
||||||
|
shadowBlur: 0.6
|
||||||
|
shadowHorizontalOffset: 0
|
||||||
|
shadowVerticalOffset: 0
|
||||||
|
blurMax: 16
|
||||||
|
}
|
||||||
|
|
||||||
|
RotationAnimator on rotation {
|
||||||
|
running: root.inProgress
|
||||||
from: 0
|
from: 0
|
||||||
to: 360
|
to: 360
|
||||||
loops: Animation.Infinite
|
loops: Animation.Infinite
|
||||||
duration: 1000
|
duration: 1000
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FocusRingType {
|
||||||
|
radius: root.width / 2
|
||||||
|
visible: root.visualFocus
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
contentItem: Text {
|
contentItem: ButtonTextType {
|
||||||
height: 24
|
text: root.text
|
||||||
|
|
||||||
font.family: "PT Root UI VF"
|
color: root.connected && !root.inProgress ? AmneziaStyle.color.textAccent : AmneziaStyle.color.textPrimary
|
||||||
font.weight: 700
|
lineHeight: 30 + LanguageUiController.getLineHeightAppend()
|
||||||
font.pixelSize: 20
|
font.pixelSize: 20
|
||||||
|
|
||||||
color: ConnectionController.isConnected ? connectedButtonColor : defaultButtonColor
|
leftPadding: 16
|
||||||
text: root.text
|
rightPadding: 16
|
||||||
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HoverHandler {
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
}
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
ConnectionController.connectButtonClicked()
|
ConnectionController.connectButtonClicked()
|
||||||
}
|
}
|
||||||
|
|
||||||
Keys.onEnterPressed: this.clicked()
|
Keys.onEnterPressed: root.clicked()
|
||||||
Keys.onReturnPressed: this.clicked()
|
Keys.onReturnPressed: root.clicked()
|
||||||
}
|
}
|
||||||
@@ -8,13 +8,12 @@ import "../Controls2"
|
|||||||
import "../Controls2/TextTypes"
|
import "../Controls2/TextTypes"
|
||||||
import "../Config"
|
import "../Config"
|
||||||
|
|
||||||
DrawerType2 {
|
SheetType {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
width: parent.width
|
expandedHeight: content.implicitHeight + 16 + PageController.safeAreaBottomMargin
|
||||||
height: parent.height
|
|
||||||
|
|
||||||
expandedStateContent: ColumnLayout {
|
ColumnLayout {
|
||||||
id: content
|
id: content
|
||||||
|
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
@@ -22,13 +21,9 @@ DrawerType2 {
|
|||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
Component.onCompleted: {
|
SectionHeaderType {
|
||||||
root.expandedHeight = content.implicitHeight + 32
|
|
||||||
}
|
|
||||||
|
|
||||||
Header2Type {
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: 24
|
Layout.topMargin: 16
|
||||||
Layout.rightMargin: 16
|
Layout.rightMargin: 16
|
||||||
Layout.leftMargin: 16
|
Layout.leftMargin: 16
|
||||||
Layout.bottomMargin: 16
|
Layout.bottomMargin: 16
|
||||||
@@ -36,35 +31,34 @@ DrawerType2 {
|
|||||||
headerText: qsTr("Add new connection")
|
headerText: qsTr("Add new connection")
|
||||||
}
|
}
|
||||||
|
|
||||||
LabelWithButtonType {
|
ListItemType {
|
||||||
id: ip
|
id: ip
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: 16
|
Layout.leftMargin: 16
|
||||||
|
Layout.rightMargin: 16
|
||||||
|
|
||||||
text: qsTr("Configure your server")
|
text: qsTr("Configure your server")
|
||||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
trailingIconSource: "qrc:/images/controls/chevron-right.svg"
|
||||||
|
|
||||||
clickedFunction: function() {
|
onClicked: {
|
||||||
PageController.goToPage(PageEnum.PageSetupWizardCredentials)
|
PageController.goToPage(PageEnum.PageSetupWizardCredentials)
|
||||||
root.closeTriggered()
|
root.close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DividerType {}
|
ListItemType {
|
||||||
|
|
||||||
LabelWithButtonType {
|
|
||||||
id: qrCode
|
id: qrCode
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
Layout.leftMargin: 16
|
||||||
|
Layout.rightMargin: 16
|
||||||
|
|
||||||
text: qsTr("Open config file, key or QR code")
|
text: qsTr("Open config file, key or QR code")
|
||||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
trailingIconSource: "qrc:/images/controls/chevron-right.svg"
|
||||||
|
|
||||||
clickedFunction: function() {
|
onClicked: {
|
||||||
PageController.goToPage(PageEnum.PageSetupWizardConfigSource)
|
PageController.goToPage(PageEnum.PageSetupWizardConfigSource)
|
||||||
root.closeTriggered()
|
root.close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DividerType {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
import Style 1.0
|
||||||
|
|
||||||
|
import "../Controls2"
|
||||||
|
import "../Controls2/TextTypes"
|
||||||
|
import "../Config"
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property string label
|
||||||
|
property string value
|
||||||
|
property string copiedText
|
||||||
|
property bool showSeparator: true
|
||||||
|
|
||||||
|
implicitWidth: content.implicitWidth + 28
|
||||||
|
implicitHeight: Math.max(72, content.implicitHeight + 24)
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: content
|
||||||
|
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.leftMargin: 16
|
||||||
|
anchors.rightMargin: 12
|
||||||
|
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
spacing: 4
|
||||||
|
|
||||||
|
CaptionTextType {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
text: root.label
|
||||||
|
}
|
||||||
|
|
||||||
|
BodyTextType {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
text: root.value
|
||||||
|
color: AmneziaStyle.color.textPrimary
|
||||||
|
wrapMode: Text.WrapAnywhere
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IconButtonType {
|
||||||
|
icon.source: "qrc:/images/controls/copy.svg"
|
||||||
|
Accessible.name: root.label
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
GC.copyToClipBoard(root.value)
|
||||||
|
if (root.copiedText !== "") {
|
||||||
|
PageController.showNotificationMessage(root.copiedText)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
|
||||||
|
height: 1
|
||||||
|
visible: root.showSeparator
|
||||||
|
color: AmneziaStyle.color.borderSoft
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,110 +1,32 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Layouts
|
|
||||||
import Qt5Compat.GraphicalEffects
|
|
||||||
|
|
||||||
import Style 1.0
|
import Style 1.0
|
||||||
|
|
||||||
import "../Controls2/TextTypes"
|
import "../Controls2"
|
||||||
|
|
||||||
Item {
|
EmptyStateType {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property bool isSearchResult: true
|
property bool isSearchResult: true
|
||||||
|
|
||||||
property string categoryName: ""
|
property string categoryName: ""
|
||||||
|
|
||||||
signal showAllRequested()
|
signal showAllRequested()
|
||||||
|
|
||||||
ColumnLayout {
|
iconColor: AmneziaStyle.color.accentLight
|
||||||
anchors.centerIn: parent
|
|
||||||
width: Math.min(parent.width - 32, 320)
|
|
||||||
|
|
||||||
spacing: 16
|
title: {
|
||||||
|
if (root.categoryName !== "") {
|
||||||
Item {
|
return qsTr("Nothing found in \"%1\"").arg(root.categoryName)
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
Layout.preferredWidth: 48
|
|
||||||
Layout.preferredHeight: 48
|
|
||||||
|
|
||||||
Image {
|
|
||||||
id: searchIcon
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
source: "qrc:/images/controls/search.svg"
|
|
||||||
visible: false
|
|
||||||
}
|
|
||||||
|
|
||||||
ColorOverlay {
|
|
||||||
anchors.fill: searchIcon
|
|
||||||
source: searchIcon
|
|
||||||
color: AmneziaStyle.color.goldenApricot
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return root.isSearchResult ? qsTr("Nothing found. Check the spelling or change your query")
|
||||||
|
: qsTr("No locations available yet")
|
||||||
|
}
|
||||||
|
|
||||||
ParagraphTextType {
|
ButtonType {
|
||||||
Layout.fillWidth: true
|
visible: root.isSearchResult || root.categoryName !== ""
|
||||||
|
variant: ButtonType.Text
|
||||||
|
text: qsTr("Show all")
|
||||||
|
|
||||||
color: AmneziaStyle.color.textPrimary
|
onClicked: root.showAllRequested()
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
|
|
||||||
text: {
|
|
||||||
if (root.categoryName !== "") {
|
|
||||||
return qsTr("Nothing found in \"%1\"").arg(root.categoryName)
|
|
||||||
}
|
|
||||||
return root.isSearchResult ? qsTr("Nothing found. Check the spelling or change your query")
|
|
||||||
: qsTr("No locations available yet")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: showAll
|
|
||||||
|
|
||||||
property bool isFocusable: root.isSearchResult || root.categoryName !== ""
|
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
Layout.preferredWidth: showAllText.implicitWidth + 16
|
|
||||||
Layout.preferredHeight: showAllText.implicitHeight + 16
|
|
||||||
|
|
||||||
visible: root.isSearchResult || root.categoryName !== ""
|
|
||||||
|
|
||||||
Keys.onEnterPressed: root.showAllRequested()
|
|
||||||
Keys.onReturnPressed: root.showAllRequested()
|
|
||||||
Keys.onSpacePressed: root.showAllRequested()
|
|
||||||
|
|
||||||
Keys.onTabPressed: {
|
|
||||||
FocusController.nextKeyTabItem()
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onBacktabPressed: {
|
|
||||||
FocusController.previousKeyTabItem()
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
color: AmneziaStyle.color.transparent
|
|
||||||
radius: 8
|
|
||||||
|
|
||||||
border.width: showAll.activeFocus ? 1 : 0
|
|
||||||
border.color: AmneziaStyle.color.paleGray
|
|
||||||
}
|
|
||||||
|
|
||||||
ParagraphTextType {
|
|
||||||
id: showAllText
|
|
||||||
|
|
||||||
anchors.centerIn: parent
|
|
||||||
|
|
||||||
color: AmneziaStyle.color.goldenApricot
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
|
|
||||||
text: qsTr("Show all")
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
onClicked: root.showAllRequested()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import QtQuick
|
||||||
|
|
||||||
|
import Style 1.0
|
||||||
|
|
||||||
|
import "../Controls2"
|
||||||
|
|
||||||
|
ListItemType {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property bool favorite: false
|
||||||
|
property string countryFlagCode
|
||||||
|
|
||||||
|
signal favoriteToggled()
|
||||||
|
|
||||||
|
leadingSlotWidth: 24
|
||||||
|
icon.source: root.countryFlagCode !== "" ? "qrc:/countriesFlags/images/flagKit/" + root.countryFlagCode + ".svg" : ""
|
||||||
|
icon.width: 21
|
||||||
|
icon.height: 15
|
||||||
|
icon.color: AmneziaStyle.color.transparent
|
||||||
|
|
||||||
|
FavoriteButton {
|
||||||
|
favorite: root.favorite
|
||||||
|
|
||||||
|
onClicked: root.favoriteToggled()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,107 +0,0 @@
|
|||||||
import QtQuick
|
|
||||||
import QtQuick.Controls
|
|
||||||
import QtQuick.Layouts
|
|
||||||
|
|
||||||
import Style 1.0
|
|
||||||
|
|
||||||
import "../Controls2"
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property alias textField: input
|
|
||||||
property alias text: input.text
|
|
||||||
readonly property alias displayText: input.displayText
|
|
||||||
|
|
||||||
signal cleared()
|
|
||||||
|
|
||||||
implicitHeight: 64
|
|
||||||
|
|
||||||
color: AmneziaStyle.color.surfaceBase
|
|
||||||
radius: 16
|
|
||||||
border.width: 1
|
|
||||||
border.color: input.activeFocus ? AmneziaStyle.color.paleGray : AmneziaStyle.color.borderSoft
|
|
||||||
|
|
||||||
Behavior on border.color {
|
|
||||||
PropertyAnimation { duration: 200 }
|
|
||||||
}
|
|
||||||
|
|
||||||
function clear() {
|
|
||||||
input.text = ""
|
|
||||||
root.cleared()
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.leftMargin: 16
|
|
||||||
anchors.rightMargin: 8
|
|
||||||
spacing: 8
|
|
||||||
|
|
||||||
TextField {
|
|
||||||
id: input
|
|
||||||
|
|
||||||
property bool isFocusable: true
|
|
||||||
|
|
||||||
Keys.onTabPressed: {
|
|
||||||
FocusController.nextKeyTabItem()
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onBacktabPressed: {
|
|
||||||
FocusController.previousKeyTabItem()
|
|
||||||
}
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
color: AmneziaStyle.color.textPrimary
|
|
||||||
placeholderText: qsTr("Country or region")
|
|
||||||
placeholderTextColor: AmneziaStyle.color.mutedGray
|
|
||||||
|
|
||||||
selectionColor: AmneziaStyle.color.richBrown
|
|
||||||
selectedTextColor: AmneziaStyle.color.textPrimary
|
|
||||||
|
|
||||||
font.pixelSize: 16
|
|
||||||
font.weight: 400
|
|
||||||
font.family: "PT Root UI VF"
|
|
||||||
|
|
||||||
inputMethodHints: Qt.ImhNoAutoUppercase | Qt.ImhNoPredictiveText
|
|
||||||
|
|
||||||
maximumLength: 64
|
|
||||||
|
|
||||||
topPadding: 0
|
|
||||||
bottomPadding: 0
|
|
||||||
leftPadding: 0
|
|
||||||
rightPadding: 0
|
|
||||||
|
|
||||||
background: Rectangle {
|
|
||||||
color: AmneziaStyle.color.transparent
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onEscapePressed: function(event) {
|
|
||||||
if (input.text === "") {
|
|
||||||
event.accepted = false
|
|
||||||
return
|
|
||||||
}
|
|
||||||
root.clear()
|
|
||||||
}
|
|
||||||
|
|
||||||
ContextMenu.menu: ContextMenuType {
|
|
||||||
textObj: input
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ImageButtonType {
|
|
||||||
visible: input.text !== ""
|
|
||||||
|
|
||||||
implicitWidth: 40
|
|
||||||
implicitHeight: 40
|
|
||||||
|
|
||||||
hoverEnabled: true
|
|
||||||
image: "qrc:/images/controls/close.svg"
|
|
||||||
imageColor: AmneziaStyle.color.paleGray
|
|
||||||
|
|
||||||
onClicked: root.clear()
|
|
||||||
Keys.onEnterPressed: root.clear()
|
|
||||||
Keys.onReturnPressed: root.clear()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +1,6 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
|
|
||||||
import Style 1.0
|
|
||||||
|
|
||||||
import "../Controls2"
|
import "../Controls2"
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
@@ -17,37 +15,35 @@ RowLayout {
|
|||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.leftMargin: 16
|
Layout.leftMargin: 16
|
||||||
Layout.rightMargin: 16
|
Layout.rightMargin: 16
|
||||||
Layout.topMargin: 12
|
Layout.topMargin: 16
|
||||||
|
|
||||||
spacing: 8
|
spacing: 8
|
||||||
|
|
||||||
CountrySearchField {
|
SearchFieldType {
|
||||||
id: searchField
|
id: searchField
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
placeholderText: qsTr("Country or region")
|
||||||
|
maximumLength: 64
|
||||||
|
|
||||||
onTextChanged: {
|
onTextChanged: {
|
||||||
const current = searchField.text
|
const current = searchField.text
|
||||||
const clean = root.listModel.sanitizeSearchFieldText(current)
|
const clean = root.listModel.sanitizeSearchFieldText(current)
|
||||||
if (clean !== current) {
|
if (clean !== current) {
|
||||||
const cursor = searchField.textField.cursorPosition - (current.length - clean.length)
|
const cursor = searchField.cursorPosition - (current.length - clean.length)
|
||||||
searchField.text = clean
|
searchField.text = clean
|
||||||
searchField.textField.cursorPosition = Math.max(0, Math.min(cursor, clean.length))
|
searchField.cursorPosition = Math.max(0, Math.min(cursor, clean.length))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onDisplayTextChanged: root.listModel.searchText = root.listModel.sanitizeSearchFieldText(searchField.displayText)
|
onDisplayTextChanged: root.listModel.searchText = root.listModel.sanitizeSearchFieldText(searchField.displayText)
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageButtonType {
|
IconButtonType {
|
||||||
objectName: "sortButton"
|
objectName: "sortButton"
|
||||||
|
|
||||||
implicitWidth: 64
|
icon.source: "qrc:/images/controls/sort-desc.svg"
|
||||||
implicitHeight: 64
|
|
||||||
|
|
||||||
hoverEnabled: true
|
|
||||||
image: "qrc:/images/controls/sort-desc.svg"
|
|
||||||
imageColor: AmneziaStyle.color.paleGray
|
|
||||||
|
|
||||||
onClicked: root.sortRequested()
|
onClicked: root.sortRequested()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import Qt5Compat.GraphicalEffects
|
import QtQuick.Controls.impl
|
||||||
|
|
||||||
import Style 1.0
|
import Style 1.0
|
||||||
|
|
||||||
import "../Config"
|
import "../Config"
|
||||||
|
import "../Controls2"
|
||||||
import "../Controls2/TextTypes"
|
import "../Controls2/TextTypes"
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
@@ -12,7 +13,7 @@ Item {
|
|||||||
|
|
||||||
property var listModel: null
|
property var listModel: null
|
||||||
|
|
||||||
property bool isFocusable: root.isRealSection && !root.isPinnedOverlay
|
activeFocusOnTab: root.isRealSection && !root.isPinnedOverlay
|
||||||
|
|
||||||
property string sectionKey: ""
|
property string sectionKey: ""
|
||||||
property bool isPinnedOverlay: false
|
property bool isPinnedOverlay: false
|
||||||
@@ -47,8 +48,16 @@ Item {
|
|||||||
readonly property string subsubregionId: root.isRealSection ? root.listModel.sectionSubsubregionId(root.sectionKey) : ""
|
readonly property string subsubregionId: root.isRealSection ? root.listModel.sectionSubsubregionId(root.sectionKey) : ""
|
||||||
|
|
||||||
readonly property int level: root.subsubregionId !== "" ? 3 : (root.subregionId !== "" ? 2 : 1)
|
readonly property int level: root.subsubregionId !== "" ? 3 : (root.subregionId !== "" ? 2 : 1)
|
||||||
readonly property color titleColor: root.level === 1 ? AmneziaStyle.color.textPrimary
|
readonly property color chevronColor: root.level === 1 ? AmneziaStyle.color.iconDefault
|
||||||
: AmneziaStyle.color.textTertiary
|
: AmneziaStyle.color.iconSecondary
|
||||||
|
|
||||||
|
readonly property string displayTitle: {
|
||||||
|
if (!root.isRealSection) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
root.listModel.layoutRevision
|
||||||
|
return "%1 · %2".arg(root.title).arg(root.listModel.sectionCount(root.sectionKey))
|
||||||
|
}
|
||||||
|
|
||||||
readonly property bool collapsed: {
|
readonly property bool collapsed: {
|
||||||
if (!root.isRealSection) {
|
if (!root.isRealSection) {
|
||||||
@@ -70,7 +79,7 @@ Item {
|
|||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
color: root.isPinnedOverlay ? AmneziaStyle.color.midnightBlack
|
color: root.isPinnedOverlay ? AmneziaStyle.color.bgBase
|
||||||
: AmneziaStyle.color.transparent
|
: AmneziaStyle.color.transparent
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,30 +91,23 @@ Item {
|
|||||||
anchors.bottomMargin: root.contentBottomMargin
|
anchors.bottomMargin: root.contentBottomMargin
|
||||||
spacing: 8
|
spacing: 8
|
||||||
|
|
||||||
ParagraphTextType {
|
BodyTextType {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
color: root.titleColor
|
visible: root.level === 1
|
||||||
lineHeight: (root.level === 1 ? 24 : 18) + LanguageUiController.getLineHeightAppend()
|
color: AmneziaStyle.color.textPrimary
|
||||||
font.pixelSize: root.level === 1 ? 16 : 14
|
font.weight: 600
|
||||||
font.weight: root.level === 1 ? 700 : 400
|
text: root.displayTitle
|
||||||
font.letterSpacing: root.level === 1 ? -0.4 : 0
|
|
||||||
horizontalAlignment: Text.AlignLeft
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
|
|
||||||
text: {
|
|
||||||
if (!root.isRealSection) {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
root.listModel.layoutRevision
|
|
||||||
if (!root.collapsed) {
|
|
||||||
return root.title
|
|
||||||
}
|
|
||||||
return "%1 (%2)".arg(root.title).arg(root.listModel.sectionCount(root.sectionKey))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Image {
|
SmallTextType {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
visible: root.level !== 1
|
||||||
|
text: root.displayTitle
|
||||||
|
}
|
||||||
|
|
||||||
|
IconImage {
|
||||||
Layout.preferredWidth: 24
|
Layout.preferredWidth: 24
|
||||||
Layout.preferredHeight: 24
|
Layout.preferredHeight: 24
|
||||||
Layout.leftMargin: 10
|
Layout.leftMargin: 10
|
||||||
@@ -113,13 +115,8 @@ Item {
|
|||||||
visible: root.canToggle
|
visible: root.canToggle
|
||||||
source: root.collapsed ? "qrc:/images/controls/chevron-down.svg"
|
source: root.collapsed ? "qrc:/images/controls/chevron-down.svg"
|
||||||
: "qrc:/images/controls/chevron-up.svg"
|
: "qrc:/images/controls/chevron-up.svg"
|
||||||
|
sourceSize: Qt.size(24, 24)
|
||||||
layer {
|
color: root.chevronColor
|
||||||
enabled: true
|
|
||||||
effect: ColorOverlay {
|
|
||||||
color: root.titleColor
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,22 +141,9 @@ Item {
|
|||||||
Keys.onReturnPressed: root.toggle()
|
Keys.onReturnPressed: root.toggle()
|
||||||
Keys.onSpacePressed: root.toggle()
|
Keys.onSpacePressed: root.toggle()
|
||||||
|
|
||||||
Keys.onTabPressed: {
|
FocusRingType {
|
||||||
FocusController.nextKeyTabItem()
|
anchors.margins: 0
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onBacktabPressed: {
|
|
||||||
FocusController.previousKeyTabItem()
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.margins: 2
|
|
||||||
|
|
||||||
color: AmneziaStyle.color.transparent
|
|
||||||
radius: 8
|
radius: 8
|
||||||
|
visible: root.activeFocus
|
||||||
border.width: root.activeFocus ? 1 : 0
|
|
||||||
border.color: AmneziaStyle.color.paleGray
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,7 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
|
|
||||||
import Style 1.0
|
|
||||||
|
|
||||||
import "../Config"
|
import "../Config"
|
||||||
import "../Controls2"
|
import "../Controls2"
|
||||||
import "../Controls2/TextTypes"
|
|
||||||
|
|
||||||
ListViewType {
|
ListViewType {
|
||||||
id: root
|
id: root
|
||||||
@@ -14,10 +11,10 @@ ListViewType {
|
|||||||
readonly property var chips: root.listModel !== null ? root.listModel.useCases : []
|
readonly property var chips: root.listModel !== null ? root.listModel.useCases : []
|
||||||
|
|
||||||
visible: root.chips.length > 1
|
visible: root.chips.length > 1
|
||||||
isFocusable: root.visible
|
|
||||||
|
|
||||||
implicitHeight: root.visible ? 36 : 0
|
implicitHeight: root.visible ? 34 : 0
|
||||||
|
|
||||||
|
clip: false
|
||||||
orientation: ListView.Horizontal
|
orientation: ListView.Horizontal
|
||||||
spacing: 8
|
spacing: 8
|
||||||
leftMargin: 16
|
leftMargin: 16
|
||||||
@@ -27,74 +24,18 @@ ListViewType {
|
|||||||
|
|
||||||
model: root.chips
|
model: root.chips
|
||||||
|
|
||||||
delegate: Item {
|
delegate: ChipType {
|
||||||
id: delegateRoot
|
|
||||||
|
|
||||||
required property var modelData
|
required property var modelData
|
||||||
|
|
||||||
implicitWidth: chip.implicitWidth
|
readonly property string useCaseId: modelData.useCaseId
|
||||||
implicitHeight: 36
|
|
||||||
|
|
||||||
Rectangle {
|
checked: root.listModel !== null && root.listModel.activeUseCaseId === useCaseId
|
||||||
id: chip
|
text: "%1 · %2".arg(CountryRegionNames.useCaseName(root.listModel, useCaseId)).arg(modelData.count)
|
||||||
|
|
||||||
readonly property string useCaseId: delegateRoot.modelData.useCaseId
|
onClicked: {
|
||||||
readonly property bool active: root.listModel !== null
|
if (root.listModel !== null) {
|
||||||
&& root.listModel.activeUseCaseId === chip.useCaseId
|
root.listModel.activeUseCaseId = useCaseId
|
||||||
|
|
||||||
property bool isFocusable: true
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
implicitWidth: label.implicitWidth + 32
|
|
||||||
radius: height / 2
|
|
||||||
|
|
||||||
color: chipHover.hovered ? AmneziaStyle.color.surfaceHovered
|
|
||||||
: AmneziaStyle.color.surfaceBase
|
|
||||||
border.width: chip.activeFocus ? 2 : (chip.active ? 1 : 0)
|
|
||||||
border.color: chip.active ? AmneziaStyle.color.goldenApricot
|
|
||||||
: AmneziaStyle.color.paleGray
|
|
||||||
|
|
||||||
function select() {
|
|
||||||
if (root.listModel !== null) {
|
|
||||||
root.listModel.activeUseCaseId = chip.useCaseId
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ParagraphTextType {
|
|
||||||
id: label
|
|
||||||
|
|
||||||
anchors.centerIn: parent
|
|
||||||
|
|
||||||
color: chip.active ? AmneziaStyle.color.textPrimary : AmneziaStyle.color.paleGray
|
|
||||||
wrapMode: Text.NoWrap
|
|
||||||
lineHeightMode: Text.ProportionalHeight
|
|
||||||
lineHeight: 1.0
|
|
||||||
font.letterSpacing: -0.4
|
|
||||||
|
|
||||||
text: "%1 · %2".arg(CountryRegionNames.useCaseName(root.listModel, chip.useCaseId))
|
|
||||||
.arg(delegateRoot.modelData.count)
|
|
||||||
}
|
|
||||||
|
|
||||||
HoverHandler {
|
|
||||||
id: chipHover
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
}
|
|
||||||
|
|
||||||
TapHandler {
|
|
||||||
gesturePolicy: TapHandler.ReleaseWithinBounds
|
|
||||||
onTapped: chip.select()
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onEnterPressed: chip.select()
|
|
||||||
Keys.onReturnPressed: chip.select()
|
|
||||||
Keys.onSpacePressed: chip.select()
|
|
||||||
|
|
||||||
Keys.onTabPressed: FocusController.nextKeyTabItem()
|
|
||||||
Keys.onBacktabPressed: FocusController.previousKeyTabItem()
|
|
||||||
Keys.onLeftPressed: FocusController.nextKeyLeftItem()
|
|
||||||
Keys.onRightPressed: FocusController.nextKeyRightItem()
|
|
||||||
Keys.onUpPressed: FocusController.nextKeyUpItem()
|
|
||||||
Keys.onDownPressed: FocusController.nextKeyDownItem()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import QtQuick
|
||||||
|
|
||||||
|
import "../Controls2"
|
||||||
|
|
||||||
|
IconButtonType {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property bool favorite: false
|
||||||
|
|
||||||
|
icon.source: root.favorite ? "qrc:/images/controls/star-filled.svg" : "qrc:/images/controls/star.svg"
|
||||||
|
|
||||||
|
Accessible.name: root.favorite ? qsTr("Remove from favorites") : qsTr("Add to favorites")
|
||||||
|
}
|
||||||
@@ -1,98 +1,55 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Layouts
|
|
||||||
|
|
||||||
import SortFilterProxyModel 0.2
|
|
||||||
|
|
||||||
import PageEnum 1.0
|
import PageEnum 1.0
|
||||||
|
|
||||||
import "../Controls2"
|
import "../Controls2"
|
||||||
import "../Controls2/TextTypes"
|
|
||||||
|
|
||||||
ListViewType {
|
ListViewType {
|
||||||
id: menuContent
|
id: root
|
||||||
|
|
||||||
property var rootWidth
|
signal activated()
|
||||||
property var selectedText
|
|
||||||
|
|
||||||
width: rootWidth
|
leftMargin: 16
|
||||||
anchors.top: parent.top
|
rightMargin: 16
|
||||||
anchors.bottom: parent.bottom
|
bottomMargin: 16
|
||||||
|
|
||||||
ButtonGroup {
|
ButtonGroup {
|
||||||
id: containersRadioButtonGroup
|
id: containersRadioButtonGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
delegate: Item {
|
delegate: RadioItemType {
|
||||||
implicitWidth: rootWidth
|
width: root.width - root.leftMargin - root.rightMargin
|
||||||
implicitHeight: content.implicitHeight
|
|
||||||
|
|
||||||
ColumnLayout {
|
text: name
|
||||||
id: content
|
subtitle: description
|
||||||
|
showSeparator: index < root.count - 1
|
||||||
|
|
||||||
anchors.fill: parent
|
ButtonGroup.group: containersRadioButtonGroup
|
||||||
anchors.rightMargin: 16
|
|
||||||
anchors.leftMargin: 16
|
|
||||||
|
|
||||||
VerticalRadioButton {
|
icon.source: isInstalled ? "" : "qrc:/images/controls/download.svg"
|
||||||
id: containerRadioButton
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
checkable: isInstalled && !ConnectionController.isConnected
|
||||||
|
checked: root.model.mapToSource(index) === ServersUiController.serverDefaultContainer(ServersUiController.defaultServerId)
|
||||||
|
|
||||||
text: name
|
onClicked: {
|
||||||
descriptionText: description
|
if (ConnectionController.isConnected && isInstalled) {
|
||||||
|
PageController.showNotificationMessage(qsTr("Unable change protocol while there is an active connection"))
|
||||||
ButtonGroup.group: containersRadioButtonGroup
|
return
|
||||||
|
|
||||||
imageSource: "qrc:/images/controls/download.svg"
|
|
||||||
showImage: !isInstalled
|
|
||||||
|
|
||||||
checkable: isInstalled && !ConnectionController.isConnected
|
|
||||||
checked: proxyDefaultServerContainersModel.mapToSource(index) === ServersUiController.serverDefaultContainer(ServersUiController.defaultServerId)
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
if (ConnectionController.isConnected && isInstalled) {
|
|
||||||
PageController.showNotificationMessage(qsTr("Unable change protocol while there is an active connection"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var containerIndex = proxyDefaultServerContainersModel.mapToSource(index)
|
|
||||||
|
|
||||||
if (!isInstalled) {
|
|
||||||
ServersUiController.setProcessedServerId(ServersUiController.defaultServerId)
|
|
||||||
ServersUiController.processedContainerIndex = containerIndex
|
|
||||||
PageController.goToPage(PageEnum.PageSetupWizardProtocolSettings)
|
|
||||||
containersDropDown.closeTriggered()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
containersDropDown.closeTriggered()
|
|
||||||
ServersUiController.setDefaultContainer(ServersUiController.defaultServerId, containerIndex)
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: containerRadioButton
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
enabled: false
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onEnterPressed: {
|
|
||||||
if (checkable) {
|
|
||||||
checked = true
|
|
||||||
}
|
|
||||||
containerRadioButton.clicked()
|
|
||||||
}
|
|
||||||
Keys.onReturnPressed: {
|
|
||||||
if (checkable) {
|
|
||||||
checked = true
|
|
||||||
}
|
|
||||||
containerRadioButton.clicked()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DividerType {
|
var containerIndex = root.model.mapToSource(index)
|
||||||
Layout.fillWidth: true
|
|
||||||
|
if (!isInstalled) {
|
||||||
|
ServersUiController.setProcessedServerId(ServersUiController.defaultServerId)
|
||||||
|
ServersUiController.processedContainerIndex = containerIndex
|
||||||
|
PageController.goToPage(PageEnum.PageSetupWizardProtocolSettings)
|
||||||
|
root.activated()
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
root.activated()
|
||||||
|
ServersUiController.setDefaultContainer(ServersUiController.defaultServerId, containerIndex)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Controls.impl
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
import Style 1.0
|
||||||
|
|
||||||
|
import "../Controls2"
|
||||||
|
import "../Controls2/TextTypes"
|
||||||
|
|
||||||
|
AbstractButton {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property string leftImageSource
|
||||||
|
property color leftImageColor: AmneziaStyle.color.iconDefault
|
||||||
|
property int leftImageWidth: 24
|
||||||
|
property int leftImageHeight: 24
|
||||||
|
property string rightImageSource
|
||||||
|
property bool largeText: false
|
||||||
|
property int maximumLineCount: 1
|
||||||
|
|
||||||
|
implicitWidth: implicitContentWidth + leftPadding + rightPadding
|
||||||
|
implicitHeight: Math.max(40, implicitContentHeight + topPadding + bottomPadding)
|
||||||
|
|
||||||
|
horizontalPadding: 16
|
||||||
|
verticalPadding: 8
|
||||||
|
|
||||||
|
hoverEnabled: enabled
|
||||||
|
focusPolicy: enabled ? Qt.TabFocus : Qt.NoFocus
|
||||||
|
|
||||||
|
Accessible.name: root.text
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
radius: 16
|
||||||
|
color: root.enabled && (root.hovered || root.down) ? AmneziaStyle.color.bgStateLayer : AmneziaStyle.color.transparent
|
||||||
|
border.width: root.enabled && root.down ? 1 : 0
|
||||||
|
border.color: AmneziaStyle.color.borderEmphasis
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
ColorAnimation { duration: 150 }
|
||||||
|
}
|
||||||
|
|
||||||
|
FocusRingType {
|
||||||
|
radius: 16
|
||||||
|
visible: root.visualFocus
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
contentItem: Item {
|
||||||
|
implicitWidth: row.implicitWidth
|
||||||
|
implicitHeight: row.implicitHeight
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: row
|
||||||
|
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: Math.min(implicitWidth, parent.width)
|
||||||
|
|
||||||
|
spacing: 8
|
||||||
|
|
||||||
|
Image {
|
||||||
|
Layout.preferredWidth: root.leftImageWidth
|
||||||
|
Layout.preferredHeight: root.leftImageHeight
|
||||||
|
|
||||||
|
visible: root.leftImageSource !== "" && root.leftImageColor.a === 0
|
||||||
|
source: visible ? root.leftImageSource : ""
|
||||||
|
sourceSize: Qt.size(root.leftImageWidth, root.leftImageHeight)
|
||||||
|
fillMode: Image.PreserveAspectFit
|
||||||
|
}
|
||||||
|
|
||||||
|
IconImage {
|
||||||
|
Layout.preferredWidth: root.leftImageWidth
|
||||||
|
Layout.preferredHeight: root.leftImageHeight
|
||||||
|
|
||||||
|
visible: root.leftImageSource !== "" && root.leftImageColor.a > 0
|
||||||
|
source: visible ? root.leftImageSource : ""
|
||||||
|
sourceSize: Qt.size(root.leftImageWidth, root.leftImageHeight)
|
||||||
|
color: root.leftImageColor
|
||||||
|
}
|
||||||
|
|
||||||
|
BodyTextType {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
visible: root.largeText
|
||||||
|
text: root.text
|
||||||
|
color: AmneziaStyle.color.textTertiary
|
||||||
|
maximumLineCount: root.maximumLineCount
|
||||||
|
elide: Text.ElideRight
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
SmallTextType {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
visible: !root.largeText
|
||||||
|
text: root.text
|
||||||
|
maximumLineCount: root.maximumLineCount
|
||||||
|
elide: Text.ElideRight
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
IconImage {
|
||||||
|
Layout.preferredWidth: 16
|
||||||
|
Layout.preferredHeight: 16
|
||||||
|
|
||||||
|
visible: root.rightImageSource !== ""
|
||||||
|
source: root.rightImageSource
|
||||||
|
sourceSize: Qt.size(16, 16)
|
||||||
|
color: AmneziaStyle.color.iconDefault
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HoverHandler {
|
||||||
|
cursorShape: root.enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.onEnterPressed: root.clicked()
|
||||||
|
Keys.onReturnPressed: root.clicked()
|
||||||
|
}
|
||||||
@@ -0,0 +1,125 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
import Style 1.0
|
||||||
|
|
||||||
|
import "../Controls2"
|
||||||
|
import "../Controls2/TextTypes"
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property bool expanded: false
|
||||||
|
property bool pressed: false
|
||||||
|
property bool outdatedWarningVisible: false
|
||||||
|
property bool protocolSelectionVisible: false
|
||||||
|
property bool protocolSelectable: false
|
||||||
|
property string protocolText
|
||||||
|
|
||||||
|
signal expandRequested()
|
||||||
|
signal serverInfoRequested()
|
||||||
|
signal protocolSelectionRequested()
|
||||||
|
|
||||||
|
objectName: "collapsedColumnLayout"
|
||||||
|
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
objectName: "rowLayout"
|
||||||
|
|
||||||
|
Layout.topMargin: 16
|
||||||
|
Layout.leftMargin: 16
|
||||||
|
Layout.rightMargin: 16
|
||||||
|
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
|
||||||
|
|
||||||
|
spacing: 10
|
||||||
|
|
||||||
|
H1TextType {
|
||||||
|
objectName: "collapsedButtonHeader"
|
||||||
|
|
||||||
|
Layout.maximumWidth: root.width - 32 - (chevron.visible ? chevron.width + 10 : 0)
|
||||||
|
|
||||||
|
maximumLineCount: 1
|
||||||
|
wrapMode: Text.NoWrap
|
||||||
|
elide: Qt.ElideRight
|
||||||
|
|
||||||
|
text: ServersUiController.defaultServerName
|
||||||
|
horizontalAlignment: Qt.AlignHCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
IconButtonType {
|
||||||
|
id: chevron
|
||||||
|
objectName: "collapsedButtonChevron"
|
||||||
|
|
||||||
|
visible: !root.expanded
|
||||||
|
|
||||||
|
hoverEnabled: false
|
||||||
|
down: pressed || root.pressed
|
||||||
|
size: IconButtonType.Small
|
||||||
|
icon.source: "qrc:/images/controls/chevron-down.svg"
|
||||||
|
|
||||||
|
onClicked: root.expandRequested()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HomeInfoButton {
|
||||||
|
objectName: "serverDescriptionButton"
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.maximumWidth: implicitWidth
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
Layout.topMargin: 6
|
||||||
|
Layout.leftMargin: 16
|
||||||
|
Layout.rightMargin: 16
|
||||||
|
Layout.bottomMargin: (root.outdatedWarningVisible || root.protocolSelectionVisible) ? 8 : 16
|
||||||
|
|
||||||
|
enabled: ServersUiController.defaultServerImagePathCollapsed !== ""
|
||||||
|
|
||||||
|
largeText: true
|
||||||
|
maximumLineCount: 2
|
||||||
|
text: ServersUiController.defaultServerDescriptionCollapsed
|
||||||
|
leftImageSource: ServersUiController.defaultServerImagePathCollapsed
|
||||||
|
leftImageColor: AmneziaStyle.color.transparent
|
||||||
|
leftImageWidth: 20
|
||||||
|
leftImageHeight: 15
|
||||||
|
rightImageSource: enabled ? (root.expanded ? "qrc:/images/controls/chevron-down.svg" : "qrc:/images/controls/chevron-right.svg") : ""
|
||||||
|
|
||||||
|
onClicked: root.serverInfoRequested()
|
||||||
|
}
|
||||||
|
|
||||||
|
NotificationType {
|
||||||
|
objectName: "outdatedContainerWarning"
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.leftMargin: 16
|
||||||
|
Layout.rightMargin: 16
|
||||||
|
Layout.bottomMargin: 16
|
||||||
|
|
||||||
|
visible: root.outdatedWarningVisible
|
||||||
|
|
||||||
|
variant: NotificationType.Warning
|
||||||
|
text: qsTr("AmneziaWG 2.0 is outdated and no longer supported. Continued use requires a fresh installation of the AmneziaWG 3.1 container.")
|
||||||
|
}
|
||||||
|
|
||||||
|
HomeInfoButton {
|
||||||
|
objectName: "protocolButton"
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.maximumWidth: implicitWidth
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
Layout.leftMargin: 16
|
||||||
|
Layout.rightMargin: 16
|
||||||
|
Layout.bottomMargin: 16
|
||||||
|
|
||||||
|
visible: root.protocolSelectionVisible
|
||||||
|
enabled: root.protocolSelectable
|
||||||
|
|
||||||
|
largeText: true
|
||||||
|
text: root.protocolText
|
||||||
|
leftImageSource: "qrc:/images/controls/arrow-left-right.svg"
|
||||||
|
leftImageColor: AmneziaStyle.color.iconSecondary
|
||||||
|
rightImageSource: enabled ? "qrc:/images/controls/chevron-down.svg" : ""
|
||||||
|
|
||||||
|
onClicked: root.protocolSelectionRequested()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,15 +8,14 @@ import "../Controls2"
|
|||||||
import "../Controls2/TextTypes"
|
import "../Controls2/TextTypes"
|
||||||
import "../Config"
|
import "../Config"
|
||||||
|
|
||||||
DrawerType2 {
|
SheetType {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property bool isAppSplitTinnelingEnabled: Qt.platform.os === "windows" || Qt.platform.os === "android"
|
property bool isAppSplitTinnelingEnabled: Qt.platform.os === "windows" || Qt.platform.os === "android"
|
||||||
|
|
||||||
anchors.fill: parent
|
expandedHeight: Math.min(content.implicitHeight + 24 + PageController.safeAreaBottomMargin, windowHeight * 0.9)
|
||||||
expandedHeight: parent.height * 0.9
|
|
||||||
|
|
||||||
expandedStateContent: ColumnLayout {
|
ColumnLayout {
|
||||||
id: content
|
id: content
|
||||||
|
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
@@ -24,74 +23,67 @@ DrawerType2 {
|
|||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
Header2Type {
|
SectionHeaderType {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: 24
|
Layout.topMargin: 16
|
||||||
Layout.rightMargin: 16
|
Layout.rightMargin: 16
|
||||||
Layout.leftMargin: 16
|
Layout.leftMargin: 16
|
||||||
Layout.bottomMargin: 16
|
Layout.bottomMargin: 32
|
||||||
|
|
||||||
headerText: qsTr("Split tunneling")
|
headerText: qsTr("Split tunneling")
|
||||||
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")
|
||||||
}
|
}
|
||||||
|
|
||||||
LabelWithButtonType {
|
ListItemType {
|
||||||
id: splitTunnelingSwitch
|
id: splitTunnelingSwitch
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: 16
|
Layout.leftMargin: 16
|
||||||
|
Layout.rightMargin: 16
|
||||||
|
|
||||||
visible: ServersUiController.isDefaultServerDefaultContainerHasSplitTunneling
|
visible: ServersUiController.isDefaultServerDefaultContainerHasSplitTunneling
|
||||||
|
|
||||||
text: qsTr("Split tunneling on the server")
|
text: qsTr("Split tunneling on the server")
|
||||||
descriptionText: qsTr("Enabled \nCan't be disabled for current server")
|
subtitle: qsTr("Enabled \nCan't be disabled for current server")
|
||||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
trailingIconSource: "qrc:/images/controls/chevron-right.svg"
|
||||||
|
|
||||||
clickedFunction: function() {
|
onClicked: {
|
||||||
PageController.goToPage(PageEnum.PageSettingsSplitTunneling)
|
PageController.goToPage(PageEnum.PageSettingsSplitTunneling)
|
||||||
root.closeTriggered()
|
root.close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DividerType {
|
ListItemType {
|
||||||
visible: ServersUiController.isDefaultServerDefaultContainerHasSplitTunneling
|
|
||||||
}
|
|
||||||
|
|
||||||
LabelWithButtonType {
|
|
||||||
id: siteBasedSplitTunnelingSwitch
|
id: siteBasedSplitTunnelingSwitch
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: 16
|
Layout.leftMargin: 16
|
||||||
|
Layout.rightMargin: 16
|
||||||
|
|
||||||
text: qsTr("Site-based split tunneling")
|
text: qsTr("Site-based split tunneling")
|
||||||
descriptionText: enabled && IpSplitTunnelingController.isSplitTunnelingEnabled ? qsTr("Enabled") : qsTr("Disabled")
|
subtitle: enabled && IpSplitTunnelingController.isSplitTunnelingEnabled ? qsTr("Enabled") : qsTr("Disabled")
|
||||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
trailingIconSource: "qrc:/images/controls/chevron-right.svg"
|
||||||
|
|
||||||
clickedFunction: function() {
|
onClicked: {
|
||||||
PageController.goToPage(PageEnum.PageSettingsSplitTunneling)
|
PageController.goToPage(PageEnum.PageSettingsSplitTunneling)
|
||||||
root.closeTriggered()
|
root.close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DividerType {
|
ListItemType {
|
||||||
}
|
|
||||||
|
|
||||||
LabelWithButtonType {
|
|
||||||
id: appSplitTunnelingSwitch
|
id: appSplitTunnelingSwitch
|
||||||
visible: isAppSplitTinnelingEnabled
|
visible: isAppSplitTinnelingEnabled
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
Layout.leftMargin: 16
|
||||||
|
Layout.rightMargin: 16
|
||||||
|
|
||||||
text: qsTr("App-based split tunneling")
|
text: qsTr("App-based split tunneling")
|
||||||
descriptionText: AppSplitTunnelingController.isSplitTunnelingEnabled ? qsTr("Enabled") : qsTr("Disabled")
|
subtitle: AppSplitTunnelingController.isSplitTunnelingEnabled ? qsTr("Enabled") : qsTr("Disabled")
|
||||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
trailingIconSource: "qrc:/images/controls/chevron-right.svg"
|
||||||
|
|
||||||
clickedFunction: function() {
|
onClicked: {
|
||||||
PageController.goToPage(PageEnum.PageSettingsAppSplitTunneling)
|
PageController.goToPage(PageEnum.PageSettingsAppSplitTunneling)
|
||||||
root.closeTriggered()
|
root.close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DividerType {
|
|
||||||
visible: isAppSplitTinnelingEnabled
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -10,11 +10,10 @@ import SortFilterProxyModel 0.2
|
|||||||
import InstalledAppsModel 1.0
|
import InstalledAppsModel 1.0
|
||||||
import Style 1.0
|
import Style 1.0
|
||||||
|
|
||||||
DrawerType2 {
|
SheetType {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
anchors.fill: parent
|
expandedHeight: windowHeight * 0.9
|
||||||
expandedHeight: parent.height * 0.9
|
|
||||||
|
|
||||||
onAboutToShow: {
|
onAboutToShow: {
|
||||||
PageController.showBusyIndicator(true)
|
PageController.showBusyIndicator(true)
|
||||||
@@ -26,121 +25,128 @@ DrawerType2 {
|
|||||||
id: installedAppsModel
|
id: installedAppsModel
|
||||||
}
|
}
|
||||||
|
|
||||||
expandedStateContent: Item {
|
Item {
|
||||||
id: container
|
id: container
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
implicitHeight: expandedHeight
|
SectionHeaderType {
|
||||||
|
id: header
|
||||||
ColumnLayout {
|
|
||||||
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: 12
|
||||||
|
anchors.leftMargin: 16
|
||||||
|
anchors.rightMargin: 16
|
||||||
|
|
||||||
|
headerText: qsTr("Choose application")
|
||||||
|
}
|
||||||
|
|
||||||
|
ListViewType {
|
||||||
|
id: listView
|
||||||
|
|
||||||
|
anchors.top: header.bottom
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
anchors.bottom: searchField.top
|
anchors.bottom: searchField.top
|
||||||
anchors.topMargin: 16
|
anchors.topMargin: 16
|
||||||
|
anchors.bottomMargin: 8
|
||||||
|
|
||||||
BackButtonType {
|
leftMargin: 16
|
||||||
backButtonImage: "qrc:/images/controls/arrow-left.svg"
|
rightMargin: 16
|
||||||
backButtonFunction: function() {
|
topMargin: 16
|
||||||
root.closeTriggered()
|
|
||||||
|
model: SortFilterProxyModel {
|
||||||
|
id: proxyInstalledAppsModel
|
||||||
|
sourceModel: installedAppsModel
|
||||||
|
filters: RegExpFilter {
|
||||||
|
roleName: "appName"
|
||||||
|
pattern: ".*" + searchField.text + ".*"
|
||||||
|
caseSensitivity: Qt.CaseInsensitive
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Header2Type {
|
delegate: Item {
|
||||||
id: header
|
width: listView.width - listView.leftMargin - listView.rightMargin
|
||||||
Layout.fillWidth: true
|
implicitHeight: Math.max(72, row.implicitHeight + 24)
|
||||||
Layout.topMargin: 16
|
|
||||||
Layout.rightMargin: 16
|
|
||||||
Layout.leftMargin: 16
|
|
||||||
|
|
||||||
headerText: qsTr("Choose application")
|
RowLayout {
|
||||||
}
|
id: row
|
||||||
|
|
||||||
ListViewType {
|
anchors.fill: parent
|
||||||
id: listView
|
anchors.leftMargin: 16
|
||||||
|
anchors.rightMargin: 12
|
||||||
|
|
||||||
Layout.fillWidth: true
|
spacing: 12
|
||||||
Layout.fillHeight: true
|
|
||||||
Layout.topMargin: 16
|
|
||||||
Layout.rightMargin: 16
|
|
||||||
Layout.leftMargin: 16
|
|
||||||
|
|
||||||
model: SortFilterProxyModel {
|
CheckBoxType {
|
||||||
id: proxyInstalledAppsModel
|
Layout.fillWidth: true
|
||||||
sourceModel: installedAppsModel
|
|
||||||
filters: RegExpFilter {
|
|
||||||
roleName: "appName"
|
|
||||||
pattern: ".*" + searchField.textField.text + ".*"
|
|
||||||
caseSensitivity: Qt.CaseInsensitive
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ButtonGroup {
|
spacing: 20
|
||||||
id: buttonGroup
|
|
||||||
}
|
|
||||||
|
|
||||||
delegate: ColumnLayout {
|
text: appName
|
||||||
width: listView.width
|
checked: isAppSelected
|
||||||
|
onCheckedChanged: {
|
||||||
RowLayout {
|
installedAppsModel.selectedStateChanged(proxyInstalledAppsModel.mapToSource(index), checked)
|
||||||
CheckBoxType {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
text: appName
|
|
||||||
checked: isAppSelected
|
|
||||||
onCheckedChanged: {
|
|
||||||
installedAppsModel.selectedStateChanged(proxyInstalledAppsModel.mapToSource(index), checked)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Layout.preferredWidth: 48
|
||||||
|
Layout.preferredHeight: 48
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
|
||||||
source: "image://installedAppImage/" + appIcon
|
source: "image://installedAppImage/" + appIcon
|
||||||
|
|
||||||
sourceSize.width: 24
|
sourceSize.width: 24
|
||||||
sourceSize.height: 24
|
sourceSize.height: 24
|
||||||
|
|
||||||
Layout.rightMargin: 48
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
DividerType {}
|
Rectangle {
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
|
||||||
|
height: 1
|
||||||
|
color: AmneziaStyle.color.borderSoft
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TextFieldWithHeaderType {
|
SearchFieldType {
|
||||||
id: searchField
|
id: searchField
|
||||||
|
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.bottom: addButton.top
|
anchors.bottom: addButton.top
|
||||||
anchors.bottomMargin: 16
|
anchors.bottomMargin: 12
|
||||||
anchors.rightMargin: 16
|
anchors.rightMargin: 16
|
||||||
anchors.leftMargin: 16
|
anchors.leftMargin: 16
|
||||||
|
|
||||||
backgroundColor: AmneziaStyle.color.slateGray
|
placeholderText: qsTr("application name")
|
||||||
|
|
||||||
textField.placeholderText: qsTr("application name")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BasicButtonType {
|
ButtonType {
|
||||||
id: addButton
|
id: addButton
|
||||||
|
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
anchors.bottomMargin: 16
|
anchors.bottomMargin: Math.max(16, PageController.safeAreaBottomMargin)
|
||||||
anchors.rightMargin: 16
|
anchors.rightMargin: 16
|
||||||
anchors.leftMargin: 16
|
anchors.leftMargin: 16
|
||||||
|
|
||||||
text: qsTr("Add selected")
|
text: qsTr("Add selected")
|
||||||
|
|
||||||
clickedFunc: function() {
|
onClicked: {
|
||||||
PageController.showBusyIndicator(true)
|
PageController.showBusyIndicator(true)
|
||||||
AppSplitTunnelingController.addApps(installedAppsModel.getSelectedAppsInfo())
|
AppSplitTunnelingController.addApps(installedAppsModel.getSelectedAppsInfo())
|
||||||
PageController.showBusyIndicator(false)
|
PageController.showBusyIndicator(false)
|
||||||
root.closeTriggered()
|
root.close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,121 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Controls.impl
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
import Style 1.0
|
||||||
|
|
||||||
|
import "../Controls2"
|
||||||
|
import "../Controls2/TextTypes"
|
||||||
|
|
||||||
|
ItemDelegate {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property string subtitle
|
||||||
|
property bool critical: false
|
||||||
|
|
||||||
|
implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
|
||||||
|
implicitContentWidth + leftPadding + rightPadding)
|
||||||
|
implicitHeight: implicitContentHeight + topPadding + bottomPadding
|
||||||
|
|
||||||
|
padding: 16
|
||||||
|
spacing: 12
|
||||||
|
|
||||||
|
hoverEnabled: true
|
||||||
|
focusPolicy: Qt.TabFocus
|
||||||
|
opacity: enabled ? 1.0 : 0.4
|
||||||
|
|
||||||
|
icon.width: 26
|
||||||
|
icon.height: 26
|
||||||
|
|
||||||
|
Accessible.name: root.text
|
||||||
|
Accessible.description: root.subtitle
|
||||||
|
|
||||||
|
Keys.onEnterPressed: root.click()
|
||||||
|
Keys.onReturnPressed: root.click()
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
implicitWidth: 358
|
||||||
|
|
||||||
|
radius: 16
|
||||||
|
color: {
|
||||||
|
if (!root.enabled) {
|
||||||
|
return AmneziaStyle.color.bgSurface
|
||||||
|
}
|
||||||
|
if (root.down || root.hovered) {
|
||||||
|
return AmneziaStyle.color.bgStateLayer
|
||||||
|
}
|
||||||
|
return root.critical ? AmneziaStyle.color.promoOverlay : AmneziaStyle.color.bgBase
|
||||||
|
}
|
||||||
|
border.width: 1
|
||||||
|
border.color: {
|
||||||
|
if (root.enabled && root.down) {
|
||||||
|
return AmneziaStyle.color.borderMedium
|
||||||
|
}
|
||||||
|
return root.critical ? AmneziaStyle.color.promoRecommendation : AmneziaStyle.color.borderSoft
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
ColorAnimation { duration: 150 }
|
||||||
|
}
|
||||||
|
|
||||||
|
FocusRingType {
|
||||||
|
radius: 16
|
||||||
|
visible: root.visualFocus
|
||||||
|
}
|
||||||
|
|
||||||
|
HoverHandler {
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
enabled: root.enabled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
contentItem: RowLayout {
|
||||||
|
spacing: root.spacing
|
||||||
|
|
||||||
|
Image {
|
||||||
|
Layout.preferredWidth: root.icon.width
|
||||||
|
Layout.preferredHeight: root.icon.height
|
||||||
|
|
||||||
|
visible: root.icon.source.toString() !== ""
|
||||||
|
source: root.icon.source
|
||||||
|
sourceSize: Qt.size(root.icon.width, root.icon.height)
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
spacing: 4
|
||||||
|
|
||||||
|
H3TextType {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
text: root.text
|
||||||
|
textFormat: Text.RichText
|
||||||
|
}
|
||||||
|
|
||||||
|
SmallTextType {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
visible: text !== ""
|
||||||
|
text: root.subtitle
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Layout.preferredWidth: 36
|
||||||
|
Layout.preferredHeight: 36
|
||||||
|
Layout.leftMargin: 4 - root.spacing
|
||||||
|
|
||||||
|
IconImage {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: 20
|
||||||
|
height: 20
|
||||||
|
|
||||||
|
source: "qrc:/images/controls/chevron-right.svg"
|
||||||
|
sourceSize: Qt.size(20, 20)
|
||||||
|
color: AmneziaStyle.color.iconDefault
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property alias source: image.source
|
||||||
|
|
||||||
|
implicitWidth: 358
|
||||||
|
implicitHeight: root.width
|
||||||
|
|
||||||
|
Layout.preferredHeight: root.width
|
||||||
|
|
||||||
|
color: "white"
|
||||||
|
radius: 16
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: image
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: 12
|
||||||
|
|
||||||
|
smooth: false
|
||||||
|
fillMode: Image.PreserveAspectFit
|
||||||
|
sourceSize.width: width
|
||||||
|
sourceSize.height: height
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,7 +11,7 @@ import "../Controls2/TextTypes"
|
|||||||
|
|
||||||
import "../Config"
|
import "../Config"
|
||||||
|
|
||||||
DrawerType2 {
|
SheetType {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property string headerText
|
property string headerText
|
||||||
@@ -22,71 +22,55 @@ DrawerType2 {
|
|||||||
property var yesButtonFunction
|
property var yesButtonFunction
|
||||||
property var noButtonFunction
|
property var noButtonFunction
|
||||||
|
|
||||||
expandedStateContent: ColumnLayout {
|
expandedHeight: content.implicitHeight + 8 + PageController.safeAreaBottomMargin
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
id: content
|
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
|
||||||
|
|
||||||
spacing: 8
|
spacing: 12
|
||||||
|
|
||||||
onImplicitHeightChanged: {
|
SectionHeaderType {
|
||||||
root.expandedHeight = content.implicitHeight + 32 + PageController.safeAreaBottomMargin
|
|
||||||
}
|
|
||||||
|
|
||||||
Header2TextType {
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: 16
|
Layout.topMargin: 16
|
||||||
|
Layout.bottomMargin: 14
|
||||||
Layout.rightMargin: 16
|
Layout.rightMargin: 16
|
||||||
Layout.leftMargin: 16
|
Layout.leftMargin: 16
|
||||||
|
|
||||||
text: root.headerText
|
headerText: root.headerText
|
||||||
|
descriptionText: root.descriptionText
|
||||||
}
|
}
|
||||||
|
|
||||||
ParagraphTextType {
|
ButtonType {
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.topMargin: 8
|
|
||||||
Layout.rightMargin: 16
|
|
||||||
Layout.leftMargin: 16
|
|
||||||
|
|
||||||
text: root.descriptionText
|
|
||||||
}
|
|
||||||
|
|
||||||
BasicButtonType {
|
|
||||||
id: yesButton
|
id: yesButton
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: 16
|
|
||||||
Layout.rightMargin: 16
|
Layout.rightMargin: 16
|
||||||
Layout.leftMargin: 16
|
Layout.leftMargin: 16
|
||||||
|
|
||||||
text: root.yesButtonText
|
text: root.yesButtonText
|
||||||
|
|
||||||
clickedFunc: function() {
|
onClicked: {
|
||||||
if (root.yesButtonFunction && typeof root.yesButtonFunction === "function") {
|
if (root.yesButtonFunction && typeof root.yesButtonFunction === "function") {
|
||||||
root.yesButtonFunction()
|
root.yesButtonFunction()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BasicButtonType {
|
ButtonType {
|
||||||
id: noButton
|
id: noButton
|
||||||
|
variant: ButtonType.Secondary
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.rightMargin: 16
|
Layout.rightMargin: 16
|
||||||
Layout.leftMargin: 16
|
Layout.leftMargin: 16
|
||||||
|
|
||||||
defaultColor: AmneziaStyle.color.transparent
|
|
||||||
hoveredColor: AmneziaStyle.color.translucentWhite
|
|
||||||
pressedColor: AmneziaStyle.color.sheerWhite
|
|
||||||
disabledColor: AmneziaStyle.color.mutedGray
|
|
||||||
textColor: AmneziaStyle.color.paleGray
|
|
||||||
borderWidth: 1
|
|
||||||
|
|
||||||
visible: root.noButtonText !== ""
|
visible: root.noButtonText !== ""
|
||||||
|
|
||||||
text: root.noButtonText
|
text: root.noButtonText
|
||||||
|
|
||||||
clickedFunc: function() {
|
onClicked: {
|
||||||
if (root.noButtonFunction && typeof root.noButtonFunction === "function") {
|
if (root.noButtonFunction && typeof root.noButtonFunction === "function") {
|
||||||
root.noButtonFunction()
|
root.noButtonFunction()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,21 +9,27 @@ import "../Controls2/TextTypes"
|
|||||||
|
|
||||||
import "../Config"
|
import "../Config"
|
||||||
|
|
||||||
DrawerType2 {
|
SheetType {
|
||||||
property string serverNameText
|
property string serverNameText
|
||||||
|
|
||||||
id: root
|
id: root
|
||||||
objectName: "serverNameEditDrawer"
|
objectName: "serverNameEditDrawer"
|
||||||
|
|
||||||
expandedStateContent: ColumnLayout {
|
expandedHeight: content.implicitHeight + 16 + 8 + PageController.safeAreaBottomMargin + PageController.imeHeight
|
||||||
|
|
||||||
|
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: 32
|
anchors.topMargin: 16
|
||||||
anchors.leftMargin: 16
|
anchors.leftMargin: 16
|
||||||
anchors.rightMargin: 16
|
anchors.rightMargin: 16
|
||||||
|
|
||||||
TextFieldWithHeaderType {
|
spacing: 26
|
||||||
|
|
||||||
|
TextFieldType {
|
||||||
id: serverName
|
id: serverName
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
@@ -33,14 +39,14 @@ DrawerType2 {
|
|||||||
checkEmptyText: true
|
checkEmptyText: true
|
||||||
}
|
}
|
||||||
|
|
||||||
BasicButtonType {
|
ButtonType {
|
||||||
id: saveButton
|
id: saveButton
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
text: qsTr("Save")
|
text: qsTr("Save")
|
||||||
|
|
||||||
clickedFunc: function() {
|
onClicked: {
|
||||||
if (serverName.textField.text === "") {
|
if (serverName.textField.text === "") {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -48,7 +54,7 @@ DrawerType2 {
|
|||||||
if (serverName.textField.text !== root.serverNameText) {
|
if (serverName.textField.text !== root.serverNameText) {
|
||||||
ServersUiController.editServerName(ServersUiController.processedServerId, serverName.textField.text);
|
ServersUiController.editServerName(ServersUiController.processedServerId, serverName.textField.text);
|
||||||
}
|
}
|
||||||
root.closeTriggered()
|
root.close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,42 +8,52 @@ import "../Controls2"
|
|||||||
import "../Controls2/TextTypes"
|
import "../Controls2/TextTypes"
|
||||||
import "../Config"
|
import "../Config"
|
||||||
|
|
||||||
DrawerType2 {
|
SheetType {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
expandedStateContent: Item {
|
expandedHeight: windowHeight * 0.9
|
||||||
|
|
||||||
|
Item {
|
||||||
id: container
|
id: container
|
||||||
|
|
||||||
implicitHeight: root.height * 0.9
|
anchors.fill: parent
|
||||||
|
|
||||||
Component.onCompleted: {
|
|
||||||
root.expandedHeight = container.implicitHeight
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: backButtonLayout
|
id: headerLayout
|
||||||
|
|
||||||
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: 16
|
|
||||||
|
|
||||||
BackButtonType {
|
spacing: 0
|
||||||
id: backButton
|
|
||||||
|
|
||||||
|
Item {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: 2
|
||||||
|
Layout.preferredHeight: 48
|
||||||
|
|
||||||
backButtonImage: "qrc:/images/controls/arrow-left.svg"
|
IconButtonType {
|
||||||
backButtonFunction: function() { root.closeTriggered() }
|
id: backButton
|
||||||
|
|
||||||
|
x: 16
|
||||||
|
y: 4
|
||||||
|
|
||||||
|
size: IconButtonType.Small
|
||||||
|
icon.source: "qrc:/images/controls/arrow-left.svg"
|
||||||
|
Accessible.name: qsTr("Back")
|
||||||
|
|
||||||
|
onClicked: root.close()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Header2Type {
|
SectionHeaderType {
|
||||||
id: header
|
id: header
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: 16
|
Layout.topMargin: 16
|
||||||
Layout.rightMargin: 16
|
|
||||||
Layout.leftMargin: 16
|
Layout.leftMargin: 16
|
||||||
|
Layout.rightMargin: 16
|
||||||
|
Layout.bottomMargin: 32
|
||||||
|
|
||||||
headerText: qsTr("Choose language")
|
headerText: qsTr("Choose language")
|
||||||
}
|
}
|
||||||
@@ -52,7 +62,7 @@ DrawerType2 {
|
|||||||
ListViewType {
|
ListViewType {
|
||||||
id: listView
|
id: listView
|
||||||
|
|
||||||
anchors.top: backButtonLayout.bottom
|
anchors.top: headerLayout.bottom
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
@@ -61,111 +71,37 @@ DrawerType2 {
|
|||||||
|
|
||||||
model: LanguageModel
|
model: LanguageModel
|
||||||
|
|
||||||
|
footer: Item {
|
||||||
|
width: listView.width
|
||||||
|
height: 16 + PageController.safeAreaBottomMargin
|
||||||
|
}
|
||||||
|
|
||||||
ButtonGroup {
|
ButtonGroup {
|
||||||
id: buttonGroup
|
id: buttonGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
delegate: Item {
|
delegate: Item {
|
||||||
implicitWidth: root.width
|
width: listView.width
|
||||||
implicitHeight: delegateContent.implicitHeight
|
implicitHeight: languageItem.implicitHeight
|
||||||
|
|
||||||
ColumnLayout {
|
RadioItemType {
|
||||||
id: delegateContent
|
id: languageItem
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
anchors.leftMargin: 16
|
||||||
|
anchors.rightMargin: 16
|
||||||
|
|
||||||
RadioButton {
|
text: languageName
|
||||||
id: radioButton
|
|
||||||
|
|
||||||
implicitWidth: parent.width
|
ButtonGroup.group: buttonGroup
|
||||||
implicitHeight: radioButtonContent.implicitHeight
|
checked: listView.selectedIndex === index
|
||||||
|
|
||||||
hoverEnabled: true
|
onClicked: {
|
||||||
|
listView.selectedIndex = index
|
||||||
property bool isFocusable: true
|
LanguageUiController.changeLanguage(languageIndex)
|
||||||
|
root.close()
|
||||||
Keys.onTabPressed: {
|
|
||||||
FocusController.nextKeyTabItem()
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onBacktabPressed: {
|
|
||||||
FocusController.previousKeyTabItem()
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onUpPressed: {
|
|
||||||
FocusController.nextKeyUpItem()
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onDownPressed: {
|
|
||||||
FocusController.nextKeyDownItem()
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onLeftPressed: {
|
|
||||||
FocusController.nextKeyLeftItem()
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onRightPressed: {
|
|
||||||
FocusController.nextKeyRightItem()
|
|
||||||
}
|
|
||||||
|
|
||||||
indicator: Rectangle {
|
|
||||||
width: parent.width - 1
|
|
||||||
height: parent.height
|
|
||||||
color: radioButton.hovered ? AmneziaStyle.color.slateGray : AmneziaStyle.color.onyxBlack
|
|
||||||
border.color: radioButton.focus ? AmneziaStyle.color.paleGray : AmneziaStyle.color.transparent
|
|
||||||
border.width: radioButton.focus ? 1 : 0
|
|
||||||
|
|
||||||
Behavior on color {
|
|
||||||
PropertyAnimation { duration: 200 }
|
|
||||||
}
|
|
||||||
Behavior on border.color {
|
|
||||||
PropertyAnimation { duration: 200 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
id: radioButtonContent
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
anchors.rightMargin: 16
|
|
||||||
anchors.leftMargin: 16
|
|
||||||
|
|
||||||
spacing: 0
|
|
||||||
|
|
||||||
z: 1
|
|
||||||
|
|
||||||
ParagraphTextType {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.topMargin: 20
|
|
||||||
Layout.bottomMargin: 20
|
|
||||||
|
|
||||||
text: languageName
|
|
||||||
}
|
|
||||||
|
|
||||||
Image {
|
|
||||||
source: "qrc:/images/controls/check.svg"
|
|
||||||
visible: radioButton.checked
|
|
||||||
|
|
||||||
width: 24
|
|
||||||
height: 24
|
|
||||||
|
|
||||||
Layout.rightMargin: 8
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ButtonGroup.group: buttonGroup
|
|
||||||
checked: listView.selectedIndex === index
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
listView.selectedIndex = index
|
|
||||||
LanguageUiController.changeLanguage(languageIndex)
|
|
||||||
root.closeTriggered()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Keys.onEnterPressed: radioButton.clicked()
|
|
||||||
Keys.onReturnPressed: radioButton.clicked()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Controls.impl
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
import Style 1.0
|
||||||
|
|
||||||
|
import "../Controls2"
|
||||||
|
|
||||||
|
RadioItemType {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property bool showWarning: false
|
||||||
|
|
||||||
|
signal settingsRequested()
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Layout.preferredWidth: 48
|
||||||
|
Layout.preferredHeight: 48
|
||||||
|
|
||||||
|
visible: root.showWarning
|
||||||
|
|
||||||
|
IconImage {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: 24
|
||||||
|
height: 24
|
||||||
|
|
||||||
|
source: "qrc:/images/controls/alert-circle.svg"
|
||||||
|
sourceSize: Qt.size(24, 24)
|
||||||
|
color: AmneziaStyle.color.statusWarning
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IconButtonType {
|
||||||
|
icon.source: "qrc:/images/controls/settings.svg"
|
||||||
|
Accessible.name: qsTr("Server settings")
|
||||||
|
|
||||||
|
onClicked: root.settingsRequested()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,29 +1,21 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Layouts
|
|
||||||
|
|
||||||
import SortFilterProxyModel 0.2
|
|
||||||
|
|
||||||
import PageEnum 1.0
|
import PageEnum 1.0
|
||||||
import ContainerProps 1.0
|
|
||||||
import ContainersModelFilters 1.0
|
|
||||||
import Style 1.0
|
import Style 1.0
|
||||||
|
|
||||||
import "./"
|
|
||||||
import "../Controls2"
|
import "../Controls2"
|
||||||
import "../Controls2/TextTypes"
|
|
||||||
import "../Config"
|
|
||||||
|
|
||||||
ListViewType {
|
ListViewType {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property int selectedIndex: ServersUiController.getServerIndexById(ServersUiController.defaultServerId)
|
property int selectedIndex: ServersUiController.getServerIndexById(ServersUiController.defaultServerId)
|
||||||
|
|
||||||
anchors.top: serversMenuHeader.bottom
|
signal serverSettingsOpened()
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.left: parent.left
|
leftMargin: 16
|
||||||
anchors.bottom: parent.bottom
|
rightMargin: 16
|
||||||
anchors.topMargin: 16
|
bottomMargin: 16
|
||||||
|
|
||||||
model: ServersModel
|
model: ServersModel
|
||||||
|
|
||||||
@@ -34,121 +26,67 @@ ListViewType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
delegate: Item {
|
ButtonGroup {
|
||||||
id: menuContentDelegate
|
id: serversRadioButtonGroup
|
||||||
objectName: "menuContentDelegate"
|
objectName: "serversRadioButtonGroup"
|
||||||
|
}
|
||||||
|
|
||||||
property variant delegateData: model
|
function openServerSettings(serverId) {
|
||||||
property VerticalRadioButton serverRadioButtonProperty: serverRadioButton
|
ServersUiController.setProcessedServerId(serverId)
|
||||||
|
|
||||||
implicitWidth: root.width
|
if (ServersUiController.isServerFromApi(ServersUiController.processedServerId)) {
|
||||||
implicitHeight: serverRadioButtonContent.implicitHeight
|
if (ServersUiController.isServerCountrySelectionAvailable(ServersUiController.processedServerId)) {
|
||||||
|
PageController.goToPage(PageEnum.PageSettingsApiAvailableCountries)
|
||||||
ColumnLayout {
|
} else {
|
||||||
id: serverRadioButtonContent
|
PageController.showBusyIndicator(true)
|
||||||
objectName: "serverRadioButtonContent"
|
let result = SubscriptionUiController.getAccountInfo(ServersUiController.processedServerId, false)
|
||||||
|
PageController.showBusyIndicator(false)
|
||||||
anchors.fill: parent
|
if (!result) {
|
||||||
anchors.rightMargin: 16
|
return
|
||||||
anchors.leftMargin: 16
|
|
||||||
|
|
||||||
spacing: 0
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
objectName: "serverRadioButtonRowLayout"
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
VerticalRadioButton {
|
|
||||||
id: serverRadioButton
|
|
||||||
objectName: "serverRadioButton"
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
text: name
|
|
||||||
descriptionText: isServerFromGatewayApi && (isSubscriptionExpired || isSubscriptionExpiringSoon)
|
|
||||||
? (isSubscriptionExpired ? qsTr("Subscription expired. Please renew") : qsTr("Subscription expiring soon"))
|
|
||||||
: serverDescription
|
|
||||||
descriptionColor: isServerFromGatewayApi && (isSubscriptionExpired || isSubscriptionExpiringSoon)
|
|
||||||
? (isSubscriptionExpired ? AmneziaStyle.color.vibrantRed : AmneziaStyle.color.goldenApricot)
|
|
||||||
: AmneziaStyle.color.mutedGray
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
ServersUiController.setDefaultServerAtIndex(index)
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onEnterPressed: serverRadioButton.clicked()
|
|
||||||
Keys.onReturnPressed: serverRadioButton.clicked()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageButtonType {
|
PageController.goToPage(PageEnum.PageSettingsApiServerInfo)
|
||||||
id: outdatedContainerWarningIcon
|
|
||||||
objectName: "outdatedContainerWarningIcon"
|
|
||||||
|
|
||||||
visible: ServersUiController.serverHasOutdatedAwgContainer(serverId)
|
|
||||||
|
|
||||||
hoverEnabled: false
|
|
||||||
image: "qrc:/images/controls/alert-circle.svg"
|
|
||||||
imageColor: AmneziaStyle.color.goldenApricot
|
|
||||||
|
|
||||||
implicitWidth: 40
|
|
||||||
implicitHeight: 56
|
|
||||||
}
|
|
||||||
|
|
||||||
ImageButtonType {
|
|
||||||
id: serverInfoButton
|
|
||||||
objectName: "serverInfoButton"
|
|
||||||
|
|
||||||
image: "qrc:/images/controls/settings.svg"
|
|
||||||
imageColor: AmneziaStyle.color.paleGray
|
|
||||||
|
|
||||||
implicitWidth: 56
|
|
||||||
implicitHeight: 56
|
|
||||||
|
|
||||||
z: 1
|
|
||||||
|
|
||||||
onClicked: function() {
|
|
||||||
ServersUiController.setProcessedServerId(serverId)
|
|
||||||
|
|
||||||
if (ServersUiController.isServerFromApi(ServersUiController.processedServerId)) {
|
|
||||||
if (ServersUiController.isServerCountrySelectionAvailable(ServersUiController.processedServerId)) {
|
|
||||||
PageController.goToPage(PageEnum.PageSettingsApiAvailableCountries)
|
|
||||||
} else {
|
|
||||||
PageController.showBusyIndicator(true)
|
|
||||||
let result = SubscriptionUiController.getAccountInfo(ServersUiController.processedServerId, false)
|
|
||||||
PageController.showBusyIndicator(false)
|
|
||||||
if (!result) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
PageController.goToPage(PageEnum.PageSettingsApiServerInfo)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
PageController.goToPage(PageEnum.PageSettingsServerInfo)
|
|
||||||
}
|
|
||||||
|
|
||||||
drawer.closeTriggered()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DividerType {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.leftMargin: 0
|
|
||||||
Layout.rightMargin: 0
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
PageController.goToPage(PageEnum.PageSettingsServerInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
root.serverSettingsOpened()
|
||||||
|
}
|
||||||
|
|
||||||
|
delegate: ServerListItem {
|
||||||
|
objectName: "serverRadioButton"
|
||||||
|
|
||||||
|
readonly property bool subscriptionAlert: isServerFromGatewayApi && (isSubscriptionExpired || isSubscriptionExpiringSoon)
|
||||||
|
|
||||||
|
width: root.width - root.leftMargin - root.rightMargin
|
||||||
|
|
||||||
|
text: name
|
||||||
|
subtitle: subscriptionAlert
|
||||||
|
? (isSubscriptionExpired ? qsTr("Subscription expired. Please renew") : qsTr("Subscription expiring soon"))
|
||||||
|
: serverDescription
|
||||||
|
subtitleColor: subscriptionAlert
|
||||||
|
? (isSubscriptionExpired ? AmneziaStyle.color.statusDestructive : AmneziaStyle.color.statusWarning)
|
||||||
|
: AmneziaStyle.color.textTertiary
|
||||||
|
showWarning: ServersUiController.serverHasOutdatedAwgContainer(serverId)
|
||||||
|
showSeparator: index < root.count - 1
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
ServersUiController.setDefaultServerAtIndex(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
onSettingsRequested: root.openServerSettings(serverId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,41 +1,59 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls.impl
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
|
|
||||||
import SortFilterProxyModel 0.2
|
|
||||||
|
|
||||||
import PageEnum 1.0
|
import PageEnum 1.0
|
||||||
import Style 1.0
|
import Style 1.0
|
||||||
|
|
||||||
import "../Controls2"
|
import "../Controls2"
|
||||||
import "../Controls2/TextTypes"
|
|
||||||
|
|
||||||
|
|
||||||
ListViewType {
|
ListViewType {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
||||||
delegate: ColumnLayout {
|
delegate: Item {
|
||||||
width: root.width
|
id: delegateRoot
|
||||||
|
|
||||||
property bool isOutdatedAwgContainer: Boolean(isInstalled && ServersUiController.isContainerOutdatedAwg(root.model.mapToSource(index)))
|
readonly property bool isOutdatedAwgContainer: Boolean(isInstalled && ServersUiController.isContainerOutdatedAwg(root.model.mapToSource(index)))
|
||||||
|
|
||||||
LabelWithButtonType {
|
implicitWidth: root.width
|
||||||
Layout.fillWidth: true
|
implicitHeight: containerItem.implicitHeight
|
||||||
|
|
||||||
|
ListItemType {
|
||||||
|
id: containerItem
|
||||||
|
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.leftMargin: 16
|
||||||
|
anchors.rightMargin: 16
|
||||||
|
|
||||||
text: name
|
text: name
|
||||||
descriptionText: description
|
subtitle: description
|
||||||
rightWarningImageSource: isOutdatedAwgContainer ? "qrc:/images/controls/alert-circle.svg" : ""
|
trailingIconSource: isInstalled ? "qrc:/images/controls/chevron-right.svg" : "qrc:/images/controls/download.svg"
|
||||||
rightImageSource: isInstalled ? "qrc:/images/controls/chevron-right.svg" : "qrc:/images/controls/download.svg"
|
trailingContentVisible: delegateRoot.isOutdatedAwgContainer
|
||||||
|
|
||||||
clickedFunction: function() {
|
Item {
|
||||||
|
Layout.preferredWidth: 48
|
||||||
|
Layout.preferredHeight: 48
|
||||||
|
|
||||||
|
IconImage {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: 24
|
||||||
|
height: 24
|
||||||
|
|
||||||
|
source: "qrc:/images/controls/alert-circle.svg"
|
||||||
|
sourceSize: Qt.size(24, 24)
|
||||||
|
color: AmneziaStyle.color.statusWarning
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
if (isInstalled) {
|
if (isInstalled) {
|
||||||
var containerIndex = root.model.mapToSource(index)
|
var containerIndex = root.model.mapToSource(index)
|
||||||
ServersUiController.processedContainerIndex = containerIndex
|
ServersUiController.processedContainerIndex = containerIndex
|
||||||
|
|
||||||
if (isVpnContainer) {
|
if (isVpnContainer) {
|
||||||
// var isThirdPartyConfig = root.model.data(index, ContainersModel.IsThirdPartyConfigRole)
|
|
||||||
if (isThirdPartyConfig) {
|
if (isThirdPartyConfig) {
|
||||||
InstallController.updateProtocols(ServersUiController.processedServerId, containerIndex)
|
InstallController.updateProtocols(ServersUiController.processedServerId, containerIndex)
|
||||||
PageController.goToPage(PageEnum.PageProtocolRaw)
|
PageController.goToPage(PageEnum.PageProtocolRaw)
|
||||||
@@ -68,14 +86,11 @@ ListViewType {
|
|||||||
PageController.goToPage(PageEnum.PageSetupWizardProtocolSettings)
|
PageController.goToPage(PageEnum.PageSetupWizardProtocolSettings)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
enabled: false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
DividerType {}
|
footer: Item {
|
||||||
|
width: root.width
|
||||||
|
height: 16 + PageController.safeAreaBottomMargin
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,181 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
import Style 1.0
|
||||||
|
|
||||||
|
import "../Controls2"
|
||||||
|
import "../Controls2/TextTypes"
|
||||||
|
|
||||||
|
SheetType {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property string clientName
|
||||||
|
property string creationDate
|
||||||
|
property string latestHandshake
|
||||||
|
property string dataReceived
|
||||||
|
property string dataSent
|
||||||
|
property string allowedIps
|
||||||
|
|
||||||
|
signal renameRequested(string newName)
|
||||||
|
signal revokeRequested()
|
||||||
|
|
||||||
|
expandedHeight: content.implicitHeight + 16 + PageController.safeAreaBottomMargin
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: content
|
||||||
|
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.topMargin: 16
|
||||||
|
anchors.leftMargin: 16
|
||||||
|
anchors.rightMargin: 16
|
||||||
|
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
H2TextType {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.bottomMargin: 12
|
||||||
|
|
||||||
|
text: root.clientName
|
||||||
|
maximumLineCount: 2
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
elide: Qt.ElideRight
|
||||||
|
}
|
||||||
|
|
||||||
|
BodyTextType {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
visible: root.creationDate !== ""
|
||||||
|
|
||||||
|
maximumLineCount: 2
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
elide: Qt.ElideRight
|
||||||
|
|
||||||
|
text: qsTranslate("PageShare", "Creation date: %1").arg(root.creationDate)
|
||||||
|
}
|
||||||
|
|
||||||
|
BodyTextType {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
visible: root.latestHandshake !== ""
|
||||||
|
|
||||||
|
maximumLineCount: 2
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
elide: Qt.ElideRight
|
||||||
|
|
||||||
|
text: qsTranslate("PageShare", "Latest handshake: %1").arg(root.latestHandshake)
|
||||||
|
}
|
||||||
|
|
||||||
|
BodyTextType {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
visible: root.dataReceived !== ""
|
||||||
|
|
||||||
|
maximumLineCount: 2
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
elide: Qt.ElideRight
|
||||||
|
|
||||||
|
text: qsTranslate("PageShare", "Data received: %1").arg(root.dataReceived)
|
||||||
|
}
|
||||||
|
|
||||||
|
BodyTextType {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
visible: root.dataSent !== ""
|
||||||
|
|
||||||
|
maximumLineCount: 2
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
elide: Qt.ElideRight
|
||||||
|
|
||||||
|
text: qsTranslate("PageShare", "Data sent: %1").arg(root.dataSent)
|
||||||
|
}
|
||||||
|
|
||||||
|
BodyTextType {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
visible: root.allowedIps !== ""
|
||||||
|
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
|
||||||
|
text: qsTranslate("PageShare", "Allowed IPs: %1").arg(root.allowedIps)
|
||||||
|
}
|
||||||
|
|
||||||
|
ButtonType {
|
||||||
|
id: renameButton
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: 24
|
||||||
|
|
||||||
|
variant: ButtonType.Secondary
|
||||||
|
text: qsTranslate("PageShare", "Rename")
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
clientNameEditor.textField.text = root.clientName
|
||||||
|
clientNameEditDrawer.open()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ButtonType {
|
||||||
|
id: revokeButton
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: 12
|
||||||
|
|
||||||
|
variant: ButtonType.Secondary
|
||||||
|
text: qsTranslate("PageShare", "Revoke")
|
||||||
|
|
||||||
|
onClicked: root.revokeRequested()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SheetType {
|
||||||
|
id: clientNameEditDrawer
|
||||||
|
|
||||||
|
expandedHeight: editContent.implicitHeight + 32 + PageController.safeAreaBottomMargin
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: editContent
|
||||||
|
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.topMargin: 16
|
||||||
|
anchors.leftMargin: 16
|
||||||
|
anchors.rightMargin: 16
|
||||||
|
|
||||||
|
spacing: 16
|
||||||
|
|
||||||
|
TextFieldType {
|
||||||
|
id: clientNameEditor
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
headerText: qsTranslate("PageShare", "Client name")
|
||||||
|
textField.text: root.clientName
|
||||||
|
textField.maximumLength: 20
|
||||||
|
checkEmptyText: true
|
||||||
|
}
|
||||||
|
|
||||||
|
ButtonType {
|
||||||
|
id: saveButton
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
text: qsTranslate("PageShare", "Save")
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
if (clientNameEditor.textField.text === "") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clientNameEditor.textField.text !== root.clientName) {
|
||||||
|
root.renameRequested(clientNameEditor.textField.text)
|
||||||
|
clientNameEditDrawer.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,15 +5,14 @@ import QtQuick.Layouts
|
|||||||
import "../Controls2"
|
import "../Controls2"
|
||||||
import "../Controls2/TextTypes"
|
import "../Controls2/TextTypes"
|
||||||
|
|
||||||
DrawerType2 {
|
SheetType {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property var listModel: null
|
property var listModel: null
|
||||||
|
|
||||||
width: parent.width
|
expandedHeight: content.implicitHeight + 32 + PageController.safeAreaBottomMargin
|
||||||
height: parent.height
|
|
||||||
|
|
||||||
expandedStateContent: ColumnLayout {
|
ColumnLayout {
|
||||||
id: content
|
id: content
|
||||||
|
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
@@ -21,25 +20,21 @@ DrawerType2 {
|
|||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
Component.onCompleted: {
|
|
||||||
root.expandedHeight = content.implicitHeight + 32 + PageController.safeAreaBottomMargin
|
|
||||||
}
|
|
||||||
|
|
||||||
ButtonGroup {
|
ButtonGroup {
|
||||||
id: sortModeGroup
|
id: sortModeGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
Header2TextType {
|
SectionHeaderType {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: 24
|
Layout.topMargin: 16
|
||||||
Layout.rightMargin: 16
|
Layout.rightMargin: 16
|
||||||
Layout.leftMargin: 16
|
Layout.leftMargin: 16
|
||||||
Layout.bottomMargin: 16
|
Layout.bottomMargin: 16
|
||||||
|
|
||||||
text: qsTr("Sort countries")
|
headerText: qsTr("Sort countries")
|
||||||
}
|
}
|
||||||
|
|
||||||
VerticalRadioButton {
|
RadioItemType {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.leftMargin: 16
|
Layout.leftMargin: 16
|
||||||
Layout.rightMargin: 16
|
Layout.rightMargin: 16
|
||||||
@@ -51,32 +46,25 @@ DrawerType2 {
|
|||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
root.listModel.sortMode = 0
|
root.listModel.sortMode = 0
|
||||||
root.closeTriggered()
|
root.close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DividerType {
|
RadioItemType {
|
||||||
Layout.fillWidth: true
|
|
||||||
}
|
|
||||||
|
|
||||||
VerticalRadioButton {
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.leftMargin: 16
|
Layout.leftMargin: 16
|
||||||
Layout.rightMargin: 16
|
Layout.rightMargin: 16
|
||||||
|
|
||||||
text: qsTr("Alphabetically")
|
text: qsTr("Alphabetically")
|
||||||
|
showSeparator: false
|
||||||
|
|
||||||
ButtonGroup.group: sortModeGroup
|
ButtonGroup.group: sortModeGroup
|
||||||
checked: root.listModel !== null && root.listModel.sortMode === 1
|
checked: root.listModel !== null && root.listModel.sortMode === 1
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
root.listModel.sortMode = 1
|
root.listModel.sortMode = 1
|
||||||
root.closeTriggered()
|
root.close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DividerType {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -9,16 +9,18 @@ import Style 1.0
|
|||||||
import "../Controls2"
|
import "../Controls2"
|
||||||
import "../Controls2/TextTypes"
|
import "../Controls2/TextTypes"
|
||||||
|
|
||||||
DrawerType2 {
|
SheetType {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property bool isRenewalAvailable: false
|
property bool isRenewalAvailable: false
|
||||||
|
|
||||||
onOpened: {
|
expandedHeight: content.implicitHeight + 8 + PageController.safeAreaBottomMargin
|
||||||
|
|
||||||
|
onAboutToShow: {
|
||||||
isRenewalAvailable = ServersUiController.isServerRenewalAvailable(ServersUiController.defaultServerId) && !ApiAccountInfoModel.data("isInAppPurchase")
|
isRenewalAvailable = ServersUiController.isServerRenewalAvailable(ServersUiController.defaultServerId) && !ApiAccountInfoModel.data("isInAppPurchase")
|
||||||
}
|
}
|
||||||
|
|
||||||
expandedStateContent: ColumnLayout {
|
ColumnLayout {
|
||||||
id: content
|
id: content
|
||||||
|
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
@@ -27,79 +29,47 @@ DrawerType2 {
|
|||||||
|
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
onImplicitHeightChanged: {
|
SectionHeaderType {
|
||||||
root.expandedHeight = content.implicitHeight + 32 + PageController.safeAreaBottomMargin
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.topMargin: 24
|
|
||||||
Layout.rightMargin: 16
|
|
||||||
Layout.leftMargin: 16
|
|
||||||
implicitHeight: titleText.implicitHeight
|
|
||||||
|
|
||||||
Header2TextType {
|
|
||||||
id: titleText
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
|
|
||||||
text: ServersUiController.serverName(ServersUiController.defaultServerId) + qsTr(" subscription has expired")
|
|
||||||
horizontalAlignment: Text.AlignLeft
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ParagraphTextType {
|
|
||||||
visible: root.isRenewalAvailable
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.topMargin: 8
|
|
||||||
Layout.rightMargin: 16
|
|
||||||
Layout.leftMargin: 16
|
|
||||||
|
|
||||||
text: qsTr("Renew to continue using VPN")
|
|
||||||
horizontalAlignment: Text.AlignLeft
|
|
||||||
}
|
|
||||||
|
|
||||||
BasicButtonType {
|
|
||||||
visible: root.isRenewalAvailable
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: 16
|
Layout.topMargin: 16
|
||||||
Layout.rightMargin: 16
|
Layout.rightMargin: 16
|
||||||
Layout.leftMargin: 16
|
Layout.leftMargin: 16
|
||||||
|
|
||||||
|
headerText: ServersUiController.serverName(ServersUiController.defaultServerId) + qsTr(" subscription has expired")
|
||||||
|
descriptionText: root.isRenewalAvailable ? qsTr("Renew to continue using VPN") : ""
|
||||||
|
}
|
||||||
|
|
||||||
|
ButtonType {
|
||||||
|
visible: root.isRenewalAvailable
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: 26
|
||||||
|
Layout.rightMargin: 16
|
||||||
|
Layout.leftMargin: 16
|
||||||
|
|
||||||
text: qsTr("Renew")
|
text: qsTr("Renew")
|
||||||
|
|
||||||
defaultColor: AmneziaStyle.color.paleGray
|
onClicked: {
|
||||||
hoveredColor: AmneziaStyle.color.lightGray
|
|
||||||
pressedColor: AmneziaStyle.color.mutedGray
|
|
||||||
textColor: AmneziaStyle.color.midnightBlack
|
|
||||||
|
|
||||||
clickedFunc: function() {
|
|
||||||
SubscriptionUiController.getRenewalLink(ServersUiController.defaultServerId)
|
SubscriptionUiController.getRenewalLink(ServersUiController.defaultServerId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BasicButtonType {
|
ButtonType {
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: 8
|
Layout.topMargin: root.isRenewalAvailable ? 12 : 26
|
||||||
Layout.bottomMargin: 8
|
Layout.rightMargin: 16
|
||||||
|
Layout.leftMargin: 16
|
||||||
|
|
||||||
implicitHeight: 25
|
variant: ButtonType.Text
|
||||||
|
|
||||||
defaultColor: AmneziaStyle.color.transparent
|
|
||||||
hoveredColor: AmneziaStyle.color.translucentWhite
|
|
||||||
pressedColor: AmneziaStyle.color.sheerWhite
|
|
||||||
textColor: AmneziaStyle.color.goldenApricot
|
|
||||||
|
|
||||||
text: qsTr("Support")
|
text: qsTr("Support")
|
||||||
|
|
||||||
clickedFunc: function() {
|
onClicked: {
|
||||||
PageController.showBusyIndicator(true)
|
PageController.showBusyIndicator(true)
|
||||||
let result = SubscriptionUiController.getAccountInfo(ServersUiController.defaultServerId, false)
|
let result = SubscriptionUiController.getAccountInfo(ServersUiController.defaultServerId, false)
|
||||||
PageController.showBusyIndicator(false)
|
PageController.showBusyIndicator(false)
|
||||||
if (result) {
|
if (result) {
|
||||||
root.closeTriggered()
|
root.close()
|
||||||
PageController.goToPage(PageEnum.PageSettingsApiSupport)
|
PageController.goToPage(PageEnum.PageSettingsApiSupport)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,95 +0,0 @@
|
|||||||
import QtQuick
|
|
||||||
import QtQuick.Layouts
|
|
||||||
|
|
||||||
import Style 1.0
|
|
||||||
|
|
||||||
import "../Controls2/TextTypes"
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property bool selected: false
|
|
||||||
property string billingPeriod: ""
|
|
||||||
property string priceLabel: ""
|
|
||||||
property string subtitle: ""
|
|
||||||
property bool showRecommendedBadge: false
|
|
||||||
property string recommendedText: "Recommended"
|
|
||||||
|
|
||||||
signal selectRequested
|
|
||||||
|
|
||||||
implicitHeight: cardLayout.implicitHeight + 28
|
|
||||||
radius: 16
|
|
||||||
color: AmneziaStyle.color.transparent
|
|
||||||
border.width: selected ? 2 : 1
|
|
||||||
border.color: selected ? AmneziaStyle.color.goldenApricot : AmneziaStyle.color.charcoalGray
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
id: cardLayout
|
|
||||||
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.topMargin: 14
|
|
||||||
anchors.leftMargin: 16
|
|
||||||
anchors.rightMargin: 16
|
|
||||||
|
|
||||||
spacing: 8
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
LabelTextType {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
text: root.billingPeriod
|
|
||||||
color: root.selected ? AmneziaStyle.color.goldenApricot : AmneziaStyle.color.paleGray
|
|
||||||
font.pixelSize: 17
|
|
||||||
font.weight: Font.DemiBold
|
|
||||||
wrapMode: Text.Wrap
|
|
||||||
}
|
|
||||||
|
|
||||||
LabelTextType {
|
|
||||||
text: root.priceLabel
|
|
||||||
color: root.selected ? AmneziaStyle.color.goldenApricot : AmneziaStyle.color.paleGray
|
|
||||||
font.pixelSize: 17
|
|
||||||
font.weight: Font.DemiBold
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
visible: root.subtitle.length > 0 || root.showRecommendedBadge
|
|
||||||
|
|
||||||
LabelTextType {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
text: root.subtitle
|
|
||||||
color: AmneziaStyle.color.mutedGray
|
|
||||||
font.pixelSize: 13
|
|
||||||
wrapMode: Text.Wrap
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
visible: root.showRecommendedBadge
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
|
||||||
radius: 10
|
|
||||||
color: AmneziaStyle.color.softViolet
|
|
||||||
implicitHeight: recLabel.implicitHeight + 8
|
|
||||||
implicitWidth: recLabel.implicitWidth + 16
|
|
||||||
|
|
||||||
LabelTextType {
|
|
||||||
id: recLabel
|
|
||||||
|
|
||||||
anchors.centerIn: parent
|
|
||||||
text: root.recommendedText
|
|
||||||
color: AmneziaStyle.color.midnightBlack
|
|
||||||
font.pixelSize: 11
|
|
||||||
font.weight: Font.Medium
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
onClicked: root.selectRequested()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -5,7 +5,7 @@ import Style 1.0
|
|||||||
|
|
||||||
import "../Controls2/TextTypes"
|
import "../Controls2/TextTypes"
|
||||||
|
|
||||||
ParagraphTextType {
|
CaptionTextType {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property string termsUrl: ""
|
property string termsUrl: ""
|
||||||
@@ -15,15 +15,12 @@ ParagraphTextType {
|
|||||||
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
textFormat: Text.RichText
|
textFormat: Text.RichText
|
||||||
color: AmneziaStyle.color.mutedGray
|
color: AmneziaStyle.color.textAccent
|
||||||
font.pixelSize: 12
|
|
||||||
lineHeight: 1.35
|
|
||||||
lineHeightMode: Text.ProportionalHeight
|
|
||||||
|
|
||||||
text: qsTr("By continuing, you agree to the <a href=\"%1\" style=\"color: %3;\">Terms of Use</a> and <a href=\"%2\" style=\"color: %3;\">Privacy Policy</a>")
|
text: qsTr("By continuing, you agree to the <a href=\"%1\" style=\"color: %3;\">Terms of Use</a> and <a href=\"%2\" style=\"color: %3;\">Privacy Policy</a>")
|
||||||
.arg(root.termsUrl)
|
.arg(root.termsUrl)
|
||||||
.arg(root.privacyUrl)
|
.arg(root.privacyUrl)
|
||||||
.arg(AmneziaStyle.color.goldenApricotString)
|
.arg(AmneziaStyle.color.textAccent)
|
||||||
|
|
||||||
onLinkActivated: function(link) {
|
onLinkActivated: function(link) {
|
||||||
Qt.openUrlExternally(link)
|
Qt.openUrlExternally(link)
|
||||||
|
|||||||
@@ -1,61 +1,7 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
|
||||||
import QtQuick.Layouts
|
|
||||||
|
|
||||||
import Style 1.0
|
|
||||||
|
|
||||||
import "../Controls2"
|
import "../Controls2"
|
||||||
import "../Controls2/TextTypes"
|
|
||||||
|
|
||||||
Rectangle {
|
SegmentedControlType {
|
||||||
id: root
|
model: ["UDP", "TCP"]
|
||||||
|
|
||||||
property real rootWidth: root.width
|
|
||||||
property int currentIndex
|
|
||||||
|
|
||||||
implicitWidth: transportProtoButtonGroup.implicitWidth
|
|
||||||
implicitHeight: transportProtoButtonGroup.implicitHeight
|
|
||||||
|
|
||||||
color: AmneziaStyle.color.onyxBlack
|
|
||||||
radius: 16
|
|
||||||
|
|
||||||
onFocusChanged: {
|
|
||||||
if (focus) {
|
|
||||||
udpButton.forceActiveFocus()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
id: transportProtoButtonGroup
|
|
||||||
|
|
||||||
spacing: 0
|
|
||||||
|
|
||||||
HorizontalRadioButton {
|
|
||||||
id: udpButton
|
|
||||||
checked: root.currentIndex === 0
|
|
||||||
|
|
||||||
hoverEnabled: root.enabled
|
|
||||||
|
|
||||||
implicitWidth: (rootWidth - 32) / 2
|
|
||||||
text: "UDP"
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
root.currentIndex = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
HorizontalRadioButton {
|
|
||||||
id: tcpButton
|
|
||||||
checked: root.currentIndex === 1
|
|
||||||
|
|
||||||
hoverEnabled: root.enabled
|
|
||||||
|
|
||||||
implicitWidth: (rootWidth - 32) / 2
|
|
||||||
text: "TCP"
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
root.currentIndex = 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -8,19 +8,14 @@ import "../Controls2"
|
|||||||
import "../Controls2/TextTypes"
|
import "../Controls2/TextTypes"
|
||||||
import "../Config"
|
import "../Config"
|
||||||
|
|
||||||
DrawerType2 {
|
SheetType {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
expandedStateContent: Item {
|
expandedHeight: content.implicitHeight + 16 + 24 + PageController.safeAreaBottomMargin
|
||||||
|
|
||||||
|
Item {
|
||||||
id: contentRoot
|
id: contentRoot
|
||||||
|
anchors.fill: parent
|
||||||
implicitHeight: content.implicitHeight + 40 + PageController.safeAreaBottomMargin
|
|
||||||
|
|
||||||
Binding {
|
|
||||||
target: root
|
|
||||||
property: "expandedHeight"
|
|
||||||
value: contentRoot.implicitHeight
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: content
|
id: content
|
||||||
@@ -28,65 +23,61 @@ DrawerType2 {
|
|||||||
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: 24
|
anchors.topMargin: 16
|
||||||
anchors.leftMargin: 16
|
|
||||||
anchors.rightMargin: 16
|
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
Header2TextType {
|
SectionHeaderType {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
Layout.leftMargin: 16
|
||||||
|
Layout.rightMargin: 16
|
||||||
|
Layout.bottomMargin: 16
|
||||||
|
|
||||||
text: qsTr("Support")
|
headerText: qsTr("Support")
|
||||||
|
descriptionText: qsTr("If the update won't install, message us")
|
||||||
}
|
}
|
||||||
|
|
||||||
AppTextType {
|
ListItemType {
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.topMargin: 8
|
|
||||||
|
|
||||||
color: AmneziaStyle.color.textTertiary
|
|
||||||
text: qsTr("If the update won't install, message us")
|
|
||||||
}
|
|
||||||
|
|
||||||
LabelWithButtonType {
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
Layout.leftMargin: 16
|
||||||
|
Layout.rightMargin: 16
|
||||||
Layout.topMargin: 16
|
Layout.topMargin: 16
|
||||||
|
|
||||||
text: qsTr("Telegram")
|
text: qsTr("Telegram")
|
||||||
descriptionText: qsTr("We'll reply in chat")
|
subtitle: qsTr("We'll reply in chat")
|
||||||
leftImageSource: "qrc:/images/controls/telegram.svg"
|
icon.source: "qrc:/images/controls/telegram.svg"
|
||||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
trailingIconSource: "qrc:/images/controls/chevron-right.svg"
|
||||||
|
|
||||||
clickedFunction: function() {
|
onClicked: {
|
||||||
Qt.openUrlExternally(qsTr("https://t.me/amnezia_vpn_en"))
|
Qt.openUrlExternally(qsTr("https://t.me/amnezia_vpn_en"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DividerType {}
|
ListItemType {
|
||||||
|
|
||||||
LabelWithButtonType {
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
Layout.leftMargin: 16
|
||||||
|
Layout.rightMargin: 16
|
||||||
|
|
||||||
text: qsTr("support@amnezia.org")
|
text: qsTr("support@amnezia.org")
|
||||||
descriptionText: qsTr("Support email")
|
subtitle: qsTr("Support email")
|
||||||
leftImageSource: "qrc:/images/controls/mail.svg"
|
icon.source: "qrc:/images/controls/mail.svg"
|
||||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
trailingIconSource: "qrc:/images/controls/chevron-right.svg"
|
||||||
|
|
||||||
clickedFunction: function() {
|
onClicked: {
|
||||||
Qt.openUrlExternally(qsTr("mailto:support@amnezia.org"))
|
Qt.openUrlExternally(qsTr("mailto:support@amnezia.org"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DividerType {}
|
ListItemType {
|
||||||
|
|
||||||
LabelWithButtonType {
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
Layout.leftMargin: 16
|
||||||
|
Layout.rightMargin: 16
|
||||||
|
|
||||||
text: qsTr("amnezia.org")
|
text: qsTr("amnezia.org")
|
||||||
descriptionText: qsTr("Download the update manually")
|
subtitle: qsTr("Download the update manually")
|
||||||
leftImageSource: "qrc:/images/controls/amnezia.svg"
|
icon.source: "qrc:/images/controls/amnezia.svg"
|
||||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
trailingIconSource: "qrc:/images/controls/chevron-right.svg"
|
||||||
|
|
||||||
clickedFunction: function() {
|
onClicked: {
|
||||||
Qt.openUrlExternally(LanguageUiController.getCurrentSiteUrl(""))
|
Qt.openUrlExternally(LanguageUiController.getCurrentSiteUrl(""))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,196 +0,0 @@
|
|||||||
import QtQuick
|
|
||||||
import QtQuick.Layouts
|
|
||||||
import Qt5Compat.GraphicalEffects
|
|
||||||
|
|
||||||
import Style 1.0
|
|
||||||
|
|
||||||
import "TextTypes"
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
default property alias trailingContent: trailingSlot.data
|
|
||||||
|
|
||||||
property string text
|
|
||||||
property string descriptionText
|
|
||||||
|
|
||||||
property string leftImageSource
|
|
||||||
property string rightImageSource
|
|
||||||
|
|
||||||
property color textColor: AmneziaStyle.color.textPrimary
|
|
||||||
property color leftImageColor: AmneziaStyle.color.surfaceInverse
|
|
||||||
|
|
||||||
property bool showDivider: true
|
|
||||||
|
|
||||||
property var clickedFunction
|
|
||||||
|
|
||||||
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()
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onEnterPressed: root.click()
|
|
||||||
Keys.onReturnPressed: root.click()
|
|
||||||
|
|
||||||
function click() {
|
|
||||||
if (clickedFunction && typeof clickedFunction === "function") {
|
|
||||||
clickedFunction()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
implicitHeight: Math.max(72, content.implicitHeight + content.anchors.topMargin + content.anchors.bottomMargin)
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: background
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.topMargin: 4
|
|
||||||
anchors.bottomMargin: 4
|
|
||||||
|
|
||||||
radius: 16
|
|
||||||
color: (mouseArea.pressed || mouseArea.containsMouse) ? AmneziaStyle.color.translucentWhite : AmneziaStyle.color.transparent
|
|
||||||
border.color: (mouseArea.pressed || root.activeFocus) ? AmneziaStyle.color.textTertiary : AmneziaStyle.color.transparent
|
|
||||||
border.width: (mouseArea.pressed || root.activeFocus) ? 1 : 0
|
|
||||||
|
|
||||||
Behavior on color {
|
|
||||||
PropertyAnimation { duration: 200 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
id: mouseArea
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
enabled: root.enabled
|
|
||||||
hoverEnabled: root.enabled
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
|
|
||||||
onClicked: root.click()
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
id: content
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.leftMargin: 12
|
|
||||||
anchors.rightMargin: 12
|
|
||||||
anchors.topMargin: 12
|
|
||||||
anchors.bottomMargin: 12
|
|
||||||
|
|
||||||
spacing: 12
|
|
||||||
|
|
||||||
Item {
|
|
||||||
Layout.preferredWidth: 48
|
|
||||||
Layout.preferredHeight: 48
|
|
||||||
|
|
||||||
visible: root.leftImageSource !== ""
|
|
||||||
|
|
||||||
Image {
|
|
||||||
id: leftImage
|
|
||||||
|
|
||||||
anchors.centerIn: parent
|
|
||||||
width: 24
|
|
||||||
height: 24
|
|
||||||
|
|
||||||
source: root.leftImageSource
|
|
||||||
sourceSize: Qt.size(24, 24)
|
|
||||||
visible: false
|
|
||||||
}
|
|
||||||
|
|
||||||
ColorOverlay {
|
|
||||||
anchors.fill: leftImage
|
|
||||||
source: leftImage
|
|
||||||
color: root.leftImageColor
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.leftMargin: 4
|
|
||||||
|
|
||||||
spacing: 4
|
|
||||||
|
|
||||||
AppH3TextType {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
text: root.text
|
|
||||||
color: root.textColor
|
|
||||||
}
|
|
||||||
|
|
||||||
AppSmallTextType {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
visible: text !== ""
|
|
||||||
|
|
||||||
text: root.descriptionText
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: trailingSlot
|
|
||||||
|
|
||||||
Layout.preferredWidth: childrenRect.width
|
|
||||||
Layout.preferredHeight: childrenRect.height
|
|
||||||
|
|
||||||
visible: children.length > 0
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
Layout.preferredWidth: 48
|
|
||||||
Layout.preferredHeight: 48
|
|
||||||
|
|
||||||
visible: root.rightImageSource !== ""
|
|
||||||
|
|
||||||
Image {
|
|
||||||
id: rightImage
|
|
||||||
|
|
||||||
anchors.centerIn: parent
|
|
||||||
width: 24
|
|
||||||
height: 24
|
|
||||||
|
|
||||||
source: root.rightImageSource
|
|
||||||
sourceSize: Qt.size(24, 24)
|
|
||||||
visible: false
|
|
||||||
}
|
|
||||||
|
|
||||||
ColorOverlay {
|
|
||||||
anchors.fill: rightImage
|
|
||||||
source: rightImage
|
|
||||||
color: AmneziaStyle.color.surfaceInverse
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
|
|
||||||
height: 1
|
|
||||||
|
|
||||||
visible: root.showDivider
|
|
||||||
color: AmneziaStyle.color.borderSoft
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -9,10 +9,13 @@ import "TextTypes"
|
|||||||
Popup {
|
Popup {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
|
enum Role { Default, Destructive }
|
||||||
|
|
||||||
property string headerText
|
property string headerText
|
||||||
property string descriptionText
|
property string descriptionText
|
||||||
property string cancelButtonText: qsTr("Cancel")
|
property string cancelButtonText: qsTr("Cancel")
|
||||||
property string confirmButtonText
|
property string confirmButtonText
|
||||||
|
property int role: AlertDialogType.Default
|
||||||
|
|
||||||
signal confirmed()
|
signal confirmed()
|
||||||
|
|
||||||
@@ -20,7 +23,7 @@ Popup {
|
|||||||
|
|
||||||
x: Math.round((parent.width - width) / 2)
|
x: Math.round((parent.width - width) / 2)
|
||||||
y: Math.round((parent.height - height) / 2)
|
y: Math.round((parent.height - height) / 2)
|
||||||
width: parent.width - 32
|
width: Math.min(parent.width - 32, 400)
|
||||||
|
|
||||||
padding: 0
|
padding: 0
|
||||||
topPadding: 20
|
topPadding: 20
|
||||||
@@ -30,7 +33,7 @@ Popup {
|
|||||||
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
|
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
|
||||||
|
|
||||||
Overlay.modal: Rectangle {
|
Overlay.modal: Rectangle {
|
||||||
color: AmneziaStyle.color.overlayScrim
|
color: AmneziaStyle.color.bgOverlay
|
||||||
}
|
}
|
||||||
|
|
||||||
enter: Transition {
|
enter: Transition {
|
||||||
@@ -42,21 +45,16 @@ Popup {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onOpened: {
|
onOpened: {
|
||||||
FocusController.pushRootObject(root)
|
|
||||||
cancelButton.forceActiveFocus()
|
cancelButton.forceActiveFocus()
|
||||||
}
|
}
|
||||||
|
|
||||||
onClosed: {
|
|
||||||
FocusController.dropRootObject(root)
|
|
||||||
}
|
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
radius: 16
|
radius: 16
|
||||||
color: AmneziaStyle.color.surfaceElevated
|
color: AmneziaStyle.color.bgSurfaceAlt
|
||||||
}
|
}
|
||||||
|
|
||||||
contentItem: ColumnLayout {
|
contentItem: ColumnLayout {
|
||||||
spacing: 0
|
spacing: 2
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
@@ -66,13 +64,13 @@ Popup {
|
|||||||
|
|
||||||
spacing: 8
|
spacing: 8
|
||||||
|
|
||||||
AppH3TextType {
|
H3TextType {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
text: root.headerText
|
text: root.headerText
|
||||||
}
|
}
|
||||||
|
|
||||||
AppSmallTextType {
|
SmallTextType {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
visible: text !== ""
|
visible: text !== ""
|
||||||
@@ -83,33 +81,30 @@ Popup {
|
|||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.leftMargin: 8
|
|
||||||
Layout.rightMargin: 8
|
|
||||||
Layout.bottomMargin: 8
|
|
||||||
|
|
||||||
spacing: 8
|
spacing: 0
|
||||||
|
|
||||||
AlertDialogButton {
|
DialogButtonType {
|
||||||
id: cancelButton
|
id: cancelButton
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredWidth: 1
|
||||||
|
|
||||||
text: root.cancelButtonText
|
text: root.cancelButtonText
|
||||||
textColor: AmneziaStyle.color.textTertiary
|
|
||||||
|
|
||||||
onClicked: {
|
onClicked: root.close()
|
||||||
root.close()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AlertDialogButton {
|
DialogButtonType {
|
||||||
id: confirmButton
|
id: confirmButton
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredWidth: 1
|
||||||
|
|
||||||
text: root.confirmButtonText
|
text: root.confirmButtonText
|
||||||
textColor: AmneziaStyle.color.accentDanger
|
emphasized: true
|
||||||
font.weight: 600
|
textColor: root.role === AlertDialogType.Destructive ? AmneziaStyle.color.statusDestructive
|
||||||
|
: AmneziaStyle.color.textAccent
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
root.close()
|
root.close()
|
||||||
@@ -118,55 +113,4 @@ Popup {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
component AlertDialogButton: AbstractButton {
|
|
||||||
id: button
|
|
||||||
|
|
||||||
property color textColor
|
|
||||||
|
|
||||||
property bool isFocusable: true
|
|
||||||
|
|
||||||
implicitHeight: 48
|
|
||||||
|
|
||||||
hoverEnabled: true
|
|
||||||
|
|
||||||
font.family: "PT Root UI VF"
|
|
||||||
font.pixelSize: 16
|
|
||||||
font.weight: 400
|
|
||||||
font.letterSpacing: -0.4
|
|
||||||
|
|
||||||
Keys.onTabPressed: FocusController.nextKeyTabItem()
|
|
||||||
Keys.onBacktabPressed: FocusController.previousKeyTabItem()
|
|
||||||
Keys.onLeftPressed: FocusController.nextKeyLeftItem()
|
|
||||||
Keys.onRightPressed: FocusController.nextKeyRightItem()
|
|
||||||
Keys.onEnterPressed: button.clicked()
|
|
||||||
Keys.onReturnPressed: button.clicked()
|
|
||||||
|
|
||||||
background: Rectangle {
|
|
||||||
radius: 16
|
|
||||||
color: (button.pressed || button.hovered) ? AmneziaStyle.color.translucentWhite : AmneziaStyle.color.transparent
|
|
||||||
border.color: (button.pressed || button.visualFocus) ? AmneziaStyle.color.textTertiary : AmneziaStyle.color.transparent
|
|
||||||
border.width: (button.pressed || button.visualFocus) ? 1 : 0
|
|
||||||
|
|
||||||
Behavior on color {
|
|
||||||
PropertyAnimation { duration: 200 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
contentItem: Text {
|
|
||||||
text: button.text
|
|
||||||
font: button.font
|
|
||||||
color: button.textColor
|
|
||||||
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
elide: Text.ElideRight
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
enabled: false
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,4 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Layouts
|
|
||||||
import Qt5Compat.GraphicalEffects
|
|
||||||
|
|
||||||
import Style 1.0
|
|
||||||
|
|
||||||
FocusScope {
|
FocusScope {
|
||||||
id: root
|
id: root
|
||||||
@@ -10,42 +6,25 @@ FocusScope {
|
|||||||
property string backButtonImage: "qrc:/images/controls/arrow-left.svg"
|
property string backButtonImage: "qrc:/images/controls/arrow-left.svg"
|
||||||
property var backButtonFunction
|
property var backButtonFunction
|
||||||
|
|
||||||
implicitWidth: content.implicitWidth
|
implicitWidth: backButton.implicitWidth + backButton.x
|
||||||
implicitHeight: content.implicitHeight
|
implicitHeight: backButton.implicitHeight
|
||||||
|
|
||||||
visible: backButtonImage !== ""
|
visible: backButtonImage !== ""
|
||||||
|
|
||||||
RowLayout {
|
IconButtonType {
|
||||||
id: content
|
id: backButton
|
||||||
|
|
||||||
anchors.fill: parent
|
x: 16
|
||||||
anchors.leftMargin: 8
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
ImageButtonType {
|
icon.source: root.backButtonImage
|
||||||
id: backButton
|
|
||||||
image: backButtonImage
|
|
||||||
imageColor: AmneziaStyle.color.paleGray
|
|
||||||
|
|
||||||
implicitWidth: 40
|
onClicked: {
|
||||||
implicitHeight: 40
|
if (root.backButtonFunction && typeof root.backButtonFunction === "function") {
|
||||||
|
root.backButtonFunction()
|
||||||
onClicked: {
|
} else {
|
||||||
if (backButtonFunction && typeof backButtonFunction === "function") {
|
PageController.closePage()
|
||||||
backButtonFunction()
|
|
||||||
} else {
|
|
||||||
PageController.closePage()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: background
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
color: AmneziaStyle.color.transparent
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Keys.onEnterPressed: backButton.clicked()
|
|
||||||
Keys.onReturnPressed: backButton.clicked()
|
|
||||||
}
|
}
|
||||||
@@ -9,6 +9,7 @@ Item {
|
|||||||
property Item sourceItem
|
property Item sourceItem
|
||||||
property real blurPadding: 40
|
property real blurPadding: 40
|
||||||
property real blurRadius: 50
|
property real blurRadius: 50
|
||||||
|
property real radius: height / 2
|
||||||
property color tintColor: Qt.alpha(AmneziaStyle.color.textStaticWhite, 0.10)
|
property color tintColor: Qt.alpha(AmneziaStyle.color.textStaticWhite, 0.10)
|
||||||
property color borderColor: Qt.alpha(AmneziaStyle.color.textStaticWhite, 0.75)
|
property color borderColor: Qt.alpha(AmneziaStyle.color.textStaticWhite, 0.75)
|
||||||
|
|
||||||
@@ -54,13 +55,13 @@ Item {
|
|||||||
id: backdropMask
|
id: backdropMask
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
radius: height / 2
|
radius: root.radius
|
||||||
visible: false
|
visible: false
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
radius: height / 2
|
radius: root.radius
|
||||||
|
|
||||||
color: root.tintColor
|
color: root.tintColor
|
||||||
border.color: root.borderColor
|
border.color: root.borderColor
|
||||||
|
|||||||
@@ -1,75 +0,0 @@
|
|||||||
import QtQuick
|
|
||||||
import QtQuick.Layouts
|
|
||||||
|
|
||||||
import Style 1.0
|
|
||||||
|
|
||||||
import "TextTypes"
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property string headerText
|
|
||||||
property int headerTextFormat: Text.AutoText
|
|
||||||
property int headerHorizontalAlignment: Text.AlignLeft
|
|
||||||
property int headerTextMaximumLineCount: 2
|
|
||||||
property int headerTextElide: Qt.ElideRight
|
|
||||||
property string descriptionText
|
|
||||||
property string descriptionLinkText
|
|
||||||
property string descriptionLinkUrl
|
|
||||||
property alias headerRow: headerRow
|
|
||||||
|
|
||||||
implicitWidth: content.implicitWidth
|
|
||||||
implicitHeight: content.implicitHeight
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
id: content
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
id: headerRow
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
Header1TextType {
|
|
||||||
id: header
|
|
||||||
Layout.fillWidth: true
|
|
||||||
text: root.headerText
|
|
||||||
textFormat: root.headerTextFormat
|
|
||||||
horizontalAlignment: root.headerHorizontalAlignment
|
|
||||||
maximumLineCount: root.headerTextMaximumLineCount
|
|
||||||
elide: root.headerTextElide
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ParagraphTextType {
|
|
||||||
id: description
|
|
||||||
Layout.topMargin: 16
|
|
||||||
Layout.fillWidth: true
|
|
||||||
text: root.descriptionText
|
|
||||||
color: AmneziaStyle.color.mutedGray
|
|
||||||
visible: root.descriptionText !== ""
|
|
||||||
}
|
|
||||||
|
|
||||||
ParagraphTextType {
|
|
||||||
id: descriptionLink
|
|
||||||
Layout.topMargin: 16
|
|
||||||
Layout.fillWidth: true
|
|
||||||
text: root.descriptionLinkText !== "" && root.descriptionLinkUrl !== ""
|
|
||||||
? ("<a href=\"" + root.descriptionLinkUrl + "\" style=\"color: " + AmneziaStyle.color.goldenApricotString + ";\">" + root.descriptionLinkText + "</a>")
|
|
||||||
: ""
|
|
||||||
textFormat: Text.RichText
|
|
||||||
visible: root.descriptionLinkText !== ""
|
|
||||||
|
|
||||||
onLinkActivated: function(link) {
|
|
||||||
Qt.openUrlExternally(link)
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
acceptedButtons: Qt.NoButton
|
|
||||||
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,210 +0,0 @@
|
|||||||
import QtQuick
|
|
||||||
import QtQuick.Controls
|
|
||||||
import QtQuick.Layouts
|
|
||||||
import Qt5Compat.GraphicalEffects
|
|
||||||
|
|
||||||
import Style 1.0
|
|
||||||
|
|
||||||
import "TextTypes"
|
|
||||||
|
|
||||||
Button {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property string hoveredColor: AmneziaStyle.color.lightGray
|
|
||||||
property string defaultColor: AmneziaStyle.color.paleGray
|
|
||||||
property string disabledColor: AmneziaStyle.color.charcoalGray
|
|
||||||
property string pressedColor: AmneziaStyle.color.mutedGray
|
|
||||||
|
|
||||||
property string textColor: AmneziaStyle.color.midnightBlack
|
|
||||||
|
|
||||||
property string borderColor: AmneziaStyle.color.paleGray
|
|
||||||
property string borderFocusedColor: AmneziaStyle.color.paleGray
|
|
||||||
property int borderWidth: 0
|
|
||||||
property int borderFocusedWidth: 1
|
|
||||||
|
|
||||||
property string leftImageSource
|
|
||||||
property string rightImageSource
|
|
||||||
property string leftImageColor: textColor
|
|
||||||
property bool changeLeftImageSize: true
|
|
||||||
|
|
||||||
property bool squareLeftSide: false
|
|
||||||
|
|
||||||
property var clickedFunc
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
hoverEnabled: true
|
|
||||||
|
|
||||||
background: Rectangle {
|
|
||||||
id: focusBorder
|
|
||||||
|
|
||||||
color: AmneziaStyle.color.transparent
|
|
||||||
border.color: root.activeFocus ? root.borderFocusedColor : AmneziaStyle.color.transparent
|
|
||||||
border.width: root.activeFocus ? root.borderFocusedWidth : 0
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
radius: 16
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: background
|
|
||||||
|
|
||||||
anchors.fill: focusBorder
|
|
||||||
anchors.margins: root.activeFocus ? 2 : 0
|
|
||||||
|
|
||||||
radius: root.activeFocus ? 14 : 16
|
|
||||||
color: {
|
|
||||||
if (root.enabled) {
|
|
||||||
if (root.pressed) {
|
|
||||||
return pressedColor
|
|
||||||
}
|
|
||||||
return root.hovered ? hoveredColor : defaultColor
|
|
||||||
} else {
|
|
||||||
return disabledColor
|
|
||||||
}
|
|
||||||
}
|
|
||||||
border.color: borderColor
|
|
||||||
border.width: borderWidth
|
|
||||||
|
|
||||||
Behavior on color {
|
|
||||||
PropertyAnimation { duration: 200 }
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
visible: root.squareLeftSide
|
|
||||||
|
|
||||||
z: 1
|
|
||||||
|
|
||||||
width: parent.radius
|
|
||||||
height: parent.radius
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
anchors.left: parent.left
|
|
||||||
color: {
|
|
||||||
if (root.enabled) {
|
|
||||||
if (root.pressed) {
|
|
||||||
return root.pressedColor
|
|
||||||
}
|
|
||||||
return root.hovered ? root.hoveredColor : root.defaultColor
|
|
||||||
} else {
|
|
||||||
return root.disabledColor
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on color {
|
|
||||||
PropertyAnimation { duration: 200 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: focusBorder
|
|
||||||
enabled: false
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
}
|
|
||||||
|
|
||||||
contentItem: Item {
|
|
||||||
anchors.fill: focusBorder
|
|
||||||
|
|
||||||
implicitWidth: content.implicitWidth
|
|
||||||
implicitHeight: content.implicitHeight
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
id: content
|
|
||||||
anchors.centerIn: parent
|
|
||||||
|
|
||||||
Image {
|
|
||||||
id: leftImage
|
|
||||||
source: root.leftImageSource
|
|
||||||
visible: root.leftImageSource === "" ? false : true
|
|
||||||
|
|
||||||
layer {
|
|
||||||
enabled: leftImageColor !== "" ? true : false
|
|
||||||
effect: ColorOverlay {
|
|
||||||
color: leftImageColor
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Component.onCompleted: {
|
|
||||||
if (root.changeLeftImageSize) {
|
|
||||||
leftImage.Layout.preferredHeight = 20
|
|
||||||
leftImage.Layout.preferredWidth = 20
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ButtonTextType {
|
|
||||||
id: buttonText
|
|
||||||
|
|
||||||
color: root.textColor
|
|
||||||
text: root.text
|
|
||||||
visible: root.text === "" ? false : true
|
|
||||||
|
|
||||||
horizontalAlignment: Text.AlignLeft
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
Image {
|
|
||||||
Layout.preferredHeight: 20
|
|
||||||
Layout.preferredWidth: 20
|
|
||||||
|
|
||||||
source: root.rightImageSource
|
|
||||||
visible: root.rightImageSource === "" ? false : true
|
|
||||||
|
|
||||||
layer {
|
|
||||||
enabled: true
|
|
||||||
effect: ColorOverlay {
|
|
||||||
color: textColor
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onEnterPressed: {
|
|
||||||
if (root.clickedFunc && typeof root.clickedFunc === "function") {
|
|
||||||
root.clickedFunc()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onReturnPressed: {
|
|
||||||
if (root.clickedFunc && typeof root.clickedFunc === "function") {
|
|
||||||
root.clickedFunc()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
if (root.clickedFunc && typeof root.clickedFunc === "function") {
|
|
||||||
root.clickedFunc()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,169 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Controls.impl
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
import Style 1.0
|
||||||
|
|
||||||
|
import "TextTypes"
|
||||||
|
|
||||||
|
AbstractButton {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property string subtitle
|
||||||
|
property string footnote
|
||||||
|
property string badgeText
|
||||||
|
property string errorText
|
||||||
|
property bool muted: false
|
||||||
|
|
||||||
|
readonly property real badgeOffset: root.badgeText !== "" ? 16 : 0
|
||||||
|
|
||||||
|
implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
|
||||||
|
implicitContentWidth + leftPadding + rightPadding)
|
||||||
|
implicitHeight: implicitContentHeight + topPadding + bottomPadding
|
||||||
|
|
||||||
|
topInset: root.badgeOffset
|
||||||
|
bottomInset: errorLabel.visible ? errorLabel.implicitHeight + 4 : 0
|
||||||
|
|
||||||
|
leftPadding: 16
|
||||||
|
rightPadding: 16
|
||||||
|
topPadding: root.badgeOffset + 12
|
||||||
|
bottomPadding: bottomInset + 12
|
||||||
|
|
||||||
|
spacing: 12
|
||||||
|
|
||||||
|
hoverEnabled: true
|
||||||
|
focusPolicy: Qt.NoFocus
|
||||||
|
opacity: enabled ? 1.0 : 0.4
|
||||||
|
|
||||||
|
icon.width: 24
|
||||||
|
icon.height: 24
|
||||||
|
icon.color: root.muted ? AmneziaStyle.color.iconSecondary : AmneziaStyle.color.iconDefault
|
||||||
|
|
||||||
|
Accessible.role: Accessible.Button
|
||||||
|
Accessible.name: root.text
|
||||||
|
Accessible.description: root.subtitle
|
||||||
|
|
||||||
|
background: Item {
|
||||||
|
implicitWidth: 358
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
radius: 16
|
||||||
|
color: root.muted ? AmneziaStyle.color.bgSurfaceAlt : AmneziaStyle.color.bgSurface
|
||||||
|
opacity: root.muted ? 0.75 : 1.0
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
radius: 16
|
||||||
|
color: !root.muted && root.enabled && (root.hovered || root.down) ? AmneziaStyle.color.bgStateLayer
|
||||||
|
: AmneziaStyle.color.transparent
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
ColorAnimation { duration: 150 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RecommendationBadgeType {
|
||||||
|
x: 16
|
||||||
|
y: -root.badgeOffset
|
||||||
|
|
||||||
|
visible: root.badgeText !== ""
|
||||||
|
text: root.badgeText
|
||||||
|
}
|
||||||
|
|
||||||
|
SmallTextType {
|
||||||
|
id: errorLabel
|
||||||
|
|
||||||
|
anchors.top: parent.bottom
|
||||||
|
anchors.topMargin: 4
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
|
||||||
|
visible: root.errorText !== ""
|
||||||
|
text: root.errorText
|
||||||
|
color: AmneziaStyle.color.statusDestructive
|
||||||
|
}
|
||||||
|
|
||||||
|
HoverHandler {
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
enabled: root.enabled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
contentItem: RowLayout {
|
||||||
|
spacing: root.spacing
|
||||||
|
|
||||||
|
IconImage {
|
||||||
|
Layout.alignment: Qt.AlignTop
|
||||||
|
Layout.preferredWidth: root.icon.width
|
||||||
|
Layout.preferredHeight: root.icon.height
|
||||||
|
|
||||||
|
visible: root.icon.source.toString() !== ""
|
||||||
|
source: root.icon.source
|
||||||
|
sourceSize: Qt.size(root.icon.width, root.icon.height)
|
||||||
|
color: root.icon.color
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.alignment: Qt.AlignTop
|
||||||
|
|
||||||
|
spacing: 4
|
||||||
|
|
||||||
|
H3TextType {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
visible: text !== ""
|
||||||
|
text: root.text
|
||||||
|
color: root.muted ? AmneziaStyle.color.textTertiary : AmneziaStyle.color.textPrimary
|
||||||
|
}
|
||||||
|
|
||||||
|
SmallTextType {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
visible: text !== "" && !root.muted
|
||||||
|
text: root.subtitle
|
||||||
|
textFormat: Text.RichText
|
||||||
|
|
||||||
|
onLinkActivated: function(link) {
|
||||||
|
Qt.openUrlExternally(link)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CaptionTextType {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
visible: text !== "" && root.muted
|
||||||
|
text: root.subtitle
|
||||||
|
textFormat: Text.RichText
|
||||||
|
color: AmneziaStyle.color.textQuaternary
|
||||||
|
}
|
||||||
|
|
||||||
|
SmallTextType {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: 8
|
||||||
|
|
||||||
|
visible: text !== ""
|
||||||
|
text: root.footnote
|
||||||
|
color: root.muted ? AmneziaStyle.color.textQuaternary : AmneziaStyle.color.textSecondary
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IconButtonType {
|
||||||
|
Layout.alignment: Qt.AlignTop
|
||||||
|
Layout.leftMargin: -root.spacing
|
||||||
|
|
||||||
|
icon.source: "qrc:/images/controls/chevron-right.svg"
|
||||||
|
icon.color: AmneziaStyle.color.iconDefault
|
||||||
|
opacity: root.muted ? 0.5 : 1.0
|
||||||
|
enabled: root.enabled
|
||||||
|
Accessible.name: root.text
|
||||||
|
|
||||||
|
onClicked: root.clicked()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
import Style 1.0
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property bool filled: false
|
||||||
|
property alias spacing: column.spacing
|
||||||
|
|
||||||
|
default property alias content: column.data
|
||||||
|
|
||||||
|
implicitHeight: column.implicitHeight + 8 + 16 + PageController.safeAreaBottomMargin
|
||||||
|
|
||||||
|
color: root.filled ? AmneziaStyle.color.bgSurfaceAlt : AmneziaStyle.color.bgBase
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: column
|
||||||
|
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.topMargin: 8
|
||||||
|
anchors.leftMargin: 16
|
||||||
|
anchors.rightMargin: 16
|
||||||
|
|
||||||
|
spacing: 12
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
import QtQuick
|
|
||||||
import QtQuick.Controls
|
|
||||||
import QtQuick.Shapes
|
|
||||||
|
|
||||||
import Style 1.0
|
|
||||||
|
|
||||||
Popup {
|
|
||||||
id: root
|
|
||||||
anchors.centerIn: parent
|
|
||||||
|
|
||||||
modal: true
|
|
||||||
closePolicy: Popup.NoAutoClose
|
|
||||||
|
|
||||||
visible: false
|
|
||||||
|
|
||||||
Overlay.modal: Rectangle {
|
|
||||||
color: AmneziaStyle.color.translucentMidnightBlack
|
|
||||||
}
|
|
||||||
|
|
||||||
background: Rectangle {
|
|
||||||
color: AmneziaStyle.color.transparent
|
|
||||||
}
|
|
||||||
|
|
||||||
BusyIndicator {
|
|
||||||
id: busyIndicator
|
|
||||||
|
|
||||||
visible: true
|
|
||||||
running: true
|
|
||||||
|
|
||||||
contentItem: Item {
|
|
||||||
implicitWidth: 46
|
|
||||||
implicitHeight: 46
|
|
||||||
transformOrigin: Item.Center
|
|
||||||
|
|
||||||
Shape {
|
|
||||||
id: shape
|
|
||||||
width: parent.implicitWidth
|
|
||||||
height: parent.implicitHeight
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
anchors.right: parent.right
|
|
||||||
layer.enabled: true
|
|
||||||
layer.samples: 4
|
|
||||||
|
|
||||||
ShapePath {
|
|
||||||
fillColor: AmneziaStyle.color.transparent
|
|
||||||
strokeColor: AmneziaStyle.color.charcoalGray
|
|
||||||
strokeWidth: 3
|
|
||||||
capStyle: ShapePath.RoundCap
|
|
||||||
|
|
||||||
PathAngleArc {
|
|
||||||
centerX: shape.width / 2
|
|
||||||
centerY: shape.height / 2
|
|
||||||
radiusX: 18
|
|
||||||
radiusY: 18
|
|
||||||
startAngle: 225
|
|
||||||
sweepAngle: -90
|
|
||||||
}
|
|
||||||
}
|
|
||||||
RotationAnimator {
|
|
||||||
target: shape
|
|
||||||
running: busyIndicator.visible && busyIndicator.running
|
|
||||||
from: 0
|
|
||||||
to: 360
|
|
||||||
loops: Animation.Infinite
|
|
||||||
duration: 1250
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,194 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Controls.impl
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
import Style 1.0
|
||||||
|
|
||||||
|
import "TextTypes"
|
||||||
|
|
||||||
|
Button {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
enum Variant { Primary, Secondary, Destructive, Text, Tonal, Surface }
|
||||||
|
enum Size { Large, Medium, Small }
|
||||||
|
|
||||||
|
property int variant: ButtonType.Primary
|
||||||
|
property int size: ButtonType.Large
|
||||||
|
property bool destructive: false
|
||||||
|
|
||||||
|
property string leftImageSource
|
||||||
|
property string rightImageSource
|
||||||
|
|
||||||
|
implicitHeight: d.height
|
||||||
|
|
||||||
|
horizontalPadding: 12
|
||||||
|
verticalPadding: 8
|
||||||
|
|
||||||
|
hoverEnabled: true
|
||||||
|
|
||||||
|
opacity: root.enabled ? 1 : d.disabledOpacity
|
||||||
|
|
||||||
|
QtObject {
|
||||||
|
id: d
|
||||||
|
|
||||||
|
readonly property bool small: root.size === ButtonType.Small
|
||||||
|
readonly property bool outlined: root.variant === ButtonType.Secondary || root.variant === ButtonType.Destructive
|
||||||
|
readonly property bool active: root.down || root.hovered
|
||||||
|
|
||||||
|
readonly property int height: root.size === ButtonType.Large ? 48 : (root.size === ButtonType.Medium ? 40 : 36)
|
||||||
|
readonly property int radius: root.size === ButtonType.Large ? 16 : (root.size === ButtonType.Medium ? 14 : 12)
|
||||||
|
readonly property int iconSize: small ? 20 : 24
|
||||||
|
readonly property int gap: root.size === ButtonType.Large ? 4 : 2
|
||||||
|
|
||||||
|
readonly property real disabledOpacity: {
|
||||||
|
switch (root.variant) {
|
||||||
|
case ButtonType.Primary: return 0.25
|
||||||
|
case ButtonType.Destructive: return 0.36
|
||||||
|
case ButtonType.Text: return 0.5
|
||||||
|
case ButtonType.Surface: return 0.5
|
||||||
|
default: return 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly property color backgroundColor: {
|
||||||
|
switch (root.variant) {
|
||||||
|
case ButtonType.Primary:
|
||||||
|
return root.down ? AmneziaStyle.color.buttonPrimaryPress
|
||||||
|
: (root.hovered ? AmneziaStyle.color.buttonPrimaryHover : AmneziaStyle.color.buttonPrimaryBg)
|
||||||
|
case ButtonType.Secondary:
|
||||||
|
case ButtonType.Destructive:
|
||||||
|
case ButtonType.Surface:
|
||||||
|
return root.down ? AmneziaStyle.color.bgPanel
|
||||||
|
: (root.hovered ? AmneziaStyle.color.bgSurfaceAlt : AmneziaStyle.color.bgSurface)
|
||||||
|
case ButtonType.Tonal:
|
||||||
|
return AmneziaStyle.color.bgSurfaceAlt
|
||||||
|
default:
|
||||||
|
return AmneziaStyle.color.transparent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly property bool stateLayerVisible: active && (root.variant === ButtonType.Text || root.variant === ButtonType.Tonal)
|
||||||
|
|
||||||
|
readonly property color borderColor: {
|
||||||
|
if (outlined) {
|
||||||
|
return active ? AmneziaStyle.color.borderBold : AmneziaStyle.color.borderSoft
|
||||||
|
}
|
||||||
|
if (root.variant === ButtonType.Text && root.down) {
|
||||||
|
return AmneziaStyle.color.borderEmphasis
|
||||||
|
}
|
||||||
|
return AmneziaStyle.color.transparent
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly property color labelColor: {
|
||||||
|
switch (root.variant) {
|
||||||
|
case ButtonType.Primary: return AmneziaStyle.color.textInverted
|
||||||
|
case ButtonType.Destructive: return AmneziaStyle.color.statusDestructive
|
||||||
|
case ButtonType.Text: return root.destructive ? AmneziaStyle.color.statusDestructive : AmneziaStyle.color.textAccent
|
||||||
|
case ButtonType.Secondary:
|
||||||
|
case ButtonType.Tonal:
|
||||||
|
return root.enabled ? AmneziaStyle.color.textSecondary : AmneziaStyle.color.textQuaternary
|
||||||
|
default: return AmneziaStyle.color.textSecondary
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly property color iconColor: {
|
||||||
|
switch (root.variant) {
|
||||||
|
case ButtonType.Primary: return AmneziaStyle.color.iconInverted
|
||||||
|
case ButtonType.Destructive: return AmneziaStyle.color.statusDestructive
|
||||||
|
case ButtonType.Text: return root.destructive ? AmneziaStyle.color.statusDestructive : AmneziaStyle.color.textAccent
|
||||||
|
case ButtonType.Secondary:
|
||||||
|
case ButtonType.Tonal:
|
||||||
|
return root.enabled ? AmneziaStyle.color.iconDefault : AmneziaStyle.color.iconTertiary
|
||||||
|
default: return AmneziaStyle.color.iconDefault
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
implicitHeight: d.height
|
||||||
|
|
||||||
|
radius: d.radius
|
||||||
|
color: d.backgroundColor
|
||||||
|
border.width: d.borderColor.a > 0 ? 1 : 0
|
||||||
|
border.color: d.borderColor
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
ColorAnimation { duration: 150 }
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
radius: parent.radius
|
||||||
|
color: AmneziaStyle.color.bgStateLayer
|
||||||
|
visible: d.stateLayerVisible
|
||||||
|
}
|
||||||
|
|
||||||
|
FocusRingType {
|
||||||
|
radius: d.radius
|
||||||
|
visible: root.visualFocus
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
contentItem: Item {
|
||||||
|
implicitWidth: row.implicitWidth
|
||||||
|
implicitHeight: row.implicitHeight
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: row
|
||||||
|
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: Math.min(implicitWidth, parent.width)
|
||||||
|
|
||||||
|
spacing: d.gap
|
||||||
|
|
||||||
|
IconImage {
|
||||||
|
Layout.preferredWidth: d.iconSize
|
||||||
|
Layout.preferredHeight: d.iconSize
|
||||||
|
|
||||||
|
visible: root.leftImageSource !== ""
|
||||||
|
source: root.leftImageSource
|
||||||
|
sourceSize: Qt.size(d.iconSize, d.iconSize)
|
||||||
|
color: d.iconColor
|
||||||
|
}
|
||||||
|
|
||||||
|
ButtonTextType {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
visible: text !== ""
|
||||||
|
text: root.text
|
||||||
|
|
||||||
|
leftPadding: 2
|
||||||
|
rightPadding: 2
|
||||||
|
|
||||||
|
color: d.labelColor
|
||||||
|
lineHeight: (d.small ? 18 : 24) + LanguageUiController.getLineHeightAppend()
|
||||||
|
font.pixelSize: d.small ? 14 : 16
|
||||||
|
font.weight: root.variant === ButtonType.Text ? 400 : 600
|
||||||
|
|
||||||
|
wrapMode: Text.NoWrap
|
||||||
|
elide: Text.ElideRight
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
IconImage {
|
||||||
|
Layout.preferredWidth: d.iconSize
|
||||||
|
Layout.preferredHeight: d.iconSize
|
||||||
|
|
||||||
|
visible: root.rightImageSource !== ""
|
||||||
|
source: root.rightImageSource
|
||||||
|
sourceSize: Qt.size(d.iconSize, d.iconSize)
|
||||||
|
color: d.iconColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HoverHandler {
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.onEnterPressed: root.clicked()
|
||||||
|
Keys.onReturnPressed: root.clicked()
|
||||||
|
}
|
||||||
@@ -1,8 +1,6 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import QtQuick.Window
|
|
||||||
import Qt5Compat.GraphicalEffects
|
|
||||||
|
|
||||||
import Style 1.0
|
import Style 1.0
|
||||||
|
|
||||||
@@ -34,20 +32,15 @@ Popup {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
leftMargin: 25
|
parent: Overlay.overlay
|
||||||
rightMargin: 25
|
|
||||||
bottomMargin: 70 + SettingsController.safeAreaBottomMargin
|
|
||||||
|
|
||||||
width: parent.width - leftMargin - rightMargin
|
width: parent ? parent.width : 0
|
||||||
|
height: Math.min(contentLayout.implicitHeight + 21 + 8 + PageController.safeAreaBottomMargin, parent ? parent.height - 20 : 0)
|
||||||
|
|
||||||
x: Math.round((parent.width - width) / 2)
|
x: 0
|
||||||
y: {
|
y: parent ? parent.height - height - root.keyboardHeight : 0
|
||||||
const centered = Math.round((parent.height - height) / 2)
|
|
||||||
if (root.keyboardHeight === 0) {
|
padding: 0
|
||||||
return centered
|
|
||||||
}
|
|
||||||
return Math.max(20, Math.min(centered, parent.height - root.keyboardHeight - height - 16))
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on y {
|
Behavior on y {
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
@@ -57,14 +50,15 @@ Popup {
|
|||||||
}
|
}
|
||||||
|
|
||||||
modal: true
|
modal: true
|
||||||
closePolicy: Popup.NoAutoClose
|
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
|
||||||
|
|
||||||
Overlay.modal: Rectangle {
|
Overlay.modal: Rectangle {
|
||||||
color: AmneziaStyle.color.translucentMidnightBlack
|
color: AmneziaStyle.color.bgOverlay
|
||||||
}
|
}
|
||||||
|
|
||||||
|
focus: true
|
||||||
|
|
||||||
onOpened: {
|
onOpened: {
|
||||||
timer.start()
|
|
||||||
solutionField.textField.text = ""
|
solutionField.textField.text = ""
|
||||||
solutionField.textField.forceActiveFocus()
|
solutionField.textField.forceActiveFocus()
|
||||||
}
|
}
|
||||||
@@ -81,155 +75,116 @@ Popup {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onClosed: {
|
enter: Transition {
|
||||||
FocusController.dropRootObject(root)
|
NumberAnimation { property: "opacity"; from: 0; to: 1; duration: 150 }
|
||||||
}
|
}
|
||||||
|
|
||||||
background: Rectangle {
|
exit: Transition {
|
||||||
anchors.fill: parent
|
NumberAnimation { property: "opacity"; from: 1; to: 0; duration: 150 }
|
||||||
color: AmneziaStyle.color.slateGray
|
|
||||||
radius: 22
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Timer {
|
background: Item {
|
||||||
id: timer
|
Rectangle {
|
||||||
interval: 200
|
anchors.fill: parent
|
||||||
onTriggered: {
|
anchors.bottomMargin: -border.width
|
||||||
FocusController.pushRootObject(root)
|
|
||||||
FocusController.setFocusItem(solutionField.textField)
|
topLeftRadius: 16
|
||||||
|
topRightRadius: 16
|
||||||
|
color: AmneziaStyle.color.bgSurfaceAlt
|
||||||
|
border.color: AmneziaStyle.color.borderFaint
|
||||||
|
border.width: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
SheetGrabberType {
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.topMargin: 16
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
}
|
}
|
||||||
repeat: false
|
|
||||||
running: true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
contentItem: Item {
|
contentItem: Item {
|
||||||
implicitWidth: contentLayout.implicitWidth
|
|
||||||
implicitHeight: contentLayout.implicitHeight
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: contentLayout
|
id: contentLayout
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.top: parent.top
|
||||||
anchors.leftMargin: 20
|
anchors.left: parent.left
|
||||||
anchors.rightMargin: 20
|
anchors.right: parent.right
|
||||||
anchors.topMargin: 20
|
anchors.topMargin: 21 + 16
|
||||||
anchors.bottomMargin: 20
|
anchors.leftMargin: 16
|
||||||
|
anchors.rightMargin: 16
|
||||||
|
|
||||||
spacing: 16
|
spacing: 0
|
||||||
|
|
||||||
Text {
|
H2TextType {
|
||||||
id: titleText
|
id: titleText
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
|
|
||||||
|
|
||||||
text: root.hint
|
text: root.hint
|
||||||
wrapMode: Text.WordWrap
|
|
||||||
color: AmneziaStyle.color.paleGray
|
|
||||||
font.pixelSize: 18
|
|
||||||
font.weight: Font.Bold
|
|
||||||
font.family: "PT Root UI VF"
|
|
||||||
lineHeight: 24 + LanguageUiController.getLineHeightAppend()
|
|
||||||
lineHeightMode: Text.FixedHeight
|
|
||||||
horizontalAlignment: Text.AlignLeft
|
|
||||||
verticalAlignment: Text.AlignTop
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Rectangle {
|
||||||
Layout.fillWidth: true
|
id: imagePanel
|
||||||
Layout.preferredHeight: 200
|
|
||||||
|
|
||||||
Rectangle {
|
Layout.fillWidth: true
|
||||||
id: imagePanel
|
Layout.preferredHeight: 128
|
||||||
|
Layout.topMargin: 32
|
||||||
|
|
||||||
|
color: AmneziaStyle.color.bgSurfaceInverse
|
||||||
|
radius: 16
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: captchaImage
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
color: AmneziaStyle.color.pearlGray
|
fillMode: Image.PreserveAspectFit
|
||||||
radius: 16
|
cache: false
|
||||||
|
|
||||||
Image {
|
Component.onCompleted: {
|
||||||
id: captchaImage
|
if (captchaImageBase64 !== "") {
|
||||||
|
source = "data:image/png;base64," + captchaImageBase64
|
||||||
anchors.centerIn: parent
|
|
||||||
fillMode: Image.PreserveAspectFit
|
|
||||||
cache: false
|
|
||||||
|
|
||||||
Component.onCompleted: {
|
|
||||||
if (captchaImageBase64 !== "") {
|
|
||||||
source = "data:image/png;base64," + captchaImageBase64
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: root
|
|
||||||
function onCaptchaImageBase64Changed() {
|
|
||||||
captchaImage.source = "data:image/png;base64," + root.captchaImageBase64
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BusyIndicator {
|
Connections {
|
||||||
anchors.centerIn: parent
|
target: root
|
||||||
running: captchaImage.status === Image.Loading
|
function onCaptchaImageBase64Changed() {
|
||||||
}
|
captchaImage.source = "data:image/png;base64," + root.captchaImageBase64
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: refreshHit
|
|
||||||
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
anchors.margins: 10
|
|
||||||
width: 44
|
|
||||||
height: 44
|
|
||||||
radius: width / 2
|
|
||||||
color: AmneziaStyle.color.charcoalGray
|
|
||||||
|
|
||||||
Image {
|
|
||||||
id: refreshIcon
|
|
||||||
|
|
||||||
anchors.centerIn: parent
|
|
||||||
width: 26
|
|
||||||
height: 26
|
|
||||||
fillMode: Image.PreserveAspectFit
|
|
||||||
smooth: true
|
|
||||||
mipmap: true
|
|
||||||
antialiasing: true
|
|
||||||
source: "qrc:/images/controls/refresh-cw.svg"
|
|
||||||
// Rasterize SVG at high resolution, then scale down — avoids blocky edges on HiDPI.
|
|
||||||
readonly property real _dpr: (Window.window && Window.window.screen)
|
|
||||||
? Window.window.screen.devicePixelRatio : 2.0
|
|
||||||
readonly property int _raster: Math.ceil(64 * Math.min(Math.max(_dpr, 1.0), 4.0))
|
|
||||||
sourceSize: Qt.size(_raster, _raster)
|
|
||||||
|
|
||||||
layer.enabled: true
|
|
||||||
layer.smooth: true
|
|
||||||
layer.textureSize: Qt.size(_raster, _raster)
|
|
||||||
layer.effect: ColorOverlay {
|
|
||||||
color: AmneziaStyle.color.goldenApricot
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
onClicked: root.refreshCaptchaRequested()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SpinnerType {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
color: AmneziaStyle.color.textInverted
|
||||||
|
visible: captchaImage.status === Image.Loading
|
||||||
|
}
|
||||||
|
|
||||||
|
IconButtonType {
|
||||||
|
id: refreshButton
|
||||||
|
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.rightMargin: 16
|
||||||
|
anchors.bottomMargin: 12
|
||||||
|
|
||||||
|
filled: true
|
||||||
|
icon.source: "qrc:/images/controls/refresh-cw.svg"
|
||||||
|
|
||||||
|
onClicked: root.refreshCaptchaRequested()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TextFieldWithHeaderType {
|
TextFieldType {
|
||||||
id: solutionField
|
id: solutionField
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.alignment: Qt.AlignLeft
|
Layout.topMargin: 26
|
||||||
|
|
||||||
headerText: qsTr("Digits from the image")
|
headerText: qsTr("Digits from the image")
|
||||||
headerTextColor: AmneziaStyle.color.mutedGray
|
|
||||||
|
|
||||||
textField.placeholderText: qsTr("_ _ _ _ _ _")
|
textField.placeholderText: qsTr("_ _ _ _ _ _")
|
||||||
textField.placeholderTextColor: AmneziaStyle.color.mutedGray
|
|
||||||
textField.inputMethodHints: Qt.ImhDigitsOnly | Qt.ImhNoPredictiveText
|
textField.inputMethodHints: Qt.ImhDigitsOnly | Qt.ImhNoPredictiveText
|
||||||
textField.maximumLength: 6
|
textField.maximumLength: 6
|
||||||
textField.validator: RegularExpressionValidator { regularExpression: /^[0-9]{0,6}$/ }
|
textField.validator: RegularExpressionValidator { regularExpression: /^[0-9]{0,6}$/ }
|
||||||
@@ -240,47 +195,18 @@ Popup {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
ButtonType {
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.preferredHeight: 8
|
|
||||||
}
|
|
||||||
|
|
||||||
BasicButtonType {
|
|
||||||
id: continueButton
|
id: continueButton
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
implicitHeight: 52
|
Layout.topMargin: 12
|
||||||
|
|
||||||
text: qsTr("Continue")
|
text: qsTr("Continue")
|
||||||
defaultColor: AmneziaStyle.color.paleGray
|
|
||||||
hoveredColor: AmneziaStyle.color.lightGray
|
|
||||||
pressedColor: AmneziaStyle.color.mutedGray
|
|
||||||
textColor: AmneziaStyle.color.midnightBlack
|
|
||||||
|
|
||||||
clickedFunc: function() {
|
onClicked: {
|
||||||
submitIfNonEmpty()
|
submitIfNonEmpty()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BasicButtonType {
|
|
||||||
id: closeButton
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
implicitHeight: 52
|
|
||||||
|
|
||||||
text: qsTr("Close")
|
|
||||||
defaultColor: AmneziaStyle.color.transparent
|
|
||||||
hoveredColor: AmneziaStyle.color.translucentWhite
|
|
||||||
pressedColor: AmneziaStyle.color.sheerWhite
|
|
||||||
textColor: AmneziaStyle.color.paleGray
|
|
||||||
borderWidth: 1
|
|
||||||
borderColor: AmneziaStyle.color.mutedGray
|
|
||||||
borderFocusedColor: AmneziaStyle.color.paleGray
|
|
||||||
|
|
||||||
clickedFunc: function() {
|
|
||||||
root.close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,166 +0,0 @@
|
|||||||
import QtQuick
|
|
||||||
import QtQuick.Controls
|
|
||||||
import QtQuick.Layouts
|
|
||||||
|
|
||||||
import Style 1.0
|
|
||||||
|
|
||||||
RadioButton {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property string headerText
|
|
||||||
property string bodyText
|
|
||||||
property string footerText
|
|
||||||
|
|
||||||
property string hoveredColor: AmneziaStyle.color.barelyTranslucentWhite
|
|
||||||
property string defaultColor: AmneziaStyle.color.transparent
|
|
||||||
property string disabledColor: AmneziaStyle.color.transparent
|
|
||||||
property string pressedColor: AmneziaStyle.color.barelyTranslucentWhite
|
|
||||||
property string selectedColor: AmneziaStyle.color.transparent
|
|
||||||
|
|
||||||
property string textColor: AmneziaStyle.color.midnightBlack
|
|
||||||
|
|
||||||
property string pressedBorderColor: AmneziaStyle.color.softGoldenApricot
|
|
||||||
property string selectedBorderColor: AmneziaStyle.color.goldenApricot
|
|
||||||
property string defaultBodredColor: AmneziaStyle.color.transparent
|
|
||||||
property string focusBorderColor: AmneziaStyle.color.paleGray
|
|
||||||
property int borderWidth: 0
|
|
||||||
|
|
||||||
implicitWidth: content.implicitWidth
|
|
||||||
implicitHeight: content.implicitHeight
|
|
||||||
|
|
||||||
hoverEnabled: true
|
|
||||||
|
|
||||||
property bool isFocusable: true
|
|
||||||
|
|
||||||
Keys.onTabPressed: {
|
|
||||||
FocusController.nextKeyTabItem()
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onBacktabPressed: {
|
|
||||||
FocusController.previousKeyTabItem()
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onUpPressed: {
|
|
||||||
FocusController.nextKeyUpItem()
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onDownPressed: {
|
|
||||||
FocusController.nextKeyDownItem()
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onLeftPressed: {
|
|
||||||
FocusController.nextKeyLeftItem()
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onRightPressed: {
|
|
||||||
FocusController.nextKeyRightItem()
|
|
||||||
}
|
|
||||||
|
|
||||||
indicator: Rectangle {
|
|
||||||
anchors.fill: parent
|
|
||||||
radius: 16
|
|
||||||
|
|
||||||
color: {
|
|
||||||
if (root.enabled) {
|
|
||||||
if (root.hovered) {
|
|
||||||
return hoveredColor
|
|
||||||
} else if (root.checked) {
|
|
||||||
return selectedColor
|
|
||||||
}
|
|
||||||
return defaultColor
|
|
||||||
} else {
|
|
||||||
return disabledColor
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
border.color: {
|
|
||||||
if (root.enabled) {
|
|
||||||
if (root.pressed) {
|
|
||||||
return pressedBorderColor
|
|
||||||
} else if (root.checked) {
|
|
||||||
return selectedBorderColor
|
|
||||||
} else if (root.activeFocus) {
|
|
||||||
return focusBorderColor
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return defaultBodredColor
|
|
||||||
}
|
|
||||||
|
|
||||||
border.width: {
|
|
||||||
if (root.enabled) {
|
|
||||||
if(root.checked || root.activeFocus) {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
return root.pressed ? 1 : 0
|
|
||||||
} else {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on color {
|
|
||||||
PropertyAnimation { duration: 200 }
|
|
||||||
}
|
|
||||||
Behavior on border.color {
|
|
||||||
PropertyAnimation { duration: 200 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
id: content
|
|
||||||
anchors.fill: parent
|
|
||||||
spacing: 16
|
|
||||||
|
|
||||||
Text {
|
|
||||||
text: root.headerText
|
|
||||||
wrapMode: Text.WordWrap
|
|
||||||
color: AmneziaStyle.color.paleGray
|
|
||||||
font.pixelSize: 25
|
|
||||||
font.weight: 700
|
|
||||||
font.family: "PT Root UI VF"
|
|
||||||
|
|
||||||
height: 30
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.rightMargin: 16
|
|
||||||
Layout.leftMargin: 16
|
|
||||||
Layout.topMargin: 16
|
|
||||||
}
|
|
||||||
|
|
||||||
Text {
|
|
||||||
text: root.bodyText
|
|
||||||
wrapMode: Text.WordWrap
|
|
||||||
color: AmneziaStyle.color.paleGray
|
|
||||||
font.pixelSize: 16
|
|
||||||
font.weight: 400
|
|
||||||
font.family: "PT Root UI VF"
|
|
||||||
|
|
||||||
height: 24
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.rightMargin: 16
|
|
||||||
Layout.leftMargin: 16
|
|
||||||
Layout.bottomMargin: root.footerText !== "" ? 0 : 16
|
|
||||||
}
|
|
||||||
|
|
||||||
Text {
|
|
||||||
text: root.footerText
|
|
||||||
wrapMode: Text.WordWrap
|
|
||||||
visible: root.footerText !== ""
|
|
||||||
color: AmneziaStyle.color.mutedGray
|
|
||||||
font.pixelSize: 13
|
|
||||||
font.weight: 400
|
|
||||||
font.family: "PT Root UI VF"
|
|
||||||
|
|
||||||
height: 16
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.rightMargin: 16
|
|
||||||
Layout.leftMargin: 16
|
|
||||||
Layout.bottomMargin: 16
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
enabled: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,205 +0,0 @@
|
|||||||
import QtQuick
|
|
||||||
import QtQuick.Controls
|
|
||||||
import QtQuick.Layouts
|
|
||||||
|
|
||||||
import Style 1.0
|
|
||||||
|
|
||||||
import "TextTypes"
|
|
||||||
|
|
||||||
Button {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property string headerText
|
|
||||||
property string bodyText
|
|
||||||
property string footerText
|
|
||||||
|
|
||||||
property color headerTextColor: AmneziaStyle.color.paleGray
|
|
||||||
property color bodyTextColor: AmneziaStyle.color.mutedGray
|
|
||||||
property bool showRecommendedBadge: false
|
|
||||||
property string recommendedText: ""
|
|
||||||
|
|
||||||
property string hoveredColor: AmneziaStyle.color.slateGray
|
|
||||||
property string defaultColor: AmneziaStyle.color.onyxBlack
|
|
||||||
|
|
||||||
property string textColor: AmneziaStyle.color.midnightBlack
|
|
||||||
|
|
||||||
property string rightImageSource
|
|
||||||
property string rightImageColor: AmneziaStyle.color.paleGray
|
|
||||||
|
|
||||||
property string leftImageSource
|
|
||||||
|
|
||||||
property alias focusItem: rightImage
|
|
||||||
|
|
||||||
hoverEnabled: true
|
|
||||||
clip: false
|
|
||||||
|
|
||||||
readonly property real cardTextOpacity: !enabled ? 1.0 : pressed ? 0.7 : hovered ? 0.8 : 1.0
|
|
||||||
|
|
||||||
background: Rectangle {
|
|
||||||
id: backgroundRect
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
radius: 16
|
|
||||||
|
|
||||||
color: root.hovered && root.enabled ? root.hoveredColor : root.defaultColor
|
|
||||||
|
|
||||||
Behavior on color {
|
|
||||||
PropertyAnimation { duration: 200 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
contentItem: Item {
|
|
||||||
id: contentRoot
|
|
||||||
|
|
||||||
z: 1
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
|
|
||||||
readonly property bool badgeVisible: root.showRecommendedBadge && root.recommendedText !== ""
|
|
||||||
|
|
||||||
implicitHeight: layoutCol.implicitHeight
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
id: layoutCol
|
|
||||||
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
spacing: 0
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: badgeTopSpacer
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.preferredHeight: contentRoot.badgeVisible ? (recBadge.height / 2 + 8) : 0
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: recBadge
|
|
||||||
|
|
||||||
visible: contentRoot.badgeVisible
|
|
||||||
z: 2
|
|
||||||
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.leftMargin: 20
|
|
||||||
anchors.verticalCenter: parent.top
|
|
||||||
|
|
||||||
radius: 10
|
|
||||||
color: AmneziaStyle.color.softViolet
|
|
||||||
implicitHeight: recLabel.implicitHeight + 8
|
|
||||||
implicitWidth: recLabel.implicitWidth + 16
|
|
||||||
|
|
||||||
width: implicitWidth
|
|
||||||
height: implicitHeight
|
|
||||||
|
|
||||||
BadgeTextType {
|
|
||||||
id: recLabel
|
|
||||||
|
|
||||||
anchors.centerIn: parent
|
|
||||||
text: root.recommendedText
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
id: content
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
Image {
|
|
||||||
id: leftImage
|
|
||||||
source: leftImageSource
|
|
||||||
|
|
||||||
visible: leftImageSource !== ""
|
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
|
|
||||||
Layout.topMargin: 24
|
|
||||||
Layout.bottomMargin: 24
|
|
||||||
Layout.leftMargin: 24
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
|
|
||||||
ListItemTitleType {
|
|
||||||
text: root.headerText
|
|
||||||
visible: text !== ""
|
|
||||||
|
|
||||||
color: root.headerTextColor
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.rightMargin: 16
|
|
||||||
Layout.leftMargin: 16
|
|
||||||
Layout.topMargin: contentRoot.badgeVisible ? 0 : 16
|
|
||||||
Layout.bottomMargin: root.bodyText !== "" ? 0 : 16
|
|
||||||
|
|
||||||
opacity: root.cardTextOpacity
|
|
||||||
}
|
|
||||||
|
|
||||||
CaptionTextType {
|
|
||||||
text: root.bodyText
|
|
||||||
visible: text !== ""
|
|
||||||
|
|
||||||
color: root.bodyTextColor
|
|
||||||
textFormat: Text.RichText
|
|
||||||
onLinkActivated: function(link) {
|
|
||||||
Qt.openUrlExternally(link)
|
|
||||||
}
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.rightMargin: 16
|
|
||||||
Layout.leftMargin: 16
|
|
||||||
Layout.bottomMargin: root.footerText !== "" ? 0 : 8
|
|
||||||
|
|
||||||
opacity: root.cardTextOpacity
|
|
||||||
}
|
|
||||||
|
|
||||||
ButtonTextType {
|
|
||||||
text: root.footerText
|
|
||||||
visible: text !== ""
|
|
||||||
|
|
||||||
color: AmneziaStyle.color.mutedGray
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.rightMargin: 16
|
|
||||||
Layout.leftMargin: 16
|
|
||||||
Layout.topMargin: 16
|
|
||||||
Layout.bottomMargin: 16
|
|
||||||
|
|
||||||
opacity: root.cardTextOpacity
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ImageButtonType {
|
|
||||||
id: rightImage
|
|
||||||
|
|
||||||
implicitWidth: 40
|
|
||||||
implicitHeight: 40
|
|
||||||
|
|
||||||
hoverEnabled: false
|
|
||||||
image: rightImageSource
|
|
||||||
imageColor: rightImageColor
|
|
||||||
visible: rightImageSource ? true : false
|
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignRight | Qt.AlignTop
|
|
||||||
Layout.topMargin: 16
|
|
||||||
Layout.bottomMargin: 16
|
|
||||||
Layout.rightMargin: 16
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: rightImageBackground
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
radius: 12
|
|
||||||
color: root.pressed ? rightImage.pressedColor : root.hovered && root.enabled ? rightImage.hoveredColor : rightImage.defaultColor
|
|
||||||
|
|
||||||
Behavior on color {
|
|
||||||
PropertyAnimation { duration: 200 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
root.clicked()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import Qt5Compat.GraphicalEffects
|
import QtQuick.Controls.impl
|
||||||
|
|
||||||
import Style 1.0
|
import Style 1.0
|
||||||
|
|
||||||
@@ -21,20 +21,17 @@ ColumnLayout {
|
|||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
spacing: 8
|
spacing: 8
|
||||||
|
|
||||||
Image {
|
IconImage {
|
||||||
Layout.preferredWidth: 18
|
Layout.preferredWidth: 18
|
||||||
Layout.preferredHeight: 18
|
Layout.preferredHeight: 18
|
||||||
|
|
||||||
source: root.iconSource
|
source: root.iconSource
|
||||||
sourceSize: Qt.size(18, 18)
|
sourceSize: Qt.size(18, 18)
|
||||||
|
color: AmneziaStyle.color.statusSuccess
|
||||||
layer.enabled: true
|
|
||||||
layer.effect: ColorOverlay {
|
|
||||||
color: AmneziaStyle.color.accentSuccess
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AppH3EmphasizedTextType {
|
H3TextType {
|
||||||
|
font.weight: 600
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
text: root.title
|
text: root.title
|
||||||
@@ -52,7 +49,7 @@ ColumnLayout {
|
|||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
AppTextType {
|
BodyTextType {
|
||||||
Layout.alignment: Qt.AlignTop
|
Layout.alignment: Qt.AlignTop
|
||||||
Layout.preferredWidth: 24
|
Layout.preferredWidth: 24
|
||||||
|
|
||||||
@@ -62,7 +59,7 @@ ColumnLayout {
|
|||||||
text: "•"
|
text: "•"
|
||||||
}
|
}
|
||||||
|
|
||||||
AppTextType {
|
BodyTextType {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
color: AmneziaStyle.color.textTertiary
|
color: AmneziaStyle.color.textTertiary
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Controls.impl
|
||||||
|
import QtQuick.Effects
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import Qt5Compat.GraphicalEffects
|
|
||||||
|
|
||||||
import Style 1.0
|
import Style 1.0
|
||||||
|
|
||||||
@@ -11,179 +12,120 @@ CheckBox {
|
|||||||
id: root
|
id: root
|
||||||
|
|
||||||
property string descriptionText
|
property string descriptionText
|
||||||
property string descriptionTextColor: AmneziaStyle.color.mutedGray
|
|
||||||
property string descriptionTextDisabledColor: AmneziaStyle.color.charcoalGray
|
|
||||||
|
|
||||||
property string textColor: AmneziaStyle.color.paleGray
|
implicitWidth: implicitContentWidth + leftPadding + rightPadding
|
||||||
property string textDisabledColor: AmneziaStyle.color.mutedGray
|
implicitHeight: Math.max(implicitContentHeight, implicitIndicatorHeight) + topPadding + bottomPadding
|
||||||
|
|
||||||
property string hoveredColor: AmneziaStyle.color.barelyTranslucentWhite
|
topPadding: 8
|
||||||
property string defaultColor: AmneziaStyle.color.transparent
|
bottomPadding: 8
|
||||||
property string pressedColor: AmneziaStyle.color.barelyTranslucentWhite
|
leftPadding: implicitIndicatorWidth + spacing
|
||||||
|
rightPadding: 0
|
||||||
|
spacing: 12
|
||||||
|
|
||||||
property string defaultBorderColor: AmneziaStyle.color.paleGray
|
hoverEnabled: true
|
||||||
property string checkedBorderColor: AmneziaStyle.color.goldenApricot
|
focusPolicy: Qt.TabFocus
|
||||||
property string checkedBorderDisabledColor: AmneziaStyle.color.deepBrown
|
|
||||||
|
|
||||||
property string borderFocusedColor: AmneziaStyle.color.paleGray
|
opacity: root.enabled ? 1 : 0.5
|
||||||
|
|
||||||
property string checkedImageColor: AmneziaStyle.color.goldenApricot
|
indicator: Item {
|
||||||
property string pressedImageColor: AmneziaStyle.color.burntOrange
|
x: 0
|
||||||
property string defaultImageColor: AmneziaStyle.color.transparent
|
y: (root.height - height) / 2
|
||||||
property string checkedDisabledImageColor: AmneziaStyle.color.mutedBrown
|
|
||||||
|
|
||||||
property string imageSource: "qrc:/images/controls/check.svg"
|
implicitWidth: 40
|
||||||
|
implicitHeight: 40
|
||||||
|
|
||||||
property bool isFocusable: true
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
Keys.onTabPressed: {
|
radius: 12
|
||||||
FocusController.nextKeyTabItem()
|
visible: root.enabled && (root.hovered || root.down)
|
||||||
}
|
color: root.checked ? AmneziaStyle.color.bgStateLayerAccent : AmneziaStyle.color.bgStateLayer
|
||||||
|
|
||||||
Keys.onBacktabPressed: {
|
|
||||||
FocusController.previousKeyTabItem()
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onUpPressed: {
|
|
||||||
FocusController.nextKeyUpItem()
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onDownPressed: {
|
|
||||||
FocusController.nextKeyDownItem()
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onLeftPressed: {
|
|
||||||
FocusController.nextKeyLeftItem()
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onRightPressed: {
|
|
||||||
FocusController.nextKeyRightItem()
|
|
||||||
}
|
|
||||||
|
|
||||||
hoverEnabled: enabled ? true : false
|
|
||||||
focusPolicy: Qt.NoFocus
|
|
||||||
|
|
||||||
background: Rectangle {
|
|
||||||
color: AmneziaStyle.color.transparent
|
|
||||||
border.color: root.focus ? borderFocusedColor : AmneziaStyle.color.transparent
|
|
||||||
border.width: 1
|
|
||||||
radius: 16
|
|
||||||
}
|
|
||||||
|
|
||||||
indicator: Rectangle {
|
|
||||||
id: background
|
|
||||||
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
|
|
||||||
implicitWidth: 56
|
|
||||||
implicitHeight: 56
|
|
||||||
radius: 16
|
|
||||||
|
|
||||||
color: {
|
|
||||||
if (root.hovered) {
|
|
||||||
return hoveredColor
|
|
||||||
}
|
|
||||||
return defaultColor
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on color {
|
|
||||||
PropertyAnimation { duration: 200 }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: imageBorder
|
id: box
|
||||||
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
|
|
||||||
width: 24
|
width: 24
|
||||||
height: 24
|
height: 24
|
||||||
color: AmneziaStyle.color.transparent
|
radius: 6
|
||||||
border.color: root.checked ?
|
|
||||||
(root.enabled ?
|
color: root.checked ? AmneziaStyle.color.bgBase : AmneziaStyle.color.transparent
|
||||||
checkedBorderColor :
|
|
||||||
checkedBorderDisabledColor) :
|
|
||||||
defaultBorderColor
|
|
||||||
border.width: 1
|
border.width: 1
|
||||||
radius: 4
|
border.color: root.checked ? AmneziaStyle.color.accentDark : AmneziaStyle.color.borderBold
|
||||||
|
|
||||||
Image {
|
Behavior on border.color {
|
||||||
anchors.centerIn: parent
|
ColorAnimation { duration: 150 }
|
||||||
|
|
||||||
source: root.pressed ? imageSource : root.checked ? imageSource : ""
|
|
||||||
layer {
|
|
||||||
enabled: true
|
|
||||||
effect: ColorOverlay {
|
|
||||||
color: {
|
|
||||||
if (root.pressed) {
|
|
||||||
return root.pressedImageColor
|
|
||||||
} else if (root.checked) {
|
|
||||||
if (root.enabled) {
|
|
||||||
return root.checkedImageColor
|
|
||||||
} else {
|
|
||||||
return root.checkedDisabledImageColor
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return root.defaultImageColor
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MultiEffect {
|
||||||
|
anchors.fill: check
|
||||||
|
|
||||||
|
source: check
|
||||||
|
visible: root.checked
|
||||||
|
shadowEnabled: true
|
||||||
|
shadowColor: AmneziaStyle.color.accentLight
|
||||||
|
shadowBlur: 0.6
|
||||||
|
shadowHorizontalOffset: 0
|
||||||
|
shadowVerticalOffset: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
IconImage {
|
||||||
|
id: check
|
||||||
|
|
||||||
|
anchors.centerIn: box
|
||||||
|
|
||||||
|
width: 16
|
||||||
|
height: 16
|
||||||
|
|
||||||
|
visible: root.checked
|
||||||
|
source: "qrc:/images/controls/check.svg"
|
||||||
|
sourceSize: Qt.size(16, 16)
|
||||||
|
color: AmneziaStyle.color.accentLight
|
||||||
|
}
|
||||||
|
|
||||||
|
FocusRingType {
|
||||||
|
target: box
|
||||||
|
radius: 6
|
||||||
|
visible: root.visualFocus
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
contentItem: Item {
|
contentItem: ColumnLayout {
|
||||||
anchors.left: parent.left
|
spacing: 4
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.leftMargin: 8 + background.width
|
|
||||||
|
|
||||||
implicitHeight: content.implicitHeight
|
H3TextType {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
ColumnLayout {
|
visible: text !== ""
|
||||||
id: content
|
text: root.text
|
||||||
|
}
|
||||||
|
|
||||||
anchors.left: parent.left
|
SmallTextType {
|
||||||
anchors.right: parent.right
|
Layout.fillWidth: true
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
|
|
||||||
spacing: 4
|
visible: text !== ""
|
||||||
|
text: root.descriptionText
|
||||||
ListItemTitleType {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
text: root.text
|
|
||||||
color: root.enabled ? root.textColor : root.textDisabledColor
|
|
||||||
}
|
|
||||||
|
|
||||||
CaptionTextType {
|
|
||||||
id: description
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
text: root.descriptionText
|
|
||||||
color: root.enabled ? root.descriptionTextColor : root.descriptionTextDisabledColor
|
|
||||||
|
|
||||||
visible: root.descriptionText !== ""
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
HoverHandler {
|
||||||
anchors.fill: parent
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
enabled: false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Keys.onEnterPressed: event => handleSwitch(event)
|
Keys.onEnterPressed: function(event) {
|
||||||
Keys.onReturnPressed: event => handleSwitch(event)
|
|
||||||
Keys.onSpacePressed: event => handleSwitch(event)
|
|
||||||
|
|
||||||
function handleSwitch(event) {
|
|
||||||
if (!event.isAutoRepeat) {
|
if (!event.isAutoRepeat) {
|
||||||
root.checked = !root.checked
|
root.toggle()
|
||||||
root.checkedChanged()
|
root.toggled()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.onReturnPressed: function(event) {
|
||||||
|
if (!event.isAutoRepeat) {
|
||||||
|
root.toggle()
|
||||||
|
root.toggled()
|
||||||
}
|
}
|
||||||
event.accepted = true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
|
||||||
|
import Style 1.0
|
||||||
|
|
||||||
|
import "TextTypes"
|
||||||
|
|
||||||
|
Button {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property bool applied: false
|
||||||
|
|
||||||
|
implicitWidth: implicitContentWidth + leftPadding + rightPadding
|
||||||
|
implicitHeight: 34
|
||||||
|
|
||||||
|
horizontalPadding: 14
|
||||||
|
verticalPadding: 8
|
||||||
|
|
||||||
|
hoverEnabled: true
|
||||||
|
|
||||||
|
opacity: root.enabled ? 1 : 0.5
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
radius: 16
|
||||||
|
|
||||||
|
color: root.applied ? AmneziaStyle.color.accentLight : AmneziaStyle.color.bgSurfaceAlt
|
||||||
|
border.width: root.applied || root.checked || (root.hovered && !root.down) ? 1 : 0
|
||||||
|
border.color: root.applied || root.checked ? AmneziaStyle.color.accentLight : AmneziaStyle.color.borderBold
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
radius: parent.radius
|
||||||
|
visible: root.down && !root.applied
|
||||||
|
color: AmneziaStyle.color.bgStateLayer
|
||||||
|
}
|
||||||
|
|
||||||
|
FocusRingType {
|
||||||
|
radius: 16
|
||||||
|
visible: root.visualFocus
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
contentItem: SmallTextType {
|
||||||
|
text: root.text
|
||||||
|
color: {
|
||||||
|
if (root.applied) {
|
||||||
|
return AmneziaStyle.color.textInverted
|
||||||
|
}
|
||||||
|
return root.checked ? AmneziaStyle.color.textPrimary : AmneziaStyle.color.textSecondary
|
||||||
|
}
|
||||||
|
font.weight: root.applied ? 600 : 400
|
||||||
|
font.letterSpacing: root.applied ? -0.4 : 0
|
||||||
|
|
||||||
|
wrapMode: Text.NoWrap
|
||||||
|
elide: Text.ElideRight
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
HoverHandler {
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.onEnterPressed: root.clicked()
|
||||||
|
Keys.onReturnPressed: root.clicked()
|
||||||
|
}
|
||||||
@@ -35,6 +35,26 @@ Item {
|
|||||||
readonly property real listOcclusion: root.pinnedBottom - root.listView.y
|
readonly property real listOcclusion: root.pinnedBottom - root.listView.y
|
||||||
readonly property bool pinnedStuck: root.listScrolled >= root.collapsibleHeight
|
readonly property bool pinnedStuck: root.listScrolled >= root.collapsibleHeight
|
||||||
|
|
||||||
|
QtObject {
|
||||||
|
id: d
|
||||||
|
|
||||||
|
property real lastScrolled: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: root.listView
|
||||||
|
|
||||||
|
function onContentYChanged() {
|
||||||
|
d.lastScrolled = root.listScrolled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function keepAtTop() {
|
||||||
|
if (d.lastScrolled <= 0 && root.listScrolled !== 0) {
|
||||||
|
root.listView.contentY = root.listView.originY
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function clampContentY(value) {
|
function clampContentY(value) {
|
||||||
const maxY = root.listView.originY + Math.max(0, root.listView.contentHeight - root.listView.height)
|
const maxY = root.listView.originY + Math.max(0, root.listView.contentHeight - root.listView.height)
|
||||||
return Math.max(root.listView.originY, Math.min(value, maxY))
|
return Math.max(root.listView.originY, Math.min(value, maxY))
|
||||||
@@ -44,18 +64,20 @@ Item {
|
|||||||
id: headerComponent
|
id: headerComponent
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
readonly property real collapsibleHeight: collapsibleColumn.implicitHeight + 4
|
readonly property real collapsibleHeight: collapsibleColumn.implicitHeight + 12
|
||||||
|
|
||||||
width: root.listView.width
|
width: root.listView.width
|
||||||
height: collapsibleHeight + pinnedBlock.height
|
height: collapsibleHeight + pinnedBlock.height
|
||||||
|
|
||||||
|
onHeightChanged: Qt.callLater(root.keepAtTop)
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: collapsibleColumn
|
id: collapsibleColumn
|
||||||
|
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
anchors.topMargin: 4
|
anchors.topMargin: 12
|
||||||
|
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
@@ -88,9 +110,9 @@ Item {
|
|||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
|
|
||||||
color: AmneziaStyle.color.midnightBlack
|
color: AmneziaStyle.color.bgBase
|
||||||
|
|
||||||
implicitHeight: navigationRow.y + navigationRow.height + 4
|
implicitHeight: navigationRow.y + navigationRow.height
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@@ -103,16 +125,20 @@ Item {
|
|||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
anchors.topMargin: 20 + PageController.safeAreaTopMargin
|
anchors.topMargin: PageController.safeAreaTopMargin
|
||||||
height: backButton.implicitHeight
|
height: 48
|
||||||
|
|
||||||
BackButtonType {
|
IconButtonType {
|
||||||
id: backButton
|
id: backButton
|
||||||
objectName: "backButton"
|
objectName: "backButton"
|
||||||
|
|
||||||
anchors.left: parent.left
|
x: 16
|
||||||
anchors.right: navigationButtonsRow.left
|
y: 4
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
|
size: IconButtonType.Small
|
||||||
|
icon.source: "qrc:/images/controls/arrow-left.svg"
|
||||||
|
|
||||||
|
onClicked: PageController.closePage()
|
||||||
}
|
}
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
@@ -120,7 +146,9 @@ Item {
|
|||||||
|
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.rightMargin: 16
|
anchors.rightMargin: 16
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
y: 4
|
||||||
|
|
||||||
|
spacing: 8
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -133,7 +161,7 @@ Item {
|
|||||||
y: topBar.height + Math.max(0, root.collapsibleHeight - root.listScrolled)
|
y: topBar.height + Math.max(0, root.collapsibleHeight - root.listScrolled)
|
||||||
height: pinnedColumn.implicitHeight
|
height: pinnedColumn.implicitHeight
|
||||||
|
|
||||||
color: AmneziaStyle.color.midnightBlack
|
color: AmneziaStyle.color.bgBase
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@@ -151,30 +179,49 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Header1TextType {
|
H1TextType {
|
||||||
id: largeTitleMetrics
|
id: largeTitleMetrics
|
||||||
|
|
||||||
visible: false
|
visible: false
|
||||||
text: movingTitle.text
|
text: movingTitle.text
|
||||||
}
|
}
|
||||||
|
|
||||||
Header1TextType {
|
H1TextType {
|
||||||
id: movingTitle
|
id: movingTitle
|
||||||
|
|
||||||
readonly property real compactScale: 18 / 32
|
readonly property real compactScale: compactTitle.font.pixelSize / movingTitle.font.pixelSize
|
||||||
readonly property real titleScale: 1 - (1 - movingTitle.compactScale) * root.collapseProgress
|
readonly property real titleScale: 1 - (1 - movingTitle.compactScale) * root.collapseProgress
|
||||||
readonly property real expandedX: 16
|
readonly property real expandedX: 16
|
||||||
readonly property real compactX: (root.width - movingTitle.width * movingTitle.compactScale) / 2
|
readonly property real compactX: (root.width - compactTitle.width) / 2
|
||||||
readonly property real expandedY: topBar.height + 4 - Math.min(0, root.listScrolled)
|
readonly property real expandedY: topBar.height + 12 - Math.min(0, root.listScrolled)
|
||||||
readonly property real compactY: navigationRow.y
|
readonly property real compactY: navigationRow.y + (navigationRow.height - compactTitle.height) / 2
|
||||||
+ (navigationRow.height - movingTitle.implicitHeight * movingTitle.compactScale) / 2
|
readonly property real currentX: movingTitle.expandedX + (movingTitle.compactX - movingTitle.expandedX) * root.collapseProgress
|
||||||
|
readonly property real currentY: Math.max(movingTitle.compactY, movingTitle.expandedY - Math.max(0, root.listScrolled))
|
||||||
|
|
||||||
x: movingTitle.expandedX + (movingTitle.compactX - movingTitle.expandedX) * root.collapseProgress
|
x: movingTitle.currentX
|
||||||
y: Math.max(movingTitle.compactY, movingTitle.expandedY - Math.max(0, root.listScrolled))
|
y: movingTitle.currentY
|
||||||
width: Math.min(movingTitle.implicitWidth, root.width - 32)
|
width: Math.min(movingTitle.implicitWidth, root.width - 32)
|
||||||
|
|
||||||
transformOrigin: Item.TopLeft
|
transformOrigin: Item.TopLeft
|
||||||
scale: movingTitle.titleScale
|
scale: movingTitle.titleScale
|
||||||
|
opacity: 1 - root.collapseProgress
|
||||||
|
|
||||||
|
wrapMode: Text.NoWrap
|
||||||
|
maximumLineCount: 1
|
||||||
|
elide: Text.ElideRight
|
||||||
|
text: root.title
|
||||||
|
}
|
||||||
|
|
||||||
|
H3TextType {
|
||||||
|
id: compactTitle
|
||||||
|
|
||||||
|
x: movingTitle.currentX
|
||||||
|
y: movingTitle.currentY
|
||||||
|
width: Math.min(compactTitle.implicitWidth, root.width - 32)
|
||||||
|
|
||||||
|
transformOrigin: Item.TopLeft
|
||||||
|
scale: movingTitle.titleScale / movingTitle.compactScale
|
||||||
|
opacity: root.collapseProgress
|
||||||
|
|
||||||
wrapMode: Text.NoWrap
|
wrapMode: Text.NoWrap
|
||||||
maximumLineCount: 1
|
maximumLineCount: 1
|
||||||
|
|||||||
@@ -53,12 +53,12 @@ Menu {
|
|||||||
|
|
||||||
MenuItem {
|
MenuItem {
|
||||||
text: qsTr("C&ut")
|
text: qsTr("C&ut")
|
||||||
enabled: textObj.selectedText
|
enabled: textObj.selectedText.length > 0
|
||||||
onTriggered: textObj.cut()
|
onTriggered: textObj.cut()
|
||||||
}
|
}
|
||||||
MenuItem {
|
MenuItem {
|
||||||
text: qsTr("&Copy")
|
text: qsTr("&Copy")
|
||||||
enabled: textObj.selectedText
|
enabled: textObj.selectedText.length > 0
|
||||||
onTriggered: textObj.copy()
|
onTriggered: textObj.copy()
|
||||||
}
|
}
|
||||||
MenuItem {
|
MenuItem {
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
|
||||||
|
import Style 1.0
|
||||||
|
|
||||||
|
import "TextTypes"
|
||||||
|
|
||||||
|
AbstractButton {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property color textColor: AmneziaStyle.color.textTertiary
|
||||||
|
property bool emphasized: false
|
||||||
|
|
||||||
|
implicitWidth: label.implicitWidth + 24
|
||||||
|
implicitHeight: 48
|
||||||
|
|
||||||
|
hoverEnabled: true
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
radius: 16
|
||||||
|
color: root.hovered || root.down ? AmneziaStyle.color.bgStateLayer : AmneziaStyle.color.transparent
|
||||||
|
border.width: root.down ? 1 : 0
|
||||||
|
border.color: AmneziaStyle.color.borderMedium
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
ColorAnimation { duration: 150 }
|
||||||
|
}
|
||||||
|
|
||||||
|
FocusRingType {
|
||||||
|
radius: 16
|
||||||
|
visible: root.visualFocus
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
contentItem: BodyTextType {
|
||||||
|
id: label
|
||||||
|
|
||||||
|
text: root.text
|
||||||
|
color: root.textColor
|
||||||
|
font.weight: root.emphasized ? 600 : 400
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
wrapMode: Text.NoWrap
|
||||||
|
elide: Text.ElideRight
|
||||||
|
}
|
||||||
|
|
||||||
|
HoverHandler {
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.onEnterPressed: root.clicked()
|
||||||
|
Keys.onReturnPressed: root.clicked()
|
||||||
|
}
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
import QtQuick
|
|
||||||
import QtQuick.Layouts
|
|
||||||
|
|
||||||
import Style 1.0
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
Layout.leftMargin: 16
|
|
||||||
Layout.rightMargin: 16
|
|
||||||
|
|
||||||
height: 1
|
|
||||||
color: AmneziaStyle.color.slateGray
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import QtQuick
|
||||||
|
|
||||||
|
import Style 1.0
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property color color: AmneziaStyle.color.promoRecommendation
|
||||||
|
|
||||||
|
implicitWidth: 16
|
||||||
|
implicitHeight: 16
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
|
||||||
|
width: 6
|
||||||
|
height: 6
|
||||||
|
radius: width / 2
|
||||||
|
|
||||||
|
color: root.color
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,386 +0,0 @@
|
|||||||
import QtQuick
|
|
||||||
import QtQuick.Controls
|
|
||||||
import QtQuick.Layouts
|
|
||||||
|
|
||||||
import Style 1.0
|
|
||||||
|
|
||||||
import "TextTypes"
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
readonly property string drawerExpandedStateName: "expanded"
|
|
||||||
readonly property string drawerCollapsedStateName: "collapsed"
|
|
||||||
|
|
||||||
readonly property bool isOpened: isExpandedStateActive() || (isCollapsedStateActive() && (dragArea.drag.active === true))
|
|
||||||
readonly property bool isClosed: isCollapsedStateActive() && (dragArea.drag.active === false)
|
|
||||||
|
|
||||||
property Component collapsedStateContent
|
|
||||||
property Component expandedStateContent
|
|
||||||
|
|
||||||
property string defaultColor: AmneziaStyle.color.onyxBlack
|
|
||||||
property string borderColor: AmneziaStyle.color.slateGray
|
|
||||||
|
|
||||||
property real expandedHeight
|
|
||||||
property real collapsedHeight: 0
|
|
||||||
|
|
||||||
property int depthIndex: 0
|
|
||||||
|
|
||||||
signal cursorEntered
|
|
||||||
signal cursorExited
|
|
||||||
signal pressed(bool pressed, bool entered)
|
|
||||||
|
|
||||||
signal aboutToHide
|
|
||||||
signal aboutToShow
|
|
||||||
signal closeTriggered
|
|
||||||
signal openTriggered
|
|
||||||
signal closed
|
|
||||||
signal opened
|
|
||||||
|
|
||||||
function isExpandedStateActive() {
|
|
||||||
return isStateActive(drawerExpandedStateName)
|
|
||||||
}
|
|
||||||
|
|
||||||
function isCollapsedStateActive() {
|
|
||||||
return isStateActive(drawerCollapsedStateName)
|
|
||||||
}
|
|
||||||
|
|
||||||
function isStateActive(stateName) {
|
|
||||||
return drawerContent.state === stateName
|
|
||||||
}
|
|
||||||
|
|
||||||
function isDrawerType2(obj) {
|
|
||||||
return obj && typeof obj.drawerExpandedStateName !== "undefined" &&
|
|
||||||
typeof obj.drawerCollapsedStateName !== "undefined"
|
|
||||||
}
|
|
||||||
|
|
||||||
function isDescendantOfDrawer(obj) {
|
|
||||||
var current = obj
|
|
||||||
while (current && current !== root.parent) {
|
|
||||||
if (isDrawerType2(current)) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
current = current.parent
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
function findComponent(obj, typeCtor) {
|
|
||||||
if (!obj)
|
|
||||||
return null
|
|
||||||
|
|
||||||
if (isDrawerType2(obj) || isDescendantOfDrawer(obj))
|
|
||||||
return null
|
|
||||||
|
|
||||||
if (obj instanceof typeCtor)
|
|
||||||
return obj
|
|
||||||
|
|
||||||
if (obj.children && obj.children.length > 0) {
|
|
||||||
for (var i = 0; i < obj.children.length; i++) {
|
|
||||||
var matchingChildren = findComponent(obj.children[i], typeCtor)
|
|
||||||
if (matchingChildren) return matchingChildren
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (obj.contentItem) {
|
|
||||||
var matchingContentItem = findComponent(obj.contentItem, typeCtor)
|
|
||||||
if (matchingContentItem) return matchingContentItem
|
|
||||||
}
|
|
||||||
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
function setParentInteractive(value) {
|
|
||||||
var flickableType = findComponent(root.parent, Flickable)
|
|
||||||
var listViewType = findComponent(root.parent, ListView)
|
|
||||||
|
|
||||||
if (flickableType) flickableType.interactive = value
|
|
||||||
if (listViewType) listViewType.interactive = value
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: Qt.application
|
|
||||||
|
|
||||||
function onStateChanged() {
|
|
||||||
if (Qt.application.state !== Qt.ApplicationActive) {
|
|
||||||
if (dragArea.drag.active) {
|
|
||||||
dragArea.drag.target = null
|
|
||||||
dragArea.drag.target = drawerContent
|
|
||||||
}
|
|
||||||
if (isOpened && !isCollapsedStateActive()) {
|
|
||||||
root.closeTriggered()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: PageController
|
|
||||||
|
|
||||||
function onCloseTopDrawer() {
|
|
||||||
if (depthIndex === PageController.getDrawerDepth()) {
|
|
||||||
if (isCollapsedStateActive()) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
aboutToHide()
|
|
||||||
|
|
||||||
drawerContent.state = root.drawerCollapsedStateName
|
|
||||||
depthIndex = 0
|
|
||||||
closed()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: root
|
|
||||||
|
|
||||||
function onCloseTriggered() {
|
|
||||||
if (isCollapsedStateActive()) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
aboutToHide()
|
|
||||||
|
|
||||||
setParentInteractive(true)
|
|
||||||
|
|
||||||
closed()
|
|
||||||
}
|
|
||||||
|
|
||||||
function onClosed() {
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
root.aboutToShow()
|
|
||||||
|
|
||||||
setParentInteractive(false)
|
|
||||||
|
|
||||||
root.opened()
|
|
||||||
}
|
|
||||||
|
|
||||||
function onOpened() {
|
|
||||||
drawerContent.state = root.drawerExpandedStateName
|
|
||||||
|
|
||||||
if (isExpandedStateActive()) {
|
|
||||||
if (PageController.getInitialPageNavigationBarColor() !== 0xFF1C1D21) {
|
|
||||||
PageController.updateNavigationBarColor(0xFF1C1D21)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
depthIndex = PageController.incrementDrawerDepth()
|
|
||||||
FocusController.pushRootObject(root)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: background
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
color: root.isCollapsedStateActive() ? AmneziaStyle.color.transparent : AmneziaStyle.color.translucentMidnightBlack
|
|
||||||
|
|
||||||
Behavior on color {
|
|
||||||
PropertyAnimation { duration: 200 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
id: emptyArea
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
root.closeTriggered()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
id: dragArea
|
|
||||||
objectName: "dragArea"
|
|
||||||
|
|
||||||
anchors.fill: drawerContentBackground
|
|
||||||
hoverEnabled: true
|
|
||||||
|
|
||||||
enabled: drawerContent.implicitHeight > 0
|
|
||||||
|
|
||||||
drag.target: drawerContent
|
|
||||||
drag.axis: Drag.YAxis
|
|
||||||
drag.maximumY: root.height - root.collapsedHeight
|
|
||||||
drag.minimumY: root.height - root.expandedHeight
|
|
||||||
|
|
||||||
/** If drag area is released at any point other than min or max y, transition to the other state */
|
|
||||||
onReleased: {
|
|
||||||
if (isCollapsedStateActive() && drawerContent.y < dragArea.drag.maximumY) {
|
|
||||||
root.openTriggered()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (isExpandedStateActive() && drawerContent.y > dragArea.drag.minimumY) {
|
|
||||||
root.closeTriggered()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onEntered: {
|
|
||||||
root.cursorEntered()
|
|
||||||
}
|
|
||||||
onExited: {
|
|
||||||
root.cursorExited()
|
|
||||||
}
|
|
||||||
onPressedChanged: {
|
|
||||||
root.pressed(pressed, entered)
|
|
||||||
}
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
if (isCollapsedStateActive()) {
|
|
||||||
root.openTriggered()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: drawerContentBackground
|
|
||||||
objectName: "drawerContentBackground"
|
|
||||||
|
|
||||||
anchors { left: drawerContent.left; right: drawerContent.right; top: drawerContent.top }
|
|
||||||
height: root.height
|
|
||||||
radius: 16
|
|
||||||
color: root.defaultColor
|
|
||||||
border.color: root.borderColor
|
|
||||||
border.width: 1
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
width: parent.radius
|
|
||||||
height: parent.radius
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.left: parent.left
|
|
||||||
color: parent.color
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: drawerContent
|
|
||||||
objectName: "drawerContent"
|
|
||||||
|
|
||||||
Drag.active: dragArea.drag.active
|
|
||||||
anchors.right: root.right
|
|
||||||
anchors.left: root.left
|
|
||||||
|
|
||||||
state: root.drawerCollapsedStateName
|
|
||||||
|
|
||||||
states: [
|
|
||||||
State {
|
|
||||||
name: root.drawerCollapsedStateName
|
|
||||||
PropertyChanges {
|
|
||||||
target: drawerContent
|
|
||||||
implicitHeight: collapsedHeight
|
|
||||||
y: root.height - root.collapsedHeight
|
|
||||||
}
|
|
||||||
PropertyChanges {
|
|
||||||
target: background
|
|
||||||
color: AmneziaStyle.color.transparent
|
|
||||||
}
|
|
||||||
PropertyChanges {
|
|
||||||
target: dragArea
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
}
|
|
||||||
PropertyChanges {
|
|
||||||
target: emptyArea
|
|
||||||
enabled: false
|
|
||||||
visible: false
|
|
||||||
}
|
|
||||||
PropertyChanges {
|
|
||||||
target: collapsedLoader
|
|
||||||
// visible: true
|
|
||||||
}
|
|
||||||
PropertyChanges {
|
|
||||||
target: expandedLoader
|
|
||||||
visible: false
|
|
||||||
|
|
||||||
}
|
|
||||||
},
|
|
||||||
State {
|
|
||||||
name: root.drawerExpandedStateName
|
|
||||||
PropertyChanges {
|
|
||||||
target: drawerContent
|
|
||||||
implicitHeight: expandedHeight
|
|
||||||
y: dragArea.drag.minimumY
|
|
||||||
}
|
|
||||||
PropertyChanges {
|
|
||||||
target: background
|
|
||||||
color: Qt.rgba(14/255, 14/255, 17/255, 0.8)
|
|
||||||
}
|
|
||||||
PropertyChanges {
|
|
||||||
target: dragArea
|
|
||||||
cursorShape: Qt.ArrowCursor
|
|
||||||
}
|
|
||||||
PropertyChanges {
|
|
||||||
target: emptyArea
|
|
||||||
enabled: true
|
|
||||||
visible: true
|
|
||||||
}
|
|
||||||
PropertyChanges {
|
|
||||||
target: collapsedLoader
|
|
||||||
// visible: false
|
|
||||||
}
|
|
||||||
PropertyChanges {
|
|
||||||
target: expandedLoader
|
|
||||||
visible: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
transitions: [
|
|
||||||
Transition {
|
|
||||||
from: root.drawerCollapsedStateName
|
|
||||||
to: root.drawerExpandedStateName
|
|
||||||
PropertyAnimation {
|
|
||||||
target: drawerContent
|
|
||||||
properties: "y"
|
|
||||||
duration: 200
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Transition {
|
|
||||||
from: root.drawerExpandedStateName
|
|
||||||
to: root.drawerCollapsedStateName
|
|
||||||
PropertyAnimation {
|
|
||||||
target: drawerContent
|
|
||||||
properties: "y"
|
|
||||||
duration: 200
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
Loader {
|
|
||||||
id: collapsedLoader
|
|
||||||
|
|
||||||
sourceComponent: root.collapsedStateContent
|
|
||||||
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.left: parent.left
|
|
||||||
}
|
|
||||||
|
|
||||||
Loader {
|
|
||||||
id: expandedLoader
|
|
||||||
|
|
||||||
sourceComponent: root.expandedStateContent
|
|
||||||
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.left: parent.left
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Controls.impl
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
|
|
||||||
import Style 1.0
|
import Style 1.0
|
||||||
@@ -7,227 +8,150 @@ import Style 1.0
|
|||||||
import "TextTypes"
|
import "TextTypes"
|
||||||
import "../Config"
|
import "../Config"
|
||||||
|
|
||||||
Item {
|
AbstractButton {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property string text
|
enum Variant { Default, Primary }
|
||||||
property string textColor: AmneziaStyle.color.paleGray
|
|
||||||
property string textDisabledColor: AmneziaStyle.color.mutedGray
|
property int variant: DropDownType.Default
|
||||||
property int textMaximumLineCount: 2
|
|
||||||
property int textElide: Qt.ElideRight
|
|
||||||
|
|
||||||
property string descriptionText
|
property string descriptionText
|
||||||
property string descriptionTextColor: AmneziaStyle.color.mutedGray
|
property string placeholderText
|
||||||
property string descriptionTextDisabledColor: AmneziaStyle.color.charcoalGray
|
|
||||||
|
|
||||||
property string headerText
|
property string headerText
|
||||||
property string headerBackButtonImage
|
property string headerBackButtonImage
|
||||||
|
|
||||||
property var rootButtonClickedFunction
|
|
||||||
property string rootButtonImage: "qrc:/images/controls/chevron-down.svg"
|
|
||||||
property string rootButtonImageColor: AmneziaStyle.color.paleGray
|
|
||||||
property string rootButtonBackgroundColor: AmneziaStyle.color.onyxBlack
|
|
||||||
property string rootButtonBackgroundHoveredColor: AmneziaStyle.color.onyxBlack
|
|
||||||
property string rootButtonBackgroundPressedColor: AmneziaStyle.color.onyxBlack
|
|
||||||
|
|
||||||
property string borderFocusedColor: AmneziaStyle.color.paleGray
|
|
||||||
property int borderFocusedWidth: 1
|
|
||||||
|
|
||||||
property string rootButtonHoveredBorderColor: AmneziaStyle.color.charcoalGray
|
|
||||||
property string rootButtonDefaultBorderColor: AmneziaStyle.color.slateGray
|
|
||||||
property string rootButtonPressedBorderColor: AmneziaStyle.color.paleGray
|
|
||||||
|
|
||||||
property int rootButtonTextLeftMargins: 16
|
|
||||||
property int rootButtonTextTopMargin: 16
|
|
||||||
property int rootButtonTextBottomMargin: 16
|
|
||||||
|
|
||||||
property real drawerHeight: 0.9
|
property real drawerHeight: 0.9
|
||||||
property bool fitContent: false
|
property bool fitContent: false
|
||||||
property Item drawerParent
|
|
||||||
property Component listView
|
property Component listView
|
||||||
|
|
||||||
signal openTriggered
|
readonly property bool isPrimary: root.variant === DropDownType.Primary
|
||||||
signal closeTriggered
|
|
||||||
|
|
||||||
readonly property bool isFocusable: true
|
function open() {
|
||||||
|
menu.open()
|
||||||
Keys.onTabPressed: {
|
|
||||||
FocusController.nextKeyTabItem()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Keys.onBacktabPressed: {
|
function close() {
|
||||||
FocusController.previousKeyTabItem()
|
menu.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
Keys.onUpPressed: {
|
implicitWidth: implicitContentWidth + leftPadding + rightPadding
|
||||||
FocusController.nextKeyUpItem()
|
implicitHeight: implicitContentHeight + topPadding + bottomPadding
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onDownPressed: {
|
|
||||||
FocusController.nextKeyDownItem()
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onLeftPressed: {
|
|
||||||
FocusController.nextKeyLeftItem()
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onRightPressed: {
|
leftPadding: 16
|
||||||
FocusController.nextKeyRightItem()
|
rightPadding: root.isPrimary ? 4 : 16
|
||||||
}
|
topPadding: root.isPrimary ? 0 : 12
|
||||||
|
bottomPadding: root.isPrimary ? 0 : 12
|
||||||
|
|
||||||
Connections {
|
hoverEnabled: true
|
||||||
target: Qt.application
|
|
||||||
|
|
||||||
function onStateChanged() {
|
opacity: root.enabled ? 1 : 0.4
|
||||||
if (Qt.application.state !== Qt.ApplicationActive) {
|
|
||||||
if (!menu.isClosed) {
|
|
||||||
menu.closeTriggered()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
implicitWidth: rootButtonContent.implicitWidth
|
onClicked: menu.open()
|
||||||
implicitHeight: rootButtonContent.implicitHeight
|
|
||||||
|
|
||||||
onOpenTriggered: {
|
background: Rectangle {
|
||||||
menu.openTriggered()
|
|
||||||
}
|
|
||||||
|
|
||||||
onCloseTriggered: {
|
|
||||||
menu.closeTriggered()
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onEnterPressed: {
|
|
||||||
if (menu.isClosed) {
|
|
||||||
menu.openTriggered()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onReturnPressed: {
|
|
||||||
if (menu.isClosed) {
|
|
||||||
menu.openTriggered()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: focusBorder
|
|
||||||
|
|
||||||
color: AmneziaStyle.color.transparent
|
|
||||||
border.color: root.activeFocus ? root.borderFocusedColor : AmneziaStyle.color.transparent
|
|
||||||
border.width: root.activeFocus ? root.borderFocusedWidth : 0
|
|
||||||
anchors.fill: rootButtonContent
|
|
||||||
radius: 16
|
radius: 16
|
||||||
|
color: {
|
||||||
|
if (!root.isPrimary) {
|
||||||
|
return AmneziaStyle.color.bgSurface
|
||||||
|
}
|
||||||
|
if (root.down) {
|
||||||
|
return AmneziaStyle.color.buttonPrimaryPress
|
||||||
|
}
|
||||||
|
return root.hovered ? AmneziaStyle.color.buttonPrimaryHover : AmneziaStyle.color.buttonPrimaryBg
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
ColorAnimation { duration: 150 }
|
||||||
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: rootButtonBackground
|
anchors.fill: parent
|
||||||
|
|
||||||
anchors.fill: focusBorder
|
radius: parent.radius
|
||||||
anchors.margins: root.activeFocus ? 2 : 0
|
visible: !root.isPrimary && root.enabled && (root.hovered || root.down)
|
||||||
radius: root.activeFocus ? 14 : 16
|
color: AmneziaStyle.color.bgStateLayer
|
||||||
|
}
|
||||||
|
|
||||||
color: {
|
FocusRingType {
|
||||||
if (root.enabled) {
|
radius: 16
|
||||||
if (root.pressed) {
|
visible: root.visualFocus
|
||||||
return root.rootButtonBackgroundPressedColor
|
|
||||||
}
|
|
||||||
return root.hovered ? root.rootButtonBackgroundHoveredColor : root.rootButtonBackgroundColor
|
|
||||||
} else {
|
|
||||||
return AmneziaStyle.color.transparent
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
border.color: rootButtonDefaultBorderColor
|
|
||||||
border.width: 1
|
|
||||||
|
|
||||||
Behavior on border.color {
|
|
||||||
PropertyAnimation { duration: 200 }
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on color {
|
|
||||||
PropertyAnimation { duration: 200 }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
contentItem: RowLayout {
|
||||||
id: rootButtonContent
|
spacing: 8
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
spacing: 0
|
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
Layout.leftMargin: rootButtonTextLeftMargins
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: rootButtonTextTopMargin
|
Layout.alignment: Qt.AlignVCenter
|
||||||
Layout.bottomMargin: rootButtonTextBottomMargin
|
|
||||||
|
|
||||||
LabelTextType {
|
spacing: 4
|
||||||
|
|
||||||
|
CaptionTextType {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
horizontalAlignment: Text.AlignLeft
|
visible: text !== ""
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
|
|
||||||
visible: root.descriptionText !== ""
|
|
||||||
|
|
||||||
color: root.enabled ? root.descriptionTextColor : root.descriptionTextDisabledColor
|
|
||||||
text: root.descriptionText
|
text: root.descriptionText
|
||||||
}
|
}
|
||||||
|
|
||||||
ButtonTextType {
|
H3TextType {
|
||||||
id: buttonText
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
horizontalAlignment: Text.AlignLeft
|
visible: !root.isPrimary
|
||||||
verticalAlignment: Text.AlignVCenter
|
text: root.text !== "" ? root.text : root.placeholderText
|
||||||
|
color: root.text !== "" ? AmneziaStyle.color.textPrimary : AmneziaStyle.color.textQuaternary
|
||||||
|
maximumLineCount: 2
|
||||||
|
elide: Text.ElideRight
|
||||||
|
}
|
||||||
|
|
||||||
color: root.enabled ? root.textColor : root.textDisabledColor
|
ButtonTextType {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
visible: root.isPrimary
|
||||||
text: root.text
|
text: root.text
|
||||||
maximumLineCount: root.textMaximumLineCount
|
color: AmneziaStyle.color.textInverted
|
||||||
elide: root.textElide
|
maximumLineCount: 1
|
||||||
|
elide: Text.ElideRight
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageButtonType {
|
Item {
|
||||||
Layout.rightMargin: 16
|
Layout.preferredWidth: 48
|
||||||
|
Layout.preferredHeight: 48
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
|
||||||
implicitWidth: 40
|
IconImage {
|
||||||
implicitHeight: 40
|
anchors.centerIn: parent
|
||||||
|
|
||||||
hoverEnabled: false
|
width: 24
|
||||||
image: rootButtonImage
|
height: 24
|
||||||
imageColor: rootButtonImageColor
|
|
||||||
|
source: "qrc:/images/controls/chevron-down.svg"
|
||||||
|
sourceSize: Qt.size(24, 24)
|
||||||
|
color: root.isPrimary ? AmneziaStyle.color.iconInverted : AmneziaStyle.color.iconDefault
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
HoverHandler {
|
||||||
anchors.fill: rootButtonContent
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
hoverEnabled: root.enabled ? true : false
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
if (rootButtonClickedFunction && typeof rootButtonClickedFunction === "function") {
|
|
||||||
rootButtonClickedFunction()
|
|
||||||
} else {
|
|
||||||
menu.openTriggered()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DrawerType2 {
|
Keys.onEnterPressed: root.clicked()
|
||||||
|
Keys.onReturnPressed: root.clicked()
|
||||||
|
|
||||||
|
SheetType {
|
||||||
id: menu
|
id: menu
|
||||||
|
|
||||||
parent: drawerParent
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
property real measuredContentHeight: 0
|
property real measuredContentHeight: 0
|
||||||
expandedHeight: (root.fitContent && measuredContentHeight > 0)
|
expandedHeight: (root.fitContent && measuredContentHeight > 0)
|
||||||
? Math.min(measuredContentHeight, drawerParent.height * root.drawerHeight)
|
? Math.min(measuredContentHeight, windowHeight * root.drawerHeight)
|
||||||
: drawerParent.height * root.drawerHeight
|
: windowHeight * root.drawerHeight
|
||||||
|
|
||||||
expandedStateContent: Item {
|
Item {
|
||||||
id: container
|
id: container
|
||||||
implicitHeight: menu.expandedHeight
|
anchors.fill: parent
|
||||||
|
|
||||||
property real fitHeight: backButton.implicitHeight + titleLabel.implicitHeight
|
property real fitHeight: backButton.implicitHeight + titleLabel.implicitHeight
|
||||||
+ (listViewLoader.item ? listViewLoader.item.contentHeight : 0) + 48
|
+ (listViewLoader.item ? listViewLoader.item.contentHeight : 0) + 48
|
||||||
@@ -243,10 +167,10 @@ Item {
|
|||||||
BackButtonType {
|
BackButtonType {
|
||||||
id: backButton
|
id: backButton
|
||||||
backButtonImage: root.headerBackButtonImage
|
backButtonImage: root.headerBackButtonImage
|
||||||
backButtonFunction: function() { menu.closeTriggered() }
|
backButtonFunction: function() { menu.close() }
|
||||||
}
|
}
|
||||||
|
|
||||||
Header2Type {
|
SectionHeaderType {
|
||||||
id: titleLabel
|
id: titleLabel
|
||||||
Layout.leftMargin: 16
|
Layout.leftMargin: 16
|
||||||
Layout.rightMargin: 16
|
Layout.rightMargin: 16
|
||||||
|
|||||||
@@ -0,0 +1,82 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls.impl
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
import Style 1.0
|
||||||
|
|
||||||
|
import "TextTypes"
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property string iconSource: "qrc:/images/controls/search.svg"
|
||||||
|
property color iconColor: AmneziaStyle.color.iconSecondary
|
||||||
|
property string title
|
||||||
|
property string description
|
||||||
|
|
||||||
|
default property alias actions: actionsRow.data
|
||||||
|
|
||||||
|
implicitWidth: content.implicitWidth + 32
|
||||||
|
implicitHeight: content.implicitHeight + 64
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: content
|
||||||
|
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.verticalCenterOffset: -32
|
||||||
|
width: Math.min(parent.width - 32, 358)
|
||||||
|
|
||||||
|
spacing: 20
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
|
||||||
|
spacing: 2
|
||||||
|
|
||||||
|
IconImage {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
Layout.preferredWidth: 50
|
||||||
|
Layout.preferredHeight: 50
|
||||||
|
|
||||||
|
visible: root.iconSource !== ""
|
||||||
|
source: root.iconSource
|
||||||
|
sourceSize: Qt.size(50, 50)
|
||||||
|
color: root.iconColor
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
Layout.preferredWidth: Math.min(280, content.width)
|
||||||
|
|
||||||
|
spacing: 6
|
||||||
|
|
||||||
|
H3TextType {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
visible: text !== ""
|
||||||
|
text: root.title
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
SmallTextType {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
visible: text !== ""
|
||||||
|
text: root.description
|
||||||
|
color: AmneziaStyle.color.textSecondary
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: actionsRow
|
||||||
|
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
|
||||||
|
spacing: 16
|
||||||
|
visible: children.length > 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,18 +1,8 @@
|
|||||||
import QtQuick 2.12
|
import QtQuick
|
||||||
import QtQuick.Controls 2.12
|
import QtQuick.Controls
|
||||||
import "../Config"
|
|
||||||
|
|
||||||
Flickable {
|
Flickable {
|
||||||
id: fl
|
id: root
|
||||||
|
|
||||||
function ensureVisible(item) {
|
|
||||||
if (item.y < fl.contentY) {
|
|
||||||
fl.contentY = item.y - 40 // 40 is a top margin
|
|
||||||
} else if (item.y + item.height > fl.contentY + fl.height) {
|
|
||||||
fl.contentY = item.y + item.height - fl.height + 40 // 40 is a bottom margin
|
|
||||||
}
|
|
||||||
fl.returnToBounds()
|
|
||||||
}
|
|
||||||
|
|
||||||
clip: true
|
clip: true
|
||||||
width: parent.width
|
width: parent.width
|
||||||
@@ -22,10 +12,5 @@ Flickable {
|
|||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.rightMargin: 1
|
anchors.rightMargin: 1
|
||||||
|
|
||||||
Keys.onUpPressed: scrollBar.decrease()
|
ScrollBar.vertical: ScrollBarType {}
|
||||||
Keys.onDownPressed: scrollBar.increase()
|
|
||||||
|
|
||||||
ScrollBar.vertical: ScrollBarType {
|
|
||||||
id: scrollBar
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Effects
|
||||||
|
|
||||||
|
import Style 1.0
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property Item target: parent
|
||||||
|
property real radius: 16
|
||||||
|
property bool accent: false
|
||||||
|
|
||||||
|
anchors.fill: target
|
||||||
|
anchors.margins: -3
|
||||||
|
|
||||||
|
MultiEffect {
|
||||||
|
anchors.fill: ring
|
||||||
|
source: ring
|
||||||
|
visible: root.visible
|
||||||
|
shadowEnabled: true
|
||||||
|
shadowColor: ring.border.color
|
||||||
|
shadowBlur: 0.5
|
||||||
|
shadowHorizontalOffset: 0
|
||||||
|
shadowVerticalOffset: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: ring
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
radius: root.radius + 3
|
||||||
|
color: AmneziaStyle.color.transparent
|
||||||
|
border.width: 1
|
||||||
|
border.color: root.accent ? AmneziaStyle.color.accentLight : AmneziaStyle.color.borderEmphasis
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
import QtQuick
|
|
||||||
import QtQuick.Layouts
|
|
||||||
|
|
||||||
import Style 1.0
|
|
||||||
|
|
||||||
import "TextTypes"
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property string actionButtonImage
|
|
||||||
property var actionButtonFunction
|
|
||||||
|
|
||||||
property alias actionButton: headerActionButton
|
|
||||||
|
|
||||||
property string headerText
|
|
||||||
property string descriptionText
|
|
||||||
|
|
||||||
implicitWidth: content.implicitWidth
|
|
||||||
implicitHeight: content.implicitHeight
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
id: content
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
Header2TextType {
|
|
||||||
id: header
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
text: root.headerText
|
|
||||||
}
|
|
||||||
|
|
||||||
ImageButtonType {
|
|
||||||
id: headerActionButton
|
|
||||||
|
|
||||||
implicitWidth: 40
|
|
||||||
implicitHeight: 40
|
|
||||||
|
|
||||||
image: root.actionButtonImage
|
|
||||||
imageColor: AmneziaStyle.color.paleGray
|
|
||||||
|
|
||||||
visible: image ? true : false
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
if (actionButtonFunction && typeof actionButtonFunction === "function") {
|
|
||||||
actionButtonFunction()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ParagraphTextType {
|
|
||||||
id: description
|
|
||||||
|
|
||||||
Layout.topMargin: 16
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
text: root.descriptionText
|
|
||||||
|
|
||||||
color: AmneziaStyle.color.mutedGray
|
|
||||||
|
|
||||||
visible: root.descriptionText !== ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onEnterPressed: {
|
|
||||||
if (actionButtonFunction && typeof actionButtonFunction === "function") {
|
|
||||||
actionButtonFunction()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onReturnPressed: {
|
|
||||||
if (actionButtonFunction && typeof actionButtonFunction === "function") {
|
|
||||||
actionButtonFunction()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,153 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Controls.impl
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
import Style 1.0
|
||||||
|
|
||||||
|
import "TextTypes"
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property string headerText
|
||||||
|
property int headerTextFormat: Text.AutoText
|
||||||
|
property int headerHorizontalAlignment: Text.AlignLeft
|
||||||
|
property int headerTextMaximumLineCount: 2
|
||||||
|
property int headerTextElide: Qt.ElideRight
|
||||||
|
|
||||||
|
property string descriptionText
|
||||||
|
|
||||||
|
property string descriptionLinkText
|
||||||
|
property string descriptionLinkUrl
|
||||||
|
|
||||||
|
property string actionButtonImage
|
||||||
|
property bool showSwitcher: false
|
||||||
|
|
||||||
|
readonly property alias actionButton: actionButton
|
||||||
|
readonly property alias switcher: switcher
|
||||||
|
|
||||||
|
signal actionClicked()
|
||||||
|
signal switcherToggled(bool checked)
|
||||||
|
|
||||||
|
implicitWidth: content.implicitWidth
|
||||||
|
implicitHeight: content.implicitHeight
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: content
|
||||||
|
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
|
||||||
|
spacing: 8
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
spacing: 8
|
||||||
|
|
||||||
|
H1TextType {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
|
||||||
|
text: root.headerText
|
||||||
|
textFormat: root.headerTextFormat
|
||||||
|
horizontalAlignment: root.headerHorizontalAlignment
|
||||||
|
maximumLineCount: root.headerTextMaximumLineCount
|
||||||
|
elide: root.headerTextElide
|
||||||
|
}
|
||||||
|
|
||||||
|
IconButtonType {
|
||||||
|
id: actionButton
|
||||||
|
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
|
||||||
|
visible: root.actionButtonImage !== ""
|
||||||
|
icon.source: root.actionButtonImage
|
||||||
|
|
||||||
|
onClicked: root.actionClicked()
|
||||||
|
}
|
||||||
|
|
||||||
|
SwitchType {
|
||||||
|
id: switcher
|
||||||
|
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
|
||||||
|
visible: root.showSwitcher
|
||||||
|
|
||||||
|
onToggled: root.switcherToggled(switcher.checked)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BodyTextType {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
visible: root.descriptionText !== ""
|
||||||
|
|
||||||
|
text: root.descriptionText
|
||||||
|
color: AmneziaStyle.color.textSecondary
|
||||||
|
}
|
||||||
|
|
||||||
|
AbstractButton {
|
||||||
|
id: descriptionLink
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
visible: root.descriptionLinkText !== "" && root.descriptionLinkUrl !== ""
|
||||||
|
|
||||||
|
implicitHeight: linkRow.implicitHeight
|
||||||
|
hoverEnabled: true
|
||||||
|
|
||||||
|
text: root.descriptionLinkText
|
||||||
|
|
||||||
|
Accessible.role: Accessible.Link
|
||||||
|
|
||||||
|
background: null
|
||||||
|
|
||||||
|
contentItem: Item {
|
||||||
|
implicitHeight: linkRow.implicitHeight
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: linkRow
|
||||||
|
|
||||||
|
width: Math.min(implicitWidth, parent.width)
|
||||||
|
|
||||||
|
spacing: 8
|
||||||
|
|
||||||
|
BodyTextType {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
text: descriptionLink.text
|
||||||
|
color: AmneziaStyle.color.textAccent
|
||||||
|
font.underline: descriptionLink.hovered
|
||||||
|
}
|
||||||
|
|
||||||
|
IconImage {
|
||||||
|
Layout.preferredWidth: 14
|
||||||
|
Layout.preferredHeight: 14
|
||||||
|
|
||||||
|
source: "qrc:/images/controls/external-link.svg"
|
||||||
|
sourceSize: Qt.size(14, 14)
|
||||||
|
color: AmneziaStyle.color.textAccent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FocusRingType {
|
||||||
|
target: linkRow
|
||||||
|
radius: 4
|
||||||
|
visible: descriptionLink.visualFocus
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HoverHandler {
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
}
|
||||||
|
|
||||||
|
onClicked: Qt.openUrlExternally(root.descriptionLinkUrl)
|
||||||
|
|
||||||
|
Keys.onEnterPressed: descriptionLink.clicked()
|
||||||
|
Keys.onReturnPressed: descriptionLink.clicked()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
import QtQuick
|
|
||||||
import QtQuick.Layouts
|
|
||||||
|
|
||||||
import Style 1.0
|
|
||||||
|
|
||||||
BaseHeaderType {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property string actionButtonImage
|
|
||||||
property var actionButtonFunction
|
|
||||||
property alias actionButton: headerActionButton
|
|
||||||
|
|
||||||
Component.onCompleted: {
|
|
||||||
headerRow.children.push(headerActionButton)
|
|
||||||
}
|
|
||||||
|
|
||||||
ImageButtonType {
|
|
||||||
id: headerActionButton
|
|
||||||
implicitWidth: 40
|
|
||||||
implicitHeight: 40
|
|
||||||
Layout.alignment: Qt.AlignRight
|
|
||||||
image: root.actionButtonImage
|
|
||||||
imageColor: AmneziaStyle.color.paleGray
|
|
||||||
visible: image ? true : false
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
if (actionButtonFunction && typeof actionButtonFunction === "function") {
|
|
||||||
actionButtonFunction()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onEnterPressed: {
|
|
||||||
if (actionButtonFunction && typeof actionButtonFunction === "function") {
|
|
||||||
actionButtonFunction()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onReturnPressed: {
|
|
||||||
if (actionButtonFunction && typeof actionButtonFunction === "function") {
|
|
||||||
actionButtonFunction()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
import QtQuick
|
|
||||||
import QtQuick.Layouts
|
|
||||||
|
|
||||||
import Style 1.0
|
|
||||||
|
|
||||||
BaseHeaderType {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property var switcherFunction
|
|
||||||
property bool showSwitcher: false
|
|
||||||
property alias switcher: headerSwitcher
|
|
||||||
|
|
||||||
Component.onCompleted: {
|
|
||||||
headerRow.children.push(headerSwitcher)
|
|
||||||
}
|
|
||||||
|
|
||||||
SwitcherType {
|
|
||||||
id: headerSwitcher
|
|
||||||
Layout.alignment: Qt.AlignRight
|
|
||||||
visible: root.showSwitcher
|
|
||||||
|
|
||||||
onToggled: {
|
|
||||||
if (switcherFunction && typeof switcherFunction === "function") {
|
|
||||||
switcherFunction(checked)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,139 +0,0 @@
|
|||||||
import QtQuick
|
|
||||||
import QtQuick.Controls
|
|
||||||
import QtQuick.Layouts
|
|
||||||
|
|
||||||
import Style 1.0
|
|
||||||
|
|
||||||
import "TextTypes"
|
|
||||||
|
|
||||||
RadioButton {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property string hoveredColor: AmneziaStyle.color.barelyTranslucentWhite
|
|
||||||
property string defaultColor: AmneziaStyle.color.transparent
|
|
||||||
property string checkedColor: AmneziaStyle.color.transparent
|
|
||||||
property string disabledColor: AmneziaStyle.color.transparent
|
|
||||||
|
|
||||||
property string textColor: AmneziaStyle.color.paleGray
|
|
||||||
property string textDisabledColor: AmneziaStyle.color.mutedGray
|
|
||||||
|
|
||||||
property string pressedBorderColor: AmneziaStyle.color.charcoalGray
|
|
||||||
property string checkedBorderColor: AmneziaStyle.color.goldenApricot
|
|
||||||
property string defaultBodredColor: AmneziaStyle.color.transparent
|
|
||||||
property string checkedDisabledBorderColor: AmneziaStyle.color.mutedBrown
|
|
||||||
property string borderFocusedColor: AmneziaStyle.color.paleGray
|
|
||||||
property int borderWidth: 0
|
|
||||||
|
|
||||||
implicitWidth: content.implicitWidth
|
|
||||||
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 {
|
|
||||||
anchors.fill: parent
|
|
||||||
radius: 16
|
|
||||||
|
|
||||||
color: {
|
|
||||||
if (root.enabled) {
|
|
||||||
if (root.hovered) {
|
|
||||||
return root.hoveredColor
|
|
||||||
} else if (root.checked) {
|
|
||||||
return root.checkedColor
|
|
||||||
}
|
|
||||||
return root.defaultColor
|
|
||||||
} else {
|
|
||||||
return root.disabledColor
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
border.color: {
|
|
||||||
if (root.enabled) {
|
|
||||||
if (root.pressed) {
|
|
||||||
return root.pressedBorderColor
|
|
||||||
} else if (root.checked) {
|
|
||||||
return root.checkedBorderColor
|
|
||||||
} else if (root.activeFocus) {
|
|
||||||
return root.borderFocusedColor
|
|
||||||
}
|
|
||||||
return root.defaultBodredColor
|
|
||||||
} else {
|
|
||||||
if (root.checked) {
|
|
||||||
return root.checkedDisabledBorderColor
|
|
||||||
}
|
|
||||||
return root.defaultBodredColor
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
border.width: {
|
|
||||||
if(root.checked || root.activeFocus) {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
return root.pressed ? 1 : 0
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on color {
|
|
||||||
PropertyAnimation { duration: 200 }
|
|
||||||
}
|
|
||||||
Behavior on border.color {
|
|
||||||
PropertyAnimation { duration: 200 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
id: content
|
|
||||||
anchors.fill: parent
|
|
||||||
spacing: 0
|
|
||||||
|
|
||||||
ButtonTextType {
|
|
||||||
text: root.text
|
|
||||||
color: root.enabled ? root.textColor : root.textDisabledColor
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.rightMargin: 24
|
|
||||||
Layout.leftMargin: 24
|
|
||||||
Layout.topMargin: 12
|
|
||||||
Layout.bottomMargin: 12
|
|
||||||
|
|
||||||
horizontalAlignment: Qt.AlignHCenter
|
|
||||||
verticalAlignment: Qt.AlignVCenter
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
enabled: false
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onEnterPressed: {
|
|
||||||
this.clicked()
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onReturnPressed: {
|
|
||||||
this.clicked()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Controls.impl
|
||||||
|
|
||||||
|
import Style 1.0
|
||||||
|
|
||||||
|
Button {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
enum Size { Small, Regular }
|
||||||
|
|
||||||
|
property int size: IconButtonType.Regular
|
||||||
|
property bool filled: false
|
||||||
|
|
||||||
|
implicitWidth: d.box
|
||||||
|
implicitHeight: d.box
|
||||||
|
|
||||||
|
padding: 0
|
||||||
|
|
||||||
|
hoverEnabled: true
|
||||||
|
display: AbstractButton.IconOnly
|
||||||
|
|
||||||
|
icon.width: d.iconSize
|
||||||
|
icon.height: d.iconSize
|
||||||
|
icon.color: root.filled ? AmneziaStyle.color.iconInverted : AmneziaStyle.color.iconDefault
|
||||||
|
|
||||||
|
opacity: root.enabled ? 1 : 0.5
|
||||||
|
|
||||||
|
QtObject {
|
||||||
|
id: d
|
||||||
|
|
||||||
|
readonly property int box: root.size === IconButtonType.Small ? 36 : 48
|
||||||
|
readonly property int iconSize: root.size === IconButtonType.Small ? 20 : 24
|
||||||
|
}
|
||||||
|
|
||||||
|
background: Item {
|
||||||
|
implicitWidth: d.box
|
||||||
|
implicitHeight: d.box
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: root.filled && root.down ? 2 : 0
|
||||||
|
|
||||||
|
radius: root.filled && root.down ? 10 : 12
|
||||||
|
color: {
|
||||||
|
if (root.filled) {
|
||||||
|
return root.hovered || root.down ? AmneziaStyle.color.bgSurfaceInverseAlt : AmneziaStyle.color.bgSurfaceInverse
|
||||||
|
}
|
||||||
|
return root.hovered || root.down ? AmneziaStyle.color.bgStateLayer : AmneziaStyle.color.transparent
|
||||||
|
}
|
||||||
|
border.width: !root.filled && root.down ? 1 : 0
|
||||||
|
border.color: AmneziaStyle.color.borderMedium
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
ColorAnimation { duration: 150 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
visible: root.filled && root.down
|
||||||
|
radius: 12
|
||||||
|
color: AmneziaStyle.color.transparent
|
||||||
|
border.width: 1
|
||||||
|
border.color: AmneziaStyle.color.borderEmphasis
|
||||||
|
}
|
||||||
|
|
||||||
|
FocusRingType {
|
||||||
|
radius: 12
|
||||||
|
visible: root.visualFocus
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
contentItem: Item {
|
||||||
|
implicitWidth: root.icon.width
|
||||||
|
implicitHeight: root.icon.height
|
||||||
|
|
||||||
|
IconImage {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
|
||||||
|
width: root.icon.width
|
||||||
|
height: root.icon.height
|
||||||
|
|
||||||
|
source: root.icon.source
|
||||||
|
sourceSize: Qt.size(root.icon.width, root.icon.height)
|
||||||
|
color: root.icon.color
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HoverHandler {
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.onEnterPressed: root.clicked()
|
||||||
|
Keys.onReturnPressed: root.clicked()
|
||||||
|
}
|
||||||
@@ -1,94 +0,0 @@
|
|||||||
import QtQuick
|
|
||||||
import QtQuick.Controls
|
|
||||||
import QtQuick.Layouts
|
|
||||||
|
|
||||||
import Style 1.0
|
|
||||||
|
|
||||||
Button {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property string image
|
|
||||||
|
|
||||||
property string hoveredColor: AmneziaStyle.color.translucentWhite
|
|
||||||
property string defaultColor: AmneziaStyle.color.transparent
|
|
||||||
property string pressedColor: AmneziaStyle.color.sheerWhite
|
|
||||||
property string disableColor: AmneziaStyle.color.slateGray
|
|
||||||
|
|
||||||
property string imageColor: AmneziaStyle.color.mutedGray
|
|
||||||
property string disableImageColor: AmneziaStyle.color.slateGray
|
|
||||||
|
|
||||||
property alias backgroundColor: background.color
|
|
||||||
property alias backgroundRadius: background.radius
|
|
||||||
|
|
||||||
property string borderFocusedColor: AmneziaStyle.color.paleGray
|
|
||||||
property int borderFocusedWidth: 1
|
|
||||||
|
|
||||||
hoverEnabled: true
|
|
||||||
|
|
||||||
icon.source: image
|
|
||||||
icon.color: root.enabled ? imageColor : disableImageColor
|
|
||||||
|
|
||||||
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()
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onEnterPressed: root.clicked()
|
|
||||||
Keys.onReturnPressed: root.clicked()
|
|
||||||
|
|
||||||
Behavior on icon.color {
|
|
||||||
PropertyAnimation { duration: 200 }
|
|
||||||
}
|
|
||||||
|
|
||||||
background: Rectangle {
|
|
||||||
id: background
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
border.color: root.activeFocus ? root.borderFocusedColor : AmneziaStyle.color.transparent
|
|
||||||
border.width: root.activeFocus ? root.borderFocusedWidth : 0
|
|
||||||
|
|
||||||
color: {
|
|
||||||
if (root.enabled) {
|
|
||||||
if (root.pressed) {
|
|
||||||
return pressedColor
|
|
||||||
}
|
|
||||||
return hovered ? hoveredColor : defaultColor
|
|
||||||
}
|
|
||||||
return defaultColor
|
|
||||||
}
|
|
||||||
radius: 12
|
|
||||||
Behavior on color {
|
|
||||||
PropertyAnimation { duration: 200 }
|
|
||||||
}
|
|
||||||
Behavior on border.color {
|
|
||||||
PropertyAnimation { duration: 200 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
enabled: false
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Controls.impl
|
||||||
|
|
||||||
|
import Style 1.0
|
||||||
|
|
||||||
|
import "TextTypes"
|
||||||
|
|
||||||
|
Button {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property bool joined: false
|
||||||
|
property string imageSource
|
||||||
|
|
||||||
|
implicitWidth: root.imageSource !== "" ? 48 : Math.max(83, implicitContentWidth + leftPadding + rightPadding)
|
||||||
|
implicitHeight: 64
|
||||||
|
|
||||||
|
horizontalPadding: 12
|
||||||
|
verticalPadding: 8
|
||||||
|
|
||||||
|
hoverEnabled: true
|
||||||
|
|
||||||
|
opacity: root.enabled ? 1 : 0.25
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
radius: 16
|
||||||
|
topLeftRadius: root.joined ? 0 : 16
|
||||||
|
bottomLeftRadius: root.joined ? 0 : 16
|
||||||
|
|
||||||
|
color: {
|
||||||
|
if (root.down) {
|
||||||
|
return AmneziaStyle.color.buttonPrimaryPress
|
||||||
|
}
|
||||||
|
return root.hovered ? AmneziaStyle.color.buttonPrimaryHover : AmneziaStyle.color.buttonPrimaryBg
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
ColorAnimation { duration: 150 }
|
||||||
|
}
|
||||||
|
|
||||||
|
FocusRingType {
|
||||||
|
radius: 16
|
||||||
|
visible: root.visualFocus
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
contentItem: Item {
|
||||||
|
implicitWidth: root.imageSource !== "" ? 24 : label.implicitWidth
|
||||||
|
implicitHeight: 24
|
||||||
|
|
||||||
|
IconImage {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
|
||||||
|
width: 24
|
||||||
|
height: 24
|
||||||
|
|
||||||
|
visible: root.imageSource !== ""
|
||||||
|
source: root.imageSource
|
||||||
|
sourceSize: Qt.size(24, 24)
|
||||||
|
color: AmneziaStyle.color.iconInverted
|
||||||
|
}
|
||||||
|
|
||||||
|
ButtonTextType {
|
||||||
|
id: label
|
||||||
|
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: Math.min(implicitWidth, parent.width)
|
||||||
|
|
||||||
|
visible: root.imageSource === "" && text !== ""
|
||||||
|
text: root.text
|
||||||
|
|
||||||
|
color: AmneziaStyle.color.textInverted
|
||||||
|
lineHeight: 18 + LanguageUiController.getLineHeightAppend()
|
||||||
|
font.pixelSize: 14
|
||||||
|
|
||||||
|
wrapMode: Text.NoWrap
|
||||||
|
elide: Text.ElideRight
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HoverHandler {
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.onEnterPressed: root.clicked()
|
||||||
|
Keys.onReturnPressed: root.clicked()
|
||||||
|
}
|
||||||
@@ -1,349 +0,0 @@
|
|||||||
import QtQuick
|
|
||||||
import QtQuick.Controls
|
|
||||||
import QtQuick.Layouts
|
|
||||||
import Qt5Compat.GraphicalEffects
|
|
||||||
|
|
||||||
import Style 1.0
|
|
||||||
|
|
||||||
import "TextTypes"
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
// property alias focusObjectName: eyeImage.objectName
|
|
||||||
property string text
|
|
||||||
property int textMaximumLineCount: 2
|
|
||||||
property int textElide: Qt.ElideRight
|
|
||||||
|
|
||||||
property string descriptionText
|
|
||||||
|
|
||||||
property var clickedFunction
|
|
||||||
|
|
||||||
property string buttonImageSource
|
|
||||||
property string rightImageSource
|
|
||||||
property string leftImageSource
|
|
||||||
property string rightWarningImageSource
|
|
||||||
property string rightWarningImageColor: AmneziaStyle.color.goldenApricot
|
|
||||||
property bool isLeftImageHoverEnabled: true
|
|
||||||
property bool isSmallLeftImage: false
|
|
||||||
|
|
||||||
property alias rightButton: rightImage
|
|
||||||
property alias eyeButton: eyeImage
|
|
||||||
|
|
||||||
property string textColor: AmneziaStyle.color.paleGray
|
|
||||||
property string textDisabledColor: AmneziaStyle.color.mutedGray
|
|
||||||
property string descriptionColor: AmneziaStyle.color.mutedGray
|
|
||||||
property string descriptionDisabledColor: AmneziaStyle.color.charcoalGray
|
|
||||||
property real textOpacity: 1.0
|
|
||||||
|
|
||||||
property string borderFocusedColor: AmneziaStyle.color.paleGray
|
|
||||||
property int borderFocusedWidth: 1
|
|
||||||
|
|
||||||
property string rightImageColor: AmneziaStyle.color.paleGray
|
|
||||||
property string leftImageColor: ""
|
|
||||||
|
|
||||||
property bool descriptionOnTop: false
|
|
||||||
property bool hideDescription: true
|
|
||||||
|
|
||||||
property bool isFocusable: !(eyeImage.visible || rightImage.visible) // TODO: this component already has focusable items
|
|
||||||
|
|
||||||
Keys.onTabPressed: {
|
|
||||||
FocusController.nextKeyTabItem()
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onBacktabPressed: {
|
|
||||||
FocusController.previousKeyTabItem()
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onUpPressed: {
|
|
||||||
FocusController.nextKeyUpItem()
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onDownPressed: {
|
|
||||||
FocusController.nextKeyDownItem()
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onLeftPressed: {
|
|
||||||
FocusController.nextKeyLeftItem()
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onRightPressed: {
|
|
||||||
FocusController.nextKeyRightItem()
|
|
||||||
}
|
|
||||||
|
|
||||||
implicitWidth: content.implicitWidth + content.anchors.topMargin + content.anchors.bottomMargin
|
|
||||||
implicitHeight: content.implicitHeight + content.anchors.leftMargin + content.anchors.rightMargin
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
id: mouseArea
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
hoverEnabled: root.enabled
|
|
||||||
|
|
||||||
onEntered: {
|
|
||||||
if (rightImageSource) {
|
|
||||||
rightImageBackground.color = rightImage.hoveredColor
|
|
||||||
} else if (leftImageSource) {
|
|
||||||
leftImageBackground.color = rightImage.hoveredColor
|
|
||||||
}
|
|
||||||
root.textOpacity = 0.8
|
|
||||||
}
|
|
||||||
|
|
||||||
onExited: {
|
|
||||||
if (rightImageSource) {
|
|
||||||
rightImageBackground.color = rightImage.defaultColor
|
|
||||||
} else if (leftImageSource) {
|
|
||||||
leftImageBackground.color = rightImage.defaultColor
|
|
||||||
}
|
|
||||||
root.textOpacity = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
onPressedChanged: {
|
|
||||||
if (rightImageSource) {
|
|
||||||
rightImageBackground.color = pressed ? rightImage.pressedColor : entered ? rightImage.hoveredColor : rightImage.defaultColor
|
|
||||||
} else if (leftImageSource) {
|
|
||||||
leftImageBackground.color = pressed ? rightImage.pressedColor : entered ? rightImage.hoveredColor : rightImage.defaultColor
|
|
||||||
}
|
|
||||||
root.textOpacity = 0.7
|
|
||||||
}
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
if (clickedFunction && typeof clickedFunction === "function") {
|
|
||||||
clickedFunction()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
id: content
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.leftMargin: 16
|
|
||||||
anchors.rightMargin: 16
|
|
||||||
anchors.topMargin: 16
|
|
||||||
anchors.bottomMargin: 16
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: leftImageBackground
|
|
||||||
|
|
||||||
visible: leftImageSource ? true : false
|
|
||||||
|
|
||||||
Layout.preferredHeight: (rightImageSource || !isLeftImageHoverEnabled || isSmallLeftImage) ? 40 : 56
|
|
||||||
Layout.preferredWidth: (rightImageSource || !isLeftImageHoverEnabled || isSmallLeftImage)? 40 : 56
|
|
||||||
Layout.rightMargin: isSmallLeftImage ? 8 : (rightImageSource || !isLeftImageHoverEnabled) ? 16 : 0
|
|
||||||
|
|
||||||
radius: 12
|
|
||||||
color: AmneziaStyle.color.transparent
|
|
||||||
|
|
||||||
Behavior on color {
|
|
||||||
PropertyAnimation { duration: 200 }
|
|
||||||
}
|
|
||||||
|
|
||||||
Image {
|
|
||||||
id: leftImage
|
|
||||||
|
|
||||||
anchors.centerIn: parent
|
|
||||||
source: leftImageSource
|
|
||||||
visible: leftImageColor === ""
|
|
||||||
}
|
|
||||||
|
|
||||||
ColorOverlay {
|
|
||||||
anchors.fill: leftImage
|
|
||||||
source: leftImage
|
|
||||||
color: leftImageColor
|
|
||||||
visible: leftImageColor !== ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
property real textLineHeight: 21.6
|
|
||||||
property real descriptionTextLineHeight: 16
|
|
||||||
|
|
||||||
property int textPixelSize: 18
|
|
||||||
property int descriptionTextSize: 13
|
|
||||||
|
|
||||||
ListItemTitleType {
|
|
||||||
text: root.text
|
|
||||||
color: {
|
|
||||||
if (root.enabled) {
|
|
||||||
return root.descriptionOnTop ? root.descriptionColor : root.textColor
|
|
||||||
} else {
|
|
||||||
return root.descriptionOnTop ? root.descriptionDisabledColor : root.textDisabledColor
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
maximumLineCount: root.textMaximumLineCount
|
|
||||||
elide: root.textElide
|
|
||||||
|
|
||||||
opacity: root.textOpacity
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
lineHeight: root.descriptionOnTop ? parent.descriptionTextLineHeight : parent.textLineHeight
|
|
||||||
font.pixelSize: root.descriptionOnTop ? parent.descriptionTextSize : parent.textPixelSize
|
|
||||||
font.letterSpacing: root.descriptionOnTop ? 0.02 : 0
|
|
||||||
|
|
||||||
horizontalAlignment: Text.AlignLeft
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
|
|
||||||
Behavior on opacity {
|
|
||||||
PropertyAnimation { duration: 200 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
CaptionTextType {
|
|
||||||
id: description
|
|
||||||
|
|
||||||
text: (eyeImage.visible && hideDescription) ? replaceWithAsterisks(root.descriptionText) : root.descriptionText
|
|
||||||
color: {
|
|
||||||
if (root.enabled) {
|
|
||||||
return root.descriptionOnTop ? root.textColor : root.descriptionColor
|
|
||||||
} else {
|
|
||||||
return root.descriptionOnTop ? root.textDisabledColor : root.descriptionDisabledColor
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
opacity: root.textOpacity
|
|
||||||
|
|
||||||
visible: root.descriptionText !== ""
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
lineHeight: root.descriptionOnTop ? parent.textLineHeight : parent.descriptionTextLineHeight
|
|
||||||
font.pixelSize: root.descriptionOnTop ? parent.textPixelSize : parent.descriptionTextSize
|
|
||||||
font.letterSpacing: root.descriptionOnTop ? 0 : 0.02
|
|
||||||
|
|
||||||
horizontalAlignment: Text.AlignLeft
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
|
|
||||||
Behavior on opacity {
|
|
||||||
PropertyAnimation { duration: 200 }
|
|
||||||
}
|
|
||||||
|
|
||||||
function replaceWithAsterisks(input) {
|
|
||||||
return '*'.repeat(input.length)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: rightWarningImageBackground
|
|
||||||
|
|
||||||
visible: rightWarningImageSource !== ""
|
|
||||||
|
|
||||||
Layout.preferredWidth: 40
|
|
||||||
Layout.preferredHeight: 40
|
|
||||||
Layout.alignment: Qt.AlignRight
|
|
||||||
|
|
||||||
radius: 12
|
|
||||||
color: AmneziaStyle.color.transparent
|
|
||||||
|
|
||||||
Image {
|
|
||||||
id: rightWarningImage
|
|
||||||
|
|
||||||
anchors.centerIn: parent
|
|
||||||
source: rightWarningImageSource
|
|
||||||
visible: false
|
|
||||||
}
|
|
||||||
|
|
||||||
ColorOverlay {
|
|
||||||
anchors.fill: rightWarningImage
|
|
||||||
source: rightWarningImage
|
|
||||||
color: rightWarningImageColor
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ImageButtonType {
|
|
||||||
id: eyeImage
|
|
||||||
visible: buttonImageSource !== ""
|
|
||||||
|
|
||||||
implicitWidth: 40
|
|
||||||
implicitHeight: 40
|
|
||||||
|
|
||||||
hoverEnabled: true
|
|
||||||
image: buttonImageSource
|
|
||||||
imageColor: rightImageColor
|
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignRight
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: eyeImageBackground
|
|
||||||
anchors.fill: parent
|
|
||||||
radius: 12
|
|
||||||
color: AmneziaStyle.color.transparent
|
|
||||||
|
|
||||||
Behavior on color {
|
|
||||||
PropertyAnimation { duration: 200 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
hideDescription = !hideDescription
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onEnterPressed: {
|
|
||||||
clicked()
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onReturnPressed: {
|
|
||||||
clicked()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ImageButtonType {
|
|
||||||
id: rightImage
|
|
||||||
|
|
||||||
implicitWidth: 40
|
|
||||||
implicitHeight: 40
|
|
||||||
|
|
||||||
hoverEnabled: false
|
|
||||||
image: rightImageSource
|
|
||||||
imageColor: rightImageColor
|
|
||||||
visible: rightImageSource ? true : false
|
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignRight
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: rightImageBackground
|
|
||||||
anchors.fill: parent
|
|
||||||
radius: 12
|
|
||||||
color: AmneziaStyle.color.transparent
|
|
||||||
|
|
||||||
Behavior on color {
|
|
||||||
PropertyAnimation { duration: 200 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onClicked: {
|
|
||||||
if (clickedFunction && typeof clickedFunction === "function") {
|
|
||||||
clickedFunction()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: background
|
|
||||||
anchors.fill: root
|
|
||||||
color: AmneziaStyle.color.transparent
|
|
||||||
|
|
||||||
border.color: root.activeFocus ? root.borderFocusedColor : AmneziaStyle.color.transparent
|
|
||||||
border.width: root.activeFocus ? root.borderFocusedWidth : 0
|
|
||||||
|
|
||||||
Behavior on color {
|
|
||||||
PropertyAnimation { duration: 200 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onEnterPressed: {
|
|
||||||
if (!rightImageSource && clickedFunction && typeof clickedFunction === "function") {
|
|
||||||
clickedFunction()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onReturnPressed: {
|
|
||||||
if (!rightImageSource && clickedFunction && typeof clickedFunction === "function") {
|
|
||||||
clickedFunction()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
import QtQuick
|
|
||||||
import QtQuick.Controls
|
|
||||||
import QtQuick.Layouts
|
|
||||||
|
|
||||||
import Style 1.0
|
|
||||||
|
|
||||||
import "TextTypes"
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property string imageSource
|
|
||||||
property string leftText
|
|
||||||
property var rightText
|
|
||||||
property bool isRightTextUndefined: rightText === undefined
|
|
||||||
property int rightTextFormat: Text.PlainText
|
|
||||||
|
|
||||||
visible: !isRightTextUndefined
|
|
||||||
|
|
||||||
Image {
|
|
||||||
Layout.preferredHeight: 18
|
|
||||||
Layout.preferredWidth: 18
|
|
||||||
source: root.imageSource
|
|
||||||
}
|
|
||||||
|
|
||||||
ListItemTitleType {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.rightMargin: 10
|
|
||||||
Layout.alignment: Qt.AlignRight
|
|
||||||
|
|
||||||
text: root.leftText
|
|
||||||
}
|
|
||||||
|
|
||||||
ParagraphTextType {
|
|
||||||
visible: root.rightText !== ""
|
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignLeft
|
|
||||||
|
|
||||||
text: root.isRightTextUndefined ? "" : root.rightText
|
|
||||||
textFormat: root.rightTextFormat
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Controls.impl
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
import Style 1.0
|
||||||
|
|
||||||
|
import "TextTypes"
|
||||||
|
|
||||||
|
AbstractButton {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
enum Size { Small, Medium }
|
||||||
|
|
||||||
|
property int size: LinkButtonType.Small
|
||||||
|
property bool colored: true
|
||||||
|
property string leftImageSource
|
||||||
|
property string rightImageSource
|
||||||
|
|
||||||
|
padding: 0
|
||||||
|
|
||||||
|
hoverEnabled: true
|
||||||
|
|
||||||
|
opacity: root.enabled ? 1 : 0.5
|
||||||
|
|
||||||
|
QtObject {
|
||||||
|
id: d
|
||||||
|
|
||||||
|
readonly property color contentColor: {
|
||||||
|
if (!root.colored) {
|
||||||
|
return AmneziaStyle.color.textTertiary
|
||||||
|
}
|
||||||
|
if (root.down) {
|
||||||
|
return AmneziaStyle.color.accentDeep
|
||||||
|
}
|
||||||
|
return root.hovered ? AmneziaStyle.color.accentDefault : AmneziaStyle.color.textAccent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
background: Item {
|
||||||
|
FocusRingType {
|
||||||
|
radius: 4
|
||||||
|
visible: root.visualFocus
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
contentItem: Item {
|
||||||
|
implicitWidth: row.implicitWidth
|
||||||
|
implicitHeight: row.implicitHeight
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: row
|
||||||
|
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: Math.min(implicitWidth, parent.width)
|
||||||
|
|
||||||
|
spacing: 8
|
||||||
|
|
||||||
|
IconImage {
|
||||||
|
Layout.preferredWidth: 14
|
||||||
|
Layout.preferredHeight: 14
|
||||||
|
|
||||||
|
visible: root.leftImageSource !== ""
|
||||||
|
source: root.leftImageSource
|
||||||
|
sourceSize: Qt.size(14, 14)
|
||||||
|
color: d.contentColor
|
||||||
|
}
|
||||||
|
|
||||||
|
SmallTextType {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
text: root.text
|
||||||
|
color: d.contentColor
|
||||||
|
font.letterSpacing: -0.4
|
||||||
|
font.pixelSize: root.size === LinkButtonType.Medium ? 16 : 14
|
||||||
|
lineHeight: (root.size === LinkButtonType.Medium ? 24 : 18) + LanguageUiController.getLineHeightAppend()
|
||||||
|
|
||||||
|
wrapMode: Text.NoWrap
|
||||||
|
elide: Text.ElideRight
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
IconImage {
|
||||||
|
Layout.preferredWidth: 14
|
||||||
|
Layout.preferredHeight: 14
|
||||||
|
|
||||||
|
visible: root.rightImageSource !== ""
|
||||||
|
source: root.rightImageSource
|
||||||
|
sourceSize: Qt.size(14, 14)
|
||||||
|
color: d.contentColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HoverHandler {
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.onEnterPressed: root.clicked()
|
||||||
|
Keys.onReturnPressed: root.clicked()
|
||||||
|
}
|
||||||
Loaded 100 of 241 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user