Browse Source

Merge pull request #2222 from IvanSavenko/fix_long_touch_gesture

Improvements for long tap (right-click) handling
Ivan Savenko 2 years ago
parent
commit
849ce1124d
73 changed files with 457 additions and 393 deletions
  1. 5 0
      Mods/vcmi/config/vcmi/english.json
  2. 9 4
      Mods/vcmi/config/vcmi/ukrainian.json
  3. 3 4
      client/adventureMap/CInfoBar.cpp
  4. 1 1
      client/adventureMap/CInfoBar.h
  5. 3 4
      client/adventureMap/CList.cpp
  6. 1 1
      client/adventureMap/CList.h
  7. 4 5
      client/adventureMap/CMinimap.cpp
  8. 1 1
      client/adventureMap/CMinimap.h
  9. 0 2
      client/adventureMap/CResDataBar.cpp
  10. 7 11
      client/battle/BattleFieldController.cpp
  11. 2 2
      client/battle/BattleFieldController.h
  12. 15 14
      client/battle/BattleInterfaceClasses.cpp
  13. 5 4
      client/battle/BattleInterfaceClasses.h
  14. 2 0
      client/eventsSDL/InputHandler.cpp
  15. 4 4
      client/eventsSDL/InputSourceMouse.cpp
  16. 25 10
      client/eventsSDL/InputSourceTouch.cpp
  17. 6 12
      client/eventsSDL/InputSourceTouch.h
  18. 6 1
      client/gui/CIntObject.cpp
  19. 7 0
      client/gui/CIntObject.h
  20. 45 42
      client/gui/EventDispatcher.cpp
  21. 6 4
      client/gui/EventDispatcher.h
  22. 11 3
      client/gui/EventsReceiver.cpp
  23. 22 9
      client/gui/EventsReceiver.h
  24. 8 0
      client/gui/WindowHandler.cpp
  25. 3 0
      client/gui/WindowHandler.h
  26. 2 2
      client/lobby/CBonusSelection.cpp
  27. 1 1
      client/lobby/CBonusSelection.h
  28. 3 3
      client/lobby/CSelectionBase.cpp
  29. 1 1
      client/lobby/CSelectionBase.h
  30. 8 11
      client/lobby/OptionsTab.cpp
  31. 1 1
      client/lobby/OptionsTab.h
  32. 1 1
      client/lobby/RandomMapTab.cpp
  33. 2 7
      client/mainmenu/CreditsScreen.cpp
  34. 0 1
      client/mainmenu/CreditsScreen.h
  35. 4 4
      client/mapView/MapViewActions.cpp
  36. 2 2
      client/mapView/MapViewActions.h
  37. 3 3
      client/widgets/Buttons.cpp
  38. 1 1
      client/widgets/Buttons.h
  39. 6 14
      client/widgets/CArtifactHolder.cpp
  40. 2 3
      client/widgets/CArtifactHolder.h
  41. 3 3
      client/widgets/CComponent.cpp
  42. 1 1
      client/widgets/CComponent.h
  43. 4 4
      client/widgets/CGarrisonInt.cpp
  44. 1 1
      client/widgets/CGarrisonInt.h
  45. 2 2
      client/widgets/CWindowWithArtifacts.cpp
  46. 9 24
      client/widgets/MiscWidgets.cpp
  47. 2 4
      client/widgets/MiscWidgets.h
  48. 4 4
      client/widgets/Scrollable.cpp
  49. 1 1
      client/widgets/Scrollable.h
  50. 2 2
      client/widgets/Slider.cpp
  51. 22 32
      client/windows/CCastleInterface.cpp
  52. 6 7
      client/windows/CCastleInterface.h
  53. 0 6
      client/windows/CCreatureWindow.cpp
  54. 0 1
      client/windows/CCreatureWindow.h
  55. 10 13
      client/windows/CKingdomInterface.cpp
  56. 1 1
      client/windows/CKingdomInterface.h
  57. 6 7
      client/windows/CSpellWindow.cpp
  58. 2 2
      client/windows/CSpellWindow.h
  59. 3 6
      client/windows/CTradeWindow.cpp
  60. 1 1
      client/windows/CTradeWindow.h
  61. 9 13
      client/windows/CWindowObject.cpp
  62. 1 2
      client/windows/CWindowObject.h
  63. 3 4
      client/windows/CreaturePurchaseCard.cpp
  64. 1 1
      client/windows/CreaturePurchaseCard.h
  65. 9 13
      client/windows/GUIClasses.cpp
  66. 3 3
      client/windows/GUIClasses.h
  67. 4 17
      client/windows/InfoWindows.cpp
  68. 1 4
      client/windows/InfoWindows.h
  69. 0 3
      client/windows/settings/BattleOptionsTab.cpp
  70. 81 10
      client/windows/settings/GeneralOptionsTab.cpp
  71. 4 0
      client/windows/settings/GeneralOptionsTab.h
  72. 11 8
      config/schemas/settings.json
  73. 12 0
      config/widgets/settings/generalOptionsTab.json

+ 5 - 0
Mods/vcmi/config/vcmi/english.json

@@ -67,6 +67,11 @@
 	"vcmi.systemOptions.scalingButton.help"    : "{Interface Scaling}\n\nChanges scaling of in-game interface",
 	"vcmi.systemOptions.scalingMenu.hover"     : "Select Interface Scaling",
 	"vcmi.systemOptions.scalingMenu.help"      : "Change in-game interface scaling.",
+	"vcmi.systemOptions.longTouchButton.hover"   : "Long Touch Interval: %d ms", // Translation note: "ms" = "milliseconds"
+	"vcmi.systemOptions.longTouchButton.help"    : "{Long Touch Interval}\n\nWhen using touchscreen, popup windows will appear after touching screen for specified duration, in milliseconds",
+	"vcmi.systemOptions.longTouchMenu.hover"     : "Select Long Touch Interval",
+	"vcmi.systemOptions.longTouchMenu.help"      : "Change duration of long touch interval.",
+	"vcmi.systemOptions.longTouchMenu.entry"     : "%d milliseconds",
 	"vcmi.systemOptions.framerateButton.hover"  : "Show FPS",
 	"vcmi.systemOptions.framerateButton.help"   : "{Show FPS}\n\nToggle the visibility of the Frames Per Second counter in the corner of the game window",
 

+ 9 - 4
Mods/vcmi/config/vcmi/ukrainian.json

@@ -42,10 +42,6 @@
 	"vcmi.systemOptions.fullscreenBorderless.help"  : "{На весь екран (безрамкове вікно)}\n\nЯкщо обрано, VCMI працюватиме у режимі безрамкового вікна на весь екран. У цьому режимі гра завжди використовує ту саму роздільну здатність, що й робочий стіл, ігноруючи вибрану роздільну здатність",
 	"vcmi.systemOptions.fullscreenExclusive.hover"  : "На весь екран (ексклюзивний режим)",
 	"vcmi.systemOptions.fullscreenExclusive.help"   : "{На весь екран (ексклюзивний режим)}\n\nnЯкщо вибрано, VCMI працюватиме у ексклюзивному повноекранному режимі. У цьому режимі гра змінюватиме роздільну здатність монітора на вибрану роздільну здатність",
-	"vcmi.adventureOptions.infoBarPick.help" : "{Повідомлення у панелі статусу}\n\nЗа можливості, повідомлення про відвідування об'єктів карти пригод будуть відображені у панелі статусу замість окремого вікна",
-	"vcmi.adventureOptions.infoBarPick.hover" : "Повідомлення у панелі статусу",
-	"vcmi.systemOptions.fullscreenButton.hover" : "Повноекранний режим",
-	"vcmi.systemOptions.fullscreenButton.help"  : "{Повноекранний режим}\n\n Якщо обрано, VCMI буде запускатися в режимі на весь екран, інакше — віконний режим",
 	"vcmi.systemOptions.resolutionButton.hover" : "Роздільна здатність: %wx%h",
 	"vcmi.systemOptions.resolutionButton.help"  : "{Роздільна здатність}\n\n Зміна розширення екрану в грі. Аби зміни набули чинності необхідно перезавантажити гру.",
 	"vcmi.systemOptions.resolutionMenu.hover"   : "Обрати роздільну здатність",
@@ -54,9 +50,16 @@
 	"vcmi.systemOptions.scalingButton.help"    : "{Масштабування інтерфейсу}\n\nЗмінити масштабування ігрового інтерфейсу",
 	"vcmi.systemOptions.scalingMenu.hover"     : "Виберіть масштабування інтерфейсу",
 	"vcmi.systemOptions.scalingMenu.help"      : "Змінити масштабування ігрового інтерфейсу.",
+	"vcmi.systemOptions.longTouchButton.hover"   : "Інтервал довгого дотику: %d мс", // Translation note: "ms" = "milliseconds"
+	"vcmi.systemOptions.longTouchButton.help"    : "{Інтервал довгого дотику}\n\nПри використанні сенсорного екрану спливаючі вікна з'являтимуться після дотику до екрану протягом заданої тривалості, в мілісекундах",
+	"vcmi.systemOptions.longTouchMenu.hover"     : "Виберіть інтервал довгого дотику",
+	"vcmi.systemOptions.longTouchMenu.help"      : "Змінити тривалість інтервалу довгого дотику.",
+	"vcmi.systemOptions.longTouchMenu.entry"     : "%d мілісекунд",
 	"vcmi.systemOptions.framerateButton.hover"  : "Лічильник кадрів",
 	"vcmi.systemOptions.framerateButton.help"   : "{Лічильник кадрів}\n\n Перемикає видимість лічильника кадрів на секунду у кутку ігрового вікна",
 
+	"vcmi.adventureOptions.infoBarPick.help" : "{Повідомлення у панелі статусу}\n\nЗа можливості, повідомлення про відвідування об'єктів карти пригод будуть відображені у панелі статусу замість окремого вікна",
+	"vcmi.adventureOptions.infoBarPick.hover" : "Повідомлення у панелі статусу",
 	"vcmi.adventureOptions.numericQuantities.hover" : "Числовий показник кількості",
 	"vcmi.adventureOptions.numericQuantities.help" : "{Числовий показник кількості}\n\n Показує приблизну кількість ворожих істот у числовому форматі А-Б.",
 	"vcmi.adventureOptions.forceMovementInfo.hover" : "Завжди показувати вартість руху",
@@ -89,6 +92,8 @@
 	"vcmi.battleOptions.animationsSpeed6.help": "Встановити миттєву швидкість анімації",
 	"vcmi.battleOptions.movementHighlightOnHover.hover": "Підсвічувати зону руху істоти",
 	"vcmi.battleOptions.movementHighlightOnHover.help": "{Підсвічувати зону руху істоти}\n\nПідсвічувати можливу зону руху істоти при наведенні курсора миші на неї",
+	"vcmi.battleOptions.rangedFullDamageLimitHighlightOnHover.hover": "Межа повного шкоди пострілом",
+	"vcmi.battleOptions.rangedFullDamageLimitHighlightOnHover.help": "{Межа повного шкоди пострілом}\n\nПідсвічувати зону у якій створіння може завдавати максимальної шкоди пострілом при наведенні на неї курсору миші.",
 	"vcmi.battleOptions.skipBattleIntroMusic.hover": "Пропускати вступну музику",
 	"vcmi.battleOptions.skipBattleIntroMusic.help": "{Пропускати вступну музику}\n\n Пропускати коротку музику, яка грає на початку кожної битви перед початком дії. Також можна пропустити, натиснувши клавішу ESC.",
 	

+ 3 - 4
client/adventureMap/CInfoBar.cpp

@@ -284,10 +284,9 @@ void CInfoBar::clickLeft(tribool down, bool previousState)
 	}
 }
 
-void CInfoBar::clickRight(tribool down, bool previousState)
+void CInfoBar::showPopupWindow()
 {
-	if (down)
-		CRClickPopup::createAndPush(CGI->generaltexth->allTexts[109]);
+	CRClickPopup::createAndPush(CGI->generaltexth->allTexts[109]);
 }
 
 void CInfoBar::hover(bool on)
@@ -299,7 +298,7 @@ void CInfoBar::hover(bool on)
 }
 
 CInfoBar::CInfoBar(const Rect & position)
