Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Ui::scroll_with_delta only scrolling if the ScrollArea is focused #4303

Merged
merged 7 commits into from
May 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 32 additions & 24 deletions crates/egui/src/containers/scroll_area.rs
Original file line number Diff line number Diff line change
Expand Up @@ -782,15 +782,22 @@ impl Prepared {

let content_size = content_ui.min_size();

let scroll_delta = content_ui
.ctx()
.frame_state_mut(|state| std::mem::take(&mut state.scroll_delta));

for d in 0..2 {
// FrameState::scroll_delta is inverted from the way we apply the delta, so we need to negate it.
let mut delta = -scroll_delta[d];

// We always take both scroll targets regardless of which scroll axes are enabled. This
// is to avoid them leaking to other scroll areas.
let scroll_target = content_ui
.ctx()
.frame_state_mut(|state| state.scroll_target[d].take());

if scroll_enabled[d] {
if let Some((target_range, align)) = scroll_target {
delta += if let Some((target_range, align)) = scroll_target {
let min = content_ui.min_rect().min[d];
let clip_rect = content_ui.clip_rect();
let visible_range = min..=min + clip_rect.size()[d];
Expand All @@ -799,7 +806,7 @@ impl Prepared {
let clip_end = clip_rect.max[d];
let mut spacing = ui.spacing().item_spacing[d];

let delta = if let Some(align) = align {
if let Some(align) = align {
let center_factor = align.to_factor();

let offset =
Expand All @@ -816,31 +823,32 @@ impl Prepared {
} else {
// Ui is already in view, no need to adjust scroll.
0.0
};
}
} else {
0.0
};

if delta != 0.0 {
let target_offset = state.offset[d] + delta;
if delta != 0.0 {
let target_offset = state.offset[d] + delta;

if !animated {
state.offset[d] = target_offset;
} else if let Some(animation) = &mut state.offset_target[d] {
// For instance: the user is continuously calling `ui.scroll_to_cursor`,
// so we don't want to reset the animation, but perhaps update the target:
animation.target_offset = target_offset;
} else {
// The further we scroll, the more time we take.
// TODO(emilk): let users configure this in `Style`.
let now = ui.input(|i| i.time);
let points_per_second = 1000.0;
let animation_duration =
(delta.abs() / points_per_second).clamp(0.1, 0.3);
state.offset_target[d] = Some(ScrollTarget {
animation_time_span: (now, now + animation_duration as f64),
target_offset,
});
}
ui.ctx().request_repaint();
if !animated {
state.offset[d] = target_offset;
} else if let Some(animation) = &mut state.offset_target[d] {
// For instance: the user is continuously calling `ui.scroll_to_cursor`,
// so we don't want to reset the animation, but perhaps update the target:
animation.target_offset = target_offset;
} else {
// The further we scroll, the more time we take.
// TODO(emilk): let users configure this in `Style`.
let now = ui.input(|i| i.time);
let points_per_second = 1000.0;
let animation_duration = (delta.abs() / points_per_second).clamp(0.1, 0.3);
state.offset_target[d] = Some(ScrollTarget {
animation_time_span: (now, now + animation_duration as f64),
target_offset,
});
}
ui.ctx().request_repaint();
}
}
}
Expand Down
16 changes: 15 additions & 1 deletion crates/egui/src/frame_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,20 @@ pub(crate) struct FrameState {
/// Initialized to `None` at the start of each frame.
pub(crate) tooltip_state: Option<TooltipFrameState>,

/// horizontal, vertical
/// The current scroll area should scroll to this range (horizontal, vertical).
pub(crate) scroll_target: [Option<(Rangef, Option<Align>)>; 2],

/// The current scroll area should scroll by this much.
///
/// The delta dictates how the _content_ should move.
///
/// A positive X-value indicates the content is being moved right,
/// as when swiping right on a touch-screen or track-pad with natural scrolling.
///
/// A positive Y-value indicates the content is being moved down,
/// as when swiping down on a touch-screen or track-pad with natural scrolling.
pub(crate) scroll_delta: Vec2,

#[cfg(feature = "accesskit")]
pub(crate) accesskit_state: Option<AccessKitFrameState>,

Expand All @@ -63,6 +74,7 @@ impl Default for FrameState {
used_by_panels: Rect::NAN,
tooltip_state: None,
scroll_target: [None, None],
scroll_delta: Vec2::default(),
#[cfg(feature = "accesskit")]
accesskit_state: None,
highlight_this_frame: Default::default(),
Expand All @@ -84,6 +96,7 @@ impl FrameState {
used_by_panels,
tooltip_state,
scroll_target,
scroll_delta,
#[cfg(feature = "accesskit")]
accesskit_state,
highlight_this_frame,
Expand All @@ -99,6 +112,7 @@ impl FrameState {
*used_by_panels = Rect::NOTHING;
*tooltip_state = None;
*scroll_target = [None, None];
*scroll_delta = Vec2::default();

#[cfg(debug_assertions)]
{
Expand Down
7 changes: 5 additions & 2 deletions crates/egui/src/ui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1073,6 +1073,8 @@ impl Ui {
/// A positive Y-value indicates the content is being moved down,
/// as when swiping down on a touch-screen or track-pad with natural scrolling.
///
/// If this is called multiple times per frame for the same [`ScrollArea`], the deltas will be summed.
///
/// /// See also: [`Response::scroll_to_me`], [`Ui::scroll_to_rect`], [`Ui::scroll_to_cursor`]
///
/// ```
Expand All @@ -1091,8 +1093,9 @@ impl Ui {
/// # });
/// ```
pub fn scroll_with_delta(&self, delta: Vec2) {
self.ctx()
.input_mut(|input| input.smooth_scroll_delta += delta);
self.ctx().frame_state_mut(|state| {
state.scroll_delta += delta;
});
}
}

Expand Down
21 changes: 21 additions & 0 deletions crates/egui_demo_lib/src/demo/scrolling.rs
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,7 @@ struct ScrollTo {
track_item: usize,
tack_item_align: Option<Align>,
offset: f32,
delta: f32,
}

impl Default for ScrollTo {
Expand All @@ -244,6 +245,7 @@ impl Default for ScrollTo {
track_item: 25,
tack_item_align: Some(Align::Center),
offset: 0.0,
delta: 64.0,
}
}
}
Expand All @@ -258,6 +260,7 @@ impl super::View for ScrollTo {
let mut go_to_scroll_offset = false;
let mut scroll_top = false;
let mut scroll_bottom = false;
let mut scroll_delta = None;

ui.horizontal(|ui| {
ui.label("Scroll to a specific item index:");
Expand Down Expand Up @@ -294,6 +297,20 @@ impl super::View for ScrollTo {
scroll_bottom |= ui.button("Scroll to bottom").clicked();
});

ui.horizontal(|ui| {
ui.label("Scroll by");
DragValue::new(&mut self.delta)
.speed(1.0)
.suffix("px")
.ui(ui);
if ui.button("⬇").clicked() {
scroll_delta = Some(self.delta * Vec2::UP); // scroll down (move contents up)
}
if ui.button("⬆").clicked() {
scroll_delta = Some(self.delta * Vec2::DOWN); // scroll up (move contents down)
}
});

let mut scroll_area = ScrollArea::vertical().max_height(200.0).auto_shrink(false);
if go_to_scroll_offset {
scroll_area = scroll_area.vertical_scroll_offset(self.offset);
Expand All @@ -305,6 +322,10 @@ impl super::View for ScrollTo {
if scroll_top {
ui.scroll_to_cursor(Some(Align::TOP));
}
if let Some(scroll_delta) = scroll_delta {
ui.scroll_with_delta(scroll_delta);
}

ui.vertical(|ui| {
for item in 1..=num_items {
if track_item && item == self.track_item {
Expand Down
Loading