Do not notify the parent unintentionally

This commit is contained in:
MrSubidubi 2025-08-13 18:15:40 +02:00
parent 40084aa94c
commit fd33832609
2 changed files with 83 additions and 37 deletions

View file

@ -2504,7 +2504,7 @@ impl Window {
&mut self, &mut self,
key: impl Into<ElementId>, key: impl Into<ElementId>,
cx: &mut App, cx: &mut App,
init: impl FnOnce(&mut Self, &mut Context<S>) -> S, init: impl FnOnce(&mut Self, &mut App) -> S,
) -> Entity<S> { ) -> Entity<S> {
let current_view = self.current_view(); let current_view = self.current_view();
self.with_global_id(key.into(), |global_id, window| { self.with_global_id(key.into(), |global_id, window| {
@ -2537,7 +2537,7 @@ impl Window {
pub fn use_state<S: 'static>( pub fn use_state<S: 'static>(
&mut self, &mut self,
cx: &mut App, cx: &mut App,
init: impl FnOnce(&mut Self, &mut Context<S>) -> S, init: impl FnOnce(&mut Self, &mut App) -> S,
) -> Entity<S> { ) -> Entity<S> {
self.use_keyed_state( self.use_keyed_state(
ElementId::CodeLocation(*core::panic::Location::caller()), ElementId::CodeLocation(*core::panic::Location::caller()),

View file

@ -1,13 +1,13 @@
use std::{fmt::Debug, marker::PhantomData, ops::Not, time::Duration}; use std::{fmt::Debug, marker::PhantomData, ops::Not, time::Duration};
use gpui::{ use gpui::{
Along, App, Axis as ScrollbarAxis, BorderStyle, Bounds, ContentMask, Context, Corner, Corners, Along, App, AppContext as _, Axis as ScrollbarAxis, BorderStyle, Bounds, ContentMask, Context,
CursorStyle, Div, Edges, Element, ElementId, Entity, EntityId, GlobalElementId, Hitbox, Corner, Corners, CursorStyle, Div, Edges, Element, ElementId, Entity, EntityId,
HitboxBehavior, Hsla, InteractiveElement, IntoElement, IsZero, LayoutId, ListState, GlobalElementId, Hitbox, HitboxBehavior, Hsla, InteractiveElement, IntoElement, IsZero,
MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, Point, LayoutId, ListState, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentElement,
Position, Render, ScrollHandle, ScrollWheelEvent, Size, Stateful, StatefulInteractiveElement, Pixels, Point, Position, Render, ScrollHandle, ScrollWheelEvent, Size, Stateful,
Style, Styled, Task, UniformListScrollHandle, Window, prelude::FluentBuilder as _, px, quad, StatefulInteractiveElement, Style, Styled, Task, UniformList, UniformListDecoration,
relative, size, UniformListScrollHandle, Window, prelude::FluentBuilder as _, px, quad, relative, size,
}; };
use settings::SettingsStore; use settings::SettingsStore;
use smallvec::SmallVec; use smallvec::SmallVec;
@ -92,6 +92,23 @@ pub mod scrollbars {
impl Global for ScrollbarAutoHide {} impl Global for ScrollbarAutoHide {}
} }
fn get_scrollbar_state<S, T>(
mut config: Scrollbars<S, T>,
caller_location: &'static std::panic::Location,
window: &mut Window,
cx: &mut App,
) -> Entity<ScrollbarStateWrapper<S, T>>
where
S: ScrollbarVisibilitySetting,
T: ScrollableHandle,
{
let element_id = config.id.take().unwrap_or_else(|| caller_location.into());
window.use_keyed_state(element_id, cx, |window, cx| {
ScrollbarStateWrapper(cx.new(|cx| ScrollbarState::new_from_config(config, window, cx)))
})
}
pub trait WithScrollbar: Sized { pub trait WithScrollbar: Sized {
type Output; type Output;
@ -146,7 +163,7 @@ impl WithScrollbar for Stateful<Div> {
#[track_caller] #[track_caller]
fn custom_scrollbars<S, T>( fn custom_scrollbars<S, T>(
self, self,
mut config: Scrollbars<S, T>, config: Scrollbars<S, T>,
window: &mut Window, window: &mut Window,
cx: &mut App, cx: &mut App,
) -> Self::Output ) -> Self::Output
@ -154,16 +171,11 @@ impl WithScrollbar for Stateful<Div> {
S: ScrollbarVisibilitySetting, S: ScrollbarVisibilitySetting,
T: ScrollableHandle, T: ScrollableHandle,
{ {
let element_id = config render_scrollbar(
.id get_scrollbar_state(config, std::panic::Location::caller(), window, cx),
.take() self,
.unwrap_or_else(|| core::panic::Location::caller().into()); cx,
)
let scrollbar = window.use_keyed_state(element_id.clone(), cx, |window, cx| {
ScrollbarState::new_from_config(config, window, cx)
});
render_scrollbar(scrollbar, self, cx)
} }
} }
@ -173,7 +185,7 @@ impl WithScrollbar for Div {
#[track_caller] #[track_caller]
fn custom_scrollbars<S, T>( fn custom_scrollbars<S, T>(
self, self,
mut config: Scrollbars<S, T>, config: Scrollbars<S, T>,
window: &mut Window, window: &mut Window,
cx: &mut App, cx: &mut App,
) -> Self::Output ) -> Self::Output
@ -181,14 +193,7 @@ impl WithScrollbar for Div {
S: ScrollbarVisibilitySetting, S: ScrollbarVisibilitySetting,
T: ScrollableHandle, T: ScrollableHandle,
{ {
let element_id = config let scrollbar = get_scrollbar_state(config, std::panic::Location::caller(), window, cx);
.id
.take()
.unwrap_or_else(|| core::panic::Location::caller().into());
let scrollbar = window.use_keyed_state(element_id.clone(), cx, |window, cx| {
ScrollbarState::new_from_config(config, window, cx)
});
// We know this ID stays consistent as long as the element is rendered for // We know this ID stays consistent as long as the element is rendered for
// consecutive frames, which is sufficient for our use case here // consecutive frames, which is sufficient for our use case here
let scrollbar_entity_id = scrollbar.entity_id(); let scrollbar_entity_id = scrollbar.entity_id();
@ -202,7 +207,7 @@ impl WithScrollbar for Div {
} }
fn render_scrollbar<S, T>( fn render_scrollbar<S, T>(
scrollbar: Entity<ScrollbarState<S, T>>, scrollbar: Entity<ScrollbarStateWrapper<S, T>>,
div: Stateful<Div>, div: Stateful<Div>,
cx: &App, cx: &App,
) -> Stateful<Div> ) -> Stateful<Div>
@ -210,28 +215,63 @@ where
S: ScrollbarVisibilitySetting, S: ScrollbarVisibilitySetting,
T: ScrollableHandle, T: ScrollableHandle,
{ {
if scrollbar.read(cx).disabled() { let state = &scrollbar.read(cx).0;
if state.read(cx).disabled() {
div div
} else { } else {
div.when_some(scrollbar.read(cx).handle_to_track(), |this, handle| { div.when_some(state.read(cx).handle_to_track(), |this, handle| {
this.track_scroll(handle) this.track_scroll(handle)
}) })
.when_some( .when_some(
scrollbar state
.read(cx) .read(cx)
.space_to_reserve_for(ScrollbarAxis::Horizontal), .space_to_reserve_for(ScrollbarAxis::Horizontal),
|this, space| this.pb(space), |this, space| this.pb(space),
) )
.when_some( .when_some(
scrollbar state.read(cx).space_to_reserve_for(ScrollbarAxis::Vertical),
.read(cx)
.space_to_reserve_for(ScrollbarAxis::Vertical),
|this, space| this.pr(space), |this, space| this.pr(space),
) )
.child(scrollbar) .child(state.clone())
} }
} }
// impl<S: ScrollbarVisibilitySetting, T: ScrollableHandle> UniformListDecoration
// for ScrollbarState<S, T>
// {
// fn compute(
// &self,
// visible_range: Range<usize>,
// bounds: Bounds<Pixels>,
// scroll_offset: Point<Pixels>,
// item_height: Pixels,
// item_count: usize,
// window: &mut Window,
// cx: &mut App,
// ) -> gpui::AnyElement {
// let element = ScrollbarElement::new(self);
// }
// }
// impl WithScrollbar for UniformList {
// type Output = Self;
// fn custom_scrollbars<S, T>(
// self,
// config: Scrollbars<S, T>,
// window: &mut Window,
// cx: &mut App,
// ) -> Self::Output
// where
// S: ScrollbarVisibilitySetting,
// T: ScrollableHandle,
// {
// let scrollbar = get_scrollbar_state(config, std::panic::Location::caller(), window, cx);
// self.with_decoration(scrollbar)
// }
// }
#[derive(PartialEq, Eq)] #[derive(PartialEq, Eq)]
pub enum ScrollAxes { pub enum ScrollAxes {
Horizontal, Horizontal,
@ -417,6 +457,12 @@ impl VisibilityState {
} }
} }
/// This is used to ensure notifies within the state do not notify the parent
/// unintentionally.
struct ScrollbarStateWrapper<S: ScrollbarVisibilitySetting, T: ScrollableHandle>(
Entity<ScrollbarState<S, T>>,
);
/// A scrollbar state that should be persisted across frames. /// A scrollbar state that should be persisted across frames.
struct ScrollbarState<S: ScrollbarVisibilitySetting, T: ScrollableHandle = ScrollHandle> { struct ScrollbarState<S: ScrollbarVisibilitySetting, T: ScrollableHandle = ScrollHandle> {
thumb_state: ThumbState, thumb_state: ThumbState,