Skip to content

Commit 35ab266

Browse files
authored
Add handler for deferred execution of messages (#2951)
* Add Handler for defered execution of messages * Cleanup * Track graph execution id to associate messages with their corresponding execution id * Rename ViewportReady -> NavigationReady * Defer layer deselection
1 parent 2247dd9 commit 35ab266

File tree

21 files changed

+182
-131
lines changed

21 files changed

+182
-131
lines changed

editor/src/dispatcher.rs

Lines changed: 8 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ use crate::messages::prelude::*;
55

66
#[derive(Debug, Default)]
77
pub struct Dispatcher {
8-
buffered_queue: Option<Vec<VecDeque<Message>>>,
98
message_queues: Vec<VecDeque<Message>>,
109
pub responses: Vec<FrontendMessage>,
1110
pub message_handlers: DispatcherMessageHandlers,
@@ -17,6 +16,7 @@ pub struct DispatcherMessageHandlers {
1716
app_window_message_handler: AppWindowMessageHandler,
1817
broadcast_message_handler: BroadcastMessageHandler,
1918
debug_message_handler: DebugMessageHandler,
19+
defer_message_handler: DeferMessageHandler,
2020
dialog_message_handler: DialogMessageHandler,
2121
globals_message_handler: GlobalsMessageHandler,
2222
input_preprocessor_message_handler: InputPreprocessorMessageHandler,
@@ -51,7 +51,10 @@ const SIDE_EFFECT_FREE_MESSAGES: &[MessageDiscriminant] = &[
5151
MessageDiscriminant::Frontend(FrontendMessageDiscriminant::UpdateDocumentLayerStructure),
5252
MessageDiscriminant::Frontend(FrontendMessageDiscriminant::TriggerFontLoad),
5353
];
54-
const DEBUG_MESSAGE_BLOCK_LIST: &[MessageDiscriminant] = &[MessageDiscriminant::Broadcast(BroadcastMessageDiscriminant::TriggerEvent(BroadcastEventDiscriminant::AnimationFrame))];
54+
const DEBUG_MESSAGE_BLOCK_LIST: &[MessageDiscriminant] = &[
55+
MessageDiscriminant::Broadcast(BroadcastMessageDiscriminant::TriggerEvent(BroadcastEventDiscriminant::AnimationFrame)),
56+
MessageDiscriminant::Animation(AnimationMessageDiscriminant::IncrementFrameCounter),
57+
];
5558
// TODO: Find a way to combine these with the list above. We use strings for now since these are the standard variant names used by multiple messages. But having these also type-checked would be best.
5659
const DEBUG_MESSAGE_ENDING_BLOCK_LIST: &[&str] = &["PointerMove", "PointerOutsideViewport", "Overlays", "Draw", "CurrentTime", "Time"];
5760

@@ -91,14 +94,6 @@ impl Dispatcher {
9194

9295
pub fn handle_message<T: Into<Message>>(&mut self, message: T, process_after_all_current: bool) {
9396
let message = message.into();
94-
// Add all additional messages to the buffer if it exists (except from the end buffer message)
95-
if !matches!(message, Message::EndBuffer { .. }) {
96-
if let Some(buffered_queue) = &mut self.buffered_queue {
97-
Self::schedule_execution(buffered_queue, true, [message]);
98-
99-
return;
100-
}
101-
}
10297

10398
// If we are not maintaining the buffer, simply add to the current queue
10499
Self::schedule_execution(&mut self.message_queues, process_after_all_current, [message]);
@@ -137,6 +132,9 @@ impl Dispatcher {
137132
Message::Debug(message) => {
138133
self.message_handlers.debug_message_handler.process_message(message, &mut queue, ());
139134
}
135+
Message::Defer(message) => {
136+
self.message_handlers.defer_message_handler.process_message(message, &mut queue, ());
137+
}
140138
Message::Dialog(message) => {
141139
let context = DialogMessageContext {
142140
portfolio: &self.message_handlers.portfolio_message_handler,
@@ -232,37 +230,6 @@ impl Dispatcher {
232230
Message::Batched { messages } => {
233231
messages.iter().for_each(|message| self.handle_message(message.to_owned(), false));
234232
}
235-
Message::StartBuffer => {
236-
self.buffered_queue = Some(std::mem::take(&mut self.message_queues));
237-
}
238-
Message::EndBuffer { render_metadata } => {
239-
// Assign the message queue to the currently buffered queue
240-
if let Some(buffered_queue) = self.buffered_queue.take() {
241-
self.cleanup_queues(false);
242-
assert!(self.message_queues.is_empty(), "message queues are always empty when ending a buffer");
243-
self.message_queues = buffered_queue;
244-
};
245-
246-
let graphene_std::renderer::RenderMetadata {
247-
upstream_footprints: footprints,
248-
local_transforms,
249-
first_instance_source_id,
250-
click_targets,
251-
clip_targets,
252-
} = render_metadata;
253-
254-
// Run these update state messages immediately
255-
let messages = [
256-
DocumentMessage::UpdateUpstreamTransforms {
257-
upstream_footprints: footprints,
258-
local_transforms,
259-
first_instance_source_id,
260-
},
261-
DocumentMessage::UpdateClickTargets { click_targets },
262-
DocumentMessage::UpdateClipTargets { clip_targets },
263-
];
264-
Self::schedule_execution(&mut self.message_queues, false, messages.map(Message::from));
265-
}
266233
}
267234

268235
// If there are child messages, append the queue to the list of queues
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
use crate::messages::prelude::*;
2+
3+
#[impl_message(Message, Defer)]
4+
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
5+
pub enum DeferMessage {
6+
TriggerGraphRun(u64),
7+
AfterGraphRun { messages: Vec<Message> },
8+
TriggerNavigationReady,
9+
AfterNavigationReady { messages: Vec<Message> },
10+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
use crate::messages::prelude::*;
2+
3+
#[derive(Debug, Default, ExtractField)]
4+
pub struct DeferMessageHandler {
5+
after_graph_run: Vec<(u64, Message)>,
6+
after_viewport_resize: Vec<Message>,
7+
current_graph_submission_id: u64,
8+
}
9+
10+
#[message_handler_data]
11+
impl MessageHandler<DeferMessage, ()> for DeferMessageHandler {
12+
fn process_message(&mut self, message: DeferMessage, responses: &mut VecDeque<Message>, _: ()) {
13+
match message {
14+
DeferMessage::AfterGraphRun { mut messages } => {
15+
self.after_graph_run.extend(messages.drain(..).map(|m| (self.current_graph_submission_id, m)));
16+
}
17+
DeferMessage::AfterNavigationReady { messages } => {
18+
self.after_viewport_resize.extend_from_slice(&messages);
19+
}
20+
DeferMessage::TriggerGraphRun(execution_id) => {
21+
self.current_graph_submission_id = execution_id;
22+
for message in self.after_graph_run.extract_if(.., |x| x.0 < self.current_graph_submission_id) {
23+
responses.push_front(message.1);
24+
}
25+
}
26+
DeferMessage::TriggerNavigationReady => {
27+
for message in self.after_viewport_resize.drain(..) {
28+
responses.push_front(message);
29+
}
30+
}
31+
}
32+
}
33+
34+
advertise_actions!(DeferMessageDiscriminant;
35+
);
36+
}

editor/src/messages/defer/mod.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
mod defer_message;
2+
mod defer_message_handler;
3+
4+
#[doc(inline)]
5+
pub use defer_message::{DeferMessage, DeferMessageDiscriminant};
6+
#[doc(inline)]
7+
pub use defer_message_handler::DeferMessageHandler;

editor/src/messages/dialog/new_document_dialog/new_document_dialog_message_handler.rs

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,18 +24,21 @@ impl MessageHandler<NewDocumentDialogMessage, ()> for NewDocumentDialogMessageHa
2424

2525
let create_artboard = !self.infinite && self.dimensions.x > 0 && self.dimensions.y > 0;
2626
if create_artboard {
27-
responses.add(Message::StartBuffer);
28-
responses.add(GraphOperationMessage::NewArtboard {
29-
id: NodeId::new(),
30-
artboard: graphene_std::Artboard::new(IVec2::ZERO, self.dimensions.as_ivec2()),
27+
responses.add(NodeGraphMessage::RunDocumentGraph);
28+
responses.add(DeferMessage::AfterGraphRun {
29+
messages: vec![
30+
GraphOperationMessage::NewArtboard {
31+
id: NodeId::new(),
32+
artboard: graphene_std::Artboard::new(IVec2::ZERO, self.dimensions.as_ivec2()),
33+
}
34+
.into(),
35+
],
3136
});
3237
}
3338

34-
// TODO: Figure out how to get StartBuffer to work here so we can delete this and use `DocumentMessage::ZoomCanvasToFitAll` instead
35-
// Currently, it is necessary to use `FrontendMessage::TriggerDelayedZoomCanvasToFitAll` rather than `DocumentMessage::ZoomCanvasToFitAll` because the size of the viewport is not yet populated
36-
responses.add(Message::StartBuffer);
37-
responses.add(FrontendMessage::TriggerDelayedZoomCanvasToFitAll);
38-
responses.add(DocumentMessage::DeselectAllLayers);
39+
responses.add(DeferMessage::AfterNavigationReady {
40+
messages: vec![DocumentMessage::ZoomCanvasToFitAll.into(), DocumentMessage::DeselectAllLayers.into()],
41+
});
3942
}
4043
}
4144

editor/src/messages/frontend/frontend_message.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,6 @@ pub enum FrontendMessage {
5959
#[serde(rename = "commitDate")]
6060
commit_date: String,
6161
},
62-
TriggerDelayedZoomCanvasToFitAll,
6362
TriggerDownloadImage {
6463
svg: String,
6564
name: String,

editor/src/messages/message.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
use crate::messages::prelude::*;
2-
use graphene_std::renderer::RenderMetadata;
32
use graphite_proc_macros::*;
43

54
#[impl_message]
@@ -15,6 +14,8 @@ pub enum Message {
1514
#[child]
1615
Debug(DebugMessage),
1716
#[child]
17+
Defer(DeferMessage),
18+
#[child]
1819
Dialog(DialogMessage),
1920
#[child]
2021
Frontend(FrontendMessage),
@@ -40,10 +41,6 @@ pub enum Message {
4041
Batched {
4142
messages: Box<[Message]>,
4243
},
43-
StartBuffer,
44-
EndBuffer {
45-
render_metadata: RenderMetadata,
46-
},
4744
}
4845

4946
/// Provides an impl of `specta::Type` for `MessageDiscriminant`, the struct created by `impl_message`.

editor/src/messages/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ pub mod animation;
44
pub mod app_window;
55
pub mod broadcast;
66
pub mod debug;
7+
pub mod defer;
78
pub mod dialog;
89
pub mod frontend;
910
pub mod globals;

editor/src/messages/portfolio/document/document_message_handler.rs

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1435,6 +1435,20 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
14351435
},
14361436
})
14371437
}
1438+
// Some parts of the editior (e.g. navigation messages) depend on these bounds to be present
1439+
let bounds = if self.graph_view_overlay_open {
1440+
self.network_interface.all_nodes_bounding_box(&self.breadcrumb_network_path).cloned()
1441+
} else {
1442+
self.network_interface.document_bounds_document_space(true)
1443+
};
1444+
if bounds.is_some() {
1445+
responses.add(DeferMessage::TriggerNavigationReady);
1446+
} else {
1447+
// If we don't have bounds yet, we need wait until the node graph has run once more
1448+
responses.add(DeferMessage::AfterGraphRun {
1449+
messages: vec![DocumentMessage::PTZUpdate.into()],
1450+
});
1451+
}
14381452
}
14391453
DocumentMessage::SelectionStepBack => {
14401454
self.network_interface.selection_step_back(&self.selection_network_path);
@@ -1866,14 +1880,14 @@ impl DocumentMessageHandler {
18661880

18671881
let previous_network = std::mem::replace(&mut self.network_interface, network_interface);
18681882

1869-
// Push the UpdateOpenDocumentsList message to the bus in order to update the save status of the open documents
1870-
responses.add(PortfolioMessage::UpdateOpenDocumentsList);
1871-
responses.add(NodeGraphMessage::SelectedNodesUpdated);
1872-
responses.add(NodeGraphMessage::ForceRunDocumentGraph);
18731883
// TODO: Remove once the footprint is used to load the imports/export distances from the edge
1874-
responses.add(NodeGraphMessage::UnloadWires);
1875-
responses.add(NodeGraphMessage::SetGridAlignedEdges);
1876-
responses.add(Message::StartBuffer);
1884+
responses.push_front(NodeGraphMessage::UnloadWires.into());
1885+
responses.push_front(NodeGraphMessage::SetGridAlignedEdges.into());
1886+
1887+
// Push the UpdateOpenDocumentsList message to the bus in order to update the save status of the open documents
1888+
responses.push_front(NodeGraphMessage::ForceRunDocumentGraph.into());
1889+
responses.push_front(NodeGraphMessage::SelectedNodesUpdated.into());
1890+
responses.push_front(PortfolioMessage::UpdateOpenDocumentsList.into());
18771891
Some(previous_network)
18781892
}
18791893
pub fn redo_with_history(&mut self, ipp: &InputPreprocessorMessageHandler, responses: &mut VecDeque<Message>) {

editor/src/messages/portfolio/portfolio_message_handler.rs

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -568,8 +568,9 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
568568

569569
responses.add(NodeGraphMessage::RunDocumentGraph);
570570
responses.add(NodeGraphMessage::SelectedNodesSet { nodes: all_new_ids });
571-
responses.add(Message::StartBuffer);
572-
responses.add(PortfolioMessage::CenterPastedLayers { layers });
571+
responses.add(DeferMessage::AfterGraphRun {
572+
messages: vec![PortfolioMessage::CenterPastedLayers { layers }.into()],
573+
});
573574
}
574575
}
575576
}
@@ -701,13 +702,12 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
701702

702703
if create_document {
703704
// Wait for the document to be rendered so the click targets can be calculated in order to determine the artboard size that will encompass the pasted image
704-
responses.add(Message::StartBuffer);
705-
responses.add(DocumentMessage::WrapContentInArtboard { place_artboard_at_origin: true });
706-
707-
// TODO: Figure out how to get StartBuffer to work here so we can delete this and use `DocumentMessage::ZoomCanvasToFitAll` instead
708-
// Currently, it is necessary to use `FrontendMessage::TriggerDelayedZoomCanvasToFitAll` rather than `DocumentMessage::ZoomCanvasToFitAll` because the size of the viewport is not yet populated
709-
responses.add(Message::StartBuffer);
710-
responses.add(FrontendMessage::TriggerDelayedZoomCanvasToFitAll);
705+
responses.add(DeferMessage::AfterNavigationReady {
706+
messages: vec![DocumentMessage::ZoomCanvasToFitAll.into()],
707+
});
708+
responses.add(DeferMessage::AfterGraphRun {
709+
messages: vec![DocumentMessage::WrapContentInArtboard { place_artboard_at_origin: true }.into()],
710+
});
711711
}
712712
}
713713
PortfolioMessage::PasteSvg {
@@ -733,13 +733,13 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
733733

734734
if create_document {
735735
// Wait for the document to be rendered so the click targets can be calculated in order to determine the artboard size that will encompass the pasted image
736-
responses.add(Message::StartBuffer);
737-
responses.add(DocumentMessage::WrapContentInArtboard { place_artboard_at_origin: true });
736+
responses.add(DeferMessage::AfterGraphRun {
737+
messages: vec![DocumentMessage::WrapContentInArtboard { place_artboard_at_origin: true }.into()],
738+
});
738739

739-
// TODO: Figure out how to get StartBuffer to work here so we can delete this and use `DocumentMessage::ZoomCanvasToFitAll` instead
740-
// Currently, it is necessary to use `FrontendMessage::TriggerDelayedZoomCanvasToFitAll` rather than `DocumentMessage::ZoomCanvasToFitAll` because the size of the viewport is not yet populated
741-
responses.add(Message::StartBuffer);
742-
responses.add(FrontendMessage::TriggerDelayedZoomCanvasToFitAll);
740+
responses.add(DeferMessage::AfterNavigationReady {
741+
messages: vec![DocumentMessage::ZoomCanvasToFitAll.into()],
742+
});
743743
}
744744
}
745745
PortfolioMessage::PrevDocument => {
@@ -1019,9 +1019,6 @@ impl PortfolioMessageHandler {
10191019
/text>"#
10201020
// It's a mystery why the `/text>` tag above needs to be missing its `<`, but when it exists it prints the `<` character in the text. However this works with it removed.
10211021
.to_string();
1022-
responses.add(Message::EndBuffer {
1023-
render_metadata: graphene_std::renderer::RenderMetadata::default(),
1024-
});
10251022
responses.add(FrontendMessage::UpdateDocumentArtwork { svg: error });
10261023
}
10271024
result

0 commit comments

Comments
 (0)