1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-08-13 19:54:17 +02:00

Added panning gesture, activated by mouse wheel press

This commit is contained in:
Ivan Savenko
2023-05-26 21:46:09 +03:00
parent 1a5c69a424
commit 48859e186e
14 changed files with 55 additions and 137 deletions

View File

@@ -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);

View File

@@ -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;
}
}

View File

@@ -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:

View File

@@ -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;

View File

@@ -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);

View File

@@ -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;
}
}

View File

@@ -29,7 +29,6 @@ class AEventsReceiver
bool strongInterestState;
std::map<MouseButton, bool> 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;

View File

@@ -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
{

View File

@@ -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);

View File

@@ -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()

View File

@@ -25,21 +25,11 @@
MapViewActions::MapViewActions(MapView & owner, const std::shared_ptr<MapViewModel> & 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<IMapRendererContext> & context)
@@ -52,18 +42,8 @@ void MapViewActions::clickLeft(tribool down, bool previousState)
if(indeterminate(down))
return;
if(swipeEnabled())
{
if(handleSwipeStateChange(static_cast<bool>(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;
}
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<bool>(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)

View File

@@ -18,19 +18,11 @@ class MapView;
class MapViewActions : public CIntObject
{
bool isSwiping;
Point swipeInitialViewPos;
Point swipeInitialRealPos;
MapView & owner;
std::shared_ptr<MapViewModel> model;
std::shared_ptr<IMapRendererContext> 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<MapViewModel> & 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;

View File

@@ -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();
}

View File

@@ -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<void(int)> onSelect;//called when OK button is pressed, returns id of selected item.