mirror of
https://invent.kde.org/system/dolphin
synced 2024-08-22 01:40:02 +00:00
[dolphin] Animate gifs on preview
Summary: Adds the capability to view animated images on the preview in the information panel. This was a request from a user back in 2009 (https://bugs.kde.org/show_bug.cgi?id=182257), but I think nowadays with stickers/memes and what not, it's even more useful. It keeps the size default transition of the preview viewer before starting the animation, so that the visual integration is smoother. {F7289110} FEATURE: 182257 FIXED-IN: 19.11.80 Test Plan: Open the information panel and hover over some animated images (gif/webp/mng) Reviewers: #dolphin, #vdg, ngraham, elvisangelaccio Reviewed By: #vdg, ngraham Subscribers: pino, fuksitter, meven, broulik, kfm-devel Tags: #dolphin Differential Revision: https://phabricator.kde.org/D23538
This commit is contained in:
parent
96e84bef52
commit
c0ca8f2c79
|
@ -169,6 +169,7 @@ void InformationPanelContent::showItem(const KFileItem& item)
|
|||
if (item != m_item) {
|
||||
m_item = item;
|
||||
|
||||
m_preview->stopAnimatedImage();
|
||||
refreshMetaData();
|
||||
}
|
||||
refreshPreview();
|
||||
|
@ -237,7 +238,8 @@ void InformationPanelContent::refreshPreview()
|
|||
refreshPixmapView();
|
||||
|
||||
const QString mimeType = m_item.mimetype();
|
||||
m_isVideo = mimeType.startsWith(QLatin1String("video/"));
|
||||
const bool isAnimatedImage = m_preview->isAnimatedImage(itemUrl.toLocalFile());
|
||||
m_isVideo = !isAnimatedImage && mimeType.startsWith(QLatin1String("video/"));
|
||||
usePhonon = m_isVideo || mimeType.startsWith(QLatin1String("audio/"));
|
||||
|
||||
if (usePhonon) {
|
||||
|
@ -268,6 +270,9 @@ void InformationPanelContent::refreshPreview()
|
|||
adjustWidgetSizes(parentWidget()->width());
|
||||
}
|
||||
} else {
|
||||
if (isAnimatedImage) {
|
||||
m_preview->setAnimatedImageFileName(itemUrl.toLocalFile());
|
||||
}
|
||||
// When we don't need it, hide the phonon widget first to avoid flickering
|
||||
m_phononWidget->hide();
|
||||
m_preview->show();
|
||||
|
@ -276,6 +281,7 @@ void InformationPanelContent::refreshPreview()
|
|||
}
|
||||
}
|
||||
} else {
|
||||
m_preview->stopAnimatedImage();
|
||||
m_preview->hide();
|
||||
m_phononWidget->hide();
|
||||
}
|
||||
|
@ -303,6 +309,8 @@ void InformationPanelContent::showItems(const KFileItemList& items)
|
|||
m_previewJob->kill();
|
||||
}
|
||||
|
||||
m_preview->stopAnimatedImage();
|
||||
|
||||
m_preview->setPixmap(
|
||||
QIcon::fromTheme(QStringLiteral("dialog-information")).pixmap(KIconLoader::SizeEnormous, KIconLoader::SizeEnormous)
|
||||
);
|
||||
|
|
|
@ -21,14 +21,18 @@
|
|||
|
||||
#include <KIconLoader>
|
||||
|
||||
#include <QImageReader>
|
||||
#include <QMovie>
|
||||
#include <QPainter>
|
||||
#include <QStyle>
|
||||
|
||||
PixmapViewer::PixmapViewer(QWidget* parent, Transition transition) :
|
||||
QWidget(parent),
|
||||
m_animatedImage(nullptr),
|
||||
m_transition(transition),
|
||||
m_animationStep(0),
|
||||
m_sizeHint()
|
||||
m_sizeHint(),
|
||||
m_hasAnimatedImage(false)
|
||||
{
|
||||
setMinimumWidth(KIconLoader::SizeEnormous);
|
||||
setMinimumHeight(KIconLoader::SizeEnormous);
|
||||
|
@ -52,6 +56,11 @@ void PixmapViewer::setPixmap(const QPixmap& pixmap)
|
|||
return;
|
||||
}
|
||||
|
||||
// Avoid flicker with static pixmap if an animated image is running
|
||||
if (m_animatedImage && m_animatedImage->state() == QMovie::Running) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((m_transition != NoTransition) && (m_animation.state() == QTimeLine::Running)) {
|
||||
m_pendingPixmaps.enqueue(pixmap);
|
||||
if (m_pendingPixmaps.count() > 5) {
|
||||
|
@ -65,15 +74,26 @@ void PixmapViewer::setPixmap(const QPixmap& pixmap)
|
|||
m_pixmap = pixmap;
|
||||
update();
|
||||
|
||||
const bool animate = (m_transition != NoTransition) &&
|
||||
(m_pixmap.size() != m_oldPixmap.size());
|
||||
if (animate) {
|
||||
const bool animateTransition = (m_transition != NoTransition) &&
|
||||
(m_pixmap.size() != m_oldPixmap.size());
|
||||
if (animateTransition) {
|
||||
m_animation.start();
|
||||
} else if (m_hasAnimatedImage) {
|
||||
// If there is no transition animation but an animatedImage
|
||||
// and it is not already running, start animating now
|
||||
if (m_animatedImage->state() != QMovie::Running) {
|
||||
m_animatedImage->setScaledSize(m_pixmap.size());
|
||||
m_animatedImage->start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PixmapViewer::setSizeHint(const QSize& size)
|
||||
{
|
||||
if (m_animatedImage && size != m_sizeHint) {
|
||||
m_animatedImage->stop();
|
||||
}
|
||||
|
||||
m_sizeHint = size;
|
||||
updateGeometry();
|
||||
}
|
||||
|
@ -83,13 +103,37 @@ QSize PixmapViewer::sizeHint() const
|
|||
return m_sizeHint;
|
||||
}
|
||||
|
||||
void PixmapViewer::setAnimatedImageFileName(const QString &fileName)
|
||||
{
|
||||
if (!m_animatedImage) {
|
||||
m_animatedImage = new QMovie(this);
|
||||
connect(m_animatedImage, &QMovie::frameChanged, this, &PixmapViewer::updateAnimatedImageFrame);
|
||||
}
|
||||
|
||||
if (m_animatedImage->fileName() != fileName) {
|
||||
m_animatedImage->stop();
|
||||
m_animatedImage->setFileName(fileName);
|
||||
}
|
||||
|
||||
m_hasAnimatedImage = m_animatedImage->isValid() && (m_animatedImage->frameCount() > 1);
|
||||
}
|
||||
|
||||
|
||||
QString PixmapViewer::animatedImageFileName() const
|
||||
{
|
||||
if (!m_hasAnimatedImage) {
|
||||
return QString();
|
||||
}
|
||||
return m_animatedImage->fileName();
|
||||
}
|
||||
|
||||
void PixmapViewer::paintEvent(QPaintEvent* event)
|
||||
{
|
||||
QWidget::paintEvent(event);
|
||||
|
||||
QPainter painter(this);
|
||||
|
||||
if (m_transition != NoTransition) {
|
||||
if (m_transition != NoTransition || (m_hasAnimatedImage && m_animatedImage->state() != QMovie::Running)) {
|
||||
const float value = m_animation.currentValue();
|
||||
const int scaledWidth = static_cast<int>((m_oldPixmap.width() * (1.0 - value)) + (m_pixmap.width() * value));
|
||||
const int scaledHeight = static_cast<int>((m_oldPixmap.height() * (1.0 - value)) + (m_pixmap.height() * value));
|
||||
|
@ -118,8 +162,32 @@ void PixmapViewer::checkPendingPixmaps()
|
|||
m_pixmap = pixmap;
|
||||
update();
|
||||
m_animation.start();
|
||||
} else if (m_hasAnimatedImage) {
|
||||
m_animatedImage->setScaledSize(m_pixmap.size());
|
||||
m_animatedImage->start();
|
||||
} else {
|
||||
m_oldPixmap = m_pixmap;
|
||||
}
|
||||
}
|
||||
|
||||
void PixmapViewer::updateAnimatedImageFrame()
|
||||
{
|
||||
Q_ASSERT (m_animatedImage);
|
||||
|
||||
m_pixmap = m_animatedImage->currentPixmap();
|
||||
update();
|
||||
}
|
||||
|
||||
void PixmapViewer::stopAnimatedImage()
|
||||
{
|
||||
if (m_hasAnimatedImage) {
|
||||
m_animatedImage->stop();
|
||||
m_hasAnimatedImage = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool PixmapViewer::isAnimatedImage(const QString &fileName)
|
||||
{
|
||||
const QByteArray imageFormat = QImageReader::imageFormat(fileName);
|
||||
return !imageFormat.isEmpty() && QMovie::supportedFormats().contains(imageFormat);
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include <QWidget>
|
||||
|
||||
class QPaintEvent;
|
||||
class QMovie;
|
||||
|
||||
/**
|
||||
* @brief Widget which shows a pixmap centered inside the boundaries.
|
||||
|
@ -73,20 +74,33 @@ public:
|
|||
void setSizeHint(const QSize& size);
|
||||
QSize sizeHint() const override;
|
||||
|
||||
void setAnimatedImageFileName(const QString& fileName);
|
||||
QString animatedImageFileName() const;
|
||||
|
||||
void stopAnimatedImage();
|
||||
|
||||
/**
|
||||
* Checks if \a fileName contains an animated image supported by QMovie.
|
||||
*/
|
||||
static bool isAnimatedImage(const QString &fileName);
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent* event) override;
|
||||
|
||||
private Q_SLOTS:
|
||||
void checkPendingPixmaps();
|
||||
void updateAnimatedImageFrame();
|
||||
|
||||
private:
|
||||
QPixmap m_pixmap;
|
||||
QPixmap m_oldPixmap;
|
||||
QMovie* m_animatedImage;
|
||||
QQueue<QPixmap> m_pendingPixmaps;
|
||||
QTimeLine m_animation;
|
||||
Transition m_transition;
|
||||
int m_animationStep;
|
||||
QSize m_sizeHint;
|
||||
bool m_hasAnimatedImage;
|
||||
};
|
||||
|
||||
inline QPixmap PixmapViewer::pixmap() const
|
||||
|
|
Loading…
Reference in a new issue