fltk_float/grid/
builder.rs

1use std::borrow::Borrow;
2
3use fltk::group::Group;
4use fltk::prelude::*;
5
6use crate::{Padding, WrapperFactory};
7
8use super::{Cell, CellAlign, Grid, GridProperties, StripeCell};
9
10use self::group::StripeGroupBuilder;
11
12mod cell;
13mod group;
14mod stripe;
15
16pub use cell::CellBuilder;
17pub use stripe::StripeBuilder;
18
19pub struct GridBuilder<G: GroupExt + Clone = Group, F: Borrow<WrapperFactory> = WrapperFactory> {
20    props: GridProperties<G>,
21    factory: F,
22    default_cell_padding: Padding,
23    default_row_align: Vec<CellAlign>,
24    default_col_align: Vec<CellAlign>,
25    next_row: usize,
26    next_col: usize,
27}
28
29#[derive(Clone, Copy)]
30pub struct StripeGroupRef {
31    kind: StripeKind,
32    idx: usize,
33}
34
35#[derive(Clone, Copy)]
36enum StripeKind {
37    Row,
38    Column,
39}
40
41impl<G: GroupExt + Clone> GridBuilder<G> {
42    pub fn new(group: G) -> Self {
43        Self::with_factory(group, WrapperFactory::new())
44    }
45}
46
47impl<G: GroupExt + Clone, F: Borrow<WrapperFactory>> GridBuilder<G, F> {
48    pub fn with_factory(group: G, factory: F) -> Self {
49        Self {
50            props: GridProperties {
51                group,
52                padding: Default::default(),
53                row_spacing: 0,
54                col_spacing: 0,
55                cells: Vec::new(),
56                spans: Vec::new(),
57                groups: Vec::new(),
58                rows: Vec::new(),
59                cols: Vec::new(),
60            },
61            factory,
62            default_cell_padding: Default::default(),
63            default_row_align: Vec::new(),
64            default_col_align: Vec::new(),
65            next_row: 0,
66            next_col: 0,
67        }
68    }
69
70    pub fn with_row_spacing(mut self, spacing: i32) -> Self {
71        self.props.row_spacing = std::cmp::max(0, spacing);
72        self
73    }
74
75    pub fn with_col_spacing(mut self, spacing: i32) -> Self {
76        self.props.col_spacing = std::cmp::max(0, spacing);
77        self
78    }
79
80    pub fn with_left_padding(mut self, padding: i32) -> Self {
81        self.props.padding.left = padding;
82        self
83    }
84
85    pub fn with_top_padding(mut self, padding: i32) -> Self {
86        self.props.padding.top = padding;
87        self
88    }
89
90    pub fn with_right_padding(mut self, padding: i32) -> Self {
91        self.props.padding.right = padding;
92        self
93    }
94
95    pub fn with_bottom_padding(mut self, padding: i32) -> Self {
96        self.props.padding.bottom = padding;
97        self
98    }
99
100    pub fn with_padding(mut self, left: i32, top: i32, right: i32, bottom: i32) -> Self {
101        self.props.padding = Padding {
102            left,
103            top,
104            right,
105            bottom,
106        };
107        self
108    }
109
110    pub fn with_default_cell_padding(
111        mut self,
112        left: i32,
113        top: i32,
114        right: i32,
115        bottom: i32,
116    ) -> Self {
117        self.default_cell_padding = Padding {
118            left,
119            top,
120            right,
121            bottom,
122        };
123        self
124    }
125
126    pub fn num_rows(&self) -> usize {
127        self.props.rows.len()
128    }
129
130    pub fn num_cols(&self) -> usize {
131        self.props.cols.len()
132    }
133
134    pub fn row(&mut self) -> StripeBuilder<G, F> {
135        StripeBuilder::new(self, StripeKind::Row, None)
136    }
137
138    pub fn col(&mut self) -> StripeBuilder<G, F> {
139        StripeBuilder::new(self, StripeKind::Column, None)
140    }
141
142    pub fn row_group(&mut self) -> StripeGroupBuilder<G, F> {
143        StripeGroupBuilder::new(self, StripeKind::Row)
144    }
145
146    pub fn col_group(&mut self) -> StripeGroupBuilder<G, F> {
147        StripeGroupBuilder::new(self, StripeKind::Column)
148    }
149
150    pub fn extend_group(&mut self, group: StripeGroupRef) -> StripeBuilder<G, F> {
151        StripeBuilder::new(self, group.kind, Some(group.idx))
152    }
153
154    pub fn cell(&mut self) -> Option<CellBuilder<G, F>> {
155        let (row, col) = self.next_free_cell()?;
156        Some(CellBuilder::new(self, row, col, 1, 1))
157    }
158
159    pub fn cell_at(&mut self, row: usize, col: usize) -> Option<CellBuilder<G, F>> {
160        if (row >= self.props.rows.len()) && (col >= self.props.cols.len()) {
161            return None;
162        }
163        match self.props.rows[row].cells[col] {
164            StripeCell::Free | StripeCell::Skipped => Some(CellBuilder::new(self, row, col, 1, 1)),
165            _ => None,
166        }
167    }
168
169    pub fn span(&mut self, row_span: usize, col_span: usize) -> Option<CellBuilder<G, F>> {
170        if (row_span == 0) || (col_span == 0) {
171            return None;
172        }
173
174        let (row, col) = self.next_free_cell()?;
175        if self.is_span_available(row, col, row_span, col_span, false) {
176            Some(CellBuilder::new(self, row, col, row_span, col_span))
177        } else {
178            None
179        }
180    }
181
182    pub fn span_at(
183        &mut self,
184        row: usize,
185        col: usize,
186        row_span: usize,
187        col_span: usize,
188    ) -> Option<CellBuilder<G, F>> {
189        if (row_span == 0) || (col_span == 0) {
190            return None;
191        }
192
193        if (row >= self.props.rows.len()) && (col >= self.props.cols.len()) {
194            return None;
195        }
196        if self.is_span_available(row, col, row_span, col_span, true) {
197            Some(CellBuilder::new(self, row, col, row_span, col_span))
198        } else {
199            None
200        }
201    }
202
203    pub fn end(self) -> Grid<G> {
204        self.props.group.end();
205        Grid::new(self.props)
206    }
207
208    fn next_free_cell(&mut self) -> Option<(usize, usize)> {
209        let mut row = self.next_row;
210        let mut col = self.next_col;
211
212        loop {
213            if col >= self.props.cols.len() {
214                col = 0;
215                row += 1;
216            }
217            if row >= self.props.rows.len() {
218                return None;
219            }
220            if let StripeCell::Free = self.props.rows[row].cells[col] {
221                break;
222            }
223            col += 1;
224        }
225
226        self.next_row = row;
227        self.next_col = col;
228
229        Some((row, col))
230    }
231
232    fn is_span_available(
233        &self,
234        row: usize,
235        col: usize,
236        row_span: usize,
237        col_span: usize,
238        allow_skipped: bool,
239    ) -> bool {
240        let top = row;
241        let bottom = top + row_span;
242        let left = col;
243        let right = left + col_span;
244
245        if (bottom > self.props.rows.len()) || (right > self.props.cols.len()) {
246            return false;
247        }
248
249        for cell_row in top..bottom {
250            for cell_col in left..right {
251                let cell_available = match self.props.rows[cell_row].cells[cell_col] {
252                    StripeCell::Free => true,
253                    StripeCell::Skipped if allow_skipped => true,
254                    _ => false,
255                };
256                if !cell_available {
257                    return false;
258                }
259            }
260        }
261
262        true
263    }
264
265    fn add_cell(&mut self, cell: Cell) {
266        if (cell.props.row_span > 1) || (cell.props.col_span > 1) {
267            return self.add_span(cell);
268        }
269
270        let row = cell.props.row;
271        let col = cell.props.col;
272
273        let cell_idx = self.props.cells.len();
274        self.props.cells.push(cell);
275
276        self.props.rows[row].cells[col] = StripeCell::Cell(cell_idx);
277        self.props.cols[col].cells[row] = StripeCell::Cell(cell_idx);
278    }
279
280    fn add_span(&mut self, span: Cell) {
281        let top = span.props.row;
282        let bottom = top + span.props.row_span;
283        let left = span.props.col;
284        let right = left + span.props.col_span;
285
286        self.props.spans.push(span);
287
288        for row in top..bottom {
289            for col in left..right {
290                self.props.rows[row].cells[col] = StripeCell::Span;
291                self.props.cols[col].cells[row] = StripeCell::Span;
292            }
293        }
294    }
295}