|
| 1 | +use crate::consts::{COLOR_ACCENT, PATH_OUTLINE_WEIGHT}; |
| 2 | +use crate::document::DocumentMessageHandler; |
| 3 | +use crate::message_prelude::*; |
| 4 | + |
| 5 | +use graphene::layers::layer_info::LayerDataType; |
| 6 | +use graphene::layers::style::{self, Fill, Stroke}; |
| 7 | +use graphene::{LayerId, Operation}; |
| 8 | + |
| 9 | +use glam::DAffine2; |
| 10 | +use kurbo::{BezPath, Shape}; |
| 11 | +use std::collections::VecDeque; |
| 12 | + |
| 13 | +/// Manages the overlay used by the select tool for outlining selected shapes and when hovering over a non selected shape. |
| 14 | +#[derive(Clone, Debug, Default)] |
| 15 | +pub struct PathOutline { |
| 16 | + hovered_layer_path: Option<Vec<LayerId>>, |
| 17 | + hovered_overlay_path: Option<Vec<LayerId>>, |
| 18 | + selected_overlay_paths: Vec<Vec<LayerId>>, |
| 19 | +} |
| 20 | + |
| 21 | +impl PathOutline { |
| 22 | + /// Creates an outline of a layer either with a pre-existing overlay or by generating a new one |
| 23 | + fn create_outline(document_layer_path: Vec<LayerId>, overlay_path: Option<Vec<LayerId>>, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) -> Option<Vec<LayerId>> { |
| 24 | + // Get layer data |
| 25 | + let document_layer = document.graphene_document.layer(&document_layer_path).ok()?; |
| 26 | + |
| 27 | + // Get the bezpath from the shape or text |
| 28 | + let path = match &document_layer.data { |
| 29 | + LayerDataType::Shape(shape) => Some(shape.path.clone()), |
| 30 | + LayerDataType::Text(text) => Some(text.to_bez_path_nonmut(&document.graphene_document.font_cache)), |
| 31 | + _ => document_layer |
| 32 | + .aabounding_box_for_transform(DAffine2::IDENTITY, &document.graphene_document.font_cache) |
| 33 | + .map(|bounds| kurbo::Rect::new(bounds[0].x, bounds[0].y, bounds[1].x, bounds[1].y).to_path(0.)), |
| 34 | + }?; |
| 35 | + |
| 36 | + // Generate a new overlay layer if necessary |
| 37 | + let overlay = match overlay_path { |
| 38 | + Some(path) => path, |
| 39 | + None => { |
| 40 | + let overlay_path = vec![generate_uuid()]; |
| 41 | + let operation = Operation::AddOverlayShape { |
| 42 | + path: overlay_path.clone(), |
| 43 | + bez_path: BezPath::new(), |
| 44 | + style: style::PathStyle::new(Some(Stroke::new(COLOR_ACCENT, PATH_OUTLINE_WEIGHT)), Fill::None), |
| 45 | + closed: false, |
| 46 | + }; |
| 47 | + |
| 48 | + responses.push_back(DocumentMessage::Overlays(operation.into()).into()); |
| 49 | + |
| 50 | + overlay_path |
| 51 | + } |
| 52 | + }; |
| 53 | + |
| 54 | + // Update the shape bezpath |
| 55 | + let operation = Operation::SetShapePath { |
| 56 | + path: overlay.clone(), |
| 57 | + bez_path: path, |
| 58 | + }; |
| 59 | + responses.push_back(DocumentMessage::Overlays(operation.into()).into()); |
| 60 | + |
| 61 | + // Update the transform to match the document |
| 62 | + let operation = Operation::SetLayerTransform { |
| 63 | + path: overlay.clone(), |
| 64 | + transform: document.graphene_document.multiply_transforms(&document_layer_path).unwrap().to_cols_array(), |
| 65 | + }; |
| 66 | + responses.push_back(DocumentMessage::Overlays(operation.into()).into()); |
| 67 | + |
| 68 | + Some(overlay) |
| 69 | + } |
| 70 | + |
| 71 | + /// Removes the hovered overlay and deletes path references |
| 72 | + pub fn clear_hovered(&mut self, responses: &mut VecDeque<Message>) { |
| 73 | + if let Some(path) = self.hovered_overlay_path.take() { |
| 74 | + let operation = Operation::DeleteLayer { path }; |
| 75 | + responses.push_back(DocumentMessage::Overlays(operation.into()).into()); |
| 76 | + } |
| 77 | + self.hovered_layer_path = None; |
| 78 | + } |
| 79 | + |
| 80 | + /// Updates the overlay, generating a new one if necessary |
| 81 | + pub fn update_hovered(&mut self, new_layer_path: Vec<LayerId>, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) { |
| 82 | + // Check if we are hovering over a different layer than before |
| 83 | + if self.hovered_layer_path.as_ref().map_or(true, |old| &new_layer_path != old) { |
| 84 | + self.hovered_overlay_path = Self::create_outline(new_layer_path.clone(), self.hovered_overlay_path.take(), document, responses); |
| 85 | + if self.hovered_overlay_path.is_none() { |
| 86 | + self.clear_hovered(responses); |
| 87 | + } |
| 88 | + } |
| 89 | + self.hovered_layer_path = Some(new_layer_path); |
| 90 | + } |
| 91 | + |
| 92 | + /// Clears overlays for the seleted paths and removes references |
| 93 | + pub fn clear_selected(&mut self, responses: &mut VecDeque<Message>) { |
| 94 | + if let Some(path) = self.selected_overlay_paths.pop() { |
| 95 | + let operation = Operation::DeleteLayer { path }; |
| 96 | + responses.push_back(DocumentMessage::Overlays(operation.into()).into()); |
| 97 | + } |
| 98 | + } |
| 99 | + |
| 100 | + /// Updates the selected overlays, generating or removing overlays if necessary |
| 101 | + pub fn update_selected<'a>(&mut self, selected: impl Iterator<Item = &'a [LayerId]>, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) { |
| 102 | + let mut old_overlay_paths = std::mem::take(&mut self.selected_overlay_paths); |
| 103 | + |
| 104 | + for document_layer_path in selected { |
| 105 | + if let Some(overlay_path) = Self::create_outline(document_layer_path.to_vec(), old_overlay_paths.pop(), document, responses) { |
| 106 | + self.selected_overlay_paths.push(overlay_path); |
| 107 | + } |
| 108 | + } |
| 109 | + for path in old_overlay_paths { |
| 110 | + let operation = Operation::DeleteLayer { path }; |
| 111 | + responses.push_back(DocumentMessage::Overlays(operation.into()).into()); |
| 112 | + } |
| 113 | + } |
| 114 | +} |
0 commit comments