mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-14 00:44:21 +00:00
Shape Builder: a deferred pointer reply describes the gesture's document
A ui.pointer reply that waits for the merge now records the document and tool of the gesture and reads the selection from that document when it is sent, so a command that switches documents first (such as file.new) no longer makes the reply report the other document.
This commit is contained in:
@@ -88,7 +88,9 @@ impl VectorcraftApp {
|
||||
}
|
||||
}
|
||||
}
|
||||
control::Outcome::AfterToolEdit { from, to } => self.pointer_replies.push((from, to, reply)),
|
||||
control::Outcome::AfterToolEdit { from, to, document, tool } => {
|
||||
self.pointer_replies.push(control::PendingPointerReply { from, to, document, tool, reply });
|
||||
}
|
||||
}
|
||||
}
|
||||
if handled == control::MAX_CONTROL_REQUESTS_PER_FRAME {
|
||||
@@ -120,11 +122,12 @@ impl VectorcraftApp {
|
||||
|
||||
/// Sends the waiting `ui.pointer` replies whose events have been made.
|
||||
fn send_pointer_replies(&mut self) {
|
||||
for (from, to, reply) in std::mem::take(&mut self.pointer_replies) {
|
||||
for pending in std::mem::take(&mut self.pointer_replies) {
|
||||
let control::PendingPointerReply { from, to, document, tool, reply } = pending;
|
||||
if self.session.input_applied(from, to) {
|
||||
let _ = reply.send(control::pointer_reply(self));
|
||||
let _ = reply.send(control::pointer_reply_for(self, document, &tool));
|
||||
} else {
|
||||
self.pointer_replies.push((from, to, reply));
|
||||
self.pointer_replies.push(control::PendingPointerReply { from, to, document, tool, reply });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,6 +95,14 @@ fn reserve_synthetic(app: &mut VectorcraftApp, additional: usize) -> Result<(),
|
||||
|
||||
pub type ControlResponse = Value;
|
||||
|
||||
pub(crate) struct PendingPointerReply {
|
||||
pub from: u64,
|
||||
pub to: u64,
|
||||
pub document: Option<u64>,
|
||||
pub tool: String,
|
||||
pub reply: Sender<ControlResponse>,
|
||||
}
|
||||
|
||||
pub struct ControlRequest {
|
||||
pub method: String,
|
||||
pub params: Value,
|
||||
@@ -151,14 +159,28 @@ pub enum Outcome {
|
||||
AfterToolEdit {
|
||||
from: u64,
|
||||
to: u64,
|
||||
document: Option<u64>,
|
||||
tool: String,
|
||||
},
|
||||
}
|
||||
|
||||
/// The reply to `ui.pointer`: the selection, the key object and the active tool.
|
||||
pub(crate) fn pointer_reply(app: &mut VectorcraftApp) -> Value {
|
||||
match app.run("document.inspect", json!({})) {
|
||||
Ok(d) => json!({"ok": true, "result": {"selection": d["selection"], "key": d["key"], "tool": app.session.tool_id()}}),
|
||||
Err(e) => json!({"ok": false, "error": e.to_string()}),
|
||||
pointer_reply_for(app, app.session.active().map(|d| d.uid), app.session.tool_id())
|
||||
}
|
||||
|
||||
pub(crate) fn pointer_reply_for(app: &VectorcraftApp, document: Option<u64>, tool: &str) -> Value {
|
||||
let state = document.and_then(|uid| app.session.documents().iter().find(|d| d.uid == uid));
|
||||
match state {
|
||||
Some(state) => json!({
|
||||
"ok": true,
|
||||
"result": {
|
||||
"selection": state.selection.objects.iter().map(|id| id.0).collect::<Vec<_>>(),
|
||||
"key": state.selection.key.map(|id| id.0),
|
||||
"tool": tool,
|
||||
}
|
||||
}),
|
||||
None => json!({"ok": false, "error": "the gesture's document is no longer open"}),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -558,7 +580,7 @@ fn pointer_events(app: &mut VectorcraftApp, ctx: &egui::Context, req: &ControlRe
|
||||
// a hover is answered at once.
|
||||
let to = app.session.input_count();
|
||||
if !app.session.input_applied(from, to) {
|
||||
return Outcome::AfterToolEdit { from, to };
|
||||
return Outcome::AfterToolEdit { from, to, document: app.session.active().map(|d| d.uid), tool: app.session.tool_id().to_string() };
|
||||
}
|
||||
Outcome::Done(pointer_reply(app))
|
||||
}
|
||||
|
||||
@@ -12,6 +12,31 @@ fn input_request(app: &mut VectorcraftApp, method: &str, params: Value) -> Value
|
||||
r
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deferred_pointer_reply_uses_the_gesture_document_after_file_new() {
|
||||
let mut app = VectorcraftApp::new(Session::new(), Services::default());
|
||||
app.run("file.new", json!({"width": 200, "height": 200})).unwrap();
|
||||
let id = app.run("shape.rectangle", json!({"x": 10, "y": 10, "width": 40, "height": 40})).unwrap()["id"].as_u64().unwrap();
|
||||
app.run("select.set", json!({"ids": [id]})).unwrap();
|
||||
let (reply, received) = std::sync::mpsc::channel();
|
||||
let at = app.session.input_count();
|
||||
app.pointer_replies.push(super::PendingPointerReply {
|
||||
from: at,
|
||||
to: at,
|
||||
document: app.session.active().map(|d| d.uid),
|
||||
tool: app.session.tool_id().to_string(),
|
||||
reply,
|
||||
});
|
||||
|
||||
app.run("file.new", json!({"width": 200, "height": 200})).unwrap();
|
||||
app.answer_pointer_replies(&egui::Context::default());
|
||||
|
||||
let result = received.try_recv().unwrap();
|
||||
assert_eq!(result["ok"], true, "{result}");
|
||||
assert_eq!(result["result"]["selection"], json!([id]));
|
||||
assert_eq!(result["result"]["key"], Value::Null);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ui_set_turns_the_image_trace_preview_off_and_on() {
|
||||
let mut app = VectorcraftApp::new(Session::new(), Services::default());
|
||||
|
||||
@@ -81,7 +81,7 @@ use tests::{
|
||||
mod tests;
|
||||
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::mpsc::{Receiver, Sender};
|
||||
use std::sync::mpsc::Receiver;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use serde_json::{Value, json};
|
||||
@@ -136,7 +136,7 @@ pub struct VectorcraftApp {
|
||||
pending_screenshots: Vec<PendingScreenshot>,
|
||||
/// `ui.pointer` replies waiting for their events and the edit the tool computes on another
|
||||
/// thread ([`control::Outcome::AfterToolEdit`]), with the numbers of those events.
|
||||
pointer_replies: Vec<(u64, u64, Sender<ControlResponse>)>,
|
||||
pointer_replies: Vec<control::PendingPointerReply>,
|
||||
/// Admitted captures whose settled frame has not come yet (issued one at a time).
|
||||
queued_screenshots: Vec<QueuedScreenshot>,
|
||||
screenshot_token: u64,
|
||||
|
||||
@@ -66,8 +66,9 @@ pub const ROTATE_VIEW_ANGLES: [(&str, f64); 12] = [
|
||||
("-90°", -90.0),
|
||||
];
|
||||
|
||||
use super::{ControlResponse, Sender};
|
||||
use super::ControlResponse;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::mpsc::Sender;
|
||||
|
||||
/// Zoom stop stepping reads this (set each UI frame and before commands) so canvas wheel zoom
|
||||
/// matches the status bar when Display Print Size at 100% Zoom is on.
|
||||
|
||||
Reference in New Issue
Block a user