mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-31 21:27:00 -06:00
Gradient: Cmd/Ctrl-click on the Gradient Fill box or the Gradient button resets to White, Black
The reference app resets a gradient to black and white when the gradient button in the Tools panel is Cmd/Ctrl-clicked, and to the default black and white linear gradient when the Gradient Fill box in the Gradient panel is Cmd/Ctrl-clicked. In VectorCraft a Cmd/Ctrl-click on either did what a plain click does, and the reset was only in the Gradient panel menu. A Cmd/Ctrl-click on the Gradient panel's thumbnail or on the Tools panel's Gradient button now makes the gradient of the active Fill or Stroke a linear gradient with the White, Black stops, through paint.editGradient, as one undo step. A solid or no paint becomes that gradient, and with nothing selected the default paint changes. A plain click keeps its behavior, and the panel menu's Reset to White, Black is unchanged. Docs: docs/target-app-parity.md.
This commit is contained in:
@@ -9,6 +9,8 @@
|
||||
//! recolours the stop it lands on; a global or spot colour (or a tint of one) stays linked, so
|
||||
//! editing the swatch recolours the stop. The thumbnail menu marks the gradient swatch the
|
||||
//! gradient was applied from. Hide Options leaves the thumbnail, the proxy and the slider.
|
||||
//! Cmd/Ctrl-click on the thumbnail (the Gradient Fill box) or on the Tools panel's Gradient
|
||||
//! button resets the active proxy's gradient to the linear White, Black gradient.
|
||||
//!
|
||||
//! With the Stroke proxy in front, the Stroke buttons lay a linear or radial gradient within the
|
||||
//! stroke (placed on the page like a fill's), along it or across it (angle and aspect ratio then
|
||||
@@ -189,6 +191,13 @@ 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.
|
||||
pub(crate) fn reset_to_linear_white_black(app: &mut VectorcraftApp) {
|
||||
edit(app, json!({ "kind": "linear", "stops": stops_json(&Gradient::default().stops) }), Live::Released);
|
||||
}
|
||||
|
||||
/// Save the gradient as a swatch; its stops keep their color models.
|
||||
fn save_to_swatches(app: &mut VectorcraftApp, g: &GradientPaint) {
|
||||
app.run("swatch.new", super::swatch_paint_params(&Paint::Gradient(Box::new(g.clone())))).ok();
|
||||
@@ -444,8 +453,12 @@ fn thumbnail(app: &mut VectorcraftApp, ui: &mut Ui, g: &GradientPaint, is_grad:
|
||||
widgets::gradient_chip(ui, r, g);
|
||||
ui.painter().rect_stroke(r, 0.0, Stroke::new(1.0, t.border), StrokeKind::Inside);
|
||||
let resp = resp.on_hover_text(tl!("Gradient Fill: click to apply, drag onto art"));
|
||||
if resp.clicked() && !is_grad {
|
||||
edit(app, json!({}), Live::Released);
|
||||
if resp.clicked() {
|
||||
if ui.input(|i| i.modifiers.command) {
|
||||
reset_to_linear_white_black(app);
|
||||
} else if !is_grad {
|
||||
edit(app, json!({}), Live::Released);
|
||||
}
|
||||
}
|
||||
// The dragged copy carries no placement: it fits the art it lands on.
|
||||
widgets::drag_source(ui, &resp, || PanelDrag::paint(Paint::Gradient(Box::new(g.clone()))));
|
||||
|
||||
@@ -134,9 +134,14 @@ impl Panel {
|
||||
}
|
||||
|
||||
fn click(&mut self, app: &mut VectorcraftApp, at: Pos2) -> Vec<(String, Pos2)> {
|
||||
let b = |pressed| Event::PointerButton { pos: at, button: PointerButton::Primary, pressed, modifiers: Modifiers::NONE };
|
||||
self.frame(app, vec![Event::PointerMoved(at), b(true)], Modifiers::NONE);
|
||||
self.frame(app, vec![b(false)], Modifiers::NONE);
|
||||
self.click_with(app, at, Modifiers::NONE)
|
||||
}
|
||||
|
||||
/// A click at `at` with `m` held.
|
||||
fn click_with(&mut self, app: &mut VectorcraftApp, at: Pos2, m: Modifiers) -> Vec<(String, Pos2)> {
|
||||
let b = |pressed| Event::PointerButton { pos: at, button: PointerButton::Primary, pressed, modifiers: m };
|
||||
self.frame(app, vec![Event::PointerMoved(at), b(true)], m);
|
||||
self.frame(app, vec![b(false)], m);
|
||||
self.frame(app, vec![], Modifiers::NONE)
|
||||
}
|
||||
|
||||
@@ -312,6 +317,51 @@ fn the_thumbnail_menu_applies_gradient_swatches_and_saves_one() {
|
||||
assert_eq!(gradient_swatches(&app).len(), before + 1);
|
||||
}
|
||||
|
||||
/// Cmd/Ctrl-click on the Gradient Fill box (the thumbnail) resets the active proxy's gradient to
|
||||
/// the linear White, Black gradient as one undo step; a plain click leaves a gradient as it is.
|
||||
#[test]
|
||||
fn cmd_click_on_the_gradient_fill_box_resets_to_the_linear_white_black_gradient() {
|
||||
let stops = json!([{"offset": 0, "color": "#ff0000"}, {"offset": 0.5, "color": "#ffff00"}, {"offset": 1, "color": "#0000ff"}]);
|
||||
let mut app = app(json!({"gradient": {"kind": "radial", "stops": stops}}));
|
||||
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(&mut app, thumb);
|
||||
assert_eq!(hexes(&app), ["#ff0000", "#ffff00", "#0000ff"], "a plain click keeps the gradient");
|
||||
p.click_with(&mut app, thumb, Modifiers::COMMAND);
|
||||
assert_eq!(fill(&app).gradient.kind, GradientKind::Linear);
|
||||
assert_eq!(hexes(&app), ["#ffffff", "#000000"]);
|
||||
assert_eq!(app.session.last_gradient.gradient.stops.len(), 2, "the Tools panel's Gradient button shows it");
|
||||
app.run("edit.undo", json!({})).unwrap();
|
||||
assert_eq!(fill(&app).gradient.kind, GradientKind::Radial, "one undo step");
|
||||
assert_eq!(hexes(&app), ["#ff0000", "#ffff00", "#0000ff"]);
|
||||
// With the Stroke proxy in front, a solid stroke becomes the gradient; the fill stays.
|
||||
app.run("paint.setStroke", json!({"color": "#00ff00"})).unwrap();
|
||||
app.session.fill_active = false;
|
||||
p.click_with(&mut app, thumb, Modifiers::COMMAND);
|
||||
let stroke = match crate::panels::current_paints(&app).1 {
|
||||
Paint::Gradient(g) => *g,
|
||||
s => panic!("expected a gradient stroke, got {s:?}"),
|
||||
};
|
||||
assert_eq!(stroke.gradient.kind, GradientKind::Linear);
|
||||
assert_eq!(stroke.gradient.stops.iter().map(|s| s.color.to_hex()).collect::<Vec<_>>(), ["#ffffff", "#000000"]);
|
||||
assert_eq!(fill(&app).gradient.kind, GradientKind::Radial);
|
||||
}
|
||||
|
||||
/// With the options hidden the Gradient Fill box stays, and Cmd/Ctrl-click on it resets a
|
||||
/// freeform gradient too.
|
||||
#[test]
|
||||
fn cmd_click_on_the_gradient_fill_box_with_the_options_hidden() {
|
||||
let mut app = app(json!({"gradient": {"kind": "freeform"}}));
|
||||
let mut p = Panel::new();
|
||||
set_pstate(&p.ctx, HIDE_OPTIONS, true);
|
||||
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);
|
||||
assert_eq!(fill(&app).gradient.kind, GradientKind::Linear);
|
||||
assert_eq!(hexes(&app), ["#ffffff", "#000000"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn alt_drag_copies_a_stop_and_alt_dropping_on_a_stop_swaps_colours() {
|
||||
let mut app = app(json!({"gradient": {}}));
|
||||
|
||||
@@ -496,7 +496,10 @@ fn bottom_controls(app: &mut VectorcraftApp, ui: &mut Ui, t: &Tokens, cols: usiz
|
||||
clicked = Some(cmd);
|
||||
}
|
||||
}
|
||||
if let Some(cmd) = clicked {
|
||||
// Cmd/Ctrl-click on Gradient resets the gradient to White, Black.
|
||||
if clicked == Some("paint.lastGradient") && ui.input(|i| i.modifiers.command) {
|
||||
crate::panels::gradient::reset_to_linear_white_black(app);
|
||||
} else if let Some(cmd) = clicked {
|
||||
app.run(cmd, json!({})).ok();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -610,6 +610,35 @@ fn the_paint_and_drawing_mode_buttons_fit_the_toolbar_width() {
|
||||
assert_eq!(app.session.draw_mode, vectorcraft_engine::DrawMode::Behind);
|
||||
}
|
||||
|
||||
/// A click on the Gradient button applies the last gradient; Cmd/Ctrl-click resets the active
|
||||
/// proxy's gradient to the linear White, Black gradient instead, as one undo step.
|
||||
#[test]
|
||||
fn cmd_click_on_the_gradient_button_resets_to_the_linear_white_black_gradient() {
|
||||
let mut app = VectorcraftApp::new(Session::new(), Default::default());
|
||||
app.run("file.new", json!({"width": 300, "height": 300})).unwrap();
|
||||
app.run("shape.rectangle", json!({"x": 100, "y": 100, "width": 100, "height": 100})).unwrap();
|
||||
let stops = json!([{"offset": 0, "color": "#ff0000"}, {"offset": 1, "color": "#0000ff"}]);
|
||||
app.run("paint.setFill", json!({"gradient": {"kind": "radial", "stops": stops}})).unwrap();
|
||||
app.run("paint.setFill", json!({"color": "#336699"})).unwrap();
|
||||
let fill = |app: &VectorcraftApp| match crate::panels::current_paints(app).0 {
|
||||
Paint::Gradient(g) => (g.gradient.kind, g.gradient.stops.iter().map(|s| s.color.to_hex()).collect::<Vec<_>>()),
|
||||
p => panic!("expected a gradient fill, got {p:?}"),
|
||||
};
|
||||
let ctx = egui::Context::default();
|
||||
frame_in(&mut app, &ctx, 0.0, vec![], 900.0);
|
||||
let gradient = widget_rects(&ctx, vec2(11.0, 11.0))[1].center();
|
||||
click_in(&mut app, &ctx, 1.0, gradient, PointerButton::Primary, 900.0);
|
||||
assert_eq!(fill(&app), (vectorcraft_color::GradientKind::Radial, vec!["#ff0000".into(), "#0000ff".into()]), "the last gradient");
|
||||
let m = egui::Modifiers::COMMAND;
|
||||
let b = |pressed| Event::PointerButton { pos: gradient, button: PointerButton::Primary, pressed, modifiers: m };
|
||||
frame_in(&mut app, &ctx, 2.0, vec![Event::ModifiersChanged(m), Event::PointerMoved(gradient), b(true)], 900.0);
|
||||
frame_in(&mut app, &ctx, 2.05, vec![b(false)], 900.0);
|
||||
frame_in(&mut app, &ctx, 2.1, vec![Event::ModifiersChanged(egui::Modifiers::NONE)], 900.0);
|
||||
assert_eq!(fill(&app), (vectorcraft_color::GradientKind::Linear, vec!["#ffffff".into(), "#000000".into()]));
|
||||
app.run("edit.undo", json!({})).unwrap();
|
||||
assert_eq!(fill(&app).0, vectorcraft_color::GradientKind::Radial, "one undo step");
|
||||
}
|
||||
|
||||
/// How many columns of tool buttons `rects` make.
|
||||
fn columns(rects: &[egui::Rect]) -> usize {
|
||||
let mut lefts: Vec<i32> = rects.iter().map(|r| r.left().round() as i32).collect();
|
||||
|
||||
Reference in New Issue
Block a user