mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-14 19:26:56 -04:00
130 lines
6.3 KiB
Rust
130 lines
6.3 KiB
Rust
//! Visible art is readable by the Eyedropper without making it editable (#1251).
|
|
#![allow(clippy::unwrap_used)]
|
|
|
|
use serde_json::json;
|
|
use vectorcraft_doc::NodeId;
|
|
use vectorcraft_engine::{Session, UiRequest, ViewInfo};
|
|
use vectorcraft_tools::{Mods, PointerEvent, PointerKind};
|
|
|
|
fn fixture() -> (Session, NodeId, NodeId, NodeId) {
|
|
let mut s = Session::new();
|
|
s.execute("file.new", &json!({"width": 400, "height": 300})).unwrap();
|
|
let layer = s.doc().unwrap().doc.layers[0].id;
|
|
let source = NodeId(s.execute("shape.rectangle", &json!({"x": 20, "y": 20, "width": 60, "height": 60})).unwrap()["id"].as_u64().unwrap());
|
|
s.execute("paint.setFill", &json!({"color": "#e23355"})).unwrap();
|
|
s.execute("stroke.set", &json!({"weight": 7})).unwrap();
|
|
s.execute("layer.new", &json!({"name": "Target"})).unwrap();
|
|
let target = NodeId(s.execute("shape.rectangle", &json!({"x": 140, "y": 100, "width": 120, "height": 80})).unwrap()["id"].as_u64().unwrap());
|
|
s.execute("paint.setFill", &json!({"color": "#2255ee"})).unwrap();
|
|
s.execute("stroke.set", &json!({"weight": 1})).unwrap();
|
|
(s, source, target, layer)
|
|
}
|
|
|
|
fn click(s: &mut Session, mods: Mods) {
|
|
s.select_tool("eyedropper", ViewInfo::default()).unwrap();
|
|
for kind in [PointerKind::Down, PointerKind::Up] {
|
|
s.pointer(&PointerEvent::new(kind, 40.0, 40.0).with_mods(mods), ViewInfo::default()).unwrap();
|
|
}
|
|
}
|
|
|
|
fn assert_sample(s: &mut Session, source: NodeId, target: NodeId, mods: Mods) {
|
|
let st = s.doc().unwrap();
|
|
let before = st.doc.clone();
|
|
let selection = st.selection.clone();
|
|
let history = st.history.undo.len();
|
|
click(s, mods);
|
|
let st = s.doc().unwrap();
|
|
let source_before = before.node(source).unwrap();
|
|
let target_after = st.doc.node(target).unwrap();
|
|
assert_eq!(json!(st.doc.node(source).unwrap()), json!(source_before), "sampling must not edit its source");
|
|
assert_eq!(json!(st.selection), json!(selection));
|
|
assert_eq!(target_after.geometric_bounds(), before.node(target).unwrap().geometric_bounds());
|
|
if mods.alt {
|
|
assert_eq!(target_after.appearance.items.len(), before.node(target).unwrap().appearance.items.len() + source_before.appearance.items.len());
|
|
} else {
|
|
assert_eq!(target_after.appearance.fill_paint(), source_before.appearance.fill_paint());
|
|
assert_eq!(target_after.appearance.stroke().unwrap().width, if mods.shift { 1.0 } else { 7.0 });
|
|
}
|
|
assert_eq!(st.history.undo.len(), history + 1);
|
|
let after = st.doc.clone();
|
|
s.execute("edit.undo", &json!({})).unwrap();
|
|
assert_eq!(json!(s.doc().unwrap().doc), json!(before));
|
|
s.execute("edit.redo", &json!({})).unwrap();
|
|
assert_eq!(json!(s.doc().unwrap().doc), json!(after));
|
|
}
|
|
|
|
fn read_modes() -> [Mods; 3] {
|
|
[Mods::default(), Mods { shift: true, ..Default::default() }, Mods { shift: true, alt: true, ..Default::default() }]
|
|
}
|
|
|
|
#[test]
|
|
fn locked_objects_and_layers_are_sources_in_all_read_modes() {
|
|
for lock_layer in [false, true] {
|
|
for mods in read_modes() {
|
|
let (mut s, source, target, layer) = fixture();
|
|
s.execute("layer.setProps", &json!({"id": if lock_layer { layer } else { source }, "locked": true})).unwrap();
|
|
assert_sample(&mut s, source, target, mods);
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn isolation_allows_reading_surrounding_art_without_leaving_the_group() {
|
|
for mods in read_modes() {
|
|
let (mut s, source, target, _) = fixture();
|
|
let group = NodeId(s.execute("object.group", &json!({})).unwrap()["id"].as_u64().unwrap());
|
|
s.execute("object.isolate", &json!({"id": group})).unwrap();
|
|
s.execute("select.set", &json!({"ids": [target]})).unwrap();
|
|
assert_sample(&mut s, source, target, mods);
|
|
assert_eq!(s.doc().unwrap().isolation, Some(group));
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn alt_reverse_still_respects_locks_and_isolation_and_hidden_sources_are_skipped() {
|
|
for restriction in ["object", "layer", "isolation", "hidden"] {
|
|
let (mut s, source, target, layer) = fixture();
|
|
match restriction {
|
|
"isolation" => {
|
|
let group = s.execute("object.group", &json!({})).unwrap()["id"].clone();
|
|
s.execute("object.isolate", &json!({"id": group})).unwrap();
|
|
s.execute("select.set", &json!({"ids": [target]})).unwrap();
|
|
}
|
|
"hidden" => {
|
|
s.execute("layer.setProps", &json!({"id": layer, "visible": false})).unwrap();
|
|
}
|
|
_ => {
|
|
s.execute("layer.setProps", &json!({"id": if restriction == "layer" { layer } else { source }, "locked": true})).unwrap();
|
|
}
|
|
}
|
|
let before = json!(s.doc().unwrap().doc);
|
|
let history = s.doc().unwrap().history.undo.len();
|
|
click(&mut s, Mods { alt: restriction != "hidden", ..Default::default() });
|
|
assert_eq!(json!(s.doc().unwrap().doc), before);
|
|
assert_eq!(s.doc().unwrap().history.undo.len(), history);
|
|
s.select_tool("selection", ViewInfo::default()).unwrap();
|
|
s.pointer(&PointerEvent::new(PointerKind::Down, 40.0, 40.0), ViewInfo::default()).unwrap();
|
|
assert!(!s.doc().unwrap().selection.objects.contains(&source));
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn a_gradient_stop_can_read_a_locked_source_and_return_to_the_gradient_tool() {
|
|
let (mut s, source, target, layer) = fixture();
|
|
s.execute("paint.editGradient", &json!({})).unwrap();
|
|
s.execute("gradient.selectStop", &json!({"index": 1})).unwrap();
|
|
s.execute("layer.setProps", &json!({"id": layer, "locked": true})).unwrap();
|
|
s.select_tool("eyedropper", ViewInfo::default()).unwrap();
|
|
s.set_tool_option("stop", &json!("gradient"));
|
|
let before = json!(s.doc().unwrap().doc.node(source).unwrap());
|
|
let requests = s
|
|
.pointer(&PointerEvent::new(PointerKind::Down, 40.0, 40.0).with_mods(Mods { alt: true, ..Default::default() }), ViewInfo::default())
|
|
.unwrap();
|
|
assert!(matches!(requests.as_slice(), [UiRequest::SwitchTool(tool)] if tool == "gradient"));
|
|
assert_eq!(s.tool_options()["stop"], json!(null));
|
|
assert_eq!(json!(s.doc().unwrap().doc.node(source).unwrap()), before);
|
|
let paint = s.doc().unwrap().doc.node(target).unwrap().appearance.fill_paint();
|
|
let vectorcraft_color::Paint::Gradient(g) = paint else { panic!("gradient retained") };
|
|
assert_eq!(g.gradient.stops[1].color.to_hex(), "#e23355");
|
|
}
|