lgui-platform-winit 0.2.2

Winit desktop backend for LGUI
use super::*;

#[cfg(feature = "store")]
use lgui_store::StoreApplicationExt as _;

#[derive(Debug)]
pub struct WinitApplicationError(pub(super) String);

impl fmt::Display for WinitApplicationError {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        formatter.write_str(&self.0)
    }
}

impl std::error::Error for WinitApplicationError {}

impl From<winit::error::EventLoopError> for WinitApplicationError {
    fn from(error: winit::error::EventLoopError) -> Self {
        Self(error.to_string())
    }
}

#[derive(Clone, Copy, Debug)]
pub struct WinitApplication {
    pub(super) preference: GraphicsPreference,
}

impl WinitApplication {
    pub fn new(preference: GraphicsPreference) -> Self {
        Self { preference }
    }
}

impl ApplicationBackend for WinitApplication {
    type Error = WinitApplicationError;

    fn run(
        self,
        options: WindowOptions,
        view: AppView,
        context: ApplicationContext,
    ) -> Result<(), Self::Error> {
        if !graphics_preference_supported(self.preference) {
            return Err(WinitApplicationError(format!(
                "the winit backend does not support the explicit {} driver on this target",
                self.preference.as_str()
            )));
        }

        let event_loop = EventLoop::<WinitUserEvent>::with_user_event()
            .build()
            .map_err(|error| WinitApplicationError(error.to_string()))?;
        let proxy = event_loop.create_proxy();
        lgui_core::backend::install_window_commands(&context.windows(), {
            let proxy = proxy.clone();
            move |command| {
                let _ = proxy.send_event(WinitUserEvent::Window(command));
            }
        });
        let application_handle = ApplicationHandle::new(
            {
                let proxy = proxy.clone();
                move |task| {
                    let _ = proxy.send_event(WinitUserEvent::Task(task));
                }
            },
            {
                let proxy = proxy.clone();
                move || {
                    let _ = proxy.send_event(WinitUserEvent::RequestAllFrames);
                }
            },
        );
        context.resources().provide(application_handle.clone());
        #[cfg(feature = "images")]
        if context
            .try_resource::<lgui_assets::ImageCacheHandle>()
            .is_none()
        {
            let loader = context
                .try_resource::<lgui_assets::RemoteImageLoaderHandle>()
                .map(|loader| (*loader).clone())
                .unwrap_or_else(lgui_assets::http_image_loader);
            let image_wake = application_handle.clone();
            let budget = context.memory().options().budget.cache_bytes;
            let cache = lgui_assets::backend::async_image_cache(
                loader,
                move || image_wake.request_frame(),
                budget,
                context.memory().clone(),
            );
            context.resources().provide(cache);
        }
        #[cfg(feature = "store")]
        context.stores().set_wake({
            let proxy = proxy.clone();
            Arc::new(move || {
                let _ = proxy.send_event(WinitUserEvent::RequestAllFrames);
            })
        });
        #[cfg(all(feature = "notifications-win32", target_os = "windows"))]
        lgui_platform_win32::install_notification_service(&context)
            .map_err(|error| WinitApplicationError(error.to_string()))?;
        #[cfg(feature = "renderer-skia")]
        let _text_environment = lgui_core::backend::install_text_environment(
            &context,
            lgui_render_skia::skia_text_system_handle(),
        );

        let display = event_loop.owned_display_handle();
        let soft_context =
            Arc::new(SoftContext::new(display).map_err(|error| {
                WinitApplicationError(format!("create software display: {error}"))
            })?);
        let main_id = options.id.clone();
        let mut handler = WinitHost {
            initial: Some((options, view)),
            main_id,
            context,
            proxy,
            soft_context,
            windows: HashMap::new(),
            ids: HashMap::new(),
            scale_preference: None,
            preference: self.preference,
            #[cfg(all(feature = "tray-win32", target_os = "windows"))]
            tray_host: None,
            exit_requested: false,
            backgrounded: false,
        };
        event_loop.run_app(&mut handler)?;
        Ok(())
    }
}