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:
parent
977a1b7a82
commit
decdd3b6ac
13 changed files with 819 additions and 541 deletions
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue