2018-12-06 21:18:20 +03:00
|
|
|
use crate::colors::ColorScheme;
|
2018-06-22 21:01:44 +03:00
|
|
|
use abstutil;
|
2018-10-18 02:57:15 +03:00
|
|
|
//use cpuprofiler;
|
2018-12-06 21:18:20 +03:00
|
|
|
use crate::objects::{Ctx, RenderingHints, ID, ROOT_MENU};
|
|
|
|
use crate::plugins;
|
|
|
|
use crate::plugins::debug::layers::ToggleableLayers;
|
|
|
|
use crate::plugins::debug::DebugMode;
|
|
|
|
use crate::plugins::edit::EditMode;
|
|
|
|
use crate::plugins::time_travel::TimeTravel;
|
|
|
|
use crate::plugins::Plugin;
|
|
|
|
use crate::render::{DrawMap, RenderOptions};
|
2018-12-04 20:58:34 +03:00
|
|
|
use ezgui::{Canvas, Color, EventLoopMode, GfxCtx, Text, UserInput, BOTTOM_LEFT, GUI};
|
2018-08-10 23:28:34 +03:00
|
|
|
use kml;
|
2018-12-05 22:45:07 +03:00
|
|
|
use map_model::{BuildingID, IntersectionID, LaneID, Map};
|
2018-10-06 01:44:12 +03:00
|
|
|
use piston::input::Key;
|
2018-12-06 22:05:11 +03:00
|
|
|
use serde_derive::{Deserialize, Serialize};
|
2018-08-27 23:03:35 +03:00
|
|
|
use sim;
|
2018-11-23 00:02:04 +03:00
|
|
|
use sim::{GetDrawAgents, Sim, SimFlags, Tick};
|
2018-10-25 01:59:53 +03:00
|
|
|
use std::cell::RefCell;
|
2018-12-05 21:39:17 +03:00
|
|
|
use std::collections::{HashMap, HashSet};
|
2018-11-02 00:00:14 +03:00
|
|
|
use std::panic;
|
2018-03-13 18:04:21 +03:00
|
|
|
use std::process;
|
|
|
|
|
2018-09-14 23:23:39 +03:00
|
|
|
const MIN_ZOOM_FOR_MOUSEOVER: f64 = 4.0;
|
2018-03-13 18:04:21 +03:00
|
|
|
|
2018-10-22 05:23:47 +03:00
|
|
|
pub struct UI {
|
|
|
|
primary: PerMapUI,
|
2018-10-22 05:59:37 +03:00
|
|
|
primary_plugins: PluginsPerMap,
|
2018-10-22 05:23:47 +03:00
|
|
|
// When running an A/B test, this is populated too.
|
2018-10-22 05:59:37 +03:00
|
|
|
secondary: Option<(PerMapUI, PluginsPerMap)>,
|
2018-10-22 05:23:47 +03:00
|
|
|
|
|
|
|
plugins: PluginsPerUI,
|
|
|
|
|
2018-10-22 06:18:15 +03:00
|
|
|
// TODO describe An index into plugin_handlers.
|
2018-10-22 05:23:47 +03:00
|
|
|
active_plugin: Option<usize>,
|
|
|
|
|
|
|
|
canvas: Canvas,
|
2018-10-25 01:59:53 +03:00
|
|
|
// TODO mutable ColorScheme to slurp up defaults is NOT ideal.
|
|
|
|
cs: RefCell<ColorScheme>,
|
2018-10-22 05:23:47 +03:00
|
|
|
|
|
|
|
// Remember this to support loading a new PerMapUI
|
|
|
|
kml: Option<String>,
|
2018-09-13 08:43:58 +03:00
|
|
|
}
|
2018-03-13 18:04:21 +03:00
|
|
|
|
2018-12-04 20:58:34 +03:00
|
|
|
impl GUI<RenderingHints> for UI {
|
|
|
|
fn event(&mut self, input: UserInput) -> (EventLoopMode, RenderingHints) {
|
|
|
|
match panic::catch_unwind(panic::AssertUnwindSafe(|| self.inner_event(input))) {
|
|
|
|
Ok(hints) => (hints.mode, hints),
|
2018-11-02 00:00:14 +03:00
|
|
|
Err(err) => {
|
2018-11-12 22:12:14 +03:00
|
|
|
error!("********************************************************************************");
|
|
|
|
error!("UI broke! Primary sim:");
|
|
|
|
self.primary.sim.dump_before_abort();
|
|
|
|
if let Some((s, _)) = &self.secondary {
|
|
|
|
error!("Secondary sim:");
|
|
|
|
s.sim.dump_before_abort();
|
|
|
|
}
|
2018-11-02 00:00:14 +03:00
|
|
|
self.save_editor_state();
|
|
|
|
panic::resume_unwind(err);
|
2018-10-22 05:23:47 +03:00
|
|
|
}
|
|
|
|
}
|
2018-09-13 08:43:58 +03:00
|
|
|
}
|
2018-09-13 03:39:53 +03:00
|
|
|
|
2018-10-06 21:50:55 +03:00
|
|
|
fn get_mut_canvas(&mut self) -> &mut Canvas {
|
2018-10-22 05:23:47 +03:00
|
|
|
&mut self.canvas
|
2018-10-06 21:50:55 +03:00
|
|
|
}
|
|
|
|
|
2018-12-04 20:58:34 +03:00
|
|
|
fn draw(&self, g: &mut GfxCtx, hints: RenderingHints) {
|
2018-10-26 22:55:59 +03:00
|
|
|
g.clear(
|
|
|
|
self.cs
|
|
|
|
.borrow_mut()
|
|
|
|
.get("map background", Color::rgb(242, 239, 233)),
|
|
|
|
);
|
2018-10-22 05:23:47 +03:00
|
|
|
|
|
|
|
let (statics, dynamics) = self.primary.draw_map.get_objects_onscreen(
|
2018-11-02 01:29:35 +03:00
|
|
|
self.canvas.get_screen_bounds(),
|
2018-12-06 05:44:23 +03:00
|
|
|
self.primary_plugins.debug_mode(),
|
2018-10-22 05:23:47 +03:00
|
|
|
&self.primary.map,
|
2018-11-23 00:02:04 +03:00
|
|
|
self.get_draw_agent_source(),
|
2018-10-22 05:23:47 +03:00
|
|
|
self,
|
|
|
|
);
|
|
|
|
for obj in statics.into_iter() {
|
|
|
|
let opts = RenderOptions {
|
2018-12-04 20:58:34 +03:00
|
|
|
color: self.color_obj(obj.get_id(), &hints),
|
2018-10-22 05:23:47 +03:00
|
|
|
cam_zoom: self.canvas.cam_zoom,
|
2018-12-06 05:44:23 +03:00
|
|
|
debug_mode: self.primary_plugins.layers().debug_mode.is_enabled(),
|
2018-10-22 05:23:47 +03:00
|
|
|
};
|
|
|
|
obj.draw(
|
|
|
|
g,
|
|
|
|
opts,
|
|
|
|
Ctx {
|
2018-10-25 01:59:53 +03:00
|
|
|
cs: &mut self.cs.borrow_mut(),
|
2018-10-22 05:23:47 +03:00
|
|
|
map: &self.primary.map,
|
2018-10-22 19:43:48 +03:00
|
|
|
draw_map: &self.primary.draw_map,
|
2018-10-22 05:23:47 +03:00
|
|
|
canvas: &self.canvas,
|
|
|
|
sim: &self.primary.sim,
|
2018-12-04 20:58:34 +03:00
|
|
|
hints: &hints,
|
2018-10-22 05:23:47 +03:00
|
|
|
},
|
|
|
|
);
|
|
|
|
}
|
|
|
|
for obj in dynamics.into_iter() {
|
|
|
|
let opts = RenderOptions {
|
2018-12-04 20:58:34 +03:00
|
|
|
color: self.color_obj(obj.get_id(), &hints),
|
2018-10-22 05:23:47 +03:00
|
|
|
cam_zoom: self.canvas.cam_zoom,
|
2018-12-06 05:44:23 +03:00
|
|
|
debug_mode: self.primary_plugins.layers().debug_mode.is_enabled(),
|
2018-10-22 05:23:47 +03:00
|
|
|
};
|
|
|
|
obj.draw(
|
|
|
|
g,
|
|
|
|
opts,
|
|
|
|
Ctx {
|
2018-10-25 01:59:53 +03:00
|
|
|
cs: &mut self.cs.borrow_mut(),
|
2018-10-22 05:23:47 +03:00
|
|
|
map: &self.primary.map,
|
2018-10-22 19:43:48 +03:00
|
|
|
draw_map: &self.primary.draw_map,
|
2018-10-22 05:23:47 +03:00
|
|
|
canvas: &self.canvas,
|
|
|
|
sim: &self.primary.sim,
|
2018-12-04 20:58:34 +03:00
|
|
|
hints: &hints,
|
2018-10-22 05:23:47 +03:00
|
|
|
},
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2018-12-06 23:23:32 +03:00
|
|
|
let ctx = Ctx {
|
|
|
|
cs: &mut self.cs.borrow_mut(),
|
|
|
|
map: &self.primary.map,
|
|
|
|
draw_map: &self.primary.draw_map,
|
|
|
|
canvas: &self.canvas,
|
|
|
|
sim: &self.primary.sim,
|
|
|
|
hints: &hints,
|
|
|
|
};
|
|
|
|
if let Some(p) = self.get_active_plugin() {
|
|
|
|
p.draw(g, ctx);
|
|
|
|
} else {
|
|
|
|
// If no other mode was active, give the ambient plugins in ViewMode a chance.
|
|
|
|
self.primary_plugins.view_mode().draw(g, ctx);
|
2018-10-22 19:43:48 +03:00
|
|
|
}
|
2018-11-06 04:48:36 +03:00
|
|
|
|
2018-12-04 20:58:34 +03:00
|
|
|
self.canvas.draw_text(g, hints.osd, BOTTOM_LEFT);
|
2018-10-22 05:23:47 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// All of the state that's bound to a specific map+edit has to live here.
|
|
|
|
// TODO How can we arrange the code so that we statically know that we don't pass anything from UI
|
|
|
|
// to something in PerMapUI?
|
|
|
|
pub struct PerMapUI {
|
|
|
|
pub map: Map,
|
2018-10-22 06:28:51 +03:00
|
|
|
pub draw_map: DrawMap,
|
2018-10-22 05:23:47 +03:00
|
|
|
pub sim: Sim,
|
|
|
|
|
|
|
|
pub current_selection: Option<ID>,
|
|
|
|
pub recalculate_current_selection: bool,
|
2018-10-22 18:42:09 +03:00
|
|
|
pub current_flags: SimFlags,
|
2018-10-22 05:59:37 +03:00
|
|
|
}
|
2018-10-22 05:23:47 +03:00
|
|
|
|
2018-10-22 05:59:37 +03:00
|
|
|
pub struct PluginsPerMap {
|
2018-10-22 05:23:47 +03:00
|
|
|
// Anything that holds onto any kind of ID has to live here!
|
2018-10-22 19:59:18 +03:00
|
|
|
list: Vec<Box<Plugin>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl PluginsPerMap {
|
2018-12-06 05:44:23 +03:00
|
|
|
fn debug_mode(&self) -> &DebugMode {
|
|
|
|
self.list[0].downcast_ref::<DebugMode>().unwrap()
|
2018-10-22 19:59:18 +03:00
|
|
|
}
|
|
|
|
|
2018-12-06 07:00:58 +03:00
|
|
|
fn view_mode(&self) -> &Box<Plugin> {
|
2018-10-22 19:59:18 +03:00
|
|
|
&self.list[1]
|
|
|
|
}
|
|
|
|
|
2018-11-23 00:02:04 +03:00
|
|
|
fn time_travel(&self) -> &TimeTravel {
|
2018-12-06 07:00:58 +03:00
|
|
|
self.list[2].downcast_ref::<TimeTravel>().unwrap()
|
2018-11-23 00:02:04 +03:00
|
|
|
}
|
2018-12-06 05:44:23 +03:00
|
|
|
|
|
|
|
fn layers(&self) -> &ToggleableLayers {
|
|
|
|
&self.list[0].downcast_ref::<DebugMode>().unwrap().layers
|
|
|
|
}
|
|
|
|
|
|
|
|
fn layers_mut(&mut self) -> &mut ToggleableLayers {
|
|
|
|
&mut self.list[0].downcast_mut::<DebugMode>().unwrap().layers
|
|
|
|
}
|
2018-10-22 05:23:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
impl PerMapUI {
|
2018-10-26 20:24:55 +03:00
|
|
|
pub fn new(flags: SimFlags, kml: &Option<String>) -> (PerMapUI, PluginsPerMap) {
|
2018-10-31 01:10:58 +03:00
|
|
|
let mut timer = abstutil::Timer::new("setup PerMapUI");
|
2018-10-29 01:39:24 +03:00
|
|
|
|
2018-11-30 23:36:27 +03:00
|
|
|
let (map, sim) = sim::load(flags.clone(), Some(Tick::from_seconds(30)), &mut timer);
|
2018-11-21 23:26:23 +03:00
|
|
|
let extra_shapes: Vec<kml::ExtraShape> = if let Some(path) = kml {
|
|
|
|
if path.ends_with(".kml") {
|
|
|
|
kml::load(&path, &map.get_gps_bounds(), &mut timer)
|
|
|
|
.expect("Couldn't load extra KML shapes")
|
|
|
|
.shapes
|
|
|
|
} else {
|
|
|
|
let shapes: kml::ExtraShapes =
|
|
|
|
abstutil::read_binary(&path, &mut timer).expect("Couldn't load ExtraShapes");
|
|
|
|
shapes.shapes
|
|
|
|
}
|
2018-10-22 05:23:47 +03:00
|
|
|
} else {
|
|
|
|
Vec::new()
|
|
|
|
};
|
|
|
|
|
2018-10-29 01:39:24 +03:00
|
|
|
timer.start("draw_map");
|
2018-11-30 23:36:27 +03:00
|
|
|
let draw_map = DrawMap::new(&map, extra_shapes, &mut timer);
|
2018-10-29 01:39:24 +03:00
|
|
|
timer.stop("draw_map");
|
2018-10-22 05:23:47 +03:00
|
|
|
|
2018-12-06 05:44:23 +03:00
|
|
|
let debug_mode = DebugMode::new(&map);
|
2018-12-06 20:35:13 +03:00
|
|
|
let view_mode = plugins::view::ViewMode::new(&map, &draw_map, &mut timer);
|
2018-10-29 01:08:03 +03:00
|
|
|
|
2018-10-29 01:39:24 +03:00
|
|
|
timer.done();
|
2018-10-22 05:23:47 +03:00
|
|
|
|
2018-10-22 05:59:37 +03:00
|
|
|
let state = PerMapUI {
|
2018-10-22 05:23:47 +03:00
|
|
|
map,
|
|
|
|
draw_map,
|
|
|
|
sim,
|
|
|
|
|
|
|
|
current_selection: None,
|
|
|
|
recalculate_current_selection: false,
|
|
|
|
current_flags: flags,
|
2018-10-22 05:59:37 +03:00
|
|
|
};
|
|
|
|
let plugins = PluginsPerMap {
|
2018-10-22 19:59:18 +03:00
|
|
|
list: vec![
|
2018-12-06 05:44:23 +03:00
|
|
|
Box::new(debug_mode),
|
2018-12-06 20:35:13 +03:00
|
|
|
Box::new(view_mode),
|
2018-11-23 21:48:29 +03:00
|
|
|
Box::new(plugins::time_travel::TimeTravel::new()),
|
2018-10-22 19:59:18 +03:00
|
|
|
],
|
2018-10-22 05:59:37 +03:00
|
|
|
};
|
|
|
|
(state, plugins)
|
2018-09-13 08:43:58 +03:00
|
|
|
}
|
2018-03-13 18:04:21 +03:00
|
|
|
}
|
|
|
|
|
2018-10-22 05:23:47 +03:00
|
|
|
// aka plugins that don't depend on map
|
|
|
|
struct PluginsPerUI {
|
2018-10-22 19:59:18 +03:00
|
|
|
list: Vec<Box<Plugin>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl PluginsPerUI {
|
2018-12-06 00:56:21 +03:00
|
|
|
fn edit_mode(&self) -> &EditMode {
|
|
|
|
self.list[0].downcast_ref::<EditMode>().unwrap()
|
|
|
|
}
|
2018-10-22 05:23:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
impl UI {
|
|
|
|
pub fn new(flags: SimFlags, kml: Option<String>) -> UI {
|
2018-09-28 00:55:59 +03:00
|
|
|
// Do this first, so anything logged by sim::load isn't lost.
|
2018-10-22 20:47:29 +03:00
|
|
|
let logs = plugins::logs::DisplayLogs::new();
|
2018-09-28 00:55:59 +03:00
|
|
|
|
2018-10-26 20:24:55 +03:00
|
|
|
let (primary, primary_plugins) = PerMapUI::new(flags, &kml);
|
2018-03-13 18:04:21 +03:00
|
|
|
let mut ui = UI {
|
2018-10-22 05:59:37 +03:00
|
|
|
primary,
|
|
|
|
primary_plugins,
|
2018-10-08 22:55:42 +03:00
|
|
|
secondary: None,
|
2018-03-13 18:04:21 +03:00
|
|
|
|
2018-10-21 22:18:25 +03:00
|
|
|
plugins: PluginsPerUI {
|
2018-10-22 19:59:18 +03:00
|
|
|
list: vec![
|
2018-12-06 00:56:21 +03:00
|
|
|
Box::new(EditMode::new()),
|
2018-10-22 19:59:18 +03:00
|
|
|
Box::new(logs),
|
2018-10-22 21:06:03 +03:00
|
|
|
Box::new(plugins::diff_all::DiffAllState::new()),
|
2018-10-22 20:47:29 +03:00
|
|
|
Box::new(plugins::diff_worlds::DiffWorldsState::new()),
|
2018-11-23 02:15:56 +03:00
|
|
|
Box::new(plugins::sim_controls::SimController::new()),
|
2018-10-22 19:59:18 +03:00
|
|
|
],
|
2018-10-21 22:18:25 +03:00
|
|
|
},
|
2018-03-13 18:04:21 +03:00
|
|
|
|
2018-09-13 19:21:41 +03:00
|
|
|
active_plugin: None,
|
|
|
|
|
2018-09-10 03:10:34 +03:00
|
|
|
canvas: Canvas::new(),
|
2018-10-26 20:24:55 +03:00
|
|
|
cs: RefCell::new(ColorScheme::load().unwrap()),
|
2018-10-09 00:12:44 +03:00
|
|
|
|
|
|
|
kml,
|
2018-10-08 22:55:42 +03:00
|
|
|
};
|
|
|
|
|
2018-10-22 05:23:47 +03:00
|
|
|
match abstutil::read_json::<EditorState>("editor_state") {
|
|
|
|
Ok(ref state) if ui.primary.map.get_name().to_string() == state.map_name => {
|
|
|
|
info!("Loaded previous editor_state");
|
|
|
|
ui.canvas.cam_x = state.cam_x;
|
|
|
|
ui.canvas.cam_y = state.cam_y;
|
|
|
|
ui.canvas.cam_zoom = state.cam_zoom;
|
|
|
|
}
|
|
|
|
_ => {
|
2018-11-26 22:37:19 +03:00
|
|
|
warn!("Couldn't load editor_state or it's for a different map, so just focusing on an arbitrary building");
|
|
|
|
// TODO window_size isn't set yet, so this actually kinda breaks
|
2018-12-05 22:45:07 +03:00
|
|
|
let focus_pt = ID::Building(BuildingID(0))
|
|
|
|
.canonical_point(&ui.primary.map, &ui.primary.sim, &ui.primary.draw_map)
|
|
|
|
.or_else(|| {
|
|
|
|
ID::Lane(LaneID(0)).canonical_point(
|
|
|
|
&ui.primary.map,
|
|
|
|
&ui.primary.sim,
|
|
|
|
&ui.primary.draw_map,
|
|
|
|
)
|
2018-12-06 21:05:42 +03:00
|
|
|
})
|
|
|
|
.expect("Can't get canonical_point of BuildingID(0) or Road(0)");
|
2018-12-05 22:45:07 +03:00
|
|
|
ui.canvas.center_on_map_pt(focus_pt);
|
2018-10-22 05:23:47 +03:00
|
|
|
}
|
2018-10-08 22:55:42 +03:00
|
|
|
}
|
2018-10-22 05:22:16 +03:00
|
|
|
|
2018-12-06 05:44:23 +03:00
|
|
|
ui.primary_plugins
|
2018-10-22 19:59:18 +03:00
|
|
|
.layers_mut()
|
|
|
|
.handle_zoom(-1.0, ui.canvas.cam_zoom);
|
2018-09-13 08:43:58 +03:00
|
|
|
|
2018-10-22 05:23:47 +03:00
|
|
|
ui
|
|
|
|
}
|
2018-10-21 22:18:25 +03:00
|
|
|
|
2018-12-04 20:58:34 +03:00
|
|
|
fn inner_event(&mut self, mut input: UserInput) -> RenderingHints {
|
|
|
|
let mut hints = RenderingHints {
|
|
|
|
mode: EventLoopMode::InputOnly,
|
|
|
|
osd: Text::new(),
|
2018-12-05 21:06:32 +03:00
|
|
|
suppress_intersection_icon: None,
|
2018-12-05 21:39:17 +03:00
|
|
|
color_crosswalks: HashMap::new(),
|
2018-12-04 21:31:47 +03:00
|
|
|
hide_crosswalks: HashSet::new(),
|
2018-12-04 23:37:44 +03:00
|
|
|
hide_turn_icons: HashSet::new(),
|
2018-12-04 20:58:34 +03:00
|
|
|
};
|
|
|
|
|
2018-11-02 00:00:14 +03:00
|
|
|
// First update the camera and handle zoom
|
|
|
|
let old_zoom = self.canvas.cam_zoom;
|
|
|
|
self.canvas.handle_event(&mut input);
|
|
|
|
let new_zoom = self.canvas.cam_zoom;
|
2018-12-06 05:44:23 +03:00
|
|
|
self.primary_plugins
|
|
|
|
.layers_mut()
|
|
|
|
.handle_zoom(old_zoom, new_zoom);
|
2018-11-02 00:00:14 +03:00
|
|
|
|
|
|
|
// Always handle mouseover
|
|
|
|
if old_zoom >= MIN_ZOOM_FOR_MOUSEOVER && new_zoom < MIN_ZOOM_FOR_MOUSEOVER {
|
|
|
|
self.primary.current_selection = None;
|
|
|
|
}
|
|
|
|
if !self.canvas.is_dragging()
|
|
|
|
&& input.get_moved_mouse().is_some()
|
|
|
|
&& new_zoom >= MIN_ZOOM_FOR_MOUSEOVER
|
|
|
|
{
|
|
|
|
self.primary.current_selection = self.mouseover_something();
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO Normally we'd return InputOnly here if there was an active plugin, but actually, we
|
|
|
|
// want some keys to always be pressable (sim controller stuff, quitting the game?)
|
|
|
|
|
|
|
|
// If there's an active plugin, just run it.
|
|
|
|
if let Some(idx) = self.active_plugin {
|
2018-12-04 20:58:34 +03:00
|
|
|
if !self.run_plugin(idx, &mut input, &mut hints) {
|
2018-11-02 00:00:14 +03:00
|
|
|
self.active_plugin = None;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Run each plugin, short-circuiting if the plugin claimed it was active.
|
|
|
|
for idx in 0..self.plugins.list.len() + self.primary_plugins.list.len() {
|
2018-12-04 20:58:34 +03:00
|
|
|
if self.run_plugin(idx, &mut input, &mut hints) {
|
2018-11-02 00:00:14 +03:00
|
|
|
self.active_plugin = Some(idx);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if input.unimportant_key_pressed(Key::Escape, ROOT_MENU, "quit") {
|
|
|
|
self.save_editor_state();
|
|
|
|
self.cs.borrow().save();
|
|
|
|
info!("Saved color_scheme");
|
|
|
|
//cpuprofiler::PROFILER.lock().unwrap().stop().unwrap();
|
|
|
|
process::exit(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if self.primary.recalculate_current_selection {
|
|
|
|
self.primary.recalculate_current_selection = false;
|
|
|
|
self.primary.current_selection = self.mouseover_something();
|
|
|
|
}
|
|
|
|
|
2018-12-04 20:58:34 +03:00
|
|
|
input.populate_osd(&mut hints.osd);
|
|
|
|
|
|
|
|
hints
|
2018-11-02 00:00:14 +03:00
|
|
|
}
|
|
|
|
|
2018-07-06 20:02:01 +03:00
|
|
|
fn mouseover_something(&self) -> Option<ID> {
|
2018-09-19 00:41:30 +03:00
|
|
|
let pt = self.canvas.get_cursor_in_map_space();
|
2018-06-23 19:01:53 +03:00
|
|
|
|
2018-10-08 22:55:42 +03:00
|
|
|
let (statics, dynamics) = self.primary.draw_map.get_objects_onscreen(
|
2018-11-02 01:29:35 +03:00
|
|
|
self.canvas.get_screen_bounds(),
|
2018-12-06 05:44:23 +03:00
|
|
|
self.primary_plugins.debug_mode(),
|
2018-10-08 22:55:42 +03:00
|
|
|
&self.primary.map,
|
2018-11-23 00:02:04 +03:00
|
|
|
self.get_draw_agent_source(),
|
2018-09-14 22:58:12 +03:00
|
|
|
self,
|
2018-09-14 22:16:32 +03:00
|
|
|
);
|
|
|
|
// Check front-to-back
|
|
|
|
for obj in dynamics.into_iter() {
|
|
|
|
if obj.contains_pt(pt) {
|
|
|
|
return Some(obj.get_id());
|
2018-06-23 19:01:53 +03:00
|
|
|
}
|
|
|
|
}
|
2018-09-14 22:16:32 +03:00
|
|
|
for obj in statics.into_iter().rev() {
|
|
|
|
if obj.contains_pt(pt) {
|
|
|
|
return Some(obj.get_id());
|
2018-09-14 00:09:55 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-23 19:01:53 +03:00
|
|
|
None
|
|
|
|
}
|
|
|
|
|
2018-12-04 20:58:34 +03:00
|
|
|
fn color_obj(&self, id: ID, hints: &RenderingHints) -> Option<Color> {
|
2018-10-08 22:55:42 +03:00
|
|
|
if Some(id) == self.primary.current_selection {
|
2018-10-25 01:59:53 +03:00
|
|
|
return Some(self.cs.borrow_mut().get("selected", Color::BLUE));
|
2018-09-16 02:50:19 +03:00
|
|
|
}
|
|
|
|
|
2018-10-17 21:55:31 +03:00
|
|
|
let ctx = Ctx {
|
2018-10-25 01:59:53 +03:00
|
|
|
cs: &mut self.cs.borrow_mut(),
|
2018-10-17 21:55:31 +03:00
|
|
|
map: &self.primary.map,
|
2018-10-22 19:43:48 +03:00
|
|
|
draw_map: &self.primary.draw_map,
|
2018-10-17 21:55:31 +03:00
|
|
|
canvas: &self.canvas,
|
|
|
|
sim: &self.primary.sim,
|
2018-12-04 20:58:34 +03:00
|
|
|
hints,
|
2018-10-17 21:55:31 +03:00
|
|
|
};
|
2018-09-16 02:50:19 +03:00
|
|
|
if let Some(p) = self.get_active_plugin() {
|
2018-12-06 07:00:58 +03:00
|
|
|
p.color_for(id, ctx)
|
|
|
|
} else {
|
|
|
|
// If no other mode was active, give the ambient plugins in ViewMode a chance.
|
|
|
|
self.primary_plugins.view_mode().color_for(id, ctx)
|
2018-09-16 02:50:19 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-22 19:59:18 +03:00
|
|
|
fn get_active_plugin(&self) -> Option<&Box<Plugin>> {
|
2018-09-15 00:41:58 +03:00
|
|
|
let idx = self.active_plugin?;
|
2018-10-22 19:59:18 +03:00
|
|
|
let len = self.plugins.list.len();
|
|
|
|
if idx < len {
|
|
|
|
Some(&self.plugins.list[idx])
|
|
|
|
} else {
|
|
|
|
Some(&self.primary_plugins.list[idx - len])
|
|
|
|
}
|
2018-09-15 00:41:58 +03:00
|
|
|
}
|
2018-10-22 06:18:15 +03:00
|
|
|
|
2018-12-04 20:58:34 +03:00
|
|
|
fn run_plugin(
|
|
|
|
&mut self,
|
|
|
|
idx: usize,
|
|
|
|
input: &mut UserInput,
|
|
|
|
hints: &mut RenderingHints,
|
|
|
|
) -> bool {
|
2018-10-22 18:42:09 +03:00
|
|
|
let active = {
|
2018-11-23 02:15:56 +03:00
|
|
|
let mut ctx = PluginCtx {
|
2018-10-22 18:42:09 +03:00
|
|
|
primary: &mut self.primary,
|
2018-11-23 02:15:56 +03:00
|
|
|
primary_plugins: None,
|
2018-10-22 18:42:09 +03:00
|
|
|
secondary: &mut self.secondary,
|
|
|
|
canvas: &mut self.canvas,
|
2018-10-25 01:59:53 +03:00
|
|
|
cs: &mut self.cs.borrow_mut(),
|
2018-10-22 18:42:09 +03:00
|
|
|
input,
|
2018-12-04 20:58:34 +03:00
|
|
|
hints,
|
2018-10-22 18:42:09 +03:00
|
|
|
kml: &self.kml,
|
|
|
|
};
|
2018-10-22 19:59:18 +03:00
|
|
|
let len = self.plugins.list.len();
|
|
|
|
if idx < len {
|
2018-11-23 02:15:56 +03:00
|
|
|
ctx.primary_plugins = Some(&mut self.primary_plugins);
|
2018-10-22 19:59:18 +03:00
|
|
|
self.plugins.list[idx].event(ctx)
|
|
|
|
} else {
|
|
|
|
self.primary_plugins.list[idx - len].event(ctx)
|
|
|
|
}
|
2018-10-22 06:18:15 +03:00
|
|
|
};
|
2018-10-22 18:42:09 +03:00
|
|
|
active
|
2018-10-22 06:18:15 +03:00
|
|
|
}
|
2018-11-02 00:00:14 +03:00
|
|
|
|
|
|
|
fn save_editor_state(&self) {
|
|
|
|
let state = EditorState {
|
|
|
|
map_name: self.primary.map.get_name().clone(),
|
|
|
|
cam_x: self.canvas.cam_x,
|
|
|
|
cam_y: self.canvas.cam_y,
|
|
|
|
cam_zoom: self.canvas.cam_zoom,
|
|
|
|
};
|
|
|
|
// TODO maybe make state line up with the map, so loading from a new map doesn't break
|
|
|
|
abstutil::write_json("editor_state", &state).expect("Saving editor_state failed");
|
|
|
|
info!("Saved editor_state");
|
|
|
|
}
|
2018-11-23 00:02:04 +03:00
|
|
|
|
|
|
|
fn get_draw_agent_source(&self) -> Box<&GetDrawAgents> {
|
|
|
|
let tt = self.primary_plugins.time_travel();
|
|
|
|
if tt.is_active() {
|
|
|
|
Box::new(tt)
|
|
|
|
} else {
|
|
|
|
Box::new(&self.primary.sim)
|
|
|
|
}
|
|
|
|
}
|
2018-03-13 18:04:21 +03:00
|
|
|
}
|
2018-06-22 21:01:44 +03:00
|
|
|
|
|
|
|
#[derive(Serialize, Deserialize, Debug)]
|
|
|
|
pub struct EditorState {
|
2018-09-09 23:21:53 +03:00
|
|
|
pub map_name: String,
|
2018-06-22 21:01:44 +03:00
|
|
|
pub cam_x: f64,
|
|
|
|
pub cam_y: f64,
|
|
|
|
pub cam_zoom: f64,
|
|
|
|
}
|
2018-09-14 22:50:28 +03:00
|
|
|
|
2018-09-14 22:58:12 +03:00
|
|
|
pub trait ShowTurnIcons {
|
|
|
|
fn show_icons_for(&self, id: IntersectionID) -> bool;
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ShowTurnIcons for UI {
|
|
|
|
fn show_icons_for(&self, id: IntersectionID) -> bool {
|
2018-12-06 05:44:23 +03:00
|
|
|
self.primary_plugins
|
|
|
|
.layers()
|
|
|
|
.show_all_turn_icons
|
|
|
|
.is_enabled()
|
2018-12-06 00:56:21 +03:00
|
|
|
|| self.plugins.edit_mode().show_turn_icons(id)
|
2018-12-02 00:28:12 +03:00
|
|
|
|| {
|
|
|
|
if let Some(ID::Turn(t)) = self.primary.current_selection {
|
|
|
|
t.parent == id
|
|
|
|
} else {
|
|
|
|
false
|
|
|
|
}
|
|
|
|
}
|
2018-09-14 22:58:12 +03:00
|
|
|
}
|
|
|
|
}
|
2018-10-03 17:47:59 +03:00
|
|
|
|
2018-10-22 19:08:18 +03:00
|
|
|
// This mirrors many, but not all, of the fields in UI.
|
2018-10-22 06:18:15 +03:00
|
|
|
pub struct PluginCtx<'a> {
|
|
|
|
pub primary: &'a mut PerMapUI,
|
2018-11-23 02:15:56 +03:00
|
|
|
// Only filled out for PluginsPerUI, not for PluginsPerMap.
|
|
|
|
pub primary_plugins: Option<&'a mut PluginsPerMap>,
|
2018-10-22 06:18:15 +03:00
|
|
|
pub secondary: &'a mut Option<(PerMapUI, PluginsPerMap)>,
|
|
|
|
pub canvas: &'a mut Canvas,
|
|
|
|
pub cs: &'a mut ColorScheme,
|
|
|
|
pub input: &'a mut UserInput,
|
2018-12-04 20:58:34 +03:00
|
|
|
pub hints: &'a mut RenderingHints,
|
2018-10-22 06:18:15 +03:00
|
|
|
pub kml: &'a Option<String>,
|
2018-10-03 17:47:59 +03:00
|
|
|
}
|