1use crate::clipboard_data_transfer::ClipboardDataTransfer;
6use crate::clipboard_read_session::ClipboardReadSession;
7use crate::cursor::FerroNativeCursor;
8use crate::deferred_framebuffer::DeferredFramebuffer;
9use crate::ferro_native_drag_source::{data_transfer_from_handle, free_data_transfer_handle};
10use crate::ferro_native_text_input_method::FerroNativeTextInputMethod;
11use crate::frn_automation_peer::FrnAutomationPeer;
12use crate::helpers::*;
13use crate::interop::*;
14use crate::ferro_native_gl_platform_graphics::GlPlatformSurface;
15use crate::metal::MetalPlatformSurface;
16use crate::native_control_host_impl::NativeControlHostImpl;
17use crate::platform_behavior_inhibition::PlatformBehaviorInhibition;
18use ferroui_base::input::platform::{IClipboard, PlatformDataTransfer};
19use ferroui_base::input::raw::{
20 IDragDropDevice, RawDragEvent, IRawInputEventArgs, RawKeyEventArgs, RawMouseWheelEventArgs, RawPointerEventArgs, RawPointerEventType,
21 RawPointerGestureEventArgs, RawPointerPoint, RawTextInputEventArgs,
22};
23use ferroui_base::input::text_input::ITextInputMethodImpl;
24use ferroui_base::input::{
25 DragDropEffects, IDataTransfer, CaptureSource, IInputDevice, IInputRoot, IKeyboardDevice, IPointer, KeyDeviceType, MouseDevice, PenDevice,
26 StandardCursorType,
27};
28use ferroui_base::platform::surfaces::{
29 FramebufferLockProperties, IFramebufferPlatformSurface, IFramebufferRenderTarget, IPlatformRenderSurface,
30 IPlatformRenderSurfaceRenderTarget,
31};
32use ferroui_base::platform::{
33 ICursorFactory, ICursorImpl, ILockedFramebuffer, IMacOSTopLevelPlatformHandle, IPlatformBehaviorInhibition,
34 RenderTargetSceneInfo,
35};
36use ferroui_base::reactive::IDisposable;
37use ferroui_base::threading::{Dispatcher, DispatcherPriority};
38use ferroui_base::utilities::ThreadBound;
39use ferroui_base::{FerroLocator, LocatorExtensions, PixelPoint, Point, Rect, Ref, Size, Vector, Visual};
40use ferroui_controls::automation::peers::{AutomationPeer, ControlAutomationPeer};
41use ferroui_controls::platform::{
42 INativeControlHostImpl, IPlatformHandle, IPopupImpl, IScreenImpl, ITopLevelImpl, PlatformThemeVariant,
43};
44use ferroui_controls::{
45 AcrylicPlatformCompensationLevels, Control, TopLevel, WindowResizeReason, WindowTransparencyLevel,
46};
47use ferroui_microcom::{ComPtr, HResult};
48use std::any::{Any, TypeId};
49use std::cell::{Cell, RefCell};
50use std::ffi::{c_void, CStr};
51use std::rc::Rc;
52use std::sync::atomic::{AtomicBool, Ordering};
53use std::sync::{Arc, Mutex, MutexGuard};
54
55pub struct MacOSTopLevelHandle {
61 native: ComPtr<IFrnTopLevel>,
62 window_base: Option<ComPtr<IFrnWindowBase>>,
63 handle: isize,
64 handle_descriptor: &'static str,
65}
66
67impl MacOSTopLevelHandle {
68 pub(crate) fn from_top_level(native: ComPtr<IFrnTopLevel>) -> Rc<Self> {
70 let mut this = Self { native, window_base: None, handle: 0, handle_descriptor: "NSView" };
71 this.handle = this.ns_view();
72 Rc::new(this)
73 }
74
75 pub(crate) fn from_window_base(native: ComPtr<IFrnWindowBase>) -> Rc<Self> {
77 let top_level = ComPtr::<IFrnTopLevel>::from_ref(&native);
78 let mut this =
79 Self { native: top_level, window_base: Some(native), handle: 0, handle_descriptor: "NSWindow" };
80 this.handle = this.ns_window();
81 Rc::new(this)
82 }
83
84 pub(crate) fn native(&self) -> &ComPtr<IFrnTopLevel> {
85 &self.native
86 }
87
88 pub(crate) fn window_base(&self) -> Option<&ComPtr<IFrnWindowBase>> {
89 self.window_base.as_ref()
90 }
91}
92
93impl IMacOSTopLevelPlatformHandle for MacOSTopLevelHandle {
94 fn ns_view(&self) -> isize {
95 self.native.obtain_ns_view_handle().check() as isize
96 }
97
98 fn get_ns_view_retained(&self) -> isize {
99 self.native.obtain_ns_view_handle_retained().check() as isize
100 }
101
102 fn ns_window(&self) -> isize {
103 self.window_base.as_ref().map_or(0, |native| native.obtain_ns_window_handle().check() as isize)
104 }
105
106 fn get_ns_window_retained(&self) -> isize {
107 self.window_base.as_ref().map_or(0, |native| native.obtain_ns_window_handle_retained().check() as isize)
108 }
109}
110
111impl IPlatformHandle for MacOSTopLevelHandle {
112 fn handle(&self) -> isize {
113 self.handle
114 }
115
116 fn handle_descriptor(&self) -> Option<&str> {
117 Some(self.handle_descriptor)
118 }
119
120 fn as_any(&self) -> &dyn Any {
121 self
122 }
123}
124
125pub(crate) trait TopLevelParent: 'static {
129 fn top_level(&self) -> &Rc<TopLevelImpl>;
131
132 fn dispose_top_level(&self) {
134 self.top_level().dispose();
135 }
136
137 fn chrome_hit_test(&self, _e: &RawPointerEventArgs) -> bool {
140 false
141 }
142
143 fn create_popup_core(&self) -> Option<Rc<dyn IPopupImpl>>;
145
146 fn set_frame_theme_variant_core(&self, _theme_variant: Option<PlatformThemeVariant>) {
147 }
149
150 fn try_get_feature_core(&self, feature_type: TypeId) -> Option<Rc<dyn Any>> {
151 self.top_level().try_get_feature(feature_type)
152 }
153}
154
155pub struct TopLevelImpl {
162 input_root: RefCell<Option<Rc<dyn IInputRoot>>>,
163 native_control_host: RefCell<Option<Rc<NativeControlHostImpl>>>,
164 platform_behavior_inhibition: RefCell<Option<Rc<PlatformBehaviorInhibition>>>,
165 input_method: RefCell<Option<Rc<FerroNativeTextInputMethod>>>,
166
167 mouse: Rc<MouseDevice>,
168 pen: Rc<PenDevice>,
169
170 keyboard: Option<Rc<dyn IKeyboardDevice>>,
171 cursor_factory: Option<Rc<dyn ICursorFactory>>,
172
173 factory: ComPtr<IFerroNativeFactory>,
174
175 saved_logical_size: Cell<Size>,
176 saved_scaling: Cell<f64>,
177 transparency_level: Cell<WindowTransparencyLevel>,
178
179 handle: RefCell<Option<Rc<MacOSTopLevelHandle>>>,
180
181 surfaces: SharedSurfaces,
184 gl_surface: RefCell<Option<Arc<GlPlatformSurface>>>,
185 metal_surface: RefCell<Option<Arc<MetalPlatformSurface>>>,
186 framebuffer_surface: RefCell<Option<Arc<TopLevelFramebufferSurface>>>,
187
188 input: RefCell<Option<Rc<dyn Fn(Rc<dyn IRawInputEventArgs>)>>>,
189 paint: RefCell<Option<Rc<dyn Fn(Rect)>>>,
190 resized: RefCell<Option<Rc<dyn Fn(Size, WindowResizeReason)>>>,
191 scaling_changed: RefCell<Option<Rc<dyn Fn(f64)>>>,
192 transparency_level_changed: RefCell<Option<Rc<dyn Fn(WindowTransparencyLevel)>>>,
193 closed: RefCell<Option<Rc<dyn Fn()>>>,
194 lost_focus: RefCell<Option<Rc<dyn Fn()>>>,
195}
196
197impl TopLevelImpl {
198 pub(crate) fn new(factory: ComPtr<IFerroNativeFactory>) -> Rc<TopLevelImpl> {
199 let locator = FerroLocator::current();
200 Rc::new(TopLevelImpl {
201 input_root: RefCell::new(None),
202 native_control_host: RefCell::new(None),
203 platform_behavior_inhibition: RefCell::new(None),
204 input_method: RefCell::new(None),
205 mouse: MouseDevice::primary(),
206 pen: PenDevice::new(false),
207 keyboard: locator.get_service::<dyn IKeyboardDevice>(),
208 cursor_factory: locator.get_service::<dyn ICursorFactory>(),
209 factory,
210 saved_logical_size: Cell::new(Size::default()),
211 saved_scaling: Cell::new(0.0),
212 transparency_level: Cell::new(WindowTransparencyLevel::none()),
213 handle: RefCell::new(None),
214 surfaces: Arc::new(Mutex::new(Vec::new())),
215 gl_surface: RefCell::new(None),
216 metal_surface: RefCell::new(None),
217 framebuffer_surface: RefCell::new(None),
218 input: RefCell::new(None),
219 paint: RefCell::new(None),
220 resized: RefCell::new(None),
221 scaling_changed: RefCell::new(None),
222 transparency_level_changed: RefCell::new(None),
223 closed: RefCell::new(None),
224 lost_focus: RefCell::new(None),
225 })
226 }
227
228 pub(crate) fn init(&self, handle: Rc<MacOSTopLevelHandle>) {
229 let native = handle.native().clone();
230 *self.handle.borrow_mut() = Some(handle);
231 self.saved_logical_size.set(self.client_size());
232 self.saved_scaling.set(native.get_scaling().check());
233 *self.native_control_host.borrow_mut() = Some(NativeControlHostImpl::new(native.create_native_control_host().check()));
234 *self.platform_behavior_inhibition.borrow_mut() = self
235 .factory
236 .create_platform_behavior_inhibition()
237 .check()
238 .map(|native| Rc::new(PlatformBehaviorInhibition::new(native)));
239
240 *self.input_method.borrow_mut() = Some(FerroNativeTextInputMethod::new(&native));
241 let gl_surface = GlPlatformSurface::new(native.clone());
242 let metal_surface = MetalPlatformSurface::new(native.clone());
243 let framebuffer_surface =
244 TopLevelFramebufferSurface::new(native, self.saved_logical_size.get(), self.saved_scaling.get());
245 *self.gl_surface.borrow_mut() = Some(gl_surface.clone());
246 *self.metal_surface.borrow_mut() = Some(metal_surface.clone());
247 *self.framebuffer_surface.borrow_mut() = Some(framebuffer_surface.clone());
248 let gl_surface: Arc<dyn IPlatformRenderSurface> = gl_surface;
249 let metal_surface: Arc<dyn IPlatformRenderSurface> = metal_surface;
250 let framebuffer_surface: Arc<dyn IPlatformRenderSurface> = framebuffer_surface;
251 *lock_shared(&self.surfaces) = vec![gl_surface, metal_surface, framebuffer_surface];
252 }
253
254 fn update_surface_metrics(&self) {
257 if let Some(surface) = self.framebuffer_surface.borrow().as_ref() {
258 surface.set_metrics(self.saved_logical_size.get(), self.saved_scaling.get());
259 }
260 }
261
262 pub(crate) fn find_for_visual(visual: &Visual) -> Option<Rc<TopLevelImpl>> {
264 let platform_impl = TopLevel::get_top_level(Some(visual))?.platform_impl()?;
265 top_level_of(&*platform_impl).cloned()
266 }
267
268 pub(crate) fn begin_dragging_session(
269 &self,
270 effects: FrnDragDropEffects,
271 point: FrnPoint,
272 source: &IFrnClipboardDataSource,
273 callback: &IFrnDndResultCallback,
274 source_handle: *mut c_void,
275 ) {
276 match self.native() {
277 Some(native) => unsafe {
281 native.begin_drag_and_drop_operation(effects, point, Some(source), Some(callback), source_handle)
282 }
283 .check(),
284 None => unsafe { free_data_transfer_handle(source_handle) },
287 }
288 }
289
290 pub fn input_method(&self) -> Option<Rc<FerroNativeTextInputMethod>> {
292 self.input_method.borrow().clone()
293 }
294
295 pub(crate) fn set_input_method(&self, value: Option<Rc<FerroNativeTextInputMethod>>) {
296 let old = self.input_method.replace(value);
297 drop(old);
298 }
299
300 fn on_drag_event(
301 &self,
302 type_: FrnDragEventType,
303 position: FrnPoint,
304 modifiers: FrnInputModifiers,
305 effects: FrnDragDropEffects,
306 clipboard: Option<&IFrnClipboard>,
307 data_transfer_handle: *mut c_void,
308 ) -> FrnDragDropEffects {
309 let Some(device) = FerroLocator::current().get_service::<dyn IDragDropDevice>() else {
310 return FrnDragDropEffects::None;
311 };
312
313 let Some(input_root) = self.input_root() else {
314 return FrnDragDropEffects::None;
315 };
316
317 let data_transfer = unsafe { data_transfer_from_handle(data_transfer_handle) };
320
321 let clipboard_data_transfer = clipboard.map(|clipboard| {
323 let change_count = clipboard.get_change_count().check();
324 ClipboardDataTransfer::new(ClipboardReadSession::new(ComPtr::from_ref(clipboard), change_count))
325 });
326 struct DisposeOnExit(Option<Rc<PlatformDataTransfer>>);
327 impl Drop for DisposeOnExit {
328 fn drop(&mut self) {
329 if let Some(data_transfer) = self.0.take() {
330 data_transfer.dispose();
331 }
332 }
333 }
334 let _dispose = DisposeOnExit(clipboard_data_transfer.clone());
335
336 let data_transfer: Rc<dyn IDataTransfer> = match (data_transfer, clipboard_data_transfer) {
337 (Some(data_transfer), _) => data_transfer,
338 (None, Some(clipboard_data_transfer)) => clipboard_data_transfer,
339 (None, None) => return FrnDragDropEffects::None,
341 };
342
343 let args = Rc::new(RawDragEvent::new(
344 device,
345 to_raw_drag_event_type(type_),
346 input_root,
347 to_ferro_point(position),
348 data_transfer,
349 DragDropEffects::from_bits_retain(effects.0),
350 to_raw_input_modifiers(modifiers),
351 ));
352 self.invoke_input(args.clone());
353 FrnDragDropEffects(args.effects().bits())
354 }
355
356 pub(crate) fn factory(&self) -> &ComPtr<IFerroNativeFactory> {
357 &self.factory
358 }
359
360 pub(crate) fn input_root(&self) -> Option<Rc<dyn IInputRoot>> {
361 self.input_root.borrow().clone()
362 }
363
364 pub(crate) fn mouse(&self) -> &Rc<MouseDevice> {
365 &self.mouse
366 }
367
368 pub fn desktop_scaling(&self) -> f64 {
369 1.0
370 }
371
372 pub fn native(&self) -> Option<ComPtr<IFrnTopLevel>> {
374 self.handle.borrow().as_ref().map(|handle| handle.native().clone())
375 }
376
377 pub(crate) fn native_window_base(&self) -> Option<ComPtr<IFrnWindowBase>> {
380 self.handle.borrow().as_ref().and_then(|handle| handle.window_base().cloned())
381 }
382
383 pub fn mac_os_handle(&self) -> Option<Rc<MacOSTopLevelHandle>> {
385 self.handle.borrow().clone()
386 }
387
388 pub fn handle(&self) -> Option<Rc<dyn IPlatformHandle>> {
389 self.mac_os_handle().map(|handle| handle as Rc<dyn IPlatformHandle>)
390 }
391
392 pub fn client_size(&self) -> Size {
393 match self.native() {
394 None => Size::default(),
395 Some(native) => to_ferro_size(native.get_client_size().check()),
396 }
397 }
398
399 pub fn render_scaling(&self) -> f64 {
400 self.saved_scaling.get()
401 }
402
403 pub fn surfaces(&self) -> Vec<Arc<dyn IPlatformRenderSurface>> {
404 lock_shared(&self.surfaces).clone()
405 }
406
407 pub fn render_surfaces(&self) -> Arc<dyn Fn() -> Vec<Arc<dyn IPlatformRenderSurface>> + Send + Sync> {
417 let surfaces = self.surfaces.clone();
418 Arc::new(move || lock_shared(&surfaces).clone())
419 }
420
421 pub fn transparency_level(&self) -> WindowTransparencyLevel {
422 self.transparency_level.get()
423 }
424
425 fn set_transparency_level(&self, value: WindowTransparencyLevel) {
426 if self.transparency_level.get() != value {
427 self.transparency_level.set(value);
428 let changed = self.transparency_level_changed.borrow().clone();
429 if let Some(changed) = changed {
430 changed(value);
431 }
432 }
433 }
434
435 pub fn acrylic_compensation_levels(&self) -> AcrylicPlatformCompensationLevels {
436 AcrylicPlatformCompensationLevels::new(1.0, 0.0, 0.0)
437 }
438
439 fn invoke_input(&self, args: Rc<dyn IRawInputEventArgs>) {
440 let input = self.input.borrow().clone();
441 if let Some(input) = input {
442 input(args);
443 }
444 }
445
446 fn run_jobs_before_input() {
448 Dispatcher::ui_thread()
449 .run_jobs(Some(DispatcherPriority::from_value(DispatcherPriority::INPUT.value() + 1)));
450 }
451
452 pub fn get_automation_peer(&self) -> Option<Ref<AutomationPeer>> {
455 let focus_root = self.input_root()?.focus_root();
456 focus_root.cast::<Control>().map(|c| ControlAutomationPeer::create_peer_for_element(&c))
457 }
458
459 pub fn raw_text_input_event(&self, time_stamp: u64, text: &str) -> bool {
460 let Some(input_root) = self.input_root() else {
461 return false;
462 };
463
464 let Some(keyboard) = self.keyboard.clone() else {
465 return false;
466 };
467
468 Self::run_jobs_before_input();
469
470 let device: Rc<dyn IInputDevice> = keyboard;
471 let args = Rc::new(RawTextInputEventArgs::new(device, time_stamp, input_root, text));
472
473 self.invoke_input(args.clone());
474
475 args.handled()
476 }
477
478 pub fn raw_key_event(
479 &self,
480 type_: FrnRawKeyEventType,
481 time_stamp: u64,
482 modifiers: FrnInputModifiers,
483 key: FrnKey,
484 physical_key: FrnPhysicalKey,
485 key_symbol: Option<String>,
486 ) -> bool {
487 let Some(input_root) = self.input_root() else {
488 return false;
489 };
490
491 let Some(keyboard) = self.keyboard.clone() else {
492 return false;
493 };
494
495 Self::run_jobs_before_input();
496
497 let device: Rc<dyn IInputDevice> = keyboard;
498 let args = Rc::new(RawKeyEventArgs::new(
499 device,
500 time_stamp,
501 input_root,
502 to_raw_key_event_type(type_),
503 to_key(key),
504 to_raw_input_modifiers(modifiers),
505 to_physical_key(physical_key),
506 key_symbol,
507 KeyDeviceType::Keyboard,
508 ));
509
510 self.invoke_input(args.clone());
511
512 args.handled()
513 }
514
515 #[allow(clippy::too_many_arguments)]
516 pub(crate) fn raw_mouse_event(
517 &self,
518 type_: FrnRawMouseEventType,
519 device_type: FrnPointerDeviceType,
520 time_stamp: u64,
521 modifiers: FrnInputModifiers,
522 point: FrnPoint,
523 delta: FrnVector,
524 pressure: f32,
525 x_tilt: f32,
526 y_tilt: f32,
527 chrome_hit_test: &dyn Fn(&RawPointerEventArgs) -> bool,
528 ) {
529 let Some(input_root) = self.input_root() else {
530 return;
531 };
532
533 Self::run_jobs_before_input();
534
535 let mouse: Rc<dyn IInputDevice> = self.mouse.clone();
536 let position = to_ferro_point(point);
537 let delta = Vector::new(delta.x, delta.y);
538 let modifiers = to_raw_input_modifiers(modifiers);
539
540 if type_ == FrnRawMouseEventType::Wheel {
541 self.invoke_input(Rc::new(RawMouseWheelEventArgs::new(
542 mouse, time_stamp, input_root, position, delta, modifiers,
543 )));
544 } else if type_ == FrnRawMouseEventType::Magnify {
545 self.invoke_input(Rc::new(RawPointerGestureEventArgs::new(
546 mouse,
547 time_stamp,
548 input_root,
549 RawPointerEventType::Magnify,
550 position,
551 delta,
552 modifiers,
553 )));
554 } else if type_ == FrnRawMouseEventType::Rotate {
555 self.invoke_input(Rc::new(RawPointerGestureEventArgs::new(
556 mouse,
557 time_stamp,
558 input_root,
559 RawPointerEventType::Rotate,
560 position,
561 delta,
562 modifiers,
563 )));
564 } else if type_ == FrnRawMouseEventType::Swipe {
565 self.invoke_input(Rc::new(RawPointerGestureEventArgs::new(
566 mouse,
567 time_stamp,
568 input_root,
569 RawPointerEventType::Swipe,
570 position,
571 delta,
572 modifiers,
573 )));
574 } else {
575 let Some(type_) = to_raw_pointer_event_type(type_) else {
577 return;
578 };
579 let device: Rc<dyn IInputDevice> =
580 if device_type == FrnPointerDeviceType::Pen { self.pen.clone() } else { mouse };
581 let mut raw_point = RawPointerPoint::new();
582 raw_point.position = position;
583 raw_point.pressure = pressure;
584 raw_point.x_tilt = x_tilt;
585 raw_point.y_tilt = y_tilt;
586 let e =
587 Rc::new(RawPointerEventArgs::with_point(device, time_stamp, input_root, type_, raw_point, modifiers));
588
589 if !chrome_hit_test(&e) {
590 self.invoke_input(e);
591 }
592 }
593 }
594
595 pub fn invalidate(&self) {
596 if let Some(native) = self.native() {
597 native.invalidate().check();
598 }
599 }
600
601 pub fn set_input_root(&self, input_root: Rc<dyn IInputRoot>) {
602 let old = self.input_root.replace(Some(input_root));
603 drop(old);
604 }
605
606 pub fn point_to_client(&self, point: PixelPoint) -> Point {
607 match self.native() {
608 Some(native) => to_ferro_point(native.point_to_client(pixel_point_to_frn_point(point)).check()),
609 None => Point::default(),
610 }
611 }
612
613 pub fn point_to_screen(&self, point: Point) -> PixelPoint {
614 match self.native() {
615 Some(native) => to_ferro_pixel_point(native.point_to_screen(to_frn_point(point)).check()),
616 None => PixelPoint::default(),
617 }
618 }
619
620 pub fn set_cursor(&self, cursor: Option<Rc<dyn ICursorImpl>>) {
621 let Some(native) = self.native() else {
622 return;
623 };
624
625 let native_cursor = match cursor.as_deref().and_then(FerroNativeCursor::from_cursor_impl) {
626 Some(new_cursor) => new_cursor.cursor(),
627 None => self.cursor_factory.as_ref().and_then(|cursor_factory| {
628 let arrow = cursor_factory.get_cursor(StandardCursorType::Arrow);
629 FerroNativeCursor::from_cursor_impl(&*arrow).and_then(FerroNativeCursor::cursor)
630 }),
631 };
632 native.set_cursor(native_cursor.as_deref()).check();
633 }
634
635 pub fn set_transparency_level_hint(&self, transparency_levels: &[WindowTransparencyLevel]) {
636 for &level in transparency_levels {
637 let mut mode = None;
638
639 if level == WindowTransparencyLevel::none() {
640 mode = Some(FrnWindowTransparencyMode::Opaque);
641 }
642 if level == WindowTransparencyLevel::transparent() {
643 mode = Some(FrnWindowTransparencyMode::Transparent);
644 } else if level == WindowTransparencyLevel::acrylic_blur() {
645 mode = Some(FrnWindowTransparencyMode::Blur);
646 }
647
648 if let Some(mode) = mode {
649 if level != self.transparency_level() {
650 if let Some(native) = self.native() {
651 native.set_transparency_mode(mode).check();
652 }
653 self.set_transparency_level(level);
654 return;
655 }
656 }
657 }
658
659 if self.transparency_level() != WindowTransparencyLevel::none() {
661 if let Some(native) = self.native() {
662 native.set_transparency_mode(FrnWindowTransparencyMode::Opaque).check();
663 }
664 self.set_transparency_level(WindowTransparencyLevel::none());
665 }
666 }
667
668 pub fn try_get_feature(&self, feature_type: TypeId) -> Option<Rc<dyn Any>> {
673 if feature_type == TypeId::of::<dyn ITextInputMethodImpl>() {
674 let input_method: Rc<dyn ITextInputMethodImpl> = self.input_method()?;
675 return Some(Rc::new(input_method));
676 }
677
678 if feature_type == TypeId::of::<dyn INativeControlHostImpl>() {
679 let native_control_host: Rc<dyn INativeControlHostImpl> = self.native_control_host.borrow().clone()?;
680 return Some(Rc::new(native_control_host));
681 }
682
683 if feature_type == TypeId::of::<dyn IClipboard>() {
684 let clipboard = FerroLocator::current().get_required_service::<dyn IClipboard>();
685 return Some(Rc::new(clipboard));
686 }
687
688 if feature_type == TypeId::of::<dyn IPlatformBehaviorInhibition>() {
689 let inhibition: Rc<dyn IPlatformBehaviorInhibition> = self.platform_behavior_inhibition.borrow().clone()?;
690 return Some(Rc::new(inhibition));
691 }
692
693 if feature_type == TypeId::of::<dyn IScreenImpl>() {
694 let screens = FerroLocator::current().get_required_service::<dyn IScreenImpl>();
695 return Some(Rc::new(screens));
696 }
697
698 None
699 }
700
701 pub(crate) fn screens(&self) -> Rc<dyn IScreenImpl> {
703 FerroLocator::current().get_required_service::<dyn IScreenImpl>()
704 }
705
706 pub(crate) fn dispose(&self) {
707 lock_shared(&self.surfaces).clear();
711 let gl_surface = self.gl_surface.borrow_mut().take();
712 if let Some(gl_surface) = gl_surface {
713 gl_surface.close();
714 }
715 let metal_surface = self.metal_surface.borrow_mut().take();
716 if let Some(metal_surface) = metal_surface {
717 metal_surface.close();
718 }
719 let framebuffer_surface = self.framebuffer_surface.borrow_mut().take();
720 if let Some(framebuffer_surface) = framebuffer_surface {
721 framebuffer_surface.close();
722 }
723
724 let handle = self.handle.borrow_mut().take();
725 drop(handle);
726
727 let native_control_host = self.native_control_host.borrow_mut().take();
728 if let Some(native_control_host) = native_control_host {
729 native_control_host.dispose();
730 }
731 }
732
733 pub fn native_control_host(&self) -> Option<Rc<dyn INativeControlHostImpl>> {
736 let native_control_host: Rc<dyn INativeControlHostImpl> = self.native_control_host.borrow().clone()?;
737 Some(native_control_host)
738 }
739
740 fn on_paint(&self) {
743 Dispatcher::ui_thread().run_jobs(Some(DispatcherPriority::UI_THREAD_RENDER));
744 let s = self.client_size();
745 let paint = self.paint.borrow().clone();
746 if let Some(paint) = paint {
747 paint(Rect::new(0.0, 0.0, s.width, s.height));
748 }
749 }
750
751 fn on_resized(&self, size: &FrnSize, reason: FrnPlatformResizeReason) {
752 if self.native().is_none() {
753 return;
754 }
755
756 let s = Size::new(size.width, size.height);
757 self.saved_logical_size.set(s);
758 self.update_surface_metrics();
759 let resized = self.resized.borrow().clone();
760 if let Some(resized) = resized {
761 resized(s, to_window_resize_reason(reason));
762 }
763 }
764
765 fn on_scaling_changed(&self, scaling: f64) {
766 self.saved_scaling.set(scaling);
767 self.update_surface_metrics();
768 let scaling_changed = self.scaling_changed.borrow().clone();
769 if let Some(scaling_changed) = scaling_changed {
770 scaling_changed(scaling);
771 }
772 }
773
774 fn on_lost_focus(&self) {
775 let lost_focus = self.lost_focus.borrow().clone();
776 if let Some(lost_focus) = lost_focus {
777 lost_focus();
778 }
779
780 let pointer = self.mouse.pointer();
783 let Some(captured) = pointer.captured() else {
784 return;
785 };
786
787 let captured: Ref<Visual> = captured.upcast();
788 if pointer.capture_source() == CaptureSource::Implicit
789 && Self::find_for_visual(&captured).is_some_and(|owner| std::ptr::eq(&*owner, self))
790 {
791 self.mouse.platform_capture_lost();
792 }
793 }
794}
795
796macro_rules! callback_property {
797 ($getter:ident, $setter:ident, $field:ident, $ty:ty) => {
798 pub fn $getter(&self) -> Option<Rc<$ty>> {
799 self.$field.borrow().clone()
800 }
801
802 pub fn $setter(&self, value: Option<Rc<$ty>>) {
803 let old = self.$field.replace(value);
804 drop(old);
805 }
806 };
807}
808pub(crate) use callback_property;
809
810impl TopLevelImpl {
811 callback_property!(input, set_input, input, dyn Fn(Rc<dyn IRawInputEventArgs>));
812 callback_property!(paint, set_paint, paint, dyn Fn(Rect));
813 callback_property!(resized, set_resized, resized, dyn Fn(Size, WindowResizeReason));
814 callback_property!(scaling_changed, set_scaling_changed, scaling_changed, dyn Fn(f64));
815 callback_property!(
816 transparency_level_changed,
817 set_transparency_level_changed,
818 transparency_level_changed,
819 dyn Fn(WindowTransparencyLevel)
820 );
821 callback_property!(closed, set_closed, closed, dyn Fn());
822 callback_property!(lost_focus, set_lost_focus, lost_focus, dyn Fn());
823}
824
825type SharedSurfaces = Arc<Mutex<Vec<Arc<dyn IPlatformRenderSurface>>>>;
827
828fn lock_shared<T>(mutex: &Mutex<T>) -> MutexGuard<'_, T> {
832 mutex.lock().unwrap_or_else(|e| e.into_inner())
833}
834
835pub(crate) struct SurfaceTopLevel(ThreadBound<RefCell<Option<ComPtr<IFrnTopLevel>>>>);
847
848impl SurfaceTopLevel {
849 pub(crate) fn new(native: ComPtr<IFrnTopLevel>) -> Self {
851 Self(ThreadBound::new(RefCell::new(Some(native))))
852 }
853
854 pub(crate) fn get(&self) -> Option<ComPtr<IFrnTopLevel>> {
857 if !self.0.is_on_thread() {
858 return None;
859 }
860 self.0.get().borrow().clone()
861 }
862
863 pub(crate) fn release(&self) {
867 if !self.0.is_on_thread() {
868 return;
869 }
870 let native = self.0.get().borrow_mut().take();
871 drop(native);
872 }
873}
874
875struct FramebufferSurfaceState {
878 sync_root: Mutex<()>,
883 alive: AtomicBool,
886 metrics: Mutex<(Size, f64)>,
888}
889
890impl FramebufferSurfaceState {
891 fn new(logical_size: Size, scaling: f64) -> Arc<Self> {
892 Arc::new(Self {
893 sync_root: Mutex::new(()),
894 alive: AtomicBool::new(true),
895 metrics: Mutex::new((logical_size, scaling)),
896 })
897 }
898}
899
900pub struct TopLevelFramebufferSurface {
907 top_level: SurfaceTopLevel,
908 state: Arc<FramebufferSurfaceState>,
909}
910
911impl TopLevelFramebufferSurface {
912 pub(crate) fn new(native: ComPtr<IFrnTopLevel>, logical_size: Size, scaling: f64) -> Arc<Self> {
913 Arc::new(Self {
914 top_level: SurfaceTopLevel::new(native),
915 state: FramebufferSurfaceState::new(logical_size, scaling),
916 })
917 }
918
919 pub(crate) fn set_metrics(&self, logical_size: Size, scaling: f64) {
921 *lock_shared(&self.state.metrics) = (logical_size, scaling);
922 }
923
924 pub(crate) fn close(&self) {
927 self.state.alive.store(false, Ordering::Release);
930 self.top_level.release();
931 }
932}
933
934impl IPlatformRenderSurface for TopLevelFramebufferSurface {
935 fn is_ready(&self) -> bool {
942 self.top_level.get().is_some()
943 }
944
945 fn as_framebuffer_surface(&self) -> Option<&dyn IFramebufferPlatformSurface> {
946 Some(self)
947 }
948
949 fn as_any(&self) -> &dyn Any {
950 self
951 }
952}
953
954impl IFramebufferPlatformSurface for TopLevelFramebufferSurface {
955 fn create_framebuffer_render_target(&self) -> Rc<dyn IFramebufferRenderTarget> {
959 if !Dispatcher::ui_thread().check_access() {
960 panic!("The render target is not ready.");
961 }
962
963 let native_render_target =
964 self.top_level.get().and_then(|native| native.create_software_render_target().check());
965
966 let Some(native_render_target) = native_render_target else {
967 panic!("The render target is not ready.");
968 };
969
970 Rc::new(FramebufferRenderTarget::new(self.state.clone(), native_render_target))
971 }
972}
973
974struct SoftwareRenderTargetPtr(ComPtr<IFrnSoftwareRenderTarget>);
977
978unsafe impl Send for SoftwareRenderTargetPtr {}
1006
1007struct FramebufferRenderTarget {
1013 parent: Arc<FramebufferSurfaceState>,
1014 target: Arc<Mutex<Option<SoftwareRenderTargetPtr>>>,
1016}
1017
1018impl FramebufferRenderTarget {
1019 fn new(parent: Arc<FramebufferSurfaceState>, target: ComPtr<IFrnSoftwareRenderTarget>) -> Self {
1020 Self { parent, target: Arc::new(Mutex::new(Some(SoftwareRenderTargetPtr(target)))) }
1021 }
1022}
1023
1024impl IPlatformRenderSurfaceRenderTarget for FramebufferRenderTarget {}
1025
1026impl IFramebufferRenderTarget for FramebufferRenderTarget {
1027 fn lock(&self, _scene_info: &RenderTargetSceneInfo) -> (Rc<dyn ILockedFramebuffer>, FramebufferLockProperties) {
1028 if lock_shared(&self.target).is_none() {
1029 panic!("Cannot access a disposed object: FramebufferRenderTarget");
1030 }
1031 let (size, scaling) = *lock_shared(&self.parent.metrics);
1032 let w = (size.width * scaling).max(1.0);
1033 let h = (size.height * scaling).max(1.0);
1034 let dpi = scaling * 96.0;
1035
1036 let parent = self.parent.clone();
1037 let current_target = self.target.clone();
1038 let framebuffer = DeferredFramebuffer::new(
1039 Box::new(move |cb: &mut dyn FnMut(&IFrnSoftwareRenderTarget)| {
1040 let _sync_root = lock_shared(&parent.sync_root);
1041 if parent.alive.load(Ordering::Acquire) {
1042 if let Some(target) = lock_shared(¤t_target).as_ref() {
1043 cb(&*target.0);
1044 }
1045 }
1046 }),
1047 w as i32,
1048 h as i32,
1049 Vector::new(dpi, dpi),
1050 );
1051 (Rc::new(framebuffer), FramebufferLockProperties::default())
1052 }
1053
1054 fn retains_frame_contents(&self) -> bool {
1055 false
1056 }
1057
1058 fn dispose(&self) {
1059 let _sync_root = lock_shared(&self.parent.sync_root);
1060 let target = lock_shared(&self.target).take();
1061 drop(target);
1062 }
1063}
1064
1065pub(crate) struct TopLevelEvents<P>(pub(crate) Rc<P>);
1070
1071struct DisposeOnExit<'a, P: TopLevelParent> {
1074 parent: &'a P,
1075 native: Option<ComPtr<IFrnTopLevel>>,
1076}
1077
1078impl<P: TopLevelParent> Drop for DisposeOnExit<'_, P> {
1079 fn drop(&mut self) {
1080 self.parent.dispose_top_level();
1081 drop(self.native.take());
1082 }
1083}
1084
1085fn c_str_to_string(s: Option<&CStr>) -> Option<String> {
1086 s.map(|s| s.to_string_lossy().into_owned())
1087}
1088
1089impl<P: TopLevelParent> IFrnTopLevelEventsImpl for TopLevelEvents<P> {
1090 fn closed(&self) {
1091 crate::callback_base::guard((), || {
1092 let top_level = self.0.top_level();
1093 let _dispose = DisposeOnExit { parent: &*self.0, native: top_level.native() };
1094 let closed = top_level.closed();
1095 if let Some(closed) = closed {
1096 closed();
1097 }
1098 })
1099 }
1100
1101 fn paint(&self) -> Result<(), HResult> {
1102 crate::callback_base::guard(Ok(()), || {
1103 self.0.top_level().on_paint();
1104 Ok(())
1105 })
1106 }
1107
1108 fn resized(&self, size: &FrnSize, reason: FrnPlatformResizeReason) {
1109 crate::callback_base::guard((), || self.0.top_level().on_resized(size, reason))
1110 }
1111
1112 fn raw_mouse_event(
1113 &self,
1114 type_: FrnRawMouseEventType,
1115 device_type: FrnPointerDeviceType,
1116 time_stamp: u64,
1117 modifiers: FrnInputModifiers,
1118 point: FrnPoint,
1119 delta: FrnVector,
1120 pressure: f32,
1121 x_tilt: f32,
1122 y_tilt: f32,
1123 ) {
1124 crate::callback_base::guard((), || {
1125 self.0.top_level().raw_mouse_event(
1126 type_,
1127 device_type,
1128 time_stamp,
1129 modifiers,
1130 point,
1131 delta,
1132 pressure,
1133 x_tilt,
1134 y_tilt,
1135 &|e| self.0.chrome_hit_test(e),
1136 )
1137 })
1138 }
1139
1140 fn raw_key_event(
1141 &self,
1142 type_: FrnRawKeyEventType,
1143 time_stamp: u64,
1144 modifiers: FrnInputModifiers,
1145 key: FrnKey,
1146 physical_key: FrnPhysicalKey,
1147 key_symbol: Option<&CStr>,
1148 ) -> bool {
1149 crate::callback_base::guard(false, || {
1150 self.0.top_level().raw_key_event(
1151 type_,
1152 time_stamp,
1153 modifiers,
1154 key,
1155 physical_key,
1156 c_str_to_string(key_symbol),
1157 )
1158 })
1159 }
1160
1161 fn raw_text_input_event(&self, time_stamp: u64, text: Option<&CStr>) -> bool {
1162 crate::callback_base::guard(false, || {
1163 let text = c_str_to_string(text).unwrap_or_default();
1164 self.0.top_level().raw_text_input_event(time_stamp, &text)
1165 })
1166 }
1167
1168 fn scaling_changed(&self, scaling: f64) {
1169 crate::callback_base::guard((), || self.0.top_level().on_scaling_changed(scaling))
1170 }
1171
1172 fn run_render_priority_jobs(&self) {
1173 crate::callback_base::guard((), || {
1174 Dispatcher::ui_thread().run_jobs(Some(DispatcherPriority::UI_THREAD_RENDER));
1175 })
1176 }
1177
1178 fn lost_focus(&self) {
1179 crate::callback_base::guard((), || self.0.top_level().on_lost_focus())
1180 }
1181
1182 fn get_automation_peer(&self) -> Option<ComPtr<IFrnAutomationPeer>> {
1183 crate::callback_base::guard(None, || {
1184 let native = self.0.top_level().get_automation_peer();
1185
1186 FrnAutomationPeer::wrap_native(native)
1187 })
1188 }
1189
1190 fn drag_event(
1191 &self,
1192 type_: FrnDragEventType,
1193 position: FrnPoint,
1194 modifiers: FrnInputModifiers,
1195 effects: FrnDragDropEffects,
1196 clipboard: Option<&IFrnClipboard>,
1197 data_transfer_handle: *mut c_void,
1198 ) -> FrnDragDropEffects {
1199 crate::callback_base::guard(FrnDragDropEffects::None, || {
1200 self.0.top_level().on_drag_event(type_, position, modifiers, effects, clipboard, data_transfer_handle)
1201 })
1202 }
1203}
1204
1205macro_rules! impl_top_level_contract {
1209 ($ty:ty { $($extra:tt)* }) => {
1210 impl ferroui_base::platform::IOptionalFeatureProvider for $ty {
1211 fn try_get_feature(&self, feature_type: std::any::TypeId) -> Option<std::rc::Rc<dyn std::any::Any>> {
1212 $crate::top_level_impl::TopLevelParent::try_get_feature_core(self, feature_type)
1213 }
1214 }
1215
1216 impl ferroui_base::reactive::IDisposable for $ty {
1217 fn dispose(&self) {
1218 $crate::top_level_impl::TopLevelParent::dispose_top_level(self)
1219 }
1220 }
1221
1222 impl ferroui_controls::platform::ITopLevelImpl for $ty {
1223 fn desktop_scaling(&self) -> f64 {
1224 $crate::top_level_impl::TopLevelParent::top_level(self).desktop_scaling()
1225 }
1226
1227 fn handle(&self) -> Option<std::rc::Rc<dyn ferroui_controls::platform::IPlatformHandle>> {
1228 $crate::top_level_impl::TopLevelParent::top_level(self).handle()
1229 }
1230
1231 fn client_size(&self) -> ferroui_base::Size {
1232 $crate::top_level_impl::TopLevelParent::top_level(self).client_size()
1233 }
1234
1235 fn render_scaling(&self) -> f64 {
1236 $crate::top_level_impl::TopLevelParent::top_level(self).render_scaling()
1237 }
1238
1239 fn surfaces(
1240 &self,
1241 ) -> Vec<std::sync::Arc<dyn ferroui_base::platform::surfaces::IPlatformRenderSurface>> {
1242 $crate::top_level_impl::TopLevelParent::top_level(self).surfaces()
1243 }
1244
1245 fn render_surfaces(
1246 &self,
1247 ) -> std::sync::Arc<
1248 dyn Fn() -> Vec<std::sync::Arc<dyn ferroui_base::platform::surfaces::IPlatformRenderSurface>>
1249 + Send
1250 + Sync,
1251 > {
1252 $crate::top_level_impl::TopLevelParent::top_level(self).render_surfaces()
1253 }
1254
1255 fn compositor(&self) -> Option<std::rc::Rc<ferroui_base::rendering::composition::Compositor>> {
1256 Some($crate::ferro_native_platform::FerroNativePlatform::compositor())
1257 }
1258
1259 fn input(
1260 &self,
1261 ) -> Option<std::rc::Rc<dyn Fn(std::rc::Rc<dyn ferroui_base::input::raw::IRawInputEventArgs>)>> {
1262 $crate::top_level_impl::TopLevelParent::top_level(self).input()
1263 }
1264
1265 fn set_input(
1266 &self,
1267 value: Option<std::rc::Rc<dyn Fn(std::rc::Rc<dyn ferroui_base::input::raw::IRawInputEventArgs>)>>,
1268 ) {
1269 $crate::top_level_impl::TopLevelParent::top_level(self).set_input(value)
1270 }
1271
1272 fn paint(&self) -> Option<std::rc::Rc<dyn Fn(ferroui_base::Rect)>> {
1273 $crate::top_level_impl::TopLevelParent::top_level(self).paint()
1274 }
1275
1276 fn set_paint(&self, value: Option<std::rc::Rc<dyn Fn(ferroui_base::Rect)>>) {
1277 $crate::top_level_impl::TopLevelParent::top_level(self).set_paint(value)
1278 }
1279
1280 fn resized(
1281 &self,
1282 ) -> Option<std::rc::Rc<dyn Fn(ferroui_base::Size, ferroui_controls::WindowResizeReason)>> {
1283 $crate::top_level_impl::TopLevelParent::top_level(self).resized()
1284 }
1285
1286 fn set_resized(
1287 &self,
1288 value: Option<std::rc::Rc<dyn Fn(ferroui_base::Size, ferroui_controls::WindowResizeReason)>>,
1289 ) {
1290 $crate::top_level_impl::TopLevelParent::top_level(self).set_resized(value)
1291 }
1292
1293 fn scaling_changed(&self) -> Option<std::rc::Rc<dyn Fn(f64)>> {
1294 $crate::top_level_impl::TopLevelParent::top_level(self).scaling_changed()
1295 }
1296
1297 fn set_scaling_changed(&self, value: Option<std::rc::Rc<dyn Fn(f64)>>) {
1298 $crate::top_level_impl::TopLevelParent::top_level(self).set_scaling_changed(value)
1299 }
1300
1301 fn transparency_level_changed(
1302 &self,
1303 ) -> Option<std::rc::Rc<dyn Fn(ferroui_controls::WindowTransparencyLevel)>> {
1304 $crate::top_level_impl::TopLevelParent::top_level(self).transparency_level_changed()
1305 }
1306
1307 fn set_transparency_level_changed(
1308 &self,
1309 value: Option<std::rc::Rc<dyn Fn(ferroui_controls::WindowTransparencyLevel)>>,
1310 ) {
1311 $crate::top_level_impl::TopLevelParent::top_level(self).set_transparency_level_changed(value)
1312 }
1313
1314 fn set_input_root(&self, input_root: std::rc::Rc<dyn ferroui_base::input::IInputRoot>) {
1315 $crate::top_level_impl::TopLevelParent::top_level(self).set_input_root(input_root)
1316 }
1317
1318 fn point_to_client(&self, point: ferroui_base::PixelPoint) -> ferroui_base::Point {
1319 $crate::top_level_impl::TopLevelParent::top_level(self).point_to_client(point)
1320 }
1321
1322 fn point_to_screen(&self, point: ferroui_base::Point) -> ferroui_base::PixelPoint {
1323 $crate::top_level_impl::TopLevelParent::top_level(self).point_to_screen(point)
1324 }
1325
1326 fn set_cursor(&self, cursor: Option<std::rc::Rc<dyn ferroui_base::platform::ICursorImpl>>) {
1327 $crate::top_level_impl::TopLevelParent::top_level(self).set_cursor(cursor)
1328 }
1329
1330 fn closed(&self) -> Option<std::rc::Rc<dyn Fn()>> {
1331 $crate::top_level_impl::TopLevelParent::top_level(self).closed()
1332 }
1333
1334 fn set_closed(&self, value: Option<std::rc::Rc<dyn Fn()>>) {
1335 $crate::top_level_impl::TopLevelParent::top_level(self).set_closed(value)
1336 }
1337
1338 fn lost_focus(&self) -> Option<std::rc::Rc<dyn Fn()>> {
1339 $crate::top_level_impl::TopLevelParent::top_level(self).lost_focus()
1340 }
1341
1342 fn set_lost_focus(&self, value: Option<std::rc::Rc<dyn Fn()>>) {
1343 $crate::top_level_impl::TopLevelParent::top_level(self).set_lost_focus(value)
1344 }
1345
1346 fn create_popup(&self) -> Option<std::rc::Rc<dyn ferroui_controls::platform::IPopupImpl>> {
1347 $crate::top_level_impl::TopLevelParent::create_popup_core(self)
1348 }
1349
1350 fn set_transparency_level_hint(&self, transparency_levels: &[ferroui_controls::WindowTransparencyLevel]) {
1351 $crate::top_level_impl::TopLevelParent::top_level(self)
1352 .set_transparency_level_hint(transparency_levels)
1353 }
1354
1355 fn transparency_level(&self) -> ferroui_controls::WindowTransparencyLevel {
1356 $crate::top_level_impl::TopLevelParent::top_level(self).transparency_level()
1357 }
1358
1359 fn acrylic_compensation_levels(&self) -> ferroui_controls::AcrylicPlatformCompensationLevels {
1360 $crate::top_level_impl::TopLevelParent::top_level(self).acrylic_compensation_levels()
1361 }
1362
1363 fn set_frame_theme_variant(
1364 &self,
1365 theme_variant: Option<ferroui_controls::platform::PlatformThemeVariant>,
1366 ) {
1367 $crate::top_level_impl::TopLevelParent::set_frame_theme_variant_core(self, theme_variant)
1368 }
1369
1370 fn as_any(&self) -> &dyn std::any::Any {
1371 self
1372 }
1373
1374 $($extra)*
1375 }
1376 };
1377}
1378pub(crate) use impl_top_level_contract;
1379
1380pub(crate) fn top_level_of(top_level: &dyn ITopLevelImpl) -> Option<&Rc<TopLevelImpl>> {
1383 let any = top_level.as_any();
1384 if let Some(window) = any.downcast_ref::<crate::window_impl::WindowImpl>() {
1385 return Some(window.top_level());
1386 }
1387 if let Some(popup) = any.downcast_ref::<crate::popup_impl::PopupImpl>() {
1388 return Some(popup.top_level());
1389 }
1390 if let Some(embeddable) = any.downcast_ref::<crate::embeddable_top_level_impl::EmbeddableTopLevelImpl>() {
1391 return Some(embeddable.top_level());
1392 }
1393 None
1394}
1395
1396#[cfg(test)]
1397mod tests {
1398 use super::*;
1399 use ferroui_base::input::raw::{RawKeyEventType, RawPointerEventType};
1400 use ferroui_base::input::{FocusManager, InputElement, Key, KeyboardDevice, PhysicalKey, RawInputModifiers};
1401 struct InputRoot {
1402 root: Ref<InputElement>,
1403 }
1404
1405 impl IInputRoot for InputRoot {
1406 fn focus_manager(&self) -> Option<Rc<FocusManager>> {
1407 None
1408 }
1409 fn pointer_over_element(&self) -> Option<Ref<InputElement>> {
1410 None
1411 }
1412 fn set_pointer_over_element(&self, _value: Option<Ref<InputElement>>) {}
1413 fn cursor_element(&self) -> Option<Ref<InputElement>> {
1414 None
1415 }
1416 fn set_cursor_element(&self, _value: Option<Ref<InputElement>>) {}
1417 fn root_element(&self) -> Ref<InputElement> {
1418 self.root.clone()
1419 }
1420 fn focus_root(&self) -> Ref<InputElement> {
1421 self.root.clone()
1422 }
1423 fn pointer_over_invalidated(&self) {}
1424 }
1425
1426 fn top_level(with_input_root: bool) -> (Rc<TopLevelImpl>, Rc<RefCell<Vec<Rc<dyn IRawInputEventArgs>>>>) {
1430 let keyboard: Rc<dyn IKeyboardDevice> = KeyboardDevice::new();
1431 FerroLocator::current_mutable().bind::<dyn IKeyboardDevice>().to_constant(keyboard);
1432 let top_level = TopLevelImpl::new(create_ferro_native().expect("factory"));
1433 if with_input_root {
1434 top_level.set_input_root(Rc::new(InputRoot { root: InputElement::new() }));
1435 }
1436 let received = Rc::new(RefCell::new(Vec::new()));
1437 let sink = received.clone();
1438 top_level.set_input(Some(Rc::new(move |e| sink.borrow_mut().push(e))));
1439 (top_level, received)
1440 }
1441
1442 fn mouse(top_level: &TopLevelImpl, type_: FrnRawMouseEventType, device: FrnPointerDeviceType, swallow: bool) {
1443 top_level.raw_mouse_event(
1444 type_,
1445 device,
1446 42,
1447 FrnInputModifiers::Shift | FrnInputModifiers::LeftMouseButton,
1448 FrnPoint { x: 10.5, y: 20.25 },
1449 FrnVector { x: 1.0, y: -2.0 },
1450 0.75,
1451 0.1,
1452 0.2,
1453 &|_| swallow,
1454 );
1455 }
1456
1457 #[test]
1458 fn pointer_events_become_raw_pointer_args() {
1459 let _dispatcher = Dispatcher::unit_test_scope();
1460 let _scope = FerroLocator::enter_scope();
1461 let (top_level, received) = top_level(true);
1462
1463 mouse(&top_level, FrnRawMouseEventType::LeftButtonDown, FrnPointerDeviceType::Mouse, false);
1464
1465 let received = received.borrow();
1466 assert_eq!(received.len(), 1);
1467 let e = received[0].downcast_ref::<RawPointerEventArgs>().expect("pointer args");
1468 assert_eq!(e.type_(), RawPointerEventType::LeftButtonDown);
1469 assert_eq!(e.position(), Point::new(10.5, 20.25));
1470 assert_eq!(e.input_modifiers(), RawInputModifiers::SHIFT | RawInputModifiers::LEFT_MOUSE_BUTTON);
1471 assert_eq!(e.timestamp(), 42);
1472 assert_eq!(e.point().pressure, 0.75);
1473 assert_eq!(e.point().x_tilt, 0.1);
1474 assert_eq!(e.point().y_tilt, 0.2);
1475 assert!(e.device().as_any().is::<MouseDevice>());
1476 }
1477
1478 #[test]
1479 fn pen_events_come_from_the_pen_device() {
1480 let _dispatcher = Dispatcher::unit_test_scope();
1481 let _scope = FerroLocator::enter_scope();
1482 let (top_level, received) = top_level(true);
1483
1484 mouse(&top_level, FrnRawMouseEventType::Move, FrnPointerDeviceType::Pen, false);
1485
1486 let received = received.borrow();
1487 let e = received[0].downcast_ref::<RawPointerEventArgs>().expect("pointer args");
1488 assert_eq!(e.type_(), RawPointerEventType::Move);
1489 assert!(e.device().as_any().is::<PenDevice>());
1490 }
1491
1492 #[test]
1493 fn wheel_and_gesture_events_carry_their_delta() {
1494 let _dispatcher = Dispatcher::unit_test_scope();
1495 let _scope = FerroLocator::enter_scope();
1496 let (top_level, received) = top_level(true);
1497
1498 mouse(&top_level, FrnRawMouseEventType::Wheel, FrnPointerDeviceType::Mouse, true);
1500 mouse(&top_level, FrnRawMouseEventType::Magnify, FrnPointerDeviceType::Mouse, true);
1501 mouse(&top_level, FrnRawMouseEventType::Rotate, FrnPointerDeviceType::Mouse, true);
1502 mouse(&top_level, FrnRawMouseEventType::Swipe, FrnPointerDeviceType::Mouse, true);
1503
1504 let received = received.borrow();
1505 assert_eq!(received.len(), 4);
1506 let wheel = received[0].downcast_ref::<RawMouseWheelEventArgs>().expect("wheel args");
1507 assert_eq!(wheel.delta(), Vector::new(1.0, -2.0));
1508 assert_eq!(wheel.position(), Point::new(10.5, 20.25));
1509 for (index, type_) in
1510 [(1, RawPointerEventType::Magnify), (2, RawPointerEventType::Rotate), (3, RawPointerEventType::Swipe)]
1511 {
1512 let gesture = received[index].downcast_ref::<RawPointerGestureEventArgs>().expect("gesture args");
1513 assert_eq!(gesture.type_(), type_);
1514 assert_eq!(gesture.delta(), Vector::new(1.0, -2.0));
1515 }
1516 }
1517
1518 #[test]
1519 fn chrome_hit_test_can_swallow_pointer_events() {
1520 let _dispatcher = Dispatcher::unit_test_scope();
1521 let _scope = FerroLocator::enter_scope();
1522 let (top_level, received) = top_level(true);
1523
1524 mouse(&top_level, FrnRawMouseEventType::LeftButtonDown, FrnPointerDeviceType::Mouse, true);
1525 assert!(received.borrow().is_empty());
1526
1527 mouse(&top_level, FrnRawMouseEventType(99), FrnPointerDeviceType::Mouse, false);
1529 assert!(received.borrow().is_empty());
1530 }
1531
1532 #[test]
1533 fn key_and_text_events_report_whether_they_were_handled() {
1534 let _dispatcher = Dispatcher::unit_test_scope();
1535 let _scope = FerroLocator::enter_scope();
1536 let (top_level, received) = top_level(true);
1537
1538 let handled = top_level.raw_key_event(
1539 FrnRawKeyEventType::KeyUp,
1540 7,
1541 FrnInputModifiers::Windows,
1542 FrnKey::FrnKeyA,
1543 FrnPhysicalKey::FrnPhysicalKeyA,
1544 Some("a".to_string()),
1545 );
1546 assert!(!handled);
1547 assert!(!top_level.raw_text_input_event(8, "ä"));
1548
1549 {
1550 let received = received.borrow();
1551 let key = received[0].downcast_ref::<RawKeyEventArgs>().expect("key args");
1552 assert_eq!(key.type_(), RawKeyEventType::KeyUp);
1553 assert_eq!(key.key(), Key::A);
1554 assert_eq!(key.physical_key(), PhysicalKey::A);
1555 assert_eq!(key.modifiers(), RawInputModifiers::META);
1556 assert_eq!(key.key_symbol().as_deref(), Some("a"));
1557 assert_eq!(key.timestamp(), 7);
1558 let text = received[1].downcast_ref::<RawTextInputEventArgs>().expect("text args");
1559 assert_eq!(text.text(), "ä");
1560 }
1561
1562 top_level.set_input(Some(Rc::new(|e| e.set_handled(true))));
1563 assert!(top_level.raw_key_event(
1564 FrnRawKeyEventType::KeyDown,
1565 9,
1566 FrnInputModifiers::FrnInputModifiersNone,
1567 FrnKey::FrnKeyEscape,
1568 FrnPhysicalKey::FrnPhysicalKeyNone,
1569 None,
1570 ));
1571 assert!(top_level.raw_text_input_event(10, "x"));
1572 }
1573
1574 #[test]
1575 fn drag_events_need_a_drag_drop_device() {
1576 let _dispatcher = Dispatcher::unit_test_scope();
1577 let _scope = FerroLocator::enter_scope();
1578 let (top_level, received) = top_level(true);
1579
1580 let effects = top_level.on_drag_event(
1581 FrnDragEventType::Enter,
1582 FrnPoint { x: 1.0, y: 2.0 },
1583 FrnInputModifiers::FrnInputModifiersNone,
1584 FrnDragDropEffects::Copy,
1585 None,
1586 std::ptr::null_mut(),
1587 );
1588 assert_eq!(effects, FrnDragDropEffects::None);
1589 assert!(received.borrow().is_empty());
1590 }
1591
1592 #[test]
1593 fn nothing_is_delivered_without_an_input_root() {
1594 let _dispatcher = Dispatcher::unit_test_scope();
1595 let _scope = FerroLocator::enter_scope();
1596 let (top_level, received) = top_level(false);
1597
1598 mouse(&top_level, FrnRawMouseEventType::Move, FrnPointerDeviceType::Mouse, false);
1599 assert!(!top_level.raw_text_input_event(1, "x"));
1600 assert!(!top_level.raw_key_event(
1601 FrnRawKeyEventType::KeyDown,
1602 1,
1603 FrnInputModifiers::FrnInputModifiersNone,
1604 FrnKey::FrnKeyA,
1605 FrnPhysicalKey::FrnPhysicalKeyA,
1606 None,
1607 ));
1608 assert!(received.borrow().is_empty());
1609 }
1610
1611 #[test]
1612 fn transparency_level_hint_picks_the_first_supported_level() {
1613 let _dispatcher = Dispatcher::unit_test_scope();
1614 let _scope = FerroLocator::enter_scope();
1615 let (top_level, _received) = top_level(false);
1616 let changes = Rc::new(RefCell::new(Vec::new()));
1617 let sink = changes.clone();
1618 top_level.set_transparency_level_changed(Some(Rc::new(move |level| sink.borrow_mut().push(level))));
1619
1620 top_level.set_transparency_level_hint(&[
1622 WindowTransparencyLevel::mica(),
1623 WindowTransparencyLevel::blur(),
1624 WindowTransparencyLevel::acrylic_blur(),
1625 WindowTransparencyLevel::transparent(),
1626 ]);
1627 assert_eq!(top_level.transparency_level(), WindowTransparencyLevel::acrylic_blur());
1628
1629 top_level
1631 .set_transparency_level_hint(&[WindowTransparencyLevel::acrylic_blur(), WindowTransparencyLevel::transparent()]);
1632 assert_eq!(top_level.transparency_level(), WindowTransparencyLevel::transparent());
1633
1634 top_level.set_transparency_level_hint(&[WindowTransparencyLevel::mica()]);
1636 assert_eq!(top_level.transparency_level(), WindowTransparencyLevel::none());
1637
1638 top_level.set_transparency_level_hint(&[WindowTransparencyLevel::none()]);
1640 assert_eq!(
1641 *changes.borrow(),
1642 [
1643 WindowTransparencyLevel::acrylic_blur(),
1644 WindowTransparencyLevel::transparent(),
1645 WindowTransparencyLevel::none()
1646 ]
1647 );
1648 }
1649
1650 #[test]
1651 fn a_top_level_without_native_object_answers_with_defaults() {
1652 let _dispatcher = Dispatcher::unit_test_scope();
1653 let _scope = FerroLocator::enter_scope();
1654 let (top_level, _received) = top_level(false);
1655
1656 assert_eq!(top_level.desktop_scaling(), 1.0);
1657 assert!(top_level.native().is_none());
1658 assert!(top_level.handle().is_none());
1659 assert_eq!(top_level.client_size(), Size::default());
1660 assert!(top_level.surfaces().is_empty());
1661 assert!((top_level.render_surfaces())().is_empty());
1662 assert_eq!(top_level.point_to_client(PixelPoint::new(3, 4)), Point::default());
1663 assert_eq!(top_level.point_to_screen(Point::new(3.0, 4.0)), PixelPoint::default());
1664 assert_eq!(top_level.acrylic_compensation_levels(), AcrylicPlatformCompensationLevels::new(1.0, 0.0, 0.0));
1665 top_level.invalidate();
1667 top_level.set_cursor(None);
1668 }
1669
1670 #[test]
1673 fn automation_peer_is_the_peer_of_the_focus_root_control() {
1674 let _dispatcher = Dispatcher::unit_test_scope();
1675 let _scope = FerroLocator::enter_scope();
1676 let (top_level, _) = top_level(false);
1677 assert!(top_level.get_automation_peer().is_none());
1678
1679 top_level.set_input_root(Rc::new(InputRoot { root: InputElement::new() }));
1680 assert!(top_level.get_automation_peer().is_none());
1681
1682 let root = ferroui_controls::Border::new();
1683 top_level.set_input_root(Rc::new(InputRoot { root: root.clone().upcast() }));
1684 assert_eq!(top_level.get_automation_peer(), Some(ControlAutomationPeer::create_peer_for_element(&root)));
1685 }
1686
1687 #[test]
1690 fn the_render_surfaces_are_shared_between_threads() {
1691 fn assert_send_sync<T: Send + Sync>() {}
1692 assert_send_sync::<TopLevelFramebufferSurface>();
1693 assert_send_sync::<MetalPlatformSurface>();
1694 }
1695
1696 struct CountingRenderTarget(Rc<Cell<u32>>);
1697
1698 impl IFrnSoftwareRenderTargetImpl for CountingRenderTarget {
1699 fn set_frame(&self, _fb: *mut FrnFramebuffer) -> Result<(), HResult> {
1700 self.0.set(self.0.get() + 1);
1701 Ok(())
1702 }
1703 }
1704
1705 fn scene_info() -> RenderTargetSceneInfo {
1706 RenderTargetSceneInfo::new(
1707 ferroui_base::PixelSize::new(1, 1),
1708 1.0,
1709 ferroui_base::rendering::composition::CompositionTransparencyLevel::None,
1710 )
1711 }
1712
1713 #[test]
1716 fn a_frame_is_sized_with_the_metrics_of_the_surface() {
1717 let frames = Rc::new(Cell::new(0));
1718 let state = FramebufferSurfaceState::new(Size::new(10.0, 5.0), 2.0);
1719 let target = FramebufferRenderTarget::new(
1720 state.clone(),
1721 IFrnSoftwareRenderTarget::from_impl(CountingRenderTarget(frames.clone())),
1722 );
1723
1724 let (framebuffer, properties) = target.lock(&scene_info());
1725 assert_eq!(framebuffer.size(), ferroui_base::PixelSize::new(20, 10));
1726 assert_eq!(framebuffer.dpi(), Vector::new(192.0, 192.0));
1727 assert_eq!(properties, FramebufferLockProperties::default());
1728 assert!(!target.retains_frame_contents());
1729 assert_eq!(frames.get(), 0);
1730 framebuffer.dispose();
1731 assert_eq!(frames.get(), 1);
1732
1733 *lock_shared(&state.metrics) = (Size::new(0.0, 0.0), 1.0);
1734 let (framebuffer, _) = target.lock(&scene_info());
1735 assert_eq!(framebuffer.size(), ferroui_base::PixelSize::new(1, 1));
1736 framebuffer.dispose();
1737 assert_eq!(frames.get(), 2);
1738 }
1739
1740 #[test]
1741 fn a_frame_is_not_presented_once_the_top_level_is_gone_or_the_target_is_disposed() {
1742 let frames = Rc::new(Cell::new(0));
1743 let state = FramebufferSurfaceState::new(Size::new(4.0, 4.0), 1.0);
1744 let target = FramebufferRenderTarget::new(
1745 state.clone(),
1746 IFrnSoftwareRenderTarget::from_impl(CountingRenderTarget(frames.clone())),
1747 );
1748
1749 let (framebuffer, _) = target.lock(&scene_info());
1751 state.alive.store(false, Ordering::Release);
1752 framebuffer.dispose();
1753 assert_eq!(frames.get(), 0);
1754
1755 state.alive.store(true, Ordering::Release);
1757 let (framebuffer, _) = target.lock(&scene_info());
1758 target.dispose();
1759 framebuffer.dispose();
1760 assert_eq!(frames.get(), 0);
1761 }
1762
1763 #[test]
1764 #[should_panic(expected = "Cannot access a disposed object: FramebufferRenderTarget")]
1765 fn a_disposed_framebuffer_render_target_cannot_be_locked() {
1766 let state = FramebufferSurfaceState::new(Size::new(4.0, 4.0), 1.0);
1767 let target = FramebufferRenderTarget::new(
1768 state,
1769 IFrnSoftwareRenderTarget::from_impl(CountingRenderTarget(Rc::new(Cell::new(0)))),
1770 );
1771 target.dispose();
1772 let _ = target.lock(&scene_info());
1773 }
1774}