From fb74be9a705f528f0b50bc6701e16047173733de Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Fri, 15 Aug 2025 11:01:08 -0600 Subject: [PATCH] Restore tab scroll position, fixes #1115 --- src/app.rs | 46 +++++++++++++++++++++++++++++++++++----------- src/dialog.rs | 3 ++- src/mouse_area.rs | 2 +- src/tab.rs | 9 +++++---- 4 files changed, 43 insertions(+), 17 deletions(-) diff --git a/src/app.rs b/src/app.rs index d4150d6..ce9ae61 100644 --- a/src/app.rs +++ b/src/app.rs @@ -26,6 +26,7 @@ use cosmic::{ futures::{self, SinkExt}, keyboard::{Event as KeyEvent, Key, Modifiers}, stream, + widget::scrollable, window::{self, Event as WindowEvent, Id as WindowId}, Alignment, Event, Length, Point, Rectangle, Size, Subscription, }, @@ -664,6 +665,7 @@ pub struct App { progress_operations: BTreeSet, complete_operations: BTreeMap, failed_operations: BTreeMap, + scrollable_id: widget::Id, search_id: widget::Id, size: Option, #[cfg(all(feature = "wayland", feature = "desktop-applet"))] @@ -959,6 +961,7 @@ impl App { location: Location, activate: bool, selection_paths: Option>, + scrollable_id: widget::Id, window_id: Option, ) -> (Entity, Task) { #[cfg(feature = "gvfs")] @@ -1002,6 +1005,7 @@ impl App { self.config.tab, self.config.thumb_cfg, Some(&self.state.sort_names), + scrollable_id, window_id, ); tab.mode = match self.mode { @@ -1041,8 +1045,14 @@ impl App { activate: bool, selection_paths: Option>, ) -> Task { - self.open_tab_entity(location, activate, selection_paths, None) - .1 + self.open_tab_entity( + location, + activate, + selection_paths, + self.scrollable_id.clone(), + None, + ) + .1 } // This wrapper ensures that local folders use trash and remote folders permanently delete with a dialog @@ -2144,6 +2154,7 @@ impl Application for App { progress_operations: BTreeSet::new(), complete_operations: BTreeMap::new(), failed_operations: BTreeMap::new(), + scrollable_id: widget::Id::unique(), search_id: widget::Id::unique(), size: None, #[cfg(all(feature = "wayland", feature = "desktop-applet"))] @@ -3742,6 +3753,12 @@ impl Application for App { // Activate new tab self.tab_model.activate(entity); if let Some(tab) = self.tab_model.data::(entity) { + { + //Restore scroll + //TODO: why do scrollers with different IDs get the same scroll position? + let scroll = tab.scroll_opt.unwrap_or_default(); + tasks.push(scrollable::scroll_to(tab.scrollable_id.clone(), scroll)); + } self.activate_nav_model_location(&tab.location.clone()); } tasks.push(self.update_title()); @@ -3781,6 +3798,8 @@ impl Application for App { } } Message::TabClose(entity_opt) => { + let mut tasks = Vec::with_capacity(3); + let entity = entity_opt.unwrap_or_else(|| self.tab_model.active()); // Activate closest item @@ -3791,12 +3810,8 @@ impl Application for App { position + 1 }; - if self.tab_model.activate_position(new_position) { - if let Some(new_entity) = self.tab_model.entity_at(new_position) { - if let Some(tab) = self.tab_model.data::(new_entity) { - self.activate_nav_model_location(&tab.location.clone()); - } - } + if let Some(new_entity) = self.tab_model.entity_at(new_position) { + tasks.push(self.update(Message::TabActivate(new_entity))); } } @@ -3806,11 +3821,13 @@ impl Application for App { // If that was the last tab, close window if self.tab_model.iter().next().is_none() { if let Some(window_id) = self.core.main_window_id() { - return window::close(window_id); + tasks.push(window::close(window_id)); } } - return Task::batch([self.update_title(), self.update_watcher()]); + tasks.push(self.update_watcher()); + + return Task::batch(tasks); } Message::TabConfig(config) => { if config != self.config.tab { @@ -4567,6 +4584,7 @@ impl Application for App { Location::Desktop(crate::desktop_dir(), display, self.config.desktop), false, None, + widget::Id::unique(), Some(surface_id), ); self.windows.insert(surface_id, WindowKind::Desktop(entity)); @@ -6378,7 +6396,13 @@ pub(crate) mod test_utils { // New tab with items let location = Location::Path(path.to_owned()); let (parent_item_opt, items) = location.scan(IconSizes::default()); - let mut tab = Tab::new(location, TabConfig::default(), ThumbCfg::default(), None); + let mut tab = Tab::new( + location, + TabConfig::default(), + ThumbCfg::default(), + widget::Id::unique(), + None, + ); tab.parent_item_opt = parent_item_opt; tab.set_items(items); diff --git a/src/dialog.rs b/src/dialog.rs index 5d5498a..8412c38 100644 --- a/src/dialog.rs +++ b/src/dialog.rs @@ -36,7 +36,7 @@ use std::{ use crate::{ app::{Action, ContextPage, Message as AppMessage, PreviewItem, PreviewKind}, - config::{Config, DialogConfig, Favorite, TabConfig, ThumbCfg, TimeConfig, TIME_CONFIG_ID}, + config::{Config, DialogConfig, Favorite, ThumbCfg, TimeConfig, TIME_CONFIG_ID}, fl, home_dir, key_bind::key_binds, localize::LANGUAGE_SORTER, @@ -950,6 +950,7 @@ impl Application for App { flags.config.dialog_tab(), ThumbCfg::default(), None, + widget::Id::unique(), None, ); tab.mode = tab::Mode::Dialog(flags.kind.clone()); diff --git a/src/mouse_area.rs b/src/mouse_area.rs index bd171cd..89e4a92 100644 --- a/src/mouse_area.rs +++ b/src/mouse_area.rs @@ -524,7 +524,7 @@ fn update( viewport: &Rectangle, ) -> event::Status { let offset = layout.virtual_offset(); - let mut layout_bounds = layout.bounds(); + let layout_bounds = layout.bounds(); if let Some(message) = widget.on_resize.as_ref() { if state.viewport != Some(*viewport) { diff --git a/src/tab.rs b/src/tab.rs index e309618..005cf45 100644 --- a/src/tab.rs +++ b/src/tab.rs @@ -2455,7 +2455,7 @@ pub struct Tab { pub(crate) parent_item_opt: Option, pub(crate) items_opt: Option>, pub dnd_hovered: Option<(Location, Instant)>, - scrollable_id: widget::Id, + pub(crate) scrollable_id: widget::Id, select_focus: Option, select_range: Option<(usize, usize)>, clicked: Option, @@ -2535,6 +2535,7 @@ impl Tab { config: TabConfig, thumb_config: ThumbCfg, sorting_options: Option<&OrderMap>, + scrollable_id: widget::Id, window_id: Option, ) -> Self { let location_str = location.to_string(); @@ -2570,7 +2571,7 @@ impl Tab { gallery: false, parent_item_opt: None, items_opt: None, - scrollable_id: widget::Id::unique(), + scrollable_id, select_focus: None, select_range: None, clicked: None, @@ -3589,7 +3590,7 @@ impl Tab { Some(selected_paths), )); } - Message::RightClick(p, click_i_opt) => { + Message::RightClick(_point_opt, click_i_opt) => { if mod_ctrl || mod_shift { self.update(Message::Click(click_i_opt), modifiers); } @@ -5529,7 +5530,7 @@ impl Tab { } } } - Location::Network(uri, _display_name, path) if uri == "network:///" => { + Location::Network(uri, _display_name, _path) if uri == "network:///" => { tab_column = tab_column.push( widget::layer_container(widget::row::with_children(vec![ widget::horizontal_space().into(),