-	: CIntObject(LCLICK | RCLICK | HOVER, position.topLeft()),
+	: CIntObject(LCLICK | SHOW_POPUP | HOVER, position.topLeft()),
 	timerCounter(0),
 	state(EMPTY)
 {

+ 1 - 1
client/adventureMap/CInfoBar.h

@@ -155,7 +155,7 @@ private:
 	void tick(uint32_t msPassed) override;
 
 	void clickLeft(tribool down, bool previousState) override;
-	void clickRight(tribool down, bool previousState) override;
+	void showPopupWindow() override;
 	void hover(bool on) override;
 
 	void playNewDaySound();

+ 3 - 4
client/adventureMap/CList.cpp

@@ -31,7 +31,7 @@
 #include "../../lib/mapObjects/CGTownInstance.h"
 
 CList::CListItem::CListItem(CList * Parent)
-	: CIntObject(LCLICK | RCLICK | HOVER),
+	: CIntObject(LCLICK | SHOW_POPUP | HOVER),
 	parent(Parent),
 	selection()
 {
@@ -40,10 +40,9 @@ CList::CListItem::CListItem(CList * Parent)
 
 CList::CListItem::~CListItem() = default;
 
-void CList::CListItem::clickRight(tribool down, bool previousState)
+void CList::CListItem::showPopupWindow()
 {
-	if (down == true)
-		showTooltip();
+	showTooltip();
 }
 
 void CList::CListItem::clickLeft(tribool down, bool previousState)

+ 1 - 1
client/adventureMap/CList.h

@@ -35,7 +35,7 @@ protected:
 		CListItem(CList * parent);
 		~CListItem();
 
-		void clickRight(tribool down, bool previousState) override;
+		void showPopupWindow() override;
 		void clickLeft(tribool down, bool previousState) override;
 		void hover(bool on) override;
 		void onSelect(bool on);

+ 4 - 5
client/adventureMap/CMinimap.cpp

@@ -88,7 +88,7 @@ void CMinimapInstance::showAll(Canvas & to)
 }
 
 CMinimap::CMinimap(const Rect & position)
-	: CIntObject(LCLICK | RCLICK | HOVER | MOVE | GESTURE_PANNING, position.topLeft()),
+	: CIntObject(LCLICK | SHOW_POPUP | HOVER | MOVE | GESTURE, position.topLeft()),
 	level(0)
 {
 	OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
@@ -149,10 +149,9 @@ void CMinimap::clickLeft(tribool down, bool previousState)
 		moveAdvMapSelection(GH.getCursorPosition());
 }
 
-void CMinimap::clickRight(tribool down, bool previousState)
+void CMinimap::showPopupWindow()
 {
-	if (down)
-		CRClickPopup::createAndPush(CGI->generaltexth->zelp[291].second);
+	CRClickPopup::createAndPush(CGI->generaltexth->zelp[291].second);
 }
 
 void CMinimap::hover(bool on)
@@ -165,7 +164,7 @@ void CMinimap::hover(bool on)
 
 void CMinimap::mouseMoved(const Point & cursorPosition)
 {
-	if(isMouseButtonPressed(MouseButton::LEFT))
+	if(isMouseLeftButtonPressed())
 		moveAdvMapSelection(cursorPosition);
 }
 

+ 1 - 1
client/adventureMap/CMinimap.h

@@ -45,7 +45,7 @@ class CMinimap : public CIntObject
 
 	void gesturePanning(const Point & initialPosition, const Point & currentPosition, const Point & lastUpdateDistance) override;
 	void clickLeft(tribool down, bool previousState) override;
-	void clickRight(tribool down, bool previousState) override;
+	void showPopupWindow() override;
 	void hover (bool on) override;
 	void mouseMoved (const Point & cursorPosition) override;
 

+ 0 - 2
client/adventureMap/CResDataBar.cpp

@@ -34,8 +34,6 @@ CResDataBar::CResDataBar(const std::string & imageName, const Point & position)
 
 	pos.w = background->pos.w;
 	pos.h = background->pos.h;
-
-	addUsedEvents(RCLICK);
 }
 
 CResDataBar::CResDataBar(const std::string & defname, int x, int y, int offx, int offy, int resdist, int datedist):

+ 7 - 11
client/battle/BattleFieldController.cpp

@@ -155,7 +155,7 @@ BattleFieldController::BattleFieldController(BattleInterface & owner):
 	backgroundWithHexes = std::make_unique<Canvas>(Point(background->width(), background->height()));
 
 	updateAccessibleHexes();
-	addUsedEvents(LCLICK | RCLICK | MOVE | TIME | GESTURE_PANNING);
+	addUsedEvents(LCLICK | SHOW_POPUP | MOVE | TIME | GESTURE);
 }
 
 void BattleFieldController::activate()
@@ -176,7 +176,7 @@ void BattleFieldController::createHeroes()
 		owner.defendingHero = std::make_shared<BattleHero>(owner, owner.defendingHeroInstance, true);
 }
 
-void BattleFieldController::panning(bool on, const Point & initialPosition, const Point & finalPosition)
+void BattleFieldController::gesture(bool on, const Point & initialPosition, const Point & finalPosition)
 {
 	if (!on && pos.isInside(finalPosition))
 		clickLeft(false, false);
@@ -219,16 +219,12 @@ void BattleFieldController::clickLeft(tribool down, bool previousState)
 	}
 }
 
-void BattleFieldController::clickRight(tribool down, bool previousState)
+void BattleFieldController::showPopupWindow()
 {
-	if(down)
-	{
-		BattleHex selectedHex = getHoveredHex();
+	BattleHex selectedHex = getHoveredHex();
 
-		if (selectedHex != BattleHex::INVALID)
-			owner.actionsController->onHexRightClicked(selectedHex);
-
-	}
+	if (selectedHex != BattleHex::INVALID)
+		owner.actionsController->onHexRightClicked(selectedHex);
 }
 
 void BattleFieldController::renderBattlefield(Canvas & canvas)
@@ -858,7 +854,7 @@ void BattleFieldController::show(Canvas & to)
 
 	renderBattlefield(to);
 
