From 1a263b46becfbcb7c64c9ee467ca30708010b6db Mon Sep 17 00:00:00 2001 From: Sander van Grieken Date: Wed, 12 Apr 2023 14:29:32 +0200 Subject: [PATCH] qml: keep QEAmount instances in qechanneldetails, use millisat amounts for local/remote capacity and can send/receive, refactor channel capacity graphic to ChannelBar and use that as well in ChannelDetails --- .../gui/qml/components/ChannelDetails.qml | 292 ++++++++++-------- .../qml/components/controls/ChannelBar.qml | 48 +++ .../components/controls/ChannelDelegate.qml | 42 +-- electrum/gui/qml/qechanneldetails.py | 32 +- 4 files changed, 241 insertions(+), 173 deletions(-) create mode 100644 electrum/gui/qml/components/controls/ChannelBar.qml diff --git a/electrum/gui/qml/components/ChannelDetails.qml b/electrum/gui/qml/components/ChannelDetails.qml index b57708bc5..435c68dcd 100644 --- a/electrum/gui/qml/components/ChannelDetails.qml +++ b/electrum/gui/qml/components/ChannelDetails.qml @@ -31,177 +31,213 @@ Pane { clip:true interactive: height < contentHeight - GridLayout { + ColumnLayout { id: rootLayout width: parent.width - columns: 2 Heading { - Layout.columnSpan: 2 + // Layout.columnSpan: 2 text: !channeldetails.isBackup ? qsTr('Lightning Channel') : qsTr('Channel Backup') } - Label { - visible: channeldetails.name - text: qsTr('Node name') - color: Material.accentColor - } - - Label { - visible: channeldetails.name - text: channeldetails.name - } + GridLayout { + // id: rootLayout + // width: parent.width + Layout.fillWidth: true + columns: 2 - Label { - text: qsTr('Short channel ID') - color: Material.accentColor - } + Label { + visible: channeldetails.name + text: qsTr('Node name') + color: Material.accentColor + } - Label { - text: channeldetails.short_cid - } + Label { + Layout.fillWidth: true + visible: channeldetails.name + text: channeldetails.name + } - Label { - text: qsTr('State') - color: Material.accentColor - } + Label { + text: qsTr('Short channel ID') + color: Material.accentColor + } - Label { - text: channeldetails.state - } + Label { + text: channeldetails.short_cid + } - Label { - text: qsTr('Initiator') - color: Material.accentColor - } + Label { + text: qsTr('State') + color: Material.accentColor + } - Label { - text: channeldetails.initiator - } + Label { + text: channeldetails.state + color: channeldetails.state == 'OPEN' + ? constants.colorChannelOpen + : Material.foreground + } - Label { - text: qsTr('Capacity') - color: Material.accentColor - } + Label { + text: qsTr('Initiator') + color: Material.accentColor + } - FormattedAmount { - amount: channeldetails.capacity - } + Label { + text: channeldetails.initiator + } - Label { - text: qsTr('Can send') - color: Material.accentColor - } + Label { + text: qsTr('Channel type') + color: Material.accentColor + } - RowLayout { - visible: channeldetails.isOpen - FormattedAmount { - visible: !channeldetails.frozenForSending - amount: channeldetails.canSend - singleLine: false + Label { + text: channeldetails.channelType } + Label { - visible: channeldetails.frozenForSending - text: qsTr('n/a (frozen)') + text: qsTr('Remote node ID') + Layout.columnSpan: 2 + color: Material.accentColor } - Item { + + TextHighlightPane { + Layout.columnSpan: 2 Layout.fillWidth: true - Layout.preferredHeight: 1 - } - Pane { - background: Rectangle { color: Material.dialogColor } - padding: 0 - FlatButton { - Layout.minimumWidth: implicitWidth - text: channeldetails.frozenForSending ? qsTr('Unfreeze') : qsTr('Freeze') - onClicked: channeldetails.freezeForSending() + + RowLayout { + width: parent.width + Label { + text: channeldetails.pubkey + font.pixelSize: constants.fontSizeLarge + font.family: FixedFont + Layout.fillWidth: true + wrapMode: Text.Wrap + } + ToolButton { + icon.source: '../../icons/share.png' + icon.color: 'transparent' + onClicked: { + var dialog = app.genericShareDialog.createObject(root, + { title: qsTr('Channel node ID'), text: channeldetails.pubkey } + ) + dialog.open() + } + } } } } Label { - visible: !channeldetails.isOpen - text: qsTr('n/a (channel not open)') - } - - Label { - text: qsTr('Can Receive') + text: qsTr('Capacity and ratio') color: Material.accentColor } - RowLayout { - visible: channeldetails.isOpen - FormattedAmount { - visible: !channeldetails.frozenForReceiving - amount: channeldetails.canReceive - singleLine: false - } + TextHighlightPane { + Layout.fillWidth: true + padding: constants.paddingLarge - Label { - visible: channeldetails.frozenForReceiving - text: qsTr('n/a (frozen)') - } - Item { - Layout.fillWidth: true - Layout.preferredHeight: 1 - } - Pane { - background: Rectangle { color: Material.dialogColor } - padding: 0 - FlatButton { - Layout.minimumWidth: implicitWidth - text: channeldetails.frozenForReceiving ? qsTr('Unfreeze') : qsTr('Freeze') - onClicked: channeldetails.freezeForReceiving() + GridLayout { + width: parent.width + columns: 2 + rowSpacing: constants.paddingSmall + + ChannelBar { + Layout.columnSpan: 2 + Layout.fillWidth: true + Layout.topMargin: constants.paddingLarge + Layout.bottomMargin: constants.paddingXLarge + capacity: channeldetails.capacity + localCapacity: channeldetails.localCapacity + remoteCapacity: channeldetails.remoteCapacity } - } - } - Label { - visible: !channeldetails.isOpen - text: qsTr('n/a (channel not open)') - } + Label { + text: qsTr('Capacity') + color: Material.accentColor + } - Label { - text: qsTr('Channel type') - color: Material.accentColor - } + FormattedAmount { + amount: channeldetails.capacity + } - Label { - text: channeldetails.channelType - } + Label { + text: qsTr('Can send') + color: Material.accentColor + } - Label { - text: qsTr('Remote node ID') - Layout.columnSpan: 2 - color: Material.accentColor - } + RowLayout { + visible: channeldetails.isOpen + FormattedAmount { + visible: !channeldetails.frozenForSending + amount: channeldetails.canSend + singleLine: false + } + Label { + visible: channeldetails.frozenForSending + text: qsTr('n/a (frozen)') + } + Item { + Layout.fillWidth: true + Layout.preferredHeight: 1 + } + Pane { + background: Rectangle { color: Material.dialogColor } + padding: 0 + FlatButton { + Layout.minimumWidth: implicitWidth + text: channeldetails.frozenForSending ? qsTr('Unfreeze') : qsTr('Freeze') + onClicked: channeldetails.freezeForSending() + } + } + } - TextHighlightPane { - Layout.columnSpan: 2 - Layout.fillWidth: true + Label { + visible: !channeldetails.isOpen + text: qsTr('n/a (channel not open)') + } - RowLayout { - width: parent.width Label { - text: channeldetails.pubkey - font.pixelSize: constants.fontSizeLarge - font.family: FixedFont - Layout.fillWidth: true - wrapMode: Text.Wrap + text: qsTr('Can Receive') + color: Material.accentColor } - ToolButton { - icon.source: '../../icons/share.png' - icon.color: 'transparent' - onClicked: { - var dialog = app.genericShareDialog.createObject(root, - { title: qsTr('Channel node ID'), text: channeldetails.pubkey } - ) - dialog.open() + + RowLayout { + visible: channeldetails.isOpen + FormattedAmount { + visible: !channeldetails.frozenForReceiving + amount: channeldetails.canReceive + singleLine: false + } + + Label { + visible: channeldetails.frozenForReceiving + text: qsTr('n/a (frozen)') + } + Item { + Layout.fillWidth: true + Layout.preferredHeight: 1 } + Pane { + background: Rectangle { color: Material.dialogColor } + padding: 0 + FlatButton { + Layout.minimumWidth: implicitWidth + text: channeldetails.frozenForReceiving ? qsTr('Unfreeze') : qsTr('Freeze') + onClicked: channeldetails.freezeForReceiving() + } + } + } + + Label { + visible: !channeldetails.isOpen + text: qsTr('n/a (channel not open)') } } - } + } } } diff --git a/electrum/gui/qml/components/controls/ChannelBar.qml b/electrum/gui/qml/components/controls/ChannelBar.qml new file mode 100644 index 000000000..34378158a --- /dev/null +++ b/electrum/gui/qml/components/controls/ChannelBar.qml @@ -0,0 +1,48 @@ +import QtQuick 2.6 +import QtQuick.Controls 2.0 +import QtQuick.Layouts 1.0 +import QtQuick.Controls.Material 2.0 + +import org.electrum 1.0 + +Item { + property Amount capacity + property Amount localCapacity + property Amount remoteCapacity + + height: 10 + implicitWidth: 100 + + onWidthChanged: { + var cap = capacity.satsInt * 1000 + var twocap = cap * 2 + b1.width = width * (cap - localCapacity.msatsInt) / twocap + b2.width = width * localCapacity.msatsInt / twocap + b3.width = width * remoteCapacity.msatsInt / twocap + b4.width = width * (cap - remoteCapacity.msatsInt) / twocap + } + Rectangle { + id: b1 + x: 0 + height: parent.height + color: 'gray' + } + Rectangle { + id: b2 + anchors.left: b1.right + height: parent.height + color: constants.colorLightningLocal + } + Rectangle { + id: b3 + anchors.left: b2.right + height: parent.height + color: constants.colorLightningRemote + } + Rectangle { + id: b4 + anchors.left: b3.right + height: parent.height + color: 'gray' + } +} diff --git a/electrum/gui/qml/components/controls/ChannelDelegate.qml b/electrum/gui/qml/components/controls/ChannelDelegate.qml index bbeb8e797..d02e08b3c 100644 --- a/electrum/gui/qml/components/controls/ChannelDelegate.qml +++ b/electrum/gui/qml/components/controls/ChannelDelegate.qml @@ -105,44 +105,14 @@ ItemDelegate { } } - Item { - id: chviz - visible: !_closed + ChannelBar { Layout.fillWidth: true - height: 10 - onWidthChanged: { - var cap = model.capacity.satsInt * 1000 - var twocap = cap * 2 - b1.width = width * (cap - model.local_capacity.msatsInt) / twocap - b2.width = width * model.local_capacity.msatsInt / twocap - b3.width = width * model.remote_capacity.msatsInt / twocap - b4.width = width * (cap - model.remote_capacity.msatsInt) / twocap - } - Rectangle { - id: b1 - x: 0 - height: parent.height - color: 'gray' - } - Rectangle { - id: b2 - anchors.left: b1.right - height: parent.height - color: constants.colorLightningLocal - } - Rectangle { - id: b3 - anchors.left: b2.right - height: parent.height - color: constants.colorLightningRemote - } - Rectangle { - id: b4 - anchors.left: b3.right - height: parent.height - color: 'gray' - } + visible: !_closed + capacity: model.capacity + localCapacity: model.local_capacity + remoteCapacity: model.remote_capacity } + Item { visible: _closed Layout.fillWidth: true diff --git a/electrum/gui/qml/qechanneldetails.py b/electrum/gui/qml/qechanneldetails.py index e219e3394..3c7fa0c87 100644 --- a/electrum/gui/qml/qechanneldetails.py +++ b/electrum/gui/qml/qechanneldetails.py @@ -33,6 +33,12 @@ class QEChannelDetails(QObject, QtEventListener): self._channelid = None self._channel = None + self._capacity = QEAmount() + self._local_capacity = QEAmount() + self._remote_capacity = QEAmount() + self._can_receive = QEAmount() + self._can_send = QEAmount() + self.register_callbacks() self.destroyed.connect(lambda: self.on_destroy()) @@ -101,23 +107,31 @@ class QEChannelDetails(QObject, QtEventListener): @pyqtProperty(QEAmount, notify=channelChanged) def capacity(self): - self._capacity = QEAmount(amount_sat=self._channel.get_capacity()) + self._capacity.copyFrom(QEAmount(amount_sat=self._channel.get_capacity())) return self._capacity + @pyqtProperty(QEAmount, notify=channelChanged) + def localCapacity(self): + if not self._channel.is_backup(): + self._local_capacity = QEAmount(amount_msat=self._channel.balance(LOCAL)) + return self._local_capacity + + @pyqtProperty(QEAmount, notify=channelChanged) + def remoteCapacity(self): + if not self._channel.is_backup(): + self._remote_capacity.copyFrom(QEAmount(amount_msat=self._channel.balance(REMOTE))) + return self._remote_capacity + @pyqtProperty(QEAmount, notify=channelChanged) def canSend(self): - if self._channel.is_backup(): - self._can_send = QEAmount() - else: - self._can_send = QEAmount(amount_sat=self._channel.available_to_spend(LOCAL)/1000) + if not self._channel.is_backup(): + self._can_send.copyFrom(QEAmount(amount_msat=self._channel.available_to_spend(LOCAL))) return self._can_send @pyqtProperty(QEAmount, notify=channelChanged) def canReceive(self): - if self._channel.is_backup(): - self._can_receive = QEAmount() - else: - self._can_receive = QEAmount(amount_sat=self._channel.available_to_spend(REMOTE)/1000) + if not self._channel.is_backup(): + self._can_receive.copyFrom(QEAmount(amount_msat=self._channel.available_to_spend(REMOTE))) return self._can_receive @pyqtProperty(bool, notify=channelChanged)