Skip to content

Conversation

@kwvg
Copy link
Collaborator

@kwvg kwvg commented Jan 21, 2026

Additional Information

develop (cc50446) This PR (18d6ce6)

Breaking Changes

None expected.

Checklist

  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added or updated relevant unit/integration/functional/e2e tests (note: N/A)
  • I have made corresponding changes to the documentation (note: N/A)
  • I have assigned this pull request to a milestone (for repository code-owners and collaborators only)

kwvg added 4 commits January 21, 2026 16:17
Also `s/coinJoinEnabledChanged/showCoinJoinChanged/g` to establish a
naming convention that will be used soon.
This should fix the misalignment of the `subFeeFromAmount` `QCheckBox`
compared to the rest of the `QCheckBox`es

Review with `git log -p -n1 --ignore-space-change --color-moved=dimmed_zebra`.
Review with `git log -p -n1 --color-moved=dimmed_zebra`.
@kwvg kwvg added this to the 23.1 milestone Jan 21, 2026
@github-actions
Copy link

github-actions bot commented Jan 21, 2026

✅ No Merge Conflicts Detected

This PR currently has no conflicts with other open PRs.

@kwvg
Copy link
Collaborator Author

kwvg commented Jan 22, 2026

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Jan 22, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai
Copy link

coderabbitai bot commented Jan 22, 2026

Walkthrough

Adds a Qt governance proposal model and UI wiring: introduces Proposal and ProposalModel (ProposalList) to parse and present CGovernanceObject data as a QAbstractTableModel and integrates them into the build. Changes tab visibility handling: OptionsModel now stores showMasternodes/showGovernance flags and emits showMasternodesChanged/showGovernanceChanged/showCoinJoinChanged; BitcoinGUI and WalletView create mastenode/governance pages unconditionally and use new updateGovernanceVisibility/updateMasternodesVisibility slots to toggle visibility. Font utilities gain getScaledFont/getFontBold and per-QTextEdit HTML registration. Renames coinJoinEnabledChanged → showCoinJoinChanged and updates related connections.

Sequence Diagram(s)

mermaid
sequenceDiagram
participant OptionsModel
participant BitcoinGUI
participant Toolbar
participant QAction
OptionsModel->>BitcoinGUI: emit showGovernanceChanged / showMasternodesChanged
BitcoinGUI->>Toolbar: updateGovernanceVisibility()
BitcoinGUI->>Toolbar: updateMasternodesVisibility()
Toolbar->>QAction: enable/disable actions (m_governance_action / m_masternode_action)
BitcoinGUI->>QToolButton: show/hide governanceButton / masternodeButton

mermaid
sequenceDiagram
participant ClientModel
participant GovernanceBackend as CGovernanceObject
participant ProposalModel
participant GovernanceListUI
ClientModel->>ProposalModel: provide governance objects
ProposalModel->>ProposalModel: parse CGovernanceObject -> Proposal
ProposalModel->>GovernanceListUI: reconcile(ProposalList)
GovernanceListUI->>UI: update table view / context menu (openUrl)

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 6.85% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title comprehensively describes the main changes: UI refresh with runtime show/hide of tabs, proposal model split, bounds checking, and QTextEdit styling improvements, directly matching the changeset scope.
Description check ✅ Passed The description provides relevant context about the PR's objectives, visual diffs, dependencies, and implementation details related to the changeset's governance/masternode tabs, proposal model refactoring, and styling improvements.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Warning

Review ran into problems

🔥 Problems

Git: Failed to clone repository. Please run the @coderabbitai full review command to re-trigger a full review. If the issue persists, set path_filters to include or exclude specific files.


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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/qt/guiutil_font.h (1)

1-178: Pipeline failure: clang-format differences detected.

The CI reports formatting issues in this file. Run clang-format or the project's diff tool to fix formatting before merging.

🤖 Fix all issues with AI agents
In `@src/qt/governancelist.cpp`:
- Around line 186-188: The context menu action currently captures a raw
Proposal* (proposal) which can dangle if reconcile() modifies m_data before the
user clicks; change the lambda to capture a stable identifier (e.g.,
proposal->getHash() or id) instead of the pointer and, inside the action, look
up the Proposal from m_data (or the container that owns proposals) and only call
openUrl() if the lookup returns a valid pointer; alternatively, ensure
proposalContextMenu is disabled/cleared during reconcile() updates to avoid the
race. Reference: proposalContextMenu, proposal_url, Proposal, reconcile(),
m_data.

In `@src/qt/guiutil_font.cpp`:
- Around line 619-625: getScaledFont currently calls
g_font_registry.GetScaledFontSize(...) before calling getFont, causing the size
to be scaled twice; change getScaledFont to pass the unscaled size (baseSize *
multiplier) into getFont and let getFont (which calls
g_font_registry.GetScaledFontSize internally) perform the actual scaling. Locate
getScaledFont and replace the g_font_registry.GetScaledFontSize(...) argument
with the raw baseSize * multiplier while keeping the weight selection logic the
same.
🧹 Nitpick comments (5)
src/qt/proposalmodel.h (2)

