Skip to main content

rpi_extensions/
panel.rs

1//! Internal text component layout used by the TUI renderer.
2use serde::{Deserialize, Serialize};
3
4#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
5#[serde(rename_all = "kebab-case")]
6pub enum PanelAnchor {
7    TopLeft,
8    TopCenter,
9    #[default]
10    TopRight,
11    LeftCenter,
12    Center,
13    RightCenter,
14    BottomLeft,
15    BottomCenter,
16    BottomRight,
17}
18
19#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
20#[serde(rename_all = "kebab-case")]
21pub enum PanelLayout {
22    #[default]
23    Overlay,
24    Sidebar,
25    Footer,
26}
27
28#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
29#[serde(rename_all = "camelCase")]
30pub struct ExtensionPanel {
31    pub version: u32,
32    #[serde(default)]
33    pub order: i32,
34    #[serde(default)]
35    pub layout: PanelLayout,
36    #[serde(default)]
37    pub anchor: PanelAnchor,
38    #[serde(default)]
39    pub offset_x: i32,
40    #[serde(default)]
41    pub offset_y: i32,
42    #[serde(default = "default_width")]
43    pub width: usize,
44    #[serde(default = "default_height")]
45    pub max_height: usize,
46    #[serde(default)]
47    pub min_screen_width: usize,
48    #[serde(default = "default_border")]
49    pub border: bool,
50    #[serde(default)]
51    pub title: String,
52    pub lines: Vec<String>,
53}
54fn default_width() -> usize {
55    44
56}
57fn default_height() -> usize {
58    16
59}
60fn default_border() -> bool {
61    true
62}
63impl ExtensionPanel {
64    pub fn validate(&self) -> Result<(), String> {
65        if self.version != 1 {
66            return Err("unsupported panel version (expected 1)".into());
67        }
68        if !(4..=240).contains(&self.width)
69            || !(1..=80).contains(&self.max_height)
70            || self.min_screen_width > 1000
71            || self.offset_x.unsigned_abs() > 1000
72            || self.offset_y.unsigned_abs() > 1000
73        {
74            return Err("panel geometry is out of range".into());
75        }
76        if self.lines.len() > 64
77            || self.lines.iter().any(|line| line.len() > 2048)
78            || self.title.len() > 256
79        {
80            return Err("panel content exceeds limits".into());
81        }
82        Ok(())
83    }
84    /// Content updates retain the same overlay and its stacking position.
85    pub fn same_geometry(&self, other: &Self) -> bool {
86        self.layout == other.layout
87            && self.anchor == other.anchor
88            && self.offset_x == other.offset_x
89            && self.offset_y == other.offset_y
90            && self.width == other.width
91            && self.max_height == other.max_height
92    }
93}