use std::f64::consts::TAU;
use druid::{
kurbo::{self, BezPath, Shape, Size},
piet::RenderContext,
widget::prelude::*,
Point, Vec2,
};
pub struct Knob {
initial_data: f32,
value_preview: f32,
click: Point,
}
impl Knob {
pub fn new() -> Self {
Self {
initial_data: 0.0,
value_preview: 0.0,
click: Point::ORIGIN,
}
}
}
const SPEED: f32 = 10e-3;
impl Widget<f32> for Knob {
fn event(&mut self, ctx: &mut EventCtx, event: &Event, data: &mut f32, _env: &Env) {
match event {
Event::MouseDown(evt) => {
if !ctx.is_disabled() {
ctx.set_active(true);
self.click = evt.pos;
self.value_preview = *data;
self.initial_data = *data;
ctx.request_paint();
}
}
Event::MouseMove(evt) => {
if ctx.is_active() {
let off = evt.pos - self.click;
self.value_preview = (self.initial_data + (off.x - off.y) as f32 * SPEED)
.min(1.0)
.max(0.0);
ctx.request_paint();
}
}
Event::MouseUp(evt) => {
if ctx.is_active() && !ctx.is_disabled() {
let off = evt.pos - self.click;
*data = (self.initial_data + (off.x - off.y) as f32 * SPEED)
.min(1.0)
.max(0.0);
ctx.request_paint();
}
ctx.set_active(false);
}
_ => (),
}
}
fn lifecycle(&mut self, ctx: &mut LifeCycleCtx, event: &LifeCycle, _data: &f32, _env: &Env) {
if let LifeCycle::HotChanged(_) | LifeCycle::DisabledChanged(_) = event {
ctx.request_paint();
}
}
fn update(&mut self, ctx: &mut UpdateCtx, _old_data: &f32, _data: &f32, _env: &Env) {
ctx.request_paint();
}
fn layout(
&mut self,
_ctx: &mut LayoutCtx,
bc: &BoxConstraints,
_data: &f32,
_env: &Env,
) -> Size {
bc.max()
}
fn paint(&mut self, ctx: &mut PaintCtx, data: &f32, env: &Env) {
let size = ctx.size();
let center = (size / 2.0).to_vec2().to_point();
let radius = size.width / 2.0 * 0.7;
let arc_path: BezPath = kurbo::Arc {
center,
radii: Vec2::new(radius, radius),
start_angle: 0.0,
sweep_angle: if ctx.is_active() {
self.value_preview as f64 * TAU
} else {
*data as f64 * TAU
},
x_rotation: -TAU / 4.0,
}
.into_path(0.1);
let mut wheel = arc_path.clone();
wheel.line_to(center);
wheel.line_to(center - Vec2::new(0.0, radius));
ctx.fill(wheel, &env.get(crate::theme::ACCENT));
ctx.stroke(arc_path, &env.get(crate::theme::ACCENT_DIM), 2.0);
}
}