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 {
pub fn new(ctx: &mut EventCtx) -> DebugMode {
DebugMode {
pub fn new(ctx: &mut EventCtx) -> Box<dyn State> {
Box::new(DebugMode {
panel: Panel::new(Widget::col(vec![
Widget::row(vec![
Line("Debug Mode").small_heading().draw(ctx),
@ -86,7 +86,7 @@ impl DebugMode {
search_results: None,
all_routes: None,
highlighted_agents: None,
}
})
}
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),
&mut Timer::throwaway(),
);
Transition::Replace(Box::new(scenario::ScenarioManager::new(
scenario, ctx, app,
)))
Transition::Replace(scenario::ScenarioManager::new(scenario, ctx, app))
}),
));
}

View File

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

View File

@ -152,7 +152,7 @@ impl State for EditMode {
}
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) {

View File

@ -19,8 +19,8 @@ struct PreviewTrafficSignal {
}
impl PreviewTrafficSignal {
fn new(ctx: &mut EventCtx, app: &App) -> PreviewTrafficSignal {
PreviewTrafficSignal {
fn new(ctx: &mut EventCtx, app: &App) -> Box<dyn State> {
Box::new(PreviewTrafficSignal {
panel: Panel::new(Widget::col(vec![
"Previewing traffic signal".draw_text(ctx),
Btn::text_fg("back to editing").build_def(ctx, hotkey(Key::Escape)),
@ -29,7 +29,7 @@ impl PreviewTrafficSignal {
.build(ctx),
speed: SpeedControls::new(ctx, app),
time_panel: TimePanel::new(ctx, app),
}
})
}
}
@ -113,7 +113,7 @@ pub fn make_previewer(
.sim
.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 dont_draw_time_warp: bool,
pub time_warp_halt_limit: Duration,
pub jump_to_delay: Duration,
pub language: Option<String>,
}
@ -43,7 +43,7 @@ impl Options {
time_increment: Duration::minutes(10),
dont_draw_time_warp: false,
time_warp_halt_limit: Duration::minutes(5),
jump_to_delay: Duration::minutes(5),
language: None,
}

View File

@ -132,7 +132,7 @@ impl State for SandboxMode {
// Order here is pretty arbitrary
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 {

View File

@ -186,11 +186,11 @@ impl SpeedControls {
}
}
"jump to specific time" => {
return Some(Transition::Push(Box::new(JumpToTime::new(
return Some(Transition::Push(JumpToTime::new(
ctx,
app,
maybe_mode.cloned(),
))));
)));
}
"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 widgetry::{
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
pub struct JumpToTime {
panel: Panel,
target: Time,
halt_limit: Duration,
maybe_mode: Option<GameplayMode>,
}
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 end_of_day = app.primary.sim.get_end_of_day();
let halt_limit = app.opts.time_warp_halt_limit;
JumpToTime {
Box::new(JumpToTime {
target,
halt_limit,
maybe_mode,
panel: Panel::new(Widget::col(vec![
Widget::row(vec![
Line("Jump to what time?").small_heading().draw(ctx),
Btn::plaintext("X")
.build(ctx, "close", hotkey(Key::Escape))
.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),
Btn::plaintext("X")
.build(ctx, "close", hotkey(Key::Escape))
.align_right(),
Widget::custom_row(vec![
Btn::text_bg2("Jump to time").inactive(ctx),
Btn::text_bg2("Jump to delay").build_def(ctx, None),
])
.bg(Color::WHITE),
Line("Jump to what time?").small_heading().draw(ctx),
if app.has_prebaked().is_some() {
Widget::draw_batch(
ctx,
@ -67,19 +58,20 @@ impl JumpToTime {
0.25 * ctx.canvas.window_width,
target.to_percent(end_of_day).min(1.0),
)
.named("time slider")
.centered_horiz()
// EZGUI FIXME: margin_below having no effect here, so instead we add a margin_top
// to the subsequent element
//.margin_above(16).margin_below(16),
.margin_above(16),
build_jump_to_time_btn(target, ctx),
Widget::horiz_separator(ctx, 0.25).margin_above(16),
build_jump_to_delay_picker(halt_limit, ctx).margin_above(16),
build_jump_to_delay_button(halt_limit, ctx),
.named("time slider"),
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_time_btn(ctx, target),
]))
.exact_size_percent(50, 50)
.build(ctx),
}
})
}
}
@ -107,16 +99,8 @@ impl State for JumpToTime {
}
return Transition::Replace(TimeWarpScreen::new(ctx, app, self.target, None));
}
"choose delay" => return Transition::Keep,
"jump to delay" => {
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),
));
"Jump to delay" => {
return Transition::Replace(JumpToDelay::new(ctx, app, self.maybe_mode.take()));
}
_ => unreachable!(),
},
@ -134,17 +118,101 @@ impl State for JumpToTime {
if target != self.target {
self.target = target;
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 halt_limit != self.halt_limit {
self.halt_limit = halt_limit;
self.panel.replace(
ctx,
"jump to delay",
build_jump_to_delay_button(halt_limit, ctx),
);
if self.panel.clicked_outside(ctx) {
return Transition::Pop;
}
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(
ctx,
"jump to delay",
build_jump_to_delay_button(ctx, self.panel.dropdown_value("delay")),
);
}
_ => {}
}
if self.panel.clicked_outside(ctx) {
@ -293,7 +361,9 @@ impl State for TimeWarpScreen {
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;
}
@ -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()))
.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))
fn build_jump_to_delay_button(ctx: &EventCtx, delay: Duration) -> Widget {
Btn::text_bg2(format!("Jump to next {} delay", delay))
.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()
}