-	if (isActive() && isPanning() && getHoveredHex() != BattleHex::INVALID)
+	if (isActive() && isGesturing() && getHoveredHex() != BattleHex::INVALID)
 	{
 		auto cursorIndex = CCS->curh->get<Cursor::Combat>();
 		auto imageIndex = static_cast<size_t>(cursorIndex);

+ 2 - 2
client/battle/BattleFieldController.h

@@ -88,11 +88,11 @@ class BattleFieldController : public CIntObject
 	bool isPixelInHex(Point const & position);
 	size_t selectBattleCursor(BattleHex myNumber);
 
-	void panning(bool on, const Point & initialPosition, const Point & finalPosition) override;
+	void gesture(bool on, const Point & initialPosition, const Point & finalPosition) override;
 	void gesturePanning(const Point & initialPosition, const Point & currentPosition, const Point & lastUpdateDistance) override;
 	void mouseMoved(const Point & cursorPosition) override;
 	void clickLeft(tribool down, bool previousState) override;
-	void clickRight(tribool down, bool previousState) override;
+	void showPopupWindow() override;
 	void activate() override;
 
 	void showAll(Canvas & to) override;

+ 15 - 14
client/battle/BattleInterfaceClasses.cpp

@@ -652,14 +652,6 @@ StackQueue::StackQueue(bool Embedded, BattleInterface & owner)
 
 void StackQueue::show(Canvas & to)
 {
-	auto unitIdsToHighlight = owner.stacksController->getHoveredStacksUnitIds();
-
-	for(auto & stackBox : stackBoxes)
-	{
-		bool isBoundUnitCurrentlyHovered = vstd::contains(unitIdsToHighlight, stackBox->getBoundUnitID());
-		stackBox->toggleHighlight(isBoundUnitCurrentlyHovered);
-	}
-
 	if (embedded)
 		showAll(to);
 	CIntObject::show(to);
@@ -692,7 +684,7 @@ std::optional<uint32_t> StackQueue::getHoveredUnitIdIfAny() const
 {
 	for(const auto & stackBox : stackBoxes)
 	{
-		if(stackBox->isHovered() || stackBox->isMouseButtonPressed(MouseButton::RIGHT))
+		if(stackBox->isHovered())
 		{
 			return stackBox->getBoundUnitID();
 		}
@@ -702,7 +694,7 @@ std::optional<uint32_t> StackQueue::getHoveredUnitIdIfAny() const
 }
 
 StackQueue::StackBox::StackBox(StackQueue * owner):
-	CIntObject(RCLICK | HOVER), owner(owner)
+	CIntObject(SHOW_POPUP | HOVER), owner(owner)
 {
 	OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
 	background = std::make_shared<CPicture>(owner->embedded ? "StackQueueSmall" : "StackQueueLarge");
@@ -783,15 +775,24 @@ std::optional<uint32_t> StackQueue::StackBox::getBoundUnitID() const
 	return boundUnitID;
 }
 
-void StackQueue::StackBox::toggleHighlight(bool value)
+bool StackQueue::StackBox::isBoundUnitHighlighted() const
 {
-	highlighted = value;
+	auto unitIdsToHighlight = owner->owner.stacksController->getHoveredStacksUnitIds();
+	return vstd::contains(unitIdsToHighlight, getBoundUnitID());
 }
 
-void StackQueue::StackBox::show(Canvas & to)
+void StackQueue::StackBox::showAll(Canvas & to)
 {
-	if(highlighted)
+	CIntObject::showAll(to);
+
+	if(isBoundUnitHighlighted())
 		to.drawBorder(background->pos, Colors::CYAN, 2);
+}
 
+void StackQueue::StackBox::show(Canvas & to)
+{
 	CIntObject::show(to);
+
+	if(isBoundUnitHighlighted())
+		to.drawBorder(background->pos, Colors::CYAN, 2);
 }

+ 5 - 4
client/battle/BattleInterfaceClasses.h

@@ -167,20 +167,21 @@ class StackQueue : public CIntObject
 	{
 		StackQueue * owner;
 		std::optional<uint32_t> boundUnitID;
-		bool highlighted = false;
 
-	public:
 		std::shared_ptr<CPicture> background;
 		std::shared_ptr<CAnimImage> icon;
 		std::shared_ptr<CLabel> amount;
 		std::shared_ptr<CAnimImage> stateIcon;
 
+		void show(Canvas & to) override;
+		void showAll(Canvas & to) override;
+
+		bool isBoundUnitHighlighted() const;
+	public:
 		StackBox(StackQueue * owner);
 		void setUnit(const battle::Unit * unit, size_t turn = 0);
-		void toggleHighlight(bool value);
 		std::optional<uint32_t> getBoundUnitID() const;
 
-		void show(Canvas & to) override;
 	};
 
 	static const int QUEUE_SIZE = 10;

+ 2 - 0
client/eventsSDL/InputHandler.cpp

@@ -49,6 +49,7 @@ void InputHandler::handleCurrentEvent(const SDL_Event & current)
 			return keyboardHandler->handleEventKeyDown(current.key);
 		case SDL_KEYUP:
 			return keyboardHandler->handleEventKeyUp(current.key);
+#ifndef VCMI_EMULATE_TOUCHSCREEN_WITH_MOUSE
 		case SDL_MOUSEMOTION:
 			return mouseHandler->handleEventMouseMotion(current.motion);
 		case SDL_MOUSEBUTTONDOWN:
@@ -57,6 +58,7 @@ void InputHandler::handleCurrentEvent(const SDL_Event & current)
 			return mouseHandler->handleEventMouseButtonUp(current.button);
 		case SDL_MOUSEWHEEL:
 			return mouseHandler->handleEventMouseWheel(current.wheel);
+#endif
 		case SDL_TEXTINPUT:
 			return textHandler->handleEventTextInput(current.text);
 		case SDL_TEXTEDITING:

+ 4 - 4
client/eventsSDL/InputSourceMouse.cpp

@@ -42,10 +42,10 @@ void InputSourceMouse::handleEventMouseButtonDown(const SDL_MouseButtonEvent & b
 			if(button.clicks > 1)
 				GH.events().dispatchMouseDoubleClick(position);
 			else
-				GH.events().dispatchMouseButtonPressed(MouseButton::LEFT, position);
+				GH.events().dispatchMouseLeftButtonPressed(position);
 			break;
 		case SDL_BUTTON_RIGHT:
-			GH.events().dispatchMouseButtonPressed(MouseButton::RIGHT, position);
+			GH.events().dispatchShowPopup(position);
 			break;
 		case SDL_BUTTON_MIDDLE:
 			middleClickPosition = position;
@@ -66,10 +66,10 @@ void InputSourceMouse::handleEventMouseButtonUp(const SDL_MouseButtonEvent & but
 	switch(button.button)
 	{
 		case SDL_BUTTON_LEFT:
-			GH.events().dispatchMouseButtonReleased(MouseButton::LEFT, position);
+			GH.events().dispatchMouseLeftButtonReleased(position);
 			break;
 		case SDL_BUTTON_RIGHT:
-			GH.events().dispatchMouseButtonReleased(MouseButton::RIGHT, position);
+			GH.events().dispatchClosePopup(position);
 			break;
 		case SDL_BUTTON_MIDDLE:
 			GH.events().dispatchGesturePanningEnded(middleClickPosition, position);

+ 25 - 10
client/eventsSDL/InputSourceTouch.cpp

@@ -20,6 +20,7 @@
 #include "../gui/CGuiHandler.h"
 #include "../gui/EventDispatcher.h"
 #include "../gui/MouseButton.h"
+#include "../gui/WindowHandler.h"
 
 #include <SDL_events.h>
 #include <SDL_hints.h>
@@ -30,13 +31,18 @@ InputSourceTouch::InputSourceTouch()
 {
 	params.useRelativeMode = settings["general"]["userRelativePointer"].Bool();
 	params.relativeModeSpeedFactor = settings["general"]["relativePointerSpeedMultiplier"].Float();
+	params.longTouchTimeMilliseconds = settings["general"]["longTouchTimeMilliseconds"].Float();
 
 	if (params.useRelativeMode)
 		state = TouchState::RELATIVE_MODE;
 	else
 		state = TouchState::IDLE;
 
+#ifdef VCMI_EMULATE_TOUCHSCREEN_WITH_MOUSE
+	SDL_SetHint(SDL_HINT_MOUSE_TOUCH_EVENTS, "1");
+#else
 	SDL_SetHint(SDL_HINT_MOUSE_TOUCH_EVENTS, "0");
+#endif
 	SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "0");
 }
 
@@ -92,6 +98,9 @@ void InputSourceTouch::handleEventFingerMotion(const SDL_TouchFingerEvent & tfin
 
 void InputSourceTouch::handleEventFingerDown(const SDL_TouchFingerEvent & tfinger)
 {
+	// FIXME: better place to update potentially changed settings?
+	params.longTouchTimeMilliseconds = settings["general"]["longTouchTimeMilliseconds"].Float();
+
 	lastTapTimeTicks = tfinger.timestamp;
 
 	switch(state)
@@ -100,8 +109,10 @@ void InputSourceTouch::handleEventFingerDown(const SDL_TouchFingerEvent & tfinge
 		{
 			if(tfinger.x > 0.5)
 			{
-				MouseButton button =  tfinger.y < 0.5 ? MouseButton::RIGHT : MouseButton::LEFT;
-				GH.events().dispatchMouseButtonPressed(button, GH.getCursorPosition());
+				if (tfinger.y < 0.5)
+					GH.events().dispatchShowPopup(GH.getCursorPosition());
+				else
+					GH.events().dispatchMouseLeftButtonPressed(GH.getCursorPosition());
 			}
 			break;
 		}
@@ -138,8 +149,10 @@ void InputSourceTouch::handleEventFingerUp(const SDL_TouchFingerEvent & tfinger)
 		{
 			if(tfinger.x > 0.5)
 			{
-				MouseButton button =  tfinger.y < 0.5 ? MouseButton::RIGHT : MouseButton::LEFT;
-				GH.events().dispatchMouseButtonReleased(button, GH.getCursorPosition());
+				if (tfinger.y < 0.5)
+					GH.events().dispatchClosePopup(GH.getCursorPosition());
+				else
+					GH.events().dispatchMouseLeftButtonReleased(GH.getCursorPosition());
 			}
 			break;
 		}
@@ -151,8 +164,8 @@ void InputSourceTouch::handleEventFingerUp(const SDL_TouchFingerEvent & tfinger)
 		case TouchState::TAP_DOWN_SHORT:
 		{
 			GH.input().setCursorPosition(convertTouchToMouse(tfinger));
-			GH.events().dispatchMouseButtonPressed(MouseButton::LEFT, convertTouchToMouse(tfinger));
-			GH.events().dispatchMouseButtonReleased(MouseButton::LEFT, convertTouchToMouse(tfinger));
+			GH.events().dispatchMouseLeftButtonPressed(convertTouchToMouse(tfinger));
+			GH.events().dispatchMouseLeftButtonReleased(convertTouchToMouse(tfinger));
 			state = TouchState::IDLE;
 			break;
 		}
@@ -186,7 +199,7 @@ void InputSourceTouch::handleEventFingerUp(const SDL_TouchFingerEvent & tfinger)
 			if (SDL_GetNumTouchFingers(tfinger.touchId) == 0)
 			{
 				GH.input().setCursorPosition(convertTouchToMouse(tfinger));
-				GH.events().dispatchMouseButtonReleased(MouseButton::RIGHT, convertTouchToMouse(tfinger));
+				GH.events().dispatchClosePopup(convertTouchToMouse(tfinger));
 				state = TouchState::IDLE;
 			}
 			break;
@@ -199,10 +212,12 @@ void InputSourceTouch::handleUpdate()
 	if ( state == TouchState::TAP_DOWN_SHORT)
 	{
 		uint32_t currentTime = SDL_GetTicks();
-		if (currentTime > lastTapTimeTicks + params.longPressTimeMilliseconds)
+		if (currentTime > lastTapTimeTicks + params.longTouchTimeMilliseconds)
 		{
-			state = TouchState::TAP_DOWN_LONG;
-			GH.events().dispatchMouseButtonPressed(MouseButton::RIGHT, GH.getCursorPosition());
+			GH.events().dispatchShowPopup(GH.getCursorPosition());
+
+			if (GH.windows().isTopWindowPopup())
+				state = TouchState::TAP_DOWN_LONG;
 		}
 	}
 }

+ 6 - 12
client/eventsSDL/InputSourceTouch.h

@@ -12,6 +12,9 @@
 
 #include "../../lib/Point.h"
 
+// Debug option. If defined, mouse events will instead generate touch events, allowing testing of touch input on desktop
+// #define VCMI_EMULATE_TOUCHSCREEN_WITH_MOUSE
+
 enum class MouseButton;
 struct SDL_TouchFingerEvent;
 
@@ -33,7 +36,7 @@ enum class TouchState
 	// DOWN -> transition to TAP_DOWN_DOUBLE
 	// MOTION -> transition to TAP_DOWN_PANNING
 	// UP -> transition to IDLE, emit onLeftClickDown and onLeftClickUp
-	// on timer -> transition to TAP_DOWN_LONG, emit onRightClickDown event
+	// on timer -> transition to TAP_DOWN_LONG, emit showPopup() event
 	TAP_DOWN_SHORT,
 
 	// single finger is moving across screen
@@ -57,17 +60,8 @@ enum class TouchState
 	// right-click popup is active, waiting for new tap to hide popup
 	// DOWN -> ignored
 	// MOTION -> ignored
-	// UP -> transition to IDLE, generate onRightClickUp() event
+	// UP -> transition to IDLE, generate closePopup() event
 	TAP_DOWN_LONG_AWAIT,
-
-
-	// Possible transitions:
-	//                               -> DOUBLE
-	//                    -> PANNING -> IDLE
-	// IDLE -> DOWN_SHORT -> IDLE
-	//                    -> LONG -> IDLE
-	//                    -> DOUBLE -> PANNING
-	//                              -> IDLE
 };
 
 struct TouchInputParameters
@@ -76,7 +70,7 @@ struct TouchInputParameters
 	double relativeModeSpeedFactor = 1.0;
 
 	/// tap for period longer than specified here will be qualified as "long tap", triggering corresponding gesture
-	uint32_t longPressTimeMilliseconds = 750;
+	uint32_t longTouchTimeMilliseconds = 750;
 
 	/// moving finger for distance larger than specified will be qualified as panning gesture instead of long press
 	uint32_t panningSensitivityThreshold = 10;

+ 6 - 1
client/gui/CIntObject.cpp

@@ -90,7 +90,7 @@ void CIntObject::deactivate()
 	if (!isActive())
 		return;
 
-	deactivateEvents(ALL);
+	deactivateEvents(used | GENERAL);
 
 	assert(!isActive());
 
@@ -239,6 +239,11 @@ void CIntObject::onScreenResize()
 	center(pos, true);
 }
 
+bool CIntObject::isPopupWindow() const
+{
+	return false;
+}
+
 const Rect & CIntObject::center( const Rect &r, bool propagate )
 {
 	pos.w = r.w;

+ 7 - 0
client/gui/CIntObject.h

@@ -34,6 +34,7 @@ public:
 	virtual void show(Canvas & to) = 0;
 	virtual void showAll(Canvas & to) = 0;
 
+	virtual bool isPopupWindow() const = 0;
 	virtual void onScreenResize() = 0;
 	virtual ~IShowActivatable() = default;
 };
@@ -93,10 +94,16 @@ public:
 	//request complete redraw of this object
 	void redraw() override;
 
+	/// returns true if this element is a popup window
+	/// called only for windows
+	bool isPopupWindow() const override;
+
 	/// called only for windows whenever screen size changes
 	/// default behavior is to re-center, can be overriden
 	void onScreenResize() override;
 
+	/// returns true if UI elements wants to handle event of specific type (LCLICK, SHOW_POPUP ...)
+	/// by default, usedEvents inside UI elements are always handled
 	bool receiveEvent(const Point & position, int eventType) const override;
 
 	const Rect & center(const Rect &r, bool propagate = true); //sets pos so that r will be in the center of screen, assigns sizes of r to pos, returns new position

+ 45 - 42
client/gui/EventDispatcher.cpp

@@ -14,6 +14,7 @@
 #include "FramerateManager.h"
 #include "CGuiHandler.h"
 #include "MouseButton.h"
+#include "WindowHandler.h"
 
 #include "../../lib/Point.h"
 
@@ -27,7 +28,7 @@ void EventDispatcher::processLists(ui16 activityFlag, const Functor & cb)
 	};
 
 	processList(AEventsReceiver::LCLICK, lclickable);
-	processList(AEventsReceiver::RCLICK, rclickable);
+	processList(AEventsReceiver::SHOW_POPUP, rclickable);
 	processList(AEventsReceiver::HOVER, hoverable);
 	processList(AEventsReceiver::MOVE, motioninterested);
 	processList(AEventsReceiver::KEYBOARD, keyinterested);
@@ -35,7 +36,7 @@ void EventDispatcher::processLists(ui16 activityFlag, const Functor & cb)
 	processList(AEventsReceiver::WHEEL, wheelInterested);
 	processList(AEventsReceiver::DOUBLECLICK, doubleClickInterested);
 	processList(AEventsReceiver::TEXTINPUT, textInterested);
-	processList(AEventsReceiver::GESTURE_PANNING, panningInterested);
+	processList(AEventsReceiver::GESTURE, panningInterested);
 }
 
 void EventDispatcher::activateElement(AEventsReceiver * elem, ui16 activityFlag)
@@ -112,18 +113,6 @@ void EventDispatcher::dispatchShortcutReleased(const std::vector<EShortcut> & sh
 	}
 }
 
-EventDispatcher::EventReceiversList & EventDispatcher::getListForMouseButton(MouseButton button)
-{
-	switch (button)
-	{
-		case MouseButton::LEFT:
-			return lclickable;
-		case MouseButton::RIGHT:
-			return rclickable;
-	}
-	throw std::runtime_error("Invalid mouse button in getListForMouseButton");
-}
-
 void EventDispatcher::dispatchMouseDoubleClick(const Point & position)
 {
 	bool doubleClicked = false;
@@ -142,50 +131,64 @@ void EventDispatcher::dispatchMouseDoubleClick(const Point & position)
 	}
 
 	if(!doubleClicked)
-		dispatchMouseButtonPressed(MouseButton::LEFT, position);
+		handleLeftButtonClick(true);
+}
+
+void EventDispatcher::dispatchMouseLeftButtonPressed(const Point & position)
+{
+	handleLeftButtonClick(true);
+}
+
+void EventDispatcher::dispatchMouseLeftButtonReleased(const Point & position)
+{
+	handleLeftButtonClick(false);
 }
 
-void EventDispatcher::dispatchMouseButtonPressed(const MouseButton & button, const Point & position)
+void EventDispatcher::dispatchShowPopup(const Point & position)
 {
-	handleMouseButtonClick(getListForMouseButton(button), button, true);
+	auto hlp = rclickable;
+	for(auto & i : hlp)
+	{
+		if(!vstd::contains(rclickable, i))
+			continue;
+
+		if( !i->receiveEvent(GH.getCursorPosition(), AEventsReceiver::LCLICK))
+			continue;
+
+		i->showPopupWindow();
+	}
 }
 
-void EventDispatcher::dispatchMouseButtonReleased(const MouseButton & button, const Point & position)
+void EventDispatcher::dispatchClosePopup(const Point & position)
 {
-	handleMouseButtonClick(getListForMouseButton(button), button, false);
+	if (GH.windows().isTopWindowPopup())
+		GH.windows().popWindows(1);
+
+	assert(!GH.windows().isTopWindowPopup());
 }
 
-void EventDispatcher::handleMouseButtonClick(EventReceiversList & interestedObjs, MouseButton btn, bool isPressed)
+void EventDispatcher::handleLeftButtonClick(bool isPressed)
 {
-	auto hlp = interestedObjs;
+	auto hlp = lclickable;
 	for(auto & i : hlp)
 	{
-		if(!vstd::contains(interestedObjs, i))
+		if(!vstd::contains(lclickable, i))
 			continue;
 
-		auto prev = i->isMouseButtonPressed(btn);
+		auto prev = i->isMouseLeftButtonPressed();
 
 		if(!isPressed)
-			i->currentMouseState[btn] = isPressed;
+			i->mouseClickedState = isPressed;
 
-		if( btn == MouseButton::LEFT && i->receiveEvent(GH.getCursorPosition(), AEventsReceiver::LCLICK))
+		if( i->receiveEvent(GH.getCursorPosition(), AEventsReceiver::LCLICK))
 		{
 			if(isPressed)
-				i->currentMouseState[btn] = isPressed;
+				i->mouseClickedState = isPressed;
 			i->clickLeft(isPressed, prev);
 		}
-		else if( btn == MouseButton::RIGHT && i->receiveEvent(GH.getCursorPosition(), AEventsReceiver::RCLICK))
-		{
-			if(isPressed)
-				i->currentMouseState[btn] = isPressed;
-			i->clickRight(isPressed, prev);
-		}
 		else if(!isPressed)
 		{
-			if (btn == MouseButton::LEFT)
-				i->clickLeft(boost::logic::indeterminate, prev);
-			if (btn == MouseButton::RIGHT)
-				i->clickRight(boost::logic::indeterminate, prev);
+			i->clickLeft(boost::logic::indeterminate, prev);
 		}
 	}
 }
@@ -225,9 +228,9 @@ void EventDispatcher::dispatchGesturePanningStarted(const Point & initialPositio
 
 	for(auto it : copied)
 	{
-		if (it->receiveEvent(initialPosition, AEventsReceiver::GESTURE_PANNING))
+		if (it->receiveEvent(initialPosition, AEventsReceiver::GESTURE))
 		{
-			it->panning(true, initialPosition, initialPosition);
+			it->gesture(true, initialPosition, initialPosition);
 			it->panningState = true;
 		}
 	}
@@ -239,9 +242,9 @@ void EventDispatcher::dispatchGesturePanningEnded(const Point & initialPosition,
 
 	for(auto it : copied)
 	{
-		if (it->isPanning())
+		if (it->isGesturing())
 		{
-			it->panning(false, initialPosition, finalPosition);
+			it->gesture(false, initialPosition, finalPosition);
 			it->panningState = false;
 		}
 	}
@@ -253,7 +256,7 @@ void EventDispatcher::dispatchGesturePanning(const Point & initialPosition, cons
 
 	for(auto it : copied)
 	{
-		if (it->isPanning())
+		if (it->isGesturing())
 			it->gesturePanning(initialPosition, currentPosition, lastUpdateDistance);
 	}
 }
@@ -262,7 +265,7 @@ void EventDispatcher::dispatchGesturePinch(const Point & initialPosition, double
 {
 	for(auto it : panningInterested)
 	{
-		if (it->isPanning())
+		if (it->isGesturing())
 			it->gesturePinch(initialPosition, distance);
 	}
 }

+ 6 - 4
client/gui/EventDispatcher.h

@@ -34,9 +34,8 @@ class EventDispatcher
 	EventReceiversList textInterested;
 	EventReceiversList panningInterested;
 
-	EventReceiversList & getListForMouseButton(MouseButton button);
+	void handleLeftButtonClick(bool isPressed);
 
-	void handleMouseButtonClick(EventReceiversList & interestedObjs, MouseButton btn, bool isPressed);
 
 	template<typename Functor>
 	void processLists(ui16 activityFlag, const Functor & cb);
@@ -56,12 +55,15 @@ public:
 	void dispatchShortcutReleased(const std::vector<EShortcut> & shortcuts);
 
 	/// Mouse events
-	void dispatchMouseButtonPressed(const MouseButton & button, const Point & position);
-	void dispatchMouseButtonReleased(const MouseButton & button, const Point & position);
+	void dispatchMouseLeftButtonPressed(const Point & position);
+	void dispatchMouseLeftButtonReleased(const Point & position);
 	void dispatchMouseScrolled(const Point & distance, const Point & position);
 	void dispatchMouseDoubleClick(const Point & position);
 	void dispatchMouseMoved(const Point & distance);
 
+	void dispatchShowPopup(const Point & position);
+	void dispatchClosePopup(const Point & position);
+
 	void dispatchGesturePanningStarted(const Point & initialPosition);
 	void dispatchGesturePanningEnded(const Point & initialPosition, const Point & finalPosition);
 	void dispatchGesturePanning(const Point & initialPosition, const Point & currentPosition, const Point & lastUpdateDistance);

+ 11 - 3
client/gui/EventsReceiver.cpp

@@ -18,6 +18,7 @@ AEventsReceiver::AEventsReceiver()
 	: activeState(0)
 	, hoveredState(false)
 	, panningState(false)
+	, mouseClickedState(false)
 {
 }
 
@@ -26,7 +27,7 @@ bool AEventsReceiver::isHovered() const
 	return hoveredState;
 }
 
-bool AEventsReceiver::isPanning() const
+bool AEventsReceiver::isGesturing() const
 {
 	return panningState;
 }
@@ -36,9 +37,9 @@ bool AEventsReceiver::isActive() const
 	return activeState;
 }
 
-bool AEventsReceiver::isMouseButtonPressed(MouseButton btn) const
+bool AEventsReceiver::isMouseLeftButtonPressed() const
 {
-	return currentMouseState.count(btn) ? currentMouseState.at(btn) : false;
+	return mouseClickedState;
 }
 
 void AEventsReceiver::activateEvents(ui16 what)
@@ -52,6 +53,13 @@ void AEventsReceiver::activateEvents(ui16 what)
 void AEventsReceiver::deactivateEvents(ui16 what)
 {
 	if (what & GENERAL)
+	{
+		assert((what & activeState) == activeState);
 		activeState &= ~GENERAL;
+
+		// sanity check to avoid unexpected behavior if assertion above fails (e.g. in release)
+		// if element is deactivated (has GENERAL flag) then all existing active events should also be deactivated
+		what = activeState;
+	}
 	GH.events().deactivateElement(this, what & activeState);
 }

+ 22 - 9
client/gui/EventsReceiver.h

@@ -14,7 +14,6 @@ class Point;
 VCMI_LIB_NAMESPACE_END
 
 class EventDispatcher;
-enum class MouseButton;
 enum class EShortcut;
 using boost::logic::tribool;
 
@@ -24,10 +23,10 @@ class AEventsReceiver
 {
 	friend class EventDispatcher;
 
-	std::map<MouseButton, bool> currentMouseState;
 	ui16 activeState;
 	bool hoveredState;
 	bool panningState;
+	bool mouseClickedState;
 
 protected:
 	/// Activates particular events for this UI element. Uses unnamed enum from this class
@@ -36,12 +35,13 @@ protected:
 	void deactivateEvents(ui16 what);
 
 	virtual void clickLeft(tribool down, bool previousState) {}
-	virtual void clickRight(tribool down, bool previousState) {}
+	virtual void showPopupWindow() {}
 	virtual void clickDouble() {}
 
 	/// Called when user pans screen by specified distance
 	virtual void gesturePanning(const Point & initialPosition, const Point & currentPosition, const Point & lastUpdateDistance) {}
 
+	/// Called when user pitches screen, requesting scaling by specified factor
 	virtual void gesturePinch(const Point & centerPosition, double lastUpdateFactor) {}
 
 	virtual void wheelScrolled(int distance) {}
@@ -50,8 +50,8 @@ protected:
 	/// Called when UI element hover status changes
 	virtual void hover(bool on) {}
 
-	/// Called when UI element panning gesture status changes
-	virtual void panning(bool on, const Point & initialPosition, const Point & finalPosition) {}
+	/// Called when UI element gesture status changes
+	virtual void gesture(bool on, const Point & initialPosition, const Point & finalPosition) {}
 
 	virtual void textInputed(const std::string & enteredText) {}
 	virtual void textEdited(const std::string & enteredText) {}
@@ -71,17 +71,30 @@ public:
 	virtual ~AEventsReceiver() = default;
 
 	/// These are the arguments that can be used to determine what kind of input UI element will receive
-	enum {LCLICK=1, RCLICK=2, HOVER=4, MOVE=8, KEYBOARD=16, TIME=32, GENERAL=64, WHEEL=128, DOUBLECLICK=256, TEXTINPUT=512, GESTURE_PANNING=1024, ALL=0xffff};
+	enum
+	{
+		LCLICK = 1,
+		SHOW_POPUP = 2,
+		HOVER = 4,
+		MOVE = 8,
+		KEYBOARD = 16,
+		TIME = 32,
+		GENERAL = 64,
+		WHEEL = 128,
+		DOUBLECLICK = 256,
+		TEXTINPUT = 512,
+		GESTURE = 1024,
+	};
 
 	/// Returns true if element is currently hovered by mouse
 	bool isHovered() const;
 
 	/// Returns true if panning/swiping gesture is currently active
-	bool isPanning() const;
+	bool isGesturing() const;
 
 	/// Returns true if element is currently active and may receive events
 	bool isActive() const;
 
-	/// Returns true if particular mouse button was pressed when inside this element
-	bool isMouseButtonPressed(MouseButton btn) const;
+	/// Returns true if left mouse button was pressed when inside this element
+	bool isMouseLeftButtonPressed() const;
 };

+ 8 - 0
client/gui/WindowHandler.cpp

@@ -48,6 +48,14 @@ void WindowHandler::pushWindow(std::shared_ptr<IShowActivatable> newInt)
 	totalRedraw();
 }
 
+bool WindowHandler::isTopWindowPopup() const
+{
+	if (windowsStack.empty())
+		return false;
+
+	return windowsStack.back()->isPopupWindow();
+}
+
 void WindowHandler::popWindows(int howMany)
 {
 	if(!howMany)

+ 3 - 0
client/gui/WindowHandler.h

@@ -43,6 +43,9 @@ public:
 	/// pops one or more windows - deactivates top, deletes and removes given number of windows, activates new front
 	void popWindows(int howMany);
 
+	/// returns true if current top window is a right-click popup
+	bool isTopWindowPopup() const;
+
 	/// removes given windows from the top and activates next
 	void popWindow(std::shared_ptr<IShowActivatable> top);
 

+ 2 - 2
client/lobby/CBonusSelection.cpp

@@ -447,7 +447,7 @@ void CBonusSelection::decreaseDifficulty()
 }
 
 CBonusSelection::CRegion::CRegion(int id, bool accessible, bool selectable, const CampaignRegions & campDsc)
-	: CIntObject(LCLICK | RCLICK), idOfMapAndRegion(id), accessible(accessible), selectable(selectable)
+	: CIntObject(LCLICK | SHOW_POPUP), idOfMapAndRegion(id), accessible(accessible), selectable(selectable)
 {
 	OBJ_CONSTRUCTION;
 	static const std::string colors[2][8] =
@@ -507,7 +507,7 @@ void CBonusSelection::CRegion::clickLeft(tribool down, bool previousState)
 	}
 }
 
