Improve screen scaling behavior
This commit is contained in:
7
Cargo.lock
generated
7
Cargo.lock
generated
@@ -324,6 +324,8 @@ dependencies = [
|
|||||||
name = "ebu-dsp"
|
name = "ebu-dsp"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"femtovg",
|
||||||
|
"image",
|
||||||
"nih_plug",
|
"nih_plug",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -368,9 +370,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "femtovg"
|
name = "femtovg"
|
||||||
version = "0.18.1"
|
version = "0.19.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0530af3119be5658d8c1f7e69248d46e2c59e500dc2ef373cf25b355158ef101"
|
checksum = "be5d925785ad33d7b0ae2b445d9f157c3ab42ff3c515fff0b46d53d4a86c43c5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.10.0",
|
"bitflags 2.10.0",
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
@@ -384,6 +386,7 @@ dependencies = [
|
|||||||
"rgb",
|
"rgb",
|
||||||
"rustybuzz",
|
"rustybuzz",
|
||||||
"slotmap",
|
"slotmap",
|
||||||
|
"ttf-parser",
|
||||||
"unicode-bidi",
|
"unicode-bidi",
|
||||||
"unicode-segmentation",
|
"unicode-segmentation",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ nih_plug = { git = "https://github.com/robbert-vdh/nih-plug", version = "0.0.0",
|
|||||||
parking_lot = "0.12.5"
|
parking_lot = "0.12.5"
|
||||||
baseview = { git = "https://github.com/RustAudio/baseview", rev = "237d323c729f3aa99476ba3efa50129c5e86cad3", features = ["opengl"]}
|
baseview = { git = "https://github.com/RustAudio/baseview", rev = "237d323c729f3aa99476ba3efa50129c5e86cad3", features = ["opengl"]}
|
||||||
crossbeam = "0.8.4"
|
crossbeam = "0.8.4"
|
||||||
femtovg = "0.18.1"
|
femtovg = "0.19.3"
|
||||||
serde = { version = "1.0.228", features = ["derive"] }
|
serde = { version = "1.0.228", features = ["derive"] }
|
||||||
image = { version = "0.25.9", default-features = false, features = ["png"] }
|
image = { version = "0.25.9", default-features = false, features = ["png"] }
|
||||||
|
|
||||||
|
|||||||
@@ -4,4 +4,6 @@ version = "0.1.0"
|
|||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
femtovg = "0.19.3"
|
||||||
|
image = { version = "0.25.9", default-features = false, features = ["png"] }
|
||||||
nih_plug = { git = "https://github.com/robbert-vdh/nih-plug", version = "0.0.0", default-features = false }
|
nih_plug = { git = "https://github.com/robbert-vdh/nih-plug", version = "0.0.0", default-features = false }
|
||||||
|
|||||||
187
ebu-dsp/src/gui/mod.rs
Normal file
187
ebu-dsp/src/gui/mod.rs
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
use std::sync::{Arc, atomic::Ordering};
|
||||||
|
|
||||||
|
use femtovg::{Canvas, ImageFlags, ImageId, Paint, Path, renderer::OpenGl};
|
||||||
|
use nih_plug::prelude::AtomicF32;
|
||||||
|
|
||||||
|
use crate::{Rect, ScaledRect};
|
||||||
|
|
||||||
|
pub struct SpriteSheet {
|
||||||
|
scale_factor: Arc<AtomicF32>,
|
||||||
|
image: Result<ImageId, String>,
|
||||||
|
width: usize,
|
||||||
|
height: usize,
|
||||||
|
frame_width: usize,
|
||||||
|
frame_height: usize,
|
||||||
|
frames_x: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SpriteSheet {
|
||||||
|
pub fn empty() -> Self {
|
||||||
|
Self {
|
||||||
|
scale_factor: Arc::new(AtomicF32::new(1.0)),
|
||||||
|
image: Err("No image loaded".to_owned()),
|
||||||
|
width: 0,
|
||||||
|
height: 0,
|
||||||
|
frame_width: 0,
|
||||||
|
frame_height: 0,
|
||||||
|
frames_x: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn new(
|
||||||
|
canvas: &mut Canvas<OpenGl>,
|
||||||
|
data: &[u8],
|
||||||
|
scale_factor: Arc<AtomicF32>,
|
||||||
|
frame_width: usize,
|
||||||
|
frame_height: usize,
|
||||||
|
frames: usize,
|
||||||
|
) -> Self {
|
||||||
|
let image = canvas
|
||||||
|
.load_image_mem(data, ImageFlags::empty())
|
||||||
|
.map_err(|e| format!("{e:?}"));
|
||||||
|
if let Ok(image) = image {
|
||||||
|
let (width, height) = canvas.image_size(image).unwrap_or_default();
|
||||||
|
Self {
|
||||||
|
scale_factor,
|
||||||
|
image: Ok(image),
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
frame_width,
|
||||||
|
frame_height,
|
||||||
|
frames_x: width / frame_width,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Self {
|
||||||
|
scale_factor,
|
||||||
|
image,
|
||||||
|
width: 0,
|
||||||
|
height: 0,
|
||||||
|
frame_width,
|
||||||
|
frame_height,
|
||||||
|
frames_x: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn draw(&self, canvas: &mut Canvas<OpenGl>, x: f32, y: f32, frame: usize) {
|
||||||
|
let factor = self.scale_factor.load(Ordering::Relaxed);
|
||||||
|
|
||||||
|
let frame_x = frame % self.frames_x;
|
||||||
|
let frame_y = frame / self.frames_x;
|
||||||
|
|
||||||
|
let screen_rect = Rect {
|
||||||
|
x: x * factor,
|
||||||
|
y: y * factor,
|
||||||
|
width: self.frame_width as f32 * factor,
|
||||||
|
height: self.frame_height as f32 * factor,
|
||||||
|
};
|
||||||
|
let image_rect = Rect {
|
||||||
|
x: screen_rect.x - (frame_x * self.frame_width) as f32 * factor,
|
||||||
|
y: screen_rect.y - (frame_y * self.frame_height) as f32 * factor,
|
||||||
|
width: self.width as f32 * factor,
|
||||||
|
height: self.height as f32 * factor,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut screen_path = Path::new();
|
||||||
|
screen_path.rect(
|
||||||
|
screen_rect.x,
|
||||||
|
screen_rect.y,
|
||||||
|
screen_rect.width,
|
||||||
|
screen_rect.height,
|
||||||
|
);
|
||||||
|
if let Ok(image) = self.image {
|
||||||
|
canvas.fill_path(
|
||||||
|
&screen_path,
|
||||||
|
&Paint::image(
|
||||||
|
image,
|
||||||
|
image_rect.x,
|
||||||
|
image_rect.y,
|
||||||
|
image_rect.width,
|
||||||
|
image_rect.height,
|
||||||
|
0.0,
|
||||||
|
1.0,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn screen_bounds(&self, x: f32, y: f32) -> ScaledRect {
|
||||||
|
ScaledRect::new_from(
|
||||||
|
self.scale_factor.clone(),
|
||||||
|
(x, y, self.frame_width as f32, self.frame_height as f32),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Sprite {
|
||||||
|
scale_factor: Arc<AtomicF32>,
|
||||||
|
image: Result<ImageId, String>,
|
||||||
|
width: usize,
|
||||||
|
height: usize,
|
||||||
|
}
|
||||||
|
impl Sprite {
|
||||||
|
pub fn empty() -> Self {
|
||||||
|
Self {
|
||||||
|
scale_factor: Arc::new(AtomicF32::new(1.0)),
|
||||||
|
image: Err("No image loaded".to_owned()),
|
||||||
|
width: 0,
|
||||||
|
height: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn new(canvas: &mut Canvas<OpenGl>, data: &[u8], scale_factor: Arc<AtomicF32>) -> Self {
|
||||||
|
let image = canvas
|
||||||
|
.load_image_mem(data, ImageFlags::empty())
|
||||||
|
.map_err(|e| format!("{e:?}"));
|
||||||
|
if let Ok(image) = image {
|
||||||
|
let (width, height) = canvas.image_size(image).unwrap_or_default();
|
||||||
|
Self {
|
||||||
|
scale_factor,
|
||||||
|
image: Ok(image),
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Self {
|
||||||
|
scale_factor,
|
||||||
|
image,
|
||||||
|
width: 0,
|
||||||
|
height: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn draw(&self, canvas: &mut Canvas<OpenGl>, x: f32, y: f32, alpha: f32) {
|
||||||
|
let factor = self.scale_factor.load(Ordering::Relaxed);
|
||||||
|
|
||||||
|
let screen_rect = Rect {
|
||||||
|
x: x * factor,
|
||||||
|
y: y * factor,
|
||||||
|
width: self.width as f32 * factor,
|
||||||
|
height: self.height as f32 * factor,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut screen_path = Path::new();
|
||||||
|
screen_path.rect(
|
||||||
|
screen_rect.x,
|
||||||
|
screen_rect.y,
|
||||||
|
screen_rect.width,
|
||||||
|
screen_rect.height,
|
||||||
|
);
|
||||||
|
if let Ok(image) = self.image {
|
||||||
|
canvas.fill_path(
|
||||||
|
&screen_path,
|
||||||
|
&Paint::image(
|
||||||
|
image,
|
||||||
|
screen_rect.x,
|
||||||
|
screen_rect.y,
|
||||||
|
screen_rect.width,
|
||||||
|
screen_rect.height,
|
||||||
|
0.0,
|
||||||
|
alpha,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn screen_bounds(&self, x: f32, y: f32) -> ScaledRect {
|
||||||
|
ScaledRect::new_from(
|
||||||
|
self.scale_factor.clone(),
|
||||||
|
(x, y, self.width as f32, self.height as f32),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,13 +4,16 @@ mod comp;
|
|||||||
mod decibel;
|
mod decibel;
|
||||||
mod eq;
|
mod eq;
|
||||||
mod freq_split;
|
mod freq_split;
|
||||||
|
mod gui;
|
||||||
mod meter;
|
mod meter;
|
||||||
mod ring_buffer;
|
mod ring_buffer;
|
||||||
mod smoother;
|
mod smoother;
|
||||||
mod traits;
|
mod traits;
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
fmt::Debug, ops::Add, time::Duration
|
fmt::Debug,
|
||||||
|
sync::{Arc, atomic::Ordering},
|
||||||
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use amplitude::Amplitude;
|
pub use amplitude::Amplitude;
|
||||||
@@ -19,42 +22,159 @@ pub use comp::{Compressor, CompressorState};
|
|||||||
pub use decibel::Decibel;
|
pub use decibel::Decibel;
|
||||||
pub use eq::{Equalizer, EqualizerState};
|
pub use eq::{Equalizer, EqualizerState};
|
||||||
pub use freq_split::{FreqSplitter, FreqSplitterState};
|
pub use freq_split::{FreqSplitter, FreqSplitterState};
|
||||||
|
pub use gui::{SpriteSheet, Sprite};
|
||||||
|
use nih_plug::prelude::AtomicF32;
|
||||||
pub use traits::{FloatFormatter, IntFormatter, Lerp, Processor};
|
pub use traits::{FloatFormatter, IntFormatter, Lerp, Processor};
|
||||||
|
|
||||||
pub struct Rect<T> {
|
#[derive(Copy, Clone, Debug, Default, PartialEq)]
|
||||||
pub x: T,
|
pub struct Point {
|
||||||
pub y: T,
|
x: f32,
|
||||||
pub width: T,
|
y: f32,
|
||||||
pub height: T,
|
|
||||||
}
|
}
|
||||||
impl<T: Debug> Debug for Rect<T> {
|
impl Point {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
pub const fn new(x: f32, y: f32) -> Self {
|
||||||
f.debug_struct("Rect")
|
Self { x, y }
|
||||||
.field("x", &self.x)
|
|
||||||
.field("y", &self.y)
|
|
||||||
.field("width", &self.width)
|
|
||||||
.field("height", &self.height)
|
|
||||||
.finish()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<T: PartialOrd + Add<Output = T> + Copy> Rect<T> {
|
impl From<(f32, f32)> for Point {
|
||||||
pub fn contains(&self, value: (T, T)) -> bool {
|
fn from(value: (f32, f32)) -> Self {
|
||||||
value.0 >= self.x
|
Point {
|
||||||
&& value.1 >= self.y
|
x: value.0,
|
||||||
&& value.0 < self.x + self.width
|
y: value.1,
|
||||||
&& value.1 < self.y + self.height
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<T> Default for Rect<T>
|
|
||||||
where
|
#[derive(Clone, Debug, Default)]
|
||||||
T: Default,
|
pub struct ScaledPoint {
|
||||||
{
|
pub factor: Arc<AtomicF32>,
|
||||||
fn default() -> Self {
|
pub x: f32,
|
||||||
|
pub y: f32,
|
||||||
|
}
|
||||||
|
impl PartialEq for ScaledPoint {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.factor.load(Ordering::Acquire) == other.factor.load(Ordering::Acquire)
|
||||||
|
&& self.x == other.x
|
||||||
|
&& self.y == other.y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl ScaledPoint {
|
||||||
|
pub fn new(factor: Arc<AtomicF32>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
x: Default::default(),
|
factor,
|
||||||
y: Default::default(),
|
..Default::default()
|
||||||
width: Default::default(),
|
}
|
||||||
height: Default::default(),
|
}
|
||||||
|
pub fn new_from<P>(factor: Arc<AtomicF32>, point: P) -> Self
|
||||||
|
where
|
||||||
|
P: Into<Point>,
|
||||||
|
{
|
||||||
|
let point = point.into();
|
||||||
|
Self {
|
||||||
|
factor,
|
||||||
|
x: point.x,
|
||||||
|
y: point.y,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn set<P>(&mut self, point: P)
|
||||||
|
where
|
||||||
|
P: Into<Point>,
|
||||||
|
{
|
||||||
|
let point = point.into();
|
||||||
|
self.x = point.x;
|
||||||
|
self.y = point.y;
|
||||||
|
}
|
||||||
|
pub fn set_scaled<P>(&mut self, point: P)
|
||||||
|
where
|
||||||
|
P: Into<Point>,
|
||||||
|
{
|
||||||
|
let factor = self.factor.load(Ordering::Acquire);
|
||||||
|
let point = point.into();
|
||||||
|
self.x = point.x / factor;
|
||||||
|
self.y = point.y / factor;
|
||||||
|
}
|
||||||
|
pub fn as_point(&self) -> Point {
|
||||||
|
let factor = self.factor.load(Ordering::Acquire);
|
||||||
|
Point {
|
||||||
|
x: self.x * factor,
|
||||||
|
y: self.y * factor,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Default, PartialEq)]
|
||||||
|
pub struct Rect {
|
||||||
|
pub x: f32,
|
||||||
|
pub y: f32,
|
||||||
|
pub width: f32,
|
||||||
|
pub height: f32,
|
||||||
|
}
|
||||||
|
impl Rect {
|
||||||
|
pub fn contains<P>(&self, value: P) -> bool
|
||||||
|
where
|
||||||
|
P: Into<Point>,
|
||||||
|
{
|
||||||
|
let value = value.into();
|
||||||
|
value.x >= self.x
|
||||||
|
&& value.y >= self.y
|
||||||
|
&& value.x < self.x + self.width
|
||||||
|
&& value.y < self.y + self.height
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<(f32, f32, f32, f32)> for Rect {
|
||||||
|
fn from(value: (f32, f32, f32, f32)) -> Self {
|
||||||
|
Rect {
|
||||||
|
x: value.0,
|
||||||
|
y: value.1,
|
||||||
|
width: value.2,
|
||||||
|
height: value.3,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[derive(Clone, Debug, Default)]
|
||||||
|
pub struct ScaledRect {
|
||||||
|
pub factor: Arc<AtomicF32>,
|
||||||
|
pub x: f32,
|
||||||
|
pub y: f32,
|
||||||
|
pub width: f32,
|
||||||
|
pub height: f32,
|
||||||
|
}
|
||||||
|
impl PartialEq for ScaledRect {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.factor.load(Ordering::Acquire) == other.factor.load(Ordering::Acquire)
|
||||||
|
&& self.x == other.x
|
||||||
|
&& self.y == other.y
|
||||||
|
&& self.width == other.width
|
||||||
|
&& self.height == other.height
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl ScaledRect {
|
||||||
|
pub fn new(factor: Arc<AtomicF32>) -> Self {
|
||||||
|
Self {
|
||||||
|
factor,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn new_from<R>(factor: Arc<AtomicF32>, rect: R) -> Self
|
||||||
|
where
|
||||||
|
R: Into<Rect>,
|
||||||
|
{
|
||||||
|
let rect = rect.into();
|
||||||
|
Self {
|
||||||
|
factor,
|
||||||
|
x: rect.x,
|
||||||
|
y: rect.y,
|
||||||
|
width: rect.width,
|
||||||
|
height: rect.height,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn as_rect(&self) -> Rect {
|
||||||
|
let factor = self.factor.load(Ordering::Acquire);
|
||||||
|
Rect {
|
||||||
|
x: self.x * factor,
|
||||||
|
y: self.y * factor,
|
||||||
|
width: self.width * factor,
|
||||||
|
height: self.height * factor,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -194,4 +314,3 @@ pub fn rms<const N: usize>(sample_buffer: &RingBuffer<f64, N>, last_rms: f64) ->
|
|||||||
last_rms.powf(2.0)
|
last_rms.powf(2.0)
|
||||||
+ (1.0 / N as f64) * (sample_buffer[0].powf(2.0) - sample_buffer[N - 1].powf(2.0))
|
+ (1.0 / N as f64) * (sample_buffer[0].powf(2.0) - sample_buffer[N - 1].powf(2.0))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ use crossbeam::atomic::AtomicCell;
|
|||||||
use nih_plug::params::persist::PersistentField;
|
use nih_plug::params::persist::PersistentField;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::window::EditorWindow;
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct EditorState {
|
pub struct EditorState {
|
||||||
/// The window's size in logical pixels before applying `scale_factor`.
|
/// The window's size in logical pixels before applying `scale_factor`.
|
||||||
@@ -20,7 +22,10 @@ pub struct EditorState {
|
|||||||
impl EditorState {
|
impl EditorState {
|
||||||
pub fn from_size(size: (u32, u32)) -> Arc<Self> {
|
pub fn from_size(size: (u32, u32)) -> Arc<Self> {
|
||||||
Arc::new(Self {
|
Arc::new(Self {
|
||||||
size: AtomicCell::new(size),
|
size: AtomicCell::new((
|
||||||
|
(size.0 as f32 * EditorWindow::VIRTUAL_SCALE) as u32,
|
||||||
|
(size.1 as f32 * EditorWindow::VIRTUAL_SCALE) as u32,
|
||||||
|
)),
|
||||||
open: AtomicBool::new(false),
|
open: AtomicBool::new(false),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -47,7 +52,7 @@ impl<'a> PersistentField<'a, EditorState> for Arc<EditorState> {
|
|||||||
|
|
||||||
pub struct EditorHandle {
|
pub struct EditorHandle {
|
||||||
pub state: Arc<EditorState>,
|
pub state: Arc<EditorState>,
|
||||||
pub window: WindowHandle
|
pub window: WindowHandle,
|
||||||
}
|
}
|
||||||
unsafe impl Send for EditorHandle {}
|
unsafe impl Send for EditorHandle {}
|
||||||
impl Drop for EditorHandle {
|
impl Drop for EditorHandle {
|
||||||
|
|||||||
194
src/gui.rs
194
src/gui.rs
@@ -1,40 +1,38 @@
|
|||||||
use crate::{parameters::PluginParams, window::EditorWindow};
|
use crate::parameters::PluginParams;
|
||||||
use baseview::{
|
use baseview::{
|
||||||
Event, EventStatus, MouseButton, MouseEvent, WindowEvent, WindowHandler, gl::GlContext,
|
Event, EventStatus, MouseButton, MouseEvent, WindowEvent, WindowHandler, gl::GlContext,
|
||||||
};
|
};
|
||||||
use crossbeam::atomic::AtomicCell;
|
use ebu_dsp::{ScaledPoint, ScaledRect, Sprite, SpriteSheet};
|
||||||
use ebu_dsp::Rect;
|
use femtovg::{Canvas, Color, FontId, Paint, Path, renderer::OpenGl};
|
||||||
use femtovg::{Canvas, Color, FontId, ImageFlags, ImageId, Paint, Path, renderer::OpenGl};
|
|
||||||
use nih_plug::prelude::*;
|
use nih_plug::prelude::*;
|
||||||
use std::sync::Arc;
|
use std::sync::{Arc, atomic::Ordering};
|
||||||
|
|
||||||
const DROID_SANS_FONT: &'static [u8] = include_bytes!("../assets/DroidSans.ttf");
|
const DROID_SANS_FONT: &'static [u8] = include_bytes!("../assets/DroidSans.ttf");
|
||||||
|
|
||||||
const FRESHENER_IMAGE: &'static [u8] = include_bytes!("../assets/AirFreshener/sheet.png");
|
const FRESHENER_IMAGE: &'static [u8] = include_bytes!("../assets/AirFreshener/sheet.png");
|
||||||
const NOT_SO_FRESH_BG_IMAGE: &'static [u8] = include_bytes!("../assets/AirFreshener/bg0.png");
|
const NOT_SO_FRESH_BG_IMAGE: &'static [u8] = include_bytes!("../assets/AirFreshener/bg0.png");
|
||||||
const FRESH_DUMBLEDORE_BG_IMAGE: &'static [u8] = include_bytes!("../assets/AirFreshener/bg1.png");
|
const FRESH_DUMBLEDORE_BG_IMAGE: &'static [u8] = include_bytes!("../assets/AirFreshener/bg1.png");
|
||||||
const FRESHENER_FRAMES: u32 = 256;
|
const FRESHENER_FRAMES: usize = 256;
|
||||||
const FRESHENER_FRAMES_X: u32 = 20;
|
const FRESHENER_FRAME_WIDTH: usize = 73;
|
||||||
const FRESHENER_FRAMES_Y: u32 = 13;
|
const FRESHENER_FRAME_HEIGHT: usize = 144;
|
||||||
const FRESHENER_FRAME_WIDTH: f32 = 73.0;
|
const FRESHENER_SCREEN_X: f32 = 120.0;
|
||||||
const FRESHENER_FRAME_HEIGHT: f32 = 144.0;
|
const FRESHENER_SCREEN_Y: f32 = 20.0;
|
||||||
|
|
||||||
pub struct PluginGui {
|
pub struct PluginGui {
|
||||||
font: Result<FontId, String>,
|
font: Result<FontId, String>,
|
||||||
params: Arc<PluginParams>,
|
params: Arc<PluginParams>,
|
||||||
canvas: Option<Canvas<OpenGl>>,
|
canvas: Option<Canvas<OpenGl>>,
|
||||||
_gui_context: Arc<dyn GuiContext>,
|
_gui_context: Arc<dyn GuiContext>,
|
||||||
scaling_factor: Arc<AtomicCell<Option<f32>>>,
|
scaling_factor: Arc<AtomicF32>,
|
||||||
|
|
||||||
freshener_screen_bounds: Rect<f32>,
|
freshener_image: SpriteSheet,
|
||||||
|
freshener_image_bounds: ScaledRect,
|
||||||
freshener_image: Result<ImageId, String>,
|
not_so_fresh_image: Sprite,
|
||||||
not_so_fresh_image: Result<ImageId, String>,
|
fresh_dumbledore_image: Sprite,
|
||||||
fresh_dumbledore_image: Result<ImageId, String>,
|
|
||||||
|
|
||||||
dirty: bool,
|
dirty: bool,
|
||||||
mouse_position: (f32, f32),
|
mouse_position: ScaledPoint,
|
||||||
drag_start_mouse_pos: (f32, f32),
|
drag_start_mouse_pos: ScaledPoint,
|
||||||
drag_start_parameter_value: f32,
|
drag_start_parameter_value: f32,
|
||||||
dragging: bool,
|
dragging: bool,
|
||||||
}
|
}
|
||||||
@@ -42,13 +40,13 @@ pub struct PluginGui {
|
|||||||
fn create_canvas(
|
fn create_canvas(
|
||||||
context: &GlContext,
|
context: &GlContext,
|
||||||
params: &PluginParams,
|
params: &PluginParams,
|
||||||
scaling_factor: &AtomicCell<Option<f32>>,
|
scaling_factor: &AtomicF32,
|
||||||
) -> Result<Canvas<OpenGl>, &'static str> {
|
) -> Result<Canvas<OpenGl>, &'static str> {
|
||||||
let renderer = unsafe { OpenGl::new_from_function(|s| context.get_proc_address(s)) }
|
let renderer = unsafe { OpenGl::new_from_function(|s| context.get_proc_address(s)) }
|
||||||
.map_err(|_| "Failed to create OpenGL renderer")?;
|
.map_err(|_| "Failed to create OpenGL renderer")?;
|
||||||
let mut canvas = Canvas::new(renderer).map_err(|_| "Failed to create femtovg canvas")?;
|
let mut canvas = Canvas::new(renderer).map_err(|_| "Failed to create femtovg canvas")?;
|
||||||
let (width, height) = params.editor_state.size();
|
let (width, height) = params.editor_state.size();
|
||||||
canvas.set_size(width, height, scaling_factor.load().unwrap_or(1.0));
|
canvas.set_size(width, height, scaling_factor.load(Ordering::Relaxed));
|
||||||
Ok(canvas)
|
Ok(canvas)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,7 +55,7 @@ impl PluginGui {
|
|||||||
window: &mut baseview::Window<'_>,
|
window: &mut baseview::Window<'_>,
|
||||||
gui_context: Arc<dyn GuiContext>,
|
gui_context: Arc<dyn GuiContext>,
|
||||||
params: Arc<PluginParams>,
|
params: Arc<PluginParams>,
|
||||||
scaling_factor: Arc<AtomicCell<Option<f32>>>,
|
scaling_factor: Arc<AtomicF32>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut this = Self {
|
let mut this = Self {
|
||||||
font: Err("Not loaded".to_owned()),
|
font: Err("Not loaded".to_owned()),
|
||||||
@@ -66,14 +64,14 @@ impl PluginGui {
|
|||||||
_gui_context: gui_context,
|
_gui_context: gui_context,
|
||||||
scaling_factor: scaling_factor.clone(),
|
scaling_factor: scaling_factor.clone(),
|
||||||
dirty: true,
|
dirty: true,
|
||||||
mouse_position: (0.0, 0.0),
|
mouse_position: ScaledPoint::new(scaling_factor.clone()),
|
||||||
drag_start_mouse_pos: (0.0, 0.0),
|
drag_start_mouse_pos: ScaledPoint::new(scaling_factor.clone()),
|
||||||
drag_start_parameter_value: 0.0,
|
drag_start_parameter_value: 0.0,
|
||||||
dragging: false,
|
dragging: false,
|
||||||
freshener_image: Err("Not loaded".to_owned()),
|
freshener_image: SpriteSheet::empty(),
|
||||||
fresh_dumbledore_image: Err("Not loaded".to_owned()),
|
freshener_image_bounds: ScaledRect::new(scaling_factor.clone()),
|
||||||
not_so_fresh_image: Err("Not loaded".to_owned()),
|
fresh_dumbledore_image: Sprite::empty(),
|
||||||
freshener_screen_bounds: Rect::default(),
|
not_so_fresh_image: Sprite::empty(),
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(context) = window.gl_context() {
|
if let Some(context) = window.gl_context() {
|
||||||
@@ -84,15 +82,24 @@ impl PluginGui {
|
|||||||
this.font = canvas
|
this.font = canvas
|
||||||
.add_font_mem(DROID_SANS_FONT)
|
.add_font_mem(DROID_SANS_FONT)
|
||||||
.map_err(|err| format!("{:?}", err));
|
.map_err(|err| format!("{:?}", err));
|
||||||
this.freshener_image = canvas
|
this.freshener_image = SpriteSheet::new(
|
||||||
.load_image_mem(FRESHENER_IMAGE, ImageFlags::empty())
|
&mut canvas,
|
||||||
.map_err(|err| format!("{:?}", err));
|
FRESHENER_IMAGE,
|
||||||
this.fresh_dumbledore_image = canvas
|
scaling_factor.clone(),
|
||||||
.load_image_mem(FRESH_DUMBLEDORE_BG_IMAGE, ImageFlags::empty())
|
FRESHENER_FRAME_WIDTH,
|
||||||
.map_err(|err| format!("{:?}", err));
|
FRESHENER_FRAME_HEIGHT,
|
||||||
this.not_so_fresh_image = canvas
|
FRESHENER_FRAMES,
|
||||||
.load_image_mem(NOT_SO_FRESH_BG_IMAGE, ImageFlags::empty())
|
);
|
||||||
.map_err(|err| format!("{:?}", err));
|
this.freshener_image_bounds = this
|
||||||
|
.freshener_image
|
||||||
|
.screen_bounds(FRESHENER_SCREEN_X, FRESHENER_SCREEN_Y);
|
||||||
|
this.fresh_dumbledore_image = Sprite::new(
|
||||||
|
&mut canvas,
|
||||||
|
FRESH_DUMBLEDORE_BG_IMAGE,
|
||||||
|
scaling_factor.clone(),
|
||||||
|
);
|
||||||
|
this.not_so_fresh_image =
|
||||||
|
Sprite::new(&mut canvas, NOT_SO_FRESH_BG_IMAGE, scaling_factor.clone());
|
||||||
this.canvas = Some(canvas);
|
this.canvas = Some(canvas);
|
||||||
}
|
}
|
||||||
unsafe {
|
unsafe {
|
||||||
@@ -115,15 +122,6 @@ impl PluginGui {
|
|||||||
*/
|
*/
|
||||||
this
|
this
|
||||||
}
|
}
|
||||||
fn scaling_factor(&self) -> f32 {
|
|
||||||
if let Some(factor) = self.scaling_factor.load() {
|
|
||||||
factor
|
|
||||||
} else if let Some(canvas) = &self.canvas {
|
|
||||||
canvas.width() as f32 / EditorWindow::WINDOW_SIZE.0 as f32
|
|
||||||
} else {
|
|
||||||
1.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WindowHandler for PluginGui {
|
impl WindowHandler for PluginGui {
|
||||||
@@ -131,7 +129,7 @@ impl WindowHandler for PluginGui {
|
|||||||
if self.canvas.is_none() {
|
if self.canvas.is_none() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let scaling_factor = self.scaling_factor();
|
let scaling_factor = self.scaling_factor.load(Ordering::Relaxed);
|
||||||
let canvas = self.canvas.as_mut().unwrap();
|
let canvas = self.canvas.as_mut().unwrap();
|
||||||
|
|
||||||
if !self.dirty {
|
if !self.dirty {
|
||||||
@@ -139,12 +137,6 @@ impl WindowHandler for PluginGui {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let font_size = 12.0 * scaling_factor;
|
let font_size = 12.0 * scaling_factor;
|
||||||
self.freshener_screen_bounds = Rect {
|
|
||||||
x: 120.0 * scaling_factor,
|
|
||||||
y: 20.0 * scaling_factor,
|
|
||||||
width: FRESHENER_FRAME_WIDTH * scaling_factor,
|
|
||||||
height: FRESHENER_FRAME_HEIGHT * scaling_factor,
|
|
||||||
};
|
|
||||||
|
|
||||||
let context = match window.gl_context() {
|
let context = match window.gl_context() {
|
||||||
None => {
|
None => {
|
||||||
@@ -169,62 +161,11 @@ impl WindowHandler for PluginGui {
|
|||||||
let mut full_window_path = Path::new();
|
let mut full_window_path = Path::new();
|
||||||
full_window_path.rect(0.0, 0.0, width, height);
|
full_window_path.rect(0.0, 0.0, width, height);
|
||||||
|
|
||||||
let mut freshener_path = Path::new();
|
let freshness = self.params.freshness.unmodulated_normalized_value();
|
||||||
freshener_path.rect(
|
let frame_index = (freshness * (FRESHENER_FRAMES - 1) as f32).floor() as usize;
|
||||||
self.freshener_screen_bounds.x,
|
self.not_so_fresh_image.draw(canvas, 0.0, 0.0, 1.0);
|
||||||
self.freshener_screen_bounds.y,
|
self.fresh_dumbledore_image.draw(canvas, 0.0, 0.0, freshness);
|
||||||
self.freshener_screen_bounds.width,
|
self.freshener_image.draw(canvas, FRESHENER_SCREEN_X, FRESHENER_SCREEN_Y, frame_index);
|
||||||
self.freshener_screen_bounds.height,
|
|
||||||
);
|
|
||||||
|
|
||||||
let frame_index = (self.params.freshness.unmodulated_normalized_value()
|
|
||||||
* (FRESHENER_FRAMES - 1) as f32)
|
|
||||||
.floor();
|
|
||||||
let frame_x = (frame_index % FRESHENER_FRAMES_X as f32).floor();
|
|
||||||
let frame_y = (frame_index / FRESHENER_FRAMES_X as f32).floor();
|
|
||||||
|
|
||||||
let freshener_image_source_rect = Rect {
|
|
||||||
x: self.freshener_screen_bounds.x - frame_x * FRESHENER_FRAME_WIDTH,
|
|
||||||
y: self.freshener_screen_bounds.y - frame_y * FRESHENER_FRAME_HEIGHT,
|
|
||||||
width: FRESHENER_FRAME_WIDTH * FRESHENER_FRAMES_X as f32 * scaling_factor,
|
|
||||||
height: FRESHENER_FRAME_HEIGHT * FRESHENER_FRAMES_Y as f32 * scaling_factor,
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Ok(not_so_fresh) = self.not_so_fresh_image {
|
|
||||||
canvas.fill_path(
|
|
||||||
&full_window_path,
|
|
||||||
&Paint::image(not_so_fresh, 0.0, 0.0, width, height, 0.0, 1.0),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if let Ok(fresh_dumbledore) = self.fresh_dumbledore_image {
|
|
||||||
canvas.fill_path(
|
|
||||||
&full_window_path,
|
|
||||||
&Paint::image(
|
|
||||||
fresh_dumbledore,
|
|
||||||
0.0,
|
|
||||||
0.0,
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
0.0,
|
|
||||||
self.params.freshness.unmodulated_normalized_value(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Ok(freshener) = self.freshener_image {
|
|
||||||
canvas.fill_path(
|
|
||||||
&freshener_path,
|
|
||||||
&Paint::image(
|
|
||||||
freshener,
|
|
||||||
freshener_image_source_rect.x,
|
|
||||||
freshener_image_source_rect.y,
|
|
||||||
freshener_image_source_rect.width,
|
|
||||||
freshener_image_source_rect.height,
|
|
||||||
0.0,
|
|
||||||
1.0,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
{
|
{
|
||||||
@@ -250,21 +191,8 @@ impl WindowHandler for PluginGui {
|
|||||||
|
|
||||||
print("Debug version");
|
print("Debug version");
|
||||||
print(&format!("scaling_factor {:?}", scaling_factor));
|
print(&format!("scaling_factor {:?}", scaling_factor));
|
||||||
print(&format!(
|
print(&format!("mouse_pos {:?}", self.mouse_position));
|
||||||
"screen_bounds {:#?}",
|
print(&format!("frame_index {:?}", frame_index));
|
||||||
self.freshener_screen_bounds
|
|
||||||
));
|
|
||||||
print(&format!("frame_bounds {:#?}", freshener_image_source_rect));
|
|
||||||
|
|
||||||
if let Err(e) = &self.freshener_image {
|
|
||||||
print(e);
|
|
||||||
}
|
|
||||||
if let Err(e) = &self.fresh_dumbledore_image {
|
|
||||||
print(e);
|
|
||||||
}
|
|
||||||
if let Err(e) = &self.not_so_fresh_image {
|
|
||||||
print(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -282,23 +210,20 @@ impl WindowHandler for PluginGui {
|
|||||||
event: baseview::Event,
|
event: baseview::Event,
|
||||||
) -> baseview::EventStatus {
|
) -> baseview::EventStatus {
|
||||||
let setter = ParamSetter::new(self._gui_context.as_ref());
|
let setter = ParamSetter::new(self._gui_context.as_ref());
|
||||||
let scaling_factor = self.scaling_factor();
|
let scaling_factor = self.scaling_factor.load(Ordering::Acquire);
|
||||||
match event {
|
match event {
|
||||||
Event::Window(WindowEvent::Resized(size)) => {
|
Event::Window(WindowEvent::Resized(size)) => {
|
||||||
let phys_size = size.physical_size();
|
let phys_size = size.physical_size();
|
||||||
if let Some(canvas) = self.canvas.as_mut() {
|
if let Some(canvas) = self.canvas.as_mut() {
|
||||||
canvas.set_size(
|
canvas.set_size(phys_size.width, phys_size.height, scaling_factor);
|
||||||
phys_size.width,
|
|
||||||
phys_size.height,
|
|
||||||
self.scaling_factor.load().unwrap_or(1.0),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
}
|
}
|
||||||
Event::Mouse(MouseEvent::CursorMoved { position, .. }) => {
|
Event::Mouse(MouseEvent::CursorMoved { position, .. }) => {
|
||||||
self.mouse_position = (position.x as f32 / scaling_factor, position.y as f32 / scaling_factor);
|
self.mouse_position
|
||||||
|
.set((position.x as f32, position.y as f32));
|
||||||
if self.dragging {
|
if self.dragging {
|
||||||
let delta = self.mouse_position.1 - self.drag_start_mouse_pos.1;
|
let delta = self.mouse_position.y - self.drag_start_mouse_pos.y;
|
||||||
let new_value =
|
let new_value =
|
||||||
(self.drag_start_parameter_value - delta * 0.01).clamp(0.0, 1.0);
|
(self.drag_start_parameter_value - delta * 0.01).clamp(0.0, 1.0);
|
||||||
setter.set_parameter_normalized(&self.params.freshness, new_value);
|
setter.set_parameter_normalized(&self.params.freshness, new_value);
|
||||||
@@ -306,11 +231,14 @@ impl WindowHandler for PluginGui {
|
|||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
}
|
}
|
||||||
Event::Mouse(MouseEvent::ButtonPressed { button, .. }) => {
|
Event::Mouse(MouseEvent::ButtonPressed { button, .. }) => {
|
||||||
self.dragging = self.freshener_screen_bounds.contains(self.mouse_position)
|
self.dragging = self
|
||||||
|
.freshener_image_bounds
|
||||||
|
.as_rect()
|
||||||
|
.contains(self.mouse_position.as_point())
|
||||||
&& button == MouseButton::Left;
|
&& button == MouseButton::Left;
|
||||||
if self.dragging {
|
if self.dragging {
|
||||||
setter.begin_set_parameter(&self.params.freshness);
|
setter.begin_set_parameter(&self.params.freshness);
|
||||||
self.drag_start_mouse_pos = self.mouse_position;
|
self.drag_start_mouse_pos = self.mouse_position.clone();
|
||||||
self.drag_start_parameter_value =
|
self.drag_start_parameter_value =
|
||||||
self.params.freshness.unmodulated_normalized_value();
|
self.params.freshness.unmodulated_normalized_value();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +1,21 @@
|
|||||||
use std::sync::{Arc, atomic::Ordering};
|
use crate::{AirFreshener, editor::EditorHandle, gui::PluginGui, parameters::PluginParams};
|
||||||
use baseview::{Window, WindowOpenOptions, WindowScalePolicy, gl::GlConfig};
|
use baseview::{Window, WindowOpenOptions, WindowScalePolicy, gl::GlConfig};
|
||||||
use crossbeam::atomic::AtomicCell;
|
use nih_plug::prelude::AtomicF32;
|
||||||
use nih_plug::{editor::Editor, plugin::Plugin};
|
use nih_plug::{editor::Editor, plugin::Plugin};
|
||||||
use crate::{AirFreshener, editor::EditorHandle, parameters::PluginParams, gui::PluginGui};
|
use std::sync::{Arc, atomic::Ordering};
|
||||||
|
|
||||||
pub struct EditorWindow {
|
pub struct EditorWindow {
|
||||||
params: Arc<PluginParams>,
|
params: Arc<PluginParams>,
|
||||||
scaling_factor: Arc<AtomicCell<Option<f32>>>,
|
scaling_factor: Arc<AtomicF32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EditorWindow {
|
impl EditorWindow {
|
||||||
|
pub const VIRTUAL_SCALE: f32 = 1.0;
|
||||||
pub const WINDOW_SIZE: (u32, u32) = (230, 320);
|
pub const WINDOW_SIZE: (u32, u32) = (230, 320);
|
||||||
pub fn new(params: Arc<PluginParams>) -> Self {
|
pub fn new(params: Arc<PluginParams>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
params,
|
params,
|
||||||
#[cfg(target_os = "macos")]
|
scaling_factor: Arc::new(AtomicF32::new(EditorWindow::VIRTUAL_SCALE)),
|
||||||
scaling_factor: Arc::new(AtomicCell::new(None)),
|
|
||||||
#[cfg(not(target_os = "macos"))]
|
|
||||||
scaling_factor: Arc::new(AtomicCell::new(Some(1.0))),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -29,33 +27,43 @@ impl Editor for EditorWindow {
|
|||||||
context: Arc<dyn nih_plug::prelude::GuiContext>,
|
context: Arc<dyn nih_plug::prelude::GuiContext>,
|
||||||
) -> Box<dyn std::any::Any + Send> {
|
) -> Box<dyn std::any::Any + Send> {
|
||||||
let (unscaled_width, unscaled_height) = self.params.editor_state.size();
|
let (unscaled_width, unscaled_height) = self.params.editor_state.size();
|
||||||
let scaling_factor = self.scaling_factor.load();
|
let scaling_factor = self.scaling_factor.load(Ordering::Acquire);
|
||||||
let move_scaling_factor = self.scaling_factor.clone();
|
let move_scaling_factor = self.scaling_factor.clone();
|
||||||
let gui_context = context.clone();
|
let gui_context = context.clone();
|
||||||
let params = self.params.clone();
|
let params = self.params.clone();
|
||||||
let window = Window::open_parented(&parent, WindowOpenOptions {
|
let window = Window::open_parented(
|
||||||
title: AirFreshener::NAME.to_owned(),
|
&parent,
|
||||||
size: baseview::Size { width: unscaled_width as f64, height: unscaled_height as f64 },
|
WindowOpenOptions {
|
||||||
scale: scaling_factor.map(|factor| WindowScalePolicy::ScaleFactor(factor as f64)).unwrap_or(WindowScalePolicy::SystemScaleFactor),
|
title: AirFreshener::NAME.to_owned(),
|
||||||
gl_config: Some(GlConfig {
|
size: baseview::Size {
|
||||||
version: (3, 2),
|
width: unscaled_width as f64,
|
||||||
red_bits: 8,
|
height: unscaled_height as f64,
|
||||||
green_bits: 8,
|
},
|
||||||
blue_bits: 8,
|
#[cfg(target_os = "macos")]
|
||||||
alpha_bits: 8,
|
scale: WindowScalePolicy::SystemScaleFactor,
|
||||||
samples: None,
|
#[cfg(not(target_os = "macos"))]
|
||||||
srgb: true,
|
scale: WindowScalePolicy::ScaleFactor(scaling_factor as f64),
|
||||||
double_buffer: true,
|
gl_config: Some(GlConfig {
|
||||||
vsync: false,
|
version: (3, 2),
|
||||||
..Default::default()
|
red_bits: 8,
|
||||||
})
|
green_bits: 8,
|
||||||
}, move |window: &mut baseview::Window<'_>| -> PluginGui {
|
blue_bits: 8,
|
||||||
PluginGui::new(window, gui_context, params, move_scaling_factor)
|
alpha_bits: 8,
|
||||||
});
|
samples: None,
|
||||||
|
srgb: true,
|
||||||
|
double_buffer: true,
|
||||||
|
vsync: false,
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
move |window: &mut baseview::Window<'_>| -> PluginGui {
|
||||||
|
PluginGui::new(window, gui_context, params, move_scaling_factor)
|
||||||
|
},
|
||||||
|
);
|
||||||
self.params.editor_state.open.store(true, Ordering::Release);
|
self.params.editor_state.open.store(true, Ordering::Release);
|
||||||
Box::new(EditorHandle {
|
Box::new(EditorHandle {
|
||||||
state: self.params.editor_state.clone(),
|
state: self.params.editor_state.clone(),
|
||||||
window
|
window,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,7 +75,7 @@ impl Editor for EditorWindow {
|
|||||||
if self.params.editor_state.is_open() {
|
if self.params.editor_state.is_open() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
self.scaling_factor.store(Some(factor));
|
self.scaling_factor.store(factor, Ordering::Release);
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user