freya_hooks/
use_platform.rs1use std::sync::Arc;
2
3use dioxus_core::{
4 prelude::{
5 consume_context,
6 provide_root_context,
7 try_consume_context,
8 use_hook,
9 },
10 ScopeId,
11};
12use dioxus_signals::{
13 Readable,
14 Signal,
15};
16use freya_core::{
17 accessibility::AccessibilityFocusStrategy,
18 event_loop_messages::EventLoopMessage,
19 platform::{
20 CursorIcon,
21 EventLoopProxy,
22 Fullscreen,
23 Window,
24 },
25};
26use tokio::sync::{
27 broadcast,
28 mpsc::UnboundedSender,
29};
30use torin::prelude::Area;
31
32#[derive(Clone, Copy, PartialEq)]
33pub struct UsePlatform {
34 ticker: Signal<Arc<broadcast::Receiver<()>>>,
35 event_loop_proxy: Signal<Option<EventLoopProxy<EventLoopMessage>>>,
36 platform_emitter: Signal<Option<UnboundedSender<EventLoopMessage>>>,
37}
38
39#[derive(PartialEq, Eq, Debug)]
40pub enum UsePlatformError {
41 EventLoopProxyFailed,
42 PlatformEmitterFailed,
43}
44
45impl UsePlatform {
46 pub fn current() -> Self {
47 match try_consume_context() {
48 Some(p) => p,
49 None => provide_root_context(UsePlatform {
50 event_loop_proxy: Signal::new_in_scope(
51 try_consume_context::<EventLoopProxy<EventLoopMessage>>(),
52 ScopeId::ROOT,
53 ),
54 platform_emitter: Signal::new_in_scope(
55 try_consume_context::<UnboundedSender<EventLoopMessage>>(),
56 ScopeId::ROOT,
57 ),
58 ticker: Signal::new_in_scope(
59 consume_context::<Arc<broadcast::Receiver<()>>>(),
60 ScopeId::ROOT,
61 ),
62 }),
63 }
64 }
65
66 pub fn send(&self, event: EventLoopMessage) -> Result<(), UsePlatformError> {
67 if let Some(event_loop_proxy) = &*self.event_loop_proxy.peek() {
68 event_loop_proxy
69 .send_event(event)
70 .map_err(|_| UsePlatformError::EventLoopProxyFailed)?;
71 } else if let Some(platform_emitter) = &*self.platform_emitter.peek() {
72 platform_emitter
73 .send(event)
74 .map_err(|_| UsePlatformError::PlatformEmitterFailed)?;
75 }
76 Ok(())
77 }
78
79 pub fn set_cursor(&self, cursor_icon: CursorIcon) {
80 self.send(EventLoopMessage::SetCursorIcon(cursor_icon)).ok();
81 }
82
83 pub fn set_title(&self, title: impl Into<String>) {
84 let title = title.into();
85 self.with_window(move |window| {
86 window.set_title(&title);
87 });
88 }
89
90 pub fn with_window(&self, cb: impl FnOnce(&Window) + 'static + Send + Sync) {
91 self.send(EventLoopMessage::WithWindow(Box::new(cb))).ok();
92 }
93
94 pub fn drag_window(&self) {
95 self.with_window(|window| {
96 window.drag_window().ok();
97 });
98 }
99
100 pub fn set_maximize_window(&self, maximize: bool) {
101 self.with_window(move |window| {
102 window.set_maximized(maximize);
103 });
104 }
105
106 pub fn toggle_maximize_window(&self) {
107 self.with_window(|window| {
108 window.set_maximized(!window.is_maximized());
109 });
110 }
111
112 pub fn set_minimize_window(&self, minimize: bool) {
113 self.with_window(move |window| {
114 window.set_minimized(minimize);
115 });
116 }
117
118 pub fn toggle_minimize_window(&self) {
119 self.with_window(|window| {
120 window.set_minimized(window.is_minimized().map(|v| !v).unwrap_or_default());
121 });
122 }
123
124 pub fn toggle_fullscreen_window(&self) {
125 self.with_window(|window| match window.fullscreen() {
126 Some(_) => window.set_fullscreen(None),
127 None => window.set_fullscreen(Some(Fullscreen::Borderless(None))),
128 });
129 }
130
131 pub fn set_fullscreen_window(&self, fullscreen: bool) {
132 self.with_window(move |window| {
133 if fullscreen {
134 window.set_fullscreen(Some(Fullscreen::Borderless(None)))
135 } else {
136 window.set_fullscreen(None)
137 }
138 });
139 }
140
141 pub fn invalidate_drawing_area(&self, area: Area) {
142 self.send(EventLoopMessage::InvalidateArea(area)).ok();
143 }
144
145 pub fn request_animation_frame(&self) {
146 self.send(EventLoopMessage::RequestRerender).ok();
147 }
148
149 pub fn focus(&self, strategy: AccessibilityFocusStrategy) {
150 self.send(EventLoopMessage::FocusAccessibilityNode(strategy))
151 .ok();
152 }
153
154 pub fn new_ticker(&self) -> Ticker {
155 Ticker {
156 inner: self.ticker.peek().resubscribe(),
157 }
158 }
159
160 pub fn exit(&self) {
162 self.send(EventLoopMessage::ExitApp).ok();
163 }
164}
165
166pub fn use_platform() -> UsePlatform {
168 use_hook(UsePlatform::current)
169}
170
171pub struct Ticker {
172 inner: broadcast::Receiver<()>,
173}
174
175impl Ticker {
176 pub async fn tick(&mut self) {
177 self.inner.recv().await.ok();
178 }
179}