1use utils::map_range;
4use widget::{self, CommonBuilder, UpdateArgs};
5use {Color, Colorable, Point, Scalar, Widget};
6
7#[derive(Copy, Clone, Debug, WidgetCommon_)]
9pub struct Grid<X, Y, I> {
10 #[conrod(common_builder)]
12 pub common: CommonBuilder,
13 pub style: Style,
15 pub min_x: X,
17 pub max_x: X,
19 pub min_y: Y,
21 pub max_y: Y,
23 pub x_offset: Option<X>,
25 pub y_offset: Option<Y>,
27 pub lines: I,
29}
30
31#[derive(Copy, Clone, Debug, Default, PartialEq, WidgetStyle_)]
33pub struct Style {
34 #[conrod(default = "theme.shape_color")]
36 pub color: Option<Color>,
37 #[conrod(default = "1.0")]
39 pub thickness: Option<Scalar>,
40}
41
42#[derive(Copy, Clone, Debug)]
44pub struct Lines<T> {
45 pub step: T,
47 pub offset: Option<T>,
49 pub thickness: Option<Scalar>,
53 pub color: Option<Color>,
57}
58
59#[derive(Copy, Clone, Debug)]
61pub enum Axis<X, Y> {
62 X(Lines<X>),
64 Y(Lines<Y>),
66}
67
68widget_ids! {
69 struct Ids {
70 lines[],
71 }
72}
73
74pub struct State {
76 ids: Ids,
77}
78
79impl<T> Lines<T> {
80 pub fn step(step: T) -> Self {
84 Lines {
85 step: step,
86 offset: None,
87 thickness: None,
88 color: None,
89 }
90 }
91
92 pub fn offset(mut self, offset: T) -> Self {
96 self.offset = Some(offset);
97 self
98 }
99
100 pub fn thickness(mut self, thickness: Scalar) -> Self {
102 self.thickness = Some(thickness);
103 self
104 }
105
106 pub fn color(mut self, color: Color) -> Self {
108 self.color = Some(color);
109 self
110 }
111
112 pub fn x<Y>(self) -> Axis<T, Y> {
114 Axis::X(self)
115 }
116
117 pub fn y<X>(self) -> Axis<X, T> {
119 Axis::Y(self)
120 }
121}
122
123impl<X, Y, I> Grid<X, Y, I> {
124 pub fn new(min_x: X, max_x: X, min_y: Y, max_y: Y, lines: I) -> Grid<X, Y, I::IntoIter>
131 where
132 X: Into<Scalar>,
133 Y: Into<Scalar>,
134 I: IntoIterator<Item = Axis<X, Y>>,
135 {
136 Grid {
137 common: CommonBuilder::default(),
138 style: Style::default(),
139 min_x: min_x,
140 max_x: max_x,
141 min_y: min_y,
142 max_y: max_y,
143 x_offset: None,
144 y_offset: None,
145 lines: lines.into_iter(),
146 }
147 }
148
149 pub fn x_offset(mut self, x: X) -> Self {
151 self.x_offset = Some(x);
152 self
153 }
154
155 pub fn y_offset(mut self, y: Y) -> Self {
157 self.y_offset = Some(y);
158 self
159 }
160}
161
162impl<X, Y, I> Widget for Grid<X, Y, I>
163where
164 X: Into<Scalar>,
165 Y: Into<Scalar>,
166 I: Iterator<Item = Axis<X, Y>>,
167{
168 type State = State;
169 type Style = Style;
170 type Event = ();
171
172 fn init_state(&self, id_gen: widget::id::Generator) -> Self::State {
173 State {
174 ids: Ids::new(id_gen),
175 }
176 }
177
178 fn style(&self) -> Self::Style {
179 self.style.clone()
180 }
181
182 fn update(self, args: UpdateArgs<Self>) -> Self::Event {
184 let UpdateArgs {
185 id,
186 state,
187 style,
188 rect,
189 ui,
190 ..
191 } = args;
192 let Grid {
193 min_x,
194 max_x,
195 min_y,
196 max_y,
197 x_offset,
198 y_offset,
199 lines,
200 ..
201 } = self;
202
203 let min_x_f: Scalar = min_x.into();
204 let max_x_f: Scalar = max_x.into();
205 let len_x_f = max_x_f - min_x_f;
206 let min_y_f: Scalar = min_y.into();
207 let max_y_f: Scalar = max_y.into();
208 let len_y_f = max_y_f - min_y_f;
209
210 let x_to_scalar_len = |x: X| {
211 let x_f: Scalar = x.into();
212 map_range(x_f, 0.0, len_x_f, 0.0, rect.x.len())
213 };
214
215 let y_to_scalar_len = |y: Y| {
216 let y_f: Scalar = y.into();
217 map_range(y_f, 0.0, len_y_f, 0.0, rect.y.len())
218 };
219
220 let color = style.color(&ui.theme);
221 let thickness = style.thickness(&ui.theme);
222 let x_offset_f = x_offset.map(&x_to_scalar_len).unwrap_or(0.0);
223 let y_offset_f = y_offset.map(&y_to_scalar_len).unwrap_or(0.0);
224 let mut line_num = 0;
225
226 let x_line = |x: Scalar| -> (Point, Point) {
227 let a = [x, rect.y.start];
228 let b = [x, rect.y.end];
229 (a, b)
230 };
231
232 let y_line = |y: Scalar| -> (Point, Point) {
233 let a = [rect.x.start, y];
234 let b = [rect.x.end, y];
235 (a, b)
236 };
237
238 macro_rules! draw_lines {
239 ($lines:ident, $offset:expr, $to_scalar:ident, $step_range:expr, $line_points:ident) => {{
240 let offset = $offset + $lines.offset.map(&$to_scalar).unwrap_or(0.0);
241 let thickness = $lines.thickness.unwrap_or(thickness);
242 let color = $lines.color.unwrap_or(color);
243 let step = $to_scalar($lines.step);
244 if step == 0.0 {
245 continue;
246 }
247 let mut pos = $step_range.start + offset % step;
248 while $step_range.is_over(pos) {
249 let (a, b) = $line_points(pos);
251
252 if line_num >= state.ids.lines.len() {
254 state.update(|state| {
255 state
256 .ids
257 .lines
258 .resize(line_num + 1, &mut ui.widget_id_generator());
259 });
260 }
261 let line_id = state.ids.lines[line_num];
262
263 widget::Line::abs(a, b)
265 .color(color)
266 .thickness(thickness)
267 .parent(id)
268 .graphics_for(id)
269 .set(line_id, ui);
270
271 pos += step;
272 line_num += 1;
273 }
274 }};
275 }
276
277 for axis in lines {
278 match axis {
279 Axis::X(lines) => draw_lines!(lines, x_offset_f, x_to_scalar_len, rect.x, x_line),
280 Axis::Y(lines) => draw_lines!(lines, y_offset_f, y_to_scalar_len, rect.y, y_line),
281 }
282 }
283 }
284}
285
286impl<X, Y, I> Colorable for Grid<X, Y, I> {
287 builder_method!(color { style.color = Some(Color) });
288}