Menu.cpp 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. //
  2. // Urho3D Engine
  3. // Copyright (c) 2008-2011 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 "InputEvents.h"
  25. #include "Menu.h"
  26. #include "UIEvents.h"
  27. #include "DebugNew.h"
  28. static const ShortStringHash originHash("Origin");
  29. Menu::Menu(const std::string& name) :
  30. Button(name),
  31. mPopupOffset(IntVector2::sZero),
  32. mShowPopup(false),
  33. mAcceleratorKey(0),
  34. mAcceleratorQualifiers(0)
  35. {
  36. subscribeToEvent(this, EVENT_RELEASED, EVENT_HANDLER(Menu, handleReleased));
  37. subscribeToEvent(EVENT_UIMOUSECLICK, EVENT_HANDLER(Menu, handleFocusChanged));
  38. subscribeToEvent(EVENT_FOCUSCHANGED, EVENT_HANDLER(Menu, handleFocusChanged));
  39. }
  40. Menu::~Menu()
  41. {
  42. if (mPopup)
  43. showPopup(false);
  44. }
  45. void Menu::setStyle(const XMLElement& element, ResourceCache* cache)
  46. {
  47. Button::setStyle(element, cache);
  48. XMLElement popupElem = element.getChildElement("popup");
  49. if ((popupElem) && (popupElem.hasAttribute("name")))
  50. {
  51. UIElement* root = getRootElement();
  52. if (root)
  53. setPopup(root->getChild(popupElem.getString("name"), true));
  54. }
  55. if (element.hasChildElement("popupoffset"))
  56. setPopupOffset(element.getChildElement("popupoffset").getIntVector2("value"));
  57. }
  58. void Menu::onShowPopup()
  59. {
  60. }
  61. void Menu::setPopup(UIElement* popup)
  62. {
  63. if (popup == this)
  64. return;
  65. if ((mPopup) && (!popup))
  66. showPopup(false);
  67. mPopup = popup;
  68. // Detach from current parent (if any) to only show when it is time
  69. if (mPopup)
  70. {
  71. UIElement* parent = mPopup->getParent();
  72. if (parent)
  73. parent->removeChild(mPopup);
  74. }
  75. }
  76. void Menu::setPopupOffset(const IntVector2& offset)
  77. {
  78. mPopupOffset = offset;
  79. }
  80. void Menu::setPopupOffset(int x, int y)
  81. {
  82. mPopupOffset = IntVector2(x, y);
  83. }
  84. void Menu::showPopup(bool enable)
  85. {
  86. if (!mPopup)
  87. return;
  88. // Find the UI root element for showing the popup. If we are already detached, try to find it through the popup
  89. UIElement* root = getRootElement();
  90. if (!root)
  91. root = mPopup->getRootElement();
  92. if (!root)
  93. return;
  94. if (enable)
  95. {
  96. onShowPopup();
  97. mPopup->setPosition(getScreenPosition() + mPopupOffset);
  98. mPopup->setVisible(true);
  99. mPopup->getUserData()[originHash] = (void*)this;
  100. root->addChild(mPopup);
  101. // Set fixed high priority
  102. mPopup->setBringToFront(false);
  103. mPopup->setBringToBack(false);
  104. mPopup->bringToFront();
  105. }
  106. else
  107. {
  108. mPopup->getUserData()[originHash].clear();
  109. root->removeChild(mPopup);
  110. }
  111. mShowPopup = enable;
  112. mSelected = enable;
  113. }
  114. void Menu::setAccelerator(int key, int qualifiers)
  115. {
  116. mAcceleratorKey = key;
  117. mAcceleratorQualifiers = qualifiers;
  118. if (key)
  119. subscribeToEvent(EVENT_KEYDOWN, EVENT_HANDLER(Menu, handleKeyDown));
  120. else
  121. unsubscribeFromEvent(EVENT_KEYDOWN);
  122. }
  123. void Menu::handleReleased(StringHash eventType, VariantMap& eventData)
  124. {
  125. // Toggle popup visibility if exists
  126. showPopup(!mShowPopup);
  127. // Send event on each click if no popup, or whenever the popup is opened
  128. if ((!mPopup) || (mShowPopup))
  129. {
  130. using namespace MenuSelected;
  131. VariantMap newEventData;
  132. newEventData[P_ELEMENT] = (void*)this;
  133. sendEvent(EVENT_MENUSELECTED, newEventData);
  134. }
  135. }
  136. void Menu::handleFocusChanged(StringHash eventType, VariantMap& eventData)
  137. {
  138. if (!mShowPopup)
  139. return;
  140. using namespace FocusChanged;
  141. UIElement* element = static_cast<UIElement*>(eventData[P_ELEMENT].getPtr());
  142. UIElement* root = getRootElement();
  143. // If another element was focused due to the menu button being clicked, do not hide the popup
  144. if ((eventType == EVENT_FOCUSCHANGED) && (static_cast<UIElement*>(eventData[P_ORIGINALELEMENT].getPtr())))
  145. return;
  146. // If clicked emptiness or defocused, hide the popup
  147. if (!element)
  148. {
  149. showPopup(false);
  150. return;
  151. }
  152. // Otherwise see if the clicked element has either the menu item or the popup in its parent chain.
  153. // In that case, do not hide
  154. while (element)
  155. {
  156. if ((element == this) || (element == mPopup))
  157. return;
  158. if (element->getParent() == root)
  159. element = static_cast<UIElement*>(element->getUserData()[originHash].getPtr());
  160. else
  161. element = element->getParent();
  162. }
  163. showPopup(false);
  164. }
  165. void Menu::handleKeyDown(StringHash eventType, VariantMap& eventData)
  166. {
  167. if (!mEnabled)
  168. return;
  169. using namespace KeyDown;
  170. // Activate if accelerator key pressed
  171. if ((eventData[P_KEY].getInt() == mAcceleratorKey) && (eventData[P_QUALIFIERS].getInt() == mAcceleratorQualifiers) &&
  172. (eventData[P_REPEAT].getBool() == false))
  173. handleReleased(eventType, eventData);
  174. }