From b4a205b37c1b1ad4d4c400b43dd85faa7e929f38 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Wed, 3 Jan 2024 13:07:21 +0100 Subject: [PATCH 1/3] Pane: Fix flicker when opening/closing tabs Tab bar was losing focus for one frame which led to it skipping rendering of tab controls & flickering. It also occured when opening new tabs. --- crates/workspace2/src/pane.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/workspace2/src/pane.rs b/crates/workspace2/src/pane.rs index 9e620d993a345cb48bdd9dcfa056c61c0448e260..594c2a0b7878bc8260471b5e254fd953d3a3cd33 100644 --- a/crates/workspace2/src/pane.rs +++ b/crates/workspace2/src/pane.rs @@ -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) From f73f735d41f18837b263d30bfadbb0c240ef7292 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Wed, 3 Jan 2024 10:31:01 -0500 Subject: [PATCH 2/3] Polish off channel buttons (#3858) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR polishes off the channel buttons such that they behave as expected. The absolutely-positioned button container will now no longer take up space—and thus obscure the channel name—when there are no notifications and the channel entry is not hovered. Release Notes: - N/A --- crates/collab_ui2/src/collab_panel.rs | 141 +++++++++++++++----------- 1 file changed, 84 insertions(+), 57 deletions(-) diff --git a/crates/collab_ui2/src/collab_panel.rs b/crates/collab_ui2/src/collab_panel.rs index 45129fa790134bb024cc45b7e6a205bddb20b2ad..c8119d21279571de3f664e72d61bbbf33fc4cdd9 100644 --- a/crates/collab_ui2/src/collab_panel.rs +++ b/crates/collab_ui2/src/collab_panel.rs @@ -2110,6 +2110,47 @@ impl CollabPanel { None }; + let button_container = |cx: &mut ViewContext| { + 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| { + 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| { + 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::
( + // 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)) From 209fe7e0faf02bb1cedfbd839f940249de129b4d Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 3 Jan 2024 16:39:58 +0100 Subject: [PATCH 3/3] Focus active item when pressing tab in buffer search bar --- crates/search2/src/buffer_search.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/crates/search2/src/buffer_search.rs b/crates/search2/src/buffer_search.rs index 96b40aa2bfc229af7f0e143a2d355ce377a55161..be01266eef3830fe183d97d5d4a5afe444e856c0 100644 --- a/crates/search2/src/buffer_search.rs +++ b/crates/search2/src/buffer_search.rs @@ -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) { + 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) { if let Some(new_query) = self.search_history.next().map(str::to_string) { let _ = self.search(&new_query, Some(self.search_options), cx);