Skip to content

Commit

Permalink
Better align menus with the button that opened them (#4233)
Browse files Browse the repository at this point in the history
<img width="530" alt="Screenshot 2024-03-26 at 10 42 46"
src="https://github.com/emilk/egui/assets/1148717/4891047d-42a1-45b6-9363-c6ac93cefc8d">
<img width="268" alt="Screenshot 2024-03-26 at 10 42 57"
src="https://github.com/emilk/egui/assets/1148717/98865f38-10cc-4cbe-a80b-a767415e1469">
  • Loading branch information
emilk authored Mar 26, 2024
1 parent c530504 commit 9cfaf8b
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 22 deletions.
50 changes: 29 additions & 21 deletions crates/egui/src/menu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,11 @@ impl BarState {
/// Should be called from [`Context`] on a [`Response`]
pub fn bar_menu<R>(
&mut self,
response: &Response,
button: &Response,
add_contents: impl FnOnce(&mut Ui) -> R,
) -> Option<InnerResponse<R>> {
MenuRoot::stationary_click_interaction(response, &mut self.open_menu);
self.open_menu.show(response, add_contents)
MenuRoot::stationary_click_interaction(button, &mut self.open_menu);
self.open_menu.show(button, add_contents)
}

pub(crate) fn has_root(&self) -> bool {
Expand Down Expand Up @@ -251,11 +251,11 @@ impl MenuRootManager {
/// Should be called from [`Context`] on a [`Response`]
pub fn show<R>(
&mut self,
response: &Response,
button: &Response,
add_contents: impl FnOnce(&mut Ui) -> R,
) -> Option<InnerResponse<R>> {
if let Some(root) = self.inner.as_mut() {
let (menu_response, inner_response) = root.show(response, add_contents);
let (menu_response, inner_response) = root.show(button, add_contents);
if MenuResponse::Close == menu_response {
self.inner = None;
}
Expand Down Expand Up @@ -301,12 +301,12 @@ impl MenuRoot {

pub fn show<R>(
&mut self,
response: &Response,
button: &Response,
add_contents: impl FnOnce(&mut Ui) -> R,
) -> (MenuResponse, Option<InnerResponse<R>>) {
if self.id == response.id {
if self.id == button.id {
let inner_response =
MenuState::show(&response.ctx, &self.menu_state, self.id, add_contents);
MenuState::show(&button.ctx, &self.menu_state, self.id, add_contents);
let menu_state = self.menu_state.read();

if menu_state.response.is_close() {
Expand All @@ -319,26 +319,31 @@ impl MenuRoot {
/// Interaction with a stationary menu, i.e. fixed in another Ui.
///
/// Responds to primary clicks.
fn stationary_interaction(response: &Response, root: &mut MenuRootManager) -> MenuResponse {
let id = response.id;
fn stationary_interaction(button: &Response, root: &mut MenuRootManager) -> MenuResponse {
let id = button.id;

if (response.clicked() && root.is_menu_open(id))
|| response.ctx.input(|i| i.key_pressed(Key::Escape))
if (button.clicked() && root.is_menu_open(id))
|| button.ctx.input(|i| i.key_pressed(Key::Escape))
{
// menu open and button clicked or esc pressed
return MenuResponse::Close;
} else if (response.clicked() && !root.is_menu_open(id))
|| (response.hovered() && root.is_some())
} else if (button.clicked() && !root.is_menu_open(id))
|| (button.hovered() && root.is_some())
{
// menu not open and button clicked
// or button hovered while other menu is open
let mut pos = response.rect.left_bottom();
let mut pos = button.rect.left_bottom();

let menu_frame = Frame::menu(&button.ctx.style());
pos.x -= menu_frame.total_margin().left; // Make fist button in menu align with the parent button
pos.y += button.ctx.style().spacing.menu_spacing;

if let Some(root) = root.inner.as_mut() {
let menu_rect = root.menu_state.read().rect;
let screen_rect = response.ctx.input(|i| i.screen_rect);
let screen_rect = button.ctx.input(|i| i.screen_rect);

if pos.y + menu_rect.height() > screen_rect.max.y {
pos.y = screen_rect.max.y - menu_rect.height() - response.rect.height();
pos.y = screen_rect.max.y - menu_rect.height() - button.rect.height();
}

if pos.x + menu_rect.width() > screen_rect.max.x {
Expand All @@ -347,11 +352,11 @@ impl MenuRoot {
}

return MenuResponse::Create(pos, id);
} else if response
} else if button
.ctx
.input(|i| i.pointer.any_pressed() && i.pointer.primary_down())
{
if let Some(pos) = response.ctx.input(|i| i.pointer.interact_pos()) {
if let Some(pos) = button.ctx.input(|i| i.pointer.interact_pos()) {
if let Some(root) = root.inner.as_mut() {
if root.id == id {
// pressed somewhere while this menu is open
Expand Down Expand Up @@ -410,8 +415,8 @@ impl MenuRoot {
}

// Responds to primary clicks.
pub fn stationary_click_interaction(response: &Response, root: &mut MenuRootManager) {
let menu_response = Self::stationary_interaction(response, root);
pub fn stationary_click_interaction(button: &Response, root: &mut MenuRootManager) {
let menu_response = Self::stationary_interaction(button, root);
Self::handle_menu_response(root, menu_response);
}
}
Expand Down Expand Up @@ -623,8 +628,11 @@ impl MenuState {
// ensure to repaint once even when pointer is not moving
ui.ctx().request_repaint();
} else if !open && button.hovered() {
// TODO(emilk): open menu to the left if there isn't enough space to the right
let mut pos = button.rect.right_top();
pos.x = self.rect.right() + ui.spacing().menu_spacing;
pos.y -= Frame::menu(ui.style()).total_margin().top; // align the first button in the submenu with the parent button

self.open_submenu(sub_id, pos);
} else if open
&& ui.interact_bg(Sense::hover()).contains_pointer()
Expand Down
2 changes: 1 addition & 1 deletion crates/egui/src/style.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1033,7 +1033,7 @@ impl Default for Spacing {
icon_spacing: 4.0,
tooltip_width: 600.0,
menu_width: 150.0,
menu_spacing: 3.0,
menu_spacing: 2.0,
combo_height: 200.0,
scroll: Default::default(),
indent_ends_with_horizontal_line: false,
Expand Down

0 comments on commit 9cfaf8b

Please sign in to comment.