Implement VisibleOnHover for IconButton (#3642)

This PR implements the `VisibleOnHover` trait for `IconButton`s.

I noticed that in a lot of places we were wrapping an `IconButton` in an
extra `div` just so we could call `visible_on_hover` on it. By
implementing the trait on `IconButton` directly it allows us to avoid
the interstitial `div` entirely.

Release Notes:

- N/A
This commit is contained in:
Marshall Bowers 2023-12-13 20:42:27 -05:00 committed by GitHub
parent 15f62a49f7
commit 057b235c56
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 81 additions and 82 deletions

View file

@ -384,7 +384,13 @@ impl ChatPanel {
.right_2() .right_2()
.w_8() .w_8()
.visible_on_hover("") .visible_on_hover("")
.child(render_remove(message_id_to_remove, cx)), .children(message_id_to_remove.map(|message_id| {
IconButton::new(("remove", message_id), Icon::XCircle).on_click(
cx.listener(move |this, _, cx| {
this.remove_message(message_id, cx);
}),
)
})),
) )
.into_any() .into_any()
} }
@ -524,18 +530,6 @@ impl ChatPanel {
} }
} }
fn render_remove(message_id_to_remove: Option<u64>, cx: &mut ViewContext<ChatPanel>) -> AnyElement {
if let Some(message_id) = message_id_to_remove {
IconButton::new(("remove", message_id), Icon::XCircle)
.on_click(cx.listener(move |this, _, cx| {
this.remove_message(message_id, cx);
}))
.into_any_element()
} else {
div().into_any_element()
}
}
impl EventEmitter<Event> for ChatPanel {} impl EventEmitter<Event> for ChatPanel {}
impl Render for ChatPanel { impl Render for ChatPanel {

View file

@ -2289,18 +2289,15 @@ impl CollabPanel {
let button = match section { let button = match section {
Section::ActiveCall => channel_link.map(|channel_link| { Section::ActiveCall => channel_link.map(|channel_link| {
let channel_link_copy = channel_link.clone(); let channel_link_copy = channel_link.clone();
div()
.visible_on_hover("section-header")
.child(
IconButton::new("channel-link", Icon::Copy) IconButton::new("channel-link", Icon::Copy)
.icon_size(IconSize::Small) .icon_size(IconSize::Small)
.size(ButtonSize::None) .size(ButtonSize::None)
.visible_on_hover("section-header")
.on_click(move |_, cx| { .on_click(move |_, cx| {
let item = ClipboardItem::new(channel_link_copy.clone()); let item = ClipboardItem::new(channel_link_copy.clone());
cx.write_to_clipboard(item) cx.write_to_clipboard(item)
}) })
.tooltip(|cx| Tooltip::text("Copy channel link", cx)), .tooltip(|cx| Tooltip::text("Copy channel link", cx))
)
.into_any_element() .into_any_element()
}), }),
Section::Contacts => Some( Section::Contacts => Some(
@ -2380,9 +2377,9 @@ impl CollabPanel {
}) })
.when(!calling, |el| { .when(!calling, |el| {
el.child( el.child(
div().visible_on_hover("").child(
IconButton::new("remove_contact", Icon::Close) IconButton::new("remove_contact", Icon::Close)
.icon_color(Color::Muted) .icon_color(Color::Muted)
.visible_on_hover("")
.tooltip(|cx| Tooltip::text("Remove Contact", cx)) .tooltip(|cx| Tooltip::text("Remove Contact", cx))
.on_click(cx.listener({ .on_click(cx.listener({
let github_login = github_login.clone(); let github_login = github_login.clone();
@ -2390,7 +2387,6 @@ impl CollabPanel {
this.remove_contact(user_id, &github_login, cx); this.remove_contact(user_id, &github_login, cx);
} }
})), })),
),
) )
}), }),
) )
@ -2618,10 +2614,6 @@ impl CollabPanel {
) )
.end_slot( .end_slot(
h_stack() h_stack()
.child(
div()
.id("channel_chat")
.when(!has_messages_notification, |el| el.visible_on_hover(""))
.child( .child(
IconButton::new("channel_chat", Icon::MessageBubbles) IconButton::new("channel_chat", Icon::MessageBubbles)
.icon_color(if has_messages_notification { .icon_color(if has_messages_notification {
@ -2629,16 +2621,14 @@ impl CollabPanel {
} else { } else {
Color::Muted Color::Muted
}) })
.when(!has_messages_notification, |this| {
this.visible_on_hover("")
})
.on_click(cx.listener(move |this, _, cx| { .on_click(cx.listener(move |this, _, cx| {
this.join_channel_chat(channel_id, cx) this.join_channel_chat(channel_id, cx)
})) }))
.tooltip(|cx| Tooltip::text("Open channel chat", cx)), .tooltip(|cx| Tooltip::text("Open channel chat", cx)),
),
) )
.child(
div()
.id("channel_notes")
.when(!has_notes_notification, |el| el.visible_on_hover(""))
.child( .child(
IconButton::new("channel_notes", Icon::File) IconButton::new("channel_notes", Icon::File)
.icon_color(if has_notes_notification { .icon_color(if has_notes_notification {
@ -2646,13 +2636,13 @@ impl CollabPanel {
} else { } else {
Color::Muted Color::Muted
}) })
.when(!has_notes_notification, |this| this.visible_on_hover(""))
.on_click(cx.listener(move |this, _, cx| { .on_click(cx.listener(move |this, _, cx| {
this.open_channel_notes(channel_id, cx) this.open_channel_notes(channel_id, cx)
})) }))
.tooltip(|cx| Tooltip::text("Open channel notes", cx)), .tooltip(|cx| Tooltip::text("Open channel notes", cx)),
), ),
), ),
),
) )
.tooltip(|cx| Tooltip::text("Join channel", cx)) .tooltip(|cx| Tooltip::text("Join channel", cx))

View file

@ -9763,15 +9763,12 @@ pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> Rend
.px_1p5() .px_1p5()
.child(HighlightedLabel::new(line.clone(), highlights.clone())) .child(HighlightedLabel::new(line.clone(), highlights.clone()))
.child( .child(
div() div().border().border_color(gpui::red()).child(
.border()
.border_color(gpui::red())
.visible_on_hover(group_id)
.child(
IconButton::new(copy_id.clone(), Icon::Copy) IconButton::new(copy_id.clone(), Icon::Copy)
.icon_color(Color::Muted) .icon_color(Color::Muted)
.size(ButtonSize::Compact) .size(ButtonSize::Compact)
.style(ButtonStyle::Transparent) .style(ButtonStyle::Transparent)
.visible_on_hover(group_id)
.on_click(cx.listener(move |_, _, cx| write_to_clipboard)) .on_click(cx.listener(move |_, _, cx| write_to_clipboard))
.tooltip(|cx| Tooltip::text("Copy diagnostic message", cx)), .tooltip(|cx| Tooltip::text("Copy diagnostic message", cx)),
), ),

View file

@ -2,7 +2,6 @@ use gpui::{relative, DefiniteLength};
use gpui::{rems, transparent_black, AnyElement, AnyView, ClickEvent, Div, Hsla, Rems, Stateful}; use gpui::{rems, transparent_black, AnyElement, AnyView, ClickEvent, Div, Hsla, Rems, Stateful};
use smallvec::SmallVec; use smallvec::SmallVec;
use crate::h_stack;
use crate::prelude::*; use crate::prelude::*;
pub trait ButtonCommon: Clickable + Disableable { pub trait ButtonCommon: Clickable + Disableable {
@ -250,6 +249,7 @@ impl ButtonSize {
/// This is also used to build the prebuilt buttons. /// This is also used to build the prebuilt buttons.
#[derive(IntoElement)] #[derive(IntoElement)]
pub struct ButtonLike { pub struct ButtonLike {
base: Div,
id: ElementId, id: ElementId,
pub(super) style: ButtonStyle, pub(super) style: ButtonStyle,
pub(super) disabled: bool, pub(super) disabled: bool,
@ -264,6 +264,7 @@ pub struct ButtonLike {
impl ButtonLike { impl ButtonLike {
pub fn new(id: impl Into<ElementId>) -> Self { pub fn new(id: impl Into<ElementId>) -> Self {
Self { Self {
base: div(),
id: id.into(), id: id.into(),
style: ButtonStyle::default(), style: ButtonStyle::default(),
disabled: false, disabled: false,
@ -331,6 +332,13 @@ impl ButtonCommon for ButtonLike {
} }
} }
impl VisibleOnHover for ButtonLike {
fn visible_on_hover(mut self, group_name: impl Into<SharedString>) -> Self {
self.base = self.base.visible_on_hover(group_name);
self
}
}
impl ParentElement for ButtonLike { impl ParentElement for ButtonLike {
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> { fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> {
&mut self.children &mut self.children
@ -341,7 +349,8 @@ impl RenderOnce for ButtonLike {
type Rendered = Stateful<Div>; type Rendered = Stateful<Div>;
fn render(self, cx: &mut WindowContext) -> Self::Rendered { fn render(self, cx: &mut WindowContext) -> Self::Rendered {
h_stack() self.base
.h_flex()
.id(self.id.clone()) .id(self.id.clone())
.group("") .group("")
.flex_none() .flex_none()

View file

@ -98,6 +98,13 @@ impl ButtonCommon for IconButton {
} }
} }
impl VisibleOnHover for IconButton {
fn visible_on_hover(mut self, group_name: impl Into<SharedString>) -> Self {
self.base = self.base.visible_on_hover(group_name);
self
}
}
impl RenderOnce for IconButton { impl RenderOnce for IconButton {
type Rendered = ButtonLike; type Rendered = ButtonLike;

View file

@ -1,13 +1,15 @@
use gpui::{InteractiveElement, SharedString, Styled}; use gpui::{InteractiveElement, SharedString, Styled};
pub trait VisibleOnHover: InteractiveElement + Styled + Sized { pub trait VisibleOnHover {
/// Sets the element to only be visible when the specified group is hovered. /// Sets the element to only be visible when the specified group is hovered.
/// ///
/// Pass `""` as the `group_name` to use the global group. /// Pass `""` as the `group_name` to use the global group.
fn visible_on_hover(self, group_name: impl Into<SharedString>) -> Self;
}
impl<E: InteractiveElement + Styled> VisibleOnHover for E {
fn visible_on_hover(self, group_name: impl Into<SharedString>) -> Self { fn visible_on_hover(self, group_name: impl Into<SharedString>) -> Self {
self.invisible() self.invisible()
.group_hover(group_name, |style| style.visible()) .group_hover(group_name, |style| style.visible())
} }
} }
impl<E: InteractiveElement + Styled> VisibleOnHover for E {}