zsui 0.3.0-alpha.2

Rust-first feature-gated native UI framework for Win32, AppKit, and Linux.
Documentation
use std::collections::HashMap;

use winit::{
    application::ApplicationHandler,
    dpi::{LogicalSize, Size},
    event::WindowEvent,
    event_loop::{ActiveEventLoop, EventLoop},
    window::{Window as WinitWindow, WindowAttributes, WindowId as WinitWindowId, WindowLevel},
};

#[path = "winit_smoke.rs"]
mod smoke;

use super::{DesktopRuntimeBackend, DesktopRuntimeRequest, DesktopSmokeRequest};
use crate::{
    DesktopCapabilities, HostCapabilities, NativeWindowSmokeRunReport, PlatformName, WindowSpec,
    ZsuiError, ZsuiResult,
};

#[derive(Default)]
pub(super) struct Backend;

impl DesktopRuntimeBackend for Backend {
    #[cfg(test)]
    fn backend_name(&self) -> &'static str {
        "winit_fallback"
    }

    fn run_event_loop(self, request: DesktopRuntimeRequest) -> ZsuiResult<()> {
        let _backend_owned_state = (
            request.draw_plans,
            request.view_runtimes,
            request.shell_runtimes,
        );
        if request.windows.is_empty() {
            return Ok(());
        }
        if request.windows.iter().any(|window| window.menu.is_some()) {
            return Err(ZsuiError::unsupported(
                "native_menu",
                "the first-pass Winit host does not implement a native application menu",
            ));
        }
        if !request.trays.is_empty() {
            return Err(ZsuiError::unsupported(
                "native_window_status_item",
                "the first-pass Winit host does not implement a native status item",
            ));
        }

        let event_loop = EventLoop::new()
            .map_err(|err| ZsuiError::host("native_window_event_loop", err.to_string()))?;
        let mut app = WinitNativeApp {
            specs: request.windows,
            windows: HashMap::new(),
            startup_error: None,
        };
        event_loop
            .run_app(&mut app)
            .map_err(|err| ZsuiError::host("native_window_event_loop", err.to_string()))?;

        if let Some(err) = app.startup_error {
            Err(ZsuiError::host("create_native_window", err))
        } else {
            Ok(())
        }
    }

    fn run_smoke_event_loop(
        self,
        request: DesktopSmokeRequest,
    ) -> ZsuiResult<NativeWindowSmokeRunReport> {
        smoke::run(request)
    }

    fn scaffold_capabilities(&self) -> HostCapabilities {
        match PlatformName::current() {
            PlatformName::Macos => HostCapabilities::macos_scaffold(),
            PlatformName::Linux => HostCapabilities::linux_scaffold(),
            platform => HostCapabilities::all_unsupported(platform),
        }
    }

    fn native_host_capabilities(&self) -> HostCapabilities {
        self.scaffold_capabilities()
    }

    fn desktop_capabilities(&self) -> DesktopCapabilities {
        DesktopCapabilities::all_unsupported(PlatformName::current())
    }

    fn native_proof_backend_name(&self) -> &'static str {
        "winit-fallback"
    }

    fn native_proof_typography(&self, typography_scale: f32) -> crate::NativeTypographyProfile {
        crate::NativeTypographyProfile::fallback(
            crate::ZsTypographyPlatformStyle::current(),
            typography_scale,
        )
    }

    fn capture_process_memory(
        &self,
        sample_point: &'static str,
    ) -> Option<crate::NativeProofProcessMemoryEvidence> {
        #[cfg(all(target_os = "linux", not(target_env = "ohos")))]
        {
            return super::process_memory::capture_linux(sample_point);
        }
        #[allow(unreachable_code)]
        {
            let _ = sample_point;
            None
        }
    }
}

struct WinitNativeApp {
    specs: Vec<WindowSpec>,
    windows: HashMap<WinitWindowId, WinitWindow>,
    startup_error: Option<String>,
}

impl ApplicationHandler for WinitNativeApp {
    fn resumed(&mut self, event_loop: &ActiveEventLoop) {
        if !self.windows.is_empty() {
            return;
        }

        for spec in &self.specs {
            let mut attributes = WindowAttributes::default()
                .with_title(spec.title.clone())
                .with_inner_size(Size::Logical(LogicalSize::new(
                    spec.width as f64,
                    spec.height as f64,
                )))
                .with_visible(spec.visible)
                .with_resizable(spec.resizable)
                .with_decorations(spec.decorations)
                .with_transparent(spec.transparent);

            if let (Some(width), Some(height)) = (spec.min_width, spec.min_height) {
                attributes = attributes.with_min_inner_size(Size::Logical(LogicalSize::new(
                    width as f64,
                    height as f64,
                )));
            }
            if spec.always_on_top {
                attributes = attributes.with_window_level(WindowLevel::AlwaysOnTop);
            }

            match event_loop.create_window(attributes) {
                Ok(window) => {
                    self.windows.insert(window.id(), window);
                }
                Err(err) => {
                    self.startup_error = Some(err.to_string());
                    event_loop.exit();
                    return;
                }
            }
        }
    }

    fn window_event(
        &mut self,
        event_loop: &ActiveEventLoop,
        window_id: WinitWindowId,
        event: WindowEvent,
    ) {
        if matches!(event, WindowEvent::CloseRequested) {
            self.windows.remove(&window_id);
            if self.windows.is_empty() {
                event_loop.exit();
            }
        }
    }
}