ImageViewer: Fix animation playback replay and loop

This fixes the issue that looped animated images do not automatically
loop and that animations can generally not be replayed from start.

It introduces the "resume" state to the tool bar icon in order to
distinguish between "playing an animation from start" and "resuming the
playback of a paused animation".

The setting cache mode QMovie::CacheAll was removed because it prevents
a looped animation from looping, and also its memory consumption (which
depends on animation dimension and frames) does not seem to be
positively outweighed by anything else.

Fixes: QTCREATORBUG-29606
Change-Id: Iaca8d93766201f5f953784be7ee6d56610e63695
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
Reviewed-by: Eike Ziller <eike.ziller@qt.io>
Reviewed-by: Marcus Tillmanns <marcus.tillmanns@qt.io>
This commit is contained in:
Alessandro Portale
2023-09-13 21:55:47 +02:00
parent 7fc69e1063
commit 726f2730e7
3 changed files with 47 additions and 48 deletions

View File

@@ -234,7 +234,7 @@ void ImageViewer::ctor()
d->imageView, &ImageView::reset); d->imageView, &ImageView::reset);
connect(d->file.data(), &ImageViewerFile::reloadFinished, connect(d->file.data(), &ImageViewerFile::reloadFinished,
d->imageView, &ImageView::createScene); d->imageView, &ImageView::createScene);
connect(d->file.data(), &ImageViewerFile::isPausedChanged, connect(d->file.data(), &ImageViewerFile::movieStateChanged,
this, &ImageViewer::updatePauseAction); this, &ImageViewer::updatePauseAction);
connect(d->imageView, &ImageView::scaleFactorChanged, connect(d->imageView, &ImageView::scaleFactorChanged,
this, &ImageViewer::scaleFactorUpdate); this, &ImageViewer::scaleFactorUpdate);
@@ -349,19 +349,42 @@ void ImageViewer::togglePlay()
void ImageViewer::playToggled() void ImageViewer::playToggled()
{ {
d->file->setPaused(!d->file->isPaused()); QMovie *m = d->file->movie();
if (!m)
return;
const QMovie::MovieState state = d->file->movie()->state();
switch (state) {
case QMovie::NotRunning:
m->start();
break;
case QMovie::Paused:
m->setPaused(false);
break;
case QMovie::Running:
m->setPaused(true);
break;
}
} }
void ImageViewer::updatePauseAction() void ImageViewer::updatePauseAction()
{ {
bool isMovie = d->file->type() == ImageViewerFile::TypeMovie; const bool isMovie = d->file->type() == ImageViewerFile::TypeMovie;
if (isMovie && !d->file->isPaused()) { const QMovie::MovieState state = isMovie ? d->file->movie()->state() : QMovie::NotRunning;
d->actionPlayPause->setToolTipBase(Tr::tr("Pause Animation")); CommandAction *a = d->actionPlayPause;
d->actionPlayPause->setIcon(Icons::INTERRUPT_SMALL_TOOLBAR.icon()); switch (state) {
} else { case QMovie::NotRunning:
d->actionPlayPause->setToolTipBase(Tr::tr("Play Animation")); a->setToolTipBase(Tr::tr("Play Animation"));
d->actionPlayPause->setIcon(Icons::RUN_SMALL_TOOLBAR.icon()); a->setIcon(Icons::RUN_SMALL_TOOLBAR.icon());
d->actionPlayPause->setEnabled(isMovie); a->setEnabled(isMovie);
break;
case QMovie::Paused:
a->setToolTipBase(Tr::tr("Resume Paused Animation"));
a->setIcon(Icons::CONTINUE_SMALL_TOOLBAR.icon());
break;
case QMovie::Running:
a->setToolTipBase(Tr::tr("Pause Animation"));
a->setIcon(Icons::INTERRUPT_SMALL_TOOLBAR.icon());
break;
} }
} }

View File

