1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
use geom::Distance;
use map_model::{EditCmd, IntersectionID, TurnID, TurnType};
use widgetry::mapspace::{ObjectID, World, WorldOutcome};
use widgetry::{
Color, EventCtx, GfxCtx, HorizontalAlignment, Key, Line, Outcome, Panel, State, TextExt,
VerticalAlignment, Widget,
};
use crate::app::App;
use crate::app::Transition;
use crate::edit::apply_map_edits;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
struct ID(TurnID);
impl ObjectID for ID {}
pub struct CrosswalkEditor {
id: IntersectionID,
world: World<ID>,
panel: Panel,
}
impl CrosswalkEditor {
pub fn new_state(ctx: &mut EventCtx, app: &mut App, id: IntersectionID) -> Box<dyn State<App>> {
app.primary.current_selection = None;
let map = &app.primary.map;
let mut world = World::bounded(map.get_bounds());
for turn in &map.get_i(id).turns {
if turn.turn_type.pedestrian_crossing() {
let width = Distance::meters(3.0);
let hitbox = if let Some(line) = turn.crosswalk_line() {
line.make_polygons(width)
} else {
turn.geom.make_polygons(width)
};
world
.add(ID(turn.id))
.hitbox(hitbox)
.draw_color(Color::RED.alpha(0.5))
.hover_alpha(0.3)
.clickable()
.build(ctx);
}
}
Box::new(Self {
id,
world,
panel: Panel::new_builder(Widget::col(vec![
Line("Crosswalks editor").small_heading().into_widget(ctx),
"Click a crosswalk to toggle it between marked and unmarked".text_widget(ctx),
Line("Pedestrians can cross using both, but have priority over vehicles at marked zebra crossings").secondary().into_widget(ctx),
ctx.style()
.btn_solid_primary
.text("Finish")
.hotkey(Key::Escape)
.build_def(ctx),
]))
.aligned(HorizontalAlignment::Center, VerticalAlignment::Top)
.build(ctx),
})
}
}
impl State<App> for CrosswalkEditor {
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
if let WorldOutcome::ClickedObject(ID(turn)) = self.world.event(ctx) {
let mut edits = app.primary.map.get_edits().clone();
let old = app.primary.map.get_i_crosswalks_edit(self.id);
let mut new = old.clone();
new.0.insert(
turn,
if old.0[&turn] == TurnType::Crosswalk {
TurnType::UnmarkedCrossing
} else {
TurnType::Crosswalk
},
);
edits.commands.push(EditCmd::ChangeCrosswalks {
i: self.id,
old,
new,
});
apply_map_edits(ctx, app, edits);
return Transition::Replace(Self::new_state(ctx, app, self.id));
}
if let Outcome::Clicked(ref x) = self.panel.event(ctx) {
match x.as_ref() {
"Finish" => {
return Transition::Pop;
}
_ => unreachable!(),
}
}
Transition::Keep
}
fn draw(&self, g: &mut GfxCtx, _: &App) {
self.panel.draw(g);
self.world.draw(g);
}
}