Implement Yuwen's split jump to time/delay UI [rebuild]

This commit is contained in:
Dustin Carlino 2020-09-14 10:28:25 -07:00
parent 8b7cb7a601
commit ec28132286
9 changed files with 142 additions and 97 deletions

View File

@ -34,8 +34,8 @@ pub struct DebugMode {
} }
impl DebugMode { impl DebugMode {
pub fn new(ctx: &mut EventCtx) -> DebugMode { pub fn new(ctx: &mut EventCtx) -> Box<dyn State> {
DebugMode { Box::new(DebugMode {
panel: Panel::new(Widget::col(vec![ panel: Panel::new(Widget::col(vec![
Widget::row(vec![ Widget::row(vec![
Line("Debug Mode").small_heading().draw(ctx), Line("Debug Mode").small_heading().draw(ctx),
@ -86,7 +86,7 @@ impl DebugMode {
search_results: None, search_results: None,
all_routes: None, all_routes: None,
highlighted_agents: None, highlighted_agents: None,
} })
} }
fn reset_info(&mut self, ctx: &mut EventCtx) { fn reset_info(&mut self, ctx: &mut EventCtx) {

View File

@ -103,9 +103,7 @@ impl State for DevToolsMode {
abstutil::path_scenario(app.primary.map.get_name(), &s), abstutil::path_scenario(app.primary.map.get_name(), &s),
&mut Timer::throwaway(), &mut Timer::throwaway(),
); );
Transition::Replace(Box::new(scenario::ScenarioManager::new( Transition::Replace(scenario::ScenarioManager::new(scenario, ctx, app))
scenario, ctx, app,
)))
}), }),
)); ));
} }

View File

@ -17,7 +17,7 @@ pub struct ScenarioManager {
} }
impl ScenarioManager { impl ScenarioManager {
pub fn new(scenario: Scenario, ctx: &mut EventCtx, app: &App) -> ScenarioManager { pub fn new(scenario: Scenario, ctx: &mut EventCtx, app: &App) -> Box<dyn State> {
let mut colorer = ColorDiscrete::new( let mut colorer = ColorDiscrete::new(
app, app,
vec![ vec![
@ -45,7 +45,7 @@ impl ScenarioManager {
assert!(filled_spots.is_empty()); assert!(filled_spots.is_empty());
let (unzoomed, zoomed, legend) = colorer.build(ctx); let (unzoomed, zoomed, legend) = colorer.build(ctx);
ScenarioManager { Box::new(ScenarioManager {
panel: Panel::new(Widget::col(vec![ panel: Panel::new(Widget::col(vec![
Widget::row(vec![ Widget::row(vec![
Line(format!("Scenario {}", scenario.scenario_name)) Line(format!("Scenario {}", scenario.scenario_name))
@ -80,7 +80,7 @@ impl ScenarioManager {
unzoomed, unzoomed,
zoomed, zoomed,
scenario, scenario,
} })
} }
} }

View File

@ -152,7 +152,7 @@ impl State for EditMode {
} }
if app.opts.dev && ctx.input.pressed(lctrl(Key::D)) { if app.opts.dev && ctx.input.pressed(lctrl(Key::D)) {
return Transition::Push(Box::new(DebugMode::new(ctx))); return Transition::Push(DebugMode::new(ctx));
} }
match self.top_center.event(ctx) { match self.top_center.event(ctx) {

View File

@ -19,8 +19,8 @@ struct PreviewTrafficSignal {
} }
impl PreviewTrafficSignal { impl PreviewTrafficSignal {
fn new(ctx: &mut EventCtx, app: &App) -> PreviewTrafficSignal { fn new(ctx: &mut EventCtx, app: &App) -> Box<dyn State> {
PreviewTrafficSignal { Box::new(PreviewTrafficSignal {
panel: Panel::new(Widget::col(vec![ panel: Panel::new(Widget::col(vec![
"Previewing traffic signal".draw_text(ctx), "Previewing traffic signal".draw_text(ctx),
Btn::text_fg("back to editing").build_def(ctx, hotkey(Key::Escape)), Btn::text_fg("back to editing").build_def(ctx, hotkey(Key::Escape)),
@ -29,7 +29,7 @@ impl PreviewTrafficSignal {
.build(ctx), .build(ctx),
speed: SpeedControls::new(ctx, app), speed: SpeedControls::new(ctx, app),
time_panel: TimePanel::new(ctx, app), time_panel: TimePanel::new(ctx, app),
} })
} }
} }
@ -113,7 +113,7 @@ pub fn make_previewer(
.sim .sim
.handle_live_edited_traffic_signals(&app.primary.map); .handle_live_edited_traffic_signals(&app.primary.map);
} }
Transition::Replace(Box::new(PreviewTrafficSignal::new(ctx, app))) Transition::Replace(PreviewTrafficSignal::new(ctx, app))
}), }),
) )
} }

View File

@ -23,7 +23,7 @@ pub struct Options {
pub time_increment: Duration, pub time_increment: Duration,
pub dont_draw_time_warp: bool, pub dont_draw_time_warp: bool,
pub time_warp_halt_limit: Duration, pub jump_to_delay: Duration,
pub language: Option<String>, pub language: Option<String>,
} }
@ -43,7 +43,7 @@ impl Options {
time_increment: Duration::minutes(10), time_increment: Duration::minutes(10),
dont_draw_time_warp: false, dont_draw_time_warp: false,
time_warp_halt_limit: Duration::minutes(5), jump_to_delay: Duration::minutes(5),
language: None, language: None,
} }

View File

@ -132,7 +132,7 @@ impl State for SandboxMode {
// Order here is pretty arbitrary // Order here is pretty arbitrary
if app.opts.dev && ctx.input.pressed(lctrl(Key::D)) { if app.opts.dev && ctx.input.pressed(lctrl(Key::D)) {
return Transition::Push(Box::new(DebugMode::new(ctx))); return Transition::Push(DebugMode::new(ctx));
} }
if let Some(ref mut m) = self.controls.minimap { if let Some(ref mut m) = self.controls.minimap {

View File

@ -186,11 +186,11 @@ impl SpeedControls {
} }
} }
"jump to specific time" => { "jump to specific time" => {
return Some(Transition::Push(Box::new(JumpToTime::new( return Some(Transition::Push(JumpToTime::new(
ctx, ctx,
app, app,
maybe_mode.cloned(), maybe_mode.cloned(),
)))); )));
} }
"step forwards" => { "step forwards" => {
let dt = self.panel.persistent_split_value("step forwards"); let dt = self.panel.persistent_split_value("step forwards");

View File

@ -9,42 +9,33 @@ use geom::{Duration, Polygon, Pt2D, Ring, Time};
use instant::Instant; use instant::Instant;
use widgetry::{ use widgetry::{
hotkey, AreaSlider, Btn, Checkbox, Choice, Color, EventCtx, GeomBatch, GfxCtx, Key, Line, hotkey, AreaSlider, Btn, Checkbox, Choice, Color, EventCtx, GeomBatch, GfxCtx, Key, Line,
Outcome, Panel, PersistentSplit, Text, UpdateType, Widget, Outcome, Panel, Text, UpdateType, Widget,
}; };
// TODO Text entry would be great // TODO Text entry would be great
pub struct JumpToTime { pub struct JumpToTime {
panel: Panel, panel: Panel,
target: Time, target: Time,
halt_limit: Duration,
maybe_mode: Option<GameplayMode>, maybe_mode: Option<GameplayMode>,
} }
impl JumpToTime { impl JumpToTime {
pub fn new(ctx: &mut EventCtx, app: &App, maybe_mode: Option<GameplayMode>) -> JumpToTime { pub fn new(ctx: &mut EventCtx, app: &App, maybe_mode: Option<GameplayMode>) -> Box<dyn State> {
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 = app.opts.time_warp_halt_limit; Box::new(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![
Line("Jump to what time?").small_heading().draw(ctx),
Btn::plaintext("X") Btn::plaintext("X")
.build(ctx, "close", hotkey(Key::Escape)) .build(ctx, "close", hotkey(Key::Escape))
.align_right(), .align_right(),
]), Widget::custom_row(vec![
Checkbox::checkbox( Btn::text_bg2("Jump to time").inactive(ctx),
ctx, Btn::text_bg2("Jump to delay").build_def(ctx, None),
"skip drawing (for faster simulations)", ])
None, .bg(Color::WHITE),
app.opts.dont_draw_time_warp, Line("Jump to what time?").small_heading().draw(ctx),
)
.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,
@ -67,19 +58,20 @@ 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"),
.centered_horiz() Checkbox::checkbox(
// EZGUI FIXME: margin_below having no effect here, so instead we add a margin_top ctx,
// to the subsequent element "skip drawing (for faster simulations)",
//.margin_above(16).margin_below(16), None,
.margin_above(16), app.opts.dont_draw_time_warp,
build_jump_to_time_btn(target, ctx), )
Widget::horiz_separator(ctx, 0.25).margin_above(16), .margin_above(30)
build_jump_to_delay_picker(halt_limit, ctx).margin_above(16), .named("don't draw"),
build_jump_to_delay_button(halt_limit, ctx), build_jump_to_time_btn(ctx, target),
])) ]))
.exact_size_percent(50, 50)
.build(ctx), .build(ctx),
} })
} }
} }
@ -107,16 +99,8 @@ impl State for JumpToTime {
} }
return Transition::Replace(TimeWarpScreen::new(ctx, app, self.target, None)); return Transition::Replace(TimeWarpScreen::new(ctx, app, self.target, None));
} }
"choose delay" => return Transition::Keep, "Jump to delay" => {
"jump to delay" => { return Transition::Replace(JumpToDelay::new(ctx, app, self.maybe_mode.take()));
let halt_limit = self.panel.persistent_split_value("choose delay");
app.opts.time_warp_halt_limit = halt_limit;
return Transition::Replace(TimeWarpScreen::new(
ctx,
app,
app.primary.sim.get_end_of_day(),
Some(halt_limit),
));
} }
_ => unreachable!(), _ => unreachable!(),
}, },
@ -134,18 +118,102 @@ impl State for JumpToTime {
if target != self.target { if target != self.target {
self.target = target; self.target = target;
self.panel self.panel
.replace(ctx, "jump to time", build_jump_to_time_btn(target, ctx)); .replace(ctx, "jump to time", build_jump_to_time_btn(ctx, target));
} }
let halt_limit = self.panel.persistent_split_value("choose delay"); if self.panel.clicked_outside(ctx) {
if halt_limit != self.halt_limit { return Transition::Pop;
self.halt_limit = halt_limit; }
Transition::Keep
}
fn draw(&self, g: &mut GfxCtx, app: &App) {
State::grey_out_map(g, app);
self.panel.draw(g);
}
}
struct JumpToDelay {
panel: Panel,
maybe_mode: Option<GameplayMode>,
}
impl JumpToDelay {
pub fn new(ctx: &mut EventCtx, app: &App, maybe_mode: Option<GameplayMode>) -> Box<dyn State> {
Box::new(JumpToDelay {
maybe_mode,
panel: Panel::new(Widget::col(vec![
Btn::plaintext("X")
.build(ctx, "close", hotkey(Key::Escape))
.align_right(),
Widget::custom_row(vec![
Btn::text_bg2("Jump to time").build_def(ctx, None),
Btn::text_bg2("Jump to delay").inactive(ctx),
])
.bg(Color::WHITE),
Widget::row(vec![
Line("Jump to next").small_heading().draw(ctx),
Widget::dropdown(
ctx,
"delay",
app.opts.jump_to_delay,
vec![
Choice::new("1", Duration::minutes(1)),
Choice::new("2", Duration::minutes(2)),
Choice::new("5", Duration::minutes(5)),
Choice::new("10", Duration::minutes(10)),
],
),
Line("minute delay").small_heading().draw(ctx),
]),
Checkbox::checkbox(
ctx,
"skip drawing (for faster simulations)",
None,
app.opts.dont_draw_time_warp,
)
.margin_above(30)
.named("don't draw"),
build_jump_to_delay_button(ctx, app.opts.jump_to_delay),
]))
.exact_size_percent(50, 50)
.build(ctx),
})
}
}
impl State for JumpToDelay {
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
match self.panel.event(ctx) {
Outcome::Clicked(x) => match x.as_ref() {
"close" => {
return Transition::Pop;
}
"jump to delay" => {
let delay = self.panel.dropdown_value("delay");
app.opts.jump_to_delay = delay;
return Transition::Replace(TimeWarpScreen::new(
ctx,
app,
app.primary.sim.get_end_of_day(),
Some(delay),
));
}
"Jump to time" => {
return Transition::Replace(JumpToTime::new(ctx, app, self.maybe_mode.take()));
}
_ => unreachable!(),
},
Outcome::Changed => {
self.panel.replace( self.panel.replace(
ctx, ctx,
"jump to delay", "jump to delay",
build_jump_to_delay_button(halt_limit, ctx), build_jump_to_delay_button(ctx, self.panel.dropdown_value("delay")),
); );
} }
_ => {}
}
if self.panel.clicked_outside(ctx) { if self.panel.clicked_outside(ctx) {
return Transition::Pop; return Transition::Pop;
@ -293,7 +361,9 @@ impl State for TimeWarpScreen {
self.panel.replace(ctx, "text", txt.draw(ctx).named("text")); self.panel.replace(ctx, "text", txt.draw(ctx).named("text"));
} }
if app.primary.sim.time() == self.target { // >= because of the case of resetting to midnight. GameplayMode::initialize takes a tiny
// step past midnight after spawning things, so that agents initially appear on the map.
if app.primary.sim.time() >= self.target {
return Transition::Pop; return Transition::Pop;
} }
@ -377,39 +447,16 @@ fn compare_count(after: usize, before: usize) -> String {
} }
} }
fn build_jump_to_time_btn(target: Time, ctx: &EventCtx) -> Widget { fn build_jump_to_time_btn(ctx: &EventCtx, target: Time) -> Widget {
Btn::text_bg2(format!("Jump to {}", target.ampm_tostring())) Btn::text_bg2(format!("Jump to {}", target.ampm_tostring()))
.build(ctx, "jump to time", hotkey(Key::Enter)) .build(ctx, "jump to time", hotkey(Key::Enter))
.named("jump to time")
.centered_horiz() .centered_horiz()
.margin_above(16) .margin_above(16)
} }
fn build_jump_to_delay_button(halt_limit: Duration, ctx: &EventCtx) -> Widget { fn build_jump_to_delay_button(ctx: &EventCtx, delay: Duration) -> Widget {
Btn::text_bg2(format!("Jump to next {} delay", halt_limit)) Btn::text_bg2(format!("Jump to next {} delay", delay))
.build(ctx, "jump to delay", hotkey(Key::D)) .build(ctx, "jump to delay", hotkey(Key::D))
.named("jump to delay")
.centered_horiz() .centered_horiz()
.margin_above(16) .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()
}