configurable "jump to delay"

This commit is contained in:
Michael Kirk 2020-08-28 11:23:40 -07:00 committed by Dustin Carlino
parent d8d2877c14
commit e5620aeb10
4 changed files with 87 additions and 37 deletions

View File

@ -99,7 +99,7 @@ impl EditMode {
Transition::Multi(vec![ Transition::Multi(vec![
Transition::Pop, Transition::Pop,
Transition::Replace(SandboxMode::new(ctx, app, self.mode.clone())), Transition::Replace(SandboxMode::new(ctx, app, self.mode.clone())),
Transition::Push(TimeWarpScreen::new(ctx, app, old_sim.time(), false)), Transition::Push(TimeWarpScreen::new(ctx, app, old_sim.time(), None)),
]) ])
} else { } else {
app.primary.sim = old_sim; app.primary.sim = old_sim;

View File

@ -548,7 +548,7 @@ impl InfoPanel {
&mut actions, &mut actions,
); );
vec![sandbox, TimeWarpScreen::new(ctx, app, time, false)] vec![sandbox, TimeWarpScreen::new(ctx, app, time, None)]
}, },
))), ))),
) )

View File

@ -208,7 +208,7 @@ impl SpeedControls {
ctx, ctx,
app, app,
app.primary.sim.time() + dt, app.primary.sim.time() + dt,
false, None,
))); )));
} }
_ => unreachable!(), _ => unreachable!(),
@ -351,6 +351,7 @@ impl SpeedControls {
struct JumpToTime { struct JumpToTime {
panel: Panel, panel: Panel,
target: Time, target: Time,
halt_limit: Duration,
maybe_mode: Option<GameplayMode>, maybe_mode: Option<GameplayMode>,
} }
@ -358,8 +359,10 @@ impl JumpToTime {
fn new(ctx: &mut EventCtx, app: &App, maybe_mode: Option<GameplayMode>) -> JumpToTime { fn new(ctx: &mut EventCtx, app: &App, maybe_mode: Option<GameplayMode>) -> JumpToTime {
let target = app.primary.sim.time(); let target = app.primary.sim.time();
let end_of_day = app.primary.sim.get_end_of_day(); let end_of_day = app.primary.sim.get_end_of_day();
let halt_limit = Duration::minutes(5);
JumpToTime { JumpToTime {
target, target,
halt_limit,
maybe_mode, maybe_mode,
panel: Panel::new(Widget::col(vec![ panel: Panel::new(Widget::col(vec![
Widget::row(vec![ Widget::row(vec![
@ -368,6 +371,15 @@ impl JumpToTime {
.build(ctx, "close", hotkey(Key::Escape)) .build(ctx, "close", hotkey(Key::Escape))
.align_right(), .align_right(),
]), ]),
Checkbox::checkbox(
ctx,
"skip drawing (for faster simulations)",
None,
app.opts.dont_draw_time_warp,
)
.margin_above(30)
.named("don't draw"),
Widget::horiz_separator(ctx, 0.25).margin_above(10),
if app.has_prebaked().is_some() { if app.has_prebaked().is_some() {
Widget::draw_batch( Widget::draw_batch(
ctx, ctx,
@ -390,23 +402,16 @@ impl JumpToTime {
0.25 * ctx.canvas.window_width, 0.25 * ctx.canvas.window_width,
target.to_percent(end_of_day).min(1.0), target.to_percent(end_of_day).min(1.0),
) )
.named("time slider"), .named("time slider")
Btn::text_bg2(format!("Jump to {}", target.ampm_tostring()))
.build(ctx, "jump to time", hotkey(Key::Enter))
.centered_horiz() .centered_horiz()
.named("jump to time"), // EZGUI FIXME: margin_below having no effect here, so instead we add a margin_top
Widget::horiz_separator(ctx, 0.25).margin_above(10), // to the subsequent element
Btn::text_bg2("Jump to the next delay over 5 minutes") //.margin_above(16).margin_below(16),
.build_def(ctx, None) .margin_above(16),
.centered_horiz(), build_jump_to_time_btn(target, ctx),
Checkbox::checkbox( Widget::horiz_separator(ctx, 0.25).margin_above(16),
ctx, build_jump_to_delay_picker(halt_limit, ctx).margin_above(16),
"don't draw (for faster simulations)", build_jump_to_delay_button(halt_limit, ctx),
None,
app.opts.dont_draw_time_warp,
)
.margin_above(30)
.named("don't draw"),
])) ]))
.build(ctx), .build(ctx),
} }
@ -425,7 +430,7 @@ impl State for JumpToTime {
if let Some(mode) = self.maybe_mode.take() { if let Some(mode) = self.maybe_mode.take() {
return Transition::Multi(vec![ return Transition::Multi(vec![
Transition::Replace(SandboxMode::new(ctx, app, mode)), Transition::Replace(SandboxMode::new(ctx, app, mode)),
Transition::Push(TimeWarpScreen::new(ctx, app, self.target, false)), Transition::Push(TimeWarpScreen::new(ctx, app, self.target, None)),
]); ]);
} else { } else {
return Transition::Replace(PopupMsg::new( return Transition::Replace(PopupMsg::new(
@ -435,14 +440,15 @@ impl State for JumpToTime {
)); ));
} }
} }
return Transition::Replace(TimeWarpScreen::new(ctx, app, self.target, false)); return Transition::Replace(TimeWarpScreen::new(ctx, app, self.target, None));
} }
"Jump to the next delay over 5 minutes" => { "choose delay" => return Transition::Keep,
"jump to delay" => {
return Transition::Replace(TimeWarpScreen::new( return Transition::Replace(TimeWarpScreen::new(
ctx, ctx,
app, app,
app.primary.sim.get_end_of_day(), app.primary.sim.get_end_of_day(),
true, Some(self.panel.persistent_split_value("choose delay")),
)); ));
} }
_ => unreachable!(), _ => unreachable!(),
@ -460,15 +466,20 @@ impl State for JumpToTime {
.round_seconds(600.0); .round_seconds(600.0);
if target != self.target { if target != self.target {
self.target = target; self.target = target;
self.panel
.replace(ctx, "jump to time", build_jump_to_time_btn(target, ctx));
}
let halt_limit = self.panel.persistent_split_value("choose delay");
if halt_limit != self.halt_limit {
self.halt_limit = halt_limit;
self.panel.replace( self.panel.replace(
ctx, ctx,
"jump to time", "jump to delay",
Btn::text_bg2(format!("Jump to {}", target.ampm_tostring())) build_jump_to_delay_button(halt_limit, ctx),
.build(ctx, "jump to time", hotkey(Key::Enter))
.centered_horiz()
.named("jump to time"),
); );
} }
if self.panel.clicked_outside(ctx) { if self.panel.clicked_outside(ctx) {
return Transition::Pop; return Transition::Pop;
} }
@ -486,7 +497,7 @@ impl State for JumpToTime {
pub struct TimeWarpScreen { pub struct TimeWarpScreen {
target: Time, target: Time,
started: Instant, started: Instant,
traffic_jams: bool, halt_upon_delay: Option<Duration>,
panel: Panel, panel: Panel,
} }
@ -495,26 +506,26 @@ impl TimeWarpScreen {
ctx: &mut EventCtx, ctx: &mut EventCtx,
app: &mut App, app: &mut App,
target: Time, target: Time,
mut traffic_jams: bool, mut halt_upon_delay: Option<Duration>,
) -> Box<dyn State> { ) -> Box<dyn State> {
if traffic_jams { if let Some(halt_limit) = halt_upon_delay {
if app.primary.sim_cb.is_none() { if app.primary.sim_cb.is_none() {
app.primary.sim_cb = Some(Box::new(FindDelayedIntersections { app.primary.sim_cb = Some(Box::new(FindDelayedIntersections {
halt_limit: Duration::minutes(5), halt_limit,
report_limit: Duration::minutes(5), report_limit: halt_limit,
currently_delayed: Vec::new(), currently_delayed: Vec::new(),
})); }));
// TODO Can we get away with less frequently? Not sure about all the edge cases // TODO Can we get away with less frequently? Not sure about all the edge cases
app.primary.sim.set_periodic_callback(Duration::minutes(1)); app.primary.sim.set_periodic_callback(Duration::minutes(1));
} else { } else {
traffic_jams = false; halt_upon_delay = None;
} }
} }
Box::new(TimeWarpScreen { Box::new(TimeWarpScreen {
target, target,
started: Instant::now(), started: Instant::now(),
traffic_jams, halt_upon_delay,
panel: Panel::new(Widget::col(vec![ panel: Panel::new(Widget::col(vec![
Text::new().draw(ctx).named("text"), Text::new().draw(ctx).named("text"),
Btn::text_bg2("stop now") Btn::text_bg2("stop now")
@ -648,7 +659,7 @@ impl State for TimeWarpScreen {
} }
fn on_destroy(&mut self, _: &mut EventCtx, app: &mut App) { fn on_destroy(&mut self, _: &mut EventCtx, app: &mut App) {
if self.traffic_jams { if self.halt_upon_delay.is_some() {
assert!(app.primary.sim_cb.is_some()); assert!(app.primary.sim_cb.is_some());
app.primary.sim_cb = None; app.primary.sim_cb = None;
app.primary.sim.unset_periodic_callback(); app.primary.sim.unset_periodic_callback();
@ -761,3 +772,40 @@ fn compare_count(after: usize, before: usize) -> String {
format!("-{}", prettyprint_usize(before - after)) format!("-{}", prettyprint_usize(before - after))
} }
} }
fn build_jump_to_time_btn(target: Time, ctx: &EventCtx) -> Widget {
Btn::text_bg2(format!("Jump to {}", target.ampm_tostring()))
.build(ctx, "jump to time", hotkey(Key::Enter))
.named("jump to time")
.centered_horiz()
.margin_above(16)
}
fn build_jump_to_delay_button(halt_limit: Duration, ctx: &EventCtx) -> Widget {
Btn::text_bg2(format!("Jump to next {} delay", halt_limit))
.build(ctx, "jump to delay", hotkey(Key::D))
.named("jump to delay")
.centered_horiz()
.margin_above(16)
}
fn build_jump_to_delay_picker(halt_limit: Duration, ctx: &EventCtx) -> Widget {
// EZGUI TODO: it'd be nice if we could style the fg color for persistent splits but this needs
// to be passed into the button builder in init. so either we'd need to make a required
// argument, or introduce a persistentsplitbuilder, or re-work splitbuilder to allow mutating
// the color after the fact which requires holding more state to re-invoke the btnbuilder
PersistentSplit::new(
ctx,
"choose delay",
halt_limit,
None,
vec![
Choice::new("1 minute delay", Duration::minutes(1)),
Choice::new("2 minute delay", Duration::minutes(2)),
Choice::new("5 minute delay", Duration::minutes(5)),
Choice::new("10 minute delay", Duration::minutes(10)),
],
)
.outline(2.0, Color::WHITE)
.centered_horiz()
}

View File

@ -471,6 +471,8 @@ impl IntersectionSimState {
std::mem::replace(&mut self.events, Vec::new()) std::mem::replace(&mut self.events, Vec::new())
} }
/// returns intersections with travelers waiting for at least `threshold` since `now`, ordered
/// so the longest delayed intersection is first.
pub fn delayed_intersections( pub fn delayed_intersections(
&self, &self,
now: Time, now: Time,