Implement reactive-rendering for scrollable

This commit is contained in:
Héctor Ramón Jiménez 2024-10-23 21:07:45 +02:00
parent 908af3fed7
commit 7fbc195b11
No known key found for this signature in database
GPG key ID: 4C07CEC81AFA161F

View file

@ -81,6 +81,7 @@ pub struct Scrollable<
content: Element<'a, Message, Theme, Renderer>, content: Element<'a, Message, Theme, Renderer>,
on_scroll: Option<Box<dyn Fn(Viewport) -> Message + 'a>>, on_scroll: Option<Box<dyn Fn(Viewport) -> Message + 'a>>,
class: Theme::Class<'a>, class: Theme::Class<'a>,
last_status: Option<Status>,
} }
impl<'a, Message, Theme, Renderer> Scrollable<'a, Message, Theme, Renderer> impl<'a, Message, Theme, Renderer> Scrollable<'a, Message, Theme, Renderer>
@ -108,6 +109,7 @@ where
content: content.into(), content: content.into(),
on_scroll: None, on_scroll: None,
class: Theme::default(), class: Theme::default(),
last_status: None,
} }
.validate() .validate()
} }
@ -531,6 +533,8 @@ where
let (mouse_over_y_scrollbar, mouse_over_x_scrollbar) = let (mouse_over_y_scrollbar, mouse_over_x_scrollbar) =
scrollbars.is_mouse_over(cursor); scrollbars.is_mouse_over(cursor);
let last_offsets = (state.offset_x, state.offset_y);
if let Some(last_scrolled) = state.last_scrolled { if let Some(last_scrolled) = state.last_scrolled {
let clear_transaction = match event { let clear_transaction = match event {
Event::Mouse( Event::Mouse(
@ -549,12 +553,14 @@ where
} }
} }
let mut update = || {
if let Some(scroller_grabbed_at) = state.y_scroller_grabbed_at { if let Some(scroller_grabbed_at) = state.y_scroller_grabbed_at {
match event { match event {
Event::Mouse(mouse::Event::CursorMoved { .. }) Event::Mouse(mouse::Event::CursorMoved { .. })
| Event::Touch(touch::Event::FingerMoved { .. }) => { | Event::Touch(touch::Event::FingerMoved { .. }) => {
if let Some(scrollbar) = scrollbars.y { if let Some(scrollbar) = scrollbars.y {
let Some(cursor_position) = cursor.position() else { let Some(cursor_position) = cursor.position()
else {
return event::Status::Ignored; return event::Status::Ignored;
}; };
@ -603,7 +609,8 @@ where
content_bounds, content_bounds,
); );
state.y_scroller_grabbed_at = Some(scroller_grabbed_at); state.y_scroller_grabbed_at =
Some(scroller_grabbed_at);
let _ = notify_scroll( let _ = notify_scroll(
state, state,
@ -674,7 +681,8 @@ where
content_bounds, content_bounds,
); );
state.x_scroller_grabbed_at = Some(scroller_grabbed_at); state.x_scroller_grabbed_at =
Some(scroller_grabbed_at);
let _ = notify_scroll( let _ = notify_scroll(
state, state,
@ -692,13 +700,16 @@ where
} }
let content_status = if state.last_scrolled.is_some() let content_status = if state.last_scrolled.is_some()
&& matches!(event, Event::Mouse(mouse::Event::WheelScrolled { .. })) && matches!(
{ event,
Event::Mouse(mouse::Event::WheelScrolled { .. })
) {
event::Status::Ignored event::Status::Ignored
} else { } else {
let cursor = match cursor_over_scrollable { let cursor = match cursor_over_scrollable {
Some(cursor_position) Some(cursor_position)
if !(mouse_over_x_scrollbar || mouse_over_y_scrollbar) => if !(mouse_over_x_scrollbar
|| mouse_over_y_scrollbar) =>
{ {
mouse::Cursor::Available( mouse::Cursor::Available(
cursor_position cursor_position
@ -750,8 +761,9 @@ where
return event::Status::Captured; return event::Status::Captured;
} }
if let Event::Keyboard(keyboard::Event::ModifiersChanged(modifiers)) = if let Event::Keyboard(keyboard::Event::ModifiersChanged(
event modifiers,
)) = event
{ {
state.keyboard_modifiers = modifiers; state.keyboard_modifiers = modifiers;
@ -766,11 +778,13 @@ where
let delta = match delta { let delta = match delta {
mouse::ScrollDelta::Lines { x, y } => { mouse::ScrollDelta::Lines { x, y } => {
let is_shift_pressed = state.keyboard_modifiers.shift(); let is_shift_pressed =
state.keyboard_modifiers.shift();
// macOS automatically inverts the axes when Shift is pressed // macOS automatically inverts the axes when Shift is pressed
let (x, y) = let (x, y) = if cfg!(target_os = "macos")
if cfg!(target_os = "macos") && is_shift_pressed { && is_shift_pressed
{
(y, x) (y, x)
} else { } else {
(x, y) (x, y)
@ -791,7 +805,9 @@ where
// TODO: Configurable speed/friction (?) // TODO: Configurable speed/friction (?)
-movement * 60.0 -movement * 60.0
} }
mouse::ScrollDelta::Pixels { x, y } => -Vector::new(x, y), mouse::ScrollDelta::Pixels { x, y } => {
-Vector::new(x, y)
}
}; };
state.scroll( state.scroll(
@ -818,15 +834,18 @@ where
} }
Event::Touch(event) Event::Touch(event)
if state.scroll_area_touched_at.is_some() if state.scroll_area_touched_at.is_some()
|| !mouse_over_y_scrollbar && !mouse_over_x_scrollbar => || !mouse_over_y_scrollbar
&& !mouse_over_x_scrollbar =>
{ {
match event { match event {
touch::Event::FingerPressed { .. } => { touch::Event::FingerPressed { .. } => {
let Some(cursor_position) = cursor.position() else { let Some(cursor_position) = cursor.position()
else {
return event::Status::Ignored; return event::Status::Ignored;
}; };
state.scroll_area_touched_at = Some(cursor_position); state.scroll_area_touched_at =
Some(cursor_position);
} }
touch::Event::FingerMoved { .. } => { touch::Event::FingerMoved { .. } => {
if let Some(scroll_box_touched_at) = if let Some(scroll_box_touched_at) =
@ -879,6 +898,43 @@ where
} }
_ => event::Status::Ignored, _ => event::Status::Ignored,
} }
};
let event_status = update();
let status = if state.y_scroller_grabbed_at.is_some()
|| state.x_scroller_grabbed_at.is_some()
{
Status::Dragged {
is_horizontal_scrollbar_dragged: state
.x_scroller_grabbed_at
.is_some(),
is_vertical_scrollbar_dragged: state
.y_scroller_grabbed_at
.is_some(),
}
} else if cursor_over_scrollable.is_some() {
Status::Hovered {
is_horizontal_scrollbar_hovered: mouse_over_x_scrollbar,
is_vertical_scrollbar_hovered: mouse_over_y_scrollbar,
}
} else {
Status::Active
};
if let Event::Window(window::Event::RedrawRequested(_now)) = event {
self.last_status = Some(status);
}
if last_offsets != (state.offset_x, state.offset_y)
|| self
.last_status
.is_some_and(|last_status| last_status != status)
{
shell.request_redraw(window::RedrawRequest::NextFrame);
}
event_status
} }
fn draw( fn draw(
@ -920,27 +976,8 @@ where
_ => mouse::Cursor::Unavailable, _ => mouse::Cursor::Unavailable,
}; };
let status = if state.y_scroller_grabbed_at.is_some() let style = theme
|| state.x_scroller_grabbed_at.is_some() .style(&self.class, self.last_status.unwrap_or(Status::Active));
{
Status::Dragged {
is_horizontal_scrollbar_dragged: state
.x_scroller_grabbed_at
.is_some(),
is_vertical_scrollbar_dragged: state
.y_scroller_grabbed_at
.is_some(),
}
} else if cursor_over_scrollable.is_some() {
Status::Hovered {
is_horizontal_scrollbar_hovered: mouse_over_x_scrollbar,
is_vertical_scrollbar_hovered: mouse_over_y_scrollbar,
}
} else {
Status::Active
};
let style = theme.style(&self.class, status);
container::draw_background(renderer, &style.container, layout.bounds()); container::draw_background(renderer, &style.container, layout.bounds());
@ -1323,7 +1360,7 @@ impl operation::Scrollable for State {
} }
} }
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy, PartialEq)]
enum Offset { enum Offset {
Absolute(f32), Absolute(f32),
Relative(f32), Relative(f32),