Gradient: the Cmd/Ctrl-click reset paints only the selected characters and gives an unpainted stroke its weight

The reset ran paint.editGradient, which changes every run of a type
object and leaves a stroke without paint at weight 0. It now runs
paint.setFill or paint.setStroke with the linear White, Black gradient:
characters selected with the Type tool take it and the rest keep their
paint, and a stroke without paint gets the 1 pt weight, as a plain click
on the Gradient button already does.
This commit is contained in:
dotCooCoo
2026-10-29 10:03:12 +07:00
committed by bflatastic
parent 85d7913f10
commit 5c1ab5042f
2 changed files with 61 additions and 4 deletions
+7 -4
View File
@@ -191,11 +191,14 @@ fn write_stops(app: &mut VectorcraftApp, params: Value, select: Option<usize>, p
}
}
/// Make the gradient behind the active proxy linear with the White, Black stops, as one undo
/// step (a solid or no paint becomes that gradient). With nothing selected it sets the default.
/// Cmd/Ctrl-click on the Gradient Fill box or on the Tools panel's Gradient button runs it.
/// Paint the active proxy with the linear White, Black gradient, as one undo step (a solid or no
/// paint becomes that gradient). It runs `paint.setFill` or `paint.setStroke`, so characters
/// selected with the Type tool take it alone and an unpainted stroke gets its 1 pt weight. With
/// nothing selected it sets the default. Cmd/Ctrl-click on the Gradient Fill box or on the Tools
/// panel's Gradient button runs it.
pub(crate) fn reset_to_linear_white_black(app: &mut VectorcraftApp) {
edit(app, json!({ "kind": "linear", "stops": stops_json(&Gradient::default().stops) }), Live::Released);
let cmd = if app.session.fill_active { "paint.setFill" } else { "paint.setStroke" };
app.run(cmd, json!({ "gradient": { "kind": "linear", "stops": stops_json(&Gradient::default().stops) } })).ok();
}
/// Save the gradient as a swatch; its stops keep their color models.
@@ -362,6 +362,60 @@ fn cmd_click_on_the_gradient_fill_box_with_the_options_hidden() {
assert_eq!(hexes(&app), ["#ffffff", "#000000"]);
}
/// Cmd/Ctrl-click on the Gradient Fill box gives an unpainted stroke (no paint, no weight) the
/// White, Black gradient with the 1 pt weight, so the gradient shows.
#[test]
fn cmd_click_on_the_gradient_fill_box_gives_an_unpainted_stroke_its_weight() {
let mut app = app(json!({"color": "#336699"}));
app.run("paint.setStroke", json!({"none": true})).unwrap();
let stroke = |app: &VectorcraftApp| {
let st = app.session.doc().unwrap();
let n = st.doc.node(st.selection.objects[0]).unwrap();
n.appearance.stroke().map(|s| (s.paint.clone(), s.width)).unwrap()
};
assert_eq!(stroke(&app).1, 0.0);
app.session.fill_active = false;
let mut p = Panel::new();
p.frame(&mut app, vec![], Modifiers::NONE);
let thumb = p.widget("grad-swatches").left_center() - vec2(21.0, 0.0);
p.click_with(&mut app, thumb, Modifiers::COMMAND);
let (paint, width) = stroke(&app);
let Paint::Gradient(g) = paint else { panic!("expected a gradient stroke, got {paint:?}") };
assert_eq!(g.gradient.kind, GradientKind::Linear);
assert_eq!(width, 1.0, "the stroke gets the 1 pt default");
}
/// With characters selected with the Type tool, Cmd/Ctrl-click on the Gradient Fill box gives
/// only those characters the White, Black gradient, as one undo step.
#[test]
fn cmd_click_on_the_gradient_fill_box_resets_only_the_selected_characters() {
let mut app = VectorcraftApp::new(Session::new(), Default::default());
app.run("file.new", json!({"width": 300, "height": 300})).unwrap();
app.session.select_tool("type", Default::default()).unwrap();
let id = app.run("text.create", json!({"x": 20, "y": 50, "text": "Sample Text", "size": 24})).unwrap()["id"].clone();
let id = NodeId(id.as_u64().unwrap());
app.session.apply_actions(vec![vectorcraft_tools::Action::Notify("text.editNew".into())]).unwrap();
app.session.set_tool_option("select", &json!({"start": 0, "end": 6}));
let runs = |app: &VectorcraftApp| match app.session.doc().unwrap().doc.node(id).map(|n| &n.kind) {
Some(vectorcraft_doc::NodeKind::Text(t)) => t.runs.iter().map(|r| (r.text.clone(), r.style.fill.clone())).collect::<Vec<_>>(),
k => panic!("expected type, got {k:?}"),
};
let before = runs(&app)[0].1.clone();
let mut p = Panel::new();
p.frame(&mut app, vec![], Modifiers::NONE);
let thumb = p.widget("grad-swatches").left_center() - vec2(21.0, 0.0);
p.click_with(&mut app, thumb, Modifiers::COMMAND);
let after = runs(&app);
assert_eq!(after.len(), 2);
assert_eq!(after[0].0, "Sample");
let Paint::Gradient(g) = &after[0].1 else { panic!("expected a gradient fill, got {:?}", after[0].1) };
assert_eq!(g.gradient.kind, GradientKind::Linear);
assert_eq!(g.gradient.stops.iter().map(|s| s.color.to_hex()).collect::<Vec<_>>(), ["#ffffff", "#000000"]);
assert_eq!(after[1], (" Text".to_string(), before.clone()), "the rest keeps its fill");
app.run("edit.undo", json!({})).unwrap();
assert!(runs(&app).iter().all(|(_, f)| *f == before), "one undo step");
}
#[test]
fn alt_drag_copies_a_stop_and_alt_dropping_on_a_stop_swaps_colours() {
let mut app = app(json!({"gradient": {}}));