mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-25 23:43:25 +03:00
consistently having one(ish) plugin do stuff at a time
This commit is contained in:
parent
e2aabeb0e9
commit
18132e3c8b
@ -179,3 +179,15 @@ GUI refactoring thoughts:
|
||||
- Canvas has persistent state, GfxCtx is ephemeral every draw cycle
|
||||
- dont want to draw outside of render, but may want to readjust camera
|
||||
- compromise is maybe storing the last known window size in canvas, so we dont have to keep plumbing it between frames anyway.
|
||||
|
||||
|
||||
One UI plugin at a time:
|
||||
- What can plugins do?
|
||||
- (rarely) contribute OSD lines (in some order)
|
||||
- (rarely) do custom drawing (in some order)
|
||||
- event handling
|
||||
- mutate themselves or consume+return?
|
||||
- indicate if the plugin was active and did stuff?
|
||||
- just quit after handling each plugin? and do panning / some selection stuff earlier
|
||||
- alright, atfer the current cleanup with short-circuiting... express as a more abstract monadish thing? or since there are side effects sometimes and inconsistent arguments and such, maybe not?
|
||||
- consistently mutate a plugin or return a copy
|
||||
|
@ -1,16 +0,0 @@
|
||||
// Copyright 2018 Google LLC, licensed under http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
#[derive(PartialEq)]
|
||||
pub enum EventLoopMode {
|
||||
Animation,
|
||||
InputOnly,
|
||||
}
|
||||
|
||||
impl EventLoopMode {
|
||||
pub fn merge(self, other: EventLoopMode) -> EventLoopMode {
|
||||
match self {
|
||||
EventLoopMode::Animation => EventLoopMode::Animation,
|
||||
_ => other,
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,3 @@
|
||||
use animation;
|
||||
use ezgui::GfxCtx;
|
||||
use ezgui::canvas::Canvas;
|
||||
use ezgui::input::UserInput;
|
||||
@ -36,7 +35,7 @@ impl UI {
|
||||
}
|
||||
|
||||
impl gui::GUI for UI {
|
||||
fn event(mut self, input: &mut UserInput) -> (UI, animation::EventLoopMode) {
|
||||
fn event(mut self, input: &mut UserInput) -> (UI, gui::EventLoopMode) {
|
||||
if input.unimportant_key_pressed(Key::Escape, "Press escape to quit") {
|
||||
process::exit(0);
|
||||
}
|
||||
@ -59,7 +58,7 @@ impl gui::GUI for UI {
|
||||
|
||||
self.canvas.handle_event(input.use_event_directly());
|
||||
|
||||
(self, animation::EventLoopMode::InputOnly)
|
||||
(self, gui::EventLoopMode::InputOnly)
|
||||
}
|
||||
|
||||
// TODO Weird to mut self just to set window_size on the canvas
|
||||
|
@ -1,14 +1,19 @@
|
||||
use animation;
|
||||
use ezgui::GfxCtx;
|
||||
use ezgui::input::UserInput;
|
||||
use piston::window::Size;
|
||||
use std;
|
||||
|
||||
pub trait GUI {
|
||||
fn event(self, input: &mut UserInput) -> (Self, animation::EventLoopMode)
|
||||
fn event(self, input: &mut UserInput) -> (Self, EventLoopMode)
|
||||
where
|
||||
Self: std::marker::Sized;
|
||||
|
||||
// TODO just take OSD stuff, not all of the input
|
||||
fn draw(&mut self, g: &mut GfxCtx, input: UserInput, window_size: Size);
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
pub enum EventLoopMode {
|
||||
Animation,
|
||||
InputOnly,
|
||||
}
|
||||
|
@ -34,7 +34,6 @@ use piston::input::RenderEvent;
|
||||
use piston::window::{Window, WindowSettings};
|
||||
use structopt::StructOpt;
|
||||
|
||||
mod animation;
|
||||
mod colors;
|
||||
mod experimental;
|
||||
mod gui;
|
||||
@ -107,7 +106,7 @@ fn run<T: gui::GUI>(
|
||||
mut glyphs: GlyphCache,
|
||||
mut gui: T,
|
||||
) {
|
||||
let mut last_event_mode = animation::EventLoopMode::InputOnly;
|
||||
let mut last_event_mode = gui::EventLoopMode::InputOnly;
|
||||
|
||||
while let Some(ev) = events.next(&mut window) {
|
||||
let mut input = UserInput::new(ev.clone());
|
||||
@ -115,7 +114,7 @@ fn run<T: gui::GUI>(
|
||||
gui = new_gui;
|
||||
// Don't constantly reset the events struct -- only when laziness changes.
|
||||
if new_event_mode != last_event_mode {
|
||||
events.set_lazy(new_event_mode == animation::EventLoopMode::InputOnly);
|
||||
events.set_lazy(new_event_mode == gui::EventLoopMode::InputOnly);
|
||||
last_event_mode = new_event_mode;
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,7 @@ impl OsmClassifier {
|
||||
OsmClassifier { active: false }
|
||||
}
|
||||
|
||||
pub fn handle_event(&mut self, input: &mut UserInput) {
|
||||
pub fn handle_event(&mut self, input: &mut UserInput) -> bool {
|
||||
let msg = if self.active {
|
||||
"Press 6 to stop showing OSM classes"
|
||||
} else {
|
||||
@ -24,6 +24,9 @@ impl OsmClassifier {
|
||||
};
|
||||
if input.unimportant_key_pressed(Key::D6, msg) {
|
||||
self.active = !self.active;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -29,29 +29,33 @@ impl ColorPicker {
|
||||
ColorPicker::Inactive
|
||||
}
|
||||
|
||||
// True if plugin was active
|
||||
pub fn handle_event(
|
||||
self,
|
||||
input: &mut UserInput,
|
||||
canvas: &Canvas,
|
||||
cs: &mut ColorScheme,
|
||||
) -> ColorPicker {
|
||||
) -> (ColorPicker, bool) {
|
||||
match self {
|
||||
ColorPicker::Inactive => {
|
||||
if input.unimportant_key_pressed(Key::D8, "Press 8 to configure colors") {
|
||||
return ColorPicker::Choosing(menu::Menu::new(
|
||||
Colors::iter().map(|c| c.to_string()).collect(),
|
||||
));
|
||||
return (
|
||||
ColorPicker::Choosing(menu::Menu::new(
|
||||
Colors::iter().map(|c| c.to_string()).collect(),
|
||||
)),
|
||||
true,
|
||||
);
|
||||
}
|
||||
ColorPicker::Inactive
|
||||
(ColorPicker::Inactive, false)
|
||||
}
|
||||
ColorPicker::Choosing(mut menu) => {
|
||||
// TODO arrow keys scroll canvas too
|
||||
match menu.event(input.use_event_directly()) {
|
||||
menu::Result::Canceled => ColorPicker::Inactive,
|
||||
menu::Result::StillActive => ColorPicker::Choosing(menu),
|
||||
menu::Result::Canceled => (ColorPicker::Inactive, true),
|
||||
menu::Result::StillActive => (ColorPicker::Choosing(menu), true),
|
||||
menu::Result::Done(choice) => {
|
||||
let c = Colors::from_str(&choice).unwrap();
|
||||
ColorPicker::PickingColor(c, cs.get(c))
|
||||
(ColorPicker::PickingColor(c, cs.get(c)), true)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -64,7 +68,7 @@ impl ColorPicker {
|
||||
),
|
||||
) {
|
||||
cs.set(c, orig_color);
|
||||
return ColorPicker::Inactive;
|
||||
return (ColorPicker::Inactive, true);
|
||||
}
|
||||
|
||||
if input.key_pressed(
|
||||
@ -72,7 +76,7 @@ impl ColorPicker {
|
||||
&format!("Press enter to finalize new color for {:?}", c),
|
||||
) {
|
||||
println!("Setting color for {:?}", c);
|
||||
return ColorPicker::Inactive;
|
||||
return (ColorPicker::Inactive, true);
|
||||
}
|
||||
|
||||
if let Some(pos) = input.use_event_directly().mouse_cursor_args() {
|
||||
@ -82,11 +86,11 @@ impl ColorPicker {
|
||||
let y = (pos[1] - (start_y as f64)) / (TILE_DIMS as f64) / 255.0;
|
||||
if x >= 0.0 && x <= 1.0 && y >= 0.0 && y <= 1.0 {
|
||||
cs.set(c, get_color(x as f32, y as f32));
|
||||
return ColorPicker::PickingColor(c, orig_color);
|
||||
return (ColorPicker::PickingColor(c, orig_color), true);
|
||||
}
|
||||
}
|
||||
|
||||
ColorPicker::PickingColor(c, orig_color)
|
||||
(ColorPicker::PickingColor(c, orig_color), true)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -109,8 +113,8 @@ impl ColorPicker {
|
||||
g.draw_rectangle(
|
||||
color,
|
||||
[
|
||||
(x * TILE_DIMS + start_x) as f64,
|
||||
(y * TILE_DIMS + start_y) as f64,
|
||||
canvas.screen_to_map_x((x * TILE_DIMS + start_x) as f64),
|
||||
canvas.screen_to_map_y((y * TILE_DIMS + start_y) as f64),
|
||||
TILE_DIMS as f64,
|
||||
TILE_DIMS as f64,
|
||||
],
|
||||
|
@ -34,22 +34,23 @@ impl SearchState {
|
||||
}
|
||||
|
||||
// TODO Does this pattern where we consume self and return it work out nicer?
|
||||
pub fn event(self, input: &mut UserInput) -> SearchState {
|
||||
// True if active
|
||||
pub fn event(self, input: &mut UserInput) -> (SearchState, bool) {
|
||||
match self {
|
||||
SearchState::Empty => {
|
||||
if input.unimportant_key_pressed(Key::Slash, "Press / to start searching") {
|
||||
SearchState::EnteringSearch(TextBox::new())
|
||||
(SearchState::EnteringSearch(TextBox::new()), true)
|
||||
} else {
|
||||
self
|
||||
(self, false)
|
||||
}
|
||||
}
|
||||
SearchState::EnteringSearch(mut tb) => {
|
||||
if tb.event(input.use_event_directly()) {
|
||||
input.consume_event();
|
||||
SearchState::FilterOSM(tb.line)
|
||||
(SearchState::FilterOSM(tb.line), true)
|
||||
} else {
|
||||
input.consume_event();
|
||||
SearchState::EnteringSearch(tb)
|
||||
(SearchState::EnteringSearch(tb), true)
|
||||
}
|
||||
}
|
||||
SearchState::FilterOSM(filter) => {
|
||||
@ -57,9 +58,9 @@ impl SearchState {
|
||||
Key::Return,
|
||||
&format!("Press enter to clear the current search for {}", filter),
|
||||
) {
|
||||
SearchState::Empty
|
||||
(SearchState::Empty, true)
|
||||
} else {
|
||||
SearchState::FilterOSM(filter)
|
||||
(SearchState::FilterOSM(filter), true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
// Copyright 2018 Google LLC, licensed under http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
use animation;
|
||||
use colors::{ColorScheme, Colors};
|
||||
use control::ControlMap;
|
||||
use ezgui::GfxCtx;
|
||||
@ -26,6 +25,7 @@ pub enum ID {
|
||||
//Parcel(ParcelID),
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum SelectionState {
|
||||
Empty,
|
||||
SelectedIntersection(IntersectionID),
|
||||
@ -57,28 +57,15 @@ impl SelectionState {
|
||||
}
|
||||
|
||||
// TODO consume self
|
||||
pub fn event(
|
||||
&self,
|
||||
input: &mut UserInput,
|
||||
map: &Map,
|
||||
sim: &mut Sim,
|
||||
) -> (SelectionState, animation::EventLoopMode) {
|
||||
pub fn event(&self, input: &mut UserInput, map: &Map) -> (SelectionState, bool) {
|
||||
// TODO simplify the way this is written
|
||||
match *self {
|
||||
SelectionState::Empty => (SelectionState::Empty, animation::EventLoopMode::InputOnly),
|
||||
SelectionState::SelectedIntersection(id) => (
|
||||
SelectionState::SelectedIntersection(id),
|
||||
animation::EventLoopMode::InputOnly,
|
||||
),
|
||||
SelectionState::SelectedRoad(id, current_turn_index) => {
|
||||
if input.key_pressed(
|
||||
Key::LCtrl,
|
||||
&format!("Hold Ctrl to show road {:?}'s tooltip", id),
|
||||
) {
|
||||
(
|
||||
SelectionState::TooltipRoad(id),
|
||||
animation::EventLoopMode::InputOnly,
|
||||
)
|
||||
(SelectionState::TooltipRoad(id), true)
|
||||
} else if input
|
||||
.key_pressed(Key::Tab, "Press Tab to cycle through this road's turns")
|
||||
{
|
||||
@ -86,58 +73,24 @@ impl SelectionState {
|
||||
Some(i) => i + 1,
|
||||
None => 0,
|
||||
};
|
||||
(
|
||||
SelectionState::SelectedRoad(id, Some(idx)),
|
||||
animation::EventLoopMode::InputOnly,
|
||||
)
|
||||
(SelectionState::SelectedRoad(id, Some(idx)), true)
|
||||
} else if input.key_pressed(Key::D, "press D to debug") {
|
||||
map.get_r(id).dump_debug();
|
||||
(
|
||||
SelectionState::SelectedRoad(id, current_turn_index),
|
||||
animation::EventLoopMode::InputOnly,
|
||||
)
|
||||
(SelectionState::SelectedRoad(id, current_turn_index), true)
|
||||
} else {
|
||||
(
|
||||
SelectionState::SelectedRoad(id, current_turn_index),
|
||||
animation::EventLoopMode::InputOnly,
|
||||
)
|
||||
(self.clone(), false)
|
||||
}
|
||||
}
|
||||
SelectionState::TooltipRoad(id) => {
|
||||
if let Some(Button::Keyboard(Key::LCtrl)) =
|
||||
input.use_event_directly().release_args()
|
||||
{
|
||||
(
|
||||
SelectionState::SelectedRoad(id, None),
|
||||
animation::EventLoopMode::InputOnly,
|
||||
)
|
||||
(SelectionState::SelectedRoad(id, None), true)
|
||||
} else {
|
||||
(
|
||||
SelectionState::TooltipRoad(id),
|
||||
animation::EventLoopMode::InputOnly,
|
||||
)
|
||||
(self.clone(), false)
|
||||
}
|
||||
}
|
||||
SelectionState::SelectedBuilding(id) => (
|
||||
SelectionState::SelectedBuilding(id),
|
||||
animation::EventLoopMode::InputOnly,
|
||||
),
|
||||
SelectionState::SelectedTurn(id) => (
|
||||
SelectionState::SelectedTurn(id),
|
||||
animation::EventLoopMode::InputOnly,
|
||||
),
|
||||
SelectionState::SelectedCar(id) => {
|
||||
// TODO not sure if we should debug like this (pushing the bit down to all the
|
||||
// layers representing an entity) or by using some scary global mutable singleton
|
||||
if input.unimportant_key_pressed(Key::D, "press D to debug") {
|
||||
sim.toggle_debug(id);
|
||||
}
|
||||
|
||||
(
|
||||
SelectionState::SelectedCar(id),
|
||||
animation::EventLoopMode::InputOnly,
|
||||
)
|
||||
}
|
||||
_ => (self.clone(), false),
|
||||
}
|
||||
}
|
||||
|
||||
@ -248,10 +201,11 @@ impl Hider {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn event(&mut self, input: &mut UserInput, state: &mut SelectionState) {
|
||||
pub fn event(&mut self, input: &mut UserInput, state: &mut SelectionState) -> bool {
|
||||
if input.unimportant_key_pressed(Key::K, "Press k to unhide everything") {
|
||||
println!("Unhiding {} things", self.items.len());
|
||||
self.items.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
let item = match state {
|
||||
@ -265,8 +219,10 @@ impl Hider {
|
||||
self.items.insert(id);
|
||||
println!("Hiding {:?}", id);
|
||||
*state = SelectionState::Empty;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn show_r(&self, id: RoadID) -> bool {
|
||||
|
@ -33,7 +33,7 @@ impl SteepnessVisualizer {
|
||||
s
|
||||
}
|
||||
|
||||
pub fn handle_event(&mut self, input: &mut UserInput) {
|
||||
pub fn handle_event(&mut self, input: &mut UserInput) -> bool {
|
||||
let msg = if self.active {
|
||||
"Press 5 to stop showing steepness"
|
||||
} else {
|
||||
@ -41,6 +41,9 @@ impl SteepnessVisualizer {
|
||||
};
|
||||
if input.unimportant_key_pressed(Key::D5, msg) {
|
||||
self.active = !self.active;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -12,32 +12,33 @@ pub enum WarpState {
|
||||
}
|
||||
|
||||
impl WarpState {
|
||||
// True if active
|
||||
pub fn event(
|
||||
self,
|
||||
input: &mut UserInput,
|
||||
map: &Map,
|
||||
canvas: &mut Canvas,
|
||||
selection_state: &mut SelectionState,
|
||||
) -> WarpState {
|
||||
) -> (WarpState, bool) {
|
||||
match self {
|
||||
WarpState::Empty => {
|
||||
if input.unimportant_key_pressed(
|
||||
Key::J,
|
||||
"Press J to start searching for something to warp to",
|
||||
) {
|
||||
WarpState::EnteringSearch(TextBox::new())
|
||||
(WarpState::EnteringSearch(TextBox::new()), true)
|
||||
} else {
|
||||
self
|
||||
(self, false)
|
||||
}
|
||||
}
|
||||
WarpState::EnteringSearch(mut tb) => {
|
||||
if tb.event(input.use_event_directly()) {
|
||||
input.consume_event();
|
||||
warp(tb.line, map, canvas, selection_state);
|
||||
WarpState::Empty
|
||||
(WarpState::Empty, true)
|
||||
} else {
|
||||
input.consume_event();
|
||||
WarpState::EnteringSearch(tb)
|
||||
(WarpState::EnteringSearch(tb), true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
256
editor/src/ui.rs
256
editor/src/ui.rs
@ -3,7 +3,6 @@
|
||||
// TODO this should just be a way to handle interactions between plugins
|
||||
|
||||
use abstutil;
|
||||
use animation;
|
||||
use colors::{ColorScheme, Colors};
|
||||
use control::ControlMap;
|
||||
use control::{ModifiedStopSign, ModifiedTrafficSignal};
|
||||
@ -52,7 +51,10 @@ pub struct UI {
|
||||
show_icons: ToggleableLayer,
|
||||
debug_mode: ToggleableLayer,
|
||||
|
||||
// This is a particularly special plugin, since it's always kind of active and other things
|
||||
// read/write it.
|
||||
current_selection_state: SelectionState,
|
||||
|
||||
hider: Hider,
|
||||
current_search_state: SearchState,
|
||||
warp: WarpState,
|
||||
@ -327,12 +329,25 @@ impl UI {
|
||||
}
|
||||
|
||||
impl gui::GUI for UI {
|
||||
fn event(mut self, input: &mut UserInput) -> (UI, animation::EventLoopMode) {
|
||||
let mut event_loop_mode = animation::EventLoopMode::InputOnly;
|
||||
let mut edit_mode = false;
|
||||
fn event(mut self, input: &mut UserInput) -> (UI, gui::EventLoopMode) {
|
||||
// First update the camera and handle zoom
|
||||
let old_zoom = self.canvas.cam_zoom;
|
||||
self.canvas.handle_event(input.use_event_directly());
|
||||
let new_zoom = self.canvas.cam_zoom;
|
||||
self.zoom_for_toggleable_layers(old_zoom, new_zoom);
|
||||
|
||||
// Always handle mouseover
|
||||
if old_zoom >= MIN_ZOOM_FOR_MOUSEOVER && new_zoom < MIN_ZOOM_FOR_MOUSEOVER {
|
||||
self.current_selection_state = SelectionState::Empty;
|
||||
}
|
||||
if !self.canvas.is_dragging() && input.use_event_directly().mouse_cursor_args().is_some()
|
||||
&& new_zoom >= MIN_ZOOM_FOR_MOUSEOVER
|
||||
{
|
||||
let item = self.mouseover_something();
|
||||
self.current_selection_state = self.current_selection_state.handle_mouseover(item);
|
||||
}
|
||||
|
||||
if let Some(mut e) = self.traffic_signal_editor {
|
||||
edit_mode = true;
|
||||
if e.event(
|
||||
input,
|
||||
&self.map,
|
||||
@ -343,10 +358,10 @@ impl gui::GUI for UI {
|
||||
} else {
|
||||
self.traffic_signal_editor = Some(e);
|
||||
}
|
||||
return (self, gui::EventLoopMode::InputOnly);
|
||||
}
|
||||
|
||||
if let Some(mut e) = self.stop_sign_editor {
|
||||
edit_mode = true;
|
||||
if e.event(
|
||||
input,
|
||||
&self.map,
|
||||
@ -357,111 +372,86 @@ impl gui::GUI for UI {
|
||||
} else {
|
||||
self.stop_sign_editor = Some(e);
|
||||
}
|
||||
return (self, gui::EventLoopMode::InputOnly);
|
||||
}
|
||||
|
||||
// TODO disabling temporarily since it conflicts with warp. need to solve the
|
||||
// one-plugin-at-a-time problem.
|
||||
if !edit_mode && false {
|
||||
self.color_picker =
|
||||
{
|
||||
let (new_color_picker, active) =
|
||||
self.color_picker
|
||||
.handle_event(input, &mut self.canvas, &mut self.cs);
|
||||
}
|
||||
|
||||
self.current_search_state = self.current_search_state.event(input);
|
||||
self.warp = self.warp.event(
|
||||
input,
|
||||
&self.map,
|
||||
&mut self.canvas,
|
||||
&mut self.current_selection_state,
|
||||
);
|
||||
|
||||
if !edit_mode && self.sim_ctrl.event(input, &self.map, &self.control_map) {
|
||||
event_loop_mode = event_loop_mode.merge(animation::EventLoopMode::Animation);
|
||||
}
|
||||
|
||||
let old_zoom = self.canvas.cam_zoom;
|
||||
self.canvas.handle_event(input.use_event_directly());
|
||||
let new_zoom = self.canvas.cam_zoom;
|
||||
self.zoom_for_toggleable_layers(old_zoom, new_zoom);
|
||||
|
||||
if !edit_mode {
|
||||
if self.show_roads.handle_event(input) {
|
||||
if let SelectionState::SelectedRoad(_, _) = self.current_selection_state {
|
||||
self.current_selection_state = SelectionState::Empty;
|
||||
}
|
||||
if let SelectionState::TooltipRoad(_) = self.current_selection_state {
|
||||
self.current_selection_state = SelectionState::Empty;
|
||||
}
|
||||
self.color_picker = new_color_picker;
|
||||
if active {
|
||||
return (self, gui::EventLoopMode::InputOnly);
|
||||
}
|
||||
if self.show_buildings.handle_event(input) {
|
||||
if let SelectionState::SelectedBuilding(_) = self.current_selection_state {
|
||||
self.current_selection_state = SelectionState::Empty;
|
||||
}
|
||||
}
|
||||
if self.show_intersections.handle_event(input) {
|
||||
if let SelectionState::SelectedIntersection(_) = self.current_selection_state {
|
||||
self.current_selection_state = SelectionState::Empty;
|
||||
}
|
||||
}
|
||||
self.show_parcels.handle_event(input);
|
||||
self.show_icons.handle_event(input);
|
||||
self.steepness_viz.handle_event(input);
|
||||
self.osm_classifier.handle_event(input);
|
||||
self.debug_mode.handle_event(input);
|
||||
}
|
||||
|
||||
if old_zoom >= MIN_ZOOM_FOR_MOUSEOVER && new_zoom < MIN_ZOOM_FOR_MOUSEOVER {
|
||||
self.current_selection_state = SelectionState::Empty;
|
||||
}
|
||||
if !self.canvas.is_dragging() && input.use_event_directly().mouse_cursor_args().is_some()
|
||||
&& new_zoom >= MIN_ZOOM_FOR_MOUSEOVER
|
||||
{
|
||||
let item = self.mouseover_something();
|
||||
self.current_selection_state = self.current_selection_state.handle_mouseover(item);
|
||||
let (new_search, active) = self.current_search_state.event(input);
|
||||
self.current_search_state = new_search;
|
||||
if active {
|
||||
return (self, gui::EventLoopMode::InputOnly);
|
||||
}
|
||||
}
|
||||
self.hider.event(input, &mut self.current_selection_state);
|
||||
// TODO can't get this destructuring expressed right
|
||||
let (new_selection_state, new_event_loop_mode) =
|
||||
self.current_selection_state
|
||||
.event(input, &self.map, &mut self.sim_ctrl.sim);
|
||||
event_loop_mode = event_loop_mode.merge(new_event_loop_mode);
|
||||
self.current_selection_state = new_selection_state;
|
||||
match self.current_selection_state {
|
||||
SelectionState::SelectedRoad(id, _) => {
|
||||
if self.floodfiller.is_none() {
|
||||
if input.key_pressed(Key::F, "Press F to start floodfilling from this road") {
|
||||
self.floodfiller = Some(Floodfiller::new(id));
|
||||
}
|
||||
}
|
||||
|
||||
if self.map.get_r(id).lane_type == map_model::LaneType::Driving {
|
||||
if input.key_pressed(Key::A, "Press A to add a car starting from this road") {
|
||||
if !self.sim_ctrl.sim.spawn_one_on_road(id) {
|
||||
println!("No room, sorry");
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
let (new_warp, active) = self.warp.event(
|
||||
input,
|
||||
&self.map,
|
||||
&mut self.canvas,
|
||||
&mut self.current_selection_state,
|
||||
);
|
||||
self.warp = new_warp;
|
||||
if active {
|
||||
return (self, gui::EventLoopMode::InputOnly);
|
||||
}
|
||||
SelectionState::SelectedIntersection(id) => {
|
||||
if self.traffic_signal_editor.is_none()
|
||||
&& self.control_map.traffic_signals.contains_key(&id)
|
||||
{
|
||||
if input.key_pressed(
|
||||
Key::E,
|
||||
&format!("Press E to edit traffic signal for {:?}", id),
|
||||
) {
|
||||
self.traffic_signal_editor = Some(TrafficSignalEditor::new(id));
|
||||
}
|
||||
}
|
||||
if self.stop_sign_editor.is_none() && self.control_map.stop_signs.contains_key(&id)
|
||||
{
|
||||
if input.key_pressed(Key::E, &format!("Press E to edit stop sign for {:?}", id))
|
||||
{
|
||||
self.stop_sign_editor = Some(StopSignEditor::new(id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if self.show_roads.handle_event(input) {
|
||||
if let SelectionState::SelectedRoad(_, _) = self.current_selection_state {
|
||||
self.current_selection_state = SelectionState::Empty;
|
||||
}
|
||||
_ => {}
|
||||
if let SelectionState::TooltipRoad(_) = self.current_selection_state {
|
||||
self.current_selection_state = SelectionState::Empty;
|
||||
}
|
||||
return (self, gui::EventLoopMode::InputOnly);
|
||||
}
|
||||
|
||||
if self.show_buildings.handle_event(input) {
|
||||
if let SelectionState::SelectedBuilding(_) = self.current_selection_state {
|
||||
self.current_selection_state = SelectionState::Empty;
|
||||
}
|
||||
return (self, gui::EventLoopMode::InputOnly);
|
||||
}
|
||||
|
||||
if self.show_intersections.handle_event(input) {
|
||||
if let SelectionState::SelectedIntersection(_) = self.current_selection_state {
|
||||
self.current_selection_state = SelectionState::Empty;
|
||||
}
|
||||
return (self, gui::EventLoopMode::InputOnly);
|
||||
}
|
||||
|
||||
if self.show_parcels.handle_event(input) {
|
||||
return (self, gui::EventLoopMode::InputOnly);
|
||||
}
|
||||
|
||||
if self.show_icons.handle_event(input) {
|
||||
return (self, gui::EventLoopMode::InputOnly);
|
||||
}
|
||||
|
||||
if self.debug_mode.handle_event(input) {
|
||||
return (self, gui::EventLoopMode::InputOnly);
|
||||
}
|
||||
|
||||
if self.steepness_viz.handle_event(input) {
|
||||
return (self, gui::EventLoopMode::InputOnly);
|
||||
}
|
||||
|
||||
if self.osm_classifier.handle_event(input) {
|
||||
return (self, gui::EventLoopMode::InputOnly);
|
||||
}
|
||||
|
||||
if self.hider.event(input, &mut self.current_selection_state) {
|
||||
return (self, gui::EventLoopMode::InputOnly);
|
||||
}
|
||||
|
||||
if let Some(mut f) = self.floodfiller {
|
||||
@ -470,6 +460,7 @@ impl gui::GUI for UI {
|
||||
} else {
|
||||
self.floodfiller = Some(f);
|
||||
}
|
||||
return (self, gui::EventLoopMode::InputOnly);
|
||||
}
|
||||
|
||||
if let Some(mut v) = self.geom_validator {
|
||||
@ -478,14 +469,70 @@ impl gui::GUI for UI {
|
||||
} else {
|
||||
self.geom_validator = Some(v);
|
||||
}
|
||||
return (self, gui::EventLoopMode::InputOnly);
|
||||
}
|
||||
if input.unimportant_key_pressed(Key::I, "Validate map geometry") {
|
||||
self.geom_validator = Some(Validator::new(&self.draw_map));
|
||||
return (self, gui::EventLoopMode::InputOnly);
|
||||
}
|
||||
|
||||
if input.unimportant_key_pressed(Key::S, "Spawn 1000 cars in random places") {
|
||||
self.sim_ctrl.sim.spawn_many_on_empty_roads(&self.map, 1000);
|
||||
return (self, gui::EventLoopMode::InputOnly);
|
||||
}
|
||||
|
||||
if input.unimportant_key_pressed(Key::I, "Validate map geometry") {
|
||||
self.geom_validator = Some(Validator::new(&self.draw_map));
|
||||
match self.current_selection_state {
|
||||
SelectionState::SelectedCar(id) => {
|
||||
// TODO not sure if we should debug like this (pushing the bit down to all the
|
||||
// layers representing an entity) or by using some scary global mutable singleton
|
||||
if input.unimportant_key_pressed(Key::D, "press D to debug") {
|
||||
self.sim_ctrl.sim.toggle_debug(id);
|
||||
return (self, gui::EventLoopMode::InputOnly);
|
||||
}
|
||||
}
|
||||
SelectionState::SelectedRoad(id, _) => {
|
||||
if input.key_pressed(Key::F, "Press F to start floodfilling from this road") {
|
||||
self.floodfiller = Some(Floodfiller::new(id));
|
||||
return (self, gui::EventLoopMode::InputOnly);
|
||||
}
|
||||
|
||||
if self.map.get_r(id).lane_type == map_model::LaneType::Driving {
|
||||
if input.key_pressed(Key::A, "Press A to add a car starting from this road") {
|
||||
if !self.sim_ctrl.sim.spawn_one_on_road(id) {
|
||||
println!("No room, sorry");
|
||||
}
|
||||
return (self, gui::EventLoopMode::InputOnly);
|
||||
}
|
||||
}
|
||||
}
|
||||
SelectionState::SelectedIntersection(id) => {
|
||||
if self.control_map.traffic_signals.contains_key(&id) {
|
||||
if input.key_pressed(
|
||||
Key::E,
|
||||
&format!("Press E to edit traffic signal for {:?}", id),
|
||||
) {
|
||||
self.traffic_signal_editor = Some(TrafficSignalEditor::new(id));
|
||||
return (self, gui::EventLoopMode::InputOnly);
|
||||
}
|
||||
}
|
||||
if self.control_map.stop_signs.contains_key(&id) {
|
||||
if input.key_pressed(Key::E, &format!("Press E to edit stop sign for {:?}", id))
|
||||
{
|
||||
self.stop_sign_editor = Some(StopSignEditor::new(id));
|
||||
return (self, gui::EventLoopMode::InputOnly);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// Do this one lastish, since it conflicts with lots of other stuff
|
||||
{
|
||||
let (new_selection, active) = self.current_selection_state.event(input, &self.map);
|
||||
self.current_selection_state = new_selection;
|
||||
if active {
|
||||
return (self, gui::EventLoopMode::InputOnly);
|
||||
}
|
||||
}
|
||||
|
||||
if input.unimportant_key_pressed(Key::Escape, "Press escape to quit") {
|
||||
@ -503,7 +550,12 @@ impl gui::GUI for UI {
|
||||
process::exit(0);
|
||||
}
|
||||
|
||||
(self, event_loop_mode)
|
||||
// Sim controller plugin is kind of always active? If nothing else ran, let it use keys.
|
||||
if self.sim_ctrl.event(input, &self.map, &self.control_map) {
|
||||
(self, gui::EventLoopMode::Animation)
|
||||
} else {
|
||||
(self, gui::EventLoopMode::InputOnly)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO Weird to mut self just to set window_size on the canvas
|
||||
|
@ -3,13 +3,13 @@
|
||||
use GfxCtx;
|
||||
use aabb_quadtree::geom::{Point, Rect};
|
||||
use graphics::Transformed;
|
||||
use piston::input::{Button, Event, Key, MouseButton, MouseCursorEvent, MouseScrollEvent,
|
||||
PressEvent, ReleaseEvent};
|
||||
use piston::input::{Button, Event, MouseButton, MouseCursorEvent, MouseScrollEvent, PressEvent,
|
||||
ReleaseEvent};
|
||||
use piston::window::Size;
|
||||
use text;
|
||||
|
||||
const ZOOM_SPEED: f64 = 0.05;
|
||||
const PAN_SPEED: f64 = 10.0;
|
||||
//const PAN_SPEED: f64 = 10.0;
|
||||
|
||||
pub struct Canvas {
|
||||
pub cam_x: f64,
|
||||
@ -60,7 +60,8 @@ impl Canvas {
|
||||
if let Some(Button::Mouse(MouseButton::Left)) = ev.release_args() {
|
||||
self.left_mouse_drag_from = None;
|
||||
}
|
||||
if let Some(Button::Keyboard(key)) = ev.press_args() {
|
||||
// These aren't used much now, and they conflict with other plugins
|
||||
/*if let Some(Button::Keyboard(key)) = ev.press_args() {
|
||||
match key {
|
||||
Key::Up => self.cam_y -= PAN_SPEED,
|
||||
Key::Down => self.cam_y += PAN_SPEED,
|
||||
@ -70,7 +71,7 @@ impl Canvas {
|
||||
Key::W => self.zoom_towards_mouse(ZOOM_SPEED),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
if let Some(scroll) = ev.mouse_scroll_args() {
|
||||
self.zoom_towards_mouse(scroll[1] * ZOOM_SPEED);
|
||||
}
|
||||
@ -123,10 +124,10 @@ impl Canvas {
|
||||
)
|
||||
}
|
||||
|
||||
fn screen_to_map_x(&self, x: f64) -> f64 {
|
||||
pub fn screen_to_map_x(&self, x: f64) -> f64 {
|
||||
(x + self.cam_x) / self.cam_zoom
|
||||
}
|
||||
fn screen_to_map_y(&self, y: f64) -> f64 {
|
||||
pub fn screen_to_map_y(&self, y: f64) -> f64 {
|
||||
(y + self.cam_y) / self.cam_zoom
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user