diff options
author | 2023-05-02 06:40:48 +0200 | |
---|---|---|
committer | 2023-05-02 06:40:48 +0200 | |
commit | 8e8808f0e187ed6671441f5016f07bfcba426452 (patch) | |
tree | bc4d2eeb57f60e654a4a8ed7c562f79ee507abab /widget | |
parent | 2d7d9a130ece3fb6fa4cd52f9b32b4abd7887cf5 (diff) | |
parent | c8952ee4a1118fe67bfdf40fc77f3ff30f5d0278 (diff) | |
download | iced-8e8808f0e187ed6671441f5016f07bfcba426452.tar.gz iced-8e8808f0e187ed6671441f5016f07bfcba426452.tar.bz2 iced-8e8808f0e187ed6671441f5016f07bfcba426452.zip |
Merge branch 'master' into advanced-text
Diffstat (limited to 'widget')
-rw-r--r-- | widget/src/lazy/responsive.rs | 54 | ||||
-rw-r--r-- | widget/src/scrollable.rs | 107 | ||||
-rw-r--r-- | widget/src/slider.rs | 4 | ||||
-rw-r--r-- | widget/src/text_input.rs | 7 | ||||
-rw-r--r-- | widget/src/vertical_slider.rs | 4 |
5 files changed, 122 insertions, 54 deletions
diff --git a/widget/src/lazy/responsive.rs b/widget/src/lazy/responsive.rs index 7b2fc37c..b41d978b 100644 --- a/widget/src/lazy/responsive.rs +++ b/widget/src/lazy/responsive.rs @@ -42,7 +42,7 @@ where view: Box::new(view), content: RefCell::new(Content { size: Size::ZERO, - layout: layout::Node::new(Size::ZERO), + layout: None, element: Element::new(horizontal_space(0)), }), } @@ -51,7 +51,7 @@ where struct Content<'a, Message, Renderer> { size: Size, - layout: layout::Node, + layout: Option<layout::Node>, element: Element<'a, Message, Renderer>, } @@ -59,10 +59,19 @@ impl<'a, Message, Renderer> Content<'a, Message, Renderer> where Renderer: core::Renderer, { + fn layout(&mut self, renderer: &Renderer) { + if self.layout.is_none() { + self.layout = + Some(self.element.as_widget().layout( + renderer, + &layout::Limits::new(Size::ZERO, self.size), + )); + } + } + fn update( &mut self, tree: &mut Tree, - renderer: &Renderer, new_size: Size, view: &dyn Fn(Size) -> Element<'a, Message, Renderer>, ) { @@ -74,11 +83,6 @@ where self.size = new_size; tree.diff(&self.element); - - self.layout = self - .element - .as_widget() - .layout(renderer, &layout::Limits::new(Size::ZERO, self.size)); } fn resolve<R, T>( @@ -97,11 +101,12 @@ where where R: Deref<Target = Renderer>, { - self.update(tree, renderer.deref(), layout.bounds().size(), view); + self.update(tree, layout.bounds().size(), view); + self.layout(renderer.deref()); let content_layout = Layout::with_offset( layout.position() - Point::ORIGIN, - &self.layout, + self.layout.as_ref().unwrap(), ); f(tree, renderer, content_layout, &mut self.element) @@ -179,7 +184,10 @@ where let state = tree.state.downcast_mut::<State>(); let mut content = self.content.borrow_mut(); - content.resolve( + let mut local_messages = vec![]; + let mut local_shell = Shell::new(&mut local_messages); + + let status = content.resolve( &mut state.tree.borrow_mut(), renderer, layout, @@ -192,10 +200,18 @@ where cursor_position, renderer, clipboard, - shell, + &mut local_shell, ) }, - ) + ); + + if local_shell.is_layout_invalid() { + content.layout = None; + } + + shell.merge(local_shell, std::convert::identity); + + status } fn draw( @@ -274,22 +290,18 @@ where types: PhantomData, overlay_builder: |content: &mut RefMut<'_, Content<'_, _, _>>, tree| { - content.update( - tree, - renderer, - layout.bounds().size(), - &self.view, - ); + content.update(tree, layout.bounds().size(), &self.view); + content.layout(renderer); let Content { element, - layout: content_layout, + layout: content_layout_node, .. } = content.deref_mut(); let content_layout = Layout::with_offset( layout.bounds().position() - Point::ORIGIN, - content_layout, + content_layout_node.as_ref().unwrap(), ); element diff --git a/widget/src/scrollable.rs b/widget/src/scrollable.rs index 161ae664..fd51a6a8 100644 --- a/widget/src/scrollable.rs +++ b/widget/src/scrollable.rs @@ -16,7 +16,7 @@ use crate::core::{ use crate::runtime::Command; pub use crate::style::scrollable::{Scrollbar, Scroller, StyleSheet}; -pub use operation::scrollable::RelativeOffset; +pub use operation::scrollable::{AbsoluteOffset, RelativeOffset}; /// A widget that can vertically display an infinite amount of content with a /// scrollbar. @@ -32,7 +32,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, } @@ -87,12 +87,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 } @@ -390,6 +386,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, @@ -430,7 +435,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<'_>, @@ -890,7 +895,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>, @@ -902,31 +907,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); } } @@ -939,7 +949,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 { @@ -960,6 +970,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)] @@ -969,18 +983,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 { @@ -1046,6 +1093,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/widget/src/slider.rs b/widget/src/slider.rs index 5a884e21..18a49665 100644 --- a/widget/src/slider.rs +++ b/widget/src/slider.rs @@ -389,7 +389,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) }; @@ -415,7 +415,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/widget/src/text_input.rs b/widget/src/text_input.rs index 32d0b1f8..364ec3cd 100644 --- a/widget/src/text_input.rs +++ b/widget/src/text_input.rs @@ -973,9 +973,12 @@ pub fn draw<Renderer>( size: icon.size.unwrap_or_else(|| renderer.default_size()), font: icon.font, 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, shaping: text::Shaping::Advanced, }); } diff --git a/widget/src/vertical_slider.rs b/widget/src/vertical_slider.rs index a7551aef..2635611d 100644 --- a/widget/src/vertical_slider.rs +++ b/widget/src/vertical_slider.rs @@ -387,7 +387,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) }; @@ -414,7 +414,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, |