Skip to main content

ferroui_controls/platform/
i_screen_impl.rs

1use super::{IPlatformHandle, ITopLevelImpl, IWindowBaseImpl, Screen, ScreenHelper};
2use ferroui_base::threading::{Dispatcher, DispatcherOperation, DispatcherPriority};
3use ferroui_base::{PixelPoint, PixelRect};
4use std::cell::{Cell, RefCell};
5use std::collections::{HashMap, HashSet};
6use std::future::Future;
7use std::hash::Hash;
8use std::ops::Deref;
9use std::pin::Pin;
10use std::rc::Rc;
11
12/// The screens of the platform.
13pub trait IScreenImpl {
14    /// Gets the total number of screens available on the device.
15    fn screen_count(&self) -> i32;
16
17    /// Gets the list of all screens available on the device.
18    fn all_screens(&self) -> Vec<Rc<Screen>>;
19
20    /// Gets the method called when the screens change.
21    fn changed(&self) -> Option<Rc<dyn Fn()>>;
22
23    /// Sets a method called when the screens change.
24    fn set_changed(&self, value: Option<Rc<dyn Fn()>>);
25
26    /// The screen that `window` is on.
27    fn screen_from_window(&self, window: &dyn IWindowBaseImpl) -> Option<Rc<Screen>>;
28
29    /// The screen that `top_level` is on.
30    fn screen_from_top_level(&self, top_level: &dyn ITopLevelImpl) -> Option<Rc<Screen>>;
31
32    /// The screen that contains `point`.
33    fn screen_from_point(&self, point: PixelPoint) -> Option<Rc<Screen>>;
34
35    /// The screen that the largest part of `rect` is on.
36    fn screen_from_rect(&self, rect: PixelRect) -> Option<Rc<Screen>>;
37
38    /// Asks the platform for the permission to access the details of all
39    /// screens; resolves to whether it was granted.
40    ///
41    /// The returned future is not tied to an executor; on the UI thread it
42    /// is driven by the dispatcher (`invoke_async_task_local`).
43    fn request_screen_details(&self) -> Pin<Box<dyn Future<Output = bool>>>;
44}
45
46/// A screen that is identified by a platform handle.
47///
48/// Backend screen types embed a `PlatformScreen` and expose it through
49/// `AsRef<PlatformScreen>`; the screen data itself is shared as
50/// [`Rc<Screen>`](Screen).
51pub struct PlatformScreen {
52    screen: Rc<Screen>,
53}
54
55impl PlatformScreen {
56    /// Creates a screen identified by `platform_handle`.
57    pub fn new(platform_handle: Rc<dyn IPlatformHandle>) -> Self {
58        Self { screen: Rc::new(Screen::new(Some(platform_handle))) }
59    }
60
61    /// The shared screen.
62    pub fn screen(&self) -> &Rc<Screen> {
63        &self.screen
64    }
65}
66
67impl Deref for PlatformScreen {
68    type Target = Screen;
69
70    fn deref(&self) -> &Screen {
71        &self.screen
72    }
73}
74
75impl AsRef<PlatformScreen> for PlatformScreen {
76    fn as_ref(&self) -> &PlatformScreen {
77        self
78    }
79}
80
81/// The state of a screens implementation that is based on
82/// [`ScreensBaseImpl`]: the screens by key and the cached values.
83pub struct ScreensBase<TKey, TScreen> {
84    all_screens_by_key: RefCell<HashMap<TKey, Rc<TScreen>>>,
85    all_screens: RefCell<Option<Vec<Rc<TScreen>>>>,
86    screen_count: Cell<Option<i32>>,
87    screen_details_request_granted: Rc<Cell<Option<bool>>>,
88    on_change_operation: RefCell<Option<DispatcherOperation>>,
89    changed: RefCell<Option<Rc<dyn Fn()>>>,
90}
91
92impl<TKey, TScreen> ScreensBase<TKey, TScreen> {
93    /// Creates the state of a screens implementation with no screens.
94    pub fn new() -> Self {
95        Self {
96            all_screens_by_key: RefCell::new(HashMap::new()),
97            all_screens: RefCell::new(None),
98            screen_count: Cell::new(None),
99            screen_details_request_granted: Rc::new(Cell::new(None)),
100            on_change_operation: RefCell::new(None),
101            changed: RefCell::new(None),
102        }
103    }
104}
105
106impl<TKey, TScreen> Default for ScreensBase<TKey, TScreen> {
107    fn default() -> Self {
108        Self::new()
109    }
110}
111
112/// The overridable part of a screens implementation that keeps one screen
113/// object per platform screen key, preserving the objects across changes.
114///
115/// A backend embeds a [`ScreensBase`], returns it from
116/// [`screens_base`](Self::screens_base) and implements the two required
117/// members; it then is an [`IScreenImpl`] and has the members of
118/// [`ScreensBaseImplExt`]. The backend calls
119/// [`on_changed`](ScreensBaseImplExt::on_changed) when the platform reports
120/// a change of the screens.
121pub trait ScreensBaseImpl: 'static {
122    /// What identifies a screen for the platform.
123    type Key: Eq + Hash + Clone + 'static;
124
125    /// The screen type of the backend.
126    type Screen: AsRef<PlatformScreen> + 'static;
127
128    /// The state embedded in the implementation.
129    fn screens_base(&self) -> &ScreensBase<Self::Key, Self::Screen>;
130
131    /// The keys of all screens that currently exist.
132    fn get_all_screen_keys(&self) -> Vec<Self::Key>;
133
134    /// Creates the screen for a key that was not known before.
135    fn create_screen_from_key(&self, key: &Self::Key) -> Rc<Self::Screen>;
136
137    /// Called for a screen that was just created.
138    fn screen_added(&self, screen: &Rc<Self::Screen>) {
139        self.screen_changed(screen);
140    }
141
142    /// Called for a known screen each time the screens are refreshed.
143    fn screen_changed(&self, _screen: &Rc<Self::Screen>) {}
144
145    /// Called for a screen whose key disappeared.
146    fn screen_removed(&self, screen: &Rc<Self::Screen>) {
147        (**screen).as_ref().on_removed();
148    }
149
150    /// The number of screens.
151    fn get_screen_count(&self) -> i32 {
152        self.all_platform_screens().len() as i32
153    }
154
155    /// See [`IScreenImpl::request_screen_details`]; the outcome is cached.
156    fn request_screen_details_core(&self) -> Pin<Box<dyn Future<Output = bool>>> {
157        Box::pin(std::future::ready(true))
158    }
159
160    /// The screen that `top_level` is on.
161    fn screen_from_top_level_core(&self, top_level: &dyn ITopLevelImpl) -> Option<Rc<Screen>> {
162        match top_level.as_window_impl() {
163            Some(window) => ScreenHelper::screen_from_window(window, &IScreenImpl::all_screens(self)),
164            None => None,
165        }
166    }
167
168    /// The screen that contains `point`.
169    fn screen_from_point_core(&self, point: PixelPoint) -> Option<Rc<Screen>> {
170        ScreenHelper::screen_from_point(point, &IScreenImpl::all_screens(self))
171    }
172
173    /// The screen that the largest part of `rect` is on.
174    fn screen_from_rect_core(&self, rect: PixelRect) -> Option<Rc<Screen>> {
175        ScreenHelper::screen_from_rect(rect, &IScreenImpl::all_screens(self))
176    }
177}
178
179/// The non-overridable members of a [`ScreensBaseImpl`].
180pub trait ScreensBaseImplExt: ScreensBaseImpl {
181    /// Invalidates the cached screens and schedules the changed
182    /// notification, so that multiple continuous platform events are
183    /// accumulated into one.
184    ///
185    /// Must be called on the UI thread.
186    fn on_changed(self: &Rc<Self>);
187
188    /// The screen of a key, if the key is one of the current screen keys.
189    fn try_get_screen(&self, key: &Self::Key) -> Option<Rc<Self::Screen>>;
190
191    /// All current screens, as the screen type of the backend.
192    fn all_platform_screens(&self) -> Vec<Rc<Self::Screen>>;
193}
194
195fn ensure_screens<T: ScreensBaseImpl + ?Sized>(this: &T) {
196    let base = this.screens_base();
197    if base.all_screens.borrow().is_some() {
198        return;
199    }
200
201    // This is not synchronized, as it is expected to be called on the UI
202    // thread only.
203    Dispatcher::ui_thread().verify_access();
204
205    let screens = this.get_all_screen_keys();
206
207    let removed_keys: Vec<T::Key> = {
208        let screens_set: HashSet<&T::Key> = screens.iter().collect();
209        let all_screens_by_key = base.all_screens_by_key.borrow();
210        all_screens_by_key.keys().filter(|key| !screens_set.contains(key)).cloned().collect()
211    };
212    for old_screen_key in removed_keys {
213        let removed = base.all_screens_by_key.borrow_mut().remove(&old_screen_key);
214        if let Some(screen) = removed {
215            this.screen_removed(&screen);
216        }
217    }
218
219    let mut temp_screens = Vec::with_capacity(screens.len());
220    for new_screen_key in &screens {
221        let old_screen = base.all_screens_by_key.borrow().get(new_screen_key).cloned();
222        match old_screen {
223            Some(old_screen) => {
224                this.screen_changed(&old_screen);
225                temp_screens.push(old_screen);
226            }
227            None => {
228                let new_screen = this.create_screen_from_key(new_screen_key);
229                this.screen_added(&new_screen);
230                base.all_screens_by_key.borrow_mut().insert(new_screen_key.clone(), new_screen.clone());
231                temp_screens.push(new_screen);
232            }
233        }
234    }
235
236    *base.all_screens.borrow_mut() = Some(temp_screens);
237}
238
239impl<T: ScreensBaseImpl + ?Sized> ScreensBaseImplExt for T {
240    fn on_changed(self: &Rc<Self>) {
241        let base = self.screens_base();
242
243        // Mark cached fields invalid.
244        base.screen_count.set(None);
245        *base.all_screens.borrow_mut() = None;
246
247        // Schedule a delayed job, so we can accumulate multiple continuous
248        // events into one.
249        let previous = base.on_change_operation.take();
250        if let Some(previous) = previous {
251            previous.abort();
252        }
253
254        let this = Rc::downgrade(self);
255        let operation = Dispatcher::ui_thread().invoke_async_local_with_priority(
256            move || {
257                let Some(this) = this.upgrade() else {
258                    return;
259                };
260                let base = this.screens_base();
261
262                // Ensure screens if there is at least one subscriber
263                // already, or at least one screen was previously
264                // materialized, which we need to update now.
265                let has_subscriber = base.changed.borrow().is_some();
266                if has_subscriber || !base.all_screens_by_key.borrow().is_empty() {
267                    ensure_screens(&*this);
268                    let changed = base.changed.borrow().clone();
269                    if let Some(changed) = changed {
270                        changed();
271                    }
272                }
273            },
274            DispatcherPriority::INPUT,
275        );
276        *base.on_change_operation.borrow_mut() = Some(operation);
277    }
278
279    fn try_get_screen(&self, key: &Self::Key) -> Option<Rc<Self::Screen>> {
280        ensure_screens(self);
281        self.screens_base().all_screens_by_key.borrow().get(key).cloned()
282    }
283
284    fn all_platform_screens(&self) -> Vec<Rc<Self::Screen>> {
285        ensure_screens(self);
286        self.screens_base().all_screens.borrow().clone().unwrap_or_default()
287    }
288}
289
290impl<T: ScreensBaseImpl + ?Sized> IScreenImpl for T {
291    fn screen_count(&self) -> i32 {
292        let base = self.screens_base();
293        match base.screen_count.get() {
294            Some(screen_count) => screen_count,
295            None => {
296                let screen_count = self.get_screen_count();
297                base.screen_count.set(Some(screen_count));
298                screen_count
299            }
300        }
301    }
302
303    fn all_screens(&self) -> Vec<Rc<Screen>> {
304        ensure_screens(self);
305        match self.screens_base().all_screens.borrow().as_ref() {
306            Some(all_screens) => {
307                all_screens.iter().map(|screen| (**screen).as_ref().screen().clone()).collect()
308            }
309            None => Vec::new(),
310        }
311    }
312
313    fn changed(&self) -> Option<Rc<dyn Fn()>> {
314        self.screens_base().changed.borrow().clone()
315    }
316
317    fn set_changed(&self, value: Option<Rc<dyn Fn()>>) {
318        let previous = self.screens_base().changed.replace(value);
319        drop(previous);
320    }
321
322    fn screen_from_window(&self, window: &dyn IWindowBaseImpl) -> Option<Rc<Screen>> {
323        IScreenImpl::screen_from_top_level(self, window)
324    }
325
326    fn screen_from_top_level(&self, top_level: &dyn ITopLevelImpl) -> Option<Rc<Screen>> {
327        self.screen_from_top_level_core(top_level)
328    }
329
330    fn screen_from_point(&self, point: PixelPoint) -> Option<Rc<Screen>> {
331        self.screen_from_point_core(point)
332    }
333
334    fn screen_from_rect(&self, rect: PixelRect) -> Option<Rc<Screen>> {
335        self.screen_from_rect_core(rect)
336    }
337
338    fn request_screen_details(&self) -> Pin<Box<dyn Future<Output = bool>>> {
339        let granted = self.screens_base().screen_details_request_granted.clone();
340        if let Some(granted) = granted.get() {
341            return Box::pin(std::future::ready(granted));
342        }
343
344        let request = self.request_screen_details_core();
345        Box::pin(async move {
346            let result = request.await;
347            granted.set(Some(result));
348            result
349        })
350    }
351}