Initial Air Freshener plugin implementation
This commit is contained in:
250
src/gui.rs
Normal file
250
src/gui.rs
Normal file
@@ -0,0 +1,250 @@
|
||||
use crate::{parameters::PluginParams, window::EditorWindow};
|
||||
use baseview::{Event, EventStatus, MouseButton, MouseEvent, WindowEvent, WindowHandler};
|
||||
use ebu_dsp::Rect;
|
||||
use femtovg::{Canvas, Color, ImageFlags, ImageId, Paint, Path, renderer::OpenGl};
|
||||
use nih_plug::prelude::*;
|
||||
use std::sync::Arc;
|
||||
|
||||
//const DROID_SANS_FONT: &'static [u8] = include_bytes!("../resources/fonts/DroidSans.ttf");
|
||||
|
||||
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 FRESH_DUMBLEDORE_BG_IMAGE: &'static [u8] =
|
||||
include_bytes!("../assets/AirFreshener/bg1.png");
|
||||
const FRESHENER_FRAMES: u32 = 256;
|
||||
const FRESHENER_FRAMES_X: u32 = 20;
|
||||
const FRESHENER_FRAMES_Y: u32 = 13;
|
||||
const FRESHENER_FRAME_WIDTH: f32 = 73.0;
|
||||
const FRESHENER_FRAME_HEIGHT: f32 = 144.0;
|
||||
|
||||
pub struct PluginGui {
|
||||
// font: FontId,
|
||||
params: Arc<PluginParams>,
|
||||
canvas: Canvas<OpenGl>,
|
||||
_gui_context: Arc<dyn GuiContext>,
|
||||
scaling_factor: f32,
|
||||
|
||||
freshener_image: ImageId,
|
||||
not_so_fresh_image: ImageId,
|
||||
fresh_dumbledore_image: ImageId,
|
||||
|
||||
freshener_bounds: Rect<f32>,
|
||||
|
||||
dirty: bool,
|
||||
mouse_position: (f32, f32),
|
||||
drag_start_mouse_pos: (f32, f32),
|
||||
drag_start_parameter_value: f32,
|
||||
dragging: bool,
|
||||
}
|
||||
|
||||
impl PluginGui {
|
||||
pub fn new(
|
||||
window: &mut baseview::Window<'_>,
|
||||
gui_context: Arc<dyn GuiContext>,
|
||||
params: Arc<PluginParams>,
|
||||
scaling_factor: f32,
|
||||
) -> Self {
|
||||
let context = window
|
||||
.gl_context()
|
||||
.expect("Failed to get window OpenGL context");
|
||||
unsafe {
|
||||
context.make_current();
|
||||
}
|
||||
let renderer = unsafe { OpenGl::new_from_function(|s| context.get_proc_address(s)) }
|
||||
.expect("Failed to create femtovg renderer");
|
||||
let mut canvas = Canvas::new(renderer).expect("Failed to create femtovg canvas");
|
||||
let (width, height) = params.editor_state.size();
|
||||
|
||||
canvas.set_size(width, height, scaling_factor);
|
||||
let le_fresh = canvas
|
||||
.load_image_mem(FRESHENER_IMAGE, ImageFlags::empty())
|
||||
.expect("Failed to load le fresh");
|
||||
let not_so_fresh_image = canvas
|
||||
.load_image_mem(NOT_SO_FRESH_BG_IMAGE, ImageFlags::empty())
|
||||
.expect("Failed to load not so fresh");
|
||||
let fresh_dumbledore_image = canvas
|
||||
.load_image_mem(FRESH_DUMBLEDORE_BG_IMAGE, ImageFlags::empty())
|
||||
.expect("Failed to load fresh dumbledore");
|
||||
//let font = canvas
|
||||
// .add_font_mem(DROID_SANS_FONT)
|
||||
// .expect("Failed to load font");
|
||||
|
||||
unsafe {
|
||||
context.make_not_current();
|
||||
}
|
||||
Self {
|
||||
//font,
|
||||
params,
|
||||
canvas,
|
||||
_gui_context: gui_context,
|
||||
scaling_factor,
|
||||
dirty: true,
|
||||
mouse_position: (0.0, 0.0),
|
||||
drag_start_mouse_pos: (0.0, 0.0),
|
||||
drag_start_parameter_value: 0.0,
|
||||
dragging: false,
|
||||
freshener_image: le_fresh,
|
||||
fresh_dumbledore_image,
|
||||
not_so_fresh_image,
|
||||
freshener_bounds: Rect {
|
||||
x: 120.0,
|
||||
y: 20.0,
|
||||
width: FRESHENER_FRAME_WIDTH,
|
||||
height: FRESHENER_FRAME_HEIGHT,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl WindowHandler for PluginGui {
|
||||
fn on_frame(&mut self, window: &mut baseview::Window) {
|
||||
const WINDOW_WIDTH: f32 = EditorWindow::WINDOW_SIZE.0 as f32;
|
||||
const WINDOW_HEIGHT: f32 = EditorWindow::WINDOW_SIZE.1 as f32;
|
||||
|
||||
if !self.dirty {
|
||||
//return;
|
||||
}
|
||||
|
||||
let context = window
|
||||
.gl_context()
|
||||
.expect("Failed to get window OpenGL context");
|
||||
unsafe {
|
||||
context.make_current();
|
||||
}
|
||||
let (width, height) = (self.canvas.width(), self.canvas.height());
|
||||
self.canvas.reset();
|
||||
self.canvas
|
||||
.clear_rect(0, 0, width, height, Color::rgbaf(0.0, 0.0, 0.0, 1.0));
|
||||
|
||||
let mut full_window_path = Path::new();
|
||||
full_window_path.rect(
|
||||
0.0,
|
||||
0.0,
|
||||
EditorWindow::WINDOW_SIZE.0 as f32,
|
||||
EditorWindow::WINDOW_SIZE.1 as f32,
|
||||
);
|
||||
|
||||
let mut freshener_path = Path::new();
|
||||
freshener_path.rect(
|
||||
self.freshener_bounds.x,
|
||||
self.freshener_bounds.y,
|
||||
self.freshener_bounds.width,
|
||||
self.freshener_bounds.height,
|
||||
);
|
||||
|
||||
let bbox = self.canvas.path_bbox(&freshener_path);
|
||||
|
||||
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();
|
||||
|
||||
self.canvas.fill_path(
|
||||
&full_window_path,
|
||||
&Paint::image(
|
||||
self.not_so_fresh_image,
|
||||
0.0,
|
||||
0.0,
|
||||
WINDOW_WIDTH,
|
||||
WINDOW_HEIGHT,
|
||||
0.0,
|
||||
1.0,
|
||||
),
|
||||
);
|
||||
self.canvas.fill_path(
|
||||
&full_window_path,
|
||||
&Paint::image(
|
||||
self.fresh_dumbledore_image,
|
||||
0.0,
|
||||
0.0,
|
||||
WINDOW_WIDTH,
|
||||
WINDOW_HEIGHT,
|
||||
0.0,
|
||||
self.params.freshness.unmodulated_normalized_value(),
|
||||
),
|
||||
);
|
||||
|
||||
self.canvas.fill_path(
|
||||
&freshener_path,
|
||||
&Paint::image(
|
||||
self.freshener_image,
|
||||
bbox.minx - frame_x * FRESHENER_FRAME_WIDTH,
|
||||
bbox.miny - frame_y * FRESHENER_FRAME_HEIGHT,
|
||||
self.freshener_bounds.width * FRESHENER_FRAMES_X as f32,
|
||||
self.freshener_bounds.height * FRESHENER_FRAMES_Y as f32,
|
||||
0.0,
|
||||
1.0,
|
||||
),
|
||||
);
|
||||
/*
|
||||
let debug = format!("{:#?}", self.params.comp_state.load());
|
||||
for (i, line) in debug.split('\n').enumerate() {
|
||||
self.canvas
|
||||
.fill_text(
|
||||
10.0,
|
||||
10.0 + i as f32 * 12.5,
|
||||
line,
|
||||
&Paint::color(Color::rgbf(1.0, 1.0, 1.0))
|
||||
.with_font(&[self.font])
|
||||
.with_font_size(12.5)
|
||||
.with_text_baseline(femtovg::Baseline::Top),
|
||||
)
|
||||
.expect("Failed to render font");
|
||||
}
|
||||
*/
|
||||
|
||||
self.canvas.flush();
|
||||
context.swap_buffers();
|
||||
unsafe {
|
||||
context.make_not_current();
|
||||
}
|
||||
self.dirty = false;
|
||||
}
|
||||
|
||||
fn on_event(
|
||||
&mut self,
|
||||
_window: &mut baseview::Window,
|
||||
event: baseview::Event,
|
||||
) -> baseview::EventStatus {
|
||||
let setter = ParamSetter::new(self._gui_context.as_ref());
|
||||
match event {
|
||||
Event::Window(WindowEvent::Resized(size)) => {
|
||||
let phys_size = size.physical_size();
|
||||
self.canvas
|
||||
.set_size(phys_size.width, phys_size.height, self.scaling_factor);
|
||||
self.dirty = true;
|
||||
}
|
||||
Event::Mouse(MouseEvent::CursorMoved { position, .. }) => {
|
||||
self.mouse_position = (position.x as f32, position.y as f32);
|
||||
if self.dragging {
|
||||
let delta = self.mouse_position.1 - self.drag_start_mouse_pos.1;
|
||||
let new_value =
|
||||
(self.drag_start_parameter_value - delta * 0.01).clamp(0.0, 1.0);
|
||||
setter.set_parameter_normalized(&self.params.freshness, new_value);
|
||||
}
|
||||
self.dirty = true;
|
||||
}
|
||||
Event::Mouse(MouseEvent::ButtonPressed { button, .. }) => {
|
||||
self.dragging = self.freshener_bounds.contains(self.mouse_position)
|
||||
&& button == MouseButton::Left;
|
||||
if self.dragging {
|
||||
setter.begin_set_parameter(&self.params.freshness);
|
||||
self.drag_start_mouse_pos = self.mouse_position;
|
||||
self.drag_start_parameter_value =
|
||||
self.params.freshness.unmodulated_normalized_value();
|
||||
}
|
||||
self.dirty = true;
|
||||
}
|
||||
Event::Mouse(MouseEvent::ButtonReleased { .. }) => {
|
||||
if self.dragging {
|
||||
setter.end_set_parameter(&self.params.freshness);
|
||||
}
|
||||
self.dragging = false;
|
||||
self.dirty = true;
|
||||
}
|
||||
_ => return EventStatus::Ignored,
|
||||
}
|
||||
EventStatus::Captured
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user