mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-19 02:48:12 +08:00
Artboard nodes (#1328)
* Create artboard nodes * Update node when resizing artboard * Render clipped artboards * More stable feature * Do not render old artboards * Fix some issues with transforms * Fix crash when drawing rectangle * Format * Allow renaming document from Properties panel * Adjust artboard label styling * Fix document graph refresh so artboards show up * Make "Clear Artboards" coming soon * Fix displaying an infinite canvas * Show document name in node graph options bar * info!() to debug!() * Fix Properties panel not being cleared when all docs closed * Remove dead code * Remove debug logs added in this branch --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
committed by
Keavon Chambers
parent
ad0dc3276e
commit
08f9be6aaf
@@ -5,7 +5,9 @@ use crate::messages::layout::utility_types::widgets::input_widgets::{CheckboxInp
|
||||
use crate::messages::layout::utility_types::widgets::label_widgets::{Separator, SeparatorDirection, SeparatorType, TextLabel};
|
||||
use crate::messages::prelude::*;
|
||||
|
||||
use glam::UVec2;
|
||||
use graphene_core::uuid::generate_uuid;
|
||||
|
||||
use glam::{IVec2, UVec2};
|
||||
|
||||
/// A dialog to allow users to set some initial options about a new document.
|
||||
#[derive(Debug, Clone, Default)]
|
||||
@@ -27,13 +29,20 @@ impl MessageHandler<NewDocumentDialogMessage, ()> for NewDocumentDialogMessageHa
|
||||
responses.add(PortfolioMessage::NewDocumentWithName { name: self.name.clone() });
|
||||
|
||||
if !self.infinite && self.dimensions.x > 0 && self.dimensions.y > 0 {
|
||||
let id = generate_uuid();
|
||||
responses.add(ArtboardMessage::AddArtboard {
|
||||
id: None,
|
||||
id: Some(id),
|
||||
position: (0., 0.),
|
||||
size: (self.dimensions.x as f64, self.dimensions.y as f64),
|
||||
});
|
||||
responses.add(GraphOperationMessage::NewArtboard {
|
||||
id,
|
||||
artboard: graphene_core::Artboard::new(IVec2::ZERO, self.dimensions.as_ivec2()),
|
||||
});
|
||||
responses.add(DocumentMessage::ZoomCanvasToFitAll);
|
||||
}
|
||||
responses.add(NodeGraphMessage::RunDocumentGraph);
|
||||
responses.add(NodeGraphMessage::UpdateNewNodeGraph);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -150,6 +150,9 @@ pub enum FrontendMessage {
|
||||
layout_target: LayoutTarget,
|
||||
diff: Vec<WidgetDiff>,
|
||||
},
|
||||
UpdateDocumentNodeRender {
|
||||
svg: String,
|
||||
},
|
||||
UpdateDocumentOverlays {
|
||||
svg: String,
|
||||
},
|
||||
@@ -163,6 +166,9 @@ pub enum FrontendMessage {
|
||||
size: (f64, f64),
|
||||
multiplier: (f64, f64),
|
||||
},
|
||||
UpdateDocumentTransform {
|
||||
transform: String,
|
||||
},
|
||||
UpdateEyedropperSamplingState {
|
||||
#[serde(rename = "mousePosition")]
|
||||
mouse_position: Option<(f64, f64)>,
|
||||
|
||||
@@ -32,17 +32,7 @@ impl MessageHandler<InputPreprocessorMessage, KeyboardPlatformLayout> for InputP
|
||||
// TODO: Extend this to multiple viewports instead of setting it to the value of this last loop iteration
|
||||
self.viewport_bounds = bounds;
|
||||
|
||||
responses.add(Operation::TransformLayer {
|
||||
path: vec![],
|
||||
transform: glam::DAffine2::from_translation(translation).to_cols_array(),
|
||||
});
|
||||
responses.add(DocumentMessage::Artboard(
|
||||
Operation::TransformLayer {
|
||||
path: vec![],
|
||||
transform: glam::DAffine2::from_translation(translation).to_cols_array(),
|
||||
}
|
||||
.into(),
|
||||
));
|
||||
responses.add(NavigationMessage::TranslateCanvas { delta: DVec2::ZERO });
|
||||
responses.add(FrontendMessage::TriggerViewportResize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,9 +61,11 @@ impl MessageHandler<ArtboardMessage, &PersistentData> for ArtboardMessageHandler
|
||||
responses.add(DocumentMessage::RenderDocument);
|
||||
}
|
||||
ClearArtboards => {
|
||||
for &artboard in self.artboard_ids.iter() {
|
||||
responses.add_front(ArtboardMessage::DeleteArtboard { artboard });
|
||||
}
|
||||
// TODO: Make this remove the artboard layers from the graph (and cleanly reconnect the artwork)
|
||||
responses.add(DialogMessage::RequestComingSoonDialog { issue: None });
|
||||
// for &artboard in self.artboard_ids.iter() {
|
||||
// responses.add_front(ArtboardMessage::DeleteArtboard { artboard });
|
||||
// }
|
||||
}
|
||||
DeleteArtboard { artboard } => {
|
||||
self.artboard_ids.retain(|&id| id != artboard);
|
||||
@@ -78,11 +80,13 @@ impl MessageHandler<ArtboardMessage, &PersistentData> for ArtboardMessageHandler
|
||||
responses.add(FrontendMessage::UpdateDocumentArtboards {
|
||||
svg: r##"<rect width="100%" height="100%" fill="#ffffff" />"##.to_string(),
|
||||
})
|
||||
} else {
|
||||
let render_data = RenderData::new(&persistent_data.font_cache, ViewMode::Normal, None);
|
||||
responses.add(FrontendMessage::UpdateDocumentArtboards {
|
||||
svg: self.artboards_document.render_root(&render_data),
|
||||
});
|
||||
// TODO: Delete this whole legacy code path when cleaning up/removing the old (non-node based) artboard implementation
|
||||
// TODO: The below code was used to draw the non-node based artboards, but we still need the above code to draw the infinite canvas until the refactor is complete and all this code can be removed
|
||||
// } else {
|
||||
// let render_data = RenderData::new(&persistent_data.font_cache, ViewMode::Normal, None);
|
||||
// responses.add(FrontendMessage::UpdateDocumentArtboards {
|
||||
// svg: self.artboards_document.render_root(&render_data),
|
||||
// });
|
||||
}
|
||||
}
|
||||
ResizeArtboard { artboard, position, mut size } => {
|
||||
|
||||
@@ -124,6 +124,9 @@ pub enum DocumentMessage {
|
||||
mouse: Option<(f64, f64)>,
|
||||
},
|
||||
Redo,
|
||||
RenameDocument {
|
||||
new_name: String,
|
||||
},
|
||||
RenameLayer {
|
||||
layer_path: Vec<LayerId>,
|
||||
new_name: String,
|
||||
|
||||
@@ -197,6 +197,7 @@ impl MessageHandler<DocumentMessage, (u64, &InputPreprocessorMessageHandler, &Pe
|
||||
#[remain::unsorted]
|
||||
PropertiesPanel(message) => {
|
||||
let properties_panel_message_handler_data = PropertiesPanelMessageHandlerData {
|
||||
document_name: &self.name.as_str(),
|
||||
artwork_document: &self.document_legacy,
|
||||
artboard_document: &self.artboard_message_handler.artboards_document,
|
||||
selected_layers: &mut self.layer_metadata.iter().filter_map(|(path, data)| data.selected.then_some(path.as_slice())),
|
||||
@@ -208,7 +209,8 @@ impl MessageHandler<DocumentMessage, (u64, &InputPreprocessorMessageHandler, &Pe
|
||||
}
|
||||
#[remain::unsorted]
|
||||
NodeGraph(message) => {
|
||||
self.node_graph_handler.process_message(message, responses, (&mut self.document_legacy, executor, document_id));
|
||||
self.node_graph_handler
|
||||
.process_message(message, responses, (&mut self.document_legacy, executor, document_id, self.name.as_str()));
|
||||
}
|
||||
#[remain::unsorted]
|
||||
GraphOperation(message) => GraphOperationMessageHandler.process_message(message, responses, (&mut self.document_legacy, &mut self.node_graph_handler)),
|
||||
@@ -659,6 +661,11 @@ impl MessageHandler<DocumentMessage, (u64, &InputPreprocessorMessageHandler, &Pe
|
||||
responses.add(RenderDocument);
|
||||
responses.add(FolderChanged { affected_folder_path: vec![] });
|
||||
}
|
||||
RenameDocument { new_name } => {
|
||||
self.name = new_name;
|
||||
responses.add(PortfolioMessage::UpdateOpenDocumentsList);
|
||||
responses.add(NodeGraphMessage::UpdateNewNodeGraph);
|
||||
}
|
||||
RenameLayer { layer_path, new_name } => responses.add(DocumentOperation::RenameLayer { layer_path, new_name }),
|
||||
RenderDocument => {
|
||||
responses.add(FrontendMessage::UpdateDocumentArtwork {
|
||||
@@ -1096,6 +1103,7 @@ impl DocumentMessageHandler {
|
||||
let starting_root_transform = document.navigation_handler.calculate_offset_transform(ipp.viewport_bounds.size() / 2.);
|
||||
document.document_legacy.root.transform = starting_root_transform;
|
||||
document.artboard_message_handler.artboards_document.root.transform = starting_root_transform;
|
||||
|
||||
document
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo};
|
||||
|
||||
use document_legacy::document::Document;
|
||||
use document_legacy::Operation as DocumentOperation;
|
||||
use graphene_core::renderer::format_transform_matrix;
|
||||
|
||||
use glam::{DAffine2, DVec2};
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -352,6 +353,9 @@ impl NavigationMessageHandler {
|
||||
}
|
||||
.into(),
|
||||
));
|
||||
let transform = format_transform_matrix(self.calculate_offset_transform(scaled_half_viewport));
|
||||
responses.add(FrontendMessage::UpdateDocumentTransform { transform });
|
||||
// TODO: Artboard pos
|
||||
}
|
||||
|
||||
pub fn center_zoom(&self, viewport_bounds: DVec2, zoom_factor: f64, mouse: DVec2) -> Message {
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
use crate::messages::prelude::*;
|
||||
|
||||
use graph_craft::document::NodeId;
|
||||
use graphene_core::uuid::ManipulatorGroupId;
|
||||
use graphene_core::vector::brush_stroke::BrushStroke;
|
||||
use graphene_core::vector::style::{Fill, Stroke};
|
||||
use graphene_core::vector::ManipulatorPointId;
|
||||
use graphene_core::Artboard;
|
||||
|
||||
use glam::{DAffine2, DVec2};
|
||||
use glam::{DAffine2, DVec2, IVec2};
|
||||
|
||||
pub type LayerIdentifier = Vec<document_legacy::LayerId>;
|
||||
|
||||
@@ -51,6 +53,20 @@ pub enum GraphOperationMessage {
|
||||
layer: LayerIdentifier,
|
||||
strokes: Vec<BrushStroke>,
|
||||
},
|
||||
|
||||
NewArtboard {
|
||||
id: NodeId,
|
||||
artboard: Artboard,
|
||||
},
|
||||
ResizeArtboard {
|
||||
id: NodeId,
|
||||
location: IVec2,
|
||||
dimensions: IVec2,
|
||||
},
|
||||
DeleteArtboard {
|
||||
id: NodeId,
|
||||
},
|
||||
ClearArtboards,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Copy, Debug, serde::Serialize, serde::Deserialize)]
|
||||
|
||||
@@ -4,12 +4,13 @@ use crate::messages::prelude::*;
|
||||
use document_legacy::document::Document;
|
||||
use document_legacy::{LayerId, Operation};
|
||||
use graph_craft::document::value::TaggedValue;
|
||||
use graph_craft::document::{generate_uuid, NodeId, NodeInput, NodeNetwork};
|
||||
use graph_craft::document::{generate_uuid, DocumentNode, DocumentNodeMetadata, NodeId, NodeInput, NodeNetwork, NodeOutput};
|
||||
use graphene_core::vector::brush_stroke::BrushStroke;
|
||||
use graphene_core::vector::style::{Fill, FillType, Stroke};
|
||||
use graphene_core::Artboard;
|
||||
use transform_utils::LayerBounds;
|
||||
|
||||
use glam::{DAffine2, DVec2};
|
||||
use glam::{DAffine2, DVec2, IVec2};
|
||||
|
||||
pub mod transform_utils;
|
||||
|
||||
@@ -21,27 +22,152 @@ struct ModifyInputsContext<'a> {
|
||||
node_graph: &'a mut NodeGraphMessageHandler,
|
||||
responses: &'a mut VecDeque<Message>,
|
||||
layer: &'a [LayerId],
|
||||
outwards_links: HashMap<NodeId, Vec<NodeId>>,
|
||||
layer_node: Option<NodeId>,
|
||||
}
|
||||
impl<'a> ModifyInputsContext<'a> {
|
||||
/// Get the node network from the document
|
||||
fn new(layer: &'a [LayerId], document: &'a mut Document, node_graph: &'a mut NodeGraphMessageHandler, responses: &'a mut VecDeque<Message>) -> Option<Self> {
|
||||
document.layer_mut(layer).ok().and_then(|layer| layer.as_layer_network_mut().ok()).map(|network| Self {
|
||||
outwards_links: network.collect_outwards_links(),
|
||||
network,
|
||||
node_graph,
|
||||
responses,
|
||||
layer,
|
||||
layer_node: None,
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the node network from the document
|
||||
fn new_doc(document: &'a mut Document, node_graph: &'a mut NodeGraphMessageHandler, responses: &'a mut VecDeque<Message>) -> Self {
|
||||
Self {
|
||||
outwards_links: document.document_network.collect_outwards_links(),
|
||||
network: &mut document.document_network,
|
||||
node_graph,
|
||||
responses,
|
||||
layer: &[],
|
||||
layer_node: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn locate_layer(&mut self, mut id: NodeId) -> Option<NodeId> {
|
||||
while self.network.nodes.get(&id)?.name != "Layer" {
|
||||
id = self.outwards_links.get(&id)?.first().copied()?;
|
||||
}
|
||||
self.layer_node = Some(id);
|
||||
Some(id)
|
||||
}
|
||||
|
||||
/// Updates the input of an existing node
|
||||
fn modify_existing_node_inputs(&mut self, node_id: NodeId, update_input: impl FnOnce(&mut Vec<NodeInput>)) {
|
||||
let document_node = self.network.nodes.get_mut(&node_id).unwrap();
|
||||
update_input(&mut document_node.inputs);
|
||||
}
|
||||
|
||||
pub fn insert_between(&mut self, pre: NodeOutput, post: NodeOutput, mut node: DocumentNode, input: usize, output: usize) -> Option<NodeId> {
|
||||
let id = generate_uuid();
|
||||
let pre_node = self.network.nodes.get_mut(&pre.node_id)?;
|
||||
node.metadata.position = pre_node.metadata.position;
|
||||
|
||||
let post_node = self.network.nodes.get_mut(&post.node_id)?;
|
||||
node.inputs[input] = NodeInput::node(pre.node_id, pre.node_output_index);
|
||||
post_node.inputs[post.node_output_index] = NodeInput::node(id, output);
|
||||
|
||||
self.network.nodes.insert(id, node);
|
||||
|
||||
self.shift_upstream(id, IVec2::new(-8, 0));
|
||||
|
||||
Some(id)
|
||||
}
|
||||
|
||||
pub fn insert_layer_below(&mut self, node_id: NodeId, input_index: usize) -> Option<NodeId> {
|
||||
let layer_node = resolve_document_node_type("Layer").expect("Layer node");
|
||||
|
||||
let new_id = generate_uuid();
|
||||
let post_node = self.network.nodes.get_mut(&node_id)?;
|
||||
post_node.inputs[input_index] = NodeInput::node(new_id, 0);
|
||||
let document_node = layer_node.to_document_node_default_inputs([], DocumentNodeMetadata::position(post_node.metadata.position + IVec2::new(0, 2)));
|
||||
|
||||
self.network.nodes.insert(new_id, document_node);
|
||||
|
||||
Some(new_id)
|
||||
}
|
||||
|
||||
pub fn insert_node_before(&mut self, new_id: NodeId, node_id: NodeId, input_index: usize, mut document_node: DocumentNode, offset: IVec2) -> Option<NodeId> {
|
||||
let post_node = self.network.nodes.get_mut(&node_id)?;
|
||||
|
||||
post_node.inputs[input_index] = NodeInput::node(new_id, 0);
|
||||
document_node.metadata.position = post_node.metadata.position + offset;
|
||||
self.network.nodes.insert(new_id, document_node);
|
||||
|
||||
Some(new_id)
|
||||
}
|
||||
|
||||
pub fn create_layer(&mut self, new_id: NodeId, output_node_id: NodeId) -> Option<NodeId> {
|
||||
let mut current_node = output_node_id;
|
||||
let mut input_index = 0;
|
||||
let mut current_input = &self.network.nodes.get(¤t_node)?.inputs[input_index];
|
||||
|
||||
while let NodeInput::Node { node_id, output_index, .. } = current_input {
|
||||
let mut sibling_node = &self.network.nodes.get(node_id)?;
|
||||
if sibling_node.name == "Layer" {
|
||||
current_node = *node_id;
|
||||
input_index = 7;
|
||||
current_input = &self.network.nodes.get(¤t_node)?.inputs[input_index];
|
||||
} else {
|
||||
// Insert a layer node between the output and the new
|
||||
let layer_node = resolve_document_node_type("Layer").expect("Layer node");
|
||||
let node = layer_node.to_document_node_default_inputs([], DocumentNodeMetadata::default());
|
||||
let node_id = self.insert_between(NodeOutput::new(*node_id, *output_index), NodeOutput::new(current_node, input_index), node, 0, 0)?;
|
||||
current_node = node_id;
|
||||
input_index = 7;
|
||||
current_input = &self.network.nodes.get(¤t_node)?.inputs[input_index];
|
||||
}
|
||||
}
|
||||
|
||||
let layer_node = resolve_document_node_type("Layer").expect("Node").to_document_node_default_inputs([], Default::default());
|
||||
let layer_node = self.insert_node_before(new_id, current_node, input_index, layer_node, IVec2::new(0, 3))?;
|
||||
|
||||
Some(layer_node)
|
||||
}
|
||||
|
||||
fn insert_artboard(&mut self, artboard: Artboard, layer: NodeId) -> Option<NodeId> {
|
||||
let artboard_node = resolve_document_node_type("Artboard").expect("Node").to_document_node_default_inputs(
|
||||
[
|
||||
None,
|
||||
Some(NodeInput::value(TaggedValue::IVec2(artboard.location), false)),
|
||||
Some(NodeInput::value(TaggedValue::IVec2(artboard.dimensions), false)),
|
||||
Some(NodeInput::value(TaggedValue::Color(artboard.background), false)),
|
||||
Some(NodeInput::value(TaggedValue::Bool(artboard.clip), false)),
|
||||
],
|
||||
Default::default(),
|
||||
);
|
||||
self.insert_node_before(generate_uuid(), layer, 0, artboard_node, IVec2::new(-8, 0))
|
||||
}
|
||||
|
||||
fn shift_upstream(&mut self, node_id: NodeId, shift: IVec2) {
|
||||
let mut shift_nodes = HashSet::new();
|
||||
let mut stack = vec![node_id];
|
||||
while let Some(node) = stack.pop() {
|
||||
let Some(node) = self.network.nodes.get(&node_id) else { continue };
|
||||
for input in &node.inputs {
|
||||
let NodeInput::Node { node_id, .. } = input else { continue };
|
||||
if shift_nodes.insert(*node_id) {
|
||||
stack.push(*node_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for node_id in shift_nodes {
|
||||
if let Some(node) = self.network.nodes.get_mut(&node_id) {
|
||||
node.metadata.position += shift;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Inserts a new node and modifies the inputs
|
||||
fn modify_new_node(&mut self, name: &'static str, update_input: impl FnOnce(&mut Vec<NodeInput>)) {
|
||||
let output_node_id = self.network.outputs[0].node_id;
|
||||
let output_node_id = self.layer_node.unwrap_or(self.network.outputs[0].node_id);
|
||||
let Some(output_node) = self.network.nodes.get_mut(&output_node_id) else {
|
||||
warn!("Output node doesn't exist");
|
||||
return;
|
||||
@@ -65,7 +191,7 @@ impl<'a> ModifyInputsContext<'a> {
|
||||
|
||||
/// Changes the inputs of a specific node
|
||||
fn modify_inputs(&mut self, name: &'static str, skip_rerender: bool, update_input: impl FnOnce(&mut Vec<NodeInput>)) {
|
||||
let existing_node_id = self.network.primary_flow().find(|(node, _)| node.name == name).map(|(_, id)| id);
|
||||
let existing_node_id = self.network.primary_flow_from_opt(self.layer_node).find(|(node, _)| node.name == name).map(|(_, id)| id);
|
||||
if let Some(node_id) = existing_node_id {
|
||||
self.modify_existing_node_inputs(node_id, update_input);
|
||||
} else {
|
||||
@@ -229,6 +355,36 @@ impl<'a> ModifyInputsContext<'a> {
|
||||
inputs[2] = NodeInput::value(TaggedValue::BrushStrokes(strokes), false);
|
||||
});
|
||||
}
|
||||
|
||||
fn resize_artboard(&mut self, location: IVec2, dimensions: IVec2) {
|
||||
self.modify_inputs("Artboard", false, |inputs| {
|
||||
inputs[1] = NodeInput::value(TaggedValue::IVec2(location), false);
|
||||
inputs[2] = NodeInput::value(TaggedValue::IVec2(dimensions), false);
|
||||
});
|
||||
}
|
||||
|
||||
fn delete_layer(&mut self, id: NodeId) {
|
||||
let mut new_input = None;
|
||||
let post_node = self.outwards_links.get(&id).and_then(|links| links.first().copied());
|
||||
let mut delete_nodes = vec![id];
|
||||
for (node, id) in self.network.primary_flow_from_opt(Some(id)) {
|
||||
delete_nodes.push(id);
|
||||
if node.name == "Artboard" {
|
||||
new_input = Some(node.inputs[0].clone());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for node_id in delete_nodes {
|
||||
self.network.nodes.remove(&node_id);
|
||||
}
|
||||
|
||||
if let (Some(new_input), Some(post_node)) = (new_input, post_node) {
|
||||
if let Some(node) = self.network.nodes.get_mut(&post_node) {
|
||||
node.inputs[0] = new_input;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MessageHandler<GraphOperationMessage, (&mut Document, &mut NodeGraphMessageHandler)> for GraphOperationMessageHandler {
|
||||
@@ -316,6 +472,31 @@ impl MessageHandler<GraphOperationMessage, (&mut Document, &mut NodeGraphMessage
|
||||
modify_inputs.brush_modify(strokes);
|
||||
}
|
||||
}
|
||||
GraphOperationMessage::NewArtboard { id, artboard } => {
|
||||
let mut modify_inputs = ModifyInputsContext::new_doc(document, node_graph, responses);
|
||||
if let Some(layer) = modify_inputs.create_layer(id, modify_inputs.network.outputs[0].node_id) {
|
||||
modify_inputs.insert_artboard(artboard, layer);
|
||||
}
|
||||
|
||||
//modify_inputs.brush_modify(strokes);
|
||||
}
|
||||
GraphOperationMessage::ResizeArtboard { id, location, dimensions } => {
|
||||
let mut modify_inputs = ModifyInputsContext::new_doc(document, node_graph, responses);
|
||||
if let Some(layer) = modify_inputs.locate_layer(id) {
|
||||
modify_inputs.resize_artboard(location, dimensions);
|
||||
}
|
||||
}
|
||||
GraphOperationMessage::DeleteArtboard { id } => {
|
||||
let mut modify_inputs = ModifyInputsContext::new_doc(document, node_graph, responses);
|
||||
modify_inputs.delete_layer(id);
|
||||
}
|
||||
GraphOperationMessage::ClearArtboards => {
|
||||
let mut modify_inputs = ModifyInputsContext::new_doc(document, node_graph, responses);
|
||||
let artboard_nodes = modify_inputs.network.nodes.iter().filter(|(_, node)| node.name == "Artboard").map(|(id, _)| *id).collect::<Vec<_>>();
|
||||
for id in artboard_nodes {
|
||||
modify_inputs.delete_layer(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -156,7 +156,7 @@ impl NodeGraphMessageHandler {
|
||||
self.get_root_network_mut(document).nested_network_mut(&self.nested_path)
|
||||
}
|
||||
|
||||
/// Send the cached layout for the bar at the top of the node panel to the frontend
|
||||
/// Send the cached layout to the frontend for the options bar at the top of the node panel
|
||||
fn send_node_bar_layout(&self, responses: &mut VecDeque<Message>) {
|
||||
responses.add(LayoutMessage::SendLayout {
|
||||
layout: Layout::WidgetLayout(WidgetLayout::new(self.widgets.to_vec())),
|
||||
@@ -165,34 +165,48 @@ impl NodeGraphMessageHandler {
|
||||
}
|
||||
|
||||
/// Collect the addresses of the currently viewed nested node e.g. Root -> MyFunFilter -> Exposure
|
||||
fn collect_nested_addresses(&mut self, document: &Document, responses: &mut VecDeque<Message>) {
|
||||
// // Build path list
|
||||
let mut path = vec![];
|
||||
if let Some(layer) = self.layer_path.as_ref().and_then(|path| document.layer(path).ok()) {
|
||||
path.push(format!("Layer: {}", layer.name.as_deref().unwrap_or("Untitled Layer")));
|
||||
} else {
|
||||
path.push("Document Network".to_string());
|
||||
}
|
||||
fn collect_nested_addresses(&mut self, document: &Document, document_name: &str, responses: &mut VecDeque<Message>) {
|
||||
let layer_if_selected = self.layer_path.as_ref().and_then(|path| document.layer(path).ok());
|
||||
|
||||
// Build path list for the layer, or otherwise the root document
|
||||
let path_root = match layer_if_selected {
|
||||
Some(layer) => layer.name.as_deref().unwrap_or("Untitled Layer"),
|
||||
None => document_name,
|
||||
};
|
||||
let mut path = vec![path_root.to_string()];
|
||||
|
||||
let (icon, tooltip) = match layer_if_selected {
|
||||
Some(_) => ("Layer", "Layer"),
|
||||
None => ("File", "Document"),
|
||||
};
|
||||
|
||||
let mut network = Some(self.get_root_network(document));
|
||||
for node_id in &self.nested_path {
|
||||
let node = network.and_then(|network| network.nodes.get(node_id));
|
||||
|
||||
if let Some(DocumentNode { name, .. }) = node {
|
||||
path.push(name.clone());
|
||||
}
|
||||
|
||||
network = node.and_then(|node| node.implementation.get_network());
|
||||
}
|
||||
|
||||
let nesting = path.len();
|
||||
|
||||
// Update UI
|
||||
self.widgets[0] = LayoutGroup::Row {
|
||||
widgets: vec![BreadcrumbTrailButtons::new(path.clone())
|
||||
.on_update(move |input: &u64| {
|
||||
NodeGraphMessage::ExitNestedNetwork {
|
||||
depth_of_nesting: nesting - (*input as usize) - 1,
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.widget_holder()],
|
||||
widgets: vec![
|
||||
IconLabel::new(icon).tooltip(tooltip).widget_holder(),
|
||||
WidgetHolder::unrelated_separator(),
|
||||
BreadcrumbTrailButtons::new(path.clone())
|
||||
.on_update(move |input: &u64| {
|
||||
NodeGraphMessage::ExitNestedNetwork {
|
||||
depth_of_nesting: nesting - (*input as usize) - 1,
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.widget_holder(),
|
||||
],
|
||||
};
|
||||
|
||||
self.send_node_bar_layout(responses);
|
||||
@@ -425,9 +439,9 @@ impl NodeGraphMessageHandler {
|
||||
}
|
||||
}
|
||||
|
||||
impl MessageHandler<NodeGraphMessage, (&mut Document, &NodeGraphExecutor, u64)> for NodeGraphMessageHandler {
|
||||
impl MessageHandler<NodeGraphMessage, (&mut Document, &NodeGraphExecutor, u64, &str)> for NodeGraphMessageHandler {
|
||||
#[remain::check]
|
||||
fn process_message(&mut self, message: NodeGraphMessage, responses: &mut VecDeque<Message>, (document, executor, document_id): (&mut Document, &NodeGraphExecutor, u64)) {
|
||||
fn process_message(&mut self, message: NodeGraphMessage, responses: &mut VecDeque<Message>, (document, executor, document_id, document_name): (&mut Document, &NodeGraphExecutor, u64, &str)) {
|
||||
#[remain::sorted]
|
||||
match message {
|
||||
NodeGraphMessage::CloseNodeGraph => {
|
||||
@@ -564,7 +578,7 @@ impl MessageHandler<NodeGraphMessage, (&mut Document, &NodeGraphExecutor, u64)>
|
||||
if let Some(network) = self.get_active_network(document) {
|
||||
Self::send_graph(network, executor, &self.layer_path, responses);
|
||||
}
|
||||
self.collect_nested_addresses(document, responses);
|
||||
self.collect_nested_addresses(document, document_name, responses);
|
||||
self.update_selected(document, responses);
|
||||
}
|
||||
NodeGraphMessage::DuplicateSelectedNodes => {
|
||||
@@ -600,7 +614,7 @@ impl MessageHandler<NodeGraphMessage, (&mut Document, &NodeGraphExecutor, u64)>
|
||||
if let Some(network) = self.get_active_network(document) {
|
||||
Self::send_graph(network, executor, &self.layer_path, responses);
|
||||
}
|
||||
self.collect_nested_addresses(document, responses);
|
||||
self.collect_nested_addresses(document, document_name, responses);
|
||||
self.update_selected(document, responses);
|
||||
}
|
||||
NodeGraphMessage::ExposeInput { node_id, input_index, new_exposed } => {
|
||||
@@ -662,7 +676,7 @@ impl MessageHandler<NodeGraphMessage, (&mut Document, &NodeGraphExecutor, u64)>
|
||||
let node_types = document_node_types::collect_node_types();
|
||||
responses.add(FrontendMessage::UpdateNodeTypes { node_types });
|
||||
}
|
||||
self.collect_nested_addresses(document, responses);
|
||||
self.collect_nested_addresses(document, document_name, responses);
|
||||
self.update_selected(document, responses);
|
||||
}
|
||||
NodeGraphMessage::PasteNodes { serialized_nodes } => {
|
||||
@@ -895,7 +909,7 @@ impl MessageHandler<NodeGraphMessage, (&mut Document, &NodeGraphExecutor, u64)>
|
||||
let node_types = document_node_types::collect_node_types();
|
||||
responses.add(FrontendMessage::UpdateNodeTypes { node_types });
|
||||
}
|
||||
self.collect_nested_addresses(document, responses);
|
||||
self.collect_nested_addresses(document, document_name, responses);
|
||||
self.update_selected(document, responses);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,12 +204,13 @@ fn static_nodes() -> Vec<DocumentNodeType> {
|
||||
DocumentNodeType {
|
||||
name: "Artboard",
|
||||
category: "General",
|
||||
identifier: NodeImplementation::proto("graphene_core::ConstructArtboardNode<_, _, _>"),
|
||||
identifier: NodeImplementation::proto("graphene_core::ConstructArtboardNode<_, _, _, _>"),
|
||||
inputs: vec![
|
||||
DocumentInputType::value("Graphic Group", TaggedValue::GraphicGroup(GraphicGroup::EMPTY), true),
|
||||
DocumentInputType::value("Location", TaggedValue::IVec2(glam::IVec2::ZERO), false),
|
||||
DocumentInputType::value("Dimensions", TaggedValue::IVec2(glam::IVec2::new(1920, 1080)), false),
|
||||
DocumentInputType::value("Background", TaggedValue::Color(Color::WHITE), false),
|
||||
DocumentInputType::value("Clip", TaggedValue::Bool(false), false),
|
||||
],
|
||||
outputs: vec![DocumentOutputType::new("Out", FrontendGraphDataType::Artboard)],
|
||||
properties: node_properties::artboard_properties,
|
||||
|
||||
@@ -1623,5 +1623,8 @@ pub fn artboard_properties(document_node: &DocumentNode, node_id: NodeId, _conte
|
||||
let location = vec2_widget(document_node, node_id, 1, "Location", "X", "Y", " px", add_blank_assist);
|
||||
let dimensions = vec2_widget(document_node, node_id, 2, "Dimensions", "W", "H", " px", add_blank_assist);
|
||||
let background = color_widget(document_node, node_id, 3, "Background", ColorInput::default().allow_none(false), true);
|
||||
vec![location, dimensions, background]
|
||||
let clip = LayoutGroup::Row {
|
||||
widgets: bool_widget(document_node, node_id, 4, "Clip", true),
|
||||
};
|
||||
vec![location, dimensions, background, clip]
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ impl<'a> MessageHandler<PropertiesPanelMessage, (&PersistentData, PropertiesPane
|
||||
use PropertiesPanelMessage::*;
|
||||
|
||||
let PropertiesPanelMessageHandlerData {
|
||||
document_name,
|
||||
artwork_document,
|
||||
artboard_document,
|
||||
selected_layers,
|
||||
@@ -63,6 +64,9 @@ impl<'a> MessageHandler<PropertiesPanelMessage, (&PersistentData, PropertiesPane
|
||||
}
|
||||
}
|
||||
ClearSelection => {
|
||||
// This causes the Properties panel to change, so this needs to happen before the following lines clear the Properties panel
|
||||
responses.add(NodeGraphMessage::CloseNodeGraph);
|
||||
|
||||
responses.add(LayoutMessage::SendLayout {
|
||||
layout: Layout::WidgetLayout(WidgetLayout::new(vec![])),
|
||||
layout_target: LayoutTarget::PropertiesOptions,
|
||||
@@ -71,7 +75,6 @@ impl<'a> MessageHandler<PropertiesPanelMessage, (&PersistentData, PropertiesPane
|
||||
layout: Layout::WidgetLayout(WidgetLayout::new(vec![])),
|
||||
layout_target: LayoutTarget::PropertiesSections,
|
||||
});
|
||||
responses.add(NodeGraphMessage::CloseNodeGraph);
|
||||
self.active_selection = None;
|
||||
}
|
||||
Deactivate => responses.add(BroadcastMessage::UnsubscribeEvent {
|
||||
@@ -151,7 +154,7 @@ impl<'a> MessageHandler<PropertiesPanelMessage, (&PersistentData, PropertiesPane
|
||||
executor,
|
||||
network: &artwork_document.document_network,
|
||||
};
|
||||
register_document_graph_properties(context, node_graph_message_handler);
|
||||
register_document_graph_properties(context, node_graph_message_handler, document_name);
|
||||
}
|
||||
}
|
||||
UpdateSelectedDocumentProperties => responses.add(PropertiesPanelMessage::SetActiveLayers {
|
||||
|
||||
@@ -78,19 +78,14 @@ pub fn register_artboard_layer_properties(layer: &Layer, responses: &mut VecDequ
|
||||
..Default::default()
|
||||
})),
|
||||
WidgetHolder::unrelated_separator(),
|
||||
WidgetHolder::new(Widget::TextLabel(TextLabel {
|
||||
value: "Artboard".into(),
|
||||
..TextLabel::default()
|
||||
})),
|
||||
WidgetHolder::unrelated_separator(),
|
||||
WidgetHolder::new(Widget::TextInput(TextInput {
|
||||
value: layer.name.clone().unwrap_or_else(|| "Untitled".to_string()),
|
||||
value: layer.name.clone().unwrap_or_else(|| "Untitled Artboard".to_string()),
|
||||
on_update: WidgetCallback::new(|text_input: &TextInput| PropertiesPanelMessage::ModifyName { name: text_input.value.clone() }.into()),
|
||||
..Default::default()
|
||||
})),
|
||||
WidgetHolder::related_separator(),
|
||||
WidgetHolder::new(Widget::PopoverButton(PopoverButton {
|
||||
header: "Options Bar".into(),
|
||||
header: "Additional Options".into(),
|
||||
text: "Coming soon".into(),
|
||||
..Default::default()
|
||||
})),
|
||||
@@ -261,22 +256,14 @@ pub fn register_artwork_layer_properties(
|
||||
})),
|
||||
},
|
||||
WidgetHolder::unrelated_separator(),
|
||||
WidgetHolder::new(Widget::TextLabel(TextLabel {
|
||||
value: match &layer.data {
|
||||
LayerDataType::Layer(_) => "Layer".into(),
|
||||
other => LayerDataTypeDiscriminant::from(other).to_string(),
|
||||
},
|
||||
..TextLabel::default()
|
||||
})),
|
||||
WidgetHolder::unrelated_separator(),
|
||||
WidgetHolder::new(Widget::TextInput(TextInput {
|
||||
value: layer.name.clone().unwrap_or_else(|| "Untitled".to_string()),
|
||||
value: layer.name.clone().unwrap_or_else(|| "Untitled Layer".to_string()),
|
||||
on_update: WidgetCallback::new(|text_input: &TextInput| PropertiesPanelMessage::ModifyName { name: text_input.value.clone() }.into()),
|
||||
..Default::default()
|
||||
})),
|
||||
WidgetHolder::related_separator(),
|
||||
WidgetHolder::new(Widget::PopoverButton(PopoverButton {
|
||||
header: "Options Bar".into(),
|
||||
header: "Additional Options".into(),
|
||||
text: "Coming soon".into(),
|
||||
..Default::default()
|
||||
})),
|
||||
@@ -326,18 +313,18 @@ pub fn register_artwork_layer_properties(
|
||||
});
|
||||
}
|
||||
|
||||
pub fn register_document_graph_properties(mut context: NodePropertiesContext, node_graph_message_handler: &NodeGraphMessageHandler) {
|
||||
pub fn register_document_graph_properties(mut context: NodePropertiesContext, node_graph_message_handler: &NodeGraphMessageHandler, document_name: &str) {
|
||||
let mut properties_sections = Vec::new();
|
||||
node_graph_message_handler.collate_properties(&mut context, &mut properties_sections);
|
||||
let options_bar = vec![LayoutGroup::Row {
|
||||
widgets: vec![
|
||||
IconLabel::new("File").widget_holder(),
|
||||
IconLabel::new("File").tooltip("Document").widget_holder(),
|
||||
WidgetHolder::unrelated_separator(),
|
||||
TextLabel::new("Document graph").widget_holder(),
|
||||
WidgetHolder::unrelated_separator(),
|
||||
TextInput::new("No layer selected").disabled(true).widget_holder(),
|
||||
TextInput::new(document_name)
|
||||
.on_update(|text_input| DocumentMessage::RenameDocument { new_name: text_input.value.clone() }.into())
|
||||
.widget_holder(),
|
||||
WidgetHolder::related_separator(),
|
||||
PopoverButton::new("Options Bar", "Coming soon").widget_holder(),
|
||||
PopoverButton::new("Additional Options", "Coming soon").widget_holder(),
|
||||
],
|
||||
}];
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ use serde::{Deserialize, Serialize};
|
||||
use crate::{messages::prelude::NodeGraphMessageHandler, node_graph_executor::NodeGraphExecutor};
|
||||
|
||||
pub struct PropertiesPanelMessageHandlerData<'a> {
|
||||
pub document_name: &'a str,
|
||||
pub artwork_document: &'a DocumentLegacy,
|
||||
pub artboard_document: &'a DocumentLegacy,
|
||||
pub selected_layers: &'a mut dyn Iterator<Item = &'a [LayerId]>,
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::utility_types::PersistentData;
|
||||
use crate::application::generate_uuid;
|
||||
use crate::consts::{DEFAULT_DOCUMENT_NAME, GRAPHITE_DOCUMENT_VERSION};
|
||||
@@ -19,6 +17,9 @@ use graph_craft::document::value::TaggedValue;
|
||||
use graph_craft::document::{NodeId, NodeInput};
|
||||
use graphene_core::text::Font;
|
||||
|
||||
use glam::DAffine2;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct PortfolioMessageHandler {
|
||||
menu_bar_message_handler: MenuBarMessageHandler,
|
||||
@@ -114,12 +115,13 @@ impl MessageHandler<PortfolioMessage, (&InputPreprocessorMessageHandler, &Prefer
|
||||
|
||||
// Actually delete the document (delay to delete document is required to let the document and properties panel messages above get processed)
|
||||
responses.add(PortfolioMessage::DeleteDocument { document_id });
|
||||
responses.add(FrontendMessage::TriggerIndexedDbRemoveDocument { document_id });
|
||||
|
||||
// Send the new list of document tab names
|
||||
responses.add(PortfolioMessage::UpdateOpenDocumentsList);
|
||||
responses.add(FrontendMessage::TriggerIndexedDbRemoveDocument { document_id });
|
||||
responses.add(DocumentMessage::RenderDocument);
|
||||
responses.add(DocumentMessage::DocumentStructureChanged);
|
||||
|
||||
if let Some(document) = self.active_document() {
|
||||
for layer in document.layer_metadata.keys() {
|
||||
responses.add(DocumentMessage::LayerChanged { affected_layer_path: layer.clone() });
|
||||
@@ -246,7 +248,7 @@ impl MessageHandler<PortfolioMessage, (&InputPreprocessorMessageHandler, &Prefer
|
||||
}
|
||||
PortfolioMessage::ImaginatePreferences => self.executor.update_imaginate_preferences(preferences.get_imaginate_preferences()),
|
||||
PortfolioMessage::ImaginateServerHostname => {
|
||||
info!("setting imaginate persistent data");
|
||||
debug!("setting imaginate persistent data");
|
||||
self.persistent_data.imaginate.set_host_name(&preferences.imaginate_server_hostname);
|
||||
}
|
||||
PortfolioMessage::Import => {
|
||||
@@ -466,6 +468,7 @@ impl MessageHandler<PortfolioMessage, (&InputPreprocessorMessageHandler, &Prefer
|
||||
responses.add(BroadcastEvent::DocumentIsDirty);
|
||||
responses.add(PortfolioMessage::UpdateDocumentWidgets);
|
||||
responses.add(NavigationMessage::TranslateCanvas { delta: (0., 0.).into() });
|
||||
responses.add(NodeGraphMessage::RunDocumentGraph);
|
||||
}
|
||||
PortfolioMessage::SetActiveDocument { document_id } => {
|
||||
self.active_document_id = Some(document_id);
|
||||
@@ -664,7 +667,8 @@ impl PortfolioMessageHandler {
|
||||
}
|
||||
|
||||
pub fn poll_node_graph_evaluation(&mut self, responses: &mut VecDeque<Message>) {
|
||||
self.executor.poll_node_graph_evaluation(responses).unwrap_or_else(|e| {
|
||||
let transform = self.active_document().map(|document| document.document_legacy.root.transform).unwrap_or(DAffine2::IDENTITY);
|
||||
self.executor.poll_node_graph_evaluation(transform, responses).unwrap_or_else(|e| {
|
||||
log::error!("Error while evaluating node graph: {}", e);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo};
|
||||
use document_legacy::intersection::Quad;
|
||||
use document_legacy::LayerId;
|
||||
|
||||
use glam::{DVec2, Vec2Swizzles};
|
||||
use glam::{DVec2, IVec2, Vec2Swizzles};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Default)]
|
||||
@@ -133,10 +133,6 @@ impl Fsm for ArtboardToolFsmState {
|
||||
tool_data.bounding_box_overlays = Some(bounding_box_overlays);
|
||||
|
||||
responses.add(OverlaysMessage::Rerender);
|
||||
responses.add(PropertiesPanelMessage::SetActiveLayers {
|
||||
paths: vec![vec![tool_data.selected_artboard.unwrap()]],
|
||||
document: TargetDocument::Artboard,
|
||||
});
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
@@ -196,11 +192,6 @@ impl Fsm for ArtboardToolFsmState {
|
||||
.start_snap(document, input, document.bounding_boxes(None, Some(intersection[0]), render_data), true, true);
|
||||
tool_data.snap_manager.add_all_document_handles(document, input, &[], &[], &[]);
|
||||
|
||||
responses.add(PropertiesPanelMessage::SetActiveLayers {
|
||||
paths: vec![intersection.clone()],
|
||||
document: TargetDocument::Artboard,
|
||||
});
|
||||
|
||||
ArtboardToolFsmState::Dragging
|
||||
} else {
|
||||
tool_data.selected_artboard = None;
|
||||
@@ -226,6 +217,11 @@ impl Fsm for ArtboardToolFsmState {
|
||||
position: position.round().into(),
|
||||
size: size.round().into(),
|
||||
});
|
||||
responses.add(GraphOperationMessage::ResizeArtboard {
|
||||
id: tool_data.selected_artboard.unwrap(),
|
||||
location: position.round().as_ivec2(),
|
||||
dimensions: size.round().as_ivec2(),
|
||||
});
|
||||
|
||||
responses.add(BroadcastEvent::DocumentIsDirty);
|
||||
}
|
||||
@@ -251,6 +247,11 @@ impl Fsm for ArtboardToolFsmState {
|
||||
position: position.round().into(),
|
||||
size: size.round().into(),
|
||||
});
|
||||
responses.add(GraphOperationMessage::ResizeArtboard {
|
||||
id: tool_data.selected_artboard.unwrap(),
|
||||
location: position.round().as_ivec2(),
|
||||
dimensions: size.round().as_ivec2(),
|
||||
});
|
||||
|
||||
responses.add(BroadcastEvent::DocumentIsDirty);
|
||||
|
||||
@@ -285,6 +286,11 @@ impl Fsm for ArtboardToolFsmState {
|
||||
position: start.round().into(),
|
||||
size: size.round().into(),
|
||||
});
|
||||
responses.add(GraphOperationMessage::ResizeArtboard {
|
||||
id: tool_data.selected_artboard.unwrap(),
|
||||
location: start.round().as_ivec2(),
|
||||
dimensions: size.round().as_ivec2(),
|
||||
});
|
||||
} else {
|
||||
let id = generate_uuid();
|
||||
tool_data.selected_artboard = Some(id);
|
||||
@@ -297,14 +303,20 @@ impl Fsm for ArtboardToolFsmState {
|
||||
position: start.round().into(),
|
||||
size: (1., 1.),
|
||||
});
|
||||
responses.add(GraphOperationMessage::NewArtboard {
|
||||
id,
|
||||
artboard: graphene_core::Artboard {
|
||||
graphic_group: graphene_core::GraphicGroup::EMPTY,
|
||||
location: start.round().as_ivec2(),
|
||||
dimensions: IVec2::splat(1),
|
||||
background: graphene_core::Color::WHITE,
|
||||
clip: false,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// Have to put message here instead of when Artboard is created
|
||||
// This might result in a few more calls but it is not reliant on the order of messages
|
||||
responses.add(PropertiesPanelMessage::SetActiveLayers {
|
||||
paths: vec![vec![tool_data.selected_artboard.unwrap()]],
|
||||
document: TargetDocument::Artboard,
|
||||
});
|
||||
|
||||
responses.add(BroadcastEvent::DocumentIsDirty);
|
||||
|
||||
@@ -352,6 +364,8 @@ impl Fsm for ArtboardToolFsmState {
|
||||
(_, ArtboardToolMessage::DeleteSelected) => {
|
||||
if let Some(artboard) = tool_data.selected_artboard.take() {
|
||||
responses.add(ArtboardMessage::DeleteArtboard { artboard });
|
||||
responses.add(GraphOperationMessage::DeleteArtboard { id: artboard });
|
||||
|
||||
responses.add(BroadcastEvent::DocumentIsDirty);
|
||||
}
|
||||
ArtboardToolFsmState::Ready
|
||||
@@ -363,6 +377,11 @@ impl Fsm for ArtboardToolFsmState {
|
||||
position: (bounds.bounds[0].x + delta_x, bounds.bounds[0].y + delta_y),
|
||||
size: (bounds.bounds[1] - bounds.bounds[0]).round().into(),
|
||||
});
|
||||
responses.add(GraphOperationMessage::ResizeArtboard {
|
||||
id: tool_data.selected_artboard.unwrap(),
|
||||
location: DVec2::new(bounds.bounds[0].x + delta_x, bounds.bounds[0].y + delta_y).round().as_ivec2(),
|
||||
dimensions: (bounds.bounds[1] - bounds.bounds[0]).round().as_ivec2(),
|
||||
});
|
||||
}
|
||||
|
||||
ArtboardToolFsmState::Ready
|
||||
|
||||
@@ -214,7 +214,7 @@ impl NodeRuntime {
|
||||
graphic_group.render_svg(&mut render, &render_params);
|
||||
let [min, max] = bounds.unwrap_or_default();
|
||||
render.format_svg(min, max);
|
||||
info!("SVG {}", render.svg);
|
||||
debug!("SVG {}", render.svg);
|
||||
|
||||
if let (Some(layer_id), Some(node_id)) = (layer_path.last().copied(), node_path.get(node_path.len() - 2).copied()) {
|
||||
let old_thumbnail = self.thumbnails.entry(layer_id).or_default().entry(node_id).or_default();
|
||||
@@ -283,16 +283,16 @@ struct ExecutionContext {
|
||||
|
||||
impl Default for NodeGraphExecutor {
|
||||
fn default() -> Self {
|
||||
let (request_sender, request_reciever) = std::sync::mpsc::channel();
|
||||
let (response_sender, response_reciever) = std::sync::mpsc::channel();
|
||||
let (request_sender, request_receiver) = std::sync::mpsc::channel();
|
||||
let (response_sender, response_receiver) = std::sync::mpsc::channel();
|
||||
NODE_RUNTIME.with(|runtime| {
|
||||
runtime.borrow_mut().replace(NodeRuntime::new(request_reciever, response_sender));
|
||||
runtime.borrow_mut().replace(NodeRuntime::new(request_receiver, response_sender));
|
||||
});
|
||||
|
||||
Self {
|
||||
futures: Default::default(),
|
||||
sender: request_sender,
|
||||
receiver: response_reciever,
|
||||
receiver: response_receiver,
|
||||
last_output_type: Default::default(),
|
||||
thumbnails: Default::default(),
|
||||
}
|
||||
@@ -423,7 +423,7 @@ impl NodeGraphExecutor {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn poll_node_graph_evaluation(&mut self, responses: &mut VecDeque<Message>) -> Result<(), String> {
|
||||
pub fn poll_node_graph_evaluation(&mut self, transform: DAffine2, responses: &mut VecDeque<Message>) -> Result<(), String> {
|
||||
let results = self.receiver.try_iter().collect::<Vec<_>>();
|
||||
for response in results {
|
||||
match response {
|
||||
@@ -437,7 +437,7 @@ impl NodeGraphExecutor {
|
||||
let node_graph_output = result.map_err(|e| format!("Node graph evaluation failed: {:?}", e))?;
|
||||
let execution_context = self.futures.remove(&generation_id).ok_or_else(|| "Invalid generation ID".to_string())?;
|
||||
responses.extend(updates);
|
||||
self.process_node_graph_output(node_graph_output, execution_context.layer_path.clone(), responses, execution_context.document_id)?;
|
||||
self.process_node_graph_output(node_graph_output, execution_context.layer_path.clone(), transform, responses, execution_context.document_id)?;
|
||||
responses.add(DocumentMessage::LayerChanged {
|
||||
affected_layer_path: execution_context.layer_path,
|
||||
});
|
||||
@@ -456,7 +456,7 @@ impl NodeGraphExecutor {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn process_node_graph_output(&mut self, node_graph_output: TaggedValue, layer_path: Vec<LayerId>, responses: &mut VecDeque<Message>, document_id: u64) -> Result<(), String> {
|
||||
fn process_node_graph_output(&mut self, node_graph_output: TaggedValue, layer_path: Vec<LayerId>, transform: DAffine2, responses: &mut VecDeque<Message>, document_id: u64) -> Result<(), String> {
|
||||
self.last_output_type.insert(layer_path.clone(), Some(node_graph_output.ty()));
|
||||
match node_graph_output {
|
||||
TaggedValue::VectorData(vector_data) => {
|
||||
@@ -489,11 +489,30 @@ impl NodeGraphExecutor {
|
||||
}
|
||||
}
|
||||
TaggedValue::Artboard(artboard) => {
|
||||
info!("{artboard:#?}");
|
||||
debug!("{artboard:#?}");
|
||||
return Err("Artboard (see console)".to_string());
|
||||
}
|
||||
TaggedValue::GraphicGroup(graphic_group) => {
|
||||
info!("{graphic_group:#?}");
|
||||
debug!("{graphic_group:#?}");
|
||||
use graphene_core::renderer::{format_transform_matrix, GraphicElementRendered, RenderParams, SvgRender};
|
||||
|
||||
// Setup rendering
|
||||
let mut render = SvgRender::new();
|
||||
let render_params = RenderParams::new(ViewMode::Normal, None, false);
|
||||
|
||||
// Render svg
|
||||
graphic_group.render_svg(&mut render, &render_params);
|
||||
|
||||
// Conctenate the defs and the svg into one string
|
||||
let mut svg = "<defs>".to_string();
|
||||
svg.push_str(&render.svg_defs);
|
||||
svg.push_str("</defs>");
|
||||
use std::fmt::Write;
|
||||
write!(svg, "{}", render.svg).unwrap();
|
||||
|
||||
// Send to frontend
|
||||
responses.add(FrontendMessage::UpdateDocumentNodeRender { svg });
|
||||
|
||||
return Err("Graphic group (see console)".to_string());
|
||||
}
|
||||
_ => {
|
||||
|
||||
Reference in New Issue
Block a user