From 6d032c378f65543c1a2fdc341e98448422d3e8b9 Mon Sep 17 00:00:00 2001 From: Basile Clement Date: Mon, 1 Jan 2024 22:51:36 +0100 Subject: [PATCH] Improve drag & drop behavior (#4963) * Improve drag & drop behavior This patch tweaks the drag & drop behavior (in particular, the grid placement) to be more intuitive. More precisely, with this patch the drag & drop will: - Only use the "hot spot" (i.e. position of the cursor on the card) for zones where the card is actually displayed around the cursor (in particular, not on the table where the card snaps to the grid). - Use better boundaries computed with respect to the center of the card (rather than its top left corner) for determining which grid cell a card should go to - Align behavior of the preview and the actual effect when overflow of the 3-card stacks occurs - Avoid visual glitches where the cursor ends up outside of the card or at incorrect offsets when moving the mouse too fast (which translates to overflows of the hot spot computation) * Address review comments - Use simpler computation for restricting hotSpot range - Prevent dropping cards onto full 3-card slots --- cockatrice/src/abstractcarddragitem.cpp | 9 ++------- cockatrice/src/carddragitem.cpp | 18 +++++++++++++++--- cockatrice/src/carditem.cpp | 4 +++- cockatrice/src/tablezone.cpp | 6 +++--- 4 files changed, 23 insertions(+), 14 deletions(-) diff --git a/cockatrice/src/abstractcarddragitem.cpp b/cockatrice/src/abstractcarddragitem.cpp index d3d830c7..a52879ed 100644 --- a/cockatrice/src/abstractcarddragitem.cpp +++ b/cockatrice/src/abstractcarddragitem.cpp @@ -20,13 +20,8 @@ AbstractCardDragItem::AbstractCardDragItem(AbstractCardItem *_item, parentDrag->addChildDrag(this); setZValue(2000000007 + hotSpot.x() * 1000000 + hotSpot.y() * 1000 + 1000); } else { - if ((hotSpot.x() < 0) || (hotSpot.y() < 0)) { - qDebug() << "CardDragItem: coordinate overflow: x =" << hotSpot.x() << ", y =" << hotSpot.y(); - hotSpot = QPointF(); - } else if ((hotSpot.x() > CARD_WIDTH) || (hotSpot.y() > CARD_HEIGHT)) { - qDebug() << "CardDragItem: coordinate overflow: x =" << hotSpot.x() << ", y =" << hotSpot.y(); - hotSpot = QPointF(CARD_WIDTH, CARD_HEIGHT); - } + hotSpot = QPointF{qBound(0.0, hotSpot.x(), static_cast(CARD_WIDTH - 1)), + qBound(0.0, hotSpot.y(), static_cast(CARD_HEIGHT - 1))}; setCursor(Qt::ClosedHandCursor); setZValue(2000000007); } diff --git a/cockatrice/src/carddragitem.cpp b/cockatrice/src/carddragitem.cpp index a91ea7fc..22af1f21 100644 --- a/cockatrice/src/carddragitem.cpp +++ b/cockatrice/src/carddragitem.cpp @@ -53,8 +53,20 @@ void CardDragItem::updatePosition(const QPointF &cursorScenePos) QPointF zonePos = currentZone->scenePos(); QPointF cursorPosInZone = cursorScenePos - zonePos; - QPointF cardTopLeft = cursorPosInZone - hotSpot; - QPointF closestGridPoint = cursorZone->closestGridPoint(cardTopLeft); + + // If we are on a Table, we center the card around the cursor, because we + // snap it into place and no longer see it being dragged. + // + // For other zones (where we do display the card under the cursor), we use + // the hotspot to feel like the card was dragged at the corresponding + // position. + TableZone *tableZone = qobject_cast(cursorZone); + QPointF closestGridPoint; + if (tableZone) + closestGridPoint = tableZone->closestGridPoint(cursorPosInZone); + else + closestGridPoint = cursorPosInZone - hotSpot; + QPointF newPos = zonePos + closestGridPoint; if (newPos != pos()) { @@ -83,7 +95,7 @@ void CardDragItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) QList dragItemList; CardZone *startZone = static_cast(item)->getZone(); - if (currentZone && !(static_cast(item)->getAttachedTo() && (startZone == currentZone))) { + if (currentZone && !(static_cast(item)->getAttachedTo() && (startZone == currentZone)) && !occupied) { dragItemList.append(this); for (int i = 0; i < childDrags.size(); i++) { CardDragItem *c = static_cast(childDrags[i]); diff --git a/cockatrice/src/carditem.cpp b/cockatrice/src/carditem.cpp index d4ddc6fe..6a3bb86c 100644 --- a/cockatrice/src/carditem.cpp +++ b/cockatrice/src/carditem.cpp @@ -348,7 +348,9 @@ void CardItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) bool forceFaceDown = event->modifiers().testFlag(Qt::ShiftModifier); - createDragItem(id, event->pos(), event->scenePos(), facedown || forceFaceDown); + // Use the buttonDownPos to align the hot spot with the position when + // the user originally clicked + createDragItem(id, event->buttonDownPos(Qt::LeftButton), event->scenePos(), facedown || forceFaceDown); dragItem->grabMouse(); int childIndex = 0; diff --git a/cockatrice/src/tablezone.cpp b/cockatrice/src/tablezone.cpp index 16bd8709..a21e99db 100644 --- a/cockatrice/src/tablezone.cpp +++ b/cockatrice/src/tablezone.cpp @@ -346,7 +346,7 @@ QPoint TableZone::mapToGrid(const QPointF &mapPoint) const // Below calculation effectively rounds to the nearest grid point. const int gridPointHeight = CARD_HEIGHT + PADDING_Y; - int gridPointY = (y + gridPointHeight / 2) / gridPointHeight; + int gridPointY = (y + PADDING_Y / 2) / gridPointHeight; gridPointY = clampValidTableRow(gridPointY); @@ -357,7 +357,7 @@ QPoint TableZone::mapToGrid(const QPointF &mapPoint) const // widths of each card stack along the row. // Offset point by the margin amount to reference point within grid area. - int x = mapPoint.x() - MARGIN_LEFT; + int x = mapPoint.x() - MARGIN_LEFT + PADDING_X / 2; // Maximum value is a card width from the right margin, referenced to the // grid area. @@ -385,7 +385,7 @@ QPoint TableZone::mapToGrid(const QPointF &mapPoint) const QPointF TableZone::closestGridPoint(const QPointF &point) { - QPoint gridPoint = mapToGrid(point + QPoint(1, 1)); + QPoint gridPoint = mapToGrid(point); gridPoint.setX((gridPoint.x() / 3) * 3); if (getCardFromGrid(gridPoint)) gridPoint.setX(gridPoint.x() + 1);