Restore tab scroll position, fixes #1115

This commit is contained in:
Jeremy Soller 2025-08-15 11:01:08 -06:00
parent 5e0645d1c2
commit fb74be9a70
No known key found for this signature in database
GPG key ID: 670FDFB5428E05CA
4 changed files with 43 additions and 17 deletions

View file

@ -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<u64>,
complete_operations: BTreeMap<u64, Operation>,
failed_operations: BTreeMap<u64, (Operation, Controller, String)>,
scrollable_id: widget::Id,
search_id: widget::Id,
size: Option<Size>,
#[cfg(all(feature = "wayland", feature = "desktop-applet"))]
@ -959,6 +961,7 @@ impl App {
location: Location,
activate: bool,
selection_paths: Option<Vec<PathBuf>>,
scrollable_id: widget::Id,
window_id: Option<window::Id>,
) -> (Entity, Task<Message>) {
#[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<Vec<PathBuf>>,
) -> Task<Message> {
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::<Tab>(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::<Tab>(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);

View file

@ -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());

View file

@ -524,7 +524,7 @@ fn update<Message: Clone>(
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) {

View file

@ -2455,7 +2455,7 @@ pub struct Tab {
pub(crate) parent_item_opt: Option<Item>,
pub(crate) items_opt: Option<Vec<Item>>,
pub dnd_hovered: Option<(Location, Instant)>,
scrollable_id: widget::Id,
pub(crate) scrollable_id: widget::Id,
select_focus: Option<usize>,
select_range: Option<(usize, usize)>,
clicked: Option<usize>,
@ -2535,6 +2535,7 @@ impl Tab {
config: TabConfig,
thumb_config: ThumbCfg,
sorting_options: Option<&OrderMap<String, (HeadingOptions, bool)>>,
scrollable_id: widget::Id,
window_id: Option<window::Id>,
) -> 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(),