Menu.cpp 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. /*
  2. * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/Debug.h>
  7. #include <AK/HashMap.h>
  8. #include <LibGUI/Action.h>
  9. #include <LibGUI/ActionGroup.h>
  10. #include <LibGUI/Menu.h>
  11. #include <LibGUI/MenuItem.h>
  12. #include <LibGUI/WindowServerConnection.h>
  13. #include <LibGfx/Bitmap.h>
  14. namespace GUI {
  15. static HashMap<int, Menu*>& all_menus()
  16. {
  17. static HashMap<int, Menu*>* map;
  18. if (!map)
  19. map = new HashMap<int, Menu*>();
  20. return *map;
  21. }
  22. Menu* Menu::from_menu_id(int menu_id)
  23. {
  24. auto it = all_menus().find(menu_id);
  25. if (it == all_menus().end())
  26. return nullptr;
  27. return (*it).value;
  28. }
  29. Menu::Menu(String name)
  30. : m_name(move(name))
  31. {
  32. }
  33. Menu::~Menu()
  34. {
  35. unrealize_menu();
  36. }
  37. void Menu::set_icon(const Gfx::Bitmap* icon)
  38. {
  39. m_icon = icon;
  40. }
  41. void Menu::add_action(NonnullRefPtr<Action> action)
  42. {
  43. m_items.append(make<MenuItem>(m_menu_id, move(action)));
  44. }
  45. Menu& Menu::add_submenu(const String& name)
  46. {
  47. auto submenu = Menu::construct(name);
  48. m_items.append(make<MenuItem>(m_menu_id, submenu));
  49. return submenu;
  50. }
  51. void Menu::add_separator()
  52. {
  53. m_items.append(make<MenuItem>(m_menu_id, MenuItem::Type::Separator));
  54. }
  55. void Menu::realize_if_needed(const RefPtr<Action>& default_action)
  56. {
  57. if (m_menu_id == -1 || m_last_default_action.ptr() != default_action)
  58. realize_menu(default_action);
  59. }
  60. void Menu::popup(const Gfx::IntPoint& screen_position, const RefPtr<Action>& default_action)
  61. {
  62. realize_if_needed(default_action);
  63. WindowServerConnection::the().post_message(Messages::WindowServer::PopupMenu(m_menu_id, screen_position));
  64. }
  65. void Menu::dismiss()
  66. {
  67. if (m_menu_id == -1)
  68. return;
  69. WindowServerConnection::the().post_message(Messages::WindowServer::DismissMenu(m_menu_id));
  70. }
  71. int Menu::realize_menu(RefPtr<Action> default_action)
  72. {
  73. unrealize_menu();
  74. m_menu_id = WindowServerConnection::the().send_sync<Messages::WindowServer::CreateMenu>(m_name)->menu_id();
  75. dbgln_if(MENU_DEBUG, "GUI::Menu::realize_menu(): New menu ID: {}", m_menu_id);
  76. VERIFY(m_menu_id > 0);
  77. for (size_t i = 0; i < m_items.size(); ++i) {
  78. auto& item = m_items[i];
  79. item.set_menu_id({}, m_menu_id);
  80. item.set_identifier({}, i);
  81. if (item.type() == MenuItem::Type::Separator) {
  82. WindowServerConnection::the().send_sync<Messages::WindowServer::AddMenuSeparator>(m_menu_id);
  83. continue;
  84. }
  85. if (item.type() == MenuItem::Type::Submenu) {
  86. auto& submenu = *item.submenu();
  87. submenu.realize_if_needed(default_action);
  88. auto icon = submenu.icon() ? submenu.icon()->to_shareable_bitmap() : Gfx::ShareableBitmap();
  89. WindowServerConnection::the().send_sync<Messages::WindowServer::AddMenuItem>(m_menu_id, i, submenu.menu_id(), submenu.name(), true, false, false, false, "", icon, false);
  90. continue;
  91. }
  92. if (item.type() == MenuItem::Type::Action) {
  93. auto& action = *item.action();
  94. auto shortcut_text = action.shortcut().is_valid() ? action.shortcut().to_string() : String();
  95. bool exclusive = action.group() && action.group()->is_exclusive() && action.is_checkable();
  96. bool is_default = (default_action.ptr() == &action);
  97. auto icon = action.icon() ? action.icon()->to_shareable_bitmap() : Gfx::ShareableBitmap();
  98. WindowServerConnection::the().send_sync<Messages::WindowServer::AddMenuItem>(m_menu_id, i, -1, action.text(), action.is_enabled(), action.is_checkable(), action.is_checkable() ? action.is_checked() : false, is_default, shortcut_text, icon, exclusive);
  99. }
  100. }
  101. all_menus().set(m_menu_id, this);
  102. m_last_default_action = default_action;
  103. return m_menu_id;
  104. }
  105. void Menu::unrealize_menu()
  106. {
  107. if (m_menu_id == -1)
  108. return;
  109. all_menus().remove(m_menu_id);
  110. WindowServerConnection::the().send_sync<Messages::WindowServer::DestroyMenu>(m_menu_id);
  111. m_menu_id = -1;
  112. }
  113. void Menu::realize_menu_if_needed()
  114. {
  115. if (menu_id() == -1)
  116. realize_menu();
  117. }
  118. Action* Menu::action_at(size_t index)
  119. {
  120. if (index >= m_items.size())
  121. return nullptr;
  122. return m_items[index].action();
  123. }
  124. void Menu::visibility_did_change(Badge<WindowServerConnection>, bool visible)
  125. {
  126. if (m_visible == visible)
  127. return;
  128. m_visible = visible;
  129. if (on_visibility_change)
  130. on_visibility_change(visible);
  131. }
  132. }