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);
    }
}