widgetry/widgets/
persistent_split.rs

1use geom::Polygon;
2
3use crate::{
4    Button, ButtonBuilder, Choice, Color, ControlState, Dropdown, EventCtx, GeomBatch, GfxCtx,
5    JustDraw, MultiKey, Outcome, ScreenDims, ScreenPt, Widget, WidgetImpl, WidgetOutput,
6};
7
8// TODO Radio buttons in the menu
9pub struct PersistentSplit<T: Clone + PartialEq> {
10    current_value: T,
11    btn: Button,
12    spacer: JustDraw,
13    dropdown: Dropdown<T>,
14}
15
16impl<T: 'static + PartialEq + Clone + std::fmt::Debug> PersistentSplit<T> {
17    pub fn widget<MK: Into<Option<MultiKey>>>(
18        ctx: &EventCtx,
19        label: &str,
20        default_value: T,
21        hotkey: MK,
22        choices: Vec<Choice<T>>,
23    ) -> Widget {
24        let outline = ctx.style().btn_outline.outline;
25        Widget::new(Box::new(PersistentSplit::new(
26            ctx,
27            label,
28            default_value,
29            hotkey,
30            choices,
31        )))
32        .outline(outline)
33        .named(label)
34    }
35
36    pub fn new<MK: Into<Option<MultiKey>>>(
37        ctx: &EventCtx,
38        label: &str,
39        default_value: T,
40        hotkey: MK,
41        choices: Vec<Choice<T>>,
42    ) -> PersistentSplit<T> {
43        let dropdown = Dropdown::new(ctx, "change", default_value, choices, true);
44        let mut btn = button_builder(ctx).label_text(dropdown.current_value_label());
45
46        if let Some(multikey) = hotkey.into() {
47            btn = btn.hotkey(multikey)
48        }
49        let btn = btn.build(ctx, label);
50
51        let outline_style = &ctx.style().btn_outline;
52        let (_, outline_color) = outline_style.outline;
53
54        PersistentSplit {
55            current_value: dropdown.current_value(),
56            spacer: JustDraw::wrap(
57                ctx,
58                GeomBatch::from(vec![(
59                    outline_color,
60                    Polygon::rectangle(3.0, btn.get_dims().height),
61                )]),
62            )
63            .take_just_draw(),
64            btn,
65            dropdown,
66        }
67    }
68}
69
70fn button_builder<'a, 'c>(ctx: &EventCtx) -> ButtonBuilder<'a, 'c> {
71    ctx.style()
72        .btn_plain
73        .btn()
74        .outline((0.0, Color::CLEAR), ControlState::Default)
75}
76
77impl<T: 'static + PartialEq + Clone> PersistentSplit<T> {
78    pub fn current_value(&self) -> T {
79        self.current_value.clone()
80    }
81}
82
83impl<T: 'static + Clone + PartialEq> WidgetImpl for PersistentSplit<T> {
84    fn get_dims(&self) -> ScreenDims {
85        let dims1 = self.btn.get_dims();
86        let dims2 = self.spacer.get_dims();
87        let dims3 = self.dropdown.get_dims();
88        ScreenDims::new(
89            dims1.width + dims2.width + dims3.width,
90            dims1.height.max(dims2.height).max(dims3.height),
91        )
92    }
93
94    fn set_pos(&mut self, top_left: ScreenPt) {
95        self.btn.set_pos(top_left);
96        self.spacer
97            .set_pos(ScreenPt::new(top_left.x + self.btn.dims.width, top_left.y));
98        self.dropdown.set_pos(ScreenPt::new(
99            top_left.x + self.btn.dims.width + self.spacer.get_dims().width,
100            top_left.y,
101        ));
102    }
103
104    fn event(&mut self, ctx: &mut EventCtx, output: &mut WidgetOutput) {
105        self.btn.event(ctx, output);
106        if let Outcome::Clicked(_) = output.outcome {
107            return;
108        }
109
110        let mut tmp_output = WidgetOutput::new();
111        self.dropdown.event(ctx, &mut tmp_output);
112
113        let new_value = self.dropdown.current_value();
114        if new_value != self.current_value {
115            self.current_value = new_value;
116            let label = self.btn.action.clone();
117            let mut button_builder =
118                button_builder(ctx).label_text(self.dropdown.current_value_label());
119            if let Some(multikey) = self.btn.hotkey.take() {
120                button_builder = button_builder.hotkey(multikey)
121            }
122            self.btn = button_builder.build(ctx, &label);
123            output.redo_layout = true;
124            output.outcome = Outcome::Changed(label);
125        } else if let Outcome::Focused(_) = tmp_output.outcome {
126            output.outcome = Outcome::Focused(self.btn.action.clone());
127        }
128    }
129
130    fn draw(&self, g: &mut GfxCtx) {
131        self.btn.draw(g);
132        self.spacer.draw(g);
133        self.dropdown.draw(g);
134    }
135}