add in a shim to collect contextual actions. just silently pass-through to old behavior for now.

This commit is contained in:
Dustin Carlino 2019-12-11 15:04:32 -08:00
parent 14f4dc2e3c
commit 061aed2624
22 changed files with 127 additions and 142 deletions

View File

@ -176,9 +176,9 @@ impl State for ABTestMode {
.and_then(|id| id.agent_id())
{
if let Some(trip) = ui.primary.sim.agent_to_trip(agent) {
if ctx
.input
.contextual_action(Key::B, format!("Show {}'s parallel world", agent))
if ui
.per_obj
.action(ctx, Key::B, format!("Show {}'s parallel world", agent))
{
self.diff_trip = Some(DiffOneTrip::new(
trip,

View File

@ -39,10 +39,7 @@ impl AgentTools {
.and_then(|id| id.agent_id())
{
if let Some(trip) = ui.primary.sim.agent_to_trip(agent) {
if ctx
.input
.contextual_action(Key::F, format!("follow {}", agent))
{
if ui.per_obj.action(ctx, Key::F, format!("follow {}", agent)) {
self.following = Some((
trip,
ui.primary
@ -123,10 +120,7 @@ impl AgentTools {
.and_then(|id| id.agent_id())
.and_then(|agent| ui.primary.sim.agent_to_trip(agent))
{
if ctx
.input
.contextual_action(Key::T, format!("explore {}", trip))
{
if ui.per_obj.action(ctx, Key::T, format!("explore {}", trip)) {
return Some(Transition::Push(Box::new(TripExplorer::new(trip, ctx, ui))));
}
}

View File

@ -73,7 +73,7 @@ impl CommonState {
}
if let Some(ref id) = ui.primary.current_selection {
if ctx.input.contextual_action(Key::I, "info") || ctx.normal_left_click() {
if ui.per_obj.action(ctx, Key::I, "info") || ctx.normal_left_click() {
return Some(Transition::Push(Box::new(info::InfoPanel::new(
id.clone(),
ui,

View File

@ -20,7 +20,7 @@ impl RouteExplorer {
.and_then(|id| id.agent_id())?;
let path = ui.primary.sim.get_path(agent)?.clone();
if !ctx.input.contextual_action(Key::E, "explore route") {
if !ui.per_obj.action(ctx, Key::E, "explore route") {
return None;
}

View File

@ -52,9 +52,9 @@ impl RouteViewer {
if let RouteViewer::Hovering(_, agent, _) = self {
// If there's a current route, then there must be a trip.
let trip = ui.primary.sim.agent_to_trip(*agent).unwrap();
if ctx
.input
.contextual_action(Key::R, format!("show {}'s route", agent))
if ui
.per_obj
.action(ctx, Key::R, format!("show {}'s route", agent))
{
*self = show_route(trip, ui, ctx);
menu.push_action(hotkey(Key::R), "stop showing agent's route", ctx);

View File

@ -18,17 +18,17 @@ impl TurnCyclerState {
if let TurnCyclerState::CycleTurns(current, idx) = self {
if *current != id {
*self = TurnCyclerState::ShowLane(id);
} else if ctx
.input
.contextual_action(Key::Z, "cycle through this lane's turns")
} else if ui
.per_obj
.action(ctx, Key::Z, "cycle through this lane's turns")
{
*self = TurnCyclerState::CycleTurns(id, *idx + 1);
}
} else {
*self = TurnCyclerState::ShowLane(id);
if ctx
.input
.contextual_action(Key::Z, "cycle through this lane's turns")
if ui
.per_obj
.action(ctx, Key::Z, "cycle through this lane's turns")
{
*self = TurnCyclerState::CycleTurns(id, 0);
}
@ -36,9 +36,9 @@ impl TurnCyclerState {
}
Some(ID::Intersection(i)) => {
if let Some(ref signal) = ui.primary.map.maybe_get_traffic_signal(i) {
if ctx
.input
.contextual_action(Key::F, "show full traffic signal diagram")
if ui
.per_obj
.action(ctx, Key::F, "show full traffic signal diagram")
{
ui.primary.current_selection = None;
let (idx, _, _) =

View File

@ -20,14 +20,11 @@ impl Floodfiller {
if !lt.supports_any_movement() {
return None;
}
if ctx
.input
.contextual_action(Key::F, "floodfill from this lane")
{
if ui.per_obj.action(ctx, Key::F, "floodfill from this lane") {
find_reachable_from(l, map)
} else if ctx
.input
.contextual_action(Key::S, "show strongly-connected components")
} else if ui
.per_obj
.action(ctx, Key::S, "show strongly-connected components")
{
let constraints = PathConstraints::from_lt(lt);
let (good, bad) = connectivity::find_scc(map, constraints);

View File

@ -188,10 +188,7 @@ impl State for DebugMode {
match ui.primary.current_selection {
Some(ID::Lane(_)) | Some(ID::Intersection(_)) | Some(ID::ExtraShape(_)) => {
let id = ui.primary.current_selection.clone().unwrap();
if ctx
.input
.contextual_action(Key::H, format!("hide {:?}", id))
{
if ui.per_obj.action(ctx, Key::H, format!("hide {:?}", id)) {
println!("Hiding {:?}", id);
ui.primary.current_selection = None;
if self.hidden.is_empty() {
@ -212,17 +209,14 @@ impl State for DebugMode {
}
if let Some(ID::Car(id)) = ui.primary.current_selection {
if ctx
.input
.contextual_action(Key::Backspace, "forcibly kill this car")
if ui
.per_obj
.action(ctx, Key::Backspace, "forcibly kill this car")
{
ui.primary.sim.kill_stuck_car(id, &ui.primary.map);
ui.primary.sim.step(&ui.primary.map, Duration::seconds(0.1));
ui.primary.current_selection = None;
} else if ctx
.input
.contextual_action(Key::G, "find front of blockage")
{
} else if ui.per_obj.action(ctx, Key::G, "find front of blockage") {
return Transition::Push(msg(
"Blockage results",
vec![format!(

View File

@ -26,7 +26,7 @@ impl ObjectDebugger {
}
if let Some(ref id) = ui.primary.current_selection {
if ctx.input.contextual_action(Key::D, "debug") {
if ui.per_obj.action(ctx, Key::D, "debug") {
dump_debug(
id.clone(),
&ui.primary.map,

View File

@ -22,9 +22,9 @@ impl PolygonDebugger {
match ui.primary.current_selection {
Some(ID::Intersection(id)) => {
let i = ui.primary.map.get_i(id);
if ctx
.input
.contextual_action(Key::X, "debug intersection geometry")
if ui
.per_obj
.action(ctx, Key::X, "debug intersection geometry")
{
let pts = i.polygon.points();
let mut pts_without_last = pts.clone();
@ -40,10 +40,7 @@ impl PolygonDebugger {
),
center: Some(Pt2D::center(&pts_without_last)),
});
} else if ctx
.input
.contextual_action(Key::F2, "debug sidewalk corners")
{
} else if ui.per_obj.action(ctx, Key::F2, "debug sidewalk corners") {
return Some(PolygonDebugger {
slider: WarpingItemSlider::new(
calculate_corners(
@ -63,7 +60,7 @@ impl PolygonDebugger {
}
}
Some(ID::Lane(id)) => {
if ctx.input.contextual_action(Key::X, "debug lane geometry") {
if ui.per_obj.action(ctx, Key::X, "debug lane geometry") {
return Some(PolygonDebugger {
slider: WarpingItemSlider::new(
ui.primary
@ -80,7 +77,10 @@ impl PolygonDebugger {
),
center: None,
});
} else if ctx.input.contextual_action(Key::F2, "debug lane triangles") {
} else if ui
.per_obj
.action(ctx, Key::F2, "debug lane triangles geometry")
{
return Some(PolygonDebugger {
slider: WarpingItemSlider::new(
ui.primary
@ -106,7 +106,7 @@ impl PolygonDebugger {
}
}
Some(ID::Area(id)) => {
if ctx.input.contextual_action(Key::X, "debug area geometry") {
if ui.per_obj.action(ctx, Key::X, "debug area geometry") {
let pts = &ui.primary.map.get_a(id).polygon.points();
let center = if pts[0] == *pts.last().unwrap() {
// TODO The center looks really wrong for Volunteer Park and others, but I
@ -126,7 +126,7 @@ impl PolygonDebugger {
),
center: Some(center),
});
} else if ctx.input.contextual_action(Key::F2, "debug area triangles") {
} else if ui.per_obj.action(ctx, Key::F2, "debug area triangles") {
return Some(PolygonDebugger {
slider: WarpingItemSlider::new(
ui.primary

View File

@ -160,10 +160,7 @@ impl LaneEditor {
if let Some(ID::Lane(l)) = ui.primary.current_selection {
if let Some(idx) = self.active_idx {
if ctx
.input
.contextual_action(Key::Space, &self.brushes[idx].label)
{
if ui.per_obj.action(ctx, Key::Space, &self.brushes[idx].label) {
// These errors are universal.
if ui.primary.map.get_l(l).is_sidewalk() {
return Some(Transition::Push(msg(
@ -192,15 +189,15 @@ impl LaneEditor {
}
}
if ctx
.input
.contextual_action(Key::U, "bulk edit lanes on this road")
if ui
.per_obj
.action(ctx, Key::U, "bulk edit lanes on this road")
{
return Some(Transition::Push(make_bulk_edit_lanes(
ui.primary.map.get_l(l).parent,
)));
} else if let Some(lt) = ui.primary.map.get_edits().original_lts.get(&l) {
if ctx.input.contextual_action(Key::R, "revert") {
if ui.per_obj.action(ctx, Key::R, "revert") {
if let Some(err) = can_change_lane_type(l, *lt, &ui.primary.map) {
return Some(Transition::Push(msg("Error", vec![err])));
}
@ -214,7 +211,7 @@ impl LaneEditor {
apply_map_edits(&mut ui.primary, &ui.cs, ctx, edits);
}
} else if ui.primary.map.get_edits().reversed_lanes.contains(&l) {
if ctx.input.contextual_action(Key::R, "revert") {
if ui.per_obj.action(ctx, Key::R, "revert") {
match (self.brushes[self.reverse_idx].apply)(&ui.primary.map, l) {
Ok(Some(cmd)) => {
let mut edits = ui.primary.map.get_edits().clone();
@ -232,9 +229,9 @@ impl LaneEditor {
// Woo, a special case! The construction tool also applies to intersections.
if let Some(ID::Intersection(i)) = ui.primary.current_selection {
if self.active_idx == Some(self.construction_idx)
&& ctx
.input
.contextual_action(Key::Space, &self.brushes[self.construction_idx].label)
&& ui
.per_obj
.action(ctx, Key::Space, &self.brushes[self.construction_idx].label)
{
let it = ui.primary.map.get_i(i).intersection_type;
if it != IntersectionType::Construction && it != IntersectionType::Border {

View File

@ -188,9 +188,9 @@ impl State for EditMode {
if let Some(ID::Intersection(id)) = ui.primary.current_selection {
if ui.primary.map.maybe_get_stop_sign(id).is_some() {
if self.mode.can_edit_stop_signs()
&& ctx
.input
.contextual_action(Key::E, format!("edit stop signs for {}", id))
&& ui
.per_obj
.action(ctx, Key::E, format!("edit stop signs for {}", id))
{
return Transition::Push(Box::new(stop_signs::StopSignEditor::new(
id, ctx, ui,
@ -201,7 +201,7 @@ impl State for EditMode {
.get_edits()
.changed_intersections
.contains(&id)
&& ctx.input.contextual_action(Key::R, "revert")
&& ui.per_obj.action(ctx, Key::R, "revert")
{
let mut edits = ui.primary.map.get_edits().clone();
edits
@ -214,9 +214,9 @@ impl State for EditMode {
}
}
if ui.primary.map.maybe_get_traffic_signal(id).is_some() {
if ctx
.input
.contextual_action(Key::E, format!("edit traffic signal for {}", id))
if ui
.per_obj
.action(ctx, Key::E, format!("edit traffic signal for {}", id))
{
return Transition::Push(Box::new(traffic_signals::TrafficSignalEditor::new(
id, ctx, ui,
@ -227,7 +227,7 @@ impl State for EditMode {
.get_edits()
.changed_intersections
.contains(&id)
&& ctx.input.contextual_action(Key::R, "revert")
&& ui.per_obj.action(ctx, Key::R, "revert")
{
let mut edits = ui.primary.map.get_edits().clone();
edits
@ -240,8 +240,7 @@ impl State for EditMode {
apply_map_edits(&mut ui.primary, &ui.cs, ctx, edits);
}
}
if ui.primary.map.get_i(id).is_closed() && ctx.input.contextual_action(Key::R, "revert")
{
if ui.primary.map.get_i(id).is_closed() && ui.per_obj.action(ctx, Key::R, "revert") {
let mut edits = ui.primary.map.get_edits().clone();
edits
.commands

View File

@ -67,7 +67,7 @@ impl State for StopSignEditor {
}
if let Some(r) = self.selected_sign {
if ctx.input.contextual_action(Key::Space, "toggle stop sign") {
if ui.per_obj.action(ctx, Key::Space, "toggle stop sign") {
let mut sign = ui.primary.map.get_stop_sign(self.id).clone();
sign.flip_sign(r);

View File

@ -110,7 +110,8 @@ impl State for TrafficSignalEditor {
}
};
if let Some(pri) = next_priority {
if ctx.input.contextual_action(
if ui.per_obj.action(
ctx,
Key::Space,
format!(
"toggle from {:?} to {:?}",

View File

@ -12,8 +12,8 @@ use ezgui::{
// top-level game states.
pub struct Game {
// A stack of states
pub states: Vec<Box<dyn State>>,
pub ui: UI,
states: Vec<Box<dyn State>>,
ui: UI,
}
impl Game {

View File

@ -7,6 +7,7 @@ mod game;
mod helpers;
mod managed;
mod mission;
mod obj_actions;
mod options;
mod pregame;
mod render;

View File

@ -203,7 +203,7 @@ impl State for ScenarioManager {
let from = self.trips_from_bldg.get(b);
let to = self.trips_to_bldg.get(b);
if !from.is_empty() || !to.is_empty() {
if ctx.input.contextual_action(Key::T, "browse trips") {
if ui.per_obj.action(ctx, Key::T, "browse trips") {
// TODO Avoid the clone? Just happens once though.
let mut all_trips = from.clone();
all_trips.extend(to);
@ -215,9 +215,7 @@ impl State for ScenarioManager {
OD::Bldg(b),
));
} else if self.demand.is_none()
&& ctx
.input
.contextual_action(Key::P, "show trips to and from")
&& ui.per_obj.action(ctx, Key::P, "show trips to and from")
{
self.demand = Some(show_demand(&self.scenario, from, to, OD::Bldg(b), ui, ctx));
self.menu
@ -228,7 +226,7 @@ impl State for ScenarioManager {
let from = self.trips_from_border.get(i);
let to = self.trips_to_border.get(i);
if !from.is_empty() || !to.is_empty() {
if ctx.input.contextual_action(Key::T, "browse trips") {
if ui.per_obj.action(ctx, Key::T, "browse trips") {
// TODO Avoid the clone? Just happens once though.
let mut all_trips = from.clone();
all_trips.extend(to);
@ -240,9 +238,7 @@ impl State for ScenarioManager {
OD::Border(i),
));
} else if self.demand.is_none()
&& ctx
.input
.contextual_action(Key::P, "show trips to and from")
&& ui.per_obj.action(ctx, Key::P, "show trips to and from")
{
self.demand = Some(show_demand(
&self.scenario,

13
game/src/obj_actions.rs Normal file
View File

@ -0,0 +1,13 @@
use ezgui::{EventCtx, Key};
pub struct PerObjectActions {}
impl PerObjectActions {
pub fn new() -> PerObjectActions {
PerObjectActions {}
}
pub fn action<S: Into<String>>(&self, ctx: &mut EventCtx, key: Key, label: S) -> bool {
ctx.input.contextual_action(key, label)
}
}

View File

@ -100,7 +100,7 @@ impl BusRouteExplorer {
if routes.is_empty() {
return None;
}
if !ctx.input.contextual_action(Key::E, "explore bus route") {
if !ui.per_obj.action(ctx, Key::E, "explore bus route") {
return None;
}
if routes.len() == 1 {

View File

@ -47,11 +47,7 @@ impl AgentSpawner {
match ui.primary.current_selection {
Some(ID::Building(id)) => {
let spots = ui.primary.sim.get_free_offstreet_spots(id);
if !spots.is_empty()
&& ctx
.input
.contextual_action(Key::F6, "seed a parked car here")
{
if !spots.is_empty() && ui.per_obj.action(ctx, Key::F6, "seed a parked car here") {
let mut rng = ui.primary.current_flags.sim_flags.make_rng();
ui.primary.sim.seed_parked_car(
Scenario::rand_car(&mut rng),
@ -60,10 +56,11 @@ impl AgentSpawner {
);
return None;
}
if ctx
.input
.contextual_action(Key::F3, "spawn a pedestrian starting here just walking")
{
if ui.per_obj.action(
ctx,
Key::F3,
"spawn a pedestrian starting here just walking",
) {
return Some(Box::new(AgentSpawner {
menu: ModalMenu::new(
"Agent Spawner",
@ -77,7 +74,8 @@ impl AgentSpawner {
let parked = ui.primary.sim.get_parked_cars_by_owner(id);
// TODO Check if it's claimed... Haha if it is, MaybeUsingParkedCar still snags it!
if !parked.is_empty()
&& ctx.input.contextual_action(
&& ui.per_obj.action(
ctx,
Key::F5,
"spawn a pedestrian here using an owned parked car",
)
@ -93,10 +91,7 @@ impl AgentSpawner {
}));
}
if let Some(pos) = Position::bldg_via_driving(id, map) {
if ctx
.input
.contextual_action(Key::F4, "spawn a car starting here")
{
if ui.per_obj.action(ctx, Key::F4, "spawn a car starting here") {
return Some(Box::new(AgentSpawner {
menu: ModalMenu::new(
"Agent Spawner",
@ -109,9 +104,9 @@ impl AgentSpawner {
}
}
if let Some(pos) = Position::bldg_via_biking(id, map) {
if ctx
.input
.contextual_action(Key::F7, "spawn a bike starting here")
if ui
.per_obj
.action(ctx, Key::F7, "spawn a bike starting here")
{
return Some(Box::new(AgentSpawner {
menu: ModalMenu::new(
@ -127,9 +122,7 @@ impl AgentSpawner {
}
Some(ID::Lane(id)) => {
if map.get_l(id).is_driving()
&& ctx
.input
.contextual_action(Key::F3, "spawn a car starting here")
&& ui.per_obj.action(ctx, Key::F3, "spawn a car starting here")
{
return Some(Box::new(AgentSpawner {
menu: ModalMenu::new(
@ -141,9 +134,9 @@ impl AgentSpawner {
maybe_goal: None,
}));
} else if map.get_l(id).is_sidewalk()
&& ctx
.input
.contextual_action(Key::F3, "spawn a pedestrian starting here")
&& ui
.per_obj
.action(ctx, Key::F3, "spawn a pedestrian starting here")
{
return Some(Box::new(AgentSpawner {
menu: ModalMenu::new(
@ -160,9 +153,9 @@ impl AgentSpawner {
}
}
Some(ID::Intersection(i)) => {
if ctx
.input
.contextual_action(Key::Z, "spawn agents around this intersection")
if ui
.per_obj
.action(ctx, Key::Z, "spawn agents around this intersection")
{
spawn_agents_around(i, ui, ctx);
}
@ -253,7 +246,7 @@ impl State for AgentSpawner {
}
}
if self.maybe_goal.is_some() && ctx.input.contextual_action(Key::F3, "end the agent here") {
if self.maybe_goal.is_some() && ui.per_obj.action(ctx, Key::F3, "end the agent here") {
let mut rng = ui.primary.current_flags.sim_flags.make_rng();
let sim = &mut ui.primary.sim;
let err = schedule_trip(
@ -504,9 +497,9 @@ impl SpawnManyAgents {
pub fn new(ctx: &mut EventCtx, ui: &mut UI) -> Option<Box<dyn State>> {
if let Some(ID::Lane(l)) = ui.primary.current_selection {
if ui.primary.map.get_l(l).is_driving()
&& ctx
.input
.contextual_action(Key::F2, "spawn many cars starting here")
&& ui
.per_obj
.action(ctx, Key::F2, "spawn many cars starting here")
{
return Some(Box::new(SpawnManyAgents {
menu: ModalMenu::new(
@ -581,7 +574,7 @@ impl State for SpawnManyAgents {
if self.maybe_goal.is_some()
&& self.schedule.is_none()
&& ctx.input.contextual_action(Key::F2, "end the swarm here")
&& ui.per_obj.action(ctx, Key::F2, "end the swarm here")
{
return Transition::Push(WizardState::new(Box::new(move |wiz, ctx, _| {
let mut wizard = wiz.wrap(ctx);

View File

@ -84,13 +84,10 @@ impl State for SandboxMode {
fn event(&mut self, ctx: &mut EventCtx, ui: &mut UI) -> Transition {
{
let mut txt = Text::new();
txt.add(Line(""));
{
let edits = ui.primary.map.get_edits();
txt.add(Line(format!("Edits: {}", edits.edits_name)));
if edits.dirty {
txt.append(Line("*"));
}
let edits = ui.primary.map.get_edits();
txt.add(Line(format!("Edits: {}", edits.edits_name)));
if edits.dirty {
txt.append(Line("*"));
}
self.menu.set_info(ctx, txt);
}
@ -209,9 +206,11 @@ impl State for SandboxMode {
.map(|p| p.vehicle.id)
.collect::<Vec<_>>();
if !cars.is_empty()
&& ctx
.input
.contextual_action(Key::P, format!("examine {} cars parked here", cars.len()))
&& ui.per_obj.action(
ctx,
Key::P,
format!("examine {} cars parked here", cars.len()),
)
{
return Transition::Push(WizardState::new(Box::new(move |wiz, ctx, _| {
let _id = wiz.wrap(ctx).choose("Examine which car?", || {
@ -224,9 +223,9 @@ impl State for SandboxMode {
}
}
if let Some(ID::Lane(l)) = ui.primary.current_selection {
if ctx
.input
.contextual_action(Key::T, "throughput over 1-hour buckets")
if ui
.per_obj
.action(ctx, Key::T, "throughput over 1-hour buckets")
{
let r = ui.primary.map.get_l(l).parent;
let bucket = Duration::hours(1);
@ -234,20 +233,17 @@ impl State for SandboxMode {
}
}
if let Some(ID::Intersection(i)) = ui.primary.current_selection {
if ctx
.input
.contextual_action(Key::T, "throughput over 1-hour buckets")
if ui
.per_obj
.action(ctx, Key::T, "throughput over 1-hour buckets")
{
let bucket = Duration::hours(1);
self.overlay = Overlays::intersection_throughput(i, bucket, ctx, ui);
} else if ctx
.input
.contextual_action(Key::D, "delay over 1-hour buckets")
{
} else if ui.per_obj.action(ctx, Key::D, "delay over 1-hour buckets") {
let bucket = Duration::hours(1);
self.overlay = Overlays::intersection_delay_over_time(i, bucket, ctx, ui);
} else if ui.primary.map.get_i(i).is_traffic_signal()
&& ctx.input.contextual_action(Key::E, "show current demand")
&& ui.per_obj.action(ctx, Key::E, "show current demand")
{
self.overlay = Overlays::intersection_demand(i, ctx, ui);
}

View File

@ -1,4 +1,5 @@
use crate::helpers::{ColorScheme, ID};
use crate::obj_actions::PerObjectActions;
use crate::options::Options;
use crate::render::{
draw_vehicle, AgentCache, AgentColorScheme, DrawCtx, DrawMap, DrawOptions, DrawPedCrowd,
@ -18,6 +19,8 @@ pub struct UI {
pub cs: ColorScheme,
pub agent_cs: AgentColorScheme,
pub opts: Options,
pub per_obj: PerObjectActions,
}
impl UI {
@ -77,6 +80,7 @@ impl UI {
cs,
agent_cs: AgentColorScheme::ByID,
opts,
per_obj: PerObjectActions::new(),
}
}