mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-27 06:38:12 +08:00
Migrate the Text tool to the document graph (#1435)
* Update text tool to document graph * Fix selection issue * Log graph reruns and text node evals * Hash to set node * Fix let node crash * Fix loading document with fonts * Allow pressing enter to edit * Cleanup * Code review nits --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
co-authored by
Keavon Chambers
parent
3d4e3a74e5
commit
b8906f344e
@@ -1430,37 +1430,18 @@ impl DocumentMessageHandler {
|
|||||||
|
|
||||||
/// Loads layer resources such as creating the blob URLs for the images and loading all of the fonts in the document
|
/// Loads layer resources such as creating the blob URLs for the images and loading all of the fonts in the document
|
||||||
pub fn load_layer_resources(&self, responses: &mut VecDeque<Message>, root: &LayerDataType, mut path: Vec<LayerId>, _document_id: u64) {
|
pub fn load_layer_resources(&self, responses: &mut VecDeque<Message>, root: &LayerDataType, mut path: Vec<LayerId>, _document_id: u64) {
|
||||||
fn walk_layers(data: &LayerDataType, path: &mut Vec<LayerId>, responses: &mut VecDeque<Message>, fonts: &mut HashSet<Font>) {
|
let mut fonts = HashSet::new();
|
||||||
match data {
|
for (_node_id, node) in self.document_legacy.document_network.recursive_nodes() {
|
||||||
LayerDataType::Folder(folder) => {
|
for input in &node.inputs {
|
||||||
for (id, layer) in folder.layer_ids.iter().zip(folder.layers().iter()) {
|
if let NodeInput::Value {
|
||||||
path.push(*id);
|
tagged_value: TaggedValue::Font(font),
|
||||||
walk_layers(&layer.data, path, responses, fonts);
|
..
|
||||||
path.pop();
|
} = input
|
||||||
}
|
{
|
||||||
|
fonts.insert(font.clone());
|
||||||
}
|
}
|
||||||
LayerDataType::Layer(layer) => {
|
|
||||||
if layer.cached_output_data == CachedOutputData::None {
|
|
||||||
responses.add(DocumentMessage::InputFrameRasterizeRegionBelowLayer { layer_path: path.clone() });
|
|
||||||
}
|
|
||||||
for node in layer.network.nodes.values() {
|
|
||||||
for input in &node.inputs {
|
|
||||||
if let NodeInput::Value {
|
|
||||||
tagged_value: TaggedValue::Font(font),
|
|
||||||
..
|
|
||||||
} = input
|
|
||||||
{
|
|
||||||
fonts.insert(font.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut fonts = HashSet::new();
|
|
||||||
walk_layers(root, &mut path, responses, &mut fonts);
|
|
||||||
for font in fonts {
|
for font in fonts {
|
||||||
responses.add_front(FrontendMessage::TriggerFontLoad { font, is_default: false });
|
responses.add_front(FrontendMessage::TriggerFontLoad { font, is_default: false });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ use crate::messages::prelude::*;
|
|||||||
use bezier_rs::Subpath;
|
use bezier_rs::Subpath;
|
||||||
use graph_craft::document::NodeId;
|
use graph_craft::document::NodeId;
|
||||||
use graphene_core::raster::ImageFrame;
|
use graphene_core::raster::ImageFrame;
|
||||||
|
use graphene_core::text::Font;
|
||||||
use graphene_core::uuid::ManipulatorGroupId;
|
use graphene_core::uuid::ManipulatorGroupId;
|
||||||
use graphene_core::vector::brush_stroke::BrushStroke;
|
use graphene_core::vector::brush_stroke::BrushStroke;
|
||||||
use graphene_core::vector::style::{Fill, Stroke};
|
use graphene_core::vector::style::{Fill, Stroke};
|
||||||
@@ -68,6 +69,12 @@ pub enum GraphOperationMessage {
|
|||||||
id: NodeId,
|
id: NodeId,
|
||||||
subpaths: Vec<Subpath<ManipulatorGroupId>>,
|
subpaths: Vec<Subpath<ManipulatorGroupId>>,
|
||||||
},
|
},
|
||||||
|
NewTextLayer {
|
||||||
|
id: NodeId,
|
||||||
|
text: String,
|
||||||
|
font: Font,
|
||||||
|
size: f64,
|
||||||
|
},
|
||||||
ResizeArtboard {
|
ResizeArtboard {
|
||||||
id: NodeId,
|
id: NodeId,
|
||||||
location: IVec2,
|
location: IVec2,
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ use document_legacy::{LayerId, Operation};
|
|||||||
use graph_craft::document::value::TaggedValue;
|
use graph_craft::document::value::TaggedValue;
|
||||||
use graph_craft::document::{generate_uuid, DocumentNode, NodeId, NodeInput, NodeNetwork, NodeOutput};
|
use graph_craft::document::{generate_uuid, DocumentNode, NodeId, NodeInput, NodeNetwork, NodeOutput};
|
||||||
use graphene_core::raster::ImageFrame;
|
use graphene_core::raster::ImageFrame;
|
||||||
|
use graphene_core::text::Font;
|
||||||
use graphene_core::uuid::ManipulatorGroupId;
|
use graphene_core::uuid::ManipulatorGroupId;
|
||||||
use graphene_core::vector::brush_stroke::BrushStroke;
|
use graphene_core::vector::brush_stroke::BrushStroke;
|
||||||
use graphene_core::vector::style::{Fill, FillType, Stroke};
|
use graphene_core::vector::style::{Fill, FillType, Stroke};
|
||||||
@@ -178,6 +179,34 @@ impl<'a> ModifyInputsContext<'a> {
|
|||||||
self.responses.add(NodeGraphMessage::SendGraph { should_rerender: true });
|
self.responses.add(NodeGraphMessage::SendGraph { should_rerender: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn insert_text(&mut self, text: String, font: Font, size: f64, layer: NodeId) {
|
||||||
|
let text = resolve_document_node_type("Text").expect("Text node does not exist").to_document_node(
|
||||||
|
[
|
||||||
|
NodeInput::Network(graph_craft::concrete!(graphene_std::wasm_application_io::WasmEditorApi)),
|
||||||
|
NodeInput::value(TaggedValue::String(text), false),
|
||||||
|
NodeInput::value(TaggedValue::Font(font), false),
|
||||||
|
NodeInput::value(TaggedValue::F64(size), false),
|
||||||
|
],
|
||||||
|
Default::default(),
|
||||||
|
);
|
||||||
|
let cull = resolve_document_node_type("Cull").expect("Cull node does not exist").default_document_node();
|
||||||
|
let transform = resolve_document_node_type("Transform").expect("Transform node does not exist").default_document_node();
|
||||||
|
let fill = resolve_document_node_type("Fill").expect("Fill node does not exist").default_document_node();
|
||||||
|
let stroke = resolve_document_node_type("Stroke").expect("Stroke node does not exist").default_document_node();
|
||||||
|
|
||||||
|
let stroke_id = generate_uuid();
|
||||||
|
self.insert_node_before(stroke_id, layer, 0, stroke, IVec2::new(-8, 0));
|
||||||
|
let fill_id = generate_uuid();
|
||||||
|
self.insert_node_before(fill_id, stroke_id, 0, fill, IVec2::new(-8, 0));
|
||||||
|
let transform_id = generate_uuid();
|
||||||
|
self.insert_node_before(transform_id, fill_id, 0, transform, IVec2::new(-8, 0));
|
||||||
|
let cull_id = generate_uuid();
|
||||||
|
self.insert_node_before(cull_id, transform_id, 0, cull, IVec2::new(-8, 0));
|
||||||
|
let text_id = generate_uuid();
|
||||||
|
self.insert_node_before(text_id, cull_id, 0, text, IVec2::new(-8, 0));
|
||||||
|
self.responses.add(NodeGraphMessage::SendGraph { should_rerender: true });
|
||||||
|
}
|
||||||
|
|
||||||
fn insert_image_data(&mut self, image_frame: ImageFrame<Color>, layer: NodeId) {
|
fn insert_image_data(&mut self, image_frame: ImageFrame<Color>, layer: NodeId) {
|
||||||
let image = {
|
let image = {
|
||||||
let node_type = resolve_document_node_type("Image").expect("Image node does not exist");
|
let node_type = resolve_document_node_type("Image").expect("Image node does not exist");
|
||||||
@@ -560,6 +589,12 @@ impl MessageHandler<GraphOperationMessage, (&mut Document, &mut NodeGraphMessage
|
|||||||
modify_inputs.insert_vector_data(subpaths, layer);
|
modify_inputs.insert_vector_data(subpaths, layer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
GraphOperationMessage::NewTextLayer { id, text, font, size } => {
|
||||||
|
let mut modify_inputs = ModifyInputsContext::new(document, node_graph, responses);
|
||||||
|
if let Some(layer) = modify_inputs.create_layer(id, modify_inputs.network.outputs[0].node_id, 0) {
|
||||||
|
modify_inputs.insert_text(text, font, size, layer);
|
||||||
|
}
|
||||||
|
}
|
||||||
GraphOperationMessage::ResizeArtboard { id, location, dimensions } => {
|
GraphOperationMessage::ResizeArtboard { id, location, dimensions } => {
|
||||||
if let Some(mut modify_inputs) = ModifyInputsContext::new_layer(&[id], document, node_graph, responses) {
|
if let Some(mut modify_inputs) = ModifyInputsContext::new_layer(&[id], document, node_graph, responses) {
|
||||||
modify_inputs.resize_artboard(location, dimensions);
|
modify_inputs.resize_artboard(location, dimensions);
|
||||||
|
|||||||
@@ -86,14 +86,17 @@ pub enum NodeGraphMessage {
|
|||||||
input_index: usize,
|
input_index: usize,
|
||||||
value: TaggedValue,
|
value: TaggedValue,
|
||||||
},
|
},
|
||||||
SetSelectNodes {
|
SetSelectedNodes {
|
||||||
nodes: Vec<NodeId>,
|
nodes: Vec<NodeId>,
|
||||||
},
|
},
|
||||||
ShiftNode {
|
ShiftNode {
|
||||||
node_id: NodeId,
|
node_id: NodeId,
|
||||||
},
|
},
|
||||||
ToggleHidden,
|
ToggleHidden,
|
||||||
ToggleHiddenImpl,
|
SetHidden {
|
||||||
|
node_id: NodeId,
|
||||||
|
hidden: bool,
|
||||||
|
},
|
||||||
TogglePreview {
|
TogglePreview {
|
||||||
node_id: NodeId,
|
node_id: NodeId,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -746,7 +746,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
|
|||||||
}
|
}
|
||||||
|
|
||||||
let nodes = new_ids.values().copied().collect();
|
let nodes = new_ids.values().copied().collect();
|
||||||
responses.add(NodeGraphMessage::SetSelectNodes { nodes });
|
responses.add(NodeGraphMessage::SetSelectedNodes { nodes });
|
||||||
|
|
||||||
responses.add(NodeGraphMessage::SendGraph { should_rerender: false });
|
responses.add(NodeGraphMessage::SendGraph { should_rerender: false });
|
||||||
}
|
}
|
||||||
@@ -817,7 +817,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
NodeGraphMessage::SetSelectNodes { nodes } => {
|
NodeGraphMessage::SetSelectedNodes { nodes } => {
|
||||||
responses.add(document.metadata.set_selected_nodes(nodes));
|
responses.add(document.metadata.set_selected_nodes(nodes));
|
||||||
responses.add(PropertiesPanelMessage::ResendActiveProperties);
|
responses.add(PropertiesPanelMessage::ResendActiveProperties);
|
||||||
}
|
}
|
||||||
@@ -870,31 +870,27 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
|
|||||||
responses.add(NodeGraphMessage::SendGraph { should_rerender: false });
|
responses.add(NodeGraphMessage::SendGraph { should_rerender: false });
|
||||||
}
|
}
|
||||||
NodeGraphMessage::ToggleHidden => {
|
NodeGraphMessage::ToggleHidden => {
|
||||||
responses.add(DocumentMessage::StartTransaction);
|
if let Some(network) = document.document_network.nested_network(&self.network) {
|
||||||
responses.add(NodeGraphMessage::ToggleHiddenImpl);
|
responses.add(DocumentMessage::StartTransaction);
|
||||||
|
|
||||||
|
let new_hidden = !document.metadata.selected_nodes().any(|id| network.disabled.contains(id));
|
||||||
|
for &node_id in document.metadata.selected_nodes() {
|
||||||
|
responses.add(NodeGraphMessage::SetHidden { node_id, hidden: new_hidden });
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
NodeGraphMessage::ToggleHiddenImpl => {
|
NodeGraphMessage::SetHidden { node_id, hidden } => {
|
||||||
if let Some(network) = document.document_network.nested_network_mut(&self.network) {
|
if let Some(network) = document.document_network.nested_network_mut(&self.network) {
|
||||||
// Check if any of the selected nodes are hidden
|
if !hidden {
|
||||||
if document.metadata.selected_nodes().any(|id| network.disabled.contains(id)) {
|
network.disabled.retain(|&id| node_id != id);
|
||||||
// Remove all selected nodes from the disabled list
|
} else if !network.inputs.contains(&node_id) && !network.original_outputs().iter().any(|output| output.node_id == node_id) {
|
||||||
network.disabled.retain(|id| !document.metadata.selected_nodes_ref().contains(id));
|
network.disabled.push(node_id);
|
||||||
} else {
|
|
||||||
let original_outputs = network.original_outputs().iter().map(|output| output.node_id).collect::<Vec<_>>();
|
|
||||||
// Add all selected nodes to the disabled list (excluding input or output nodes)
|
|
||||||
network
|
|
||||||
.disabled
|
|
||||||
.extend(document.metadata.selected_nodes().filter(|&id| !network.inputs.contains(id) && !original_outputs.contains(id)));
|
|
||||||
}
|
}
|
||||||
Self::send_graph(network, &self.layer_path, responses);
|
Self::send_graph(network, &self.layer_path, responses);
|
||||||
|
|
||||||
// Only generate node graph if one of the selected nodes is connected to the output
|
// Only generate node graph if one of the selected nodes is connected to the output
|
||||||
if document.metadata.selected_nodes().any(|&node_id| network.connected_to_output(node_id)) {
|
if network.connected_to_output(node_id) {
|
||||||
if let Some(layer_path) = self.layer_path.clone() {
|
responses.add(NodeGraphMessage::RunDocumentGraph);
|
||||||
responses.add(DocumentMessage::InputFrameRasterizeRegionBelowLayer { layer_path });
|
|
||||||
} else {
|
|
||||||
responses.add(NodeGraphMessage::RunDocumentGraph);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.update_selection_action_buttons(document, responses);
|
self.update_selection_action_buttons(document, responses);
|
||||||
|
|||||||
+11
-4
@@ -2483,7 +2483,7 @@ impl DocumentNodeType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn wrap_network_in_scope(mut network: NodeNetwork) -> NodeNetwork {
|
pub fn wrap_network_in_scope(mut network: NodeNetwork, hash: u64) -> NodeNetwork {
|
||||||
network.generate_node_paths(&[]);
|
network.generate_node_paths(&[]);
|
||||||
|
|
||||||
let node_ids = network.nodes.keys().copied().collect::<Vec<_>>();
|
let node_ids = network.nodes.keys().copied().collect::<Vec<_>>();
|
||||||
@@ -2520,11 +2520,18 @@ pub fn wrap_network_in_scope(mut network: NodeNetwork) -> NodeNetwork {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut begin_scope = resolve_document_node_type("Begin Scope")
|
||||||
|
.expect("Begin Scope node type not found")
|
||||||
|
.to_document_node(vec![input_type.unwrap()], DocumentNodeMetadata::default());
|
||||||
|
if let DocumentNodeImplementation::Network(g) = &mut begin_scope.implementation {
|
||||||
|
if let Some(node) = g.nodes.get_mut(&0) {
|
||||||
|
node.hash = hash;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// wrap the inner network in a scope
|
// wrap the inner network in a scope
|
||||||
let nodes = vec![
|
let nodes = vec![
|
||||||
resolve_document_node_type("Begin Scope")
|
begin_scope,
|
||||||
.expect("Begin Scope node type not found")
|
|
||||||
.to_document_node(vec![input_type.unwrap()], DocumentNodeMetadata::default()),
|
|
||||||
inner_network,
|
inner_network,
|
||||||
resolve_document_node_type("End Scope")
|
resolve_document_node_type("End Scope")
|
||||||
.expect("End Scope node type not found")
|
.expect("End Scope node type not found")
|
||||||
|
|||||||
@@ -219,14 +219,13 @@ impl MessageHandler<PortfolioMessage, (&InputPreprocessorMessageHandler, &Prefer
|
|||||||
} => {
|
} => {
|
||||||
let font = Font::new(font_family, font_style);
|
let font = Font::new(font_family, font_style);
|
||||||
|
|
||||||
if let Some(document) = self.active_document_mut() {
|
|
||||||
Self::uploaded_new_font(document, &font, responses);
|
|
||||||
responses.add(DocumentMessage::RenderDocument);
|
|
||||||
responses.add(BroadcastEvent::DocumentIsDirty);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.persistent_data.font_cache.insert(font, preview_url, data, is_default);
|
self.persistent_data.font_cache.insert(font, preview_url, data, is_default);
|
||||||
self.executor.update_font_cache(self.persistent_data.font_cache.clone());
|
self.executor.update_font_cache(self.persistent_data.font_cache.clone());
|
||||||
|
|
||||||
|
if let Some(document) = self.active_document_mut() {
|
||||||
|
responses.add(NodeGraphMessage::RunDocumentGraph);
|
||||||
|
responses.add(BroadcastEvent::DocumentIsDirty);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
PortfolioMessage::GraphViewOverlay { open } => {
|
PortfolioMessage::GraphViewOverlay { open } => {
|
||||||
self.graph_view_overlay_open = open;
|
self.graph_view_overlay_open = open;
|
||||||
@@ -675,37 +674,6 @@ impl PortfolioMessageHandler {
|
|||||||
self.document_ids.iter().position(|id| id == &document_id).expect("Active document is missing from document ids")
|
self.document_ids.iter().position(|id| id == &document_id).expect("Active document is missing from document ids")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn uploaded_new_font(document: &mut DocumentMessageHandler, target_font: &Font, responses: &mut VecDeque<Message>) {
|
|
||||||
let mut stack = vec![(&document.document_legacy.root, Vec::new())];
|
|
||||||
|
|
||||||
while let Some((layer, layer_path)) = stack.pop() {
|
|
||||||
match &layer.data {
|
|
||||||
LayerDataType::Folder(folder) => stack.extend(folder.layers.iter().zip(folder.layer_ids.iter().map(|id| {
|
|
||||||
let mut x = layer_path.clone();
|
|
||||||
x.push(*id);
|
|
||||||
x
|
|
||||||
}))),
|
|
||||||
LayerDataType::Layer(layer) => {
|
|
||||||
let input_is_font = |input: &NodeInput| {
|
|
||||||
let NodeInput::Value {
|
|
||||||
tagged_value: TaggedValue::Font(font),
|
|
||||||
..
|
|
||||||
} = input
|
|
||||||
else {
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
font == target_font
|
|
||||||
};
|
|
||||||
let should_rerender = layer.network.nodes.values().any(|node| node.inputs.iter().any(input_is_font));
|
|
||||||
if should_rerender {
|
|
||||||
responses.add(DocumentMessage::InputFrameRasterizeRegionBelowLayer { layer_path });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn poll_node_graph_evaluation(&mut self, responses: &mut VecDeque<Message>) {
|
pub fn poll_node_graph_evaluation(&mut self, responses: &mut VecDeque<Message>) {
|
||||||
let Some(active_document) = self.active_document_id.and_then(|id| self.documents.get_mut(&id)) else {
|
let Some(active_document) = self.active_document_id.and_then(|id| self.documents.get_mut(&id)) else {
|
||||||
warn!("Polling node graph with no document");
|
warn!("Polling node graph with no document");
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ use bezier_rs::{ManipulatorGroup, Subpath};
|
|||||||
use document_legacy::{document::Document, document_metadata::LayerNodeIdentifier, LayerId, Operation};
|
use document_legacy::{document::Document, document_metadata::LayerNodeIdentifier, LayerId, Operation};
|
||||||
use graph_craft::document::{value::TaggedValue, DocumentNode, NodeId, NodeInput, NodeNetwork};
|
use graph_craft::document::{value::TaggedValue, DocumentNode, NodeId, NodeInput, NodeNetwork};
|
||||||
use graphene_core::raster::ImageFrame;
|
use graphene_core::raster::ImageFrame;
|
||||||
|
use graphene_core::text::Font;
|
||||||
use graphene_core::uuid::ManipulatorGroupId;
|
use graphene_core::uuid::ManipulatorGroupId;
|
||||||
use graphene_core::vector::style::{FillType, Gradient};
|
use graphene_core::vector::style::{FillType, Gradient};
|
||||||
use graphene_core::Color;
|
use graphene_core::Color;
|
||||||
@@ -16,10 +17,10 @@ use std::collections::VecDeque;
|
|||||||
pub fn new_vector_layer(subpaths: Vec<Subpath<ManipulatorGroupId>>, layer_path: Vec<LayerId>, responses: &mut VecDeque<Message>) {
|
pub fn new_vector_layer(subpaths: Vec<Subpath<ManipulatorGroupId>>, layer_path: Vec<LayerId>, responses: &mut VecDeque<Message>) {
|
||||||
let id = *layer_path.last().unwrap();
|
let id = *layer_path.last().unwrap();
|
||||||
responses.add(GraphOperationMessage::NewVectorLayer { id, subpaths });
|
responses.add(GraphOperationMessage::NewVectorLayer { id, subpaths });
|
||||||
responses.add(NodeGraphMessage::SetSelectNodes { nodes: vec![id] })
|
responses.add(NodeGraphMessage::SetSelectedNodes { nodes: vec![id] })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creat a new bitmap layer from an [`graphene_core::raster::ImageFrame<Color>`]
|
/// Create a new bitmap layer from an [`graphene_core::raster::ImageFrame<Color>`]
|
||||||
pub fn new_image_layer(image_frame: ImageFrame<Color>, layer_path: Vec<LayerId>, responses: &mut VecDeque<Message>) {
|
pub fn new_image_layer(image_frame: ImageFrame<Color>, layer_path: Vec<LayerId>, responses: &mut VecDeque<Message>) {
|
||||||
responses.add(GraphOperationMessage::NewBitmapLayer {
|
responses.add(GraphOperationMessage::NewBitmapLayer {
|
||||||
id: *layer_path.last().unwrap(),
|
id: *layer_path.last().unwrap(),
|
||||||
@@ -119,6 +120,52 @@ pub fn get_gradient(layer: LayerNodeIdentifier, document: &Document) -> Option<G
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the current fill of a layer from the closest fill node
|
||||||
|
pub fn get_fill_color(layer: LayerNodeIdentifier, document: &Document) -> Option<Color> {
|
||||||
|
let inputs = NodeGraphLayer::new(layer, document)?.find_node_inputs("Fill")?;
|
||||||
|
let TaggedValue::Color(color) = inputs.get(2)?.as_value()? else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
Some(*color)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_text_id(layer: LayerNodeIdentifier, document: &Document) -> Option<NodeId> {
|
||||||
|
NodeGraphLayer::new(layer, document)?.node_id("Text")
|
||||||
|
}
|
||||||
|
pub fn get_fill_id(layer: LayerNodeIdentifier, document: &Document) -> Option<NodeId> {
|
||||||
|
NodeGraphLayer::new(layer, document)?.node_id("Fill")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets properties from the text node
|
||||||
|
pub fn get_text(layer: LayerNodeIdentifier, document: &Document) -> Option<(&String, &Font, f64)> {
|
||||||
|
let inputs = NodeGraphLayer::new(layer, document)?.find_node_inputs("Text")?;
|
||||||
|
let NodeInput::Value {
|
||||||
|
tagged_value: TaggedValue::String(text),
|
||||||
|
..
|
||||||
|
} = &inputs[1]
|
||||||
|
else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
|
||||||
|
let NodeInput::Value {
|
||||||
|
tagged_value: TaggedValue::Font(font),
|
||||||
|
..
|
||||||
|
} = &inputs[2]
|
||||||
|
else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
|
||||||
|
let NodeInput::Value {
|
||||||
|
tagged_value: TaggedValue::F64(font_size),
|
||||||
|
..
|
||||||
|
} = inputs[3]
|
||||||
|
else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
|
||||||
|
Some((text, font, font_size))
|
||||||
|
}
|
||||||
|
|
||||||
/// Is a specified layer an artboard?
|
/// Is a specified layer an artboard?
|
||||||
pub fn is_artboard(layer: LayerNodeIdentifier, document: &Document) -> bool {
|
pub fn is_artboard(layer: LayerNodeIdentifier, document: &Document) -> bool {
|
||||||
NodeGraphLayer::new(layer, document).is_some_and(|layer| layer.uses_node("Artboard"))
|
NodeGraphLayer::new(layer, document).is_some_and(|layer| layer.uses_node("Artboard"))
|
||||||
@@ -193,6 +240,11 @@ impl<'a> NodeGraphLayer<'a> {
|
|||||||
self.primary_layer_flow().any(|(node, _id)| node.name == node_name)
|
self.primary_layer_flow().any(|(node, _id)| node.name == node_name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Node id of a node if it exists in the layer's primary flow
|
||||||
|
pub fn node_id(&self, node_name: &str) -> Option<NodeId> {
|
||||||
|
self.primary_layer_flow().find(|(node, _id)| node.name == node_name).map(|(_node, id)| id)
|
||||||
|
}
|
||||||
|
|
||||||
/// Find all of the inputs of a specific node within the layer's primary flow
|
/// Find all of the inputs of a specific node within the layer's primary flow
|
||||||
pub fn find_node_inputs(&self, node_name: &str) -> Option<&'a Vec<NodeInput>> {
|
pub fn find_node_inputs(&self, node_name: &str) -> Option<&'a Vec<NodeInput>> {
|
||||||
self.primary_layer_flow().find(|(node, _id)| node.name == node_name).map(|(node, _id)| &node.inputs)
|
self.primary_layer_flow().find(|(node, _id)| node.name == node_name).map(|(node, _id)| &node.inputs)
|
||||||
|
|||||||
@@ -246,7 +246,7 @@ impl PathToolData {
|
|||||||
if shift {
|
if shift {
|
||||||
responses.add(NodeGraphMessage::AddSelectNodes { nodes: vec![layer.to_node()] });
|
responses.add(NodeGraphMessage::AddSelectNodes { nodes: vec![layer.to_node()] });
|
||||||
} else {
|
} else {
|
||||||
responses.add(NodeGraphMessage::SetSelectNodes { nodes: vec![layer.to_node()] });
|
responses.add(NodeGraphMessage::SetSelectedNodes { nodes: vec![layer.to_node()] });
|
||||||
}
|
}
|
||||||
self.drag_start_pos = input.mouse.position;
|
self.drag_start_pos = input.mouse.position;
|
||||||
self.previous_mouse_position = input.mouse.position;
|
self.previous_mouse_position = input.mouse.position;
|
||||||
@@ -382,7 +382,7 @@ impl Fsm for PathToolFsmState {
|
|||||||
let shift_pressed = input.keyboard.get(add_to_selection as usize);
|
let shift_pressed = input.keyboard.get(add_to_selection as usize);
|
||||||
|
|
||||||
if tool_data.drag_start_pos == tool_data.previous_mouse_position {
|
if tool_data.drag_start_pos == tool_data.previous_mouse_position {
|
||||||
responses.add(NodeGraphMessage::SetSelectNodes { nodes: vec![] });
|
responses.add(NodeGraphMessage::SetSelectedNodes { nodes: vec![] });
|
||||||
} else {
|
} else {
|
||||||
shape_editor.select_all_in_quad(&document.document_legacy, [tool_data.drag_start_pos, tool_data.previous_mouse_position], !shift_pressed);
|
shape_editor.select_all_in_quad(&document.document_legacy, [tool_data.drag_start_pos, tool_data.previous_mouse_position], !shift_pressed);
|
||||||
tool_data.refresh_overlays(document, shape_editor, shape_overlay, responses);
|
tool_data.refresh_overlays(document, shape_editor, shape_overlay, responses);
|
||||||
@@ -397,7 +397,7 @@ impl Fsm for PathToolFsmState {
|
|||||||
let shift_pressed = input.keyboard.get(shift_mirror_distance as usize);
|
let shift_pressed = input.keyboard.get(shift_mirror_distance as usize);
|
||||||
|
|
||||||
if tool_data.drag_start_pos == tool_data.previous_mouse_position {
|
if tool_data.drag_start_pos == tool_data.previous_mouse_position {
|
||||||
responses.add(NodeGraphMessage::SetSelectNodes { nodes: vec![] });
|
responses.add(NodeGraphMessage::SetSelectedNodes { nodes: vec![] });
|
||||||
} else {
|
} else {
|
||||||
shape_editor.select_all_in_quad(&document.document_legacy, [tool_data.drag_start_pos, tool_data.previous_mouse_position], !shift_pressed);
|
shape_editor.select_all_in_quad(&document.document_legacy, [tool_data.drag_start_pos, tool_data.previous_mouse_position], !shift_pressed);
|
||||||
tool_data.refresh_overlays(document, shape_editor, shape_overlay, responses);
|
tool_data.refresh_overlays(document, shape_editor, shape_overlay, responses);
|
||||||
|
|||||||
@@ -369,7 +369,7 @@ impl SelectToolData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Select the originals
|
// Select the originals
|
||||||
responses.add(NodeGraphMessage::SetSelectNodes {
|
responses.add(NodeGraphMessage::SetSelectedNodes {
|
||||||
nodes: originals.iter().map(|layer| layer.to_node()).collect::<Vec<_>>(),
|
nodes: originals.iter().map(|layer| layer.to_node()).collect::<Vec<_>>(),
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -719,13 +719,13 @@ impl Fsm for SelectToolFsmState {
|
|||||||
tool_data.layers_dragging.clear();
|
tool_data.layers_dragging.clear();
|
||||||
tool_data.layers_dragging.extend(replacement_selected_layers.iter());
|
tool_data.layers_dragging.extend(replacement_selected_layers.iter());
|
||||||
|
|
||||||
responses.add(NodeGraphMessage::SetSelectNodes {
|
responses.add(NodeGraphMessage::SetSelectedNodes {
|
||||||
nodes: replacement_selected_layers.iter().map(|layer| layer.to_node()).collect(),
|
nodes: replacement_selected_layers.iter().map(|layer| layer.to_node()).collect(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else if let Some(selecting_layer) = tool_data.select_single_layer.take() {
|
} else if let Some(selecting_layer) = tool_data.select_single_layer.take() {
|
||||||
if !tool_data.has_dragged {
|
if !tool_data.has_dragged {
|
||||||
responses.add(NodeGraphMessage::SetSelectNodes {
|
responses.add(NodeGraphMessage::SetSelectedNodes {
|
||||||
nodes: vec![selecting_layer.to_node()],
|
nodes: vec![selecting_layer.to_node()],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -787,7 +787,7 @@ impl Fsm for SelectToolFsmState {
|
|||||||
let quad = tool_data.selection_quad();
|
let quad = tool_data.selection_quad();
|
||||||
// For shallow select we don't update dragging layers until inside drag_start_shallowest_manipulation()
|
// For shallow select we don't update dragging layers until inside drag_start_shallowest_manipulation()
|
||||||
tool_data.layers_dragging = document.metadata().intersect_quad(quad, &document.document_legacy.document_network).collect();
|
tool_data.layers_dragging = document.metadata().intersect_quad(quad, &document.document_legacy.document_network).collect();
|
||||||
responses.add_front(NodeGraphMessage::SetSelectNodes {
|
responses.add_front(NodeGraphMessage::SetSelectedNodes {
|
||||||
nodes: tool_data.layers_dragging.iter().map(|layer| layer.to_node()).collect(),
|
nodes: tool_data.layers_dragging.iter().map(|layer| layer.to_node()).collect(),
|
||||||
});
|
});
|
||||||
responses.add_front(DocumentMessage::Overlays(
|
responses.add_front(DocumentMessage::Overlays(
|
||||||
@@ -799,18 +799,14 @@ impl Fsm for SelectToolFsmState {
|
|||||||
SelectToolFsmState::Ready
|
SelectToolFsmState::Ready
|
||||||
}
|
}
|
||||||
(SelectToolFsmState::Ready, SelectToolMessage::Enter) => {
|
(SelectToolFsmState::Ready, SelectToolMessage::Enter) => {
|
||||||
let mut selected_layers = document.selected_layers();
|
let mut selected_layers = document.metadata().selected_layers();
|
||||||
|
|
||||||
if let Some(layer_path) = selected_layers.next() {
|
if let Some(layer) = selected_layers.next() {
|
||||||
// Check that only one layer is selected
|
// Check that only one layer is selected
|
||||||
if selected_layers.next().is_none() {
|
if selected_layers.next().is_none() {
|
||||||
if let Ok(layer) = document.document_legacy.layer(layer_path) {
|
if is_text_layer(layer, &document.document_legacy) {
|
||||||
if let Ok(network) = layer.as_layer_network() {
|
responses.add_front(ToolMessage::ActivateTool { tool_type: ToolType::Text });
|
||||||
if network.nodes.values().any(|node| node.name == "Text") {
|
responses.add(TextToolMessage::EditSelected);
|
||||||
responses.add_front(ToolMessage::ActivateTool { tool_type: ToolType::Text });
|
|
||||||
responses.add(TextToolMessage::EditSelected);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -928,7 +924,7 @@ fn drag_shallowest_manipulation(responses: &mut VecDeque<Message>, mut selected:
|
|||||||
let new_selected = ancestor.unwrap_or_else(|| layer.child_of_root(document.metadata()));
|
let new_selected = ancestor.unwrap_or_else(|| layer.child_of_root(document.metadata()));
|
||||||
|
|
||||||
tool_data.layers_dragging = vec![new_selected];
|
tool_data.layers_dragging = vec![new_selected];
|
||||||
responses.add(NodeGraphMessage::SetSelectNodes {
|
responses.add(NodeGraphMessage::SetSelectedNodes {
|
||||||
nodes: tool_data.layers_dragging.iter().map(|layer| layer.to_node()).collect(),
|
nodes: tool_data.layers_dragging.iter().map(|layer| layer.to_node()).collect(),
|
||||||
});
|
});
|
||||||
// tool_data
|
// tool_data
|
||||||
@@ -938,7 +934,7 @@ fn drag_shallowest_manipulation(responses: &mut VecDeque<Message>, mut selected:
|
|||||||
|
|
||||||
fn drag_deepest_manipulation(responses: &mut VecDeque<Message>, mut selected: Vec<LayerNodeIdentifier>, tool_data: &mut SelectToolData) {
|
fn drag_deepest_manipulation(responses: &mut VecDeque<Message>, mut selected: Vec<LayerNodeIdentifier>, tool_data: &mut SelectToolData) {
|
||||||
tool_data.layers_dragging.append(&mut selected);
|
tool_data.layers_dragging.append(&mut selected);
|
||||||
responses.add(NodeGraphMessage::SetSelectNodes {
|
responses.add(NodeGraphMessage::SetSelectedNodes {
|
||||||
nodes: tool_data.layers_dragging.iter().map(|layer| layer.to_node()).collect(),
|
nodes: tool_data.layers_dragging.iter().map(|layer| layer.to_node()).collect(),
|
||||||
});
|
});
|
||||||
// tool_data
|
// tool_data
|
||||||
@@ -961,7 +957,7 @@ fn edit_layer_shallowest_manipulation(document: &DocumentMessageHandler, layer:
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
responses.add(NodeGraphMessage::SetSelectNodes { nodes: vec![new_selected.to_node()] });
|
responses.add(NodeGraphMessage::SetSelectedNodes { nodes: vec![new_selected.to_node()] });
|
||||||
}
|
}
|
||||||
|
|
||||||
fn edit_layer_deepest_manipulation(layer: LayerNodeIdentifier, document: &Document, responses: &mut VecDeque<Message>) {
|
fn edit_layer_deepest_manipulation(layer: LayerNodeIdentifier, document: &Document, responses: &mut VecDeque<Message>) {
|
||||||
|
|||||||
@@ -4,7 +4,9 @@ use crate::application::generate_uuid;
|
|||||||
use crate::consts::{COLOR_ACCENT, SELECTION_TOLERANCE};
|
use crate::consts::{COLOR_ACCENT, SELECTION_TOLERANCE};
|
||||||
use crate::messages::portfolio::document::node_graph::new_text_network;
|
use crate::messages::portfolio::document::node_graph::new_text_network;
|
||||||
use crate::messages::tool::common_functionality::color_selector::{ToolColorOptions, ToolColorType};
|
use crate::messages::tool::common_functionality::color_selector::{ToolColorOptions, ToolColorType};
|
||||||
|
use crate::messages::tool::common_functionality::graph_modification_utils::{self, is_text_layer};
|
||||||
|
|
||||||
|
use document_legacy::document_metadata::LayerNodeIdentifier;
|
||||||
use document_legacy::intersection::Quad;
|
use document_legacy::intersection::Quad;
|
||||||
use document_legacy::layers::layer_info::Layer;
|
use document_legacy::layers::layer_info::Layer;
|
||||||
use document_legacy::layers::style::{self, Fill, RenderData, Stroke};
|
use document_legacy::layers::style::{self, Fill, RenderData, Stroke};
|
||||||
@@ -216,7 +218,7 @@ pub struct EditingText {
|
|||||||
|
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
struct TextToolData {
|
struct TextToolData {
|
||||||
layer_path: Vec<LayerId>,
|
layer: LayerNodeIdentifier,
|
||||||
overlays: Vec<Vec<LayerId>>,
|
overlays: Vec<Vec<LayerId>>,
|
||||||
editing_text: Option<EditingText>,
|
editing_text: Option<EditingText>,
|
||||||
new_text: String,
|
new_text: String,
|
||||||
@@ -224,9 +226,10 @@ 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, render_data: &RenderData, responses: &mut VecDeque<Message>) {
|
fn set_editing(&self, editable: bool, render_data: &RenderData, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) {
|
||||||
let path = self.layer_path.clone();
|
if let Some(node_id) = graph_modification_utils::get_fill_id(self.layer, &document.document_legacy) {
|
||||||
responses.add(Operation::SetLayerVisibility { path, visible: !editable });
|
responses.add(NodeGraphMessage::SetHidden { node_id, hidden: 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 {
|
||||||
@@ -243,18 +246,9 @@ impl TextToolData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn load_layer_text_node(&mut self, document: &DocumentMessageHandler) -> Option<()> {
|
fn load_layer_text_node(&mut self, document: &DocumentMessageHandler) -> Option<()> {
|
||||||
let transform = document.document_legacy.multiply_transforms(&self.layer_path).ok()?;
|
let transform = document.metadata().transform_to_viewport(self.layer);
|
||||||
let layer = document.document_legacy.layer(&self.layer_path).ok()?;
|
let color = graph_modification_utils::get_fill_color(self.layer, &document.document_legacy).unwrap_or(Color::BLACK);
|
||||||
let color = layer
|
let (text, font, font_size) = graph_modification_utils::get_text(self.layer, &document.document_legacy)?;
|
||||||
.style()
|
|
||||||
.ok()
|
|
||||||
.map_or(Color::BLACK, |style| if let Fill::Solid(solid_color) = style.fill() { *solid_color } else { Color::BLACK });
|
|
||||||
|
|
||||||
let network = get_network(&self.layer_path, document)?;
|
|
||||||
let node_id = get_text_node_id(network)?;
|
|
||||||
let node = network.nodes.get(&node_id)?;
|
|
||||||
|
|
||||||
let (text, font, font_size) = Self::extract_text_node_inputs(node)?;
|
|
||||||
self.editing_text = Some(EditingText {
|
self.editing_text = Some(EditingText {
|
||||||
text: text.clone(),
|
text: text.clone(),
|
||||||
font: font.clone(),
|
font: font.clone(),
|
||||||
@@ -266,53 +260,24 @@ impl TextToolData {
|
|||||||
Some(())
|
Some(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start_editing_layer(&mut self, layer_path: &[LayerId], tool_state: TextToolFsmState, document: &DocumentMessageHandler, render_data: &RenderData, responses: &mut VecDeque<Message>) {
|
fn start_editing_layer(&mut self, layer: LayerNodeIdentifier, tool_state: TextToolFsmState, document: &DocumentMessageHandler, render_data: &RenderData, responses: &mut VecDeque<Message>) {
|
||||||
if tool_state == TextToolFsmState::Editing {
|
if tool_state == TextToolFsmState::Editing {
|
||||||
self.set_editing(false, render_data, responses);
|
self.set_editing(false, render_data, document, responses);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.layer_path = layer_path.into();
|
self.layer = layer;
|
||||||
self.load_layer_text_node(document);
|
self.load_layer_text_node(document);
|
||||||
|
|
||||||
responses.add(DocumentMessage::StartTransaction);
|
responses.add(DocumentMessage::StartTransaction);
|
||||||
|
|
||||||
self.set_editing(true, render_data, responses);
|
self.set_editing(true, render_data, document, responses);
|
||||||
|
|
||||||
let replacement_selected_layers = vec![self.layer_path.clone()];
|
responses.add(NodeGraphMessage::SetSelectedNodes { nodes: vec![self.layer.to_node()] });
|
||||||
responses.add(DocumentMessage::SetSelectedLayers { replacement_selected_layers });
|
|
||||||
}
|
|
||||||
|
|
||||||
fn extract_text_node_inputs(node: &DocumentNode) -> Option<(&String, &Font, f64)> {
|
|
||||||
let NodeInput::Value {
|
|
||||||
tagged_value: TaggedValue::String(text),
|
|
||||||
..
|
|
||||||
} = &node.inputs[1]
|
|
||||||
else {
|
|
||||||
return None;
|
|
||||||
};
|
|
||||||
let NodeInput::Value {
|
|
||||||
tagged_value: TaggedValue::Font(font),
|
|
||||||
..
|
|
||||||
} = &node.inputs[2]
|
|
||||||
else {
|
|
||||||
return None;
|
|
||||||
};
|
|
||||||
let NodeInput::Value {
|
|
||||||
tagged_value: TaggedValue::F64(font_size),
|
|
||||||
..
|
|
||||||
} = &node.inputs[3]
|
|
||||||
else {
|
|
||||||
return None;
|
|
||||||
};
|
|
||||||
Some((text, font, *font_size))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn interact(&mut self, state: TextToolFsmState, mouse: DVec2, document: &DocumentMessageHandler, render_data: &RenderData, responses: &mut VecDeque<Message>) -> TextToolFsmState {
|
fn interact(&mut self, state: TextToolFsmState, mouse: DVec2, document: &DocumentMessageHandler, render_data: &RenderData, responses: &mut VecDeque<Message>) -> TextToolFsmState {
|
||||||
let tolerance = DVec2::splat(SELECTION_TOLERANCE);
|
|
||||||
let quad = Quad::from_box([mouse - tolerance, mouse + tolerance]);
|
|
||||||
|
|
||||||
// Check if the user has selected an existing text layer
|
// Check if the user has selected an existing text layer
|
||||||
if let Some(clicked_text_layer_path) = document.document_legacy.intersects_quad_root(quad, render_data).last().filter(|l| is_text_layer(document, l)) {
|
if let Some(clicked_text_layer_path) = document.metadata().click(mouse, &document.network()).filter(|&layer| is_text_layer(layer, &document.document_legacy)) {
|
||||||
self.start_editing_layer(clicked_text_layer_path, state, document, render_data, responses);
|
self.start_editing_layer(clicked_text_layer_path, state, document, render_data, responses);
|
||||||
|
|
||||||
TextToolFsmState::Editing
|
TextToolFsmState::Editing
|
||||||
@@ -321,35 +286,33 @@ impl TextToolData {
|
|||||||
else if let Some(editing_text) = self.editing_text.as_ref().filter(|_| state == TextToolFsmState::Ready) {
|
else if let Some(editing_text) = self.editing_text.as_ref().filter(|_| state == TextToolFsmState::Ready) {
|
||||||
responses.add(DocumentMessage::StartTransaction);
|
responses.add(DocumentMessage::StartTransaction);
|
||||||
|
|
||||||
let network = new_text_network(String::new(), editing_text.font.clone(), editing_text.font_size);
|
self.layer = LayerNodeIdentifier::new_unchecked(generate_uuid());
|
||||||
|
|
||||||
responses.add(Operation::AddFrame {
|
responses.add(GraphOperationMessage::NewTextLayer {
|
||||||
path: self.layer_path.clone(),
|
id: self.layer.to_node(),
|
||||||
insert_index: -1,
|
text: String::new(),
|
||||||
transform: DAffine2::ZERO.to_cols_array(),
|
font: editing_text.font.clone(),
|
||||||
network,
|
size: editing_text.font_size,
|
||||||
});
|
});
|
||||||
responses.add(GraphOperationMessage::FillSet {
|
responses.add(GraphOperationMessage::FillSet {
|
||||||
layer: self.layer_path.clone(),
|
layer: self.layer.to_path(),
|
||||||
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 },
|
||||||
});
|
});
|
||||||
responses.add(GraphOperationMessage::TransformSet {
|
responses.add(GraphOperationMessage::TransformSet {
|
||||||
layer: self.layer_path.clone(),
|
layer: self.layer.to_path(),
|
||||||
transform: editing_text.transform,
|
transform: editing_text.transform,
|
||||||
transform_in: TransformIn::Viewport,
|
transform_in: TransformIn::Viewport,
|
||||||
skip_rerender: true,
|
skip_rerender: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
self.set_editing(true, render_data, responses);
|
self.set_editing(true, render_data, document, responses);
|
||||||
|
|
||||||
let replacement_selected_layers = vec![self.layer_path.clone()];
|
responses.add(NodeGraphMessage::SetSelectedNodes { nodes: self.layer.to_path() });
|
||||||
|
|
||||||
responses.add(DocumentMessage::SetSelectedLayers { replacement_selected_layers });
|
|
||||||
|
|
||||||
TextToolFsmState::Editing
|
TextToolFsmState::Editing
|
||||||
} else {
|
} else {
|
||||||
// Removing old text as editable
|
// Removing old text as editable
|
||||||
self.set_editing(false, render_data, responses);
|
self.set_editing(false, render_data, document, responses);
|
||||||
|
|
||||||
resize_overlays(&mut self.overlays, responses, 0);
|
resize_overlays(&mut self.overlays, responses, 0);
|
||||||
|
|
||||||
@@ -365,7 +328,7 @@ impl TextToolData {
|
|||||||
let far = graphene_core::text::bounding_box(&self.new_text, buzz_face, editing_text.font_size, None);
|
let far = graphene_core::text::bounding_box(&self.new_text, buzz_face, editing_text.font_size, None);
|
||||||
let quad = Quad::from_box([DVec2::ZERO, far]);
|
let quad = Quad::from_box([DVec2::ZERO, far]);
|
||||||
|
|
||||||
let transformed_quad = document.document_legacy.multiply_transforms(&self.layer_path).ok()? * quad;
|
let transformed_quad = document.metadata().transform_to_viewport(self.layer) * quad;
|
||||||
let bounds = transformed_quad.bounding_box();
|
let bounds = transformed_quad.bounding_box();
|
||||||
|
|
||||||
let operation = Operation::SetLayerTransformInViewport {
|
let operation = Operation::SetLayerTransformInViewport {
|
||||||
@@ -386,7 +349,7 @@ impl TextToolData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn fix_text_bounds(&self, new_text: &str, _document: &DocumentMessageHandler, render_data: &RenderData, responses: &mut VecDeque<Message>) -> Option<()> {
|
fn fix_text_bounds(&self, new_text: &str, _document: &DocumentMessageHandler, render_data: &RenderData, responses: &mut VecDeque<Message>) -> Option<()> {
|
||||||
let layer = self.layer_path.clone();
|
let layer = self.layer.to_path();
|
||||||
let old_bounds = self.get_bounds(&self.editing_text.as_ref()?.text, render_data)?;
|
let old_bounds = self.get_bounds(&self.editing_text.as_ref()?.text, render_data)?;
|
||||||
let new_bounds = self.get_bounds(new_text, render_data)?;
|
let new_bounds = self.get_bounds(new_text, render_data)?;
|
||||||
responses.add(GraphOperationMessage::UpdateBounds { layer, old_bounds, new_bounds });
|
responses.add(GraphOperationMessage::UpdateBounds { layer, old_bounds, new_bounds });
|
||||||
@@ -419,21 +382,15 @@ fn resize_overlays(overlays: &mut Vec<Vec<LayerId>>, responses: &mut VecDeque<Me
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn update_overlays(document: &DocumentMessageHandler, tool_data: &mut TextToolData, responses: &mut VecDeque<Message>, render_data: &RenderData) {
|
fn update_overlays(document: &DocumentMessageHandler, tool_data: &mut TextToolData, responses: &mut VecDeque<Message>, render_data: &RenderData) {
|
||||||
let get_bounds = |layer: &Layer, path: &[LayerId], document: &DocumentMessageHandler, render_data: &RenderData| {
|
let get_bounds = |layer: LayerNodeIdentifier, document: &DocumentMessageHandler, render_data: &RenderData| {
|
||||||
let node_graph = layer.as_layer_network().ok()?;
|
let (text, font, font_size) = graph_modification_utils::get_text(layer, &document.document_legacy)?;
|
||||||
let node_id = get_text_node_id(node_graph)?;
|
|
||||||
let document_node = node_graph.nodes.get(&node_id)?;
|
|
||||||
let (text, font, font_size) = TextToolData::extract_text_node_inputs(document_node)?;
|
|
||||||
let buzz_face = render_data.font_cache.get(font).map(|data| load_face(data));
|
let buzz_face = render_data.font_cache.get(font).map(|data| load_face(data));
|
||||||
let far = graphene_core::text::bounding_box(text, buzz_face, font_size, None);
|
let far = graphene_core::text::bounding_box(text, buzz_face, font_size, None);
|
||||||
let quad = Quad::from_box([DVec2::ZERO, far]);
|
let quad = Quad::from_box([DVec2::ZERO, far]);
|
||||||
let multiplied = document.document_legacy.multiply_transforms(path).ok()? * quad;
|
let multiplied = document.metadata().transform_to_viewport(layer) * quad;
|
||||||
Some(multiplied.bounding_box())
|
Some(multiplied.bounding_box())
|
||||||
};
|
};
|
||||||
let bounds = document.selected_layers().filter_map(|path| match document.document_legacy.layer(path) {
|
let bounds = document.metadata().selected_layers().filter_map(|layer| get_bounds(layer, document, render_data));
|
||||||
Ok(layer) => get_bounds(layer, path, document, render_data),
|
|
||||||
Err(_) => None,
|
|
||||||
});
|
|
||||||
let bounds = bounds.collect::<Vec<_>>();
|
let bounds = bounds.collect::<Vec<_>>();
|
||||||
|
|
||||||
let new_len = bounds.len();
|
let new_len = bounds.len();
|
||||||
@@ -448,36 +405,20 @@ fn update_overlays(document: &DocumentMessageHandler, tool_data: &mut TextToolDa
|
|||||||
resize_overlays(&mut tool_data.overlays, responses, new_len);
|
resize_overlays(&mut tool_data.overlays, responses, new_len);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_network<'a>(layer_path: &[LayerId], document: &'a DocumentMessageHandler) -> Option<&'a NodeNetwork> {
|
fn can_edit_selected(document: &DocumentMessageHandler) -> Option<LayerNodeIdentifier> {
|
||||||
let layer = document.document_legacy.layer(layer_path).ok()?;
|
let mut selected_layers = document.metadata().selected_layers();
|
||||||
layer.as_layer_network().ok()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_text_node_id(network: &NodeNetwork) -> Option<NodeId> {
|
let layer = selected_layers.next()?;
|
||||||
network.nodes.iter().find(|(_, node)| node.name == "Text").map(|(&id, _)| id)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_text_layer(document: &DocumentMessageHandler, layer_path: &[LayerId]) -> bool {
|
|
||||||
let Some(network) = get_network(layer_path, document) else {
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
get_text_node_id(network).is_some()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn can_edit_selected(document: &DocumentMessageHandler) -> Option<Vec<LayerId>> {
|
|
||||||
let mut selected_layers = document.selected_layers();
|
|
||||||
|
|
||||||
let layer_path = selected_layers.next()?.to_vec();
|
|
||||||
// Check that only one layer is selected
|
// Check that only one layer is selected
|
||||||
if selected_layers.next().is_some() {
|
if selected_layers.next().is_some() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
if !is_text_layer(document, &layer_path) {
|
if !is_text_layer(layer, &document.document_legacy) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(layer_path)
|
Some(layer)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Fsm for TextToolFsmState {
|
impl Fsm for TextToolFsmState {
|
||||||
@@ -498,7 +439,7 @@ impl Fsm for TextToolFsmState {
|
|||||||
match (self, event) {
|
match (self, event) {
|
||||||
(TextToolFsmState::Editing, TextToolMessage::DocumentIsDirty) => {
|
(TextToolFsmState::Editing, TextToolMessage::DocumentIsDirty) => {
|
||||||
responses.add(FrontendMessage::DisplayEditableTextboxTransform {
|
responses.add(FrontendMessage::DisplayEditableTextboxTransform {
|
||||||
transform: document.document_legacy.multiply_transforms(&tool_data.layer_path).ok().unwrap_or_default().to_cols_array(),
|
transform: document.metadata().transform_to_viewport(tool_data.layer).to_cols_array(),
|
||||||
});
|
});
|
||||||
tool_data.update_bounds_overlay(document, render_data, responses);
|
tool_data.update_bounds_overlay(document, render_data, responses);
|
||||||
TextToolFsmState::Editing
|
TextToolFsmState::Editing
|
||||||
@@ -517,13 +458,12 @@ impl Fsm for TextToolFsmState {
|
|||||||
color: tool_options.fill.active_color(),
|
color: tool_options.fill.active_color(),
|
||||||
});
|
});
|
||||||
tool_data.new_text = String::new();
|
tool_data.new_text = String::new();
|
||||||
tool_data.layer_path = document.get_path_for_new_layer();
|
|
||||||
|
|
||||||
tool_data.interact(state, input.mouse.position, document, render_data, responses)
|
tool_data.interact(state, input.mouse.position, document, render_data, responses)
|
||||||
}
|
}
|
||||||
(state, TextToolMessage::EditSelected) => {
|
(state, TextToolMessage::EditSelected) => {
|
||||||
if let Some(layer_path) = can_edit_selected(document) {
|
if let Some(layer) = can_edit_selected(document) {
|
||||||
tool_data.start_editing_layer(&layer_path, state, document, render_data, responses);
|
tool_data.start_editing_layer(layer, state, document, render_data, responses);
|
||||||
return TextToolFsmState::Editing;
|
return TextToolFsmState::Editing;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -531,7 +471,7 @@ impl Fsm for TextToolFsmState {
|
|||||||
}
|
}
|
||||||
(state, TextToolMessage::Abort) => {
|
(state, TextToolMessage::Abort) => {
|
||||||
if state == TextToolFsmState::Editing {
|
if state == TextToolFsmState::Editing {
|
||||||
tool_data.set_editing(false, render_data, responses);
|
tool_data.set_editing(false, render_data, document, responses);
|
||||||
}
|
}
|
||||||
|
|
||||||
resize_overlays(&mut tool_data.overlays, responses, 0);
|
resize_overlays(&mut tool_data.overlays, responses, 0);
|
||||||
@@ -544,17 +484,15 @@ impl Fsm for TextToolFsmState {
|
|||||||
TextToolFsmState::Editing
|
TextToolFsmState::Editing
|
||||||
}
|
}
|
||||||
(TextToolFsmState::Editing, TextToolMessage::TextChange { new_text }) => {
|
(TextToolFsmState::Editing, TextToolMessage::TextChange { new_text }) => {
|
||||||
let layer_path = tool_data.layer_path.clone();
|
|
||||||
let network = get_network(&layer_path, document).unwrap();
|
|
||||||
tool_data.fix_text_bounds(&new_text, document, render_data, responses);
|
tool_data.fix_text_bounds(&new_text, document, render_data, responses);
|
||||||
responses.add(NodeGraphMessage::SetQualifiedInputValue {
|
responses.add(NodeGraphMessage::SetQualifiedInputValue {
|
||||||
layer_path,
|
layer_path: Vec::new(),
|
||||||
node_path: vec![get_text_node_id(network).unwrap()],
|
node_path: vec![graph_modification_utils::get_text_id(tool_data.layer, &document.document_legacy).unwrap()],
|
||||||
input_index: 1,
|
input_index: 1,
|
||||||
value: TaggedValue::String(new_text),
|
value: TaggedValue::String(new_text),
|
||||||
});
|
});
|
||||||
|
|
||||||
tool_data.set_editing(false, render_data, responses);
|
tool_data.set_editing(false, render_data, document, responses);
|
||||||
|
|
||||||
resize_overlays(&mut tool_data.overlays, responses, 0);
|
resize_overlays(&mut tool_data.overlays, responses, 0);
|
||||||
|
|
||||||
|
|||||||
@@ -107,6 +107,8 @@ thread_local! {
|
|||||||
pub(crate) static NODE_RUNTIME: Rc<RefCell<Option<NodeRuntime>>> = Rc::new(RefCell::new(None));
|
pub(crate) static NODE_RUNTIME: Rc<RefCell<Option<NodeRuntime>>> = Rc::new(RefCell::new(None));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type MonitorNodes = Vec<Vec<NodeId>>;
|
||||||
|
|
||||||
impl NodeRuntime {
|
impl NodeRuntime {
|
||||||
fn new(receiver: Receiver<NodeRuntimeMessage>, sender: Sender<NodeGraphUpdate>) -> Self {
|
fn new(receiver: Receiver<NodeRuntimeMessage>, sender: Sender<NodeGraphUpdate>) -> Self {
|
||||||
let executor = DynamicExecutor::default();
|
let executor = DynamicExecutor::default();
|
||||||
@@ -146,15 +148,7 @@ impl NodeRuntime {
|
|||||||
viewport_resolution,
|
viewport_resolution,
|
||||||
..
|
..
|
||||||
}) => {
|
}) => {
|
||||||
let network = wrap_network_in_scope(graph);
|
let (result, monitor_nodes) = self.execute_network(&path, graph, transform, viewport_resolution).await;
|
||||||
|
|
||||||
let monitor_nodes = network
|
|
||||||
.recursive_nodes()
|
|
||||||
.filter(|(_, node)| node.implementation == DocumentNodeImplementation::proto("graphene_core::memo::MonitorNode<_>"))
|
|
||||||
.map(|(_, node)| node.path.clone().unwrap_or_default())
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
let result = self.execute_network(&path, network, transform, viewport_resolution).await;
|
|
||||||
let mut responses = VecDeque::new();
|
let mut responses = VecDeque::new();
|
||||||
self.update_thumbnails(&path, &monitor_nodes, &mut responses);
|
self.update_thumbnails(&path, &monitor_nodes, &mut responses);
|
||||||
self.update_upstream_transforms(&path, &monitor_nodes, &mut responses);
|
self.update_upstream_transforms(&path, &monitor_nodes, &mut responses);
|
||||||
@@ -173,11 +167,10 @@ impl NodeRuntime {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn execute_network<'a>(&'a mut self, path: &[LayerId], scoped_network: NodeNetwork, transform: DAffine2, viewport_resolution: UVec2) -> Result<TaggedValue, String> {
|
async fn execute_network<'a>(&'a mut self, path: &[LayerId], graph: NodeNetwork, transform: DAffine2, viewport_resolution: UVec2) -> (Result<TaggedValue, String>, MonitorNodes) {
|
||||||
if self.wasm_io.is_none() {
|
if self.wasm_io.is_none() {
|
||||||
self.wasm_io = Some(WasmApplicationIo::new().await);
|
self.wasm_io = Some(WasmApplicationIo::new().await);
|
||||||
}
|
}
|
||||||
|
|
||||||
let editor_api = WasmEditorApi {
|
let editor_api = WasmEditorApi {
|
||||||
font_cache: &self.font_cache,
|
font_cache: &self.font_cache,
|
||||||
application_io: self.wasm_io.as_ref().unwrap(),
|
application_io: self.wasm_io.as_ref().unwrap(),
|
||||||
@@ -197,15 +190,33 @@ impl NodeRuntime {
|
|||||||
image_frame: None,
|
image_frame: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use std::collections::hash_map::DefaultHasher;
|
||||||
|
use std::hash::Hash;
|
||||||
|
use std::hash::Hasher;
|
||||||
|
// Required to ensure that the appropriate protonodes are reinserted when the Editor API changes.
|
||||||
|
let mut graph_input_hash = DefaultHasher::new();
|
||||||
|
editor_api.font_cache.hash(&mut graph_input_hash);
|
||||||
|
|
||||||
|
let scoped_network = wrap_network_in_scope(graph, graph_input_hash.finish());
|
||||||
|
|
||||||
|
let monitor_nodes = scoped_network
|
||||||
|
.recursive_nodes()
|
||||||
|
.filter(|(_, node)| node.implementation == DocumentNodeImplementation::proto("graphene_core::memo::MonitorNode<_>"))
|
||||||
|
.map(|(_, node)| node.path.clone().unwrap_or_default())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
// We assume only one output
|
// We assume only one output
|
||||||
assert_eq!(scoped_network.outputs.len(), 1, "Graph with multiple outputs not yet handled");
|
assert_eq!(scoped_network.outputs.len(), 1, "Graph with multiple outputs not yet handled");
|
||||||
let c = Compiler {};
|
let c = Compiler {};
|
||||||
let proto_network = c.compile_single(scoped_network)?;
|
let proto_network = match c.compile_single(scoped_network) {
|
||||||
|
Ok(network) => network,
|
||||||
|
Err(e) => return (Err(e), monitor_nodes),
|
||||||
|
};
|
||||||
|
|
||||||
assert_ne!(proto_network.nodes.len(), 0, "No protonodes exist?");
|
assert_ne!(proto_network.nodes.len(), 0, "No protonodes exist?");
|
||||||
if let Err(e) = self.executor.update(proto_network).await {
|
if let Err(e) = self.executor.update(proto_network).await {
|
||||||
error!("Failed to update executor:\n{e}");
|
error!("Failed to update executor:\n{e}");
|
||||||
return Err(e);
|
return (Err(e), monitor_nodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
use graph_craft::graphene_compiler::Executor;
|
use graph_craft::graphene_compiler::Executor;
|
||||||
@@ -215,7 +226,11 @@ impl NodeRuntime {
|
|||||||
Some(t) if t == concrete!(()) => (&self.executor).execute(()).await.map_err(|e| e.to_string()),
|
Some(t) if t == concrete!(()) => (&self.executor).execute(()).await.map_err(|e| e.to_string()),
|
||||||
Some(t) => Err(format!("Invalid input type {t:?}")),
|
Some(t) => Err(format!("Invalid input type {t:?}")),
|
||||||
_ => Err("No input type".to_string()),
|
_ => Err("No input type".to_string()),
|
||||||
}?;
|
};
|
||||||
|
let result = match result {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(e) => return (Err(e), monitor_nodes),
|
||||||
|
};
|
||||||
|
|
||||||
if let TaggedValue::SurfaceFrame(SurfaceFrame { surface_id, transform: _ }) = result {
|
if let TaggedValue::SurfaceFrame(SurfaceFrame { surface_id, transform: _ }) = result {
|
||||||
let old_id = self.canvas_cache.insert(path.to_vec(), surface_id);
|
let old_id = self.canvas_cache.insert(path.to_vec(), surface_id);
|
||||||
@@ -227,7 +242,7 @@ impl NodeRuntime {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(result)
|
(Ok((result)), monitor_nodes)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Recomputes the thumbnails for the layers in the graph, modifying the state and updating the UI.
|
/// Recomputes the thumbnails for the layers in the graph, modifying the state and updating the UI.
|
||||||
|
|||||||
@@ -652,7 +652,7 @@ impl JsEditorHandle {
|
|||||||
#[wasm_bindgen(js_name = selectNodes)]
|
#[wasm_bindgen(js_name = selectNodes)]
|
||||||
pub fn select_nodes(&self, nodes: Option<Vec<u64>>) {
|
pub fn select_nodes(&self, nodes: Option<Vec<u64>>) {
|
||||||
let nodes = nodes.unwrap_or_default();
|
let nodes = nodes.unwrap_or_default();
|
||||||
let message = NodeGraphMessage::SetSelectNodes { nodes };
|
let message = NodeGraphMessage::SetSelectedNodes { nodes };
|
||||||
self.dispatch(message);
|
self.dispatch(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -41,6 +41,9 @@ pub struct DocumentNode {
|
|||||||
pub metadata: DocumentNodeMetadata,
|
pub metadata: DocumentNodeMetadata,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub skip_deduplication: bool,
|
pub skip_deduplication: bool,
|
||||||
|
/// Used as a hash of the graph input where applicable. This ensures that protonodes that depend on the graph's input are always regenerated.
|
||||||
|
#[serde(default)]
|
||||||
|
pub hash: u64,
|
||||||
pub path: Option<Vec<NodeId>>,
|
pub path: Option<Vec<NodeId>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,6 +106,7 @@ impl DocumentNode {
|
|||||||
construction_args: args,
|
construction_args: args,
|
||||||
document_node_path: self.path.unwrap_or(Vec::new()),
|
document_node_path: self.path.unwrap_or(Vec::new()),
|
||||||
skip_deduplication: self.skip_deduplication,
|
skip_deduplication: self.skip_deduplication,
|
||||||
|
hash: self.hash,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -197,6 +197,7 @@ pub struct ProtoNode {
|
|||||||
pub identifier: NodeIdentifier,
|
pub identifier: NodeIdentifier,
|
||||||
pub document_node_path: Vec<NodeId>,
|
pub document_node_path: Vec<NodeId>,
|
||||||
pub skip_deduplication: bool,
|
pub skip_deduplication: bool,
|
||||||
|
pub hash: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A ProtoNodeInput represents the input of a node in a ProtoNetwork.
|
/// A ProtoNodeInput represents the input of a node in a ProtoNetwork.
|
||||||
@@ -235,6 +236,7 @@ impl ProtoNode {
|
|||||||
if self.skip_deduplication {
|
if self.skip_deduplication {
|
||||||
self.document_node_path.hash(&mut hasher);
|
self.document_node_path.hash(&mut hasher);
|
||||||
}
|
}
|
||||||
|
self.hash.hash(&mut hasher);
|
||||||
std::mem::discriminant(&self.input).hash(&mut hasher);
|
std::mem::discriminant(&self.input).hash(&mut hasher);
|
||||||
match self.input {
|
match self.input {
|
||||||
ProtoNodeInput::None => (),
|
ProtoNodeInput::None => (),
|
||||||
@@ -256,6 +258,7 @@ impl ProtoNode {
|
|||||||
input: ProtoNodeInput::None,
|
input: ProtoNodeInput::None,
|
||||||
document_node_path: path,
|
document_node_path: path,
|
||||||
skip_deduplication: false,
|
skip_deduplication: false,
|
||||||
|
hash: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -361,6 +364,7 @@ impl ProtoNetwork {
|
|||||||
input,
|
input,
|
||||||
document_node_path: path,
|
document_node_path: path,
|
||||||
skip_deduplication: false,
|
skip_deduplication: false,
|
||||||
|
hash: 0,
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
|
|
||||||
|
|||||||
@@ -102,6 +102,7 @@ impl BorrowTree {
|
|||||||
old_nodes.remove(&id);
|
old_nodes.remove(&id);
|
||||||
}
|
}
|
||||||
self.source_map.retain(|_, nid| !old_nodes.contains(nid));
|
self.source_map.retain(|_, nid| !old_nodes.contains(nid));
|
||||||
|
self.nodes.retain(|nid, _| !old_nodes.contains(nid));
|
||||||
Ok(old_nodes.into_iter().collect())
|
Ok(old_nodes.into_iter().collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -730,9 +730,47 @@ fn node_registry() -> HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstruct
|
|||||||
register_node!(graphene_core::vector::RepeatNode<_, _>, input: VectorData, params: [DVec2, u32]),
|
register_node!(graphene_core::vector::RepeatNode<_, _>, input: VectorData, params: [DVec2, u32]),
|
||||||
register_node!(graphene_core::vector::BoundingBoxNode, input: VectorData, params: []),
|
register_node!(graphene_core::vector::BoundingBoxNode, input: VectorData, params: []),
|
||||||
register_node!(graphene_core::vector::CircularRepeatNode<_, _, _>, input: VectorData, params: [f32, f32, u32]),
|
register_node!(graphene_core::vector::CircularRepeatNode<_, _, _>, input: VectorData, params: [f32, f32, u32]),
|
||||||
register_node!(graphene_core::transform::CullNode<_>, input: Footprint, params: [VectorData]),
|
vec![(
|
||||||
|
NodeIdentifier::new("graphene_core::transform::CullNode<_>"),
|
||||||
|
|args| {
|
||||||
|
Box::pin(async move {
|
||||||
|
let mut args = args.clone();
|
||||||
|
args.reverse();
|
||||||
|
let node = <graphene_core::transform::CullNode<_>>::new(graphene_std::any::input_node::<VectorData>(args.pop().expect("Not enough arguments provided to construct node")));
|
||||||
|
let any: DynAnyNode<Footprint, _, _> = graphene_std::any::DynAnyNode::new(node);
|
||||||
|
Box::new(any) as Box<dyn for<'i> NodeIO<'i, graph_craft::proto::Any<'i>, Output = (core::pin::Pin<Box<dyn core::future::Future<Output = graph_craft::proto::Any<'i>> + 'i>>)> + '_>
|
||||||
|
})
|
||||||
|
},
|
||||||
|
{
|
||||||
|
let node = <graphene_core::transform::CullNode<_>>::new((graphene_std::any::PanicNode::<(), VectorData>::new()));
|
||||||
|
let params = vec![fn_type!((), VectorData)];
|
||||||
|
let mut node_io = <graphene_core::transform::CullNode<_> as NodeIO<'_, Footprint>>::to_node_io(&node, params);
|
||||||
|
node_io.input = concrete!(<Footprint as StaticType>::Static);
|
||||||
|
node_io
|
||||||
|
},
|
||||||
|
)],
|
||||||
register_node!(graphene_core::transform::CullNode<_>, input: Footprint, params: [graphene_core::Artboard]),
|
register_node!(graphene_core::transform::CullNode<_>, input: Footprint, params: [graphene_core::Artboard]),
|
||||||
register_node!(graphene_core::transform::CullNode<_>, input: Footprint, params: [graphene_core::GraphicGroup]),
|
vec![(
|
||||||
|
NodeIdentifier::new("graphene_core::transform::CullNode<_>"),
|
||||||
|
|args| {
|
||||||
|
Box::pin(async move {
|
||||||
|
let mut args = args.clone();
|
||||||
|
args.reverse();
|
||||||
|
let node = <graphene_core::transform::CullNode<_>>::new(graphene_std::any::input_node::<graphene_core::GraphicGroup>(
|
||||||
|
args.pop().expect("Not enough arguments provided to construct node"),
|
||||||
|
));
|
||||||
|
let any: DynAnyNode<Footprint, _, _> = graphene_std::any::DynAnyNode::new(node);
|
||||||
|
Box::new(any) as Box<dyn for<'i> NodeIO<'i, graph_craft::proto::Any<'i>, Output = (core::pin::Pin<Box<dyn core::future::Future<Output = graph_craft::proto::Any<'i>> + 'i>>)> + '_>
|
||||||
|
})
|
||||||
|
},
|
||||||
|
{
|
||||||
|
let node = <graphene_core::transform::CullNode<_>>::new((graphene_std::any::PanicNode::<(), graphene_core::GraphicGroup>::new()));
|
||||||
|
let params = vec![fn_type!((), graphene_core::GraphicGroup)];
|
||||||
|
let mut node_io = <graphene_core::transform::CullNode<_> as NodeIO<'_, Footprint>>::to_node_io(&node, params);
|
||||||
|
node_io.input = concrete!(<Footprint as StaticType>::Static);
|
||||||
|
node_io
|
||||||
|
},
|
||||||
|
)],
|
||||||
register_node!(graphene_std::raster::SampleNode<_>, input: Footprint, params: [ImageFrame<Color>]),
|
register_node!(graphene_std::raster::SampleNode<_>, input: Footprint, params: [ImageFrame<Color>]),
|
||||||
register_node!(graphene_std::raster::MandelbrotNode, input: Footprint, params: []),
|
register_node!(graphene_std::raster::MandelbrotNode, input: Footprint, params: []),
|
||||||
register_node!(graphene_core::vector::ResamplePoints<_>, input: VectorData, params: [f64]),
|
register_node!(graphene_core::vector::ResamplePoints<_>, input: VectorData, params: [f64]),
|
||||||
|
|||||||
Reference in New Issue
Block a user