Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/Makefile.qt.include
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,7 @@ QML_RES_ICONS = \
qml/res/icons/blockclock-size-showcase.png \
qml/res/icons/blocktime-dark.png \
qml/res/icons/blocktime-light.png \
qml/res/icons/caret-down-medium-filled.png \
qml/res/icons/caret-left.png \
qml/res/icons/caret-right.png \
qml/res/icons/check.png \
Expand Down Expand Up @@ -402,6 +403,7 @@ QML_RES_QML = \
qml/components/ConnectionSettings.qml \
qml/components/DeveloperOptions.qml \
qml/components/ExternalPopup.qml \
qml/components/FeeSelection.qml \
qml/components/NetworkTrafficGraph.qml \
qml/components/NetworkIndicator.qml \
qml/components/OptionPopup.qml \
Expand Down
2 changes: 2 additions & 0 deletions src/qml/bitcoin_qml.qrc
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
<file>components/ConnectionSettings.qml</file>
<file>components/DeveloperOptions.qml</file>
<file>components/ExternalPopup.qml</file>
<file>components/FeeSelection.qml</file>
<file>components/NetworkTrafficGraph.qml</file>
<file>components/NetworkIndicator.qml</file>
<file>components/OptionPopup.qml</file>
Expand Down Expand Up @@ -107,6 +108,7 @@
<file alias="blocktime-dark">res/icons/blocktime-dark.png</file>
<file alias="blocktime-light">res/icons/blocktime-light.png</file>
<file alias="bitcoin">../qt/res/icons/bitcoin.png</file>
<file alias="caret-down-medium-filled">res/icons/caret-down-medium-filled.png</file>
<file alias="caret-left">res/icons/caret-left.png</file>
<file alias="caret-right">res/icons/caret-right.png</file>
<file alias="check">res/icons/check.png</file>
Expand Down
187 changes: 187 additions & 0 deletions src/qml/components/FeeSelection.qml
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
// Copyright (c) 2025 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15

import "../controls"
import "../components"

Item {
id: root

property int selectedIndex: 1
property string selectedLabel: feeModel.get(selectedIndex).feeLabel

signal feeChanged(int target)

width: parent ? parent.width : 300
height: 40

CoreText {
id: label
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
text: qsTr("Fee")
font.pixelSize: 15
}

Button {
id: dropDownButton
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
text: root.selectedLabel
font.pixelSize: 15

hoverEnabled: true

HoverHandler {
cursorShape: Qt.PointingHandCursor
}

onPressed: feePopup.open()

contentItem: RowLayout {
spacing: 5
anchors.centerIn: parent

CoreText {
id: value
text: root.selectedLabel
font.pixelSize: 15

Behavior on color {
ColorAnimation { duration: 150 }
}
}

Icon {
id: caret
source: "image://images/caret-down-medium-filled"
Layout.preferredWidth: 30
size: 30
color: dropDownButton.enabled ? Theme.color.orange : Theme.color.neutral4

Behavior on color {
ColorAnimation { duration: 150 }
}
}
}

background: Rectangle {
id: dropDownButtonBg
color: Theme.color.background
radius: 6
Behavior on color {
ColorAnimation { duration: 150 }
}
}

states: [
State {
name: "CHECKED"; when: dropDownButton.checked
PropertyChanges { target: icon; color: activeColor }
},
State {
name: "HOVER"; when: dropDownButton.hovered
PropertyChanges { target: dropDownButtonBg; color: Theme.color.neutral2 }
},
State {
name: "DISABLED"; when: !dropDownButton.enabled
PropertyChanges { target: dropDownButtonBg; color: Theme.color.background }
}
]
}

Popup {
id: feePopup
modal: true
dim: false

background: Rectangle {
color: Theme.color.background
radius: 6
border.color: Theme.color.neutral3
}

width: 260
height: Math.min(feeModel.count * 40 + 20, 300)
x: parent.width - width
y: parent.height

contentItem: ListView {
id: feeList
model: feeModel
interactive: false
width: 260
height: contentHeight
delegate: ItemDelegate {
required property string feeLabel
required property int index
required property int target

width: ListView.view.width
height: 40

background: Rectangle {
width: parent.width - 4
height: parent.height - 4
radius: 6
color: Theme.color.neutral3
visible: mouseArea.containsMouse
}

RowLayout {
anchors.fill: parent
anchors.margins: 12
spacing: 10

CoreText {
text: feeLabel
font.pixelSize: 15
}

Item { Layout.fillWidth: true }

Icon {
visible: index === root.selectedIndex
source: "image://images/check"
color: Theme.color.orange
size: 20
}
}

MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
root.selectedIndex = index
root.selectedLabel = feeLabel
root.feeChanged(target)
feePopup.close()
}
}

Rectangle {
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
height: 1
color: Theme.color.neutral3
visible: index < feeModel.count - 1
}
}
}
}