-void CBonusSelection::CRegion::clickRight(tribool down, bool previousState)
+void CBonusSelection::CRegion::showPopupWindow()
 {
 	// FIXME: For some reason "down" is only ever contain indeterminate_value
 	auto text = CSH->si->campState->camp->scenarios[idOfMapAndRegion].regionText;

+ 1 - 1
client/lobby/CBonusSelection.h

@@ -46,7 +46,7 @@ public:
 		CRegion(int id, bool accessible, bool selectable, const CampaignRegions & campDsc);
 		void updateState();
 		void clickLeft(tribool down, bool previousState) override;
-		void clickRight(tribool down, bool previousState) override;
+		void showPopupWindow() override;
 	};
 
 	void createBonusesIcons();

+ 3 - 3
client/lobby/CSelectionBase.cpp

@@ -341,7 +341,7 @@ void CChatBox::addNewMessage(const std::string & text)
 }
 
 CFlagBox::CFlagBox(const Rect & rect)
-	: CIntObject(RCLICK)
+	: CIntObject(SHOW_POPUP)
 {
 	pos += rect.topLeft();
 	pos.w = rect.w;
@@ -378,9 +378,9 @@ void CFlagBox::recreate()
 	}
 }
 
-void CFlagBox::clickRight(tribool down, bool previousState)
+void CFlagBox::showPopupWindow()
 {
-	if(down && SEL->getMapInfo())
+	if(SEL->getMapInfo())
 		GH.windows().createAndPushWindow<CFlagBoxTooltipBox>(iconsTeamFlags);
 }
 

+ 1 - 1
client/lobby/CSelectionBase.h

@@ -135,7 +135,7 @@ class CFlagBox : public CIntObject
 public:
 	CFlagBox(const Rect & rect);
 	void recreate();
-	void clickRight(tribool down, bool previousState) override;
+	void showPopupWindow() override;
 	void showTeamsPopup();
 
 	class CFlagBoxTooltipBox : public CWindowObject

+ 8 - 11
client/lobby/OptionsTab.cpp

@@ -413,7 +413,7 @@ void OptionsTab::CPlayerOptionTooltipBox::genBonusWindow()
 }
 
 OptionsTab::SelectedBox::SelectedBox(Point position, PlayerSettings & settings, SelType type)
-	: Scrollable(RCLICK, position, Orientation::HORIZONTAL)
+	: Scrollable(SHOW_POPUP, position, Orientation::HORIZONTAL)
 	, CPlayerSettingsHelper(settings, type)
 {
 	OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
@@ -432,18 +432,15 @@ void OptionsTab::SelectedBox::update()
 	subtitle->setText(getName());
 }
 
-void OptionsTab::SelectedBox::clickRight(tribool down, bool previousState)
+void OptionsTab::SelectedBox::showPopupWindow()
 {
-	if(down)
-	{
-		// cases when we do not need to display a message
-		if(settings.castle == -2 && CPlayerSettingsHelper::type == TOWN)
-			return;
-		if(settings.hero == -2 && !SEL->getPlayerInfo(settings.color.getNum()).hasCustomMainHero() && CPlayerSettingsHelper::type == HERO)
-			return;
+	// cases when we do not need to display a message
+	if(settings.castle == -2 && CPlayerSettingsHelper::type == TOWN)
+		return;
+	if(settings.hero == -2 && !SEL->getPlayerInfo(settings.color.getNum()).hasCustomMainHero() && CPlayerSettingsHelper::type == HERO)
+		return;
 
-		GH.windows().createAndPushWindow<CPlayerOptionTooltipBox>(*this);
-	}
+	GH.windows().createAndPushWindow<CPlayerOptionTooltipBox>(*this);
 }
 
 void OptionsTab::SelectedBox::scrollBy(int distance)

+ 1 - 1
client/lobby/OptionsTab.h

@@ -101,7 +101,7 @@ public:
 		std::shared_ptr<CLabel> subtitle;
 
 		SelectedBox(Point position, PlayerSettings & settings, SelType type);
-		void clickRight(tribool down, bool previousState) override;
+		void showPopupWindow() override;
 		void scrollBy(int distance) override;
 
 		void update();

+ 1 - 1
client/lobby/RandomMapTab.cpp

@@ -474,7 +474,7 @@ void TemplatesDropBox::clickLeft(tribool down, bool previousState)
 		auto w = widget<CSlider>("slider");
 
 		// pop the interface only if the mouse is not clicking on the slider
-		if (!w || !w->isMouseButtonPressed(MouseButton::LEFT))
+		if (!w || !w->isMouseLeftButtonPressed())
 		{
 			assert(GH.windows().isTopWindow(this));
 			GH.windows().popWindows(1);

+ 2 - 7
client/mainmenu/CreditsScreen.cpp

@@ -20,7 +20,7 @@
 #include "../../lib/filesystem/Filesystem.h"
 
 CreditsScreen::CreditsScreen(Rect rect)
-	: CIntObject(LCLICK | RCLICK), positionCounter(0)
+	: CIntObject(LCLICK), positionCounter(0)
 {
 	pos.w = rect.w;
 	pos.h = rect.h;
@@ -43,15 +43,10 @@ void CreditsScreen::show(Canvas & to)
 
 	//end of credits, close this screen
 	if(credits->textSize.y + 600 < positionCounter / 2)
-		clickRight(false, false);
+		clickLeft(false, false);
 }
 
 void CreditsScreen::clickLeft(tribool down, bool previousState)
-{
-	clickRight(down, previousState);
-}
-
-void CreditsScreen::clickRight(tribool down, bool previousState)
 {
 	CTabbedInt * menu = dynamic_cast<CTabbedInt *>(parent);
 	assert(menu);

+ 0 - 1
client/mainmenu/CreditsScreen.h

@@ -22,5 +22,4 @@ public:
 	CreditsScreen(Rect rect);
 	void show(Canvas & to) override;
 	void clickLeft(tribool down, bool previousState) override;
-	void clickRight(tribool down, bool previousState) override;
 };

+ 4 - 4
client/mapView/MapViewActions.cpp

@@ -33,7 +33,7 @@ MapViewActions::MapViewActions(MapView & owner, const std::shared_ptr<MapViewMod
 	pos.w = model->getPixelsVisibleDimensions().x;
 	pos.h = model->getPixelsVisibleDimensions().y;
 
-	addUsedEvents(LCLICK | RCLICK | GESTURE_PANNING | HOVER | MOVE | WHEEL);
+	addUsedEvents(LCLICK | SHOW_POPUP | GESTURE | HOVER | MOVE | WHEEL);
 }
 
 void MapViewActions::setContext(const std::shared_ptr<IMapRendererContext> & context)
@@ -55,11 +55,11 @@ void MapViewActions::clickLeft(tribool down, bool previousState)
 		adventureInt->onTileLeftClicked(tile);
 }
 
