mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-01 21:57:12 -06:00
Merge pull request #5060 from storytold/fix/artboard-panel-reorder-4687
Fix #4687: the Artboards panel reorders artboards by dragging a row
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
//! Artboards panel: numbered list with inline rename (double-click the name, or the panel menu's
|
||||
//! Rename; while several artboards are selected, the name typed goes to all of them, numbered),
|
||||
//! move up / down, new
|
||||
//! move up / down (or drag a row above or below another), new
|
||||
//! (Alt-click: Artboard Options for the new artboard first), delete; a row dragged onto New
|
||||
//! Artboard duplicates its artboard (as Duplicate Artboards does, with
|
||||
//! its art when the Artboard tool's Move/Copy Artwork with Artboard is on). The highlighted row is the active artboard, the one the status bar's navigator shows and
|
||||
@@ -405,6 +405,8 @@ pub fn show(app: &mut VectorcraftApp, ui: &mut Ui) {
|
||||
let all = picked(app, abs.len());
|
||||
let editing: Option<usize> = pstate(ui.ctx(), "ab-edit");
|
||||
let mut row_clicked = false;
|
||||
// A row dropped on another row: (from, to), the index it lands at.
|
||||
let mut dropped = None;
|
||||
let list_rect = widgets::list_box(ui, |ui| {
|
||||
ui.set_min_height(110.0);
|
||||
ui.spacing_mut().item_spacing.y = 0.0;
|
||||
@@ -512,10 +514,26 @@ pub fn show(app: &mut VectorcraftApp, ui: &mut Ui) {
|
||||
} else if resp.double_clicked() {
|
||||
start_rename(ui.ctx(), app, i);
|
||||
}
|
||||
// Another row dragged over this one lands above or below it by the pointer's half,
|
||||
// a line showing where.
|
||||
if let Some(from) = resp.dnd_hover_payload::<RowDrag>().map(|d| d.0).filter(|from| *from != i) {
|
||||
let after = ui.input(|inp| inp.pointer.interact_pos()).is_some_and(|p| p.y > r.center().y);
|
||||
let y = if after { r.bottom() } else { r.top() };
|
||||
ui.painter().line_segment([pos2(r.left(), y), pos2(r.right(), y)], egui::Stroke::new(2.0, t.accent));
|
||||
if resp.dnd_release_payload::<RowDrag>().is_some() {
|
||||
let to = i + usize::from(after);
|
||||
dropped = Some((from, if from < to { to - 1 } else { to }));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
scrolled.inner_rect
|
||||
});
|
||||
if let Some((from, to)) = dropped.filter(|(from, to)| from != to)
|
||||
&& app.run("artboard.reorder", json!({"index": from, "to": to})).is_ok()
|
||||
{
|
||||
select(app, to);
|
||||
}
|
||||
// Keyboard: the panel takes it with a click on a row and gives it up when the pointer is
|
||||
// pressed outside the list or the panel is not drawn (`ab-keys`: the frame it last had it in).
|
||||
let pressed_outside = ui.input(|i| i.pointer.any_pressed() && i.pointer.interact_pos().is_some_and(|p| !list_rect.contains(p)));
|
||||
|
||||
@@ -994,6 +994,50 @@ fn dragging_a_row_onto_new_artboard_duplicates_it() {
|
||||
assert_eq!(app.view().unwrap().artboard, 1);
|
||||
}
|
||||
|
||||
/// #4687: a row dragged above or below another row reorders the artboards there (a line shows
|
||||
/// where it lands), in one undo step, and the moved artboard becomes the active one.
|
||||
#[test]
|
||||
fn dragging_a_row_onto_another_row_reorders_the_artboards() {
|
||||
let mut app = VectorcraftApp::new(Session::new(), Default::default());
|
||||
app.session.execute("file.new", &json!({"width": 100, "height": 100, "artboards": 3})).unwrap();
|
||||
let ctx = egui::Context::default();
|
||||
let screen = Some(egui::Rect::from_min_size(egui::Pos2::ZERO, vec2(240.0, 400.0)));
|
||||
let frame = |app: &mut VectorcraftApp, events: Vec<egui::Event>| {
|
||||
let mut out = ctx.run_ui(egui::RawInput { events, screen_rect: screen, ..Default::default() }, |ui| show(app, ui));
|
||||
out.textures_delta.clear();
|
||||
out.shapes
|
||||
};
|
||||
let shapes = frame(&mut app, vec![]);
|
||||
let name_at = |label: &str| {
|
||||
shapes
|
||||
.iter()
|
||||
.find_map(|c| match &c.shape {
|
||||
egui::Shape::Text(t) if t.galley.text() == label => Some(t.pos + vec2(4.0, 4.0)),
|
||||
_ => None,
|
||||
})
|
||||
.unwrap()
|
||||
};
|
||||
let (first, last) = (name_at("Artboard 1"), name_at("Artboard 3"));
|
||||
let names = |app: &VectorcraftApp| app.session.active().unwrap().doc.artboards.iter().map(|a| a.name.clone()).collect::<Vec<_>>();
|
||||
let button = |at, pressed| egui::Event::PointerButton { pos: at, button: egui::PointerButton::Primary, pressed, modifiers: Default::default() };
|
||||
// Artboard 1 dragged onto the lower half of Artboard 3's row lands below it.
|
||||
let below = last + vec2(0.0, 8.0);
|
||||
frame(&mut app, vec![egui::Event::PointerMoved(first), button(first, true)]);
|
||||
frame(&mut app, vec![egui::Event::PointerMoved(first + vec2(0.0, 12.0))]);
|
||||
let held = frame(&mut app, vec![egui::Event::PointerMoved(below)]);
|
||||
let accent = Tokens::get(&ctx).accent;
|
||||
assert!(
|
||||
held.iter().any(|c| matches!(&c.shape, egui::Shape::LineSegment { stroke, .. } if stroke.color == accent)),
|
||||
"a line shows where it lands"
|
||||
);
|
||||
frame(&mut app, vec![button(below, false)]);
|
||||
assert_eq!(names(&app), ["Artboard 2", "Artboard 3", "Artboard 1"]);
|
||||
assert_eq!(app.view().unwrap().artboard, 2, "the moved artboard is active");
|
||||
// One undo step puts it back.
|
||||
app.session.execute("edit.undo", &json!({})).unwrap();
|
||||
assert_eq!(names(&app), ["Artboard 1", "Artboard 2", "Artboard 3"]);
|
||||
}
|
||||
|
||||
/// A click in a row's lock column locks its artboard and shows the lock next to the name, a
|
||||
/// second click unlocks it, and each click is one undo step. Delete Artboard leaves a locked
|
||||
/// artboard.
|
||||
|
||||
+3
-1
@@ -1,6 +1,6 @@
|
||||
# UI and interaction parity
|
||||
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-01 · **Change:** minor (Path Precision Tools add-on: Expand Dashes into Paths, #4922) · **Target:** Adobe Illustrator 2026 (30.x)
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-01 · **Change:** minor (Artboards panel: drag a row to reorder the artboards, #4687) · **Target:** Adobe Illustrator 2026 (30.x)
|
||||
|
||||
## Feature submission evidence
|
||||
|
||||
@@ -228,6 +228,7 @@ tools, never checked side by side) isn't captured row by row.
|
||||
| Escape while typing ends the edit, keeps the type selected and switches to the Selection tool | done | #1802 (#1686, #1656) |
|
||||
| Selection tool: selected area type and type on a path show an in port and an out port (empty, an arrow where threaded, the red "+" of overflow); a click on either loads the text cursor; a click on a closed path, on empty canvas or a drag threads the text there, after the clicked frame from its out port, before it from its in port; Escape or another tool drops it; a double-click on a threaded port breaks the thread there | partial | #2572 (`text.thread.add`, `before` for the in port); the out port sits on the frame's right side above the bottom-right corner handle and the in port on its left side below the top-left one, so the handles can still be grabbed; a double-click on a threaded port breaks the thread (`text.thread.break`, [archived help](https://web.archive.org/web/20220127090642/https://helpx.adobe.com/illustrator/using/creating-text.html)); the loaded cursor borrows the I-beam |
|
||||
| Shift-click selects several artboards (Artboard tool, Artboards panel); they move, Alt-copy and delete together | done | `artboard.move`/`artboard.delete` `indices` (#1139); panel menu, trash and Properties share selected-set deletion; all-selected retains artboard 1 and a single board cannot be deleted, with pointer-event and Undo/Redo coverage |
|
||||
| Artboards panel: drag a row above or below another to reorder the artboards (as Move Up / Move Down do) | done | #4687; a line shows where the row lands, `artboard.reorder` in one undo step, the moved artboard becomes active; public forum answers describe the drag |
|
||||
| Artboards panel › Delete Empty Artboards | done | `artboard.deleteEmpty`; one undo step, guides go too, locked artboards stay, and artboard 1 when every artboard is empty and none is locked; an artboard is empty when no object (hidden, locked and template art included; guides not) has visual bounds that meet or touch it, a group counting by its members and a clip group or clipping layer by its members inside the clip (our reading of "no artwork"; the pages give no finer rule); [spec](https://helpx.adobe.com/illustrator/desktop/create-manage-artboards/add-edit-artboards/delete-artboards.html) |
|
||||
| Artboard Delete/Backspace delete selected boards while protecting artwork, the last board and active drags | done | Headless shortcut regressions in `tests/distortkeys.rs` cover retaining the original first board when all are selected, single-board protection, artwork, saved state and Undo/Redo; the navigator follows the tool’s surviving board through repeated deletion and history changes. |
|
||||
| Artboard navigator synchronizes the active tool selection before deletion | done | Actual status-bar Next → Delete/Backspace and Next → Properties Delete regressions in `chrome.rs` cover surviving board records, preserved artwork and Undo/Redo; other tools keep object selection and history. **Tab-switch follow-up:** document-tab changes align the Artboard tool with the displayed board. Actual A → B → A tab clicks followed by Properties Delete and controls for same-tab multiselection, Move/Scale Artwork options and other tools passed, along with historical full CI on `a0671777`. [Validation evidence](target-app-parity.md#evidence-what-ships-today-by-area). **Clean-tab close:** with the Artboard tool active, closing a clean active tab restores the surviving document's displayed board before Properties Delete. |
|
||||
@@ -393,6 +394,7 @@ separate. Scores and hours unchanged. [Behavior and command details](development
|
||||
|
||||
| Date | Change | Summary |
|
||||
|---|---|---|
|
||||
| 2026-11-01 | minor | Artboards panel: a row dragged above or below another reorders the artboards (#4687); scores and estimates unchanged |
|
||||
| 2026-11-01 | minor | Document Tools add-on (a VectorCraft addition): Export All Open Documents as PDF or SVG (#4920); scores and estimates unchanged |
|
||||
| 2026-11-01 | minor | Path Precision Tools add-on (a VectorCraft addition): Expand Dashes into Paths (#4922); scores and estimates unchanged |
|
||||
| 2026-11-01 | minor | Arrange Tools add-on (a VectorCraft addition): Randomize Opacity (#4925); scores and estimates unchanged |
|
||||
|
||||
Reference in New Issue
Block a user