Icons: the Selection arrow is black and Direct Selection's white in every theme

Icons are drawn in the theme's colour. The Selection tool's filled arrow
therefore came out light in the dark themes and the Direct Selection
tool's hollow one dark, so they looked swapped to anyone who knows the
black Selection and white Direct Selection arrows (reported by a user).
Both arrows now paint a fill in a fixed colour (black, white;
dc-arrow-fill.svg, original) under their outline in the theme's colour.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
bflatastic 2026-10-11 14:57:47 +07:00
parent 0089e7bb8b
commit 2afd0b659c
6 changed files with 46 additions and 3 deletions

View File

@ -98,6 +98,7 @@ Generated-in-code art is original and has no file to list. This covers the defau
| `assets/icons/dc-arc.svg` | VectorCraft contributors | Original work, drawn for VectorCraft | MIT OR Apache-2.0 | |
| `assets/icons/dc-arrow-down.svg` | VectorCraft contributors | Original work, drawn for VectorCraft | MIT OR Apache-2.0 | |
| `assets/icons/dc-arrow-extend.svg` | VectorCraft contributors | Original work, drawn for VectorCraft | MIT OR Apache-2.0 | |
| `assets/icons/dc-arrow-fill.svg` | VectorCraft contributors | Original work, drawn for VectorCraft | MIT OR Apache-2.0 | The Selection and Direct Selection arrows' fill, painted black or white under their outline |
| `assets/icons/dc-arrow-tip.svg` | VectorCraft contributors | Original work, drawn for VectorCraft | MIT OR Apache-2.0 | |
| `assets/icons/dc-arrow-up.svg` | VectorCraft contributors | Original work, drawn for VectorCraft | MIT OR Apache-2.0 | |
| `assets/icons/dc-artboard-options.svg` | VectorCraft contributors | Original work, drawn for VectorCraft | MIT OR Apache-2.0 | |

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M6 3 L6 19 L10 15 L13 21 L15.5 20 L12.6 14 L18 14 Z" fill="currentColor"/></svg>

After

Width:  |  Height:  |  Size: 173 B

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M6 3 L6 19 L10 15 L13 21 L15.5 20 L12.6 14 L18 14 Z" fill="currentColor" stroke-width="1"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M6 3 L6 19 L10 15 L13 21 L15.5 20 L12.6 14 L18 14 Z" fill="none"/></svg>

Before

Width:  |  Height:  |  Size: 290 B

After

Width:  |  Height:  |  Size: 265 B

View File

@ -66,6 +66,7 @@ pub static ICONS: &[(&str, &[u8])] = &[
("dc-dir-off", include_bytes!("../../../assets/icons/dc-dir-off.svg")),
("dc-dir-on", include_bytes!("../../../assets/icons/dc-dir-on.svg")),
("dc-direct", include_bytes!("../../../assets/icons/dc-direct.svg")),
("dc-arrow-fill", include_bytes!("../../../assets/icons/dc-arrow-fill.svg")),
("dc-dist-bottom", include_bytes!("../../../assets/icons/dc-dist-bottom.svg")),
("dc-dist-hcenter", include_bytes!("../../../assets/icons/dc-dist-hcenter.svg")),
("dc-dist-hspace", include_bytes!("../../../assets/icons/dc-dist-hspace.svg")),

View File

@ -105,8 +105,19 @@ pub fn source(name: &str) -> ImageSource<'static> {
table().source(name)
}
/// Paint icon `name` into `rect` tinted with `tint`.
/// Paint icon `name` into `rect` tinted with `tint`. The Selection and Direct Selection arrows are
/// black and white whatever the theme, as users know them (a light theme colour would make the
/// Selection arrow look like Direct Selection's): their fill is painted first, then their outline
/// in `tint`.
pub fn paint(ui: &Ui, name: &str, rect: Rect, tint: Color32) {
let fill = match name {
"dc-selection" => Some(Color32::BLACK),
"dc-direct" => Some(Color32::WHITE),
_ => None,
};
if let Some(fill) = fill {
egui::Image::new(source("dc-arrow-fill")).tint(fill).fit_to_exact_size(rect.size()).paint_at(ui, rect);
}
egui::Image::new(source(name)).tint(tint).fit_to_exact_size(rect.size()).paint_at(ui, rect);
}
@ -121,6 +132,35 @@ pub fn icon(ui: &mut Ui, name: &str, size: f32, tint: Color32) -> egui::Response
mod tests {
use super::*;
/// The tints of the images `name` paints, bottom first.
fn tints(name: &str, tint: Color32) -> Vec<Color32> {
let ctx = egui::Context::default();
egui_extras::install_image_loaders(&ctx);
let draw = |ui: &mut Ui| paint(ui, name, Rect::from_min_size(egui::Pos2::ZERO, Vec2::splat(24.0)), tint);
// The first frame loads the images; the second draws them.
ctx.run_ui(egui::RawInput::default(), draw).textures_delta.clear();
let mut out = ctx.run_ui(egui::RawInput::default(), draw);
out.textures_delta.clear();
out.shapes
.iter()
.filter_map(|s| match &s.shape {
egui::Shape::Mesh(m) => m.vertices.first().map(|v| v.color),
egui::Shape::Rect(r) if r.brush.is_some() => Some(r.fill),
_ => None,
})
.collect()
}
/// The Selection arrow is black and Direct Selection's white in every theme (a reported swap
/// in the dark themes), under an outline in the theme's colour; other icons are one layer.
#[test]
fn the_selection_arrows_are_black_and_white_under_the_theme_outline() {
let light = Color32::from_gray(220);
assert_eq!(tints("dc-selection", light), [Color32::BLACK, light]);
assert_eq!(tints("dc-direct", light), [Color32::WHITE, light]);
assert_eq!(tints("dc-group-select", light), [light]);
}
/// Characterize the pre-extraction loader against every embedded asset, including custom
/// icons. A shared-crate update must not silently change our stroke weight or cache keys.
#[test]