conrod_core/widget/
toggle.rs1use position::{self, Align};
4use text;
5use widget;
6use {Borderable, Color, Colorable, FontSize, Labelable, Positionable, Scalar, Widget};
7
8#[derive(Clone, WidgetCommon_)]
16pub struct Toggle<'a> {
17 #[conrod(common_builder)]
18 common: widget::CommonBuilder,
19 value: bool,
20 maybe_label: Option<&'a str>,
21 style: Style,
22 pub enabled: bool,
24}
25
26#[derive(Copy, Clone, Debug, Default, PartialEq, WidgetStyle_)]
28pub struct Style {
29 #[conrod(default = "theme.shape_color")]
31 pub color: Option<Color>,
32 #[conrod(default = "theme.border_width")]
34 pub border: Option<Scalar>,
35 #[conrod(default = "theme.border_color")]
37 pub border_color: Option<Color>,
38 #[conrod(default = "theme.label_color")]
40 pub label_color: Option<Color>,
41 #[conrod(default = "theme.font_size_medium")]
43 pub label_font_size: Option<FontSize>,
44 #[conrod(default = "theme.font_id")]
46 pub label_font_id: Option<Option<text::font::Id>>,
47 #[conrod(default = "position::Relative::Align(Align::Middle)")]
49 pub label_x: Option<position::Relative>,
50 #[conrod(default = "position::Relative::Align(Align::Middle)")]
52 pub label_y: Option<position::Relative>,
53}
54
55widget_ids! {
56 struct Ids {
57 rectangle,
58 label,
59 }
60}
61
62pub struct State {
64 ids: Ids,
65}
66
67#[derive(Clone, Debug)]
72#[allow(missing_copy_implementations)]
73pub struct TimesClicked {
74 state: bool,
75 count: u16,
76}
77
78impl Iterator for TimesClicked {
79 type Item = bool;
80 fn next(&mut self) -> Option<Self::Item> {
81 if self.count > 0 {
82 self.count -= 1;
83 self.state = !self.state;
84 Some(self.state)
85 } else {
86 None
87 }
88 }
89}
90
91impl<'a> Toggle<'a> {
92 pub fn new(value: bool) -> Toggle<'a> {
94 Toggle {
95 common: widget::CommonBuilder::default(),
96 style: Style::default(),
97 maybe_label: None,
98 value: value,
99 enabled: true,
100 }
101 }
102
103 pub fn label_font_id(mut self, font_id: text::font::Id) -> Self {
105 self.style.label_font_id = Some(Some(font_id));
106 self
107 }
108
109 pub fn label_x(mut self, x: position::Relative) -> Self {
111 self.style.label_x = Some(x);
112 self
113 }
114
115 pub fn label_y(mut self, y: position::Relative) -> Self {
117 self.style.label_y = Some(y);
118 self
119 }
120
121 builder_methods! {
122 pub enabled { enabled = bool }
123 }
124}
125
126impl<'a> Widget for Toggle<'a> {
127 type State = State;
128 type Style = Style;
129 type Event = TimesClicked;
130
131 fn init_state(&self, id_gen: widget::id::Generator) -> Self::State {
132 State {
133 ids: Ids::new(id_gen),
134 }
135 }
136
137 fn style(&self) -> Self::Style {
138 self.style.clone()
139 }
140
141 fn update(self, args: widget::UpdateArgs<Self>) -> Self::Event {
143 let widget::UpdateArgs {
144 id,
145 state,
146 style,
147 rect,
148 ui,
149 ..
150 } = args;
151 let Toggle {
152 value,
153 enabled,
154 maybe_label,
155 ..
156 } = self;
157
158 let times_clicked = TimesClicked {
159 state: value,
160 count: if enabled {
161 let input = ui.widget_input(id);
162 (input.clicks().left().count() + input.taps().count()) as u16
163 } else {
164 0
165 },
166 };
167
168 let dim = rect.dim();
170 let border = style.border(ui.theme());
171 let color = {
172 let color = style.color(ui.theme());
173 let new_value = times_clicked.clone().last().unwrap_or(value);
174 let color = if new_value {
175 color
176 } else {
177 color.with_luminance(0.1)
178 };
179 match ui.widget_input(id).mouse() {
180 Some(mouse) => {
181 if mouse.buttons.left().is_down() {
182 color.clicked()
183 } else {
184 color.highlighted()
185 }
186 }
187 None => color,
188 }
189 };
190 let border_color = style.border_color(ui.theme());
191 widget::BorderedRectangle::new(dim)
192 .middle_of(id)
193 .graphics_for(id)
194 .color(color)
195 .border(border)
196 .border_color(border_color)
197 .set(state.ids.rectangle, ui);
198
199 if let Some(label) = maybe_label {
201 let color = style.label_color(ui.theme());
202 let font_size = style.label_font_size(ui.theme());
203 let font_id = style.label_font_id(&ui.theme).or(ui.fonts.ids().next());
204 let x = style.label_x(&ui.theme);
205 let y = style.label_y(&ui.theme);
206 widget::Text::new(label)
207 .and_then(font_id, widget::Text::font_id)
208 .x_position_relative_to(id, x)
209 .y_position_relative_to(id, y)
210 .graphics_for(id)
211 .color(color)
212 .font_size(font_size)
213 .set(state.ids.label, ui);
214 }
215
216 times_clicked
217 }
218}
219
220impl<'a> Colorable for Toggle<'a> {
221 builder_method!(color { style.color = Some(Color) });
222}
223
224impl<'a> Borderable for Toggle<'a> {
225 builder_methods! {
226 border { style.border = Some(Scalar) }
227 border_color { style.border_color = Some(Color) }
228 }
229}
230
231impl<'a> Labelable<'a> for Toggle<'a> {
232 builder_methods! {
233 label { maybe_label = Some(&'a str) }
234 label_color { style.label_color = Some(Color) }
235 label_font_size { style.label_font_size = Some(FontSize) }
236 }
237}