fix: allow file tree to scroll

This commit is contained in:
2026-06-04 23:20:23 +01:00
parent b8971027ac
commit 39093f3b69
2 changed files with 69 additions and 31 deletions

View File

@@ -120,6 +120,13 @@ impl DiffViewState {
) { ) {
self.0.borrow_mut().new_side_highlights = Some(highlights); self.0.borrow_mut().new_side_highlights = Some(highlights);
} }
pub(crate) fn scroll_to_diff_line(&mut self, line_i: util::diff::DiffLineIndex) {
self.0
.borrow_mut()
.list_state
.scroll_to_reveal_item(line_i.into());
}
} }
impl gpui::RenderOnce for DiffView { impl gpui::RenderOnce for DiffView {

View File

@@ -1,8 +1,9 @@
use std::{cell::RefCell, collections::HashSet, rc::Rc, sync::Arc}; use std::{cell::RefCell, collections::HashSet, rc::Rc, sync::Arc};
use gpui::{ use gpui::{
InteractiveElement, IntoElement, ParentElement, StatefulInteractiveElement, Styled, div, list, InteractiveElement, IntoElement, ListHorizontalSizingBehavior, ListSizingBehavior,
prelude::FluentBuilder, px, ParentElement, StatefulInteractiveElement, Styled, UniformListScrollHandle, div,
prelude::FluentBuilder, px, uniform_list,
}; };
use crate::{ use crate::{
@@ -11,10 +12,7 @@ use crate::{
font_icon::{FontIcon, font_icon}, font_icon::{FontIcon, font_icon},
text::text, text::text,
}, },
util::{ util::{self, file::FileTreeItemKind},
self,
file::{FileTreeItem, FileTreeItemKind},
},
}; };
#[derive(gpui::IntoElement)] #[derive(gpui::IntoElement)]
@@ -36,10 +34,11 @@ pub(crate) struct Item {
pub(crate) struct FileTreeState(Rc<RefCell<FileTreeStateInner>>); pub(crate) struct FileTreeState(Rc<RefCell<FileTreeStateInner>>);
struct FileTreeStateInner { struct FileTreeStateInner {
list_state: gpui::ListState, scroll_handle: UniformListScrollHandle,
collapsed_dirs: HashSet<Arc<str>>, collapsed_dirs: HashSet<Arc<str>>,
visible_items: Vec<usize>, visible_items: Vec<usize>,
highlighted_items: HashSet<usize>, highlighted_items: HashSet<usize>,
max_width_item_index: Option<usize>,
} }
pub(crate) fn file_tree( pub(crate) fn file_tree(
@@ -56,17 +55,18 @@ pub(crate) fn file_tree(
impl FileTreeState { impl FileTreeState {
pub(crate) fn new() -> Self { pub(crate) fn new() -> Self {
Self(Rc::new(RefCell::new(FileTreeStateInner { Self(Rc::new(RefCell::new(FileTreeStateInner {
list_state: gpui::ListState::new(0, gpui::ListAlignment::Top, px(50.)), scroll_handle: UniformListScrollHandle::new(),
collapsed_dirs: HashSet::new(), collapsed_dirs: HashSet::new(),
visible_items: Vec::new(), visible_items: Vec::new(),
highlighted_items: HashSet::new(), highlighted_items: HashSet::new(),
max_width_item_index: None,
}))) })))
} }
pub(crate) fn reset(&self, items: &[util::file::FileTreeItem]) { pub(crate) fn reset(&self, items: &[util::file::FileTreeItem]) {
let mut state = self.0.borrow_mut(); let mut state = self.0.borrow_mut();
state.list_state.reset(items.len());
state.visible_items = (0..items.len()).collect::<Vec<_>>(); state.visible_items = (0..items.len()).collect::<Vec<_>>();
state.update_max_width_item_index(items);
} }
pub(crate) fn highlight_item(&mut self, index: usize) { pub(crate) fn highlight_item(&mut self, index: usize) {
@@ -104,7 +104,21 @@ impl FileTreeState {
state.visible_items.push(i); state.visible_items.push(i);
} }
state.list_state.reset(state.visible_items.len()); state.update_max_width_item_index(items);
}
}
impl FileTreeStateInner {
fn update_max_width_item_index(&mut self, items: &[util::file::FileTreeItem]) {
self.max_width_item_index = self
.visible_items
.iter()
.enumerate()
.max_by_key(|(_, item_i)| {
let item = &items[**item_i];
item.level.saturating_mul(2) + item.name.chars().count()
})
.map(|(visible_i, _)| visible_i);
} }
} }
@@ -124,28 +138,41 @@ impl gpui::RenderOnce for FileTree {
_window: &mut gpui::Window, _window: &mut gpui::Window,
_cx: &mut gpui::App, _cx: &mut gpui::App,
) -> impl gpui::prelude::IntoElement { ) -> impl gpui::prelude::IntoElement {
let list_state = self.state.0.borrow().list_state.clone(); let state = self.state.0.borrow();
let item_count = state.visible_items.len();
let scroll_handle = state.scroll_handle.clone();
let max_width_item_index = state.max_width_item_index;
drop(state);
list(list_state, move |i, window, cx| { uniform_list("file-tree", item_count, move |range, window, cx| {
let state = self.state.0.borrow(); range
let item_index = state.visible_items[i]; .map(|i| {
let on_item_click = self.on_item_click.as_ref().map(Rc::clone); let item_index = self.state.0.borrow().visible_items[i];
let on_item_click = self.on_item_click.as_ref().map(Rc::clone);
let item = (self.item)(item_index, window, cx); let item = (self.item)(item_index, window, cx);
let item = Item { let state = self.state.0.borrow();
is_expanded: !state.collapsed_dirs.contains(&item.full_path), let item = Item {
is_highlighed: state.highlighted_items.contains(&item_index), is_expanded: !state.collapsed_dirs.contains(&item.full_path),
item, is_highlighed: state.highlighted_items.contains(&item_index),
on_click: Box::new(move |window, cx| { item,
if let Some(f) = &on_item_click { on_click: Box::new(move |window, cx| {
f(&i, window, cx); if let Some(f) = &on_item_click {
} f(&item_index, window, cx);
}), }
}; }),
};
item.into_any_element() item.into_any_element()
})
.collect()
}) })
.size_full() .h_full()
.min_w_full()
.with_sizing_behavior(ListSizingBehavior::Infer)
.with_horizontal_sizing_behavior(ListHorizontalSizingBehavior::Unconstrained)
.with_width_from_item(max_width_item_index)
.track_scroll(scroll_handle)
} }
} }
@@ -154,7 +181,7 @@ impl gpui::RenderOnce for Item {
let theme = app::current_theme(cx); let theme = app::current_theme(cx);
div() div()
.id(gpui::SharedString::new(self.item.full_path)) .id(gpui::SharedString::new(self.item.full_path))
.w_full() .min_w_full()
.flex() .flex()
.flex_row() .flex_row()
.items_center() .items_center()
@@ -162,7 +189,6 @@ impl gpui::RenderOnce for Item {
.pr_2() .pr_2()
.py_0p5() .py_0p5()
.pl(px(8. + (8 * self.item.level) as f32)) .pl(px(8. + (8 * self.item.level) as f32))
.rounded_sm()
.hover(|it| { .hover(|it| {
if self.is_highlighed { if self.is_highlighed {
it it
@@ -191,6 +217,11 @@ impl gpui::RenderOnce for Item {
it.bg(theme.colors.accent_muted) it.bg(theme.colors.accent_muted)
.text_color(theme.colors.accent_fg) .text_color(theme.colors.accent_fg)
}) })
.child(text(gpui::SharedString::new(self.item.name)).text_sm()) .child(
text(gpui::SharedString::new(self.item.name))
.text_sm()
.whitespace_nowrap()
.flex_shrink_0(),
)
} }
} }