mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-11-24 09:24:26 +03:00
convert more to NewModalMenu
This commit is contained in:
parent
d10cbd9b7c
commit
36df2e7908
@ -17,7 +17,7 @@ use map_model::{
|
||||
use std::collections::{BTreeSet, HashMap};
|
||||
|
||||
pub enum EditMode {
|
||||
ViewingDiffs(CommonState),
|
||||
ViewingDiffs(CommonState, NewModalMenu),
|
||||
Saving(Wizard),
|
||||
Loading(Wizard),
|
||||
EditingStopSign(IntersectionID, NewModalMenu),
|
||||
@ -25,37 +25,39 @@ pub enum EditMode {
|
||||
}
|
||||
|
||||
impl EditMode {
|
||||
pub fn new() -> EditMode {
|
||||
EditMode::ViewingDiffs(CommonState::new())
|
||||
pub fn new(ctx: &EventCtx) -> EditMode {
|
||||
EditMode::ViewingDiffs(
|
||||
CommonState::new(),
|
||||
NewModalMenu::new(
|
||||
"Map Edit Mode",
|
||||
vec![
|
||||
(Key::Escape, "quit"),
|
||||
(Key::S, "save edits"),
|
||||
(Key::L, "load different edits"),
|
||||
],
|
||||
ctx,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn event(state: &mut GameState, ctx: &mut EventCtx) -> EventLoopMode {
|
||||
ctx.canvas.handle_event(ctx.input);
|
||||
|
||||
// Common functionality
|
||||
let mut txt = Text::prompt("Map Edit Mode");
|
||||
txt.add_line(state.ui.primary.map.get_edits().edits_name.clone());
|
||||
txt.add_line(state.ui.primary.map.get_edits().describe());
|
||||
txt.add_line("Right-click a lane or intersection to start editing".to_string());
|
||||
match state.mode {
|
||||
Mode::Edit(EditMode::ViewingDiffs(_))
|
||||
| Mode::Edit(EditMode::Saving(_))
|
||||
| Mode::Edit(EditMode::Loading(_)) => {
|
||||
Mode::Edit(EditMode::ViewingDiffs(ref mut common, ref mut menu)) => {
|
||||
let mut txt = Text::prompt("Map Edit Mode");
|
||||
// TODO Display info/hints on more lines.
|
||||
ctx.input
|
||||
.set_mode_with_new_prompt("Map Edit Mode", txt, ctx.canvas);
|
||||
// TODO Clicking this works, but the key doesn't
|
||||
if ctx.input.modal_action("quit") {
|
||||
txt.add_line(state.ui.primary.map.get_edits().edits_name.clone());
|
||||
txt.add_line(state.ui.primary.map.get_edits().describe());
|
||||
txt.add_line("Right-click a lane or intersection to start editing".to_string());
|
||||
menu.update_prompt(txt, ctx);
|
||||
menu.handle_event(ctx);
|
||||
if menu.action("quit") {
|
||||
// TODO Warn about unsaved edits
|
||||
state.mode = Mode::SplashScreen(Wizard::new(), None);
|
||||
return EventLoopMode::InputOnly;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
match state.mode {
|
||||
Mode::Edit(EditMode::ViewingDiffs(ref mut common)) => {
|
||||
ctx.canvas.handle_event(ctx.input);
|
||||
|
||||
// TODO Reset when transitioning in/out of this state? Or maybe we just don't draw
|
||||
// the effects of it. Or eventually, the Option<ID> itself will live in here
|
||||
// directly.
|
||||
@ -72,10 +74,10 @@ impl EditMode {
|
||||
}
|
||||
|
||||
// TODO Only if current edits are unsaved
|
||||
if ctx.input.modal_action("save edits") {
|
||||
if menu.action("save edits") {
|
||||
state.mode = Mode::Edit(EditMode::Saving(Wizard::new()));
|
||||
return EventLoopMode::InputOnly;
|
||||
} else if ctx.input.modal_action("load different edits") {
|
||||
} else if menu.action("load different edits") {
|
||||
state.mode = Mode::Edit(EditMode::Loading(Wizard::new()));
|
||||
return EventLoopMode::InputOnly;
|
||||
}
|
||||
@ -131,7 +133,7 @@ impl EditMode {
|
||||
.is_some()
|
||||
|| wizard.aborted()
|
||||
{
|
||||
state.mode = Mode::Edit(EditMode::new());
|
||||
state.mode = Mode::Edit(EditMode::new(ctx));
|
||||
}
|
||||
}
|
||||
Mode::Edit(EditMode::Loading(ref mut wizard)) => {
|
||||
@ -141,13 +143,14 @@ impl EditMode {
|
||||
"Load which map edits?",
|
||||
) {
|
||||
apply_map_edits(&mut state.ui, ctx, new_edits);
|
||||
state.mode = Mode::Edit(EditMode::new());
|
||||
state.mode = Mode::Edit(EditMode::new(ctx));
|
||||
} else if wizard.aborted() {
|
||||
state.mode = Mode::Edit(EditMode::new());
|
||||
state.mode = Mode::Edit(EditMode::new(ctx));
|
||||
}
|
||||
}
|
||||
Mode::Edit(EditMode::EditingStopSign(i, ref mut menu)) => {
|
||||
menu.handle_event(ctx);
|
||||
ctx.canvas.handle_event(ctx.input);
|
||||
|
||||
state.ui.primary.current_selection = state.ui.handle_mouseover(
|
||||
ctx,
|
||||
@ -181,7 +184,7 @@ impl EditMode {
|
||||
apply_map_edits(&mut state.ui, ctx, new_edits);
|
||||
}
|
||||
} else if menu.action("quit") {
|
||||
state.mode = Mode::Edit(EditMode::new());
|
||||
state.mode = Mode::Edit(EditMode::new(ctx));
|
||||
} else if menu.action("reset to default") {
|
||||
let mut new_edits = state.ui.primary.map.get_edits().clone();
|
||||
new_edits.stop_sign_overrides.remove(&i);
|
||||
@ -190,7 +193,7 @@ impl EditMode {
|
||||
}
|
||||
Mode::Edit(EditMode::EditingTrafficSignal(ref mut editor)) => {
|
||||
if editor.event(ctx, &mut state.ui) {
|
||||
state.mode = Mode::Edit(EditMode::new());
|
||||
state.mode = Mode::Edit(EditMode::new(ctx));
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
@ -201,7 +204,7 @@ impl EditMode {
|
||||
|
||||
pub fn draw(state: &GameState, g: &mut GfxCtx) {
|
||||
match state.mode {
|
||||
Mode::Edit(EditMode::ViewingDiffs(ref common)) => {
|
||||
Mode::Edit(EditMode::ViewingDiffs(ref common, ref menu)) => {
|
||||
state.ui.draw(
|
||||
g,
|
||||
common.draw_options(&state.ui),
|
||||
@ -209,6 +212,7 @@ impl EditMode {
|
||||
&ShowEverything::new(),
|
||||
);
|
||||
common.draw(g, &state.ui);
|
||||
menu.draw(g);
|
||||
|
||||
// TODO Similar to drawing areas with traffic or not -- would be convenient to just
|
||||
// supply a set of things to highlight and have something else take care of drawing
|
||||
|
@ -56,6 +56,7 @@ impl TrafficSignalEditor {
|
||||
pub fn event(&mut self, ctx: &mut EventCtx, ui: &mut UI) -> bool {
|
||||
self.menu.handle_event(ctx);
|
||||
self.diagram_top_left = self.menu.get_bottom_left(ctx);
|
||||
ctx.canvas.handle_event(ctx.input);
|
||||
|
||||
ui.primary.current_selection = ui.handle_mouseover(
|
||||
ctx,
|
||||
|
@ -40,7 +40,7 @@ impl GameState {
|
||||
let splash = !flags.no_splash;
|
||||
let mut rng = flags.sim_flags.make_rng();
|
||||
let mut game = GameState {
|
||||
mode: Mode::Sandbox(SandboxMode::new()),
|
||||
mode: Mode::Sandbox(SandboxMode::new(canvas)),
|
||||
ui: UI::new(flags, prerender, canvas),
|
||||
};
|
||||
if splash {
|
||||
@ -71,45 +71,6 @@ impl GameState {
|
||||
impl GUI for GameState {
|
||||
fn modal_menus(&self) -> Vec<ModalMenu> {
|
||||
vec![
|
||||
ModalMenu::new(
|
||||
"Map Edit Mode",
|
||||
vec![
|
||||
(Key::Escape, "quit"),
|
||||
(Key::S, "save edits"),
|
||||
(Key::L, "load different edits"),
|
||||
],
|
||||
),
|
||||
ModalMenu::new(
|
||||
"Sandbox Mode",
|
||||
vec![
|
||||
(Key::Escape, "quit"),
|
||||
(Key::LeftBracket, "slow down sim"),
|
||||
(Key::RightBracket, "speed up sim"),
|
||||
(Key::O, "save sim state"),
|
||||
(Key::Y, "load previous sim state"),
|
||||
(Key::U, "load next sim state"),
|
||||
(Key::Space, "run/pause sim"),
|
||||
(Key::M, "run one step of sim"),
|
||||
(Key::X, "reset sim"),
|
||||
(Key::S, "seed the sim with agents"),
|
||||
// TODO Strange to always have this. Really it's a case of stacked modal?
|
||||
(Key::F, "stop following agent"),
|
||||
(Key::R, "stop showing agent's route"),
|
||||
// TODO This should probably be a debug thing instead
|
||||
(Key::L, "show/hide route for all agents"),
|
||||
(Key::A, "show/hide active traffic"),
|
||||
(Key::T, "start time traveling"),
|
||||
],
|
||||
),
|
||||
ModalMenu::new("Agent Spawner", vec![(Key::Escape, "quit")]),
|
||||
ModalMenu::new(
|
||||
"Time Traveler",
|
||||
vec![
|
||||
(Key::Escape, "quit"),
|
||||
(Key::Comma, "rewind"),
|
||||
(Key::Dot, "forwards"),
|
||||
],
|
||||
),
|
||||
ModalMenu::new(
|
||||
"Debug Mode",
|
||||
vec![
|
||||
@ -345,7 +306,7 @@ fn splash_screen(
|
||||
)?
|
||||
.as_str()
|
||||
{
|
||||
x if x == sandbox => break Some(Mode::Sandbox(SandboxMode::new())),
|
||||
x if x == sandbox => break Some(Mode::Sandbox(SandboxMode::new(ctx.canvas))),
|
||||
x if x == load_map => {
|
||||
let current_map = ui.primary.map.get_name().to_string();
|
||||
if let Some((name, _)) = wizard.choose_something_no_keys::<String>(
|
||||
@ -361,14 +322,14 @@ fn splash_screen(
|
||||
let mut flags = ui.primary.current_flags.clone();
|
||||
flags.sim_flags.load = PathBuf::from(format!("../data/maps/{}.abst", name));
|
||||
*ui = UI::new(flags, ctx.prerender, ctx.canvas);
|
||||
break Some(Mode::Sandbox(SandboxMode::new()));
|
||||
break Some(Mode::Sandbox(SandboxMode::new(ctx.canvas)));
|
||||
} else if wizard.aborted() {
|
||||
break Some(Mode::SplashScreen(Wizard::new(), maybe_screensaver.take()));
|
||||
} else {
|
||||
break None;
|
||||
}
|
||||
}
|
||||
x if x == edit => break Some(Mode::Edit(EditMode::new())),
|
||||
x if x == edit => break Some(Mode::Edit(EditMode::new(ctx))),
|
||||
x if x == tutorial => {
|
||||
break Some(Mode::Tutorial(TutorialMode::Part1(
|
||||
ctx.canvas.center_to_map_pt(),
|
||||
|
@ -48,7 +48,7 @@ impl ScenarioEditor {
|
||||
&mut ui.primary.current_flags.sim_flags.make_rng(),
|
||||
&mut Timer::new("instantiate scenario"),
|
||||
);
|
||||
return Some(Mode::Sandbox(SandboxMode::new()));
|
||||
return Some(Mode::Sandbox(SandboxMode::new(ctx.canvas)));
|
||||
} else if ctx.input.modal_action("visualize") {
|
||||
let neighborhoods = Neighborhood::load_all(
|
||||
ui.primary.map.get_name(),
|
||||
|
@ -8,7 +8,7 @@ use crate::game::{GameState, Mode};
|
||||
use crate::render::DrawOptions;
|
||||
use crate::ui::ShowEverything;
|
||||
use abstutil::elapsed_seconds;
|
||||
use ezgui::{EventCtx, EventLoopMode, GfxCtx, Key, Text, Wizard};
|
||||
use ezgui::{Canvas, EventCtx, EventLoopMode, GfxCtx, Key, NewModalMenu, Text, Wizard};
|
||||
use geom::Duration;
|
||||
use sim::{Benchmark, Sim, TripID};
|
||||
use std::time::Instant;
|
||||
@ -24,6 +24,7 @@ pub struct SandboxMode {
|
||||
state: State,
|
||||
// TODO Not while Spawning or TimeTraveling...
|
||||
common: CommonState,
|
||||
menu: NewModalMenu,
|
||||
}
|
||||
|
||||
enum State {
|
||||
@ -38,33 +39,44 @@ enum State {
|
||||
}
|
||||
|
||||
impl SandboxMode {
|
||||
pub fn new() -> SandboxMode {
|
||||
pub fn new(canvas: &Canvas) -> SandboxMode {
|
||||
SandboxMode {
|
||||
desired_speed: 1.0,
|
||||
state: State::Paused,
|
||||
following: None,
|
||||
route_viewer: route_viewer::RouteViewer::Inactive,
|
||||
show_activity: show_activity::ShowActivity::Inactive,
|
||||
time_travel: time_travel::TimeTravel::new(),
|
||||
time_travel: time_travel::TimeTravel::new(canvas),
|
||||
common: CommonState::new(),
|
||||
menu: NewModalMenu::hacky_new(
|
||||
"Sandbox Mode",
|
||||
vec![
|
||||
(Key::Escape, "quit"),
|
||||
(Key::LeftBracket, "slow down sim"),
|
||||
(Key::RightBracket, "speed up sim"),
|
||||
(Key::O, "save sim state"),
|
||||
(Key::Y, "load previous sim state"),
|
||||
(Key::U, "load next sim state"),
|
||||
(Key::Space, "run/pause sim"),
|
||||
(Key::M, "run one step of sim"),
|
||||
(Key::X, "reset sim"),
|
||||
(Key::S, "seed the sim with agents"),
|
||||
// TODO Strange to always have this. Really it's a case of stacked modal?
|
||||
(Key::F, "stop following agent"),
|
||||
(Key::R, "stop showing agent's route"),
|
||||
// TODO This should probably be a debug thing instead
|
||||
(Key::L, "show/hide route for all agents"),
|
||||
(Key::A, "show/hide active traffic"),
|
||||
(Key::T, "start time traveling"),
|
||||
],
|
||||
canvas,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn event(state: &mut GameState, ctx: &mut EventCtx) -> EventLoopMode {
|
||||
match state.mode {
|
||||
Mode::Sandbox(ref mut mode) => {
|
||||
ctx.canvas.handle_event(ctx.input);
|
||||
state.ui.primary.current_selection = state.ui.handle_mouseover(
|
||||
ctx,
|
||||
None,
|
||||
&state.ui.primary.sim,
|
||||
&ShowEverything::new(),
|
||||
false,
|
||||
);
|
||||
if let Some(evmode) = mode.common.event(ctx, &state.ui) {
|
||||
return evmode;
|
||||
}
|
||||
|
||||
if let State::Spawning(ref mut spawner) = mode.state {
|
||||
if spawner.event(ctx, &mut state.ui) {
|
||||
mode.state = State::Paused;
|
||||
@ -110,10 +122,24 @@ impl SandboxMode {
|
||||
txt.add_line("Showing active traffic".to_string());
|
||||
}
|
||||
}
|
||||
ctx.input
|
||||
.set_mode_with_new_prompt("Sandbox Mode", txt, ctx.canvas);
|
||||
mode.menu.update_prompt(txt, ctx);
|
||||
mode.menu.handle_event(ctx);
|
||||
|
||||
if let Some(spawner) = spawner::AgentSpawner::new(ctx, &mut state.ui) {
|
||||
ctx.canvas.handle_event(ctx.input);
|
||||
state.ui.primary.current_selection = state.ui.handle_mouseover(
|
||||
ctx,
|
||||
None,
|
||||
&state.ui.primary.sim,
|
||||
&ShowEverything::new(),
|
||||
false,
|
||||
);
|
||||
if let Some(evmode) = mode.common.event(ctx, &state.ui) {
|
||||
return evmode;
|
||||
}
|
||||
|
||||
if let Some(spawner) =
|
||||
spawner::AgentSpawner::new(ctx, &mut state.ui, &mut mode.menu)
|
||||
{
|
||||
mode.state = State::Spawning(spawner);
|
||||
return EventLoopMode::InputOnly;
|
||||
}
|
||||
@ -148,13 +174,13 @@ impl SandboxMode {
|
||||
// get_canonical_point_for_trip
|
||||
println!("{} is gone... temporarily or not?", trip);
|
||||
}
|
||||
if ctx.input.modal_action("stop following agent") {
|
||||
if mode.menu.action("stop following agent") {
|
||||
mode.following = None;
|
||||
}
|
||||
}
|
||||
mode.route_viewer.event(ctx, &mut state.ui);
|
||||
mode.show_activity.event(ctx, &mut state.ui);
|
||||
if ctx.input.modal_action("start time traveling") {
|
||||
mode.route_viewer.event(ctx, &mut state.ui, &mut mode.menu);
|
||||
mode.show_activity.event(ctx, &mut state.ui, &mut mode.menu);
|
||||
if mode.menu.action("start time traveling") {
|
||||
mode.state = State::TimeTraveling;
|
||||
mode.time_travel.start(state.ui.primary.sim.time());
|
||||
// Do this again, in case recording was previously disabled.
|
||||
@ -162,7 +188,7 @@ impl SandboxMode {
|
||||
return EventLoopMode::InputOnly;
|
||||
}
|
||||
|
||||
if ctx.input.modal_action("quit") {
|
||||
if mode.menu.action("quit") {
|
||||
// TODO This shouldn't be necessary when we plumb state around instead of
|
||||
// sharing it in the old structure.
|
||||
state.ui.primary.sim = Sim::new(
|
||||
@ -174,14 +200,14 @@ impl SandboxMode {
|
||||
return EventLoopMode::InputOnly;
|
||||
}
|
||||
|
||||
if ctx.input.modal_action("slow down sim") {
|
||||
if mode.menu.action("slow down sim") {
|
||||
mode.desired_speed -= ADJUST_SPEED;
|
||||
mode.desired_speed = mode.desired_speed.max(0.0);
|
||||
}
|
||||
if ctx.input.modal_action("speed up sim") {
|
||||
if mode.menu.action("speed up sim") {
|
||||
mode.desired_speed += ADJUST_SPEED;
|
||||
}
|
||||
if !state.ui.primary.sim.is_empty() && ctx.input.modal_action("reset sim") {
|
||||
if !state.ui.primary.sim.is_empty() && mode.menu.action("reset sim") {
|
||||
// TODO savestate_every gets lost
|
||||
state.ui.primary.sim = Sim::new(
|
||||
&state.ui.primary.map,
|
||||
@ -193,10 +219,10 @@ impl SandboxMode {
|
||||
|
||||
match mode.state {
|
||||
State::Paused => {
|
||||
if ctx.input.modal_action("save sim state") {
|
||||
if mode.menu.action("save sim state") {
|
||||
state.ui.primary.sim.save();
|
||||
}
|
||||
if ctx.input.modal_action("load previous sim state") {
|
||||
if mode.menu.action("load previous sim state") {
|
||||
let prev_state = state
|
||||
.ui
|
||||
.primary
|
||||
@ -215,7 +241,7 @@ impl SandboxMode {
|
||||
}
|
||||
}
|
||||
}
|
||||
if ctx.input.modal_action("load next sim state") {
|
||||
if mode.menu.action("load next sim state") {
|
||||
let next_state = state
|
||||
.ui
|
||||
.primary
|
||||
@ -233,13 +259,13 @@ impl SandboxMode {
|
||||
}
|
||||
}
|
||||
|
||||
if ctx.input.modal_action("run/pause sim") {
|
||||
if mode.menu.action("run/pause sim") {
|
||||
mode.state = State::Running {
|
||||
last_step: Instant::now(),
|
||||
benchmark: state.ui.primary.sim.start_benchmark(),
|
||||
speed: "...".to_string(),
|
||||
};
|
||||
} else if ctx.input.modal_action("run one step of sim") {
|
||||
} else if mode.menu.action("run one step of sim") {
|
||||
state.ui.primary.sim.step(&state.ui.primary.map);
|
||||
//*ctx.recalculate_current_selection = true;
|
||||
}
|
||||
@ -250,7 +276,7 @@ impl SandboxMode {
|
||||
ref mut benchmark,
|
||||
ref mut speed,
|
||||
} => {
|
||||
if ctx.input.modal_action("run/pause sim") {
|
||||
if mode.menu.action("run/pause sim") {
|
||||
mode.state = State::Paused;
|
||||
} else if ctx.input.nonblocking_is_update_event() {
|
||||
// TODO https://gafferongames.com/post/fix_your_timestep/
|
||||
@ -293,6 +319,7 @@ impl SandboxMode {
|
||||
&mode.time_travel,
|
||||
&ShowEverything::new(),
|
||||
);
|
||||
mode.time_travel.draw(g);
|
||||
}
|
||||
_ => {
|
||||
state.ui.draw(
|
||||
@ -302,6 +329,7 @@ impl SandboxMode {
|
||||
&ShowEverything::new(),
|
||||
);
|
||||
mode.common.draw(g, &state.ui);
|
||||
mode.menu.draw(g);
|
||||
mode.route_viewer.draw(g, &state.ui);
|
||||
mode.show_activity.draw(g, &state.ui);
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::helpers::ID;
|
||||
use crate::ui::UI;
|
||||
use ezgui::{Color, EventCtx, GfxCtx, Key};
|
||||
use ezgui::{Color, EventCtx, GfxCtx, Key, NewModalMenu};
|
||||
use geom::{Duration, PolyLine};
|
||||
use map_model::LANE_THICKNESS;
|
||||
use sim::{AgentID, TripID};
|
||||
@ -13,14 +13,14 @@ pub enum RouteViewer {
|
||||
}
|
||||
|
||||
impl RouteViewer {
|
||||
pub fn event(&mut self, ctx: &mut EventCtx, ui: &mut UI) {
|
||||
pub fn event(&mut self, ctx: &mut EventCtx, ui: &mut UI, menu: &mut NewModalMenu) {
|
||||
match self {
|
||||
RouteViewer::Inactive => {
|
||||
if let Some(agent) = ui.primary.current_selection.and_then(|id| id.agent_id()) {
|
||||
if let Some(trace) = ui.primary.sim.trace_route(agent, &ui.primary.map, None) {
|
||||
*self = RouteViewer::Hovering(ui.primary.sim.time(), agent, trace);
|
||||
}
|
||||
} else if ctx.input.modal_action("show/hide route for all agents") {
|
||||
} else if menu.action("show/hide route for all agents") {
|
||||
*self = debug_all_routes(ui);
|
||||
}
|
||||
}
|
||||
@ -56,14 +56,14 @@ impl RouteViewer {
|
||||
}
|
||||
RouteViewer::Active(time, trip, _) => {
|
||||
// TODO Using the modal menu from parent is weird...
|
||||
if ctx.input.modal_action("stop showing agent's route") {
|
||||
if menu.action("stop showing agent's route") {
|
||||
*self = RouteViewer::Inactive;
|
||||
} else if *time != ui.primary.sim.time() {
|
||||
*self = show_route(*trip, ui);
|
||||
}
|
||||
}
|
||||
RouteViewer::DebugAllRoutes(time, _) => {
|
||||
if ctx.input.modal_action("show/hide route for all agents") {
|
||||
if menu.action("show/hide route for all agents") {
|
||||
*self = RouteViewer::Inactive;
|
||||
} else if *time != ui.primary.sim.time() {
|
||||
*self = debug_all_routes(ui);
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::render::MIN_ZOOM_FOR_DETAIL;
|
||||
use crate::ui::UI;
|
||||
use ezgui::{Color, EventCtx, GfxCtx};
|
||||
use ezgui::{Color, EventCtx, GfxCtx, NewModalMenu};
|
||||
use geom::{Bounds, Duration, Polygon, Pt2D};
|
||||
use map_model::{RoadID, Traversable};
|
||||
use std::collections::HashMap;
|
||||
@ -12,18 +12,18 @@ pub enum ShowActivity {
|
||||
}
|
||||
|
||||
impl ShowActivity {
|
||||
pub fn event(&mut self, ctx: &mut EventCtx, ui: &mut UI) {
|
||||
pub fn event(&mut self, ctx: &mut EventCtx, ui: &mut UI, menu: &mut NewModalMenu) {
|
||||
let zoomed = ctx.canvas.cam_zoom >= MIN_ZOOM_FOR_DETAIL;
|
||||
|
||||
// If we survive past this, recompute current state.
|
||||
match self {
|
||||
ShowActivity::Inactive => {
|
||||
if !ctx.input.modal_action("show/hide active traffic") {
|
||||
if !menu.action("show/hide active traffic") {
|
||||
return;
|
||||
}
|
||||
}
|
||||
ShowActivity::Zoomed(time, ref heatmap) => {
|
||||
if ctx.input.modal_action("show/hide active traffic") {
|
||||
if menu.action("show/hide active traffic") {
|
||||
*self = ShowActivity::Inactive;
|
||||
return;
|
||||
}
|
||||
@ -35,7 +35,7 @@ impl ShowActivity {
|
||||
}
|
||||
}
|
||||
ShowActivity::Unzoomed(time, _) => {
|
||||
if ctx.input.modal_action("show/hide active traffic") {
|
||||
if menu.action("show/hide active traffic") {
|
||||
*self = ShowActivity::Inactive;
|
||||
return;
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ use crate::helpers::ID;
|
||||
use crate::render::DrawOptions;
|
||||
use crate::ui::{ShowEverything, UI};
|
||||
use abstutil::Timer;
|
||||
use ezgui::{EventCtx, GfxCtx, Key};
|
||||
use ezgui::{EventCtx, GfxCtx, Key, NewModalMenu};
|
||||
use geom::PolyLine;
|
||||
use map_model::{
|
||||
BuildingID, IntersectionID, IntersectionType, LaneType, PathRequest, Position, LANE_THICKNESS,
|
||||
@ -11,6 +11,7 @@ use rand::seq::SliceRandom;
|
||||
use sim::{DrivingGoal, Scenario, SidewalkSpot, TripSpec};
|
||||
|
||||
pub struct AgentSpawner {
|
||||
menu: NewModalMenu,
|
||||
from: Source,
|
||||
maybe_goal: Option<(Goal, Option<PolyLine>)>,
|
||||
}
|
||||
@ -28,7 +29,12 @@ enum Goal {
|
||||
}
|
||||
|
||||
impl AgentSpawner {
|
||||
pub fn new(ctx: &mut EventCtx, ui: &mut UI) -> Option<AgentSpawner> {
|
||||
pub fn new(
|
||||
ctx: &mut EventCtx,
|
||||
ui: &mut UI,
|
||||
sandbox_menu: &mut NewModalMenu,
|
||||
) -> Option<AgentSpawner> {
|
||||
let menu = NewModalMenu::new("Agent Spawner", vec![(Key::Escape, "quit")], ctx);
|
||||
let map = &ui.primary.map;
|
||||
match ui.primary.current_selection {
|
||||
Some(ID::Building(id)) => {
|
||||
@ -37,6 +43,7 @@ impl AgentSpawner {
|
||||
.contextual_action(Key::F3, "spawn a pedestrian starting here")
|
||||
{
|
||||
return Some(AgentSpawner {
|
||||
menu,
|
||||
from: Source::Walking(id),
|
||||
maybe_goal: None,
|
||||
});
|
||||
@ -50,6 +57,7 @@ impl AgentSpawner {
|
||||
.contextual_action(Key::F4, "spawn a car starting here")
|
||||
{
|
||||
return Some(AgentSpawner {
|
||||
menu,
|
||||
from: Source::Driving(
|
||||
b.front_path.sidewalk.equiv_pos(driving_lane, map),
|
||||
),
|
||||
@ -65,6 +73,7 @@ impl AgentSpawner {
|
||||
.contextual_action(Key::F3, "spawn an agent starting here")
|
||||
{
|
||||
return Some(AgentSpawner {
|
||||
menu,
|
||||
from: Source::Driving(Position::new(id, map.get_l(id).length() / 2.0)),
|
||||
maybe_goal: None,
|
||||
});
|
||||
@ -80,8 +89,7 @@ impl AgentSpawner {
|
||||
}
|
||||
None => {
|
||||
if ui.primary.sim.is_empty() {
|
||||
// TODO Weird. This mode belongs to the parent SpawnMode, not AgentSpawner.
|
||||
if ctx.input.modal_action("seed the sim with agents") {
|
||||
if sandbox_menu.action("seed the sim with agents") {
|
||||
Scenario::scaled_run(map, ui.primary.current_flags.num_agents).instantiate(
|
||||
&mut ui.primary.sim,
|
||||
map,
|
||||
@ -100,11 +108,15 @@ impl AgentSpawner {
|
||||
// Returns true if the spawner editor is done and we should go back to main sandbox mode.
|
||||
pub fn event(&mut self, ctx: &mut EventCtx, ui: &mut UI) -> bool {
|
||||
// TODO Instructions to select target building/lane
|
||||
ctx.input.set_mode("Agent Spawner", ctx.canvas);
|
||||
if ctx.input.modal_action("quit") {
|
||||
self.menu.handle_event(ctx);
|
||||
if self.menu.action("quit") {
|
||||
return true;
|
||||
}
|
||||
|
||||
ctx.canvas.handle_event(ctx.input);
|
||||
ui.primary.current_selection =
|
||||
ui.handle_mouseover(ctx, None, &ui.primary.sim, &ShowEverything::new(), false);
|
||||
|
||||
let map = &ui.primary.map;
|
||||
|
||||
let new_goal = match ui.primary.current_selection {
|
||||
@ -236,6 +248,8 @@ impl AgentSpawner {
|
||||
opts.override_colors.insert(src, ui.cs.get("selected"));
|
||||
ui.draw(g, opts, &ui.primary.sim, &ShowEverything::new());
|
||||
|
||||
self.menu.draw(g);
|
||||
|
||||
if let Some((_, Some(ref trace))) = self.maybe_goal {
|
||||
g.draw_polygon(ui.cs.get("route"), &trace.make_polygons(LANE_THICKNESS));
|
||||
}
|
||||
|
@ -1,12 +1,13 @@
|
||||
use crate::ui::UI;
|
||||
use abstutil::MultiMap;
|
||||
use ezgui::EventCtx;
|
||||
use ezgui::{Canvas, EventCtx, GfxCtx, Key, NewModalMenu, Text};
|
||||
use geom::Duration;
|
||||
use map_model::{Map, Traversable};
|
||||
use sim::{CarID, DrawCarInput, DrawPedestrianInput, GetDrawAgents, PedestrianID, TIMESTEP};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
pub struct TimeTravel {
|
||||
menu: NewModalMenu,
|
||||
// TODO Could be more efficient
|
||||
state_per_time: BTreeMap<Duration, StateAtTime>,
|
||||
pub current_time: Option<Duration>,
|
||||
@ -23,7 +24,7 @@ struct StateAtTime {
|
||||
}
|
||||
|
||||
impl TimeTravel {
|
||||
pub fn new() -> TimeTravel {
|
||||
pub fn new(canvas: &Canvas) -> TimeTravel {
|
||||
TimeTravel {
|
||||
state_per_time: BTreeMap::new(),
|
||||
current_time: None,
|
||||
@ -31,6 +32,15 @@ impl TimeTravel {
|
||||
first_time: Duration::ZERO,
|
||||
last_time: Duration::ZERO,
|
||||
should_record: false,
|
||||
menu: NewModalMenu::hacky_new(
|
||||
"Time Traveler",
|
||||
vec![
|
||||
(Key::Escape, "quit"),
|
||||
(Key::Comma, "rewind"),
|
||||
(Key::Dot, "forwards"),
|
||||
],
|
||||
canvas,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
@ -81,22 +91,27 @@ impl TimeTravel {
|
||||
// Returns true if done.
|
||||
pub fn event(&mut self, ctx: &mut EventCtx) -> bool {
|
||||
let time = self.current_time.unwrap();
|
||||
ctx.input.set_mode_with_prompt(
|
||||
"Time Traveler",
|
||||
format!("Time Traveler at {}", time),
|
||||
&ctx.canvas,
|
||||
);
|
||||
if time > self.first_time && ctx.input.modal_action("rewind") {
|
||||
self.menu.handle_event(ctx);
|
||||
self.menu
|
||||
.update_prompt(Text::prompt(&format!("Time Traveler at {}", time)), ctx);
|
||||
|
||||
ctx.canvas.handle_event(ctx.input);
|
||||
|
||||
if time > self.first_time && self.menu.action("rewind") {
|
||||
self.current_time = Some(time - TIMESTEP);
|
||||
} else if time < self.last_time && ctx.input.modal_action("forwards") {
|
||||
} else if time < self.last_time && self.menu.action("forwards") {
|
||||
self.current_time = Some(time + TIMESTEP);
|
||||
} else if ctx.input.modal_action("quit") {
|
||||
} else if self.menu.action("quit") {
|
||||
self.current_time = None;
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn draw(&self, g: &mut GfxCtx) {
|
||||
self.menu.draw(g);
|
||||
}
|
||||
|
||||
fn get_current_state(&self) -> &StateAtTime {
|
||||
&self.state_per_time[&self.current_time.unwrap()]
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::widgets::{Menu, Position};
|
||||
use crate::{EventCtx, GfxCtx, InputResult, Key, ScreenPt, Text};
|
||||
use crate::{Canvas, EventCtx, GfxCtx, InputResult, Key, ScreenPt, Text};
|
||||
|
||||
pub struct NewModalMenu {
|
||||
menu: Menu<Key>,
|
||||
@ -8,6 +8,15 @@ pub struct NewModalMenu {
|
||||
|
||||
impl NewModalMenu {
|
||||
pub fn new(prompt_line: &str, choices: Vec<(Key, &str)>, ctx: &EventCtx) -> NewModalMenu {
|
||||
NewModalMenu::hacky_new(prompt_line, choices, ctx.canvas)
|
||||
}
|
||||
|
||||
// TODO Pass EventCtx when constructing the GUI?
|
||||
pub fn hacky_new(
|
||||
prompt_line: &str,
|
||||
choices: Vec<(Key, &str)>,
|
||||
canvas: &Canvas,
|
||||
) -> NewModalMenu {
|
||||
let mut menu = Menu::new(
|
||||
Some(Text::prompt(prompt_line)),
|
||||
choices
|
||||
@ -17,7 +26,7 @@ impl NewModalMenu {
|
||||
false,
|
||||
true,
|
||||
Position::TopRightOfScreen,
|
||||
ctx.canvas,
|
||||
canvas,
|
||||
);
|
||||
menu.mark_all_inactive();
|
||||
NewModalMenu {
|
||||
@ -62,6 +71,11 @@ impl NewModalMenu {
|
||||
false
|
||||
}
|
||||
|
||||
pub fn update_prompt(&mut self, txt: Text, _: &EventCtx) {
|
||||
// TODO Do need to recalculate geometry
|
||||
self.menu.change_prompt(txt);
|
||||
}
|
||||
|
||||
pub fn draw(&self, g: &mut GfxCtx) {
|
||||
self.menu.draw(g);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user