From dc158f708f2334205feee39c8ccb7c57edd573c1 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Fri, 12 Jan 2024 10:23:22 -0700 Subject: [PATCH 01/12] Update chat panel with current channel --- crates/call/src/call.rs | 2 ++ crates/call/src/room.rs | 3 +++ crates/collab_ui/src/chat_panel.rs | 14 +++++++++++++- 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/crates/call/src/call.rs b/crates/call/src/call.rs index 87d2e9aa78..a37cc3bc78 100644 --- a/crates/call/src/call.rs +++ b/crates/call/src/call.rs @@ -442,6 +442,8 @@ impl ActiveCall { .location .as_ref() .and_then(|location| location.upgrade()); + let channel_id = room.update(cx, |room, cx| room.channel_id()); + cx.emit(Event::RoomJoined { channel_id }); room.update(cx, |room, cx| room.set_location(location.as_ref(), cx)) } } else { diff --git a/crates/call/src/room.rs b/crates/call/src/room.rs index 45c6c15fb0..6dae1e6a4d 100644 --- a/crates/call/src/room.rs +++ b/crates/call/src/room.rs @@ -26,6 +26,9 @@ pub const RECONNECT_TIMEOUT: Duration = Duration::from_secs(30); #[derive(Clone, Debug, PartialEq, Eq)] pub enum Event { + RoomJoined { + channel_id: Option, + }, ParticipantLocationChanged { participant_id: proto::PeerId, }, diff --git a/crates/collab_ui/src/chat_panel.rs b/crates/collab_ui/src/chat_panel.rs index 5786ab10d4..bbe0a6b4fe 100644 --- a/crates/collab_ui/src/chat_panel.rs +++ b/crates/collab_ui/src/chat_panel.rs @@ -1,6 +1,6 @@ use crate::{channel_view::ChannelView, is_channels_feature_enabled, ChatPanelSettings}; use anyhow::Result; -use call::ActiveCall; +use call::{room, ActiveCall}; use channel::{ChannelChat, ChannelChatEvent, ChannelMessageId, ChannelStore}; use client::Client; use collections::HashMap; @@ -140,6 +140,18 @@ impl ChatPanel { cx.notify(); }, )); + this.subscriptions.push(cx.subscribe( + &ActiveCall::global(cx), + move |this: &mut Self, _, event: &room::Event, cx| match event { + room::Event::RoomJoined { channel_id } => { + if let Some(channel_id) = channel_id { + this.select_channel(*channel_id, None, cx) + .detach_and_log_err(cx) + } + } + _ => {} + }, + )); this }) From f0d490c671400c75cfb87a267729610bf6998a3e Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Fri, 12 Jan 2024 10:34:24 -0700 Subject: [PATCH 02/12] Open chat panel for guests --- crates/call/src/call.rs | 6 ++++-- crates/call/src/room.rs | 1 + crates/collab_ui/src/chat_panel.rs | 9 +++++++-- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/crates/call/src/call.rs b/crates/call/src/call.rs index a37cc3bc78..72bb6a4e6e 100644 --- a/crates/call/src/call.rs +++ b/crates/call/src/call.rs @@ -442,8 +442,10 @@ impl ActiveCall { .location .as_ref() .and_then(|location| location.upgrade()); - let channel_id = room.update(cx, |room, cx| room.channel_id()); - cx.emit(Event::RoomJoined { channel_id }); + let (channel_id, role) = room.update(cx, |room, _| { + (room.channel_id(), room.local_participant().role) + }); + cx.emit(Event::RoomJoined { channel_id, role }); room.update(cx, |room, cx| room.set_location(location.as_ref(), cx)) } } else { diff --git a/crates/call/src/room.rs b/crates/call/src/room.rs index 6dae1e6a4d..23a6ea4a1c 100644 --- a/crates/call/src/room.rs +++ b/crates/call/src/room.rs @@ -28,6 +28,7 @@ pub const RECONNECT_TIMEOUT: Duration = Duration::from_secs(30); pub enum Event { RoomJoined { channel_id: Option, + role: proto::ChannelRole, }, ParticipantLocationChanged { participant_id: proto::PeerId, diff --git a/crates/collab_ui/src/chat_panel.rs b/crates/collab_ui/src/chat_panel.rs index bbe0a6b4fe..a170f95697 100644 --- a/crates/collab_ui/src/chat_panel.rs +++ b/crates/collab_ui/src/chat_panel.rs @@ -16,6 +16,7 @@ use menu::Confirm; use message_editor::MessageEditor; use project::Fs; use rich_text::RichText; +use rpc::proto; use serde::{Deserialize, Serialize}; use settings::{Settings, SettingsStore}; use std::sync::Arc; @@ -143,10 +144,14 @@ impl ChatPanel { this.subscriptions.push(cx.subscribe( &ActiveCall::global(cx), move |this: &mut Self, _, event: &room::Event, cx| match event { - room::Event::RoomJoined { channel_id } => { + room::Event::RoomJoined { channel_id, role } => { if let Some(channel_id) = channel_id { this.select_channel(*channel_id, None, cx) - .detach_and_log_err(cx) + .detach_and_log_err(cx); + + if *role == proto::ChannelRole::Guest { + cx.emit(PanelEvent::Activate) + } } } _ => {} From 50f3bbbc8b38bec8e687db8bcebd1667454450c0 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Fri, 12 Jan 2024 15:08:14 -0700 Subject: [PATCH 03/12] Open chat when joining a channel with guests (and close it when leaving a channel again) --- crates/call/src/call.rs | 6 ++---- crates/call/src/room.rs | 17 ++++++++++++--- crates/collab_ui/src/chat_panel.rs | 21 ++++++++++++++++--- .../project_shared_notification.rs | 2 +- 4 files changed, 35 insertions(+), 11 deletions(-) diff --git a/crates/call/src/call.rs b/crates/call/src/call.rs index 72bb6a4e6e..6d57a42ff7 100644 --- a/crates/call/src/call.rs +++ b/crates/call/src/call.rs @@ -442,10 +442,8 @@ impl ActiveCall { .location .as_ref() .and_then(|location| location.upgrade()); - let (channel_id, role) = room.update(cx, |room, _| { - (room.channel_id(), room.local_participant().role) - }); - cx.emit(Event::RoomJoined { channel_id, role }); + let channel_id = room.read(cx).channel_id(); + cx.emit(Event::RoomJoined { channel_id }); room.update(cx, |room, cx| room.set_location(location.as_ref(), cx)) } } else { diff --git a/crates/call/src/room.rs b/crates/call/src/room.rs index 23a6ea4a1c..e17a3667ba 100644 --- a/crates/call/src/room.rs +++ b/crates/call/src/room.rs @@ -28,7 +28,6 @@ pub const RECONNECT_TIMEOUT: Duration = Duration::from_secs(30); pub enum Event { RoomJoined { channel_id: Option, - role: proto::ChannelRole, }, ParticipantLocationChanged { participant_id: proto::PeerId, @@ -53,7 +52,9 @@ pub enum Event { RemoteProjectInvitationDiscarded { project_id: u64, }, - Left, + Left { + channel_id: Option, + }, } pub struct Room { @@ -361,7 +362,9 @@ impl Room { pub(crate) fn leave(&mut self, cx: &mut ModelContext) -> Task> { cx.notify(); - cx.emit(Event::Left); + cx.emit(Event::Left { + channel_id: self.channel_id(), + }); self.leave_internal(cx) } @@ -602,6 +605,14 @@ impl Room { .map(|participant| participant.role) } + pub fn contains_guests(&self) -> bool { + self.local_participant.role == proto::ChannelRole::Guest + || self + .remote_participants + .values() + .any(|p| p.role == proto::ChannelRole::Guest) + } + pub fn local_participant_is_admin(&self) -> bool { self.local_participant.role == proto::ChannelRole::Admin } diff --git a/crates/collab_ui/src/chat_panel.rs b/crates/collab_ui/src/chat_panel.rs index a170f95697..f8ee12ef81 100644 --- a/crates/collab_ui/src/chat_panel.rs +++ b/crates/collab_ui/src/chat_panel.rs @@ -143,17 +143,26 @@ impl ChatPanel { )); this.subscriptions.push(cx.subscribe( &ActiveCall::global(cx), - move |this: &mut Self, _, event: &room::Event, cx| match event { - room::Event::RoomJoined { channel_id, role } => { + move |this: &mut Self, call, event: &room::Event, cx| match event { + room::Event::RoomJoined { channel_id } => { if let Some(channel_id) = channel_id { this.select_channel(*channel_id, None, cx) .detach_and_log_err(cx); - if *role == proto::ChannelRole::Guest { + if call + .read(cx) + .room() + .is_some_and(|room| room.read(cx).contains_guests()) + { cx.emit(PanelEvent::Activate) } } } + room::Event::Left { channel_id } => { + if channel_id == &this.channel_id(cx) { + cx.emit(PanelEvent::Close) + } + } _ => {} }, )); @@ -162,6 +171,12 @@ impl ChatPanel { }) } + pub fn channel_id(&self, cx: &AppContext) -> Option { + self.active_chat + .as_ref() + .map(|(chat, _)| chat.read(cx).channel_id) + } + pub fn is_scrolled_to_bottom(&self) -> bool { self.is_scrolled_to_bottom } diff --git a/crates/collab_ui/src/notifications/project_shared_notification.rs b/crates/collab_ui/src/notifications/project_shared_notification.rs index 88fe540c39..b8ceefcd76 100644 --- a/crates/collab_ui/src/notifications/project_shared_notification.rs +++ b/crates/collab_ui/src/notifications/project_shared_notification.rs @@ -58,7 +58,7 @@ pub fn init(app_state: &Arc, cx: &mut AppContext) { } } - room::Event::Left => { + room::Event::Left { .. } => { for (_, windows) in notification_windows.drain() { for window in windows { window From eafe0944e0b4b8c0a23f6fef9f90e6b56fd06710 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Fri, 12 Jan 2024 16:01:51 -0700 Subject: [PATCH 04/12] Some tweaks for chat panels --- crates/collab_ui/src/chat_panel.rs | 189 +++++++++++------------------ 1 file changed, 69 insertions(+), 120 deletions(-) diff --git a/crates/collab_ui/src/chat_panel.rs b/crates/collab_ui/src/chat_panel.rs index f8ee12ef81..ecd7d1a739 100644 --- a/crates/collab_ui/src/chat_panel.rs +++ b/crates/collab_ui/src/chat_panel.rs @@ -1,4 +1,4 @@ -use crate::{channel_view::ChannelView, is_channels_feature_enabled, ChatPanelSettings}; +use crate::{collab_panel, is_channels_feature_enabled, ChatPanelSettings, CollabPanel}; use anyhow::Result; use call::{room, ActiveCall}; use channel::{ChannelChat, ChannelChatEvent, ChannelMessageId, ChannelStore}; @@ -7,9 +7,9 @@ use collections::HashMap; use db::kvp::KEY_VALUE_STORE; use editor::Editor; use gpui::{ - actions, div, list, prelude::*, px, AnyElement, AppContext, AsyncWindowContext, ClickEvent, - ElementId, EventEmitter, FocusableView, ListOffset, ListScrollEvent, ListState, Model, Render, - Subscription, Task, View, ViewContext, VisualContext, WeakView, + actions, div, list, prelude::*, px, Action, AnyElement, AppContext, AsyncWindowContext, + ClickEvent, ElementId, EventEmitter, FocusHandle, FocusableView, ListOffset, ListScrollEvent, + ListState, Model, Render, Subscription, Task, View, ViewContext, VisualContext, WeakView, }; use language::LanguageRegistry; use menu::Confirm; @@ -21,7 +21,9 @@ use serde::{Deserialize, Serialize}; use settings::{Settings, SettingsStore}; use std::sync::Arc; use time::{OffsetDateTime, UtcOffset}; -use ui::{prelude::*, Avatar, Button, IconButton, IconName, Label, TabBar, Tooltip}; +use ui::{ + prelude::*, Avatar, Button, IconButton, IconName, Key, KeyBinding, Label, TabBar, Tooltip, +}; use util::{ResultExt, TryFutureExt}; use workspace::{ dock::{DockPosition, Panel, PanelEvent}, @@ -55,7 +57,6 @@ pub struct ChatPanel { active: bool, pending_serialization: Task>, subscriptions: Vec, - workspace: WeakView, is_scrolled_to_bottom: bool, markdown_data: HashMap, } @@ -90,8 +91,6 @@ impl ChatPanel { ) }); - let workspace_handle = workspace.weak_handle(); - cx.new_view(|cx: &mut ViewContext| { let view = cx.view().downgrade(); let message_list = @@ -123,7 +122,6 @@ impl ChatPanel { message_editor: input_editor, local_timezone: cx.local_timezone(), subscriptions: Vec::new(), - workspace: workspace_handle, is_scrolled_to_bottom: true, active: false, width: None, @@ -291,50 +289,6 @@ impl ChatPanel { } } - fn render_channel(&self, cx: &mut ViewContext) -> AnyElement { - v_stack() - .full() - .on_action(cx.listener(Self::send)) - .child( - h_stack().z_index(1).child( - TabBar::new("chat_header") - .child( - h_stack() - .w_full() - .h(rems(ui::Tab::HEIGHT_IN_REMS)) - .px_2() - .child(Label::new( - self.active_chat - .as_ref() - .and_then(|c| { - Some(format!("#{}", c.0.read(cx).channel(cx)?.name)) - }) - .unwrap_or_default(), - )), - ) - .end_child( - IconButton::new("notes", IconName::File) - .on_click(cx.listener(Self::open_notes)) - .tooltip(|cx| Tooltip::text("Open notes", cx)), - ) - .end_child( - IconButton::new("call", IconName::AudioOn) - .on_click(cx.listener(Self::join_call)) - .tooltip(|cx| Tooltip::text("Join call", cx)), - ), - ), - ) - .child(div().flex_grow().px_2().py_1().map(|this| { - if self.active_chat.is_some() { - this.child(list(self.message_list.clone()).full()) - } else { - this - } - })) - .child(h_stack().p_2().child(self.message_editor.clone())) - .into_any() - } - fn render_message(&mut self, ix: usize, cx: &mut ViewContext) -> impl IntoElement { let active_chat = &self.active_chat.as_ref().unwrap().0; let (message, is_continuation_from_previous, is_continuation_to_next, is_admin) = @@ -453,44 +407,6 @@ impl ChatPanel { rich_text::render_markdown(message.body.clone(), &mentions, language_registry, None) } - fn render_sign_in_prompt(&self, cx: &mut ViewContext) -> impl IntoElement { - v_stack() - .gap_2() - .p_4() - .child( - Button::new("sign-in", "Sign in") - .style(ButtonStyle::Filled) - .icon_color(Color::Muted) - .icon(IconName::Github) - .icon_position(IconPosition::Start) - .full_width() - .on_click(cx.listener(move |this, _, cx| { - let client = this.client.clone(); - cx.spawn(|this, mut cx| async move { - if client - .authenticate_and_connect(true, &cx) - .log_err() - .await - .is_some() - { - this.update(&mut cx, |_, cx| { - cx.focus_self(); - }) - .ok(); - } - }) - .detach(); - })), - ) - .child( - div().flex().w_full().items_center().child( - Label::new("Sign in to chat.") - .color(Color::Muted) - .size(LabelSize::Small), - ), - ) - } - fn send(&mut self, _: &Confirm, cx: &mut ViewContext) { if let Some((chat, _)) = self.active_chat.as_ref() { let message = self @@ -567,24 +483,6 @@ impl ChatPanel { Ok(()) }) } - - fn open_notes(&mut self, _: &ClickEvent, cx: &mut ViewContext) { - if let Some((chat, _)) = &self.active_chat { - let channel_id = chat.read(cx).channel_id; - if let Some(workspace) = self.workspace.upgrade() { - ChannelView::open(channel_id, workspace, cx).detach(); - } - } - } - - fn join_call(&mut self, _: &ClickEvent, cx: &mut ViewContext) { - if let Some((chat, _)) = &self.active_chat { - let channel_id = chat.read(cx).channel_id; - ActiveCall::global(cx) - .update(cx, |call, cx| call.join_channel(channel_id, cx)) - .detach_and_log_err(cx); - } - } } impl EventEmitter for ChatPanel {} @@ -592,19 +490,70 @@ impl EventEmitter for ChatPanel {} impl Render for ChatPanel { fn render(&mut self, cx: &mut ViewContext) -> impl IntoElement { v_stack() - .size_full() - .map(|this| match (self.client.user_id(), self.active_chat()) { - (Some(_), Some(_)) => this.child(self.render_channel(cx)), - (Some(_), None) => this.child( - div().p_4().child( - Label::new("Select a channel to chat in.") - .size(LabelSize::Small) - .color(Color::Muted), + .full() + .on_action(cx.listener(Self::send)) + .child( + h_stack().z_index(1).child( + TabBar::new("chat_header").child( + h_stack() + .w_full() + .h(rems(ui::Tab::HEIGHT_IN_REMS)) + .px_2() + .child(Label::new( + self.active_chat + .as_ref() + .and_then(|c| { + Some(format!("#{}", c.0.read(cx).channel(cx)?.name)) + }) + .unwrap_or("Chat".to_string()), + )), ), ), - (None, _) => this.child(self.render_sign_in_prompt(cx)), - }) - .min_w(px(150.)) + ) + .child(div().flex_grow().px_2().py_1().map(|this| { + if self.active_chat.is_some() { + this.child(list(self.message_list.clone()).full()) + } else { + this.child( + div() + .p_4() + .child( + Label::new("Select a channel to chat in.") + .size(LabelSize::Small) + .color(Color::Muted), + ) + .child( + div().pt_1().w_full().items_center().child( + Button::new("toggle-collab", "Open") + .full_width() + .key_binding(KeyBinding::for_action( + &collab_panel::ToggleFocus, + cx, + )) + .on_click(|_, cx| { + cx.dispatch_action( + collab_panel::ToggleFocus.boxed_clone(), + ) + }), + ), + ), + ) + } + })) + .child(h_stack().p_2().map(|el| { + if self.active_chat.is_some() { + el.child(self.message_editor.clone()) + } else { + el.child( + div() + .rounded_md() + .h_7() + .w_full() + .bg(cx.theme().colors().editor_background), + ) + } + })) + .into_any() } } From c2ff9fe2da012be54a78b3d335bb3983aeb424d2 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Sat, 13 Jan 2024 14:32:24 -0700 Subject: [PATCH 05/12] Don't lose focus on default panel state --- crates/collab_ui/src/chat_panel.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/crates/collab_ui/src/chat_panel.rs b/crates/collab_ui/src/chat_panel.rs index ecd7d1a739..20c5857ad2 100644 --- a/crates/collab_ui/src/chat_panel.rs +++ b/crates/collab_ui/src/chat_panel.rs @@ -59,6 +59,7 @@ pub struct ChatPanel { subscriptions: Vec, is_scrolled_to_bottom: bool, markdown_data: HashMap, + focus_handle: FocusHandle, } #[derive(Serialize, Deserialize)] @@ -126,6 +127,7 @@ impl ChatPanel { active: false, width: None, markdown_data: Default::default(), + focus_handle: cx.focus_handle(), }; let mut old_dock_position = this.position(cx); @@ -490,6 +492,7 @@ impl EventEmitter for ChatPanel {} impl Render for ChatPanel { fn render(&mut self, cx: &mut ViewContext) -> impl IntoElement { v_stack() + .track_focus(&self.focus_handle) .full() .on_action(cx.listener(Self::send)) .child( @@ -559,7 +562,11 @@ impl Render for ChatPanel { impl FocusableView for ChatPanel { fn focus_handle(&self, cx: &AppContext) -> gpui::FocusHandle { - self.message_editor.read(cx).focus_handle(cx) + if self.active_chat.is_some() { + self.message_editor.read(cx).focus_handle(cx) + } else { + self.focus_handle.clone() + } } } From f6ef07e7166eadbb4e65de941e4c5422162319d5 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Sat, 13 Jan 2024 21:37:13 -0700 Subject: [PATCH 06/12] Make chat prettier (to my eyes at least) --- crates/channel/src/channel_chat.rs | 4 +- crates/collab_ui/src/chat_panel.rs | 106 ++++++++++++++++++++++------- crates/gpui/src/styled.rs | 11 ++- crates/ui/src/components/avatar.rs | 10 ++- 4 files changed, 101 insertions(+), 30 deletions(-) diff --git a/crates/channel/src/channel_chat.rs b/crates/channel/src/channel_chat.rs index d2250972f3..e9353a1441 100644 --- a/crates/channel/src/channel_chat.rs +++ b/crates/channel/src/channel_chat.rs @@ -144,7 +144,7 @@ impl ChannelChat { message: MessageParams, cx: &mut ModelContext, ) -> Result>> { - if message.text.is_empty() { + if message.text.trim().is_empty() { Err(anyhow!("message body can't be empty"))?; } @@ -174,6 +174,8 @@ impl ChannelChat { let user_store = self.user_store.clone(); let rpc = self.rpc.clone(); let outgoing_messages_lock = self.outgoing_messages_lock.clone(); + + // todo - handle messages that fail to send (e.g. >1024 chars) Ok(cx.spawn(move |this, mut cx| async move { let outgoing_message_guard = outgoing_messages_lock.lock().await; let request = rpc.request(proto::SendChannelMessage { diff --git a/crates/collab_ui/src/chat_panel.rs b/crates/collab_ui/src/chat_panel.rs index 20c5857ad2..dd3d741522 100644 --- a/crates/collab_ui/src/chat_panel.rs +++ b/crates/collab_ui/src/chat_panel.rs @@ -8,8 +8,9 @@ use db::kvp::KEY_VALUE_STORE; use editor::Editor; use gpui::{ actions, div, list, prelude::*, px, Action, AnyElement, AppContext, AsyncWindowContext, - ClickEvent, ElementId, EventEmitter, FocusHandle, FocusableView, ListOffset, ListScrollEvent, - ListState, Model, Render, Subscription, Task, View, ViewContext, VisualContext, WeakView, + ClickEvent, DismissEvent, ElementId, EventEmitter, FocusHandle, FocusableView, FontWeight, + ListOffset, ListScrollEvent, ListState, Model, Render, Subscription, Task, View, ViewContext, + VisualContext, WeakView, }; use language::LanguageRegistry; use menu::Confirm; @@ -22,7 +23,8 @@ use settings::{Settings, SettingsStore}; use std::sync::Arc; use time::{OffsetDateTime, UtcOffset}; use ui::{ - prelude::*, Avatar, Button, IconButton, IconName, Key, KeyBinding, Label, TabBar, Tooltip, + popover_menu, prelude::*, Avatar, Button, ContextMenu, IconButton, IconName, Key, KeyBinding, + Label, TabBar, Tooltip, }; use util::{ResultExt, TryFutureExt}; use workspace::{ @@ -60,6 +62,7 @@ pub struct ChatPanel { is_scrolled_to_bottom: bool, markdown_data: HashMap, focus_handle: FocusHandle, + open_context_menu: Option<(u64, Subscription)>, } #[derive(Serialize, Deserialize)] @@ -128,6 +131,7 @@ impl ChatPanel { width: None, markdown_data: Default::default(), focus_handle: cx.focus_handle(), + open_context_menu: None, }; let mut old_dock_position = this.position(cx); @@ -348,50 +352,100 @@ impl ChatPanel { ChannelMessageId::Saved(id) => ("saved-message", id).into(), ChannelMessageId::Pending(id) => ("pending-message", id).into(), }; + let this = cx.view().clone(); v_stack() .w_full() - .id(element_id) .relative() .overflow_hidden() - .group("") .when(!is_continuation_from_previous, |this| { - this.child( + this.pt_3().child( h_stack() - .gap_2() - .child(Avatar::new(message.sender.avatar_uri.clone())) - .child(Label::new(message.sender.github_login.clone())) + .child( + div().absolute().child( + Avatar::new(message.sender.avatar_uri.clone()) + .size(cx.rem_size() * 1.5), + ), + ) + .child( + div() + .pl(cx.rem_size() * 1.5 + px(6.0)) + .pr(px(8.0)) + .font_weight(FontWeight::BOLD) + .child(Label::new(message.sender.github_login.clone())), + ) .child( Label::new(format_timestamp( message.timestamp, now, self.local_timezone, )) + .size(LabelSize::Small) .color(Color::Muted), ), ) }) - .when(!is_continuation_to_next, |this| - // HACK: This should really be a margin, but margins seem to get collapsed. - this.pb_2()) - .child(text.element("body".into(), cx)) + .when(is_continuation_from_previous, |this| this.pt_1()) .child( - div() - .absolute() - .top_1() - .right_2() - .w_8() - .visible_on_hover("") - .children(message_id_to_remove.map(|message_id| { - IconButton::new(("remove", message_id), IconName::XCircle).on_click( - cx.listener(move |this, _, cx| { - this.remove_message(message_id, cx); - }), - ) - })), + v_stack() + .w_full() + .text_ui_sm() + .id(element_id) + .group("") + .child(text.element("body".into(), cx)) + .child( + div() + .absolute() + .z_index(1) + .right_0() + .w_6() + .bg(cx.theme().colors().panel_background) + .when(!self.has_open_menu(message_id_to_remove), |el| { + el.visible_on_hover("") + }) + .children(message_id_to_remove.map(|message_id| { + popover_menu(("menu", message_id)) + .trigger(IconButton::new( + ("trigger", message_id), + IconName::Ellipsis, + )) + .menu(move |cx| { + Some(Self::render_message_menu(&this, message_id, cx)) + }) + })), + ), ) } + fn has_open_menu(&self, message_id: Option) -> bool { + match self.open_context_menu.as_ref() { + Some((id, _)) => Some(*id) == message_id, + None => false, + } + } + + fn render_message_menu( + this: &View, + message_id: u64, + cx: &mut WindowContext, + ) -> View { + let menu = { + let this = this.clone(); + ContextMenu::build(cx, move |menu, _| { + menu.entry("Delete message", None, move |cx| { + this.update(cx, |this, cx| this.remove_message(message_id, cx)) + }) + }) + }; + this.update(cx, |this, cx| { + let subscription = cx.subscribe(&menu, |this: &mut Self, _, _: &DismissEvent, _| { + this.open_context_menu = None; + }); + this.open_context_menu = Some((message_id, subscription)); + }); + menu + } + fn render_markdown_with_mentions( language_registry: &Arc, current_user_id: u64, diff --git a/crates/gpui/src/styled.rs b/crates/gpui/src/styled.rs index 2749c31a78..0eba1771f5 100644 --- a/crates/gpui/src/styled.rs +++ b/crates/gpui/src/styled.rs @@ -1,7 +1,7 @@ use crate::{ self as gpui, hsla, point, px, relative, rems, AbsoluteLength, AlignItems, CursorStyle, - DefiniteLength, Display, Fill, FlexDirection, Hsla, JustifyContent, Length, Position, - SharedString, StyleRefinement, Visibility, WhiteSpace, + DefiniteLength, Display, Fill, FlexDirection, FontWeight, Hsla, JustifyContent, Length, + Position, SharedString, StyleRefinement, Visibility, WhiteSpace, }; use crate::{BoxShadow, TextStyleRefinement}; use smallvec::{smallvec, SmallVec}; @@ -494,6 +494,13 @@ pub trait Styled: Sized { self } + fn font_weight(mut self, weight: FontWeight) -> Self { + self.text_style() + .get_or_insert_with(Default::default) + .font_weight = Some(weight); + self + } + fn text_bg(mut self, bg: impl Into) -> Self { self.text_style() .get_or_insert_with(Default::default) diff --git a/crates/ui/src/components/avatar.rs b/crates/ui/src/components/avatar.rs index 9e64e1223c..a97adb73b7 100644 --- a/crates/ui/src/components/avatar.rs +++ b/crates/ui/src/components/avatar.rs @@ -26,6 +26,7 @@ pub enum AvatarShape { #[derive(IntoElement)] pub struct Avatar { image: Img, + size: Option, border_color: Option, is_available: Option, } @@ -36,7 +37,7 @@ impl RenderOnce for Avatar { self = self.shape(AvatarShape::Circle); } - let size = cx.rem_size(); + let size = self.size.unwrap_or_else(|| cx.rem_size()); div() .size(size + px(2.)) @@ -78,6 +79,7 @@ impl Avatar { image: img(src), is_available: None, border_color: None, + size: None, } } @@ -124,4 +126,10 @@ impl Avatar { self.is_available = is_available.into(); self } + + /// Size overrides the avatar size. By default they are 1rem. + pub fn size(mut self, size: impl Into>) -> Self { + self.size = size.into(); + self + } } From c810af40d3a63ef7c82696101c6023e66b6e1185 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Sat, 13 Jan 2024 21:53:22 -0700 Subject: [PATCH 07/12] Fix multiple mentions in one message --- crates/collab/src/db/queries/messages.rs | 1 + crates/rich_text/src/rich_text.rs | 28 +++++++++++++----------- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/crates/collab/src/db/queries/messages.rs b/crates/collab/src/db/queries/messages.rs index 47bb27df39..96942c052d 100644 --- a/crates/collab/src/db/queries/messages.rs +++ b/crates/collab/src/db/queries/messages.rs @@ -256,6 +256,7 @@ impl Database { message_id = result.last_insert_id; let mentioned_user_ids = mentions.iter().map(|m| m.user_id).collect::>(); + let mentions = mentions .iter() .filter_map(|mention| { diff --git a/crates/rich_text/src/rich_text.rs b/crates/rich_text/src/rich_text.rs index b4a87b1e5d..ac3414555a 100644 --- a/crates/rich_text/src/rich_text.rs +++ b/crates/rich_text/src/rich_text.rs @@ -39,6 +39,7 @@ pub struct RichText { /// Allows one to specify extra links to the rendered markdown, which can be used /// for e.g. mentions. +#[derive(Debug)] pub struct Mention { pub range: Range, pub is_self_mention: bool, @@ -138,20 +139,21 @@ pub fn render_markdown_mut( if let Some(language) = ¤t_language { render_code(text, highlights, t.as_ref(), language); } else { - if let Some(mention) = mentions.first() { - if source_range.contains_inclusive(&mention.range) { - mentions = &mentions[1..]; - let range = (prev_len + mention.range.start - source_range.start) - ..(prev_len + mention.range.end - source_range.start); - highlights.push(( - range.clone(), - if mention.is_self_mention { - Highlight::SelfMention - } else { - Highlight::Mention - }, - )); + while let Some(mention) = mentions.first() { + if !source_range.contains_inclusive(&mention.range) { + break; } + mentions = &mentions[1..]; + let range = (prev_len + mention.range.start - source_range.start) + ..(prev_len + mention.range.end - source_range.start); + highlights.push(( + range.clone(), + if mention.is_self_mention { + Highlight::SelfMention + } else { + Highlight::Mention + }, + )); } text.push_str(t.as_ref()); From 818cbb24157334adac4a448c237e0ab35019e41c Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Sat, 13 Jan 2024 22:19:21 -0700 Subject: [PATCH 08/12] Show a border when scrolled in chat --- crates/collab_ui/src/chat_panel.rs | 37 ++++++++++++++++++------------ crates/gpui/src/elements/list.rs | 2 ++ 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/crates/collab_ui/src/chat_panel.rs b/crates/collab_ui/src/chat_panel.rs index dd3d741522..ee0241eb12 100644 --- a/crates/collab_ui/src/chat_panel.rs +++ b/crates/collab_ui/src/chat_panel.rs @@ -112,7 +112,7 @@ impl ChatPanel { if event.visible_range.start < MESSAGE_LOADING_THRESHOLD { this.load_more_messages(cx); } - this.is_scrolled_to_bottom = event.visible_range.end == event.count; + this.is_scrolled_to_bottom = !event.is_scrolled; })); let mut this = Self { @@ -567,7 +567,7 @@ impl Render for ChatPanel { ), ), ) - .child(div().flex_grow().px_2().py_1().map(|this| { + .child(div().flex_grow().px_2().pt_1().map(|this| { if self.active_chat.is_some() { this.child(list(self.message_list.clone()).full()) } else { @@ -597,19 +597,26 @@ impl Render for ChatPanel { ) } })) - .child(h_stack().p_2().map(|el| { - if self.active_chat.is_some() { - el.child(self.message_editor.clone()) - } else { - el.child( - div() - .rounded_md() - .h_7() - .w_full() - .bg(cx.theme().colors().editor_background), - ) - } - })) + .child( + h_stack() + .when(!self.is_scrolled_to_bottom, |el| { + el.border_t_1().border_color(cx.theme().colors().border) + }) + .p_2() + .map(|el| { + if self.active_chat.is_some() { + el.child(self.message_editor.clone()) + } else { + el.child( + div() + .rounded_md() + .h_7() + .w_full() + .bg(cx.theme().colors().editor_background), + ) + } + }), + ) .into_any() } } diff --git a/crates/gpui/src/elements/list.rs b/crates/gpui/src/elements/list.rs index 2a47a16741..50e7af5138 100644 --- a/crates/gpui/src/elements/list.rs +++ b/crates/gpui/src/elements/list.rs @@ -43,6 +43,7 @@ pub enum ListAlignment { pub struct ListScrollEvent { pub visible_range: Range, pub count: usize, + pub is_scrolled: bool, } #[derive(Clone)] @@ -253,6 +254,7 @@ impl StateInner { &ListScrollEvent { visible_range, count: self.items.summary().count, + is_scrolled: self.logical_scroll_top.is_some(), }, cx, ); From fee369bca1571fd00fd9c7b59f8fbc36bb96e6e0 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Sat, 13 Jan 2024 22:26:25 -0700 Subject: [PATCH 09/12] Fix re-docking chat panel --- crates/collab_ui/src/chat_panel.rs | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/crates/collab_ui/src/chat_panel.rs b/crates/collab_ui/src/chat_panel.rs index ee0241eb12..13b217eec4 100644 --- a/crates/collab_ui/src/chat_panel.rs +++ b/crates/collab_ui/src/chat_panel.rs @@ -70,13 +70,6 @@ struct SerializedChatPanel { width: Option, } -#[derive(Debug)] -pub enum Event { - DockPositionChanged, - Focus, - Dismissed, -} - actions!(chat_panel, [ToggleFocus]); impl ChatPanel { @@ -140,7 +133,7 @@ impl ChatPanel { let new_dock_position = this.position(cx); if new_dock_position != old_dock_position { old_dock_position = new_dock_position; - cx.emit(Event::DockPositionChanged); + cx.emit(PanelEvent::ChangePosition); } cx.notify(); }, @@ -541,8 +534,6 @@ impl ChatPanel { } } -impl EventEmitter for ChatPanel {} - impl Render for ChatPanel { fn render(&mut self, cx: &mut ViewContext) -> impl IntoElement { v_stack() @@ -662,7 +653,7 @@ impl Panel for ChatPanel { if active { self.acknowledge_last_message(cx); if !is_channels_feature_enabled(cx) { - cx.emit(Event::Dismissed); + cx.emit(PanelEvent::Close); } } } From 898645681fe6e14e746988b376bdeffbebcb7090 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Sat, 13 Jan 2024 22:35:33 -0700 Subject: [PATCH 10/12] Move settings subscription to dock Reduces likelihood of panels being unable to move themselves --- crates/assistant/src/assistant_panel.rs | 11 ----------- crates/collab_ui/src/chat_panel.rs | 11 ----------- crates/collab_ui/src/collab_panel.rs | 13 ------------- crates/project_panel/src/project_panel.rs | 12 ------------ crates/terminal_view/src/terminal_panel.rs | 9 --------- crates/workspace/src/dock.rs | 19 +++++++++++++------ 6 files changed, 13 insertions(+), 62 deletions(-) diff --git a/crates/assistant/src/assistant_panel.rs b/crates/assistant/src/assistant_panel.rs index d4743afb71..9519cfacdb 100644 --- a/crates/assistant/src/assistant_panel.rs +++ b/crates/assistant/src/assistant_panel.rs @@ -192,17 +192,6 @@ impl AssistantPanel { retrieve_context_in_next_inline_assist: false, }; - let mut old_dock_position = this.position(cx); - this.subscriptions = - vec![cx.observe_global::(move |this, cx| { - let new_dock_position = this.position(cx); - if new_dock_position != old_dock_position { - old_dock_position = new_dock_position; - cx.emit(PanelEvent::ChangePosition); - } - cx.notify(); - })]; - this }) }) diff --git a/crates/collab_ui/src/chat_panel.rs b/crates/collab_ui/src/chat_panel.rs index 13b217eec4..e5c3a13442 100644 --- a/crates/collab_ui/src/chat_panel.rs +++ b/crates/collab_ui/src/chat_panel.rs @@ -127,17 +127,6 @@ impl ChatPanel { open_context_menu: None, }; - let mut old_dock_position = this.position(cx); - this.subscriptions.push(cx.observe_global::( - move |this: &mut Self, cx| { - let new_dock_position = this.position(cx); - if new_dock_position != old_dock_position { - old_dock_position = new_dock_position; - cx.emit(PanelEvent::ChangePosition); - } - cx.notify(); - }, - )); this.subscriptions.push(cx.subscribe( &ActiveCall::global(cx), move |this: &mut Self, call, event: &room::Event, cx| match event { diff --git a/crates/collab_ui/src/collab_panel.rs b/crates/collab_ui/src/collab_panel.rs index 5ad3d6cfa3..bbd24136ad 100644 --- a/crates/collab_ui/src/collab_panel.rs +++ b/crates/collab_ui/src/collab_panel.rs @@ -254,19 +254,6 @@ impl CollabPanel { this.update_entries(false, cx); - // Update the dock position when the setting changes. - let mut old_dock_position = this.position(cx); - this.subscriptions.push(cx.observe_global::( - move |this: &mut Self, cx| { - let new_dock_position = this.position(cx); - if new_dock_position != old_dock_position { - old_dock_position = new_dock_position; - cx.emit(PanelEvent::ChangePosition); - } - cx.notify(); - }, - )); - let active_call = ActiveCall::global(cx); this.subscriptions .push(cx.observe(&this.user_store, |this, _, cx| { diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs index 251e26ebfb..5fa8fb102d 100644 --- a/crates/project_panel/src/project_panel.rs +++ b/crates/project_panel/src/project_panel.rs @@ -246,18 +246,6 @@ impl ProjectPanel { }; this.update_visible_entries(None, cx); - // Update the dock position when the setting changes. - let mut old_dock_position = this.position(cx); - ProjectPanelSettings::register(cx); - cx.observe_global::(move |this, cx| { - let new_dock_position = this.position(cx); - if new_dock_position != old_dock_position { - old_dock_position = new_dock_position; - cx.emit(PanelEvent::ChangePosition); - } - }) - .detach(); - this }); diff --git a/crates/terminal_view/src/terminal_panel.rs b/crates/terminal_view/src/terminal_panel.rs index d0b52f5eb2..2156aad18a 100644 --- a/crates/terminal_view/src/terminal_panel.rs +++ b/crates/terminal_view/src/terminal_panel.rs @@ -159,15 +159,6 @@ impl TerminalPanel { height: None, _subscriptions: subscriptions, }; - let mut old_dock_position = this.position(cx); - cx.observe_global::(move |this, cx| { - let new_dock_position = this.position(cx); - if new_dock_position != old_dock_position { - old_dock_position = new_dock_position; - cx.emit(PanelEvent::ChangePosition); - } - }) - .detach(); this } diff --git a/crates/workspace/src/dock.rs b/crates/workspace/src/dock.rs index 0c75259726..742ff3b6f8 100644 --- a/crates/workspace/src/dock.rs +++ b/crates/workspace/src/dock.rs @@ -7,6 +7,7 @@ use gpui::{ }; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; +use settings::SettingsStore; use std::sync::Arc; use ui::{h_stack, ContextMenu, IconButton, Tooltip}; use ui::{prelude::*, right_click_menu}; @@ -14,7 +15,6 @@ use ui::{prelude::*, right_click_menu}; const RESIZE_HANDLE_SIZE: Pixels = Pixels(6.); pub enum PanelEvent { - ChangePosition, ZoomIn, ZoomOut, Activate, @@ -177,7 +177,7 @@ impl DockPosition { struct PanelEntry { panel: Arc, - _subscriptions: [Subscription; 2], + _subscriptions: [Subscription; 3], } pub struct PanelButtons { @@ -321,9 +321,15 @@ impl Dock { ) { let subscriptions = [ cx.observe(&panel, |_, _, cx| cx.notify()), - cx.subscribe(&panel, move |this, panel, event, cx| match event { - PanelEvent::ChangePosition => { + cx.observe_global::({ + let workspace = workspace.clone(); + let panel = panel.clone(); + + move |this, cx| { let new_position = panel.read(cx).position(cx); + if new_position == this.position { + return; + } let Ok(new_dock) = workspace.update(cx, |workspace, cx| { if panel.is_zoomed(cx) { @@ -354,6 +360,8 @@ impl Dock { } }); } + }), + cx.subscribe(&panel, move |this, panel, event, cx| match event { PanelEvent::ZoomIn => { this.set_panel_zoomed(&panel.to_any(), true, cx); if !panel.focus_handle(cx).contains_focused(cx) { @@ -735,9 +743,8 @@ pub mod test { true } - fn set_position(&mut self, position: DockPosition, cx: &mut ViewContext) { + fn set_position(&mut self, position: DockPosition, _: &mut ViewContext) { self.position = position; - cx.emit(PanelEvent::ChangePosition); } fn size(&self, _: &WindowContext) -> Pixels { From 4d87a67af8626a43f37c3458c4e9f04ed2414d4e Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Sat, 13 Jan 2024 22:44:47 -0700 Subject: [PATCH 11/12] Remove unused imports --- crates/assistant/src/assistant_panel.rs | 9 +++---- crates/collab_ui/src/chat_panel.rs | 29 +++++++--------------- crates/collab_ui/src/collab_panel.rs | 2 +- crates/project_panel/src/project_panel.rs | 2 +- crates/terminal_view/src/terminal_panel.rs | 2 +- 5 files changed, 16 insertions(+), 28 deletions(-) diff --git a/crates/assistant/src/assistant_panel.rs b/crates/assistant/src/assistant_panel.rs index 9519cfacdb..2b0a0941c0 100644 --- a/crates/assistant/src/assistant_panel.rs +++ b/crates/assistant/src/assistant_panel.rs @@ -40,7 +40,7 @@ use language::{language_settings::SoftWrap, Buffer, LanguageRegistry, ToOffset a use project::Project; use search::{buffer_search::DivRegistrar, BufferSearchBar}; use semantic_index::{SemanticIndex, SemanticIndexStatus}; -use settings::{Settings, SettingsStore}; +use settings::Settings; use std::{ cell::Cell, cmp, @@ -165,7 +165,7 @@ impl AssistantPanel { cx.on_focus_in(&focus_handle, Self::focus_in).detach(); cx.on_focus_out(&focus_handle, Self::focus_out).detach(); - let mut this = Self { + Self { workspace: workspace_handle, active_editor_index: Default::default(), prev_active_editor_index: Default::default(), @@ -190,9 +190,7 @@ impl AssistantPanel { _watch_saved_conversations, semantic_index, retrieve_context_in_next_inline_assist: false, - }; - - this + } }) }) }) @@ -3122,6 +3120,7 @@ mod tests { use crate::MessageId; use ai::test::FakeCompletionProvider; use gpui::AppContext; + use settings::SettingsStore; #[gpui::test] fn test_inserting_and_removing_messages(cx: &mut AppContext) { diff --git a/crates/collab_ui/src/chat_panel.rs b/crates/collab_ui/src/chat_panel.rs index e5c3a13442..c919ceac9f 100644 --- a/crates/collab_ui/src/chat_panel.rs +++ b/crates/collab_ui/src/chat_panel.rs @@ -1,4 +1,4 @@ -use crate::{collab_panel, is_channels_feature_enabled, ChatPanelSettings, CollabPanel}; +use crate::{collab_panel, is_channels_feature_enabled, ChatPanelSettings}; use anyhow::Result; use call::{room, ActiveCall}; use channel::{ChannelChat, ChannelChatEvent, ChannelMessageId, ChannelStore}; @@ -7,24 +7,22 @@ use collections::HashMap; use db::kvp::KEY_VALUE_STORE; use editor::Editor; use gpui::{ - actions, div, list, prelude::*, px, Action, AnyElement, AppContext, AsyncWindowContext, - ClickEvent, DismissEvent, ElementId, EventEmitter, FocusHandle, FocusableView, FontWeight, - ListOffset, ListScrollEvent, ListState, Model, Render, Subscription, Task, View, ViewContext, - VisualContext, WeakView, + actions, div, list, prelude::*, px, Action, AppContext, AsyncWindowContext, DismissEvent, + ElementId, EventEmitter, FocusHandle, FocusableView, FontWeight, ListOffset, ListScrollEvent, + ListState, Model, Render, Subscription, Task, View, ViewContext, VisualContext, WeakView, }; use language::LanguageRegistry; use menu::Confirm; use message_editor::MessageEditor; use project::Fs; use rich_text::RichText; -use rpc::proto; use serde::{Deserialize, Serialize}; -use settings::{Settings, SettingsStore}; +use settings::Settings; use std::sync::Arc; use time::{OffsetDateTime, UtcOffset}; use ui::{ - popover_menu, prelude::*, Avatar, Button, ContextMenu, IconButton, IconName, Key, KeyBinding, - Label, TabBar, Tooltip, + popover_menu, prelude::*, Avatar, Button, ContextMenu, IconButton, IconName, KeyBinding, Label, + TabBar, }; use util::{ResultExt, TryFutureExt}; use workspace::{ @@ -279,7 +277,7 @@ impl ChatPanel { fn render_message(&mut self, ix: usize, cx: &mut ViewContext) -> impl IntoElement { let active_chat = &self.active_chat.as_ref().unwrap().0; - let (message, is_continuation_from_previous, is_continuation_to_next, is_admin) = + let (message, is_continuation_from_previous, is_admin) = active_chat.update(cx, |active_chat, cx| { let is_admin = self .channel_store @@ -288,13 +286,9 @@ impl ChatPanel { let last_message = active_chat.message(ix.saturating_sub(1)); let this_message = active_chat.message(ix).clone(); - let next_message = - active_chat.message(ix.saturating_add(1).min(active_chat.message_count() - 1)); let is_continuation_from_previous = last_message.id != this_message.id && last_message.sender.id == this_message.sender.id; - let is_continuation_to_next = this_message.id != next_message.id - && this_message.sender.id == next_message.sender.id; if let ChannelMessageId::Saved(id) = this_message.id { if this_message @@ -306,12 +300,7 @@ impl ChatPanel { } } - ( - this_message, - is_continuation_from_previous, - is_continuation_to_next, - is_admin, - ) + (this_message, is_continuation_from_previous, is_admin) }); let _is_pending = message.is_pending(); diff --git a/crates/collab_ui/src/collab_panel.rs b/crates/collab_ui/src/collab_panel.rs index bbd24136ad..38569adb20 100644 --- a/crates/collab_ui/src/collab_panel.rs +++ b/crates/collab_ui/src/collab_panel.rs @@ -26,7 +26,7 @@ use menu::{Cancel, Confirm, SelectNext, SelectPrev}; use project::{Fs, Project}; use rpc::proto::{self, PeerId}; use serde_derive::{Deserialize, Serialize}; -use settings::{Settings, SettingsStore}; +use settings::Settings; use smallvec::SmallVec; use std::{mem, sync::Arc}; use theme::{ActiveTheme, ThemeSettings}; diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs index 5fa8fb102d..113900dcd2 100644 --- a/crates/project_panel/src/project_panel.rs +++ b/crates/project_panel/src/project_panel.rs @@ -1,6 +1,6 @@ pub mod file_associations; mod project_panel_settings; -use settings::{Settings, SettingsStore}; +use settings::Settings; use db::kvp::KEY_VALUE_STORE; use editor::{scroll::autoscroll::Autoscroll, Cancel, Editor}; diff --git a/crates/terminal_view/src/terminal_panel.rs b/crates/terminal_view/src/terminal_panel.rs index 2156aad18a..dee18ea73b 100644 --- a/crates/terminal_view/src/terminal_panel.rs +++ b/crates/terminal_view/src/terminal_panel.rs @@ -11,7 +11,7 @@ use itertools::Itertools; use project::{Fs, ProjectEntryId}; use search::{buffer_search::DivRegistrar, BufferSearchBar}; use serde::{Deserialize, Serialize}; -use settings::{Settings, SettingsStore}; +use settings::Settings; use terminal::terminal_settings::{TerminalDockPosition, TerminalSettings}; use ui::{h_stack, ButtonCommon, Clickable, IconButton, IconSize, Selectable, Tooltip}; use util::{ResultExt, TryFutureExt}; From b34c78016fe051cef04547c89b5a74b1181c5ec3 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Sun, 14 Jan 2024 12:26:54 -0700 Subject: [PATCH 12/12] Fix tests for TestPanel --- crates/workspace/src/dock.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/workspace/src/dock.rs b/crates/workspace/src/dock.rs index 742ff3b6f8..52f768e03f 100644 --- a/crates/workspace/src/dock.rs +++ b/crates/workspace/src/dock.rs @@ -743,8 +743,9 @@ pub mod test { true } - fn set_position(&mut self, position: DockPosition, _: &mut ViewContext) { + fn set_position(&mut self, position: DockPosition, cx: &mut ViewContext) { self.position = position; + cx.update_global::(|_, _| {}); } fn size(&self, _: &WindowContext) -> Pixels {