From 85c6036b424715f0fcdbec7f48c5c9ad18d6e82a Mon Sep 17 00:00:00 2001 From: Calli Date: Thu, 19 Dec 2024 18:13:25 +0100 Subject: [PATCH] fix: select multiple lines at once and at station to multiple at once --- src/algorithm/drawing/mod.rs | 5 +- src/components/canvas/mouse_down.rs | 52 ++++++++--------- src/components/canvas/mouse_move.rs | 13 +++-- src/components/canvas/mouse_out.rs | 2 +- src/components/canvas/mouse_up.rs | 63 ++++++++++++--------- src/components/molecules/error_box.rs | 11 ++-- src/components/organisms/canvas_controls.rs | 2 +- src/components/organisms/sidebar.rs | 2 +- src/components/state/error.rs | 3 + src/components/state/map.rs | 33 +++++------ src/models/map.rs | 6 +- 11 files changed, 106 insertions(+), 86 deletions(-) diff --git a/src/algorithm/drawing/mod.rs b/src/algorithm/drawing/mod.rs index 981ef28..87d0313 100644 --- a/src/algorithm/drawing/mod.rs +++ b/src/algorithm/drawing/mod.rs @@ -49,8 +49,9 @@ where map.draw(&context, state.get_canvas_state(), 1.0); state - .get_selected_line() - .inspect(|d| d.draw(map, &context, state.get_canvas_state())); + .get_selected_lines() + .iter() + .for_each(|d| d.draw(map, &context, state.get_canvas_state())); state .get_box_select() diff --git a/src/components/canvas/mouse_down.rs b/src/components/canvas/mouse_down.rs index 88ccdf4..e581ff5 100644 --- a/src/components/canvas/mouse_down.rs +++ b/src/components/canvas/mouse_down.rs @@ -57,22 +57,24 @@ pub fn on_mouse_down(map_state: &mut MapState, ev: &UiEvent, shift_key: bool) { } // Handle a click while having a new line selected - if let Some(selected_line) = map_state - .get_selected_line() - .copied() - { - if let Some(station_at_pos) = station_at_node { - let (before, after) = selected_line.get_before_after(); - let mut line = map - .get_or_add_line(selected_line.get_line()) - .clone(); - - line.add_station(&mut map, station_at_pos, before, after); - - map.add_line(line); - map_state.set_map(map); - map_state.clear_selected_line(); + let selected_lines = map_state + .get_selected_lines() + .to_vec(); + if !selected_lines.is_empty() { + for selected_line in selected_lines { + if let Some(station_at_pos) = station_at_node { + let (before, after) = selected_line.get_before_after(); + let mut line = map + .get_or_add_line(selected_line.get_line()) + .clone(); + + line.add_station(&mut map, station_at_pos, before, after); + + map.add_line(line); + } } + map_state.set_map(map); + map_state.clear_selected_lines(); return; } @@ -149,21 +151,13 @@ pub fn on_mouse_down(map_state: &mut MapState, ev: &UiEvent, shift_key: bool) { } // Handle a click on a line - if let Some(selected_line) = map - .line_at_node(mouse_pos) - .cloned() + let selected_lines = map + .lines_at_node(mouse_pos) + .into_iter() .map(|l| SelectedLine::new(&l, &map, mouse_pos, Some(mouse_pos))) - { - map_state.set_selected_line(selected_line); - for edge in map.get_edges() { - if edge - .get_nodes() - .contains(&mouse_pos) - { - edge.print_info(); - break; - } - } + .collect::>(); + if !selected_lines.is_empty() { + map_state.set_selected_lines(selected_lines); } // Select the clicked edge, unless this was a double click. diff --git a/src/components/canvas/mouse_move.rs b/src/components/canvas/mouse_move.rs index 1955340..3464298 100644 --- a/src/components/canvas/mouse_move.rs +++ b/src/components/canvas/mouse_move.rs @@ -20,11 +20,16 @@ pub fn on_mouse_move(map_state_signal: &RwSignal, ev: &UiEvent) { let mouse_pos = GridNode::from_canvas_pos(canvas_pos, canvas_state); // Handle move of selected line. - if let Some(selected) = map_state.get_mut_selected_line() { - if selected.get_current_hover() != mouse_pos { - selected.set_current_hover(mouse_pos); - map_state_signal.set(map_state); + if !map_state + .get_selected_lines() + .is_empty() + { + for selected in map_state.get_mut_selected_lines() { + if selected.get_current_hover() != mouse_pos { + selected.set_current_hover(mouse_pos); + } } + map_state_signal.set(map_state); return; } diff --git a/src/components/canvas/mouse_out.rs b/src/components/canvas/mouse_out.rs index 22daebe..d71c92e 100644 --- a/src/components/canvas/mouse_out.rs +++ b/src/components/canvas/mouse_out.rs @@ -6,7 +6,7 @@ use crate::MapState; /// /// [mouseout]: https://developer.mozilla.org/en-US/docs/Web/API/Element/mouseout_event pub fn on_mouse_out(map_state: &mut MapState) { - map_state.clear_selected_line(); + map_state.clear_selected_lines(); map_state.clear_box_select(); map_state.clear_drag_offset(); } diff --git a/src/components/canvas/mouse_up.rs b/src/components/canvas/mouse_up.rs index 9fea962..b6282d7 100644 --- a/src/components/canvas/mouse_up.rs +++ b/src/components/canvas/mouse_up.rs @@ -17,6 +17,7 @@ use crate::{ }, models::{ GridNode, + SelectedLine, SelectedStation, }, MapState, @@ -62,7 +63,10 @@ pub fn on_mouse_up( } }, ActionType::RemoveLine => { - if let Some(selected_line) = map.line_at_node(mouse_pos) { + for selected_line in map + .lines_at_node(mouse_pos) + .clone() + { if let Err(err) = map.remove_line(selected_line.get_id()) { error_state.update(|state| state.set_error(err)); } @@ -99,37 +103,47 @@ pub fn on_mouse_up( } // Handle a mouseup while having a line selected - if let Some(selected_line) = map_state - .get_selected_line() - .copied() - { - if let Some(station_at_pos) = station_at_node { - let (before, after) = selected_line.get_before_after(); - let mut line = map - .get_or_add_line(selected_line.get_line()) - .clone(); - - line.add_station(&mut map, station_at_pos, before, after); + let selected_lines = map_state + .get_selected_lines() + .to_vec(); + if !selected_lines.is_empty() { + let mut added_station = false; + for selected_line in selected_lines.clone() { + if let Some(station_at_pos) = station_at_node { + let (before, after) = selected_line.get_before_after(); + let mut line = map + .get_or_add_line(selected_line.get_line()) + .clone(); + + line.add_station(&mut map, station_at_pos, before, after); + + if let Some(before_station) = before { + let edge_id = map.get_edge_id_between(before_station, station_at_pos); + recalculate_edge_nodes(&mut map, edge_id); + } - if let Some(before_station) = before { - let edge_id = map.get_edge_id_between(before_station, station_at_pos); - recalculate_edge_nodes(&mut map, edge_id); - } + if let Some(after_station) = after { + let edge_id = map.get_edge_id_between(station_at_pos, after_station); + recalculate_edge_nodes(&mut map, edge_id); + } - if let Some(after_station) = after { - let edge_id = map.get_edge_id_between(station_at_pos, after_station); - recalculate_edge_nodes(&mut map, edge_id); + map.add_line(line); + added_station = true; } + } - map.add_line(line); + if added_station { map_state.set_map(map); - map_state.clear_all_selections(); + map_state.clear_selected_lines(); return; } - map_state.clear_selected_line(); + map_state.clear_selected_lines(); - if let Some(grabbed_at) = selected_line.get_grabbed_at() { + if let Some(grabbed_at) = selected_lines + .first() + .and_then(SelectedLine::get_grabbed_at) + { if grabbed_at == mouse_pos { // Handle a single click on an edge if edge_at_node.is_some() @@ -154,9 +168,6 @@ pub fn on_mouse_up( map_state.set_clicked_on_edge(selected_edge, canvas_pos); return; } - } else { - map_state.clear_all_selections(); - return; } } } diff --git a/src/components/molecules/error_box.rs b/src/components/molecules/error_box.rs index 95ab3e1..75b869f 100644 --- a/src/components/molecules/error_box.rs +++ b/src/components/molecules/error_box.rs @@ -6,7 +6,10 @@ use wasm_bindgen::{ JsCast, }; -use crate::components::ErrorState; +use crate::{ + components::ErrorState, + Error, +}; /// A pop-up box for displaying errors. #[component] @@ -15,7 +18,7 @@ pub fn ErrorBox() -> impl IntoView { use_context::>().expect("to have found the global error state"); let on_click = move |_| { - error_state.update(|state| state.clear_error()); + error_state.update(ErrorState::clear_error); }; let has_error = move || { @@ -27,11 +30,11 @@ pub fn ErrorBox() -> impl IntoView { let err = error_state .get() .get_error() - .map(|e| e.to_user_friendly_string()); + .map(Error::to_user_friendly_string); if err.is_some() { let f = Closure::wrap(Box::new(move || { - error_state.update(|state| state.clear_error()); + error_state.update(ErrorState::clear_error); }) as Box); window() .set_timeout_with_callback_and_timeout_and_arguments_0( diff --git a/src/components/organisms/canvas_controls.rs b/src/components/organisms/canvas_controls.rs index b1fa54e..50c15e9 100644 --- a/src/components/organisms/canvas_controls.rs +++ b/src/components/organisms/canvas_controls.rs @@ -160,7 +160,7 @@ pub fn CanvasControls() -> impl IntoView { { state.set_map(map); } - }) + }); } if let Some(error) = resp.error { error_state.update(|state| { diff --git a/src/components/organisms/sidebar.rs b/src/components/organisms/sidebar.rs index 2b95568..7e9028c 100644 --- a/src/components/organisms/sidebar.rs +++ b/src/components/organisms/sidebar.rs @@ -71,7 +71,7 @@ pub fn Sidebar() -> impl IntoView { map_state.update(|state| { state.clear_all_selections(); let line = SelectedLine::new_line(state.get_mut_map()); - state.set_selected_line(line); + state.set_selected_lines(vec![line]); }); }; diff --git a/src/components/state/error.rs b/src/components/state/error.rs index 25ef5bb..a8dcba0 100644 --- a/src/components/state/error.rs +++ b/src/components/state/error.rs @@ -1,9 +1,12 @@ +//! Contains the [`ErrorState`] and its methods. + use crate::Error; /// Contains the current error state. /// This state is then used to display the error message to the user. #[derive(Clone, Debug)] pub struct ErrorState { + /// The error last encountered. error: Option, } diff --git a/src/components/state/map.rs b/src/components/state/map.rs index d20bbb8..4b6bd48 100644 --- a/src/components/state/map.rs +++ b/src/components/state/map.rs @@ -56,8 +56,8 @@ pub struct MapState { map: Map, /// The currently selected [`crate::models::Station`]s. selected_stations: Vec, - /// The currently selected [`crate::models::Line`]. - selected_line: Option, + /// The currently selected [`crate::models::Line`]s. + selected_lines: Vec, /// The type of action that is currently selected. selected_action: Option, /// The currently selected edges. @@ -89,7 +89,7 @@ impl MapState { Self { map, selected_stations: Vec::new(), - selected_line: None, + selected_lines: Vec::new(), selected_action: None, selected_edges: Vec::new(), canvas: CanvasState::default(), @@ -106,7 +106,7 @@ impl MapState { /// Clear all selections. pub fn clear_all_selections(&mut self) { self.clear_selected_stations(); - self.clear_selected_line(); + self.clear_selected_lines(); self.clear_selected_action(); self.clear_selected_edges(); self.clear_box_select(); @@ -192,26 +192,27 @@ impl MapState { self.selected_action = None; } - /// A mutable getter method for the selected line. - pub fn get_mut_selected_line(&mut self) -> Option<&mut SelectedLine> { - self.selected_line + /// A mutable getter method for the selected lines. + pub fn get_mut_selected_lines(&mut self) -> &mut [SelectedLine] { + self.selected_lines .as_mut() } - /// A getter method for the selected line. - pub fn get_selected_line(&self) -> Option<&SelectedLine> { - self.selected_line + /// A getter method for the selected lines. + pub fn get_selected_lines(&self) -> &[SelectedLine] { + self.selected_lines .as_ref() } - /// A setter method for the selected line. - pub fn set_selected_line(&mut self, line: SelectedLine) { - self.selected_line = Some(line); + /// A setter method for the selected lines. + pub fn set_selected_lines(&mut self, lines: Vec) { + self.selected_lines = lines; } - /// Set the selected line to None. - pub fn clear_selected_line(&mut self) { - self.selected_line = None; + /// Clear the list of selected lines. + pub fn clear_selected_lines(&mut self) { + self.selected_lines + .clear(); } /// A getter method for the selected edges. diff --git a/src/models/map.rs b/src/models/map.rs index 1440dec..7b371fc 100644 --- a/src/models/map.rs +++ b/src/models/map.rs @@ -264,10 +264,12 @@ impl Map { } /// Get the line that goes through the given grid node. - pub fn line_at_node(&self, node: GridNode) -> Option<&Line> { + pub fn lines_at_node(&self, node: GridNode) -> Vec { self.lines .values() - .find(|l| l.visits_node(self, node)) + .filter(|l| l.visits_node(self, node)) + .cloned() + .collect::>() } /// Get the edge located on the given grid node.