RadialMenu.cpp 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  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 "../GameEngine.h"
  16. #include "../gui/WindowHandler.h"
  17. #include "../render/IImage.h"
  18. #include "../../lib/texts/CGeneralTextHandler.h"
  19. #include "../../lib/GameLibrary.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, LIBRARY->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. if (bestDistance > 2 * requiredDistanceFromCenter)
  80. return nullptr;
  81. assert(bestItem);
  82. return bestItem;
  83. }
  84. void RadialMenu::gesturePanning(const Point & initialPosition, const Point & currentPosition, const Point & lastUpdateDistance)
  85. {
  86. auto newSelection = findNearestItem(currentPosition);
  87. if (newSelection != selectedItem)
  88. {
  89. if (selectedItem)
  90. selectedItem->setSelected(false);
  91. if (newSelection)
  92. {
  93. ENGINE->statusbar()->write(newSelection->hoverText);
  94. newSelection->setSelected(true);
  95. }
  96. else
  97. ENGINE->statusbar()->clear();
  98. selectedItem = newSelection;
  99. ENGINE->windows().totalRedraw();
  100. }
  101. }
  102. void RadialMenu::gesture(bool on, const Point & initialPosition, const Point & finalPosition)
  103. {
  104. if (on)
  105. return;
  106. auto item = findNearestItem(finalPosition);
  107. // we need to close this window first so if action spawns a new window it won't be closed instead
  108. ENGINE->windows().popWindows(1);
  109. if (item)
  110. {
  111. ENGINE->input().hapticFeedback();
  112. item->callback();
  113. }
  114. }