mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-11-25 11:44:25 +03:00
converting editmode over
This commit is contained in:
parent
f86e9666d5
commit
6f2f6a3a75
@ -7,7 +7,7 @@ mod objects;
|
||||
mod polygons;
|
||||
|
||||
use crate::common::CommonState;
|
||||
//use crate::edit::EditMode;
|
||||
use crate::edit::EditMode;
|
||||
use crate::helpers::ID;
|
||||
use crate::sandbox::SandboxMode;
|
||||
use crate::state::{State, Transition};
|
||||
@ -136,13 +136,15 @@ impl State for DebugMode {
|
||||
}
|
||||
if self.menu.action("sandbox mode") {
|
||||
return (
|
||||
Transition::Push(Box::new(SandboxMode::new(ctx))),
|
||||
Transition::Replace(Box::new(SandboxMode::new(ctx))),
|
||||
EventLoopMode::InputOnly,
|
||||
);
|
||||
}
|
||||
if self.menu.action("edit mode") {
|
||||
//state.mode = Mode::Edit(EditMode::new(ctx, ui));
|
||||
//return EventLoopMode::InputOnly;
|
||||
return (
|
||||
Transition::Replace(Box::new(EditMode::new(ctx, ui))),
|
||||
EventLoopMode::InputOnly,
|
||||
);
|
||||
}
|
||||
|
||||
if self.menu.action("show/hide chokepoints") {
|
||||
@ -558,7 +560,7 @@ impl State for SearchOSM {
|
||||
}
|
||||
}
|
||||
|
||||
fn draw(&self, g: &mut GfxCtx, ui: &UI) {
|
||||
fn draw(&self, g: &mut GfxCtx, _: &UI) {
|
||||
self.entry.draw(g);
|
||||
}
|
||||
}
|
||||
|
@ -3,13 +3,13 @@ mod traffic_signals;
|
||||
|
||||
use crate::common::CommonState;
|
||||
use crate::debug::DebugMode;
|
||||
use crate::game::{GameState, Mode};
|
||||
use crate::helpers::{ColorScheme, ID};
|
||||
use crate::render::{
|
||||
DrawCtx, DrawIntersection, DrawLane, DrawMap, DrawOptions, DrawTurn, Renderable,
|
||||
MIN_ZOOM_FOR_DETAIL,
|
||||
};
|
||||
use crate::sandbox::SandboxMode;
|
||||
use crate::state::{State, Transition};
|
||||
use crate::ui::{PerMapUI, ShowEverything, UI};
|
||||
use abstutil::Timer;
|
||||
use ezgui::{
|
||||
@ -21,13 +21,9 @@ use map_model::{
|
||||
};
|
||||
use std::collections::{BTreeSet, HashMap};
|
||||
|
||||
pub enum EditMode {
|
||||
ViewingDiffs(CommonState, ModalMenu),
|
||||
Saving(Wizard),
|
||||
Loading(Wizard),
|
||||
EditingStopSign(stop_signs::StopSignEditor),
|
||||
EditingTrafficSignal(traffic_signals::TrafficSignalEditor),
|
||||
BulkEditLanes(RoadID, Wizard),
|
||||
pub struct EditMode {
|
||||
common: CommonState,
|
||||
menu: ModalMenu,
|
||||
}
|
||||
|
||||
impl EditMode {
|
||||
@ -35,9 +31,9 @@ impl EditMode {
|
||||
// TODO Warn first?
|
||||
ui.primary.reset_sim();
|
||||
|
||||
EditMode::ViewingDiffs(
|
||||
CommonState::new(),
|
||||
ModalMenu::new(
|
||||
EditMode {
|
||||
common: CommonState::new(),
|
||||
menu: ModalMenu::new(
|
||||
"Map Edit Mode",
|
||||
vec![
|
||||
vec![
|
||||
@ -52,344 +48,346 @@ impl EditMode {
|
||||
.concat(),
|
||||
ctx,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl State for EditMode {
|
||||
fn event(&mut self, ctx: &mut EventCtx, ui: &mut UI) -> (Transition, EventLoopMode) {
|
||||
// The .clone() is probably not that expensive, and it makes later code a bit
|
||||
// easier to read. :)
|
||||
let orig_edits = ui.primary.map.get_edits().clone();
|
||||
let mut txt = Text::prompt("Map Edit Mode");
|
||||
{
|
||||
txt.add_line(orig_edits.edits_name.clone());
|
||||
txt.add_line(format!("{} lanes", orig_edits.lane_overrides.len()));
|
||||
txt.add_line(format!(
|
||||
"{} stop signs ",
|
||||
orig_edits.stop_sign_overrides.len()
|
||||
));
|
||||
txt.add_line(format!(
|
||||
"{} traffic signals",
|
||||
orig_edits.traffic_signal_overrides.len()
|
||||
));
|
||||
txt.add_line("Right-click a lane or intersection to start editing".to_string());
|
||||
}
|
||||
self.menu.handle_event(ctx, Some(txt));
|
||||
|
||||
ctx.canvas.handle_event(ctx.input);
|
||||
|
||||
// TODO Reset when transitioning in/out of this state? Or maybe we just don't draw
|
||||
// the effects of it. Or eventually, the Option<ID> itself will live in here
|
||||
// directly.
|
||||
// TODO Only mouseover lanes and intersections?
|
||||
if ctx.redo_mouseover() {
|
||||
ui.primary.current_selection = ui.recalculate_current_selection(
|
||||
ctx,
|
||||
&ui.primary.sim,
|
||||
&ShowEverything::new(),
|
||||
false,
|
||||
);
|
||||
}
|
||||
if let Some(evmode) = self.common.event(ctx, ui, &mut self.menu) {
|
||||
return (Transition::Keep, evmode);
|
||||
}
|
||||
|
||||
if self.menu.action("quit") {
|
||||
// TODO Warn about unsaved edits
|
||||
// TODO Maybe put a loading screen around these.
|
||||
ui.primary
|
||||
.map
|
||||
.recalculate_pathfinding_after_edits(&mut Timer::new("apply pending map edits"));
|
||||
return (Transition::Pop, EventLoopMode::InputOnly);
|
||||
}
|
||||
if self.menu.action("sandbox mode") {
|
||||
ui.primary
|
||||
.map
|
||||
.recalculate_pathfinding_after_edits(&mut Timer::new("apply pending map edits"));
|
||||
return (
|
||||
Transition::Replace(Box::new(SandboxMode::new(ctx))),
|
||||
EventLoopMode::InputOnly,
|
||||
);
|
||||
}
|
||||
if self.menu.action("debug mode") {
|
||||
ui.primary
|
||||
.map
|
||||
.recalculate_pathfinding_after_edits(&mut Timer::new("apply pending map edits"));
|
||||
return (
|
||||
Transition::Replace(Box::new(DebugMode::new(ctx, ui))),
|
||||
EventLoopMode::InputOnly,
|
||||
);
|
||||
}
|
||||
|
||||
// TODO Only if current edits are unsaved
|
||||
if self.menu.action("save edits") {
|
||||
return (
|
||||
Transition::Push(Box::new(Saving {
|
||||
wizard: Wizard::new(),
|
||||
})),
|
||||
EventLoopMode::InputOnly,
|
||||
);
|
||||
} else if self.menu.action("load different edits") {
|
||||
return (
|
||||
Transition::Push(Box::new(Loading {
|
||||
wizard: Wizard::new(),
|
||||
})),
|
||||
EventLoopMode::InputOnly,
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(ID::Lane(id)) = ui.primary.current_selection {
|
||||
// TODO Urgh, borrow checker.
|
||||
{
|
||||
let lane = ui.primary.map.get_l(id);
|
||||
let road = ui.primary.map.get_r(lane.parent);
|
||||
if lane.lane_type != LaneType::Sidewalk {
|
||||
if let Some(new_type) = next_valid_type(road, lane, &ui.primary.map) {
|
||||
if ctx
|
||||
.input
|
||||
.contextual_action(Key::Space, &format!("toggle to {:?}", new_type))
|
||||
{
|
||||
let mut new_edits = orig_edits.clone();
|
||||
new_edits.lane_overrides.insert(lane.id, new_type);
|
||||
apply_map_edits(&mut ui.primary, &ui.cs, ctx, new_edits);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
let lane = ui.primary.map.get_l(id);
|
||||
let road = ui.primary.map.get_r(lane.parent);
|
||||
if lane.lane_type != LaneType::Sidewalk {
|
||||
for (lt, name, key) in &[
|
||||
(LaneType::Driving, "driving", Key::D),
|
||||
(LaneType::Parking, "parking", Key::P),
|
||||
(LaneType::Biking, "biking", Key::B),
|
||||
(LaneType::Bus, "bus", Key::T),
|
||||
] {
|
||||
if can_change_lane_type(road, lane, *lt, &ui.primary.map)
|
||||
&& ctx
|
||||
.input
|
||||
.contextual_action(*key, &format!("change to {} lane", name))
|
||||
{
|
||||
let mut new_edits = orig_edits.clone();
|
||||
new_edits.lane_overrides.insert(lane.id, *lt);
|
||||
apply_map_edits(&mut ui.primary, &ui.cs, ctx, new_edits);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ctx
|
||||
.input
|
||||
.contextual_action(Key::U, "bulk edit lanes on this road")
|
||||
{
|
||||
return (
|
||||
Transition::Push(Box::new(BulkEditLanes {
|
||||
road: ui.primary.map.get_l(id).parent,
|
||||
wizard: Wizard::new(),
|
||||
})),
|
||||
EventLoopMode::InputOnly,
|
||||
);
|
||||
} else if orig_edits.lane_overrides.contains_key(&id)
|
||||
&& ctx.input.contextual_action(Key::R, "revert")
|
||||
{
|
||||
let mut new_edits = orig_edits.clone();
|
||||
new_edits.lane_overrides.remove(&id);
|
||||
apply_map_edits(&mut ui.primary, &ui.cs, ctx, new_edits);
|
||||
}
|
||||
}
|
||||
if let Some(ID::Intersection(id)) = ui.primary.current_selection {
|
||||
if ui.primary.map.maybe_get_stop_sign(id).is_some() {
|
||||
if ctx
|
||||
.input
|
||||
.contextual_action(Key::E, &format!("edit stop signs for {}", id))
|
||||
{
|
||||
return (
|
||||
Transition::Push(Box::new(stop_signs::StopSignEditor::new(id, ctx, ui))),
|
||||
EventLoopMode::InputOnly,
|
||||
);
|
||||
} else if orig_edits.stop_sign_overrides.contains_key(&id)
|
||||
&& ctx.input.contextual_action(Key::R, "revert")
|
||||
{
|
||||
let mut new_edits = orig_edits.clone();
|
||||
new_edits.stop_sign_overrides.remove(&id);
|
||||
apply_map_edits(&mut ui.primary, &ui.cs, ctx, new_edits);
|
||||
}
|
||||
}
|
||||
if ui.primary.map.maybe_get_traffic_signal(id).is_some() {
|
||||
if ctx
|
||||
.input
|
||||
.contextual_action(Key::E, &format!("edit traffic signal for {}", id))
|
||||
{
|
||||
return (
|
||||
Transition::Push(Box::new(traffic_signals::TrafficSignalEditor::new(
|
||||
id, ctx, ui,
|
||||
))),
|
||||
EventLoopMode::InputOnly,
|
||||
);
|
||||
} else if orig_edits.traffic_signal_overrides.contains_key(&id)
|
||||
&& ctx.input.contextual_action(Key::R, "revert")
|
||||
{
|
||||
let mut new_edits = orig_edits.clone();
|
||||
new_edits.traffic_signal_overrides.remove(&id);
|
||||
apply_map_edits(&mut ui.primary, &ui.cs, ctx, new_edits);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(Transition::Keep, EventLoopMode::InputOnly)
|
||||
}
|
||||
|
||||
pub fn event(state: &mut GameState, ctx: &mut EventCtx) -> EventLoopMode {
|
||||
match state.mode {
|
||||
Mode::Edit(EditMode::ViewingDiffs(ref mut common, ref mut menu)) => {
|
||||
// The .clone() is probably not that expensive, and it makes later code a bit
|
||||
// easier to read. :)
|
||||
let orig_edits = state.ui.primary.map.get_edits().clone();
|
||||
let mut txt = Text::prompt("Map Edit Mode");
|
||||
{
|
||||
txt.add_line(orig_edits.edits_name.clone());
|
||||
txt.add_line(format!("{} lanes", orig_edits.lane_overrides.len()));
|
||||
txt.add_line(format!(
|
||||
"{} stop signs ",
|
||||
orig_edits.stop_sign_overrides.len()
|
||||
));
|
||||
txt.add_line(format!(
|
||||
"{} traffic signals",
|
||||
orig_edits.traffic_signal_overrides.len()
|
||||
));
|
||||
txt.add_line("Right-click a lane or intersection to start editing".to_string());
|
||||
}
|
||||
menu.handle_event(ctx, Some(txt));
|
||||
fn draw_default_ui(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
ctx.canvas.handle_event(ctx.input);
|
||||
fn draw(&self, g: &mut GfxCtx, ui: &UI) {
|
||||
ui.draw(
|
||||
g,
|
||||
self.common.draw_options(ui),
|
||||
&ui.primary.sim,
|
||||
&ShowEverything::new(),
|
||||
);
|
||||
|
||||
// TODO Reset when transitioning in/out of this state? Or maybe we just don't draw
|
||||
// the effects of it. Or eventually, the Option<ID> itself will live in here
|
||||
// directly.
|
||||
// TODO Only mouseover lanes and intersections?
|
||||
if ctx.redo_mouseover() {
|
||||
state.ui.primary.current_selection = state.ui.recalculate_current_selection(
|
||||
ctx,
|
||||
&state.ui.primary.sim,
|
||||
&ShowEverything::new(),
|
||||
false,
|
||||
// More generally we might want to show the diff between two edits, but for now,
|
||||
// just show diff relative to basemap.
|
||||
let edits = ui.primary.map.get_edits();
|
||||
|
||||
let ctx = DrawCtx {
|
||||
cs: &ui.cs,
|
||||
map: &ui.primary.map,
|
||||
draw_map: &ui.primary.draw_map,
|
||||
sim: &ui.primary.sim,
|
||||
};
|
||||
let mut opts = DrawOptions::new();
|
||||
|
||||
// TODO Similar to drawing areas with traffic or not -- would be convenient to just
|
||||
// supply a set of things to highlight and have something else take care of drawing
|
||||
// with detail or not.
|
||||
if g.canvas.cam_zoom >= MIN_ZOOM_FOR_DETAIL {
|
||||
g.enable_hatching();
|
||||
|
||||
for l in edits.lane_overrides.keys() {
|
||||
ctx.draw_map.get_l(*l).draw(g, &opts, &ctx);
|
||||
}
|
||||
for i in edits
|
||||
.stop_sign_overrides
|
||||
.keys()
|
||||
.chain(edits.traffic_signal_overrides.keys())
|
||||
{
|
||||
ctx.draw_map.get_i(*i).draw(g, &opts, &ctx);
|
||||
}
|
||||
|
||||
g.disable_hatching();
|
||||
|
||||
// The hatching covers up the selection outline, so redraw it.
|
||||
match ui.primary.current_selection {
|
||||
Some(ID::Lane(l)) => {
|
||||
g.draw_polygon(
|
||||
ui.cs.get("selected"),
|
||||
&ctx.draw_map.get_l(l).get_outline(&ctx.map),
|
||||
);
|
||||
}
|
||||
if let Some(evmode) = common.event(ctx, &mut state.ui, menu) {
|
||||
return evmode;
|
||||
Some(ID::Intersection(i)) => {
|
||||
g.draw_polygon(
|
||||
ui.cs.get("selected"),
|
||||
&ctx.draw_map.get_i(i).get_outline(&ctx.map),
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
} else {
|
||||
let color = ui.cs.get_def("unzoomed map diffs", Color::RED);
|
||||
for l in edits.lane_overrides.keys() {
|
||||
g.draw_polygon(color, &ctx.map.get_parent(*l).get_thick_polygon().unwrap());
|
||||
}
|
||||
|
||||
if menu.action("quit") {
|
||||
// TODO Warn about unsaved edits
|
||||
// TODO Maybe put a loading screen around these.
|
||||
state
|
||||
.ui
|
||||
.primary
|
||||
.map
|
||||
.recalculate_pathfinding_after_edits(&mut Timer::new(
|
||||
"apply pending map edits",
|
||||
));
|
||||
state.mode = Mode::SplashScreen(Wizard::new(), None);
|
||||
return EventLoopMode::InputOnly;
|
||||
}
|
||||
if menu.action("sandbox mode") {
|
||||
state
|
||||
.ui
|
||||
.primary
|
||||
.map
|
||||
.recalculate_pathfinding_after_edits(&mut Timer::new(
|
||||
"apply pending map edits",
|
||||
));
|
||||
state.mode = Mode::Sandbox(SandboxMode::new(ctx));
|
||||
return EventLoopMode::InputOnly;
|
||||
}
|
||||
if menu.action("debug mode") {
|
||||
state
|
||||
.ui
|
||||
.primary
|
||||
.map
|
||||
.recalculate_pathfinding_after_edits(&mut Timer::new(
|
||||
"apply pending map edits",
|
||||
));
|
||||
state.mode = Mode::Debug(DebugMode::new(ctx, &state.ui));
|
||||
return EventLoopMode::InputOnly;
|
||||
}
|
||||
|
||||
// TODO Only if current edits are unsaved
|
||||
if menu.action("save edits") {
|
||||
state.mode = Mode::Edit(EditMode::Saving(Wizard::new()));
|
||||
return EventLoopMode::InputOnly;
|
||||
} else if menu.action("load different edits") {
|
||||
state.mode = Mode::Edit(EditMode::Loading(Wizard::new()));
|
||||
return EventLoopMode::InputOnly;
|
||||
}
|
||||
|
||||
if let Some(ID::Lane(id)) = state.ui.primary.current_selection {
|
||||
// TODO Urgh, borrow checker.
|
||||
{
|
||||
let lane = state.ui.primary.map.get_l(id);
|
||||
let road = state.ui.primary.map.get_r(lane.parent);
|
||||
if lane.lane_type != LaneType::Sidewalk {
|
||||
if let Some(new_type) =
|
||||
next_valid_type(road, lane, &state.ui.primary.map)
|
||||
{
|
||||
if ctx.input.contextual_action(
|
||||
Key::Space,
|
||||
&format!("toggle to {:?}", new_type),
|
||||
) {
|
||||
let mut new_edits = orig_edits.clone();
|
||||
new_edits.lane_overrides.insert(lane.id, new_type);
|
||||
apply_map_edits(
|
||||
&mut state.ui.primary,
|
||||
&state.ui.cs,
|
||||
ctx,
|
||||
new_edits,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
let lane = state.ui.primary.map.get_l(id);
|
||||
let road = state.ui.primary.map.get_r(lane.parent);
|
||||
if lane.lane_type != LaneType::Sidewalk {
|
||||
for (lt, name, key) in &[
|
||||
(LaneType::Driving, "driving", Key::D),
|
||||
(LaneType::Parking, "parking", Key::P),
|
||||
(LaneType::Biking, "biking", Key::B),
|
||||
(LaneType::Bus, "bus", Key::T),
|
||||
] {
|
||||
if can_change_lane_type(road, lane, *lt, &state.ui.primary.map)
|
||||
&& ctx.input.contextual_action(
|
||||
*key,
|
||||
&format!("change to {} lane", name),
|
||||
)
|
||||
{
|
||||
let mut new_edits = orig_edits.clone();
|
||||
new_edits.lane_overrides.insert(lane.id, *lt);
|
||||
apply_map_edits(
|
||||
&mut state.ui.primary,
|
||||
&state.ui.cs,
|
||||
ctx,
|
||||
new_edits,
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ctx
|
||||
.input
|
||||
.contextual_action(Key::U, "bulk edit lanes on this road")
|
||||
{
|
||||
state.mode = Mode::Edit(EditMode::BulkEditLanes(
|
||||
state.ui.primary.map.get_l(id).parent,
|
||||
Wizard::new(),
|
||||
));
|
||||
} else if orig_edits.lane_overrides.contains_key(&id)
|
||||
&& ctx.input.contextual_action(Key::R, "revert")
|
||||
{
|
||||
let mut new_edits = orig_edits.clone();
|
||||
new_edits.lane_overrides.remove(&id);
|
||||
apply_map_edits(&mut state.ui.primary, &state.ui.cs, ctx, new_edits);
|
||||
}
|
||||
}
|
||||
if let Some(ID::Intersection(id)) = state.ui.primary.current_selection {
|
||||
if state.ui.primary.map.maybe_get_stop_sign(id).is_some() {
|
||||
if ctx
|
||||
.input
|
||||
.contextual_action(Key::E, &format!("edit stop signs for {}", id))
|
||||
{
|
||||
state.mode = Mode::Edit(EditMode::EditingStopSign(
|
||||
stop_signs::StopSignEditor::new(id, ctx, &mut state.ui),
|
||||
));
|
||||
} else if orig_edits.stop_sign_overrides.contains_key(&id)
|
||||
&& ctx.input.contextual_action(Key::R, "revert")
|
||||
{
|
||||
let mut new_edits = orig_edits.clone();
|
||||
new_edits.stop_sign_overrides.remove(&id);
|
||||
apply_map_edits(&mut state.ui.primary, &state.ui.cs, ctx, new_edits);
|
||||
}
|
||||
}
|
||||
if state.ui.primary.map.maybe_get_traffic_signal(id).is_some() {
|
||||
if ctx
|
||||
.input
|
||||
.contextual_action(Key::E, &format!("edit traffic signal for {}", id))
|
||||
{
|
||||
state.mode = Mode::Edit(EditMode::EditingTrafficSignal(
|
||||
traffic_signals::TrafficSignalEditor::new(id, ctx, &mut state.ui),
|
||||
));
|
||||
} else if orig_edits.traffic_signal_overrides.contains_key(&id)
|
||||
&& ctx.input.contextual_action(Key::R, "revert")
|
||||
{
|
||||
let mut new_edits = orig_edits.clone();
|
||||
new_edits.traffic_signal_overrides.remove(&id);
|
||||
apply_map_edits(&mut state.ui.primary, &state.ui.cs, ctx, new_edits);
|
||||
}
|
||||
}
|
||||
}
|
||||
for i in edits
|
||||
.stop_sign_overrides
|
||||
.keys()
|
||||
.chain(edits.traffic_signal_overrides.keys())
|
||||
{
|
||||
opts.override_colors.insert(ID::Intersection(*i), color);
|
||||
ctx.draw_map.get_i(*i).draw(g, &opts, &ctx);
|
||||
}
|
||||
Mode::Edit(EditMode::Saving(ref mut wizard)) => {
|
||||
ctx.canvas.handle_event(ctx.input);
|
||||
if save_edits(wizard.wrap(ctx), &mut state.ui.primary.map).is_some()
|
||||
|| wizard.aborted()
|
||||
{
|
||||
state.mode = Mode::Edit(EditMode::new(ctx, &mut state.ui));
|
||||
}
|
||||
}
|
||||
Mode::Edit(EditMode::Loading(ref mut wizard)) => {
|
||||
ctx.canvas.handle_event(ctx.input);
|
||||
if let Some(new_edits) = load_edits(
|
||||
&state.ui.primary.map,
|
||||
&mut wizard.wrap(ctx),
|
||||
"Load which map edits?",
|
||||
) {
|
||||
apply_map_edits(&mut state.ui.primary, &state.ui.cs, ctx, new_edits);
|
||||
state.mode = Mode::Edit(EditMode::new(ctx, &mut state.ui));
|
||||
} else if wizard.aborted() {
|
||||
state.mode = Mode::Edit(EditMode::new(ctx, &mut state.ui));
|
||||
}
|
||||
}
|
||||
Mode::Edit(EditMode::EditingStopSign(ref mut editor)) => {
|
||||
if editor.event(ctx, &mut state.ui) {
|
||||
state.mode = Mode::Edit(EditMode::new(ctx, &mut state.ui));
|
||||
}
|
||||
}
|
||||
Mode::Edit(EditMode::EditingTrafficSignal(ref mut editor)) => {
|
||||
if editor.event(ctx, &mut state.ui) {
|
||||
state.mode = Mode::Edit(EditMode::new(ctx, &mut state.ui));
|
||||
}
|
||||
}
|
||||
Mode::Edit(EditMode::BulkEditLanes(r, ref mut wizard)) => {
|
||||
ctx.canvas.handle_event(ctx.input);
|
||||
if let Some(edits) = bulk_edit(r, &mut wizard.wrap(ctx), &state.ui.primary.map) {
|
||||
apply_map_edits(&mut state.ui.primary, &state.ui.cs, ctx, edits);
|
||||
state.mode = Mode::Edit(EditMode::new(ctx, &mut state.ui));
|
||||
} else if wizard.aborted() {
|
||||
state.mode = Mode::Edit(EditMode::new(ctx, &mut state.ui));
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
EventLoopMode::InputOnly
|
||||
self.common.draw(g, ui);
|
||||
self.menu.draw(g);
|
||||
}
|
||||
}
|
||||
|
||||
struct Saving {
|
||||
wizard: Wizard,
|
||||
}
|
||||
|
||||
impl State for Saving {
|
||||
fn event(&mut self, ctx: &mut EventCtx, ui: &mut UI) -> (Transition, EventLoopMode) {
|
||||
ctx.canvas.handle_event(ctx.input);
|
||||
if save_edits(self.wizard.wrap(ctx), &mut ui.primary.map).is_some() || self.wizard.aborted()
|
||||
{
|
||||
(Transition::Pop, EventLoopMode::InputOnly)
|
||||
} else {
|
||||
(Transition::Keep, EventLoopMode::InputOnly)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn draw(state: &GameState, g: &mut GfxCtx) {
|
||||
match state.mode {
|
||||
Mode::Edit(EditMode::ViewingDiffs(ref common, ref menu)) => {
|
||||
state.ui.draw(
|
||||
g,
|
||||
common.draw_options(&state.ui),
|
||||
&state.ui.primary.sim,
|
||||
&ShowEverything::new(),
|
||||
);
|
||||
fn draw(&self, g: &mut GfxCtx, _: &UI) {
|
||||
// TODO Still draw the diffs, yo
|
||||
self.wizard.draw(g);
|
||||
}
|
||||
}
|
||||
|
||||
// More generally we might want to show the diff between two edits, but for now,
|
||||
// just show diff relative to basemap.
|
||||
let edits = state.ui.primary.map.get_edits();
|
||||
struct Loading {
|
||||
wizard: Wizard,
|
||||
}
|
||||
|
||||
let ctx = DrawCtx {
|
||||
cs: &state.ui.cs,
|
||||
map: &state.ui.primary.map,
|
||||
draw_map: &state.ui.primary.draw_map,
|
||||
sim: &state.ui.primary.sim,
|
||||
};
|
||||
let mut opts = DrawOptions::new();
|
||||
|
||||
// TODO Similar to drawing areas with traffic or not -- would be convenient to just
|
||||
// supply a set of things to highlight and have something else take care of drawing
|
||||
// with detail or not.
|
||||
if g.canvas.cam_zoom >= MIN_ZOOM_FOR_DETAIL {
|
||||
g.enable_hatching();
|
||||
|
||||
for l in edits.lane_overrides.keys() {
|
||||
ctx.draw_map.get_l(*l).draw(g, &opts, &ctx);
|
||||
}
|
||||
for i in edits
|
||||
.stop_sign_overrides
|
||||
.keys()
|
||||
.chain(edits.traffic_signal_overrides.keys())
|
||||
{
|
||||
ctx.draw_map.get_i(*i).draw(g, &opts, &ctx);
|
||||
}
|
||||
|
||||
g.disable_hatching();
|
||||
|
||||
// The hatching covers up the selection outline, so redraw it.
|
||||
match state.ui.primary.current_selection {
|
||||
Some(ID::Lane(l)) => {
|
||||
g.draw_polygon(
|
||||
state.ui.cs.get("selected"),
|
||||
&ctx.draw_map.get_l(l).get_outline(&ctx.map),
|
||||
);
|
||||
}
|
||||
Some(ID::Intersection(i)) => {
|
||||
g.draw_polygon(
|
||||
state.ui.cs.get("selected"),
|
||||
&ctx.draw_map.get_i(i).get_outline(&ctx.map),
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
} else {
|
||||
let color = state.ui.cs.get_def("unzoomed map diffs", Color::RED);
|
||||
for l in edits.lane_overrides.keys() {
|
||||
g.draw_polygon(color, &ctx.map.get_parent(*l).get_thick_polygon().unwrap());
|
||||
}
|
||||
|
||||
for i in edits
|
||||
.stop_sign_overrides
|
||||
.keys()
|
||||
.chain(edits.traffic_signal_overrides.keys())
|
||||
{
|
||||
opts.override_colors.insert(ID::Intersection(*i), color);
|
||||
ctx.draw_map.get_i(*i).draw(g, &opts, &ctx);
|
||||
}
|
||||
}
|
||||
|
||||
common.draw(g, &state.ui);
|
||||
menu.draw(g);
|
||||
}
|
||||
Mode::Edit(EditMode::Saving(ref wizard))
|
||||
| Mode::Edit(EditMode::Loading(ref wizard))
|
||||
| Mode::Edit(EditMode::BulkEditLanes(_, ref wizard)) => {
|
||||
state.ui.draw(
|
||||
g,
|
||||
DrawOptions::new(),
|
||||
&state.ui.primary.sim,
|
||||
&ShowEverything::new(),
|
||||
);
|
||||
|
||||
// TODO Still draw the diffs, yo
|
||||
wizard.draw(g);
|
||||
}
|
||||
Mode::Edit(EditMode::EditingStopSign(ref editor)) => {
|
||||
editor.draw(g, state);
|
||||
}
|
||||
Mode::Edit(EditMode::EditingTrafficSignal(ref editor)) => {
|
||||
editor.draw(g, state);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
impl State for Loading {
|
||||
fn event(&mut self, ctx: &mut EventCtx, ui: &mut UI) -> (Transition, EventLoopMode) {
|
||||
ctx.canvas.handle_event(ctx.input);
|
||||
if let Some(new_edits) = load_edits(
|
||||
&ui.primary.map,
|
||||
&mut self.wizard.wrap(ctx),
|
||||
"Load which map edits?",
|
||||
) {
|
||||
apply_map_edits(&mut ui.primary, &ui.cs, ctx, new_edits);
|
||||
(Transition::Pop, EventLoopMode::InputOnly)
|
||||
} else if self.wizard.aborted() {
|
||||
(Transition::Pop, EventLoopMode::InputOnly)
|
||||
} else {
|
||||
(Transition::Keep, EventLoopMode::InputOnly)
|
||||
}
|
||||
}
|
||||
|
||||
fn draw(&self, g: &mut GfxCtx, _: &UI) {
|
||||
// TODO Still draw the diffs, yo
|
||||
self.wizard.draw(g);
|
||||
}
|
||||
}
|
||||
|
||||
struct BulkEditLanes {
|
||||
road: RoadID,
|
||||
wizard: Wizard,
|
||||
}
|
||||
|
||||
impl State for BulkEditLanes {
|
||||
fn event(&mut self, ctx: &mut EventCtx, ui: &mut UI) -> (Transition, EventLoopMode) {
|
||||
ctx.canvas.handle_event(ctx.input);
|
||||
if let Some(edits) = bulk_edit(self.road, &mut self.wizard.wrap(ctx), &ui.primary.map) {
|
||||
apply_map_edits(&mut ui.primary, &ui.cs, ctx, edits);
|
||||
(Transition::Pop, EventLoopMode::InputOnly)
|
||||
} else if self.wizard.aborted() {
|
||||
(Transition::Pop, EventLoopMode::InputOnly)
|
||||
} else {
|
||||
(Transition::Keep, EventLoopMode::InputOnly)
|
||||
}
|
||||
}
|
||||
|
||||
fn draw(&self, g: &mut GfxCtx, _: &UI) {
|
||||
// TODO Still draw the diffs, yo
|
||||
self.wizard.draw(g);
|
||||
}
|
||||
}
|
||||
|
||||
fn save_edits(mut wizard: WrappedWizard, map: &mut Map) -> Option<()> {
|
||||
|
@ -1,10 +1,10 @@
|
||||
use crate::common::CommonState;
|
||||
use crate::edit::apply_map_edits;
|
||||
use crate::game::GameState;
|
||||
use crate::helpers::ID;
|
||||
use crate::render::{DrawIntersection, DrawOptions, DrawTurn};
|
||||
use crate::state::{State, Transition};
|
||||
use crate::ui::{ShowEverything, UI};
|
||||
use ezgui::{hotkey, Color, EventCtx, GeomBatch, GfxCtx, Key, ModalMenu, Text};
|
||||
use ezgui::{hotkey, Color, EventCtx, EventLoopMode, GeomBatch, GfxCtx, Key, ModalMenu, Text};
|
||||
use geom::Polygon;
|
||||
use map_model::{IntersectionID, RoadID, TurnID, TurnPriority};
|
||||
use std::collections::HashMap;
|
||||
@ -48,9 +48,10 @@ impl StopSignEditor {
|
||||
selected_turn: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Returns true if the editor is done and we should go back to main edit mode.
|
||||
pub fn event(&mut self, ctx: &mut EventCtx, ui: &mut UI) -> bool {
|
||||
impl State for StopSignEditor {
|
||||
fn event(&mut self, ctx: &mut EventCtx, ui: &mut UI) -> (Transition, EventLoopMode) {
|
||||
self.menu.handle_event(ctx, None);
|
||||
ctx.canvas.handle_event(ctx.input);
|
||||
|
||||
@ -107,23 +108,23 @@ impl StopSignEditor {
|
||||
apply_map_edits(&mut ui.primary, &ui.cs, ctx, new_edits);
|
||||
}
|
||||
} else if self.menu.action("quit") {
|
||||
return true;
|
||||
return (Transition::Pop, EventLoopMode::InputOnly);
|
||||
} else if self.menu.action("reset to default") {
|
||||
let mut new_edits = ui.primary.map.get_edits().clone();
|
||||
new_edits.stop_sign_overrides.remove(&self.id);
|
||||
apply_map_edits(&mut ui.primary, &ui.cs, ctx, new_edits);
|
||||
}
|
||||
false
|
||||
(Transition::Keep, EventLoopMode::InputOnly)
|
||||
}
|
||||
|
||||
pub fn draw(&self, g: &mut GfxCtx, state: &GameState) {
|
||||
state.ui.draw(
|
||||
fn draw(&self, g: &mut GfxCtx, ui: &UI) {
|
||||
ui.draw(
|
||||
g,
|
||||
DrawOptions::new(),
|
||||
&state.ui.primary.sim,
|
||||
&ui.primary.sim,
|
||||
&ShowEverything::new(),
|
||||
);
|
||||
let map = &state.ui.primary.map;
|
||||
let map = &ui.primary.map;
|
||||
let sign = map.get_stop_sign(self.id);
|
||||
|
||||
let mut batch = GeomBatch::new();
|
||||
@ -132,33 +133,31 @@ impl StopSignEditor {
|
||||
// The intersection will already draw enabled stop signs
|
||||
if Some(*r) == self.selected_sign {
|
||||
batch.push(
|
||||
state.ui.cs.get_def("selected stop sign", Color::BLUE),
|
||||
ui.cs.get_def("selected stop sign", Color::BLUE),
|
||||
octagon.clone(),
|
||||
);
|
||||
if !sign.roads[r].enabled {
|
||||
batch.push(state.ui.cs.get("stop sign pole").alpha(0.6), pole.clone());
|
||||
batch.push(ui.cs.get("stop sign pole").alpha(0.6), pole.clone());
|
||||
}
|
||||
} else if !sign.roads[r].enabled {
|
||||
batch.push(
|
||||
state.ui.cs.get("stop sign on side of road").alpha(0.6),
|
||||
ui.cs.get("stop sign on side of road").alpha(0.6),
|
||||
octagon.clone(),
|
||||
);
|
||||
batch.push(state.ui.cs.get("stop sign pole").alpha(0.6), pole.clone());
|
||||
batch.push(ui.cs.get("stop sign pole").alpha(0.6), pole.clone());
|
||||
}
|
||||
}
|
||||
|
||||
for t in &state.ui.primary.draw_map.get_turns(self.id, map) {
|
||||
for t in &ui.primary.draw_map.get_turns(self.id, map) {
|
||||
let arrow_color = match sign.get_priority(t.id) {
|
||||
TurnPriority::Priority => {
|
||||
state.ui.cs.get_def("priority stop sign turn", Color::GREEN)
|
||||
}
|
||||
TurnPriority::Yield => state.ui.cs.get_def("yield stop sign turn", Color::YELLOW),
|
||||
TurnPriority::Stop => state.ui.cs.get_def("stop turn", Color::RED),
|
||||
TurnPriority::Banned => state.ui.cs.get_def("banned turn", Color::BLACK),
|
||||
TurnPriority::Priority => ui.cs.get_def("priority stop sign turn", Color::GREEN),
|
||||
TurnPriority::Yield => ui.cs.get_def("yield stop sign turn", Color::YELLOW),
|
||||
TurnPriority::Stop => ui.cs.get_def("stop turn", Color::RED),
|
||||
TurnPriority::Banned => ui.cs.get_def("banned turn", Color::BLACK),
|
||||
};
|
||||
t.draw_icon(
|
||||
&mut batch,
|
||||
&state.ui.cs,
|
||||
&ui.cs,
|
||||
arrow_color,
|
||||
self.selected_turn == Some(t.id),
|
||||
);
|
||||
@ -167,7 +166,7 @@ impl StopSignEditor {
|
||||
DrawTurn::draw_dashed(
|
||||
map.get_t(id),
|
||||
&mut batch,
|
||||
state.ui.cs.get_def("selected turn", Color::RED),
|
||||
ui.cs.get_def("selected turn", Color::RED),
|
||||
);
|
||||
}
|
||||
batch.draw(g);
|
||||
@ -176,14 +175,14 @@ impl StopSignEditor {
|
||||
if let Some(r) = self.selected_sign {
|
||||
let mut osd = Text::from_line("Stop sign for ".to_string());
|
||||
osd.append(
|
||||
state.ui.primary.map.get_r(r).get_name(),
|
||||
Some(state.ui.cs.get("OSD name color")),
|
||||
ui.primary.map.get_r(r).get_name(),
|
||||
Some(ui.cs.get("OSD name color")),
|
||||
);
|
||||
CommonState::draw_custom_osd(g, osd);
|
||||
} else if let Some(t) = self.selected_turn {
|
||||
CommonState::draw_osd(g, &state.ui, Some(ID::Turn(t)));
|
||||
CommonState::draw_osd(g, ui, Some(ID::Turn(t)));
|
||||
} else {
|
||||
CommonState::draw_osd(g, &state.ui, None);
|
||||
CommonState::draw_osd(g, ui, None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,13 @@
|
||||
use crate::common::CommonState;
|
||||
use crate::edit::apply_map_edits;
|
||||
use crate::game::GameState;
|
||||
use crate::helpers::ID;
|
||||
use crate::render::{draw_signal_cycle, draw_signal_diagram, DrawCtx, DrawOptions, DrawTurn};
|
||||
use crate::state::{State, Transition};
|
||||
use crate::ui::{ShowEverything, UI};
|
||||
use abstutil::Timer;
|
||||
use ezgui::{
|
||||
hotkey, Color, EventCtx, GeomBatch, GfxCtx, Key, ModalMenu, MultiKey, Wizard, WrappedWizard,
|
||||
hotkey, Color, EventCtx, EventLoopMode, GeomBatch, GfxCtx, Key, ModalMenu, MultiKey, Wizard,
|
||||
WrappedWizard,
|
||||
};
|
||||
use geom::Duration;
|
||||
use map_model::{ControlTrafficSignal, Cycle, IntersectionID, Map, TurnID, TurnPriority, TurnType};
|
||||
@ -56,9 +57,10 @@ impl TrafficSignalEditor {
|
||||
icon_selected: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Returns true if the editor is done and we should go back to main edit mode.
|
||||
pub fn event(&mut self, ctx: &mut EventCtx, ui: &mut UI) -> bool {
|
||||
impl State for TrafficSignalEditor {
|
||||
fn event(&mut self, ctx: &mut EventCtx, ui: &mut UI) -> (Transition, EventLoopMode) {
|
||||
self.menu.handle_event(ctx, None);
|
||||
ctx.canvas.handle_event(ctx.input);
|
||||
|
||||
@ -148,7 +150,7 @@ impl TrafficSignalEditor {
|
||||
}
|
||||
} else {
|
||||
if self.menu.action("quit") {
|
||||
return true;
|
||||
return (Transition::Pop, EventLoopMode::InputOnly);
|
||||
}
|
||||
|
||||
if self.current_cycle != 0 && self.menu.action("select previous cycle") {
|
||||
@ -234,41 +236,34 @@ impl TrafficSignalEditor {
|
||||
apply_map_edits(&mut ui.primary, &ui.cs, ctx, new_edits);
|
||||
}
|
||||
|
||||
false
|
||||
(Transition::Keep, EventLoopMode::InputOnly)
|
||||
}
|
||||
|
||||
pub fn draw(&self, g: &mut GfxCtx, state: &GameState) {
|
||||
fn draw(&self, g: &mut GfxCtx, ui: &UI) {
|
||||
{
|
||||
let mut opts = DrawOptions::new();
|
||||
opts.suppress_traffic_signal_details = Some(self.i);
|
||||
state
|
||||
.ui
|
||||
.draw(g, opts, &state.ui.primary.sim, &ShowEverything::new());
|
||||
ui.draw(g, opts, &ui.primary.sim, &ShowEverything::new());
|
||||
}
|
||||
|
||||
let mut batch = GeomBatch::new();
|
||||
let ctx = DrawCtx {
|
||||
cs: &state.ui.cs,
|
||||
map: &state.ui.primary.map,
|
||||
draw_map: &state.ui.primary.draw_map,
|
||||
sim: &state.ui.primary.sim,
|
||||
cs: &ui.cs,
|
||||
map: &ui.primary.map,
|
||||
draw_map: &ui.primary.draw_map,
|
||||
sim: &ui.primary.sim,
|
||||
};
|
||||
let map = &state.ui.primary.map;
|
||||
let map = &ui.primary.map;
|
||||
let cycle = &map.get_traffic_signal(self.i).cycles[self.current_cycle];
|
||||
for t in &state.ui.primary.draw_map.get_turns(self.i, map) {
|
||||
for t in &ui.primary.draw_map.get_turns(self.i, map) {
|
||||
let arrow_color = match cycle.get_priority(t.id) {
|
||||
TurnPriority::Priority => state
|
||||
.ui
|
||||
TurnPriority::Priority => ui
|
||||
.cs
|
||||
.get_def("priority turn in current cycle", Color::GREEN),
|
||||
TurnPriority::Yield => state
|
||||
.ui
|
||||
TurnPriority::Yield => ui
|
||||
.cs
|
||||
.get_def("yield turn in current cycle", Color::rgb(255, 105, 180)),
|
||||
TurnPriority::Banned => state
|
||||
.ui
|
||||
.cs
|
||||
.get_def("turn not in current cycle", Color::BLACK),
|
||||
TurnPriority::Banned => ui.cs.get_def("turn not in current cycle", Color::BLACK),
|
||||
TurnPriority::Stop => panic!("Can't have TurnPriority::Stop in a traffic signal"),
|
||||
};
|
||||
t.draw_icon(
|
||||
@ -280,7 +275,7 @@ impl TrafficSignalEditor {
|
||||
}
|
||||
draw_signal_cycle(cycle, None, &mut batch, &ctx);
|
||||
if let Some(id) = self.icon_selected {
|
||||
DrawTurn::draw_dashed(map.get_t(id), &mut batch, state.ui.cs.get("selected turn"));
|
||||
DrawTurn::draw_dashed(map.get_t(id), &mut batch, ui.cs.get("selected turn"));
|
||||
}
|
||||
batch.draw(g);
|
||||
|
||||
@ -294,9 +289,9 @@ impl TrafficSignalEditor {
|
||||
|
||||
self.menu.draw(g);
|
||||
if let Some(t) = self.icon_selected {
|
||||
CommonState::draw_osd(g, &state.ui, Some(ID::Turn(t)));
|
||||
CommonState::draw_osd(g, ui, Some(ID::Turn(t)));
|
||||
} else {
|
||||
CommonState::draw_osd(g, &state.ui, None);
|
||||
CommonState::draw_osd(g, ui, None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
//use crate::abtest::ABTestMode;
|
||||
use crate::debug::DebugMode;
|
||||
//use crate::edit::EditMode;
|
||||
use crate::edit::EditMode;
|
||||
//use crate::mission::MissionEditMode;
|
||||
use crate::render::DrawOptions;
|
||||
use crate::sandbox::SandboxMode;
|
||||
@ -277,8 +277,8 @@ fn splash_screen(
|
||||
None
|
||||
}
|
||||
}
|
||||
/*x if x == edit => break Some(Mode::Edit(EditMode::new(ctx, ui))),
|
||||
x if x == tutorial => break Some(Mode::Tutorial(TutorialMode::new(ctx, ui))),*/
|
||||
x if x == edit => Some(Transition::Push(Box::new(EditMode::new(ctx, ui)))),
|
||||
//x if x == tutorial => break Some(Mode::Tutorial(TutorialMode::new(ctx, ui))),
|
||||
x if x == debug => Some(Transition::Push(Box::new(DebugMode::new(ctx, ui)))),
|
||||
/*x if x == mission => break Some(Mode::Mission(MissionEditMode::new(ctx, ui))),
|
||||
x if x == abtest => {
|
||||
|
@ -1,7 +1,7 @@
|
||||
//mod abtest;
|
||||
mod common;
|
||||
mod debug;
|
||||
//mod edit;
|
||||
mod edit;
|
||||
mod game;
|
||||
mod helpers;
|
||||
//mod mission;
|
||||
|
@ -7,7 +7,7 @@ mod time_travel;
|
||||
|
||||
use crate::common::{CommonState, SpeedControls};
|
||||
use crate::debug::DebugMode;
|
||||
//use crate::edit::EditMode;
|
||||
use crate::edit::EditMode;
|
||||
use crate::state::{State, Transition};
|
||||
//use crate::mission::input_time;
|
||||
use crate::ui::{ShowEverything, UI};
|
||||
@ -181,8 +181,10 @@ impl State for SandboxMode {
|
||||
);
|
||||
}
|
||||
if self.menu.action("edit mode") {
|
||||
//state.mode = Mode::Edit(EditMode::new(ctx, &mut state.ui));
|
||||
//return EventLoopMode::InputOnly;
|
||||
return (
|
||||
Transition::Replace(Box::new(EditMode::new(ctx, ui))),
|
||||
EventLoopMode::InputOnly,
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(dt) = self.speed.event(ctx, &mut self.menu, ui.primary.sim.time()) {
|
||||
|
Loading…
Reference in New Issue
Block a user