浏览代码

Merge pull request #310 from Fayth/feature/swipe-support

Adventure map swipe support for non-android platforms
ArseniyShestakov 8 年之前
父节点
当前提交
64531e060d

+ 0 - 2
client/VCMI_client.vcxproj

@@ -292,6 +292,4 @@
     </ProjectReference>
   </ItemGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
-  <ImportGroup Label="ExtensionTargets">
-  </ImportGroup>
 </Project>

+ 2 - 8
client/VCMI_client.vcxproj.filters

@@ -109,17 +109,10 @@
       <Filter>gui</Filter>
     </ClCompile>
     <ClCompile Include="..\CCallback.cpp" />
-  </ItemGroup>
-  <ItemGroup>
-    <ClCompile Include="CQuestLog.cpp">
-      <Filter>Source Files</Filter>
-    </ClCompile>
+    <ClCompile Include="SDLRWwrapper.cpp" />
   </ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="VCMI_client.rc" />
-    <ClInclude Include="CQuestLog.h">
-      <Filter>Header Files</Filter>
-    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <None Include="vcmi.ico" />
@@ -254,5 +247,6 @@
       <Filter>gui</Filter>
     </ClInclude>
     <ClInclude Include="..\CCallback.h" />
+    <ClInclude Include="SDLRWwrapper.h" />
   </ItemGroup>
 </Project>

+ 48 - 58
client/gui/CGuiHandler.cpp

