declaring modal menus

This commit is contained in:
Dustin Carlino 2018-12-17 12:33:44 -08:00
parent 2194ea38a5
commit bbacdfb676
5 changed files with 125 additions and 11 deletions

View File

@ -1,6 +1,6 @@
use crate::objects::Ctx;
use crate::plugins::{Plugin, PluginCtx};
use ezgui::{Color, GfxCtx, Key, Text, TOP_RIGHT};
use ezgui::{Color, GfxCtx, Text, TOP_RIGHT};
use sim::{ScoreSummary, Tick};
pub enum ShowScoreState {
@ -18,15 +18,12 @@ impl Plugin for ShowScoreState {
fn ambient_event(&mut self, ctx: &mut PluginCtx) {
match self {
ShowScoreState::Inactive => {
if ctx.input.action_chosen("show sim info sidepanel") {
if ctx.input.action_chosen("show/hide sim info sidepanel") {
*self = panel(ctx);
}
}
ShowScoreState::Active(last_tick, _) => {
if ctx
.input
.key_pressed(Key::Dot, "Hide the sim info sidepanel")
{
if ctx.input.action_chosen("show/hide sim info sidepanel") {
*self = ShowScoreState::Inactive;
} else if *last_tick != ctx.primary.sim.time {
*self = panel(ctx);

View File

@ -5,7 +5,8 @@ use crate::objects::{Ctx, RenderingHints, ID};
use crate::render::RenderOptions;
use crate::state::UIState;
use ezgui::{
Canvas, Color, EventLoopMode, Folder, GfxCtx, Key, Text, TopMenu, UserInput, BOTTOM_LEFT, GUI,
Canvas, Color, EventLoopMode, Folder, GfxCtx, Key, ModalMenu, Text, TopMenu, UserInput,
BOTTOM_LEFT, GUI,
};
use kml;
use map_model::{BuildingID, LaneID};
@ -69,7 +70,7 @@ impl<S: UIState> GUI<RenderingHints> for UI<S> {
(Key::S, "seed the sim with agents"),
(Key::Space, "run/pause sim"),
(Key::M, "run one step of sim"),
(Key::Dot, "show sim info sidepanel"),
(Key::Dot, "show/hide sim info sidepanel"),
(Key::T, "start time traveling"),
],
),
@ -87,6 +88,81 @@ impl<S: UIState> GUI<RenderingHints> for UI<S> {
))
}
fn modal_menus() -> Vec<ModalMenu> {
vec![
ModalMenu::new(
"Traffic Signal Editor",
vec![
(Key::Enter, "quit"),
(Key::D, "change cycle duration"),
(Key::P, "choose a preset signal"),
(Key::K, "move current cycle up"),
(Key::J, "move current cycle down"),
(Key::Backspace, "delete current cycle"),
(Key::N, "add a new empty cycle"),
(Key::M, "add a new pedestrian scramble cycle"),
],
),
ModalMenu::new(
"Scenario Editor",
vec![(Key::Q, "save"), (Key::E, "edit"), (Key::I, "instantiate")],
),
ModalMenu::new("Road Editor", vec![(Key::Enter, "quit")]),
ModalMenu::new(
"Color Picker",
vec![
(Key::Backspace, "revert color"),
(Key::Enter, "finalize color"),
],
),
ModalMenu::new(
"Stop Sign Editor",
vec![(Key::Enter, "quit"), (Key::R, "reset to default")],
),
ModalMenu::new("A/B Test Editor", vec![(Key::R, "run A/B test")]),
ModalMenu::new(
"Neighborhood Editor",
vec![
// TODO one key for save XOR quit, based on internal state...
(Key::Enter, "save and quit"),
(Key::X, "export as an Osmosis polygon filter"),
(Key::P, "add a new point"),
],
),
ModalMenu::new(
"Time Traveler",
vec![
(Key::Enter, "quit"),
(Key::Comma, "rewind"),
(Key::Dot, "forwards"),
],
),
ModalMenu::new(
"Geometry Debugger",
vec![(Key::Enter, "quit"), (Key::N, "see next problem")],
),
ModalMenu::new("OSM Classifier", vec![(Key::Num6, "quit")]),
ModalMenu::new(
"Floodfiller",
vec![
(Key::Space, "step forwards"),
(Key::Tab, "finish floodfilling"),
],
),
ModalMenu::new("Chokepoints Debugger", vec![(Key::Enter, "quit")]),
ModalMenu::new("A/B Trip Explorer", vec![(Key::Enter, "quit")]),
ModalMenu::new("A/B All Trips Explorer", vec![(Key::Enter, "quit")]),
ModalMenu::new("Agent Follower", vec![(Key::F, "quit")]),
ModalMenu::new("Search", vec![(Key::Enter, "quit")]),
ModalMenu::new("Neighborhood Summaries", vec![(Key::Z, "quit")]),
ModalMenu::new(
"Agent Route Debugger",
vec![(Key::R, "quit"), (Key::L, "show/hide all agents' routes")],
),
ModalMenu::new("Active Traffic Visualizer", vec![(Key::A, "quit")]),
]
}
fn event(&mut self, input: &mut UserInput) -> (EventLoopMode, RenderingHints) {
let mut hints = RenderingHints {
mode: EventLoopMode::InputOnly,

View File

@ -2,7 +2,7 @@ use crate::menu::{Menu, Position};
use crate::top_menu::TopMenu;
use crate::{Canvas, Event, InputResult, Key, Text};
use geom::Pt2D;
use std::collections::{BTreeMap, HashMap};
use std::collections::{BTreeMap, HashMap, HashSet};
// As we check for user input, record the input and the thing that would happen. This will let us
// build up some kind of OSD of possible actions.
@ -400,3 +400,34 @@ impl UserInput {
}
}
}
pub struct ModalMenu {
pub(crate) name: String,
pub(crate) actions: Vec<(Key, String)>,
}
impl ModalMenu {
pub fn new(name: &str, raw_actions: Vec<(Key, &str)>) -> ModalMenu {
let mut keys: HashSet<Key> = HashSet::new();
let mut action_names: HashSet<String> = HashSet::new();
let mut actions = Vec::new();
for (key, action) in raw_actions {
if keys.contains(&key) {
panic!("ModalMenu {} uses {:?} twice", name, key);
}
keys.insert(key);
if action_names.contains(action) {
panic!("ModalMenu {} defines \"{}\" twice", name, action);
}
action_names.insert(action.to_string());
actions.push((key, action.to_string()));
}
ModalMenu {
name: name.to_string(),
actions,
}
}
}

View File

@ -18,7 +18,7 @@ pub use crate::canvas::{
};
pub use crate::color::Color;
pub use crate::event::{Event, Key};
pub use crate::input::UserInput;
pub use crate::input::{ModalMenu, UserInput};
pub use crate::log_scroller::LogScroller;
pub use crate::runner::{run, EventLoopMode, GUI};
pub use crate::scrolling_menu::ScrollingMenu;

View File

@ -1,9 +1,10 @@
use crate::input::ContextMenu;
use crate::{Canvas, Event, GfxCtx, TopMenu, UserInput};
use crate::{Canvas, Event, GfxCtx, ModalMenu, TopMenu, UserInput};
use glutin_window::GlutinWindow;
use opengl_graphics::{GlGraphics, OpenGL};
use piston::event_loop::{EventLoop, EventSettings, Events};
use piston::window::{Window, WindowSettings};
use std::collections::HashMap;
use std::{panic, process};
pub trait GUI<T> {
@ -11,6 +12,9 @@ pub trait GUI<T> {
fn top_menu(_canvas: &Canvas) -> Option<TopMenu> {
None
}
fn modal_menus() -> Vec<ModalMenu> {
Vec::new()
}
fn event(&mut self, input: &mut UserInput) -> (EventLoopMode, T);
fn get_mut_canvas(&mut self) -> &mut Canvas;
fn draw(&self, g: &mut GfxCtx, data: &T);
@ -38,10 +42,16 @@ pub fn run<T, G: GUI<T>>(mut gui: G, window_title: &str, initial_width: u32, ini
let mut events = Events::new(EventSettings::new().lazy(true));
let mut gl = GlGraphics::new(opengl);
// TODO Probably time to bundle this state up. :)
let mut last_event_mode = EventLoopMode::InputOnly;
let mut context_menu = ContextMenu::Inactive;
let mut top_menu = G::top_menu(gui.get_mut_canvas());
let modal_menus: HashMap<String, ModalMenu> = G::modal_menus()
.into_iter()
.map(|m| (m.name.clone(), m))
.collect();
let mut last_data: Option<T> = None;
while let Some(ev) = events.next(&mut window) {
use piston::input::{CloseEvent, RenderEvent};
if let Some(args) = ev.render_args() {