Fix many regressions introduced mostly in #1946 (#1986)

* Fix text tool

* Implement buffering to fix freehand tool

* Fix tools

* Fix clippy lints

* Small fixes

* Move vector modify back to Monitor nodes

* Code review

* Fix abort

* Fix svg import

---------

Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
adamgerhant
2024-09-26 15:37:03 -07:00
committed by GitHub
co-authored by Keavon Chambers
parent c738b4a1f9
commit 20470b566b
18 changed files with 152 additions and 91 deletions
+31 -1
View File
@@ -6,6 +6,7 @@ use graphene_core::text::Font;
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct Dispatcher { pub struct Dispatcher {
buffered_queue: Option<Vec<VecDeque<Message>>>,
message_queues: Vec<VecDeque<Message>>, message_queues: Vec<VecDeque<Message>>,
pub responses: Vec<FrontendMessage>, pub responses: Vec<FrontendMessage>,
pub message_handlers: DispatcherMessageHandlers, pub message_handlers: DispatcherMessageHandlers,
@@ -65,8 +66,18 @@ impl Dispatcher {
pub fn handle_message<T: Into<Message>>(&mut self, message: T) { pub fn handle_message<T: Into<Message>>(&mut self, message: T) {
self.message_queues.push(VecDeque::from_iter([message.into()])); self.message_queues.push(VecDeque::from_iter([message.into()]));
while let Some(message) = self.message_queues.last_mut().and_then(VecDeque::pop_front) { while let Some(message) = self.message_queues.last_mut().and_then(VecDeque::pop_front) {
// Do not buffer the EndBuffer message
if !matches!(message, Message::EndBuffer(_)) {
if let Some(buffered_queue) = &mut self.buffered_queue {
// Store each message in a deque so that its children are added before future messages
let mut message_deque = VecDeque::new();
message_deque.push_back(message);
buffered_queue.push(message_deque);
continue;
}
}
// Skip processing of this message if it will be processed later (at the end of the shallowest level queue) // Skip processing of this message if it will be processed later (at the end of the shallowest level queue)
if SIDE_EFFECT_FREE_MESSAGES.contains(&message.to_discriminant()) { if SIDE_EFFECT_FREE_MESSAGES.contains(&message.to_discriminant()) {
let already_in_queue = self.message_queues.first().filter(|queue| queue.contains(&message)).is_some(); let already_in_queue = self.message_queues.first().filter(|queue| queue.contains(&message)).is_some();
@@ -90,6 +101,25 @@ impl Dispatcher {
// Process the action by forwarding it to the relevant message handler, or saving the FrontendMessage to be sent to the frontend // Process the action by forwarding it to the relevant message handler, or saving the FrontendMessage to be sent to the frontend
match message { match message {
Message::StartBuffer => {
self.buffered_queue = Some(std::mem::take(&mut self.message_queues));
}
Message::EndBuffer(render_metadata) => {
// The buffered vec is added before the metadata messages, because the end of the vec is processed first
if let Some(buffered_queue) = self.buffered_queue.take() {
self.message_queues.extend(buffered_queue);
};
let graphene_std::renderer::RenderMetadata { footprints, click_targets } = render_metadata;
let mut update_upstream_transform = VecDeque::new();
update_upstream_transform.push_back(DocumentMessage::UpdateUpstreamTransforms { upstream_transforms: footprints }.into());
self.message_queues.push(update_upstream_transform);
let mut update_click_targets = VecDeque::new();
update_click_targets.push_back(DocumentMessage::UpdateClickTargets { click_targets }.into());
self.message_queues.push(update_click_targets);
}
Message::NoOp => {} Message::NoOp => {}
Message::Init => { Message::Init => {
// Load persistent data from the browser database // Load persistent data from the browser database
@@ -30,11 +30,12 @@ impl MessageHandler<NewDocumentDialogMessage, ()> for NewDocumentDialogMessageHa
id: NodeId(generate_uuid()), id: NodeId(generate_uuid()),
artboard: graphene_core::Artboard::new(IVec2::ZERO, self.dimensions.as_ivec2()), artboard: graphene_core::Artboard::new(IVec2::ZERO, self.dimensions.as_ivec2()),
}); });
responses.add(FrontendMessage::TriggerDelayedZoomCanvasToFitAll);
} }
responses.add(NodeGraphMessage::RunDocumentGraph); responses.add(NodeGraphMessage::RunDocumentGraph);
responses.add(NodeGraphMessage::UpdateNewNodeGraph); responses.add(NodeGraphMessage::UpdateNewNodeGraph);
responses.add(Message::StartBuffer);
responses.add(FrontendMessage::TriggerDelayedZoomCanvasToFitAll);
} }
} }
+2
View File
@@ -8,6 +8,8 @@ pub enum Message {
NoOp, NoOp,
Init, Init,
Batched(Box<[Message]>), Batched(Box<[Message]>),
StartBuffer,
EndBuffer(graphene_std::renderer::RenderMetadata),
#[child] #[child]
Broadcast(BroadcastMessage), Broadcast(BroadcastMessage),
@@ -1028,7 +1028,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
return; return;
} }
self.document_undo_history.pop_back(); self.undo(ipp, responses);
self.network_interface.finish_transaction(); self.network_interface.finish_transaction();
responses.add(OverlaysMessage::Draw); responses.add(OverlaysMessage::Draw);
} }
@@ -67,10 +67,8 @@ impl MessageHandler<GraphOperationMessage, GraphOperationMessageData<'_>> for Gr
transform_in, transform_in,
skip_rerender, skip_rerender,
} => { } => {
let parent_transform = network_interface.document_metadata().downstream_transform_to_viewport(layer);
let current_transform = Some(network_interface.document_metadata().transform_to_viewport(layer));
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer, network_interface, responses) { if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer, network_interface, responses) {
modify_inputs.transform_set(transform, transform_in, parent_transform, current_transform, skip_rerender); modify_inputs.transform_set(transform, transform_in, skip_rerender);
} }
} }
GraphOperationMessage::TransformSetPivot { layer, pivot } => { GraphOperationMessage::TransformSetPivot { layer, pivot } => {
@@ -191,6 +189,7 @@ impl MessageHandler<GraphOperationMessage, GraphOperationMessageData<'_>> for Gr
let layer = modify_inputs.create_layer(id); let layer = modify_inputs.create_layer(id);
modify_inputs.insert_text(text, font, size, layer); modify_inputs.insert_text(text, font, size, layer);
network_interface.move_layer_to_stack(layer, parent, insert_index, &[]); network_interface.move_layer_to_stack(layer, parent, insert_index, &[]);
responses.add(GraphOperationMessage::StrokeSet { layer, stroke: Stroke::default() });
responses.add(NodeGraphMessage::RunDocumentGraph); responses.add(NodeGraphMessage::RunDocumentGraph);
} }
GraphOperationMessage::ResizeArtboard { layer, location, dimensions } => { GraphOperationMessage::ResizeArtboard { layer, location, dimensions } => {
@@ -15,6 +15,7 @@ use graphene_core::vector::{PointId, VectorModificationType};
use graphene_core::{Artboard, Color}; use graphene_core::{Artboard, Color};
use glam::{DAffine2, DVec2, IVec2}; use glam::{DAffine2, DVec2, IVec2};
use graphene_std::vector::VectorData;
#[derive(PartialEq, Clone, Copy, Debug, serde::Serialize, serde::Deserialize)] #[derive(PartialEq, Clone, Copy, Debug, serde::Serialize, serde::Deserialize)]
pub enum TransformIn { pub enum TransformIn {
@@ -145,9 +146,12 @@ impl<'a> ModifyInputsContext<'a> {
} }
pub fn insert_vector_data(&mut self, subpaths: Vec<Subpath<PointId>>, layer: LayerNodeIdentifier) { pub fn insert_vector_data(&mut self, subpaths: Vec<Subpath<PointId>>, layer: LayerNodeIdentifier) {
let vector_data = VectorData::from_subpaths(subpaths, true);
let path = resolve_document_node_type("Path") let path = resolve_document_node_type("Path")
.expect("Path node does not exist") .expect("Path node does not exist")
.node_template_input_override([Some(NodeInput::value(TaggedValue::Subpaths(subpaths), false))]); .node_template_input_override([Some(NodeInput::value(TaggedValue::VectorData(vector_data), false))]);
let transform = resolve_document_node_type("Transform").expect("Transform node does not exist").default_node_template(); let transform = resolve_document_node_type("Transform").expect("Transform node does not exist").default_node_template();
let fill = resolve_document_node_type("Fill").expect("Fill node does not exist").default_node_template(); let fill = resolve_document_node_type("Fill").expect("Fill node does not exist").default_node_template();
let stroke = resolve_document_node_type("Stroke").expect("Stroke node does not exist").default_node_template(); let stroke = resolve_document_node_type("Stroke").expect("Stroke node does not exist").default_node_template();
@@ -195,8 +199,6 @@ impl<'a> ModifyInputsContext<'a> {
let stroke_id = NodeId(generate_uuid()); let stroke_id = NodeId(generate_uuid());
self.network_interface.insert_node(stroke_id, stroke, &[]); self.network_interface.insert_node(stroke_id, stroke, &[]);
self.network_interface.move_node_to_chain_start(&stroke_id, layer, &[]); self.network_interface.move_node_to_chain_start(&stroke_id, layer, &[]);
self.responses.add(NodeGraphMessage::RunDocumentGraph);
} }
pub fn insert_image_data(&mut self, image_frame: ImageFrame<Color>, layer: LayerNodeIdentifier) { pub fn insert_image_data(&mut self, image_frame: ImageFrame<Color>, layer: LayerNodeIdentifier) {
@@ -334,22 +336,15 @@ impl<'a> ModifyInputsContext<'a> {
} }
} }
pub fn transform_set(&mut self, mut transform: DAffine2, transform_in: TransformIn, parent_transform: DAffine2, current_transform: Option<DAffine2>, skip_rerender: bool) { pub fn transform_set(&mut self, transform: DAffine2, transform_in: TransformIn, skip_rerender: bool) {
let Some(transform_node_id) = self.existing_node_id("Transform") else { return }; let final_transform = match transform_in {
let upstream_transform = self.network_interface.document_metadata().upstream_transform(transform_node_id); TransformIn::Local => DAffine2::IDENTITY * transform,
let to = match transform_in { TransformIn::Scope { scope } => scope * transform,
TransformIn::Local => DAffine2::IDENTITY, TransformIn::Viewport => self.network_interface.document_metadata().downstream_transform_to_viewport(self.layer_node.unwrap()).inverse() * transform,
TransformIn::Scope { scope } => scope * parent_transform,
TransformIn::Viewport => parent_transform,
}; };
if current_transform let Some(transform_node_id) = self.existing_node_id("Transform") else { return };
.filter(|transform| transform.matrix2.determinant() != 0. && upstream_transform.matrix2.determinant() != 0.)
.is_some()
{
transform *= upstream_transform.inverse();
}
let final_transform = to.inverse() * transform;
transform_utils::update_transform(self.network_interface, &transform_node_id, final_transform); transform_utils::update_transform(self.network_interface, &transform_node_id, final_transform);
self.responses.add(PropertiesPanelMessage::Refresh); self.responses.add(PropertiesPanelMessage::Refresh);
@@ -196,6 +196,19 @@ impl ArtboardToolData {
location: position.round().as_ivec2(), location: position.round().as_ivec2(),
dimensions: size.round().as_ivec2(), dimensions: size.round().as_ivec2(),
}); });
// TODO: Resize artboard children when resizing left/top edges so that they stay in the same viewport space
// let old_top_left = bounds.bounds[0].round().as_ivec2();
// let new_top_left = position.round().as_ivec2();
// let top_left_delta = new_top_left - old_top_left;
// if top_left_delta != IVec2::ZERO {
// responses.add(GraphOperationMessage::TransformChange {
// layer: self.selected_artboard.unwrap(),
// transform: DAffine2::from_translation((-top_left_delta).into()),
// transform_in: TransformIn::Local,
// skip_rerender: false,
// });
// }
} }
} }
@@ -206,6 +206,7 @@ impl Fsm for EllipseToolFsmState {
let nodes = vec![(NodeId(0), node)]; let nodes = vec![(NodeId(0), node)];
let layer = graph_modification_utils::new_custom(NodeId(generate_uuid()), nodes, document.new_layer_parent(true), responses); let layer = graph_modification_utils::new_custom(NodeId(generate_uuid()), nodes, document.new_layer_parent(true), responses);
responses.add(Message::StartBuffer);
responses.add(GraphOperationMessage::TransformSet { responses.add(GraphOperationMessage::TransformSet {
layer, layer,
transform: DAffine2::from_scale_angle_translation(DVec2::ONE, 0., input.mouse.position), transform: DAffine2::from_scale_angle_translation(DVec2::ONE, 0., input.mouse.position),
@@ -229,15 +229,11 @@ impl Fsm for FreehandToolFsmState {
let nodes = vec![(NodeId(0), node)]; let nodes = vec![(NodeId(0), node)];
let layer = graph_modification_utils::new_custom(NodeId(generate_uuid()), nodes, parent, responses); let layer = graph_modification_utils::new_custom(NodeId(generate_uuid()), nodes, parent, responses);
responses.add(Message::StartBuffer);
tool_options.fill.apply_fill(layer, responses); tool_options.fill.apply_fill(layer, responses);
tool_options.stroke.apply_stroke(tool_data.weight, layer, responses); tool_options.stroke.apply_stroke(tool_data.weight, layer, responses);
tool_data.layer = Some(layer); tool_data.layer = Some(layer);
let transform = document.metadata().transform_to_viewport(parent);
let position = transform.inverse().transform_point2(input.mouse.position);
extend_path_with_next_segment(tool_data, position, responses);
FreehandToolFsmState::Drawing FreehandToolFsmState::Drawing
} }
(FreehandToolFsmState::Drawing, FreehandToolMessage::PointerMove) => { (FreehandToolFsmState::Drawing, FreehandToolMessage::PointerMove) => {
@@ -188,6 +188,7 @@ impl Fsm for LineToolFsmState {
let nodes = vec![(NodeId(0), node)]; let nodes = vec![(NodeId(0), node)];
let layer = graph_modification_utils::new_custom(NodeId(generate_uuid()), nodes, document.new_layer_parent(false), responses); let layer = graph_modification_utils::new_custom(NodeId(generate_uuid()), nodes, document.new_layer_parent(false), responses);
responses.add(Message::StartBuffer);
responses.add(GraphOperationMessage::TransformSet { responses.add(GraphOperationMessage::TransformSet {
layer, layer,
transform: DAffine2::from_scale_angle_translation(DVec2::ONE, 0., input.mouse.position), transform: DAffine2::from_scale_angle_translation(DVec2::ONE, 0., input.mouse.position),
@@ -525,21 +525,11 @@ impl Fsm for PenToolFsmState {
tool_data.layer = Some(layer); tool_data.layer = Some(layer);
tool_data.next_point = position; tool_data.next_point = position;
tool_data.next_handle_start = position; tool_data.next_handle_start = position;
} else { } else if let Some(layer) = tool_data.layer {
// New path layer // Add the first point to a new layer
let node_type = resolve_document_node_type("Path").expect("Path node does not exist");
let nodes = vec![(NodeId(0), node_type.default_node_template())];
let parent = document.new_layer_parent(true);
let layer = graph_modification_utils::new_custom(NodeId(generate_uuid()), nodes, parent, responses);
tool_options.fill.apply_fill(layer, responses);
tool_options.stroke.apply_stroke(tool_options.line_weight, layer, responses);
tool_data.layer = Some(layer);
// Generate first point // Generate first point
let id = PointId::generate(); let id = PointId::generate();
let transform = document.metadata().transform_to_document(parent); let pos = document.metadata().transform_to_viewport(layer).inverse().transform_point2(viewport);
let pos = transform.inverse().transform_point2(snapped.snapped_point_document);
let modification_type = VectorModificationType::InsertPoint { id, position: pos }; let modification_type = VectorModificationType::InsertPoint { id, position: pos };
responses.add(GraphOperationMessage::Vector { layer, modification_type }); responses.add(GraphOperationMessage::Vector { layer, modification_type });
tool_data.add_point(LastPoint { tool_data.add_point(LastPoint {
@@ -550,9 +540,21 @@ impl Fsm for PenToolFsmState {
}); });
tool_data.next_point = pos; tool_data.next_point = pos;
tool_data.next_handle_start = pos; tool_data.next_handle_start = pos;
} else {
// New path layer
let node_type = resolve_document_node_type("Path").expect("Path node does not exist");
let nodes = vec![(NodeId(0), node_type.default_node_template())];
let parent = document.new_layer_parent(true);
let layer = graph_modification_utils::new_custom(NodeId(generate_uuid()), nodes, parent, responses);
tool_options.fill.apply_fill(layer, responses);
tool_options.stroke.apply_stroke(tool_options.line_weight, layer, responses);
tool_data.layer = Some(layer);
responses.add(Message::StartBuffer);
responses.add(PenToolMessage::DragStart);
return PenToolFsmState::Ready;
} }
tool_data.handle_end = None; tool_data.handle_end = None;
// Enter the dragging handle state while the mouse is held down, allowing the user to move the mouse and position the handle // Enter the dragging handle state while the mouse is held down, allowing the user to move the mouse and position the handle
PenToolFsmState::DraggingHandle PenToolFsmState::DraggingHandle
} }
@@ -265,6 +265,7 @@ impl Fsm for PolygonToolFsmState {
let nodes = vec![(NodeId(0), node)]; let nodes = vec![(NodeId(0), node)];
let layer = graph_modification_utils::new_custom(NodeId(generate_uuid()), nodes, document.new_layer_parent(false), responses); let layer = graph_modification_utils::new_custom(NodeId(generate_uuid()), nodes, document.new_layer_parent(false), responses);
responses.add(Message::StartBuffer);
responses.add(GraphOperationMessage::TransformSet { responses.add(GraphOperationMessage::TransformSet {
layer, layer,
transform: DAffine2::from_scale_angle_translation(DVec2::ONE, 0., input.mouse.position), transform: DAffine2::from_scale_angle_translation(DVec2::ONE, 0., input.mouse.position),
@@ -212,6 +212,7 @@ impl Fsm for RectangleToolFsmState {
let nodes = vec![(NodeId(0), node)]; let nodes = vec![(NodeId(0), node)];
let layer = graph_modification_utils::new_custom(NodeId(generate_uuid()), nodes, document.new_layer_parent(true), responses); let layer = graph_modification_utils::new_custom(NodeId(generate_uuid()), nodes, document.new_layer_parent(true), responses);
responses.add(Message::StartBuffer);
responses.add(GraphOperationMessage::TransformSet { responses.add(GraphOperationMessage::TransformSet {
layer, layer,
transform: DAffine2::from_scale_angle_translation(DVec2::ONE, 0., input.mouse.position), transform: DAffine2::from_scale_angle_translation(DVec2::ONE, 0., input.mouse.position),
@@ -205,14 +205,6 @@ impl Fsm for SplineToolFsmState {
responses.add(DocumentMessage::DeselectAllLayers); responses.add(DocumentMessage::DeselectAllLayers);
let parent = document.new_layer_parent(true); let parent = document.new_layer_parent(true);
let transform = document.metadata().transform_to_viewport(parent);
let snapped_position = input.mouse.position;
let pos = transform.inverse().transform_point2(snapped_position);
tool_data.points.push(pos);
tool_data.next_point = pos;
tool_data.weight = tool_options.line_weight; tool_data.weight = tool_options.line_weight;
@@ -225,6 +217,8 @@ impl Fsm for SplineToolFsmState {
tool_options.stroke.apply_stroke(tool_data.weight, layer, responses); tool_options.stroke.apply_stroke(tool_data.weight, layer, responses);
tool_data.layer = Some(layer); tool_data.layer = Some(layer);
responses.add(Message::StartBuffer);
SplineToolFsmState::Drawing SplineToolFsmState::Drawing
} }
(SplineToolFsmState::Drawing, SplineToolMessage::DragStop) => { (SplineToolFsmState::Drawing, SplineToolMessage::DragStop) => {
@@ -237,11 +231,9 @@ impl Fsm for SplineToolFsmState {
let transform = document.metadata().transform_to_viewport(layer); let transform = document.metadata().transform_to_viewport(layer);
let pos = transform.inverse().transform_point2(snapped_position); let pos = transform.inverse().transform_point2(snapped_position);
if let Some(last_pos) = tool_data.points.last() { if tool_data.points.last().map_or(true, |last_pos| last_pos.distance(pos) > DRAG_THRESHOLD) {
if last_pos.distance(pos) > DRAG_THRESHOLD { tool_data.points.push(pos);
tool_data.points.push(pos); tool_data.next_point = pos;
tool_data.next_point = pos;
}
} }
update_spline(document, tool_data, true, responses); update_spline(document, tool_data, true, responses);
@@ -213,11 +213,7 @@ struct TextToolData {
impl TextToolData { impl TextToolData {
/// Set the editing state of the currently modifying layer /// Set the editing state of the currently modifying layer
fn set_editing(&self, editable: bool, font_cache: &FontCache, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) { fn set_editing(&self, editable: bool, font_cache: &FontCache, responses: &mut VecDeque<Message>) {
if let Some(node_id) = graph_modification_utils::get_fill_id(self.layer, &document.network_interface) {
responses.add(NodeGraphMessage::SetVisibility { node_id, visible: !editable });
}
if let Some(editing_text) = self.editing_text.as_ref().filter(|_| editable) { if let Some(editing_text) = self.editing_text.as_ref().filter(|_| editable) {
responses.add(FrontendMessage::DisplayEditableTextbox { responses.add(FrontendMessage::DisplayEditableTextbox {
text: editing_text.text.clone(), text: editing_text.text.clone(),
@@ -229,6 +225,8 @@ impl TextToolData {
}); });
} else { } else {
responses.add(FrontendMessage::DisplayRemoveEditableTextbox); responses.add(FrontendMessage::DisplayRemoveEditableTextbox);
// Clear all selected nodes when no longer editing
responses.add(NodeGraphMessage::SelectedNodesSet { nodes: Vec::new() });
} }
} }
@@ -253,17 +251,23 @@ impl TextToolData {
} }
if tool_state == TextToolFsmState::Editing { if tool_state == TextToolFsmState::Editing {
self.set_editing(false, font_cache, document, responses); self.set_editing(false, font_cache, responses);
} }
self.layer = layer; self.layer = layer;
self.load_layer_text_node(document); if self.load_layer_text_node(document).is_some() {
responses.add(DocumentMessage::AddTransaction);
responses.add(DocumentMessage::AddTransaction); self.set_editing(true, font_cache, responses);
self.set_editing(true, font_cache, document, responses); responses.add(NodeGraphMessage::SelectedNodesSet { nodes: vec![self.layer.to_node()] });
// Make the rendered text invisible while editing
responses.add(NodeGraphMessage::SelectedNodesSet { nodes: vec![self.layer.to_node()] }); responses.add(NodeGraphMessage::SetInput {
input_connector: InputConnector::node(graph_modification_utils::get_text_id(self.layer, &document.network_interface).unwrap(), 1),
input: NodeInput::value(TaggedValue::String("".to_string()), false),
});
responses.add(NodeGraphMessage::RunDocumentGraph);
};
} }
fn interact( fn interact(
@@ -294,6 +298,7 @@ impl TextToolData {
parent: document.new_layer_parent(true), parent: document.new_layer_parent(true),
insert_index: 0, insert_index: 0,
}); });
responses.add(Message::StartBuffer);
responses.add(GraphOperationMessage::FillSet { responses.add(GraphOperationMessage::FillSet {
layer: self.layer, layer: self.layer,
fill: if editing_text.color.is_some() { Fill::Solid(editing_text.color.unwrap()) } else { Fill::None }, fill: if editing_text.color.is_some() { Fill::Solid(editing_text.color.unwrap()) } else { Fill::None },
@@ -305,14 +310,15 @@ impl TextToolData {
skip_rerender: true, skip_rerender: true,
}); });
self.set_editing(true, font_cache, document, responses); self.set_editing(true, font_cache, responses);
responses.add(NodeGraphMessage::SelectedNodesSet { nodes: vec![self.layer.to_node()] }); responses.add(NodeGraphMessage::SelectedNodesSet { nodes: vec![self.layer.to_node()] });
responses.add(NodeGraphMessage::RunDocumentGraph);
TextToolFsmState::Editing TextToolFsmState::Editing
} else { } else {
// Removing old text as editable // Removing old text as editable
self.set_editing(false, font_cache, document, responses); self.set_editing(false, font_cache, responses);
TextToolFsmState::Ready TextToolFsmState::Ready
} }
@@ -404,7 +410,7 @@ impl Fsm for TextToolFsmState {
} }
(state, TextToolMessage::Abort) => { (state, TextToolMessage::Abort) => {
if state == TextToolFsmState::Editing { if state == TextToolFsmState::Editing {
tool_data.set_editing(false, font_cache, document, responses); tool_data.set_editing(false, font_cache, responses);
} }
TextToolFsmState::Ready TextToolFsmState::Ready
@@ -415,12 +421,13 @@ impl Fsm for TextToolFsmState {
TextToolFsmState::Editing TextToolFsmState::Editing
} }
(TextToolFsmState::Editing, TextToolMessage::TextChange { new_text }) => { (TextToolFsmState::Editing, TextToolMessage::TextChange { new_text }) => {
tool_data.set_editing(false, font_cache, responses);
responses.add(NodeGraphMessage::SetInput { responses.add(NodeGraphMessage::SetInput {
input_connector: InputConnector::node(graph_modification_utils::get_text_id(tool_data.layer, &document.network_interface).unwrap(), 1), input_connector: InputConnector::node(graph_modification_utils::get_text_id(tool_data.layer, &document.network_interface).unwrap(), 1),
input: NodeInput::value(TaggedValue::String(new_text), false), input: NodeInput::value(TaggedValue::String(new_text), false),
}); });
responses.add(NodeGraphMessage::RunDocumentGraph);
tool_data.set_editing(false, font_cache, document, responses);
TextToolFsmState::Ready TextToolFsmState::Ready
} }
+21 -10
View File
@@ -15,7 +15,8 @@ use graphene_core::renderer::{RenderSvgSegmentList, SvgSegment};
use graphene_core::text::FontCache; use graphene_core::text::FontCache;
use graphene_core::transform::Footprint; use graphene_core::transform::Footprint;
use graphene_core::vector::style::ViewMode; use graphene_core::vector::style::ViewMode;
use graphene_std::renderer::{format_transform_matrix, RenderMetadata}; use graphene_std::renderer::format_transform_matrix;
use graphene_std::vector::VectorData;
use graphene_std::wasm_application_io::{WasmApplicationIo, WasmEditorApi}; use graphene_std::wasm_application_io::{WasmApplicationIo, WasmEditorApi};
use interpreted_executor::dynamic_executor::{DynamicExecutor, IntrospectError, ResolvedDocumentNodeTypesDelta}; use interpreted_executor::dynamic_executor::{DynamicExecutor, IntrospectError, ResolvedDocumentNodeTypesDelta};
@@ -44,6 +45,7 @@ pub struct NodeRuntime {
// TODO: Remove, it doesn't need to be persisted anymore // TODO: Remove, it doesn't need to be persisted anymore
/// The current renders of the thumbnails for layer nodes. /// The current renders of the thumbnails for layer nodes.
thumbnail_renders: HashMap<NodeId, Vec<SvgSegment>>, thumbnail_renders: HashMap<NodeId, Vec<SvgSegment>>,
vector_modify: HashMap<NodeId, VectorData>,
} }
/// Messages passed from the editor thread to the node runtime thread. /// Messages passed from the editor thread to the node runtime thread.
@@ -74,6 +76,7 @@ pub struct ExecutionResponse {
result: Result<TaggedValue, String>, result: Result<TaggedValue, String>,
responses: VecDeque<FrontendMessage>, responses: VecDeque<FrontendMessage>,
transform: DAffine2, transform: DAffine2,
vector_modify: HashMap<NodeId, VectorData>,
} }
pub struct CompilationResponse { pub struct CompilationResponse {
@@ -131,6 +134,7 @@ impl NodeRuntime {
monitor_nodes: Vec::new(), monitor_nodes: Vec::new(),
thumbnail_renders: Default::default(), thumbnail_renders: Default::default(),
vector_modify: Default::default(),
} }
} }
@@ -203,6 +207,7 @@ impl NodeRuntime {
let result = self.execute_network(render_config).await; let result = self.execute_network(render_config).await;
let mut responses = VecDeque::new(); let mut responses = VecDeque::new();
// TODO: Only process monitor nodes if the graph has changed, not when only the Footprint changes
self.process_monitor_nodes(&mut responses, self.update_thumbnails); self.process_monitor_nodes(&mut responses, self.update_thumbnails);
self.update_thumbnails = false; self.update_thumbnails = false;
@@ -211,6 +216,7 @@ impl NodeRuntime {
result, result,
responses, responses,
transform, transform,
vector_modify: self.vector_modify.clone(),
}); });
} }
} }
@@ -282,8 +288,18 @@ impl NodeRuntime {
if let Some(io) = introspected_data.downcast_ref::<IORecord<Footprint, graphene_core::GraphicElement>>() { if let Some(io) = introspected_data.downcast_ref::<IORecord<Footprint, graphene_core::GraphicElement>>() {
Self::process_graphic_element(&mut self.thumbnail_renders, parent_network_node_id, &io.output, responses, update_thumbnails) Self::process_graphic_element(&mut self.thumbnail_renders, parent_network_node_id, &io.output, responses, update_thumbnails)
} else if let Some(io) = introspected_data.downcast_ref::<IORecord<(), graphene_core::GraphicElement>>() {
Self::process_graphic_element(&mut self.thumbnail_renders, parent_network_node_id, &io.output, responses, update_thumbnails)
} else if let Some(io) = introspected_data.downcast_ref::<IORecord<Footprint, graphene_core::Artboard>>() { } else if let Some(io) = introspected_data.downcast_ref::<IORecord<Footprint, graphene_core::Artboard>>() {
Self::process_graphic_element(&mut self.thumbnail_renders, parent_network_node_id, &io.output, responses, update_thumbnails) Self::process_graphic_element(&mut self.thumbnail_renders, parent_network_node_id, &io.output, responses, update_thumbnails)
} else if let Some(io) = introspected_data.downcast_ref::<IORecord<(), graphene_core::Artboard>>() {
Self::process_graphic_element(&mut self.thumbnail_renders, parent_network_node_id, &io.output, responses, update_thumbnails)
}
// Insert the vector modify if we are dealing with vector data
else if let Some(record) = introspected_data.downcast_ref::<IORecord<Footprint, VectorData>>() {
self.vector_modify.insert(parent_network_node_id, record.output.clone());
} else if let Some(record) = introspected_data.downcast_ref::<IORecord<(), VectorData>>() {
self.vector_modify.insert(parent_network_node_id, record.output.clone());
} }
} }
} }
@@ -535,6 +551,7 @@ impl NodeGraphExecutor {
result, result,
responses: existing_responses, responses: existing_responses,
transform, transform,
vector_modify,
} = execution_response; } = execution_response;
responses.add(OverlaysMessage::Draw); responses.add(OverlaysMessage::Draw);
@@ -550,6 +567,7 @@ impl NodeGraphExecutor {
}; };
responses.extend(existing_responses.into_iter().map(Into::into)); responses.extend(existing_responses.into_iter().map(Into::into));
document.network_interface.update_vector_modify(vector_modify);
let execution_context = self.futures.remove(&execution_id).ok_or_else(|| "Invalid generation ID".to_string())?; let execution_context = self.futures.remove(&execution_id).ok_or_else(|| "Invalid generation ID".to_string())?;
if let Some(export_config) = execution_context.export_config { if let Some(export_config) = execution_context.export_config {
@@ -614,12 +632,6 @@ impl NodeGraphExecutor {
fn process_node_graph_output(&mut self, node_graph_output: TaggedValue, transform: DAffine2, responses: &mut VecDeque<Message>) -> Result<(), String> { fn process_node_graph_output(&mut self, node_graph_output: TaggedValue, transform: DAffine2, responses: &mut VecDeque<Message>) -> Result<(), String> {
match node_graph_output { match node_graph_output {
TaggedValue::RenderOutput(render_output) => { TaggedValue::RenderOutput(render_output) => {
let RenderMetadata {
footprints,
click_targets,
vector_data,
} = render_output.metadata;
match render_output.data { match render_output.data {
graphene_std::wasm_application_io::RenderOutputType::Svg(svg) => { graphene_std::wasm_application_io::RenderOutputType::Svg(svg) => {
// Send to frontend // Send to frontend
@@ -638,9 +650,8 @@ impl NodeGraphExecutor {
return Err(format!("Invalid node graph output type: {:#?}", render_output.data)); return Err(format!("Invalid node graph output type: {:#?}", render_output.data));
} }
} }
responses.add(DocumentMessage::UpdateUpstreamTransforms { upstream_transforms: footprints });
responses.add(DocumentMessage::UpdateClickTargets { click_targets }); responses.add(Message::EndBuffer(render_output.metadata));
responses.add(DocumentMessage::UpdateVectorModify { vector_modify: vector_data });
responses.add(DocumentMessage::RenderScrollbars); responses.add(DocumentMessage::RenderScrollbars);
responses.add(DocumentMessage::RenderRulers); responses.add(DocumentMessage::RenderRulers);
responses.add(OverlaysMessage::Draw); responses.add(OverlaysMessage::Draw);
@@ -270,12 +270,13 @@ pub fn to_transform(transform: DAffine2) -> usvg::Transform {
usvg::Transform::from_row(cols[0] as f32, cols[1] as f32, cols[2] as f32, cols[3] as f32, cols[4] as f32, cols[5] as f32) usvg::Transform::from_row(cols[0] as f32, cols[1] as f32, cols[2] as f32, cols[3] as f32, cols[4] as f32, cols[5] as f32)
} }
// TODO: Click targets can be removed from the render output, since the vector data is available in the vector modify data from Monitor nodes.
// This will require that the transform for child layers into that layer space be calculated, or it could be returned from the RenderOutput instead of click targets.
#[derive(Debug, Clone, PartialEq, DynAny)] #[derive(Debug, Clone, PartialEq, DynAny)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct RenderMetadata { pub struct RenderMetadata {
pub footprints: HashMap<NodeId, (Footprint, DAffine2)>, pub footprints: HashMap<NodeId, (Footprint, DAffine2)>,
pub click_targets: HashMap<NodeId, Vec<ClickTarget>>, pub click_targets: HashMap<NodeId, Vec<ClickTarget>>,
pub vector_data: HashMap<NodeId, VectorData>,
} }
pub trait GraphicElementRendered { pub trait GraphicElementRendered {
@@ -454,10 +455,14 @@ impl GraphicElementRendered for VectorData {
} }
subpath subpath
}; };
metadata
.click_targets let click_targets = self
.insert(element_id, self.stroke_bezier_paths().map(fill).map(|subpath| ClickTarget::new(subpath, stroke_width)).collect()); .stroke_bezier_paths()
metadata.vector_data.insert(element_id, self.clone()); .map(fill)
.map(|subpath| ClickTarget::new(subpath, stroke_width))
.collect::<Vec<ClickTarget>>();
metadata.click_targets.insert(element_id, click_targets);
} }
if let Some(upstream_graphic_group) = &self.upstream_graphic_group { if let Some(upstream_graphic_group) = &self.upstream_graphic_group {
@@ -606,14 +611,19 @@ impl GraphicElementRendered for Artboard {
"g", "g",
// Group tag attributes // Group tag attributes
|attributes| { |attributes| {
let matrix = format_transform_matrix(self.transform());
if !matrix.is_empty() {
attributes.push("transform", matrix);
}
if self.clip { if self.clip {
let id = format!("artboard-{}", generate_uuid()); let id = format!("artboard-{}", generate_uuid());
let selector = format!("url(#{id})"); let selector = format!("url(#{id})");
write!( write!(
&mut attributes.0.svg_defs, &mut attributes.0.svg_defs,
r##"<clipPath id="{id}"><rect x="0" y="0" width="{}" height="{}" /></clipPath>"##, r##"<clipPath id="{id}"><rect x="0" y="0" width="{}" height="{}"/></clipPath>"##,
self.dimensions.x, self.dimensions.y self.dimensions.x, self.dimensions.y,
) )
.unwrap(); .unwrap();
attributes.push("clip-path", selector); attributes.push("clip-path", selector);
@@ -635,13 +645,13 @@ impl GraphicElementRendered for Artboard {
} }
} }
fn collect_metadata(&self, metadata: &mut RenderMetadata, footprint: Footprint, element_id: Option<NodeId>) { fn collect_metadata(&self, metadata: &mut RenderMetadata, mut footprint: Footprint, element_id: Option<NodeId>) {
if let Some(element_id) = element_id { if let Some(element_id) = element_id {
let subpath = Subpath::new_rect(DVec2::ZERO, self.dimensions.as_dvec2()); let subpath = Subpath::new_rect(DVec2::ZERO, self.dimensions.as_dvec2());
metadata.click_targets.insert(element_id, vec![ClickTarget::new(subpath, 0.)]); metadata.click_targets.insert(element_id, vec![ClickTarget::new(subpath, 0.)]);
metadata.footprints.insert(element_id, (footprint, DAffine2::from_translation(self.location.as_dvec2()))); metadata.footprints.insert(element_id, (footprint, DAffine2::from_translation(self.location.as_dvec2())));
} }
footprint.transform *= self.transform();
self.graphic_group.collect_metadata(metadata, footprint, None); self.graphic_group.collect_metadata(metadata, footprint, None);
} }
@@ -225,7 +225,6 @@ async fn render<'a: 'n, T: 'n + GraphicElementRendered + WasmNotSend>(
let mut metadata = RenderMetadata { let mut metadata = RenderMetadata {
footprints: HashMap::new(), footprints: HashMap::new(),
click_targets: HashMap::new(), click_targets: HashMap::new(),
vector_data: HashMap::new(),
}; };
data.collect_metadata(&mut metadata, footprint, None); data.collect_metadata(&mut metadata, footprint, None);