use crate::*;
#[derive(Clone, Debug, Default)]
pub(crate) struct MonoState {
last_id: Option<Id>,
last_size: Option<Vec2>,
}
impl MonoState {
fn tooltip_size(&self, id: Id) -> Option<Vec2> {
if self.last_id == Some(id) {
self.last_size
} else {
None
}
}
fn set_tooltip_size(&mut self, id: Id, size: Vec2) {
if self.last_id == Some(id) {
if let Some(stored_size) = &mut self.last_size {
*stored_size = stored_size.max(size);
return;
}
}
self.last_id = Some(id);
self.last_size = Some(size);
}
}
pub fn show_tooltip(ctx: &CtxRef, id: Id, add_contents: impl FnOnce(&mut Ui)) {
show_tooltip_at_pointer(ctx, id, add_contents)
}
pub fn show_tooltip_at_pointer(ctx: &CtxRef, id: Id, add_contents: impl FnOnce(&mut Ui)) {
let suggested_pos = ctx
.input()
.pointer
.hover_pos()
.map(|pointer_pos| pointer_pos + vec2(16.0, 16.0));
show_tooltip_at(ctx, id, suggested_pos, add_contents)
}
pub fn show_tooltip_under(ctx: &CtxRef, id: Id, rect: &Rect, add_contents: impl FnOnce(&mut Ui)) {
show_tooltip_at(
ctx,
id,
Some(rect.left_bottom() + vec2(-2.0, 4.0)),
add_contents,
)
}
pub fn show_tooltip_at(
ctx: &CtxRef,
mut id: Id,
suggested_position: Option<Pos2>,
add_contents: impl FnOnce(&mut Ui),
) {
let mut tooltip_rect = Rect::NOTHING;
let position = if let Some((stored_id, stored_tooltip_rect)) = ctx.frame_state().tooltip_rect {
id = stored_id;
tooltip_rect = stored_tooltip_rect;
tooltip_rect.left_bottom()
} else if let Some(position) = suggested_position {
position
} else if ctx.memory().everything_is_visible() {
Pos2::default()
} else {
return; };
let expected_size = ctx
.memory()
.data_temp
.get_or_default::<crate::containers::popup::MonoState>()
.tooltip_size(id);
let expected_size = expected_size.unwrap_or_else(|| vec2(64.0, 32.0));
let position = position.min(ctx.input().screen_rect().right_bottom() - expected_size);
let position = position.max(ctx.input().screen_rect().left_top());
let response = show_tooltip_area(ctx, id, position, add_contents);
ctx.memory()
.data_temp
.get_mut_or_default::<crate::containers::popup::MonoState>()
.set_tooltip_size(id, response.rect.size());
ctx.frame_state().tooltip_rect = Some((id, tooltip_rect.union(response.rect)));
}
pub fn show_tooltip_text(ctx: &CtxRef, id: Id, text: impl ToString) {
show_tooltip(ctx, id, |ui| {
ui.add(crate::widgets::Label::new(text));
})
}
fn show_tooltip_area(
ctx: &CtxRef,
id: Id,
window_pos: Pos2,
add_contents: impl FnOnce(&mut Ui),
) -> Response {
use containers::*;
Area::new(id)
.order(Order::Tooltip)
.fixed_pos(window_pos)
.interactable(false)
.show(ctx, |ui| {
Frame::popup(&ctx.style()).show(ui, |ui| {
ui.set_max_width(ui.spacing().tooltip_width);
add_contents(ui);
});
})
}
pub fn popup_below_widget(
ui: &Ui,
popup_id: Id,
widget_response: &Response,
add_contents: impl FnOnce(&mut Ui),
) {
if ui.memory().is_popup_open(popup_id) {
let parent_clip_rect = ui.clip_rect();
Area::new(popup_id)
.order(Order::Foreground)
.fixed_pos(widget_response.rect.left_bottom())
.show(ui.ctx(), |ui| {
ui.set_clip_rect(parent_clip_rect); let frame = Frame::popup(ui.style());
let frame_margin = frame.margin;
frame.show(ui, |ui| {
ui.with_layout(Layout::top_down_justified(Align::LEFT), |ui| {
ui.set_width(widget_response.rect.width() - 2.0 * frame_margin.x);
add_contents(ui)
});
});
});
if ui.input().key_pressed(Key::Escape) || widget_response.clicked_elsewhere() {
ui.memory().close_popup();
}
}
}