Implement reactive-rendering for scrollable
This commit is contained in:
parent
908af3fed7
commit
7fbc195b11
1 changed files with 362 additions and 325 deletions
|
|
@ -81,6 +81,7 @@ pub struct Scrollable<
|
|||
content: Element<'a, Message, Theme, Renderer>,
|
||||
on_scroll: Option<Box<dyn Fn(Viewport) -> Message + 'a>>,
|
||||
class: Theme::Class<'a>,
|
||||
last_status: Option<Status>,
|
||||
}
|
||||
|
||||
impl<'a, Message, Theme, Renderer> Scrollable<'a, Message, Theme, Renderer>
|
||||
|
|
@ -108,6 +109,7 @@ where
|
|||
content: content.into(),
|
||||
on_scroll: None,
|
||||
class: Theme::default(),
|
||||
last_status: None,
|
||||
}
|
||||
.validate()
|
||||
}
|
||||
|
|
@ -531,6 +533,8 @@ where
|
|||
let (mouse_over_y_scrollbar, mouse_over_x_scrollbar) =
|
||||
scrollbars.is_mouse_over(cursor);
|
||||
|
||||
let last_offsets = (state.offset_x, state.offset_y);
|
||||
|
||||
if let Some(last_scrolled) = state.last_scrolled {
|
||||
let clear_transaction = match event {
|
||||
Event::Mouse(
|
||||
|
|
@ -549,12 +553,14 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
let mut update = || {
|
||||
if let Some(scroller_grabbed_at) = state.y_scroller_grabbed_at {
|
||||
match event {
|
||||
Event::Mouse(mouse::Event::CursorMoved { .. })
|
||||
| Event::Touch(touch::Event::FingerMoved { .. }) => {
|
||||
if let Some(scrollbar) = scrollbars.y {
|
||||
let Some(cursor_position) = cursor.position() else {
|
||||
let Some(cursor_position) = cursor.position()
|
||||
else {
|
||||
return event::Status::Ignored;
|
||||
};
|
||||
|
||||
|
|
@ -603,7 +609,8 @@ where
|
|||
content_bounds,
|
||||
);
|
||||
|
||||
state.y_scroller_grabbed_at = Some(scroller_grabbed_at);
|
||||
state.y_scroller_grabbed_at =
|
||||
Some(scroller_grabbed_at);
|
||||
|
||||
let _ = notify_scroll(
|
||||
state,
|
||||
|
|
@ -674,7 +681,8 @@ where
|
|||
content_bounds,
|
||||
);
|
||||
|
||||
state.x_scroller_grabbed_at = Some(scroller_grabbed_at);
|
||||
state.x_scroller_grabbed_at =
|
||||
Some(scroller_grabbed_at);
|
||||
|
||||
let _ = notify_scroll(
|
||||
state,
|
||||
|
|
@ -692,13 +700,16 @@ where
|
|||
}
|
||||
|
||||
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
|
||||
} else {
|
||||
let cursor = match cursor_over_scrollable {
|
||||
Some(cursor_position)
|
||||
if !(mouse_over_x_scrollbar || mouse_over_y_scrollbar) =>
|
||||
if !(mouse_over_x_scrollbar
|
||||
|| mouse_over_y_scrollbar) =>
|
||||
{
|
||||
mouse::Cursor::Available(
|
||||
cursor_position
|
||||
|
|
@ -750,8 +761,9 @@ where
|
|||
return event::Status::Captured;
|
||||
}
|
||||
|
||||
if let Event::Keyboard(keyboard::Event::ModifiersChanged(modifiers)) =
|
||||
event
|
||||
if let Event::Keyboard(keyboard::Event::ModifiersChanged(
|
||||
modifiers,
|
||||
)) = event
|
||||
{
|
||||
state.keyboard_modifiers = modifiers;
|
||||
|
||||
|
|
@ -766,11 +778,13 @@ where
|
|||
|
||||
let delta = match delta {
|
||||
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
|
||||
let (x, y) =
|
||||
if cfg!(target_os = "macos") && is_shift_pressed {
|
||||
let (x, y) = if cfg!(target_os = "macos")
|
||||
&& is_shift_pressed
|
||||
{
|
||||
(y, x)
|
||||
} else {
|
||||
(x, y)
|
||||
|
|
@ -791,7 +805,9 @@ where
|
|||
// TODO: Configurable speed/friction (?)
|
||||
-movement * 60.0
|
||||
}
|
||||
mouse::ScrollDelta::Pixels { x, y } => -Vector::new(x, y),
|
||||
mouse::ScrollDelta::Pixels { x, y } => {
|
||||
-Vector::new(x, y)
|
||||
}
|
||||
};
|
||||
|
||||
state.scroll(
|
||||
|
|
@ -818,15 +834,18 @@ where
|
|||
}
|
||||
Event::Touch(event)
|
||||
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 {
|
||||
touch::Event::FingerPressed { .. } => {
|
||||
let Some(cursor_position) = cursor.position() else {
|
||||
let Some(cursor_position) = cursor.position()
|
||||
else {
|
||||
return event::Status::Ignored;
|
||||
};
|
||||
|
||||
state.scroll_area_touched_at = Some(cursor_position);
|
||||
state.scroll_area_touched_at =
|
||||
Some(cursor_position);
|
||||
}
|
||||
touch::Event::FingerMoved { .. } => {
|
||||
if let Some(scroll_box_touched_at) =
|
||||
|
|
@ -879,6 +898,43 @@ where
|
|||
}
|
||||
_ => 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(
|
||||
|
|
@ -920,27 +976,8 @@ where
|
|||
_ => mouse::Cursor::Unavailable,
|
||||
};
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
let style = theme.style(&self.class, status);
|
||||
let style = theme
|
||||
.style(&self.class, self.last_status.unwrap_or(Status::Active));
|
||||
|
||||
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 {
|
||||
Absolute(f32),
|
||||
Relative(f32),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue