making wizard use the new Menu

This commit is contained in:
Dustin Carlino 2018-12-16 16:18:25 -08:00
parent 3a47cb04e9
commit 2a5384cf11
8 changed files with 52 additions and 25 deletions

View File

@ -27,7 +27,9 @@ impl Plugin for ABTestManager {
fn blocking_event(&mut self, ctx: &mut PluginCtx) -> bool {
match self {
ABTestManager::PickABTest(ref mut wizard) => {
if let Some(ab_test) = pick_ab_test(&ctx.primary.map, wizard.wrap(ctx.input)) {
if let Some(ab_test) =
pick_ab_test(&ctx.primary.map, wizard.wrap(ctx.input, ctx.canvas))
{
let scroller = LogScroller::new_from_lines(ab_test.describe());
*self = ABTestManager::ManageABTest(ab_test, scroller);
} else if wizard.aborted() {

View File

@ -42,7 +42,7 @@ impl Plugin for DrawNeighborhoodState {
match self {
DrawNeighborhoodState::PickNeighborhood(ref mut wizard) => {
if let Some(n) = pick_neighborhood(map, wizard.wrap(input)) {
if let Some(n) = pick_neighborhood(map, wizard.wrap(input, ctx.canvas)) {
*self = DrawNeighborhoodState::EditNeighborhood(n, None);
} else if wizard.aborted() {
return false;

View File

@ -32,7 +32,7 @@ impl Plugin for EditsManager {
&ctx.primary.map,
&mut new_primary,
&ctx.canvas,
self.wizard.wrap(ctx.input),
self.wizard.wrap(ctx.input, ctx.canvas),
)
.is_some()
{

View File

@ -31,7 +31,7 @@ impl Plugin for ScenarioManager {
match self {
ScenarioManager::PickScenario(ref mut wizard) => {
if let Some(scenario) = pick_scenario(map, wizard.wrap(input)) {
if let Some(scenario) = pick_scenario(map, wizard.wrap(input, ctx.canvas)) {
let scroller = LogScroller::new_from_lines(scenario.describe());
*self = ScenarioManager::ManageScenario(scenario, scroller);
} else if wizard.aborted() {
@ -54,7 +54,7 @@ impl Plugin for ScenarioManager {
}
}
ScenarioManager::EditScenario(ref mut scenario, ref mut wizard) => {
if let Some(()) = edit_scenario(map, scenario, wizard.wrap(input)) {
if let Some(()) = edit_scenario(map, scenario, wizard.wrap(input, ctx.canvas)) {
let scroller = LogScroller::new_from_lines(scenario.describe());
// TODO autosave, or at least make it clear there are unsaved edits
*self = ScenarioManager::ManageScenario(scenario.clone(), scroller);

View File

@ -66,7 +66,7 @@ impl Plugin for TrafficSignalEditor {
.cycle_duration_wizard
.as_mut()
.unwrap()
.wrap(input)
.wrap(input, ctx.canvas)
.input_usize_prefilled(
"How long should this cycle be?",
format!(
@ -88,7 +88,7 @@ impl Plugin for TrafficSignalEditor {
if let Some(new_signal) = choose_preset(
&ctx.primary.map,
self.i,
self.preset_wizard.as_mut().unwrap().wrap(input),
self.preset_wizard.as_mut().unwrap().wrap(input, ctx.canvas),
) {
ctx.primary.map.edit_traffic_signal(new_signal);
self.preset_wizard = None;

View File

@ -37,7 +37,7 @@ impl ContextMenu {
None,
actions
.into_iter()
.map(|(hotkey, action)| (hotkey, action, hotkey))
.map(|(hotkey, action)| (Some(hotkey), action, hotkey))
.collect(),
origin,
canvas,

View File

@ -4,7 +4,7 @@ use geom::{Polygon, Pt2D};
// Stores some associated data with each choice
pub struct Menu<T: Clone> {
prompt: Option<String>,
choices: Vec<(Key, String, T)>,
choices: Vec<(Option<Key>, String, T)>,
current_idx: Option<usize>,
origin: Pt2D,
@ -15,7 +15,7 @@ pub struct Menu<T: Clone> {
impl<T: Clone> Menu<T> {
pub fn new(
prompt: Option<String>,
choices: Vec<(Key, String, T)>,
choices: Vec<(Option<Key>, String, T)>,
origin: Pt2D,
canvas: &Canvas,
) -> Menu<T> {
@ -27,7 +27,11 @@ impl<T: Clone> Menu<T> {
let mut txt = Text::new();
// TODO prompt
for (hotkey, choice, _) in &choices {
txt.add_line(format!("{} - {}", hotkey.describe(), choice));
if let Some(key) = hotkey {
txt.add_line(format!("{} - {}", key.describe(), choice));
} else {
txt.add_line(choice.to_string());
}
}
let (screen_width, screen_height) = canvas.text_dims(&txt);
// Once a menu is created, all other controls (like zooming) are disabled, so this value
@ -113,8 +117,12 @@ impl<T: Clone> Menu<T> {
} else {
None
};
txt.add_styled_line(hotkey.describe(), Color::BLUE, bg);
txt.append(format!(" - {}", choice), text::TEXT_FG_COLOR, bg);
if let Some(key) = hotkey {
txt.add_styled_line(key.describe(), Color::BLUE, bg);
txt.append(format!(" - {}", choice), text::TEXT_FG_COLOR, bg);
} else {
txt.add_styled_line(choice.to_string(), text::TEXT_FG_COLOR, bg);
}
}
canvas.draw_text_at(g, txt, self.origin);
}

View File

@ -1,4 +1,5 @@
use crate::{Canvas, GfxCtx, InputResult, LogScroller, ScrollingMenu, TextBox, UserInput};
use crate::menu::Menu;
use crate::{Canvas, GfxCtx, InputResult, Key, LogScroller, TextBox, UserInput};
use abstutil::Cloneable;
use log::warn;
use std::collections::VecDeque;
@ -6,7 +7,7 @@ use std::collections::VecDeque;
pub struct Wizard {
alive: bool,
tb: Option<TextBox>,
menu: Option<ScrollingMenu<Box<Cloneable>>>,
menu: Option<Menu<Box<Cloneable>>>,
log_scroller: Option<LogScroller>,
// In the order of queries made
@ -36,13 +37,18 @@ impl Wizard {
}
}
pub fn wrap<'a>(&'a mut self, input: &'a mut UserInput) -> WrappedWizard<'a> {
pub fn wrap<'a>(
&'a mut self,
input: &'a mut UserInput,
canvas: &'a Canvas,
) -> WrappedWizard<'a> {
assert!(self.alive);
let ready_results = VecDeque::from(self.confirmed_state.clone());
WrappedWizard {
wizard: self,
input,
canvas,
ready_results,
}
}
@ -54,7 +60,7 @@ impl Wizard {
// The caller can ask for any type at any time
pub fn current_menu_choice<R: 'static + Cloneable>(&self) -> Option<&R> {
if let Some(ref menu) = self.menu {
let item: &R = menu.current_choice().as_any().downcast_ref::<R>()?;
let item: &R = menu.current_choice()?.as_any().downcast_ref::<R>()?;
return Some(item);
}
None
@ -102,6 +108,7 @@ impl Wizard {
pub struct WrappedWizard<'a> {
wizard: &'a mut Wizard,
input: &'a mut UserInput,
canvas: &'a Canvas,
// The downcasts are safe iff the queries made to the wizard are deterministic.
ready_results: VecDeque<Box<Cloneable>>,
@ -201,16 +208,24 @@ impl<'a> WrappedWizard<'a> {
)]));
return None;
}
let boxed_choices: Vec<(String, Box<Cloneable>)> = choices
let boxed_choices: Vec<(Option<Key>, String, Box<Cloneable>)> = choices
.iter()
.map(|(s, item)| (s.to_string(), item.clone_box()))
.map(|(s, item)| (None, s.to_string(), item.clone_box()))
.collect();
self.wizard.menu = Some(ScrollingMenu::new(query, boxed_choices));
self.wizard.menu = Some(Menu::new(
Some(query.to_string()),
boxed_choices,
self.canvas.center_to_map_pt(),
self.canvas,
));
}
if let Some((choice, item)) =
input_with_menu(&mut self.wizard.menu, &mut self.wizard.alive, self.input)
{
if let Some((choice, item)) = input_with_menu(
&mut self.wizard.menu,
&mut self.wizard.alive,
self.input,
self.canvas,
) {
self.wizard
.confirmed_state
.push(Box::new((choice.to_string(), item.clone())));
@ -234,9 +249,10 @@ impl<'a> WrappedWizard<'a> {
// The caller initializes the menu, if needed. Pass in Option that must be Some().
// Bit weird to be a free function, but need to borrow a different menu and also the alive bit.
fn input_with_menu<T: Clone>(
menu: &mut Option<ScrollingMenu<T>>,
menu: &mut Option<Menu<T>>,
alive: &mut bool,
input: &mut UserInput,
canvas: &Canvas,
) -> Option<(String, T)> {
assert!(*alive);
@ -245,7 +261,8 @@ fn input_with_menu<T: Clone>(
return None;
}
match menu.as_mut().unwrap().event(input) {
let ev = input.use_event_directly().unwrap();
match menu.as_mut().unwrap().event(ev, canvas) {
InputResult::Canceled => {
*menu = None;
*alive = false;