diff --git a/editor/src/messages/portfolio/document/document_message_handler.rs b/editor/src/messages/portfolio/document/document_message_handler.rs index 6411e6b196..c8f31a1193 100644 --- a/editor/src/messages/portfolio/document/document_message_handler.rs +++ b/editor/src/messages/portfolio/document/document_message_handler.rs @@ -460,7 +460,11 @@ impl MessageHandler> for DocumentMessag match ipp.keyboard.key(resize) { // Nudge translation false => { - for layer in self.selected_nodes.selected_layers(self.metadata()) { + for layer in self + .selected_nodes + .selected_layers(self.metadata()) + .filter(|&layer| self.selected_nodes.layer_visible(layer, self.metadata())) + { responses.add(GraphOperationMessage::TransformChange { layer, transform: DAffine2::from_translation(delta), @@ -491,7 +495,11 @@ impl MessageHandler> for DocumentMessag let pivot = DAffine2::from_translation(pivot); let transformation = pivot * scale * pivot.inverse(); - for layer in self.selected_nodes.selected_layers(self.metadata()) { + for layer in self + .selected_nodes + .selected_layers(self.metadata()) + .filter(|&layer| self.selected_nodes.layer_visible(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()); let new = to.inverse() * transformation * to * original_transform; @@ -614,11 +622,11 @@ impl MessageHandler> for DocumentMessag } DocumentMessage::SelectAllLayers => { let metadata = self.metadata(); - let all_layers_except_artboards_locked = metadata + let all_layers_except_artboards_and_invisible = metadata .all_layers() .filter(move |&layer| !metadata.is_artboard(layer)) - .filter(|&layer| !self.selected_nodes.layer_locked(layer, metadata)); - let nodes = all_layers_except_artboards_locked.map(|layer| layer.to_node()).collect(); + .filter(|&layer| self.selected_nodes.layer_visible(layer, metadata)); + let nodes = all_layers_except_artboards_and_invisible.map(|layer| layer.to_node()).collect(); responses.add(NodeGraphMessage::SelectedNodesSet { nodes }); } DocumentMessage::SelectedLayersLower => { 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 334dabe56d..9190c11dfb 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 @@ -459,6 +459,7 @@ impl<'a> MessageHandler> for NodeGrap } } NodeGraphMessage::ToggleVisibility { node_id } => { + responses.add(DocumentMessage::StartTransaction); let visible = document_metadata.node_is_visible(node_id); let visible = !visible; diff --git a/editor/src/messages/tool/common_functionality/snapping.rs b/editor/src/messages/tool/common_functionality/snapping.rs index 57f5c50e29..bda723b7ad 100644 --- a/editor/src/messages/tool/common_functionality/snapping.rs +++ b/editor/src/messages/tool/common_functionality/snapping.rs @@ -113,7 +113,7 @@ fn get_closest_intersection(snap_to: DVec2, curves: &[SnappedCurve]) -> Option Option SnappedPoint { + fn find_best_snap(snap_data: &mut SnapData, point: &SnapCandidatePoint, snap_results: SnapResults, constrained: bool, off_screen: bool, to_path: bool) -> SnappedPoint { let mut snapped_points = Vec::new(); let document = snap_data.document; @@ -207,7 +207,7 @@ impl SnapManager { } } - if !contrained { + if !constrained { if document.snapping_state.target_enabled(SnapTarget::Geometry(GeometrySnapTarget::Intersection)) { if let Some(closest_curves_intersection) = get_closest_intersection(point.document_point, &snap_results.curves) { snapped_points.push(closest_curves_intersection); @@ -316,8 +316,8 @@ impl SnapManager { let mut snap_data = snap_data.clone(); snap_data.candidates = Some(&*self.candidates.get_or_insert_with(|| Self::find_candidates(&snap_data, point, bbox))); - self.layer_snapper.contrained_snap(&mut snap_data, point, &mut snap_results, constraint); - self.grid_snapper.contrained_snap(&mut snap_data, point, &mut snap_results, constraint); + self.layer_snapper.constrained_snap(&mut snap_data, point, &mut snap_results, constraint); + self.grid_snapper.constrained_snap(&mut snap_data, point, &mut snap_results, constraint); Self::find_best_snap(&mut snap_data, point, snap_results, true, false, false) } diff --git a/editor/src/messages/tool/common_functionality/snapping/grid_snapper.rs b/editor/src/messages/tool/common_functionality/snapping/grid_snapper.rs index b9f652510d..061265ec7a 100644 --- a/editor/src/messages/tool/common_functionality/snapping/grid_snapper.rs +++ b/editor/src/messages/tool/common_functionality/snapping/grid_snapper.rs @@ -150,7 +150,7 @@ impl GridSnapper { } } - pub fn contrained_snap(&mut self, snap_data: &mut SnapData, point: &SnapCandidatePoint, snap_results: &mut SnapResults, constraint: SnapConstraint) { + pub fn constrained_snap(&mut self, snap_data: &mut SnapData, point: &SnapCandidatePoint, snap_results: &mut SnapResults, constraint: SnapConstraint) { let tolerance = snap_tolerance(snap_data.document); let projected = constraint.projection(point.document_point); let lines = self.get_snap_lines(projected, snap_data); @@ -170,7 +170,7 @@ impl GridSnapper { source: point.source, target: SnapTarget::Grid(GridSnapTarget::Line), at_intersection: false, - contrained: true, + constrained: true, source_bounds: point.quad, curves: [ Some(Bezier::from_linear_dvec2(projected - constraint_direction * tolerance, projected + constraint_direction * tolerance)), diff --git a/editor/src/messages/tool/common_functionality/snapping/layer_snapper.rs b/editor/src/messages/tool/common_functionality/snapping/layer_snapper.rs index e551a4469d..bdd51df7d2 100644 --- a/editor/src/messages/tool/common_functionality/snapping/layer_snapper.rs +++ b/editor/src/messages/tool/common_functionality/snapping/layer_snapper.rs @@ -227,7 +227,7 @@ impl LayerSnapper { target: candidate.target, distance, tolerance, - contrained: true, + constrained: true, target_bounds: candidate.quad, ..Default::default() }); @@ -242,7 +242,7 @@ impl LayerSnapper { self.free_snap_paths(snap_data, point, snap_results); } - pub fn contrained_snap(&mut self, snap_data: &mut SnapData, point: &SnapCandidatePoint, snap_results: &mut SnapResults, constraint: SnapConstraint) { + pub fn constrained_snap(&mut self, snap_data: &mut SnapData, point: &SnapCandidatePoint, snap_results: &mut SnapResults, constraint: SnapConstraint) { self.snap_anchors(snap_data, point, snap_results, constraint, constraint.projection(point.document_point)); self.snap_paths_constrained(snap_data, point, snap_results, constraint); } @@ -264,7 +264,7 @@ fn normals_and_tangents(path: &SnapCandidatePath, normals: bool, tangents: bool, tolerance, curves: [Some(path.document_curve), None], source: point.source, - contrained: true, + constrained: true, ..Default::default() }); } @@ -285,7 +285,7 @@ fn normals_and_tangents(path: &SnapCandidatePath, normals: bool, tangents: bool, tolerance, curves: [Some(path.document_curve), None], source: point.source, - contrained: true, + constrained: true, ..Default::default() }); } diff --git a/editor/src/messages/tool/common_functionality/snapping/snap_results.rs b/editor/src/messages/tool/common_functionality/snapping/snap_results.rs index a399ff4ad0..b4f700d809 100644 --- a/editor/src/messages/tool/common_functionality/snapping/snap_results.rs +++ b/editor/src/messages/tool/common_functionality/snapping/snap_results.rs @@ -17,7 +17,7 @@ pub struct SnappedPoint { pub source: SnapSource, pub target: SnapTarget, pub at_intersection: bool, - pub contrained: bool, // Found when looking for contrained + pub constrained: bool, // Found when looking for constrained pub target_bounds: Option, pub source_bounds: Option, pub curves: [Option; 2], @@ -56,16 +56,16 @@ impl SnappedPoint { // Prefer closest let other_closer = other_dist < my_dist + bias; - // We should prefer the most contrained option (e.g. intersection > path) - let other_more_contrained = other.contrained && !self.contrained; - let self_more_contrained = self.contrained && !other.contrained; + // We should prefer the most constrained option (e.g. intersection > path) + let other_more_constrained = other.constrained && !self.constrained; + let self_more_constrained = self.constrained && !other.constrained; // Prefer nodes to intersections if both are at the same position - let contrained_at_same_pos = other.contrained && self.contrained && self.snapped_point_document.abs_diff_eq(other.snapped_point_document, 1.); - let other_better_constraint = contrained_at_same_pos && self.at_intersection && !other.at_intersection; - let self_better_constraint = contrained_at_same_pos && other.at_intersection && !self.at_intersection; + let constrained_at_same_pos = other.constrained && self.constrained && self.snapped_point_document.abs_diff_eq(other.snapped_point_document, 1.); + let other_better_constraint = constrained_at_same_pos && self.at_intersection && !other.at_intersection; + let self_better_constraint = constrained_at_same_pos && other.at_intersection && !self.at_intersection; - (other_closer || other_more_contrained || other_better_constraint) && !self_more_contrained && !self_better_constraint + (other_closer || other_more_constrained || other_better_constraint) && !self_more_constrained && !self_better_constraint } pub fn is_snapped(&self) -> bool { self.distance.is_finite() 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 1ecb7c93c3..67524c520d 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 @@ -45,7 +45,11 @@ impl<'a> MessageHandler> for TransformL fn process_message(&mut self, message: TransformLayerMessage, responses: &mut VecDeque, (document, input, tool_data, shape_editor): TransformData) { let using_path_tool = tool_data.active_tool_type == ToolType::Path; - let selected_layers = document.selected_nodes.selected_layers(document.metadata()).collect::>(); + let selected_layers = document + .selected_nodes + .selected_layers(document.metadata()) + .filter(|&layer| document.metadata().node_is_visible(layer.to_node())) + .collect::>(); let mut selected = Selected::new( &mut self.original_transforms, diff --git a/frontend/src/components/widgets/inputs/NumberInput.svelte b/frontend/src/components/widgets/inputs/NumberInput.svelte index 3a6e686bea..e655819ad6 100644 --- a/frontend/src/components/widgets/inputs/NumberInput.svelte +++ b/frontend/src/components/widgets/inputs/NumberInput.svelte @@ -155,6 +155,7 @@ // HELPER FUNCTIONS // ================ + // Calculates the string to display when the field is not being edited. function displayText(displayValue: number | undefined): string { if (displayValue === undefined) return "-"; @@ -164,6 +165,7 @@ return `${unitlessDisplayValue}${unPluralize(unit, displayValue)}`; } + // Removes the trailing "s" from a unit if the quantity is 1. function unPluralize(unit: string, quantity: number): string { if (quantity !== 1 || !unit.endsWith("s")) return unit; return unit.slice(0, -1); @@ -174,9 +176,13 @@ // =========================== function onTextFocused() { + // The degree of precision allowed in the number that's shown when editing the number field, where additional precision is removed to round out floating point errors. + const MAX_PRECISION = 12; + const noFloatingImprecisionValue = value === undefined ? undefined : Number(value.toPrecision(MAX_PRECISION)); + if (value === undefined) text = ""; - else if (unitIsHiddenWhenEditing) text = String(value); - else text = `${value}${unPluralize(unit, value)}`; + else if (unitIsHiddenWhenEditing) text = `${noFloatingImprecisionValue}`; + else text = `${noFloatingImprecisionValue}${unPluralize(unit, value)}`; editing = true; @@ -253,7 +259,7 @@ function onDragPointerDown(e: PointerEvent) { // Only drag the number with left click (and when it's valid to do so) - if (e.button !== BUTTON_LEFT || mode !== "Increment" || value === undefined || disabled) return; + if (e.button !== BUTTON_LEFT || mode !== "Increment" || value === undefined || disabled || editing) return; // Don't drag the text value from is input element e.preventDefault();