matrix_gui/region.rs
1//! Region and layout management module for the matrix_gui framework.
2//!
3//! This module provides utilities for defining and managing widget regions within
4//! the GUI. It includes:
5//!
6//! - [`Region`]: A struct representing a rectangular area with an associated widget ID
7//! - Layout functions for arranging widgets in grid patterns
8//! - Macros for generating region IDs and layouts at compile time
9//!
10//! # Core Concepts
11//!
12//! ## Regions
13//!
14//! A [`Region`] represents a rectangular area on the screen where a widget can be
15//! placed. Each region has an associated widget ID that uniquely identifies it.
16//!
17//! ## Layouts
18//!
19//! The module provides several layout functions for arranging regions in grids:
20//!
21//! - **Row-major**: Regions are arranged left-to-right, top-to-bottom
22//! - **Column-major**: Regions are arranged top-to-bottom, left-to-right
23//! - Both runtime and compile-time variants are available
24//!
25//! # Macros
26//!
27//! The module provides several macros for generating region layouts:
28//!
29//! - [`free_form_region!`]: Define regions with custom positions and sizes
30//! - [`region_id!`]: Generate region ID enums
31//! - [`grid_layout_row_major!`]: Create row-major grid layouts
32//! - [`grid_layout_column_major!`]: Create column-major grid layouts
33//! - [`grid_layout_row_major_with_start!`]: Create grid layouts with custom start ID
34//! - [`grid_layout_column_major_with_start!`]: Create grid layouts with custom start ID
35
36use embedded_graphics::{
37 geometry::AnchorPoint,
38 prelude::{Point, Size},
39 primitives::Rectangle,
40};
41
42use crate::{
43 prelude::{DeltaResize, LwPoint, LwRectangle, LwSize},
44 widget_state::WidgetId,
45};
46
47/// A region representing a rectangular area with an associated widget ID.
48///
49/// This struct is used to define the position and size of widgets within the GUI.
50#[derive(Copy, Clone, Eq, PartialEq, Debug, Default)]
51pub struct Region<ID> {
52 /// The widget ID associated with this region.
53 id: ID,
54 /// The rectangular area of this region.
55 area: LwRectangle<i16, u16>,
56}
57
58impl<ID: WidgetId> Region<ID> {
59 /// Creates a new region with the specified ID and dimensions.
60 ///
61 /// # Arguments
62 ///
63 /// * `id` - The widget ID for this region
64 /// * `x` - The x-coordinate of the top-left corner
65 /// * `y` - The y-coordinate of the top-left corner
66 /// * `w` - The width of the region
67 /// * `h` - The height of the region
68 pub const fn new(id: ID, x: i16, y: i16, w: u16, h: u16) -> Self {
69 let area = LwRectangle::new(LwPoint::new(x, y), LwSize::new(w, h));
70 Region { id, area }
71 }
72
73 /// Creates a new region with the specified ID and area.
74 pub const fn new_with_area(id: ID, area: LwRectangle<i16, u16>) -> Self {
75 Region { id, area }
76 }
77
78 /// Creates a new region with zero dimensions at the origin.
79 ///
80 /// # Arguments
81 ///
82 /// * `id` - The widget ID for this region
83 pub const fn zero(id: ID) -> Self {
84 Self::new(id, 0, 0, 0, 0)
85 }
86
87 /// Returns the widget ID associated with this region.
88 pub const fn id(&self) -> ID {
89 self.id
90 }
91
92 /// Returns the area of the region.
93 pub const fn area(&self) -> LwRectangle<i16, u16> {
94 self.area
95 }
96
97 /// Replace the widget ID of the region.
98 pub const fn replace_id(&self, id: ID) -> Self {
99 Self {
100 id,
101 area: self.area,
102 }
103 }
104
105 /// Returns the top-left corner of the region as a `Point`.
106 pub fn top_left(&self) -> Point {
107 self.area.top_left.into()
108 }
109
110 /// Returns the size of the region as a `Size`.
111 pub fn size(&self) -> Size {
112 self.area.size.into()
113 }
114
115 /// Returns the region as a `Rectangle`.
116 pub fn rectangle(&self) -> Rectangle {
117 self.area.into()
118 }
119
120 /// Returns the x-coordinate of the top-left corner as an `i32`.
121 pub const fn x(&self) -> i32 {
122 self.area.top_left.x as i32
123 }
124
125 /// Returns the y-coordinate of the top-left corner as an `i32`.
126 pub const fn y(&self) -> i32 {
127 self.area.top_left.y as i32
128 }
129
130 /// Returns the width of the region as a `u32`.
131 pub const fn width(&self) -> u32 {
132 self.area.size.width as u32
133 }
134
135 /// Returns the height of the region as a `u32`.
136 pub const fn height(&self) -> u32 {
137 self.area.size.height as u32
138 }
139
140 /// Returns a new region resized according to the specified delta.
141 ///
142 /// # Arguments
143 ///
144 /// * `delta` - The resize delta to use for resizing
145 pub fn delta_resize(&self, delta: DeltaResize) -> Region<ID> {
146 let area = self.area.delta_resize(delta);
147
148 Region { id: self.id, area }
149 }
150
151 /// Moves the region by the specified amount.
152 ///
153 /// # Arguments
154 ///
155 /// * `dx` - The x-direction offset
156 /// * `dy` - The y-direction offset
157 ///
158 /// # Returns
159 ///
160 /// A new `Region` instance with the moved position.
161 pub fn move_by(&self, dx: i16, dy: i16) -> Self {
162 Self {
163 id: self.id,
164 area: self.area.move_by(dx, dy),
165 }
166 }
167
168 /// Returns a new region resized according to the specified width and height.
169 ///
170 /// # Arguments
171 ///
172 /// * `width` - The new width of the region
173 /// * `height` - The new height of the region
174 /// * `anchor_point` - The anchor point to use for resizing
175 ///
176 /// # Returns
177 ///
178 /// A new `Region` instance with the resized dimensions.
179 pub fn resized(&self, width: u16, height: u16, anchor_point: AnchorPoint) -> Self {
180 Self {
181 id: self.id,
182 area: self.area.resized(width, height, anchor_point),
183 }
184 }
185}
186
187/// Creates a free-form region definition with multiple regions.
188///
189/// This macro generates:
190/// - A `RegionID` enum with variants for each region
191/// - A `REGIONID_COUNT` constant representing the number of regions
192/// - Constants for each region as uppercase identifiers
193///
194/// # Usage
195///
196/// ```rust
197/// use matrix_gui::free_form_region;
198/// use matrix_gui::prelude::*;
199///
200/// free_form_region!(
201/// RegionID,
202/// (button, 10, 10, 100, 40),
203/// (label, 10, 60, 100, 20),
204/// );
205/// ```
206///
207/// This will generate:
208/// - A `RegionID` enum with variants `Button` and `Label`
209/// - A `REGIONID_COUNT` constant set to 2
210/// - Constants `BUTTON` and `LABEL` pointing to the respective regions
211///
212/// Note: If `width` or `height` is 0, the region constant will not be generated.
213#[macro_export]
214macro_rules! free_form_region {
215 (
216 $enum_name:ident,
217 $(($name:ident $(, $x:expr, $y:expr, $width:expr, $height:expr)?)),+
218 $(,)?
219 ) => {
220 paste::paste! {
221 pub const [<$enum_name:upper _COUNT>]: usize = enum_iterator::cardinality::<[<$enum_name:camel>]>();
222
223 #[derive(Debug, PartialEq, Clone, Copy, Default, enum_iterator::Sequence)]
224 #[repr(u16)]
225 pub enum [<$enum_name:camel>] {
226 #[default]
227 $([<$name:camel>]),+
228 }
229
230 impl $crate::widget_state::WidgetId for [<$enum_name:camel>] {
231 fn id(&self) -> usize {
232 *self as usize
233 }
234 }
235 }
236
237 $(
238 matrix_gui::free_form_region_gen_region!(
239 $enum_name, $name, $($x, $y, $width, $height)?
240 );
241 )+
242 };
243}
244
245#[doc(hidden)]
246#[macro_export]
247macro_rules! free_form_region_gen_region {
248 ($enum_name:ident, $name:ident,) => {};
249 ($enum_name:ident, $name:ident, $x:expr, $y:expr, $width:expr, $height:expr) => {
250 paste::paste! {
251 pub const [<$name:upper>]: &$crate::region::Region<[<$enum_name:camel>]> = &$crate::region::Region::new(
252 [<$enum_name:camel>]::[<$name:camel>],
253 $x, $y, $width, $height
254 );
255 }
256 };
257}
258
259/// Creates a region ID enum starting from 0.
260///
261/// This macro generates an enum with the specified name and variants, implementing
262/// the `WidgetId` trait. It calls `region_id_with_start!` with a start value of 0.
263///
264/// # Usage
265///
266/// ```rust
267/// use matrix_gui::region_id;
268/// use matrix_gui::prelude::*;
269///
270/// region_id!(Button, [Ok, Cancel, Reset]);
271/// ```
272///
273/// This will generate a `Button` enum with variants `Ok`, `Cancel`, and `Reset`
274/// starting from 0.
275#[macro_export]
276macro_rules! region_id {
277 ($name:ident, [$first:ident, $($rest:ident),+ $(,)?]) => {
278 matrix_gui::region_id_with_start!($name, 0, [$first, $($rest),+]);
279 };
280}
281
282/// Creates a region ID enum with a specified starting value.
283///
284/// This macro generates an enum with the specified name and variants, implementing
285/// the `WidgetId` trait. It also generates a constant for the count of variants
286/// and an `all()` method that returns all enum variants.
287///
288/// # Usage
289///
290/// ```rust
291/// use matrix_gui::region_id_with_start;
292/// use matrix_gui::prelude::*;
293///
294/// region_id_with_start!(Button, 5, [Ok, Cancel, Reset]);
295/// ```
296///
297/// This will generate a `Button` enum with variants `Ok` (5), `Cancel` (6), and `Reset` (7).
298#[macro_export]
299macro_rules! region_id_with_start {
300 ($name:ident, $start:expr, [$first:ident, $($rest:ident),+ $(,)?]) => {
301 paste::paste! {
302 pub const [<$name:upper _COUNT>] : usize = enum_iterator::cardinality::<[<$name:camel>]>();
303 #[derive(Debug, PartialEq, Clone, Copy, Default, enum_iterator::Sequence)]
304 #[repr(u16)]
305 pub enum [<$name:camel>] {
306 #[default]
307 [<$first:camel>] = $start as u16,
308 $([<$rest:camel>]),+
309 }
310
311 impl $crate::widget_state::WidgetId for [<$name:camel>] {
312 fn id(&self) -> usize {
313 *self as usize
314 }
315 }
316
317 impl [<$name:camel>] {
318 /// Returns an array containing all enum variants.
319 pub const fn all() -> [[<$name:camel>]; [<$name:upper _COUNT>]] {
320 [
321 [<$name:camel>]::[<$first:camel>],
322 $([<$name:camel>]::[<$rest:camel>]),+
323 ]
324 }
325 }
326 }
327 };
328
329 ($name:ident, $start:expr, [$only:ident]) => {
330 paste::paste! {
331 #[repr(u16)]
332 pub enum [<$name:camel>] {
333 [<$only:camel>] = $start
334 }
335 }
336 };
337}
338
339/// Creates a column-major grid layout in place.
340///
341/// This function fills the provided region slice with regions arranged in a grid layout,
342/// ordered by columns (filling each column top to bottom before moving to the next column).
343///
344/// # Arguments
345///
346/// * `start_id` - The starting widget ID
347/// * `area` - The bounding rectangle for the entire grid
348/// * `rows` - The number of rows in the grid
349/// * `cols` - The number of columns in the grid
350/// * `gap` - The gap between adjacent regions
351/// * `regions` - The slice of regions to fill
352pub fn grid_layout_column_major_mut<ID: WidgetId>(
353 start_id: ID,
354 area: &Rectangle,
355 rows: u32,
356 cols: u32,
357 gap: u32,
358 regions: &mut [Region<ID>],
359) {
360 let col_width = (area.size.width - gap * (cols.max(1) - 1)) / cols.max(1);
361 let row_height = (area.size.height - gap * (rows.max(1) - 1)) / rows.max(1);
362 let region_size = Size::new(col_width, row_height);
363 let mut region_top_left = area.top_left;
364 let mut rows_count = 0;
365 let mut curr_id = start_id;
366
367 for region in regions {
368 region.id = curr_id;
369 region.area = Rectangle::new(region_top_left, region_size).into();
370
371 rows_count += 1;
372 if rows_count < rows {
373 region_top_left.y += row_height as i32 + gap as i32;
374 } else {
375 if region_top_left.x - area.top_left.x >= area.size.width as i32 {
376 break;
377 }
378 region_top_left.x += col_width as i32 + gap as i32;
379 region_top_left.y = area.top_left.y;
380 rows_count = 0;
381 }
382
383 if let Some(id) = curr_id.next() {
384 curr_id = id;
385 }
386 }
387}
388
389/// Creates a column-major grid layout and returns it as an array.
390///
391/// This function creates an array of regions arranged in a grid layout,
392/// ordered by columns (filling each column top to bottom before moving to the next column).
393///
394/// # Arguments
395///
396/// * `start_id` - The starting widget ID
397/// * `area` - The bounding rectangle for the entire grid
398/// * `rows` - The number of rows in the grid
399/// * `cols` - The number of columns in the grid
400/// * `gap` - The gap between adjacent regions
401///
402/// # Returns
403///
404/// An array of regions arranged in a column-major grid layout
405pub fn grid_layout_column_major<ID: WidgetId, const N: usize>(
406 start_id: ID,
407 area: &Rectangle,
408 rows: u32,
409 cols: u32,
410 gap: u32,
411) -> [Region<ID>; N] {
412 let mut regions = [Region::zero(start_id); N];
413 grid_layout_column_major_mut(start_id, area, rows, cols, gap, &mut regions);
414
415 regions
416}
417
418/// Creates a row-major grid layout in place.
419///
420/// This function fills the provided region slice with regions arranged in a grid layout,
421/// ordered by rows (filling each row left to right before moving to the next row).
422///
423/// # Arguments
424///
425/// * `start_id` - The starting widget ID
426/// * `area` - The bounding rectangle for the entire grid
427/// * `rows` - The number of rows in the grid
428/// * `cols` - The number of columns in the grid
429/// * `gap` - The gap between adjacent regions
430/// * `regions` - The slice of regions to fill
431pub fn grid_layout_row_major_mut<ID: WidgetId>(
432 start_id: ID,
433 area: &Rectangle,
434 rows: u32,
435 cols: u32,
436 gap: u32,
437 regions: &mut [Region<ID>],
438) {
439 let col_width = (area.size.width - gap * (cols.max(1) - 1)) / cols.max(1);
440 let row_height = (area.size.height - gap * (rows.max(1) - 1)) / rows.max(1);
441 let region_size = Size::new(col_width, row_height);
442 let mut region_top_left = area.top_left;
443 let mut cols_count = 0;
444 let mut curr_id = start_id;
445
446 for region in regions {
447 region.id = curr_id;
448 region.area = Rectangle::new(region_top_left, region_size).into();
449
450 cols_count += 1;
451 if cols_count < cols {
452 region_top_left.x += col_width as i32 + gap as i32;
453 } else {
454 if region_top_left.y - area.top_left.y >= area.size.height as i32 {
455 break;
456 }
457 region_top_left.y += row_height as i32 + gap as i32;
458 region_top_left.x = area.top_left.x;
459 cols_count = 0;
460 }
461
462 if let Some(id) = curr_id.next() {
463 curr_id = id;
464 }
465 }
466}
467
468/// Creates a row-major grid layout and returns it as an array.
469///
470/// This function creates an array of regions arranged in a grid layout,
471/// ordered by rows (filling each row left to right before moving to the next row).
472///
473/// # Arguments
474///
475/// * `start_id` - The starting widget ID
476/// * `area` - The bounding rectangle for the entire grid
477/// * `rows` - The number of rows in the grid
478/// * `cols` - The number of columns in the grid
479/// * `gap` - The gap between adjacent regions
480///
481/// # Returns
482///
483/// An array of regions arranged in a row-major grid layout
484pub fn grid_layout_row_major<ID: WidgetId, const N: usize>(
485 start_id: ID,
486 area: &Rectangle,
487 rows: u32,
488 cols: u32,
489 gap: u32,
490) -> [Region<ID>; N] {
491 let mut regions = [Region::zero(start_id); N];
492 grid_layout_row_major_mut(start_id, area, rows, cols, gap, &mut regions);
493
494 regions
495}
496
497/// Creates a column-major grid layout at compile time.
498///
499/// This const function creates an array of regions arranged in a grid layout,
500/// ordered by columns (filling each column top to bottom before moving to the next column).
501///
502/// # Arguments
503///
504/// * `id_list` - An array of widget IDs to use for the regions
505/// * `area` - The bounding rectangle for the entire grid
506/// * `rows` - The number of rows in the grid
507/// * `cols` - The number of columns in the grid
508/// * `gap` - The gap between adjacent regions
509///
510/// # Returns
511///
512/// An array of regions arranged in a column-major grid layout
513pub const fn const_grid_layout_column_major<ID: WidgetId, const N: usize>(
514 id_list: &[ID; N],
515 area: &Rectangle,
516 rows: u32,
517 cols: u32,
518 gap: u32,
519) -> [Region<ID>; N] {
520 assert!(N > 0, "id_list must not be empty");
521 let mut regions = [Region::zero(id_list[0]); N];
522
523 let cols_max = if cols > 0 { cols } else { 1 };
524 let rows_max = if rows > 0 { rows } else { 1 };
525 let col_width = ((area.size.width - gap * (cols_max - 1)) / cols_max) as u16;
526 let row_height = ((area.size.height - gap * (rows_max - 1)) / rows_max) as u16;
527
528 let mut x = area.top_left.x;
529 let mut y = area.top_left.y;
530 let mut rows_count = 0;
531
532 let mut i = 0;
533 while i < N {
534 regions[i] = Region::new(id_list[i], x as i16, y as i16, col_width, row_height);
535
536 rows_count += 1;
537 if rows_count < rows {
538 y += row_height as i32 + gap as i32;
539 } else {
540 if x - area.top_left.x >= area.size.width as i32 {
541 break;
542 }
543 x += col_width as i32 + gap as i32;
544 y = area.top_left.y;
545 rows_count = 0;
546 }
547
548 i += 1;
549 }
550
551 regions
552}
553
554/// Creates a row-major grid layout at compile time.
555///
556/// This const function creates an array of regions arranged in a grid layout,
557/// ordered by rows (filling each row left to right before moving to the next row).
558///
559/// # Arguments
560///
561/// * `id_list` - An array of widget IDs to use for the regions
562/// * `area` - The bounding rectangle for the entire grid
563/// * `rows` - The number of rows in the grid
564/// * `cols` - The number of columns in the grid
565/// * `gap` - The gap between adjacent regions
566///
567/// # Returns
568///
569/// An array of regions arranged in a row-major grid layout
570pub const fn const_grid_layout_row_major<ID: WidgetId, const N: usize>(
571 id_list: &[ID; N],
572 area: &Rectangle,
573 rows: u32,
574 cols: u32,
575 gap: u32,
576) -> [Region<ID>; N] {
577 assert!(N > 0, "id_list must not be empty");
578 let mut regions = [Region::zero(id_list[0]); N];
579
580 let cols_max = if cols > 0 { cols } else { 1 };
581 let rows_max = if rows > 0 { rows } else { 1 };
582 let col_width = ((area.size.width - gap * (cols_max - 1)) / cols_max) as u16;
583 let row_height = ((area.size.height - gap * (rows_max - 1)) / rows_max) as u16;
584
585 let mut x = area.top_left.x;
586 let mut y = area.top_left.y;
587 let mut cols_count = 0;
588
589 let mut i = 0;
590 while i < N {
591 regions[i] = Region::new(id_list[i], x as i16, y as i16, col_width, row_height);
592
593 cols_count += 1;
594 if cols_count < cols {
595 x += col_width as i32 + gap as i32;
596 } else {
597 if y - area.top_left.y >= area.size.height as i32 {
598 break;
599 }
600 y += row_height as i32 + gap as i32;
601 x = area.top_left.x;
602 cols_count = 0;
603 }
604
605 i += 1;
606 }
607
608 regions
609}
610
611/// Creates constant variables for a grid layout.
612///
613/// This macro generates:
614/// - A constant for the layout area
615/// - A constant for the grid layout array
616/// - Constants for each region in the grid
617///
618/// # Arguments
619///
620/// * `name` - The base name for the generated constants and enum
621/// * `base_idx` - The base index to use for region constants
622/// * `(x, y, width, height)` - The area for the grid layout
623/// * `(rows, cols, gap)` - The grid dimensions and gap
624/// * `[first, rest...]` - The region names
625/// * `layout_fn` - The layout function to use (column or row)
626/// * `suffix` - The suffix to use for the layout array constant
627#[macro_export]
628macro_rules! grid_layout_const_var {
629 ($name:ident, $base_idx:expr, ($x:expr, $y:expr, $width:expr, $height:expr),
630 ($rows:expr, $cols:expr, $gap:expr), [$first:ident, $($rest:ident),+ $(,)?], $layout_fn:ident, $suffix:ident) => {
631 paste::paste! {
632 pub const [<$name:upper _AREA>]: Rectangle =
633 Rectangle::new(Point::new($x, $y), Size::new($width, $height));
634 pub const [<$name:upper _ $suffix>]: [Region<[<$name:camel>]>; [<$name:upper _COUNT>] ] =
635 matrix_gui::region::[<const_grid_layout_ $layout_fn _major>](&[<$name:camel>]::all(),
636 &[<$name:upper _AREA>], $rows, $cols, $gap);
637 }
638 paste::paste! {
639 pub const [<$first:upper>]: &Region<[<$name:camel>]> =
640 &[<$name:upper _ $suffix>][[<$name:camel>]::[<$first:camel>] as usize - $base_idx];
641 }
642 $(paste::paste! {
643 pub const [<$rest:upper>]: &Region<[<$name:camel>]> =
644 &[<$name:upper _ $suffix>][[<$name:camel>]::[<$rest:camel>] as usize - $base_idx];
645 })+
646 }
647}
648
649/// Creates a column-major grid layout with region IDs starting from 0.
650///
651/// This macro generates:
652/// - A region ID enum with variants for each region
653/// - Constants for each region in the grid
654/// - A constant for the grid layout array
655///
656/// # Usage
657///
658/// ```rust
659/// use matrix_gui::grid_layout_column_major;
660/// use matrix_gui::prelude::*;
661///
662/// grid_layout_column_major!(
663/// Button, (10, 10, 200, 100),
664/// (2, 2, 10), [Ok, Cancel, Reset, Exit]
665/// );
666/// ```
667#[macro_export]
668macro_rules! grid_layout_column_major {
669 ($name:ident, ($x:expr, $y:expr, $width:expr, $height:expr),
670 ($rows:expr, $cols:expr, $gap:expr), [$first:ident, $($rest:ident),+ $(,)?]) => {
671 matrix_gui::region_id!($name, [$first, $($rest),+]);
672 matrix_gui::grid_layout_const_var!($name, 0, ($x, $y, $width, $height),
673 ($rows, $cols, $gap), [$first, $($rest),+], column, GLCM);
674 }
675}
676
677/// Creates a row-major grid layout with region IDs starting from 0.
678///
679/// This macro generates:
680/// - A region ID enum with variants for each region
681/// - Constants for each region in the grid
682/// - A constant for the grid layout array
683///
684/// # Usage
685///
686/// ```rust
687/// use matrix_gui::grid_layout_row_major;
688/// use matrix_gui::prelude::*;
689///
690/// grid_layout_row_major!(
691/// Button, (10, 10, 200, 100),
692/// (2, 2, 10), [Ok, Cancel, Reset, Exit]
693/// );
694/// ```
695#[macro_export]
696macro_rules! grid_layout_row_major {
697 ($name:ident, ($x:expr, $y:expr, $width:expr, $height:expr),
698 ($rows:expr, $cols:expr, $gap:expr), [$first:ident, $($rest:ident),+ $(,)?]) => {
699 matrix_gui::region_id!($name, [$first, $($rest),+]);
700 matrix_gui::grid_layout_const_var!($name, 0, ($x, $y, $width, $height),
701 ($rows, $cols, $gap), [$first, $($rest),+], row, GLRM);
702 }
703}
704
705/// Creates a column-major grid layout with region IDs starting from a specified value.
706///
707/// This macro generates:
708/// - A region ID enum with variants for each region, starting from the specified value
709/// - Constants for each region in the grid
710/// - A constant for the grid layout array
711///
712/// # Usage
713///
714/// ```rust
715/// use matrix_gui::grid_layout_column_major_with_start;
716/// use matrix_gui::prelude::*;
717///
718/// grid_layout_column_major_with_start!(
719/// Button, 5, (10, 10, 200, 100),
720/// (2, 2, 10), [Ok, Cancel, Reset, Exit]
721/// );
722/// ```
723#[macro_export]
724macro_rules! grid_layout_column_major_with_start {
725 ($name:ident, $start:expr, ($x:expr, $y:expr, $width:expr, $height:expr),
726 ($rows:expr, $cols:expr, $gap:expr), [$first:ident, $($rest:ident),+ $(,)?]) => {
727 matrix_gui::region_id_with_start!($name, $start, [$first, $($rest),+]);
728 matrix_gui::grid_layout_const_var!($name, $start, ($x, $y, $width, $height),
729 ($rows, $cols, $gap), [$first, $($rest),+], column, GLCM);
730 }
731}
732
733/// Creates a row-major grid layout with region IDs starting from a specified value.
734///
735/// This macro generates:
736/// - A region ID enum with variants for each region, starting from the specified value
737/// - Constants for each region in the grid
738/// - A constant for the grid layout array
739///
740/// # Usage
741///
742/// ```rust
743/// use matrix_gui::grid_layout_row_major_with_start;
744/// use matrix_gui::prelude::*;
745///
746/// grid_layout_row_major_with_start!(
747/// Button, 5, (10, 10, 200, 100),
748/// (2, 2, 10), [Ok, Cancel, Reset, Exit]
749/// );
750/// ```
751#[macro_export]
752macro_rules! grid_layout_row_major_with_start {
753 ($name:ident, $start:expr, ($x:expr, $y:expr, $width:expr, $height:expr),
754 ($rows:expr, $cols:expr, $gap:expr), [$first:ident, $($rest:ident),+ $(,)?]) => {
755 matrix_gui::region_id_with_start!($name, $start, [$first, $($rest),+]);
756 matrix_gui::grid_layout_const_var!($name, $start, ($x, $y, $width, $height),
757 ($rows, $cols, $gap), [$first, $($rest),+], row, GLRM);
758 }
759}