game/sandbox/dashboards/
selector.rs
1use std::cell::RefCell;
2use std::rc::Rc;
3
4use geom::{Polygon, Pt2D};
5use widgetry::{
6 Color, EventCtx, GfxCtx, HorizontalAlignment, Key, Line, Outcome, Panel, State, Text,
7 VerticalAlignment, Widget,
8};
9
10use crate::app::{App, Transition};
11
12pub struct RectangularSelector {
14 panel: Panel,
15 region: Rc<RefCell<Option<Polygon>>>,
16 corners: Option<(Pt2D, Pt2D, bool)>,
17}
18
19impl RectangularSelector {
20 pub fn new_state(
21 ctx: &mut EventCtx,
22 region: Rc<RefCell<Option<Polygon>>>,
23 ) -> Box<dyn State<App>> {
24 Box::new(RectangularSelector {
25 panel: Panel::new_builder(Widget::col(vec![
26 Widget::row(vec![
27 Line("Select a rectangular region")
28 .small_heading()
29 .into_widget(ctx),
30 ctx.style().btn_close_widget(ctx),
31 ]),
32 Text::from_all(vec![
33 Line("Hold "),
34 Line(Key::LeftControl.describe()).fg(ctx.style().text_hotkey_color),
35 Line(", then click and drag to draw"),
36 ])
37 .into_widget(ctx),
38 Widget::row(vec![
39 ctx.style()
40 .btn_solid_primary
41 .text("Apply")
42 .hotkey(Key::Enter)
43 .build_def(ctx),
44 ctx.style()
45 .btn_solid_destructive
46 .text("Clear")
47 .build_def(ctx),
48 ]),
49 ]))
50 .aligned(HorizontalAlignment::Right, VerticalAlignment::Top)
51 .build(ctx),
52 region,
53 corners: None,
54 })
55 }
56}
57
58impl State<App> for RectangularSelector {
59 fn event(&mut self, ctx: &mut EventCtx, _: &mut App) -> Transition {
60 if ctx.is_key_down(Key::LeftControl) {
61 if ctx.input.left_mouse_button_released() {
62 if let Some((_, _, ref mut dragging)) = self.corners {
63 *dragging = false;
64 }
65 }
66 if let Some(pt) = ctx.canvas.get_cursor_in_map_space() {
67 if ctx.input.left_mouse_button_pressed() {
68 self.corners = Some((pt, pt, true));
69 }
70 if let Some((_, ref mut pt2, dragging)) = self.corners {
71 if dragging {
72 *pt2 = pt;
73 }
74 }
75 }
76 } else {
77 ctx.canvas_movement();
78 }
79
80 if let Outcome::Clicked(x) = self.panel.event(ctx) {
81 match x.as_ref() {
82 "close" => {
83 return Transition::Pop;
84 }
85 "Clear" => {
86 self.region.replace(None);
87 return Transition::Pop;
88 }
89 "Apply" => {
90 if let Some(rect) = self
91 .corners
92 .and_then(|(pt1, pt2, _)| Polygon::rectangle_two_corners(pt1, pt2))
93 {
94 self.region.replace(Some(rect));
95 }
96 return Transition::Pop;
97 }
98 _ => unreachable!(),
99 }
100 }
101
102 Transition::Keep
103 }
104
105 fn draw(&self, g: &mut GfxCtx, _: &App) {
106 self.panel.draw(g);
107 if let Some(p) = self.region.borrow().clone() {
108 g.draw_polygon(Color::BLUE.alpha(0.5), p);
109 }
110 if let Some((pt1, pt2, _)) = self.corners {
111 if let Some(p) = Polygon::rectangle_two_corners(pt1, pt2) {
112 g.draw_polygon(Color::RED.alpha(0.5), p);
113 }
114 }
115 }
116}