Use stock gpui2 ui components

This commit is contained in:
Kirill Bulatov 2023-12-15 23:50:20 +02:00
parent 3586bf8b6b
commit 6a90a76bf2
3 changed files with 114 additions and 214 deletions

View file

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

View file

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

View file

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