Introduce following for assistant panel (#14479)

Release Notes:

- Added support for following into the assistant panel.

---------

Co-authored-by: Max <max@zed.dev>
Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
Co-authored-by: Nathan <nathan@zed.dev>
This commit is contained in:
Antonio Scandurra 2024-07-15 11:36:27 +02:00 committed by GitHub
parent 977a1b7a82
commit decdd3b6ac
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 819 additions and 541 deletions

View file

@ -1,6 +1,7 @@
use crate::persistence::model::DockData;
use crate::{status_bar::StatusItemView, Workspace};
use crate::{DraggedDock, Event};
use crate::{DraggedDock, Event, Pane};
use client::proto;
use gpui::{
deferred, div, px, Action, AnchorCorner, AnyView, AppContext, Axis, Entity, EntityId,
EventEmitter, FocusHandle, FocusableView, IntoElement, KeyContext, MouseButton, MouseDownEvent,
@ -23,6 +24,8 @@ pub enum PanelEvent {
Close,
}
pub use proto::PanelId;
pub trait Panel: FocusableView + EventEmitter<PanelEvent> {
fn persistent_name() -> &'static str;
fn position(&self, cx: &WindowContext) -> DockPosition;
@ -44,6 +47,12 @@ pub trait Panel: FocusableView + EventEmitter<PanelEvent> {
}
fn set_zoomed(&mut self, _zoomed: bool, _cx: &mut ViewContext<Self>) {}
fn set_active(&mut self, _active: bool, _cx: &mut ViewContext<Self>) {}
fn pane(&self) -> Option<View<Pane>> {
None
}
fn remote_id() -> Option<proto::PanelId> {
None
}
}
pub trait PanelHandle: Send + Sync {
@ -55,6 +64,8 @@ pub trait PanelHandle: Send + Sync {
fn is_zoomed(&self, cx: &WindowContext) -> bool;
fn set_zoomed(&self, zoomed: bool, cx: &mut WindowContext);
fn set_active(&self, active: bool, cx: &mut WindowContext);
fn remote_id(&self) -> Option<proto::PanelId>;
fn pane(&self, cx: &WindowContext) -> Option<View<Pane>>;
fn size(&self, cx: &WindowContext) -> Pixels;
fn set_size(&self, size: Option<Pixels>, cx: &mut WindowContext);
fn icon(&self, cx: &WindowContext) -> Option<ui::IconName>;
@ -101,6 +112,14 @@ where
self.update(cx, |this, cx| this.set_active(active, cx))
}
fn pane(&self, cx: &WindowContext) -> Option<View<Pane>> {
self.read(cx).pane()
}
fn remote_id(&self) -> Option<PanelId> {
T::remote_id()
}
fn size(&self, cx: &WindowContext) -> Pixels {
self.read(cx).size(cx)
}
@ -296,6 +315,12 @@ impl Dock {
.position(|entry| entry.panel.persistent_name() == ui_name)
}
pub fn panel_index_for_proto_id(&self, panel_id: PanelId) -> Option<usize> {
self.panel_entries
.iter()
.position(|entry| entry.panel.remote_id() == Some(panel_id))
}
pub fn active_panel_index(&self) -> usize {
self.active_panel_index
}

View file

@ -3,7 +3,7 @@ use crate::{
persistence::model::ItemId,
searchable::SearchableItemHandle,
workspace_settings::{AutosaveSetting, WorkspaceSettings},
DelayedDebouncedEditAction, FollowableItemBuilders, ItemNavHistory, ToolbarItemLocation,
DelayedDebouncedEditAction, FollowableViewRegistry, ItemNavHistory, ToolbarItemLocation,
ViewId, Workspace, WorkspaceId,
};
use anyhow::Result;
@ -472,22 +472,6 @@ impl<T: Item> ItemHandle for View<T> {
this.added_to_workspace(workspace, cx);
});
if let Some(followed_item) = self.to_followable_item_handle(cx) {
if let Some(message) = followed_item.to_state_proto(cx) {
workspace.update_followers(
followed_item.is_project_item(cx),
proto::update_followers::Variant::CreateView(proto::View {
id: followed_item
.remote_id(&workspace.client(), cx)
.map(|id| id.to_proto()),
variant: Some(message),
leader_id: workspace.leader_for_pane(&pane),
}),
cx,
);
}
}
if workspace
.panes_by_item
.insert(self.item_id(), pane.downgrade())
@ -548,11 +532,11 @@ impl<T: Item> ItemHandle for View<T> {
if let Some(item) = item.to_followable_item_handle(cx) {
let leader_id = workspace.leader_for_pane(&pane);
let follow_event = item.to_follow_event(event);
if leader_id.is_some()
&& matches!(follow_event, Some(FollowEvent::Unfollow))
{
workspace.unfollow(&pane, cx);
if let Some(leader_id) = leader_id {
if let Some(FollowEvent::Unfollow) = item.to_follow_event(event) {
workspace.unfollow(leader_id, cx);
}
}
if item.focus_handle(cx).contains_focused(cx) {
@ -682,9 +666,7 @@ impl<T: Item> ItemHandle for View<T> {
}
fn to_followable_item_handle(&self, cx: &AppContext) -> Option<Box<dyn FollowableItemHandle>> {
let builders = cx.try_global::<FollowableItemBuilders>()?;
let item = self.to_any();
Some(builders.get(&item.entity_type())?.1(&item))
FollowableViewRegistry::to_followable_view(self.clone(), cx)
}
fn on_release(
@ -769,11 +751,15 @@ pub enum FollowEvent {
Unfollow,
}
pub enum Dedup {
KeepExisting,
ReplaceExisting,
}
pub trait FollowableItem: Item {
fn remote_id(&self) -> Option<ViewId>;
fn to_state_proto(&self, cx: &WindowContext) -> Option<proto::view::Variant>;
fn from_state_proto(
pane: View<Pane>,
project: View<Workspace>,
id: ViewId,
state: &mut Option<proto::view::Variant>,
@ -794,6 +780,7 @@ pub trait FollowableItem: Item {
) -> 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>);
fn dedup(&self, existing: &Self, cx: &WindowContext) -> Option<Dedup>;
}
pub trait FollowableItemHandle: ItemHandle {
@ -815,6 +802,7 @@ pub trait FollowableItemHandle: ItemHandle {
cx: &mut WindowContext,
) -> Task<Result<()>>;
fn is_project_item(&self, cx: &WindowContext) -> bool;
fn dedup(&self, existing: &dyn FollowableItemHandle, cx: &WindowContext) -> Option<Dedup>;
}
impl<T: FollowableItem> FollowableItemHandle for View<T> {
@ -868,6 +856,11 @@ impl<T: FollowableItem> FollowableItemHandle for View<T> {
fn is_project_item(&self, cx: &WindowContext) -> bool {
self.read(cx).is_project_item(cx)
}
fn dedup(&self, existing: &dyn FollowableItemHandle, cx: &WindowContext) -> Option<Dedup> {
let existing = existing.to_any().downcast::<T>().ok()?;
self.read(cx).dedup(existing.read(cx), cx)
}
}
pub trait WeakFollowableItemHandle: Send + Sync {

View file

@ -1,6 +1,7 @@
use crate::{pane_group::element::pane_axis, AppState, FollowerState, Pane, Workspace};
use anyhow::{anyhow, Result};
use call::{ActiveCall, ParticipantLocation};
use client::proto::PeerId;
use collections::HashMap;
use gpui::{
point, size, AnyView, AnyWeakView, Axis, Bounds, IntoElement, Model, MouseButton, Pixels,
@ -95,7 +96,7 @@ impl PaneGroup {
pub(crate) fn render(
&self,
project: &Model<Project>,
follower_states: &HashMap<View<Pane>, FollowerState>,
follower_states: &HashMap<PeerId, FollowerState>,
active_call: Option<&Model<ActiveCall>>,
active_pane: &View<Pane>,
zoomed: Option<&AnyWeakView>,
@ -168,7 +169,7 @@ impl Member {
&self,
project: &Model<Project>,
basis: usize,
follower_states: &HashMap<View<Pane>, FollowerState>,
follower_states: &HashMap<PeerId, FollowerState>,
active_call: Option<&Model<ActiveCall>>,
active_pane: &View<Pane>,
zoomed: Option<&AnyWeakView>,
@ -181,19 +182,29 @@ impl Member {
return div().into_any();
}
let follower_state = follower_states.get(pane);
let leader = follower_state.and_then(|state| {
let room = active_call?.read(cx).room()?.read(cx);
room.remote_participant_for_peer_id(state.leader_id)
let follower_state = follower_states.iter().find_map(|(leader_id, state)| {
if state.center_pane == *pane {
Some((*leader_id, state))
} else {
None
}
});
let is_in_unshared_view = follower_state.map_or(false, |state| {
let leader = follower_state.as_ref().and_then(|(leader_id, _)| {
let room = active_call?.read(cx).room()?.read(cx);
room.remote_participant_for_peer_id(*leader_id)
});
let is_in_unshared_view = follower_state.as_ref().map_or(false, |(_, state)| {
state.active_view_id.is_some_and(|view_id| {
!state.items_by_leader_view_id.contains_key(&view_id)
})
});
let is_in_panel = follower_state
.as_ref()
.map_or(false, |(_, state)| state.dock_pane.is_some());
let mut leader_border = None;
let mut leader_status_box = None;
let mut leader_join_data = None;
@ -203,7 +214,11 @@ impl Member {
.players()
.color_for_participant(leader.participant_index.0)
.cursor;
leader_color.fade_out(0.3);
if is_in_panel {
leader_color.fade_out(0.75);
} else {
leader_color.fade_out(0.3);
}
leader_border = Some(leader_color);
leader_status_box = match leader.location {
@ -483,7 +498,7 @@ impl PaneAxis {
&self,
project: &Model<Project>,
basis: usize,
follower_states: &HashMap<View<Pane>, FollowerState>,
follower_states: &HashMap<PeerId, FollowerState>,
active_call: Option<&Model<ActiveCall>>,
active_pane: &View<Pane>,
zoomed: Option<&AnyWeakView>,

File diff suppressed because it is too large Load diff