Express scroll position with an element index and an offset

Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
Antonio Scandurra 2021-08-31 15:57:03 +02:00
parent 8edb2142b1
commit d410ff83a7
2 changed files with 40 additions and 36 deletions

View File

@ -27,7 +27,7 @@ struct StateInner {
last_layout_width: Option<f32>,
elements: Vec<Option<ElementRc>>,
heights: SumTree<ElementHeight>,
scroll_position: f32,
scroll_top: Option<(usize, f32)>,
orientation: Orientation,
scroll_handler: Option<Box<dyn FnMut(Range<usize>, &mut EventContext)>>,
}
@ -118,11 +118,6 @@ impl Element for List {
item_constraint.min.set_y(0.);
item_constraint.max.set_y(f32::INFINITY);
let size = constraint.max;
let visible_top = state.scroll_top(size.y());
let visible_bottom = visible_top + size.y();
if state.last_layout_width == Some(constraint.max.x()) {
let mut old_heights = state.heights.cursor::<PendingCount, ElementHeightSummary>();
let mut new_heights = old_heights.slice(&PendingCount(1), sum_tree::Bias::Left, &());
@ -132,21 +127,6 @@ impl Element for List {
let element = &mut state.elements[old_heights.sum_start().count];
let element_size = element.as_mut().unwrap().layout(item_constraint, cx);
new_heights.push(ElementHeight::Ready(element_size.y()), &());
// Adjust scroll position to keep visible elements stable
match state.orientation {
Orientation::Top => {
if new_heights.summary().height < visible_top {
state.scroll_position += element_size.y();
}
}
Orientation::Bottom => {
if new_heights.summary().height - element_size.y() > visible_bottom {
state.scroll_position += element_size.y();
}
}
}
old_heights.next(&());
} else {
new_heights.push_tree(
@ -172,6 +152,7 @@ impl Element for List {
state.last_layout_width = Some(constraint.max.x());
}
let size = constraint.max;
let visible_elements = state.elements[state.visible_range(size.y())]
.iter()
.map(|e| e.clone().unwrap())
@ -258,7 +239,7 @@ impl Element for List {
json!({
"visible_range": visible_range,
"visible_elements": visible_elements,
"scroll_position": state.scroll_position,
"scroll_top": state.scroll_top,
})
}
}
@ -271,7 +252,7 @@ impl ListState {
last_layout_width: None,
elements: (0..element_count).map(|_| None).collect(),
heights,
scroll_position: 0.,
scroll_top: None,
orientation,
scroll_handler: None,
})))
@ -314,21 +295,39 @@ impl StateInner {
fn scroll(
&mut self,
_: Vector2F,
delta: Vector2F,
mut delta: Vector2F,
precise: bool,
height: f32,
cx: &mut EventContext,
) -> bool {
if !precise {
todo!("still need to handle non-precise scroll events from a mouse wheel");
delta *= 20.;
}
let scroll_max = (self.heights.summary().height - height).max(0.);
let delta_y = match self.orientation {
Orientation::Top => -delta.y(),
Orientation::Bottom => delta.y(),
let delta_y;
let seek_bias;
match self.orientation {
Orientation::Top => {
delta_y = delta.y();
seek_bias = Bias::Right;
}
Orientation::Bottom => {
delta_y = -delta.y();
seek_bias = Bias::Left;
}
};
self.scroll_position = (self.scroll_position + delta_y).max(0.).min(scroll_max);
let scroll_max = (self.heights.summary().height - height).max(0.);
let new_scroll_top = (self.scroll_top(height) + delta_y).max(0.).min(scroll_max);
if self.orientation == Orientation::Bottom && new_scroll_top == scroll_max {
self.scroll_top = None;
} else {
let mut cursor = self.heights.cursor::<Height, Count>();
cursor.seek(&Height(new_scroll_top), seek_bias, &());
let ix = cursor.sum_start().0;
let offset = new_scroll_top - cursor.seek_start().0;
self.scroll_top = Some((ix, offset));
}
if self.scroll_handler.is_some() {
let range = self.visible_range(height);
@ -340,10 +339,15 @@ impl StateInner {
}
fn scroll_top(&self, height: f32) -> f32 {
match self.orientation {
Orientation::Top => self.scroll_position,
Orientation::Bottom => {
(self.heights.summary().height - height - self.scroll_position).max(0.)
let scroll_max = (self.heights.summary().height - height).max(0.);
if let Some((ix, offset)) = self.scroll_top {
let mut cursor = self.heights.cursor::<Count, Height>();
cursor.seek(&Count(ix), Bias::Right, &());
(cursor.sum_start().0 + offset).min(scroll_max)
} else {
match self.orientation {
Orientation::Top => 0.,
Orientation::Bottom => scroll_max,
}
}
}

View File

@ -60,13 +60,13 @@ where
fn scroll(
&self,
_: Vector2F,
delta: Vector2F,
mut delta: Vector2F,
precise: bool,
scroll_max: f32,
cx: &mut EventContext,
) -> bool {
if !precise {
todo!("still need to handle non-precise scroll events from a mouse wheel");
delta *= 20.;
}
let mut state = self.state.0.lock();