1use crate::schema::{EventPayload, Theme};
18use std::cell::{Cell, RefCell};
19use std::rc::Rc;
20
21pub use crate::schema::{LastWindowClosed, WindowMode};
22
23#[derive(Clone, Copy, Debug, Eq, PartialEq)]
25pub enum WindowDragPhase {
26 Move,
28 End,
30 Cancel,
32}
33
34impl WindowDragPhase {
35 fn from_wire(value: u32) -> Option<Self> {
36 match value {
37 crate::protocol::WINDOW_DRAG_MOVE => Some(Self::Move),
38 crate::protocol::WINDOW_DRAG_END => Some(Self::End),
39 crate::protocol::WINDOW_DRAG_CANCEL => Some(Self::Cancel),
40 _ => None,
41 }
42 }
43}
44
45#[derive(Clone, Copy, Debug, Eq, PartialEq)]
47pub enum WindowDragOverKind {
48 Enter,
49 Move,
50 Leave,
51 Drop,
53}
54
55impl WindowDragOverKind {
56 fn from_wire(value: u32) -> Option<Self> {
57 match value {
58 crate::protocol::WINDOW_DRAG_OVER_ENTER => Some(Self::Enter),
59 crate::protocol::WINDOW_DRAG_OVER_MOVE => Some(Self::Move),
60 crate::protocol::WINDOW_DRAG_OVER_LEAVE => Some(Self::Leave),
61 crate::protocol::WINDOW_DRAG_OVER_DROP => Some(Self::Drop),
62 _ => None,
63 }
64 }
65}
66
67#[derive(Clone, Copy, Debug, PartialEq)]
69pub enum WindowEvent {
70 CloseRequested,
73 FocusChanged { focused: bool },
75 Moved {
77 x: f32,
78 y: f32,
79 width: f32,
80 height: f32,
81 },
82 Drag {
85 phase: WindowDragPhase,
86 screen_x: f32,
87 screen_y: f32,
88 over_window: u32,
89 },
90 DragOver {
93 kind: WindowDragOverKind,
94 window: u32,
95 x: f32,
96 y: f32,
97 },
98 Unavailable,
100 Closed,
102}
103
104impl WindowEvent {
105 pub(crate) fn from_payload(payload: &EventPayload<'_>) -> Option<Self> {
107 Some(match *payload {
108 EventPayload::WindowCloseRequested => Self::CloseRequested,
109 EventPayload::WindowFocusChanged { focused } => Self::FocusChanged { focused },
110 EventPayload::WindowMoved {
111 x,
112 y,
113 width,
114 height,
115 } => Self::Moved {
116 x,
117 y,
118 width,
119 height,
120 },
121 EventPayload::WindowDrag {
122 phase,
123 screen_x,
124 screen_y,
125 over_window,
126 } => Self::Drag {
127 phase: WindowDragPhase::from_wire(phase)?,
128 screen_x,
129 screen_y,
130 over_window,
131 },
132 EventPayload::WindowDragOver { kind, window, x, y } => Self::DragOver {
133 kind: WindowDragOverKind::from_wire(kind)?,
134 window,
135 x,
136 y,
137 },
138 EventPayload::WindowUnavailable => Self::Unavailable,
139 EventPayload::WindowClosed => Self::Closed,
140 _ => return None,
141 })
142 }
143}
144
145#[derive(Clone, Copy, Debug, PartialEq)]
147pub struct WindowFrame {
148 pub x: f32,
149 pub y: f32,
150 pub width: f32,
151 pub height: f32,
152}
153
154#[derive(Clone, Copy, Debug, PartialEq)]
156pub struct WindowDragEvent {
157 pub phase: WindowDragPhase,
158 pub screen_x: f32,
160 pub screen_y: f32,
161 pub over_window: u32,
163}
164
165#[derive(Clone, Copy, Debug, PartialEq)]
167pub struct WindowDragOver {
168 pub kind: WindowDragOverKind,
169 pub window: u32,
171 pub x: f32,
173 pub y: f32,
174}
175
176#[derive(Clone, Copy, Debug, PartialEq)]
179pub struct DragFromPointer {
180 pub pointer_id: u32,
181 pub anchor_x: f32,
183 pub anchor_y: f32,
184}
185
186impl DragFromPointer {
187 pub(crate) fn to_bytes(self) -> Vec<u8> {
189 let mut bytes = Vec::with_capacity(12);
190 bytes.extend_from_slice(&self.pointer_id.to_le_bytes());
191 bytes.extend_from_slice(&self.anchor_x.to_bits().to_le_bytes());
192 bytes.extend_from_slice(&self.anchor_y.to_bits().to_le_bytes());
193 bytes
194 }
195}
196
197#[derive(Clone, Debug, Eq, PartialEq)]
200pub struct WindowRequest {
201 pub key: String,
203 pub request: u32,
205}
206
207pub(crate) enum Command {
209 Focus(u32),
210 Theme(u32, Theme),
211 ZoomLevel(u32, i8),
212}
213
214type RequestHandler = Rc<dyn Fn(WindowRequest)>;
215type ReopenHandler = Rc<dyn Fn()>;
216
217thread_local! {
218 static DISPATCH_WINDOW: Cell<u32> = const { Cell::new(0) };
221 static COMMANDS: RefCell<Vec<Command>> = const { RefCell::new(Vec::new()) };
222 static APPLIED_THEMES: RefCell<Vec<(u32, Theme)>> = const { RefCell::new(Vec::new()) };
224 static REQUEST_HANDLERS: RefCell<Vec<(u64, RequestHandler)>> =
225 const { RefCell::new(Vec::new()) };
226 static REOPEN_HANDLERS: RefCell<Vec<(u64, ReopenHandler)>> =
227 const { RefCell::new(Vec::new()) };
228 static NEXT_ID: Cell<u64> = const { Cell::new(1) };
229}
230
231pub(crate) struct DispatchWindow {
233 previous: u32,
234}
235
236impl DispatchWindow {
237 pub(crate) fn enter(window: u32) -> Self {
238 let previous = DISPATCH_WINDOW.with(|current| current.replace(window));
239 Self { previous }
240 }
241}
242
243impl Drop for DispatchWindow {
244 fn drop(&mut self) {
245 let _ = DISPATCH_WINDOW.try_with(|current| current.set(self.previous));
246 }
247}
248
249pub(crate) fn current_window_id() -> u32 {
254 let composing = crate::runtime::with_composer_if_idle(|composer| {
255 composer.composing().then(|| composer.enclosing_window())
256 })
257 .flatten();
258 composing.unwrap_or_else(|| DISPATCH_WINDOW.with(Cell::get))
259}
260
261#[derive(Clone, Copy, Debug, Eq, PartialEq)]
266pub struct WindowHandle {
267 node: u32,
268}
269
270pub fn current_window() -> Option<WindowHandle> {
273 match current_window_id() {
274 0 => None,
275 node => Some(WindowHandle { node }),
276 }
277}
278
279impl WindowHandle {
280 pub const fn id(&self) -> u32 {
282 self.node
283 }
284
285 pub fn focus(&self) {
288 COMMANDS.with_borrow_mut(|commands| commands.push(Command::Focus(self.node)));
289 crate::boundary::request_frame_from_worker();
290 }
291
292 pub fn set_theme(&self, theme: Theme) {
294 apply_theme(self.node, theme);
295 }
296
297 pub fn size(&self) -> crate::window::WindowSize {
300 crate::window::window_size_of(self.node)
301 }
302
303 pub fn zoom(&self) -> crate::zoom::Zoom {
305 crate::zoom::zoom_of(self.node)
306 }
307
308 pub fn set_zoom_level(&self, level: i8) {
310 queue_zoom_level(self.node, crate::zoom::clamp_level(level));
311 }
312}
313
314pub(crate) fn apply_theme(window: u32, theme: Theme) {
316 let changed = APPLIED_THEMES.with_borrow_mut(|applied| {
317 match applied.iter_mut().find(|(id, _)| *id == window) {
318 Some(entry) => {
319 if entry.1 == theme {
320 return false;
321 }
322 entry.1 = theme;
323 true
324 }
325 None => {
326 applied.push((window, theme));
327 true
328 }
329 }
330 });
331 if !changed {
332 return;
333 }
334 COMMANDS.with_borrow_mut(|commands| {
335 commands.retain(|command| !matches!(command, Command::Theme(id, _) if *id == window));
336 commands.push(Command::Theme(window, theme));
337 });
338 crate::boundary::request_frame_from_worker();
339}
340
341pub(crate) fn queue_zoom_level(window: u32, level: i8) {
343 COMMANDS.with_borrow_mut(|commands| {
344 commands.retain(|command| !matches!(command, Command::ZoomLevel(id, _) if *id == window));
345 commands.push(Command::ZoomLevel(window, level));
346 });
347 crate::boundary::request_frame_from_worker();
348}
349
350pub(crate) fn pending_zoom_level(window: u32) -> Option<i8> {
352 COMMANDS.with_borrow(|commands| {
353 commands.iter().find_map(|command| match command {
354 Command::ZoomLevel(id, level) if *id == window => Some(*level),
355 _ => None,
356 })
357 })
358}
359
360pub(crate) fn drain_commands(mut each: impl FnMut(Command)) {
362 let commands = COMMANDS.with_borrow_mut(std::mem::take);
363 for command in commands {
364 each(command);
365 }
366}
367
368pub(crate) fn forget_window(window: u32) {
370 crate::window::forget_window(window);
371 crate::zoom::forget_window(window);
372 APPLIED_THEMES.with_borrow_mut(|applied| applied.retain(|(id, _)| *id != window));
373 COMMANDS.with_borrow_mut(|commands| {
374 commands.retain(|command| match command {
375 Command::Focus(id) | Command::Theme(id, _) | Command::ZoomLevel(id, _) => *id != window,
376 });
377 });
378}
379
380#[doc(hidden)]
384pub fn window_state_len() -> usize {
385 crate::window::window_state_len()
386 + crate::zoom::window_state_len()
387 + APPLIED_THEMES.with_borrow(Vec::len)
388 + COMMANDS.with_borrow(Vec::len)
389}
390
391#[doc(hidden)]
393pub fn reset_windows() {
394 DISPATCH_WINDOW.with(|current| current.set(0));
395 COMMANDS.with_borrow_mut(Vec::clear);
396 APPLIED_THEMES.with_borrow_mut(Vec::clear);
397 REQUEST_HANDLERS.with_borrow_mut(Vec::clear);
398 REOPEN_HANDLERS.with_borrow_mut(Vec::clear);
399}
400
401fn next_id() -> u64 {
402 NEXT_ID.with(|next| {
403 let id = next.get();
404 next.set(id + 1);
405 id
406 })
407}
408
409pub struct WindowRequestSubscription {
412 id: u64,
413}
414
415impl Drop for WindowRequestSubscription {
416 fn drop(&mut self) {
417 REQUEST_HANDLERS.with_borrow_mut(|handlers| handlers.retain(|(id, _)| *id != self.id));
418 }
419}
420
421pub fn on_window_request(handler: Rc<dyn Fn(WindowRequest)>) -> WindowRequestSubscription {
426 let id = next_id();
427 REQUEST_HANDLERS.with_borrow_mut(|handlers| handlers.push((id, handler)));
428 WindowRequestSubscription { id }
429}
430
431pub(crate) fn deliver_request(request: &WindowRequest) -> bool {
433 let handlers: Vec<Rc<dyn Fn(WindowRequest)>> = REQUEST_HANDLERS.with_borrow(|handlers| {
435 handlers
436 .iter()
437 .map(|(_, handler)| handler.clone())
438 .collect()
439 });
440 for handler in &handlers {
441 handler(request.clone());
442 }
443 !handlers.is_empty()
444}
445
446pub struct ApplicationReopenSubscription {
449 id: u64,
450}
451
452impl Drop for ApplicationReopenSubscription {
453 fn drop(&mut self) {
454 REOPEN_HANDLERS.with_borrow_mut(|handlers| handlers.retain(|(id, _)| *id != self.id));
455 }
456}
457
458pub fn on_application_reopen_handler(handler: Rc<dyn Fn()>) -> ApplicationReopenSubscription {
461 let id = next_id();
462 REOPEN_HANDLERS.with_borrow_mut(|handlers| handlers.push((id, handler)));
463 ApplicationReopenSubscription { id }
464}
465
466pub(crate) fn deliver_reopen() -> bool {
468 let handlers: Vec<Rc<dyn Fn()>> = REOPEN_HANDLERS.with_borrow(|handlers| {
469 handlers
470 .iter()
471 .map(|(_, handler)| handler.clone())
472 .collect()
473 });
474 for handler in &handlers {
475 handler();
476 }
477 !handlers.is_empty()
478}