gpui: Rework overlay element (#9911)

Bennet Bo Fenner and Antonio created

There was a problem using deferred draws with `overlay` and tooltips at
the same time.

The `overlay` element was removed and was split up into two separate
elements
- `deferred`
- `anchored` - Mimics the `overlay` behavior but does not render its
children as deferred

`tooltip_container` does not defer its drawing anymore and only uses
`anchored`.

/cc @as-cii 


Release Notes:
- Fixed tooltip for the recent projects popover not showing anymore

---------

Co-authored-by: Antonio <antonio@zed.dev>

Change summary

crates/collab_ui/src/collab_panel.rs               |  22 +-
crates/collab_ui/src/collab_panel/channel_modal.rs |  12 
crates/editor/src/element.rs                       |  30 ++-
crates/gpui/src/elements/anchored.rs               | 109 +++++++--------
crates/gpui/src/elements/mod.rs                    |   4 
crates/gpui/src/window/element_cx.rs               |  14 +-
crates/project_panel/src/project_panel.rs          |  20 +-
crates/terminal_view/src/terminal_view.rs          |  16 +-
crates/ui/src/components/popover_menu.rs           |  12 -
crates/ui/src/components/right_click_menu.rs       |  19 +-
crates/ui/src/components/tooltip.rs                |   6 
crates/workspace/src/pane.rs                       |  16 +
12 files changed, 144 insertions(+), 136 deletions(-)

Detailed changes

crates/collab_ui/src/collab_panel.rs 🔗

@@ -14,12 +14,12 @@ use db::kvp::KEY_VALUE_STORE;
 use editor::{Editor, EditorElement, EditorStyle};
 use fuzzy::{match_strings, StringMatchCandidate};
 use gpui::{
-    actions, canvas, div, fill, list, overlay, point, prelude::*, px, AnyElement, AppContext,
-    AsyncWindowContext, Bounds, ClickEvent, ClipboardItem, DismissEvent, Div, EventEmitter,
-    FocusHandle, FocusableView, FontStyle, FontWeight, InteractiveElement, IntoElement, ListOffset,
-    ListState, Model, MouseDownEvent, ParentElement, Pixels, Point, PromptLevel, Render,
-    SharedString, Styled, Subscription, Task, TextStyle, View, ViewContext, VisualContext,
-    WeakView, WhiteSpace,
+    actions, anchored, canvas, deferred, div, fill, list, point, prelude::*, px, AnyElement,
+    AppContext, AsyncWindowContext, Bounds, ClickEvent, ClipboardItem, DismissEvent, Div,
+    EventEmitter, FocusHandle, FocusableView, FontStyle, FontWeight, InteractiveElement,
+    IntoElement, ListOffset, ListState, Model, MouseDownEvent, ParentElement, Pixels, Point,
+    PromptLevel, Render, SharedString, Styled, Subscription, Task, TextStyle, View, ViewContext,
+    VisualContext, WeakView, WhiteSpace,
 };
 use menu::{Cancel, Confirm, SecondaryConfirm, SelectNext, SelectPrev};
 use project::{Fs, Project};
@@ -2767,10 +2767,12 @@ impl Render for CollabPanel {
                 self.render_signed_in(cx)
             })
             .children(self.context_menu.as_ref().map(|(menu, position, _)| {
-                overlay()
-                    .position(*position)
-                    .anchor(gpui::AnchorCorner::TopLeft)
-                    .child(menu.clone())
+                deferred(
+                    anchored()
+                        .position(*position)
+                        .anchor(gpui::AnchorCorner::TopLeft)
+                        .child(menu.clone()),
+                )
             }))
     }
 }

crates/collab_ui/src/collab_panel/channel_modal.rs 🔗

@@ -5,9 +5,9 @@ use client::{
 };
 use fuzzy::{match_strings, StringMatchCandidate};
 use gpui::{
-    actions, div, overlay, AppContext, ClipboardItem, DismissEvent, EventEmitter, FocusableView,
-    Model, ParentElement, Render, Styled, Subscription, Task, View, ViewContext, VisualContext,
-    WeakView,
+    actions, anchored, deferred, div, AppContext, ClipboardItem, DismissEvent, EventEmitter,
+    FocusableView, Model, ParentElement, Render, Styled, Subscription, Task, View, ViewContext,
+    VisualContext, WeakView,
 };
 use picker::{Picker, PickerDelegate};
 use std::sync::Arc;
@@ -408,11 +408,11 @@ impl PickerDelegate for ChannelModalDelegate {
                             .when(is_me, |el| el.child(Label::new("You").color(Color::Muted)))
                             .children(
                                 if let (Some((menu, _)), true) = (&self.context_menu, selected) {
-                                    Some(
-                                        overlay()
+                                    Some(deferred(
+                                        anchored()
                                             .anchor(gpui::AnchorCorner::TopRight)
                                             .child(menu.clone()),
-                                    )
+                                    ))
                                 } else {
                                     None
                                 },

crates/editor/src/element.rs 🔗

@@ -20,13 +20,14 @@ use anyhow::Result;
 use collections::{BTreeMap, HashMap};
 use git::{blame::BlameEntry, diff::DiffHunkStatus, Oid};
 use gpui::{
-    div, fill, outline, overlay, point, px, quad, relative, size, svg, transparent_black, Action,
-    AnchorCorner, AnyElement, AnyView, AvailableSpace, Bounds, ClipboardItem, ContentMask, Corners,
-    CursorStyle, DispatchPhase, Edges, Element, ElementContext, ElementInputHandler, Entity,
-    Hitbox, Hsla, InteractiveElement, IntoElement, ModifiersChangedEvent, MouseButton,
-    MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, ScrollDelta,
-    ScrollWheelEvent, ShapedLine, SharedString, Size, Stateful, StatefulInteractiveElement, Style,
-    Styled, TextRun, TextStyle, TextStyleRefinement, View, ViewContext, WindowContext,
+    anchored, deferred, div, fill, outline, point, px, quad, relative, size, svg,
+    transparent_black, Action, AnchorCorner, AnyElement, AnyView, AvailableSpace, Bounds,
+    ClipboardItem, ContentMask, Corners, CursorStyle, DispatchPhase, Edges, Element,
+    ElementContext, ElementInputHandler, Entity, Hitbox, Hsla, InteractiveElement, IntoElement,
+    ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent,
+    ParentElement, Pixels, ScrollDelta, ScrollWheelEvent, ShapedLine, SharedString, Size, Stateful,
+    StatefulInteractiveElement, Style, Styled, TextRun, TextStyle, TextStyleRefinement, View,
+    ViewContext, WindowContext,
 };
 use itertools::Itertools;
 use language::language_settings::ShowWhitespaceSetting;
@@ -1804,12 +1805,15 @@ impl EditorElement {
 
     fn layout_mouse_context_menu(&self, cx: &mut ElementContext) -> Option<AnyElement> {
         let mouse_context_menu = self.editor.read(cx).mouse_context_menu.as_ref()?;
-        let mut element = overlay()
-            .position(mouse_context_menu.position)
-            .child(mouse_context_menu.context_menu.clone())
-            .anchor(AnchorCorner::TopLeft)
-            .snap_to_window()
-            .into_any();
+        let mut element = deferred(
+            anchored()
+                .position(mouse_context_menu.position)
+                .child(mouse_context_menu.context_menu.clone())
+                .anchor(AnchorCorner::TopLeft)
+                .snap_to_window(),
+        )
+        .into_any();
+
         element.layout(gpui::Point::default(), AvailableSpace::min_size(), cx);
         Some(element)
     }

crates/gpui/src/elements/overlay.rs → crates/gpui/src/elements/anchored.rs 🔗

@@ -6,71 +6,70 @@ use crate::{
     Pixels, Point, Size, Style,
 };
 
-/// The state that the overlay element uses to track its children.
-pub struct OverlayState {
+/// The state that the anchored element element uses to track its children.
+pub struct AnchoredState {
     child_layout_ids: SmallVec<[LayoutId; 4]>,
-    offset: Point<Pixels>,
 }
 
-/// An overlay element that can be used to display UI that
-/// floats on top of other UI elements.
-pub struct Overlay {
+/// An anchored element that can be used to display UI that
+/// will avoid overflowing the window bounds.
+pub struct Anchored {
     children: SmallVec<[AnyElement; 2]>,
     anchor_corner: AnchorCorner,
-    fit_mode: OverlayFitMode,
+    fit_mode: AnchoredFitMode,
     anchor_position: Option<Point<Pixels>>,
-    position_mode: OverlayPositionMode,
+    position_mode: AnchoredPositionMode,
 }
 
-/// overlay gives you a floating element that will avoid overflowing the window bounds.
+/// anchored gives you an element that will avoid overflowing the window bounds.
 /// Its children should have no margin to avoid measurement issues.
-pub fn overlay() -> Overlay {
-    Overlay {
+pub fn anchored() -> Anchored {
+    Anchored {
         children: SmallVec::new(),
         anchor_corner: AnchorCorner::TopLeft,
-        fit_mode: OverlayFitMode::SwitchAnchor,
+        fit_mode: AnchoredFitMode::SwitchAnchor,
         anchor_position: None,
-        position_mode: OverlayPositionMode::Window,
+        position_mode: AnchoredPositionMode::Window,
     }
 }
 
-impl Overlay {
-    /// Sets which corner of the overlay should be anchored to the current position.
+impl Anchored {
+    /// Sets which corner of the anchored element should be anchored to the current position.
     pub fn anchor(mut self, anchor: AnchorCorner) -> Self {
         self.anchor_corner = anchor;
         self
     }
 
     /// Sets the position in window coordinates
-    /// (otherwise the location the overlay is rendered is used)
+    /// (otherwise the location the anchored element is rendered is used)
     pub fn position(mut self, anchor: Point<Pixels>) -> Self {
         self.anchor_position = Some(anchor);
         self
     }
 
-    /// Sets the position mode for this overlay. Local will have this
-    /// interpret its [`Overlay::position`] as relative to the parent element.
+    /// Sets the position mode for this anchored element. Local will have this
+    /// interpret its [`Anchored::position`] as relative to the parent element.
     /// While Window will have it interpret the position as relative to the window.
-    pub fn position_mode(mut self, mode: OverlayPositionMode) -> Self {
+    pub fn position_mode(mut self, mode: AnchoredPositionMode) -> Self {
         self.position_mode = mode;
         self
     }
 
     /// Snap to window edge instead of switching anchor corner when an overflow would occur.
     pub fn snap_to_window(mut self) -> Self {
-        self.fit_mode = OverlayFitMode::SnapToWindow;
+        self.fit_mode = AnchoredFitMode::SnapToWindow;
         self
     }
 }
 
-impl ParentElement for Overlay {
+impl ParentElement for Anchored {
     fn extend(&mut self, elements: impl Iterator<Item = AnyElement>) {
         self.children.extend(elements)
     }
 }
 
-impl Element for Overlay {
-    type BeforeLayout = OverlayState;
+impl Element for Anchored {
+    type BeforeLayout = AnchoredState;
     type AfterLayout = ();
 
     fn before_layout(&mut self, cx: &mut ElementContext) -> (crate::LayoutId, Self::BeforeLayout) {
@@ -80,21 +79,15 @@ impl Element for Overlay {
             .map(|child| child.before_layout(cx))
             .collect::<SmallVec<_>>();
 
-        let overlay_style = Style {
+        let anchored_style = Style {
             position: Position::Absolute,
             display: Display::Flex,
             ..Style::default()
         };
 
-        let layout_id = cx.request_layout(&overlay_style, child_layout_ids.iter().copied());
+        let layout_id = cx.request_layout(&anchored_style, child_layout_ids.iter().copied());
 
-        (
-            layout_id,
-            OverlayState {
-                child_layout_ids,
-                offset: Point::default(),
-            },
-        )
+        (layout_id, AnchoredState { child_layout_ids })
     }
 
     fn after_layout(
@@ -128,7 +121,7 @@ impl Element for Overlay {
             size: cx.viewport_size(),
         };
 
-        if self.fit_mode == OverlayFitMode::SwitchAnchor {
+        if self.fit_mode == AnchoredFitMode::SwitchAnchor {
             let mut anchor_corner = self.anchor_corner;
 
             if desired.left() < limits.left() || desired.right() > limits.right() {
@@ -151,7 +144,7 @@ impl Element for Overlay {
             }
         }
 
-        // Snap the horizontal edges of the overlay to the horizontal edges of the window if
+        // Snap the horizontal edges of the anchored element to the horizontal edges of the window if
         // its horizontal bounds overflow, aligning to the left if it is wider than the limits.
         if desired.right() > limits.right() {
             desired.origin.x -= desired.right() - limits.right();
@@ -160,7 +153,7 @@ impl Element for Overlay {
             desired.origin.x = limits.origin.x;
         }
 
-        // Snap the vertical edges of the overlay to the vertical edges of the window if
+        // Snap the vertical edges of the anchored element to the vertical edges of the window if
         // its vertical bounds overflow, aligning to the top if it is taller than the limits.
         if desired.bottom() > limits.bottom() {
             desired.origin.y -= desired.bottom() - limits.bottom();
@@ -169,15 +162,14 @@ impl Element for Overlay {
             desired.origin.y = limits.origin.y;
         }
 
-        before_layout.offset = cx.element_offset() + desired.origin - bounds.origin;
-        before_layout.offset = point(
-            before_layout.offset.x.round(),
-            before_layout.offset.y.round(),
-        );
+        let offset = desired.origin - bounds.origin;
+        let offset = point(offset.x.round(), offset.y.round());
 
-        for child in self.children.drain(..) {
-            cx.defer_draw(child, before_layout.offset, 1);
-        }
+        cx.with_element_offset(offset, |cx| {
+            for child in &mut self.children {
+                child.after_layout(cx);
+            }
+        })
     }
 
     fn paint(
@@ -185,12 +177,15 @@ impl Element for Overlay {
         _bounds: crate::Bounds<crate::Pixels>,
         _before_layout: &mut Self::BeforeLayout,
         _after_layout: &mut Self::AfterLayout,
-        _cx: &mut ElementContext,
+        cx: &mut ElementContext,
     ) {
+        for child in &mut self.children {
+            child.paint(cx);
+        }
     }
 }
 
-impl IntoElement for Overlay {
+impl IntoElement for Anchored {
     type Element = Self;
 
     fn into_element(self) -> Self::Element {
@@ -203,25 +198,25 @@ enum Axis {
     Vertical,
 }
 
-/// Which algorithm to use when fitting the overlay to be inside the window.
+/// Which algorithm to use when fitting the anchored element to be inside the window.
 #[derive(Copy, Clone, PartialEq)]
-pub enum OverlayFitMode {
-    /// Snap the overlay to the window edge
+pub enum AnchoredFitMode {
+    /// Snap the anchored element to the window edge
     SnapToWindow,
-    /// Switch which corner anchor this overlay is attached to
+    /// Switch which corner anchor this anchored element is attached to
     SwitchAnchor,
 }
 
-/// Which algorithm to use when positioning the overlay.
+/// Which algorithm to use when positioning the anchored element.
 #[derive(Copy, Clone, PartialEq)]
-pub enum OverlayPositionMode {
-    /// Position the overlay relative to the window
+pub enum AnchoredPositionMode {
+    /// Position the anchored element relative to the window
     Window,
-    /// Position the overlay relative to its parent
+    /// Position the anchored element relative to its parent
     Local,
 }
 
-impl OverlayPositionMode {
+impl AnchoredPositionMode {
     fn get_position_and_bounds(
         &self,
         anchor_position: Option<Point<Pixels>>,
@@ -230,12 +225,12 @@ impl OverlayPositionMode {
         bounds: Bounds<Pixels>,
     ) -> (Point<Pixels>, Bounds<Pixels>) {
         match self {
-            OverlayPositionMode::Window => {
+            AnchoredPositionMode::Window => {
                 let anchor_position = anchor_position.unwrap_or(bounds.origin);
                 let bounds = anchor_corner.get_bounds(anchor_position, size);
                 (anchor_position, bounds)
             }
-            OverlayPositionMode::Local => {
+            AnchoredPositionMode::Local => {
                 let anchor_position = anchor_position.unwrap_or_default();
                 let bounds = anchor_corner.get_bounds(bounds.origin + anchor_position, size);
                 (anchor_position, bounds)
@@ -244,7 +239,7 @@ impl OverlayPositionMode {
     }
 }
 
-/// Which corner of the overlay should be considered the anchor.
+/// Which corner of the anchored element should be considered the anchor.
 #[derive(Clone, Copy, PartialEq, Eq)]
 pub enum AnchorCorner {
     /// The top left corner

crates/gpui/src/elements/mod.rs 🔗

@@ -1,21 +1,21 @@
+mod anchored;
 mod animation;
 mod canvas;
 mod deferred;
 mod div;
 mod img;
 mod list;
-mod overlay;
 mod svg;
 mod text;
 mod uniform_list;
 
+pub use anchored::*;
 pub use animation::*;
 pub use canvas::*;
 pub use deferred::*;
 pub use div::*;
 pub use img::*;
 pub use list::*;
-pub use overlay::*;
 pub use svg::*;
 pub use text::*;
 pub use uniform_list::*;

crates/gpui/src/window/element_cx.rs 🔗

@@ -356,6 +356,11 @@ impl<'a> ElementContext<'a> {
         let mut root_element = self.window.root_view.as_ref().unwrap().clone().into_any();
         root_element.layout(Point::default(), self.window.viewport_size.into(), self);
 
+        let mut sorted_deferred_draws =
+            (0..self.window.next_frame.deferred_draws.len()).collect::<SmallVec<[_; 8]>>();
+        sorted_deferred_draws.sort_by_key(|ix| self.window.next_frame.deferred_draws[*ix].priority);
+        self.layout_deferred_draws(&sorted_deferred_draws);
+
         let mut prompt_element = None;
         let mut active_drag_element = None;
         let mut tooltip_element = None;
@@ -380,17 +385,14 @@ impl<'a> ElementContext<'a> {
             tooltip_element = Some(element);
         }
 
-        let mut sorted_deferred_draws =
-            (0..self.window.next_frame.deferred_draws.len()).collect::<SmallVec<[_; 8]>>();
-        sorted_deferred_draws.sort_by_key(|ix| self.window.next_frame.deferred_draws[*ix].priority);
-        self.layout_deferred_draws(&sorted_deferred_draws);
-
         self.window.mouse_hit_test = self.window.next_frame.hit_test(self.window.mouse_position);
 
         // Now actually paint the elements.
         self.window.draw_phase = DrawPhase::Paint;
         root_element.paint(self);
 
+        self.paint_deferred_draws(&sorted_deferred_draws);
+
         if let Some(mut prompt_element) = prompt_element {
             prompt_element.paint(self)
         } else if let Some(mut drag_element) = active_drag_element {
@@ -398,8 +400,6 @@ impl<'a> ElementContext<'a> {
         } else if let Some(mut tooltip_element) = tooltip_element {
             tooltip_element.paint(self);
         }
-
-        self.paint_deferred_draws(&sorted_deferred_draws);
     }
 
     fn layout_deferred_draws(&mut self, deferred_draw_indices: &[usize]) {

crates/project_panel/src/project_panel.rs 🔗

@@ -10,11 +10,11 @@ use file_associations::FileAssociations;
 use anyhow::{anyhow, Result};
 use collections::{hash_map, HashMap};
 use gpui::{
-    actions, div, impl_actions, overlay, px, uniform_list, Action, AppContext, AssetSource,
-    AsyncWindowContext, ClipboardItem, DismissEvent, Div, EventEmitter, FocusHandle, FocusableView,
-    InteractiveElement, KeyContext, Model, MouseButton, MouseDownEvent, ParentElement, Pixels,
-    Point, PromptLevel, Render, Stateful, Styled, Subscription, Task, UniformListScrollHandle,
-    View, ViewContext, VisualContext as _, WeakView, WindowContext,
+    actions, anchored, deferred, div, impl_actions, px, uniform_list, Action, AppContext,
+    AssetSource, AsyncWindowContext, ClipboardItem, DismissEvent, Div, EventEmitter, FocusHandle,
+    FocusableView, InteractiveElement, KeyContext, Model, MouseButton, MouseDownEvent,
+    ParentElement, Pixels, Point, PromptLevel, Render, Stateful, Styled, Subscription, Task,
+    UniformListScrollHandle, View, ViewContext, VisualContext as _, WeakView, WindowContext,
 };
 use menu::{Confirm, SelectNext, SelectPrev};
 use project::{
@@ -1585,10 +1585,12 @@ impl Render for ProjectPanel {
                     .track_scroll(self.scroll_handle.clone()),
                 )
                 .children(self.context_menu.as_ref().map(|(menu, position, _)| {
-                    overlay()
-                        .position(*position)
-                        .anchor(gpui::AnchorCorner::TopLeft)
-                        .child(menu.clone())
+                    deferred(
+                        anchored()
+                            .position(*position)
+                            .anchor(gpui::AnchorCorner::TopLeft)
+                            .child(menu.clone()),
+                    )
                 }))
         } else {
             v_flex()

crates/terminal_view/src/terminal_view.rs 🔗

@@ -6,9 +6,9 @@ use collections::HashSet;
 use editor::{scroll::Autoscroll, Editor};
 use futures::{stream::FuturesUnordered, StreamExt};
 use gpui::{
-    div, impl_actions, overlay, AnyElement, AppContext, DismissEvent, EventEmitter, FocusHandle,
-    FocusableView, KeyContext, KeyDownEvent, Keystroke, Model, MouseButton, MouseDownEvent, Pixels,
-    Render, Styled, Subscription, Task, View, VisualContext, WeakView,
+    anchored, deferred, div, impl_actions, AnyElement, AppContext, DismissEvent, EventEmitter,
+    FocusHandle, FocusableView, KeyContext, KeyDownEvent, Keystroke, Model, MouseButton,
+    MouseDownEvent, Pixels, Render, Styled, Subscription, Task, View, VisualContext, WeakView,
 };
 use language::Bias;
 use persistence::TERMINAL_DB;
@@ -765,10 +765,12 @@ impl Render for TerminalView {
                 )),
             )
             .children(self.context_menu.as_ref().map(|(menu, position, _)| {
-                overlay()
-                    .position(*position)
-                    .anchor(gpui::AnchorCorner::TopLeft)
-                    .child(menu.clone())
+                deferred(
+                    anchored()
+                        .position(*position)
+                        .anchor(gpui::AnchorCorner::TopLeft)
+                        .child(menu.clone()),
+                )
             }))
     }
 }

crates/ui/src/components/popover_menu.rs 🔗

@@ -1,7 +1,7 @@
 use std::{cell::RefCell, rc::Rc};
 
 use gpui::{
-    div, overlay, point, prelude::FluentBuilder, px, AnchorCorner, AnyElement, Bounds,
+    anchored, deferred, div, point, prelude::FluentBuilder, px, AnchorCorner, AnyElement, Bounds,
     DismissEvent, DispatchPhase, Element, ElementContext, ElementId, HitboxId, InteractiveElement,
     IntoElement, LayoutId, ManagedView, MouseDownEvent, ParentElement, Pixels, Point, View,
     VisualContext, WindowContext,
@@ -176,17 +176,15 @@ impl<M: ManagedView> Element for PopoverMenu<M> {
             let mut menu_layout_id = None;
 
             let menu_element = element_state.menu.borrow_mut().as_mut().map(|menu| {
-                let mut overlay = overlay().snap_to_window().anchor(this.anchor);
-
+                let mut anchored = anchored().snap_to_window().anchor(this.anchor);
                 if let Some(child_bounds) = element_state.child_bounds {
-                    overlay = overlay.position(
+                    anchored = anchored.position(
                         this.resolved_attach().corner(child_bounds) + this.resolved_offset(cx),
                     );
                 }
+                let mut element =
+                    deferred(anchored.child(div().occlude().child(menu.clone()))).into_any();
 
-                let mut element = overlay
-                    .child(div().occlude().child(menu.clone()))
-                    .into_any();
                 menu_layout_id = Some(element.before_layout(cx));
                 element
             });

crates/ui/src/components/right_click_menu.rs 🔗

@@ -1,9 +1,10 @@
 use std::{cell::RefCell, rc::Rc};
 
 use gpui::{
-    div, overlay, AnchorCorner, AnyElement, Bounds, DismissEvent, DispatchPhase, Element,
-    ElementContext, ElementId, Hitbox, InteractiveElement, IntoElement, LayoutId, ManagedView,
-    MouseButton, MouseDownEvent, ParentElement, Pixels, Point, View, VisualContext, WindowContext,
+    anchored, deferred, div, AnchorCorner, AnyElement, Bounds, DismissEvent, DispatchPhase,
+    Element, ElementContext, ElementId, Hitbox, InteractiveElement, IntoElement, LayoutId,
+    ManagedView, MouseButton, MouseDownEvent, ParentElement, Pixels, Point, View, VisualContext,
+    WindowContext,
 };
 
 pub struct RightClickMenu<M: ManagedView> {
@@ -103,15 +104,15 @@ impl<M: ManagedView> Element for RightClickMenu<M> {
             let mut menu_layout_id = None;
 
             let menu_element = element_state.menu.borrow_mut().as_mut().map(|menu| {
-                let mut overlay = overlay().snap_to_window();
+                let mut anchored = anchored().snap_to_window();
                 if let Some(anchor) = this.anchor {
-                    overlay = overlay.anchor(anchor);
+                    anchored = anchored.anchor(anchor);
                 }
-                overlay = overlay.position(*element_state.position.borrow());
+                anchored = anchored.position(*element_state.position.borrow());
+
+                let mut element =
+                    deferred(anchored.child(div().occlude().child(menu.clone()))).into_any();
 
-                let mut element = overlay
-                    .child(div().occlude().child(menu.clone()))
-                    .into_any();
                 menu_layout_id = Some(element.before_layout(cx));
                 element
             });

crates/ui/src/components/tooltip.rs 🔗

@@ -1,4 +1,4 @@
-use gpui::{overlay, Action, AnyView, IntoElement, Render, VisualContext};
+use gpui::{anchored, Action, AnyView, IntoElement, Render, VisualContext};
 use settings::Settings;
 use theme::ThemeSettings;
 
@@ -90,8 +90,8 @@ pub fn tooltip_container<V>(
     f: impl FnOnce(Div, &mut ViewContext<V>) -> Div,
 ) -> impl IntoElement {
     let ui_font = ThemeSettings::get_global(cx).ui_font.family.clone();
-    overlay().child(
-        // padding to avoid mouse cursor
+    // padding to avoid mouse cursor
+    anchored().child(
         div().pl_2().pt_2p5().child(
             v_flex()
                 .elevation_2(cx)

crates/workspace/src/pane.rs 🔗

@@ -8,11 +8,11 @@ use anyhow::Result;
 use collections::{HashMap, HashSet, VecDeque};
 use futures::{stream::FuturesUnordered, StreamExt};
 use gpui::{
-    actions, impl_actions, overlay, prelude::*, Action, AnchorCorner, AnyElement, AppContext,
-    AsyncWindowContext, ClickEvent, DismissEvent, Div, DragMoveEvent, EntityId, EventEmitter,
-    ExternalPaths, FocusHandle, FocusableView, Model, MouseButton, NavigationDirection, Pixels,
-    Point, PromptLevel, Render, ScrollHandle, Subscription, Task, View, ViewContext, VisualContext,
-    WeakFocusHandle, WeakView, WindowContext,
+    actions, anchored, deferred, impl_actions, prelude::*, Action, AnchorCorner, AnyElement,
+    AppContext, AsyncWindowContext, ClickEvent, DismissEvent, Div, DragMoveEvent, EntityId,
+    EventEmitter, ExternalPaths, FocusHandle, FocusableView, Model, MouseButton,
+    NavigationDirection, Pixels, Point, PromptLevel, Render, ScrollHandle, Subscription, Task,
+    View, ViewContext, VisualContext, WeakFocusHandle, WeakView, WindowContext,
 };
 use parking_lot::Mutex;
 use project::{Project, ProjectEntryId, ProjectPath};
@@ -1567,7 +1567,11 @@ impl Pane {
             .bottom_0()
             .right_0()
             .size_0()
-            .child(overlay().anchor(AnchorCorner::TopRight).child(menu.clone()))
+            .child(deferred(
+                anchored()
+                    .anchor(AnchorCorner::TopRight)
+                    .child(menu.clone()),
+            ))
     }
 
     pub fn set_zoomed(&mut self, zoomed: bool, cx: &mut ViewContext<Self>) {