From 16d5f46db1720de443c958dfd3d5316f3d1065d3 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Tue, 11 Mar 2025 14:35:25 +0100 Subject: [PATCH] utils: add `cast_*` methods to allow more type-safe casting Relying on just `as_any` was error prone and will become redundant in the future, once upcasting will be stable, we also won't to impose a restriction on to which concrete type we're casting, since casting to a type that doesn't implement a base trait doesn't make much sense. Co-authored-by: Kirill Chibisov --- src/changelog/unreleased.md | 4 +- src/event_loop.rs | 4 +- src/monitor.rs | 4 +- src/platform/android.rs | 7 +-- src/platform/ios.rs | 24 +++---- src/platform/macos.rs | 50 +++++++-------- src/platform/startup_notify.rs | 7 +-- src/platform/wayland.rs | 2 +- src/platform/web.rs | 55 ++++++---------- src/platform/windows.rs | 20 +++--- src/platform/x11.rs | 2 +- .../apple/appkit/window_delegate.rs | 10 +-- src/platform_impl/apple/uikit/window.rs | 10 +-- src/platform_impl/linux/wayland/window/mod.rs | 11 +--- src/platform_impl/linux/x11/window.rs | 6 +- src/platform_impl/web/web_sys/fullscreen.rs | 2 +- src/platform_impl/windows/window.rs | 8 +-- src/utils.rs | 62 +++++++++++++++++-- src/window.rs | 6 +- 19 files changed, 158 insertions(+), 136 deletions(-) diff --git a/src/changelog/unreleased.md b/src/changelog/unreleased.md index 2690fc64..7380df27 100644 --- a/src/changelog/unreleased.md +++ b/src/changelog/unreleased.md @@ -76,8 +76,8 @@ changelog entry. ### Changed -- Change `ActiveEventLoop` to be a trait. -- Change `Window` to be a trait. +- Change `ActiveEventLoop` and `Window` to be traits, and added `cast_ref`/`cast_mut`/`cast` + methods to extract the backend type from those. - `ActiveEventLoop::create_window` now returns `Box`. - `ApplicationHandler` now uses `dyn ActiveEventLoop`. - On Web, let events wake up event loop immediately when using `ControlFlow::Poll`. diff --git a/src/event_loop.rs b/src/event_loop.rs index 8ef4eff4..796489ad 100644 --- a/src/event_loop.rs +++ b/src/event_loop.rs @@ -25,7 +25,7 @@ use crate::application::ApplicationHandler; use crate::error::{EventLoopError, RequestError}; use crate::monitor::MonitorHandle; use crate::platform_impl; -use crate::utils::AsAny; +use crate::utils::{impl_dyn_casting, AsAny}; use crate::window::{CustomCursor, CustomCursorSource, Theme, Window, WindowAttributes}; /// Provides a way to retrieve events from the system and from the windows that were registered to @@ -406,6 +406,8 @@ impl HasDisplayHandle for dyn ActiveEventLoop + '_ { } } +impl_dyn_casting!(ActiveEventLoop); + /// A proxy for the underlying display handle. /// /// The purpose of this type is to provide a cheaply cloneable handle to the underlying diff --git a/src/monitor.rs b/src/monitor.rs index d76a6fbe..88dc84a6 100644 --- a/src/monitor.rs +++ b/src/monitor.rs @@ -12,7 +12,7 @@ use std::ops::Deref; use std::sync::Arc; use crate::dpi::{PhysicalPosition, PhysicalSize}; -use crate::utils::AsAny; +use crate::utils::{impl_dyn_casting, AsAny}; /// Handle to a monitor. /// @@ -148,6 +148,8 @@ impl PartialEq for dyn MonitorHandleProvider + '_ { impl Eq for dyn MonitorHandleProvider + '_ {} +impl_dyn_casting!(MonitorHandleProvider); + /// Describes a fullscreen video mode of a monitor. /// /// Can be acquired with [`MonitorHandleProvider::video_modes`]. diff --git a/src/platform/android.rs b/src/platform/android.rs index ccfe49a2..79d0144b 100644 --- a/src/platform/android.rs +++ b/src/platform/android.rs @@ -101,20 +101,19 @@ pub trait WindowExtAndroid { impl WindowExtAndroid for dyn Window + '_ { fn content_rect(&self) -> Rect { - let window = self.as_any().downcast_ref::().unwrap(); + let window = self.cast_ref::().unwrap(); window.content_rect() } fn config(&self) -> ConfigurationRef { - let window = self.as_any().downcast_ref::().unwrap(); + let window = self.cast_ref::().unwrap(); window.config() } } impl ActiveEventLoopExtAndroid for dyn ActiveEventLoop + '_ { fn android_app(&self) -> &AndroidApp { - let event_loop = - self.as_any().downcast_ref::().unwrap(); + let event_loop = self.cast_ref::().unwrap(); &event_loop.app } } diff --git a/src/platform/ios.rs b/src/platform/ios.rs index e3c09ea8..d9438d35 100644 --- a/src/platform/ios.rs +++ b/src/platform/ios.rs @@ -214,25 +214,25 @@ pub trait WindowExtIOS { impl WindowExtIOS for dyn Window + '_ { #[inline] fn set_scale_factor(&self, scale_factor: f64) { - let window = self.as_any().downcast_ref::().unwrap(); + let window = self.cast_ref::().unwrap(); window.maybe_wait_on_main(move |w| w.set_scale_factor(scale_factor)); } #[inline] fn set_valid_orientations(&self, valid_orientations: ValidOrientations) { - let window = self.as_any().downcast_ref::().unwrap(); + let window = self.cast_ref::().unwrap(); window.maybe_wait_on_main(move |w| w.set_valid_orientations(valid_orientations)); } #[inline] fn set_prefers_home_indicator_hidden(&self, hidden: bool) { - let window = self.as_any().downcast_ref::().unwrap(); + let window = self.cast_ref::().unwrap(); window.maybe_wait_on_main(move |w| w.set_prefers_home_indicator_hidden(hidden)); } #[inline] fn set_preferred_screen_edges_deferring_system_gestures(&self, edges: ScreenEdge) { - let window = self.as_any().downcast_ref::().unwrap(); + let window = self.cast_ref::().unwrap(); window.maybe_wait_on_main(move |w| { w.set_preferred_screen_edges_deferring_system_gestures(edges) }); @@ -240,19 +240,19 @@ impl WindowExtIOS for dyn Window + '_ { #[inline] fn set_prefers_status_bar_hidden(&self, hidden: bool) { - let window = self.as_any().downcast_ref::().unwrap(); + let window = self.cast_ref::().unwrap(); window.maybe_wait_on_main(move |w| w.set_prefers_status_bar_hidden(hidden)); } #[inline] fn set_preferred_status_bar_style(&self, status_bar_style: StatusBarStyle) { - let window = self.as_any().downcast_ref::().unwrap(); + let window = self.cast_ref::().unwrap(); window.maybe_wait_on_main(move |w| w.set_preferred_status_bar_style(status_bar_style)) } #[inline] fn recognize_pinch_gesture(&self, should_recognize: bool) { - let window = self.as_any().downcast_ref::().unwrap(); + let window = self.cast_ref::().unwrap(); window.maybe_wait_on_main(move |w| w.recognize_pinch_gesture(should_recognize)); } @@ -263,7 +263,7 @@ impl WindowExtIOS for dyn Window + '_ { minimum_number_of_touches: u8, maximum_number_of_touches: u8, ) { - let window = self.as_any().downcast_ref::().unwrap(); + let window = self.cast_ref::().unwrap(); window.maybe_wait_on_main(move |w| { w.recognize_pan_gesture( should_recognize, @@ -275,13 +275,13 @@ impl WindowExtIOS for dyn Window + '_ { #[inline] fn recognize_doubletap_gesture(&self, should_recognize: bool) { - let window = self.as_any().downcast_ref::().unwrap(); + let window = self.cast_ref::().unwrap(); window.maybe_wait_on_main(move |w| w.recognize_doubletap_gesture(should_recognize)); } #[inline] fn recognize_rotation_gesture(&self, should_recognize: bool) { - let window = self.as_any().downcast_ref::().unwrap(); + let window = self.cast_ref::().unwrap(); window.maybe_wait_on_main(move |w| w.recognize_rotation_gesture(should_recognize)); } } @@ -398,13 +398,13 @@ impl MonitorHandleExtIOS for MonitorHandle { fn ui_screen(&self) -> *mut c_void { // SAFETY: The marker is only used to get the pointer of the screen let mtm = unsafe { objc2::MainThreadMarker::new_unchecked() }; - let monitor = self.as_any().downcast_ref::().unwrap(); + let monitor = self.cast_ref::().unwrap(); objc2::rc::Retained::as_ptr(monitor.ui_screen(mtm)) as *mut c_void } #[inline] fn preferred_video_mode(&self) -> VideoMode { - let monitor = self.as_any().downcast_ref::().unwrap(); + let monitor = self.cast_ref::().unwrap(); monitor.preferred_video_mode() } } diff --git a/src/platform/macos.rs b/src/platform/macos.rs index fec09008..8be7afe4 100644 --- a/src/platform/macos.rs +++ b/src/platform/macos.rs @@ -168,109 +168,109 @@ pub trait WindowExtMacOS { impl WindowExtMacOS for dyn Window + '_ { #[inline] fn simple_fullscreen(&self) -> bool { - let window = self.as_any().downcast_ref::().unwrap(); + let window = self.cast_ref::().unwrap(); window.maybe_wait_on_main(|w| w.simple_fullscreen()) } #[inline] fn set_simple_fullscreen(&self, fullscreen: bool) -> bool { - let window = self.as_any().downcast_ref::().unwrap(); + let window = self.cast_ref::().unwrap(); window.maybe_wait_on_main(move |w| w.set_simple_fullscreen(fullscreen)) } #[inline] fn has_shadow(&self) -> bool { - let window = self.as_any().downcast_ref::().unwrap(); + let window = self.cast_ref::().unwrap(); window.maybe_wait_on_main(|w| w.has_shadow()) } #[inline] fn set_has_shadow(&self, has_shadow: bool) { - let window = self.as_any().downcast_ref::().unwrap(); + let window = self.cast_ref::().unwrap(); window.maybe_wait_on_main(move |w| w.set_has_shadow(has_shadow)); } #[inline] fn set_tabbing_identifier(&self, identifier: &str) { - let window = self.as_any().downcast_ref::().unwrap(); + let window = self.cast_ref::().unwrap(); window.maybe_wait_on_main(|w| w.set_tabbing_identifier(identifier)) } #[inline] fn tabbing_identifier(&self) -> String { - let window = self.as_any().downcast_ref::().unwrap(); + let window = self.cast_ref::().unwrap(); window.maybe_wait_on_main(|w| w.tabbing_identifier()) } #[inline] fn select_next_tab(&self) { - let window = self.as_any().downcast_ref::().unwrap(); + let window = self.cast_ref::().unwrap(); window.maybe_wait_on_main(|w| w.select_next_tab()); } #[inline] fn select_previous_tab(&self) { - let window = self.as_any().downcast_ref::().unwrap(); + let window = self.cast_ref::().unwrap(); window.maybe_wait_on_main(|w| w.select_previous_tab()); } #[inline] fn select_tab_at_index(&self, index: usize) { - let window = self.as_any().downcast_ref::().unwrap(); + let window = self.cast_ref::().unwrap(); window.maybe_wait_on_main(move |w| w.select_tab_at_index(index)); } #[inline] fn num_tabs(&self) -> usize { - let window = self.as_any().downcast_ref::().unwrap(); + let window = self.cast_ref::().unwrap(); window.maybe_wait_on_main(|w| w.num_tabs()) } #[inline] fn is_document_edited(&self) -> bool { - let window = self.as_any().downcast_ref::().unwrap(); + let window = self.cast_ref::().unwrap(); window.maybe_wait_on_main(|w| w.is_document_edited()) } #[inline] fn set_document_edited(&self, edited: bool) { - let window = self.as_any().downcast_ref::().unwrap(); + let window = self.cast_ref::().unwrap(); window.maybe_wait_on_main(move |w| w.set_document_edited(edited)); } #[inline] fn set_option_as_alt(&self, option_as_alt: OptionAsAlt) { - let window = self.as_any().downcast_ref::().unwrap(); + let window = self.cast_ref::().unwrap(); window.maybe_wait_on_main(move |w| w.set_option_as_alt(option_as_alt)); } #[inline] fn option_as_alt(&self) -> OptionAsAlt { - let window = self.as_any().downcast_ref::().unwrap(); + let window = self.cast_ref::().unwrap(); window.maybe_wait_on_main(|w| w.option_as_alt()) } #[inline] fn set_borderless_game(&self, borderless_game: bool) { - let window = self.as_any().downcast_ref::().unwrap(); + let window = self.cast_ref::().unwrap(); window.maybe_wait_on_main(|w| w.set_borderless_game(borderless_game)) } #[inline] fn is_borderless_game(&self) -> bool { - let window = self.as_any().downcast_ref::().unwrap(); + let window = self.cast_ref::().unwrap(); window.maybe_wait_on_main(|w| w.is_borderless_game()) } #[inline] fn set_unified_titlebar(&self, unified_titlebar: bool) { - let window = self.as_any().downcast_ref::().unwrap(); + let window = self.cast_ref::().unwrap(); window.maybe_wait_on_main(|w| w.set_unified_titlebar(unified_titlebar)) } #[inline] fn unified_titlebar(&self) -> bool { - let window = self.as_any().downcast_ref::().unwrap(); + let window = self.cast_ref::().unwrap(); window.maybe_wait_on_main(|w| w.unified_titlebar()) } } @@ -506,7 +506,7 @@ pub trait MonitorHandleExtMacOS { impl MonitorHandleExtMacOS for MonitorHandle { fn ns_screen(&self) -> Option<*mut c_void> { - let monitor = self.as_any().downcast_ref::().unwrap(); + let monitor = self.cast_ref::().unwrap(); // SAFETY: We only use the marker to get a pointer let mtm = unsafe { objc2::MainThreadMarker::new_unchecked() }; monitor.ns_screen(mtm).map(|s| objc2::rc::Retained::as_ptr(&s) as _) @@ -532,32 +532,28 @@ pub trait ActiveEventLoopExtMacOS { impl ActiveEventLoopExtMacOS for dyn ActiveEventLoop + '_ { fn hide_application(&self) { let event_loop = self - .as_any() - .downcast_ref::() + .cast_ref::() .expect("non macOS event loop on macOS"); event_loop.hide_application() } fn hide_other_applications(&self) { let event_loop = self - .as_any() - .downcast_ref::() + .cast_ref::() .expect("non macOS event loop on macOS"); event_loop.hide_other_applications() } fn set_allows_automatic_window_tabbing(&self, enabled: bool) { let event_loop = self - .as_any() - .downcast_ref::() + .cast_ref::() .expect("non macOS event loop on macOS"); event_loop.set_allows_automatic_window_tabbing(enabled); } fn allows_automatic_window_tabbing(&self) -> bool { let event_loop = self - .as_any() - .downcast_ref::() + .cast_ref::() .expect("non macOS event loop on macOS"); event_loop.allows_automatic_window_tabbing() } diff --git a/src/platform/startup_notify.rs b/src/platform/startup_notify.rs index 9f54422d..2364bf4d 100644 --- a/src/platform/startup_notify.rs +++ b/src/platform/startup_notify.rs @@ -75,15 +75,12 @@ impl EventLoopExtStartupNotify for dyn ActiveEventLoop + '_ { impl WindowExtStartupNotify for dyn Window + '_ { fn request_activation_token(&self) -> Result { #[cfg(wayland_platform)] - if let Some(window) = self.as_any().downcast_ref::() - { + if let Some(window) = self.cast_ref::() { return window.request_activation_token(); } #[cfg(x11_platform)] - if let Some(window) = - self.as_any().downcast_ref::() - { + if let Some(window) = self.cast_ref::() { return window.request_activation_token(); } diff --git a/src/platform/wayland.rs b/src/platform/wayland.rs index bcdcccbc..86f6731b 100644 --- a/src/platform/wayland.rs +++ b/src/platform/wayland.rs @@ -26,7 +26,7 @@ pub trait ActiveEventLoopExtWayland { impl ActiveEventLoopExtWayland for dyn ActiveEventLoop + '_ { #[inline] fn is_wayland(&self) -> bool { - self.as_any().downcast_ref::().is_some() + self.cast_ref::().is_some() } } diff --git a/src/platform/web.rs b/src/platform/web.rs index 59b7b30b..4f23a47f 100644 --- a/src/platform/web.rs +++ b/src/platform/web.rs @@ -105,29 +105,23 @@ pub trait WindowExtWeb { impl WindowExtWeb for dyn Window + '_ { #[inline] fn canvas(&self) -> Option> { - self.as_any() - .downcast_ref::() - .expect("non Web window on Web") - .canvas() + self.cast_ref::().expect("non Web window on Web").canvas() } fn prevent_default(&self) -> bool { - self.as_any() - .downcast_ref::() + self.cast_ref::() .expect("non Web window on Web") .prevent_default() } fn set_prevent_default(&self, prevent_default: bool) { - self.as_any() - .downcast_ref::() + self.cast_ref::() .expect("non Web window on Web") .set_prevent_default(prevent_default) } fn is_cursor_lock_raw(&self) -> bool { - self.as_any() - .downcast_ref::() + self.cast_ref::() .expect("non Web window on Web") .is_cursor_lock_raw() } @@ -371,8 +365,7 @@ impl ActiveEventLoopExtWeb for dyn ActiveEventLoop + '_ { #[inline] fn create_custom_cursor_async(&self, source: CustomCursorSource) -> CustomCursorFuture { let event_loop = self - .as_any() - .downcast_ref::() + .cast_ref::() .expect("non Web event loop on Web"); event_loop.create_custom_cursor_async(source) } @@ -380,8 +373,7 @@ impl ActiveEventLoopExtWeb for dyn ActiveEventLoop + '_ { #[inline] fn set_poll_strategy(&self, strategy: PollStrategy) { let event_loop = self - .as_any() - .downcast_ref::() + .cast_ref::() .expect("non Web event loop on Web"); event_loop.set_poll_strategy(strategy); } @@ -389,8 +381,7 @@ impl ActiveEventLoopExtWeb for dyn ActiveEventLoop + '_ { #[inline] fn poll_strategy(&self) -> PollStrategy { let event_loop = self - .as_any() - .downcast_ref::() + .cast_ref::() .expect("non Web event loop on Web"); event_loop.poll_strategy() } @@ -398,8 +389,7 @@ impl ActiveEventLoopExtWeb for dyn ActiveEventLoop + '_ { #[inline] fn set_wait_until_strategy(&self, strategy: WaitUntilStrategy) { let event_loop = self - .as_any() - .downcast_ref::() + .cast_ref::() .expect("non Web event loop on Web"); event_loop.set_wait_until_strategy(strategy); } @@ -407,8 +397,7 @@ impl ActiveEventLoopExtWeb for dyn ActiveEventLoop + '_ { #[inline] fn wait_until_strategy(&self) -> WaitUntilStrategy { let event_loop = self - .as_any() - .downcast_ref::() + .cast_ref::() .expect("non Web event loop on Web"); event_loop.wait_until_strategy() } @@ -416,8 +405,7 @@ impl ActiveEventLoopExtWeb for dyn ActiveEventLoop + '_ { #[inline] fn is_cursor_lock_raw(&self) -> bool { let event_loop = self - .as_any() - .downcast_ref::() + .cast_ref::() .expect("non Web event loop on Web"); event_loop.is_cursor_lock_raw() } @@ -425,8 +413,7 @@ impl ActiveEventLoopExtWeb for dyn ActiveEventLoop + '_ { #[inline] fn has_multiple_screens(&self) -> Result { let event_loop = self - .as_any() - .downcast_ref::() + .cast_ref::() .expect("non Web event loop on Web"); event_loop.has_multiple_screens() } @@ -434,8 +421,7 @@ impl ActiveEventLoopExtWeb for dyn ActiveEventLoop + '_ { #[inline] fn request_detailed_monitor_permission(&self) -> MonitorPermissionFuture { let event_loop = self - .as_any() - .downcast_ref::() + .cast_ref::() .expect("non Web event loop on Web"); MonitorPermissionFuture(event_loop.request_detailed_monitor_permission()) } @@ -443,8 +429,7 @@ impl ActiveEventLoopExtWeb for dyn ActiveEventLoop + '_ { #[inline] fn has_detailed_monitor_permission(&self) -> bool { let event_loop = self - .as_any() - .downcast_ref::() + .cast_ref::() .expect("non Web event loop on Web"); event_loop.has_detailed_monitor_permission() } @@ -696,28 +681,24 @@ pub trait MonitorHandleExtWeb { impl MonitorHandleExtWeb for dyn MonitorHandleProvider + '_ { fn is_internal(&self) -> Option { - self.as_any().downcast_ref::().unwrap().is_internal() + self.cast_ref::().unwrap().is_internal() } fn orientation(&self) -> OrientationData { - self.as_any().downcast_ref::().unwrap().orientation() + self.cast_ref::().unwrap().orientation() } fn request_lock(&self, orientation_lock: OrientationLock) -> OrientationLockFuture { - let future = self - .as_any() - .downcast_ref::() - .unwrap() - .request_lock(orientation_lock); + let future = self.cast_ref::().unwrap().request_lock(orientation_lock); OrientationLockFuture(future) } fn unlock(&self) -> Result<(), OrientationLockError> { - self.as_any().downcast_ref::().unwrap().unlock() + self.cast_ref::().unwrap().unlock() } fn is_detailed(&self) -> bool { - self.as_any().downcast_ref::().unwrap().is_detailed() + self.cast_ref::().unwrap().is_detailed() } } diff --git a/src/platform/windows.rs b/src/platform/windows.rs index bfefa729..7357040f 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -343,37 +343,37 @@ pub trait WindowExtWindows { impl WindowExtWindows for dyn Window + '_ { #[inline] fn set_enable(&self, enabled: bool) { - let window = self.as_any().downcast_ref::().unwrap(); + let window = self.cast_ref::().unwrap(); window.set_enable(enabled) } #[inline] fn set_taskbar_icon(&self, taskbar_icon: Option) { - let window = self.as_any().downcast_ref::().unwrap(); + let window = self.cast_ref::().unwrap(); window.set_taskbar_icon(taskbar_icon) } #[inline] fn set_skip_taskbar(&self, skip: bool) { - let window = self.as_any().downcast_ref::().unwrap(); + let window = self.cast_ref::().unwrap(); window.set_skip_taskbar(skip) } #[inline] fn set_undecorated_shadow(&self, shadow: bool) { - let window = self.as_any().downcast_ref::().unwrap(); + let window = self.cast_ref::().unwrap(); window.set_undecorated_shadow(shadow) } #[inline] fn set_system_backdrop(&self, backdrop_type: BackdropType) { - let window = self.as_any().downcast_ref::().unwrap(); + let window = self.cast_ref::().unwrap(); window.set_system_backdrop(backdrop_type) } #[inline] fn set_border_color(&self, color: Option) { - let window = self.as_any().downcast_ref::().unwrap(); + let window = self.cast_ref::().unwrap(); window.set_border_color(color.unwrap_or(Color::NONE)) } @@ -382,26 +382,26 @@ impl WindowExtWindows for dyn Window + '_ { // The windows docs don't mention NONE as a valid options but it works in practice and is // useful to circumvent the Windows option "Show accent color on title bars and // window borders" - let window = self.as_any().downcast_ref::().unwrap(); + let window = self.cast_ref::().unwrap(); window.set_title_background_color(color.unwrap_or(Color::NONE)) } #[inline] fn set_title_text_color(&self, color: Color) { - let window = self.as_any().downcast_ref::().unwrap(); + let window = self.cast_ref::().unwrap(); window.set_title_text_color(color) } #[inline] fn set_corner_preference(&self, preference: CornerPreference) { - let window = self.as_any().downcast_ref::().unwrap(); + let window = self.cast_ref::().unwrap(); window.set_corner_preference(preference) } unsafe fn window_handle_any_thread( &self, ) -> Result, rwh_06::HandleError> { - let window = self.as_any().downcast_ref::().unwrap(); + let window = self.cast_ref::().unwrap(); unsafe { let handle = window.rwh_06_no_thread_check()?; diff --git a/src/platform/x11.rs b/src/platform/x11.rs index 656df8a2..4443b3a0 100644 --- a/src/platform/x11.rs +++ b/src/platform/x11.rs @@ -91,7 +91,7 @@ pub trait ActiveEventLoopExtX11 { impl ActiveEventLoopExtX11 for dyn ActiveEventLoop + '_ { #[inline] fn is_x11(&self) -> bool { - self.as_any().downcast_ref::().is_some() + self.cast_ref::().is_some() } } diff --git a/src/platform_impl/apple/appkit/window_delegate.rs b/src/platform_impl/apple/appkit/window_delegate.rs index ef469dcb..1bd1ac5f 100644 --- a/src/platform_impl/apple/appkit/window_delegate.rs +++ b/src/platform_impl/apple/appkit/window_delegate.rs @@ -556,7 +556,7 @@ fn new_window( let screen = match attrs.fullscreen.clone() { Some(Fullscreen::Borderless(Some(monitor))) | Some(Fullscreen::Exclusive(monitor, _)) => { - let monitor = monitor.as_any().downcast_ref::().unwrap(); + let monitor = monitor.cast_ref::().unwrap(); monitor.ns_screen(mtm).or_else(|| NSScreen::mainScreen(mtm)) }, Some(Fullscreen::Borderless(None)) => NSScreen::mainScreen(mtm), @@ -1460,7 +1460,7 @@ impl WindowDelegate { if let Some(ref fullscreen) = fullscreen { let new_screen = match fullscreen { Fullscreen::Borderless(Some(monitor)) | Fullscreen::Exclusive(monitor, _) => { - let monitor = monitor.as_any().downcast_ref::().unwrap(); + let monitor = monitor.cast_ref::().unwrap(); monitor.ns_screen(mtm) }, Fullscreen::Borderless(None) => { @@ -1519,7 +1519,7 @@ impl WindowDelegate { cgerr(CGDisplayCapture(display_id)).unwrap(); } - let monitor = monitor.as_any().downcast_ref::().unwrap(); + let monitor = monitor.cast_ref::().unwrap(); let video_mode = match monitor.video_mode_handles().find(|mode| &mode.mode == video_mode) { Some(video_mode) => video_mode, @@ -1587,7 +1587,7 @@ impl WindowDelegate { toggle_fullscreen(self.window()); }, (Some(Fullscreen::Exclusive(monitor, _)), None) => { - let monitor = monitor.as_any().downcast_ref::().unwrap(); + let monitor = monitor.cast_ref::().unwrap(); restore_and_release_display(monitor); toggle_fullscreen(self.window()); }, @@ -1618,7 +1618,7 @@ impl WindowDelegate { ); app.setPresentationOptions(presentation_options); - let monitor = monitor.as_any().downcast_ref::().unwrap(); + let monitor = monitor.cast_ref::().unwrap(); restore_and_release_display(monitor); // Restore the normal window level following the Borderless fullscreen diff --git a/src/platform_impl/apple/uikit/window.rs b/src/platform_impl/apple/uikit/window.rs index ef414af4..a959f1b7 100644 --- a/src/platform_impl/apple/uikit/window.rs +++ b/src/platform_impl/apple/uikit/window.rs @@ -81,7 +81,7 @@ impl WinitUIWindow { match window_attributes.fullscreen.clone() { Some(Fullscreen::Exclusive(monitor, ref video_mode)) => { - let monitor = monitor.as_any().downcast_ref::().unwrap(); + let monitor = monitor.cast_ref::().unwrap(); let screen = monitor.ui_screen(mtm); if let Some(video_mode) = monitor.video_modes_handles().find(|mode| &mode.mode == video_mode) @@ -91,7 +91,7 @@ impl WinitUIWindow { this.setScreen(screen); }, Some(Fullscreen::Borderless(Some(ref monitor))) => { - let monitor = monitor.as_any().downcast_ref::().unwrap(); + let monitor = monitor.cast_ref::().unwrap(); let screen = monitor.ui_screen(mtm); this.setScreen(screen); }, @@ -306,7 +306,7 @@ impl Inner { let mtm = MainThreadMarker::new().unwrap(); let uiscreen = match &monitor { Some(Fullscreen::Exclusive(monitor, video_mode)) => { - let monitor = monitor.as_any().downcast_ref::().unwrap(); + let monitor = monitor.cast_ref::().unwrap(); let uiscreen = monitor.ui_screen(mtm); if let Some(video_mode) = monitor.video_modes_handles().find(|mode| &mode.mode == video_mode) @@ -316,7 +316,7 @@ impl Inner { uiscreen.clone() }, Some(Fullscreen::Borderless(Some(monitor))) => { - monitor.as_any().downcast_ref::().unwrap().ui_screen(mtm).clone() + monitor.cast_ref::().unwrap().ui_screen(mtm).clone() }, Some(Fullscreen::Borderless(None)) => { self.current_monitor_inner().ui_screen(mtm).clone() @@ -492,7 +492,7 @@ impl Window { let screen = match fullscreen { Some(Fullscreen::Exclusive(ref monitor, _)) | Some(Fullscreen::Borderless(Some(ref monitor))) => { - let monitor = monitor.as_any().downcast_ref::().unwrap(); + let monitor = monitor.cast_ref::().unwrap(); monitor.ui_screen(mtm) }, Some(Fullscreen::Borderless(None)) | None => &main_screen, diff --git a/src/platform_impl/linux/wayland/window/mod.rs b/src/platform_impl/linux/wayland/window/mod.rs index cbcac9e5..f76195d1 100644 --- a/src/platform_impl/linux/wayland/window/mod.rs +++ b/src/platform_impl/linux/wayland/window/mod.rs @@ -23,7 +23,6 @@ use crate::event::{Ime, WindowEvent}; use crate::event_loop::AsyncRequestSerial; use crate::monitor::{Fullscreen, MonitorHandle as CoreMonitorHandle}; use crate::platform_impl::wayland::output; -use crate::utils::AsAny; use crate::window::{ Cursor, CursorGrabMode, ImePurpose, ResizeDirection, Theme, UserAttentionType, Window as CoreWindow, WindowAttributes, WindowButtons, WindowId, WindowLevel, @@ -146,10 +145,7 @@ impl Window { #[cfg_attr(not(x11_platform), allow(clippy::bind_instead_of_map))] Some(Fullscreen::Borderless(monitor)) => { let output = monitor.as_ref().and_then(|monitor| { - monitor - .as_any() - .downcast_ref::() - .map(|handle| &handle.proxy) + monitor.cast_ref::().map(|handle| &handle.proxy) }); window.set_fullscreen(output) @@ -446,10 +442,7 @@ impl CoreWindow for Window { #[cfg_attr(not(x11_platform), allow(clippy::bind_instead_of_map))] Some(Fullscreen::Borderless(monitor)) => { let output = monitor.as_ref().and_then(|monitor| { - monitor - .as_any() - .downcast_ref::() - .map(|handle| &handle.proxy) + monitor.cast_ref::().map(|handle| &handle.proxy) }); self.window.set_fullscreen(output) diff --git a/src/platform_impl/linux/x11/window.rs b/src/platform_impl/linux/x11/window.rs index f4ee7bae..ac184981 100644 --- a/src/platform_impl/linux/x11/window.rs +++ b/src/platform_impl/linux/x11/window.rs @@ -1067,13 +1067,11 @@ impl UnownedWindow { let (monitor, video_mode): (Cow<'_, X11MonitorHandle>, Option<&VideoMode>) = match &fullscreen { Fullscreen::Exclusive(monitor, video_mode) => { - let monitor = - monitor.as_any().downcast_ref::().unwrap(); + let monitor = monitor.cast_ref::().unwrap(); (Cow::Borrowed(monitor), Some(video_mode)) }, Fullscreen::Borderless(Some(monitor)) => { - let monitor = - monitor.as_any().downcast_ref::().unwrap(); + let monitor = monitor.cast_ref::().unwrap(); (Cow::Borrowed(monitor), None) }, Fullscreen::Borderless(None) => { diff --git a/src/platform_impl/web/web_sys/fullscreen.rs b/src/platform_impl/web/web_sys/fullscreen.rs index 5c688604..95a21baa 100644 --- a/src/platform_impl/web/web_sys/fullscreen.rs +++ b/src/platform_impl/web/web_sys/fullscreen.rs @@ -65,7 +65,7 @@ pub(crate) fn request_fullscreen( return; } - let monitor = monitor.as_any().downcast_ref::().unwrap(); + let monitor = monitor.cast_ref::().unwrap(); if let Some(monitor) = monitor.detailed(main_thread) { let options: FullscreenOptions = Object::new().unchecked_into(); diff --git a/src/platform_impl/windows/window.rs b/src/platform_impl/windows/window.rs index b02c5592..e6aeec4a 100644 --- a/src/platform_impl/windows/window.rs +++ b/src/platform_impl/windows/window.rs @@ -790,7 +790,7 @@ impl CoreWindow for Window { // fullscreen match (&old_fullscreen, &fullscreen) { (_, Some(Fullscreen::Exclusive(monitor, video_mode))) => { - let monitor = monitor.as_any().downcast_ref::().unwrap(); + let monitor = monitor.cast_ref::().unwrap(); let video_mode = match monitor.video_mode_handles().find(|mode| &mode.mode == video_mode) { Some(monitor) => monitor, @@ -882,9 +882,9 @@ impl CoreWindow for Window { let monitor = match &fullscreen { Fullscreen::Exclusive(monitor, _) - | Fullscreen::Borderless(Some(monitor)) => Some(Cow::Borrowed( - monitor.as_any().downcast_ref::().unwrap(), - )), + | Fullscreen::Borderless(Some(monitor)) => { + Some(Cow::Borrowed(monitor.cast_ref::().unwrap())) + }, Fullscreen::Borderless(None) => None, }; diff --git a/src/utils.rs b/src/utils.rs index d43cc3f8..c0dcbcca 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -28,19 +28,71 @@ impl Deref for Lazy { } } -pub trait AsAny { - fn as_any(&self) -> &dyn Any; - fn as_any_mut(&mut self) -> &mut dyn Any; +// NOTE: This is `pub`, but isn't actually exposed outside the crate. +// NOTE: Marked as `#[doc(hidden)]` and underscored, because they can be quite difficult to use +// correctly, see discussion in #4160. +// FIXME: Remove and replace with a coercion once rust-lang/rust#65991 is in MSRV (1.86). +#[doc(hidden)] +pub trait AsAny: Any { + #[doc(hidden)] + fn __as_any(&self) -> &dyn Any; + #[doc(hidden)] + fn __as_any_mut(&mut self) -> &mut dyn Any; + #[doc(hidden)] + fn __into_any(self: Box) -> Box; } impl AsAny for T { #[inline(always)] - fn as_any(&self) -> &dyn Any { + fn __as_any(&self) -> &dyn Any { self } #[inline(always)] - fn as_any_mut(&mut self) -> &mut dyn Any { + fn __as_any_mut(&mut self) -> &mut dyn Any { + self + } + + #[inline(always)] + fn __into_any(self: Box) -> Box { self } } + +macro_rules! impl_dyn_casting { + ($trait:ident) => { + impl dyn $trait + '_ { + /// Downcast to the backend concrete type. + /// + /// Returns `None` if the object was not from that backend. + pub fn cast_ref(&self) -> Option<&T> { + let this: &dyn std::any::Any = self.__as_any(); + this.downcast_ref::() + } + + /// Mutable downcast to the backend concrete type. + /// + /// Returns `None` if the object was not from that backend. + pub fn cast_mut(&mut self) -> Option<&mut T> { + let this: &mut dyn std::any::Any = self.__as_any_mut(); + this.downcast_mut::() + } + + /// Owned downcast to the backend concrete type. + /// + /// Returns `Err` with `self` if the object was not from that backend. + pub fn cast(self: Box) -> Result, Box> { + let reference: &dyn std::any::Any = self.__as_any(); + if reference.is::() { + let this: Box = self.__into_any(); + // Unwrap is okay, we just checked the type of `self` is `T`. + Ok(this.downcast::().unwrap()) + } else { + Err(self) + } + } + } + }; +} + +pub(crate) use impl_dyn_casting; diff --git a/src/window.rs b/src/window.rs index f6f39c9c..8caf32de 100644 --- a/src/window.rs +++ b/src/window.rs @@ -12,7 +12,7 @@ use crate::error::RequestError; pub use crate::icon::{BadIcon, Icon}; use crate::monitor::{Fullscreen, MonitorHandle}; use crate::platform_impl::PlatformSpecificWindowAttributes; -use crate::utils::AsAny; +use crate::utils::{impl_dyn_casting, AsAny}; /// Identifier of a window. Unique for each window. /// @@ -1335,7 +1335,7 @@ pub trait Window: AsAny + Send + Sync + fmt::Debug { fn rwh_06_window_handle(&self) -> &dyn rwh_06::HasWindowHandle; } -impl dyn Window { +impl dyn Window + '_ { /// Create a new [`WindowAttributes`] which allows modifying the window's attributes before /// creation. pub fn default_attributes() -> WindowAttributes { @@ -1343,6 +1343,8 @@ impl dyn Window { } } +impl_dyn_casting!(Window); + impl PartialEq for dyn Window + '_ { fn eq(&self, other: &dyn Window) -> bool { self.id().eq(&other.id())