widgetry/tools/
choose_something.rs

1use crate::{
2    Choice, DrawBaselayer, EventCtx, GfxCtx, Line, Menu, Outcome, Panel, State, Transition, Widget,
3};
4
5/// Choose something from a menu, then feed the answer to a callback.
6pub struct ChooseSomething<A, T> {
7    panel: Panel,
8    // Wrapped in an Option so that we can consume it once
9    cb: Option<Box<dyn FnOnce(T, &mut EventCtx, &mut A) -> Transition<A>>>,
10}
11
12impl<A: 'static, T: 'static> ChooseSomething<A, T> {
13    pub fn new_state<I: Into<String>>(
14        ctx: &mut EventCtx,
15        query: I,
16        choices: Vec<Choice<T>>,
17        cb: Box<dyn FnOnce(T, &mut EventCtx, &mut A) -> Transition<A>>,
18    ) -> Box<dyn State<A>> {
19        Box::new(ChooseSomething {
20            panel: Panel::new_builder(Widget::col(vec![
21                Widget::row(vec![
22                    Line(query).small_heading().into_widget(ctx),
23                    ctx.style().btn_close_widget(ctx),
24                ]),
25                Menu::widget(ctx, choices).named("menu"),
26            ]))
27            .build(ctx),
28            cb: Some(cb),
29        })
30    }
31}
32
33impl<A: 'static, T: 'static> State<A> for ChooseSomething<A, T> {
34    fn event(&mut self, ctx: &mut EventCtx, app: &mut A) -> Transition<A> {
35        match self.panel.event(ctx) {
36            Outcome::Clicked(x) => match x.as_ref() {
37                "close" => Transition::Pop,
38                _ => {
39                    let data = self.panel.take_menu_choice::<T>("menu");
40                    // If the callback doesn't replace or pop this ChooseSomething state, then
41                    // it'll break when the user tries to interact with the menu again.
42                    (self.cb.take().unwrap())(data, ctx, app)
43                }
44            },
45            _ => {
46                if ctx.normal_left_click() && ctx.canvas.get_cursor_in_screen_space().is_none() {
47                    return Transition::Pop;
48                }
49                Transition::Keep
50            }
51        }
52    }
53
54    fn draw_baselayer(&self) -> DrawBaselayer {
55        DrawBaselayer::PreviousState
56    }
57
58    fn draw(&self, g: &mut GfxCtx, _: &A) {
59        super::grey_out_map(g);
60        self.panel.draw(g);
61    }
62}