Merge pull request #816 from ellieplayswow/bugfix/drag-scroll-virtual-cursor-desync

Bugfix to how virtual cursor is calculated before DragEnd when drag-scrolling
This commit is contained in:
Jeremy Soller 2025-02-21 06:49:03 -07:00 committed by GitHub
commit cd43d722d6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 65 additions and 33 deletions

View file

@ -23,7 +23,6 @@ use cosmic::{
widget::Id, widget::Id,
Element, Renderer, Theme, Element, Renderer, Theme,
}; };
use crate::tab::DOUBLE_CLICK_DURATION; use crate::tab::DOUBLE_CLICK_DURATION;
/// Emit messages on mouse events. /// Emit messages on mouse events.
@ -223,6 +222,7 @@ struct State {
last_position: Option<Point>, last_position: Option<Point>,
last_virtual_position: Option<Point>, last_virtual_position: Option<Point>,
last_in_bounds_position: Option<Point>, last_in_bounds_position: Option<Point>,
last_cursor_offset: Option<Point>,
drag_initiated: Option<Point>, drag_initiated: Option<Point>,
modifiers: Modifiers, modifiers: Modifiers,
prev_click: Option<(mouse::Click, Instant)>, prev_click: Option<(mouse::Click, Instant)>,
@ -515,23 +515,31 @@ fn update<Message: Clone>(
} }
} }
if let Event::Mouse(mouse::Event::CursorMoved { .. }) = event { // check if offset differs to calculate virtual position
let position_in = cursor.position_in(layout_bounds); let mut need_to_recalculate = false;
match (position_in, state.last_position) { match (state.last_cursor_offset, widget.cursor_offset) {
(None, Some(_)) => { // check if offset has changed between updates
if let Some(message) = widget.on_exit.as_ref() { (Some(last_cursor_offset), Some(cursor_offset)) => {
shell.publish(message()) if last_cursor_offset != cursor_offset {
} state.last_cursor_offset = Some(cursor_offset);
need_to_recalculate = true;
} }
(Some(_), None) => { },
if let Some(message) = widget.on_enter.as_ref() {
shell.publish(message())
}
}
_ => {}
}
state.last_position = position_in;
// we've started moving out of bounds
(None, Some(cursor_offset)) => {
state.last_cursor_offset = Some(cursor_offset);
need_to_recalculate = true;
},
// we've moved inbounds
(Some(_), None) => {
state.last_cursor_offset = None;
}
_ => {}
}
if need_to_recalculate {
// if we have a cursor_offset, we need to calculate our "virtual" position // if we have a cursor_offset, we need to calculate our "virtual" position
// (where we think the ABSOLUTE cursor is) - we'll take the last in bounds position and // (where we think the ABSOLUTE cursor is) - we'll take the last in bounds position and
// clamp it to the layout bounds // clamp it to the layout bounds
@ -562,6 +570,25 @@ fn update<Message: Clone>(
state.last_virtual_position = Some(new_virtual_pos); state.last_virtual_position = Some(new_virtual_pos);
} }
} }
}
if let Event::Mouse(mouse::Event::CursorMoved { .. }) = event {
let position_in = cursor.position_in(layout_bounds);
match (position_in, state.last_position) {
(None, Some(_)) => {
if let Some(message) = widget.on_exit.as_ref() {
shell.publish(message())
}
}
(Some(_), None) => {
if let Some(message) = widget.on_enter.as_ref() {
shell.publish(message())
}
}
_ => {}
}
state.last_position = position_in;
// set the last in bounds position to be the ABSOLUTE version of position_in // set the last in bounds position to be the ABSOLUTE version of position_in
if position_in.is_some() { if position_in.is_some() {

View file

@ -2241,24 +2241,7 @@ impl Tab {
0.0 0.0
}; };
let mut new_offset = Point {
x: 0.0,
y: scroll_y
};
if let Some(virtual_cursor_offset) = self.virtual_cursor_offset {
new_offset = Point {
x: new_offset.x + virtual_cursor_offset.x,
y: new_offset.y + virtual_cursor_offset.y,
};
}
if let Some(last_scroll_position) = self.last_scroll_position {
new_offset.x = pos.x - last_scroll_position.x;
}
self.virtual_cursor_offset = Some(new_offset);
self.last_scroll_offset = Some(new_offset);
commands.push(Command::AutoScroll(Some(scroll_y))); commands.push(Command::AutoScroll(Some(scroll_y)));
} }
@ -2962,6 +2945,28 @@ impl Tab {
self.scroll_opt = Some(viewport.absolute_offset()); self.scroll_opt = Some(viewport.absolute_offset());
} }
Message::ScrollTab(scroll_speed) => { Message::ScrollTab(scroll_speed) => {
let mut new_offset = Point {
x: 0.0,
y: scroll_speed
};
if let Some(virtual_cursor_offset) = self.virtual_cursor_offset {
new_offset = Point {
x: new_offset.x + virtual_cursor_offset.x,
y: new_offset.y + virtual_cursor_offset.y,
};
}
if let Some(last_scroll_position) = self.last_scroll_position {
if let Some(global_cursor_position) = self.global_cursor_position {
new_offset.x = global_cursor_position.x - last_scroll_position.x;
}
}
self.virtual_cursor_offset = Some(new_offset);
self.last_scroll_offset = Some(new_offset);
commands.push(Command::Iced( commands.push(Command::Iced(
scrollable::scroll_by(self.scrollable_id.clone(), AbsoluteOffset { scrollable::scroll_by(self.scrollable_id.clone(), AbsoluteOffset {
x: 0.0, x: 0.0,