use super::{Widget, WidgetBase, WidgetId, LayoutContext, PaintContext, EventContext};
use crate::css::{ClassList, WidgetState};
use crate::event::{Event, EventResult};
use crate::geometry::{Color, Point, Rect, Size};
use crate::layout::{Constraints, LayoutResult};
use crate::render::Painter;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum SpinnerSize {
Small,
#[default]
Medium,
Large,
XLarge,
}
impl SpinnerSize {
pub fn pixels(&self) -> f32 {
match self {
SpinnerSize::Small => 16.0,
SpinnerSize::Medium => 24.0,
SpinnerSize::Large => 32.0,
SpinnerSize::XLarge => 48.0,
}
}
pub fn stroke_width(&self) -> f32 {
match self {
SpinnerSize::Small => 2.0,
SpinnerSize::Medium => 3.0,
SpinnerSize::Large => 4.0,
SpinnerSize::XLarge => 5.0,
}
}
}
pub struct Spinner {
base: WidgetBase,
size: SpinnerSize,
color: Option<Color>,
#[allow(dead_code)]
rotation: f32,
}
impl Spinner {
pub fn new() -> Self {
Self {
base: WidgetBase::new().with_class("spinner"),
size: SpinnerSize::default(),
color: None,
rotation: 0.0,
}
}
pub fn size(mut self, size: SpinnerSize) -> Self {
self.size = size;
self
}
pub fn color(mut self, color: Color) -> Self {
self.color = Some(color);
self
}
pub fn class(mut self, class: &str) -> Self {
self.base.classes.add(class);
self
}
}
impl Default for Spinner {
fn default() -> Self {
Self::new()
}
}
impl Widget for Spinner {
fn id(&self) -> WidgetId {
self.base.id
}
fn type_name(&self) -> &'static str {
"spinner"
}
fn element_id(&self) -> Option<&str> {
self.base.element_id.as_deref()
}
fn classes(&self) -> &ClassList {
&self.base.classes
}
fn state(&self) -> WidgetState {
self.base.state
}
fn intrinsic_size(&self, _ctx: &LayoutContext) -> Size {
let size = self.size.pixels();
Size::new(size, size)
}
fn layout(&mut self, constraints: Constraints, ctx: &LayoutContext) -> LayoutResult {
let size = constraints.constrain(self.intrinsic_size(ctx));
self.base.bounds.size = size;
LayoutResult::new(size)
}
fn paint(&self, painter: &mut Painter, rect: Rect, ctx: &PaintContext) {
let theme = ctx.style_ctx.theme;
let color = self.color.unwrap_or(theme.colors.primary);
let size = self.size.pixels();
let stroke = self.size.stroke_width();
let center_x = rect.x() + rect.width() / 2.0;
let center_y = rect.y() + rect.height() / 2.0;
let radius = (size - stroke) / 2.0;
let _track_color = color.with_alpha(0.2);
let segments = 12;
for i in 0..segments {
let angle = (i as f32) * (std::f32::consts::PI * 2.0 / segments as f32);
let x = center_x + angle.cos() * radius;
let y = center_y + angle.sin() * radius;
let alpha = ((i as f32 / segments as f32) * 0.8 + 0.2).min(1.0);
let segment_color = color.with_alpha(alpha);
painter.fill_rect(
Rect::new(x - stroke / 2.0, y - stroke / 2.0, stroke, stroke),
segment_color,
);
}
painter.draw_text(
"⟳",
Point::new(
center_x - size * 0.3,
center_y + size * 0.25,
),
color,
size * 0.8,
);
}
fn handle_event(&mut self, _event: &Event, _ctx: &mut EventContext) -> EventResult {
EventResult::Ignored
}
fn bounds(&self) -> Rect {
self.base.bounds
}
fn set_bounds(&mut self, bounds: Rect) {
self.base.bounds = bounds;
}
}