mirror of
https://github.com/zed-industries/zed.git
synced 2025-02-12 05:27:07 +00:00
Merge remote-tracking branch 'origin/main' into app-menus
This commit is contained in:
commit
d2fe9f8f9b
33 changed files with 1021 additions and 738 deletions
12
Cargo.lock
generated
12
Cargo.lock
generated
|
@ -7071,6 +7071,17 @@ dependencies = [
|
|||
"workspace",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quick_action_bar2"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"editor2",
|
||||
"gpui2",
|
||||
"search2",
|
||||
"ui2",
|
||||
"workspace2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.33"
|
||||
|
@ -11890,6 +11901,7 @@ dependencies = [
|
|||
"postage",
|
||||
"project2",
|
||||
"project_panel2",
|
||||
"quick_action_bar2",
|
||||
"rand 0.8.5",
|
||||
"regex",
|
||||
"rope2",
|
||||
|
|
|
@ -89,6 +89,7 @@ members = [
|
|||
"crates/project_panel",
|
||||
"crates/project_panel2",
|
||||
"crates/project_symbols",
|
||||
"crates/quick_action_bar2",
|
||||
"crates/recent_projects",
|
||||
"crates/rope",
|
||||
"crates/rpc",
|
||||
|
|
|
@ -17,18 +17,8 @@
|
|||
"cmd-enter": "menu::SecondaryConfirm",
|
||||
"escape": "menu::Cancel",
|
||||
"ctrl-c": "menu::Cancel",
|
||||
"cmd-{": "pane::ActivatePrevItem",
|
||||
"cmd-}": "pane::ActivateNextItem",
|
||||
"alt-cmd-left": "pane::ActivatePrevItem",
|
||||
"alt-cmd-right": "pane::ActivateNextItem",
|
||||
"cmd-w": "pane::CloseActiveItem",
|
||||
"alt-cmd-t": "pane::CloseInactiveItems",
|
||||
"ctrl-alt-cmd-w": "workspace::CloseInactiveTabsAndPanes",
|
||||
"cmd-k u": "pane::CloseCleanItems",
|
||||
"cmd-k cmd-w": "pane::CloseAllItems",
|
||||
"cmd-shift-w": "workspace::CloseWindow",
|
||||
"cmd-s": "workspace::Save",
|
||||
"cmd-shift-s": "workspace::SaveAs",
|
||||
"cmd-o": "workspace::Open",
|
||||
"cmd-=": "zed::IncreaseBufferFontSize",
|
||||
"cmd-+": "zed::IncreaseBufferFontSize",
|
||||
"cmd--": "zed::DecreaseBufferFontSize",
|
||||
|
@ -38,15 +28,7 @@
|
|||
"cmd-h": "zed::Hide",
|
||||
"alt-cmd-h": "zed::HideOthers",
|
||||
"cmd-m": "zed::Minimize",
|
||||
"ctrl-cmd-f": "zed::ToggleFullScreen",
|
||||
"cmd-n": "workspace::NewFile",
|
||||
"cmd-shift-n": "workspace::NewWindow",
|
||||
"cmd-o": "workspace::Open",
|
||||
"alt-cmd-o": "projects::OpenRecent",
|
||||
"alt-cmd-b": "branches::OpenRecent",
|
||||
"ctrl-~": "workspace::NewTerminal",
|
||||
"ctrl-`": "terminal_panel::ToggleFocus",
|
||||
"shift-escape": "workspace::ToggleZoom"
|
||||
"ctrl-cmd-f": "zed::ToggleFullScreen"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -284,6 +266,15 @@
|
|||
{
|
||||
"context": "Pane",
|
||||
"bindings": {
|
||||
"cmd-{": "pane::ActivatePrevItem",
|
||||
"cmd-}": "pane::ActivateNextItem",
|
||||
"alt-cmd-left": "pane::ActivatePrevItem",
|
||||
"alt-cmd-right": "pane::ActivateNextItem",
|
||||
"cmd-w": "pane::CloseActiveItem",
|
||||
"alt-cmd-t": "pane::CloseInactiveItems",
|
||||
"ctrl-alt-cmd-w": "workspace::CloseInactiveTabsAndPanes",
|
||||
"cmd-k u": "pane::CloseCleanItems",
|
||||
"cmd-k cmd-w": "pane::CloseAllItems",
|
||||
"cmd-f": "project_search::ToggleFocus",
|
||||
"cmd-g": "search::SelectNextMatch",
|
||||
"cmd-shift-g": "search::SelectPrevMatch",
|
||||
|
@ -389,6 +380,15 @@
|
|||
{
|
||||
"context": "Workspace",
|
||||
"bindings": {
|
||||
"alt-cmd-o": "projects::OpenRecent",
|
||||
"alt-cmd-b": "branches::OpenRecent",
|
||||
"ctrl-~": "workspace::NewTerminal",
|
||||
"cmd-s": "workspace::Save",
|
||||
"cmd-shift-s": "workspace::SaveAs",
|
||||
"cmd-n": "workspace::NewFile",
|
||||
"cmd-shift-n": "workspace::NewWindow",
|
||||
"ctrl-`": "terminal_panel::ToggleFocus",
|
||||
"shift-escape": "workspace::ToggleZoom",
|
||||
"cmd-1": ["workspace::ActivatePane", 0],
|
||||
"cmd-2": ["workspace::ActivatePane", 1],
|
||||
"cmd-3": ["workspace::ActivatePane", 2],
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
use gpui::{
|
||||
Component, Element, EventEmitter, IntoElement, ParentElement, Render, StyledText, Subscription,
|
||||
Div, Element, EventEmitter, IntoElement, ParentElement, Render, StyledText, Subscription,
|
||||
ViewContext, WeakView,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use theme::ActiveTheme;
|
||||
use ui::{ButtonCommon, ButtonLike, ButtonStyle, Clickable, Disableable, Label};
|
||||
use ui::{prelude::*, ButtonLike, ButtonStyle, Label};
|
||||
use workspace::{
|
||||
item::{ItemEvent, ItemHandle},
|
||||
ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView, Workspace,
|
||||
|
@ -36,54 +36,51 @@ impl EventEmitter<Event> for Breadcrumbs {}
|
|||
impl EventEmitter<ToolbarItemEvent> for Breadcrumbs {}
|
||||
|
||||
impl Render for Breadcrumbs {
|
||||
type Element = Component<ButtonLike>;
|
||||
type Element = Div;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
let button = ButtonLike::new("breadcrumbs")
|
||||
.style(ButtonStyle::Transparent)
|
||||
.disabled(true);
|
||||
let element = h_stack().text_ui();
|
||||
|
||||
let active_item = match &self.active_item {
|
||||
Some(active_item) => active_item,
|
||||
None => return button.into_element(),
|
||||
let Some(active_item) = &self
|
||||
.active_item
|
||||
.as_ref()
|
||||
.filter(|item| item.downcast::<editor::Editor>().is_some())
|
||||
else {
|
||||
return 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())
|
||||
let Some(segments) = active_item.breadcrumbs(cx.theme(), cx) else {
|
||||
return element;
|
||||
};
|
||||
|
||||
let highlighted_segments = segments.into_iter().map(|segment| {
|
||||
StyledText::new(segment.text)
|
||||
.with_highlights(&cx.text_style(), segment.highlights.unwrap_or_default())
|
||||
.into_any()
|
||||
});
|
||||
let breadcrumbs = Itertools::intersperse_with(highlighted_segments, || {
|
||||
Label::new("›").into_any_element()
|
||||
});
|
||||
|
||||
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()
|
||||
element.child(
|
||||
ButtonLike::new("toggle outline view")
|
||||
.style(ButtonStyle::Subtle)
|
||||
.child(h_stack().gap_1().children(breadcrumbs))
|
||||
// We disable the button when it is not focused
|
||||
// due to ... @julia what was the reason again?
|
||||
.disabled(!self.pane_focused)
|
||||
.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();
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -169,7 +169,7 @@ use editor::Editor;
|
|||
use feature_flags::{ChannelsAlpha, FeatureFlagAppExt, FeatureFlagViewExt};
|
||||
use fuzzy::{match_strings, StringMatchCandidate};
|
||||
use gpui::{
|
||||
actions, canvas, div, img, overlay, point, prelude::*, px, rems, serde_json, Action,
|
||||
actions, canvas, div, img, overlay, point, prelude::*, px, rems, serde_json, size, Action,
|
||||
AppContext, AsyncWindowContext, Bounds, ClipboardItem, DismissEvent, Div, EventEmitter,
|
||||
FocusHandle, Focusable, FocusableView, Hsla, InteractiveElement, IntoElement, Length, Model,
|
||||
MouseDownEvent, ParentElement, Pixels, Point, PromptLevel, Quad, Render, RenderOnce,
|
||||
|
@ -1204,14 +1204,9 @@ impl CollabPanel {
|
|||
.detach_and_log_err(cx);
|
||||
});
|
||||
}))
|
||||
.left_child(IconButton::new(0, Icon::Folder))
|
||||
.child(
|
||||
h_stack()
|
||||
.w_full()
|
||||
.justify_between()
|
||||
.child(render_tree_branch(is_last, cx))
|
||||
.child(Label::new(project_name.clone())),
|
||||
)
|
||||
.left_child(render_tree_branch(is_last, cx))
|
||||
.child(IconButton::new(0, Icon::Folder))
|
||||
.child(Label::new(project_name.clone()))
|
||||
.tooltip(move |cx| Tooltip::text(format!("Open {}", project_name), cx))
|
||||
|
||||
// enum JoinProject {}
|
||||
|
@ -1299,70 +1294,20 @@ impl CollabPanel {
|
|||
is_last: bool,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> impl IntoElement {
|
||||
// enum OpenSharedScreen {}
|
||||
let id = peer_id.map_or(usize::MAX, |id| id.as_u64() as usize);
|
||||
|
||||
// let host_avatar_width = theme
|
||||
// .contact_avatar
|
||||
// .width
|
||||
// .or(theme.contact_avatar.height)
|
||||
// .unwrap_or(0.);
|
||||
// let tree_branch = theme.tree_branch;
|
||||
|
||||
// let handler = MouseEventHandler::new::<OpenSharedScreen, _>(
|
||||
// peer_id.map(|id| id.as_u64()).unwrap_or(0) as usize,
|
||||
// cx,
|
||||
// |mouse_state, cx| {
|
||||
// let tree_branch = *tree_branch.in_state(is_selected).style_for(mouse_state);
|
||||
// let row = theme
|
||||
// .project_row
|
||||
// .in_state(is_selected)
|
||||
// .style_for(mouse_state);
|
||||
|
||||
// Flex::row()
|
||||
// .with_child(render_tree_branch(
|
||||
// tree_branch,
|
||||
// &row.name.text,
|
||||
// is_last,
|
||||
// vec2f(host_avatar_width, theme.row_height),
|
||||
// cx.font_cache(),
|
||||
// ))
|
||||
// .with_child(
|
||||
// Svg::new("icons/desktop.svg")
|
||||
// .with_color(theme.channel_hash.color)
|
||||
// .constrained()
|
||||
// .with_width(theme.channel_hash.width)
|
||||
// .aligned()
|
||||
// .left(),
|
||||
// )
|
||||
// .with_child(
|
||||
// Label::new("Screen", row.name.text.clone())
|
||||
// .aligned()
|
||||
// .left()
|
||||
// .contained()
|
||||
// .with_style(row.name.container)
|
||||
// .flex(1., false),
|
||||
// )
|
||||
// .constrained()
|
||||
// .with_height(theme.row_height)
|
||||
// .contained()
|
||||
// .with_style(row.container)
|
||||
// },
|
||||
// );
|
||||
// if peer_id.is_none() {
|
||||
// return handler.into_any();
|
||||
// }
|
||||
// handler
|
||||
// .with_cursor_style(CursorStyle::PointingHand)
|
||||
// .on_click(MouseButton::Left, move |_, this, cx| {
|
||||
// if let Some(workspace) = this.workspace.upgrade(cx) {
|
||||
// workspace.update(cx, |workspace, cx| {
|
||||
// workspace.open_shared_screen(peer_id.unwrap(), cx)
|
||||
// });
|
||||
// }
|
||||
// })
|
||||
// .into_any()
|
||||
|
||||
div()
|
||||
ListItem::new(("screen", id))
|
||||
.left_child(render_tree_branch(is_last, cx))
|
||||
.child(IconButton::new(0, Icon::Screen))
|
||||
.child(Label::new("Screen"))
|
||||
.when_some(peer_id, |this, _| {
|
||||
this.on_click(cx.listener(move |this, _, cx| {
|
||||
this.workspace.update(cx, |workspace, cx| {
|
||||
workspace.open_shared_screen(peer_id.unwrap(), cx)
|
||||
});
|
||||
}))
|
||||
.tooltip(move |cx| Tooltip::text(format!("Open shared screen"), cx))
|
||||
})
|
||||
}
|
||||
|
||||
fn take_editing_state(&mut self, cx: &mut ViewContext<Self>) -> bool {
|
||||
|
@ -1415,54 +1360,14 @@ impl CollabPanel {
|
|||
channel_id: ChannelId,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> impl IntoElement {
|
||||
// enum ChannelNotes {}
|
||||
// let host_avatar_width = theme
|
||||
// .contact_avatar
|
||||
// .width
|
||||
// .or(theme.contact_avatar.height)
|
||||
// .unwrap_or(0.);
|
||||
|
||||
// MouseEventHandler::new::<ChannelNotes, _>(ix as usize, cx, |state, cx| {
|
||||
// let tree_branch = *theme.tree_branch.in_state(is_selected).style_for(state);
|
||||
// let row = theme.project_row.in_state(is_selected).style_for(state);
|
||||
|
||||
// Flex::<Self>::row()
|
||||
// .with_child(render_tree_branch(
|
||||
// tree_branch,
|
||||
// &row.name.text,
|
||||
// false,
|
||||
// vec2f(host_avatar_width, theme.row_height),
|
||||
// cx.font_cache(),
|
||||
// ))
|
||||
// .with_child(
|
||||
// Svg::new("icons/file.svg")
|
||||
// .with_color(theme.channel_hash.color)
|
||||
// .constrained()
|
||||
// .with_width(theme.channel_hash.width)
|
||||
// .aligned()
|
||||
// .left(),
|
||||
// )
|
||||
// .with_child(
|
||||
// Label::new("notes", theme.channel_name.text.clone())
|
||||
// .contained()
|
||||
// .with_style(theme.channel_name.container)
|
||||
// .aligned()
|
||||
// .left()
|
||||
// .flex(1., true),
|
||||
// )
|
||||
// .constrained()
|
||||
// .with_height(theme.row_height)
|
||||
// .contained()
|
||||
// .with_style(*theme.channel_row.style_for(is_selected, state))
|
||||
// .with_padding_left(theme.channel_row.default_style().padding.left)
|
||||
// })
|
||||
// .on_click(MouseButton::Left, move |_, this, cx| {
|
||||
// this.open_channel_notes(&OpenChannelNotes { channel_id }, cx);
|
||||
// })
|
||||
// .with_cursor_style(CursorStyle::PointingHand)
|
||||
// .into_any()
|
||||
|
||||
div()
|
||||
ListItem::new("channel-notes")
|
||||
.on_click(cx.listener(move |this, _, cx| {
|
||||
this.open_channel_notes(channel_id, cx);
|
||||
}))
|
||||
.left_child(render_tree_branch(false, cx))
|
||||
.child(IconButton::new(0, Icon::File))
|
||||
.child(Label::new("notes"))
|
||||
.tooltip(move |cx| Tooltip::text("Open Channel Notes", cx))
|
||||
}
|
||||
|
||||
fn render_channel_chat(
|
||||
|
@ -1470,53 +1375,14 @@ impl CollabPanel {
|
|||
channel_id: ChannelId,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> impl IntoElement {
|
||||
// enum ChannelChat {}
|
||||
// let host_avatar_width = theme
|
||||
// .contact_avatar
|
||||
// .width
|
||||
// .or(theme.contact_avatar.height)
|
||||
// .unwrap_or(0.);
|
||||
|
||||
// MouseEventHandler::new::<ChannelChat, _>(ix as usize, cx, |state, cx| {
|
||||
// let tree_branch = *theme.tree_branch.in_state(is_selected).style_for(state);
|
||||
// let row = theme.project_row.in_state(is_selected).style_for(state);
|
||||
|
||||
// Flex::<Self>::row()
|
||||
// .with_child(render_tree_branch(
|
||||
// tree_branch,
|
||||
// &row.name.text,
|
||||
// true,
|
||||
// vec2f(host_avatar_width, theme.row_height),
|
||||
// cx.font_cache(),
|
||||
// ))
|
||||
// .with_child(
|
||||
// Svg::new("icons/conversations.svg")
|
||||
// .with_color(theme.channel_hash.color)
|
||||
// .constrained()
|
||||
// .with_width(theme.channel_hash.width)
|
||||
// .aligned()
|
||||
// .left(),
|
||||
// )
|
||||
// .with_child(
|
||||
// Label::new("chat", theme.channel_name.text.clone())
|
||||
// .contained()
|
||||
// .with_style(theme.channel_name.container)
|
||||
// .aligned()
|
||||
// .left()
|
||||
// .flex(1., true),
|
||||
// )
|
||||
// .constrained()
|
||||
// .with_height(theme.row_height)
|
||||
// .contained()
|
||||
// .with_style(*theme.channel_row.style_for(is_selected, state))
|
||||
// .with_padding_left(theme.channel_row.default_style().padding.left)
|
||||
// })
|
||||
// .on_click(MouseButton::Left, move |_, this, cx| {
|
||||
// this.join_channel_chat(&JoinChannelChat { channel_id }, cx);
|
||||
// })
|
||||
// .with_cursor_style(CursorStyle::PointingHand)
|
||||
// .into_any()
|
||||
div()
|
||||
ListItem::new("channel-chat")
|
||||
.on_click(cx.listener(move |this, _, cx| {
|
||||
this.join_channel_chat(channel_id, cx);
|
||||
}))
|
||||
.left_child(render_tree_branch(true, cx))
|
||||
.child(IconButton::new(0, Icon::MessageBubbles))
|
||||
.child(Label::new("chat"))
|
||||
.tooltip(move |cx| Tooltip::text("Open Chat", cx))
|
||||
}
|
||||
|
||||
// fn render_channel_invite(
|
||||
|
@ -3119,30 +2985,24 @@ impl CollabPanel {
|
|||
}
|
||||
|
||||
fn render_tree_branch(is_last: bool, cx: &mut WindowContext) -> impl IntoElement {
|
||||
let text_style = cx.text_style();
|
||||
let rem_size = cx.rem_size();
|
||||
let text_system = cx.text_system();
|
||||
let font_id = text_system.font_id(&text_style.font()).unwrap();
|
||||
let font_size = text_style.font_size.to_pixels(rem_size);
|
||||
let line_height = text_style.line_height_in_pixels(rem_size);
|
||||
let cap_height = text_system.cap_height(font_id, font_size);
|
||||
let baseline_offset = text_system.baseline_offset(font_id, font_size, line_height);
|
||||
let width = cx.rem_size() * 2.5;
|
||||
let line_height = cx.text_style().line_height_in_pixels(rem_size);
|
||||
let width = rem_size * 1.5;
|
||||
let thickness = px(2.);
|
||||
let color = cx.theme().colors().text;
|
||||
|
||||
canvas(move |bounds, cx| {
|
||||
let start_x = bounds.left() + (bounds.size.width / 2.) - (width / 2.);
|
||||
let end_x = bounds.right();
|
||||
let start_y = bounds.top();
|
||||
let end_y = bounds.top() + baseline_offset - (cap_height / 2.);
|
||||
let start_x = (bounds.left() + bounds.right() - thickness) / 2.;
|
||||
let start_y = (bounds.top() + bounds.bottom() - thickness) / 2.;
|
||||
let right = bounds.right();
|
||||
let top = bounds.top();
|
||||
|
||||
cx.paint_quad(
|
||||
Bounds::from_corners(
|
||||
point(start_x, start_y),
|
||||
point(start_x, top),
|
||||
point(
|
||||
start_x + thickness,
|
||||
if is_last { end_y } else { bounds.bottom() },
|
||||
if is_last { start_y } else { bounds.bottom() },
|
||||
),
|
||||
),
|
||||
Default::default(),
|
||||
|
@ -3151,7 +3011,7 @@ fn render_tree_branch(is_last: bool, cx: &mut WindowContext) -> impl IntoElement
|
|||
Hsla::transparent_black(),
|
||||
);
|
||||
cx.paint_quad(
|
||||
Bounds::from_corners(point(start_x, end_y), point(end_x, end_y + thickness)),
|
||||
Bounds::from_corners(point(start_x, start_y), point(right, start_y + thickness)),
|
||||
Default::default(),
|
||||
color,
|
||||
Default::default(),
|
||||
|
|
|
@ -88,7 +88,7 @@ struct DiagnosticGroupState {
|
|||
block_count: usize,
|
||||
}
|
||||
|
||||
impl EventEmitter<ItemEvent> for ProjectDiagnosticsEditor {}
|
||||
impl EventEmitter<EditorEvent> for ProjectDiagnosticsEditor {}
|
||||
|
||||
impl Render for ProjectDiagnosticsEditor {
|
||||
type Element = Focusable<Div>;
|
||||
|
@ -158,7 +158,7 @@ impl ProjectDiagnosticsEditor {
|
|||
});
|
||||
let editor_event_subscription =
|
||||
cx.subscribe(&editor, |this, _editor, event: &EditorEvent, cx| {
|
||||
Self::emit_item_event_for_editor_event(event, cx);
|
||||
cx.emit(event.clone());
|
||||
if event == &EditorEvent::Focused && this.path_states.is_empty() {
|
||||
cx.focus(&this.focus_handle);
|
||||
}
|
||||
|
@ -183,40 +183,6 @@ impl ProjectDiagnosticsEditor {
|
|||
this
|
||||
}
|
||||
|
||||
fn emit_item_event_for_editor_event(event: &EditorEvent, cx: &mut ViewContext<Self>) {
|
||||
match event {
|
||||
EditorEvent::Closed => cx.emit(ItemEvent::CloseItem),
|
||||
|
||||
EditorEvent::Saved | EditorEvent::TitleChanged => {
|
||||
cx.emit(ItemEvent::UpdateTab);
|
||||
cx.emit(ItemEvent::UpdateBreadcrumbs);
|
||||
}
|
||||
|
||||
EditorEvent::Reparsed => {
|
||||
cx.emit(ItemEvent::UpdateBreadcrumbs);
|
||||
}
|
||||
|
||||
EditorEvent::SelectionsChanged { local } if *local => {
|
||||
cx.emit(ItemEvent::UpdateBreadcrumbs);
|
||||
}
|
||||
|
||||
EditorEvent::DirtyChanged => {
|
||||
cx.emit(ItemEvent::UpdateTab);
|
||||
}
|
||||
|
||||
EditorEvent::BufferEdited => {
|
||||
cx.emit(ItemEvent::Edit);
|
||||
cx.emit(ItemEvent::UpdateBreadcrumbs);
|
||||
}
|
||||
|
||||
EditorEvent::ExcerptsAdded { .. } | EditorEvent::ExcerptsRemoved { .. } => {
|
||||
cx.emit(ItemEvent::Edit);
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn deploy(workspace: &mut Workspace, _: &Deploy, cx: &mut ViewContext<Workspace>) {
|
||||
if let Some(existing) = workspace.item_of_type::<ProjectDiagnosticsEditor>(cx) {
|
||||
workspace.activate_item(&existing, cx);
|
||||
|
@ -333,8 +299,7 @@ impl ProjectDiagnosticsEditor {
|
|||
|
||||
this.update(&mut cx, |this, cx| {
|
||||
this.summary = this.project.read(cx).diagnostic_summary(false, cx);
|
||||
cx.emit(ItemEvent::UpdateTab);
|
||||
cx.emit(ItemEvent::UpdateBreadcrumbs);
|
||||
cx.emit(EditorEvent::TitleChanged);
|
||||
})?;
|
||||
anyhow::Ok(())
|
||||
}
|
||||
|
@ -649,6 +614,12 @@ impl FocusableView for ProjectDiagnosticsEditor {
|
|||
}
|
||||
|
||||
impl Item for ProjectDiagnosticsEditor {
|
||||
type Event = EditorEvent;
|
||||
|
||||
fn to_item_events(event: &EditorEvent, f: impl FnMut(ItemEvent)) {
|
||||
Editor::to_item_events(event, f)
|
||||
}
|
||||
|
||||
fn deactivated(&mut self, cx: &mut ViewContext<Self>) {
|
||||
self.editor.update(cx, |editor, cx| editor.deactivated(cx));
|
||||
}
|
||||
|
|
|
@ -1675,8 +1675,7 @@ impl Editor {
|
|||
if let Some(project) = project.as_ref() {
|
||||
if buffer.read(cx).is_singleton() {
|
||||
project_subscriptions.push(cx.observe(project, |_, _, cx| {
|
||||
cx.emit(ItemEvent::UpdateTab);
|
||||
cx.emit(ItemEvent::UpdateBreadcrumbs);
|
||||
cx.emit(EditorEvent::TitleChanged);
|
||||
}));
|
||||
}
|
||||
project_subscriptions.push(cx.subscribe(project, |editor, _, event, cx| {
|
||||
|
@ -2141,10 +2140,6 @@ impl Editor {
|
|||
cx.emit(SearchEvent::ActiveMatchChanged)
|
||||
}
|
||||
|
||||
if local {
|
||||
cx.emit(ItemEvent::UpdateBreadcrumbs);
|
||||
}
|
||||
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
|
@ -8573,8 +8568,6 @@ impl Editor {
|
|||
self.update_visible_copilot_suggestion(cx);
|
||||
}
|
||||
cx.emit(EditorEvent::BufferEdited);
|
||||
cx.emit(ItemEvent::Edit);
|
||||
cx.emit(ItemEvent::UpdateBreadcrumbs);
|
||||
cx.emit(SearchEvent::MatchesInvalidated);
|
||||
|
||||
if *sigleton_buffer_edited {
|
||||
|
@ -8622,20 +8615,14 @@ impl Editor {
|
|||
self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
|
||||
cx.emit(EditorEvent::ExcerptsRemoved { ids: ids.clone() })
|
||||
}
|
||||
multi_buffer::Event::Reparsed => {
|
||||
cx.emit(ItemEvent::UpdateBreadcrumbs);
|
||||
}
|
||||
multi_buffer::Event::DirtyChanged => {
|
||||
cx.emit(ItemEvent::UpdateTab);
|
||||
}
|
||||
multi_buffer::Event::Saved
|
||||
| multi_buffer::Event::FileHandleChanged
|
||||
| multi_buffer::Event::Reloaded => {
|
||||
cx.emit(ItemEvent::UpdateTab);
|
||||
cx.emit(ItemEvent::UpdateBreadcrumbs);
|
||||
multi_buffer::Event::Reparsed => cx.emit(EditorEvent::Reparsed),
|
||||
multi_buffer::Event::DirtyChanged => cx.emit(EditorEvent::DirtyChanged),
|
||||
multi_buffer::Event::Saved => cx.emit(EditorEvent::Saved),
|
||||
multi_buffer::Event::FileHandleChanged | multi_buffer::Event::Reloaded => {
|
||||
cx.emit(EditorEvent::TitleChanged)
|
||||
}
|
||||
multi_buffer::Event::DiffBaseChanged => cx.emit(EditorEvent::DiffBaseChanged),
|
||||
multi_buffer::Event::Closed => cx.emit(ItemEvent::CloseItem),
|
||||
multi_buffer::Event::Closed => cx.emit(EditorEvent::Closed),
|
||||
multi_buffer::Event::DiagnosticsUpdated => {
|
||||
self.refresh_active_diagnostics(cx);
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ use util::{
|
|||
test::{marked_text_ranges, marked_text_ranges_by, sample_text, TextRangeMarker},
|
||||
};
|
||||
use workspace::{
|
||||
item::{FollowEvent, FollowableEvents, FollowableItem, Item, ItemHandle},
|
||||
item::{FollowEvent, FollowableItem, Item, ItemHandle},
|
||||
NavigationEntry, ViewId,
|
||||
};
|
||||
|
||||
|
@ -6476,7 +6476,7 @@ async fn test_following(cx: &mut gpui::TestAppContext) {
|
|||
cx.subscribe(
|
||||
&follower.root_view(cx).unwrap(),
|
||||
move |_, _, event: &EditorEvent, cx| {
|
||||
if matches!(event.to_follow_event(), Some(FollowEvent::Unfollow)) {
|
||||
if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
|
||||
*is_still_following.borrow_mut() = false;
|
||||
}
|
||||
|
||||
|
|
|
@ -32,10 +32,10 @@ use std::{
|
|||
};
|
||||
use text::Selection;
|
||||
use theme::{ActiveTheme, Theme};
|
||||
use ui::{Color, Label};
|
||||
use ui::{h_stack, Color, Label};
|
||||
use util::{paths::PathExt, paths::FILE_ROW_COLUMN_DELIMITER, ResultExt, TryFutureExt};
|
||||
use workspace::{
|
||||
item::{BreadcrumbText, FollowEvent, FollowableEvents, FollowableItemHandle},
|
||||
item::{BreadcrumbText, FollowEvent, FollowableItemHandle},
|
||||
StatusItemView,
|
||||
};
|
||||
use workspace::{
|
||||
|
@ -46,27 +46,7 @@ use workspace::{
|
|||
|
||||
pub const MAX_TAB_TITLE_LEN: usize = 24;
|
||||
|
||||
impl FollowableEvents for EditorEvent {
|
||||
fn to_follow_event(&self) -> Option<workspace::item::FollowEvent> {
|
||||
match self {
|
||||
EditorEvent::Edited => Some(FollowEvent::Unfollow),
|
||||
EditorEvent::SelectionsChanged { local }
|
||||
| EditorEvent::ScrollPositionChanged { local, .. } => {
|
||||
if *local {
|
||||
Some(FollowEvent::Unfollow)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl EventEmitter<ItemEvent> for Editor {}
|
||||
|
||||
impl FollowableItem for Editor {
|
||||
type FollowableEvent = EditorEvent;
|
||||
fn remote_id(&self) -> Option<ViewId> {
|
||||
self.remote_id
|
||||
}
|
||||
|
@ -241,9 +221,24 @@ impl FollowableItem for Editor {
|
|||
}))
|
||||
}
|
||||
|
||||
fn to_follow_event(event: &EditorEvent) -> Option<workspace::item::FollowEvent> {
|
||||
match event {
|
||||
EditorEvent::Edited => Some(FollowEvent::Unfollow),
|
||||
EditorEvent::SelectionsChanged { local }
|
||||
| EditorEvent::ScrollPositionChanged { local, .. } => {
|
||||
if *local {
|
||||
Some(FollowEvent::Unfollow)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn add_event_to_update_proto(
|
||||
&self,
|
||||
event: &Self::FollowableEvent,
|
||||
event: &EditorEvent,
|
||||
update: &mut Option<proto::update_view::Variant>,
|
||||
cx: &WindowContext,
|
||||
) -> bool {
|
||||
|
@ -528,6 +523,8 @@ fn deserialize_anchor(buffer: &MultiBufferSnapshot, anchor: proto::EditorAnchor)
|
|||
}
|
||||
|
||||
impl Item for Editor {
|
||||
type Event = EditorEvent;
|
||||
|
||||
fn navigate(&mut self, data: Box<dyn std::any::Any>, cx: &mut ViewContext<Self>) -> bool {
|
||||
if let Ok(data) = data.downcast::<NavigationData>() {
|
||||
let newest_selection = self.selections.newest::<Point>(cx);
|
||||
|
@ -586,28 +583,25 @@ impl Item for Editor {
|
|||
fn tab_content(&self, detail: Option<usize>, cx: &WindowContext) -> AnyElement {
|
||||
let theme = cx.theme();
|
||||
|
||||
AnyElement::new(
|
||||
div()
|
||||
.flex()
|
||||
.flex_row()
|
||||
.items_center()
|
||||
.gap_2()
|
||||
.child(Label::new(self.title(cx).to_string()))
|
||||
.children(detail.and_then(|detail| {
|
||||
let path = path_for_buffer(&self.buffer, detail, false, cx)?;
|
||||
let description = path.to_string_lossy();
|
||||
let description = detail.and_then(|detail| {
|
||||
let path = path_for_buffer(&self.buffer, detail, false, cx)?;
|
||||
let description = path.to_string_lossy();
|
||||
let description = description.trim();
|
||||
|
||||
Some(
|
||||
div().child(
|
||||
Label::new(util::truncate_and_trailoff(
|
||||
&description,
|
||||
MAX_TAB_TITLE_LEN,
|
||||
))
|
||||
.color(Color::Muted),
|
||||
),
|
||||
)
|
||||
})),
|
||||
)
|
||||
if description.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(util::truncate_and_trailoff(&description, MAX_TAB_TITLE_LEN))
|
||||
});
|
||||
|
||||
h_stack()
|
||||
.gap_2()
|
||||
.child(Label::new(self.title(cx).to_string()))
|
||||
.when_some(description, |this, description| {
|
||||
this.child(Label::new(description).color(Color::Muted))
|
||||
})
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
fn for_each_project_item(
|
||||
|
@ -841,6 +835,40 @@ impl Item for Editor {
|
|||
Some("Editor")
|
||||
}
|
||||
|
||||
fn to_item_events(event: &EditorEvent, mut f: impl FnMut(ItemEvent)) {
|
||||
match event {
|
||||
EditorEvent::Closed => f(ItemEvent::CloseItem),
|
||||
|
||||
EditorEvent::Saved | EditorEvent::TitleChanged => {
|
||||
f(ItemEvent::UpdateTab);
|
||||
f(ItemEvent::UpdateBreadcrumbs);
|
||||
}
|
||||
|
||||
EditorEvent::Reparsed => {
|
||||
f(ItemEvent::UpdateBreadcrumbs);
|
||||
}
|
||||
|
||||
EditorEvent::SelectionsChanged { local } if *local => {
|
||||
f(ItemEvent::UpdateBreadcrumbs);
|
||||
}
|
||||
|
||||
EditorEvent::DirtyChanged => {
|
||||
f(ItemEvent::UpdateTab);
|
||||
}
|
||||
|
||||
EditorEvent::BufferEdited => {
|
||||
f(ItemEvent::Edit);
|
||||
f(ItemEvent::UpdateBreadcrumbs);
|
||||
}
|
||||
|
||||
EditorEvent::ExcerptsAdded { .. } | EditorEvent::ExcerptsRemoved { .. } => {
|
||||
f(ItemEvent::Edit);
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize(
|
||||
project: Model<Project>,
|
||||
_workspace: WeakView<Workspace>,
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
use crate::{Bounds, Element, IntoElement, Pixels, StyleRefinement, Styled, WindowContext};
|
||||
use refineable::Refineable as _;
|
||||
|
||||
use crate::{Bounds, Element, IntoElement, Pixels, Style, StyleRefinement, Styled, WindowContext};
|
||||
|
||||
pub fn canvas(callback: impl 'static + FnOnce(Bounds<Pixels>, &mut WindowContext)) -> Canvas {
|
||||
Canvas {
|
||||
paint_callback: Box::new(callback),
|
||||
style: Default::default(),
|
||||
style: StyleRefinement::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -32,7 +34,9 @@ impl Element for Canvas {
|
|||
_: Option<Self::State>,
|
||||
cx: &mut WindowContext,
|
||||
) -> (crate::LayoutId, Self::State) {
|
||||
let layout_id = cx.request_layout(&self.style.clone().into(), []);
|
||||
let mut style = Style::default();
|
||||
style.refine(&self.style);
|
||||
let layout_id = cx.request_layout(&style, []);
|
||||
(layout_id, ())
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ use std::{
|
|||
use crate::DisplayId;
|
||||
use collections::HashMap;
|
||||
use parking_lot::Mutex;
|
||||
pub use sys::CVSMPTETime as SmtpeTime;
|
||||
pub use sys::CVTimeStamp as VideoTimestamp;
|
||||
|
||||
pub(crate) struct MacDisplayLinker {
|
||||
|
@ -153,7 +154,7 @@ mod sys {
|
|||
kCVTimeStampTopField | kCVTimeStampBottomField;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy)]
|
||||
#[derive(Clone, Copy, Default)]
|
||||
pub struct CVSMPTETime {
|
||||
pub subframes: i16,
|
||||
pub subframe_divisor: i16,
|
||||
|
|
|
@ -148,18 +148,25 @@ impl Platform for TestPlatform {
|
|||
fn set_display_link_output_callback(
|
||||
&self,
|
||||
_display_id: DisplayId,
|
||||
_callback: Box<dyn FnMut(&crate::VideoTimestamp, &crate::VideoTimestamp) + Send>,
|
||||
mut callback: Box<dyn FnMut(&crate::VideoTimestamp, &crate::VideoTimestamp) + Send>,
|
||||
) {
|
||||
unimplemented!()
|
||||
let timestamp = crate::VideoTimestamp {
|
||||
version: 0,
|
||||
video_time_scale: 0,
|
||||
video_time: 0,
|
||||
host_time: 0,
|
||||
rate_scalar: 0.0,
|
||||
video_refresh_period: 0,
|
||||
smpte_time: crate::SmtpeTime::default(),
|
||||
flags: 0,
|
||||
reserved: 0,
|
||||
};
|
||||
callback(×tamp, ×tamp)
|
||||
}
|
||||
|
||||
fn start_display_link(&self, _display_id: DisplayId) {
|
||||
unimplemented!()
|
||||
}
|
||||
fn start_display_link(&self, _display_id: DisplayId) {}
|
||||
|
||||
fn stop_display_link(&self, _display_id: DisplayId) {
|
||||
unimplemented!()
|
||||
}
|
||||
fn stop_display_link(&self, _display_id: DisplayId) {}
|
||||
|
||||
fn open_url(&self, _url: &str) {
|
||||
unimplemented!()
|
||||
|
|
|
@ -2816,3 +2816,9 @@ impl From<(&'static str, EntityId)> for ElementId {
|
|||
ElementId::NamedInteger(name.into(), id.as_u64() as usize)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(&'static str, usize)> for ElementId {
|
||||
fn from((name, id): (&'static str, usize)) -> Self {
|
||||
ElementId::NamedInteger(name.into(), id)
|
||||
}
|
||||
}
|
||||
|
|
22
crates/quick_action_bar2/Cargo.toml
Normal file
22
crates/quick_action_bar2/Cargo.toml
Normal file
|
@ -0,0 +1,22 @@
|
|||
[package]
|
||||
name = "quick_action_bar2"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
||||
[lib]
|
||||
path = "src/quick_action_bar.rs"
|
||||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
#assistant = { path = "../assistant" }
|
||||
editor = { package = "editor2", path = "../editor2" }
|
||||
gpui = { package = "gpui2", path = "../gpui2" }
|
||||
search = { package = "search2", path = "../search2" }
|
||||
workspace = { package = "workspace2", path = "../workspace2" }
|
||||
ui = { package = "ui2", path = "../ui2" }
|
||||
|
||||
[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"] }
|
288
crates/quick_action_bar2/src/quick_action_bar.rs
Normal file
288
crates/quick_action_bar2/src/quick_action_bar.rs
Normal file
|
@ -0,0 +1,288 @@
|
|||
// use assistant::{assistant_panel::InlineAssist, AssistantPanel};
|
||||
use editor::Editor;
|
||||
|
||||
use gpui::{
|
||||
Action, Div, ElementId, EventEmitter, InteractiveElement, ParentElement, Render, Stateful,
|
||||
Styled, Subscription, View, ViewContext, WeakView,
|
||||
};
|
||||
use search::BufferSearchBar;
|
||||
use ui::{prelude::*, ButtonSize, ButtonStyle, Icon, IconButton, IconSize, Tooltip};
|
||||
use workspace::{
|
||||
item::ItemHandle, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView, Workspace,
|
||||
};
|
||||
|
||||
pub struct QuickActionBar {
|
||||
buffer_search_bar: View<BufferSearchBar>,
|
||||
active_item: Option<Box<dyn ItemHandle>>,
|
||||
_inlay_hints_enabled_subscription: Option<Subscription>,
|
||||
#[allow(unused)]
|
||||
workspace: WeakView<Workspace>,
|
||||
}
|
||||
|
||||
impl QuickActionBar {
|
||||
pub fn new(buffer_search_bar: View<BufferSearchBar>, workspace: &Workspace) -> Self {
|
||||
Self {
|
||||
buffer_search_bar,
|
||||
active_item: None,
|
||||
_inlay_hints_enabled_subscription: None,
|
||||
workspace: workspace.weak_handle(),
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn active_editor(&self) -> Option<View<Editor>> {
|
||||
self.active_item
|
||||
.as_ref()
|
||||
.and_then(|item| item.downcast::<Editor>())
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for QuickActionBar {
|
||||
type Element = Stateful<Div>;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
let search_button = QuickActionBarButton::new(
|
||||
"toggle buffer search",
|
||||
Icon::MagnifyingGlass,
|
||||
!self.buffer_search_bar.read(cx).is_dismissed(),
|
||||
Box::new(search::buffer_search::Deploy { focus: false }),
|
||||
"Buffer Search",
|
||||
);
|
||||
let assistant_button = QuickActionBarButton::new(
|
||||
"toggle inline assitant",
|
||||
Icon::MagicWand,
|
||||
false,
|
||||
Box::new(gpui::NoAction),
|
||||
"Inline assistant",
|
||||
);
|
||||
h_stack()
|
||||
.id("quick action bar")
|
||||
.p_1()
|
||||
.gap_2()
|
||||
.child(search_button)
|
||||
.child(
|
||||
div()
|
||||
.border()
|
||||
.border_color(gpui::red())
|
||||
.child(assistant_button),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl EventEmitter<ToolbarItemEvent> for QuickActionBar {}
|
||||
|
||||
// impl View for QuickActionBar {
|
||||
// fn ui_name() -> &'static str {
|
||||
// "QuickActionsBar"
|
||||
// }
|
||||
|
||||
// fn render(&mut self, cx: &mut gpui::ViewContext<'_, '_, Self>) -> gpui::AnyElement<Self> {
|
||||
// let Some(editor) = self.active_editor() else {
|
||||
// return div();
|
||||
// };
|
||||
|
||||
// let mut bar = Flex::row();
|
||||
// if editor.read(cx).supports_inlay_hints(cx) {
|
||||
// bar = bar.with_child(render_quick_action_bar_button(
|
||||
// 0,
|
||||
// "icons/inlay_hint.svg",
|
||||
// editor.read(cx).inlay_hints_enabled(),
|
||||
// (
|
||||
// "Toggle Inlay Hints".to_string(),
|
||||
// Some(Box::new(editor::ToggleInlayHints)),
|
||||
// ),
|
||||
// cx,
|
||||
// |this, cx| {
|
||||
// if let Some(editor) = this.active_editor() {
|
||||
// editor.update(cx, |editor, cx| {
|
||||
// editor.toggle_inlay_hints(&editor::ToggleInlayHints, cx);
|
||||
// });
|
||||
// }
|
||||
// },
|
||||
// ));
|
||||
// }
|
||||
|
||||
// if editor.read(cx).buffer().read(cx).is_singleton() {
|
||||
// let search_bar_shown = !self.buffer_search_bar.read(cx).is_dismissed();
|
||||
// let search_action = buffer_search::Deploy { focus: true };
|
||||
|
||||
// bar = bar.with_child(render_quick_action_bar_button(
|
||||
// 1,
|
||||
// "icons/magnifying_glass.svg",
|
||||
// search_bar_shown,
|
||||
// (
|
||||
// "Buffer Search".to_string(),
|
||||
// Some(Box::new(search_action.clone())),
|
||||
// ),
|
||||
// cx,
|
||||
// move |this, cx| {
|
||||
// this.buffer_search_bar.update(cx, |buffer_search_bar, cx| {
|
||||
// if search_bar_shown {
|
||||
// buffer_search_bar.dismiss(&buffer_search::Dismiss, cx);
|
||||
// } else {
|
||||
// buffer_search_bar.deploy(&search_action, cx);
|
||||
// }
|
||||
// });
|
||||
// },
|
||||
// ));
|
||||
// }
|
||||
|
||||
// bar.add_child(render_quick_action_bar_button(
|
||||
// 2,
|
||||
// "icons/magic-wand.svg",
|
||||
// false,
|
||||
// ("Inline Assist".into(), Some(Box::new(InlineAssist))),
|
||||
// cx,
|
||||
// move |this, cx| {
|
||||
// if let Some(workspace) = this.workspace.upgrade(cx) {
|
||||
// workspace.update(cx, |workspace, cx| {
|
||||
// AssistantPanel::inline_assist(workspace, &Default::default(), cx);
|
||||
// });
|
||||
// }
|
||||
// },
|
||||
// ));
|
||||
|
||||
// bar.into_any()
|
||||
// }
|
||||
// }
|
||||
|
||||
#[derive(IntoElement)]
|
||||
struct QuickActionBarButton {
|
||||
id: ElementId,
|
||||
icon: Icon,
|
||||
toggled: bool,
|
||||
action: Box<dyn Action>,
|
||||
tooltip: SharedString,
|
||||
tooltip_meta: Option<SharedString>,
|
||||
}
|
||||
|
||||
impl QuickActionBarButton {
|
||||
fn new(
|
||||
id: impl Into<ElementId>,
|
||||
icon: Icon,
|
||||
toggled: bool,
|
||||
action: Box<dyn Action>,
|
||||
tooltip: impl Into<SharedString>,
|
||||
) -> Self {
|
||||
Self {
|
||||
id: id.into(),
|
||||
icon,
|
||||
toggled,
|
||||
action,
|
||||
tooltip: tooltip.into(),
|
||||
tooltip_meta: None,
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn meta(mut self, meta: Option<impl Into<SharedString>>) -> Self {
|
||||
self.tooltip_meta = meta.map(|meta| meta.into());
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for QuickActionBarButton {
|
||||
type Rendered = IconButton;
|
||||
|
||||
fn render(self, _: &mut WindowContext) -> Self::Rendered {
|
||||
let tooltip = self.tooltip.clone();
|
||||
let action = self.action.boxed_clone();
|
||||
let tooltip_meta = self.tooltip_meta.clone();
|
||||
|
||||
IconButton::new(self.id.clone(), self.icon)
|
||||
.size(ButtonSize::Compact)
|
||||
.icon_size(IconSize::Small)
|
||||
.style(ButtonStyle::Subtle)
|
||||
.selected(self.toggled)
|
||||
.tooltip(move |cx| {
|
||||
if let Some(meta) = &tooltip_meta {
|
||||
Tooltip::with_meta(tooltip.clone(), Some(&*action), meta.clone(), cx)
|
||||
} else {
|
||||
Tooltip::for_action(tooltip.clone(), &*action, cx)
|
||||
}
|
||||
})
|
||||
.on_click({
|
||||
let action = self.action.boxed_clone();
|
||||
move |_, cx| cx.dispatch_action(action.boxed_clone())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// fn render_quick_action_bar_button<
|
||||
// F: 'static + Fn(&mut QuickActionBar, &mut ViewContext<QuickActionBar>),
|
||||
// >(
|
||||
// index: usize,
|
||||
// icon: &'static str,
|
||||
// toggled: bool,
|
||||
// tooltip: (String, Option<Box<dyn Action>>),
|
||||
// cx: &mut ViewContext<QuickActionBar>,
|
||||
// on_click: F,
|
||||
// ) -> AnyElement<QuickActionBar> {
|
||||
// enum QuickActionBarButton {}
|
||||
|
||||
// let theme = theme::current(cx);
|
||||
// let (tooltip_text, action) = tooltip;
|
||||
|
||||
// MouseEventHandler::new::<QuickActionBarButton, _>(index, cx, |mouse_state, _| {
|
||||
// let style = theme
|
||||
// .workspace
|
||||
// .toolbar
|
||||
// .toggleable_tool
|
||||
// .in_state(toggled)
|
||||
// .style_for(mouse_state);
|
||||
// Svg::new(icon)
|
||||
// .with_color(style.color)
|
||||
// .constrained()
|
||||
// .with_width(style.icon_width)
|
||||
// .aligned()
|
||||
// .constrained()
|
||||
// .with_width(style.button_width)
|
||||
// .with_height(style.button_width)
|
||||
// .contained()
|
||||
// .with_style(style.container)
|
||||
// })
|
||||
// .with_cursor_style(CursorStyle::PointingHand)
|
||||
// .on_click(MouseButton::Left, move |_, pane, cx| on_click(pane, cx))
|
||||
// .with_tooltip::<QuickActionBarButton>(index, tooltip_text, action, theme.tooltip.clone(), cx)
|
||||
// .into_any_named("quick action bar button")
|
||||
// }
|
||||
|
||||
impl ToolbarItemView for QuickActionBar {
|
||||
fn set_active_pane_item(
|
||||
&mut self,
|
||||
active_pane_item: Option<&dyn ItemHandle>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> ToolbarItemLocation {
|
||||
match active_pane_item {
|
||||
Some(active_item) => {
|
||||
self.active_item = Some(active_item.boxed_clone());
|
||||
self._inlay_hints_enabled_subscription.take();
|
||||
|
||||
if let Some(editor) = active_item.downcast::<Editor>() {
|
||||
let mut inlay_hints_enabled = editor.read(cx).inlay_hints_enabled();
|
||||
let mut supports_inlay_hints = editor.read(cx).supports_inlay_hints(cx);
|
||||
self._inlay_hints_enabled_subscription =
|
||||
Some(cx.observe(&editor, move |_, editor, cx| {
|
||||
let editor = editor.read(cx);
|
||||
let new_inlay_hints_enabled = editor.inlay_hints_enabled();
|
||||
let new_supports_inlay_hints = editor.supports_inlay_hints(cx);
|
||||
let should_notify = inlay_hints_enabled != new_inlay_hints_enabled
|
||||
|| supports_inlay_hints != new_supports_inlay_hints;
|
||||
inlay_hints_enabled = new_inlay_hints_enabled;
|
||||
supports_inlay_hints = new_supports_inlay_hints;
|
||||
if should_notify {
|
||||
cx.notify()
|
||||
}
|
||||
}));
|
||||
ToolbarItemLocation::PrimaryRight
|
||||
} else {
|
||||
ToolbarItemLocation::Hidden
|
||||
}
|
||||
}
|
||||
None => {
|
||||
self.active_item = None;
|
||||
ToolbarItemLocation::Hidden
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -736,6 +736,8 @@ impl InputHandler for TerminalView {
|
|||
}
|
||||
|
||||
impl Item for TerminalView {
|
||||
type Event = ItemEvent;
|
||||
|
||||
fn tab_tooltip_text(&self, cx: &AppContext) -> Option<SharedString> {
|
||||
Some(self.terminal().read(cx).title().into())
|
||||
}
|
||||
|
@ -843,6 +845,10 @@ impl Item for TerminalView {
|
|||
// .detach();
|
||||
self.workspace_id = workspace.database_id();
|
||||
}
|
||||
|
||||
fn to_item_events(event: &Self::Event, mut f: impl FnMut(ItemEvent)) {
|
||||
f(*event)
|
||||
}
|
||||
}
|
||||
|
||||
impl SearchableItem for TerminalView {
|
||||
|
|
|
@ -5,7 +5,7 @@ use crate::ColorScale;
|
|||
use crate::{SystemColors, ThemeColors};
|
||||
|
||||
pub(crate) fn neutral() -> ColorScaleSet {
|
||||
slate()
|
||||
sand()
|
||||
}
|
||||
|
||||
impl ThemeColors {
|
||||
|
@ -29,12 +29,12 @@ impl ThemeColors {
|
|||
element_disabled: neutral().light_alpha().step_3(),
|
||||
drop_target_background: blue().light_alpha().step_2(),
|
||||
ghost_element_background: system.transparent,
|
||||
ghost_element_hover: neutral().light_alpha().step_4(),
|
||||
ghost_element_active: neutral().light_alpha().step_5(),
|
||||
ghost_element_hover: neutral().light_alpha().step_3(),
|
||||
ghost_element_active: neutral().light_alpha().step_4(),
|
||||
ghost_element_selected: neutral().light_alpha().step_5(),
|
||||
ghost_element_disabled: neutral().light_alpha().step_3(),
|
||||
text: yellow().light().step_9(),
|
||||
text_muted: neutral().light().step_11(),
|
||||
text: neutral().light().step_12(),
|
||||
text_muted: neutral().light().step_10(),
|
||||
text_placeholder: neutral().light().step_10(),
|
||||
text_disabled: neutral().light().step_9(),
|
||||
text_accent: blue().light().step_11(),
|
||||
|
@ -53,13 +53,13 @@ impl ThemeColors {
|
|||
editor_gutter_background: neutral().light().step_1(), // todo!("pick the right colors")
|
||||
editor_subheader_background: neutral().light().step_2(),
|
||||
editor_active_line_background: neutral().light_alpha().step_3(),
|
||||
editor_line_number: neutral().light_alpha().step_3(), // todo!("pick the right colors")
|
||||
editor_active_line_number: neutral().light_alpha().step_3(), // todo!("pick the right colors")
|
||||
editor_highlighted_line_background: neutral().light_alpha().step_4(), // todo!("pick the right colors")
|
||||
editor_invisible: neutral().light_alpha().step_4(), // todo!("pick the right colors")
|
||||
editor_wrap_guide: neutral().light_alpha().step_4(), // todo!("pick the right colors")
|
||||
editor_active_wrap_guide: neutral().light_alpha().step_4(), // todo!("pick the right colors")
|
||||
editor_document_highlight_read_background: neutral().light_alpha().step_4(), // todo!("pick the right colors")
|
||||
editor_line_number: neutral().light().step_10(),
|
||||
editor_active_line_number: neutral().light().step_11(),
|
||||
editor_highlighted_line_background: neutral().light_alpha().step_3(),
|
||||
editor_invisible: neutral().light().step_10(),
|
||||
editor_wrap_guide: neutral().light_alpha().step_7(),
|
||||
editor_active_wrap_guide: neutral().light_alpha().step_8(), // todo!("pick the right colors")
|
||||
editor_document_highlight_read_background: neutral().light_alpha().step_3(), // todo!("pick the right colors")
|
||||
editor_document_highlight_write_background: neutral().light_alpha().step_4(), // todo!("pick the right colors")
|
||||
terminal_background: neutral().light().step_1(),
|
||||
terminal_ansi_black: black().light().step_12(),
|
||||
|
|
|
@ -1,47 +1,51 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use crate::{
|
||||
default_color_scales,
|
||||
one_themes::{one_dark, one_family},
|
||||
Theme, ThemeFamily,
|
||||
Appearance, PlayerColors, StatusColors, SyntaxTheme, SystemColors, Theme, ThemeColors,
|
||||
ThemeFamily, ThemeStyles,
|
||||
};
|
||||
|
||||
// fn zed_pro_daylight() -> Theme {
|
||||
// Theme {
|
||||
// id: "zed_pro_daylight".to_string(),
|
||||
// name: "Zed Pro Daylight".into(),
|
||||
// appearance: Appearance::Light,
|
||||
// styles: ThemeStyles {
|
||||
// system: SystemColors::default(),
|
||||
// colors: ThemeColors::light(),
|
||||
// status: StatusColors::light(),
|
||||
// player: PlayerColors::light(),
|
||||
// syntax: Arc::new(SyntaxTheme::light()),
|
||||
// },
|
||||
// }
|
||||
// }
|
||||
fn zed_pro_daylight() -> Theme {
|
||||
Theme {
|
||||
id: "zed_pro_daylight".to_string(),
|
||||
name: "Zed Pro Daylight".into(),
|
||||
appearance: Appearance::Light,
|
||||
styles: ThemeStyles {
|
||||
system: SystemColors::default(),
|
||||
colors: ThemeColors::light(),
|
||||
status: StatusColors::light(),
|
||||
player: PlayerColors::light(),
|
||||
syntax: Arc::new(SyntaxTheme::light()),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// pub(crate) fn zed_pro_moonlight() -> Theme {
|
||||
// Theme {
|
||||
// id: "zed_pro_moonlight".to_string(),
|
||||
// name: "Zed Pro Moonlight".into(),
|
||||
// appearance: Appearance::Dark,
|
||||
// styles: ThemeStyles {
|
||||
// system: SystemColors::default(),
|
||||
// colors: ThemeColors::dark(),
|
||||
// status: StatusColors::dark(),
|
||||
// player: PlayerColors::dark(),
|
||||
// syntax: Arc::new(SyntaxTheme::dark()),
|
||||
// },
|
||||
// }
|
||||
// }
|
||||
pub(crate) fn zed_pro_moonlight() -> Theme {
|
||||
Theme {
|
||||
id: "zed_pro_moonlight".to_string(),
|
||||
name: "Zed Pro Moonlight".into(),
|
||||
appearance: Appearance::Dark,
|
||||
styles: ThemeStyles {
|
||||
system: SystemColors::default(),
|
||||
colors: ThemeColors::dark(),
|
||||
status: StatusColors::dark(),
|
||||
player: PlayerColors::dark(),
|
||||
syntax: Arc::new(SyntaxTheme::dark()),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// pub fn zed_pro_family() -> ThemeFamily {
|
||||
// ThemeFamily {
|
||||
// id: "zed_pro".to_string(),
|
||||
// name: "Zed Pro".into(),
|
||||
// author: "Zed Team".into(),
|
||||
// themes: vec![zed_pro_daylight(), zed_pro_moonlight()],
|
||||
// scales: default_color_scales(),
|
||||
// }
|
||||
// }
|
||||
pub fn zed_pro_family() -> ThemeFamily {
|
||||
ThemeFamily {
|
||||
id: "zed_pro".to_string(),
|
||||
name: "Zed Pro".into(),
|
||||
author: "Zed Team".into(),
|
||||
themes: vec![zed_pro_daylight(), zed_pro_moonlight()],
|
||||
scales: default_color_scales(),
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ThemeFamily {
|
||||
fn default() -> Self {
|
||||
|
|
|
@ -6,8 +6,8 @@ use gpui::{HighlightStyle, SharedString};
|
|||
use refineable::Refineable;
|
||||
|
||||
use crate::{
|
||||
one_themes::one_family, Appearance, PlayerColors, StatusColors, SyntaxTheme, SystemColors,
|
||||
Theme, ThemeColors, ThemeFamily, ThemeStyles, UserTheme, UserThemeFamily,
|
||||
one_themes::one_family, zed_pro_family, Appearance, PlayerColors, StatusColors, SyntaxTheme,
|
||||
SystemColors, Theme, ThemeColors, ThemeFamily, ThemeStyles, UserTheme, UserThemeFamily,
|
||||
};
|
||||
|
||||
pub struct ThemeRegistry {
|
||||
|
@ -117,7 +117,7 @@ impl Default for ThemeRegistry {
|
|||
themes: HashMap::default(),
|
||||
};
|
||||
|
||||
this.insert_theme_families([one_family()]);
|
||||
this.insert_theme_families([zed_pro_family(), one_family()]);
|
||||
|
||||
this
|
||||
}
|
||||
|
|
|
@ -22,8 +22,8 @@ impl SyntaxTheme {
|
|||
highlights: vec![
|
||||
("attribute".into(), cyan().light().step_11().into()),
|
||||
("boolean".into(), tomato().light().step_11().into()),
|
||||
("comment".into(), neutral().light().step_11().into()),
|
||||
("comment.doc".into(), iris().light().step_12().into()),
|
||||
("comment".into(), neutral().light().step_10().into()),
|
||||
("comment.doc".into(), iris().light().step_11().into()),
|
||||
("constant".into(), red().light().step_9().into()),
|
||||
("constructor".into(), red().light().step_9().into()),
|
||||
("embedded".into(), red().light().step_9().into()),
|
||||
|
@ -32,11 +32,11 @@ impl SyntaxTheme {
|
|||
("enum".into(), red().light().step_9().into()),
|
||||
("function".into(), red().light().step_9().into()),
|
||||
("hint".into(), red().light().step_9().into()),
|
||||
("keyword".into(), orange().light().step_11().into()),
|
||||
("keyword".into(), orange().light().step_9().into()),
|
||||
("label".into(), red().light().step_9().into()),
|
||||
("link_text".into(), red().light().step_9().into()),
|
||||
("link_uri".into(), red().light().step_9().into()),
|
||||
("number".into(), red().light().step_9().into()),
|
||||
("number".into(), purple().light().step_10().into()),
|
||||
("operator".into(), red().light().step_9().into()),
|
||||
("predictive".into(), red().light().step_9().into()),
|
||||
("preproc".into(), red().light().step_9().into()),
|
||||
|
@ -49,16 +49,16 @@ impl SyntaxTheme {
|
|||
),
|
||||
(
|
||||
"punctuation.delimiter".into(),
|
||||
neutral().light().step_11().into(),
|
||||
neutral().light().step_10().into(),
|
||||
),
|
||||
(
|
||||
"punctuation.list_marker".into(),
|
||||
blue().light().step_11().into(),
|
||||
),
|
||||
("punctuation.special".into(), red().light().step_9().into()),
|
||||
("string".into(), jade().light().step_11().into()),
|
||||
("string".into(), jade().light().step_9().into()),
|
||||
("string.escape".into(), red().light().step_9().into()),
|
||||
("string.regex".into(), tomato().light().step_11().into()),
|
||||
("string.regex".into(), tomato().light().step_9().into()),
|
||||
("string.special".into(), red().light().step_9().into()),
|
||||
(
|
||||
"string.special.symbol".into(),
|
||||
|
@ -67,7 +67,7 @@ impl SyntaxTheme {
|
|||
("tag".into(), red().light().step_9().into()),
|
||||
("text.literal".into(), red().light().step_9().into()),
|
||||
("title".into(), red().light().step_9().into()),
|
||||
("type".into(), red().light().step_9().into()),
|
||||
("type".into(), cyan().light().step_9().into()),
|
||||
("variable".into(), red().light().step_9().into()),
|
||||
("variable.special".into(), red().light().step_9().into()),
|
||||
("variant".into(), red().light().step_9().into()),
|
||||
|
|
|
@ -2,14 +2,14 @@ use feature_flags::FeatureFlagAppExt;
|
|||
use fs::Fs;
|
||||
use fuzzy::{match_strings, StringMatch, StringMatchCandidate};
|
||||
use gpui::{
|
||||
actions, AppContext, DismissEvent, EventEmitter, FocusableView, Render, SharedString, View,
|
||||
ViewContext, VisualContext, WeakView,
|
||||
actions, AppContext, DismissEvent, Div, EventEmitter, FocusableView, Render, SharedString,
|
||||
View, ViewContext, VisualContext, WeakView,
|
||||
};
|
||||
use picker::{Picker, PickerDelegate};
|
||||
use settings::{update_settings_file, SettingsStore};
|
||||
use std::sync::Arc;
|
||||
use theme::{Theme, ThemeRegistry, ThemeSettings};
|
||||
use ui::{prelude::*, ListItem};
|
||||
use ui::{prelude::*, v_stack, ListItem};
|
||||
use util::ResultExt;
|
||||
use workspace::{ui::HighlightedLabel, Workspace};
|
||||
|
||||
|
@ -65,10 +65,10 @@ impl FocusableView for ThemeSelector {
|
|||
}
|
||||
|
||||
impl Render for ThemeSelector {
|
||||
type Element = View<Picker<ThemeSelectorDelegate>>;
|
||||
type Element = Div;
|
||||
|
||||
fn render(&mut self, _cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
self.picker.clone()
|
||||
v_stack().min_w_96().child(self.picker.clone())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ mod context_menu;
|
|||
mod disclosure;
|
||||
mod divider;
|
||||
mod icon;
|
||||
mod indicator;
|
||||
mod keybinding;
|
||||
mod label;
|
||||
mod list;
|
||||
|
@ -24,6 +25,7 @@ pub use context_menu::*;
|
|||
pub use disclosure::*;
|
||||
pub use divider::*;
|
||||
pub use icon::*;
|
||||
pub use indicator::*;
|
||||
pub use keybinding::*;
|
||||
pub use label::*;
|
||||
pub use list::*;
|
||||
|
|
|
@ -1,15 +1,26 @@
|
|||
use gpui::{rems, svg, IntoElement, Svg};
|
||||
use gpui::{rems, svg, IntoElement, Rems, Svg};
|
||||
use strum::EnumIter;
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
#[derive(Default, PartialEq, Copy, Clone)]
|
||||
pub enum IconSize {
|
||||
XSmall,
|
||||
Small,
|
||||
#[default]
|
||||
Medium,
|
||||
}
|
||||
|
||||
impl IconSize {
|
||||
pub fn rems(self) -> Rems {
|
||||
match self {
|
||||
IconSize::XSmall => rems(12. / 16.),
|
||||
IconSize::Small => rems(14. / 16.),
|
||||
IconSize::Medium => rems(16. / 16.),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Copy, Clone, EnumIter)]
|
||||
pub enum Icon {
|
||||
Ai,
|
||||
|
@ -170,13 +181,8 @@ impl RenderOnce for IconElement {
|
|||
type Rendered = Svg;
|
||||
|
||||
fn render(self, cx: &mut WindowContext) -> Self::Rendered {
|
||||
let svg_size = match self.size {
|
||||
IconSize::Small => rems(14. / 16.),
|
||||
IconSize::Medium => rems(16. / 16.),
|
||||
};
|
||||
|
||||
svg()
|
||||
.size(svg_size)
|
||||
.size(self.size.rems())
|
||||
.flex_none()
|
||||
.path(self.path)
|
||||
.text_color(self.color.color(cx))
|
||||
|
|
60
crates/ui2/src/components/indicator.rs
Normal file
60
crates/ui2/src/components/indicator.rs
Normal file
|
@ -0,0 +1,60 @@
|
|||
use gpui::{Div, Position};
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
#[derive(Default)]
|
||||
pub enum IndicatorStyle {
|
||||
#[default]
|
||||
Dot,
|
||||
Bar,
|
||||
}
|
||||
|
||||
#[derive(IntoElement)]
|
||||
pub struct Indicator {
|
||||
position: Position,
|
||||
style: IndicatorStyle,
|
||||
color: Color,
|
||||
}
|
||||
|
||||
impl Indicator {
|
||||
pub fn dot() -> Self {
|
||||
Self {
|
||||
position: Position::Relative,
|
||||
style: IndicatorStyle::Dot,
|
||||
color: Color::Default,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bar() -> Self {
|
||||
Self {
|
||||
position: Position::Relative,
|
||||
style: IndicatorStyle::Dot,
|
||||
color: Color::Default,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn color(mut self, color: Color) -> Self {
|
||||
self.color = color;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn absolute(mut self) -> Self {
|
||||
self.position = Position::Absolute;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for Indicator {
|
||||
type Rendered = Div;
|
||||
|
||||
fn render(self, cx: &mut WindowContext) -> Self::Rendered {
|
||||
div()
|
||||
.flex_none()
|
||||
.map(|this| match self.style {
|
||||
IndicatorStyle::Dot => this.w_1p5().h_1p5().rounded_full(),
|
||||
IndicatorStyle::Bar => this.w_full().h_1p5().rounded_t_md(),
|
||||
})
|
||||
.when(self.position == Position::Absolute, |this| this.absolute())
|
||||
.bg(self.color.color(cx))
|
||||
}
|
||||
}
|
|
@ -8,5 +8,6 @@ pub use crate::clickable::*;
|
|||
pub use crate::disableable::*;
|
||||
pub use crate::fixed::*;
|
||||
pub use crate::selectable::*;
|
||||
pub use crate::{h_stack, v_stack};
|
||||
pub use crate::{ButtonCommon, Color, StyledExt};
|
||||
pub use theme::ActiveTheme;
|
||||
|
|
|
@ -259,6 +259,8 @@ impl FocusableView for WelcomePage {
|
|||
}
|
||||
|
||||
impl Item for WelcomePage {
|
||||
type Event = ItemEvent;
|
||||
|
||||
fn tab_content(&self, _: Option<usize>, _: &WindowContext) -> AnyElement {
|
||||
"Welcome to Zed!".into_any()
|
||||
}
|
||||
|
@ -278,4 +280,8 @@ impl Item for WelcomePage {
|
|||
_settings_subscription: cx.observe_global::<SettingsStore>(move |_, cx| cx.notify()),
|
||||
}))
|
||||
}
|
||||
|
||||
fn to_item_events(event: &Self::Event, mut f: impl FnMut(workspace::item::ItemEvent)) {
|
||||
f(*event)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -78,7 +78,7 @@ impl Settings for ItemSettings {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, Hash, Debug)]
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Hash, Debug)]
|
||||
pub enum ItemEvent {
|
||||
CloseItem,
|
||||
UpdateTab,
|
||||
|
@ -92,7 +92,9 @@ pub struct BreadcrumbText {
|
|||
pub highlights: Option<Vec<(Range<usize>, HighlightStyle)>>,
|
||||
}
|
||||
|
||||
pub trait Item: FocusableView + EventEmitter<ItemEvent> {
|
||||
pub trait Item: FocusableView + EventEmitter<Self::Event> {
|
||||
type Event;
|
||||
|
||||
fn deactivated(&mut self, _: &mut ViewContext<Self>) {}
|
||||
fn workspace_deactivated(&mut self, _: &mut ViewContext<Self>) {}
|
||||
fn navigate(&mut self, _: Box<dyn Any>, _: &mut ViewContext<Self>) -> bool {
|
||||
|
@ -155,6 +157,8 @@ pub trait Item: FocusableView + EventEmitter<ItemEvent> {
|
|||
unimplemented!("reload() must be implemented if can_save() returns true")
|
||||
}
|
||||
|
||||
fn to_item_events(event: &Self::Event, f: impl FnMut(ItemEvent));
|
||||
|
||||
fn act_as_type<'a>(
|
||||
&'a self,
|
||||
type_id: TypeId,
|
||||
|
@ -206,12 +210,12 @@ pub trait Item: FocusableView + EventEmitter<ItemEvent> {
|
|||
}
|
||||
|
||||
pub trait ItemHandle: 'static + Send {
|
||||
fn focus_handle(&self, cx: &WindowContext) -> FocusHandle;
|
||||
fn subscribe_to_item_events(
|
||||
&self,
|
||||
cx: &mut WindowContext,
|
||||
handler: Box<dyn Fn(&ItemEvent, &mut WindowContext) + Send>,
|
||||
handler: Box<dyn Fn(ItemEvent, &mut WindowContext)>,
|
||||
) -> gpui::Subscription;
|
||||
fn focus_handle(&self, cx: &WindowContext) -> FocusHandle;
|
||||
fn tab_tooltip_text(&self, cx: &AppContext) -> Option<SharedString>;
|
||||
fn tab_description(&self, detail: usize, cx: &AppContext) -> Option<SharedString>;
|
||||
fn tab_content(&self, detail: Option<usize>, cx: &WindowContext) -> AnyElement;
|
||||
|
@ -285,20 +289,20 @@ impl dyn ItemHandle {
|
|||
}
|
||||
|
||||
impl<T: Item> ItemHandle for View<T> {
|
||||
fn focus_handle(&self, cx: &WindowContext) -> FocusHandle {
|
||||
self.focus_handle(cx)
|
||||
}
|
||||
|
||||
fn subscribe_to_item_events(
|
||||
&self,
|
||||
cx: &mut WindowContext,
|
||||
handler: Box<dyn Fn(&ItemEvent, &mut WindowContext) + Send>,
|
||||
handler: Box<dyn Fn(ItemEvent, &mut WindowContext)>,
|
||||
) -> gpui::Subscription {
|
||||
cx.subscribe(self, move |_, event, cx| {
|
||||
handler(event, cx);
|
||||
T::to_item_events(event, |item_event| handler(item_event, cx));
|
||||
})
|
||||
}
|
||||
|
||||
fn focus_handle(&self, cx: &WindowContext) -> FocusHandle {
|
||||
self.focus_handle(cx)
|
||||
}
|
||||
|
||||
fn tab_tooltip_text(&self, cx: &AppContext) -> Option<SharedString> {
|
||||
self.read(cx).tab_tooltip_text(cx)
|
||||
}
|
||||
|
@ -461,7 +465,7 @@ impl<T: Item> ItemHandle for View<T> {
|
|||
}
|
||||
}
|
||||
|
||||
match event {
|
||||
T::to_item_events(event, |event| match event {
|
||||
ItemEvent::CloseItem => {
|
||||
pane.update(cx, |pane, cx| {
|
||||
pane.close_item_by_id(item.item_id(), crate::SaveIntent::Close, cx)
|
||||
|
@ -489,7 +493,7 @@ impl<T: Item> ItemHandle for View<T> {
|
|||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
||||
cx.on_blur(&self.focus_handle(cx), move |workspace, cx| {
|
||||
|
@ -655,12 +659,7 @@ pub enum FollowEvent {
|
|||
Unfollow,
|
||||
}
|
||||
|
||||
pub trait FollowableEvents {
|
||||
fn to_follow_event(&self) -> Option<FollowEvent>;
|
||||
}
|
||||
|
||||
pub trait FollowableItem: Item {
|
||||
type FollowableEvent: FollowableEvents;
|
||||
fn remote_id(&self) -> Option<ViewId>;
|
||||
fn to_state_proto(&self, cx: &WindowContext) -> Option<proto::view::Variant>;
|
||||
fn from_state_proto(
|
||||
|
@ -670,9 +669,10 @@ pub trait FollowableItem: Item {
|
|||
state: &mut Option<proto::view::Variant>,
|
||||
cx: &mut WindowContext,
|
||||
) -> Option<Task<Result<View<Self>>>>;
|
||||
fn to_follow_event(event: &Self::Event) -> Option<FollowEvent>;
|
||||
fn add_event_to_update_proto(
|
||||
&self,
|
||||
event: &Self::FollowableEvent,
|
||||
event: &Self::Event,
|
||||
update: &mut Option<proto::update_view::Variant>,
|
||||
cx: &WindowContext,
|
||||
) -> bool;
|
||||
|
@ -683,7 +683,6 @@ pub trait FollowableItem: Item {
|
|||
cx: &mut ViewContext<Self>,
|
||||
) -> Task<Result<()>>;
|
||||
fn is_project_item(&self, cx: &WindowContext) -> bool;
|
||||
|
||||
fn set_leader_peer_id(&mut self, leader_peer_id: Option<PeerId>, cx: &mut ViewContext<Self>);
|
||||
}
|
||||
|
||||
|
@ -739,10 +738,7 @@ impl<T: FollowableItem> FollowableItemHandle for View<T> {
|
|||
}
|
||||
|
||||
fn to_follow_event(&self, event: &dyn Any) -> Option<FollowEvent> {
|
||||
event
|
||||
.downcast_ref()
|
||||
.map(T::FollowableEvent::to_follow_event)
|
||||
.flatten()
|
||||
T::to_follow_event(event.downcast_ref()?)
|
||||
}
|
||||
|
||||
fn apply_update_proto(
|
||||
|
@ -929,6 +925,12 @@ pub mod test {
|
|||
}
|
||||
|
||||
impl Item for TestItem {
|
||||
type Event = ItemEvent;
|
||||
|
||||
fn to_item_events(event: &Self::Event, mut f: impl FnMut(ItemEvent)) {
|
||||
f(*event)
|
||||
}
|
||||
|
||||
fn tab_description(&self, detail: usize, _: &AppContext) -> Option<SharedString> {
|
||||
self.tab_descriptions.as_ref().and_then(|descriptions| {
|
||||
let description = *descriptions.get(detail).or_else(|| descriptions.last())?;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::{
|
||||
item::{Item, ItemHandle, ItemSettings, WeakItemHandle},
|
||||
item::{ClosePosition, Item, ItemHandle, ItemSettings, WeakItemHandle},
|
||||
toolbar::Toolbar,
|
||||
workspace_settings::{AutosaveSetting, WorkspaceSettings},
|
||||
NewCenterTerminal, NewFile, NewSearch, SplitDirection, ToggleZoom, Workspace,
|
||||
|
@ -27,7 +27,8 @@ use std::{
|
|||
};
|
||||
|
||||
use ui::{
|
||||
h_stack, prelude::*, right_click_menu, Color, Icon, IconButton, IconElement, Label, Tooltip,
|
||||
h_stack, prelude::*, right_click_menu, ButtonSize, Color, Icon, IconButton, IconSize,
|
||||
Indicator, Label, Tooltip,
|
||||
};
|
||||
use ui::{v_stack, ContextMenu};
|
||||
use util::truncate_and_remove_front;
|
||||
|
@ -1418,22 +1419,7 @@ impl Pane {
|
|||
cx: &mut ViewContext<'_, Pane>,
|
||||
) -> impl IntoElement {
|
||||
let label = item.tab_content(Some(detail), cx);
|
||||
let close_icon = || {
|
||||
let id = item.item_id();
|
||||
|
||||
div()
|
||||
.id(ix)
|
||||
.invisible()
|
||||
.group_hover("", |style| style.visible())
|
||||
.child(
|
||||
IconButton::new("close_tab", Icon::Close).on_click(cx.listener(
|
||||
move |pane, _, cx| {
|
||||
pane.close_item_by_id(id, SaveIntent::Close, cx)
|
||||
.detach_and_log_err(cx);
|
||||
},
|
||||
)),
|
||||
)
|
||||
};
|
||||
let close_side = &ItemSettings::get_global(cx).close_position;
|
||||
|
||||
let (text_color, tab_bg, tab_hover_bg, tab_active_bg) = match ix == self.active_item_index {
|
||||
false => (
|
||||
|
@ -1450,102 +1436,129 @@ impl Pane {
|
|||
),
|
||||
};
|
||||
|
||||
let close_right = ItemSettings::get_global(cx).close_position.right();
|
||||
let is_active = ix == self.active_item_index;
|
||||
|
||||
let indicator = {
|
||||
let indicator_color = match (item.has_conflict(cx), item.is_dirty(cx)) {
|
||||
(true, _) => Some(Color::Warning),
|
||||
(_, true) => Some(Color::Accent),
|
||||
(false, false) => None,
|
||||
};
|
||||
|
||||
h_stack()
|
||||
.w_3()
|
||||
.h_3()
|
||||
.justify_center()
|
||||
.absolute()
|
||||
.map(|this| match close_side {
|
||||
ClosePosition::Left => this.right_1(),
|
||||
ClosePosition::Right => this.left_1(),
|
||||
})
|
||||
.when_some(indicator_color, |this, indicator_color| {
|
||||
this.child(Indicator::dot().color(indicator_color))
|
||||
})
|
||||
};
|
||||
|
||||
let close_button = {
|
||||
let id = item.item_id();
|
||||
|
||||
h_stack()
|
||||
.invisible()
|
||||
.w_3()
|
||||
.h_3()
|
||||
.justify_center()
|
||||
.absolute()
|
||||
.map(|this| match close_side {
|
||||
ClosePosition::Left => this.left_1(),
|
||||
ClosePosition::Right => this.right_1(),
|
||||
})
|
||||
.group_hover("", |style| style.visible())
|
||||
.child(
|
||||
// TODO: Fix button size
|
||||
IconButton::new("close tab", Icon::Close)
|
||||
.icon_color(Color::Muted)
|
||||
.size(ButtonSize::None)
|
||||
.icon_size(IconSize::XSmall)
|
||||
.on_click(cx.listener(move |pane, _, cx| {
|
||||
pane.close_item_by_id(id, SaveIntent::Close, cx)
|
||||
.detach_and_log_err(cx);
|
||||
})),
|
||||
)
|
||||
};
|
||||
|
||||
let tab = div()
|
||||
.group("")
|
||||
.id(ix)
|
||||
.cursor_pointer()
|
||||
.when_some(item.tab_tooltip_text(cx), |div, text| {
|
||||
div.tooltip(move |cx| cx.build_view(|cx| Tooltip::new(text.clone())).into())
|
||||
})
|
||||
.on_click(cx.listener(move |v: &mut Self, e, cx| v.activate_item(ix, true, true, cx)))
|
||||
// .on_drag(move |pane, cx| pane.render_tab(ix, item.boxed_clone(), detail, cx))
|
||||
// .drag_over::<DraggedTab>(|d| d.bg(cx.theme().colors().element_drop_target))
|
||||
// .on_drop(|_view, state: View<DraggedTab>, cx| {
|
||||
// eprintln!("{:?}", state.read(cx));
|
||||
// })
|
||||
.flex()
|
||||
.items_center()
|
||||
.justify_center()
|
||||
// todo!("Nate - I need to do some work to balance all the items in the tab once things stablize")
|
||||
.map(|this| {
|
||||
if close_right {
|
||||
this.pl_3().pr_1()
|
||||
} else {
|
||||
this.pr_1().pr_3()
|
||||
}
|
||||
})
|
||||
.py_1()
|
||||
.bg(tab_bg)
|
||||
.border_color(cx.theme().colors().border)
|
||||
.text_color(if is_active {
|
||||
cx.theme().colors().text
|
||||
} else {
|
||||
cx.theme().colors().text_muted
|
||||
})
|
||||
.bg(tab_bg)
|
||||
// 30px @ 16px/rem
|
||||
.h(rems(1.875))
|
||||
.map(|this| {
|
||||
let is_first_item = ix == 0;
|
||||
let is_last_item = ix == self.items.len() - 1;
|
||||
match ix.cmp(&self.active_item_index) {
|
||||
cmp::Ordering::Less => this.border_l().mr_px(),
|
||||
cmp::Ordering::Greater => {
|
||||
if is_last_item {
|
||||
this.mr_px().ml_px()
|
||||
cmp::Ordering::Less => {
|
||||
if is_first_item {
|
||||
this.pl_px().pr_px().border_b()
|
||||
} else {
|
||||
this.border_r().ml_px()
|
||||
this.border_l().pr_px().border_b()
|
||||
}
|
||||
}
|
||||
cmp::Ordering::Greater => {
|
||||
if is_last_item {
|
||||
this.pr_px().pl_px().border_b()
|
||||
} else {
|
||||
this.border_r().pl_px().border_b()
|
||||
}
|
||||
}
|
||||
cmp::Ordering::Equal => {
|
||||
if is_first_item {
|
||||
this.pl_px().border_r().pb_px()
|
||||
} else {
|
||||
this.border_l().border_r().pb_px()
|
||||
}
|
||||
}
|
||||
cmp::Ordering::Equal => this.border_l().border_r(),
|
||||
}
|
||||
})
|
||||
// .hover(|h| h.bg(tab_hover_bg))
|
||||
// .active(|a| a.bg(tab_active_bg))
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.items_center()
|
||||
h_stack()
|
||||
.group("")
|
||||
.id(ix)
|
||||
.relative()
|
||||
.h_full()
|
||||
.cursor_pointer()
|
||||
.when_some(item.tab_tooltip_text(cx), |div, text| {
|
||||
div.tooltip(move |cx| cx.build_view(|cx| Tooltip::new(text.clone())).into())
|
||||
})
|
||||
.on_click(
|
||||
cx.listener(move |v: &mut Self, e, cx| v.activate_item(ix, true, true, cx)),
|
||||
)
|
||||
// .on_drag(move |pane, cx| pane.render_tab(ix, item.boxed_clone(), detail, cx))
|
||||
// .drag_over::<DraggedTab>(|d| d.bg(cx.theme().colors().element_drop_target))
|
||||
// .on_drop(|_view, state: View<DraggedTab>, cx| {
|
||||
// eprintln!("{:?}", state.read(cx));
|
||||
// })
|
||||
.px_5()
|
||||
// .hover(|h| h.bg(tab_hover_bg))
|
||||
// .active(|a| a.bg(tab_active_bg))
|
||||
.gap_1()
|
||||
.text_color(text_color)
|
||||
.children(
|
||||
item.has_conflict(cx)
|
||||
.then(|| {
|
||||
div().border().border_color(gpui::red()).child(
|
||||
IconElement::new(Icon::ExclamationTriangle)
|
||||
.size(ui::IconSize::Small)
|
||||
.color(Color::Warning),
|
||||
)
|
||||
})
|
||||
.or(item.is_dirty(cx).then(|| {
|
||||
div().border().border_color(gpui::red()).child(
|
||||
IconElement::new(Icon::ExclamationTriangle)
|
||||
.size(ui::IconSize::Small)
|
||||
.color(Color::Info),
|
||||
)
|
||||
})),
|
||||
)
|
||||
.children((!close_right).then(|| close_icon()))
|
||||
.child(label)
|
||||
.children(close_right.then(|| close_icon())),
|
||||
.child(indicator)
|
||||
.child(close_button)
|
||||
.child(label),
|
||||
);
|
||||
|
||||
right_click_menu(ix).trigger(tab).menu(|cx| {
|
||||
ContextMenu::build(cx, |menu, cx| {
|
||||
menu.action(
|
||||
"Close Active Item",
|
||||
CloseActiveItem { save_intent: None }.boxed_clone(),
|
||||
)
|
||||
.action("Close Inactive Items", CloseInactiveItems.boxed_clone())
|
||||
.action("Close Clean Items", CloseCleanItems.boxed_clone())
|
||||
.action("Close Items To The Left", CloseItemsToTheLeft.boxed_clone())
|
||||
.action(
|
||||
"Close Items To The Right",
|
||||
CloseItemsToTheRight.boxed_clone(),
|
||||
)
|
||||
.action(
|
||||
"Close All Items",
|
||||
CloseAllItems { save_intent: None }.boxed_clone(),
|
||||
)
|
||||
menu.action("Close", CloseActiveItem { save_intent: None }.boxed_clone())
|
||||
.action("Close Others", CloseInactiveItems.boxed_clone())
|
||||
.separator()
|
||||
.action("Close Left", CloseItemsToTheLeft.boxed_clone())
|
||||
.action("Close Right", CloseItemsToTheRight.boxed_clone())
|
||||
.separator()
|
||||
.action("Close Clean", CloseCleanItems.boxed_clone())
|
||||
.action(
|
||||
"Close All",
|
||||
CloseAllItems { save_intent: None }.boxed_clone(),
|
||||
)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
@ -1565,116 +1578,118 @@ impl Pane {
|
|||
// Left Side
|
||||
.child(
|
||||
h_stack()
|
||||
.px_2()
|
||||
.flex()
|
||||
.flex_none()
|
||||
.gap_1()
|
||||
.px_1()
|
||||
.border_b()
|
||||
.border_r()
|
||||
.border_color(cx.theme().colors().border)
|
||||
// Nav Buttons
|
||||
.child(
|
||||
div().border().border_color(gpui::red()).child(
|
||||
IconButton::new("navigate_backward", Icon::ArrowLeft)
|
||||
.on_click({
|
||||
let view = cx.view().clone();
|
||||
move |_, cx| view.update(cx, Self::navigate_backward)
|
||||
})
|
||||
.disabled(!self.can_navigate_backward()),
|
||||
),
|
||||
IconButton::new("navigate_backward", Icon::ArrowLeft)
|
||||
.icon_size(IconSize::Small)
|
||||
.on_click({
|
||||
let view = cx.view().clone();
|
||||
move |_, cx| view.update(cx, Self::navigate_backward)
|
||||
})
|
||||
.disabled(!self.can_navigate_backward()),
|
||||
)
|
||||
.child(
|
||||
div().border().border_color(gpui::red()).child(
|
||||
IconButton::new("navigate_forward", Icon::ArrowRight)
|
||||
.on_click({
|
||||
let view = cx.view().clone();
|
||||
move |_, cx| view.update(cx, Self::navigate_backward)
|
||||
})
|
||||
.disabled(!self.can_navigate_forward()),
|
||||
),
|
||||
IconButton::new("navigate_forward", Icon::ArrowRight)
|
||||
.icon_size(IconSize::Small)
|
||||
.on_click({
|
||||
let view = cx.view().clone();
|
||||
move |_, cx| view.update(cx, Self::navigate_backward)
|
||||
})
|
||||
.disabled(!self.can_navigate_forward()),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
div().flex_1().h_full().child(
|
||||
div().id("tabs").flex().overflow_x_scroll().children(
|
||||
self.items
|
||||
.iter()
|
||||
.enumerate()
|
||||
.zip(self.tab_details(cx))
|
||||
.map(|((ix, item), detail)| self.render_tab(ix, item, detail, cx)),
|
||||
div()
|
||||
.relative()
|
||||
.flex_1()
|
||||
.h_full()
|
||||
.overflow_hidden_x()
|
||||
.child(
|
||||
div()
|
||||
.absolute()
|
||||
.top_0()
|
||||
.left_0()
|
||||
.z_index(1)
|
||||
.size_full()
|
||||
.border_b()
|
||||
.border_color(cx.theme().colors().border),
|
||||
)
|
||||
.child(
|
||||
h_stack().id("tabs").z_index(2).children(
|
||||
self.items
|
||||
.iter()
|
||||
.enumerate()
|
||||
.zip(self.tab_details(cx))
|
||||
.map(|((ix, item), detail)| self.render_tab(ix, item, detail, cx)),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
// Right Side
|
||||
.child(
|
||||
div()
|
||||
.px_1()
|
||||
h_stack()
|
||||
.flex()
|
||||
.flex_none()
|
||||
.gap_2()
|
||||
// Nav Buttons
|
||||
.gap_1()
|
||||
.px_1()
|
||||
.border_b()
|
||||
.border_l()
|
||||
.border_color(cx.theme().colors().border)
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.items_center()
|
||||
.gap_px()
|
||||
.child(
|
||||
div()
|
||||
.bg(gpui::blue())
|
||||
.border()
|
||||
.border_color(gpui::red())
|
||||
.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())
|
||||
.action(
|
||||
"New Terminal",
|
||||
NewCenterTerminal.boxed_clone(),
|
||||
)
|
||||
.action("New Search", NewSearch.boxed_clone())
|
||||
});
|
||||
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))
|
||||
}),
|
||||
IconButton::new("plus", Icon::Plus)
|
||||
.icon_size(IconSize::Small)
|
||||
.on_click(cx.listener(|this, _, cx| {
|
||||
let menu = ContextMenu::build(cx, |menu, cx| {
|
||||
menu.action("New File", NewFile.boxed_clone())
|
||||
.action(
|
||||
"New Terminal",
|
||||
NewCenterTerminal.boxed_clone(),
|
||||
)
|
||||
.action("New Search", NewSearch.boxed_clone())
|
||||
});
|
||||
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).on_click(
|
||||
cx.listener(|this, _, cx| {
|
||||
let menu = ContextMenu::build(cx, |menu, cx| {
|
||||
menu.action("Split Right", SplitRight.boxed_clone())
|
||||
.action("Split Left", SplitLeft.boxed_clone())
|
||||
.action("Split Up", SplitUp.boxed_clone())
|
||||
.action("Split Down", SplitDown.boxed_clone())
|
||||
});
|
||||
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))
|
||||
},
|
||||
),
|
||||
),
|
||||
IconButton::new("split", Icon::Split)
|
||||
.icon_size(IconSize::Small)
|
||||
.on_click(cx.listener(|this, _, cx| {
|
||||
let menu = ContextMenu::build(cx, |menu, cx| {
|
||||
menu.action("Split Right", SplitRight.boxed_clone())
|
||||
.action("Split Left", SplitLeft.boxed_clone())
|
||||
.action("Split Up", SplitUp.boxed_clone())
|
||||
.action("Split Down", SplitDown.boxed_clone())
|
||||
});
|
||||
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))
|
||||
}),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
@ -2092,6 +2107,8 @@ impl Render for Pane {
|
|||
v_stack()
|
||||
.key_context("Pane")
|
||||
.track_focus(&self.focus_handle)
|
||||
.size_full()
|
||||
.overflow_hidden()
|
||||
.on_focus_in({
|
||||
let this = this.clone();
|
||||
move |event, cx| {
|
||||
|
@ -2159,7 +2176,6 @@ impl Render for Pane {
|
|||
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| {
|
||||
pane.close_active_item(action, cx)
|
||||
|
|
|
@ -59,7 +59,6 @@ impl SharedScreen {
|
|||
}
|
||||
|
||||
impl EventEmitter<Event> for SharedScreen {}
|
||||
impl EventEmitter<ItemEvent> for SharedScreen {}
|
||||
|
||||
impl FocusableView for SharedScreen {
|
||||
fn focus_handle(&self, _: &AppContext) -> FocusHandle {
|
||||
|
@ -79,9 +78,12 @@ impl Render for SharedScreen {
|
|||
}
|
||||
|
||||
impl Item for SharedScreen {
|
||||
type Event = Event;
|
||||
|
||||
fn tab_tooltip_text(&self, _: &AppContext) -> Option<SharedString> {
|
||||
Some(format!("{}'s screen", self.user.github_login).into())
|
||||
}
|
||||
|
||||
fn deactivated(&mut self, cx: &mut ViewContext<Self>) {
|
||||
if let Some(nav_history) = self.nav_history.as_mut() {
|
||||
nav_history.push::<()>(None, cx);
|
||||
|
@ -111,4 +113,10 @@ impl Item for SharedScreen {
|
|||
let track = self.track.upgrade()?;
|
||||
Some(cx.build_view(|cx| Self::new(&track, self.peer_id, self.user.clone(), cx)))
|
||||
}
|
||||
|
||||
fn to_item_events(event: &Self::Event, mut f: impl FnMut(ItemEvent)) {
|
||||
match event {
|
||||
Event::Close => f(ItemEvent::CloseItem),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
use crate::ItemHandle;
|
||||
use gpui::{
|
||||
div, AnyView, Div, Entity, EntityId, EventEmitter, ParentElement as _, Render, Styled, View,
|
||||
AnyView, Div, Entity, EntityId, EventEmitter, ParentElement as _, Render, Styled, View,
|
||||
ViewContext, WindowContext,
|
||||
};
|
||||
use ui::prelude::*;
|
||||
use ui::{h_stack, v_stack, Icon, IconButton};
|
||||
use ui::{h_stack, v_stack};
|
||||
|
||||
pub enum ToolbarItemEvent {
|
||||
ChangeLocation(ToolbarItemLocation),
|
||||
|
@ -87,25 +87,7 @@ impl Render for Toolbar {
|
|||
.child(
|
||||
h_stack()
|
||||
.justify_between()
|
||||
// Toolbar left side
|
||||
.children(self.items.iter().map(|(child, _)| child.to_any()))
|
||||
// Toolbar right side
|
||||
.child(
|
||||
h_stack()
|
||||
.p_1()
|
||||
.child(
|
||||
div()
|
||||
.border()
|
||||
.border_color(gpui::red())
|
||||
.child(IconButton::new("buffer-search", Icon::MagnifyingGlass)),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.border()
|
||||
.border_color(gpui::red())
|
||||
.child(IconButton::new("inline-assist", Icon::MagicWand)),
|
||||
),
|
||||
),
|
||||
.children(self.items.iter().map(|(child, _)| child.to_any())),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -212,27 +212,31 @@ pub fn init(app_state: Arc<AppState>, cx: &mut AppContext) {
|
|||
init_settings(cx);
|
||||
notifications::init(cx);
|
||||
|
||||
// cx.add_global_action({
|
||||
// let app_state = Arc::downgrade(&app_state);
|
||||
// move |_: &Open, cx: &mut AppContext| {
|
||||
// let mut paths = cx.prompt_for_paths(PathPromptOptions {
|
||||
// files: true,
|
||||
// directories: true,
|
||||
// multiple: true,
|
||||
// });
|
||||
cx.on_action(Workspace::close_global);
|
||||
cx.on_action(restart);
|
||||
|
||||
// if let Some(app_state) = app_state.upgrade() {
|
||||
// cx.spawn(move |mut cx| async move {
|
||||
// if let Some(paths) = paths.recv().await.flatten() {
|
||||
// cx.update(|cx| {
|
||||
// open_paths(&paths, &app_state, None, cx).detach_and_log_err(cx)
|
||||
// });
|
||||
// }
|
||||
// })
|
||||
// .detach();
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
cx.on_action({
|
||||
let app_state = Arc::downgrade(&app_state);
|
||||
move |_: &Open, cx: &mut AppContext| {
|
||||
let mut paths = cx.prompt_for_paths(PathPromptOptions {
|
||||
files: true,
|
||||
directories: true,
|
||||
multiple: true,
|
||||
});
|
||||
|
||||
if let Some(app_state) = app_state.upgrade() {
|
||||
cx.spawn(move |mut cx| async move {
|
||||
if let Some(paths) = paths.await.log_err().flatten() {
|
||||
cx.update(|cx| {
|
||||
open_paths(&paths, &app_state, None, cx).detach_and_log_err(cx)
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
type ProjectItemBuilders =
|
||||
|
@ -1076,7 +1080,6 @@ impl Workspace {
|
|||
}
|
||||
}
|
||||
|
||||
// todo!(Non-window-actions)
|
||||
pub fn close_global(_: &CloseWindow, cx: &mut AppContext) {
|
||||
cx.windows().iter().find(|window| {
|
||||
window
|
||||
|
@ -1094,21 +1097,18 @@ impl Workspace {
|
|||
});
|
||||
}
|
||||
|
||||
pub fn close(
|
||||
&mut self,
|
||||
_: &CloseWindow,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Option<Task<Result<()>>> {
|
||||
pub fn close_window(&mut self, _: &CloseWindow, cx: &mut ViewContext<Self>) {
|
||||
let window = cx.window_handle();
|
||||
let prepare = self.prepare_to_close(false, cx);
|
||||
Some(cx.spawn(|_, mut cx| async move {
|
||||
cx.spawn(|_, mut cx| async move {
|
||||
if prepare.await? {
|
||||
window.update(&mut cx, |_, cx| {
|
||||
cx.remove_window();
|
||||
})?;
|
||||
}
|
||||
Ok(())
|
||||
}))
|
||||
anyhow::Ok(())
|
||||
})
|
||||
.detach_and_log_err(cx)
|
||||
}
|
||||
|
||||
pub fn prepare_to_close(
|
||||
|
@ -2325,42 +2325,44 @@ impl Workspace {
|
|||
}))
|
||||
}
|
||||
|
||||
// pub fn follow_next_collaborator(
|
||||
// &mut self,
|
||||
// _: &FollowNextCollaborator,
|
||||
// cx: &mut ViewContext<Self>,
|
||||
// ) -> Option<Task<Result<()>>> {
|
||||
// let collaborators = self.project.read(cx).collaborators();
|
||||
// let next_leader_id = if let Some(leader_id) = self.leader_for_pane(&self.active_pane) {
|
||||
// let mut collaborators = collaborators.keys().copied();
|
||||
// for peer_id in collaborators.by_ref() {
|
||||
// if peer_id == leader_id {
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// collaborators.next()
|
||||
// } else if let Some(last_leader_id) =
|
||||
// self.last_leaders_by_pane.get(&self.active_pane.downgrade())
|
||||
// {
|
||||
// if collaborators.contains_key(last_leader_id) {
|
||||
// Some(*last_leader_id)
|
||||
// } else {
|
||||
// None
|
||||
// pub fn follow_next_collaborator(
|
||||
// &mut self,
|
||||
// _: &FollowNextCollaborator,
|
||||
// cx: &mut ViewContext<Self>,
|
||||
// ) {
|
||||
// let collaborators = self.project.read(cx).collaborators();
|
||||
// let next_leader_id = if let Some(leader_id) = self.leader_for_pane(&self.active_pane) {
|
||||
// let mut collaborators = collaborators.keys().copied();
|
||||
// for peer_id in collaborators.by_ref() {
|
||||
// if peer_id == leader_id {
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// collaborators.next()
|
||||
// } else if let Some(last_leader_id) =
|
||||
// self.last_leaders_by_pane.get(&self.active_pane.downgrade())
|
||||
// {
|
||||
// if collaborators.contains_key(last_leader_id) {
|
||||
// Some(*last_leader_id)
|
||||
// } else {
|
||||
// None
|
||||
// };
|
||||
|
||||
// let pane = self.active_pane.clone();
|
||||
// let Some(leader_id) = next_leader_id.or_else(|| collaborators.keys().copied().next())
|
||||
// else {
|
||||
// return None;
|
||||
// };
|
||||
// if Some(leader_id) == self.unfollow(&pane, cx) {
|
||||
// return None;
|
||||
// }
|
||||
// self.follow(leader_id, cx)
|
||||
// } else {
|
||||
// None
|
||||
// };
|
||||
|
||||
// let pane = self.active_pane.clone();
|
||||
// let Some(leader_id) = next_leader_id.or_else(|| collaborators.keys().copied().next())
|
||||
// else {
|
||||
// return;
|
||||
// };
|
||||
// if Some(leader_id) == self.unfollow(&pane, cx) {
|
||||
// return;
|
||||
// }
|
||||
// if let Some(task) = self.follow(leader_id, cx) {
|
||||
// task.detach();
|
||||
// }
|
||||
// }
|
||||
|
||||
pub fn follow(
|
||||
&mut self,
|
||||
|
@ -2409,6 +2411,18 @@ impl Workspace {
|
|||
self.start_following(leader_id, cx)
|
||||
}
|
||||
|
||||
// // if you're already following, find the right pane and focus it.
|
||||
// for (pane, state) in &self.follower_states {
|
||||
// if leader_id == state.leader_id {
|
||||
// cx.focus(pane);
|
||||
// return None;
|
||||
// }
|
||||
// }
|
||||
|
||||
// // Otherwise, follow.
|
||||
// self.start_following(leader_id, cx)
|
||||
// }
|
||||
|
||||
pub fn unfollow(&mut self, pane: &View<Pane>, cx: &mut ViewContext<Self>) -> Option<PeerId> {
|
||||
let state = self.follower_states.remove(pane)?;
|
||||
let leader_id = state.leader_id;
|
||||
|
@ -2625,8 +2639,6 @@ impl Workspace {
|
|||
update: proto::UpdateFollowers,
|
||||
cx: &mut AsyncWindowContext,
|
||||
) -> Result<()> {
|
||||
dbg!("process_leader_update", &update);
|
||||
|
||||
match update.variant.ok_or_else(|| anyhow!("invalid update"))? {
|
||||
proto::update_followers::Variant::UpdateActiveView(update_active_view) => {
|
||||
this.update(cx, |this, _| {
|
||||
|
@ -3221,13 +3233,8 @@ impl Workspace {
|
|||
|
||||
fn actions(&self, div: Div, cx: &mut ViewContext<Self>) -> Div {
|
||||
self.add_workspace_actions_listeners(div, cx)
|
||||
// cx.add_async_action(Workspace::open);
|
||||
// cx.add_async_action(Workspace::follow_next_collaborator);
|
||||
// cx.add_async_action(Workspace::close);
|
||||
.on_action(cx.listener(Self::close_inactive_items_and_panes))
|
||||
.on_action(cx.listener(Self::close_all_items_and_panes))
|
||||
// cx.add_global_action(Workspace::close_global);
|
||||
// cx.add_global_action(restart);
|
||||
.on_action(cx.listener(Self::save_all))
|
||||
.on_action(cx.listener(Self::add_folder_to_project))
|
||||
.on_action(cx.listener(|workspace, _: &Unfollow, cx| {
|
||||
|
@ -3276,6 +3283,9 @@ impl Workspace {
|
|||
workspace.close_all_docks(cx);
|
||||
}),
|
||||
)
|
||||
.on_action(cx.listener(Workspace::open))
|
||||
.on_action(cx.listener(Workspace::close_window))
|
||||
|
||||
// cx.add_action(Workspace::activate_pane_at_index);
|
||||
// cx.add_action(|workspace: &mut Workspace, _: &ReopenClosedItem, cx| {
|
||||
// workspace.reopen_closed_item(cx).detach();
|
||||
|
@ -3880,8 +3890,6 @@ impl WorkspaceStore {
|
|||
let leader_id = envelope.original_sender_id()?;
|
||||
let update = envelope.payload;
|
||||
|
||||
dbg!("handle_upate_followers");
|
||||
|
||||
this.update(&mut cx, |this, cx| {
|
||||
for workspace in &this.workspaces {
|
||||
workspace.update(cx, |workspace, cx| {
|
||||
|
|
|
@ -55,7 +55,7 @@ outline = { package = "outline2", path = "../outline2" }
|
|||
project = { package = "project2", path = "../project2" }
|
||||
project_panel = { package = "project_panel2", path = "../project_panel2" }
|
||||
# project_symbols = { path = "../project_symbols" }
|
||||
# quick_action_bar = { path = "../quick_action_bar" }
|
||||
quick_action_bar = { package = "quick_action_bar2", path = "../quick_action_bar2" }
|
||||
# recent_projects = { path = "../recent_projects" }
|
||||
rope = { package = "rope2", path = "../rope2"}
|
||||
rpc = { package = "rpc2", path = "../rpc2" }
|
||||
|
|
|
@ -22,6 +22,7 @@ pub use open_listener::*;
|
|||
use anyhow::{anyhow, Context as _};
|
||||
use futures::{channel::mpsc, StreamExt};
|
||||
use project_panel::ProjectPanel;
|
||||
use quick_action_bar::QuickActionBar;
|
||||
use settings::{initial_local_settings_content, load_default_keymap, KeymapFile, Settings};
|
||||
use std::{borrow::Cow, ops::Deref, sync::Arc};
|
||||
use terminal_view::terminal_panel::TerminalPanel;
|
||||
|
@ -103,11 +104,10 @@ pub fn initialize_workspace(app_state: Arc<AppState>, cx: &mut AppContext) {
|
|||
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)
|
||||
// });
|
||||
// toolbar.add_item(quick_action_bar, cx);
|
||||
|
||||
let quick_action_bar = cx
|
||||
.build_view(|_| QuickActionBar::new(buffer_search_bar, workspace));
|
||||
toolbar.add_item(quick_action_bar, cx);
|
||||
let diagnostic_editor_controls =
|
||||
cx.build_view(|_| diagnostics::ToolbarControls::new());
|
||||
// toolbar.add_item(diagnostic_editor_controls, cx);
|
||||
|
@ -171,9 +171,7 @@ pub fn initialize_workspace(app_state: Arc<AppState>, cx: &mut AppContext) {
|
|||
cx.on_window_should_close(move |cx| {
|
||||
handle
|
||||
.update(cx, |workspace, cx| {
|
||||
if let Some(task) = workspace.close(&Default::default(), cx) {
|
||||
task.detach_and_log_err(cx);
|
||||
}
|
||||
workspace.close_window(&Default::default(), cx);
|
||||
false
|
||||
})
|
||||
.unwrap_or(true)
|
||||
|
|
Loading…
Reference in a new issue