@@ -60,6 +60,7 @@ void CGuiHandler::processLists(const ui16 activityFlag, std::function<void (std:
 {
 	processList(CIntObject::LCLICK,activityFlag,&lclickable,cb);
 	processList(CIntObject::RCLICK,activityFlag,&rclickable,cb);
+	processList(CIntObject::MCLICK,activityFlag,&mclickable,cb);
 	processList(CIntObject::HOVER,activityFlag,&hoverable,cb);
 	processList(CIntObject::MOVE,activityFlag,&motioninterested,cb);
 	processList(CIntObject::KEYBOARD,activityFlag,&keyinterested,cb);
@@ -260,18 +261,18 @@ void CGuiHandler::handleEvent(SDL_Event *sEvent)
 		CCS->curh->cursorMove(sEvent->motion.x, sEvent->motion.y);
 		handleMouseMotion(sEvent);
 	}
-	else if (sEvent->type==SDL_MOUSEBUTTONDOWN)
+	else if(sEvent->type == SDL_MOUSEBUTTONDOWN)
 	{
-		if(sEvent->button.button == SDL_BUTTON_LEFT)
+		switch(sEvent->button.button)
 		{
-
-			if(lastClick == sEvent->motion  &&  (SDL_GetTicks() - lastClickTime) < 300)
+		case SDL_BUTTON_LEFT:
+			if(lastClick == sEvent->motion && (SDL_GetTicks() - lastClickTime) < 300)
 			{
 				std::list<CIntObject*> hlp = doubleClickInterested;
-				for(auto i=hlp.begin(); i != hlp.end() && current; i++)
+				for(auto i = hlp.begin(); i != hlp.end() && current; i++)
 				{
-					if(!vstd::contains(doubleClickInterested,*i)) continue;
-					if (isItIn(&(*i)->pos,sEvent->motion.x,sEvent->motion.y))
+					if(!vstd::contains(doubleClickInterested, *i)) continue;
+					if(isItIn(&(*i)->pos, sEvent->motion.x, sEvent->motion.y))
 					{
 						(*i)->onDoubleClick();
 					}
@@ -282,31 +283,16 @@ void CGuiHandler::handleEvent(SDL_Event *sEvent)
 			lastClick = sEvent->motion;
 			lastClickTime = SDL_GetTicks();
 
-			std::list<CIntObject*> hlp = lclickable;
-			for(auto i=hlp.begin(); i != hlp.end() && current; i++)
-			{
-				if(!vstd::contains(lclickable,*i)) continue;
-				if (isItIn(&(*i)->pos,sEvent->motion.x,sEvent->motion.y))
-				{
-					prev = (*i)->pressedL;
-					(*i)->pressedL = true;
-					(*i)->clickLeft(true, prev);
-				}
-			}
-		}
-		else if (sEvent->button.button == SDL_BUTTON_RIGHT)
-		{
-			std::list<CIntObject*> hlp = rclickable;
-			for(auto i=hlp.begin(); i != hlp.end() && current; i++)
-			{
-				if(!vstd::contains(rclickable,*i)) continue;
-				if (isItIn(&(*i)->pos,sEvent->motion.x,sEvent->motion.y))
-				{
-					prev = (*i)->pressedR;
-					(*i)->pressedR = true;
-					(*i)->clickRight(true, prev);
-				}
-			}
+			handleMouseButtonClick(lclickable, EIntObjMouseBtnType::LEFT, true);
+			break;
+		case SDL_BUTTON_RIGHT:
+			handleMouseButtonClick(rclickable, EIntObjMouseBtnType::RIGHT, true);
+			break;
+		case SDL_BUTTON_MIDDLE:
+			handleMouseButtonClick(mclickable, EIntObjMouseBtnType::MIDDLE, true);
+			break;
+		default:
+			break;
 		}
 	}
 	else if (sEvent->type == SDL_MOUSEWHEEL)
@@ -336,40 +322,44 @@ void CGuiHandler::handleEvent(SDL_Event *sEvent)
 		}
 	}
 	//todo: muiltitouch
-	else if ((sEvent->type==SDL_MOUSEBUTTONUP) && (sEvent->button.button == SDL_BUTTON_LEFT))
+	else if(sEvent->type == SDL_MOUSEBUTTONUP)
 	{
-		std::list<CIntObject*> hlp = lclickable;
-		for(auto i=hlp.begin(); i != hlp.end() && current; i++)
+		switch(sEvent->button.button)
 		{
-			if(!vstd::contains(lclickable,*i)) continue;
-			prev = (*i)->pressedL;
-			(*i)->pressedL = false;
-			if (isItIn(&(*i)->pos,sEvent->motion.x,sEvent->motion.y))
-			{
-				(*i)->clickLeft(false, prev);
-			}
-			else
-				(*i)->clickLeft(boost::logic::indeterminate, prev);
+		case SDL_BUTTON_LEFT:
+			handleMouseButtonClick(lclickable, EIntObjMouseBtnType::LEFT, false);
+			break;
+		case SDL_BUTTON_RIGHT:
+			handleMouseButtonClick(rclickable, EIntObjMouseBtnType::RIGHT, false);
+			break;
+		case SDL_BUTTON_MIDDLE:
+			handleMouseButtonClick(mclickable, EIntObjMouseBtnType::MIDDLE, false);
+			break;
 		}
 	}
-	else if ((sEvent->type==SDL_MOUSEBUTTONUP) && (sEvent->button.button == SDL_BUTTON_RIGHT))
+	current = nullptr;
+} //event end
+
+void CGuiHandler::handleMouseButtonClick(CIntObjectList & interestedObjs, EIntObjMouseBtnType btn, bool isPressed)
+{
+	auto hlp = interestedObjs;
+	for(auto i = hlp.begin(); i != hlp.end() && current; i++)
 	{
-		std::list<CIntObject*> hlp = rclickable;
-		for(auto i=hlp.begin(); i != hlp.end() && current; i++)
+		if(!vstd::contains(interestedObjs, *i)) continue;
+
+		auto prev = (*i)->mouseState(btn);
+		if(!isPressed)
+			(*i)->updateMouseState(btn, isPressed);
+		if(isItIn(&(*i)->pos, current->motion.x, current->motion.y))
 		{
-			if(!vstd::contains(rclickable,*i)) continue;
-			prev = (*i)->pressedR;
-			(*i)->pressedR = false;
-			if (isItIn(&(*i)->pos,sEvent->motion.x,sEvent->motion.y))
-			{
-				(*i)->clickRight(false, prev);
-			}
-			else
-				(*i)->clickRight(boost::logic::indeterminate, prev);
+			if(isPressed)
+				(*i)->updateMouseState(btn, isPressed);
+			(*i)->click(btn, isPressed, prev);
 		}
+		else if(!isPressed)
+			(*i)->click(btn, boost::logic::indeterminate, prev);
 	}
-	current = nullptr;
-} //event end
+}
 
 void CGuiHandler::handleMouseMotion(SDL_Event *sEvent)
 {

+ 3 - 0
client/gui/CGuiHandler.h

@@ -10,6 +10,7 @@ class CIntObject;
 class IUpdateable;
 class IShowActivatable;
 class IShowable;
+enum class EIntObjMouseBtnType;
 template <typename T> struct CondSh;
 
 /*
@@ -53,6 +54,7 @@ private:
 	//active GUI elements (listening for events
 	CIntObjectList lclickable,
 				   rclickable,
+				   mclickable,
 				   hoverable,
 				   keyinterested,
 				   motioninterested,
@@ -62,6 +64,7 @@ private:
 	               textInterested;
 
 
+	void handleMouseButtonClick(CIntObjectList & interestedObjs, EIntObjMouseBtnType btn, bool isPressed);
 	void processLists(const ui16 activityFlag, std::function<void (std::list<CIntObject*> *)> cb);
 public:
 	void handleElementActivate(CIntObject * elem, ui16 activityFlag);

+ 22 - 12
client/gui/CIntObject.cpp

@@ -16,7 +16,7 @@ CIntObject::CIntObject(int used_, Point pos_):
 	parent(parent_m),
 	active(active_m)
 {
-	pressedL = pressedR = hovered = captureAllKeys = strongInterest = false;
+	hovered = captureAllKeys = strongInterest = false;
 	toNextTick = timerDelay = 0;
 	used = used_;
 
@@ -134,6 +134,23 @@ CIntObject::~CIntObject()
 		parent_m->removeChild(this);
 }
 
+void CIntObject::click(EIntObjMouseBtnType btn, tribool down, bool previousState)
+{
+	switch(btn)
+	{
+	default:
+	case EIntObjMouseBtnType::LEFT:
+		clickLeft(down, previousState);
+		break;
+	case EIntObjMouseBtnType::MIDDLE:
+		clickMiddle(down, previousState);
+		break;
+	case EIntObjMouseBtnType::RIGHT:
+		clickRight(down, previousState);
+		break;
+	}
+}
+
 void CIntObject::printAtLoc( const std::string & text, int x, int y, EFonts font, SDL_Color kolor/*=Colors::WHITE*/, SDL_Surface * dst/*=screen*/ )
 {
 	graphics->fonts[font]->renderTextLeft(dst, text, kolor, Point(pos.x + x, pos.y + y));
@@ -340,16 +357,9 @@ void CKeyShortcut::keyPressed(const SDL_KeyboardEvent & key)
 	if(vstd::contains(assignedKeys,key.keysym.sym)
 	 || vstd::contains(assignedKeys, CGuiHandler::numToDigit(key.keysym.sym)))
 	{
-		bool prev = pressedL;
-		if(key.state == SDL_PRESSED)
-		{
-			pressedL = true;
-			clickLeft(true, prev);
-		}
-		else
-		{
-			pressedL = false;
-			clickLeft(false, prev);
-		}
+		bool prev = mouseState(EIntObjMouseBtnType::LEFT);		
+		updateMouseState(EIntObjMouseBtnType::LEFT, key.state == SDL_PRESSED);
+		clickLeft(key.state == SDL_PRESSED, prev);
+		
 	}
 }

