use conv::{ApproxFrom, ApproxInto, RoundToNearest};
use std::fmt::Debug;
use std::ops::{Add, Sub};
use super::DragHandle;
use kas::event::NavKey;
use kas::prelude::*;
pub trait SliderType:
Copy
+ Debug
+ PartialOrd
+ Add<Output = Self>
+ Sub<Output = Self>
+ ApproxInto<f64>
+ ApproxFrom<f64, RoundToNearest>
+ 'static
{
}
impl<
T: Copy
+ Debug
+ PartialOrd
+ Add<Output = Self>
+ Sub<Output = T>
+ ApproxInto<f64>
+ ApproxFrom<f64, RoundToNearest>
+ 'static,
> SliderType for T
{
}
#[handler(send=noauto, msg = T)]
#[widget(config(key_nav = true))]
#[derive(Clone, Debug, Default, Widget)]
pub struct Slider<T: SliderType, D: Directional> {
#[widget_core]
core: CoreData,
direction: D,
range: (T, T),
step: T,
value: T,
handle_size: Size,
#[widget]
handle: DragHandle,
}
impl<T: SliderType, D: Directional + Default> Slider<T, D> {
pub fn new(min: T, max: T, step: T) -> Self {
Slider::new_with_direction(min, max, step, D::default())
}
}
impl<T: SliderType, D: Directional> Slider<T, D> {
#[inline]
pub fn new_with_direction(min: T, max: T, step: T, direction: D) -> Self {
assert!(min <= max);
let value = min;
Slider {
core: Default::default(),
direction,
range: (min, max),
step,
value,
handle_size: Default::default(),
handle: DragHandle::new(),
}
}
#[inline]
pub fn with_value(mut self, value: T) -> Self {
self.value = value;
self
}
#[inline]
pub fn value(&self) -> T {
self.value
}
pub fn set_value(&mut self, mut value: T) -> TkAction {
if value < self.range.0 {
value = self.range.0;
} else if value > self.range.1 {
value = self.range.1;
}
if value == self.value {
TkAction::None
} else {
self.value = value;
self.handle.set_offset(self.offset()).1
}
}
fn offset(&self) -> Coord {
let a: f64 = (self.value - self.range.0).approx_into().unwrap();
let b: f64 = (self.range.1 - self.range.0).approx_into().unwrap();
let max_offset = self.handle.max_offset();
let mut frac = a / b;
if self.direction.is_reversed() {
frac = 1.0 - frac;
}
match self.direction.is_vertical() {
false => Coord((max_offset.0 as f64 * frac) as i32, 0),
true => Coord(0, (max_offset.1 as f64 * frac) as i32),
}
}
fn set_offset(&mut self, offset: Coord) -> bool {
let b: f64 = (self.range.1 - self.range.0).approx_into().unwrap();
let max_offset = self.handle.max_offset();
let mut a = match self.direction.is_vertical() {
false => b * offset.0 as f64 / max_offset.0 as f64,
true => b * offset.1 as f64 / max_offset.1 as f64,
};
if self.direction.is_reversed() {
a = b - a;
}
let value = T::approx_from(a).unwrap() + self.range.0;
let value = if !(value >= self.range.0) {
self.range.0
} else if !(value <= self.range.1) {
self.range.1
} else {
value
};
if value != self.value {
self.value = value;
return true;
}
false
}
}
impl<T: SliderType, D: Directional> Layout for Slider<T, D> {
fn size_rules(&mut self, size_handle: &mut dyn SizeHandle, axis: AxisInfo) -> SizeRules {
let (mut size, min_len) = size_handle.slider();
if self.direction.is_vertical() {
size = size.transpose();
}
self.handle_size = size;
let margins = (0, 0);
if self.direction.is_vertical() == axis.is_vertical() {
SizeRules::new(min_len, min_len, margins, StretchPolicy::HighUtility)
} else {
SizeRules::fixed(size.1, margins)
}
}
fn set_rect(&mut self, rect: Rect, align: AlignHints) {
self.core.rect = rect;
self.handle.set_rect(rect, align);
let size = self.handle_size.min(rect.size);
let _ = self.handle.set_size_and_offset(size, self.offset());
}
fn find_id(&self, coord: Coord) -> Option<WidgetId> {
if !self.rect().contains(coord) {
return None;
}
self.handle.find_id(coord).or(Some(self.id()))
}
fn draw(&self, draw_handle: &mut dyn DrawHandle, mgr: &event::ManagerState, disabled: bool) {
let dir = self.direction.as_direction();
let state = self.input_state(mgr, disabled) | self.handle.input_state(mgr, disabled);
draw_handle.slider(self.core.rect, self.handle.rect(), dir, state);
}
}
impl<T: SliderType, D: Directional> event::SendEvent for Slider<T, D> {
fn send(&mut self, mgr: &mut Manager, id: WidgetId, event: Event) -> Response<Self::Msg> {
if self.is_disabled() {
return Response::Unhandled(event);
}
let offset = if id <= self.handle.id() {
match self.handle.send(mgr, id, event).try_into() {
Ok(res) => return res,
Err(offset) => offset,
}
} else {
match event {
Event::NavKey(key) => {
let rev = self.direction.is_reversed();
let v = match key {
NavKey::Left | NavKey::Up => match rev {
false => self.value - self.step,
true => self.value + self.step,
},
NavKey::Right | NavKey::Down => match rev {
false => self.value + self.step,
true => self.value - self.step,
},
NavKey::PageUp | NavKey::PageDown => {
let mut x = self.step + self.step;
x = x + x;
x = x + x;
x = x + x;
match rev == (key == NavKey::PageDown) {
false => self.value + x,
true => self.value - x,
}
}
NavKey::Home => self.range.0,
NavKey::End => self.range.1,
};
let action = self.set_value(v);
return if action == TkAction::None {
Response::None
} else {
mgr.send_action(action);
Response::Msg(self.value)
};
}
Event::PressStart { source, coord, .. } => {
self.handle.handle_press_on_track(mgr, source, coord)
}
ev @ _ => return Response::Unhandled(ev),
}
};
let r = if self.set_offset(offset) {
Response::Msg(self.value)
} else {
Response::None
};
*mgr += self.handle.set_offset(self.offset()).1;
r
}
}