Skip to content

Commit 06e028b

Browse files
committed
Implement bounding box for selected layers (#349)
* Implement bounding box for selected layers * Add shift modifier for multi selection
1 parent ec93adb commit 06e028b

File tree

10 files changed

+159
-74
lines changed

10 files changed

+159
-74
lines changed

editor/src/communication/dispatcher.rs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ impl Dispatcher {
3030
| Message::Frontend(FrontendMessage::UpdateScrollbars { .. })
3131
| Message::Frontend(FrontendMessage::SetCanvasZoom { .. })
3232
| Message::Frontend(FrontendMessage::SetCanvasRotation { .. })
33-
| Message::Documents(DocumentsMessage::Document(DocumentMessage::DispatchOperation { .. }))
3433
) || MessageDiscriminant::from(&message).local_name().ends_with("MouseMove"))
3534
{
3635
log::trace!("Message: {:?}", message);
@@ -152,7 +151,7 @@ mod test {
152151
let document_before_copy = editor.dispatcher.documents_message_handler.active_document().document.clone();
153152
let shape_id = document_before_copy.root.as_folder().unwrap().layer_ids[1];
154153

155-
editor.handle_message(DocumentMessage::SelectLayers(vec![vec![shape_id]])).unwrap();
154+
editor.handle_message(DocumentMessage::SetSelectedLayers(vec![vec![shape_id]])).unwrap();
156155
editor.handle_message(DocumentsMessage::CopySelectedLayers).unwrap();
157156
editor.handle_message(DocumentsMessage::PasteLayers { path: vec![], insert_index: -1 }).unwrap();
158157

@@ -212,7 +211,7 @@ mod test {
212211
})
213212
.unwrap();
214213

215-
editor.handle_message(DocumentMessage::SelectLayers(vec![vec![folder_id]])).unwrap();
214+
editor.handle_message(DocumentMessage::SetSelectedLayers(vec![vec![folder_id]])).unwrap();
216215

217216
let document_before_copy = editor.dispatcher.documents_message_handler.active_document().document.clone();
218217

@@ -276,7 +275,7 @@ mod test {
276275
let rect_id = document_before_copy.root.as_folder().unwrap().layer_ids[RECT_INDEX];
277276
let ellipse_id = document_before_copy.root.as_folder().unwrap().layer_ids[ELLIPSE_INDEX];
278277

279-
editor.handle_message(DocumentMessage::SelectLayers(vec![vec![rect_id], vec![ellipse_id]])).unwrap();
278+
editor.handle_message(DocumentMessage::SetSelectedLayers(vec![vec![rect_id], vec![ellipse_id]])).unwrap();
280279
editor.handle_message(DocumentsMessage::CopySelectedLayers).unwrap();
281280
editor.handle_message(DocumentMessage::DeleteSelectedLayers).unwrap();
282281
editor.draw_rect(0., 800., 12., 200.);
@@ -310,7 +309,7 @@ mod test {
310309

311310
let verify_order = |handler: &mut DocumentMessageHandler| (handler.all_layers_sorted(), handler.non_selected_layers_sorted(), handler.selected_layers_sorted());
312311

313-
editor.handle_message(DocumentMessage::SelectLayers(vec![vec![0], vec![2]])).unwrap();
312+
editor.handle_message(DocumentMessage::SetSelectedLayers(vec![vec![0], vec![2]])).unwrap();
314313

315314
editor.handle_message(DocumentMessage::ReorderSelectedLayers(1)).unwrap();
316315
let (all, non_selected, selected) = verify_order(&mut editor.dispatcher.documents_message_handler.active_document_mut());

editor/src/document/document_file.rs

Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,10 @@ pub enum DocumentMessage {
6565
#[child]
6666
Movement(MovementMessage),
6767
DispatchOperation(Box<DocumentOperation>),
68-
SelectLayers(Vec<Vec<LayerId>>),
68+
SetSelectedLayers(Vec<Vec<LayerId>>),
69+
AddSelectedLayers(Vec<Vec<LayerId>>),
6970
SelectAllLayers,
71+
SelectionChanged,
7072
DeselectAllLayers,
7173
DeleteLayer(Vec<LayerId>),
7274
DeleteSelectedLayers,
@@ -125,9 +127,17 @@ impl DocumentMessageHandler {
125127
self.layer_data.values_mut().for_each(|layer_data| layer_data.selected = false);
126128
}
127129
fn select_layer(&mut self, path: &[LayerId]) -> Option<Message> {
130+
if self.document.layer(path).ok()?.overlay {
131+
return None;
132+
}
128133
self.layer_data(path).selected = true;
134+
let data = self.layer_panel_entry(path.to_vec()).ok()?;
129135
// TODO: Add deduplication
130-
(!path.is_empty()).then(|| self.handle_folder_changed(path[..path.len() - 1].to_vec())).flatten()
136+
(!path.is_empty()).then(|| FrontendMessage::UpdateLayer { path: path.to_vec().into(), data }.into())
137+
}
138+
pub fn selected_layers_bounding_box(&self) -> Option<[DVec2; 2]> {
139+
let paths = self.selected_layers().map(|vec| &vec[..]);
140+
self.document.combined_viewport_bounding_box(paths)
131141
}
132142
pub fn layerdata(&self, path: &[LayerId]) -> &LayerData {
133143
self.layer_data.get(path).expect("Layerdata does not exist")
@@ -247,6 +257,7 @@ impl DocumentMessageHandler {
247257
.iter()
248258
.zip(paths.iter().zip(data))
249259
.rev()
260+
.filter(|(layer, _)| !layer.overlay)
250261
.map(|(layer, (path, data))| {
251262
layer_panel_entry(
252263
&data,
@@ -345,35 +356,40 @@ impl MessageHandler<DocumentMessage, &InputPreprocessor> for DocumentMessageHand
345356
self.layer_data(&path).expanded ^= true;
346357
responses.extend(self.handle_folder_changed(path));
347358
}
359+
SelectionChanged => responses.push_back(SelectMessage::UpdateSelectionBoundingBox.into()),
348360
DeleteSelectedLayers => {
349361
for path in self.selected_layers().cloned() {
350362
responses.push_back(DocumentOperation::DeleteLayer { path }.into())
351363
}
364+
responses.push_back(SelectMessage::UpdateSelectionBoundingBox.into());
352365
}
353366
DuplicateSelectedLayers => {
354367
for path in self.selected_layers_sorted() {
355368
responses.push_back(DocumentOperation::DuplicateLayer { path }.into())
356369
}
357370
}
358-
SelectLayers(paths) => {
371+
SetSelectedLayers(paths) => {
359372
self.clear_selection();
373+
responses.push_front(AddSelectedLayers(paths).into());
374+
}
375+
AddSelectedLayers(paths) => {
360376
for path in paths {
361377
responses.extend(self.select_layer(&path));
362378
}
363379
// TODO: Correctly update layer panel in clear_selection instead of here
364380
responses.extend(self.handle_folder_changed(Vec::new()));
381+
responses.push_back(SelectMessage::UpdateSelectionBoundingBox.into());
365382
}
366383
SelectAllLayers => {
367-
let all_layer_paths = self.layer_data.keys().filter(|path| !path.is_empty()).cloned().collect::<Vec<_>>();
368-
for path in all_layer_paths {
369-
responses.extend(self.select_layer(&path));
370-
}
371-
}
372-
DeselectAllLayers => {
373-
self.clear_selection();
374-
let children = self.layer_panel(&[]).expect("The provided Path was not valid");
375-
responses.push_back(FrontendMessage::ExpandFolder { path: vec![].into(), children }.into());
384+
let all_layer_paths = self
385+
.layer_data
386+
.keys()
387+
.filter(|path| !path.is_empty() && !self.document.layer(path).unwrap().overlay)
388+
.cloned()
389+
.collect::<Vec<_>>();
390+
responses.push_back(SetSelectedLayers(all_layer_paths).into());
376391
}
392+
DeselectAllLayers => responses.push_back(SetSelectedLayers(vec![]).into()),
377393
Undo => {
378394
// this is a temporary fix and will be addressed by #123
379395
if let Some(id) = self.document.root.as_folder().unwrap().list_layers().last() {
@@ -391,10 +407,11 @@ impl MessageHandler<DocumentMessage, &InputPreprocessor> for DocumentMessageHand
391407
DocumentResponse::FolderChanged { path } => self.handle_folder_changed(path),
392408
DocumentResponse::DeletedLayer { path } => {
393409
self.layer_data.remove(&path);
394-
None
410+
411+
Some(SelectMessage::UpdateSelectionBoundingBox.into())
395412
}
396413
DocumentResponse::LayerChanged { path } => self.layer_panel_entry(path.clone()).ok().map(|data| FrontendMessage::UpdateLayer { path: path.into(), data }.into()),
397-
DocumentResponse::CreatedLayer { path } => self.select_layer(&path),
414+
DocumentResponse::CreatedLayer { path } => (!self.document.layer(&path).unwrap().overlay).then(|| SetSelectedLayers(vec![path]).into()),
398415
DocumentResponse::DocumentChanged => unreachable!(),
399416
})
400417
.flatten(),
@@ -441,6 +458,7 @@ impl MessageHandler<DocumentMessage, &InputPreprocessor> for DocumentMessageHand
441458
};
442459
responses.push_back(operation.into());
443460
}
461+
responses.push_back(SelectMessage::UpdateSelectionBoundingBox.into());
444462
}
445463
MoveSelectedLayersTo { path, insert_index } => {
446464
responses.push_back(DocumentsMessage::CopySelectedLayers.into());
@@ -496,6 +514,7 @@ impl MessageHandler<DocumentMessage, &InputPreprocessor> for DocumentMessageHand
496514
.into(),
497515
);
498516
}
517+
responses.push_back(SelectMessage::UpdateSelectionBoundingBox.into());
499518
}
500519
}
501520
AlignSelectedLayers(axis, aggregate) => {
@@ -528,6 +547,7 @@ impl MessageHandler<DocumentMessage, &InputPreprocessor> for DocumentMessageHand
528547
.into(),
529548
);
530549
}
550+
responses.push_back(SelectMessage::UpdateSelectionBoundingBox.into());
531551
}
532552
}
533553
RenameLayer(path, name) => responses.push_back(DocumentOperation::RenameLayer { path, name }.into()),

