RadialMenu.cpp 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. /*
  2. * RadialMenu.cpp, part of VCMI engine
  3. *
  4. * Authors: listed in file AUTHORS in main folder
  5. *
  6. * License: GNU General Public License v2.0 or later
  7. * Full text of license available in license.txt file, in main folder
  8. *
  9. */
  10. #include "StdInc.h"
  11. #include "RadialMenu.h"
  12. #include "Images.h"
  13. #include "TextControls.h"
  14. #include "../eventsSDL/InputHandler.h"
  15. #include "../gui/CGuiHandler.h"
  16. #include "../gui/WindowHandler.h"
  17. #include "../render/IImage.h"
  18. #include "../CGameInfo.h"
  19. #include "../../lib/CGeneralTextHandler.h"
  20. RadialMenuItem::RadialMenuItem(const std::string & imageName, const std::string & hoverText, const std::function<void()> & callback)
  21. : callback(callback)
  22. , hoverText(hoverText)
  23. {
  24. OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
  25. inactiveImage = std::make_shared<CPicture>("radialMenu/itemInactive", Point(0, 0));
  26. selectedImage = std::make_shared<CPicture>("radialMenu/itemEmpty", Point(0, 0));
  27. iconImage = std::make_shared<CPicture>("radialMenu/" + imageName, Point(0, 0));
  28. pos = selectedImage->pos;
  29. selectedImage->setEnabled(false);
  30. }
  31. void RadialMenuItem::setSelected(bool selected)
  32. {
  33. selectedImage->setEnabled(selected);
  34. inactiveImage->setEnabled(!selected);
  35. }
  36. RadialMenu::RadialMenu(const Point & positionToCenter, const std::vector<RadialMenuConfig> & menuConfig):
  37. centerPosition(positionToCenter)
  38. {
  39. OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
  40. pos += positionToCenter;
  41. Point itemSize = Point(70, 80);
  42. moveBy(-itemSize / 2);
  43. pos.w = itemSize.x;
  44. pos.h = itemSize.y;
  45. for (auto const & item : menuConfig)
  46. addItem(item.itemPosition, item.enabled, item.imageName, item.hoverText, item.callback);
  47. statusBar = CGStatusBar::create(-80, -100, "radialMenu/statusBar");
  48. for(const auto & item : items)
  49. pos = pos.include(item->pos);
  50. fitToScreen(10);
  51. addUsedEvents(GESTURE);
  52. }
  53. void RadialMenu::addItem(const Point & offset, bool enabled, const std::string & path, const std::string & hoverText, const std::function<void()>& callback )
  54. {
  55. if (!enabled)
  56. return;
  57. auto item = std::make_shared<RadialMenuItem>(path, CGI->generaltexth->translate(hoverText), callback);
  58. item->moveBy(offset);
  59. items.push_back(item);
  60. }
  61. std::shared_ptr<RadialMenuItem> RadialMenu::findNearestItem(const Point & cursorPosition) const
  62. {
  63. static const int requiredDistanceFromCenter = 45;
  64. // cursor is inside centeral area -> no selection
  65. if ((centerPosition - cursorPosition).length() < requiredDistanceFromCenter)
  66. return nullptr;
  67. int bestDistance = std::numeric_limits<int>::max();
  68. std::shared_ptr<RadialMenuItem> bestItem;
  69. for(const auto & item : items)
  70. {
  71. Point vector = item->pos.center() - cursorPosition;
  72. if (vector.length() < bestDistance)
  73. {
  74. bestDistance = vector.length();
  75. bestItem = item;
  76. }
  77. }
  78. assert(bestItem);
  79. return bestItem;
  80. }
  81. void RadialMenu::gesturePanning(const Point & initialPosition, const Point & currentPosition, const Point & lastUpdateDistance)
  82. {
  83. auto newSelection = findNearestItem(currentPosition);
  84. if (newSelection != selectedItem)
  85. {
  86. if (selectedItem)
  87. selectedItem->setSelected(false);
  88. if (newSelection)
  89. {
  90. GH.statusbar()->write(newSelection->hoverText);
  91. newSelection->setSelected(true);
  92. }
  93. else
  94. GH.statusbar()->clear();
  95. selectedItem = newSelection;
  96. GH.windows().totalRedraw();
  97. }
  98. }
  99. void RadialMenu::gesture(bool on, const Point & initialPosition, const Point & finalPosition)
  100. {
  101. if (on)
  102. return;
  103. auto item = findNearestItem(finalPosition);
  104. // we need to close this window first so if action spawns a new window it won't be closed instead
  105. GH.windows().popWindows(1);
  106. if (item)
  107. {
  108. GH.input().hapticFeedback();
  109. item->callback();
  110. }
  111. }