use super::Widget;
use core::marker::PhantomData;
use embedded_graphics::{pixelcolor::PixelColor, prelude::*, primitives::Rectangle};
use zest_core::{Constraints, Length, RenderError, Renderer, TouchPhase};
use zest_theme::Theme;
pub struct Spinner<'a, C: PixelColor, M: Clone> {
rect: Rectangle,
angle: i32,
arc_deg: i32,
width: u32,
track: bool,
track_color: Option<C>,
arc_color: Option<C>,
w: Length,
h: Length,
_phantom: PhantomData<&'a M>,
}
impl<'a, C: PixelColor, M: Clone> Spinner<'a, C, M> {
pub fn new(angle: i32) -> Self {
Self {
rect: Rectangle::zero(),
angle,
arc_deg: 90,
width: 5,
track: true,
track_color: None,
arc_color: None,
w: Length::Fill,
h: Length::Fill,
_phantom: PhantomData,
}
}
#[must_use]
pub fn angle(mut self, angle: i32) -> Self {
self.angle = angle;
self
}
#[must_use]
pub fn arc_deg(mut self, arc_deg: i32) -> Self {
self.arc_deg = arc_deg;
self
}
#[must_use]
pub fn width_px(mut self, width: u32) -> Self {
self.width = width;
self
}
#[must_use]
pub fn track(mut self, on: bool) -> Self {
self.track = on;
self
}
#[must_use]
pub fn track_color(mut self, color: C) -> Self {
self.track_color = Some(color);
self
}
#[must_use]
pub fn arc_color(mut self, color: C) -> Self {
self.arc_color = Some(color);
self
}
#[must_use]
pub fn width(mut self, width: impl Into<Length>) -> Self {
self.w = width.into();
self
}
#[must_use]
pub fn height(mut self, height: impl Into<Length>) -> Self {
self.h = height.into();
self
}
fn center(&self) -> Point {
Point::new(
self.rect.top_left.x + self.rect.size.width as i32 / 2,
self.rect.top_left.y + self.rect.size.height as i32 / 2,
)
}
fn radius(&self) -> u32 {
let smaller = self.rect.size.width.min(self.rect.size.height);
(smaller / 2).saturating_sub(self.width.div_ceil(2))
}
}
impl<'a, C: PixelColor, M: Clone> Widget<C, M> for Spinner<'a, C, M> {
fn measure(&mut self, constraints: Constraints) -> Size {
let w = self.w.resolve(constraints.max.width, constraints.max.width);
let h = self
.h
.resolve(constraints.max.height, constraints.max.height);
constraints.clamp(Size::new(w, h))
}
fn preferred_size(&self) -> (Length, Length) {
(self.w, self.h)
}
fn arrange(&mut self, rect: Rectangle) {
self.rect = rect;
}
fn rect(&self) -> Rectangle {
self.rect
}
fn handle_touch(&mut self, _point: Point, _phase: TouchPhase) -> Option<M> {
None
}
fn draw<'t>(
&self,
renderer: &mut dyn Renderer<C>,
theme: &Theme<'t, C>,
) -> Result<(), RenderError> {
let center = self.center();
let radius = self.radius();
if radius == 0 {
return Ok(());
}
let track = self.track_color.unwrap_or(theme.background.divider);
let arc = self.arc_color.unwrap_or(theme.accent.base);
if self.track {
renderer.stroke_arc(center, radius, 0, 360, self.width, track)?;
}
renderer.stroke_arc(center, radius, self.angle, self.arc_deg, self.width, arc)?;
Ok(())
}
}