Menu.cpp 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. //
  2. // Urho3D Engine
  3. // Copyright (c) 2008-2012 Lasse Öörni
  4. //
  5. // Permission is hereby granted, free of charge, to any person obtaining a copy
  6. // of this software and associated documentation files (the "Software"), to deal
  7. // in the Software without restriction, including without limitation the rights
  8. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. // copies of the Software, and to permit persons to whom the Software is
  10. // furnished to do so, subject to the following conditions:
  11. //
  12. // The above copyright notice and this permission notice shall be included in
  13. // all copies or substantial portions of the Software.
  14. //
  15. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  21. // THE SOFTWARE.
  22. //
  23. #include "Precompiled.h"
  24. #include "Context.h"
  25. #include "InputEvents.h"
  26. #include "Log.h"
  27. #include "Menu.h"
  28. #include "UIEvents.h"
  29. #include "DebugNew.h"
  30. static const ShortStringHash originHash("Origin");
  31. OBJECTTYPESTATIC(Menu);
  32. Menu::Menu(Context* context) :
  33. Button(context),
  34. popupOffset_(IntVector2::ZERO),
  35. showPopup_(false),
  36. acceleratorKey_(0),
  37. acceleratorQualifiers_(0)
  38. {
  39. SubscribeToEvent(this, E_PRESSED, HANDLER(Menu, HandlePressedReleased));
  40. SubscribeToEvent(this, E_RELEASED, HANDLER(Menu, HandlePressedReleased));
  41. SubscribeToEvent(E_UIMOUSECLICK, HANDLER(Menu, HandleFocusChanged));
  42. SubscribeToEvent(E_FOCUSCHANGED, HANDLER(Menu, HandleFocusChanged));
  43. }
  44. Menu::~Menu()
  45. {
  46. if (popup_)
  47. ShowPopup(false);
  48. }
  49. void Menu::RegisterObject(Context* context)
  50. {
  51. context->RegisterFactory<Menu>();
  52. }
  53. void Menu::SetStyle(const XMLElement& element)
  54. {
  55. Button::SetStyle(element);
  56. XMLElement popupElem = element.GetChild("popup");
  57. if (popupElem && popupElem.HasAttribute("name"))
  58. {
  59. UIElement* root = GetRoot();
  60. if (root)
  61. SetPopup(root->GetChild(popupElem.GetAttribute("name"), true));
  62. }
  63. if (element.HasChild("popupoffset"))
  64. SetPopupOffset(element.GetChild("popupoffset").GetIntVector2("value"));
  65. }
  66. void Menu::OnShowPopup()
  67. {
  68. }
  69. void Menu::SetPopup(UIElement* popup)
  70. {
  71. if (popup == this)
  72. return;
  73. if (popup_ && !popup)
  74. ShowPopup(false);
  75. popup_ = popup;
  76. // Detach from current parent (if any) to only show when it is time
  77. if (popup_)
  78. popup_->Remove();
  79. }
  80. void Menu::SetPopupOffset(const IntVector2& offset)
  81. {
  82. popupOffset_ = offset;
  83. }
  84. void Menu::SetPopupOffset(int x, int y)
  85. {
  86. popupOffset_ = IntVector2(x, y);
  87. }
  88. void Menu::ShowPopup(bool enable)
  89. {
  90. if (!popup_)
  91. return;
  92. if (enable)
  93. {
  94. // Find the UI root element for showing the popup
  95. UIElement* root = GetRoot();
  96. if (!root)
  97. return;
  98. OnShowPopup();
  99. if (popup_->GetParent() != root)
  100. root->AddChild(popup_);
  101. popup_->SetPosition(GetScreenPosition() + popupOffset_);
  102. popup_->SetVisible(true);
  103. popup_->SetVar(originHash, (void*)this);
  104. popup_->BringToFront();
  105. }
  106. else
  107. {
  108. // If the popup has child menus, hide their popups as well
  109. PODVector<UIElement*> children;
  110. popup_->GetChildren(children, true);
  111. for (PODVector<UIElement*>::ConstIterator i = children.Begin(); i != children.End(); ++i)
  112. {
  113. Menu* menu = dynamic_cast<Menu*>(*i);
  114. if (menu)
  115. menu->ShowPopup(false);
  116. }
  117. popup_->SetVar(originHash, Variant());
  118. popup_->SetVisible(false);
  119. popup_->Remove();
  120. }
  121. showPopup_ = enable;
  122. selected_ = enable;
  123. }
  124. void Menu::SetAccelerator(int key, int qualifiers)
  125. {
  126. acceleratorKey_ = key;
  127. acceleratorQualifiers_ = qualifiers;
  128. if (key)
  129. SubscribeToEvent(E_KEYDOWN, HANDLER(Menu, HandleKeyDown));
  130. else
  131. UnsubscribeFromEvent(E_KEYDOWN);
  132. }
  133. void Menu::HandlePressedReleased(StringHash eventType, VariantMap& eventData)
  134. {
  135. // If this menu shows a sublevel popup, react to button press. Else react to release
  136. if (eventType == E_PRESSED)
  137. {
  138. if (!popup_)
  139. return;
  140. }
  141. if (eventType == E_RELEASED)
  142. {
  143. if (popup_)
  144. return;
  145. }
  146. // Toggle popup visibility if exists
  147. ShowPopup(!showPopup_);
  148. // Send event on each click if no popup, or whenever the popup is opened
  149. if (!popup_ || showPopup_)
  150. {
  151. using namespace MenuSelected;
  152. VariantMap newEventData;
  153. newEventData[P_ELEMENT] = (void*)this;
  154. SendEvent(E_MENUSELECTED, newEventData);
  155. }
  156. }
  157. void Menu::HandleFocusChanged(StringHash eventType, VariantMap& eventData)
  158. {
  159. if (!showPopup_)
  160. return;
  161. using namespace FocusChanged;
  162. UIElement* element = static_cast<UIElement*>(eventData[P_ELEMENT].GetPtr());
  163. UIElement* root = GetRoot();
  164. // If another element was focused due to the menu button being clicked, do not hide the popup
  165. if (eventType == E_FOCUSCHANGED && static_cast<UIElement*>(eventData[P_CLICKEDELEMENT].GetPtr()))
  166. return;
  167. // If clicked emptiness or defocused, hide the popup
  168. if (!element)
  169. {
  170. ShowPopup(false);
  171. return;
  172. }
  173. // Otherwise see if the clicked element has either the menu item or the popup in its parent chain.
  174. // In that case, do not hide
  175. while (element)
  176. {
  177. if (element == this || element == popup_)
  178. return;
  179. if (element->GetParent() == root)
  180. element = static_cast<UIElement*>(element->GetVar(originHash).GetPtr());
  181. else
  182. element = element->GetParent();
  183. }
  184. ShowPopup(false);
  185. }
  186. void Menu::HandleKeyDown(StringHash eventType, VariantMap& eventData)
  187. {
  188. if (!active_)
  189. return;
  190. using namespace KeyDown;
  191. // Activate if accelerator key pressed
  192. if (eventData[P_KEY].GetInt() == acceleratorKey_ && (acceleratorQualifiers_ == QUAL_ANY || eventData[P_QUALIFIERS].GetInt() ==
  193. acceleratorQualifiers_) && eventData[P_REPEAT].GetBool() == false)
  194. HandlePressedReleased(eventType, eventData);
  195. }