populate OSD during event. this lets wizard display prompts easily.

This commit is contained in:
Dustin Carlino 2018-09-20 14:37:49 -07:00
parent c95f5e7a4d
commit b365f595bd
9 changed files with 110 additions and 117 deletions

View File

@ -25,7 +25,13 @@ impl DrawPolygonState {
DrawPolygonState::Empty
}
pub fn event(&mut self, input: &mut UserInput, canvas: &Canvas, map: &Map) -> bool {
pub fn event(
&mut self,
input: &mut UserInput,
canvas: &Canvas,
map: &Map,
osd: &mut TextOSD,
) -> bool {
let mut new_state: Option<DrawPolygonState> = None;
match self {
DrawPolygonState::Empty => {
@ -38,6 +44,9 @@ impl DrawPolygonState {
}
}
DrawPolygonState::DrawingPoints(ref mut pts, ref mut current_idx, name) => {
osd.pad_if_nonempty();
osd.add_line(format!("Currently editing {}", name));
if input.key_pressed(Key::Tab, "list existing polygons") {
let (names, polygons) = load_all_polygons(map.get_name());
if names.is_empty() {
@ -80,6 +89,9 @@ impl DrawPolygonState {
}
}
DrawPolygonState::MovingPoint(ref mut pts, idx, name) => {
osd.pad_if_nonempty();
osd.add_line(format!("Currently editing {}", name));
pts[*idx] = canvas.get_cursor_in_map_space();
if let Some(Button::Keyboard(Key::LCtrl)) =
input.use_event_directly().release_args()
@ -103,6 +115,9 @@ impl DrawPolygonState {
).expect("Saving polygon selection failed");
println!("Saved {}", path);
new_state = Some(DrawPolygonState::Empty);
} else {
osd.pad_if_nonempty();
tb.populate_osd(osd);
}
input.consume_event();
}
@ -131,21 +146,6 @@ impl DrawPolygonState {
}
}
pub fn populate_osd(&self, osd: &mut TextOSD) {
match self {
DrawPolygonState::NamingPolygon(tb, _) => {
osd.pad_if_nonempty();
tb.populate_osd(osd);
}
DrawPolygonState::DrawingPoints(_, _, name)
| DrawPolygonState::MovingPoint(_, _, name) => {
osd.pad_if_nonempty();
osd.add_line(format!("Currently editing {}", name));
}
_ => {}
}
}
pub fn draw(&self, g: &mut GfxCtx, canvas: &Canvas) {
// TODO add colorscheme entries
let red = [1.0, 0.0, 0.0, 1.0];

View File

@ -26,7 +26,7 @@ impl SearchState {
None
}
pub fn event(&mut self, input: &mut UserInput) -> bool {
pub fn event(&mut self, input: &mut UserInput, osd: &mut TextOSD) -> bool {
let mut new_state: Option<SearchState> = None;
match self {
SearchState::Empty => {
@ -37,6 +37,9 @@ impl SearchState {
SearchState::EnteringSearch(tb) => {
if tb.event(input.use_event_directly()) {
new_state = Some(SearchState::FilterOSM(tb.line.clone()));
} else {
osd.pad_if_nonempty();
tb.populate_osd(osd);
}
input.consume_event();
}
@ -57,13 +60,6 @@ impl SearchState {
_ => true,
}
}
pub fn populate_osd(&self, osd: &mut TextOSD) {
if let SearchState::EnteringSearch(tb) = self {
osd.pad_if_nonempty();
tb.populate_osd(osd);
}
}
}
impl Colorizer for SearchState {

View File

@ -35,6 +35,7 @@ impl SimController {
control_map: &ControlMap,
sim: &mut Sim,
selected: Option<ID>,
osd: &mut TextOSD,
) -> EventLoopMode {
if input.unimportant_key_pressed(Key::S, "Seed the map with agents") {
sim.small_spawn(map);
@ -106,19 +107,18 @@ impl SimController {
}
}
}
if self.last_step.is_some() {
EventLoopMode::Animation
} else {
EventLoopMode::InputOnly
}
}
pub fn populate_osd(&self, sim: &Sim, osd: &mut TextOSD) {
osd.pad_if_nonempty();
osd.add_line(sim.summary());
osd.add_line(format!(
"Speed: {0} / desired {1:.2}x",
self.sim_speed, self.desired_speed
));
if self.last_step.is_some() {
EventLoopMode::Animation
} else {
EventLoopMode::InputOnly
}
}
}

View File

@ -20,6 +20,7 @@ impl WarpState {
sim: &Sim,
canvas: &mut Canvas,
selected: &mut Option<ID>,
osd: &mut TextOSD,
) -> bool {
let mut new_state: Option<WarpState> = None;
match self {
@ -33,6 +34,9 @@ impl WarpState {
if tb.event(input.use_event_directly()) {
warp(tb.line.clone(), map, sim, canvas, selected);
new_state = Some(WarpState::Empty);
} else {
osd.pad_if_nonempty();
tb.populate_osd(osd);
}
input.consume_event();
}
@ -45,13 +49,6 @@ impl WarpState {
_ => true,
}
}
pub fn populate_osd(&self, osd: &mut TextOSD) {
if let WarpState::EnteringSearch(tb) = self {
osd.pad_if_nonempty();
tb.populate_osd(osd);
}
}
}
impl Colorizer for WarpState {}

View File

@ -1,9 +1,9 @@
use ezgui::{GfxCtx, Menu, MenuResult, TextBox, TextOSD, UserInput, Canvas};
use ezgui::{Canvas, GfxCtx, Menu, MenuResult, TextBox, TextOSD, UserInput};
use map_model::Map;
use piston::input::Key;
use plugins::Colorizer;
use sim::Tick;
use std::collections::VecDeque;
use plugins::Colorizer;
#[derive(Debug)]
struct SpawnOverTime {
@ -35,7 +35,7 @@ impl WizardSample {
WizardSample::Inactive
}
pub fn event(&mut self, input: &mut UserInput, map: &Map) -> bool {
pub fn event(&mut self, input: &mut UserInput, map: &Map, osd: &mut TextOSD) -> bool {
let mut new_state: Option<WizardSample> = None;
match self {
WizardSample::Inactive => {
@ -44,7 +44,7 @@ impl WizardSample {
}
}
WizardSample::Active(ref mut wizard) => {
if let Some(spec) = workflow(wizard.wrap(input, map)) {
if let Some(spec) = workflow(wizard.wrap(input, map, osd)) {
println!("Got answer: {:?}", spec);
new_state = Some(WizardSample::Inactive);
} else if wizard.aborted() {
@ -62,18 +62,6 @@ impl WizardSample {
}
}
pub fn populate_osd(&self, osd: &mut TextOSD) {
match self {
WizardSample::Active(wizard) => {
if let Some(ref tb) = wizard.tb {
osd.pad_if_nonempty();
tb.populate_osd(osd);
}
}
_ => {}
}
}
pub fn draw(&self, g: &mut GfxCtx, canvas: &Canvas) {
// TODO nothing yet, but do show neighborhood preview later
}
@ -120,7 +108,12 @@ impl Wizard {
}
}
fn wrap<'a>(&'a mut self, input: &'a mut UserInput, map: &'a Map) -> WrappedWizard<'a> {
fn wrap<'a>(
&'a mut self,
input: &'a mut UserInput,
map: &'a Map,
osd: &'a mut TextOSD,
) -> WrappedWizard<'a> {
assert!(self.alive);
let ready_usize = VecDeque::from(self.state_usize.clone());
@ -128,6 +121,7 @@ impl Wizard {
wizard: self,
input,
map,
osd,
ready_usize,
}
}
@ -136,7 +130,12 @@ impl Wizard {
!self.alive
}
fn input_usize(&mut self, query: &str, input: &mut UserInput) -> Option<usize> {
fn input_usize(
&mut self,
query: &str,
input: &mut UserInput,
osd: &mut TextOSD,
) -> Option<usize> {
assert!(self.alive);
// Otherwise, we try to use one event for two inputs potentially
@ -148,21 +147,23 @@ impl Wizard {
self.tb = Some(TextBox::new());
}
if self.tb.as_mut().unwrap().event(input.use_event_directly()) {
input.consume_event();
let done = self.tb.as_mut().unwrap().event(input.use_event_directly());
input.consume_event();
if done {
let line = self.tb.as_ref().unwrap().line.clone();
self.tb = None;
if let Some(num) = line.parse::<usize>().ok() {
self.tb = None;
self.state_usize.push(num);
Some(num)
} else {
println!("Invalid number {} -- assuming you meant to abort", line);
self.tb = None;
self.alive = false;
None
}
} else {
input.consume_event();
osd.pad_if_nonempty();
osd.add_line(query.to_string());
self.tb.as_ref().unwrap().populate_osd(osd);
None
}
}
@ -173,6 +174,7 @@ struct WrappedWizard<'a> {
wizard: &'a mut Wizard,
input: &'a mut UserInput,
map: &'a Map,
osd: &'a mut TextOSD,
ready_usize: VecDeque<usize>,
}
@ -182,6 +184,6 @@ impl<'a> WrappedWizard<'a> {
if !self.ready_usize.is_empty() {
return self.ready_usize.pop_front();
}
self.wizard.input_usize(query, self.input)
self.wizard.input_usize(query, self.input, self.osd)
}
}

View File

@ -21,7 +21,6 @@ use plugins::debug_objects::DebugObjectsState;
use plugins::draw_polygon::DrawPolygonState;
use plugins::floodfill::Floodfiller;
use plugins::follow::FollowState;
use plugins::wizard::WizardSample;
use plugins::geom_validation::Validator;
use plugins::hider::Hider;
use plugins::road_editor::RoadEditor;
@ -33,6 +32,7 @@ use plugins::stop_sign_editor::StopSignEditor;
use plugins::traffic_signal_editor::TrafficSignalEditor;
use plugins::turn_cycler::TurnCyclerState;
use plugins::warp::WarpState;
use plugins::wizard::WizardSample;
use plugins::Colorizer;
use render::{DrawMap, RenderOptions};
use sim;
@ -48,19 +48,19 @@ const MIN_ZOOM_FOR_MOUSEOVER: f64 = 4.0;
// Necessary so we can iterate over and run the plugins, which mutably borrow UI.
pub struct UIWrapper {
ui: UI,
plugins: Vec<Box<Fn(&mut UI, &mut UserInput) -> bool>>,
plugins: Vec<Box<Fn(&mut UI, &mut UserInput, &mut TextOSD) -> bool>>,
}
impl GUI for UIWrapper {
fn event(&mut self, input: &mut UserInput) -> EventLoopMode {
self.ui.event(input, &self.plugins)
fn event(&mut self, input: UserInput, osd: &mut TextOSD) -> EventLoopMode {
self.ui.event(input, osd, &self.plugins)
}
fn draw(&mut self, g: &mut GfxCtx, input: UserInput, window_size: Size) {
fn draw(&mut self, g: &mut GfxCtx, osd: TextOSD, window_size: Size) {
// Since self is mut here, we can set window_size on the canvas, but then let the real
// draw() be immutable.
self.ui.canvas.start_drawing(g, window_size);
self.ui.draw(g, input);
self.ui.draw(g, osd);
}
}
@ -155,7 +155,7 @@ impl UIWrapper {
UIWrapper {
ui,
plugins: vec![
Box::new(|ui, input| {
Box::new(|ui, input, _osd| {
let layer_changed = {
let mut changed = false;
for layer in ui.toggleable_layers().into_iter() {
@ -173,7 +173,7 @@ impl UIWrapper {
false
}
}),
Box::new(|ui, input| {
Box::new(|ui, input, _osd| {
ui.traffic_signal_editor.event(
input,
&ui.map,
@ -181,7 +181,7 @@ impl UIWrapper {
ui.current_selection,
)
}),
Box::new(|ui, input| {
Box::new(|ui, input, _osd| {
ui.stop_sign_editor.event(
input,
&ui.map,
@ -189,7 +189,7 @@ impl UIWrapper {
ui.current_selection,
)
}),
Box::new(|ui, input| {
Box::new(|ui, input, _osd| {
ui.road_editor.event(
input,
ui.current_selection,
@ -199,17 +199,18 @@ impl UIWrapper {
&mut ui.sim,
)
}),
Box::new(|ui, input| ui.search_state.event(input)),
Box::new(|ui, input| {
Box::new(|ui, input, osd| ui.search_state.event(input, osd)),
Box::new(|ui, input, osd| {
ui.warp.event(
input,
&ui.map,
&ui.sim,
&mut ui.canvas,
&mut ui.current_selection,
osd,
)
}),
Box::new(|ui, input| {
Box::new(|ui, input, _osd| {
ui.follow.event(
input,
&ui.map,
@ -218,12 +219,16 @@ impl UIWrapper {
ui.current_selection,
)
}),
Box::new(|ui, input| ui.show_route.event(input, &ui.sim, ui.current_selection)),
Box::new(|ui, input| ui.color_picker.event(input, &mut ui.canvas, &mut ui.cs)),
Box::new(|ui, input| ui.steepness_viz.event(input)),
Box::new(|ui, input| ui.osm_classifier.event(input)),
Box::new(|ui, input| ui.hider.event(input, &mut ui.current_selection)),
Box::new(|ui, input| {
Box::new(|ui, input, _osd| {
ui.show_route.event(input, &ui.sim, ui.current_selection)
}),
Box::new(|ui, input, _osd| {
ui.color_picker.event(input, &mut ui.canvas, &mut ui.cs)
}),
Box::new(|ui, input, _osd| ui.steepness_viz.event(input)),
Box::new(|ui, input, _osd| ui.osm_classifier.event(input)),
Box::new(|ui, input, _osd| ui.hider.event(input, &mut ui.current_selection)),
Box::new(|ui, input, _osd| {
ui.debug_objects.event(
ui.current_selection,
input,
@ -232,14 +237,16 @@ impl UIWrapper {
&ui.control_map,
)
}),
Box::new(|ui, input| ui.floodfiller.event(&ui.map, input, ui.current_selection)),
Box::new(|ui, input| {
Box::new(|ui, input, _osd| {
ui.floodfiller.event(&ui.map, input, ui.current_selection)
}),
Box::new(|ui, input, _osd| {
ui.geom_validator
.event(input, &mut ui.canvas, &ui.map, &ui.draw_map)
}),
Box::new(|ui, input| ui.turn_cycler.event(input, ui.current_selection)),
Box::new(|ui, input| ui.draw_polygon.event(input, &ui.canvas, &ui.map)),
Box::new(|ui, input| ui.wizard_sample.event(input, &ui.map)),
Box::new(|ui, input, _osd| ui.turn_cycler.event(input, ui.current_selection)),
Box::new(|ui, input, osd| ui.draw_polygon.event(input, &ui.canvas, &ui.map, osd)),
Box::new(|ui, input, osd| ui.wizard_sample.event(input, &ui.map, osd)),
],
}
}
@ -322,8 +329,9 @@ impl UI {
fn event(
&mut self,
input: &mut UserInput,
plugins: &Vec<Box<Fn(&mut UI, &mut UserInput) -> bool>>,
mut input: UserInput,
osd: &mut TextOSD,
plugins: &Vec<Box<Fn(&mut UI, &mut UserInput, &mut TextOSD) -> bool>>,
) -> EventLoopMode {
// First update the camera and handle zoom
let old_zoom = self.canvas.cam_zoom;
@ -349,13 +357,13 @@ impl UI {
// If there's an active plugin, just run it.
if let Some(idx) = self.active_plugin {
if !plugins[idx](self, input) {
if !plugins[idx](self, &mut input, osd) {
self.active_plugin = None;
}
} else {
// Run each plugin, short-circuiting if the plugin claimed it was active.
for (idx, plugin) in plugins.iter().enumerate() {
if plugin(self, input) {
if plugin(self, &mut input, osd) {
self.active_plugin = Some(idx);
break;
}
@ -381,16 +389,19 @@ impl UI {
}
// Sim controller plugin is kind of always active? If nothing else ran, let it use keys.
self.sim_ctrl.event(
input,
let result = self.sim_ctrl.event(
&mut input,
&self.map,
&self.control_map,
&mut self.sim,
self.current_selection,
)
osd,
);
input.populate_osd(osd);
result
}
fn draw(&self, g: &mut GfxCtx, input: UserInput) {
fn draw(&self, g: &mut GfxCtx, osd: TextOSD) {
g.clear(self.cs.get(Colors::Background));
let (statics, dynamics) = self.draw_map.get_objects_onscreen(
@ -453,14 +464,6 @@ impl UI {
self.draw_polygon.draw(g, &self.canvas);
self.wizard_sample.draw(g, &self.canvas);
// TODO Only if active (except for the weird sim_ctrl)?
let mut osd = TextOSD::new();
input.populate_osd(&mut osd);
self.sim_ctrl.populate_osd(&self.sim, &mut osd);
self.search_state.populate_osd(&mut osd);
self.warp.populate_osd(&mut osd);
self.draw_polygon.populate_osd(&mut osd);
self.wizard_sample.populate_osd(&mut osd);
self.canvas.draw_osd_notification(g, osd);
}

View File

@ -4,13 +4,11 @@ use opengl_graphics::{Filter, GlGraphics, GlyphCache, OpenGL, TextureSettings};
use piston::event_loop::{EventLoop, EventSettings, Events};
use piston::input::RenderEvent;
use piston::window::{Size, Window, WindowSettings};
use GfxCtx;
use {GfxCtx, TextOSD};
pub trait GUI {
fn event(&mut self, input: &mut UserInput) -> EventLoopMode;
// TODO just take OSD stuff, not all of the input
fn draw(&mut self, g: &mut GfxCtx, input: UserInput, window_size: Size);
fn event(&mut self, input: UserInput, osd: &mut TextOSD) -> EventLoopMode;
fn draw(&mut self, g: &mut GfxCtx, osd: TextOSD, window_size: Size);
}
#[derive(PartialEq)]
@ -41,8 +39,8 @@ pub fn run<T: GUI>(mut gui: T, window_title: &str, initial_width: u32, initial_h
let mut last_event_mode = EventLoopMode::InputOnly;
while let Some(ev) = events.next(&mut window) {
let mut input = UserInput::new(ev.clone());
let new_event_mode = gui.event(&mut input);
let mut osd = TextOSD::new();
let new_event_mode = gui.event(UserInput::new(ev.clone()), &mut osd);
// Don't constantly reset the events struct -- only when laziness changes.
if new_event_mode != last_event_mode {
events.set_lazy(new_event_mode == EventLoopMode::InputOnly);
@ -51,11 +49,7 @@ pub fn run<T: GUI>(mut gui: T, window_title: &str, initial_width: u32, initial_h
if let Some(args) = ev.render_args() {
gl.draw(args.viewport(), |c, g| {
gui.draw(
&mut GfxCtx::new(&mut glyphs, g, c),
input,
window.draw_size(),
);
gui.draw(&mut GfxCtx::new(&mut glyphs, g, c), osd, window.draw_size());
});
}
}

View File

@ -32,6 +32,7 @@ impl TextBox {
// TODO a way to abort out
// Returns true if the user confirmed their entry.
// TODO return the entered string if done...
pub fn event(&mut self, ev: &Event) -> bool {
// Done?
if let Some(Button::Keyboard(Key::Return)) = ev.press_args() {

View File

@ -4,7 +4,7 @@ extern crate graphics;
extern crate map_model;
extern crate piston;
use ezgui::{Canvas, EventLoopMode, GfxCtx, UserInput, GUI};
use ezgui::{Canvas, EventLoopMode, GfxCtx, TextOSD, UserInput, GUI};
use geom::{Circle, PolyLine, Polygon, Pt2D};
use graphics::types::Color;
use map_model::LANE_THICKNESS;
@ -44,7 +44,7 @@ impl UI {
}
impl GUI for UI {
fn event(&mut self, input: &mut UserInput) -> EventLoopMode {
fn event(&mut self, mut input: UserInput, _osd: &mut TextOSD) -> EventLoopMode {
if input.unimportant_key_pressed(Key::Escape, "quit") {
process::exit(0);
}
@ -71,7 +71,7 @@ impl GUI for UI {
}
// TODO Weird to mut self just to set window_size on the canvas
fn draw(&mut self, g: &mut GfxCtx, _input: UserInput, window_size: Size) {
fn draw(&mut self, g: &mut GfxCtx, _osd: TextOSD, window_size: Size) {
g.clear(WHITE);
self.canvas.start_drawing(g, window_size);