Maintain logical scroll top when splicing List elements
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
@@ -261,6 +261,15 @@ impl ListState {
|
||||
pub fn splice(&self, old_range: Range<usize>, count: usize) {
|
||||
let state = &mut *self.0.borrow_mut();
|
||||
|
||||
if let Some((ix, offset)) = state.scroll_top.as_mut() {
|
||||
if old_range.contains(ix) {
|
||||
*ix = old_range.start;
|
||||
*offset = 0.;
|
||||
} else if old_range.end <= *ix {
|
||||
*ix = *ix - (old_range.end - old_range.start) + count;
|
||||
}
|
||||
}
|
||||
|
||||
let mut old_heights = state.heights.cursor::<Count, ()>();
|
||||
let mut new_heights = old_heights.slice(&Count(old_range.start), Bias::Right, &());
|
||||
old_heights.seek_forward(&Count(old_range.end), Bias::Right, &());
|
||||
@@ -439,7 +448,7 @@ mod tests {
|
||||
fn test_layout(cx: &mut crate::MutableAppContext) {
|
||||
let mut presenter = cx.build_presenter(0, 0.);
|
||||
|
||||
let mut elements = vec![20., 30., 10.];
|
||||
let mut elements = vec![20., 30., 100.];
|
||||
let state = ListState::new(elements.len(), Orientation::Top);
|
||||
|
||||
let mut list = List::new(
|
||||
@@ -458,10 +467,20 @@ mod tests {
|
||||
ElementHeightSummary {
|
||||
count: 3,
|
||||
pending_count: 0,
|
||||
height: 60.
|
||||
height: 150.
|
||||
}
|
||||
);
|
||||
|
||||
state.0.borrow_mut().scroll(
|
||||
Default::default(),
|
||||
vec2f(0., 54.),
|
||||
true,
|
||||
size.y(),
|
||||
&mut presenter.build_event_context(cx),
|
||||
);
|
||||
assert_eq!(state.0.borrow().scroll_top, Some((2, 4.)));
|
||||
assert_eq!(state.0.borrow().scroll_top(size.y()), 54.);
|
||||
|
||||
elements.splice(1..2, vec![40., 50.]);
|
||||
elements.push(60.);
|
||||
state.splice(1..2, 2);
|
||||
@@ -471,7 +490,7 @@ mod tests {
|
||||
ElementHeightSummary {
|
||||
count: 5,
|
||||
pending_count: 3,
|
||||
height: 30.
|
||||
height: 120.
|
||||
}
|
||||
);
|
||||
|
||||
@@ -491,9 +510,11 @@ mod tests {
|
||||
ElementHeightSummary {
|
||||
count: 5,
|
||||
pending_count: 0,
|
||||
height: 180.
|
||||
height: 270.
|
||||
}
|
||||
);
|
||||
assert_eq!(state.0.borrow().scroll_top, Some((3, 4.)));
|
||||
assert_eq!(state.0.borrow().scroll_top(size.y()), 114.);
|
||||
}
|
||||
|
||||
fn item(height: f32) -> ElementBox {
|
||||
|
||||
@@ -160,15 +160,7 @@ impl Presenter {
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let mut event_cx = EventContext {
|
||||
rendered_views: &mut self.rendered_views,
|
||||
dispatched_actions: Default::default(),
|
||||
font_cache: &self.font_cache,
|
||||
text_layout_cache: &self.text_layout_cache,
|
||||
view_stack: Default::default(),
|
||||
invalidated_views: Default::default(),
|
||||
app: cx,
|
||||
};
|
||||
let mut event_cx = self.build_event_context(cx);
|
||||
event_cx.dispatch_event(root_view_id, &event);
|
||||
|
||||
let invalidated_views = event_cx.invalidated_views;
|
||||
@@ -183,6 +175,21 @@ impl Presenter {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_event_context<'a>(
|
||||
&'a mut self,
|
||||
cx: &'a mut MutableAppContext,
|
||||
) -> EventContext<'a> {
|
||||
EventContext {
|
||||
rendered_views: &mut self.rendered_views,
|
||||
dispatched_actions: Default::default(),
|
||||
font_cache: &self.font_cache,
|
||||
text_layout_cache: &self.text_layout_cache,
|
||||
view_stack: Default::default(),
|
||||
invalidated_views: Default::default(),
|
||||
app: cx,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn debug_elements(&self, cx: &AppContext) -> Option<json::Value> {
|
||||
cx.root_view_id(self.window_id)
|
||||
.and_then(|root_view_id| self.rendered_views.get(&root_view_id))
|
||||
@@ -274,8 +281,9 @@ impl<'a> EventContext<'a> {
|
||||
}
|
||||
|
||||
pub fn notify(&mut self) {
|
||||
self.invalidated_views
|
||||
.insert(*self.view_stack.last().unwrap());
|
||||
if let Some(view_id) = self.view_stack.last() {
|
||||
self.invalidated_views.insert(*view_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user