Add scrollable Viewport
This commit is contained in:
parent
b623f280ed
commit
6ad5e03d71
4 changed files with 61 additions and 52 deletions
|
|
@ -36,7 +36,7 @@ enum Message {
|
|||
ScrollerWidthChanged(u16),
|
||||
ScrollToBeginning,
|
||||
ScrollToEnd,
|
||||
Scrolled(scrollable::CurrentOffset),
|
||||
Scrolled(scrollable::Viewport),
|
||||
}
|
||||
|
||||
impl Application for ScrollableDemo {
|
||||
|
|
@ -104,8 +104,8 @@ impl Application for ScrollableDemo {
|
|||
self.current_scroll_offset,
|
||||
)
|
||||
}
|
||||
Message::Scrolled(offset) => {
|
||||
self.current_scroll_offset = offset.relative;
|
||||
Message::Scrolled(viewport) => {
|
||||
self.current_scroll_offset = viewport.relative_offset();
|
||||
|
||||
Command::none()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,15 +64,6 @@ pub fn scroll_to<T>(target: Id, offset: AbsoluteOffset) -> impl Operation<T> {
|
|||
ScrollTo { target, offset }
|
||||
}
|
||||
|
||||
/// The current absolute & relative offset of a [`Scrollable`]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Default)]
|
||||
pub struct CurrentOffset {
|
||||
/// The [`AbsoluteOffset`] of a [`Scrollable`]
|
||||
pub absolute: AbsoluteOffset,
|
||||
/// The [`RelativeOffset`] of a [`Scrollable`]
|
||||
pub relative: RelativeOffset,
|
||||
}
|
||||
|
||||
/// The amount of absolute offset in each direction of a [`Scrollable`].
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Default)]
|
||||
pub struct AbsoluteOffset {
|
||||
|
|
|
|||
|
|
@ -15,9 +15,7 @@ use crate::{
|
|||
};
|
||||
|
||||
pub use iced_style::scrollable::StyleSheet;
|
||||
pub use operation::scrollable::{
|
||||
AbsoluteOffset, CurrentOffset, RelativeOffset,
|
||||
};
|
||||
pub use operation::scrollable::{AbsoluteOffset, RelativeOffset};
|
||||
|
||||
pub mod style {
|
||||
//! The styles of a [`Scrollable`].
|
||||
|
|
@ -40,7 +38,7 @@ where
|
|||
vertical: Properties,
|
||||
horizontal: Option<Properties>,
|
||||
content: Element<'a, Message, Renderer>,
|
||||
on_scroll: Option<Box<dyn Fn(CurrentOffset) -> Message + 'a>>,
|
||||
on_scroll: Option<Box<dyn Fn(Viewport) -> Message + 'a>>,
|
||||
style: <Renderer::Theme as StyleSheet>::Style,
|
||||
}
|
||||
|
||||
|
|
@ -95,11 +93,8 @@ where
|
|||
|
||||
/// Sets a function to call when the [`Scrollable`] is scrolled.
|
||||
///
|
||||
/// The function takes the [`CurrentOffset`] of the [`Scrollable`]
|
||||
pub fn on_scroll(
|
||||
mut self,
|
||||
f: impl Fn(CurrentOffset) -> 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
|
||||
}
|
||||
|
|
@ -437,7 +432,7 @@ pub fn update<Message>(
|
|||
shell: &mut Shell<'_, Message>,
|
||||
vertical: &Properties,
|
||||
horizontal: Option<&Properties>,
|
||||
on_scroll: &Option<Box<dyn Fn(CurrentOffset) -> Message + '_>>,
|
||||
on_scroll: &Option<Box<dyn Fn(Viewport) -> Message + '_>>,
|
||||
update_content: impl FnOnce(
|
||||
Event,
|
||||
Layout<'_>,
|
||||
|
|
@ -897,7 +892,7 @@ pub fn draw<Renderer>(
|
|||
|
||||
fn notify_on_scroll<Message>(
|
||||
state: &mut State,
|
||||
on_scroll: &Option<Box<dyn Fn(CurrentOffset) -> Message + '_>>,
|
||||
on_scroll: &Option<Box<dyn Fn(Viewport) -> Message + '_>>,
|
||||
bounds: Rectangle,
|
||||
content_bounds: Rectangle,
|
||||
shell: &mut Shell<'_, Message>,
|
||||
|
|
@ -909,39 +904,29 @@ fn notify_on_scroll<Message>(
|
|||
return;
|
||||
}
|
||||
|
||||
let absolute_x =
|
||||
state.offset_x.absolute(bounds.width, content_bounds.width);
|
||||
let relative_x = absolute_x / (content_bounds.width - bounds.width);
|
||||
|
||||
let absolute_y = state
|
||||
.offset_y
|
||||
.absolute(bounds.height, content_bounds.height);
|
||||
let relative_y = absolute_y / (content_bounds.height - bounds.height);
|
||||
|
||||
let absolute = AbsoluteOffset {
|
||||
x: absolute_x,
|
||||
y: absolute_y,
|
||||
};
|
||||
let relative = RelativeOffset {
|
||||
x: relative_x,
|
||||
y: relative_y,
|
||||
let viewport = Viewport {
|
||||
offset_x: state.offset_x,
|
||||
offset_y: state.offset_y,
|
||||
bounds,
|
||||
content_bounds,
|
||||
};
|
||||
|
||||
// Don't publish redundant offsets to shell
|
||||
if let Some(prev_relative) = state.last_notified {
|
||||
// Don't publish redundant viewports to shell
|
||||
if let Some(last_notified) = state.last_notified {
|
||||
let prev = last_notified.relative_offset();
|
||||
let curr = viewport.relative_offset();
|
||||
|
||||
let unchanged = |a: f32, b: f32| {
|
||||
(a - b).abs() <= f32::EPSILON || (a.is_nan() && b.is_nan())
|
||||
};
|
||||
|
||||
if unchanged(prev_relative.x, relative.x)
|
||||
&& unchanged(prev_relative.y, relative.y)
|
||||
{
|
||||
if unchanged(prev.x, curr.x) && unchanged(prev.y, curr.y) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
shell.publish(on_scroll(CurrentOffset { absolute, relative }));
|
||||
state.last_notified = Some(relative);
|
||||
shell.publish(on_scroll(viewport));
|
||||
state.last_notified = Some(viewport);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -954,7 +939,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 {
|
||||
|
|
@ -988,18 +973,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 {
|
||||
|
|
|
|||
|
|
@ -109,8 +109,8 @@ pub mod radio {
|
|||
pub mod scrollable {
|
||||
//! Navigate an endless amount of content with a scrollbar.
|
||||
pub use iced_native::widget::scrollable::{
|
||||
snap_to, style::Scrollbar, style::Scroller, AbsoluteOffset,
|
||||
CurrentOffset, Id, Properties, RelativeOffset, StyleSheet,
|
||||
snap_to, style::Scrollbar, style::Scroller, AbsoluteOffset, Id,
|
||||
Properties, RelativeOffset, StyleSheet, Viewport,
|
||||
};
|
||||
|
||||
/// A widget that can vertically display an infinite amount of content
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue