diff --git a/electrum/gui/qml/components/CpfpBumpFeeDialog.qml b/electrum/gui/qml/components/CpfpBumpFeeDialog.qml index c60084f87..a81c235a6 100644 --- a/electrum/gui/qml/components/CpfpBumpFeeDialog.qml +++ b/electrum/gui/qml/components/CpfpBumpFeeDialog.qml @@ -26,185 +26,198 @@ ElDialog { anchors.fill: parent spacing: 0 - GridLayout { - Layout.preferredWidth: parent.width - Layout.leftMargin: constants.paddingLarge - Layout.rightMargin: constants.paddingLarge - columns: 2 - - Label { - Layout.columnSpan: 2 - Layout.fillWidth: true - text: qsTr('A CPFP is a transaction that sends an unconfirmed output back to yourself, with a high fee. The goal is to have miners confirm the parent transaction in order to get the fee attached to the child transaction.') - wrapMode: Text.Wrap - } + Flickable { + Layout.fillWidth: true + Layout.fillHeight: true - Label { - Layout.columnSpan: 2 - Layout.fillWidth: true - Layout.bottomMargin: constants.paddingLarge - text: qsTr('The proposed fee is computed using your fee/kB settings, applied to the total size of both child and parent transactions. After you broadcast a CPFP transaction, it is normal to see a new unconfirmed transaction in your history.') - wrapMode: Text.Wrap - } + leftMargin: constants.paddingLarge + rightMargin: constants.paddingLarge - Label { - Layout.preferredWidth: 1 - Layout.fillWidth: true - text: qsTr('Total size') - color: Material.accentColor - } + contentHeight: rootLayout.height + clip: true + interactive: height < contentHeight - Label { - Layout.preferredWidth: 1 - Layout.fillWidth: true - text: qsTr('%1 bytes').arg(cpfpfeebumper.totalSize) - } + GridLayout { + id: rootLayout + width: parent.width - Label { - text: qsTr('Input amount') - color: Material.accentColor - } + columns: 2 - FormattedAmount { - amount: cpfpfeebumper.inputAmount - } + Label { + Layout.columnSpan: 2 + Layout.fillWidth: true + text: qsTr('A CPFP is a transaction that sends an unconfirmed output back to yourself, with a high fee. The goal is to have miners confirm the parent transaction in order to get the fee attached to the child transaction.') + wrapMode: Text.Wrap + } - Label { - text: qsTr('Output amount') - color: Material.accentColor - } + Label { + Layout.columnSpan: 2 + Layout.fillWidth: true + Layout.bottomMargin: constants.paddingLarge + text: qsTr('The proposed fee is computed using your fee/kB settings, applied to the total size of both child and parent transactions. After you broadcast a CPFP transaction, it is normal to see a new unconfirmed transaction in your history.') + wrapMode: Text.Wrap + } - FormattedAmount { - amount: cpfpfeebumper.outputAmount - valid: cpfpfeebumper.valid - } + Label { + Layout.preferredWidth: 1 + Layout.fillWidth: true + text: qsTr('Total size') + color: Material.accentColor + } - Slider { - id: feeslider - leftPadding: constants.paddingMedium - snapMode: Slider.SnapOnRelease - stepSize: 1 - from: 0 - to: cpfpfeebumper.sliderSteps - onValueChanged: { - if (activeFocus) - cpfpfeebumper.sliderPos = value - } - Component.onCompleted: { - value = cpfpfeebumper.sliderPos - } - Connections { - target: cpfpfeebumper - function onSliderPosChanged() { - feeslider.value = cpfpfeebumper.sliderPos - } + Label { + Layout.preferredWidth: 1 + Layout.fillWidth: true + text: qsTr('%1 bytes').arg(cpfpfeebumper.totalSize) } - } - FeeMethodComboBox { - id: feemethod - feeslider: cpfpfeebumper - } + Label { + text: qsTr('Input amount') + color: Material.accentColor + } - Label { - visible: feemethod.currentValue - text: qsTr('Target') - color: Material.accentColor - } + FormattedAmount { + amount: cpfpfeebumper.inputAmount + } - Label { - visible: feemethod.currentValue - text: cpfpfeebumper.target - } + Label { + text: qsTr('Output amount') + color: Material.accentColor + } - Label { - text: qsTr('Fee for child') - color: Material.accentColor - } + FormattedAmount { + amount: cpfpfeebumper.outputAmount + valid: cpfpfeebumper.valid + } - FormattedAmount { - amount: cpfpfeebumper.feeForChild - valid: cpfpfeebumper.valid - } + RowLayout { + Layout.columnSpan: 2 + Slider { + id: feeslider + leftPadding: constants.paddingMedium + snapMode: Slider.SnapOnRelease + stepSize: 1 + from: 0 + to: cpfpfeebumper.sliderSteps + onValueChanged: { + if (activeFocus) + cpfpfeebumper.sliderPos = value + } + Component.onCompleted: { + value = cpfpfeebumper.sliderPos + } + Connections { + target: cpfpfeebumper + function onSliderPosChanged() { + feeslider.value = cpfpfeebumper.sliderPos + } + } + } - Label { - text: qsTr('Total fee') - color: Material.accentColor - } + FeeMethodComboBox { + id: feemethod + feeslider: cpfpfeebumper + } + } - FormattedAmount { - amount: cpfpfeebumper.totalFee - valid: cpfpfeebumper.valid - } + Label { + visible: feemethod.currentValue + text: qsTr('Target') + color: Material.accentColor + } - Label { - text: qsTr('Total fee rate') - color: Material.accentColor - } + Label { + visible: feemethod.currentValue + text: cpfpfeebumper.target + } - RowLayout { Label { - text: cpfpfeebumper.valid ? cpfpfeebumper.totalFeeRate : '' - font.family: FixedFont + text: qsTr('Fee for child') + color: Material.accentColor + } + + FormattedAmount { + amount: cpfpfeebumper.feeForChild + valid: cpfpfeebumper.valid } Label { - visible: cpfpfeebumper.valid - text: 'sat/vB' + text: qsTr('Total fee') color: Material.accentColor } - } - InfoTextArea { - Layout.columnSpan: 2 - Layout.preferredWidth: parent.width * 3/4 - Layout.alignment: Qt.AlignHCenter - Layout.topMargin: constants.paddingLarge - visible: cpfpfeebumper.warning != '' - text: cpfpfeebumper.warning - iconStyle: InfoTextArea.IconStyle.Warn - } + FormattedAmount { + amount: cpfpfeebumper.totalFee + valid: cpfpfeebumper.valid + } - Label { - visible: cpfpfeebumper.valid - text: qsTr('Outputs') - Layout.columnSpan: 2 - color: Material.accentColor - } + Label { + text: qsTr('Total fee rate') + color: Material.accentColor + } + + RowLayout { + Label { + text: cpfpfeebumper.valid ? cpfpfeebumper.totalFeeRate : '' + font.family: FixedFont + } - Repeater { - model: cpfpfeebumper.valid ? cpfpfeebumper.outputs : [] - delegate: TextHighlightPane { + Label { + visible: cpfpfeebumper.valid + text: 'sat/vB' + color: Material.accentColor + } + } + + InfoTextArea { Layout.columnSpan: 2 - Layout.fillWidth: true + Layout.preferredWidth: parent.width * 3/4 + Layout.alignment: Qt.AlignHCenter + Layout.topMargin: constants.paddingLarge + visible: cpfpfeebumper.warning != '' + text: cpfpfeebumper.warning + iconStyle: InfoTextArea.IconStyle.Warn + } - RowLayout { - width: parent.width - Label { - text: modelData.address - Layout.fillWidth: true - wrapMode: Text.Wrap - font.pixelSize: constants.fontSizeLarge - font.family: FixedFont - color: modelData.is_mine ? constants.colorMine : Material.foreground - } - Label { - text: Config.formatSats(modelData.value_sats) - font.pixelSize: constants.fontSizeMedium - font.family: FixedFont - } - Label { - text: Config.baseUnit - font.pixelSize: constants.fontSizeMedium - color: Material.accentColor + Label { + visible: cpfpfeebumper.valid + text: qsTr('Outputs') + Layout.columnSpan: 2 + color: Material.accentColor + } + + Repeater { + model: cpfpfeebumper.valid ? cpfpfeebumper.outputs : [] + delegate: TextHighlightPane { + Layout.columnSpan: 2 + Layout.fillWidth: true + + RowLayout { + width: parent.width + Label { + text: modelData.address + Layout.fillWidth: true + wrapMode: Text.Wrap + font.pixelSize: constants.fontSizeLarge + font.family: FixedFont + color: modelData.is_mine ? constants.colorMine : Material.foreground + } + Label { + text: Config.formatSats(modelData.value_sats) + font.pixelSize: constants.fontSizeMedium + font.family: FixedFont + } + Label { + text: Config.baseUnit + font.pixelSize: constants.fontSizeMedium + color: Material.accentColor + } } } } } } - Item { Layout.fillHeight: true; Layout.preferredWidth: 1 } - FlatButton { id: sendButton Layout.fillWidth: true diff --git a/electrum/gui/qml/components/RbfBumpFeeDialog.qml b/electrum/gui/qml/components/RbfBumpFeeDialog.qml index e5bea81bb..b1d406c3d 100644 --- a/electrum/gui/qml/components/RbfBumpFeeDialog.qml +++ b/electrum/gui/qml/components/RbfBumpFeeDialog.qml @@ -26,201 +26,212 @@ ElDialog { anchors.fill: parent spacing: 0 - GridLayout { - Layout.preferredWidth: parent.width - Layout.leftMargin: constants.paddingLarge - Layout.rightMargin: constants.paddingLarge - columns: 2 - - Label { - Layout.columnSpan: 2 - Layout.fillWidth: true - text: qsTr('Increase your transaction\'s fee to improve its position in the mempool') - wrapMode: Text.Wrap - } + Flickable { + Layout.fillWidth: true + Layout.fillHeight: true - Label { - Layout.preferredWidth: 1 - Layout.fillWidth: true - text: qsTr('Method') - color: Material.accentColor - } + leftMargin: constants.paddingLarge + rightMargin: constants.paddingLarge - RowLayout { - Layout.preferredWidth: 1 - Layout.fillWidth: true - ElComboBox { - enabled: rbffeebumper.canChangeBumpMethod - - textRole: 'text' - valueRole: 'value' - - model: [ - { text: qsTr('Preserve payment'), value: 'preserve_payment' }, - { text: qsTr('Decrease payment'), value: 'decrease_payment' } - ] - onCurrentValueChanged: { - if (activeFocus) - rbffeebumper.bumpMethod = currentValue - } - Component.onCompleted: { - currentIndex = indexOfValue(rbffeebumper.bumpMethod) - } - } - Item { Layout.fillWidth: true; Layout.preferredHeight: 1 } - } + contentHeight: rootLayout.height + clip: true + interactive: height < contentHeight - Label { - Layout.preferredWidth: 1 - Layout.fillWidth: true - text: qsTr('Old fee') - color: Material.accentColor - } + GridLayout { + id: rootLayout - FormattedAmount { - Layout.preferredWidth: 1 - Layout.fillWidth: true - amount: rbffeebumper.oldfee - } + width: parent.width - Label { - text: qsTr('Old fee rate') - color: Material.accentColor - } + columns: 2 - RowLayout { Label { - id: oldfeeRate - text: rbffeebumper.oldfeeRate - font.family: FixedFont + Layout.columnSpan: 2 + Layout.fillWidth: true + text: qsTr('Increase your transaction\'s fee to improve its position in the mempool') + wrapMode: Text.Wrap } Label { - text: 'sat/vB' + Layout.preferredWidth: 1 + Layout.fillWidth: true + text: qsTr('Method') color: Material.accentColor } - } - Label { - text: qsTr('Mining fee') - color: Material.accentColor - } + RowLayout { + Layout.preferredWidth: 1 + Layout.fillWidth: true + Layout.minimumWidth: bumpMethodComboBox.implicitWidth + + ElComboBox { + id: bumpMethodComboBox + enabled: rbffeebumper.canChangeBumpMethod + + textRole: 'text' + valueRole: 'value' + + model: [ + { text: qsTr('Preserve payment'), value: 'preserve_payment' }, + { text: qsTr('Decrease payment'), value: 'decrease_payment' } + ] + onCurrentValueChanged: { + if (activeFocus) + rbffeebumper.bumpMethod = currentValue + } + Component.onCompleted: { + currentIndex = indexOfValue(rbffeebumper.bumpMethod) + } + } + Item { Layout.fillWidth: true; Layout.preferredHeight: 1 } + } - FormattedAmount { - amount: rbffeebumper.fee - valid: rbffeebumper.valid - } + Label { + Layout.preferredWidth: 1 + Layout.fillWidth: true + text: qsTr('Old fee') + color: Material.accentColor + } - Label { - text: qsTr('Fee rate') - color: Material.accentColor - } + FormattedAmount { + Layout.preferredWidth: 1 + Layout.fillWidth: true + amount: rbffeebumper.oldfee + } - RowLayout { Label { - id: feeRate - text: rbffeebumper.valid ? rbffeebumper.feeRate : '' - font.family: FixedFont + text: qsTr('Old fee rate') + color: Material.accentColor + } + + RowLayout { + Label { + id: oldfeeRate + text: rbffeebumper.oldfeeRate + font.family: FixedFont + } + + Label { + text: 'sat/vB' + color: Material.accentColor + } } Label { - visible: rbffeebumper.valid - text: 'sat/vB' + text: qsTr('Mining fee') color: Material.accentColor } - } - Label { - text: qsTr('Target') - color: Material.accentColor - } + FormattedAmount { + amount: rbffeebumper.fee + valid: rbffeebumper.valid + } - Label { - id: targetdesc - text: rbffeebumper.target - } + Label { + text: qsTr('Fee rate') + color: Material.accentColor + } - RowLayout { - Layout.columnSpan: 2 - Layout.fillWidth: true - Slider { - id: feeslider - Layout.fillWidth: true - leftPadding: constants.paddingMedium - snapMode: Slider.SnapOnRelease - stepSize: 1 - from: 0 - to: rbffeebumper.sliderSteps - onValueChanged: { - if (activeFocus) - rbffeebumper.sliderPos = value + RowLayout { + Label { + id: feeRate + text: rbffeebumper.valid ? rbffeebumper.feeRate : '' + font.family: FixedFont } - Component.onCompleted: { - value = rbffeebumper.sliderPos - } - Connections { - target: rbffeebumper - function onSliderPosChanged() { - feeslider.value = rbffeebumper.sliderPos - } + + Label { + visible: rbffeebumper.valid + text: 'sat/vB' + color: Material.accentColor } } - FeeMethodComboBox { - id: target - feeslider: rbffeebumper + Label { + text: qsTr('Target') + color: Material.accentColor } - } - InfoTextArea { - Layout.columnSpan: 2 - Layout.preferredWidth: parent.width * 3/4 - Layout.alignment: Qt.AlignHCenter - visible: rbffeebumper.warning != '' - text: rbffeebumper.warning - iconStyle: InfoTextArea.IconStyle.Warn - } + Label { + id: targetdesc + text: rbffeebumper.target + } - Label { - visible: rbffeebumper.valid - text: qsTr('Outputs') - Layout.columnSpan: 2 - color: Material.accentColor - } + RowLayout { + Layout.columnSpan: 2 + Slider { + id: feeslider + leftPadding: constants.paddingMedium + snapMode: Slider.SnapOnRelease + stepSize: 1 + from: 0 + to: rbffeebumper.sliderSteps + onValueChanged: { + if (activeFocus) + rbffeebumper.sliderPos = value + } + Component.onCompleted: { + value = rbffeebumper.sliderPos + } + Connections { + target: rbffeebumper + function onSliderPosChanged() { + feeslider.value = rbffeebumper.sliderPos + } + } + } - Repeater { - model: rbffeebumper.valid ? rbffeebumper.outputs : [] - delegate: TextHighlightPane { + FeeMethodComboBox { + id: target + feeslider: rbffeebumper + } + } + + InfoTextArea { Layout.columnSpan: 2 Layout.fillWidth: true + visible: rbffeebumper.warning != '' + text: rbffeebumper.warning + iconStyle: InfoTextArea.IconStyle.Warn + } - RowLayout { - width: parent.width - Label { - text: modelData.address - Layout.fillWidth: true - wrapMode: Text.Wrap - font.pixelSize: constants.fontSizeLarge - font.family: FixedFont - color: modelData.is_mine ? constants.colorMine : Material.foreground - } - Label { - text: Config.formatSats(modelData.value_sats) - font.pixelSize: constants.fontSizeMedium - font.family: FixedFont - } - Label { - text: Config.baseUnit - font.pixelSize: constants.fontSizeMedium - color: Material.accentColor + Label { + visible: rbffeebumper.valid + text: qsTr('Outputs') + Layout.columnSpan: 2 + color: Material.accentColor + } + + Repeater { + model: rbffeebumper.valid ? rbffeebumper.outputs : [] + delegate: TextHighlightPane { + Layout.columnSpan: 2 + Layout.fillWidth: true + + RowLayout { + width: parent.width + Label { + text: modelData.address + Layout.fillWidth: true + wrapMode: Text.Wrap + font.pixelSize: constants.fontSizeLarge + font.family: FixedFont + color: modelData.is_mine ? constants.colorMine : Material.foreground + } + Label { + text: Config.formatSats(modelData.value_sats) + font.pixelSize: constants.fontSizeMedium + font.family: FixedFont + } + Label { + text: Config.baseUnit + font.pixelSize: constants.fontSizeMedium + color: Material.accentColor + } } } } } } - Item { Layout.fillHeight: true; Layout.preferredWidth: 1 } - FlatButton { id: sendButton Layout.fillWidth: true diff --git a/electrum/gui/qml/components/RbfCancelDialog.qml b/electrum/gui/qml/components/RbfCancelDialog.qml index 1f561aa88..e083a04e5 100644 --- a/electrum/gui/qml/components/RbfCancelDialog.qml +++ b/electrum/gui/qml/components/RbfCancelDialog.qml @@ -25,173 +25,183 @@ ElDialog { anchors.fill: parent spacing: 0 - GridLayout { + Flickable { Layout.fillWidth: true - Layout.leftMargin: constants.paddingLarge - Layout.rightMargin: constants.paddingLarge + Layout.fillHeight: true - columns: 2 + leftMargin: constants.paddingLarge + rightMargin: constants.paddingLarge - Label { - Layout.columnSpan: 2 - Layout.fillWidth: true - text: qsTr('Cancel an unconfirmed RBF transaction by double-spending its inputs back to your wallet with a higher fee.') - wrapMode: Text.Wrap - } - - Label { - text: qsTr('Old fee') - color: Material.accentColor - } - - FormattedAmount { - amount: txcanceller.oldfee - } + contentHeight: rootLayout.height + clip: true + interactive: height < contentHeight - Label { - text: qsTr('Old fee rate') - color: Material.accentColor - } + GridLayout { + id: rootLayout + width: parent.width + columns: 2 - RowLayout { Label { - id: oldfeeRate - text: txcanceller.oldfeeRate - font.family: FixedFont + Layout.columnSpan: 2 + Layout.fillWidth: true + text: qsTr('Cancel an unconfirmed RBF transaction by double-spending its inputs back to your wallet with a higher fee.') + wrapMode: Text.Wrap } Label { - text: 'sat/vB' + text: qsTr('Old fee') color: Material.accentColor } - } - Label { - text: qsTr('Mining fee') - color: Material.accentColor - } + FormattedAmount { + amount: txcanceller.oldfee + } - FormattedAmount { - amount: txcanceller.fee - valid: txcanceller.valid - } + Label { + text: qsTr('Old fee rate') + color: Material.accentColor + } - Label { - text: qsTr('Fee rate') - color: Material.accentColor - } + RowLayout { + Label { + id: oldfeeRate + text: txcanceller.oldfeeRate + font.family: FixedFont + } - RowLayout { - Label { - id: feeRate - text: txcanceller.valid ? txcanceller.feeRate : '' - font.family: FixedFont + Label { + text: 'sat/vB' + color: Material.accentColor + } } Label { - visible: txcanceller.valid - text: 'sat/vB' + text: qsTr('Mining fee') color: Material.accentColor } - } - Label { - text: qsTr('Target') - color: Material.accentColor - } + FormattedAmount { + amount: txcanceller.fee + valid: txcanceller.valid + } - Label { - id: targetdesc - text: txcanceller.target - } + Label { + text: qsTr('Fee rate') + color: Material.accentColor + } - Slider { - id: feeslider - leftPadding: constants.paddingMedium - snapMode: Slider.SnapOnRelease - stepSize: 1 - from: 0 - to: txcanceller.sliderSteps - onValueChanged: { - if (activeFocus) - txcanceller.sliderPos = value - } - Component.onCompleted: { - value = txcanceller.sliderPos - } - Connections { - target: txcanceller - function onSliderPosChanged() { - feeslider.value = txcanceller.sliderPos + RowLayout { + Label { + id: feeRate + text: txcanceller.valid ? txcanceller.feeRate : '' + font.family: FixedFont + } + + Label { + visible: txcanceller.valid + text: 'sat/vB' + color: Material.accentColor } } - } - FeeMethodComboBox { - id: target - feeslider: txcanceller - } + Label { + text: qsTr('Target') + color: Material.accentColor + } - CheckBox { - id: final_cb - text: qsTr('Replace-by-Fee') - Layout.columnSpan: 2 - checked: txcanceller.rbf - onCheckedChanged: { - if (activeFocus) - txcanceller.rbf = checked + Label { + id: targetdesc + text: txcanceller.target } - } - InfoTextArea { - Layout.columnSpan: 2 - Layout.preferredWidth: parent.width * 3/4 - Layout.alignment: Qt.AlignHCenter - visible: txcanceller.warning != '' - text: txcanceller.warning - iconStyle: InfoTextArea.IconStyle.Warn - } + RowLayout { + Layout.columnSpan: 2 + Slider { + id: feeslider + leftPadding: constants.paddingMedium + snapMode: Slider.SnapOnRelease + stepSize: 1 + from: 0 + to: txcanceller.sliderSteps + onValueChanged: { + if (activeFocus) + txcanceller.sliderPos = value + } + Component.onCompleted: { + value = txcanceller.sliderPos + } + Connections { + target: txcanceller + function onSliderPosChanged() { + feeslider.value = txcanceller.sliderPos + } + } + } - Label { - visible: txcanceller.valid - text: qsTr('Outputs') - Layout.columnSpan: 2 - color: Material.accentColor - } + FeeMethodComboBox { + id: target + feeslider: txcanceller + } + } - Repeater { - model: txcanceller.valid ? txcanceller.outputs : [] - delegate: TextHighlightPane { + CheckBox { + id: final_cb + text: qsTr('Replace-by-Fee') + Layout.columnSpan: 2 + checked: txcanceller.rbf + onCheckedChanged: { + if (activeFocus) + txcanceller.rbf = checked + } + } + + InfoTextArea { Layout.columnSpan: 2 Layout.fillWidth: true + visible: txcanceller.warning != '' + text: txcanceller.warning + iconStyle: InfoTextArea.IconStyle.Warn + } - RowLayout { - width: parent.width - Label { - text: modelData.address - Layout.fillWidth: true - wrapMode: Text.Wrap - font.pixelSize: constants.fontSizeLarge - font.family: FixedFont - color: modelData.is_mine ? constants.colorMine : Material.foreground - } - Label { - text: Config.formatSats(modelData.value_sats) - font.pixelSize: constants.fontSizeMedium - font.family: FixedFont - } - Label { - text: Config.baseUnit - font.pixelSize: constants.fontSizeMedium - color: Material.accentColor + Label { + visible: txcanceller.valid + text: qsTr('Outputs') + Layout.columnSpan: 2 + color: Material.accentColor + } + + Repeater { + model: txcanceller.valid ? txcanceller.outputs : [] + delegate: TextHighlightPane { + Layout.columnSpan: 2 + Layout.fillWidth: true + + RowLayout { + width: parent.width + Label { + text: modelData.address + Layout.fillWidth: true + wrapMode: Text.Wrap + font.pixelSize: constants.fontSizeLarge + font.family: FixedFont + color: modelData.is_mine ? constants.colorMine : Material.foreground + } + Label { + text: Config.formatSats(modelData.value_sats) + font.pixelSize: constants.fontSizeMedium + font.family: FixedFont + } + Label { + text: Config.baseUnit + font.pixelSize: constants.fontSizeMedium + color: Material.accentColor + } } } } } } - Item { Layout.fillHeight: true; Layout.preferredWidth: 1 } - FlatButton { id: confirmButton Layout.fillWidth: true