diff --git a/client/VCMI_client.vcxproj b/client/VCMI_client.vcxproj index ec30a83ab..9d0476656 100644 --- a/client/VCMI_client.vcxproj +++ b/client/VCMI_client.vcxproj @@ -292,6 +292,4 @@ - - \ No newline at end of file diff --git a/client/VCMI_client.vcxproj.filters b/client/VCMI_client.vcxproj.filters index d389ba18b..b044f1d67 100644 --- a/client/VCMI_client.vcxproj.filters +++ b/client/VCMI_client.vcxproj.filters @@ -109,17 +109,10 @@ gui - - - - Source Files - + - - Header Files - @@ -254,5 +247,6 @@ gui + \ No newline at end of file diff --git a/client/gui/CGuiHandler.cpp b/client/gui/CGuiHandler.cpp index c4eb536ad..57345a1e4 100644 --- a/client/gui/CGuiHandler.cpp +++ b/client/gui/CGuiHandler.cpp @@ -60,6 +60,7 @@ void CGuiHandler::processLists(const ui16 activityFlag, std::functioncurh->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 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 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 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,41 +322,45 @@ 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 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); - } - } - else if ((sEvent->type==SDL_MOUSEBUTTONUP) && (sEvent->button.button == SDL_BUTTON_RIGHT)) - { - std::list hlp = rclickable; - for(auto i=hlp.begin(); i != hlp.end() && current; i++) - { - 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); + 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; } } 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++) + { + 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(isPressed) + (*i)->updateMouseState(btn, isPressed); + (*i)->click(btn, isPressed, prev); + } + else if(!isPressed) + (*i)->click(btn, boost::logic::indeterminate, prev); + } +} + void CGuiHandler::handleMouseMotion(SDL_Event *sEvent) { //sending active, hovered hoverable objects hover() call diff --git a/client/gui/CGuiHandler.h b/client/gui/CGuiHandler.h index 3449dd76c..9a10d24eb 100644 --- a/client/gui/CGuiHandler.h +++ b/client/gui/CGuiHandler.h @@ -10,6 +10,7 @@ class CIntObject; class IUpdateable; class IShowActivatable; class IShowable; +enum class EIntObjMouseBtnType; template 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 *)> cb); public: void handleElementActivate(CIntObject * elem, ui16 activityFlag); diff --git a/client/gui/CIntObject.cpp b/client/gui/CIntObject.cpp index 8fa3b3025..40e7c281f 100644 --- a/client/gui/CIntObject.cpp +++ b/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); + } } diff --git a/client/gui/CIntObject.h b/client/gui/CIntObject.h index 75d8c2507..31f479738 100644 --- a/client/gui/CIntObject.h +++ b/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 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); diff --git a/client/widgets/AdventureMapClasses.cpp b/client/widgets/AdventureMapClasses.cpp index 1cea6ad3c..12019ad92 100644 --- a/client/widgets/AdventureMapClasses.cpp +++ b/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(); } diff --git a/client/widgets/Buttons.cpp b/client/widgets/Buttons.cpp index 8784e0d1a..7abc4a5c5 100644 --- a/client/widgets/Buttons.cpp +++ b/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; } diff --git a/client/windows/CAdvmapInterface.cpp b/client/windows/CAdvmapInterface.cpp index e82e34a9e..131e510e2 100644 --- a/client/windows/CAdvmapInterface.cpp +++ b/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()]; diff --git a/client/windows/CAdvmapInterface.h b/client/windows/CAdvmapInterface.h index 8ae36b743..fca0b3da1 100644 --- a/client/windows/CAdvmapInterface.h +++ b/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 }; diff --git a/config/schemas/settings.json b/config/schemas/settings.json index 22e0f75dc..995d67eca 100644 --- a/config/schemas/settings.json +++ b/config/schemas/settings.json @@ -41,7 +41,7 @@ }, "swipe" : { "type" : "boolean", - "default" : false + "default" : true }, "saveRandomMaps" : { "type" : "boolean",