using popup text box in a few more places to show important messages

This commit is contained in:
Dustin Carlino 2019-11-01 11:33:15 -07:00
parent b6dee0b35d
commit e9e6664f61
9 changed files with 96 additions and 70 deletions

View File

@ -349,15 +349,14 @@ impl<'a, 'b> WrappedWizard<'a, 'b> {
self.wizard.alive = false; self.wizard.alive = false;
} }
// Note this will abort the wizard once done!
pub fn acknowledge<S: Into<String>, F: Fn() -> Vec<S>>( pub fn acknowledge<S: Into<String>, F: Fn() -> Vec<S>>(
&mut self, &mut self,
title: &str, title: &str,
make_lines: F, make_lines: F,
) -> bool { ) -> Option<()> {
if !self.ready_results.is_empty() { if !self.ready_results.is_empty() {
self.ready_results.pop_front(); self.ready_results.pop_front();
return true; return Some(());
} }
if self.wizard.log_scroller.is_none() { if self.wizard.log_scroller.is_none() {
@ -375,9 +374,9 @@ impl<'a, 'b> WrappedWizard<'a, 'b> {
{ {
self.wizard.confirmed_state.push(Box::new(())); self.wizard.confirmed_state.push(Box::new(()));
self.wizard.log_scroller = None; self.wizard.log_scroller = None;
true Some(())
} else { } else {
false None
} }
} }
} }

View File

@ -1,6 +1,6 @@
use crate::common::route_viewer::RouteViewer; use crate::common::route_viewer::RouteViewer;
use crate::common::{ColorLegend, RouteExplorer, TripExplorer}; use crate::common::{ColorLegend, RouteExplorer, TripExplorer};
use crate::game::{Transition, WizardState}; use crate::game::{msg, Transition, WizardState};
use crate::render::{AgentColorScheme, MIN_ZOOM_FOR_DETAIL}; use crate::render::{AgentColorScheme, MIN_ZOOM_FOR_DETAIL};
use crate::ui::UI; use crate::ui::UI;
use ezgui::{hotkey, Choice, EventCtx, GfxCtx, Key, MenuUnderButton, ModalMenu}; use ezgui::{hotkey, Choice, EventCtx, GfxCtx, Key, MenuUnderButton, ModalMenu};
@ -71,9 +71,12 @@ impl AgentTools {
self.following = Some((trip, None, ui.primary.sim.time())); self.following = Some((trip, None, ui.primary.sim.time()));
} }
TripResult::TripDone => { TripResult::TripDone => {
println!("{} is done or aborted, so no more following", trip);
self.following = None; self.following = None;
menu.remove_action("stop following agent", ctx); menu.remove_action("stop following agent", ctx);
return Some(Transition::Push(msg(
"Follower",
vec![format!("{} is done or aborted, so no more following", trip)],
)));
} }
TripResult::TripDoesntExist => { TripResult::TripDoesntExist => {
println!("{} doesn't exist yet, so not following", trip); println!("{} doesn't exist yet, so not following", trip);

View File

@ -25,10 +25,8 @@ fn warp_to(wiz: &mut Wizard, ctx: &mut EventCtx, ui: &mut UI) -> Option<Transiti
EventLoopMode::Animation, EventLoopMode::Animation,
)); ));
} }
if wizard.acknowledge("Bad warp ID", || vec![format!("{} isn't a valid ID", to)]) { wizard.acknowledge("Bad warp ID", || vec![format!("{} isn't a valid ID", to)])?;
return Some(Transition::Pop); Some(Transition::Pop)
}
None
} }
pub struct Warping { pub struct Warping {

View File

@ -551,11 +551,13 @@ fn make_bulk_edit_lanes(road: RoadID) -> Box<dyn State> {
cnt += 1; cnt += 1;
} }
} }
// TODO pop this up. warn about road names changing and being weird. :) // TODO warn about road names changing and being weird. :)
println!( wizard.acknowledge("Bulk lane edit", || {
"Changed {} {:?} lanes to {:?} lanes on {}", vec![format!(
cnt, from, to, road_name "Changed {} {:?} lanes to {:?} lanes on {}",
); cnt, from, to, road_name
)]
})?;
apply_map_edits(&mut ui.primary, &ui.cs, ctx, edits); apply_map_edits(&mut ui.primary, &ui.cs, ctx, edits);
Some(Transition::Pop) Some(Transition::Pop)
})) }))

View File

@ -193,3 +193,12 @@ impl State for WizardState {
self.wizard.draw(g); self.wizard.draw(g);
} }
} }
// TODO Word wrap
pub fn msg<S: Into<String>>(title: &'static str, lines: Vec<S>) -> Box<dyn State> {
let str_lines: Vec<String> = lines.into_iter().map(|l| l.into()).collect();
WizardState::new(Box::new(move |wiz, ctx, _| {
wiz.wrap(ctx).acknowledge(title, || str_lines.clone())?;
Some(Transition::Pop)
}))
}

View File

@ -1,4 +1,4 @@
use crate::game::{Transition, WizardState}; use crate::game::{msg, Transition, WizardState};
use crate::render::AgentColorScheme; use crate::render::AgentColorScheme;
use crate::sandbox::{analytics, bus_explorer, spawner, SandboxMode}; use crate::sandbox::{analytics, bus_explorer, spawner, SandboxMode};
use crate::ui::UI; use crate::ui::UI;
@ -195,7 +195,7 @@ impl GameplayState {
)))); ))));
} }
if self.menu.action("help") { if self.menu.action("help") {
return Some(help(vec!["This simulation is empty by default.", "Try right-clicking an intersection and choosing to spawn agents (or just hover over it and press Z).", "You can also spawn agents from buildings or lanes.", "You can also start a full scenario to get realistic traffic."])); return Some(Transition::Push(msg("Help", vec!["This simulation is empty by default.", "Try right-clicking an intersection and choosing to spawn agents (or just hover over it and press Z).", "You can also spawn agents from buildings or lanes.", "You can also start a full scenario to get realistic traffic."])));
} }
if let Some(new_state) = spawner::AgentSpawner::new(ctx, ui) { if let Some(new_state) = spawner::AgentSpawner::new(ctx, ui) {
return Some(Transition::Push(new_state)); return Some(Transition::Push(new_state));
@ -209,11 +209,14 @@ impl GameplayState {
)))); ))));
} }
if self.menu.action("help") { if self.menu.action("help") {
return Some(help(vec![ return Some(Transition::Push(msg(
"Do things seem a bit quiet?", "Help",
"The simulation starts at midnight, so you might need to wait a bit.", vec![
"Try using the speed controls on the left.", "Do things seem a bit quiet?",
])); "The simulation starts at midnight, so you might need to wait a bit.",
"Try using the speed controls on the left.",
],
)));
} }
} }
State::OptimizeBus { State::OptimizeBus {
@ -279,12 +282,15 @@ impl GameplayState {
)))); ))));
} }
if self.menu.action("help") { if self.menu.action("help") {
return Some(help(vec![ return Some(Transition::Push(msg(
"First find where the bus gets stuck.", "Help",
"Then use edit mode to try to speed things up.", vec![
"Try making dedicated bus lanes", "First find where the bus gets stuck.",
"and adjusting traffic signals.", "Then use edit mode to try to speed things up.",
])); "Try making dedicated bus lanes",
"and adjusting traffic signals.",
],
)));
} }
} }
State::CreateGridlock { ref mut time } => { State::CreateGridlock { ref mut time } => {
@ -304,11 +310,11 @@ impl GameplayState {
} }
if self.menu.action("help") { if self.menu.action("help") {
return Some(help(vec![ return Some(Transition::Push(msg("Help", vec![
"You might notice a few places in the map where gridlock forms already.", "You might notice a few places in the map where gridlock forms already.",
"You can make things worse!", "You can make things worse!",
"How few lanes can you close for construction before everything grinds to a halt?", "How few lanes can you close for construction before everything grinds to a halt?",
])); ])));
} }
} }
State::FasterTrips { mode, ref mut time } => { State::FasterTrips { mode, ref mut time } => {
@ -320,9 +326,10 @@ impl GameplayState {
} }
if self.menu.action("help") { if self.menu.action("help") {
return Some(help(vec![ return Some(Transition::Push(msg(
"How can you possibly speed up all trips of some mode?", "Help",
])); vec!["How can you possibly speed up all trips of some mode?"],
)));
} }
} }
} }
@ -442,17 +449,6 @@ fn change_scenario(wiz: &mut Wizard, ctx: &mut EventCtx, ui: &mut UI) -> Option<
)))) ))))
} }
// TODO Word wrap
fn help(lines: Vec<&'static str>) -> Transition {
Transition::Push(WizardState::new(Box::new(move |wiz, ctx, _| {
if wiz.wrap(ctx).acknowledge("Help", || lines.clone()) {
Some(Transition::Pop)
} else {
None
}
})))
}
// Must call menu.event first. Returns true if the caller should set the analytics to the custom // Must call menu.event first. Returns true if the caller should set the analytics to the custom
// thing. // thing.
fn manage_analytics( fn manage_analytics(

View File

@ -8,7 +8,7 @@ mod trip_stats;
use crate::common::{time_controls, AgentTools, CommonState, SpeedControls}; use crate::common::{time_controls, AgentTools, CommonState, SpeedControls};
use crate::debug::DebugMode; use crate::debug::DebugMode;
use crate::edit::EditMode; use crate::edit::EditMode;
use crate::game::{State, Transition, WizardState}; use crate::game::{msg, State, Transition, WizardState};
use crate::helpers::ID; use crate::helpers::ID;
use crate::ui::{ShowEverything, UI}; use crate::ui::{ShowEverything, UI};
use ezgui::{ use ezgui::{
@ -190,7 +190,7 @@ impl State for SandboxMode {
} }
if self.save_tools.action("load previous sim state") { if self.save_tools.action("load previous sim state") {
self.speed.pause(); self.speed.pause();
ctx.loading_screen("load previous savestate", |ctx, mut timer| { if let Some(t) = ctx.loading_screen("load previous savestate", |ctx, mut timer| {
let prev_state = ui let prev_state = ui
.primary .primary
.sim .sim
@ -202,14 +202,20 @@ impl State for SandboxMode {
Some(new_sim) => { Some(new_sim) => {
ui.primary.sim = new_sim; ui.primary.sim = new_sim;
ui.recalculate_current_selection(ctx); ui.recalculate_current_selection(ctx);
None
} }
None => println!("Couldn't load previous savestate {:?}", prev_state), None => Some(Transition::Push(msg(
"Error",
vec![format!("Couldn't load previous savestate {:?}", prev_state)],
))),
} }
}); }) {
return t;
}
} }
if self.save_tools.action("load next sim state") { if self.save_tools.action("load next sim state") {
self.speed.pause(); self.speed.pause();
ctx.loading_screen("load next savestate", |ctx, mut timer| { if let Some(t) = ctx.loading_screen("load next savestate", |ctx, mut timer| {
let next_state = ui.primary.sim.find_next_savestate(ui.primary.sim.time()); let next_state = ui.primary.sim.find_next_savestate(ui.primary.sim.time());
match next_state match next_state
.clone() .clone()
@ -218,10 +224,16 @@ impl State for SandboxMode {
Some(new_sim) => { Some(new_sim) => {
ui.primary.sim = new_sim; ui.primary.sim = new_sim;
ui.recalculate_current_selection(ctx); ui.recalculate_current_selection(ctx);
None
} }
None => println!("Couldn't load next savestate {:?}", next_state), None => Some(Transition::Push(msg(
"Error",
vec![format!("Couldn't load next savestate {:?}", next_state)],
))),
} }
}); }) {
return t;
}
} }
if self.save_tools.action("pick a savestate to load") { if self.save_tools.action("pick a savestate to load") {
self.speed.pause(); self.speed.pause();

View File

@ -1,5 +1,5 @@
use crate::common::CommonState; use crate::common::CommonState;
use crate::game::{State, Transition}; use crate::game::{msg, State, Transition};
use crate::helpers::ID; use crate::helpers::ID;
use crate::render::DrawOptions; use crate::render::DrawOptions;
use crate::ui::{ShowEverything, UI}; use crate::ui::{ShowEverything, UI};
@ -213,7 +213,7 @@ impl State for AgentSpawner {
if self.maybe_goal.is_some() && ctx.input.contextual_action(Key::F3, "end the agent here") { if self.maybe_goal.is_some() && ctx.input.contextual_action(Key::F3, "end the agent here") {
let mut rng = ui.primary.current_flags.sim_flags.make_rng(); let mut rng = ui.primary.current_flags.sim_flags.make_rng();
let sim = &mut ui.primary.sim; let sim = &mut ui.primary.sim;
schedule_trip( let err = schedule_trip(
&self.from, &self.from,
self.maybe_goal.take().unwrap().0, self.maybe_goal.take().unwrap().0,
map, map,
@ -223,7 +223,11 @@ impl State for AgentSpawner {
sim.spawn_all_trips(map, &mut Timer::new("spawn trip"), false); sim.spawn_all_trips(map, &mut Timer::new("spawn trip"), false);
sim.step(map, SMALL_DT); sim.step(map, SMALL_DT);
ui.recalculate_current_selection(ctx); ui.recalculate_current_selection(ctx);
return Transition::Pop; if let Some(e) = err {
return Transition::Replace(msg("Spawning error", vec![e]));
} else {
return Transition::Pop;
}
} }
Transition::Keep Transition::Keep
@ -311,7 +315,14 @@ fn spawn_agents_around(i: IntersectionID, ui: &mut UI, ctx: &EventCtx) {
ui.recalculate_current_selection(ctx); ui.recalculate_current_selection(ctx);
} }
fn schedule_trip(src: &Source, raw_goal: Goal, map: &Map, sim: &mut Sim, rng: &mut XorShiftRng) { // Returns optional error message
fn schedule_trip(
src: &Source,
raw_goal: Goal,
map: &Map,
sim: &mut Sim,
rng: &mut XorShiftRng,
) -> Option<String> {
match src { match src {
Source::WalkFromBldg(_) | Source::WalkFromSidewalk(_) => { Source::WalkFromBldg(_) | Source::WalkFromSidewalk(_) => {
let start = match src { let start = match src {
@ -327,8 +338,7 @@ fn schedule_trip(src: &Source, raw_goal: Goal, map: &Map, sim: &mut Sim, rng: &m
if let Some(goal) = SidewalkSpot::end_at_border(to, map) { if let Some(goal) = SidewalkSpot::end_at_border(to, map) {
goal goal
} else { } else {
println!("Can't end a walking trip at {}; no sidewalks", to); return Some(format!("Can't end a walking trip at {}; no sidewalks", to));
return;
} }
} }
}; };
@ -368,8 +378,7 @@ fn schedule_trip(src: &Source, raw_goal: Goal, map: &Map, sim: &mut Sim, rng: &m
if let Some(g) = DrivingGoal::end_at_border(to, vec![LaneType::Driving], map) { if let Some(g) = DrivingGoal::end_at_border(to, vec![LaneType::Driving], map) {
g g
} else { } else {
println!("Can't end a car trip at {}; no driving lanes", to); return Some(format!("Can't end a car trip at {}; no driving lanes", to));
return;
} }
} }
}; };
@ -387,7 +396,7 @@ fn schedule_trip(src: &Source, raw_goal: Goal, map: &Map, sim: &mut Sim, rng: &m
map, map,
); );
} else { } else {
println!("Can't make a car appear at {:?}", from); return Some(format!("Can't make a car appear at {:?}", from));
} }
} }
Source::WalkFromBldgThenMaybeUseCar(b) => { Source::WalkFromBldgThenMaybeUseCar(b) => {
@ -405,4 +414,5 @@ fn schedule_trip(src: &Source, raw_goal: Goal, map: &Map, sim: &mut Sim, rng: &m
} }
} }
} }
None
} }

View File

@ -193,7 +193,7 @@ fn splash_screen(
x if x == tutorial => Some(Transition::Push(Box::new(TutorialMode::new(ctx)))), x if x == tutorial => Some(Transition::Push(Box::new(TutorialMode::new(ctx)))),
x if x == mission => Some(Transition::Push(Box::new(MissionEditMode::new(ctx)))), x if x == mission => Some(Transition::Push(Box::new(MissionEditMode::new(ctx)))),
x if x == about => { x if x == about => {
if wizard.acknowledge("About A/B Street", || { wizard.acknowledge("About A/B Street", || {
vec![ vec![
"Author: Dustin Carlino (dabreegster@gmail.com)", "Author: Dustin Carlino (dabreegster@gmail.com)",
"http://github.com/dabreegster/abstreet", "http://github.com/dabreegster/abstreet",
@ -201,14 +201,11 @@ fn splash_screen(
"", "",
"Press ENTER to continue", "Press ENTER to continue",
] ]
}) { })?;
Some(Transition::Replace(Box::new(SplashScreen { Some(Transition::Replace(Box::new(SplashScreen {
wizard: Wizard::new(), wizard: Wizard::new(),
maybe_screensaver: maybe_screensaver.take(), maybe_screensaver: maybe_screensaver.take(),
}))) })))
} else {
None
}
} }
x if x == quit => Some(Transition::Pop), x if x == quit => Some(Transition::Pop),
_ => unreachable!(), _ => unreachable!(),