Skip to main content

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}