veloren_voxygen/ui/widgets/
radio_list.rs

1use conrod_core::{
2    Color, FontSize, Positionable, Rect, Sizeable, Widget, WidgetCommon, builder_methods, image,
3    text,
4    widget::{self, button},
5    widget_ids,
6};
7
8#[derive(Clone, WidgetCommon)]
9pub struct RadioList<'a, T> {
10    #[conrod(common_builder)]
11    common: widget::CommonBuilder,
12    f_image: button::Image,
13    t_image: button::Image,
14    selected: usize,
15    options_labels: &'a [(&'a T, &'a str)],
16    label_style: widget::text::Style,
17    label_spacing: f64,
18    button_spacing: [f64; 2],
19    button_dims: [f64; 2],
20}
21
22widget_ids! {
23    struct Ids {
24        buttons[],
25        labels[],
26    }
27}
28
29pub struct State {
30    ids: Ids,
31}
32
33impl<'a, T> RadioList<'a, T> {
34    builder_methods! {
35        pub text_color { label_style.color = Some(Color) }
36        pub font_size { label_style.font_size = Some(FontSize) }
37        pub justify { label_style.justify = Some(text::Justify) }
38        pub line_spacing { label_style.line_spacing = Some(f64) }
39        pub label_spacing { label_spacing = f64 }
40        pub button_spacing { button_spacing = [f64; 2] }
41        pub button_dims { button_dims = [f64; 2] }
42    }
43
44    pub fn new(
45        selected: usize,
46        f_image_id: image::Id,
47        t_image_id: image::Id,
48        options_labels: &'a [(&'a T, &'a str)],
49    ) -> Self {
50        Self {
51            common: widget::CommonBuilder::default(),
52            f_image: button::Image {
53                image_id: f_image_id,
54                hover_image_id: None,
55                press_image_id: None,
56                src_rect: None,
57                color: button::ImageColor::None,
58            },
59            t_image: button::Image {
60                image_id: t_image_id,
61                hover_image_id: None,
62                press_image_id: None,
63                src_rect: None,
64                color: button::ImageColor::None,
65            },
66            selected,
67            label_style: widget::text::Style::default(),
68            options_labels,
69            label_spacing: 10.0,
70            button_spacing: [5.0, 5.0],
71            button_dims: [15.0, 15.0],
72        }
73    }
74
75    #[must_use]
76    pub fn source_rectangle(mut self, rect: Rect) -> Self {
77        self.f_image.src_rect = Some(rect);
78        self.t_image.src_rect = Some(rect);
79        self
80    }
81
82    #[must_use]
83    pub fn image_colors(mut self, f_color: Color, t_color: Color) -> Self {
84        self.f_image.color = button::ImageColor::Normal(f_color);
85        self.t_image.color = button::ImageColor::Normal(t_color);
86        self
87    }
88
89    #[must_use]
90    pub fn image_color_with_feedback(mut self, f_color: Color, t_color: Color) -> Self {
91        self.f_image.color = button::ImageColor::WithFeedback(f_color);
92        self.t_image.color = button::ImageColor::WithFeedback(t_color);
93        self
94    }
95
96    #[must_use]
97    pub fn hover_images(mut self, f_id: image::Id, t_id: image::Id) -> Self {
98        self.f_image.hover_image_id = Some(f_id);
99        self.t_image.hover_image_id = Some(t_id);
100        self
101    }
102
103    #[must_use]
104    pub fn press_images(mut self, f_id: image::Id, t_id: image::Id) -> Self {
105        self.f_image.press_image_id = Some(f_id);
106        self.t_image.press_image_id = Some(t_id);
107        self
108    }
109}
110
111impl<'a, T> Widget for RadioList<'a, T> {
112    type Event = Option<(usize, &'a T)>;
113    type State = State;
114    type Style = ();
115
116    fn init_state(&self, id_gen: widget::id::Generator) -> Self::State {
117        State {
118            ids: Ids::new(id_gen),
119        }
120    }
121
122    fn style(&self) -> Self::Style {}
123
124    fn update(self, args: widget::UpdateArgs<Self>) -> Self::Event {
125        let widget::UpdateArgs {
126            id,
127            state,
128            ui,
129            rect,
130            ..
131        } = args;
132        let Self {
133            f_image,
134            t_image,
135            selected,
136            options_labels,
137            label_style,
138            label_spacing,
139            button_spacing,
140            button_dims,
141            ..
142        } = self;
143
144        // Ensure we have enough widget ids
145        let num_items = options_labels.len();
146        if state.ids.buttons.len() < num_items || state.ids.labels.len() < num_items {
147            state.update(|s| {
148                s.ids
149                    .buttons
150                    .resize(num_items, &mut ui.widget_id_generator());
151                s.ids
152                    .labels
153                    .resize(num_items, &mut ui.widget_id_generator());
154            });
155        }
156
157        // Check if the button was clicked.
158        // (Can't use `.set().was_clicked()` because we are changing the image after
159        // setting the widget, which causes flickering since it takes a frame to
160        // change after the mouse button is lifted).
161        let current_selection = (0..num_items)
162            .find(|i| {
163                ui.widget_input(state.ids.buttons[*i])
164                    .clicks()
165                    .left()
166                    .count()
167                    % 2
168                    == 1
169            })
170            .unwrap_or(selected);
171
172        let (x, y, w, h) = rect.x_y_w_h();
173        for (i, _j) in options_labels.iter().enumerate().take(num_items) {
174            let image = if i == current_selection {
175                t_image
176            } else {
177                f_image
178            };
179            // Button
180            let mut button = button::Button::image(image.image_id)
181                .wh(button_dims)
182                //TODO: implement default width / height functions
183                .x_y(
184                    x - w / 2.0 + button_spacing[0],
185                    y - h / 2.0
186                        - i as f64 * (button_dims[1] + button_spacing[1])
187                        - button_spacing[1],
188                )
189                .parent(id);
190            button.show = image;
191            button.set(state.ids.buttons[i], ui);
192            // Label
193            widget::Text::new(options_labels[i].1)
194                .graphics_for(state.ids.buttons[i])
195                .parent(id)
196                .with_style(label_style)
197                .right_from(state.ids.buttons[i], label_spacing)
198                .set(state.ids.labels[i], ui);
199        }
200
201        if current_selection != selected {
202            Some((current_selection, options_labels[current_selection].0))
203        } else {
204            None
205        }
206    }
207}