use std::fmt::Debug;
use iced_native::{
event, keyboard, layout, mouse, Clipboard, Element, Event, Hasher, Layout,
Length, Point, Rectangle, Size, Widget,
};
use std::hash::Hash;
use crate::core::{ModulationRange, Normal, NormalParam};
use crate::native::{text_marks, tick_marks};
static DEFAULT_HEIGHT: u16 = 14;
static DEFAULT_SCALAR: f32 = 0.9575;
static DEFAULT_MODIFIER_SCALAR: f32 = 0.02;
#[allow(missing_debug_implementations)]
pub struct HSlider<'a, Message, Renderer: self::Renderer> {
state: &'a mut State,
on_change: Box<dyn Fn(Normal) -> Message>,
scalar: f32,
modifier_scalar: f32,
modifier_keys: keyboard::Modifiers,
width: Length,
height: Length,
style: Renderer::Style,
tick_marks: Option<&'a tick_marks::Group>,
text_marks: Option<&'a text_marks::Group>,
mod_range_1: Option<&'a ModulationRange>,
mod_range_2: Option<&'a ModulationRange>,
}
impl<'a, Message, Renderer: self::Renderer> HSlider<'a, Message, Renderer> {
pub fn new<F>(state: &'a mut State, on_change: F) -> Self
where
F: 'static + Fn(Normal) -> Message,
{
HSlider {
state,
on_change: Box::new(on_change),
scalar: DEFAULT_SCALAR,
modifier_scalar: DEFAULT_MODIFIER_SCALAR,
modifier_keys: keyboard::Modifiers {
control: true,
..Default::default()
},
width: Length::Fill,
height: Length::from(Length::Units(DEFAULT_HEIGHT)),
style: Renderer::Style::default(),
tick_marks: None,
text_marks: None,
mod_range_1: None,
mod_range_2: None,
}
}
pub fn width(mut self, width: Length) -> Self {
self.width = width;
self
}
pub fn height(mut self, height: Length) -> Self {
self.height = height;
self
}
pub fn style(mut self, style: impl Into<Renderer::Style>) -> Self {
self.style = style.into();
self
}
pub fn modifier_keys(mut self, modifier_keys: keyboard::Modifiers) -> Self {
self.modifier_keys = modifier_keys;
self
}
pub fn scalar(mut self, scalar: f32) -> Self {
self.scalar = scalar;
self
}
pub fn modifier_scalar(mut self, scalar: f32) -> Self {
self.modifier_scalar = scalar;
self
}
pub fn tick_marks(mut self, tick_marks: &'a tick_marks::Group) -> Self {
self.tick_marks = Some(tick_marks);
self
}
pub fn text_marks(mut self, text_marks: &'a text_marks::Group) -> Self {
self.text_marks = Some(text_marks);
self
}
pub fn mod_range(mut self, mod_range: &'a ModulationRange) -> Self {
self.mod_range_1 = Some(mod_range);
self
}
pub fn mod_range_2(mut self, mod_range: &'a ModulationRange) -> Self {
self.mod_range_1 = Some(mod_range);
self
}
}
#[derive(Debug, Clone)]
pub struct State {
pub normal_param: NormalParam,
is_dragging: bool,
prev_drag_x: f32,
continuous_normal: f32,
pressed_modifiers: keyboard::Modifiers,
last_click: Option<mouse::Click>,
tick_marks_cache: crate::graphics::tick_marks::PrimitiveCache,
text_marks_cache: crate::graphics::text_marks::PrimitiveCache,
}
impl State {
pub fn new(normal_param: NormalParam) -> Self {
Self {
normal_param,
is_dragging: false,
prev_drag_x: 0.0,
continuous_normal: normal_param.value.as_f32(),
pressed_modifiers: Default::default(),
last_click: None,
tick_marks_cache: Default::default(),
text_marks_cache: Default::default(),
}
}
pub fn is_dragging(&self) -> bool {
self.is_dragging
}
}
impl<'a, Message, Renderer> Widget<Message, Renderer>
for HSlider<'a, Message, Renderer>
where
Renderer: self::Renderer,
{
fn width(&self) -> Length {
Length::Shrink
}
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,
messages: &mut Vec<Message>,
_renderer: &Renderer,
_clipboard: Option<&dyn Clipboard>,
) -> event::Status {
match event {
Event::Mouse(mouse_event) => match mouse_event {
mouse::Event::CursorMoved { .. } => {
if self.state.is_dragging {
let bounds_width = layout.bounds().width;
if bounds_width > 0.0 {
let mut movement_x = (cursor_position.x
- self.state.prev_drag_x)
/ bounds_width;
if self
.state
.pressed_modifiers
.matches(self.modifier_keys)
{
movement_x *= self.modifier_scalar;
} else {
movement_x *= self.scalar;
}
let normal =
self.state.continuous_normal + movement_x;
self.state.continuous_normal = normal;
self.state.prev_drag_x = cursor_position.x;
self.state.normal_param.value = normal.into();
messages.push((self.on_change)(
self.state.normal_param.value,
));
return event::Status::Captured;
}
}
}
mouse::Event::ButtonPressed(mouse::Button::Left) => {
if layout.bounds().contains(cursor_position) {
let click = mouse::Click::new(
cursor_position,
self.state.last_click,
);
match click.kind() {
mouse::click::Kind::Single => {
self.state.is_dragging = true;
self.state.prev_drag_x = cursor_position.x;
}
_ => {
self.state.is_dragging = false;
self.state.normal_param.value =
self.state.normal_param.default;
messages.push((self.on_change)(
self.state.normal_param.value,
));
}
}
self.state.last_click = Some(click);
return event::Status::Captured;
}
}
mouse::Event::ButtonReleased(mouse::Button::Left) => {
self.state.is_dragging = false;
self.state.continuous_normal =
self.state.normal_param.value.as_f32();
return event::Status::Captured;
}
_ => {}
},
Event::Keyboard(keyboard_event) => match keyboard_event {
keyboard::Event::KeyPressed { modifiers, .. } => {
self.state.pressed_modifiers = modifiers;
return event::Status::Captured;
}
keyboard::Event::KeyReleased { modifiers, .. } => {
self.state.pressed_modifiers = modifiers;
return event::Status::Captured;
}
_ => {}
},
_ => {}
}
event::Status::Ignored
}
fn draw(
&self,
renderer: &mut Renderer,
_defaults: &Renderer::Defaults,
layout: Layout<'_>,
cursor_position: Point,
_viewport: &Rectangle,
) -> Renderer::Output {
renderer.draw(
layout.bounds(),
cursor_position,
self.state.normal_param.value,
self.state.is_dragging,
self.mod_range_1,
self.mod_range_2,
self.tick_marks,
self.text_marks,
&self.style,
&self.state.tick_marks_cache,
&self.state.text_marks_cache,
)
}
fn hash_layout(&self, state: &mut Hasher) {
struct Marker;
std::any::TypeId::of::<Marker>().hash(state);
self.width.hash(state);
self.height.hash(state);
}
}
pub trait Renderer: iced_native::Renderer {
type Style: Default;
fn draw(
&mut self,
bounds: Rectangle,
cursor_position: Point,
normal: Normal,
is_dragging: bool,
mod_range_1: Option<&ModulationRange>,
mod_range_2: Option<&ModulationRange>,
tick_marks: Option<&tick_marks::Group>,
text_marks: Option<&text_marks::Group>,
style: &Self::Style,
tick_marks_cache: &crate::tick_marks::PrimitiveCache,
text_marks_cache: &crate::text_marks::PrimitiveCache,
) -> Self::Output;
}
impl<'a, Message, Renderer> From<HSlider<'a, Message, Renderer>>
for Element<'a, Message, Renderer>
where
Renderer: 'a + self::Renderer,
Message: 'a,
{
fn from(
h_slider: HSlider<'a, Message, Renderer>,
) -> Element<'a, Message, Renderer> {
Element::new(h_slider)
}
}