mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-10 01:50:22 -06:00
Recolor Artwork: Reset discards slider edits and reads the art's own colors
Reset now also clears the slider edit, so the preview and OK keep each color's model again instead of sending model "new". Changing the Colors count, a Preserve flag or Limit to Library, or pressing Reset, with the preview on reduced the previewed colors, so the rows no longer matched the art; the preview is now rolled back before the colors are reduced. The Tint mode follows the swatch recolor.apply links: the first library swatch of the library color nearest the row's new color, shown as Tint only when that swatch is a spot color.
This commit is contained in:
@@ -163,6 +163,10 @@ fn sync(app: &mut VectorcraftApp, d: &mut Dialog) {
|
||||
if !d.bool("__art") || d.fields.get(DONE) == Some(&sig) {
|
||||
return;
|
||||
}
|
||||
// The art's own colors are reduced, not those of the preview, which runs again afterwards.
|
||||
if d.fields.remove(super::form::PREVIEWED).is_some() {
|
||||
let _ = app.session.cancel_interaction();
|
||||
}
|
||||
let mut p = sig.clone();
|
||||
p["method"] = json!(d.str("method"));
|
||||
match app.session.execute("recolor.reduce", &p) {
|
||||
@@ -488,6 +492,8 @@ fn assign_tab(app: &mut VectorcraftApp, ui: &mut Ui, d: &mut Dialog, rows: &mut
|
||||
}
|
||||
if widgets::icon_button(ui, "rotate-ccw", tl!("Reset the rows to the artwork's colors"), false, 24.0).clicked() {
|
||||
d.fields.remove("__reduced");
|
||||
d.fields.remove(SLID);
|
||||
d.fields.remove(COMPS);
|
||||
}
|
||||
let target = SliderTarget::of(app, d, rows);
|
||||
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| mode_menu(ui, d, &target));
|
||||
@@ -572,17 +578,15 @@ fn mode_id(m: Mode) -> String {
|
||||
serde_json::to_value(m).ok().and_then(|v| v.as_str().map(str::to_string)).unwrap_or_default()
|
||||
}
|
||||
|
||||
/// The name of the spot color of the library (Limit to Library) that new color `c` is.
|
||||
/// The name of the library swatch (Limit to Library) that `recolor.apply` gives new color `c`, when
|
||||
/// it is a spot color: the first swatch of the library color nearest `c`.
|
||||
fn spot_of(app: &VectorcraftApp, d: &Dialog, c: &Color) -> Option<String> {
|
||||
let lib = d.str("limitTo");
|
||||
if lib.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let k = ColorKey::of(c);
|
||||
swatchlib::library_swatches(&app.session, &lib)?
|
||||
.into_iter()
|
||||
.find(|w| w.spot && w.paint.color().is_some_and(|x| ColorKey::of(&x) == k))
|
||||
.map(|w| w.name)
|
||||
let near = swatchlib::limit_palette(&app.session, &lib)?.nearest(*c);
|
||||
swatchlib::library_swatches(&app.session, &lib)?.into_iter().find(|w| w.paint.color() == Some(near)).filter(|w| w.spot).map(|w| w.name)
|
||||
}
|
||||
|
||||
/// The mode the sliders show new color `c` in, `None` for Tint: the mode picked in the menu, else
|
||||
|
||||
@@ -346,6 +346,73 @@ fn the_tint_slider_sets_a_spot_rows_tint() {
|
||||
}
|
||||
}
|
||||
|
||||
/// Makes the swatches of the `swatch.new` params `swatches`, saves them as a library and loads it,
|
||||
/// removes them from the document again, and returns the library's id.
|
||||
fn library_of(app: &mut VectorcraftApp, swatches: &[Value]) -> String {
|
||||
let names: Vec<Value> = swatches.iter().map(|s| s["name"].clone()).collect();
|
||||
for s in swatches {
|
||||
app.run("swatch.new", s.clone()).unwrap();
|
||||
}
|
||||
let data = app.run("swatch.library.save", json!({"names": names, "name": "Lib"})).unwrap()["data"].as_str().unwrap().to_string();
|
||||
let lib = app.run("swatch.library.load", json!({"data": data, "name": "lib.vcswatches"})).unwrap()["library"].as_str().unwrap().to_string();
|
||||
app.run("swatch.delete", json!({"names": names})).unwrap();
|
||||
lib
|
||||
}
|
||||
|
||||
/// When the library's first swatch of a row's new color is a process color, the row shows no Tint,
|
||||
/// even with a spot color of that color after it: `recolor.apply` gives the art the first one.
|
||||
#[test]
|
||||
fn a_process_swatch_before_a_spot_one_of_its_color_shows_no_tint() {
|
||||
let mut app = app();
|
||||
let lib = library_of(
|
||||
&mut app,
|
||||
&[
|
||||
json!({"name": "Mix Process", "color": {"c": 0, "m": 1, "y": 0, "k": 0}}),
|
||||
json!({"name": "Mix Ink", "color": {"c": 0, "m": 1, "y": 0, "k": 0}, "spot": true}),
|
||||
],
|
||||
);
|
||||
art(&mut app, &["cmyk 0 100 0 0"]);
|
||||
let mut h = assign_harness(app, json!({"library": lib}));
|
||||
set(h.state_mut(), "__sel", json!([0]));
|
||||
h.run_steps(2);
|
||||
assert!(h.query_by_role_and_label(egui::accesskit::Role::Slider, "T").is_none(), "no Tint for a process swatch");
|
||||
assert!(h.query_by_role_and_label(egui::accesskit::Role::Slider, "C").is_some(), "the CMYK sliders");
|
||||
}
|
||||
|
||||
/// A spot color whose components are finer than a row's color key still shows Tint: the row's
|
||||
/// rounded color snaps back to it, as `recolor.apply` snaps it.
|
||||
#[test]
|
||||
fn a_spot_color_finer_than_the_rows_key_shows_tint() {
|
||||
let mut app = app();
|
||||
let lib = library_of(&mut app, &[json!({"name": "Mix Fine", "color": {"c": 0, "m": 0.12345, "y": 0, "k": 0}, "spot": true})]);
|
||||
art(&mut app, &["cmyk 0 12 0 0"]);
|
||||
let mut h = assign_harness(app, json!({"library": lib}));
|
||||
set(h.state_mut(), "__sel", json!([0]));
|
||||
h.run_steps(2);
|
||||
assert!(h.query_by_role_and_label(egui::accesskit::Role::Slider, "T").is_some(), "the spot row shows Tint");
|
||||
}
|
||||
|
||||
/// Reset with the preview on reads the art's own colors again and discards a slider edit: OK then
|
||||
/// gives RGB art a CMYK color group's color in RGB, as it does without the edit.
|
||||
#[test]
|
||||
fn reset_after_a_slider_edit_keeps_the_arts_model() {
|
||||
let mut app = app();
|
||||
app.run("swatch.newGroup", json!({"name": "Mix", "colors": ["cmyk 0 100 0 0"], "keepModel": true})).unwrap();
|
||||
art(&mut app, &["#ff00ff"]);
|
||||
let mut h = assign_harness(app, json!({"group": "Mix"}));
|
||||
set(h.state_mut(), "__sel", json!([0]));
|
||||
h.run_steps(2);
|
||||
click_slider(&mut h, "C", 0.5);
|
||||
assert_eq!(field(h.state(), "__slid"), json!(true), "the sliders edited the row");
|
||||
h.get_by_role_and_label(egui::accesskit::Role::Button, "Reset the rows to the artwork's colors").click();
|
||||
h.run_steps(3);
|
||||
assert_eq!(field(h.state(), "rows")[0]["from"], json!(["rgb 255 0 255"]), "Reset reads the art's colors, not the preview's");
|
||||
h.get_by_label("OK").click();
|
||||
h.run_steps(2);
|
||||
assert!(h.state().ui.dialog.is_none());
|
||||
assert!(matches!(first_fill(h.state()).color(), Some(Color::Rgb { .. })), "the art stays RGB: {:?}", first_fill(h.state()));
|
||||
}
|
||||
|
||||
/// The dialog keeps its height when the tab or the sliders' color mode changes.
|
||||
#[test]
|
||||
fn switching_tabs_or_slider_modes_keeps_the_dialog_height() {
|
||||
|
||||
Reference in New Issue
Block a user