use std::rc::Rc;
use gpui::{
prelude::FluentBuilder as _, px, App, AppContext as _, Bounds, Context, Empty, Entity,
IntoElement, Pixels, Point, Render, Styled, Window,
};
use crate::{
highlighter::DiagnosticEntry,
input::{
popovers::{render_markdown, Popover},
InputState,
},
};
pub struct DiagnosticPopover {
state: Entity<InputState>,
pub(crate) diagnostic: Rc<DiagnosticEntry>,
bounds: Bounds<Pixels>,
open: bool,
}
impl DiagnosticPopover {
pub fn new(
diagnostic: &DiagnosticEntry,
state: Entity<InputState>,
cx: &mut App,
) -> Entity<Self> {
let diagnostic = Rc::new(diagnostic.clone());
cx.new(|_| Self {
diagnostic,
state,
bounds: Bounds::default(),
open: true,
})
}
pub(crate) fn show(&mut self, cx: &mut Context<Self>) {
self.open = true;
cx.notify();
}
pub(crate) fn hide(&mut self, cx: &mut Context<Self>) {
self.open = false;
cx.notify();
}
pub(crate) fn check_to_hide(&mut self, mouse_position: Point<Pixels>, cx: &mut Context<Self>) {
if !self.open {
return;
}
let padding = px(5.);
let bounds = Bounds {
origin: self.bounds.origin.map(|v| v - padding),
size: self.bounds.size.map(|v| v + padding * 2.),
};
if !bounds.contains(&mouse_position) {
self.hide(cx);
}
}
}
impl Render for DiagnosticPopover {
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
if !self.open {
return Empty.into_any_element();
}
let message = self.diagnostic.message.clone();
let (border, bg, fg) = (
self.diagnostic.severity.border(cx),
self.diagnostic.severity.bg(cx),
self.diagnostic.severity.fg(cx),
);
Popover::new(
"diagnostic-popover",
self.state.clone(),
self.diagnostic.range.clone(),
move |window, cx| render_markdown("message", message.clone(), window, cx),
)
.when(!self.open, |this| this.invisible())
.px_1()
.py_0p5()
.bg(bg)
.text_color(fg)
.border_1()
.border_color(border)
.into_any_element()
}
}