@@ -18,13 +18,14 @@ use gpui::{
MouseEventHandler, Orientation, OverlayPositionMode, Padding, ParentElement, SafeStylable,
Stack, Svg,
},
+ fonts::TextStyle,
geometry::{
rect::RectF,
vector::{vec2f, Vector2F},
},
impl_actions,
platform::{CursorStyle, MouseButton, PromptLevel},
- serde_json, AnyElement, AppContext, AsyncAppContext, Element, Entity, ModelHandle,
+ serde_json, AnyElement, AppContext, AsyncAppContext, Element, Entity, FontCache, ModelHandle,
Subscription, Task, View, ViewContext, ViewHandle, WeakViewHandle,
};
use menu::{Confirm, SelectNext, SelectPrev};
@@ -183,6 +184,7 @@ pub struct CollabPanel {
#[derive(Serialize, Deserialize)]
struct SerializedChannelsPanel {
width: Option<f32>,
+ collapsed_channels: Vec<ChannelId>,
}
#[derive(Debug)]
@@ -227,6 +229,9 @@ enum ListEntry {
channel: Arc<Channel>,
depth: usize,
},
+ ChannelNotes {
+ channel_id: ChannelId,
+ },
ChannelEditor {
depth: usize,
},
@@ -370,6 +375,12 @@ impl CollabPanel {
return channel_row;
}
}
+ ListEntry::ChannelNotes { channel_id } => this.render_channel_notes(
+ *channel_id,
+ &theme.collab_panel,
+ is_selected,
+ cx,
+ ),
ListEntry::ChannelInvite(channel) => Self::render_channel_invite(
channel.clone(),
this.channel_store.clone(),
@@ -509,6 +520,7 @@ impl CollabPanel {
if let Some(serialized_panel) = serialized_panel {
panel.update(cx, |panel, cx| {
panel.width = serialized_panel.width;
+ panel.collapsed_channels = serialized_panel.collapsed_channels;
cx.notify();
});
}
@@ -519,12 +531,16 @@ impl CollabPanel {
fn serialize(&mut self, cx: &mut ViewContext<Self>) {
let width = self.width;
+ let collapsed_channels = self.collapsed_channels.clone();
self.pending_serialization = cx.background().spawn(
async move {
KEY_VALUE_STORE
.write_kvp(
COLLABORATION_PANEL_KEY.into(),
- serde_json::to_string(&SerializedChannelsPanel { width })?,
+ serde_json::to_string(&SerializedChannelsPanel {
+ width,
+ collapsed_channels,
+ })?,
)
.await?;
anyhow::Ok(())
@@ -548,6 +564,10 @@ impl CollabPanel {
if !self.collapsed_sections.contains(&Section::ActiveCall) {
let room = room.read(cx);
+ if let Some(channel_id) = room.channel_id() {
+ self.entries.push(ListEntry::ChannelNotes { channel_id })
+ }
+
// Populate the active user.
if let Some(user) = user_store.current_user() {
self.match_candidates.clear();
@@ -1007,25 +1027,19 @@ impl CollabPanel {
) -> AnyElement<Self> {
enum JoinProject {}
- let font_cache = cx.font_cache();
- let host_avatar_height = theme
+ let host_avatar_width = theme
.contact_avatar
.width
.or(theme.contact_avatar.height)
.unwrap_or(0.);
- let row = &theme.project_row.inactive_state().default;
let tree_branch = theme.tree_branch;
- let line_height = row.name.text.line_height(font_cache);
- let cap_height = row.name.text.cap_height(font_cache);
- let baseline_offset =
- row.name.text.baseline_offset(font_cache) + (theme.row_height - line_height) / 2.;
let project_name = if worktree_root_names.is_empty() {
"untitled".to_string()
} else {
worktree_root_names.join(", ")
};
- MouseEventHandler::new::<JoinProject, _>(project_id as usize, cx, |mouse_state, _| {
+ MouseEventHandler::new::<JoinProject, _>(project_id as usize, cx, |mouse_state, cx| {
let tree_branch = *tree_branch.in_state(is_selected).style_for(mouse_state);
let row = theme
.project_row
@@ -1033,39 +1047,20 @@ impl CollabPanel {
.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(
- Stack::new()
- .with_child(Canvas::new(move |scene, bounds, _, _, _| {
- let start_x =
- bounds.min_x() + (bounds.width() / 2.) - (tree_branch.width / 2.);
- let end_x = bounds.max_x();
- let start_y = bounds.min_y();
- let end_y = bounds.min_y() + baseline_offset - (cap_height / 2.);
-
- scene.push_quad(gpui::Quad {
- bounds: RectF::from_points(
- vec2f(start_x, start_y),
- vec2f(
- start_x + tree_branch.width,
- if is_last { end_y } else { bounds.max_y() },
- ),
- ),
- background: Some(tree_branch.color),
- border: gpui::Border::default(),
- corner_radii: (0.).into(),
- });
- scene.push_quad(gpui::Quad {
- bounds: RectF::from_points(
- vec2f(start_x, end_y),
- vec2f(end_x, end_y + tree_branch.width),
- ),
- background: Some(tree_branch.color),
- border: gpui::Border::default(),
- corner_radii: (0.).into(),
- });
- }))
+ Svg::new("icons/file_icons/folder.svg")
+ .with_color(theme.channel_hash.color)
.constrained()
- .with_width(host_avatar_height),
+ .with_width(theme.channel_hash.width)
+ .aligned()
+ .left(),
)
.with_child(
Label::new(project_name, row.name.text.clone())
@@ -1240,7 +1235,7 @@ impl CollabPanel {
});
if let Some(name) = channel_name {
- Cow::Owned(format!("Current Call - #{}", name))
+ Cow::Owned(format!("#{}", name))
} else {
Cow::Borrowed("Current Call")
}
@@ -1676,6 +1671,61 @@ impl CollabPanel {
.into_any()
}
+ fn render_channel_notes(
+ &self,
+ channel_id: ChannelId,
+ theme: &theme::CollabPanel,
+ is_selected: bool,
+ cx: &mut ViewContext<Self>,
+ ) -> AnyElement<Self> {
+ enum ChannelNotes {}
+ let host_avatar_width = theme
+ .contact_avatar
+ .width
+ .or(theme.contact_avatar.height)
+ .unwrap_or(0.);
+
+ MouseEventHandler::new::<ChannelNotes, _>(channel_id 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/radix/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_buffer(&OpenChannelBuffer { channel_id }, cx);
+ })
+ .with_cursor_style(CursorStyle::PointingHand)
+ .into_any()
+ }
+
fn render_channel_invite(
channel: Arc<Channel>,
channel_store: ModelHandle<ChannelStore>,
@@ -2114,6 +2164,7 @@ impl CollabPanel {
self.collapsed_channels.insert(ix, channel_id);
}
};
+ self.serialize(cx);
self.update_entries(true, cx);
cx.notify();
cx.focus_self();
@@ -2392,6 +2443,51 @@ impl CollabPanel {
}
}
+fn render_tree_branch(
+ branch_style: theme::TreeBranch,
+ row_style: &TextStyle,
+ is_last: bool,
+ size: Vector2F,
+ font_cache: &FontCache,
+) -> gpui::elements::ConstrainedBox<CollabPanel> {
+ let line_height = row_style.line_height(font_cache);
+ let cap_height = row_style.cap_height(font_cache);
+ let baseline_offset = row_style.baseline_offset(font_cache) + (size.y() - line_height) / 2.;
+
+ Canvas::new(move |scene, bounds, _, _, _| {
+ scene.paint_layer(None, |scene| {
+ let start_x = bounds.min_x() + (bounds.width() / 2.) - (branch_style.width / 2.);
+ let end_x = bounds.max_x();
+ let start_y = bounds.min_y();
+ let end_y = bounds.min_y() + baseline_offset - (cap_height / 2.);
+
+ scene.push_quad(gpui::Quad {
+ bounds: RectF::from_points(
+ vec2f(start_x, start_y),
+ vec2f(
+ start_x + branch_style.width,
+ if is_last { end_y } else { bounds.max_y() },
+ ),
+ ),
+ background: Some(branch_style.color),
+ border: gpui::Border::default(),
+ corner_radii: (0.).into(),
+ });
+ scene.push_quad(gpui::Quad {
+ bounds: RectF::from_points(
+ vec2f(start_x, end_y),
+ vec2f(end_x, end_y + branch_style.width),
+ ),
+ background: Some(branch_style.color),
+ border: gpui::Border::default(),
+ corner_radii: (0.).into(),
+ });
+ })
+ })
+ .constrained()
+ .with_width(size.x())
+}
+
impl View for CollabPanel {
fn ui_name() -> &'static str {
"CollabPanel"
@@ -2601,6 +2697,14 @@ impl PartialEq for ListEntry {
return channel_1.id == channel_2.id && depth_1 == depth_2;
}
}
+ ListEntry::ChannelNotes { channel_id } => {
+ if let ListEntry::ChannelNotes {
+ channel_id: other_id,
+ } = other
+ {
+ return channel_id == other_id;
+ }
+ }
ListEntry::ChannelInvite(channel_1) => {
if let ListEntry::ChannelInvite(channel_2) = other {
return channel_1.id == channel_2.id;