+ 10 - 7
client/gui/CIntObject.h

@@ -61,6 +61,7 @@ public:
 	virtual ~IShowActivatable(){}; //d-tor
 };
 
+enum class EIntObjMouseBtnType { LEFT, MIDDLE, RIGHT };
 //typedef ui16 ActivityFlag;
 
 // Base UI element
@@ -73,6 +74,8 @@ class CIntObject : public IShowActivatable //interface object
 	int toNextTick;
 	int timerDelay;
 
+	std::map<EIntObjMouseBtnType, bool> currentMouseState;
+
 	void onTimer(int timePassed);
 
 	//non-const versions of fields to allow changing them in CIntObject
@@ -104,13 +107,13 @@ public:
 	CIntObject(int used=0, Point offset=Point());
 	virtual ~CIntObject(); //d-tor
 
-	//l-clicks handling
-	/*const*/ bool pressedL; //for determining if object is L-pressed
-	virtual void clickLeft(tribool down, bool previousState){}
+	void updateMouseState(EIntObjMouseBtnType btn, bool state) { currentMouseState[btn] = state; }
+	bool mouseState(EIntObjMouseBtnType btn) const { return currentMouseState.count(btn) ? currentMouseState.at(btn) : false; }
 
-	//r-clicks handling
-	/*const*/ bool pressedR; //for determining if object is R-pressed
-	virtual void clickRight(tribool down, bool previousState){}
+	virtual void click(EIntObjMouseBtnType btn, tribool down, bool previousState);
+	virtual void clickLeft(tribool down, bool previousState) {}
+	virtual void clickRight(tribool down, bool previousState) {}
+	virtual void clickMiddle(tribool down, bool previousState) {}
 
 	//hover handling
 	/*const*/ bool hovered;  //for determining if object is hovered
