Merge pull request #812 from ellieplayswow/feature/drag-scroll
adding in drag-scroll functionality when cursor is outside of scrollable viewport
This commit is contained in:
commit
f934a5a2c5
3 changed files with 224 additions and 6 deletions
35
src/app.rs
35
src/app.rs
|
|
@ -18,6 +18,7 @@ use cosmic::{
|
||||||
app::{self, context_drawer, message, Core, Task},
|
app::{self, context_drawer, message, Core, Task},
|
||||||
cosmic_config, cosmic_theme, executor,
|
cosmic_config, cosmic_theme, executor,
|
||||||
iced::{
|
iced::{
|
||||||
|
self,
|
||||||
clipboard::dnd::DndAction,
|
clipboard::dnd::DndAction,
|
||||||
event,
|
event,
|
||||||
futures::{self, SinkExt},
|
futures::{self, SinkExt},
|
||||||
|
|
@ -54,6 +55,7 @@ use std::{
|
||||||
sync::{Arc, Mutex},
|
sync::{Arc, Mutex},
|
||||||
time::{self, Instant},
|
time::{self, Instant},
|
||||||
};
|
};
|
||||||
|
use cosmic::iced::mouse::Event::CursorMoved;
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
use trash::TrashItem;
|
use trash::TrashItem;
|
||||||
#[cfg(feature = "wayland")]
|
#[cfg(feature = "wayland")]
|
||||||
|
|
@ -277,6 +279,7 @@ pub enum Message {
|
||||||
Config(Config),
|
Config(Config),
|
||||||
Copy(Option<Entity>),
|
Copy(Option<Entity>),
|
||||||
CosmicSettings(&'static str),
|
CosmicSettings(&'static str),
|
||||||
|
CursorMoved(Point),
|
||||||
Cut(Option<Entity>),
|
Cut(Option<Entity>),
|
||||||
DesktopConfig(DesktopConfig),
|
DesktopConfig(DesktopConfig),
|
||||||
DesktopViewOptions,
|
DesktopViewOptions,
|
||||||
|
|
@ -330,6 +333,7 @@ pub enum Message {
|
||||||
Rename(Option<Entity>),
|
Rename(Option<Entity>),
|
||||||
ReplaceResult(ReplaceResult),
|
ReplaceResult(ReplaceResult),
|
||||||
RestoreFromTrash(Option<Entity>),
|
RestoreFromTrash(Option<Entity>),
|
||||||
|
ScrollTab(i16),
|
||||||
SearchActivate,
|
SearchActivate,
|
||||||
SearchClear,
|
SearchClear,
|
||||||
SearchInput(String),
|
SearchInput(String),
|
||||||
|
|
@ -548,6 +552,7 @@ pub struct App {
|
||||||
tab_dnd_hover: Option<(Entity, Instant)>,
|
tab_dnd_hover: Option<(Entity, Instant)>,
|
||||||
nav_drag_id: DragId,
|
nav_drag_id: DragId,
|
||||||
tab_drag_id: DragId,
|
tab_drag_id: DragId,
|
||||||
|
auto_scroll_speed: Option<i16>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
|
|
@ -1626,6 +1631,7 @@ impl Application for App {
|
||||||
tab_dnd_hover: None,
|
tab_dnd_hover: None,
|
||||||
nav_drag_id: DragId::new(),
|
nav_drag_id: DragId::new(),
|
||||||
tab_drag_id: DragId::new(),
|
tab_drag_id: DragId::new(),
|
||||||
|
auto_scroll_speed: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut commands = vec![app.update_config()];
|
let mut commands = vec![app.update_config()];
|
||||||
|
|
@ -1926,6 +1932,10 @@ impl Application for App {
|
||||||
let contents = ClipboardCopy::new(ClipboardKind::Copy, &paths);
|
let contents = ClipboardCopy::new(ClipboardKind::Copy, &paths);
|
||||||
return clipboard::write_data(contents);
|
return clipboard::write_data(contents);
|
||||||
}
|
}
|
||||||
|
Message::CursorMoved(pos) => {
|
||||||
|
let entity = self.tab_model.active();
|
||||||
|
return self.update(Message::TabMessage(Some(entity), tab::Message::CursorMoved(pos)));
|
||||||
|
}
|
||||||
Message::Cut(entity_opt) => {
|
Message::Cut(entity_opt) => {
|
||||||
let paths = self.selected_paths(entity_opt);
|
let paths = self.selected_paths(entity_opt);
|
||||||
let contents = ClipboardCopy::new(ClipboardKind::Cut, &paths);
|
let contents = ClipboardCopy::new(ClipboardKind::Cut, &paths);
|
||||||
|
|
@ -2788,6 +2798,10 @@ impl Application for App {
|
||||||
self.operation(Operation::Restore { items: trash_items });
|
self.operation(Operation::Restore { items: trash_items });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Message::ScrollTab(scroll_speed) => {
|
||||||
|
let entity = self.tab_model.active();
|
||||||
|
return self.update(Message::TabMessage(Some(entity), tab::Message::ScrollTab((scroll_speed as f32) / 10.0)));
|
||||||
|
}
|
||||||
Message::SearchActivate => {
|
Message::SearchActivate => {
|
||||||
return if self.search_get().is_none() {
|
return if self.search_get().is_none() {
|
||||||
self.search_set_active(Some(String::new()))
|
self.search_set_active(Some(String::new()))
|
||||||
|
|
@ -2925,6 +2939,17 @@ impl Application for App {
|
||||||
config_set!(favorites, favorites);
|
config_set!(favorites, favorites);
|
||||||
commands.push(self.update_config());
|
commands.push(self.update_config());
|
||||||
}
|
}
|
||||||
|
tab::Command::AutoScroll(scroll_speed) => {
|
||||||
|
// converting an f32 to an i16 here by multiplying by 10 and casting to i16
|
||||||
|
// further resolution isn't necessary
|
||||||
|
if let Some(scroll_speed_float) = scroll_speed {
|
||||||
|
self.auto_scroll_speed = Some((scroll_speed_float * 10.0) as i16);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
self.auto_scroll_speed = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
tab::Command::ChangeLocation(tab_title, tab_path, selection_paths) => {
|
tab::Command::ChangeLocation(tab_title, tab_path, selection_paths) => {
|
||||||
self.activate_nav_model_location(&tab_path);
|
self.activate_nav_model_location(&tab_path);
|
||||||
|
|
||||||
|
|
@ -4536,7 +4561,8 @@ impl Application for App {
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
Event::Mouse(CursorMoved { position: pos }) => Some(Message::CursorMoved(pos)),
|
||||||
_ => None,
|
_ => None,
|
||||||
}),
|
}),
|
||||||
Config::subscription().map(|update| {
|
Config::subscription().map(|update| {
|
||||||
|
|
@ -4707,6 +4733,13 @@ impl Application for App {
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
if let Some(scroll_speed) = self.auto_scroll_speed {
|
||||||
|
subscriptions.push(
|
||||||
|
iced::time::every(time::Duration::from_millis(10)).with(scroll_speed)
|
||||||
|
.map(|(scroll_speed, _)| Message::ScrollTab(scroll_speed))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
for (key, mounter) in MOUNTERS.iter() {
|
for (key, mounter) in MOUNTERS.iter() {
|
||||||
subscriptions.push(
|
subscriptions.push(
|
||||||
mounter.subscription().with(*key).map(
|
mounter.subscription().with(*key).map(
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
@ -199,8 +205,8 @@ impl<'a, Message, F> OnMouseButton<'a, Message> for F where F: Fn(Option<Point>)
|
||||||
pub trait OnDrag<'a, Message>: Fn(Option<Rectangle>) -> Message + 'a {}
|
pub trait OnDrag<'a, Message>: Fn(Option<Rectangle>) -> Message + 'a {}
|
||||||
impl<'a, Message, F> OnDrag<'a, Message> for F where F: Fn(Option<Rectangle>) -> Message + 'a {}
|
impl<'a, Message, F> OnDrag<'a, Message> for F where F: Fn(Option<Rectangle>) -> Message + 'a {}
|
||||||
|
|
||||||
pub trait OnResize<'a, Message>: Fn(Size) -> Message + 'a {}
|
pub trait OnResize<'a, Message>: Fn(Size, Rectangle) -> Message + 'a {}
|
||||||
impl<'a, Message, F> OnResize<'a, Message> for F where F: Fn(Size) -> Message + 'a {}
|
impl<'a, Message, F> OnResize<'a, Message> for F where F: Fn(Size, Rectangle) -> Message + 'a {}
|
||||||
|
|
||||||
pub trait OnScroll<'a, Message>: Fn(mouse::ScrollDelta, Modifiers) -> Option<Message> + 'a {}
|
pub trait OnScroll<'a, Message>: Fn(mouse::ScrollDelta, Modifiers) -> Option<Message> + 'a {}
|
||||||
impl<'a, Message, F> OnScroll<'a, Message> for F where
|
impl<'a, Message, F> OnScroll<'a, Message> for F where
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -374,6 +383,7 @@ where
|
||||||
cursor,
|
cursor,
|
||||||
shell,
|
shell,
|
||||||
tree.state.downcast_mut::<State>(),
|
tree.state.downcast_mut::<State>(),
|
||||||
|
viewport,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -493,6 +503,7 @@ fn update<Message: Clone>(
|
||||||
cursor: mouse::Cursor,
|
cursor: mouse::Cursor,
|
||||||
shell: &mut Shell<'_, Message>,
|
shell: &mut Shell<'_, Message>,
|
||||||
state: &mut State,
|
state: &mut State,
|
||||||
|
viewport: &Rectangle
|
||||||
) -> event::Status {
|
) -> event::Status {
|
||||||
let layout_bounds = layout.bounds();
|
let layout_bounds = layout.bounds();
|
||||||
|
|
||||||
|
|
@ -500,7 +511,7 @@ fn update<Message: Clone>(
|
||||||
let size = layout_bounds.size();
|
let size = layout_bounds.size();
|
||||||
if state.size != Some(size) {
|
if state.size != Some(size) {
|
||||||
state.size = Some(size);
|
state.size = Some(size);
|
||||||
shell.publish(message(size));
|
shell.publish(message(size, *viewport));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -520,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) {
|
||||||
|
|
|
||||||
140
src/tab.rs
140
src/tab.rs
|
|
@ -83,6 +83,8 @@ 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: f32 = 15.0;
|
||||||
|
|
||||||
//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";
|
||||||
const TIME_FORMAT: &str = "%-I:%M %p";
|
const TIME_FORMAT: &str = "%-I:%M %p";
|
||||||
|
|
@ -1098,6 +1100,7 @@ pub enum Command {
|
||||||
Action(Action),
|
Action(Action),
|
||||||
AddNetworkDrive,
|
AddNetworkDrive,
|
||||||
AddToSidebar(PathBuf),
|
AddToSidebar(PathBuf),
|
||||||
|
AutoScroll(Option<f32>),
|
||||||
ChangeLocation(String, Location, Option<Vec<PathBuf>>),
|
ChangeLocation(String, Location, Option<Vec<PathBuf>>),
|
||||||
DropFiles(PathBuf, ClipboardPaste),
|
DropFiles(PathBuf, ClipboardPaste),
|
||||||
EmptyTrash,
|
EmptyTrash,
|
||||||
|
|
@ -1121,6 +1124,8 @@ pub enum Message {
|
||||||
Click(Option<usize>),
|
Click(Option<usize>),
|
||||||
DoubleClick(Option<usize>),
|
DoubleClick(Option<usize>),
|
||||||
ClickRelease(Option<usize>),
|
ClickRelease(Option<usize>),
|
||||||
|
CursorMoved(Point),
|
||||||
|
MouseAreaResized(Size, Rectangle),
|
||||||
DragEnd(Option<usize>),
|
DragEnd(Option<usize>),
|
||||||
Config(TabConfig),
|
Config(TabConfig),
|
||||||
ContextAction(Action),
|
ContextAction(Action),
|
||||||
|
|
@ -1153,6 +1158,7 @@ pub enum Message {
|
||||||
RightClick(Option<usize>),
|
RightClick(Option<usize>),
|
||||||
MiddleClick(usize),
|
MiddleClick(usize),
|
||||||
Scroll(Viewport),
|
Scroll(Viewport),
|
||||||
|
ScrollTab(f32),
|
||||||
ScrollToFocus,
|
ScrollToFocus,
|
||||||
SearchContext(Location, SearchContextWrapper),
|
SearchContext(Location, SearchContextWrapper),
|
||||||
SearchReady(bool),
|
SearchReady(bool),
|
||||||
|
|
@ -1772,6 +1778,12 @@ pub struct Tab {
|
||||||
selected_clicked: bool,
|
selected_clicked: bool,
|
||||||
last_right_click: Option<usize>,
|
last_right_click: Option<usize>,
|
||||||
search_context: Option<SearchContext>,
|
search_context: Option<SearchContext>,
|
||||||
|
global_cursor_position: Option<Point>,
|
||||||
|
current_drag_rect: Option<Rectangle>,
|
||||||
|
viewport_rect: Option<Rectangle>,
|
||||||
|
virtual_cursor_offset: Option<Point>,
|
||||||
|
last_scroll_position: Option<Point>,
|
||||||
|
last_scroll_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> {
|
||||||
|
|
@ -1856,6 +1868,12 @@ impl Tab {
|
||||||
selected_clicked: false,
|
selected_clicked: false,
|
||||||
last_right_click: None,
|
last_right_click: None,
|
||||||
search_context: None,
|
search_context: None,
|
||||||
|
global_cursor_position: None,
|
||||||
|
current_drag_rect: None,
|
||||||
|
viewport_rect: None,
|
||||||
|
virtual_cursor_offset: None,
|
||||||
|
last_scroll_position: None,
|
||||||
|
last_scroll_offset: None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2199,13 +2217,120 @@ impl Tab {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Message::CursorMoved(pos) => {
|
||||||
|
self.global_cursor_position = Some(pos);
|
||||||
|
|
||||||
|
// we're currently dragging
|
||||||
|
if self.current_drag_rect.is_some() {
|
||||||
|
if let Some(viewport) = self.viewport_rect {
|
||||||
|
if !viewport.contains(pos) {
|
||||||
|
if pos.y < viewport.y || pos.y > (viewport.y + viewport.height) {
|
||||||
|
// if our mouse is above the scrollable viewport, we want to scroll up
|
||||||
|
let drag_start_point = Point {
|
||||||
|
x: viewport.x,
|
||||||
|
y: viewport.y
|
||||||
|
};
|
||||||
|
// diff_y should be NEGATIVE here when close to y=0 (above the MouseArea)
|
||||||
|
// and positive when below the viewport
|
||||||
|
let diff_y = pos.y - drag_start_point.y;
|
||||||
|
let scroll_y: f32 = if diff_y > 0.0 {
|
||||||
|
DRAG_SCROLL_DISTANCE
|
||||||
|
} else if diff_y < 0.0 {
|
||||||
|
DRAG_SCROLL_DISTANCE * -1.0
|
||||||
|
} else {
|
||||||
|
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)));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if let Some(last_scroll_offset) = self.last_scroll_offset {
|
||||||
|
if let Some(last_scroll_position) = self.last_scroll_position {
|
||||||
|
self.virtual_cursor_offset = Some(Point {
|
||||||
|
x: 0.0,
|
||||||
|
y: (pos.y - last_scroll_position.y + last_scroll_offset.y)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if let Some(last_scroll_position) = self.last_scroll_position {
|
||||||
|
self.virtual_cursor_offset = Some(Point {
|
||||||
|
x: 0.0,
|
||||||
|
y: (pos.y - last_scroll_position.y)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
self.virtual_cursor_offset = Some(Point {
|
||||||
|
x: 0.0,
|
||||||
|
y: 0.0
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
commands.push(Command::AutoScroll(None));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// reset our virtual cursor offset when we're back in bounds
|
||||||
|
self.virtual_cursor_offset = None;
|
||||||
|
self.last_scroll_position = Some(pos);
|
||||||
|
self.last_scroll_offset = None;
|
||||||
|
|
||||||
|
commands.push(Command::AutoScroll(None));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Message::MouseAreaResized(_size, viewport) => {
|
||||||
|
// if we have a scroll position, we want to subtract it from the viewport
|
||||||
|
// so that we don't desync when swapping
|
||||||
|
if let Some(scroll_pos) = self.scroll_opt {
|
||||||
|
self.viewport_rect = Some(Rectangle {
|
||||||
|
x: viewport.x - scroll_pos.x,
|
||||||
|
y: viewport.y - scroll_pos.y,
|
||||||
|
width: viewport.width,
|
||||||
|
height: viewport.height
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
self.viewport_rect = Some(viewport);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Message::DragEnd(_) => {
|
Message::DragEnd(_) => {
|
||||||
self.clicked = None;
|
self.clicked = None;
|
||||||
|
self.virtual_cursor_offset = None;
|
||||||
|
self.last_scroll_offset = None;
|
||||||
|
self.last_scroll_position = None;
|
||||||
|
|
||||||
|
self.current_drag_rect = None;
|
||||||
if let Some(ref mut items) = self.items_opt {
|
if let Some(ref mut items) = self.items_opt {
|
||||||
for item in items.iter_mut() {
|
for item in items.iter_mut() {
|
||||||
item.overlaps_drag_rect = false;
|
item.overlaps_drag_rect = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
commands.push(Command::AutoScroll(None));
|
||||||
}
|
}
|
||||||
Message::DoubleClick(click_i_opt) => {
|
Message::DoubleClick(click_i_opt) => {
|
||||||
if let Some(clicked_item) = self
|
if let Some(clicked_item) = self
|
||||||
|
|
@ -2433,6 +2558,7 @@ impl Tab {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Message::Drag(rect_opt) => {
|
Message::Drag(rect_opt) => {
|
||||||
|
self.current_drag_rect = rect_opt;
|
||||||
if let Some(rect) = rect_opt {
|
if let Some(rect) = rect_opt {
|
||||||
self.context_menu = None;
|
self.context_menu = None;
|
||||||
self.location_context_menu_index = None;
|
self.location_context_menu_index = None;
|
||||||
|
|
@ -2835,6 +2961,14 @@ impl Tab {
|
||||||
Message::Scroll(viewport) => {
|
Message::Scroll(viewport) => {
|
||||||
self.scroll_opt = Some(viewport.absolute_offset());
|
self.scroll_opt = Some(viewport.absolute_offset());
|
||||||
}
|
}
|
||||||
|
Message::ScrollTab(scroll_speed) => {
|
||||||
|
commands.push(Command::Iced(
|
||||||
|
scrollable::scroll_by(self.scrollable_id.clone(), AbsoluteOffset {
|
||||||
|
x: 0.0,
|
||||||
|
y: scroll_speed
|
||||||
|
}).into(),
|
||||||
|
));
|
||||||
|
}
|
||||||
Message::ScrollToFocus => {
|
Message::ScrollToFocus => {
|
||||||
if let Some(offset) = self.select_focus_scroll() {
|
if let Some(offset) = self.select_focus_scroll() {
|
||||||
commands.push(Command::Iced(
|
commands.push(Command::Iced(
|
||||||
|
|
@ -4104,6 +4238,8 @@ impl Tab {
|
||||||
.on_drag_end(|_| Message::DragEnd(None))
|
.on_drag_end(|_| Message::DragEnd(None))
|
||||||
.show_drag_rect(true)
|
.show_drag_rect(true)
|
||||||
.on_release(|_| Message::ClickRelease(None))
|
.on_release(|_| Message::ClickRelease(None))
|
||||||
|
.on_resize(Message::MouseAreaResized)
|
||||||
|
.cursor_offset(self.virtual_cursor_offset)
|
||||||
.into(),
|
.into(),
|
||||||
true,
|
true,
|
||||||
)
|
)
|
||||||
|
|
@ -4434,6 +4570,8 @@ impl Tab {
|
||||||
.on_drag_end(|_| Message::DragEnd(None))
|
.on_drag_end(|_| Message::DragEnd(None))
|
||||||
.show_drag_rect(true)
|
.show_drag_rect(true)
|
||||||
.on_release(|_| Message::ClickRelease(None))
|
.on_release(|_| Message::ClickRelease(None))
|
||||||
|
.on_resize(Message::MouseAreaResized)
|
||||||
|
.cursor_offset(self.virtual_cursor_offset)
|
||||||
.into(),
|
.into(),
|
||||||
true,
|
true,
|
||||||
)
|
)
|
||||||
|
|
@ -4497,7 +4635,7 @@ impl Tab {
|
||||||
.on_press(move |_point_opt| Message::Click(None))
|
.on_press(move |_point_opt| Message::Click(None))
|
||||||
.on_release(|_| Message::ClickRelease(None))
|
.on_release(|_| Message::ClickRelease(None))
|
||||||
//TODO: better way to keep focused item in view
|
//TODO: better way to keep focused item in view
|
||||||
.on_resize(|_| Message::ScrollToFocus)
|
.on_resize(|_, _| Message::ScrollToFocus)
|
||||||
.on_back_press(move |_point_opt| Message::GoPrevious)
|
.on_back_press(move |_point_opt| Message::GoPrevious)
|
||||||
.on_forward_press(move |_point_opt| Message::GoNext)
|
.on_forward_press(move |_point_opt| Message::GoNext)
|
||||||
.on_scroll(respond_to_scroll_direction);
|
.on_scroll(respond_to_scroll_direction);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue