diff --git a/locales/en-US/main.ftl b/locales/en-US/main.ftl index 9b2f0b0..ead3729 100644 --- a/locales/en-US/main.ftl +++ b/locales/en-US/main.ftl @@ -22,6 +22,7 @@ action-measure-length = Measure Length action-undo = Undo action-redo = Redo +action-abort = Abort presort-by-pairwise-detours = Presort by Pairwise Detours squeeze-through-under-bands = Squeeze through under Bands diff --git a/src/bin/topola-egui/activity.rs b/src/bin/topola-egui/activity.rs index 5c29027..27c1c9c 100644 --- a/src/bin/topola-egui/activity.rs +++ b/src/bin/topola-egui/activity.rs @@ -7,6 +7,7 @@ use topola::{ InvokerStatus, }, }, + board::mesadata::AccessMesadata, drawing::graph::PrimitiveIndex, geometry::primitive::PrimitiveShape, router::{navmesh::Navmesh, trace::TraceStepper}, @@ -105,52 +106,63 @@ impl GetObstacles for ActivityStepper { } } -pub struct ActivityWithStatus { +pub struct ActivityStepperWithStatus { activity: ActivityStepper, maybe_status: Option, } -impl ActivityWithStatus { - pub fn new_execution(execution: ExecutionStepper) -> ActivityWithStatus { +impl ActivityStepperWithStatus { + pub fn new_execution(execution: ExecutionStepper) -> ActivityStepperWithStatus { Self { activity: ActivityStepper::Execution(execution), maybe_status: None, } } - pub fn step( - &mut self, - invoker: &mut Invoker, - ) -> Result { - let status = self.activity.step(invoker)?; - self.maybe_status = Some(status.clone()); - Ok(status.into()) - } - pub fn maybe_status(&self) -> Option { self.maybe_status.clone() } } -impl GetMaybeNavmesh for ActivityWithStatus { +impl Step, ActivityStatus, ActivityError, ()> + for ActivityStepperWithStatus +{ + fn step( + &mut self, + invoker: &mut Invoker, + ) -> Result { + let status = self.activity.step(invoker)?; + self.maybe_status = Some(status.clone()); + Ok(status.into()) + } +} + +impl Abort> for ActivityStepperWithStatus { + fn abort(&mut self, invoker: &mut Invoker) { + self.maybe_status = Some(ActivityStatus::Finished(String::from("aborted"))); + self.activity.abort(invoker); + } +} + +impl GetMaybeNavmesh for ActivityStepperWithStatus { fn maybe_navmesh(&self) -> Option<&Navmesh> { self.activity.maybe_navmesh() } } -impl GetMaybeTrace for ActivityWithStatus { +impl GetMaybeTrace for ActivityStepperWithStatus { fn maybe_trace(&self) -> Option<&TraceStepper> { self.activity.maybe_trace() } } -impl GetGhosts for ActivityWithStatus { +impl GetGhosts for ActivityStepperWithStatus { fn ghosts(&self) -> &[PrimitiveShape] { self.activity.ghosts() } } -impl GetObstacles for ActivityWithStatus { +impl GetObstacles for ActivityStepperWithStatus { fn obstacles(&self) -> &[PrimitiveIndex] { self.activity.obstacles() } diff --git a/src/bin/topola-egui/app.rs b/src/bin/topola-egui/app.rs index 0ff3d04..0eafb1f 100644 --- a/src/bin/topola-egui/app.rs +++ b/src/bin/topola-egui/app.rs @@ -11,10 +11,11 @@ use unic_langid::{langid, LanguageIdentifier}; use topola::{ autorouter::{invoker::Invoker, Autorouter}, specctra::{design::SpecctraDesign, mesadata::SpecctraMesadata}, + stepper::Step, }; use crate::{ - activity::{ActivityStatus, ActivityWithStatus}, + activity::{ActivityStatus, ActivityStepperWithStatus}, error_dialog::ErrorDialog, file_receiver::FileReceiver, layers::Layers, @@ -38,7 +39,7 @@ pub struct App { arc_mutex_maybe_invoker: Arc>>>, #[serde(skip)] - maybe_activity: Option, + maybe_activity: Option, #[serde(skip)] content_channel: (Sender, Receiver), @@ -50,10 +51,10 @@ pub struct App { viewport: Viewport, #[serde(skip)] - top: MenuBar, + menu_bar: MenuBar, #[serde(skip)] - bottom: StatusBar, + status_bar: StatusBar, #[serde(skip)] error_dialog: ErrorDialog, @@ -78,8 +79,8 @@ impl Default for App { content_channel: channel(), history_channel: channel(), viewport: Viewport::new(), - top: MenuBar::new(), - bottom: StatusBar::new(), + menu_bar: MenuBar::new(), + status_bar: StatusBar::new(), error_dialog: ErrorDialog::new(), maybe_layers: None, maybe_design: None, @@ -109,8 +110,8 @@ impl App { fn advance_state_by_dt(&mut self, dt: f32) { self.update_counter += dt; - while self.update_counter >= self.top.frame_timestep { - self.update_counter -= self.top.frame_timestep; + while self.update_counter >= self.menu_bar.frame_timestep { + self.update_counter -= self.menu_bar.frame_timestep; if !self.update_state() { return; @@ -200,7 +201,7 @@ impl eframe::App for App { /// Called each time the UI has to be repainted. fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { - self.top.update( + self.menu_bar.update( ctx, &self.translator, self.content_channel.0.clone(), @@ -214,10 +215,10 @@ impl eframe::App for App { self.advance_state_by_dt(ctx.input(|i| i.stable_dt)); - self.bottom + self.status_bar .update(ctx, &self.translator, &self.viewport, &self.maybe_activity); - if self.top.show_layer_manager { + if self.menu_bar.show_layer_manager { if let Some(ref mut layers) = self.maybe_layers { if let Some(invoker) = self.arc_mutex_maybe_invoker.lock().unwrap().as_ref() { layers.update(ctx, invoker.autorouter().board()); @@ -229,7 +230,7 @@ impl eframe::App for App { let _viewport_rect = self.viewport.update( ctx, - &self.top, + &self.menu_bar, &mut self.arc_mutex_maybe_invoker.lock().unwrap(), &mut self.maybe_activity, &mut self.maybe_overlay, diff --git a/src/bin/topola-egui/menu_bar.rs b/src/bin/topola-egui/menu_bar.rs index a930482..8aa194b 100644 --- a/src/bin/topola-egui/menu_bar.rs +++ b/src/bin/topola-egui/menu_bar.rs @@ -11,11 +11,12 @@ use topola::{ }, router::RouterOptions, specctra::{design::SpecctraDesign, mesadata::SpecctraMesadata}, + stepper::Abort, }; use crate::{ action::{Action, Switch, Trigger}, - activity::{ActivityStatus, ActivityWithStatus}, + activity::{ActivityStatus, ActivityStepperWithStatus}, app::execute, file_sender::FileSender, overlay::Overlay, @@ -61,7 +62,7 @@ impl MenuBar { content_sender: Sender, history_sender: Sender, arc_mutex_maybe_invoker: Arc>>>, - maybe_activity: &mut Option, + maybe_activity: &mut Option, viewport: &mut Viewport, maybe_overlay: &mut Option, maybe_design: &Option, @@ -91,20 +92,35 @@ impl MenuBar { egui::Modifiers::CTRL, egui::Key::Q, )); - let mut autoroute = Trigger::new(Action::new( - tr.text("action-autoroute"), + let mut undo = Trigger::new(Action::new( + tr.text("action-undo"), egui::Modifiers::CTRL, - egui::Key::A, + egui::Key::Z, + )); + let mut redo = Trigger::new(Action::new( + tr.text("action-redo"), + egui::Modifiers::CTRL, + egui::Key::Y, + )); + let mut abort = Trigger::new(Action::new( + tr.text("action-abort"), + egui::Modifiers::NONE, + egui::Key::Escape, + )); + let mut remove_bands = Trigger::new(Action::new( + tr.text("action-remove-bands"), + egui::Modifiers::NONE, + egui::Key::Delete, )); let mut place_via = Switch::new(Action::new( tr.text("action-place-via"), egui::Modifiers::CTRL, egui::Key::P, )); - let mut remove_bands = Trigger::new(Action::new( - tr.text("action-remove-bands"), - egui::Modifiers::NONE, - egui::Key::Delete, + let mut autoroute = Trigger::new(Action::new( + tr.text("action-autoroute"), + egui::Modifiers::CTRL, + egui::Key::A, )); let mut compare_detours = Trigger::new(Action::new( tr.text("action-compare-detours"), @@ -116,16 +132,6 @@ impl MenuBar { egui::Modifiers::NONE, egui::Key::Plus, )); - let mut undo = Trigger::new(Action::new( - tr.text("action-undo"), - egui::Modifiers::CTRL, - egui::Key::Z, - )); - let mut redo = Trigger::new(Action::new( - tr.text("action-redo"), - egui::Modifiers::CTRL, - egui::Key::Y, - )); egui::TopBottomPanel::top("menu_bar") .show(ctx, |ui| { @@ -153,6 +159,10 @@ impl MenuBar { ui.separator(); + abort.button(ctx, ui); + + ui.separator(); + remove_bands.button(ctx, ui); }); @@ -199,7 +209,10 @@ impl MenuBar { tr.text("presort-by-pairwise-detours"), ); ui.checkbox( - &mut self.autorouter_options.router_options.squeeze_through_under_bands, + &mut self + .autorouter_options + .router_options + .squeeze_through_under_bands, tr.text("squeeze-through-under-bands"), ); ui.checkbox( @@ -289,25 +302,20 @@ impl MenuBar { } } else if quit.consume_key_triggered(ctx, ui) { ctx.send_viewport_cmd(egui::ViewportCommand::Close); - } else if autoroute.consume_key_triggered(ctx, ui) { - if maybe_activity.as_mut().map_or(true, |activity| { - matches!(activity.maybe_status(), Some(ActivityStatus::Finished(..))) - }) { - if let (Some(invoker), Some(ref mut overlay)) = ( - arc_mutex_maybe_invoker.lock().unwrap().as_mut(), - maybe_overlay, - ) { - let selection = overlay.selection().clone(); - overlay.clear_selection(); - maybe_activity.insert(ActivityWithStatus::new_execution( - invoker.execute_stepper(Command::Autoroute( - selection.pin_selection, - self.autorouter_options, - ))?, - )); + } else if undo.consume_key_triggered(ctx, ui) { + if let Some(invoker) = arc_mutex_maybe_invoker.lock().unwrap().as_mut() { + invoker.undo(); + } + } else if redo.consume_key_triggered(ctx, ui) { + if let Some(invoker) = arc_mutex_maybe_invoker.lock().unwrap().as_mut() { + invoker.redo(); + } + } else if abort.consume_key_triggered(ctx, ui) { + if let Some(activity) = maybe_activity { + if let Some(invoker) = arc_mutex_maybe_invoker.lock().unwrap().as_mut() { + activity.abort(invoker); } } - } else if place_via.consume_key_enabled(ctx, ui, &mut self.is_placing_via) { } else if remove_bands.consume_key_triggered(ctx, ui) { if maybe_activity.as_mut().map_or(true, |activity| { matches!(activity.maybe_status(), Some(ActivityStatus::Finished(..))) @@ -318,13 +326,50 @@ impl MenuBar { ) { let selection = overlay.selection().clone(); overlay.clear_selection(); - maybe_activity.insert(ActivityWithStatus::new_execution( + maybe_activity.insert(ActivityStepperWithStatus::new_execution( invoker.execute_stepper(Command::RemoveBands( selection.band_selection, ))?, )); } } + } else if place_via.consume_key_enabled(ctx, ui, &mut self.is_placing_via) { + } else if autoroute.consume_key_triggered(ctx, ui) { + if maybe_activity.as_mut().map_or(true, |activity| { + matches!(activity.maybe_status(), Some(ActivityStatus::Finished(..))) + }) { + if let (Some(invoker), Some(ref mut overlay)) = ( + arc_mutex_maybe_invoker.lock().unwrap().as_mut(), + maybe_overlay, + ) { + let selection = overlay.selection().clone(); + overlay.clear_selection(); + maybe_activity.insert(ActivityStepperWithStatus::new_execution( + invoker.execute_stepper(Command::Autoroute( + selection.pin_selection, + self.autorouter_options, + ))?, + )); + } + } + } else if compare_detours.consume_key_triggered(ctx, ui) { + if maybe_activity.as_mut().map_or(true, |activity| { + matches!(activity.maybe_status(), Some(ActivityStatus::Finished(..))) + }) { + if let (Some(invoker), Some(ref mut overlay)) = ( + arc_mutex_maybe_invoker.lock().unwrap().as_mut(), + maybe_overlay, + ) { + let selection = overlay.selection().clone(); + overlay.clear_selection(); + maybe_activity.insert(ActivityStepperWithStatus::new_execution( + invoker.execute_stepper(Command::CompareDetours( + selection.pin_selection, + self.autorouter_options, + ))?, + )); + } + } } else if measure_length.consume_key_triggered(ctx, ui) { if maybe_activity.as_mut().map_or(true, |activity| { matches!(activity.maybe_status(), Some(ActivityStatus::Finished(..))) @@ -335,41 +380,14 @@ impl MenuBar { ) { let selection = overlay.selection().clone(); overlay.clear_selection(); - maybe_activity.insert(ActivityWithStatus::new_execution( + maybe_activity.insert(ActivityStepperWithStatus::new_execution( invoker.execute_stepper(Command::MeasureLength( selection.band_selection, ))?, )); } } - } else if undo.consume_key_triggered(ctx, ui) { - if let Some(invoker) = arc_mutex_maybe_invoker.lock().unwrap().as_mut() { - invoker.undo(); - } - } else if redo.consume_key_triggered(ctx, ui) { - if let Some(invoker) = arc_mutex_maybe_invoker.lock().unwrap().as_mut() { - invoker.redo(); - } - } else if compare_detours.consume_key_triggered(ctx, ui) { - if maybe_activity.as_mut().map_or(true, |activity| { - matches!(activity.maybe_status(), Some(ActivityStatus::Finished(..))) - }) { - if let (Some(invoker), Some(ref mut overlay)) = ( - arc_mutex_maybe_invoker.lock().unwrap().as_mut(), - maybe_overlay, - ) { - let selection = overlay.selection().clone(); - overlay.clear_selection(); - maybe_activity.insert(ActivityWithStatus::new_execution( - invoker.execute_stepper(Command::CompareDetours( - selection.pin_selection, - self.autorouter_options, - ))?, - )); - } - } } - Ok::<(), InvokerError>(()) }) .inner diff --git a/src/bin/topola-egui/status_bar.rs b/src/bin/topola-egui/status_bar.rs index f2996bc..fd9a1ea 100644 --- a/src/bin/topola-egui/status_bar.rs +++ b/src/bin/topola-egui/status_bar.rs @@ -1,5 +1,5 @@ use crate::{ - activity::{ActivityStatus, ActivityWithStatus}, + activity::{ActivityStatus, ActivityStepperWithStatus}, translator::Translator, viewport::Viewport, }; @@ -16,7 +16,7 @@ impl StatusBar { ctx: &egui::Context, tr: &Translator, viewport: &Viewport, - maybe_activity: &Option, + maybe_activity: &Option, ) { egui::TopBottomPanel::bottom("status_bar").show(ctx, |ui| { let latest_pos = viewport.transform.inverse() diff --git a/src/bin/topola-egui/viewport.rs b/src/bin/topola-egui/viewport.rs index 8061e85..1cd00fe 100644 --- a/src/bin/topola-egui/viewport.rs +++ b/src/bin/topola-egui/viewport.rs @@ -20,7 +20,7 @@ use topola::{ }; use crate::{ - activity::ActivityWithStatus, layers::Layers, menu_bar::MenuBar, overlay::Overlay, + activity::ActivityStepperWithStatus, layers::Layers, menu_bar::MenuBar, overlay::Overlay, painter::Painter, }; @@ -42,7 +42,7 @@ impl Viewport { ctx: &egui::Context, top: &MenuBar, maybe_invoker: &mut Option>, - maybe_activity: &mut Option, + maybe_activity: &mut Option, maybe_overlay: &mut Option, maybe_layers: &Option, ) -> egui::Rect { @@ -67,7 +67,7 @@ impl Viewport { ctx: &egui::Context, top: &MenuBar, maybe_invoker: &mut Option>, - maybe_activity: &mut Option, + maybe_activity: &mut Option, maybe_overlay: &mut Option, maybe_layers: &Option, ) -> egui::Rect {