Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 27 additions & 5 deletions editor/src/dispatcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use crate::messages::prelude::*;
pub struct Dispatcher {
message_queues: Vec<VecDeque<Message>>,
pub responses: Vec<FrontendMessage>,
pub frontend_update_messages: Vec<Message>,
pub message_handlers: DispatcherMessageHandlers,
}

Expand Down Expand Up @@ -42,19 +43,24 @@ impl DispatcherMessageHandlers {
/// The last occurrence of the message in the message queue is sufficient to ensure correct behavior.
/// In addition, these messages do not change any state in the backend (aside from caches).
const SIDE_EFFECT_FREE_MESSAGES: &[MessageDiscriminant] = &[
MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::Document(DocumentMessageDiscriminant::PropertiesPanel(
PropertiesPanelMessageDiscriminant::Refresh,
))),
MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::Document(DocumentMessageDiscriminant::DocumentStructureChanged)),
MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::Document(DocumentMessageDiscriminant::Overlays(OverlaysMessageDiscriminant::Draw))),
MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::Document(DocumentMessageDiscriminant::NodeGraph(
NodeGraphMessageDiscriminant::RunDocumentGraph,
))),
MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::SubmitActiveGraphRender),
MessageDiscriminant::Frontend(FrontendMessageDiscriminant::TriggerFontLoad),
];
/// Since we don't need to update the frontend multiple times per frame,
/// we have a set of messages which we will buffer until the next frame is requested.
const FRONTEND_UPDATE_MESSAGES: &[MessageDiscriminant] = &[
MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::Document(DocumentMessageDiscriminant::PropertiesPanel(
PropertiesPanelMessageDiscriminant::Refresh,
))),
MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::UpdateDocumentWidgets),
MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::Document(DocumentMessageDiscriminant::Overlays(OverlaysMessageDiscriminant::Draw))),
MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::Document(DocumentMessageDiscriminant::RenderRulers)),
MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::Document(DocumentMessageDiscriminant::RenderScrollbars)),
MessageDiscriminant::Frontend(FrontendMessageDiscriminant::UpdateDocumentLayerStructure),
MessageDiscriminant::Frontend(FrontendMessageDiscriminant::TriggerFontLoad),
];
const DEBUG_MESSAGE_BLOCK_LIST: &[MessageDiscriminant] = &[
MessageDiscriminant::Broadcast(BroadcastMessageDiscriminant::TriggerEvent(EventMessageDiscriminant::AnimationFrame)),
Expand Down Expand Up @@ -105,6 +111,19 @@ impl Dispatcher {

while let Some(message) = self.message_queues.last_mut().and_then(VecDeque::pop_front) {
// Skip processing of this message if it will be processed later (at the end of the shallowest level queue)
if FRONTEND_UPDATE_MESSAGES.contains(&message.to_discriminant()) {
let already_in_queue = self.message_queues.first().is_some_and(|queue| queue.contains(&message));
if already_in_queue {
self.cleanup_queues(false);
continue;
} else if self.message_queues.len() > 1 {
if !self.frontend_update_messages.contains(&message) {
self.frontend_update_messages.push(message);
}
self.cleanup_queues(false);
continue;
}
}
if SIDE_EFFECT_FREE_MESSAGES.contains(&message.to_discriminant()) {
let already_in_queue = self.message_queues.first().filter(|queue| queue.contains(&message)).is_some();
if already_in_queue {
Expand All @@ -128,6 +147,9 @@ impl Dispatcher {
// Process the action by forwarding it to the relevant message handler, or saving the FrontendMessage to be sent to the frontend
match message {
Message::Animation(message) => {
if let AnimationMessage::IncrementFrameCounter = &message {
self.message_queues[0].extend(self.frontend_update_messages.drain(..));
}
self.message_handlers.animation_message_handler.process_message(message, &mut queue, ());
}
Message::AppWindow(message) => {
Expand Down
28 changes: 27 additions & 1 deletion editor/src/messages/layout/utility_types/layout_widget.rs
Original file line number Diff line number Diff line change
Expand Up @@ -484,13 +484,19 @@ impl LayoutGroup {
}
}

#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, specta::Type)]
pub struct WidgetHolder {
#[serde(rename = "widgetId")]
pub widget_id: WidgetId,
pub widget: Widget,
}

impl PartialEq for WidgetHolder {
fn eq(&self, other: &Self) -> bool {
self.widget == other.widget
}
}

impl WidgetHolder {
#[deprecated(since = "0.0.0", note = "Please use the builder pattern, e.g. TextLabel::new(\"hello\").widget_holder()")]
pub fn new(widget: Widget) -> Self {
Expand All @@ -502,6 +508,26 @@ impl WidgetHolder {

/// Diffing updates self (where self is old) based on new, updating the list of modifications as it does so.
pub fn diff(&mut self, new: Self, widget_path: &mut [usize], widget_diffs: &mut Vec<WidgetDiff>) {
if let (Widget::PopoverButton(button1), Widget::PopoverButton(button2)) = (&mut self.widget, &new.widget) {
if button1.disabled == button2.disabled
&& button1.style == button2.style
&& button1.menu_direction == button2.menu_direction
&& button1.icon == button2.icon
&& button1.tooltip == button2.tooltip
&& button1.tooltip_shortcut == button2.tooltip_shortcut
&& button1.popover_min_width == button2.popover_min_width
{
let mut new_widget_path = widget_path.to_vec();
for (i, (a, b)) in button1.popover_layout.iter_mut().zip(button2.popover_layout.iter()).enumerate() {
new_widget_path.push(i);
a.diff(b.clone(), &mut new_widget_path, widget_diffs);
new_widget_path.pop();
}
self.widget = new.widget;
return;
}
}

// If there have been changes to the actual widget (not just the id)
if self.widget != new.widget {
// We should update to the new widget value as well as a new widget id
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ pub struct CheckboxInput {
pub tooltip: String,

#[serde(rename = "forLabel")]
#[derivative(Debug = "ignore", PartialEq = "ignore")]
pub for_label: CheckboxId,

#[serde(skip)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ pub enum SeparatorType {
Section,
}

#[derive(Clone, serde::Serialize, serde::Deserialize, Derivative, Debug, PartialEq, Eq, Default, WidgetBuilder, specta::Type)]
#[derive(Clone, serde::Serialize, serde::Deserialize, Derivative, Debug, Eq, Default, WidgetBuilder, specta::Type)]
#[derivative(PartialEq)]
pub struct TextLabel {
pub disabled: bool,

Expand All @@ -62,6 +63,7 @@ pub struct TextLabel {
pub tooltip: String,

#[serde(rename = "forCheckbox")]
#[derivative(PartialEq = "ignore")]
pub for_checkbox: CheckboxId,

// Body
Expand Down
11 changes: 9 additions & 2 deletions editor/src/node_graph_executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ use graph_craft::document::value::{RenderOutput, TaggedValue};
use graph_craft::document::{DocumentNode, DocumentNodeImplementation, NodeId, NodeInput};
use graph_craft::proto::GraphErrors;
use graph_craft::wasm_application_io::EditorPreferences;
use graphene_std::application_io::TimingInformation;
use graphene_std::application_io::{NodeGraphUpdateMessage, RenderConfig};
use graphene_std::application_io::{SurfaceFrame, TimingInformation};
use graphene_std::renderer::{RenderMetadata, format_transform_matrix};
use graphene_std::text::FontCache;
use graphene_std::transform::Footprint;
Expand Down Expand Up @@ -54,6 +54,7 @@ pub struct NodeGraphExecutor {
futures: VecDeque<(u64, ExecutionContext)>,
node_graph_hash: u64,
previous_node_to_inspect: Option<NodeId>,
last_svg_canvas: Option<SurfaceFrame>,
}

#[derive(Debug, Clone)]
Expand All @@ -76,6 +77,7 @@ impl NodeGraphExecutor {
node_graph_hash: 0,
current_execution_id: 0,
previous_node_to_inspect: None,
last_svg_canvas: None,
};
(node_runtime, node_executor)
}
Expand Down Expand Up @@ -413,14 +415,19 @@ impl NodeGraphExecutor {
// Send to frontend
responses.add(FrontendMessage::UpdateImageData { image_data });
responses.add(FrontendMessage::UpdateDocumentArtwork { svg });
self.last_svg_canvas = None;
}
RenderOutputType::CanvasFrame(frame) => {
RenderOutputType::CanvasFrame(frame) => 'block: {
if self.last_svg_canvas == Some(frame) {
break 'block;
}
let matrix = format_transform_matrix(frame.transform);
let transform = if matrix.is_empty() { String::new() } else { format!(" transform=\"{matrix}\"") };
let svg = format!(
r#"<svg><foreignObject width="{}" height="{}"{transform}><div data-canvas-placeholder="{}"></div></foreignObject></svg>"#,
frame.resolution.x, frame.resolution.y, frame.surface_id.0
);
self.last_svg_canvas = Some(frame);
responses.add(FrontendMessage::UpdateDocumentArtwork { svg });
}
RenderOutputType::Texture { .. } => {}
Expand Down
6 changes: 5 additions & 1 deletion frontend/wasm/src/editor_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1045,8 +1045,12 @@ async fn poll_node_graph_evaluation() {
crate::NODE_GRAPH_ERROR_DISPLAYED.store(false, Ordering::SeqCst);
}

// Batch responses to pool frontend updates
let batched = Message::Batched {
messages: messages.into_iter().collect(),
};
// Send each `FrontendMessage` to the JavaScript frontend
for response in messages.into_iter().flat_map(|message| editor.handle_message(message)) {
for response in editor.handle_message(batched) {
handle.send_frontend_message_to_js(response);
}

Expand Down