runmat_plot/gui/
native_window.rs1use crate::gui::lifecycle::CloseSignal;
8use crate::plots::Figure;
9use std::sync::{Arc, Mutex, OnceLock};
10
11#[derive(Debug, Clone)]
13pub enum NativeWindowResult {
14 Success(String),
15 Error(String),
16 WindowClosed,
17}
18
19impl std::fmt::Display for NativeWindowResult {
20 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
21 match self {
22 NativeWindowResult::Success(msg) => write!(f, "Success: {msg}"),
23 NativeWindowResult::Error(msg) => write!(f, "Error: {msg}"),
24 NativeWindowResult::WindowClosed => write!(f, "Window closed by user"),
25 }
26 }
27}
28
29pub struct NativeWindowManager {
31 is_initialized: bool,
32}
33
34impl Default for NativeWindowManager {
35 fn default() -> Self {
36 Self::new()
37 }
38}
39
40impl NativeWindowManager {
41 pub fn new() -> Self {
43 Self {
44 is_initialized: false,
45 }
46 }
47
48 pub fn initialize(&mut self) -> Result<(), String> {
50 if self.is_initialized {
51 return Ok(());
52 }
53
54 #[cfg(target_os = "macos")]
56 {
57 if !crate::gui::thread_manager::is_main_thread() {
58 return Err(
59 "Native window manager must be initialized on the main thread on macOS"
60 .to_string(),
61 );
62 }
63 }
64
65 self.is_initialized = true;
66 Ok(())
67 }
68
69 pub fn show_plot_native(&self, figure: Figure) -> Result<NativeWindowResult, String> {
71 self.show_plot_native_with_signal(figure, None)
72 }
73
74 pub fn show_plot_native_with_signal(
75 &self,
76 figure: Figure,
77 signal: Option<CloseSignal>,
78 ) -> Result<NativeWindowResult, String> {
79 if !self.is_initialized {
80 return Err("Native window manager not initialized".to_string());
81 }
82
83 #[cfg(target_os = "macos")]
85 {
86 self.show_plot_main_thread(figure, signal)
87 }
88
89 #[cfg(not(target_os = "macos"))]
91 {
92 self.show_plot_threaded(figure, signal)
93 }
94 }
95
96 #[cfg(target_os = "macos")]
98 fn show_plot_main_thread(
99 &self,
100 figure: Figure,
101 signal: Option<CloseSignal>,
102 ) -> Result<NativeWindowResult, String> {
103 use pollster;
104
105 let config = crate::gui::window::WindowConfig::default();
107 match pollster::block_on(crate::gui::PlotWindow::new(config)) {
112 Ok(mut window) => {
113 if let Some(sig) = signal {
114 window.install_close_signal(sig);
115 }
116 window.set_figure(figure);
118
119 match pollster::block_on(window.run()) {
121 Ok(_) => Ok(NativeWindowResult::Success(
122 "Plot window closed successfully".to_string(),
123 )),
124 Err(e) => Err(format!("Window runtime error: {e}")),
125 }
126 }
127 Err(e) => Err(format!("Failed to create plot window: {e}")),
128 }
129 }
130
131 #[cfg(not(target_os = "macos"))]
133 fn show_plot_threaded(
134 &self,
135 figure: Figure,
136 signal: Option<CloseSignal>,
137 ) -> Result<NativeWindowResult, String> {
138 match crate::gui::show_plot_global_with_signal(figure, signal) {
141 Ok(result) => match result {
142 crate::gui::GuiOperationResult::Success(msg) => {
143 Ok(NativeWindowResult::Success(msg))
144 }
145 crate::gui::GuiOperationResult::Cancelled(_msg) => {
146 Ok(NativeWindowResult::WindowClosed)
147 }
148 crate::gui::GuiOperationResult::Error { message, .. } => {
149 Ok(NativeWindowResult::Error(message))
150 }
151 },
152 Err(result) => match result {
153 crate::gui::GuiOperationResult::Success(msg) => {
154 Ok(NativeWindowResult::Success(msg))
155 }
156 crate::gui::GuiOperationResult::Cancelled(_msg) => {
157 Ok(NativeWindowResult::WindowClosed)
158 }
159 crate::gui::GuiOperationResult::Error { message, .. } => Err(message),
160 },
161 }
162 }
163}
164
165static NATIVE_WINDOW_MANAGER: OnceLock<Arc<Mutex<NativeWindowManager>>> = OnceLock::new();
167
168pub fn initialize_native_window() -> Result<(), String> {
170 let manager_mutex =
171 NATIVE_WINDOW_MANAGER.get_or_init(|| Arc::new(Mutex::new(NativeWindowManager::new())));
172
173 let mut manager = manager_mutex
174 .lock()
175 .map_err(|_| "Failed to acquire manager lock".to_string())?;
176
177 manager.initialize()
178}
179
180pub fn show_plot_native_window(figure: Figure) -> Result<String, String> {
182 show_plot_native_window_with_signal(figure, None)
183}
184
185pub fn show_plot_native_window_with_signal(
186 figure: Figure,
187 signal: Option<CloseSignal>,
188) -> Result<String, String> {
189 let manager_mutex = NATIVE_WINDOW_MANAGER
190 .get()
191 .ok_or_else(|| "Native window system not initialized".to_string())?;
192
193 let manager = manager_mutex
194 .lock()
195 .map_err(|_| "Failed to acquire manager lock".to_string())?;
196
197 match manager.show_plot_native_with_signal(figure, signal) {
198 Ok(NativeWindowResult::Success(msg)) => Ok(msg),
199 Ok(NativeWindowResult::WindowClosed) => Ok("Plot window closed by user".to_string()),
200 Ok(NativeWindowResult::Error(msg)) => Err(msg),
201 Err(msg) => Err(msg),
202 }
203}
204
205pub fn is_native_window_available() -> bool {
207 NATIVE_WINDOW_MANAGER.get().is_some()
208}