-void MapViewActions::clickRight(tribool down, bool previousState)
+void MapViewActions::showPopupWindow()
 {
 	int3 tile = model->getTileAtPoint(GH.getCursorPosition() - pos.topLeft());
 
-	if(down && context->isInMap(tile))
+	if(context->isInMap(tile))
 		adventureInt->onTileRightClicked(tile);
 }
 
@@ -91,7 +91,7 @@ void MapViewActions::gesturePinch(const Point & centerPosition, double lastUpdat
 	pinchZoomFactor = newZoom;
 }
 
-void MapViewActions::panning(bool on, const Point & initialPosition, const Point & finalPosition)
+void MapViewActions::gesture(bool on, const Point & initialPosition, const Point & finalPosition)
 {
 	pinchZoomFactor = 1.0;
 }

+ 2 - 2
client/mapView/MapViewActions.h

@@ -32,11 +32,11 @@ public:
 	void setContext(const std::shared_ptr<IMapRendererContext> & context);
 
 	void clickLeft(tribool down, bool previousState) override;
-	void clickRight(tribool down, bool previousState) override;
+	void showPopupWindow() override;
 	void gesturePanning(const Point & initialPosition, const Point & currentPosition, const Point & lastUpdateDistance) override;
 	void gesturePinch(const Point & centerPosition, double lastUpdateFactor) override;
 	void hover(bool on) override;
-	void panning(bool on, const Point & initialPosition, const Point & finalPosition) override;
+	void gesture(bool on, const Point & initialPosition, const Point & finalPosition) override;
 	void mouseMoved(const Point & cursorPosition) override;
 	void wheelScrolled(int distance) override;
 };

+ 3 - 3
client/widgets/Buttons.cpp

@@ -199,9 +199,9 @@ void CButton::clickLeft(tribool down, bool previousState)
 	}
 }
 
-void CButton::clickRight(tribool down, bool previousState)
+void CButton::showPopupWindow()
 {
-	if(down && helpBox.size()) //there is no point to show window with nothing inside...
+	if(helpBox.size()) //there is no point to show window with nothing inside...
 		CRClickPopup::createAndPush(helpBox);
 }
 
@@ -236,7 +236,7 @@ CButton::CButton(Point position, const std::string &defName, const std::pair<std
     callback(Callback)
 {
 	defActions = 255-DISPOSE;
-	addUsedEvents(LCLICK | RCLICK | HOVER | KEYBOARD);
+	addUsedEvents(LCLICK | SHOW_POPUP | HOVER | KEYBOARD);
 
 	stateToIndex[0] = 0;
 	stateToIndex[1] = 1;

+ 1 - 1
client/widgets/Buttons.h

@@ -102,7 +102,7 @@ public:
 	void setPlayerColor(PlayerColor player);
 
 	/// CIntObject overrides
-	void clickRight(tribool down, bool previousState) override;
+	void showPopupWindow() override;
 	void clickLeft(tribool down, bool previousState) override;
 	void hover (bool on) override;
 	void showAll(Canvas & to) override;

+ 6 - 14
client/widgets/CArtifactHolder.cpp

@@ -72,11 +72,6 @@ void CArtPlace::clickLeft(tribool down, bool previousState)
 	LRClickableAreaWTextComp::clickLeft(down, previousState);
 }
 
-void CArtPlace::clickRight(tribool down, bool previousState)
-{
-	LRClickableAreaWTextComp::clickRight(down, previousState);
-}
-
 const CArtifactInstance * CArtPlace::getArt()
 {
 	return ourArt;
@@ -132,10 +127,10 @@ void CCommanderArtPlace::clickLeft(tribool down, bool previousState)
 		LOCPLINT->showYesNoDialog(CGI->generaltexth->translate("vcmi.commanderWindow.artifactMessage"), [this]() { returnArtToHeroCallback(); }, []() {});
 }
 
-void CCommanderArtPlace::clickRight(tribool down, bool previousState)
+void CCommanderArtPlace::showPopupWindow()
 {
-	if(ourArt && text.size() && down)
-		CArtPlace::clickRight(down, previousState);
+	if(ourArt && text.size())
+		CArtPlace::showPopupWindow();
 }
 
 void CCommanderArtPlace::setArtifact(const CArtifactInstance * art)
@@ -197,13 +192,10 @@ void CHeroArtPlace::clickLeft(tribool down, bool previousState)
 		leftClickCallback(*this);
 }
 
-void CHeroArtPlace::clickRight(tribool down, bool previousState)
+void CHeroArtPlace::showPopupWindow()
 {
-	if(down)
-	{
-		if(rightClickCallback)
-			rightClickCallback(*this);
-	}
+	if(rightClickCallback)
+		rightClickCallback(*this);
 }
 
 void CHeroArtPlace::showAll(Canvas & to)

+ 2 - 3
client/widgets/CArtifactHolder.h

@@ -42,7 +42,6 @@ protected:
 public:
 	CArtPlace(Point position, const CArtifactInstance * Art = nullptr);
 	void clickLeft(tribool down, bool previousState) override;
-	void clickRight(tribool down, bool previousState) override;
 	const CArtifactInstance * getArt();
 
 	virtual void setArtifact(const CArtifactInstance * art)=0;
@@ -60,7 +59,7 @@ protected:
 public:
 	CCommanderArtPlace(Point position, const CGHeroInstance * commanderOwner, ArtifactPosition artSlot, const CArtifactInstance * Art = nullptr);
 	void clickLeft(tribool down, bool previousState) override;
-	void clickRight(tribool down, bool previousState) override;
+	void showPopupWindow() override;
 	void setArtifact(const CArtifactInstance * art) override;
 };
 
@@ -79,7 +78,7 @@ public:
 	void selectSlot(bool on);
 	bool isMarked() const;
 	void clickLeft(tribool down, bool previousState) override;
-	void clickRight(tribool down, bool previousState) override;
+	void showPopupWindow() override;
 	void showAll(Canvas & to) override;
 	void setArtifact(const CArtifactInstance * art) override;
 	void addCombinedArtInfo(std::map<const CArtifact*, int> & arts);

+ 3 - 3
client/widgets/CComponent.cpp

@@ -54,7 +54,7 @@ void CComponent::init(Etype Type, int Subtype, int Val, ESize imageSize, EFonts
 {
 	OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
 
-	addUsedEvents(RCLICK);
+	addUsedEvents(SHOW_POPUP);
 
 	compType = Type;
 	subtype = Subtype;
@@ -258,9 +258,9 @@ void CComponent::setSurface(std::string defName, int imgPos)
 	image = std::make_shared<CAnimImage>(defName, imgPos);
 }
 
-void CComponent::clickRight(tribool down, bool previousState)
+void CComponent::showPopupWindow()
 {
-	if(down && !getDescription().empty())
+	if(!getDescription().empty())
 		CRClickPopup::createAndPush(getDescription());
 }
 

+ 1 - 1
client/widgets/CComponent.h

@@ -65,7 +65,7 @@ public:
 	CComponent(Etype Type, int Subtype, int Val = 0, ESize imageSize=large, EFonts font = FONT_SMALL);
 	CComponent(const Component &c, ESize imageSize=large, EFonts font = FONT_SMALL);
 
-	void clickRight(tribool down, bool previousState) override; //call-in
+	void showPopupWindow() override; //call-in
 };
 
 /// component that can be selected or deselected

+ 4 - 4
client/widgets/CGarrisonInt.cpp

@@ -286,9 +286,9 @@ bool CGarrisonSlot::mustForceReselection() const
 	return false;
 }
 
-void CGarrisonSlot::clickRight(tribool down, bool previousState)
+void CGarrisonSlot::showPopupWindow()
 {
-	if(creature && down)
+	if(creature)
 	{
 		GH.windows().createAndPushWindow<CStackWindow>(myStack, true);
 	}
@@ -357,13 +357,13 @@ void CGarrisonSlot::update()
 {
 	if(getObj() != nullptr)
 	{
-		addUsedEvents(LCLICK | RCLICK | HOVER);
+		addUsedEvents(LCLICK | SHOW_POPUP | HOVER);
 		myStack = getObj()->getStackPtr(ID);
 		creature = myStack ? myStack->type : nullptr;
 	}
 	else
 	{
-		removeUsedEvents(LCLICK | RCLICK | HOVER);
+		removeUsedEvents(LCLICK | SHOW_POPUP | HOVER);
 		myStack = nullptr;
 		creature = nullptr;
 	}

+ 1 - 1
client/widgets/CGarrisonInt.h

@@ -58,7 +58,7 @@ public:
 	bool our() const;
 	SlotID getSlot() const { return ID; }
 	bool ally() const;
-	void clickRight(tribool down, bool previousState) override;
+	void showPopupWindow() override;
 	void clickLeft(tribool down, bool previousState) override;
 	void update();
 	CGarrisonSlot(CGarrisonInt *Owner, int x, int y, SlotID IID, EGarrisonType Upg=EGarrisonType::UP, const CStackInstance * creature_ = nullptr);

+ 2 - 2
client/widgets/CWindowWithArtifacts.cpp

@@ -200,7 +200,7 @@ void CWindowWithArtifacts::rightClickArtPlaceHero(CArtifactsOfHeroBase & artsIns
 						return;
 					}
 					if(artPlace.text.size())
-						artPlace.LRClickableAreaWTextComp::clickRight(boost::logic::tribool::true_value, false);
+						artPlace.LRClickableAreaWTextComp::showPopupWindow();
 				}
 			}
 			// Altar window, Market window right click handler
@@ -209,7 +209,7 @@ void CWindowWithArtifacts::rightClickArtPlaceHero(CArtifactsOfHeroBase & artsIns
 				std::is_same_v<decltype(artSetWeak), std::weak_ptr<CArtifactsOfHeroMarket>>)
 			{
 				if(artPlace.getArt() && artPlace.text.size())
-					artPlace.LRClickableAreaWTextComp::clickRight(boost::logic::tribool::true_value, false);
+					artPlace.LRClickableAreaWTextComp::showPopupWindow();
 			}
 		}, artSetWeak.value());
 }

+ 9 - 24
client/widgets/MiscWidgets.cpp

@@ -57,9 +57,9 @@ void LRClickableAreaWText::clickLeft(tribool down, bool previousState)
 		LOCPLINT->showInfoDialog(text);
 	}
 }
-void LRClickableAreaWText::clickRight(tribool down, bool previousState)
+void LRClickableAreaWText::showPopupWindow()
 {
-	if (down && !text.empty())
+	if (!text.empty())
 		CRClickPopup::createAndPush(text);
 }
 
@@ -82,7 +82,7 @@ LRClickableAreaWText::~LRClickableAreaWText()
 
 void LRClickableAreaWText::init()
 {
-	addUsedEvents(LCLICK | RCLICK | HOVER);
+	addUsedEvents(LCLICK | SHOW_POPUP | HOVER);
 }
 
 void LRClickableAreaWTextComp::clickLeft(tribool down, bool previousState)
@@ -108,22 +108,19 @@ std::shared_ptr<CComponent> LRClickableAreaWTextComp::createComponent() const
 		return std::shared_ptr<CComponent>();
 }
 
-void LRClickableAreaWTextComp::clickRight(tribool down, bool previousState)
+void LRClickableAreaWTextComp::showPopupWindow()
 {
-	if(down)
+	if(auto comp = createComponent())
 	{
-		if(auto comp = createComponent())
-		{
-			CRClickPopup::createAndPush(text, CInfoWindow::TCompsInfo(1, comp));
-			return;
-		}
+		CRClickPopup::createAndPush(text, CInfoWindow::TCompsInfo(1, comp));
+		return;
 	}
 
-	LRClickableAreaWText::clickRight(down, previousState); //only if with-component variant not occurred
+	LRClickableAreaWText::showPopupWindow(); //only if with-component variant not occurred
 }
 
 CHeroArea::CHeroArea(int x, int y, const CGHeroInstance * _hero)
-	: CIntObject(LCLICK | RCLICK | HOVER),
+	: CIntObject(LCLICK | HOVER),
 	hero(_hero)
 {
 	OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
@@ -143,12 +140,6 @@ void CHeroArea::clickLeft(tribool down, bool previousState)
 		LOCPLINT->openHeroWindow(hero);
 }
 
-void CHeroArea::clickRight(tribool down, bool previousState)
-{
-	if(hero && (!down) && previousState)
-		LOCPLINT->openHeroWindow(hero);
-}
-
 void CHeroArea::hover(bool on)
 {
 	if (on && hero)
@@ -169,12 +160,6 @@ void LRClickableAreaOpenTown::clickLeft(tribool down, bool previousState)
 	}
 }
 
-void LRClickableAreaOpenTown::clickRight(tribool down, bool previousState)
-{
-	if(town && (!down) && previousState)
-		LOCPLINT->openTownWindow(town);//TODO: popup?
-}
-
 LRClickableAreaOpenTown::LRClickableAreaOpenTown(const Rect & Pos, const CGTownInstance * Town)
 	: LRClickableAreaWTextComp(Pos, -1), town(Town)
 {

+ 2 - 4
client/widgets/MiscWidgets.h

@@ -49,7 +49,7 @@ public:
 	void init();
 
 	virtual void clickLeft(tribool down, bool previousState) override;
-	virtual void clickRight(tribool down, bool previousState) override;
+	virtual void showPopupWindow() override;
 };
 
 /// base class for hero/town/garrison tooltips
@@ -136,7 +136,6 @@ public:
 	CHeroArea(int x, int y, const CGHeroInstance * _hero);
 
 	void clickLeft(tribool down, bool previousState) override;
-	void clickRight(tribool down, bool previousState) override;
 	void hover(bool on) override;
 };
 
@@ -147,7 +146,7 @@ public:
 	int baseType;
 	int bonusValue;
 	virtual void clickLeft(tribool down, bool previousState) override;
-	virtual void clickRight(tribool down, bool previousState) override;
+	virtual void showPopupWindow() override;
 
 	LRClickableAreaWTextComp(const Rect &Pos = Rect(0,0,0,0), int BaseType = -1);
 	std::shared_ptr<CComponent> createComponent() const;
