From 8c7116af970e1f3d9041aa7d002c0f30cdc5972f Mon Sep 17 00:00:00 2001 From: Dustin Carlino Date: Wed, 7 Aug 2019 15:03:45 -0700 Subject: [PATCH] heavily revamping wizard callers... wow, what a huge difference! --- abstutil/src/io.rs | 8 +- editor/src/abtest/score.rs | 56 ++++++-------- editor/src/abtest/setup.rs | 59 ++++++--------- editor/src/common/shortcuts.rs | 57 +++++++------- editor/src/debug/bus_explorer.rs | 24 +++--- editor/src/debug/color_picker.rs | 5 +- editor/src/edit/mod.rs | 61 ++++++--------- editor/src/edit/traffic_signals.rs | 19 +---- editor/src/helpers.rs | 5 +- editor/src/mission/mod.rs | 11 +-- editor/src/mission/neighborhood.rs | 8 +- editor/src/mission/scenario.rs | 9 +-- editor/src/sandbox/mod.rs | 12 +-- editor/src/sandbox/score.rs | 19 ++--- editor/src/sandbox/spawner.rs | 24 +++--- editor/src/splash_screen.rs | 17 ++--- ezgui/src/widgets/wizard.rs | 117 ++++++----------------------- 17 files changed, 185 insertions(+), 326 deletions(-) diff --git a/abstutil/src/io.rs b/abstutil/src/io.rs index 37ebb759ec..4841cf6000 100644 --- a/abstutil/src/io.rs +++ b/abstutil/src/io.rs @@ -121,9 +121,9 @@ pub fn deserialize_multimap< } // Just list all things from a directory, return sorted by name, with file extension removed. -// Pretty hacky that we return a (String, String). Also hacky that map_name can be blank. ;) -pub fn list_all_objects(dir: &str, map_name: &str) -> Vec<(String, String)> { - let mut results: BTreeSet<(String, String)> = BTreeSet::new(); +// Hacky that map_name can be blank. ;) +pub fn list_all_objects(dir: &str, map_name: &str) -> Vec { + let mut results: BTreeSet = BTreeSet::new(); match std::fs::read_dir(format!("../data/{}/{}", dir, map_name)) { Ok(iter) => { for entry in iter { @@ -138,7 +138,7 @@ pub fn list_all_objects(dir: &str, map_name: &str) -> Vec<(String, String)> { .to_os_string() .into_string() .unwrap(); - results.insert((name.clone(), name)); + results.insert(name); } } Err(ref e) if e.kind() == ErrorKind::NotFound => {} diff --git a/editor/src/abtest/score.rs b/editor/src/abtest/score.rs index 5ed6a38f39..a72f71a345 100644 --- a/editor/src/abtest/score.rs +++ b/editor/src/abtest/score.rs @@ -105,38 +105,32 @@ impl State for Scoreboard { fn browse_trips(wiz: &mut Wizard, ctx: &mut EventCtx, ui: &mut UI) -> Option { let mut wizard = wiz.wrap(ctx); let mode = wizard - .choose_something_no_keys::( - "Browse which trips?", - Box::new(|| { - vec![ - ("walk".to_string(), TripMode::Walk), - ("bike".to_string(), TripMode::Bike), - ("transit".to_string(), TripMode::Transit), - ("drive".to_string(), TripMode::Drive), - ] - }), - )? + .choose_something_no_keys("Browse which trips?", || { + vec![ + ("walk".to_string(), TripMode::Walk), + ("bike".to_string(), TripMode::Bike), + ("transit".to_string(), TripMode::Transit), + ("drive".to_string(), TripMode::Drive), + ] + })? .1; - // TODO Ewwww. Can't do this inside choices_generator because trips isn't &'a static. - let trips = CompareTrips::new( - ui.primary.sim.get_finished_trips(), - ui.secondary.as_ref().unwrap().sim.get_finished_trips(), - ); - let mut filtered: Vec<&(TripID, TripMode, Duration, Duration)> = trips - .finished_trips - .iter() - .filter(|(_, m, t1, t2)| *m == mode && *t1 != *t2) - .collect(); - filtered.sort_by_key(|(_, _, t1, t2)| *t1 - *t2); - filtered.reverse(); - let choices: Vec<(String, TripID)> = filtered - .into_iter() - .map(|(id, _, t1, t2)| (format!("{} taking {} vs {}", id, t1, t2), *id)) - .collect(); - wizard.choose_something_no_keys::( - "Examine which trip?", - Box::new(move || choices.clone()), - )?; + wizard.choose_something_no_keys("Examine which trip?", || { + let trips = CompareTrips::new( + ui.primary.sim.get_finished_trips(), + ui.secondary.as_ref().unwrap().sim.get_finished_trips(), + ); + let mut filtered: Vec<&(TripID, TripMode, Duration, Duration)> = trips + .finished_trips + .iter() + .filter(|(_, m, t1, t2)| *m == mode && *t1 != *t2) + .collect(); + filtered.sort_by_key(|(_, _, t1, t2)| *t1 - *t2); + filtered.reverse(); + filtered + .into_iter() + .map(|(id, _, t1, t2)| (format!("{} taking {} vs {}", id, t1, t2), *id)) + .collect() + })?; // TODO show more details... Some(Transition::Pop) } diff --git a/editor/src/abtest/setup.rs b/editor/src/abtest/setup.rs index 05bec5e13c..20536e18c5 100644 --- a/editor/src/abtest/setup.rs +++ b/editor/src/abtest/setup.rs @@ -20,30 +20,29 @@ fn pick_ab_test(wiz: &mut Wizard, ctx: &mut EventCtx, ui: &mut UI) -> Option( - "Load which A/B test?", - Box::new(move || abstutil::load_all_objects(abstutil::AB_TESTS, &map_name)), - )? + .choose_something_no_keys("Load which A/B test?", || { + abstutil::load_all_objects(abstutil::AB_TESTS, ui.primary.map.get_name()) + })? .1 } else { let test_name = wizard.input_string("Name the A/B test")?; + let map_name = ui.primary.map.get_name(); let t = ABTest { test_name, - map_name: map_name.clone(), - scenario_name: choose_scenario(map_name.clone(), &mut wizard, "What scenario to run?")?, + map_name: map_name.to_string(), + scenario_name: choose_scenario(map_name, &mut wizard, "What scenario to run?")?, edits1_name: choose_edits( - map_name.clone(), + map_name, &mut wizard, "For the 1st run, what map edits to use?", )?, edits2_name: choose_edits( - map_name.clone(), + map_name, &mut wizard, "For the 2nd run, what map edits to use?", )?, @@ -232,39 +231,23 @@ fn launch_savestate(test: &ABTest, ss_path: String, ui: &mut UI, ctx: &mut Event ) } -fn choose_scenario(map_name: String, wizard: &mut WrappedWizard, query: &str) -> Option { - wizard - .choose_something_no_keys::( - query, - Box::new(move || abstutil::list_all_objects(abstutil::SCENARIOS, &map_name)), - ) - .map(|(n, _)| n) +fn choose_scenario(map_name: &str, wizard: &mut WrappedWizard, query: &str) -> Option { + wizard.choose_actual_string(query, || { + abstutil::list_all_objects(abstutil::SCENARIOS, map_name) + }) } -fn choose_edits(map_name: String, wizard: &mut WrappedWizard, query: &str) -> Option { - wizard - .choose_something_no_keys::( - query, - Box::new(move || { - let mut list = abstutil::list_all_objects("edits", &map_name); - list.push(("no_edits".to_string(), "no_edits".to_string())); - list - }), - ) - .map(|(n, _)| n) +fn choose_edits(map_name: &str, wizard: &mut WrappedWizard, query: &str) -> Option { + wizard.choose_actual_string(query, || { + let mut list = abstutil::list_all_objects("edits", map_name); + list.push("no_edits".to_string()); + list + }) } fn pick_savestate(test: &ABTest, wizard: &mut WrappedWizard) -> Option { let path = abstutil::path1(&test.map_name, abstutil::AB_TEST_SAVES, &test.test_name); - wizard - .choose_something_no_keys::<()>( - "Load which savestate?", - Box::new(move || { - abstutil::list_dir(std::path::Path::new(&path)) - .into_iter() - .map(|f| (f, ())) - .collect() - }), - ) - .map(|(f, _)| f) + wizard.choose_actual_string("Load which savestate?", || { + abstutil::list_dir(std::path::Path::new(&path)) + }) } diff --git a/editor/src/common/shortcuts.rs b/editor/src/common/shortcuts.rs index 552036a3f9..10a28117c2 100644 --- a/editor/src/common/shortcuts.rs +++ b/editor/src/common/shortcuts.rs @@ -2,9 +2,7 @@ use crate::common::warp::Warping; use crate::game::{State, Transition}; use crate::ui::UI; use abstutil::Cloneable; -use ezgui::{ - hotkey, EventCtx, EventLoopMode, GfxCtx, Key, MultiKey, Warper, Wizard, WrappedWizard, -}; +use ezgui::{hotkey, EventCtx, EventLoopMode, GfxCtx, Key, Warper, Wizard, WrappedWizard}; use geom::Pt2D; use serde_derive::{Deserialize, Serialize}; @@ -62,34 +60,33 @@ fn choose_shortcut( shortcuts: Vec, ui: &UI, ) -> Option { - // TODO Handle >9 - // TODO Allow deleting - let keys = vec![ - Key::Num1, - Key::Num2, - Key::Num3, - Key::Num4, - Key::Num5, - Key::Num6, - Key::Num7, - Key::Num8, - Key::Num9, - ]; + let (_, mut s) = wizard.new_choose_something("Jump to which shortcut?", || { + // TODO Handle >9 + // TODO Allow deleting + let keys = vec![ + Key::Num1, + Key::Num2, + Key::Num3, + Key::Num4, + Key::Num5, + Key::Num6, + Key::Num7, + Key::Num8, + Key::Num9, + ]; - let choices: Vec<(Option, String, Shortcut)> = shortcuts - .into_iter() - .enumerate() - .map(|(idx, s)| { - if idx == 0 { - (None, s.name.clone(), s) - } else { - (hotkey(keys[idx - 1]), s.name.clone(), s) - } - }) - .collect(); - - let (_, mut s) = - wizard.choose_something("Jump to which shortcut?", Box::new(move || choices.clone()))?; + shortcuts + .into_iter() + .enumerate() + .map(|(idx, s)| { + if idx == 0 { + (None, s.name.clone(), s) + } else { + (hotkey(keys[idx - 1]), s.name.clone(), s) + } + }) + .collect() + })?; if s.name == "Create a new shortcut here" { // TODO Enforce non-empty, unique names let name = wizard.input_string("Name this shortcut")?; diff --git a/editor/src/debug/bus_explorer.rs b/editor/src/debug/bus_explorer.rs index 521c952a9f..c0be73199c 100644 --- a/editor/src/debug/bus_explorer.rs +++ b/editor/src/debug/bus_explorer.rs @@ -108,20 +108,16 @@ impl BusRoutePicker { impl State for BusRoutePicker { fn event(&mut self, ctx: &mut EventCtx, ui: &mut UI) -> Transition { - // TODO Argh, constantly doing this. - let choices: Vec<(String, BusRouteID)> = self - .choices - .iter() - .map(|id| (ui.primary.map.get_br(*id).name.clone(), *id)) - .collect(); - - if let Some((_, id)) = self - .wizard - .wrap(ctx) - .choose_something_no_keys::( - "Explore which bus route?", - Box::new(move || choices.clone()), - ) + let choices = self.choices.clone(); + if let Some((_, id)) = + self.wizard + .wrap(ctx) + .choose_something_no_keys("Explore which bus route?", || { + choices + .into_iter() + .map(|id| (ui.primary.map.get_br(id).name.clone(), id)) + .collect() + }) { return Transition::Replace(Box::new(BusRouteExplorer::for_route( ui.primary.map.get_br(id), diff --git a/editor/src/debug/color_picker.rs b/editor/src/debug/color_picker.rs index e7dd76eebe..1a97dc7135 100644 --- a/editor/src/debug/color_picker.rs +++ b/editor/src/debug/color_picker.rs @@ -16,10 +16,9 @@ impl ColorChooser { } fn pick_color(wiz: &mut Wizard, ctx: &mut EventCtx, ui: &mut UI) -> Option { - let choices = ui.cs.color_names(); - let (name, _) = wiz + let name = wiz .wrap(ctx) - .choose_something_no_keys::<()>("Change which color?", Box::new(move || choices.clone()))?; + .choose_actual_string("Change which color?", || ui.cs.color_names())?; Some(Transition::Replace(Box::new(ColorChanger { name: name.clone(), original: ui.cs.get_modified(&name), diff --git a/editor/src/edit/mod.rs b/editor/src/edit/mod.rs index a94fd3a478..d5d94ce203 100644 --- a/editor/src/edit/mod.rs +++ b/editor/src/edit/mod.rs @@ -322,14 +322,11 @@ fn load_edits(wiz: &mut Wizard, ctx: &mut EventCtx, ui: &mut UI) -> Option( - "Load which map edits?", - Box::new(move || { - let mut list = abstutil::load_all_objects("edits", &map_name); - list.push(("no_edits".to_string(), MapEdits::new(map_name.clone()))); - list - }), - )?; + let (_, new_edits) = wizard.choose_something_no_keys("Load which map edits?", || { + let mut list = abstutil::load_all_objects("edits", &map_name); + list.push(("no_edits".to_string(), MapEdits::new(map_name.clone()))); + list + })?; apply_map_edits(&mut ui.primary, &ui.cs, ctx, new_edits); Some(Transition::Pop) } @@ -502,35 +499,25 @@ impl State for BulkEditLanes { } fn bulk_edit(r: RoadID, wizard: &mut WrappedWizard, map: &Map) -> Option { - let from = wizard - .choose_something( - "Change all lanes of type...", - Box::new(|| { - vec![ - (None, "driving".to_string(), LaneType::Driving), - (None, "parking".to_string(), LaneType::Parking), - (None, "biking".to_string(), LaneType::Biking), - (None, "bus".to_string(), LaneType::Bus), - ] - }), - )? - .1; - let to = wizard - .choose_something( - "Change to all lanes of type...", - Box::new(move || { - vec![ - (None, "driving".to_string(), LaneType::Driving), - (None, "parking".to_string(), LaneType::Parking), - (None, "biking".to_string(), LaneType::Biking), - (None, "bus".to_string(), LaneType::Bus), - ] - .into_iter() - .filter(|(_, _, lt)| *lt != from) - .collect() - }), - )? - .1; + let (_, from) = wizard.choose_something_no_keys("Change all lanes of type...", || { + vec![ + ("driving".to_string(), LaneType::Driving), + ("parking".to_string(), LaneType::Parking), + ("biking".to_string(), LaneType::Biking), + ("bus".to_string(), LaneType::Bus), + ] + })?; + let (_, to) = wizard.choose_something_no_keys("Change to all lanes of type...", || { + vec![ + ("driving".to_string(), LaneType::Driving), + ("parking".to_string(), LaneType::Parking), + ("biking".to_string(), LaneType::Biking), + ("bus".to_string(), LaneType::Bus), + ] + .into_iter() + .filter(|(_, lt)| *lt != from) + .collect() + })?; // Do the dirty deed. Match by road name; OSM way ID changes a fair bit. let road_name = map.get_r(r).get_name(); diff --git a/editor/src/edit/traffic_signals.rs b/editor/src/edit/traffic_signals.rs index abd48a82ef..23508698e1 100644 --- a/editor/src/edit/traffic_signals.rs +++ b/editor/src/edit/traffic_signals.rs @@ -5,9 +5,7 @@ use crate::helpers::ID; use crate::render::{draw_signal_cycle, DrawCtx, DrawOptions, DrawTurn, TrafficSignalDiagram}; use crate::ui::{ShowEverything, UI}; use abstutil::Timer; -use ezgui::{ - hotkey, Color, EventCtx, GeomBatch, GfxCtx, Key, ModalMenu, MultiKey, Wizard, WrappedWizard, -}; +use ezgui::{hotkey, Color, EventCtx, GeomBatch, GfxCtx, Key, ModalMenu, Wizard, WrappedWizard}; use geom::Duration; use map_model::{ControlTrafficSignal, Cycle, IntersectionID, Map, TurnID, TurnPriority, TurnType}; @@ -268,19 +266,10 @@ fn choose_preset( id: IntersectionID, mut wizard: WrappedWizard, ) -> Option { - // TODO I wanted to do all of this work just once per wizard, but we can't touch map inside a - // closure. Grr. - let choices: Vec<(Option, String, ControlTrafficSignal)> = - ControlTrafficSignal::get_possible_policies(map, id) - .into_iter() - .map(|(name, ts)| (None, name, ts)) - .collect(); - wizard - .choose_something::( - "Use which preset for this intersection?", - Box::new(move || choices.clone()), - ) + .choose_something_no_keys("Use which preset for this intersection?", || { + ControlTrafficSignal::get_possible_policies(map, id) + }) .map(|(_, ts)| ts) } diff --git a/editor/src/helpers.rs b/editor/src/helpers.rs index 5f1c8d1659..e2f27fd20d 100644 --- a/editor/src/helpers.rs +++ b/editor/src/helpers.rs @@ -105,9 +105,8 @@ impl ColorScheme { self.map[name] } - // Just for the color picker plugin, that's why the funky return value - pub fn color_names(&self) -> Vec<(String, ())> { - let mut names: Vec<(String, ())> = self.map.keys().map(|n| (n.clone(), ())).collect(); + pub fn color_names(&self) -> Vec { + let mut names: Vec = self.map.keys().map(|n| n.to_string()).collect(); names.sort(); names } diff --git a/editor/src/mission/mod.rs b/editor/src/mission/mod.rs index bb09ae2f78..859187525e 100644 --- a/editor/src/mission/mod.rs +++ b/editor/src/mission/mod.rs @@ -113,12 +113,13 @@ fn convert_trips_to_scenario( fn load_scenario(wiz: &mut Wizard, ctx: &mut EventCtx, ui: &mut UI) -> Option { let map_name = ui.primary.map.get_name().to_string(); - let (_, s) = wiz.wrap(ctx).choose_something_no_keys::( - "Load which scenario?", - Box::new(move || abstutil::list_all_objects(abstutil::SCENARIOS, &map_name)), - )?; + let s = wiz + .wrap(ctx) + .choose_actual_string("Load which scenario?", || { + abstutil::list_all_objects(abstutil::SCENARIOS, &map_name) + })?; let scenario = abstutil::read_binary( - &abstutil::path1_bin(ui.primary.map.get_name(), abstutil::SCENARIOS, &s), + &abstutil::path1_bin(&map_name, abstutil::SCENARIOS, &s), &mut Timer::throwaway(), ) .unwrap(); diff --git a/editor/src/mission/neighborhood.rs b/editor/src/mission/neighborhood.rs index 5054bcbc83..4edfe772c9 100644 --- a/editor/src/mission/neighborhood.rs +++ b/editor/src/mission/neighborhood.rs @@ -191,11 +191,9 @@ fn load_neighborhood_builder( wizard: &mut WrappedWizard, query: &str, ) -> Option { - let map_name = map.get_name().to_string(); wizard - .choose_something_no_keys::( - query, - Box::new(move || abstutil::load_all_objects(abstutil::NEIGHBORHOODS, &map_name)), - ) + .choose_something_no_keys(query, || { + abstutil::load_all_objects(abstutil::NEIGHBORHOODS, &map.get_name()) + }) .map(|(_, n)| n) } diff --git a/editor/src/mission/scenario.rs b/editor/src/mission/scenario.rs index 44d8acc7f9..7cd48c245d 100644 --- a/editor/src/mission/scenario.rs +++ b/editor/src/mission/scenario.rs @@ -186,14 +186,11 @@ fn edit_scenario(map: &Map, scenario: &mut Scenario, mut wizard: WrappedWizard) } fn choose_neighborhood(map: &Map, wizard: &mut WrappedWizard, query: &str) -> Option { - let map_name = map.get_name().to_string(); - let gps_bounds = map.get_gps_bounds().clone(); // Load the full object, since we usually visualize the neighborhood when menuing over it wizard - .choose_something_no_keys::( - query, - Box::new(move || Neighborhood::load_all(&map_name, &gps_bounds)), - ) + .choose_something_no_keys(query, || { + Neighborhood::load_all(map.get_name(), map.get_gps_bounds()) + }) .map(|(n, _)| n) } diff --git a/editor/src/sandbox/mod.rs b/editor/src/sandbox/mod.rs index 6949a77423..e9177253e4 100644 --- a/editor/src/sandbox/mod.rs +++ b/editor/src/sandbox/mod.rs @@ -211,15 +211,11 @@ impl State for SandboxMode { fn load_savestate(wiz: &mut Wizard, ctx: &mut EventCtx, ui: &mut UI) -> Option { let path = ui.primary.sim.save_dir(); - let (ss, _) = wiz.wrap(ctx).choose_something_no_keys::<()>( - "Load which savestate?", - Box::new(move || { + let ss = wiz + .wrap(ctx) + .choose_actual_string("Load which savestate?", || { abstutil::list_dir(std::path::Path::new(&path)) - .into_iter() - .map(|f| (f, ())) - .collect() - }), - )?; + })?; ctx.loading_screen("load savestate", |ctx, mut timer| { ui.primary.sim = Sim::load_savestate(ss, &mut timer).expect("Can't load savestate"); diff --git a/editor/src/sandbox/score.rs b/editor/src/sandbox/score.rs index 3a58dc99a4..69da8e0a12 100644 --- a/editor/src/sandbox/score.rs +++ b/editor/src/sandbox/score.rs @@ -68,17 +68,14 @@ impl State for Scoreboard { fn browse_trips(wiz: &mut Wizard, ctx: &mut EventCtx, ui: &mut UI) -> Option { let mut wizard = wiz.wrap(ctx); - let (_, mode) = wizard.choose_something_no_keys::( - "Browse which trips?", - Box::new(|| { - vec![ - ("walk".to_string(), TripMode::Walk), - ("bike".to_string(), TripMode::Bike), - ("transit".to_string(), TripMode::Transit), - ("drive".to_string(), TripMode::Drive), - ] - }), - )?; + let (_, mode) = wizard.choose_something_no_keys("Browse which trips?", || { + vec![ + ("walk".to_string(), TripMode::Walk), + ("bike".to_string(), TripMode::Bike), + ("transit".to_string(), TripMode::Transit), + ("drive".to_string(), TripMode::Drive), + ] + })?; wizard.new_choose_something("Examine which trip?", || { let trips = ui.primary.sim.get_finished_trips(); let mut filtered: Vec<&(TripID, TripMode, Duration)> = trips diff --git a/editor/src/sandbox/spawner.rs b/editor/src/sandbox/spawner.rs index 99ff5c0f4e..0485bc82ac 100644 --- a/editor/src/sandbox/spawner.rs +++ b/editor/src/sandbox/spawner.rs @@ -363,21 +363,19 @@ fn instantiate_scenario(wiz: &mut Wizard, ctx: &mut EventCtx, ui: &mut UI) -> Op "random scenario with some agents".to_string() }; let map = &ui.primary.map; - let map_name = map.get_name().to_string(); - let (_, scenario_name) = wiz.wrap(ctx).choose_something_no_keys::( - "Instantiate which scenario?", - Box::new(move || { - let mut list = vec![ - (builtin.clone(), "builtin".to_string()), - ("just buses".to_string(), "just buses".to_string()), - ]; - list.extend(abstutil::list_all_objects(abstutil::SCENARIOS, &map_name)); - list - }), - )?; + let scenario_name = + wiz.wrap(ctx) + .choose_actual_string("Instantiate which scenario?", || { + let mut list = vec![builtin.clone(), "just buses".to_string()]; + list.extend(abstutil::list_all_objects( + abstutil::SCENARIOS, + map.get_name(), + )); + list + })?; - let scenario = if scenario_name == "builtin" { + let scenario = if scenario_name == builtin { if let Some(n) = num_agents { Scenario::scaled_run(map, n) } else { diff --git a/editor/src/splash_screen.rs b/editor/src/splash_screen.rs index 33f06ab7b2..93f4ac95cf 100644 --- a/editor/src/splash_screen.rs +++ b/editor/src/splash_screen.rs @@ -159,16 +159,13 @@ fn splash_screen( { x if x == sandbox => Some(Transition::Push(Box::new(SandboxMode::new(ctx)))), x if x == load_map => { - let current_map = ui.primary.map.get_name().to_string(); - if let Some((name, _)) = wizard.choose_something_no_keys::( - "Load which map?", - Box::new(move || { - abstutil::list_all_objects("maps", "") - .into_iter() - .filter(|(n, _)| n != ¤t_map) - .collect() - }), - ) { + if let Some(name) = wizard.choose_actual_string("Load which map?", || { + let current_map = ui.primary.map.get_name(); + abstutil::list_all_objects("maps", "") + .into_iter() + .filter(|n| n != current_map) + .collect() + }) { ui.save_editor_state(ctx.canvas); // This retains no state, but that's probably fine. let mut flags = ui.primary.current_flags.clone(); diff --git a/ezgui/src/widgets/wizard.rs b/ezgui/src/widgets/wizard.rs index 8a47329528..fbcdaac554 100644 --- a/ezgui/src/widgets/wizard.rs +++ b/ezgui/src/widgets/wizard.rs @@ -324,107 +324,24 @@ impl<'a, 'b> WrappedWizard<'a, 'b> { } } - pub fn choose_something( + // TODO rename choose_something and choose_something_hotkeys + pub fn choose_something_no_keys< + R: 'static + Clone + Cloneable, + F: FnOnce() -> Vec<(String, R)>, + >( &mut self, query: &str, - choices_generator: Box Vec<(Option, String, R)>>, + choices_generator: F, ) -> Option<(String, R)> { - if !self.ready_results.is_empty() { - let first = self.ready_results.pop_front().unwrap(); - // We have to downcast twice! \o/ - let pair: &(String, Box) = first - .as_any() - .downcast_ref::<(String, Box)>() - .unwrap(); - let item: &R = pair.1.as_any().downcast_ref::().unwrap(); - return Some((pair.0.to_string(), item.clone())); - } - - // If the menu was empty, wait for the user to acknowledge the text-box before aborting the - // wizard. - if self.wizard.log_scroller.is_some() { - if self - .wizard - .log_scroller - .as_mut() - .unwrap() - .event(self.ctx.input) - { - self.wizard.log_scroller = None; - self.wizard.alive = false; - } - return None; - } - - if self.wizard.menu.is_none() { - let choices: Vec<(Option, String, R)> = choices_generator(); - if choices.is_empty() { - self.wizard.log_scroller = Some(LogScroller::new( - "Wizard".to_string(), - vec![format!("No choices for \"{}\", canceling wizard", query)], - )); - return None; - } - let boxed_choices: Vec<(Option, String, Box)> = choices - .into_iter() - .map(|(multikey, s, item)| (multikey, s, item.clone_box())) - .collect(); - self.wizard.menu = Some(Menu::new( - Text::prompt(query), - vec![boxed_choices], - true, - false, - Position::ScreenCenter, - self.ctx.canvas, - )); - } - - assert!(self.wizard.alive); - - // Otherwise, we try to use one event for two inputs potentially - if self.ctx.input.has_been_consumed() { - return None; - } - - let ev = self.ctx.input.use_event_directly().unwrap(); - match self - .wizard - .menu - .as_mut() - .unwrap() - .event(ev, self.ctx.canvas) - { - InputResult::Canceled => { - self.wizard.menu = None; - self.wizard.alive = false; - None - } - InputResult::StillActive => None, - InputResult::Done(choice, item) => { - self.wizard.menu = None; - self.wizard - .confirmed_state - .push(Box::new((choice.to_string(), item.clone()))); - let downcasted_item: &R = item.as_any().downcast_ref::().unwrap(); - Some((choice, downcasted_item.clone())) - } - } - } - - pub fn choose_something_no_keys( - &mut self, - query: &str, - choices_generator: Box Vec<(String, R)>>, - ) -> Option<(String, R)> { - let wrapped_generator = Box::new(move || { + self.new_choose_something(query, || { choices_generator() .into_iter() - .map(|(name, data)| (None, name, data)) + .map(|(s, data)| (None, s, data)) .collect() - }); - self.choose_something(query, wrapped_generator) + }) } + // TODO rename choose_str pub fn choose_string(&mut self, query: &str, choices: Vec<&str>) -> Option { self.new_choose_something(query, || { choices @@ -435,6 +352,20 @@ impl<'a, 'b> WrappedWizard<'a, 'b> { .map(|(s, _)| s) } + pub fn choose_actual_string Vec>( + &mut self, + query: &str, + choices_generator: F, + ) -> Option { + self.new_choose_something(query, || { + choices_generator() + .into_iter() + .map(|s| (None, s, ())) + .collect() + }) + .map(|(s, _)| s) + } + pub fn choose_string_hotkeys( &mut self, query: &str,