#[cfg(target_os = "macos")]
mod activation;
#[cfg(target_os = "macos")]
mod application_shell;
#[cfg(target_os = "macos")]
mod environment;
#[cfg(target_os = "macos")]
mod platform_menus;
pub use incular_desktop::RunError;
#[doc(hidden)]
#[must_use]
#[cfg(target_os = "macos")]
pub fn application_shell_capabilities(
system: incular_platform::NativeWindowSystem,
) -> incular_platform::ApplicationServiceCapabilities {
let shell = application_shell::MacosApplicationShell::default();
let mut capabilities = incular_platform::PlatformCapabilities::default();
shell.refine_capabilities(system, &mut capabilities);
capabilities.application_services
}
#[cfg(target_os = "macos")]
#[derive(Clone, Default)]
struct MacosDesktopPlatformServices {
application_shell: application_shell::MacosApplicationShell,
activation: activation::MacosActivationState,
menus: std::rc::Rc<platform_menus::MacosPlatformMenuDelegate>,
environment: environment::MacosEnvironmentState,
}
#[cfg(target_os = "macos")]
impl incular_desktop::DesktopPlatformServices for MacosDesktopPlatformServices {
fn application_shell(&self) -> &dyn incular_desktop::DesktopApplicationShellServices {
self
}
fn system_environment_preferences(&self) -> incular_platform::SystemEnvironmentPreferences {
self.environment.preferences()
}
fn application_active(&self) -> Option<bool> {
self.environment.application_active()
}
fn start_system_environment_watch(
&self,
_tokio: &dyn Fn() -> incular_runtime::TokioHandle,
wake: std::sync::Arc<dyn Fn() + Send + Sync>,
) {
self.environment.start_watch(wake);
}
fn take_system_environment_change(&self) -> bool {
self.environment.take_settings_change()
}
fn take_application_lifecycle_events(&self) -> Vec<incular_platform::PlatformLifecycle> {
self.environment.take_lifecycle_events()
}
fn start_application_activation_watch(
&self,
deliver: std::sync::Arc<dyn Fn(incular_platform::ApplicationActivation) + Send + Sync>,
) {
self.activation.start_watch(deliver);
}
fn external_file_drag_support(
&self,
system: incular_platform::NativeWindowSystem,
) -> incular_platform::CapabilitySupport {
if system == incular_platform::NativeWindowSystem::AppKit {
incular_platform::CapabilitySupport::Supported
} else {
incular_platform::CapabilitySupport::Unsupported
}
}
fn external_drag_position(
&self,
system: incular_platform::NativeWindowSystem,
window: &winit::window::Window,
) -> Option<winit::dpi::PhysicalPosition<f64>> {
if system != incular_platform::NativeWindowSystem::AppKit {
return None;
}
let native = appkit_window(window)?;
let view = native.contentView()?;
let point = unsafe { native.mouseLocationOutsideOfEventStream() };
let local = view.convertPoint_fromView(point, None);
let bounds = view.bounds();
let scale = window.scale_factor();
if !scale.is_finite() || scale <= 0.0 {
return None;
}
Some(winit::dpi::PhysicalPosition::new(
local.x * scale,
(bounds.size.height - local.y) * scale,
))
}
fn platform_menu_delegate(&self) -> std::rc::Rc<dyn incular_widgets::PlatformMenuDelegate> {
self.menus.clone()
}
fn flush_platform_menu_events(&self) {
self.menus.flush_events();
}
fn work_area_support(
&self,
system: incular_platform::NativeWindowSystem,
) -> incular_platform::CapabilitySupport {
if system == incular_platform::NativeWindowSystem::AppKit {
incular_platform::CapabilitySupport::Supported
} else {
incular_platform::CapabilitySupport::Unsupported
}
}
fn work_area(
&self,
monitor: &winit::monitor::MonitorHandle,
) -> Option<incular_platform::PhysicalScreenRect> {
use winit::platform::macos::MonitorHandleExtMacOS;
let screen = monitor.ns_screen()?.cast::<objc2_app_kit::NSScreen>();
let screen = unsafe { &*screen };
let frame = screen.frame();
let visible = screen.visibleFrame();
let scale = monitor.scale_factor();
if !scale.is_finite() || scale <= 0.0 {
return None;
}
let frame_max_x = frame.origin.x + frame.size.width;
let frame_max_y = frame.origin.y + frame.size.height;
let visible_max_x = visible.origin.x + visible.size.width;
let visible_max_y = visible.origin.y + visible.size.height;
let left = ((visible.origin.x - frame.origin.x).max(0.0) * scale).round();
let right = ((frame_max_x - visible_max_x).max(0.0) * scale).round();
let bottom = ((visible.origin.y - frame.origin.y).max(0.0) * scale).round();
let top = ((frame_max_y - visible_max_y).max(0.0) * scale).round();
let to_i64 = |value: f64| {
(value.is_finite() && value >= 0.0 && value <= i64::MAX as f64).then_some(value as i64)
};
let left = to_i64(left)?;
let right = to_i64(right)?;
let bottom = to_i64(bottom)?;
let top = to_i64(top)?;
let bounds_position = monitor.position();
let bounds_size = monitor.size();
let width = i64::from(bounds_size.width)
.checked_sub(left)?
.checked_sub(right)?;
let height = i64::from(bounds_size.height)
.checked_sub(top)?
.checked_sub(bottom)?;
Some(incular_platform::PhysicalScreenRect::new(
i32::try_from(i64::from(bounds_position.x).checked_add(left)?).ok()?,
i32::try_from(i64::from(bounds_position.y).checked_add(top)?).ok()?,
u32::try_from(width).ok()?,
u32::try_from(height).ok()?,
))
}
fn transient_support(
&self,
system: incular_platform::NativeWindowSystem,
_role: incular_config::TransientRole,
) -> incular_platform::CapabilitySupport {
if system == incular_platform::NativeWindowSystem::AppKit {
incular_platform::CapabilitySupport::Supported
} else {
incular_platform::CapabilitySupport::Unsupported
}
}
fn configure_transient_attributes(
&self,
_system: incular_platform::NativeWindowSystem,
_parent: &winit::window::Window,
_role: incular_config::TransientRole,
attributes: winit::window::WindowAttributes,
) -> winit::window::WindowAttributes {
attributes.with_active(false)
}
fn attach_transient(
&self,
system: incular_platform::NativeWindowSystem,
parent: &winit::window::Window,
popup: &winit::window::Window,
_role: incular_config::TransientRole,
) -> incular_platform::PlatformOperationResult {
if system != incular_platform::NativeWindowSystem::AppKit {
return Ok(());
}
let Some(parent_window) = appkit_window(parent) else {
return Err(incular_platform::PlatformOperationError::unavailable());
};
let Some(popup_window) = appkit_window(popup) else {
return Err(incular_platform::PlatformOperationError::unavailable());
};
unsafe {
parent_window.addChildWindow_ordered(
&popup_window,
objc2_app_kit::NSWindowOrderingMode::NSWindowAbove,
);
}
Ok(())
}
fn detach_transient(
&self,
system: incular_platform::NativeWindowSystem,
parent: &winit::window::Window,
popup: &winit::window::Window,
_role: incular_config::TransientRole,
) {
if system != incular_platform::NativeWindowSystem::AppKit {
return;
}
if let (Some(parent_window), Some(popup_window)) =
(appkit_window(parent), appkit_window(popup))
{
unsafe { parent_window.removeChildWindow(&popup_window) };
}
}
}
#[cfg(target_os = "macos")]
fn appkit_window(
window: &winit::window::Window,
) -> Option<objc2::rc::Retained<objc2_app_kit::NSWindow>> {
use raw_window_handle::RawWindowHandle;
let RawWindowHandle::AppKit(handle) =
incular_desktop::winit_adapter::raw_window_handles(window).window
else {
return None;
};
let view = unsafe { handle.ns_view.cast::<objc2_app_kit::NSView>().as_ref() };
view.window()
}
pub fn run_application(application: incular_runtime::Application) -> Result<(), RunError> {
#[cfg(target_os = "macos")]
{
incular_desktop::run_application_with_services(
application,
MacosDesktopPlatformServices::default(),
)
}
#[cfg(not(target_os = "macos"))]
incular_desktop::run_application(application)
}
pub fn run_window(
runtime: incular_runtime::Runtime,
on_action: impl FnMut(incular_widgets::internal::ActionId) + 'static,
) -> Result<(), RunError> {
#[cfg(target_os = "macos")]
{
incular_desktop::run_window_with_services(
runtime,
on_action,
MacosDesktopPlatformServices::default(),
)
}
#[cfg(not(target_os = "macos"))]
incular_desktop::run_window(runtime, on_action)
}
#[cfg(feature = "devtools")]
pub use incular_desktop::{
DevToolsLaunchMode, devtools_launch_mode_from, devtools_runner, devtools_ui_candidates,
};
#[cfg(target_os = "macos")]
impl incular_desktop::DesktopApplicationShellServices for MacosDesktopPlatformServices {
fn refine_application_shell_capabilities(
&self,
system: incular_platform::NativeWindowSystem,
capabilities: &mut incular_platform::PlatformCapabilities,
) {
self.application_shell
.refine_capabilities(system, capabilities);
}
fn start_application_shell_watch(
&self,
deliver: std::sync::Arc<dyn Fn(incular_runtime::NativeApplicationShellEvent) + Send + Sync>,
complete: std::sync::Arc<
dyn Fn(incular_runtime::NativeApplicationShellCompletion) + Send + Sync,
>,
) {
self.application_shell.start_watch(deliver, complete);
}
fn apply_application_shell_request(
&self,
system: incular_platform::NativeWindowSystem,
request: incular_runtime::NativeApplicationShellRequest,
target_window: Option<&winit::window::Window>,
) -> incular_runtime::NativeApplicationShellApplyResult {
self.application_shell.apply(system, request, target_window)
}
}