mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2025-08-04 13:30:48 +00:00
Improve layer panel positioning for upstream nodes (#1928)
* Improve layer panel positioning for upstream nodes * highlight parents * Final improvements * Fill for selection box, bug fixes * Code review --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
parent
da13f21486
commit
2bd213f1aa
21 changed files with 321 additions and 255 deletions
|
@ -70,6 +70,7 @@ pub const ASYMPTOTIC_EFFECT: f64 = 0.5;
|
|||
pub const SCALE_EFFECT: f64 = 0.5;
|
||||
|
||||
// Colors
|
||||
// Keep changes to these colors updated with `Editor.svelte`
|
||||
pub const COLOR_OVERLAY_BLUE: &str = "#00a8ff";
|
||||
pub const COLOR_OVERLAY_YELLOW: &str = "#ffc848";
|
||||
pub const COLOR_OVERLAY_WHITE: &str = "#ffffff";
|
||||
|
|
|
@ -648,6 +648,15 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
|
|||
resize_opposite_corner,
|
||||
} => {
|
||||
self.backup(responses);
|
||||
if self.graph_view_overlay_open {
|
||||
responses.add(NodeGraphMessage::ShiftNodes {
|
||||
node_ids: self.network_interface.selected_nodes(&[]).unwrap().selected_nodes().cloned().collect(),
|
||||
displacement_x: delta_x.signum() as i32,
|
||||
displacement_y: delta_y.signum() as i32,
|
||||
move_upstream: ipp.keyboard.get(Key::Shift as usize),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
let opposite_corner = ipp.keyboard.key(resize_opposite_corner);
|
||||
let delta = DVec2::new(delta_x, delta_y);
|
||||
|
|
|
@ -809,6 +809,11 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
|
|||
}
|
||||
}
|
||||
|
||||
// Auto convert node to layer when inserting on a single stack wire
|
||||
if stack_wires.len() == 1 && node_wires.is_empty() {
|
||||
network_interface.set_to_node_or_layer(&selected_node_id, selection_network_path, true)
|
||||
}
|
||||
|
||||
let overlapping_wire = if network_interface.is_layer(&selected_node_id, selection_network_path) {
|
||||
if stack_wires.len() == 1 {
|
||||
stack_wires.first()
|
||||
|
@ -987,71 +992,12 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
|
|||
network_interface.set_input(&input_connector, input, selection_network_path);
|
||||
}
|
||||
NodeGraphMessage::ShiftNodes {
|
||||
mut node_ids,
|
||||
node_ids,
|
||||
displacement_x,
|
||||
displacement_y,
|
||||
move_upstream,
|
||||
} => {
|
||||
if move_upstream {
|
||||
for node_id in network_interface.upstream_flow_back_from_nodes(node_ids.clone(), selection_network_path, network_interface::FlowType::UpstreamFlow) {
|
||||
if network_interface.is_absolute(&node_id, selection_network_path) && node_ids.iter().all(|id| *id != node_id) {
|
||||
node_ids.push(node_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut filtered_node_ids = Vec::new();
|
||||
for selected_node in &node_ids {
|
||||
// Deselect chain nodes upstream from a selected layer
|
||||
if network_interface.is_chain(selected_node, selection_network_path)
|
||||
&& network_interface
|
||||
.downstream_layer(selected_node, selection_network_path)
|
||||
.is_some_and(|downstream_layer| node_ids.contains(&downstream_layer.to_node()))
|
||||
{
|
||||
// Deselect stack nodes upstream from a selected layer
|
||||
continue;
|
||||
}
|
||||
|
||||
// Deselect stack nodes upstream from a selected layer
|
||||
let mut is_upstream_from_selected_absolute_layer = false;
|
||||
let mut current_node = *selected_node;
|
||||
loop {
|
||||
if network_interface.is_absolute(selected_node, selection_network_path) {
|
||||
break;
|
||||
}
|
||||
let Some(outward_wires) = network_interface.outward_wires(selection_network_path) else {
|
||||
break;
|
||||
};
|
||||
let Some(outward_wires) = outward_wires.get(&OutputConnector::node(current_node, 0)) else {
|
||||
break;
|
||||
};
|
||||
if outward_wires.is_empty() {
|
||||
break;
|
||||
}
|
||||
let Some(downstream_node) = outward_wires[0].node_id() else {
|
||||
break;
|
||||
};
|
||||
if outward_wires[0].input_index() != 0 {
|
||||
break;
|
||||
}
|
||||
if !network_interface.is_layer(&downstream_node, selection_network_path) {
|
||||
break;
|
||||
}
|
||||
// Break the iteration if the layer is absolute(top of stack), or it is selected
|
||||
if network_interface.is_absolute(&downstream_node, selection_network_path) || node_ids.contains(&downstream_node) {
|
||||
is_upstream_from_selected_absolute_layer = node_ids.contains(&downstream_node);
|
||||
break;
|
||||
}
|
||||
current_node = downstream_node;
|
||||
}
|
||||
if !is_upstream_from_selected_absolute_layer {
|
||||
filtered_node_ids.push(*selected_node)
|
||||
}
|
||||
}
|
||||
|
||||
for node_id in filtered_node_ids {
|
||||
network_interface.shift_node(&node_id, IVec2::new(displacement_x, displacement_y), selection_network_path);
|
||||
}
|
||||
network_interface.shift_selected_nodes(node_ids, displacement_x, displacement_y, move_upstream, selection_network_path);
|
||||
|
||||
if graph_view_overlay_open {
|
||||
responses.add(NodeGraphMessage::SendGraph);
|
||||
|
@ -1225,8 +1171,12 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
|
|||
log::error!("Could not get selected nodes in PointerMove");
|
||||
return;
|
||||
};
|
||||
let previous_selection = selected_nodes.selected_nodes_ref().clone();
|
||||
let mut nodes = if shift { previous_selection.clone() } else { Vec::new() };
|
||||
let previous_selection = selected_nodes.selected_nodes_ref().iter().cloned().collect::<HashSet<_>>();
|
||||
let mut nodes = if shift {
|
||||
selected_nodes.selected_nodes_ref().iter().cloned().collect::<HashSet<_>>()
|
||||
} else {
|
||||
HashSet::new()
|
||||
};
|
||||
let all_nodes = network_metadata.persistent_metadata.node_metadata.keys().cloned().collect::<Vec<_>>();
|
||||
for node_id in all_nodes {
|
||||
let Some(click_targets) = network_interface.node_click_targets(&node_id, selection_network_path) else {
|
||||
|
@ -1237,11 +1187,13 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
|
|||
.node_click_target
|
||||
.intersect_rectangle(Quad::from_box([box_selection_start, box_selection_end_graph]), DAffine2::IDENTITY)
|
||||
{
|
||||
nodes.push(node_id);
|
||||
nodes.insert(node_id);
|
||||
}
|
||||
}
|
||||
if nodes != previous_selection {
|
||||
responses.add(NodeGraphMessage::SelectedNodesSet { nodes });
|
||||
responses.add(NodeGraphMessage::SelectedNodesSet {
|
||||
nodes: nodes.into_iter().collect::<Vec<_>>(),
|
||||
});
|
||||
}
|
||||
responses.add(FrontendMessage::UpdateBox { box_selection })
|
||||
}
|
||||
|
@ -1777,10 +1729,35 @@ impl NodeGraphMessageHandler {
|
|||
.selected_layers(network_interface.document_metadata())
|
||||
.map(|layer| layer.to_node())
|
||||
.collect::<HashSet<_>>();
|
||||
|
||||
let mut selected_parents = HashSet::new();
|
||||
for selected_layer in &selected_layers {
|
||||
for ancestor in LayerNodeIdentifier::new(*selected_layer, network_interface).ancestors(network_interface.document_metadata()) {
|
||||
if ancestor != LayerNodeIdentifier::ROOT_PARENT && !selected_layers.contains(&ancestor.to_node()) {
|
||||
selected_parents.insert(ancestor.to_node());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (&node_id, node_metadata) in &network_interface.network_metadata(&[]).unwrap().persistent_metadata.node_metadata {
|
||||
if node_metadata.persistent_metadata.is_layer() {
|
||||
let layer = LayerNodeIdentifier::new(node_id, network_interface);
|
||||
|
||||
let children_allowed =
|
||||
// The layer has other layers as children along the secondary input's horizontal flow
|
||||
layer.has_children(network_interface.document_metadata())
|
||||
|| (
|
||||
// Check if the last node in the chain has an exposed left input
|
||||
network_interface.upstream_flow_back_from_nodes(vec![node_id], &[], network_interface::FlowType::HorizontalFlow).last().is_some_and(|node_id|
|
||||
network_interface.network(&[]).unwrap().nodes.get(&node_id).map_or_else(||{log::error!("Could not get node {node_id} in update_layer_panel"); false}, |node| {
|
||||
if network_interface.is_layer(&node_id, &[]) {
|
||||
node.inputs.iter().filter(|input| input.is_exposed_to_frontend(true)).nth(1).is_some_and(|input| input.as_value().is_some())
|
||||
} else {
|
||||
node.inputs.iter().filter(|input| input.is_exposed_to_frontend(true)).nth(0).is_some_and(|input| input.as_value().is_some())
|
||||
}
|
||||
}))
|
||||
);
|
||||
|
||||
let parents_visible = layer.ancestors(network_interface.document_metadata()).filter(|&ancestor| ancestor != layer).all(|layer| {
|
||||
if layer != LayerNodeIdentifier::ROOT_PARENT {
|
||||
network_interface.network(&[]).unwrap().nodes.get(&layer.to_node()).map(|node| node.visible).unwrap_or_default()
|
||||
|
@ -1797,30 +1774,26 @@ impl NodeGraphMessageHandler {
|
|||
}
|
||||
});
|
||||
|
||||
let is_selected_parent = selected_parents.contains(&node_id);
|
||||
|
||||
let data = LayerPanelEntry {
|
||||
id: node_id,
|
||||
children_allowed:
|
||||
// The layer has other layers as children along the secondary input's horizontal flow
|
||||
layer.has_children(network_interface.document_metadata())
|
||||
|| (
|
||||
// At least one secondary input is exposed on this layer node
|
||||
network_interface.network(&[]).unwrap().nodes.get(&node_id).map_or_else(||{log::error!("Could not get node {node_id} in update_layer_panel"); false}, |node_id| node_id.inputs.iter().skip(1).any(|input| input.is_exposed())) &&
|
||||
// But nothing is connected to it, since we only get 1 item (ourself) when we ask for the flow from the secondary input
|
||||
network_interface.upstream_flow_back_from_nodes(vec![node_id], &[], network_interface::FlowType::HorizontalFlow).count() == 1
|
||||
),
|
||||
children_allowed,
|
||||
children_present: layer.has_children(network_interface.document_metadata()),
|
||||
expanded: layer.has_children(network_interface.document_metadata()) && !collapsed.0.contains(&layer),
|
||||
depth: layer.ancestors(network_interface.document_metadata()).count() - 1,
|
||||
parent_id: layer.parent(network_interface.document_metadata()).and_then(|parent| if parent != LayerNodeIdentifier::ROOT_PARENT { Some(parent.to_node()) } else { None }),
|
||||
//reference: network_interface.get_reference(&node_id),
|
||||
parent_id: layer
|
||||
.parent(network_interface.document_metadata())
|
||||
.and_then(|parent| if parent != LayerNodeIdentifier::ROOT_PARENT { Some(parent.to_node()) } else { None }),
|
||||
alias: network_interface.frontend_display_name(&node_id, &[]),
|
||||
tooltip: if cfg!(debug_assertions) { format!("Layer ID: {node_id}") } else { "".into() },
|
||||
visible: network_interface.is_visible(&node_id, &[]),
|
||||
parents_visible,
|
||||
unlocked: !network_interface.is_locked(&node_id, &[]),
|
||||
parents_unlocked,
|
||||
selected: selected_layers.contains(&node_id),
|
||||
selected: selected_layers.contains(&node_id) || is_selected_parent,
|
||||
in_selected_network: selection_network_path.is_empty(),
|
||||
selected_parent: is_selected_parent,
|
||||
};
|
||||
responses.add(FrontendMessage::UpdateDocumentLayerDetails { data });
|
||||
}
|
||||
|
|
|
@ -38,10 +38,10 @@ fn grid_overlay_rectangular(document: &DocumentMessageHandler, overlay_context:
|
|||
} else {
|
||||
DVec2::new(secondary_pos, primary_end)
|
||||
};
|
||||
overlay_context.colored_line(
|
||||
overlay_context.line(
|
||||
document_to_viewport.transform_point2(start),
|
||||
document_to_viewport.transform_point2(end),
|
||||
&("#".to_string() + &grid_color.rgba_hex()),
|
||||
Some(&("#".to_string() + &grid_color.rgba_hex())),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -114,10 +114,10 @@ fn grid_overlay_isometric(document: &DocumentMessageHandler, overlay_context: &m
|
|||
let x_pos = (((min_x - origin.x) / spacing).ceil() + line_index as f64) * spacing + origin.x;
|
||||
let start = DVec2::new(x_pos, min_y);
|
||||
let end = DVec2::new(x_pos, max_y);
|
||||
overlay_context.colored_line(
|
||||
overlay_context.line(
|
||||
document_to_viewport.transform_point2(start),
|
||||
document_to_viewport.transform_point2(end),
|
||||
&("#".to_string() + &grid_color.rgba_hex()),
|
||||
Some(&("#".to_string() + &grid_color.rgba_hex())),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -132,10 +132,10 @@ fn grid_overlay_isometric(document: &DocumentMessageHandler, overlay_context: &m
|
|||
let y_pos = (((inverse_project(&min_y) - origin.y) / spacing).ceil() + line_index as f64) * spacing + origin.y;
|
||||
let start = DVec2::new(min_x, project(&DVec2::new(min_x, y_pos)));
|
||||
let end = DVec2::new(max_x, project(&DVec2::new(max_x, y_pos)));
|
||||
overlay_context.colored_line(
|
||||
overlay_context.line(
|
||||
document_to_viewport.transform_point2(start),
|
||||
document_to_viewport.transform_point2(end),
|
||||
&("#".to_string() + &grid_color.rgba_hex()),
|
||||
Some(&("#".to_string() + &grid_color.rgba_hex())),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,17 +39,17 @@ pub fn path_overlays(document: &DocumentMessageHandler, shape_editor: &mut Shape
|
|||
let not_under_anchor = |position: DVec2, anchor: DVec2| position.distance_squared(anchor) >= HIDE_HANDLE_DISTANCE * HIDE_HANDLE_DISTANCE;
|
||||
match bezier.handles {
|
||||
bezier_rs::BezierHandles::Quadratic { handle } if not_under_anchor(handle, bezier.start) && not_under_anchor(handle, bezier.end) => {
|
||||
overlay_context.line(handle, bezier.start);
|
||||
overlay_context.line(handle, bezier.end);
|
||||
overlay_context.line(handle, bezier.start, None);
|
||||
overlay_context.line(handle, bezier.end, None);
|
||||
overlay_context.manipulator_handle(handle, is_selected(selected, ManipulatorPointId::PrimaryHandle(segment_id)));
|
||||
}
|
||||
bezier_rs::BezierHandles::Cubic { handle_start, handle_end } => {
|
||||
if not_under_anchor(handle_start, bezier.start) {
|
||||
overlay_context.line(handle_start, bezier.start);
|
||||
overlay_context.line(handle_start, bezier.start, None);
|
||||
overlay_context.manipulator_handle(handle_start, is_selected(selected, ManipulatorPointId::PrimaryHandle(segment_id)));
|
||||
}
|
||||
if not_under_anchor(handle_end, bezier.end) {
|
||||
overlay_context.line(handle_end, bezier.end);
|
||||
overlay_context.line(handle_end, bezier.end, None);
|
||||
overlay_context.manipulator_handle(handle_end, is_selected(selected, ManipulatorPointId::EndHandle(segment_id)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,22 +31,22 @@ impl core::hash::Hash for OverlayContext {
|
|||
}
|
||||
|
||||
impl OverlayContext {
|
||||
pub fn quad(&mut self, quad: Quad) {
|
||||
pub fn quad(&mut self, quad: Quad, color_fill: Option<&str>) {
|
||||
self.render_context.begin_path();
|
||||
self.render_context.move_to(quad.0[3].x.round() - 0.5, quad.0[3].y.round() - 0.5);
|
||||
for i in 0..4 {
|
||||
self.render_context.line_to(quad.0[i].x.round() - 0.5, quad.0[i].y.round() - 0.5);
|
||||
}
|
||||
if let Some(color_fill) = color_fill {
|
||||
self.render_context.set_fill_style(&wasm_bindgen::JsValue::from_str(color_fill));
|
||||
self.render_context.fill();
|
||||
}
|
||||
self.render_context.set_stroke_style(&wasm_bindgen::JsValue::from_str(COLOR_OVERLAY_BLUE));
|
||||
self.render_context.stroke();
|
||||
}
|
||||
|
||||
pub fn line(&mut self, start: DVec2, end: DVec2) {
|
||||
self.dashed_line(start, end, None, None)
|
||||
}
|
||||
|
||||
pub fn colored_line(&mut self, start: DVec2, end: DVec2, color: &str) {
|
||||
self.dashed_line(start, end, Some(color), None)
|
||||
pub fn line(&mut self, start: DVec2, end: DVec2, color: Option<&str>) {
|
||||
self.dashed_line(start, end, color, None)
|
||||
}
|
||||
|
||||
pub fn dashed_line(&mut self, start: DVec2, end: DVec2, color: Option<&str>, dash_width: Option<f64>) {
|
||||
|
|
|
@ -299,32 +299,29 @@ impl NodeNetworkInterface {
|
|||
return None;
|
||||
};
|
||||
match &mut node_template.persistent_node_metadata.node_type_metadata {
|
||||
// TODO: Remove 2x2 offset and replace with layout system to find space for new node
|
||||
NodeTypePersistentMetadata::Layer(layer_metadata) => layer_metadata.position = LayerPosition::Absolute(position),
|
||||
NodeTypePersistentMetadata::Node(node_metadata) => node_metadata.position = NodePosition::Absolute(position),
|
||||
};
|
||||
}
|
||||
|
||||
// Ensure a chain node has a selected downstream layer
|
||||
if let NodeTypePersistentMetadata::Node(node_metadata) = &node_template.persistent_node_metadata.node_type_metadata {
|
||||
if matches!(node_metadata.position, NodePosition::Chain) {
|
||||
let Some(downstream_layer) = self.downstream_layer(node_id, network_path) else {
|
||||
log::error!("Could not get downstream layer in copy_nodes");
|
||||
return None;
|
||||
};
|
||||
if new_ids.keys().all(|key| *key != downstream_layer.to_node()) {
|
||||
let Some(position) = self.position(node_id, network_path) else {
|
||||
log::error!("Could not get position in create_node_template");
|
||||
return None;
|
||||
};
|
||||
node_template.persistent_node_metadata.node_type_metadata = NodeTypePersistentMetadata::Node(NodePersistentMetadata {
|
||||
position: NodePosition::Absolute(position + IVec2::new(2, 2)),
|
||||
});
|
||||
}
|
||||
}
|
||||
// Ensure a chain node has a selected downstream layer, and set absolute nodes to a chain if there is a downstream layer
|
||||
if self
|
||||
.downstream_layer(node_id, network_path)
|
||||
.map_or(true, |downstream_layer| new_ids.keys().all(|key| *key != downstream_layer.to_node()))
|
||||
{
|
||||
let Some(position) = self.position(node_id, network_path) else {
|
||||
log::error!("Could not get position in create_node_template");
|
||||
return None;
|
||||
};
|
||||
node_template.persistent_node_metadata.node_type_metadata = NodeTypePersistentMetadata::Node(NodePersistentMetadata {
|
||||
position: NodePosition::Absolute(position),
|
||||
});
|
||||
} else if let NodeTypePersistentMetadata::Node(NodePersistentMetadata { position }) = &mut node_template.persistent_node_metadata.node_type_metadata {
|
||||
*position = NodePosition::Chain;
|
||||
}
|
||||
|
||||
// Shift all absolute nodes 2 to the right and 2 down
|
||||
// TODO: Remove 2x2 offset and replace with layout system to find space for new node
|
||||
match &mut node_template.persistent_node_metadata.node_type_metadata {
|
||||
NodeTypePersistentMetadata::Layer(layer_metadata) => {
|
||||
if let LayerPosition::Absolute(position) = &mut layer_metadata.position {
|
||||
|
@ -2966,25 +2963,18 @@ impl NodeNetworkInterface {
|
|||
if matches!(reconnect_to_input, Some(NodeInput::Network { .. })) && matches!(input_to_disconnect, InputConnector::Export(_)) {
|
||||
self.disconnect_input(input_to_disconnect, network_path);
|
||||
} else if let Some(reconnect_input) = reconnect_to_input.take() {
|
||||
let original_position = reconnect_input.as_node().and_then(|downstream_node| self.position(&downstream_node, network_path));
|
||||
let original_downstream_position = input_to_disconnect.node_id().and_then(|downstream_id| self.position(&downstream_id, network_path));
|
||||
|
||||
self.set_input(input_to_disconnect, reconnect_input.clone(), network_path);
|
||||
if let Some(node_metadata) = self.node_metadata(deleting_node_id, network_path) {
|
||||
if let NodeTypePersistentMetadata::Layer(layer_metadata) = &node_metadata.persistent_metadata.node_type_metadata {
|
||||
if let LayerPosition::Stack(deleted_layer_offset) = layer_metadata.position {
|
||||
if let NodeInput::Node { node_id, .. } = reconnect_input {
|
||||
self.set_stack_position(&node_id, deleted_layer_offset, network_path);
|
||||
self.unload_upstream_node_click_targets(vec![node_id], network_path);
|
||||
}
|
||||
} else {
|
||||
// Move upstream layer to the top of the stack
|
||||
let Some(deleted_node_position) = self.position(deleting_node_id, network_path) else {
|
||||
log::error!("Could not get position in remove_references_from_network");
|
||||
return false;
|
||||
};
|
||||
if let NodeInput::Node { node_id, .. } = reconnect_input {
|
||||
self.set_absolute_position(&node_id, deleted_node_position, network_path);
|
||||
self.unload_upstream_node_click_targets(vec![node_id], network_path);
|
||||
}
|
||||
}
|
||||
if let (Some(original_position), Some(original_downstream_position)) = (original_position, original_downstream_position) {
|
||||
// Recalculate stack position (to keep layer in same place) if the upstream node is a layer in a chain
|
||||
if reconnect_input
|
||||
.as_node()
|
||||
.is_some_and(|upstream_node| !self.is_absolute(&upstream_node, network_path) && self.is_layer(&upstream_node, network_path))
|
||||
{
|
||||
let offset = (original_position.y - original_downstream_position.y - 3).max(0) as u32;
|
||||
self.set_stack_position(&reconnect_input.as_node().unwrap(), offset, network_path);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -3343,6 +3333,7 @@ impl NodeNetworkInterface {
|
|||
self.unload_all_nodes_bounding_box(network_path);
|
||||
}
|
||||
|
||||
/// Input connector is the input to the layer
|
||||
pub fn try_set_upstream_to_chain(&mut self, input_connector: &InputConnector, network_path: &[NodeId]) {
|
||||
// If the new input is to a non layer node on the same y position as the input connector, or the input connector is the side input of a layer, then set it to chain position
|
||||
if let InputConnector::Node {
|
||||
|
@ -3392,7 +3383,12 @@ impl NodeNetworkInterface {
|
|||
|
||||
pub fn force_set_upstream_to_chain(&mut self, node_id: &NodeId, network_path: &[NodeId]) {
|
||||
for upstream_id in self.upstream_flow_back_from_nodes(vec![*node_id], network_path, FlowType::HorizontalFlow).collect::<Vec<_>>().iter() {
|
||||
if !self.is_layer(upstream_id, network_path) && self.has_primary_output(node_id, network_path) {
|
||||
if !self.is_layer(upstream_id, network_path)
|
||||
&& self.has_primary_output(node_id, network_path)
|
||||
&& self
|
||||
.outward_wires(network_path)
|
||||
.is_some_and(|outward_wires| outward_wires.get(&OutputConnector::node(*upstream_id, 0)).is_some_and(|outward_wires| outward_wires.len() == 1))
|
||||
{
|
||||
self.set_chain_position(upstream_id, network_path);
|
||||
}
|
||||
// If there is an upstream layer then stop breaking the chain
|
||||
|
@ -3404,6 +3400,10 @@ impl NodeNetworkInterface {
|
|||
|
||||
/// node_id is the first chain node, not the layer
|
||||
fn set_upstream_chain_to_absolute(&mut self, node_id: &NodeId, network_path: &[NodeId]) {
|
||||
let Some(downstream_layer) = self.downstream_layer(node_id, network_path) else {
|
||||
log::error!("Could not get downstream layer in set_upstream_chain_to_absolute");
|
||||
return;
|
||||
};
|
||||
for upstream_id in self.upstream_flow_back_from_nodes(vec![*node_id], network_path, FlowType::HorizontalFlow).collect::<Vec<_>>().iter() {
|
||||
let Some(previous_position) = self.position(upstream_id, network_path) else {
|
||||
log::error!("Could not get position in set_to_node_or_layer");
|
||||
|
@ -3412,15 +3412,88 @@ impl NodeNetworkInterface {
|
|||
// Set any chain nodes to absolute positioning
|
||||
if self.is_chain(upstream_id, network_path) {
|
||||
self.set_absolute_position(upstream_id, previous_position, network_path);
|
||||
// Reload click target of the layer which used to encapsulate the chain
|
||||
self.unload_node_click_targets(&downstream_layer.to_node(), network_path);
|
||||
}
|
||||
// If there is an upstream layer then stop breaking the chain
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Reload click target of the layer which used to encapsulate the chain
|
||||
if let Some(downstream_layer) = self.downstream_layer(node_id, network_path) {
|
||||
self.unload_node_click_targets(&downstream_layer.to_node(), network_path);
|
||||
}
|
||||
|
||||
pub fn shift_selected_nodes(&mut self, mut node_ids: Vec<NodeId>, displacement_x: i32, mut displacement_y: i32, move_upstream: bool, network_path: &[NodeId]) {
|
||||
if move_upstream {
|
||||
for node_id in self.upstream_flow_back_from_nodes(node_ids.clone(), network_path, self::FlowType::UpstreamFlow).collect::<Vec<_>>() {
|
||||
if !node_ids.contains(&node_id) {
|
||||
node_ids.push(node_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut filtered_node_ids = Vec::new();
|
||||
for selected_node in &node_ids {
|
||||
// Deselect chain nodes upstream from a selected layer
|
||||
if self.is_chain(selected_node, network_path)
|
||||
&& self
|
||||
.downstream_layer(selected_node, network_path)
|
||||
.is_some_and(|downstream_layer| node_ids.contains(&downstream_layer.to_node()))
|
||||
{
|
||||
// Deselect stack nodes upstream from a selected layer
|
||||
continue;
|
||||
}
|
||||
|
||||
// Deselect stack nodes upstream from a selected layer
|
||||
let mut is_upstream_from_selected_absolute_layer = false;
|
||||
let mut current_node = *selected_node;
|
||||
loop {
|
||||
if self.is_absolute(selected_node, network_path) {
|
||||
break;
|
||||
}
|
||||
let Some(outward_wires) = self.outward_wires(network_path) else {
|
||||
break;
|
||||
};
|
||||
let Some(outward_wires) = outward_wires.get(&OutputConnector::node(current_node, 0)) else {
|
||||
break;
|
||||
};
|
||||
if outward_wires.is_empty() {
|
||||
break;
|
||||
}
|
||||
let Some(downstream_node) = outward_wires[0].node_id() else {
|
||||
break;
|
||||
};
|
||||
if outward_wires[0].input_index() != 0 {
|
||||
break;
|
||||
}
|
||||
if !self.is_layer(&downstream_node, network_path) {
|
||||
break;
|
||||
}
|
||||
// Break the iteration if the layer is absolute(top of stack), or it is selected
|
||||
if self.is_absolute(&downstream_node, network_path) || node_ids.contains(&downstream_node) {
|
||||
is_upstream_from_selected_absolute_layer = node_ids.contains(&downstream_node);
|
||||
break;
|
||||
}
|
||||
current_node = downstream_node;
|
||||
}
|
||||
if !is_upstream_from_selected_absolute_layer {
|
||||
filtered_node_ids.push(*selected_node)
|
||||
}
|
||||
}
|
||||
|
||||
// If a layer reaches the top of the stack, then stop moving up
|
||||
for node_id in &filtered_node_ids {
|
||||
let Some(node_metadata) = self.node_metadata(node_id, network_path) else { continue };
|
||||
let NodeTypePersistentMetadata::Layer(layer_metadata) = &node_metadata.persistent_metadata.node_type_metadata else {
|
||||
continue;
|
||||
};
|
||||
let LayerPosition::Stack(y_offset) = layer_metadata.position else { continue };
|
||||
if y_offset == 0 {
|
||||
displacement_y = displacement_y.max(0);
|
||||
}
|
||||
}
|
||||
|
||||
for node_id in filtered_node_ids {
|
||||
self.shift_node(&node_id, IVec2::new(displacement_x, displacement_y), network_path);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3433,6 +3506,14 @@ impl NodeNetworkInterface {
|
|||
if let NodeTypePersistentMetadata::Layer(layer_metadata) = &mut node_metadata.persistent_metadata.node_type_metadata {
|
||||
if let LayerPosition::Absolute(layer_position) = &mut layer_metadata.position {
|
||||
*layer_position += shift;
|
||||
self.unload_upstream_node_click_targets(vec![*node_id], network_path);
|
||||
for upstream_sibling in self
|
||||
.upstream_flow_back_from_nodes(vec![*node_id], network_path, FlowType::PrimaryFlow)
|
||||
.take_while(|upstream_layer| self.is_layer(upstream_layer, network_path))
|
||||
.collect::<Vec<_>>()
|
||||
{
|
||||
self.try_set_upstream_to_chain(&InputConnector::node(upstream_sibling, 1), network_path);
|
||||
}
|
||||
} else if let LayerPosition::Stack(y_offset) = &mut layer_metadata.position {
|
||||
let shifted_y_offset = *y_offset as i32 + shift.y;
|
||||
// A layer can only be shifted to a positive y_offset
|
||||
|
@ -3493,70 +3574,35 @@ impl NodeNetworkInterface {
|
|||
}
|
||||
}
|
||||
|
||||
// Disconnecting top of stack:
|
||||
// 1. Get vertical offset 1 between the top of stack and the upstream layer
|
||||
// 2. Set the position of the upstream layer to the top of stack
|
||||
// 3. Remove the top of stack
|
||||
// 4. Move top of stack to destination
|
||||
let Some(layer_to_move_position) = self.position(&layer.to_node(), network_path) else {
|
||||
log::error!("Could not get layer_to_move_position in move_layer_to_stack");
|
||||
return;
|
||||
};
|
||||
|
||||
// Disconnecting layer from stack:
|
||||
// 1. Get vertical offset 1 between the layer and the upstream layer sibling
|
||||
// 2. Get vertical offset 2 between the layer and the downstream layer sibling
|
||||
// 3. Set the offset of the upstream layer to offset 2
|
||||
// 4. Remove the layer from the stack
|
||||
// 5. Move the layer to the destination
|
||||
let previous_upstream_node = self.upstream_flow_back_from_nodes(vec![layer.to_node()], network_path, FlowType::PrimaryFlow).nth(1);
|
||||
let mut height_below_layer = 0;
|
||||
|
||||
let previous_upstream_layer = self
|
||||
.upstream_flow_back_from_nodes(vec![layer.to_node()], network_path, FlowType::PrimaryFlow)
|
||||
.skip(1)
|
||||
.find(|node_id| self.is_layer(node_id, network_path));
|
||||
|
||||
let vertical_offset_1 = if let Some(previous_upstream_layer) = previous_upstream_layer {
|
||||
let Some(node_metadata) = self.node_metadata(&previous_upstream_layer, network_path) else {
|
||||
log::error!("Could not get node_metadata in move_layer_to_stack");
|
||||
if let Some(previous_upstream_node) = previous_upstream_node {
|
||||
let Some(previous_upstream_node_position) = self.position(&previous_upstream_node, network_path) else {
|
||||
log::error!("Could not get previous upstream node position in move_layer_to_stack");
|
||||
return;
|
||||
};
|
||||
if let NodeTypePersistentMetadata::Layer(LayerPersistentMetadata { position }) = &node_metadata.persistent_metadata.node_type_metadata {
|
||||
match position {
|
||||
LayerPosition::Stack(y_offset) => *y_offset,
|
||||
_ => 0,
|
||||
}
|
||||
} else {
|
||||
let (Some(moved_layer_position), Some(previous_upstream_layer_position)) = (self.position(&layer.to_node(), network_path), self.position(&previous_upstream_layer, network_path))
|
||||
else {
|
||||
log::error!("Could not get moved layer position in move_layer_to_stack");
|
||||
return;
|
||||
};
|
||||
(previous_upstream_layer_position.y - moved_layer_position.y - 3).max(0) as u32
|
||||
}
|
||||
} else {
|
||||
0
|
||||
};
|
||||
height_below_layer = (previous_upstream_node_position.y - layer_to_move_position.y - 3).max(0) as u32;
|
||||
}
|
||||
let mut lowest_upstream_node_height = 0;
|
||||
for upstream_node in self
|
||||
.upstream_flow_back_from_nodes(vec![layer.to_node()], network_path, FlowType::LayerChildrenUpstreamFlow)
|
||||
.collect::<Vec<_>>()
|
||||
{
|
||||
let Some(upstream_node_position) = self.position(&upstream_node, network_path) else {
|
||||
log::error!("Could not get upstream node position in move_layer_to_stack");
|
||||
return;
|
||||
};
|
||||
lowest_upstream_node_height = lowest_upstream_node_height.max((upstream_node_position.y - layer_to_move_position.y).max(0) as u32);
|
||||
}
|
||||
|
||||
let Some(moved_layer_metadata) = self.node_metadata(&layer.to_node(), network_path) else {
|
||||
log::error!("Could not get node_metadata in move_layer_to_stack");
|
||||
return;
|
||||
};
|
||||
|
||||
let NodeTypePersistentMetadata::Layer(LayerPersistentMetadata { position }) = &moved_layer_metadata.persistent_metadata.node_type_metadata else {
|
||||
log::error!("Could not get layer metadata for layer in move_layer_to_stack");
|
||||
return;
|
||||
};
|
||||
|
||||
match &position {
|
||||
LayerPosition::Stack(offset) => {
|
||||
if let Some(previous_upstream_layer) = previous_upstream_layer {
|
||||
self.set_stack_position(&previous_upstream_layer, *offset, network_path);
|
||||
self.unload_upstream_node_click_targets(vec![previous_upstream_layer], network_path);
|
||||
}
|
||||
}
|
||||
LayerPosition::Absolute(stack_top_position) => {
|
||||
if let Some(previous_upstream_layer) = previous_upstream_layer {
|
||||
self.set_absolute_position(&previous_upstream_layer, *stack_top_position, network_path);
|
||||
self.unload_upstream_node_click_targets(vec![previous_upstream_layer], network_path);
|
||||
}
|
||||
}
|
||||
};
|
||||
// Height under the layer to move, which should be retained after the move
|
||||
let layer_to_move_height = height_below_layer.max(lowest_upstream_node_height);
|
||||
|
||||
// If the moved layer is a child of the new parent, then get its index after the disconnect
|
||||
if let Some(moved_layer_previous_index) = parent.children(&self.document_metadata).position(|child| child == layer) {
|
||||
|
@ -3570,43 +3616,42 @@ impl NodeNetworkInterface {
|
|||
self.remove_references_from_network(&layer.to_node(), true, network_path);
|
||||
self.disconnect_input(&InputConnector::node(layer.to_node(), 0), network_path);
|
||||
|
||||
// Moving layer to top of stack:
|
||||
// 1. Get the position of the top of the stack. If a new stack is created, then offset by (-8, 3) from the parent
|
||||
// 2. Move layer to the top of the stack
|
||||
// 3. Connect layer to the top of stack
|
||||
// 3. Set upstream layer to stack position with vertical offset 1
|
||||
|
||||
// Moving layer to stack:
|
||||
// 1. Insert the layer into the stack
|
||||
// 2. Get vertical offset 3 of the upstream layer
|
||||
// 3. Set the stack offset of the moved layer to vertical offset 3
|
||||
// 4. Set the stack offset of the upstream layer to vertical offset 1
|
||||
// TODO: Collapse space between parent and second child if top of stack is moved using the layout system
|
||||
|
||||
let post_node = ModifyInputsContext::get_post_node_with_index(self, parent, insert_index);
|
||||
|
||||
// // Get the previous input to the post node before inserting the layer
|
||||
// Get the previous input to the post node before inserting the layer
|
||||
let Some(post_node_input) = self.input_from_connector(&post_node, network_path).cloned() else {
|
||||
log::error!("Could not get previous input in move_layer_to_stack for parent {parent:?} and insert_index {insert_index}");
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(previous_layer_position) = self.position(&layer.to_node(), network_path) else {
|
||||
log::error!("Could not get previous layer position in move_layer_to_stack");
|
||||
return;
|
||||
};
|
||||
|
||||
let after_move_post_layer_position = if let Some(post_node_id) = post_node.node_id() {
|
||||
self.position(&post_node_id, network_path)
|
||||
} else {
|
||||
Some(IVec2::new(8, -3))
|
||||
};
|
||||
|
||||
let Some(after_move_post_layer_position) = after_move_post_layer_position else {
|
||||
log::error!("Could not get post node position in move_layer_to_stack");
|
||||
return;
|
||||
};
|
||||
|
||||
// Connect the layer to a parent layer/node at the top of the stack, or a non layer node midway down the stack
|
||||
if 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)) {
|
||||
match post_node_input {
|
||||
// Create a new stack
|
||||
NodeInput::Value { .. } | NodeInput::Scope(_) | NodeInput::Inline(_) => {
|
||||
self.create_wire(&OutputConnector::node(layer.to_node(), 0), &post_node, network_path);
|
||||
let post_node_position = if let Some(post_node_id) = post_node.node_id() {
|
||||
self.position(&post_node_id, network_path)
|
||||
} else {
|
||||
Some(IVec2::ZERO)
|
||||
};
|
||||
let Some(post_node_position) = post_node_position else {
|
||||
log::error!("Could not get post node position in move_layer_to_stack");
|
||||
return;
|
||||
};
|
||||
let offset = IVec2::new(-8, 3);
|
||||
self.set_absolute_position(&layer.to_node(), post_node_position + offset, network_path);
|
||||
|
||||
let final_layer_position = after_move_post_layer_position + IVec2::new(-8, 3);
|
||||
let shift = final_layer_position - previous_layer_position;
|
||||
self.shift_selected_nodes(vec![layer.to_node()], shift.x, shift.y, true, network_path);
|
||||
}
|
||||
// Move to the top of a stack
|
||||
NodeInput::Node { node_id, .. } => {
|
||||
|
@ -3614,10 +3659,12 @@ impl NodeNetworkInterface {
|
|||
log::error!("Could not get top of stack position in move_layer_to_stack");
|
||||
return;
|
||||
};
|
||||
self.set_absolute_position(&layer.to_node(), top_of_stack_position, network_path);
|
||||
let shift = top_of_stack_position - previous_layer_position;
|
||||
self.shift_selected_nodes(vec![layer.to_node()], shift.x, shift.y, true, network_path);
|
||||
self.unload_upstream_node_click_targets(vec![layer.to_node()], network_path);
|
||||
self.shift_selected_nodes(vec![node_id], 0, layer_to_move_height as i32 + 3, true, network_path);
|
||||
self.insert_node_between(&layer.to_node(), &post_node, 0, network_path);
|
||||
self.set_stack_position(&node_id, vertical_offset_1, network_path);
|
||||
self.set_stack_position_calculated_offset(&node_id, &layer.to_node(), network_path);
|
||||
}
|
||||
NodeInput::Network { .. } => {
|
||||
log::error!("Cannot move post node to parent which connects to the imports")
|
||||
|
@ -3627,8 +3674,11 @@ impl NodeNetworkInterface {
|
|||
match post_node_input {
|
||||
// Move to the bottom of the stack
|
||||
NodeInput::Value { .. } | NodeInput::Scope(_) | NodeInput::Inline(_) => {
|
||||
// TODO: Calculate height of bottom layer by getting height of upstream nodes instead of setting to 3
|
||||
let offset = after_move_post_layer_position - previous_layer_position + IVec2::new(0, 3);
|
||||
self.shift_selected_nodes(vec![layer.to_node()], offset.x, offset.y, true, network_path);
|
||||
self.create_wire(&OutputConnector::node(layer.to_node(), 0), &post_node, network_path);
|
||||
self.set_stack_position(&layer.to_node(), 0, network_path);
|
||||
self.set_stack_position_calculated_offset(&layer.to_node(), &post_node.node_id().unwrap(), network_path);
|
||||
}
|
||||
// Insert into the stack
|
||||
NodeInput::Node { node_id: upstream_node_id, .. } => {
|
||||
|
@ -3640,13 +3690,20 @@ impl NodeNetworkInterface {
|
|||
log::error!("Could not get upstream node metadata in move_layer_to_stack");
|
||||
return;
|
||||
};
|
||||
let LayerPosition::Stack(vertical_offset_3) = position.clone() else {
|
||||
// TODO: Max with height of upstream chain
|
||||
let LayerPosition::Stack(post_node_height) = position.clone() else {
|
||||
log::error!("Could not get vertical offset 3 in move_layer_to_stack");
|
||||
return;
|
||||
};
|
||||
let offset = after_move_post_layer_position - previous_layer_position + IVec2::new(0, 3 + post_node_height as i32);
|
||||
self.shift_selected_nodes(vec![layer.to_node()], offset.x, offset.y, true, network_path);
|
||||
let upstream_offset = layer_to_move_height as i32 + 3;
|
||||
self.shift_selected_nodes(vec![upstream_node_id], 0, upstream_offset, true, network_path);
|
||||
|
||||
self.insert_node_between(&layer.to_node(), &post_node, 0, network_path);
|
||||
self.set_stack_position(&layer.to_node(), vertical_offset_3, network_path);
|
||||
self.set_stack_position(&upstream_node_id, vertical_offset_1, network_path);
|
||||
|
||||
self.set_stack_position_calculated_offset(&layer.to_node(), &post_node.node_id().unwrap(), network_path);
|
||||
self.set_stack_position_calculated_offset(&upstream_node_id, &layer.to_node(), network_path);
|
||||
}
|
||||
NodeInput::Network { .. } => {
|
||||
log::error!("Cannot move post node to parent which connects to the imports")
|
||||
|
|
|
@ -53,6 +53,8 @@ pub struct LayerPanelEntry {
|
|||
pub selected: bool,
|
||||
#[serde(rename = "inSelectedNetwork")]
|
||||
pub in_selected_network: bool,
|
||||
#[serde(rename = "selectedParent")]
|
||||
pub selected_parent: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize, PartialEq, Eq, specta::Type)]
|
||||
|
|
|
@ -20,7 +20,7 @@ pub fn overlay(selected_bounds: Rect, hovered_bounds: Rect, transform: DAffine2,
|
|||
if turn_x != selected_x {
|
||||
let min_viewport = transform.transform_point2(DVec2::new(turn_x.min(selected_x), turn_y));
|
||||
let max_viewport = transform.transform_point2(DVec2::new(turn_x.max(selected_x), turn_y));
|
||||
overlay_context.line(min_viewport, max_viewport);
|
||||
overlay_context.line(min_viewport, max_viewport, None);
|
||||
let length = format!("{:.2}", transform_to_document.transform_vector2(DVec2::X * (turn_x - selected_x)).length());
|
||||
let direction = -(min_viewport - max_viewport).normalize_or_zero();
|
||||
overlay_context.angle_text(&length, (min_viewport + max_viewport) / 2., direction, 5., utility_types::Pivot::TopCentreX);
|
||||
|
@ -28,7 +28,7 @@ pub fn overlay(selected_bounds: Rect, hovered_bounds: Rect, transform: DAffine2,
|
|||
if turn_y != hovered_y {
|
||||
let min_viewport = transform.transform_point2(DVec2::new(turn_x, turn_y.min(hovered_y)));
|
||||
let max_viewport = transform.transform_point2(DVec2::new(turn_x, turn_y.max(hovered_y)));
|
||||
overlay_context.line(min_viewport, max_viewport);
|
||||
overlay_context.line(min_viewport, max_viewport, None);
|
||||
let length = format!("{:.2}", transform_to_document.transform_vector2(DVec2::Y * (turn_y - hovered_y)).length());
|
||||
let direction = (min_viewport - max_viewport).normalize_or_zero().perp();
|
||||
overlay_context.angle_text(&length, (min_viewport + max_viewport) / 2., direction, 5., utility_types::Pivot::LeftCentreY);
|
||||
|
|
|
@ -410,11 +410,12 @@ impl SnapManager {
|
|||
let start = DVec2::new(first.max().x, y);
|
||||
let end = DVec2::new(second.min().x, y);
|
||||
let signed_size = if bottom { y_size } else { -y_size };
|
||||
overlay_context.line(transform.transform_point2(start), transform.transform_point2(start + DVec2::Y * signed_size));
|
||||
overlay_context.line(transform.transform_point2(end), transform.transform_point2(end + DVec2::Y * signed_size));
|
||||
overlay_context.line(transform.transform_point2(start), transform.transform_point2(start + DVec2::Y * signed_size), None);
|
||||
overlay_context.line(transform.transform_point2(end), transform.transform_point2(end + DVec2::Y * signed_size), None);
|
||||
overlay_context.line(
|
||||
transform.transform_point2(start + DVec2::Y * signed_size / 2.),
|
||||
transform.transform_point2(end + DVec2::Y * signed_size / 2.),
|
||||
None,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -427,11 +428,12 @@ impl SnapManager {
|
|||
let start = DVec2::new(x, first.max().y);
|
||||
let end = DVec2::new(x, second.min().y);
|
||||
let signed_size = if right { x_size } else { -x_size };
|
||||
overlay_context.line(transform.transform_point2(start), transform.transform_point2(start + DVec2::X * signed_size));
|
||||
overlay_context.line(transform.transform_point2(end), transform.transform_point2(end + DVec2::X * signed_size));
|
||||
overlay_context.line(transform.transform_point2(start), transform.transform_point2(start + DVec2::X * signed_size), None);
|
||||
overlay_context.line(transform.transform_point2(end), transform.transform_point2(end + DVec2::X * signed_size), None);
|
||||
overlay_context.line(
|
||||
transform.transform_point2(start + DVec2::X * signed_size / 2.),
|
||||
transform.transform_point2(end + DVec2::X * signed_size / 2.),
|
||||
None,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -444,7 +446,7 @@ impl SnapManager {
|
|||
overlay_context.outline([Subpath::from_bezier(curve)].iter(), to_viewport);
|
||||
}
|
||||
if let Some(quad) = ind.target_bounds {
|
||||
overlay_context.quad(to_viewport * quad);
|
||||
overlay_context.quad(to_viewport * quad, None);
|
||||
}
|
||||
let viewport = to_viewport.transform_point2(ind.snapped_point_document);
|
||||
|
||||
|
@ -454,7 +456,7 @@ impl SnapManager {
|
|||
let align = [ind.alignment_target_x, ind.alignment_target_y].map(|target| target.map(|target| to_viewport.transform_point2(target)));
|
||||
let any_align = align.iter().flatten().next().is_some();
|
||||
for &target in align.iter().flatten() {
|
||||
overlay_context.line(viewport, target);
|
||||
overlay_context.line(viewport, target, None);
|
||||
}
|
||||
for &target in align.iter().flatten() {
|
||||
overlay_context.manipulator_handle(target, false);
|
||||
|
|
|
@ -288,7 +288,7 @@ impl BoundingBoxManager {
|
|||
|
||||
/// Update the position of the bounding box and transform handles
|
||||
pub fn render_overlays(&mut self, overlay_context: &mut OverlayContext) {
|
||||
overlay_context.quad(self.transform * Quad::from_box(self.bounds));
|
||||
overlay_context.quad(self.transform * Quad::from_box(self.bounds), None);
|
||||
|
||||
for position in self.evaluate_transform_handle_positions() {
|
||||
overlay_context.square(position, Some(6.), None, None);
|
||||
|
|
|
@ -259,7 +259,7 @@ impl Fsm for GradientToolFsmState {
|
|||
let Gradient { start, end, stops, .. } = gradient;
|
||||
let (start, end) = (transform.transform_point2(start), transform.transform_point2(end));
|
||||
|
||||
overlay_context.line(start, end);
|
||||
overlay_context.line(start, end, None);
|
||||
overlay_context.manipulator_handle(start, dragging == Some(GradientDragTarget::Start));
|
||||
overlay_context.manipulator_handle(end, dragging == Some(GradientDragTarget::End));
|
||||
|
||||
|
|
|
@ -461,7 +461,12 @@ impl Fsm for PathToolFsmState {
|
|||
|
||||
match self {
|
||||
Self::DrawingBox => {
|
||||
overlay_context.quad(Quad::from_box([tool_data.drag_start_pos, tool_data.previous_mouse_position]));
|
||||
let fill_color = graphene_std::Color::from_rgb_str(crate::consts::COLOR_OVERLAY_BLUE.strip_prefix('#').unwrap())
|
||||
.unwrap()
|
||||
.with_alpha(0.05)
|
||||
.rgba_hex();
|
||||
|
||||
overlay_context.quad(Quad::from_box([tool_data.drag_start_pos, tool_data.previous_mouse_position]), Some(&("#".to_string() + &fill_color)));
|
||||
}
|
||||
Self::Dragging => {
|
||||
tool_data.snap_manager.draw_overlays(SnapData::new(document, input), &mut overlay_context);
|
||||
|
|
|
@ -469,15 +469,15 @@ impl Fsm for PenToolFsmState {
|
|||
let valid = |point: DVec2, handle: DVec2| point.distance_squared(handle) >= HIDE_HANDLE_DISTANCE * HIDE_HANDLE_DISTANCE;
|
||||
let next_point = transform.transform_point2(tool_data.next_point);
|
||||
let next_handle_start = transform.transform_point2(tool_data.next_handle_start);
|
||||
overlay_context.line(next_point, next_handle_start);
|
||||
overlay_context.line(next_point, next_handle_start, None);
|
||||
let start = tool_data.latest_point().map(|point| transform.transform_point2(point.pos));
|
||||
|
||||
let handle_start = tool_data.latest_point().map(|point| transform.transform_point2(point.handle_start));
|
||||
let handle_end = tool_data.handle_end.map(|point| transform.transform_point2(point));
|
||||
|
||||
if let (Some(start), Some(handle_start), Some(handle_end)) = (start, handle_start, handle_end) {
|
||||
overlay_context.line(start, handle_start);
|
||||
overlay_context.line(next_point, handle_end);
|
||||
overlay_context.line(start, handle_start, None);
|
||||
overlay_context.line(next_point, handle_end, None);
|
||||
|
||||
path_overlays(document, shape_editor, &mut overlay_context);
|
||||
|
||||
|
|
|
@ -471,7 +471,11 @@ impl Fsm for SelectToolFsmState {
|
|||
}
|
||||
|
||||
// Update the selection box
|
||||
overlay_context.quad(quad);
|
||||
let fill_color = graphene_std::Color::from_rgb_str(crate::consts::COLOR_OVERLAY_BLUE.strip_prefix('#').unwrap())
|
||||
.unwrap()
|
||||
.with_alpha(0.05)
|
||||
.rgba_hex();
|
||||
overlay_context.quad(quad, Some(&("#".to_string() + &fill_color)));
|
||||
}
|
||||
// Only highlight layers if the viewport is not being panned (middle mouse button is pressed)
|
||||
// TODO: Don't use `Key::Mmb` directly, instead take it as a variable from the input mappings list like in all other places
|
||||
|
|
|
@ -361,7 +361,7 @@ impl Fsm for TextToolFsmState {
|
|||
if far.x != 0. && far.y != 0. {
|
||||
let quad = Quad::from_box([DVec2::ZERO, far]);
|
||||
let transformed_quad = document.metadata().transform_to_viewport(tool_data.layer) * quad;
|
||||
overlay_context.quad(transformed_quad);
|
||||
overlay_context.quad(transformed_quad, None);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -376,7 +376,7 @@ impl Fsm for TextToolFsmState {
|
|||
let far = graphene_core::text::bounding_box(text, buzz_face, font_size, None);
|
||||
let quad = Quad::from_box([DVec2::ZERO, far]);
|
||||
let multiplied = document.metadata().transform_to_viewport(layer) * quad;
|
||||
overlay_context.quad(multiplied);
|
||||
overlay_context.quad(multiplied, None);
|
||||
}
|
||||
|
||||
self
|
||||
|
|
|
@ -105,6 +105,16 @@
|
|||
--color-error-red: #d6536e;
|
||||
--color-error-red-rgb: 214, 83, 110;
|
||||
|
||||
// Keep changes to these colors updated with `editor/src/consts.rs`
|
||||
--color-overlay-blue: #00a8ff;
|
||||
--color-overlay-blue-rgb: 0, 168, 255;
|
||||
--color-overlay-yellow: #ffc848;
|
||||
--color-overlay-yellow-rgb: 255, 200, 72;
|
||||
--color-overlay-white: #ffffff;
|
||||
--color-overlay-white-rgb: 255, 255, 255;
|
||||
--color-overlay-gray: #cccccc;
|
||||
--color-overlay-gray-rgb: 204, 204, 204;
|
||||
|
||||
--color-data-general: #c5c5c5;
|
||||
--color-data-general-dim: #767676;
|
||||
--color-data-raster: #e4bb72;
|
||||
|
|
|
@ -375,7 +375,7 @@
|
|||
class="layer"
|
||||
classes={{
|
||||
selected: fakeHighlight !== undefined ? fakeHighlight === listing.entry.id : listing.entry.selected,
|
||||
"in-selected-network": listing.entry.inSelectedNetwork,
|
||||
"full-highlight": listing.entry.inSelectedNetwork && !listing.entry.selectedParent,
|
||||
"insert-folder": (draggingData?.highlightFolder || false) && draggingData?.insertParentId === listing.entry.id,
|
||||
}}
|
||||
styles={{ "--layer-indent-levels": `${listing.entry.depth - 1}` }}
|
||||
|
@ -499,9 +499,11 @@
|
|||
|
||||
// Dimming
|
||||
&.selected {
|
||||
background: #404040;
|
||||
// Halfway between 3-darkgray and 4-dimgray (this interpolation approach only works on grayscale values)
|
||||
--component: calc((Max(var(--color-3-darkgray-rgb)) + Max(var(--color-4-dimgray-rgb))) / 2);
|
||||
background: rgb(var(--component), var(--component), var(--component));
|
||||
|
||||
&.in-selected-network {
|
||||
&.full-highlight {
|
||||
background: var(--color-4-dimgray);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1027,7 +1027,7 @@
|
|||
// ancestor elements, `.graph` and `.panel`, each have the simultaneous pairing of `overflow: hidden` and `border-radius`.
|
||||
// See: https://stackoverflow.com/questions/75137879/bug-with-backdrop-filter-in-firefox
|
||||
// backdrop-filter: blur(4px);
|
||||
background: rgba(0, 0, 0, 0.33);
|
||||
background: rgba(var(--color-0-black-rgb), 0.33);
|
||||
|
||||
.node-error {
|
||||
position: absolute;
|
||||
|
@ -1161,11 +1161,10 @@
|
|||
}
|
||||
|
||||
&.selected {
|
||||
// This is the result of blending `rgba(255, 255, 255, 0.1)` over `rgba(0, 0, 0, 0.33)`
|
||||
background: rgba(66, 66, 66, 0.4);
|
||||
background: rgba(var(--color-5-dullgray-rgb), 0.5);
|
||||
|
||||
&.in-selected-network {
|
||||
background: rgba(80, 80, 80, 0.5);
|
||||
background: rgba(var(--color-6-lowergray-rgb), 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1259,18 +1258,18 @@
|
|||
|
||||
&.selected {
|
||||
.primary {
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
background: rgba(var(--color-f-white-rgb), 0.15);
|
||||
|
||||
&.in-selected-network {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
background: rgba(var(--color-f-white-rgb), 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
.parameters {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
background: rgba(var(--color-f-white-rgb), 0.1);
|
||||
|
||||
&.in-selected-network {
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
background: rgba(var(--color-f-white-rgb), 0.15);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1296,7 +1295,7 @@
|
|||
width: 100%;
|
||||
height: 24px;
|
||||
border-radius: 2px 2px 0 0;
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
background: rgba(var(--color-f-white-rgb), 0.05);
|
||||
|
||||
&.no-parameter-section {
|
||||
border-radius: 2px;
|
||||
|
@ -1359,9 +1358,9 @@
|
|||
|
||||
.box-selection {
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
background-color: rgba(77, 168, 221, 0.2);
|
||||
border: 1px solid rgba(77, 168, 221);
|
||||
pointer-events: none;
|
||||
background: rgba(var(--color-overlay-blue-rgb), 0.05);
|
||||
border: 1px solid var(--color-overlay-blue);
|
||||
z-index: 2;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -888,6 +888,8 @@ export class LayerPanelEntry {
|
|||
selected!: boolean;
|
||||
|
||||
inSelectedNetwork!: boolean;
|
||||
|
||||
selectedParent!: boolean;
|
||||
}
|
||||
|
||||
export class DisplayDialogDismiss extends JsMessage {}
|
||||
|
|
|
@ -125,7 +125,7 @@ impl<'a, I: StaticType + 'static + Send + Sync + std::panic::UnwindSafe> Executo
|
|||
Ok(result) => result.map_err(|e| e.into()),
|
||||
Err(e) => {
|
||||
Box::leak(e);
|
||||
Err("Node graph execution paniced".into())
|
||||
Err("Node graph execution panicked".into())
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue