conrod_core/widget/
matrix.rs

1//! A helper widget for laying out child widgets in the form of a grid.
2
3use graph;
4use utils;
5use widget;
6use {Scalar, Ui, UiCell, Widget};
7
8/// The number of the widget.
9pub type WidgetNum = usize;
10/// A column index.
11pub type ColNum = usize;
12/// A row index.
13pub type RowNum = usize;
14/// The width of an element.
15pub type Width = Scalar;
16/// The height of an element.
17pub type Height = Scalar;
18/// The position of an element along the *x* axis.
19pub type PosX = Scalar;
20/// The position of an element along the *y* axis.
21pub type PosY = Scalar;
22
23/// Draw a matrix of any rectangular widget type, where the matrix will provide a function with
24/// the widget number, it's `rows` and `cols` position, the width and height for the widget and
25/// the location at which the widget should be drawn.
26#[derive(Clone, WidgetCommon_)]
27#[allow(missing_copy_implementations)]
28pub struct Matrix {
29    #[conrod(common_builder)]
30    common: widget::CommonBuilder,
31    style: Style,
32    cols: usize,
33    rows: usize,
34}
35
36/// The state of the Matrix, to be cached within the `Ui`'s widget `Graph`.
37pub struct State {
38    /// A `widget::Id` for every Widget in the Matrix.
39    /// This matrix is column major, meaning the outer-most Vec represents each column, and each
40    /// inner Vec represents a row.
41    indices: Vec<Vec<widget::Id>>,
42}
43
44/// Unique styling for the `Matrix`.
45#[derive(Copy, Clone, Debug, Default, PartialEq, WidgetStyle_)]
46pub struct Style {
47    /// The width of the padding for each matrix element's "cell".
48    #[conrod(default = "0.0")]
49    pub cell_pad_w: Option<Scalar>,
50    /// The height of the padding for each matrix element's "cell".
51    #[conrod(default = "0.0")]
52    pub cell_pad_h: Option<Scalar>,
53}
54
55/// The event type yielded by the `Matrix`.
56///
57/// This can be used to iterate over each element in the `Matrix`.
58#[derive(Debug)]
59#[allow(missing_copy_implementations)]
60pub struct Elements {
61    num_rows: usize,
62    num_cols: usize,
63    row: usize,
64    col: usize,
65    matrix_id: widget::Id,
66    elem_w: Scalar,
67    elem_h: Scalar,
68    x_min: Scalar,
69    x_max: Scalar,
70    y_min: Scalar,
71    y_max: Scalar,
72}
73
74/// Data necessary for instantiating a widget for a single `Matrix` element.
75#[derive(Copy, Clone, Debug)]
76pub struct Element {
77    /// The id generated for the widget.
78    pub widget_id: widget::Id,
79    /// The row number for the `Element`.
80    pub row: usize,
81    /// The column number for the `Element`.
82    pub col: usize,
83    /// The width of the element.
84    pub w: Scalar,
85    /// The height of the element.
86    pub h: Scalar,
87    /// The *x* position of the element relative to the centre of the `Matrix`.
88    pub rel_x: Scalar,
89    /// The *y* position of the element relative to the centre of the `Matrix`.
90    pub rel_y: Scalar,
91    /// The id of the `Matrix`, used for positioning.
92    matrix_id: widget::Id,
93}
94
95impl Matrix {
96    /// Create a widget matrix context.
97    pub fn new(cols: usize, rows: usize) -> Self {
98        Matrix {
99            common: widget::CommonBuilder::default(),
100            style: Style::default(),
101            cols: cols,
102            rows: rows,
103        }
104    }
105
106    /// A builder method for adding padding to the cell.
107    pub fn cell_padding(mut self, w: Scalar, h: Scalar) -> Self {
108        self.style.cell_pad_w = Some(w);
109        self.style.cell_pad_h = Some(h);
110        self
111    }
112}
113
114impl Widget for Matrix {
115    type State = State;
116    type Style = Style;
117    type Event = Elements;
118
119    fn init_state(&self, _: widget::id::Generator) -> Self::State {
120        State {
121            indices: Vec::new(),
122        }
123    }
124
125    fn style(&self) -> Self::Style {
126        self.style.clone()
127    }
128
129    /// Update the state of the Matrix.
130    fn update(self, args: widget::UpdateArgs<Self>) -> Self::Event {
131        let widget::UpdateArgs {
132            id,
133            state,
134            rect,
135            style,
136            ui,
137            ..
138        } = args;
139        let Matrix { cols, rows, .. } = self;
140
141        // First, check that we have the correct number of columns.
142        let num_cols = state.indices.len();
143        if num_cols < cols {
144            state.update(|state| {
145                state
146                    .indices
147                    .extend((num_cols..cols).map(|_| Vec::with_capacity(rows)));
148            });
149        }
150
151        // Now check that we have the correct amount of rows in each column.
152        for col in 0..cols {
153            let num_rows = state.indices[col].len();
154            if num_rows < rows {
155                let mut id_gen = ui.widget_id_generator();
156                state.update(|state| {
157                    let extension = (num_rows..rows).map(|_| id_gen.next());
158                    state.indices[col].extend(extension);
159                });
160            }
161        }
162
163        let cell_pad_w = style.cell_pad_w(&ui.theme);
164        let cell_pad_h = style.cell_pad_h(&ui.theme);
165        let (w, h) = rect.w_h();
166        let elem_w = w / cols as Scalar;
167        let elem_h = h / rows as Scalar;
168        let (half_w, half_h) = (w / 2.0, h / 2.0);
169        let x_min = -half_w + elem_w / 2.0;
170        let x_max = half_w + elem_w / 2.0;
171        let y_min = -half_h - elem_h / 2.0;
172        let y_max = half_h - elem_h / 2.0;
173
174        let elements = Elements {
175            num_rows: rows,
176            num_cols: cols,
177            row: 0,
178            col: 0,
179            matrix_id: id,
180            elem_w: elem_w - cell_pad_w * 2.0,
181            elem_h: elem_h - cell_pad_h * 2.0,
182            x_min: x_min,
183            x_max: x_max,
184            y_min: y_min,
185            y_max: y_max,
186        };
187
188        elements
189    }
190}
191
192impl Elements {
193    /// Yield the next `Element`.
194    pub fn next(&mut self, ui: &Ui) -> Option<Element> {
195        let Elements {
196            ref mut row,
197            ref mut col,
198            num_rows,
199            num_cols,
200            matrix_id,
201            elem_w,
202            elem_h,
203            x_min,
204            x_max,
205            y_min,
206            y_max,
207        } = *self;
208
209        let (r, c) = (*row, *col);
210
211        // Retrieve the `widget::Id` that was generated for the next `Element`.
212        let widget_id = match ui
213            .widget_graph()
214            .widget(matrix_id)
215            .and_then(|container| container.unique_widget_state::<Matrix>())
216            .and_then(|&graph::UniqueWidgetState { ref state, .. }| {
217                state
218                    .indices
219                    .get(c)
220                    .and_then(|col| col.get(r).map(|&id| id))
221            }) {
222            Some(id) => id,
223            None => return None,
224        };
225
226        // Increment the elem indices.
227        *row += 1;
228        if *row >= num_rows {
229            *row = 0;
230            *col += 1;
231        }
232
233        let rel_x = utils::map_range(c as Scalar, 0.0, num_cols as Scalar, x_min, x_max);
234        let rel_y = utils::map_range(r as Scalar, 0.0, num_rows as Scalar, y_max, y_min);
235
236        Some(Element {
237            widget_id: widget_id,
238            matrix_id: matrix_id,
239            col: c,
240            row: r,
241            w: elem_w,
242            h: elem_h,
243            rel_x: rel_x,
244            rel_y: rel_y,
245        })
246    }
247}
248
249impl Element {
250    /// Sets the given widget as the widget to use for the item.
251    ///
252    /// Sets the:
253    /// - position of the widget.
254    /// - dimensions of the widget.
255    /// - parent of the widget.
256    /// - and finally sets the widget within the `Ui`.
257    pub fn set<W>(self, widget: W, ui: &mut UiCell) -> W::Event
258    where
259        W: Widget,
260    {
261        use {Positionable, Sizeable};
262        let Element {
263            widget_id,
264            matrix_id,
265            w,
266            h,
267            rel_x,
268            rel_y,
269            ..
270        } = self;
271        widget
272            .w_h(w, h)
273            .x_y_relative_to(matrix_id, rel_x, rel_y)
274            .set(widget_id, ui)
275    }
276}