forked from bitcoin/bitcoin
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
feat(qt): UI refresh (2/n, update "Governance" Tab layout, lifecycle icons, description dialogs, resumable proposal creation) #7110
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
30 commits
Select commit
Hold shift + click to select a range
a773635
qt: move masternode list fetch logic to thread and use debounce timer
kwvg 5af2bb9
qt: expose `NotifyGovernanceChanged` signal to UI code
kwvg c85e5cb
qt: move proposal list fetch to thread and use debounce timer
kwvg a7328e9
qt: add moon phase icons for governance cycle indicator
kwvg 662703e
qt: add governance cycle status bar icon
kwvg 8257eac
qt: decouple chain sync indicator from governance sync
kwvg 9bb7b0a
qt: reorder columns in "Governance" tab, make width elastic for key cols
kwvg b2b9aa1
qt: change layout of "Governance" tab controls for readability
kwvg c0d8454
qt: report compact voting status and expanded details in tooltip
kwvg f45fe85
qt: use monospace font for hashes in "Governance" tab
kwvg 4295471
qt: report proposal info using QTextEdit instead of an alert with JSON
kwvg 552f591
qt: report more proposal information in description, calculate payments
kwvg af65c0e
qt: cleanup proposal context menu, add copy JSON and visit URL options
kwvg 5f95737
qt: add voting ballot icon
kwvg 28b67d3
qt: replace "Active" column with icons that reflect voting status
kwvg 1a1d56e
qt: list locally recorded proposals in "Governance" tab
kwvg 2d8cffe
qt: add blank canvas if there's no proposals to display
kwvg 16314e7
qt: adjust layout of details page of proposal wizard to resemble DGT
kwvg 3ca061f
qt: drop JSON/hex confirmation page, use description dialogs instead
kwvg 90e0375
qt: use `SendConfirmationDialog` instead of `QMessageBox` for confirm
kwvg 2883527
qt: remove remaining pages from proposal wizard
kwvg 080c8fb
qt: add "Resume Proposal" for post-confirmation broadcast
kwvg a0da7de
qt: disable proposal buttons until synced, creation button w/o funds
kwvg 9fb0c35
qt: make governance clock opt-in
kwvg d1e67ff
fix: disable governance UI when node runs with `--disablegovernance`
kwvg 6cb7a40
qt: add pending status to signal unbroadcast proposals
kwvg a5ae10c
fix: correct tooltip display unit bug, IBD status flickering
kwvg 3d53afa
fix: hold `cs_main` to for consistent state when fetching GovernanceInfo
kwvg ce94c3e
fix: improve responsiveness in update after user interaction
kwvg 707ce77
refactor: `qt/proposalwizard.{cpp,h}` -> `qt/proposalcreate.{cpp,h}`
kwvg File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,126 @@ | ||
| #!/usr/bin/env python3 | ||
| # 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. | ||
|
|
||
| import math | ||
| import os | ||
| import struct | ||
| import zlib | ||
|
|
||
| OUTPUT_PX = 64 | ||
| CENTER = OUTPUT_PX / 2 | ||
| RADIUS = 26 | ||
| STROKE_WIDTH = 2.0 | ||
| INNER_GAP_INSET = 3.0 | ||
|
|
||
|
|
||
| def make_png(pixels): | ||
| """Encode RGBA pixel data as a PNG file.""" | ||
| raw = bytearray() | ||
| for y in range(OUTPUT_PX): | ||
| raw.append(0) # filter byte: None | ||
| for x in range(OUTPUT_PX): | ||
| raw.extend(pixels[y][x]) | ||
|
|
||
| def chunk(chunk_type, data): | ||
| c = chunk_type + data | ||
| return struct.pack('>I', len(data)) + c + struct.pack('>I', zlib.crc32(c) & 0xFFFFFFFF) | ||
|
|
||
| ihdr = struct.pack('>IIBBBBB', OUTPUT_PX, OUTPUT_PX, 8, 6, 0, 0, 0) # 8-bit RGBA | ||
| idat = zlib.compress(bytes(raw), 9) | ||
|
|
||
| out = b'\x89PNG\r\n\x1a\n' | ||
| out += chunk(b'IHDR', ihdr) | ||
| out += chunk(b'IDAT', idat) | ||
| out += chunk(b'IEND', b'') | ||
| return out | ||
|
|
||
|
|
||
| def moon_frame(frame): | ||
| """ | ||
| frame 0 = new moon (filled disc + thin inner outline gap) | ||
| frame 1 = waxing crescent (small sliver on right) | ||
| frame 2 = first quarter (right half illuminated) | ||
| frame 3 = waxing gibbous (mostly illuminated) | ||
| frame 4 = full moon (outline only) | ||
| frame 5 = waning gibbous (mostly illuminated, from left) | ||
| frame 6 = last quarter (left half illuminated) | ||
| frame 7 = waning crescent (small sliver on left) | ||
| """ | ||
| if frame > 4: | ||
| # Waning: horizontal mirror of the corresponding waxing frame | ||
| pixels = moon_frame(8 - frame) | ||
| for y in range(OUTPUT_PX): | ||
| pixels[y] = pixels[y][::-1] | ||
| return pixels | ||
|
|
||
| # Precompute frame-specific parameters | ||
| if frame == 0: | ||
| gap_outer = RADIUS - INNER_GAP_INSET | ||
| gap_inner = gap_outer - STROKE_WIDTH | ||
| else: | ||
| frame_angle = frame * math.pi / 4 | ||
|
|
||
| pixels = [[(0, 0, 0, 0)] * OUTPUT_PX for _ in range(OUTPUT_PX)] | ||
| for y in range(OUTPUT_PX): | ||
| for x in range(OUTPUT_PX): | ||
| dx = x - CENTER + 0.5 | ||
| dy = y - CENTER + 0.5 | ||
| dist = math.sqrt(dx * dx + dy * dy) | ||
| if dist > RADIUS + 1: | ||
| continue | ||
| disc_alpha = min(1.0, RADIUS + 1 - dist) | ||
| if frame == 0: | ||
| # New moon: filled disc with a thin inner outline gap | ||
| if dist < gap_inner - 0.5: | ||
| fill = 1.0 | ||
| elif dist < gap_inner + 0.5: | ||
| fill = gap_inner + 0.5 - dist | ||
| elif dist < gap_outer - 0.5: | ||
| fill = 0.0 | ||
| elif dist < gap_outer + 0.5: | ||
| fill = dist - (gap_outer - 0.5) | ||
| else: | ||
| fill = 1.0 | ||
| else: | ||
| # Frames 1-4: terminator (shadow boundary) + outline | ||
| if abs(dy) >= RADIUS: | ||
| terminator_x = 0 | ||
| else: | ||
| half_chord = math.sqrt(RADIUS * RADIUS - dy * dy) | ||
| terminator_x = math.cos(frame_angle) * half_chord | ||
| edge_dist = terminator_x - dx | ||
| if edge_dist > 1: | ||
| dark_alpha = 1.0 | ||
| elif edge_dist < -1: | ||
| dark_alpha = 0.0 | ||
| else: | ||
| dark_alpha = (edge_dist + 1) / 2 | ||
| outline_alpha = 0.0 | ||
| inner_edge = RADIUS - STROKE_WIDTH | ||
| if dist > inner_edge: | ||
| outline_alpha = min(1.0, (dist - inner_edge) / STROKE_WIDTH) | ||
| fill = max(dark_alpha, outline_alpha) | ||
| alpha = int(max(0, min(255, disc_alpha * fill * 255))) | ||
| if alpha > 0: | ||
| pixels[y][x] = (0, 0, 0, alpha) | ||
| return pixels | ||
|
|
||
|
|
||
| def main(): | ||
| script_dir = os.path.dirname(os.path.abspath(__file__)) | ||
| repo_root = os.path.join(script_dir, '..', '..') | ||
| icon_dir = os.path.join(repo_root, 'src', 'qt', 'res', 'icons') | ||
| for frame in range(8): | ||
| pixels = moon_frame(frame) | ||
| png_data = make_png(pixels) | ||
| filename = f'moon_{frame}.png' | ||
| filepath = os.path.join(icon_dir, filename) | ||
| with open(filepath, 'wb') as f: | ||
| f.write(png_data) | ||
| print(f' {filename} ({len(png_data)} bytes)') | ||
|
|
||
|
|
||
| if __name__ == '__main__': | ||
| main() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix clang-format drift in this hunk (CI failure).
The clang-format check reports differences in this file; please run clang-format/clang-format-diff for governance.cpp before merge.
🤖 Prompt for AI Agents