File indexing completed on 2025-02-02 14:19:55
0001 /* 0002 This file is part of the KDE libraries 0003 SPDX-FileCopyrightText: 2000 Kurt Granroth <granroth@kde.org> 0004 SPDX-FileCopyrightText: 2006 Hamish Rodda <rodda@kde.org> 0005 SPDX-FileCopyrightText: 2008 Pino Toscano <pino@kde.org> 0006 0007 SPDX-License-Identifier: LGPL-2.0-only 0008 */ 0009 0010 #include <kanimatedbutton.h> 0011 0012 #include <QImageReader> 0013 #include <QMovie> 0014 #include <QPainter> 0015 #include <QPixmap> 0016 #include <QTimer> 0017 0018 class KAnimatedButtonPrivate 0019 { 0020 public: 0021 KAnimatedButtonPrivate(KAnimatedButton *qq) 0022 : q(qq) 0023 { 0024 } 0025 0026 void updateIcons(); 0027 void updateCurrentIcon(); 0028 void movieFrameChanged(int number); 0029 void movieFinished(); 0030 void timerUpdate(); 0031 0032 KAnimatedButton *const q; 0033 QMovie *movie = nullptr; 0034 0035 int frames; 0036 int current_frame; 0037 QPixmap pixmap; 0038 QTimer timer; 0039 QString icon_path; 0040 QVector<QPixmap *> framesCache; // We keep copies of each frame so that 0041 // the icon code can properly cache them in QPixmapCache, 0042 // and not fill it up with dead copies 0043 }; 0044 0045 KAnimatedButton::KAnimatedButton(QWidget *parent) 0046 : QToolButton(parent) 0047 , d(new KAnimatedButtonPrivate(this)) 0048 { 0049 connect(&d->timer, &QTimer::timeout, this, [this]() { 0050 d->timerUpdate(); 0051 }); 0052 } 0053 0054 KAnimatedButton::~KAnimatedButton() 0055 { 0056 d->timer.stop(); 0057 qDeleteAll(d->framesCache); 0058 delete d->movie; 0059 } 0060 0061 void KAnimatedButton::start() 0062 { 0063 if (d->movie) { 0064 d->movie->start(); 0065 } else { 0066 d->current_frame = 0; 0067 d->timer.start(50); 0068 } 0069 } 0070 0071 void KAnimatedButton::stop() 0072 { 0073 if (d->movie) { 0074 d->movie->stop(); 0075 d->movie->jumpToFrame(0); 0076 d->movieFrameChanged(0); 0077 } else { 0078 d->current_frame = 0; 0079 d->timer.stop(); 0080 d->updateCurrentIcon(); 0081 } 0082 } 0083 0084 void KAnimatedButton::setAnimationPath(const QString &path) 0085 { 0086 if (d->icon_path == path) { 0087 return; 0088 } 0089 0090 d->timer.stop(); 0091 d->icon_path = path; 0092 d->updateIcons(); 0093 } 0094 0095 QString KAnimatedButton::animationPath() const 0096 { 0097 return d->icon_path; 0098 } 0099 0100 void KAnimatedButtonPrivate::timerUpdate() 0101 { 0102 if (!q->isVisible()) { 0103 return; 0104 } 0105 0106 current_frame++; 0107 if (current_frame == frames) { 0108 current_frame = 0; 0109 } 0110 0111 updateCurrentIcon(); 0112 } 0113 0114 void KAnimatedButtonPrivate::updateCurrentIcon() 0115 { 0116 if (pixmap.isNull()) { 0117 return; 0118 } 0119 0120 QPixmap *frame = framesCache[current_frame]; 0121 if (!frame) { 0122 const int icon_size = qMin(pixmap.width(), pixmap.height()); 0123 const int row_size = pixmap.width() / icon_size; 0124 const int row = current_frame / row_size; 0125 const int column = current_frame % row_size; 0126 frame = new QPixmap(icon_size, icon_size); 0127 frame->fill(Qt::transparent); 0128 QPainter p(frame); 0129 p.drawPixmap(QPoint(0, 0), pixmap, QRect(column * icon_size, row * icon_size, icon_size, icon_size)); 0130 p.end(); 0131 framesCache[current_frame] = frame; 0132 } 0133 0134 q->setIcon(QIcon(*frame)); 0135 } 0136 0137 void KAnimatedButtonPrivate::movieFrameChanged(int number) 0138 { 0139 Q_UNUSED(number); 0140 q->setIcon(QIcon(movie->currentPixmap())); 0141 } 0142 0143 void KAnimatedButtonPrivate::movieFinished() 0144 { 0145 // if not running, make it loop 0146 if (movie->state() == QMovie::NotRunning) { 0147 movie->start(); 0148 } 0149 } 0150 0151 void KAnimatedButtonPrivate::updateIcons() 0152 { 0153 pixmap = QPixmap(); 0154 QMovie *newMovie = nullptr; 0155 QImageReader reader(icon_path); 0156 if (QMovie::supportedFormats().contains(reader.format())) { 0157 newMovie = new QMovie(icon_path); 0158 frames = 0; 0159 newMovie->setCacheMode(QMovie::CacheAll); 0160 QObject::connect(newMovie, &QMovie::frameChanged, q, [this](int value) { 0161 movieFrameChanged(value); 0162 }); 0163 QObject::connect(newMovie, &QMovie::finished, q, [this] { 0164 movieFinished(); 0165 }); 0166 } else { 0167 const QPixmap pix(icon_path); 0168 if (pix.isNull()) { 0169 return; 0170 } 0171 0172 const int icon_size = qMin(pix.width(), pix.height()); 0173 if ((pix.height() % icon_size != 0) || (pix.width() % icon_size != 0)) { 0174 return; 0175 } 0176 0177 frames = (pix.height() / icon_size) * (pix.width() / icon_size); 0178 pixmap = pix; 0179 } 0180 0181 current_frame = 0; 0182 qDeleteAll(framesCache); 0183 framesCache.fill(nullptr); 0184 framesCache.resize(frames); 0185 delete movie; 0186 movie = newMovie; 0187 0188 if (movie) { 0189 movie->jumpToFrame(0); 0190 movieFrameChanged(0); 0191 } else { 0192 updateCurrentIcon(); 0193 } 0194 } 0195 0196 #include "moc_kanimatedbutton.cpp"