From 2ebc92319711e6fa0dda310939257334625b59c9 Mon Sep 17 00:00:00 2001 From: Nick Senger Date: Wed, 7 Jun 2023 15:19:11 -0700 Subject: feat: use lyon for easing --- examples/progress_indicators/src/circular.rs | 425 --------------------------- examples/progress_indicators/src/easing.rs | 67 ----- examples/progress_indicators/src/linear.rs | 325 -------------------- examples/progress_indicators/src/main.rs | 104 ------- 4 files changed, 921 deletions(-) delete mode 100644 examples/progress_indicators/src/circular.rs delete mode 100644 examples/progress_indicators/src/easing.rs delete mode 100644 examples/progress_indicators/src/linear.rs delete mode 100644 examples/progress_indicators/src/main.rs (limited to 'examples/progress_indicators/src') diff --git a/examples/progress_indicators/src/circular.rs b/examples/progress_indicators/src/circular.rs deleted file mode 100644 index 2a9f7e1e..00000000 --- a/examples/progress_indicators/src/circular.rs +++ /dev/null @@ -1,425 +0,0 @@ -//! Show a circular progress indicator. -use iced::widget::canvas::{self, Cursor, Program, Renderer as CanvasRenderer}; -use iced::window; -use iced_core::event::{self, Event}; -use iced_core::time::Instant; -use iced_core::widget::tree::{self, Tree}; -use iced_core::window::RedrawRequest; -use iced_core::{layout, Size}; -use iced_core::{renderer, Vector}; -use iced_core::{ - Background, Clipboard, Color, Element, Layout, Length, Point, Rectangle, - Renderer, Shell, Widget, -}; - -use super::easing::{self, Easing}; - -use std::borrow::Cow; -use std::f32::consts::PI; -use std::time::Duration; - -type R = iced_widget::renderer::Renderer; - -const MIN_RADIANS: f32 = PI / 8.0; -const WRAP_RADIANS: f32 = 2.0 * PI - PI / 4.0; -const PROCESSION_VELOCITY: u32 = u32::MAX / 120; - -#[allow(missing_debug_implementations)] -pub struct Circular<'a, Theme> -where - Theme: StyleSheet, -{ - size: f32, - bar_height: f32, - style: ::Style, - easing: Cow<'a, Easing>, - cycle_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: ::Style::default(), - easing: Cow::Borrowed(&easing::STANDARD), - cycle_duration: Duration::from_millis(600), - } - } - - /// 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: ::Style) -> Self { - self.style = style; - self - } - - /// Sets the easing of this [`Circular`]. - pub fn easing(mut self, easing: impl Into>) -> Self { - self.easing = easing.into(); - self - } - - /// Sets the cycle duration of this [`Circular`]. - pub fn cycle_duration(mut self, duration: Duration) -> Self { - self.cycle_duration = duration / 2; - self - } -} - -impl<'a, Theme> Default for Circular<'a, Theme> -where - Theme: StyleSheet, -{ - fn default() -> Self { - Self::new() - } -} - -#[derive(Clone, Copy)] -enum State { - Expanding { - start: Instant, - progress: f32, - procession: u32, - }, - Contracting { - start: Instant, - progress: f32, - procession: u32, - }, -} - -impl Default for State { - fn default() -> Self { - Self::Expanding { - start: Instant::now(), - progress: 0.0, - procession: 0, - } - } -} - -impl State { - fn next(&self, now: Instant) -> Self { - match self { - Self::Expanding { procession, .. } => Self::Contracting { - start: now, - progress: 0.0, - procession: procession.wrapping_add(PROCESSION_VELOCITY), - }, - Self::Contracting { procession, .. } => Self::Expanding { - start: now, - progress: 0.0, - procession: procession.wrapping_add( - PROCESSION_VELOCITY.wrapping_add( - ((WRAP_RADIANS / (2.0 * PI)) * u32::MAX as f32) as u32, - ), - ), - }, - } - } - - fn start(&self) -> Instant { - match self { - Self::Expanding { start, .. } | Self::Contracting { start, .. } => { - *start - } - } - } - - fn timed_transition(&self, cycle_duration: Duration, now: Instant) -> Self { - let elapsed = now.duration_since(self.start()); - - match elapsed { - elapsed if elapsed > cycle_duration => self.next(now), - _ => self.with_elapsed(cycle_duration, elapsed), - } - } - - fn with_elapsed( - &self, - cycle_duration: Duration, - elapsed: Duration, - ) -> Self { - let progress = elapsed.as_secs_f32() / cycle_duration.as_secs_f32(); - match self { - Self::Expanding { - start, procession, .. - } => Self::Expanding { - start: *start, - progress, - procession: procession.wrapping_add(PROCESSION_VELOCITY), - }, - Self::Contracting { - start, procession, .. - } => Self::Contracting { - start: *start, - progress, - procession: procession.wrapping_add(PROCESSION_VELOCITY), - }, - } - } - - fn procession(&self) -> f32 { - match self { - Self::Expanding { procession, .. } - | Self::Contracting { procession, .. } => { - *procession as f32 / u32::MAX as f32 - } - } - } -} - -impl<'a, Message, Theme> Widget> for Circular<'a, Theme> -where - Message: 'a + Clone, - Theme: StyleSheet, -{ - fn tag(&self) -> tree::Tag { - tree::Tag::of::() - } - - 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_widget::renderer::Renderer, - 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_position: Point, - _renderer: &iced_widget::renderer::Renderer, - _clipboard: &mut dyn Clipboard, - shell: &mut Shell<'_, Message>, - ) -> event::Status { - let state = tree.state.downcast_mut::(); - - if let Event::Window(window::Event::RedrawRequested(now)) = event { - *state = state.timed_transition(self.cycle_duration, now); - - shell.request_redraw(RedrawRequest::NextFrame); - } - - event::Status::Ignored - } - - fn draw( - &self, - tree: &Tree, - renderer: &mut R, - theme: &Theme, - _style: &renderer::Style, - layout: Layout<'_>, - _cursor_position: Point, - _viewport: &Rectangle, - ) { - let bounds = layout.bounds(); - let state = tree.state.downcast_ref::(); - - renderer.with_translation( - Vector::new(bounds.x, bounds.y), - |renderer| { - renderer.draw( as Program< - Message, - R, - >>::draw( - &StateWithStyle { - state, - style: &self.style, - bar_height: self.bar_height, - easing: self.easing.as_ref(), - }, - &(), - renderer, - theme, - bounds, - Cursor::Unavailable, - )); - }, - ); - } -} - -impl<'a, Message, Theme> From> - for Element<'a, Message, R> -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, - /// 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, - } - } -} - -struct StateWithStyle<'a, Theme> -where - Theme: StyleSheet, -{ - state: &'a State, - style: &'a ::Style, - easing: &'a Easing, - bar_height: f32, -} - -impl<'a, Message, Theme> - canvas::Program> - for StateWithStyle<'a, Theme> -where - Theme: StyleSheet, -{ - type State = (); - - fn update( - &self, - _state: &mut Self::State, - _event: canvas::Event, - _bounds: Rectangle, - _cursor: canvas::Cursor, - ) -> (canvas::event::Status, Option) { - (canvas::event::Status::Ignored, None) - } - - fn draw( - &self, - _state: &Self::State, - renderer: &iced_widget::renderer::Renderer, - theme: &Theme, - bounds: Rectangle, - _cursor: canvas::Cursor, - ) -> Vec { - let mut frame = canvas::Frame::new(renderer, bounds.size()); - let custom_style = ::appearance(theme, self.style); - let track_radius = frame.width() / 2.0 - self.bar_height; - - if let Some(Background::Color(color)) = custom_style.background { - let background_path = - canvas::Path::circle(frame.center(), track_radius); - frame.fill(&background_path, color); - } - - 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 = self.state.procession() * 2.0 * PI; - - match self.state { - State::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)), - }); - } - State::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), - ); - - vec![frame.into_geometry()] - } -} diff --git a/examples/progress_indicators/src/easing.rs b/examples/progress_indicators/src/easing.rs deleted file mode 100644 index 10479659..00000000 --- a/examples/progress_indicators/src/easing.rs +++ /dev/null @@ -1,67 +0,0 @@ -use flo_curves::bezier::Curve; -use flo_curves::*; -use lazy_static::lazy_static; - -use std::borrow::Cow; - -lazy_static! { - pub static ref EXAMPLES: [Easing; 3] = [ - Easing::CubicBezier(Curve::from_points( - Coord2(0.0, 0.0), - (Coord2(0.05, 0.7), Coord2(0.1, 1.0)), - Coord2(1.0, 1.0), - )), - Easing::CubicBezier(Curve::from_points( - Coord2(0.0, 0.0), - (Coord2(0.3, 0.0), Coord2(0.8, 0.15)), - Coord2(1.0, 1.0), - )), - Easing::CubicBezier(Curve::from_points( - Coord2(0.0, 0.0), - (Coord2(0.2, 0.0), Coord2(0.0, 1.0)), - Coord2(1.0, 1.0), - )) - ]; - pub static ref STANDARD: Easing = { - Easing::CubicBezier(Curve::from_points( - Coord2(0.0, 0.0), - (Coord2(0.2, 0.0), Coord2(0.0, 1.0)), - Coord2(1.0, 1.0), - )) - }; -} - -#[derive(Clone, Debug)] -pub enum Easing { - BezierPath(Vec>), - CubicBezier(Curve), -} - -impl Easing { - pub fn y_at_x(&self, x: f32) -> f32 { - let x = x as f64; - - match self { - Self::BezierPath(curves) => curves - .iter() - .find_map(|curve| { - (curve.start_point().0 <= x && curve.end_point().0 >= x) - .then(|| curve.point_at_pos(x).1 as f32) - }) - .unwrap_or_default(), - Self::CubicBezier(curve) => curve.point_at_pos(x).1 as f32, - } - } -} - -impl<'a> From for Cow<'a, Easing> { - fn from(easing: Easing) -> Self { - Cow::Owned(easing) - } -} - -impl<'a> From<&'a Easing> for Cow<'a, Easing> { - fn from(easing: &'a Easing) -> Self { - Cow::Borrowed(easing) - } -} diff --git a/examples/progress_indicators/src/linear.rs b/examples/progress_indicators/src/linear.rs deleted file mode 100644 index 735892e7..00000000 --- a/examples/progress_indicators/src/linear.rs +++ /dev/null @@ -1,325 +0,0 @@ -//! Show a linear progress indicator. -use iced::window; -use iced_core::event::{self, Event}; -use iced_core::renderer; -use iced_core::time::Instant; -use iced_core::widget::tree::{self, Tree}; -use iced_core::window::RedrawRequest; -use iced_core::{layout, Size}; -use iced_core::{ - Background, Clipboard, Color, Element, Layout, Length, Point, Rectangle, - Shell, Widget, -}; - -use super::easing::{self, Easing}; - -use std::borrow::Cow; -use std::time::Duration; - -#[allow(missing_debug_implementations)] -pub struct Linear<'a, Renderer> -where - Renderer: iced_core::Renderer, - Renderer::Theme: StyleSheet, -{ - width: Length, - height: Length, - style: ::Style, - easing: Cow<'a, Easing>, - cycle_duration: Duration, -} - -impl<'a, Renderer> Linear<'a, Renderer> -where - Renderer: iced_widget::core::Renderer, - Renderer::Theme: StyleSheet, -{ - /// Creates a new [`Linear`] with the given content. - pub fn new() -> Self { - Linear { - width: Length::Fixed(100.0), - height: Length::Fixed(4.0), - style: ::Style::default(), - easing: Cow::Borrowed(&easing::STANDARD), - cycle_duration: Duration::from_millis(600), - } - } - - /// Sets the width of the [`Linear`]. - pub fn width(mut self, width: impl Into) -> Self { - self.width = width.into(); - self - } - - /// Sets the height of the [`Linear`]. - pub fn height(mut self, height: impl Into) -> Self { - self.height = height.into(); - self - } - - /// Sets the style variant of this [`Linear`]. - pub fn style( - mut self, - style: ::Style, - ) -> Self { - self.style = style; - self - } - - /// Sets the motion easing of this [`Linear`]. - pub fn easing(mut self, easing: impl Into>) -> Self { - self.easing = easing.into(); - self - } - - /// Sets the cycle duration of this [`Linear`]. - pub fn cycle_duration(mut self, duration: Duration) -> Self { - self.cycle_duration = duration / 2; - self - } -} - -impl<'a, Renderer> Default for Linear<'a, Renderer> -where - Renderer: iced_core::Renderer, - Renderer::Theme: StyleSheet, -{ - fn default() -> Self { - Self::new() - } -} - -#[derive(Clone, Copy)] -enum State { - Expanding { start: Instant, progress: f32 }, - Contracting { start: Instant, progress: f32 }, -} - -impl Default for State { - fn default() -> Self { - Self::Expanding { - start: Instant::now(), - progress: 0.0, - } - } -} - -impl State { - fn next(&self, now: Instant) -> Self { - match self { - Self::Expanding { .. } => Self::Contracting { - start: now, - progress: 0.0, - }, - Self::Contracting { .. } => Self::Expanding { - start: now, - progress: 0.0, - }, - } - } - - fn start(&self) -> Instant { - match self { - Self::Expanding { start, .. } | Self::Contracting { start, .. } => { - *start - } - } - } - - fn timed_transition(&self, cycle_duration: Duration, now: Instant) -> Self { - let elapsed = now.duration_since(self.start()); - - match elapsed { - elapsed if elapsed > cycle_duration => self.next(now), - _ => self.with_elapsed(cycle_duration, elapsed), - } - } - - fn with_elapsed( - &self, - cycle_duration: Duration, - elapsed: Duration, - ) -> Self { - let progress = elapsed.as_secs_f32() / cycle_duration.as_secs_f32(); - match self { - Self::Expanding { start, .. } => Self::Expanding { - start: *start, - progress, - }, - Self::Contracting { start, .. } => Self::Contracting { - start: *start, - progress, - }, - } - } -} - -impl<'a, Message, Renderer> Widget for Linear<'a, Renderer> -where - Message: 'a + Clone, - Renderer: 'a + iced_core::Renderer, - Renderer::Theme: StyleSheet, -{ - fn tag(&self) -> tree::Tag { - tree::Tag::of::() - } - - fn state(&self) -> tree::State { - tree::State::new(State::default()) - } - - fn width(&self) -> Length { - self.width - } - - fn height(&self) -> Length { - self.height - } - - fn layout( - &self, - _renderer: &Renderer, - limits: &layout::Limits, - ) -> layout::Node { - let limits = limits.width(self.width).height(self.height); - let size = limits.resolve(Size::ZERO); - - layout::Node::new(size) - } - - fn on_event( - &mut self, - tree: &mut Tree, - event: Event, - _layout: Layout<'_>, - _cursor_position: Point, - _renderer: &Renderer, - _clipboard: &mut dyn Clipboard, - shell: &mut Shell<'_, Message>, - ) -> event::Status { - let state = tree.state.downcast_mut::(); - - if let Event::Window(window::Event::RedrawRequested(now)) = event { - *state = state.timed_transition(self.cycle_duration, now); - - shell.request_redraw(RedrawRequest::NextFrame); - } - - event::Status::Ignored - } - - fn draw( - &self, - tree: &Tree, - renderer: &mut Renderer, - theme: &Renderer::Theme, - _style: &renderer::Style, - layout: Layout<'_>, - _cursor_position: Point, - _viewport: &Rectangle, - ) { - let bounds = layout.bounds(); - let custom_style = theme.appearance(&self.style); - let state = tree.state.downcast_ref::(); - - renderer.fill_quad( - renderer::Quad { - bounds: Rectangle { - x: bounds.x, - y: bounds.y, - width: bounds.width, - height: bounds.height, - }, - border_radius: 0.0.into(), - border_width: 0.0, - border_color: Color::TRANSPARENT, - }, - Background::Color(custom_style.track_color), - ); - - match state { - State::Expanding { progress, .. } => renderer.fill_quad( - renderer::Quad { - bounds: Rectangle { - x: bounds.x, - y: bounds.y, - width: self.easing.y_at_x(*progress) * bounds.width, - height: bounds.height, - }, - border_radius: 0.0.into(), - border_width: 0.0, - border_color: Color::TRANSPARENT, - }, - Background::Color(custom_style.bar_color), - ), - - State::Contracting { progress, .. } => renderer.fill_quad( - renderer::Quad { - bounds: Rectangle { - x: bounds.x - + self.easing.y_at_x(*progress) * bounds.width, - y: bounds.y, - width: (1.0 - self.easing.y_at_x(*progress)) - * bounds.width, - height: bounds.height, - }, - border_radius: 0.0.into(), - border_width: 0.0, - border_color: Color::TRANSPARENT, - }, - Background::Color(custom_style.bar_color), - ), - } - } -} - -impl<'a, Message, Renderer> From> - for Element<'a, Message, Renderer> -where - Message: Clone + 'a, - Renderer: iced_core::Renderer + 'a, - Renderer::Theme: StyleSheet, -{ - fn from(linear: Linear<'a, Renderer>) -> Self { - Self::new(linear) - } -} - -#[derive(Debug, Clone, Copy)] -pub struct Appearance { - /// 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 { - 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 { - track_color: palette.background.weak.color, - bar_color: palette.primary.base.color, - } - } -} diff --git a/examples/progress_indicators/src/main.rs b/examples/progress_indicators/src/main.rs deleted file mode 100644 index 136b8d8c..00000000 --- a/examples/progress_indicators/src/main.rs +++ /dev/null @@ -1,104 +0,0 @@ -use iced::executor; -use iced::widget::{column, container, row, slider, text}; -use iced::{Application, Command, Element, Length, Settings, Theme}; - -use std::time::Duration; - -mod circular; -mod easing; -mod linear; - -use circular::Circular; -use linear::Linear; - -pub fn main() -> iced::Result { - ProgressIndicators::run(Settings { - antialiasing: true, - ..Default::default() - }) -} - -struct ProgressIndicators { - cycle_duration: f32, -} - -impl Default for ProgressIndicators { - fn default() -> Self { - Self { - cycle_duration: 2.0, - } - } -} - -#[derive(Debug, Clone, Copy)] -enum Message { - CycleDurationChanged(f32), -} - -impl Application for ProgressIndicators { - type Message = Message; - type Flags = (); - type Executor = executor::Default; - type Theme = Theme; - - fn new(_flags: Self::Flags) -> (Self, Command) { - (Self::default(), Command::none()) - } - - fn title(&self) -> String { - String::from("Progress Indicators - Iced") - } - - fn update(&mut self, message: Message) -> Command { - match message { - Message::CycleDurationChanged(duration) => { - self.cycle_duration = duration; - } - } - - Command::none() - } - - fn view(&self) -> Element { - let column = easing::EXAMPLES - .iter() - .zip(["Decelerating:", "Accelerating:", "Standard:"]) - .fold(column![], |column, (easing, label)| { - column.push( - row![ - text(label).width(150), - Linear::new().easing(easing).cycle_duration( - Duration::from_secs_f32(self.cycle_duration) - ), - Circular::new().easing(easing).cycle_duration( - Duration::from_secs_f32(self.cycle_duration) - ) - ] - .align_items(iced::Alignment::Center) - .spacing(20.0), - ) - }) - .spacing(20); - - container( - column.push( - row(vec![ - text("Cycle duration:").into(), - slider(1.0..=1000.0, self.cycle_duration * 100.0, |x| { - Message::CycleDurationChanged(x / 100.0) - }) - .width(150.0) - .into(), - text(format!("{:.2}s", self.cycle_duration)).into(), - ]) - .align_items(iced::Alignment::Center) - .spacing(20.0), - ), - ) - .width(Length::Fill) - .height(Length::Fill) - .center_x() - .center_y() - .into() - } -} -- cgit