Apply more fixes to the visual part

Kirill Bulatov created

Change summary

Cargo.lock                                     |   1 
crates/editor2/src/editor.rs                   |  13 
crates/language_tools2/Cargo.toml              |   1 
crates/language_tools2/src/lsp_log.rs          | 388 ++++++++-----------
crates/language_tools2/src/syntax_tree_view.rs | 361 ++++++++----------
5 files changed, 335 insertions(+), 429 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -5008,6 +5008,7 @@ dependencies = [
  "settings2",
  "theme2",
  "tree-sitter",
+ "ui2",
  "unindent",
  "util",
  "workspace2",

crates/editor2/src/editor.rs 🔗

@@ -1652,14 +1652,11 @@ impl Editor {
         Self::new(EditorMode::SingleLine, buffer, None, cx)
     }
 
-    //     pub fn multi_line(
-    //         field_editor_style: Option<Arc<GetFieldEditorTheme>>,
-    //         cx: &mut ViewContext<Self>,
-    //     ) -> Self {
-    //         let buffer = cx.build_model(|cx| Buffer::new(0, cx.model_id() as u64, String::new()));
-    //         let buffer = cx.build_model(|cx| MultiBuffer::singleton(buffer, cx));
-    //         Self::new(EditorMode::Full, buffer, None, field_editor_style, cx)
-    //     }
+    pub fn multi_line(cx: &mut ViewContext<Self>) -> Self {
+        let buffer = cx.build_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), String::new()));
+        let buffer = cx.build_model(|cx| MultiBuffer::singleton(buffer, cx));
+        Self::new(EditorMode::Full, buffer, None, cx)
+    }
 
     pub fn auto_height(max_lines: usize, cx: &mut ViewContext<Self>) -> Self {
         let buffer = cx.build_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), String::new()));

crates/language_tools2/Cargo.toml 🔗

@@ -17,6 +17,7 @@ language = { package = "language2", path = "../language2" }
 project = { package = "project2", path = "../project2" }
 workspace = { package = "workspace2", path = "../workspace2" }
 gpui = { package = "gpui2", path = "../gpui2" }
+ui = { package = "ui2", path = "../ui2" }
 util = { path = "../util" }
 lsp = { package = "lsp2", path = "../lsp2" }
 futures.workspace = true

crates/language_tools2/src/lsp_log.rs 🔗

@@ -1,20 +1,22 @@
 use collections::{HashMap, VecDeque};
-use editor::{Editor, MoveToEnd};
+use editor::{Editor, EditorElement, EditorEvent, MoveToEnd};
 use futures::{channel::mpsc, StreamExt};
 use gpui::{
-    actions, AnchorCorner, AnyElement, AppContext, Context, CursorStyle, Element, Empty, Entity,
-    Model, ModelContext, MouseButton, Overlay, OverlayFitMode, Subscription, View, ViewContext,
-    VisualContext, WeakModel,
+    actions, div, overlay, red, AnchorCorner, AnyElement, AppContext, Context, CursorStyle, Div,
+    EventEmitter, FocusHandle, FocusableView, InteractiveElement, IntoElement, Model, ModelContext,
+    MouseButton, OverlayFitMode, ParentElement, Render, Styled, Subscription, View, ViewContext,
+    VisualContext, WeakModel, WindowContext,
 };
 use language::{LanguageServerId, LanguageServerName};
 use lsp::IoKind;
 use project::{search::SearchQuery, Project};
 use std::{borrow::Cow, sync::Arc};
-use theme::Theme;
+use theme::{ActiveTheme, Theme};
+use ui::{h_stack, v_stack, Label};
 use workspace::{
     item::{Item, ItemHandle},
-    searchable::{SearchableItem, SearchableItemHandle},
-    ToolbarItemLocation, ToolbarItemView, Workspace,
+    searchable::{SearchEvent, SearchableItem, SearchableItemHandle},
+    ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView, Workspace,
 };
 
 const SEND_LINE: &str = "// Send:";
@@ -50,6 +52,7 @@ pub struct LspLogView {
     current_server_id: Option<LanguageServerId>,
     is_showing_rpc_trace: bool,
     project: Model<Project>,
+    focus_handle: FocusHandle,
     _log_store_subscriptions: Vec<Subscription>,
 }
 
@@ -110,9 +113,9 @@ impl LogStore {
             projects: HashMap::default(),
             io_tx,
         };
-        cx.spawn_weak(|this, mut cx| async move {
+        cx.spawn(|this, mut cx| async move {
             while let Some((project, server_id, io_kind, message)) = io_rx.next().await {
-                if let Some(this) = this.upgrade(&cx) {
+                if let Some(this) = this.upgrade() {
                     this.update(&mut cx, |this, cx| {
                         this.on_io(project, server_id, io_kind, &message, cx);
                     });
@@ -120,7 +123,7 @@ impl LogStore {
             }
             anyhow::Ok(())
         })
-        .detach();
+        .detach_and_log_err(cx);
         this
     }
 
@@ -131,7 +134,7 @@ impl LogStore {
             ProjectState {
                 servers: HashMap::default(),
                 _subscriptions: [
-                    cx.observe_release(&project, move |this, _, _| {
+                    cx.observe_release(project, move |this, _, _| {
                         this.projects.remove(&weak_project);
                     }),
                     cx.subscribe(project, |this, project, event, cx| match event {
@@ -185,14 +188,13 @@ impl LogStore {
                     .ok();
             })
         });
-        let this = cx.weak_handle();
+        let this = cx.handle().downgrade();
         let weak_project = project.downgrade();
         server_state._lsp_logs_subscription = server.map(|server| {
             let server_id = server.server_id();
             server.on_notification::<lsp::notification::LogMessage, _>({
                 move |params, mut cx| {
-                    if let Some((project, this)) = weak_project.upgrade().zip(this.upgrade(&mut cx))
-                    {
+                    if let Some((project, this)) = weak_project.upgrade().zip(this.upgrade()) {
                         this.update(&mut cx, |this, cx| {
                             this.add_language_server_log(&project, server_id, &params.message, cx);
                         });
@@ -413,14 +415,25 @@ impl LspLogView {
             }
         });
         let (editor, editor_subscription) = Self::editor_for_logs(String::new(), cx);
+
+        let focus_handle = cx.focus_handle();
+        let focus_subscription = cx.on_focus(&focus_handle, |log_view, cx| {
+            cx.focus_view(&log_view.editor);
+        });
+
         let mut this = Self {
+            focus_handle,
             editor,
             editor_subscription,
             project,
             log_store,
             current_server_id: None,
             is_showing_rpc_trace: false,
-            _log_store_subscriptions: vec![model_changes_subscription, events_subscriptions],
+            _log_store_subscriptions: vec![
+                model_changes_subscription,
+                events_subscriptions,
+                focus_subscription,
+            ],
         };
         if let Some(server_id) = server_id {
             this.show_logs_for_server(server_id, cx);
@@ -433,13 +446,18 @@ impl LspLogView {
         cx: &mut ViewContext<Self>,
     ) -> (View<Editor>, Subscription) {
         let editor = cx.build_view(|cx| {
-            let mut editor = Editor::multi_line(None, cx);
+            let mut editor = Editor::multi_line(cx);
             editor.set_text(log_contents, cx);
             editor.move_to_end(&MoveToEnd, cx);
             editor.set_read_only(true);
             editor
         });
-        let editor_subscription = cx.subscribe(&editor, |_, _, event, cx| cx.emit(event.clone()));
+        let editor_subscription = cx.subscribe(
+            &editor,
+            |_, _, event: &EditorEvent, cx: &mut ViewContext<'_, LspLogView>| {
+                cx.emit(event.clone())
+            },
+        );
         (editor, editor_subscription)
     }
 
@@ -526,7 +544,7 @@ impl LspLogView {
                 .as_singleton()
                 .expect("log buffer should be a singleton")
                 .update(cx, |_, cx| {
-                    cx.spawn_weak({
+                    cx.spawn({
                         let buffer = cx.handle();
                         |_, mut cx| async move {
                             let language = language.await.ok();
@@ -574,30 +592,34 @@ fn log_contents(lines: &VecDeque<String>) -> String {
     }
 }
 
-impl View for LspLogView {
-    fn ui_name() -> &'static str {
-        "LspLogView"
-    }
+impl Render for LspLogView {
+    // todo!()
+    // fn ui_name() -> &'static str {
+    //     "LspLogView"
+    // }
 
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
-        ChildView::new(&self.editor, cx).into_any()
+    type Element = EditorElement;
+
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
+        self.editor.update(cx, |editor, cx| editor.render(cx))
     }
+}
 
-    fn focus_in(&mut self, _: gpui::AnyView, cx: &mut ViewContext<Self>) {
-        if cx.is_self_focused() {
-            cx.focus(&self.editor);
-        }
+impl FocusableView for LspLogView {
+    fn focus_handle(&self, _: &AppContext) -> FocusHandle {
+        self.focus_handle.clone()
     }
 }
 
 impl Item for LspLogView {
-    fn tab_content<V: 'static>(
-        &self,
-        _: Option<usize>,
-        style: &theme::Tab,
-        _: &AppContext,
-    ) -> AnyElement<V> {
-        Label::new("LSP Logs", style.label.clone()).into_any()
+    type Event = EditorEvent;
+
+    fn to_item_events(event: &Self::Event, f: impl FnMut(workspace::item::ItemEvent)) {
+        Editor::to_item_events(event, f)
+    }
+
+    fn tab_content(&self, _: Option<usize>, _: bool, _: &WindowContext<'_>) -> AnyElement {
+        Label::new("LSP Logs").into_any_element()
     }
 
     fn as_searchable(&self, handle: &View<Self>) -> Option<Box<dyn SearchableItemHandle>> {
@@ -608,15 +630,6 @@ impl Item for LspLogView {
 impl SearchableItem for LspLogView {
     type Match = <Editor as SearchableItem>::Match;
 
-    fn to_search_event(
-        &mut self,
-        event: &Self::Event,
-        cx: &mut ViewContext<Self>,
-    ) -> Option<workspace::searchable::SearchEvent> {
-        self.editor
-            .update(cx, |editor, cx| editor.to_search_event(event, cx))
-    }
-
     fn clear_matches(&mut self, cx: &mut ViewContext<Self>) {
         self.editor.update(cx, |e, cx| e.clear_matches(cx))
     }
@@ -675,6 +688,8 @@ impl SearchableItem for LspLogView {
     }
 }
 
+impl EventEmitter<ToolbarItemEvent> for LspLogToolbarItemView {}
+
 impl ToolbarItemView for LspLogToolbarItemView {
     fn set_active_pane_item(
         &mut self,
@@ -688,9 +703,7 @@ impl ToolbarItemView for LspLogToolbarItemView {
                 self._log_view_subscription = Some(cx.observe(&log_view, |_, _, cx| {
                     cx.notify();
                 }));
-                return ToolbarItemLocation::PrimaryLeft {
-                    flex: Some((1., false)),
-                };
+                return ToolbarItemLocation::PrimaryLeft;
             }
         }
         self.log_view = None;
@@ -699,15 +712,17 @@ impl ToolbarItemView for LspLogToolbarItemView {
     }
 }
 
-impl View for LspLogToolbarItemView {
-    fn ui_name() -> &'static str {
-        "LspLogView"
-    }
+impl Render for LspLogToolbarItemView {
+    type Element = Div;
+    // todo!()
+    // fn ui_name() -> &'static str {
+    //     "LspLogView"
+    // }
 
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
-        let theme = theme::current(cx).clone();
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> Div {
+        let theme = cx.theme().clone();
         let Some(log_view) = self.log_view.as_ref() else {
-            return Empty::new().into_any();
+            return div();
         };
         let (menu_rows, current_server_id) = log_view.update(cx, |log_view, cx| {
             let menu_rows = log_view.menu_items(cx).unwrap_or_default();
@@ -726,19 +741,15 @@ impl View for LspLogToolbarItemView {
 
         enum LspLogScroll {}
         enum Menu {}
-        let lsp_menu = Stack::new()
-            .with_child(Self::render_language_server_menu_header(
-                current_server,
-                &theme,
-                cx,
-            ))
-            .with_children(if self.menu_open {
+        let lsp_menu = h_stack()
+            .child(Self::render_language_server_menu_header(current_server, cx))
+            .children(if self.menu_open {
                 Some(
-                    Overlay::new(
-                        MouseEventHandler::new::<Menu, _>(0, cx, move |_, cx| {
-                            Flex::column()
+                    overlay()
+                        .child(
+                            v_stack()
                                 .scrollable::<LspLogScroll>(0, None, cx)
-                                .with_children(menu_rows.into_iter().map(|row| {
+                                .children(menu_rows.into_iter().map(|row| {
                                     Self::render_language_server_menu_item(
                                         row.server_id,
                                         row.server_name,
@@ -750,51 +761,27 @@ impl View for LspLogToolbarItemView {
                                         cx,
                                     )
                                 }))
-                                .contained()
-                                .with_style(theme.toolbar_dropdown_menu.container)
-                                .constrained()
-                                .with_width(400.)
-                                .with_height(400.)
-                        })
-                        .on_down_out(MouseButton::Left, |_, this, cx| {
-                            this.menu_open = false;
-                            cx.notify()
-                        }),
-                    )
-                    .with_hoverable(true)
-                    .with_fit_mode(OverlayFitMode::SwitchAnchor)
-                    .with_anchor_corner(AnchorCorner::TopLeft)
-                    .with_z_index(999)
-                    .aligned()
-                    .bottom()
-                    .left(),
+                                .on_down_out(MouseButton::Left, |_, this, cx| {
+                                    this.menu_open = false;
+                                    cx.notify()
+                                }),
+                        )
+                        .with_hoverable(true)
+                        .with_fit_mode(OverlayFitMode::SwitchAnchor)
+                        .with_anchor_corner(AnchorCorner::TopLeft)
+                        .with_z_index(999)
+                        .bottom()
+                        .left(),
                 )
             } else {
                 None
-            })
-            .aligned()
-            .left()
-            .clipped();
+            });
 
         enum LspCleanupButton {}
-        let log_cleanup_button =
-            MouseEventHandler::new::<LspCleanupButton, _>(1, cx, |state, cx| {
-                let theme = theme::current(cx).clone();
-                let style = theme
-                    .workspace
-                    .toolbar
-                    .toggleable_text_tool
-                    .in_state(server_selected)
-                    .style_for(state);
-                Label::new("Clear", style.text.clone())
-                    .aligned()
-                    .contained()
-                    .with_style(style.container)
-                    .constrained()
-                    .with_height(theme.toolbar_dropdown_menu.row_height / 6.0 * 5.0)
-            })
-            .on_click(MouseButton::Left, move |_, this, cx| {
-                if let Some(log_view) = this.log_view.as_ref() {
+        let log_cleanup_button = div()
+            .child(Label::new("Clear"))
+            .on_mouse_down(MouseButton::Left, move |_, cx| {
+                if let Some(log_view) = self.log_view.as_ref() {
                     log_view.update(cx, |log_view, cx| {
                         log_view.editor.update(cx, |editor, cx| {
                             editor.set_read_only(false);
@@ -804,17 +791,13 @@ impl View for LspLogToolbarItemView {
                     })
                 }
             })
-            .with_cursor_style(CursorStyle::PointingHand)
-            .aligned()
-            .right();
-
-        Flex::row()
-            .with_child(lsp_menu)
-            .with_child(log_cleanup_button)
-            .contained()
-            .aligned()
-            .left()
-            .into_any_named("lsp log controls")
+            .cursor(CursorStyle::PointingHand);
+
+        h_stack()
+            .child(lsp_menu)
+            .child(log_cleanup_button)
+            .border_1()
+            .border_color(red())
     }
 }
 
@@ -871,37 +854,37 @@ impl LspLogToolbarItemView {
 
     fn render_language_server_menu_header(
         current_server: Option<LogMenuItem>,
-        theme: &Arc<Theme>,
         cx: &mut ViewContext<Self>,
-    ) -> impl Element<Self> {
+    ) -> Div {
+        let view = cx.view().clone();
         enum ToggleMenu {}
-        MouseEventHandler::new::<ToggleMenu, _>(0, cx, move |state, _| {
-            let label: Cow<str> = current_server
-                .and_then(|row| {
-                    Some(
-                        format!(
-                            "{} ({}) - {}",
-                            row.server_name.0,
-                            row.worktree_root_name,
-                            if row.rpc_trace_selected {
-                                RPC_MESSAGES
-                            } else {
-                                SERVER_LOGS
-                            },
-                        )
-                        .into(),
+        let label: Cow<str> = current_server
+            .and_then(|row| {
+                Some(
+                    format!(
+                        "{} ({}) - {}",
+                        row.server_name.0,
+                        row.worktree_root_name,
+                        if row.rpc_trace_selected {
+                            RPC_MESSAGES
+                        } else {
+                            SERVER_LOGS
+                        },
                     )
+                    .into(),
+                )
+            })
+            .unwrap_or_else(|| "No server selected".into());
+        div()
+            .child(Label::new(label))
+            .cursor(CursorStyle::PointingHand)
+            .on_mouse_down(MouseButton::Left, move |_, cx| {
+                view.update(cx, |view, cx| {
+                    view.toggle_menu(cx);
                 })
-                .unwrap_or_else(|| "No server selected".into());
-            let style = theme.toolbar_dropdown_menu.header.style_for(state);
-            Label::new(label, style.text.clone())
-                .contained()
-                .with_style(style.container)
-        })
-        .with_cursor_style(CursorStyle::PointingHand)
-        .on_click(MouseButton::Left, move |_, view, cx| {
-            view.toggle_menu(cx);
-        })
+            })
+            .border_1()
+            .border_color(red())
     }
 
     fn render_language_server_menu_item(
@@ -913,78 +896,52 @@ impl LspLogToolbarItemView {
         rpc_trace_selected: bool,
         theme: &Arc<Theme>,
         cx: &mut ViewContext<Self>,
-    ) -> impl Element<Self> {
+    ) -> Div {
         enum ActivateLog {}
         enum ActivateRpcTrace {}
         enum LanguageServerCheckbox {}
 
-        Flex::column()
-            .with_child({
-                let style = &theme.toolbar_dropdown_menu.section_header;
-                Label::new(
-                    format!("{} ({})", name.0, worktree_root_name),
-                    style.text.clone(),
-                )
-                .contained()
-                .with_style(style.container)
-                .constrained()
-                .with_height(theme.toolbar_dropdown_menu.row_height)
-            })
-            .with_child(
-                MouseEventHandler::new::<ActivateLog, _>(id.0, cx, move |state, _| {
-                    let style = theme
-                        .toolbar_dropdown_menu
-                        .item
-                        .in_state(logs_selected)
-                        .style_for(state);
-                    Label::new(SERVER_LOGS, style.text.clone())
-                        .contained()
-                        .with_style(style.container)
-                        .constrained()
-                        .with_height(theme.toolbar_dropdown_menu.row_height)
-                })
-                .with_cursor_style(CursorStyle::PointingHand)
-                .on_click(MouseButton::Left, move |_, view, cx| {
-                    view.show_logs_for_server(id, cx);
-                }),
+        let view = cx.view().clone();
+
+        v_stack()
+            .child(Label::new(format!("{} ({})", name.0, worktree_root_name)))
+            .child(
+                div()
+                    .child(Label::new(SERVER_LOGS))
+                    .cursor(CursorStyle::PointingHand)
+                    .on_mouse_down(MouseButton::Left, move |_, cx| {
+                        view.update(cx, |view, cx| {
+                            view.show_logs_for_server(id, cx);
+                        })
+                    }),
             )
-            .with_child(
-                MouseEventHandler::new::<ActivateRpcTrace, _>(id.0, cx, move |state, cx| {
-                    let style = theme
-                        .toolbar_dropdown_menu
-                        .item
-                        .in_state(rpc_trace_selected)
-                        .style_for(state);
-                    Flex::row()
-                        .with_child(
-                            Label::new(RPC_MESSAGES, style.text.clone())
-                                .constrained()
-                                .with_height(theme.toolbar_dropdown_menu.row_height),
-                        )
-                        .with_child(
-                            ui::checkbox_with_label::<LanguageServerCheckbox, _, Self, _>(
-                                Empty::new(),
-                                &theme.welcome.checkbox,
-                                rpc_trace_enabled,
-                                id.0,
-                                cx,
-                                move |this, enabled, cx| {
-                                    this.toggle_logging_for_server(id, enabled, cx);
-                                },
-                            )
-                            .flex_float(),
+            .child(
+                h_stack()
+                    .child(Label::new(RPC_MESSAGES))
+                    .child(
+                        ui::checkbox_with_label::<LanguageServerCheckbox, _, Self, _>(
+                            div(),
+                            &theme.welcome.checkbox,
+                            rpc_trace_enabled,
+                            id.0,
+                            cx,
+                            move |this, enabled, cx| {
+                                this.toggle_logging_for_server(id, enabled, cx);
+                            },
                         )
-                        .align_children_center()
-                        .contained()
-                        .with_style(style.container)
-                        .constrained()
-                        .with_height(theme.toolbar_dropdown_menu.row_height)
-                })
-                .with_cursor_style(CursorStyle::PointingHand)
-                .on_click(MouseButton::Left, move |_, view, cx| {
-                    view.show_rpc_trace_for_server(id, cx);
-                }),
+                        .flex_float(),
+                    )
+                    .border_1()
+                    .border_color(red())
+                    .cursor(CursorStyle::PointingHand)
+                    .on_mouse_down(MouseButton::Left, move |_, cx| {
+                        view.update(cx, |view, cx| {
+                            view.show_rpc_trace_for_server(id, cx);
+                        })
+                    }),
             )
+            .border_1()
+            .border_color(red())
     }
 }
 
@@ -996,14 +953,7 @@ pub enum Event {
     },
 }
 
-impl Entity for LogStore {
-    type Event = Event;
-}
-
-impl Entity for LspLogView {
-    type Event = editor::Event;
-}
-
-impl Entity for LspLogToolbarItemView {
-    type Event = ();
-}
+impl EventEmitter<Event> for LogStore {}
+impl EventEmitter<Event> for LspLogView {}
+impl EventEmitter<EditorEvent> for LspLogView {}
+impl EventEmitter<SearchEvent> for LspLogView {}

crates/language_tools2/src/syntax_tree_view.rs 🔗

@@ -1,17 +1,20 @@
 use editor::{scroll::autoscroll::Autoscroll, Anchor, Editor, ExcerptId};
 use gpui::{
-    actions, AnchorCorner, AppContext, CursorStyle, Div, Element, Empty, Entity, Focusable, Model,
-    MouseButton, Overlay, OverlayFitMode, ParentElement, Render, TextStyle, UniformList,
-    UniformListState, View, ViewContext, VisualContext, WeakView,
+    actions, div, overlay, red, uniform_list, AnyElement, AppContext, CursorStyle, Div,
+    EventEmitter, FocusHandle, FocusableView, Hsla, InteractiveElement, IntoElement, Model,
+    MouseButton, ParentElement, Render, Styled, TextStyle, UniformListState, View, ViewContext,
+    VisualContext, WeakView, WindowContext,
 };
 use language::{Buffer, OwnedSyntaxLayerInfo, SyntaxLayerInfo};
-use std::{mem, ops::Range, sync::Arc};
-use theme::{ActiveTheme, Theme, ThemeSettings};
+use settings::Settings;
+use std::{mem, ops::Range};
+use theme::{ActiveTheme, ThemeSettings};
 use tree_sitter::{Node, TreeCursor};
+use ui::{h_stack, Label};
 use workspace::{
     item::{Item, ItemHandle},
     ui::v_stack,
-    ToolbarItemLocation, ToolbarItemView, Workspace,
+    ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView, Workspace,
 };
 
 actions!(debug, [OpenSyntaxTreeView]);
@@ -37,6 +40,7 @@ pub struct SyntaxTreeView {
     list_state: UniformListState,
     selected_descendant_ix: Option<usize>,
     hovered_descendant_ix: Option<usize>,
+    focus_handle: FocusHandle,
 }
 
 pub struct SyntaxTreeToolbarItemView {
@@ -72,6 +76,7 @@ impl SyntaxTreeView {
             line_height: None,
             hovered_descendant_ix: None,
             selected_descendant_ix: None,
+            focus_handle: cx.focus_handle(),
         };
 
         this.workspace_updated(active_item, cx);
@@ -229,7 +234,7 @@ impl SyntaxTreeView {
                     editor.clear_background_highlights::<Self>(cx);
                     editor.highlight_background::<Self>(
                         vec![range],
-                        |theme| theme.editor.document_highlight_write_background,
+                        |theme| theme.editor_document_highlight_write_background,
                         cx,
                     );
                 });
@@ -281,10 +286,10 @@ impl SyntaxTreeView {
         style: &TextStyle,
         editor_theme: &theme::Editor,
         cx: &AppContext,
-    ) -> gpui::AnyElement<SyntaxTreeView> {
+    ) -> Div {
         let node = cursor.node();
         let mut range_style = style.clone();
-        let em_width = style.em_width(cx.font_cache());
+        let em_width = style.em_width(cx.text_system());
         let gutter_padding = (em_width * editor_theme.gutter_padding_factor).round();
 
         range_style.color = editor_theme.line_number;
@@ -304,64 +309,54 @@ impl SyntaxTreeView {
             anonymous_node_style.color = color;
         }
 
-        let mut row = Flex::row();
+        let mut row = h_stack();
         if let Some(field_name) = cursor.field_name() {
             let mut field_style = style.clone();
             if let Some(color) = property_color {
                 field_style.color = color;
             }
 
-            row.add_children([
-                Label::new(field_name, field_style),
-                Label::new(": ", style.clone()),
-            ]);
+            row = row.children([Label::new(field_name), Label::new(": ")]);
         }
 
         return row
-            .with_child(
+            .child(
                 if node.is_named() {
-                    Label::new(node.kind(), style.clone())
+                    Label::new(node.kind())
                 } else {
-                    Label::new(format!("\"{}\"", node.kind()), anonymous_node_style)
-                }
-                .contained()
-                .with_margin_right(em_width),
+                    Label::new(format!("\"{}\"", node.kind()))
+                }, // todo!()
+                   // .margin(em_width),
             )
-            .with_child(Label::new(format_node_range(node), range_style))
-            .contained()
-            .with_background_color(if selected {
+            .child(Label::new(format_node_range(node)))
+            .text_bg(if selected {
                 editor_theme.selection.selection
             } else if hovered && list_hovered {
                 editor_theme.active_line_background
             } else {
-                Default::default()
+                Hsla::default()
             })
-            .with_padding_left(gutter_padding + depth as f32 * 18.0)
-            .into_any();
+            // todo!()
+            // .padding(gutter_padding + depth as f32 * 18.0)
+            .border_1()
+            .border_color(red());
     }
 }
 
-impl Entity for SyntaxTreeView {
-    type Event = ();
-}
+impl Render for SyntaxTreeView {
+    // todo!()
+    // fn ui_name() -> &'static str {
+    //     "SyntaxTreeView"
+    // }
 
-impl View for SyntaxTreeView {
-    fn ui_name() -> &'static str {
-        "SyntaxTreeView"
-    }
+    type Element = Div;
 
-    fn render(&mut self, cx: &mut gpui::ViewContext<'_, '_, Self>) -> gpui::AnyElement<Self> {
-        let settings = settings::get::<ThemeSettings>(cx);
-        let font_family_id = settings.buffer_font_family;
-        let font_family_name = cx.font_cache().family_name(font_family_id).unwrap();
-        let font_properties = Default::default();
-        let font_id = cx
-            .font_cache()
-            .select_font(font_family_id, &font_properties)
-            .unwrap();
+    fn render(&mut self, cx: &mut gpui::ViewContext<'_, Self>) -> Div {
+        let settings = ThemeSettings::get_global(cx);
+        let font = settings.buffer_font;
         let font_size = settings.buffer_font_size(cx);
 
-        let editor_theme = settings.theme.editor.clone();
+        let editor_theme = settings.active_theme;
         let style = TextStyle {
             color: editor_theme.text_color,
             font_family_name,
@@ -370,10 +365,16 @@ impl View for SyntaxTreeView {
             font_size,
             font_properties: Default::default(),
             underline: Default::default(),
-            soft_wrap: false,
+            font_family: todo!(),
+            font_features: todo!(),
+            line_height: todo!(),
+            font_weight: todo!(),
+            font_style: todo!(),
+            background_color: todo!(),
+            white_space: todo!(),
         };
 
-        let line_height = cx.font_cache().line_height(font_size);
+        let line_height = cx.text_system().line_height(font_size);
         if Some(line_height) != self.line_height {
             self.line_height = Some(line_height);
             self.hover_state_changed(cx);
@@ -387,90 +388,95 @@ impl View for SyntaxTreeView {
         {
             let layer = layer.clone();
             let theme = editor_theme.clone();
-            return MouseEventHandler::new::<Self, _>(0, cx, move |state, cx| {
-                let list_hovered = state.hovered();
-                UniformList::new(
-                    self.list_state.clone(),
-                    layer.node().descendant_count(),
-                    cx,
-                    move |this, range, items, cx| {
-                        let mut cursor = layer.node().walk();
-                        let mut descendant_ix = range.start as usize;
-                        cursor.goto_descendant(descendant_ix);
-                        let mut depth = cursor.depth();
-                        let mut visited_children = false;
-                        while descendant_ix < range.end {
-                            if visited_children {
-                                if cursor.goto_next_sibling() {
-                                    visited_children = false;
-                                } else if cursor.goto_parent() {
-                                    depth -= 1;
-                                } else {
-                                    break;
-                                }
+
+            let list_hovered = state.hovered();
+            uniform_list(
+                self.list_state.clone(),
+                layer.node().descendant_count(),
+                cx,
+                move |this, range, items, cx| {
+                    let mut cursor = layer.node().walk();
+                    let mut descendant_ix = range.start as usize;
+                    cursor.goto_descendant(descendant_ix);
+                    let mut depth = cursor.depth();
+                    let mut visited_children = false;
+                    while descendant_ix < range.end {
+                        if visited_children {
+                            if cursor.goto_next_sibling() {
+                                visited_children = false;
+                            } else if cursor.goto_parent() {
+                                depth -= 1;
                             } else {
-                                items.push(Self::render_node(
-                                    &cursor,
-                                    depth,
-                                    Some(descendant_ix) == this.selected_descendant_ix,
-                                    Some(descendant_ix) == this.hovered_descendant_ix,
-                                    list_hovered,
-                                    &style,
-                                    &theme,
-                                    cx,
-                                ));
-                                descendant_ix += 1;
-                                if cursor.goto_first_child() {
-                                    depth += 1;
-                                } else {
-                                    visited_children = true;
-                                }
+                                break;
+                            }
+                        } else {
+                            items.push(Self::render_node(
+                                &cursor,
+                                depth,
+                                Some(descendant_ix) == this.selected_descendant_ix,
+                                Some(descendant_ix) == this.hovered_descendant_ix,
+                                list_hovered,
+                                &style,
+                                &theme,
+                                cx,
+                            ));
+                            descendant_ix += 1;
+                            if cursor.goto_first_child() {
+                                depth += 1;
+                            } else {
+                                visited_children = true;
                             }
                         }
-                    },
-                )
-            })
+                    }
+                },
+            )
             .on_move(move |event, this, cx| {
                 let y = event.position.y() - event.region.origin_y();
                 this.mouse_y = Some(y);
                 this.hover_state_changed(cx);
             })
-            .on_click(MouseButton::Left, move |event, this, cx| {
+            .on_mouse_down(MouseButton::Left, move |event, cx| {
                 let y = event.position.y() - event.region.origin_y();
-                this.handle_click(y, cx);
-            })
-            .contained()
-            .with_background_color(editor_theme.background)
-            .into_any();
+                self.handle_click(y, cx);
+            });
         }
 
-        Empty::new().into_any()
+        div()
+    }
+}
+
+impl EventEmitter<()> for SyntaxTreeView {}
+
+impl FocusableView for SyntaxTreeView {
+    fn focus_handle(&self, _: &AppContext) -> gpui::FocusHandle {
+        self.focus_handle.clone()
     }
 }
 
 impl Item for SyntaxTreeView {
-    fn tab_content<V: 'static>(
-        &self,
-        _: Option<usize>,
-        style: &theme::Tab,
-        _: &AppContext,
-    ) -> gpui::AnyElement<V> {
-        Label::new("Syntax Tree", style.label.clone()).into_any()
+    type Event = ();
+
+    fn to_item_events(_: &Self::Event, _: impl FnMut(workspace::item::ItemEvent)) {}
+
+    fn tab_content(&self, _: Option<usize>, _: bool, _: &WindowContext<'_>) -> AnyElement {
+        Label::new("Syntax Tree").into_any_element()
     }
 
     fn clone_on_split(
         &self,
-        _workspace_id: workspace::WorkspaceId,
+        _: workspace::WorkspaceId,
         cx: &mut ViewContext<Self>,
-    ) -> Option<Self>
+    ) -> Option<View<Self>>
     where
         Self: Sized,
     {
-        let mut clone = Self::new(self.workspace_handle.clone(), None, cx);
-        if let Some(editor) = &self.editor {
-            clone.set_editor(editor.editor.clone(), cx)
-        }
-        Some(clone)
+        Some(cx.build_view(|cx| {
+            let mut clone = Self::new(self.workspace_handle.clone(), None, cx);
+            if let Some(editor) = &self.editor {
+                clone.set_editor(editor.editor.clone(), cx)
+            }
+            clone
+        }))
     }
 }
 
@@ -483,10 +489,7 @@ impl SyntaxTreeToolbarItemView {
         }
     }
 
-    fn render_menu(
-        &mut self,
-        cx: &mut ViewContext<'_, '_, Self>,
-    ) -> Option<gpui::AnyElement<Self>> {
+    fn render_menu(&mut self, cx: &mut ViewContext<'_, Self>) -> Option<Div> {
         let theme = cx.theme().clone();
         let tree_view = self.tree_view.as_ref()?;
         let tree_view = tree_view.read(cx);
@@ -496,36 +499,23 @@ impl SyntaxTreeToolbarItemView {
         let active_layer = buffer_state.active_layer.clone()?;
         let active_buffer = buffer_state.buffer.read(cx).snapshot();
 
-        enum Menu {}
-
         Some(
             v_stack()
-                .child(Self::render_header(&theme, &active_layer, cx))
+                .child(Self::render_header(&active_layer, cx))
                 .children(self.menu_open.then(|| {
-                    overlay(
-                        mouse_event_handler::<Menu, _>(0, cx, move |_, cx| {
-                            v_stack()
-                                .with_children(active_buffer.syntax_layers().enumerate().map(
-                                    |(ix, layer)| {
-                                        Self::render_menu_item(&theme, &active_layer, layer, ix, cx)
-                                    },
-                                ))
-                                .contained()
-                                .with_style(theme.toolbar_dropdown_menu.container)
-                                .constrained()
-                                .with_width(400.)
-                                .with_height(400.)
-                        })
-                        .on_down_out(MouseButton::Left, |_, this, cx| {
-                            this.menu_open = false;
-                            cx.notify()
-                        }),
+                    overlay().child(
+                        v_stack()
+                            .children(active_buffer.syntax_layers().enumerate().map(
+                                |(ix, layer)| Self::render_menu_item(&active_layer, layer, ix, cx),
+                            ))
+                            .on_mouse_down_out(|e, cx| {
+                                if e.button == MouseButton::Left {
+                                    self.menu_open = false;
+                                    cx.notify()
+                                }
+                            }),
                     )
-                    .with_hoverable(true)
-                    .with_fit_content()
-                    .into_any()
-                }))
-                .into_any(),
+                })),
         )
     }
 
@@ -549,71 +539,39 @@ impl SyntaxTreeToolbarItemView {
         })
     }
 
-    fn render_header(
-        theme: &Arc<Theme>,
-        active_layer: &OwnedSyntaxLayerInfo,
-        cx: &mut ViewContext<Self>,
-    ) -> impl Element<Self> {
-        enum ToggleMenu {}
-        MouseEventHandler::new::<ToggleMenu, _>(0, cx, move |state, _| {
-            let style = theme.toolbar_dropdown_menu.header.style_for(state);
-            Flex::row()
-                .with_child(
-                    Label::new(active_layer.language.name().to_string(), style.text.clone())
-                        .contained()
-                        .with_margin_right(style.secondary_text_spacing),
-                )
-                .with_child(Label::new(
-                    format_node_range(active_layer.node()),
-                    style
-                        .secondary_text
-                        .clone()
-                        .unwrap_or_else(|| style.text.clone()),
-                ))
-                .contained()
-                .with_style(style.container)
-        })
-        .with_cursor_style(CursorStyle::PointingHand)
-        .on_click(MouseButton::Left, move |_, view, cx| {
-            view.toggle_menu(cx);
-        })
+    fn render_header(active_layer: &OwnedSyntaxLayerInfo, cx: &mut ViewContext<Self>) -> Div {
+        let view = cx.view().clone();
+        h_stack()
+            .child(Label::new(active_layer.language.name()))
+            .child(Label::new(format_node_range(active_layer.node())))
+            .on_mouse_down(MouseButton::Left, move |_, cx| {
+                view.update(cx, |view, cx| view.toggle_menu(cx));
+            })
+            .cursor(CursorStyle::PointingHand)
+            .border_1()
+            .border_color(red())
     }
 
     fn render_menu_item(
-        theme: &Arc<Theme>,
         active_layer: &OwnedSyntaxLayerInfo,
         layer: SyntaxLayerInfo,
         layer_ix: usize,
         cx: &mut ViewContext<Self>,
-    ) -> impl Element<Self> {
-        enum ActivateLayer {}
-        MouseEventHandler::new::<ActivateLayer, _>(layer_ix, cx, move |state, _| {
-            let is_selected = layer.node() == active_layer.node();
-            let style = theme
-                .toolbar_dropdown_menu
-                .item
-                .in_state(is_selected)
-                .style_for(state);
-            Flex::row()
-                .with_child(
-                    Label::new(layer.language.name().to_string(), style.text.clone())
-                        .contained()
-                        .with_margin_right(style.secondary_text_spacing),
-                )
-                .with_child(Label::new(
-                    format_node_range(layer.node()),
-                    style
-                        .secondary_text
-                        .clone()
-                        .unwrap_or_else(|| style.text.clone()),
-                ))
-                .contained()
-                .with_style(style.container)
-        })
-        .with_cursor_style(CursorStyle::PointingHand)
-        .on_click(MouseButton::Left, move |_, view, cx| {
-            view.select_layer(layer_ix, cx);
-        })
+    ) -> Div {
+        // todo!() styling
+        let _is_selected = layer.node() == active_layer.node();
+        let view = cx.view().clone();
+        h_stack()
+            .child(Label::new(layer.language.name().to_string()))
+            .child(Label::new(format_node_range(layer.node())))
+            .cursor(CursorStyle::PointingHand)
+            .on_mouse_down(MouseButton::Left, move |_, cx| {
+                view.update(cx, |view, cx| {
+                    view.select_layer(layer_ix, cx);
+                })
+            })
+            .border_1()
+            .border_color(red())
     }
 }
 
@@ -630,33 +588,32 @@ fn format_node_range(node: Node) -> String {
 }
 
 impl Render for SyntaxTreeToolbarItemView {
-    type Element = Focusable<Div>;
+    type Element = Div;
 
     // todo!()
     // fn ui_name() -> &'static str {
     //     "SyntaxTreeToolbarItemView"
     // }
 
-    fn render(&mut self, cx: &mut ViewContext<'_, '_, Self>) -> gpui::AnyElement<Self> {
-        self.render_menu(cx)
-            .unwrap_or_else(|| Empty::new().into_any())
+    fn render(&mut self, cx: &mut ViewContext<'_, Self>) -> Div {
+        self.render_menu(cx).unwrap_or_else(|| div())
     }
 }
 
+impl EventEmitter<ToolbarItemEvent> for SyntaxTreeToolbarItemView {}
+
 impl ToolbarItemView for SyntaxTreeToolbarItemView {
     fn set_active_pane_item(
         &mut self,
         active_pane_item: Option<&dyn ItemHandle>,
         cx: &mut ViewContext<Self>,
-    ) -> workspace::ToolbarItemLocation {
+    ) -> ToolbarItemLocation {
         self.menu_open = false;
         if let Some(item) = active_pane_item {
             if let Some(view) = item.downcast::<SyntaxTreeView>() {
                 self.tree_view = Some(view.clone());
                 self.subscription = Some(cx.observe(&view, |_, _, cx| cx.notify()));
-                return ToolbarItemLocation::PrimaryLeft {
-                    flex: Some((1., false)),
-                };
+                return ToolbarItemLocation::PrimaryLeft;
             }
         }
         self.tree_view = None;