@@ -138,7 +141,7 @@ public:
 	//double click
 	virtual void onDoubleClick(){}
 
-	enum {LCLICK=1, RCLICK=2, HOVER=4, MOVE=8, KEYBOARD=16, TIME=32, GENERAL=64, WHEEL=128, DOUBLECLICK=256, TEXTINPUT=512, ALL=0xffff};
+	enum {LCLICK=1, RCLICK=2, HOVER=4, MOVE=8, KEYBOARD=16, TIME=32, GENERAL=64, WHEEL=128, DOUBLECLICK=256, TEXTINPUT=512, MCLICK=1024, ALL=0xffff};
 	const ui16 & active;
 	void addUsedEvents(ui16 newActions);
 	void removeUsedEvents(ui16 newActions);

+ 1 - 1
client/widgets/AdventureMapClasses.cpp

@@ -587,7 +587,7 @@ void CMinimap::hover(bool on)
 
 void CMinimap::mouseMoved(const SDL_MouseMotionEvent & sEvent)
 {
-	if (pressedL)
+	if(mouseState(EIntObjMouseBtnType::LEFT))
 		moveAdvMapSelection();
 }
 

+ 1 - 1
client/widgets/Buttons.cpp

@@ -623,7 +623,7 @@ void CSlider::clickLeft(tribool down, bool previousState)
 			return;
 		// 		if (rw>1) return;
 		// 		if (rw<0) return;
-		slider->clickLeft(true, slider->pressedL);
+		slider->clickLeft(true, slider->mouseState(EIntObjMouseBtnType::LEFT));
 		moveTo(rw * positions  +  0.5);
 		return;
 	}

+ 39 - 29
client/windows/CAdvmapInterface.cpp

@@ -98,7 +98,7 @@ CTerrainRect::CTerrainRect()
 	pos.w=ADVOPT.advmapW;
 	pos.h=ADVOPT.advmapH;
 	moveX = moveY = 0;
-	addUsedEvents(LCLICK | RCLICK | HOVER | MOVE);
+	addUsedEvents(LCLICK | RCLICK | MCLICK | HOVER | MOVE);
 }
 
 CTerrainRect::~CTerrainRect()
@@ -124,17 +124,10 @@ void CTerrainRect::clickLeft(tribool down, bool previousState)
 #ifdef VCMI_ANDROID
 	if(adventureInt->swipeEnabled)
 	{
-		if(down == true)
+		if(handleSwipeStateChange(down == true))
 		{
-			swipeInitialRealPos = int3(GH.current->motion.x, GH.current->motion.y, 0);
-			swipeInitialMapPos = int3(adventureInt->position);
 			return; // if swipe is enabled, we don't process "down" events and wait for "up" (to make sure this wasn't a swiping gesture)
 		}
-		else if(isSwiping) // only accept this touch if it wasn't a swipe
-		{
-			isSwiping = false;
-			return;
-		}
 	}
 	else
 	{
@@ -165,22 +158,32 @@ void CTerrainRect::clickRight(tribool down, bool previousState)
 		adventureInt->tileRClicked(mp);
 }
 
+void CTerrainRect::clickMiddle(tribool down, bool previousState)
+{
+	handleSwipeStateChange(down == true);
+}
+
 void CTerrainRect::mouseMoved(const SDL_MouseMotionEvent & sEvent)
 {
 	handleHover(sEvent);
 
-#ifdef VCMI_ANDROID
-	if(!adventureInt->swipeEnabled || sEvent.state == 0)
+	if(!adventureInt->swipeEnabled)
 		return;
 
 	handleSwipeMove(sEvent);
-#endif // !VCMI_ANDROID
 }
 
-#ifdef VCMI_ANDROID
-
 void CTerrainRect::handleSwipeMove(const SDL_MouseMotionEvent & sEvent)
 {
+#ifdef VCMI_ANDROID
+	if(sEvent.state == 0) // any "button" is enough on android
+#else //!VCMI_ANDROID
+	if((sEvent.state & SDL_BUTTON_MMASK) == 0) // swipe only works with middle mouse on other platforms
+#endif //!VCMI_ANDROID
+	{
+		return;
+	}
+
 	if(!isSwiping)
 	{
 		// try to distinguish if this touch was meant to be a swipe or just fat-fingering press
@@ -201,7 +204,21 @@ void CTerrainRect::handleSwipeMove(const SDL_MouseMotionEvent & sEvent)
 	}
 }
 
-#endif // VCMI_ANDROID
+bool CTerrainRect::handleSwipeStateChange(bool btnPressed)
+{
+	if(btnPressed)
+	{
+		swipeInitialRealPos = int3(GH.current->motion.x, GH.current->motion.y, 0);
+		swipeInitialMapPos = int3(adventureInt->position);
+		return true;
+	}
+	else if(isSwiping) // only accept this touch if it wasn't a swipe
+	{
+		isSwiping = false;
+		return true;
+	}
+	return false;
+}
 
 void CTerrainRect::handleHover(const SDL_MouseMotionEvent &sEvent)
 {
@@ -534,11 +551,9 @@ CAdvMapInt::CAdvMapInt():
 	infoBar(Rect(ADVOPT.infoboxX, ADVOPT.infoboxY, 192, 192)), state(NA),
   spellBeingCasted(nullptr), position(int3(0, 0, 0)), selection(nullptr),
   updateScreen(false), anim(0), animValHitCount(0), heroAnim(0), heroAnimValHitCount(0),
-	activeMapPanel(nullptr), duringAITurn(false), scrollingDir(0), scrollingState(false)
-#ifdef VCMI_ANDROID
-	, swipeEnabled(settings["general"]["swipe"].Bool()), swipeMovementRequested(false),
+	activeMapPanel(nullptr), duringAITurn(false), scrollingDir(0), scrollingState(false), 
+	swipeEnabled(settings["general"]["swipe"].Bool()), swipeMovementRequested(false),
 	swipeTargetPosition(int3(-1, -1, -1))
-#endif
 {
   adventureInt = this;
 	pos.x = pos.y = 0;
@@ -1007,18 +1022,16 @@ void CAdvMapInt::show(SDL_Surface * to)
 	}
 	++heroAnim;
 
-#ifdef VCMI_ANDROID
 	if(swipeEnabled)
 	{
 		handleSwipeUpdate();
 	}
+#ifdef VCMI_ANDROID // on android, map-moving mode is exclusive (TODO technically it might work with both enabled; to be checked)
 	else
+#endif // VCMI_ANDROID
 	{
-#endif // !VCMI_ANDROID
 		handleMapScrollingUpdate();
-#ifdef VCMI_ANDROID
 	}
-#endif
 
 	for(int i = 0; i < 4; i++)
 	{
@@ -1089,14 +1102,13 @@ void CAdvMapInt::handleMapScrollingUpdate()
 	}
 }
 
