use bitflags::bitflags;
use dpi::{LogicalSize, PhysicalSize, Size};
use raw_window_handle::{HasWindowHandle, RawWindowHandle};
use std::error::Error;
use std::ffi::{c_ulong, c_void};
use std::num::{NonZeroIsize, NonZeroU32};
use std::ptr::NonNull;
pub use dpi;
use crate::context::gui::GuiContext;
pub struct SpawnedEditor<E: EditorHandle> {
pub handle: E,
pub window: E::Window,
}
pub trait HostCallbacks: 'static {
fn request_resize(&mut self, new_size: Size, scale_factor: f64) -> Result<(), Box<dyn Error>>;
fn destroyed(&mut self);
}
pub trait HostMainThreadCaller: Send + 'static {
fn call_main_thread(&mut self);
}
pub struct HostMethods {
pub callbacks: Box<dyn HostCallbacks>,
pub main_thread_caller: Box<dyn HostMainThreadCaller>,
}
pub trait EditorHandle: Send + 'static {
type Window;
type Error: Error;
fn run_until_closed(window: Self::Window) -> Result<(), Self::Error>;
fn set_parent(
&self,
parent: ParentWindowHandle,
window: &Self::Window,
) -> Result<(), Self::Error>;
fn show(&self, window: &Self::Window) -> Result<(), Self::Error>;
fn hide(&self, window: &Self::Window) -> Result<(), Self::Error>;
fn set_size(
&self,
new_size: PhysicalSize<u32>,
window: &Self::Window,
) -> Result<(), Self::Error>;
fn host_main_thread_callback(&self, window: &Self::Window);
fn adjust_size(
&self,
new_size: PhysicalSize<u32>,
window: &Self::Window,
) -> Option<PhysicalSize<u32>> {
let _ = new_size;
let _ = window;
None
}
fn set_fallback_scale_factor(
&self,
scale_factor: f64,
window: &Self::Window,
) -> Result<(), Self::Error> {
let _ = scale_factor;
let _ = window;
Ok(())
}
fn on_virtual_key_from_host(
&self,
key_code: VirtualKeyCode,
is_down: bool,
modifiers: Modifiers,
) -> bool {
let _ = key_code;
let _ = is_down;
let _ = modifiers;
false
}
fn state_changed(&self) {}
fn param_value_changed(&self, id: &str, normalized_value: f32);
fn param_modulation_changed(&self, id: &str, modulation_offset: f32);
}
pub trait Editor: Send {
type Handle: EditorHandle;
fn spawn(
&self,
parent: Option<ParentWindowHandle>,
wait_for_parent: bool,
fallback_scale_factor: Option<f64>,
gui_context: GuiContext,
host: Option<HostMethods>,
) -> Result<SpawnedEditor<Self::Handle>, Box<dyn Error>>;
fn size(&self) -> PhysicalSize<u32>;
fn resize_hint(&self) -> ResizeHint {
ResizeHint::default()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct DummyEditorError;
impl Error for DummyEditorError {}
impl std::fmt::Display for DummyEditorError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Plugin does not implement an editor")
}
}
impl EditorHandle for () {
type Window = ();
type Error = DummyEditorError;
fn run_until_closed(_window: Self::Window) -> Result<(), Self::Error> {
Err(DummyEditorError)
}
fn set_parent(
&self,
_parent: ParentWindowHandle,
_window: &Self::Window,
) -> Result<(), Self::Error> {
Err(DummyEditorError)
}
fn show(&self, _window: &Self::Window) -> Result<(), Self::Error> {
Err(DummyEditorError)
}
fn hide(&self, _window: &Self::Window) -> Result<(), Self::Error> {
Err(DummyEditorError)
}
fn host_main_thread_callback(&self, _window: &Self::Window) {}
fn set_size(
&self,
_new_size: PhysicalSize<u32>,
_window: &Self::Window,
) -> Result<(), Self::Error> {
Err(DummyEditorError)
}
fn param_value_changed(&self, _id: &str, _normalized_value: f32) {}
fn param_modulation_changed(&self, _id: &str, _modulation_offset: f32) {}
}
impl Editor for () {
type Handle = ();
fn spawn(
&self,
_parent: Option<ParentWindowHandle>,
_wait_for_parent: bool,
_fallback_scale_factor: Option<f64>,
_gui_context: GuiContext,
_host: Option<HostMethods>,
) -> Result<SpawnedEditor<Self::Handle>, Box<dyn Error>> {
Err(String::from("Plugin does not implement an editor").into())
}
fn size(&self) -> PhysicalSize<u32> {
PhysicalSize::default()
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum SizeConstraints {
Logical {
min_size: Option<LogicalSize<f32>>,
max_size: Option<LogicalSize<f32>>,
},
Physical {
min_size: Option<PhysicalSize<u32>>,
max_size: Option<PhysicalSize<u32>>,
},
}
impl SizeConstraints {
pub const fn min_logical_size(min_size: LogicalSize<f32>) -> Self {
Self::Logical {
min_size: Some(min_size),
max_size: None,
}
}
pub const fn min_physical_size(min_size: PhysicalSize<u32>) -> Self {
Self::Physical {
min_size: Some(min_size),
max_size: None,
}
}
pub const fn logical(
min_size: Option<LogicalSize<f32>>,
max_size: Option<LogicalSize<f32>>,
) -> Self {
Self::Logical { min_size, max_size }
}
pub const fn physical(
min_size: Option<PhysicalSize<u32>>,
max_size: Option<PhysicalSize<u32>>,
) -> Self {
Self::Physical { min_size, max_size }
}
}
impl Default for SizeConstraints {
fn default() -> Self {
Self::Logical {
min_size: None,
max_size: None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct ResizeHint {
pub can_resize: bool,
pub can_resize_horizontally: bool,
pub can_resize_vertically: bool,
pub preserve_aspect_ratio: bool,
pub aspect_ratio_width: u32,
pub aspect_ratio_height: u32,
pub size_constraints: SizeConstraints,
}
impl Default for ResizeHint {
fn default() -> Self {
Self::NON_RESIZABLE
}
}
impl ResizeHint {
pub const NON_RESIZABLE: Self = Self {
size_constraints: SizeConstraints::Logical {
min_size: None,
max_size: None,
},
can_resize: false,
can_resize_horizontally: false,
can_resize_vertically: false,
preserve_aspect_ratio: false,
aspect_ratio_width: 1,
aspect_ratio_height: 1,
};
pub const RESIZABLE: Self = Self {
can_resize: true,
can_resize_horizontally: true,
can_resize_vertically: true,
..Self::NON_RESIZABLE
};
pub const fn non_resizable() -> Self {
Self::NON_RESIZABLE
}
pub const fn resizable() -> Self {
Self::RESIZABLE
}
pub const fn with_min_logical_size(mut self, min_size: LogicalSize<f32>) -> Self {
self.size_constraints = SizeConstraints::Logical {
min_size: Some(min_size),
max_size: None,
};
self
}
pub const fn with_min_max_logical_size(
mut self,
min_size: Option<LogicalSize<f32>>,
max_size: Option<LogicalSize<f32>>,
) -> Self {
self.size_constraints = SizeConstraints::Logical { min_size, max_size };
self
}
pub const fn with_min_physical_size(mut self, min_size: PhysicalSize<u32>) -> Self {
self.size_constraints = SizeConstraints::Physical {
min_size: Some(min_size),
max_size: None,
};
self
}
pub const fn with_min_max_physical_size(
mut self,
min_size: Option<PhysicalSize<u32>>,
max_size: Option<PhysicalSize<u32>>,
) -> Self {
self.size_constraints = SizeConstraints::Physical { min_size, max_size };
self
}
pub const fn with_size_constraints(mut self, size_constraints: SizeConstraints) -> Self {
self.size_constraints = size_constraints;
self
}
pub const fn with_aspect_ratio(
mut self,
aspect_ratio_width: u32,
aspect_ratio_height: u32,
) -> Self {
assert!(aspect_ratio_width != 0);
assert!(aspect_ratio_height != 0);
self.aspect_ratio_width = aspect_ratio_width;
self.aspect_ratio_height = aspect_ratio_height;
self
}
pub fn is_size_valid(
&self,
new_size: PhysicalSize<u32>,
current_size: PhysicalSize<u32>,
scale_factor: f64,
) -> bool {
let adjusted_size = self.adjust_size(new_size, current_size, scale_factor);
new_size == adjusted_size
}
pub fn adjust_size(
&self,
mut new_size: PhysicalSize<u32>,
current_size: PhysicalSize<u32>,
scale_factor: f64,
) -> PhysicalSize<u32> {
if !self.can_resize {
return current_size;
}
let (min_phy_size, max_phy_size) = match self.size_constraints {
SizeConstraints::Logical { min_size, max_size } => (
min_size.map(|s| PhysicalSize {
width: (s.width as f64 * scale_factor).round() as u32,
height: (s.height as f64 * scale_factor).round() as u32,
}),
max_size.map(|s| PhysicalSize {
width: (s.width as f64 * scale_factor).round() as u32,
height: (s.height as f64 * scale_factor).round() as u32,
}),
),
SizeConstraints::Physical { min_size, max_size } => (min_size, max_size),
};
if let Some(min_size) = min_phy_size {
new_size.width = new_size.width.max(min_size.width);
new_size.height = new_size.height.max(min_size.height);
}
if let Some(max_size) = max_phy_size {
new_size.width = new_size.width.min(max_size.width);
new_size.height = new_size.height.min(max_size.height);
}
new_size.width = new_size.width.max(1);
new_size.height = new_size.height.max(1);
if self.preserve_aspect_ratio {
let adjusted_width = (new_size.height as f32 * self.aspect_ratio_width as f32
/ self.aspect_ratio_height as f32)
.round() as u32;
if let Some(min_size) = min_phy_size
&& adjusted_width < min_size.width
{
new_size = min_size;
} else if let Some(max_size) = max_phy_size
&& adjusted_width > max_size.width
{
new_size = max_size;
} else {
new_size.width = adjusted_width;
}
} else {
if !self.can_resize_horizontally {
new_size.width = current_size.width;
}
if !self.can_resize_vertically {
new_size.height = current_size.height;
}
}
new_size
}
}
#[derive(Debug, Clone, Copy)]
pub enum ParentWindowHandle {
XlibWindow(c_ulong),
XcbWindow(NonZeroU32),
AppKitNsView(NonNull<c_void>),
Win32Hwnd(NonZeroIsize),
}
impl HasWindowHandle for ParentWindowHandle {
fn window_handle(
&self,
) -> Result<raw_window_handle::WindowHandle<'_>, raw_window_handle::HandleError> {
let raw = match *self {
ParentWindowHandle::XlibWindow(window) => {
RawWindowHandle::Xlib(raw_window_handle::XlibWindowHandle::new(window))
}
ParentWindowHandle::XcbWindow(window) => {
RawWindowHandle::Xcb(raw_window_handle::XcbWindowHandle::new(window))
}
ParentWindowHandle::AppKitNsView(ns_view) => {
RawWindowHandle::AppKit(raw_window_handle::AppKitWindowHandle::new(ns_view))
}
ParentWindowHandle::Win32Hwnd(hwnd) => {
RawWindowHandle::Win32(raw_window_handle::Win32WindowHandle::new(hwnd))
}
};
Ok(unsafe { raw_window_handle::WindowHandle::borrow_raw(raw) })
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum VirtualKeyCode {
Backspace,
Tab,
Clear,
Return,
Pause,
Escape,
Space,
Next,
End,
Home,
ArrowLeft,
ArrowUp,
ArrowRight,
ArrowDown,
PageUp,
PageDown,
Select,
Print,
NumpadEnter,
Snapshot,
Insert,
Delete,
Help,
Numpad0,
Numpad1,
Numpad2,
Numpad3,
Numpad4,
Numpad5,
Numpad6,
Numpad7,
Numpad8,
Numpad9,
NumpadMultiply,
NumpadAdd,
NumpadSeparator,
NumpadSubtract,
NumpadDecimal,
NumpadDivide,
F1,
F2,
F3,
F4,
F5,
F6,
F7,
F8,
F9,
F10,
F11,
F12,
NumLock,
ScrollLock,
Shift,
Control,
Alt,
Equals,
ContextMenu,
MediaPlay,
MediaStop,
MediaPrevTrack,
MediaNextTrack,
VolumeUp,
VolumeDown,
F13,
F14,
F15,
F16,
F17,
F18,
F19,
F20,
F21,
F22,
F23,
F24,
Super,
}
bitflags! {
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
pub struct Modifiers: u8 {
const SHIFT = 1 << 0;
const ALT = 1 << 1;
const COMMAND = 1 << 2;
const CONTROL = 1 << 3;
}
}