mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-10 06:31:19 +00:00
Selection: show marquee rules and offer enclosed and directional modes
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
# VectorCraft Roadmap
|
||||
|
||||
> **QoL update (2026-11-02):** Selection-tool marquees offer Touching (default), Fully Enclosed and Drag Direction in Preferences › Selection & Anchor Display. Drag Direction uses screen left/right, including rotated views, retaining its last rule within three horizontal screen pixels of the press. E reverses the rule for the current drag. A solid enclosed outline or dashed touching outline and a translated label show the active rule. The selection region remains aligned to the document axes. This is a VectorCraft addition; scores and estimates are unchanged.
|
||||
|
||||
- Contour add-on (beyond Illustrator): drag a selected shape's edge outward or inward for a live, editable offset copy, with a floating bar for distance and Sharp, Round or Square corners; Bake turns it into paths. Scores and estimates unchanged.
|
||||
- Compound Shapes add-on (beyond Illustrator): a menu for Make, Release and Expand Compound Shape and a setting, off by default, that makes the Pathfinder panel's Shape Mode buttons create compound shapes on a click. Scores and estimates unchanged.
|
||||
|
||||
|
||||
@@ -156,6 +156,13 @@ pub const PREF_SPECS: &[PrefSpec] = &[
|
||||
p!("holdToolKeys", "General", "Options", "Hold a Tool Shortcut to Use the Tool Temporarily", bool),
|
||||
// Selection & Anchor Display
|
||||
p!("selectionTolerance", "Selection & Anchor Display", "Selection", "Tolerance", num(1.0, 8.0, "px")),
|
||||
p!(
|
||||
"marqueeSelection",
|
||||
"Selection & Anchor Display",
|
||||
"Selection",
|
||||
"Marquee Selection",
|
||||
choice(&[("touching", "Touching"), ("enclosed", "Fully Enclosed"), ("directional", "Drag Direction")])
|
||||
),
|
||||
p!("objectSelectionByPathOnly", "Selection & Anchor Display", "Selection", "Object Selection by Path Only", bool),
|
||||
p!("snapToPointTolerance", "Selection & Anchor Display", "Selection", "Snap to Point", num(1.0, 8.0, "px")),
|
||||
p!("ctrlClickSelectsBehind", "Selection & Anchor Display", "Selection", "Command Click to Select Objects Behind", bool),
|
||||
|
||||
@@ -67,6 +67,8 @@ pub struct Prefs {
|
||||
pub paste_offset: f64,
|
||||
// Selection & Anchor Display
|
||||
pub selection_tolerance: f64,
|
||||
/// Selection-tool marquee: touching (default), enclosed, or screen drag direction.
|
||||
pub marquee_selection: String,
|
||||
pub object_selection_by_path_only: bool,
|
||||
pub snap_to_point_tolerance: f64,
|
||||
pub ctrl_click_selects_behind: bool,
|
||||
@@ -378,6 +380,7 @@ impl Default for Prefs {
|
||||
hold_tool_keys: true,
|
||||
paste_offset: 10.0,
|
||||
selection_tolerance: 3.0,
|
||||
marquee_selection: "touching".into(),
|
||||
object_selection_by_path_only: false,
|
||||
snap_to_point_tolerance: 2.0,
|
||||
ctrl_click_selects_behind: true,
|
||||
|
||||
@@ -7,6 +7,32 @@ use vectorcraft_tools::{PointerEvent, PointerKind};
|
||||
use crate::cmd::prefscmds::{PREF_CATEGORIES, PREF_GROUPS, PREF_SPECS, validate};
|
||||
use crate::*;
|
||||
|
||||
/// The saved preference reaches the real tool; older preferences keep Touching and invalid
|
||||
/// values fail without changing the current setting.
|
||||
#[test]
|
||||
fn marquee_selection_preference_drives_the_selection_tool() {
|
||||
assert_eq!(serde_json::from_value::<Prefs>(json!({})).unwrap().marquee_selection, "touching");
|
||||
let mut s = Session::new();
|
||||
s.execute("file.new", &json!({"width": 400, "height": 400})).unwrap();
|
||||
s.execute("shape.rectangle", &json!({"x": 100, "y": 100, "width": 100, "height": 100})).unwrap();
|
||||
let v = ViewInfo::default();
|
||||
s.select_tool("selection", v).unwrap();
|
||||
for (mode, selected) in [("touching", true), ("enclosed", false), ("directional", false)] {
|
||||
set_pref(&mut s, "marqueeSelection", json!(mode));
|
||||
s.execute("select.none", &json!({})).unwrap();
|
||||
for (k, x, y) in [(PointerKind::Down, 50.0, 50.0), (PointerKind::Drag, 150.0, 150.0), (PointerKind::Up, 150.0, 150.0)] {
|
||||
s.pointer(&PointerEvent::new(k, x, y), v).unwrap();
|
||||
}
|
||||
assert_eq!(!s.doc().unwrap().selection.objects.is_empty(), selected, "{mode}");
|
||||
let persisted = s.prefs.to_json();
|
||||
assert_eq!(serde_json::from_value::<Prefs>(persisted).unwrap().marquee_selection, mode);
|
||||
}
|
||||
assert!(s.execute("prefs.set", &json!({"key": "marqueeSelection", "value": "unknown"})).is_err());
|
||||
assert_eq!(s.prefs.marquee_selection, "directional");
|
||||
s.execute("prefs.reset", &json!({})).unwrap();
|
||||
assert_eq!(s.prefs.marquee_selection, "touching");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn every_spec_matches_a_prefs_field_and_back() {
|
||||
let all = Prefs::default().to_json();
|
||||
|
||||
@@ -319,6 +319,11 @@ impl Session {
|
||||
slices_locked: self.menu.slices_locked,
|
||||
auto_add_delete: !self.prefs.disable_auto_add_delete,
|
||||
selection_tolerance: self.prefs.selection_tolerance,
|
||||
marquee_mode: match self.prefs.marquee_selection.as_str() {
|
||||
"enclosed" => vectorcraft_tools::MarqueeMode::Enclosed,
|
||||
"directional" => vectorcraft_tools::MarqueeMode::Directional,
|
||||
_ => vectorcraft_tools::MarqueeMode::Touching,
|
||||
},
|
||||
anchor_size: self.prefs.anchor_size,
|
||||
path_only: self.prefs.object_selection_by_path_only,
|
||||
type_path_only: self.prefs.type_selection_by_path_only,
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
use super::{Document, NodeId, Paint, Point, Selection, Unit, Vec2, distort, select, xform};
|
||||
|
||||
/// Which objects a Selection-tool marquee reaches before E reverses its rule for this drag.
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||
pub enum MarqueeMode {
|
||||
#[default]
|
||||
Touching,
|
||||
Enclosed,
|
||||
/// Screen-right takes enclosed objects; screen-left takes touched objects.
|
||||
Directional,
|
||||
}
|
||||
|
||||
/// Paint defaults for new art (the fill/stroke proxy) and the rest of the new-art template.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct PaintDefaults {
|
||||
@@ -120,6 +130,8 @@ pub struct ToolContext<'a> {
|
||||
pub selection: &'a Selection,
|
||||
/// Screen pixels per document point.
|
||||
pub zoom: f64,
|
||||
/// Selection-tool marquee rule; other selection tools keep their own rules.
|
||||
pub marquee_mode: MarqueeMode,
|
||||
pub isolation: Option<NodeId>,
|
||||
pub paint: &'a PaintDefaults,
|
||||
pub outline: bool,
|
||||
|
||||
@@ -57,7 +57,7 @@ mod context;
|
||||
mod cursor;
|
||||
mod input;
|
||||
|
||||
pub use context::{GlyphSnap, PaintDefaults, ScreenFrame, ToolContext};
|
||||
pub use context::{GlyphSnap, MarqueeMode, PaintDefaults, ScreenFrame, ToolContext};
|
||||
pub use cursor::Cursor;
|
||||
pub use input::{Action, Mods, PenPose, PointerEvent, PointerKind, ToolKey};
|
||||
|
||||
@@ -66,6 +66,8 @@ pub use input::{Action, Mods, PenPose, PointerEvent, PointerKind, ToolKey};
|
||||
pub enum Overlay {
|
||||
/// Marquee / rubber band rectangle.
|
||||
Marquee(Rect),
|
||||
/// Selection-tool marquee, with its effective rule and pointer position for feedback.
|
||||
SelectionMarquee { rect: Rect, enclosed: bool, pointer: Point },
|
||||
/// A path preview (e.g. pen rubber band) in a colour (RGB).
|
||||
Path { path: BezPath, color: [u8; 3], width: f32, dashed: bool },
|
||||
/// A line segment (smart guide, handle line).
|
||||
@@ -482,6 +484,7 @@ pub(crate) mod testutil {
|
||||
revision: (0, 0),
|
||||
selection: s,
|
||||
zoom: 1.0,
|
||||
marquee_mode: MarqueeMode::Touching,
|
||||
isolation: None,
|
||||
paint: p,
|
||||
outline: false,
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
//! The bounding box stands at the selection's own angle after a rotation, so its handles scale
|
||||
//! along the objects' axes. A handle drag resizes area type's frame (the type area) instead of
|
||||
//! scaling its type: the text reflows at its size.
|
||||
//! E pressed during a marquee drag makes the marquee take only the objects entirely inside it, and
|
||||
//! E pressed again makes it take the objects it touches.
|
||||
//! Marquee Selection defaults to touched objects, with fully enclosed and screen drag-direction
|
||||
//! alternatives. E reverses the rule for the current drag. The overlay reports the effective rule.
|
||||
//! Escape during a drag (a move or an Alt-drag copy, a scale, a rotation, a marquee, a widget or
|
||||
//! bracket drag) cancels it: the art returns to its state at the press.
|
||||
//! Selected area type and type on a path show an in port and an out port. A click on the out port
|
||||
@@ -55,6 +55,29 @@ use crate::shaperwidget;
|
||||
use crate::typewidget;
|
||||
use crate::{Action, Cursor, Mods, Overlay, PointerEvent, PointerKind, Tool, ToolContext, ToolKey, json_ids};
|
||||
|
||||
/// Direction is read on screen, even in a turned view. Within three pixels of the press's
|
||||
/// horizontal position, retain the previous rule so hand jitter does not flip it.
|
||||
fn marquee_direction(cx: &ToolContext, start: Point, cur: Point, previous: bool) -> bool {
|
||||
let dx = cx.screen.and_then(|f| Some(f.to_screen(cur)?.0 - f.to_screen(start)?.0)).unwrap_or((cur.x - start.x) * cx.zoom);
|
||||
if dx > 3.0 {
|
||||
true
|
||||
} else if dx < -3.0 {
|
||||
false
|
||||
} else {
|
||||
previous
|
||||
}
|
||||
}
|
||||
|
||||
/// The same effective rule drives artwork, ruler guides and the visible feedback.
|
||||
fn marquee_enclosed(cx: &ToolContext, inverted: bool, directional_inside: bool) -> bool {
|
||||
let inside = match cx.marquee_mode {
|
||||
crate::MarqueeMode::Touching => false,
|
||||
crate::MarqueeMode::Enclosed => true,
|
||||
crate::MarqueeMode::Directional => directional_inside,
|
||||
};
|
||||
inside != inverted
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
enum State {
|
||||
#[default]
|
||||
@@ -97,9 +120,10 @@ enum State {
|
||||
cur: Point,
|
||||
/// Shift: the objects inside leave the selection if selected, else join it.
|
||||
toggle: bool,
|
||||
/// E pressed an odd number of times during the drag: the marquee takes only the objects
|
||||
/// that lie entirely inside it.
|
||||
/// E pressed an odd number of times during the drag: reverse the chosen rule.
|
||||
enclosed: bool,
|
||||
/// Last horizontal direction outside the 3 screen-pixel dead zone at the press.
|
||||
directional_inside: bool,
|
||||
},
|
||||
/// Dragging a Live Corners widget.
|
||||
Corner(CornerDrag),
|
||||
@@ -952,8 +976,9 @@ impl Tool for SelectionTool {
|
||||
}
|
||||
vec![Action::Preview("object.transform".into(), params)]
|
||||
}
|
||||
(PointerKind::Drag, State::Marquee { start, toggle, enclosed, .. }) => {
|
||||
self.state = State::Marquee { start, cur: p, toggle, enclosed };
|
||||
(PointerKind::Drag, State::Marquee { start, toggle, enclosed, directional_inside, .. }) => {
|
||||
self.state =
|
||||
State::Marquee { start, cur: p, toggle, enclosed, directional_inside: marquee_direction(cx, start, p, directional_inside) };
|
||||
vec![]
|
||||
}
|
||||
(PointerKind::Drag, State::Corner(mut c)) => {
|
||||
@@ -1019,13 +1044,14 @@ impl Tool for SelectionTool {
|
||||
out.push(Action::Commit);
|
||||
out
|
||||
}
|
||||
(PointerKind::Up, State::Marquee { start, toggle, enclosed, .. }) => {
|
||||
(PointerKind::Up, State::Marquee { start, toggle, enclosed, directional_inside, .. }) => {
|
||||
self.state = State::Idle;
|
||||
let r = Rect::from_points(start, p);
|
||||
if r.width() < Self::drag_threshold(cx) && r.height() < Self::drag_threshold(cx) {
|
||||
return if toggle { vec![] } else { vec![Action::Exec("select.none".into(), json!({}))] };
|
||||
}
|
||||
let touched = marquee(cx.doc, r, cx.isolation, false);
|
||||
let enclosed = marquee_enclosed(cx, enclosed, marquee_direction(cx, start, p, directional_inside));
|
||||
let ids = if enclosed { inside(cx, r, touched) } else { touched };
|
||||
let art = Action::Exec(if toggle { "select.toggle" } else { "select.set" }.into(), json!({ "ids": json_ids(&ids) }));
|
||||
std::iter::once(art).chain(crate::rulerguide::marquee_select(cx, r, enclosed)).collect()
|
||||
@@ -1046,7 +1072,11 @@ impl Tool for SelectionTool {
|
||||
}
|
||||
}
|
||||
match &self.state {
|
||||
State::Marquee { start, cur, .. } => o.push(Overlay::Marquee(Rect::from_points(*start, *cur))),
|
||||
State::Marquee { start, cur, enclosed, directional_inside, .. } => o.push(Overlay::SelectionMarquee {
|
||||
rect: Rect::from_points(*start, *cur),
|
||||
enclosed: marquee_enclosed(cx, *enclosed, *directional_inside),
|
||||
pointer: *cur,
|
||||
}),
|
||||
State::Corner(c) => o.extend(c.overlays(cx)),
|
||||
State::Pie(d) => o.extend(d.overlays(cx)),
|
||||
State::RotateAll(d) => o.extend(d.overlays(cx)),
|
||||
@@ -1544,7 +1574,7 @@ impl SelectionTool {
|
||||
self.state = if !m.shift && selected_hidden_at(cx.doc, p, cx.selection_hit_options(), &cx.selection.objects).is_some() {
|
||||
State::Moving { start: grab, press: p, hold: grab, began: false, deselect: None, key: None, exclude: None }
|
||||
} else {
|
||||
State::Marquee { start: p, cur: p, toggle: m.shift, enclosed: false }
|
||||
State::Marquee { start: p, cur: p, toggle: m.shift, enclosed: false, directional_inside: false }
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1307,7 +1307,7 @@ fn escape_cancels_a_marquee() {
|
||||
t.pointer(&cx, &ev(PointerKind::Drag, 120.0, 120.0));
|
||||
assert_eq!(t.key(&cx, ToolKey::Escape, Mods::default()), vec![Action::Cancel], "Escape cancels the marquee");
|
||||
assert!(!t.busy());
|
||||
assert!(t.overlays(&cx).iter().all(|o| !matches!(o, Overlay::Marquee(_))), "the marquee is removed");
|
||||
assert!(t.overlays(&cx).iter().all(|o| !matches!(o, Overlay::Marquee(_) | Overlay::SelectionMarquee { .. })), "the marquee is removed");
|
||||
assert!(t.pointer(&cx, &ev(PointerKind::Up, 120.0, 120.0)).is_empty(), "the release selects nothing");
|
||||
}
|
||||
|
||||
@@ -1380,6 +1380,58 @@ fn e_during_a_marquee_takes_only_the_objects_inside() {
|
||||
assert!(t.busy() && !t.claims_key(&cx, e));
|
||||
}
|
||||
|
||||
/// Each rule selects what its feedback promises, with E reversing it for just this gesture.
|
||||
#[test]
|
||||
fn marquee_rules_and_feedback_agree() {
|
||||
use crate::MarqueeMode::{Directional, Enclosed, Touching};
|
||||
let (d, id) = doc_with_rect();
|
||||
let (s, paint) = (Selection::default(), paint());
|
||||
for (mode, right, expected) in
|
||||
[(Touching, true, true), (Touching, false, true), (Enclosed, true, false), (Directional, true, false), (Directional, false, true)]
|
||||
{
|
||||
let c = ToolContext { marquee_mode: mode, ..cx(&d, &s, &paint) };
|
||||
for inverted in [false, true] {
|
||||
let mut tool = SelectionTool::default();
|
||||
let (a, b) = if right { ((50.0, 50.0), (150.0, 150.0)) } else { ((150.0, 50.0), (50.0, 150.0)) };
|
||||
tool.pointer(&c, &ev(PointerKind::Down, a.0, a.1));
|
||||
tool.pointer(&c, &ev(PointerKind::Drag, b.0, b.1));
|
||||
if inverted {
|
||||
tool.key(&c, ToolKey::Letter('e'), Mods::default());
|
||||
}
|
||||
let selected = expected != inverted;
|
||||
assert!(
|
||||
tool.overlays(&c).iter().any(|o| matches!(o, Overlay::SelectionMarquee { enclosed, .. } if *enclosed != selected)),
|
||||
"{mode:?} right={right} E={inverted}"
|
||||
);
|
||||
let out = tool.pointer(&c, &ev(PointerKind::Up, b.0, b.1));
|
||||
assert_eq!(out, vec![Action::Exec("select.set".into(), json!({"ids": if selected { vec![id.0] } else { vec![] }}))]);
|
||||
assert!(!tool.overlays(&c).iter().any(|o| matches!(o, Overlay::SelectionMarquee { .. })));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Screen direction survives view rotation; near the press's x position, the last mode is kept.
|
||||
#[test]
|
||||
fn directional_marquee_uses_screen_direction_and_a_dead_zone() {
|
||||
let (d, _) = doc_with_rect();
|
||||
let (s, paint) = (Selection::default(), paint());
|
||||
for angle in [0.0, 45.0, 90.0, 180.0, 270.0] {
|
||||
for zoom in [0.25, 1.0, 4.0] {
|
||||
let mut screen = turned_view(angle).unwrap();
|
||||
screen.right /= zoom;
|
||||
screen.down /= zoom;
|
||||
let c = ToolContext { marquee_mode: crate::MarqueeMode::Directional, screen: Some(screen), zoom, ..cx(&d, &s, &paint) };
|
||||
let start = screen.at(20.0, 20.0);
|
||||
assert!(marquee_direction(&c, start, screen.at(24.0, 60.0), false));
|
||||
assert!(!marquee_direction(&c, start, screen.at(16.0, 60.0), true));
|
||||
for x in [18.0, 20.0, 22.0] {
|
||||
assert!(marquee_direction(&c, start, screen.at(x, 60.0), true));
|
||||
assert!(!marquee_direction(&c, start, screen.at(x, 60.0), false));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// With E pressed during the drag, a marquee takes only the artboard guides that lie entirely
|
||||
/// inside it, and no canvas guide, which runs across the whole canvas.
|
||||
#[test]
|
||||
|
||||
@@ -275,6 +275,29 @@ pub(super) fn draw_overlays(p: &egui::Painter, xf: &Xf, overlays: &[Overlay], t:
|
||||
match o {
|
||||
// Where the tools select: the document rectangle, turned with the view.
|
||||
Overlay::Marquee(r) => marquee_through(p, &xf.quad(*r)),
|
||||
Overlay::SelectionMarquee { rect, enclosed, pointer } => {
|
||||
let quad = xf.quad(*rect);
|
||||
if *enclosed {
|
||||
let points: Vec<Pos2> = quad.iter().chain(quad.first()).copied().collect();
|
||||
p.add(Shape::line(points.clone(), Stroke::new(3.0, Color32::from_white_alpha(230))));
|
||||
p.add(Shape::line(points, Stroke::new(1.0, Color32::from_gray(40))));
|
||||
} else {
|
||||
marquee_through(p, &quad);
|
||||
}
|
||||
let (current, other) = if *enclosed { (tl!("Fully Enclosed"), tl!("Touching")) } else { (tl!("Touching"), tl!("Fully Enclosed")) };
|
||||
let label = format!("{current} · E: {other}");
|
||||
let galley = p.layout(label, egui::FontId::proportional(11.0), t.text, (p.clip_rect().width() - 12.0).max(1.0));
|
||||
let size = galley.size() + vec2(12.0, 8.0);
|
||||
let wanted = xf.to_screen(*pointer) + vec2(14.0, 14.0);
|
||||
let clip = p.clip_rect();
|
||||
let min = egui::pos2(
|
||||
wanted.x.clamp(clip.min.x, (clip.max.x - size.x).max(clip.min.x)),
|
||||
wanted.y.clamp(clip.min.y, (clip.max.y - size.y).max(clip.min.y)),
|
||||
);
|
||||
let badge = egui::Rect::from_min_size(min, size);
|
||||
p.rect_filled(badge, CornerRadius::same(3), t.panel);
|
||||
p.galley(badge.min + vec2(6.0, 4.0), galley, t.text);
|
||||
}
|
||||
Overlay::Path { path, color, width, dashed } => {
|
||||
let s = Stroke::new(*width, c32(*color));
|
||||
if *dashed {
|
||||
|
||||
@@ -2,6 +2,65 @@ use super::*;
|
||||
use vectorcraft_engine::Session;
|
||||
use vectorcraft_geom::Shape as _;
|
||||
|
||||
/// The marquee explains E without depending on color. At the canvas edge the hint stays in
|
||||
/// view, in every brightness, and the two rules have visibly different outlines.
|
||||
#[test]
|
||||
fn marquee_feedback_explains_the_rule_and_stays_on_canvas() {
|
||||
let draw = |ui: &mut egui::Ui| {
|
||||
let t = Tokens::get(ui.ctx());
|
||||
let xf = Xf { rect: ui.max_rect(), zoom: 1.0, center: Point::new(160.0, 100.0), rot: 0.0 };
|
||||
let prefs = vectorcraft_engine::Prefs::default();
|
||||
for (enclosed, y) in [(false, 80.0), (true, 180.0)] {
|
||||
overlays::draw_overlays(
|
||||
ui.painter(),
|
||||
&xf,
|
||||
&[Overlay::SelectionMarquee { rect: Rect::new(20.0, y - 60.0, 300.0, y), enclosed, pointer: Point::new(315.0, y) }],
|
||||
&t,
|
||||
overlays::HandleLook::of(&prefs),
|
||||
);
|
||||
}
|
||||
};
|
||||
for brightness in crate::theme::Brightness::ALL {
|
||||
for width in [160.0, 320.0] {
|
||||
let ctx = egui::Context::default();
|
||||
crate::theme::install_fonts(&ctx);
|
||||
crate::theme::apply(&ctx, brightness);
|
||||
let raw = egui::RawInput { screen_rect: Some(egui::Rect::from_min_size(Pos2::ZERO, vec2(width, 200.0))), ..Default::default() };
|
||||
let mut out = ctx.run_ui(raw, |ui| draw(ui));
|
||||
out.textures_delta.clear();
|
||||
let text = crate::tests_labels::shapes_text(&out);
|
||||
assert!(text.contains("Fully Enclosed · E: Touching") && text.contains("Touching · E: Fully Enclosed"), "{text}");
|
||||
for shape in &out.shapes {
|
||||
if let egui::Shape::Text(label) = &shape.shape {
|
||||
assert!(
|
||||
label.pos.x >= shape.clip_rect.min.x && label.pos.x + label.galley.size().x <= shape.clip_rect.max.x,
|
||||
"{brightness:?}: hint clipped"
|
||||
);
|
||||
assert!(
|
||||
label.pos.y >= shape.clip_rect.min.y && label.pos.y + label.galley.size().y <= shape.clip_rect.max.y,
|
||||
"{brightness:?}: hint clipped vertically"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Optional visual QA of our own UI. Captures never enter the repository.
|
||||
if let Some(path) = std::env::var_os("VECTORCRAFT_MARQUEE_CAPTURE") {
|
||||
let mut ready = false;
|
||||
let mut harness = egui_kittest::Harness::builder().with_size(vec2(320.0, 200.0)).wgpu().build_ui(move |ui| {
|
||||
if !ready {
|
||||
crate::theme::install_fonts(ui.ctx());
|
||||
crate::theme::apply(ui.ctx(), crate::theme::Brightness::MediumDark);
|
||||
ready = true;
|
||||
return;
|
||||
}
|
||||
draw(ui);
|
||||
});
|
||||
harness.run_steps(4);
|
||||
harness.render().unwrap().save(path).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn subpixel_dock_layout_does_not_change_pointer_document_coordinates() {
|
||||
let mut observed = Vec::new();
|
||||
|
||||
@@ -2980,6 +2980,11 @@ digits Malayalam Малаялам
|
||||
Selected Swatches Избрани образци
|
||||
Selected Swatches Only Само избрани образци
|
||||
Selection Селекция
|
||||
# Marquee selection modes (draft translations; native review welcome).
|
||||
Marquee Selection Избор с рамка
|
||||
Touching Докоснати обекти
|
||||
Fully Enclosed Изцяло вътре
|
||||
Drag Direction Посока на плъзгане
|
||||
Selection & Anchor Display Показване на селекция и опорни точки
|
||||
Selection Only Само селекция
|
||||
Selection Tool Инструмент „Селекция“
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -179,6 +179,11 @@
|
||||
Ungroup Razgrupiši
|
||||
Lock Zaključaj
|
||||
Selection Odabir
|
||||
# Marquee selection modes (draft translations; native review welcome).
|
||||
Marquee Selection Pravougaoni odabir
|
||||
Touching Dodirnuti objekti
|
||||
Fully Enclosed Potpuno obuhvaćeni
|
||||
Drag Direction Smjer povlačenja
|
||||
All Artwork Above Sav crtež iznad
|
||||
Other Layers Ostali slojevi
|
||||
Unlock All Otključaj sve
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -3798,6 +3798,11 @@ digits Malayalam Malaiàlam
|
||||
Selected Swatches Only (0) Només les mostres seleccionades (0)
|
||||
Selected Symbols Only Només els símbols seleccionats
|
||||
Selection Selecció
|
||||
# Marquee selection modes (draft translations; native review welcome).
|
||||
Marquee Selection Selecció rectangular
|
||||
Touching Objectes tocats
|
||||
Fully Enclosed Totalment inclosos
|
||||
Drag Direction Direcció de l’arrossegament
|
||||
Selection & Anchor Display Selecció i visualització d’ancoratges
|
||||
Selection Only Només la selecció
|
||||
Selection Tool Eina de selecció
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -187,6 +187,11 @@
|
||||
Ungroup Oddělit
|
||||
Lock Zamknout
|
||||
Selection Výběr
|
||||
# Marquee selection modes (draft translations; native review welcome).
|
||||
Marquee Selection Obdélníkový výběr
|
||||
Touching Dotčené objekty
|
||||
Fully Enclosed Zcela uvnitř
|
||||
Drag Direction Směr tažení
|
||||
All Artwork Above Veškerou kresbu výše
|
||||
Other Layers Ostatní vrstvy
|
||||
Unlock All Odemknout vše
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -172,6 +172,11 @@
|
||||
Ungroup Afgruppér
|
||||
Lock Lås
|
||||
Selection Markering
|
||||
# Marquee selection modes (draft translations; native review welcome).
|
||||
Marquee Selection Rektangelmarkering
|
||||
Touching Berørte objekter
|
||||
Fully Enclosed Helt omsluttet
|
||||
Drag Direction Trækretning
|
||||
All Artwork Above Al grafik ovenfor
|
||||
Other Layers Andre lag
|
||||
Unlock All Lås alle op
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -3022,6 +3022,11 @@ digits Malayalam Malayalam
|
||||
Selected Swatches Ausgewählte Farbfelder
|
||||
Selected Swatches Only Nur ausgewählte Farbfelder
|
||||
Selection Auswahl
|
||||
# Marquee selection modes (draft translations; native review welcome).
|
||||
Marquee Selection Rechteckauswahl
|
||||
Touching Berührt
|
||||
Fully Enclosed Vollständig eingeschlossen
|
||||
Drag Direction Ziehrichtung
|
||||
Selection & Anchor Display Auswahl und Ankerpunktanzeige
|
||||
Selection Only Nur Auswahl
|
||||
Selection Tool Auswahl-Werkzeug
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -1935,6 +1935,11 @@ font-technology Variable Μεταβλητές
|
||||
Select All Επιλογή όλων
|
||||
Select: Επιλογή:
|
||||
Selection Επιλογή
|
||||
# Marquee selection modes (draft translations; native review welcome).
|
||||
Marquee Selection Ορθογώνια επιλογή
|
||||
Touching Αντικείμενα που τέμνονται
|
||||
Fully Enclosed Πλήρως εντός
|
||||
Drag Direction Κατεύθυνση σύρσης
|
||||
Selection Tool Επιλογή (Selection Tool)
|
||||
Send Backward Μεταφορά ένα βήμα πίσω
|
||||
Send to Back Μεταφορά στο παρασκήνιο
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 168 and column 5.
|
@@ -3062,6 +3062,11 @@ digits Malayalam Malayálam
|
||||
Selected Swatches Muestras seleccionadas
|
||||
Selected Swatches Only Solo muestras seleccionadas
|
||||
Selection Selección
|
||||
# Marquee selection modes (draft translations; native review welcome).
|
||||
Marquee Selection Selección rectangular
|
||||
Touching Objetos tocados
|
||||
Fully Enclosed Totalmente incluidos
|
||||
Drag Direction Dirección de arrastre
|
||||
Selection & Anchor Display Visualización de selección y anclas
|
||||
Selection Only Solo la selección
|
||||
Selection Tool Herramienta Selección
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -194,6 +194,11 @@
|
||||
Ungroup Pura ryhmitys
|
||||
Lock Lukitse
|
||||
Selection Valinta
|
||||
# Marquee selection modes (draft translations; native review welcome).
|
||||
Marquee Selection Suorakulmiovalinta
|
||||
Touching Kosketetut
|
||||
Fully Enclosed Kokonaan sisällä
|
||||
Drag Direction Vetosuunta
|
||||
All Artwork Above Kaikki yllä oleva kuvitus
|
||||
Other Layers Muut tasot
|
||||
Unlock All Avaa kaikki lukitukset
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -3054,6 +3054,11 @@ digits Malayalam Malayalam
|
||||
Selected Swatches Nuances sélectionnées
|
||||
Selected Swatches Only Nuances sélectionnées uniquement
|
||||
Selection Sélection
|
||||
# Marquee selection modes (draft translations; native review welcome).
|
||||
Marquee Selection Sélection rectangulaire
|
||||
Touching Objets touchés
|
||||
Fully Enclosed Entièrement inclus
|
||||
Drag Direction Sens du glissement
|
||||
Selection & Anchor Display Affichage de la sélection et des points d’ancrage
|
||||
Selection Only Sélection uniquement
|
||||
Selection Tool Outil Sélection
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -3667,6 +3667,11 @@ mark-style Japanese Japán
|
||||
Selected Swatches Only Csak a kijelölt színminták
|
||||
Selected Swatches Only (0) Csak a kijelölt színminták (0)
|
||||
Selection Kijelölés
|
||||
# Marquee selection modes (draft translations; native review welcome).
|
||||
Marquee Selection Téglalapos kijelölés
|
||||
Touching Érintett objektumok
|
||||
Fully Enclosed Teljesen befoglalt
|
||||
Drag Direction Húzás iránya
|
||||
Selection & Anchor Display Kijelölés és horgonypontok megjelenítése
|
||||
Selection Only Csak a kijelölés
|
||||
Selection Tool Kijelölés
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -172,6 +172,11 @@
|
||||
Ungroup Buka grup
|
||||
Lock Kunci
|
||||
Selection Seleksi
|
||||
# Marquee selection modes (draft translations; native review welcome).
|
||||
Marquee Selection Seleksi persegi panjang
|
||||
Touching Objek tersentuh
|
||||
Fully Enclosed Sepenuhnya di dalam
|
||||
Drag Direction Arah seret
|
||||
All Artwork Above Semua karya seni di atas
|
||||
Other Layers Lapisan lain
|
||||
Unlock All Buka kunci semua
|
||||
|
||||
|
@@ -3098,6 +3098,11 @@ digits Malayalam Malayalam
|
||||
Selected Swatches Campioni selezionati
|
||||
Selected Swatches Only Solo campioni selezionati
|
||||
Selection Selezione
|
||||
# Marquee selection modes (draft translations; native review welcome).
|
||||
Marquee Selection Selezione rettangolare
|
||||
Touching Oggetti toccati
|
||||
Fully Enclosed Interamente inclusi
|
||||
Drag Direction Direzione di trascinamento
|
||||
Selection & Anchor Display Visualizzazione selezione e punti di ancoraggio
|
||||
Selection Only Solo selezione
|
||||
Selection Tool Strumento Selezione
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -213,6 +213,11 @@ digits Malayalam マラヤーラム数字
|
||||
Ungroup グループ解除
|
||||
Lock ロック
|
||||
Selection 選択
|
||||
# Marquee selection modes (draft translations; native review welcome).
|
||||
Marquee Selection 矩形選択
|
||||
Touching 接触するオブジェクト
|
||||
Fully Enclosed 完全に囲まれたオブジェクト
|
||||
Drag Direction ドラッグ方向
|
||||
All Artwork Above 前面のすべてのアートワーク
|
||||
Other Layers その他のレイヤー
|
||||
Unlock All すべてをロック解除
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -146,6 +146,11 @@
|
||||
Ungroup დაჯგუფების მოხსნა
|
||||
Lock ჩაკეტვა
|
||||
Selection მონიშნული
|
||||
# Marquee selection modes (draft translations; native review welcome).
|
||||
Marquee Selection მართკუთხა მონიშვნა
|
||||
Touching შეხებული ობიექტები
|
||||
Fully Enclosed სრულად მოქცეული
|
||||
Drag Direction გადათრევის მიმართულება
|
||||
All Artwork Above ზემოთ მდებარე მთელი ნახატი
|
||||
Other Layers სხვა შრეები
|
||||
Unlock All ყველაფრის განბლოკვა
|
||||
|
||||
|
Can't render this file because it is too large.
|
@@ -206,6 +206,11 @@ digits Malayalam 말라얄람어
|
||||
Ungroup 그룹 해제
|
||||
Lock 잠그기
|
||||
Selection 선택 영역
|
||||
# Marquee selection modes (draft translations; native review welcome).
|
||||
Marquee Selection 사각형 선택
|
||||
Touching 닿는 개체
|
||||
Fully Enclosed 완전히 포함된 개체
|
||||
Drag Direction 드래그 방향
|
||||
All Artwork Above 위의 모든 아트워크
|
||||
Other Layers 다른 레이어
|
||||
Unlock All 모두 잠금 해제
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -186,6 +186,11 @@
|
||||
Ungroup Groepering opheffen
|
||||
Lock Vergrendelen
|
||||
Selection Selectie
|
||||
# Marquee selection modes (draft translations; native review welcome).
|
||||
Marquee Selection Rechthoekselectie
|
||||
Touching Geraakt
|
||||
Fully Enclosed Volledig omsloten
|
||||
Drag Direction Sleeprichting
|
||||
All Artwork Above Alle artwork erboven
|
||||
Other Layers Andere lagen
|
||||
Unlock All Alles ontgrendelen
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -152,6 +152,11 @@
|
||||
Ungroup Rozgrupuj
|
||||
Lock Zablokuj
|
||||
Selection Zaznaczenie
|
||||
# Marquee selection modes (draft translations; native review welcome).
|
||||
Marquee Selection Zaznaczanie prostokątne
|
||||
Touching Dotknięte
|
||||
Fully Enclosed Całkowicie wewnątrz
|
||||
Drag Direction Kierunek przeciągania
|
||||
All Artwork Above Całą kompozycję powyżej
|
||||
Other Layers Pozostałe warstwy
|
||||
Unlock All Odblokuj wszystko
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -2564,6 +2564,11 @@ digits Malayalam Malaiala
|
||||
Selected Swatches Amostras Selecionadas
|
||||
Selected Swatches Only Somente Amostras Selecionadas
|
||||
Selection Seleção
|
||||
# Marquee selection modes (draft translations; native review welcome).
|
||||
Marquee Selection Seleção retangular
|
||||
Touching Objetos tocados
|
||||
Fully Enclosed Totalmente contidos
|
||||
Drag Direction Direção do arrasto
|
||||
Selection & Anchor Display Exibição de Seleção e Âncora
|
||||
Selection Only Somente Seleção
|
||||
Selection Tool Ferramenta Seleção
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -3093,6 +3093,11 @@ digits Malayalam Малаялам
|
||||
Selected Swatches Выбранные образцы
|
||||
Selected Swatches Only Только выбранные образцы
|
||||
Selection Выделение
|
||||
# Marquee selection modes (draft translations; native review welcome).
|
||||
Marquee Selection Выделение рамкой
|
||||
Touching Пересекаемые
|
||||
Fully Enclosed Полностью внутри
|
||||
Drag Direction Направление перетаскивания
|
||||
Selection & Anchor Display Отображение выделения и опорных точек
|
||||
Selection Only Только выделение
|
||||
Selection Tool Инструмент «Выделение»
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -2986,6 +2986,11 @@ digits Malayalam Malayalam
|
||||
Selected Swatches Markerade färgrutor
|
||||
Selected Swatches Only Endast markerade färgrutor
|
||||
Selection Markering
|
||||
# Marquee selection modes (draft translations; native review welcome).
|
||||
Marquee Selection Rektangelmarkering
|
||||
Touching Berörda objekt
|
||||
Fully Enclosed Helt omslutna
|
||||
Drag Direction Dragriktning
|
||||
Selection & Anchor Display Visning av markering och ankarpunkter
|
||||
Selection Only Endast markering
|
||||
Selection Tool Markeringsverktyg
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 5 and column 25.
|
@@ -184,6 +184,16 @@ fn font_height_reference_labels_are_in_complete_catalogs() {
|
||||
}
|
||||
}
|
||||
|
||||
/// Preference choices and the marquee's E hint share the same translated labels.
|
||||
#[test]
|
||||
fn marquee_selection_labels_are_in_complete_catalogs() {
|
||||
for lang in LANGUAGES.iter().filter(|lang| lang.complete_menus) {
|
||||
for label in ["Marquee Selection", "Touching", "Fully Enclosed", "Drag Direction"] {
|
||||
assert!(lang.catalog().plain(label).is_some(), "{} is missing {label}", lang.code);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Each language counts with its own plural rule: the form a count picks (`one`, then `few`, then
|
||||
/// `other`/`many`).
|
||||
#[test]
|
||||
|
||||
@@ -2890,6 +2890,11 @@ digits Malayalam Malayalam
|
||||
Selected Swatches Seçilen Renk Örnekleri
|
||||
Selected Swatches Only Yalnızca Seçilen Renk Örnekleri
|
||||
Selection Seçim
|
||||
# Marquee selection modes (draft translations; native review welcome).
|
||||
Marquee Selection Dikdörtgen seçim
|
||||
Touching Dokunulanlar
|
||||
Fully Enclosed Tamamen içeride
|
||||
Drag Direction Sürükleme yönü
|
||||
Selection & Anchor Display Seçim ve Bağlantı Ekranı
|
||||
Selection Only Yalnızca Seçim
|
||||
Selection Tool Seçim Aracı
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 5 and column 25.
|
@@ -3090,6 +3090,11 @@ digits Malayalam Малаялам
|
||||
Selected Swatches Вибрані зразки
|
||||
Selected Swatches Only Тільки вибрані зразки
|
||||
Selection Виділення
|
||||
# Marquee selection modes (draft translations; native review welcome).
|
||||
Marquee Selection Виділення рамкою
|
||||
Touching Перетнуті
|
||||
Fully Enclosed Повністю всередині
|
||||
Drag Direction Напрямок перетягування
|
||||
Selection & Anchor Display Відображення виділення та опорних точок
|
||||
Selection Only Тільки виділення
|
||||
Selection Tool Інструмент «Виділення»
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 182 and column 5.
|
@@ -3418,6 +3418,11 @@ mark-style Japanese Kiểu Nhật
|
||||
Selected Swatches Mẫu màu đã chọn
|
||||
Selected Swatches Only Chỉ mẫu màu đã chọn
|
||||
Selection Vùng chọn
|
||||
# Marquee selection modes (draft translations; native review welcome).
|
||||
Marquee Selection Chọn bằng khung
|
||||
Touching Đối tượng chạm khung
|
||||
Fully Enclosed Nằm hoàn toàn trong khung
|
||||
Drag Direction Hướng kéo
|
||||
Selection & Anchor Display Hiển thị vùng chọn & điểm neo
|
||||
Selection Only Chỉ vùng chọn
|
||||
Selection Tool Công cụ Chọn
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 204 and column 5.
|
@@ -2934,6 +2934,11 @@ digits Malayalam 马拉雅拉姆数字
|
||||
Selected Swatches 所选色板
|
||||
Selected Swatches Only 仅限选择的色板
|
||||
Selection 所选对象
|
||||
# Marquee selection modes (draft translations; native review welcome).
|
||||
Marquee Selection 框选
|
||||
Touching 接触对象
|
||||
Fully Enclosed 完全包含
|
||||
Drag Direction 拖动方向
|
||||
Selection & Anchor Display 选择和锚点显示
|
||||
Selection Only 仅所选内容
|
||||
Selection Tool 选择工具
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -3964,6 +3964,11 @@ digits Malayalam 馬來亞拉姆數字
|
||||
Selected Swatches 選取的色票
|
||||
Selected Swatches Only 僅限選取的色票
|
||||
Selection 選取範圍
|
||||
# Marquee selection modes (draft translations; native review welcome).
|
||||
Marquee Selection 框選
|
||||
Touching 接觸物件
|
||||
Fully Enclosed 完全包含
|
||||
Drag Direction 拖曳方向
|
||||
Selection & Anchor Display 選取和錨點顯示
|
||||
Selection Only 僅限選取範圍
|
||||
Selection Tool 選取工具
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -2,6 +2,24 @@ use super::*;
|
||||
use crate::Services;
|
||||
use vectorcraft_engine::Session;
|
||||
|
||||
/// The three marquee choices are available in Preferences and saved through the same path as
|
||||
/// other selection preferences.
|
||||
#[test]
|
||||
fn marquee_selection_choices_are_saved_from_preferences() {
|
||||
let mut app = app();
|
||||
open(&mut app, Some("Selection & Anchor Display"));
|
||||
let text = crate::tests_labels::painted_text(&mut app, |a, ui| show(a, ui.ctx()));
|
||||
assert!(text.contains("Marquee Selection") && text.contains("Touching"), "{text}");
|
||||
app.ui.dialog.as_mut().unwrap().fields.insert("marqueeSelection".into(), json!("directional"));
|
||||
confirm(&mut app).unwrap();
|
||||
assert_eq!(app.session.prefs.marquee_selection, "directional");
|
||||
let saved: Prefs = serde_json::from_value(app.ui.engine_prefs.clone()).unwrap();
|
||||
assert_eq!(saved.marquee_selection, "directional");
|
||||
open(&mut app, Some("Selection & Anchor Display"));
|
||||
let text = crate::tests_labels::painted_text(&mut app, |a, ui| show(a, ui.ctx()));
|
||||
assert!(text.contains("Drag Direction"), "{text}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn geometric_snapping_is_a_saved_smart_guides_preference() {
|
||||
let mut a = VectorcraftApp::new(vectorcraft_engine::Session::new(), Default::default());
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
# Gaps
|
||||
|
||||
> **QoL update (2026-11-02):** Selection-tool marquees offer Touching (default), Fully Enclosed and Drag Direction in Preferences › Selection & Anchor Display. Drag Direction uses screen left/right, including rotated views, retaining its last rule within three horizontal screen pixels of the press. E reverses the rule for the current drag. A solid enclosed outline or dashed touching outline and a translated label show the active rule. The selection region remains aligned to the document axes. This is a VectorCraft addition; scores and estimates are unchanged.
|
||||
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-31 · **Change:** minor (G5: dynamic symbols, #4938; estimate 15–26 → 14–24 h) · **Target:** Adobe Illustrator 2026 (30.x; G12: Units › East Asian Type acts, with Q and Ha units; estimate unchanged) · **Target:** Adobe Illustrator 2026 (30.x)
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-07 · **Change:** minor (Indian-language hyphenation review and license allowlist, #4947; opening a slow file shows a progress window, part of #5930; Hindi catalog and contributor loanword preferences; agent wording pass complete; wider native proofreading welcome; an image zoomed in on is separated, colorized or proofed only where it shows (#4155); scores and estimates unchanged; a document's first frame renders on the render worker, so opening a heavy file no longer freezes the window; scores and estimates unchanged; large images in CMYK documents are shrunk before they are separated into ink planes (#4155); scores and estimates unchanged; `.ai` paths with a Calligraphic brush open brushed, with the 6D Art Pen's channels, and Windows reads the pen's tilt and barrel rotation, #372; scores and estimates unchanged; Compound Shapes add-on, beyond Illustrator; scores and estimates unchanged; Contour add-on, beyond Illustrator; scores and estimates unchanged; a slow first frame is waited for instead of restarting without the graphics adapter, and the menu bar no longer waits for the installed fonts' scan; scores and estimates unchanged) · **Target:** Adobe Illustrator 2026 (30.x)
|
||||
|
||||
## Feature submission evidence
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# VectorCraft MCP server
|
||||
|
||||
Selection-tool marquees: `prefs.set {key: "marqueeSelection", value: "touching" | "enclosed" | "directional"}` chooses touched objects (default), fully enclosed objects, or screen-right enclosed / screen-left touched objects. Direction follows the screen when the view is rotated; within three horizontal screen pixels of the press, the last rule remains (initially touching). Without a screen frame, direction uses document x at the current zoom. E reverses the rule for the current drag; Shift toggles the reached objects and Escape cancels. The outline and label show the effective rule. Ruler guides use that rule too; Direct Selection and the document-aligned selection region are unchanged.
|
||||
|
||||
`vectorcraft-cli mcp` runs a [Model Context Protocol](https://modelcontextprotocol.io) server on stdio
|
||||
(newline-delimited JSON-RPC 2.0; see [Protocol](#protocol) for version negotiation). Agents use it to
|
||||
draw, inspect and look at VectorCraft documents.
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
# UI and interaction parity
|
||||
|
||||
> **QoL update (2026-11-02):** Selection-tool marquees offer Touching (default), Fully Enclosed and Drag Direction in Preferences › Selection & Anchor Display. Drag Direction uses screen left/right, including rotated views, retaining its last rule within three horizontal screen pixels of the press. E reverses the rule for the current drag. A solid enclosed outline or dashed touching outline and a translated label show the active rule. The selection region remains aligned to the document axes. This is a VectorCraft addition; scores and estimates are unchanged.
|
||||
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-31 · **Change:** minor (Color Effects add-on: Find Nearest Swatch, #4923) · **Target:** Adobe Illustrator 2026 (30.x; Units › East Asian Type acts, with Q and Ha units; scores unchanged) · **Target:** Adobe Illustrator 2026 (30.x)
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-06 · **Change:** minor (Drop Shadow and glow dialogs: spinner arrows, #5367, #5368; Stroke Weight Up and Down shortcuts, #5797; Layers: Rename (`ui.rename`) opens the name field of the selected layer, group or object, #5324; Properties in Edit Artboards: Transform section above the Artboards section, #5219; scrubby labels: the Control bar's Stroke: and Opacity: links and the Properties panel's Stroke link scrub their fields, #4088; Properties: Quick Actions has Repeat Options for one selected repeat, #5181; Direct Selection: Shift-click picks several blend spine points, which drag together, #5285; Direct Selection: Cmd/Ctrl held lends the Selection tool and shows its bounding box, #3901; Join tool trims two crossing open paths and joins them at the crossing, #5844; Control bar: Paragraph: link and Align left/center/right while type is selected, #5372; shape tools: a press on the selection's centre point moves it and one in the rotate zone outside a box corner turns it, #5477; scores and estimates unchanged) · **Target:** Adobe Illustrator 2026 (30.x; Units › East Asian Type acts, with Q and Ha units; scores unchanged)
|
||||
|
||||
## Feature submission evidence
|
||||
|
||||
Reference in New Issue
Block a user