use crate::EguiSettings;
use crate::EguiState;
use crossbeam::atomic::AtomicCell;
use egui::Context;
use egui::{Vec2, ViewportCommand};
use egui_baseview::baseview::{PhySize, Size, WindowHandle, WindowOpenOptions, WindowScalePolicy};
use egui_baseview::{EguiWindow, Queue};
use nice_plug_core::context::gui::GuiContext;
use nice_plug_core::context::gui::ParamSetter;
use nice_plug_core::editor::Editor;
use nice_plug_core::editor::ParentWindowHandle;
use parking_lot::Mutex;
use raw_window_handle::{HasRawWindowHandle, RawWindowHandle};
use std::sync::Arc;
use std::sync::atomic::Ordering;
pub(crate) struct EguiEditor<T> {
pub(crate) egui_state: Arc<EguiState>,
pub(crate) user_state: Arc<Mutex<T>>,
pub(crate) settings: Arc<EguiSettings>,
pub(crate) build: Arc<dyn Fn(&Context, &mut Queue, &mut T) + 'static + Send + Sync>,
pub(crate) update:
Arc<dyn Fn(&mut egui::Ui, &ParamSetter, &mut Queue, &mut T) + 'static + Send + Sync>,
pub(crate) scaling_factor: AtomicCell<Option<f32>>,
}
struct ParentWindowHandleAdapter(ParentWindowHandle);
unsafe impl HasRawWindowHandle for ParentWindowHandleAdapter {
fn raw_window_handle(&self) -> RawWindowHandle {
match self.0 {
ParentWindowHandle::X11Window(window) => {
let mut handle = raw_window_handle::XcbWindowHandle::empty();
handle.window = window;
RawWindowHandle::Xcb(handle)
}
ParentWindowHandle::AppKitNsView(ns_view) => {
let mut handle = raw_window_handle::AppKitWindowHandle::empty();
handle.ns_view = ns_view;
RawWindowHandle::AppKit(handle)
}
ParentWindowHandle::Win32Hwnd(hwnd) => {
let mut handle = raw_window_handle::Win32WindowHandle::empty();
handle.hwnd = hwnd;
RawWindowHandle::Win32(handle)
}
}
}
}
impl<T> Editor for EguiEditor<T>
where
T: 'static + Send,
{
fn spawn(
&self,
parent: ParentWindowHandle,
context: Arc<dyn GuiContext>,
) -> Box<dyn std::any::Any + Send> {
let build = self.build.clone();
let update = self.update.clone();
let state = self.user_state.clone();
let egui_state = self.egui_state.clone();
#[cfg(all(feature = "opengl", not(feature = "wgpu")))]
let gl_config = {
let is_x11 = matches!(&parent, ParentWindowHandle::X11Window(_));
let mut gl_config = self.settings.gl_config.clone();
if is_x11 && self.settings.enable_vsync_on_x11 {
gl_config.vsync = true;
}
gl_config
};
let (unscaled_width, unscaled_height) = self.egui_state.size();
let scaling_factor = self.scaling_factor.load();
#[cfg(all(feature = "opengl", not(feature = "wgpu")))]
let window_settings = WindowOpenOptions {
title: String::from("egui window"),
size: Size::new(unscaled_width as f64, unscaled_height as f64),
scale: scaling_factor
.map(|factor| WindowScalePolicy::ScaleFactor(factor as f64))
.unwrap_or(WindowScalePolicy::SystemScaleFactor),
gl_config: Some(gl_config),
};
#[cfg(feature = "wgpu")]
let window_settings = WindowOpenOptions {
title: String::from("egui window"),
size: Size::new(unscaled_width as f64, unscaled_height as f64),
scale: scaling_factor
.map(|factor| WindowScalePolicy::ScaleFactor(factor as f64))
.unwrap_or(WindowScalePolicy::SystemScaleFactor),
..Default::default()
};
let window = EguiWindow::open_parented(
&ParentWindowHandleAdapter(parent),
window_settings,
self.settings.graphics_config.clone(),
state,
move |egui_ctx, queue, state| build(egui_ctx, queue, &mut state.lock()),
move |egui_ctx, queue, state| {
let setter = ParamSetter::new(context.as_ref());
if let Some(new_size) = egui_state.requested_size.swap(None) {
if context.request_resize() {
let scale = egui_ctx.pixels_per_point();
queue.resize(PhySize::new(
(new_size.0 as f32 * scale).round() as u32,
(new_size.1 as f32 * scale).round() as u32,
));
egui_ctx.send_viewport_cmd(ViewportCommand::InnerSize(Vec2::new(
new_size.0 as f32,
new_size.1 as f32,
)));
egui_state.size.store(new_size);
}
}
egui_ctx.request_repaint();
(update)(egui_ctx, &setter, queue, &mut state.lock());
},
);
self.egui_state.open.store(true, Ordering::Release);
Box::new(EguiEditorHandle {
egui_state: self.egui_state.clone(),
window,
})
}
fn size(&self) -> (u32, u32) {
let new_size = self.egui_state.requested_size.load();
if let Some(new_size) = new_size {
new_size
} else {
self.egui_state.size()
}
}
fn set_scale_factor(&self, factor: f32) -> bool {
if self.egui_state.is_open() {
return false;
}
self.scaling_factor.store(Some(factor));
true
}
fn param_value_changed(&self, _id: &str, _normalized_value: f32) {
}
fn param_modulation_changed(&self, _id: &str, _modulation_offset: f32) {}
fn param_values_changed(&self) {
}
}
struct EguiEditorHandle {
egui_state: Arc<EguiState>,
window: WindowHandle,
}
unsafe impl Send for EguiEditorHandle {}
impl Drop for EguiEditorHandle {
fn drop(&mut self) {
self.egui_state.open.store(false, Ordering::Release);
self.window.close();
}
}