terminal: sustain drag autoscroll
This commit is contained in:
parent
c829fb24d0
commit
4922c7f07b
1 changed files with 403 additions and 85 deletions
|
|
@ -17,6 +17,7 @@ use cosmic::{
|
||||||
event::{Event, Status},
|
event::{Event, Status},
|
||||||
keyboard::{Event as KeyEvent, Key, Modifiers},
|
keyboard::{Event as KeyEvent, Key, Modifiers},
|
||||||
mouse::{self, Button, Event as MouseEvent, ScrollDelta},
|
mouse::{self, Button, Event as MouseEvent, ScrollDelta},
|
||||||
|
window::RedrawRequest,
|
||||||
},
|
},
|
||||||
iced_core::{
|
iced_core::{
|
||||||
Border, Shell,
|
Border, Shell,
|
||||||
|
|
@ -49,6 +50,62 @@ use crate::{
|
||||||
mouse_reporter::MouseReporter, terminal::Metadata,
|
mouse_reporter::MouseReporter, terminal::Metadata,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const AUTOSCROLL_INTERVAL: Duration = Duration::from_millis(100);
|
||||||
|
|
||||||
|
/// Drives repeated drag updates while the pointer is outside the widget.
|
||||||
|
struct DragAutoscroll {
|
||||||
|
active: bool,
|
||||||
|
pointer: Option<Point>,
|
||||||
|
last_tick: Instant,
|
||||||
|
interval: Duration,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DragAutoscroll {
|
||||||
|
fn new(interval: Duration) -> Self {
|
||||||
|
Self {
|
||||||
|
active: false,
|
||||||
|
pointer: None,
|
||||||
|
last_tick: Instant::now(),
|
||||||
|
interval,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start(&mut self, pointer: Point) {
|
||||||
|
self.pointer = Some(pointer);
|
||||||
|
if !self.active {
|
||||||
|
self.active = true;
|
||||||
|
self.last_tick = Instant::now();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_pointer(&mut self, pointer: Point) {
|
||||||
|
if self.active {
|
||||||
|
self.pointer = Some(pointer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stop(&mut self) {
|
||||||
|
self.active = false;
|
||||||
|
self.pointer = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_active(&self) -> bool {
|
||||||
|
self.active
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the stored pointer when the next tick is due.
|
||||||
|
fn next_due(&mut self) -> Option<(Point, f32)> {
|
||||||
|
if self.active && self.last_tick.elapsed() >= self.interval {
|
||||||
|
let elapsed = self.last_tick.elapsed();
|
||||||
|
self.last_tick = Instant::now();
|
||||||
|
let ticks = (elapsed.as_secs_f32() / self.interval.as_secs_f32()).max(1.0);
|
||||||
|
self.pointer.map(|pointer| (pointer, ticks))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct TerminalBox<'a, Message> {
|
pub struct TerminalBox<'a, Message> {
|
||||||
terminal: &'a Mutex<Terminal>,
|
terminal: &'a Mutex<Terminal>,
|
||||||
id: Option<Id>,
|
id: Option<Id>,
|
||||||
|
|
@ -763,8 +820,31 @@ where
|
||||||
shell.publish(on_window_focused());
|
shell.publish(on_window_focused());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
cosmic::iced::window::Event::RedrawRequested(_) => {
|
||||||
|
if is_mouse_mode {
|
||||||
|
state.autoscroll.stop();
|
||||||
|
} else {
|
||||||
|
if let Some((pointer, multiplier)) = state.autoscroll.next_due() {
|
||||||
|
if update_buffer_drag(
|
||||||
|
state,
|
||||||
|
&mut terminal,
|
||||||
|
buffer_size,
|
||||||
|
pointer,
|
||||||
|
layout.bounds(),
|
||||||
|
self.padding,
|
||||||
|
multiplier,
|
||||||
|
) {
|
||||||
|
status = Status::Captured;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if state.autoscroll.is_active() {
|
||||||
|
shell.request_redraw(RedrawRequest::NextFrame);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
cosmic::iced::window::Event::Unfocused => {
|
cosmic::iced::window::Event::Unfocused => {
|
||||||
state.is_focused = false;
|
state.is_focused = false;
|
||||||
|
state.autoscroll.stop();
|
||||||
if let Some(on_window_unfocused) = &self.on_window_unfocused {
|
if let Some(on_window_unfocused) = &self.on_window_unfocused {
|
||||||
shell.publish(on_window_unfocused());
|
shell.publish(on_window_unfocused());
|
||||||
}
|
}
|
||||||
|
|
@ -1027,6 +1107,7 @@ where
|
||||||
let row = y / terminal.size().cell_height;
|
let row = y / terminal.size().cell_height;
|
||||||
|
|
||||||
if is_mouse_mode {
|
if is_mouse_mode {
|
||||||
|
state.autoscroll.stop();
|
||||||
terminal.report_mouse(event, &state.modifiers, col as u32, row as u32);
|
terminal.report_mouse(event, &state.modifiers, col as u32, row as u32);
|
||||||
} else {
|
} else {
|
||||||
state.is_focused = true;
|
state.is_focused = true;
|
||||||
|
|
@ -1083,6 +1164,8 @@ where
|
||||||
state.click = Some((click_kind, Instant::now()));
|
state.click = Some((click_kind, Instant::now()));
|
||||||
state.dragging = Some(Dragging::Buffer {
|
state.dragging = Some(Dragging::Buffer {
|
||||||
edge_scroll_remainder: 0.0,
|
edge_scroll_remainder: 0.0,
|
||||||
|
last_edge_direction: EdgeScrollDirection::None,
|
||||||
|
last_edge_overshoot: 0.0,
|
||||||
last_point: location,
|
last_point: location,
|
||||||
last_side: side,
|
last_side: side,
|
||||||
});
|
});
|
||||||
|
|
@ -1150,6 +1233,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Event::Mouse(MouseEvent::ButtonReleased(Button::Left)) => {
|
Event::Mouse(MouseEvent::ButtonReleased(Button::Left)) => {
|
||||||
|
state.autoscroll.stop();
|
||||||
if let Some(dragging) = state.dragging.take() {
|
if let Some(dragging) = state.dragging.take() {
|
||||||
if let Dragging::Buffer {
|
if let Dragging::Buffer {
|
||||||
last_point,
|
last_point,
|
||||||
|
|
@ -1194,6 +1278,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Event::Mouse(MouseEvent::ButtonReleased(_button)) => {
|
Event::Mouse(MouseEvent::ButtonReleased(_button)) => {
|
||||||
|
state.autoscroll.stop();
|
||||||
if let Some(p) = cursor_position.position_in(layout.bounds()) {
|
if let Some(p) = cursor_position.position_in(layout.bounds()) {
|
||||||
let x = p.x - self.padding.left;
|
let x = p.x - self.padding.left;
|
||||||
let y = p.y - self.padding.top;
|
let y = p.y - self.padding.top;
|
||||||
|
|
@ -1220,8 +1305,9 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(p) = cursor_position.position() {
|
if let Some(p) = cursor_position.position() {
|
||||||
let x = (p.x - layout.bounds().x) - self.padding.left;
|
let bounds = layout.bounds();
|
||||||
let y = (p.y - layout.bounds().y) - self.padding.top;
|
let x = (p.x - bounds.x) - self.padding.left;
|
||||||
|
let y = (p.y - bounds.y) - self.padding.top;
|
||||||
//TODO: better calculation of position
|
//TODO: better calculation of position
|
||||||
let col = x / terminal.size().cell_width;
|
let col = x / terminal.size().cell_width;
|
||||||
let row = y / terminal.size().cell_height;
|
let row = y / terminal.size().cell_height;
|
||||||
|
|
@ -1236,65 +1322,45 @@ where
|
||||||
if is_mouse_mode {
|
if is_mouse_mode {
|
||||||
terminal.report_mouse(event, &state.modifiers, col as u32, row as u32);
|
terminal.report_mouse(event, &state.modifiers, col as u32, row as u32);
|
||||||
} else {
|
} else {
|
||||||
if let Some(dragging) = state.dragging.as_mut() {
|
let handled_buffer_drag = update_buffer_drag(
|
||||||
match dragging {
|
state,
|
||||||
Dragging::Buffer {
|
&mut terminal,
|
||||||
edge_scroll_remainder,
|
buffer_size,
|
||||||
last_point,
|
p,
|
||||||
last_side,
|
bounds,
|
||||||
} => {
|
self.padding,
|
||||||
let size = terminal.size();
|
0.0,
|
||||||
let buffer_height = buffer_size.1.unwrap_or(size.height as f32);
|
);
|
||||||
let max_row_index =
|
if handled_buffer_drag {
|
||||||
(size.screen_lines().saturating_sub(1)) as f32;
|
status = Status::Captured;
|
||||||
let max_col_index = (size.columns().saturating_sub(1)) as f32;
|
} else if let Some(Dragging::Scrollbar {
|
||||||
let (scroll_delta, adjusted_row, new_remainder) =
|
start_y,
|
||||||
edge_scroll_adjustment(
|
start_scroll,
|
||||||
y,
|
}) = state.dragging.as_mut()
|
||||||
buffer_height,
|
{
|
||||||
size.cell_height,
|
let start_y = *start_y;
|
||||||
max_row_index,
|
let start_scroll = *start_scroll;
|
||||||
*edge_scroll_remainder,
|
let scroll_offset = terminal.with_buffer(|buffer| {
|
||||||
);
|
(y - start_y) / buffer.size().1.unwrap_or(1.0)
|
||||||
*edge_scroll_remainder = new_remainder;
|
});
|
||||||
let col = (x / size.cell_width).clamp(0.0, max_col_index);
|
terminal.scroll_to(start_scroll.0 + scroll_offset);
|
||||||
let row = adjusted_row.clamp(0.0, max_row_index);
|
status = Status::Captured;
|
||||||
if scroll_delta != 0 {
|
}
|
||||||
terminal.scroll(TerminalScroll::Delta(scroll_delta));
|
|
||||||
}
|
if matches!(state.dragging, Some(Dragging::Buffer { .. })) {
|
||||||
let location = terminal.viewport_to_point(TermPoint::new(
|
if cursor_position.position_in(bounds).is_some() {
|
||||||
row as usize,
|
state.autoscroll.stop();
|
||||||
TermColumn(col as usize),
|
} else {
|
||||||
));
|
if state.autoscroll.is_active() {
|
||||||
let side = if col.fract() < 0.5 {
|
state.autoscroll.update_pointer(p);
|
||||||
TermSide::Left
|
} else {
|
||||||
} else {
|
state.autoscroll.start(p);
|
||||||
TermSide::Right
|
}
|
||||||
};
|
shell.request_redraw(RedrawRequest::NextFrame);
|
||||||
{
|
}
|
||||||
let mut term = terminal.term.lock();
|
} else {
|
||||||
if let Some(selection) = &mut term.selection {
|
state.autoscroll.stop();
|
||||||
selection.update(location, side);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*last_point = location;
|
|
||||||
*last_side = side;
|
|
||||||
terminal.needs_update = true;
|
|
||||||
}
|
|
||||||
Dragging::Scrollbar {
|
|
||||||
start_y,
|
|
||||||
start_scroll,
|
|
||||||
} => {
|
|
||||||
let start_y = *start_y;
|
|
||||||
let start_scroll = *start_scroll;
|
|
||||||
let scroll_offset = terminal.with_buffer(|buffer| {
|
|
||||||
(y - start_y) / buffer.size().1.unwrap_or(1.0)
|
|
||||||
});
|
|
||||||
terminal.scroll_to(start_scroll.0 + scroll_offset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
status = Status::Captured;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1469,6 +1535,8 @@ enum ClickKind {
|
||||||
enum Dragging {
|
enum Dragging {
|
||||||
Buffer {
|
Buffer {
|
||||||
edge_scroll_remainder: f32,
|
edge_scroll_remainder: f32,
|
||||||
|
last_edge_direction: EdgeScrollDirection,
|
||||||
|
last_edge_overshoot: f32,
|
||||||
last_point: TermPoint,
|
last_point: TermPoint,
|
||||||
last_side: TermSide,
|
last_side: TermSide,
|
||||||
},
|
},
|
||||||
|
|
@ -1478,32 +1546,77 @@ enum Dragging {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||||
|
enum EdgeScrollDirection {
|
||||||
|
None,
|
||||||
|
Top,
|
||||||
|
Bottom,
|
||||||
|
}
|
||||||
|
|
||||||
fn edge_scroll_adjustment(
|
fn edge_scroll_adjustment(
|
||||||
y: f32,
|
y: f32,
|
||||||
buffer_height: f32,
|
buffer_height: f32,
|
||||||
cell_height: f32,
|
cell_height: f32,
|
||||||
max_row_index: f32,
|
max_row_index: f32,
|
||||||
scroll_remainder: f32,
|
scroll_remainder: f32,
|
||||||
) -> (i32, f32, f32) {
|
last_direction: EdgeScrollDirection,
|
||||||
|
last_overshoot: f32,
|
||||||
|
forced_increment: f32,
|
||||||
|
) -> (i32, f32, f32, EdgeScrollDirection, f32) {
|
||||||
if cell_height <= 0.0 {
|
if cell_height <= 0.0 {
|
||||||
return (0, 0.0, 0.0);
|
return (0, 0.0, 0.0, EdgeScrollDirection::None, 0.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut row = y / cell_height;
|
let mut row = y / cell_height;
|
||||||
let mut delta = 0;
|
let mut delta = 0;
|
||||||
let mut remainder = scroll_remainder;
|
let mut remainder = scroll_remainder;
|
||||||
|
let mut direction = EdgeScrollDirection::None;
|
||||||
|
let mut overshoot = 0.0;
|
||||||
|
|
||||||
if y < 0.0 {
|
if y < 0.0 {
|
||||||
remainder = remainder.max(0.0);
|
direction = EdgeScrollDirection::Top;
|
||||||
remainder += (-y) / cell_height;
|
overshoot = (-y) / cell_height;
|
||||||
|
let previous_overshoot = if last_direction == direction {
|
||||||
|
last_overshoot
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
};
|
||||||
|
let overshoot_delta = overshoot - previous_overshoot;
|
||||||
|
let forced = if forced_increment > 0.0 {
|
||||||
|
forced_increment * overshoot.max(1.0)
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
};
|
||||||
|
if overshoot_delta > 0.0 || forced > 0.0 {
|
||||||
|
remainder = remainder.max(0.0) + overshoot_delta.max(0.0) + forced;
|
||||||
|
} else {
|
||||||
|
remainder = remainder.max(0.0).min(overshoot.fract());
|
||||||
|
}
|
||||||
delta = remainder.trunc() as i32;
|
delta = remainder.trunc() as i32;
|
||||||
remainder -= delta as f32;
|
remainder -= delta as f32;
|
||||||
row = 0.0;
|
row = 0.0;
|
||||||
} else if y > buffer_height {
|
} else if y > buffer_height {
|
||||||
remainder = remainder.min(0.0);
|
direction = EdgeScrollDirection::Bottom;
|
||||||
remainder -= (y - buffer_height) / cell_height;
|
overshoot = (y - buffer_height) / cell_height;
|
||||||
delta = remainder.trunc() as i32;
|
let previous_overshoot = if last_direction == direction {
|
||||||
remainder -= delta as f32;
|
last_overshoot
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
};
|
||||||
|
let overshoot_delta = overshoot - previous_overshoot;
|
||||||
|
let forced = if forced_increment > 0.0 {
|
||||||
|
forced_increment * overshoot.max(1.0)
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
};
|
||||||
|
if overshoot_delta > 0.0 || forced > 0.0 {
|
||||||
|
remainder = remainder.max(0.0) + overshoot_delta.max(0.0) + forced;
|
||||||
|
} else {
|
||||||
|
remainder = remainder.max(0.0).min(overshoot.fract());
|
||||||
|
}
|
||||||
|
let lines = remainder.trunc() as i32;
|
||||||
|
delta = -lines;
|
||||||
|
remainder -= lines as f32;
|
||||||
row = max_row_index;
|
row = max_row_index;
|
||||||
} else {
|
} else {
|
||||||
remainder = 0.0;
|
remainder = 0.0;
|
||||||
|
|
@ -1511,7 +1624,77 @@ fn edge_scroll_adjustment(
|
||||||
|
|
||||||
row = row.clamp(0.0, max_row_index);
|
row = row.clamp(0.0, max_row_index);
|
||||||
|
|
||||||
(delta, row, remainder)
|
(delta, row, remainder, direction, overshoot)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_buffer_drag(
|
||||||
|
state: &mut State,
|
||||||
|
terminal: &mut Terminal,
|
||||||
|
buffer_size: (Option<f32>, Option<f32>),
|
||||||
|
pointer: Point,
|
||||||
|
bounds: Rectangle<f32>,
|
||||||
|
padding: Padding,
|
||||||
|
forced_increment: f32,
|
||||||
|
) -> bool {
|
||||||
|
let Some(dragging) = state.dragging.as_mut() else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
let Dragging::Buffer {
|
||||||
|
edge_scroll_remainder,
|
||||||
|
last_edge_direction,
|
||||||
|
last_edge_overshoot,
|
||||||
|
last_point,
|
||||||
|
last_side,
|
||||||
|
} = dragging
|
||||||
|
else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
let x = (pointer.x - bounds.x) - padding.left;
|
||||||
|
let y = (pointer.y - bounds.y) - padding.top;
|
||||||
|
|
||||||
|
let size = terminal.size();
|
||||||
|
let buffer_height = buffer_size.1.unwrap_or(size.height as f32);
|
||||||
|
let max_row_index = (size.screen_lines().saturating_sub(1)) as f32;
|
||||||
|
let max_col_index = (size.columns().saturating_sub(1)) as f32;
|
||||||
|
let (scroll_delta, adjusted_row, new_remainder, new_direction, new_overshoot) =
|
||||||
|
edge_scroll_adjustment(
|
||||||
|
y,
|
||||||
|
buffer_height,
|
||||||
|
size.cell_height,
|
||||||
|
max_row_index,
|
||||||
|
*edge_scroll_remainder,
|
||||||
|
*last_edge_direction,
|
||||||
|
*last_edge_overshoot,
|
||||||
|
forced_increment,
|
||||||
|
);
|
||||||
|
*edge_scroll_remainder = new_remainder;
|
||||||
|
*last_edge_direction = new_direction;
|
||||||
|
*last_edge_overshoot = new_overshoot;
|
||||||
|
let col = (x / size.cell_width).clamp(0.0, max_col_index);
|
||||||
|
let row = adjusted_row.clamp(0.0, max_row_index);
|
||||||
|
if scroll_delta != 0 {
|
||||||
|
terminal.scroll(TerminalScroll::Delta(scroll_delta));
|
||||||
|
}
|
||||||
|
let location =
|
||||||
|
terminal.viewport_to_point(TermPoint::new(row as usize, TermColumn(col as usize)));
|
||||||
|
let side = if col.fract() < 0.5 {
|
||||||
|
TermSide::Left
|
||||||
|
} else {
|
||||||
|
TermSide::Right
|
||||||
|
};
|
||||||
|
{
|
||||||
|
let mut term = terminal.term.lock();
|
||||||
|
if let Some(selection) = &mut term.selection {
|
||||||
|
selection.update(location, side);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*last_point = location;
|
||||||
|
*last_side = side;
|
||||||
|
terminal.needs_update = true;
|
||||||
|
|
||||||
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct State {
|
pub struct State {
|
||||||
|
|
@ -1521,6 +1704,7 @@ pub struct State {
|
||||||
is_focused: bool,
|
is_focused: bool,
|
||||||
scroll_pixels: f32,
|
scroll_pixels: f32,
|
||||||
scrollbar_rect: Cell<Rectangle<f32>>,
|
scrollbar_rect: Cell<Rectangle<f32>>,
|
||||||
|
autoscroll: DragAutoscroll,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl State {
|
impl State {
|
||||||
|
|
@ -1533,6 +1717,7 @@ impl State {
|
||||||
is_focused: false,
|
is_focused: false,
|
||||||
scroll_pixels: 0.0,
|
scroll_pixels: 0.0,
|
||||||
scrollbar_rect: Cell::new(Rectangle::default()),
|
scrollbar_rect: Cell::new(Rectangle::default()),
|
||||||
|
autoscroll: DragAutoscroll::new(AUTOSCROLL_INTERVAL),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1609,7 +1794,7 @@ fn ss3(code: &str, modifiers: u8) -> Option<Vec<u8>> {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::edge_scroll_adjustment;
|
use super::{EdgeScrollDirection, edge_scroll_adjustment};
|
||||||
|
|
||||||
const BUFFER_HEIGHT: f32 = 200.0;
|
const BUFFER_HEIGHT: f32 = 200.0;
|
||||||
const CELL_HEIGHT: f32 = 20.0;
|
const CELL_HEIGHT: f32 = 20.0;
|
||||||
|
|
@ -1617,57 +1802,190 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn edge_scroll_small_top_overshoot_does_not_scroll_immediately() {
|
fn edge_scroll_small_top_overshoot_does_not_scroll_immediately() {
|
||||||
let (delta, row, remainder) =
|
let (delta, row, remainder, direction, overshoot) = edge_scroll_adjustment(
|
||||||
edge_scroll_adjustment(-5.0, BUFFER_HEIGHT, CELL_HEIGHT, MAX_ROW, 0.0);
|
-5.0,
|
||||||
|
BUFFER_HEIGHT,
|
||||||
|
CELL_HEIGHT,
|
||||||
|
MAX_ROW,
|
||||||
|
0.0,
|
||||||
|
EdgeScrollDirection::None,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
);
|
||||||
assert_eq!(delta, 0);
|
assert_eq!(delta, 0);
|
||||||
assert_eq!(row, 0.0);
|
assert_eq!(row, 0.0);
|
||||||
assert!((remainder - 0.25).abs() < f32::EPSILON);
|
assert!((remainder - 0.25).abs() < f32::EPSILON);
|
||||||
|
|
||||||
|
let (delta_repeat, row_repeat, remainder_repeat, _, _) = edge_scroll_adjustment(
|
||||||
|
-5.0,
|
||||||
|
BUFFER_HEIGHT,
|
||||||
|
CELL_HEIGHT,
|
||||||
|
MAX_ROW,
|
||||||
|
remainder,
|
||||||
|
direction,
|
||||||
|
overshoot,
|
||||||
|
0.0,
|
||||||
|
);
|
||||||
|
assert_eq!(delta_repeat, 0);
|
||||||
|
assert_eq!(row_repeat, 0.0);
|
||||||
|
assert!((remainder_repeat - 0.25).abs() < f32::EPSILON);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn edge_scroll_top_accumulates_into_scroll() {
|
fn edge_scroll_top_accumulates_into_scroll() {
|
||||||
let (_, _, remainder) =
|
let (_delta, _row, mut remainder, mut direction, mut overshoot) = edge_scroll_adjustment(
|
||||||
edge_scroll_adjustment(-5.0, BUFFER_HEIGHT, CELL_HEIGHT, MAX_ROW, 0.0);
|
-5.0,
|
||||||
let (delta, row, remainder) =
|
BUFFER_HEIGHT,
|
||||||
edge_scroll_adjustment(-45.0, BUFFER_HEIGHT, CELL_HEIGHT, MAX_ROW, remainder);
|
CELL_HEIGHT,
|
||||||
|
MAX_ROW,
|
||||||
|
0.0,
|
||||||
|
EdgeScrollDirection::None,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
);
|
||||||
|
let (delta, row, new_remainder, new_direction, new_overshoot) = edge_scroll_adjustment(
|
||||||
|
-45.0,
|
||||||
|
BUFFER_HEIGHT,
|
||||||
|
CELL_HEIGHT,
|
||||||
|
MAX_ROW,
|
||||||
|
remainder,
|
||||||
|
direction,
|
||||||
|
overshoot,
|
||||||
|
0.0,
|
||||||
|
);
|
||||||
assert_eq!(delta, 2);
|
assert_eq!(delta, 2);
|
||||||
assert_eq!(row, 0.0);
|
assert_eq!(row, 0.0);
|
||||||
assert!((remainder - 0.5).abs() < f32::EPSILON);
|
assert!((new_remainder - 0.25).abs() < f32::EPSILON);
|
||||||
|
remainder = new_remainder;
|
||||||
|
direction = new_direction;
|
||||||
|
overshoot = new_overshoot;
|
||||||
|
|
||||||
|
// repeated event with the same overshoot should not accumulate more scroll
|
||||||
|
let (delta, _, remainder, _, _) = edge_scroll_adjustment(
|
||||||
|
-45.0,
|
||||||
|
BUFFER_HEIGHT,
|
||||||
|
CELL_HEIGHT,
|
||||||
|
MAX_ROW,
|
||||||
|
remainder,
|
||||||
|
direction,
|
||||||
|
overshoot,
|
||||||
|
0.0,
|
||||||
|
);
|
||||||
|
assert_eq!(delta, 0);
|
||||||
|
assert!((remainder - 0.25).abs() < f32::EPSILON);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn edge_scroll_inside_viewport_resets_remainder() {
|
fn edge_scroll_inside_viewport_resets_remainder() {
|
||||||
let (_, _, remainder) =
|
let (_delta, _row, remainder, direction, overshoot) = edge_scroll_adjustment(
|
||||||
edge_scroll_adjustment(-25.0, BUFFER_HEIGHT, CELL_HEIGHT, MAX_ROW, 0.0);
|
-25.0,
|
||||||
let (delta, row, remainder) =
|
BUFFER_HEIGHT,
|
||||||
edge_scroll_adjustment(60.0, BUFFER_HEIGHT, CELL_HEIGHT, MAX_ROW, remainder);
|
CELL_HEIGHT,
|
||||||
|
MAX_ROW,
|
||||||
|
0.0,
|
||||||
|
EdgeScrollDirection::None,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
);
|
||||||
|
let (delta, row, remainder, direction, overshoot) = edge_scroll_adjustment(
|
||||||
|
60.0,
|
||||||
|
BUFFER_HEIGHT,
|
||||||
|
CELL_HEIGHT,
|
||||||
|
MAX_ROW,
|
||||||
|
remainder,
|
||||||
|
direction,
|
||||||
|
overshoot,
|
||||||
|
0.0,
|
||||||
|
);
|
||||||
assert_eq!(delta, 0);
|
assert_eq!(delta, 0);
|
||||||
assert!((row - 3.0).abs() < f32::EPSILON);
|
assert!((row - 3.0).abs() < f32::EPSILON);
|
||||||
assert_eq!(remainder, 0.0);
|
assert_eq!(remainder, 0.0);
|
||||||
|
assert_eq!(direction, EdgeScrollDirection::None);
|
||||||
|
assert_eq!(overshoot, 0.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn edge_scroll_bottom_accumulates_scroll() {
|
fn edge_scroll_bottom_accumulates_scroll() {
|
||||||
let (delta, row, remainder) = edge_scroll_adjustment(
|
let (delta, row, remainder, direction, overshoot) = edge_scroll_adjustment(
|
||||||
BUFFER_HEIGHT + 1.0,
|
BUFFER_HEIGHT + 1.0,
|
||||||
BUFFER_HEIGHT,
|
BUFFER_HEIGHT,
|
||||||
CELL_HEIGHT,
|
CELL_HEIGHT,
|
||||||
MAX_ROW,
|
MAX_ROW,
|
||||||
0.0,
|
0.0,
|
||||||
|
EdgeScrollDirection::None,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
);
|
);
|
||||||
assert_eq!(delta, 0);
|
assert_eq!(delta, 0);
|
||||||
assert!((row - MAX_ROW).abs() < f32::EPSILON);
|
assert!((row - MAX_ROW).abs() < f32::EPSILON);
|
||||||
assert!((remainder + 0.05).abs() < f32::EPSILON);
|
assert!((remainder - 0.05).abs() < f32::EPSILON);
|
||||||
|
|
||||||
let (delta, row, remainder) = edge_scroll_adjustment(
|
let (delta, row, remainder, direction, overshoot) = edge_scroll_adjustment(
|
||||||
BUFFER_HEIGHT + 45.0,
|
BUFFER_HEIGHT + 45.0,
|
||||||
BUFFER_HEIGHT,
|
BUFFER_HEIGHT,
|
||||||
CELL_HEIGHT,
|
CELL_HEIGHT,
|
||||||
MAX_ROW,
|
MAX_ROW,
|
||||||
remainder,
|
remainder,
|
||||||
|
direction,
|
||||||
|
overshoot,
|
||||||
|
0.0,
|
||||||
);
|
);
|
||||||
assert_eq!(delta, -2);
|
assert_eq!(delta, -2);
|
||||||
assert!((row - MAX_ROW).abs() < f32::EPSILON);
|
assert!((row - MAX_ROW).abs() < f32::EPSILON);
|
||||||
assert!((remainder + 0.3).abs() < f32::EPSILON);
|
assert!((remainder - 0.25).abs() < f32::EPSILON);
|
||||||
|
|
||||||
|
let (delta, _, remainder, _, _) = edge_scroll_adjustment(
|
||||||
|
BUFFER_HEIGHT + 45.0,
|
||||||
|
BUFFER_HEIGHT,
|
||||||
|
CELL_HEIGHT,
|
||||||
|
MAX_ROW,
|
||||||
|
remainder,
|
||||||
|
direction,
|
||||||
|
overshoot,
|
||||||
|
0.0,
|
||||||
|
);
|
||||||
|
assert_eq!(delta, 0);
|
||||||
|
assert!((remainder - 0.25).abs() < f32::EPSILON);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn edge_scroll_forced_increment_continues_scrolling() {
|
||||||
|
let (delta, _, remainder, direction, overshoot) = edge_scroll_adjustment(
|
||||||
|
-5.0,
|
||||||
|
BUFFER_HEIGHT,
|
||||||
|
CELL_HEIGHT,
|
||||||
|
MAX_ROW,
|
||||||
|
0.0,
|
||||||
|
EdgeScrollDirection::None,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
);
|
||||||
|
assert_eq!(delta, 0);
|
||||||
|
|
||||||
|
let (delta, _, remainder, direction, overshoot) = edge_scroll_adjustment(
|
||||||
|
-5.0,
|
||||||
|
BUFFER_HEIGHT,
|
||||||
|
CELL_HEIGHT,
|
||||||
|
MAX_ROW,
|
||||||
|
remainder,
|
||||||
|
direction,
|
||||||
|
overshoot,
|
||||||
|
1.0,
|
||||||
|
);
|
||||||
|
assert_eq!(delta, 1);
|
||||||
|
assert!((remainder - 0.25).abs() < f32::EPSILON);
|
||||||
|
|
||||||
|
let (delta, _, remainder, _, _) = edge_scroll_adjustment(
|
||||||
|
-5.0,
|
||||||
|
BUFFER_HEIGHT,
|
||||||
|
CELL_HEIGHT,
|
||||||
|
MAX_ROW,
|
||||||
|
remainder,
|
||||||
|
direction,
|
||||||
|
overshoot,
|
||||||
|
1.0,
|
||||||
|
);
|
||||||
|
assert_eq!(delta, 1);
|
||||||
|
assert!((remainder - 0.25).abs() < f32::EPSILON);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue