vectorcraft/crates/engine/src/tests_proxyitems.rs

106 lines
5.2 KiB
Rust

//! The Fill/Stroke proxies and the Appearance panel's active item: Invert / Complement and the "?"
//! state follow the active fill or stroke, objects without bounds still show their paints, and the
//! proxy focus can be set directly (M3.51).
use serde_json::json;
use vectorcraft_color::{Color, Paint};
use vectorcraft_doc::{Appearance, Node};
use vectorcraft_geom::PathData;
use super::*;
fn session() -> Session {
let mut s = Session::new();
s.execute("file.new", &json!({"width": 400, "height": 300})).unwrap();
s
}
/// A selected rectangle with a red fill below its stroke and a blue fill on top: [Fill, Stroke, Fill].
fn two_fills(s: &mut Session, x: f64) -> NodeId {
let r = s.execute("shape.rectangle", &json!({"x": x, "y": 10, "width": 50, "height": 50})).unwrap();
s.execute("paint.setFill", &json!({"color": "#ff0000"})).unwrap();
s.execute("appearance.addFill", &json!({})).unwrap();
s.execute("appearance.setItem", &json!({"index": 2, "color": "#0000ff"})).unwrap();
NodeId(r["id"].as_u64().unwrap())
}
fn item_hex(s: &Session, id: NodeId, index: usize) -> String {
s.doc().unwrap().doc.node(id).unwrap().appearance.items[index].paint().color().unwrap().to_hex()
}
#[test]
fn invert_and_complement_recolour_the_active_item() {
let mut s = session();
let id = two_fills(&mut s, 10.0);
// Without an active item the proxy's colours go (every fill, as Edit Colors does).
s.execute("paint.invert", &json!({})).unwrap();
assert_eq!((item_hex(&s, id, 0), item_hex(&s, id, 2)), ("#00ffff".into(), "#ffff00".into()));
s.execute("edit.undo", &json!({})).unwrap();
// The bottom fill is the Appearance panel's active row: only it changes, as one undo step.
s.execute("appearance.setActiveItem", &json!({"index": 0})).unwrap();
let undo = s.doc().unwrap().history.undo.len();
assert_eq!(s.execute("paint.invert", &json!({})).unwrap(), json!({"changed": 1}));
assert_eq!((item_hex(&s, id, 0), item_hex(&s, id, 2)), ("#00ffff".into(), "#0000ff".into()));
assert_eq!(s.doc().unwrap().history.undo.len(), undo + 1);
assert_eq!(s.proxy_paints().0.color().unwrap().to_hex(), "#00ffff", "the proxy shows the active row");
assert_eq!(s.recent_colors[0].to_hex(), "#00ffff");
s.execute("paint.complement", &json!({})).unwrap();
assert_eq!((item_hex(&s, id, 0), item_hex(&s, id, 2)), ("#ff0000".into(), "#0000ff".into()));
// The Stroke proxy isn't the active row's kind: the stroke is complemented as usual.
s.execute("paint.complement", &json!({"stroke": true})).unwrap();
assert_eq!(item_hex(&s, id, 1), "#000000", "black's complement is black");
assert_eq!((item_hex(&s, id, 0), item_hex(&s, id, 2)), ("#ff0000".into(), "#0000ff".into()));
}
#[test]
fn the_mixed_proxy_compares_the_active_item() {
let mut s = session();
let a = two_fills(&mut s, 10.0);
let b = two_fills(&mut s, 100.0);
// Same top fills, different bottom fills.
s.execute("appearance.setItem", &json!({"index": 0, "color": "#00ff00", "ids": [b.0]})).unwrap();
s.execute("select.set", &json!({"ids": [a.0, b.0]})).unwrap();
assert_eq!(s.proxy_mixed(), (false, false));
s.execute("appearance.setActiveItem", &json!({"index": 0})).unwrap();
assert_eq!(s.proxy_mixed(), (true, false), "the active bottom fills differ");
assert_eq!(s.execute("paint.proxies", &json!({})).unwrap()["fillMixed"], true);
// A stroke row leaves the fill proxy on the top fills.
s.execute("appearance.setActiveItem", &json!({"index": 1})).unwrap();
assert_eq!(s.proxy_mixed(), (false, false));
}
#[test]
fn an_object_without_bounds_shows_its_paints() {
let mut s = session();
let id = s
.edit("Add", |d, _| {
let l = d.layers[0].id;
let id = d.alloc_id();
let ap = Appearance::basic(Paint::solid(Color::from_hex("#123456").unwrap()), Paint::solid(Color::from_hex("#654321").unwrap()), 2.0);
d.insert(Some(l), 0, Node::path(id, PathData::default(), ap)).map_err(|e| crate::EngineError::Other(e.to_string()))
})
.unwrap();
assert!(s.doc().unwrap().doc.node(id).unwrap().geometric_bounds().is_none());
s.execute("select.set", &json!({"ids": [id.0]})).unwrap();
let (f, st) = s.proxy_paints();
assert_eq!((f.color().unwrap().to_hex(), st.color().unwrap().to_hex()), ("#123456".into(), "#654321".into()));
}
#[test]
fn toggle_active_can_name_the_proxy() {
let mut s = Session::new();
assert_eq!(s.execute("paint.toggleActive", &json!({"fill": false})).unwrap(), json!({"fillActive": false}));
assert_eq!(s.execute("paint.toggleActive", &json!({"fill": false})).unwrap(), json!({"fillActive": false}), "already in front");
assert_eq!(s.execute("paint.toggleActive", &json!({})).unwrap(), json!({"fillActive": true}));
}
#[test]
fn the_last_colour_keeps_its_model() {
let mut s = session();
s.execute("shape.rectangle", &json!({"x": 10, "y": 10, "width": 50, "height": 50})).unwrap();
s.execute("paint.setFill", &json!({"color": {"c": 0.1, "m": 0.9, "y": 0.2, "k": 0.0}})).unwrap();
s.execute("paint.setFill", &json!({"none": true})).unwrap();
s.execute("paint.lastColor", &json!({})).unwrap();
assert_eq!(s.proxy_paints().0.color(), Some(Color::cmyk(0.1, 0.9, 0.2, 0.0)));
}