use iced_native::{
input::{mouse, ButtonState, keyboard},
layout, Clipboard, Element, Event, Hasher, Layout, Length, Point,
Rectangle, Size, Widget,
};
use std::hash::Hash;
use crate::{Normal, Param};
static DEFAULT_MODIFIER_SCALAR: f32 = 0.02;
#[allow(missing_debug_implementations)]
pub struct HSlider<'a, Message, Renderer: self::Renderer> {
state: &'a mut State,
normal: Normal,
default_normal: Normal,
on_change: Box<dyn Fn((u32, Normal)) -> Message>,
modifier_scalar: f32,
modifier_keys: keyboard::ModifiersState,
width: Length,
style: Renderer::Style,
}
impl<'a, Message, Renderer: self::Renderer>HSlider<'a, Message, Renderer> {
pub fn new<F>(
state: &'a mut State,
param: &impl Param,
on_change: F,
) -> Self
where
F: 'static + Fn((u32, Normal)) -> Message,
{
HSlider {
state,
normal: param.normal(),
default_normal: param.default_normal(),
on_change: Box::new(on_change),
modifier_scalar: DEFAULT_MODIFIER_SCALAR,
modifier_keys: keyboard::ModifiersState {
control: true,
..Default::default()
},
width: Length::Fill,
style: Renderer::Style::default(),
}
}
pub fn width(mut self, width: Length) -> Self {
self.width = width;
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::ModifiersState,
) -> Self {
self.modifier_keys = modifier_keys;
self
}
pub fn modifier_scalar(mut self, scalar: f32) -> Self {
self.modifier_scalar = scalar;
self
}
}
#[derive(Debug, Clone, Copy)]
pub struct State {
id: u32,
is_dragging: bool,
prev_drag_x: f32,
continuous_normal: f32,
pressed_modifiers: keyboard::ModifiersState,
last_click: Option<mouse::Click>,
}
impl State {
pub fn new(param: &impl Param) -> Self {
Self {
id: param.id(),
is_dragging: false,
prev_drag_x: 0.0,
continuous_normal: param.normal().value(),
pressed_modifiers: Default::default(),
last_click: None,
}
}
}
impl<'a, Message, Renderer> Widget<Message, Renderer>
for HSlider<'a, Message, Renderer>
where
Renderer: self::Renderer,
{
fn width(&self) -> Length {
self.width
}
fn height(&self) -> Length {
Length::Shrink
}
fn layout(
&self,
renderer: &Renderer,
limits: &layout::Limits,
) -> layout::Node {
let limits = limits
.width(self.width)
.height(Length::from(Length::Units(
renderer.height(&self.style)
)));
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>,
) {
match event {
Event::Mouse(mouse::Event::Input {
button: mouse::Button::Left,
state,
}) => match state {
ButtonState::Pressed => {
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;
messages.push((self.on_change)(
(self.state.id, self.default_normal)
));
}
}
self.state.last_click = Some(click);
}
}
ButtonState::Released => {
self.state.is_dragging = false;
self.state.continuous_normal = self.normal.value();
}
},
Event::Mouse(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;
}
let normal =
self.state.continuous_normal + movement_x;
self.state.continuous_normal = normal;
self.state.prev_drag_x = cursor_position.x;
messages.push((self.on_change)(
(self.state.id, normal.into())
));
}
}
},
Event::Keyboard(keyboard::Event::Input {
modifiers,
..
}) => {
self.state.pressed_modifiers = modifiers;
},
_ => {}
}
}
fn draw(
&self,
renderer: &mut Renderer,
_defaults: &Renderer::Defaults,
layout: Layout<'_>,
cursor_position: Point,
) -> Renderer::Output {
renderer.draw(
layout.bounds(),
cursor_position,
self.normal,
self.state.is_dragging,
&self.style,
)
}
fn hash_layout(&self, state: &mut Hasher) {
struct Marker;
std::any::TypeId::of::<Marker>().hash(state);
self.width.hash(state);
}
}
pub trait Renderer: iced_native::Renderer {
type Style: Default;
fn height(&self, style_sheet: &Self::Style) -> u16;
fn draw(
&mut self,
bounds: Rectangle,
cursor_position: Point,
normal: Normal,
is_dragging: bool,
style: &Self::Style,
) -> 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)
}
}