mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-07 15:51:56 +00:00
Select: Link Block Series ignores selected type that Select All cannot reach
Type selected by id on a hidden, locked or template layer, or outside the isolated group, no longer counts as selected type: the command stays off and does not select that frame's thread. The check reads the object's ancestry, so the Select menu does not walk the whole document.
This commit is contained in:
@@ -11,7 +11,7 @@ use vectorcraft_geom::Vec2;
|
||||
|
||||
use super::edit::selected_roots;
|
||||
use super::pathops::Beside;
|
||||
use super::select::{selectable_ids, walk_selectable};
|
||||
use super::select::{is_selectable, selectable_ids, walk_selectable};
|
||||
use super::{
|
||||
CommandSpec, EngineError, Point, Result, Session, bad, bool_or, checked_ids_param, cmd, f64_or, f64_req, has_doc, has_selection, id_param, ok,
|
||||
str_param,
|
||||
@@ -32,7 +32,7 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
"Link Block Series",
|
||||
["Select", "Same"],
|
||||
None,
|
||||
"{} select every frame of the threads of the selected type objects (and of the visible, unlocked type inside selected groups); type in no thread selects itself; frames that are hidden or locked, inside a hidden or locked object or layer, on a template layer, or outside the isolated group or layer are left out → {count}",
|
||||
"{} select every frame of the threads of the selected type objects (and of the type inside selected groups); type in no thread selects itself; type and frames that are hidden or locked, inside a hidden or locked object or layer, on a template layer, or outside the isolated group or layer neither count as selected nor are selected → {count}",
|
||||
has_text_selection,
|
||||
same_link_block_series
|
||||
),
|
||||
@@ -322,8 +322,9 @@ fn same_text(s: &mut Session, cmd: &str, eq: fn(&CharStyle, &CharStyle) -> bool)
|
||||
finish_same(s, cmd, ids)
|
||||
}
|
||||
|
||||
/// The type objects among the selected objects and inside them, in selection order, leaving out
|
||||
/// every hidden or locked object with what it holds.
|
||||
/// The type objects 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.
|
||||
fn selected_text(s: &Session) -> Vec<NodeId> {
|
||||
fn inside(n: &Node, out: &mut Vec<NodeId>) {
|
||||
if !n.visible || n.locked {
|
||||
@@ -338,8 +339,12 @@ fn selected_text(s: &Session) -> Vec<NodeId> {
|
||||
}
|
||||
let Some(st) = s.active() else { return vec![] };
|
||||
let mut out = vec![];
|
||||
for n in st.selection.objects.iter().filter_map(|id| st.doc.node(*id)) {
|
||||
inside(n, &mut out);
|
||||
for id in &st.selection.objects {
|
||||
if is_selectable(&st.doc, st.isolation, *id)
|
||||
&& let Some(n) = st.doc.node(*id)
|
||||
{
|
||||
inside(n, &mut out);
|
||||
}
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
@@ -249,6 +249,25 @@ pub(crate) fn selectable_ids(d: &Document, iso: Option<NodeId>) -> BTreeSet<Node
|
||||
ids
|
||||
}
|
||||
|
||||
/// Whether [`walk_selectable`] visits `id`, read from the objects that hold it instead of a walk of
|
||||
/// the document.
|
||||
pub(crate) fn is_selectable(d: &Document, iso: Option<NodeId>, id: NodeId) -> bool {
|
||||
let Some(a) = d.ancestry(id) else { return false };
|
||||
if !a.iter().all(|i| d.node(*i).is_some_and(|n| n.visible && !n.locked && !n.is_template())) {
|
||||
return false;
|
||||
}
|
||||
match iso.and_then(|i| d.node(i)) {
|
||||
// Inside the isolated container, or the single object isolated.
|
||||
Some(c) => a.iter().position(|i| *i == c.id).is_some_and(|p| p + 1 < a.len() || !c.is_container()),
|
||||
None => {
|
||||
let locks = d.locked_artboards();
|
||||
// The object Select All takes: the outermost one that is not a layer.
|
||||
let top = a.iter().filter_map(|i| d.node(*i)).find(|n| !n.is_layer());
|
||||
top.is_some_and(|t| locks.is_empty() || !locks.holds(t))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn all(s: &mut Session, _: &Value) -> Result<Value> {
|
||||
// While the Type tool edits text, Select All takes all of that text and leaves the art
|
||||
// selection as it is (Illustrator: text cursor in a text object).
|
||||
|
||||
@@ -1993,6 +1993,22 @@ fn select_same_link_block_series_selects_the_frames_of_a_thread() {
|
||||
assert!(s.execute("select.same.linkBlockSeries", &json!({})).is_err(), "{props}");
|
||||
assert_eq!(selected(&s), vec![g], "{props}");
|
||||
}
|
||||
s.execute("layer.setProps", &json!({"id": d.0, "locked": false})).unwrap();
|
||||
// A frame on a hidden layer, selected by id, is not type to start from.
|
||||
let l1 = s.doc().unwrap().doc.layers[0].id;
|
||||
s.execute("layer.setProps", &json!({"id": l1.0, "visible": false})).unwrap();
|
||||
sel(&mut s, &[a]);
|
||||
assert!(!find_command("select.same.linkBlockSeries").unwrap().info(&s).enabled);
|
||||
assert!(s.execute("select.same.linkBlockSeries", &json!({})).is_err());
|
||||
s.execute("layer.setProps", &json!({"id": l1.0, "visible": true})).unwrap();
|
||||
// In an isolated group, only the frames inside it count and are selected.
|
||||
s.execute("object.isolate", &json!({"id": g.0})).unwrap();
|
||||
sel(&mut s, &[d]);
|
||||
assert_eq!(s.execute("select.same.linkBlockSeries", &json!({})).unwrap()["count"], 1);
|
||||
assert_eq!(selected(&s), vec![d]);
|
||||
sel(&mut s, &[a]);
|
||||
assert!(!find_command("select.same.linkBlockSeries").unwrap().info(&s).enabled);
|
||||
assert!(s.execute("select.same.linkBlockSeries", &json!({})).is_err());
|
||||
}
|
||||
|
||||
/// #1737: Select > Same reads type's fill and stroke from its characters (the first run), so type
|
||||
|
||||
+3
-2
@@ -4886,8 +4886,9 @@ doesn't exist, a `port` other than `in` or `out`, and a side where no frame of a
|
||||
`select.same.linkBlockSeries` (Select › Same › Link Block Series) selects every frame of the threads that the selected
|
||||
type objects, and the type inside selected groups, are in, in thread order, and returns `{count}`. Type in no thread
|
||||
selects itself, and other selected objects are left out. With frames of several threads selected, it selects the frames
|
||||
of all of them. Hidden or locked type inside a selected group does not count as selected, and hidden or locked frames of
|
||||
a thread are left out. It is off while no type is selected, and records no undo step.
|
||||
of all of them. Type that is hidden or locked, inside a hidden or locked group or layer, on a template layer, or outside
|
||||
the isolated group does not count as selected, and such frames of a thread are left out. It is off while no other type
|
||||
is selected, and records no undo step.
|
||||
|
||||
In every thread, a paragraph that continues from one frame into the next has its first line, first-line indent and
|
||||
bullet or number in the frame where it starts. In the next frame its first line starts where its wrapped lines start,
|
||||
|
||||
Reference in New Issue
Block a user