mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-28 03:37:00 -06:00
Merge pull request #4309 from nrq6993-a11y/stream/vc-feat3-g4-mixed-optical-margin
G4: Optical margin menu uses shared text flag
This commit is contained in:
@@ -40,8 +40,7 @@ pub fn checked(app: &VectorcraftApp, id: &str, p: &Value) -> Option<bool> {
|
||||
"type.orientation.vertical" | "type.orientation.horizontal" => {
|
||||
selected_text(false).is_some_and(|t| t.vertical == (id == "type.orientation.vertical"))
|
||||
}
|
||||
// Checked when the first type object `selected_text` finds has it on.
|
||||
"type.opticalMarginAlignment" => selected_text(false).is_some_and(|t| t.optical_margin),
|
||||
"type.opticalMarginAlignment" => crate::panels::character::shared_text_flag(app, |t| t.optical_margin) == Some(true),
|
||||
"view.outline" => v.outline,
|
||||
"view.pixelPreview" => v.pixel_preview,
|
||||
"view.trimView" => v.trim_view,
|
||||
|
||||
@@ -239,6 +239,22 @@ fn optical_margin_alignment_is_a_checked_type_menu_item() {
|
||||
assert_eq!(checked(&app, "type.orientation.horizontal", &Value::Null), Some(false));
|
||||
}
|
||||
|
||||
/// Type › Optical Margin Alignment menu check uses `shared_text_flag`; mixed values stay unchecked.
|
||||
#[test]
|
||||
fn optical_margin_alignment_menu_unchecked_when_shared_flag_none() {
|
||||
let mut app = VectorcraftApp::new(vectorcraft_engine::Session::new(), crate::Services::default());
|
||||
app.run("file.new", json!({})).unwrap();
|
||||
let off = app.run("text.create", json!({"x": 10, "y": 40, "text": "Off"})).unwrap()["id"].clone();
|
||||
let on = app.run("text.create", json!({"x": 10, "y": 80, "text": "On"})).unwrap()["id"].clone();
|
||||
app.run("type.opticalMarginAlignment", json!({"on": true, "id": on})).unwrap();
|
||||
app.run("select.set", json!({"ids": [off, on]})).unwrap();
|
||||
let id = "type.opticalMarginAlignment";
|
||||
assert_eq!(crate::panels::character::shared_text_flag(&app, |t| t.optical_margin), None);
|
||||
assert_eq!(checked(&app, id, &Value::Null), Some(false));
|
||||
app.run("select.set", json!({"ids": [on]})).unwrap();
|
||||
assert_eq!(checked(&app, id, &Value::Null), Some(true));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn panel_ids_are_case_insensitive_and_labels_work() {
|
||||
assert_eq!(normalize_panel("layers"), Some("layers"));
|
||||
|
||||
@@ -54,6 +54,38 @@ pub(crate) fn first_selected_text(app: &VectorcraftApp) -> Option<&vectorcraft_d
|
||||
None
|
||||
}
|
||||
|
||||
/// A boolean on every selected text object (`Some(true)` / `Some(false)`), or `None` when they
|
||||
/// differ. When the Type tool edits text, only that object counts.
|
||||
pub(crate) fn shared_text_flag(app: &VectorcraftApp, f: impl Fn(&vectorcraft_doc::TextObject) -> bool) -> Option<bool> {
|
||||
if let Some((id, _, _)) = text_editing(app) {
|
||||
return app.session.active().and_then(|st| st.doc.node(id)).and_then(|n| match &n.kind {
|
||||
NodeKind::Text(t) => Some(f(t)),
|
||||
_ => None,
|
||||
});
|
||||
}
|
||||
let st = app.session.active()?;
|
||||
let mut value: Option<bool> = None;
|
||||
let mut mixed = false;
|
||||
let mut saw = false;
|
||||
for node in st.selection.objects.iter().filter_map(|id| st.doc.node(*id)) {
|
||||
node.walk(&mut |n| {
|
||||
if let NodeKind::Text(t) = &n.kind {
|
||||
saw = true;
|
||||
if mixed {
|
||||
return;
|
||||
}
|
||||
let v = f(t);
|
||||
match value {
|
||||
None => value = Some(v),
|
||||
Some(a) if a == v => {}
|
||||
Some(_) => mixed = true,
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
if !saw || mixed { None } else { value }
|
||||
}
|
||||
|
||||
/// `f` of the character styles the panels act on: the runs the Type tool's selection covers (the
|
||||
/// caret's style at a caret), else every run of the selected type, groups' included. `None` (a
|
||||
/// blank field) where they differ, or with no type.
|
||||
|
||||
@@ -488,6 +488,30 @@ fn character_direction_checks_the_direction_of_the_selection() {
|
||||
assert_eq!(checked(&mut app), ["Default"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shared_text_flag_optical_margin_three_object_selections() {
|
||||
let mut app = VectorcraftApp::new(Session::new(), Default::default());
|
||||
app.run("file.new", json!({})).unwrap();
|
||||
let a = app.run("text.create", json!({"x": 10, "y": 40, "text": "a"})).unwrap()["id"].clone();
|
||||
let b = app.run("text.create", json!({"x": 10, "y": 80, "text": "b"})).unwrap()["id"].clone();
|
||||
let c = app.run("text.create", json!({"x": 10, "y": 120, "text": "c"})).unwrap()["id"].clone();
|
||||
app.run("type.opticalMarginAlignment", json!({"on": true, "id": a})).unwrap();
|
||||
app.run("type.opticalMarginAlignment", json!({"on": false, "id": b})).unwrap();
|
||||
app.run("type.opticalMarginAlignment", json!({"on": false, "id": c})).unwrap();
|
||||
app.run("select.set", json!({"ids": [a.clone(), b.clone(), c.clone()]})).unwrap();
|
||||
assert_eq!(shared_text_flag(&app, |t| t.optical_margin), None);
|
||||
app.run("type.opticalMarginAlignment", json!({"on": false, "id": a})).unwrap();
|
||||
app.run("type.opticalMarginAlignment", json!({"on": true, "id": b})).unwrap();
|
||||
app.run("type.opticalMarginAlignment", json!({"on": true, "id": c})).unwrap();
|
||||
app.run("select.set", json!({"ids": [a.clone(), b.clone(), c.clone()]})).unwrap();
|
||||
assert_eq!(shared_text_flag(&app, |t| t.optical_margin), None);
|
||||
app.run("type.opticalMarginAlignment", json!({"on": true, "id": a})).unwrap();
|
||||
app.run("type.opticalMarginAlignment", json!({"on": true, "id": b})).unwrap();
|
||||
app.run("type.opticalMarginAlignment", json!({"on": true, "id": c})).unwrap();
|
||||
app.run("select.set", json!({"ids": [a, b, c]})).unwrap();
|
||||
assert_eq!(shared_text_flag(&app, |t| t.optical_margin), Some(true));
|
||||
}
|
||||
|
||||
/// Small Caps, Superscript and Subscript buttons and menu items stay off when the selection mixes them.
|
||||
#[test]
|
||||
fn character_mixed_small_caps_and_position_show_off() {
|
||||
|
||||
Reference in New Issue
Block a user