Add settings for removing the assistant and collaboration panel buttons

Add a not-logged-in state to the collaboration panel

co-authored-by: max <max@zed.dev>
This commit is contained in:
Mikayla 2023-08-07 16:27:47 -07:00
parent e37e76fc0b
commit 8980a9f1c1
No known key found for this signature in database
10 changed files with 195 additions and 115 deletions

View file

@ -122,13 +122,17 @@
// Amount of indentation for nested items. // Amount of indentation for nested items.
"indent_size": 20 "indent_size": 20
}, },
"channels_panel": { "collaboration_panel": {
// Whether to show the collaboration panel button in the status bar.
"button": true,
// Where to dock channels panel. Can be 'left' or 'right'. // Where to dock channels panel. Can be 'left' or 'right'.
"dock": "left", "dock": "left",
// Default width of the channels panel. // Default width of the channels panel.
"default_width": 240 "default_width": 240
}, },
"assistant": { "assistant": {
// Whether to show the assistant panel button in the status bar.
"button": true,
// Where to dock the assistant. Can be 'left', 'right' or 'bottom'. // Where to dock the assistant. Can be 'left', 'right' or 'bottom'.
"dock": "right", "dock": "right",
// Default width when the assistant is docked to the left or right. // Default width when the assistant is docked to the left or right.
@ -220,7 +224,9 @@
"copilot": { "copilot": {
// The set of glob patterns for which copilot should be disabled // The set of glob patterns for which copilot should be disabled
// in any matching file. // in any matching file.
"disabled_globs": [".env"] "disabled_globs": [
".env"
]
}, },
// Settings specific to journaling // Settings specific to journaling
"journal": { "journal": {

View file

@ -192,6 +192,7 @@ impl AssistantPanel {
old_dock_position = new_dock_position; old_dock_position = new_dock_position;
cx.emit(AssistantPanelEvent::DockPositionChanged); cx.emit(AssistantPanelEvent::DockPositionChanged);
} }
cx.notify();
})]; })];
this this
@ -790,8 +791,10 @@ impl Panel for AssistantPanel {
} }
} }
fn icon_path(&self) -> &'static str { fn icon_path(&self, cx: &WindowContext) -> Option<&'static str> {
"icons/robot_14.svg" settings::get::<AssistantSettings>(cx)
.button
.then(|| "icons/robot_14.svg")
} }
fn icon_tooltip(&self) -> (String, Option<Box<dyn Action>>) { fn icon_tooltip(&self) -> (String, Option<Box<dyn Action>>) {

View file

@ -13,6 +13,7 @@ pub enum AssistantDockPosition {
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
pub struct AssistantSettings { pub struct AssistantSettings {
pub button: bool,
pub dock: AssistantDockPosition, pub dock: AssistantDockPosition,
pub default_width: f32, pub default_width: f32,
pub default_height: f32, pub default_height: f32,
@ -20,6 +21,7 @@ pub struct AssistantSettings {
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug)] #[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug)]
pub struct AssistantSettingsContent { pub struct AssistantSettingsContent {
pub button: Option<bool>,
pub dock: Option<AssistantDockPosition>, pub dock: Option<AssistantDockPosition>,
pub default_width: Option<f32>, pub default_width: Option<f32>,
pub default_height: Option<f32>, pub default_height: Option<f32>,

View file

@ -27,7 +27,7 @@ use gpui::{
Subscription, Task, View, ViewContext, ViewHandle, WeakViewHandle, Subscription, Task, View, ViewContext, ViewHandle, WeakViewHandle,
}; };
use menu::{Confirm, SelectNext, SelectPrev}; use menu::{Confirm, SelectNext, SelectPrev};
use panel_settings::{ChannelsPanelDockPosition, ChannelsPanelSettings}; use panel_settings::{CollaborationPanelDockPosition, CollaborationPanelSettings};
use project::{Fs, Project}; use project::{Fs, Project};
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use settings::SettingsStore; use settings::SettingsStore;
@ -65,7 +65,7 @@ impl_actions!(collab_panel, [RemoveChannel, NewChannel, AddMember]);
const CHANNELS_PANEL_KEY: &'static str = "ChannelsPanel"; const CHANNELS_PANEL_KEY: &'static str = "ChannelsPanel";
pub fn init(_client: Arc<Client>, cx: &mut AppContext) { pub fn init(_client: Arc<Client>, cx: &mut AppContext) {
settings::register::<panel_settings::ChannelsPanelSettings>(cx); settings::register::<panel_settings::CollaborationPanelSettings>(cx);
contact_finder::init(cx); contact_finder::init(cx);
channel_modal::init(cx); channel_modal::init(cx);
@ -95,6 +95,7 @@ pub struct CollabPanel {
entries: Vec<ListEntry>, entries: Vec<ListEntry>,
selection: Option<usize>, selection: Option<usize>,
user_store: ModelHandle<UserStore>, user_store: ModelHandle<UserStore>,
client: Arc<Client>,
channel_store: ModelHandle<ChannelStore>, channel_store: ModelHandle<ChannelStore>,
project: ModelHandle<Project>, project: ModelHandle<Project>,
match_candidates: Vec<StringMatchCandidate>, match_candidates: Vec<StringMatchCandidate>,
@ -320,6 +321,7 @@ impl CollabPanel {
match_candidates: Vec::default(), match_candidates: Vec::default(),
collapsed_sections: Vec::default(), collapsed_sections: Vec::default(),
workspace: workspace.weak_handle(), workspace: workspace.weak_handle(),
client: workspace.app_state().client.clone(),
list_state, list_state,
}; };
this.update_entries(cx); this.update_entries(cx);
@ -334,6 +336,7 @@ impl CollabPanel {
old_dock_position = new_dock_position; old_dock_position = new_dock_position;
cx.emit(Event::DockPositionChanged); cx.emit(Event::DockPositionChanged);
} }
cx.notify();
}), }),
); );
@ -1862,6 +1865,31 @@ impl View for CollabPanel {
fn render(&mut self, cx: &mut gpui::ViewContext<'_, '_, Self>) -> gpui::AnyElement<Self> { fn render(&mut self, cx: &mut gpui::ViewContext<'_, '_, Self>) -> gpui::AnyElement<Self> {
let theme = &theme::current(cx).collab_panel; let theme = &theme::current(cx).collab_panel;
if self.user_store.read(cx).current_user().is_none() {
enum LogInButton {}
return Flex::column()
.with_child(
MouseEventHandler::<LogInButton, _>::new(0, cx, |state, _| {
let button = theme.log_in_button.style_for(state);
Label::new("Sign in to collaborate", button.text.clone())
.contained()
.with_style(button.container)
})
.on_click(MouseButton::Left, |_, this, cx| {
let client = this.client.clone();
cx.spawn(|_, cx| async move {
client.authenticate_and_connect(true, &cx).await.log_err()
})
.detach();
})
.with_cursor_style(CursorStyle::PointingHand),
)
.contained()
.with_style(theme.container)
.into_any();
}
enum PanelFocus {} enum PanelFocus {}
MouseEventHandler::<PanelFocus, _>::new(0, cx, |_, cx| { MouseEventHandler::<PanelFocus, _>::new(0, cx, |_, cx| {
Stack::new() Stack::new()
@ -1901,9 +1929,9 @@ impl View for CollabPanel {
impl Panel for CollabPanel { impl Panel for CollabPanel {
fn position(&self, cx: &gpui::WindowContext) -> DockPosition { fn position(&self, cx: &gpui::WindowContext) -> DockPosition {
match settings::get::<ChannelsPanelSettings>(cx).dock { match settings::get::<CollaborationPanelSettings>(cx).dock {
ChannelsPanelDockPosition::Left => DockPosition::Left, CollaborationPanelDockPosition::Left => DockPosition::Left,
ChannelsPanelDockPosition::Right => DockPosition::Right, CollaborationPanelDockPosition::Right => DockPosition::Right,
} }
} }
@ -1912,13 +1940,15 @@ impl Panel for CollabPanel {
} }
fn set_position(&mut self, position: DockPosition, cx: &mut ViewContext<Self>) { fn set_position(&mut self, position: DockPosition, cx: &mut ViewContext<Self>) {
settings::update_settings_file::<ChannelsPanelSettings>( settings::update_settings_file::<CollaborationPanelSettings>(
self.fs.clone(), self.fs.clone(),
cx, cx,
move |settings| { move |settings| {
let dock = match position { let dock = match position {
DockPosition::Left | DockPosition::Bottom => ChannelsPanelDockPosition::Left, DockPosition::Left | DockPosition::Bottom => {
DockPosition::Right => ChannelsPanelDockPosition::Right, CollaborationPanelDockPosition::Left
}
DockPosition::Right => CollaborationPanelDockPosition::Right,
}; };
settings.dock = Some(dock); settings.dock = Some(dock);
}, },
@ -1927,7 +1957,7 @@ impl Panel for CollabPanel {
fn size(&self, cx: &gpui::WindowContext) -> f32 { fn size(&self, cx: &gpui::WindowContext) -> f32 {
self.width self.width
.unwrap_or_else(|| settings::get::<ChannelsPanelSettings>(cx).default_width) .unwrap_or_else(|| settings::get::<CollaborationPanelSettings>(cx).default_width)
} }
fn set_size(&mut self, size: f32, cx: &mut ViewContext<Self>) { fn set_size(&mut self, size: f32, cx: &mut ViewContext<Self>) {
@ -1936,8 +1966,10 @@ impl Panel for CollabPanel {
cx.notify(); cx.notify();
} }
fn icon_path(&self) -> &'static str { fn icon_path(&self, cx: &gpui::WindowContext) -> Option<&'static str> {
"icons/radix/person.svg" settings::get::<CollaborationPanelSettings>(cx)
.button
.then(|| "icons/radix/person.svg")
} }
fn icon_tooltip(&self) -> (String, Option<Box<dyn gpui::Action>>) { fn icon_tooltip(&self) -> (String, Option<Box<dyn gpui::Action>>) {

View file

@ -5,27 +5,29 @@ use settings::Setting;
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] #[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
pub enum ChannelsPanelDockPosition { pub enum CollaborationPanelDockPosition {
Left, Left,
Right, Right,
} }
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
pub struct ChannelsPanelSettings { pub struct CollaborationPanelSettings {
pub dock: ChannelsPanelDockPosition, pub button: bool,
pub dock: CollaborationPanelDockPosition,
pub default_width: f32, pub default_width: f32,
} }
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug)] #[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug)]
pub struct ChannelsPanelSettingsContent { pub struct CollaborationPanelSettingsContent {
pub dock: Option<ChannelsPanelDockPosition>, pub button: Option<bool>,
pub dock: Option<CollaborationPanelDockPosition>,
pub default_width: Option<f32>, pub default_width: Option<f32>,
} }
impl Setting for ChannelsPanelSettings { impl Setting for CollaborationPanelSettings {
const KEY: Option<&'static str> = Some("channels_panel"); const KEY: Option<&'static str> = Some("collaboration_panel");
type FileContent = ChannelsPanelSettingsContent; type FileContent = CollaborationPanelSettingsContent;
fn load( fn load(
default_value: &Self::FileContent, default_value: &Self::FileContent,

View file

@ -1657,8 +1657,8 @@ impl workspace::dock::Panel for ProjectPanel {
cx.notify(); cx.notify();
} }
fn icon_path(&self) -> &'static str { fn icon_path(&self, _: &WindowContext) -> Option<&'static str> {
"icons/folder_tree_16.svg" Some("icons/folder_tree_16.svg")
} }
fn icon_tooltip(&self) -> (String, Option<Box<dyn Action>>) { fn icon_tooltip(&self) -> (String, Option<Box<dyn Action>>) {

View file

@ -396,8 +396,8 @@ impl Panel for TerminalPanel {
} }
} }
fn icon_path(&self) -> &'static str { fn icon_path(&self, _: &WindowContext) -> Option<&'static str> {
"icons/terminal_12.svg" Some("icons/terminal_12.svg")
} }
fn icon_tooltip(&self) -> (String, Option<Box<dyn Action>>) { fn icon_tooltip(&self) -> (String, Option<Box<dyn Action>>) {

View file

@ -220,6 +220,7 @@ pub struct CopilotAuthAuthorized {
pub struct CollabPanel { pub struct CollabPanel {
#[serde(flatten)] #[serde(flatten)]
pub container: ContainerStyle, pub container: ContainerStyle,
pub log_in_button: Interactive<ContainedText>,
pub channel_hash: Icon, pub channel_hash: Icon,
pub channel_modal: ChannelModal, pub channel_modal: ChannelModal,
pub user_query_editor: FieldEditor, pub user_query_editor: FieldEditor,

View file

@ -14,7 +14,7 @@ pub trait Panel: View {
fn set_position(&mut self, position: DockPosition, cx: &mut ViewContext<Self>); fn set_position(&mut self, position: DockPosition, cx: &mut ViewContext<Self>);
fn size(&self, cx: &WindowContext) -> f32; fn size(&self, cx: &WindowContext) -> f32;
fn set_size(&mut self, size: f32, cx: &mut ViewContext<Self>); fn set_size(&mut self, size: f32, cx: &mut ViewContext<Self>);
fn icon_path(&self) -> &'static str; fn icon_path(&self, cx: &WindowContext) -> Option<&'static str>;
fn icon_tooltip(&self) -> (String, Option<Box<dyn Action>>); fn icon_tooltip(&self) -> (String, Option<Box<dyn Action>>);
fn icon_label(&self, _: &WindowContext) -> Option<String> { fn icon_label(&self, _: &WindowContext) -> Option<String> {
None None
@ -51,7 +51,7 @@ pub trait PanelHandle {
fn set_active(&self, active: bool, cx: &mut WindowContext); fn set_active(&self, active: bool, cx: &mut WindowContext);
fn size(&self, cx: &WindowContext) -> f32; fn size(&self, cx: &WindowContext) -> f32;
fn set_size(&self, size: f32, cx: &mut WindowContext); fn set_size(&self, size: f32, cx: &mut WindowContext);
fn icon_path(&self, cx: &WindowContext) -> &'static str; fn icon_path(&self, cx: &WindowContext) -> Option<&'static str>;
fn icon_tooltip(&self, cx: &WindowContext) -> (String, Option<Box<dyn Action>>); fn icon_tooltip(&self, cx: &WindowContext) -> (String, Option<Box<dyn Action>>);
fn icon_label(&self, cx: &WindowContext) -> Option<String>; fn icon_label(&self, cx: &WindowContext) -> Option<String>;
fn has_focus(&self, cx: &WindowContext) -> bool; fn has_focus(&self, cx: &WindowContext) -> bool;
@ -98,8 +98,8 @@ where
self.update(cx, |this, cx| this.set_active(active, cx)) self.update(cx, |this, cx| this.set_active(active, cx))
} }
fn icon_path(&self, cx: &WindowContext) -> &'static str { fn icon_path(&self, cx: &WindowContext) -> Option<&'static str> {
self.read(cx).icon_path() self.read(cx).icon_path(cx)
} }
fn icon_tooltip(&self, cx: &WindowContext) -> (String, Option<Box<dyn Action>>) { fn icon_tooltip(&self, cx: &WindowContext) -> (String, Option<Box<dyn Action>>) {
@ -490,8 +490,9 @@ impl View for PanelButtons {
.map(|item| (item.panel.clone(), item.context_menu.clone())) .map(|item| (item.panel.clone(), item.context_menu.clone()))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
Flex::row() Flex::row()
.with_children(panels.into_iter().enumerate().map( .with_children(panels.into_iter().enumerate().filter_map(
|(panel_ix, (view, context_menu))| { |(panel_ix, (view, context_menu))| {
let icon_path = view.icon_path(cx)?;
let is_active = is_open && panel_ix == active_ix; let is_active = is_open && panel_ix == active_ix;
let (tooltip, tooltip_action) = if is_active { let (tooltip, tooltip_action) = if is_active {
( (
@ -505,94 +506,96 @@ impl View for PanelButtons {
} else { } else {
view.icon_tooltip(cx) view.icon_tooltip(cx)
}; };
Stack::new() Some(
.with_child( Stack::new()
MouseEventHandler::<Self, _>::new(panel_ix, cx, |state, cx| { .with_child(
let style = button_style.in_state(is_active); MouseEventHandler::<Self, _>::new(panel_ix, cx, |state, cx| {
let style = button_style.in_state(is_active);
let style = style.style_for(state); let style = style.style_for(state);
Flex::row() Flex::row()
.with_child( .with_child(
Svg::new(view.icon_path(cx)) Svg::new(icon_path)
.with_color(style.icon_color) .with_color(style.icon_color)
.constrained() .constrained()
.with_width(style.icon_size) .with_width(style.icon_size)
.aligned(),
)
.with_children(if let Some(label) = view.icon_label(cx) {
Some(
Label::new(label, style.label.text.clone())
.contained()
.with_style(style.label.container)
.aligned(), .aligned(),
) )
} else { .with_children(if let Some(label) = view.icon_label(cx) {
None Some(
}) Label::new(label, style.label.text.clone())
.constrained() .contained()
.with_height(style.icon_size) .with_style(style.label.container)
.contained() .aligned(),
.with_style(style.container)
})
.with_cursor_style(CursorStyle::PointingHand)
.on_click(MouseButton::Left, {
let tooltip_action =
tooltip_action.as_ref().map(|action| action.boxed_clone());
move |_, this, cx| {
if let Some(tooltip_action) = &tooltip_action {
let window_id = cx.window_id();
let view_id = this.workspace.id();
let tooltip_action = tooltip_action.boxed_clone();
cx.spawn(|_, mut cx| async move {
cx.dispatch_action(
window_id,
view_id,
&*tooltip_action,
) )
.ok(); } else {
None
}) })
.detach(); .constrained()
} .with_height(style.icon_size)
} .contained()
}) .with_style(style.container)
.on_click(MouseButton::Right, { })
let view = view.clone(); .with_cursor_style(CursorStyle::PointingHand)
let menu = context_menu.clone(); .on_click(MouseButton::Left, {
move |_, _, cx| { let tooltip_action =
const POSITIONS: [DockPosition; 3] = [ tooltip_action.as_ref().map(|action| action.boxed_clone());
DockPosition::Left, move |_, this, cx| {
DockPosition::Right, if let Some(tooltip_action) = &tooltip_action {
DockPosition::Bottom, let window_id = cx.window_id();
]; let view_id = this.workspace.id();
let tooltip_action = tooltip_action.boxed_clone();
menu.update(cx, |menu, cx| { cx.spawn(|_, mut cx| async move {
let items = POSITIONS cx.dispatch_action(
.into_iter() window_id,
.filter(|position| { view_id,
*position != dock_position &*tooltip_action,
&& view.position_is_valid(*position, cx)
})
.map(|position| {
let view = view.clone();
ContextMenuItem::handler(
format!("Dock {}", position.to_label()),
move |cx| view.set_position(position, cx),
) )
.ok();
}) })
.collect(); .detach();
menu.show(Default::default(), menu_corner, items, cx); }
}) }
} })
}) .on_click(MouseButton::Right, {
.with_tooltip::<Self>( let view = view.clone();
panel_ix, let menu = context_menu.clone();
tooltip, move |_, _, cx| {
tooltip_action, const POSITIONS: [DockPosition; 3] = [
tooltip_style.clone(), DockPosition::Left,
cx, DockPosition::Right,
), DockPosition::Bottom,
) ];
.with_child(ChildView::new(&context_menu, cx))
menu.update(cx, |menu, cx| {
let items = POSITIONS
.into_iter()
.filter(|position| {
*position != dock_position
&& view.position_is_valid(*position, cx)
})
.map(|position| {
let view = view.clone();
ContextMenuItem::handler(
format!("Dock {}", position.to_label()),
move |cx| view.set_position(position, cx),
)
})
.collect();
menu.show(Default::default(), menu_corner, items, cx);
})
}
})
.with_tooltip::<Self>(
panel_ix,
tooltip,
tooltip_action,
tooltip_style.clone(),
cx,
),
)
.with_child(ChildView::new(&context_menu, cx)),
)
}, },
)) ))
.contained() .contained()
@ -702,8 +705,8 @@ pub mod test {
self.size = size; self.size = size;
} }
fn icon_path(&self) -> &'static str { fn icon_path(&self, _: &WindowContext) -> Option<&'static str> {
"icons/test_panel.svg" Some("icons/test_panel.svg")
} }
fn icon_tooltip(&self) -> (String, Option<Box<dyn Action>>) { fn icon_tooltip(&self) -> (String, Option<Box<dyn Action>>) {

View file

@ -67,6 +67,37 @@ export default function contacts_panel(): any {
return { return {
channel_modal: channel_modal(), channel_modal: channel_modal(),
log_in_button: interactive({
base: {
background: background(theme.middle),
border: border(theme.middle, "active"),
corner_radius: 4,
margin: {
top: 16,
left: 16,
right: 16,
},
padding: {
top: 3,
bottom: 3,
left: 7,
right: 7,
},
...text(theme.middle, "sans", "default", { size: "sm" }),
},
state: {
hovered: {
...text(theme.middle, "sans", "default", { size: "sm" }),
background: background(theme.middle, "hovered"),
border: border(theme.middle, "active"),
},
clicked: {
...text(theme.middle, "sans", "default", { size: "sm" }),
background: background(theme.middle, "pressed"),
border: border(theme.middle, "active"),
},
},
}),
background: background(layer), background: background(layer),
padding: { padding: {
top: 12, top: 12,