use {Color, Colorable, Point, Scalar, Widget};
use widget::{self, CommonBuilder, UpdateArgs};
use utils::map_range;
#[derive(Copy, Clone, Debug, WidgetCommon_)]
pub struct Grid<X, Y, I> {
#[conrod(common_builder)]
pub common: CommonBuilder,
pub style: Style,
pub min_x: X,
pub max_x: X,
pub min_y: Y,
pub max_y: Y,
pub x_offset: Option<X>,
pub y_offset: Option<Y>,
pub lines: I,
}
#[derive(Copy, Clone, Debug, Default, PartialEq, WidgetStyle_)]
pub struct Style {
#[conrod(default = "theme.shape_color")]
pub color: Option<Color>,
#[conrod(default = "1.0")]
pub thickness: Option<Scalar>,
}
#[derive(Copy, Clone, Debug)]
pub struct Lines<T> {
pub step: T,
pub offset: Option<T>,
pub thickness: Option<Scalar>,
pub color: Option<Color>,
}
#[derive(Copy, Clone, Debug)]
pub enum Axis<X, Y> {
X(Lines<X>),
Y(Lines<Y>),
}
widget_ids! {
struct Ids {
lines[],
}
}
pub struct State {
ids: Ids,
}
impl<T> Lines<T> {
pub fn step(step: T) -> Self {
Lines {
step: step,
offset: None,
thickness: None,
color: None,
}
}
pub fn offset(mut self, offset: T) -> Self {
self.offset = Some(offset);
self
}
pub fn thickness(mut self, thickness: Scalar) -> Self {
self.thickness = Some(thickness);
self
}
pub fn color(mut self, color: Color) -> Self {
self.color = Some(color);
self
}
pub fn x<Y>(self) -> Axis<T, Y> {
Axis::X(self)
}
pub fn y<X>(self) -> Axis<X, T> {
Axis::Y(self)
}
}
impl<X, Y, I> Grid<X, Y, I> {
pub fn new(min_x: X, max_x: X, min_y: Y, max_y: Y, lines: I) -> Grid<X, Y, I::IntoIter>
where
X: Into<Scalar>,
Y: Into<Scalar>,
I: IntoIterator<Item = Axis<X, Y>>,
{
Grid {
common: CommonBuilder::default(),
style: Style::default(),
min_x: min_x,
max_x: max_x,
min_y: min_y,
max_y: max_y,
x_offset: None,
y_offset: None,
lines: lines.into_iter(),
}
}
pub fn x_offset(mut self, x: X) -> Self {
self.x_offset = Some(x);
self
}
pub fn y_offset(mut self, y: Y) -> Self {
self.y_offset = Some(y);
self
}
}
impl<X, Y, I> Widget for Grid<X, Y, I>
where
X: Into<Scalar>,
Y: Into<Scalar>,
I: Iterator<Item = Axis<X, Y>>,
{
type State = State;
type Style = Style;
type Event = ();
fn init_state(&self, id_gen: widget::id::Generator) -> Self::State {
State { ids: Ids::new(id_gen) }
}
fn style(&self) -> Self::Style {
self.style.clone()
}
fn update(self, args: UpdateArgs<Self>) -> Self::Event {
let UpdateArgs {
id,
state,
style,
rect,
ui,
..
} = args;
let Grid {
min_x,
max_x,
min_y,
max_y,
x_offset,
y_offset,
lines,
..
} = self;
let min_x_f: Scalar = min_x.into();
let max_x_f: Scalar = max_x.into();
let len_x_f = max_x_f - min_x_f;
let min_y_f: Scalar = min_y.into();
let max_y_f: Scalar = max_y.into();
let len_y_f = max_y_f - min_y_f;
let x_to_scalar_len = |x: X| {
let x_f: Scalar = x.into();
map_range(x_f, 0.0, len_x_f, 0.0, rect.x.len())
};
let y_to_scalar_len = |y: Y| {
let y_f: Scalar = y.into();
map_range(y_f, 0.0, len_y_f, 0.0, rect.y.len())
};
let color = style.color(&ui.theme);
let thickness = style.thickness(&ui.theme);
let x_offset_f = x_offset.map(&x_to_scalar_len).unwrap_or(0.0);
let y_offset_f = y_offset.map(&y_to_scalar_len).unwrap_or(0.0);
let mut line_num = 0;
let x_line = |x: Scalar| -> (Point, Point) {
let a = [x, rect.y.start];
let b = [x, rect.y.end];
(a, b)
};
let y_line = |y: Scalar| -> (Point, Point) {
let a = [rect.x.start, y];
let b = [rect.x.end, y];
(a, b)
};
macro_rules! draw_lines {
($lines:ident, $offset:expr, $to_scalar:ident, $step_range:expr, $line_points:ident) => {{
let offset = $offset + $lines.offset.map(&$to_scalar).unwrap_or(0.0);
let thickness = $lines.thickness.unwrap_or(thickness);
let color = $lines.color.unwrap_or(color);
let step = $to_scalar($lines.step);
if step == 0.0 {
continue;
}
let mut pos = $step_range.start + offset % step;
while $step_range.is_over(pos) {
let (a, b) = $line_points(pos);
if line_num >= state.ids.lines.len() {
state.update(|state| {
state.ids.lines.resize(line_num+1, &mut ui.widget_id_generator());
});
}
let line_id = state.ids.lines[line_num];
widget::Line::abs(a, b)
.color(color)
.thickness(thickness)
.parent(id)
.graphics_for(id)
.set(line_id, ui);
pos += step;
line_num += 1;
}
}};
};
for axis in lines {
match axis {
Axis::X(lines) => draw_lines!(lines, x_offset_f, x_to_scalar_len, rect.x, x_line),
Axis::Y(lines) => draw_lines!(lines, y_offset_f, y_to_scalar_len, rect.y, y_line),
}
}
}
}
impl<X, Y, I> Colorable for Grid<X, Y, I> {
builder_method!(color { style.color = Some(Color) });
}