This commit is contained in:
aiongg
2026-10-25 02:21:07 +00:00
4 changed files with 184 additions and 1 deletions
+23
View File
@@ -42,6 +42,29 @@ pub fn field_type(kind: &StyleKind, key: &str) -> Option<VariableType> {
}
}
/// The variable scopes a picker offers for `key` of a style of `kind`: a variable scoped to any one
/// of them fits. A paint style's colors take fill and stroke variables, an effect style's fields
/// take effect variables, and a text style's fields take the variables of their font property.
/// None when every variable of the field's type fits (a grid's fields, as on layers) or the style
/// has no such field.
pub fn field_scopes(kind: &StyleKind, key: &str) -> Option<&'static [&'static str]> {
field_type(kind, key)?;
Some(match (kind, list_binding(key)) {
(StyleKind::Paint { .. }, _) => &["FRAME_FILL", "SHAPE_FILL", "TEXT_FILL", "STROKE_COLOR"],
(StyleKind::Effect { .. }, Some((_, _, "color"))) => &["EFFECT_COLOR"],
(StyleKind::Effect { .. }, _) => &["EFFECT_FLOAT"],
(StyleKind::Text { .. }, _) => match key {
"fontSize" => &["FONT_SIZE"],
"lineHeight" => &["LINE_HEIGHT"],
"letterSpacing" => &["LETTER_SPACING"],
"fontFamily" => &["FONT_FAMILY"],
"fontStyle" => &["FONT_STYLE"],
_ => return None,
},
_ => return None,
})
}
/// The key a style stores a binding of `key` under: the first paint's colour has one spelling.
pub fn canonical(key: &str) -> String {
match list_binding(key) {
+40
View File
@@ -182,6 +182,46 @@ fn what_a_style_cannot_bind_is_refused() {
}
}
#[test]
fn style_fields_offer_their_properties_scopes() {
let mut s = Session::new();
let (_, _, a, _, sid) = two_modes(&mut s);
run(&mut s, "effect.add", json!({"ids": [a], "type": "dropShadow"}));
let fx = run(&mut s, "style.create", json!({"type": "effect", "name": "Drop", "from": a}))["id"].as_str().unwrap().to_string();
let light = node(&s, a).parent.unwrap();
run(&mut s, "grid.add", json!({"ids": [light]}));
let grid = run(&mut s, "style.create", json!({"type": "grid", "name": "Base", "from": light}))["id"].as_str().unwrap().to_string();
let body = run(&mut s, "style.create", json!({"type": "text", "name": "Body", "value": {"fontFamily": "Inter", "fontSize": 14}}))["id"]
.as_str()
.unwrap()
.to_string();
let doc = s.doc().unwrap();
let scopes = |st: &str, key: &str| crate::style_vars::field_scopes(&doc.style(st).unwrap().kind, key);
let fills: &[&str] = &["FRAME_FILL", "SHAPE_FILL", "TEXT_FILL", "STROKE_COLOR"];
for (st, key, want) in [
(&sid, "fill", Some(fills)),
(&sid, "fills/0/color", Some(fills)),
(&fx, "effects/0/color", Some(&["EFFECT_COLOR"][..])),
(&fx, "effects/0/radius", Some(&["EFFECT_FLOAT"][..])),
(&fx, "effects/0/offsetX", Some(&["EFFECT_FLOAT"][..])),
(&body, "fontSize", Some(&["FONT_SIZE"][..])),
(&body, "lineHeight", Some(&["LINE_HEIGHT"][..])),
(&body, "letterSpacing", Some(&["LETTER_SPACING"][..])),
(&body, "fontFamily", Some(&["FONT_FAMILY"][..])),
(&body, "fontStyle", Some(&["FONT_STYLE"][..])),
// Grid fields take any number variable, as on layers; fields a style lacks take none.
(&grid, "layoutGrids/0/size", None),
(&sid, "fills/3/color", None),
(&body, "opacity", None),
] {
assert_eq!(scopes(st, key), want, "{st} {key}");
}
// As on layers, scopes limit pickers only: an out-of-scope variable still binds.
run(&mut s, "variable.update", json!({"variable": "size", "scopes": ["CORNER_RADIUS"]}));
run(&mut s, "style.bind", json!({"style": body, "field": "fontSize", "variable": "size"}));
assert!(s.doc().unwrap().style(&body).unwrap().bound.contains_key("fontSize"));
}
#[test]
fn rest_and_fig_carry_style_bindings() {
let mut s = Session::new();