feat(qt): UI refresh (5/n, add proposal information widget to information, donut chart for proposal allocation)#7159
feat(qt): UI refresh (5/n, add proposal information widget to information, donut chart for proposal allocation)#7159kwvg wants to merge 7 commits intodashpay:developfrom
Conversation
✅ No Merge Conflicts DetectedThis PR currently has no conflicts with other open PRs. |
|
This pull request has conflicts, please rebase. |
ddde0fb to
5961ed6
Compare
…r Dash-specific reporting in debug window) f85a459 refactor(qt): move debug log action from General widget to File menu (Kittywhiskers Van Gogh) 7839115 feat(qt): add tooltip for quorum statistics with rotation, expiry, age (Kittywhiskers Van Gogh) 8a721c2 refactor(qt): move "Chainlocks" outside hlayout due to value width (Kittywhiskers Van Gogh) dff26d6 refactor(qt): use horizontal layout with vertical grids, reorder data (Kittywhiskers Van Gogh) 12dc7fe feat(qt): report quorum statistics in network widget (Kittywhiskers Van Gogh) 26ed211 feat(qt): report credit pool statistics in network widget (Kittywhiskers Van Gogh) b9a14cc qt: show more instantsend counters (pending, waiting, unprotected) in UI (Kittywhiskers Van Gogh) a4e9fbc qt: report chainlock time to maintain parity with block fields (Kittywhiskers Van Gogh) ab8d6d2 qt: register chainlocks information as a feed, treat UI notif as trigger (Kittywhiskers Van Gogh) 654724d qt: register instantsend information as a feed, replace polling approach (Kittywhiskers Van Gogh) ba406f5 interfaces: introduce UI signal `NotifyInstantSendChanged` (Kittywhiskers Van Gogh) d1f61f5 refactor: drop now-unused cached masternode list routine (Kittywhiskers Van Gogh) 6bb3e6b chore(qt): update header and label descriptions based on capability (Kittywhiskers Van Gogh) 918cba1 refactor(qt): move Dash-specific reporting to network widget (Kittywhiskers Van Gogh) 0a64bab refactor(qt): move debug window stats to separate information widget (Kittywhiskers Van Gogh) dfec7d8 fix(qt): align headers in debug window's information tab (Kittywhiskers Van Gogh) b524eef qt: precompute mappings to avoid expensive searches (Kittywhiskers Van Gogh) 960666b qt: switch ProposalList to consume masternode list feed (Kittywhiskers Van Gogh) d9beeec qt: switch RPCConsole and leftover MasternodeList code to feed (Kittywhiskers Van Gogh) Pull request description: ## Additional Information * Depends on #7112 * Depends on #7110 * Depends on #7146 * Dependency for #7159 | v23.0.2 (cdc5a63) | This PR (WIP code) | | -------------------- | ------------------- | |  |  | | <div align="center">See above</div> |  | | <div align="center">Does not exist in this build</div> |  | | <div align="center">Does not exist in this build</div> |  | ## Breaking Changes None expected. ## Checklist - [x] I have performed a self-review of my own code - [x] I have commented my code, particularly in hard-to-understand areas - [x] I have added or updated relevant unit/integration/functional/e2e tests **(note: N/A)** - [x] I have made corresponding changes to the documentation **(note: N/A)** - [x] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_ ACKs for top commit: PastaPastaPasta: utACK f85a459 UdjinM6: light ACK f85a459 Tree-SHA512: 8f555721c9fc9951c5fcdcb4b0a8e102a16ce5c89cf0a2641e416f09689fc3de5f502cf05ba7abe5c6acd962f0d072bcd8bf787a0c9d21b1fd706a58ff69b326
WalkthroughAdds a governance information UI and related plumbing: a DonutChart widget and a ProposalInfo widget (UI file and wiring into debugwindow and proposallist), new ProposalData fields for allocation and voter counts, UI interactions to open Governance info from ProposalList/WalletView/RPCConsole, and visual/style updates. On the backend, introduces CGovernanceObject::GetUniqueVoterCount and exposes unique-voter queries via node interfaces and GOVImpl. Also updates proposal model sorting keys and Makefile.qt.include to register new Qt sources/mocs/headers. Sequence Diagram(s)sequenceDiagram
actor User
participant ProposalList
participant WalletView
participant BitcoinGUI
participant RPCConsole
participant ProposalInfo
participant ClientModel
participant ProposalFeed
participant MasternodeFeed
User->>ProposalList: Clicks Info Button
ProposalList->>WalletView: emit showProposalInfo()
WalletView->>BitcoinGUI: forward showProposalInfo()
BitcoinGUI->>RPCConsole: setInfoView(Governance)
BitcoinGUI->>BitcoinGUI: open Debug Window
RPCConsole->>ProposalInfo: ensure tab visible / setClientModel
ProposalInfo->>ClientModel: setClientModel()
ClientModel->>ProposalFeed: provide feed reference
ClientModel->>MasternodeFeed: provide feed reference
ProposalFeed->>ProposalInfo: dataReady
MasternodeFeed->>ProposalInfo: dataReady
ProposalInfo->>ProposalInfo: updateProposalInfo()
ProposalInfo->>DonutChart: setData(funding slices)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (1)
src/qt/forms/debugwindow.ui (1)
1359-1363: Missing<container>1</container>in ProposalInfo custom widget registration.Other custom widgets in this file (
InformationWidget,NetworkWidget,TrafficGraphWidget) include<container>1</container>. ProposalInfo omits it. While this doesn't affect runtime behavior, it's inconsistent and could affect Qt Designer usability.Suggested fix
<customwidget> <class>ProposalInfo</class> <extends>QWidget</extends> <header>qt/proposalinfo.h</header> + <container>1</container> </customwidget>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/qt/forms/debugwindow.ui` around lines 1359 - 1363, The ProposalInfo custom widget registration is missing the <container>1</container> tag which other widgets (e.g., InformationWidget, NetworkWidget, TrafficGraphWidget) include; update the ProposalInfo <customwidget> block (class ProposalInfo, extends QWidget, header qt/proposalinfo.h) to add a <container>1</container> element so its registration matches the others and improves Qt Designer consistency.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/qt/donutchart.cpp`:
- Around line 55-66: DonutChart::chartGeometry can produce a negative
m_inner_radius when outer_radius is smaller than the clamped donut_thickness;
change the computation so the inner radius is defensively clamped to >= 0.
Specifically, after computing outer_radius and donut_thickness (using
CHART_MARGIN and qBound as now), set m_inner_radius = qMax(0, outer_radius -
donut_thickness) (or ensure donut_thickness = qMin(donut_thickness,
outer_radius)) before returning the Geometry; this guarantees drawEllipse and
subsequent max_text_width calculations never receive a negative radius.
In `@src/qt/proposalinfo.cpp`:
- Around line 78-90: The code only connects ProposalFeed::dataReady to
ProposalInfo::updateProposalInfo, so masternode updates don't trigger a refresh;
in ProposalInfo::setClientModel after acquiring m_feed_masternode and
m_feed_proposal, also connect the masternode feed's update signal (e.g.
MasternodeFeed::dataReady or the actual signal provided by the feed returned by
feedMasternode()) to this->updateProposalInfo using connect, and ensure
updateProposalInfo() is called when both feeds are present; reference
ProposalInfo::setClientModel, m_feed_masternode, m_feed_proposal, connect and
updateProposalInfo when making the change.
- Around line 165-234: The labelBudgetAllocated currently shows budget_requested
while the center DonutChart text and allocation percent use budget_funded,
causing inconsistent UI when some proposals are unfunded; update the allocation
display to consistently reflect funded allocations by replacing budget_requested
with budget_funded in the ui->labelBudgetAllocated text and alloc_pct
calculation (refer to variables budget_funded, budget_requested, alloc_pct,
alloc_pct_chart and the ui elements ui->labelBudgetAllocated and
ui->budgetChart/DonutChart::CenterText), and ensure the donut slices remain
showing individual proposals but the summary percent and "X / Y" values use
budget_funded to match the chart center.
In `@src/qt/proposallist.cpp`:
- Around line 391-434: In ProposalList::updateInfoTooltip fix the voting-time
math by computing voting_remaining_blocks = std::max<int>(0, remaining_blocks -
m_gov_info.superblockmaturitywindow) and use that value (and its formatted
duration) when building tooltip2 for the "left for voting" branch; keep
remaining_blocks for the "left for superblock" branch and preserve the
awaiting_superblock check, so the tooltip shows voting_remaining_blocks for
voting time left and remaining_blocks for superblock time left.
---
Nitpick comments:
In `@src/qt/forms/debugwindow.ui`:
- Around line 1359-1363: The ProposalInfo custom widget registration is missing
the <container>1</container> tag which other widgets (e.g., InformationWidget,
NetworkWidget, TrafficGraphWidget) include; update the ProposalInfo
<customwidget> block (class ProposalInfo, extends QWidget, header
qt/proposalinfo.h) to add a <container>1</container> element so its registration
matches the others and improves Qt Designer consistency.
ℹ️ Review info
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (26)
src/Makefile.qt.includesrc/governance/object.cppsrc/governance/object.hsrc/interfaces/node.hsrc/node/interfaces.cppsrc/qt/bitcoingui.cppsrc/qt/clientfeeds.cppsrc/qt/clientfeeds.hsrc/qt/donutchart.cppsrc/qt/donutchart.hsrc/qt/forms/debugwindow.uisrc/qt/forms/proposalinfo.uisrc/qt/forms/proposallist.uisrc/qt/proposalinfo.cppsrc/qt/proposalinfo.hsrc/qt/proposallist.cppsrc/qt/proposallist.hsrc/qt/proposalmodel.cppsrc/qt/res/css/dark.csssrc/qt/res/css/general.csssrc/qt/res/css/light.csssrc/qt/rpcconsole.cppsrc/qt/rpcconsole.hsrc/qt/walletview.cppsrc/qt/walletview.htest/util/data/non-backported.txt
This reporting supersedes the old "Masternode count:" in the proposal list.
This is also an opportunity to make sure our tooltips are in sync.
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (1)
src/qt/clientfeeds.cpp (1)
239-244: Consider merging the voter-count loop with the earlier proposal loop.
govObjListis iterated twice with the samePROPOSALfilter (lines 228-234 and 239-244). Folding thegetObjUniqueVoterscalls into the first loop would eliminate the redundant pass.Not a correctness issue—just a minor efficiency/clarity improvement, and the list is small.
Proposed consolidation
for (const auto& govObj : govObjList) { if (govObj.GetObjectType() != GovernanceObject::PROPOSAL) { continue; // Skip triggers. } ret->m_proposals.emplace_back(std::make_shared<Proposal>(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; - for (const auto& govObj : govObjList) { - if (govObj.GetObjectType() != GovernanceObject::PROPOSAL) continue; - 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); - }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/qt/clientfeeds.cpp` around lines 239 - 244, The second loop over govObjList that calls m_client_model.node().gov().getObjUniqueVoters for GovernanceObject::PROPOSAL is redundant; fold that logic into the earlier proposal-processing loop that already filters by GovernanceObject::PROPOSAL so you only iterate once. Inside the existing proposal loop (where govObj is checked for GovernanceObject::PROPOSAL), call const auto voters = m_client_model.node().gov().getObjUniqueVoters(govObj, VOTE_SIGNAL_FUNDING) and update ret->m_max_regular_voters = std::max(ret->m_max_regular_voters, voters.m_regular) and ret->m_max_evo_voters = std::max(ret->m_max_evo_voters, voters.m_evo), then remove the separate second loop.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/Makefile.qt.include`:
- Around line 36-38: Replace the wildcard entries `src/qt/proposalinfo.*` and
`src/qt/donutchart.*` in test/util/data/non-backported.txt with explicit C++
source/header patterns to avoid matching .ui files: add
`src/qt/proposalinfo.cpp`, `src/qt/proposalinfo.h`, `src/qt/donutchart.cpp`, and
`src/qt/donutchart.h` so clang-format exclusion only applies to the .cpp/.h
files and not the .ui files.
In `@src/qt/donutchart.cpp`:
- Around line 1-11: Run clang-format-diff to apply the project's formatting
rules to src/qt/donutchart.cpp: reformat the file to match the repository style
(fix include ordering/spacing and any indentation/line breaks around the
DonutChart implementation and related functions), stage the modified file and
commit; ensure the resulting file compiles and that only formatting changes are
included in the commit.
In `@src/qt/proposalinfo.cpp`:
- Around line 93-103: The current setWalletModel assigns m_wallet_model but
returns early when passed nullptr, preventing updateProposalInfo from running
and leaving controlled/vote labels stale; remove the early return and after
setting this->m_wallet_model always check if m_feed_masternode &&
m_feed_proposal then call updateProposalInfo() so that calling
setWalletModel(nullptr) triggers updateProposalInfo() (which will clear/revert
labels to "N/A") — change in ProposalInfo::setWalletModel to assign
m_wallet_model and always run the m_feed_masternode && m_feed_proposal
conditional call to updateProposalInfo().
In `@src/qt/proposalmodel.cpp`:
- Around line 243-252: The switch-case lines in proposal->status(isFundable) are
manually column-aligned which triggers clang-format-diff failures; reformat the
block to follow the project's clang-format style (remove extra spaces before the
return statements) by running clang-format-diff on the changed lines and
applying the suggested changes so the cases for ProposalStatus::Funded, Passing,
Unfunded, Voting, Confirming, Pending, Failing, and Lapsed match the repository
formatting.
---
Nitpick comments:
In `@src/qt/clientfeeds.cpp`:
- Around line 239-244: The second loop over govObjList that calls
m_client_model.node().gov().getObjUniqueVoters for GovernanceObject::PROPOSAL is
redundant; fold that logic into the earlier proposal-processing loop that
already filters by GovernanceObject::PROPOSAL so you only iterate once. Inside
the existing proposal loop (where govObj is checked for
GovernanceObject::PROPOSAL), call const auto voters =
m_client_model.node().gov().getObjUniqueVoters(govObj, VOTE_SIGNAL_FUNDING) and
update ret->m_max_regular_voters = std::max(ret->m_max_regular_voters,
voters.m_regular) and ret->m_max_evo_voters = std::max(ret->m_max_evo_voters,
voters.m_evo), then remove the separate second loop.
ℹ️ Review info
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (22)
src/Makefile.qt.includesrc/qt/bitcoingui.cppsrc/qt/clientfeeds.cppsrc/qt/clientfeeds.hsrc/qt/donutchart.cppsrc/qt/donutchart.hsrc/qt/forms/debugwindow.uisrc/qt/forms/proposalinfo.uisrc/qt/forms/proposallist.uisrc/qt/proposalinfo.cppsrc/qt/proposalinfo.hsrc/qt/proposallist.cppsrc/qt/proposallist.hsrc/qt/proposalmodel.cppsrc/qt/res/css/dark.csssrc/qt/res/css/general.csssrc/qt/res/css/light.csssrc/qt/rpcconsole.cppsrc/qt/rpcconsole.hsrc/qt/walletview.cppsrc/qt/walletview.htest/util/data/non-backported.txt
🚧 Files skipped from review as they are similar to previous changes (6)
- test/util/data/non-backported.txt
- src/qt/rpcconsole.h
- src/qt/res/css/dark.css
- src/qt/proposallist.h
- src/qt/proposalinfo.h
- src/qt/donutchart.h
| qt/forms/proposalcreate.ui \ | ||
| qt/forms/proposalinfo.ui \ | ||
| qt/forms/proposallist.ui \ |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let's check what's in the non-backported.txt file
cat test/util/data/non-backported.txt | head -50Repository: dashpay/dash
Length of output: 1063
🏁 Script executed:
# Search for donutchart and proposalinfo entries
rg "donutchart|proposalinfo" test/util/data/non-backported.txtRepository: dashpay/dash
Length of output: 97
🏁 Script executed:
# Let's also check what files exist in the PR context
# Look for proposalinfo and donutchart files in the repo
fd -e "proposalinfo|donutchart" --extension h --extension cpp --extension uiRepository: dashpay/dash
Length of output: 50368
Use specific C++ file patterns instead of wildcards to exclude .ui files from the non-backported list.
The entries src/qt/proposalinfo.* and src/qt/donutchart.* in test/util/data/non-backported.txt use wildcards that match .ui files. Since non-backported.txt is used only for clang-format, .ui files must be excluded. Replace with specific entries: src/qt/proposalinfo.cpp, src/qt/proposalinfo.h, src/qt/donutchart.cpp, and src/qt/donutchart.h.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/Makefile.qt.include` around lines 36 - 38, Replace the wildcard entries
`src/qt/proposalinfo.*` and `src/qt/donutchart.*` in
test/util/data/non-backported.txt with explicit C++ source/header patterns to
avoid matching .ui files: add `src/qt/proposalinfo.cpp`,
`src/qt/proposalinfo.h`, `src/qt/donutchart.cpp`, and `src/qt/donutchart.h` so
clang-format exclusion only applies to the .cpp/.h files and not the .ui files.
| // 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 <qt/donutchart.h> | ||
|
|
||
| #include <qt/guiutil.h> | ||
|
|
||
| #include <QMouseEvent> | ||
| #include <QPainter> | ||
| #include <QtMath> |
There was a problem hiding this comment.
Fix formatting to pass CI.
The pipeline reports clang-format differences for this file. Please run clang-format-diff and commit the formatting corrections.
🧰 Tools
🪛 GitHub Actions: Clang Diff Format Check
[error] 1-1: Clang format differences detected in file after running clang-format-diff. Update formatting to match project style.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/qt/donutchart.cpp` around lines 1 - 11, Run clang-format-diff to apply
the project's formatting rules to src/qt/donutchart.cpp: reformat the file to
match the repository style (fix include ordering/spacing and any
indentation/line breaks around the DonutChart implementation and related
functions), stage the modified file and commit; ensure the resulting file
compiles and that only formatting changes are included in the commit.
| #ifdef ENABLE_WALLET | ||
| void ProposalInfo::setWalletModel(WalletModel* model) | ||
| { | ||
| this->m_wallet_model = model; | ||
| if (!m_wallet_model) { | ||
| return; | ||
| } | ||
| if (m_feed_masternode && m_feed_proposal) { | ||
| updateProposalInfo(); | ||
| } | ||
| } |
There was a problem hiding this comment.
Refresh stats when the wallet model is cleared.
setWalletModel(nullptr) returns early, so controlled/vote labels can remain stale. Trigger updateProposalInfo() even when the wallet model is null so labels revert to “N/A.”
🔧 Proposed fix
void ProposalInfo::setWalletModel(WalletModel* model)
{
this->m_wallet_model = model;
- if (!m_wallet_model) {
- return;
- }
- if (m_feed_masternode && m_feed_proposal) {
- updateProposalInfo();
- }
+ if (m_feed_masternode && m_feed_proposal) {
+ updateProposalInfo();
+ }
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/qt/proposalinfo.cpp` around lines 93 - 103, The current setWalletModel
assigns m_wallet_model but returns early when passed nullptr, preventing
updateProposalInfo from running and leaving controlled/vote labels stale; remove
the early return and after setting this->m_wallet_model always check if
m_feed_masternode && m_feed_proposal then call updateProposalInfo() so that
calling setWalletModel(nullptr) triggers updateProposalInfo() (which will
clear/revert labels to "N/A") — change in ProposalInfo::setWalletModel to assign
m_wallet_model and always run the m_feed_masternode && m_feed_proposal
conditional call to updateProposalInfo().
| switch (proposal->status(isFundable)) { | ||
| case ProposalStatus::Funded: return (0 << 16) + deficit; | ||
| case ProposalStatus::Passing: return (1 << 16) + deficit; | ||
| case ProposalStatus::Unfunded: return (2 << 16) + deficit; | ||
| case ProposalStatus::Voting: return (3 << 16) + deficit; | ||
| case ProposalStatus::Confirming: return (4 << 16) + deficit; | ||
| case ProposalStatus::Pending: return (5 << 16) + deficit; | ||
| case ProposalStatus::Failing: return (6 << 16) + deficit; | ||
| case ProposalStatus::Lapsed: return (7 << 16) + deficit; | ||
| } // no default case, so the compiler can warn about missing cases |
There was a problem hiding this comment.
CI failure: clang-format-diff reports formatting differences.
The pipeline is failing due to formatting mismatches. The manual column-alignment on these case lines (extra spaces before return) is likely the cause. Run clang-format-diff on the changed lines and apply the result.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/qt/proposalmodel.cpp` around lines 243 - 252, The switch-case lines in
proposal->status(isFundable) are manually column-aligned which triggers
clang-format-diff failures; reformat the block to follow the project's
clang-format style (remove extra spaces before the return statements) by running
clang-format-diff on the changed lines and applying the suggested changes so the
cases for ProposalStatus::Funded, Passing, Unfunded, Voting, Confirming,
Pending, Failing, and Lapsed match the repository formatting.
Additional Information
{Masternode,Proposal}Lists #7146Breaking Changes
None expected.
Checklist