From 5a7bd7c51f2697dc2f15f70a289dcd45736998da Mon Sep 17 00:00:00 2001 From: Iris Morelle Date: Wed, 15 Jan 2025 23:02:02 -0300 Subject: [PATCH] gui: Better positioning of submenus in the main UI (#8517) This makes it possible to fetch the on-screen coordinates of a drop down menu item as the menu is dismissed by clicking on it so the caller can use these coordinates to make a more informed decision as to where to spawn a submenu, improving the previous mechanism wherein the submenu would have its top left corner set at the mouse's location which would produce different results depending on where exactly in the menu item's box the mouse was at the time the parent menu was dismissed. --- changelog_entries/submenu-positioning.md | 2 ++ src/gui/dialogs/drop_down_menu.cpp | 13 ++++++++++++- src/gui/dialogs/drop_down_menu.hpp | 6 ++++++ src/hotkey/command_executor.cpp | 11 +++++++++-- 4 files changed, 29 insertions(+), 3 deletions(-) create mode 100644 changelog_entries/submenu-positioning.md diff --git a/changelog_entries/submenu-positioning.md b/changelog_entries/submenu-positioning.md new file mode 100644 index 00000000000..a3d804f44d9 --- /dev/null +++ b/changelog_entries/submenu-positioning.md @@ -0,0 +1,2 @@ + ### User interface + * Submenus are now positioned at the location of the menu item that spawned them, rather than the location of the mouse cursor at the time of click diff --git a/src/gui/dialogs/drop_down_menu.cpp b/src/gui/dialogs/drop_down_menu.cpp index 25527fbf145..2518f45ae46 100644 --- a/src/gui/dialogs/drop_down_menu.cpp +++ b/src/gui/dialogs/drop_down_menu.cpp @@ -85,6 +85,7 @@ drop_down_menu::drop_down_menu(styled_widget* parent, const std::vector& , items_(items.begin(), items.end()) , button_pos_(parent->get_rectangle()) , selected_item_(selected_item) + , selected_item_pos_(-1, -1) , use_markup_(parent->get_use_markup()) , keep_open_(keep_open) , mouse_down_happened_(false) @@ -241,7 +242,17 @@ void drop_down_menu::pre_show() void drop_down_menu::post_show() { - selected_item_ = find_widget("list", true).get_selected_row(); + const listbox& list = find_widget("list", true); + selected_item_ = list.get_selected_row(); + if(selected_item_ != -1) { + const grid* row_grid = list.get_row_grid(selected_item_); + if(row_grid) { + selected_item_pos_.x = row_grid->get_x(); + selected_item_pos_.y = row_grid->get_y(); + } + } else { + selected_item_pos_.x = selected_item_pos_.y = -1; + } } boost::dynamic_bitset<> drop_down_menu::get_toggle_states() const diff --git a/src/gui/dialogs/drop_down_menu.hpp b/src/gui/dialogs/drop_down_menu.hpp index cd870710d27..5d28a325b4e 100644 --- a/src/gui/dialogs/drop_down_menu.hpp +++ b/src/gui/dialogs/drop_down_menu.hpp @@ -44,6 +44,11 @@ public: return selected_item_; } + point selected_item_pos() const + { + return selected_item_pos_; + } + /** If a toggle button widget is present, returns the toggled state of each row's button. */ boost::dynamic_bitset<> get_toggle_states() const; @@ -86,6 +91,7 @@ private: SDL_Rect button_pos_; int selected_item_; + point selected_item_pos_; bool use_markup_; diff --git a/src/hotkey/command_executor.cpp b/src/hotkey/command_executor.cpp index 8c6a032af00..1fd53af82ef 100644 --- a/src/hotkey/command_executor.cpp +++ b/src/hotkey/command_executor.cpp @@ -417,11 +417,19 @@ void command_executor::show_menu(const std::vector& items_arg, int xloc, get_menu_images(gui, items); int res = -1; + point selection_pos; { SDL_Rect pos {xloc, yloc, 1, 1}; gui2::dialogs::drop_down_menu mmenu(pos, items, -1, true, false); // TODO: last value should be variable if(mmenu.show()) { res = mmenu.selected_item(); + if(res >= 0) { + // Get selection coordinates for a potential submenu below + selection_pos = mmenu.selected_item_pos(); + // Compensate for borders + selection_pos.x--; + selection_pos.y--; + } } } // This will kill the dialog. if (res < 0 || std::size_t(res) >= items.size()) return; @@ -429,8 +437,7 @@ void command_executor::show_menu(const std::vector& items_arg, int xloc, std::string id = items[res]["id"]; const theme::menu* submenu = gui.get_theme().get_menu_item(id); if (submenu) { - auto [x, y] = sdl::get_mouse_location(); - this->show_menu(submenu->items(), x, y, submenu->is_context(), gui); + this->show_menu(submenu->items(), selection_pos.x, selection_pos.y, submenu->is_context(), gui); } else { hotkey::ui_command cmd = hotkey::ui_command(id, res); do_execute_command(cmd);