diff options
Diffstat (limited to 'native')
| -rw-r--r-- | native/Cargo.toml | 2 | ||||
| -rw-r--r-- | native/src/widget/operation/scrollable.rs | 41 | ||||
| -rw-r--r-- | native/src/widget/scrollable.rs | 107 | ||||
| -rw-r--r-- | native/src/widget/slider.rs | 4 | ||||
| -rw-r--r-- | native/src/widget/text_input.rs | 7 | ||||
| -rw-r--r-- | native/src/widget/vertical_slider.rs | 4 | 
6 files changed, 130 insertions, 35 deletions
| diff --git a/native/Cargo.toml b/native/Cargo.toml index 869c1bb0..5ba06b67 100644 --- a/native/Cargo.toml +++ b/native/Cargo.toml @@ -1,6 +1,6 @@  [package]  name = "iced_native" -version = "0.10.1" +version = "0.10.3"  authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]  edition = "2021"  description = "A renderer-agnostic library for native GUIs" diff --git a/native/src/widget/operation/scrollable.rs b/native/src/widget/operation/scrollable.rs index 3b20631f..f947344d 100644 --- a/native/src/widget/operation/scrollable.rs +++ b/native/src/widget/operation/scrollable.rs @@ -5,6 +5,9 @@ use crate::widget::{Id, Operation};  pub trait Scrollable {      /// Snaps the scroll of the widget to the given `percentage` along the horizontal & vertical axis.      fn snap_to(&mut self, offset: RelativeOffset); + +    /// Scroll the widget to the given [`AbsoluteOffset`] along the horizontal & vertical axis. +    fn scroll_to(&mut self, offset: AbsoluteOffset);  }  /// Produces an [`Operation`] that snaps the widget with the given [`Id`] to @@ -34,7 +37,43 @@ pub fn snap_to<T>(target: Id, offset: RelativeOffset) -> impl Operation<T> {      SnapTo { target, offset }  } -/// The amount of offset in each direction of a [`Scrollable`]. +/// Produces an [`Operation`] that scrolls the widget with the given [`Id`] to +/// the provided [`AbsoluteOffset`]. +pub fn scroll_to<T>(target: Id, offset: AbsoluteOffset) -> impl Operation<T> { +    struct ScrollTo { +        target: Id, +        offset: AbsoluteOffset, +    } + +    impl<T> Operation<T> for ScrollTo { +        fn container( +            &mut self, +            _id: Option<&Id>, +            operate_on_children: &mut dyn FnMut(&mut dyn Operation<T>), +        ) { +            operate_on_children(self) +        } + +        fn scrollable(&mut self, state: &mut dyn Scrollable, id: Option<&Id>) { +            if Some(&self.target) == id { +                state.scroll_to(self.offset); +            } +        } +    } + +    ScrollTo { target, offset } +} + +/// The amount of absolute offset in each direction of a [`Scrollable`]. +#[derive(Debug, Clone, Copy, PartialEq, Default)] +pub struct AbsoluteOffset { +    /// The amount of horizontal offset +    pub x: f32, +    /// The amount of vertical offset +    pub y: f32, +} + +/// The amount of relative offset in each direction of a [`Scrollable`].  ///  /// A value of `0.0` means start, while `1.0` means end.  #[derive(Debug, Clone, Copy, PartialEq, Default)] diff --git a/native/src/widget/scrollable.rs b/native/src/widget/scrollable.rs index 78dcdca2..e35a4f96 100644 --- a/native/src/widget/scrollable.rs +++ b/native/src/widget/scrollable.rs @@ -15,7 +15,7 @@ use crate::{  };  pub use iced_style::scrollable::StyleSheet; -pub use operation::scrollable::RelativeOffset; +pub use operation::scrollable::{AbsoluteOffset, RelativeOffset};  pub mod style {      //! The styles of a [`Scrollable`]. @@ -38,7 +38,7 @@ where      vertical: Properties,      horizontal: Option<Properties>,      content: Element<'a, Message, Renderer>, -    on_scroll: Option<Box<dyn Fn(RelativeOffset) -> Message + 'a>>, +    on_scroll: Option<Box<dyn Fn(Viewport) -> Message + 'a>>,      style: <Renderer::Theme as StyleSheet>::Style,  } @@ -93,12 +93,8 @@ where      /// Sets a function to call when the [`Scrollable`] is scrolled.      /// -    /// The function takes the new relative x & y offset of the [`Scrollable`] -    /// (e.g. `0` means beginning, while `1` means end). -    pub fn on_scroll( -        mut self, -        f: impl Fn(RelativeOffset) -> Message + 'a, -    ) -> Self { +    /// The function takes the [`Viewport`] of the [`Scrollable`] +    pub fn on_scroll(mut self, f: impl Fn(Viewport) -> Message + 'a) -> Self {          self.on_scroll = Some(Box::new(f));          self      } @@ -396,6 +392,15 @@ pub fn snap_to<Message: 'static>(      Command::widget(operation::scrollable::snap_to(id.0, offset))  } +/// Produces a [`Command`] that scrolls the [`Scrollable`] with the given [`Id`] +/// to the provided [`AbsoluteOffset`] along the x & y axis. +pub fn scroll_to<Message: 'static>( +    id: Id, +    offset: AbsoluteOffset, +) -> Command<Message> { +    Command::widget(operation::scrollable::scroll_to(id.0, offset)) +} +  /// Computes the layout of a [`Scrollable`].  pub fn layout<Renderer>(      renderer: &Renderer, @@ -436,7 +441,7 @@ pub fn update<Message>(      shell: &mut Shell<'_, Message>,      vertical: &Properties,      horizontal: Option<&Properties>, -    on_scroll: &Option<Box<dyn Fn(RelativeOffset) -> Message + '_>>, +    on_scroll: &Option<Box<dyn Fn(Viewport) -> Message + '_>>,      update_content: impl FnOnce(          Event,          Layout<'_>, @@ -896,7 +901,7 @@ pub fn draw<Renderer>(  fn notify_on_scroll<Message>(      state: &mut State, -    on_scroll: &Option<Box<dyn Fn(RelativeOffset) -> Message + '_>>, +    on_scroll: &Option<Box<dyn Fn(Viewport) -> Message + '_>>,      bounds: Rectangle,      content_bounds: Rectangle,      shell: &mut Shell<'_, Message>, @@ -908,31 +913,36 @@ fn notify_on_scroll<Message>(              return;          } -        let x = state.offset_x.absolute(bounds.width, content_bounds.width) -            / (content_bounds.width - bounds.width); +        let viewport = Viewport { +            offset_x: state.offset_x, +            offset_y: state.offset_y, +            bounds, +            content_bounds, +        }; -        let y = state -            .offset_y -            .absolute(bounds.height, content_bounds.height) -            / (content_bounds.height - bounds.height); +        // Don't publish redundant viewports to shell +        if let Some(last_notified) = state.last_notified { +            let last_relative_offset = last_notified.relative_offset(); +            let current_relative_offset = viewport.relative_offset(); -        let new_offset = RelativeOffset { x, y }; +            let last_absolute_offset = last_notified.absolute_offset(); +            let current_absolute_offset = viewport.absolute_offset(); -        // Don't publish redundant offsets to shell -        if let Some(prev_offset) = state.last_notified {              let unchanged = |a: f32, b: f32| {                  (a - b).abs() <= f32::EPSILON || (a.is_nan() && b.is_nan())              }; -            if unchanged(prev_offset.x, new_offset.x) -                && unchanged(prev_offset.y, new_offset.y) +            if unchanged(last_relative_offset.x, current_relative_offset.x) +                && unchanged(last_relative_offset.y, current_relative_offset.y) +                && unchanged(last_absolute_offset.x, current_absolute_offset.x) +                && unchanged(last_absolute_offset.y, current_absolute_offset.y)              {                  return;              }          } -        shell.publish(on_scroll(new_offset)); -        state.last_notified = Some(new_offset); +        shell.publish(on_scroll(viewport)); +        state.last_notified = Some(viewport);      }  } @@ -945,7 +955,7 @@ pub struct State {      offset_x: Offset,      x_scroller_grabbed_at: Option<f32>,      keyboard_modifiers: keyboard::Modifiers, -    last_notified: Option<RelativeOffset>, +    last_notified: Option<Viewport>,  }  impl Default for State { @@ -966,6 +976,10 @@ impl operation::Scrollable for State {      fn snap_to(&mut self, offset: RelativeOffset) {          State::snap_to(self, offset);      } + +    fn scroll_to(&mut self, offset: AbsoluteOffset) { +        State::scroll_to(self, offset) +    }  }  #[derive(Debug, Clone, Copy)] @@ -975,18 +989,51 @@ enum Offset {  }  impl Offset { -    fn absolute(self, window: f32, content: f32) -> f32 { +    fn absolute(self, viewport: f32, content: f32) -> f32 {          match self {              Offset::Absolute(absolute) => { -                absolute.min((content - window).max(0.0)) +                absolute.min((content - viewport).max(0.0))              }              Offset::Relative(percentage) => { -                ((content - window) * percentage).max(0.0) +                ((content - viewport) * percentage).max(0.0)              }          }      }  } +/// The current [`Viewport`] of the [`Scrollable`]. +#[derive(Debug, Clone, Copy)] +pub struct Viewport { +    offset_x: Offset, +    offset_y: Offset, +    bounds: Rectangle, +    content_bounds: Rectangle, +} + +impl Viewport { +    /// Returns the [`AbsoluteOffset`] of the current [`Viewport`]. +    pub fn absolute_offset(&self) -> AbsoluteOffset { +        let x = self +            .offset_x +            .absolute(self.bounds.width, self.content_bounds.width); +        let y = self +            .offset_y +            .absolute(self.bounds.height, self.content_bounds.height); + +        AbsoluteOffset { x, y } +    } + +    /// Returns the [`RelativeOffset`] of the current [`Viewport`]. +    pub fn relative_offset(&self) -> RelativeOffset { +        let AbsoluteOffset { x, y } = self.absolute_offset(); + +        let x = x / (self.content_bounds.width - self.bounds.width); +        let y = y / (self.content_bounds.height - self.bounds.height); + +        RelativeOffset { x, y } +    } +} +  impl State {      /// Creates a new [`State`] with the scrollbar(s) at the beginning.      pub fn new() -> Self { @@ -1052,6 +1099,12 @@ impl State {          self.offset_y = Offset::Relative(offset.y.clamp(0.0, 1.0));      } +    /// Scroll to the provided [`AbsoluteOffset`]. +    pub fn scroll_to(&mut self, offset: AbsoluteOffset) { +        self.offset_x = Offset::Absolute(offset.x.max(0.0)); +        self.offset_y = Offset::Absolute(offset.y.max(0.0)); +    } +      /// Unsnaps the current scroll position, if snapped, given the bounds of the      /// [`Scrollable`] and its contents.      pub fn unsnap(&mut self, bounds: Rectangle, content_bounds: Rectangle) { diff --git a/native/src/widget/slider.rs b/native/src/widget/slider.rs index ba467834..cfb0a8fe 100644 --- a/native/src/widget/slider.rs +++ b/native/src/widget/slider.rs @@ -391,7 +391,7 @@ pub fn draw<T, R>(      let offset = if range_start >= range_end {          0.0      } else { -        (bounds.width - handle_width / 2.0) * (value - range_start) +        (bounds.width - handle_width) * (value - range_start)              / (range_end - range_start)      }; @@ -417,7 +417,7 @@ pub fn draw<T, R>(              bounds: Rectangle {                  x: bounds.x + offset + handle_width / 2.0,                  y: rail_y - style.rail.width / 2.0, -                width: bounds.width - offset, +                width: bounds.width - offset - handle_width / 2.0,                  height: style.rail.width,              },              border_radius: Default::default(), diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index 8627aa98..6113cf94 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -970,9 +970,12 @@ pub fn draw<Renderer>(              size: icon.size.unwrap_or_else(|| renderer.default_size()),              font: icon.font.clone(),              color: appearance.icon_color, -            bounds: icon_layout.bounds(), +            bounds: Rectangle { +                y: text_bounds.center_y(), +                ..icon_layout.bounds() +            },              horizontal_alignment: alignment::Horizontal::Left, -            vertical_alignment: alignment::Vertical::Top, +            vertical_alignment: alignment::Vertical::Center,          });      } diff --git a/native/src/widget/vertical_slider.rs b/native/src/widget/vertical_slider.rs index 5af69676..cb2de480 100644 --- a/native/src/widget/vertical_slider.rs +++ b/native/src/widget/vertical_slider.rs @@ -384,7 +384,7 @@ pub fn draw<T, R>(      let offset = if range_start >= range_end {          0.0      } else { -        (bounds.height - handle_width / 2.0) * (value - range_end) +        (bounds.height - handle_width) * (value - range_end)              / (range_start - range_end)      }; @@ -411,7 +411,7 @@ pub fn draw<T, R>(                  x: rail_x - style.rail.width / 2.0,                  y: bounds.y + offset + handle_width / 2.0,                  width: style.rail.width, -                height: bounds.height - offset, +                height: bounds.height - offset - handle_width / 2.0,              },              border_radius: Default::default(),              border_width: 0.0, | 
