From 399111df530217bbba7d02dfb7cf1fb781a289b5 Mon Sep 17 00:00:00 2001 From: Keavon Chambers Date: Sun, 1 Aug 2021 01:29:23 -0700 Subject: [PATCH] Add a selected_layers() function and refactor code to use it (#314) * Add a selected_layers() function * Refactor AlignSelectedLayers Co-authored-by: Dennis Kobert --- .../src/document/document_message_handler.rs | 78 ++++++++----------- 1 file changed, 31 insertions(+), 47 deletions(-) diff --git a/core/editor/src/document/document_message_handler.rs b/core/editor/src/document/document_message_handler.rs index 7ab5bdba6..6ab2a0d0c 100644 --- a/core/editor/src/document/document_message_handler.rs +++ b/core/editor/src/document/document_message_handler.rs @@ -136,6 +136,9 @@ impl DocumentMessageHandler { // TODO: Add deduplication (!path.is_empty()).then(|| self.handle_folder_changed(path[..path.len() - 1].to_vec())).flatten() } + fn selected_layers(&self) -> impl Iterator> { + self.active_document().layer_data.iter().filter_map(|(path, data)| data.selected.then(|| path)) + } fn layerdata(&self, path: &[LayerId]) -> &LayerData { self.active_document().layer_data.get(path).expect("Layerdata does not exist") } @@ -155,8 +158,7 @@ impl DocumentMessageHandler { ); } - /// Returns the paths to all layers in order, optionally including only selected or non - /// selected layers. + /// Returns the paths to all layers in order, optionally including only selected or non-selected layers. fn layers_sorted(&self, selected: Option) -> Vec> { // Compute the indices for each layer to be able to sort them let mut layers_with_indices: Vec<(Vec, Vec)> = self @@ -366,17 +368,14 @@ impl MessageHandler for DocumentMessageHand .into(), ), SetBlendModeForSelectedLayers(blend_mode) => { - let active_document = self.active_document(); - - for path in active_document.layer_data.iter().filter_map(|(path, data)| data.selected.then(|| path.clone())) { + for path in self.selected_layers().cloned() { responses.push_back(DocumentOperation::SetLayerBlendMode { path, blend_mode }.into()); } } SetOpacityForSelectedLayers(opacity) => { let opacity = opacity.clamp(0., 1.); - let active_document = self.active_document(); - for path in active_document.layer_data.iter().filter_map(|(path, data)| data.selected.then(|| path.clone())) { + for path in self.selected_layers().cloned() { responses.push_back(DocumentOperation::SetLayerOpacity { path, opacity }.into()); } } @@ -388,8 +387,7 @@ impl MessageHandler for DocumentMessageHand responses.extend(self.handle_folder_changed(path)); } DeleteSelectedLayers => { - let paths = self.selected_layers_sorted(); - for path in paths { + for path in self.selected_layers().cloned() { responses.push_back(DocumentOperation::DeleteLayer { path }.into()) } } @@ -608,14 +606,12 @@ impl MessageHandler for DocumentMessageHand responses.push_back(FrontendMessage::SetCanvasRotation { new_radians: new }.into()); } NudgeSelectedLayers(x, y) => { - let paths = self.selected_layers_sorted(); - let delta = { let root_layer_rotation = self.layerdata_mut(&[]).rotation; let rotate_to_viewport_space = DAffine2::from_angle(root_layer_rotation).inverse(); rotate_to_viewport_space.transform_point2((x, y).into()) }; - for path in paths { + for path in self.selected_layers().cloned() { let operation = DocumentOperation::TransformLayer { path, transform: DAffine2::from_translation(delta).to_cols_array(), @@ -711,13 +707,12 @@ impl MessageHandler for DocumentMessageHand } AlignSelectedLayers(axis, aggregate) => { // TODO: Handle folder nested transforms with the transforms API - let selected_paths = self.selected_layers_sorted(); - if selected_paths.is_empty() { + if self.selected_layers().next().is_none() { return; } - let selected_layers = selected_paths.iter().filter_map(|path| { - let layer = self.active_document().document.layer(path).unwrap(); + let selected_layers = self.selected_layers().cloned().filter_map(|path| { + let layer = self.active_document().document.layer(&path).ok()?; let point = { let bounding_box = layer.bounding_box(layer.transform, layer.style)?; match aggregate { @@ -731,44 +726,33 @@ impl MessageHandler for DocumentMessageHand AlignAxis::X => (point.x, layer.transform.translation.x), AlignAxis::Y => (point.y, layer.transform.translation.y), }; - Some((path.clone(), bounding_box_coord, translation_coord)) + Some((path, bounding_box_coord, translation_coord)) }); + let selected_layers: Vec<_> = selected_layers.collect(); - let bounding_box_coords = selected_layers.clone().map(|(_, bounding_box_coord, _)| bounding_box_coord); - let aggregated_coord = match aggregate { - AlignAggregate::Min => bounding_box_coords.reduce(|a, b| a.min(b)).unwrap(), - AlignAggregate::Max => bounding_box_coords.reduce(|a, b| a.max(b)).unwrap(), + let bounding_box_coords = selected_layers.iter().map(|(_, bounding_box_coord, _)| bounding_box_coord).cloned(); + if let Some(aggregated_coord) = match aggregate { + AlignAggregate::Min => bounding_box_coords.reduce(|a, b| a.min(b)), + AlignAggregate::Max => bounding_box_coords.reduce(|a, b| a.max(b)), AlignAggregate::Center => { // TODO: Refactor with `reduce` and `merge_bounding_boxes` once the latter is added - let bounding_boxes = selected_paths.iter().filter_map(|path| { - let layer = self.active_document().document.layer(path).unwrap(); - layer.bounding_box(layer.transform, layer.style) - }); - let min = bounding_boxes - .clone() + self.selected_layers() + .filter_map(|path| self.active_document().document.layer(path).ok().map(|layer| layer.bounding_box(layer.transform, layer.style)).flatten()) .map(|bbox| match axis { - AlignAxis::X => bbox[0].x, - AlignAxis::Y => bbox[0].y, + AlignAxis::X => (bbox[0].x, bbox[1].x), + AlignAxis::Y => (bbox[0].y, bbox[1].y), }) - .reduce(|a, b| a.min(b)) - .unwrap(); - let max = bounding_boxes - .clone() - .map(|bbox| match axis { - AlignAxis::X => bbox[1].x, - AlignAxis::Y => bbox[1].y, - }) - .reduce(|a, b| a.max(b)) - .unwrap(); - (min + max) / 2. + .reduce(|(a, b), (c, d)| (a.min(c), b.max(d))) + .map(|(min, max)| (min + max) / 2.) } - AlignAggregate::Average => bounding_box_coords.sum::() / selected_paths.len() as f64, - }; - for (path, bounding_box_coord, translation_coord) in selected_layers { - let new_coord = aggregated_coord - (bounding_box_coord - translation_coord); - match axis { - AlignAxis::X => responses.push_back(DocumentMessage::SetLayerTranslation(path, Some(new_coord), None).into()), - AlignAxis::Y => responses.push_back(DocumentMessage::SetLayerTranslation(path, None, Some(new_coord)).into()), + AlignAggregate::Average => Some(bounding_box_coords.sum::() / selected_layers.len() as f64), + } { + for (path, bounding_box_coord, translation_coord) in selected_layers { + let new_coord = aggregated_coord - (bounding_box_coord - translation_coord); + match axis { + AlignAxis::X => responses.push_back(DocumentMessage::SetLayerTranslation(path, Some(new_coord), None).into()), + AlignAxis::Y => responses.push_back(DocumentMessage::SetLayerTranslation(path, None, Some(new_coord)).into()), + } } } }