20-48: Good design for the Proposal wrapper class.

The class cleanly encapsulates governance object data with cached QString fields. A few observations:

  1. The clientModel raw pointer is acceptable here since Proposal doesn't own it, but methods like isActive(), GetAbsoluteYesCount(), and votingStatus() dereference it without null checks. If a Proposal is ever used after clientModel becomes invalid, this could cause a crash.

  2. Consider adding a brief comment documenting the lifetime requirement (e.g., clientModel must outlive the Proposal).


89-91: Minor: Redundant empty check in isValidRow.

The !m_data.empty() check is redundant since row >= 0 && row < rowCount() already handles the empty case (when m_data is empty, rowCount() returns 0, so any row >= 0 will fail the row < rowCount() check).

Simplified version
-    bool isValidRow(int row) const { return !m_data.empty() && row >= 0 && row < rowCount(); }
+    bool isValidRow(int row) const { return row >= 0 && row < rowCount(); }
src/qt/proposalmodel.cpp (3)

28-32: Early return leaves Proposal in partially initialized state.

If prop_data.read() fails, the constructor returns early with m_hash set but all other fields (m_title, m_startDate, m_endDate, m_paymentAmount, m_url) at their default values. This could result in proposals being displayed with a hash but empty title and dates.

Consider either:

  1. Throwing an exception or using a factory that returns std::optional<Proposal>
  2. Adding an isValid() method to check if parsing succeeded
  3. Documenting this behavior is intentional for malformed governance objects

95-103: Unused parameter warnings.

The index parameter is unused in rowCount() and columnCount(), which is expected for flat (non-hierarchical) models. Consider using Q_UNUSED to suppress compiler warnings.

Suppress unused parameter warnings
 int ProposalModel::rowCount(const QModelIndex& index) const
 {
+    Q_UNUSED(index)
     return static_cast<int>(m_data.size());
 }

 int ProposalModel::columnCount(const QModelIndex& index) const
 {
+    Q_UNUSED(index)
     return Column::_COUNT;
 }

136-164: Unreachable code: break statements after return.

Lines 138 and 161 contain break statements that are unreachable because the inner switch cases either return or fall through to default which returns. The outer switch at line 163 also has unreachable code paths.

Remove unreachable breaks
         default:
             return {};
-        };
-        break;
+        }
     }
     case Qt::EditRole:
     {
         // Edit role is used for sorting, so return the raw values where possible
         switch (index.column()) {
         ...
         default:
             return {};
-        };
-        break;
+        }
     }
-    };
+    }
     return {};

Comment on lines 186 to 188
proposalContextMenu->clear();
proposalContextMenu->addAction(proposal_url, proposal, &Proposal::openUrl);
proposalContextMenu->addAction(proposal_url, [proposal]() { proposal->openUrl(); });

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Potential dangling pointer in context menu lambda.

The lambda [proposal]() { proposal->openUrl(); } captures a raw pointer to a Proposal owned by m_data. If reconcile() is called between when the context menu is displayed and when the user clicks the action (removing this proposal), the captured pointer would dangle.

This is a low-probability race condition since:

  1. The update timer interval is typically several seconds
  2. The proposal would need to be removed in that exact window

Consider capturing by hash and looking up the proposal when the action is triggered, or disabling the menu during updates.

Safer alternative using hash lookup
     // right click menu with option to open proposal url
-    QString proposal_url = proposal->url();
+    const QString proposal_url = proposal->url();
+    const QString proposal_hash = proposal->hash();
     proposal_url.replace(QChar('&'), QString("&&"));

     proposalContextMenu->clear();
-    proposalContextMenu->addAction(proposal_url, [proposal]() { proposal->openUrl(); });
+    proposalContextMenu->addAction(proposal_url, [this, proposal_hash]() {
+        // Re-lookup proposal to avoid dangling pointer if reconcile() ran
+        for (int row = 0; row < proposalModel->rowCount(); ++row) {
+            if (auto* p = proposalModel->getProposalAt(proposalModel->index(row, 0)); 
+                p && p->hash() == proposal_hash) {
+                p->openUrl();
+                return;
+            }
+        }
+    });
🤖 Prompt for AI Agents
In `@src/qt/governancelist.cpp` around lines 186 - 188, The context menu action
currently captures a raw Proposal* (proposal) which can dangle if reconcile()
modifies m_data before the user clicks; change the lambda to capture a stable
identifier (e.g., proposal->getHash() or id) instead of the pointer and, inside
the action, look up the Proposal from m_data (or the container that owns
proposals) and only call openUrl() if the lookup returns a valid pointer;
alternatively, ensure proposalContextMenu is disabled/cleared during reconcile()
updates to avoid the race. Reference: proposalContextMenu, proposal_url,
Proposal, reconcile(), m_data.

@kwvg kwvg marked this pull request as ready for review January 22, 2026 14:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant