mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-11-10 20:13:24 -08:00
[Game] Smooth board hover-zoom with fade helpers
This commit is contained in:
parent
92f05fd363
commit
63d64d6578
4 changed files with 145 additions and 4 deletions
|
|
@ -21,6 +21,7 @@
|
|||
#include <QFont>
|
||||
#include <QGraphicsScene>
|
||||
#include <QGraphicsSceneMouseEvent>
|
||||
#include <QGraphicsView>
|
||||
#include <QHash>
|
||||
#include <QLineF>
|
||||
#include <QList>
|
||||
|
|
@ -29,6 +30,7 @@
|
|||
#include <QPen>
|
||||
#include <QPixmap>
|
||||
#include <QPointF>
|
||||
#include <QPropertyAnimation>
|
||||
#include <QRectF>
|
||||
#include <QTimer>
|
||||
#include <QTransform>
|
||||
|
|
@ -42,6 +44,13 @@
|
|||
|
||||
class QSizeF;
|
||||
|
||||
// Width the hovered card smooth-zooms to: exactly the hover panel picture size, so
|
||||
// all four corners coincide when the panel swaps in over it.
|
||||
static constexpr qreal HOVER_ZOOM_WIDTH = 270.0;
|
||||
static constexpr int HOVER_ZOOM_IN_MS = 220;
|
||||
static constexpr int HOVER_ZOOM_OUT_MS = 140;
|
||||
static constexpr int FADE_DURATION_MS = 140;
|
||||
|
||||
AbstractCardItem::AbstractCardItem(QGraphicsItem *parent, const CardRef &cardRef, PlayerLogic *_owner, int _id)
|
||||
: ArrowTarget(_owner, parent), state(new CardState(this, nullptr, cardRef, _id)), tapAngle(0),
|
||||
bgColor(Qt::transparent), isHovered(false), realZValue(0)
|
||||
|
|
@ -54,12 +63,36 @@ AbstractCardItem::AbstractCardItem(QGraphicsItem *parent, const CardRef &cardRef
|
|||
connect(state, &CardState::cardInfoChanged, this, &AbstractCardItem::onCardInfoChanged);
|
||||
connect(state, &CardState::cardRefChanged, this, &AbstractCardItem::onCardRefChanged);
|
||||
connect(state, &CardState::tappedChanged, this, &AbstractCardItem::onTappedChanged);
|
||||
connect(state, &CardState::facedownChanged, this, [this] { update(); });
|
||||
connect(state, &CardState::facedownChanged, this, [this](bool faceDown) {
|
||||
update();
|
||||
emit faceDownChanged(faceDown);
|
||||
});
|
||||
connect(state, &CardState::colorChanged, this, [this] {
|
||||
cacheBgColor();
|
||||
update();
|
||||
});
|
||||
|
||||
hoverZoom = new QPropertyAnimation(this, "cardScale", this);
|
||||
hoverZoom->setEasingCurve(QEasingCurve::InOutCubic);
|
||||
// Settling the unhover waits for the shrink-out to finish: the stacking override,
|
||||
// the clip escape and the ancestor lift all stay put while the card is still large,
|
||||
// so siblings and the clip container cannot cut through the shrinking card.
|
||||
connect(hoverZoom, &QPropertyAnimation::finished, this, [this] {
|
||||
if (!isHovered) {
|
||||
setZValue(realZValue);
|
||||
// Final repaint at the settled footprint: with no scale delta the animation
|
||||
// emits no per-frame updates, and zones would keep a stale clip hole behind.
|
||||
if (scene()) {
|
||||
scene()->update(sceneBoundingRect());
|
||||
}
|
||||
emit hoverShrinkFinished(this);
|
||||
}
|
||||
});
|
||||
|
||||
opacityAnim = new QPropertyAnimation(this, "cardOpacity", this);
|
||||
opacityAnim->setDuration(FADE_DURATION_MS);
|
||||
opacityAnim->setEasingCurve(QEasingCurve::OutCubic);
|
||||
|
||||
connect(&SettingsCache::instance().cardsDisplay(), &CardsDisplaySettings::displayCardNamesChanged, this,
|
||||
[this] { update(); });
|
||||
connect(&SettingsCache::instance().cardsDisplay(), &CardsDisplaySettings::cardLangChanged, this,
|
||||
|
|
@ -262,9 +295,38 @@ void AbstractCardItem::setHovered(bool _hovered)
|
|||
}
|
||||
|
||||
isHovered = _hovered;
|
||||
setZValue(_hovered ? ZValues::HOVERED_CARD : realZValue);
|
||||
setScale(_hovered && SettingsCache::instance().cardsDisplay().getScaleCards() ? 1.1 : 1);
|
||||
setTransformOriginPoint(_hovered ? CardDimensions::WIDTH_HALF_F : 0, _hovered ? CardDimensions::HEIGHT_HALF_F : 0);
|
||||
// The stacking override is one-directional: it applies the moment the hover starts
|
||||
// and only drops once the shrink-out finishes (see the hoverZoom finished handler).
|
||||
// Restoring it here would let siblings paint over the still-enlarged card.
|
||||
if (_hovered) {
|
||||
setZValue(ZValues::HOVERED_CARD);
|
||||
}
|
||||
// Scale about the center in both directions: at scale 1 the origin is moot,
|
||||
// and keeping it centered avoids drift while shrinking back down.
|
||||
setTransformOriginPoint(CardDimensions::WIDTH_HALF_F, CardDimensions::HEIGHT_HALF_F);
|
||||
|
||||
// Smooth-zoom up to the hover picture size (instead of the old 1.1 snap) so the
|
||||
// hover panel can then swap in over the zoomed card with matching corners.
|
||||
// The target is constant ON-SCREEN pixels: divide out the board view zoom so
|
||||
// fullscreen (or zoomed) boards don't overshoot while the fixed-size panel matches.
|
||||
qreal target = 1.0;
|
||||
if (_hovered && SettingsCache::instance().cardsDisplay().getScaleCards()) {
|
||||
const qreal w = boundingRect().width();
|
||||
qreal viewScale = 1.0;
|
||||
if (scene() && !scene()->views().isEmpty()) {
|
||||
if (const auto *view = scene()->views().first()) {
|
||||
viewScale = qAbs(view->transform().m11());
|
||||
}
|
||||
}
|
||||
if (w > 0.0 && viewScale > 0.0) {
|
||||
target = qBound(1.0, HOVER_ZOOM_WIDTH / (w * viewScale), 6.0);
|
||||
}
|
||||
}
|
||||
hoverZoom->stop();
|
||||
hoverZoom->setStartValue(scale());
|
||||
hoverZoom->setEndValue(target);
|
||||
hoverZoom->setDuration(_hovered ? HOVER_ZOOM_IN_MS : HOVER_ZOOM_OUT_MS);
|
||||
hoverZoom->start();
|
||||
update();
|
||||
}
|
||||
|
||||
|
|
@ -273,6 +335,59 @@ void AbstractCardItem::setColor(const QString &_color)
|
|||
state->setColor(_color);
|
||||
}
|
||||
|
||||
qreal AbstractCardItem::getCardScale() const
|
||||
{
|
||||
return scale();
|
||||
}
|
||||
|
||||
void AbstractCardItem::setCardScale(qreal _scale)
|
||||
{
|
||||
if (qFuzzyCompare(_scale, scale())) {
|
||||
return;
|
||||
}
|
||||
// The scale animates every frame: keep the scene index in step or hover
|
||||
// picking tests a stale rect and the hover oscillates on/off by itself.
|
||||
// Repaint the union of the old and new footprints, not just the card: zones
|
||||
// dodge the zoomed card via a clip hole computed at paint time, and without
|
||||
// a repaint they keep a stale hole — covering the card mid-animation and
|
||||
// leaving a black rect behind once the hover ends.
|
||||
const QRectF oldFootprint = sceneBoundingRect();
|
||||
prepareGeometryChange();
|
||||
setScale(_scale);
|
||||
if (scene()) {
|
||||
scene()->update(oldFootprint.united(sceneBoundingRect()));
|
||||
} else {
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
qreal AbstractCardItem::getCardOpacity() const
|
||||
{
|
||||
return opacity();
|
||||
}
|
||||
|
||||
void AbstractCardItem::setCardOpacity(qreal _opacity)
|
||||
{
|
||||
setOpacity(_opacity);
|
||||
update();
|
||||
}
|
||||
|
||||
void AbstractCardItem::fadeCardOut()
|
||||
{
|
||||
opacityAnim->stop();
|
||||
opacityAnim->setStartValue(opacity());
|
||||
opacityAnim->setEndValue(0.0);
|
||||
opacityAnim->start();
|
||||
}
|
||||
|
||||
void AbstractCardItem::fadeCardIn()
|
||||
{
|
||||
opacityAnim->stop();
|
||||
opacityAnim->setStartValue(opacity());
|
||||
opacityAnim->setEndValue(1.0);
|
||||
opacityAnim->start();
|
||||
}
|
||||
|
||||
void AbstractCardItem::cacheBgColor()
|
||||
{
|
||||
QChar colorChar;
|
||||
|
|
@ -310,6 +425,7 @@ void AbstractCardItem::cacheBgColor()
|
|||
|
||||
void AbstractCardItem::onTappedChanged(bool newTapped, bool canAnimate)
|
||||
{
|
||||
emit tappedChanged(newTapped);
|
||||
if (SettingsCache::instance().cardsDisplay().getTapAnimation() && canAnimate) {
|
||||
static_cast<GameScene *>(scene())->registerAnimationItem(this);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -23,10 +23,13 @@ class PlayerLogic;
|
|||
class CardInfo;
|
||||
class QPainter;
|
||||
class QPoint;
|
||||
class QPropertyAnimation;
|
||||
|
||||
class AbstractCardItem : public ArrowTarget, public IAnimatedItem
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(qreal cardScale READ getCardScale WRITE setCardScale)
|
||||
Q_PROPERTY(qreal cardOpacity READ getCardOpacity WRITE setCardOpacity)
|
||||
protected:
|
||||
CardState *state;
|
||||
int tapAngle;
|
||||
|
|
@ -37,6 +40,8 @@ private:
|
|||
qreal realZValue;
|
||||
bool pendingSelectionCollapse;
|
||||
int selectionCollapseGeneration = 0; ///< Guards the deferred collapse timer against stale firings
|
||||
QPropertyAnimation *hoverZoom = nullptr;
|
||||
QPropertyAnimation *opacityAnim = nullptr;
|
||||
private slots:
|
||||
void pixmapUpdated();
|
||||
void onCardInfoChanged();
|
||||
|
|
@ -57,6 +62,10 @@ signals:
|
|||
void playSelectedFaceDown(AbstractCardItem *card);
|
||||
void hideSelected(AbstractCardItem *card);
|
||||
void selectionChanged(AbstractCardItem *card, bool selected);
|
||||
void tappedChanged(bool tapped);
|
||||
void faceDownChanged(bool faceDown);
|
||||
/** @brief Emitted when the hover shrink-out animation runs to completion while unhovered. */
|
||||
void hoverShrinkFinished(AbstractCardItem *card);
|
||||
|
||||
public:
|
||||
enum
|
||||
|
|
@ -116,6 +125,13 @@ public:
|
|||
}
|
||||
void setRealZValue(qreal _zValue);
|
||||
void setHovered(bool _hovered);
|
||||
qreal getCardScale() const;
|
||||
void setCardScale(qreal scale);
|
||||
qreal getCardOpacity() const;
|
||||
void setCardOpacity(qreal opacity);
|
||||
/** @brief Fades the card out under the hover panel, or back in when it closes. */
|
||||
void fadeCardOut();
|
||||
void fadeCardIn();
|
||||
bool getIsHovered() const
|
||||
{
|
||||
return isHovered;
|
||||
|
|
|
|||
|
|
@ -117,6 +117,11 @@ void CardItem::deleteLater()
|
|||
void CardItem::setZone(CardZoneLogic *_zone)
|
||||
{
|
||||
state->setZone(_zone);
|
||||
// A card faded out under the open hover panel must come back as soon as it changes
|
||||
// zones; otherwise it stays invisible at its destination until the panel closes.
|
||||
if (opacity() < 1.0) {
|
||||
fadeCardIn();
|
||||
}
|
||||
}
|
||||
|
||||
void CardItem::retranslateUi()
|
||||
|
|
|
|||
|
|
@ -34,6 +34,10 @@ constexpr qreal OVERLAY_BASE = ZValueLayerManager::OVERLAY_BASE;
|
|||
|
||||
// Overlay layer Z-values for items that should appear above normal cards
|
||||
constexpr qreal HOVERED_CARD = ZValueLayerManager::overlayZValue(1.0);
|
||||
// Lifts the hovered card's whole player area above sibling areas (and their zone
|
||||
// decorations) while hovering. One level only: Qt compares subtrees at their highest
|
||||
// differing ancestor, so the top ancestor decides. Below arrows/views/drags on purpose.
|
||||
constexpr qreal HOVERED_AREA = ZValueLayerManager::overlayZValue(2.0);
|
||||
constexpr qreal ARROWS = ZValueLayerManager::overlayZValue(3.0);
|
||||
constexpr qreal ZONE_VIEW_WIDGET = ZValueLayerManager::overlayZValue(4.0);
|
||||
constexpr qreal DRAG_ITEM = ZValueLayerManager::overlayZValue(5.0);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue