widgetry/widgets/
persistent_split.rs1use 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
8pub 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}