File indexing completed on 2025-01-26 03:49:00
0001 /* 0002 * Copyright (C) 2010 Parker Coates <coates@kde.org> 0003 * 0004 * This program is free software; you can redistribute it and/or 0005 * modify it under the terms of the GNU General Public License as 0006 * published by the Free Software Foundation; either version 2 of 0007 * the License, or (at your option) any later version. 0008 * 0009 * This program is distributed in the hope that it will be useful, 0010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0012 * GNU General Public License for more details. 0013 * 0014 * You should have received a copy of the GNU General Public License 0015 * along with this program. If not, see <http://www.gnu.org/licenses/>. 0016 * 0017 */ 0018 0019 #include "kcard.h" 0020 #include "kcard_p.h" 0021 0022 // own 0023 #include "kabstractcarddeck.h" 0024 #include "kcardpile.h" 0025 // Qt 0026 #include <QPainter> 0027 #include <QPropertyAnimation> 0028 // Std 0029 #include <cmath> 0030 0031 namespace 0032 { 0033 const qreal raisedZValue = 10000; 0034 } 0035 0036 KCardAnimation::KCardAnimation(KCardPrivate *d, int duration, QPointF pos, qreal rotation, bool faceUp) 0037 : QAbstractAnimation(d) 0038 , d(d) 0039 , m_duration(duration) 0040 , m_x0(d->q->x()) 0041 , m_y0(d->q->y()) 0042 , m_rotation0(d->q->rotation()) 0043 , m_flippedness0(d->flippedness()) 0044 , m_xDelta(pos.x() - m_x0) 0045 , m_yDelta(pos.y() - m_y0) 0046 , m_rotationDelta(rotation - m_rotation0) 0047 , m_flippednessDelta((faceUp ? 1.0 : 0.0) - m_flippedness0) 0048 { 0049 qreal w = d->deck->cardWidth(); 0050 qreal h = d->deck->cardHeight(); 0051 qreal diagSquared = w * w + h * h; 0052 qreal distSquared = m_xDelta * m_xDelta + m_yDelta * m_yDelta; 0053 0054 m_flipProgressFactor = qMax<qreal>(1, sqrt(distSquared / diagSquared)); 0055 } 0056 0057 int KCardAnimation::duration() const 0058 { 0059 return m_duration; 0060 } 0061 0062 void KCardAnimation::updateCurrentTime(int msec) 0063 { 0064 qreal progress = qreal(msec) / m_duration; 0065 qreal flipProgress = qMin<qreal>(1, progress * m_flipProgressFactor); 0066 0067 d->q->setPos(m_x0 + m_xDelta * progress, m_y0 + m_yDelta * progress); 0068 d->q->setRotation(m_rotation0 + m_rotationDelta * progress); 0069 d->setFlippedness(m_flippedness0 + m_flippednessDelta * flipProgress); 0070 } 0071 0072 KCardPrivate::KCardPrivate(KCard *card) 0073 : QObject(card) 0074 , q(card) 0075 { 0076 } 0077 0078 void KCardPrivate::setFlippedness(qreal flippedness) 0079 { 0080 if (flippedness == flipValue) 0081 return; 0082 0083 if (flipValue < 0.5 && flippedness >= 0.5) 0084 q->setPixmap(frontPixmap); 0085 else if (flipValue >= 0.5 && flippedness < 0.5) 0086 q->setPixmap(backPixmap); 0087 0088 flipValue = flippedness; 0089 0090 qreal xOffset = deck->cardWidth() * (0.5 - qAbs(flippedness - 0.5)); 0091 qreal xScale = qAbs(2 * flippedness - 1); 0092 0093 q->setTransform(QTransform().translate(xOffset, 0).scale(xScale, 1)); 0094 } 0095 0096 qreal KCardPrivate::flippedness() const 0097 { 0098 return flipValue; 0099 } 0100 0101 void KCardPrivate::setHighlightedness(qreal highlightedness) 0102 { 0103 highlightValue = highlightedness; 0104 q->update(); 0105 } 0106 0107 qreal KCardPrivate::highlightedness() const 0108 { 0109 return highlightValue; 0110 } 0111 0112 KCard::KCard(quint32 id, KAbstractCardDeck *deck) 0113 : QObject() 0114 , QGraphicsPixmapItem() 0115 , d(new KCardPrivate(this)) 0116 { 0117 d->id = id; 0118 d->deck = deck; 0119 0120 d->faceUp = true; 0121 d->flipValue = d->faceUp ? 1 : 0; 0122 d->highlighted = false; 0123 d->highlightValue = d->highlighted ? 1 : 0; 0124 0125 d->pile = nullptr; 0126 0127 d->animation = nullptr; 0128 0129 d->fadeAnimation = new QPropertyAnimation(d, "highlightedness", d); 0130 d->fadeAnimation->setDuration(150); 0131 d->fadeAnimation->setKeyValueAt(0, 0); 0132 d->fadeAnimation->setKeyValueAt(1, 1); 0133 } 0134 0135 KCard::~KCard() 0136 { 0137 stopAnimation(); 0138 0139 // If the card is in a pile, remove it from there. 0140 if (pile()) 0141 pile()->remove(this); 0142 } 0143 0144 int KCard::type() const 0145 { 0146 return KCard::Type; 0147 } 0148 0149 quint32 KCard::id() const 0150 { 0151 return d->id; 0152 } 0153 0154 int KCard::rank() const 0155 { 0156 return d->deck->rankFromId(d->id); 0157 } 0158 0159 int KCard::suit() const 0160 { 0161 return d->deck->suitFromId(d->id); 0162 } 0163 0164 int KCard::color() const 0165 { 0166 return d->deck->colorFromId(d->id); 0167 } 0168 0169 void KCard::setPile(KCardPile *pile) 0170 { 0171 d->pile = pile; 0172 } 0173 0174 KCardPile *KCard::pile() const 0175 { 0176 return d->pile; 0177 } 0178 0179 void KCard::setFaceUp(bool faceUp) 0180 { 0181 qreal flippedness = faceUp ? 1.0 : 0.0; 0182 if (d->faceUp != faceUp || d->flipValue != flippedness) { 0183 d->faceUp = faceUp; 0184 d->setFlippedness(flippedness); 0185 } 0186 } 0187 0188 bool KCard::isFaceUp() const 0189 { 0190 return d->faceUp; 0191 } 0192 0193 void KCard::animate(QPointF pos, qreal z, qreal rotation, bool faceUp, bool raised, int duration) 0194 { 0195 stopAnimation(); 0196 0197 if (duration > 0 && (qAbs(pos.x() - x()) > 2 || qAbs(pos.y() - y()) > 2 || qAbs(rotation - this->rotation()) > 2 || faceUp != d->faceUp)) { 0198 if (raised) 0199 raise(); 0200 0201 d->destZ = z; 0202 d->faceUp = faceUp; 0203 0204 d->animation = new KCardAnimation(d, duration, pos, rotation, faceUp); 0205 connect(d->animation, &KCardAnimation::finished, this, &KCard::stopAnimation); 0206 d->animation->start(); 0207 Q_EMIT animationStarted(this); 0208 } else { 0209 setPos(pos); 0210 setZValue(z); 0211 setRotation(rotation); 0212 setFaceUp(faceUp); 0213 } 0214 } 0215 0216 bool KCard::isAnimated() const 0217 { 0218 return d->animation != nullptr; 0219 } 0220 0221 void KCard::raise() 0222 { 0223 if (zValue() < raisedZValue) 0224 setZValue(raisedZValue + zValue()); 0225 } 0226 0227 void KCard::setHighlighted(bool flag) 0228 { 0229 if (flag != d->highlighted) { 0230 d->highlighted = flag; 0231 0232 d->fadeAnimation->setDirection(flag ? QAbstractAnimation::Forward : QAbstractAnimation::Backward); 0233 0234 if (d->fadeAnimation->state() != QAbstractAnimation::Running) 0235 d->fadeAnimation->start(); 0236 } 0237 } 0238 0239 bool KCard::isHighlighted() const 0240 { 0241 return d->highlighted; 0242 } 0243 0244 void KCard::completeAnimation() 0245 { 0246 if (!d->animation) 0247 return; 0248 0249 d->animation->disconnect(this); 0250 if (d->animation->state() != QAbstractAnimation::Stopped) 0251 d->animation->setCurrentTime(d->animation->duration()); 0252 0253 stopAnimation(); 0254 } 0255 0256 void KCard::stopAnimation() 0257 { 0258 if (!d->animation) 0259 return; 0260 0261 delete d->animation; 0262 d->animation = nullptr; 0263 0264 setZValue(d->destZ); 0265 0266 Q_EMIT animationStopped(this); 0267 } 0268 0269 void KCard::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) 0270 { 0271 Q_UNUSED(option); 0272 Q_UNUSED(widget); 0273 0274 if (pixmap().size() != d->deck->cardSize() * pixmap().devicePixelRatio()) { 0275 QPixmap newPix = d->deck->cardPixmap(d->id, d->faceUp); 0276 if (d->faceUp) 0277 setFrontPixmap(newPix); 0278 else 0279 setBackPixmap(newPix); 0280 } 0281 0282 // Enable smooth pixmap transformation only if the card is rotated. We 0283 // don't really need it otherwise and it slows down our flip animations. 0284 painter->setRenderHint(QPainter::SmoothPixmapTransform, int(rotation()) % 90); 0285 0286 QPixmap pix = pixmap(); 0287 0288 if (d->highlightValue > 0) { 0289 QPainter p(&pix); 0290 p.setCompositionMode(QPainter::CompositionMode_SourceAtop); 0291 p.fillRect(0, 0, pix.width(), pix.height(), QColor::fromRgbF(0, 0, 0, 0.5 * d->highlightValue)); 0292 } 0293 0294 painter->drawPixmap(0, 0, pix); 0295 } 0296 0297 void KCard::setFrontPixmap(const QPixmap &pix) 0298 { 0299 d->frontPixmap = pix; 0300 if (d->flipValue >= 0.5) 0301 setPixmap(d->frontPixmap); 0302 } 0303 0304 void KCard::setBackPixmap(const QPixmap &pix) 0305 { 0306 d->backPixmap = pix; 0307 if (d->flipValue < 0.5) 0308 setPixmap(d->backPixmap); 0309 } 0310 0311 void KCard::setPixmap(const QPixmap &pix) 0312 { 0313 QGraphicsPixmapItem::setPixmap(pix); 0314 } 0315 0316 #include "moc_kcard.cpp" 0317 #include "moc_kcard_p.cpp"