convert the last caller away from wizard...

This commit is contained in:
Dustin Carlino 2020-08-07 13:52:27 -07:00
parent f61881f6fa
commit b881fa605b
14 changed files with 199 additions and 89 deletions

View File

@ -1,6 +1,6 @@
use crate::app::{App, PerMap};
use crate::common::Tab;
use crate::game::{msg, State, Transition};
use crate::game::{PopupMsg, State, Transition};
use crate::helpers::ID;
use crate::info::OpenTrip;
use crate::sandbox::SandboxMode;
@ -133,7 +133,8 @@ impl State for DebugWarp {
if let Some(t) = warp_to_id(ctx, app, &input) {
t
} else {
Transition::Replace(msg(
Transition::Replace(PopupMsg::new(
ctx,
"Bad warp ID",
vec![format!("{} isn't a valid ID", input)],
))

View File

@ -5,7 +5,7 @@ pub mod shared_row;
use crate::app::{App, ShowLayers, ShowObject};
use crate::common::{tool_panel, CommonState, ContextualActions};
use crate::game::{msg, ChooseSomething, DrawBaselayer, PromptInput, State, Transition};
use crate::game::{ChooseSomething, DrawBaselayer, PopupMsg, PromptInput, State, Transition};
use crate::helpers::ID;
use crate::options::OptionsPanel;
use crate::render::{calculate_corners, DrawOptions};
@ -145,7 +145,8 @@ impl State for DebugMode {
app.recalculate_current_selection(ctx);
None
}
None => Some(Transition::Push(msg(
None => Some(Transition::Push(PopupMsg::new(
ctx,
"Error",
vec![format!(
"Couldn't load previous savestate {:?}",
@ -170,7 +171,8 @@ impl State for DebugMode {
app.recalculate_current_selection(ctx);
None
}
None => Some(Transition::Push(msg(
None => Some(Transition::Push(PopupMsg::new(
ctx,
"Error",
vec![format!("Couldn't load next savestate {:?}", next_state)],
))),
@ -507,7 +509,8 @@ impl ContextualActions for Actions {
app.primary.current_selection = None;
Transition::Keep
}
(ID::Car(c), "find front of blockage") => Transition::Push(msg(
(ID::Car(c), "find front of blockage") => Transition::Push(PopupMsg::new(
ctx,
"Blockage results",
vec![format!(
"{} is ultimately blocked by {}",

View File

@ -1,6 +1,6 @@
use crate::app::{App, ShowEverything};
use crate::common::{CityPicker, ColorLegend};
use crate::game::{msg, State, Transition};
use crate::game::{PopupMsg, State, Transition};
use crate::helpers::{nice_map_name, open_browser, ID};
use abstutil::{prettyprint_usize, Tags, Timer};
use ezgui::{
@ -324,7 +324,8 @@ impl State for ParkingMapper {
}
"Generate OsmChange file" => {
if self.data.is_empty() {
return Transition::Push(msg(
return Transition::Push(PopupMsg::new(
ctx,
"No changes yet",
vec!["Map some parking first"],
));
@ -337,11 +338,14 @@ impl State for ParkingMapper {
timer,
)
}) {
Ok(()) => Transition::Push(msg(
Ok(()) => Transition::Push(PopupMsg::new(
ctx,
"Diff generated",
vec!["diff.osc created. Load it in JOSM, verify, and upload!"],
)),
Err(err) => Transition::Push(msg("Error", vec![format!("{}", err)])),
Err(err) => {
Transition::Push(PopupMsg::new(ctx, "Error", vec![format!("{}", err)]))
}
};
}
"change map" => {
@ -469,7 +473,8 @@ impl State for ChangeWay {
.current_choice()
.clone();
if value == Value::Complicated {
Transition::Replace(msg(
Transition::Replace(PopupMsg::new(
ctx,
"Complicated road",
vec![
"You'll have to manually split the way in ID or JOSM and apply \

View File

@ -1,7 +1,7 @@
use crate::app::App;
use crate::edit::select::RoadSelector;
use crate::edit::{apply_map_edits, change_speed_limit, try_change_lt};
use crate::game::{msg, State, Transition};
use crate::game::{PopupMsg, State, Transition};
use ezgui::{
hotkey, Btn, Choice, Composite, Drawable, EventCtx, GfxCtx, HorizontalAlignment, Key, Line,
Outcome, TextExt, VerticalAlignment, Widget,
@ -219,7 +219,7 @@ fn change_lane_types(
timer.next();
for l in app.primary.map.get_r(*r).all_lanes() {
if app.primary.map.get_l(l).lane_type == from {
match try_change_lt(&mut app.primary.map, l, to) {
match try_change_lt(ctx, &mut app.primary.map, l, to) {
Ok(cmd) => {
let mut edits = app.primary.map.get_edits().clone();
edits.commands.push(cmd);
@ -239,7 +239,8 @@ fn change_lane_types(
// TODO Need to express the errors in some form that we can union here.
msg(
PopupMsg::new(
ctx,
"Changed lane types",
vec![format!(
"Changed {} {:?} lanes to {:?} lanes. {} errors",

View File

@ -157,24 +157,26 @@ impl State for LaneEditor {
"Revert" => {
// TODO It's hard to revert both changes at once.
if let Some(lt) = map.get_edits().original_lts.get(&self.l).cloned() {
try_change_lt(map, self.l, lt)
try_change_lt(ctx, map, self.l, lt)
} else {
try_reverse(map, self.l)
try_reverse(ctx, map, self.l)
}
}
"reverse lane direction" => try_reverse(map, self.l),
"reverse lane direction" => try_reverse(ctx, map, self.l),
"convert to a driving lane" => {
try_change_lt(map, self.l, LaneType::Driving)
try_change_lt(ctx, map, self.l, LaneType::Driving)
}
"convert to a protected bike lane" => {
try_change_lt(map, self.l, LaneType::Biking)
try_change_lt(ctx, map, self.l, LaneType::Biking)
}
"convert to a bus-only lane" => {
try_change_lt(ctx, map, self.l, LaneType::Bus)
}
"convert to a bus-only lane" => try_change_lt(map, self.l, LaneType::Bus),
"convert to an on-street parking lane" => {
try_change_lt(map, self.l, LaneType::Parking)
try_change_lt(ctx, map, self.l, LaneType::Parking)
}
"close for construction" => {
try_change_lt(map, self.l, LaneType::Construction)
try_change_lt(ctx, map, self.l, LaneType::Construction)
}
_ => unreachable!(),
};

View File

@ -19,7 +19,7 @@ pub use self::validate::{
use crate::app::{App, ShowEverything};
use crate::common::{tool_panel, CommonState, Warping};
use crate::debug::DebugMode;
use crate::game::{msg, State, Transition};
use crate::game::{PopupMsg, State, Transition};
use crate::helpers::ID;
use crate::options::OptionsPanel;
use crate::render::{DrawIntersection, DrawMap, DrawRoad};
@ -489,7 +489,11 @@ impl State for LoadEdits {
LoadEdits::new(ctx, app, self.mode.clone()),
// TODO Menu draws at a weird Z-order to deal with tooltips, so now the
// menu underneath bleeds through
msg("Error", vec![format!("Can't load {}", path), err.clone()]),
PopupMsg::new(
ctx,
"Error",
vec![format!("Can't load {}", path), err.clone()],
),
),
}
}

View File

@ -1,7 +1,7 @@
use crate::app::{App, ShowEverything};
use crate::common::CommonState;
use crate::edit::{apply_map_edits, check_sidewalk_connectivity, StopSignEditor};
use crate::game::{msg, ChooseSomething, DrawBaselayer, State, Transition};
use crate::game::{ChooseSomething, DrawBaselayer, PopupMsg, State, Transition};
use crate::render::{
draw_signal_phase, make_signal_diagram, DrawOptions, DrawTurnGroup, BIG_ARROW_THICKNESS,
};
@ -767,7 +767,8 @@ fn check_for_missing_groups(
app.primary.map.incremental_edit_traffic_signal(new_signal);
*composite = make_signal_diagram(ctx, app, id, 0, true);
Transition::Push(msg(
Transition::Push(PopupMsg::new(
ctx,
"Error: missing turns",
vec![
format!("{} turns are missing from this traffic signal", num_missing),

View File

@ -1,6 +1,6 @@
use crate::app::App;
use crate::common::ColorDiscrete;
use crate::game::{msg, State, WizardState};
use crate::game::{PopupMsg, State};
use abstutil::Timer;
use ezgui::{Color, EventCtx};
use map_model::{connectivity, EditCmd, LaneID, LaneType, Map, PathConstraints};
@ -38,22 +38,23 @@ pub fn check_sidewalk_connectivity(
return None;
}
let mut err_state = msg(
"Error",
vec![format!(
"Can't close this intersection; {} sidewalks disconnected",
newly_disconnected.len()
)],
);
let mut c = ColorDiscrete::new(app, vec![("disconnected", Color::RED)]);
let num = newly_disconnected.len();
for l in newly_disconnected {
c.add_l(*l, "disconnected");
}
let (unzoomed, zoomed, _) = c.build(ctx);
err_state.downcast_mut::<WizardState>().unwrap().also_draw = Some((unzoomed, zoomed));
Some(err_state)
Some(PopupMsg::also_draw(
ctx,
"Error",
vec![format!(
"Can't close this intersection; {} sidewalks disconnected",
num
)],
unzoomed,
zoomed,
))
}
#[allow(unused)]
@ -96,25 +97,24 @@ pub fn check_blackholes(ctx: &mut EventCtx, app: &mut App, cmd: EditCmd) -> Opti
return None;
}
let mut err_state = msg(
"Error",
vec![format!(
"{} lanes have been disconnected",
newly_disconnected.len()
)],
);
let mut c = ColorDiscrete::new(app, vec![("disconnected", Color::RED)]);
let num = newly_disconnected.len();
for l in newly_disconnected {
c.add_l(l, "disconnected");
}
let (unzoomed, zoomed, _) = c.build(ctx);
err_state.downcast_mut::<WizardState>().unwrap().also_draw = Some((unzoomed, zoomed));
Some(err_state)
Some(PopupMsg::also_draw(
ctx,
"Error",
vec![format!("{} lanes have been disconnected", num)],
unzoomed,
zoomed,
))
}
pub fn try_change_lt(
ctx: &mut EventCtx,
map: &mut Map,
l: LaneID,
new_lt: LaneType,
@ -158,14 +158,15 @@ pub fn try_change_lt(
if errors.is_empty() {
Ok(cmd)
} else {
Err(msg("Error", errors))
Err(PopupMsg::new(ctx, "Error", errors))
}
}
pub fn try_reverse(map: &Map, l: LaneID) -> Result<EditCmd, Box<dyn State>> {
pub fn try_reverse(ctx: &mut EventCtx, map: &Map, l: LaneID) -> Result<EditCmd, Box<dyn State>> {
let lane = map.get_l(l);
if map.get_r(lane.parent).dir_and_offset(l).1 != 0 {
Err(msg(
Err(PopupMsg::new(
ctx,
"Error",
vec!["You can only reverse the lanes next to the road's yellow center line"],
))

View File

@ -4,8 +4,9 @@ use crate::pregame::TitleScreen;
use crate::render::DrawOptions;
use crate::sandbox::{GameplayMode, SandboxMode};
use ezgui::{
hotkey, Btn, Canvas, Choice, Composite, Drawable, EventCtx, GfxCtx, HorizontalAlignment, Key,
Line, Menu, Outcome, ScreenRectangle, VerticalAlignment, Widget, Wizard, GUI,
hotkey, hotkeys, Btn, Canvas, Choice, Composite, Drawable, EventCtx, GeomBatch, GfxCtx,
HorizontalAlignment, Key, Line, Menu, Outcome, ScreenRectangle, Text, VerticalAlignment,
Widget, Wizard, GUI,
};
use geom::Polygon;
use map_model::PermanentMapEdits;
@ -352,15 +353,6 @@ impl State for WizardState {
}
}
// 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)
}))
}
pub struct ChooseSomething<T> {
composite: Composite,
cb: Box<dyn Fn(T, &mut EventCtx, &mut App) -> Transition>,
@ -497,3 +489,75 @@ impl State for PromptInput {
self.composite.draw(g);
}
}
pub struct PopupMsg {
composite: Composite,
unzoomed: Drawable,
zoomed: Drawable,
}
impl PopupMsg {
pub fn new<I: Into<String>>(ctx: &mut EventCtx, title: &str, lines: Vec<I>) -> Box<dyn State> {
PopupMsg::also_draw(
ctx,
title,
lines,
ctx.upload(GeomBatch::new()),
ctx.upload(GeomBatch::new()),
)
}
pub fn also_draw<I: Into<String>>(
ctx: &mut EventCtx,
title: &str,
lines: Vec<I>,
unzoomed: Drawable,
zoomed: Drawable,
) -> Box<dyn State> {
let mut txt = Text::new();
txt.add(Line(title).small_heading());
for l in lines {
txt.add(Line(l));
}
Box::new(PopupMsg {
composite: Composite::new(Widget::col(vec![
txt.draw(ctx),
Btn::text_bg2("OK").build_def(ctx, hotkeys(vec![Key::Enter, Key::Escape])),
]))
.build(ctx),
unzoomed,
zoomed,
})
}
}
impl State for PopupMsg {
fn event(&mut self, ctx: &mut EventCtx, _: &mut App) -> Transition {
match self.composite.event(ctx) {
Outcome::Clicked(x) => match x.as_ref() {
"OK" => Transition::Pop,
_ => unreachable!(),
},
_ => {
if ctx.normal_left_click() && ctx.canvas.get_cursor_in_screen_space().is_none() {
return Transition::Pop;
}
Transition::Keep
}
}
}
fn draw_baselayer(&self) -> DrawBaselayer {
DrawBaselayer::PreviousState
}
fn draw(&self, g: &mut GfxCtx, app: &App) {
State::grey_out_map(g, app);
self.composite.draw(g);
if g.canvas.cam_zoom < app.opts.min_zoom_for_detail {
g.redraw(&self.unzoomed);
} else {
g.redraw(&self.zoomed);
}
}
}

View File

@ -2,7 +2,7 @@ use crate::app::App;
use crate::challenges::ChallengesPicker;
use crate::devtools::DevToolsMode;
use crate::edit::apply_map_edits;
use crate::game::{msg, DrawBaselayer, State, Transition};
use crate::game::{DrawBaselayer, PopupMsg, State, Transition};
use crate::helpers::open_browser;
use crate::sandbox::gameplay::Tutorial;
use crate::sandbox::{GameplayMode, SandboxMode};
@ -428,7 +428,11 @@ impl State for Proposals {
}
});
if let Some(err) = maybe_err {
return Transition::Push(msg("Can't load proposal", vec![err]));
return Transition::Push(PopupMsg::new(
ctx,
"Can't load proposal",
vec![err],
));
} else {
app.layer = Some(Box::new(crate::layer::map::Static::edits(ctx, app)));
return Transition::Push(Box::new(SandboxMode::new(

View File

@ -1,7 +1,7 @@
use crate::app::App;
use crate::common::CityPicker;
use crate::edit::EditMode;
use crate::game::{msg, State, Transition};
use crate::game::{PopupMsg, State, Transition};
use crate::helpers::{checkbox_per_mode, nice_map_name};
use crate::sandbox::gameplay::freeform::make_change_traffic;
use crate::sandbox::gameplay::{GameplayMode, GameplayState};
@ -370,13 +370,15 @@ impl State for ChangeMode {
from_modes.remove(&to_mode);
if from_modes.is_empty() {
return Transition::Push(msg(
return Transition::Push(PopupMsg::new(
ctx,
"Error",
vec!["You have to select at least one mode to convert from"],
));
}
if p1 >= p2 {
return Transition::Push(msg(
return Transition::Push(PopupMsg::new(
ctx,
"Error",
vec!["Your time range is backwards"],
));

View File

@ -2,7 +2,7 @@ use crate::app::App;
use crate::common::{tool_panel, Minimap, Warping};
use crate::cutscene::CutsceneBuilder;
use crate::edit::EditMode;
use crate::game::{msg, State, Transition};
use crate::game::{PopupMsg, State, Transition};
use crate::helpers::ID;
use crate::sandbox::gameplay::{GameplayMode, GameplayState};
use crate::sandbox::{
@ -265,7 +265,8 @@ impl Tutorial {
if !tut.score_delivered {
tut.score_delivered = true;
if before == after {
return Some(Transition::Push(msg(
return Some(Transition::Push(PopupMsg::new(
ctx,
"All trips completed",
vec![
"Your changes didn't affect anything!",
@ -274,7 +275,8 @@ impl Tutorial {
)));
}
if after > before {
return Some(Transition::Push(msg(
return Some(Transition::Push(PopupMsg::new(
ctx,
"All trips completed",
vec![
"Your changes made things worse!".to_string(),
@ -288,7 +290,8 @@ impl Tutorial {
)));
}
if before - after < CAR_BIKE_CONTENTION_GOAL {
return Some(Transition::Push(msg(
return Some(Transition::Push(PopupMsg::new(
ctx,
"All trips completed",
vec![
"Nice, you helped things a bit!".to_string(),
@ -301,7 +304,8 @@ impl Tutorial {
],
)));
}
return Some(Transition::Push(msg(
return Some(Transition::Push(PopupMsg::new(
ctx,
"All trips completed",
vec![format!(
"Awesome! All trips originally took {}, but now they only took {}",
@ -1329,7 +1333,7 @@ pub fn actions(app: &App, id: ID) -> Vec<(Key, String)> {
}
}
pub fn execute(_: &mut EventCtx, app: &mut App, id: ID, action: String) -> Transition {
pub fn execute(ctx: &mut EventCtx, app: &mut App, id: ID, action: String) -> Transition {
let mut tut = app.session.tutorial.as_mut().unwrap();
let response = match (id, action.as_ref()) {
(ID::Car(c), "draw WASH ME") => {
@ -1341,12 +1345,14 @@ pub fn execute(_: &mut EventCtx, app: &mut App, id: ID, action: String) -> Trans
if c == ESCORT {
if is_parked {
tut.prank_done = true;
msg(
PopupMsg::new(
ctx,
"Prank in progress",
vec!["You quickly scribble on the window..."],
)
} else {
msg(
PopupMsg::new(
ctx,
"Not yet!",
vec![
"You're going to run up to an occupied car and draw on their windows?",
@ -1356,7 +1362,8 @@ pub fn execute(_: &mut EventCtx, app: &mut App, id: ID, action: String) -> Trans
)
}
} else if c.1 == VehicleType::Bike {
msg(
PopupMsg::new(
ctx,
"That's a bike",
vec![
"Achievement unlocked: You attempted to draw WASH ME on a cyclist.",
@ -1366,7 +1373,8 @@ pub fn execute(_: &mut EventCtx, app: &mut App, id: ID, action: String) -> Trans
],
)
} else {
msg(
PopupMsg::new(
ctx,
"Wrong car",
vec![
"You're looking at the wrong car.",
@ -1382,7 +1390,8 @@ pub fn execute(_: &mut EventCtx, app: &mut App, id: ID, action: String) -> Trans
let percent = (app.primary.sim.get_free_onstreet_spots(l).len() as f64)
/ (lane.number_parking_spots() as f64);
if percent > 0.1 {
msg(
PopupMsg::new(
ctx,
"Not quite",
vec![
format!("This lane has {:.0}% spots free", percent * 100.0),
@ -1392,10 +1401,14 @@ pub fn execute(_: &mut EventCtx, app: &mut App, id: ID, action: String) -> Trans
)
} else {
tut.parking_found = true;
msg("Noice", vec!["Yup, parallel parking would be tough here!"])
PopupMsg::new(
ctx,
"Noice",
vec!["Yup, parallel parking would be tough here!"],
)
}
} else {
msg("Uhh..", vec!["That's not even a parking lane"])
PopupMsg::new(ctx, "Uhh..", vec!["That's not even a parking lane"])
}
}
_ => unreachable!(),

View File

@ -1,6 +1,6 @@
use crate::app::{App, FindDelayedIntersections, ShowEverything};
use crate::common::Warping;
use crate::game::{msg, DrawBaselayer, State, Transition};
use crate::game::{DrawBaselayer, PopupMsg, State, Transition};
use crate::helpers::ID;
use crate::render::DrawOptions;
use crate::sandbox::{GameplayMode, SandboxMode};
@ -181,7 +181,8 @@ impl SpeedControls {
mode.clone(),
))));
} else {
return Some(Transition::Push(msg(
return Some(Transition::Push(PopupMsg::new(
ctx,
"Error",
vec!["Sorry, you can't go rewind time from this mode."],
)));
@ -284,7 +285,11 @@ impl SpeedControls {
// TODO Need to do this anywhere that steps the sim, like TimeWarpScreen.
let alerts = app.primary.sim.clear_alerts();
if !alerts.is_empty() {
let popup = msg("Alerts", alerts.iter().map(|(_, _, msg)| msg).collect());
let popup = PopupMsg::new(
ctx,
"Alerts",
alerts.iter().map(|(_, _, msg)| msg).collect(),
);
let maybe_id = match alerts[0].1 {
AlertLocation::Nil => None,
AlertLocation::Intersection(i) => Some(ID::Intersection(i)),
@ -432,7 +437,8 @@ impl State for JumpToTime {
TimeWarpScreen::new(ctx, app, self.target, false),
);
} else {
return Transition::Replace(msg(
return Transition::Replace(PopupMsg::new(
ctx,
"Error",
vec!["Sorry, you can't go rewind time from this mode."],
));
@ -541,7 +547,8 @@ impl State for TimeWarpScreen {
);
for (t, maybe_i, alert) in app.primary.sim.clear_alerts() {
// TODO Just the first :(
return Transition::Replace(msg(
return Transition::Replace(PopupMsg::new(
ctx,
"Alert",
vec![format!("At {}, near {:?}, {}", t, maybe_i, alert)],
));

View File

@ -1,7 +1,7 @@
use crate::app::{App, ShowEverything};
use crate::common::CommonState;
use crate::edit::ClusterTrafficSignalEditor;
use crate::game::{msg, DrawBaselayer, State, Transition};
use crate::game::{DrawBaselayer, PopupMsg, State, Transition};
use crate::helpers::ID;
use crate::render::{DrawOptions, BIG_ARROW_THICKNESS};
use ezgui::{
@ -71,7 +71,8 @@ impl State for UberTurnPicker {
}
"View uber-turns" => {
if self.members.len() < 2 {
return Transition::Push(msg(
return Transition::Push(PopupMsg::new(
ctx,
"Error",
vec!["Select at least two intersections"],
));
@ -86,7 +87,8 @@ impl State for UberTurnPicker {
}
"Edit" => {
if self.members.len() < 2 {
return Transition::Push(msg(
return Transition::Push(PopupMsg::new(
ctx,
"Error",
vec!["Select at least two intersections"],
));