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);
|
qmlRegisterSingletonType<TextUtils>("TextUtils", 1, 0, "TextUtils", &TextUtils::qmlInstance);
|
||||||
|
|
||||||
QStandardPaths::setTestModeEnabled(true);
|
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/fastexpressionfilter.h
|
||||||
include/StatusQ/fastexpressionrole.h
|
include/StatusQ/fastexpressionrole.h
|
||||||
include/StatusQ/fastexpressionsorter.h
|
include/StatusQ/fastexpressionsorter.h
|
||||||
|
include/StatusQ/formatteddoubleproperty.h
|
||||||
include/StatusQ/leftjoinmodel.h
|
include/StatusQ/leftjoinmodel.h
|
||||||
include/StatusQ/modelutilsinternal.h
|
include/StatusQ/modelutilsinternal.h
|
||||||
include/StatusQ/permissionutilsinternal.h
|
include/StatusQ/permissionutilsinternal.h
|
||||||
|
@ -116,6 +117,7 @@ add_library(StatusQ SHARED
|
||||||
src/fastexpressionfilter.cpp
|
src/fastexpressionfilter.cpp
|
||||||
src/fastexpressionrole.cpp
|
src/fastexpressionrole.cpp
|
||||||
src/fastexpressionsorter.cpp
|
src/fastexpressionsorter.cpp
|
||||||
|
src/formatteddoubleproperty.cpp
|
||||||
src/leftjoinmodel.cpp
|
src/leftjoinmodel.cpp
|
||||||
src/modelutilsinternal.cpp
|
src/modelutilsinternal.cpp
|
||||||
src/permissionutilsinternal.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/submodelproxymodel.h"
|
||||||
#include "StatusQ/sumaggregator.h"
|
#include "StatusQ/sumaggregator.h"
|
||||||
#include "StatusQ/writableproxymodel.h"
|
#include "StatusQ/writableproxymodel.h"
|
||||||
|
#include "StatusQ/formatteddoubleproperty.h"
|
||||||
|
|
||||||
#include "wallet/managetokenscontroller.h"
|
#include "wallet/managetokenscontroller.h"
|
||||||
#include "wallet/managetokensmodel.h"
|
#include "wallet/managetokensmodel.h"
|
||||||
|
@ -52,6 +53,7 @@ public:
|
||||||
qmlRegisterType<RolesRenamingModel>("StatusQ", 0, 1, "RolesRenamingModel");
|
qmlRegisterType<RolesRenamingModel>("StatusQ", 0, 1, "RolesRenamingModel");
|
||||||
qmlRegisterType<SumAggregator>("StatusQ", 0, 1, "SumAggregator");
|
qmlRegisterType<SumAggregator>("StatusQ", 0, 1, "SumAggregator");
|
||||||
qmlRegisterType<WritableProxyModel>("StatusQ", 0, 1, "WritableProxyModel");
|
qmlRegisterType<WritableProxyModel>("StatusQ", 0, 1, "WritableProxyModel");
|
||||||
|
qmlRegisterType<FormattedDoubleProperty>("StatusQ", 0, 1, "FormattedDoubleProperty");
|
||||||
|
|
||||||
qmlRegisterSingletonType<QClipboardProxy>("StatusQ", 0, 1, "QClipboardProxy", &QClipboardProxy::qmlInstance);
|
qmlRegisterSingletonType<QClipboardProxy>("StatusQ", 0, 1, "QClipboardProxy", &QClipboardProxy::qmlInstance);
|
||||||
|
|
||||||
|
|
|
@ -32,12 +32,13 @@ StatusListView {
|
||||||
subTitle: qsTr("%n token(s) · Last updated %1 @%2",
|
subTitle: qsTr("%n token(s) · Last updated %1 @%2",
|
||||||
"",
|
"",
|
||||||
model.tokensCount).arg(LocaleUtils.formatDate(model.updatedAt * 1000)).arg(LocaleUtils.formatTime(model.updatedAt, Locale.ShortFormat))
|
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.name: model.image
|
||||||
asset.isImage: true
|
asset.isImage: true
|
||||||
border.width: 1
|
border.width: 1
|
||||||
border.color: Theme.palette.baseColor5
|
border.color: Theme.palette.baseColor5
|
||||||
components: [
|
components: [
|
||||||
StatusButton {
|
StatusFlatButton {
|
||||||
text: qsTr("View")
|
text: qsTr("View")
|
||||||
|
|
||||||
onClicked: keyFilter.value = model.key
|
onClicked: keyFilter.value = model.key
|
||||||
|
@ -56,7 +57,7 @@ StatusListView {
|
||||||
width: parent.width - 4 // The rectangular path is rendered outside
|
width: parent.width - 4 // The rectangular path is rendered outside
|
||||||
|
|
||||||
icon: "add"
|
icon: "add"
|
||||||
text: qsTr("Add Token List (coming soon)")
|
text: qsTr("Add a Token List (coming soon)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -275,7 +275,7 @@ SettingsContentBase {
|
||||||
id: manageTokensView
|
id: manageTokensView
|
||||||
sourcesOfTokensModel: tokensStore.sourcesOfTokensModel
|
sourcesOfTokensModel: tokensStore.sourcesOfTokensModel
|
||||||
tokensListModel: tokensStore.extendedFlatTokensModel
|
tokensListModel: tokensStore.extendedFlatTokensModel
|
||||||
baseWalletAssetsModel: RootStore.assets // TODO include community assets (#12369)
|
baseWalletAssetsModel: RootStore.assets
|
||||||
baseWalletCollectiblesModel: {
|
baseWalletCollectiblesModel: {
|
||||||
RootStore.setFillterAllAddresses() // FIXME no other way to get _all_ collectibles?
|
RootStore.setFillterAllAddresses() // FIXME no other way to get _all_ collectibles?
|
||||||
// TODO concat proxy model to include community collectibles (#12519)
|
// TODO concat proxy model to include community collectibles (#12519)
|
||||||
|
|
|
@ -1,9 +1,15 @@
|
||||||
import QtQuick 2.15
|
import QtQuick 2.15
|
||||||
|
import QtQuick.Controls 2.15
|
||||||
import QtQuick.Layouts 1.15
|
import QtQuick.Layouts 1.15
|
||||||
|
|
||||||
|
import StatusQ.Core 0.1
|
||||||
import StatusQ.Controls 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.controls 1.0
|
||||||
|
import shared.stores 1.0 as SharedStores
|
||||||
import utils 1.0
|
import utils 1.0
|
||||||
|
|
||||||
import AppLayouts.Profile.panels 1.0
|
import AppLayouts.Profile.panels 1.0
|
||||||
|
@ -25,6 +31,7 @@ ColumnLayout {
|
||||||
return false
|
return false
|
||||||
if (tabBar.currentIndex > d.collectiblesTabIndex)
|
if (tabBar.currentIndex > d.collectiblesTabIndex)
|
||||||
return false
|
return false
|
||||||
|
// FIXME take advanced settings into account here too (#13178)
|
||||||
if (tabBar.currentIndex === d.collectiblesTabIndex && baseWalletCollectiblesModel.isFetching)
|
if (tabBar.currentIndex === d.collectiblesTabIndex && baseWalletCollectiblesModel.isFetching)
|
||||||
return false
|
return false
|
||||||
return loader.item && loader.item.dirty
|
return loader.item && loader.item.dirty
|
||||||
|
@ -33,6 +40,7 @@ ColumnLayout {
|
||||||
function saveChanges() {
|
function saveChanges() {
|
||||||
if (tabBar.currentIndex > d.collectiblesTabIndex)
|
if (tabBar.currentIndex > d.collectiblesTabIndex)
|
||||||
return
|
return
|
||||||
|
// FIXME save advanced settings (#13178)
|
||||||
loader.item.saveSettings()
|
loader.item.saveSettings()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,7 +55,7 @@ ColumnLayout {
|
||||||
|
|
||||||
readonly property int assetsTabIndex: 0
|
readonly property int assetsTabIndex: 0
|
||||||
readonly property int collectiblesTabIndex: 1
|
readonly property int collectiblesTabIndex: 1
|
||||||
readonly property int tokenSourcesTabIndex: 2
|
readonly property int advancedTabIndex: 2
|
||||||
|
|
||||||
function checkLoadMoreCollectibles() {
|
function checkLoadMoreCollectibles() {
|
||||||
if (tabBar.currentIndex !== collectiblesTabIndex)
|
if (tabBar.currentIndex !== collectiblesTabIndex)
|
||||||
|
@ -88,7 +96,7 @@ ColumnLayout {
|
||||||
|
|
||||||
StatusTabButton {
|
StatusTabButton {
|
||||||
width: implicitWidth
|
width: implicitWidth
|
||||||
text: qsTr("Token lists")
|
text: qsTr("Advanced")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,8 +113,8 @@ ColumnLayout {
|
||||||
return tokensPanel
|
return tokensPanel
|
||||||
case d.collectiblesTabIndex:
|
case d.collectiblesTabIndex:
|
||||||
return collectiblesPanel
|
return collectiblesPanel
|
||||||
case d.tokenSourcesTabIndex:
|
case d.advancedTabIndex:
|
||||||
return supportedTokensListPanel
|
return advancedTab
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -116,7 +124,6 @@ ColumnLayout {
|
||||||
ManageAssetsPanel {
|
ManageAssetsPanel {
|
||||||
baseModel: root.baseWalletAssetsModel
|
baseModel: root.baseWalletAssetsModel
|
||||||
}
|
}
|
||||||
// TODO #12611 add Advanced section
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
|
@ -128,10 +135,74 @@ ColumnLayout {
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: supportedTokensListPanel
|
id: advancedTab
|
||||||
SupportedTokenListsPanel {
|
ColumnLayout {
|
||||||
sourcesOfTokensModel: root.sourcesOfTokensModel
|
spacing: 0
|
||||||
tokensListModel: root.tokensListModel
|
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"
|
markerRoleName: "sourceGroup"
|
||||||
onRowsRemoved: root.clearFilter() // different underlying model -> uncheck all groups
|
onRowsRemoved: root.clearFilter() // different underlying model -> uncheck all
|
||||||
}
|
}
|
||||||
|
|
||||||
readonly property var combinedProxyModel: SortFilterProxyModel {
|
readonly property var combinedProxyModel: SortFilterProxyModel {
|
||||||
|
@ -81,12 +81,13 @@ ComboBox {
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
filters: [
|
filters: [
|
||||||
ExpressionFilter {
|
FastExpressionFilter {
|
||||||
enabled: d.searchTextLowerCase !== ""
|
enabled: d.searchTextLowerCase !== ""
|
||||||
expression: {
|
expression: {
|
||||||
d.searchTextLowerCase // ensure expression is reevaluated when searchString changes
|
d.searchTextLowerCase // ensure expression is reevaluated when searchString changes
|
||||||
return model.groupName.toLowerCase().includes(d.searchTextLowerCase) || model.groupId.toLowerCase().includes(d.searchTextLowerCase)
|
return model.groupName.toLowerCase().includes(d.searchTextLowerCase) || model.groupId.toLowerCase().includes(d.searchTextLowerCase)
|
||||||
}
|
}
|
||||||
|
expectedRoles: ["groupName", "groupId"]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,30 +83,34 @@ ColumnLayout {
|
||||||
roleNames: ["collectionName", "communityName"]
|
roleNames: ["collectionName", "communityName"]
|
||||||
}
|
}
|
||||||
filters: [
|
filters: [
|
||||||
ExpressionFilter {
|
FastExpressionFilter {
|
||||||
expression: {
|
expression: {
|
||||||
d.controller.settingsDirty
|
d.controller.settingsDirty
|
||||||
return d.controller.filterAcceptsSymbol(model.symbol) && (customFilter.isCommunity ? !!model.communityId : !model.communityId)
|
return d.controller.filterAcceptsSymbol(model.symbol) && (customFilter.isCommunity ? !!model.communityId : !model.communityId)
|
||||||
}
|
}
|
||||||
|
expectedRoles: ["symbol", "communityId"]
|
||||||
},
|
},
|
||||||
ExpressionFilter {
|
FastExpressionFilter {
|
||||||
enabled: customFilter.isCommunity && cmbFilter.hasEnabledFilters
|
enabled: customFilter.isCommunity && cmbFilter.hasEnabledFilters
|
||||||
expression: cmbFilter.selectedFilterGroupIds.includes(model.communityId) ||
|
expression: cmbFilter.selectedFilterGroupIds.includes(model.communityId) ||
|
||||||
(!model.communityId && cmbFilter.selectedFilterGroupIds.includes(""))
|
(!model.communityId && cmbFilter.selectedFilterGroupIds.includes(""))
|
||||||
|
expectedRoles: ["communityId"]
|
||||||
},
|
},
|
||||||
ExpressionFilter {
|
FastExpressionFilter {
|
||||||
enabled: !customFilter.isCommunity && cmbFilter.hasEnabledFilters
|
enabled: !customFilter.isCommunity && cmbFilter.hasEnabledFilters
|
||||||
expression: cmbFilter.selectedFilterGroupIds.includes(model.collectionUid) ||
|
expression: cmbFilter.selectedFilterGroupIds.includes(model.collectionUid) ||
|
||||||
(!model.collectionUid && cmbFilter.selectedFilterGroupIds.includes(""))
|
(!model.collectionUid && cmbFilter.selectedFilterGroupIds.includes(""))
|
||||||
|
expectedRoles: ["collectionUid"]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
sorters: [
|
sorters: [
|
||||||
ExpressionSorter {
|
FastExpressionSorter {
|
||||||
expression: {
|
expression: {
|
||||||
d.controller.settingsDirty
|
d.controller.settingsDirty
|
||||||
return d.controller.lessThan(modelLeft.symbol, modelRight.symbol)
|
return d.controller.lessThan(modelLeft.symbol, modelRight.symbol)
|
||||||
}
|
}
|
||||||
enabled: d.isCustomView
|
enabled: d.isCustomView
|
||||||
|
expectedRoles: ["symbol"]
|
||||||
},
|
},
|
||||||
RoleSorter {
|
RoleSorter {
|
||||||
roleName: cmbTokenOrder.currentSortRoleName
|
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
|
ContactsListAndSearch 1.0 ContactsListAndSearch.qml
|
||||||
CopyButton 1.0 CopyButton.qml
|
CopyButton 1.0 CopyButton.qml
|
||||||
CopyToClipBoardButton 1.0 CopyToClipBoardButton.qml
|
CopyToClipBoardButton 1.0 CopyToClipBoardButton.qml
|
||||||
|
CurrencyAmountInput 1.0 CurrencyAmountInput.qml
|
||||||
DisabledTooltipButton 1.0 DisabledTooltipButton.qml
|
DisabledTooltipButton 1.0 DisabledTooltipButton.qml
|
||||||
EmojiHash 1.0 EmojiHash.qml
|
EmojiHash 1.0 EmojiHash.qml
|
||||||
ErrorDetails 1.0 ErrorDetails.qml
|
ErrorDetails 1.0 ErrorDetails.qml
|
||||||
|
|
Loading…
Reference in New Issue