Implement following and more collaboration panel features in zed2 (#3504)
This commit is contained in:
commit
5b6fd3ac1d
14 changed files with 189 additions and 304 deletions
|
@ -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,
|
||||
};
|
||||
|
||||
|
@ -6478,7 +6478,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;
|
||||
}
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ use theme::{ActiveTheme, Theme};
|
|||
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);
|
||||
|
@ -838,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,
|
||||
|
|
|
@ -147,18 +147,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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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())?;
|
||||
|
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2625,8 +2625,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, _| {
|
||||
|
@ -3880,8 +3878,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| {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue