diff --git a/editor/src/messages/input_mapper/default_mapping.rs b/editor/src/messages/input_mapper/default_mapping.rs index 4920f8d72..eec0b96e9 100644 --- a/editor/src/messages/input_mapper/default_mapping.rs +++ b/editor/src/messages/input_mapper/default_mapping.rs @@ -58,6 +58,7 @@ pub fn default_mapping() -> Mapping { entry!(KeyDown(KeyC); modifiers=[Accel], action_dispatch=NodeGraphMessage::Copy), entry!(KeyDown(KeyD); modifiers=[Accel], action_dispatch=NodeGraphMessage::DuplicateSelectedNodes), entry!(KeyDown(KeyH); modifiers=[Accel], action_dispatch=NodeGraphMessage::ToggleSelectedVisibility), + entry!(KeyDown(KeyL); modifiers=[Accel], action_dispatch=NodeGraphMessage::ToggleSelectedLocked), // // TransformLayerMessage entry!(KeyDown(Enter); action_dispatch=TransformLayerMessage::ApplyTransformOperation), diff --git a/editor/src/messages/portfolio/document/document_message_handler.rs b/editor/src/messages/portfolio/document/document_message_handler.rs index e708c31bf..16cda6c78 100644 --- a/editor/src/messages/portfolio/document/document_message_handler.rs +++ b/editor/src/messages/portfolio/document/document_message_handler.rs @@ -190,7 +190,7 @@ impl MessageHandler> for DocumentMessag AlignAggregate::Max => combined_box[1], AlignAggregate::Center => (combined_box[0] + combined_box[1]) / 2., }; - for layer in self.selected_nodes.selected_layers(self.metadata()) { + for layer in self.selected_nodes.selected_unlocked_layers(self.metadata()) { let Some(bbox) = self.metadata().bounding_box_viewport(layer) else { continue; }; @@ -313,10 +313,10 @@ impl MessageHandler> for DocumentMessag FlipAxis::X => DVec2::new(-1., 1.), FlipAxis::Y => DVec2::new(1., -1.), }; - if let Some([min, max]) = self.selected_visible_layers_bounding_box_viewport() { + if let Some([min, max]) = self.selected_visible_and_unlock_layers_bounding_box_viewport() { let center = (max + min) / 2.; let bbox_trans = DAffine2::from_translation(-center); - for layer in self.selected_nodes.selected_layers(self.metadata()) { + for layer in self.selected_nodes.selected_unlocked_layers(self.metadata()) { responses.add(GraphOperationMessage::TransformChange { layer, transform: DAffine2::from_scale(scale), @@ -463,7 +463,7 @@ impl MessageHandler> for DocumentMessag for layer in self .selected_nodes .selected_layers(self.metadata()) - .filter(|&layer| self.selected_nodes.layer_visible(layer, self.metadata())) + .filter(|&layer| self.selected_nodes.layer_visible(layer, self.metadata()) && !self.selected_nodes.layer_locked(layer, self.metadata())) { responses.add(GraphOperationMessage::TransformChange { layer, @@ -498,7 +498,7 @@ impl MessageHandler> for DocumentMessag for layer in self .selected_nodes .selected_layers(self.metadata()) - .filter(|&layer| self.selected_nodes.layer_visible(layer, self.metadata())) + .filter(|&layer| self.selected_nodes.layer_visible(layer, self.metadata()) && !self.selected_nodes.layer_locked(layer, self.metadata())) { let to = self.metadata().document_to_viewport.inverse() * self.metadata().downstream_transform_to_viewport(layer); let original_transform = self.metadata().upstream_transform(layer.to_node()); @@ -622,11 +622,11 @@ impl MessageHandler> for DocumentMessag } DocumentMessage::SelectAllLayers => { let metadata = self.metadata(); - let all_layers_except_artboards_and_invisible = metadata + let all_layers_except_artboards_invisible_and_locked = metadata .all_layers() .filter(move |&layer| !metadata.is_artboard(layer)) - .filter(|&layer| self.selected_nodes.layer_visible(layer, metadata)); - let nodes = all_layers_except_artboards_and_invisible.map(|layer| layer.to_node()).collect(); + .filter(|&layer| self.selected_nodes.layer_visible(layer, metadata) && !self.selected_nodes.layer_locked(layer, metadata)); + let nodes = all_layers_except_artboards_invisible_and_locked.map(|layer| layer.to_node()).collect(); responses.add(NodeGraphMessage::SelectedNodesSet { nodes }); } DocumentMessage::SelectedLayersLower => { @@ -834,6 +834,7 @@ impl DocumentMessageHandler { .root() .descendants(&self.metadata) .filter(|&layer| self.selected_nodes.layer_visible(layer, self.metadata())) + .filter(|&layer| !self.selected_nodes.layer_locked(layer, self.metadata())) .filter(|&layer| !is_artboard(layer, network)) .filter_map(|layer| self.metadata.click_target(layer).map(|targets| (layer, targets))) .filter(move |(layer, target)| target.iter().any(move |target| target.intersect_rectangle(document_quad, self.metadata.transform_to_document(*layer)))) @@ -847,6 +848,7 @@ impl DocumentMessageHandler { .root() .descendants(&self.metadata) .filter(|&layer| self.selected_nodes.layer_visible(layer, self.metadata())) + .filter(|&layer| !self.selected_nodes.layer_locked(layer, self.metadata())) .filter_map(|layer| self.metadata.click_target(layer).map(|targets| (layer, targets))) .filter(move |(layer, target)| target.iter().any(|target: &ClickTarget| target.intersect_point(point, self.metadata.transform_to_document(*layer)))) .map(|(layer, _)| layer) @@ -865,6 +867,13 @@ impl DocumentMessageHandler { .reduce(graphene_core::renderer::Quad::combine_bounds) } + pub fn selected_visible_and_unlock_layers_bounding_box_viewport(&self) -> Option<[DVec2; 2]> { + self.selected_nodes + .selected_visible_and_unlocked_layers(self.metadata()) + .filter_map(|layer| self.metadata.bounding_box_viewport(layer)) + .reduce(graphene_core::renderer::Quad::combine_bounds) + } + pub fn network(&self) -> &NodeNetwork { &self.network } @@ -1361,7 +1370,7 @@ impl DocumentMessageHandler { let has_selection = self.selected_nodes.selected_layers(self.metadata()).next().is_some(); let selection_all_visible = self.selected_nodes.selected_layers(self.metadata()).all(|layer| self.metadata().node_is_visible(layer.to_node())); - let selection_all_locked = false; // TODO: Implement + let selection_all_locked = self.selected_nodes.selected_layers(self.metadata()).all(|layer| self.metadata().node_is_locked(layer.to_node())); let layers_panel_options_bar = WidgetLayout::new(vec![LayoutGroup::Row { widgets: vec![ @@ -1415,8 +1424,8 @@ impl DocumentMessageHandler { IconButton::new(if selection_all_locked { "PadlockLocked" } else { "PadlockUnlocked" }, 24) .hover_icon(Some((if selection_all_locked { "PadlockUnlocked" } else { "PadlockLocked" }).into())) .tooltip(if selection_all_locked { "Unlock Selected" } else { "Lock Selected" }) - .tooltip_shortcut(action_keys!(DialogMessageDiscriminant::RequestComingSoonDialog)) - .on_update(|_| DialogMessage::RequestComingSoonDialog { issue: Some(1127) }.into()) + .tooltip_shortcut(action_keys!(NodeGraphMessageDiscriminant::ToggleSelectedLocked)) + .on_update(|_| NodeGraphMessage::ToggleSelectedLocked.into()) .disabled(!has_selection) .widget_holder(), IconButton::new(if selection_all_visible { "EyeVisible" } else { "EyeHidden" }, 24) diff --git a/editor/src/messages/portfolio/document/node_graph/node_graph_message.rs b/editor/src/messages/portfolio/document/node_graph/node_graph_message.rs index 7769be001..0bcf41694 100644 --- a/editor/src/messages/portfolio/document/node_graph/node_graph_message.rs +++ b/editor/src/messages/portfolio/document/node_graph/node_graph_message.rs @@ -97,6 +97,14 @@ pub enum NodeGraphMessage { node_id: NodeId, visible: bool, }, + ToggleSelectedLocked, + ToggleLocked { + node_id: NodeId, + }, + SetLocked { + node_id: NodeId, + locked: bool, + }, SetName { node_id: NodeId, name: String, diff --git a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs index b0be96da1..f8e2c7fb4 100644 --- a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs +++ b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs @@ -487,6 +487,40 @@ impl<'a> MessageHandler> for NodeGrap responses.add(NodeGraphMessage::RunDocumentGraph); } })(); + document_metadata.load_structure(document_network, selected_nodes); + self.update_selection_action_buttons(document_network, document_metadata, selected_nodes, responses); + } + NodeGraphMessage::ToggleSelectedLocked => { + responses.add(DocumentMessage::StartTransaction); + + let is_locked = !selected_nodes.selected_nodes().any(|&id| document_metadata.node_is_locked(id)); + + for &node_id in selected_nodes.selected_nodes() { + responses.add(NodeGraphMessage::SetLocked { node_id, locked: is_locked }); + } + } + NodeGraphMessage::ToggleLocked { node_id } => { + responses.add(DocumentMessage::StartTransaction); + let is_locked = !document_metadata.node_is_locked(node_id); + responses.add(NodeGraphMessage::SetLocked { node_id, locked: is_locked }); + } + NodeGraphMessage::SetLocked { node_id, locked } => { + if let Some(network) = document_network.nested_network_mut(&self.network) { + let is_locked = if !locked { + false + } else if !network.imports.contains(&node_id) && !network.original_outputs().iter().any(|output| output.node_id == node_id) { + true + } else { + return; + }; + let Some(node) = network.nodes.get_mut(&node_id) else { return }; + node.locked = is_locked; + + if network.connected_to_output(node_id) { + responses.add(NodeGraphMessage::RunDocumentGraph); + } + } + document_metadata.load_structure(document_network, selected_nodes); self.update_selection_action_buttons(document_network, document_metadata, selected_nodes, responses); } NodeGraphMessage::SetName { node_id, name } => { @@ -551,9 +585,9 @@ impl<'a> MessageHandler> for NodeGrap impl NodeGraphMessageHandler { pub fn actions_with_node_graph_open(&self, graph_open: bool) -> ActionList { if self.has_selection && graph_open { - actions!(NodeGraphMessageDiscriminant; ToggleSelectedVisibility, DuplicateSelectedNodes, DeleteSelectedNodes, Cut, Copy) + actions!(NodeGraphMessageDiscriminant; ToggleSelectedVisibility, ToggleSelectedLocked, DuplicateSelectedNodes, DeleteSelectedNodes, Cut, Copy) } else if self.has_selection { - actions!(NodeGraphMessageDiscriminant; ToggleSelectedVisibility) + actions!(NodeGraphMessageDiscriminant; ToggleSelectedVisibility, ToggleSelectedLocked) } else { actions!(NodeGraphMessageDiscriminant;) } @@ -567,7 +601,7 @@ impl NodeGraphMessageHandler { }); } - /// Updates the buttons for visibility and preview + /// Updates the buttons for visibility, locked, and preview fn update_selection_action_buttons(&mut self, document_network: &NodeNetwork, document_metadata: &DocumentMetadata, selected_nodes: &SelectedNodes, responses: &mut VecDeque) { if let Some(network) = document_network.nested_network(&self.network) { let mut widgets = Vec::new(); @@ -759,6 +793,7 @@ impl NodeGraphMessageHandler { position: node.metadata.position.into(), previewed: network.outputs_contain(node_id), visible: node.visible, + locked: node.locked, errors: errors.map(|e| format!("{e:?}")), }); } @@ -784,6 +819,11 @@ impl NodeGraphMessageHandler { .filter(|&ancestor| ancestor != layer) .all(|layer| network.nodes.get(&layer.to_node()).map(|node| node.visible).unwrap_or_default()); + let parents_unlocked = layer + .ancestors(metadata) + .filter(|&ancestor| ancestor != layer) + .all(|layer| network.nodes.get(&layer.to_node()).map(|node| !node.locked).unwrap_or_default()); + let data = LayerPanelEntry { id: node_id, layer_classification, @@ -795,8 +835,8 @@ impl NodeGraphMessageHandler { tooltip: if cfg!(debug_assertions) { format!("Layer ID: {node_id}") } else { "".into() }, visible: node.visible, parents_visible, - unlocked: true, - parents_unlocked: true, + unlocked: !node.locked, + parents_unlocked, }; responses.add(FrontendMessage::UpdateDocumentLayerDetails { data }); } diff --git a/editor/src/messages/portfolio/document/node_graph/utility_types.rs b/editor/src/messages/portfolio/document/node_graph/utility_types.rs index 6bf6ff538..0f71ce0d5 100644 --- a/editor/src/messages/portfolio/document/node_graph/utility_types.rs +++ b/editor/src/messages/portfolio/document/node_graph/utility_types.rs @@ -85,6 +85,7 @@ pub struct FrontendNode { pub exposed_outputs: Vec, pub position: (i32, i32), pub visible: bool, + pub locked: bool, pub previewed: bool, pub errors: Option, } diff --git a/editor/src/messages/portfolio/document/utility_types/clipboards.rs b/editor/src/messages/portfolio/document/utility_types/clipboards.rs index a9db42114..31aa61485 100644 --- a/editor/src/messages/portfolio/document/utility_types/clipboards.rs +++ b/editor/src/messages/portfolio/document/utility_types/clipboards.rs @@ -20,6 +20,7 @@ pub struct CopyBufferEntry { pub nodes: HashMap, pub selected: bool, pub visible: bool, + pub locked: bool, pub collapsed: bool, pub alias: String, } diff --git a/editor/src/messages/portfolio/document/utility_types/document_metadata.rs b/editor/src/messages/portfolio/document/utility_types/document_metadata.rs index 944675290..1132bfae7 100644 --- a/editor/src/messages/portfolio/document/utility_types/document_metadata.rs +++ b/editor/src/messages/portfolio/document/utility_types/document_metadata.rs @@ -23,6 +23,7 @@ pub struct DocumentMetadata { artboards: HashSet, folders: HashSet, hidden: HashSet, + locked: HashSet, click_targets: HashMap>, /// Transform from document space to viewport space. pub document_to_viewport: DAffine2, @@ -36,6 +37,7 @@ impl Default for DocumentMetadata { artboards: HashSet::new(), folders: HashSet::new(), hidden: HashSet::new(), + locked: HashSet::new(), click_targets: HashMap::new(), document_to_viewport: DAffine2::IDENTITY, } @@ -126,6 +128,10 @@ impl DocumentMetadata { !self.hidden.contains(&layer) } + pub fn node_is_locked(&self, layer: NodeId) -> bool { + self.locked.contains(&layer) + } + /// Folders sorted from most nested to least nested pub fn folders_sorted_by_most_nested(&self, layers: impl Iterator) -> Vec { let mut folders: Vec<_> = layers.filter(|layer| self.folders.contains(layer)).collect(); @@ -149,6 +155,7 @@ impl DocumentMetadata { self.artboards = HashSet::new(); self.folders = HashSet::new(); self.hidden = HashSet::new(); + self.locked = HashSet::new(); let id = graph.exports[0].node_id; let Some(output_node) = graph.nodes.get(&id) else { @@ -180,6 +187,10 @@ impl DocumentMetadata { if !current_node.visible { self.hidden.insert(current_node_id); } + + if current_node.locked { + self.locked.insert(current_node_id); + } } // Get the sibling below diff --git a/editor/src/messages/portfolio/document/utility_types/nodes.rs b/editor/src/messages/portfolio/document/utility_types/nodes.rs index c792d57f2..5dae6cda8 100644 --- a/editor/src/messages/portfolio/document/utility_types/nodes.rs +++ b/editor/src/messages/portfolio/document/utility_types/nodes.rs @@ -71,6 +71,19 @@ impl SelectedNodes { self.selected_layers(metadata).filter(move |&layer| self.layer_visible(layer, metadata)) } + pub fn layer_locked(&self, layer: LayerNodeIdentifier, metadata: &DocumentMetadata) -> bool { + layer.ancestors(metadata).any(|layer| metadata.node_is_locked(layer.to_node())) + } + + pub fn selected_unlocked_layers<'a>(&'a self, metadata: &'a DocumentMetadata) -> impl Iterator + '_ { + self.selected_layers(metadata).filter(move |&layer| !self.layer_locked(layer, metadata)) + } + + pub fn selected_visible_and_unlocked_layers<'a>(&'a self, metadata: &'a DocumentMetadata) -> impl Iterator + '_ { + self.selected_layers(metadata) + .filter(move |&layer| self.layer_visible(layer, metadata) && !self.layer_locked(layer, metadata)) + } + pub fn selected_layers<'a>(&'a self, metadata: &'a DocumentMetadata) -> impl Iterator + '_ { metadata.all_layers().filter(|layer| self.0.contains(&layer.to_node())) } diff --git a/editor/src/messages/portfolio/portfolio_message_handler.rs b/editor/src/messages/portfolio/portfolio_message_handler.rs index d6042afd7..ed2753a80 100644 --- a/editor/src/messages/portfolio/portfolio_message_handler.rs +++ b/editor/src/messages/portfolio/portfolio_message_handler.rs @@ -203,6 +203,7 @@ impl MessageHandler> for PortfolioMes .collect(), selected: active_document.selected_nodes.selected_layers_contains(layer, active_document.metadata()), visible: active_document.selected_nodes.layer_visible(layer, active_document.metadata()), + locked: active_document.selected_nodes.layer_locked(layer, active_document.metadata()), collapsed: false, alias: previous_alias, }); @@ -391,6 +392,9 @@ impl MessageHandler> for PortfolioMes if !entry.visible { responses.add(NodeGraphMessage::SetVisibility { node_id: id, visible: false }); } + if entry.locked { + responses.add(NodeGraphMessage::SetLocked { node_id: id, locked: true }); + } } }; diff --git a/editor/src/messages/tool/common_functionality/pivot.rs b/editor/src/messages/tool/common_functionality/pivot.rs index c762ca50b..c9e4a1638 100644 --- a/editor/src/messages/tool/common_functionality/pivot.rs +++ b/editor/src/messages/tool/common_functionality/pivot.rs @@ -45,7 +45,7 @@ impl Pivot { /// Recomputes the pivot position and transform. fn recalculate_pivot(&mut self, document: &DocumentMessageHandler) { - let mut layers = document.selected_nodes.selected_visible_layers(document.metadata()); + let mut layers = document.selected_nodes.selected_visible_and_unlocked_layers(document.metadata()); let Some(first) = layers.next() else { // If no layers are selected then we revert things back to default self.normalized_pivot = DVec2::splat(0.5); @@ -66,14 +66,14 @@ impl Pivot { // If more than one layer is selected we use the AABB with the mean of the pivots let xy_summation = document .selected_nodes - .selected_visible_layers(document.metadata()) + .selected_visible_and_unlocked_layers(document.metadata()) .map(|layer| graph_modification_utils::get_viewport_pivot(layer, &document.network, &document.metadata)) .reduce(|a, b| a + b) .unwrap_or_default(); let pivot = xy_summation / selected_layers_count as f64; self.pivot = Some(pivot); - let [min, max] = document.selected_visible_layers_bounding_box_viewport().unwrap_or([DVec2::ZERO, DVec2::ONE]); + let [min, max] = document.selected_visible_and_unlock_layers_bounding_box_viewport().unwrap_or([DVec2::ZERO, DVec2::ONE]); self.normalized_pivot = (pivot - min) / (max - min); self.transform_from_normalized = DAffine2::from_translation(min) * DAffine2::from_scale(max - min); @@ -101,7 +101,7 @@ impl Pivot { /// Sets the viewport position of the pivot for all selected layers. pub fn set_viewport_position(&self, position: DVec2, document: &DocumentMessageHandler, responses: &mut VecDeque) { - for layer in document.selected_nodes.selected_visible_layers(document.metadata()) { + for layer in document.selected_nodes.selected_visible_and_unlocked_layers(document.metadata()) { let transform = Self::get_layer_pivot_transform(layer, document); let pivot = transform.inverse().transform_point2(position); // Only update the pivot when computed position is finite. Infinite can happen when scale is 0. diff --git a/editor/src/messages/tool/common_functionality/transformation_cage.rs b/editor/src/messages/tool/common_functionality/transformation_cage.rs index b47c38e3a..81909ddd5 100644 --- a/editor/src/messages/tool/common_functionality/transformation_cage.rs +++ b/editor/src/messages/tool/common_functionality/transformation_cage.rs @@ -206,7 +206,11 @@ pub fn axis_align_drag(axis_align: bool, position: DVec2, start: DVec2) -> DVec2 let snap_resolution = SELECTION_DRAG_ANGLE.to_radians(); let angle = -mouse_position.angle_between(DVec2::X); let snapped_angle = (angle / snap_resolution).round() * snap_resolution; - DVec2::new(snapped_angle.cos(), snapped_angle.sin()) * mouse_position.length() + start + if snapped_angle.is_finite() { + start + DVec2::new(snapped_angle.cos(), snapped_angle.sin()) * mouse_position.length() + } else { + start + } } else { position } diff --git a/editor/src/messages/tool/tool_messages/select_tool.rs b/editor/src/messages/tool/tool_messages/select_tool.rs index 5894dd04a..b1718e4db 100644 --- a/editor/src/messages/tool/tool_messages/select_tool.rs +++ b/editor/src/messages/tool/tool_messages/select_tool.rs @@ -386,12 +386,12 @@ impl Fsm for SelectToolFsmState { (_, SelectToolMessage::Overlays(mut overlay_context)) => { tool_data.snap_manager.draw_overlays(SnapData::new(document, input), &mut overlay_context); - let selected_layers_count = document.selected_nodes.selected_layers(document.metadata()).count(); + let selected_layers_count = document.selected_nodes.selected_unlocked_layers(document.metadata()).count(); tool_data.selected_layers_changed = selected_layers_count != tool_data.selected_layers_count; tool_data.selected_layers_count = selected_layers_count; // Outline selected layers - for layer in document.selected_nodes.selected_visible_layers(document.metadata()) { + for layer in document.selected_nodes.selected_visible_and_unlocked_layers(document.metadata()) { overlay_context.outline(document.metadata().layer_outline(layer), document.metadata().transform_to_viewport(layer)); } @@ -405,13 +405,13 @@ impl Fsm for SelectToolFsmState { // Update bounds let transform = document .selected_nodes - .selected_visible_layers(document.metadata()) + .selected_visible_and_unlocked_layers(document.metadata()) .next() .map(|layer| document.metadata().transform_to_viewport(layer)); let transform = transform.unwrap_or(DAffine2::IDENTITY); let bounds = document .selected_nodes - .selected_visible_layers(document.metadata()) + .selected_visible_and_unlocked_layers(document.metadata()) .filter_map(|layer| { document .metadata() @@ -472,7 +472,7 @@ impl Fsm for SelectToolFsmState { .map(|bounding_box| bounding_box.check_rotate(input.mouse.position)) .unwrap_or_default(); - let mut selected: Vec<_> = document.selected_nodes.selected_visible_layers(document.metadata()).collect(); + let mut selected: Vec<_> = document.selected_nodes.selected_visible_and_unlocked_layers(document.metadata()).collect(); let intersection = document.click(input.mouse.position, &document.network); // If the user is dragging the bounding box bounds, go into ResizingBounds mode. @@ -626,7 +626,7 @@ impl Fsm for SelectToolFsmState { let snapped = if axis_align { let constraint = SnapConstraint::Line { origin: point.document_point, - direction: total_mouse_delta_document.normalize(), + direction: total_mouse_delta_document.try_normalize().unwrap_or(DVec2::X), }; tool_data.snap_manager.constrained_snap(&snap_data, point, constraint, None) } else { diff --git a/editor/src/messages/tool/transform_layer/transform_layer_message_handler.rs b/editor/src/messages/tool/transform_layer/transform_layer_message_handler.rs index 67524c520..1773d01c6 100644 --- a/editor/src/messages/tool/transform_layer/transform_layer_message_handler.rs +++ b/editor/src/messages/tool/transform_layer/transform_layer_message_handler.rs @@ -48,7 +48,7 @@ impl<'a> MessageHandler> for TransformL let selected_layers = document .selected_nodes .selected_layers(document.metadata()) - .filter(|&layer| document.metadata().node_is_visible(layer.to_node())) + .filter(|&layer| document.metadata().node_is_visible(layer.to_node()) && !document.metadata().node_is_locked(layer.to_node())) .collect::>(); let mut selected = Selected::new( diff --git a/frontend/src/components/panels/Layers.svelte b/frontend/src/components/panels/Layers.svelte index 03f862051..90f3d7a69 100644 --- a/frontend/src/components/panels/Layers.svelte +++ b/frontend/src/components/panels/Layers.svelte @@ -133,6 +133,10 @@ editor.instance.toggleLayerVisibility(id); } + function toggleLayerLock(id: bigint) { + editor.instance.toggleLayerLock(id); + } + function handleExpandArrowClick(id: bigint) { editor.instance.toggleLayerExpansion(id); } @@ -424,11 +428,11 @@ (toggleLayerVisibility(listing.entry.id), e?.stopPropagation())} + action={(e) => (toggleLayerLock(listing.entry.id), e?.stopPropagation())} size={24} - icon={listing.entry.parentsUnlocked ? "PadlockLocked" : "PadlockUnlocked"} - hoverIcon={listing.entry.parentsUnlocked ? "PadlockUnlocked" : "PadlockLocked"} - tooltip={listing.entry.parentsUnlocked ? "Unlock" : "Lock"} + icon={listing.entry.unlocked ? "PadlockUnlocked" : "PadlockLocked"} + hoverIcon={listing.entry.unlocked ? "PadlockLocked" : "PadlockUnlocked"} + tooltip={listing.entry.unlocked ? "Lock" : "Unlock"} /> {/if}