Skip to main content

flywheel/layout/
region.rs

1//! Region and Layout: Pre-computed static layout regions.
2
3use super::rect::Rect;
4
5/// Unique identifier for a layout region.
6#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
7pub struct RegionId(pub u16);
8
9impl RegionId {
10    /// Create a new region ID.
11    pub const fn new(id: u16) -> Self {
12        Self(id)
13    }
14}
15
16/// A layout region with position and dirty tracking.
17#[derive(Clone, Debug)]
18pub struct Region {
19    /// Unique identifier.
20    pub id: RegionId,
21    /// Position and size.
22    pub rect: Rect,
23    /// Z-index for overlays (higher = on top).
24    pub z_index: u8,
25    /// Dirty generation (incremented when content changes).
26    pub dirty_generation: u64,
27}
28
29impl Region {
30    /// Create a new region.
31    pub const fn new(id: RegionId, rect: Rect) -> Self {
32        Self {
33            id,
34            rect,
35            z_index: 0,
36            dirty_generation: 0,
37        }
38    }
39
40    /// Set the z-index.
41    #[must_use]
42    pub const fn with_z_index(mut self, z: u8) -> Self {
43        self.z_index = z;
44        self
45    }
46
47    /// Mark the region as dirty.
48    pub const fn mark_dirty(&mut self) {
49        self.dirty_generation += 1;
50    }
51}
52
53/// Pre-computed layout with static regions.
54#[derive(Clone, Debug)]
55pub struct Layout {
56    /// Flat list of regions (no tree).
57    pub regions: Vec<Region>,
58    /// Terminal size.
59    pub terminal_size: (u16, u16),
60    /// Global generation counter.
61    generation: u64,
62}
63
64impl Layout {
65    /// Create a new layout for the given terminal size.
66    pub const fn new(width: u16, height: u16) -> Self {
67        Self {
68            regions: Vec::new(),
69            terminal_size: (width, height),
70            generation: 0,
71        }
72    }
73
74    /// Add a region to the layout.
75    pub fn add_region(&mut self, region: Region) {
76        self.regions.push(region);
77    }
78
79    /// Get a region by ID.
80    pub fn get(&self, id: RegionId) -> Option<&Region> {
81        self.regions.iter().find(|r| r.id == id)
82    }
83
84    /// Get a mutable region by ID.
85    pub fn get_mut(&mut self, id: RegionId) -> Option<&mut Region> {
86        self.regions.iter_mut().find(|r| r.id == id)
87    }
88
89    /// Get all dirty regions.
90    pub fn dirty_regions(&self) -> impl Iterator<Item = &Region> {
91        self.regions.iter().filter(|r| r.dirty_generation > 0)
92    }
93
94    /// Clear all dirty flags.
95    pub fn clear_dirty(&mut self) {
96        for region in &mut self.regions {
97            region.dirty_generation = 0;
98        }
99    }
100
101    /// Resize the layout and recompute regions.
102    pub const fn resize(&mut self, width: u16, height: u16) {
103        self.terminal_size = (width, height);
104        self.generation += 1;
105        // Subclasses should override to recompute region positions
106    }
107}