Add a placeholder for a parameter slider widget

This commit is contained in:
Robbert van der Helm
2022-03-14 00:53:22 +01:00
parent 5711f77cc7
commit 291abb8bcf
6 changed files with 223 additions and 16 deletions

View File

@@ -2,7 +2,7 @@
//!
//! TODO: Proper usage example, for now check out the gain_gui example
use baseview::{Size, WindowOpenOptions, WindowScalePolicy};
use baseview::{WindowOpenOptions, WindowScalePolicy};
use crossbeam::atomic::AtomicCell;
use crossbeam::channel;
use nih_plug::prelude::{Editor, GuiContext, ParentWindowHandle};
@@ -106,12 +106,12 @@ pub trait IcedEditor: 'static + Send + Sync + Sized {
}
/// See [`Application::renderer_settings`].
fn renderer_settings() -> iced_baseview::renderer::settings::Settings {
iced_baseview::renderer::settings::Settings {
fn renderer_settings() -> iced_baseview::backend::settings::Settings {
iced_baseview::backend::settings::Settings {
// Enable some anti-aliasing by default. Since GUIs are likely very simple and most of
// the work will be on the CPU anyways this should not affect performance much.
antialiasing: Some(iced_baseview::renderer::settings::Antialiasing::MSAAx4),
..iced_baseview::renderer::settings::Settings::default()
antialiasing: Some(iced_baseview::backend::settings::Antialiasing::MSAAx4),
..iced_baseview::backend::settings::Settings::default()
}
}
@@ -196,7 +196,7 @@ impl<E: IcedEditor> Editor for IcedEditorWrapper<E> {
window: WindowOpenOptions {
title: String::from("iced window"),
// Baseview should be doing the DPI scaling for us
size: Size::new(unscaled_width as f64, unscaled_height as f64),
size: baseview::Size::new(unscaled_width as f64, unscaled_height as f64),
// NOTE: For some reason passing 1.0 here causes the UI to be scaled on macOS but
// not the mouse events.
scale: scaling_factor

View File

@@ -1,7 +1,16 @@
//! Widgets and utilities for making widgets to integrate iced with NIH-plug.
//!
//! # Note
//!
//! None of these widgets are finalized, and their sizes or looks can change at any point. Feel free
//! to copy the widgets and modify them to your personal taste.
use nih_plug::param::internals::ParamPtr;
mod param_slider;
pub use param_slider::ParamSlider;
/// A message to update a parameter value. Since NIH-plug manages the parameters, interacting with
/// parameter values with iced works a little different from updating any other state. This main
/// [`IcedEditor`][super::IcedEditor] should have a [`Message`][super::IcedEditor::Message] variant

View File

@@ -0,0 +1,199 @@
//! 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 super::ParamMessage;
/// A slider that integrates with NIH-plug's [`Param`] types.
///
/// TODO: There are currently no styling options at all
pub struct ParamSlider<'a, P: Param, Renderer: text::Renderer> {
param: &'a P,
height: Length,
width: Length,
text_size: Option<u16>,
font: Renderer::Font,
}
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 {
Self {
param,
width: Length::Units(180),
height: Length::Units(30),
text_size: None,
font: Renderer::Font::default(),
}
}
/// Sets the width of the [`ParamSlider`].
pub fn width(mut self, width: Length) -> Self {
self.width = width;
self
}
/// Sets the height of the [`ParamSlider`].
pub fn height(mut self, height: Length) -> Self {
self.height = height;
self
}
/// Sets the text size of the [`ParamSlider`].
pub fn text_size(mut self, size: u16) -> Self {
self.text_size = Some(size);
self
}
/// Sets the font of the [`ParamSlider`].
pub fn font(mut self, font: Renderer::Font) -> Self {
self.font = font;
self
}
}
impl<'a, P: Param, Renderer: text::Renderer> Widget<ParamMessage, Renderer>
for ParamSlider<'a, P, Renderer>
{
fn width(&self) -> Length {
self.width
}
fn height(&self) -> Length {
self.height
}
fn layout(&self, _renderer: &Renderer, limits: &layout::Limits) -> layout::Node {
let limits = limits.width(self.width).height(self.height);
let size = limits.resolve(Size::ZERO);
layout::Node::new(size)
}
fn on_event(
&mut self,
_event: Event,
_layout: Layout<'_>,
_cursor_position: Point,
_renderer: &Renderer,
_clipboard: &mut dyn Clipboard,
_shell: &mut Shell<'_, ParamMessage>,
) -> event::Status {
// TODO: Handle interaction
event::Status::Ignored
}
fn mouse_interaction(
&self,
layout: Layout<'_>,
cursor_position: Point,
_viewport: &Rectangle,
_renderer: &Renderer,
) -> mouse::Interaction {
let bounds = layout.bounds();
let is_mouse_over = bounds.contains(cursor_position);
if is_mouse_over {
mouse::Interaction::Pointer
} else {
mouse::Interaction::default()
}
}
fn draw(
&self,
renderer: &mut Renderer,
_style: &renderer::Style,
layout: Layout<'_>,
cursor_position: Point,
_viewport: &Rectangle,
) {
let bounds = layout.bounds();
let is_mouse_over = bounds.contains(cursor_position);
// TODO:
let background_color = if is_mouse_over {
Color::new(0.5, 0.5, 0.5, 0.2)
} else {
Color::TRANSPARENT
};
renderer.fill_quad(
renderer::Quad {
bounds,
border_color: Color::BLACK,
border_width: 1.0,
border_radius: 0.0,
},
background_color,
);
// TODO:
// 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,
// });
// 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,
// })
// }
}
}
impl<'a, P: Param> ParamSlider<'a, P, backend::Renderer> {
/// Convert this [`ParamSlider`] into an [`Element`] with the correct message. You should have a
/// variant on your own message type that wraps around [`ParamMessage`] so you can forward those
/// messages to
/// [`IcedEditor::handle_param_message()`][crate::IcedEditor::handle_param_message()].
pub fn map<Message, F>(self, f: F) -> Element<'a, Message>
where
Message: 'static,
F: Fn(ParamMessage) -> Message + 'static,
{
Element::from(self).map(f)
}
}
impl<'a, P: Param> From<ParamSlider<'a, P, backend::Renderer>> for Element<'a, ParamMessage> {
fn from(widget: ParamSlider<'a, P, backend::Renderer>) -> Self {
Element::new(widget)
}
}

View File

@@ -152,7 +152,7 @@ impl<E: IcedEditor> Application for IcedEditorWrapperApplication<E> {
}
#[inline]
fn renderer_settings() -> iced_baseview::renderer::settings::Settings {
fn renderer_settings() -> iced_baseview::backend::settings::Settings {
E::renderer_settings()
}
}