Return `impl IntoElement` in `Render` and `RenderOnce` traits (#3839)

Nathan Sobo created

- Returning `IntoElement` because it's more universal than `Element` and
allows us to easily return components.
- Using `impl IntoElement` in `RenderOnce` for consistency, which
requires `Component` to allocate an `AnyElement`. We use a bump
allocated arena anyway for these, and my benchmark doesn't show an
impact.


![frame-time-histogram](https://github.com/zed-industries/zed/assets/1789/d3889cca-9ebc-4d72-aa68-34a5be3bad3c)

In this histogram, frames-0 is this PR, frames-1 is main.

Release Notes:

- N/A

Change summary

crates/activity_indicator2/src/activity_indicator.rs               |   2 
crates/assistant2/src/assistant_panel.rs                           |   2 
crates/auto_update2/src/update_notification.rs                     |   4 
crates/breadcrumbs2/src/breadcrumbs.rs                             |   2 
crates/collab_ui2/src/channel_view.rs                              |   2 
crates/collab_ui2/src/chat_panel.rs                                |   2 
crates/collab_ui2/src/chat_panel/message_editor.rs                 |   4 
crates/collab_ui2/src/collab_panel.rs                              |   2 
crates/collab_ui2/src/collab_panel/channel_modal.rs                |   2 
crates/collab_ui2/src/collab_panel/contact_finder.rs               |   2 
crates/collab_ui2/src/collab_titlebar_item.rs                      |   2 
crates/collab_ui2/src/face_pile.rs                                 |   6 
crates/collab_ui2/src/notification_panel.rs                        |   4 
crates/collab_ui2/src/notifications/incoming_call_notification.rs  |   2 
crates/collab_ui2/src/notifications/project_shared_notification.rs |   2 
crates/command_palette2/src/command_palette.rs                     |   2 
crates/copilot2/src/sign_in.rs                                     |   2 
crates/copilot_button2/src/copilot_button.rs                       |   4 
crates/diagnostics2/src/items.rs                                   |   2 
crates/diagnostics2/src/toolbar_controls.rs                        |   2 
crates/editor2/src/editor.rs                                       |   2 
crates/editor2/src/items.rs                                        |   2 
crates/feedback2/src/deploy_feedback_button.rs                     |   2 
crates/feedback2/src/feedback_modal.rs                             |   2 
crates/file_finder2/src/file_finder.rs                             |   2 
crates/go_to_line2/src/go_to_line.rs                               |   2 
crates/gpui2/src/app/test_context.rs                               |   6 
crates/gpui2/src/element.rs                                        |  73 
crates/gpui2/src/elements/div.rs                                   |   2 
crates/gpui2/src/elements/overlay.rs                               |   2 
crates/gpui2/src/interactive.rs                                    |   2 
crates/gpui2/src/view.rs                                           |   4 
crates/language_selector2/src/active_buffer_language.rs            |   6 
crates/language_selector2/src/language_selector.rs                 |   2 
crates/language_tools2/src/lsp_log.rs                              |  12 
crates/language_tools2/src/syntax_tree_view.rs                     |   6 
crates/outline2/src/outline.rs                                     |   2 
crates/picker2/src/picker2.rs                                      |   2 
crates/project_panel2/src/project_panel.rs                         |   2 
crates/quick_action_bar2/src/quick_action_bar.rs                   |   6 
crates/recent_projects2/src/highlighted_workspace_location.rs      |   4 
crates/recent_projects2/src/recent_projects.rs                     |   2 
crates/search2/src/buffer_search.rs                                |   2 
crates/search2/src/project_search.rs                               |   4 
crates/story/src/story.rs                                          |  15 
crates/storybook2/src/stories/auto_height_editor.rs                |   4 
crates/storybook2/src/stories/cursor.rs                            |   2 
crates/storybook2/src/stories/focus.rs                             |   2 
crates/storybook2/src/stories/kitchen_sink.rs                      |   2 
crates/storybook2/src/stories/overflow_scroll.rs                   |   2 
crates/storybook2/src/stories/picker.rs                            |   2 
crates/storybook2/src/stories/scroll.rs                            |   2 
crates/storybook2/src/stories/text.rs                              |   6 
crates/storybook2/src/stories/viewport_units.rs                    |   2 
crates/storybook2/src/stories/z_index.rs                           |   6 
crates/storybook2/src/storybook2.rs                                |   2 
crates/terminal_view2/src/terminal_panel.rs                        |   8 
crates/terminal_view2/src/terminal_view.rs                         |   2 
crates/theme2/src/styles/stories/color.rs                          |   2 
crates/theme2/src/styles/stories/players.rs                        |   4 
crates/theme_selector2/src/theme_selector.rs                       |   2 
crates/ui2/src/components/avatar.rs                                |   6 
crates/ui2/src/components/button/button.rs                         |   5 
crates/ui2/src/components/button/button_icon.rs                    |   4 
crates/ui2/src/components/button/button_like.rs                    |   6 
crates/ui2/src/components/button/icon_button.rs                    |   4 
crates/ui2/src/components/button/toggle_button.rs                  |   4 
crates/ui2/src/components/checkbox.rs                              |   6 
crates/ui2/src/components/context_menu.rs                          |   2 
crates/ui2/src/components/disclosure.rs                            |   4 
crates/ui2/src/components/divider.rs                               |   6 
crates/ui2/src/components/icon.rs                                  |   6 
crates/ui2/src/components/indicator.rs                             |   6 
crates/ui2/src/components/keybinding.rs                            |  14 
crates/ui2/src/components/label/highlighted_label.rs               |   4 
crates/ui2/src/components/label/label.rs                           |   4 
crates/ui2/src/components/label/label_like.rs                      |   6 
crates/ui2/src/components/list/list.rs                             |   6 
crates/ui2/src/components/list/list_header.rs                      |   7 
crates/ui2/src/components/list/list_item.rs                        |   8 
crates/ui2/src/components/list/list_separator.rs                   |   6 
crates/ui2/src/components/list/list_sub_header.rs                  |   6 
crates/ui2/src/components/popover.rs                               |   6 
crates/ui2/src/components/popover_menu.rs                          |   4 
crates/ui2/src/components/right_click_menu.rs                      |   4 
crates/ui2/src/components/stories/avatar.rs                        |   2 
crates/ui2/src/components/stories/button.rs                        |   2 
crates/ui2/src/components/stories/checkbox.rs                      |   2 
crates/ui2/src/components/stories/context_menu.rs                  |   2 
crates/ui2/src/components/stories/disclosure.rs                    |   2 
crates/ui2/src/components/stories/icon.rs                          |   2 
crates/ui2/src/components/stories/icon_button.rs                   |   2 
crates/ui2/src/components/stories/keybinding.rs                    |   2 
crates/ui2/src/components/stories/label.rs                         |   2 
crates/ui2/src/components/stories/list.rs                          |   2 
crates/ui2/src/components/stories/list_header.rs                   |   2 
crates/ui2/src/components/stories/list_item.rs                     |   2 
crates/ui2/src/components/stories/tab.rs                           |   2 
crates/ui2/src/components/stories/tab_bar.rs                       |   2 
crates/ui2/src/components/stories/toggle_button.rs                 |   2 
crates/ui2/src/components/tab.rs                                   |   5 
crates/ui2/src/components/tab_bar.rs                               |   6 
crates/ui2/src/components/tooltip.rs                               |   2 
crates/vcs_menu2/src/lib.rs                                        |   2 
crates/vim2/src/mode_indicator.rs                                  |   2 
crates/welcome2/src/base_keymap_picker.rs                          |   2 
crates/welcome2/src/welcome.rs                                     |   2 
crates/workspace2/src/dock.rs                                      |   6 
crates/workspace2/src/item.rs                                      |   4 
crates/workspace2/src/modal_layer.rs                               |   2 
crates/workspace2/src/notifications.rs                             |   2 
crates/workspace2/src/pane.rs                                      |   4 
crates/workspace2/src/shared_screen.rs                             |   2 
crates/workspace2/src/status_bar.rs                                |   2 
crates/workspace2/src/toolbar.rs                                   |   2 
crates/workspace2/src/workspace2.rs                                |   4 
crates/workspace2_ui/src/titlebar.rs                               | 442 
117 files changed, 631 insertions(+), 292 deletions(-)

Detailed changes

crates/activity_indicator2/src/activity_indicator.rs 🔗

@@ -304,7 +304,7 @@ impl ActivityIndicator {
 impl EventEmitter<Event> for ActivityIndicator {}
 
 impl Render for ActivityIndicator {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
         let content = self.content_to_render(cx);
 
         let mut result = h_stack()

crates/assistant2/src/assistant_panel.rs 🔗

@@ -1101,7 +1101,7 @@ fn build_api_key_editor(cx: &mut ViewContext<AssistantPanel>) -> View<Editor> {
 }
 
 impl Render for AssistantPanel {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
         if let Some(api_key_editor) = self.api_key_editor.clone() {
             v_stack()
                 .on_action(cx.listener(AssistantPanel::save_credentials))

crates/auto_update2/src/update_notification.rs 🔗

@@ -1,5 +1,5 @@
 use gpui::{
-    div, DismissEvent, Element, EventEmitter, InteractiveElement, ParentElement, Render,
+    div, DismissEvent, EventEmitter, InteractiveElement, IntoElement, ParentElement, Render,
     SemanticVersion, StatefulInteractiveElement, Styled, ViewContext,
 };
 use menu::Cancel;
@@ -13,7 +13,7 @@ pub struct UpdateNotification {
 impl EventEmitter<DismissEvent> for UpdateNotification {}
 
 impl Render for UpdateNotification {
-    fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> impl Element {
+    fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> impl IntoElement {
         let app_name = cx.global::<ReleaseChannel>().display_name();
 
         v_stack()

crates/breadcrumbs2/src/breadcrumbs.rs 🔗

@@ -30,7 +30,7 @@ impl Breadcrumbs {
 impl EventEmitter<ToolbarItemEvent> for Breadcrumbs {}
 
 impl Render for Breadcrumbs {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
         let element = h_stack().text_ui();
         let Some(active_item) = self.active_item.as_ref() else {
             return element;

crates/collab_ui2/src/channel_view.rs 🔗

@@ -222,7 +222,7 @@ impl ChannelView {
 impl EventEmitter<EditorEvent> for ChannelView {}
 
 impl Render for ChannelView {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
         self.editor.clone()
     }
 }

crates/collab_ui2/src/chat_panel.rs 🔗

@@ -549,7 +549,7 @@ impl ChatPanel {
 impl EventEmitter<Event> for ChatPanel {}
 
 impl Render for ChatPanel {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
         div()
             .full()
             .child(if self.client.user_id().is_some() {

crates/collab_ui2/src/chat_panel/message_editor.rs 🔗

@@ -3,7 +3,7 @@ use client::UserId;
 use collections::HashMap;
 use editor::{AnchorRangeExt, Editor};
 use gpui::{
-    AsyncWindowContext, Element, FocusableView, Model, Render, SharedString, Task, View,
+    AsyncWindowContext, FocusableView, IntoElement, Model, Render, SharedString, Task, View,
     ViewContext, WeakView,
 };
 use language::{language_settings::SoftWrap, Buffer, BufferSnapshot, LanguageRegistry};
@@ -196,7 +196,7 @@ impl MessageEditor {
 }
 
 impl Render for MessageEditor {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
         self.editor.to_any()
     }
 }

crates/collab_ui2/src/collab_panel.rs 🔗

@@ -2323,7 +2323,7 @@ fn render_tree_branch(is_last: bool, cx: &mut WindowContext) -> impl IntoElement
 }
 
 impl Render for CollabPanel {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
         v_stack()
             .key_context("CollabPanel")
             .on_action(cx.listener(CollabPanel::cancel))

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

@@ -142,7 +142,7 @@ impl FocusableView for ChannelModal {
 }
 
 impl Render for ChannelModal {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
         let channel_store = self.channel_store.read(cx);
         let Some(channel) = channel_store.channel_for_id(self.channel_id) else {
             return div();

crates/collab_ui2/src/collab_panel/contact_finder.rs 🔗

@@ -35,7 +35,7 @@ impl ContactFinder {
 }
 
 impl Render for ContactFinder {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
         v_stack()
             .elevation_3(cx)
             .child(

crates/collab_ui2/src/collab_titlebar_item.rs 🔗

@@ -56,7 +56,7 @@ pub struct CollabTitlebarItem {
 }
 
 impl Render for CollabTitlebarItem {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
         let room = ActiveCall::global(cx).read(cx).room().cloned();
         let current_user = self.user_store.read(cx).current_user();
         let client = self.client.clone();

crates/collab_ui2/src/face_pile.rs 🔗

@@ -1,5 +1,5 @@
 use gpui::{
-    div, AnyElement, Div, ElementId, IntoElement, ParentElement, RenderOnce, Styled, WindowContext,
+    div, AnyElement, ElementId, IntoElement, ParentElement, RenderOnce, Styled, WindowContext,
 };
 use smallvec::SmallVec;
 
@@ -9,9 +9,7 @@ pub struct FacePile {
 }
 
 impl RenderOnce for FacePile {
-    type Output = Div;
-
-    fn render(self, _: &mut WindowContext) -> Self::Output {
+    fn render(self, _: &mut WindowContext) -> impl IntoElement {
         let player_count = self.faces.len();
         let player_list = self.faces.into_iter().enumerate().map(|(ix, player)| {
             let isnt_last = ix < player_count - 1;

crates/collab_ui2/src/notification_panel.rs 🔗

@@ -540,7 +540,7 @@ impl NotificationPanel {
 }
 
 impl Render for NotificationPanel {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
         v_stack()
             .size_full()
             .child(
@@ -708,7 +708,7 @@ impl NotificationToast {
 }
 
 impl Render for NotificationToast {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
         let user = self.actor.clone();
 
         h_stack()

crates/collab_ui2/src/notifications/incoming_call_notification.rs 🔗

@@ -117,7 +117,7 @@ impl IncomingCallNotification {
 }
 
 impl Render for IncomingCallNotification {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
         // TODO: Is there a better place for us to initialize the font?
         let (ui_font, ui_font_size) = {
             let theme_settings = ThemeSettings::get_global(cx);

crates/collab_ui2/src/notifications/project_shared_notification.rs 🔗

@@ -118,7 +118,7 @@ impl ProjectSharedNotification {
 }
 
 impl Render for ProjectSharedNotification {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
         // TODO: Is there a better place for us to initialize the font?
         let (ui_font, ui_font_size) = {
             let theme_settings = ThemeSettings::get_global(cx);

crates/command_palette2/src/command_palette.rs 🔗

@@ -83,7 +83,7 @@ impl FocusableView for CommandPalette {
 }
 
 impl Render for CommandPalette {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
         v_stack().w(rems(34.)).child(self.picker.clone())
     }
 }

crates/copilot2/src/sign_in.rs 🔗

@@ -181,7 +181,7 @@ impl CopilotCodeVerification {
 }
 
 impl Render for CopilotCodeVerification {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
         let prompt = match &self.status {
             Status::SigningIn {
                 prompt: Some(prompt),

crates/copilot_button2/src/copilot_button.rs 🔗

@@ -3,7 +3,7 @@ use copilot::{Copilot, SignOut, Status};
 use editor::{scroll::autoscroll::Autoscroll, Editor};
 use fs::Fs;
 use gpui::{
-    div, Action, AnchorCorner, AppContext, AsyncWindowContext, Element, Entity, ParentElement,
+    div, Action, AnchorCorner, AppContext, AsyncWindowContext, Entity, IntoElement, ParentElement,
     Render, Subscription, View, ViewContext, WeakView, WindowContext,
 };
 use language::{
@@ -34,7 +34,7 @@ pub struct CopilotButton {
 }
 
 impl Render for CopilotButton {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
         let all_language_settings = all_language_settings(None, cx);
         if !all_language_settings.copilot.feature_enabled {
             return div();

crates/diagnostics2/src/items.rs 🔗

@@ -21,7 +21,7 @@ pub struct DiagnosticIndicator {
 }
 
 impl Render for DiagnosticIndicator {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
         let diagnostic_indicator = match (self.summary.error_count, self.summary.warning_count) {
             (0, 0) => h_stack().child(
                 IconElement::new(Icon::Check)

crates/diagnostics2/src/toolbar_controls.rs 🔗

@@ -9,7 +9,7 @@ pub struct ToolbarControls {
 }
 
 impl Render for ToolbarControls {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
         let include_warnings = self
             .editor
             .as_ref()

crates/editor2/src/editor.rs 🔗

@@ -9287,7 +9287,7 @@ impl FocusableView for Editor {
 }
 
 impl Render for Editor {
-    fn render<'a>(&mut self, cx: &mut ViewContext<'a, Self>) -> impl 'static + Element {
+    fn render<'a>(&mut self, cx: &mut ViewContext<'a, Self>) -> impl IntoElement {
         let settings = ThemeSettings::get_global(cx);
         let text_style = match self.mode {
             EditorMode::SingleLine | EditorMode::AutoHeight { .. } => TextStyle {

crates/editor2/src/items.rs 🔗

@@ -1193,7 +1193,7 @@ impl CursorPosition {
 }
 
 impl Render for CursorPosition {
-    fn render(&mut self, _: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, _: &mut ViewContext<Self>) -> impl IntoElement {
         div().when_some(self.position, |el, position| {
             let mut text = format!(
                 "{}{FILE_ROW_COLUMN_DELIMITER}{}",

crates/feedback2/src/deploy_feedback_button.rs 🔗

@@ -17,7 +17,7 @@ impl DeployFeedbackButton {
 }
 
 impl Render for DeployFeedbackButton {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
         let is_open = self
             .workspace
             .upgrade()

crates/feedback2/src/feedback_modal.rs 🔗

@@ -396,7 +396,7 @@ impl FeedbackModal {
 }
 
 impl Render for FeedbackModal {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
         self.update_submission_state(cx);
 
         let submit_button_text = if self.awaiting_submission() {

crates/file_finder2/src/file_finder.rs 🔗

@@ -118,7 +118,7 @@ impl FocusableView for FileFinder {
     }
 }
 impl Render for FileFinder {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
         v_stack().w(rems(34.)).child(self.picker.clone())
     }
 }

crates/go_to_line2/src/go_to_line.rs 🔗

@@ -152,7 +152,7 @@ impl GoToLine {
 }
 
 impl Render for GoToLine {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
         div()
             .elevation_2(cx)
             .key_context("GoToLine")

crates/gpui2/src/app/test_context.rs 🔗

@@ -1,7 +1,7 @@
 use crate::{
     div, Action, AnyView, AnyWindowHandle, AppCell, AppContext, AsyncAppContext,
-    BackgroundExecutor, Bounds, ClipboardItem, Context, Element, Entity, EventEmitter,
-    ForegroundExecutor, InputEvent, KeyDownEvent, Keystroke, Model, ModelContext, Pixels, Platform,
+    BackgroundExecutor, Bounds, ClipboardItem, Context, Entity, EventEmitter, ForegroundExecutor,
+    InputEvent, IntoElement, KeyDownEvent, Keystroke, Model, ModelContext, Pixels, Platform,
     PlatformWindow, Point, Render, Result, Size, Task, TestDispatcher, TestPlatform, TestWindow,
     TestWindowHandlers, TextSystem, View, ViewContext, VisualContext, WindowBounds, WindowContext,
     WindowHandle, WindowOptions,
@@ -731,7 +731,7 @@ impl AnyWindowHandle {
 pub struct EmptyView {}
 
 impl Render for EmptyView {
-    fn render(&mut self, _cx: &mut crate::ViewContext<Self>) -> impl Element {
+    fn render(&mut self, _cx: &mut crate::ViewContext<Self>) -> impl IntoElement {
         div()
     }
 }

crates/gpui2/src/element.rs 🔗

@@ -6,6 +6,10 @@ use derive_more::{Deref, DerefMut};
 pub(crate) use smallvec::SmallVec;
 use std::{any::Any, fmt::Debug};
 
+/// Implemented by types that participate in laying out and painting the contents of a window.
+/// Elements form a tree and are laid out according to web-based layout rules.
+/// Rather than calling methods on implementers of this trait directly, you'll usually call `into_any` to convert  them into an AnyElement, which manages state internally.
+/// You can create custom elements by implementing this trait.
 pub trait Element: 'static + IntoElement {
     type State: 'static;
 
@@ -107,15 +111,13 @@ pub trait IntoElement: Sized {
 }
 
 pub trait Render: 'static + Sized {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Element;
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement;
 }
 
 /// You can derive [IntoElement] on any type that implements this trait.
 /// It is used to allow views to be expressed in terms of abstract data.
 pub trait RenderOnce: 'static {
-    type Output: IntoElement;
-
-    fn render(self, cx: &mut WindowContext) -> Self::Output;
+    fn render(self, cx: &mut WindowContext) -> impl IntoElement;
 }
 
 pub trait ParentElement {
@@ -139,62 +141,29 @@ pub trait ParentElement {
     }
 }
 
-pub struct Component<C> {
-    component: Option<C>,
-}
-
-pub struct ComponentState<C: RenderOnce> {
-    rendered_element: Option<<C::Output as IntoElement>::Element>,
-    rendered_element_state: Option<<<C::Output as IntoElement>::Element as Element>::State>,
-}
+pub struct Component<C: RenderOnce>(Option<C>);
 
-impl<C> Component<C> {
+impl<C: RenderOnce> Component<C> {
     pub fn new(component: C) -> Self {
-        Component {
-            component: Some(component),
-        }
+        Component(Some(component))
     }
 }
 
 impl<C: RenderOnce> Element for Component<C> {
-    type State = ComponentState<C>;
+    type State = AnyElement;
 
     fn request_layout(
         &mut self,
-        state: Option<Self::State>,
+        _: Option<Self::State>,
         cx: &mut WindowContext,
     ) -> (LayoutId, Self::State) {
-        let mut element = self.component.take().unwrap().render(cx).into_element();
-        if let Some(element_id) = element.element_id() {
-            let layout_id =
-                cx.with_element_state(element_id, |state, cx| element.request_layout(state, cx));
-            let state = ComponentState {
-                rendered_element: Some(element),
-                rendered_element_state: None,
-            };
-            (layout_id, state)
-        } else {
-            let (layout_id, state) =
-                element.request_layout(state.and_then(|s| s.rendered_element_state), cx);
-            let state = ComponentState {
-                rendered_element: Some(element),
-                rendered_element_state: Some(state),
-            };
-            (layout_id, state)
-        }
+        let mut element = self.0.take().unwrap().render(cx).into_any_element();
+        let layout_id = element.request_layout(cx);
+        (layout_id, element)
     }
 
-    fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext) {
-        let mut element = state.rendered_element.take().unwrap();
-        if let Some(element_id) = element.element_id() {
-            cx.with_element_state(element_id, |element_state, cx| {
-                let mut element_state = element_state.unwrap();
-                element.paint(bounds, &mut element_state, cx);
-                ((), element_state)
-            });
-        } else {
-            element.paint(bounds, state.rendered_element_state.as_mut().unwrap(), cx);
-        }
+    fn paint(&mut self, _: Bounds<Pixels>, element: &mut Self::State, cx: &mut WindowContext) {
+        element.paint(cx)
     }
 }
 
@@ -216,7 +185,7 @@ pub struct GlobalElementId(SmallVec<[ElementId; 32]>);
 trait ElementObject {
     fn element_id(&self) -> Option<ElementId>;
 
-    fn layout(&mut self, cx: &mut WindowContext) -> LayoutId;
+    fn request_layout(&mut self, cx: &mut WindowContext) -> LayoutId;
 
     fn paint(&mut self, cx: &mut WindowContext);
 
@@ -391,7 +360,7 @@ where
         self.as_ref().unwrap().element_id()
     }
 
-    fn layout(&mut self, cx: &mut WindowContext) -> LayoutId {
+    fn request_layout(&mut self, cx: &mut WindowContext) -> LayoutId {
         DrawableElement::request_layout(self.as_mut().unwrap(), cx)
     }
 
@@ -431,8 +400,8 @@ impl AnyElement {
         AnyElement(element)
     }
 
-    pub fn layout(&mut self, cx: &mut WindowContext) -> LayoutId {
-        self.0.layout(cx)
+    pub fn request_layout(&mut self, cx: &mut WindowContext) -> LayoutId {
+        self.0.request_layout(cx)
     }
 
     pub fn paint(&mut self, cx: &mut WindowContext) {
@@ -471,7 +440,7 @@ impl Element for AnyElement {
         _: Option<Self::State>,
         cx: &mut WindowContext,
     ) -> (LayoutId, Self::State) {
-        let layout_id = self.layout(cx);
+        let layout_id = self.request_layout(cx);
         (layout_id, ())
     }
 

crates/gpui2/src/elements/div.rs 🔗

@@ -781,7 +781,7 @@ impl Element for Div {
                     child_layout_ids = self
                         .children
                         .iter_mut()
-                        .map(|child| child.layout(cx))
+                        .map(|child| child.request_layout(cx))
                         .collect::<SmallVec<_>>();
                     cx.request_layout(&style, child_layout_ids.iter().copied())
                 })

crates/gpui2/src/elements/overlay.rs 🔗

@@ -68,7 +68,7 @@ impl Element for Overlay {
         let child_layout_ids = self
             .children
             .iter_mut()
-            .map(|child| child.layout(cx))
+            .map(|child| child.request_layout(cx))
             .collect::<SmallVec<_>>();
 
         let overlay_style = Style {

crates/gpui2/src/interactive.rs 🔗

@@ -205,7 +205,7 @@ impl ExternalPaths {
 }
 
 impl Render for ExternalPaths {
-    fn render(&mut self, _: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, _: &mut ViewContext<Self>) -> impl IntoElement {
         div() // Intentionally left empty because the platform will render icons for the dragged files
     }
 }

crates/gpui2/src/view.rs 🔗

@@ -87,7 +87,7 @@ impl<V: Render> Element for View<V> {
         cx: &mut WindowContext,
     ) -> (LayoutId, Self::State) {
         let mut element = self.update(cx, |view, cx| view.render(cx).into_any_element());
-        let layout_id = element.layout(cx);
+        let layout_id = element.request_layout(cx);
         (layout_id, Some(element))
     }
 
@@ -321,7 +321,7 @@ mod any_view {
     ) -> (LayoutId, AnyElement) {
         let view = view.clone().downcast::<V>().unwrap();
         let mut element = view.update(cx, |view, cx| view.render(cx).into_any_element());
-        let layout_id = element.layout(cx);
+        let layout_id = element.request_layout(cx);
         (layout_id, element)
     }
 

crates/language_selector2/src/active_buffer_language.rs 🔗

@@ -1,7 +1,5 @@
 use editor::Editor;
-use gpui::{
-    div, Element, IntoElement, ParentElement, Render, Subscription, View, ViewContext, WeakView,
-};
+use gpui::{div, IntoElement, ParentElement, Render, Subscription, View, ViewContext, WeakView};
 use std::sync::Arc;
 use ui::{Button, ButtonCommon, Clickable, LabelSize, Tooltip};
 use workspace::{item::ItemHandle, StatusItemView, Workspace};
@@ -38,7 +36,7 @@ impl ActiveBufferLanguage {
 }
 
 impl Render for ActiveBufferLanguage {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
         div().when_some(self.active_language.as_ref(), |el, active_language| {
             let active_language_text = if let Some(active_language_text) = active_language {
                 active_language_text.to_string()

crates/language_selector2/src/language_selector.rs 🔗

@@ -67,7 +67,7 @@ impl LanguageSelector {
 }
 
 impl Render for LanguageSelector {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
         v_stack().w(rems(34.)).child(self.picker.clone())
     }
 }

crates/language_tools2/src/lsp_log.rs 🔗

@@ -2,9 +2,9 @@ use collections::{HashMap, VecDeque};
 use editor::{Editor, EditorEvent, MoveToEnd};
 use futures::{channel::mpsc, StreamExt};
 use gpui::{
-    actions, div, AnchorCorner, AnyElement, AppContext, Context, Element, EventEmitter,
-    FocusHandle, FocusableView, IntoElement, Model, ModelContext, ParentElement, Render, Styled,
-    Subscription, View, ViewContext, VisualContext, WeakModel, WindowContext,
+    actions, div, AnchorCorner, AnyElement, AppContext, Context, EventEmitter, FocusHandle,
+    FocusableView, IntoElement, Model, ModelContext, ParentElement, Render, Styled, Subscription,
+    View, ViewContext, VisualContext, WeakModel, WindowContext,
 };
 use language::{LanguageServerId, LanguageServerName};
 use lsp::IoKind;
@@ -595,9 +595,9 @@ fn log_contents(lines: &VecDeque<String>) -> String {
 }
 
 impl Render for LspLogView {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
         self.editor
-            .update(cx, |editor, cx| editor.render(cx).into_any())
+            .update(cx, |editor, cx| editor.render(cx).into_any_element())
     }
 }
 
@@ -708,7 +708,7 @@ impl ToolbarItemView for LspLogToolbarItemView {
 }
 
 impl Render for LspLogToolbarItemView {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
         let Some(log_view) = self.log_view.clone() else {
             return div();
         };

crates/language_tools2/src/syntax_tree_view.rs 🔗

@@ -1,6 +1,6 @@
 use editor::{scroll::autoscroll::Autoscroll, Anchor, Editor, ExcerptId};
 use gpui::{
-    actions, canvas, div, rems, uniform_list, AnyElement, AppContext, AvailableSpace, Div, Element,
+    actions, canvas, div, rems, uniform_list, AnyElement, AppContext, AvailableSpace, Div,
     EventEmitter, FocusHandle, FocusableView, Hsla, InteractiveElement, IntoElement, Model,
     MouseButton, MouseDownEvent, MouseMoveEvent, ParentElement, Pixels, Render, Styled,
     UniformListScrollHandle, View, ViewContext, VisualContext, WeakView, WindowContext,
@@ -305,7 +305,7 @@ impl SyntaxTreeView {
 }
 
 impl Render for SyntaxTreeView {
-    fn render(&mut self, cx: &mut gpui::ViewContext<'_, Self>) -> impl Element {
+    fn render(&mut self, cx: &mut gpui::ViewContext<'_, Self>) -> impl IntoElement {
         let settings = ThemeSettings::get_global(cx);
         let line_height = cx
             .text_style()
@@ -505,7 +505,7 @@ fn format_node_range(node: Node) -> String {
 }
 
 impl Render for SyntaxTreeToolbarItemView {
-    fn render(&mut self, cx: &mut ViewContext<'_, Self>) -> impl Element {
+    fn render(&mut self, cx: &mut ViewContext<'_, Self>) -> impl IntoElement {
         self.render_menu(cx)
             .unwrap_or_else(|| popover_menu("Empty Syntax Tree"))
     }

crates/outline2/src/outline.rs 🔗

@@ -57,7 +57,7 @@ impl EventEmitter<DismissEvent> for OutlineView {}
 impl ModalView for OutlineView {}
 
 impl Render for OutlineView {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
         v_stack().w(rems(34.)).child(self.picker.clone())
     }
 }

crates/picker2/src/picker2.rs 🔗

@@ -235,7 +235,7 @@ impl<D: PickerDelegate> EventEmitter<DismissEvent> for Picker<D> {}
 impl<D: PickerDelegate> ModalView for Picker<D> {}
 
 impl<D: PickerDelegate> Render for Picker<D> {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
         let picker_editor = h_stack()
             .overflow_hidden()
             .flex_none()

crates/project_panel2/src/project_panel.rs 🔗

@@ -1480,7 +1480,7 @@ impl ProjectPanel {
 }
 
 impl Render for ProjectPanel {
-    fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> impl Element {
+    fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> impl IntoElement {
         let has_worktree = self.visible_entries.len() != 0;
 
         if has_worktree {

crates/quick_action_bar2/src/quick_action_bar.rs 🔗

@@ -36,7 +36,7 @@ impl QuickActionBar {
 }
 
 impl Render for QuickActionBar {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
         let Some(editor) = self.active_editor() else {
             return div().id("empty quick action bar");
         };
@@ -136,9 +136,7 @@ impl QuickActionBarButton {
 }
 
 impl RenderOnce for QuickActionBarButton {
-    type Output = IconButton;
-
-    fn render(self, _: &mut WindowContext) -> Self::Output {
+    fn render(self, _: &mut WindowContext) -> impl IntoElement {
         let tooltip = self.tooltip.clone();
         let action = self.action.boxed_clone();
 

crates/recent_projects2/src/highlighted_workspace_location.rs 🔗

@@ -43,9 +43,7 @@ impl HighlightedText {
 }
 
 impl RenderOnce for HighlightedText {
-    type Output = HighlightedLabel;
-
-    fn render(self, _cx: &mut WindowContext) -> Self::Output {
+    fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
         HighlightedLabel::new(self.text, self.highlight_positions)
     }
 }

crates/recent_projects2/src/recent_projects.rs 🔗

@@ -103,7 +103,7 @@ impl FocusableView for RecentProjects {
 }
 
 impl Render for RecentProjects {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
         v_stack()
             .w(rems(self.rem_width))
             .child(self.picker.clone())

crates/search2/src/buffer_search.rs 🔗

@@ -101,7 +101,7 @@ impl BufferSearchBar {
 impl EventEmitter<Event> for BufferSearchBar {}
 impl EventEmitter<workspace::ToolbarItemEvent> for BufferSearchBar {}
 impl Render for BufferSearchBar {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
         if self.dismissed {
             return div();
         }

crates/search2/src/project_search.rs 🔗

@@ -279,7 +279,7 @@ pub enum ViewEvent {
 impl EventEmitter<ViewEvent> for ProjectSearchView {}
 
 impl Render for ProjectSearchView {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
         if self.has_matches() {
             div()
                 .flex_1()
@@ -1548,7 +1548,7 @@ impl ProjectSearchBar {
 }
 
 impl Render for ProjectSearchBar {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
         let Some(search) = self.active_project_search.clone() else {
             return div();
         };

crates/story/src/story.rs 🔗

@@ -1,6 +1,5 @@
 use gpui::{
-    div, hsla, prelude::*, px, rems, AnyElement, Div, ElementId, Hsla, SharedString, Stateful,
-    WindowContext,
+    div, hsla, prelude::*, px, rems, AnyElement, Div, ElementId, Hsla, SharedString, WindowContext,
 };
 use itertools::Itertools;
 use smallvec::SmallVec;
@@ -74,9 +73,7 @@ impl ParentElement for StoryContainer {
 }
 
 impl RenderOnce for StoryContainer {
-    type Output = Stateful<Div>;
-
-    fn render(self, _cx: &mut WindowContext) -> Self::Output {
+    fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
         div()
             .size_full()
             .flex()
@@ -294,9 +291,7 @@ impl StoryItem {
 }
 
 impl RenderOnce for StoryItem {
-    type Output = Div;
-
-    fn render(self, _cx: &mut WindowContext) -> Self::Output {
+    fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
         div()
             .my_2()
             .flex()
@@ -358,9 +353,7 @@ impl StorySection {
 }
 
 impl RenderOnce for StorySection {
-    type Output = Div;
-
-    fn render(self, _cx: &mut WindowContext) -> Self::Output {
+    fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
         let children: SmallVec<[AnyElement; 2]> = SmallVec::from_iter(Itertools::intersperse_with(
             self.children.into_iter(),
             || Story::divider().into_any_element(),

crates/storybook2/src/stories/auto_height_editor.rs 🔗

@@ -1,6 +1,6 @@
 use editor::Editor;
 use gpui::{
-    div, white, Element, KeyBinding, ParentElement, Render, Styled, View, ViewContext,
+    div, white, IntoElement, KeyBinding, ParentElement, Render, Styled, View, ViewContext,
     VisualContext, WindowContext,
 };
 
@@ -22,7 +22,7 @@ impl AutoHeightEditorStory {
 }
 
 impl Render for AutoHeightEditorStory {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
         div()
             .size_full()
             .bg(white())

crates/storybook2/src/stories/cursor.rs 🔗

@@ -5,7 +5,7 @@ use ui::prelude::*;
 pub struct CursorStory;
 
 impl Render for CursorStory {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
         let all_cursors: [(&str, Box<dyn Fn(Stateful<Div>) -> Stateful<Div>>); 19] = [
             (
                 "cursor_default",

crates/storybook2/src/stories/focus.rs 🔗

@@ -56,7 +56,7 @@ impl FocusStory {
 }
 
 impl Render for FocusStory {
-    fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> impl Element {
+    fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> impl IntoElement {
         let theme = cx.theme();
         let color_1 = theme.status().created;
         let color_2 = theme.status().modified;

crates/storybook2/src/stories/kitchen_sink.rs 🔗

@@ -14,7 +14,7 @@ impl KitchenSinkStory {
 }
 
 impl Render for KitchenSinkStory {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
         let component_stories = ComponentStory::iter()
             .map(|selector| selector.story(cx))
             .collect::<Vec<_>>();

crates/storybook2/src/stories/overflow_scroll.rs 🔗

@@ -6,7 +6,7 @@ use ui::prelude::*;
 pub struct OverflowScrollStory;
 
 impl Render for OverflowScrollStory {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
         Story::container()
             .child(Story::title("Overflow Scroll"))
             .child(Story::label("`overflow_x_scroll`"))

crates/storybook2/src/stories/picker.rs 🔗

@@ -200,7 +200,7 @@ impl PickerStory {
 }
 
 impl Render for PickerStory {
-    fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> impl Element {
+    fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> impl IntoElement {
         div()
             .bg(cx.theme().styles.colors.background)
             .size_full()

crates/storybook2/src/stories/scroll.rs 🔗

@@ -11,7 +11,7 @@ impl ScrollStory {
 }
 
 impl Render for ScrollStory {
-    fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> impl Element {
+    fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> impl IntoElement {
         let theme = cx.theme();
         let color_1 = theme.status().created;
         let color_2 = theme.status().modified;

crates/storybook2/src/stories/text.rs 🔗

@@ -1,6 +1,6 @@
 use gpui::{
-    div, green, red, Element, HighlightStyle, InteractiveText, IntoElement, ParentElement, Render,
-    Styled, StyledText, View, VisualContext, WindowContext,
+    div, green, red, HighlightStyle, InteractiveText, IntoElement, ParentElement, Render, Styled,
+    StyledText, View, VisualContext, WindowContext,
 };
 use indoc::indoc;
 use story::*;
@@ -14,7 +14,7 @@ impl TextStory {
 }
 
 impl Render for TextStory {
-    fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> impl Element {
+    fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> impl IntoElement {
         StoryContainer::new("Text Story", "crates/storybook2/src/stories/text.rs")
             .children(
                 vec![

crates/storybook2/src/stories/viewport_units.rs 🔗

@@ -6,7 +6,7 @@ use ui::prelude::*;
 pub struct ViewportUnitsStory;
 
 impl Render for ViewportUnitsStory {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
         Story::container().child(
             div()
                 .flex()

crates/storybook2/src/stories/z_index.rs 🔗

@@ -7,7 +7,7 @@ use ui::prelude::*;
 pub struct ZIndexStory;
 
 impl Render for ZIndexStory {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
         Story::container().child(Story::title("z-index")).child(
             div()
                 .flex()
@@ -80,9 +80,7 @@ struct ZIndexExample {
 }
 
 impl RenderOnce for ZIndexExample {
-    type Output = Div;
-
-    fn render(self, _cx: &mut WindowContext) -> Self::Output {
+    fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
         div()
             .relative()
             .size_full()

crates/storybook2/src/storybook2.rs 🔗

@@ -113,7 +113,7 @@ impl StoryWrapper {
 }
 
 impl Render for StoryWrapper {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
         div()
             .flex()
             .flex_col()

crates/terminal_view2/src/terminal_panel.rs 🔗

@@ -3,9 +3,9 @@ use std::{path::PathBuf, sync::Arc};
 use crate::TerminalView;
 use db::kvp::KEY_VALUE_STORE;
 use gpui::{
-    actions, div, serde_json, AppContext, AsyncWindowContext, Element, Entity, EventEmitter,
-    ExternalPaths, FocusHandle, FocusableView, IntoElement, ParentElement, Pixels, Render, Styled,
-    Subscription, Task, View, ViewContext, VisualContext, WeakView, WindowContext,
+    actions, div, serde_json, AppContext, AsyncWindowContext, Entity, EventEmitter, ExternalPaths,
+    FocusHandle, FocusableView, IntoElement, ParentElement, Pixels, Render, Styled, Subscription,
+    Task, View, ViewContext, VisualContext, WeakView, WindowContext,
 };
 use project::Fs;
 use serde::{Deserialize, Serialize};
@@ -329,7 +329,7 @@ impl TerminalPanel {
 impl EventEmitter<PanelEvent> for TerminalPanel {}
 
 impl Render for TerminalPanel {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
         div().size_full().child(self.pane.clone())
     }
 }

crates/terminal_view2/src/terminal_view.rs 🔗

@@ -617,7 +617,7 @@ impl TerminalView {
 }
 
 impl Render for TerminalView {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
         let terminal_handle = self.terminal.clone();
 
         let focused = self.focus_handle.is_focused(cx);

crates/theme2/src/styles/stories/color.rs 🔗

@@ -7,7 +7,7 @@ use crate::{default_color_scales, ColorScaleStep};
 pub struct ColorsStory;
 
 impl Render for ColorsStory {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
         let color_scales = default_color_scales();
 
         Story::container().child(Story::title("Colors")).child(

crates/theme2/src/styles/stories/players.rs 🔗

@@ -1,4 +1,4 @@
-use gpui::{div, img, px, Element, ParentElement, Render, Styled, ViewContext};
+use gpui::{div, img, px, IntoElement, ParentElement, Render, Styled, ViewContext};
 use story::Story;
 
 use crate::{ActiveTheme, PlayerColors};
@@ -6,7 +6,7 @@ use crate::{ActiveTheme, PlayerColors};
 pub struct PlayerStory;
 
 impl Render for PlayerStory {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
         Story::container().child(
             div()
                 .flex()

crates/theme_selector2/src/theme_selector.rs 🔗

@@ -69,7 +69,7 @@ impl FocusableView for ThemeSelector {
 }
 
 impl Render for ThemeSelector {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
         v_stack().w(rems(34.)).child(self.picker.clone())
     }
 }

crates/ui2/src/components/avatar.rs 🔗

@@ -1,5 +1,5 @@
 use crate::prelude::*;
-use gpui::{img, Div, Hsla, ImageSource, Img, IntoElement, Styled};
+use gpui::{img, Hsla, ImageSource, Img, IntoElement, Styled};
 
 #[derive(Debug, Default, PartialEq, Clone)]
 pub enum Shape {
@@ -16,9 +16,7 @@ pub struct Avatar {
 }
 
 impl RenderOnce for Avatar {
-    type Output = Div;
-
-    fn render(mut self, cx: &mut WindowContext) -> Self::Output {
+    fn render(mut self, cx: &mut WindowContext) -> impl IntoElement {
         if self.image.style().corner_radii.top_left.is_none() {
             self = self.shape(Shape::Circle);
         }

crates/ui2/src/components/button/button.rs 🔗

@@ -136,9 +136,8 @@ impl ButtonCommon for Button {
 }
 
 impl RenderOnce for Button {
-    type Output = ButtonLike;
-
-    fn render(self, _cx: &mut WindowContext) -> Self::Output {
+    #[allow(refining_impl_trait)]
+    fn render(self, _cx: &mut WindowContext) -> ButtonLike {
         let is_disabled = self.base.disabled;
         let is_selected = self.base.selected;
 

crates/ui2/src/components/button/button_icon.rs 🔗

@@ -63,9 +63,7 @@ impl Selectable for ButtonIcon {
 }
 
 impl RenderOnce for ButtonIcon {
-    type Output = IconElement;
-
-    fn render(self, _cx: &mut WindowContext) -> Self::Output {
+    fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
         let icon = self
             .selected_icon
             .filter(|_| self.selected)

crates/ui2/src/components/button/button_like.rs 🔗

@@ -1,5 +1,5 @@
 use gpui::{relative, DefiniteLength, MouseButton};
-use gpui::{rems, transparent_black, AnyElement, AnyView, ClickEvent, Div, Hsla, Rems, Stateful};
+use gpui::{rems, transparent_black, AnyElement, AnyView, ClickEvent, Hsla, Rems};
 use smallvec::SmallVec;
 
 use crate::prelude::*;
@@ -363,9 +363,7 @@ impl ParentElement for ButtonLike {
 }
 
 impl RenderOnce for ButtonLike {
-    type Output = Stateful<Div>;
-
-    fn render(self, cx: &mut WindowContext) -> Self::Output {
+    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
         self.base
             .h_flex()
             .id(self.id.clone())

crates/ui2/src/components/button/icon_button.rs 🔗

@@ -106,9 +106,7 @@ impl VisibleOnHover for IconButton {
 }
 
 impl RenderOnce for IconButton {
-    type Output = ButtonLike;
-
-    fn render(self, _cx: &mut WindowContext) -> Self::Output {
+    fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
         let is_disabled = self.base.disabled;
         let is_selected = self.base.selected;
 

crates/ui2/src/components/button/toggle_button.rs 🔗

@@ -99,9 +99,7 @@ impl ButtonCommon for ToggleButton {
 }
 
 impl RenderOnce for ToggleButton {
-    type Output = ButtonLike;
-
-    fn render(self, _cx: &mut WindowContext) -> Self::Output {
+    fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
         let is_disabled = self.base.disabled;
         let is_selected = self.base.selected;
 

crates/ui2/src/components/checkbox.rs 🔗

@@ -1,4 +1,4 @@
-use gpui::{div, prelude::*, Div, Element, ElementId, IntoElement, Styled, WindowContext};
+use gpui::{div, prelude::*, Element, ElementId, IntoElement, Styled, WindowContext};
 
 use crate::prelude::*;
 use crate::{Color, Icon, IconElement, Selection};
@@ -19,9 +19,7 @@ pub struct Checkbox {
 }
 
 impl RenderOnce for Checkbox {
-    type Output = gpui::Stateful<Div>;
-
-    fn render(self, cx: &mut WindowContext) -> Self::Output {
+    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
         let group_id = format!("checkbox_group_{:?}", self.id);
 
         let icon = match self.checked {

crates/ui2/src/components/context_menu.rs 🔗

@@ -232,7 +232,7 @@ impl ContextMenuItem {
 }
 
 impl Render for ContextMenu {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
         div().elevation_2(cx).flex().flex_row().child(
             v_stack()
                 .min_w(px(200.))

crates/ui2/src/components/disclosure.rs 🔗

@@ -28,9 +28,7 @@ impl Disclosure {
 }
 
 impl RenderOnce for Disclosure {
-    type Output = IconButton;
-
-    fn render(self, _cx: &mut WindowContext) -> Self::Output {
+    fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
         IconButton::new(
             self.id,
             match self.is_open {

crates/ui2/src/components/divider.rs 🔗

@@ -1,4 +1,4 @@
-use gpui::{Div, Hsla, IntoElement};
+use gpui::{Hsla, IntoElement};
 
 use crate::prelude::*;
 
@@ -31,9 +31,7 @@ pub struct Divider {
 }
 
 impl RenderOnce for Divider {
-    type Output = Div;
-
-    fn render(self, cx: &mut WindowContext) -> Self::Output {
+    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
         div()
             .map(|this| match self.direction {
                 DividerDirection::Horizontal => {

crates/ui2/src/components/icon.rs 🔗

@@ -1,4 +1,4 @@
-use gpui::{rems, svg, IntoElement, Rems, Svg};
+use gpui::{rems, svg, IntoElement, Rems};
 use strum::EnumIter;
 
 use crate::prelude::*;
@@ -200,9 +200,7 @@ pub struct IconElement {
 }
 
 impl RenderOnce for IconElement {
-    type Output = Svg;
-
-    fn render(self, cx: &mut WindowContext) -> Self::Output {
+    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
         svg()
             .size(self.size.rems())
             .flex_none()

crates/ui2/src/components/indicator.rs 🔗

@@ -1,4 +1,4 @@
-use gpui::{Div, Position};
+use gpui::Position;
 
 use crate::prelude::*;
 
@@ -45,9 +45,7 @@ impl Indicator {
 }
 
 impl RenderOnce for Indicator {
-    type Output = Div;
-
-    fn render(self, cx: &mut WindowContext) -> Self::Output {
+    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
         div()
             .flex_none()
             .map(|this| match self.style {

crates/ui2/src/components/keybinding.rs 🔗

@@ -1,5 +1,5 @@
 use crate::{h_stack, prelude::*, Icon, IconElement, IconSize};
-use gpui::{relative, rems, Action, Div, FocusHandle, IntoElement, Keystroke};
+use gpui::{relative, rems, Action, FocusHandle, IntoElement, Keystroke};
 
 #[derive(IntoElement, Clone)]
 pub struct KeyBinding {
@@ -11,9 +11,7 @@ pub struct KeyBinding {
 }
 
 impl RenderOnce for KeyBinding {
-    type Output = Div;
-
-    fn render(self, cx: &mut WindowContext) -> Self::Output {
+    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
         h_stack()
             .flex_none()
             .gap_2()
@@ -87,9 +85,7 @@ pub struct Key {
 }
 
 impl RenderOnce for Key {
-    type Output = Div;
-
-    fn render(self, cx: &mut WindowContext) -> Self::Output {
+    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
         let single_char = self.key.len() == 1;
 
         div()
@@ -121,9 +117,7 @@ pub struct KeyIcon {
 }
 
 impl RenderOnce for KeyIcon {
-    type Output = Div;
-
-    fn render(self, _cx: &mut WindowContext) -> Self::Output {
+    fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
         div()
             .w(rems(14. / 16.))
             .child(IconElement::new(self.icon).size(IconSize::Small))

crates/ui2/src/components/label/highlighted_label.rs 🔗

@@ -46,9 +46,7 @@ impl LabelCommon for HighlightedLabel {
 }
 
 impl RenderOnce for HighlightedLabel {
-    type Output = LabelLike;
-
-    fn render(self, cx: &mut WindowContext) -> Self::Output {
+    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
         let highlight_color = cx.theme().colors().text_accent;
 
         let mut highlight_indices = self.highlight_indices.iter().copied().peekable();

crates/ui2/src/components/label/label.rs 🔗

@@ -40,9 +40,7 @@ impl LabelCommon for Label {
 }
 
 impl RenderOnce for Label {
-    type Output = LabelLike;
-
-    fn render(self, _cx: &mut WindowContext) -> Self::Output {
+    fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
         self.base.child(self.label)
     }
 }

crates/ui2/src/components/label/label_like.rs 🔗

@@ -1,4 +1,4 @@
-use gpui::{relative, AnyElement, Div, Styled};
+use gpui::{relative, AnyElement, Styled};
 use smallvec::SmallVec;
 
 use crate::prelude::*;
@@ -76,9 +76,7 @@ impl ParentElement for LabelLike {
 }
 
 impl RenderOnce for LabelLike {
-    type Output = Div;
-
-    fn render(self, cx: &mut WindowContext) -> Self::Output {
+    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
         div()
             .when(self.strikethrough, |this| {
                 this.relative().child(

crates/ui2/src/components/list/list.rs 🔗

@@ -1,4 +1,4 @@
-use gpui::{AnyElement, Div};
+use gpui::AnyElement;
 use smallvec::SmallVec;
 
 use crate::{prelude::*, v_stack, Label, ListHeader};
@@ -46,9 +46,7 @@ impl ParentElement for List {
 }
 
 impl RenderOnce for List {
-    type Output = Div;
-
-    fn render(self, _cx: &mut WindowContext) -> Self::Output {
+    fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
         v_stack().w_full().py_1().children(self.header).map(|this| {
             match (self.children.is_empty(), self.toggle) {
                 (false, _) => this.children(self.children),

crates/ui2/src/components/list/list_header.rs 🔗

@@ -1,6 +1,5 @@
-use gpui::{AnyElement, ClickEvent, Div, Stateful};
-
 use crate::{h_stack, prelude::*, Disclosure, Label};
+use gpui::{AnyElement, ClickEvent};
 
 #[derive(IntoElement)]
 pub struct ListHeader {
@@ -76,9 +75,7 @@ impl Selectable for ListHeader {
 }
 
 impl RenderOnce for ListHeader {
-    type Output = Stateful<Div>;
-
-    fn render(self, cx: &mut WindowContext) -> Self::Output {
+    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
         h_stack()
             .id(self.label.clone())
             .w_full()

crates/ui2/src/components/list/list_item.rs 🔗

@@ -1,6 +1,4 @@
-use gpui::{
-    px, AnyElement, AnyView, ClickEvent, Div, MouseButton, MouseDownEvent, Pixels, Stateful,
-};
+use gpui::{px, AnyElement, AnyView, ClickEvent, MouseButton, MouseDownEvent, Pixels};
 use smallvec::SmallVec;
 
 use crate::{prelude::*, Disclosure};
@@ -147,9 +145,7 @@ impl ParentElement for ListItem {
 }
 
 impl RenderOnce for ListItem {
-    type Output = Stateful<Div>;
-
-    fn render(self, cx: &mut WindowContext) -> Self::Output {
+    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
         h_stack()
             .id(self.id)
             .w_full()

crates/ui2/src/components/list/list_separator.rs 🔗

@@ -1,14 +1,10 @@
-use gpui::Div;
-
 use crate::prelude::*;
 
 #[derive(IntoElement)]
 pub struct ListSeparator;
 
 impl RenderOnce for ListSeparator {
-    type Output = Div;
-
-    fn render(self, cx: &mut WindowContext) -> Self::Output {
+    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
         div()
             .h_px()
             .w_full()

crates/ui2/src/components/list/list_sub_header.rs 🔗

@@ -1,5 +1,3 @@
-use gpui::Div;
-
 use crate::prelude::*;
 use crate::{h_stack, Icon, IconElement, IconSize, Label};
 
@@ -26,9 +24,7 @@ impl ListSubHeader {
 }
 
 impl RenderOnce for ListSubHeader {
-    type Output = Div;
-
-    fn render(self, _cx: &mut WindowContext) -> Self::Output {
+    fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
         h_stack().flex_1().w_full().relative().py_1().child(
             div()
                 .h_6()

crates/ui2/src/components/popover.rs 🔗

@@ -1,5 +1,5 @@
 use gpui::{
-    div, AnyElement, Div, Element, ElementId, IntoElement, ParentElement, RenderOnce, Styled,
+    div, AnyElement, Element, ElementId, IntoElement, ParentElement, RenderOnce, Styled,
     WindowContext,
 };
 use smallvec::SmallVec;
@@ -41,9 +41,7 @@ pub struct Popover {
 }
 
 impl RenderOnce for Popover {
-    type Output = Div;
-
-    fn render(self, cx: &mut WindowContext) -> Self::Output {
+    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
         div()
             .flex()
             .gap_1()

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

@@ -153,7 +153,7 @@ impl<M: ManagedView> Element for PopoverMenu<M> {
             }
 
             let mut element = overlay.child(menu.clone()).into_any();
-            menu_layout_id = Some(element.layout(cx));
+            menu_layout_id = Some(element.request_layout(cx));
             element
         });
 
@@ -164,7 +164,7 @@ impl<M: ManagedView> Element for PopoverMenu<M> {
 
         let child_layout_id = child_element
             .as_mut()
-            .map(|child_element| child_element.layout(cx));
+            .map(|child_element| child_element.request_layout(cx));
 
         let layout_id = cx.request_layout(
             &gpui::Style::default(),

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

@@ -81,7 +81,7 @@ impl<M: ManagedView> Element for RightClickMenu<M> {
             overlay = overlay.position(*position.borrow());
 
             let mut element = overlay.child(menu.clone()).into_any();
-            menu_layout_id = Some(element.layout(cx));
+            menu_layout_id = Some(element.request_layout(cx));
             element
         });
 
@@ -92,7 +92,7 @@ impl<M: ManagedView> Element for RightClickMenu<M> {
 
         let child_layout_id = child_element
             .as_mut()
-            .map(|child_element| child_element.layout(cx));
+            .map(|child_element| child_element.request_layout(cx));
 
         let layout_id = cx.request_layout(
             &gpui::Style::default(),

crates/ui2/src/components/stories/avatar.rs 🔗

@@ -7,7 +7,7 @@ use crate::Avatar;
 pub struct AvatarStory;
 
 impl Render for AvatarStory {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
         Story::container()
             .child(Story::title_for::<Avatar>())
             .child(Story::label("Default"))

crates/ui2/src/components/stories/button.rs 🔗

@@ -7,7 +7,7 @@ use crate::{Button, ButtonStyle};
 pub struct ButtonStory;
 
 impl Render for ButtonStory {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
         Story::container()
             .child(Story::title_for::<Button>())
             .child(Story::label("Default"))

crates/ui2/src/components/stories/checkbox.rs 🔗

@@ -7,7 +7,7 @@ use crate::{h_stack, Checkbox};
 pub struct CheckboxStory;
 
 impl Render for CheckboxStory {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
         Story::container()
             .child(Story::title_for::<Checkbox>())
             .child(Story::label("Default"))

crates/ui2/src/components/stories/context_menu.rs 🔗

@@ -20,7 +20,7 @@ fn build_menu(cx: &mut WindowContext, header: impl Into<SharedString>) -> View<C
 pub struct ContextMenuStory;
 
 impl Render for ContextMenuStory {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
         Story::container()
             .on_action(|_: &PrintCurrentDate, _| {
                 println!("printing unix time!");

crates/ui2/src/components/stories/disclosure.rs 🔗

@@ -7,7 +7,7 @@ use crate::Disclosure;
 pub struct DisclosureStory;
 
 impl Render for DisclosureStory {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
         Story::container()
             .child(Story::title_for::<Disclosure>())
             .child(Story::label("Toggled"))

crates/ui2/src/components/stories/icon.rs 🔗

@@ -8,7 +8,7 @@ use crate::{Icon, IconElement};
 pub struct IconStory;
 
 impl Render for IconStory {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
         let icons = Icon::iter();
 
         Story::container()

crates/ui2/src/components/stories/icon_button.rs 🔗

@@ -7,7 +7,7 @@ use crate::{Icon, IconButton};
 pub struct IconButtonStory;
 
 impl Render for IconButtonStory {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
         let default_button = StoryItem::new(
             "Default",
             IconButton::new("default_icon_button", Icon::Hash),

crates/ui2/src/components/stories/keybinding.rs 🔗

@@ -13,7 +13,7 @@ pub fn binding(key: &str) -> gpui::KeyBinding {
 }
 
 impl Render for KeybindingStory {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
         let all_modifier_permutations = ["ctrl", "alt", "cmd", "shift"].into_iter().permutations(2);
 
         Story::container()

crates/ui2/src/components/stories/label.rs 🔗

@@ -5,7 +5,7 @@ use story::Story;
 pub struct LabelStory;
 
 impl Render for LabelStory {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
         Story::container()
             .child(Story::title_for::<Label>())
             .child(Story::label("Default"))

crates/ui2/src/components/stories/list.rs 🔗

@@ -7,7 +7,7 @@ use crate::{List, ListItem};
 pub struct ListStory;
 
 impl Render for ListStory {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
         Story::container()
             .child(Story::title_for::<List>())
             .child(Story::label("Default"))

crates/ui2/src/components/stories/list_header.rs 🔗

@@ -7,7 +7,7 @@ use crate::{Icon, ListHeader};
 pub struct ListHeaderStory;
 
 impl Render for ListHeaderStory {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
         Story::container()
             .child(Story::title_for::<ListHeader>())
             .child(Story::label("Default"))

crates/ui2/src/components/stories/list_item.rs 🔗

@@ -7,7 +7,7 @@ use crate::{Icon, ListItem};
 pub struct ListItemStory;
 
 impl Render for ListItemStory {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
         Story::container()
             .bg(cx.theme().colors().background)
             .child(Story::title_for::<ListItem>())

crates/ui2/src/components/stories/tab.rs 🔗

@@ -9,7 +9,7 @@ use crate::{Indicator, Tab};
 pub struct TabStory;
 
 impl Render for TabStory {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
         Story::container()
             .child(Story::title_for::<Tab>())
             .child(Story::label("Default"))

crates/ui2/src/components/stories/tab_bar.rs 🔗

@@ -6,7 +6,7 @@ use crate::{prelude::*, Tab, TabBar, TabPosition};
 pub struct TabBarStory;
 
 impl Render for TabBarStory {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
         let tab_count = 20;
         let selected_tab_index = 3;
 

crates/ui2/src/components/stories/toggle_button.rs 🔗

@@ -6,7 +6,7 @@ use crate::{prelude::*, ToggleButton};
 pub struct ToggleButtonStory;
 
 impl Render for ToggleButtonStory {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
         StoryContainer::new(
             "Toggle Button",
             "crates/ui2/src/components/stories/toggle_button.rs",

crates/ui2/src/components/tab.rs 🔗

@@ -93,9 +93,8 @@ impl ParentElement for Tab {
 }
 
 impl RenderOnce for Tab {
-    type Output = Stateful<Div>;
-
-    fn render(self, cx: &mut WindowContext) -> Self::Output {
+    #[allow(refining_impl_trait)]
+    fn render(self, cx: &mut WindowContext) -> Stateful<Div> {
         let (text_color, tab_bg, _tab_hover_bg, _tab_active_bg) = match self.selected {
             false => (
                 cx.theme().colors().text_muted,

crates/ui2/src/components/tab_bar.rs 🔗

@@ -1,4 +1,4 @@
-use gpui::{AnyElement, ScrollHandle, Stateful};
+use gpui::{AnyElement, ScrollHandle};
 use smallvec::SmallVec;
 
 use crate::prelude::*;
@@ -89,9 +89,7 @@ impl ParentElement for TabBar {
 }
 
 impl RenderOnce for TabBar {
-    type Output = Stateful<Div>;
-
-    fn render(self, cx: &mut WindowContext) -> Self::Output {
+    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
         const HEIGHT_IN_REMS: f32 = 30. / 16.;
 
         div()

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

@@ -68,7 +68,7 @@ impl Tooltip {
 }
 
 impl Render for Tooltip {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
         let ui_font = ThemeSettings::get_global(cx).ui_font.family.clone();
         overlay().child(
             // padding to avoid mouse cursor

crates/vcs_menu2/src/lib.rs 🔗

@@ -65,7 +65,7 @@ impl FocusableView for BranchList {
 }
 
 impl Render for BranchList {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
         v_stack()
             .w(rems(self.rem_width))
             .child(self.picker.clone())

crates/vim2/src/mode_indicator.rs 🔗

@@ -47,7 +47,7 @@ impl ModeIndicator {
 }
 
 impl Render for ModeIndicator {
-    fn render(&mut self, _: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, _: &mut ViewContext<Self>) -> impl IntoElement {
         let Some(mode) = self.mode.as_ref() else {
             return div().into_any();
         };

crates/welcome2/src/base_keymap_picker.rs 🔗

@@ -64,7 +64,7 @@ impl BaseKeymapSelector {
 }
 
 impl Render for BaseKeymapSelector {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
         self.picker.clone()
     }
 }

crates/welcome2/src/welcome.rs 🔗

@@ -58,7 +58,7 @@ pub struct WelcomePage {
 }
 
 impl Render for WelcomePage {
-    fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> impl Element {
+    fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> impl IntoElement {
         h_stack().full().track_focus(&self.focus_handle).child(
             v_stack()
                 .w_96()

crates/workspace2/src/dock.rs 🔗

@@ -510,7 +510,7 @@ impl Dock {
 }
 
 impl Render for Dock {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
         if let Some(entry) = self.visible_entry() {
             let size = entry.panel.size(cx);
 
@@ -597,7 +597,7 @@ impl PanelButtons {
 }
 
 impl Render for PanelButtons {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
         // todo!()
         let dock = self.dock.read(cx);
         let active_index = dock.active_panel_index;
@@ -719,7 +719,7 @@ pub mod test {
     }
 
     impl Render for TestPanel {
-        fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl Element {
+        fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
             div()
         }
     }

crates/workspace2/src/item.rs 🔗

@@ -760,7 +760,7 @@ pub mod test {
     use super::{Item, ItemEvent};
     use crate::{ItemId, ItemNavHistory, Pane, Workspace, WorkspaceId};
     use gpui::{
-        AnyElement, AppContext, Context as _, Element, EntityId, EventEmitter, FocusableView,
+        AnyElement, AppContext, Context as _, EntityId, EventEmitter, FocusableView,
         InteractiveElement, IntoElement, Model, Render, SharedString, Task, View, ViewContext,
         VisualContext, WeakView,
     };
@@ -910,7 +910,7 @@ pub mod test {
     }
 
     impl Render for TestItem {
-        fn render(&mut self, _: &mut ViewContext<Self>) -> impl Element {
+        fn render(&mut self, _: &mut ViewContext<Self>) -> impl IntoElement {
             gpui::div().track_focus(&self.focus_handle)
         }
     }

crates/workspace2/src/modal_layer.rs 🔗

@@ -104,7 +104,7 @@ impl ModalLayer {
 }
 
 impl Render for ModalLayer {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
         let Some(active_modal) = &self.active_modal else {
             return div();
         };

crates/workspace2/src/notifications.rs 🔗

@@ -219,7 +219,7 @@ pub mod simple_message_notification {
     }
 
     impl Render for MessageNotification {
-        fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Element {
+        fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
             v_stack()
                 .elevation_3(cx)
                 .p_4()

crates/workspace2/src/pane.rs 🔗

@@ -1867,7 +1867,7 @@ impl FocusableView for Pane {
 }
 
 impl Render for Pane {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
         v_stack()
             .key_context("Pane")
             .track_focus(&self.focus_handle)
@@ -2746,7 +2746,7 @@ mod tests {
 }
 
 impl Render for DraggedTab {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
         let ui_font = ThemeSettings::get_global(cx).ui_font.family.clone();
         let item = &self.pane.read(cx).items[self.ix];
         let label = item.tab_content(Some(self.detail), false, cx);

crates/workspace2/src/shared_screen.rs 🔗

@@ -66,7 +66,7 @@ impl FocusableView for SharedScreen {
     }
 }
 impl Render for SharedScreen {
-    fn render(&mut self, _: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, _: &mut ViewContext<Self>) -> impl IntoElement {
         div().track_focus(&self.focus).size_full().children(
             self.frame
                 .as_ref()

crates/workspace2/src/status_bar.rs 🔗

@@ -33,7 +33,7 @@ pub struct StatusBar {
 }
 
 impl Render for StatusBar {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
         div()
             .py_0p5()
             .px_1()

crates/workspace2/src/toolbar.rs 🔗

@@ -93,7 +93,7 @@ impl Toolbar {
 }
 
 impl Render for Toolbar {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
         if !self.has_any_visible_items() {
             return div();
         }

crates/workspace2/src/workspace2.rs 🔗

@@ -3487,7 +3487,7 @@ impl FocusableView for Workspace {
 struct DraggedDock(DockPosition);
 
 impl Render for Workspace {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Element {
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
         let mut context = KeyContext::default();
         context.add("Workspace");
 
@@ -4305,7 +4305,7 @@ impl Element for DisconnectedOverlay {
                 "Your connection to the remote project has been lost.",
             ))
             .into_any();
-        (overlay.layout(cx), overlay)
+        (overlay.request_layout(cx), overlay)
     }
 
     fn paint(&mut self, bounds: Bounds<Pixels>, overlay: &mut Self::State, cx: &mut WindowContext) {

crates/workspace2_ui/src/titlebar.rs 🔗

@@ -0,0 +1,442 @@
+use gpui::{
+    div, img, prelude::*, px, rems, DismissEvent, Div, EventEmitter, FocusHandle, FocusableView,
+    SharedString, Stateful, View, ViewContext, WindowContext,
+};
+use theme::ActiveFabricTheme;
+use ui::{
+    popover_menu, Button, ButtonCommon, ButtonStyle, Clickable, Color, LabelSize, PopoverMenu,
+    Tooltip,
+};
+
+#[derive(Clone, Copy, Debug)]
+pub struct PeerId(pub u32);
+
+#[derive(Clone, Copy, Debug)]
+pub struct ProjectId(u64);
+
+pub trait TitlebarDelegate: 'static + Sized {
+    fn toggle_following(&mut self, peer_index: PeerId, cx: &mut ViewContext<Self>);
+}
+
+impl TitlebarDelegate for () {
+    fn toggle_following(&mut self, peer: PeerId, _cx: &mut ViewContext<Self>) {
+        log::info!("toggle following {:?}", peer);
+    }
+}
+
+#[derive(IntoElement)]
+pub struct Titlebar<D: TitlebarDelegate = ()> {
+    pub delegate: View<D>,
+    pub full_screen: bool,
+    pub project_host: Option<ProjectHost<D>>,
+    pub projects: Projects<D>,
+    pub branches: Option<Branches>,
+    pub collaborators: Vec<FacePile>,
+}
+
+#[derive(IntoElement)]
+pub struct ProjectHost<D: TitlebarDelegate> {
+    pub delegate: View<D>,
+    pub id: PeerId,
+    pub login: SharedString,
+    pub peer_index: u32,
+}
+
+#[derive(IntoElement)]
+pub struct Projects<D: TitlebarDelegate> {
+    pub delegate: View<D>,
+    pub current: SharedString,
+    pub recent: Vec<Project>,
+}
+
+#[derive(Clone)]
+pub struct Project {
+    pub name: SharedString,
+    pub id: ProjectId,
+}
+
+pub struct ProjectsMenu<D> {
+    delegate: View<D>,
+    focus: FocusHandle,
+    recent: Vec<Project>,
+}
+
+pub struct Branches {
+    pub current: SharedString,
+}
+
+#[derive(IntoElement, Default)]
+pub struct FacePile {
+    pub faces: Vec<Avatar>,
+}
+
+#[derive(IntoElement)]
+pub struct Avatar {
+    pub image_uri: SharedString,
+    pub audio_status: AudioStatus,
+    pub available: Option<bool>,
+    pub shape: AvatarShape,
+}
+
+pub enum AvatarShape {
+    Square,
+    Circle,
+}
+
+impl<D: TitlebarDelegate> RenderOnce for Titlebar<D> {
+    type Output = Stateful<Div>;
+
+    fn render(self, cx: &mut ui::prelude::WindowContext) -> Self::Output {
+        div()
+            .flex()
+            .flex_col()
+            .id("titlebar")
+            .justify_between()
+            .w_full()
+            .h(rems(1.75))
+            // Set a non-scaling min-height here to ensure the titlebar is
+            // always at least the height of the traffic lights.
+            .min_h(px(32.))
+            .pl_2()
+            .when(self.full_screen, |this| {
+                // Use pixels here instead of a rem-based size because the macOS traffic
+                // lights are a static size, and don't scale with the rest of the UI.
+                this.pl(px(80.))
+            })
+            .bg(cx.theme().denim.default.background)
+            .on_click(|event, cx| {
+                if event.up.click_count == 2 {
+                    cx.zoom_window();
+                }
+            })
+            // left side
+            .child(
+                div()
+                    .flex()
+                    .flex_row()
+                    .gap_1()
+                    .children(self.project_host)
+                    .child(self.projects), // .children(self.render_project_branch(cx))
+                                           // .children(self.render_collaborators(cx)),
+            )
+        // right side
+        // .child(
+        //     div()
+        //         .flex()
+        //         .flex_row()
+        //         .gap_1()
+        //         .pr_1()
+        //         .when_some(room, |this, room| {
+        //             let room = room.read(cx);
+        //             let project = self.project.read(cx);
+        //             let is_local = project.is_local();
+        //             let is_shared = is_local && project.is_shared();
+        //             let is_muted = room.is_muted(cx);
+        //             let is_deafened = room.is_deafened().unwrap_or(false);
+        //             let is_screen_sharing = room.is_screen_sharing();
+
+        //             this.when(is_local, |this| {
+        //                 this.child(
+        //                     Button::new(
+        //                         "toggle_sharing",
+        //                         if is_shared { "Unshare" } else { "Share" },
+        //                     )
+        //                     .style(ButtonStyle::Subtle)
+        //                     .label_size(LabelSize::Small)
+        //                     .on_click(cx.listener(
+        //                         move |this, _, cx| {
+        //                             if is_shared {
+        //                                 this.unshare_project(&Default::default(), cx);
+        //                             } else {
+        //                                 this.share_project(&Default::default(), cx);
+        //                             }
+        //                         },
+        //                     )),
+        //                 )
+        //             })
+        //             .child(
+        //                 IconButton::new("leave-call", ui::Icon::Exit)
+        //                     .style(ButtonStyle::Subtle)
+        //                     .icon_size(IconSize::Small)
+        //                     .on_click(move |_, cx| {
+        //                         ActiveCall::global(cx)
+        //                             .update(cx, |call, cx| call.hang_up(cx))
+        //                             .detach_and_log_err(cx);
+        //                     }),
+        //             )
+        //             .child(
+        //                 IconButton::new(
+        //                     "mute-microphone",
+        //                     if is_muted {
+        //                         ui::Icon::MicMute
+        //                     } else {
+        //                         ui::Icon::Mic
+        //                     },
+        //                 )
+        //                 .style(ButtonStyle::Subtle)
+        //                 .icon_size(IconSize::Small)
+        //                 .selected(is_muted)
+        //                 .on_click(move |_, cx| crate::toggle_mute(&Default::default(), cx)),
+        //             )
+        //             .child(
+        //                 IconButton::new(
+        //                     "mute-sound",
+        //                     if is_deafened {
+        //                         ui::Icon::AudioOff
+        //                     } else {
+        //                         ui::Icon::AudioOn
+        //                     },
+        //                 )
+        //                 .style(ButtonStyle::Subtle)
+        //                 .icon_size(IconSize::Small)
+        //                 .selected(is_deafened)
+        //                 .tooltip(move |cx| {
+        //                     Tooltip::with_meta("Deafen Audio", None, "Mic will be muted", cx)
+        //                 })
+        //                 .on_click(move |_, cx| crate::toggle_mute(&Default::default(), cx)),
+        //             )
+        //             .child(
+        //                 IconButton::new("screen-share", ui::Icon::Screen)
+        //                     .style(ButtonStyle::Subtle)
+        //                     .icon_size(IconSize::Small)
+        //                     .selected(is_screen_sharing)
+        //                     .on_click(move |_, cx| {
+        //                         crate::toggle_screen_sharing(&Default::default(), cx)
+        //                     }),
+        //             )
+        //         })
+        //         .map(|el| {
+        //             let status = self.client.status();
+        //             let status = &*status.borrow();
+        //             if matches!(status, client::Status::Connected { .. }) {
+        //                 el.child(self.render_user_menu_button(cx))
+        //             } else {
+        //                 el.children(self.render_connection_status(status, cx))
+        //                     .child(self.render_sign_in_button(cx))
+        //                     .child(self.render_user_menu_button(cx))
+        //             }
+        //         }),
+        // )
+    }
+}
+
+impl<D: TitlebarDelegate> RenderOnce for ProjectHost<D> {
+    type Output = Button;
+
+    fn render(self, _: &mut WindowContext) -> Self::Output {
+        let delegate = self.delegate;
+        Button::new("project-host", self.login)
+            .color(Color::Player(self.peer_index))
+            .style(ButtonStyle::Subtle)
+            .label_size(LabelSize::Small)
+            .tooltip(move |cx| Tooltip::text("Toggle following", cx))
+            .on_click(move |_, cx| {
+                let host_id = self.id;
+                delegate.update(cx, |this, cx| this.toggle_following(host_id, cx))
+            })
+    }
+}
+
+impl<D: 'static> EventEmitter<DismissEvent> for ProjectsMenu<D> {}
+
+impl<D: TitlebarDelegate> Render for ProjectsMenu<D> {
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Element {
+        todo!()
+    }
+}
+
+impl<D: TitlebarDelegate> FocusableView for ProjectsMenu<D> {
+    fn focus_handle(&self, _cx: &gpui::AppContext) -> gpui::FocusHandle {
+        self.focus.clone()
+    }
+}
+
+impl<D: TitlebarDelegate> RenderOnce for Projects<D> {
+    type Output = PopoverMenu<ProjectsMenu<D>>;
+
+    fn render(self, _cx: &mut WindowContext) -> Self::Output {
+        let delegate = self.delegate;
+        let recent_projects = self.recent;
+
+        popover_menu("recent-projects")
+            .trigger(
+                Button::new("trigger", self.current)
+                    .style(ButtonStyle::Subtle)
+                    .label_size(LabelSize::Small)
+                    .tooltip(move |cx| Tooltip::text("Recent Projects", cx)),
+            )
+            .menu(move |cx| {
+                if recent_projects.is_empty() {
+                    None
+                } else {
+                    Some(cx.new_view(|cx| ProjectsMenu {
+                        delegate: delegate.clone(),
+                        focus: cx.focus_handle(),
+                        recent: recent_projects.clone(),
+                    }))
+                }
+            })
+    }
+}
+
+impl RenderOnce for FacePile {
+    type Output = Div;
+
+    fn render(self, _: &mut WindowContext) -> Self::Output {
+        let face_count = self.faces.len();
+        div()
+            .p_1()
+            .flex()
+            .items_center()
+            .children(self.faces.into_iter().enumerate().map(|(ix, avatar)| {
+                let last_child = ix == face_count - 1;
+                div()
+                    .z_index((face_count - ix) as u8)
+                    .when(!last_child, |div| div.neg_mr_1())
+                    .child(avatar)
+            }))
+    }
+}
+
+impl RenderOnce for Avatar {
+    type Output = Div;
+
+    fn render(self, cx: &mut WindowContext) -> Self::Output {
+        div()
+            .map(|this| match self.shape {
+                AvatarShape::Square => this.rounded_md(),
+                AvatarShape::Circle => this.rounded_full(),
+            })
+            .map(|this| match self.audio_status {
+                AudioStatus::None => this,
+                AudioStatus::Muted => this.border_color(cx.theme().muted),
+                AudioStatus::Speaking => this.border_color(cx.theme().speaking),
+            })
+            .size(cx.rem_size() + px(2.))
+            .child(
+                img(self.image_uri)
+                    .size(cx.rem_size())
+                    .bg(cx.theme().cotton.disabled.background),
+            )
+            .children(self.available.map(|is_free| {
+                // Non-integer sizes result in non-round indicators.
+                let indicator_size = (cx.rem_size() * 0.4).round();
+
+                div()
+                    .absolute()
+                    .z_index(1)
+                    .bg(if is_free {
+                        cx.theme().positive.default.background
+                    } else {
+                        cx.theme().negative.default.background
+                    })
+                    .size(indicator_size)
+                    .rounded(indicator_size)
+                    .bottom_0()
+                    .right_0()
+            }))
+    }
+}
+
+pub enum AudioStatus {
+    None,
+    Muted,
+    Speaking,
+}
+
+// impl Titlebar {
+//     pub fn render_project_host(&self, cx: &mut ViewContext<Self>) -> Option<impl Element> {
+//         let host = self.project.read(cx).host()?;
+//         let host = self.user_store.read(cx).get_cached_user(host.user_id)?;
+//         let participant_index = self
+//             .user_store
+//             .read(cx)
+//             .participant_indices()
+//             .get(&host.id)?;
+//         Some(
+//             div().border().border_color(gpui::red()).child(
+//                 Button::new("project_owner_trigger", host.github_login.clone())
+//                     .color(Color::Player(participant_index.0))
+//                     .style(ButtonStyle::Subtle)
+//                     .label_size(LabelSize::Small)
+//                     .tooltip(move |cx| Tooltip::text("Toggle following", cx)),
+//             ),
+//         )
+//     }
+
+//     pub fn render_project_branch(&self, cx: &mut ViewContext<Self>) -> Option<impl Element> {
+//         let entry = {
+//             let mut names_and_branches =
+//                 self.project.read(cx).visible_worktrees(cx).map(|worktree| {
+//                     let worktree = worktree.read(cx);
+//                     worktree.root_git_entry()
+//                 });
+
+//             names_and_branches.next().flatten()
+//         };
+//         let workspace = self.workspace.upgrade()?;
+//         let branch_name = entry
+//             .as_ref()
+//             .and_then(RepositoryEntry::branch)
+//             .map(|branch| util::truncate_and_trailoff(&branch, MAX_BRANCH_NAME_LENGTH))?;
+//         Some(
+//             popover_menu("project_branch_trigger")
+//                 .trigger(
+//                     Button::new("project_branch_trigger", branch_name)
+//                         .color(Color::Muted)
+//                         .style(ButtonStyle::Subtle)
+//                         .label_size(LabelSize::Small)
+//                         .tooltip(move |cx| {
+//                             Tooltip::with_meta(
+//                                 "Recent Branches",
+//                                 Some(&ToggleVcsMenu),
+//                                 "Local branches only",
+//                                 cx,
+//                             )
+//                         }),
+//                 )
+//                 .menu(move |cx| Self::render_vcs_popover(workspace.clone(), cx)),
+//         )
+//     }
+
+//     fn render_collaborator(
+//         &self,
+//         user: &Arc<User>,
+//         peer_id: PeerId,
+//         is_present: bool,
+//         is_speaking: bool,
+//         is_muted: bool,
+//         room: &Room,
+//         project_id: Option<u64>,
+//         current_user: &Arc<User>,
+//     ) -> Option<FacePile> {
+//         let followers = project_id.map_or(&[] as &[_], |id| room.followers_for(peer_id, id));
+
+//         let pile = FacePile::default()
+//             .child(
+//                 Avatar::new(user.avatar_uri.clone())
+//                     .grayscale(!is_present)
+//                     .border_color(if is_speaking {
+//                         gpui::blue()
+//                     } else if is_muted {
+//                         gpui::red()
+//                     } else {
+//                         Hsla::default()
+//                     }),
+//             )
+//             .children(followers.iter().filter_map(|follower_peer_id| {
+//                 let follower = room
+//                     .remote_participants()
+//                     .values()
+//                     .find_map(|p| (p.peer_id == *follower_peer_id).then_some(&p.user))
+//                     .or_else(|| {
+//                         (self.client.peer_id() == Some(*follower_peer_id)).then_some(current_user)
+//                     })?
+//                     .clone();
+
+//                 Some(Avatar::new(follower.avatar_uri.clone()))
+//             }));
+
+//         Some(pile)
+//     }
+// }