Skip to main content

ferroui_controls/embedding/offscreen/
offscreen_top_level_impl.rs

1use crate::platform::{IPlatformHandle, IPopupImpl, ITopLevelImpl, PlatformThemeVariant};
2use crate::{AcrylicPlatformCompensationLevels, WindowResizeReason, WindowTransparencyLevel};
3use ferroui_base::input::raw::IRawInputEventArgs;
4use ferroui_base::input::{IInputRoot, IMouseDevice};
5use ferroui_base::platform::surfaces::IPlatformRenderSurface;
6use ferroui_base::platform::{ICursorImpl, IOptionalFeatureProvider};
7use ferroui_base::reactive::IDisposable;
8use ferroui_base::rendering::composition::Compositor;
9use ferroui_base::{PixelPoint, Point, Rect, Size};
10use std::any::{Any, TypeId};
11use std::cell::{Cell, RefCell};
12use std::rc::Rc;
13
14/// The abstract and the overridable members of an offscreen top-level
15/// implementation: what a class deriving from
16/// [`OffscreenTopLevelImplBase`] provides.
17///
18/// This API is unstable.
19pub trait OffscreenTopLevelImplOverrides {
20    /// The surfaces the top-level is rendered to.
21    fn surfaces(&self) -> Vec<std::sync::Arc<dyn IPlatformRenderSurface>>;
22
23    /// The mouse device of the top-level.
24    fn mouse_device(&self) -> Rc<dyn IMouseDevice>;
25
26    /// Disposes the implementation. An override calls
27    /// [`OffscreenTopLevelImplBase::base_dispose`], as this does.
28    fn dispose(&self, base: &OffscreenTopLevelImplBase) {
29        base.base_dispose();
30    }
31
32    /// The scaling of the desktop the top-level is on; the render scaling
33    /// by default.
34    fn desktop_scaling(&self, base: &OffscreenTopLevelImplBase) -> f64 {
35        base.render_scaling()
36    }
37
38    /// Converts a point from screen to client coordinates.
39    fn point_to_client(&self, point: PixelPoint) -> Point {
40        point.to_point(1.0)
41    }
42
43    /// Converts a point from client to screen coordinates.
44    fn point_to_screen(&self, point: Point) -> PixelPoint {
45        PixelPoint::from_point(point, 1.0)
46    }
47
48    /// Sets the cursor associated with the top-level; ignored by default.
49    fn set_cursor(&self, _cursor: Option<Rc<dyn ICursorImpl>>) {}
50
51    /// Queries for an optional feature; none by default.
52    fn try_get_feature(&self, _feature_type: TypeId) -> Option<Rc<dyn Any>> {
53        None
54    }
55}
56
57/// The base of the top-level implementations that are not backed by a
58/// window of the platform: the client size and the scaling are set by the
59/// owner, which also feeds the input and provides the surfaces.
60///
61/// This API is unstable.
62pub struct OffscreenTopLevelImplBase {
63    overrides: Rc<dyn OffscreenTopLevelImplOverrides>,
64    scaling: Cell<f64>,
65    client_size: Cell<Size>,
66    input_root: RefCell<Option<Rc<dyn IInputRoot>>>,
67    is_disposed: Cell<bool>,
68    compositor: Rc<Compositor>,
69    input: RefCell<Option<Rc<dyn Fn(Rc<dyn IRawInputEventArgs>)>>>,
70    paint: RefCell<Option<Rc<dyn Fn(Rect)>>>,
71    resized: RefCell<Option<Rc<dyn Fn(Size, WindowResizeReason)>>>,
72    scaling_changed: RefCell<Option<Rc<dyn Fn(f64)>>>,
73    transparency_level_changed: RefCell<Option<Rc<dyn Fn(WindowTransparencyLevel)>>>,
74    closed: RefCell<Option<Rc<dyn Fn()>>>,
75    lost_focus: RefCell<Option<Rc<dyn Fn()>>>,
76}
77
78impl OffscreenTopLevelImplBase {
79    /// Creates the implementation over the members of the deriving class,
80    /// with a compositor of its own.
81    pub fn new(overrides: Rc<dyn OffscreenTopLevelImplOverrides>) -> Rc<Self> {
82        Rc::new(Self {
83            overrides,
84            scaling: Cell::new(1.0),
85            client_size: Cell::new(Size::default()),
86            input_root: RefCell::new(None),
87            is_disposed: Cell::new(false),
88            compositor: Compositor::new(None, false),
89            input: RefCell::new(None),
90            paint: RefCell::new(None),
91            resized: RefCell::new(None),
92            scaling_changed: RefCell::new(None),
93            transparency_level_changed: RefCell::new(None),
94            closed: RefCell::new(None),
95            lost_focus: RefCell::new(None),
96        })
97    }
98
99    /// The members of the deriving class.
100    pub fn overrides(&self) -> &Rc<dyn OffscreenTopLevelImplOverrides> {
101        &self.overrides
102    }
103
104    /// The input root the top-level set.
105    pub fn input_root(&self) -> Option<Rc<dyn IInputRoot>> {
106        self.input_root.borrow().clone()
107    }
108
109    /// Whether the implementation has been disposed.
110    pub fn is_disposed(&self) -> bool {
111        self.is_disposed.get()
112    }
113
114    /// The `Dispose` of the base class.
115    pub fn base_dispose(&self) {
116        self.is_disposed.set(true);
117    }
118
119    /// The compositor of the top-level.
120    pub fn compositor(&self) -> &Rc<Compositor> {
121        &self.compositor
122    }
123
124    /// The client size of the top-level.
125    pub fn client_size(&self) -> Size {
126        self.client_size.get()
127    }
128
129    /// The size of the frame of the top-level: an offscreen top-level has none.
130    pub fn frame_size(&self) -> Option<Size> {
131        None
132    }
133
134    /// Sets the client size and notifies the top-level.
135    pub fn set_client_size(&self, value: Size) {
136        self.client_size.set(value);
137        let resized = self.resized.borrow().clone();
138        if let Some(resized) = resized {
139            resized(value, WindowResizeReason::Unspecified);
140        }
141    }
142
143    /// The scaling factor of the top-level.
144    pub fn render_scaling(&self) -> f64 {
145        self.scaling.get()
146    }
147
148    /// Sets the scaling factor and notifies the top-level.
149    pub fn set_render_scaling(&self, value: f64) {
150        self.scaling.set(value);
151        let scaling_changed = self.scaling_changed.borrow().clone();
152        if let Some(scaling_changed) = scaling_changed {
153            scaling_changed(value);
154        }
155    }
156
157    /// The mouse device of the top-level.
158    pub fn mouse_device(&self) -> Rc<dyn IMouseDevice> {
159        self.overrides.mouse_device()
160    }
161}
162
163impl IDisposable for OffscreenTopLevelImplBase {
164    fn dispose(&self) {
165        self.overrides.dispose(self);
166    }
167}
168
169impl IOptionalFeatureProvider for OffscreenTopLevelImplBase {
170    fn try_get_feature(&self, feature_type: TypeId) -> Option<Rc<dyn Any>> {
171        self.overrides.try_get_feature(feature_type)
172    }
173}
174
175impl ITopLevelImpl for OffscreenTopLevelImplBase {
176    fn desktop_scaling(&self) -> f64 {
177        self.overrides.desktop_scaling(self)
178    }
179
180    fn handle(&self) -> Option<Rc<dyn IPlatformHandle>> {
181        None
182    }
183
184    fn client_size(&self) -> Size {
185        self.client_size.get()
186    }
187
188    fn render_scaling(&self) -> f64 {
189        self.scaling.get()
190    }
191
192    fn surfaces(&self) -> Vec<std::sync::Arc<dyn IPlatformRenderSurface>> {
193        self.overrides.surfaces()
194    }
195
196    fn compositor(&self) -> Option<Rc<Compositor>> {
197        Some(self.compositor.clone())
198    }
199
200    fn input(&self) -> Option<Rc<dyn Fn(Rc<dyn IRawInputEventArgs>)>> {
201        self.input.borrow().clone()
202    }
203
204    fn set_input(&self, value: Option<Rc<dyn Fn(Rc<dyn IRawInputEventArgs>)>>) {
205        *self.input.borrow_mut() = value;
206    }
207
208    fn paint(&self) -> Option<Rc<dyn Fn(Rect)>> {
209        self.paint.borrow().clone()
210    }
211
212    fn set_paint(&self, value: Option<Rc<dyn Fn(Rect)>>) {
213        *self.paint.borrow_mut() = value;
214    }
215
216    fn resized(&self) -> Option<Rc<dyn Fn(Size, WindowResizeReason)>> {
217        self.resized.borrow().clone()
218    }
219
220    fn set_resized(&self, value: Option<Rc<dyn Fn(Size, WindowResizeReason)>>) {
221        *self.resized.borrow_mut() = value;
222    }
223
224    fn scaling_changed(&self) -> Option<Rc<dyn Fn(f64)>> {
225        self.scaling_changed.borrow().clone()
226    }
227
228    fn set_scaling_changed(&self, value: Option<Rc<dyn Fn(f64)>>) {
229        *self.scaling_changed.borrow_mut() = value;
230    }
231
232    fn transparency_level_changed(&self) -> Option<Rc<dyn Fn(WindowTransparencyLevel)>> {
233        self.transparency_level_changed.borrow().clone()
234    }
235
236    fn set_transparency_level_changed(&self, value: Option<Rc<dyn Fn(WindowTransparencyLevel)>>) {
237        *self.transparency_level_changed.borrow_mut() = value;
238    }
239
240    fn set_input_root(&self, input_root: Rc<dyn IInputRoot>) {
241        *self.input_root.borrow_mut() = Some(input_root);
242    }
243
244    fn point_to_client(&self, point: PixelPoint) -> Point {
245        self.overrides.point_to_client(point)
246    }
247
248    fn point_to_screen(&self, point: Point) -> PixelPoint {
249        self.overrides.point_to_screen(point)
250    }
251
252    fn set_cursor(&self, cursor: Option<Rc<dyn ICursorImpl>>) {
253        self.overrides.set_cursor(cursor)
254    }
255
256    fn closed(&self) -> Option<Rc<dyn Fn()>> {
257        self.closed.borrow().clone()
258    }
259
260    fn set_closed(&self, value: Option<Rc<dyn Fn()>>) {
261        *self.closed.borrow_mut() = value;
262    }
263
264    fn lost_focus(&self) -> Option<Rc<dyn Fn()>> {
265        self.lost_focus.borrow().clone()
266    }
267
268    fn set_lost_focus(&self, value: Option<Rc<dyn Fn()>>) {
269        *self.lost_focus.borrow_mut() = value;
270    }
271
272    fn create_popup(&self) -> Option<Rc<dyn IPopupImpl>> {
273        None
274    }
275
276    fn set_transparency_level_hint(&self, _transparency_levels: &[WindowTransparencyLevel]) {}
277
278    fn transparency_level(&self) -> WindowTransparencyLevel {
279        WindowTransparencyLevel::none()
280    }
281
282    fn acrylic_compensation_levels(&self) -> AcrylicPlatformCompensationLevels {
283        AcrylicPlatformCompensationLevels::new(1.0, 1.0, 1.0)
284    }
285
286    fn set_frame_theme_variant(&self, _theme_variant: Option<PlatformThemeVariant>) {}
287
288    fn as_any(&self) -> &dyn Any {
289        self
290    }
291}