use {Scalar, Ui, UiCell, Widget};
use graph;
use utils;
use widget;
pub type WidgetNum = usize;
pub type ColNum = usize;
pub type RowNum = usize;
pub type Width = Scalar;
pub type Height = Scalar;
pub type PosX = Scalar;
pub type PosY = Scalar;
#[derive(Clone, WidgetCommon_)]
#[allow(missing_copy_implementations)]
pub struct Matrix {
#[conrod(common_builder)]
common: widget::CommonBuilder,
style: Style,
cols: usize,
rows: usize,
}
pub struct State {
indices: Vec<Vec<widget::Id>>,
}
#[derive(Copy, Clone, Debug, Default, PartialEq, WidgetStyle_)]
pub struct Style {
#[conrod(default = "0.0")]
pub cell_pad_w: Option<Scalar>,
#[conrod(default = "0.0")]
pub cell_pad_h: Option<Scalar>,
}
#[derive(Debug)]
#[allow(missing_copy_implementations)]
pub struct Elements {
num_rows: usize,
num_cols: usize,
row: usize,
col: usize,
matrix_id: widget::Id,
elem_w: Scalar,
elem_h: Scalar,
x_min: Scalar, x_max: Scalar,
y_min: Scalar, y_max: Scalar,
}
#[derive(Copy, Clone, Debug)]
pub struct Element {
pub widget_id: widget::Id,
pub row: usize,
pub col: usize,
pub w: Scalar,
pub h: Scalar,
pub rel_x: Scalar,
pub rel_y: Scalar,
matrix_id: widget::Id,
}
impl Matrix {
pub fn new(cols: usize, rows: usize) -> Self {
Matrix {
common: widget::CommonBuilder::default(),
style: Style::default(),
cols: cols,
rows: rows,
}
}
pub fn cell_padding(mut self, w: Scalar, h: Scalar) -> Self {
self.style.cell_pad_w = Some(w);
self.style.cell_pad_h = Some(h);
self
}
}
impl Widget for Matrix {
type State = State;
type Style = Style;
type Event = Elements;
fn init_state(&self, _: widget::id::Generator) -> Self::State {
State { indices: Vec::new() }
}
fn style(&self) -> Self::Style {
self.style.clone()
}
fn update(self, args: widget::UpdateArgs<Self>) -> Self::Event {
let widget::UpdateArgs { id, state, rect, style, ui, .. } = args;
let Matrix { cols, rows, .. } = self;
let num_cols = state.indices.len();
if num_cols < cols {
state.update(|state| {
state.indices.extend((num_cols..cols).map(|_| Vec::with_capacity(rows)));
});
}
for col in 0..cols {
let num_rows = state.indices[col].len();
if num_rows < rows {
let mut id_gen = ui.widget_id_generator();
state.update(|state| {
let extension = (num_rows..rows).map(|_| id_gen.next());
state.indices[col].extend(extension);
});
}
}
let cell_pad_w = style.cell_pad_w(&ui.theme);
let cell_pad_h = style.cell_pad_h(&ui.theme);
let (w, h) = rect.w_h();
let elem_w = w / cols as Scalar;
let elem_h = h / rows as Scalar;
let (half_w, half_h) = (w / 2.0, h / 2.0);
let x_min = -half_w + elem_w / 2.0;
let x_max = half_w + elem_w / 2.0;
let y_min = -half_h - elem_h / 2.0;
let y_max = half_h - elem_h / 2.0;
let elements = Elements {
num_rows: rows,
num_cols: cols,
row: 0,
col: 0,
matrix_id: id,
elem_w: elem_w - cell_pad_w * 2.0,
elem_h: elem_h - cell_pad_h * 2.0,
x_min: x_min,
x_max: x_max,
y_min: y_min,
y_max: y_max,
};
elements
}
}
impl Elements {
pub fn next(&mut self, ui: &Ui) -> Option<Element> {
let Elements {
ref mut row,
ref mut col,
num_rows,
num_cols,
matrix_id,
elem_w,
elem_h,
x_min, x_max,
y_min, y_max,
} = *self;
let (r, c) = (*row, *col);
let widget_id = match ui.widget_graph().widget(matrix_id)
.and_then(|container| container.unique_widget_state::<Matrix>())
.and_then(|&graph::UniqueWidgetState { ref state, .. }| {
state.indices.get(c).and_then(|col| col.get(r).map(|&id| id))
})
{
Some(id) => id,
None => return None,
};
*row += 1;
if *row >= num_rows {
*row = 0;
*col += 1;
}
let rel_x = utils::map_range(c as Scalar, 0.0, num_cols as Scalar, x_min, x_max);
let rel_y = utils::map_range(r as Scalar, 0.0, num_rows as Scalar, y_max, y_min);
Some(Element {
widget_id: widget_id,
matrix_id: matrix_id,
col: c,
row: r,
w: elem_w,
h: elem_h,
rel_x: rel_x,
rel_y: rel_y,
})
}
}
impl Element {
pub fn set<W>(self, widget: W, ui: &mut UiCell) -> W::Event
where W: Widget,
{
use {Positionable, Sizeable};
let Element { widget_id, matrix_id, w, h, rel_x, rel_y, .. } = self;
widget
.w_h(w, h)
.x_y_relative_to(matrix_id, rel_x, rel_y)
.set(widget_id, ui)
}
}