ListModel {
id: feeModel
ListElement { feeLabel: qsTr("High (~10 mins)"); target: 1 }
ListElement { feeLabel: qsTr("Default (~60 mins)"); target: 6 }
ListElement { feeLabel: qsTr("Low (~24 hrs)"); target: 144 }
}
}
13 changes: 13 additions & 0 deletions src/qml/models/walletqmlmodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -205,3 +205,16 @@ std::vector<COutPoint> WalletQmlModel::listSelectedCoins() const
{
return m_coin_control.ListSelected();
}

unsigned int WalletQmlModel::feeTargetBlocks() const
{
return m_coin_control.m_confirm_target.value_or(wallet::DEFAULT_TX_CONFIRM_TARGET);
}

void WalletQmlModel::setFeeTargetBlocks(unsigned int target_blocks)
{
if (m_coin_control.m_confirm_target != target_blocks) {
m_coin_control.m_confirm_target = target_blocks;
Q_EMIT feeTargetBlocksChanged();
}
}
4 changes: 4 additions & 0 deletions src/qml/models/walletqmlmodel.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ class WalletQmlModel : public QObject
Q_PROPERTY(CoinsListModel* coinsListModel READ coinsListModel CONSTANT)
Q_PROPERTY(SendRecipient* sendRecipient READ sendRecipient CONSTANT)
Q_PROPERTY(WalletQmlModelTransaction* currentTransaction READ currentTransaction NOTIFY currentTransactionChanged)
Q_PROPERTY(unsigned int targetBlocks READ feeTargetBlocks WRITE setFeeTargetBlocks NOTIFY feeTargetBlocksChanged)

public:
WalletQmlModel(std::unique_ptr<interfaces::Wallet> wallet, QObject* parent = nullptr);
Expand Down Expand Up @@ -63,11 +64,14 @@ class WalletQmlModel : public QObject
void unselectCoin(const COutPoint& output);
bool isSelectedCoin(const COutPoint& output);
std::vector<COutPoint> listSelectedCoins() const;
unsigned int feeTargetBlocks() const;
void setFeeTargetBlocks(unsigned int target_blocks);

Q_SIGNALS:
void nameChanged();
void balanceChanged();
void currentTransactionChanged();
void feeTargetBlocksChanged();

private:
std::unique_ptr<interfaces::Wallet> m_wallet;
Expand Down
21 changes: 5 additions & 16 deletions src/qml/pages/wallet/Send.qml
Original file line number Diff line number Diff line change
Expand Up @@ -199,23 +199,12 @@ PageStack {
Layout.fillWidth: true
}

Item {
height: feeLabel.height + feeValue.height
FeeSelection {
id: feeSelection
Layout.fillWidth: true
CoreText {
id: feeLabel
anchors.left: parent.left
anchors.top: parent.top
text: "Fee"
font.pixelSize: 15
}

CoreText {
id: feeValue
anchors.right: parent.right
anchors.top: parent.top
text: qsTr("Default (~2,000 sats)")
font.pixelSize: 15
Layout.preferredHeight: 50
onFeeChanged: {
root.wallet.targetBlocks = target
}
}

Expand Down
Binary file added src/qml/res/icons/caret-down-medium-filled.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions src/qml/res/src/caret-down-medium-filled.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading