mirror of
https://github.com/logos-blockchain/logos-blockchain-ui.git
synced 2026-04-01 17:03:31 +00:00
feat: add deplayment config path in ui + some visual improvements
This commit is contained in:
parent
c8e4afb4d9
commit
d94d1c3f3e
7
flake.lock
generated
7
flake.lock
generated
@ -801,16 +801,17 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1771871578,
|
||||
"narHash": "sha256-6Mu3cmdhd8e7i+n8OWcaIBye+i12gwlwt1fhd9QCbCI=",
|
||||
"lastModified": 1770837874,
|
||||
"narHash": "sha256-wr75lv1q4U1FS5+l/6ypwzJFJe06l2RyUvx1npoRS88=",
|
||||
"owner": "logos-co",
|
||||
"repo": "logos-liblogos",
|
||||
"rev": "19d29d4ef99292d9285b3a561cb7ea8029be3b74",
|
||||
"rev": "e3741c01fd3abf6b7bd9ff2fa8edf89c41fc0cea",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "logos-co",
|
||||
"repo": "logos-liblogos",
|
||||
"rev": "e3741c01fd3abf6b7bd9ff2fa8edf89c41fc0cea",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
# Follow the same nixpkgs as logos-liblogos to ensure compatibility
|
||||
nixpkgs.follows = "logos-liblogos/nixpkgs";
|
||||
logos-cpp-sdk.url = "github:logos-co/logos-cpp-sdk";
|
||||
logos-liblogos.url = "github:logos-co/logos-liblogos";
|
||||
logos-liblogos.url = "github:logos-co/logos-liblogos?rev=e3741c01fd3abf6b7bd9ff2fa8edf89c41fc0cea";
|
||||
logos-blockchain-module.url = "github:logos-blockchain/logos-blockchain-module";
|
||||
logos-capability-module.url = "github:logos-co/logos-capability-module";
|
||||
logos-design-system.url = "github:logos-co/logos-design-system";
|
||||
|
||||
@ -1,34 +1,43 @@
|
||||
#include "BlockchainBackend.h"
|
||||
#include <QByteArray>
|
||||
#include <QClipboard>
|
||||
#include <QDebug>
|
||||
#include <QDateTime>
|
||||
#include <QGuiApplication>
|
||||
#include <QSettings>
|
||||
#include <QTimer>
|
||||
#include <QUrl>
|
||||
#include <QVariant>
|
||||
|
||||
namespace {
|
||||
const char SETTINGS_ORG[] = "Logos";
|
||||
const char SETTINGS_APP[] = "BlockchainUI";
|
||||
const char CONFIG_PATH_KEY[] = "configPath";
|
||||
const char USER_CONFIG_KEY[] = "userConfigPath";
|
||||
const char DEPLOYMENT_CONFIG_KEY[] = "deploymentConfigPath";
|
||||
const QString BLOCKCHAIN_MODULE_NAME = QStringLiteral("liblogos_blockchain_module");
|
||||
}
|
||||
|
||||
BlockchainBackend::BlockchainBackend(LogosAPI* logosAPI, QObject* parent)
|
||||
: QObject(parent),
|
||||
m_status(NotStarted),
|
||||
m_configPath(""),
|
||||
m_userConfig(""),
|
||||
m_deploymentConfig(""),
|
||||
m_logModel(new LogModel(this)),
|
||||
m_logosAPI(nullptr),
|
||||
m_blockchainClient(nullptr)
|
||||
{
|
||||
QSettings s(SETTINGS_ORG, SETTINGS_APP);
|
||||
const QString envConfigPath = QString::fromUtf8(qgetenv("LB_CONFIG_PATH"));
|
||||
const QString savedConfigPath = s.value(CONFIG_PATH_KEY).toString();
|
||||
const QString savedUserConfig = s.value(USER_CONFIG_KEY).toString();
|
||||
const QString savedDeploymentConfig = s.value(DEPLOYMENT_CONFIG_KEY).toString();
|
||||
|
||||
if (!envConfigPath.isEmpty()) {
|
||||
m_configPath = envConfigPath;
|
||||
} else if (!savedConfigPath.isEmpty()) {
|
||||
m_configPath = savedConfigPath;
|
||||
m_userConfig = envConfigPath;
|
||||
} else if (!savedUserConfig.isEmpty()) {
|
||||
m_userConfig = savedUserConfig;
|
||||
}
|
||||
if (!savedDeploymentConfig.isEmpty()) {
|
||||
m_deploymentConfig = savedDeploymentConfig;
|
||||
}
|
||||
|
||||
if (!logosAPI) {
|
||||
@ -67,14 +76,25 @@ void BlockchainBackend::setStatus(BlockchainStatus newStatus)
|
||||
}
|
||||
}
|
||||
|
||||
void BlockchainBackend::setConfigPath(const QString& path)
|
||||
void BlockchainBackend::setUserConfig(const QString& path)
|
||||
{
|
||||
const QString localPath = QUrl::fromUserInput(path).toLocalFile();
|
||||
if (m_configPath != localPath) {
|
||||
m_configPath = localPath;
|
||||
if (m_userConfig != localPath) {
|
||||
m_userConfig = localPath;
|
||||
QSettings s(SETTINGS_ORG, SETTINGS_APP);
|
||||
s.setValue(CONFIG_PATH_KEY, m_configPath);
|
||||
emit configPathChanged();
|
||||
s.setValue(USER_CONFIG_KEY, m_userConfig);
|
||||
emit userConfigChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void BlockchainBackend::setDeploymentConfig(const QString& path)
|
||||
{
|
||||
const QString localPath = QUrl::fromUserInput(path).toLocalFile();
|
||||
if (m_deploymentConfig != localPath) {
|
||||
m_deploymentConfig = localPath;
|
||||
QSettings s(SETTINGS_ORG, SETTINGS_APP);
|
||||
s.setValue(DEPLOYMENT_CONFIG_KEY, m_deploymentConfig);
|
||||
emit deploymentConfigChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@ -83,6 +103,12 @@ void BlockchainBackend::clearLogs()
|
||||
m_logModel->clear();
|
||||
}
|
||||
|
||||
void BlockchainBackend::copyToClipboard(const QString& text)
|
||||
{
|
||||
if (QGuiApplication::clipboard())
|
||||
QGuiApplication::clipboard()->setText(text);
|
||||
}
|
||||
|
||||
QString BlockchainBackend::getBalance(const QString& addressHex)
|
||||
{
|
||||
if (!m_blockchainClient) {
|
||||
@ -118,7 +144,7 @@ void BlockchainBackend::startBlockchain()
|
||||
setStatus(Starting);
|
||||
|
||||
QVariant result = m_blockchainClient->invokeRemoteMethod(
|
||||
BLOCKCHAIN_MODULE_NAME, "start", m_configPath, QString());
|
||||
BLOCKCHAIN_MODULE_NAME, "start", m_userConfig, m_deploymentConfig);
|
||||
int resultCode = result.isValid() ? result.toInt() : -1;
|
||||
|
||||
if (resultCode == 0 || resultCode == 1) {
|
||||
@ -174,11 +200,7 @@ void BlockchainBackend::onNewBlock(const QVariantList& data)
|
||||
QString line;
|
||||
if (!data.isEmpty()) {
|
||||
QString blockInfo = data.first().toString();
|
||||
QString shortInfo = blockInfo.left(80);
|
||||
if (blockInfo.length() > 80) {
|
||||
shortInfo += "...";
|
||||
}
|
||||
line = QString("[%1] 📦 New block: %2").arg(timestamp, shortInfo);
|
||||
line = QString("[%1] 📦 New block: %2").arg(timestamp, blockInfo);
|
||||
} else {
|
||||
line = QString("[%1] 📦 New block (no data)").arg(timestamp);
|
||||
}
|
||||
|
||||
@ -27,7 +27,8 @@ public:
|
||||
Q_ENUM(BlockchainStatus)
|
||||
|
||||
Q_PROPERTY(BlockchainStatus status READ status NOTIFY statusChanged)
|
||||
Q_PROPERTY(QString configPath READ configPath WRITE setConfigPath NOTIFY configPathChanged)
|
||||
Q_PROPERTY(QString userConfig READ userConfig WRITE setUserConfig NOTIFY userConfigChanged)
|
||||
Q_PROPERTY(QString deploymentConfig READ deploymentConfig WRITE setDeploymentConfig NOTIFY deploymentConfigChanged)
|
||||
Q_PROPERTY(LogModel* logModel READ logModel CONSTANT)
|
||||
Q_PROPERTY(QStringList knownAddresses READ knownAddresses NOTIFY knownAddressesChanged)
|
||||
|
||||
@ -35,12 +36,15 @@ public:
|
||||
~BlockchainBackend();
|
||||
|
||||
BlockchainStatus status() const { return m_status; }
|
||||
QString configPath() const { return m_configPath; }
|
||||
QString userConfig() const { return m_userConfig; }
|
||||
QString deploymentConfig() const { return m_deploymentConfig; }
|
||||
LogModel* logModel() const { return m_logModel; }
|
||||
QStringList knownAddresses() const { return m_knownAddresses; }
|
||||
|
||||
void setConfigPath(const QString& path);
|
||||
void setUserConfig(const QString& path);
|
||||
void setDeploymentConfig(const QString& path);
|
||||
Q_INVOKABLE void clearLogs();
|
||||
Q_INVOKABLE void copyToClipboard(const QString& text);
|
||||
Q_INVOKABLE QString getBalance(const QString& addressHex);
|
||||
Q_INVOKABLE QString transferFunds(
|
||||
const QString& fromKeyHex,
|
||||
@ -55,14 +59,16 @@ public slots:
|
||||
|
||||
signals:
|
||||
void statusChanged();
|
||||
void configPathChanged();
|
||||
void userConfigChanged();
|
||||
void deploymentConfigChanged();
|
||||
void knownAddressesChanged();
|
||||
|
||||
private:
|
||||
void setStatus(BlockchainStatus newStatus);
|
||||
|
||||
BlockchainStatus m_status;
|
||||
QString m_configPath;
|
||||
QString m_userConfig;
|
||||
QString m_deploymentConfig;
|
||||
LogModel* m_logModel;
|
||||
QStringList m_knownAddresses;
|
||||
|
||||
|
||||
@ -64,15 +64,17 @@ Rectangle {
|
||||
Layout.preferredWidth: parent.width / 2
|
||||
statusText: _d.getStatusString(backend.status)
|
||||
statusColor: _d.getStatusColor(backend.status)
|
||||
configPath: backend.configPath
|
||||
canStart: !!backend.configPath
|
||||
userConfig: backend.userConfig
|
||||
deploymentConfig: backend.deploymentConfig
|
||||
canStart: !!backend.userConfig
|
||||
&& backend.status !== BlockchainBackend.Starting
|
||||
&& backend.status !== BlockchainBackend.Stopping
|
||||
isRunning: backend.status === BlockchainBackend.Running
|
||||
|
||||
onStartRequested: backend.startBlockchain()
|
||||
onStopRequested: backend.stopBlockchain()
|
||||
onChangeConfigRequested: fileDialog.open()
|
||||
onChangeUserConfigRequested: userConfigFileDialog.open()
|
||||
onChangeDeploymentConfigRequested: deploymentConfigFileDialog.open()
|
||||
}
|
||||
|
||||
WalletView {
|
||||
@ -96,16 +98,23 @@ Rectangle {
|
||||
|
||||
logModel: backend.logModel
|
||||
onClearRequested: backend.clearLogs()
|
||||
onCopyToClipboard: (text) => backend.copyToClipboard(text)
|
||||
}
|
||||
}
|
||||
|
||||
FileDialog {
|
||||
id: fileDialog
|
||||
id: userConfigFileDialog
|
||||
modality: Qt.NonModal
|
||||
nameFilters: ["YAML files (*.yaml)"]
|
||||
currentFolder: StandardPaths.standardLocations(StandardPaths.DocumentsLocation)[0]
|
||||
onAccepted: {
|
||||
backend.configPath = selectedFile
|
||||
}
|
||||
onAccepted: backend.userConfig = selectedFile
|
||||
}
|
||||
|
||||
FileDialog {
|
||||
id: deploymentConfigFileDialog
|
||||
modality: Qt.NonModal
|
||||
nameFilters: ["YAML files (*.yaml)"]
|
||||
currentFolder: StandardPaths.standardLocations(StandardPaths.DocumentsLocation)[0]
|
||||
onAccepted: backend.deploymentConfig = selectedFile
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,6 +12,7 @@ Control {
|
||||
required property var logModel // LogModel (QAbstractListModel with "text" role)
|
||||
|
||||
signal clearRequested()
|
||||
signal copyToClipboard(string text)
|
||||
|
||||
background: Rectangle {
|
||||
color: Theme.palette.background
|
||||
@ -60,11 +61,18 @@ Control {
|
||||
model: root.logModel
|
||||
spacing: 2
|
||||
|
||||
delegate: LogosText {
|
||||
delegate: ItemDelegate{
|
||||
width: ListView.view.width
|
||||
text: model.text
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
wrapMode: Text.Wrap
|
||||
contentItem: LogosText {
|
||||
text: model.text
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
wrapMode: Text.Wrap
|
||||
}
|
||||
background: Rectangle {
|
||||
color: hovered ? Theme.palette.background: "transparent"
|
||||
radius: 2
|
||||
}
|
||||
onClicked: root.copyToClipboard(model.text)
|
||||
}
|
||||
|
||||
LogosText {
|
||||
|
||||
@ -11,13 +11,15 @@ ColumnLayout {
|
||||
// --- Public API ---
|
||||
required property string statusText
|
||||
required property color statusColor
|
||||
required property string configPath
|
||||
required property string userConfig
|
||||
required property string deploymentConfig
|
||||
required property bool canStart
|
||||
required property bool isRunning
|
||||
|
||||
signal startRequested()
|
||||
signal stopRequested()
|
||||
signal changeConfigRequested()
|
||||
signal changeUserConfigRequested()
|
||||
signal changeDeploymentConfigRequested()
|
||||
|
||||
spacing: Theme.spacing.large
|
||||
|
||||
@ -71,14 +73,15 @@ ColumnLayout {
|
||||
Rectangle {
|
||||
Layout.preferredWidth: parent.width * 0.9
|
||||
Layout.preferredHeight: implicitHeight
|
||||
implicitHeight: configContent.implicitHeight + 2 * Theme.spacing.large
|
||||
implicitHeight: contentLayout.implicitHeight + 2 * Theme.spacing.large
|
||||
|
||||
color: Theme.palette.backgroundTertiary
|
||||
radius: Theme.spacing.radiusLarge
|
||||
border.color: Theme.palette.border
|
||||
border.width: 1
|
||||
|
||||
ColumnLayout {
|
||||
id: configContent
|
||||
id: contentLayout
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
@ -86,29 +89,55 @@ ColumnLayout {
|
||||
anchors.margins: Theme.spacing.large
|
||||
spacing: Theme.spacing.medium
|
||||
|
||||
LogosText {
|
||||
text: qsTr("Current Config: ")
|
||||
font.bold: true
|
||||
// User Config Card
|
||||
ConfigSelectionPanel {
|
||||
id: userConfigContent
|
||||
|
||||
title: qsTr("User Config: ")
|
||||
configPath: root.userConfig || qsTr("No file selected")
|
||||
onChangeRequested: root.changeUserConfigRequested()
|
||||
}
|
||||
|
||||
LogosText {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: -Theme.spacing.medium
|
||||
text: root.configPath || qsTr("No file selected")
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
color: Theme.palette.textSecondary
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
// Deployment Config Card
|
||||
ConfigSelectionPanel {
|
||||
id: deploymentConfigContent
|
||||
|
||||
LogosButton {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.preferredWidth: parent.width
|
||||
Layout.preferredHeight: 50
|
||||
text: qsTr("Change")
|
||||
onClicked: root.changeConfigRequested()
|
||||
title: qsTr("Deployment Config: ")
|
||||
configPath: root.deploymentConfig || qsTr("No file selected")
|
||||
onChangeRequested: root.changeDeploymentConfigRequested()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item { Layout.fillHeight: true }
|
||||
|
||||
component ConfigSelectionPanel: ColumnLayout {
|
||||
property string title
|
||||
property string configPath
|
||||
signal changeRequested()
|
||||
|
||||
spacing: Theme.spacing.medium
|
||||
|
||||
LogosText {
|
||||
text: title
|
||||
font.bold: true
|
||||
}
|
||||
|
||||
LogosText {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: -Theme.spacing.small
|
||||
text: configPath
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
color: Theme.palette.textSecondary
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
LogosButton {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 50
|
||||
text: qsTr("Change")
|
||||
onClicked: changeRequested()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -50,80 +50,9 @@ ColumnLayout {
|
||||
}
|
||||
|
||||
// Dropdown of known addresses, or type a custom address
|
||||
ComboBox {
|
||||
StyledAddressComboBox {
|
||||
id: balanceAddressCombo
|
||||
|
||||
Layout.fillWidth: true
|
||||
padding: Theme.spacing.large
|
||||
|
||||
editable: true
|
||||
model: knownAddresses
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
|
||||
background: Rectangle {
|
||||
color: Theme.palette.backgroundTertiary
|
||||
radius: Theme.spacing.radiusLarge
|
||||
border.color: Theme.palette.border
|
||||
border.width: 1
|
||||
}
|
||||
indicator: LogosText {
|
||||
id: indicatorText
|
||||
text: "▼"
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
color: Theme.palette.textSecondary
|
||||
x: balanceAddressCombo.width - width - Theme.spacing.small
|
||||
y: (balanceAddressCombo.height - height) / 2
|
||||
visible: balanceAddressCombo.count > 0
|
||||
}
|
||||
contentItem: LogosText {
|
||||
width: parent.width - indicatorText.width - Theme.spacing.large
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
font.bold: true
|
||||
text: balanceAddressCombo.displayText
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
delegate: ItemDelegate {
|
||||
id: delegate
|
||||
|
||||
width: balanceAddressCombo.width
|
||||
contentItem: LogosText {
|
||||
width: parent.width
|
||||
height: contentHeight + Theme.spacing.large
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
font.bold: true
|
||||
text: modelData
|
||||
elide: Text.ElideRight
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
background: Rectangle {
|
||||
color: delegate.highlighted ?
|
||||
Theme.palette.backgroundTertiary:
|
||||
Theme.palette.backgroundSecondary
|
||||
}
|
||||
highlighted: balanceAddressCombo.highlightedIndex === index
|
||||
}
|
||||
popup: Popup {
|
||||
y: balanceAddressCombo.height - 1
|
||||
width: balanceAddressCombo.width
|
||||
height: contentItem.implicitHeight + 100
|
||||
padding: 1
|
||||
|
||||
contentItem: ListView {
|
||||
clip: true
|
||||
implicitHeight: contentHeight
|
||||
model: balanceAddressCombo.popup.visible ? balanceAddressCombo.delegateModel : null
|
||||
ScrollIndicator.vertical: ScrollIndicator { }
|
||||
highlightFollowsCurrentItem: false
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
color: Theme.palette.backgroundSecondary
|
||||
border.color: Theme.palette.border
|
||||
border.width: 1
|
||||
radius: Theme.spacing.radiusLarge
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
@ -169,7 +98,7 @@ ColumnLayout {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.margins: Theme.spacing.large
|
||||
spacing: Theme.spacing.large
|
||||
spacing: Theme.spacing.small
|
||||
|
||||
LogosText {
|
||||
text: qsTr("Transfer funds")
|
||||
@ -177,18 +106,22 @@ ColumnLayout {
|
||||
font.bold: true
|
||||
}
|
||||
|
||||
CustomTextFeild {
|
||||
id: transferFromField
|
||||
placeholderText: qsTr("From key (64 hex chars)")
|
||||
StyledAddressComboBox {
|
||||
id: transferFromCombo
|
||||
model: knownAddresses
|
||||
}
|
||||
|
||||
CustomTextFeild {
|
||||
LogosTextField {
|
||||
id: transferToField
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 30
|
||||
placeholderText: qsTr("To key (64 hex chars)")
|
||||
}
|
||||
|
||||
CustomTextFeild {
|
||||
LogosTextField {
|
||||
id: transferAmountField
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 30
|
||||
placeholderText: qsTr("Amount")
|
||||
}
|
||||
|
||||
@ -201,7 +134,7 @@ ColumnLayout {
|
||||
id: transferButton
|
||||
text: qsTr("Transfer")
|
||||
Layout.alignment: Qt.AlignRight
|
||||
onClicked: root.transferRequested(transferFromField.text, transferToField.text, transferAmountField.text)
|
||||
onClicked: root.transferRequested(transferFromCombo.currentText.trim(), transferToField.text.trim(), transferAmountField.text)
|
||||
}
|
||||
|
||||
LogosButton {
|
||||
@ -226,18 +159,100 @@ ColumnLayout {
|
||||
Layout.preferredHeight: Theme.spacing.small
|
||||
}
|
||||
|
||||
component CustomTextFeild: TextField {
|
||||
id: textField
|
||||
component StyledAddressComboBox: ComboBox {
|
||||
id: comboControl
|
||||
|
||||
Layout.fillWidth: true
|
||||
placeholderTextColor: Theme.palette.textMuted
|
||||
padding: Theme.spacing.large
|
||||
editable: true
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
|
||||
background: Rectangle {
|
||||
radius: Theme.spacing.radiusSmall
|
||||
color: Theme.palette.backgroundSecondary
|
||||
border.color: textField.activeFocus ?
|
||||
Theme.palette.overlayOrange :
|
||||
Theme.palette.backgroundElevated
|
||||
color: Theme.palette.backgroundTertiary
|
||||
radius: Theme.spacing.radiusLarge
|
||||
border.color: Theme.palette.border
|
||||
border.width: 1
|
||||
}
|
||||
indicator: LogosText {
|
||||
id: comboIndicator
|
||||
text: "▼"
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
color: Theme.palette.textSecondary
|
||||
x: comboControl.width - width - Theme.spacing.small
|
||||
y: (comboControl.height - height) / 2
|
||||
visible: comboControl.count > 0
|
||||
}
|
||||
contentItem: Item {
|
||||
implicitWidth: comboControl.availableWidth
|
||||
implicitHeight: 30
|
||||
|
||||
TextField {
|
||||
id: comboTextField
|
||||
anchors.fill: parent
|
||||
leftPadding: 0
|
||||
rightPadding: comboControl.count > 0 ? comboIndicator.width + Theme.spacing.small : Theme.spacing.small
|
||||
topPadding: 0
|
||||
bottomPadding: 0
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
font.bold: true
|
||||
text: comboControl.editText
|
||||
onTextChanged: if (text !== comboControl.editText) comboControl.editText = text
|
||||
selectByMouse: true
|
||||
color: Theme.palette.text
|
||||
background: Item { }
|
||||
}
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
visible: comboControl.count > 0
|
||||
z: 1
|
||||
onPressed: {
|
||||
comboControl.popup.visible ? comboControl.popup.close() : comboControl.popup.open()
|
||||
}
|
||||
}
|
||||
}
|
||||
delegate: ItemDelegate {
|
||||
id: comboDelegate
|
||||
width: comboControl.width
|
||||
contentItem: LogosText {
|
||||
width: parent.width
|
||||
height: contentHeight + Theme.spacing.large
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
font.bold: true
|
||||
text: modelData
|
||||
elide: Text.ElideMiddle
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
background: Rectangle {
|
||||
color: comboDelegate.highlighted ?
|
||||
Theme.palette.backgroundTertiary :
|
||||
Theme.palette.backgroundSecondary
|
||||
}
|
||||
highlighted: comboControl.highlightedIndex === index
|
||||
}
|
||||
popup: Popup {
|
||||
y: comboControl.height - 1
|
||||
width: comboControl.width
|
||||
height: contentItem.implicitHeight
|
||||
padding: 1
|
||||
|
||||
onOpened: if (comboControl.count === 0) close()
|
||||
|
||||
contentItem: ListView {
|
||||
clip: true
|
||||
implicitHeight: contentHeight
|
||||
model: comboControl.popup.visible ? comboControl.delegateModel : null
|
||||
ScrollIndicator.vertical: ScrollIndicator { }
|
||||
highlightFollowsCurrentItem: false
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
color: Theme.palette.backgroundSecondary
|
||||
border.color: Theme.palette.border
|
||||
border.width: 1
|
||||
radius: Theme.spacing.radiusLarge
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user