builder-based API for wizard choosing stuff. need to convert other

callers now.
This commit is contained in:
Dustin Carlino 2019-09-11 11:52:34 -07:00
parent 224461fcdc
commit aa3fed3395
8 changed files with 86 additions and 44 deletions

View File

@ -19,7 +19,7 @@ pub use crate::runner::{run, EventLoopMode, GUI};
pub use crate::screen_geom::{ScreenDims, ScreenPt};
pub use crate::text::{Line, Text, TextSpan, HOTKEY_COLOR};
pub use crate::widgets::{
Autocomplete, ItemSlider, ModalMenu, Scroller, Slider, SliderWithTextBox, Warper,
Autocomplete, Choice, ItemSlider, ModalMenu, Scroller, Slider, SliderWithTextBox, Warper,
WarpingItemSlider, Wizard, WrappedWizard,
};

View File

@ -378,17 +378,24 @@ impl<T: Clone> Menu<T> {
.collect()
}
// If there's no matching choice, be silent. The two callers don't care.
pub fn mark_active(&mut self, label: &str) {
pub fn mark_active(&mut self, label: &str, is_active: bool) {
for choice in self.choices.iter_mut() {
if choice.label == label {
if choice.active {
panic!("Menu choice for {} was already active", choice.label);
if choice.active == is_active {
panic!(
"Menu choice for {} already had active={}",
choice.label, is_active
);
}
choice.active = true;
choice.active = is_active;
return;
}
}
// TODO Fix a violation of this, then enable.
/*panic!(
"Menu with prompt {:?} has no choice {} to mark active",
self.prompt, label
);*/
}
pub fn mark_all_inactive(&mut self) {

View File

@ -16,4 +16,4 @@ pub(crate) use self::screenshot::{screenshot_current, screenshot_everything};
pub use self::scroller::Scroller;
pub use self::slider::{ItemSlider, Slider, SliderWithTextBox, WarpingItemSlider};
pub use self::warper::Warper;
pub use self::wizard::{Wizard, WrappedWizard};
pub use self::wizard::{Choice, Wizard, WrappedWizard};

View File

@ -72,7 +72,7 @@ impl ModalMenu {
return true;
}
} else {
self.menu.mark_active(name);
self.menu.mark_active(name, true);
}
false
}

View File

@ -1,7 +1,7 @@
use crate::widgets::log_scroller::LogScroller;
use crate::widgets::text_box::TextBox;
use crate::widgets::{Menu, Position};
use crate::{EventCtx, GfxCtx, InputResult, MultiKey, SliderWithTextBox, Text, UserInput};
use crate::{EventCtx, GfxCtx, InputResult, Key, MultiKey, SliderWithTextBox, Text, UserInput};
use abstutil::Cloneable;
use geom::Duration;
use std::collections::VecDeque;
@ -233,10 +233,7 @@ impl<'a, 'b> WrappedWizard<'a, 'b> {
)
}
pub fn choose_something_hotkeys<
R: 'static + Clone + Cloneable,
F: FnOnce() -> Vec<(Option<MultiKey>, String, R)>,
>(
pub fn choose<R: 'static + Clone + Cloneable, F: FnOnce() -> Vec<Choice<R>>>(
&mut self,
query: &str,
choices_generator: F,
@ -269,7 +266,7 @@ impl<'a, 'b> WrappedWizard<'a, 'b> {
}
if self.wizard.menu.is_none() {
let choices: Vec<(Option<MultiKey>, String, R)> = choices_generator();
let choices: Vec<Choice<R>> = choices_generator();
if choices.is_empty() {
self.wizard.log_scroller = Some(LogScroller::new(
"Wizard".to_string(),
@ -277,18 +274,26 @@ impl<'a, 'b> WrappedWizard<'a, 'b> {
));
return None;
}
let boxed_choices: Vec<(Option<MultiKey>, String, Box<dyn Cloneable>)> = choices
.into_iter()
.map(|(multikey, s, item)| (multikey, s, item.clone_box()))
.collect();
self.wizard.menu = Some(Menu::new(
let mut boxed_choices = Vec::new();
let mut inactive_labels = Vec::new();
for choice in choices {
if !choice.active {
inactive_labels.push(choice.label.clone());
}
boxed_choices.push((choice.hotkey, choice.label, choice.data.clone_box()));
}
let mut menu = Menu::new(
Text::prompt(query),
vec![boxed_choices],
true,
false,
Position::ScreenCenter,
self.ctx.canvas,
));
);
for label in inactive_labels {
menu.mark_active(&label, false);
}
self.wizard.menu = Some(menu);
}
assert!(self.wizard.alive);
@ -328,19 +333,19 @@ impl<'a, 'b> WrappedWizard<'a, 'b> {
query: &str,
choices_generator: F,
) -> Option<(String, R)> {
self.choose_something_hotkeys(query, || {
self.choose(query, || {
choices_generator()
.into_iter()
.map(|(s, data)| (None, s, data))
.map(|(s, data)| Choice::new(s, data))
.collect()
})
}
pub fn choose_str(&mut self, query: &str, choices: Vec<&str>) -> Option<String> {
self.choose_something_hotkeys(query, || {
self.choose(query, || {
choices
.into_iter()
.map(|s| (None, s.to_string(), ()))
.map(|s| Choice::new(s.to_string(), ()))
.collect()
})
.map(|(s, _)| s)
@ -351,10 +356,10 @@ impl<'a, 'b> WrappedWizard<'a, 'b> {
query: &str,
choices_generator: F,
) -> Option<String> {
self.choose_something_hotkeys(query, || {
self.choose(query, || {
choices_generator()
.into_iter()
.map(|s| (None, s, ()))
.map(|s| Choice::new(s, ()))
.collect()
})
.map(|(s, _)| s)
@ -396,3 +401,33 @@ impl<'a, 'b> WrappedWizard<'a, 'b> {
}
}
}
pub struct Choice<T: Clone> {
label: String,
data: T,
hotkey: Option<MultiKey>,
active: bool,
}
impl<T: Clone> Choice<T> {
pub fn new<S: Into<String>>(label: S, data: T) -> Choice<T> {
Choice {
label: label.into(),
data,
hotkey: None,
active: true,
}
}
pub fn key(mut self, key: Key) -> Choice<T> {
assert_eq!(self.hotkey, None);
self.hotkey = Some(MultiKey { key, lctrl: false });
self
}
pub fn inactive(mut self) -> Choice<T> {
assert!(self.active);
self.active = false;
self
}
}

View File

@ -2,7 +2,7 @@ use crate::common::Warping;
use crate::game::{State, Transition, WizardState};
use crate::ui::UI;
use abstutil::Cloneable;
use ezgui::{hotkey, EventCtx, EventLoopMode, Key, Wizard};
use ezgui::{Choice, EventCtx, EventLoopMode, Key, Wizard};
use geom::Pt2D;
use serde_derive::{Deserialize, Serialize};
@ -27,7 +27,7 @@ fn choose_shortcut(wiz: &mut Wizard, ctx: &mut EventCtx, ui: &mut UI) -> Option<
let cam_zoom = ctx.canvas.cam_zoom;
let mut wizard = wiz.wrap(ctx);
let (_, mut s) = wizard.choose_something_hotkeys("Jump to which shortcut?", || {
let (_, mut s) = wizard.choose("Jump to which shortcut?", || {
// TODO Handle >9
// TODO Allow deleting
let keys = vec![
@ -57,9 +57,9 @@ fn choose_shortcut(wiz: &mut Wizard, ctx: &mut EventCtx, ui: &mut UI) -> Option<
.enumerate()
.map(|(idx, s)| {
if idx == 0 {
(None, s.name.clone(), s)
Choice::new(s.name.clone(), s)
} else {
(hotkey(keys[idx - 1]), s.name.clone(), s)
Choice::new(s.name.clone(), s).key(keys[idx - 1])
}
})
.collect()

View File

@ -2,7 +2,7 @@ use crate::game::{State, Transition, WizardState};
use crate::ui::UI;
use abstutil::prettyprint_usize;
use ezgui::{
hotkey, Color, EventCtx, GfxCtx, HorizontalAlignment, Key, Line, ModalMenu, Text,
hotkey, Choice, Color, EventCtx, GfxCtx, HorizontalAlignment, Key, Line, ModalMenu, Text,
VerticalAlignment, Wizard,
};
use geom::{Duration, DurationHistogram};
@ -81,7 +81,7 @@ fn browse_trips(wiz: &mut Wizard, ctx: &mut EventCtx, ui: &mut UI) -> Option<Tra
("drive".to_string(), TripMode::Drive),
]
})?;
wizard.choose_something_hotkeys("Examine which trip?", || {
wizard.choose("Examine which trip?", || {
let trips = ui.primary.sim.get_finished_trips();
let mut filtered: Vec<&(TripID, TripMode, Duration)> = trips
.finished_trips
@ -93,7 +93,7 @@ fn browse_trips(wiz: &mut Wizard, ctx: &mut EventCtx, ui: &mut UI) -> Option<Tra
filtered
.into_iter()
// TODO Show percentile for time
.map(|(id, _, dt)| (None, format!("{} taking {}", id, dt), *id))
.map(|(id, _, dt)| Choice::new(format!("{} taking {}", id, dt), *id))
.collect()
})?;
// TODO show trip departure, where it started and ended

View File

@ -7,7 +7,7 @@ use crate::sandbox::SandboxMode;
use crate::tutorial::TutorialMode;
use crate::ui::UI;
use abstutil::elapsed_seconds;
use ezgui::{hotkey, Canvas, EventCtx, EventLoopMode, GfxCtx, Key, UserInput, Wizard};
use ezgui::{Canvas, Choice, EventCtx, EventLoopMode, GfxCtx, Key, UserInput, Wizard};
use geom::{Duration, Line, Pt2D, Speed};
use map_model::Map;
use rand::Rng;
@ -141,17 +141,17 @@ fn splash_screen(
// TODO No hotkey for quit because it's just the normal menu escape?
match wizard
.choose_something_hotkeys("Welcome to A/B Street!", || {
.choose("Welcome to A/B Street!", || {
vec![
(hotkey(Key::S), sandbox.to_string(), ()),
(hotkey(Key::L), load_map.to_string(), ()),
(hotkey(Key::E), edit.to_string(), ()),
(hotkey(Key::T), tutorial.to_string(), ()),
(hotkey(Key::D), debug.to_string(), ()),
(hotkey(Key::M), mission.to_string(), ()),
(hotkey(Key::A), abtest.to_string(), ()),
(None, about.to_string(), ()),
(None, quit.to_string(), ()),
Choice::new(sandbox, ()).key(Key::S),
Choice::new(load_map, ()).key(Key::L),
Choice::new(edit, ()).key(Key::E),
Choice::new(tutorial, ()).key(Key::T),
Choice::new(debug, ()).key(Key::D),
Choice::new(mission, ()).key(Key::M),
Choice::new(abtest, ()).key(Key::A),
Choice::new(about, ()),
Choice::new(quit, ()),
]
})?
.0