mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-25 07:25:47 +03:00
filling out edits manager
This commit is contained in:
parent
53993feebb
commit
b1ef405892
@ -4,7 +4,7 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use serde_cbor;
|
||||
use serde_json;
|
||||
use std;
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::fs::File;
|
||||
use std::hash::Hash;
|
||||
use std::io::{Error, ErrorKind, Read, Write};
|
||||
@ -101,6 +101,16 @@ pub fn deserialize_multimap<
|
||||
Ok(map)
|
||||
}
|
||||
|
||||
// Just list all things from a directory, return sorted by name.
|
||||
pub fn list_all_objects(dir: &str, map_name: &str) -> Vec<String> {
|
||||
let mut results: BTreeSet<String> = BTreeSet::new();
|
||||
for entry in std::fs::read_dir(format!("../data/{}/{}/", dir, map_name)).unwrap() {
|
||||
let name = entry.unwrap().file_name().into_string().unwrap();
|
||||
results.insert(name);
|
||||
}
|
||||
results.into_iter().collect()
|
||||
}
|
||||
|
||||
// Load all serialized things from a directory, return sorted by name.
|
||||
pub fn load_all_objects<T: DeserializeOwned>(dir: &str, map_name: &str) -> Vec<(String, T)> {
|
||||
let mut tree: BTreeMap<String, T> = BTreeMap::new();
|
||||
|
@ -10,6 +10,6 @@ mod io;
|
||||
pub use abst_multimap::MultiMap;
|
||||
pub use clone::Cloneable;
|
||||
pub use io::{
|
||||
deserialize_btreemap, deserialize_multimap, load_all_objects, read_binary, read_json,
|
||||
serialize_btreemap, serialize_multimap, to_json, write_binary, write_json,
|
||||
deserialize_btreemap, deserialize_multimap, list_all_objects, load_all_objects, read_binary,
|
||||
read_json, serialize_btreemap, serialize_multimap, to_json, write_binary, write_json,
|
||||
};
|
||||
|
@ -1,22 +1,134 @@
|
||||
use abstutil;
|
||||
use control::ControlMap;
|
||||
use ezgui::{Canvas, GfxCtx, UserInput};
|
||||
use ezgui::{Canvas, GfxCtx, UserInput, Wizard, WrappedWizard};
|
||||
use map_model::Map;
|
||||
use objects::SIM_SETUP;
|
||||
use piston::input::Key;
|
||||
use plugins::road_editor::RoadEditor;
|
||||
use plugins::Colorizer;
|
||||
use sim::{MapEdits, SimFlags};
|
||||
|
||||
// TODO ahh, something needs to remember edits_name.
|
||||
pub struct EditsManager {
|
||||
current_flags: SimFlags,
|
||||
state: State,
|
||||
}
|
||||
|
||||
pub struct EditsManager {}
|
||||
enum State {
|
||||
Inactive,
|
||||
ManageEdits(Wizard),
|
||||
}
|
||||
|
||||
impl EditsManager {
|
||||
pub fn new() -> EditsManager {
|
||||
EditsManager {}
|
||||
pub fn new(current_flags: SimFlags) -> EditsManager {
|
||||
EditsManager {
|
||||
current_flags,
|
||||
state: State::Inactive,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn event(&mut self, input: &mut UserInput, map: &Map, control_map: &ControlMap) -> bool {
|
||||
false
|
||||
pub fn event(
|
||||
&mut self,
|
||||
input: &mut UserInput,
|
||||
map: &Map,
|
||||
control_map: &ControlMap,
|
||||
road_editor: &RoadEditor,
|
||||
new_flags: &mut Option<SimFlags>,
|
||||
) -> bool {
|
||||
let mut new_state: Option<State> = None;
|
||||
match self.state {
|
||||
State::Inactive => {
|
||||
if input.unimportant_key_pressed(Key::Q, SIM_SETUP, "manage map edits") {
|
||||
new_state = Some(State::ManageEdits(Wizard::new()));
|
||||
}
|
||||
}
|
||||
State::ManageEdits(ref mut wizard) => {
|
||||
if manage_edits(
|
||||
&mut self.current_flags,
|
||||
map,
|
||||
control_map,
|
||||
road_editor,
|
||||
new_flags,
|
||||
wizard.wrap(input),
|
||||
).is_some()
|
||||
{
|
||||
} else if wizard.aborted() {
|
||||
new_state = Some(State::Inactive);
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(s) = new_state {
|
||||
self.state = s;
|
||||
}
|
||||
match self.state {
|
||||
State::Inactive => false,
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn draw(&self, g: &mut GfxCtx, canvas: &Canvas) {}
|
||||
pub fn draw(&self, g: &mut GfxCtx, canvas: &Canvas) {
|
||||
match self.state {
|
||||
State::ManageEdits(ref wizard) => {
|
||||
wizard.draw(g, canvas);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Colorizer for EditsManager {}
|
||||
|
||||
fn manage_edits(
|
||||
current_flags: &mut SimFlags,
|
||||
map: &Map,
|
||||
control_map: &ControlMap,
|
||||
road_editor: &RoadEditor,
|
||||
new_flags: &mut Option<SimFlags>,
|
||||
mut wizard: WrappedWizard,
|
||||
) -> Option<()> {
|
||||
// TODO Indicate how many edits are there / if there are any unsaved edits
|
||||
let load = "Load other map edits";
|
||||
let save_new = "Save these new map edits";
|
||||
let save_existing = &format!("Save {}", current_flags.edits_name);
|
||||
let choices: Vec<&str> = if current_flags.edits_name == "no_edits" {
|
||||
vec![save_new, load]
|
||||
} else {
|
||||
vec![save_existing, load]
|
||||
};
|
||||
match wizard.choose_string("Manage map edits", choices)?.as_str() {
|
||||
x if x == save_new => {
|
||||
let name = wizard.input_string("Name the map edits")?;
|
||||
save(&name, map, control_map, road_editor);
|
||||
// No need to reload everything
|
||||
current_flags.edits_name = name;
|
||||
Some(())
|
||||
}
|
||||
x if x == save_existing => {
|
||||
save(¤t_flags.edits_name, map, control_map, road_editor);
|
||||
Some(())
|
||||
}
|
||||
x if x == load => {
|
||||
let map_name = map.get_name().to_string();
|
||||
let edits = abstutil::list_all_objects("edits", &map_name);
|
||||
let edit_refs = edits.iter().map(|s| s.as_str()).collect();
|
||||
let load_name = wizard.choose_string("Load which map edits?", edit_refs)?;
|
||||
let mut flags = current_flags.clone();
|
||||
flags.edits_name = load_name;
|
||||
*new_flags = Some(flags);
|
||||
Some(())
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn save(edits_name: &str, map: &Map, control_map: &ControlMap, road_editor: &RoadEditor) {
|
||||
abstutil::write_json(
|
||||
&format!("../data/edits/{}/{}.json", map.get_name(), edits_name),
|
||||
&MapEdits {
|
||||
edits_name: edits_name.to_string(),
|
||||
map_name: map.get_name().to_string(),
|
||||
road_edits: road_editor.get_edits().clone(),
|
||||
stop_signs: control_map.get_stop_signs_savestate(),
|
||||
traffic_signals: control_map.get_traffic_signals_savestate(),
|
||||
},
|
||||
).expect("Saving map edits failed");
|
||||
}
|
||||
|
212
editor/src/ui.rs
212
editor/src/ui.rs
@ -37,7 +37,7 @@ use plugins::warp::WarpState;
|
||||
use plugins::Colorizer;
|
||||
use render::{DrawMap, RenderOptions};
|
||||
use sim;
|
||||
use sim::{MapEdits, Sim, SimFlags};
|
||||
use sim::{Sim, SimFlags};
|
||||
use std::process;
|
||||
|
||||
// TODO ideally these would be tuned kind of dynamically based on rendering speed
|
||||
@ -48,16 +48,22 @@ 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, &mut Text) -> bool>>,
|
||||
plugins: Vec<Box<Fn(PluginCtx) -> bool>>,
|
||||
|
||||
// Remember these to support loading a new UIWrapper
|
||||
flags: SimFlags,
|
||||
// Remember this to support loading a new UIWrapper
|
||||
kml: Option<String>,
|
||||
}
|
||||
|
||||
impl GUI for UIWrapper {
|
||||
fn event(&mut self, input: UserInput, osd: &mut Text) -> EventLoopMode {
|
||||
self.ui.event(input, osd, &self.plugins)
|
||||
// If we should start over and load something new, fill this out.
|
||||
let mut new_flags: Option<SimFlags> = None;
|
||||
let result = self.ui.event(input, osd, &self.plugins, &mut new_flags);
|
||||
if let Some(flags) = new_flags {
|
||||
info!("Reloading everything...");
|
||||
*self = UIWrapper::new(flags, self.kml.clone());
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
fn draw(&mut self, g: &mut GfxCtx, osd: Text, window_size: Size) {
|
||||
@ -122,7 +128,7 @@ impl UIWrapper {
|
||||
turn_cycler: TurnCyclerState::new(),
|
||||
draw_neighborhoods: DrawNeighborhoodState::new(),
|
||||
scenarios: ScenarioManager::new(),
|
||||
edits_manager: EditsManager::new(),
|
||||
edits_manager: EditsManager::new(flags),
|
||||
logs,
|
||||
|
||||
active_plugin: None,
|
||||
@ -152,11 +158,11 @@ impl UIWrapper {
|
||||
UIWrapper {
|
||||
ui,
|
||||
plugins: vec![
|
||||
Box::new(|ui, input, _osd| {
|
||||
Box::new(|ctx| {
|
||||
let layer_changed = {
|
||||
let mut changed = false;
|
||||
for layer in ui.toggleable_layers().into_iter() {
|
||||
if layer.event(input) {
|
||||
for layer in ctx.ui.toggleable_layers().into_iter() {
|
||||
if layer.event(ctx.input) {
|
||||
changed = true;
|
||||
break;
|
||||
}
|
||||
@ -164,92 +170,119 @@ impl UIWrapper {
|
||||
changed
|
||||
};
|
||||
if layer_changed {
|
||||
ui.current_selection = ui.mouseover_something();
|
||||
ctx.ui.current_selection = ctx.ui.mouseover_something();
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}),
|
||||
Box::new(|ui, input, _osd| {
|
||||
ui.traffic_signal_editor.event(
|
||||
input,
|
||||
&ui.map,
|
||||
&mut ui.control_map,
|
||||
ui.current_selection,
|
||||
Box::new(|ctx| {
|
||||
ctx.ui.traffic_signal_editor.event(
|
||||
ctx.input,
|
||||
&ctx.ui.map,
|
||||
&mut ctx.ui.control_map,
|
||||
ctx.ui.current_selection,
|
||||
)
|
||||
}),
|
||||
Box::new(|ui, input, _osd| {
|
||||
ui.stop_sign_editor.event(
|
||||
input,
|
||||
&ui.map,
|
||||
&mut ui.control_map,
|
||||
ui.current_selection,
|
||||
Box::new(|ctx| {
|
||||
ctx.ui.stop_sign_editor.event(
|
||||
ctx.input,
|
||||
&ctx.ui.map,
|
||||
&mut ctx.ui.control_map,
|
||||
ctx.ui.current_selection,
|
||||
)
|
||||
}),
|
||||
Box::new(|ui, input, _osd| {
|
||||
ui.road_editor.event(
|
||||
input,
|
||||
ui.current_selection,
|
||||
&mut ui.map,
|
||||
&mut ui.draw_map,
|
||||
&ui.control_map,
|
||||
&mut ui.sim,
|
||||
Box::new(|ctx| {
|
||||
ctx.ui.road_editor.event(
|
||||
ctx.input,
|
||||
ctx.ui.current_selection,
|
||||
&mut ctx.ui.map,
|
||||
&mut ctx.ui.draw_map,
|
||||
&ctx.ui.control_map,
|
||||
&mut ctx.ui.sim,
|
||||
)
|
||||
}),
|
||||
Box::new(|ui, input, _osd| ui.search_state.event(input)),
|
||||
Box::new(|ui, input, _osd| {
|
||||
ui.warp.event(
|
||||
input,
|
||||
&ui.map,
|
||||
&ui.sim,
|
||||
&mut ui.canvas,
|
||||
&mut ui.current_selection,
|
||||
Box::new(|ctx| ctx.ui.search_state.event(ctx.input)),
|
||||
Box::new(|ctx| {
|
||||
ctx.ui.warp.event(
|
||||
ctx.input,
|
||||
&ctx.ui.map,
|
||||
&ctx.ui.sim,
|
||||
&mut ctx.ui.canvas,
|
||||
&mut ctx.ui.current_selection,
|
||||
)
|
||||
}),
|
||||
Box::new(|ui, input, _osd| {
|
||||
ui.follow.event(
|
||||
input,
|
||||
&ui.map,
|
||||
&ui.sim,
|
||||
&mut ui.canvas,
|
||||
ui.current_selection,
|
||||
Box::new(|ctx| {
|
||||
ctx.ui.follow.event(
|
||||
ctx.input,
|
||||
&ctx.ui.map,
|
||||
&ctx.ui.sim,
|
||||
&mut ctx.ui.canvas,
|
||||
ctx.ui.current_selection,
|
||||
)
|
||||
}),
|
||||
Box::new(|ui, input, _osd| {
|
||||
ui.show_route.event(input, &ui.sim, ui.current_selection)
|
||||
Box::new(|ctx| {
|
||||
ctx.ui
|
||||
.show_route
|
||||
.event(ctx.input, &ctx.ui.sim, ctx.ui.current_selection)
|
||||
}),
|
||||
Box::new(|ui, input, _osd| {
|
||||
ui.color_picker.event(input, &mut ui.canvas, &mut ui.cs)
|
||||
Box::new(|ctx| {
|
||||
ctx.ui
|
||||
.color_picker
|
||||
.event(ctx.input, &mut ctx.ui.canvas, &mut ctx.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,
|
||||
&ui.map,
|
||||
&mut ui.sim,
|
||||
&ui.control_map,
|
||||
Box::new(|ctx| ctx.ui.steepness_viz.event(ctx.input)),
|
||||
Box::new(|ctx| ctx.ui.osm_classifier.event(ctx.input)),
|
||||
Box::new(|ctx| ctx.ui.hider.event(ctx.input, &mut ctx.ui.current_selection)),
|
||||
Box::new(|ctx| {
|
||||
ctx.ui.debug_objects.event(
|
||||
ctx.ui.current_selection,
|
||||
ctx.input,
|
||||
&ctx.ui.map,
|
||||
&mut ctx.ui.sim,
|
||||
&ctx.ui.control_map,
|
||||
)
|
||||
}),
|
||||
Box::new(|ui, input, _osd| {
|
||||
ui.floodfiller.event(&ui.map, input, ui.current_selection)
|
||||
Box::new(|ctx| {
|
||||
ctx.ui
|
||||
.floodfiller
|
||||
.event(&ctx.ui.map, ctx.input, ctx.ui.current_selection)
|
||||
}),
|
||||
Box::new(|ui, input, _osd| {
|
||||
ui.geom_validator
|
||||
.event(input, &mut ui.canvas, &ui.map, &ui.draw_map)
|
||||
Box::new(|ctx| {
|
||||
ctx.ui.geom_validator.event(
|
||||
ctx.input,
|
||||
&mut ctx.ui.canvas,
|
||||
&ctx.ui.map,
|
||||
&ctx.ui.draw_map,
|
||||
)
|
||||
}),
|
||||
Box::new(|ui, input, _osd| ui.turn_cycler.event(input, ui.current_selection)),
|
||||
Box::new(|ui, input, osd| {
|
||||
ui.draw_neighborhoods.event(input, &ui.canvas, &ui.map, osd)
|
||||
Box::new(|ctx| {
|
||||
ctx.ui
|
||||
.turn_cycler
|
||||
.event(ctx.input, ctx.ui.current_selection)
|
||||
}),
|
||||
Box::new(|ui, input, _osd| ui.scenarios.event(input, &ui.map, &mut ui.sim)),
|
||||
Box::new(|ui, input, _osd| ui.edits_manager.event(input, &ui.map, &ui.control_map)),
|
||||
Box::new(|ui, input, _osd| ui.logs.event(input)),
|
||||
Box::new(|ctx| {
|
||||
ctx.ui
|
||||
.draw_neighborhoods
|
||||
.event(ctx.input, &ctx.ui.canvas, &ctx.ui.map, ctx.osd)
|
||||
}),
|
||||
Box::new(|ctx| {
|
||||
ctx.ui
|
||||
.scenarios
|
||||
.event(ctx.input, &ctx.ui.map, &mut ctx.ui.sim)
|
||||
}),
|
||||
Box::new(|ctx| {
|
||||
ctx.ui.edits_manager.event(
|
||||
ctx.input,
|
||||
&ctx.ui.map,
|
||||
&ctx.ui.control_map,
|
||||
&ctx.ui.road_editor,
|
||||
ctx.new_flags,
|
||||
)
|
||||
}),
|
||||
Box::new(|ctx| ctx.ui.logs.event(ctx.input)),
|
||||
],
|
||||
|
||||
flags,
|
||||
kml,
|
||||
}
|
||||
}
|
||||
@ -336,7 +369,8 @@ impl UI {
|
||||
&mut self,
|
||||
mut input: UserInput,
|
||||
osd: &mut Text,
|
||||
plugins: &Vec<Box<Fn(&mut UI, &mut UserInput, &mut Text) -> bool>>,
|
||||
plugins: &Vec<Box<Fn(PluginCtx) -> bool>>,
|
||||
new_flags: &mut Option<SimFlags>,
|
||||
) -> EventLoopMode {
|
||||
// First update the camera and handle zoom
|
||||
let old_zoom = self.canvas.cam_zoom;
|
||||
@ -362,13 +396,23 @@ impl UI {
|
||||
|
||||
// If there's an active plugin, just run it.
|
||||
if let Some(idx) = self.active_plugin {
|
||||
if !plugins[idx](self, &mut input, osd) {
|
||||
if !plugins[idx](PluginCtx {
|
||||
ui: self,
|
||||
input: &mut input,
|
||||
osd,
|
||||
new_flags,
|
||||
}) {
|
||||
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, &mut input, osd) {
|
||||
if plugin(PluginCtx {
|
||||
ui: self,
|
||||
input: &mut input,
|
||||
osd,
|
||||
new_flags,
|
||||
}) {
|
||||
self.active_plugin = Some(idx);
|
||||
break;
|
||||
}
|
||||
@ -385,18 +429,7 @@ impl UI {
|
||||
// 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");
|
||||
abstutil::write_json("color_scheme", &self.cs).expect("Saving color_scheme failed");
|
||||
// TODO do this from a plugin!
|
||||
abstutil::write_json(
|
||||
&format!("../data/edits/{}/ui.json", self.map.get_name()),
|
||||
&MapEdits {
|
||||
edits_name: "ui".to_string(),
|
||||
map_name: self.map.get_name().to_string(),
|
||||
road_edits: self.road_editor.get_edits().clone(),
|
||||
stop_signs: self.control_map.get_stop_signs_savestate(),
|
||||
traffic_signals: self.control_map.get_traffic_signals_savestate(),
|
||||
},
|
||||
).expect("Saving map_edits.json failed");
|
||||
info!("Saved editor_state, color_scheme, and map_edits.json");
|
||||
info!("Saved editor_state and color_scheme");
|
||||
process::exit(0);
|
||||
}
|
||||
|
||||
@ -612,3 +645,10 @@ impl ShowTurnIcons for UI {
|
||||
|| self.traffic_signal_editor.show_turn_icons(id)
|
||||
}
|
||||
}
|
||||
|
||||
struct PluginCtx<'a> {
|
||||
ui: &'a mut UI,
|
||||
input: &'a mut UserInput,
|
||||
osd: &'a mut Text,
|
||||
new_flags: &'a mut Option<SimFlags>,
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user