#![allow(dead_code)]
use crate::common::*;
use crate::slider::SliderEvent;
use femtovg::{renderer::OpenGl, Canvas, LineCap, Paint, Path, Solidity};
use std::sync::{Arc, Mutex};
pub struct ControlKnob {
sliding: bool, pub value: f32,
temp: f32,
mouse_down_posy: f32,
shift_pressed: bool,
pub back: Entity,
pub slider: Entity,
pub tick: Entity,
min: f32,
max: f32,
pub is_log: bool,
pub on_change: Option<Arc<Mutex<dyn Fn(f32) -> Event>>>,
pub on_changing: Option<Arc<dyn Fn(&Self,&mut State, Entity)>>,
}
impl ControlKnob {
pub fn new(init: f32, min: f32, max: f32) -> Self {
ControlKnob {
sliding: false,
value: init,
temp: init,
mouse_down_posy: 0.0,
shift_pressed: false,
back: Entity::null(),
slider: Entity::null(),
tick: Entity::null(),
min,
max,
is_log: false,
on_change: None,
on_changing: None,
}
}
pub fn with_log_scale(mut self) -> Self {
self.is_log = true;
self
}
pub fn on_change<F>(mut self, message: F) -> Self
where
F: Fn(f32) -> Event,
F: 'static,
{
self.on_change = Some(Arc::new(Mutex::new(message)));
self
}
pub fn on_changing<F>(mut self, message: F) -> Self
where
F: Fn(&Self, &mut State, Entity),
F: 'static,
{
self.on_changing = Some(Arc::new(message));
self
}
}
impl Widget for ControlKnob {
type Ret = Entity;
type Data = ();
fn on_build(&mut self, state: &mut State, entity: Entity) -> Self::Ret {
self.back = Element::new().build(state, entity, |builder| {
builder
.set_hoverable(false)
.set_display(Display::None)
.class("back")
});
self.slider = Element::new().build(state, entity, |builder| {
builder
.set_hoverable(false)
.set_display(Display::None)
.class("slider")
});
self.tick = Element::new().build(state, entity, |builder| {
builder
.set_hoverable(false)
.set_display(Display::None)
.class("tick")
});
entity.set_element(state, "knob")
}
fn on_event(&mut self, state: &mut State, entity: Entity, event: &mut Event) {
if let Some(slider_event) = event.message.downcast::<SliderEvent>() {
match slider_event {
SliderEvent::SetValue(val) => {
if event.target == entity {
if event.target == entity {
self.value = ((*val).min(self.max)).max(self.min);
state.insert_event(
Event::new(WindowEvent::Redraw).target(Entity::root()),
);
}
}
}
_ => {}
}
}
if let Some(window_event) = event.message.downcast::<WindowEvent>() {
match window_event {
WindowEvent::MouseDown(button) => {
if event.target == entity && *button == MouseButton::Left {
self.sliding = true;
self.mouse_down_posy = state.mouse.left.pos_down.1;
state.capture(entity);
state.focused = entity;
self.temp = self.value;
}
}
WindowEvent::MouseUp(button) => {
if event.target == entity && *button == MouseButton::Left {
self.sliding = false;
state.release(entity);
self.temp = self.value;
}
}
WindowEvent::MouseMove(_, y) => {
if event.target == entity {
if self.sliding {
let dy = self.mouse_down_posy - *y;
let normalised = if state.modifiers.shift {
dy / 1000.0
} else {
dy / 200.0
};
let new_val = if self.is_log {
let _t = self.temp.log10()
+ (self.max.log10() - self.min.log10()) * normalised;
10.0f32.powf(
self.temp.log10()
+ (self.max.log10() - self.min.log10()) * normalised,
)
} else {
self.temp + (self.max - self.min) * normalised
};
self.value = (new_val.min(self.max)).max(self.min);
if let Some(on_change) = &self.on_change {
let mut event = (on_change.lock().unwrap())(self.value);
if event.target == Entity::null() {
event.target = entity;
}
event.origin = entity;
state.insert_event(event);
}
if let Some(on_changing) = &self.on_changing {
(on_changing)(self, state, entity);
}
state.insert_event(
Event::new(SliderEvent::ValueChanged(self.value)).target(entity),
);
state.insert_event(
Event::new(WindowEvent::Redraw).target(Entity::root()),
);
}
}
}
WindowEvent::KeyDown(keycode, _) => {
if *keycode == keyboard_types::Code::ShiftLeft {
if !self.shift_pressed {
self.shift_pressed = true;
}
self.mouse_down_posy = state.mouse.cursory;
self.temp = self.value;
}
}
WindowEvent::KeyUp(keycode, _) => {
if *keycode == keyboard_types::Code::ShiftLeft {
if self.shift_pressed {
self.shift_pressed = false;
}
self.mouse_down_posy = state.mouse.cursory;
self.temp = self.value;
}
}
_ => {}
}
}
}
fn on_draw(&mut self, state: &mut State, entity: Entity, canvas: &mut Canvas<OpenGl>) {
if state.data.get_visibility(entity) == Visibility::Invisible {
return;
}
let opacity = state.data.get_opacity(entity);
let mut knob_color: femtovg::Color = entity.get_background_color(state).into();
knob_color.set_alphaf(knob_color.a * opacity);
let mut back_color: femtovg::Color = self.back.get_background_color(state).into();
back_color.set_alphaf(back_color.a * opacity);
let mut slider_color: femtovg::Color = self.slider.get_background_color(state).into();
slider_color.set_alphaf(slider_color.a * opacity);
let mut tick_color: femtovg::Color = self.tick.get_background_color(state).into();
tick_color.set_alphaf(tick_color.a * opacity);
let posx = state.data.get_posx(entity);
let posy = state.data.get_posy(entity);
let width = state.data.get_width(entity);
let height = state.data.get_height(entity);
let cx = posx + 0.5 * width;
let cy = posy + 0.5 * height;
let r1 = width / 2.0;
let r0 = r1 - 10.0;
use std::f32::consts::PI;
let start = -(PI + PI / 4.0);
let end = PI / 4.0;
let (min, max, value) = if self.is_log {
(self.min.log10(), self.max.log10(), self.value.log10())
} else {
(self.min, self.max, self.value)
};
let zero_position = if self.is_log {
start
} else {
(-min / (max - min)) * (end - start) + start
};
let normalised = (value - min) / (max - min);
let current = normalised * (end - start) + start;
canvas.save();
let mut path = Path::new();
path.arc(cx, cy, r1 - 2.5, end, start, Solidity::Solid);
let mut paint = Paint::color(back_color);
paint.set_line_width(5.0);
paint.set_line_cap(LineCap::Butt);
canvas.stroke_path(&mut path, paint);
if current != zero_position {
let mut path = Path::new();
if current > zero_position {
path.arc(cx, cy, r1 - 2.5, current, zero_position, Solidity::Solid);
} else {
path.arc(cx, cy, r1 - 2.5, zero_position, current, Solidity::Solid);
}
let mut paint = Paint::color(slider_color);
paint.set_line_width(5.0);
paint.set_line_cap(LineCap::Butt);
canvas.stroke_path(&mut path, paint);
}
let mut path = Path::new();
path.circle(cx, cy, r0 + 1.0);
let paint = Paint::color(knob_color);
canvas.fill_path(&mut path, paint);
canvas.save();
canvas.translate(cx, cy);
canvas.rotate(current - PI / 2.0);
let mut path = Path::new();
path.circle(0.0, r0 - 2.5, 2.0);
let paint = Paint::color(tick_color);
canvas.fill_path(&mut path, paint);
canvas.restore();
canvas.restore();
}
}