perf: improve animation smoothness on Wayland
This commit is contained in:
@@ -1,8 +1,19 @@
|
||||
#include "slidingstackedwidget.h"
|
||||
#include <QWidget>
|
||||
#include <QLabel>
|
||||
#include <QScreen>
|
||||
#include "../../qEmbyCore/config/configstore.h"
|
||||
#include "config/config_keys.h"
|
||||
#include <cmath>
|
||||
|
||||
namespace {
|
||||
QPoint interpolatePoint(const QPoint &start, const QPoint &end, qreal progress)
|
||||
{
|
||||
return QPoint(
|
||||
qRound(start.x() + (end.x() - start.x()) * progress),
|
||||
qRound(start.y() + (end.y() - start.y()) * progress));
|
||||
}
|
||||
}
|
||||
|
||||
SlidingStackedWidget::SlidingStackedWidget(QWidget *parent)
|
||||
: QStackedWidget(parent),
|
||||
@@ -11,8 +22,9 @@ SlidingStackedWidget::SlidingStackedWidget(QWidget *parent)
|
||||
m_isAnimating(false),
|
||||
m_nextIndex(0)
|
||||
{
|
||||
m_animGroup = new QParallelAnimationGroup(this);
|
||||
connect(m_animGroup, &QParallelAnimationGroup::finished, this, &SlidingStackedWidget::animationDoneSlot);
|
||||
m_slideTimer.setTimerType(Qt::PreciseTimer);
|
||||
connect(&m_slideTimer, &QTimer::timeout, this,
|
||||
&SlidingStackedWidget::updateSlideAnimation);
|
||||
}
|
||||
|
||||
void SlidingStackedWidget::setSpeed(int speed)
|
||||
@@ -45,22 +57,7 @@ void SlidingStackedWidget::slideInIdx(int idx, SlideDirection direction)
|
||||
|
||||
|
||||
if (m_isAnimating) {
|
||||
|
||||
|
||||
|
||||
bool allTargetsAlive = true;
|
||||
for (int i = 0; i < m_animGroup->animationCount(); ++i) {
|
||||
auto *anim = qobject_cast<QPropertyAnimation *>(m_animGroup->animationAt(i));
|
||||
if (anim && !anim->targetObject()) {
|
||||
allTargetsAlive = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (allTargetsAlive) {
|
||||
m_animGroup->stop();
|
||||
}
|
||||
|
||||
m_slideTimer.stop();
|
||||
animationDoneSlot();
|
||||
}
|
||||
|
||||
@@ -126,45 +123,50 @@ void SlidingStackedWidget::slideInIdx(int idx, SlideDirection direction)
|
||||
QPoint pNext = widgetNext->pos();
|
||||
QPoint pNow = widgetNow->pos();
|
||||
|
||||
|
||||
|
||||
const bool useSnapshot =
|
||||
ConfigStore::instance()->get<bool>(ConfigKeys::SnapshotNavigation, false);
|
||||
ConfigStore::instance()->get<bool>(ConfigKeys::SnapshotNavigation, true);
|
||||
|
||||
QWidget *animTargetNow = widgetNow;
|
||||
QWidget *animTargetNext = widgetNext;
|
||||
bool usingSnapshotAnimation = false;
|
||||
|
||||
if (useSnapshot) {
|
||||
QPixmap snapshot = widgetNow->grab();
|
||||
m_snapshotLabel = new QLabel(this);
|
||||
m_snapshotLabel->setPixmap(snapshot);
|
||||
m_snapshotLabel->setGeometry(widgetNow->geometry());
|
||||
m_snapshotLabel->show();
|
||||
widgetNow->hide();
|
||||
clearSnapshotLabels();
|
||||
|
||||
m_currentSnapshotLabel =
|
||||
createSnapshotLabel(widgetNow, QRect(pNow, QSize(width, height)));
|
||||
|
||||
widgetNext->show();
|
||||
widgetNext->raise();
|
||||
m_nextSnapshotLabel =
|
||||
createSnapshotLabel(widgetNext,
|
||||
QRect(QPoint(pNext.x() + offsetX,
|
||||
pNext.y() + offsetY),
|
||||
QSize(width, height)));
|
||||
|
||||
usingSnapshotAnimation =
|
||||
m_currentSnapshotLabel && m_nextSnapshotLabel;
|
||||
}
|
||||
|
||||
|
||||
widgetNext->move(pNext.x() + offsetX, pNext.y() + offsetY);
|
||||
widgetNext->show();
|
||||
widgetNext->raise();
|
||||
if (usingSnapshotAnimation) {
|
||||
animTargetNow = m_currentSnapshotLabel;
|
||||
animTargetNext = m_nextSnapshotLabel;
|
||||
widgetNow->hide();
|
||||
widgetNext->hide();
|
||||
} else {
|
||||
clearSnapshotLabels();
|
||||
|
||||
|
||||
QObject *animTarget = useSnapshot ? static_cast<QObject*>(m_snapshotLabel)
|
||||
: static_cast<QObject*>(widgetNow);
|
||||
QPropertyAnimation *animNow = new QPropertyAnimation(animTarget, "pos");
|
||||
animNow->setDuration(m_speed);
|
||||
animNow->setEasingCurve(m_animationType);
|
||||
animNow->setStartValue(pNow);
|
||||
animNow->setEndValue(QPoint(pNow.x() - offsetX, pNow.y() - offsetY));
|
||||
widgetNext->move(pNext.x() + offsetX, pNext.y() + offsetY);
|
||||
widgetNext->show();
|
||||
widgetNext->raise();
|
||||
}
|
||||
|
||||
|
||||
QPropertyAnimation *animNext = new QPropertyAnimation(widgetNext, "pos");
|
||||
animNext->setDuration(m_speed);
|
||||
animNext->setEasingCurve(m_animationType);
|
||||
animNext->setStartValue(widgetNext->pos());
|
||||
animNext->setEndValue(pNext);
|
||||
|
||||
m_animGroup->clear();
|
||||
m_animGroup->addAnimation(animNow);
|
||||
m_animGroup->addAnimation(animNext);
|
||||
m_animGroup->start();
|
||||
startSlideAnimation(animTargetNow,
|
||||
pNow,
|
||||
QPoint(pNow.x() - offsetX, pNow.y() - offsetY),
|
||||
animTargetNext,
|
||||
QPoint(pNext.x() + offsetX, pNext.y() + offsetY),
|
||||
pNext);
|
||||
}
|
||||
|
||||
void SlidingStackedWidget::disposeWidgetWhenSafe(QWidget *widget)
|
||||
@@ -186,14 +188,124 @@ void SlidingStackedWidget::disposeWidgetWhenSafe(QWidget *widget)
|
||||
}
|
||||
}
|
||||
|
||||
QLabel *SlidingStackedWidget::createSnapshotLabel(QWidget *source,
|
||||
const QRect &geometry)
|
||||
{
|
||||
if (!source || geometry.size().isEmpty())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QPixmap snapshot = source->grab(QRect(QPoint(0, 0), geometry.size()));
|
||||
if (snapshot.isNull())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto *label = new QLabel(this);
|
||||
label->setAttribute(Qt::WA_TransparentForMouseEvents, true);
|
||||
label->setPixmap(snapshot);
|
||||
label->setGeometry(geometry);
|
||||
label->show();
|
||||
label->raise();
|
||||
return label;
|
||||
}
|
||||
|
||||
void SlidingStackedWidget::clearSnapshotLabels()
|
||||
{
|
||||
if (m_currentSnapshotLabel)
|
||||
{
|
||||
m_currentSnapshotLabel->hide();
|
||||
m_currentSnapshotLabel->deleteLater();
|
||||
m_currentSnapshotLabel = nullptr;
|
||||
}
|
||||
|
||||
if (m_nextSnapshotLabel)
|
||||
{
|
||||
m_nextSnapshotLabel->hide();
|
||||
m_nextSnapshotLabel->deleteLater();
|
||||
m_nextSnapshotLabel = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void SlidingStackedWidget::startSlideAnimation(QWidget *widgetNow,
|
||||
const QPoint &nowStart,
|
||||
const QPoint &nowEnd,
|
||||
QWidget *widgetNext,
|
||||
const QPoint &nextStart,
|
||||
const QPoint &nextEnd)
|
||||
{
|
||||
m_animWidgetNow = widgetNow;
|
||||
m_animWidgetNext = widgetNext;
|
||||
m_animNowStart = nowStart;
|
||||
m_animNowEnd = nowEnd;
|
||||
m_animNextStart = nextStart;
|
||||
m_animNextEnd = nextEnd;
|
||||
|
||||
if (!m_animWidgetNow || !m_animWidgetNext || m_speed <= 0)
|
||||
{
|
||||
animationDoneSlot();
|
||||
return;
|
||||
}
|
||||
|
||||
m_slideTimer.setInterval(animationFrameIntervalMs());
|
||||
m_slideClock.restart();
|
||||
updateSlideAnimation();
|
||||
m_slideTimer.start();
|
||||
}
|
||||
|
||||
void SlidingStackedWidget::updateSlideAnimation()
|
||||
{
|
||||
if (!m_animWidgetNow || !m_animWidgetNext)
|
||||
{
|
||||
animationDoneSlot();
|
||||
return;
|
||||
}
|
||||
|
||||
const qreal elapsedMs = m_slideClock.isValid()
|
||||
? m_slideClock.nsecsElapsed() / 1000000.0
|
||||
: static_cast<qreal>(m_speed);
|
||||
const qreal linearProgress =
|
||||
qBound<qreal>(0.0, elapsedMs / qMax(1, m_speed), 1.0);
|
||||
const qreal easedProgress =
|
||||
QEasingCurve(m_animationType).valueForProgress(linearProgress);
|
||||
|
||||
m_animWidgetNow->move(
|
||||
interpolatePoint(m_animNowStart, m_animNowEnd, easedProgress));
|
||||
m_animWidgetNext->move(
|
||||
interpolatePoint(m_animNextStart, m_animNextEnd, easedProgress));
|
||||
|
||||
if (linearProgress >= 1.0)
|
||||
{
|
||||
animationDoneSlot();
|
||||
}
|
||||
}
|
||||
|
||||
int SlidingStackedWidget::animationFrameIntervalMs() const
|
||||
{
|
||||
const QScreen *targetScreen = screen();
|
||||
if (!targetScreen && window())
|
||||
{
|
||||
targetScreen = window()->screen();
|
||||
}
|
||||
|
||||
const qreal refreshRate = targetScreen ? targetScreen->refreshRate() : 0.0;
|
||||
if (refreshRate <= 0.0)
|
||||
{
|
||||
return 16;
|
||||
}
|
||||
|
||||
return qBound(4,
|
||||
static_cast<int>(std::lround(1000.0 / refreshRate)),
|
||||
16);
|
||||
}
|
||||
|
||||
void SlidingStackedWidget::animationDoneSlot()
|
||||
{
|
||||
|
||||
if (m_snapshotLabel) {
|
||||
m_snapshotLabel->hide();
|
||||
m_snapshotLabel->deleteLater();
|
||||
m_snapshotLabel = nullptr;
|
||||
}
|
||||
m_slideTimer.stop();
|
||||
m_animWidgetNow.clear();
|
||||
m_animWidgetNext.clear();
|
||||
clearSnapshotLabels();
|
||||
|
||||
|
||||
if (m_nextIndex >= 0 && m_nextIndex < count()) {
|
||||
|
||||
Reference in New Issue
Block a user