project_panel: Only show sticky item shadow when list is scrolled (#34050)
Follow up: https://github.com/zed-industries/zed/pull/34042 - Removes `top_slot_items` from `uniform_list` in favor of using existing `decorations` - Add condition to only show shadow for sticky item when list is scrolled and scrollable Release Notes: - N/A
This commit is contained in:
parent
f1db3b4e1d
commit
1f3575ad6e
4 changed files with 180 additions and 178 deletions
|
@ -147,6 +147,7 @@ mod uniform_list {
|
|||
&self,
|
||||
visible_range: Range<usize>,
|
||||
bounds: Bounds<Pixels>,
|
||||
_scroll_offset: Point<Pixels>,
|
||||
item_height: Pixels,
|
||||
item_count: usize,
|
||||
window: &mut Window,
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use std::ops::Range;
|
||||
use std::{ops::Range, rc::Rc};
|
||||
|
||||
use gpui::{
|
||||
AnyElement, App, AvailableSpace, Bounds, Context, Entity, Pixels, Render, UniformListTopSlot,
|
||||
AnyElement, App, AvailableSpace, Bounds, Context, Element, ElementId, Entity, GlobalElementId,
|
||||
InspectorElementId, IntoElement, LayoutId, Pixels, Point, Render, Style, UniformListDecoration,
|
||||
Window, point, size,
|
||||
};
|
||||
use smallvec::SmallVec;
|
||||
|
@ -10,16 +11,16 @@ pub trait StickyCandidate {
|
|||
fn depth(&self) -> usize;
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct StickyItems<T> {
|
||||
compute_fn: Box<dyn Fn(Range<usize>, &mut Window, &mut App) -> Vec<T>>,
|
||||
render_fn: Box<dyn Fn(T, &mut Window, &mut App) -> SmallVec<[AnyElement; 8]>>,
|
||||
last_item_is_drifting: bool,
|
||||
anchor_index: Option<usize>,
|
||||
compute_fn: Rc<dyn Fn(Range<usize>, &mut Window, &mut App) -> SmallVec<[T; 8]>>,
|
||||
render_fn: Rc<dyn Fn(T, &mut Window, &mut App) -> SmallVec<[AnyElement; 8]>>,
|
||||
}
|
||||
|
||||
pub fn sticky_items<V, T>(
|
||||
entity: Entity<V>,
|
||||
compute_fn: impl Fn(&mut V, Range<usize>, &mut Window, &mut Context<V>) -> Vec<T> + 'static,
|
||||
compute_fn: impl Fn(&mut V, Range<usize>, &mut Window, &mut Context<V>) -> SmallVec<[T; 8]>
|
||||
+ 'static,
|
||||
render_fn: impl Fn(&mut V, T, &mut Window, &mut Context<V>) -> SmallVec<[AnyElement; 8]> + 'static,
|
||||
) -> StickyItems<T>
|
||||
where
|
||||
|
@ -29,37 +30,106 @@ where
|
|||
let entity_compute = entity.clone();
|
||||
let entity_render = entity.clone();
|
||||
|
||||
let compute_fn = Box::new(
|
||||
move |range: Range<usize>, window: &mut Window, cx: &mut App| -> Vec<T> {
|
||||
let compute_fn = Rc::new(
|
||||
move |range: Range<usize>, window: &mut Window, cx: &mut App| -> SmallVec<[T; 8]> {
|
||||
entity_compute.update(cx, |view, cx| compute_fn(view, range, window, cx))
|
||||
},
|
||||
);
|
||||
let render_fn = Box::new(
|
||||
let render_fn = Rc::new(
|
||||
move |entry: T, window: &mut Window, cx: &mut App| -> SmallVec<[AnyElement; 8]> {
|
||||
entity_render.update(cx, |view, cx| render_fn(view, entry, window, cx))
|
||||
},
|
||||
);
|
||||
|
||||
StickyItems {
|
||||
compute_fn,
|
||||
render_fn,
|
||||
last_item_is_drifting: false,
|
||||
anchor_index: None,
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> UniformListTopSlot for StickyItems<T>
|
||||
struct StickyItemsElement {
|
||||
elements: SmallVec<[AnyElement; 8]>,
|
||||
}
|
||||
|
||||
impl IntoElement for StickyItemsElement {
|
||||
type Element = Self;
|
||||
|
||||
fn into_element(self) -> Self::Element {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Element for StickyItemsElement {
|
||||
type RequestLayoutState = ();
|
||||
type PrepaintState = ();
|
||||
|
||||
fn id(&self) -> Option<ElementId> {
|
||||
None
|
||||
}
|
||||
|
||||
fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {
|
||||
None
|
||||
}
|
||||
|
||||
fn request_layout(
|
||||
&mut self,
|
||||
_id: Option<&GlobalElementId>,
|
||||
_inspector_id: Option<&InspectorElementId>,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> (LayoutId, Self::RequestLayoutState) {
|
||||
(window.request_layout(Style::default(), [], cx), ())
|
||||
}
|
||||
|
||||
fn prepaint(
|
||||
&mut self,
|
||||
_id: Option<&GlobalElementId>,
|
||||
_inspector_id: Option<&InspectorElementId>,
|
||||
_bounds: Bounds<Pixels>,
|
||||
_request_layout: &mut Self::RequestLayoutState,
|
||||
_window: &mut Window,
|
||||
_cx: &mut App,
|
||||
) -> Self::PrepaintState {
|
||||
()
|
||||
}
|
||||
|
||||
fn paint(
|
||||
&mut self,
|
||||
_id: Option<&GlobalElementId>,
|
||||
_inspector_id: Option<&InspectorElementId>,
|
||||
_bounds: Bounds<Pixels>,
|
||||
_request_layout: &mut Self::RequestLayoutState,
|
||||
_prepaint: &mut Self::PrepaintState,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) {
|
||||
// reverse so that last item is bottom most among sticky items
|
||||
for item in self.elements.iter_mut().rev() {
|
||||
item.paint(window, cx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> UniformListDecoration for StickyItems<T>
|
||||
where
|
||||
T: StickyCandidate + Clone + 'static,
|
||||
{
|
||||
fn compute(
|
||||
&mut self,
|
||||
&self,
|
||||
visible_range: Range<usize>,
|
||||
bounds: Bounds<Pixels>,
|
||||
scroll_offset: Point<Pixels>,
|
||||
item_height: Pixels,
|
||||
_item_count: usize,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> SmallVec<[AnyElement; 8]> {
|
||||
) -> AnyElement {
|
||||
let entries = (self.compute_fn)(visible_range.clone(), window, cx);
|
||||
let mut elements = SmallVec::new();
|
||||
|
||||
let mut anchor_entry = None;
|
||||
let mut last_item_is_drifting = false;
|
||||
let mut anchor_index = None;
|
||||
|
||||
let mut iter = entries.iter().enumerate().peekable();
|
||||
while let Some((ix, current_entry)) = iter.next() {
|
||||
|
@ -75,8 +145,8 @@ where
|
|||
let next_depth = next_entry.depth();
|
||||
|
||||
if next_depth < current_depth && next_depth < index_in_range {
|
||||
self.last_item_is_drifting = true;
|
||||
self.anchor_index = Some(visible_range.start + ix);
|
||||
last_item_is_drifting = true;
|
||||
anchor_index = Some(visible_range.start + ix);
|
||||
anchor_entry = Some(current_entry.clone());
|
||||
break;
|
||||
}
|
||||
|
@ -84,67 +154,36 @@ where
|
|||
}
|
||||
|
||||
if let Some(anchor_entry) = anchor_entry {
|
||||
(self.render_fn)(anchor_entry, window, cx)
|
||||
} else {
|
||||
SmallVec::new()
|
||||
}
|
||||
}
|
||||
elements = (self.render_fn)(anchor_entry, window, cx);
|
||||
let items_count = elements.len();
|
||||
|
||||
fn prepaint(
|
||||
&self,
|
||||
items: &mut SmallVec<[AnyElement; 8]>,
|
||||
bounds: Bounds<Pixels>,
|
||||
item_height: Pixels,
|
||||
scroll_offset: gpui::Point<Pixels>,
|
||||
padding: gpui::Edges<Pixels>,
|
||||
can_scroll_horizontally: bool,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) {
|
||||
let items_count = items.len();
|
||||
for (ix, element) in elements.iter_mut().enumerate() {
|
||||
let mut item_y_offset = None;
|
||||
if ix == items_count - 1 && last_item_is_drifting {
|
||||
if let Some(anchor_index) = anchor_index {
|
||||
let scroll_top = -scroll_offset.y;
|
||||
let anchor_top = item_height * anchor_index;
|
||||
let sticky_area_height = item_height * items_count;
|
||||
item_y_offset =
|
||||
Some((anchor_top - scroll_top - sticky_area_height).min(Pixels::ZERO));
|
||||
};
|
||||
}
|
||||
|
||||
for (ix, item) in items.iter_mut().enumerate() {
|
||||
let mut item_y_offset = None;
|
||||
if ix == items_count - 1 && self.last_item_is_drifting {
|
||||
if let Some(anchor_index) = self.anchor_index {
|
||||
let scroll_top = -scroll_offset.y;
|
||||
let anchor_top = item_height * anchor_index;
|
||||
let sticky_area_height = item_height * items_count;
|
||||
item_y_offset =
|
||||
Some((anchor_top - scroll_top - sticky_area_height).min(Pixels::ZERO));
|
||||
};
|
||||
}
|
||||
let sticky_origin = bounds.origin
|
||||
+ point(
|
||||
-scroll_offset.x,
|
||||
-scroll_offset.y + item_height * ix + item_y_offset.unwrap_or(Pixels::ZERO),
|
||||
);
|
||||
|
||||
let sticky_origin = bounds.origin
|
||||
+ point(
|
||||
if can_scroll_horizontally {
|
||||
scroll_offset.x + padding.left
|
||||
} else {
|
||||
scroll_offset.x
|
||||
},
|
||||
item_height * ix + padding.top + item_y_offset.unwrap_or(Pixels::ZERO),
|
||||
let available_space = size(
|
||||
AvailableSpace::Definite(bounds.size.width),
|
||||
AvailableSpace::Definite(item_height),
|
||||
);
|
||||
|
||||
let available_width = if can_scroll_horizontally {
|
||||
bounds.size.width + scroll_offset.x.abs()
|
||||
} else {
|
||||
bounds.size.width
|
||||
};
|
||||
|
||||
let available_space = size(
|
||||
AvailableSpace::Definite(available_width),
|
||||
AvailableSpace::Definite(item_height),
|
||||
);
|
||||
|
||||
item.layout_as_root(available_space, window, cx);
|
||||
item.prepaint_at(sticky_origin, window, cx);
|
||||
element.layout_as_root(available_space, window, cx);
|
||||
element.prepaint_at(sticky_origin, window, cx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn paint(&self, items: &mut SmallVec<[AnyElement; 8]>, window: &mut Window, cx: &mut App) {
|
||||
// reverse so that last item is bottom most among sticky items
|
||||
for item in items.iter_mut().rev() {
|
||||
item.paint(window, cx);
|
||||
}
|
||||
StickyItemsElement { elements }.into_any_element()
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue