mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-25 23:43:25 +03:00
heavily revamping wizard callers... wow, what a huge difference!
This commit is contained in:
parent
3b1671fdcb
commit
8c7116af97
@ -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<String> {
|
||||
let mut results: BTreeSet<String> = 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 => {}
|
||||
|
@ -105,19 +105,16 @@ impl State for Scoreboard {
|
||||
fn browse_trips(wiz: &mut Wizard, ctx: &mut EventCtx, ui: &mut UI) -> Option<Transition> {
|
||||
let mut wizard = wiz.wrap(ctx);
|
||||
let mode = wizard
|
||||
.choose_something_no_keys::<TripMode>(
|
||||
"Browse which trips?",
|
||||
Box::new(|| {
|
||||
.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.
|
||||
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(),
|
||||
@ -129,14 +126,11 @@ fn browse_trips(wiz: &mut Wizard, ctx: &mut EventCtx, ui: &mut UI) -> Option<Tra
|
||||
.collect();
|
||||
filtered.sort_by_key(|(_, _, t1, t2)| *t1 - *t2);
|
||||
filtered.reverse();
|
||||
let choices: Vec<(String, TripID)> = filtered
|
||||
filtered
|
||||
.into_iter()
|
||||
.map(|(id, _, t1, t2)| (format!("{} taking {} vs {}", id, t1, t2), *id))
|
||||
.collect();
|
||||
wizard.choose_something_no_keys::<TripID>(
|
||||
"Examine which trip?",
|
||||
Box::new(move || choices.clone()),
|
||||
)?;
|
||||
.collect()
|
||||
})?;
|
||||
// TODO show more details...
|
||||
Some(Transition::Pop)
|
||||
}
|
||||
|
@ -20,30 +20,29 @@ fn pick_ab_test(wiz: &mut Wizard, ctx: &mut EventCtx, ui: &mut UI) -> Option<Tra
|
||||
let mut wizard = wiz.wrap(ctx);
|
||||
let load_existing = "Load existing A/B test";
|
||||
let create_new = "Create new A/B test";
|
||||
let map_name = ui.primary.map.get_name().to_string();
|
||||
let ab_test = if wizard
|
||||
.choose_string("What A/B test to manage?", vec![load_existing, create_new])?
|
||||
== load_existing
|
||||
{
|
||||
wizard
|
||||
.choose_something_no_keys::<ABTest>(
|
||||
"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<String> {
|
||||
wizard
|
||||
.choose_something_no_keys::<String>(
|
||||
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<String> {
|
||||
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<String> {
|
||||
wizard
|
||||
.choose_something_no_keys::<String>(
|
||||
query,
|
||||
Box::new(move || {
|
||||
let mut list = abstutil::list_all_objects("edits", &map_name);
|
||||
list.push(("no_edits".to_string(), "no_edits".to_string()));
|
||||
fn choose_edits(map_name: &str, wizard: &mut WrappedWizard, query: &str) -> Option<String> {
|
||||
wizard.choose_actual_string(query, || {
|
||||
let mut list = abstutil::list_all_objects("edits", map_name);
|
||||
list.push("no_edits".to_string());
|
||||
list
|
||||
}),
|
||||
)
|
||||
.map(|(n, _)| n)
|
||||
})
|
||||
}
|
||||
|
||||
fn pick_savestate(test: &ABTest, wizard: &mut WrappedWizard) -> Option<String> {
|
||||
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 || {
|
||||
wizard.choose_actual_string("Load which savestate?", || {
|
||||
abstutil::list_dir(std::path::Path::new(&path))
|
||||
.into_iter()
|
||||
.map(|f| (f, ()))
|
||||
.collect()
|
||||
}),
|
||||
)
|
||||
.map(|(f, _)| f)
|
||||
})
|
||||
}
|
||||
|
@ -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,6 +60,7 @@ fn choose_shortcut(
|
||||
shortcuts: Vec<Shortcut>,
|
||||
ui: &UI,
|
||||
) -> Option<Shortcut> {
|
||||
let (_, mut s) = wizard.new_choose_something("Jump to which shortcut?", || {
|
||||
// TODO Handle >9
|
||||
// TODO Allow deleting
|
||||
let keys = vec![
|
||||
@ -76,7 +75,7 @@ fn choose_shortcut(
|
||||
Key::Num9,
|
||||
];
|
||||
|
||||
let choices: Vec<(Option<MultiKey>, String, Shortcut)> = shortcuts
|
||||
shortcuts
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(idx, s)| {
|
||||
@ -86,10 +85,8 @@ fn choose_shortcut(
|
||||
(hotkey(keys[idx - 1]), s.name.clone(), s)
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
let (_, mut s) =
|
||||
wizard.choose_something("Jump to which shortcut?", Box::new(move || choices.clone()))?;
|
||||
.collect()
|
||||
})?;
|
||||
if s.name == "Create a new shortcut here" {
|
||||
// TODO Enforce non-empty, unique names
|
||||
let name = wizard.input_string("Name this shortcut")?;
|
||||
|
@ -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
|
||||
let choices = self.choices.clone();
|
||||
if let Some((_, id)) =
|
||||
self.wizard
|
||||
.wrap(ctx)
|
||||
.choose_something_no_keys::<BusRouteID>(
|
||||
"Explore which bus route?",
|
||||
Box::new(move || choices.clone()),
|
||||
)
|
||||
.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),
|
||||
|
@ -16,10 +16,9 @@ impl ColorChooser {
|
||||
}
|
||||
|
||||
fn pick_color(wiz: &mut Wizard, ctx: &mut EventCtx, ui: &mut UI) -> Option<Transition> {
|
||||
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),
|
||||
|
@ -322,14 +322,11 @@ fn load_edits(wiz: &mut Wizard, ctx: &mut EventCtx, ui: &mut UI) -> Option<Trans
|
||||
|
||||
// TODO Exclude current
|
||||
let map_name = map.get_name().to_string();
|
||||
let (_, new_edits) = wizard.choose_something_no_keys::<MapEdits>(
|
||||
"Load which map edits?",
|
||||
Box::new(move || {
|
||||
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<MapEdits> {
|
||||
let from = wizard
|
||||
.choose_something(
|
||||
"Change all lanes of type...",
|
||||
Box::new(|| {
|
||||
let (_, from) = wizard.choose_something_no_keys("Change all lanes of type...", || {
|
||||
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),
|
||||
("driving".to_string(), LaneType::Driving),
|
||||
("parking".to_string(), LaneType::Parking),
|
||||
("biking".to_string(), LaneType::Biking),
|
||||
("bus".to_string(), LaneType::Bus),
|
||||
]
|
||||
}),
|
||||
)?
|
||||
.1;
|
||||
let to = wizard
|
||||
.choose_something(
|
||||
"Change to all lanes of type...",
|
||||
Box::new(move || {
|
||||
})?;
|
||||
let (_, to) = wizard.choose_something_no_keys("Change to all lanes of type...", || {
|
||||
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),
|
||||
("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)
|
||||
.filter(|(_, lt)| *lt != from)
|
||||
.collect()
|
||||
}),
|
||||
)?
|
||||
.1;
|
||||
})?;
|
||||
|
||||
// Do the dirty deed. Match by road name; OSM way ID changes a fair bit.
|
||||
let road_name = map.get_r(r).get_name();
|
||||
|
@ -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<ControlTrafficSignal> {
|
||||
// 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<MultiKey>, String, ControlTrafficSignal)> =
|
||||
ControlTrafficSignal::get_possible_policies(map, id)
|
||||
.into_iter()
|
||||
.map(|(name, ts)| (None, name, ts))
|
||||
.collect();
|
||||
|
||||
wizard
|
||||
.choose_something::<ControlTrafficSignal>(
|
||||
"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)
|
||||
}
|
||||
|
||||
|
@ -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<String> {
|
||||
let mut names: Vec<String> = self.map.keys().map(|n| n.to_string()).collect();
|
||||
names.sort();
|
||||
names
|
||||
}
|
||||
|
@ -113,12 +113,13 @@ fn convert_trips_to_scenario(
|
||||
|
||||
fn load_scenario(wiz: &mut Wizard, ctx: &mut EventCtx, ui: &mut UI) -> Option<Transition> {
|
||||
let map_name = ui.primary.map.get_name().to_string();
|
||||
let (_, s) = wiz.wrap(ctx).choose_something_no_keys::<String>(
|
||||
"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();
|
||||
|
@ -191,11 +191,9 @@ fn load_neighborhood_builder(
|
||||
wizard: &mut WrappedWizard,
|
||||
query: &str,
|
||||
) -> Option<NeighborhoodBuilder> {
|
||||
let map_name = map.get_name().to_string();
|
||||
wizard
|
||||
.choose_something_no_keys::<NeighborhoodBuilder>(
|
||||
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)
|
||||
}
|
||||
|
@ -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<String> {
|
||||
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::<Neighborhood>(
|
||||
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)
|
||||
}
|
||||
|
||||
|
@ -211,15 +211,11 @@ impl State for SandboxMode {
|
||||
fn load_savestate(wiz: &mut Wizard, ctx: &mut EventCtx, ui: &mut UI) -> Option<Transition> {
|
||||
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");
|
||||
|
@ -68,17 +68,14 @@ impl State for Scoreboard {
|
||||
|
||||
fn browse_trips(wiz: &mut Wizard, ctx: &mut EventCtx, ui: &mut UI) -> Option<Transition> {
|
||||
let mut wizard = wiz.wrap(ctx);
|
||||
let (_, mode) = wizard.choose_something_no_keys::<TripMode>(
|
||||
"Browse which trips?",
|
||||
Box::new(|| {
|
||||
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
|
||||
|
@ -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::<String>(
|
||||
"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));
|
||||
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 {
|
||||
|
@ -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::<String>(
|
||||
"Load which map?",
|
||||
Box::new(move || {
|
||||
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 != ¤t_map)
|
||||
.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();
|
||||
|
@ -324,107 +324,24 @@ impl<'a, 'b> WrappedWizard<'a, 'b> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn choose_something<R: 'static + Clone + Cloneable>(
|
||||
// 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<Fn() -> Vec<(Option<MultiKey>, 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<Cloneable>) = first
|
||||
.as_any()
|
||||
.downcast_ref::<(String, Box<Cloneable>)>()
|
||||
.unwrap();
|
||||
let item: &R = pair.1.as_any().downcast_ref::<R>().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<MultiKey>, 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<MultiKey>, String, Box<Cloneable>)> = 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::<R>().unwrap();
|
||||
Some((choice, downcasted_item.clone()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn choose_something_no_keys<R: 'static + Clone + Cloneable>(
|
||||
&mut self,
|
||||
query: &str,
|
||||
choices_generator: Box<Fn() -> 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<String> {
|
||||
self.new_choose_something(query, || {
|
||||
choices
|
||||
@ -435,6 +352,20 @@ impl<'a, 'b> WrappedWizard<'a, 'b> {
|
||||
.map(|(s, _)| s)
|
||||
}
|
||||
|
||||
pub fn choose_actual_string<F: Fn() -> Vec<String>>(
|
||||
&mut self,
|
||||
query: &str,
|
||||
choices_generator: F,
|
||||
) -> Option<String> {
|
||||
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,
|
||||
|
Loading…
Reference in New Issue
Block a user