Compare commits
24
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fe67997696 | ||
|
|
899f4461bb | ||
|
|
4b9d11d1d4 | ||
|
|
6e8a36be86 | ||
|
|
c65f80d22e | ||
|
|
f9457ef311 | ||
|
|
1374c1933f | ||
|
|
1f244f6282 | ||
|
|
80e5b338fd | ||
|
|
019471c804 | ||
|
|
88fb57dd3b | ||
|
|
a93ef16143 | ||
|
|
77d0e9b8fd | ||
|
|
d73a158418 | ||
|
|
1a23cc1912 | ||
|
|
48309d3040 | ||
|
|
718171fd7b | ||
|
|
efe3116610 | ||
|
|
aab44c1e0d | ||
|
|
eabc62f796 | ||
|
|
2971c60737 | ||
|
|
29e9557d5f | ||
|
|
89f54a85c8 | ||
|
|
73c77c29c2 |
@@ -1,3 +1,34 @@
|
||||
<a name=""></a>
|
||||
## v0.9.0 (2021-09-13)
|
||||
|
||||
|
||||
#### Breaking Changes
|
||||
|
||||
* **StatusExpandableItem:** Refactored the StatusExpandableSettingsItem to support different types ([718171fd](718171fd))
|
||||
|
||||
#### Bug Fixes
|
||||
|
||||
* **StatusAppThreePanelLayout:** Fix margin between left and center panels ([29e9557d](29e9557d))
|
||||
* **StatusChatListAndCategories:** rely on correct tooltip settings prop ([2971c607](2971c607))
|
||||
* **StatusCheckbox:** give checkbox label proper theme color ([88fb57dd](88fb57dd))
|
||||
* **StatusModal:** don't reserve header subtitle space ([eabc62f7](eabc62f7), closes [#378](378))
|
||||
|
||||
#### Features
|
||||
|
||||
* **Status.Core.Theme:** add RobotoMono font ([aab44c1e](aab44c1e), closes [#342](342))
|
||||
* **StatusBaseInput:** introduce `component` property ([019471c8](019471c8), closes [#380](380))
|
||||
* **StatusExpandableItem:** Refactored the StatusExpandableSettingsItem to support different types ([718171fd](718171fd))
|
||||
* **StatusInput:**
|
||||
* introduce `ValidationMode` ([d73a1584](d73a1584))
|
||||
* exposed edit component ([73c77c29](73c77c29))
|
||||
* **StatusQ.Controls.Validators:**
|
||||
* introduce `StatusAddressAndEnsValidator` ([a93ef161](a93ef161))
|
||||
* introduce `StatusAddressValidator` ([77d0e9b8](77d0e9b8))
|
||||
* **StatusSearchPopup:** introduce forceActiveFocus API ([efe31166](efe31166))
|
||||
* **StatusValidator:** allow validators to provide default `errorMessage` ([1a23cc19](1a23cc19))
|
||||
|
||||
|
||||
|
||||
<a name=""></a>
|
||||
## v0.8.0 (2021-09-06)
|
||||
|
||||
|
||||
@@ -13,6 +13,11 @@ Column {
|
||||
onClicked: simpleModal.open()
|
||||
}
|
||||
|
||||
StatusButton {
|
||||
text: "Simple title modal"
|
||||
onClicked: simpleTitleModal.open()
|
||||
}
|
||||
|
||||
StatusButton {
|
||||
text: "Modal with header image"
|
||||
onClicked: headerImageModal.open()
|
||||
@@ -65,6 +70,12 @@ Column {
|
||||
header.subTitle: "Subtitle"
|
||||
}
|
||||
|
||||
StatusModal {
|
||||
id: simpleTitleModal
|
||||
anchors.centerIn: parent
|
||||
header.title: "Some Title"
|
||||
}
|
||||
|
||||
StatusModal {
|
||||
id: headerImageModal
|
||||
anchors.centerIn: parent
|
||||
|
||||
@@ -11,35 +11,46 @@ Column {
|
||||
leftPadding: 20
|
||||
rightPadding: 20
|
||||
|
||||
StatusExpandableSettingsItem {
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: 30
|
||||
color: Theme.palette.baseColor2
|
||||
StatusBaseText {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: "Type Primary"
|
||||
color: Theme.palette.directColor1
|
||||
}
|
||||
}
|
||||
|
||||
StatusExpandableItem {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
expandable: false
|
||||
icon.name: "seed-phrase"
|
||||
primaryText: qsTr("Back up seed phrase")
|
||||
secondaryText: qsTr("Back up your seed phrase now to secure this account ajhaDH SDHSAHDLSADBSA,DLISAHDLASD ADASDHASLDHALSDHAS DAS,DASJDGLIASGD")
|
||||
primaryText: "Back up seed phrase"
|
||||
secondaryText: "Back up your seed phrase now to secure this account ajhaDH SDHSAHDLSADBSA,DLISAHDLASD ADASDHASLDHALSDHAS DAS,DASJDGLIASGD"
|
||||
button.text: qsTr("Back up seed phrase")
|
||||
}
|
||||
|
||||
StatusExpandableSettingsItem {
|
||||
StatusExpandableItem {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
expandable: true
|
||||
icon.name: "secret"
|
||||
primaryText: qsTr("Account signing phrase")
|
||||
secondaryText: qsTr("View your signing phrase and ensure that you never get scammed")
|
||||
primaryText: "Account signing phrase"
|
||||
secondaryText: "View your signing phrase and ensure that you never get scammed. View your signing phrase and ensure that you never get scammed."
|
||||
expandableComponent: notImplemented
|
||||
}
|
||||
|
||||
StatusExpandableSettingsItem {
|
||||
StatusExpandableItem {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
expandable: true
|
||||
icon.name: "seed-phrase"
|
||||
primaryText: qsTr("View private key")
|
||||
secondaryText: qsTr("Back up your seed phrase now to secure this account")
|
||||
primaryText: "View private key"
|
||||
secondaryText: "Back up your seed phrase now to secure this account"
|
||||
expandableComponent: notImplemented
|
||||
button.text: qsTr("View private key")
|
||||
button.text: "View private key"
|
||||
button.icon.name: "tiny/public-chat"
|
||||
button.onClicked: {
|
||||
// To-do open enter password Modal
|
||||
@@ -47,6 +58,78 @@ Column {
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: 30
|
||||
color: Theme.palette.baseColor2
|
||||
StatusBaseText {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: "Type Secondary"
|
||||
color: Theme.palette.directColor1
|
||||
}
|
||||
}
|
||||
|
||||
StatusExpandableItem {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
type: StatusExpandableItem.Type.Secondary
|
||||
expandable: true
|
||||
image.source: "https://pbs.twimg.com/profile_images/1369221718338895873/T_5fny6o_400x400.jpg"
|
||||
primaryText: "CryptoKitties"
|
||||
additionalText: "1456 USD"
|
||||
expandableComponent: notImplemented
|
||||
}
|
||||
|
||||
StatusExpandableItem {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
type: StatusExpandableItem.Type.Secondary
|
||||
expandable: true
|
||||
image.source: "https://pbs.twimg.com/profile_images/1369221718338895873/T_5fny6o_400x400.jpg"
|
||||
primaryText: "Adding Really long text to test scenario of having very long text along with tertiary text"
|
||||
additionalText: "564.90 USD"
|
||||
expandableComponent: notImplemented
|
||||
}
|
||||
|
||||
StatusExpandableItem {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
type: StatusExpandableItem.Type.Secondary
|
||||
expandable: true
|
||||
primaryText: "CryptoKitties"
|
||||
additionalText: "1456 USD"
|
||||
expandableComponent: notImplemented
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: 30
|
||||
color: Theme.palette.baseColor2
|
||||
StatusBaseText {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: "Type Tertiary"
|
||||
color: Theme.palette.directColor1
|
||||
}
|
||||
}
|
||||
|
||||
StatusExpandableItem {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
type: StatusExpandableItem.Type.Tertiary
|
||||
expandable: true
|
||||
primaryText: "CryptoKitties"
|
||||
expandableComponent: notImplemented
|
||||
}
|
||||
|
||||
StatusExpandableItem {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
type: StatusExpandableItem.Type.Tertiary
|
||||
expandable: true
|
||||
primaryText: "Rescue Moon"
|
||||
expandableComponent: notImplemented
|
||||
}
|
||||
|
||||
Component {
|
||||
id: notImplemented
|
||||
Rectangle {
|
||||
|
||||
@@ -84,21 +84,35 @@ Column {
|
||||
}
|
||||
|
||||
StatusInput {
|
||||
id: input
|
||||
label: "Label"
|
||||
charLimit: 30
|
||||
input.placeholderText: "Input with validator"
|
||||
|
||||
validators: [
|
||||
StatusMinLengthValidator { minLength: 10 }
|
||||
]
|
||||
|
||||
onTextChanged: {
|
||||
if (errors && errors.minLength) {
|
||||
errorMessage = `Value can't be shorter than ${errors.minLength.min} but got ${errors.minLength.actual}`
|
||||
StatusMinLengthValidator {
|
||||
minLength: 10
|
||||
errorMessage: {
|
||||
if (input.errors && input.errors.minLength) {
|
||||
return `Value can't be shorter than ${input.errors.minLength.min} but got ${input.errors.minLength.actual}`
|
||||
}
|
||||
return ""
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
StatusInput {
|
||||
label: "Label"
|
||||
input.placeholderText: "Input width component (right side)"
|
||||
input.component: StatusIcon {
|
||||
icon: "cancel"
|
||||
height: 16
|
||||
color: Theme.palette.dangerColor1
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
StatusInput {
|
||||
input.multiline: true
|
||||
input.placeholderText: "Multiline"
|
||||
|
||||
+1
-1
@@ -170,7 +170,7 @@ StatusWindow {
|
||||
onClicked: page.sourceComponent = othersComponent
|
||||
}
|
||||
StatusNavigationListItem {
|
||||
title: "StatusExpandableSettingsItem"
|
||||
title: "StatusExpandableItem"
|
||||
selected: page.sourceComponent == settingsComponent
|
||||
onClicked: page.sourceComponent = settingsComponent
|
||||
}
|
||||
|
||||
+12
-2
@@ -12,7 +12,8 @@ DEFINES += QT_DEPRECATED_WARNINGS
|
||||
SOURCES += \
|
||||
handler.cpp \
|
||||
main.cpp \
|
||||
sandboxapp.cpp
|
||||
sandboxapp.cpp \
|
||||
spellchecker.cpp
|
||||
|
||||
!macx {
|
||||
SOURCES += statuswindow.cpp
|
||||
@@ -24,6 +25,14 @@ macx {
|
||||
CONFIG -= app_bundle
|
||||
OBJECTIVE_SOURCES += \
|
||||
statuswindow_mac.mm
|
||||
|
||||
hunspellTarget.depends = FORCE
|
||||
hunspellTarget.commands = brew install hunspell
|
||||
QMAKE_EXTRA_TARGETS += hunspellTarget
|
||||
|
||||
|
||||
LIBS += -L"/usr/local/lib" -lhunspell-1.7
|
||||
INCLUDEPATH += /usr/local/include/hunspell
|
||||
}
|
||||
|
||||
ios {
|
||||
@@ -51,7 +60,8 @@ else: unix:!android: target.path = /opt/$${TARGET}/bin
|
||||
HEADERS += \
|
||||
handler.h \
|
||||
sandboxapp.h \
|
||||
statuswindow.h
|
||||
statuswindow.h \
|
||||
spellchecker.h
|
||||
|
||||
OTHER_FILES += $$files($$PWD/../*.qml, true)
|
||||
OTHER_FILES += $$files($$PWD/*.qml, true)
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <QDebug>
|
||||
|
||||
#include "statuswindow.h"
|
||||
#include "spellchecker.h"
|
||||
|
||||
SandboxApp::SandboxApp(int &argc, char **argv)
|
||||
: QGuiApplication(argc, argv),
|
||||
@@ -16,6 +17,7 @@ SandboxApp::SandboxApp(int &argc, char **argv)
|
||||
void SandboxApp::startEngine()
|
||||
{
|
||||
qmlRegisterType<StatusWindow>("Sandbox", 0, 1, "StatusWindow");
|
||||
qmlRegisterType<SpellChecker>("Sandbox", 0, 1, "Spellchecker");
|
||||
|
||||
#ifdef QT_DEBUG
|
||||
const QUrl url(applicationDirPath() + "/../main.qml");
|
||||
|
||||
@@ -0,0 +1,174 @@
|
||||
#include "spellchecker.h"
|
||||
|
||||
#include "hunspell.hxx"
|
||||
#include <QTextCodec>
|
||||
#include <QFile>
|
||||
#include <QDebug>
|
||||
#include <QLocale>
|
||||
|
||||
#include <QRegularExpression>
|
||||
#include <QApplication>
|
||||
#include <QDir>
|
||||
|
||||
SpellChecker::SpellChecker(QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_hunspell(nullptr)
|
||||
, m_userDict("userDict_")
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
SpellChecker::~SpellChecker()
|
||||
{
|
||||
#ifdef Q_OS_MACOS
|
||||
delete m_hunspell;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool SpellChecker::spell(const QString &word)
|
||||
{
|
||||
#ifdef Q_OS_MACOS
|
||||
return m_hunspell->spell(m_codec->fromUnicode(word).toStdString());
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool SpellChecker::isInit() const
|
||||
{
|
||||
return !m_hunspell;
|
||||
}
|
||||
|
||||
void SpellChecker::initHunspell()
|
||||
{
|
||||
#ifdef Q_OS_MACOS
|
||||
if (m_hunspell) {
|
||||
delete m_hunspell;
|
||||
}
|
||||
|
||||
QString dictFile = QApplication::applicationDirPath() + "/dictionaries/" + m_lang + "/index.dic";
|
||||
QString affixFile = QApplication::applicationDirPath() + "/dictionaries/" + m_lang + "/index.aff";
|
||||
QByteArray dictFilePathBA = dictFile.toLocal8Bit();
|
||||
QByteArray affixFilePathBA = affixFile.toLocal8Bit();
|
||||
m_hunspell = new Hunspell(affixFilePathBA.constData(),
|
||||
dictFilePathBA.constData());
|
||||
|
||||
// detect encoding analyzing the SET option in the affix file
|
||||
auto encoding = QStringLiteral("ISO8859-15");
|
||||
QFile _affixFile(affixFile);
|
||||
if (_affixFile.open(QIODevice::ReadOnly)) {
|
||||
QTextStream stream(&_affixFile);
|
||||
QRegularExpression enc_detector(
|
||||
QStringLiteral("^\\s*SET\\s+([A-Z0-9\\-]+)\\s*"),
|
||||
QRegularExpression::CaseInsensitiveOption);
|
||||
QString sLine;
|
||||
QRegularExpressionMatch match;
|
||||
while (!stream.atEnd()) {
|
||||
sLine = stream.readLine();
|
||||
if (sLine.isEmpty()) { continue; }
|
||||
match = enc_detector.match(sLine);
|
||||
if (match.hasMatch()) {
|
||||
encoding = match.captured(1);
|
||||
qDebug() << "Encoding set to " + encoding;
|
||||
break;
|
||||
}
|
||||
}
|
||||
_affixFile.close();
|
||||
}
|
||||
m_codec = QTextCodec::codecForName(encoding.toLatin1().constData());
|
||||
|
||||
QString userDict = m_userDict + m_lang + ".txt";
|
||||
|
||||
if (!userDict.isEmpty()) {
|
||||
QFile userDictonaryFile(userDict);
|
||||
if (userDictonaryFile.open(QIODevice::ReadOnly)) {
|
||||
QTextStream stream(&userDictonaryFile);
|
||||
for (QString word = stream.readLine();
|
||||
!word.isEmpty();
|
||||
word = stream.readLine())
|
||||
ignoreWord(word);
|
||||
userDictonaryFile.close();
|
||||
} else {
|
||||
qWarning() << "User dictionary in " << userDict
|
||||
<< "could not be opened";
|
||||
}
|
||||
} else {
|
||||
qDebug() << "User dictionary not set.";
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
QVariantList SpellChecker::suggest(const QString &word)
|
||||
{
|
||||
int numSuggestions = 0;
|
||||
QVariantList suggestions;
|
||||
#ifdef Q_OS_MACOS
|
||||
std::vector<std::string> wordlist;
|
||||
wordlist = m_hunspell->suggest(m_codec->fromUnicode(word).toStdString());
|
||||
|
||||
numSuggestions = static_cast<int>(wordlist.size());
|
||||
if (numSuggestions > 0) {
|
||||
suggestions.reserve(numSuggestions);
|
||||
for (int i = 0; i < numSuggestions; i++) {
|
||||
suggestions << m_codec->toUnicode(
|
||||
QByteArray::fromStdString(wordlist[i]));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return suggestions;
|
||||
}
|
||||
|
||||
void SpellChecker::ignoreWord(const QString &word)
|
||||
{
|
||||
#ifdef Q_OS_MACOS
|
||||
m_hunspell->add(m_codec->fromUnicode(word).constData());
|
||||
#endif
|
||||
}
|
||||
|
||||
void SpellChecker::addToUserWordlist(const QString &word)
|
||||
{
|
||||
#ifdef Q_OS_MACOS
|
||||
QString userDict = m_userDict + m_lang + ".txt";
|
||||
if (!userDict.isEmpty()) {
|
||||
QFile userDictonaryFile(userDict);
|
||||
if (userDictonaryFile.open(QIODevice::Append)) {
|
||||
QTextStream stream(&userDictonaryFile);
|
||||
stream << word << "\n";
|
||||
userDictonaryFile.close();
|
||||
} else {
|
||||
qWarning() << "User dictionary in " << userDict
|
||||
<< "could not be opened for appending a new word";
|
||||
}
|
||||
} else {
|
||||
qDebug() << "User dictionary not set.";
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
const QString& SpellChecker::lang() const
|
||||
{
|
||||
return m_lang;
|
||||
}
|
||||
|
||||
void SpellChecker::setLang(const QString& lang)
|
||||
{
|
||||
if (m_lang != lang) {
|
||||
m_lang = lang;
|
||||
initHunspell();
|
||||
emit langChanged();
|
||||
}
|
||||
}
|
||||
|
||||
const QString& SpellChecker::userDict() const
|
||||
{
|
||||
return m_userDict;
|
||||
}
|
||||
|
||||
void SpellChecker::setUserDict(const QString& userDict)
|
||||
{
|
||||
if (m_userDict != userDict) {
|
||||
m_userDict = userDict;
|
||||
emit userDictChanged();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
#ifndef SPELLCHECKER_H
|
||||
#define SPELLCHECKER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QVariant>
|
||||
#include <QQuickTextDocument>
|
||||
#include <QSyntaxHighlighter>
|
||||
|
||||
#ifdef Q_OS_MACOS
|
||||
class Hunspell;
|
||||
#endif
|
||||
class QTextCodec;
|
||||
|
||||
class SpellChecker : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QString lang READ lang WRITE setLang NOTIFY langChanged)
|
||||
Q_PROPERTY(QString userDict READ userDict WRITE setUserDict NOTIFY userDictChanged)
|
||||
|
||||
public:
|
||||
explicit SpellChecker(QObject *parent = nullptr);
|
||||
~SpellChecker();
|
||||
|
||||
Q_INVOKABLE bool spell(const QString& word);
|
||||
Q_INVOKABLE QVariantList suggest(const QString &word);
|
||||
Q_INVOKABLE void ignoreWord(const QString &word);
|
||||
Q_INVOKABLE void addToUserWordlist(const QString &word);
|
||||
Q_INVOKABLE bool isInit() const;
|
||||
|
||||
const QString& lang() const;
|
||||
void setLang(const QString& lang);
|
||||
|
||||
const QString& userDict() const;
|
||||
void setUserDict(const QString& userDict);
|
||||
|
||||
signals:
|
||||
void langChanged();
|
||||
void userDictChanged();
|
||||
|
||||
private:
|
||||
void initHunspell();
|
||||
|
||||
private:
|
||||
QString m_lang;
|
||||
QString m_userDict;
|
||||
|
||||
QQuickTextDocument *m_document;
|
||||
#ifdef Q_OS_MACOS
|
||||
Hunspell *m_hunspell;
|
||||
#endif
|
||||
QTextCodec *m_codec;
|
||||
};
|
||||
|
||||
#endif // SPELLCHECKER_H
|
||||
@@ -28,6 +28,8 @@ Item {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 5
|
||||
anchors.right: (implicitWidth > parent.width - 50) ? statusMenuButton.left : undefined
|
||||
anchors.rightMargin: 5
|
||||
type: StatusChatInfoButton.Type.OneToOneChat
|
||||
onClicked: statusChatInfoToolBar.chatInfoButtonClicked()
|
||||
}
|
||||
@@ -39,7 +41,7 @@ Item {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
visible: popupMenuSlot.active
|
||||
width: 32
|
||||
width: visible ? 32 : 0
|
||||
height: 32
|
||||
|
||||
type: StatusRoundButton.Type.Secondary
|
||||
|
||||
@@ -129,8 +129,8 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
addButton.tooltip: statusChatListAndCategories.addButtonToolTip
|
||||
menuButton.tooltip: statusChatListAndCategories.menuButtonToolTip
|
||||
addButton.tooltip: statusChatListAndCategories.categoryAddButtonToolTip
|
||||
menuButton.tooltip: statusChatListAndCategories.categoryMenuButtonToolTip
|
||||
|
||||
originalOrder: model.position
|
||||
categoryId: model.categoryId
|
||||
|
||||
@@ -93,19 +93,8 @@ Rectangle {
|
||||
|
||||
property bool showMoreMenu: false
|
||||
onClicked: {
|
||||
if (!showMoreMenu) {
|
||||
popupMenuSlot.item.popup(-popupMenuSlot.item.width + menuButton.width, menuButton.height + 4)
|
||||
statusChatToolBar.menuButtonClicked()
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: menuClosedUpdater
|
||||
interval: 100
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
menuButton.showMoreMenu = false
|
||||
}
|
||||
popupMenuSlot.item.popup(-popupMenuSlot.item.width + menuButton.width, menuButton.height + 4)
|
||||
statusChatToolBar.menuButtonClicked()
|
||||
}
|
||||
|
||||
Loader {
|
||||
@@ -114,15 +103,13 @@ Rectangle {
|
||||
onLoaded: {
|
||||
popupMenuSlot.item.closeHandler = function () {
|
||||
menuButton.highlighted = false
|
||||
menuClosedUpdater.start()
|
||||
}
|
||||
|
||||
popupMenuSlot.item.openHandler = function () {
|
||||
menuButton.highlighted = true
|
||||
menuButton.showMoreMenu = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
@@ -131,7 +118,7 @@ Rectangle {
|
||||
color: Theme.palette.directColor7
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
visible: notificationButton.visible &&
|
||||
(menuButton.visible || membersButton.visible || searchButton.visible)
|
||||
(menuButton.visible || membersButton.visible || searchButton.visible)
|
||||
}
|
||||
|
||||
StatusFlatRoundButton {
|
||||
|
||||
@@ -0,0 +1,229 @@
|
||||
import QtQuick 2.14
|
||||
import StatusQ.Core 0.1
|
||||
import StatusQ.Controls 0.1
|
||||
import StatusQ.Core.Theme 0.1
|
||||
import StatusQ.Components 0.1
|
||||
|
||||
Rectangle {
|
||||
id: statusExpandableItem
|
||||
|
||||
property alias primaryText: primaryText.text
|
||||
property alias secondaryText: secondaryText.text
|
||||
property alias additionalText: additionalText.text
|
||||
property alias button: button
|
||||
property alias expandableComponent: expandableRegion.sourceComponent
|
||||
|
||||
property int type: StatusExpandableItem.Type.Primary
|
||||
property bool expandable: true
|
||||
property bool expanded: false
|
||||
|
||||
property StatusIconSettings icon: StatusIconSettings {
|
||||
color: Theme.palette.directColor1
|
||||
background: StatusIconBackgroundSettings {
|
||||
width: 32
|
||||
height: 32
|
||||
color: Theme.palette.primaryColor2
|
||||
}
|
||||
}
|
||||
property StatusImageSettings image: StatusImageSettings {
|
||||
width: 40
|
||||
height: 40
|
||||
}
|
||||
|
||||
enum Type {
|
||||
Primary, // 0
|
||||
Secondary, // 1
|
||||
Tertiary // 2
|
||||
}
|
||||
|
||||
implicitWidth: 718
|
||||
|
||||
radius: (statusExpandableItem.type === StatusExpandableItem.Type.Primary) ? 8 : 0
|
||||
color: "transparent"
|
||||
border.color: (statusExpandableItem.type === StatusExpandableItem.Type.Primary) ? Theme.palette.baseColor2 : "transparent"
|
||||
state: "COLLAPSED"
|
||||
clip: true
|
||||
|
||||
Rectangle {
|
||||
id: separatorRect
|
||||
anchors.top: parent.top
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
width: parent.width
|
||||
height: 1
|
||||
|
||||
color: Theme.palette.baseColor2
|
||||
visible: (statusExpandableItem.type === StatusExpandableItem.Type.Tertiary)
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: identicon
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 25
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: (statusExpandableItem.type === StatusExpandableItem.Type.Secondary) ? 0 : 11
|
||||
active: (statusExpandableItem.type !== StatusExpandableItem.Type.Tertiary)
|
||||
sourceComponent: !!statusExpandableItem.image.source.toString() ? roundedImage :
|
||||
!!statusExpandableItem.icon.name.toString() ? roundedIcon : letterIdenticon
|
||||
}
|
||||
|
||||
Component {
|
||||
id: roundedImage
|
||||
StatusRoundedImage {
|
||||
image.source: statusExpandableItem.image.source
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: roundedIcon
|
||||
StatusRoundIcon {
|
||||
icon.background.width: statusExpandableItem.icon.background.width
|
||||
icon.background.height: statusExpandableItem.icon.background.height
|
||||
icon.background.color: statusExpandableItem.icon.background.color
|
||||
icon.color: statusExpandableItem.icon.color
|
||||
icon.name: statusExpandableItem.icon.name
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: letterIdenticon
|
||||
StatusLetterIdenticon {
|
||||
height: 40
|
||||
width: 40
|
||||
name: primaryText.text
|
||||
letterSize: 20
|
||||
color: Theme.palette.miscColor5
|
||||
}
|
||||
}
|
||||
|
||||
StatusBaseText {
|
||||
id: primaryText
|
||||
anchors.top: (statusExpandableItem.type === StatusExpandableItem.Type.Primary) ||
|
||||
(statusExpandableItem.type === StatusExpandableItem.Type.Tertiary) ? parent.top : undefined
|
||||
anchors.topMargin: (statusExpandableItem.type === StatusExpandableItem.Type.Tertiary) ? 29 : 17
|
||||
anchors.left: identicon.active ? identicon.right : parent.left
|
||||
anchors.leftMargin: (statusExpandableItem.type === StatusExpandableItem.Type.Primary) ? 10 : 16
|
||||
|
||||
anchors.verticalCenter: (statusExpandableItem.type === StatusExpandableItem.Type.Secondary) ? identicon.verticalCenter : undefined
|
||||
|
||||
width: !!additionalText.text ? (button.visible ? parent.width - icon.background.width - button.width - additionalText.contentWidth - 110 :
|
||||
parent.width - icon.background.width - additionalText.contentWidth - 110) :
|
||||
(button.visible ? parent.width - icon.background.width - button.width - 70 :
|
||||
parent.width - icon.background.width - 70)
|
||||
|
||||
font.weight: (statusExpandableItem.type === StatusExpandableItem.Type.Primary) ? Font.Medium : Font.Normal
|
||||
font.pixelSize: (statusExpandableItem.type === StatusExpandableItem.Type.Primary) ? 15 : 17
|
||||
lineHeight: (statusExpandableItem.type === StatusExpandableItem.Type.Primary) ? 22 : 24
|
||||
lineHeightMode: Text.FixedHeight
|
||||
elide: Text.ElideRight
|
||||
color: (statusExpandableItem.type === StatusExpandableItem.Type.Tertiary) ? Theme.palette.baseColor1 : Theme.palette.directColor1
|
||||
}
|
||||
|
||||
StatusBaseText {
|
||||
id: secondaryText
|
||||
anchors.top: primaryText.bottom
|
||||
anchors.topMargin: 4
|
||||
anchors.left: primaryText.left
|
||||
anchors.right: primaryText.right
|
||||
|
||||
font.pixelSize: 15
|
||||
lineHeight: 22
|
||||
lineHeightMode: Text.FixedHeight
|
||||
elide: Text.ElideRight
|
||||
color: Theme.palette.directColor3
|
||||
}
|
||||
|
||||
StatusBaseText {
|
||||
id: additionalText
|
||||
anchors.verticalCenter: primaryText.verticalCenter
|
||||
anchors.verticalCenterOffset: 2
|
||||
anchors.right: expandImage.left
|
||||
anchors.rightMargin: 16
|
||||
|
||||
font.pixelSize: 15
|
||||
lineHeight: 24
|
||||
lineHeightMode: Text.FixedHeight
|
||||
elide: Text.ElideRight
|
||||
color: Theme.palette.baseColor1
|
||||
}
|
||||
|
||||
StatusButton {
|
||||
id: button
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 19
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 16
|
||||
visible: !!text
|
||||
}
|
||||
|
||||
StatusIcon {
|
||||
id: expandImage
|
||||
anchors.verticalCenter: (statusExpandableItem.type === StatusExpandableItem.Type.Tertiary) ?
|
||||
primaryText.verticalCenter : identicon.verticalCenter
|
||||
anchors.verticalCenterOffset:(statusExpandableItem.type === StatusExpandableItem.Type.Tertiary) ? -3 : -1
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: (statusExpandableItem.type === StatusExpandableItem.Type.Primary) ? 23 : 6
|
||||
visible: expandable && !button.visible
|
||||
color: (statusExpandableItem.type === StatusExpandableItem.Type.Tertiary) ?
|
||||
Theme.palette.baseColor1 :
|
||||
Theme.palette.directColor1
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
if(expandable) {
|
||||
expanded = !expanded
|
||||
}
|
||||
}
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
visible: !button.visible && expandable
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: expandableRegion
|
||||
anchors.top: !!secondaryText.text ? secondaryText.bottom: primaryText.bottom
|
||||
anchors.topMargin: 16
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: (statusExpandableItem.type === StatusExpandableItem.Type.Primary) ? 48 : 0
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: (statusExpandableItem.type === StatusExpandableItem.Type.Primary) ? 16 : 0
|
||||
active: false
|
||||
}
|
||||
|
||||
onExpandedChanged: {
|
||||
if(expanded) {
|
||||
state = "EXPANDED"
|
||||
}
|
||||
else {
|
||||
state = "COLLAPSED"
|
||||
}
|
||||
}
|
||||
|
||||
states: [
|
||||
State {
|
||||
name: "EXPANDED"
|
||||
PropertyChanges {target: expandImage; icon: "chevron-up"}
|
||||
PropertyChanges {target: statusExpandableItem; height: 82 + expandableRegion.height + 22}
|
||||
PropertyChanges {target: expandableRegion; active: true}
|
||||
},
|
||||
State {
|
||||
name: "COLLAPSED"
|
||||
PropertyChanges {target: expandImage; icon: "chevron-down"}
|
||||
PropertyChanges {target: statusExpandableItem; height: 82}
|
||||
PropertyChanges {target: expandableRegion; active: false}
|
||||
}
|
||||
]
|
||||
|
||||
transitions: [
|
||||
Transition {
|
||||
from: "COLLAPSED"
|
||||
to: "EXPANDED"
|
||||
NumberAnimation { properties: "height"; duration: 200 }
|
||||
},
|
||||
Transition {
|
||||
from: "EXPANDED"
|
||||
to: "COLLAPSED"
|
||||
NumberAnimation { properties: "height"; duration: 200 }
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,155 +0,0 @@
|
||||
import QtQuick 2.14
|
||||
import StatusQ.Core 0.1
|
||||
import StatusQ.Controls 0.1
|
||||
import StatusQ.Core.Theme 0.1
|
||||
import StatusQ.Components 0.1
|
||||
|
||||
Rectangle {
|
||||
id: statusExpandableSettingsItem
|
||||
|
||||
property alias primaryText: primaryText.text
|
||||
property alias secondaryText: secondaryText.text
|
||||
property alias button: button
|
||||
property alias expandableComponent: expandableRegion.sourceComponent
|
||||
|
||||
property bool expandable: true
|
||||
property bool expanded: false
|
||||
property StatusIconSettings icon: StatusIconSettings {
|
||||
color: Theme.palette.directColor1
|
||||
background: StatusIconBackgroundSettings {
|
||||
width: 32
|
||||
height: 32
|
||||
color: Theme.palette.primaryColor2
|
||||
}
|
||||
}
|
||||
|
||||
implicitWidth: 718
|
||||
|
||||
radius: 8
|
||||
color: "transparent"
|
||||
border.color: Theme.palette.baseColor2
|
||||
state: "COLLAPSED"
|
||||
clip: true
|
||||
|
||||
StatusRoundIcon {
|
||||
id: roundIcon
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 25
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 11
|
||||
|
||||
icon.background.width: statusExpandableSettingsItem.icon.background.width
|
||||
icon.background.height: statusExpandableSettingsItem.icon.background.height
|
||||
icon.background.color: statusExpandableSettingsItem.icon.background.color
|
||||
icon.color: statusExpandableSettingsItem.icon.color
|
||||
icon.name: statusExpandableSettingsItem.icon.name
|
||||
}
|
||||
|
||||
StatusBaseText {
|
||||
id: primaryText
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 17
|
||||
anchors.left: roundIcon.right
|
||||
anchors.leftMargin: 10
|
||||
|
||||
width: button.visible ? parent.width - icon.background.width - button.width - 70 :
|
||||
parent.width - icon.background.width - 70
|
||||
|
||||
font.weight: Font.Medium
|
||||
font.pixelSize: 15
|
||||
lineHeight: 22
|
||||
lineHeightMode: Text.FixedHeight
|
||||
elide: Text.ElideRight
|
||||
color: Theme.palette.directColor1
|
||||
}
|
||||
|
||||
StatusBaseText {
|
||||
id: secondaryText
|
||||
anchors.top: primaryText.bottom
|
||||
anchors.topMargin: 4
|
||||
anchors.left: primaryText.left
|
||||
anchors.right: primaryText.right
|
||||
|
||||
font.pixelSize: 15
|
||||
lineHeight: 22
|
||||
lineHeightMode: Text.FixedHeight
|
||||
elide: Text.ElideRight
|
||||
color: Theme.palette.directColor3
|
||||
}
|
||||
|
||||
StatusButton {
|
||||
id: button
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 19
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 16
|
||||
visible: !!text
|
||||
}
|
||||
|
||||
StatusIcon {
|
||||
id: expandImage
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 36
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 23
|
||||
visible: expandable && !button.visible
|
||||
color: Theme.palette.directColor1
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: expandableRegion
|
||||
anchors.top: secondaryText.bottom
|
||||
anchors.topMargin: 16
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 48
|
||||
width: parent.width - 64
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
if(expandable) {
|
||||
expanded = !expanded
|
||||
}
|
||||
}
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
visible: !button.visible && expandable
|
||||
}
|
||||
|
||||
onExpandedChanged: {
|
||||
if(expanded) {
|
||||
state = "EXPANDED"
|
||||
}
|
||||
else {
|
||||
state = "COLLAPSED"
|
||||
}
|
||||
}
|
||||
|
||||
states: [
|
||||
State {
|
||||
name: "EXPANDED"
|
||||
PropertyChanges {target: expandImage; icon: "chevron-up"}
|
||||
PropertyChanges {target: statusExpandableSettingsItem; height: 82 + expandableRegion.height + 22}
|
||||
PropertyChanges {target: expandableRegion; active: true}
|
||||
},
|
||||
State {
|
||||
name: "COLLAPSED"
|
||||
PropertyChanges {target: expandImage; icon: "chevron-down"}
|
||||
PropertyChanges {target: statusExpandableSettingsItem; height: 82}
|
||||
PropertyChanges {target: expandableRegion; active: false}
|
||||
}
|
||||
]
|
||||
|
||||
transitions: [
|
||||
Transition {
|
||||
from: "COLLAPSED"
|
||||
to: "EXPANDED"
|
||||
NumberAnimation { properties: "height"; duration: 200 }
|
||||
},
|
||||
Transition {
|
||||
from: "EXPANDED"
|
||||
to: "COLLAPSED"
|
||||
NumberAnimation { properties: "height"; duration: 200 }
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -20,4 +20,4 @@ StatusRoundIcon 0.1 StatusRoundIcon.qml
|
||||
StatusRoundedImage 0.1 StatusRoundedImage.qml
|
||||
StatusMacWindowButtons 0.1 StatusMacWindowButtons.qml
|
||||
StatusListItemBadge 0.1 StatusListItemBadge.qml
|
||||
StatusExpandableSettingsItem 0.1 StatusExpandableSettingsItem.qml
|
||||
StatusExpandableItem 0.1 StatusExpandableItem.qml
|
||||
|
||||
@@ -47,6 +47,7 @@ Item {
|
||||
property bool valid: true
|
||||
property bool pristine: true
|
||||
property bool dirty: false
|
||||
property bool pending: false
|
||||
property bool leftIcon: true
|
||||
|
||||
property StatusIconSettings icon: StatusIconSettings {
|
||||
@@ -56,6 +57,23 @@ Item {
|
||||
color: Theme.palette.baseColor1
|
||||
}
|
||||
|
||||
property Item component
|
||||
|
||||
onClearableChanged: {
|
||||
if (clearable && !component) {
|
||||
clearButtonLoader.active = true
|
||||
clearButtonLoader.parent = statusBaseInputComponentSlot
|
||||
} else {
|
||||
clearButtonLoader.active = false
|
||||
}
|
||||
}
|
||||
|
||||
onComponentChanged: {
|
||||
if (!!component) {
|
||||
component.parent = statusBaseInputComponentSlot
|
||||
}
|
||||
}
|
||||
|
||||
implicitWidth: 448
|
||||
implicitHeight: multiline ? Math.max((edit.implicitHeight + topPadding + bottomPadding), 44) : 44
|
||||
|
||||
@@ -80,149 +98,168 @@ Item {
|
||||
return sensor.containsMouse ? Theme.palette.primaryColor2 : "transparent"
|
||||
}
|
||||
|
||||
StatusIcon {
|
||||
id: statusIcon
|
||||
anchors.topMargin: 10
|
||||
anchors.left: statusBaseInput.leftIcon ? parent.left : undefined
|
||||
anchors.right: !statusBaseInput.leftIcon ? parent.right : undefined
|
||||
anchors.leftMargin: 10
|
||||
anchors.rightMargin: 10
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
icon: statusBaseInput.icon.name
|
||||
width: statusBaseInput.icon.width
|
||||
height: statusBaseInput.icon.height
|
||||
color: statusBaseInput.icon.color
|
||||
visible: !!statusBaseInput.icon.name
|
||||
}
|
||||
|
||||
Flickable {
|
||||
id: flick
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: (statusIcon.visible && statusBaseInput.leftIcon) ?
|
||||
statusIcon.right : parent.left
|
||||
anchors.right: (statusIcon.visible && !statusBaseInput.leftIcon) ?
|
||||
statusIcon.left : parent.right
|
||||
anchors.leftMargin: statusIcon.visible && statusBaseInput.leftIcon ? 8
|
||||
: statusBaseInput.leftPadding
|
||||
anchors.rightMargin: statusBaseInput.rightPadding + clearable ? clearButton.width
|
||||
: statusIcon.visible && !leftIcon ? 8: 0
|
||||
anchors.topMargin: statusBaseInput.topPadding
|
||||
anchors.bottomMargin: statusBaseInput.bottomPadding
|
||||
contentWidth: edit.paintedWidth
|
||||
contentHeight: edit.paintedHeight
|
||||
boundsBehavior: Flickable.StopAtBounds
|
||||
QC.ScrollBar.vertical: QC.ScrollBar { interactive: multiline; enabled: multiline }
|
||||
function ensureVisible(r) {
|
||||
if (contentX >= r.x)
|
||||
contentX = r.x;
|
||||
else if (contentX+width <= r.x+r.width)
|
||||
contentX = r.x+r.width-width;
|
||||
if (contentY >= r.y)
|
||||
contentY = r.y;
|
||||
else if (contentY+height <= r.y+r.height)
|
||||
contentY = r.y+r.height-height;
|
||||
}
|
||||
TextEdit {
|
||||
id: edit
|
||||
property string previousText: text
|
||||
width: flick.width
|
||||
height: flick.height
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
selectByMouse: true
|
||||
selectionColor: Theme.palette.primaryColor2
|
||||
selectedTextColor: color
|
||||
focus: true
|
||||
font.pixelSize: 15
|
||||
font.family: Theme.palette.baseFont.name
|
||||
color: Theme.palette.directColor1
|
||||
onCursorRectangleChanged: { flick.ensureVisible(cursorRectangle); }
|
||||
wrapMode: statusBaseInput.multiline ? Text.WrapAtWordBoundaryOrAnywhere : TextEdit.NoWrap
|
||||
onActiveFocusChanged: {
|
||||
if (statusBaseInput.pristine) {
|
||||
statusBaseInput.pristine = false
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onReturnPressed: {
|
||||
if (multiline) {
|
||||
event.accepted = false
|
||||
} else {
|
||||
event.accepted = true
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onEnterPressed: {
|
||||
if (multiline) {
|
||||
event.accepted = false
|
||||
} else {
|
||||
event.accepted = true
|
||||
}
|
||||
}
|
||||
|
||||
Keys.forwardTo: [statusBaseInput]
|
||||
|
||||
onTextChanged: {
|
||||
statusBaseInput.dirty = true
|
||||
if (statusBaseInput.maximumLength > 0) {
|
||||
if (text.length > statusBaseInput.maximumLength) {
|
||||
var cursor = cursorPosition;
|
||||
text = previousText;
|
||||
if (cursor > text.length) {
|
||||
cursorPosition = text.length;
|
||||
} else {
|
||||
cursorPosition = cursor-1;
|
||||
}
|
||||
}
|
||||
previousText = text
|
||||
}
|
||||
}
|
||||
StatusBaseText {
|
||||
id: placeholder
|
||||
visible: (edit.text.length === 0)
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: statusBaseInput.rightPadding
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
font.pixelSize: 15
|
||||
elide: StatusBaseText.ElideRight
|
||||
font.family: Theme.palette.baseFont.name
|
||||
color: statusBaseInput.enabled ? Theme.palette.baseColor1 :
|
||||
Theme.palette.directColor6
|
||||
}
|
||||
}
|
||||
|
||||
} // Flickable
|
||||
MouseArea {
|
||||
id: sensor
|
||||
enabled: !edit.activeFocus
|
||||
hoverEnabled: true
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.IBeamCursor
|
||||
onClicked: edit.forceActiveFocus()
|
||||
}
|
||||
onClicked: {
|
||||
edit.forceActiveFocus()
|
||||
}
|
||||
|
||||
StatusIcon {
|
||||
id: statusIcon
|
||||
anchors.topMargin: 10
|
||||
anchors.left: statusBaseInput.leftIcon ? parent.left : undefined
|
||||
anchors.right: !statusBaseInput.leftIcon ? parent.right : undefined
|
||||
anchors.leftMargin: 10
|
||||
anchors.rightMargin: 10
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
icon: statusBaseInput.icon.name
|
||||
width: statusBaseInput.icon.width
|
||||
height: statusBaseInput.icon.height
|
||||
color: statusBaseInput.icon.color
|
||||
visible: !!statusBaseInput.icon.name
|
||||
}
|
||||
|
||||
Flickable {
|
||||
id: flick
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: (statusIcon.visible && statusBaseInput.leftIcon) ?
|
||||
statusIcon.right : parent.left
|
||||
anchors.right: {
|
||||
if (!!statusBaseInput.component) {
|
||||
return statusBaseInputComponentSlot.left
|
||||
}
|
||||
return statusIcon.visible && !statusBaseInput.leftIcon ? statusIcon.left : parent.right
|
||||
}
|
||||
anchors.leftMargin: statusIcon.visible && statusBaseInput.leftIcon ? 8
|
||||
: statusBaseInput.leftPadding
|
||||
anchors.rightMargin: {
|
||||
return clearable ? clearButtonLoader.width + 12 :
|
||||
(statusIcon.visible && !leftIcon) || !!statusBaseInput.component ? 8 : 0
|
||||
}
|
||||
anchors.topMargin: statusBaseInput.topPadding
|
||||
anchors.bottomMargin: statusBaseInput.bottomPadding
|
||||
contentWidth: edit.paintedWidth
|
||||
contentHeight: edit.paintedHeight
|
||||
boundsBehavior: Flickable.StopAtBounds
|
||||
QC.ScrollBar.vertical: QC.ScrollBar { interactive: multiline; enabled: multiline }
|
||||
function ensureVisible(r) {
|
||||
if (contentX >= r.x)
|
||||
contentX = r.x;
|
||||
else if (contentX+width <= r.x+r.width)
|
||||
contentX = r.x+r.width-width;
|
||||
if (contentY >= r.y)
|
||||
contentY = r.y;
|
||||
else if (contentY+height <= r.y+r.height)
|
||||
contentY = r.y+r.height-height;
|
||||
}
|
||||
TextEdit {
|
||||
id: edit
|
||||
property string previousText: text
|
||||
width: flick.width
|
||||
height: flick.height
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
selectByMouse: true
|
||||
selectionColor: Theme.palette.primaryColor2
|
||||
selectedTextColor: color
|
||||
focus: true
|
||||
font.pixelSize: 15
|
||||
font.family: Theme.palette.baseFont.name
|
||||
color: Theme.palette.directColor1
|
||||
onCursorRectangleChanged: { flick.ensureVisible(cursorRectangle); }
|
||||
wrapMode: statusBaseInput.multiline ? Text.WrapAtWordBoundaryOrAnywhere : TextEdit.NoWrap
|
||||
onActiveFocusChanged: {
|
||||
if (statusBaseInput.pristine) {
|
||||
statusBaseInput.pristine = false
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onReturnPressed: {
|
||||
if (multiline) {
|
||||
event.accepted = false
|
||||
} else {
|
||||
event.accepted = true
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onEnterPressed: {
|
||||
if (multiline) {
|
||||
event.accepted = false
|
||||
} else {
|
||||
event.accepted = true
|
||||
}
|
||||
}
|
||||
|
||||
Keys.forwardTo: [statusBaseInput]
|
||||
|
||||
onTextChanged: {
|
||||
statusBaseInput.dirty = true
|
||||
if (statusBaseInput.maximumLength > 0) {
|
||||
if (text.length > statusBaseInput.maximumLength) {
|
||||
var cursor = cursorPosition;
|
||||
text = previousText;
|
||||
if (cursor > text.length) {
|
||||
cursorPosition = text.length;
|
||||
} else {
|
||||
cursorPosition = cursor-1;
|
||||
}
|
||||
}
|
||||
previousText = text
|
||||
}
|
||||
}
|
||||
StatusBaseText {
|
||||
id: placeholder
|
||||
visible: (edit.text.length === 0)
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: statusBaseInput.rightPadding
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
font.pixelSize: 15
|
||||
elide: StatusBaseText.ElideRight
|
||||
font.family: Theme.palette.baseFont.name
|
||||
color: statusBaseInput.enabled ? Theme.palette.baseColor1 :
|
||||
Theme.palette.directColor6
|
||||
}
|
||||
}
|
||||
|
||||
} // Flickable
|
||||
|
||||
Item {
|
||||
id: statusBaseInputComponentSlot
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 12
|
||||
width: childrenRect.width
|
||||
height: childrenRect.height
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
} // Rectangle
|
||||
|
||||
StatusFlatRoundButton {
|
||||
id: clearButton
|
||||
visible: edit.text.length != 0 &&
|
||||
statusBaseInput.clearable &&
|
||||
!statusBaseInput.multiline &&
|
||||
edit.activeFocus
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 8
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
type: StatusFlatRoundButton.Type.Secondary
|
||||
width: 24
|
||||
height: 24
|
||||
icon.name: "clear"
|
||||
icon.width: 16
|
||||
icon.height: 16
|
||||
icon.color: Theme.palette.baseColor1
|
||||
onClicked: {
|
||||
edit.clear()
|
||||
Loader {
|
||||
id: clearButtonLoader
|
||||
sourceComponent: StatusFlatRoundButton {
|
||||
id: clearButton
|
||||
visible: edit.text.length != 0 &&
|
||||
statusBaseInput.clearable &&
|
||||
!statusBaseInput.multiline &&
|
||||
edit.activeFocus
|
||||
type: StatusFlatRoundButton.Type.Secondary
|
||||
width: 24
|
||||
height: 24
|
||||
icon.name: "clear"
|
||||
icon.width: 16
|
||||
icon.height: 16
|
||||
icon.color: Theme.palette.baseColor1
|
||||
onClicked: {
|
||||
edit.clear()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ CheckBox {
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
wrapMode: Text.WordWrap
|
||||
width: parent.width
|
||||
color: Theme.palette.directColor1
|
||||
leftPadding: !!statusCheckBox.text ? statusCheckBox.indicator.width + statusCheckBox.spacing
|
||||
: statusCheckBox.indicator.width
|
||||
}
|
||||
|
||||
@@ -24,31 +24,100 @@ Item {
|
||||
|
||||
property alias input: statusBaseInput
|
||||
property alias valid: statusBaseInput.valid
|
||||
property alias pending: statusBaseInput.pending
|
||||
property alias text: statusBaseInput.text
|
||||
property string label: ""
|
||||
property string secondaryLabel: ""
|
||||
property int charLimit: 0
|
||||
property string errorMessage: ""
|
||||
property real leftPadding: 16
|
||||
property real rightPadding: 16
|
||||
property list<StatusValidator> validators
|
||||
property list<StatusValidator> asyncValidators
|
||||
property int validationMode: StatusInput.ValidationMode.OnlyWhenDirty
|
||||
|
||||
property var pendingValidators: []
|
||||
|
||||
enum ValidationMode {
|
||||
OnlyWhenDirty, // validates input only after it has become dirty
|
||||
Always // validates input even before it has become dirty
|
||||
}
|
||||
|
||||
property var errors: ({})
|
||||
property var asyncErrors: ({})
|
||||
|
||||
function reset() {
|
||||
statusBaseInput.valid = false
|
||||
statusBaseInput.pristine = true
|
||||
statusBaseInput.text = ""
|
||||
errorMessage = ""
|
||||
}
|
||||
|
||||
function validate() {
|
||||
if (!statusBaseInput.dirty && validationMode === StatusInput.ValidationMode.OnlyWhenDirty) {
|
||||
return
|
||||
}
|
||||
statusBaseInput.valid = true
|
||||
if (validators.length) {
|
||||
for (let idx in validators) {
|
||||
let result = validators[idx].validate(statusBaseInput.text)
|
||||
let validator = validators[idx]
|
||||
let result = validator.validate(statusBaseInput.text)
|
||||
|
||||
if (typeof result === "boolean" && result) {
|
||||
statusBaseInput.valid = true
|
||||
statusBaseInput.valid = statusBaseInput.valid && true
|
||||
delete errors[validator.name]
|
||||
} else {
|
||||
if (!errors) {
|
||||
errors = {}
|
||||
}
|
||||
errors[validators[idx].name] = result
|
||||
statusBaseInput.valid = false
|
||||
result.errorMessage = validator.errorMessage
|
||||
errors[validator.name] = result
|
||||
statusBaseInput.valid = statusBaseInput.valid && false
|
||||
}
|
||||
}
|
||||
if (errors){
|
||||
let errs = Object.values(errors)
|
||||
if (errs && errs[0]) {
|
||||
errorMessage.text = errs[0].errorMessage || root.errorMessage;
|
||||
} else {
|
||||
errorMessage.text = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (asyncValidators.length && !Object.values(errors).length) {
|
||||
root.pending = true
|
||||
for (let idx in asyncValidators) {
|
||||
let asyncValidator = asyncValidators[idx]
|
||||
if (pendingValidators.indexOf(asyncValidator.name) == -1) {
|
||||
asyncValidator.input = root
|
||||
pendingValidators.push(asyncValidator.name)
|
||||
asyncValidator.validate(statusBaseInput.text)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function updateValidity(validatorName, result) {
|
||||
if (!asyncErrors) {
|
||||
asyncErrors = {}
|
||||
}
|
||||
|
||||
if (typeof result === "boolean" && result) {
|
||||
if (asyncErrors[validatorName] !== undefined) {
|
||||
delete asyncErrors[validatorName]
|
||||
}
|
||||
errorMessage.text = ""
|
||||
} else {
|
||||
asyncErrors[validatorName] = result
|
||||
for (let idx in asyncValidators) {
|
||||
errorMessage.text = asyncValidators[idx].errorMessage || root.errorMessage
|
||||
break;
|
||||
}
|
||||
}
|
||||
pendingValidators = pendingValidators.filter(v => v !== validatorName)
|
||||
root.pending = pendingValidators.length > 0
|
||||
root.valid = Object.values(asyncErrors).length == 0
|
||||
}
|
||||
|
||||
Component.onCompleted: validate()
|
||||
@@ -58,9 +127,9 @@ Item {
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.topMargin: visible ? 8 : 0
|
||||
anchors.leftMargin: 16
|
||||
anchors.leftMargin: root.leftPadding
|
||||
anchors.right: (charLimitLabel.visible ? charLimitLabel.right : parent.right)
|
||||
anchors.rightMargin: 16
|
||||
anchors.rightMargin: root.rightPadding
|
||||
height: visible ? 17 : 0
|
||||
visible: !!root.label
|
||||
spacing: 5
|
||||
@@ -89,7 +158,7 @@ Item {
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: visible ? 11 : 0
|
||||
anchors.rightMargin: 16
|
||||
anchors.rightMargin: root.rightPadding
|
||||
visible: root.charLimit > 0
|
||||
|
||||
text: "%1 / %2".arg(statusBaseInput.text.length).arg(root.charLimit)
|
||||
@@ -104,8 +173,8 @@ Item {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: charLimitLabel.visible ? 11 : 8
|
||||
anchors.leftMargin: 16
|
||||
anchors.rightMargin: 16
|
||||
anchors.leftMargin: root.leftPadding
|
||||
anchors.rightMargin: root.rightPadding
|
||||
maximumLength: root.charLimit
|
||||
onTextChanged: root.validate()
|
||||
|
||||
@@ -118,16 +187,16 @@ Item {
|
||||
anchors.top: statusBaseInput.bottom
|
||||
anchors.topMargin: 11
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 16
|
||||
anchors.rightMargin: root.rightPadding
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 16
|
||||
anchors.leftMargin: root.leftPadding
|
||||
|
||||
height: visible ? implicitHeight : 0
|
||||
visible: !!root.errorMessage && !statusBaseInput.valid
|
||||
visible: !!text && !statusBaseInput.valid
|
||||
|
||||
font.pixelSize: 12
|
||||
color: Theme.palette.dangerColor1
|
||||
text: root.errorMessage
|
||||
|
||||
|
||||
horizontalAlignment: Text.AlignRight
|
||||
wrapMode: Text.WordWrap
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import StatusQ.Controls 0.1
|
||||
import StatusQ.Core.Utils 0.1
|
||||
|
||||
StatusValidator {
|
||||
name: "addressOrEns"
|
||||
|
||||
errorMessage: "Please enter a valid address or ENS name."
|
||||
|
||||
validate: function (t) {
|
||||
return Utils.isValidAddress(t) || Utils.isValidEns(t) ?
|
||||
true :
|
||||
{ actual: t }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import StatusQ.Controls 0.1
|
||||
import StatusQ.Core.Utils 0.1
|
||||
|
||||
StatusValidator {
|
||||
name: "address"
|
||||
|
||||
errorMessage: "Please enter a valid address."
|
||||
|
||||
validate: function (t) {
|
||||
return Utils.isValidAddress(t) ? true : { actual: t }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,12 @@ StatusValidator {
|
||||
|
||||
name: "minLength"
|
||||
|
||||
errorMessage: {
|
||||
minLength === 1 ?
|
||||
"Please enter a value" :
|
||||
`The value must be at least ${minLength} characters.`
|
||||
}
|
||||
|
||||
validate: function (value) {
|
||||
return value.length >= minLength ? true : {
|
||||
min: minLength,
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import QtQuick 2.13
|
||||
import StatusQ.Controls 0.1
|
||||
|
||||
QtObject {
|
||||
id: statusValidator
|
||||
|
||||
property string name: ""
|
||||
property string errorMessage: "invalid input"
|
||||
property StatusInput input
|
||||
|
||||
property var validate: function (value) {
|
||||
return true
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
module StatusQ.Controls.Validators
|
||||
|
||||
StatusAddressValidator 0.1 StatusAddressValidator.qml
|
||||
StatusAddressOrEnsValidator 0.1 StatusAddressOrEnsValidator.qml
|
||||
StatusValidator 0.1 StatusValidator.qml
|
||||
StatusMinLengthValidator 0.1 StatusMinLengthValidator.qml
|
||||
StatusMaxLengthValidator 0.1 StatusMaxLengthValidator.qml
|
||||
|
||||
@@ -68,6 +68,30 @@ ThemePalette {
|
||||
source: "../../../assets/fonts/InterStatus/InterStatus-Black.otf"
|
||||
}
|
||||
|
||||
property QtObject codeFont: FontLoader {
|
||||
source: "../../../assets/fonts/RobotoMono/RobotoMono-Regular.ttf"
|
||||
}
|
||||
|
||||
property QtObject codeFontThin: FontLoader {
|
||||
source: "../../../assets/fonts/RobotoMono/RobotoMono-Thin.ttf"
|
||||
}
|
||||
|
||||
property QtObject codeFontExtraLight: FontLoader {
|
||||
source: "../../../assets/fonts/RobotoMono/RobotoMono-ExtraLight.ttf"
|
||||
}
|
||||
|
||||
property QtObject codeFontLight: FontLoader {
|
||||
source: "../../../assets/fonts/RobotoMono/RobotoMono-Light.ttf"
|
||||
}
|
||||
|
||||
property QtObject codeFontMedium: FontLoader {
|
||||
source: "../../../assets/fonts/RobotoMono/RobotoMono-Medium.ttf"
|
||||
}
|
||||
|
||||
property QtObject codeFontBold: FontLoader {
|
||||
source: "../../../assets/fonts/RobotoMono/RobotoMono-Bold.ttf"
|
||||
}
|
||||
|
||||
property color dropShadow: getColor('black', 0.08)
|
||||
|
||||
baseColor1: getColor('graphite5')
|
||||
|
||||
@@ -68,6 +68,30 @@ ThemePalette {
|
||||
source: "../../../assets/fonts/InterStatus/InterStatus-Black.otf"
|
||||
}
|
||||
|
||||
property QtObject codeFont: FontLoader {
|
||||
source: "../../../assets/fonts/RobotoMono/RobotoMono-Regular.ttf"
|
||||
}
|
||||
|
||||
property QtObject codeFontThin: FontLoader {
|
||||
source: "../../../assets/fonts/RobotoMono/RobotoMono-Thin.ttf"
|
||||
}
|
||||
|
||||
property QtObject codeFontExtraLight: FontLoader {
|
||||
source: "../../../assets/fonts/RobotoMono/RobotoMono-ExtraLight.ttf"
|
||||
}
|
||||
|
||||
property QtObject codeFontLight: FontLoader {
|
||||
source: "../../../assets/fonts/RobotoMono/RobotoMono-Light.ttf"
|
||||
}
|
||||
|
||||
property QtObject codeFontMedium: FontLoader {
|
||||
source: "../../../assets/fonts/RobotoMono/RobotoMono-Medium.ttf"
|
||||
}
|
||||
|
||||
property QtObject codeFontBold: FontLoader {
|
||||
source: "../../../assets/fonts/RobotoMono/RobotoMono-Bold.ttf"
|
||||
}
|
||||
|
||||
baseColor1: getColor('grey5')
|
||||
baseColor2: getColor('grey4')
|
||||
baseColor3: getColor('grey3')
|
||||
|
||||
@@ -24,6 +24,13 @@ QtObject {
|
||||
property FontLoader monoFontExtraBold
|
||||
property FontLoader monoFontBlack
|
||||
|
||||
property FontLoader codeFont
|
||||
property FontLoader codeFontThin
|
||||
property FontLoader codeFontExtraLight
|
||||
property FontLoader codeFontLight
|
||||
property FontLoader codeFontMedium
|
||||
property FontLoader codeFontBold
|
||||
|
||||
property color black: getColor('black')
|
||||
property color white: getColor('white')
|
||||
|
||||
|
||||
@@ -14,6 +14,18 @@ QtObject {
|
||||
}
|
||||
return returnPos;
|
||||
}
|
||||
function isValidAddress(inputValue) {
|
||||
return inputValue !== "0x" && /^0x[a-fA-F0-9]{40}$/.test(inputValue)
|
||||
}
|
||||
|
||||
function isValidEns(inputValue) {
|
||||
if (!inputValue) {
|
||||
return false
|
||||
}
|
||||
const isEmail = /(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])/.test(inputValue)
|
||||
const isDomain = /(?:(?:(?<thld>[\w\-]*)(?:\.))?(?<sld>[\w\-]*))\.(?<tld>[\w\-]*)/.test(inputValue)
|
||||
return isEmail || isDomain || (inputValue.startsWith("@") && inputValue.length > 1)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ SplitView {
|
||||
SplitView.minimumWidth: 300
|
||||
SplitView.fillWidth: true
|
||||
SplitView.fillHeight: true
|
||||
leftPadding: -5
|
||||
contentItem: (!!centerPanel) ? centerPanel : null
|
||||
background: Rectangle {
|
||||
anchors.fill: parent
|
||||
|
||||
@@ -20,6 +20,10 @@ Menu {
|
||||
property var openHandler
|
||||
property var closeHandler
|
||||
|
||||
dim: true
|
||||
Overlay.modeless: MouseArea {}
|
||||
|
||||
|
||||
signal menuItemClicked(int menuIndex)
|
||||
|
||||
onOpened: {
|
||||
|
||||
@@ -51,10 +51,19 @@ StatusModal {
|
||||
setSearchSelection(defaultSearchLocationText, "", "", false, "", "transparent")
|
||||
}
|
||||
|
||||
function forceActiveFocus() {
|
||||
contentItem.searchInput.forceActiveFocus()
|
||||
}
|
||||
|
||||
onOpened: {
|
||||
forceActiveFocus();
|
||||
}
|
||||
|
||||
contentItem: Item {
|
||||
width: parent.width
|
||||
height: root.height
|
||||
property alias searchText: inputText.text
|
||||
property alias searchInput: inputText
|
||||
|
||||
ColumnLayout {
|
||||
id: contentItemColumn
|
||||
|
||||
@@ -119,6 +119,7 @@ Row {
|
||||
color:Theme.palette.baseColor1
|
||||
width: parent.width
|
||||
elide: statusImageWithTitle.subTitleElide
|
||||
visible: !!statusImageWithTitle.subTitle
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,4 +1,5 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M15.4083 12C15.4083 13.8823 13.8824 15.4082 12 15.4082C10.1177 15.4082 8.5918 13.8823 8.5918 12C8.5918 10.1177 10.1177 8.59174 12 8.59174C13.8824 8.59174 15.4083 10.1177 15.4083 12ZM13.9083 12C13.9083 13.0539 13.0539 13.9082 12 13.9082C10.9461 13.9082 10.0918 13.0539 10.0918 12C10.0918 10.9461 10.9461 10.0917 12 10.0917C13.0539 10.0917 13.9083 10.9461 13.9083 12Z" fill="black"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.2772 23H11.7228C10.8413 23 10.0666 22.4156 9.82441 21.568L9.3263 19.8247C9.22927 19.485 8.84755 19.3171 8.53163 19.4751L6.9006 20.2906C6.14049 20.6707 5.22247 20.5217 4.62156 19.9208L4.07924 19.3784C3.47832 18.7775 3.32935 17.8595 3.7094 17.0994L4.52491 15.4684C4.68287 15.1525 4.51495 14.7707 4.17534 14.6737L2.43196 14.1756C1.58437 13.9334 1 13.1587 1 12.2772V11.7228C1 10.8413 1.58437 10.0666 2.43196 9.82441L4.17534 9.3263C4.51495 9.22927 4.68287 8.84755 4.52491 8.53163L3.7094 6.9006C3.32935 6.14049 3.47832 5.22247 4.07924 4.62156L4.62156 4.07924C5.22247 3.47832 6.14049 3.32935 6.9006 3.7094L8.53163 4.52491C8.84755 4.68287 9.22927 4.51495 9.3263 4.17534L9.82441 2.43196C10.0666 1.58436 10.8413 1 11.7228 1H12.2772C13.1587 1 13.9334 1.58436 14.1756 2.43196L14.6737 4.17534C14.7707 4.51495 15.1525 4.68287 15.4684 4.52491L17.0994 3.7094C17.8595 3.32935 18.7775 3.47832 19.3784 4.07924L19.9208 4.62156C20.5217 5.22247 20.6707 6.14049 20.2906 6.9006L19.4751 8.53163C19.3171 8.84755 19.485 9.22927 19.8247 9.3263L21.568 9.82441C22.4156 10.0666 23 10.8413 23 11.7228V12.2772C23 13.1587 22.4156 13.9334 21.568 14.1756L19.8247 14.6737C19.485 14.7707 19.3171 15.1525 19.4751 15.4684L20.2906 17.0994C20.6707 17.8595 20.5217 18.7775 19.9208 19.3784L19.3784 19.9208C18.7775 20.5217 17.8595 20.6707 17.0994 20.2906L15.4684 19.4751C15.1525 19.3171 14.7707 19.485 14.6737 19.8247L14.1756 21.568C13.9334 22.4156 13.1587 23 12.2772 23ZM11.7228 21.5C11.511 21.5 11.3249 21.3596 11.2667 21.156L10.7686 19.4126C10.4135 18.1699 9.01678 17.5555 7.86081 18.1334L6.22978 18.949C6.04716 19.0403 5.82659 19.0045 5.68222 18.8601L5.1399 18.3178C4.99552 18.1734 4.95973 17.9528 5.05104 17.7702L5.86655 16.1392C6.44454 14.9832 5.8301 13.5865 4.58742 13.2314L2.84404 12.7333C2.6404 12.6751 2.5 12.489 2.5 12.2772V11.7228C2.5 11.511 2.6404 11.3249 2.84404 11.2667L4.58742 10.7686C5.83011 10.4135 6.44454 9.01678 5.86656 7.86081L5.05104 6.22978C4.95973 6.04716 4.99552 5.82659 5.1399 5.68222L5.68222 5.1399C5.82659 4.99552 6.04716 4.95973 6.22978 5.05104L7.86081 5.86655C9.01677 6.44454 10.4135 5.83011 10.7686 4.58742L11.2667 2.84404C11.3249 2.6404 11.511 2.5 11.7228 2.5H12.2772C12.489 2.5 12.6751 2.6404 12.7333 2.84404L13.2314 4.58742C13.5865 5.83011 14.9832 6.44454 16.1392 5.86655L17.7702 5.05104C17.9528 4.95973 18.1734 4.99552 18.3178 5.1399L18.8601 5.68222C19.0045 5.82659 19.0403 6.04716 18.949 6.22978L18.1334 7.86081C17.5555 9.01678 18.1699 10.4135 19.4126 10.7686L21.156 11.2667C21.3596 11.3249 21.5 11.511 21.5 11.7228V12.2772C21.5 12.489 21.3596 12.6751 21.156 12.7333L19.4126 13.2314C18.1699 13.5865 17.5555 14.9832 18.1334 16.1392L18.949 17.7702C19.0403 17.9528 19.0045 18.1734 18.8601 18.3178L18.3178 18.8601C18.1734 19.0045 17.9528 19.0403 17.7702 18.949L16.1392 18.1334C14.9832 17.5555 13.5865 18.1699 13.2314 19.4126L12.7333 21.156C12.6751 21.3596 12.489 21.5 12.2772 21.5H11.7228Z" fill="black"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 13C13.933 13 15.5 11.433 15.5 9.5C15.5 7.567 13.933 6 12 6C10.067 6 8.5 7.567 8.5 9.5C8.5 11.433 10.067 13 12 13ZM12 11.5C13.1046 11.5 14 10.6046 14 9.5C14 8.39543 13.1046 7.5 12 7.5C10.8954 7.5 10 8.39543 10 9.5C10 10.6046 10.8954 11.5 12 11.5Z" fill="black"/>
|
||||
<path d="M16.4465 16.2731C16.8119 16.5572 16.822 17.0954 16.4657 17.3909C16.1611 17.6435 15.723 17.6 15.4043 17.3655C14.4513 16.6643 13.2741 16.25 12.0002 16.25C10.7262 16.25 9.54901 16.6643 8.59597 17.3655C8.27728 17.6 7.83919 17.6435 7.53463 17.3909C7.17828 17.0954 7.18836 16.5572 7.55383 16.2731C8.78161 15.3185 10.3245 14.75 12.0002 14.75C13.6758 14.75 15.2187 15.3185 16.4465 16.2731Z" fill="black"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22ZM12 20.5C16.6944 20.5 20.5 16.6944 20.5 12C20.5 7.30558 16.6944 3.5 12 3.5C7.30558 3.5 3.5 7.30558 3.5 12C3.5 16.6944 7.30558 20.5 12 20.5Z" fill="black"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 1.1 KiB |
+1
-1
@@ -266,7 +266,7 @@
|
||||
<file>src/assets/img/icons/windows_titlebar/status.svg</file>
|
||||
<file>src/StatusQ/Controls/StatusSwitchTabButton.qml</file>
|
||||
<file>src/StatusQ/Controls/StatusSwitchTabBar.qml</file>
|
||||
<file>src/StatusQ/Components/StatusExpandableSettingsItem.qml</file>
|
||||
<file>src/StatusQ/Components/StatusExpandableItem.qml</file>
|
||||
<file>src/assets/img/icons/snt.svg</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
Reference in New Issue
Block a user