2026-02-23 10:36:09 +01:00

238 lines
8.0 KiB
QML

import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Logos.Theme
import Logos.Controls
Rectangle {
id: root
// --- Public API: data in ---
property var fromAccountModel: null // LEZAccountFilterModel from backend (filtered by public/private)
property string transferResult: ""
// --- Public API: signals out ---
signal transferRequested(bool isPublic, string fromAccountId, string toAddress, string amount)
readonly property int fromFilterCount: fromAccountModel ? fromAccountModel.count : 0
QtObject {
id: d
readonly property bool sendEnabled: toField && amountField && manualFromField
&& toField.text.length > 0 && amountField.text.length > 0
&& ((fromFilterCount > 0 && fromCombo.currentIndex >= 0)
|| (fromFilterCount === 0 && manualFromField.text.trim().length > 0))
}
Binding {
target: fromAccountModel
property: "filterByPublic"
value: transferTypeBar.currentIndex === 0
when: fromAccountModel != null
}
radius: Theme.spacing.radiusXlarge
color: Theme.palette.backgroundSecondary
ColumnLayout {
anchors.fill: parent
anchors.margins: Theme.spacing.large
spacing: Theme.spacing.large
LogosText {
text: qsTr("Transfer")
font.pixelSize: Theme.typography.titleText
font.weight: Theme.typography.weightBold
color: Theme.palette.text
}
// Transfer type toggle
TabBar {
id: transferTypeBar
Layout.preferredWidth: 200
currentIndex: 0
background: Rectangle {
color: Theme.palette.backgroundSecondary
radius: Theme.spacing.radiusSmall
}
LogosTabButton {
text: qsTr("Public")
}
LogosTabButton {
text: qsTr("Private")
}
}
// From: dropdown when accounts exist, or manual entry when list is empty
ColumnLayout {
Layout.fillWidth: true
spacing: Theme.spacing.small
LogosText {
text: qsTr("From")
font.pixelSize: Theme.typography.secondaryText
color: Theme.palette.textSecondary
}
LogosTextField {
id: manualFromField
Layout.fillWidth: true
placeholderText: qsTr("Paste or type from address")
visible: fromFilterCount === 0
}
ComboBox {
id: fromCombo
Layout.fillWidth: true
leftPadding: 12
rightPadding: 12
implicitHeight: 40
model: fromAccountModel
textRole: "name"
valueRole: "address"
visible: fromFilterCount > 0
background: Rectangle {
radius: Theme.spacing.radiusSmall
color: Theme.palette.backgroundSecondary
border.width: 1
border.color: fromCombo.popup.visible ? Theme.palette.overlayOrange : Theme.palette.backgroundElevated
}
indicator: LogosText {
id: indicatorText
text: "▼"
font.pixelSize: Theme.typography.secondaryText
color: Theme.palette.textSecondary
x: fromCombo.width - width - 12
y: (fromCombo.height - height) / 2
visible: fromCombo.count > 0
}
contentItem: TextInput {
readOnly: true
selectByMouse: true
width: fromCombo.width - indicatorText.width - 12
font.pixelSize: Theme.typography.secondaryText
color: Theme.palette.text
text: fromCombo.currentValue ?? ""
verticalAlignment: Text.AlignVCenter
clip: true
}
delegate: ItemDelegate {
id: delegate
width: fromCombo.width
leftPadding: 12
rightPadding: 12
contentItem: LogosText {
width: parent.width - parent.leftPadding - parent.rightPadding
font.pixelSize: Theme.typography.secondaryText
color: Theme.palette.text
text: model.name
elide: Text.ElideRight
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignVCenter
}
background: Rectangle {
color: delegate.highlighted
? Theme.palette.backgroundElevated
: Theme.palette.backgroundSecondary
}
highlighted: fromCombo.highlightedIndex === index
}
popup: Popup {
y: fromCombo.height - 1
width: fromCombo.width
height: Math.min(contentItem.implicitHeight + 8, 300)
padding: 0
contentItem: ListView {
clip: true
implicitHeight: contentHeight
model: fromCombo.popup.visible ? fromCombo.delegateModel : null
ScrollIndicator.vertical: ScrollIndicator { }
highlightFollowsCurrentItem: false
}
background: Rectangle {
color: Theme.palette.backgroundSecondary
border.width: 1
border.color: Theme.palette.backgroundElevated
radius: Theme.spacing.radiusSmall
}
}
}
}
// To field
ColumnLayout {
Layout.fillWidth: true
spacing: Theme.spacing.small
LogosText {
text: qsTr("To")
font.pixelSize: Theme.typography.secondaryText
color: Theme.palette.textSecondary
}
LogosTextField {
id: toField
Layout.fillWidth: true
placeholderText: qsTr("Recipient public key")
}
}
// Amount field
ColumnLayout {
Layout.fillWidth: true
spacing: Theme.spacing.small
LogosText {
text: qsTr("Amount")
font.pixelSize: Theme.typography.secondaryText
color: Theme.palette.textSecondary
}
LogosTextField {
id: amountField
Layout.fillWidth: true
placeholderText: "0.00"
}
}
// Send button
LogosButton {
Layout.fillWidth: true
text: qsTr("Send")
font.pixelSize: Theme.typography.secondaryText
enabled: d.sendEnabled
onClicked: {
var fromId = fromFilterCount > 0 && fromCombo.currentIndex >= 0
? (fromCombo.currentValue ?? "")
: manualFromField.text.trim()
if (fromId.length > 0)
root.transferRequested(transferTypeBar.currentIndex === 0, fromId, toField.text.trim(), amountField.text.trim())
}
}
// Result label
LogosText {
Layout.fillWidth: true
text: root.transferResult
font.pixelSize: Theme.typography.secondaryText
color: root.transferResult.length > 0 ? Theme.palette.textSecondary : "transparent"
wrapMode: Text.WordWrap
}
Item {
Layout.fillHeight: true
}
}
}