-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Add Popup and Tooltip, unifying the previous behaviours
#5713
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
Preview available at https://egui-pr-preview.github.io/pr/5713-lucasunify-tooltip-popup |
12cf1b7 to
199db96
Compare
|
While I think it's nice that the ComboBoxes indicate the direction they will open in, it can look somewhat confusing in edge cases: (the second last combobox is bigger so it will wrap while the last one doesn't yet wrap) Since this is also difficult to implement with the new Popup positioning, I'll remove this behavior and always point the arrow down. |
crates/egui/src/containers/popup.rs
Outdated
| /// Similar to [`Align2`] but for aligning something to the outside of some rect. | ||
| /// ```text | ||
| /// ┌───────────┐ ┌────────┐ ┌─────────┐ | ||
| /// │ TOP_START │ │ TOP │ │ TOP_END │ | ||
| /// └───────────┘ └────────┘ └─────────┘ | ||
| /// ┌──────────┐ ┌────────────────────────────────────┐ ┌───────────┐ | ||
| /// │LEFT_START│ │ │ │RIGHT_START│ | ||
| /// └──────────┘ │ │ └───────────┘ | ||
| /// ┌──────────┐ │ │ ┌───────────┐ | ||
| /// │ LEFT │ │ some_rect │ │ RIGHT │ | ||
| /// └──────────┘ │ │ └───────────┘ | ||
| /// ┌──────────┐ │ │ ┌───────────┐ | ||
| /// │ LEFT_END │ │ │ │ RIGHT_END │ | ||
| /// └──────────┘ └────────────────────────────────────┘ └───────────┘ | ||
| /// ┌────────────┐ ┌──────┐ ┌──────────┐ | ||
| /// │BOTTOM_START│ │BOTTOM│ │BOTTOM_END│ | ||
| /// └────────────┘ └──────┘ └──────────┘ | ||
| /// ``` | ||
| /// # egui::__run_test_ui(|ui| { | ||
| /// if ui.ui_contains_pointer() { | ||
| /// egui::show_tooltip_text(ui.ctx(), ui.layer_id(), egui::Id::new("my_tooltip"), "Helpful text"); | ||
| /// } | ||
| /// # }); | ||
| /// ``` | ||
| pub fn show_tooltip_text( | ||
| ctx: &Context, | ||
| parent_layer: LayerId, | ||
| widget_id: Id, | ||
| text: impl Into<WidgetText>, | ||
| ) -> Option<()> { | ||
| show_tooltip(ctx, parent_layer, widget_id, |ui| { | ||
| crate::widgets::Label::new(text).ui(ui); | ||
| }) | ||
| } | ||
| // TODO: Find a better name for Position and PositionAlign | ||
| #[derive(Clone, Copy, Debug, PartialEq, Eq)] | ||
| pub struct PositionAlign(pub Position, pub Align); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The great gojs library models similar conception as two properties of a Spot type in the widget (which they called GraphObject):
- alignment
- alignmentFocus
For examples of positioning see the Panels examples, especially a Panel.Spot type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice, that is a nice way to think about it! I've changed my PositionAlign to be based on two Align2, which feels nicer and allows for more alignments. It's called Align4 now 😄
34b082b to
fc9821e
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a lot of code to review here 😅 I've only looked at a small portion of it so far. I think I need a walk-through
|
|
||
| if parent_ui.input(|i| i.key_pressed(Key::Escape)) || should_close { | ||
| parent_ui.memory_mut(|mem| mem.close_popup()); | ||
| Some(response) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As you said: it would be nice to have a response.closed() flag the user can check. That could also be set by Area, CollapsingPanel, ColorPickerButton etc
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Created another issue for this: #5727
| match open_kind { | ||
| OpenKind::Open | OpenKind::Closed => {} | ||
| OpenKind::Bool(open, close_behavior) => { | ||
| if should_close(close_behavior) { | ||
| *open = false; | ||
| } | ||
| } | ||
| OpenKind::Memory { close_behavior, .. } => { | ||
| if should_close(close_behavior) { | ||
| ctx.memory_mut(|mem| mem.close_popup()); | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be nice with something like
if should_close(close_behavior) {
open_kind.set_open(false);
}…thought maybe for a later PR
| fs.layers | ||
| .entry(parent_layer) | ||
| .or_default() | ||
| .widget_with_tooltip = Some(widget_id); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could maybe get rid of LayerState::open_popups if we also stored which widgets has a popup open (attached to them). That would allow us to simplify and improve the "don't show a tooltip if this widget has a popup open" check in
egui/crates/egui/src/response.rs
Lines 615 to 619 in b36311a
| let any_open_popups = self.ctx.prev_pass_state(|fs| { | |
| fs.layers | |
| .get(&self.layer_id) | |
| .map_or(false, |layer| !layer.open_popups.is_empty()) | |
| }); |
(can be saved for a future PR though)
Continuation of #5713 **Silently breaking changes:** - Menus now close on click by default, this is configurable via `PopupCloseBehavior` **Additional additions:** - `Button::right_text` - `StyleModifier` This is a rewrite of the egui menus, with the following goals: - submenu buttons should work everywhere, in a popup, context menu, area, in some random Ui - remove the menu state from Ui - should work just like the previous menu - ~proper support for keyboard navigation~ - It's better now but requires further work to be perfect - support `PopupCloseBehavior` * Closes #4607 * [x] I have followed the instructions in the PR template
### Related ### What I was curious how my work on egui [menus](emilk/egui#5716) and [popups](emilk/egui#5713) would work in rerun, so I gave it a try. ### Findings so far - ~The new tooltip positioning works well unless we scroll in the scroll area. Maybe we should intersect the widget rect with the clip rect.~ Fixed! https://github.com/user-attachments/assets/54e7ffb1-f0a9-4f2d-8776-e0bea8777903
This introduces new `Tooltip` and `Popup` structs that unify and extend the old popups and tooltips. `Popup` handles the positioning and optionally stores state on whether the popup is open (for click based popups like `ComboBox`, menus, context menus). `Tooltip` is based on `Popup` and handles state of whether the tooltip should be shown (which turns out to be quite complex to handles all the edge cases). Both `Popup` and `Tooltip` can easily be constructed from a `Response` and then customized via builder methods. This also introduces `PositionAlign`, for aligning something outside of a `Rect` (in contrast to `Align2` for aligning inside a `Rect`). But I don't like the name, any suggestions? Inspired by [mui's tooltip positioning](https://mui.com/material-ui/react-tooltip/#positioned-tooltips). * Part of emilk#4607 * [x] I have followed the instructions in the PR template TODOs: - [x] Automatic tooltip positioning based on available space - [x] Review / fix / remove all code TODOs - [x] ~Update the helper fns on `Response` to be consistent in naming and parameters (Some use tooltip, some hover_ui, some take &self, some take self)~ actually, I think the naming and parameter make sense on second thought - [x] Make sure all old code is marked deprecated For discussion during review: - the following check in `show_tooltip_for` still necessary?: ```rust let is_touch_screen = ctx.input(|i| i.any_touches()); let allow_placing_below = !is_touch_screen; // There is a finger below. TODO: Needed? ```
Continuation of emilk#5713 **Silently breaking changes:** - Menus now close on click by default, this is configurable via `PopupCloseBehavior` **Additional additions:** - `Button::right_text` - `StyleModifier` This is a rewrite of the egui menus, with the following goals: - submenu buttons should work everywhere, in a popup, context menu, area, in some random Ui - remove the menu state from Ui - should work just like the previous menu - ~proper support for keyboard navigation~ - It's better now but requires further work to be perfect - support `PopupCloseBehavior` * Closes emilk#4607 * [x] I have followed the instructions in the PR template
This introduces new
TooltipandPopupstructs that unify and extend the old popups and tooltips.Popuphandles the positioning and optionally stores state on whether the popup is open (for click based popups likeComboBox, menus, context menus).Tooltipis based onPopupand handles state of whether the tooltip should be shown (which turns out to be quite complex to handles all the edge cases).Both
PopupandTooltipcan easily be constructed from aResponseand then customized via builder methods.This also introduces
PositionAlign, for aligning something outside of aRect(in contrast toAlign2for aligning inside aRect). But I don't like the name, any suggestions? Inspired by mui's tooltip positioning.TODOs:
Update the helper fns onactually, I think the naming and parameter make sense on second thoughtResponseto be consistent in naming and parameters (Some use tooltip, some hover_ui, some take &self, some take self)For discussion during review:
show_tooltip_forstill necessary?: