egui: add button and key to abort execution

It actually finishes execution, as I haven't implemented actual aborting
yet.
This commit is contained in:
Mikolaj Wielgus 2024-10-02 02:28:43 +02:00
parent bfcae7f308
commit 9159312ea5
6 changed files with 131 additions and 99 deletions

View File

@ -22,6 +22,7 @@ action-measure-length = Measure Length
action-undo = Undo action-undo = Undo
action-redo = Redo action-redo = Redo
action-abort = Abort
presort-by-pairwise-detours = Presort by Pairwise Detours presort-by-pairwise-detours = Presort by Pairwise Detours
squeeze-through-under-bands = Squeeze through under Bands squeeze-through-under-bands = Squeeze through under Bands

View File

@ -7,6 +7,7 @@ use topola::{
InvokerStatus, InvokerStatus,
}, },
}, },
board::mesadata::AccessMesadata,
drawing::graph::PrimitiveIndex, drawing::graph::PrimitiveIndex,
geometry::primitive::PrimitiveShape, geometry::primitive::PrimitiveShape,
router::{navmesh::Navmesh, trace::TraceStepper}, router::{navmesh::Navmesh, trace::TraceStepper},
@ -105,52 +106,63 @@ impl GetObstacles for ActivityStepper {
} }
} }
pub struct ActivityWithStatus { pub struct ActivityStepperWithStatus {
activity: ActivityStepper, activity: ActivityStepper,
maybe_status: Option<ActivityStatus>, maybe_status: Option<ActivityStatus>,
} }
impl ActivityWithStatus { impl ActivityStepperWithStatus {
pub fn new_execution(execution: ExecutionStepper) -> ActivityWithStatus { pub fn new_execution(execution: ExecutionStepper) -> ActivityStepperWithStatus {
Self { Self {
activity: ActivityStepper::Execution(execution), activity: ActivityStepper::Execution(execution),
maybe_status: None, maybe_status: None,
} }
} }
pub fn step(
&mut self,
invoker: &mut Invoker<SpecctraMesadata>,
) -> Result<ActivityStatus, ActivityError> {
let status = self.activity.step(invoker)?;
self.maybe_status = Some(status.clone());
Ok(status.into())
}
pub fn maybe_status(&self) -> Option<ActivityStatus> { pub fn maybe_status(&self) -> Option<ActivityStatus> {
self.maybe_status.clone() self.maybe_status.clone()
} }
} }
impl GetMaybeNavmesh for ActivityWithStatus { impl Step<Invoker<SpecctraMesadata>, ActivityStatus, ActivityError, ()>
for ActivityStepperWithStatus
{
fn step(
&mut self,
invoker: &mut Invoker<SpecctraMesadata>,
) -> Result<ActivityStatus, ActivityError> {
let status = self.activity.step(invoker)?;
self.maybe_status = Some(status.clone());
Ok(status.into())
}
}
impl Abort<Invoker<SpecctraMesadata>> for ActivityStepperWithStatus {
fn abort(&mut self, invoker: &mut Invoker<SpecctraMesadata>) {
self.maybe_status = Some(ActivityStatus::Finished(String::from("aborted")));
self.activity.abort(invoker);
}
}
impl GetMaybeNavmesh for ActivityStepperWithStatus {
fn maybe_navmesh(&self) -> Option<&Navmesh> { fn maybe_navmesh(&self) -> Option<&Navmesh> {
self.activity.maybe_navmesh() self.activity.maybe_navmesh()
} }
} }
impl GetMaybeTrace for ActivityWithStatus { impl GetMaybeTrace for ActivityStepperWithStatus {
fn maybe_trace(&self) -> Option<&TraceStepper> { fn maybe_trace(&self) -> Option<&TraceStepper> {
self.activity.maybe_trace() self.activity.maybe_trace()
} }
} }
impl GetGhosts for ActivityWithStatus { impl GetGhosts for ActivityStepperWithStatus {
fn ghosts(&self) -> &[PrimitiveShape] { fn ghosts(&self) -> &[PrimitiveShape] {
self.activity.ghosts() self.activity.ghosts()
} }
} }
impl GetObstacles for ActivityWithStatus { impl GetObstacles for ActivityStepperWithStatus {
fn obstacles(&self) -> &[PrimitiveIndex] { fn obstacles(&self) -> &[PrimitiveIndex] {
self.activity.obstacles() self.activity.obstacles()
} }

View File

@ -11,10 +11,11 @@ use unic_langid::{langid, LanguageIdentifier};
use topola::{ use topola::{
autorouter::{invoker::Invoker, Autorouter}, autorouter::{invoker::Invoker, Autorouter},
specctra::{design::SpecctraDesign, mesadata::SpecctraMesadata}, specctra::{design::SpecctraDesign, mesadata::SpecctraMesadata},
stepper::Step,
}; };
use crate::{ use crate::{
activity::{ActivityStatus, ActivityWithStatus}, activity::{ActivityStatus, ActivityStepperWithStatus},
error_dialog::ErrorDialog, error_dialog::ErrorDialog,
file_receiver::FileReceiver, file_receiver::FileReceiver,
layers::Layers, layers::Layers,
@ -38,7 +39,7 @@ pub struct App {
arc_mutex_maybe_invoker: Arc<Mutex<Option<Invoker<SpecctraMesadata>>>>, arc_mutex_maybe_invoker: Arc<Mutex<Option<Invoker<SpecctraMesadata>>>>,
#[serde(skip)] #[serde(skip)]
maybe_activity: Option<ActivityWithStatus>, maybe_activity: Option<ActivityStepperWithStatus>,
#[serde(skip)] #[serde(skip)]
content_channel: (Sender<String>, Receiver<String>), content_channel: (Sender<String>, Receiver<String>),
@ -50,10 +51,10 @@ pub struct App {
viewport: Viewport, viewport: Viewport,
#[serde(skip)] #[serde(skip)]
top: MenuBar, menu_bar: MenuBar,
#[serde(skip)] #[serde(skip)]
bottom: StatusBar, status_bar: StatusBar,
#[serde(skip)] #[serde(skip)]
error_dialog: ErrorDialog, error_dialog: ErrorDialog,
@ -78,8 +79,8 @@ impl Default for App {
content_channel: channel(), content_channel: channel(),
history_channel: channel(), history_channel: channel(),
viewport: Viewport::new(), viewport: Viewport::new(),
top: MenuBar::new(), menu_bar: MenuBar::new(),
bottom: StatusBar::new(), status_bar: StatusBar::new(),
error_dialog: ErrorDialog::new(), error_dialog: ErrorDialog::new(),
maybe_layers: None, maybe_layers: None,
maybe_design: None, maybe_design: None,
@ -109,8 +110,8 @@ impl App {
fn advance_state_by_dt(&mut self, dt: f32) { fn advance_state_by_dt(&mut self, dt: f32) {
self.update_counter += dt; self.update_counter += dt;
while self.update_counter >= self.top.frame_timestep { while self.update_counter >= self.menu_bar.frame_timestep {
self.update_counter -= self.top.frame_timestep; self.update_counter -= self.menu_bar.frame_timestep;
if !self.update_state() { if !self.update_state() {
return; return;
@ -200,7 +201,7 @@ impl eframe::App for App {
/// Called each time the UI has to be repainted. /// Called each time the UI has to be repainted.
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
self.top.update( self.menu_bar.update(
ctx, ctx,
&self.translator, &self.translator,
self.content_channel.0.clone(), 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.advance_state_by_dt(ctx.input(|i| i.stable_dt));
self.bottom self.status_bar
.update(ctx, &self.translator, &self.viewport, &self.maybe_activity); .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(ref mut layers) = self.maybe_layers {
if let Some(invoker) = self.arc_mutex_maybe_invoker.lock().unwrap().as_ref() { if let Some(invoker) = self.arc_mutex_maybe_invoker.lock().unwrap().as_ref() {
layers.update(ctx, invoker.autorouter().board()); layers.update(ctx, invoker.autorouter().board());
@ -229,7 +230,7 @@ impl eframe::App for App {
let _viewport_rect = self.viewport.update( let _viewport_rect = self.viewport.update(
ctx, ctx,
&self.top, &self.menu_bar,
&mut self.arc_mutex_maybe_invoker.lock().unwrap(), &mut self.arc_mutex_maybe_invoker.lock().unwrap(),
&mut self.maybe_activity, &mut self.maybe_activity,
&mut self.maybe_overlay, &mut self.maybe_overlay,

View File

@ -11,11 +11,12 @@ use topola::{
}, },
router::RouterOptions, router::RouterOptions,
specctra::{design::SpecctraDesign, mesadata::SpecctraMesadata}, specctra::{design::SpecctraDesign, mesadata::SpecctraMesadata},
stepper::Abort,
}; };
use crate::{ use crate::{
action::{Action, Switch, Trigger}, action::{Action, Switch, Trigger},
activity::{ActivityStatus, ActivityWithStatus}, activity::{ActivityStatus, ActivityStepperWithStatus},
app::execute, app::execute,
file_sender::FileSender, file_sender::FileSender,
overlay::Overlay, overlay::Overlay,
@ -61,7 +62,7 @@ impl MenuBar {
content_sender: Sender<String>, content_sender: Sender<String>,
history_sender: Sender<String>, history_sender: Sender<String>,
arc_mutex_maybe_invoker: Arc<Mutex<Option<Invoker<SpecctraMesadata>>>>, arc_mutex_maybe_invoker: Arc<Mutex<Option<Invoker<SpecctraMesadata>>>>,
maybe_activity: &mut Option<ActivityWithStatus>, maybe_activity: &mut Option<ActivityStepperWithStatus>,
viewport: &mut Viewport, viewport: &mut Viewport,
maybe_overlay: &mut Option<Overlay>, maybe_overlay: &mut Option<Overlay>,
maybe_design: &Option<SpecctraDesign>, maybe_design: &Option<SpecctraDesign>,
@ -91,20 +92,35 @@ impl MenuBar {
egui::Modifiers::CTRL, egui::Modifiers::CTRL,
egui::Key::Q, egui::Key::Q,
)); ));
let mut autoroute = Trigger::new(Action::new( let mut undo = Trigger::new(Action::new(
tr.text("action-autoroute"), tr.text("action-undo"),
egui::Modifiers::CTRL, 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( let mut place_via = Switch::new(Action::new(
tr.text("action-place-via"), tr.text("action-place-via"),
egui::Modifiers::CTRL, egui::Modifiers::CTRL,
egui::Key::P, egui::Key::P,
)); ));
let mut remove_bands = Trigger::new(Action::new( let mut autoroute = Trigger::new(Action::new(
tr.text("action-remove-bands"), tr.text("action-autoroute"),
egui::Modifiers::NONE, egui::Modifiers::CTRL,
egui::Key::Delete, egui::Key::A,
)); ));
let mut compare_detours = Trigger::new(Action::new( let mut compare_detours = Trigger::new(Action::new(
tr.text("action-compare-detours"), tr.text("action-compare-detours"),
@ -116,16 +132,6 @@ impl MenuBar {
egui::Modifiers::NONE, egui::Modifiers::NONE,
egui::Key::Plus, 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") egui::TopBottomPanel::top("menu_bar")
.show(ctx, |ui| { .show(ctx, |ui| {
@ -153,6 +159,10 @@ impl MenuBar {
ui.separator(); ui.separator();
abort.button(ctx, ui);
ui.separator();
remove_bands.button(ctx, ui); remove_bands.button(ctx, ui);
}); });
@ -199,7 +209,10 @@ impl MenuBar {
tr.text("presort-by-pairwise-detours"), tr.text("presort-by-pairwise-detours"),
); );
ui.checkbox( 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"), tr.text("squeeze-through-under-bands"),
); );
ui.checkbox( ui.checkbox(
@ -289,25 +302,20 @@ impl MenuBar {
} }
} else if quit.consume_key_triggered(ctx, ui) { } else if quit.consume_key_triggered(ctx, ui) {
ctx.send_viewport_cmd(egui::ViewportCommand::Close); ctx.send_viewport_cmd(egui::ViewportCommand::Close);
} else if autoroute.consume_key_triggered(ctx, ui) { } else if undo.consume_key_triggered(ctx, ui) {
if maybe_activity.as_mut().map_or(true, |activity| { if let Some(invoker) = arc_mutex_maybe_invoker.lock().unwrap().as_mut() {
matches!(activity.maybe_status(), Some(ActivityStatus::Finished(..))) invoker.undo();
}) { }
if let (Some(invoker), Some(ref mut overlay)) = ( } else if redo.consume_key_triggered(ctx, ui) {
arc_mutex_maybe_invoker.lock().unwrap().as_mut(), if let Some(invoker) = arc_mutex_maybe_invoker.lock().unwrap().as_mut() {
maybe_overlay, invoker.redo();
) { }
let selection = overlay.selection().clone(); } else if abort.consume_key_triggered(ctx, ui) {
overlay.clear_selection(); if let Some(activity) = maybe_activity {
maybe_activity.insert(ActivityWithStatus::new_execution( if let Some(invoker) = arc_mutex_maybe_invoker.lock().unwrap().as_mut() {
invoker.execute_stepper(Command::Autoroute( activity.abort(invoker);
selection.pin_selection,
self.autorouter_options,
))?,
));
} }
} }
} else if place_via.consume_key_enabled(ctx, ui, &mut self.is_placing_via) {
} else if remove_bands.consume_key_triggered(ctx, ui) { } else if remove_bands.consume_key_triggered(ctx, ui) {
if maybe_activity.as_mut().map_or(true, |activity| { if maybe_activity.as_mut().map_or(true, |activity| {
matches!(activity.maybe_status(), Some(ActivityStatus::Finished(..))) matches!(activity.maybe_status(), Some(ActivityStatus::Finished(..)))
@ -318,13 +326,50 @@ impl MenuBar {
) { ) {
let selection = overlay.selection().clone(); let selection = overlay.selection().clone();
overlay.clear_selection(); overlay.clear_selection();
maybe_activity.insert(ActivityWithStatus::new_execution( maybe_activity.insert(ActivityStepperWithStatus::new_execution(
invoker.execute_stepper(Command::RemoveBands( invoker.execute_stepper(Command::RemoveBands(
selection.band_selection, 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) { } else if measure_length.consume_key_triggered(ctx, ui) {
if maybe_activity.as_mut().map_or(true, |activity| { if maybe_activity.as_mut().map_or(true, |activity| {
matches!(activity.maybe_status(), Some(ActivityStatus::Finished(..))) matches!(activity.maybe_status(), Some(ActivityStatus::Finished(..)))
@ -335,41 +380,14 @@ impl MenuBar {
) { ) {
let selection = overlay.selection().clone(); let selection = overlay.selection().clone();
overlay.clear_selection(); overlay.clear_selection();
maybe_activity.insert(ActivityWithStatus::new_execution( maybe_activity.insert(ActivityStepperWithStatus::new_execution(
invoker.execute_stepper(Command::MeasureLength( invoker.execute_stepper(Command::MeasureLength(
selection.band_selection, 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>(()) Ok::<(), InvokerError>(())
}) })
.inner .inner

View File

@ -1,5 +1,5 @@
use crate::{ use crate::{
activity::{ActivityStatus, ActivityWithStatus}, activity::{ActivityStatus, ActivityStepperWithStatus},
translator::Translator, translator::Translator,
viewport::Viewport, viewport::Viewport,
}; };
@ -16,7 +16,7 @@ impl StatusBar {
ctx: &egui::Context, ctx: &egui::Context,
tr: &Translator, tr: &Translator,
viewport: &Viewport, viewport: &Viewport,
maybe_activity: &Option<ActivityWithStatus>, maybe_activity: &Option<ActivityStepperWithStatus>,
) { ) {
egui::TopBottomPanel::bottom("status_bar").show(ctx, |ui| { egui::TopBottomPanel::bottom("status_bar").show(ctx, |ui| {
let latest_pos = viewport.transform.inverse() let latest_pos = viewport.transform.inverse()

View File

@ -20,7 +20,7 @@ use topola::{
}; };
use crate::{ use crate::{
activity::ActivityWithStatus, layers::Layers, menu_bar::MenuBar, overlay::Overlay, activity::ActivityStepperWithStatus, layers::Layers, menu_bar::MenuBar, overlay::Overlay,
painter::Painter, painter::Painter,
}; };
@ -42,7 +42,7 @@ impl Viewport {
ctx: &egui::Context, ctx: &egui::Context,
top: &MenuBar, top: &MenuBar,
maybe_invoker: &mut Option<Invoker<SpecctraMesadata>>, maybe_invoker: &mut Option<Invoker<SpecctraMesadata>>,
maybe_activity: &mut Option<ActivityWithStatus>, maybe_activity: &mut Option<ActivityStepperWithStatus>,
maybe_overlay: &mut Option<Overlay>, maybe_overlay: &mut Option<Overlay>,
maybe_layers: &Option<Layers>, maybe_layers: &Option<Layers>,
) -> egui::Rect { ) -> egui::Rect {
@ -67,7 +67,7 @@ impl Viewport {
ctx: &egui::Context, ctx: &egui::Context,
top: &MenuBar, top: &MenuBar,
maybe_invoker: &mut Option<Invoker<SpecctraMesadata>>, maybe_invoker: &mut Option<Invoker<SpecctraMesadata>>,
maybe_activity: &mut Option<ActivityWithStatus>, maybe_activity: &mut Option<ActivityStepperWithStatus>,
maybe_overlay: &mut Option<Overlay>, maybe_overlay: &mut Option<Overlay>,
maybe_layers: &Option<Layers>, maybe_layers: &Option<Layers>,
) -> egui::Rect { ) -> egui::Rect {