Merge PR #500 from NikitkaRa: Format Shape: line compound, cap, join, arrows and sizes; lock aspect ratio

# Conflicts:
#	crates/engine/src/tests.rs
This commit is contained in:
Brandon Thomas
2026-10-14 13:08:46 -04:00
4 changed files with 128 additions and 5 deletions
+31 -3
View File
@@ -51,7 +51,7 @@ pub fn specs() -> Vec<CommandSpec> {
"Shape Outline",
["Shape Format", "Shape Styles"],
None,
"{color?, none?, width?: pt, dash?: solid|dot|dash|lgDash|dashDot|sysDot|sysDash, cap?, join?, head?: none|triangle|stealth|diamond|oval|arrow, tail?, reset?, ids?}",
"{color?, none?, width?: pt, dash?: solid|dot|dash|lgDash|dashDot|sysDot|sysDash, cap?: flat|round|square, join?: round|bevel|miter, compound?: single|double|thickThin|thinThick|triple, head?: none|triangle|stealth|diamond|oval|arrow, tail?, headSize?: sm|med|lg, tailSize?, reset?, ids?}",
has_selection,
line
),
@@ -350,6 +350,15 @@ fn line(s: &mut Session, p: &Value) -> Result<Value> {
"square" => deckcraft_model::style::LineCap::Square,
_ => deckcraft_model::style::LineCap::Flat,
});
let compound = str_param(p, "compound").map(|c| match c {
"double" => deckcraft_model::style::Compound::Double,
"thickThin" => deckcraft_model::style::Compound::ThickThin,
"thinThick" => deckcraft_model::style::Compound::ThinThick,
"triple" => deckcraft_model::style::Compound::Triple,
_ => deckcraft_model::style::Compound::Single,
});
let end_size = |k: &str| str_param(p, k).filter(|v| matches!(*v, "sm" | "med" | "lg")).map(str::to_string);
let (head_size, tail_size) = (end_size("headSize"), end_size("tailSize"));
let join = str_param(p, "join").map(|c| match c {
"bevel" => deckcraft_model::style::LineJoin::Bevel,
"miter" => deckcraft_model::style::LineJoin::Miter,
@@ -376,11 +385,20 @@ fn line(s: &mut Session, p: &Value) -> Result<Value> {
if dash.is_some() {
l.dash.clone_from(&dash);
}
let keep_size = |old: &Option<LineEnd>, new: &Option<LineEnd>| {
new.clone().map(|mut n| {
if let Some(o) = old {
n.w.clone_from(&o.w);
n.len.clone_from(&o.len);
}
n
})
};
if let Some(h) = &head {
l.head.clone_from(h);
l.head = keep_size(&l.head, h);
}
if let Some(t) = &tail {
l.tail.clone_from(t);
l.tail = keep_size(&l.tail, t);
}
if cap.is_some() {
l.cap = cap;
@@ -388,6 +406,16 @@ fn line(s: &mut Session, p: &Value) -> Result<Value> {
if join.is_some() {
l.join = join;
}
if compound.is_some() {
l.compound = compound;
}
// Arrowhead size (width and length together), kept when the kind changes.
for (end, size) in [(&mut l.head, &head_size), (&mut l.tail, &tail_size)] {
if let (Some(e), Some(sz)) = (end.as_mut(), size) {
e.w.clone_from(sz);
e.len.clone_from(sz);
}
}
Ok(())
})
}
+24
View File
@@ -1207,3 +1207,27 @@ fn pdf_options_take_paper_and_orientation() {
assert!(cmd::file::pdf_options(&json!({"paper": "tabloid"}), 1).is_err());
assert!(cmd::file::pdf_options(&json!({"orientation": "sideways"}), 1).is_err());
}
#[test]
fn shape_line_sets_compound_and_arrow_sizes() {
// The Format Shape pane's Line page: compound type, arrowheads and their sizes.
use deckcraft_model::style::Compound;
let mut s = Session::with_new();
s.execute("slide.new", &json!({"layout": "blank"})).unwrap();
s.execute("shape.insert", &json!({"preset": "line", "rect": [100, 100, 200, 0]})).unwrap();
s.execute("shape.line", &json!({"compound": "thickThin", "head": "oval", "tail": "triangle", "tailSize": "lg"})).unwrap();
let line = |s: &Session| s.doc().unwrap().selected_shapes()[0].line.clone().unwrap();
let l = line(&s);
assert_eq!(l.compound, Some(Compound::ThickThin));
assert_eq!(l.head.as_ref().map(|e| (e.kind.as_str(), e.w.as_str())), Some(("oval", "med")));
assert_eq!(l.tail.as_ref().map(|e| (e.kind.as_str(), e.w.as_str(), e.len.as_str())), Some(("triangle", "lg", "lg")));
// Changing the arrow keeps its size; an unknown size is ignored; "none" removes it.
s.execute("shape.line", &json!({"tail": "stealth", "headSize": "huge"})).unwrap();
let l = line(&s);
assert_eq!(l.tail.as_ref().map(|e| (e.kind.as_str(), e.w.as_str())), Some(("stealth", "lg")));
assert_eq!(l.head.as_ref().map(|e| e.w.as_str()), Some("med"));
s.execute("shape.line", &json!({"head": "none", "compound": "single"})).unwrap();
let l = line(&s);
assert!(l.head.is_none());
assert_eq!(l.compound, Some(Compound::Single));
}