editor/src/document/movement_handler.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ impl MessageHandler<MovementMessage, (&mut LayerData, &Document, &InputPreproces
105105

106106
layerdata.rotation += rotation;
107107
layerdata.snap_rotate = snapping;
108+
responses.push_back(SelectMessage::UpdateSelectionBoundingBox.into());
108109
responses.push_back(
109110
FrontendMessage::SetCanvasRotation {
110111
new_radians: layerdata.snapped_angle(),
@@ -172,6 +173,7 @@ impl MessageHandler<MovementMessage, (&mut LayerData, &Document, &InputPreproces
172173
layerdata.rotation = new;
173174
self.create_document_transform_from_layerdata(layerdata, &ipp.viewport_bounds, responses);
174175
responses.push_back(FrontendMessage::SetCanvasRotation { new_radians: new }.into());
176+
responses.push_back(SelectMessage::UpdateSelectionBoundingBox.into());
175177
}
176178
ZoomCanvasToFitAll => {
177179
if let Some([pos1, pos2]) = document.visible_layers_bounding_box() {

editor/src/input/input_mapper.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ impl Default for Mapping {
133133
// Higher priority than entries in sections below
134134
// Select
135135
entry! {action=SelectMessage::MouseMove, message=InputMapperMessage::PointerMove},
136-
entry! {action=SelectMessage::DragStart, key_down=Lmb},
136+
entry! {action=SelectMessage::DragStart{add_to_selection: KeyShift}, key_down=Lmb},
137137
entry! {action=SelectMessage::DragStop, key_up=Lmb},
138138
entry! {action=SelectMessage::Abort, key_down=Rmb},
139139
entry! {action=SelectMessage::Abort, key_down=KeyEscape},

editor/src/input/keyboard.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use crate::message_prelude::*;
2+
use serde::{Deserialize, Serialize};
23

34
pub const NUMBER_OF_KEYS: usize = Key::NumKeys as usize;
45
// Edit this to specify the storage type used
@@ -12,7 +13,7 @@ const KEY_MASK_STORAGE_LENGTH: usize = (NUMBER_OF_KEYS + STORAGE_SIZE_BITS - 1)
1213
pub type KeyStates = BitVector<KEY_MASK_STORAGE_LENGTH>;
1314

1415
#[impl_message(Message, InputMapperMessage, KeyDown)]
15-
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
16+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
1617
pub enum Key {
1718
UnknownKey,
1819
// MouseKeys

editor/src/tool/tool_message_handler.rs

Lines changed: 43 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ pub enum ToolMessage {
1616
SelectSecondaryColor(Color),
1717
SwapColors,
1818
ResetColors,
19+
NoOp,
1920
SetToolOptions(ToolType, ToolOptions),
2021
#[child]
2122
Fill(FillMessage),
@@ -59,16 +60,27 @@ impl MessageHandler<ToolMessage, (&DocumentMessageHandler, &InputPreprocessor)>
5960
update_working_colors(&self.tool_state.document_tool_data, responses);
6061
}
6162
SelectTool(tool) => {
62-
let mut reset = |tool| match tool {
63-
ToolType::Ellipse => responses.push_back(EllipseMessage::Abort.into()),
64-
ToolType::Rectangle => responses.push_back(RectangleMessage::Abort.into()),
65-
ToolType::Shape => responses.push_back(ShapeMessage::Abort.into()),
66-
ToolType::Line => responses.push_back(LineMessage::Abort.into()),
67-
ToolType::Pen => responses.push_back(PenMessage::Abort.into()),
68-
_ => (),
63+
let old_tool = self.tool_state.tool_data.active_tool_type;
64+
let reset = |tool| match tool {
65+
ToolType::Ellipse => EllipseMessage::Abort.into(),
66+
ToolType::Rectangle => RectangleMessage::Abort.into(),
67+
ToolType::Shape => ShapeMessage::Abort.into(),
68+
ToolType::Line => LineMessage::Abort.into(),
69+
ToolType::Pen => PenMessage::Abort.into(),
70+
ToolType::Select => SelectMessage::Abort.into(),
71+
_ => ToolMessage::NoOp,
6972
};
70-
reset(tool);
71-
reset(self.tool_state.tool_data.active_tool_type);
73+
let (new, old) = (reset(tool), reset(old_tool));
74+
let mut send_to_tool = |tool_type, message: ToolMessage| {
75+
if let Some(tool) = self.tool_state.tool_data.tools.get_mut(&tool_type) {
76+
tool.process_action(message, (document, &self.tool_state.document_tool_data, input), responses);
77+
}
78+
};
79+
send_to_tool(tool, new);
80+
send_to_tool(old_tool, old);
81+
if tool == ToolType::Select {
82+
responses.push_back(SelectMessage::UpdateSelectionBoundingBox.into());
83+
}
7284
self.tool_state.tool_data.active_tool_type = tool;
7385

7486
responses.push_back(FrontendMessage::SetActiveTool { tool_name: tool.to_string() }.into())
@@ -88,22 +100,11 @@ impl MessageHandler<ToolMessage, (&DocumentMessageHandler, &InputPreprocessor)>
88100
self.tool_state.document_tool_data.tool_options.insert(tool_type, tool_options);
89101
}
90102
message => {
91-
let tool_type = match message {
92-
Fill(_) => ToolType::Fill,
93-
Rectangle(_) => ToolType::Rectangle,
94-
Ellipse(_) => ToolType::Ellipse,
95-
Shape(_) => ToolType::Shape,
96-
Line(_) => ToolType::Line,
97-
Pen(_) => ToolType::Pen,
98-
Select(_) => ToolType::Select,
99-
Crop(_) => ToolType::Crop,
100-
Eyedropper(_) => ToolType::Eyedropper,
101-
Navigate(_) => ToolType::Navigate,
102-
Path(_) => ToolType::Path,
103-
_ => unreachable!(),
104-
};
103+
let tool_type = message_to_tool_type(&message);
105104
if let Some(tool) = self.tool_state.tool_data.tools.get_mut(&tool_type) {
106-
tool.process_action(message, (document, &self.tool_state.document_tool_data, input), responses);
105+
if tool_type == self.tool_state.tool_data.active_tool_type {
106+
tool.process_action(message, (document, &self.tool_state.document_tool_data, input), responses);
107+
}
107108
}
108109
}
109110
}
@@ -115,6 +116,24 @@ impl MessageHandler<ToolMessage, (&DocumentMessageHandler, &InputPreprocessor)>
115116
}
116117
}
117118

119+
fn message_to_tool_type(message: &ToolMessage) -> ToolType {
120+
use ToolMessage::*;
121+
match message {
122+
Fill(_) => ToolType::Fill,
123+
Rectangle(_) => ToolType::Rectangle,
124+
Ellipse(_) => ToolType::Ellipse,
125+
Shape(_) => ToolType::Shape,
126+
Line(_) => ToolType::Line,
127+
Pen(_) => ToolType::Pen,
128+
Select(_) => ToolType::Select,
129+
Crop(_) => ToolType::Crop,
130+
Eyedropper(_) => ToolType::Eyedropper,
131+
Navigate(_) => ToolType::Navigate,
132+
Path(_) => ToolType::Path,
133+
_ => unreachable!(),
134+
}
135+
}
136+
118137
fn update_working_colors(doc_data: &DocumentToolData, responses: &mut VecDeque<Message>) {
119138
responses.push_back(
120139
FrontendMessage::UpdateWorkingColors {

0 commit comments

Comments
 (0)