Add struct field visualization to the editor message hierarchy tree visualization on the website (#2917)

* Fix Message Tree: Enforce Structure and Visibility

* Code review

* fix the erroreous ouputs

* error handling for MessageHandler

* Fix website visualization HTML generation

* error handling for tuple-style message enum variant

* cleanup

* Update messages

* Normalize BroadcastEvent

---------

Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
Mohd Mohsin
2025-08-18 21:04:29 -07:00
committed by GitHub
co-authored by Keavon Chambers
parent 5ed45ead6f
commit 17d70dc60e
59 changed files with 988 additions and 506 deletions
@@ -53,7 +53,9 @@ pub enum DocumentMessage {
DocumentHistoryBackward,
DocumentHistoryForward,
DocumentStructureChanged,
DrawArtboardOverlays(OverlayContext),
DrawArtboardOverlays {
context: OverlayContext,
},
DuplicateSelectedLayers,
EnterNestedNetwork {
node_id: NodeId,
@@ -72,9 +74,15 @@ pub enum DocumentMessage {
open: bool,
},
GraphViewOverlayToggle,
GridOptions(GridSnapping),
GridOverlays(OverlayContext),
GridVisibility(bool),
GridOptions {
options: GridSnapping,
},
GridOverlays {
context: OverlayContext,
},
GridVisibility {
visible: bool,
},
GroupSelectedLayers {
group_folder_type: GroupFolderType,
},
@@ -391,7 +391,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
responses.add(FrontendMessage::UpdateDocumentLayerStructure { data_buffer });
}
}
DocumentMessage::DrawArtboardOverlays(overlay_context) => {
DocumentMessage::DrawArtboardOverlays { context: overlay_context } => {
if !overlay_context.visibility_settings.artboard_name() {
return;
}
@@ -588,19 +588,19 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
DocumentMessage::GraphViewOverlayToggle => {
responses.add(DocumentMessage::GraphViewOverlay { open: !self.graph_view_overlay_open });
}
DocumentMessage::GridOptions(grid) => {
self.snapping_state.grid = grid;
DocumentMessage::GridOptions { options } => {
self.snapping_state.grid = options;
self.snapping_state.grid_snapping = true;
responses.add(OverlaysMessage::Draw);
responses.add(PortfolioMessage::UpdateDocumentWidgets);
}
DocumentMessage::GridOverlays(mut overlay_context) => {
DocumentMessage::GridOverlays { context: mut overlay_context } => {
if self.snapping_state.grid_snapping {
grid_overlay(self, &mut overlay_context)
}
}
DocumentMessage::GridVisibility(enabled) => {
self.snapping_state.grid_snapping = enabled;
DocumentMessage::GridVisibility { visible } => {
self.snapping_state.grid_snapping = visible;
responses.add(OverlaysMessage::Draw);
}
DocumentMessage::GroupSelectedLayers { group_folder_type } => {
@@ -1062,7 +1062,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
if !parent_layers.is_empty() {
let nodes = parent_layers.into_iter().collect();
responses.add(NodeGraphMessage::SelectedNodesSet { nodes });
responses.add(BroadcastEvent::SelectionChanged);
responses.add(EventMessage::SelectionChanged);
}
}
DocumentMessage::SelectAllLayers => {
@@ -1137,7 +1137,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
} else {
responses.add_front(NodeGraphMessage::SelectedNodesAdd { nodes: vec![id] });
}
responses.add(BroadcastEvent::SelectionChanged);
responses.add(EventMessage::SelectionChanged);
} else {
nodes.push(id);
}
@@ -1206,7 +1206,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
Some(overlays_type) => overlays_type,
None => {
visibility_settings.all = visible;
responses.add(BroadcastEvent::ToolAbort);
responses.add(EventMessage::ToolAbort);
responses.add(OverlaysMessage::Draw);
return;
}
@@ -1229,7 +1229,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
OverlaysType::Handles => visibility_settings.handles = visible,
}
responses.add(BroadcastEvent::ToolAbort);
responses.add(EventMessage::ToolAbort);
responses.add(OverlaysMessage::Draw);
}
DocumentMessage::SetRangeSelectionLayer { new_layer } => {
@@ -1443,12 +1443,14 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
let transform = self.navigation_handler.calculate_offset_transform(ipp.viewport_bounds.center(), &self.document_ptz);
self.network_interface.set_document_to_viewport_transform(transform);
// Ensure selection box is kept in sync with the pointer when the PTZ changes
responses.add(SelectToolMessage::PointerMove(SelectToolPointerKeys {
axis_align: Key::Shift,
snap_angle: Key::Shift,
center: Key::Alt,
duplicate: Key::Alt,
}));
responses.add(SelectToolMessage::PointerMove {
modifier_keys: SelectToolPointerKeys {
axis_align: Key::Shift,
snap_angle: Key::Shift,
center: Key::Alt,
duplicate: Key::Alt,
},
});
responses.add(NodeGraphMessage::RunDocumentGraph);
} else {
let Some(network_metadata) = self.network_interface.network_metadata(&self.breadcrumb_network_path) else {
@@ -1477,11 +1479,11 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
}
DocumentMessage::SelectionStepBack => {
self.network_interface.selection_step_back(&self.selection_network_path);
responses.add(BroadcastEvent::SelectionChanged);
responses.add(EventMessage::SelectionChanged);
}
DocumentMessage::SelectionStepForward => {
self.network_interface.selection_step_forward(&self.selection_network_path);
responses.add(BroadcastEvent::SelectionChanged);
responses.add(EventMessage::SelectionChanged);
}
DocumentMessage::WrapContentInArtboard { place_artboard_at_origin } => {
// Get bounding box of all layers
@@ -2484,7 +2486,7 @@ impl DocumentMessageHandler {
.icon("Grid")
.tooltip("Grid")
.tooltip_shortcut(action_keys!(DocumentMessageDiscriminant::ToggleGridVisibility))
.on_update(|optional_input: &CheckboxInput| DocumentMessage::GridVisibility(optional_input.checked).into())
.on_update(|optional_input: &CheckboxInput| DocumentMessage::GridVisibility { visible: optional_input.checked }.into())
.widget_holder(),
PopoverButton::new()
.popover_layout(overlay_options(&self.snapping_state.grid))
@@ -139,7 +139,7 @@ impl MessageHandler<NavigationMessage, NavigationMessageContext<'_>> for Navigat
let transformed_delta = document_to_viewport.inverse().transform_vector2(delta);
ptz.pan += transformed_delta;
responses.add(BroadcastEvent::CanvasTransformed);
responses.add(EventMessage::CanvasTransformed);
responses.add(DocumentMessage::PTZUpdate);
}
NavigationMessage::CanvasPanAbortPrepare { x_not_y_axis } => {
@@ -286,7 +286,7 @@ impl MessageHandler<NavigationMessage, NavigationMessageContext<'_>> for Navigat
ptz.flip = !ptz.flip;
responses.add(DocumentMessage::PTZUpdate);
responses.add(BroadcastEvent::CanvasTransformed);
responses.add(EventMessage::CanvasTransformed);
responses.add(MenuBarMessage::SendLayout);
responses.add(PortfolioMessage::UpdateDocumentWidgets);
}
@@ -325,7 +325,7 @@ impl MessageHandler<NavigationMessage, NavigationMessageContext<'_>> for Navigat
self.navigation_operation = NavigationOperation::None;
// Send the final messages to close out the operation
responses.add(BroadcastEvent::CanvasTransformed);
responses.add(EventMessage::CanvasTransformed);
responses.add(ToolMessage::UpdateCursor);
responses.add(ToolMessage::UpdateHints);
responses.add(NavigateToolMessage::End);
@@ -129,7 +129,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
NodeGraphMessage::AddPathNode => {
if let Some(layer) = make_path_editable_is_allowed(network_interface, network_interface.document_metadata()) {
responses.add(NodeGraphMessage::CreateNodeInLayerWithTransaction { node_type: "Path".to_string(), layer });
responses.add(BroadcastEvent::SelectionChanged);
responses.add(EventMessage::SelectionChanged);
}
}
NodeGraphMessage::AddImport => {
@@ -142,7 +142,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
}
NodeGraphMessage::Init => {
responses.add(BroadcastMessage::SubscribeEvent {
on: BroadcastEvent::SelectionChanged,
on: EventMessage::SelectionChanged,
send: Box::new(NodeGraphMessage::SelectedNodesUpdated.into()),
});
network_interface.load_structure();
@@ -1472,7 +1472,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
return;
};
selected_nodes.add_selected_nodes(nodes);
responses.add(BroadcastEvent::SelectionChanged);
responses.add(EventMessage::SelectionChanged);
}
NodeGraphMessage::SelectedNodesRemove { nodes } => {
let Some(selected_nodes) = network_interface.selected_nodes_mut(selection_network_path) else {
@@ -1480,7 +1480,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
return;
};
selected_nodes.retain_selected_nodes(|node| !nodes.contains(node));
responses.add(BroadcastEvent::SelectionChanged);
responses.add(EventMessage::SelectionChanged);
}
NodeGraphMessage::SelectedNodesSet { nodes } => {
let Some(selected_nodes) = network_interface.selected_nodes_mut(selection_network_path) else {
@@ -1488,7 +1488,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
return;
};
selected_nodes.set_selected_nodes(nodes);
responses.add(BroadcastEvent::SelectionChanged);
responses.add(EventMessage::SelectionChanged);
}
NodeGraphMessage::SendClickTargets => responses.add(FrontendMessage::UpdateClickTargets {
click_targets: Some(network_interface.collect_frontend_click_targets(breadcrumb_network_path)),
@@ -1888,7 +1888,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
return;
};
selected_nodes.clear_selected_nodes();
responses.add(BroadcastEvent::SelectionChanged);
responses.add(EventMessage::SelectionChanged);
responses.add(NodeGraphMessage::SendGraph);
}
@@ -200,7 +200,7 @@ pub fn overlay_options(grid: &GridSnapping) -> Vec<LayoutGroup> {
move |input: &I| {
let mut grid = grid.clone();
update(&mut grid, input);
DocumentMessage::GridOptions(grid).into()
DocumentMessage::GridOptions { options: grid }.into()
}
}
let update_origin = |grid, update: fn(&mut GridSnapping) -> Option<&mut f64>| {
@@ -7,14 +7,14 @@ use crate::messages::prelude::*;
pub enum OverlaysMessage {
Draw,
// Serde functionality isn't used but is required by the message system macros
AddProvider(
AddProvider {
#[serde(skip, default = "empty_provider")]
#[derivative(Debug = "ignore", PartialEq = "ignore")]
OverlayProvider,
),
RemoveProvider(
provider: OverlayProvider,
},
RemoveProvider {
#[serde(skip, default = "empty_provider")]
#[derivative(Debug = "ignore", PartialEq = "ignore")]
OverlayProvider,
),
provider: OverlayProvider,
},
}
@@ -56,12 +56,14 @@ impl MessageHandler<OverlaysMessage, OverlaysMessageContext<'_>> for OverlaysMes
let _ = canvas_context.reset_transform();
if visibility_settings.all() {
responses.add(DocumentMessage::GridOverlays(OverlayContext {
render_context: canvas_context.clone(),
size: size.as_dvec2(),
device_pixel_ratio,
visibility_settings: visibility_settings.clone(),
}));
responses.add(DocumentMessage::GridOverlays {
context: OverlayContext {
render_context: canvas_context.clone(),
size: size.as_dvec2(),
device_pixel_ratio,
visibility_settings: visibility_settings.clone(),
},
});
for provider in &self.overlay_providers {
responses.add(provider(OverlayContext {
render_context: canvas_context.clone(),
@@ -81,22 +83,22 @@ impl MessageHandler<OverlaysMessage, OverlaysMessageContext<'_>> for OverlaysMes
let overlay_context = OverlayContext::new(size, device_pixel_ratio, visibility_settings);
if visibility_settings.all() {
responses.add(DocumentMessage::GridOverlays(overlay_context.clone()));
responses.add(DocumentMessage::GridOverlays { context: overlay_context.clone() });
for provider in &self.overlay_providers {
responses.add(provider(overlay_context.clone()));
}
}
responses.add(FrontendMessage::RenderOverlays(overlay_context));
responses.add(FrontendMessage::RenderOverlays { context: overlay_context });
}
#[cfg(all(not(target_family = "wasm"), test))]
OverlaysMessage::Draw => {
let _ = (responses, visibility_settings, ipp, device_pixel_ratio);
}
OverlaysMessage::AddProvider(message) => {
OverlaysMessage::AddProvider { provider: message } => {
self.overlay_providers.insert(message);
}
OverlaysMessage::RemoveProvider(message) => {
OverlaysMessage::RemoveProvider { provider: message } => {
self.overlay_providers.remove(&message);
}
}