From 37b16f8f4b3a8e7f28ffd588d683a61fe6c44892 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Fri, 20 Feb 2026 14:06:30 +0530 Subject: [PATCH 1/7] interfaces: add unique voter count reporting --- src/governance/object.cpp | 21 +++++++++++++++++++++ src/governance/object.h | 7 +++++++ src/interfaces/node.h | 5 +++++ src/node/interfaces.cpp | 14 ++++++++++++++ 4 files changed, 47 insertions(+) diff --git a/src/governance/object.cpp b/src/governance/object.cpp index 5d78d10dd348..fc99fa4d559e 100644 --- a/src/governance/object.cpp +++ b/src/governance/object.cpp @@ -592,6 +592,27 @@ int CGovernanceObject::GetAbstainCount(const CDeterministicMNList& tip_mn_list, return CountMatchingVotes(tip_mn_list, eVoteSignalIn, VOTE_OUTCOME_ABSTAIN); } +CGovernanceObject::UniqueVoterCount CGovernanceObject::GetUniqueVoterCount(const CDeterministicMNList& tip_mn_list, vote_signal_enum_t eVoteSignalIn) const +{ + LOCK(cs); + UniqueVoterCount result; + for (const auto& [outpoint, recVote] : mapCurrentMNVotes) { + if (recVote.mapInstances.count(eVoteSignalIn) == 0) { + continue; + } + auto dmn = tip_mn_list.GetMNByCollateral(outpoint); + if (!dmn) { + continue; + } + if (dmn->nType == MnType::Evo) { + ++result.m_evo; + } else { + ++result.m_regular; + } + } + return result; +} + bool CGovernanceObject::GetCurrentMNVotes(const COutPoint& mnCollateralOutpoint, vote_rec_t& voteRecord) const { LOCK(cs); diff --git a/src/governance/object.h b/src/governance/object.h index 05eb940bf275..2f2443915835 100644 --- a/src/governance/object.h +++ b/src/governance/object.h @@ -227,6 +227,13 @@ class CGovernanceObject int GetAbstainCount(const CDeterministicMNList& tip_mn_list, vote_signal_enum_t eVoteSignalIn) const EXCLUSIVE_LOCKS_REQUIRED(!cs); + struct UniqueVoterCount { + uint16_t m_regular{0}; + uint16_t m_evo{0}; + }; + UniqueVoterCount GetUniqueVoterCount(const CDeterministicMNList& tip_mn_list, vote_signal_enum_t eVoteSignalIn) const + EXCLUSIVE_LOCKS_REQUIRED(!cs); + bool GetCurrentMNVotes(const COutPoint& mnCollateralOutpoint, vote_rec_t& voteRecord) const EXCLUSIVE_LOCKS_REQUIRED(!cs); diff --git a/src/interfaces/node.h b/src/interfaces/node.h index e91b69e310f4..2baecfb39ca7 100644 --- a/src/interfaces/node.h +++ b/src/interfaces/node.h @@ -143,6 +143,11 @@ class GOV int32_t m_yes{0}; }; virtual Votes getObjVotes(const CGovernanceObject& obj, vote_signal_enum_t vote_signal) = 0; + struct UniqueVoters { + uint16_t m_regular{0}; + uint16_t m_evo{0}; + }; + virtual UniqueVoters getObjUniqueVoters(const CGovernanceObject& obj, vote_signal_enum_t vote_signal) = 0; virtual bool getObjLocalValidity(const CGovernanceObject& obj, std::string& error, bool check_collateral) = 0; virtual bool existsObj(const uint256& hash) = 0; virtual bool isEnabled() = 0; diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp index 8db8a2c610e5..975af2e68e4d 100644 --- a/src/node/interfaces.cpp +++ b/src/node/interfaces.cpp @@ -244,6 +244,20 @@ class GOVImpl : public GOV } return ret; } + UniqueVoters getObjUniqueVoters(const CGovernanceObject& obj, vote_signal_enum_t vote_signal) override + { + if (context().govman != nullptr && context().dmnman != nullptr) { + const auto& tip_mn_list{context().dmnman->GetListAtChainTip()}; + if (auto govobj{context().govman->FindGovernanceObject(obj.GetHash())}) { + const auto count = govobj->GetUniqueVoterCount(tip_mn_list, vote_signal); + return {.m_regular = count.m_regular, .m_evo = count.m_evo}; + } else { + const auto count = obj.GetUniqueVoterCount(tip_mn_list, vote_signal); + return {.m_regular = count.m_regular, .m_evo = count.m_evo}; + } + } + return {0, 0}; + } bool existsObj(const uint256& hash) override { if (context().govman != nullptr) { From 00052096446b67d39b55c10c8eea9e9ff92bc587 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Tue, 24 Feb 2026 15:20:53 +0530 Subject: [PATCH 2/7] feat(qt): report proposal information --- src/Makefile.qt.include | 4 + src/qt/clientfeeds.cpp | 4 + src/qt/clientfeeds.h | 3 + src/qt/forms/debugwindow.ui | 20 ++ src/qt/forms/proposalinfo.ui | 310 ++++++++++++++++++++++++++++++ src/qt/proposalinfo.cpp | 165 ++++++++++++++++ src/qt/proposalinfo.h | 45 +++++ src/qt/res/css/general.css | 6 +- src/qt/rpcconsole.cpp | 13 ++ src/qt/rpcconsole.h | 1 + test/util/data/non-backported.txt | 1 + 11 files changed, 570 insertions(+), 2 deletions(-) create mode 100644 src/qt/forms/proposalinfo.ui create mode 100644 src/qt/proposalinfo.cpp create mode 100644 src/qt/proposalinfo.h diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index 070f011f42f9..ed9ae5bada54 100644 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -34,6 +34,7 @@ QT_FORMS_UI = \ qt/forms/optionsdialog.ui \ qt/forms/overviewpage.ui \ qt/forms/proposalcreate.ui \ + qt/forms/proposalinfo.ui \ qt/forms/proposallist.ui \ qt/forms/proposalresume.ui \ qt/forms/psbtoperationsdialog.ui \ @@ -83,6 +84,7 @@ QT_MOC_CPP = \ qt/moc_peertablemodel.cpp \ qt/moc_peertablesortproxy.cpp \ qt/moc_proposalcreate.cpp \ + qt/moc_proposalinfo.cpp \ qt/moc_proposallist.cpp \ qt/moc_proposalmodel.cpp \ qt/moc_proposalresume.cpp \ @@ -170,6 +172,7 @@ BITCOIN_QT_H = \ qt/peertablemodel.h \ qt/peertablesortproxy.h \ qt/proposalcreate.h \ + qt/proposalinfo.h \ qt/proposallist.h \ qt/proposalmodel.h \ qt/proposalresume.h \ @@ -272,6 +275,7 @@ BITCOIN_QT_BASE_CPP = \ qt/optionsmodel.cpp \ qt/peertablemodel.cpp \ qt/peertablesortproxy.cpp \ + qt/proposalinfo.cpp \ qt/qvalidatedlineedit.cpp \ qt/qvaluecombobox.cpp \ qt/rpcconsole.cpp \ diff --git a/src/qt/clientfeeds.cpp b/src/qt/clientfeeds.cpp index c9dad9391fcb..95ec4da26e12 100644 --- a/src/qt/clientfeeds.cpp +++ b/src/qt/clientfeeds.cpp @@ -231,10 +231,14 @@ void ProposalFeed::fetch() } ret->m_proposals.emplace_back(std::make_shared(m_client_model, govObj, ret->m_gov_info, ret->m_gov_info.requiredConfs, /*is_broadcast=*/true)); + const auto voters = m_client_model.node().gov().getObjUniqueVoters(govObj, VOTE_SIGNAL_FUNDING); + ret->m_max_regular_voters = std::max(ret->m_max_regular_voters, voters.m_regular); + ret->m_max_evo_voters = std::max(ret->m_max_evo_voters, voters.m_evo); } auto fundable{m_client_model.node().gov().getFundableProposalHashes()}; ret->m_fundable_hashes = std::move(fundable.hashes); + ret->m_allocated = fundable.allocated; setData(std::move(ret)); } diff --git a/src/qt/clientfeeds.h b/src/qt/clientfeeds.h index 08ab6e7fc853..b0c6bcb68f5a 100644 --- a/src/qt/clientfeeds.h +++ b/src/qt/clientfeeds.h @@ -190,9 +190,12 @@ class QuorumFeed : public Feed { using Proposals = std::vector>; struct ProposalData { + CAmount m_allocated{0}; int m_abs_vote_req{0}; interfaces::GOV::GovernanceInfo m_gov_info; Proposals m_proposals; + uint16_t m_max_evo_voters{0}; + uint16_t m_max_regular_voters{0}; Uint256HashSet m_fundable_hashes; }; diff --git a/src/qt/forms/debugwindow.ui b/src/qt/forms/debugwindow.ui index e1749c2d85af..a669d79602a2 100644 --- a/src/qt/forms/debugwindow.ui +++ b/src/qt/forms/debugwindow.ui @@ -136,6 +136,20 @@ + + + Go&vernance + + + 0 + 0 + 0 + 0 + + + + + @@ -1342,6 +1356,12 @@ + + ProposalInfo + QWidget +
qt/proposalinfo.h
+ 1 +
InformationWidget QWidget diff --git a/src/qt/forms/proposalinfo.ui b/src/qt/forms/proposalinfo.ui new file mode 100644 index 000000000000..b27a4d49e8a6 --- /dev/null +++ b/src/qt/forms/proposalinfo.ui @@ -0,0 +1,310 @@ + + + ProposalInfo + + + + 0 + 0 + 700 + 400 + + + + + + + + 1 + 0 + + + + + 12 + + + + + General + + + + + + + Voting cycles + + + + + + + IBeamCursor + + + N/A + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Last superblock + + + + + + + IBeamCursor + + + N/A + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Next superblock + + + + + + + IBeamCursor + + + N/A + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Voting cutoff block + + + + + + + IBeamCursor + + + N/A + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Participation + + + + + + + Masternodes voting + + + + + + + IBeamCursor + + + N/A + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + EvoNodes voting + + + + + + + IBeamCursor + + + N/A + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Passing threshold + + + + + + + IBeamCursor + + + N/A + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Proposals + + + + + + + Proposal Count + + + + + + + IBeamCursor + + + N/A + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Budget allocated + + + + + + + IBeamCursor + + + N/A + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Passing Proposals + + + + + + + IBeamCursor + + + N/A + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Unfunded proposals + + + 10 + + + + + + + IBeamCursor + + + N/A + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Failing Proposals + + + + + + + IBeamCursor + + + N/A + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + diff --git a/src/qt/proposalinfo.cpp b/src/qt/proposalinfo.cpp new file mode 100644 index 000000000000..257cef4bf152 --- /dev/null +++ b/src/qt/proposalinfo.cpp @@ -0,0 +1,165 @@ +// Copyright (c) 2026 The Dash Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +namespace { +uint16_t GetCycleCount(const interfaces::GOV::GovernanceInfo& gov_info, const Consensus::Params& consensus_params) +{ + const auto first_offset{(gov_info.superblockcycle - consensus_params.nSuperblockStartBlock % gov_info.superblockcycle) % gov_info.superblockcycle}; + const auto first_height{consensus_params.nSuperblockStartBlock + first_offset}; + return gov_info.lastsuperblock >= first_height ? ((gov_info.lastsuperblock - first_height) / gov_info.superblockcycle + 1) : 0; +} + +QString FormatBlocksWithTime(int blocks, int64_t block_spacing, bool past) +{ + if (blocks <= 0) return QObject::tr("now"); + const QString duration{GUIUtil::formatBlockDuration(blocks, block_spacing)}; + return past ? QObject::tr("%1 ago").arg(duration) + : QObject::tr("%1 left").arg(duration); +} +} // anonymous namespace + +ProposalInfo::ProposalInfo(QWidget* parent) : + QWidget(parent), + ui(new Ui::ProposalInfo) +{ + ui->setupUi(this); + GUIUtil::setFont({ui->labelGeneral, + ui->labelParticipation, + ui->labelProposals}, + {GUIUtil::FontWeight::Bold, 16}); + + for (auto* element : {ui->labelParticipation, ui->labelProposals}) { + element->setContentsMargins(0, 10, 0, 0); + } +} + +ProposalInfo::~ProposalInfo() +{ + delete ui; +} + +void ProposalInfo::changeEvent(QEvent* e) +{ + if (e->type() == QEvent::StyleChange) { + updateProposalInfo(); + } + QWidget::changeEvent(e); +} + +void ProposalInfo::setClientModel(ClientModel* model) +{ + this->m_client_model = model; + if (!m_client_model) { + return; + } + m_feed_masternode = m_client_model->feedMasternode(); + m_feed_proposal = m_client_model->feedProposal(); + if (m_feed_masternode && m_feed_proposal) { + connect(m_feed_masternode, &MasternodeFeed::dataReady, this, &ProposalInfo::updateProposalInfo); + connect(m_feed_proposal, &ProposalFeed::dataReady, this, &ProposalInfo::updateProposalInfo); + updateProposalInfo(); + } +} + +void ProposalInfo::updateProposalInfo() +{ + if (!m_client_model || !m_feed_masternode || !m_feed_proposal) { + return; + } + + const auto data_mn = m_feed_masternode->data(); + const auto data_pr = m_feed_proposal->data(); + if (!data_mn || !data_mn->m_valid || !data_pr || data_pr->m_gov_info.superblockcycle <= 0) { + return; + } + + const auto& gov_info = data_pr->m_gov_info; + const auto& consensus_params = Params().GetConsensus(); + const int64_t block_spacing = gov_info.targetSpacing; + const int32_t passing_threshold = data_pr->m_abs_vote_req; + + // General section + ui->labelSuperblockCycle->setText(QString::number(GetCycleCount(gov_info, consensus_params))); + ui->labelPassingThreshold->setText(tr("%1 votes").arg(passing_threshold)); + + const auto block_height = m_client_model->getNumBlocks(); + ui->labelLastSuperblock->setText(tr("%1 (%2)").arg(gov_info.lastsuperblock) + .arg(FormatBlocksWithTime(block_height - gov_info.lastsuperblock, block_spacing, /*past=*/true))); + ui->labelNextSuperblock->setText(tr("%1 (%2)").arg(gov_info.nextsuperblock) + .arg(FormatBlocksWithTime(gov_info.nextsuperblock - block_height, block_spacing, /*past=*/false))); + + const auto voting_cutoff_height = gov_info.nextsuperblock - gov_info.superblockmaturitywindow; + ui->labelVotingDeadline->setText(tr("%1 (%2)").arg(voting_cutoff_height) + .arg(FormatBlocksWithTime(voting_cutoff_height - block_height, block_spacing, /*past=*/false))); + + // Participation section + ui->labelMasternodesVoting->setText(tr("%1 (%2 eligible)").arg(data_pr->m_max_regular_voters).arg(data_mn->m_counts.m_valid_mn)); + ui->labelEvoNodesVoting->setText(tr("%1 (%2 eligible)").arg(data_pr->m_max_evo_voters).arg(data_mn->m_counts.m_valid_evo)); + + // Proposals section + const BitcoinUnit display_unit{m_client_model->getOptionsModel()->getDisplayUnit()}; + const CAmount budget_avail{gov_info.governancebudget}; + + std::vector> passing_proposals; + uint16_t proposals_fail{0}, proposals_total{0}; + for (const auto& prop : data_pr->m_proposals) { + proposals_total++; + if (prop->getAbsoluteYesCount() >= passing_threshold) { + passing_proposals.push_back(prop); + } else { + proposals_fail++; + } + } + + CAmount budget_requested{0}, unfunded_shortfall{0}; + uint16_t unfunded_count{0}; + for (const auto& prop : passing_proposals) { + const CAmount proposed_amount{prop->paymentAmount()}; + budget_requested += proposed_amount; + if (data_pr->m_fundable_hashes.count(prop->objHash()) == 0) { + unfunded_count++; + unfunded_shortfall += proposed_amount; + } + } + + ui->labelProposalCount->setText(QString::number(proposals_total)); + ui->labelPassingProposals->setText(QString::number(passing_proposals.size())); + ui->labelFailingProposals->setText(QString::number(proposals_fail)); + + if (budget_avail > 0) { + const double alloc_pct{(static_cast(budget_requested) / static_cast(budget_avail)) * 100.0}; + ui->labelBudgetAllocated->setText( + tr("%1 / %2 (%3%)").arg(GUIUtil::formatAmount(display_unit, budget_requested, /*is_signed=*/false, /*truncate=*/2)) + .arg(GUIUtil::formatAmount(display_unit, budget_avail, /*is_signed=*/false, /*truncate=*/2)) + .arg(alloc_pct, 0, 'f', 2)); + } else { + ui->labelBudgetAllocated->setText(tr("N/A")); + } + + if (unfunded_count > 0) { + ui->labelUnfundedProposals->setText( + tr("%1 (short %2)").arg(unfunded_count) + .arg(GUIUtil::formatAmount(display_unit, unfunded_shortfall))); + } else { + ui->labelUnfundedProposals->setText(QString::number(0)); + } +} diff --git a/src/qt/proposalinfo.h b/src/qt/proposalinfo.h new file mode 100644 index 000000000000..033472ea37a3 --- /dev/null +++ b/src/qt/proposalinfo.h @@ -0,0 +1,45 @@ +// Copyright (c) 2026 The Dash Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_QT_PROPOSALINFO_H +#define BITCOIN_QT_PROPOSALINFO_H + +#include + +class ClientModel; +class MasternodeFeed; +class ProposalFeed; +namespace Ui { +class ProposalInfo; +} // namespace Ui + +QT_BEGIN_NAMESPACE +class QEvent; +QT_END_NAMESPACE + +class ProposalInfo : public QWidget +{ + Q_OBJECT + + Ui::ProposalInfo* ui; + +public: + explicit ProposalInfo(QWidget* parent = nullptr); + ~ProposalInfo() override; + + void setClientModel(ClientModel* model); + +public Q_SLOTS: + void updateProposalInfo(); + +protected: + void changeEvent(QEvent* e) override; + +private: + ClientModel* m_client_model{nullptr}; + MasternodeFeed* m_feed_masternode{nullptr}; + ProposalFeed* m_feed_proposal{nullptr}; +}; + +#endif // BITCOIN_QT_PROPOSALINFO_H diff --git a/src/qt/res/css/general.css b/src/qt/res/css/general.css index 890005f87b62..c2e46f27faad 100644 --- a/src/qt/res/css/general.css +++ b/src/qt/res/css/general.css @@ -1735,14 +1735,16 @@ QWidget#RPCConsole QLabel#label_alerts { padding: 5px; } -QWidget#InformationWidget QLabel#labelNetwork, QWidget#InformationWidget QLabel#label_10, QWidget#InformationWidget QLabel#labelMempoolTitle, +QWidget#InformationWidget QLabel#labelNetwork, QWidget#NetworkWidget QLabel#labelChainLocks, QWidget#NetworkWidget QLabel#labelCreditPool, QWidget#NetworkWidget QLabel#labelInstantSend, QWidget#NetworkWidget QLabel#labelMasternodes, -QWidget#NetworkWidget QLabel#labelQuorums { /* Margin between Network and Block Chain headers */ +QWidget#NetworkWidget QLabel#labelQuorums, +QWidget#ProposalInfo QLabel#labelParticipation, +QWidget#ProposalInfo QLabel#labelProposals { qproperty-alignment: 'AlignLeft | AlignBottom'; min-height: 25px; } diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index c6c88aff22c8..8b7af26c9ce0 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -606,6 +607,8 @@ RPCConsole::RPCConsole(interfaces::Node& node, QWidget* parent, Qt::WindowFlags connect(pageButtons, QOverload::of(&QButtonGroup::buttonClicked), this, &RPCConsole::showPage); #endif + // Governance tab is shown only when the governance system is active + ui->tabWidgetInfo->removeTab(ToUnderlying(InfoView::Governance)); // Keep tabs compact; prevent Qt from stretching them to fill the bar width ui->tabWidgetInfo->tabBar()->setExpanding(false); @@ -702,12 +705,17 @@ void RPCConsole::setClientModel(ClientModel *model, int bestblock_height, int64_ ui->informationWidget->setClientModel(model); ui->networkWidget->setClientModel(model); + ui->proposalInfo->setClientModel(model); ui->trafficGraph->setClientModel(model); if (model && clientModel->getPeerTableModel() && clientModel->getBanTableModel()) { // Keep up to date with client connect(model, &ClientModel::numBlocksChanged, ui->informationWidget, &InformationWidget::setNumBlocks); m_feed_masternode = model->feedMasternode(); + if (m_node.gov().isEnabled() && ui->tabWidgetInfo->indexOf(ui->tabGovernance) == -1) { + ui->tabWidgetInfo->insertTab(ToUnderlying(InfoView::Governance), ui->tabGovernance, tr("&Governance")); + } + // set up peer table ui->peerWidget->setModel(model->peerTableSortProxy()); ui->peerWidget->verticalHeader()->hide(); @@ -802,6 +810,11 @@ void RPCConsole::setClientModel(ClientModel *model, int bestblock_height, int64_ startExecutor(); } if (!model) { + // Remove governance tab so it is not duplicated if a new model is set later + const int gov_tab_idx{ui->tabWidgetInfo->indexOf(ui->tabGovernance)}; + if (gov_tab_idx != -1) { + ui->tabWidgetInfo->removeTab(gov_tab_idx); + } // Client model is being set to 0, this means shutdown() is about to be called. thread.quit(); thread.wait(); diff --git a/src/qt/rpcconsole.h b/src/qt/rpcconsole.h index 985f8109c499..9202a483ea52 100644 --- a/src/qt/rpcconsole.h +++ b/src/qt/rpcconsole.h @@ -84,6 +84,7 @@ class RPCConsole: public QWidget enum class InfoView : uint8_t { General, Network, + Governance, }; std::vector tabs() const { return {TabTypes::INFO, TabTypes::CONSOLE, TabTypes::GRAPH, TabTypes::PEERS, TabTypes::REPAIR}; } diff --git a/test/util/data/non-backported.txt b/test/util/data/non-backported.txt index 98e3fb82057a..8befc7290423 100644 --- a/test/util/data/non-backported.txt +++ b/test/util/data/non-backported.txt @@ -37,6 +37,7 @@ src/qt/masternodemodel.* src/qt/mnemonicverificationdialog.* src/qt/networkwidget.* src/qt/proposalcreate.* +src/qt/proposalinfo.* src/qt/proposallist.* src/qt/proposalmodel.* src/qt/proposalresume.* From e7de61de16b7a8a77d52d057aa5d2c1b1aa9f9b3 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Tue, 24 Feb 2026 15:21:58 +0530 Subject: [PATCH 3/7] feat(qt): add wallet-specific section to proposal widget This reporting supersedes the old "Masternode count:" in the proposal list. --- src/qt/forms/proposalcreate.ui | 1 - src/qt/forms/proposalinfo.ui | 69 ++++++++++++++++++++++++++++------ src/qt/forms/proposallist.ui | 17 --------- src/qt/proposalinfo.cpp | 49 +++++++++++++++++++++++- src/qt/proposalinfo.h | 11 ++++++ src/qt/proposallist.cpp | 13 +------ src/qt/proposallist.h | 1 - src/qt/res/css/general.css | 1 + src/qt/rpcconsole.cpp | 1 + 9 files changed, 120 insertions(+), 43 deletions(-) diff --git a/src/qt/forms/proposalcreate.ui b/src/qt/forms/proposalcreate.ui index 9cbe85c342c9..f75e7320729d 100644 --- a/src/qt/forms/proposalcreate.ui +++ b/src/qt/forms/proposalcreate.ui @@ -323,7 +323,6 @@ - diff --git a/src/qt/forms/proposalinfo.ui b/src/qt/forms/proposalinfo.ui index b27a4d49e8a6..98dd80d9847f 100644 --- a/src/qt/forms/proposalinfo.ui +++ b/src/qt/forms/proposalinfo.ui @@ -178,20 +178,67 @@ + + + Node + + + + + + + Masternodes controlled + + + + + + + IBeamCursor + + + N/A + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Votes controlled + + + + + + + IBeamCursor + + + N/A + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + Proposals - + Proposal Count - + IBeamCursor @@ -204,14 +251,14 @@ - + Budget allocated - + IBeamCursor @@ -224,14 +271,14 @@ - + Passing Proposals - + IBeamCursor @@ -244,7 +291,7 @@ - + Unfunded proposals @@ -254,7 +301,7 @@ - + IBeamCursor @@ -267,14 +314,14 @@ - + Failing Proposals - + IBeamCursor @@ -287,7 +334,7 @@ - + Qt::Vertical diff --git a/src/qt/forms/proposallist.ui b/src/qt/forms/proposallist.ui index 67ee680f0a5b..c2f3c5cb75bf 100644 --- a/src/qt/forms/proposallist.ui +++ b/src/qt/forms/proposallist.ui @@ -59,23 +59,6 @@ - - - - Masternode Count: - - - Number of masternodes this wallet can vote with (masternodes for which this wallet holds the voting key) - - - - - - - 0 - - - diff --git a/src/qt/proposalinfo.cpp b/src/qt/proposalinfo.cpp index 257cef4bf152..0f6de6daa8e8 100644 --- a/src/qt/proposalinfo.cpp +++ b/src/qt/proposalinfo.cpp @@ -6,7 +6,9 @@ #include #include +#include #include +#include