From 75074c329792d72ac53b2db0ae13c56e345d1466 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Fri, 15 Dec 2023 15:32:09 +0200 Subject: [PATCH] Apply more fixes to the visual part --- Cargo.lock | 1 + crates/editor2/src/editor.rs | 13 +- crates/language_tools2/Cargo.toml | 1 + crates/language_tools2/src/lsp_log.rs | 388 ++++++++---------- .../language_tools2/src/syntax_tree_view.rs | 361 +++++++--------- 5 files changed, 335 insertions(+), 429 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4f418a1813837cdb60f325305aa223a4b1a8a41e..7237ab6905db5a5aadfa1c4613114c3f12ae6bca 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5008,6 +5008,7 @@ dependencies = [ "settings2", "theme2", "tree-sitter", + "ui2", "unindent", "util", "workspace2", diff --git a/crates/editor2/src/editor.rs b/crates/editor2/src/editor.rs index 58f8e857a9a167219b5e1cdeec5a2bbb1b5b62f9..81158c5351052d450a0b329e03a09bc1dc305eac 100644 --- a/crates/editor2/src/editor.rs +++ b/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>, - // cx: &mut ViewContext, - // ) -> 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 { + 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 { let buffer = cx.build_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), String::new())); diff --git a/crates/language_tools2/Cargo.toml b/crates/language_tools2/Cargo.toml index ca3ede2ef9a1e80e32afedec15bf8c0bdf02afbc..bf5202cda9959556a5d225c8b1ce01591d7c8eac 100644 --- a/crates/language_tools2/Cargo.toml +++ b/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 diff --git a/crates/language_tools2/src/lsp_log.rs b/crates/language_tools2/src/lsp_log.rs index 6e433ba8a64e0bcd9469bf96306b81c3025e947b..ca4f25e177fc3204ea155600d184d76c50bdfa08 100644 --- a/crates/language_tools2/src/lsp_log.rs +++ b/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, is_showing_rpc_trace: bool, project: Model, + focus_handle: FocusHandle, _log_store_subscriptions: Vec, } @@ -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::({ 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, ¶ms.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, ) -> (View, 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 { } } -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) -> AnyElement { - ChildView::new(&self.editor, cx).into_any() + type Element = EditorElement; + + fn render(&mut self, cx: &mut ViewContext) -> Self::Element { + self.editor.update(cx, |editor, cx| editor.render(cx)) } +} - fn focus_in(&mut self, _: gpui::AnyView, cx: &mut ViewContext) { - 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( - &self, - _: Option, - style: &theme::Tab, - _: &AppContext, - ) -> AnyElement { - 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, _: bool, _: &WindowContext<'_>) -> AnyElement { + Label::new("LSP Logs").into_any_element() } fn as_searchable(&self, handle: &View) -> Option> { @@ -608,15 +630,6 @@ impl Item for LspLogView { impl SearchableItem for LspLogView { type Match = ::Match; - fn to_search_event( - &mut self, - event: &Self::Event, - cx: &mut ViewContext, - ) -> Option { - self.editor - .update(cx, |editor, cx| editor.to_search_event(event, cx)) - } - fn clear_matches(&mut self, cx: &mut ViewContext) { self.editor.update(cx, |e, cx| e.clear_matches(cx)) } @@ -675,6 +688,8 @@ impl SearchableItem for LspLogView { } } +impl EventEmitter 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) -> AnyElement { - let theme = theme::current(cx).clone(); + fn render(&mut self, cx: &mut ViewContext) -> 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::(0, cx, move |_, cx| { - Flex::column() + overlay() + .child( + v_stack() .scrollable::(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::(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, - theme: &Arc, cx: &mut ViewContext, - ) -> impl Element { + ) -> Div { + let view = cx.view().clone(); enum ToggleMenu {} - MouseEventHandler::new::(0, cx, move |state, _| { - let label: Cow = 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 = 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, cx: &mut ViewContext, - ) -> impl Element { + ) -> 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::(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::(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::( - 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::( + 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 for LogStore {} +impl EventEmitter for LspLogView {} +impl EventEmitter for LspLogView {} +impl EventEmitter for LspLogView {} diff --git a/crates/language_tools2/src/syntax_tree_view.rs b/crates/language_tools2/src/syntax_tree_view.rs index 80ff211176598539538bd3d8cd490feb7f2ef5cd..7efd5f572323d08a8c2c9b9e675eeedee2fd5964 100644 --- a/crates/language_tools2/src/syntax_tree_view.rs +++ b/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, hovered_descendant_ix: Option, + 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::(cx); editor.highlight_background::( 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 { + ) -> 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 { - let settings = settings::get::(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::(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( - &self, - _: Option, - style: &theme::Tab, - _: &AppContext, - ) -> gpui::AnyElement { - 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, _: bool, _: &WindowContext<'_>) -> AnyElement { + Label::new("Syntax Tree").into_any_element() } fn clone_on_split( &self, - _workspace_id: workspace::WorkspaceId, + _: workspace::WorkspaceId, cx: &mut ViewContext, - ) -> Option + ) -> Option> 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> { + fn render_menu(&mut self, cx: &mut ViewContext<'_, Self>) -> Option
{ 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::(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, - active_layer: &OwnedSyntaxLayerInfo, - cx: &mut ViewContext, - ) -> impl Element { - enum ToggleMenu {} - MouseEventHandler::new::(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) -> 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, active_layer: &OwnedSyntaxLayerInfo, layer: SyntaxLayerInfo, layer_ix: usize, cx: &mut ViewContext, - ) -> impl Element { - enum ActivateLayer {} - MouseEventHandler::new::(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
; + type Element = Div; // todo!() // fn ui_name() -> &'static str { // "SyntaxTreeToolbarItemView" // } - fn render(&mut self, cx: &mut ViewContext<'_, '_, Self>) -> gpui::AnyElement { - 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 for SyntaxTreeToolbarItemView {} + impl ToolbarItemView for SyntaxTreeToolbarItemView { fn set_active_pane_item( &mut self, active_pane_item: Option<&dyn ItemHandle>, cx: &mut ViewContext, - ) -> workspace::ToolbarItemLocation { + ) -> ToolbarItemLocation { self.menu_open = false; if let Some(item) = active_pane_item { if let Some(view) = item.downcast::() { 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;