Adding in new functionality to scroll the file window when dragging outside the viewport of the scrollable

This commit is contained in:
ellieplayswow 2025-02-17 23:52:14 +00:00
parent a092aa2383
commit 7c6df15f08
3 changed files with 58 additions and 5 deletions

View file

@ -54,6 +54,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 +278,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,
@ -1926,6 +1928,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);
@ -4536,7 +4542,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| {

View file

@ -199,8 +199,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
@ -374,6 +374,7 @@ where
cursor, cursor,
shell, shell,
tree.state.downcast_mut::<State>(), tree.state.downcast_mut::<State>(),
viewport,
) )
} }
@ -493,6 +494,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 +502,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));
} }
} }

View file

@ -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: u8 = 5;
//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";
@ -1121,6 +1123,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),
@ -1772,6 +1776,9 @@ 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>,
} }
fn calculate_dir_size(path: &Path, controller: Controller) -> Result<u64, String> { fn calculate_dir_size(path: &Path, controller: Controller) -> Result<u64, String> {
@ -1856,6 +1863,9 @@ 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,
} }
} }
@ -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(_) => { Message::DragEnd(_) => {
self.clicked = None; self.clicked = 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;
@ -2433,6 +2475,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;
@ -4434,6 +4477,7 @@ 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)
.into(), .into(),
true, true,
) )
@ -4497,7 +4541,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);