mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-25 03:18:11 +08:00
Merge branch 'master' into merge_point
This commit is contained in:
+1
-4
@@ -16,7 +16,6 @@ wasm = ["wasm-bindgen", "graphene-std/wasm"]
|
||||
gpu = ["interpreted-executor/gpu", "wgpu-executor"]
|
||||
resvg = ["graphene-std/resvg"]
|
||||
vello = ["graphene-std/vello", "resvg"]
|
||||
ron = []
|
||||
|
||||
[dependencies]
|
||||
# Local dependencies
|
||||
@@ -45,9 +44,7 @@ once_cell = { workspace = true }
|
||||
web-sys = { workspace = true }
|
||||
vello = { workspace = true }
|
||||
base64 = { workspace = true }
|
||||
|
||||
# Required dependencies
|
||||
spin = "0.9.8"
|
||||
spin = { workspace = true }
|
||||
|
||||
# Optional local dependencies
|
||||
wgpu-executor = { workspace = true, optional = true }
|
||||
|
||||
@@ -8,6 +8,7 @@ use crate::messages::portfolio::document::utility_types::nodes::{JsRawBuffer, La
|
||||
use crate::messages::portfolio::document::utility_types::wires::{WirePath, WirePathUpdate};
|
||||
use crate::messages::prelude::*;
|
||||
use crate::messages::tool::utility_types::HintData;
|
||||
use glam::IVec2;
|
||||
use graph_craft::document::NodeId;
|
||||
use graphene_std::raster::Image;
|
||||
use graphene_std::raster::color::Color;
|
||||
@@ -124,12 +125,19 @@ pub enum FrontendMessage {
|
||||
document_id: DocumentId,
|
||||
},
|
||||
UpdateImportsExports {
|
||||
imports: Vec<(FrontendGraphOutput, i32, i32)>,
|
||||
exports: Vec<(FrontendGraphInput, i32, i32)>,
|
||||
#[serde(rename = "addImport")]
|
||||
add_import: Option<(i32, i32)>,
|
||||
#[serde(rename = "addExport")]
|
||||
add_export: Option<(i32, i32)>,
|
||||
/// If the primary import is not visible, then it is None.
|
||||
imports: Vec<Option<FrontendGraphOutput>>,
|
||||
/// If the primary export is not visible, then it is None.
|
||||
exports: Vec<Option<FrontendGraphInput>>,
|
||||
/// The primary import location.
|
||||
#[serde(rename = "importPosition")]
|
||||
import_position: IVec2,
|
||||
/// The primary export location.
|
||||
#[serde(rename = "exportPosition")]
|
||||
export_position: IVec2,
|
||||
/// The document network does not have an add import or export button.
|
||||
#[serde(rename = "addImportExport")]
|
||||
add_import_export: bool,
|
||||
},
|
||||
UpdateInSelectedNetwork {
|
||||
#[serde(rename = "inSelectedNetwork")]
|
||||
|
||||
@@ -17,7 +17,7 @@ use crate::messages::portfolio::document::overlays::utility_types::{OverlaysType
|
||||
use crate::messages::portfolio::document::properties_panel::properties_panel_message_handler::PropertiesPanelMessageContext;
|
||||
use crate::messages::portfolio::document::utility_types::document_metadata::{DocumentMetadata, LayerNodeIdentifier};
|
||||
use crate::messages::portfolio::document::utility_types::misc::{AlignAggregate, AlignAxis, DocumentMode, FlipAxis, PTZ};
|
||||
use crate::messages::portfolio::document::utility_types::network_interface::{FlowType, InputConnector, NodeTemplate};
|
||||
use crate::messages::portfolio::document::utility_types::network_interface::{FlowType, InputConnector, NodeTemplate, OutputConnector};
|
||||
use crate::messages::portfolio::document::utility_types::nodes::RawBuffer;
|
||||
use crate::messages::portfolio::utility_types::PanelType;
|
||||
use crate::messages::portfolio::utility_types::PersistentData;
|
||||
@@ -2766,7 +2766,7 @@ impl DocumentMessageHandler {
|
||||
});
|
||||
}
|
||||
|
||||
pub fn update_layers_panel_bottom_bar_widgets(&self, layers_panel_open: bool, responses: &mut VecDeque<Message>) {
|
||||
pub fn update_layers_panel_bottom_bar_widgets(&mut self, layers_panel_open: bool, responses: &mut VecDeque<Message>) {
|
||||
if !layers_panel_open {
|
||||
return;
|
||||
}
|
||||
@@ -2776,6 +2776,7 @@ impl DocumentMessageHandler {
|
||||
let selected_layer = selected_layers.next();
|
||||
let has_selection = selected_layer.is_some();
|
||||
let has_multiple_selection = selected_layers.next().is_some();
|
||||
for _ in selected_layers {}
|
||||
|
||||
let widgets = vec![
|
||||
PopoverButton::new()
|
||||
@@ -2789,7 +2790,7 @@ impl DocumentMessageHandler {
|
||||
let graph_layer = graph_modification_utils::NodeGraphLayer::new(layer, &self.network_interface);
|
||||
let node_type = graph_layer.horizontal_layer_flow().nth(1);
|
||||
if let Some(node_id) = node_type {
|
||||
let (output_type, _) = self.network_interface.output_type(&node_id, 0, &self.selection_network_path);
|
||||
let (output_type, _) = self.network_interface.output_type(&OutputConnector::node(node_id, 0), &self.selection_network_path);
|
||||
Some(format!("type:{}", output_type.nested_type()))
|
||||
} else {
|
||||
None
|
||||
|
||||
@@ -382,7 +382,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
||||
inputs: vec![
|
||||
NodeInput::network(graphene_std::Type::Fn(Box::new(concrete!(Context)), Box::new(concrete!(Table<Artboard>))), 0),
|
||||
NodeInput::node(NodeId(2), 0),
|
||||
NodeInput::Reflection(graph_craft::document::DocumentNodeMetadata::DocumentNodePath),
|
||||
],
|
||||
implementation: DocumentNodeImplementation::ProtoNode(graphic::extend::IDENTIFIER),
|
||||
..Default::default()
|
||||
@@ -771,6 +770,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
||||
document_node: DocumentNode {
|
||||
implementation: DocumentNodeImplementation::Network(NodeNetwork {
|
||||
exports: vec![
|
||||
NodeInput::value(TaggedValue::None, false),
|
||||
NodeInput::node(NodeId(0), 0),
|
||||
NodeInput::node(NodeId(1), 0),
|
||||
NodeInput::node(NodeId(2), 0),
|
||||
@@ -825,8 +825,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
||||
},
|
||||
persistent_node_metadata: DocumentNodePersistentMetadata {
|
||||
input_metadata: vec![("Image", "TODO").into()],
|
||||
output_names: vec!["Red".to_string(), "Green".to_string(), "Blue".to_string(), "Alpha".to_string()],
|
||||
has_primary_output: false,
|
||||
output_names: vec!["".to_string(), "Red".to_string(), "Green".to_string(), "Blue".to_string(), "Alpha".to_string()],
|
||||
network_metadata: Some(NodeNetworkMetadata {
|
||||
persistent_metadata: NodeNetworkPersistentMetadata {
|
||||
node_metadata: [
|
||||
@@ -883,7 +882,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
||||
node_template: NodeTemplate {
|
||||
document_node: DocumentNode {
|
||||
implementation: DocumentNodeImplementation::Network(NodeNetwork {
|
||||
exports: vec![NodeInput::node(NodeId(0), 0), NodeInput::node(NodeId(1), 0)],
|
||||
exports: vec![NodeInput::value(TaggedValue::None, false), NodeInput::node(NodeId(0), 0), NodeInput::node(NodeId(1), 0)],
|
||||
nodes: [
|
||||
DocumentNode {
|
||||
inputs: vec![NodeInput::network(concrete!(Table<Raster<CPU>>), 0), NodeInput::value(TaggedValue::XY(XY::X), false)],
|
||||
@@ -905,13 +904,12 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
||||
|
||||
..Default::default()
|
||||
}),
|
||||
inputs: vec![NodeInput::value(TaggedValue::Raster(Default::default()), true)],
|
||||
inputs: vec![NodeInput::value(TaggedValue::DVec2(DVec2::ZERO), true)],
|
||||
..Default::default()
|
||||
},
|
||||
persistent_node_metadata: DocumentNodePersistentMetadata {
|
||||
input_metadata: vec![("Vec2", "TODO").into()],
|
||||
output_names: vec!["X".to_string(), "Y".to_string()],
|
||||
has_primary_output: false,
|
||||
output_names: vec!["".to_string(), "X".to_string(), "Y".to_string()],
|
||||
network_metadata: Some(NodeNetworkMetadata {
|
||||
persistent_metadata: NodeNetworkPersistentMetadata {
|
||||
node_metadata: [
|
||||
|
||||
-1
@@ -77,7 +77,6 @@ pub(super) fn post_process_nodes(mut custom: Vec<DocumentNodeDefinition>) -> Vec
|
||||
})
|
||||
.collect(),
|
||||
output_names: vec![output_type.to_string()],
|
||||
has_primary_output: true,
|
||||
locked: false,
|
||||
..Default::default()
|
||||
},
|
||||
|
||||
@@ -18,7 +18,11 @@ pub enum NodeGraphMessage {
|
||||
},
|
||||
AddPathNode,
|
||||
AddImport,
|
||||
AddPrimaryImport,
|
||||
AddSecondaryImport,
|
||||
AddExport,
|
||||
AddPrimaryExport,
|
||||
AddSecondaryExport,
|
||||
Init,
|
||||
SelectedNodesUpdated,
|
||||
Copy,
|
||||
@@ -63,6 +67,12 @@ pub enum NodeGraphMessage {
|
||||
set_to_exposed: bool,
|
||||
start_transaction: bool,
|
||||
},
|
||||
ExposeEncapsulatingPrimaryInput {
|
||||
exposed: bool,
|
||||
},
|
||||
ExposePrimaryExport {
|
||||
exposed: bool,
|
||||
},
|
||||
InsertNode {
|
||||
node_id: NodeId,
|
||||
node_template: NodeTemplate,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use super::utility_types::{BoxSelection, ContextMenuInformation, DragStart, FrontendGraphInput, FrontendGraphOutput, FrontendNode};
|
||||
use super::utility_types::{BoxSelection, ContextMenuInformation, DragStart, FrontendNode};
|
||||
use super::{document_node_definitions, node_properties};
|
||||
use crate::consts::GRID_SIZE;
|
||||
use crate::messages::input_mapper::utility_types::macros::action_keys;
|
||||
@@ -127,7 +127,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
||||
responses.add(NodeGraphMessage::SelectedNodesSet { nodes: vec![new_layer_id] });
|
||||
}
|
||||
NodeGraphMessage::AddPathNode => {
|
||||
if let Some(layer) = make_path_editable_is_allowed(network_interface, network_interface.document_metadata()) {
|
||||
if let Some(layer) = make_path_editable_is_allowed(network_interface) {
|
||||
responses.add(NodeGraphMessage::CreateNodeInLayerWithTransaction { node_type: "Path".to_string(), layer });
|
||||
responses.add(EventMessage::SelectionChanged);
|
||||
}
|
||||
@@ -136,10 +136,44 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
||||
network_interface.add_import(graph_craft::document::value::TaggedValue::None, true, -1, "", "", breadcrumb_network_path);
|
||||
responses.add(NodeGraphMessage::SendGraph);
|
||||
}
|
||||
NodeGraphMessage::AddPrimaryImport => {
|
||||
if network_interface.number_of_imports(breadcrumb_network_path) == 0 {
|
||||
responses.add(NodeGraphMessage::AddImport);
|
||||
} else {
|
||||
responses.add(NodeGraphMessage::ExposeEncapsulatingPrimaryInput { exposed: true });
|
||||
}
|
||||
}
|
||||
NodeGraphMessage::AddSecondaryImport => {
|
||||
// If necessary, add a hidden primary import before the secondary import
|
||||
if network_interface.number_of_imports(breadcrumb_network_path) == 0 {
|
||||
responses.add(NodeGraphMessage::AddImport);
|
||||
responses.add(NodeGraphMessage::ExposeEncapsulatingPrimaryInput { exposed: false });
|
||||
}
|
||||
|
||||
// Add the secondary import
|
||||
responses.add(NodeGraphMessage::AddImport);
|
||||
}
|
||||
NodeGraphMessage::AddExport => {
|
||||
network_interface.add_export(graph_craft::document::value::TaggedValue::None, -1, "", breadcrumb_network_path);
|
||||
responses.add(NodeGraphMessage::SendGraph);
|
||||
}
|
||||
NodeGraphMessage::AddPrimaryExport => {
|
||||
if network_interface.number_of_exports(breadcrumb_network_path) == 0 {
|
||||
responses.add(NodeGraphMessage::AddExport);
|
||||
} else {
|
||||
responses.add(NodeGraphMessage::ExposePrimaryExport { exposed: true });
|
||||
}
|
||||
}
|
||||
NodeGraphMessage::AddSecondaryExport => {
|
||||
// If necessary, add a hidden primary import before the secondary import
|
||||
if network_interface.number_of_exports(breadcrumb_network_path) == 0 {
|
||||
responses.add(NodeGraphMessage::AddExport);
|
||||
responses.add(NodeGraphMessage::ExposePrimaryExport { exposed: false });
|
||||
}
|
||||
|
||||
// Add the secondary export
|
||||
responses.add(NodeGraphMessage::AddExport);
|
||||
}
|
||||
NodeGraphMessage::Init => {
|
||||
responses.add(BroadcastMessage::SubscribeEvent {
|
||||
on: EventMessage::SelectionChanged,
|
||||
@@ -401,6 +435,77 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
||||
responses.add(DocumentMessage::ZoomCanvasTo100Percent);
|
||||
}
|
||||
}
|
||||
NodeGraphMessage::ExposeEncapsulatingPrimaryInput { exposed } => {
|
||||
let Some((node_id, network_path)) = breadcrumb_network_path.split_last() else {
|
||||
return;
|
||||
};
|
||||
|
||||
let encapsulating_connector = InputConnector::node(*node_id, 0);
|
||||
if !exposed {
|
||||
network_interface.disconnect_input(&encapsulating_connector, network_path);
|
||||
}
|
||||
|
||||
let Some(mut input) = network_interface.input_from_connector(&encapsulating_connector, network_path).cloned() else {
|
||||
return;
|
||||
};
|
||||
|
||||
if let NodeInput::Value { exposed: old_exposed, .. } = &mut input {
|
||||
*old_exposed = exposed;
|
||||
}
|
||||
|
||||
network_interface.set_input(&encapsulating_connector, input, network_path);
|
||||
|
||||
let Some(outward_wires) = network_interface.outward_wires(breadcrumb_network_path) else {
|
||||
log::error!("Could not get outward wires in remove_import");
|
||||
return;
|
||||
};
|
||||
let Some(downstream_connections) = outward_wires.get(&OutputConnector::Import(0)).cloned() else {
|
||||
log::error!("Could not get outward wires for import in remove_import");
|
||||
return;
|
||||
};
|
||||
|
||||
// Disconnect all connections in the encapsulating network
|
||||
for downstream_connection in &downstream_connections {
|
||||
network_interface.disconnect_input(downstream_connection, breadcrumb_network_path);
|
||||
}
|
||||
|
||||
responses.add(NodeGraphMessage::UpdateImportsExports);
|
||||
responses.add(NodeGraphMessage::SendWires);
|
||||
}
|
||||
NodeGraphMessage::ExposePrimaryExport { exposed } => {
|
||||
let export_connector: InputConnector = InputConnector::Export(0);
|
||||
if !exposed {
|
||||
network_interface.disconnect_input(&export_connector, breadcrumb_network_path);
|
||||
}
|
||||
|
||||
let Some(mut input) = network_interface.input_from_connector(&export_connector, breadcrumb_network_path).cloned() else {
|
||||
return;
|
||||
};
|
||||
|
||||
if let NodeInput::Value { exposed: old_exposed, .. } = &mut input {
|
||||
*old_exposed = exposed;
|
||||
}
|
||||
|
||||
network_interface.set_input(&export_connector, input, breadcrumb_network_path);
|
||||
|
||||
// Disconnect all connections in the encapsulating network
|
||||
if let Some((encapsulating_node, encapsulating_path)) = breadcrumb_network_path.split_last() {
|
||||
let Some(outward_wires) = network_interface.outward_wires(encapsulating_path) else {
|
||||
log::error!("Could not get outward wires in remove_import");
|
||||
return;
|
||||
};
|
||||
let Some(downstream_connections) = outward_wires.get(&OutputConnector::node(*encapsulating_node, 0)).cloned() else {
|
||||
log::error!("Could not get outward wires for import in remove_import");
|
||||
return;
|
||||
};
|
||||
|
||||
for downstream_connection in &downstream_connections {
|
||||
network_interface.disconnect_input(downstream_connection, encapsulating_path);
|
||||
}
|
||||
}
|
||||
|
||||
responses.add(NodeGraphMessage::UpdateImportsExports);
|
||||
}
|
||||
NodeGraphMessage::InsertNode { node_id, node_template } => {
|
||||
network_interface.insert_node(node_id, node_template, selection_network_path);
|
||||
}
|
||||
@@ -706,21 +811,21 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
||||
return;
|
||||
};
|
||||
|
||||
if modify_import_export.add_import_export.clicked_input_port_from_point(node_graph_point).is_some() {
|
||||
if let Some(remove_import_index) = modify_import_export.remove_imports_exports.clicked_output_port_from_point(node_graph_point) {
|
||||
responses.add(DocumentMessage::AddTransaction);
|
||||
responses.add(NodeGraphMessage::AddExport);
|
||||
return;
|
||||
} else if modify_import_export.add_import_export.clicked_output_port_from_point(node_graph_point).is_some() {
|
||||
responses.add(DocumentMessage::AddTransaction);
|
||||
responses.add(NodeGraphMessage::AddImport);
|
||||
return;
|
||||
} else if let Some(remove_import_index) = modify_import_export.remove_imports_exports.clicked_output_port_from_point(node_graph_point) {
|
||||
responses.add(DocumentMessage::AddTransaction);
|
||||
responses.add(NodeGraphMessage::RemoveImport { import_index: remove_import_index });
|
||||
if remove_import_index == 0 {
|
||||
responses.add(NodeGraphMessage::ExposeEncapsulatingPrimaryInput { exposed: false })
|
||||
} else {
|
||||
responses.add(NodeGraphMessage::RemoveImport { import_index: remove_import_index });
|
||||
}
|
||||
return;
|
||||
} else if let Some(remove_export_index) = modify_import_export.remove_imports_exports.clicked_input_port_from_point(node_graph_point) {
|
||||
responses.add(DocumentMessage::AddTransaction);
|
||||
responses.add(NodeGraphMessage::RemoveExport { export_index: remove_export_index });
|
||||
if remove_export_index == 0 {
|
||||
responses.add(NodeGraphMessage::ExposePrimaryExport { exposed: false })
|
||||
} else {
|
||||
responses.add(NodeGraphMessage::RemoveExport { export_index: remove_export_index });
|
||||
}
|
||||
return;
|
||||
} else if let Some(move_import_index) = modify_import_export.reorder_imports_exports.clicked_output_port_from_point(node_graph_point) {
|
||||
responses.add(DocumentMessage::StartTransaction);
|
||||
@@ -756,10 +861,8 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
||||
}
|
||||
|
||||
// Alt-click sets the clicked node as previewed
|
||||
if alt_click {
|
||||
if let Some(clicked_node) = clicked_id {
|
||||
self.preview_on_mouse_up = Some(clicked_node);
|
||||
}
|
||||
if alt_click && let Some(clicked_node) = clicked_id {
|
||||
self.preview_on_mouse_up = Some(clicked_node);
|
||||
}
|
||||
|
||||
// Begin moving an existing wire
|
||||
@@ -785,14 +888,8 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
||||
self.initial_disconnecting = false;
|
||||
|
||||
self.wire_in_progress_from_connector = network_interface.output_position(&clicked_output, selection_network_path);
|
||||
if let Some((output_type, source)) = clicked_output
|
||||
.node_id()
|
||||
.map(|node_id| network_interface.output_type(&node_id, clicked_output.index(), breadcrumb_network_path))
|
||||
{
|
||||
self.wire_in_progress_type = FrontendGraphDataType::displayed_type(&output_type, &source);
|
||||
} else {
|
||||
self.wire_in_progress_type = FrontendGraphDataType::General;
|
||||
}
|
||||
let (output_type, source) = &network_interface.output_type(&clicked_output, breadcrumb_network_path);
|
||||
self.wire_in_progress_type = FrontendGraphDataType::displayed_type(output_type, source);
|
||||
|
||||
self.update_node_graph_hints(responses);
|
||||
return;
|
||||
@@ -1023,7 +1120,8 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
||||
};
|
||||
(position > point.y).then_some(*index)
|
||||
})
|
||||
.unwrap_or(modify_import_export.reorder_imports_exports.output_ports().count()),
|
||||
.filter(|end_index| *end_index > 0) // An import cannot be reordered to be the primary
|
||||
.unwrap_or_else(|| modify_import_export.reorder_imports_exports.output_ports().count() + 1),
|
||||
);
|
||||
responses.add(FrontendMessage::UpdateImportReorderIndex { index: self.end_index });
|
||||
} else if self.reordering_export.is_some() {
|
||||
@@ -1043,7 +1141,8 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
||||
};
|
||||
(position > point.y).then_some(*index)
|
||||
})
|
||||
.unwrap_or(modify_import_export.reorder_imports_exports.input_ports().count()),
|
||||
.filter(|end_index| *end_index > 0) // An export cannot be reordered to be the primary
|
||||
.unwrap_or_else(|| modify_import_export.reorder_imports_exports.input_ports().count() + 1),
|
||||
);
|
||||
responses.add(FrontendMessage::UpdateExportReorderIndex { index: self.end_index });
|
||||
}
|
||||
@@ -1098,27 +1197,27 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
||||
responses.add(NodeGraphMessage::RunDocumentGraph);
|
||||
|
||||
responses.add(NodeGraphMessage::SendGraph);
|
||||
} else if output_connector.is_some() && input_connector.is_none() && !self.initial_disconnecting {
|
||||
} else if !self.initial_disconnecting
|
||||
&& input_connector.is_none()
|
||||
&& let Some(output_connector) = output_connector
|
||||
{
|
||||
// If the add node menu is already open, we don't want to open it again
|
||||
if self.context_menu.is_some() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the output types from the network interface
|
||||
let (output_type, type_source) = network_interface.output_type(&output_connector, selection_network_path);
|
||||
let Some(network_metadata) = network_interface.network_metadata(selection_network_path) else {
|
||||
warn!("No network_metadata");
|
||||
return;
|
||||
};
|
||||
// Get the compatible type from the output connector
|
||||
let compatible_type = output_connector.and_then(|output_connector| {
|
||||
output_connector.node_id().and_then(|node_id| {
|
||||
// Get the output types from the network interface
|
||||
let (output_type, type_source) = network_interface.output_type(&node_id, output_connector.index(), selection_network_path);
|
||||
|
||||
match type_source {
|
||||
TypeSource::RandomProtonodeImplementation | TypeSource::Error(_) => None,
|
||||
_ => Some(format!("type:{}", output_type.nested_type())),
|
||||
}
|
||||
})
|
||||
});
|
||||
let compatible_type = match type_source {
|
||||
TypeSource::RandomProtonodeImplementation | TypeSource::Error(_) => None,
|
||||
_ => Some(format!("type:{}", output_type.nested_type())),
|
||||
};
|
||||
|
||||
let appear_right_of_mouse = if ipp.mouse.position.x > ipp.viewport_bounds.size().x - 173. { -173. } else { 0. };
|
||||
let appear_above_mouse = if ipp.mouse.position.y > ipp.viewport_bounds.size().y - 34. { -34. } else { 0. };
|
||||
let node_graph_shift = DVec2::new(appear_right_of_mouse, appear_above_mouse) / network_metadata.persistent_metadata.navigation_metadata.node_graph_to_viewport.matrix2.x_axis.x;
|
||||
@@ -1442,11 +1541,13 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
||||
}
|
||||
NodeGraphMessage::RemoveImport { import_index: usize } => {
|
||||
network_interface.remove_import(usize, selection_network_path);
|
||||
responses.add(NodeGraphMessage::UpdateImportsExports);
|
||||
responses.add(NodeGraphMessage::SendGraph);
|
||||
responses.add(NodeGraphMessage::RunDocumentGraph);
|
||||
}
|
||||
NodeGraphMessage::RemoveExport { export_index: usize } => {
|
||||
network_interface.remove_export(usize, selection_network_path);
|
||||
responses.add(NodeGraphMessage::UpdateImportsExports);
|
||||
responses.add(NodeGraphMessage::SendGraph);
|
||||
responses.add(NodeGraphMessage::RunDocumentGraph);
|
||||
}
|
||||
@@ -1821,7 +1922,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
||||
|
||||
let shift = ipp.keyboard.get(Key::Shift as usize);
|
||||
let Some(selected_nodes) = network_interface.selected_nodes_in_nested_network(selection_network_path) else {
|
||||
log::error!("Could not get selected nodes in PointerMove");
|
||||
log::error!("Could not get selected nodes in UpdateBoxSelection");
|
||||
return;
|
||||
};
|
||||
let previous_selection = selected_nodes.selected_nodes_ref().iter().cloned().collect::<HashSet<_>>();
|
||||
@@ -1850,29 +1951,25 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
||||
}
|
||||
}
|
||||
NodeGraphMessage::UpdateImportsExports => {
|
||||
let imports = network_interface.frontend_imports(breadcrumb_network_path).unwrap_or_default();
|
||||
let exports = network_interface.frontend_exports(breadcrumb_network_path).unwrap_or_default();
|
||||
let add_import = network_interface
|
||||
.frontend_import_export_modify(
|
||||
|modify_import_export_click_target| modify_import_export_click_target.add_import_export.output_ports().collect::<Vec<_>>(),
|
||||
breadcrumb_network_path,
|
||||
)
|
||||
.into_iter()
|
||||
.next();
|
||||
let add_export = network_interface
|
||||
.frontend_import_export_modify(
|
||||
|modify_import_export_click_target| modify_import_export_click_target.add_import_export.input_ports().collect::<Vec<_>>(),
|
||||
breadcrumb_network_path,
|
||||
)
|
||||
.into_iter()
|
||||
.next();
|
||||
let imports = network_interface.frontend_imports(breadcrumb_network_path);
|
||||
let exports = network_interface.frontend_exports(breadcrumb_network_path);
|
||||
|
||||
let Some((import_position, export_position)) = network_interface.import_export_position(breadcrumb_network_path) else {
|
||||
log::error!("Could not get import export positions");
|
||||
return;
|
||||
};
|
||||
|
||||
// Do not show the add import or add export button in the document network;
|
||||
let add_import_export = !breadcrumb_network_path.is_empty();
|
||||
|
||||
responses.add(NodeGraphMessage::UpdateVisibleNodes);
|
||||
responses.add(NodeGraphMessage::SendWires);
|
||||
responses.add(FrontendMessage::UpdateImportsExports {
|
||||
imports,
|
||||
exports,
|
||||
add_import,
|
||||
add_export,
|
||||
import_position,
|
||||
export_position,
|
||||
add_import_export,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1990,11 +2087,6 @@ impl NodeGraphMessageHandler {
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(network) = network_interface.nested_network(breadcrumb_network_path) else {
|
||||
warn!("No network in update_selection_action_buttons");
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(selected_nodes) = network_interface.selected_nodes_in_nested_network(breadcrumb_network_path) else {
|
||||
warn!("No selected nodes in update_selection_action_buttons");
|
||||
return;
|
||||
@@ -2008,6 +2100,7 @@ impl NodeGraphMessageHandler {
|
||||
let mut selected_layers = selected_nodes.selected_layers(network_interface.document_metadata());
|
||||
let selected_layer = selected_layers.next();
|
||||
let has_multiple_selection = selected_layers.next().is_some();
|
||||
for _ in selected_layers {}
|
||||
|
||||
let mut widgets = vec![
|
||||
PopoverButton::new()
|
||||
@@ -2020,7 +2113,7 @@ impl NodeGraphMessageHandler {
|
||||
let graph_layer = graph_modification_utils::NodeGraphLayer::new(layer, network_interface);
|
||||
let node_type = graph_layer.horizontal_layer_flow().nth(1);
|
||||
if let Some(node_id) = node_type {
|
||||
let (output_type, _) = network_interface.output_type(&node_id, 0, &[]);
|
||||
let (output_type, _) = network_interface.output_type(&OutputConnector::node(node_id, 0), &[]);
|
||||
Some(format!("type:{}", output_type.nested_type()))
|
||||
} else {
|
||||
None
|
||||
@@ -2106,6 +2199,11 @@ impl NodeGraphMessageHandler {
|
||||
|
||||
let mut selection = selected_nodes.selected_nodes();
|
||||
let (selection, no_other_selections) = (selection.next(), selection.count() == 0);
|
||||
|
||||
let Some(network) = network_interface.nested_network(breadcrumb_network_path) else {
|
||||
warn!("No network in update_selection_action_buttons");
|
||||
return;
|
||||
};
|
||||
let previewing = if matches!(network_interface.previewing(breadcrumb_network_path), Previewing::Yes { .. }) {
|
||||
network.exports.iter().find_map(|export| {
|
||||
let NodeInput::Node { node_id, .. } = export else { return None };
|
||||
@@ -2338,7 +2436,7 @@ impl NodeGraphMessageHandler {
|
||||
let graph_layer = graph_modification_utils::NodeGraphLayer::new(layer_identifier, context.network_interface);
|
||||
let node_type = graph_layer.horizontal_layer_flow().nth(1);
|
||||
if let Some(node_id) = node_type {
|
||||
let (output_type, _) = context.network_interface.output_type(&node_id, 0, &[]);
|
||||
let (output_type, _) = context.network_interface.output_type(&OutputConnector::node(node_id, 0), &[]);
|
||||
Some(format!("type:{}", output_type.nested_type()))
|
||||
} else {
|
||||
None
|
||||
@@ -2426,110 +2524,48 @@ impl NodeGraphMessageHandler {
|
||||
}
|
||||
|
||||
fn collect_nodes(&self, network_interface: &mut NodeNetworkInterface, breadcrumb_network_path: &[NodeId]) -> Vec<FrontendNode> {
|
||||
let Some(outward_wires) = network_interface.outward_wires(breadcrumb_network_path).cloned() else {
|
||||
return Vec::new();
|
||||
};
|
||||
let mut can_be_layer_lookup = HashSet::new();
|
||||
let mut position_lookup = HashMap::new();
|
||||
let Some(network) = network_interface.nested_network(breadcrumb_network_path) else {
|
||||
log::error!("Could not get nested network when collecting nodes");
|
||||
return Vec::new();
|
||||
};
|
||||
|
||||
for node_id in network.nodes.keys().cloned().collect::<Vec<_>>() {
|
||||
if network_interface.is_eligible_to_be_layer(&node_id, breadcrumb_network_path) {
|
||||
can_be_layer_lookup.insert(node_id);
|
||||
}
|
||||
if let Some(position) = network_interface.position(&node_id, breadcrumb_network_path) {
|
||||
position_lookup.insert(node_id, position);
|
||||
} else {
|
||||
log::error!("Could not get position for node {node_id}");
|
||||
}
|
||||
}
|
||||
|
||||
let mut frontend_inputs_lookup = frontend_inputs_lookup(breadcrumb_network_path, network_interface);
|
||||
let Some(network) = network_interface.nested_network(breadcrumb_network_path) else {
|
||||
log::error!("Could not get nested network when collecting nodes");
|
||||
return Vec::new();
|
||||
};
|
||||
let Some(network_metadata) = network_interface.network_metadata(breadcrumb_network_path) else {
|
||||
log::error!("Could not get network_metadata when collecting nodes");
|
||||
return Vec::new();
|
||||
};
|
||||
|
||||
let mut nodes = Vec::new();
|
||||
for (&node_id, node) in &network.nodes {
|
||||
let node_id_path = [breadcrumb_network_path, (&[node_id])].concat();
|
||||
for (node_id, visible) in network.nodes.iter().map(|(node_id, node)| (*node_id, node.visible)).collect::<Vec<_>>() {
|
||||
let node_id_path = [breadcrumb_network_path, &[node_id]].concat();
|
||||
|
||||
let inputs = frontend_inputs_lookup.remove(&node_id).unwrap_or_default();
|
||||
let primary_input_connector = InputConnector::node(node_id, 0);
|
||||
|
||||
let mut inputs = inputs.into_iter().map(|input| {
|
||||
input.map(|input| FrontendGraphInput {
|
||||
data_type: FrontendGraphDataType::displayed_type(&input.ty, &input.type_source),
|
||||
resolved_type: format!("{:?}", &input.ty),
|
||||
valid_types: input.valid_types.iter().map(|ty| ty.to_string()).collect(),
|
||||
name: input.input_name,
|
||||
description: input.input_description,
|
||||
connected_to: input.output_connector,
|
||||
})
|
||||
});
|
||||
|
||||
let primary_input = inputs.next().flatten();
|
||||
let exposed_inputs = inputs.flatten().collect();
|
||||
|
||||
let (output_type, type_source) = network_interface.output_type(&node_id, 0, breadcrumb_network_path);
|
||||
let frontend_data_type = FrontendGraphDataType::displayed_type(&output_type, &type_source);
|
||||
|
||||
let connected_to = outward_wires.get(&OutputConnector::node(node_id, 0)).cloned().unwrap_or_default();
|
||||
let primary_output = if network_interface.has_primary_output(&node_id, breadcrumb_network_path) {
|
||||
Some(FrontendGraphOutput {
|
||||
data_type: frontend_data_type,
|
||||
name: "Output 1".to_string(),
|
||||
description: String::new(),
|
||||
resolved_type: format!("{output_type:?}"),
|
||||
connected_to,
|
||||
})
|
||||
let primary_input = if network_interface
|
||||
.input_from_connector(&primary_input_connector, breadcrumb_network_path)
|
||||
.is_some_and(|input| input.is_exposed())
|
||||
{
|
||||
network_interface.frontend_input_from_connector(&primary_input_connector, breadcrumb_network_path)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let exposed_inputs = (1..network_interface.number_of_inputs(&node_id, breadcrumb_network_path))
|
||||
.filter_map(|input_index| network_interface.frontend_input_from_connector(&InputConnector::node(node_id, input_index), breadcrumb_network_path))
|
||||
.collect();
|
||||
|
||||
let mut exposed_outputs = Vec::new();
|
||||
for output_index in 0..network_interface.number_of_outputs(&node_id, breadcrumb_network_path) {
|
||||
if output_index == 0 && network_interface.has_primary_output(&node_id, breadcrumb_network_path) {
|
||||
continue;
|
||||
}
|
||||
let (output_type, type_source) = network_interface.output_type(&node_id, 0, breadcrumb_network_path);
|
||||
let data_type = FrontendGraphDataType::displayed_type(&output_type, &type_source);
|
||||
let primary_output = network_interface.frontend_output_from_connector(&OutputConnector::node(node_id, 0), breadcrumb_network_path);
|
||||
|
||||
let Some(node_metadata) = network_metadata.persistent_metadata.node_metadata.get(&node_id) else {
|
||||
log::error!("Could not get node_metadata when getting output for {node_id}");
|
||||
continue;
|
||||
};
|
||||
let output_name = node_metadata
|
||||
.persistent_metadata
|
||||
.output_names
|
||||
.get(output_index)
|
||||
.cloned()
|
||||
.filter(|output_name| !output_name.is_empty())
|
||||
.unwrap_or_else(|| output_type.nested_type().to_string());
|
||||
|
||||
let connected_to = outward_wires.get(&OutputConnector::node(node_id, output_index)).cloned().unwrap_or_default();
|
||||
exposed_outputs.push(FrontendGraphOutput {
|
||||
data_type,
|
||||
name: output_name,
|
||||
description: String::new(),
|
||||
resolved_type: format!("{output_type:?}"),
|
||||
connected_to,
|
||||
});
|
||||
}
|
||||
let Some(network) = network_interface.nested_network(breadcrumb_network_path) else {
|
||||
log::error!("Could not get nested network when collecting nodes");
|
||||
return Vec::new();
|
||||
let exposed_outputs = (1..network_interface.number_of_outputs(&node_id, breadcrumb_network_path))
|
||||
.filter_map(|output_index| network_interface.frontend_output_from_connector(&OutputConnector::node(node_id, output_index), breadcrumb_network_path))
|
||||
.collect();
|
||||
let (primary_output_connected_to_layer, primary_input_connected_to_layer) = if network_interface.is_layer(&node_id, breadcrumb_network_path) {
|
||||
(
|
||||
network_interface.primary_output_connected_to_layer(&node_id, breadcrumb_network_path),
|
||||
network_interface.primary_input_connected_to_layer(&node_id, breadcrumb_network_path),
|
||||
)
|
||||
} else {
|
||||
(false, false)
|
||||
};
|
||||
let is_export = network.exports.first().is_some_and(|export| export.as_node().is_some_and(|export_node_id| node_id == export_node_id));
|
||||
|
||||
let is_export = network_interface
|
||||
.input_from_connector(&InputConnector::Export(0), breadcrumb_network_path)
|
||||
.is_some_and(|export| export.as_node().is_some_and(|export_node_id| node_id == export_node_id));
|
||||
let is_root_node = network_interface.root_node(breadcrumb_network_path).is_some_and(|root_node| root_node.node_id == node_id);
|
||||
|
||||
let Some(position) = position_lookup.get(&node_id).map(|pos| (pos.x, pos.y)) else {
|
||||
let Some(position) = network_interface.position(&node_id, breadcrumb_network_path) else {
|
||||
log::error!("Could not get position for node: {node_id}");
|
||||
continue;
|
||||
};
|
||||
@@ -2555,19 +2591,20 @@ impl NodeGraphMessageHandler {
|
||||
is_layer: network_interface
|
||||
.node_metadata(&node_id, breadcrumb_network_path)
|
||||
.is_some_and(|node_metadata| node_metadata.persistent_metadata.is_layer()),
|
||||
can_be_layer: can_be_layer_lookup.contains(&node_id),
|
||||
can_be_layer: network_interface.is_eligible_to_be_layer(&node_id, breadcrumb_network_path),
|
||||
reference: network_interface.reference(&node_id, breadcrumb_network_path).cloned().unwrap_or_default(),
|
||||
display_name: network_interface.display_name(&node_id, breadcrumb_network_path),
|
||||
primary_input,
|
||||
exposed_inputs,
|
||||
primary_output,
|
||||
exposed_outputs,
|
||||
primary_output_connected_to_layer,
|
||||
primary_input_connected_to_layer,
|
||||
position,
|
||||
previewed,
|
||||
visible: node.visible,
|
||||
visible,
|
||||
locked,
|
||||
errors,
|
||||
ui_only: false,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2717,73 +2754,6 @@ impl NodeGraphMessageHandler {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct InputLookup {
|
||||
input_name: String,
|
||||
input_description: String,
|
||||
ty: Type,
|
||||
type_source: TypeSource,
|
||||
valid_types: Vec<Type>,
|
||||
output_connector: Option<OutputConnector>,
|
||||
}
|
||||
|
||||
type FrontendInputsLookup = HashMap<NodeId, Vec<Option<InputLookup>>>;
|
||||
|
||||
/// Create a lookup hashmap that can be used to create the frontend inputs. This is needed because `input_type` requires a mutable `network_interface`.
|
||||
fn frontend_inputs_lookup(breadcrumb_network_path: &[NodeId], network_interface: &mut NodeNetworkInterface) -> FrontendInputsLookup {
|
||||
let Some(network) = network_interface.nested_network(breadcrumb_network_path) else {
|
||||
return Default::default();
|
||||
};
|
||||
let mut frontend_inputs_lookup = HashMap::new();
|
||||
for (node_id, index, output_connector, is_exposed) in network
|
||||
.nodes
|
||||
.iter()
|
||||
.flat_map(|(node_id, node)| {
|
||||
node.inputs
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, input)| (*node_id, index, OutputConnector::from_input(input), input.is_exposed()))
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
{
|
||||
// Skip not exposed inputs (they still get an entry to help with finding the primary input)
|
||||
let lookup = if !is_exposed {
|
||||
None
|
||||
} else {
|
||||
// Get the name from the metadata here (since it also requires a reference to the `network_interface`)
|
||||
let (input_name, input_description) = network_interface.displayed_input_name_and_description(&node_id, index, breadcrumb_network_path);
|
||||
Some(InputLookup {
|
||||
input_name,
|
||||
input_description,
|
||||
output_connector,
|
||||
..Default::default()
|
||||
})
|
||||
};
|
||||
frontend_inputs_lookup.entry(node_id).or_insert_with(Vec::new).push(lookup);
|
||||
}
|
||||
|
||||
for (&node_id, value) in frontend_inputs_lookup.iter_mut() {
|
||||
for (index, value) in value.iter_mut().enumerate() {
|
||||
// Skip not exposed inputs for efficiency
|
||||
let Some(value) = value else { continue };
|
||||
// Resolve the type (done in a separate loop because it requires a mutable reference to the `network_interface`)
|
||||
let (ty, type_source) = network_interface.input_type(&InputConnector::node(node_id, index), breadcrumb_network_path);
|
||||
value.ty = ty;
|
||||
value.type_source = type_source;
|
||||
}
|
||||
}
|
||||
|
||||
for (&node_id, value) in frontend_inputs_lookup.iter_mut() {
|
||||
for (index, value) in value.iter_mut().enumerate() {
|
||||
// Skip not exposed inputs for efficiency
|
||||
let Some(value) = value else { continue };
|
||||
// Resolve the type (done in a separate loop because it requires a mutable reference to the `network_interface`)
|
||||
value.valid_types = network_interface.valid_input_types(&InputConnector::node(node_id, index), breadcrumb_network_path);
|
||||
}
|
||||
}
|
||||
frontend_inputs_lookup
|
||||
}
|
||||
|
||||
impl Default for NodeGraphMessageHandler {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
|
||||
@@ -173,6 +173,8 @@ pub(crate) fn property_from_type(
|
||||
Some(x) if x == TypeId::of::<String>() => text_widget(default_info).into(),
|
||||
Some(x) if x == TypeId::of::<DVec2>() => vec2_widget(default_info, "X", "Y", "", None, false),
|
||||
Some(x) if x == TypeId::of::<DAffine2>() => transform_widget(default_info, &mut extra_widgets),
|
||||
Some(x) if x == TypeId::of::<Color>() => color_widget(default_info, ColorInput::default()),
|
||||
Some(x) if x == TypeId::of::<Option<Color>>() => color_widget(default_info, ColorInput::default()),
|
||||
// ==========================
|
||||
// PRIMITIVE COLLECTION TYPES
|
||||
// ==========================
|
||||
@@ -926,6 +928,22 @@ pub fn color_widget(parameter_widgets_info: ParameterWidgetsInfo, color_button:
|
||||
|
||||
// Add the color input
|
||||
match &**tagged_value {
|
||||
TaggedValue::ColorNotInTable(color) => widgets.push(
|
||||
color_button
|
||||
.value(FillChoice::Solid(*color))
|
||||
.allow_none(false)
|
||||
.on_update(update_value(|input: &ColorInput| TaggedValue::ColorNotInTable(input.value.as_solid().unwrap()), node_id, index))
|
||||
.on_commit(commit_value)
|
||||
.widget_holder(),
|
||||
),
|
||||
TaggedValue::OptionalColorNotInTable(color) => widgets.push(
|
||||
color_button
|
||||
.value(color.map_or(FillChoice::None, FillChoice::Solid))
|
||||
.allow_none(true)
|
||||
.on_update(update_value(|input: &ColorInput| TaggedValue::OptionalColorNotInTable(input.value.as_solid()), node_id, index))
|
||||
.on_commit(commit_value)
|
||||
.widget_holder(),
|
||||
),
|
||||
TaggedValue::Color(color_table) => widgets.push(
|
||||
color_button
|
||||
.value(match color_table.iter().next() {
|
||||
@@ -965,7 +983,7 @@ pub fn color_widget(parameter_widgets_info: ParameterWidgetsInfo, color_button:
|
||||
.on_commit(commit_value)
|
||||
.widget_holder(),
|
||||
),
|
||||
_ => {}
|
||||
x => warn!("Colour {x:?}"),
|
||||
}
|
||||
|
||||
LayoutGroup::Row { widgets }
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, OutputConnector, TypeSource};
|
||||
use crate::messages::portfolio::document::utility_types::network_interface::TypeSource;
|
||||
use glam::IVec2;
|
||||
use graph_craft::document::NodeId;
|
||||
use graph_craft::document::value::TaggedValue;
|
||||
use graphene_std::Type;
|
||||
@@ -60,7 +61,8 @@ pub struct FrontendGraphInput {
|
||||
#[serde(rename = "validTypes")]
|
||||
pub valid_types: Vec<String>,
|
||||
#[serde(rename = "connectedTo")]
|
||||
pub connected_to: Option<OutputConnector>,
|
||||
/// Either "nothing", "import index {index}", or "{node name} output {output_index}".
|
||||
pub connected_to: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
@@ -68,11 +70,13 @@ pub struct FrontendGraphOutput {
|
||||
#[serde(rename = "dataType")]
|
||||
pub data_type: FrontendGraphDataType,
|
||||
pub name: String,
|
||||
pub description: String,
|
||||
#[serde(rename = "resolvedType")]
|
||||
pub resolved_type: String,
|
||||
pub description: String,
|
||||
/// If connected to an export, it is "export index {index}".
|
||||
/// If connected to a node, it is "{node name} input {input_index}".
|
||||
#[serde(rename = "connectedTo")]
|
||||
pub connected_to: Vec<InputConnector>,
|
||||
pub connected_to: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
@@ -93,13 +97,15 @@ pub struct FrontendNode {
|
||||
pub primary_output: Option<FrontendGraphOutput>,
|
||||
#[serde(rename = "exposedOutputs")]
|
||||
pub exposed_outputs: Vec<FrontendGraphOutput>,
|
||||
pub position: (i32, i32),
|
||||
#[serde(rename = "primaryOutputConnectedToLayer")]
|
||||
pub primary_output_connected_to_layer: bool,
|
||||
#[serde(rename = "primaryInputConnectedToLayer")]
|
||||
pub primary_input_connected_to_layer: bool,
|
||||
pub position: IVec2,
|
||||
pub visible: bool,
|
||||
pub locked: bool,
|
||||
pub previewed: bool,
|
||||
pub errors: Option<String>,
|
||||
#[serde(rename = "uiOnly")]
|
||||
pub ui_only: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+136
@@ -0,0 +1,136 @@
|
||||
use crate::messages::portfolio::document::utility_types::network_interface::{DocumentNodePersistentMetadata, InputMetadata, InputPersistentMetadata, NodeNetworkMetadata, NodeTypePersistentMetadata};
|
||||
use serde_json::Value;
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// Persistent metadata for each node in the network, which must be included when creating, serializing, and deserializing saving a node.
|
||||
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct DocumentNodePersistentMetadataInputNames {
|
||||
pub reference: Option<String>,
|
||||
#[serde(default)]
|
||||
pub display_name: String,
|
||||
pub input_names: Vec<String>,
|
||||
pub output_names: Vec<String>,
|
||||
pub has_primary_output: bool,
|
||||
#[serde(default)]
|
||||
pub locked: bool,
|
||||
#[serde(default)]
|
||||
pub pinned: bool,
|
||||
pub node_type_metadata: NodeTypePersistentMetadata,
|
||||
pub network_metadata: Option<NodeNetworkMetadata>,
|
||||
}
|
||||
|
||||
impl From<DocumentNodePersistentMetadataInputNames> for DocumentNodePersistentMetadata {
|
||||
fn from(old: DocumentNodePersistentMetadataInputNames) -> Self {
|
||||
DocumentNodePersistentMetadata {
|
||||
input_metadata: Vec::new(),
|
||||
..old.into()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct DocumentNodePersistentMetadataPropertiesRow {
|
||||
pub reference: Option<String>,
|
||||
#[serde(default)]
|
||||
pub display_name: String,
|
||||
pub input_properties: Vec<PropertiesRow>,
|
||||
pub output_names: Vec<String>,
|
||||
pub has_primary_output: bool,
|
||||
#[serde(default)]
|
||||
pub locked: bool,
|
||||
#[serde(default)]
|
||||
pub pinned: bool,
|
||||
pub node_type_metadata: NodeTypePersistentMetadata,
|
||||
pub network_metadata: Option<NodeNetworkMetadata>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct PropertiesRow {
|
||||
pub input_data: HashMap<String, Value>,
|
||||
pub widget_override: Option<String>,
|
||||
#[serde(skip)]
|
||||
pub input_name: String,
|
||||
#[serde(skip)]
|
||||
pub input_description: String,
|
||||
}
|
||||
|
||||
impl From<DocumentNodePersistentMetadataPropertiesRow> for DocumentNodePersistentMetadata {
|
||||
fn from(old: DocumentNodePersistentMetadataPropertiesRow) -> Self {
|
||||
let mut input_metadata = Vec::new();
|
||||
for properties_row in old.input_properties {
|
||||
input_metadata.push(InputMetadata {
|
||||
persistent_metadata: InputPersistentMetadata {
|
||||
input_data: properties_row.input_data,
|
||||
widget_override: properties_row.widget_override,
|
||||
input_name: properties_row.input_name,
|
||||
input_description: properties_row.input_description,
|
||||
},
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
DocumentNodePersistentMetadata {
|
||||
reference: old.reference,
|
||||
display_name: old.display_name,
|
||||
input_metadata: Vec::new(),
|
||||
output_names: old.output_names,
|
||||
locked: old.locked,
|
||||
pinned: old.pinned,
|
||||
node_type_metadata: old.node_type_metadata,
|
||||
network_metadata: old.network_metadata,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Persistent metadata for each node in the network, which must be included when creating, serializing, and deserializing saving a node.
|
||||
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct DocumentNodePersistentMetadataHasPrimaryOutput {
|
||||
pub reference: Option<String>,
|
||||
#[serde(default)]
|
||||
pub display_name: String,
|
||||
pub input_metadata: Vec<InputMetadata>,
|
||||
pub output_names: Vec<String>,
|
||||
pub has_primary_output: bool,
|
||||
#[serde(default)]
|
||||
pub locked: bool,
|
||||
#[serde(default)]
|
||||
pub pinned: bool,
|
||||
pub node_type_metadata: NodeTypePersistentMetadata,
|
||||
pub network_metadata: Option<NodeNetworkMetadata>,
|
||||
}
|
||||
|
||||
impl From<DocumentNodePersistentMetadataHasPrimaryOutput> for DocumentNodePersistentMetadata {
|
||||
fn from(old: DocumentNodePersistentMetadataHasPrimaryOutput) -> Self {
|
||||
DocumentNodePersistentMetadata {
|
||||
reference: old.reference,
|
||||
display_name: old.display_name,
|
||||
input_metadata: old.input_metadata,
|
||||
output_names: old.output_names,
|
||||
locked: old.locked,
|
||||
pinned: old.pinned,
|
||||
node_type_metadata: old.node_type_metadata,
|
||||
network_metadata: old.network_metadata,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deserialize_node_persistent_metadata<'de, D>(deserializer: D) -> Result<DocumentNodePersistentMetadata, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
use serde::Deserialize;
|
||||
|
||||
let value = Value::deserialize(deserializer)?;
|
||||
if let Ok(document) = serde_json::from_value::<DocumentNodePersistentMetadataHasPrimaryOutput>(value.clone()) {
|
||||
return Ok(document.into());
|
||||
};
|
||||
if let Ok(document) = serde_json::from_value::<DocumentNodePersistentMetadata>(value.clone()) {
|
||||
return Ok(document);
|
||||
};
|
||||
if let Ok(document) = serde_json::from_value::<DocumentNodePersistentMetadataPropertiesRow>(value.clone()) {
|
||||
return Ok(document.into());
|
||||
};
|
||||
match serde_json::from_value::<DocumentNodePersistentMetadataInputNames>(value.clone()) {
|
||||
Ok(document) => Ok(document.into()),
|
||||
Err(e) => Err(serde::de::Error::custom(e)),
|
||||
}
|
||||
}
|
||||
@@ -230,6 +230,14 @@ const NODE_REPLACEMENTS: &[NodeReplacement<'static>] = &[
|
||||
"graphene_math_nodes::CoordinateValueNode",
|
||||
],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::vector::cut_segments::IDENTIFIER,
|
||||
aliases: &["graphene_core::vector::SplitSegmentsNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::vector::cut_path::IDENTIFIER,
|
||||
aliases: &["graphene_core::vector::SplitPathNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::vector::vec_2_to_point::IDENTIFIER,
|
||||
aliases: &["graphene_core::vector::PositionToPointNode"],
|
||||
|
||||
@@ -109,7 +109,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
|
||||
let metadata = &document.network_interface.document_network_metadata().persistent_metadata;
|
||||
(!metadata.selection_undo_history.is_empty(), !metadata.selection_redo_history.is_empty())
|
||||
};
|
||||
self.menu_bar_message_handler.make_path_editable_is_allowed = make_path_editable_is_allowed(&document.network_interface, document.metadata()).is_some();
|
||||
self.menu_bar_message_handler.make_path_editable_is_allowed = make_path_editable_is_allowed(&mut document.network_interface).is_some();
|
||||
}
|
||||
|
||||
self.menu_bar_message_handler.process_message(message, responses, ());
|
||||
@@ -1144,7 +1144,7 @@ impl PortfolioMessageHandler {
|
||||
}
|
||||
}
|
||||
|
||||
fn load_document(&mut self, new_document: DocumentMessageHandler, document_id: DocumentId, layers_panel_open: bool, responses: &mut VecDeque<Message>, to_front: bool) {
|
||||
fn load_document(&mut self, mut new_document: DocumentMessageHandler, document_id: DocumentId, layers_panel_open: bool, responses: &mut VecDeque<Message>, to_front: bool) {
|
||||
if to_front {
|
||||
self.document_ids.push_front(document_id);
|
||||
} else {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use super::snapping::{SnapCandidatePoint, SnapData, SnapManager};
|
||||
use super::transformation_cage::{BoundingBoxManager, SizeSnapData};
|
||||
use crate::consts::ROTATE_INCREMENT;
|
||||
use crate::messages::portfolio::document::utility_types::document_metadata::{DocumentMetadata, LayerNodeIdentifier};
|
||||
use crate::messages::portfolio::document::utility_types::network_interface::NodeNetworkInterface;
|
||||
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
|
||||
use crate::messages::portfolio::document::utility_types::network_interface::{NodeNetworkInterface, OutputConnector};
|
||||
use crate::messages::portfolio::document::utility_types::transformation::Selected;
|
||||
use crate::messages::prelude::*;
|
||||
use crate::messages::tool::common_functionality::graph_modification_utils::{NodeGraphLayer, get_text};
|
||||
@@ -568,25 +568,21 @@ pub fn find_two_param_best_approximate(p1: DVec2, p3: DVec2, d1: DVec2, d2: DVec
|
||||
(d1 * len1, d2 * len2)
|
||||
}
|
||||
|
||||
pub fn make_path_editable_is_allowed(network_interface: &NodeNetworkInterface, metadata: &DocumentMetadata) -> Option<LayerNodeIdentifier> {
|
||||
pub fn make_path_editable_is_allowed(network_interface: &mut NodeNetworkInterface) -> Option<LayerNodeIdentifier> {
|
||||
// Must have exactly one layer selected
|
||||
let selected_nodes = network_interface.selected_nodes();
|
||||
let mut selected_layers = selected_nodes.selected_layers(metadata);
|
||||
let mut selected_layers = selected_nodes.selected_layers(network_interface.document_metadata());
|
||||
let first_layer = selected_layers.next()?;
|
||||
if selected_layers.next().is_some() {
|
||||
return None;
|
||||
}
|
||||
for _ in selected_layers {}
|
||||
|
||||
// Must be a layer of type Table<Vector>
|
||||
let compatible_type = NodeGraphLayer::new(first_layer, network_interface)
|
||||
.horizontal_layer_flow()
|
||||
.nth(1)
|
||||
.map(|node_id| {
|
||||
let (output_type, _) = network_interface.output_type(&node_id, 0, &[]);
|
||||
output_type.nested_type() == concrete!(Table<Vector>).nested_type()
|
||||
})
|
||||
.unwrap_or_default();
|
||||
if !compatible_type {
|
||||
let node_id = NodeGraphLayer::new(first_layer, network_interface).horizontal_layer_flow().nth(1)?;
|
||||
|
||||
let (output_type, _) = network_interface.output_type(&OutputConnector::node(node_id, 0), &[]);
|
||||
if output_type.nested_type() != concrete!(Table<Vector>).nested_type() {
|
||||
return None;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ use crate::messages::portfolio::document::overlays::utility_types::OverlayContex
|
||||
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
|
||||
use crate::messages::tool::common_functionality::auto_panning::AutoPanning;
|
||||
use crate::messages::tool::common_functionality::compass_rose::Axis;
|
||||
use crate::messages::tool::common_functionality::measure;
|
||||
use crate::messages::tool::common_functionality::resize::Resize;
|
||||
use crate::messages::tool::common_functionality::snapping;
|
||||
use crate::messages::tool::common_functionality::snapping::SnapCandidatePoint;
|
||||
@@ -12,7 +13,7 @@ use crate::messages::tool::common_functionality::snapping::SnapManager;
|
||||
use crate::messages::tool::common_functionality::transformation_cage::*;
|
||||
use graph_craft::document::NodeId;
|
||||
use graphene_std::Artboard;
|
||||
use graphene_std::renderer::Quad;
|
||||
use graphene_std::renderer::{Quad, Rect};
|
||||
use graphene_std::table::Table;
|
||||
|
||||
#[derive(Default, ExtractField)]
|
||||
@@ -241,6 +242,39 @@ impl Fsm for ArtboardToolFsmState {
|
||||
tool_data.bounding_box_manager.take();
|
||||
}
|
||||
|
||||
// Measure with Alt held down between selected artboard and hovered layers/artboards
|
||||
// TODO: Don't use `Key::Alt` directly, instead take it as a variable from the input mappings list like in all other places
|
||||
let alt_pressed = input.keyboard.get(Key::Alt as usize);
|
||||
let quick_measurement_enabled = overlay_context.visibility_settings.quick_measurement();
|
||||
let not_resizing = !matches!(state, ArtboardToolFsmState::ResizingBounds);
|
||||
|
||||
if quick_measurement_enabled && not_resizing && alt_pressed {
|
||||
// Get the selected artboard bounds
|
||||
let selected_artboard_bounds = tool_data.selected_artboard.and_then(|layer| document.metadata().bounding_box_document(layer)).map(Rect::from_box);
|
||||
|
||||
// Find hovered artboard or regular layer
|
||||
let hovered_artboard = ArtboardToolData::hovered_artboard(document, input);
|
||||
let hovered_layer = document.click_xray(input).find(|&layer| !document.network_interface.is_artboard(&layer.to_node(), &[]));
|
||||
|
||||
// Get bounds for the hovered object (prioritize artboards)
|
||||
let hovered_bounds = if let Some(artboard) = hovered_artboard {
|
||||
document.metadata().bounding_box_document(artboard).map(Rect::from_box)
|
||||
} else if let Some(layer) = hovered_layer {
|
||||
document.metadata().bounding_box_document(layer).map(Rect::from_box)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// If both selected artboard and hovered object bounds exist, overlay measurement lines
|
||||
if let (Some(selected_bounds), Some(hovered_bounds)) = (selected_artboard_bounds, hovered_bounds) {
|
||||
// Don't measure if it's the same artboard
|
||||
if selected_artboard_bounds != Some(hovered_bounds) {
|
||||
let document_to_viewport = document.metadata().document_to_viewport;
|
||||
measure::overlay(selected_bounds, hovered_bounds, document_to_viewport, document_to_viewport, &mut overlay_context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tool_data.snap_manager.draw_overlays(SnapData::new(document, input), &mut overlay_context);
|
||||
|
||||
self
|
||||
|
||||
@@ -3171,7 +3171,7 @@ impl Fsm for PathToolFsmState {
|
||||
colinear,
|
||||
};
|
||||
|
||||
tool_data.make_path_editable_is_allowed = make_path_editable_is_allowed(&document.network_interface, document.metadata()).is_some();
|
||||
tool_data.make_path_editable_is_allowed = make_path_editable_is_allowed(&mut document.network_interface).is_some();
|
||||
tool_data.update_selection_status(shape_editor, document);
|
||||
tool_data.update_merge_point_toggle(shape_editor, document, tool_action_data.preferences.vector_meshes);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user