ferroui_controls/primitives/
uniform_grid.rs1use crate::{ControlImpl, Panel, PanelImpl};
2use ferroui_base::input::InputElementImpl;
3use ferroui_base::interactivity::InteractiveImpl;
4use ferroui_base::layout::{LayoutHelper, Layoutable, LayoutableImpl};
5use ferroui_base::utilities::math_utilities::max;
6use ferroui_base::{
7 ferro_class, ferro_impl_classes, ferro_property, instantiate, FerroObjectImpl, FerroProperty,
8 Rect, Ref, Size, StyledElementImpl, StyledProperty, VisualImpl,
9};
10use std::cell::Cell;
11
12#[repr(C)]
15pub struct UniformGrid {
16 base: Panel,
17 rows: Cell<i32>,
18 columns: Cell<i32>,
19}
20
21ferro_class!(UniformGrid: Panel);
22ferroui_base::ferro_class_info!(UniformGrid { new: UniformGrid::new });
23ferro_impl_classes!(
24 UniformGrid: StyledElementImpl,
25 VisualImpl,
26 InteractiveImpl,
27 InputElementImpl,
28 ControlImpl,
29 PanelImpl
30);
31
32ferroui_base::ferro_impl_classes!(UniformGrid: FerroObjectImpl);
33
34impl LayoutableImpl for UniformGrid {
35 fn measure_override(this: &Self, available_size: Size) -> Size {
36 this.update_rows_and_columns();
37
38 let rows = this.rows.get() as f64;
39 let columns = this.columns.get() as f64;
40 let mut max_width = 0.0;
41 let mut max_height = 0.0;
42
43 let child_available_size = Size::new(
44 (available_size.width - (columns - 1.0) * this.column_spacing()) / columns,
45 (available_size.height - (rows - 1.0) * this.row_spacing()) / rows,
46 );
47
48 for child in this.children().snapshot().iter() {
49 child.measure(child_available_size);
50
51 if child.desired_size().width > max_width {
52 max_width = child.desired_size().width;
53 }
54
55 if child.desired_size().height > max_height {
56 max_height = child.desired_size().height;
57 }
58 }
59
60 if this.use_layout_rounding() {
61 let scale = LayoutHelper::get_layout_scale(this);
62 max_width = LayoutHelper::round_layout_value(max_width, scale);
63 max_height = LayoutHelper::round_layout_value(max_height, scale);
64 }
65
66 let total_width = max_width * columns + this.column_spacing() * (columns - 1.0);
67 let total_height = max_height * rows + this.row_spacing() * (rows - 1.0);
68
69 Size::new(max(total_width, 0.0), max(total_height, 0.0))
70 }
71
72 fn arrange_override(this: &Self, final_size: Size) -> Size {
73 let mut x = this.first_column();
74 let mut y = 0;
75
76 let column_spacing = this.column_spacing();
77 let row_spacing = this.row_spacing();
78 let rows = this.rows.get();
79 let columns = this.columns.get();
80
81 let mut width = max((final_size.width - (columns - 1) as f64 * column_spacing) / columns as f64, 0.0);
82 let mut height = max((final_size.height - (rows - 1) as f64 * row_spacing) / rows as f64, 0.0);
83
84 if this.use_layout_rounding() {
87 let scale = LayoutHelper::get_layout_scale(this);
88 width = LayoutHelper::round_layout_value(width, scale);
89 height = LayoutHelper::round_layout_value(height, scale);
90 }
91
92 for child in this.children().snapshot().iter() {
93 if !child.is_visible() {
94 continue;
95 }
96
97 let rect = Rect::new(x as f64 * (width + column_spacing), y as f64 * (height + row_spacing), width, height);
98
99 child.arrange(rect);
100
101 x += 1;
102
103 if x >= columns {
104 x = 0;
105 y += 1;
106 }
107 }
108
109 final_size
110 }
111}
112
113ferroui_base::ferro_properties! { impl UniformGrid {
114 ferro_property!(
115 pub fn rows_property() -> StyledProperty<i32> {
117 FerroProperty::register::<UniformGrid, _>("Rows", 0)
118 }
119 );
120
121 ferro_property!(
122 pub fn columns_property() -> StyledProperty<i32> {
124 FerroProperty::register::<UniformGrid, _>("Columns", 0)
125 }
126 );
127
128 ferro_property!(
129 pub fn first_column_property() -> StyledProperty<i32> {
131 FerroProperty::register::<UniformGrid, _>("FirstColumn", 0)
132 }
133 );
134
135 ferro_property!(
136 pub fn row_spacing_property() -> StyledProperty<f64> {
138 FerroProperty::register::<UniformGrid, _>("RowSpacing", 0.0)
139 }
140 );
141
142 ferro_property!(
143 pub fn column_spacing_property() -> StyledProperty<f64> {
145 FerroProperty::register::<UniformGrid, _>("ColumnSpacing", 0.0)
146 }
147 );
148} }
149
150impl UniformGrid {
151 fn static_constructor() {
152 Layoutable::affects_measure::<UniformGrid>(&[
153 Self::rows_property().as_property(),
154 Self::columns_property().as_property(),
155 Self::first_column_property().as_property(),
156 ]);
157 }
158
159 pub fn construct() -> Self {
161 Self { base: Panel::construct(), rows: Cell::new(0), columns: Cell::new(0) }
162 }
163
164 pub fn new() -> Ref<Self> {
165 instantiate(Self::construct())
166 }
167
168 pub fn rows(&self) -> i32 {
171 self.get_value(Self::rows_property())
172 }
173
174 pub fn set_rows(&self, value: i32) {
175 self.set_value(Self::rows_property(), value)
176 }
177
178 pub fn columns(&self) -> i32 {
181 self.get_value(Self::columns_property())
182 }
183
184 pub fn set_columns(&self, value: i32) {
185 self.set_value(Self::columns_property(), value)
186 }
187
188 pub fn first_column(&self) -> i32 {
191 self.get_value(Self::first_column_property())
192 }
193
194 pub fn set_first_column(&self, value: i32) {
195 self.set_value(Self::first_column_property(), value)
196 }
197
198 pub fn row_spacing(&self) -> f64 {
200 self.get_value(Self::row_spacing_property())
201 }
202
203 pub fn set_row_spacing(&self, value: f64) {
204 self.set_value(Self::row_spacing_property(), value)
205 }
206
207 pub fn column_spacing(&self) -> f64 {
209 self.get_value(Self::column_spacing_property())
210 }
211
212 pub fn set_column_spacing(&self, value: f64) {
213 self.set_value(Self::column_spacing_property(), value)
214 }
215
216 fn update_rows_and_columns(&self) {
217 let mut rows = self.rows();
218 let mut columns = self.columns();
219
220 if self.first_column() >= columns {
221 self.set_current_value(Self::first_column_property(), 0);
222 }
223
224 let mut item_count = self.first_column();
225
226 for child in self.children().snapshot().iter() {
227 if child.is_visible() {
228 item_count += 1;
229 }
230 }
231
232 if rows == 0 {
233 if columns == 0 {
234 rows = (item_count as f64).sqrt().ceil() as i32;
235 columns = rows;
236 } else {
237 rows = item_count / columns;
238
239 if item_count % columns != 0 {
240 rows += 1;
241 }
242 }
243 } else if columns == 0 {
244 columns = item_count / rows;
245
246 if item_count % rows != 0 {
247 columns += 1;
248 }
249 }
250
251 self.rows.set(rows);
252 self.columns.set(columns);
253 }
254}