Fix profile menu hover flickering due to documentation asides (#29958)

Fixes https://github.com/zed-industries/zed/issues/29909 

🍐'd with @nathansobo 

Release Notes:

- N/A

---------

Co-authored-by: Nathan <nathan@zed.dev>
This commit is contained in:
Mikayla Maki 2025-05-05 16:05:47 -07:00 committed by GitHub
parent 2aa06d1d0f
commit b214c9e4a8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 131 additions and 70 deletions

View file

@ -50,7 +50,7 @@ pub struct ContextMenuEntry {
handler: Rc<dyn Fn(Option<&FocusHandle>, &mut Window, &mut App)>,
action: Option<Box<dyn Action>>,
disabled: bool,
documentation_aside: Option<Rc<dyn Fn(&mut App) -> AnyElement>>,
documentation_aside: Option<DocumentationAside>,
end_slot_icon: Option<IconName>,
end_slot_title: Option<SharedString>,
end_slot_handler: Option<Rc<dyn Fn(Option<&FocusHandle>, &mut Window, &mut App)>>,
@ -124,9 +124,14 @@ impl ContextMenuEntry {
pub fn documentation_aside(
mut self,
element: impl Fn(&mut App) -> AnyElement + 'static,
side: DocumentationSide,
render: impl Fn(&mut App) -> AnyElement + 'static,
) -> Self {
self.documentation_aside = Some(Rc::new(element));
self.documentation_aside = Some(DocumentationAside {
side,
render: Rc::new(render),
});
self
}
}
@ -150,10 +155,22 @@ pub struct ContextMenu {
_on_blur_subscription: Subscription,
keep_open_on_confirm: bool,
eager: bool,
documentation_aside: Option<(usize, Rc<dyn Fn(&mut App) -> AnyElement>)>,
documentation_aside: Option<(usize, DocumentationAside)>,
fixed_width: Option<DefiniteLength>,
}
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum DocumentationSide {
Left,
Right,
}
#[derive(Clone)]
pub struct DocumentationAside {
side: DocumentationSide,
render: Rc<dyn Fn(&mut App) -> AnyElement>,
}
impl Focusable for ContextMenu {
fn focus_handle(&self, _cx: &App) -> FocusHandle {
self.focus_handle.clone()
@ -933,27 +950,19 @@ impl ContextMenu {
.into_any_element()
};
let documentation_aside_callback = documentation_aside.clone();
div()
.id(("context-menu-child", ix))
.when_some(
documentation_aside_callback.clone(),
|this, documentation_aside_callback| {
this.occlude()
.on_hover(cx.listener(move |menu, hovered, _, cx| {
if *hovered {
menu.documentation_aside =
Some((ix, documentation_aside_callback.clone()));
cx.notify();
} else if matches!(menu.documentation_aside, Some((id, _)) if id == ix)
{
menu.documentation_aside = None;
cx.notify();
}
}))
},
)
.when_some(documentation_aside.clone(), |this, documentation_aside| {
this.occlude()
.on_hover(cx.listener(move |menu, hovered, _, cx| {
if *hovered {
menu.documentation_aside = Some((ix, documentation_aside.clone()));
} else if matches!(menu.documentation_aside, Some((id, _)) if id == ix) {
menu.documentation_aside = None;
}
cx.notify();
}))
})
.child(
ListItem::new(ix)
.group_name("label_container")
@ -992,21 +1001,18 @@ impl ContextMenu {
})
.map(|binding| {
div().ml_4().child(binding.disabled(*disabled)).when(
*disabled && documentation_aside_callback.is_some(),
*disabled && documentation_aside.is_some(),
|parent| parent.invisible(),
)
})
}))
.when(
*disabled && documentation_aside_callback.is_some(),
|parent| {
parent.child(
Icon::new(IconName::Info)
.size(IconSize::XSmall)
.color(Color::Muted),
)
},
),
.when(*disabled && documentation_aside.is_some(), |parent| {
parent.child(
Icon::new(IconName::Info)
.size(IconSize::XSmall)
.color(Color::Muted),
)
}),
)
.when_some(
end_slot_icon
@ -1108,10 +1114,17 @@ impl Render for ContextMenu {
let rem_size = window.rem_size();
let is_wide_window = window_size.width / rem_size > rems_from_px(800.).0;
let aside = self
.documentation_aside
.as_ref()
.map(|(_, callback)| callback.clone());
let aside = self.documentation_aside.clone();
let render_aside = |aside: DocumentationAside, cx: &mut Context<Self>| {
WithRemSize::new(ui_font_size)
.occlude()
.elevation_2(cx)
.p_2()
.overflow_hidden()
.when(is_wide_window, |this| this.max_w_96())
.when(!is_wide_window, |this| this.max_w_48())
.child((aside.render)(cx))
};
h_flex()
.when(is_wide_window, |this| this.flex_row())
@ -1119,15 +1132,8 @@ impl Render for ContextMenu {
.w_full()
.items_start()
.gap_1()
.child(div().children(aside.map(|aside| {
WithRemSize::new(ui_font_size)
.occlude()
.elevation_2(cx)
.p_2()
.overflow_hidden()
.when(is_wide_window, |this| this.max_w_96())
.when(!is_wide_window, |this| this.max_w_48())
.child(aside(cx))
.child(div().children(aside.clone().and_then(|(_, aside)| {
(aside.side == DocumentationSide::Left).then(|| render_aside(aside, cx))
})))
.child(
WithRemSize::new(ui_font_size)
@ -1185,5 +1191,8 @@ impl Render for ContextMenu {
),
),
)
.child(div().children(aside.and_then(|(_, aside)| {
(aside.side == DocumentationSide::Right).then(|| render_aside(aside, cx))
})))
}
}