1
0
Fork 0

Finish the drawing for the iced param slider

This commit is contained in:
Robbert van der Helm 2022-03-14 13:17:19 +01:00
parent 291abb8bcf
commit 4e9ee27c19
6 changed files with 96 additions and 54 deletions

View file

@ -1,4 +1,4 @@
//! Utilities for creating these widgets
//! Utilities for creating these widgets.
use egui::Color32;

View file

@ -52,8 +52,8 @@ pub fn create_iced_editor<E: IcedEditor>(
/// A plugin editor using `iced`. This wraps around [`Application`] with the only change being that
/// the usual `new()` function now additionally takes a `Arc<dyn GuiContext>` that the editor can
/// store to interact with the parameters. The editor should have a `Pin<Arc<impl Params>>` as part
/// of their [`Flags`][Self::Flags] so it can read the current parameter values. See [`Application`]
/// for more information.
/// of their [`InitializationFlags`][Self::InitializationFlags] so it can read the current parameter
/// values. See [`Application`] for more information.
pub trait IcedEditor: 'static + Send + Sync + Sized {
/// See [`Application::Executor`]. You'll likely want to use [`crate::executor::Default`].
type Executor: Executor;

View file

@ -8,6 +8,7 @@
use nih_plug::param::internals::ParamPtr;
mod param_slider;
pub mod util;
pub use param_slider::ParamSlider;

View file

@ -1,14 +1,12 @@
//! A slider that integrates with NIH-plug's [`Param`] types.
use crate::backend;
use crate::event::{self, Event};
use crate::layout;
use crate::mouse;
use crate::renderer;
use crate::text;
use crate::{Clipboard, Color, Element, Layout, Length, Point, Rectangle, Shell, Size, Widget};
use nih_plug::prelude::Param;
use crate::{
alignment, backend, event, layout, mouse, renderer, text, Clipboard, Color, Element, Event,
Layout, Length, Point, Rectangle, Shell, Size, Widget,
};
use nih_plug::prelude::{GuiContext, Param, ParamSetter};
use super::util;
use super::ParamMessage;
/// A slider that integrates with NIH-plug's [`Param`] types.
@ -16,6 +14,9 @@ use super::ParamMessage;
/// TODO: There are currently no styling options at all
pub struct ParamSlider<'a, P: Param, Renderer: text::Renderer> {
param: &'a P,
/// We'll visualize the parameter's current value by drawing the difference between the current
/// normalized value and the default normalized value.
setter: ParamSetter<'a>,
height: Length,
width: Length,
@ -25,9 +26,13 @@ pub struct ParamSlider<'a, P: Param, Renderer: text::Renderer> {
impl<'a, P: Param, Renderer: text::Renderer> ParamSlider<'a, P, Renderer> {
/// Creates a new [`ParamSlider`] for the given parameter.
pub fn new(param: &'a P) -> Self {
pub fn new(param: &'a P, context: &'a dyn GuiContext) -> Self {
let setter = ParamSetter::new(context);
Self {
param,
setter,
width: Length::Units(180),
height: Length::Units(30),
text_size: None,
@ -111,17 +116,19 @@ impl<'a, P: Param, Renderer: text::Renderer> Widget<ParamMessage, Renderer>
fn draw(
&self,
renderer: &mut Renderer,
_style: &renderer::Style,
style: &renderer::Style,
layout: Layout<'_>,
cursor_position: Point,
_viewport: &Rectangle,
) {
const BORDER_WIDTH: f32 = 1.0;
let bounds = layout.bounds();
let is_mouse_over = bounds.contains(cursor_position);
// TODO:
// The bar itself
let background_color = if is_mouse_over {
Color::new(0.5, 0.5, 0.5, 0.2)
Color::new(0.5, 0.5, 0.5, 0.1)
} else {
Color::TRANSPARENT
};
@ -130,51 +137,70 @@ impl<'a, P: Param, Renderer: text::Renderer> Widget<ParamMessage, Renderer>
renderer::Quad {
bounds,
border_color: Color::BLACK,
border_width: 1.0,
border_width: BORDER_WIDTH,
border_radius: 0.0,
},
background_color,
);
// TODO:
// We'll visualize the difference between the current value and the default value
let current_value = self.param.normalized_value();
let fill_start_x = util::remap_rect_x(
&bounds,
self.setter.default_normalized_param_value(self.param),
);
let fill_end_x = util::remap_rect_x(&bounds, current_value);
// renderer.fill_text(Text {
// content: &Renderer::ARROW_DOWN_ICON.to_string(),
// font: Renderer::ICON_FONT,
// size: bounds.height * style.icon_size,
// bounds: Rectangle {
// x: bounds.x + bounds.width - f32::from(self.padding.horizontal()),
// y: bounds.center_y(),
// ..bounds
// },
// color: style.text_color,
// horizontal_alignment: alignment::Horizontal::Right,
// vertical_alignment: alignment::Vertical::Center,
// });
let fill_color = Color::from_rgb8(196, 196, 196);
let fill_rect = Rectangle {
x: fill_start_x.min(fill_end_x),
y: bounds.y + BORDER_WIDTH,
width: (fill_end_x - fill_start_x).abs(),
height: bounds.height - BORDER_WIDTH * 2.0,
};
renderer.fill_quad(
renderer::Quad {
bounds: fill_rect,
border_color: Color::TRANSPARENT,
border_width: 0.0,
border_radius: 0.0,
},
fill_color,
);
// if let Some(label) = self
// .selected
// .as_ref()
// .map(ToString::to_string)
// .as_ref()
// .or_else(|| self.placeholder.as_ref())
// {
// renderer.fill_text(Text {
// content: label,
// size: f32::from(self.text_size.unwrap_or(renderer.default_size())),
// font: self.font.clone(),
// color: is_selected
// .then(|| style.text_color)
// .unwrap_or(style.placeholder_color),
// bounds: Rectangle {
// x: bounds.x + f32::from(self.padding.left),
// y: bounds.center_y(),
// ..bounds
// },
// horizontal_alignment: alignment::Horizontal::Left,
// vertical_alignment: alignment::Vertical::Center,
// })
// }
// We'll overlay the label on the slider. To make it more readable (and because it looks
// cool), the parts that overlap with the fill rect will be rendered in white while the rest
// will be rendered in black.
let display_value = self.param.to_string();
let text_size = self.text_size.unwrap_or_else(|| renderer.default_size()) as f32;
let text_bounds = Rectangle {
x: bounds.center_x(),
y: bounds.center_y(),
..bounds
};
renderer.fill_text(text::Text {
content: &display_value,
font: self.font.clone(),
size: text_size,
bounds: text_bounds,
color: style.text_color,
horizontal_alignment: alignment::Horizontal::Center,
vertical_alignment: alignment::Vertical::Center,
});
// This will clip to the filled area
renderer.with_layer(fill_rect, |renderer| {
let filled_text_color = Color::from_rgb8(80, 80, 80);
renderer.fill_text(text::Text {
content: &display_value,
font: self.font.clone(),
size: text_size,
bounds: text_bounds,
color: filled_text_color,
horizontal_alignment: alignment::Horizontal::Center,
vertical_alignment: alignment::Vertical::Center,
});
});
}
}

View file

@ -0,0 +1,15 @@
//! Utilities for creating these widgets.
use crate::Rectangle;
/// Remap a `[0, 1]` value to an x-coordinate within this rectangle. The value will be clamped to
/// `[0, 1]` if it isn't already in that range.
pub fn remap_rect_x(rect: &Rectangle, t: f32) -> f32 {
rect.x + (rect.width * t.clamp(0.0, 1.0))
}
/// Remap a `[0, 1]` value to a y-coordinate within this rectangle. The value will be clamped to
/// `[0, 1]` if it isn't already in that range.
pub fn remap_rect_y(rect: &Rectangle, t: f32) -> f32 {
rect.y + (rect.height * t.clamp(0.0, 1.0))
}

View file

@ -84,7 +84,7 @@ impl IcedEditor for GainEditor {
.vertical_alignment(alignment::Vertical::Center),
)
.push(
ParamSlider::new(&self.params.gain).map(Message::ParamUpdate)
ParamSlider::new(&self.params.gain, self.context.as_ref()).map(Message::ParamUpdate)
// Button::new(&mut self.gain_dummy_state, Text::new("Gain"))
// .height(30.into())
// .width(180.into()),