Skip to main content

miracle_plugin/
container.rs

1use super::bindings;
2use super::host::*;
3use super::window::*;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
6#[repr(u32)]
7pub enum ContainerType {
8    /// The container has a single window in it.
9    #[default]
10    Window = 0,
11    /// The container has multiple children in it.
12    Parent = 1,
13}
14
15impl From<ContainerType> for bindings::miracle_container_type {
16    fn from(value: ContainerType) -> Self {
17        value as bindings::miracle_container_type
18    }
19}
20
21impl TryFrom<bindings::miracle_container_type> for ContainerType {
22    type Error = ();
23
24    fn try_from(value: bindings::miracle_container_type) -> Result<Self, Self::Error> {
25        match value {
26            0 => Ok(Self::Window),
27            1 => Ok(Self::Parent),
28            _ => Err(()),
29        }
30    }
31}
32
33#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
34#[repr(u32)]
35pub enum LayoutScheme {
36    #[default]
37    None = 0,
38    Horizontal = 1,
39    Vertical = 2,
40    Tabbed = 3,
41    Stacking = 4,
42}
43
44impl From<LayoutScheme> for bindings::miracle_layout_scheme {
45    fn from(value: LayoutScheme) -> Self {
46        value as bindings::miracle_layout_scheme
47    }
48}
49
50impl TryFrom<bindings::miracle_layout_scheme> for LayoutScheme {
51    type Error = ();
52
53    fn try_from(value: bindings::miracle_layout_scheme) -> Result<Self, Self::Error> {
54        match value {
55            0 => Ok(Self::None),
56            1 => Ok(Self::Horizontal),
57            2 => Ok(Self::Vertical),
58            3 => Ok(Self::Tabbed),
59            4 => Ok(Self::Stacking),
60            _ => Err(()),
61        }
62    }
63}
64
65#[derive(Debug, Clone, Copy)]
66pub struct Container {
67    /// The type of the container.
68    pub container_type: ContainerType,
69    /// Whether the container is floating within its workspace.
70    pub is_floating: bool,
71    /// How the container is laying out its content.
72    pub layout_scheme: LayoutScheme,
73    /// The number of child containers.
74    pub num_children: u32,
75    internal: u64,
76}
77
78impl Container {
79    pub fn id(&self) -> u64 {
80        self.internal
81    }
82
83    /// Get a child container from the parent container by index.
84    ///
85    /// Returns `None` if the index is out of bounds or if the container
86    /// is not of type `Parent`.
87    pub fn child_at(&self, index: u32) -> Option<Container> {
88        if self.container_type != ContainerType::Parent || index >= self.num_children {
89            return None;
90        }
91
92        let mut child_container =
93            std::mem::MaybeUninit::<crate::bindings::miracle_container_t>::uninit();
94        unsafe {
95            let result = miracle_container_get_child_at(
96                self.internal as i64,
97                index,
98                child_container.as_mut_ptr() as i32,
99            );
100
101            if result != 0 {
102                return None;
103            }
104
105            let child_container = child_container.assume_init();
106            Some(Container::from(child_container))
107        }
108    }
109
110    /// Get all children from a parent container.
111    ///
112    /// Returns an empty vector if the container is not of type `Parent`.
113    pub fn get_children(&self) -> Vec<Container> {
114        if self.container_type != ContainerType::Parent {
115            return Vec::new();
116        }
117        (0..self.num_children)
118            .filter_map(|i| self.child_at(i))
119            .collect()
120    }
121
122    /// Get the window info from a window container.
123    ///
124    /// Returns `None` if the container is not of type `Window`.
125    pub fn window(&self) -> Option<WindowInfo> {
126        if self.container_type != ContainerType::Window {
127            return None;
128        }
129
130        const NAME_BUF_LEN: usize = 256;
131        let mut window = std::mem::MaybeUninit::<crate::bindings::miracle_window_info_t>::uninit();
132        let mut name_buf: [u8; NAME_BUF_LEN] = [0; NAME_BUF_LEN];
133
134        unsafe {
135            let result = miracle_container_get_window(
136                self.internal as i64,
137                window.as_mut_ptr() as i32,
138                name_buf.as_mut_ptr() as i32,
139                NAME_BUF_LEN as i32,
140            );
141
142            if result != 0 {
143                return None;
144            }
145
146            let window = window.assume_init();
147
148            // Find the null terminator to get the actual string length
149            let name_len = name_buf
150                .iter()
151                .position(|&c| c == 0)
152                .unwrap_or(NAME_BUF_LEN);
153            let name = String::from_utf8_lossy(&name_buf[..name_len]).into_owned();
154
155            Some(WindowInfo::from_c_with_name(&window, name))
156        }
157    }
158}
159
160impl From<bindings::miracle_container_t> for Container {
161    fn from(value: bindings::miracle_container_t) -> Self {
162        Self {
163            container_type: ContainerType::try_from(value.type_).unwrap_or_default(),
164            is_floating: value.is_floating != 0,
165            layout_scheme: LayoutScheme::try_from(value.layout_scheme).unwrap_or_default(),
166            num_children: value.num_child_containers,
167            internal: value.internal,
168        }
169    }
170}