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
This commit is contained in:
Basile Clement 2024-01-01 22:51:36 +01:00 committed by GitHub
parent badd8952f5
commit 6d032c378f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 23 additions and 14 deletions

View file

@ -20,13 +20,8 @@ AbstractCardDragItem::AbstractCardDragItem(AbstractCardItem *_item,
parentDrag->addChildDrag(this); parentDrag->addChildDrag(this);
setZValue(2000000007 + hotSpot.x() * 1000000 + hotSpot.y() * 1000 + 1000); setZValue(2000000007 + hotSpot.x() * 1000000 + hotSpot.y() * 1000 + 1000);
} else { } else {
if ((hotSpot.x() < 0) || (hotSpot.y() < 0)) { hotSpot = QPointF{qBound(0.0, hotSpot.x(), static_cast<qreal>(CARD_WIDTH - 1)),
qDebug() << "CardDragItem: coordinate overflow: x =" << hotSpot.x() << ", y =" << hotSpot.y(); qBound(0.0, hotSpot.y(), static_cast<qreal>(CARD_HEIGHT - 1))};
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);
}
setCursor(Qt::ClosedHandCursor); setCursor(Qt::ClosedHandCursor);
setZValue(2000000007); setZValue(2000000007);
} }

View file

@ -53,8 +53,20 @@ void CardDragItem::updatePosition(const QPointF &cursorScenePos)
QPointF zonePos = currentZone->scenePos(); QPointF zonePos = currentZone->scenePos();
QPointF cursorPosInZone = cursorScenePos - zonePos; 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<TableZone *>(cursorZone);
QPointF closestGridPoint;
if (tableZone)
closestGridPoint = tableZone->closestGridPoint(cursorPosInZone);
else
closestGridPoint = cursorPosInZone - hotSpot;
QPointF newPos = zonePos + closestGridPoint; QPointF newPos = zonePos + closestGridPoint;
if (newPos != pos()) { if (newPos != pos()) {
@ -83,7 +95,7 @@ void CardDragItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
QList<CardDragItem *> dragItemList; QList<CardDragItem *> dragItemList;
CardZone *startZone = static_cast<CardItem *>(item)->getZone(); CardZone *startZone = static_cast<CardItem *>(item)->getZone();
if (currentZone && !(static_cast<CardItem *>(item)->getAttachedTo() && (startZone == currentZone))) { if (currentZone && !(static_cast<CardItem *>(item)->getAttachedTo() && (startZone == currentZone)) && !occupied) {
dragItemList.append(this); dragItemList.append(this);
for (int i = 0; i < childDrags.size(); i++) { for (int i = 0; i < childDrags.size(); i++) {
CardDragItem *c = static_cast<CardDragItem *>(childDrags[i]); CardDragItem *c = static_cast<CardDragItem *>(childDrags[i]);

View file

@ -348,7 +348,9 @@ void CardItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
bool forceFaceDown = event->modifiers().testFlag(Qt::ShiftModifier); 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(); dragItem->grabMouse();
int childIndex = 0; int childIndex = 0;

View file

@ -346,7 +346,7 @@ QPoint TableZone::mapToGrid(const QPointF &mapPoint) const
// Below calculation effectively rounds to the nearest grid point. // Below calculation effectively rounds to the nearest grid point.
const int gridPointHeight = CARD_HEIGHT + PADDING_Y; const int gridPointHeight = CARD_HEIGHT + PADDING_Y;
int gridPointY = (y + gridPointHeight / 2) / gridPointHeight; int gridPointY = (y + PADDING_Y / 2) / gridPointHeight;
gridPointY = clampValidTableRow(gridPointY); gridPointY = clampValidTableRow(gridPointY);
@ -357,7 +357,7 @@ QPoint TableZone::mapToGrid(const QPointF &mapPoint) const
// widths of each card stack along the row. // widths of each card stack along the row.
// Offset point by the margin amount to reference point within grid area. // 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 // Maximum value is a card width from the right margin, referenced to the
// grid area. // grid area.
@ -385,7 +385,7 @@ QPoint TableZone::mapToGrid(const QPointF &mapPoint) const
QPointF TableZone::closestGridPoint(const QPointF &point) QPointF TableZone::closestGridPoint(const QPointF &point)
{ {
QPoint gridPoint = mapToGrid(point + QPoint(1, 1)); QPoint gridPoint = mapToGrid(point);
gridPoint.setX((gridPoint.x() / 3) * 3); gridPoint.setX((gridPoint.x() / 3) * 3);
if (getCardFromGrid(gridPoint)) if (getCardFromGrid(gridPoint))
gridPoint.setX(gridPoint.x() + 1); gridPoint.setX(gridPoint.x() + 1);