Improve the positioning of tooltips
This commit is contained in:
parent
d5bb85b245
commit
4354f7582f
7 changed files with 91 additions and 27 deletions
|
@ -14,6 +14,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||
* Add `egui::plot::Plot` to plot some 2D data
|
||||
* Add `Ui::hyperlink_to(label, url)`.
|
||||
|
||||
### Changed 🔧
|
||||
|
||||
* Improve the positioning of tooltips.
|
||||
|
||||
|
||||
## 0.9.0 - 2021-02-07 - Light Mode and much more
|
||||
|
||||
<img src="media/0.9.0-disabled.gif" width="50%">
|
||||
|
|
|
@ -11,21 +11,47 @@ use crate::*;
|
|||
/// ```
|
||||
/// # let mut ui = egui::Ui::__test();
|
||||
/// if ui.ui_contains_pointer() {
|
||||
/// egui::show_tooltip(ui.ctx(), |ui| {
|
||||
/// egui::show_tooltip(ui.ctx(), egui::Id::new("my_tooltip"), |ui| {
|
||||
/// ui.label("Helpful text");
|
||||
/// });
|
||||
/// }
|
||||
/// ```
|
||||
pub fn show_tooltip(ctx: &CtxRef, add_contents: impl FnOnce(&mut Ui)) {
|
||||
let tooltip_rect = ctx.frame_state().tooltip_rect;
|
||||
pub fn show_tooltip(ctx: &CtxRef, id: Id, add_contents: impl FnOnce(&mut Ui)) {
|
||||
show_tooltip_at_pointer(ctx, id, add_contents)
|
||||
}
|
||||
|
||||
let window_pos = if let Some(tooltip_rect) = tooltip_rect {
|
||||
pub fn show_tooltip_at_pointer(ctx: &CtxRef, id: Id, add_contents: impl FnOnce(&mut Ui)) {
|
||||
let suggested_pos = ctx
|
||||
.input()
|
||||
.pointer
|
||||
.tooltip_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 {
|
||||
// if there are multiple tooltips open they should use the same id for the `tooltip_size` caching to work.
|
||||
id = stored_id;
|
||||
tooltip_rect = stored_tooltip_rect;
|
||||
tooltip_rect.left_bottom()
|
||||
} else if let Some(pointer_pos) = ctx.input().pointer.tooltip_pos() {
|
||||
let expected_size = vec2(ctx.style().spacing.tooltip_width, 32.0);
|
||||
let position = pointer_pos + vec2(16.0, 16.0);
|
||||
let position = position.min(ctx.input().screen_rect().right_bottom() - expected_size);
|
||||
let position = position.max(ctx.input().screen_rect().left_top());
|
||||
} else if let Some(position) = suggested_position {
|
||||
position
|
||||
} else if ctx.memory().everything_is_visible() {
|
||||
Pos2::default()
|
||||
|
@ -33,12 +59,15 @@ pub fn show_tooltip(ctx: &CtxRef, add_contents: impl FnOnce(&mut Ui)) {
|
|||
return; // No good place for a tooltip :(
|
||||
};
|
||||
|
||||
// TODO: default size
|
||||
let id = Id::tooltip();
|
||||
let response = show_tooltip_area(ctx, id, window_pos, add_contents);
|
||||
let expected_size = ctx.memory().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 tooltip_rect = tooltip_rect.unwrap_or(Rect::NOTHING);
|
||||
ctx.frame_state().tooltip_rect = Some(tooltip_rect.union(response.rect));
|
||||
let response = show_tooltip_area(ctx, id, position, add_contents);
|
||||
ctx.memory().set_tooltip_size(id, response.rect.size());
|
||||
|
||||
ctx.frame_state().tooltip_rect = Some((id, tooltip_rect.union(response.rect)));
|
||||
}
|
||||
|
||||
/// Show some text at the current pointer position (if any).
|
||||
|
@ -50,11 +79,11 @@ pub fn show_tooltip(ctx: &CtxRef, add_contents: impl FnOnce(&mut Ui)) {
|
|||
/// ```
|
||||
/// # let mut ui = egui::Ui::__test();
|
||||
/// if ui.ui_contains_pointer() {
|
||||
/// egui::show_tooltip_text(ui.ctx(), "Helpful text");
|
||||
/// egui::show_tooltip_text(ui.ctx(), egui::Id::new("my_tooltip"), "Helpful text");
|
||||
/// }
|
||||
/// ```
|
||||
pub fn show_tooltip_text(ctx: &CtxRef, text: impl Into<String>) {
|
||||
show_tooltip(ctx, |ui| {
|
||||
pub fn show_tooltip_text(ctx: &CtxRef, id: Id, text: impl Into<String>) {
|
||||
show_tooltip(ctx, id, |ui| {
|
||||
ui.add(crate::widgets::Label::new(text));
|
||||
})
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ pub(crate) struct FrameState {
|
|||
/// If a tooltip has been shown this frame, where was it?
|
||||
/// This is used to prevent multiple tooltips to cover each other.
|
||||
/// Initialized to `None` at the start of each frame.
|
||||
pub(crate) tooltip_rect: Option<Rect>,
|
||||
pub(crate) tooltip_rect: Option<(Id, Rect)>,
|
||||
|
||||
/// Cleared by the first `ScrollArea` that makes use of it.
|
||||
pub(crate) scroll_delta: Vec2,
|
||||
|
|
|
@ -32,14 +32,11 @@ use std::hash::Hash;
|
|||
pub struct Id(u64);
|
||||
|
||||
impl Id {
|
||||
pub fn background() -> Self {
|
||||
pub(crate) fn background() -> Self {
|
||||
Self(0)
|
||||
}
|
||||
|
||||
pub fn tooltip() -> Self {
|
||||
Self(1)
|
||||
}
|
||||
|
||||
/// Generate a new `Id` by hashing some source (e.g. a string or integer).
|
||||
pub fn new(source: impl Hash) -> Id {
|
||||
// NOTE: AHasher is NOT suitable for this!
|
||||
use std::collections::hash_map::DefaultHasher;
|
||||
|
@ -49,6 +46,7 @@ impl Id {
|
|||
Id(hasher.finish())
|
||||
}
|
||||
|
||||
/// Generate a new `Id` by hashing the parent `Id` and the given argument.
|
||||
pub fn with(self, child: impl Hash) -> Id {
|
||||
// NOTE: AHasher is NOT suitable for this!
|
||||
use std::collections::hash_map::DefaultHasher;
|
||||
|
|
|
@ -2,7 +2,7 @@ use std::collections::{HashMap, HashSet};
|
|||
|
||||
use crate::{
|
||||
area, collapsing_header, menu, resize, scroll_area, util::Cache, widgets::text_edit, window,
|
||||
Id, LayerId, Pos2, Rect, Style,
|
||||
Id, LayerId, Pos2, Rect, Style, Vec2,
|
||||
};
|
||||
use epaint::color::{Color32, Hsva};
|
||||
|
||||
|
@ -51,6 +51,10 @@ pub struct Memory {
|
|||
#[cfg_attr(feature = "persistence", serde(skip))]
|
||||
popup: Option<Id>,
|
||||
|
||||
/// Used to clamp the tooltip to the screen.
|
||||
#[cfg_attr(feature = "persistence", serde(skip))]
|
||||
tooltip_size: Option<(Id, Vec2)>,
|
||||
|
||||
#[cfg_attr(feature = "persistence", serde(skip))]
|
||||
everything_is_visible: bool,
|
||||
}
|
||||
|
@ -254,6 +258,29 @@ impl Memory {
|
|||
pub fn reset_areas(&mut self) {
|
||||
self.areas = Default::default();
|
||||
}
|
||||
|
||||
pub(crate) fn tooltip_size(&self, id: Id) -> Option<Vec2> {
|
||||
if let Some((stored_id, stored_size)) = self.tooltip_size {
|
||||
if stored_id == id {
|
||||
Some(stored_size)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn set_tooltip_size(&mut self, id: Id, size: Vec2) {
|
||||
if let Some((stored_id, stored_size)) = &mut self.tooltip_size {
|
||||
if *stored_id == id {
|
||||
*stored_size = stored_size.max(size);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
self.tooltip_size = Some((id, size));
|
||||
}
|
||||
}
|
||||
|
||||
/// ## Popups
|
||||
|
|
|
@ -193,7 +193,12 @@ impl Response {
|
|||
if (self.hovered() && self.ctx.input().pointer.tooltip_pos().is_some())
|
||||
|| self.ctx.memory().everything_is_visible()
|
||||
{
|
||||
crate::containers::show_tooltip(&self.ctx, add_contents);
|
||||
crate::containers::show_tooltip_under(
|
||||
&self.ctx,
|
||||
self.id.with("__tooltip"),
|
||||
&self.rect,
|
||||
add_contents,
|
||||
);
|
||||
}
|
||||
self
|
||||
}
|
||||
|
|
|
@ -293,7 +293,7 @@ impl Default for Spacing {
|
|||
text_edit_width: 280.0,
|
||||
icon_width: 16.0,
|
||||
icon_spacing: 0.0,
|
||||
tooltip_width: 400.0,
|
||||
tooltip_width: 600.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -505,7 +505,7 @@ impl Spacing {
|
|||
ui.add(Slider::f32(text_edit_width, 0.0..=1000.0).text("text_edit_width"));
|
||||
ui.add(Slider::f32(icon_width, 0.0..=60.0).text("icon_width"));
|
||||
ui.add(Slider::f32(icon_spacing, 0.0..=10.0).text("icon_spacing"));
|
||||
ui.add(Slider::f32(tooltip_width, 0.0..=10.0).text("tooltip_width"));
|
||||
ui.add(Slider::f32(tooltip_width, 0.0..=1000.0).text("tooltip_width"));
|
||||
|
||||
ui.vertical_centered(|ui| reset_button(ui, self));
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue