Merge pull request #695 from storytold/fix/693-artboard-paste
Fix #693: a click activates its artboard; in-place pastes go onto it
This commit is contained in:
commit
c4e20023de
File diff suppressed because one or more lines are too long
|
|
@ -55,7 +55,7 @@ pub fn specs() -> Vec<CommandSpec> {
|
|||
"Paste in Front",
|
||||
["Edit"],
|
||||
Some("Cmd+F"),
|
||||
"{swatchConflict?} paste in place just above the top selected object, or on top of the current layer when nothing is selected (resources and layers as edit.paste) → {ids, added, merged, renamed}",
|
||||
"{swatchConflict?, artboard?: index (the active artboard: the objects go where they were on the artboard they were copied from, on this one)} paste in place just above the top selected object, or on top of the current layer when nothing is selected (resources and layers as edit.paste) → {ids, added, merged, renamed}",
|
||||
has_clipboard,
|
||||
|s, p| paste(s, p, PasteMode::Front)
|
||||
),
|
||||
|
|
@ -64,7 +64,7 @@ pub fn specs() -> Vec<CommandSpec> {
|
|||
"Paste in Back",
|
||||
["Edit"],
|
||||
Some("Cmd+B"),
|
||||
"{swatchConflict?} paste in place just below the bottom selected object, or at the bottom of the current layer when nothing is selected (resources and layers as edit.paste) → {ids, added, merged, renamed}",
|
||||
"{swatchConflict?, artboard?: index (as edit.pasteInFront)} paste in place just below the bottom selected object, or at the bottom of the current layer when nothing is selected (resources and layers as edit.paste) → {ids, added, merged, renamed}",
|
||||
has_clipboard,
|
||||
|s, p| paste(s, p, PasteMode::Back)
|
||||
),
|
||||
|
|
@ -73,7 +73,7 @@ pub fn specs() -> Vec<CommandSpec> {
|
|||
"Paste in Place",
|
||||
["Edit"],
|
||||
Some("Cmd+Shift+V"),
|
||||
"{swatchConflict?} paste where the objects were copied (resources and layers as edit.paste) → {ids, added, merged, renamed}",
|
||||
"{swatchConflict?, artboard?: index (as edit.pasteInFront)} paste where the objects were copied (resources and layers as edit.paste) → {ids, added, merged, renamed}",
|
||||
has_clipboard,
|
||||
|s, p| paste(s, p, PasteMode::InPlace)
|
||||
),
|
||||
|
|
@ -310,6 +310,17 @@ fn paste(s: &mut Session, p: &Value, mode: PasteMode) -> Result<Value> {
|
|||
r
|
||||
}
|
||||
|
||||
/// Paste in Place, in Front or in Back onto artboard `artboard` (the active one, #693): where the
|
||||
/// objects were on the artboard they were copied from, on that one; without it (or a copy from no
|
||||
/// artboard), where they were.
|
||||
fn onto_artboard(doc: &Document, p: &Value, clip: &Clipboard) -> Affine {
|
||||
let target = p.get("artboard").and_then(Value::as_u64).and_then(|i| usize::try_from(i).ok()).and_then(|i| doc.artboards.get(i));
|
||||
match (target, clip.source_artboard) {
|
||||
(Some(t), Some(src)) => Affine::translate(t.rect.origin() - src.origin()),
|
||||
_ => Affine::IDENTITY,
|
||||
}
|
||||
}
|
||||
|
||||
fn paste_clip(s: &mut Session, p: &Value, mode: PasteMode, clip: &Clipboard, choices: &SwatchChoices) -> Result<Value> {
|
||||
let off = s.prefs.paste_offset;
|
||||
let st = s.doc()?;
|
||||
|
|
@ -345,7 +356,8 @@ fn paste_clip(s: &mut Session, p: &Value, mode: PasteMode, clip: &Clipboard, cho
|
|||
let src = clip.source_artboard.or_else(|| st.doc.artboards.first().map(|a| a.rect)).map(|r| r.origin()).unwrap_or_default();
|
||||
st.doc.artboards.iter().map(|a| Affine::translate(a.rect.origin() - src)).collect()
|
||||
}
|
||||
_ => vec![Affine::IDENTITY],
|
||||
// In place, in front, in back: onto the active artboard when the UI names it.
|
||||
_ => vec![onto_artboard(&st.doc, p, clip)],
|
||||
};
|
||||
let (ids, imported, artboard) = s.edit(mode.label(), |d, sel| {
|
||||
let imported = clip.import_into(d, choices, same_doc);
|
||||
|
|
|
|||
|
|
@ -551,6 +551,26 @@ fn paste_on_all_artboards_keeps_the_offset_to_the_source_artboard() {
|
|||
}
|
||||
}
|
||||
|
||||
/// Paste in Place, in Front and in Back onto the active artboard (#693): where the objects were on
|
||||
/// the artboard they were copied from, on the one named; without it, where they were.
|
||||
#[test]
|
||||
fn in_place_pastes_go_onto_the_artboard_named() {
|
||||
let mut s = Session::new();
|
||||
s.execute("file.new", &json!({"width": 200, "height": 100, "artboards": 3})).unwrap();
|
||||
let boards: Vec<Rect> = doc(&s).artboards.iter().map(|a| a.rect).collect();
|
||||
let r = rect(&mut s, boards[0].x0 + 10.0, boards[0].y0 + 15.0, 20.0, 20.0);
|
||||
copy(&mut s, &[r]);
|
||||
for cmd in ["edit.pasteInPlace", "edit.pasteInFront", "edit.pasteInBack"] {
|
||||
let ids = ids_of(&s.execute(cmd, &json!({"artboard": 2})).unwrap());
|
||||
assert_eq!(doc(&s).bounds_of(&ids, false).unwrap().origin(), Point::new(boards[2].x0 + 10.0, boards[2].y0 + 15.0), "{cmd}");
|
||||
let ids = ids_of(&s.execute(cmd, &json!({})).unwrap());
|
||||
assert_eq!(doc(&s).bounds_of(&ids, false).unwrap().origin(), Point::new(boards[0].x0 + 10.0, boards[0].y0 + 15.0), "{cmd} without one");
|
||||
// An artboard that isn't there: where they were.
|
||||
let ids = ids_of(&s.execute(cmd, &json!({"artboard": 9})).unwrap());
|
||||
assert_eq!(doc(&s).bounds_of(&ids, false).unwrap().origin(), Point::new(boards[0].x0 + 10.0, boards[0].y0 + 15.0), "{cmd} on no artboard");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn paste_remembers_layers_restores_the_source_layers() {
|
||||
let mut s = session();
|
||||
|
|
|
|||
|
|
@ -864,6 +864,15 @@ pub fn dispatch(app: &mut VectorcraftApp, ev: &PointerEvent, view: vectorcraft_e
|
|||
{
|
||||
v.artboard = i;
|
||||
}
|
||||
// A press with a selection tool inside an artboard makes it the active one (#693), as the
|
||||
// Artboard tool's does: Paste in Place and in Front or Back then paste onto it.
|
||||
if ev.kind == PointerKind::Down
|
||||
&& vectorcraft_tools::catalog::is_selection_tool(app.session.tool_id())
|
||||
&& let Some(i) = app.session.active().and_then(|d| d.doc.artboard_at(ev.pos))
|
||||
&& let Some(v) = app.view_mut()
|
||||
{
|
||||
v.artboard = i;
|
||||
}
|
||||
apply_requests(app, r);
|
||||
}
|
||||
|
||||
|
|
@ -2051,6 +2060,34 @@ mod tests {
|
|||
out.textures_delta.clear();
|
||||
}
|
||||
|
||||
/// A click with the Selection tool inside an artboard makes it the active one, and Paste in
|
||||
/// Place then pastes onto it, where the objects were on their own artboard (#693).
|
||||
#[test]
|
||||
fn a_click_activates_its_artboard_and_paste_in_place_goes_onto_it() {
|
||||
let mut app = VectorcraftApp::new(Session::new(), Default::default());
|
||||
app.session.execute("file.new", &json!({"width": 200, "height": 100, "artboards": 3})).unwrap();
|
||||
let boards: Vec<_> = app.session.active().unwrap().doc.artboards.iter().map(|a| a.rect).collect();
|
||||
app.session.execute("shape.rectangle", &json!({"x": boards[0].x0 + 10.0, "y": boards[0].y0 + 15.0, "width": 20, "height": 20})).unwrap();
|
||||
app.run("edit.copy", json!({})).unwrap();
|
||||
let ctx = egui::Context::default();
|
||||
frame(&mut app, &ctx, vec![]);
|
||||
app.select_tool("selection");
|
||||
let view = app.view_info();
|
||||
let at = boards[2].center();
|
||||
for kind in [PointerKind::Down, PointerKind::Up] {
|
||||
dispatch(&mut app, &PointerEvent { kind, pos: at, mods: Default::default(), pressure: 1.0 }, view);
|
||||
}
|
||||
assert_eq!(app.view().unwrap().artboard, 2, "the clicked artboard is the active one");
|
||||
let ids: Vec<vectorcraft_doc::NodeId> = app.run("edit.pasteInPlace", json!({})).unwrap()["ids"]
|
||||
.as_array()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|v| vectorcraft_doc::NodeId(v.as_u64().unwrap()))
|
||||
.collect();
|
||||
let b = app.session.active().unwrap().doc.bounds_of(&ids, false).unwrap();
|
||||
assert_eq!((b.x0, b.y0), (boards[2].x0 + 10.0, boards[2].y0 + 15.0));
|
||||
}
|
||||
|
||||
/// Enable Touch Gestures (#585): a two-finger tap undoes and a three-finger one redoes, and the
|
||||
/// first finger's press (the pointer egui makes of it) draws nothing. Off, a tap does nothing.
|
||||
#[test]
|
||||
|
|
|
|||
|
|
@ -534,6 +534,14 @@ impl VectorcraftApp {
|
|||
{
|
||||
p.insert("center".into(), serde_json::json!([c.x, c.y]));
|
||||
}
|
||||
// In place, in front, in back: onto the active artboard (#693).
|
||||
if matches!(id, "edit.pasteInPlace" | "edit.pasteInFront" | "edit.pasteInBack")
|
||||
&& params.get("artboard").is_none()
|
||||
&& let Some(i) = self.view().map(|v| v.artboard)
|
||||
&& let Some(p) = params.as_object_mut()
|
||||
{
|
||||
p.insert("artboard".into(), serde_json::json!(i));
|
||||
}
|
||||
if let Some(r) = dialogs::swatch_conflict::ask(self, id, ¶ms) {
|
||||
return r;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue