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:
vkamn authored and GitHub committed 2026-10-14 11:13:36 +08:00
1 parent 3e8077f2ca
commit 11355e1de0
241 files changed
+27663 -30989

No files matched your search

+2 -2
View File
@@ -5,7 +5,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(PROJECT AmneziaVPN)
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()
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})
# 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)
math(EXPR APP_ANDROID_VERSION_CODE "${APP_ANDROID_VERSION_CODE} + ${APP_ANDROID_VERSION_CODE_OFFSET}")
endif()
+1 -1
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="black">#FF0E0E11</color>
<color name="black">#FF101012</color>
<style name="NoActionBar">
<item name="android:windowBackground">@color/black</item>
<item name="android:colorBackground">@color/black</item>
-2
View File
@@ -66,7 +66,6 @@ set(HEADERS ${HEADERS}
${CLIENT_ROOT_DIR}/core/utils/serialization/transfer.h
${CLIENT_ROOT_DIR}/../common/logger/logger.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/countryCatalog.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}/../common/logger/logger.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/countryCatalog.cpp
${CLIENT_ROOT_DIR}/core/utils/api/gatewayPayloadBuilder.cpp
@@ -187,7 +187,6 @@ void CoreController::initControllers()
if (m_engine) {
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,
+3
View File
@@ -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

+6
View File
@@ -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

-5
View File
@@ -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

+2 -5
View File
@@ -1,6 +1,3 @@
<svg width="19" height="18" viewBox="0 0 19 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="0.5" width="18" height="18" rx="5" fill="white"/>
<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 width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<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"/>
</svg>

Before

Width:  |  Height:  |  Size: 511 B

After

Width:  |  Height:  |  Size: 1.3 KiB

+2 -1
View File
@@ -42,6 +42,7 @@
<file>controls/infinity.svg</file>
<file>controls/info.svg</file>
<file>controls/mail.svg</file>
<file>controls/mail-warning.svg</file>
<file>controls/map-pin.svg</file>
<file>controls/more-vertical.svg</file>
<file>controls/news.svg</file>
@@ -49,6 +50,7 @@
<file>controls/unread-dot.svg</file>
<file>controls/plus.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.png</file>
<file>controls/radio-button-pressed.svg</file>
@@ -62,7 +64,6 @@
<file>controls/server.svg</file>
<file>controls/settings-2.svg</file>
<file>controls/settings.svg</file>
<file>controls/settings-news.svg</file>
<file>controls/share-2.svg</file>
<file>controls/star.svg</file>
<file>controls/star-filled.svg</file>
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+118 -179
View File
@@ -1,215 +1,154 @@
#include "focusController.h"
#include "ui/utils/qmlUtils.h"
#include <QGuiApplication>
#include <QKeyEvent>
#include <QQmlApplicationEngine>
#include <QQuickItem>
#include <QQuickWindow>
#include <QStyleHints>
#include "logger.h"
#include <algorithm>
namespace {
Logger logger("FocusController");
namespace
{
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)
: QObject { parent },
m_engine { engine },
m_focusChain {},
m_focusedItem { nullptr },
m_rootObjects {},
m_defaultFocusItem { nullptr },
m_lvfc { nullptr }
FocusController::FocusController(QQmlApplicationEngine *engine, QObject *parent) : QObject { parent }
{
QObject::connect(m_engine, &QQmlApplicationEngine::objectCreated, this, [this](QObject *object, const QUrl &url) {
QQuickItem *newDefaultFocusItem = object->findChild<QQuickItem *>("defaultFocusItem");
if (newDefaultFocusItem && m_defaultFocusItem != newDefaultFocusItem) {
m_defaultFocusItem = newDefaultFocusItem;
QGuiApplication::styleHints()->setTabFocusBehavior(Qt::TabFocusAllControls);
connect(engine, &QQmlApplicationEngine::objectCreated, this, [this](QObject *object) {
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);
}
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;
if (event->type() != QEvent::KeyPress) {
return false;
}
emit focusedItemChanged();
auto *window = qobject_cast<QQuickWindow *>(watched);
auto *keyEvent = static_cast<QKeyEvent *>(event);
if (!window || (keyEvent->modifiers() & ~(Qt::ShiftModifier | Qt::KeypadModifier))) {
return false;
}
QQuickItem *current = window->activeFocusItem() ? window->activeFocusItem() : window->contentItem();
bool forward = false;
switch (keyEvent->key()) {
case Qt::Key_Tab: forward = true; break;
case Qt::Key_Backtab: forward = false; break;
case Qt::Key_Down:
case Qt::Key_Right: forward = true; [[fallthrough]];
case Qt::Key_Up:
case Qt::Key_Left:
if (wantsArrowKey(current, keyEvent->key())) {
return false;
}
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;
}
void FocusController::setFocusOnDefaultItem()
bool FocusController::wantsArrowKey(const QQuickItem *item, int key)
{
setFocusItem(m_defaultFocusItem);
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::pushRootObject(QObject *object)
void FocusController::sortListViewDelegates(QQuickItem *root)
{
m_rootObjects.push(object);
dropListView();
// setFocusOnDefaultItem();
}
void FocusController::dropRootObject(QObject *object)
{
if (m_rootObjects.empty()) {
if (!root->isVisible()) {
return;
}
if (m_rootObjects.top() == object) {
m_rootObjects.pop();
dropListView();
setFocusOnDefaultItem();
} else {
logger.warning() << "TRY TO DROP WRONG ROOT OBJECT: " << m_rootObjects.top() << " SHOULD BE: " << object;
if (root->inherits("QQuickListView")) {
if (auto *contentItem = root->property("contentItem").value<QQuickItem *>()) {
QList<QQuickItem *> delegates = contentItem->childItems();
const auto byPosition = [](const QQuickItem *a, const QQuickItem *b) {
return a->y() != b->y() ? a->y() < b->y() : a->x() < b->x();
};
if (!std::is_sorted(delegates.cbegin(), delegates.cend(), byPosition)) {
std::stable_sort(delegates.begin(), delegates.end(), byPosition);
for (qsizetype i = 1; i < delegates.size(); ++i) {
delegates.at(i)->stackAfter(delegates.at(i - 1));
}
}
}
}
const QList<QQuickItem *> children = root->childItems();
for (QQuickItem *child : children) {
sortListViewDelegates(child);
}
}
void FocusController::resetRootObject()
void FocusController::ensureVisible(QQuickItem *item)
{
m_rootObjects.clear();
for (QQuickItem *flickable = item->parentItem(); flickable; flickable = flickable->parentItem()) {
if (!flickable->inherits("QQuickFlickable")) {
continue;
}
auto *contentItem = flickable->property("contentItem").value<QQuickItem *>();
if (!contentItem) {
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");
}
}
void FocusController::reload(Direction direction)
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)
{
m_focusChain.clear();
const qreal current = flickable->property(position).toReal();
QObject *rootObject = (m_rootObjects.empty() ? m_engine->rootObjects().value(0) : m_rootObjects.top());
if (!rootObject) {
logger.error() << "No ROOT OBJECT found!";
resetRootObject();
dropListView();
return;
qreal target = current;
if (start < current) {
target = start - ensureVisibleMargin;
} else if (end > current + viewSize) {
target = std::min(end - viewSize + ensureVisibleMargin, start - ensureVisibleMargin);
}
m_focusChain.append(FocusControl::getSubChain(rootObject));
std::sort(m_focusChain.begin(), m_focusChain.end(),
direction == Direction::Forward ? FocusControl::isLess : FocusControl::isMore);
if (m_focusChain.empty()) {
logger.warning() << "Focus chain is empty!";
resetRootObject();
dropListView();
return;
}
}
void FocusController::nextItem(Direction direction)
{
reload(direction);
if (m_lvfc && FocusControl::isListView(m_focusedItem)) {
direction == Direction::Forward ? focusNextListViewItem() : focusPreviousListViewItem();
return;
}
if (m_focusChain.empty()) {
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();
}
return;
}
setFocusItem(focusedItem);
}
void FocusController::focusNextListViewItem()
{
m_lvfc->reloadFocusChain();
if (m_lvfc->isLastFocusItemInListView() || m_lvfc->isReturnNeeded()) {
dropListView();
nextItem(Direction::Forward);
return;
} else if (m_lvfc->isLastFocusItemInDelegate()) {
m_lvfc->resetFocusChain();
m_lvfc->nextDelegate();
}
m_lvfc->focusNextItem();
}
void FocusController::focusPreviousListViewItem()
{
m_lvfc->reloadFocusChain();
if (m_lvfc->isFirstFocusItemInListView() || m_lvfc->isReturnNeeded()) {
dropListView();
nextItem(Direction::Backward);
return;
} else if (m_lvfc->isFirstFocusItemInDelegate()) {
m_lvfc->resetFocusChain();
m_lvfc->previousDelegate();
}
m_lvfc->focusPreviousItem();
}
void FocusController::dropListView()
{
if (m_lvfc) {
delete m_lvfc;
m_lvfc = nullptr;
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)));
}
}
+10 -41
View File
@@ -1,57 +1,26 @@
#ifndef FOCUSCONTROLLER_H
#define FOCUSCONTROLLER_H
#include "ui/controllers/qml/listViewFocusController.h"
#include <QQmlApplicationEngine>
/*!
* \brief The FocusController class makes focus control more straightforward
* \details Focus is handled only for visible and enabled items which have
* `isFocused` property from top left to bottom right.
* \note There are items handled differently (e.g. ListView)
*/
class QQuickItem;
class FocusController : public QObject
{
Q_OBJECT
public:
explicit FocusController(QQmlApplicationEngine *engine, QObject *parent = nullptr);
~FocusController() override = default;
Q_INVOKABLE void nextKeyTabItem();
Q_INVOKABLE void previousKeyTabItem();
Q_INVOKABLE void nextKeyUpItem();
Q_INVOKABLE void nextKeyDownItem();
Q_INVOKABLE void nextKeyLeftItem();
Q_INVOKABLE void nextKeyRightItem();
Q_INVOKABLE void setFocusItem(QQuickItem *item);
Q_INVOKABLE void setFocusOnDefaultItem();
Q_INVOKABLE void pushRootObject(QObject *object);
Q_INVOKABLE void dropRootObject(QObject *object);
Q_INVOKABLE void resetRootObject();
protected:
bool eventFilter(QObject *watched, QEvent *event) override;
private:
enum class Direction {
Forward,
Backward,
};
void reload(Direction direction);
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();
static bool wantsArrowKey(const QQuickItem *item, int key);
static void sortListViewDelegates(QQuickItem *root);
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,
const char *endMargin);
};
#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
+2 -33
View File
@@ -89,12 +89,7 @@ void PageController::keyPressEvent(Qt::Key key)
switch (key) {
case Qt::Key_Back:
case Qt::Key_Escape: {
if (m_drawerDepth) {
emit closeTopDrawer();
decrementDrawerDepth();
} else {
emit escapePressed();
}
break;
}
default: return;
@@ -104,9 +99,9 @@ void PageController::keyPressEvent(Qt::Key key)
unsigned int PageController::getInitialPageNavigationBarColor()
{
if (m_serversController->getServersCount()) {
return 0xFF1C1D21;
return 0xFF1F1F23;
} else {
return 0xFF0E0E11;
return 0xFF101012;
}
}
@@ -150,32 +145,6 @@ void PageController::closeApplication()
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()
{
#ifdef Q_OS_ANDROID
@@ -41,11 +41,6 @@ public slots:
void closeApplication();
void setDrawerDepth(const int depth);
int getDrawerDepth() const;
int incrementDrawerDepth();
int decrementDrawerDepth();
bool isEdgeToEdgeEnabled();
int getStatusBarHeight();
int getNavigationBarHeight();
@@ -88,7 +83,6 @@ signals:
void unsupportedConnectDrawerRequested();
void escapePressed();
void closeTopDrawer();
void imeHeightChanged(int height);
void safeAreaTopMarginChanged();
@@ -100,8 +94,6 @@ private:
bool m_isTriggeredByConnectButton;
int m_drawerDepth = 0;
mutable int m_cachedStatusBarHeight = -1;
mutable int m_cachedNavigationBarHeight = -1;
mutable bool m_cachedEdgeToEdgeEnabled = false;
-150
View File
@@ -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))
}
}
}
+21 -31
View File
@@ -1,15 +1,13 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Style 1.0
import "../Controls2"
import "../Controls2/TextTypes"
Item {
id: root
property bool enabled: true
property string placeholderText: ""
property alias textField: searchField.textField
@@ -17,57 +15,49 @@ Item {
signal moreClicked()
implicitWidth: 360
implicitHeight: 96
implicitHeight: content.implicitHeight + 32 + PageController.safeAreaBottomMargin
Rectangle {
id: background
anchors.fill: parent
color: "#0E0F12"
color: AmneziaStyle.color.bgBase
opacity: 0.85
z: -1
}
RowLayout {
id: addSiteButton
id: content
enabled: root.enabled
spacing: 2
anchors.fill: parent
anchors.topMargin: 16
anchors.leftMargin: 16
anchors.rightMargin: 16
anchors.bottomMargin: 16 + PageController.safeAreaBottomMargin
anchors {
fill: parent
topMargin: 16
leftMargin: 16
rightMargin: 16
bottomMargin: 24
}
spacing: 8
TextFieldWithHeaderType {
TextFieldType {
id: searchField
Layout.fillWidth: true
rightButtonClickedOnEnter: true
textField.placeholderText: root.placeholderText
rightButtonClickedOnEnter: true
placeholderText: root.placeholderText
buttonImageSource: "qrc:/images/controls/plus.svg"
buttonJoined: true
clickedFunc: function() {
root.addClicked(textField.text)
textField.text = ""
root.addClicked(searchField.textField.text)
searchField.textField.text = ""
}
}
ImageButtonType {
id: addSiteButtonImage
implicitWidth: 56
implicitHeight: 56
IconButtonType {
implicitWidth: 64
implicitHeight: 64
image: "qrc:/images/controls/more-vertical.svg"
imageColor: AmneziaStyle.color.paleGray
icon.source: "qrc:/images/controls/more-vertical.svg"
onClicked: root.moreClicked()
Keys.onReturnPressed: addSiteButtonImage.clicked()
Keys.onEnterPressed: addSiteButtonImage.clicked()
}
}
}
+1 -2
View File
@@ -5,7 +5,7 @@ import QtQuick.Layouts
import "../Controls2"
TextFieldWithHeaderType {
TextFieldType {
id: root
property var scroller: null
@@ -14,7 +14,6 @@ TextFieldWithHeaderType {
signal edited(string text)
Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
+28 -34
View File
@@ -1,64 +1,58 @@
import QtQuick
import QtQuick.Controls.impl
import QtQuick.Layouts
import Style 1.0
import "../Controls2"
import "../Controls2/TextTypes"
RowLayout {
id: root
property string iconSource: ""
property string titleText: ""
property string bodyText: ""
property bool link: false
property string iconSource
property string title
property string subtitle
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
spacing: 12
Image {
IconImage {
Layout.alignment: Qt.AlignTop
Layout.preferredWidth: 22
Layout.preferredHeight: 22
Layout.preferredWidth: 24
Layout.preferredHeight: 24
source: root.iconSource
fillMode: Image.PreserveAspectFit
sourceSize: Qt.size(24, 24)
color: AmneziaStyle.color.iconDefault
}
ColumnLayout {
Layout.fillWidth: true
spacing: 4
LabelTextType {
H3TextType {
Layout.fillWidth: true
text: root.titleText
color: AmneziaStyle.color.paleGray
font.pixelSize: 16
font.weight: Font.DemiBold
wrapMode: Text.Wrap
text: root.title
}
Item {
SmallTextType {
Layout.fillWidth: true
implicitHeight: bodyLabel.implicitHeight
LabelTextType {
id: bodyLabel
width: parent.width
text: root.bodyLineText
color: root.link ? AmneziaStyle.color.goldenApricot : AmneziaStyle.color.mutedGray
font.pixelSize: 14
wrapMode: Text.Wrap
visible: !root.isLink && text !== ""
text: root.subtitle
}
MouseArea {
anchors.fill: bodyLabel
visible: root.bodyClickable
cursorShape: Qt.PointingHandCursor
onClicked: Qt.openUrlExternally("https://t.me/" + root.bodyText)
}
LinkButtonType {
Layout.alignment: Qt.AlignLeft
Layout.maximumWidth: parent.width
visible: root.isLink && root.subtitle !== ""
text: "@" + root.subtitle
onClicked: Qt.openUrlExternally("https://t.me/" + root.subtitle)
}
}
}
+15 -18
View File
@@ -1,39 +1,36 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import "."
import Style 1.0
Rectangle {
Pane {
id: root
property var benefitsModel: null
visible: benefitsModel && benefitsModel.rowCount() > 0
visible: root.benefitsModel && root.benefitsModel.rowCount() > 0
padding: 16
background: Rectangle {
radius: 16
color: AmneziaStyle.color.benefitsPanelBackground
implicitHeight: inner.implicitHeight + 24
color: AmneziaStyle.color.bgSurface
}
ColumnLayout {
id: inner
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.margins: 12
spacing: 20
contentItem: ColumnLayout {
spacing: 16
Repeater {
model: benefitsModel
model: root.benefitsModel
delegate: BenefitRow {
Layout.fillWidth: true
iconSource: model.icon
titleText: model.title
bodyText: model.body
link: !!model.link
title: model.title
subtitle: model.body
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 {}
}
}
}
}
}
+93 -131
View File
@@ -1,50 +1,25 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import QtQuick.Effects
import QtQuick.Shapes
import Qt5Compat.GraphicalEffects
import ConnectionState 1.0
import PageEnum 1.0
import Style 1.0
import "../Controls2"
import "../Controls2/TextTypes"
Button {
id: root
property string defaultButtonColor: AmneziaStyle.color.paleGray
property string progressButtonColor: AmneziaStyle.color.paleGray
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()
}
property bool connected: ConnectionController.isConnected
property bool inProgress: ConnectionController.isConnectionInProgress
implicitWidth: 190
implicitHeight: 190
padding: 0
hoverEnabled: true
text: ConnectionController.connectionStateText
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 {
implicitWidth: parent.width
implicitHeight: parent.height
transformOrigin: Item.Center
implicitWidth: 190
implicitHeight: 190
Shape {
id: backgroundCircle
width: parent.implicitWidth
height: parent.implicitHeight
anchors.bottom: parent.bottom
anchors.right: parent.right
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 {
fillColor: AmneziaStyle.color.transparent
strokeColor: AmneziaStyle.color.paleGray
strokeWidth: root.buttonActiveFocus ? 1 : 0
capStyle: ShapePath.RoundCap
PathAngleArc {
centerX: backgroundCircle.width / 2
centerY: backgroundCircle.height / 2
radiusX: 94
radiusY: 94
startAngle: 0
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 {
id: shape
width: parent.implicitWidth
height: parent.implicitHeight
anchors.bottom: parent.bottom
anchors.right: parent.right
layer.enabled: true
layer.samples: 4
visible: ConnectionController.isConnectionInProgress
visible: root.inProgress
opacity: 0.1
preferredRendererType: Shape.CurveRenderer
ShapePath {
fillColor: AmneziaStyle.color.transparent
strokeColor: AmneziaStyle.color.paleGray
strokeWidth: 3
strokeColor: AmneziaStyle.color.borderEmphasis
strokeWidth: 1
PathAngleArc {
centerX: root.width / 2
centerY: root.height / 2
radiusX: d.ringRadius
radiusY: d.ringRadius
startAngle: 0
sweepAngle: 360
}
}
}
Item {
anchors.fill: parent
Item {
id: ringSource
anchors.fill: parent
anchors.margins: -d.glowMargin
Shape {
anchors.fill: parent
preferredRendererType: Shape.CurveRenderer
ShapePath {
fillColor: AmneziaStyle.color.transparent
strokeColor: d.ringColor
strokeWidth: 2
capStyle: ShapePath.RoundCap
PathAngleArc {
centerX: shape.width / 2
centerY: shape.height / 2
radiusX: 93
radiusY: 93
startAngle: 245
sweepAngle: -180
centerX: ringSource.width / 2
centerY: ringSource.height / 2
radiusX: d.ringRadius
radiusY: d.ringRadius
startAngle: 100
sweepAngle: root.inProgress ? 160 : 360
}
}
}
}
RotationAnimator {
target: shape
running: ConnectionController.isConnectionInProgress
MultiEffect {
anchors.fill: ringSource
source: ringSource
shadowEnabled: true
shadowColor: d.ringColor
shadowBlur: 0.6
shadowHorizontalOffset: 0
shadowVerticalOffset: 0
blurMax: 16
}
RotationAnimator on rotation {
running: root.inProgress
from: 0
to: 360
loops: Animation.Infinite
duration: 1000
}
}
FocusRingType {
radius: root.width / 2
visible: root.visualFocus
}
}
contentItem: Text {
height: 24
contentItem: ButtonTextType {
text: root.text
font.family: "PT Root UI VF"
font.weight: 700
color: root.connected && !root.inProgress ? AmneziaStyle.color.textAccent : AmneziaStyle.color.textPrimary
lineHeight: 30 + LanguageUiController.getLineHeightAppend()
font.pixelSize: 20
color: ConnectionController.isConnected ? connectedButtonColor : defaultButtonColor
text: root.text
leftPadding: 16
rightPadding: 16
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
HoverHandler {
cursorShape: Qt.PointingHandCursor
}
onClicked: {
ConnectionController.connectButtonClicked()
}
Keys.onEnterPressed: this.clicked()
Keys.onReturnPressed: this.clicked()
Keys.onEnterPressed: root.clicked()
Keys.onReturnPressed: root.clicked()
}
@@ -8,13 +8,12 @@ import "../Controls2"
import "../Controls2/TextTypes"
import "../Config"
DrawerType2 {
SheetType {
id: root
width: parent.width
height: parent.height
expandedHeight: content.implicitHeight + 16 + PageController.safeAreaBottomMargin
expandedStateContent: ColumnLayout {
ColumnLayout {
id: content
anchors.top: parent.top
@@ -22,13 +21,9 @@ DrawerType2 {
anchors.right: parent.right
spacing: 0
Component.onCompleted: {
root.expandedHeight = content.implicitHeight + 32
}
Header2Type {
SectionHeaderType {
Layout.fillWidth: true
Layout.topMargin: 24
Layout.topMargin: 16
Layout.rightMargin: 16
Layout.leftMargin: 16
Layout.bottomMargin: 16
@@ -36,35 +31,34 @@ DrawerType2 {
headerText: qsTr("Add new connection")
}
LabelWithButtonType {
ListItemType {
id: ip
Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
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)
root.closeTriggered()
root.close()
}
}
DividerType {}
LabelWithButtonType {
ListItemType {
id: qrCode
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
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)
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.Layouts
import Qt5Compat.GraphicalEffects
import Style 1.0
import "../Controls2/TextTypes"
import "../Controls2"
Item {
EmptyStateType {
id: root
property bool isSearchResult: true
property string categoryName: ""
signal showAllRequested()
ColumnLayout {
anchors.centerIn: parent
width: Math.min(parent.width - 32, 320)
iconColor: AmneziaStyle.color.accentLight
spacing: 16
Item {
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
}
}
ParagraphTextType {
Layout.fillWidth: true
color: AmneziaStyle.color.textPrimary
horizontalAlignment: Text.AlignHCenter
text: {
title: {
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
ButtonType {
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
variant: ButtonType.Text
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()
}
}
}
+9 -13
View File
@@ -1,8 +1,6 @@
import QtQuick
import QtQuick.Layouts
import Style 1.0
import "../Controls2"
RowLayout {
@@ -17,37 +15,35 @@ RowLayout {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
Layout.topMargin: 12
Layout.topMargin: 16
spacing: 8
CountrySearchField {
SearchFieldType {
id: searchField
Layout.fillWidth: true
placeholderText: qsTr("Country or region")
maximumLength: 64
onTextChanged: {
const current = searchField.text
const clean = root.listModel.sanitizeSearchFieldText(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.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)
}
ImageButtonType {
IconButtonType {
objectName: "sortButton"
implicitWidth: 64
implicitHeight: 64
hoverEnabled: true
image: "qrc:/images/controls/sort-desc.svg"
imageColor: AmneziaStyle.color.paleGray
icon.source: "qrc:/images/controls/sort-desc.svg"
onClicked: root.sortRequested()
}
@@ -1,10 +1,11 @@
import QtQuick
import QtQuick.Layouts
import Qt5Compat.GraphicalEffects
import QtQuick.Controls.impl
import Style 1.0
import "../Config"
import "../Controls2"
import "../Controls2/TextTypes"
Item {
@@ -12,7 +13,7 @@ Item {
property var listModel: null
property bool isFocusable: root.isRealSection && !root.isPinnedOverlay
activeFocusOnTab: root.isRealSection && !root.isPinnedOverlay
property string sectionKey: ""
property bool isPinnedOverlay: false
@@ -47,8 +48,16 @@ Item {
readonly property string subsubregionId: root.isRealSection ? root.listModel.sectionSubsubregionId(root.sectionKey) : ""
readonly property int level: root.subsubregionId !== "" ? 3 : (root.subregionId !== "" ? 2 : 1)
readonly property color titleColor: root.level === 1 ? AmneziaStyle.color.textPrimary
: AmneziaStyle.color.textTertiary
readonly property color chevronColor: root.level === 1 ? AmneziaStyle.color.iconDefault
: 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: {
if (!root.isRealSection) {
@@ -70,7 +79,7 @@ Item {
Rectangle {
anchors.fill: parent
color: root.isPinnedOverlay ? AmneziaStyle.color.midnightBlack
color: root.isPinnedOverlay ? AmneziaStyle.color.bgBase
: AmneziaStyle.color.transparent
}
@@ -82,30 +91,23 @@ Item {
anchors.bottomMargin: root.contentBottomMargin
spacing: 8
ParagraphTextType {
BodyTextType {
Layout.fillWidth: true
color: root.titleColor
lineHeight: (root.level === 1 ? 24 : 18) + LanguageUiController.getLineHeightAppend()
font.pixelSize: root.level === 1 ? 16 : 14
font.weight: root.level === 1 ? 700 : 400
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))
}
visible: root.level === 1
color: AmneziaStyle.color.textPrimary
font.weight: 600
text: root.displayTitle
}
Image {
SmallTextType {
Layout.fillWidth: true
visible: root.level !== 1
text: root.displayTitle
}
IconImage {
Layout.preferredWidth: 24
Layout.preferredHeight: 24
Layout.leftMargin: 10
@@ -113,13 +115,8 @@ Item {
visible: root.canToggle
source: root.collapsed ? "qrc:/images/controls/chevron-down.svg"
: "qrc:/images/controls/chevron-up.svg"
layer {
enabled: true
effect: ColorOverlay {
color: root.titleColor
}
}
sourceSize: Qt.size(24, 24)
color: root.chevronColor
}
}
@@ -144,22 +141,9 @@ Item {
Keys.onReturnPressed: root.toggle()
Keys.onSpacePressed: root.toggle()
Keys.onTabPressed: {
FocusController.nextKeyTabItem()
}
Keys.onBacktabPressed: {
FocusController.previousKeyTabItem()
}
Rectangle {
anchors.fill: parent
anchors.margins: 2
color: AmneziaStyle.color.transparent
FocusRingType {
anchors.margins: 0
radius: 8
border.width: root.activeFocus ? 1 : 0
border.color: AmneziaStyle.color.paleGray
visible: root.activeFocus
}
}
@@ -1,10 +1,7 @@
import QtQuick
import Style 1.0
import "../Config"
import "../Controls2"
import "../Controls2/TextTypes"
ListViewType {
id: root
@@ -14,10 +11,10 @@ ListViewType {
readonly property var chips: root.listModel !== null ? root.listModel.useCases : []
visible: root.chips.length > 1
isFocusable: root.visible
implicitHeight: root.visible ? 36 : 0
implicitHeight: root.visible ? 34 : 0
clip: false
orientation: ListView.Horizontal
spacing: 8
leftMargin: 16
@@ -27,74 +24,18 @@ ListViewType {
model: root.chips
delegate: Item {
id: delegateRoot
delegate: ChipType {
required property var modelData
implicitWidth: chip.implicitWidth
implicitHeight: 36
readonly property string useCaseId: modelData.useCaseId
Rectangle {
id: chip
checked: root.listModel !== null && root.listModel.activeUseCaseId === useCaseId
text: "%1 · %2".arg(CountryRegionNames.useCaseName(root.listModel, useCaseId)).arg(modelData.count)
readonly property string useCaseId: delegateRoot.modelData.useCaseId
readonly property bool active: root.listModel !== null
&& root.listModel.activeUseCaseId === chip.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() {
onClicked: {
if (root.listModel !== null) {
root.listModel.activeUseCaseId = chip.useCaseId
root.listModel.activeUseCaseId = 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,54 +1,36 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import SortFilterProxyModel 0.2
import PageEnum 1.0
import "../Controls2"
import "../Controls2/TextTypes"
ListViewType {
id: menuContent
id: root
property var rootWidth
property var selectedText
signal activated()
width: rootWidth
anchors.top: parent.top
anchors.bottom: parent.bottom
leftMargin: 16
rightMargin: 16
bottomMargin: 16
ButtonGroup {
id: containersRadioButtonGroup
}
delegate: Item {
implicitWidth: rootWidth
implicitHeight: content.implicitHeight
ColumnLayout {
id: content
anchors.fill: parent
anchors.rightMargin: 16
anchors.leftMargin: 16
VerticalRadioButton {
id: containerRadioButton
Layout.fillWidth: true
delegate: RadioItemType {
width: root.width - root.leftMargin - root.rightMargin
text: name
descriptionText: description
subtitle: description
showSeparator: index < root.count - 1
ButtonGroup.group: containersRadioButtonGroup
imageSource: "qrc:/images/controls/download.svg"
showImage: !isInstalled
icon.source: isInstalled ? "" : "qrc:/images/controls/download.svg"
checkable: isInstalled && !ConnectionController.isConnected
checked: proxyDefaultServerContainersModel.mapToSource(index) === ServersUiController.serverDefaultContainer(ServersUiController.defaultServerId)
checked: root.model.mapToSource(index) === ServersUiController.serverDefaultContainer(ServersUiController.defaultServerId)
onClicked: {
if (ConnectionController.isConnected && isInstalled) {
@@ -56,43 +38,18 @@ ListViewType {
return
}
var containerIndex = proxyDefaultServerContainersModel.mapToSource(index)
var containerIndex = root.model.mapToSource(index)
if (!isInstalled) {
ServersUiController.setProcessedServerId(ServersUiController.defaultServerId)
ServersUiController.processedContainerIndex = containerIndex
PageController.goToPage(PageEnum.PageSetupWizardProtocolSettings)
containersDropDown.closeTriggered()
root.activated()
return
}
containersDropDown.closeTriggered()
root.activated()
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 {
Layout.fillWidth: true
}
}
}
}
+120
View File
@@ -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 "../Config"
DrawerType2 {
SheetType {
id: root
property bool isAppSplitTinnelingEnabled: Qt.platform.os === "windows" || Qt.platform.os === "android"
anchors.fill: parent
expandedHeight: parent.height * 0.9
expandedHeight: Math.min(content.implicitHeight + 24 + PageController.safeAreaBottomMargin, windowHeight * 0.9)
expandedStateContent: ColumnLayout {
ColumnLayout {
id: content
anchors.top: parent.top
@@ -24,74 +23,67 @@ DrawerType2 {
anchors.right: parent.right
spacing: 0
Header2Type {
SectionHeaderType {
Layout.fillWidth: true
Layout.topMargin: 24
Layout.topMargin: 16
Layout.rightMargin: 16
Layout.leftMargin: 16
Layout.bottomMargin: 16
Layout.bottomMargin: 32
headerText: qsTr("Split tunneling")
descriptionText: qsTr("Allows you to connect to some sites or applications through a VPN connection and bypass others")
}
LabelWithButtonType {
ListItemType {
id: splitTunnelingSwitch
Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
visible: ServersUiController.isDefaultServerDefaultContainerHasSplitTunneling
text: qsTr("Split tunneling on the server")
descriptionText: qsTr("Enabled \nCan't be disabled for current server")
rightImageSource: "qrc:/images/controls/chevron-right.svg"
subtitle: qsTr("Enabled \nCan't be disabled for current server")
trailingIconSource: "qrc:/images/controls/chevron-right.svg"
clickedFunction: function() {
onClicked: {
PageController.goToPage(PageEnum.PageSettingsSplitTunneling)
root.closeTriggered()
root.close()
}
}
DividerType {
visible: ServersUiController.isDefaultServerDefaultContainerHasSplitTunneling
}
LabelWithButtonType {
ListItemType {
id: siteBasedSplitTunnelingSwitch
Layout.fillWidth: true
Layout.topMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("Site-based split tunneling")
descriptionText: enabled && IpSplitTunnelingController.isSplitTunnelingEnabled ? qsTr("Enabled") : qsTr("Disabled")
rightImageSource: "qrc:/images/controls/chevron-right.svg"
subtitle: enabled && IpSplitTunnelingController.isSplitTunnelingEnabled ? qsTr("Enabled") : qsTr("Disabled")
trailingIconSource: "qrc:/images/controls/chevron-right.svg"
clickedFunction: function() {
onClicked: {
PageController.goToPage(PageEnum.PageSettingsSplitTunneling)
root.closeTriggered()
root.close()
}
}
DividerType {
}
LabelWithButtonType {
ListItemType {
id: appSplitTunnelingSwitch
visible: isAppSplitTinnelingEnabled
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("App-based split tunneling")
descriptionText: AppSplitTunnelingController.isSplitTunnelingEnabled ? qsTr("Enabled") : qsTr("Disabled")
rightImageSource: "qrc:/images/controls/chevron-right.svg"
subtitle: AppSplitTunnelingController.isSplitTunnelingEnabled ? qsTr("Enabled") : qsTr("Disabled")
trailingIconSource: "qrc:/images/controls/chevron-right.svg"
clickedFunction: function() {
onClicked: {
PageController.goToPage(PageEnum.PageSettingsAppSplitTunneling)
root.closeTriggered()
root.close()
}
}
DividerType {
visible: isAppSplitTinnelingEnabled
}
}
}
@@ -10,11 +10,10 @@ import SortFilterProxyModel 0.2
import InstalledAppsModel 1.0
import Style 1.0
DrawerType2 {
SheetType {
id: root
anchors.fill: parent
expandedHeight: parent.height * 0.9
expandedHeight: windowHeight * 0.9
onAboutToShow: {
PageController.showBusyIndicator(true)
@@ -26,33 +25,19 @@ DrawerType2 {
id: installedAppsModel
}
expandedStateContent: Item {
Item {
id: container
anchors.fill: parent
implicitHeight: expandedHeight
ColumnLayout {
id: backButton
SectionHeaderType {
id: header
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: searchField.top
anchors.topMargin: 16
BackButtonType {
backButtonImage: "qrc:/images/controls/arrow-left.svg"
backButtonFunction: function() {
root.closeTriggered()
}
}
Header2Type {
id: header
Layout.fillWidth: true
Layout.topMargin: 16
Layout.rightMargin: 16
Layout.leftMargin: 16
anchors.topMargin: 12
anchors.leftMargin: 16
anchors.rightMargin: 16
headerText: qsTr("Choose application")
}
@@ -60,33 +45,45 @@ DrawerType2 {
ListViewType {
id: listView
Layout.fillWidth: true
Layout.fillHeight: true
Layout.topMargin: 16
Layout.rightMargin: 16
Layout.leftMargin: 16
anchors.top: header.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: searchField.top
anchors.topMargin: 16
anchors.bottomMargin: 8
leftMargin: 16
rightMargin: 16
topMargin: 16
model: SortFilterProxyModel {
id: proxyInstalledAppsModel
sourceModel: installedAppsModel
filters: RegExpFilter {
roleName: "appName"
pattern: ".*" + searchField.textField.text + ".*"
pattern: ".*" + searchField.text + ".*"
caseSensitivity: Qt.CaseInsensitive
}
}
ButtonGroup {
id: buttonGroup
}
delegate: ColumnLayout {
width: listView.width
delegate: Item {
width: listView.width - listView.leftMargin - listView.rightMargin
implicitHeight: Math.max(72, row.implicitHeight + 24)
RowLayout {
id: row
anchors.fill: parent
anchors.leftMargin: 16
anchors.rightMargin: 12
spacing: 12
CheckBoxType {
Layout.fillWidth: true
spacing: 20
text: appName
checked: isAppSelected
onCheckedChanged: {
@@ -94,53 +91,62 @@ DrawerType2 {
}
}
Item {
Layout.preferredWidth: 48
Layout.preferredHeight: 48
Image {
anchors.centerIn: parent
source: "image://installedAppImage/" + appIcon
sourceSize.width: 24
sourceSize.height: 24
Layout.rightMargin: 48
}
}
DividerType {}
}
}
}
TextFieldWithHeaderType {
Rectangle {
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
height: 1
color: AmneziaStyle.color.borderSoft
}
}
}
SearchFieldType {
id: searchField
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: addButton.top
anchors.bottomMargin: 16
anchors.bottomMargin: 12
anchors.rightMargin: 16
anchors.leftMargin: 16
backgroundColor: AmneziaStyle.color.slateGray
textField.placeholderText: qsTr("application name")
placeholderText: qsTr("application name")
}
BasicButtonType {
ButtonType {
id: addButton
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.bottomMargin: 16
anchors.bottomMargin: Math.max(16, PageController.safeAreaBottomMargin)
anchors.rightMargin: 16
anchors.leftMargin: 16
text: qsTr("Add selected")
clickedFunc: function() {
onClicked: {
PageController.showBusyIndicator(true)
AppSplitTunnelingController.addApps(installedAppsModel.getSelectedAppsInfo())
PageController.showBusyIndicator(false)
root.closeTriggered()
root.close()
}
}
}
+121
View File
@@ -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
}
}
}
}
+28
View File
@@ -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
}
}
+14 -30
View File
@@ -11,7 +11,7 @@ import "../Controls2/TextTypes"
import "../Config"
DrawerType2 {
SheetType {
id: root
property string headerText
@@ -22,71 +22,55 @@ DrawerType2 {
property var yesButtonFunction
property var noButtonFunction
expandedStateContent: ColumnLayout {
expandedHeight: content.implicitHeight + 8 + PageController.safeAreaBottomMargin
ColumnLayout {
id: content
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
spacing: 8
spacing: 12
onImplicitHeightChanged: {
root.expandedHeight = content.implicitHeight + 32 + PageController.safeAreaBottomMargin
}
Header2TextType {
SectionHeaderType {
Layout.fillWidth: true
Layout.topMargin: 16
Layout.bottomMargin: 14
Layout.rightMargin: 16
Layout.leftMargin: 16
text: root.headerText
headerText: root.headerText
descriptionText: root.descriptionText
}
ParagraphTextType {
Layout.fillWidth: true
Layout.topMargin: 8
Layout.rightMargin: 16
Layout.leftMargin: 16
text: root.descriptionText
}
BasicButtonType {
ButtonType {
id: yesButton
Layout.fillWidth: true
Layout.topMargin: 16
Layout.rightMargin: 16
Layout.leftMargin: 16
text: root.yesButtonText
clickedFunc: function() {
onClicked: {
if (root.yesButtonFunction && typeof root.yesButtonFunction === "function") {
root.yesButtonFunction()
}
}
}
BasicButtonType {
ButtonType {
id: noButton
variant: ButtonType.Secondary
Layout.fillWidth: true
Layout.rightMargin: 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 !== ""
text: root.noButtonText
clickedFunc: function() {
onClicked: {
if (root.noButtonFunction && typeof root.noButtonFunction === "function") {
root.noButtonFunction()
}
@@ -9,21 +9,27 @@ import "../Controls2/TextTypes"
import "../Config"
DrawerType2 {
SheetType {
property string serverNameText
id: root
objectName: "serverNameEditDrawer"
expandedStateContent: ColumnLayout {
expandedHeight: content.implicitHeight + 16 + 8 + PageController.safeAreaBottomMargin + PageController.imeHeight
ColumnLayout {
id: content
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 32
anchors.topMargin: 16
anchors.leftMargin: 16
anchors.rightMargin: 16
TextFieldWithHeaderType {
spacing: 26
TextFieldType {
id: serverName
Layout.fillWidth: true
@@ -33,14 +39,14 @@ DrawerType2 {
checkEmptyText: true
}
BasicButtonType {
ButtonType {
id: saveButton
Layout.fillWidth: true
text: qsTr("Save")
clickedFunc: function() {
onClicked: {
if (serverName.textField.text === "") {
return
}
@@ -48,7 +54,7 @@ DrawerType2 {
if (serverName.textField.text !== root.serverNameText) {
ServersUiController.editServerName(ServersUiController.processedServerId, serverName.textField.text);
}
root.closeTriggered()
root.close()
}
}
}
+37 -101
View File
@@ -8,42 +8,52 @@ import "../Controls2"
import "../Controls2/TextTypes"
import "../Config"
DrawerType2 {
SheetType {
id: root
expandedStateContent: Item {
expandedHeight: windowHeight * 0.9
Item {
id: container
implicitHeight: root.height * 0.9
Component.onCompleted: {
root.expandedHeight = container.implicitHeight
}
anchors.fill: parent
ColumnLayout {
id: backButtonLayout
id: headerLayout
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 16
BackButtonType {
spacing: 0
Item {
Layout.fillWidth: true
Layout.topMargin: 2
Layout.preferredHeight: 48
IconButtonType {
id: backButton
Layout.fillWidth: true
x: 16
y: 4
backButtonImage: "qrc:/images/controls/arrow-left.svg"
backButtonFunction: function() { root.closeTriggered() }
size: IconButtonType.Small
icon.source: "qrc:/images/controls/arrow-left.svg"
Accessible.name: qsTr("Back")
onClicked: root.close()
}
}
Header2Type {
SectionHeaderType {
id: header
Layout.fillWidth: true
Layout.topMargin: 16
Layout.rightMargin: 16
Layout.leftMargin: 16
Layout.rightMargin: 16
Layout.bottomMargin: 32
headerText: qsTr("Choose language")
}
@@ -52,7 +62,7 @@ DrawerType2 {
ListViewType {
id: listView
anchors.top: backButtonLayout.bottom
anchors.top: headerLayout.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
@@ -61,97 +71,27 @@ DrawerType2 {
model: LanguageModel
footer: Item {
width: listView.width
height: 16 + PageController.safeAreaBottomMargin
}
ButtonGroup {
id: buttonGroup
}
delegate: Item {
implicitWidth: root.width
implicitHeight: delegateContent.implicitHeight
width: listView.width
implicitHeight: languageItem.implicitHeight
ColumnLayout {
id: delegateContent
RadioItemType {
id: languageItem
anchors.fill: parent
RadioButton {
id: radioButton
implicitWidth: parent.width
implicitHeight: radioButtonContent.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 {
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
anchors.rightMargin: 16
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
@@ -159,14 +99,10 @@ DrawerType2 {
onClicked: {
listView.selectedIndex = index
LanguageUiController.changeLanguage(languageIndex)
root.closeTriggered()
root.close()
}
}
}
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()
}
}
+42 -104
View File
@@ -1,29 +1,21 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import SortFilterProxyModel 0.2
import PageEnum 1.0
import ContainerProps 1.0
import ContainersModelFilters 1.0
import Style 1.0
import "./"
import "../Controls2"
import "../Controls2/TextTypes"
import "../Config"
ListViewType {
id: root
property int selectedIndex: ServersUiController.getServerIndexById(ServersUiController.defaultServerId)
anchors.top: serversMenuHeader.bottom
anchors.right: parent.right
anchors.left: parent.left
anchors.bottom: parent.bottom
anchors.topMargin: 16
signal serverSettingsOpened()
leftMargin: 16
rightMargin: 16
bottomMargin: 16
model: ServersModel
@@ -34,92 +26,12 @@ ListViewType {
}
}
delegate: Item {
id: menuContentDelegate
objectName: "menuContentDelegate"
property variant delegateData: model
property VerticalRadioButton serverRadioButtonProperty: serverRadioButton
implicitWidth: root.width
implicitHeight: serverRadioButtonContent.implicitHeight
ColumnLayout {
id: serverRadioButtonContent
objectName: "serverRadioButtonContent"
anchors.fill: parent
anchors.rightMargin: 16
anchors.leftMargin: 16
spacing: 0
RowLayout {
objectName: "serverRadioButtonRowLayout"
Layout.fillWidth: true
VerticalRadioButton {
id: serverRadioButton
objectName: "serverRadioButton"
Layout.fillWidth: true
text: name
descriptionText: 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
ButtonGroup {
id: serversRadioButtonGroup
objectName: "serversRadioButtonGroup"
}
root.selectedIndex = index
ServersUiController.setDefaultServerAtIndex(index)
}
Keys.onEnterPressed: serverRadioButton.clicked()
Keys.onReturnPressed: serverRadioButton.clicked()
}
ImageButtonType {
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() {
function openServerSettings(serverId) {
ServersUiController.setProcessedServerId(serverId)
if (ServersUiController.isServerFromApi(ServersUiController.processedServerId)) {
@@ -139,16 +51,42 @@ ListViewType {
PageController.goToPage(PageEnum.PageSettingsServerInfo)
}
drawer.closeTriggered()
}
}
root.serverSettingsOpened()
}
DividerType {
Layout.fillWidth: true
Layout.leftMargin: 0
Layout.rightMargin: 0
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.Controls
import QtQuick.Controls.impl
import QtQuick.Layouts
import SortFilterProxyModel 0.2
import PageEnum 1.0
import Style 1.0
import "../Controls2"
import "../Controls2/TextTypes"
ListViewType {
id: root
anchors.fill: parent
delegate: ColumnLayout {
width: root.width
delegate: Item {
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 {
Layout.fillWidth: true
implicitWidth: root.width
implicitHeight: containerItem.implicitHeight
ListItemType {
id: containerItem
anchors.left: parent.left
anchors.right: parent.right
anchors.leftMargin: 16
anchors.rightMargin: 16
text: name
descriptionText: description
rightWarningImageSource: isOutdatedAwgContainer ? "qrc:/images/controls/alert-circle.svg" : ""
rightImageSource: isInstalled ? "qrc:/images/controls/chevron-right.svg" : "qrc:/images/controls/download.svg"
subtitle: description
trailingIconSource: 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) {
var containerIndex = root.model.mapToSource(index)
ServersUiController.processedContainerIndex = containerIndex
if (isVpnContainer) {
// var isThirdPartyConfig = root.model.data(index, ContainersModel.IsThirdPartyConfigRole)
if (isThirdPartyConfig) {
InstallController.updateProtocols(ServersUiController.processedServerId, containerIndex)
PageController.goToPage(PageEnum.PageProtocolRaw)
@@ -68,14 +86,11 @@ ListViewType {
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/TextTypes"
DrawerType2 {
SheetType {
id: root
property var listModel: null
width: parent.width
height: parent.height
expandedHeight: content.implicitHeight + 32 + PageController.safeAreaBottomMargin
expandedStateContent: ColumnLayout {
ColumnLayout {
id: content
anchors.top: parent.top
@@ -21,25 +20,21 @@ DrawerType2 {
anchors.right: parent.right
spacing: 0
Component.onCompleted: {
root.expandedHeight = content.implicitHeight + 32 + PageController.safeAreaBottomMargin
}
ButtonGroup {
id: sortModeGroup
}
Header2TextType {
SectionHeaderType {
Layout.fillWidth: true
Layout.topMargin: 24
Layout.topMargin: 16
Layout.rightMargin: 16
Layout.leftMargin: 16
Layout.bottomMargin: 16
text: qsTr("Sort countries")
headerText: qsTr("Sort countries")
}
VerticalRadioButton {
RadioItemType {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
@@ -51,32 +46,25 @@ DrawerType2 {
onClicked: {
root.listModel.sortMode = 0
root.closeTriggered()
root.close()
}
}
DividerType {
Layout.fillWidth: true
}
VerticalRadioButton {
RadioItemType {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("Alphabetically")
showSeparator: false
ButtonGroup.group: sortModeGroup
checked: root.listModel !== null && root.listModel.sortMode === 1
onClicked: {
root.listModel.sortMode = 1
root.closeTriggered()
root.close()
}
}
DividerType {
Layout.fillWidth: true
}
}
}
@@ -9,16 +9,18 @@ import Style 1.0
import "../Controls2"
import "../Controls2/TextTypes"
DrawerType2 {
SheetType {
id: root
property bool isRenewalAvailable: false
onOpened: {
expandedHeight: content.implicitHeight + 8 + PageController.safeAreaBottomMargin
onAboutToShow: {
isRenewalAvailable = ServersUiController.isServerRenewalAvailable(ServersUiController.defaultServerId) && !ApiAccountInfoModel.data("isInAppPurchase")
}
expandedStateContent: ColumnLayout {
ColumnLayout {
id: content
anchors.top: parent.top
@@ -27,79 +29,47 @@ DrawerType2 {
spacing: 0
onImplicitHeightChanged: {
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
SectionHeaderType {
Layout.fillWidth: true
Layout.topMargin: 16
Layout.rightMargin: 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")
defaultColor: AmneziaStyle.color.paleGray
hoveredColor: AmneziaStyle.color.lightGray
pressedColor: AmneziaStyle.color.mutedGray
textColor: AmneziaStyle.color.midnightBlack
clickedFunc: function() {
onClicked: {
SubscriptionUiController.getRenewalLink(ServersUiController.defaultServerId)
}
}
BasicButtonType {
Layout.alignment: Qt.AlignHCenter
Layout.topMargin: 8
Layout.bottomMargin: 8
ButtonType {
Layout.fillWidth: true
Layout.topMargin: root.isRenewalAvailable ? 12 : 26
Layout.rightMargin: 16
Layout.leftMargin: 16
implicitHeight: 25
defaultColor: AmneziaStyle.color.transparent
hoveredColor: AmneziaStyle.color.translucentWhite
pressedColor: AmneziaStyle.color.sheerWhite
textColor: AmneziaStyle.color.goldenApricot
variant: ButtonType.Text
text: qsTr("Support")
clickedFunc: function() {
onClicked: {
PageController.showBusyIndicator(true)
let result = SubscriptionUiController.getAccountInfo(ServersUiController.defaultServerId, false)
PageController.showBusyIndicator(false)
if (result) {
root.closeTriggered()
root.close()
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"
ParagraphTextType {
CaptionTextType {
id: root
property string termsUrl: ""
@@ -15,15 +15,12 @@ ParagraphTextType {
horizontalAlignment: Text.AlignHCenter
textFormat: Text.RichText
color: AmneziaStyle.color.mutedGray
font.pixelSize: 12
lineHeight: 1.35
lineHeightMode: Text.ProportionalHeight
color: AmneziaStyle.color.textAccent
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.privacyUrl)
.arg(AmneziaStyle.color.goldenApricotString)
.arg(AmneziaStyle.color.textAccent)
onLinkActivated: function(link) {
Qt.openUrlExternally(link)
@@ -1,61 +1,7 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Style 1.0
import "../Controls2"
import "../Controls2/TextTypes"
Rectangle {
id: root
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
}
}
}
SegmentedControlType {
model: ["UDP", "TCP"]
}
@@ -8,19 +8,14 @@ import "../Controls2"
import "../Controls2/TextTypes"
import "../Config"
DrawerType2 {
SheetType {
id: root
expandedStateContent: Item {
expandedHeight: content.implicitHeight + 16 + 24 + PageController.safeAreaBottomMargin
Item {
id: contentRoot
implicitHeight: content.implicitHeight + 40 + PageController.safeAreaBottomMargin
Binding {
target: root
property: "expandedHeight"
value: contentRoot.implicitHeight
}
anchors.fill: parent
ColumnLayout {
id: content
@@ -28,65 +23,61 @@ DrawerType2 {
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 24
anchors.leftMargin: 16
anchors.rightMargin: 16
anchors.topMargin: 16
spacing: 0
Header2TextType {
SectionHeaderType {
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 {
Layout.fillWidth: true
Layout.topMargin: 8
color: AmneziaStyle.color.textTertiary
text: qsTr("If the update won't install, message us")
}
LabelWithButtonType {
ListItemType {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
Layout.topMargin: 16
text: qsTr("Telegram")
descriptionText: qsTr("We'll reply in chat")
leftImageSource: "qrc:/images/controls/telegram.svg"
rightImageSource: "qrc:/images/controls/chevron-right.svg"
subtitle: qsTr("We'll reply in chat")
icon.source: "qrc:/images/controls/telegram.svg"
trailingIconSource: "qrc:/images/controls/chevron-right.svg"
clickedFunction: function() {
onClicked: {
Qt.openUrlExternally(qsTr("https://t.me/amnezia_vpn_en"))
}
}
DividerType {}
LabelWithButtonType {
ListItemType {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("support@amnezia.org")
descriptionText: qsTr("Support email")
leftImageSource: "qrc:/images/controls/mail.svg"
rightImageSource: "qrc:/images/controls/chevron-right.svg"
subtitle: qsTr("Support email")
icon.source: "qrc:/images/controls/mail.svg"
trailingIconSource: "qrc:/images/controls/chevron-right.svg"
clickedFunction: function() {
onClicked: {
Qt.openUrlExternally(qsTr("mailto:support@amnezia.org"))
}
}
DividerType {}
LabelWithButtonType {
ListItemType {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
text: qsTr("amnezia.org")
descriptionText: qsTr("Download the update manually")
leftImageSource: "qrc:/images/controls/amnezia.svg"
rightImageSource: "qrc:/images/controls/chevron-right.svg"
subtitle: qsTr("Download the update manually")
icon.source: "qrc:/images/controls/amnezia.svg"
trailingIconSource: "qrc:/images/controls/chevron-right.svg"
clickedFunction: function() {
onClicked: {
Qt.openUrlExternally(LanguageUiController.getCurrentSiteUrl(""))
}
}
-196
View File
@@ -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
}
}
+18 -74
View File
@@ -9,10 +9,13 @@ import "TextTypes"
Popup {
id: root
enum Role { Default, Destructive }
property string headerText
property string descriptionText
property string cancelButtonText: qsTr("Cancel")
property string confirmButtonText
property int role: AlertDialogType.Default
signal confirmed()
@@ -20,7 +23,7 @@ Popup {
x: Math.round((parent.width - width) / 2)
y: Math.round((parent.height - height) / 2)
width: parent.width - 32
width: Math.min(parent.width - 32, 400)
padding: 0
topPadding: 20
@@ -30,7 +33,7 @@ Popup {
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
Overlay.modal: Rectangle {
color: AmneziaStyle.color.overlayScrim
color: AmneziaStyle.color.bgOverlay
}
enter: Transition {
@@ -42,21 +45,16 @@ Popup {
}
onOpened: {
FocusController.pushRootObject(root)
cancelButton.forceActiveFocus()
}
onClosed: {
FocusController.dropRootObject(root)
}
background: Rectangle {
radius: 16
color: AmneziaStyle.color.surfaceElevated
color: AmneziaStyle.color.bgSurfaceAlt
}
contentItem: ColumnLayout {
spacing: 0
spacing: 2
ColumnLayout {
Layout.fillWidth: true
@@ -66,13 +64,13 @@ Popup {
spacing: 8
AppH3TextType {
H3TextType {
Layout.fillWidth: true
text: root.headerText
}
AppSmallTextType {
SmallTextType {
Layout.fillWidth: true
visible: text !== ""
@@ -83,33 +81,30 @@ Popup {
RowLayout {
Layout.fillWidth: true
Layout.leftMargin: 8
Layout.rightMargin: 8
Layout.bottomMargin: 8
spacing: 8
spacing: 0
AlertDialogButton {
DialogButtonType {
id: cancelButton
Layout.fillWidth: true
Layout.preferredWidth: 1
text: root.cancelButtonText
textColor: AmneziaStyle.color.textTertiary
onClicked: {
root.close()
}
onClicked: root.close()
}
AlertDialogButton {
DialogButtonType {
id: confirmButton
Layout.fillWidth: true
Layout.preferredWidth: 1
text: root.confirmButtonText
textColor: AmneziaStyle.color.accentDanger
font.weight: 600
emphasized: true
textColor: root.role === AlertDialogType.Destructive ? AmneziaStyle.color.statusDestructive
: AmneziaStyle.color.textAccent
onClicked: {
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
}
}
}
+9 -30
View File
@@ -1,8 +1,4 @@
import QtQuick
import QtQuick.Layouts
import Qt5Compat.GraphicalEffects
import Style 1.0
FocusScope {
id: root
@@ -10,42 +6,25 @@ FocusScope {
property string backButtonImage: "qrc:/images/controls/arrow-left.svg"
property var backButtonFunction
implicitWidth: content.implicitWidth
implicitHeight: content.implicitHeight
implicitWidth: backButton.implicitWidth + backButton.x
implicitHeight: backButton.implicitHeight
visible: backButtonImage !== ""
RowLayout {
id: content
anchors.fill: parent
anchors.leftMargin: 8
ImageButtonType {
IconButtonType {
id: backButton
image: backButtonImage
imageColor: AmneziaStyle.color.paleGray
implicitWidth: 40
implicitHeight: 40
x: 16
anchors.verticalCenter: parent.verticalCenter
icon.source: root.backButtonImage
onClicked: {
if (backButtonFunction && typeof backButtonFunction === "function") {
backButtonFunction()
if (root.backButtonFunction && typeof root.backButtonFunction === "function") {
root.backButtonFunction()
} else {
PageController.closePage()
}
}
}
Rectangle {
id: background
Layout.fillWidth: true
color: AmneziaStyle.color.transparent
}
}
Keys.onEnterPressed: backButton.clicked()
Keys.onReturnPressed: backButton.clicked()
}
+3 -2
View File
@@ -9,6 +9,7 @@ Item {
property Item sourceItem
property real blurPadding: 40
property real blurRadius: 50
property real radius: height / 2
property color tintColor: Qt.alpha(AmneziaStyle.color.textStaticWhite, 0.10)
property color borderColor: Qt.alpha(AmneziaStyle.color.textStaticWhite, 0.75)
@@ -54,13 +55,13 @@ Item {
id: backdropMask
anchors.fill: parent
radius: height / 2
radius: root.radius
visible: false
}
Rectangle {
anchors.fill: parent
radius: height / 2
radius: root.radius
color: root.tintColor
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
}
}
}
}
-210
View File
@@ -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()
}
}
}
+169
View File
@@ -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()
}
}
}
+30
View File
@@ -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
}
}
}
}
}
+194
View File
@@ -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()
}
+59 -133
View File
@@ -1,8 +1,6 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import QtQuick.Window
import Qt5Compat.GraphicalEffects
import Style 1.0
@@ -34,20 +32,15 @@ Popup {
return 0
}
leftMargin: 25
rightMargin: 25
bottomMargin: 70 + SettingsController.safeAreaBottomMargin
parent: Overlay.overlay
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)
y: {
const centered = Math.round((parent.height - height) / 2)
if (root.keyboardHeight === 0) {
return centered
}
return Math.max(20, Math.min(centered, parent.height - root.keyboardHeight - height - 16))
}
x: 0
y: parent ? parent.height - height - root.keyboardHeight : 0
padding: 0
Behavior on y {
NumberAnimation {
@@ -57,14 +50,15 @@ Popup {
}
modal: true
closePolicy: Popup.NoAutoClose
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
Overlay.modal: Rectangle {
color: AmneziaStyle.color.translucentMidnightBlack
color: AmneziaStyle.color.bgOverlay
}
focus: true
onOpened: {
timer.start()
solutionField.textField.text = ""
solutionField.textField.forceActiveFocus()
}
@@ -81,77 +75,69 @@ Popup {
}
}
onClosed: {
FocusController.dropRootObject(root)
enter: Transition {
NumberAnimation { property: "opacity"; from: 0; to: 1; duration: 150 }
}
background: Rectangle {
exit: Transition {
NumberAnimation { property: "opacity"; from: 1; to: 0; duration: 150 }
}
background: Item {
Rectangle {
anchors.fill: parent
color: AmneziaStyle.color.slateGray
radius: 22
anchors.bottomMargin: -border.width
topLeftRadius: 16
topRightRadius: 16
color: AmneziaStyle.color.bgSurfaceAlt
border.color: AmneziaStyle.color.borderFaint
border.width: 1
}
Timer {
id: timer
interval: 200
onTriggered: {
FocusController.pushRootObject(root)
FocusController.setFocusItem(solutionField.textField)
SheetGrabberType {
anchors.top: parent.top
anchors.topMargin: 16
anchors.horizontalCenter: parent.horizontalCenter
}
repeat: false
running: true
}
contentItem: Item {
implicitWidth: contentLayout.implicitWidth
implicitHeight: contentLayout.implicitHeight
anchors.fill: parent
ColumnLayout {
id: contentLayout
anchors.fill: parent
anchors.leftMargin: 20
anchors.rightMargin: 20
anchors.topMargin: 20
anchors.bottomMargin: 20
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 21 + 16
anchors.leftMargin: 16
anchors.rightMargin: 16
spacing: 16
spacing: 0
Text {
H2TextType {
id: titleText
Layout.fillWidth: true
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
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 {
Layout.fillWidth: true
Layout.preferredHeight: 200
Rectangle {
id: imagePanel
anchors.fill: parent
color: AmneziaStyle.color.pearlGray
Layout.fillWidth: true
Layout.preferredHeight: 128
Layout.topMargin: 32
color: AmneziaStyle.color.bgSurfaceInverse
radius: 16
clip: true
Image {
id: captchaImage
anchors.centerIn: parent
anchors.fill: parent
fillMode: Image.PreserveAspectFit
cache: false
@@ -169,67 +155,36 @@ Popup {
}
}
BusyIndicator {
SpinnerType {
anchors.centerIn: parent
running: captchaImage.status === Image.Loading
color: AmneziaStyle.color.textInverted
visible: captchaImage.status === Image.Loading
}
Rectangle {
id: refreshHit
IconButtonType {
id: refreshButton
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.margins: 10
width: 44
height: 44
radius: width / 2
color: AmneziaStyle.color.charcoalGray
anchors.rightMargin: 16
anchors.bottomMargin: 12
Image {
id: refreshIcon
filled: true
icon.source: "qrc:/images/controls/refresh-cw.svg"
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()
}
}
}
}
TextFieldWithHeaderType {
TextFieldType {
id: solutionField
Layout.fillWidth: true
Layout.alignment: Qt.AlignLeft
Layout.topMargin: 26
headerText: qsTr("Digits from the image")
headerTextColor: AmneziaStyle.color.mutedGray
textField.placeholderText: qsTr("_ _ _ _ _ _")
textField.placeholderTextColor: AmneziaStyle.color.mutedGray
textField.inputMethodHints: Qt.ImhDigitsOnly | Qt.ImhNoPredictiveText
textField.maximumLength: 6
textField.validator: RegularExpressionValidator { regularExpression: /^[0-9]{0,6}$/ }
@@ -240,47 +195,18 @@ Popup {
}
}
Item {
Layout.fillWidth: true
Layout.preferredHeight: 8
}
BasicButtonType {
ButtonType {
id: continueButton
Layout.fillWidth: true
implicitHeight: 52
Layout.topMargin: 12
text: qsTr("Continue")
defaultColor: AmneziaStyle.color.paleGray
hoveredColor: AmneziaStyle.color.lightGray
pressedColor: AmneziaStyle.color.mutedGray
textColor: AmneziaStyle.color.midnightBlack
clickedFunc: function() {
onClicked: {
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()
}
}
}
}
-166
View File
@@ -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.Layouts
import Qt5Compat.GraphicalEffects
import QtQuick.Controls.impl
import Style 1.0
@@ -21,20 +21,17 @@ ColumnLayout {
Layout.fillWidth: true
spacing: 8
Image {
IconImage {
Layout.preferredWidth: 18
Layout.preferredHeight: 18
source: root.iconSource
sourceSize: Qt.size(18, 18)
layer.enabled: true
layer.effect: ColorOverlay {
color: AmneziaStyle.color.accentSuccess
}
color: AmneziaStyle.color.statusSuccess
}
AppH3EmphasizedTextType {
H3TextType {
font.weight: 600
Layout.fillWidth: true
text: root.title
@@ -52,7 +49,7 @@ ColumnLayout {
Layout.fillWidth: true
spacing: 0
AppTextType {
BodyTextType {
Layout.alignment: Qt.AlignTop
Layout.preferredWidth: 24
@@ -62,7 +59,7 @@ ColumnLayout {
text: "•"
}
AppTextType {
BodyTextType {
Layout.fillWidth: true
color: AmneziaStyle.color.textTertiary
+75 -133
View File
@@ -1,7 +1,8 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.impl
import QtQuick.Effects
import QtQuick.Layouts
import Qt5Compat.GraphicalEffects
import Style 1.0
@@ -11,179 +12,120 @@ CheckBox {
id: root
property string descriptionText
property string descriptionTextColor: AmneziaStyle.color.mutedGray
property string descriptionTextDisabledColor: AmneziaStyle.color.charcoalGray
property string textColor: AmneziaStyle.color.paleGray
property string textDisabledColor: AmneziaStyle.color.mutedGray
implicitWidth: implicitContentWidth + leftPadding + rightPadding
implicitHeight: Math.max(implicitContentHeight, implicitIndicatorHeight) + topPadding + bottomPadding
property string hoveredColor: AmneziaStyle.color.barelyTranslucentWhite
property string defaultColor: AmneziaStyle.color.transparent
property string pressedColor: AmneziaStyle.color.barelyTranslucentWhite
topPadding: 8
bottomPadding: 8
leftPadding: implicitIndicatorWidth + spacing
rightPadding: 0
spacing: 12
property string defaultBorderColor: AmneziaStyle.color.paleGray
property string checkedBorderColor: AmneziaStyle.color.goldenApricot
property string checkedBorderDisabledColor: AmneziaStyle.color.deepBrown
hoverEnabled: true
focusPolicy: Qt.TabFocus
property string borderFocusedColor: AmneziaStyle.color.paleGray
opacity: root.enabled ? 1 : 0.5
property string checkedImageColor: AmneziaStyle.color.goldenApricot
property string pressedImageColor: AmneziaStyle.color.burntOrange
property string defaultImageColor: AmneziaStyle.color.transparent
property string checkedDisabledImageColor: AmneziaStyle.color.mutedBrown
indicator: Item {
x: 0
y: (root.height - height) / 2
property string imageSource: "qrc:/images/controls/check.svg"
implicitWidth: 40
implicitHeight: 40
property bool isFocusable: true
Rectangle {
anchors.fill: parent
Keys.onTabPressed: {
FocusController.nextKeyTabItem()
}
Keys.onBacktabPressed: {
FocusController.previousKeyTabItem()
}
Keys.onUpPressed: {
FocusController.nextKeyUpItem()
}
Keys.onDownPressed: {
FocusController.nextKeyDownItem()
}
Keys.onLeftPressed: {
FocusController.nextKeyLeftItem()
}
Keys.onRightPressed: {
FocusController.nextKeyRightItem()
}
hoverEnabled: enabled ? true : false
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 }
radius: 12
visible: root.enabled && (root.hovered || root.down)
color: root.checked ? AmneziaStyle.color.bgStateLayerAccent : AmneziaStyle.color.bgStateLayer
}
Rectangle {
id: imageBorder
id: box
anchors.centerIn: parent
width: 24
height: 24
color: AmneziaStyle.color.transparent
border.color: root.checked ?
(root.enabled ?
checkedBorderColor :
checkedBorderDisabledColor) :
defaultBorderColor
radius: 6
color: root.checked ? AmneziaStyle.color.bgBase : AmneziaStyle.color.transparent
border.width: 1
radius: 4
border.color: root.checked ? AmneziaStyle.color.accentDark : AmneziaStyle.color.borderBold
Image {
anchors.centerIn: parent
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
}
}
}
}
}
Behavior on border.color {
ColorAnimation { duration: 150 }
}
}
contentItem: Item {
anchors.left: parent.left
anchors.right: parent.right
anchors.leftMargin: 8 + background.width
MultiEffect {
anchors.fill: check
implicitHeight: content.implicitHeight
source: check
visible: root.checked
shadowEnabled: true
shadowColor: AmneziaStyle.color.accentLight
shadowBlur: 0.6
shadowHorizontalOffset: 0
shadowVerticalOffset: 0
}
ColumnLayout {
id: content
IconImage {
id: check
anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
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: ColumnLayout {
spacing: 4
ListItemTitleType {
H3TextType {
Layout.fillWidth: true
visible: text !== ""
text: root.text
color: root.enabled ? root.textColor : root.textDisabledColor
}
CaptionTextType {
id: description
SmallTextType {
Layout.fillWidth: true
visible: text !== ""
text: root.descriptionText
color: root.enabled ? root.descriptionTextColor : root.descriptionTextDisabledColor
visible: root.descriptionText !== ""
}
}
}
MouseArea {
anchors.fill: parent
HoverHandler {
cursorShape: Qt.PointingHandCursor
enabled: false
}
Keys.onEnterPressed: event => handleSwitch(event)
Keys.onReturnPressed: event => handleSwitch(event)
Keys.onSpacePressed: event => handleSwitch(event)
function handleSwitch(event) {
Keys.onEnterPressed: function(event) {
if (!event.isAutoRepeat) {
root.checked = !root.checked
root.checkedChanged()
root.toggle()
root.toggled()
}
}
Keys.onReturnPressed: function(event) {
if (!event.isAutoRepeat) {
root.toggle()
root.toggled()
}
event.accepted = true
}
}
+67
View File
@@ -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 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) {
const maxY = root.listView.originY + Math.max(0, root.listView.contentHeight - root.listView.height)
return Math.max(root.listView.originY, Math.min(value, maxY))
@@ -44,18 +64,20 @@ Item {
id: headerComponent
Item {
readonly property real collapsibleHeight: collapsibleColumn.implicitHeight + 4
readonly property real collapsibleHeight: collapsibleColumn.implicitHeight + 12
width: root.listView.width
height: collapsibleHeight + pinnedBlock.height
onHeightChanged: Qt.callLater(root.keepAtTop)
ColumnLayout {
id: collapsibleColumn
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.topMargin: 4
anchors.topMargin: 12
spacing: 0
@@ -88,9 +110,9 @@ Item {
anchors.left: parent.left
anchors.right: parent.right
color: AmneziaStyle.color.midnightBlack
color: AmneziaStyle.color.bgBase
implicitHeight: navigationRow.y + navigationRow.height + 4
implicitHeight: navigationRow.y + navigationRow.height
MouseArea {
anchors.fill: parent
@@ -103,16 +125,20 @@ Item {
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.topMargin: 20 + PageController.safeAreaTopMargin
height: backButton.implicitHeight
anchors.topMargin: PageController.safeAreaTopMargin
height: 48
BackButtonType {
IconButtonType {
id: backButton
objectName: "backButton"
anchors.left: parent.left
anchors.right: navigationButtonsRow.left
anchors.verticalCenter: parent.verticalCenter
x: 16
y: 4
size: IconButtonType.Small
icon.source: "qrc:/images/controls/arrow-left.svg"
onClicked: PageController.closePage()
}
Row {
@@ -120,7 +146,9 @@ Item {
anchors.right: parent.right
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)
height: pinnedColumn.implicitHeight
color: AmneziaStyle.color.midnightBlack
color: AmneziaStyle.color.bgBase
MouseArea {
anchors.fill: parent
@@ -151,30 +179,49 @@ Item {
}
}
Header1TextType {
H1TextType {
id: largeTitleMetrics
visible: false
text: movingTitle.text
}
Header1TextType {
H1TextType {
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 expandedX: 16
readonly property real compactX: (root.width - movingTitle.width * movingTitle.compactScale) / 2
readonly property real expandedY: topBar.height + 4 - Math.min(0, root.listScrolled)
readonly property real compactY: navigationRow.y
+ (navigationRow.height - movingTitle.implicitHeight * movingTitle.compactScale) / 2
readonly property real compactX: (root.width - compactTitle.width) / 2
readonly property real expandedY: topBar.height + 12 - Math.min(0, root.listScrolled)
readonly property real compactY: navigationRow.y + (navigationRow.height - compactTitle.height) / 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
y: Math.max(movingTitle.compactY, movingTitle.expandedY - Math.max(0, root.listScrolled))
x: movingTitle.currentX
y: movingTitle.currentY
width: Math.min(movingTitle.implicitWidth, root.width - 32)
transformOrigin: Item.TopLeft
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
maximumLineCount: 1
+2 -2
View File
@@ -53,12 +53,12 @@ Menu {
MenuItem {
text: qsTr("C&ut")
enabled: textObj.selectedText
enabled: textObj.selectedText.length > 0
onTriggered: textObj.cut()
}
MenuItem {
text: qsTr("&Copy")
enabled: textObj.selectedText
enabled: textObj.selectedText.length > 0
onTriggered: textObj.copy()
}
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()
}
-14
View File
@@ -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
}
+22
View File
@@ -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
}
}
-386
View File
@@ -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
}
}
}
+85 -161
View File
@@ -1,5 +1,6 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.impl
import QtQuick.Layouts
import Style 1.0
@@ -7,227 +8,150 @@ import Style 1.0
import "TextTypes"
import "../Config"
Item {
AbstractButton {
id: root
property string text
property string textColor: AmneziaStyle.color.paleGray
property string textDisabledColor: AmneziaStyle.color.mutedGray
property int textMaximumLineCount: 2
property int textElide: Qt.ElideRight
enum Variant { Default, Primary }
property int variant: DropDownType.Default
property string descriptionText
property string descriptionTextColor: AmneziaStyle.color.mutedGray
property string descriptionTextDisabledColor: AmneziaStyle.color.charcoalGray
property string placeholderText
property string headerText
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 bool fitContent: false
property Item drawerParent
property Component listView
signal openTriggered
signal closeTriggered
readonly property bool isPrimary: root.variant === DropDownType.Primary
readonly property bool isFocusable: true
Keys.onTabPressed: {
FocusController.nextKeyTabItem()
function open() {
menu.open()
}
Keys.onBacktabPressed: {
FocusController.previousKeyTabItem()
function close() {
menu.close()
}
Keys.onUpPressed: {
FocusController.nextKeyUpItem()
}
implicitWidth: implicitContentWidth + leftPadding + rightPadding
implicitHeight: implicitContentHeight + topPadding + bottomPadding
Keys.onDownPressed: {
FocusController.nextKeyDownItem()
}
leftPadding: 16
rightPadding: root.isPrimary ? 4 : 16
topPadding: root.isPrimary ? 0 : 12
bottomPadding: root.isPrimary ? 0 : 12
Keys.onLeftPressed: {
FocusController.nextKeyLeftItem()
}
hoverEnabled: true
Keys.onRightPressed: {
FocusController.nextKeyRightItem()
}
opacity: root.enabled ? 1 : 0.4
Connections {
target: Qt.application
onClicked: menu.open()
function onStateChanged() {
if (Qt.application.state !== Qt.ApplicationActive) {
if (!menu.isClosed) {
menu.closeTriggered()
}
}
}
}
implicitWidth: rootButtonContent.implicitWidth
implicitHeight: rootButtonContent.implicitHeight
onOpenTriggered: {
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
background: Rectangle {
radius: 16
Rectangle {
id: rootButtonBackground
anchors.fill: focusBorder
anchors.margins: root.activeFocus ? 2 : 0
radius: root.activeFocus ? 14 : 16
color: {
if (root.enabled) {
if (root.pressed) {
return root.rootButtonBackgroundPressedColor
if (!root.isPrimary) {
return AmneziaStyle.color.bgSurface
}
return root.hovered ? root.rootButtonBackgroundHoveredColor : root.rootButtonBackgroundColor
} else {
return AmneziaStyle.color.transparent
if (root.down) {
return AmneziaStyle.color.buttonPrimaryPress
}
}
border.color: rootButtonDefaultBorderColor
border.width: 1
Behavior on border.color {
PropertyAnimation { duration: 200 }
return root.hovered ? AmneziaStyle.color.buttonPrimaryHover : AmneziaStyle.color.buttonPrimaryBg
}
Behavior on color {
PropertyAnimation { duration: 200 }
}
}
ColorAnimation { duration: 150 }
}
RowLayout {
id: rootButtonContent
Rectangle {
anchors.fill: parent
spacing: 0
radius: parent.radius
visible: !root.isPrimary && root.enabled && (root.hovered || root.down)
color: AmneziaStyle.color.bgStateLayer
}
FocusRingType {
radius: 16
visible: root.visualFocus
}
}
contentItem: RowLayout {
spacing: 8
ColumnLayout {
Layout.leftMargin: rootButtonTextLeftMargins
Layout.topMargin: rootButtonTextTopMargin
Layout.bottomMargin: rootButtonTextBottomMargin
Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter
LabelTextType {
spacing: 4
CaptionTextType {
Layout.fillWidth: true
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignVCenter
visible: root.descriptionText !== ""
color: root.enabled ? root.descriptionTextColor : root.descriptionTextDisabledColor
visible: text !== ""
text: root.descriptionText
}
ButtonTextType {
id: buttonText
H3TextType {
Layout.fillWidth: true
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignVCenter
visible: !root.isPrimary
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
maximumLineCount: root.textMaximumLineCount
elide: root.textElide
color: AmneziaStyle.color.textInverted
maximumLineCount: 1
elide: Text.ElideRight
}
}
ImageButtonType {
Layout.rightMargin: 16
Item {
Layout.preferredWidth: 48
Layout.preferredHeight: 48
Layout.alignment: Qt.AlignVCenter
implicitWidth: 40
implicitHeight: 40
IconImage {
anchors.centerIn: parent
hoverEnabled: false
image: rootButtonImage
imageColor: rootButtonImageColor
width: 24
height: 24
source: "qrc:/images/controls/chevron-down.svg"
sourceSize: Qt.size(24, 24)
color: root.isPrimary ? AmneziaStyle.color.iconInverted : AmneziaStyle.color.iconDefault
}
}
}
MouseArea {
anchors.fill: rootButtonContent
HoverHandler {
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
parent: drawerParent
anchors.fill: parent
property real measuredContentHeight: 0
expandedHeight: (root.fitContent && measuredContentHeight > 0)
? Math.min(measuredContentHeight, drawerParent.height * root.drawerHeight)
: drawerParent.height * root.drawerHeight
? Math.min(measuredContentHeight, windowHeight * root.drawerHeight)
: windowHeight * root.drawerHeight
expandedStateContent: Item {
Item {
id: container
implicitHeight: menu.expandedHeight
anchors.fill: parent
property real fitHeight: backButton.implicitHeight + titleLabel.implicitHeight
+ (listViewLoader.item ? listViewLoader.item.contentHeight : 0) + 48
@@ -243,10 +167,10 @@ Item {
BackButtonType {
id: backButton
backButtonImage: root.headerBackButtonImage
backButtonFunction: function() { menu.closeTriggered() }
backButtonFunction: function() { menu.close() }
}
Header2Type {
SectionHeaderType {
id: titleLabel
Layout.leftMargin: 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
}
}
}
+4 -19
View File
@@ -1,18 +1,8 @@
import QtQuick 2.12
import QtQuick.Controls 2.12
import "../Config"
import QtQuick
import QtQuick.Controls
Flickable {
id: fl
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()
}
id: root
clip: true
width: parent.width
@@ -22,10 +12,5 @@ Flickable {
anchors.right: parent.right
anchors.rightMargin: 1
Keys.onUpPressed: scrollBar.decrease()
Keys.onDownPressed: scrollBar.increase()
ScrollBar.vertical: ScrollBarType {
id: scrollBar
}
ScrollBar.vertical: ScrollBarType {}
}
+37
View File
@@ -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
}
}
-79
View File
@@ -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()
}
}
}
+153
View File
@@ -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
}
}
+101
View File
@@ -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