Tooltips are now shown above the widget if they do not fit under it.
This commit is contained in:
parent
7c1c775020
commit
934dc42e58
2 changed files with 56 additions and 17 deletions
|
@ -8,28 +8,34 @@ use crate::*;
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub(crate) struct MonoState {
|
pub(crate) struct MonoState {
|
||||||
last_id: Option<Id>,
|
last_id: Option<Id>,
|
||||||
last_size: Option<Vec2>,
|
last_size: Vec<Vec2>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MonoState {
|
impl MonoState {
|
||||||
fn tooltip_size(&self, id: Id) -> Option<Vec2> {
|
fn tooltip_size(&self, id: Id, index: usize) -> Option<Vec2> {
|
||||||
if self.last_id == Some(id) {
|
if self.last_id == Some(id) {
|
||||||
self.last_size
|
self.last_size.get(index).cloned()
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_tooltip_size(&mut self, id: Id, size: Vec2) {
|
fn set_tooltip_size(&mut self, id: Id, index: usize, size: Vec2) {
|
||||||
if self.last_id == Some(id) {
|
if self.last_id == Some(id) {
|
||||||
if let Some(stored_size) = &mut self.last_size {
|
if let Some(stored_size) = self.last_size.get_mut(index) {
|
||||||
*stored_size = stored_size.max(size);
|
*stored_size = size;
|
||||||
return;
|
} else {
|
||||||
|
self.last_size
|
||||||
|
.extend((0..index - self.last_size.len()).map(|_| Vec2::ZERO));
|
||||||
|
self.last_size.push(size);
|
||||||
}
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.last_id = Some(id);
|
self.last_id = Some(id);
|
||||||
self.last_size = Some(size);
|
self.last_size.clear();
|
||||||
|
self.last_size.extend((0..index).map(|_| Vec2::ZERO));
|
||||||
|
self.last_size.push(size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,16 +90,21 @@ pub fn show_tooltip_at_pointer<R>(
|
||||||
show_tooltip_at(ctx, id, suggested_pos, add_contents)
|
show_tooltip_at(ctx, id, suggested_pos, add_contents)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Show a tooltip under the given area.
|
||||||
|
///
|
||||||
|
/// If the tooltip does not fit under the area, it tries to place it above it instead.
|
||||||
pub fn show_tooltip_under<R>(
|
pub fn show_tooltip_under<R>(
|
||||||
ctx: &CtxRef,
|
ctx: &CtxRef,
|
||||||
id: Id,
|
id: Id,
|
||||||
rect: &Rect,
|
rect: &Rect,
|
||||||
add_contents: impl FnOnce(&mut Ui) -> R,
|
add_contents: impl FnOnce(&mut Ui) -> R,
|
||||||
) -> Option<R> {
|
) -> Option<R> {
|
||||||
show_tooltip_at(
|
let expanded_rect = rect.expand2(vec2(2.0, 4.0));
|
||||||
|
show_tooltip_at_avoid(
|
||||||
ctx,
|
ctx,
|
||||||
id,
|
id,
|
||||||
Some(rect.left_bottom() + vec2(-2.0, 4.0)),
|
Some(expanded_rect.left_bottom()),
|
||||||
|
expanded_rect,
|
||||||
add_contents,
|
add_contents,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -103,16 +114,31 @@ pub fn show_tooltip_under<R>(
|
||||||
/// Returns `None` if the tooltip could not be placed.
|
/// Returns `None` if the tooltip could not be placed.
|
||||||
pub fn show_tooltip_at<R>(
|
pub fn show_tooltip_at<R>(
|
||||||
ctx: &CtxRef,
|
ctx: &CtxRef,
|
||||||
mut id: Id,
|
id: Id,
|
||||||
suggested_position: Option<Pos2>,
|
suggested_position: Option<Pos2>,
|
||||||
add_contents: impl FnOnce(&mut Ui) -> R,
|
add_contents: impl FnOnce(&mut Ui) -> R,
|
||||||
) -> Option<R> {
|
) -> Option<R> {
|
||||||
let mut tooltip_rect = Rect::NOTHING;
|
show_tooltip_at_avoid(ctx, id, suggested_position, Rect::NOTHING, add_contents)
|
||||||
|
}
|
||||||
|
|
||||||
let position = if let Some((stored_id, stored_tooltip_rect)) = ctx.frame_state().tooltip_rect {
|
fn show_tooltip_at_avoid<R>(
|
||||||
|
ctx: &CtxRef,
|
||||||
|
mut id: Id,
|
||||||
|
suggested_position: Option<Pos2>,
|
||||||
|
mut avoid_rect: Rect,
|
||||||
|
add_contents: impl FnOnce(&mut Ui) -> R,
|
||||||
|
) -> Option<R> {
|
||||||
|
let mut tooltip_rect = Rect::NOTHING;
|
||||||
|
let mut count = 0;
|
||||||
|
|
||||||
|
let position = if let Some((stored_id, stored_tooltip_rect, stored_count)) =
|
||||||
|
ctx.frame_state().tooltip_rect
|
||||||
|
{
|
||||||
// if there are multiple tooltips open they should use the same id for the `tooltip_size` caching to work.
|
// if there are multiple tooltips open they should use the same id for the `tooltip_size` caching to work.
|
||||||
id = stored_id;
|
id = stored_id;
|
||||||
tooltip_rect = stored_tooltip_rect;
|
tooltip_rect = stored_tooltip_rect;
|
||||||
|
count = stored_count;
|
||||||
|
avoid_rect = avoid_rect.union(tooltip_rect);
|
||||||
tooltip_rect.left_bottom()
|
tooltip_rect.left_bottom()
|
||||||
} else if let Some(position) = suggested_position {
|
} else if let Some(position) = suggested_position {
|
||||||
position
|
position
|
||||||
|
@ -126,18 +152,31 @@ pub fn show_tooltip_at<R>(
|
||||||
.memory()
|
.memory()
|
||||||
.data_temp
|
.data_temp
|
||||||
.get_or_default::<crate::containers::popup::MonoState>()
|
.get_or_default::<crate::containers::popup::MonoState>()
|
||||||
.tooltip_size(id);
|
.tooltip_size(id, count);
|
||||||
let expected_size = expected_size.unwrap_or_else(|| vec2(64.0, 32.0));
|
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.min(ctx.input().screen_rect().right_bottom() - expected_size);
|
||||||
|
// Place the tooltip above the avoid_rect if necessary.
|
||||||
|
let new_rect = Rect::from_min_size(position, expected_size);
|
||||||
|
// Note: We do not use Rect::intersects() since it returns true even if the rects only touch.
|
||||||
|
let position = if avoid_rect.min.x < new_rect.max.x
|
||||||
|
&& new_rect.min.x < avoid_rect.max.x
|
||||||
|
&& avoid_rect.min.y < new_rect.max.y
|
||||||
|
&& new_rect.min.y < avoid_rect.max.y
|
||||||
|
{
|
||||||
|
Pos2::new(position.x, avoid_rect.min.y - expected_size.y)
|
||||||
|
} else {
|
||||||
|
position
|
||||||
|
};
|
||||||
|
|
||||||
let position = position.max(ctx.input().screen_rect().left_top());
|
let position = position.max(ctx.input().screen_rect().left_top());
|
||||||
|
|
||||||
let InnerResponse { inner, response } = show_tooltip_area(ctx, id, position, add_contents);
|
let InnerResponse { inner, response } = show_tooltip_area(ctx, id, position, add_contents);
|
||||||
ctx.memory()
|
ctx.memory()
|
||||||
.data_temp
|
.data_temp
|
||||||
.get_mut_or_default::<crate::containers::popup::MonoState>()
|
.get_mut_or_default::<crate::containers::popup::MonoState>()
|
||||||
.set_tooltip_size(id, response.rect.size());
|
.set_tooltip_size(id, count, response.rect.size());
|
||||||
|
|
||||||
ctx.frame_state().tooltip_rect = Some((id, tooltip_rect.union(response.rect)));
|
ctx.frame_state().tooltip_rect = Some((id, tooltip_rect.union(response.rect), count + 1));
|
||||||
Some(inner)
|
Some(inner)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ pub(crate) struct FrameState {
|
||||||
/// If a tooltip has been shown this frame, where was it?
|
/// If a tooltip has been shown this frame, where was it?
|
||||||
/// This is used to prevent multiple tooltips to cover each other.
|
/// This is used to prevent multiple tooltips to cover each other.
|
||||||
/// Initialized to `None` at the start of each frame.
|
/// Initialized to `None` at the start of each frame.
|
||||||
pub(crate) tooltip_rect: Option<(Id, Rect)>,
|
pub(crate) tooltip_rect: Option<(Id, Rect, usize)>,
|
||||||
|
|
||||||
/// Cleared by the first `ScrollArea` that makes use of it.
|
/// Cleared by the first `ScrollArea` that makes use of it.
|
||||||
pub(crate) scroll_delta: Vec2,
|
pub(crate) scroll_delta: Vec2,
|
||||||
|
|
Loading…
Reference in a new issue