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