Skip to main content

slt/layout/
command.rs

1use super::*;
2
3/// Main axis direction for a container's children.
4#[non_exhaustive]
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub enum Direction {
7    /// Lay out children horizontally (left to right).
8    Row,
9    /// Lay out children vertically (top to bottom).
10    Column,
11}
12
13/// Arguments for [`Command::BeginContainer`].
14///
15/// Boxed inside the variant so that the surrounding `Command` enum stays
16/// small — a frame may contain hundreds of commands, and most variants
17/// (for example `EndContainer`) carry no payload.
18#[derive(Debug, Clone)]
19pub(crate) struct BeginContainerArgs {
20    pub direction: Direction,
21    pub gap: u32,
22    pub align: Align,
23    pub align_self: Option<Align>,
24    pub justify: Justify,
25    pub border: Option<Border>,
26    pub border_sides: BorderSides,
27    pub border_style: Style,
28    pub bg_color: Option<Color>,
29    pub padding: Padding,
30    pub margin: Margin,
31    pub constraints: Constraints,
32    pub title: Option<(String, Style)>,
33    pub grow: u16,
34    pub group_name: Option<String>,
35}
36
37/// Arguments for [`Command::BeginScrollable`].
38///
39/// Boxed for the same reason as [`BeginContainerArgs`] — keeps the
40/// `Command` enum from being dragged up to the width of this payload.
41#[derive(Debug, Clone)]
42pub(crate) struct BeginScrollableArgs {
43    pub grow: u16,
44    pub border: Option<Border>,
45    pub border_sides: BorderSides,
46    pub border_style: Style,
47    pub padding: Padding,
48    pub margin: Margin,
49    pub constraints: Constraints,
50    pub title: Option<(String, Style)>,
51    pub scroll_offset: u32,
52}
53
54#[derive(Debug, Clone)]
55pub(crate) enum Command {
56    Text {
57        content: String,
58        cursor_offset: Option<usize>,
59        style: Style,
60        grow: u16,
61        align: Align,
62        wrap: bool,
63        truncate: bool,
64        margin: Margin,
65        constraints: Constraints,
66    },
67    BeginContainer(Box<BeginContainerArgs>),
68    BeginScrollable(Box<BeginScrollableArgs>),
69    Link {
70        text: String,
71        url: String,
72        style: Style,
73        margin: Margin,
74        constraints: Constraints,
75    },
76    RichText {
77        segments: Vec<(String, Style)>,
78        wrap: bool,
79        align: Align,
80        margin: Margin,
81        constraints: Constraints,
82    },
83    EndContainer,
84    BeginOverlay {
85        modal: bool,
86    },
87    EndOverlay,
88    Spacer {
89        grow: u16,
90    },
91    FocusMarker(usize),
92    InteractionMarker(usize),
93    RawDraw {
94        draw_id: usize,
95        constraints: Constraints,
96        grow: u16,
97        margin: Margin,
98    },
99}
100
101#[cfg(test)]
102mod size_tests {
103    use super::Command;
104
105    /// Regression guard for the `Command` enum size.
106    ///
107    /// A frame may push hundreds of `Command` values into a single `Vec`,
108    /// so every byte in the enum variant-union multiplies across the frame.
109    /// Fat variants (`BeginContainer`, `BeginScrollable`) are boxed to keep
110    /// the common 0-payload variants (e.g. `EndContainer`) cheap.
111    ///
112    /// The current ceiling reflects the largest remaining inline variant
113    /// (`Text`, which carries a `String` + `Constraints` + `Style` + small
114    /// scalars). If this test fires after a refactor, either box the new
115    /// fat variant or bump this bound with justification.
116    #[test]
117    fn command_enum_size_is_bounded() {
118        const MAX_BYTES: usize = 128;
119        let actual = std::mem::size_of::<Command>();
120        assert!(
121            actual <= MAX_BYTES,
122            "Command enum grew to {actual} bytes (limit {MAX_BYTES}); \
123             consider boxing the new fat variant"
124        );
125    }
126}