sctk_adwaita/
parts.rs

1use smithay_client_toolkit::reexports::client::{
2    backend::ObjectId,
3    protocol::{wl_subsurface::WlSubsurface, wl_surface::WlSurface},
4    Dispatch, Proxy, QueueHandle,
5};
6
7use smithay_client_toolkit::{
8    compositor::SurfaceData,
9    subcompositor::{SubcompositorState, SubsurfaceData},
10};
11
12use crate::theme::{BORDER_SIZE, HEADER_SIZE, RESIZE_HANDLE_SIZE};
13use crate::{pointer::Location, wl_typed::WlTyped};
14
15/// The decoration's 'parts'.
16#[derive(Debug)]
17pub struct DecorationParts {
18    parts: [Part; 5],
19}
20
21impl DecorationParts {
22    // XXX keep in sync with `Self;:new`.
23    // Order is important. The lower the number, the earlier the part gets drawn.
24    // Because the header can overlap other parts, we draw it last.
25    pub const TOP: usize = 0;
26    pub const LEFT: usize = 1;
27    pub const RIGHT: usize = 2;
28    pub const BOTTOM: usize = 3;
29    pub const HEADER: usize = 4;
30
31    pub fn new<State>(
32        base_surface: &WlTyped<WlSurface, SurfaceData>,
33        subcompositor: &SubcompositorState,
34        queue_handle: &QueueHandle<State>,
35    ) -> Self
36    where
37        State: Dispatch<WlSurface, SurfaceData> + Dispatch<WlSubsurface, SubsurfaceData> + 'static,
38    {
39        // XXX the order must be in sync with associated constants.
40        let parts = [
41            // Top.
42            Part::new(
43                base_surface,
44                subcompositor,
45                queue_handle,
46                Rect {
47                    x: -(BORDER_SIZE as i32),
48                    y: -(HEADER_SIZE as i32 + BORDER_SIZE as i32),
49                    width: 0, // Defined by `Self::resize`.
50                    height: BORDER_SIZE,
51                },
52                Some(Rect {
53                    x: BORDER_SIZE as i32 - RESIZE_HANDLE_SIZE as i32,
54                    y: BORDER_SIZE as i32 - RESIZE_HANDLE_SIZE as i32,
55                    width: 0, // Defined by `Self::resize`.
56                    height: RESIZE_HANDLE_SIZE,
57                }),
58            ),
59            // Left.
60            Part::new(
61                base_surface,
62                subcompositor,
63                queue_handle,
64                Rect {
65                    x: -(BORDER_SIZE as i32),
66                    y: -(HEADER_SIZE as i32),
67                    width: BORDER_SIZE,
68                    height: 0, // Defined by `Self::resize`.
69                },
70                Some(Rect {
71                    x: BORDER_SIZE as i32 - RESIZE_HANDLE_SIZE as i32,
72                    y: 0,
73                    width: RESIZE_HANDLE_SIZE,
74                    height: 0, // Defined by `Self::resize`.
75                }),
76            ),
77            // Right.
78            Part::new(
79                base_surface,
80                subcompositor,
81                queue_handle,
82                Rect {
83                    x: 0, // Defined by `Self::resize`.
84                    y: -(HEADER_SIZE as i32),
85                    width: BORDER_SIZE,
86                    height: 0, // Defined by `Self::resize`.
87                },
88                Some(Rect {
89                    x: 0,
90                    y: 0,
91                    width: RESIZE_HANDLE_SIZE,
92                    height: 0, // Defined by `Self::resize`.
93                }),
94            ),
95            // Bottom.
96            Part::new(
97                base_surface,
98                subcompositor,
99                queue_handle,
100                Rect {
101                    x: -(BORDER_SIZE as i32),
102                    y: 0,     // Defined by `Self::resize`.
103                    width: 0, // Defined by `Self::resize`.
104                    height: BORDER_SIZE,
105                },
106                Some(Rect {
107                    x: BORDER_SIZE as i32 - RESIZE_HANDLE_SIZE as i32,
108                    y: 0,
109                    width: 0, // Defined by `Self::resize`,
110                    height: RESIZE_HANDLE_SIZE,
111                }),
112            ),
113            // Header.
114            Part::new(
115                base_surface,
116                subcompositor,
117                queue_handle,
118                Rect {
119                    x: 0,
120                    y: -(HEADER_SIZE as i32),
121                    width: 0, // Defined by `Self::resize`.
122                    height: HEADER_SIZE,
123                },
124                None,
125            ),
126        ];
127
128        Self { parts }
129    }
130
131    pub fn parts(&self) -> std::iter::Enumerate<std::slice::Iter<Part>> {
132        self.parts.iter().enumerate()
133    }
134
135    pub fn hide(&self) {
136        for part in self.parts.iter() {
137            part.subsurface.set_sync();
138            part.surface.attach(None, 0, 0);
139            part.surface.commit();
140        }
141    }
142
143    pub fn hide_borders(&self) {
144        for (_, part) in self.parts().filter(|(idx, _)| *idx != Self::HEADER) {
145            part.surface.attach(None, 0, 0);
146            part.surface.commit();
147        }
148    }
149
150    // These unwraps are guaranteed to succeed because the affected options are filled above
151    // and then never emptied afterwards.
152    #[allow(clippy::unwrap_used)]
153    pub fn resize(&mut self, width: u32, height: u32) {
154        self.parts[Self::HEADER].surface_rect.width = width;
155
156        self.parts[Self::BOTTOM].surface_rect.width = width + 2 * BORDER_SIZE;
157        self.parts[Self::BOTTOM].surface_rect.y = height as i32;
158        self.parts[Self::BOTTOM].input_rect.as_mut().unwrap().width =
159            self.parts[Self::BOTTOM].surface_rect.width - (BORDER_SIZE * 2)
160                + (RESIZE_HANDLE_SIZE * 2);
161
162        self.parts[Self::TOP].surface_rect.width = self.parts[Self::BOTTOM].surface_rect.width;
163        self.parts[Self::TOP].input_rect.as_mut().unwrap().width =
164            self.parts[Self::TOP].surface_rect.width - (BORDER_SIZE * 2) + (RESIZE_HANDLE_SIZE * 2);
165
166        self.parts[Self::LEFT].surface_rect.height = height + HEADER_SIZE;
167        self.parts[Self::LEFT].input_rect.as_mut().unwrap().height =
168            self.parts[Self::LEFT].surface_rect.height;
169
170        self.parts[Self::RIGHT].surface_rect.height = self.parts[Self::LEFT].surface_rect.height;
171        self.parts[Self::RIGHT].surface_rect.x = width as i32;
172        self.parts[Self::RIGHT].input_rect.as_mut().unwrap().height =
173            self.parts[Self::RIGHT].surface_rect.height;
174    }
175
176    pub fn header(&self) -> &Part {
177        &self.parts[Self::HEADER]
178    }
179
180    pub fn side_height(&self) -> u32 {
181        self.parts[Self::LEFT].surface_rect.height
182    }
183
184    pub fn find_surface(&self, surface: &ObjectId) -> Location {
185        let pos = match self
186            .parts
187            .iter()
188            .position(|part| &part.surface.id() == surface)
189        {
190            Some(pos) => pos,
191            None => return Location::None,
192        };
193
194        match pos {
195            Self::HEADER => Location::Head,
196            Self::TOP => Location::Top,
197            Self::BOTTOM => Location::Bottom,
198            Self::LEFT => Location::Left,
199            Self::RIGHT => Location::Right,
200            _ => unreachable!(),
201        }
202    }
203}
204
205#[derive(Debug, Clone, Copy)]
206pub struct Rect {
207    pub x: i32,
208    pub y: i32,
209    pub width: u32,
210    pub height: u32,
211}
212
213#[derive(Debug)]
214pub struct Part {
215    pub surface: WlTyped<WlSurface, SurfaceData>,
216    pub subsurface: WlTyped<WlSubsurface, SubsurfaceData>,
217
218    /// Positioned relative to the main surface.
219    pub surface_rect: Rect,
220    /// Positioned relative to the local surface, aka. `surface_rect`.
221    ///
222    /// `None` if it fully covers `surface_rect`.
223    pub input_rect: Option<Rect>,
224}
225
226impl Part {
227    fn new<State>(
228        parent: &WlTyped<WlSurface, SurfaceData>,
229        subcompositor: &SubcompositorState,
230        queue_handle: &QueueHandle<State>,
231        surface_rect: Rect,
232        input_rect: Option<Rect>,
233    ) -> Part
234    where
235        State: Dispatch<WlSurface, SurfaceData> + Dispatch<WlSubsurface, SubsurfaceData> + 'static,
236    {
237        let (subsurface, surface) =
238            subcompositor.create_subsurface(parent.inner().clone(), queue_handle);
239
240        let subsurface = WlTyped::wrap::<State>(subsurface);
241        let surface = WlTyped::wrap::<State>(surface);
242
243        // Sync with the parent surface.
244        subsurface.set_sync();
245
246        Part {
247            surface,
248            subsurface,
249            surface_rect,
250            input_rect,
251        }
252    }
253}
254
255impl Drop for Part {
256    fn drop(&mut self) {
257        self.subsurface.destroy();
258        self.surface.destroy();
259    }
260}