RadialMenu.cpp 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  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/texts/CGeneralTextHandler.h"
  20. RadialMenuItem::RadialMenuItem(const std::string & imageName, const std::string & hoverText, const std::function<void()> & callback, bool alternativeLayout)
  21. : callback(callback)
  22. , hoverText(hoverText)
  23. {
  24. OBJECT_CONSTRUCTION;
  25. inactiveImage = std::make_shared<CPicture>(ImagePath::builtin(alternativeLayout ? "radialMenu/itemInactiveAlt" : "radialMenu/itemInactive"), Point(0, 0));
  26. selectedImage = std::make_shared<CPicture>(ImagePath::builtin(alternativeLayout ? "radialMenu/itemEmptyAlt" : "radialMenu/itemEmpty"), Point(0, 0));
  27. iconImage = std::make_shared<CPicture>(ImagePath::builtin("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, bool alternativeLayout):
  37. centerPosition(positionToCenter), alternativeLayout(alternativeLayout)
  38. {
  39. OBJECT_CONSTRUCTION;
  40. pos += positionToCenter;
  41. Point itemSize = alternativeLayout ? Point(80, 70) : 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, ImagePath::builtin("radialMenu/statusBar"));
  48. for(const auto & item : items)
  49. pos = pos.include(item->pos);
  50. pos = pos.include(statusBar->pos);
  51. fitToScreen(10);
  52. addUsedEvents(GESTURE);
  53. }
  54. void RadialMenu::addItem(const Point & offset, bool enabled, const std::string & path, const std::string & hoverText, const std::function<void()>& callback )
  55. {
  56. if (!enabled)
  57. return;
  58. auto item = std::make_shared<RadialMenuItem>(path, CGI->generaltexth->translate(hoverText), callback, alternativeLayout);
  59. item->moveBy(offset);
  60. items.push_back(item);
  61. }
  62. std::shared_ptr<RadialMenuItem> RadialMenu::findNearestItem(const Point & cursorPosition) const
  63. {
  64. static const int requiredDistanceFromCenter = 45;
  65. // cursor is inside centeral area -> no selection
  66. if ((centerPosition - cursorPosition).length() < requiredDistanceFromCenter)
  67. return nullptr;
  68. int bestDistance = std::numeric_limits<int>::max();
  69. std::shared_ptr<RadialMenuItem> bestItem;
  70. for(const auto & item : items)
  71. {
  72. Point vector = item->pos.center() - cursorPosition;
  73. if (vector.length() < bestDistance)
  74. {
  75. bestDistance = vector.length();
  76. bestItem = item;
  77. }
  78. }
  79. assert(bestItem);
  80. return bestItem;
  81. }
  82. void RadialMenu::gesturePanning(const Point & initialPosition, const Point & currentPosition, const Point & lastUpdateDistance)
  83. {
  84. auto newSelection = findNearestItem(currentPosition);
  85. if (newSelection != selectedItem)
  86. {
  87. if (selectedItem)
  88. selectedItem->setSelected(false);
  89. if (newSelection)
  90. {
  91. GH.statusbar()->write(newSelection->hoverText);
  92. newSelection->setSelected(true);
  93. }
  94. else
  95. GH.statusbar()->clear();
  96. selectedItem = newSelection;
  97. GH.windows().totalRedraw();
  98. }
  99. }
  100. void RadialMenu::gesture(bool on, const Point & initialPosition, const Point & finalPosition)
  101. {
  102. if (on)
  103. return;
  104. auto item = findNearestItem(finalPosition);
  105. // we need to close this window first so if action spawns a new window it won't be closed instead
  106. GH.windows().popWindows(1);
  107. if (item)
  108. {
  109. GH.input().hapticFeedback();
  110. item->callback();
  111. }
  112. }