use std::f64::consts::PI;
use tracing::{instrument, trace};
use druid::kurbo::Line;
use druid::widget::prelude::*;
use druid::{theme, Color, Data, KeyOrValue, Point, Vec2};
pub struct Spinner {
t: f64,
color: KeyOrValue<Color>,
}
impl Spinner {
pub fn new() -> Spinner {
Spinner::default()
}
pub fn with_color(mut self, color: impl Into<KeyOrValue<Color>>) -> Self {
self.color = color.into();
self
}
pub fn set_color(&mut self, color: impl Into<KeyOrValue<Color>>) {
self.color = color.into();
}
}
impl Default for Spinner {
fn default() -> Self {
Spinner {
t: 0.0,
color: theme::TEXT_COLOR.into(),
}
}
}
impl<T: Data> Widget<T> for Spinner {
#[instrument(name = "Spinner", level = "trace", skip(self, ctx, event, _data, _env))]
fn event(&mut self, ctx: &mut EventCtx, event: &Event, _data: &mut T, _env: &Env) {
if let Event::AnimFrame(interval) = event {
self.t += (*interval as f64) * 1e-9;
if self.t >= 1.0 {
self.t = 0.0;
}
ctx.request_anim_frame();
ctx.request_paint();
}
}
#[instrument(name = "Spinner", level = "trace", skip(self, ctx, event, _data, _env))]
fn lifecycle(&mut self, ctx: &mut LifeCycleCtx, event: &LifeCycle, _data: &T, _env: &Env) {
if let LifeCycle::WidgetAdded = event {
ctx.request_anim_frame();
ctx.request_paint();
}
}
#[instrument(
name = "Spinner",
level = "trace",
skip(self, _ctx, _old_data, _data, _env)
)]
fn update(&mut self, _ctx: &mut UpdateCtx, _old_data: &T, _data: &T, _env: &Env) {}
#[instrument(
name = "Spinner",
level = "trace",
skip(self, _layout_ctx, bc, _data, env)
)]
fn layout(
&mut self,
_layout_ctx: &mut LayoutCtx,
bc: &BoxConstraints,
_data: &T,
env: &Env,
) -> Size {
bc.debug_check("Spinner");
let size = if bc.is_width_bounded() && bc.is_height_bounded() {
bc.max()
} else {
bc.constrain(Size::new(
env.get(theme::BASIC_WIDGET_HEIGHT),
env.get(theme::BASIC_WIDGET_HEIGHT),
))
};
trace!("Computed size: {}", size);
size
}
#[instrument(name = "Spinner", level = "trace", skip(self, ctx, _data, env))]
fn paint(&mut self, ctx: &mut PaintCtx, _data: &T, env: &Env) {
let t = self.t;
let (width, height) = (ctx.size().width, ctx.size().height);
let center = Point::new(width / 2.0, height / 2.0);
let (r, g, b, original_alpha) = Color::as_rgba(self.color.resolve(env));
let scale_factor = width.min(height) / 40.0;
for step in 1..=12 {
let step = f64::from(step);
let fade_t = (t * 12.0 + 1.0).trunc();
let fade = ((fade_t + step).rem_euclid(12.0) / 12.0) + 1.0 / 12.0;
let angle = Vec2::from_angle((step / 12.0) * -2.0 * PI);
let ambit_start = center + (10.0 * scale_factor * angle);
let ambit_end = center + (20.0 * scale_factor * angle);
let color = Color::rgba(r, g, b, fade * original_alpha);
ctx.stroke(
Line::new(ambit_start, ambit_end),
&color,
3.0 * scale_factor,
);
}
}
}