@@ -159,7 +158,6 @@ class LRClickableAreaOpenTown: public LRClickableAreaWTextComp
 public:
 	const CGTownInstance * town;
 	void clickLeft(tribool down, bool previousState) override;
-	void clickRight(tribool down, bool previousState) override;
 	LRClickableAreaOpenTown(const Rect & Pos, const CGTownInstance * Town);
 };
 

+ 4 - 4
client/widgets/Scrollable.cpp

@@ -12,7 +12,7 @@
 #include "Scrollable.h"
 
 Scrollable::Scrollable(int used, Point position, Orientation orientation)
-	: CIntObject(used | WHEEL | GESTURE_PANNING, position)
+	: CIntObject(used | WHEEL | GESTURE, position)
 	, scrollStep(1)
 	, panningDistanceSingle(32)
 	, panningDistanceAccumulated(0)
@@ -20,7 +20,7 @@ Scrollable::Scrollable(int used, Point position, Orientation orientation)
 {
 }
 
-void Scrollable::panning(bool on, const Point & initialPosition, const Point & finalPosition)
+void Scrollable::gesture(bool on, const Point & initialPosition, const Point & finalPosition)
 {
 	panningDistanceAccumulated = 0;
 }
@@ -88,7 +88,7 @@ void Scrollable::setPanningStep(int to)
 void Scrollable::setScrollingEnabled(bool on)
 {
 	if (on)
-		addUsedEvents(WHEEL | GESTURE_PANNING);
+		addUsedEvents(WHEEL | GESTURE);
 	else
-		removeUsedEvents(WHEEL | GESTURE_PANNING);
+		removeUsedEvents(WHEEL | GESTURE);
 }

+ 1 - 1
client/widgets/Scrollable.h

@@ -33,7 +33,7 @@ class Scrollable : public CIntObject
 protected:
 	Scrollable(int used, Point position, Orientation orientation);
 
-	void panning(bool on, const Point & initialPosition, const Point & finalPosition) override;
+	void gesture(bool on, const Point & initialPosition, const Point & finalPosition) override;
 	void wheelScrolled(int distance) override;
 	void gesturePanning(const Point & initialPosition, const Point & currentPosition, const Point & lastUpdateDistance) override;
 

+ 2 - 2
client/widgets/Slider.cpp

@@ -142,7 +142,7 @@ void CSlider::clickLeft(tribool down, bool previousState)
 			return;
 		// 		if (rw>1) return;
 		// 		if (rw<0) return;
-		slider->clickLeft(true, slider->isMouseButtonPressed(MouseButton::LEFT));
+		slider->clickLeft(true, slider->isMouseLeftButtonPressed());
 		scrollTo((int)(rw * positions  +  0.5));
 		return;
 	}
@@ -151,7 +151,7 @@ void CSlider::clickLeft(tribool down, bool previousState)
 
 bool CSlider::receiveEvent(const Point &position, int eventType) const
 {
-	if (eventType != WHEEL && eventType != GESTURE_PANNING)
+	if (eventType != WHEEL && eventType != GESTURE)
 	{
 		return CIntObject::receiveEvent(position, eventType);
 	}

+ 22 - 32
client/windows/CCastleInterface.cpp

@@ -69,7 +69,7 @@ CBuildingRect::CBuildingRect(CCastleBuildings * Par, const CGTownInstance * Town
 	  area(nullptr),
 	  stateTimeCounter(BUILD_ANIMATION_FINISHED_TIMEPOINT)
 {
-	addUsedEvents(LCLICK | RCLICK | HOVER | TIME);
+	addUsedEvents(LCLICK | SHOW_POPUP | HOVER | TIME);
 	pos.x += str->pos.x;
 	pos.y += str->pos.y;
 
@@ -141,9 +141,9 @@ void CBuildingRect::clickLeft(tribool down, bool previousState)
 	}
 }
 
-void CBuildingRect::clickRight(tribool down, bool previousState)
+void CBuildingRect::showPopupWindow()
 {
-	if((!area) || (!((bool)down)) || (this!=parent->selectedBuilding) || getBuilding() == nullptr)
+	if((!area) || (this!=parent->selectedBuilding) || getBuilding() == nullptr)
 		return;
 	if( !area->isTransparent(GH.getCursorPosition() - pos.topLeft()) ) //inside building image
 	{
@@ -328,7 +328,7 @@ CHeroGSlot::CHeroGSlot(int x, int y, int updown, const CGHeroInstance * h, HeroS
 
 	set(h);
 
-	addUsedEvents(LCLICK | RCLICK | HOVER);
+	addUsedEvents(LCLICK | SHOW_POPUP | HOVER);
 }
 
 CHeroGSlot::~CHeroGSlot() = default;
@@ -415,9 +415,9 @@ void CHeroGSlot::clickLeft(tribool down, bool previousState)
 	}
 }
 
-void CHeroGSlot::clickRight(tribool down, bool previousState)
+void CHeroGSlot::showPopupWindow()
 {
-	if(hero && down)
+	if(hero)
 	{
 		GH.windows().createAndPushWindow<CInfoBoxPopup>(Point(pos.x + 175, pos.y + 100), hero);
 	}
@@ -1007,7 +1007,7 @@ CCreaInfo::CCreaInfo(Point position, const CGTownInstance * Town, int Level, boo
 		level = -1;
 		return;//No creature
 	}
-	addUsedEvents(LCLICK | RCLICK | HOVER);
+	addUsedEvents(LCLICK | SHOW_POPUP | HOVER);
 
 	ui32 creatureID = town->creatures[level].second.back();
 	creature = CGI->creh->objects[creatureID];
@@ -1089,15 +1089,12 @@ std::string CCreaInfo::genGrowthText()
 	return descr;
 }
 
-void CCreaInfo::clickRight(tribool down, bool previousState)
+void CCreaInfo::showPopupWindow()
 {
-	if(down)
-	{
-		if (showAvailable)
-			GH.windows().createAndPushWindow<CDwellingInfoBox>(GH.screenDimensions().x / 2, GH.screenDimensions().y / 2, town, level);
-		else
-			CRClickPopup::createAndPush(genGrowthText(), std::make_shared<CComponent>(CComponent::creature, creature->getId()));
-	}
+	if (showAvailable)
+		GH.windows().createAndPushWindow<CDwellingInfoBox>(GH.screenDimensions().x / 2, GH.screenDimensions().y / 2, town, level);
+	else
+		CRClickPopup::createAndPush(genGrowthText(), std::make_shared<CComponent>(CComponent::creature, creature->getId()));
 }
 
 bool CCreaInfo::getShowAvailable()
@@ -1110,7 +1107,7 @@ CTownInfo::CTownInfo(int posX, int posY, const CGTownInstance * Town, bool townH
 	building(nullptr)
 {
 	OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
-	addUsedEvents(RCLICK | HOVER);
+	addUsedEvents(SHOW_POPUP | HOVER);
 	pos.x += posX;
 	pos.y += posY;
 	int buildID;
@@ -1144,9 +1141,9 @@ void CTownInfo::hover(bool on)
 	}
 }
 
-void CTownInfo::clickRight(tribool down, bool previousState)
+void CTownInfo::showPopupWindow()
 {
-	if(building && down)
+	if(building)
 	{
 		auto c =  std::make_shared<CComponent>(CComponent::building, building->town->faction->getIndex(), building->bid);
 		CRClickPopup::createAndPush(CInfoWindow::genText(building->getNameTranslated(), building->getDescriptionTranslated()), c);
@@ -1342,7 +1339,7 @@ CHallInterface::CBuildingBox::CBuildingBox(int x, int y, const CGTownInstance *
 	building(Building)
 {
 	OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
-	addUsedEvents(LCLICK | RCLICK | HOVER);
+	addUsedEvents(LCLICK | SHOW_POPUP | HOVER);
 	pos.x += x;
 	pos.y += y;
 	pos.w = 154;
@@ -1396,10 +1393,9 @@ void CHallInterface::CBuildingBox::clickLeft(tribool down, bool previousState)
 		GH.windows().createAndPushWindow<CBuildWindow>(town,building,state,0);
 }
 
-void CHallInterface::CBuildingBox::clickRight(tribool down, bool previousState)
+void CHallInterface::CBuildingBox::showPopupWindow()
 {
-	if(down)
-		GH.windows().createAndPushWindow<CBuildWindow>(town,building,state,1);
+	GH.windows().createAndPushWindow<CBuildWindow>(town,building,state,1);
 }
 
 CHallInterface::CHallInterface(const CGTownInstance * Town):
@@ -1668,7 +1664,7 @@ CFortScreen::RecruitArea::RecruitArea(int posX, int posY, const CGTownInstance *
 	pos.h = 126;
 
 	if(!town->creatures[level].second.empty())
-		addUsedEvents(LCLICK | RCLICK | HOVER);//Activate only if dwelling is present
+		addUsedEvents(LCLICK | HOVER);//Activate only if dwelling is present
 
 	icons = std::make_shared<CPicture>("TPCAINFO", 261, 3);
 
@@ -1758,11 +1754,6 @@ void CFortScreen::RecruitArea::clickLeft(tribool down, bool previousState)
 		LOCPLINT->castleInt->builds->enterDwelling(level);
 }
 
-void CFortScreen::RecruitArea::clickRight(tribool down, bool previousState)
-{
-	clickLeft(down, false); //r-click does same as l-click - opens recr. window
-}
-
 CMageGuildScreen::CMageGuildScreen(CCastleInterface * owner,std::string imagem)
 	: CStatusbarWindow(BORDERED, imagem)
 {
@@ -1807,7 +1798,7 @@ CMageGuildScreen::Scroll::Scroll(Point position, const CSpell *Spell)
 {
 	OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
 
-	addUsedEvents(LCLICK | RCLICK | HOVER);
+	addUsedEvents(LCLICK | SHOW_POPUP | HOVER);
 	pos += position;
 	image = std::make_shared<CAnimImage>("SPELLSCR", spell->id);
 	pos = image->pos;
@@ -1819,10 +1810,9 @@ void CMageGuildScreen::Scroll::clickLeft(tribool down, bool previousState)
 		LOCPLINT->showInfoDialog(spell->getDescriptionTranslated(0), std::make_shared<CComponent>(CComponent::spell, spell->id));
 }
 
-void CMageGuildScreen::Scroll::clickRight(tribool down, bool previousState)
+void CMageGuildScreen::Scroll::showPopupWindow()
 {
-	if(down)
-		CRClickPopup::createAndPush(spell->getDescriptionTranslated(0), std::make_shared<CComponent>(CComponent::spell, spell->id));
+	CRClickPopup::createAndPush(spell->getDescriptionTranslated(0), std::make_shared<CComponent>(CComponent::spell, spell->id));
 }
 
 void CMageGuildScreen::Scroll::hover(bool on)

+ 6 - 7
client/windows/CCastleInterface.h

@@ -67,7 +67,7 @@ public:
 	bool operator<(const CBuildingRect & p2) const;
 	void hover(bool on) override;
 	void clickLeft(tribool down, bool previousState) override;
-	void clickRight(tribool down, bool previousState) override;
+	void showPopupWindow() override;
 	void mouseMoved (const Point & cursorPosition) override;
 	void tick(uint32_t msPassed) override;
 	void show(Canvas & to) override;
@@ -112,7 +112,7 @@ public:
 
 	void hover (bool on) override;
 	void clickLeft(tribool down, bool previousState) override;
-	void clickRight(tribool down, bool previousState) override;
+	void showPopupWindow() override;
 	void deactivate() override;
 };
 
@@ -192,7 +192,7 @@ public:
 	void update();
 	void hover(bool on) override;
 	void clickLeft(tribool down, bool previousState) override;
-	void clickRight(tribool down, bool previousState) override;
+	void showPopupWindow() override;
 	bool getShowAvailable();
 };
 
@@ -207,7 +207,7 @@ public:
 	CTownInfo(int posX, int posY, const CGTownInstance * town, bool townHall);
 
 	void hover(bool on) override;
-	void clickRight(tribool down, bool previousState) override;
+	void showPopupWindow() override;
 };
 
 /// Class which manages the castle window
@@ -274,7 +274,7 @@ class CHallInterface : public CStatusbarWindow
 		CBuildingBox(int x, int y, const CGTownInstance * Town, const CBuilding * Building);
 		void hover(bool on) override;
 		void clickLeft(tribool down, bool previousState) override;
-		void clickRight(tribool down, bool previousState) override;
+		void showPopupWindow() override;
 	};
 	const CGTownInstance * town;
 
@@ -346,7 +346,6 @@ class CFortScreen : public CStatusbarWindow
 		void creaturesChangedEventHandler();
 		void hover(bool on) override;
 		void clickLeft(tribool down, bool previousState) override;
-		void clickRight(tribool down, bool previousState) override;
 	};
 	std::shared_ptr<CLabel> title;
 	std::vector<std::shared_ptr<RecruitArea>> recAreas;
@@ -372,7 +371,7 @@ class CMageGuildScreen : public CStatusbarWindow
 	public:
 		Scroll(Point position, const CSpell *Spell);
 		void clickLeft(tribool down, bool previousState) override;
-		void clickRight(tribool down, bool previousState) override;
+		void showPopupWindow() override;
 		void hover(bool on) override;
 	};
 	std::shared_ptr<CPicture> window;

+ 0 - 6
client/windows/CCreatureWindow.cpp

@@ -120,12 +120,6 @@ void CCommanderSkillIcon::clickLeft(tribool down, bool previousState)
 		callback();
 }
 
-void CCommanderSkillIcon::clickRight(tribool down, bool previousState)
-{
-	if(down)
-		LRClickableAreaWText::clickRight(down, previousState);
-}
-
 static std::string skillToFile(int skill, int level, bool selected)
 {
 	// FIXME: is this a correct hadling?

+ 0 - 1
client/windows/CCreatureWindow.h

@@ -38,7 +38,6 @@ public:
 	std::function<void()> callback;
 
 	void clickLeft(tribool down, bool previousState) override;
-	void clickRight(tribool down, bool previousState) override;
 
 	void setObject(std::shared_ptr<CIntObject> object);
 };

+ 10 - 13
client/windows/CKingdomInterface.cpp

@@ -49,7 +49,7 @@ InfoBox::InfoBox(Point position, InfoPos Pos, InfoSize Size, std::shared_ptr<IIn
 	name(nullptr)
 {
 	assert(data);
-	addUsedEvents(LCLICK | RCLICK);
+	addUsedEvents(LCLICK | SHOW_POPUP);
 	EFonts font = (size < SIZE_MEDIUM)? FONT_SMALL: FONT_MEDIUM;
 
 	OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
@@ -90,18 +90,15 @@ InfoBox::InfoBox(Point position, InfoPos Pos, InfoSize Size, std::shared_ptr<IIn
 
 InfoBox::~InfoBox() = default;
 
-void InfoBox::clickRight(tribool down, bool previousState)
+void InfoBox::showPopupWindow()
 {
-	if (down)
-	{
-		std::shared_ptr<CComponent> comp;
-		std::string text;
-		data->prepareMessage(text, comp);
-		if (comp)
-			CRClickPopup::createAndPush(text, CInfoWindow::TCompsInfo(1, comp));
-		else if (!text.empty())
-			CRClickPopup::createAndPush(text);
-	}
+	std::shared_ptr<CComponent> comp;
+	std::string text;
+	data->prepareMessage(text, comp);
+	if (comp)
+		CRClickPopup::createAndPush(text, CInfoWindow::TCompsInfo(1, comp));
+	else if (!text.empty())
+		CRClickPopup::createAndPush(text);
 }
 
 void InfoBox::clickLeft(tribool down, bool previousState)
@@ -602,7 +599,7 @@ void CKingdomInterface::generateMinesList(const std::vector<const CGObjectInstan
 		std::string value = std::to_string(minesCount[i]);
 		auto data = std::make_shared<InfoBoxCustom>(value, "", "OVMINES", i, CGI->generaltexth->translate("core.minename", i));
 		minesBox[i] = std::make_shared<InfoBox>(Point(20+i*80, 31+footerPos), InfoBox::POS_INSIDE, InfoBox::SIZE_SMALL, data);
-		minesBox[i]->removeUsedEvents(LCLICK|RCLICK); //fixes #890 - mines boxes ignore clicks
+		minesBox[i]->removeUsedEvents(LCLICK|SHOW_POPUP); //fixes #890 - mines boxes ignore clicks
 	}
 	incomeArea = std::make_shared<CHoverableArea>();
 	incomeArea->pos = Rect(pos.x+580, pos.y+31+footerPos, 136, 68);

+ 1 - 1
client/windows/CKingdomInterface.h

@@ -73,7 +73,7 @@ public:
 	InfoBox(Point position, InfoPos Pos, InfoSize Size, std::shared_ptr<IInfoBoxData> Data);
 	~InfoBox();
 
-	void clickRight(tribool down, bool previousState) override;
+	void showPopupWindow() override;
 	void clickLeft(tribool down, bool previousState) override;
 
 	//Update object if data may have changed

+ 6 - 7
client/windows/CSpellWindow.cpp

@@ -43,7 +43,7 @@
 
 CSpellWindow::InteractiveArea::InteractiveArea(const Rect & myRect, std::function<void()> funcL, int helpTextId, CSpellWindow * _owner)
 {
-	addUsedEvents(LCLICK | RCLICK | HOVER);
+	addUsedEvents(LCLICK | SHOW_POPUP | HOVER);
 	pos = myRect;
 	onLeft = funcL;
 	hoverText = CGI->generaltexth->zelp[helpTextId].first;
@@ -57,10 +57,9 @@ void CSpellWindow::InteractiveArea::clickLeft(tribool down, bool previousState)
 		onLeft();
 }
 
-void CSpellWindow::InteractiveArea::clickRight(tribool down, bool previousState)
+void CSpellWindow::InteractiveArea::showPopupWindow()
 {
-	if (down)
-		CRClickPopup::createAndPush(helpText);
+	CRClickPopup::createAndPush(helpText);
 }
 
 void CSpellWindow::InteractiveArea::hover(bool on)
@@ -453,7 +452,7 @@ CSpellWindow::SpellArea::SpellArea(Rect pos, CSpellWindow * owner)
 {
 	this->pos = pos;
 	this->owner = owner;
-	addUsedEvents(LCLICK | RCLICK | HOVER);
+	addUsedEvents(LCLICK | SHOW_POPUP | HOVER);
 
 	schoolLevel = -1;
 	mySpell = nullptr;
@@ -541,9 +540,9 @@ void CSpellWindow::SpellArea::clickLeft(tribool down, bool previousState)
 	}
 }
 
-void CSpellWindow::SpellArea::clickRight(tribool down, bool previousState)
+void CSpellWindow::SpellArea::showPopupWindow()
 {
-	if(mySpell && down)
+	if(mySpell)
 	{
 		std::string dmgInfo;
 		auto causedDmg = owner->myInt->cb->estimateSpellDamage(mySpell, owner->myHero);

+ 2 - 2
client/windows/CSpellWindow.h

@@ -45,7 +45,7 @@ class CSpellWindow : public CWindowObject
 		void setSpell(const CSpell * spell);
 
 		void clickLeft(tribool down, bool previousState) override;
-		void clickRight(tribool down, bool previousState) override;
+		void showPopupWindow() override;
 		void hover(bool on) override;
 	};
 
@@ -58,7 +58,7 @@ class CSpellWindow : public CWindowObject
 		std::string helpText;
 	public:
 		void clickLeft(tribool down, bool previousState) override;
-		void clickRight(tribool down, bool previousState) override;
+		void showPopupWindow() override;
 		void hover(bool on) override;
 
 		InteractiveArea(const Rect &myRect, std::function<void()> funcL, int helpTextId, CSpellWindow * _owner);

+ 3 - 6
client/windows/CTradeWindow.cpp

@@ -38,7 +38,7 @@
 #include "../../lib/mapObjects/CGMarket.h"
 
 CTradeWindow::CTradeableItem::CTradeableItem(Point pos, EType Type, int ID, bool Left, int Serial)
-	: CIntObject(LCLICK | HOVER | RCLICK, pos),
+	: CIntObject(LCLICK | HOVER | SHOW_POPUP, pos),
 	type(EType(-1)),// set to invalid, will be corrected in setType
 	id(ID),
 	serial(Serial),
@@ -263,12 +263,10 @@ void CTradeWindow::CTradeableItem::hover(bool on)
 	}
 }
 
-void CTradeWindow::CTradeableItem::clickRight(tribool down, bool previousState)
+void CTradeWindow::CTradeableItem::showPopupWindow()
 {
-	if(down)
+	switch(type)
 	{
-		switch(type)
-		{
 		case CREATURE:
 		case CREATURE_PLACEHOLDER:
 			//GH.statusbar->print(boost::str(boost::format(CGI->generaltexth->allTexts[481]) % CGI->creh->objects[id]->namePl));
@@ -279,7 +277,6 @@ void CTradeWindow::CTradeableItem::clickRight(tribool down, bool previousState)
 			if(id >= 0)
 				CRClickPopup::createAndPush(CGI->artifacts()->getByIndex(id)->getDescriptionTranslated());
 			break;
-		}
 	}
 }
 

+ 1 - 1
client/windows/CTradeWindow.h

@@ -56,7 +56,7 @@ public:
 
 		void showAllAt(const Point & dstPos, const std::string & customSub, Canvas & to);
 
-		void clickRight(tribool down, bool previousState) override;
+		void showPopupWindow() override;
 		void hover(bool on) override;
 		void showAll(Canvas & to) override;
 		void clickLeft(tribool down, bool previousState) override;

+ 9 - 13
client/windows/CWindowObject.cpp

@@ -34,7 +34,7 @@
 #include <SDL_surface.h>
 
 CWindowObject::CWindowObject(int options_, std::string imageName, Point centerAt):
-	WindowBase(getUsedEvents(options_), Point()),
+	WindowBase(0, Point()),
 	options(options_),
 	background(createBg(imageName, options & PLAYER_COLORED))
 {
@@ -55,7 +55,7 @@ CWindowObject::CWindowObject(int options_, std::string imageName, Point centerAt
 }
 
 CWindowObject::CWindowObject(int options_, std::string imageName):
-	WindowBase(getUsedEvents(options_), Point()),
+	WindowBase(0, Point()),
 	options(options_),
 	background(createBg(imageName, options_ & PLAYER_COLORED))
 {
@@ -75,7 +75,11 @@ CWindowObject::CWindowObject(int options_, std::string imageName):
 		setShadow(true);
 }
 
-CWindowObject::~CWindowObject() = default;
+CWindowObject::~CWindowObject()
+{
+	if(options & RCLICK_POPUP)
+		CCS->curh->show();
+}
 
 std::shared_ptr<CPicture> CWindowObject::createBg(std::string imageName, bool playerColored)
 {
@@ -103,13 +107,6 @@ void CWindowObject::setBackground(std::string filename)
 	updateShadow();
 }
 
-int CWindowObject::getUsedEvents(int options)
-{
-	if (options & RCLICK_POPUP)
-		return RCLICK;
-	return 0;
-}
-
 void CWindowObject::updateShadow()
 {
 	setShadow(false);
@@ -235,10 +232,9 @@ void CWindowObject::showAll(Canvas & to)
 		CMessage::drawBorder(color, to.getInternalSurface(), pos.w+28, pos.h+29, pos.x-14, pos.y-15);
 }
 
-void CWindowObject::clickRight(tribool down, bool previousState)
+bool CWindowObject::isPopupWindow() const
 {
-	close();
-	CCS->curh->show();
+	return options & RCLICK_POPUP;
 }
 
 CStatusbarWindow::CStatusbarWindow(int options, std::string imageName, Point centerAt) : CWindowObject(options, imageName, centerAt)

+ 1 - 2
client/windows/CWindowObject.h

@@ -16,7 +16,6 @@ class CGStatusBar;
 class CWindowObject : public WindowBase
 {
 	std::shared_ptr<CPicture> createBg(std::string imageName, bool playerColored);
-	int getUsedEvents(int options);
 
 	std::vector<std::shared_ptr<CPicture>> shadowParts;
 
@@ -28,7 +27,7 @@ protected:
 	std::shared_ptr<CPicture> background;
 
 	//Used only if RCLICK_POPUP was set
-	void clickRight(tribool down, bool previousState) override;
+	bool isPopupWindow() const override;
 	//To display border
 	void updateShadow();
 	void setBackground(std::string filename);

+ 3 - 4
client/windows/CreaturePurchaseCard.cpp

@@ -114,7 +114,7 @@ void CreaturePurchaseCard::initView()
 }
 
 CreaturePurchaseCard::CCreatureClickArea::CCreatureClickArea(const Point & position, const std::shared_ptr<CCreaturePic> creaturePic, const CCreature * creatureOnTheCard)
-	: CIntObject(RCLICK),
+	: CIntObject(SHOW_POPUP),
 	creatureOnTheCard(creatureOnTheCard)
 {
 	pos.x += position.x;
@@ -123,8 +123,7 @@ CreaturePurchaseCard::CCreatureClickArea::CCreatureClickArea(const Point & posit
 	pos.h = CREATURE_HEIGHT;
 }
 
-void CreaturePurchaseCard::CCreatureClickArea::clickRight(tribool down, bool previousState)
+void CreaturePurchaseCard::CCreatureClickArea::showPopupWindow()
 {
-	if (down)
-		GH.windows().createAndPushWindow<CStackWindow>(creatureOnTheCard, true);
+	GH.windows().createAndPushWindow<CStackWindow>(creatureOnTheCard, true);
 }

+ 1 - 1
client/windows/CreaturePurchaseCard.h

@@ -49,7 +49,7 @@ private:
 	{
 	public:
 		CCreatureClickArea(const Point & pos, const std::shared_ptr<CCreaturePic> creaturePic, const CCreature * creatureOnTheCard);
-		void clickRight(tribool down, bool previousState) override;
+		void showPopupWindow() override;
 		const CCreature * creatureOnTheCard;
 
 		// These are obtained by guessing and checking. I'm not sure how the other numbers

+ 9 - 13
client/windows/GUIClasses.cpp

@@ -75,7 +75,7 @@
 #include "../lib/TextOperations.h"
 
 CRecruitmentWindow::CCreatureCard::CCreatureCard(CRecruitmentWindow * window, const CCreature * crea, int totalAmount)
-	: CIntObject(LCLICK | RCLICK),
+	: CIntObject(LCLICK | SHOW_POPUP),
 	parent(window),
 	selected(false),
 	creature(crea),
@@ -100,10 +100,9 @@ void CRecruitmentWindow::CCreatureCard::clickLeft(tribool down, bool previousSta
 		parent->select(this->shared_from_this());
 }
 
-void CRecruitmentWindow::CCreatureCard::clickRight(tribool down, bool previousState)
+void CRecruitmentWindow::CCreatureCard::showPopupWindow()
 {
-	if(down)
-		GH.windows().createAndPushWindow<CStackWindow>(creature, true);
+	GH.windows().createAndPushWindow<CStackWindow>(creature, true);
 }
 
 void CRecruitmentWindow::CCreatureCard::showAll(Canvas & to)
@@ -557,14 +556,14 @@ void CTavernWindow::HeroPortrait::clickLeft(tribool down, bool previousState)
 		*_sel = _id;
 }
 
-void CTavernWindow::HeroPortrait::clickRight(tribool down, bool previousState)
+void CTavernWindow::HeroPortrait::showPopupWindow()
 {
-	if(h && down)
+	if(h)
 		GH.windows().createAndPushWindow<CRClickPopupInt>(std::make_shared<CHeroWindow>(h));
 }
 
 CTavernWindow::HeroPortrait::HeroPortrait(int & sel, int id, int x, int y, const CGHeroInstance * H)
-	: CIntObject(LCLICK | RCLICK | HOVER),
+	: CIntObject(LCLICK | SHOW_POPUP | HOVER),
 	h(H), _sel(&sel), _id(id)
 {
 	OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
@@ -1237,7 +1236,7 @@ CTransformerWindow::CTransformerWindow(const IMarket * _market, const CGHeroInst
 }
 
 CUniversityWindow::CItem::CItem(CUniversityWindow * _parent, int _ID, int X, int Y)
-	: CIntObject(LCLICK | RCLICK | HOVER),
+	: CIntObject(LCLICK | SHOW_POPUP | HOVER),
 	ID(_ID),
 	parent(_parent)
 {
@@ -1266,12 +1265,9 @@ void CUniversityWindow::CItem::clickLeft(tribool down, bool previousState)
 	}
 }
 
-void CUniversityWindow::CItem::clickRight(tribool down, bool previousState)
+void CUniversityWindow::CItem::showPopupWindow()
 {
-	if(down)
-	{
-		CRClickPopup::createAndPush(CGI->skillh->getByIndex(ID)->getDescriptionTranslated(1), std::make_shared<CComponent>(CComponent::secskill, ID, 1));
-	}
+	CRClickPopup::createAndPush(CGI->skillh->getByIndex(ID)->getDescriptionTranslated(1), std::make_shared<CComponent>(CComponent::secskill, ID, 1));
 }
 
 void CUniversityWindow::CItem::hover(bool on)

+ 3 - 3
client/windows/GUIClasses.h

@@ -60,7 +60,7 @@ class CRecruitmentWindow : public CStatusbarWindow
 		CCreatureCard(CRecruitmentWindow * window, const CCreature * crea, int totalAmount);
 
 		void clickLeft(tribool down, bool previousState) override;
-		void clickRight(tribool down, bool previousState) override;
+		void showPopupWindow() override;
 		void showAll(Canvas & to) override;
 	};
 
@@ -206,7 +206,7 @@ public:
 		const CGHeroInstance * h;
 
 		void clickLeft(tribool down, bool previousState) override;
-		void clickRight(tribool down, bool previousState) override;
+		void showPopupWindow() override;
 		void hover (bool on) override;
 		HeroPortrait(int & sel, int id, int x, int y, const CGHeroInstance * H);
 
@@ -418,7 +418,7 @@ class CUniversityWindow : public CStatusbarWindow
 
 		void showAll(Canvas & to) override;
 		void clickLeft(tribool down, bool previousState) override;
-		void clickRight(tribool down, bool previousState) override;
+		void showPopupWindow() override;
 		void hover(bool on) override;
 		int state();//0=can't learn, 1=learned, 2=can learn
 		CItem(CUniversityWindow * _parent, int _ID, int X, int Y);

+ 4 - 17
client/windows/InfoWindows.cpp

@@ -288,12 +288,9 @@ void CInfoPopup::init(int x, int y)
 	vstd::amin(pos.y, GH.screenDimensions().y - bitmap->h);
 }
 
-
-void CRClickPopup::clickRight(tribool down, bool previousState)
+bool CRClickPopup::isPopupWindow() const
 {
-	if(down)
-		return;
-	close();
+	return true;
 }
 
 void CRClickPopup::close()
@@ -309,9 +306,8 @@ void CRClickPopup::createAndPush(const std::string &txt, const CInfoWindow::TCom
 
 	auto temp = std::make_shared<CInfoWindow>(txt, player, comps);
 	temp->center(GH.getCursorPosition()); //center on mouse
-#ifdef VCMI_IOS
-    // TODO: enable also for android?
-    temp->moveBy({0, -temp->pos.h / 2});
+#ifdef VCMI_MOBILE
+	temp->moveBy({0, -temp->pos.h / 2});
 #endif
 	temp->fitToScreen(10);
 
@@ -342,15 +338,6 @@ void CRClickPopup::createAndPush(const CGObjectInstance * obj, const Point & p,
 	}
 }
 
-CRClickPopup::CRClickPopup()
-{
-	addUsedEvents(RCLICK);
-}
-
-CRClickPopup::~CRClickPopup()
-{
-}
-
 CRClickPopupInt::CRClickPopupInt(std::shared_ptr<CIntObject> our)
 {
 	CCS->curh->hide();

+ 1 - 4
client/windows/InfoWindows.h

@@ -76,10 +76,7 @@ class CRClickPopup : public WindowBase
 {
 public:
 	virtual void close();
-	void clickRight(tribool down, bool previousState) override;
-
-	CRClickPopup();
-	virtual ~CRClickPopup();
+	bool isPopupWindow() const override;
 
 	static std::shared_ptr<WindowBase> createInfoWin(Point position, const CGObjectInstance * specific);
 	static void createAndPush(const std::string & txt, const CInfoWindow::TCompsInfo &comps = CInfoWindow::TCompsInfo());

+ 0 - 3
client/windows/settings/BattleOptionsTab.cpp

@@ -12,7 +12,6 @@
 
 #include "../../battle/BattleInterface.h"
 #include "../../gui/CGuiHandler.h"
-#include "../../eventsSDL/InputHandler.h"
 #include "../../../lib/CConfigHandler.h"
 #include "../../../lib/filesystem/ResourceID.h"
 #include "../../../lib/CGeneralTextHandler.h"
@@ -24,8 +23,6 @@ BattleOptionsTab::BattleOptionsTab(BattleInterface * owner)
 	OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
 	type |= REDRAW_PARENT;
 
-	//addConditional("touchscreen", GH.input().hasTouchInputDevice());
-
 	const JsonNode config(ResourceID("config/widgets/settings/battleOptionsTab.json"));
 	addCallback("viewGridChanged", [this, owner](bool value)
 	{

+ 81 - 10
client/windows/settings/GeneralOptionsTab.cpp

@@ -8,24 +8,25 @@
  *
  */
 #include "StdInc.h"
-
 #include "GeneralOptionsTab.h"
 
-#include "../../../lib/CGeneralTextHandler.h"
-#include "../../../lib/filesystem/ResourceID.h"
-#include "../../gui/CGuiHandler.h"
-#include "../../gui/WindowHandler.h"
-#include "../../widgets/Buttons.h"
-#include "../../widgets/Slider.h"
-#include "../../widgets/TextControls.h"
-#include "../../widgets/Images.h"
 #include "CGameInfo.h"
 #include "CMusicHandler.h"
 #include "CPlayerInterface.h"
-#include "windows/GUIClasses.h"
 #include "CServerHandler.h"
 #include "render/IScreenHandler.h"
+#include "windows/GUIClasses.h"
+
+#include "../../eventsSDL/InputHandler.h"
+#include "../../gui/CGuiHandler.h"
+#include "../../gui/WindowHandler.h"
+#include "../../widgets/Buttons.h"
+#include "../../widgets/Images.h"
+#include "../../widgets/Slider.h"
+#include "../../widgets/TextControls.h"
 
+#include "../../../lib/CGeneralTextHandler.h"
+#include "../../../lib/filesystem/ResourceID.h"
 
 static void setIntSetting(std::string group, std::string field, int value)
 {
@@ -52,6 +53,22 @@ static std::string scalingToLabelString( int scaling)
 	return string;
 }
 
+static std::string longTouchToEntryString( int duration)
+{
+	std::string string = CGI->generaltexth->translate("vcmi.systemOptions.longTouchMenu.entry");
+	boost::replace_all(string, "%d", std::to_string(duration));
+
+	return string;
+}
+
+static std::string longTouchToLabelString( int duration)
+{
+	std::string string = CGI->generaltexth->translate("vcmi.systemOptions.longTouchButton.hover");
+	boost::replace_all(string, "%d", std::to_string(duration));
+
+	return string;
+}
+
 static std::string resolutionToEntryString( int w, int h)
 {
 	std::string string = "%wx%h";
@@ -79,6 +96,7 @@ GeneralOptionsTab::GeneralOptionsTab()
 	OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
 	type |= REDRAW_PARENT;
 
+	addConditional("touchscreen", GH.input().hasTouchInputDevice());
 #ifdef VCMI_MOBILE
 	addConditional("mobile", true);
 	addConditional("desktop", false);
@@ -127,6 +145,10 @@ GeneralOptionsTab::GeneralOptionsTab()
 	{
 		selectGameScaling();
 	});
+	addCallback("setLongTouchDuration", [this](int dummyValue)
+	{
+		selectLongTouchDuration();
+	});
 	addCallback("framerateChanged", [](bool value)
 	{
 		setBoolSetting("video", "showfps", value);
@@ -150,6 +172,10 @@ GeneralOptionsTab::GeneralOptionsTab()
 	std::shared_ptr<CLabel> scalingLabel = widget<CLabel>("scalingLabel");
 	scalingLabel->setText(scalingToLabelString(currentResolution["scaling"].Integer()));
 
+	std::shared_ptr<CLabel> longTouchLabel = widget<CLabel>("longTouchLabel");
+	if (longTouchLabel)
+		longTouchLabel->setText(longTouchToLabelString(settings["general"]["longTouchTimeMilliseconds"].Integer()));
+
 	std::shared_ptr<CToggleButton> spellbookAnimationCheckbox = widget<CToggleButton>("spellbookAnimationCheckbox");
 	spellbookAnimationCheckbox->setSelected(settings["video"]["spellbookAnimation"].Bool());
 
@@ -324,3 +350,48 @@ void GeneralOptionsTab::setGameScaling(int index)
 
 	widget<CLabel>("scalingLabel")->setText(scalingToLabelString(scaling));
 }
+
+void GeneralOptionsTab::selectLongTouchDuration()
+{
+	longTouchDurations = { 500, 750, 1000, 1250, 1500, 1750, 2000 };
+
+	std::vector<std::string> items;
+	size_t currentIndex = 0;
+	size_t i = 0;
+	for(const auto & it : longTouchDurations)
+	{
+		auto resolutionStr = longTouchToEntryString(it);
+		if(widget<CLabel>("longTouchLabel")->getText() == longTouchToLabelString(it))
+			currentIndex = i;
+
+		items.push_back(std::move(resolutionStr));
+		++i;
+	}
+
+	GH.windows().createAndPushWindow<CObjectListWindow>(
+		items,
+		nullptr,
+		CGI->generaltexth->translate("vcmi.systemOptions.longTouchMenu.hover"),
+		CGI->generaltexth->translate("vcmi.systemOptions.longTouchMenu.help"),
+		[this](int index)
+		{
+			setLongTouchDuration(index);
+		},
+		currentIndex
+	);
+}
+
+void GeneralOptionsTab::setLongTouchDuration(int index)
+{
+	assert(index >= 0 && index < longTouchDurations.size());
+
+	if ( index < 0 || index >= longTouchDurations.size() )
+		return;
+
+	int scaling = longTouchDurations[index];
+
+	Settings longTouchTime = settings.write["general"]["longTouchTimeMilliseconds"];
+	longTouchTime->Float() = scaling;
+
+	widget<CLabel>("longTouchLabel")->setText(longTouchToLabelString(scaling));
+}

+ 4 - 0
client/windows/settings/GeneralOptionsTab.h

@@ -20,6 +20,7 @@ private:
 
 	std::vector<Point> supportedResolutions;
 	std::vector<int> supportedScaling;
+	std::vector<int> longTouchDurations;
 
 	void setFullscreenMode( bool on, bool exclusive);
 
@@ -29,6 +30,9 @@ private:
 	void selectGameScaling();
 	void setGameScaling(int index);
 
+	void selectLongTouchDuration();
+	void setLongTouchDuration(int index);
+
 public:
 	GeneralOptionsTab();
 

+ 11 - 8
config/schemas/settings.json

@@ -21,15 +21,19 @@
 				"playerName",
 				"music",
 				"sound",
+				"saveRandomMaps",
+				"lastMap",
 				"language",
 				"gameDataLanguage",
-				"saveRandomMaps",
+				"lastSave",
+				"lastSettingsTab"
+				"lastCampaign",
 				"saveFrequency",
 				"notifications",
 				"extraDump",
 				"userRelativePointer",
 				"relativePointerSpeedMultiplier",
-				"lastSettingsTab"
+				"longTouchTimeMilliseconds"
 			],
 			"properties" : {
 				"playerName" : {
@@ -93,6 +97,10 @@
 				"relativePointerSpeedMultiplier" : {
 					"type" : "number",
 					"default" : 1
+				},
+				"longTouchTimeMilliseconds" : {
+					"type" : "number",
+					"default" : 1000
 				}
 			}
 		},
@@ -102,13 +110,12 @@
 			"default" : {},
 			"required" : [ 
 				"resolution", 
-				"bitsPerPixel", 
 				"fullscreen", 
 				"realFullscreen", 
 				"cursor", 
+				"showIntro", 
 				"spellbookAnimation", 
 				"driver", 
-				"showIntro", 
 				"displayIndex",
 				"showfps",
 				"targetfps"
@@ -125,10 +132,6 @@
 					},
 					"default" : {"width" : 800, "height" : 600, "scaling" : 100 }
 				},
-				"bitsPerPixel" : {
-					"type" : "number",
-					"default" : 32
-				},
 				"fullscreen" : {
 					"type" : "boolean",
 					"default" : false

+ 12 - 0
config/widgets/settings/generalOptionsTab.json

@@ -52,6 +52,11 @@
 				},
 				{
 					"text": "core.genrltxt.577"
+				},
+				{
+					"name": "longTouchLabel",
+					"text": "vcmi.systemOptions.longTouchButton.hover",
+					"created" : "touchscreen"
 				}
 			]
 		},
@@ -95,6 +100,13 @@
 					"help": "core.help.364",
 					"callback": "spellbookAnimationChanged"
 				},
+				{
+					"name": "longTouchButton",
+					"type": "buttonGear",
+					"help": "vcmi.systemOptions.longTouchButton",
+					"callback": "setLongTouchDuration",
+					"created" : "touchscreen"
+				}
 			]
 		},
 /////////////////////////////////////// Right section - Audio Settings