runmat_plot/gui/
native_window.rs

1//! Native cross-platform window management
2//!
3//! This module provides a robust, cross-platform native window implementation
4//! that properly handles platform-specific requirements (especially macOS EventLoop)
5//! while leveraging our world-class WGPU rendering engine.
6
7use crate::plots::Figure;
8use std::sync::{Arc, Mutex, OnceLock};
9
10/// Result from native window operations
11#[derive(Debug, Clone)]
12pub enum NativeWindowResult {
13    Success(String),
14    Error(String),
15    WindowClosed,
16}
17
18impl std::fmt::Display for NativeWindowResult {
19    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
20        match self {
21            NativeWindowResult::Success(msg) => write!(f, "Success: {msg}"),
22            NativeWindowResult::Error(msg) => write!(f, "Error: {msg}"),
23            NativeWindowResult::WindowClosed => write!(f, "Window closed by user"),
24        }
25    }
26}
27
28/// Native window manager that properly handles cross-platform requirements
29pub struct NativeWindowManager {
30    is_initialized: bool,
31}
32
33impl Default for NativeWindowManager {
34    fn default() -> Self {
35        Self::new()
36    }
37}
38
39impl NativeWindowManager {
40    /// Create a new native window manager
41    pub fn new() -> Self {
42        Self {
43            is_initialized: false,
44        }
45    }
46
47    /// Initialize the native window system
48    pub fn initialize(&mut self) -> Result<(), String> {
49        if self.is_initialized {
50            return Ok(());
51        }
52
53        // Verify main thread requirements on macOS
54        #[cfg(target_os = "macos")]
55        {
56            if !crate::gui::thread_manager::is_main_thread() {
57                return Err(
58                    "Native window manager must be initialized on the main thread on macOS"
59                        .to_string(),
60                );
61            }
62        }
63
64        self.is_initialized = true;
65        Ok(())
66    }
67
68    /// Show a plot using native window with proper cross-platform handling
69    pub fn show_plot_native(&self, figure: Figure) -> Result<NativeWindowResult, String> {
70        if !self.is_initialized {
71            return Err("Native window manager not initialized".to_string());
72        }
73
74        // On macOS, run directly on main thread
75        #[cfg(target_os = "macos")]
76        {
77            self.show_plot_main_thread(figure)
78        }
79
80        // On other platforms, can use thread-based approach
81        #[cfg(not(target_os = "macos"))]
82        {
83            self.show_plot_threaded(figure)
84        }
85    }
86
87    /// Show plot directly on main thread (macOS)
88    #[cfg(target_os = "macos")]
89    fn show_plot_main_thread(&self, figure: Figure) -> Result<NativeWindowResult, String> {
90        use pollster;
91
92        // Create and run the plot window directly using our WGPU rendering engine
93        let config = crate::gui::window::WindowConfig::default();
94        // We don't need a tokio runtime for the direct approach on macOS
95        // pollster handles the async execution for us
96
97        // Use pollster to block on the async PlotWindow creation
98        match pollster::block_on(crate::gui::PlotWindow::new(config)) {
99            Ok(mut window) => {
100                // Set the figure data
101                window.set_figure(figure);
102
103                // Run the window event loop (this will block until the window closes)
104                match pollster::block_on(window.run()) {
105                    Ok(_) => Ok(NativeWindowResult::Success(
106                        "Plot window closed successfully".to_string(),
107                    )),
108                    Err(e) => Err(format!("Window runtime error: {e}")),
109                }
110            }
111            Err(e) => Err(format!("Failed to create plot window: {e}")),
112        }
113    }
114
115    /// Show plot using threaded approach (non-macOS)
116    #[cfg(not(target_os = "macos"))]
117    fn show_plot_threaded(&self, figure: Figure) -> Result<NativeWindowResult, String> {
118        // For non-macOS platforms, we can use the existing thread-based approach
119        // This would use the thread_manager system
120        match crate::gui::show_plot_global(figure) {
121            Ok(result) => match result {
122                crate::gui::GuiOperationResult::Success(msg) => {
123                    Ok(NativeWindowResult::Success(msg))
124                }
125                crate::gui::GuiOperationResult::Cancelled(_msg) => {
126                    Ok(NativeWindowResult::WindowClosed)
127                }
128                crate::gui::GuiOperationResult::Error { message, .. } => {
129                    Ok(NativeWindowResult::Error(message))
130                }
131            },
132            Err(result) => match result {
133                crate::gui::GuiOperationResult::Success(msg) => {
134                    Ok(NativeWindowResult::Success(msg))
135                }
136                crate::gui::GuiOperationResult::Cancelled(_msg) => {
137                    Ok(NativeWindowResult::WindowClosed)
138                }
139                crate::gui::GuiOperationResult::Error { message, .. } => Err(message),
140            },
141        }
142    }
143}
144
145/// Global native window manager
146static NATIVE_WINDOW_MANAGER: OnceLock<Arc<Mutex<NativeWindowManager>>> = OnceLock::new();
147
148/// Initialize the native window system
149pub fn initialize_native_window() -> Result<(), String> {
150    let manager_mutex =
151        NATIVE_WINDOW_MANAGER.get_or_init(|| Arc::new(Mutex::new(NativeWindowManager::new())));
152
153    let mut manager = manager_mutex
154        .lock()
155        .map_err(|_| "Failed to acquire manager lock".to_string())?;
156
157    manager.initialize()
158}
159
160/// Show a plot using native window
161pub fn show_plot_native_window(figure: Figure) -> Result<String, String> {
162    let manager_mutex = NATIVE_WINDOW_MANAGER
163        .get()
164        .ok_or_else(|| "Native window system not initialized".to_string())?;
165
166    let manager = manager_mutex
167        .lock()
168        .map_err(|_| "Failed to acquire manager lock".to_string())?;
169
170    match manager.show_plot_native(figure) {
171        Ok(NativeWindowResult::Success(msg)) => Ok(msg),
172        Ok(NativeWindowResult::WindowClosed) => Ok("Plot window closed by user".to_string()),
173        Ok(NativeWindowResult::Error(msg)) => Err(msg),
174        Err(msg) => Err(msg),
175    }
176}
177
178/// Check if native window system is available
179pub fn is_native_window_available() -> bool {
180    NATIVE_WINDOW_MANAGER.get().is_some()
181}