@@ -6,6 +6,7 @@
#include "imageviewerconstants.h" #include "imageviewerconstants.h"
#include "imageviewertr.h" #include "imageviewertr.h"
#include "utils/algorithm.h"
#include <coreplugin/editormanager/documentmodel.h> #include <coreplugin/editormanager/documentmodel.h>
#include <coreplugin/editormanager/ieditor.h> #include <coreplugin/editormanager/ieditor.h>
@@ -115,20 +116,8 @@ Core::IDocument::OpenResult ImageViewerFile::openImpl(QString *errorString,
return OpenResult::CannotHandle; return OpenResult::CannotHandle;
} }
m_type = TypeMovie; m_type = TypeMovie;
m_movie->setCacheMode(QMovie::CacheAll);
connect(
m_movie,
&QMovie::finished,
m_movie,
[this] {
if (m_movie->isValid())
m_movie->start();
},
Qt::QueuedConnection);
connect(m_movie, &QMovie::resized, this, &ImageViewerFile::imageSizeChanged); connect(m_movie, &QMovie::resized, this, &ImageViewerFile::imageSizeChanged);
m_movie->start(); connect(m_movie, &QMovie::stateChanged, this, &ImageViewerFile::movieStateChanged);
m_isPaused = false; // force update
setPaused(true);
} else { } else {
m_pixmap = new QPixmap(fileName); m_pixmap = new QPixmap(fileName);
if (m_pixmap->isNull()) { if (m_pixmap->isNull()) {
@@ -169,18 +158,9 @@ bool ImageViewerFile::reload(QString *errorString,
return success; return success;
} }
bool ImageViewerFile::isPaused() const QMovie *ImageViewerFile::movie() const
{ {
return m_isPaused; return m_movie;
}
void ImageViewerFile::setPaused(bool paused)
{
if (!m_movie || m_isPaused == paused)
return;
m_isPaused = paused;
m_movie->setPaused(paused);
emit isPausedChanged(m_isPaused);
} }
QGraphicsItem *ImageViewerFile::createGraphicsItem() const QGraphicsItem *ImageViewerFile::createGraphicsItem() const
@@ -221,16 +201,13 @@ ImageViewerFile::ImageType ImageViewerFile::type() const
void ImageViewerFile::updateVisibility() void ImageViewerFile::updateVisibility()
{ {
if (!m_movie || m_isPaused) if (!m_movie || m_movie->state() != QMovie::Running)
return; return;
bool visible = false; const bool anyVisible = Utils::anyOf(Core::DocumentModel::editorsForDocument(this),
for (Core::IEditor *editor : Core::DocumentModel::editorsForDocument(this)) { [] (Core::IEditor *editor)
if (editor->widget()->isVisible()) { { return editor->widget()->isVisible(); });
visible = true; if (!anyVisible)
break; m_movie->setPaused(true);
}
}
m_movie->setPaused(!visible);
} }
void ImageViewerFile::cleanUp() void ImageViewerFile::cleanUp()

View File

@@ -6,9 +6,10 @@
#include <coreplugin/idocument.h> #include <coreplugin/idocument.h>
#include <QMovie>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class QGraphicsItem; class QGraphicsItem;
class QMovie;
class QPixmap; class QPixmap;
#ifndef QT_NO_SVG #ifndef QT_NO_SVG
@@ -40,8 +41,7 @@ public:
ReloadBehavior reloadBehavior(ChangeTrigger state, ChangeType type) const override; ReloadBehavior reloadBehavior(ChangeTrigger state, ChangeType type) const override;
bool reload(QString *errorString, ReloadFlag flag, ChangeType type) override; bool reload(QString *errorString, ReloadFlag flag, ChangeType type) override;
bool isPaused() const; QMovie *movie() const;
void setPaused(bool paused);
QGraphicsItem *createGraphicsItem() const; QGraphicsItem *createGraphicsItem() const;
ImageType type() const; ImageType type() const;
@@ -51,7 +51,7 @@ public:
signals: signals:
void openFinished(bool success); void openFinished(bool success);
void imageSizeChanged(const QSize &size); void imageSizeChanged(const QSize &size);
void isPausedChanged(bool paused); void movieStateChanged();
private: private:
void cleanUp(); void cleanUp();
@@ -63,7 +63,6 @@ private:
#endif #endif
QMovie *m_movie = nullptr; QMovie *m_movie = nullptr;
QPixmap *m_pixmap = nullptr; QPixmap *m_pixmap = nullptr;
bool m_isPaused = false;
}; };
} // ImageViewer::Internal } // ImageViewer::Internal