-#ifdef VCMI_ANDROID
-
 void CAdvMapInt::handleSwipeUpdate()
 {
 	if(swipeMovementRequested)
 	{
-		position.x = swipeTargetPosition.x;
-		position.y = swipeTargetPosition.y;
+		auto fixedPos = LOCPLINT->repairScreenPos(swipeTargetPosition);
+		position.x = fixedPos.x;
+		position.y = fixedPos.y;
 		CCS->curh->changeGraphic(ECursor::DEFAULT, 0);
 		updateScreen = true;
 		minimap.redraw();
@@ -1104,8 +1116,6 @@ void CAdvMapInt::handleSwipeUpdate()
 	}
 }
 
-#endif
-
 void CAdvMapInt::selectionChanged()
 {
 	const CGTownInstance *to = LOCPLINT->towns[townList.getSelectedIndex()];

+ 5 - 8
client/windows/CAdvmapInterface.h

@@ -62,10 +62,10 @@ class CTerrainRect
 	bool isSwiping;
 	static constexpr float SwipeTouchSlop = 16.0f;
 
-	void handleHover(const SDL_MouseMotionEvent &sEvent);
-#ifdef VCMI_ANDROID
-	void handleSwipeMove(const SDL_MouseMotionEvent &sEvent);
-#endif // VCMI_ANDROID
+	void handleHover(const SDL_MouseMotionEvent & sEvent);
+	void handleSwipeMove(const SDL_MouseMotionEvent & sEvent);
+	/// handles start/finish of swipe (press/release of corresponding button); returns true if state change was handled
+	bool handleSwipeStateChange(bool btnPressed);
 public:
 	int tilesw, tilesh; //width and height of terrain to blit in tiles
 	int3 curHoveredTile;
@@ -77,6 +77,7 @@ public:
 	void deactivate() override;
 	void clickLeft(tribool down, bool previousState) override;
 	void clickRight(tribool down, bool previousState) override;
+	void clickMiddle(tribool down, bool previousState) override;
 	void hover(bool on) override;
 	void mouseMoved (const SDL_MouseMotionEvent & sEvent) override;
 	void show(SDL_Surface * to) override;
@@ -132,11 +133,9 @@ public:
 	enum{LEFT=1, RIGHT=2, UP=4, DOWN=8};
 	ui8 scrollingDir; //uses enum: LEFT RIGHT, UP, DOWN
 	bool scrollingState;
-#ifdef VCMI_ANDROID
 	bool swipeEnabled;
 	bool swipeMovementRequested;
 	int3 swipeTargetPosition;
-#endif // !VCMI_ANDROID
 
 	enum{NA, INGAME, WAITING} state;
 
@@ -260,9 +259,7 @@ public:
 	void changeMode(EAdvMapMode newMode, float newScale = 0.36f);
 
 	void handleMapScrollingUpdate();
-#ifdef VCMI_ANDROID
 	void handleSwipeUpdate();
-#endif
 
 };
 

+ 1 - 1
config/schemas/settings.json

@@ -41,7 +41,7 @@
 				},
 				"swipe" : {
 					"type" : "boolean",
-					"default" : false
+					"default" : true
 				},
 				"saveRandomMaps" : {
 					"type" : "boolean",