diff --git a/src/app.rs b/src/app.rs index cf1991d..d5cba63 100644 --- a/src/app.rs +++ b/src/app.rs @@ -15,7 +15,6 @@ use cosmic::iced::{ #[cfg(feature = "wayland")] use cosmic::iced_winit::commands::overlap_notify::overlap_notify; use cosmic::{ - action, app::{self, context_drawer, Core, Task}, cosmic_config, cosmic_theme, executor, iced::{ diff --git a/src/dialog.rs b/src/dialog.rs index 713da9e..5437284 100644 --- a/src/dialog.rs +++ b/src/dialog.rs @@ -2,13 +2,13 @@ // SPDX-License-Identifier: GPL-3.0-only use cosmic::{ - app::{self, context_drawer, cosmic::Cosmic, Core, Task}, + app::{context_drawer, cosmic::Cosmic, Core, Task}, cosmic_config, cosmic_theme, executor, iced::{ - event, + self, event, futures::{self, SinkExt}, keyboard::{Event as KeyEvent, Key, Modifiers}, - stream, window, Alignment, Event, Length, Size, Subscription, + mouse, stream, window, Alignment, Event, Length, Point, Size, Subscription, }, theme, widget::{ @@ -375,6 +375,7 @@ enum Message { Cancel, Choice(usize, usize), Config(Config), + CursorMoved(Point), DialogCancel, DialogComplete, DialogUpdate(DialogPage), @@ -389,6 +390,7 @@ enum Message { Open, Preview, Save(bool), + ScrollTab(i16), SearchActivate, SearchClear, SearchInput(String), @@ -409,6 +411,7 @@ impl From for Message { AppMessage::None => Message::None, AppMessage::Preview(_entity_opt) => Message::Preview, AppMessage::SearchActivate => Message::SearchActivate, + AppMessage::ScrollTab(scroll_speed) => Message::ScrollTab(scroll_speed), AppMessage::TabMessage(_entity_opt, tab_message) => Message::TabMessage(tab_message), AppMessage::TabView(_entity_opt, view) => Message::TabView(view), AppMessage::ToggleFoldersFirst => Message::ToggleFoldersFirst, @@ -469,6 +472,7 @@ struct App { tab: Tab, key_binds: HashMap, watcher_opt: Option<(Debouncer, HashSet)>, + auto_scroll_speed: Option, } impl App { @@ -896,6 +900,7 @@ impl Application for App { tab, key_binds, watcher_opt: None, + auto_scroll_speed: None, }; let commands = Task::batch([ @@ -1214,6 +1219,9 @@ impl Application for App { return self.update_config(); } } + Message::CursorMoved(pos) => { + return self.update(Message::TabMessage(tab::Message::CursorMoved(pos))); + } Message::DialogCancel => { self.dialog_pages.pop_front(); } @@ -1480,6 +1488,11 @@ impl Application for App { } } } + Message::ScrollTab(scroll_speed) => { + return self.update(Message::TabMessage(tab::Message::ScrollTab( + (scroll_speed as f32) / 10.0, + ))); + } Message::SearchActivate => { return if self.search_get().is_none() { self.search_set(Some(String::new())) @@ -1545,6 +1558,15 @@ impl Application for App { tab::Command::WindowToggleMaximize => { commands.push(window::toggle_maximize(self.flags.window_id)); } + 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; + } + } unsupported => { log::warn!("{unsupported:?} not supported in dialog mode"); } @@ -1728,6 +1750,9 @@ impl Application for App { Event::Keyboard(KeyEvent::ModifiersChanged(modifiers)) => { Some(Message::Modifiers(modifiers)) } + Event::Mouse(mouse::Event::CursorMoved { position: pos }) => { + Some(Message::CursorMoved(pos)) + } _ => None, }), Config::subscription().map(|update| { @@ -1825,6 +1850,14 @@ impl Application for App { .map(Message::TabMessage), ]; + 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() { subscriptions.push( mounter.subscription().with(*key).map( diff --git a/src/mouse_area.rs b/src/mouse_area.rs index f26de40..d263b34 100644 --- a/src/mouse_area.rs +++ b/src/mouse_area.rs @@ -49,7 +49,6 @@ 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,11 +184,6 @@ 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 { @@ -221,8 +215,6 @@ impl<'a, Message, F> OnEnterExit<'a, Message> for F where F: Fn() -> Message + ' struct State { last_position: Option, last_virtual_position: Option, - last_in_bounds_position: Option, - last_cursor_offset: Option, drag_initiated: Option, modifiers: Modifiers, prev_click: Option<(mouse::Click, Instant)>, @@ -300,7 +292,6 @@ impl<'a, Message> MouseArea<'a, Message> { on_exit: None, on_scroll: None, show_drag_rect: false, - cursor_offset: None, } } } @@ -431,6 +422,7 @@ where let mut bg_color = cosmic.accent_color(); //TODO: get correct alpha bg_color.alpha = 0.2; + renderer.start_layer(*viewport); renderer.fill_quad( Quad { bounds, @@ -443,6 +435,7 @@ where }, Color::from(bg_color), ); + renderer.end_layer(); } } } @@ -515,65 +508,7 @@ fn update( } } - // check if offset differs to calculate virtual position - let mut need_to_recalculate = false; - match (state.last_cursor_offset, widget.cursor_offset) { - // check if offset has changed between updates - (Some(last_cursor_offset), Some(cursor_offset)) => { - if last_cursor_offset != cursor_offset { - state.last_cursor_offset = Some(cursor_offset); - need_to_recalculate = true; - } - } - - // 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 - // (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); - } - } - } - - if let Event::Mouse(mouse::Event::CursorMoved { .. }) = event { + if let Event::Mouse(mouse::Event::CursorMoved { position }) = event { let position_in = cursor.position_in(layout_bounds); match (position_in, state.last_position) { (None, Some(_)) => { @@ -590,10 +525,10 @@ fn update( } state.last_position = position_in; - // 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); - } + state.last_virtual_position = Some(Point::new( + viewport.x - layout_bounds.x + position.x, + viewport.y - layout_bounds.y + position.y, + )); } if state.drag_initiated.is_none() && !cursor.is_over(layout_bounds) { diff --git a/src/tab.rs b/src/tab.rs index 0e0ece4..ca03a5f 100644 --- a/src/tab.rs +++ b/src/tab.rs @@ -1160,7 +1160,6 @@ pub enum Message { DoubleClick(Option), ClickRelease(Option), CursorMoved(Point), - MouseAreaResized(Size, Rectangle), DragEnd(Option), Config(TabConfig), ContextAction(Action), @@ -1824,10 +1823,10 @@ pub struct Tab { search_context: Option, global_cursor_position: Option, current_drag_rect: Option, - viewport_rect: Option, virtual_cursor_offset: Option, last_scroll_position: Option, last_scroll_offset: Option, + scroll_bounds_opt: Option, } fn calculate_dir_size(path: &Path, controller: Controller) -> Result { @@ -1914,10 +1913,10 @@ impl Tab { 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, + scroll_bounds_opt: None, } } @@ -2217,6 +2216,7 @@ impl Tab { self.items_opt = None; //TODO: remember scroll by location? self.scroll_opt = None; + self.scroll_bounds_opt = None; self.select_focus = None; self.search_context = None; if let Some(history_i) = history_i_opt { @@ -2281,21 +2281,23 @@ impl Tab { // 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 + if let Some(scroll_bounds) = self.scroll_bounds_opt { + if !scroll_bounds.contains(pos) { + if pos.y < scroll_bounds.y + || pos.y > (scroll_bounds.y + scroll_bounds.height) + { + // if our mouse is above the scroll bounds, we want to scroll up let drag_start_point = Point { - x: viewport.x, - y: viewport.y, + x: scroll_bounds.x, + y: scroll_bounds.y, }; // diff_y should be NEGATIVE here when close to y=0 (above the MouseArea) - // and positive when below the viewport + // and positive when below the scroll bounds 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 + -DRAG_SCROLL_DISTANCE } else { 0.0 }; @@ -2334,20 +2336,6 @@ impl Tab { } } } - 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(_) => { self.clicked = None; self.virtual_cursor_offset = None; @@ -2604,11 +2592,15 @@ impl Tab { } } Message::Drag(rect_opt) => { - self.current_drag_rect = rect_opt; + if self.mode.multiple() { + self.current_drag_rect = rect_opt; + } if let Some(rect) = rect_opt { self.context_menu = None; self.location_context_menu_index = None; - self.select_rect(rect, mod_ctrl, mod_shift); + if self.mode.multiple() { + self.select_rect(rect, mod_ctrl, mod_shift); + } if self.select_focus.take().is_some() { // Unfocus currently focused button commands.push(Command::Iced( @@ -3005,6 +2997,7 @@ impl Tab { } Message::Scroll(viewport) => { + self.scroll_bounds_opt = Some(viewport.bounds()); self.scroll_opt = Some(viewport.absolute_offset()); } Message::ScrollTab(scroll_speed) => { @@ -4312,10 +4305,8 @@ impl Tab { .on_press(|_| Message::Click(None)) .on_drag(Message::Drag) .on_drag_end(|_| Message::DragEnd(None)) - .show_drag_rect(true) + .show_drag_rect(self.mode.multiple()) .on_release(|_| Message::ClickRelease(None)) - .on_resize(Message::MouseAreaResized) - .cursor_offset(self.virtual_cursor_offset) .into(), true, ) @@ -4647,10 +4638,8 @@ impl Tab { .on_press(|_| Message::Click(None)) .on_drag(Message::Drag) .on_drag_end(|_| Message::DragEnd(None)) - .show_drag_rect(true) + .show_drag_rect(self.mode.multiple()) .on_release(|_| Message::ClickRelease(None)) - .on_resize(Message::MouseAreaResized) - .cursor_offset(self.virtual_cursor_offset) .into(), true, )