feat: Add Advanced settings in new Advanced Tab
... under Settings/Wallet/Manage Tokens - rename Tokens List tab to Advanced tab - introduce a new `CurrencyAmountInput` component, backed by `FormattedDoubleProperty` C++ class (plus the respective SB page) - use `FastExpressionFoo` for the collectibles views as well Fixes #12611 Fixes #13040
This commit is contained in:
parent
e053d267f0
commit
263ed2a822
|
@ -0,0 +1,138 @@
|
|||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
|
||||
import Storybook 1.0
|
||||
import utils 1.0
|
||||
|
||||
import shared.controls 1.0
|
||||
|
||||
SplitView {
|
||||
id: root
|
||||
|
||||
orientation: Qt.Horizontal
|
||||
|
||||
Item {
|
||||
SplitView.fillWidth: true
|
||||
SplitView.fillHeight: true
|
||||
|
||||
CurrencyAmountInput {
|
||||
id: input
|
||||
anchors.centerIn: parent
|
||||
currencySymbol: ctrlCurrencySymbol.text
|
||||
decimals: ctrlDecimals.value
|
||||
readOnly: ctrlReadOnly.checked
|
||||
enabled: ctrlEnabled.checked
|
||||
value: ctrlAmount.text
|
||||
}
|
||||
}
|
||||
|
||||
LogsAndControlsPanel {
|
||||
SplitView.minimumWidth: 300
|
||||
SplitView.preferredWidth: 400
|
||||
|
||||
SplitView.fillHeight: true
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
RowLayout {
|
||||
Label {
|
||||
text: "Value:\t"
|
||||
}
|
||||
TextField {
|
||||
id: ctrlAmount
|
||||
text: "0.10"
|
||||
placeholderText: "Numeric value"
|
||||
onEditingFinished: input.value = text
|
||||
}
|
||||
}
|
||||
RowLayout {
|
||||
Label {
|
||||
text: "Currency:\t"
|
||||
}
|
||||
TextField {
|
||||
id: ctrlCurrencySymbol
|
||||
text: "EUR"
|
||||
placeholderText: "Currency symbol"
|
||||
}
|
||||
}
|
||||
RowLayout {
|
||||
Label {
|
||||
text: "Decimals:\t"
|
||||
}
|
||||
SpinBox {
|
||||
id: ctrlDecimals
|
||||
from: 0
|
||||
to: 18
|
||||
value: 2
|
||||
}
|
||||
}
|
||||
Switch {
|
||||
id: ctrlReadOnly
|
||||
text: "Read only"
|
||||
}
|
||||
Switch {
|
||||
id: ctrlEnabled
|
||||
text: "Enabled"
|
||||
checked: true
|
||||
}
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
Label {
|
||||
text: "Numeric value:"
|
||||
}
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignRight
|
||||
horizontalAlignment: Text.AlignRight
|
||||
font.bold: true
|
||||
text: input.asString
|
||||
}
|
||||
}
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Label {
|
||||
text: "Valid:"
|
||||
}
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignRight
|
||||
horizontalAlignment: Text.AlignRight
|
||||
font.bold: true
|
||||
text: input.valid ? "true" : "false"
|
||||
}
|
||||
}
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Label {
|
||||
text: "Formatted as locale string:"
|
||||
}
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignRight
|
||||
horizontalAlignment: Text.AlignRight
|
||||
font.bold: true
|
||||
text: input.asLocaleString
|
||||
}
|
||||
}
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Label {
|
||||
text: "Locale:"
|
||||
}
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignRight
|
||||
horizontalAlignment: Text.AlignRight
|
||||
font.bold: true
|
||||
text: input.locale
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// category: Controls
|
||||
|
||||
// https://www.figma.com/file/eM26pyHZUeAwMLviaS1KJn/%E2%9A%99%EF%B8%8F-Wallet-Settings%3A-Manage-Tokens?type=design&node-id=305-139866&mode=design&t=g49O9LFh8PkuPxZB-0
|
|
@ -24,6 +24,8 @@ public slots:
|
|||
qmlRegisterSingletonType<TextUtils>("TextUtils", 1, 0, "TextUtils", &TextUtils::qmlInstance);
|
||||
|
||||
QStandardPaths::setTestModeEnabled(true);
|
||||
|
||||
QLocale::setDefault(QLocale(QLocale::English, QLocale::UnitedStates));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,182 @@
|
|||
import QtQuick 2.15
|
||||
import QtTest 1.15
|
||||
|
||||
import Storybook 1.0
|
||||
import shared.controls 1.0
|
||||
|
||||
Item {
|
||||
id: root
|
||||
width: 600
|
||||
height: 400
|
||||
|
||||
Component {
|
||||
id: componentUnderTest
|
||||
CurrencyAmountInput {
|
||||
id: input
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: defaults
|
||||
|
||||
readonly property int decimals: 2
|
||||
readonly property string currencySymbol: "USD"
|
||||
}
|
||||
|
||||
TestCase {
|
||||
name: "CurrencyAmountInput"
|
||||
when: windowShown
|
||||
|
||||
property CurrencyAmountInput controlUnderTest: null
|
||||
|
||||
function init() {
|
||||
controlUnderTest = createTemporaryObject(componentUnderTest, root)
|
||||
controlUnderTest.locale = "en_US"
|
||||
}
|
||||
|
||||
function test_getSetValueProgramatically() {
|
||||
verify(!!controlUnderTest)
|
||||
|
||||
// initial value is 0
|
||||
verify(controlUnderTest.value === 0)
|
||||
|
||||
// initial num of decimals is 2
|
||||
verify(controlUnderTest.decimals === defaults.decimals)
|
||||
|
||||
// verify setting a value yields a valid state
|
||||
controlUnderTest.value = 1.23
|
||||
|
||||
// verify both the value and text displayed is correct
|
||||
verify(controlUnderTest.valid)
|
||||
verify(controlUnderTest.value === 1.23)
|
||||
verify(controlUnderTest.text === "1.23")
|
||||
verify(controlUnderTest.text === controlUnderTest.asString)
|
||||
|
||||
// verify setting value as text works as well
|
||||
controlUnderTest.value = "456.78"
|
||||
verify(controlUnderTest.valid)
|
||||
verify(controlUnderTest.value === 456.78)
|
||||
verify(controlUnderTest.text === "456.78")
|
||||
verify(controlUnderTest.text === controlUnderTest.asString)
|
||||
}
|
||||
|
||||
function test_inputValueManually() {
|
||||
verify(!!controlUnderTest)
|
||||
|
||||
// click the control to get focus and type "1.23"
|
||||
mouseClick(controlUnderTest)
|
||||
controlUnderTest.clear()
|
||||
keyClick(Qt.Key_1)
|
||||
keyClick(Qt.Key_Period)
|
||||
keyClick(Qt.Key_2)
|
||||
keyClick(Qt.Key_3)
|
||||
|
||||
// verify we get 1.23 back
|
||||
verify(controlUnderTest.valid)
|
||||
verify(controlUnderTest.value === 1.23)
|
||||
verify(controlUnderTest.text === "1.23")
|
||||
verify(controlUnderTest.text === controlUnderTest.asString)
|
||||
}
|
||||
|
||||
function test_decimals() {
|
||||
verify(!!controlUnderTest)
|
||||
|
||||
// initial num of decimals is 2
|
||||
verify(controlUnderTest.decimals === defaults.decimals)
|
||||
|
||||
// set 8 decimals
|
||||
controlUnderTest.decimals = 8
|
||||
verify(controlUnderTest.decimals === 8)
|
||||
|
||||
// set a number with 8 decimals
|
||||
controlUnderTest.value = 1.12345678
|
||||
|
||||
// verify the value and validity
|
||||
verify(controlUnderTest.valid)
|
||||
verify(controlUnderTest.value === 1.12345678)
|
||||
verify(controlUnderTest.text === "1.12345678")
|
||||
verify(controlUnderTest.text === controlUnderTest.asString)
|
||||
|
||||
// set back to 3 decimals
|
||||
controlUnderTest.decimals = 3
|
||||
|
||||
// setting a value with more decimals -> invalid
|
||||
controlUnderTest.value = 1.1234
|
||||
verify(!controlUnderTest.valid)
|
||||
}
|
||||
|
||||
function test_currencySymbol() {
|
||||
verify(!!controlUnderTest)
|
||||
|
||||
// USD is default
|
||||
verify(controlUnderTest.currencySymbol === defaults.currencySymbol)
|
||||
|
||||
// try setting a different one
|
||||
controlUnderTest.currencySymbol = "EUR"
|
||||
verify(controlUnderTest.currencySymbol === "EUR")
|
||||
|
||||
// try clearing the currency symbol
|
||||
controlUnderTest.currencySymbol = ""
|
||||
verify(controlUnderTest.currencySymbol === "")
|
||||
}
|
||||
|
||||
function test_explicitLocale() {
|
||||
verify(!!controlUnderTest)
|
||||
controlUnderTest.locale = "cs_CZ"
|
||||
|
||||
// verify setting a value programatically yields a valid state
|
||||
controlUnderTest.value = 1.23
|
||||
verify(controlUnderTest.valid)
|
||||
verify(controlUnderTest.value === 1.23)
|
||||
verify(controlUnderTest.text === "1,23")
|
||||
|
||||
// verify the text displayed observes the locale decimal point (,)
|
||||
verify(controlUnderTest.text === "1,23")
|
||||
|
||||
// verify typing both a period and comma (this locale's decimal separator) both yield the same correct value
|
||||
mouseClick(controlUnderTest)
|
||||
|
||||
// first with the default comma (,) as decimal separator
|
||||
controlUnderTest.clear()
|
||||
keyClick(Qt.Key_6)
|
||||
keyClick(Qt.Key_Comma)
|
||||
keyClick(Qt.Key_6)
|
||||
keyClick(Qt.Key_6)
|
||||
verify(controlUnderTest.valid)
|
||||
verify(controlUnderTest.value === 6.66)
|
||||
verify(controlUnderTest.text === "6,66")
|
||||
|
||||
// try the fallback decimal separator (.)
|
||||
controlUnderTest.clear()
|
||||
verify(controlUnderTest.text === "")
|
||||
keyClick(Qt.Key_6)
|
||||
keyClick(Qt.Key_Period)
|
||||
keyClick(Qt.Key_6)
|
||||
keyClick(Qt.Key_6)
|
||||
verify(controlUnderTest.valid)
|
||||
verify(controlUnderTest.value === 6.66)
|
||||
verify(controlUnderTest.text === "6,66")
|
||||
}
|
||||
|
||||
function test_validator() {
|
||||
verify(!!controlUnderTest)
|
||||
controlUnderTest.decimals = 4
|
||||
controlUnderTest.value = 1.1234
|
||||
verify(controlUnderTest.valid)
|
||||
|
||||
controlUnderTest.decimals = 3
|
||||
verify(!controlUnderTest.valid)
|
||||
|
||||
// delete one char from the middle and type some string
|
||||
mouseClick(controlUnderTest)
|
||||
keyClick(Qt.Key_Left)
|
||||
keyClick(Qt.Key_Left)
|
||||
keyClick(Qt.Key_Backspace)
|
||||
keyClick(Qt.Key_A) // <== should get ignored
|
||||
|
||||
verify(controlUnderTest.valid)
|
||||
verify(controlUnderTest.value === 1.134)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -98,6 +98,7 @@ add_library(StatusQ SHARED
|
|||
include/StatusQ/fastexpressionfilter.h
|
||||
include/StatusQ/fastexpressionrole.h
|
||||
include/StatusQ/fastexpressionsorter.h
|
||||
include/StatusQ/formatteddoubleproperty.h
|
||||
include/StatusQ/leftjoinmodel.h
|
||||
include/StatusQ/modelutilsinternal.h
|
||||
include/StatusQ/permissionutilsinternal.h
|
||||
|
@ -116,6 +117,7 @@ add_library(StatusQ SHARED
|
|||
src/fastexpressionfilter.cpp
|
||||
src/fastexpressionrole.cpp
|
||||
src/fastexpressionsorter.cpp
|
||||
src/formatteddoubleproperty.cpp
|
||||
src/leftjoinmodel.cpp
|
||||
src/modelutilsinternal.cpp
|
||||
src/permissionutilsinternal.cpp
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
#pragma once
|
||||
|
||||
#include <QLocale>
|
||||
|
||||
/**
|
||||
* @brief The FormattedDoubleProperty class serves as a proxy for numeric inputs in QML
|
||||
*
|
||||
* It keeps track of its internal `double` value as returns that as a @p value.
|
||||
* When setting the @p value, it accepts both a numeric value (int/float/double) or a string
|
||||
* representation thereof.
|
||||
*/
|
||||
class FormattedDoubleProperty : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(QVariant value READ value WRITE setValue NOTIFY valueChanged FINAL) //< internal value (double)
|
||||
Q_PROPERTY(QString asString READ asString NOTIFY valueChanged FINAL) //< value as a "C" string, `.` as decimal separator
|
||||
Q_PROPERTY(QString locale READ locale WRITE setLocale NOTIFY localeChanged FINAL) //< locale name, defaults to user's own
|
||||
public:
|
||||
explicit FormattedDoubleProperty(QObject* parent = nullptr);
|
||||
|
||||
/**
|
||||
* @param decimals numbers of decimals to display (defaults to shortest possible)
|
||||
* @return @p value formatted according to the selected locale, with the specified number of decimal places
|
||||
*/
|
||||
Q_INVOKABLE QString asLocaleString(int decimals = QLocale::FloatingPointShortest) const;
|
||||
|
||||
signals:
|
||||
void valueChanged();
|
||||
void localeChanged();
|
||||
|
||||
private:
|
||||
QVariant value() const;
|
||||
void setValue(const QVariant& newValue);
|
||||
double m_value{0.0};
|
||||
|
||||
QString asString() const;
|
||||
|
||||
QString locale() const;
|
||||
void setLocale(const QString& newLocale);
|
||||
QLocale m_locale{QLocale()};
|
||||
};
|
|
@ -0,0 +1,66 @@
|
|||
#include "StatusQ/formatteddoubleproperty.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
FormattedDoubleProperty::FormattedDoubleProperty(QObject* parent)
|
||||
: QObject{parent}
|
||||
{
|
||||
m_locale.setNumberOptions(QLocale::DefaultNumberOptions | QLocale::OmitGroupSeparator);
|
||||
}
|
||||
|
||||
QVariant FormattedDoubleProperty::value() const
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
|
||||
void FormattedDoubleProperty::setValue(const QVariant& newValue)
|
||||
{
|
||||
if (!newValue.isValid()) {
|
||||
qWarning() << "Setting property to invalid value:" << newValue << "is not supported";
|
||||
return;
|
||||
}
|
||||
|
||||
auto ok = false;
|
||||
auto tempValue = newValue.toDouble(&ok);
|
||||
|
||||
if (!ok && newValue.canConvert<QString>()) {
|
||||
tempValue = m_locale.toDouble(newValue.toString(), &ok);
|
||||
}
|
||||
|
||||
if (!ok || qIsNaN(tempValue)) {
|
||||
qWarning() << "Failed set value property from:" << newValue << "; with type:" << newValue.typeName();
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_value != tempValue) {
|
||||
m_value = tempValue;
|
||||
emit valueChanged();
|
||||
}
|
||||
}
|
||||
|
||||
QString FormattedDoubleProperty::asString() const
|
||||
{
|
||||
return QString::number(m_value, 'f', QLocale::FloatingPointShortest);
|
||||
}
|
||||
|
||||
QString FormattedDoubleProperty::asLocaleString(int decimals) const
|
||||
{
|
||||
return m_locale.toString(m_value, 'f', decimals);
|
||||
}
|
||||
|
||||
QString FormattedDoubleProperty::locale() const
|
||||
{
|
||||
return m_locale.name();
|
||||
}
|
||||
|
||||
void FormattedDoubleProperty::setLocale(const QString& newLocale)
|
||||
{
|
||||
if (m_locale.name() == newLocale)
|
||||
return;
|
||||
|
||||
if (newLocale.isEmpty())
|
||||
m_locale = QLocale(); // user default
|
||||
else
|
||||
m_locale = QLocale(newLocale); // explicit
|
||||
emit localeChanged();
|
||||
}
|
|
@ -19,6 +19,7 @@
|
|||
#include "StatusQ/submodelproxymodel.h"
|
||||
#include "StatusQ/sumaggregator.h"
|
||||
#include "StatusQ/writableproxymodel.h"
|
||||
#include "StatusQ/formatteddoubleproperty.h"
|
||||
|
||||
#include "wallet/managetokenscontroller.h"
|
||||
#include "wallet/managetokensmodel.h"
|
||||
|
@ -52,6 +53,7 @@ public:
|
|||
qmlRegisterType<RolesRenamingModel>("StatusQ", 0, 1, "RolesRenamingModel");
|
||||
qmlRegisterType<SumAggregator>("StatusQ", 0, 1, "SumAggregator");
|
||||
qmlRegisterType<WritableProxyModel>("StatusQ", 0, 1, "WritableProxyModel");
|
||||
qmlRegisterType<FormattedDoubleProperty>("StatusQ", 0, 1, "FormattedDoubleProperty");
|
||||
|
||||
qmlRegisterSingletonType<QClipboardProxy>("StatusQ", 0, 1, "QClipboardProxy", &QClipboardProxy::qmlInstance);
|
||||
|
||||
|
|
|
@ -32,12 +32,13 @@ StatusListView {
|
|||
subTitle: qsTr("%n token(s) · Last updated %1 @%2",
|
||||
"",
|
||||
model.tokensCount).arg(LocaleUtils.formatDate(model.updatedAt * 1000)).arg(LocaleUtils.formatTime(model.updatedAt, Locale.ShortFormat))
|
||||
statusListItemSubTitle.font.pixelSize: Style.current.additionalTextSize
|
||||
asset.name: model.image
|
||||
asset.isImage: true
|
||||
border.width: 1
|
||||
border.color: Theme.palette.baseColor5
|
||||
components: [
|
||||
StatusButton {
|
||||
StatusFlatButton {
|
||||
text: qsTr("View")
|
||||
|
||||
onClicked: keyFilter.value = model.key
|
||||
|
@ -56,7 +57,7 @@ StatusListView {
|
|||
width: parent.width - 4 // The rectangular path is rendered outside
|
||||
|
||||
icon: "add"
|
||||
text: qsTr("Add Token List (coming soon)")
|
||||
text: qsTr("Add a Token List (coming soon)")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -275,7 +275,7 @@ SettingsContentBase {
|
|||
id: manageTokensView
|
||||
sourcesOfTokensModel: tokensStore.sourcesOfTokensModel
|
||||
tokensListModel: tokensStore.extendedFlatTokensModel
|
||||
baseWalletAssetsModel: RootStore.assets // TODO include community assets (#12369)
|
||||
baseWalletAssetsModel: RootStore.assets
|
||||
baseWalletCollectiblesModel: {
|
||||
RootStore.setFillterAllAddresses() // FIXME no other way to get _all_ collectibles?
|
||||
// TODO concat proxy model to include community collectibles (#12519)
|
||||
|
|
|
@ -1,9 +1,15 @@
|
|||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
|
||||
import StatusQ.Core 0.1
|
||||
import StatusQ.Controls 0.1
|
||||
import StatusQ.Components 0.1
|
||||
import StatusQ.Core.Theme 0.1
|
||||
import StatusQ.Popups.Dialog 0.1
|
||||
|
||||
import shared.controls 1.0
|
||||
import shared.stores 1.0 as SharedStores
|
||||
import utils 1.0
|
||||
|
||||
import AppLayouts.Profile.panels 1.0
|
||||
|
@ -25,6 +31,7 @@ ColumnLayout {
|
|||
return false
|
||||
if (tabBar.currentIndex > d.collectiblesTabIndex)
|
||||
return false
|
||||
// FIXME take advanced settings into account here too (#13178)
|
||||
if (tabBar.currentIndex === d.collectiblesTabIndex && baseWalletCollectiblesModel.isFetching)
|
||||
return false
|
||||
return loader.item && loader.item.dirty
|
||||
|
@ -33,6 +40,7 @@ ColumnLayout {
|
|||
function saveChanges() {
|
||||
if (tabBar.currentIndex > d.collectiblesTabIndex)
|
||||
return
|
||||
// FIXME save advanced settings (#13178)
|
||||
loader.item.saveSettings()
|
||||
}
|
||||
|
||||
|
@ -47,7 +55,7 @@ ColumnLayout {
|
|||
|
||||
readonly property int assetsTabIndex: 0
|
||||
readonly property int collectiblesTabIndex: 1
|
||||
readonly property int tokenSourcesTabIndex: 2
|
||||
readonly property int advancedTabIndex: 2
|
||||
|
||||
function checkLoadMoreCollectibles() {
|
||||
if (tabBar.currentIndex !== collectiblesTabIndex)
|
||||
|
@ -88,7 +96,7 @@ ColumnLayout {
|
|||
|
||||
StatusTabButton {
|
||||
width: implicitWidth
|
||||
text: qsTr("Token lists")
|
||||
text: qsTr("Advanced")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -105,8 +113,8 @@ ColumnLayout {
|
|||
return tokensPanel
|
||||
case d.collectiblesTabIndex:
|
||||
return collectiblesPanel
|
||||
case d.tokenSourcesTabIndex:
|
||||
return supportedTokensListPanel
|
||||
case d.advancedTabIndex:
|
||||
return advancedTab
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -116,7 +124,6 @@ ColumnLayout {
|
|||
ManageAssetsPanel {
|
||||
baseModel: root.baseWalletAssetsModel
|
||||
}
|
||||
// TODO #12611 add Advanced section
|
||||
}
|
||||
|
||||
Component {
|
||||
|
@ -128,10 +135,74 @@ ColumnLayout {
|
|||
}
|
||||
|
||||
Component {
|
||||
id: supportedTokensListPanel
|
||||
id: advancedTab
|
||||
ColumnLayout {
|
||||
spacing: 0
|
||||
StatusBaseText {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 18
|
||||
Layout.bottomMargin: 18
|
||||
text: qsTr("Token lists")
|
||||
color: Theme.palette.baseColor1
|
||||
}
|
||||
SupportedTokenListsPanel {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
sourcesOfTokensModel: root.sourcesOfTokensModel
|
||||
tokensListModel: root.tokensListModel
|
||||
}
|
||||
StatusBaseText {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 40 + 18
|
||||
Layout.bottomMargin: 26
|
||||
text: qsTr("Asset settings")
|
||||
color: Theme.palette.baseColor1
|
||||
}
|
||||
StatusDialogDivider {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
StatusListItem {
|
||||
Layout.fillWidth: true
|
||||
title: qsTr("Show community assets when sending tokens")
|
||||
|
||||
components: [
|
||||
StatusSwitch {
|
||||
id: showCommunityAssetsSwitch
|
||||
checked: true // FIXME integrate with backend (#13178)
|
||||
onCheckedChanged: {
|
||||
// FIXME integrate with backend (#13178)
|
||||
}
|
||||
}
|
||||
]
|
||||
onClicked: {
|
||||
showCommunityAssetsSwitch.checked = !showCommunityAssetsSwitch.checked
|
||||
}
|
||||
}
|
||||
StatusDialogDivider {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
StatusListItem {
|
||||
Layout.fillWidth: true
|
||||
title: qsTr("Don’t display assets with balance lower than")
|
||||
|
||||
components: [
|
||||
CurrencyAmountInput {
|
||||
enabled: displayThresholdSwitch.checked
|
||||
currencySymbol: SharedStores.RootStore.currencyStore.currentCurrency
|
||||
value: 0.10 // FIXME integrate with backend (#13178)
|
||||
},
|
||||
StatusSwitch {
|
||||
id: displayThresholdSwitch
|
||||
checked: false // FIXME integrate with backend (#13178)
|
||||
onCheckedChanged: {
|
||||
// FIXME integrate with backend (#13178)
|
||||
}
|
||||
}
|
||||
]
|
||||
onClicked: {
|
||||
displayThresholdSwitch.checked = !displayThresholdSwitch.checked
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -63,7 +63,7 @@ ComboBox {
|
|||
]
|
||||
|
||||
markerRoleName: "sourceGroup"
|
||||
onRowsRemoved: root.clearFilter() // different underlying model -> uncheck all groups
|
||||
onRowsRemoved: root.clearFilter() // different underlying model -> uncheck all
|
||||
}
|
||||
|
||||
readonly property var combinedProxyModel: SortFilterProxyModel {
|
||||
|
@ -81,12 +81,13 @@ ComboBox {
|
|||
}
|
||||
]
|
||||
filters: [
|
||||
ExpressionFilter {
|
||||
FastExpressionFilter {
|
||||
enabled: d.searchTextLowerCase !== ""
|
||||
expression: {
|
||||
d.searchTextLowerCase // ensure expression is reevaluated when searchString changes
|
||||
return model.groupName.toLowerCase().includes(d.searchTextLowerCase) || model.groupId.toLowerCase().includes(d.searchTextLowerCase)
|
||||
}
|
||||
expectedRoles: ["groupName", "groupId"]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -83,30 +83,34 @@ ColumnLayout {
|
|||
roleNames: ["collectionName", "communityName"]
|
||||
}
|
||||
filters: [
|
||||
ExpressionFilter {
|
||||
FastExpressionFilter {
|
||||
expression: {
|
||||
d.controller.settingsDirty
|
||||
return d.controller.filterAcceptsSymbol(model.symbol) && (customFilter.isCommunity ? !!model.communityId : !model.communityId)
|
||||
}
|
||||
expectedRoles: ["symbol", "communityId"]
|
||||
},
|
||||
ExpressionFilter {
|
||||
FastExpressionFilter {
|
||||
enabled: customFilter.isCommunity && cmbFilter.hasEnabledFilters
|
||||
expression: cmbFilter.selectedFilterGroupIds.includes(model.communityId) ||
|
||||
(!model.communityId && cmbFilter.selectedFilterGroupIds.includes(""))
|
||||
expectedRoles: ["communityId"]
|
||||
},
|
||||
ExpressionFilter {
|
||||
FastExpressionFilter {
|
||||
enabled: !customFilter.isCommunity && cmbFilter.hasEnabledFilters
|
||||
expression: cmbFilter.selectedFilterGroupIds.includes(model.collectionUid) ||
|
||||
(!model.collectionUid && cmbFilter.selectedFilterGroupIds.includes(""))
|
||||
expectedRoles: ["collectionUid"]
|
||||
}
|
||||
]
|
||||
sorters: [
|
||||
ExpressionSorter {
|
||||
FastExpressionSorter {
|
||||
expression: {
|
||||
d.controller.settingsDirty
|
||||
return d.controller.lessThan(modelLeft.symbol, modelRight.symbol)
|
||||
}
|
||||
enabled: d.isCustomView
|
||||
expectedRoles: ["symbol"]
|
||||
},
|
||||
RoleSorter {
|
||||
roleName: cmbTokenOrder.currentSortRoleName
|
||||
|
|
|
@ -0,0 +1,121 @@
|
|||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
|
||||
import StatusQ 0.1
|
||||
import StatusQ.Core 0.1
|
||||
import StatusQ.Controls 0.1
|
||||
import StatusQ.Components 0.1
|
||||
import StatusQ.Core.Theme 0.1
|
||||
|
||||
import utils 1.0
|
||||
|
||||
/*!
|
||||
\qmltype CurrencyAmountInput
|
||||
\inherits TextField
|
||||
\brief Provides a text input field that accepts a numeric value, with optional currency symbol ("USD").
|
||||
Utilizes a builtin DoubleValidator to validate the user's input.
|
||||
It accepts both the native decimal separator and optionally a period (`.`) for locales that don't use this.
|
||||
\inqmlmodule shared.controls 1.0
|
||||
|
||||
Internally it uses FormattedDoubleProperty object that keeps track of the value.
|
||||
*/
|
||||
TextField {
|
||||
id: root
|
||||
|
||||
property alias value: internalProp.value // accepts double/float or string representation, rejects NaN
|
||||
readonly property bool valid: acceptableInput
|
||||
|
||||
property int decimals: 2 // number of decimal places to display
|
||||
property string currencySymbol: "USD" // currency symbol, optional
|
||||
property double minValue: 0 // min value
|
||||
property double maxValue: Number.MAX_VALUE // max value
|
||||
|
||||
property alias locale: internalProp.locale // locale code name (affects the validator and decimal point handler)
|
||||
readonly property string asLocaleString: {
|
||||
internalProp.value
|
||||
return root.valid ? internalProp.asLocaleString(root.decimals) : "NaN"
|
||||
}
|
||||
readonly property string asString: internalProp.asString // "C" locale string
|
||||
|
||||
FormattedDoubleProperty {
|
||||
id: internalProp
|
||||
onValueChanged: {
|
||||
const oldPos = root.cursorPosition
|
||||
root.text = asLocaleString() // min number of decimals, strip zeroes
|
||||
root.cursorPosition = oldPos
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onPressed: (event) => {
|
||||
// additionally accept dot (.) and convert it to the correct decimal point char
|
||||
if (event.key === Qt.Key_Period) {
|
||||
root.insert(root.cursorPosition, Qt.locale(root.locale).decimalPoint)
|
||||
event.accepted = true
|
||||
} else if (event.modifiers === Qt.NoModifier && event.key >= Qt.Key_A && event.key <= Qt.Key_Z) {
|
||||
// reject typing non-numbers (can happen when the validator is in an intermediate state)
|
||||
event.accepted = true
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: text = internalProp.asLocaleString(decimals)
|
||||
onTextEdited: value = text
|
||||
|
||||
font.family: Style.current.baseFont.name
|
||||
font.pixelSize: Style.current.primaryTextFontSize
|
||||
|
||||
leftPadding: Style.current.padding
|
||||
rightPadding: currencySymbol !== "" ?
|
||||
currencySymbolText.width + currencySymbolText.anchors.leftMargin + currencySymbolText.anchors.rightMargin :
|
||||
Style.current.padding
|
||||
topPadding: 10
|
||||
bottomPadding: 10
|
||||
|
||||
opacity: enabled ? 1 : 0.3
|
||||
color: readOnly ? Theme.palette.baseColor1 : Theme.palette.directColor1
|
||||
selectionColor: Theme.palette.primaryColor2
|
||||
selectedTextColor: Theme.palette.directColor1
|
||||
placeholderTextColor: Theme.palette.baseColor1
|
||||
|
||||
hoverEnabled: !readOnly
|
||||
selectByMouse: true
|
||||
inputMethodHints: Qt.ImhFormattedNumbersOnly
|
||||
|
||||
validator: DoubleValidator {
|
||||
notation: DoubleValidator.StandardNotation
|
||||
decimals: root.decimals
|
||||
bottom: root.minValue
|
||||
top: root.maxValue
|
||||
locale: internalProp.locale
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
radius: Style.current.radius
|
||||
color: Theme.palette.statusAppNavBar.backgroundColor
|
||||
border.width: root.cursorVisible || root.hovered || !root.valid ? 1 : 0
|
||||
border.color: {
|
||||
if (!root.valid)
|
||||
return Theme.palette.dangerColor1
|
||||
if (root.cursorVisible)
|
||||
return Theme.palette.primaryColor1
|
||||
if (root.hovered)
|
||||
return Theme.palette.primaryColor2
|
||||
return "transparent"
|
||||
}
|
||||
Behavior on border.color { ColorAnimation {} }
|
||||
}
|
||||
|
||||
cursorDelegate: StatusCursorDelegate {
|
||||
cursorVisible: root.cursorVisible
|
||||
}
|
||||
|
||||
StatusBaseText {
|
||||
id: currencySymbolText
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.leftMargin: Style.current.padding
|
||||
anchors.rightMargin: Style.current.padding
|
||||
color: Theme.palette.baseColor1
|
||||
text: root.currencySymbol
|
||||
visible: !!text
|
||||
}
|
||||
}
|
|
@ -7,6 +7,7 @@ ContactSelector 1.0 ContactSelector.qml
|
|||
ContactsListAndSearch 1.0 ContactsListAndSearch.qml
|
||||
CopyButton 1.0 CopyButton.qml
|
||||
CopyToClipBoardButton 1.0 CopyToClipBoardButton.qml
|
||||
CurrencyAmountInput 1.0 CurrencyAmountInput.qml
|
||||
DisabledTooltipButton 1.0 DisabledTooltipButton.qml
|
||||
EmojiHash 1.0 EmojiHash.qml
|
||||
ErrorDetails 1.0 ErrorDetails.qml
|
||||
|
|
Loading…
Reference in New Issue