feat(gui): scalable SVG icons for toolbar and preferences on hi-DPI - #487
Conversation
The toolbar and Preferences page-list icons were fixed-size raster art (32x32 / 16x16) embedded in the executable. Because the GUI is per-monitor-DPI aware, those bitmaps were drawn at their physical pixel size and looked tiny and blurry on 4K and other hi-DPI screens. This migrates them to resolution-independent SVG so wx rasterizes each icon crisply at whatever logical size / DPI the widget requests. Pipeline: - embed_icons.py now embeds each icon's optional same-name .svg twin next to its PNG. AMuleIconEntry gains svg_data/svg_len. The PNG stays mandatory (raster fallback and natural size); an .svg with no .png twin is a build error. - CamuleArtProvider::CreateBitmapBundle() serves an SVG-backed wxBitmapBundle via wxBitmapBundle::FromSVG() when wx has SVG support (wxHAS_SVG), and otherwise falls back to the PNG plus a smooth 2x upscale. A malformed/unsupported SVG also degrades to the PNG path. - The main toolbar (Add_Skin_Icon) and the Preferences page list request bundles from the art provider; an active user skin still takes precedence and keeps its own PNG art. - CMake globs *.svg alongside *.png for the generated table; the checked-in icon_data.c fallback (used only when Python3 is absent at configure time) is regenerated to match. Artwork: - 28 toolbar/preferences icons redrawn as flat, NanoSVG-compatible SVGs (shapes/paths/gradients only), plus an SVG for the IP2Country pin. The Linux Tux (Directories) reuses the Crystal Project penguin and the Events mule follows the eMule mascot. Notes for reviewers: - Requires wx >= 3.2 (already the project minimum). NanoSVG is on by default in wx's CMake and autotools builds; without it the icons fall back to the embedded PNGs, so nothing breaks. - The generic wxListCtrl on wxGTK/wxOSX only selects the hi-res rendition from wx 3.3; wxMSW uses it from 3.2. - Licensing: the Tux is Crystal Project (LGPL, Everaldo); the mule follows the eMule project logo -- please confirm attribution suits the project before merge. Co-Authored-By: Claude Opus 4.8 <[email protected]>
# Conflicts: # src/PrefsUnifiedDlg.cpp
clang-tidy Tier-2 (modernize-use-nullptr) flagged the two NULL comparisons on the newly added lines. Switch them to nullptr. Co-Authored-By: Claude Opus 4.8 <[email protected]>
|
Really nice work — the pipeline is clean, and the PNG-fallback + skin-precedence design plus the edge-case handling (UTF-8 write for mingw, 1. Reuse our existing locale-safe lowercasing. For the toolbar art-id fold in #include "CCtypeAsciiScope.h"
...
CCtypeAsciiScope scope;
const wxString artName = iconName.Lower();2. Licensing. Thanks for flagging it — the main thing to settle, and it's really just the two non-original icons: Directories (Crystal Project Tux, LGPL 2.1, © Everaldo) and Events (derived from the eMule mascot). Everything else is your own redraw and is fine. For the LGPL Tux, a Simplest path: since the rest of the set is already your own art, replacing just those two — with original drawings (a generic folder / a generic events glyph) or CC0 / public-domain equivalents — avoids the attribution-in-app requirement entirely. We can go either way; the team should weigh in. 3. Screenshots. Could you attach before/after shots of the new toolbar + Preferences list on a hi-DPI display? The payoff is entirely visual and none of us has eyes on it yet, so it'd let the team review the artwork and agree on direction. |
|
One more minor point I should have included above — in image = image.Scale(size, ...); // natural -> requested size
wxImage image2x = image.Scale(2x, ...); // 2x of the *rescaled* imageThat's a scale-of-a-scale (two lossy resamples). Deriving the 2× from the original instead — |
- Add_Skin_Icon: fold the toolbar art id with the existing CCtypeAsciiScope RAII helper (pins LC_CTYPE to "C") plus wxString::Lower(), instead of a hand-rolled ASCII loop. Matches the locale-safe country-code lowercasing in MaxMindDBDatabase.cpp that feeds the same CamuleArtProvider. - CamuleArtProvider::CreateBitmapBundle: in the PNG fallback, derive both the 1x and 2x renditions from the original decoded image so each is a single high-quality resample, instead of building the 2x from the already-rescaled 1x (scale-of-a-scale). Co-Authored-By: Claude Opus 4.8 <[email protected]>
…icon The Preferences "Directories" icon embeds the Tux penguin from the Crystal Project icon set (Everaldo Coelho, LGPL v2.1). LGPL 2.1 §6(c) requires the credit to appear among the copyright notices shown to the user, so add it to the About dialog (untranslated, alongside the existing notices) and document the component in docs/THIRDPARTY.md next to the picojson / muleunit entries. Co-Authored-By: Claude Opus 4.8 <[email protected]>
|
Thanks for the quick turnaround on the review points! On the licensing though — the LGPL About-dialog credit you added is technically correct, but on reflection carrying a permanent in-app copyright notice (+ THIRDPARTY.md entry + license text) for a single icon feels disproportionate. Since you've already hand-drawn the other 28 icons, the cleaner path is to do the same for the two derived ones:
Either way keeps the app free of a per-icon copyright notice. |
# Conflicts: # src/CMakeLists.txt
…tion The Preferences "Directories" icon was redrawn from the Crystal Project Tux (LGPL v2.1), which obliged a permanent in-app copyright notice plus a THIRDPARTY.md entry and license reference for a single icon. Per reviewer feedback (amule-org#487) that is disproportionate, so replace it with original, hand-drawn art that carries no third-party derivation: - src/icons/prefs_directories.svg: original amber folder glyph, NanoSVG-safe - src/AboutDialog.cpp: drop the Crystal Project credit line - docs/THIRDPARTY.md: drop the "Crystal Project icons" section - src/icons/icon_data.c: regenerate (Directories SVG twin updated) The Events icon keeps the eMule mascot: it is GPL-consistent with aMule (which is itself based on eMule) and carries no separate attribution. Co-Authored-By: Claude Opus 4.8 <[email protected]>
Following the same reviewer feedback (amule-org#487), replace the Events icon — previously traced from the eMule mascot — with an original notification bell drawn from scratch, so the icon tree carries no derived art at all and needs no attribution. - src/icons/prefs_events.svg: original bell glyph, NanoSVG-safe - src/icons/icon_data.c: regenerate (Events SVG twin updated, ~91KB -> ~0.7KB) Co-Authored-By: Claude Opus 4.8 <[email protected]>
The Events SVG became a bell in 6989700, but its PNG twin (prefs_events.png) still held the old eMule mascot — so the raster fallback path and any non-SVG build still rendered the mascot. Replace the PNG with the bell so the Events icon is the bell across every render path. - src/icons/prefs_events.png: bell raster (was the eMule mascot) - src/icons/icon_data.c: regenerate Co-Authored-By: Claude Opus 4.8 <[email protected]>
Same fix as the Events bell: the Directories SVG is the folder, but its PNG twin still held the old penguin, so the raster fallback path and non-SVG builds still rendered the penguin. Replace the PNG with the folder so Directories is consistent across every render path. - src/icons/prefs_directories.png: folder raster (was the old penguin) - src/icons/icon_data.c: regenerate Co-Authored-By: Claude Opus 4.8 <[email protected]>
|
All the review points are addressed — the I'm building on all platforms to verify, and I'll post screenshots here so the team can weigh in on the artwork. Thanks for the quick iterations! |
I'm adding more icons, as I forgot to rework several icons of the main window. |
Migrate the remaining hand-pixelled main-window images to the SVG art
pipeline: the status bar (log "i", users, up/down speed arrows and the
eD2k/Kad connection globe), the transfer window's clear-completed check
(+ its disabled state) and the show/hide-sources chevron toggles.
- src/icons/: 18 new icons. Each PNG twin is the original bitmap,
extracted from muuli_wdr.cpp's embedded RGB/XPM data, so the raster
fallback stays pixel-identical to today's art; each SVG is a
hand-drawn remaster. The globe keeps the original composition
scheme -- a base globe plus one overlay arrow per network state --
with identical arrow geometry across base and overlays so the
overlays cover the base exactly at any raster scale.
- muuli_wdr.cpp: the status bar and transfer window widgets take
their art from wxArtProvider bundles ("amule:" ids); the now-unused
dlStatusImages()/connImages() image tables are removed.
- amuleDlg.cpp: ShowConnectionState composes the globe from bundles
rasterized at the window's DPI scale (GetBitmapFor), replacing the
wxImageList overlay draw onto the widget's current bitmap;
ShowTransferRate picks the speed-state art id directly.
- TransferWnd/SharedFilesWnd: the sources-list toggles use the
bundle-aware SetBitmapPressed/SetBitmapCurrent setters (the legacy
SetBitmapSelected/SetBitmapHover aliases are wxBitmap-only).
Co-Authored-By: Claude Opus 4.8 <[email protected]>
OK, I tested on all platforms MacOS, Windows, Linux and they all render correctly. Navigation bar:
Preferences panel:
Windows screenshots are taken with Display scaling at 175%. @ngosang @danim7 @mrjimenez your feedback appreciated before we can merge this. |
Co-Authored-By: Claude Opus 4.8 <[email protected]>
|
Btw @Zeyckler, if you're comfortable working with SVG, we could use a hand updating the aMule logo as well. I put together an SVG logo with SVGGenie AI It's not perfect, but it could serve as a starting point. |
…e-avatar users, chevron arrows) Address review feedback on the newly-added status icons: - status_conn_*: the connection arrows are now curved arrows converging on the globe centre, matching the original artwork -- the Kad arrow (top-right) points south-west and the eD2k arrow (bottom-left, its 180-degree rotation) points north-east. The eD2k/Kad -> corner mapping matches the original (eD2k bottom-left, Kad top-right) so each network's colour lands in the right corner. - status_users: a single avatar (head + torso), not two figures. - arrows_down / arrows_up: three distinct stacked chevron-arrows instead of a solid tree; arrows_up is arrows_down mirrored vertically (same geometry). SVG-only change; the PNG twins remain the extracted originals. icon_data.c regenerated. Co-Authored-By: Claude Opus 4.8 <[email protected]>
…zes, one-piece globe arrows) Further review feedback on the main-window icons: - status_users: single figure in a 3/4 pose -- head turned to look down-right, a distinct bent left arm, rounded turned back -- replacing the flat front-facing avatar. - arrows_down / arrows_up: three stacked triangles sized small -> medium -> big (biggest at the tip); the previous size order was reversed. arrows_up remains the vertical mirror of arrows_down. - status_conn_*: the two network arrows are now single-piece curved arrows (head and body one silhouette), shorter and thicker; the globe gains an Africa + Europe landmass instead of abstract green blobs. SVG-only; PNG twins stay the extracted originals. icon_data.c regenerated. Co-Authored-By: Claude Opus 4.8 <[email protected]>
… arrows, redrawn users) - arrows_down / arrows_up: the three triangles now stack with the SMALL one on top (front) and the big one at the back, matching the original (whose internal edges show the smaller triangle in front). Sizes stay small->medium->big; up remains the vertical mirror of down. - status_conn_*: each network arrow now starts at the globe rim -- Kad at the north-east edge, eD2k at the south-west -- and points inward, still a single short, thick one-piece curved arrow. - status_users: redrawn from scratch to match the original pose -- a single chunky figure, small head turned to look down-right, a rounded right shoulder/back and a distinct left arm. SVG-only; PNG twins unchanged. icon_data.c regenerated. Co-Authored-By: Claude Opus 4.8 <[email protected]>
- status_users: redrawn as a clean, glossy person (round head + smooth rounded shoulders, subtle turn/arm seam) instead of the lumpy silhouette. - status_conn_*: thicker one-piece arrows (chunkier head and body). - status_conn_base: pull the globe's top highlight fully inside the rim. SVG-only; PNG twins unchanged. icon_data.c regenerated. Co-Authored-By: Claude Opus 4.8 <[email protected]>
…ng users - status_conn_*: adopt the user-supplied curved-arrow artwork, baked into 16x16 coordinates (no transform attribute, NanoSVG-safe). Kept the NE (Kad) / SW (eD2k) positions and the inward-pointing direction; recoloured per state (red/yellow/orange/green with a darker outline) in place of black. - status_users: redrawn front-facing with both arms -- head, torso and two arms, kept the glossy green look. SVG-only; PNG twins unchanged. icon_data.c regenerated. Co-Authored-By: Claude Opus 4.8 <[email protected]>
…eing clipped - status_users: cut the torso flush with the bottom of the arms (a head-and- shoulders bust) instead of a long body. - transfer_clear_completed(+_disabled): inset the tick so it keeps a margin inside its viewBox. - muuli_wdr.cpp: the clear-completed button had collapsed toward the bitmap's best size (clipping the tick); give it a fixed 34x34 min size so the icon is no longer cut off. Co-Authored-By: Claude Opus 4.8 <[email protected]>
ShowConnectionState drew the Kad/eD2k overlay arrows straight into the
bitmap returned by GetBitmapBundle("amule:status_conn_base").GetBitmapFor().
wxArtProvider caches that bundle and wxBitmapBundleImplSVG caches its
rasterized bitmap, handing it back copy-on-write; a wxMemoryDC draws into
the shared pixel buffer without unsharing it. So the overlays were being
stamped into the cached base globe and accumulated on every later state
change (and poisoned the base for any other consumer of the bundle).
Deep-copy the base into a private bitmap (preserving the DPI scale factor)
before compositing the overlays onto it.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
The red plug cable in prefs_connection.svg and its twin toolbar_connect.svg dipped past the 0 0 32 32 viewBox (the 3.4-wide stroke reached y~=33.7), so its rounded tip was clipped flat against the icon's bottom edge. Raise the lower curve of both cord strokes to keep the whole stroke within bounds, and regenerate icon_data.c for the two updated SVGs. Also harden embed_icons.py: error out if two icons resolve to the same art id or C identifier. sanitise() folds '-'/'.' to '_', so distinct names could silently collide into a duplicate C symbol; not triggered by the current icon set, but a future rename now fails loudly at generation time. Co-Authored-By: Claude Opus 4.8 <[email protected]>
wxBitmapBundle::GetBitmapFor() returns an *unscaled* bitmap (scale factor 1.0) whose pixel size already matches the window DPI. Setting the composed globe on the status-bar wxStaticBitmap as-is rendered it DPI-scale times larger than the sibling icons (info, users, speed), which are bundle-backed and size themselves in logical units -- clearly oversized on hi-DPI displays. Stamp the window's DPI scale factor on the composite so its logical size matches the siblings (a no-op at 100% DPI). Verified in-app at hi-DPI: the globe now measures the same height as the users/speed icons. Co-Authored-By: Claude Opus 4.8 <[email protected]>
|
Here's how the icons look in a local build on a hi-DPI display (Windows, 4K @ 225% scaling), where the vector art is the whole point — everything is rasterized by wx at the display's scale instead of upscaling the old 16/32px bitmaps. Main window — the toolbar glyphs (Disconnect, Networks, Searches, Downloads, Shared files, Messages, Statistics, Preferences, Import, About) plus the status-bar icons (info, users, up/down speed, and the composed eD2k/Kad connection globe): Preferences — the page-list icons down the left side: |
Replace the three hand-drawn continent blobs with actual coastlines extracted from a proper Earth-globe SVG (Americas, Europe/Africa/Asia, northern islands), baked through the source's full transform chain into plain NanoSVG-safe paths and remapped onto our globe so coastlines that touch the source's circle meet the rim's border line. All landmasses share one uniform green, and the polar gloss is dropped -- the flatter, detailed globe reads better at status-bar size. icon_data.c regenerated. Co-Authored-By: Claude Opus 4.8 <[email protected]>
got3nks: the red arrows read well, but green and yellow blend into the globe. Give every overlay arrow a bolder outline (stroke 0.4 -> 0.6) and darken the green/yellow/orange outline tones so the boundary does the work the hue cannot; red keeps its outline colour. Lighten the globe's rim (#0d3b6e -> #1a5da8) so the darkest edges in the icon are the arrows, not the border -- the arrows now lead the composition. icon_data.c regenerated. Co-Authored-By: Claude Opus 4.8 <[email protected]>
Co-Authored-By: Claude Opus 4.8 <[email protected]>
|
a29e152 takes the outline route: every overlay arrow gets a bolder outline (0.4 → 0.6 units at 16px) and the green/yellow/orange outlines go darker, so the boundary does the work the hue can't — red keeps its color since that one already worked. On top of that the globe's rim is lightened a step, so the darkest edges in the icon now belong to the arrows rather than the border. Bonus from the same session (fdc2838): the hand-drawn continent blobs got replaced with real coastlines extracted from a proper Earth-globe SVG, recolored to the icon's green. Green / yellow / red pairs, before → after, dark and light bars: Same request as always — a check on your dark setup would be great. |
|
@got3nks do you think it would be worthy to use the same globe (colors, shape, etc.) in the "Networks" and "Connection" (in settings)? So the 3 icons could share the globe part. |
|
Not sure, I'd probably keep them different |
got3nks on macOS: the overlay arrows are still hard to read at status-bar size; bigger is the reliable fix. The arrows were enlarged by hand in the SVG editor, then normalized: the SW arrow is the exact 180-degree point reflection of the NE one, and the pair is balanced to an isometric 14.8x14.8 footprint with equal 0.30 canvas margins on all four sides -- the arrows now use practically the whole 16x16 box. All seven state overlays regenerated from the same geometry; editor cruft (style attributes) stripped; icon_data.c updated. Co-Authored-By: Claude Opus 4.8 <[email protected]>
Co-Authored-By: Claude Opus 4.8 <[email protected]>
|
Great timing — I'd just finished doing exactly that. aba924f enlarges the arrows substantially (I resized them by hand in the SVG editor, then normalized the pair): they now use practically the whole 16×16 box — a 14.8×14.8 isometric footprint with equal 0.3-unit margins, the SW arrow being the exact 180° reflection of the NE one. Current (what you screenshotted) vs the enlarged version, green/yellow/red pairs on dark and light bars: If macOS still wants more after this, I'm happy to take you up on the wider status-bar slot idea — with the SVG pipeline the glyph can render at any logical size, so widening the slot is purely a muuli layout tweak. |
got3nks compared the SVG glyph against the original raster side by side: the old art's arrows are bolder and larger, and asked for the SVG to match. Rework the arrows with substantially bigger chevron heads and shorter bodies (hand-drawn in the SVG editor), normalized as before: the SW arrow is the exact 180-degree reflection of the NE one, and the pair is balanced to an isometric 14.84x14.84 footprint with equal 0.28 canvas margins. All seven state overlays regenerated from the same geometry; icon_data.c updated. Co-Authored-By: Claude Opus 4.8 <[email protected]>
Co-Authored-By: Claude Opus 4.8 <[email protected]>
|
Agreed — next to the original the arrows were still too light. 7ed6060 reworks them with substantially bigger chevron heads and shorter bodies (redrawn by hand again), normalized as usual: both arrows identical (the SW one is the exact 180° reflection of the NE one) and the pair balanced to an isometric 14.84×14.84 footprint with equal margins. Original PNG vs the previous SVG vs the new one, on dark and light bars: Hopefully this closes the gap on macOS — and the wider-slot option stays on the table if you'd like even more presence. |
|
@Zeyckler I took a pass at making the connection-globe arrows more readable at status-bar size — bolder solid arrows with big heads and a thin outline, a brighter green, offset into two lanes so they pass as an exchange (heads keep a gap), tails reaching the corners. Consider it a starting point rather than a final design — feel free to refine it, tweak the shape/colours, or take it in a different direction entirely. I pushed it as a branch on my fork so you can pull it in and build on it: It's based on your current branch head, so it should apply cleanly. It touches only the eight Preview: |
|
@got3nks I don't like that straight arrow 😅 ; it loses the "curve" around the globe. IMHO, my design is more accurate to the original artwork, but as I said, I'm not a graphic designer, so I'm doing my best with AI and manual editing of the SVG in Boxy SVG Editor. Have you tried my last design on your Mac? Maybe it is enough for this first iteration of SVG migration. |
|
That would help, but I’d also try to make their body bigger |
Further legibility pass from the review thread (bigger arrow bodies, higher contrast). Scale the whole arrow up ~10% about its centroid so both head and body grow -- the pair now fills a 15.2x15.2 footprint with the heads reaching the canvas edge and the tails pulled inward. Brighten the arrow fills (lift the gradient's base/deep stops; keep the dark outlines for the edge), and tone the globe down a notch (sea, continents and rim each moved partway toward a muted blue-green) so the arrows carry the icon. Symmetry/isometry preserved: SW arrow is the exact 180-degree reflection of NE, equal canvas margins. All seven overlays regenerated; icon_data.c updated. Co-Authored-By: Claude Opus 4.8 <[email protected]>
Co-Authored-By: Claude Opus 4.8 <[email protected]>
|
Done — 27d2432 pushes on all three fronts at once:
Symmetry/isometry preserved as before (SW = exact 180° of NE, equal margins). Before → after, green/yellow/red on dark and light bars: If the arrows want even more presence, the wider status-bar slot is still on the table — the SVG renders at any logical size, so it's just a muuli layout tweak whenever you want it. |
|
Looks promising. Will build and test it tomorrow, thanks @Zeyckler. |
|
Tested locally on a hi-DPI display (master + this branch) — the toolbar, status, and preferences icons all render crisply at scale, and the connection arrows read much better after the latest tweak. Really nice work, @Zeyckler — this is a great quality-of-life improvement for the GUI, and the embedded-SVG approach via the art provider keeps it dependency-free. Merging now. Thanks for the contribution! |
|
Now that this is merged — if you'd like to keep going, @Zeyckler, here's what's still on the old raster path after this PR, in case you want to extend the SVG coverage: Just need an SVG twin — the art provider picks these up automatically once a matching
Still on the legacy
No pressure — the toolbar, status bar, and preferences work you already landed is the high-visibility win. Just flagging it in case you'd like to take it further. Also a heads-up: we're preparing a 3.1.0 release targeted for August, and your SVG work will be part of it. Thanks again for the contribution! |
|
@Zeyckler In Linux dark theme the networks icon(s) in the toolbar looks darker and smaller than the other icons. |
…ree, and tab-close icons to CamuleArtProvider Continues the icon-system cleanup scoped in amule-org#675, independent of amule-org#732/amule-org#733 (different files, no overlap). - amuleDlgImages(14)/(15): the small header icons in the Friends and Messages tabs. Friends gets a new "amule:friends" SVG; Messages reuses the existing "amule:toolbar_messages" art, requested at an explicit 16x16 so it doesn't inherit the toolbar's 32x32 natural size. - amuleDlgImages(18)/(30): the "reload list" buttons (shared files, ED2K server list, Kad node list) -- three call sites, one new "amule:reload" SVG (see below for its own history). - amuleSpecial(1)/(2): the shared-directory tree's folder icons get new "amule:folder"/"amule:folder_shared" SVGs -- same shape, tinted orange vs. red, matching how the original raw bitmaps only differed by colour. - amuleSpecial(3)/(4): the chat/search notebook tabs' close-on-hover icon, replaced with wx's own stock wxART_CLOSE for both states instead of a second bespoke asset (both were the same "X in a box" bitmap, differing only by a hover-highlight border colour). reload.svg's own history, per PR review: the first hand-drawn attempt (thin blue single arc) didn't match the original at all -- got3nks caught it, I reconstructed the original amuleDlgImages(18) raster to check (a thicker green double-arrow circle) and redrew closer to that, which still wasn't good enough. Final version is AI-vectorized via Recraft (through the Higgsfield MCP), which got3nks preferred over further hand-drawn iterations. src/icons/icon_data.c (the checked-in fallback used when Python3 is absent at configure time, per amule-org#487) regenerated via embed_icons.py to match. Rebased onto current master (picking up amule-org#732/amule-org#733/amule-org#739, which all touch the same amuleSpecial/amuleDlgImages functions) -- conflicts resolved by redoing the index deletions against the current tree rather than replaying the stale patch, since amule-org#725/amule-org#733 already moved the surrounding line numbers. Verified via a full amule build (macOS) and a visual check of every call site: Friends/Messages tab headers, the ED2K server-list and Kad node-list reload buttons, the shared-files reload button, and the Preferences > Directory shared-folder tree.
…or tab-close (#675) (#735) * feat(gui): migrate Friends/Messages headers, reload buttons, folder tree, and tab-close icons to CamuleArtProvider Continues the icon-system cleanup scoped in #675, independent of #732/#733 (different files, no overlap). - amuleDlgImages(14)/(15): the small header icons in the Friends and Messages tabs. Friends gets a new "amule:friends" SVG; Messages reuses the existing "amule:toolbar_messages" art, requested at an explicit 16x16 so it doesn't inherit the toolbar's 32x32 natural size. - amuleDlgImages(18)/(30): the "reload list" buttons (shared files, ED2K server list, Kad node list) -- three call sites, one new "amule:reload" SVG (see below for its own history). - amuleSpecial(1)/(2): the shared-directory tree's folder icons get new "amule:folder"/"amule:folder_shared" SVGs -- same shape, tinted orange vs. red, matching how the original raw bitmaps only differed by colour. - amuleSpecial(3)/(4): the chat/search notebook tabs' close-on-hover icon, replaced with wx's own stock wxART_CLOSE for both states instead of a second bespoke asset (both were the same "X in a box" bitmap, differing only by a hover-highlight border colour). reload.svg's own history, per PR review: the first hand-drawn attempt (thin blue single arc) didn't match the original at all -- got3nks caught it, I reconstructed the original amuleDlgImages(18) raster to check (a thicker green double-arrow circle) and redrew closer to that, which still wasn't good enough. Final version is AI-vectorized via Recraft (through the Higgsfield MCP), which got3nks preferred over further hand-drawn iterations. src/icons/icon_data.c (the checked-in fallback used when Python3 is absent at configure time, per #487) regenerated via embed_icons.py to match. Rebased onto current master (picking up #732/#733/#739, which all touch the same amuleSpecial/amuleDlgImages functions) -- conflicts resolved by redoing the index deletions against the current tree rather than replaying the stale patch, since #725/#733 already moved the surrounding line numbers. Verified via a full amule build (macOS) and a visual check of every call site: Friends/Messages tab headers, the ED2K server-list and Kad node-list reload buttons, the shared-files reload button, and the Preferences > Directory shared-folder tree. * fix(gui): thicker reload stroke, dedicated message-bubble icon (#735 review) got3nks, testing #735: - reload: arrow bodies read too thin at 16px. Regenerated via Recraft with an explicit thicker/bolder-stroke prompt. - Messages panel header: reusing "amule:toolbar_messages" (the main toolbar's detailed gradient mascot bust) at 16x16 doesn't read as "messages" once shrunk that far from its 32x32 native size. Added a dedicated "amule:message" chat-bubble glyph instead, sized for legibility at 16x16 specifically, and pointed the Messages panel header at it instead of the toolbar art. Folder/folder_shared and the tab-close X were already approved as-is. Verified via a full amule build (macOS) and a visual check of both fixes: the shared-files reload button and the Messages panel header.











Summary
The main-toolbar buttons and the Preferences page-list icons were fixed-size raster art (32×32 / 16×16) embedded in the executable. Because the GUI is per-monitor-DPI aware, those bitmaps get drawn at their physical pixel size and look tiny/blurry on 4K and other hi-DPI displays. This migrates them to resolution-independent SVG, so wx rasterizes each icon crisply at whatever logical size / DPI the widget asks for.
Pipeline
embed_icons.pynow embeds each icon's optional same-name.svgtwin next to its PNG.AMuleIconEntrygainssvg_data/svg_len. The PNG stays mandatory — it's the raster fallback and defines the icon's natural size — so an.svgwith no matching.pngis a build error.CamuleArtProvider::CreateBitmapBundle()serves an SVG-backedwxBitmapBundleviawxBitmapBundle::FromSVG()when wx has SVG support (wxHAS_SVG); otherwise (or if an SVG fails to parse) it falls back to the PNG plus a smooth 2× upscale.Add_Skin_Icon) and the Preferences page list pull bundles from the art provider. An active user skin still takes precedence and keeps its own PNG art.*.svgalongside*.pngfor the generated table; the checked-inicon_data.cfallback (used only when Python3 is absent at configure time) is regenerated to match.Artwork
Notes for reviewers
wxListCtrlon wxGTK/wxOSX only selects the hi-res rendition from wx 3.3; wxMSW uses it from 3.2. Toolbar (wxToolBar) benefits everywhere.Test plan
text/filter/mask/clipPath/use/image/pattern/xlink).icon_data.cfallback is byte-in-sync with the current.svg/.pngset (29 SVG twins embedded).