Fix assorted Clippy lints (#3390)

This commit is contained in:
Dennis Kobert
2025-11-17 07:58:31 +00:00
committed by GitHub
parent ebb434692a
commit 181c30bc0a
30 changed files with 669 additions and 694 deletions
+4 -4
View File
@@ -290,10 +290,10 @@ impl Dispatcher {
list.extend(self.message_handlers.input_preprocessor_message_handler.actions()); list.extend(self.message_handlers.input_preprocessor_message_handler.actions());
list.extend(self.message_handlers.key_mapping_message_handler.actions()); list.extend(self.message_handlers.key_mapping_message_handler.actions());
list.extend(self.message_handlers.debug_message_handler.actions()); list.extend(self.message_handlers.debug_message_handler.actions());
if let Some(document) = self.message_handlers.portfolio_message_handler.active_document() { if let Some(document) = self.message_handlers.portfolio_message_handler.active_document()
if !document.graph_view_overlay_open { && !document.graph_view_overlay_open
list.extend(self.message_handlers.tool_message_handler.actions()); {
} list.extend(self.message_handlers.tool_message_handler.actions());
} }
list.extend(self.message_handlers.portfolio_message_handler.actions()); list.extend(self.message_handlers.portfolio_message_handler.actions());
list list
@@ -156,10 +156,10 @@ impl LayoutMessageHandler {
let blue = color.get("blue").and_then(|x| x.as_f64()).map(|x| x as f32); let blue = color.get("blue").and_then(|x| x.as_f64()).map(|x| x as f32);
let alpha = color.get("alpha").and_then(|x| x.as_f64()).map(|x| x as f32); let alpha = color.get("alpha").and_then(|x| x.as_f64()).map(|x| x as f32);
if let (Some(red), Some(green), Some(blue), Some(alpha)) = (red, green, blue, alpha) { if let (Some(red), Some(green), Some(blue), Some(alpha)) = (red, green, blue, alpha)
if let Some(color) = Color::from_rgbaf32(red, green, blue, alpha) { && let Some(color) = Color::from_rgbaf32(red, green, blue, alpha)
return Some(color); {
} return Some(color);
} }
None None
}; };
@@ -508,23 +508,22 @@ impl WidgetHolder {
/// Diffing updates self (where self is old) based on new, updating the list of modifications as it does so. /// Diffing updates self (where self is old) based on new, updating the list of modifications as it does so.
pub fn diff(&mut self, new: Self, widget_path: &mut [usize], widget_diffs: &mut Vec<WidgetDiff>) { pub fn diff(&mut self, new: Self, widget_path: &mut [usize], widget_diffs: &mut Vec<WidgetDiff>) {
if let (Widget::PopoverButton(button1), Widget::PopoverButton(button2)) = (&mut self.widget, &new.widget) { if let (Widget::PopoverButton(button1), Widget::PopoverButton(button2)) = (&mut self.widget, &new.widget)
if button1.disabled == button2.disabled && button1.disabled == button2.disabled
&& button1.style == button2.style && button1.style == button2.style
&& button1.menu_direction == button2.menu_direction && button1.menu_direction == button2.menu_direction
&& button1.icon == button2.icon && button1.icon == button2.icon
&& button1.tooltip == button2.tooltip && button1.tooltip == button2.tooltip
&& button1.tooltip_shortcut == button2.tooltip_shortcut && button1.tooltip_shortcut == button2.tooltip_shortcut
&& button1.popover_min_width == button2.popover_min_width && button1.popover_min_width == button2.popover_min_width
{ {
let mut new_widget_path = widget_path.to_vec(); let mut new_widget_path = widget_path.to_vec();
for (i, (a, b)) in button1.popover_layout.iter_mut().zip(button2.popover_layout.iter()).enumerate() { for (i, (a, b)) in button1.popover_layout.iter_mut().zip(button2.popover_layout.iter()).enumerate() {
new_widget_path.push(i); new_widget_path.push(i);
a.diff(b.clone(), &mut new_widget_path, widget_diffs); a.diff(b.clone(), &mut new_widget_path, widget_diffs);
new_widget_path.pop(); new_widget_path.pop();
}
return;
} }
return;
} }
// If there have been changes to the actual widget (not just the id) // If there have been changes to the actual widget (not just the id)
@@ -621,15 +620,15 @@ impl DiffUpdate {
let apply_shortcut_to_tooltip = |tooltip_shortcut: &mut ActionKeys, tooltip: &mut String| { let apply_shortcut_to_tooltip = |tooltip_shortcut: &mut ActionKeys, tooltip: &mut String| {
let shortcut_text = tooltip_shortcut.to_keys(action_input_mapping); let shortcut_text = tooltip_shortcut.to_keys(action_input_mapping);
if let ActionKeys::Keys(_keys) = tooltip_shortcut { if let ActionKeys::Keys(_keys) = tooltip_shortcut
if !shortcut_text.is_empty() { && !shortcut_text.is_empty()
if !tooltip.is_empty() { {
tooltip.push(' '); if !tooltip.is_empty() {
} tooltip.push(' ');
tooltip.push('(');
tooltip.push_str(&shortcut_text);
tooltip.push(')');
} }
tooltip.push('(');
tooltip.push_str(&shortcut_text);
tooltip.push(')');
} }
}; };
@@ -14,10 +14,10 @@ pub(super) fn post_process_nodes(mut custom: Vec<DocumentNodeDefinition>) -> Vec
.. ..
} = node_template; } = node_template;
if let DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier { name }) = implementation { if let DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier { name }) = implementation
if let Some((new_name, _suffix)) = name.rsplit_once("<") { && let Some((new_name, _suffix)) = name.rsplit_once("<")
*name = Cow::Owned(new_name.to_string()) {
} *name = Cow::Owned(new_name.to_string())
}; };
} }
@@ -1011,10 +1011,11 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
// Disconnect if the wire was previously connected to an input // Disconnect if the wire was previously connected to an input
if let Some(disconnecting) = &self.disconnecting { if let Some(disconnecting) = &self.disconnecting {
let mut disconnect_root_node = false; let mut disconnect_root_node = false;
if let Previewing::Yes { root_node_to_restore } = network_interface.previewing(selection_network_path) { if let Previewing::Yes { root_node_to_restore } = network_interface.previewing(selection_network_path)
if root_node_to_restore.is_some() && *disconnecting == InputConnector::Export(0) { && root_node_to_restore.is_some()
disconnect_root_node = true; && *disconnecting == InputConnector::Export(0)
} {
disconnect_root_node = true;
} }
if disconnect_root_node { if disconnect_root_node {
responses.add(NodeGraphMessage::DisconnectRootNode); responses.add(NodeGraphMessage::DisconnectRootNode);
@@ -1168,13 +1169,13 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
responses.add(NodeGraphMessage::TogglePreview { node_id: preview_node }); responses.add(NodeGraphMessage::TogglePreview { node_id: preview_node });
self.preview_on_mouse_up = None; self.preview_on_mouse_up = None;
} }
if let Some(node_to_deselect) = self.deselect_on_pointer_up.take() { if let Some(node_to_deselect) = self.deselect_on_pointer_up.take()
if !self.drag_start.as_ref().is_some_and(|t| t.1) { && !self.drag_start.as_ref().is_some_and(|t| t.1)
let mut new_selected_nodes = selected_nodes.selected_nodes_ref().clone(); {
new_selected_nodes.remove(node_to_deselect); let mut new_selected_nodes = selected_nodes.selected_nodes_ref().clone();
responses.add(NodeGraphMessage::SelectedNodesSet { nodes: new_selected_nodes }); new_selected_nodes.remove(node_to_deselect);
return; responses.add(NodeGraphMessage::SelectedNodesSet { nodes: new_selected_nodes });
} return;
} }
let point = network_metadata let point = network_metadata
.persistent_metadata .persistent_metadata
@@ -492,7 +492,7 @@ pub fn footprint_widget(parameter_widgets_info: ParameterWidgetsInfo, extra_widg
); );
} }
let widgets = vec![ let widgets = [
LayoutGroup::Row { widgets: location_widgets }, LayoutGroup::Row { widgets: location_widgets },
LayoutGroup::Row { widgets: scale_widgets }, LayoutGroup::Row { widgets: scale_widgets },
LayoutGroup::Row { widgets: resolution_widgets }, LayoutGroup::Row { widgets: resolution_widgets },
@@ -213,10 +213,10 @@ pub fn overlay_options(grid: &GridSnapping) -> Vec<LayoutGroup> {
} }
let update_origin = |grid, update: fn(&mut GridSnapping) -> Option<&mut f64>| { let update_origin = |grid, update: fn(&mut GridSnapping) -> Option<&mut f64>| {
update_val::<NumberInput, _>(grid, move |grid, val| { update_val::<NumberInput, _>(grid, move |grid, val| {
if let Some(val) = val.value { if let Some(val) = val.value
if let Some(update) = update(grid) { && let Some(update) = update(grid)
*update = val; {
} *update = val;
} }
}) })
}; };
@@ -91,16 +91,14 @@ impl DocumentMetadata {
let mut use_local = true; let mut use_local = true;
let graph_layer = graph_modification_utils::NodeGraphLayer::new(layer, network_interface); let graph_layer = graph_modification_utils::NodeGraphLayer::new(layer, network_interface);
if let Some(path_node) = graph_layer.upstream_visible_node_id_from_name_in_layer("Path") { if let Some(path_node) = graph_layer.upstream_visible_node_id_from_name_in_layer("Path")
if let Some(&source) = self.first_element_source_ids.get(&layer.to_node()) { && let Some(&source) = self.first_element_source_ids.get(&layer.to_node())
if !network_interface && !network_interface
.upstream_flow_back_from_nodes(vec![path_node], &[], FlowType::HorizontalFlow) .upstream_flow_back_from_nodes(vec![path_node], &[], FlowType::HorizontalFlow)
.any(|upstream| Some(upstream) == source) .any(|upstream| Some(upstream) == source)
{ {
use_local = false; use_local = false;
info!("Local transform is invalid — using the identity for the local transform instead") info!("Local transform is invalid — using the identity for the local transform instead")
}
}
} }
let local_transform = use_local.then(|| self.local_transforms.get(&layer.to_node()).copied()).flatten().unwrap_or_default(); let local_transform = use_local.then(|| self.local_transforms.get(&layer.to_node()).copied()).flatten().unwrap_or_default();
@@ -79,10 +79,11 @@ impl NodeNetworkInterface {
if let Some(network) = node.implementation.get_network_mut() { if let Some(network) = node.implementation.get_network_mut() {
fix_network(network); fix_network(network);
} }
if let DocumentNodeImplementation::ProtoNode(protonode) = &node.implementation { if let DocumentNodeImplementation::ProtoNode(protonode) = &node.implementation
if protonode.name.contains("PathModifyNode") && node.inputs.len() < 3 { && protonode.name.contains("PathModifyNode")
node.inputs.push(NodeInput::Reflection(graph_craft::document::DocumentNodeMetadata::DocumentNodePath)); && node.inputs.len() < 3
} {
node.inputs.push(NodeInput::Reflection(graph_craft::document::DocumentNodeMetadata::DocumentNodePath));
} }
} }
} }
@@ -1450,21 +1451,20 @@ impl NodeNetworkInterface {
.all_layers() .all_layers()
.filter(|layer| include_artboards || !self.is_artboard(&layer.to_node(), &[])) .filter(|layer| include_artboards || !self.is_artboard(&layer.to_node(), &[]))
.filter_map(|layer| { .filter_map(|layer| {
if !self.is_artboard(&layer.to_node(), &[]) { if !self.is_artboard(&layer.to_node(), &[])
if let Some(artboard_node_identifier) = layer && let Some(artboard_node_identifier) = layer
.ancestors(self.document_metadata()) .ancestors(self.document_metadata())
.find(|ancestor| *ancestor != LayerNodeIdentifier::ROOT_PARENT && self.is_artboard(&ancestor.to_node(), &[])) .find(|ancestor| *ancestor != LayerNodeIdentifier::ROOT_PARENT && self.is_artboard(&ancestor.to_node(), &[]))
{
let artboard = self.document_node(&artboard_node_identifier.to_node(), &[]);
let clip_input = artboard.unwrap().inputs.get(5).unwrap();
if let NodeInput::Value { tagged_value, .. } = clip_input
&& tagged_value.clone().deref() == &TaggedValue::Bool(true)
{ {
let artboard = self.document_node(&artboard_node_identifier.to_node(), &[]); return Some(Quad::clip(
let clip_input = artboard.unwrap().inputs.get(5).unwrap(); self.document_metadata.bounding_box_document(layer).unwrap_or_default(),
if let NodeInput::Value { tagged_value, .. } = clip_input { self.document_metadata.bounding_box_document(artboard_node_identifier).unwrap_or_default(),
if tagged_value.clone().deref() == &TaggedValue::Bool(true) { ));
return Some(Quad::clip(
self.document_metadata.bounding_box_document(layer).unwrap_or_default(),
self.document_metadata.bounding_box_document(artboard_node_identifier).unwrap_or_default(),
));
}
}
} }
} }
self.document_metadata.bounding_box_document(layer) self.document_metadata.bounding_box_document(layer)
@@ -1557,7 +1557,7 @@ impl NodeNetworkInterface {
log::error!("Could not get network or network_metadata in upstream_flow_back_from_nodes"); log::error!("Could not get network or network_metadata in upstream_flow_back_from_nodes");
return FlowIter { return FlowIter {
stack: Vec::new(), stack: Vec::new(),
network: &self.document_network(), network: self.document_network(),
network_metadata: &self.network_metadata, network_metadata: &self.network_metadata,
flow_type: FlowType::UpstreamFlow, flow_type: FlowType::UpstreamFlow,
}; };
@@ -2535,10 +2535,8 @@ impl NodeNetworkInterface {
}; };
// If the node is a layer, then the width and click targets need to be recalculated // If the node is a layer, then the width and click targets need to be recalculated
if is_layer { if is_layer && let NodeTypeTransientMetadata::Layer(layer_metadata) = &mut node_metadata.transient_metadata.node_type_metadata {
if let NodeTypeTransientMetadata::Layer(layer_metadata) = &mut node_metadata.transient_metadata.node_type_metadata { layer_metadata.layer_width.unload();
layer_metadata.layer_width.unload();
}
} }
} }
@@ -3484,17 +3482,17 @@ impl NodeNetworkInterface {
pub fn compute_modified_vector(&self, layer: LayerNodeIdentifier) -> Option<Vector> { pub fn compute_modified_vector(&self, layer: LayerNodeIdentifier) -> Option<Vector> {
let graph_layer = graph_modification_utils::NodeGraphLayer::new(layer, self); let graph_layer = graph_modification_utils::NodeGraphLayer::new(layer, self);
if let Some(path_node) = graph_layer.upstream_visible_node_id_from_name_in_layer("Path") { if let Some(path_node) = graph_layer.upstream_visible_node_id_from_name_in_layer("Path")
if let Some(vector) = self.document_metadata.vector_modify.get(&path_node) { && let Some(vector) = self.document_metadata.vector_modify.get(&path_node)
let mut modified = vector.clone(); {
let mut modified = vector.clone();
let path_node = self.document_network().nodes.get(&path_node); let path_node = self.document_network().nodes.get(&path_node);
let modification_input = path_node.and_then(|node: &DocumentNode| node.inputs.get(1)).and_then(|input| input.as_value()); let modification_input = path_node.and_then(|node: &DocumentNode| node.inputs.get(1)).and_then(|input| input.as_value());
if let Some(TaggedValue::VectorModification(modification)) = modification_input { if let Some(TaggedValue::VectorModification(modification)) = modification_input {
modification.apply(&mut modified); modification.apply(&mut modified);
}
return Some(modified);
} }
return Some(modified);
} }
self.document_metadata self.document_metadata
@@ -3701,10 +3699,11 @@ impl NodeNetworkInterface {
let mut encapsulating_path = network_path.to_vec(); let mut encapsulating_path = network_path.to_vec();
// Set the parent node (if it exists) to be a non layer if it is no longer eligible to be a layer // Set the parent node (if it exists) to be a non layer if it is no longer eligible to be a layer
if let Some(parent_id) = encapsulating_path.pop() { if let Some(parent_id) = encapsulating_path.pop()
if !self.is_eligible_to_be_layer(&parent_id, &encapsulating_path) && self.is_layer(&parent_id, &encapsulating_path) { && !self.is_eligible_to_be_layer(&parent_id, &encapsulating_path)
self.set_to_node_or_layer(&parent_id, &encapsulating_path, false); && self.is_layer(&parent_id, &encapsulating_path)
} {
self.set_to_node_or_layer(&parent_id, &encapsulating_path, false);
}; };
// There will not be an encapsulating node if the network is the document network // There will not be an encapsulating node if the network is the document network
@@ -4170,12 +4169,12 @@ impl NodeNetworkInterface {
/// Used to ensure the display name is the reference name in case it is empty. /// Used to ensure the display name is the reference name in case it is empty.
pub fn validate_display_name_metadata(&mut self, node_id: &NodeId, network_path: &[NodeId]) { pub fn validate_display_name_metadata(&mut self, node_id: &NodeId, network_path: &[NodeId]) {
let Some(metadata) = self.node_metadata_mut(node_id, network_path) else { return }; let Some(metadata) = self.node_metadata_mut(node_id, network_path) else { return };
if metadata.persistent_metadata.display_name.is_empty() { if metadata.persistent_metadata.display_name.is_empty()
if let Some(reference) = metadata.persistent_metadata.reference.clone() { && let Some(reference) = metadata.persistent_metadata.reference.clone()
// Keep the name for merge nodes as empty {
if reference != "Merge" { // Keep the name for merge nodes as empty
metadata.persistent_metadata.display_name = reference; if reference != "Merge" {
} metadata.persistent_metadata.display_name = reference;
} }
} }
} }
@@ -4245,10 +4244,10 @@ impl NodeNetworkInterface {
} }
// If the previous input is connected to a chain node, then set all upstream chain nodes to absolute position // If the previous input is connected to a chain node, then set all upstream chain nodes to absolute position
if let NodeInput::Node { node_id: previous_upstream_id, .. } = &previous_input { if let NodeInput::Node { node_id: previous_upstream_id, .. } = &previous_input
if self.is_chain(previous_upstream_id, network_path) { && self.is_chain(previous_upstream_id, network_path)
self.set_upstream_chain_to_absolute(previous_upstream_id, network_path); {
} self.set_upstream_chain_to_absolute(previous_upstream_id, network_path);
} }
if let NodeInput::Node { node_id: new_upstream_id, .. } = &new_input { if let NodeInput::Node { node_id: new_upstream_id, .. } = &new_input {
// If the new input is connected to a chain node, then break its chain // If the new input is connected to a chain node, then break its chain
@@ -4424,11 +4423,11 @@ impl NodeNetworkInterface {
}; };
// If it is a layer and is connected to a single layer, set its position to stack at its previous y position // If it is a layer and is connected to a single layer, set its position to stack at its previous y position
if old_upstream_node_is_layer && outward_wires.len() == 1 && outward_wires[0].input_index() == 0 { if old_upstream_node_is_layer && outward_wires.len() == 1 && outward_wires[0].input_index() == 0 {
if let Some(downstream_node_id) = outward_wires[0].node_id() { if let Some(downstream_node_id) = outward_wires[0].node_id()
if self.is_layer(&downstream_node_id, network_path) { && self.is_layer(&downstream_node_id, network_path)
self.set_stack_position_calculated_offset(&old_upstream_node_id, &downstream_node_id, network_path); {
self.unload_upstream_node_click_targets(vec![old_upstream_node_id], network_path); self.set_stack_position_calculated_offset(&old_upstream_node_id, &downstream_node_id, network_path);
} self.unload_upstream_node_click_targets(vec![old_upstream_node_id], network_path);
} }
} }
// If it is a node and is eligible to be in a chain, then set it to chain positioning // If it is a node and is eligible to be in a chain, then set it to chain positioning
@@ -4488,18 +4487,16 @@ impl NodeNetworkInterface {
return; return;
}; };
let mut other_outward_wires = outward_wires.iter().filter(|outward_wire| *outward_wire != input_connector); let mut other_outward_wires = outward_wires.iter().filter(|outward_wire| *outward_wire != input_connector);
if let Some(other_outward_wire) = other_outward_wires.next().cloned() { if let Some(other_outward_wire) = other_outward_wires.next().cloned()
if other_outward_wires.next().is_none() { && other_outward_wires.next().is_none()
if let InputConnector::Node { && let InputConnector::Node {
node_id: downstream_node_id, node_id: downstream_node_id,
input_index, input_index,
} = other_outward_wire } = other_outward_wire
{ && self.is_layer(&downstream_node_id, network_path)
if self.is_layer(&downstream_node_id, network_path) && input_index == 0 { && input_index == 0
self.set_stack_position_calculated_offset(upstream_node_id, &downstream_node_id, network_path); {
} self.set_stack_position_calculated_offset(upstream_node_id, &downstream_node_id, network_path);
}
}
} }
} }
} }
@@ -4625,10 +4622,10 @@ impl NodeNetworkInterface {
let Some(downstream_node) = self.document_node(deleted_node_id, network_path) else { continue }; let Some(downstream_node) = self.document_node(deleted_node_id, network_path) else { continue };
let Some(input) = downstream_node.inputs.first() else { continue }; let Some(input) = downstream_node.inputs.first() else { continue };
if let NodeInput::Node { node_id, .. } = input { if let NodeInput::Node { node_id, .. } = input
if *node_id == current_node_id { && *node_id == current_node_id
stack.push(OutputConnector::node(*deleted_node_id, 0)); {
} stack.push(OutputConnector::node(*deleted_node_id, 0));
} }
} }
} }
@@ -4719,12 +4716,12 @@ impl NodeNetworkInterface {
for downstream_input in &downstream_inputs_to_disconnect { for downstream_input in &downstream_inputs_to_disconnect {
self.disconnect_input(downstream_input, network_path); self.disconnect_input(downstream_input, network_path);
// Prevent reconnecting export to import until https://github.com/GraphiteEditor/Graphite/issues/1762 is solved // Prevent reconnecting export to import until https://github.com/GraphiteEditor/Graphite/issues/1762 is solved
if !(matches!(reconnect_to_input, Some(NodeInput::Import { .. })) && matches!(downstream_input, InputConnector::Export(_))) { if !(matches!(reconnect_to_input, Some(NodeInput::Import { .. })) && matches!(downstream_input, InputConnector::Export(_)))
if let Some(reconnect_input) = &reconnect_to_input { && let Some(reconnect_input) = &reconnect_to_input
reconnect_node = reconnect_input.as_node().and_then(|node_id| if self.is_stack(&node_id, network_path) { Some(node_id) } else { None }); {
self.disconnect_input(&InputConnector::node(*node_id, 0), network_path); reconnect_node = reconnect_input.as_node().and_then(|node_id| if self.is_stack(&node_id, network_path) { Some(node_id) } else { None });
self.set_input(downstream_input, reconnect_input.clone(), network_path); self.disconnect_input(&InputConnector::node(*node_id, 0), network_path);
} self.set_input(downstream_input, reconnect_input.clone(), network_path);
} }
} }
@@ -5296,11 +5293,9 @@ impl NodeNetworkInterface {
if let Some(outward_wires) = self if let Some(outward_wires) = self
.outward_wires(network_path) .outward_wires(network_path)
.and_then(|outward_wires| outward_wires.get(&OutputConnector::node(*node_id, 0))) .and_then(|outward_wires| outward_wires.get(&OutputConnector::node(*node_id, 0)))
.cloned() .cloned() && outward_wires.len() == 1
{ {
if outward_wires.len() == 1 { self.try_set_upstream_to_chain(&outward_wires[0], network_path)
self.try_set_upstream_to_chain(&outward_wires[0], network_path)
}
} }
} }
@@ -5400,13 +5395,13 @@ impl NodeNetworkInterface {
}; };
if !shift_without_push { if !shift_without_push {
for node_id in node_ids.clone() { for node_id in node_ids.clone() {
if self.is_layer(&node_id, network_path) { if self.is_layer(&node_id, network_path)
if let Some(owned_nodes) = self.owned_nodes(&node_id, network_path) { && let Some(owned_nodes) = self.owned_nodes(&node_id, network_path)
for owned_node in owned_nodes { {
node_ids.remove(owned_node); for owned_node in owned_nodes {
} node_ids.remove(owned_node);
}; }
} };
} }
} }
@@ -5428,22 +5423,22 @@ impl NodeNetworkInterface {
log::error!("Could not get node metadata for node {node_id} in shift_selected_nodes"); log::error!("Could not get node metadata for node {node_id} in shift_selected_nodes");
return; return;
}; };
if let NodeTypePersistentMetadata::Layer(layer_metadata) = &node_metadata.persistent_metadata.node_type_metadata { if let NodeTypePersistentMetadata::Layer(layer_metadata) = &node_metadata.persistent_metadata.node_type_metadata
if let LayerPosition::Stack(offset) = layer_metadata.position { && let LayerPosition::Stack(offset) = layer_metadata.position
// If the upstream layer is selected, then skip {
let Some(outward_wires) = self.outward_wires(network_path).and_then(|outward_wires| outward_wires.get(&OutputConnector::node(*node_id, 0))) else { // If the upstream layer is selected, then skip
log::error!("Could not get outward wires in shift_selected_nodes"); let Some(outward_wires) = self.outward_wires(network_path).and_then(|outward_wires| outward_wires.get(&OutputConnector::node(*node_id, 0))) else {
return; log::error!("Could not get outward wires in shift_selected_nodes");
}; return;
if let Some(upstream_node) = outward_wires.first() { };
if node_ids.contains(&upstream_node.node_id().expect("Stack layer should have downstream layer")) { if let Some(upstream_node) = outward_wires.first()
continue; && node_ids.contains(&upstream_node.node_id().expect("Stack layer should have downstream layer"))
} {
} continue;
// Offset cannot be negative, so cancel the shift }
if offset == 0 { // Offset cannot be negative, so cancel the shift
return; if offset == 0 {
} return;
} }
} }
} }
@@ -5528,11 +5523,11 @@ impl NodeNetworkInterface {
log::error!("Could not get nested network_metadata in export_ports"); log::error!("Could not get nested network_metadata in export_ports");
continue; continue;
}; };
if let TransientMetadata::Loaded(stack_dependents) = &mut network_metadata.transient_metadata.stack_dependents { if let TransientMetadata::Loaded(stack_dependents) = &mut network_metadata.transient_metadata.stack_dependents
if let Some(LayerOwner::None(offset)) = stack_dependents.get_mut(node_id) { && let Some(LayerOwner::None(offset)) = stack_dependents.get_mut(node_id)
*offset += shift_sign; {
self.transaction_modified(); *offset += shift_sign;
}; self.transaction_modified();
}; };
// Shift the upstream layer so that it stays in the same place // Shift the upstream layer so that it stays in the same place
@@ -5804,14 +5799,13 @@ impl NodeNetworkInterface {
// A layer is considered to be the height of that layer plus the height to the upstream layer sibling // A layer is considered to be the height of that layer plus the height to the upstream layer sibling
// If a non artboard layer is attempted to be connected to the exports, and there is already an artboard connected, then connect the layer to the artboard. // If a non artboard layer is attempted to be connected to the exports, and there is already an artboard connected, then connect the layer to the artboard.
if let Some(first_layer) = LayerNodeIdentifier::ROOT_PARENT.children(&self.document_metadata).next() { if let Some(first_layer) = LayerNodeIdentifier::ROOT_PARENT.children(&self.document_metadata).next()
if parent == LayerNodeIdentifier::ROOT_PARENT && parent == LayerNodeIdentifier::ROOT_PARENT
&& self.reference(&layer.to_node(), network_path).is_none_or(|reference| *reference != Some("Artboard".to_string())) && self.reference(&layer.to_node(), network_path).is_none_or(|reference| *reference != Some("Artboard".to_string()))
&& self.is_artboard(&first_layer.to_node(), network_path) && self.is_artboard(&first_layer.to_node(), network_path)
{ {
parent = first_layer; parent = first_layer;
insert_index = 0; insert_index = 0;
}
} }
let Some(layer_to_move_position) = self.position(&layer.to_node(), network_path) else { let Some(layer_to_move_position) = self.position(&layer.to_node(), network_path) else {
@@ -5870,22 +5864,20 @@ impl NodeNetworkInterface {
let mut downstream_height = 0; let mut downstream_height = 0;
let inserting_into_stack = let inserting_into_stack =
!(post_node.input_index() == 1 || matches!(post_node, InputConnector::Export(_)) || !post_node.node_id().is_some_and(|post_node_id| self.is_layer(&post_node_id, network_path))); !(post_node.input_index() == 1 || matches!(post_node, InputConnector::Export(_)) || !post_node.node_id().is_some_and(|post_node_id| self.is_layer(&post_node_id, network_path)));
if inserting_into_stack { if inserting_into_stack && let Some(downstream_node) = post_node.node_id() {
if let Some(downstream_node) = post_node.node_id() { let Some(downstream_node_position) = self.position(&downstream_node, network_path) else {
let Some(downstream_node_position) = self.position(&downstream_node, network_path) else { log::error!("Could not get downstream node position in move_layer_to_stack");
log::error!("Could not get downstream node position in move_layer_to_stack"); return;
return; };
}; let mut lowest_y_position = downstream_node_position.y + 3;
let mut lowest_y_position = downstream_node_position.y + 3;
for bottom_position in self.upstream_nodes_below_layer(&downstream_node, network_path).iter().filter_map(|node_id| { for bottom_position in self.upstream_nodes_below_layer(&downstream_node, network_path).iter().filter_map(|node_id| {
let is_layer = self.is_layer(node_id, network_path); let is_layer = self.is_layer(node_id, network_path);
self.position(node_id, network_path).map(|position| position.y + if is_layer { 3 } else { 2 }) self.position(node_id, network_path).map(|position| position.y + if is_layer { 3 } else { 2 })
}) { }) {
lowest_y_position = lowest_y_position.max(bottom_position); lowest_y_position = lowest_y_position.max(bottom_position);
}
downstream_height = lowest_y_position - (downstream_node_position.y + 3);
} }
downstream_height = lowest_y_position - (downstream_node_position.y + 3);
} }
let mut highest_y_position = layer_to_move_position.y; let mut highest_y_position = layer_to_move_position.y;
@@ -5933,53 +5925,53 @@ impl NodeNetworkInterface {
} }
// If inserting into a stack with a parent, ensure the parent stack has enough space for the child stack // If inserting into a stack with a parent, ensure the parent stack has enough space for the child stack
if parent != LayerNodeIdentifier::ROOT_PARENT { if parent != LayerNodeIdentifier::ROOT_PARENT
if let Some(upstream_sibling) = parent.next_sibling(&self.document_metadata) { && let Some(upstream_sibling) = parent.next_sibling(&self.document_metadata)
let Some(parent_position) = self.position(&parent.to_node(), network_path) else { {
log::error!("Could not get parent position in move_layer_to_stack"); let Some(parent_position) = self.position(&parent.to_node(), network_path) else {
return; log::error!("Could not get parent position in move_layer_to_stack");
}; return;
let last_child = parent.last_child(&self.document_metadata).unwrap_or(parent); };
let last_child = parent.last_child(&self.document_metadata).unwrap_or(parent);
let Some(mut last_child_position) = self.position(&last_child.to_node(), network_path) else { let Some(mut last_child_position) = self.position(&last_child.to_node(), network_path) else {
log::error!("Could not get last child position in move_layer_to_stack"); log::error!("Could not get last child position in move_layer_to_stack");
return; return;
}; };
if self.is_layer(&last_child.to_node(), network_path) { if self.is_layer(&last_child.to_node(), network_path) {
last_child_position.y += 3; last_child_position.y += 3;
} else { } else {
last_child_position.y += 2; last_child_position.y += 2;
}
// If inserting below the current last child, then the last child is layer to move
if post_node.node_id() == Some(last_child.to_node()) {
last_child_position += height_above_layer + 3 + height_below_layer;
}
let Some(upstream_sibling_position) = self.position(&upstream_sibling.to_node(), network_path) else {
log::error!("Could not get upstream sibling position in move_layer_to_stack");
return;
};
let target_gap = last_child_position.y - parent_position.y + 3;
let current_gap = upstream_sibling_position.y - parent_position.y;
let upstream_nodes = self
.upstream_flow_back_from_nodes(vec![upstream_sibling.to_node()], network_path, FlowType::UpstreamFlow)
.collect::<Vec<_>>();
let Some(selected_nodes) = self.selected_nodes_mut(network_path) else {
log::error!("Could not get selected nodes in move_layer_to_stack");
return;
};
let old_selected_nodes = selected_nodes.replace_with(upstream_nodes);
for _ in 0..(target_gap - current_gap).max(0) {
self.shift_selected_nodes(Direction::Down, true, network_path);
}
let _ = self.selected_nodes_mut(network_path).unwrap().replace_with(old_selected_nodes);
} }
// If inserting below the current last child, then the last child is layer to move
if post_node.node_id() == Some(last_child.to_node()) {
last_child_position += height_above_layer + 3 + height_below_layer;
}
let Some(upstream_sibling_position) = self.position(&upstream_sibling.to_node(), network_path) else {
log::error!("Could not get upstream sibling position in move_layer_to_stack");
return;
};
let target_gap = last_child_position.y - parent_position.y + 3;
let current_gap = upstream_sibling_position.y - parent_position.y;
let upstream_nodes = self
.upstream_flow_back_from_nodes(vec![upstream_sibling.to_node()], network_path, FlowType::UpstreamFlow)
.collect::<Vec<_>>();
let Some(selected_nodes) = self.selected_nodes_mut(network_path) else {
log::error!("Could not get selected nodes in move_layer_to_stack");
return;
};
let old_selected_nodes = selected_nodes.replace_with(upstream_nodes);
for _ in 0..(target_gap - current_gap).max(0) {
self.shift_selected_nodes(Direction::Down, true, network_path);
}
let _ = self.selected_nodes_mut(network_path).unwrap().replace_with(old_selected_nodes);
} }
// Connect the layer to a parent layer/node at the top of the stack, or a non layer node midway down the stack // Connect the layer to a parent layer/node at the top of the stack, or a non layer node midway down the stack
@@ -581,11 +581,9 @@ pub fn document_migration_upgrades(document: &mut DocumentMessageHandler, reset_
} }
fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId], document: &mut DocumentMessageHandler, reset_node_definitions_on_open: bool) -> Option<()> { fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId], document: &mut DocumentMessageHandler, reset_node_definitions_on_open: bool) -> Option<()> {
if reset_node_definitions_on_open { if reset_node_definitions_on_open && let Some(Some(reference)) = document.network_interface.reference(node_id, network_path) {
if let Some(Some(reference)) = document.network_interface.reference(node_id, network_path) { let node_definition = resolve_document_node_type(reference)?;
let node_definition = resolve_document_node_type(reference)?; document.network_interface.replace_implementation(node_id, network_path, &mut node_definition.default_node_template());
document.network_interface.replace_implementation(node_id, network_path, &mut node_definition.default_node_template());
}
} }
// Upgrade old nodes to use `Context` instead of `()` or `Footprint` as their call argument // Upgrade old nodes to use `Context` instead of `()` or `Footprint` as their call argument
@@ -1018,11 +1016,11 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
.network_interface .network_interface
.input_from_connector(&InputConnector::Node { node_id: *node_id, input_index: 3 }, network_path)?; .input_from_connector(&InputConnector::Node { node_id: *node_id, input_index: 3 }, network_path)?;
if let NodeInput::Value { tagged_value, exposed } = quantity_value { if let NodeInput::Value { tagged_value, exposed } = quantity_value
if let TaggedValue::F64(value) = **tagged_value { && let TaggedValue::F64(value) = **tagged_value
let new_quantity_value = NodeInput::value(TaggedValue::U32(value as u32), *exposed); {
document.network_interface.set_input(&InputConnector::node(*node_id, 3), new_quantity_value, network_path); let new_quantity_value = NodeInput::value(TaggedValue::U32(value as u32), *exposed);
} document.network_interface.set_input(&InputConnector::node(*node_id, 3), new_quantity_value, network_path);
} }
} }
@@ -1037,18 +1035,18 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
let mut upgraded = false; let mut upgraded = false;
if let Some(NodeInput::Value { tagged_value, exposed: _ }) = index_3_value { if let Some(NodeInput::Value { tagged_value, exposed: _ }) = index_3_value
if matches!(*tagged_value, TaggedValue::DVec2(_)) { && matches!(*tagged_value, TaggedValue::DVec2(_))
// Move index 3 to the end {
document.network_interface.set_input(&InputConnector::node(*node_id, 0), old_inputs[0].clone(), network_path); // Move index 3 to the end
document.network_interface.set_input(&InputConnector::node(*node_id, 1), old_inputs[1].clone(), network_path); document.network_interface.set_input(&InputConnector::node(*node_id, 0), old_inputs[0].clone(), network_path);
document.network_interface.set_input(&InputConnector::node(*node_id, 2), old_inputs[2].clone(), network_path); document.network_interface.set_input(&InputConnector::node(*node_id, 1), old_inputs[1].clone(), network_path);
document.network_interface.set_input(&InputConnector::node(*node_id, 3), old_inputs[4].clone(), network_path); document.network_interface.set_input(&InputConnector::node(*node_id, 2), old_inputs[2].clone(), network_path);
document.network_interface.set_input(&InputConnector::node(*node_id, 4), old_inputs[5].clone(), network_path); document.network_interface.set_input(&InputConnector::node(*node_id, 3), old_inputs[4].clone(), network_path);
document.network_interface.set_input(&InputConnector::node(*node_id, 5), old_inputs[3].clone(), network_path); document.network_interface.set_input(&InputConnector::node(*node_id, 4), old_inputs[5].clone(), network_path);
document.network_interface.set_input(&InputConnector::node(*node_id, 5), old_inputs[3].clone(), network_path);
upgraded = true; upgraded = true;
}
} }
if !upgraded { if !upgraded {
@@ -116,22 +116,22 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
self.menu_bar_message_handler.process_message(message, responses, ()); self.menu_bar_message_handler.process_message(message, responses, ());
} }
PortfolioMessage::Document(message) => { PortfolioMessage::Document(message) => {
if let Some(document_id) = self.active_document_id { if let Some(document_id) = self.active_document_id
if let Some(document) = self.documents.get_mut(&document_id) { && let Some(document) = self.documents.get_mut(&document_id)
let document_inputs = DocumentMessageContext { {
document_id, let document_inputs = DocumentMessageContext {
ipp, document_id,
persistent_data: &self.persistent_data, ipp,
executor: &mut self.executor, persistent_data: &self.persistent_data,
current_tool, executor: &mut self.executor,
preferences, current_tool,
viewport, preferences,
data_panel_open: self.data_panel_open, viewport,
layers_panel_open: self.layers_panel_open, data_panel_open: self.data_panel_open,
properties_panel_open: self.properties_panel_open, layers_panel_open: self.layers_panel_open,
}; properties_panel_open: self.properties_panel_open,
document.process_message(message, responses, document_inputs) };
} document.process_message(message, responses, document_inputs)
} }
} }
@@ -175,11 +175,11 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
} }
} }
PortfolioMessage::AutoSaveActiveDocument => { PortfolioMessage::AutoSaveActiveDocument => {
if let Some(document_id) = self.active_document_id { if let Some(document_id) = self.active_document_id
if let Some(document) = self.active_document_mut() { && let Some(document) = self.active_document_mut()
document.set_auto_save_state(true); {
responses.add(PortfolioMessage::AutoSaveDocument { document_id }); document.set_auto_save_state(true);
} responses.add(PortfolioMessage::AutoSaveDocument { document_id });
} }
} }
PortfolioMessage::AutoSaveAllDocuments => { PortfolioMessage::AutoSaveAllDocuments => {
@@ -272,7 +272,7 @@ fn calculate_isometric_top_line_points(columns: u32, rows: u32, spacing: DVec2,
let offset = if columns == 1 || rows == 1 { DVec2::ZERO } else { DVec2::new(spacing.x * 0.5, 0.) }; let offset = if columns == 1 || rows == 1 { DVec2::ZERO } else { DVec2::new(spacing.x * 0.5, 0.) };
let isometric_spacing = calculate_isometric_offset(spacing, angles); let isometric_spacing = calculate_isometric_offset(spacing, angles);
let isometric_offset = DVec2::new(0., isometric_spacing.y); let isometric_offset = DVec2::new(0., isometric_spacing.y);
let end_isometric_offset = if columns % 2 == 0 { DVec2::ZERO } else { DVec2::new(0., isometric_spacing.y) }; let end_isometric_offset = if columns.is_multiple_of(2) { DVec2::ZERO } else { DVec2::new(0., isometric_spacing.y) };
(top_left + offset - isometric_offset, top_right - offset - end_isometric_offset) (top_left + offset - isometric_offset, top_right - offset - end_isometric_offset)
} }
@@ -282,7 +282,7 @@ fn calculate_isometric_bottom_line_points(columns: u32, rows: u32, spacing: DVec
let bottom_right = calculate_isometric_point(columns - 1, rows - 1, angles, spacing); let bottom_right = calculate_isometric_point(columns - 1, rows - 1, angles, spacing);
let offset = if columns == 1 || rows == 1 { DVec2::ZERO } else { DVec2::new(spacing.x * 0.5, 0.) }; let offset = if columns == 1 || rows == 1 { DVec2::ZERO } else { DVec2::new(spacing.x * 0.5, 0.) };
let isometric_offset = if columns % 2 == 0 { let isometric_offset = if columns.is_multiple_of(2) {
let offset = calculate_isometric_offset(spacing, angles); let offset = calculate_isometric_offset(spacing, angles);
DVec2::new(0., offset.y) DVec2::new(0., offset.y)
} else { } else {
@@ -108,10 +108,10 @@ impl NumberOfPointsDial {
let viewport = document.metadata().transform_to_viewport(layer); let viewport = document.metadata().transform_to_viewport(layer);
let center = viewport.transform_point2(DVec2::ZERO); let center = viewport.transform_point2(DVec2::ZERO);
if let Some(closest_segment) = shape_editor.upper_closest_segment(&document.network_interface, mouse_position, POINT_RADIUS_HANDLE_SEGMENT_THRESHOLD) { if let Some(closest_segment) = shape_editor.upper_closest_segment(&document.network_interface, mouse_position, POINT_RADIUS_HANDLE_SEGMENT_THRESHOLD)
if closest_segment.layer() == layer { && closest_segment.layer() == layer
return; {
} return;
} }
let point_on_max_radius = star_vertex_position(viewport, 0, sides, radius1, radius2); let point_on_max_radius = star_vertex_position(viewport, 0, sides, radius1, radius2);
@@ -126,10 +126,10 @@ impl NumberOfPointsDial {
let viewport = document.metadata().transform_to_viewport(layer); let viewport = document.metadata().transform_to_viewport(layer);
let center = viewport.transform_point2(DVec2::ZERO); let center = viewport.transform_point2(DVec2::ZERO);
if let Some(closest_segment) = shape_editor.upper_closest_segment(&document.network_interface, mouse_position, POINT_RADIUS_HANDLE_SEGMENT_THRESHOLD) { if let Some(closest_segment) = shape_editor.upper_closest_segment(&document.network_interface, mouse_position, POINT_RADIUS_HANDLE_SEGMENT_THRESHOLD)
if closest_segment.layer() == layer { && closest_segment.layer() == layer
return; {
} return;
} }
let point_on_max_radius = polygon_vertex_position(viewport, 0, sides, radius); let point_on_max_radius = polygon_vertex_position(viewport, 0, sides, radius);
@@ -691,10 +691,10 @@ impl ShapeState {
let mut selected_stack = Vec::new(); let mut selected_stack = Vec::new();
// Find all subpaths that have been clicked // Find all subpaths that have been clicked
for stroke in vector.stroke_bezier_paths() { for stroke in vector.stroke_bezier_paths() {
if stroke.contains_point(layer_mouse) { if stroke.contains_point(layer_mouse)
if let Some(first) = stroke.manipulator_groups().first() { && let Some(first) = stroke.manipulator_groups().first()
selected_stack.push(first.id); {
} selected_stack.push(first.id);
} }
} }
state.clear_points(); state.clear_points();
@@ -929,20 +929,20 @@ impl ShapeState {
ManipulatorPointId::Anchor(point) => self.move_anchor(point, &vector, delta, layer, None, responses), ManipulatorPointId::Anchor(point) => self.move_anchor(point, &vector, delta, layer, None, responses),
ManipulatorPointId::PrimaryHandle(segment) => { ManipulatorPointId::PrimaryHandle(segment) => {
self.move_primary(segment, delta, layer, responses); self.move_primary(segment, delta, layer, responses);
if let Some(handle) = point.as_handle() { if let Some(handle) = point.as_handle()
if let Some(handles) = vector.colinear_manipulators.iter().find(|handles| handles[0] == handle || handles[1] == handle) { && let Some(handles) = vector.colinear_manipulators.iter().find(|handles| handles[0] == handle || handles[1] == handle)
let modification_type = VectorModificationType::SetG1Continuous { handles: *handles, enabled: false }; {
responses.add(GraphOperationMessage::Vector { layer, modification_type }); let modification_type = VectorModificationType::SetG1Continuous { handles: *handles, enabled: false };
} responses.add(GraphOperationMessage::Vector { layer, modification_type });
} }
} }
ManipulatorPointId::EndHandle(segment) => { ManipulatorPointId::EndHandle(segment) => {
self.move_end(segment, delta, layer, responses); self.move_end(segment, delta, layer, responses);
if let Some(handle) = point.as_handle() { if let Some(handle) = point.as_handle()
if let Some(handles) = vector.colinear_manipulators.iter().find(|handles| handles[0] == handle || handles[1] == handle) { && let Some(handles) = vector.colinear_manipulators.iter().find(|handles| handles[0] == handle || handles[1] == handle)
let modification_type = VectorModificationType::SetG1Continuous { handles: *handles, enabled: false }; {
responses.add(GraphOperationMessage::Vector { layer, modification_type }); let modification_type = VectorModificationType::SetG1Continuous { handles: *handles, enabled: false };
} responses.add(GraphOperationMessage::Vector { layer, modification_type });
} }
} }
} }
@@ -1078,10 +1078,10 @@ impl ShapeState {
for &point in layer_state.selected_points.iter() { for &point in layer_state.selected_points.iter() {
// Skip a point which has more than 2 segments connected (vector meshes) // Skip a point which has more than 2 segments connected (vector meshes)
if let ManipulatorPointId::Anchor(anchor) = point { if let ManipulatorPointId::Anchor(anchor) = point
if vector.all_connected(anchor).count() > 2 { && vector.all_connected(anchor).count() > 2
continue; {
} continue;
} }
// Here we take handles as the current handle and the most opposite non-colinear-handle // Here we take handles as the current handle and the most opposite non-colinear-handle
@@ -1409,10 +1409,11 @@ impl ShapeState {
match point { match point {
ManipulatorPointId::Anchor(anchor) => { ManipulatorPointId::Anchor(anchor) => {
if let Some(handles) = Self::dissolve_anchor(anchor, responses, layer, &vector) { if let Some(handles) = Self::dissolve_anchor(anchor, responses, layer, &vector)
if !vector.all_connected(anchor).any(|a| selected_segments.contains(&a.segment)) && vector.all_connected(anchor).count() <= 2 { && !vector.all_connected(anchor).any(|a| selected_segments.contains(&a.segment))
missing_anchors.insert(anchor, handles); && vector.all_connected(anchor).count() <= 2
} {
missing_anchors.insert(anchor, handles);
} }
deleted_anchors.insert(anchor); deleted_anchors.insert(anchor);
} }
@@ -1643,11 +1644,11 @@ impl ShapeState {
responses.add(GraphOperationMessage::Vector { layer, modification_type }); responses.add(GraphOperationMessage::Vector { layer, modification_type });
} }
} }
} else if let Some(handle) = point.as_handle() { } else if let Some(handle) = point.as_handle()
if let Some(handles) = vector.colinear_manipulators.iter().find(|handles| handles[0] == handle || handles[1] == handle) { && let Some(handles) = vector.colinear_manipulators.iter().find(|handles| handles[0] == handle || handles[1] == handle)
let modification_type = VectorModificationType::SetG1Continuous { handles: *handles, enabled: false }; {
responses.add(GraphOperationMessage::Vector { layer, modification_type }); let modification_type = VectorModificationType::SetG1Continuous { handles: *handles, enabled: false };
} responses.add(GraphOperationMessage::Vector { layer, modification_type });
} }
} }
} }
@@ -1728,17 +1729,20 @@ impl ShapeState {
let bezier = bezier.apply_transformation(|point| viewspace.transform_point2(point)); let bezier = bezier.apply_transformation(|point| viewspace.transform_point2(point));
let valid = |handle: DVec2, control: DVec2| handle.distance_squared(control) > crate::consts::HIDE_HANDLE_DISTANCE.powi(2); let valid = |handle: DVec2, control: DVec2| handle.distance_squared(control) > crate::consts::HIDE_HANDLE_DISTANCE.powi(2);
if let Some(primary_handle) = bezier.handle_start() { if let Some(primary_handle) = bezier.handle_start()
if valid(primary_handle, bezier.start) && (bezier.handle_end().is_some() || valid(primary_handle, bezier.end)) && primary_handle.distance_squared(pos) <= closest_distance_squared { && valid(primary_handle, bezier.start)
closest_distance_squared = primary_handle.distance_squared(pos); && (bezier.handle_end().is_some() || valid(primary_handle, bezier.end))
manipulator_point = Some(ManipulatorPointId::PrimaryHandle(segment_id)); && primary_handle.distance_squared(pos) <= closest_distance_squared
} {
closest_distance_squared = primary_handle.distance_squared(pos);
manipulator_point = Some(ManipulatorPointId::PrimaryHandle(segment_id));
} }
if let Some(end_handle) = bezier.handle_end() { if let Some(end_handle) = bezier.handle_end()
if valid(end_handle, bezier.end) && end_handle.distance_squared(pos) <= closest_distance_squared { && valid(end_handle, bezier.end)
closest_distance_squared = end_handle.distance_squared(pos); && end_handle.distance_squared(pos) <= closest_distance_squared
manipulator_point = Some(ManipulatorPointId::EndHandle(segment_id)); {
} closest_distance_squared = end_handle.distance_squared(pos);
manipulator_point = Some(ManipulatorPointId::EndHandle(segment_id));
} }
} }
@@ -276,22 +276,22 @@ impl SnapManager {
snapped_points.push(closest_curve.clone()); snapped_points.push(closest_curve.clone());
} }
if document.snapping_state.target_enabled(SnapTarget::Grid(GridSnapTarget::Line)) { if document.snapping_state.target_enabled(SnapTarget::Grid(GridSnapTarget::Line))
if let Some(closest_line) = get_closest_line(&snap_results.grid_lines) { && let Some(closest_line) = get_closest_line(&snap_results.grid_lines)
snapped_points.push(closest_line.clone()); {
} snapped_points.push(closest_line.clone());
} }
if !constrained { if !constrained {
if document.snapping_state.target_enabled(SnapTarget::Path(PathSnapTarget::IntersectionPoint)) { if document.snapping_state.target_enabled(SnapTarget::Path(PathSnapTarget::IntersectionPoint))
if let Some(closest_curves_intersection) = get_closest_intersection(point.document_point, &snap_results.curves) { && let Some(closest_curves_intersection) = get_closest_intersection(point.document_point, &snap_results.curves)
snapped_points.push(closest_curves_intersection); {
} snapped_points.push(closest_curves_intersection);
} }
if document.snapping_state.target_enabled(SnapTarget::Grid(GridSnapTarget::Intersection)) { if document.snapping_state.target_enabled(SnapTarget::Grid(GridSnapTarget::Intersection))
if let Some(closest_grid_intersection) = get_grid_intersection(point.document_point, &snap_results.grid_lines) { && let Some(closest_grid_intersection) = get_grid_intersection(point.document_point, &snap_results.grid_lines)
snapped_points.push(closest_grid_intersection); {
} snapped_points.push(closest_grid_intersection);
} }
} }
@@ -67,34 +67,36 @@ impl AlignmentSnapper {
let target_position = target_point.document_point; let target_position = target_point.document_point;
// Perpendicular snap for line's endpoints // Perpendicular snap for line's endpoints
if let Some(quad) = target_point.quad.map(|q| q.0) { if let Some(quad) = target_point.quad.map(|q| q.0)
if quad[0] == quad[3] && quad[1] == quad[2] && quad[0] == target_point.document_point { && quad[0] == quad[3]
let [p1, p2, ..] = quad; && quad[1] == quad[2]
let Some(direction) = (p2 - p1).try_normalize() else { return }; && quad[0] == target_point.document_point
let normal = DVec2::new(-direction.y, direction.x); {
let [p1, p2, ..] = quad;
let Some(direction) = (p2 - p1).try_normalize() else { return };
let normal = DVec2::new(-direction.y, direction.x);
for endpoint in [p1, p2] { for endpoint in [p1, p2] {
if let Some(perpendicular_snap) = Quad::intersect_rays(point.document_point, direction, endpoint, normal) { if let Some(perpendicular_snap) = Quad::intersect_rays(point.document_point, direction, endpoint, normal) {
let distance_squared = point.document_point.distance_squared(perpendicular_snap); let distance_squared = point.document_point.distance_squared(perpendicular_snap);
if distance_squared < tolerance_squared { if distance_squared < tolerance_squared {
let distance = distance_squared.sqrt(); let distance = distance_squared.sqrt();
let distance_to_align_target = perpendicular_snap.distance_squared(endpoint).sqrt(); let distance_to_align_target = perpendicular_snap.distance_squared(endpoint).sqrt();
let snap_point = SnappedPoint { let snap_point = SnappedPoint {
snapped_point_document: perpendicular_snap, snapped_point_document: perpendicular_snap,
source: point.source, source: point.source,
target: SnapTarget::Alignment(AlignmentSnapTarget::PerpendicularToEndpoint), target: SnapTarget::Alignment(AlignmentSnapTarget::PerpendicularToEndpoint),
target_bounds: Some(Quad(quad)), target_bounds: Some(Quad(quad)),
distance, distance,
tolerance, tolerance,
distance_to_align_target, distance_to_align_target,
fully_constrained: false, fully_constrained: false,
at_intersection: true, at_intersection: true,
alignment_target_horizontal: Some(endpoint), alignment_target_horizontal: Some(endpoint),
..Default::default() ..Default::default()
}; };
snap_results.points.push(snap_point); snap_results.points.push(snap_point);
}
} }
} }
} }
@@ -769,17 +769,17 @@ impl BoundingBoxManager {
let edges = self.check_selected_edges(input.mouse.position); let edges = self.check_selected_edges(input.mouse.position);
let is_near_square = edges.is_some_and(|hover_edge| self.over_extended_edge_midpoint(input.mouse.position, hover_edge)); let is_near_square = edges.is_some_and(|hover_edge| self.over_extended_edge_midpoint(input.mouse.position, hover_edge));
if dragging_bounds && is_near_square { if dragging_bounds
if let Some(skew_edge) = skew_edge { && is_near_square
if self.check_skew_handle(input.mouse.position, skew_edge) { && let Some(skew_edge) = skew_edge
if skew_edge.0 || skew_edge.1 { && self.check_skew_handle(input.mouse.position, skew_edge)
return MouseCursorIcon::EWResize; {
} else if skew_edge.2 || skew_edge.3 { if skew_edge.0 || skew_edge.1 {
return MouseCursorIcon::NSResize; return MouseCursorIcon::EWResize;
} } else if skew_edge.2 || skew_edge.3 {
} return MouseCursorIcon::NSResize;
}; }
} };
match edges { match edges {
Some((top, bottom, left, right)) => match (top, bottom, left, right) { Some((top, bottom, left, right)) => match (top, bottom, left, right) {
@@ -588,10 +588,10 @@ pub fn make_path_editable_is_allowed(network_interface: &mut NodeNetworkInterfac
// Must not already have an existing Path node, in the right-most part of the layer chain, which has an empty set of modifications // Must not already have an existing Path node, in the right-most part of the layer chain, which has an empty set of modifications
// (otherwise users could repeatedly keep running this command and stacking up empty Path nodes) // (otherwise users could repeatedly keep running this command and stacking up empty Path nodes)
if let Some(TaggedValue::VectorModification(modifications)) = NodeGraphLayer::new(first_layer, network_interface).find_input("Path", 1) { if let Some(TaggedValue::VectorModification(modifications)) = NodeGraphLayer::new(first_layer, network_interface).find_input("Path", 1)
if modifications.as_ref() == &VectorModification::default() { && modifications.as_ref() == &VectorModification::default()
return None; {
} return None;
} }
Some(first_layer) Some(first_layer)
@@ -411,18 +411,18 @@ impl Fsm for BrushToolFsmState {
} }
(BrushToolFsmState::Drawing, BrushToolMessage::PointerMove) => { (BrushToolFsmState::Drawing, BrushToolMessage::PointerMove) => {
if let Some(layer) = tool_data.layer { if let Some(layer) = tool_data.layer
if let Some(stroke) = tool_data.strokes.last_mut() { && let Some(stroke) = tool_data.strokes.last_mut()
let layer_position = document {
.network_interface let layer_position = document
.document_metadata() .network_interface
.downstream_transform_to_viewport(layer) .document_metadata()
.inverse() .downstream_transform_to_viewport(layer)
.transform_point2(input.mouse.position); .inverse()
let layer_position = tool_data.transform.inverse().transform_point2(layer_position); .transform_point2(input.mouse.position);
let layer_position = tool_data.transform.inverse().transform_point2(layer_position);
stroke.trace.push(BrushInputSample { position: layer_position }) stroke.trace.push(BrushInputSample { position: layer_position })
}
} }
tool_data.update_strokes(responses); tool_data.update_strokes(responses);
@@ -367,20 +367,18 @@ fn extend_path_with_next_segment(tool_data: &mut FreehandToolData, position: DVe
modification_type: VectorModificationType::InsertPoint { id, position }, modification_type: VectorModificationType::InsertPoint { id, position },
}); });
if extend { if extend && let Some((_, previous_position)) = tool_data.end_point {
if let Some((_, previous_position)) = tool_data.end_point { let next_id = SegmentId::generate();
let next_id = SegmentId::generate(); let points = [previous_position, id];
let points = [previous_position, id];
responses.add(GraphOperationMessage::Vector { responses.add(GraphOperationMessage::Vector {
layer, layer,
modification_type: VectorModificationType::InsertSegment { modification_type: VectorModificationType::InsertSegment {
id: next_id, id: next_id,
points, points,
handles: [None, None], handles: [None, None],
}, },
}); });
}
} }
tool_data.dragged = true; tool_data.dragged = true;
@@ -474,10 +474,10 @@ impl Fsm for GradientToolFsmState {
} }
(GradientToolFsmState::Drawing, GradientToolMessage::PointerOutsideViewport { .. }) => { (GradientToolFsmState::Drawing, GradientToolMessage::PointerOutsideViewport { .. }) => {
// Auto-panning // Auto-panning
if let Some(shift) = tool_data.auto_panning.shift_viewport(input, viewport, responses) { if let Some(shift) = tool_data.auto_panning.shift_viewport(input, viewport, responses)
if let Some(selected_gradient) = &mut tool_data.selected_gradient { && let Some(selected_gradient) = &mut tool_data.selected_gradient
selected_gradient.transform.translation += shift; {
} selected_gradient.transform.translation += shift;
} }
GradientToolFsmState::Drawing GradientToolFsmState::Drawing
@@ -497,12 +497,11 @@ impl Fsm for GradientToolFsmState {
tool_data.snap_manager.cleanup(responses); tool_data.snap_manager.cleanup(responses);
let was_dragging = tool_data.selected_gradient.is_some(); let was_dragging = tool_data.selected_gradient.is_some();
if !was_dragging { if !was_dragging
if let Some(selected_layer) = document.click(input, viewport) { && let Some(selected_layer) = document.click(input, viewport)
if let Some(gradient) = get_gradient(selected_layer, &document.network_interface) { && let Some(gradient) = get_gradient(selected_layer, &document.network_interface)
tool_data.selected_gradient = Some(SelectedGradient::new(gradient, selected_layer, document)); {
} tool_data.selected_gradient = Some(SelectedGradient::new(gradient, selected_layer, document));
}
} }
GradientToolFsmState::Ready GradientToolFsmState::Ready
} }
@@ -750,8 +750,8 @@ impl PathToolData {
// If the point is already selected and shift (`extend_selection`) is used, keep the selection unchanged. // If the point is already selected and shift (`extend_selection`) is used, keep the selection unchanged.
// Otherwise, select the first point within the threshold. // Otherwise, select the first point within the threshold.
if !(already_selected && extend_selection) { if !(already_selected && extend_selection)
if let Some(updated_selection_info) = shape_editor.change_point_selection( && let Some(updated_selection_info) = shape_editor.change_point_selection(
&document.network_interface, &document.network_interface,
input.mouse.position, input.mouse.position,
SELECTION_THRESHOLD, SELECTION_THRESHOLD,
@@ -759,8 +759,7 @@ impl PathToolData {
path_overlay_mode, path_overlay_mode,
self.frontier_handles_info.as_ref(), self.frontier_handles_info.as_ref(),
) { ) {
selection_info = updated_selection_info; selection_info = updated_selection_info;
}
} }
if let Some(selected_points) = selection_info { if let Some(selected_points) = selection_info {
@@ -778,28 +777,26 @@ impl PathToolData {
self.saved_selection_before_handle_drag = old_selection; self.saved_selection_before_handle_drag = old_selection;
} }
if handle_drag_from_anchor { if handle_drag_from_anchor && let Some((layer, point)) = shape_editor.find_nearest_point_indices(&document.network_interface, input.mouse.position, SELECTION_THRESHOLD) {
if let Some((layer, point)) = shape_editor.find_nearest_point_indices(&document.network_interface, input.mouse.position, SELECTION_THRESHOLD) { // Check that selected point is an anchor
// Check that selected point is an anchor if let (Some(point_id), Some(vector)) = (point.as_anchor(), document.network_interface.compute_modified_vector(layer)) {
if let (Some(point_id), Some(vector)) = (point.as_anchor(), document.network_interface.compute_modified_vector(layer)) { let handles = vector.all_connected(point_id).collect::<Vec<_>>();
let handles = vector.all_connected(point_id).collect::<Vec<_>>(); self.alt_clicked_on_anchor = true;
self.alt_clicked_on_anchor = true; for handle in &handles {
for handle in &handles { let modification_type = handle.set_relative_position(DVec2::ZERO);
let modification_type = handle.set_relative_position(DVec2::ZERO); responses.add(GraphOperationMessage::Vector { layer, modification_type });
responses.add(GraphOperationMessage::Vector { layer, modification_type }); for &handles in &vector.colinear_manipulators {
for &handles in &vector.colinear_manipulators { if handles.contains(handle) {
if handles.contains(handle) { let modification_type = VectorModificationType::SetG1Continuous { handles, enabled: false };
let modification_type = VectorModificationType::SetG1Continuous { handles, enabled: false }; responses.add(GraphOperationMessage::Vector { layer, modification_type });
responses.add(GraphOperationMessage::Vector { layer, modification_type });
}
} }
} }
let manipulator_point_id = handles[0].to_manipulator_point();
shape_editor.deselect_all_points();
shape_editor.select_point_by_layer_and_id(manipulator_point_id, layer);
responses.add(PathToolMessage::SelectedPointUpdated);
} }
let manipulator_point_id = handles[0].to_manipulator_point();
shape_editor.deselect_all_points();
shape_editor.select_point_by_layer_and_id(manipulator_point_id, layer);
responses.add(PathToolMessage::SelectedPointUpdated);
} }
} }
@@ -2098,19 +2095,19 @@ impl Fsm for PathToolFsmState {
let break_molding = input.keyboard.get(break_colinear_molding as usize); let break_molding = input.keyboard.get(break_colinear_molding as usize);
// Logic for molding segment // Logic for molding segment
if let Some(segment) = &mut tool_data.segment { if let Some(segment) = &mut tool_data.segment
if let Some(molding_segment_handles) = tool_data.molding_info { && let Some(molding_segment_handles) = tool_data.molding_info
tool_data.temporary_adjacent_handles_while_molding = segment.mold_handle_positions( {
document, tool_data.temporary_adjacent_handles_while_molding = segment.mold_handle_positions(
responses, document,
molding_segment_handles, responses,
input.mouse.position, molding_segment_handles,
break_molding, input.mouse.position,
tool_data.temporary_adjacent_handles_while_molding, break_molding,
); tool_data.temporary_adjacent_handles_while_molding,
);
return PathToolFsmState::Dragging(tool_data.dragging_state); return PathToolFsmState::Dragging(tool_data.dragging_state);
}
} }
let anchor_and_handle_toggled = input.keyboard.get(move_anchor_with_handles as usize); let anchor_and_handle_toggled = input.keyboard.get(move_anchor_with_handles as usize);
@@ -3384,15 +3381,13 @@ fn update_dynamic_hints(
let at_least_one_point_selected = shape_editor.selected_points().count() >= 1; let at_least_one_point_selected = shape_editor.selected_points().count() >= 1;
let mut single_colinear_anchor_selected = false; let mut single_colinear_anchor_selected = false;
if single_anchor_selected { if single_anchor_selected
if let (Some(anchor), Some(layer)) = ( && let (Some(anchor), Some(layer)) = (
shape_editor.selected_points().next(), shape_editor.selected_points().next(),
document.network_interface.selected_nodes().selected_layers(document.metadata()).next(), document.network_interface.selected_nodes().selected_layers(document.metadata()).next(),
) { ) && let Some(vector) = document.network_interface.compute_modified_vector(layer)
if let Some(vector) = document.network_interface.compute_modified_vector(layer) { {
single_colinear_anchor_selected = vector.colinear(*anchor) single_colinear_anchor_selected = vector.colinear(*anchor)
}
}
} }
let drag_selected_hints = vec![HintInfo::mouse(MouseMotion::LmbDrag, "Drag Selected")]; let drag_selected_hints = vec![HintInfo::mouse(MouseMotion::LmbDrag, "Drag Selected")];
@@ -728,8 +728,8 @@ impl PenToolData {
// Store the segment // Store the segment
let id = SegmentId::generate(); let id = SegmentId::generate();
if self.path_closed { if self.path_closed
if let Some((handles, handle1_pos)) = match self.get_opposite_handle_type(TargetHandle::PreviewInHandle, &vector) { && let Some((handles, handle1_pos)) = match self.get_opposite_handle_type(TargetHandle::PreviewInHandle, &vector) {
TargetHandle::PriorOutHandle(segment) => { TargetHandle::PriorOutHandle(segment) => {
let handles = [HandleId::end(id), HandleId::primary(segment)]; let handles = [HandleId::end(id), HandleId::primary(segment)];
let handle1_pos = handles[1].to_manipulator_point().get_position(&vector); let handle1_pos = handles[1].to_manipulator_point().get_position(&vector);
@@ -742,15 +742,14 @@ impl PenToolData {
} }
_ => None, _ => None,
} { } {
let angle = (handle_end - next_point).angle_to(handle1_pos - next_point); let angle = (handle_end - next_point).angle_to(handle1_pos - next_point);
let pi = std::f64::consts::PI; let pi = std::f64::consts::PI;
let colinear = (angle - pi).abs() < 1e-6 || (angle + pi).abs() < 1e-6; let colinear = (angle - pi).abs() < 1e-6 || (angle + pi).abs() < 1e-6;
responses.add(GraphOperationMessage::Vector { responses.add(GraphOperationMessage::Vector {
layer, layer,
modification_type: VectorModificationType::SetG1Continuous { handles, enabled: colinear }, modification_type: VectorModificationType::SetG1Continuous { handles, enabled: colinear },
}); });
self.cleanup(responses); self.cleanup(responses);
}
} }
self.prior_segment = Some(id); self.prior_segment = Some(id);
@@ -938,10 +937,10 @@ impl PenToolData {
} }
fn move_anchor_and_handles(&mut self, delta: DVec2, layer: LayerNodeIdentifier, responses: &mut VecDeque<Message>, vector: &Vector) { fn move_anchor_and_handles(&mut self, delta: DVec2, layer: LayerNodeIdentifier, responses: &mut VecDeque<Message>, vector: &Vector) {
if self.handle_end.is_none() { if self.handle_end.is_none()
if let Some(latest_pt) = self.latest_point_mut() { && let Some(latest_pt) = self.latest_point_mut()
latest_pt.pos += delta; {
} latest_pt.pos += delta;
} }
let Some(end_point) = self.prior_segment_endpoint else { return }; let Some(end_point) = self.prior_segment_endpoint else { return };
@@ -1210,10 +1209,11 @@ impl PenToolData {
snap.update_indicator(snapped); snap.update_indicator(snapped);
} }
if let Some(relative) = relative.map(|layer| transform.transform_point2(layer)) { if let Some(relative) = relative.map(|layer| transform.transform_point2(layer))
if (relative - document_pos) != DVec2::ZERO && (relative - document_pos).length_squared() > f64::EPSILON * 100. { && (relative - document_pos) != DVec2::ZERO
self.angle = -(relative - document_pos).angle_to(DVec2::X) && (relative - document_pos).length_squared() > f64::EPSILON * 100.
} {
self.angle = -(relative - document_pos).angle_to(DVec2::X)
} }
transform.inverse().transform_point2(document_pos) transform.inverse().transform_point2(document_pos)
@@ -1245,20 +1245,20 @@ impl PenToolData {
self.current_layer = Some(layer); self.current_layer = Some(layer);
self.extend_existing_path(document, layer, point, position); self.extend_existing_path(document, layer, point, position);
return; return;
} else if preferences.vector_meshes { } else if preferences.vector_meshes
if let Some(closest_segment) = shape_editor.upper_closest_segment(&document.network_interface, viewport_vec, tolerance) { && let Some(closest_segment) = shape_editor.upper_closest_segment(&document.network_interface, viewport_vec, tolerance)
let (point, segments) = closest_segment.adjusted_insert(responses); {
let layer = closest_segment.layer(); let (point, segments) = closest_segment.adjusted_insert(responses);
let position = closest_segment.closest_point_document(); let layer = closest_segment.layer();
let position = closest_segment.closest_point_document();
// Setting any one of the new segments created as the previous segment // Setting any one of the new segments created as the previous segment
self.prior_segment_endpoint = Some(point); self.prior_segment_endpoint = Some(point);
self.prior_segment_layer = Some(layer); self.prior_segment_layer = Some(layer);
self.prior_segments = Some(segments.to_vec()); self.prior_segments = Some(segments.to_vec());
self.extend_existing_path(document, layer, point, position); self.extend_existing_path(document, layer, point, position);
return; return;
}
} }
if append { if append {
@@ -1629,13 +1629,14 @@ impl Fsm for PenToolFsmState {
let viewport_vec = document.metadata().document_to_viewport.transform_point2(snapped.snapped_point_document); let viewport_vec = document.metadata().document_to_viewport.transform_point2(snapped.snapped_point_document);
let close_to_point = closest_point(document, viewport_vec, tolerance, document.metadata().all_layers(), |_| false, preferences).is_some(); let close_to_point = closest_point(document, viewport_vec, tolerance, document.metadata().all_layers(), |_| false, preferences).is_some();
if preferences.vector_meshes && !close_to_point { if preferences.vector_meshes
if let Some(closest_segment) = shape_editor.upper_closest_segment(&document.network_interface, viewport_vec, tolerance) { && !close_to_point
let pos = closest_segment.closest_point_to_viewport(); && let Some(closest_segment) = shape_editor.upper_closest_segment(&document.network_interface, viewport_vec, tolerance)
let perp = closest_segment.calculate_perp(document); {
overlay_context.manipulator_anchor(pos, true, None); let pos = closest_segment.closest_point_to_viewport();
overlay_context.line(pos - perp * SEGMENT_OVERLAY_SIZE, pos + perp * SEGMENT_OVERLAY_SIZE, Some(COLOR_OVERLAY_BLUE), None); let perp = closest_segment.calculate_perp(document);
} overlay_context.manipulator_anchor(pos, true, None);
overlay_context.line(pos - perp * SEGMENT_OVERLAY_SIZE, pos + perp * SEGMENT_OVERLAY_SIZE, Some(COLOR_OVERLAY_BLUE), None);
} }
tool_data.snap_manager.draw_overlays(SnapData::new(document, input, viewport), &mut overlay_context); tool_data.snap_manager.draw_overlays(SnapData::new(document, input, viewport), &mut overlay_context);
self self
@@ -1758,13 +1759,11 @@ impl Fsm for PenToolFsmState {
let snapped = tool_data.snap_manager.free_snap(&SnapData::new(document, input, viewport), &point, SnapTypeConfiguration::default()); let snapped = tool_data.snap_manager.free_snap(&SnapData::new(document, input, viewport), &point, SnapTypeConfiguration::default());
let viewport = document.metadata().document_to_viewport.transform_point2(snapped.snapped_point_document); let viewport = document.metadata().document_to_viewport.transform_point2(snapped.snapped_point_document);
let close_to_point = closest_point(document, viewport, tolerance, document.metadata().all_layers(), |_| false, preferences).is_some(); let close_to_point = closest_point(document, viewport, tolerance, document.metadata().all_layers(), |_| false, preferences).is_some();
if !close_to_point { if !close_to_point && let Some(closest_segment) = shape_editor.upper_closest_segment(&document.network_interface, viewport, tolerance) {
if let Some(closest_segment) = shape_editor.upper_closest_segment(&document.network_interface, viewport, tolerance) { let pos = closest_segment.closest_point_to_viewport();
let pos = closest_segment.closest_point_to_viewport(); let perp = closest_segment.calculate_perp(document);
let perp = closest_segment.calculate_perp(document); overlay_context.manipulator_anchor(pos, true, None);
overlay_context.manipulator_anchor(pos, true, None); overlay_context.line(pos - perp * SEGMENT_OVERLAY_SIZE, pos + perp * SEGMENT_OVERLAY_SIZE, Some(COLOR_OVERLAY_BLUE), None);
overlay_context.line(pos - perp * SEGMENT_OVERLAY_SIZE, pos + perp * SEGMENT_OVERLAY_SIZE, Some(COLOR_OVERLAY_BLUE), None);
}
} }
} }
@@ -776,19 +776,19 @@ impl Fsm for SelectToolFsmState {
let is_resizing_or_rotating = matches!(self, SelectToolFsmState::ResizingBounds | SelectToolFsmState::SkewingBounds { .. } | SelectToolFsmState::RotatingBounds); let is_resizing_or_rotating = matches!(self, SelectToolFsmState::ResizingBounds | SelectToolFsmState::SkewingBounds { .. } | SelectToolFsmState::RotatingBounds);
if overlay_context.visibility_settings.transform_cage() { if overlay_context.visibility_settings.transform_cage()
if let Some(bounds) = tool_data.bounding_box_manager.as_mut() { && let Some(bounds) = tool_data.bounding_box_manager.as_mut()
let edges = bounds.check_selected_edges(input.mouse.position); {
let is_skewing = matches!(self, SelectToolFsmState::SkewingBounds { .. }); let edges = bounds.check_selected_edges(input.mouse.position);
let is_near_square = edges.is_some_and(|hover_edge| bounds.over_extended_edge_midpoint(input.mouse.position, hover_edge)); let is_skewing = matches!(self, SelectToolFsmState::SkewingBounds { .. });
if is_skewing || (dragging_bounds && is_near_square && !is_resizing_or_rotating) { let is_near_square = edges.is_some_and(|hover_edge| bounds.over_extended_edge_midpoint(input.mouse.position, hover_edge));
bounds.render_skew_gizmos(&mut overlay_context, tool_data.skew_edge); if is_skewing || (dragging_bounds && is_near_square && !is_resizing_or_rotating) {
} bounds.render_skew_gizmos(&mut overlay_context, tool_data.skew_edge);
if !is_skewing && dragging_bounds { }
if let Some(edges) = edges { if !is_skewing
tool_data.skew_edge = bounds.get_closest_edge(edges, input.mouse.position); && dragging_bounds && let Some(edges) = edges
} {
} tool_data.skew_edge = bounds.get_closest_edge(edges, input.mouse.position);
} }
} }
@@ -887,33 +887,32 @@ impl Fsm for SelectToolFsmState {
compass_rose_state.axis_type().and_then(|axis| axis.is_constraint().then_some((axis, true))) compass_rose_state.axis_type().and_then(|axis| axis.is_constraint().then_some((axis, true)))
}; };
if show_compass_with_ring.is_some() { if show_compass_with_ring.is_some()
if let Some((axis, hover)) = axis_state { && let Some((axis, hover)) = axis_state
if axis.is_constraint() { && axis.is_constraint()
let e0 = tool_data {
.bounding_box_manager let e0 = tool_data
.as_ref() .bounding_box_manager
.map(|bounding_box_manager| bounding_box_manager.transform * Quad::from_box(bounding_box_manager.bounds)) .as_ref()
.map_or(DVec2::X, |quad| (quad.top_left() - quad.top_right()).normalize_or(DVec2::X)); .map(|bounding_box_manager| bounding_box_manager.transform * Quad::from_box(bounding_box_manager.bounds))
.map_or(DVec2::X, |quad| (quad.top_left() - quad.top_right()).normalize_or(DVec2::X));
let (direction, color) = match axis { let (direction, color) = match axis {
Axis::X => (e0, COLOR_OVERLAY_RED), Axis::X => (e0, COLOR_OVERLAY_RED),
Axis::Y => (e0.perp(), COLOR_OVERLAY_GREEN), Axis::Y => (e0.perp(), COLOR_OVERLAY_GREEN),
_ => unreachable!(), _ => unreachable!(),
}; };
let viewport_diagonal = viewport.size().into_dvec2().length(); let viewport_diagonal = viewport.size().into_dvec2().length();
let color = if !hover { let color = if !hover {
color color
} else { } else {
let color_string = &graphene_std::Color::from_rgb_str(color.strip_prefix('#').unwrap()).unwrap().with_alpha(0.25).to_rgba_hex_srgb(); let color_string = &graphene_std::Color::from_rgb_str(color.strip_prefix('#').unwrap()).unwrap().with_alpha(0.25).to_rgba_hex_srgb();
&format!("#{color_string}") &format!("#{color_string}")
}; };
let line_center = tool_data.line_center; let line_center = tool_data.line_center;
overlay_context.line(line_center - direction * viewport_diagonal, line_center + direction * viewport_diagonal, Some(color), None); overlay_context.line(line_center - direction * viewport_diagonal, line_center + direction * viewport_diagonal, Some(color), None);
}
}
} }
if axis_state.is_none_or(|(axis, _)| !axis.is_constraint()) && tool_data.axis_align { if axis_state.is_none_or(|(axis, _)| !axis.is_constraint()) && tool_data.axis_align {
@@ -1369,11 +1368,11 @@ impl Fsm for SelectToolFsmState {
} }
(SelectToolFsmState::ResizingBounds | SelectToolFsmState::SkewingBounds { .. }, SelectToolMessage::PointerOutsideViewport { .. }) => { (SelectToolFsmState::ResizingBounds | SelectToolFsmState::SkewingBounds { .. }, SelectToolMessage::PointerOutsideViewport { .. }) => {
// Auto-panning // Auto-panning
if let Some(shift) = tool_data.auto_panning.shift_viewport(input, viewport, responses) { if let Some(shift) = tool_data.auto_panning.shift_viewport(input, viewport, responses)
if let Some(bounds) = &mut tool_data.bounding_box_manager { && let Some(bounds) = &mut tool_data.bounding_box_manager
bounds.center_of_transformation += shift; {
bounds.original_bound_transform.translation += shift; bounds.center_of_transformation += shift;
} bounds.original_bound_transform.translation += shift;
} }
self self
@@ -1499,10 +1498,10 @@ impl Fsm for SelectToolFsmState {
tool_data.axis_align = false; tool_data.axis_align = false;
tool_data.snap_manager.cleanup(responses); tool_data.snap_manager.cleanup(responses);
if !matches!(self, SelectToolFsmState::DraggingPivot) { if !matches!(self, SelectToolFsmState::DraggingPivot)
if let Some(bounds) = &mut tool_data.bounding_box_manager { && let Some(bounds) = &mut tool_data.bounding_box_manager
bounds.original_transforms.clear(); {
} bounds.original_transforms.clear();
} }
let selection = tool_data.nested_selection_behavior; let selection = tool_data.nested_selection_behavior;
@@ -560,10 +560,10 @@ impl Fsm for TextToolFsmState {
bounding_box_manager.render_quad(&mut overlay_context); bounding_box_manager.render_quad(&mut overlay_context);
// Draw red overlay if text is clipped // Draw red overlay if text is clipped
let transformed_quad = layer_transform * bounds; let transformed_quad = layer_transform * bounds;
if let Some((text, font, typesetting, _)) = graph_modification_utils::get_text(layer.unwrap(), &document.network_interface) { if let Some((text, font, typesetting, _)) = graph_modification_utils::get_text(layer.unwrap(), &document.network_interface)
if lines_clipping(text.as_str(), font, font_cache, typesetting) { && lines_clipping(text.as_str(), font, font_cache, typesetting)
overlay_context.line(transformed_quad.0[2], transformed_quad.0[3], Some(COLOR_OVERLAY_RED), Some(3.)); {
} overlay_context.line(transformed_quad.0[2], transformed_quad.0[3], Some(COLOR_OVERLAY_RED), Some(3.));
} }
bounding_box_manager.render_overlays(&mut overlay_context, false); bounding_box_manager.render_overlays(&mut overlay_context, false);
@@ -697,63 +697,63 @@ impl Fsm for TextToolFsmState {
TextToolFsmState::Dragging TextToolFsmState::Dragging
} }
(TextToolFsmState::ResizingBounds, TextToolMessage::PointerMove { center, lock_ratio }) => { (TextToolFsmState::ResizingBounds, TextToolMessage::PointerMove { center, lock_ratio }) => {
if let Some(bounds) = &mut tool_data.bounding_box_manager { if let Some(bounds) = &mut tool_data.bounding_box_manager
if let Some(movement) = &mut bounds.selected_edges { && let Some(movement) = &mut bounds.selected_edges
let (centered, constrain) = (input.keyboard.key(center), input.keyboard.key(lock_ratio)); {
let center_position = centered.then_some(bounds.center_of_transformation); let (centered, constrain) = (input.keyboard.key(center), input.keyboard.key(lock_ratio));
let center_position = centered.then_some(bounds.center_of_transformation);
let Some(dragging_layer) = tool_data.layer_dragging else { return TextToolFsmState::Ready }; let Some(dragging_layer) = tool_data.layer_dragging else { return TextToolFsmState::Ready };
let Some(node_id) = graph_modification_utils::get_text_id(dragging_layer.id, &document.network_interface) else { let Some(node_id) = graph_modification_utils::get_text_id(dragging_layer.id, &document.network_interface) else {
warn!("Cannot get text node id"); warn!("Cannot get text node id");
tool_data.layer_dragging.take(); tool_data.layer_dragging.take();
return TextToolFsmState::Ready; return TextToolFsmState::Ready;
}; };
let selected = vec![dragging_layer.id]; let selected = vec![dragging_layer.id];
let snap = Some(SizeSnapData { let snap = Some(SizeSnapData {
manager: &mut tool_data.resize.snap_manager, manager: &mut tool_data.resize.snap_manager,
points: &mut tool_data.snap_candidates, points: &mut tool_data.snap_candidates,
snap_data: SnapData::ignore(document, input, viewport, &selected), snap_data: SnapData::ignore(document, input, viewport, &selected),
}); });
let (position, size) = movement.new_size(input.mouse.position, bounds.original_bound_transform, center_position, constrain, snap); let (position, size) = movement.new_size(input.mouse.position, bounds.original_bound_transform, center_position, constrain, snap);
// Normalize so the size is always positive // Normalize so the size is always positive
let (position, size) = (position.min(position + size), size.abs()); let (position, size) = (position.min(position + size), size.abs());
// Compute the offset needed for the top left in bounds space // Compute the offset needed for the top left in bounds space
let original_position = movement.bounds[0].min(movement.bounds[1]); let original_position = movement.bounds[0].min(movement.bounds[1]);
let translation_bounds_space = position - original_position; let translation_bounds_space = position - original_position;
// Compute a transformation from bounds->viewport->layer // Compute a transformation from bounds->viewport->layer
let transform_to_layer = document.metadata().transform_to_viewport(dragging_layer.id).inverse() * bounds.original_bound_transform; let transform_to_layer = document.metadata().transform_to_viewport(dragging_layer.id).inverse() * bounds.original_bound_transform;
let size_layer = transform_to_layer.transform_vector2(size); let size_layer = transform_to_layer.transform_vector2(size);
// Find the translation necessary from the original position in viewport space // Find the translation necessary from the original position in viewport space
let translation_viewport = bounds.original_bound_transform.transform_vector2(translation_bounds_space); let translation_viewport = bounds.original_bound_transform.transform_vector2(translation_bounds_space);
responses.add(NodeGraphMessage::SetInput { responses.add(NodeGraphMessage::SetInput {
input_connector: InputConnector::node(node_id, 6), input_connector: InputConnector::node(node_id, 6),
input: NodeInput::value(TaggedValue::OptionalF64(Some(size_layer.x)), false), input: NodeInput::value(TaggedValue::OptionalF64(Some(size_layer.x)), false),
}); });
responses.add(NodeGraphMessage::SetInput { responses.add(NodeGraphMessage::SetInput {
input_connector: InputConnector::node(node_id, 7), input_connector: InputConnector::node(node_id, 7),
input: NodeInput::value(TaggedValue::OptionalF64(Some(size_layer.y)), false), input: NodeInput::value(TaggedValue::OptionalF64(Some(size_layer.y)), false),
}); });
responses.add(GraphOperationMessage::TransformSet { responses.add(GraphOperationMessage::TransformSet {
layer: dragging_layer.id, layer: dragging_layer.id,
transform: DAffine2::from_translation(translation_viewport) * document.metadata().document_to_viewport * dragging_layer.original_transform, transform: DAffine2::from_translation(translation_viewport) * document.metadata().document_to_viewport * dragging_layer.original_transform,
transform_in: TransformIn::Viewport, transform_in: TransformIn::Viewport,
skip_rerender: false, skip_rerender: false,
}); });
responses.add(NodeGraphMessage::RunDocumentGraph); responses.add(NodeGraphMessage::RunDocumentGraph);
// Auto-panning // Auto-panning
let messages = [ let messages = [
TextToolMessage::PointerOutsideViewport { center, lock_ratio }.into(), TextToolMessage::PointerOutsideViewport { center, lock_ratio }.into(),
TextToolMessage::PointerMove { center, lock_ratio }.into(), TextToolMessage::PointerMove { center, lock_ratio }.into(),
]; ];
tool_data.auto_panning.setup_by_mouse_position(input, viewport, &messages, responses); tool_data.auto_panning.setup_by_mouse_position(input, viewport, &messages, responses);
}
} }
TextToolFsmState::ResizingBounds TextToolFsmState::ResizingBounds
} }
@@ -771,11 +771,11 @@ impl Fsm for TextToolFsmState {
} }
(TextToolFsmState::ResizingBounds | TextToolFsmState::Dragging, TextToolMessage::PointerOutsideViewport { .. }) => { (TextToolFsmState::ResizingBounds | TextToolFsmState::Dragging, TextToolMessage::PointerOutsideViewport { .. }) => {
// Auto-panning // Auto-panning
if let Some(shift) = tool_data.auto_panning.shift_viewport(input, viewport, responses) { if let Some(shift) = tool_data.auto_panning.shift_viewport(input, viewport, responses)
if let Some(bounds) = &mut tool_data.bounding_box_manager { && let Some(bounds) = &mut tool_data.bounding_box_manager
bounds.center_of_transformation += shift; {
bounds.original_bound_transform.translation += shift; bounds.center_of_transformation += shift;
} bounds.original_bound_transform.translation += shift;
} }
self self
@@ -808,11 +808,9 @@ impl Fsm for TextToolFsmState {
let has_dragged = (start - end).length_squared() > DRAG_THRESHOLD * DRAG_THRESHOLD; let has_dragged = (start - end).length_squared() > DRAG_THRESHOLD * DRAG_THRESHOLD;
// Check if the user has clicked (no dragging) on some existing text // Check if the user has clicked (no dragging) on some existing text
if !has_dragged { if !has_dragged && let Some(clicked_text_layer_path) = TextToolData::check_click(document, input, font_cache) {
if let Some(clicked_text_layer_path) = TextToolData::check_click(document, input, font_cache) { tool_data.start_editing_layer(clicked_text_layer_path, self, document, font_cache, responses);
tool_data.start_editing_layer(clicked_text_layer_path, self, document, font_cache, responses); return TextToolFsmState::Editing;
return TextToolFsmState::Editing;
}
} }
// Otherwise create some new text // Otherwise create some new text
@@ -846,11 +844,9 @@ impl Fsm for TextToolFsmState {
bounds.original_transforms.clear(); bounds.original_transforms.clear();
} }
if drag_too_small { if drag_too_small && let Some(layer_info) = &tool_data.layer_dragging {
if let Some(layer_info) = &tool_data.layer_dragging { tool_data.start_editing_layer(layer_info.id, self, document, font_cache, responses);
tool_data.start_editing_layer(layer_info.id, self, document, font_cache, responses); return TextToolFsmState::Editing;
return TextToolFsmState::Editing;
}
} }
tool_data.layer_dragging.take(); tool_data.layer_dragging.take();
+2 -2
View File
@@ -272,7 +272,7 @@ impl NodeGraphExecutor {
use image::{ImageFormat, RgbImage, RgbaImage}; use image::{ImageFormat, RgbImage, RgbaImage};
let Some(image) = RgbaImage::from_raw(width, height, data) else { let Some(image) = RgbaImage::from_raw(width, height, data) else {
return Err(format!("Failed to create image buffer for export")); return Err("Failed to create image buffer for export".to_string());
}; };
let mut encoded = Vec::new(); let mut encoded = Vec::new();
@@ -298,7 +298,7 @@ impl NodeGraphExecutor {
} }
} }
FileType::Svg => { FileType::Svg => {
return Err(format!("SVG cannot be exported from an image buffer")); return Err("SVG cannot be exported from an image buffer".to_string());
} }
} }
+53 -55
View File
@@ -686,10 +686,10 @@ impl NodeNetwork {
fn replace_node_inputs(&mut self, node_id: NodeId, old_input: (NodeId, usize), new_input: (NodeId, usize)) { fn replace_node_inputs(&mut self, node_id: NodeId, old_input: (NodeId, usize), new_input: (NodeId, usize)) {
let Some(node) = self.nodes.get_mut(&node_id) else { return }; let Some(node) = self.nodes.get_mut(&node_id) else { return };
node.inputs.iter_mut().for_each(|input| { node.inputs.iter_mut().for_each(|input| {
if let NodeInput::Node { node_id: input_id, output_index, .. } = input { if let NodeInput::Node { node_id: input_id, output_index, .. } = input
if (*input_id, *output_index) == old_input { && (*input_id, *output_index) == old_input
(*input_id, *output_index) = new_input; {
} (*input_id, *output_index) = new_input;
} }
}); });
} }
@@ -738,10 +738,10 @@ impl NodeNetwork {
let mut are_inputs_used = vec![false; number_of_inputs]; let mut are_inputs_used = vec![false; number_of_inputs];
for node in &self.nodes { for node in &self.nodes {
for node_input in &node.1.inputs { for node_input in &node.1.inputs {
if let NodeInput::Import { import_index, .. } = node_input { if let NodeInput::Import { import_index, .. } = node_input
if let Some(is_used) = are_inputs_used.get_mut(*import_index) { && let Some(is_used) = are_inputs_used.get_mut(*import_index)
*is_used = true; {
} *is_used = true;
} }
} }
} }
@@ -937,50 +937,48 @@ impl NodeNetwork {
fn remove_id_node(&mut self, id: NodeId) -> Result<(), String> { fn remove_id_node(&mut self, id: NodeId) -> Result<(), String> {
let node = self.nodes.get(&id).ok_or_else(|| format!("Node with id {id} does not exist"))?.clone(); let node = self.nodes.get(&id).ok_or_else(|| format!("Node with id {id} does not exist"))?.clone();
if let DocumentNodeImplementation::ProtoNode(ident) = &node.implementation { if let DocumentNodeImplementation::ProtoNode(ident) = &node.implementation
if ident.name == "graphene_core::ops::IdentityNode" { && *ident == graphene_core::ops::identity::IDENTIFIER
assert_eq!(node.inputs.len(), 1, "Id node has more than one input"); {
if let NodeInput::Node { node_id, output_index, .. } = node.inputs[0] { assert_eq!(node.inputs.len(), 1, "Id node has more than one input");
let node_input_output_index = output_index; if let NodeInput::Node { node_id, output_index, .. } = node.inputs[0] {
// TODO fix let node_input_output_index = output_index;
if let Some(input_node) = self.nodes.get_mut(&node_id) { // TODO fix
for &dep in &node.original_location.dependants[0] { if let Some(input_node) = self.nodes.get_mut(&node_id) {
input_node.original_location.dependants[output_index].push(dep); for &dep in &node.original_location.dependants[0] {
input_node.original_location.dependants[output_index].push(dep);
}
}
let input_node_id = node_id;
for output in self.nodes.values_mut() {
for (index, input) in output.inputs.iter_mut().enumerate() {
if let NodeInput::Node {
node_id: output_node_id,
output_index: output_output_index,
..
} = input && *output_node_id == id
{
*output_node_id = input_node_id;
*output_output_index = node_input_output_index;
let input_source = &mut output.original_location.inputs_source;
for source in node.original_location.inputs(index) {
input_source.insert(source, index);
}
} }
} }
for node_input in self.exports.iter_mut() {
let input_node_id = node_id; if let NodeInput::Node { node_id, output_index, .. } = node_input
for output in self.nodes.values_mut() { && *node_id == id
for (index, input) in output.inputs.iter_mut().enumerate() { {
if let NodeInput::Node { *node_id = input_node_id;
node_id: output_node_id, *output_index = node_input_output_index;
output_index: output_output_index,
..
} = input
{
if *output_node_id == id {
*output_node_id = input_node_id;
*output_output_index = node_input_output_index;
let input_source = &mut output.original_location.inputs_source;
for source in node.original_location.inputs(index) {
input_source.insert(source, index);
}
}
}
}
for node_input in self.exports.iter_mut() {
if let NodeInput::Node { node_id, output_index, .. } = node_input {
if *node_id == id {
*node_id = input_node_id;
*output_index = node_input_output_index;
}
}
} }
} }
} }
self.nodes.remove(&id);
} }
self.nodes.remove(&id);
} }
Ok(()) Ok(())
} }
@@ -1047,15 +1045,15 @@ impl NodeNetwork {
let nodes: Vec<_> = self.nodes.into_iter().map(|(id, node)| (id, node.resolve_proto_node())).collect(); let nodes: Vec<_> = self.nodes.into_iter().map(|(id, node)| (id, node.resolve_proto_node())).collect();
// Create a network to evaluate each output // Create a network to evaluate each output
if self.exports.len() == 1 { if self.exports.len() == 1
if let NodeInput::Node { node_id, .. } = self.exports[0] { && let NodeInput::Node { node_id, .. } = self.exports[0]
return vec![ProtoNetwork { {
inputs: Vec::new(), return vec![ProtoNetwork {
output: node_id, inputs: Vec::new(),
nodes, output: node_id,
}] nodes,
.into_iter(); }]
} .into_iter();
} }
// Create a network to evaluate each output // Create a network to evaluate each output
@@ -308,10 +308,10 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
let mut new_name = id.name.replace('\n', " "); let mut new_name = id.name.replace('\n', " ");
// Remove struct generics for all nodes except for the IntoNode and ConvertNode // Remove struct generics for all nodes except for the IntoNode and ConvertNode
if !(new_name.contains("IntoNode") || new_name.contains("ConvertNode")) { if !(new_name.contains("IntoNode") || new_name.contains("ConvertNode"))
if let Some((path, _generics)) = new_name.split_once("<") { && let Some((path, _generics)) = new_name.split_once("<")
new_name = path.to_string(); {
} new_name = path.to_string();
} }
let nid = ProtoNodeIdentifier { name: Cow::Owned(new_name) }; let nid = ProtoNodeIdentifier { name: Cow::Owned(new_name) };
@@ -50,14 +50,12 @@ impl BasicItem {
let token: LitStr = attribute.parse_args()?; let token: LitStr = attribute.parse_args()?;
self.icon = Some(token.value()); self.icon = Some(token.value());
} }
if attribute.path().is_ident("doc") { if attribute.path().is_ident("doc")
if let Meta::NameValue(meta_name_value) = &attribute.meta { && let Meta::NameValue(meta_name_value) = &attribute.meta
if let Expr::Lit(el) = &meta_name_value.value { && let Expr::Lit(el) = &meta_name_value.value
if let syn::Lit::Str(token) = &el.lit { && let syn::Lit::Str(token) = &el.lit
self.description = Some(token.value()); {
} self.description = Some(token.value());
}
}
} }
Ok(()) Ok(())
} }
+16 -17
View File
@@ -530,10 +530,10 @@ fn parse_field(pat_ident: PatIdent, ty: Type, attrs: &[Attribute]) -> syn::Resul
}) })
}) })
.transpose()?; .transpose()?;
if let Some(range) = &number_mode_range { if let Some(range) = &number_mode_range
if range.elems.len() != 2 { && range.elems.len() != 2
return Err(Error::new_spanned(range, "Expected a tuple of two values for `range` for the min and max, respectively")); {
} return Err(Error::new_spanned(range, "Expected a tuple of two values for `range` for the min and max, respectively"));
} }
let unit = extract_attribute(attrs, "unit") let unit = extract_attribute(attrs, "unit")
@@ -641,20 +641,19 @@ fn parse_field(pat_ident: PatIdent, ty: Type, attrs: &[Attribute]) -> syn::Resul
fn parse_node_type(ty: &Type) -> (bool, Option<Type>, Option<Type>) { fn parse_node_type(ty: &Type) -> (bool, Option<Type>, Option<Type>) {
if let Type::ImplTrait(impl_trait) = ty { if let Type::ImplTrait(impl_trait) = ty {
for bound in &impl_trait.bounds { for bound in &impl_trait.bounds {
if let syn::TypeParamBound::Trait(trait_bound) = bound { if let syn::TypeParamBound::Trait(trait_bound) = bound
if trait_bound.path.segments.last().is_some_and(|seg| seg.ident == "Node") { && trait_bound.path.segments.last().is_some_and(|seg| seg.ident == "Node")
if let syn::PathArguments::AngleBracketed(args) = &trait_bound.path.segments.last().unwrap().arguments { && let syn::PathArguments::AngleBracketed(args) = &trait_bound.path.segments.last().unwrap().arguments
let input_type = args.args.iter().find_map(|arg| if let syn::GenericArgument::Type(ty) = arg { Some(ty.clone()) } else { None }); {
let output_type = args.args.iter().find_map(|arg| { let input_type = args.args.iter().find_map(|arg| if let syn::GenericArgument::Type(ty) = arg { Some(ty.clone()) } else { None });
if let syn::GenericArgument::AssocType(assoc_type) = arg { let output_type = args.args.iter().find_map(|arg| {
if assoc_type.ident == "Output" { Some(assoc_type.ty.clone()) } else { None } if let syn::GenericArgument::AssocType(assoc_type) = arg {
} else { if assoc_type.ident == "Output" { Some(assoc_type.ty.clone()) } else { None }
None } else {
} None
});
return (true, input_type, output_type);
} }
} });
return (true, input_type, output_type);
} }
} }
} }