Merge pull request #4316 from ham9zah/fix/appearance-mixed-fill-chip

Appearance: a mixed selection's object chip shows "?" when the fills differ
This commit is contained in:
BFlat
2026-10-29 10:47:43 +07:00
committed by GitHub
2 changed files with 12 additions and 12 deletions
+7 -10
View File
@@ -219,12 +219,10 @@ fn text(ui: &Ui, pos: egui::Pos2, s: &str, strong: bool) {
ui.painter().text(pos, egui::Align2::LEFT_CENTER, s, egui::FontId::proportional(12.5), if strong { t.text_strong } else { t.text });
}
fn chip(ui: &Ui, r: Rect, p: &Paint) {
let t = Tokens::get(ui.ctx());
fn chip(ui: &Ui, r: Rect, p: &Paint, mixed: bool) {
ui.painter().rect_filled(r.expand(1.0), 0.0, egui::Color32::BLACK);
widgets::paint_chip(ui, r, p);
widgets::proxy_chip(ui, r, p, mixed, None);
ui.painter().rect_stroke(r, 0.0, Stroke::new(1.0, egui::Color32::WHITE), StrokeKind::Inside);
let _ = t;
}
/// A dotted-underline link drawn at `pos`; returns clicked.
@@ -271,13 +269,12 @@ pub fn show(app: &mut VectorcraftApp, ui: &mut Ui) {
};
if !hide_thumb {
let th = Rect::from_min_size(r.left_center() + vec2(6.0, -12.0), vec2(24.0, 24.0));
// Objects whose fills differ show no one of them.
// Objects whose fills differ show "?", like the Properties panel's Fill chip.
let fill = match &node {
Some(_) if mix.as_ref().is_some_and(|m| m.fills_differ) => Paint::None,
Some(n) => n.appearance.fill_paint(),
None => app.session.paint.fill.clone(),
};
chip(ui, th, &fill);
chip(ui, th, &fill, mix.as_ref().is_some_and(|m| m.fills_differ));
if let Some(n) = node.as_ref().filter(|_| !mixed) {
thumbnail_drag(ui, th, n.id);
}
@@ -394,7 +391,7 @@ fn default_stack(app: &mut VectorcraftApp, ui: &mut Ui) {
} else if link(ui, pos2(lx, r.center().y), ("ap-def-link", i), tl!("Stroke:")) {
app.ui.open_panel = Some("stroke".into());
}
chip(ui, Rect::from_min_size(pos2(r.left() + EYE_W + 76.0, r.center().y - 9.0), vec2(18.0, 18.0)), it.paint());
chip(ui, Rect::from_min_size(pos2(r.left() + EYE_W + 76.0, r.center().y - 9.0), vec2(18.0, 18.0)), it.paint(), false);
if let AppearanceItem::Stroke(st) = it {
let w = app.session.stroke_unit().format(st.width);
text(ui, pos2(r.left() + EYE_W + 106.0, r.center().y), &w, false);
@@ -496,7 +493,7 @@ fn stack(app: &mut VectorcraftApp, ui: &mut Ui, n: &Node, sel: Sel, object_row:
// Swatch with a swatches popup that sets this item's paint; Shift-click opens the Color
// panel's mixer on this row instead.
let cr = Rect::from_min_size(pos2(r.left() + EYE_W + 76.0, r.center().y - 9.0), vec2(18.0, 18.0));
chip(ui, cr, paint);
chip(ui, cr, paint, false);
let cresp = ui
.interact(cr.expand(2.0), ui.scope_id().with(("ap-chip", i)), Sense::click())
.on_hover_text(tl!("Click to choose a swatch, Shift-click to mix a colour"));
@@ -683,7 +680,7 @@ fn contents_row(app: &mut VectorcraftApp, ui: &mut Ui, n: &Node, label: &str, se
if characters {
for (k, stroke) in [false, true].into_iter().enumerate() {
let cr = Rect::from_min_size(pos2(r.left() + EYE_W + 100.0 + 24.0 * k as f32, r.center().y - 9.0), vec2(18.0, 18.0));
chip(ui, cr, n.proxy_paint(stroke, None).map_or(&Paint::None, |(p, ..)| p));
chip(ui, cr, n.proxy_paint(stroke, None).map_or(&Paint::None, |(p, ..)| p), false);
}
}
// An image's row has no members to target.
@@ -554,17 +554,20 @@ fn a_mixed_selection_lists_what_its_objects_share() {
run(&mut app, "select.set", json!({"ids": ids}));
let shown = frame(&mut app);
let kind = app.session.active().unwrap().doc.node(vectorcraft_doc::NodeId(ids[0].as_u64().unwrap())).unwrap().kind_label();
assert_eq!(shown.first().map(String::as_str), Some(kind), "the object row names their kind: {shown:?}");
assert_eq!(shown.iter().find(|t| *t != "?").map(String::as_str), Some(kind), "the object row names their kind: {shown:?}");
assert!(shown.iter().any(|t| t == "Mixed Appearances"), "{shown:?}");
assert!(shown.iter().any(|t| t == "Stroke:") && !shown.iter().any(|t| t == "Fill:"), "the stroke, not the fills: {shown:?}");
assert!(shown.iter().any(|t| t.contains("Feather")), "the shared effect: {shown:?}");
assert!(shown.iter().any(|t| t == "Opacity:"), "{shown:?}");
// The object row's chip says the fills differ, like the Properties panel's "?" chip.
assert!(shown.iter().any(|t| t == "?"), "{shown:?}");
// Alike objects list their whole stack, without the note.
run(&mut app, "paint.setFill", json!({"ids": ids, "color": "#ffff00"}));
let shown = frame(&mut app);
assert!(!shown.iter().any(|t| t == "Mixed Appearances") && shown.iter().any(|t| t == "Fill:"), "{shown:?}");
assert!(!shown.iter().any(|t| t == "?"), "{shown:?}");
// A path and type.
let t = run(&mut app, "text.create", json!({"x": 0, "y": 180, "text": "Hi"}))["id"].clone();
run(&mut app, "select.set", json!({"ids": [ids[0].clone(), t]}));
assert_eq!(frame(&mut app).first().map(String::as_str), Some("Mixed Objects"));
assert_eq!(frame(&mut app).iter().find(|t| *t != "?").map(String::as_str), Some("Mixed Objects"));
}