mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-11-11 04:23:29 -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 <QFont>
|
||||||
#include <QGraphicsScene>
|
#include <QGraphicsScene>
|
||||||
#include <QGraphicsSceneMouseEvent>
|
#include <QGraphicsSceneMouseEvent>
|
||||||
|
#include <QGraphicsView>
|
||||||
#include <QHash>
|
#include <QHash>
|
||||||
#include <QLineF>
|
#include <QLineF>
|
||||||
#include <QList>
|
#include <QList>
|
||||||
|
|
@ -29,6 +30,7 @@
|
||||||
#include <QPen>
|
#include <QPen>
|
||||||
#include <QPixmap>
|
#include <QPixmap>
|
||||||
#include <QPointF>
|
#include <QPointF>
|
||||||
|
#include <QPropertyAnimation>
|
||||||
#include <QRectF>
|
#include <QRectF>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QTransform>
|
#include <QTransform>
|
||||||
|
|
@ -42,6 +44,13 @@
|
||||||
|
|
||||||
class QSizeF;
|
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)
|
AbstractCardItem::AbstractCardItem(QGraphicsItem *parent, const CardRef &cardRef, PlayerLogic *_owner, int _id)
|
||||||
: ArrowTarget(_owner, parent), state(new CardState(this, nullptr, cardRef, _id)), tapAngle(0),
|
: ArrowTarget(_owner, parent), state(new CardState(this, nullptr, cardRef, _id)), tapAngle(0),
|
||||||
bgColor(Qt::transparent), isHovered(false), realZValue(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::cardInfoChanged, this, &AbstractCardItem::onCardInfoChanged);
|
||||||
connect(state, &CardState::cardRefChanged, this, &AbstractCardItem::onCardRefChanged);
|
connect(state, &CardState::cardRefChanged, this, &AbstractCardItem::onCardRefChanged);
|
||||||
connect(state, &CardState::tappedChanged, this, &AbstractCardItem::onTappedChanged);
|
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] {
|
connect(state, &CardState::colorChanged, this, [this] {
|
||||||
cacheBgColor();
|
cacheBgColor();
|
||||||
update();
|
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,
|
connect(&SettingsCache::instance().cardsDisplay(), &CardsDisplaySettings::displayCardNamesChanged, this,
|
||||||
[this] { update(); });
|
[this] { update(); });
|
||||||
connect(&SettingsCache::instance().cardsDisplay(), &CardsDisplaySettings::cardLangChanged, this,
|
connect(&SettingsCache::instance().cardsDisplay(), &CardsDisplaySettings::cardLangChanged, this,
|
||||||
|
|
@ -262,9 +295,38 @@ void AbstractCardItem::setHovered(bool _hovered)
|
||||||
}
|
}
|
||||||
|
|
||||||
isHovered = _hovered;
|
isHovered = _hovered;
|
||||||
setZValue(_hovered ? ZValues::HOVERED_CARD : realZValue);
|
// The stacking override is one-directional: it applies the moment the hover starts
|
||||||
setScale(_hovered && SettingsCache::instance().cardsDisplay().getScaleCards() ? 1.1 : 1);
|
// and only drops once the shrink-out finishes (see the hoverZoom finished handler).
|
||||||
setTransformOriginPoint(_hovered ? CardDimensions::WIDTH_HALF_F : 0, _hovered ? CardDimensions::HEIGHT_HALF_F : 0);
|
// 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();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -273,6 +335,59 @@ void AbstractCardItem::setColor(const QString &_color)
|
||||||
state->setColor(_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()
|
void AbstractCardItem::cacheBgColor()
|
||||||
{
|
{
|
||||||
QChar colorChar;
|
QChar colorChar;
|
||||||
|
|
@ -310,6 +425,7 @@ void AbstractCardItem::cacheBgColor()
|
||||||
|
|
||||||
void AbstractCardItem::onTappedChanged(bool newTapped, bool canAnimate)
|
void AbstractCardItem::onTappedChanged(bool newTapped, bool canAnimate)
|
||||||
{
|
{
|
||||||
|
emit tappedChanged(newTapped);
|
||||||
if (SettingsCache::instance().cardsDisplay().getTapAnimation() && canAnimate) {
|
if (SettingsCache::instance().cardsDisplay().getTapAnimation() && canAnimate) {
|
||||||
static_cast<GameScene *>(scene())->registerAnimationItem(this);
|
static_cast<GameScene *>(scene())->registerAnimationItem(this);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -23,10 +23,13 @@ class PlayerLogic;
|
||||||
class CardInfo;
|
class CardInfo;
|
||||||
class QPainter;
|
class QPainter;
|
||||||
class QPoint;
|
class QPoint;
|
||||||
|
class QPropertyAnimation;
|
||||||
|
|
||||||
class AbstractCardItem : public ArrowTarget, public IAnimatedItem
|
class AbstractCardItem : public ArrowTarget, public IAnimatedItem
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
Q_PROPERTY(qreal cardScale READ getCardScale WRITE setCardScale)
|
||||||
|
Q_PROPERTY(qreal cardOpacity READ getCardOpacity WRITE setCardOpacity)
|
||||||
protected:
|
protected:
|
||||||
CardState *state;
|
CardState *state;
|
||||||
int tapAngle;
|
int tapAngle;
|
||||||
|
|
@ -37,6 +40,8 @@ private:
|
||||||
qreal realZValue;
|
qreal realZValue;
|
||||||
bool pendingSelectionCollapse;
|
bool pendingSelectionCollapse;
|
||||||
int selectionCollapseGeneration = 0; ///< Guards the deferred collapse timer against stale firings
|
int selectionCollapseGeneration = 0; ///< Guards the deferred collapse timer against stale firings
|
||||||
|
QPropertyAnimation *hoverZoom = nullptr;
|
||||||
|
QPropertyAnimation *opacityAnim = nullptr;
|
||||||
private slots:
|
private slots:
|
||||||
void pixmapUpdated();
|
void pixmapUpdated();
|
||||||
void onCardInfoChanged();
|
void onCardInfoChanged();
|
||||||
|
|
@ -57,6 +62,10 @@ signals:
|
||||||
void playSelectedFaceDown(AbstractCardItem *card);
|
void playSelectedFaceDown(AbstractCardItem *card);
|
||||||
void hideSelected(AbstractCardItem *card);
|
void hideSelected(AbstractCardItem *card);
|
||||||
void selectionChanged(AbstractCardItem *card, bool selected);
|
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:
|
public:
|
||||||
enum
|
enum
|
||||||
|
|
@ -116,6 +125,13 @@ public:
|
||||||
}
|
}
|
||||||
void setRealZValue(qreal _zValue);
|
void setRealZValue(qreal _zValue);
|
||||||
void setHovered(bool _hovered);
|
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
|
bool getIsHovered() const
|
||||||
{
|
{
|
||||||
return isHovered;
|
return isHovered;
|
||||||
|
|
|
||||||
|
|
@ -117,6 +117,11 @@ void CardItem::deleteLater()
|
||||||
void CardItem::setZone(CardZoneLogic *_zone)
|
void CardItem::setZone(CardZoneLogic *_zone)
|
||||||
{
|
{
|
||||||
state->setZone(_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()
|
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
|
// Overlay layer Z-values for items that should appear above normal cards
|
||||||
constexpr qreal HOVERED_CARD = ZValueLayerManager::overlayZValue(1.0);
|
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 ARROWS = ZValueLayerManager::overlayZValue(3.0);
|
||||||
constexpr qreal ZONE_VIEW_WIDGET = ZValueLayerManager::overlayZValue(4.0);
|
constexpr qreal ZONE_VIEW_WIDGET = ZValueLayerManager::overlayZValue(4.0);
|
||||||
constexpr qreal DRAG_ITEM = ZValueLayerManager::overlayZValue(5.0);
|
constexpr qreal DRAG_ITEM = ZValueLayerManager::overlayZValue(5.0);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue