1use std::collections::HashMap;
2
3use gpui_kit::{
4 App, ElementId, Entity, Global, WeakEntity, Window, WindowId, base::TooltipOverlay,
5};
6
7#[derive(Default)]
14pub struct TooltipHost {
15 overlays: HashMap<WindowId, WeakEntity<TooltipOverlay>>,
16 requesters: HashMap<WindowId, ElementId>,
21}
22
23impl Global for TooltipHost {}
24
25impl TooltipHost {
26 pub fn register(window: &Window, overlay: &Entity<TooltipOverlay>, cx: &mut App) {
31 let id = window.window_handle().window_id();
32 let overlays = &mut cx.default_global::<Self>().overlays;
33 overlays.retain(|_, overlay| overlay.upgrade().is_some());
34 overlays.insert(id, overlay.downgrade());
35 }
36
37 pub fn overlay(window: &Window, cx: &App) -> Option<Entity<TooltipOverlay>> {
39 let id = window.window_handle().window_id();
40 cx.try_global::<Self>()?.overlays.get(&id)?.upgrade()
41 }
42
43 pub(crate) fn note_requester(window: &Window, trigger: &ElementId, cx: &mut App) {
44 let id = window.window_handle().window_id();
45 cx.default_global::<Self>()
46 .requesters
47 .insert(id, trigger.clone());
48 }
49
50 pub(crate) fn is_requester(window: &Window, trigger: &ElementId, cx: &App) -> bool {
51 let id = window.window_handle().window_id();
52 cx.try_global::<Self>()
53 .and_then(|host| host.requesters.get(&id))
54 .is_some_and(|requester| requester == trigger)
55 }
56}
57
58#[cfg(test)]
59mod tests {
60 use super::*;
61 use gpui_kit::{AppContext as _, Context, IntoElement, Render, TestAppContext, div};
62
63 struct Empty;
64
65 impl Render for Empty {
66 fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
67 div()
68 }
69 }
70
71 #[gpui_kit::test]
72 fn registry_is_per_window_and_weak(cx: &mut TestAppContext) {
73 cx.update(|cx| {
74 gpui_kit::init(cx);
75 crate::init(cx);
76 });
77 let (_, cx) = cx.add_window_view(|_, _| Empty);
78 cx.update(|window, cx| {
79 assert!(TooltipHost::overlay(window, cx).is_none());
80 let overlay = cx.new(|_| TooltipOverlay::new());
81 TooltipHost::register(window, &overlay, cx);
82 assert_eq!(
83 TooltipHost::overlay(window, cx).map(|o| o.entity_id()),
84 Some(overlay.entity_id())
85 );
86 drop(overlay);
87 });
88 cx.update(|window, cx| {
90 assert!(TooltipHost::overlay(window, cx).is_none());
91 });
92 }
93}