From 48859e186e1a09970cfb8773925e55a3d5cb0898 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Fri, 26 May 2023 21:46:09 +0300 Subject: [PATCH] Added panning gesture, activated by mouse wheel press --- client/eventsSDL/InputHandler.cpp | 4 ++ client/eventsSDL/InputSourceMouse.cpp | 10 ++-- client/eventsSDL/InputSourceTouch.cpp | 11 +--- client/gui/EventDispatcher.cpp | 27 +++++++-- client/gui/EventDispatcher.h | 6 +- client/gui/EventsReceiver.cpp | 17 ------ client/gui/EventsReceiver.h | 18 +++--- client/lobby/SelectionTab.cpp | 2 +- client/lobby/SelectionTab.h | 2 +- client/mapView/MapView.cpp | 2 +- client/mapView/MapViewActions.cpp | 79 ++------------------------- client/mapView/MapViewActions.h | 10 +--- client/windows/GUIClasses.cpp | 2 +- client/windows/GUIClasses.h | 2 +- 14 files changed, 55 insertions(+), 137 deletions(-) diff --git a/client/eventsSDL/InputHandler.cpp b/client/eventsSDL/InputHandler.cpp index ef6323d85..15f243365 100644 --- a/client/eventsSDL/InputHandler.cpp +++ b/client/eventsSDL/InputHandler.cpp @@ -168,7 +168,11 @@ void InputHandler::preprocessEvent(const SDL_Event & ev) // This prevents freezes when every motion event takes longer to handle than interval at which // the events arrive (like dragging on the minimap in world view, with redraw at every event) // so that the events would start piling up faster than they can be processed. + int xAccumulated = eventsQueue.back().motion.xrel + ev.motion.xrel; + int yAccumulated = eventsQueue.back().motion.yrel + ev.motion.yrel; eventsQueue.back() = ev; + eventsQueue.back().motion.xrel = xAccumulated; + eventsQueue.back().motion.yrel = yAccumulated; return; } eventsQueue.push_back(ev); diff --git a/client/eventsSDL/InputSourceMouse.cpp b/client/eventsSDL/InputSourceMouse.cpp index 8599e8a46..fd82b3a2a 100644 --- a/client/eventsSDL/InputSourceMouse.cpp +++ b/client/eventsSDL/InputSourceMouse.cpp @@ -22,9 +22,13 @@ void InputSourceMouse::handleEventMouseMotion(const SDL_MouseMotionEvent & motion) { Point newPosition(motion.x, motion.y); + Point distance(-motion.xrel, -motion.yrel); GH.input().setCursorPosition(newPosition); + if (mouseButtonsMask & SDL_BUTTON(SDL_BUTTON_MIDDLE)) + GH.events().dispatchGesturePanning(distance); + mouseButtonsMask = motion.state; } @@ -44,9 +48,6 @@ void InputSourceMouse::handleEventMouseButtonDown(const SDL_MouseButtonEvent & b case SDL_BUTTON_RIGHT: GH.events().dispatchMouseButtonPressed(MouseButton::RIGHT, position); break; - case SDL_BUTTON_MIDDLE: - GH.events().dispatchMouseButtonPressed(MouseButton::MIDDLE, position); - break; } } @@ -71,9 +72,6 @@ void InputSourceMouse::handleEventMouseButtonUp(const SDL_MouseButtonEvent & but case SDL_BUTTON_RIGHT: GH.events().dispatchMouseButtonReleased(MouseButton::RIGHT, position); break; - case SDL_BUTTON_MIDDLE: - GH.events().dispatchMouseButtonReleased(MouseButton::MIDDLE, position); - break; } } diff --git a/client/eventsSDL/InputSourceTouch.cpp b/client/eventsSDL/InputSourceTouch.cpp index f293af00c..eeb9338f8 100644 --- a/client/eventsSDL/InputSourceTouch.cpp +++ b/client/eventsSDL/InputSourceTouch.cpp @@ -104,11 +104,6 @@ void InputSourceTouch::handleEventFingerDown(const SDL_TouchFingerEvent & tfinge break; } case TouchState::TAP_DOWN_SHORT: - { - GH.input().setCursorPosition(convertTouchToMouse(tfinger)); - state = TouchState::TAP_DOWN_DOUBLE; - break; - } case TouchState::TAP_DOWN_PANNING: { GH.input().setCursorPosition(convertTouchToMouse(tfinger)); @@ -116,10 +111,6 @@ void InputSourceTouch::handleEventFingerDown(const SDL_TouchFingerEvent & tfinge break; } case TouchState::TAP_DOWN_DOUBLE: - { - // TODO? ignore? - break; - } case TouchState::TAP_DOWN_LONG: { // no-op @@ -163,6 +154,8 @@ void InputSourceTouch::handleEventFingerUp(const SDL_TouchFingerEvent & tfinger) { if (SDL_GetNumTouchFingers(tfinger.touchId) == 1) state = TouchState::TAP_DOWN_PANNING; + if (SDL_GetNumTouchFingers(tfinger.touchId) == 0) + state = TouchState::IDLE; break; } case TouchState::TAP_DOWN_LONG: diff --git a/client/gui/EventDispatcher.cpp b/client/gui/EventDispatcher.cpp index 5b3867e14..e48927627 100644 --- a/client/gui/EventDispatcher.cpp +++ b/client/gui/EventDispatcher.cpp @@ -28,7 +28,6 @@ void EventDispatcher::processLists(ui16 activityFlag, const Functor & cb) processList(AEventsReceiver::LCLICK, lclickable); processList(AEventsReceiver::RCLICK, rclickable); - processList(AEventsReceiver::MCLICK, mclickable); processList(AEventsReceiver::HOVER, hoverable); processList(AEventsReceiver::MOVE, motioninterested); processList(AEventsReceiver::KEYBOARD, keyinterested); @@ -36,6 +35,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); } void EventDispatcher::activateElement(AEventsReceiver * elem, ui16 activityFlag) @@ -120,8 +120,6 @@ EventDispatcher::EventReceiversList & EventDispatcher::getListForMouseButton(Mou return lclickable; case MouseButton::RIGHT: return rclickable; - case MouseButton::MIDDLE: - return mclickable; } throw std::runtime_error("Invalid mouse button in getListForMouseButton"); } @@ -138,7 +136,7 @@ void EventDispatcher::dispatchMouseDoubleClick(const Point & position) if(i->isInside(position)) { - i->onDoubleClick(); + i->clickDouble(); doubleClicked = true; } } @@ -172,10 +170,19 @@ void EventDispatcher::handleMouseButtonClick(EventReceiversList & interestedObjs { if(isPressed) i->currentMouseState[btn] = isPressed; - i->click(btn, isPressed, prev); + + if (btn == MouseButton::LEFT) + i->clickLeft(isPressed, prev); + if (btn == MouseButton::RIGHT) + i->clickRight(isPressed, prev); } else if(!isPressed) - i->click(btn, boost::logic::indeterminate, prev); + { + if (btn == MouseButton::LEFT) + i->clickLeft(boost::logic::indeterminate, prev); + if (btn == MouseButton::RIGHT) + i->clickRight(boost::logic::indeterminate, prev); + } } } @@ -206,6 +213,14 @@ void EventDispatcher::dispatchTextEditing(const std::string & text) } } +void EventDispatcher::dispatchGesturePanning(const Point & distance) +{ + for(auto it : panningInterested) + { + it->gesturePanning(distance); + } +} + void EventDispatcher::dispatchMouseMoved(const Point & position) { EventReceiversList newlyHovered; diff --git a/client/gui/EventDispatcher.h b/client/gui/EventDispatcher.h index 3f5712000..328022e03 100644 --- a/client/gui/EventDispatcher.h +++ b/client/gui/EventDispatcher.h @@ -25,7 +25,6 @@ class EventDispatcher /// list of UI elements that are interested in particular event EventReceiversList lclickable; EventReceiversList rclickable; - EventReceiversList mclickable; EventReceiversList hoverable; EventReceiversList keyinterested; EventReceiversList motioninterested; @@ -33,6 +32,7 @@ class EventDispatcher EventReceiversList wheelInterested; EventReceiversList doubleClickInterested; EventReceiversList textInterested; + EventReceiversList panningInterested; EventReceiversList & getListForMouseButton(MouseButton button); @@ -60,7 +60,9 @@ public: void dispatchMouseButtonReleased(const MouseButton & button, const Point & position); void dispatchMouseScrolled(const Point & distance, const Point & position); void dispatchMouseDoubleClick(const Point & position); - void dispatchMouseMoved(const Point & position); + void dispatchMouseMoved(const Point & distance); + + void dispatchGesturePanning(const Point & position); /// Text input events void dispatchTextInput(const std::string & text); diff --git a/client/gui/EventsReceiver.cpp b/client/gui/EventsReceiver.cpp index 69db18857..e096d87ee 100644 --- a/client/gui/EventsReceiver.cpp +++ b/client/gui/EventsReceiver.cpp @@ -55,20 +55,3 @@ void AEventsReceiver::deactivateEvents(ui16 what) activeState &= ~GENERAL; GH.events().deactivateElement(this, what & activeState); } - -void AEventsReceiver::click(MouseButton btn, tribool down, bool previousState) -{ - switch(btn) - { - default: - case MouseButton::LEFT: - clickLeft(down, previousState); - break; - case MouseButton::MIDDLE: - clickMiddle(down, previousState); - break; - case MouseButton::RIGHT: - clickRight(down, previousState); - break; - } -} diff --git a/client/gui/EventsReceiver.h b/client/gui/EventsReceiver.h index 50672dae6..495f8f6df 100644 --- a/client/gui/EventsReceiver.h +++ b/client/gui/EventsReceiver.h @@ -29,7 +29,6 @@ class AEventsReceiver bool strongInterestState; std::map currentMouseState; - void click(MouseButton btn, tribool down, bool previousState); protected: /// If set, UI element will receive all mouse movement events, even those outside this element @@ -42,20 +41,21 @@ protected: virtual void clickLeft(tribool down, bool previousState) {} virtual void clickRight(tribool down, bool previousState) {} - virtual void clickMiddle(tribool down, bool previousState) {} + virtual void clickDouble() {} + + virtual void gesturePanning(const Point & distanceDelta) {} + virtual void wheelScrolled(bool down, bool in) {} + virtual void mouseMoved(const Point & cursorPosition) {} + virtual void hover(bool on) {} virtual void textInputed(const std::string & enteredText) {} virtual void textEdited(const std::string & enteredText) {} - virtual void tick(uint32_t msPassed) {} - virtual void wheelScrolled(bool down, bool in) {} - virtual void mouseMoved(const Point & cursorPosition) {} - virtual void hover(bool on) {} - virtual void onDoubleClick() {} - virtual void keyPressed(EShortcut key) {} virtual void keyReleased(EShortcut key) {} + virtual void tick(uint32_t msPassed) {} + virtual bool captureThisKey(EShortcut key) = 0; virtual bool isInside(const Point & position) = 0; @@ -64,7 +64,7 @@ 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, MCLICK=1024, ALL=0xffff}; + 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}; /// Returns true if element is currently hovered by mouse bool isHovered() const; diff --git a/client/lobby/SelectionTab.cpp b/client/lobby/SelectionTab.cpp index d92335791..c28ae2dd6 100644 --- a/client/lobby/SelectionTab.cpp +++ b/client/lobby/SelectionTab.cpp @@ -313,7 +313,7 @@ void SelectionTab::keyPressed(EShortcut key) select((int)selectionPos - slider->getValue() + moveBy); } -void SelectionTab::onDoubleClick() +void SelectionTab::clickDouble() { if(getLine() != -1) //double clicked scenarios list { diff --git a/client/lobby/SelectionTab.h b/client/lobby/SelectionTab.h index 06fab6f75..48e79f991 100644 --- a/client/lobby/SelectionTab.h +++ b/client/lobby/SelectionTab.h @@ -68,7 +68,7 @@ public: void clickLeft(tribool down, bool previousState) override; void keyPressed(EShortcut key) override; - void onDoubleClick() override; + void clickDouble() override; void filter(int size, bool selectFirst = false); //0 - all void sortBy(int criteria); diff --git a/client/mapView/MapView.cpp b/client/mapView/MapView.cpp index e175e999d..a83c0731c 100644 --- a/client/mapView/MapView.cpp +++ b/client/mapView/MapView.cpp @@ -117,7 +117,7 @@ void MapView::onMapScrolled(const Point & distance) void MapView::onMapSwiped(const Point & viewPosition) { isSwiping = true; - controller->setViewCenter(viewPosition, model->getLevel()); + controller->setViewCenter(model->getMapViewCenter() + viewPosition, model->getLevel()); } void MapView::onMapSwipeEnded() diff --git a/client/mapView/MapViewActions.cpp b/client/mapView/MapViewActions.cpp index 530b93ad5..bd694551c 100644 --- a/client/mapView/MapViewActions.cpp +++ b/client/mapView/MapViewActions.cpp @@ -25,21 +25,11 @@ MapViewActions::MapViewActions(MapView & owner, const std::shared_ptr & model) : model(model) , owner(owner) - , isSwiping(false) { pos.w = model->getPixelsVisibleDimensions().x; pos.h = model->getPixelsVisibleDimensions().y; - addUsedEvents(LCLICK | RCLICK | MCLICK | HOVER | MOVE | WHEEL); -} - -bool MapViewActions::swipeEnabled() const -{ -#if defined(VCMI_ANDROID) || defined(VCMI_IOS) - return settings["general"]["swipe"].Bool(); -#else - return settings["general"]["swipeDesktop"].Bool(); -#endif + addUsedEvents(LCLICK | RCLICK | GESTURE_PANNING | HOVER | MOVE | WHEEL); } void MapViewActions::setContext(const std::shared_ptr & context) @@ -52,18 +42,8 @@ void MapViewActions::clickLeft(tribool down, bool previousState) if(indeterminate(down)) return; - if(swipeEnabled()) - { - if(handleSwipeStateChange(static_cast(down))) - { - 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(down == false) - return; - } + if(down == false) + return; int3 tile = model->getTileAtPoint(GH.getCursorPosition() - pos.topLeft()); @@ -73,24 +53,15 @@ void MapViewActions::clickLeft(tribool down, bool previousState) void MapViewActions::clickRight(tribool down, bool previousState) { - if(isSwiping) - return; - int3 tile = model->getTileAtPoint(GH.getCursorPosition() - pos.topLeft()); if(down && context->isInMap(tile)) adventureInt->onTileRightClicked(tile); } -void MapViewActions::clickMiddle(tribool down, bool previousState) -{ - handleSwipeStateChange(static_cast(down)); -} - void MapViewActions::mouseMoved(const Point & cursorPosition) { handleHover(cursorPosition); - handleSwipeMove(cursorPosition); } void MapViewActions::wheelScrolled(bool down, bool in) @@ -100,49 +71,9 @@ void MapViewActions::wheelScrolled(bool down, bool in) adventureInt->hotkeyZoom(down ? -1 : +1); } -void MapViewActions::handleSwipeMove(const Point & cursorPosition) +void MapViewActions::gesturePanning(const Point & distance) { - // unless swipe is enabled, swipe move only works with middle mouse button - if(!swipeEnabled() && !GH.isMouseButtonPressed(MouseButton::MIDDLE)) - return; - - // on mobile platforms with enabled swipe we use left button - if(swipeEnabled() && !GH.isMouseButtonPressed(MouseButton::LEFT)) - return; - - if(!isSwiping) - { - static constexpr int touchSwipeSlop = 16; - Point distance = (cursorPosition - swipeInitialRealPos); - - // try to distinguish if this touch was meant to be a swipe or just fat-fingering press - if(std::abs(distance.x) + std::abs(distance.y) > touchSwipeSlop) - isSwiping = true; - } - - if(isSwiping) - { - Point swipeTargetPosition = swipeInitialViewPos + swipeInitialRealPos - cursorPosition; - owner.onMapSwiped(swipeTargetPosition); - } -} - -bool MapViewActions::handleSwipeStateChange(bool btnPressed) -{ - if(btnPressed) - { - swipeInitialRealPos = GH.getCursorPosition(); - swipeInitialViewPos = model->getMapViewCenter(); - return true; - } - - if(isSwiping) // only accept this touch if it wasn't a swipe - { - owner.onMapSwipeEnded(); - isSwiping = false; - return true; - } - return false; + owner.onMapSwiped(distance); } void MapViewActions::handleHover(const Point & cursorPosition) diff --git a/client/mapView/MapViewActions.h b/client/mapView/MapViewActions.h index b59bb3a60..66d358dbb 100644 --- a/client/mapView/MapViewActions.h +++ b/client/mapView/MapViewActions.h @@ -18,19 +18,11 @@ class MapView; class MapViewActions : public CIntObject { - bool isSwiping; - - Point swipeInitialViewPos; - Point swipeInitialRealPos; - MapView & owner; std::shared_ptr model; std::shared_ptr context; void handleHover(const Point & cursorPosition); - void handleSwipeMove(const Point & cursorPosition); - bool handleSwipeStateChange(bool btnPressed); - bool swipeEnabled() const; public: MapViewActions(MapView & owner, const std::shared_ptr & model); @@ -39,7 +31,7 @@ public: void clickLeft(tribool down, bool previousState) override; void clickRight(tribool down, bool previousState) override; - void clickMiddle(tribool down, bool previousState) override; + void gesturePanning(const Point & distance) override; void hover(bool on) override; void mouseMoved(const Point & cursorPosition) override; void wheelScrolled(bool down, bool in) override; diff --git a/client/windows/GUIClasses.cpp b/client/windows/GUIClasses.cpp index 9bfc6c273..4f7161656 100644 --- a/client/windows/GUIClasses.cpp +++ b/client/windows/GUIClasses.cpp @@ -1788,7 +1788,7 @@ void CObjectListWindow::CItem::clickLeft(tribool down, bool previousState) parent->changeSelection(index); } -void CObjectListWindow::CItem::onDoubleClick() +void CObjectListWindow::CItem::clickDouble() { parent->elementSelected(); } diff --git a/client/windows/GUIClasses.h b/client/windows/GUIClasses.h index d7d2e968c..39584afe0 100644 --- a/client/windows/GUIClasses.h +++ b/client/windows/GUIClasses.h @@ -163,7 +163,7 @@ class CObjectListWindow : public CWindowObject void select(bool on); void clickLeft(tribool down, bool previousState) override; - void onDoubleClick() override; + void clickDouble() override; }; std::function onSelect;//called when OK button is pressed, returns id of selected item.