aboutsummaryrefslogtreecommitdiff
path: root/src/gui/circular.rs
diff options
context:
space:
mode:
authorNathan Reiner <nathan@nathanreiner.xyz>2023-07-26 09:42:17 +0200
committerNathan Reiner <nathan@nathanreiner.xyz>2023-07-26 09:42:17 +0200
commit7679fcc3a0c4fadea00a1a320938851b1518028d (patch)
treeede3c597dbef79f282e7465aeb8652f20bc0515a /src/gui/circular.rs
parente1cb45be6575c805e3b0ab1e2d03d4047acf88d1 (diff)
add viewmanager
Diffstat (limited to 'src/gui/circular.rs')
-rw-r--r--src/gui/circular.rs421
1 files changed, 0 insertions, 421 deletions
diff --git a/src/gui/circular.rs b/src/gui/circular.rs
deleted file mode 100644
index 3a35e02..0000000
--- a/src/gui/circular.rs
+++ /dev/null
@@ -1,421 +0,0 @@
-//! Show a circular progress indicator.
-use iced::advanced::layout;
-use iced::advanced::renderer;
-use iced::advanced::widget::tree::{self, Tree};
-use iced::advanced::{Clipboard, Layout, Renderer, Shell, Widget};
-use iced::event;
-use iced::mouse;
-use iced::time::Instant;
-use iced::widget::canvas;
-use iced::window::{self, RedrawRequest};
-use iced::{
- Background, Color, Element, Event, Length, Rectangle, Size, Vector,
-};
-
-use super::easing::{self, Easing};
-
-use std::f32::consts::PI;
-use std::time::Duration;
-
-const MIN_RADIANS: f32 = PI / 8.0;
-const WRAP_RADIANS: f32 = 2.0 * PI - PI / 4.0;
-const BASE_ROTATION_SPEED: u32 = u32::MAX / 80;
-
-#[allow(missing_debug_implementations)]
-pub struct Circular<'a, Theme>
-where
- Theme: StyleSheet,
-{
- size: f32,
- bar_height: f32,
- style: <Theme as StyleSheet>::Style,
- easing: &'a Easing,
- cycle_duration: Duration,
- rotation_duration: Duration,
-}
-
-impl<'a, Theme> Circular<'a, Theme>
-where
- Theme: StyleSheet,
-{
- /// Creates a new [`Circular`] with the given content.
- pub fn new() -> Self {
- Circular {
- size: 40.0,
- bar_height: 4.0,
- style: <Theme as StyleSheet>::Style::default(),
- easing: &easing::STANDARD,
- cycle_duration: Duration::from_millis(600),
- rotation_duration: Duration::from_secs(2),
- }
- }
-
- /// Sets the size of the [`Circular`].
- pub fn size(mut self, size: f32) -> Self {
- self.size = size;
- self
- }
-
- /// Sets the bar height of the [`Circular`].
- pub fn bar_height(mut self, bar_height: f32) -> Self {
- self.bar_height = bar_height;
- self
- }
-
- /// Sets the style variant of this [`Circular`].
- pub fn style(mut self, style: <Theme as StyleSheet>::Style) -> Self {
- self.style = style;
- self
- }
-
- /// Sets the easing of this [`Circular`].
- pub fn easing(mut self, easing: &'a Easing) -> Self {
- self.easing = easing;
- self
- }
-
- /// Sets the cycle duration of this [`Circular`].
- pub fn cycle_duration(mut self, duration: Duration) -> Self {
- self.cycle_duration = duration / 2;
- self
- }
-
- /// Sets the base rotation duration of this [`Circular`]. This is the duration that a full
- /// rotation would take if the cycle rotation were set to 0.0 (no expanding or contracting)
- pub fn rotation_duration(mut self, duration: Duration) -> Self {
- self.rotation_duration = duration;
- self
- }
-}
-
-impl<'a, Theme> Default for Circular<'a, Theme>
-where
- Theme: StyleSheet,
-{
- fn default() -> Self {
- Self::new()
- }
-}
-
-#[derive(Clone, Copy)]
-enum Animation {
- Expanding {
- start: Instant,
- progress: f32,
- rotation: u32,
- last: Instant,
- },
- Contracting {
- start: Instant,
- progress: f32,
- rotation: u32,
- last: Instant,
- },
-}
-
-impl Default for Animation {
- fn default() -> Self {
- Self::Expanding {
- start: Instant::now(),
- progress: 0.0,
- rotation: 0,
- last: Instant::now(),
- }
- }
-}
-
-impl Animation {
- fn next(&self, additional_rotation: u32, now: Instant) -> Self {
- match self {
- Self::Expanding { rotation, .. } => Self::Contracting {
- start: now,
- progress: 0.0,
- rotation: rotation.wrapping_add(additional_rotation),
- last: now,
- },
- Self::Contracting { rotation, .. } => Self::Expanding {
- start: now,
- progress: 0.0,
- rotation: rotation.wrapping_add(
- BASE_ROTATION_SPEED.wrapping_add(
- ((WRAP_RADIANS / (2.0 * PI)) * u32::MAX as f32) as u32,
- ),
- ),
- last: now,
- },
- }
- }
-
- fn start(&self) -> Instant {
- match self {
- Self::Expanding { start, .. } | Self::Contracting { start, .. } => {
- *start
- }
- }
- }
-
- fn last(&self) -> Instant {
- match self {
- Self::Expanding { last, .. } | Self::Contracting { last, .. } => {
- *last
- }
- }
- }
-
- fn timed_transition(
- &self,
- cycle_duration: Duration,
- rotation_duration: Duration,
- now: Instant,
- ) -> Self {
- let elapsed = now.duration_since(self.start());
- let additional_rotation = ((now - self.last()).as_secs_f32()
- / rotation_duration.as_secs_f32()
- * (u32::MAX) as f32) as u32;
-
- match elapsed {
- elapsed if elapsed > cycle_duration => {
- self.next(additional_rotation, now)
- }
- _ => self.with_elapsed(
- cycle_duration,
- additional_rotation,
- elapsed,
- now,
- ),
- }
- }
-
- fn with_elapsed(
- &self,
- cycle_duration: Duration,
- additional_rotation: u32,
- elapsed: Duration,
- now: Instant,
- ) -> Self {
- let progress = elapsed.as_secs_f32() / cycle_duration.as_secs_f32();
- match self {
- Self::Expanding {
- start, rotation, ..
- } => Self::Expanding {
- start: *start,
- progress,
- rotation: rotation.wrapping_add(additional_rotation),
- last: now,
- },
- Self::Contracting {
- start, rotation, ..
- } => Self::Contracting {
- start: *start,
- progress,
- rotation: rotation.wrapping_add(additional_rotation),
- last: now,
- },
- }
- }
-
- fn rotation(&self) -> f32 {
- match self {
- Self::Expanding { rotation, .. }
- | Self::Contracting { rotation, .. } => {
- *rotation as f32 / u32::MAX as f32
- }
- }
- }
-}
-
-#[derive(Default)]
-struct State {
- animation: Animation,
- cache: canvas::Cache,
-}
-
-impl<'a, Message, Theme> Widget<Message, iced::Renderer<Theme>>
- for Circular<'a, Theme>
-where
- Message: 'a + Clone,
- Theme: StyleSheet,
-{
- fn tag(&self) -> tree::Tag {
- tree::Tag::of::<State>()
- }
-
- fn state(&self) -> tree::State {
- tree::State::new(State::default())
- }
-
- fn width(&self) -> Length {
- Length::Fixed(self.size)
- }
-
- fn height(&self) -> Length {
- Length::Fixed(self.size)
- }
-
- fn layout(
- &self,
- _renderer: &iced::Renderer<Theme>,
- limits: &layout::Limits,
- ) -> layout::Node {
- let limits = limits.width(self.size).height(self.size);
- let size = limits.resolve(Size::ZERO);
-
- layout::Node::new(size)
- }
-
- fn on_event(
- &mut self,
- tree: &mut Tree,
- event: Event,
- _layout: Layout<'_>,
- _cursor: mouse::Cursor,
- _renderer: &iced::Renderer<Theme>,
- _clipboard: &mut dyn Clipboard,
- shell: &mut Shell<'_, Message>,
- ) -> event::Status {
- const FRAME_RATE: u64 = 60;
-
- let state = tree.state.downcast_mut::<State>();
-
- if let Event::Window(window::Event::RedrawRequested(now)) = event {
- state.animation = state.animation.timed_transition(
- self.cycle_duration,
- self.rotation_duration,
- now,
- );
-
- state.cache.clear();
- shell.request_redraw(RedrawRequest::At(
- now + Duration::from_millis(1000 / FRAME_RATE),
- ));
- }
-
- event::Status::Ignored
- }
-
- fn draw(
- &self,
- tree: &Tree,
- renderer: &mut iced::Renderer<Theme>,
- theme: &Theme,
- _style: &renderer::Style,
- layout: Layout<'_>,
- _cursor: mouse::Cursor,
- _viewport: &Rectangle,
- ) {
- let state = tree.state.downcast_ref::<State>();
- let bounds = layout.bounds();
- let custom_style =
- <Theme as StyleSheet>::appearance(theme, &self.style);
-
- let geometry = state.cache.draw(renderer, bounds.size(), |frame| {
- let track_radius = frame.width() / 2.0 - self.bar_height;
- let track_path = canvas::Path::circle(frame.center(), track_radius);
-
- frame.stroke(
- &track_path,
- canvas::Stroke::default()
- .with_color(custom_style.track_color)
- .with_width(self.bar_height),
- );
-
- let mut builder = canvas::path::Builder::new();
-
- let start = state.animation.rotation() * 2.0 * PI;
-
- match state.animation {
- Animation::Expanding { progress, .. } => {
- builder.arc(canvas::path::Arc {
- center: frame.center(),
- radius: track_radius,
- start_angle: start,
- end_angle: start
- + MIN_RADIANS
- + WRAP_RADIANS * (self.easing.y_at_x(progress)),
- });
- }
- Animation::Contracting { progress, .. } => {
- builder.arc(canvas::path::Arc {
- center: frame.center(),
- radius: track_radius,
- start_angle: start
- + WRAP_RADIANS * (self.easing.y_at_x(progress)),
- end_angle: start + MIN_RADIANS + WRAP_RADIANS,
- });
- }
- }
-
- let bar_path = builder.build();
-
- frame.stroke(
- &bar_path,
- canvas::Stroke::default()
- .with_color(custom_style.bar_color)
- .with_width(self.bar_height),
- );
- });
-
- renderer.with_translation(
- Vector::new(bounds.x, bounds.y),
- |renderer| {
- use iced::advanced::graphics::geometry::Renderer as _;
-
- renderer.draw(vec![geometry]);
- },
- );
- }
-}
-
-impl<'a, Message, Theme> From<Circular<'a, Theme>>
- for Element<'a, Message, iced::Renderer<Theme>>
-where
- Message: Clone + 'a,
- Theme: StyleSheet + 'a,
-{
- fn from(circular: Circular<'a, Theme>) -> Self {
- Self::new(circular)
- }
-}
-
-#[derive(Debug, Clone, Copy)]
-pub struct Appearance {
- /// The [`Background`] of the progress indicator.
- pub background: Option<Background>,
- /// The track [`Color`] of the progress indicator.
- pub track_color: Color,
- /// The bar [`Color`] of the progress indicator.
- pub bar_color: Color,
-}
-
-impl std::default::Default for Appearance {
- fn default() -> Self {
- Self {
- background: None,
- track_color: Color::TRANSPARENT,
- bar_color: Color::BLACK,
- }
- }
-}
-
-/// A set of rules that dictate the style of an indicator.
-pub trait StyleSheet {
- /// The supported style of the [`StyleSheet`].
- type Style: Default;
-
- /// Produces the active [`Appearance`] of a indicator.
- fn appearance(&self, style: &Self::Style) -> Appearance;
-}
-
-impl StyleSheet for iced::Theme {
- type Style = ();
-
- fn appearance(&self, _style: &Self::Style) -> Appearance {
- let palette = self.extended_palette();
-
- Appearance {
- background: None,
- track_color: palette.background.weak.color,
- bar_color: palette.primary.base.color,
- }
- }
-}