From 7c6df15f08cc81a1d77571e8cc296f066820b638 Mon Sep 17 00:00:00 2001 From: ellieplayswow <164806095+ellieplayswow@users.noreply.github.com> Date: Mon, 17 Feb 2025 23:52:14 +0000 Subject: [PATCH 1/8] Adding in new functionality to scroll the file window when dragging outside the viewport of the scrollable --- src/app.rs | 9 ++++++++- src/mouse_area.rs | 8 +++++--- src/tab.rs | 46 +++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 58 insertions(+), 5 deletions(-) diff --git a/src/app.rs b/src/app.rs index f348e56..0651bbe 100644 --- a/src/app.rs +++ b/src/app.rs @@ -54,6 +54,7 @@ use std::{ sync::{Arc, Mutex}, time::{self, Instant}, }; +use cosmic::iced::mouse::Event::CursorMoved; use tokio::sync::mpsc; use trash::TrashItem; #[cfg(feature = "wayland")] @@ -277,6 +278,7 @@ pub enum Message { Config(Config), Copy(Option), CosmicSettings(&'static str), + CursorMoved(Point), Cut(Option), DesktopConfig(DesktopConfig), DesktopViewOptions, @@ -1926,6 +1928,10 @@ impl Application for App { let contents = ClipboardCopy::new(ClipboardKind::Copy, &paths); 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) => { let paths = self.selected_paths(entity_opt); let contents = ClipboardCopy::new(ClipboardKind::Cut, &paths); @@ -4536,7 +4542,8 @@ impl Application for App { } _ => None, } - } + }, + Event::Mouse(CursorMoved { position: pos }) => Some(Message::CursorMoved(pos)), _ => None, }), Config::subscription().map(|update| { diff --git a/src/mouse_area.rs b/src/mouse_area.rs index 164cac7..4ab6c05 100644 --- a/src/mouse_area.rs +++ b/src/mouse_area.rs @@ -199,8 +199,8 @@ impl<'a, Message, F> OnMouseButton<'a, Message> for F where F: Fn(Option) pub trait OnDrag<'a, Message>: Fn(Option) -> Message + 'a {} impl<'a, Message, F> OnDrag<'a, Message> for F where F: Fn(Option) -> Message + 'a {} -pub trait OnResize<'a, Message>: Fn(Size) -> Message + 'a {} -impl<'a, Message, F> OnResize<'a, Message> for F where F: 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, Rectangle) -> Message + 'a {} pub trait OnScroll<'a, Message>: Fn(mouse::ScrollDelta, Modifiers) -> Option + 'a {} impl<'a, Message, F> OnScroll<'a, Message> for F where @@ -374,6 +374,7 @@ where cursor, shell, tree.state.downcast_mut::(), + viewport, ) } @@ -493,6 +494,7 @@ fn update( cursor: mouse::Cursor, shell: &mut Shell<'_, Message>, state: &mut State, + viewport: &Rectangle ) -> event::Status { let layout_bounds = layout.bounds(); @@ -500,7 +502,7 @@ fn update( let size = layout_bounds.size(); if state.size != Some(size) { state.size = Some(size); - shell.publish(message(size)); + shell.publish(message(size, *viewport)); } } diff --git a/src/tab.rs b/src/tab.rs index ef33a97..1254147 100644 --- a/src/tab.rs +++ b/src/tab.rs @@ -83,6 +83,8 @@ const MAX_SEARCH_RESULTS: usize = 200; //TODO: configurable thumbnail size? const THUMBNAIL_SIZE: u32 = (ICON_SIZE_GRID as u32) * (ICON_SCALE_MAX as u32); +const DRAG_SCROLL_DISTANCE: u8 = 5; + //TODO: adjust for locales? const DATE_TIME_FORMAT: &str = "%b %-d, %-Y, %-I:%M %p"; const TIME_FORMAT: &str = "%-I:%M %p"; @@ -1121,6 +1123,8 @@ pub enum Message { Click(Option), DoubleClick(Option), ClickRelease(Option), + CursorMoved(Point), + MouseAreaResized(Size, Rectangle), DragEnd(Option), Config(TabConfig), ContextAction(Action), @@ -1772,6 +1776,9 @@ pub struct Tab { selected_clicked: bool, last_right_click: Option, search_context: Option, + global_cursor_position: Option, + current_drag_rect: Option, + viewport_rect: Option, } fn calculate_dir_size(path: &Path, controller: Controller) -> Result { @@ -1856,6 +1863,9 @@ impl Tab { selected_clicked: false, last_right_click: None, search_context: None, + global_cursor_position: None, + current_drag_rect: None, + viewport_rect: None, } } @@ -2199,8 +2209,40 @@ 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) { + // 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 - viewport.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( + scrollable::scroll_by(self.scrollable_id.clone(), AbsoluteOffset { + x: 0.0, + y: scroll_y as f32 + }).into(), + )); + } + } + } + } + Message::MouseAreaResized(_size, viewport) => { + self.viewport_rect = Some(viewport); + } Message::DragEnd(_) => { self.clicked = None; + self.current_drag_rect = None; if let Some(ref mut items) = self.items_opt { for item in items.iter_mut() { item.overlaps_drag_rect = false; @@ -2433,6 +2475,7 @@ impl Tab { } } Message::Drag(rect_opt) => { + self.current_drag_rect = rect_opt; if let Some(rect) = rect_opt { self.context_menu = None; self.location_context_menu_index = None; @@ -4434,6 +4477,7 @@ impl Tab { .on_drag_end(|_| Message::DragEnd(None)) .show_drag_rect(true) .on_release(|_| Message::ClickRelease(None)) + .on_resize(Message::MouseAreaResized) .into(), true, ) @@ -4497,7 +4541,7 @@ impl Tab { .on_press(move |_point_opt| Message::Click(None)) .on_release(|_| Message::ClickRelease(None)) //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_forward_press(move |_point_opt| Message::GoNext) .on_scroll(respond_to_scroll_direction); From df74f085ce54d54532662855845e1b8b278c8f31 Mon Sep 17 00:00:00 2001 From: ellieplayswow <164806095+ellieplayswow@users.noreply.github.com> Date: Tue, 18 Feb 2025 00:07:22 +0000 Subject: [PATCH 2/8] Adding in resize listener for gridview --- src/tab.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tab.rs b/src/tab.rs index 1254147..f6b68e3 100644 --- a/src/tab.rs +++ b/src/tab.rs @@ -4147,6 +4147,7 @@ impl Tab { .on_drag_end(|_| Message::DragEnd(None)) .show_drag_rect(true) .on_release(|_| Message::ClickRelease(None)) + .on_resize(Message::MouseAreaResized) .into(), true, ) From 23596a16468de4a047b48ab213bfbebe5126564e Mon Sep 17 00:00:00 2001 From: ellieplayswow <164806095+ellieplayswow@users.noreply.github.com> Date: Tue, 18 Feb 2025 11:54:39 +0000 Subject: [PATCH 3/8] Implementing 'virtual cursor offset' to help track & dynamically update drag rect --- src/mouse_area.rs | 47 ++++++++++++++++++++++++++++++++- src/tab.rs | 67 ++++++++++++++++++++++++++++++++++++----------- 2 files changed, 98 insertions(+), 16 deletions(-) diff --git a/src/mouse_area.rs b/src/mouse_area.rs index 4ab6c05..22fe1bc 100644 --- a/src/mouse_area.rs +++ b/src/mouse_area.rs @@ -50,6 +50,7 @@ pub struct MouseArea<'a, Message> { on_enter: Option>>, on_exit: Option>>, show_drag_rect: bool, + cursor_offset: Option } impl<'a, Message> MouseArea<'a, Message> { @@ -185,6 +186,11 @@ impl<'a, Message> MouseArea<'a, Message> { self } + pub fn cursor_offset(mut self, offset: Option) -> Self { + self.cursor_offset = offset; + self + } + /// Sets the widget's unique identifier. #[must_use] 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)] struct State { last_position: Option, + last_virtual_position: Option, + last_in_bounds_position: Option, drag_initiated: Option, modifiers: Modifiers, prev_click: Option<(mouse::Click, Instant)>, @@ -224,7 +232,7 @@ struct State { impl State { fn drag_rect(&self, cursor: mouse::Cursor) -> Option { 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 { let min_x = drag_source.x.min(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_scroll: None, show_drag_rect: false, + cursor_offset: None } } } @@ -522,6 +531,42 @@ fn update( _ => {} } 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) { diff --git a/src/tab.rs b/src/tab.rs index f6b68e3..c135f15 100644 --- a/src/tab.rs +++ b/src/tab.rs @@ -83,7 +83,7 @@ const MAX_SEARCH_RESULTS: usize = 200; //TODO: configurable thumbnail size? 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? const DATE_TIME_FORMAT: &str = "%b %-d, %-Y, %-I:%M %p"; @@ -1779,6 +1779,7 @@ pub struct Tab { global_cursor_position: Option, current_drag_rect: Option, viewport_rect: Option, + virtual_cursor_offset: Option, } fn calculate_dir_size(path: &Path, controller: Controller) -> Result { @@ -1866,6 +1867,7 @@ impl Tab { global_cursor_position: None, current_drag_rect: None, viewport_rect: None, + virtual_cursor_offset: None } } @@ -2216,23 +2218,56 @@ impl Tab { if self.current_drag_rect.is_some() { if let Some(viewport) = self.viewport_rect { if !viewport.contains(pos) { - // 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 - viewport.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 - }; + 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: 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( - scrollable::scroll_by(self.scrollable_id.clone(), AbsoluteOffset { + let mut new_offset = Point { x: 0.0, 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) .on_release(|_| Message::ClickRelease(None)) .on_resize(Message::MouseAreaResized) + .cursor_offset(self.virtual_cursor_offset) .into(), true, ) @@ -4479,6 +4515,7 @@ impl Tab { .show_drag_rect(true) .on_release(|_| Message::ClickRelease(None)) .on_resize(Message::MouseAreaResized) + .cursor_offset(self.virtual_cursor_offset) .into(), true, ) From 4a3b95e615605a9c918229cb76507b57b0d551d1 Mon Sep 17 00:00:00 2001 From: ellieplayswow <164806095+ellieplayswow@users.noreply.github.com> Date: Tue, 18 Feb 2025 13:14:40 +0000 Subject: [PATCH 4/8] Fixing some jankiness by decoupling scroll offset vs 'virtual' cursor position & resetting after drag end --- src/tab.rs | 41 +++++++++++++++++++++++++++++++++++------ 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/src/tab.rs b/src/tab.rs index c135f15..5e6fdcf 100644 --- a/src/tab.rs +++ b/src/tab.rs @@ -1780,6 +1780,8 @@ pub struct Tab { current_drag_rect: Option, viewport_rect: Option, virtual_cursor_offset: Option, + last_scroll_position: Option, + last_scroll_offset: Option, } fn calculate_dir_size(path: &Path, controller: Controller) -> Result { @@ -1867,7 +1869,9 @@ impl Tab { global_cursor_position: None, current_drag_rect: None, viewport_rect: None, - virtual_cursor_offset: None + virtual_cursor_offset: None, + last_scroll_position: None, + last_scroll_offset: None } } @@ -2248,6 +2252,7 @@ impl Tab { } self.virtual_cursor_offset = Some(new_offset); + self.last_scroll_offset = Some(new_offset); commands.push(Command::Iced( scrollable::scroll_by(self.scrollable_id.clone(), AbsoluteOffset { @@ -2257,17 +2262,37 @@ impl Tab { )); } else { - if self.virtual_cursor_offset.is_none() { - self.virtual_cursor_offset = Some(Point { - x: 0.0, - y: 0.0 - }); + 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 + }); + } + } } } 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; } } } @@ -2277,6 +2302,10 @@ impl Tab { } Message::DragEnd(_) => { 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 { for item in items.iter_mut() { From e774ea17741c15275a6e86b1ed97c89d148c6024 Mon Sep 17 00:00:00 2001 From: ellieplayswow <164806095+ellieplayswow@users.noreply.github.com> Date: Tue, 18 Feb 2025 13:32:54 +0000 Subject: [PATCH 5/8] Fixing issue where swapping between list & grid view 'desync' the virtual cursor --- src/tab.rs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/tab.rs b/src/tab.rs index 5e6fdcf..255582f 100644 --- a/src/tab.rs +++ b/src/tab.rs @@ -2298,7 +2298,20 @@ impl Tab { } } Message::MouseAreaResized(_size, viewport) => { - self.viewport_rect = Some(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(_) => { self.clicked = None; From 169d8ef36562041ac16fc93e7d2f1330a127c8ad Mon Sep 17 00:00:00 2001 From: ellieplayswow <164806095+ellieplayswow@users.noreply.github.com> Date: Tue, 18 Feb 2025 13:58:00 +0000 Subject: [PATCH 6/8] Fixing issue with x-axis desyncing --- src/tab.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/tab.rs b/src/tab.rs index 255582f..78daf42 100644 --- a/src/tab.rs +++ b/src/tab.rs @@ -2244,13 +2244,17 @@ impl Tab { y: scroll_y as f32 }; - if let Some(offset) = self.virtual_cursor_offset { + if let Some(virtual_cursor_offset) = self.virtual_cursor_offset { new_offset = Point { - x: new_offset.x + offset.x, - y: new_offset.y + offset.y, + 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); From 9d60ca1564d7eb78d0e298af0f2cbe761aa427b8 Mon Sep 17 00:00:00 2001 From: ellieplayswow <164806095+ellieplayswow@users.noreply.github.com> Date: Wed, 19 Feb 2025 11:29:19 +0000 Subject: [PATCH 7/8] Moving autoscroll to a subscription on fixed timer + calculating speed based off of distance to edge --- src/app.rs | 18 ++++++++++++++++++ src/tab.rs | 56 +++++++++++++++++++++++++++++++++++++++++------------- 2 files changed, 61 insertions(+), 13 deletions(-) diff --git a/src/app.rs b/src/app.rs index 0651bbe..5bce184 100644 --- a/src/app.rs +++ b/src/app.rs @@ -18,6 +18,7 @@ use cosmic::{ app::{self, context_drawer, message, Core, Task}, cosmic_config, cosmic_theme, executor, iced::{ + self, clipboard::dnd::DndAction, event, futures::{self, SinkExt}, @@ -332,6 +333,7 @@ pub enum Message { Rename(Option), ReplaceResult(ReplaceResult), RestoreFromTrash(Option), + ScrollTab(f32), SearchActivate, SearchClear, SearchInput(String), @@ -550,6 +552,7 @@ pub struct App { tab_dnd_hover: Option<(Entity, Instant)>, nav_drag_id: DragId, tab_drag_id: DragId, + auto_scroll_speed: Option } impl App { @@ -1628,6 +1631,7 @@ impl Application for App { tab_dnd_hover: None, nav_drag_id: DragId::new(), tab_drag_id: DragId::new(), + auto_scroll_speed: None, }; let mut commands = vec![app.update_config()]; @@ -2794,6 +2798,10 @@ impl Application for App { 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))); + } Message::SearchActivate => { return if self.search_get().is_none() { self.search_set_active(Some(String::new())) @@ -2931,6 +2939,9 @@ impl Application for App { config_set!(favorites, favorites); commands.push(self.update_config()); } + tab::Command::AutoScroll(scroll_speed) => { + self.auto_scroll_speed = scroll_speed; + } tab::Command::ChangeLocation(tab_title, tab_path, selection_paths) => { self.activate_nav_model_location(&tab_path); @@ -4714,6 +4725,13 @@ impl Application for App { ), ]; + if let Some(scroll_speed) = self.auto_scroll_speed { + subscriptions.push( + iced::time::every(time::Duration::from_millis(10)) + .map(move |_| Message::ScrollTab(scroll_speed)) + ); + } + for (key, mounter) in MOUNTERS.iter() { subscriptions.push( mounter.subscription().with(*key).map( diff --git a/src/tab.rs b/src/tab.rs index 78daf42..71a1304 100644 --- a/src/tab.rs +++ b/src/tab.rs @@ -83,7 +83,8 @@ const MAX_SEARCH_RESULTS: usize = 200; //TODO: configurable thumbnail size? const THUMBNAIL_SIZE: u32 = (ICON_SIZE_GRID as u32) * (ICON_SCALE_MAX as u32); -const DRAG_SCROLL_DISTANCE: u8 = 1; +const DRAG_SCROLL_DISTANCE: f32 = 15.0; +const DRAG_SCROLL_RATIO_MAXIMUM: f32 = 3.0; //TODO: adjust for locales? const DATE_TIME_FORMAT: &str = "%b %-d, %-Y, %-I:%M %p"; @@ -1100,6 +1101,7 @@ pub enum Command { Action(Action), AddNetworkDrive, AddToSidebar(PathBuf), + AutoScroll(Option), ChangeLocation(String, Location, Option>), DropFiles(PathBuf, ClipboardPaste), EmptyTrash, @@ -1157,6 +1159,7 @@ pub enum Message { RightClick(Option), MiddleClick(usize), Scroll(Viewport), + ScrollTab(f32), ScrollToFocus, SearchContext(Location, SearchContextWrapper), SearchReady(bool), @@ -2231,17 +2234,36 @@ impl Tab { // 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: i8 = if diff_y > 0.0 { - DRAG_SCROLL_DISTANCE as i8 + let mut scroll_y: f32 = if diff_y > 0.0 { + DRAG_SCROLL_DISTANCE } else if diff_y < 0.0 { - DRAG_SCROLL_DISTANCE as i8 * -1 + DRAG_SCROLL_DISTANCE * -1.0 } else { - 0 + 0.0 }; + + // estimate distance and use that to control speed + // go up to 3x speed + let quarter_height = viewport.height / 4.0; + let cursor_y_distance = if diff_y > 0.0 { + pos.y - (viewport.y + viewport.height) + } else if diff_y < 0.0 { + pos.y - viewport.y + } else { + 0.0 + }.abs(); + + let mut speed_ratio = (cursor_y_distance / quarter_height) + 1.0; + if speed_ratio > DRAG_SCROLL_RATIO_MAXIMUM { + speed_ratio = DRAG_SCROLL_RATIO_MAXIMUM; + } + + scroll_y = scroll_y * speed_ratio; + let mut new_offset = Point { x: 0.0, - y: scroll_y as f32 + y: scroll_y }; if let Some(virtual_cursor_offset) = self.virtual_cursor_offset { @@ -2258,12 +2280,7 @@ impl Tab { self.virtual_cursor_offset = Some(new_offset); self.last_scroll_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(), - )); + commands.push(Command::AutoScroll(Some(scroll_y))); } else { if let Some(last_scroll_offset) = self.last_scroll_offset { @@ -2288,8 +2305,9 @@ impl Tab { y: 0.0 }); } - } + + commands.push(Command::AutoScroll(None)); } } else { @@ -2297,6 +2315,8 @@ impl Tab { self.virtual_cursor_offset = None; self.last_scroll_position = Some(pos); self.last_scroll_offset = None; + + commands.push(Command::AutoScroll(None)); } } } @@ -2329,6 +2349,8 @@ impl Tab { item.overlaps_drag_rect = false; } } + + commands.push(Command::AutoScroll(None)); } Message::DoubleClick(click_i_opt) => { if let Some(clicked_item) = self @@ -2959,6 +2981,14 @@ impl Tab { Message::Scroll(viewport) => { 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 => { if let Some(offset) = self.select_focus_scroll() { commands.push(Command::Iced( From 1e5123af17907b3f8c8db0a6a6959b016cbb151e Mon Sep 17 00:00:00 2001 From: ellieplayswow <164806095+ellieplayswow@users.noreply.github.com> Date: Wed, 19 Feb 2025 19:30:40 +0000 Subject: [PATCH 8/8] Fixing issue with scroll speed subscription not working in dev profile, removing speed code as redundant --- src/app.rs | 20 ++++++++++++++------ src/tab.rs | 24 ++---------------------- 2 files changed, 16 insertions(+), 28 deletions(-) diff --git a/src/app.rs b/src/app.rs index 5bce184..1aca306 100644 --- a/src/app.rs +++ b/src/app.rs @@ -333,7 +333,7 @@ pub enum Message { Rename(Option), ReplaceResult(ReplaceResult), RestoreFromTrash(Option), - ScrollTab(f32), + ScrollTab(i16), SearchActivate, SearchClear, SearchInput(String), @@ -552,7 +552,7 @@ pub struct App { tab_dnd_hover: Option<(Entity, Instant)>, nav_drag_id: DragId, tab_drag_id: DragId, - auto_scroll_speed: Option + auto_scroll_speed: Option } impl App { @@ -2800,7 +2800,7 @@ impl Application for App { } Message::ScrollTab(scroll_speed) => { let entity = self.tab_model.active(); - return self.update(Message::TabMessage(Some(entity), tab::Message::ScrollTab(scroll_speed))); + return self.update(Message::TabMessage(Some(entity), tab::Message::ScrollTab((scroll_speed as f32) / 10.0))); } Message::SearchActivate => { return if self.search_get().is_none() { @@ -2940,7 +2940,15 @@ impl Application for App { commands.push(self.update_config()); } tab::Command::AutoScroll(scroll_speed) => { - self.auto_scroll_speed = 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) => { self.activate_nav_model_location(&tab_path); @@ -4727,8 +4735,8 @@ impl Application for App { if let Some(scroll_speed) = self.auto_scroll_speed { subscriptions.push( - iced::time::every(time::Duration::from_millis(10)) - .map(move |_| Message::ScrollTab(scroll_speed)) + iced::time::every(time::Duration::from_millis(10)).with(scroll_speed) + .map(|(scroll_speed, _)| Message::ScrollTab(scroll_speed)) ); } diff --git a/src/tab.rs b/src/tab.rs index 71a1304..f0a1071 100644 --- a/src/tab.rs +++ b/src/tab.rs @@ -84,7 +84,6 @@ const MAX_SEARCH_RESULTS: usize = 200; const THUMBNAIL_SIZE: u32 = (ICON_SIZE_GRID as u32) * (ICON_SCALE_MAX as u32); const DRAG_SCROLL_DISTANCE: f32 = 15.0; -const DRAG_SCROLL_RATIO_MAXIMUM: f32 = 3.0; //TODO: adjust for locales? const DATE_TIME_FORMAT: &str = "%b %-d, %-Y, %-I:%M %p"; @@ -2234,7 +2233,7 @@ impl Tab { // 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 mut scroll_y: f32 = if diff_y > 0.0 { + let scroll_y: f32 = if diff_y > 0.0 { DRAG_SCROLL_DISTANCE } else if diff_y < 0.0 { DRAG_SCROLL_DISTANCE * -1.0 @@ -2242,25 +2241,6 @@ impl Tab { 0.0 }; - - // estimate distance and use that to control speed - // go up to 3x speed - let quarter_height = viewport.height / 4.0; - let cursor_y_distance = if diff_y > 0.0 { - pos.y - (viewport.y + viewport.height) - } else if diff_y < 0.0 { - pos.y - viewport.y - } else { - 0.0 - }.abs(); - - let mut speed_ratio = (cursor_y_distance / quarter_height) + 1.0; - if speed_ratio > DRAG_SCROLL_RATIO_MAXIMUM { - speed_ratio = DRAG_SCROLL_RATIO_MAXIMUM; - } - - scroll_y = scroll_y * speed_ratio; - let mut new_offset = Point { x: 0.0, y: scroll_y @@ -2274,7 +2254,7 @@ impl Tab { } if let Some(last_scroll_position) = self.last_scroll_position { - new_offset.x = (pos.x - last_scroll_position.x); + new_offset.x = pos.x - last_scroll_position.x; } self.virtual_cursor_offset = Some(new_offset);