Qt: Add animation to setup wizard

This commit is contained in:
Stenzek
2026-07-03 01:19:54 +10:00
parent 866d230428
commit f31d99e77b
6 changed files with 165 additions and 1 deletions

View File

@@ -23,6 +23,8 @@ set(SRCS
advancedsettingswidget.cpp
advancedsettingswidget.h
advancedsettingswidget.ui
animatedstackedwidget.cpp
animatedstackedwidget.h
asynchttprequest.cpp
asynchttprequest.h
asyncpixmaploader.cpp

View File

@@ -0,0 +1,122 @@
// SPDX-FileCopyrightText: 2019-2026 Connor McLaughlin <stenzek@gmail.com>
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
#include "animatedstackedwidget.h"
#include <QtGui/QPainter>
#include <QtWidgets/QStyle>
#include <utility>
#include "moc_animatedstackedwidget.cpp"
class SlideTransitionOverlay final : public QWidget
{
public:
explicit SlideTransitionOverlay(QWidget* parent) : QWidget(parent) { setAttribute(Qt::WA_TransparentForMouseEvents); }
void start(QPixmap outgoing, QPixmap incoming, bool forward)
{
m_outgoing = std::move(outgoing);
m_incoming = std::move(incoming);
m_direction = forward ? 1 : -1;
m_progress = 0.0;
show();
raise();
}
void setProgress(qreal progress)
{
m_progress = progress;
update();
}
void finish()
{
hide();
m_outgoing = {};
m_incoming = {};
}
protected:
void paintEvent(QPaintEvent* event) override
{
Q_UNUSED(event);
QPainter painter(this);
const int offset = qRound(static_cast<qreal>(width()) * m_progress);
painter.drawPixmap(QPoint(-m_direction * offset, 0), m_outgoing);
painter.drawPixmap(QPoint(m_direction * (width() - offset), 0), m_incoming);
}
private:
QPixmap m_outgoing;
QPixmap m_incoming;
qreal m_progress = 0.0;
int m_direction = 1;
};
static QPixmap GrabPage(QWidget* page)
{
const qreal dpr = page->devicePixelRatioF();
QPixmap pixmap((QSizeF(page->size()) * dpr).toSize());
pixmap.setDevicePixelRatio(dpr);
pixmap.fill(page->palette().window().color());
page->render(&pixmap);
return pixmap;
}
AnimatedStackedWidget::AnimatedStackedWidget(QWidget* parent) : QStackedWidget(parent), m_animation(this)
{
m_overlay = new SlideTransitionOverlay(this);
m_overlay->hide();
m_animation.setDuration(150);
m_animation.setEasingCurve(QEasingCurve::OutCubic);
connect(&m_animation, &QVariantAnimation::valueChanged, this,
[this](const QVariant& value) { m_overlay->setProgress(value.toReal()); });
connect(&m_animation, &QVariantAnimation::finished, this, &AnimatedStackedWidget::finishAnimation);
}
AnimatedStackedWidget::~AnimatedStackedWidget() = default;
void AnimatedStackedWidget::setCurrentIndex(int index)
{
const int previous_index = currentIndex();
if (index == previous_index || index < 0 || index >= count())
return;
finishAnimation();
if (!isVisible() || !style()->styleHint(QStyle::SH_Widget_Animate, nullptr, this))
{
QStackedWidget::setCurrentIndex(index);
return;
}
const QPixmap outgoing = GrabPage(currentWidget());
QStackedWidget::setCurrentIndex(index);
const QPixmap incoming = GrabPage(currentWidget());
m_overlay->setGeometry(contentsRect());
m_overlay->start(outgoing, incoming, index > previous_index);
m_animation.setStartValue(0.0);
m_animation.setEndValue(1.0);
m_animation.start();
}
void AnimatedStackedWidget::setCurrentWidget(QWidget* widget)
{
setCurrentIndex(indexOf(widget));
}
void AnimatedStackedWidget::resizeEvent(QResizeEvent* event)
{
finishAnimation();
QStackedWidget::resizeEvent(event);
}
void AnimatedStackedWidget::finishAnimation()
{
m_animation.stop();
m_overlay->finish();
}

View File

@@ -0,0 +1,31 @@
// SPDX-FileCopyrightText: 2019-2026 Connor McLaughlin <stenzek@gmail.com>
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
#pragma once
#include <QtCore/QVariantAnimation>
#include <QtWidgets/QStackedWidget>
class SlideTransitionOverlay;
class AnimatedStackedWidget final : public QStackedWidget
{
Q_OBJECT
public:
explicit AnimatedStackedWidget(QWidget* parent = nullptr);
~AnimatedStackedWidget() override;
public Q_SLOTS:
void setCurrentIndex(int index);
void setCurrentWidget(QWidget* widget);
protected:
void resizeEvent(QResizeEvent* event) override;
private:
void finishAnimation();
QVariantAnimation m_animation;
SlideTransitionOverlay* m_overlay = nullptr;
};

View File

@@ -6,6 +6,7 @@
<ClCompile Include="achievementsettingswidget.cpp" />
<ClCompile Include="achievementlogindialog.cpp" />
<ClCompile Include="advancedsettingswidget.cpp" />
<ClCompile Include="animatedstackedwidget.cpp" />
<ClCompile Include="asynchttprequest.cpp" />
<ClCompile Include="asyncpixmaploader.cpp" />
<ClCompile Include="audiosettingswidget.cpp" />
@@ -113,6 +114,7 @@
<QtMoc Include="selectdiscdialog.h" />
<ClInclude Include="settingwidgetbinder.h" />
<QtMoc Include="cardradiobutton.h" />
<QtMoc Include="animatedstackedwidget.h" />
<QtMoc Include="consolesettingswidget.h" />
<QtMoc Include="emulationsettingswidget.h" />
<QtMoc Include="gamelistsettingswidget.h" />

View File

@@ -15,6 +15,7 @@
<ClCompile Include="qtprogresscallback.cpp" />
<ClCompile Include="interfacesettingswidget.cpp" />
<ClCompile Include="advancedsettingswidget.cpp" />
<ClCompile Include="animatedstackedwidget.cpp" />
<ClCompile Include="aboutdialog.cpp" />
<ClCompile Include="memorycardsettingswidget.cpp" />
<ClCompile Include="inputbindingdialog.cpp" />
@@ -126,6 +127,7 @@
<QtMoc Include="svgiconengine.h" />
<QtMoc Include="svgwidget.h" />
<QtMoc Include="cardradiobutton.h" />
<QtMoc Include="animatedstackedwidget.h" />
</ItemGroup>
<ItemGroup>
<QtUi Include="consolesettingswidget.ui" />

View File

@@ -201,7 +201,7 @@
</layout>
</item>
<item row="0" column="1">
<widget class="QStackedWidget" name="pages">
<widget class="AnimatedStackedWidget" name="pages">
<property name="currentIndex">
<number>0</number>
</property>
@@ -1354,6 +1354,11 @@ You can change this option at a later time by using the toolbar in Desktop Mode
</layout>
</widget>
<customwidgets>
<customwidget>
<class>AnimatedStackedWidget</class>
<extends>QStackedWidget</extends>
<header>animatedstackedwidget.h</header>
</customwidget>
<customwidget>
<class>CardRadioButton</class>
<extends>QRadioButton</extends>