Better align menus with the button that opened them (#4233)
<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">
This commit is contained in:
parent
c530504a04
commit
9cfaf8b961
|
|
@ -43,11 +43,11 @@ impl BarState {
|
||||||
/// Should be called from [`Context`] on a [`Response`]
|
/// Should be called from [`Context`] on a [`Response`]
|
||||||
pub fn bar_menu<R>(
|
pub fn bar_menu<R>(
|
||||||
&mut self,
|
&mut self,
|
||||||
response: &Response,
|
button: &Response,
|
||||||
add_contents: impl FnOnce(&mut Ui) -> R,
|
add_contents: impl FnOnce(&mut Ui) -> R,
|
||||||
) -> Option<InnerResponse<R>> {
|
) -> Option<InnerResponse<R>> {
|
||||||
MenuRoot::stationary_click_interaction(response, &mut self.open_menu);
|
MenuRoot::stationary_click_interaction(button, &mut self.open_menu);
|
||||||
self.open_menu.show(response, add_contents)
|
self.open_menu.show(button, add_contents)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn has_root(&self) -> bool {
|
pub(crate) fn has_root(&self) -> bool {
|
||||||
|
|
@ -251,11 +251,11 @@ impl MenuRootManager {
|
||||||
/// Should be called from [`Context`] on a [`Response`]
|
/// Should be called from [`Context`] on a [`Response`]
|
||||||
pub fn show<R>(
|
pub fn show<R>(
|
||||||
&mut self,
|
&mut self,
|
||||||
response: &Response,
|
button: &Response,
|
||||||
add_contents: impl FnOnce(&mut Ui) -> R,
|
add_contents: impl FnOnce(&mut Ui) -> R,
|
||||||
) -> Option<InnerResponse<R>> {
|
) -> Option<InnerResponse<R>> {
|
||||||
if let Some(root) = self.inner.as_mut() {
|
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 {
|
if MenuResponse::Close == menu_response {
|
||||||
self.inner = None;
|
self.inner = None;
|
||||||
}
|
}
|
||||||
|
|
@ -301,12 +301,12 @@ impl MenuRoot {
|
||||||
|
|
||||||
pub fn show<R>(
|
pub fn show<R>(
|
||||||
&mut self,
|
&mut self,
|
||||||
response: &Response,
|
button: &Response,
|
||||||
add_contents: impl FnOnce(&mut Ui) -> R,
|
add_contents: impl FnOnce(&mut Ui) -> R,
|
||||||
) -> (MenuResponse, Option<InnerResponse<R>>) {
|
) -> (MenuResponse, Option<InnerResponse<R>>) {
|
||||||
if self.id == response.id {
|
if self.id == button.id {
|
||||||
let inner_response =
|
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();
|
let menu_state = self.menu_state.read();
|
||||||
|
|
||||||
if menu_state.response.is_close() {
|
if menu_state.response.is_close() {
|
||||||
|
|
@ -319,26 +319,31 @@ impl MenuRoot {
|
||||||
/// Interaction with a stationary menu, i.e. fixed in another Ui.
|
/// Interaction with a stationary menu, i.e. fixed in another Ui.
|
||||||
///
|
///
|
||||||
/// Responds to primary clicks.
|
/// Responds to primary clicks.
|
||||||
fn stationary_interaction(response: &Response, root: &mut MenuRootManager) -> MenuResponse {
|
fn stationary_interaction(button: &Response, root: &mut MenuRootManager) -> MenuResponse {
|
||||||
let id = response.id;
|
let id = button.id;
|
||||||
|
|
||||||
if (response.clicked() && root.is_menu_open(id))
|
if (button.clicked() && root.is_menu_open(id))
|
||||||
|| response.ctx.input(|i| i.key_pressed(Key::Escape))
|
|| button.ctx.input(|i| i.key_pressed(Key::Escape))
|
||||||
{
|
{
|
||||||
// menu open and button clicked or esc pressed
|
// menu open and button clicked or esc pressed
|
||||||
return MenuResponse::Close;
|
return MenuResponse::Close;
|
||||||
} else if (response.clicked() && !root.is_menu_open(id))
|
} else if (button.clicked() && !root.is_menu_open(id))
|
||||||
|| (response.hovered() && root.is_some())
|
|| (button.hovered() && root.is_some())
|
||||||
{
|
{
|
||||||
// menu not open and button clicked
|
// menu not open and button clicked
|
||||||
// or button hovered while other menu is open
|
// 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() {
|
if let Some(root) = root.inner.as_mut() {
|
||||||
let menu_rect = root.menu_state.read().rect;
|
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 {
|
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 {
|
if pos.x + menu_rect.width() > screen_rect.max.x {
|
||||||
|
|
@ -347,11 +352,11 @@ impl MenuRoot {
|
||||||
}
|
}
|
||||||
|
|
||||||
return MenuResponse::Create(pos, id);
|
return MenuResponse::Create(pos, id);
|
||||||
} else if response
|
} else if button
|
||||||
.ctx
|
.ctx
|
||||||
.input(|i| i.pointer.any_pressed() && i.pointer.primary_down())
|
.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 let Some(root) = root.inner.as_mut() {
|
||||||
if root.id == id {
|
if root.id == id {
|
||||||
// pressed somewhere while this menu is open
|
// pressed somewhere while this menu is open
|
||||||
|
|
@ -410,8 +415,8 @@ impl MenuRoot {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Responds to primary clicks.
|
// Responds to primary clicks.
|
||||||
pub fn stationary_click_interaction(response: &Response, root: &mut MenuRootManager) {
|
pub fn stationary_click_interaction(button: &Response, root: &mut MenuRootManager) {
|
||||||
let menu_response = Self::stationary_interaction(response, root);
|
let menu_response = Self::stationary_interaction(button, root);
|
||||||
Self::handle_menu_response(root, menu_response);
|
Self::handle_menu_response(root, menu_response);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -623,8 +628,11 @@ impl MenuState {
|
||||||
// ensure to repaint once even when pointer is not moving
|
// ensure to repaint once even when pointer is not moving
|
||||||
ui.ctx().request_repaint();
|
ui.ctx().request_repaint();
|
||||||
} else if !open && button.hovered() {
|
} 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();
|
let mut pos = button.rect.right_top();
|
||||||
pos.x = self.rect.right() + ui.spacing().menu_spacing;
|
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);
|
self.open_submenu(sub_id, pos);
|
||||||
} else if open
|
} else if open
|
||||||
&& ui.interact_bg(Sense::hover()).contains_pointer()
|
&& ui.interact_bg(Sense::hover()).contains_pointer()
|
||||||
|
|
|
||||||
|
|
@ -1033,7 +1033,7 @@ impl Default for Spacing {
|
||||||
icon_spacing: 4.0,
|
icon_spacing: 4.0,
|
||||||
tooltip_width: 600.0,
|
tooltip_width: 600.0,
|
||||||
menu_width: 150.0,
|
menu_width: 150.0,
|
||||||
menu_spacing: 3.0,
|
menu_spacing: 2.0,
|
||||||
combo_height: 200.0,
|
combo_height: 200.0,
|
||||||
scroll: Default::default(),
|
scroll: Default::default(),
|
||||||
indent_ends_with_horizontal_line: false,
|
indent_ends_with_horizontal_line: false,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue