diff options
| author | 2022-10-07 11:41:50 -0700 | |
|---|---|---|
| committer | 2022-10-07 12:01:07 -0700 | |
| commit | 12a87c54eb68b992676060c80e518ffb29445cfc (patch) | |
| tree | b3fba16f2dd95a084cdbc3850f59a049c103c355 /graphics/src/gradient | |
| parent | f9a6efcaa03728f43aaa105af8936c1ed4778388 (diff) | |
| download | iced-12a87c54eb68b992676060c80e518ffb29445cfc.tar.gz iced-12a87c54eb68b992676060c80e518ffb29445cfc.tar.bz2 iced-12a87c54eb68b992676060c80e518ffb29445cfc.zip  | |
Added support for relative positioning of gradient fills. Addressed some PR feedback.
Diffstat (limited to 'graphics/src/gradient')
| -rw-r--r-- | graphics/src/gradient/linear.rs | 148 | 
1 files changed, 127 insertions, 21 deletions
diff --git a/graphics/src/gradient/linear.rs b/graphics/src/gradient/linear.rs index 6bf69b43..a9cfd55d 100644 --- a/graphics/src/gradient/linear.rs +++ b/graphics/src/gradient/linear.rs @@ -1,7 +1,6 @@  //! Linear gradient builder & definition. -  use crate::gradient::{ColorStop, Gradient}; -use crate::{Color, Point}; +use crate::{Color, Point, Size};  /// A linear gradient that can be used in the style of [`super::Fill`] or [`super::Stroke`].  #[derive(Debug, Clone, PartialEq)] @@ -14,18 +13,115 @@ pub struct Linear {      pub color_stops: Vec<ColorStop>,  } +#[derive(Debug)] +/// The position of the gradient within its bounds. +pub enum Position { +    /// The gradient will be positioned with respect to two points. +    Absolute { +        /// The starting point of the gradient. +        start: Point, +        /// The ending point of the gradient. +        end: Point, +    }, +    /// The gradient will be positioned relative to the provided bounds. +    Relative { +        /// The top left position of the bounds. +        top_left: Point, +        /// The width & height of the bounds. +        size: Size, +        /// The start [Location] of the gradient. +        start: Location, +        /// The end [Location] of the gradient. +        end: Location, +    }, +} + +impl Into<Position> for (Point, Point) { +    fn into(self) -> Position { +        Position::Absolute { +            start: self.0, +            end: self.1, +        } +    } +} + +#[derive(Debug)] +/// The location of a relatively-positioned gradient. +pub enum Location { +    /// Top left. +    TopLeft, +    /// Top. +    Top, +    /// Top right. +    TopRight, +    /// Right. +    Right, +    /// Bottom right. +    BottomRight, +    /// Bottom. +    Bottom, +    /// Bottom left. +    BottomLeft, +    /// Left. +    Left, +} + +impl Location { +    fn to_absolute(&self, top_left: Point, size: Size) -> Point { +        match self { +            Location::TopLeft => top_left, +            Location::Top => { +                Point::new(top_left.x + size.width / 2.0, top_left.y) +            } +            Location::TopRight => { +                Point::new(top_left.x + size.width, top_left.y) +            } +            Location::Right => Point::new( +                top_left.x + size.width, +                top_left.y + size.height / 2.0, +            ), +            Location::BottomRight => { +                Point::new(top_left.x + size.width, top_left.y + size.height) +            } +            Location::Bottom => Point::new( +                top_left.x + size.width / 2.0, +                top_left.y + size.height, +            ), +            Location::BottomLeft => { +                Point::new(top_left.x, top_left.y + size.height) +            } +            Location::Left => { +                Point::new(top_left.x, top_left.y + size.height / 2.0) +            } +        } +    } +} +  /// A [`Linear`] builder.  #[derive(Debug)]  pub struct Builder {      start: Point,      end: Point, -    stops: Vec<(f32, Color)>, +    stops: Vec<ColorStop>,      valid: bool,  }  impl Builder {      /// Creates a new [`Builder`]. -    pub fn new(start: Point, end: Point) -> Self { +    pub fn new(position: Position) -> Self { +        let (start, end) = match position { +            Position::Absolute { start, end } => (start, end), +            Position::Relative { +                top_left, +                size, +                start, +                end, +            } => ( +                start.to_absolute(top_left, size), +                end.to_absolute(top_left, size), +            ), +        }; +          Self {              start,              end, @@ -36,37 +132,47 @@ impl Builder {      /// Adds a new stop, defined by an offset and a color, to the gradient.      /// -    /// `offset` must be between `0.0` and `1.0`. +    /// `offset` must be between `0.0` and `1.0` or the gradient cannot be built. +    /// +    /// Note: when using the [Glow] backend, any color stop added after the 16th +    /// will not be displayed. +    /// +    /// On [backend::Wgpu] backend this limitation does not exist (technical limit is 524,288 stops).      pub fn add_stop(mut self, offset: f32, color: Color) -> Self { -        if !(0.0..=1.0).contains(&offset) { +        if offset.is_finite() && (0.0..=1.0).contains(&offset) { +            match self.stops.binary_search_by(|stop| { +                stop.offset.partial_cmp(&offset).unwrap() +            }) { +                Ok(_) => { +                    //the offset already exists in the gradient +                    self.valid = false; +                } +                Err(index) => { +                    self.stops.insert(index, ColorStop { offset, color }) +                } +            } +        } else {              self.valid = false;          } - -        //TODO: can sort on insert here -        self.stops.push((offset, color));          self      }      /// Builds the linear [`Gradient`] of this [`Builder`].      /// -    /// Returns `None` if no stops were added to the builder or +    /// Returns `Err` if no stops were added to the builder or      /// if stops not between 0.0 and 1.0 were added. -    pub fn build(self) -> Option<Gradient> { +    pub fn build(self) -> Result<Gradient, &'static str> {          if self.stops.is_empty() || !self.valid { -            return None; +            return Err("Valid gradient conditions: \ +            1) Must contain at least one color stop. \ +             2) Every color stop offset must be unique. \ +             3) Every color stop must be within the range of 0.0..=1.0");          } -        let mut stops: Vec<ColorStop> = self.stops.clone().into_iter().map(|f| ColorStop { -            offset: f.0, -            color: f.1 -        }).collect(); - -        stops.sort_by(|a, b| a.offset.partial_cmp(&b.offset).unwrap()); - -        Some(Gradient::Linear(Linear { +        Ok(Gradient::Linear(Linear {              start: self.start,              end: self.end, -            color_stops: stops +            color_stops: self.stops,          }))      }  }  | 
