Zed2 breadcrumbs & tab bar context menus & pane focus events (#3475)

See title

Release Notes:

- N/A
This commit is contained in:
Julia
2023-12-01 12:53:15 -05:00
committed by GitHub
17 changed files with 548 additions and 5836 deletions
Generated
+18
View File
@@ -1095,6 +1095,23 @@ dependencies = [
"workspace",
]
[[package]]
name = "breadcrumbs2"
version = "0.1.0"
dependencies = [
"collections",
"editor2",
"gpui2",
"itertools 0.10.5",
"language2",
"project2",
"search2",
"settings2",
"theme2",
"ui2",
"workspace2",
]
[[package]]
name = "bromberg_sl2"
version = "0.6.0"
@@ -11726,6 +11743,7 @@ dependencies = [
"audio2",
"auto_update2",
"backtrace",
"breadcrumbs2",
"call2",
"channel2",
"chrono",
+1
View File
@@ -9,6 +9,7 @@ members = [
"crates/auto_update",
"crates/auto_update2",
"crates/breadcrumbs",
"crates/breadcrumbs2",
"crates/call",
"crates/call2",
"crates/channel",
+28
View File
@@ -0,0 +1,28 @@
[package]
name = "breadcrumbs2"
version = "0.1.0"
edition = "2021"
publish = false
[lib]
path = "src/breadcrumbs.rs"
doctest = false
[dependencies]
collections = { path = "../collections" }
editor = { package = "editor2", path = "../editor2" }
gpui = { package = "gpui2", path = "../gpui2" }
ui = { package = "ui2", path = "../ui2" }
language = { package = "language2", path = "../language2" }
project = { package = "project2", path = "../project2" }
search = { package = "search2", path = "../search2" }
settings = { package = "settings2", path = "../settings2" }
theme = { package = "theme2", path = "../theme2" }
workspace = { package = "workspace2", path = "../workspace2" }
# outline = { path = "../outline" }
itertools = "0.10"
[dev-dependencies]
editor = { package = "editor2", path = "../editor2", features = ["test-support"] }
gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
workspace = { package = "workspace2", path = "../workspace2", features = ["test-support"] }
+204
View File
@@ -0,0 +1,204 @@
use gpui::{
Component, Element, EventEmitter, IntoElement, ParentElement, Render, StyledText, Subscription,
ViewContext, WeakView,
};
use itertools::Itertools;
use theme::ActiveTheme;
use ui::{ButtonCommon, ButtonLike, ButtonStyle, Clickable, Disableable, Label};
use workspace::{
item::{ItemEvent, ItemHandle},
ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView, Workspace,
};
pub enum Event {
UpdateLocation,
}
pub struct Breadcrumbs {
pane_focused: bool,
active_item: Option<Box<dyn ItemHandle>>,
subscription: Option<Subscription>,
_workspace: WeakView<Workspace>,
}
impl Breadcrumbs {
pub fn new(workspace: &Workspace) -> Self {
Self {
pane_focused: false,
active_item: Default::default(),
subscription: Default::default(),
_workspace: workspace.weak_handle(),
}
}
}
impl EventEmitter<Event> for Breadcrumbs {}
impl EventEmitter<ToolbarItemEvent> for Breadcrumbs {}
impl Render for Breadcrumbs {
type Element = Component<ButtonLike>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
let button = ButtonLike::new("breadcrumbs")
.style(ButtonStyle::Transparent)
.disabled(true);
let active_item = match &self.active_item {
Some(active_item) => active_item,
None => return button.into_element(),
};
let not_editor = active_item.downcast::<editor::Editor>().is_none();
let breadcrumbs = match active_item.breadcrumbs(cx.theme(), cx) {
Some(breadcrumbs) => breadcrumbs,
None => return button.into_element(),
}
.into_iter()
.map(|breadcrumb| {
StyledText::new(breadcrumb.text)
.with_highlights(&cx.text_style(), breadcrumb.highlights.unwrap_or_default())
.into_any()
});
let button = button.children(Itertools::intersperse_with(breadcrumbs, || {
Label::new(" ").into_any_element()
}));
if not_editor || !self.pane_focused {
return button.into_element();
}
// let this = cx.view().downgrade();
button
.style(ButtonStyle::Filled)
.disabled(false)
.on_click(move |_, _cx| {
todo!("outline::toggle");
// this.update(cx, |this, cx| {
// if let Some(workspace) = this.workspace.upgrade() {
// workspace.update(cx, |_workspace, _cx| {
// outline::toggle(workspace, &Default::default(), cx)
// })
// }
// })
// .ok();
})
.into_element()
}
}
// impl View for Breadcrumbs {
// fn ui_name() -> &'static str {
// "Breadcrumbs"
// }
// fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
// let active_item = match &self.active_item {
// Some(active_item) => active_item,
// None => return Empty::new().into_any(),
// };
// let not_editor = active_item.downcast::<editor::Editor>().is_none();
// let theme = theme::current(cx).clone();
// let style = &theme.workspace.toolbar.breadcrumbs;
// let breadcrumbs = match active_item.breadcrumbs(&theme, cx) {
// Some(breadcrumbs) => breadcrumbs,
// None => return Empty::new().into_any(),
// }
// .into_iter()
// .map(|breadcrumb| {
// Text::new(
// breadcrumb.text,
// theme.workspace.toolbar.breadcrumbs.default.text.clone(),
// )
// .with_highlights(breadcrumb.highlights.unwrap_or_default())
// .into_any()
// });
// let crumbs = Flex::row()
// .with_children(Itertools::intersperse_with(breadcrumbs, || {
// Label::new(" ", style.default.text.clone()).into_any()
// }))
// .constrained()
// .with_height(theme.workspace.toolbar.breadcrumb_height)
// .contained();
// if not_editor || !self.pane_focused {
// return crumbs
// .with_style(style.default.container)
// .aligned()
// .left()
// .into_any();
// }
// MouseEventHandler::new::<Breadcrumbs, _>(0, cx, |state, _| {
// let style = style.style_for(state);
// crumbs.with_style(style.container)
// })
// .on_click(MouseButton::Left, |_, this, cx| {
// if let Some(workspace) = this.workspace.upgrade(cx) {
// workspace.update(cx, |workspace, cx| {
// outline::toggle(workspace, &Default::default(), cx)
// })
// }
// })
// .with_tooltip::<Breadcrumbs>(
// 0,
// "Show symbol outline".to_owned(),
// Some(Box::new(outline::Toggle)),
// theme.tooltip.clone(),
// cx,
// )
// .aligned()
// .left()
// .into_any()
// }
// }
impl ToolbarItemView for Breadcrumbs {
fn set_active_pane_item(
&mut self,
active_pane_item: Option<&dyn ItemHandle>,
cx: &mut ViewContext<Self>,
) -> ToolbarItemLocation {
cx.notify();
self.active_item = None;
if let Some(item) = active_pane_item {
let this = cx.view().downgrade();
self.subscription = Some(item.subscribe_to_item_events(
cx,
Box::new(move |event, cx| {
if let ItemEvent::UpdateBreadcrumbs = event {
this.update(cx, |_, cx| {
cx.emit(Event::UpdateLocation);
cx.notify();
})
.ok();
}
}),
));
self.active_item = Some(item.boxed_clone());
item.breadcrumb_location(cx)
} else {
ToolbarItemLocation::Hidden
}
}
// fn location_for_event(
// &self,
// _: &Event,
// current_location: ToolbarItemLocation,
// cx: &AppContext,
// ) -> ToolbarItemLocation {
// if let Some(active_item) = self.active_item.as_ref() {
// active_item.breadcrumb_location(cx)
// } else {
// current_location
// }
// }
fn pane_focus_update(&mut self, pane_focused: bool, _: &mut ViewContext<Self>) {
self.pane_focused = pane_focused;
}
}
+1 -1
View File
@@ -346,7 +346,7 @@ impl<T: Entity> Drop for PendingEntitySubscription<T> {
}
}
#[derive(Copy, Clone)]
#[derive(Debug, Copy, Clone)]
pub struct TelemetrySettings {
pub diagnostics: bool,
pub metrics: bool,
+1
View File
@@ -350,6 +350,7 @@ impl Telemetry {
milliseconds_since_first_event: self.milliseconds_since_first_event(),
};
dbg!(telemetry_settings);
self.report_clickhouse_event(event, telemetry_settings, true)
}
+2 -32
View File
@@ -154,7 +154,6 @@ pub fn render_parsed_markdown(
}
}),
);
let runs = text_runs_for_highlights(&parsed.text, &editor_style.text, highlights);
let mut links = Vec::new();
let mut link_ranges = Vec::new();
@@ -167,7 +166,7 @@ pub fn render_parsed_markdown(
InteractiveText::new(
element_id,
StyledText::new(parsed.text.clone()).with_runs(runs),
StyledText::new(parsed.text.clone()).with_highlights(&editor_style.text, highlights),
)
.on_click(link_ranges, move |clicked_range_ix, cx| {
match &links[clicked_range_ix] {
@@ -1199,11 +1198,7 @@ impl CompletionsMenu {
),
);
let completion_label = StyledText::new(completion.label.text.clone())
.with_runs(text_runs_for_highlights(
&completion.label.text,
&style.text,
highlights,
));
.with_highlights(&style.text, highlights);
let documentation_label =
if let Some(Documentation::SingleLine(text)) = documentation {
Some(SharedString::from(text.clone()))
@@ -9755,31 +9750,6 @@ pub fn diagnostic_style(
}
}
pub fn text_runs_for_highlights(
text: &str,
default_style: &TextStyle,
highlights: impl IntoIterator<Item = (Range<usize>, HighlightStyle)>,
) -> Vec<TextRun> {
let mut runs = Vec::new();
let mut ix = 0;
for (range, highlight) in highlights {
if ix < range.start {
runs.push(default_style.clone().to_run(range.start - ix));
}
runs.push(
default_style
.clone()
.highlight(highlight)
.to_run(range.len()),
);
ix = range.end;
}
if ix < text.len() {
runs.push(default_style.to_run(text.len() - ix));
}
runs
}
pub fn styled_runs_for_code_label<'a>(
label: &'a CodeLabel,
syntax_theme: &'a theme::SyntaxTheme,
+25 -3
View File
@@ -1,6 +1,7 @@
use crate::{
Bounds, DispatchPhase, Element, ElementId, IntoElement, LayoutId, MouseDownEvent, MouseUpEvent,
Pixels, Point, SharedString, Size, TextRun, WhiteSpace, WindowContext, WrappedLine,
Bounds, DispatchPhase, Element, ElementId, HighlightStyle, IntoElement, LayoutId,
MouseDownEvent, MouseUpEvent, Pixels, Point, SharedString, Size, TextRun, TextStyle,
WhiteSpace, WindowContext, WrappedLine,
};
use anyhow::anyhow;
use parking_lot::{Mutex, MutexGuard};
@@ -87,7 +88,28 @@ impl StyledText {
}
}
pub fn with_runs(mut self, runs: Vec<TextRun>) -> Self {
pub fn with_highlights(
mut self,
default_style: &TextStyle,
highlights: impl IntoIterator<Item = (Range<usize>, HighlightStyle)>,
) -> Self {
let mut runs = Vec::new();
let mut ix = 0;
for (range, highlight) in highlights {
if ix < range.start {
runs.push(default_style.clone().to_run(range.start - ix));
}
runs.push(
default_style
.clone()
.highlight(highlight)
.to_run(range.len()),
);
ix = range.end;
}
if ix < self.text.len() {
runs.push(default_style.to_run(self.text.len() - ix));
}
self.runs = Some(runs);
self
}
+6 -8
View File
@@ -1,6 +1,6 @@
use gpui::{
blue, div, green, red, white, Div, InteractiveText, ParentElement, Render, Styled, StyledText,
TextRun, View, VisualContext, WindowContext,
blue, div, green, red, white, Div, HighlightStyle, InteractiveText, ParentElement, Render,
Styled, StyledText, View, VisualContext, WindowContext,
};
use ui::v_stack;
@@ -59,13 +59,11 @@ impl Render for TextStory {
))).child(
InteractiveText::new(
"interactive",
StyledText::new("Hello world, how is it going?").with_runs(vec![
cx.text_style().to_run(6),
TextRun {
StyledText::new("Hello world, how is it going?").with_highlights(&cx.text_style(), [
(6..11, HighlightStyle {
background_color: Some(green()),
..cx.text_style().to_run(5)
},
cx.text_style().to_run(18),
..Default::default()
}),
]),
)
.on_click(vec![2..4, 1..3, 7..9], |range_ix, _cx| {
@@ -317,7 +317,7 @@ impl RenderOnce for ButtonLike {
.id(self.id.clone())
.h(self.size.height())
.rounded_md()
.cursor_pointer()
.when(!self.disabled, |el| el.cursor_pointer())
.gap_1()
.px_1()
.bg(self.style.enabled(cx).background)
+23 -27
View File
@@ -1,6 +1,8 @@
use std::ops::Range;
use crate::prelude::*;
use crate::styled_ext::StyledExt;
use gpui::{relative, Div, IntoElement, StyledText, TextRun, WindowContext};
use gpui::{relative, Div, HighlightStyle, IntoElement, StyledText, WindowContext};
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Default)]
pub enum LabelSize {
@@ -99,38 +101,32 @@ impl RenderOnce for HighlightedLabel {
fn render(self, cx: &mut WindowContext) -> Self::Rendered {
let highlight_color = cx.theme().colors().text_accent;
let mut text_style = cx.text_style().clone();
let mut highlight_indices = self.highlight_indices.iter().copied().peekable();
let mut highlights: Vec<(Range<usize>, HighlightStyle)> = Vec::new();
let mut runs: Vec<TextRun> = Vec::new();
while let Some(start_ix) = highlight_indices.next() {
let mut end_ix = start_ix;
for (char_ix, char) in self.label.char_indices() {
let mut color = self.color.color(cx);
if let Some(highlight_ix) = highlight_indices.peek() {
if char_ix == *highlight_ix {
color = highlight_color;
highlight_indices.next();
loop {
end_ix = end_ix + self.label[end_ix..].chars().next().unwrap().len_utf8();
if let Some(&next_ix) = highlight_indices.peek() {
if next_ix == end_ix {
end_ix = next_ix;
highlight_indices.next();
continue;
}
}
break;
}
let last_run = runs.last_mut();
let start_new_run = if let Some(last_run) = last_run {
if color == last_run.color {
last_run.len += char.len_utf8();
false
} else {
true
}
} else {
true
};
if start_new_run {
text_style.color = color;
runs.push(text_style.to_run(char.len_utf8()))
}
highlights.push((
start_ix..end_ix,
HighlightStyle {
color: Some(highlight_color),
..Default::default()
},
));
}
div()
@@ -150,7 +146,7 @@ impl RenderOnce for HighlightedLabel {
LabelSize::Default => this.text_ui(),
LabelSize::Small => this.text_ui_sm(),
})
.child(StyledText::new(self.label).with_runs(runs))
.child(StyledText::new(self.label).with_highlights(&cx.text_style(), highlights))
}
}
+230 -48
View File
@@ -2,14 +2,15 @@ use crate::{
item::{Item, ItemHandle, ItemSettings, WeakItemHandle},
toolbar::Toolbar,
workspace_settings::{AutosaveSetting, WorkspaceSettings},
SplitDirection, Workspace,
NewCenterTerminal, NewFile, NewSearch, SplitDirection, ToggleZoom, Workspace,
};
use anyhow::Result;
use collections::{HashMap, HashSet, VecDeque};
use gpui::{
actions, prelude::*, Action, AppContext, AsyncWindowContext, Div, EntityId, EventEmitter,
FocusHandle, Focusable, FocusableView, Model, Pixels, Point, PromptLevel, Render, Task, View,
ViewContext, VisualContext, WeakView, WindowContext,
actions, overlay, prelude::*, Action, AnchorCorner, AnyWeakView, AppContext,
AsyncWindowContext, DismissEvent, Div, EntityId, EventEmitter, FocusHandle, Focusable,
FocusableView, Model, Pixels, Point, PromptLevel, Render, Task, View, ViewContext,
VisualContext, WeakView, WindowContext,
};
use parking_lot::Mutex;
use project::{Project, ProjectEntryId, ProjectPath};
@@ -25,8 +26,8 @@ use std::{
},
};
use ui::v_stack;
use ui::{prelude::*, Color, Icon, IconButton, IconElement, Tooltip};
use ui::{prelude::*, right_click_menu, Color, Icon, IconButton, IconElement, Tooltip};
use ui::{v_stack, ContextMenu};
use util::truncate_and_remove_front;
#[derive(PartialEq, Clone, Copy, Deserialize, Debug)]
@@ -50,7 +51,7 @@ pub enum SaveIntent {
//todo!("Do we need the default bound on actions? Decide soon")
// #[register_action]
#[derive(Clone, Deserialize, PartialEq, Debug)]
#[derive(Action, Clone, Deserialize, PartialEq, Debug)]
pub struct ActivateItem(pub usize);
// #[derive(Clone, PartialEq)]
@@ -143,17 +144,24 @@ impl fmt::Debug for Event {
}
}
struct FocusedView {
view: AnyWeakView,
focus_handle: FocusHandle,
}
pub struct Pane {
focus_handle: FocusHandle,
items: Vec<Box<dyn ItemHandle>>,
activation_history: Vec<EntityId>,
zoomed: bool,
active_item_index: usize,
// last_focused_view_by_item: HashMap<usize, AnyWeakViewHandle>,
last_focused_view_by_item: HashMap<EntityId, FocusHandle>,
autoscroll: bool,
nav_history: NavHistory,
toolbar: View<Toolbar>,
// tab_bar_context_menu: TabBarContextMenu,
tab_bar_focus_handle: FocusHandle,
new_item_menu: Option<View<ContextMenu>>,
split_item_menu: Option<View<ContextMenu>>,
// tab_context_menu: ViewHandle<ContextMenu>,
workspace: WeakView<Workspace>,
project: Model<Project>,
@@ -306,7 +314,7 @@ impl Pane {
activation_history: Vec::new(),
zoomed: false,
active_item_index: 0,
// last_focused_view_by_item: Default::default(),
last_focused_view_by_item: Default::default(),
autoscroll: false,
nav_history: NavHistory(Arc::new(Mutex::new(NavHistoryState {
mode: NavigationMode::Normal,
@@ -318,6 +326,9 @@ impl Pane {
next_timestamp,
}))),
toolbar: cx.build_view(|_| Toolbar::new()),
tab_bar_focus_handle: cx.focus_handle(),
new_item_menu: None,
split_item_menu: None,
// tab_bar_context_menu: TabBarContextMenu {
// kind: TabBarContextMenuKind::New,
// handle: context_menu,
@@ -392,9 +403,48 @@ impl Pane {
}
pub fn has_focus(&self, cx: &WindowContext) -> bool {
// todo!(); // inline this manually
self.focus_handle.contains_focused(cx)
}
fn focus_in(&mut self, cx: &mut ViewContext<Self>) {
if !self.has_focus(cx) {
cx.emit(Event::Focus);
cx.notify();
}
self.toolbar.update(cx, |toolbar, cx| {
toolbar.focus_changed(true, cx);
});
if let Some(active_item) = self.active_item() {
if self.focus_handle.is_focused(cx) {
// Pane was focused directly. We need to either focus a view inside the active item,
// or focus the active item itself
if let Some(weak_last_focused_view) =
self.last_focused_view_by_item.get(&active_item.item_id())
{
weak_last_focused_view.focus(cx);
return;
}
active_item.focus_handle(cx).focus(cx);
} else if !self.tab_bar_focus_handle.contains_focused(cx) {
if let Some(focused) = cx.focused() {
self.last_focused_view_by_item
.insert(active_item.item_id(), focused);
}
}
}
}
fn focus_out(&mut self, cx: &mut ViewContext<Self>) {
self.toolbar.update(cx, |toolbar, cx| {
toolbar.focus_changed(false, cx);
});
cx.notify();
}
pub fn active_item_index(&self) -> usize {
self.active_item_index
}
@@ -652,21 +702,16 @@ impl Pane {
.position(|i| i.item_id() == item.item_id())
}
// pub fn toggle_zoom(&mut self, _: &ToggleZoom, cx: &mut ViewContext<Self>) {
// // Potentially warn the user of the new keybinding
// let workspace_handle = self.workspace().clone();
// cx.spawn(|_, mut cx| async move { notify_of_new_dock(&workspace_handle, &mut cx) })
// .detach();
// if self.zoomed {
// cx.emit(Event::ZoomOut);
// } else if !self.items.is_empty() {
// if !self.has_focus {
// cx.focus_self();
// }
// cx.emit(Event::ZoomIn);
// }
// }
pub fn toggle_zoom(&mut self, _: &ToggleZoom, cx: &mut ViewContext<Self>) {
if self.zoomed {
cx.emit(Event::ZoomOut);
} else if !self.items.is_empty() {
if !self.focus_handle.contains_focused(cx) {
cx.focus_self();
}
cx.emit(Event::ZoomIn);
}
}
pub fn activate_item(
&mut self,
@@ -1403,7 +1448,7 @@ impl Pane {
let close_right = ItemSettings::get_global(cx).close_position.right();
let is_active = ix == self.active_item_index;
div()
let tab = div()
.group("")
.id(ix)
.cursor_pointer()
@@ -1477,13 +1522,41 @@ impl Pane {
.children((!close_right).then(|| close_icon()))
.child(label)
.children(close_right.then(|| close_icon())),
)
);
right_click_menu(ix).trigger(tab).menu(|cx| {
ContextMenu::build(cx, |menu, cx| {
menu.action(
"Close Active Item",
CloseActiveItem { save_intent: None }.boxed_clone(),
cx,
)
.action("Close Inactive Items", CloseInactiveItems.boxed_clone(), cx)
.action("Close Clean Items", CloseCleanItems.boxed_clone(), cx)
.action(
"Close Items To The Left",
CloseItemsToTheLeft.boxed_clone(),
cx,
)
.action(
"Close Items To The Right",
CloseItemsToTheRight.boxed_clone(),
cx,
)
.action(
"Close All Items",
CloseAllItems { save_intent: None }.boxed_clone(),
cx,
)
})
})
}
fn render_tab_bar(&mut self, cx: &mut ViewContext<'_, Pane>) -> impl IntoElement {
div()
.group("tab_bar")
.id("tab_bar")
.track_focus(&self.tab_bar_focus_handle)
.w_full()
.flex()
.bg(cx.theme().colors().tab_bar_background)
@@ -1550,20 +1623,87 @@ impl Pane {
.gap_px()
.child(
div()
.bg(gpui::blue())
.border()
.border_color(gpui::red())
.child(IconButton::new("plus", Icon::Plus)),
.child(IconButton::new("plus", Icon::Plus).on_click(
cx.listener(|this, _, cx| {
let menu = ContextMenu::build(cx, |menu, cx| {
menu.action("New File", NewFile.boxed_clone(), cx)
.action(
"New Terminal",
NewCenterTerminal.boxed_clone(),
cx,
)
.action(
"New Search",
NewSearch.boxed_clone(),
cx,
)
});
cx.subscribe(
&menu,
|this, _, event: &DismissEvent, cx| {
this.focus(cx);
this.new_item_menu = None;
},
)
.detach();
this.new_item_menu = Some(menu);
}),
))
.when_some(self.new_item_menu.as_ref(), |el, new_item_menu| {
el.child(Self::render_menu_overlay(new_item_menu))
}),
)
.child(
div()
.border()
.border_color(gpui::red())
.child(IconButton::new("split", Icon::Split)),
.child(IconButton::new("split", Icon::Split).on_click(
cx.listener(|this, _, cx| {
let menu = ContextMenu::build(cx, |menu, cx| {
menu.action(
"Split Right",
SplitRight.boxed_clone(),
cx,
)
.action("Split Left", SplitLeft.boxed_clone(), cx)
.action("Split Up", SplitUp.boxed_clone(), cx)
.action("Split Down", SplitDown.boxed_clone(), cx)
});
cx.subscribe(
&menu,
|this, _, event: &DismissEvent, cx| {
this.focus(cx);
this.split_item_menu = None;
},
)
.detach();
this.split_item_menu = Some(menu);
}),
))
.when_some(
self.split_item_menu.as_ref(),
|el, split_item_menu| {
el.child(Self::render_menu_overlay(split_item_menu))
},
),
),
),
)
}
fn render_menu_overlay(menu: &View<ContextMenu>) -> Div {
div()
.absolute()
.z_index(1)
.bottom_0()
.right_0()
.size_0()
.child(overlay().anchor(AnchorCorner::TopRight).child(menu.clone()))
}
// fn render_tabs(&mut self, cx: &mut ViewContext<Self>) -> impl Element<Self> {
// let theme = theme::current(cx).clone();
@@ -1962,9 +2102,23 @@ impl Render for Pane {
type Element = Focusable<Div>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
let this = cx.view().downgrade();
v_stack()
.key_context("Pane")
.track_focus(&self.focus_handle)
.on_focus_in({
let this = this.clone();
move |event, cx| {
this.update(cx, |this, cx| this.focus_in(cx)).ok();
}
})
.on_focus_out({
let this = this.clone();
move |event, cx| {
this.update(cx, |this, cx| this.focus_out(cx)).ok();
}
})
.on_action(cx.listener(|pane, _: &SplitLeft, cx| pane.split(SplitDirection::Left, cx)))
.on_action(cx.listener(|pane, _: &SplitUp, cx| pane.split(SplitDirection::Up, cx)))
.on_action(
@@ -1973,25 +2127,53 @@ impl Render for Pane {
.on_action(cx.listener(|pane, _: &SplitDown, cx| pane.split(SplitDirection::Down, cx)))
.on_action(cx.listener(|pane, _: &GoBack, cx| pane.navigate_backward(cx)))
.on_action(cx.listener(|pane, _: &GoForward, cx| pane.navigate_forward(cx)))
// cx.add_action(Pane::toggle_zoom);
// cx.add_action(|pane: &mut Pane, action: &ActivateItem, cx| {
// pane.activate_item(action.0, true, true, cx);
// });
// cx.add_action(|pane: &mut Pane, _: &ActivateLastItem, cx| {
// pane.activate_item(pane.items.len() - 1, true, true, cx);
// });
// cx.add_action(|pane: &mut Pane, _: &ActivatePrevItem, cx| {
// pane.activate_prev_item(true, cx);
// });
// cx.add_action(|pane: &mut Pane, _: &ActivateNextItem, cx| {
// pane.activate_next_item(true, cx);
// });
// cx.add_async_action(Pane::close_active_item);
// cx.add_async_action(Pane::close_inactive_items);
// cx.add_async_action(Pane::close_clean_items);
// cx.add_async_action(Pane::close_items_to_the_left);
// cx.add_async_action(Pane::close_items_to_the_right);
// cx.add_async_action(Pane::close_all_items);
.on_action(cx.listener(Pane::toggle_zoom))
.on_action(cx.listener(|pane: &mut Pane, action: &ActivateItem, cx| {
pane.activate_item(action.0, true, true, cx);
}))
.on_action(cx.listener(|pane: &mut Pane, _: &ActivateLastItem, cx| {
pane.activate_item(pane.items.len() - 1, true, true, cx);
}))
.on_action(cx.listener(|pane: &mut Pane, _: &ActivatePrevItem, cx| {
pane.activate_prev_item(true, cx);
}))
.on_action(cx.listener(|pane: &mut Pane, _: &ActivateNextItem, cx| {
pane.activate_next_item(true, cx);
}))
.on_action(
cx.listener(|pane: &mut Self, action: &CloseActiveItem, cx| {
pane.close_active_item(action, cx)
.map(|task| task.detach_and_log_err(cx));
}),
)
.on_action(
cx.listener(|pane: &mut Self, action: &CloseInactiveItems, cx| {
pane.close_inactive_items(action, cx)
.map(|task| task.detach_and_log_err(cx));
}),
)
.on_action(
cx.listener(|pane: &mut Self, action: &CloseCleanItems, cx| {
pane.close_clean_items(action, cx)
.map(|task| task.detach_and_log_err(cx));
}),
)
.on_action(
cx.listener(|pane: &mut Self, action: &CloseItemsToTheLeft, cx| {
pane.close_items_to_the_left(action, cx)
.map(|task| task.detach_and_log_err(cx));
}),
)
.on_action(
cx.listener(|pane: &mut Self, action: &CloseItemsToTheRight, cx| {
pane.close_items_to_the_right(action, cx)
.map(|task| task.detach_and_log_err(cx));
}),
)
.on_action(cx.listener(|pane: &mut Self, action: &CloseAllItems, cx| {
pane.close_all_items(action, cx)
.map(|task| task.detach_and_log_err(cx));
}))
.size_full()
.on_action(
cx.listener(|pane: &mut Self, action: &CloseActiveItem, cx| {
+3 -13
View File
@@ -4,7 +4,7 @@ use gpui::{
ViewContext, WindowContext,
};
use ui::prelude::*;
use ui::{h_stack, v_stack, ButtonLike, Color, Icon, IconButton, Label};
use ui::{h_stack, v_stack, Icon, IconButton};
pub enum ToolbarItemEvent {
ChangeLocation(ToolbarItemLocation),
@@ -87,17 +87,8 @@ impl Render for Toolbar {
.child(
h_stack()
.justify_between()
.child(
// Toolbar left side
h_stack().border().border_color(gpui::red()).p_1().child(
ButtonLike::new("breadcrumb")
.child(Label::new("crates/workspace2/src/toolbar.rs"))
.child(Label::new("").color(Color::Muted))
.child(Label::new("impl Render for Toolbar"))
.child(Label::new("").color(Color::Muted))
.child(Label::new("fn render")),
),
)
// Toolbar left side
.children(self.items.iter().map(|(child, _)| child.to_any()))
// Toolbar right side
.child(
h_stack()
@@ -116,7 +107,6 @@ impl Render for Toolbar {
),
),
)
.children(self.items.iter().map(|(child, _)| child.to_any()))
}
}
-81
View File
@@ -3585,87 +3585,6 @@ fn open_items(
})
}
// todo!()
// fn notify_of_new_dock(workspace: &WeakView<Workspace>, cx: &mut AsyncAppContext) {
// const NEW_PANEL_BLOG_POST: &str = "https://zed.dev/blog/new-panel-system";
// const NEW_DOCK_HINT_KEY: &str = "show_new_dock_key";
// const MESSAGE_ID: usize = 2;
// if workspace
// .read_with(cx, |workspace, cx| {
// workspace.has_shown_notification_once::<MessageNotification>(MESSAGE_ID, cx)
// })
// .unwrap_or(false)
// {
// return;
// }
// if db::kvp::KEY_VALUE_STORE
// .read_kvp(NEW_DOCK_HINT_KEY)
// .ok()
// .flatten()
// .is_some()
// {
// if !workspace
// .read_with(cx, |workspace, cx| {
// workspace.has_shown_notification_once::<MessageNotification>(MESSAGE_ID, cx)
// })
// .unwrap_or(false)
// {
// cx.update(|cx| {
// cx.update_global::<NotificationTracker, _, _>(|tracker, _| {
// let entry = tracker
// .entry(TypeId::of::<MessageNotification>())
// .or_default();
// if !entry.contains(&MESSAGE_ID) {
// entry.push(MESSAGE_ID);
// }
// });
// });
// }
// return;
// }
// cx.spawn(|_| async move {
// db::kvp::KEY_VALUE_STORE
// .write_kvp(NEW_DOCK_HINT_KEY.to_string(), "seen".to_string())
// .await
// .ok();
// })
// .detach();
// workspace
// .update(cx, |workspace, cx| {
// workspace.show_notification_once(2, cx, |cx| {
// cx.build_view(|_| {
// MessageNotification::new_element(|text, _| {
// Text::new(
// "Looking for the dock? Try ctrl-`!\nshift-escape now zooms your pane.",
// text,
// )
// .with_custom_runs(vec![26..32, 34..46], |_, bounds, cx| {
// let code_span_background_color = settings::get::<ThemeSettings>(cx)
// .theme
// .editor
// .document_highlight_read_background;
// cx.scene().push_quad(gpui::Quad {
// bounds,
// background: Some(code_span_background_color),
// border: Default::default(),
// corner_radii: (2.0).into(),
// })
// })
// .into_any()
// })
// .with_click_message("Read more about the new panel system")
// .on_click(|cx| cx.platform().open_url(NEW_PANEL_BLOG_POST))
// })
// })
// })
// .ok();
fn notify_if_database_failed(workspace: WindowHandle<Workspace>, cx: &mut AsyncAppContext) {
const REPORT_ISSUE_URL: &str ="https://github.com/zed-industries/community/issues/new?assignees=&labels=defect%2Ctriage&template=2_bug_report.yml";
+1 -1
View File
@@ -19,7 +19,7 @@ ai = { package = "ai2", path = "../ai2"}
audio = { package = "audio2", path = "../audio2" }
activity_indicator = { package = "activity_indicator2", path = "../activity_indicator2"}
auto_update = { package = "auto_update2", path = "../auto_update2" }
# breadcrumbs = { path = "../breadcrumbs" }
breadcrumbs = { package = "breadcrumbs2", path = "../breadcrumbs2" }
call = { package = "call2", path = "../call2" }
channel = { package = "channel2", path = "../channel2" }
cli = { path = "../cli" }
+4 -3
View File
@@ -7,6 +7,7 @@ mod only_instance;
mod open_listener;
pub use assets::*;
use breadcrumbs::Breadcrumbs;
use collections::VecDeque;
use editor::{Editor, MultiBuffer};
use gpui::{
@@ -95,11 +96,11 @@ pub fn initialize_workspace(app_state: Arc<AppState>, cx: &mut AppContext) {
if let workspace::Event::PaneAdded(pane) = event {
pane.update(cx, |pane, cx| {
pane.toolbar().update(cx, |toolbar, cx| {
// todo!()
// let breadcrumbs = cx.add_view(|_| Breadcrumbs::new(workspace));
// toolbar.add_item(breadcrumbs, cx);
let breadcrumbs = cx.build_view(|_| Breadcrumbs::new(workspace));
toolbar.add_item(breadcrumbs, cx);
let buffer_search_bar = cx.build_view(search::BufferSearchBar::new);
toolbar.add_item(buffer_search_bar.clone(), cx);
// todo!()
// let quick_action_bar = cx.add_view(|_| {
// QuickActionBar::new(buffer_search_bar, workspace)
// });
-5618
View File
File diff suppressed because it is too large Load Diff