#![cfg(feature = "egui-widgets")]
use crate::error::{Error, Result};
use crate::plugin::Plugin;
use raw_window_handle::RawWindowHandle;
use std::sync::{Arc, Mutex};
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct EditorRect {
pub x: f32,
pub y: f32,
pub width: f32,
pub height: f32,
}
pub struct EmbeddedEditor {
plugin: Arc<Mutex<Plugin>>,
#[cfg(target_os = "macos")]
inner: macos::MacEmbed,
}
impl EmbeddedEditor {
pub fn embed(
plugin: Arc<Mutex<Plugin>>,
parent: RawWindowHandle,
rect: EditorRect,
) -> Result<Self> {
#[cfg(target_os = "macos")]
{
let inner = macos::MacEmbed::new(&plugin, parent, rect)?;
Ok(Self { plugin, inner })
}
#[cfg(not(target_os = "macos"))]
{
let _ = (&plugin, parent, rect);
Err(Error::Other(
"editor embedding is only implemented on macOS so far".to_string(),
))
}
}
pub fn set_rect(&self, rect: EditorRect) {
#[cfg(target_os = "macos")]
self.inner.set_rect(rect);
#[cfg(not(target_os = "macos"))]
let _ = rect;
}
pub fn close(self) {}
}
impl Drop for EmbeddedEditor {
fn drop(&mut self) {
if let Ok(mut p) = self.plugin.lock() {
let _ = p.close_editor();
}
}
}
#[cfg(target_os = "macos")]
mod macos {
use super::*;
use objc2::{rc::Retained, MainThreadMarker, MainThreadOnly};
use objc2_app_kit::NSView;
use objc2_foundation::{NSPoint, NSRect, NSSize};
pub struct MacEmbed {
parent: Retained<NSView>,
child: Retained<NSView>,
}
impl MacEmbed {
pub fn new(
plugin: &Arc<Mutex<Plugin>>,
parent: RawWindowHandle,
rect: EditorRect,
) -> Result<Self> {
let mtm = MainThreadMarker::new().ok_or_else(|| {
Error::Other("editor embedding must run on the main thread".to_string())
})?;
let RawWindowHandle::AppKit(h) = parent else {
return Err(Error::Other(
"expected an AppKit window handle for the parent".to_string(),
));
};
let parent: Retained<NSView> =
unsafe { Retained::retain(h.ns_view.as_ptr() as *mut NSView) }
.ok_or_else(|| Error::Other("null parent NSView".to_string()))?;
let frame = NSRect::new(
NSPoint::new(rect.x as f64, 0.0),
NSSize::new(rect.width as f64, rect.height as f64),
);
let child = NSView::initWithFrame(NSView::alloc(mtm), frame);
parent.addSubview(&child);
let handle = crate::plugin::WindowHandle::from_nsview(
Retained::as_ptr(&child) as *mut std::ffi::c_void
);
plugin
.lock()
.map_err(|_| Error::Other("plugin lock poisoned".to_string()))?
.open_editor(handle)?;
let embed = Self { parent, child };
embed.set_rect(rect);
Ok(embed)
}
pub fn set_rect(&self, rect: EditorRect) {
let flipped = self.parent.isFlipped();
let parent_height = self.parent.bounds().size.height;
let y = if flipped {
rect.y as f64
} else {
parent_height - (rect.y + rect.height) as f64
};
let frame = NSRect::new(
NSPoint::new(rect.x as f64, y),
NSSize::new(rect.width as f64, rect.height as f64),
);
self.child.setFrame(frame);
}
}
impl Drop for MacEmbed {
fn drop(&mut self) {
self.child.removeFromSuperview();
}
}
}