Introduce autoscroll support for elements (#10889)
This pull request introduces the new `ElementContext::request_autoscroll(bounds)` and `ElementContext::take_autoscroll()` methods in GPUI. These new APIs enable container elements such as `List` to change their scroll position if one of their children requested an autoscroll. We plan to use this in the revamped assistant. As a drive-by, we also: - Renamed `Element::before_layout` to `Element::request_layout` - Renamed `Element::after_layout` to `Element::prepaint` - Introduced a new `List::splice_focusable` method to splice focusable elements into the list, which enables rendering offscreen elements that are focused. Release Notes: - N/A --------- Co-authored-by: Nathan <nathan@zed.dev>
This commit is contained in:
parent
efcd31c254
commit
bcbf2f2fd3
31 changed files with 780 additions and 513 deletions
|
@ -168,10 +168,13 @@ pub struct PopoverMenuFrameState {
|
|||
}
|
||||
|
||||
impl<M: ManagedView> Element for PopoverMenu<M> {
|
||||
type BeforeLayout = PopoverMenuFrameState;
|
||||
type AfterLayout = Option<HitboxId>;
|
||||
type RequestLayoutState = PopoverMenuFrameState;
|
||||
type PrepaintState = Option<HitboxId>;
|
||||
|
||||
fn before_layout(&mut self, cx: &mut ElementContext) -> (gpui::LayoutId, Self::BeforeLayout) {
|
||||
fn request_layout(
|
||||
&mut self,
|
||||
cx: &mut ElementContext,
|
||||
) -> (gpui::LayoutId, Self::RequestLayoutState) {
|
||||
self.with_element_state(cx, |this, element_state, cx| {
|
||||
let mut menu_layout_id = None;
|
||||
|
||||
|
@ -186,7 +189,7 @@ impl<M: ManagedView> Element for PopoverMenu<M> {
|
|||
.with_priority(1)
|
||||
.into_any();
|
||||
|
||||
menu_layout_id = Some(element.before_layout(cx));
|
||||
menu_layout_id = Some(element.request_layout(cx));
|
||||
element
|
||||
});
|
||||
|
||||
|
@ -196,7 +199,7 @@ impl<M: ManagedView> Element for PopoverMenu<M> {
|
|||
|
||||
let child_layout_id = child_element
|
||||
.as_mut()
|
||||
.map(|child_element| child_element.before_layout(cx));
|
||||
.map(|child_element| child_element.request_layout(cx));
|
||||
|
||||
let layout_id = cx.request_layout(
|
||||
&gpui::Style::default(),
|
||||
|
@ -214,22 +217,22 @@ impl<M: ManagedView> Element for PopoverMenu<M> {
|
|||
})
|
||||
}
|
||||
|
||||
fn after_layout(
|
||||
fn prepaint(
|
||||
&mut self,
|
||||
_bounds: Bounds<Pixels>,
|
||||
before_layout: &mut Self::BeforeLayout,
|
||||
request_layout: &mut Self::RequestLayoutState,
|
||||
cx: &mut ElementContext,
|
||||
) -> Option<HitboxId> {
|
||||
self.with_element_state(cx, |_this, element_state, cx| {
|
||||
if let Some(child) = before_layout.child_element.as_mut() {
|
||||
child.after_layout(cx);
|
||||
if let Some(child) = request_layout.child_element.as_mut() {
|
||||
child.prepaint(cx);
|
||||
}
|
||||
|
||||
if let Some(menu) = before_layout.menu_element.as_mut() {
|
||||
menu.after_layout(cx);
|
||||
if let Some(menu) = request_layout.menu_element.as_mut() {
|
||||
menu.prepaint(cx);
|
||||
}
|
||||
|
||||
before_layout.child_layout_id.map(|layout_id| {
|
||||
request_layout.child_layout_id.map(|layout_id| {
|
||||
let bounds = cx.layout_bounds(layout_id);
|
||||
element_state.child_bounds = Some(bounds);
|
||||
cx.insert_hitbox(bounds, false).id
|
||||
|
@ -240,16 +243,16 @@ impl<M: ManagedView> Element for PopoverMenu<M> {
|
|||
fn paint(
|
||||
&mut self,
|
||||
_: Bounds<gpui::Pixels>,
|
||||
before_layout: &mut Self::BeforeLayout,
|
||||
request_layout: &mut Self::RequestLayoutState,
|
||||
child_hitbox: &mut Option<HitboxId>,
|
||||
cx: &mut ElementContext,
|
||||
) {
|
||||
self.with_element_state(cx, |_this, _element_state, cx| {
|
||||
if let Some(mut child) = before_layout.child_element.take() {
|
||||
if let Some(mut child) = request_layout.child_element.take() {
|
||||
child.paint(cx);
|
||||
}
|
||||
|
||||
if let Some(mut menu) = before_layout.menu_element.take() {
|
||||
if let Some(mut menu) = request_layout.menu_element.take() {
|
||||
menu.paint(cx);
|
||||
|
||||
if let Some(child_hitbox) = *child_hitbox {
|
||||
|
|
|
@ -96,10 +96,13 @@ pub struct MenuHandleFrameState {
|
|||
}
|
||||
|
||||
impl<M: ManagedView> Element for RightClickMenu<M> {
|
||||
type BeforeLayout = MenuHandleFrameState;
|
||||
type AfterLayout = Hitbox;
|
||||
type RequestLayoutState = MenuHandleFrameState;
|
||||
type PrepaintState = Hitbox;
|
||||
|
||||
fn before_layout(&mut self, cx: &mut ElementContext) -> (gpui::LayoutId, Self::BeforeLayout) {
|
||||
fn request_layout(
|
||||
&mut self,
|
||||
cx: &mut ElementContext,
|
||||
) -> (gpui::LayoutId, Self::RequestLayoutState) {
|
||||
self.with_element_state(cx, |this, element_state, cx| {
|
||||
let mut menu_layout_id = None;
|
||||
|
||||
|
@ -114,7 +117,7 @@ impl<M: ManagedView> Element for RightClickMenu<M> {
|
|||
.with_priority(1)
|
||||
.into_any();
|
||||
|
||||
menu_layout_id = Some(element.before_layout(cx));
|
||||
menu_layout_id = Some(element.request_layout(cx));
|
||||
element
|
||||
});
|
||||
|
||||
|
@ -125,7 +128,7 @@ impl<M: ManagedView> Element for RightClickMenu<M> {
|
|||
|
||||
let child_layout_id = child_element
|
||||
.as_mut()
|
||||
.map(|child_element| child_element.before_layout(cx));
|
||||
.map(|child_element| child_element.request_layout(cx));
|
||||
|
||||
let layout_id = cx.request_layout(
|
||||
&gpui::Style::default(),
|
||||
|
@ -143,21 +146,21 @@ impl<M: ManagedView> Element for RightClickMenu<M> {
|
|||
})
|
||||
}
|
||||
|
||||
fn after_layout(
|
||||
fn prepaint(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
before_layout: &mut Self::BeforeLayout,
|
||||
request_layout: &mut Self::RequestLayoutState,
|
||||
cx: &mut ElementContext,
|
||||
) -> Hitbox {
|
||||
cx.with_element_id(Some(self.id.clone()), |cx| {
|
||||
let hitbox = cx.insert_hitbox(bounds, false);
|
||||
|
||||
if let Some(child) = before_layout.child_element.as_mut() {
|
||||
child.after_layout(cx);
|
||||
if let Some(child) = request_layout.child_element.as_mut() {
|
||||
child.prepaint(cx);
|
||||
}
|
||||
|
||||
if let Some(menu) = before_layout.menu_element.as_mut() {
|
||||
menu.after_layout(cx);
|
||||
if let Some(menu) = request_layout.menu_element.as_mut() {
|
||||
menu.prepaint(cx);
|
||||
}
|
||||
|
||||
hitbox
|
||||
|
@ -167,16 +170,16 @@ impl<M: ManagedView> Element for RightClickMenu<M> {
|
|||
fn paint(
|
||||
&mut self,
|
||||
_bounds: Bounds<gpui::Pixels>,
|
||||
before_layout: &mut Self::BeforeLayout,
|
||||
hitbox: &mut Self::AfterLayout,
|
||||
request_layout: &mut Self::RequestLayoutState,
|
||||
hitbox: &mut Self::PrepaintState,
|
||||
cx: &mut ElementContext,
|
||||
) {
|
||||
self.with_element_state(cx, |this, element_state, cx| {
|
||||
if let Some(mut child) = before_layout.child_element.take() {
|
||||
if let Some(mut child) = request_layout.child_element.take() {
|
||||
child.paint(cx);
|
||||
}
|
||||
|
||||
if let Some(mut menu) = before_layout.menu_element.take() {
|
||||
if let Some(mut menu) = request_layout.menu_element.take() {
|
||||
menu.paint(cx);
|
||||
return;
|
||||
}
|
||||
|
@ -188,7 +191,7 @@ impl<M: ManagedView> Element for RightClickMenu<M> {
|
|||
let attach = this.attach;
|
||||
let menu = element_state.menu.clone();
|
||||
let position = element_state.position.clone();
|
||||
let child_layout_id = before_layout.child_layout_id;
|
||||
let child_layout_id = request_layout.child_layout_id;
|
||||
let child_bounds = cx.layout_bounds(child_layout_id.unwrap());
|
||||
|
||||
let hitbox_id = hitbox.id;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue