Use stock gpui2 ui components

Kirill Bulatov created

Change summary

crates/language_tools2/src/lsp_log.rs          | 183 +++++++------------
crates/language_tools2/src/syntax_tree_view.rs | 143 ++++-----------
crates/ui2/src/components/context_menu.rs      |   2 
3 files changed, 114 insertions(+), 214 deletions(-)

Detailed changes

crates/language_tools2/src/lsp_log.rs 🔗

@@ -2,16 +2,18 @@ use collections::{HashMap, VecDeque};
 use editor::{Editor, EditorElement, EditorEvent, MoveToEnd};
 use futures::{channel::mpsc, StreamExt};
 use gpui::{
-    actions, div, overlay, red, AnyElement, AppContext, Context, CursorStyle, Div, EventEmitter,
-    FocusHandle, FocusableView, InteractiveElement, IntoElement, Model, ModelContext, MouseButton,
-    MouseDownEvent, ParentElement, Render, Styled, Subscription, View, ViewContext, VisualContext,
+    actions, div, red, AnchorCorner, AnyElement, AppContext, Context, CursorStyle, Div,
+    EventEmitter, FocusHandle, FocusableView, InteractiveElement, IntoElement, Model, ModelContext,
+    MouseButton, 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 ui::{h_stack, v_stack, Checkbox, Label};
+use ui::{
+    h_stack, popover_menu, v_stack, Button, Checkbox, Clickable, ContextMenu, Divider, Label,
+};
 use workspace::{
     item::{Item, ItemHandle},
     searchable::{SearchEvent, SearchableItem, SearchableItemHandle},
@@ -58,7 +60,6 @@ pub struct LspLogView {
 pub struct LspLogToolbarItemView {
     log_view: Option<View<LspLogView>>,
     _log_view_subscription: Option<Subscription>,
-    menu_open: bool,
 }
 
 #[derive(Copy, Clone, PartialEq, Eq)]
@@ -594,11 +595,6 @@ fn log_contents(lines: &VecDeque<String>) -> String {
 }
 
 impl Render for LspLogView {
-    // todo!()
-    // fn ui_name() -> &'static str {
-    //     "LspLogView"
-    // }
-
     type Element = EditorElement;
 
     fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
@@ -697,7 +693,6 @@ impl ToolbarItemView for LspLogToolbarItemView {
         active_pane_item: Option<&dyn ItemHandle>,
         cx: &mut ViewContext<Self>,
     ) -> workspace::ToolbarItemLocation {
-        self.menu_open = false;
         if let Some(item) = active_pane_item {
             if let Some(log_view) = item.downcast::<LspLogView>() {
                 self.log_view = Some(log_view.clone());
@@ -715,13 +710,9 @@ impl ToolbarItemView for LspLogToolbarItemView {
 
 impl Render for LspLogToolbarItemView {
     type Element = Div;
-    // todo!()
-    // fn ui_name() -> &'static str {
-    //     "LspLogView"
-    // }
 
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> Div {
-        let Some(log_view) = self.log_view.as_ref() else {
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
+        let Some(log_view) = self.log_view.clone() else {
             return div();
         };
         let (menu_rows, current_server_id) = log_view.update(cx, |log_view, cx| {
@@ -737,70 +728,63 @@ impl Render for LspLogToolbarItemView {
                 None
             }
         });
-        // todo!() styling
-        let _server_selected = current_server.is_some();
 
-        let lsp_menu = h_stack()
-            .size_full()
-            .child(Self::render_language_server_menu_header(current_server, cx))
-            .children(if self.menu_open {
-                Some(
-                    overlay().child(
-                        v_stack()
-                            .size_full()
-                            // todo!()
-                            // .scrollable::<LspLogScroll>(0, None, cx)
-                            .children(menu_rows.into_iter().map(|row| {
-                                Self::render_language_server_menu_item(
-                                    row.server_id,
-                                    row.server_name,
-                                    &row.worktree_root_name,
-                                    row.rpc_trace_enabled,
-                                    row.logs_selected,
-                                    row.rpc_trace_selected,
-                                    cx,
-                                )
-                            }))
-                            .on_mouse_down_out(cx.listener(|this, event: &MouseDownEvent, cx| {
-                                if event.button == MouseButton::Left {
-                                    this.menu_open = false;
-                                    cx.notify()
-                                }
-                            })),
-                    ), // todo!()
-                       // .with_hoverable(true)
-                       // .with_fit_mode(OverlayFitMode::SwitchAnchor)
-                       // .with_anchor_corner(AnchorCorner::TopLeft)
-                )
-            } else {
-                None
-            })
-            .z_index(99);
-
-        let log_cleanup_button = div()
-            .child(Label::new("Clear"))
-            .on_mouse_down(
-                MouseButton::Left,
-                cx.listener(move |this, _, cx| {
-                    if let Some(log_view) = this.log_view.as_ref() {
-                        log_view.update(cx, |log_view, cx| {
-                            log_view.editor.update(cx, |editor, cx| {
-                                editor.set_read_only(false);
-                                editor.clear(cx);
-                                editor.set_read_only(true);
-                            });
-                        })
+        let lsp_menu = popover_menu("LspLogView")
+            .anchor(AnchorCorner::TopLeft)
+            .trigger(Self::render_language_server_menu_header(current_server))
+            .menu(move |cx| {
+                let menu_rows = menu_rows.clone();
+                let log_view = log_view.clone();
+                ContextMenu::build(cx, move |mut menu, cx| {
+                    for row in menu_rows {
+                        menu = menu
+                            .header(format!(
+                                "{} ({})",
+                                row.server_name.0, row.worktree_root_name
+                            ))
+                            .entry(
+                                format!("{SERVER_LOGS} ({})", row.server_name.0),
+                                |cx| {
+                                    dbg!("????????????????????");
+                                }, // cx.handler_for(&log_view, move |view, cx| {
+                                   //     // todo!() why does not it work???
+                                   //     dbg!("~~~~~~~~~~~~~~~~~~~~~~~~~~??@@@#", row.server_id);
+                                   //     view.show_logs_for_server(row.server_id, cx)
+                                   // }),
+                            )
+                            // TODO kb custom element with checkbox & toggle logging for server
+                            .entry(
+                                format!("{RPC_MESSAGES} ({})", row.server_name.0),
+                                |cx| {
+                                    dbg!("?????????????@@@@@@@@@@@@@@@");
+                                }, // cx.handler_for(&log_view, move |view, cx| {
+                                   //     view.show_rpc_trace_for_server(row.server_id, cx)
+                                   // }),
+                            )
                     }
-                }),
-            )
-            .cursor(CursorStyle::PointingHand);
+                    menu
+                })
+            });
 
-        h_stack()
-            .size_full()
-            .child(lsp_menu)
-            .child(log_cleanup_button)
-            .border_1()
-            .border_color(red())
+        h_stack().size_full().child(lsp_menu).child(
+            div()
+                .child(
+                    Button::new("clear_log_button", "Clear").on_click(cx.listener(
+                        |this, _, cx| {
+                            if let Some(log_view) = this.log_view.as_ref() {
+                                log_view.update(cx, |log_view, cx| {
+                                    log_view.editor.update(cx, |editor, cx| {
+                                        editor.set_read_only(false);
+                                        editor.clear(cx);
+                                        editor.set_read_only(true);
+                                    });
+                                })
+                            }
+                        },
+                    )),
+                )
+                .ml_2(),
+        )
     }
 }
 
@@ -810,17 +794,11 @@ const SERVER_LOGS: &str = "Server Logs";
 impl LspLogToolbarItemView {
     pub fn new() -> Self {
         Self {
-            menu_open: false,
             log_view: None,
             _log_view_subscription: None,
         }
     }
 
-    fn toggle_menu(&mut self, cx: &mut ViewContext<Self>) {
-        self.menu_open = !self.menu_open;
-        cx.notify();
-    }
-
     fn toggle_logging_for_server(
         &mut self,
         id: LanguageServerId,
@@ -842,7 +820,6 @@ impl LspLogToolbarItemView {
     fn show_logs_for_server(&mut self, id: LanguageServerId, cx: &mut ViewContext<Self>) {
         if let Some(log_view) = &self.log_view {
             log_view.update(cx, |view, cx| view.show_logs_for_server(id, cx));
-            self.menu_open = false;
             cx.notify();
         }
     }
@@ -850,19 +827,16 @@ impl LspLogToolbarItemView {
     fn show_rpc_trace_for_server(&mut self, id: LanguageServerId, cx: &mut ViewContext<Self>) {
         if let Some(log_view) = &self.log_view {
             log_view.update(cx, |view, cx| view.show_rpc_trace_for_server(id, cx));
-            self.menu_open = false;
             cx.notify();
         }
     }
 
-    fn render_language_server_menu_header(
-        current_server: Option<LogMenuItem>,
-        cx: &mut ViewContext<Self>,
-    ) -> Div {
-        let label: Cow<str> = current_server
-            .and_then(|row| {
-                Some(
-                    format!(
+    fn render_language_server_menu_header(current_server: Option<LogMenuItem>) -> Button {
+        Button::new(
+            "language_server_menu_header",
+            current_server
+                .and_then(|row| {
+                    Some(Cow::Owned(format!(
                         "{} ({}) - {}",
                         row.server_name.0,
                         row.worktree_root_name,
@@ -871,22 +845,10 @@ impl LspLogToolbarItemView {
                         } else {
                             SERVER_LOGS
                         },
-                    )
-                    .into(),
-                )
-            })
-            .unwrap_or_else(|| "No server selected".into());
-        div()
-            .child(Label::new(label))
-            .cursor(CursorStyle::PointingHand)
-            .on_mouse_down(
-                MouseButton::Left,
-                cx.listener(move |view, _, cx| {
-                    view.toggle_menu(cx);
-                }),
-            )
-            .border_1()
-            .border_color(red())
+                    )))
+                })
+                .unwrap_or_else(|| "No server selected".into()),
+        )
     }
 
     fn render_language_server_menu_item(
@@ -894,7 +856,6 @@ impl LspLogToolbarItemView {
         name: LanguageServerName,
         worktree_root_name: &str,
         rpc_trace_enabled: bool,
-        // todo!() styling
         _logs_selected: bool,
         _rpc_trace_selected: bool,
         cx: &mut ViewContext<Self>,

crates/language_tools2/src/syntax_tree_view.rs 🔗

@@ -1,20 +1,19 @@
 use editor::{scroll::autoscroll::Autoscroll, Anchor, Editor, ExcerptId};
 use gpui::{
-    actions, div, overlay, red, uniform_list, AnyElement, AppContext, CursorStyle, Div,
-    EventEmitter, FocusHandle, FocusableView, Hsla, InteractiveElement, IntoElement, Model,
-    MouseButton, MouseDownEvent, MouseMoveEvent, ParentElement, Pixels, Render, Styled, TextStyle,
-    UniformListScrollHandle, View, ViewContext, VisualContext, WeakView, WindowContext,
+    actions, div, rems, uniform_list, AnyElement, AppContext, Div, EventEmitter, FocusHandle,
+    FocusableView, Hsla, InteractiveElement, IntoElement, Model, MouseButton, MouseDownEvent,
+    MouseMoveEvent, ParentElement, Pixels, Render, Styled, TextStyle, UniformListScrollHandle,
+    View, ViewContext, VisualContext, WeakView, WindowContext,
 };
-use language::{Buffer, OwnedSyntaxLayerInfo, SyntaxLayerInfo};
+use language::{Buffer, OwnedSyntaxLayerInfo};
 use settings::Settings;
 use std::{mem, ops::Range};
 use theme::{Theme, ThemeSettings};
 use tree_sitter::{Node, TreeCursor};
-use ui::{h_stack, Label};
+use ui::{h_stack, popover_menu, ButtonLike, ContextMenu, Label, PopoverMenu};
 use workspace::{
     item::{Item, ItemHandle},
-    ui::v_stack,
-    ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView, Workspace,
+    SplitDirection, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView, Workspace,
 };
 
 actions!(debug, [OpenSyntaxTreeView]);
@@ -26,7 +25,7 @@ pub fn init(cx: &mut AppContext) {
             let workspace_handle = workspace.weak_handle();
             let syntax_tree_view =
                 cx.build_view(|cx| SyntaxTreeView::new(workspace_handle, active_item, cx));
-            workspace.add_item(Box::new(syntax_tree_view), cx);
+            workspace.split_item(SplitDirection::Right, Box::new(syntax_tree_view), cx)
         });
     })
     .detach();
@@ -46,7 +45,6 @@ pub struct SyntaxTreeView {
 pub struct SyntaxTreeToolbarItemView {
     tree_view: Option<View<SyntaxTreeView>>,
     subscription: Option<gpui::Subscription>,
-    menu_open: bool,
 }
 
 struct EditorState {
@@ -279,7 +277,7 @@ impl SyntaxTreeView {
 
     fn render_node(
         cursor: &TreeCursor,
-        _depth: u32,
+        depth: u32,
         selected: bool,
         hovered: bool,
         list_hovered: bool,
@@ -290,18 +288,6 @@ impl SyntaxTreeView {
         let editor_colors = editor_theme.colors();
         let node = cursor.node();
         let mut range_style = style.clone();
-        // todo!() styling
-        // let font_id = cx.text_system().font_id(&style.text.font()).unwrap();
-        // let font_size = style.text.font_size.to_pixels(cx.rem_size());
-        // let line_height = style.text.line_height_in_pixels(cx.rem_size());
-        // let em_width = cx
-        //     .text_system()
-        //     .typographic_bounds(font_id, font_size, 'm')
-        //     .unwrap()
-        //     .size
-        //     .width;
-        // let gutter_padding = (em_width * editor_theme.gutter_padding_factor).round();
-
         range_style.color = editor_colors.editor_line_number;
 
         let mut anonymous_node_style = style.clone();
@@ -335,8 +321,7 @@ impl SyntaxTreeView {
                     Label::new(node.kind())
                 } else {
                     Label::new(format!("\"{}\"", node.kind()))
-                }, // todo!()
-                   // .margin(em_width),
+                },
             )
             .child(Label::new(format_node_range(node)))
             .text_bg(if selected {
@@ -346,10 +331,10 @@ impl SyntaxTreeView {
             } else {
                 Hsla::default()
             })
-            // todo!()
+            // todo!() does not work
+            .ml(rems(depth as f32 * 180.0))
             // .padding(gutter_padding + depth as f32 * 18.0)
-            .border_1()
-            .border_color(red());
+            ;
     }
 }
 
@@ -389,8 +374,6 @@ impl Render for SyntaxTreeView {
             let layer = layer.clone();
             let theme = editor_theme.clone();
 
-            // todo!()
-            // let list_hovered = state.hovered();
             let list_hovered = false;
             let list = uniform_list(
                 cx.view().clone(),
@@ -434,6 +417,7 @@ impl Render for SyntaxTreeView {
                     items
                 },
             )
+            // todo!() does scroll either editor or the tree
             .track_scroll(self.list_scroll_handle.clone())
             .on_mouse_move(cx.listener(move |tree_view, event: &MouseMoveEvent, cx| {
                 tree_view.mouse_y = Some(event.position.y);
@@ -492,13 +476,12 @@ impl Item for SyntaxTreeView {
 impl SyntaxTreeToolbarItemView {
     pub fn new() -> Self {
         Self {
-            menu_open: false,
             tree_view: None,
             subscription: None,
         }
     }
 
-    fn render_menu(&mut self, cx: &mut ViewContext<'_, Self>) -> Option<Div> {
+    fn render_menu(&mut self, cx: &mut ViewContext<'_, Self>) -> Option<PopoverMenu<ContextMenu>> {
         let tree_view = self.tree_view.as_ref()?;
         let tree_view = tree_view.read(cx);
 
@@ -507,34 +490,30 @@ impl SyntaxTreeToolbarItemView {
         let active_layer = buffer_state.active_layer.clone()?;
         let active_buffer = buffer_state.buffer.read(cx).snapshot();
 
+        let view = cx.view().clone();
         Some(
-            v_stack()
-                .size_full()
-                .child(Self::render_header(&active_layer, cx))
-                .children(self.menu_open.then(|| {
-                    overlay().child(
-                        v_stack()
-                            .size_full()
-                            .children(active_buffer.syntax_layers().enumerate().map(
-                                |(ix, layer)| Self::render_menu_item(&active_layer, layer, ix, cx),
-                            ))
-                            .on_mouse_down_out(cx.listener(|this, e: &MouseDownEvent, cx| {
-                                if e.button == MouseButton::Left {
-                                    this.menu_open = false;
-                                    cx.notify()
-                                }
-                            })),
-                    )
-                }))
-                .z_index(99),
+            popover_menu("Syntax Tree")
+                .trigger(Self::render_header(&active_layer))
+                .menu(move |cx| {
+                    ContextMenu::build(cx, |mut menu, cx| {
+                        for (layer_ix, layer) in active_buffer.syntax_layers().enumerate() {
+                            menu = menu.entry(
+                                format!(
+                                    "{} {}",
+                                    layer.language.name(),
+                                    format_node_range(layer.node())
+                                ),
+                                cx.handler_for(&view, move |view, cx| {
+                                    view.select_layer(layer_ix, cx);
+                                }),
+                            );
+                        }
+                        menu
+                    })
+                }),
         )
     }
 
-    fn toggle_menu(&mut self, cx: &mut ViewContext<Self>) {
-        self.menu_open = !self.menu_open;
-        cx.notify();
-    }
-
     fn select_layer(&mut self, layer_ix: usize, cx: &mut ViewContext<Self>) -> Option<()> {
         let tree_view = self.tree_view.as_ref()?;
         tree_view.update(cx, |view, cx| {
@@ -544,50 +523,15 @@ impl SyntaxTreeToolbarItemView {
             let layer = snapshot.syntax_layers().nth(layer_ix)?;
             buffer_state.active_layer = Some(layer.to_owned());
             view.selected_descendant_ix = None;
-            self.menu_open = false;
             cx.notify();
             Some(())
         })
     }
 
-    fn render_header(active_layer: &OwnedSyntaxLayerInfo, cx: &mut ViewContext<Self>) -> Div {
-        h_stack()
-            .size_full()
+    fn render_header(active_layer: &OwnedSyntaxLayerInfo) -> ButtonLike {
+        ButtonLike::new("syntax tree header")
             .child(Label::new(active_layer.language.name()))
             .child(Label::new(format_node_range(active_layer.node())))
-            .on_mouse_down(
-                MouseButton::Left,
-                cx.listener(move |view, _, cx| {
-                    view.toggle_menu(cx);
-                }),
-            )
-            .cursor(CursorStyle::PointingHand)
-            .border_1()
-            .border_color(red())
-    }
-
-    fn render_menu_item(
-        active_layer: &OwnedSyntaxLayerInfo,
-        layer: SyntaxLayerInfo,
-        layer_ix: usize,
-        cx: &mut ViewContext<Self>,
-    ) -> Div {
-        // todo!() styling
-        let _is_selected = layer.node() == active_layer.node();
-        h_stack()
-            .size_full()
-            .child(Label::new(layer.language.name().to_string()))
-            .child(Label::new(format_node_range(layer.node())))
-            .cursor(CursorStyle::PointingHand)
-            .on_mouse_down(
-                MouseButton::Left,
-                cx.listener(move |view, _, cx| {
-                    view.select_layer(layer_ix, cx);
-                }),
-            )
-            .border_1()
-            .border_color(red())
-            .bg(red())
     }
 }
 
@@ -604,15 +548,11 @@ fn format_node_range(node: Node) -> String {
 }
 
 impl Render for SyntaxTreeToolbarItemView {
-    type Element = Div;
-
-    // todo!()
-    // fn ui_name() -> &'static str {
-    //     "SyntaxTreeToolbarItemView"
-    // }
+    type Element = PopoverMenu<ContextMenu>;
 
-    fn render(&mut self, cx: &mut ViewContext<'_, Self>) -> Div {
-        self.render_menu(cx).unwrap_or_else(|| div())
+    fn render(&mut self, cx: &mut ViewContext<'_, Self>) -> PopoverMenu<ContextMenu> {
+        self.render_menu(cx)
+            .unwrap_or_else(|| popover_menu("Empty Syntax Tree"))
     }
 }
 
@@ -624,7 +564,6 @@ impl ToolbarItemView for SyntaxTreeToolbarItemView {
         active_pane_item: Option<&dyn ItemHandle>,
         cx: &mut ViewContext<Self>,
     ) -> 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());

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

@@ -9,7 +9,7 @@ use gpui::{
 use menu::{SelectFirst, SelectLast, SelectNext, SelectPrev};
 use std::{rc::Rc, time::Duration};
 
-pub enum ContextMenuItem {
+enum ContextMenuItem {
     Separator,
     Header(SharedString),
     Entry {