runmat_plot/gui/
single_window_manager.rs

1//! Single Window Manager - V8-caliber EventLoop management
2//!
3//! Ensures only one interactive plot window exists at a time, avoiding
4//! winit's "EventLoop can't be recreated" error.
5
6use crate::gui::window::WindowConfig;
7use crate::plots::Figure;
8use std::sync::atomic::{AtomicBool, Ordering};
9use std::sync::Mutex;
10
11/// Global flag to track if an EventLoop is currently active
12static WINDOW_ACTIVE: AtomicBool = AtomicBool::new(false);
13
14/// Thread-safe window manager for sequential plot display
15static WINDOW_MANAGER: Mutex<()> = Mutex::new(());
16
17/// Show a plot using a single, managed window approach
18pub fn show_plot_sequential(figure: Figure) -> Result<String, String> {
19    // Acquire exclusive access to the window system
20    let _guard = WINDOW_MANAGER
21        .lock()
22        .map_err(|_| "Window manager lock failed".to_string())?;
23
24    // Check if a window is already active
25    if WINDOW_ACTIVE.load(Ordering::Acquire) {
26        return Err("Another plot window is already open. Please close it first.".to_string());
27    }
28
29    // Mark window as active
30    WINDOW_ACTIVE.store(true, Ordering::Release);
31
32    let result = show_plot_internal(figure);
33
34    // Mark window as inactive when done
35    WINDOW_ACTIVE.store(false, Ordering::Release);
36
37    result
38}
39
40/// Internal function that actually creates and runs the window
41fn show_plot_internal(figure: Figure) -> Result<String, String> {
42    use crate::gui::PlotWindow;
43
44    // Create window directly on the current thread (main thread)
45    let config = WindowConfig::default();
46
47    // Use existing runtime or create one if none exists
48    let handle = tokio::runtime::Handle::try_current();
49
50    match handle {
51        Ok(handle) => {
52            // Use existing runtime
53            tokio::task::block_in_place(|| {
54                handle.block_on(async {
55                    let mut window = PlotWindow::new(config)
56                        .await
57                        .map_err(|e| format!("Failed to create plot window: {e}"))?;
58
59                    // Set the figure data
60                    window.set_figure(figure);
61
62                    // Run the window (this will consume the EventLoop)
63                    window
64                        .run()
65                        .await
66                        .map_err(|e| format!("Window execution failed: {e}"))?;
67
68                    Ok("Plot window closed successfully".to_string())
69                })
70            })
71        }
72        Err(_) => {
73            // No runtime available, create one
74            let rt = tokio::runtime::Runtime::new()
75                .map_err(|e| format!("Failed to create async runtime: {e}"))?;
76
77            rt.block_on(async {
78                let mut window = PlotWindow::new(config)
79                    .await
80                    .map_err(|e| format!("Failed to create plot window: {e}"))?;
81
82                // Set the figure data
83                window.set_figure(figure);
84
85                // Run the window (this will consume the EventLoop)
86                window
87                    .run()
88                    .await
89                    .map_err(|e| format!("Window execution failed: {e}"))?;
90
91                Ok("Plot window closed successfully".to_string())
92            })
93        }
94    }
95}
96
97/// Check if the window system is available
98pub fn is_window_available() -> bool {
99    !WINDOW_ACTIVE.load(Ordering::Acquire)
100}