|
|
@ -6,6 +6,29 @@ use crate::*; |
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
fn when_was_a_toolip_last_shown_id() -> Id { |
|
|
|
Id::new("when_was_a_toolip_last_shown") |
|
|
|
} |
|
|
|
|
|
|
|
pub fn seconds_since_last_tooltip(ctx: &Context) -> f32 { |
|
|
|
let when_was_a_toolip_last_shown = |
|
|
|
ctx.data(|d| d.get_temp::<f64>(when_was_a_toolip_last_shown_id())); |
|
|
|
|
|
|
|
if let Some(when_was_a_toolip_last_shown) = when_was_a_toolip_last_shown { |
|
|
|
let now = ctx.input(|i| i.time); |
|
|
|
(now - when_was_a_toolip_last_shown) as f32 |
|
|
|
} else { |
|
|
|
f32::INFINITY |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
fn remember_that_tooltip_was_shown(ctx: &Context) { |
|
|
|
let now = ctx.input(|i| i.time); |
|
|
|
ctx.data_mut(|data| data.insert_temp::<f64>(when_was_a_toolip_last_shown_id(), now)); |
|
|
|
} |
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
/// Show a tooltip at the current pointer position (if any).
|
|
|
|
///
|
|
|
|
/// Most of the time it is easier to use [`Response::on_hover_ui`].
|
|
|
@ -123,14 +146,16 @@ fn show_tooltip_at_dyn<'c, R>( |
|
|
|
widget_rect = transform * widget_rect; |
|
|
|
} |
|
|
|
|
|
|
|
// if there are multiple tooltips open they should use the same common_id for the `tooltip_size` caching to work.
|
|
|
|
remember_that_tooltip_was_shown(ctx); |
|
|
|
|
|
|
|
let mut state = ctx.frame_state_mut(|fs| { |
|
|
|
// Remember that this is the widget showing the tooltip:
|
|
|
|
fs.tooltip_state |
|
|
|
.per_layer_tooltip_widget |
|
|
|
.insert(parent_layer, widget_id); |
|
|
|
fs.layers |
|
|
|
.entry(parent_layer) |
|
|
|
.or_default() |
|
|
|
.widget_with_tooltip = Some(widget_id); |
|
|
|
|
|
|
|
fs.tooltip_state |
|
|
|
fs.tooltips |
|
|
|
.widget_tooltips |
|
|
|
.get(&widget_id) |
|
|
|
.copied() |
|
|
@ -174,7 +199,7 @@ fn show_tooltip_at_dyn<'c, R>( |
|
|
|
|
|
|
|
state.tooltip_count += 1; |
|
|
|
state.bounding_rect = state.bounding_rect.union(response.rect); |
|
|
|
ctx.frame_state_mut(|fs| fs.tooltip_state.widget_tooltips.insert(widget_id, state)); |
|
|
|
ctx.frame_state_mut(|fs| fs.tooltips.widget_tooltips.insert(widget_id, state)); |
|
|
|
|
|
|
|
inner |
|
|
|
} |
|
|
@ -182,7 +207,7 @@ fn show_tooltip_at_dyn<'c, R>( |
|
|
|
/// What is the id of the next tooltip for this widget?
|
|
|
|
pub fn next_tooltip_id(ctx: &Context, widget_id: Id) -> Id { |
|
|
|
let tooltip_count = ctx.frame_state(|fs| { |
|
|
|
fs.tooltip_state |
|
|
|
fs.tooltips |
|
|
|
.widget_tooltips |
|
|
|
.get(&widget_id) |
|
|
|
.map_or(0, |state| state.tooltip_count) |
|
|
@ -351,53 +376,61 @@ pub fn popup_above_or_below_widget<R>( |
|
|
|
close_behavior: PopupCloseBehavior, |
|
|
|
add_contents: impl FnOnce(&mut Ui) -> R, |
|
|
|
) -> Option<R> { |
|
|
|
if parent_ui.memory(|mem| mem.is_popup_open(popup_id)) { |
|
|
|
let (mut pos, pivot) = match above_or_below { |
|
|
|
AboveOrBelow::Above => (widget_response.rect.left_top(), Align2::LEFT_BOTTOM), |
|
|
|
AboveOrBelow::Below => (widget_response.rect.left_bottom(), Align2::LEFT_TOP), |
|
|
|
}; |
|
|
|
if let Some(transform) = parent_ui |
|
|
|
.ctx() |
|
|
|
.memory(|m| m.layer_transforms.get(&parent_ui.layer_id()).copied()) |
|
|
|
{ |
|
|
|
pos = transform * pos; |
|
|
|
} |
|
|
|
if !parent_ui.memory(|mem| mem.is_popup_open(popup_id)) { |
|
|
|
return None; |
|
|
|
} |
|
|
|
|
|
|
|
let (mut pos, pivot) = match above_or_below { |
|
|
|
AboveOrBelow::Above => (widget_response.rect.left_top(), Align2::LEFT_BOTTOM), |
|
|
|
AboveOrBelow::Below => (widget_response.rect.left_bottom(), Align2::LEFT_TOP), |
|
|
|
}; |
|
|
|
if let Some(transform) = parent_ui |
|
|
|
.ctx() |
|
|
|
.memory(|m| m.layer_transforms.get(&parent_ui.layer_id()).copied()) |
|
|
|
{ |
|
|
|
pos = transform * pos; |
|
|
|
} |
|
|
|
|
|
|
|
let frame = Frame::popup(parent_ui.style()); |
|
|
|
let frame_margin = frame.total_margin(); |
|
|
|
let inner_width = widget_response.rect.width() - frame_margin.sum().x; |
|
|
|
|
|
|
|
parent_ui.ctx().frame_state_mut(|fs| { |
|
|
|
fs.layers |
|
|
|
.entry(parent_ui.layer_id()) |
|
|
|
.or_default() |
|
|
|
.open_popups |
|
|
|
.insert(popup_id) |
|
|
|
}); |
|
|
|
|
|
|
|
let frame = Frame::popup(parent_ui.style()); |
|
|
|
let frame_margin = frame.total_margin(); |
|
|
|
let inner_width = widget_response.rect.width() - frame_margin.sum().x; |
|
|
|
|
|
|
|
let response = Area::new(popup_id) |
|
|
|
.kind(UiKind::Popup) |
|
|
|
.order(Order::Foreground) |
|
|
|
.fixed_pos(pos) |
|
|
|
.default_width(inner_width) |
|
|
|
.pivot(pivot) |
|
|
|
.show(parent_ui.ctx(), |ui| { |
|
|
|
frame |
|
|
|
.show(ui, |ui| { |
|
|
|
ui.with_layout(Layout::top_down_justified(Align::LEFT), |ui| { |
|
|
|
ui.set_min_width(inner_width); |
|
|
|
add_contents(ui) |
|
|
|
}) |
|
|
|
.inner |
|
|
|
let response = Area::new(popup_id) |
|
|
|
.kind(UiKind::Popup) |
|
|
|
.order(Order::Foreground) |
|
|
|
.fixed_pos(pos) |
|
|
|
.default_width(inner_width) |
|
|
|
.pivot(pivot) |
|
|
|
.show(parent_ui.ctx(), |ui| { |
|
|
|
frame |
|
|
|
.show(ui, |ui| { |
|
|
|
ui.with_layout(Layout::top_down_justified(Align::LEFT), |ui| { |
|
|
|
ui.set_min_width(inner_width); |
|
|
|
add_contents(ui) |
|
|
|
}) |
|
|
|
.inner |
|
|
|
}); |
|
|
|
|
|
|
|
let should_close = match close_behavior { |
|
|
|
PopupCloseBehavior::CloseOnClick => widget_response.clicked_elsewhere(), |
|
|
|
PopupCloseBehavior::CloseOnClickOutside => { |
|
|
|
widget_response.clicked_elsewhere() && response.response.clicked_elsewhere() |
|
|
|
} |
|
|
|
PopupCloseBehavior::IgnoreClicks => false, |
|
|
|
}; |
|
|
|
|
|
|
|
if parent_ui.input(|i| i.key_pressed(Key::Escape)) || should_close { |
|
|
|
parent_ui.memory_mut(|mem| mem.close_popup()); |
|
|
|
}) |
|
|
|
.inner |
|
|
|
}); |
|
|
|
|
|
|
|
let should_close = match close_behavior { |
|
|
|
PopupCloseBehavior::CloseOnClick => widget_response.clicked_elsewhere(), |
|
|
|
PopupCloseBehavior::CloseOnClickOutside => { |
|
|
|
widget_response.clicked_elsewhere() && response.response.clicked_elsewhere() |
|
|
|
} |
|
|
|
Some(response.inner) |
|
|
|
} else { |
|
|
|
None |
|
|
|
PopupCloseBehavior::IgnoreClicks => false, |
|
|
|
}; |
|
|
|
|
|
|
|
if parent_ui.input(|i| i.key_pressed(Key::Escape)) || should_close { |
|
|
|
parent_ui.memory_mut(|mem| mem.close_popup()); |
|
|
|
} |
|
|
|
Some(response.inner) |
|
|
|
} |
|
|
|