use crate::gui::lifecycle::CloseSignal;
use crate::plots::Figure;
use std::sync::{Arc, Mutex, OnceLock};
#[derive(Debug, Clone)]
pub enum NativeWindowResult {
Success(String),
Error(String),
WindowClosed,
}
impl std::fmt::Display for NativeWindowResult {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
NativeWindowResult::Success(msg) => write!(f, "Success: {msg}"),
NativeWindowResult::Error(msg) => write!(f, "Error: {msg}"),
NativeWindowResult::WindowClosed => write!(f, "Window closed by user"),
}
}
}
pub struct NativeWindowManager {
is_initialized: bool,
}
impl Default for NativeWindowManager {
fn default() -> Self {
Self::new()
}
}
impl NativeWindowManager {
pub fn new() -> Self {
Self {
is_initialized: false,
}
}
pub fn initialize(&mut self) -> Result<(), String> {
if self.is_initialized {
return Ok(());
}
#[cfg(target_os = "macos")]
{
if !crate::gui::thread_manager::is_main_thread() {
return Err(
"Native window manager must be initialized on the main thread on macOS"
.to_string(),
);
}
}
self.is_initialized = true;
Ok(())
}
pub fn show_plot_native(&self, figure: Figure) -> Result<NativeWindowResult, String> {
self.show_plot_native_with_signal(figure, None)
}
pub fn show_plot_native_with_signal(
&self,
figure: Figure,
signal: Option<CloseSignal>,
) -> Result<NativeWindowResult, String> {
if !self.is_initialized {
return Err("Native window manager not initialized".to_string());
}
#[cfg(target_os = "macos")]
{
self.show_plot_main_thread(figure, signal)
}
#[cfg(not(target_os = "macos"))]
{
self.show_plot_threaded(figure, signal)
}
}
#[cfg(target_os = "macos")]
fn show_plot_main_thread(
&self,
figure: Figure,
signal: Option<CloseSignal>,
) -> Result<NativeWindowResult, String> {
use pollster;
let config = crate::gui::window::WindowConfig::default();
match pollster::block_on(crate::gui::PlotWindow::new(config)) {
Ok(mut window) => {
if let Some(sig) = signal {
window.install_close_signal(sig);
}
window.set_figure(figure);
match pollster::block_on(window.run()) {
Ok(_) => Ok(NativeWindowResult::Success(
"Plot window closed successfully".to_string(),
)),
Err(e) => Err(format!("Window runtime error: {e}")),
}
}
Err(e) => Err(format!("Failed to create plot window: {e}")),
}
}
#[cfg(not(target_os = "macos"))]
fn show_plot_threaded(
&self,
figure: Figure,
signal: Option<CloseSignal>,
) -> Result<NativeWindowResult, String> {
match crate::gui::show_plot_global_with_signal(figure, signal) {
Ok(result) => match result {
crate::gui::GuiOperationResult::Success(msg) => {
Ok(NativeWindowResult::Success(msg))
}
crate::gui::GuiOperationResult::Cancelled(_msg) => {
Ok(NativeWindowResult::WindowClosed)
}
crate::gui::GuiOperationResult::Error { message, .. } => {
Ok(NativeWindowResult::Error(message))
}
},
Err(result) => match result {
crate::gui::GuiOperationResult::Success(msg) => {
Ok(NativeWindowResult::Success(msg))
}
crate::gui::GuiOperationResult::Cancelled(_msg) => {
Ok(NativeWindowResult::WindowClosed)
}
crate::gui::GuiOperationResult::Error { message, .. } => Err(message),
},
}
}
}
static NATIVE_WINDOW_MANAGER: OnceLock<Arc<Mutex<NativeWindowManager>>> = OnceLock::new();
pub fn initialize_native_window() -> Result<(), String> {
let manager_mutex =
NATIVE_WINDOW_MANAGER.get_or_init(|| Arc::new(Mutex::new(NativeWindowManager::new())));
let mut manager = manager_mutex
.lock()
.map_err(|_| "Failed to acquire manager lock".to_string())?;
manager.initialize()
}
pub fn show_plot_native_window(figure: Figure) -> Result<String, String> {
show_plot_native_window_with_signal(figure, None)
}
pub fn show_plot_native_window_with_signal(
figure: Figure,
signal: Option<CloseSignal>,
) -> Result<String, String> {
let manager_mutex = NATIVE_WINDOW_MANAGER
.get()
.ok_or_else(|| "Native window system not initialized".to_string())?;
let manager = manager_mutex
.lock()
.map_err(|_| "Failed to acquire manager lock".to_string())?;
match manager.show_plot_native_with_signal(figure, signal) {
Ok(NativeWindowResult::Success(msg)) => Ok(msg),
Ok(NativeWindowResult::WindowClosed) => Ok("Plot window closed by user".to_string()),
Ok(NativeWindowResult::Error(msg)) => Err(msg),
Err(msg) => Err(msg),
}
}
pub fn is_native_window_available() -> bool {
NATIVE_WINDOW_MANAGER.get().is_some()
}