Select: the font Same commands take their reference from the type on a selected layer

A layer that was selected (for example with select.set) gave no reference to
Select > Same > Font Family, Font Family & Style, Font Family, Style & Size,
Font Size and the text fill and stroke color commands: the layer itself is
not selectable, so the type on it was never reached and the command returned
"select a text object". A selected layer or sublayer now gives the type on it
that can be selected. A hidden, locked or template layer gives none.
This commit is contained in:
dotCooCoo
2026-11-04 12:44:48 +07:00
committed by bflatastic
parent 5c224a0d08
commit f581aa8a56
2 changed files with 42 additions and 5 deletions
+12 -5
View File
@@ -328,8 +328,9 @@ fn same_text(s: &mut Session, cmd: &str, eq: fn(&CharStyle, &CharStyle) -> bool)
/// Call `f` with each type object among the selected objects and inside them, in selection order,
/// keeping only those [`walk_selectable`] visits: none that is hidden, locked, on a hidden, locked
/// or template layer, or outside the isolated group. A type object inside two selected objects is
/// given twice. The walk stops when `f` returns false.
/// or template layer, or outside the isolated group. A selected layer or sublayer gives the type
/// on it that passes the same test. A type object inside two selected objects is given twice. The
/// walk stops when `f` returns false.
fn each_selected_text(s: &Session, mut f: impl FnMut(NodeId) -> bool) {
fn inside(n: &Node, f: &mut impl FnMut(NodeId) -> bool) -> bool {
if !n.visible || n.locked {
@@ -340,12 +341,18 @@ fn each_selected_text(s: &Session, mut f: impl FnMut(NodeId) -> bool) {
}
n.children().into_iter().flatten().all(|c| inside(c, f))
}
fn from(n: &Node, selectable: &impl Fn(NodeId) -> bool, f: &mut impl FnMut(NodeId) -> bool) -> bool {
if n.is_layer() {
return n.children().into_iter().flatten().all(|c| from(c, selectable, f));
}
!selectable(n.id) || inside(n, f)
}
let Some(st) = s.active() else { return };
let locks = st.doc.locked_artboards();
let selectable = |id| is_selectable(&st.doc, st.isolation, &locks, id);
for id in &st.selection.objects {
if is_selectable(&st.doc, st.isolation, &locks, *id)
&& let Some(n) = st.doc.node(*id)
&& !inside(n, &mut f)
if let Some(n) = st.doc.node(*id)
&& !from(n, &selectable, &mut f)
{
return;
}
+30
View File
@@ -1995,6 +1995,36 @@ fn select_same_font_skips_type_that_cannot_be_selected() {
assert_eq!(selected(&s), vec![g]);
}
/// A selected layer gives the type on it that can be selected as the reference of the font Same
/// commands: a hidden or locked layer gives none.
#[test]
fn select_same_font_takes_its_reference_from_the_type_on_a_selected_layer() {
let mut s = session();
let typed =
|s: &mut Session, y: f64, font: &str| id_of(&s.execute("text.create", &json!({"x": 0, "y": y, "text": "type", "font": font})).unwrap());
let arial = typed(&mut s, 20.0, "Arial");
let courier = typed(&mut s, 60.0, "Courier");
let l2 = id_of(&s.execute("layer.new", &json!({"top": true})).unwrap());
let on_layer = typed(&mut s, 100.0, "Courier");
sel(&mut s, &[l2]);
assert_eq!(s.execute("select.same.fontFamily", &json!({})).unwrap()["count"], 2);
let mut got = selected(&s);
got.sort();
let mut want = vec![courier, on_layer];
want.sort();
assert_eq!(got, want);
assert!(!selected(&s).contains(&arial));
for (off, on) in [
(json!({"id": l2.0, "visible": false}), json!({"id": l2.0, "visible": true})),
(json!({"id": l2.0, "locked": true}), json!({"id": l2.0, "locked": false})),
] {
s.execute("layer.setProps", &off).unwrap();
sel(&mut s, &[l2]);
assert!(s.execute("select.same.fontFamily", &json!({})).is_err(), "{off}");
s.execute("layer.setProps", &on).unwrap();
}
}
/// Select › Same › Text Fill & Stroke Color selects the type with characters whose fill and stroke
/// both match the first character of the selected type: the same fill with another stroke, or the
/// same stroke with another fill, is left out, and in type with several runs one run has to match