1
+ use std:: time:: Duration ;
2
+
3
+ use interpreted_executor:: ui_runtime:: CompilationRequest ;
4
+
1
5
use crate :: messages:: debug:: utility_types:: MessageLoggingVerbosity ;
2
6
use crate :: messages:: defer:: DeferMessageContext ;
3
7
use crate :: messages:: dialog:: DialogMessageContext ;
@@ -7,7 +11,6 @@ use crate::messages::prelude::*;
7
11
#[ derive( Debug , Default ) ]
8
12
pub struct Dispatcher {
9
13
message_queues : Vec < VecDeque < Message > > ,
10
- pub responses : Vec < FrontendMessage > ,
11
14
pub message_handlers : DispatcherMessageHandlers ,
12
15
}
13
16
@@ -28,6 +31,15 @@ pub struct DispatcherMessageHandlers {
28
31
tool_message_handler : ToolMessageHandler ,
29
32
}
30
33
34
+ // Output messages are what the editor returns after processing Messages. It is handled by the scope outside the editor,
35
+ // which has access to the node graph executor, frontend, etc
36
+ pub enum EditorOutput {
37
+ // These messages perform some side effect other than updating the frontend, but outside the scope of the editor
38
+ RequestNativeNodeGraphRender { compilation_request : CompilationRequest } ,
39
+ RequestDeferredMessage { message : Box < Message > , timeout : Duration } ,
40
+ FrontendMessage { frontend_message : FrontendMessage } ,
41
+ }
42
+
31
43
impl DispatcherMessageHandlers {
32
44
pub fn with_executor ( executor : crate :: node_graph_executor:: NodeGraphExecutor ) -> Self {
33
45
Self {
@@ -41,7 +53,6 @@ impl DispatcherMessageHandlers {
41
53
/// The last occurrence of the message in the message queue is sufficient to ensure correct behavior.
42
54
/// In addition, these messages do not change any state in the backend (aside from caches).
43
55
const SIDE_EFFECT_FREE_MESSAGES : & [ MessageDiscriminant ] = & [
44
- MessageDiscriminant :: Portfolio ( PortfolioMessageDiscriminant :: Document ( DocumentMessageDiscriminant :: NodeGraph ( NodeGraphMessageDiscriminant :: SendGraph ) ) ) ,
45
56
MessageDiscriminant :: Portfolio ( PortfolioMessageDiscriminant :: Document ( DocumentMessageDiscriminant :: PropertiesPanel (
46
57
PropertiesPanelMessageDiscriminant :: Refresh ,
47
58
) ) ) ,
@@ -93,12 +104,14 @@ impl Dispatcher {
93
104
}
94
105
}
95
106
96
- pub fn handle_message < T : Into < Message > > ( & mut self , message : T , process_after_all_current : bool ) {
107
+ pub fn handle_message < T : Into < Message > > ( & mut self , message : T , process_after_all_current : bool ) -> Vec < EditorOutput > {
97
108
let message = message. into ( ) ;
98
-
99
109
// If we are not maintaining the buffer, simply add to the current queue
100
110
Self :: schedule_execution ( & mut self . message_queues , process_after_all_current, [ message] ) ;
101
111
112
+ let mut side_effects = Vec :: new ( ) ;
113
+ let mut output_messages = Vec :: new ( ) ;
114
+
102
115
while let Some ( message) = self . message_queues . last_mut ( ) . and_then ( VecDeque :: pop_front) {
103
116
// Skip processing of this message if it will be processed later (at the end of the shallowest level queue)
104
117
if SIDE_EFFECT_FREE_MESSAGES . contains ( & message. to_discriminant ( ) ) {
@@ -148,20 +161,8 @@ impl Dispatcher {
148
161
self . message_handlers . dialog_message_handler . process_message ( message, & mut queue, context) ;
149
162
}
150
163
Message :: Frontend ( message) => {
151
- // Handle these messages immediately by returning early
152
- if let FrontendMessage :: TriggerFontLoad { .. } = message {
153
- self . responses . push ( message) ;
154
- self . cleanup_queues ( false ) ;
155
-
156
- // Return early to avoid running the code after the match block
157
- return ;
158
- } else {
159
- // `FrontendMessage`s are saved and will be sent to the frontend after the message queue is done being processed
160
- // Deduplicate the render native node graph messages. TODO: Replace responses with hashset
161
- if !( message == FrontendMessage :: RequestNativeNodeGraphRender && self . responses . contains ( & FrontendMessage :: RequestNativeNodeGraphRender ) ) {
162
- self . responses . push ( message) ;
163
- }
164
- }
164
+ // `FrontendMessage`s are saved and will be sent to the frontend after the message queue is done being processed
165
+ output_messages. push ( EditorOutput :: FrontendMessage { frontend_message : message } ) ;
165
166
}
166
167
Message :: Globals ( message) => {
167
168
self . message_handlers . globals_message_handler . process_message ( message, & mut queue, ( ) ) ;
@@ -213,14 +214,19 @@ impl Dispatcher {
213
214
Message :: Preferences ( message) => {
214
215
self . message_handlers . preferences_message_handler . process_message ( message, & mut queue, ( ) ) ;
215
216
}
217
+ Message :: SideEffect ( message) => {
218
+ if !side_effects. contains ( & message) {
219
+ side_effects. push ( message) ;
220
+ }
221
+ }
216
222
Message :: Tool ( message) => {
217
223
let Some ( document_id) = self . message_handlers . portfolio_message_handler . active_document_id ( ) else {
218
224
warn ! ( "Called ToolMessage without an active document.\n Got {message:?}" ) ;
219
- return ;
225
+ return Vec :: new ( ) ;
220
226
} ;
221
227
let Some ( document) = self . message_handlers . portfolio_message_handler . documents . get_mut ( & document_id) else {
222
228
warn ! ( "Called ToolMessage with an invalid active document.\n Got {message:?}" ) ;
223
- return ;
229
+ return Vec :: new ( ) ;
224
230
} ;
225
231
226
232
let context = ToolMessageContext {
@@ -236,7 +242,9 @@ impl Dispatcher {
236
242
}
237
243
Message :: NoOp => { }
238
244
Message :: Batched { messages } => {
239
- messages. iter ( ) . for_each ( |message| self . handle_message ( message. to_owned ( ) , false ) ) ;
245
+ for nested_outputs in messages. into_iter ( ) . map ( |message| self . handle_message ( message, false ) ) {
246
+ output_messages. extend ( nested_outputs) ;
247
+ }
240
248
}
241
249
}
242
250
@@ -247,6 +255,12 @@ impl Dispatcher {
247
255
248
256
self . cleanup_queues ( false ) ;
249
257
}
258
+
259
+ // The full message tree has been processed, so the side effects can be processed
260
+ for message in side_effects {
261
+ output_messages. extend ( self . handle_side_effect ( message) ) ;
262
+ }
263
+ output_messages
250
264
}
251
265
252
266
pub fn collect_actions ( & self ) -> ActionList {
@@ -314,6 +328,7 @@ impl Dispatcher {
314
328
315
329
#[ cfg( test) ]
316
330
mod test {
331
+ use crate :: messages:: side_effects:: EditorOutputMessage ;
317
332
pub use crate :: test_utils:: test_prelude:: * ;
318
333
319
334
/// Create an editor with three layers
@@ -513,7 +528,10 @@ mod test {
513
528
514
529
for response in responses {
515
530
// Check for the existence of the file format incompatibility warning dialog after opening the test file
516
- if let FrontendMessage :: UpdateDialogColumn1 { layout_target : _, diff } = response {
531
+ if let EditorOutputMessage :: FrontendMessage {
532
+ frontend_message : FrontendMessage :: UpdateDialogColumn1 { layout_target : _, diff } ,
533
+ } = response
534
+ {
517
535
if let DiffUpdate :: SubLayout ( sub_layout) = & diff[ 0 ] . new_value {
518
536
if let LayoutGroup :: Row { widgets } = & sub_layout[ 0 ] {
519
537
if let Widget :: TextLabel ( TextLabel { value, .. } ) = & widgets[ 0 ] . widget {
0 commit comments