Implementing 'virtual cursor offset' to help track & dynamically update drag rect
This commit is contained in:
parent
df74f085ce
commit
23596a1646
2 changed files with 98 additions and 16 deletions
|
|
@ -50,6 +50,7 @@ pub struct MouseArea<'a, Message> {
|
||||||
on_enter: Option<Box<dyn OnEnterExit<'a, Message>>>,
|
on_enter: Option<Box<dyn OnEnterExit<'a, Message>>>,
|
||||||
on_exit: Option<Box<dyn OnEnterExit<'a, Message>>>,
|
on_exit: Option<Box<dyn OnEnterExit<'a, Message>>>,
|
||||||
show_drag_rect: bool,
|
show_drag_rect: bool,
|
||||||
|
cursor_offset: Option<Point>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, Message> MouseArea<'a, Message> {
|
impl<'a, Message> MouseArea<'a, Message> {
|
||||||
|
|
@ -185,6 +186,11 @@ impl<'a, Message> MouseArea<'a, Message> {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn cursor_offset(mut self, offset: Option<Point>) -> Self {
|
||||||
|
self.cursor_offset = offset;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Sets the widget's unique identifier.
|
/// Sets the widget's unique identifier.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn with_id(mut self, id: Id) -> Self {
|
pub fn with_id(mut self, id: Id) -> Self {
|
||||||
|
|
@ -215,6 +221,8 @@ impl<'a, Message, F> OnEnterExit<'a, Message> for F where F: Fn() -> Message + '
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct State {
|
struct State {
|
||||||
last_position: Option<Point>,
|
last_position: Option<Point>,
|
||||||
|
last_virtual_position: Option<Point>,
|
||||||
|
last_in_bounds_position: 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)>,
|
||||||
|
|
@ -224,7 +232,7 @@ struct State {
|
||||||
impl State {
|
impl State {
|
||||||
fn drag_rect(&self, cursor: mouse::Cursor) -> Option<Rectangle> {
|
fn drag_rect(&self, cursor: mouse::Cursor) -> Option<Rectangle> {
|
||||||
if let Some(drag_source) = self.drag_initiated {
|
if let Some(drag_source) = self.drag_initiated {
|
||||||
if let Some(position) = cursor.position() {
|
if let Some(position) = cursor.position().or(self.last_virtual_position) {
|
||||||
if position.distance(drag_source) > 1.0 {
|
if position.distance(drag_source) > 1.0 {
|
||||||
let min_x = drag_source.x.min(position.x);
|
let min_x = drag_source.x.min(position.x);
|
||||||
let max_x = drag_source.x.max(position.x);
|
let max_x = drag_source.x.max(position.x);
|
||||||
|
|
@ -292,6 +300,7 @@ impl<'a, Message> MouseArea<'a, Message> {
|
||||||
on_exit: None,
|
on_exit: None,
|
||||||
on_scroll: None,
|
on_scroll: None,
|
||||||
show_drag_rect: false,
|
show_drag_rect: false,
|
||||||
|
cursor_offset: None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -522,6 +531,42 @@ fn update<Message: Clone>(
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
state.last_position = position_in;
|
state.last_position = position_in;
|
||||||
|
|
||||||
|
// 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
|
||||||
|
// clamp it to the layout bounds
|
||||||
|
if let Some(offset) = widget.cursor_offset {
|
||||||
|
if let Some(in_bounds_pos) = state.last_in_bounds_position {
|
||||||
|
let mut new_virtual_pos = Point {
|
||||||
|
x: in_bounds_pos.x + offset.x,
|
||||||
|
y: in_bounds_pos.y + offset.y
|
||||||
|
};
|
||||||
|
|
||||||
|
// clamp to the viewport
|
||||||
|
new_virtual_pos.x = if new_virtual_pos.x > (layout_bounds.width + layout_bounds.x) {
|
||||||
|
layout_bounds.width + layout_bounds.x
|
||||||
|
} else if new_virtual_pos.x < 0.0 {
|
||||||
|
0.0
|
||||||
|
} else {
|
||||||
|
new_virtual_pos.x
|
||||||
|
};
|
||||||
|
|
||||||
|
new_virtual_pos.y = if new_virtual_pos.y > (layout_bounds.height + layout_bounds.y) {
|
||||||
|
layout_bounds.height + layout_bounds.y
|
||||||
|
} else if new_virtual_pos.y < 0.0 {
|
||||||
|
0.0
|
||||||
|
} else {
|
||||||
|
new_virtual_pos.y
|
||||||
|
};
|
||||||
|
|
||||||
|
state.last_virtual_position = Some(new_virtual_pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the last in bounds position to be the ABSOLUTE version of position_in
|
||||||
|
if position_in.is_some() {
|
||||||
|
state.last_in_bounds_position = cursor.position_over(layout_bounds);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if state.drag_initiated.is_none() && !cursor.is_over(layout_bounds) {
|
if state.drag_initiated.is_none() && !cursor.is_over(layout_bounds) {
|
||||||
|
|
|
||||||
67
src/tab.rs
67
src/tab.rs
|
|
@ -83,7 +83,7 @@ const MAX_SEARCH_RESULTS: usize = 200;
|
||||||
//TODO: configurable thumbnail size?
|
//TODO: configurable thumbnail size?
|
||||||
const THUMBNAIL_SIZE: u32 = (ICON_SIZE_GRID as u32) * (ICON_SCALE_MAX as u32);
|
const THUMBNAIL_SIZE: u32 = (ICON_SIZE_GRID as u32) * (ICON_SCALE_MAX as u32);
|
||||||
|
|
||||||
const DRAG_SCROLL_DISTANCE: u8 = 5;
|
const DRAG_SCROLL_DISTANCE: u8 = 1;
|
||||||
|
|
||||||
//TODO: adjust for locales?
|
//TODO: adjust for locales?
|
||||||
const DATE_TIME_FORMAT: &str = "%b %-d, %-Y, %-I:%M %p";
|
const DATE_TIME_FORMAT: &str = "%b %-d, %-Y, %-I:%M %p";
|
||||||
|
|
@ -1779,6 +1779,7 @@ pub struct Tab {
|
||||||
global_cursor_position: Option<Point>,
|
global_cursor_position: Option<Point>,
|
||||||
current_drag_rect: Option<Rectangle>,
|
current_drag_rect: Option<Rectangle>,
|
||||||
viewport_rect: Option<Rectangle>,
|
viewport_rect: Option<Rectangle>,
|
||||||
|
virtual_cursor_offset: Option<Point>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn calculate_dir_size(path: &Path, controller: Controller) -> Result<u64, String> {
|
fn calculate_dir_size(path: &Path, controller: Controller) -> Result<u64, String> {
|
||||||
|
|
@ -1866,6 +1867,7 @@ impl Tab {
|
||||||
global_cursor_position: None,
|
global_cursor_position: None,
|
||||||
current_drag_rect: None,
|
current_drag_rect: None,
|
||||||
viewport_rect: None,
|
viewport_rect: None,
|
||||||
|
virtual_cursor_offset: None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2216,23 +2218,56 @@ impl Tab {
|
||||||
if self.current_drag_rect.is_some() {
|
if self.current_drag_rect.is_some() {
|
||||||
if let Some(viewport) = self.viewport_rect {
|
if let Some(viewport) = self.viewport_rect {
|
||||||
if !viewport.contains(pos) {
|
if !viewport.contains(pos) {
|
||||||
// diff_y should be NEGATIVE here when close to y=0 (above the MouseArea)
|
if pos.y < viewport.y || pos.y > (viewport.y + viewport.height) {
|
||||||
// and positive when below the viewport
|
// if our mouse is above the scrollable viewport, we want to scroll up
|
||||||
let diff_y = pos.y - viewport.y;
|
let drag_start_point = Point {
|
||||||
let scroll_y: i8 = if diff_y > 0.0 {
|
x: viewport.x,
|
||||||
DRAG_SCROLL_DISTANCE as i8
|
y: viewport.y
|
||||||
} else if diff_y < 0.0 {
|
};
|
||||||
DRAG_SCROLL_DISTANCE as i8 * -1
|
// diff_y should be NEGATIVE here when close to y=0 (above the MouseArea)
|
||||||
} else {
|
// and positive when below the viewport
|
||||||
0
|
let diff_y = pos.y - drag_start_point.y;
|
||||||
};
|
let scroll_y: i8 = if diff_y > 0.0 {
|
||||||
|
DRAG_SCROLL_DISTANCE as i8
|
||||||
|
} else if diff_y < 0.0 {
|
||||||
|
DRAG_SCROLL_DISTANCE as i8 * -1
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
commands.push(Command::Iced(
|
let mut new_offset = Point {
|
||||||
scrollable::scroll_by(self.scrollable_id.clone(), AbsoluteOffset {
|
|
||||||
x: 0.0,
|
x: 0.0,
|
||||||
y: scroll_y as f32
|
y: scroll_y as f32
|
||||||
}).into(),
|
};
|
||||||
));
|
|
||||||
|
if let Some(offset) = self.virtual_cursor_offset {
|
||||||
|
new_offset = Point {
|
||||||
|
x: new_offset.x + offset.x,
|
||||||
|
y: new_offset.y + offset.y,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
self.virtual_cursor_offset = Some(new_offset);
|
||||||
|
|
||||||
|
commands.push(Command::Iced(
|
||||||
|
scrollable::scroll_by(self.scrollable_id.clone(), AbsoluteOffset {
|
||||||
|
x: 0.0,
|
||||||
|
y: scroll_y as f32
|
||||||
|
}).into(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if self.virtual_cursor_offset.is_none() {
|
||||||
|
self.virtual_cursor_offset = Some(Point {
|
||||||
|
x: 0.0,
|
||||||
|
y: 0.0
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// reset our virtual cursor offset when we're back in bounds
|
||||||
|
self.virtual_cursor_offset = None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -4148,6 +4183,7 @@ impl Tab {
|
||||||
.show_drag_rect(true)
|
.show_drag_rect(true)
|
||||||
.on_release(|_| Message::ClickRelease(None))
|
.on_release(|_| Message::ClickRelease(None))
|
||||||
.on_resize(Message::MouseAreaResized)
|
.on_resize(Message::MouseAreaResized)
|
||||||
|
.cursor_offset(self.virtual_cursor_offset)
|
||||||
.into(),
|
.into(),
|
||||||
true,
|
true,
|
||||||
)
|
)
|
||||||
|
|
@ -4479,6 +4515,7 @@ impl Tab {
|
||||||
.show_drag_rect(true)
|
.show_drag_rect(true)
|
||||||
.on_release(|_| Message::ClickRelease(None))
|
.on_release(|_| Message::ClickRelease(None))
|
||||||
.on_resize(Message::MouseAreaResized)
|
.on_resize(Message::MouseAreaResized)
|
||||||
|
.cursor_offset(self.virtual_cursor_offset)
|
||||||
.into(),
|
.into(),
|
||||||
true,
|
true,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue