1313#include < qt/bitcoinunits.h>
1414#include < qt/clientfeeds.h>
1515#include < qt/clientmodel.h>
16+ #include < qt/donutchart.h>
1617#include < qt/guiutil.h>
1718#include < qt/guiutil_font.h>
1819#include < qt/optionsmodel.h>
@@ -162,10 +163,12 @@ void ProposalInfo::updateProposalInfo()
162163 ui->labelWeightedVotes ->setText (tr (" N/A" ));
163164#endif // ENABLE_WALLET
164165
165- // Proposals section
166+ // Proposals section with budget chart
166167 const BitcoinUnit display_unit{m_client_model->getOptionsModel ()->getDisplayUnit ()};
167168 const CAmount budget_avail{gov_info.governancebudget };
169+ const QColor base_blue{GUIUtil::getThemedQColor (GUIUtil::ThemedColor::BLUE).darker (160 )};
168170
171+ // Collect passing proposals sorted by vote count
169172 std::vector<std::shared_ptr<Proposal>> passing_proposals;
170173 uint16_t proposals_fail{0 }, proposals_total{0 };
171174 for (const auto & prop : data_pr->m_proposals ) {
@@ -176,28 +179,55 @@ void ProposalInfo::updateProposalInfo()
176179 proposals_fail++;
177180 }
178181 }
182+ std::sort (passing_proposals.begin (), passing_proposals.end (),
183+ [](const std::shared_ptr<Proposal>& a, const std::shared_ptr<Proposal>& b) {
184+ return a->getAbsoluteYesCount () > b->getAbsoluteYesCount ();
185+ });
179186
180- CAmount budget_requested {0 }, unfunded_shortfall{0 };
187+ CAmount budget_funded {0 }, unfunded_shortfall{0 };
181188 uint16_t unfunded_count{0 };
182- for (const auto & prop : passing_proposals) {
189+ std::vector<DonutChart::Slice> chart_slices{};
190+
191+ for (size_t idx{0 }; idx < passing_proposals.size (); idx++) {
192+ const auto & prop{passing_proposals[idx]};
183193 const CAmount proposed_amount{prop->paymentAmount ()};
184- budget_requested += proposed_amount;
185- if (data_pr->m_fundable_hashes .count (prop->objHash ()) == 0 ) {
194+ if (data_pr->m_fundable_hashes .count (prop->objHash ()) > 0 ) {
195+ budget_funded += proposed_amount;
196+ } else {
186197 unfunded_count++;
187198 unfunded_shortfall += proposed_amount;
188199 }
200+ const double slice_pct{budget_avail <= 0
201+ ? 0.0
202+ : (static_cast <double >(proposed_amount) / static_cast <double >(budget_avail)) * 100.0 };
203+ chart_slices.emplace_back (DonutChart::Slice{
204+ static_cast <double >(proposed_amount),
205+ (idx == 0 ) ? base_blue : base_blue.lighter (100 + static_cast <int >(idx * 20 )),
206+ prop->title ().isEmpty () ? tr (" Proposal %1" ).arg (idx + 1 ) : prop->title (),
207+ GUIUtil::formatAmount (display_unit, proposed_amount),
208+ tr (" %1% of budget" ).arg (slice_pct, 0 , ' f' , 1 )
209+ });
189210 }
190211
191212 ui->labelProposalCount ->setText (QString::number (proposals_total));
192213 ui->labelPassingProposals ->setText (QString::number (passing_proposals.size ()));
193214 ui->labelFailingProposals ->setText (QString::number (proposals_fail));
194215
216+ const double alloc_pct_chart{budget_avail <= 0
217+ ? 0.0
218+ : (static_cast <double >(budget_funded) / static_cast <double >(budget_avail)) * 100.0 };
219+ DonutChart::CenterText default_text{
220+ GUIUtil::formatAmount (display_unit, budget_funded),
221+ tr (" / %1" ).arg (GUIUtil::formatAmount (display_unit, budget_avail)),
222+ tr (" %1% allocated" ).arg (alloc_pct_chart, 0 , ' f' , 1 )
223+ };
224+ ui->budgetChart ->setData (std::move (chart_slices), static_cast <double >(budget_avail), std::move (default_text));
225+
195226 if (budget_avail > 0 ) {
196- const double alloc_pct{(static_cast <double >(budget_requested) / static_cast <double >(budget_avail)) * 100.0 };
197227 ui->labelBudgetAllocated ->setText (
198- tr (" %1 / %2 (%3%)" ).arg (GUIUtil::formatAmount (display_unit, budget_requested , /* is_signed=*/ false , /* truncate=*/ 2 ))
228+ tr (" %1 / %2 (%3%)" ).arg (GUIUtil::formatAmount (display_unit, budget_funded , /* is_signed=*/ false , /* truncate=*/ 2 ))
199229 .arg (GUIUtil::formatAmount (display_unit, budget_avail, /* is_signed=*/ false , /* truncate=*/ 2 ))
200- .arg (alloc_pct , 0 , ' f' , 2 ));
230+ .arg (alloc_pct_chart , 0 , ' f' , 2 ));
201231 } else {
202232 ui->labelBudgetAllocated ->setText (tr (" N/A" ));
203233 }
0 commit comments