mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-01 02:33:54 +03:00
builder-based API for wizard choosing stuff. need to convert other
callers now.
This commit is contained in:
parent
224461fcdc
commit
aa3fed3395
@ -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,
|
||||
};
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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};
|
||||
|
@ -72,7 +72,7 @@ impl ModalMenu {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
self.menu.mark_active(name);
|
||||
self.menu.mark_active(name, true);
|
||||
}
|
||||
false
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user