Three readable line type icons in the Sketch Palette, with construction orange moved to a caller contract (#747)
The Sketch Palette's line types are three drawn icons (solid, one gap, gap with a dot) instead of text glyphs, and the construction orange is now chosen by the caller (`fixed_accent` on the toolbar) rather than hardcoded in the icons.
This commit is contained in:
1 parent
aae70053ee
commit
4a989f6e2d
5 files changed
+308
-33
No files matched your search
+162
-20
@@ -62,6 +62,20 @@ impl Pen<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
/// The stroke width for the Normal/Construction/Centerline line-type icons. It is heavier than
|
||||
/// the generic icon weight (`Pen::w`). It is sized from whichever side of `r` is smaller. A
|
||||
/// short, wide button and a square toolbar slot both get a stroke their gaps can survive next
|
||||
/// to.
|
||||
fn line_type_stroke(r: Rect) -> f32 {
|
||||
(r.width().min(r.height()) / 24.0 * 2.0).max(1.6)
|
||||
}
|
||||
|
||||
/// The Centerline icon's dot radius. It is sized from `r`. The stroke width plays no part in it.
|
||||
/// It stays a round mark even next to a heavy stroke.
|
||||
fn line_type_dot_radius(r: Rect) -> f32 {
|
||||
(r.width().min(r.height()) * 0.16).max(2.4)
|
||||
}
|
||||
|
||||
/// Paint the icon `name` into `r`. `accent` highlights the "action" part.
|
||||
pub fn paint(p: &Painter, r: Rect, name: &str, ink: Color32, fill: Color32, accent: Color32) {
|
||||
let pen = Pen { p, r, ink, fill, accent, w: (r.width() / 24.0 * 1.4).max(1.0) };
|
||||
@@ -345,28 +359,49 @@ pub fn paint(p: &Painter, r: Rect, name: &str, ink: Color32, fill: Color32, acce
|
||||
pen.arrow((12.0, 11.0), (19.5, 11.0), a);
|
||||
pen.line(&[(9.0, 15.0), (15.0, 15.0)], ink);
|
||||
}
|
||||
// Construction geometry has one fixed meaning everywhere in the app (the theme's
|
||||
// construction orange), independent of the tab accent the caller passes in: a plain
|
||||
// construction curve is an even dashed line, a centerline the ISO long-short rhythm.
|
||||
"construction" | "centerline" => {
|
||||
let orange = crate::theme::Tokens::get().construction_geom;
|
||||
if name == "construction" {
|
||||
for i in 0..4 {
|
||||
let t = i as f32 * 4.5;
|
||||
pen.line(&[(3.0 + t, 19.0 - t), (5.5 + t, 16.5 - t)], orange);
|
||||
}
|
||||
} else {
|
||||
let (fx, fy) = (2.5, 20.5);
|
||||
let (dx, dy) = (19.0, -18.0);
|
||||
let seg = |t0: f32, t1: f32| {
|
||||
pen.line(&[(fx + dx * t0, fy + dy * t0), (fx + dx * t1, fy + dy * t1)], orange);
|
||||
};
|
||||
seg(0.03, 0.32);
|
||||
seg(0.40, 0.48);
|
||||
seg(0.56, 0.85);
|
||||
seg(0.93, 1.0);
|
||||
// The Sketch Palette's line type selector (normal, construction, centerline) paints all
|
||||
// three on the same diagonal, from (3, 19) to (19, 3). Only the dash pattern changes
|
||||
// between them. Normal draws in `ink` and ignores `accent`. It is a plain line. It is
|
||||
// never construction geometry. Its colour must stay fixed even if a caller passes the
|
||||
// construction orange for it. Construction and centerline draw only in `accent` and
|
||||
// ignore `ink`. The caller decides the colour. The palette and the toolbar both pass
|
||||
// the theme's construction orange.
|
||||
//
|
||||
// These three are drawn small. The Sketch Palette draws them down to about 18 px
|
||||
// square. At that size a repeating dash pattern loses its gaps. It reads as a solid
|
||||
// line. Two patterns that only differ in dash phase read as the same texture too.
|
||||
// Normal, Construction and Centerline instead get three different silhouettes. One is
|
||||
// an unbroken line. One is a line broken by a single gap. One is a line broken by a
|
||||
// single gap with a bold dot sitting in it. A viewer can name the shape on sight. No
|
||||
// rhythm needs measuring. The stroke width and the dot radius both come from the rect
|
||||
// actually passed in. That keeps the shapes legible in the palette, the toolbar and the
|
||||
// command palette, at every size any of them uses.
|
||||
"normal" => {
|
||||
let w = line_type_stroke(r);
|
||||
p.add(Shape::line(vec![pen.at(3.0, 19.0), pen.at(19.0, 3.0)], Stroke::new(w, ink)));
|
||||
}
|
||||
"construction" => {
|
||||
// One gap, no dot. It is an unmistakably broken line. Nothing sits in the break.
|
||||
let w = line_type_stroke(r);
|
||||
let at = |t: f32| pen.at(3.0 + 16.0 * t, 19.0 - 16.0 * t);
|
||||
for (t0, t1) in [(0.0, 0.35), (0.65, 1.0)] {
|
||||
p.add(Shape::line(vec![at(t0), at(t1)], Stroke::new(w, a)));
|
||||
}
|
||||
}
|
||||
"centerline" => {
|
||||
// The same gap as Construction, with a bold dot centred in it. The silhouette is
|
||||
// line, dot, line. The dot's radius comes from the rect. It reads as a distinct
|
||||
// round mark on its own, never as a thicker stretch of the stroke. The dashes stop
|
||||
// well short of it on both sides. The dot never touches either stroke. It always
|
||||
// reads as a separate shape.
|
||||
let w = line_type_stroke(r);
|
||||
let dot_r = line_type_dot_radius(r);
|
||||
let at = |t: f32| pen.at(3.0 + 16.0 * t, 19.0 - 16.0 * t);
|
||||
for (t0, t1) in [(0.0, 0.26), (0.74, 1.0)] {
|
||||
p.add(Shape::line(vec![at(t0), at(t1)], Stroke::new(w, a)));
|
||||
}
|
||||
p.circle_filled(at(0.5), dot_r, a);
|
||||
}
|
||||
"c_hv" => {
|
||||
pen.line(&[(4.0, 18.0), (20.0, 18.0)], a);
|
||||
pen.line(&[(6.0, 4.0), (6.0, 14.0)], a);
|
||||
@@ -1030,3 +1065,110 @@ pub fn paint(p: &Painter, r: Rect, name: &str, ink: Color32, fill: Color32, acce
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
/// A debug dump of everything `paint` draws for `name`, for comparing two colour choices
|
||||
/// without depending on epaint's internal shape fields.
|
||||
fn drawn(name: &str, ink: Color32, fill: Color32, accent: Color32) -> String {
|
||||
let ctx = egui::Context::default();
|
||||
let screen = Rect::from_min_size(Pos2::ZERO, vec2(100.0, 100.0));
|
||||
let input = egui::RawInput { screen_rect: Some(screen), ..Default::default() };
|
||||
let out = ctx.run_ui(input, |ui| {
|
||||
let r = Rect::from_min_size(Pos2::ZERO, vec2(24.0, 24.0));
|
||||
paint(ui.painter(), r, name, ink, fill, accent);
|
||||
});
|
||||
format!("{:?}", out.shapes)
|
||||
}
|
||||
|
||||
/// The Sketch Palette and the toolbar both rely on this contract. Construction and
|
||||
/// centerline must track whatever accent colour the caller passes (both callers pass the
|
||||
/// theme's construction orange) and must not change with `ink`.
|
||||
#[test]
|
||||
fn construction_and_centerline_icons_follow_the_callers_accent_only() {
|
||||
for name in ["construction", "centerline"] {
|
||||
let base = drawn(name, Color32::BLACK, Color32::WHITE, Color32::from_rgb(1, 2, 3));
|
||||
let other_accent = drawn(name, Color32::BLACK, Color32::WHITE, Color32::from_rgb(4, 5, 6));
|
||||
assert_ne!(base, other_accent, "{name} icon must change with the caller's accent colour");
|
||||
let other_ink = drawn(name, Color32::from_rgb(9, 9, 9), Color32::WHITE, Color32::from_rgb(1, 2, 3));
|
||||
assert_eq!(base, other_ink, "{name} icon has no ink-coloured parts: ink alone must not change it");
|
||||
}
|
||||
}
|
||||
|
||||
/// Normal is a plain line type. It is never construction geometry. Its icon must never pick
|
||||
/// up the accent colour. It can never render construction orange even if a caller passes it
|
||||
/// by mistake.
|
||||
#[test]
|
||||
fn normal_icon_never_uses_the_accent_colour() {
|
||||
let one_accent = drawn("normal", Color32::BLACK, Color32::WHITE, Color32::from_rgb(1, 2, 3));
|
||||
let other_accent = drawn("normal", Color32::BLACK, Color32::WHITE, Color32::from_rgb(4, 5, 6));
|
||||
assert_eq!(one_accent, other_accent, "the Normal icon must not change with the accent colour");
|
||||
let other_ink = drawn("normal", Color32::from_rgb(9, 9, 9), Color32::WHITE, Color32::from_rgb(1, 2, 3));
|
||||
assert_ne!(one_accent, other_ink, "the Normal icon must change with ink");
|
||||
}
|
||||
|
||||
/// The dashes (as their first and last point) and the dots (as centre and radius) `paint`
|
||||
/// actually drew for `name`, at `side` pixels square: the Sketch Palette draws these three
|
||||
/// icons down to about 18 px.
|
||||
fn dashes_and_dots(name: &str, side: f32) -> (Vec<(Pos2, Pos2)>, Vec<(Pos2, f32)>) {
|
||||
let ctx = egui::Context::default();
|
||||
let screen = Rect::from_min_size(Pos2::ZERO, vec2(100.0, 100.0));
|
||||
let input = egui::RawInput { screen_rect: Some(screen), ..Default::default() };
|
||||
let r = Rect::from_min_size(Pos2::ZERO, vec2(side, side));
|
||||
let out = ctx.run_ui(input, |ui| {
|
||||
paint(ui.painter(), r, name, Color32::BLACK, Color32::WHITE, Color32::from_rgb(232, 140, 48));
|
||||
});
|
||||
let dashes = out
|
||||
.shapes
|
||||
.iter()
|
||||
.filter_map(|s| match &s.shape {
|
||||
Shape::Path(p) => Some((*p.points.first()?, *p.points.last()?)),
|
||||
_ => None,
|
||||
})
|
||||
.collect();
|
||||
let dots = out.shapes.iter().filter_map(|s| if let Shape::Circle(c) = &s.shape { Some((c.center, c.radius)) } else { None }).collect();
|
||||
(dashes, dots)
|
||||
}
|
||||
|
||||
/// Pins the dash count and the gap. A future tidy-up might add dashes back in. It might
|
||||
/// narrow the gap to look more faithful to the ISO convention. Either change would flatten
|
||||
/// the icon back into a near-solid line at the size the Sketch Palette actually draws it,
|
||||
/// about 18 px square. That is the exact defect this fix addressed.
|
||||
#[test]
|
||||
fn construction_and_centerline_read_as_two_dashes_with_a_gap_wider_than_the_stroke() {
|
||||
for (name, want_dots) in [("construction", 0), ("centerline", 1)] {
|
||||
for side in [18.0, 26.0] {
|
||||
let (dashes, dots) = dashes_and_dots(name, side);
|
||||
assert_eq!(dashes.len(), 2, "{name} at {side}px must be exactly two dashes");
|
||||
assert_eq!(dots.len(), want_dots, "{name} at {side}px has the wrong number of dots");
|
||||
let (_, gap_start) = dashes[0];
|
||||
let (gap_end, _) = dashes[1];
|
||||
let gap = gap_start.distance(gap_end);
|
||||
let stroke = line_type_stroke(Rect::from_min_size(Pos2::ZERO, vec2(side, side)));
|
||||
assert!(
|
||||
gap > stroke * 2.0,
|
||||
"{name} at {side}px: the gap of {gap:.1}px must exceed the {stroke:.1}px stroke by a wide margin, or the dashes read as one solid line"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Pins the shape of the Centerline dot. It must be large enough to read as a round mark
|
||||
/// beside the stroke. It must sit clear of both dashes. The silhouette must stay line, dot,
|
||||
/// line.
|
||||
#[test]
|
||||
fn centerline_dot_is_a_bold_mark_clear_of_both_dashes() {
|
||||
for side in [18.0, 26.0] {
|
||||
let (dashes, dots) = dashes_and_dots("centerline", side);
|
||||
let (center, radius) = dots[0];
|
||||
let stroke = line_type_stroke(Rect::from_min_size(Pos2::ZERO, vec2(side, side)));
|
||||
assert!(radius > stroke * 1.2, "centerline dot at {side}px: radius {radius:.1}px must exceed the {stroke:.1}px stroke by a wide margin");
|
||||
for (a, b) in &dashes {
|
||||
assert!(center.distance(*a) > radius, "centerline dot at {side}px must not touch a dash end");
|
||||
assert!(center.distance(*b) > radius, "centerline dot at {side}px must not touch a dash end");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -400,22 +400,46 @@ pub fn show(app: &mut SolveApp, ctx: &egui::Context) {
|
||||
ui.vertical(|ui| {
|
||||
ui.horizontal(|ui| {
|
||||
ui.label(app.tr("ui.linetype"));
|
||||
let btn_w = 30.0;
|
||||
// Taller and wider than the old text-only buttons. Construction and
|
||||
// Centerline need a round dot and a clear gap to read apart. 30 by
|
||||
// 20 px left too little square room for either.
|
||||
let btn_w = 36.0;
|
||||
let group_w = 3.0 * btn_w + 2.0 * ui.spacing().item_spacing.x;
|
||||
crate::frame::right_aligned(ui, width, group_w, |ui| {
|
||||
ui.horizontal(|ui| {
|
||||
// With a selection: its line type, and a click converts it;
|
||||
// without: the mode new curves are drawn in.
|
||||
// without: the mode new curves are drawn in. Each button
|
||||
// still carries its old glyph as hidden (transparent) text.
|
||||
// That keeps its hit target, size and accessible name
|
||||
// exactly as before. A drawn icon (icons.rs) paints over it.
|
||||
// Construction and Centerline always draw in the theme's
|
||||
// construction orange. Normal's icon never changes with the
|
||||
// accent colour. It stays neutral in every case.
|
||||
let current = mode(app);
|
||||
for (i, (label, tip)) in [("—", "Normal"), ("- -", "Construction"), ("-·-", "Centerline")].iter().enumerate() {
|
||||
for (i, (label, icon, tip)) in
|
||||
[("—", "normal", "Normal"), ("- -", "construction", "Construction"), ("-·-", "centerline", "Centerline")]
|
||||
.iter()
|
||||
.enumerate()
|
||||
{
|
||||
let on = if has_curves { selected_types.get(i).copied().unwrap_or(false) } else { current == i as u8 };
|
||||
let tr_tip = app.tr_label(tip);
|
||||
let tip = if has_curves {
|
||||
let hover = if has_curves {
|
||||
crate::i18n::format("sketch.make_selection", &[("linetype", tr_tip)])
|
||||
} else {
|
||||
crate::i18n::format("sketch.draw_curves", &[("linetype", tr_tip)])
|
||||
};
|
||||
if ui.add(egui::Button::new(*label).selected(on).min_size(vec2(btn_w, 20.0))).on_hover_text(tip).clicked() {
|
||||
let btn = egui::Button::new(RichText::new(*label).color(Color32::TRANSPARENT))
|
||||
.selected(on)
|
||||
.min_size(vec2(btn_w, 28.0));
|
||||
let resp = ui.add(btn).on_hover_text(hover);
|
||||
let accent = if *icon == "normal" { t.icon } else { t.construction_geom };
|
||||
// A square icon area. The button's own rect is
|
||||
// text-shaped, wider than tall. A flattened diagonal
|
||||
// leaves less room for a dash pattern's gaps to read.
|
||||
let side = (resp.rect.width().min(resp.rect.height()) - 3.0).max(12.0);
|
||||
let icon_rect = egui::Rect::from_center_size(resp.rect.center(), vec2(side, side));
|
||||
crate::icons::paint(ui.painter(), icon_rect, icon, t.icon, t.icon_fill, accent);
|
||||
if resp.clicked() {
|
||||
linetype = Some(i as u8);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,28 @@ fn panel_accent(t: Tokens, tab: &str, panel: &str) -> Color32 {
|
||||
}
|
||||
}
|
||||
|
||||
/// The accent stroke colour a command's icon keeps on every surface that draws it, where the
|
||||
/// command's own meaning fixes it. Construction geometry carries one fixed meaning across the
|
||||
/// whole app. `sketch.construction` and `sketch.centerline` therefore draw their icon in the
|
||||
/// theme's construction orange on the ribbon, in a panel's drop-down and under the pointer while
|
||||
/// being dragged. The Sketch Palette's own line type buttons pass that same orange. `icons::paint`
|
||||
/// draws both of these icon names in the accent its caller hands it, never in a colour of its own.
|
||||
/// The caller is the only place the rule can live. Every other command has no fixed colour. It
|
||||
/// keeps the accent of whichever surface draws it.
|
||||
fn fixed_accent(t: Tokens, id: &str) -> Option<Color32> {
|
||||
match id {
|
||||
"sketch.construction" | "sketch.centerline" => Some(t.construction_geom),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// The accent stroke colour for one command's icon on the ribbon. It covers the command's own
|
||||
/// button and a collapsed panel's single button. A command with a `fixed_accent` keeps that.
|
||||
/// Every other command takes its `panel_accent`.
|
||||
fn command_accent(t: Tokens, tab: &str, panel: &str, id: &str) -> Color32 {
|
||||
fixed_accent(t, id).unwrap_or_else(|| panel_accent(t, tab, panel))
|
||||
}
|
||||
|
||||
/// Panels shown on each tab (in order). Commands come from the registry by (tab, panel).
|
||||
pub fn panels(tab: &str) -> &'static [&'static str] {
|
||||
if let Some(p) = crate::workspace::panels(tab) {
|
||||
@@ -605,7 +627,7 @@ pub fn toolbar(app: &mut SolveApp, ui: &mut egui::Ui) {
|
||||
painter.rect_filled(br, 4.0, t.hover);
|
||||
}
|
||||
if let Some(c) = shown.first().or(cmds.first()) {
|
||||
let acc = panel_accent(t, &tab, panel);
|
||||
let acc = command_accent(t, &tab, panel, c.id);
|
||||
icons::paint(&painter, Rect::from_center_size(br.center() - vec2(0.0, 3.0), vec2(26.0, 26.0)), c.icon, t.icon, t.icon_fill, acc);
|
||||
}
|
||||
caret(&painter, pos2(br.center().x, br.bottom() - 3.0), t.text_dim);
|
||||
@@ -655,8 +677,11 @@ pub fn toolbar(app: &mut SolveApp, ui: &mut egui::Ui) {
|
||||
} else if resp.hovered() && info.enabled {
|
||||
painter.rect_filled(br, 4.0, t.hover);
|
||||
}
|
||||
let (ink, fill, acc) =
|
||||
if info.enabled { (t.icon, t.icon_fill, panel_accent(t, &tab, panel)) } else { (t.border, t.panel_header, t.border) };
|
||||
let (ink, fill, acc) = if info.enabled {
|
||||
(t.icon, t.icon_fill, command_accent(t, &tab, panel, selected.id))
|
||||
} else {
|
||||
(t.border, t.panel_header, t.border)
|
||||
};
|
||||
let icon = match selected.id {
|
||||
"sketch.finish" => "finish",
|
||||
"config.table" => "configuration_table",
|
||||
@@ -941,10 +966,11 @@ fn dropdown_row(app: &SolveApp, ui: &mut egui::Ui, c: &'static solidcraft_engine
|
||||
p.rect_filled(rect, 3.0, t.hover);
|
||||
crate::scenario::publish_handle(&format!("dropdown_hot:{}", c.id), rect.center());
|
||||
}
|
||||
// Every other row keeps the plain accent it always has; only a constraint command's row
|
||||
// switches to the constraint red, to match its button on the ribbon.
|
||||
// Every other row keeps the plain accent it always has. A constraint command's row switches
|
||||
// to the constraint red, to match its button on the ribbon. A command with a `fixed_accent`
|
||||
// keeps that colour ahead of either.
|
||||
let (ink, fill, acc, text) = if info.enabled {
|
||||
let acc = if c.panel == "CONSTRAINTS" { t.glyph_text } else { t.accent };
|
||||
let acc = fixed_accent(t, c.id).unwrap_or(if c.panel == "CONSTRAINTS" { t.glyph_text } else { t.accent });
|
||||
(t.icon, t.icon_fill, acc, t.text)
|
||||
} else {
|
||||
(t.border, t.panel_header, t.border, t.text_dim)
|
||||
@@ -1078,7 +1104,7 @@ fn drag_ghost(ctx: &egui::Context, id: &str, at: egui::Pos2, removing: bool) {
|
||||
let p = ctx.layer_painter(egui::LayerId::new(egui::Order::Tooltip, egui::Id::new("sc_toolbar_drag")));
|
||||
let r = Rect::from_center_size(at + vec2(14.0, 16.0), vec2(30.0, 30.0));
|
||||
p.rect(r, 4.0, t.toolbar.gamma_multiply(0.92), Stroke::new(1.0, if removing { t.error } else { t.accent }), egui::StrokeKind::Inside);
|
||||
icons::paint(&p, r.shrink(5.0), c.icon, t.icon, t.icon_fill, t.accent);
|
||||
icons::paint(&p, r.shrink(5.0), c.icon, t.icon, t.icon_fill, fixed_accent(t, c.id).unwrap_or(t.accent));
|
||||
if removing {
|
||||
let s = Stroke::new(2.0, t.error);
|
||||
let x = Rect::from_center_size(r.right_top(), vec2(9.0, 9.0));
|
||||
@@ -1523,3 +1549,66 @@ mod panel_drop_tests {
|
||||
h.step(&json!({"expect":{"handle_order":["button:solid.scale","button:solid.extrude"]}})).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod icon_accent_tests {
|
||||
use super::*;
|
||||
|
||||
/// `icons.rs` pins one half of the construction icon contract. `construction` and
|
||||
/// `centerline` take their colour from whatever accent their caller hands over. That half
|
||||
/// stays green when the caller hands over the wrong colour. That is exactly what the toolbar
|
||||
/// did. This pins the other half: the colour the toolbar actually passes for those two
|
||||
/// commands, on every surface the toolbar draws them on.
|
||||
#[test]
|
||||
fn the_toolbar_passes_construction_orange_for_both_line_type_commands() {
|
||||
for t in [Tokens::light(), Tokens::dark()] {
|
||||
// The regression itself: the SKETCH tab's accent is green. These two icons drew in
|
||||
// it. Construction orange has to be a colour no panel accent can supply.
|
||||
assert_ne!(t.construction_geom, t.sketch_accent, "construction orange must differ from the sketch accent");
|
||||
assert_ne!(t.construction_geom, t.accent, "construction orange must differ from the plain accent");
|
||||
assert_ne!(t.construction_geom, t.glyph_text, "construction orange must differ from the constraint red");
|
||||
for id in ["sketch.construction", "sketch.centerline"] {
|
||||
assert_eq!(fixed_accent(t, id), Some(t.construction_geom), "{id} must have a fixed accent");
|
||||
// Its ribbon button, a collapsed panel's single button, and a drop-down row.
|
||||
assert_eq!(command_accent(t, "SKETCH", "CREATE", id), t.construction_geom, "{id} on the SKETCH ribbon");
|
||||
assert_eq!(command_accent(t, "SOLID", "CREATE", id), t.construction_geom, "{id} on the SOLID ribbon");
|
||||
assert_eq!(fixed_accent(t, id).unwrap_or(t.accent), t.construction_geom, "{id} in a drop-down row");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Those two and no others. Every other command's icon keeps the accent of the panel it sits
|
||||
/// on, so the constraint red and the sketch green both survive the exception above.
|
||||
#[test]
|
||||
fn no_other_command_overrides_the_accent_of_its_own_panel() {
|
||||
let t = Tokens::light();
|
||||
let specs = solidcraft_engine::command_specs();
|
||||
let mut fixed: Vec<&str> = specs.iter().map(|c| c.id).filter(|id| fixed_accent(t, id).is_some()).collect();
|
||||
fixed.sort_unstable();
|
||||
assert_eq!(fixed, ["sketch.centerline", "sketch.construction"], "only the two line type commands fix their own accent");
|
||||
for c in &specs {
|
||||
if fixed.contains(&c.id) {
|
||||
continue;
|
||||
}
|
||||
let want = panel_accent(t, c.tab, c.panel);
|
||||
assert_eq!(command_accent(t, c.tab, c.panel, c.id), want, "{} must keep the accent of its own panel", c.id);
|
||||
}
|
||||
// The fix did not flatten every icon to one colour. A sketch tool is still green, a
|
||||
// constraint still red, a solid tool still blue. A missing id fails this as well.
|
||||
let accent_of = |id: &str| solidcraft_engine::find_command(id).map(|c| command_accent(t, c.tab, c.panel, c.id));
|
||||
assert_eq!(accent_of("sketch.text"), Some(t.sketch_accent));
|
||||
assert_eq!(accent_of("sketch.constraint.polygon"), Some(t.glyph_text));
|
||||
assert_eq!(accent_of("solid.extrude"), Some(t.accent));
|
||||
}
|
||||
|
||||
/// Both line type commands sit on the SKETCH tab's CREATE panel. The accent above is
|
||||
/// therefore a colour a person can actually see. Their icons are the two `icons.rs` draws
|
||||
/// from the caller's accent.
|
||||
#[test]
|
||||
fn both_line_type_commands_are_create_panel_commands_with_their_own_icons() {
|
||||
for (id, icon) in [("sketch.construction", "construction"), ("sketch.centerline", "centerline")] {
|
||||
let seen = solidcraft_engine::find_command(id).map(|c| (c.tab, c.panel, c.icon));
|
||||
assert_eq!(seen, Some(("SKETCH", "CREATE", icon)), "{id} must be on the SKETCH CREATE panel");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user