@@ -2110,6 +2110,47 @@ impl CollabPanel {
None
};
+ let button_container = |cx: &mut ViewContext<Self>| {
+ h_stack()
+ .absolute()
+ // We're using a negative coordinate for the right anchor to
+ // counteract the padding of the `ListItem`.
+ //
+ // This prevents a gap from showing up between the background
+ // of this element and the edge of the collab panel.
+ .right(rems(-0.5))
+ // HACK: Without this the channel name clips on top of the icons, but I'm not sure why.
+ .z_index(10)
+ .bg(cx.theme().colors().panel_background)
+ .when(is_selected || is_active, |this| {
+ this.bg(cx.theme().colors().ghost_element_selected)
+ })
+ };
+
+ let messages_button = |cx: &mut ViewContext<Self>| {
+ IconButton::new("channel_chat", Icon::MessageBubbles)
+ .icon_size(IconSize::Small)
+ .icon_color(if has_messages_notification {
+ Color::Default
+ } else {
+ Color::Muted
+ })
+ .on_click(cx.listener(move |this, _, cx| this.join_channel_chat(channel_id, cx)))
+ .tooltip(|cx| Tooltip::text("Open channel chat", cx))
+ };
+
+ let notes_button = |cx: &mut ViewContext<Self>| {
+ IconButton::new("channel_notes", Icon::File)
+ .icon_size(IconSize::Small)
+ .icon_color(if has_notes_notification {
+ Color::Default
+ } else {
+ Color::Muted
+ })
+ .on_click(cx.listener(move |this, _, cx| this.open_channel_notes(channel_id, cx)))
+ .tooltip(|cx| Tooltip::text("Open channel notes", cx))
+ };
+
let width = self.width.unwrap_or(px(240.));
div()
@@ -2172,63 +2213,49 @@ impl CollabPanel {
.child(Label::new(channel.name.clone()))
.children(face_pile.map(|face_pile| face_pile.render(cx))),
)
- .end_slot(
- h_stack()
- .absolute()
- // We're using a negative coordinate for the right anchor to
- // counteract the padding of the `ListItem`.
- //
- // This prevents a gap from showing up between the background
- // of this element and the edge of the collab panel.
- .right(rems(-0.5))
- // HACK: Without this the channel name clips on top of the icons, but I'm not sure why.
- .z_index(10)
- .bg(cx.theme().colors().panel_background)
- .when(is_selected || is_active, |this| {
- this.bg(cx.theme().colors().ghost_element_selected)
- })
- .child(
- h_stack()
- .px_1()
- // The element hover background has a slight transparency to it, so we
- // need to apply it to the inner element so that it blends with the solid
- // background color of the absolutely-positioned element.
- .group_hover("", |style| {
- style.bg(cx.theme().colors().ghost_element_hover)
- })
- .child(
- IconButton::new("channel_chat", Icon::MessageBubbles)
- .icon_size(IconSize::Small)
- .icon_color(if has_messages_notification {
- Color::Default
- } else {
- Color::Muted
- })
- .when(!has_messages_notification, |this| {
- this.visible_on_hover("")
- })
- .on_click(cx.listener(move |this, _, cx| {
- this.join_channel_chat(channel_id, cx)
- }))
- .tooltip(|cx| Tooltip::text("Open channel chat", cx)),
- )
- .child(
- IconButton::new("channel_notes", Icon::File)
- .icon_size(IconSize::Small)
- .icon_color(if has_notes_notification {
- Color::Default
- } else {
- Color::Muted
- })
- .when(!has_notes_notification, |this| {
- this.visible_on_hover("")
- })
- .on_click(cx.listener(move |this, _, cx| {
- this.open_channel_notes(channel_id, cx)
- }))
- .tooltip(|cx| Tooltip::text("Open channel notes", cx)),
- ),
- ),
+ .end_slot::<Div>(
+ // If we have a notification for either button, we want to show the corresponding
+ // button(s) as indicators.
+ if has_messages_notification || has_notes_notification {
+ Some(
+ button_container(cx).child(
+ h_stack()
+ .px_1()
+ .children(
+ // We only want to render the messages button if there are unseen messages.
+ // This way we don't take up any space that might overlap the channel name
+ // when there are no notifications.
+ has_messages_notification.then(|| messages_button(cx)),
+ )
+ .child(
+ // We always want the notes button to take up space to prevent layout
+ // shift when hovering over the channel.
+ // However, if there are is no notes notification we just show an empty slot.
+ notes_button(cx)
+ .when(!has_notes_notification, |this| {
+ this.visible_on_hover("")
+ }),
+ ),
+ ),
+ )
+ } else {
+ None
+ },
+ )
+ .end_hover_slot(
+ // When we hover the channel entry we want to always show both buttons.
+ button_container(cx).child(
+ h_stack()
+ .px_1()
+ // The element hover background has a slight transparency to it, so we
+ // need to apply it to the inner element so that it blends with the solid
+ // background color of the absolutely-positioned element.
+ .group_hover("", |style| {
+ style.bg(cx.theme().colors().ghost_element_hover)
+ })
+ .child(messages_button(cx))
+ .child(notes_button(cx)),
+ ),
),
)
.tooltip(|cx| Tooltip::text("Join channel", cx))
@@ -7,7 +7,7 @@ use crate::{
ToggleCaseSensitive, ToggleReplace, ToggleWholeWord,
};
use collections::HashMap;
-use editor::{Editor, EditorElement, EditorStyle};
+use editor::{Editor, EditorElement, EditorStyle, Tab};
use futures::channel::oneshot;
use gpui::{
actions, div, impl_actions, Action, AppContext, ClickEvent, EventEmitter, FocusableView,
@@ -190,6 +190,7 @@ impl Render for BufferSearchBar {
.w_full()
.gap_2()
.key_context(key_context)
+ .capture_action(cx.listener(Self::tab))
.on_action(cx.listener(Self::previous_history_query))
.on_action(cx.listener(Self::next_history_query))
.on_action(cx.listener(Self::dismiss))
@@ -932,6 +933,14 @@ impl BufferSearchBar {
}
}
+ fn tab(&mut self, _: &Tab, cx: &mut ViewContext<Self>) {
+ if let Some(item) = self.active_searchable_item.as_ref() {
+ let focus_handle = item.focus_handle(cx);
+ cx.focus(&focus_handle);
+ cx.stop_propagation();
+ }
+ }
+
fn next_history_query(&mut self, _: &NextHistoryQuery, cx: &mut ViewContext<Self>) {
if let Some(new_query) = self.search_history.next().map(str::to_string) {
let _ = self.search(&new_query, Some(self.search_options), cx);
@@ -1687,7 +1687,7 @@ impl Pane {
),
)
})
- .when(self.has_focus(cx), |tab_bar| {
+ .when(self.was_focused || self.has_focus(cx), |tab_bar| {
tab_bar.end_child({
let render_tab_buttons = self.render_tab_bar_buttons.clone();
render_tab_buttons(self, cx)