Merge pull request #2034 from jroweboy/loading-widget
QT Frontend: Add a Loading screen with progressbar
This commit is contained in:
		| @@ -45,5 +45,8 @@ function(copy_yuzu_Qt5_deps target_dir) | ||||
|  | ||||
|     windows_copy_files(yuzu ${Qt5_PLATFORMS_DIR} ${PLATFORMS} qwindows$<$<CONFIG:Debug>:d>.*) | ||||
|     windows_copy_files(yuzu ${Qt5_STYLES_DIR} ${STYLES} qwindowsvistastyle$<$<CONFIG:Debug>:d>.*) | ||||
|     windows_copy_files(yuzu ${Qt5_IMAGEFORMATS_DIR} ${IMAGEFORMATS} qjpeg$<$<CONFIG:Debug>:d>.*) | ||||
|     windows_copy_files(yuzu ${Qt5_IMAGEFORMATS_DIR} ${IMAGEFORMATS} | ||||
|         qjpeg$<$<CONFIG:Debug>:d>.* | ||||
|         qgif$<$<CONFIG:Debug>:d>.* | ||||
|         ) | ||||
| endfunction(copy_yuzu_Qt5_deps) | ||||
|   | ||||
| @@ -68,6 +68,8 @@ add_executable(yuzu | ||||
|     game_list_p.h | ||||
|     game_list_worker.cpp | ||||
|     game_list_worker.h | ||||
|     loading_screen.cpp | ||||
|     loading_screen.h | ||||
|     hotkeys.cpp | ||||
|     hotkeys.h | ||||
|     main.cpp | ||||
| @@ -102,9 +104,10 @@ set(UIS | ||||
|     configuration/configure_system.ui | ||||
|     configuration/configure_touchscreen_advanced.ui | ||||
|     configuration/configure_web.ui | ||||
|     hotkeys.ui | ||||
|     main.ui | ||||
|     compatdb.ui | ||||
|     hotkeys.ui | ||||
|     loading_screen.ui | ||||
|     main.ui | ||||
| ) | ||||
|  | ||||
| file(GLOB COMPAT_LIST | ||||
|   | ||||
| @@ -3,9 +3,7 @@ | ||||
| #include <QKeyEvent> | ||||
| #include <QScreen> | ||||
| #include <QWindow> | ||||
|  | ||||
| #include <fmt/format.h> | ||||
|  | ||||
| #include "common/microprofile.h" | ||||
| #include "common/scm_rev.h" | ||||
| #include "core/core.h" | ||||
| @@ -17,6 +15,7 @@ | ||||
| #include "video_core/renderer_base.h" | ||||
| #include "video_core/video_core.h" | ||||
| #include "yuzu/bootmanager.h" | ||||
| #include "yuzu/main.h" | ||||
|  | ||||
| EmuThread::EmuThread(GRenderWindow* render_window) : render_window(render_window) {} | ||||
|  | ||||
| @@ -114,6 +113,8 @@ GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread) | ||||
|  | ||||
|     InputCommon::Init(); | ||||
|     InputCommon::StartJoystickEventHandler(); | ||||
|     connect(this, &GRenderWindow::FirstFrameDisplayed, static_cast<GMainWindow*>(parent), | ||||
|             &GMainWindow::OnLoadComplete); | ||||
| } | ||||
|  | ||||
| GRenderWindow::~GRenderWindow() { | ||||
| @@ -141,6 +142,10 @@ void GRenderWindow::SwapBuffers() { | ||||
|     child->makeCurrent(); | ||||
|  | ||||
|     child->swapBuffers(); | ||||
|     if (!first_frame) { | ||||
|         emit FirstFrameDisplayed(); | ||||
|         first_frame = true; | ||||
|     } | ||||
| } | ||||
|  | ||||
| void GRenderWindow::MakeCurrent() { | ||||
| @@ -309,6 +314,8 @@ void GRenderWindow::InitRenderTarget() { | ||||
|         delete layout(); | ||||
|     } | ||||
|  | ||||
|     first_frame = false; | ||||
|  | ||||
|     // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground, | ||||
|     // WA_DontShowOnScreen, WA_DeleteOnClose | ||||
|     QGLFormat fmt; | ||||
|   | ||||
| @@ -152,6 +152,7 @@ public slots: | ||||
| signals: | ||||
|     /// Emitted when the window is closed | ||||
|     void Closed(); | ||||
|     void FirstFrameDisplayed(); | ||||
|  | ||||
| private: | ||||
|     std::pair<unsigned, unsigned> ScaleTouch(const QPointF pos) const; | ||||
| @@ -171,6 +172,8 @@ private: | ||||
|     /// Temporary storage of the screenshot taken | ||||
|     QImage screenshot_image; | ||||
|  | ||||
|     bool first_frame = false; | ||||
|  | ||||
| protected: | ||||
|     void showEvent(QShowEvent* event) override; | ||||
| }; | ||||
|   | ||||
							
								
								
									
										84
									
								
								src/yuzu/loading_screen.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								src/yuzu/loading_screen.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,84 @@ | ||||
| // Copyright 2019 yuzu Emulator Project | ||||
| // Licensed under GPLv2 or any later version | ||||
| // Refer to the license.txt file included. | ||||
|  | ||||
| #include <QBuffer> | ||||
| #include <QByteArray> | ||||
| #include <QHBoxLayout> | ||||
| #include <QIODevice> | ||||
| #include <QImage> | ||||
| #include <QLabel> | ||||
| #include <QPainter> | ||||
| #include <QPalette> | ||||
| #include <QPixmap> | ||||
| #include <QProgressBar> | ||||
| #include <QStyleOption> | ||||
| #include <QWindow> | ||||
| #include "common/logging/log.h" | ||||
| #include "core/loader/loader.h" | ||||
| #include "ui_loading_screen.h" | ||||
| #include "yuzu/loading_screen.h" | ||||
|  | ||||
| // Mingw seems to not have QMovie at all. If QMovie is missing then use a single frame instead of an | ||||
| // showing the full animation | ||||
| #if !YUZU_QT_MOVIE_MISSING | ||||
| #include <QMovie> | ||||
| #endif | ||||
|  | ||||
| LoadingScreen::LoadingScreen(QWidget* parent) | ||||
|     : QWidget(parent), ui(std::make_unique<Ui::LoadingScreen>()) { | ||||
|     ui->setupUi(this); | ||||
|     // Progress bar is hidden until we have a use for it. | ||||
|     ui->progress_bar->hide(); | ||||
| } | ||||
|  | ||||
| LoadingScreen::~LoadingScreen() = default; | ||||
|  | ||||
| void LoadingScreen::Prepare(Loader::AppLoader& loader) { | ||||
|     std::vector<u8> buffer; | ||||
|     if (loader.ReadBanner(buffer) == Loader::ResultStatus::Success) { | ||||
| #ifdef YUZU_QT_MOVIE_MISSING | ||||
|         QPixmap map; | ||||
|         map.loadFromData(buffer.data(), buffer.size()); | ||||
|         ui->banner->setPixmap(map); | ||||
| #else | ||||
|         backing_mem = | ||||
|             std::make_unique<QByteArray>(reinterpret_cast<char*>(buffer.data()), buffer.size()); | ||||
|         backing_buf = std::make_unique<QBuffer>(backing_mem.get()); | ||||
|         backing_buf->open(QIODevice::ReadOnly); | ||||
|         animation = std::make_unique<QMovie>(backing_buf.get(), QByteArray("GIF")); | ||||
|         animation->start(); | ||||
|         ui->banner->setMovie(animation.get()); | ||||
| #endif | ||||
|         buffer.clear(); | ||||
|     } | ||||
|     if (loader.ReadLogo(buffer) == Loader::ResultStatus::Success) { | ||||
|         QPixmap map; | ||||
|         map.loadFromData(buffer.data(), buffer.size()); | ||||
|         ui->logo->setPixmap(map); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void LoadingScreen::OnLoadProgress(std::size_t value, std::size_t total) { | ||||
|     if (total != previous_total) { | ||||
|         ui->progress_bar->setMaximum(total); | ||||
|         previous_total = total; | ||||
|     } | ||||
|     ui->progress_bar->setValue(value); | ||||
| } | ||||
|  | ||||
| void LoadingScreen::paintEvent(QPaintEvent* event) { | ||||
|     QStyleOption opt; | ||||
|     opt.init(this); | ||||
|     QPainter p(this); | ||||
|     style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); | ||||
|     QWidget::paintEvent(event); | ||||
| } | ||||
|  | ||||
| void LoadingScreen::Clear() { | ||||
| #ifndef YUZU_QT_MOVIE_MISSING | ||||
|     animation.reset(); | ||||
|     backing_buf.reset(); | ||||
|     backing_mem.reset(); | ||||
| #endif | ||||
| } | ||||
							
								
								
									
										56
									
								
								src/yuzu/loading_screen.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								src/yuzu/loading_screen.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,56 @@ | ||||
| // Copyright 2019 yuzu Emulator Project | ||||
| // Licensed under GPLv2 or any later version | ||||
| // Refer to the license.txt file included. | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <memory> | ||||
| #include <QWidget> | ||||
|  | ||||
| #if !QT_CONFIG(movie) | ||||
| #define YUZU_QT_MOVIE_MISSING 1 | ||||
| #endif | ||||
|  | ||||
| namespace Loader { | ||||
| class AppLoader; | ||||
| } | ||||
|  | ||||
| namespace Ui { | ||||
| class LoadingScreen; | ||||
| } | ||||
|  | ||||
| class QBuffer; | ||||
| class QByteArray; | ||||
| class QMovie; | ||||
|  | ||||
| class LoadingScreen : public QWidget { | ||||
|     Q_OBJECT | ||||
|  | ||||
| public: | ||||
|     explicit LoadingScreen(QWidget* parent = nullptr); | ||||
|  | ||||
|     ~LoadingScreen(); | ||||
|  | ||||
|     /// Call before showing the loading screen to load the widgets with the logo and banner for the | ||||
|     /// currently loaded application. | ||||
|     void Prepare(Loader::AppLoader& loader); | ||||
|  | ||||
|     /// After the loading screen is hidden, the owner of this class can call this to clean up any | ||||
|     /// used resources such as the logo and banner. | ||||
|     void Clear(); | ||||
|  | ||||
|     // In order to use a custom widget with a stylesheet, you need to override the paintEvent | ||||
|     // See https://wiki.qt.io/How_to_Change_the_Background_Color_of_QWidget | ||||
|     void paintEvent(QPaintEvent* event) override; | ||||
|  | ||||
|     void OnLoadProgress(std::size_t value, std::size_t total); | ||||
|  | ||||
| private: | ||||
| #ifndef YUZU_QT_MOVIE_MISSING | ||||
|     std::unique_ptr<QMovie> animation; | ||||
|     std::unique_ptr<QBuffer> backing_buf; | ||||
|     std::unique_ptr<QByteArray> backing_mem; | ||||
| #endif | ||||
|     std::unique_ptr<Ui::LoadingScreen> ui; | ||||
|     std::size_t previous_total = 0; | ||||
| }; | ||||
							
								
								
									
										79
									
								
								src/yuzu/loading_screen.ui
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								src/yuzu/loading_screen.ui
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,79 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <ui version="4.0"> | ||||
|  <class>LoadingScreen</class> | ||||
|  <widget class="QWidget" name="LoadingScreen"> | ||||
|   <property name="geometry"> | ||||
|    <rect> | ||||
|     <x>0</x> | ||||
|     <y>0</y> | ||||
|     <width>746</width> | ||||
|     <height>495</height> | ||||
|    </rect> | ||||
|   </property> | ||||
|   <property name="styleSheet"> | ||||
|    <string notr="true">background-color: rgb(0, 0, 0);</string> | ||||
|   </property> | ||||
|   <layout class="QVBoxLayout"> | ||||
|    <property name="spacing"> | ||||
|     <number>0</number> | ||||
|    </property> | ||||
|    <property name="leftMargin"> | ||||
|     <number>0</number> | ||||
|    </property> | ||||
|    <property name="topMargin"> | ||||
|     <number>0</number> | ||||
|    </property> | ||||
|    <property name="rightMargin"> | ||||
|     <number>0</number> | ||||
|    </property> | ||||
|    <property name="bottomMargin"> | ||||
|     <number>0</number> | ||||
|    </property> | ||||
|    <item> | ||||
|     <widget class="QLabel" name="logo"> | ||||
|      <property name="text"> | ||||
|       <string/> | ||||
|      </property> | ||||
|      <property name="alignment"> | ||||
|       <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> | ||||
|      </property> | ||||
|      <property name="margin"> | ||||
|       <number>30</number> | ||||
|      </property> | ||||
|     </widget> | ||||
|    </item> | ||||
|    <item> | ||||
|     <layout class="QHBoxLayout" name="horizontalLayout"> | ||||
|      <item> | ||||
|       <widget class="QProgressBar" name="progress_bar"> | ||||
|        <property name="styleSheet"> | ||||
|         <string notr="true">font-size: 26px;</string> | ||||
|        </property> | ||||
|        <property name="value"> | ||||
|         <number>0</number> | ||||
|        </property> | ||||
|        <property name="format"> | ||||
|         <string>Loading Shaders %v out of %m</string> | ||||
|        </property> | ||||
|       </widget> | ||||
|      </item> | ||||
|     </layout> | ||||
|    </item> | ||||
|    <item alignment="Qt::AlignRight|Qt::AlignBottom"> | ||||
|     <widget class="QLabel" name="banner"> | ||||
|      <property name="styleSheet"> | ||||
|       <string notr="true">background-color: black;</string> | ||||
|      </property> | ||||
|      <property name="text"> | ||||
|       <string/> | ||||
|      </property> | ||||
|      <property name="margin"> | ||||
|       <number>30</number> | ||||
|      </property> | ||||
|     </widget> | ||||
|    </item> | ||||
|   </layout> | ||||
|  </widget> | ||||
|  <resources/> | ||||
|  <connections/> | ||||
| </ui> | ||||
| @@ -92,6 +92,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual | ||||
| #include "yuzu/game_list.h" | ||||
| #include "yuzu/game_list_p.h" | ||||
| #include "yuzu/hotkeys.h" | ||||
| #include "yuzu/loading_screen.h" | ||||
| #include "yuzu/main.h" | ||||
| #include "yuzu/ui_settings.h" | ||||
|  | ||||
| @@ -411,6 +412,10 @@ void GMainWindow::InitializeWidgets() { | ||||
|     game_list = new GameList(vfs, this); | ||||
|     ui.horizontalLayout->addWidget(game_list); | ||||
|  | ||||
|     loading_screen = new LoadingScreen(this); | ||||
|     loading_screen->hide(); | ||||
|     ui.horizontalLayout->addWidget(loading_screen); | ||||
|  | ||||
|     // Create status bar | ||||
|     message_label = new QLabel(); | ||||
|     // Configured separately for left alignment | ||||
| @@ -897,8 +902,9 @@ void GMainWindow::BootGame(const QString& filename) { | ||||
|                        .arg(Common::g_build_fullname, Common::g_scm_branch, Common::g_scm_desc, | ||||
|                             QString::fromStdString(title_name))); | ||||
|  | ||||
|     render_window->show(); | ||||
|     render_window->setFocus(); | ||||
|     loading_screen->Prepare(Core::System::GetInstance().GetAppLoader()); | ||||
|     loading_screen->show(); | ||||
|     loading_screen->setFocus(); | ||||
|  | ||||
|     emulation_running = true; | ||||
|     if (ui.action_Fullscreen->isChecked()) { | ||||
| @@ -932,6 +938,8 @@ void GMainWindow::ShutdownGame() { | ||||
|     ui.action_Load_Amiibo->setEnabled(false); | ||||
|     ui.action_Capture_Screenshot->setEnabled(false); | ||||
|     render_window->hide(); | ||||
|     loading_screen->hide(); | ||||
|     loading_screen->Clear(); | ||||
|     game_list->show(); | ||||
|     game_list->setFilterFocus(); | ||||
|     setWindowTitle(QString("yuzu %1| %2-%3") | ||||
| @@ -1505,6 +1513,13 @@ void GMainWindow::OnStopGame() { | ||||
|     ShutdownGame(); | ||||
| } | ||||
|  | ||||
| void GMainWindow::OnLoadComplete() { | ||||
|     loading_screen->hide(); | ||||
|     loading_screen->Clear(); | ||||
|     render_window->show(); | ||||
|     render_window->setFocus(); | ||||
| } | ||||
|  | ||||
| void GMainWindow::OnMenuReportCompatibility() { | ||||
|     if (!Settings::values.yuzu_token.empty() && !Settings::values.yuzu_username.empty()) { | ||||
|         CompatDB compatdb{this}; | ||||
| @@ -1771,9 +1786,8 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) { | ||||
|             this, tr("Confirm Key Rederivation"), | ||||
|             tr("You are about to force rederive all of your keys. \nIf you do not know what this " | ||||
|                "means or what you are doing, \nthis is a potentially destructive action. \nPlease " | ||||
|                "make " | ||||
|                "sure this is what you want \nand optionally make backups.\n\nThis will delete your " | ||||
|                "autogenerated key files and re-run the key derivation module."), | ||||
|                "make sure this is what you want \nand optionally make backups.\n\nThis will delete " | ||||
|                "your autogenerated key files and re-run the key derivation module."), | ||||
|             QMessageBox::StandardButtons{QMessageBox::Ok, QMessageBox::Cancel}); | ||||
|  | ||||
|         if (res == QMessageBox::Cancel) | ||||
| @@ -1818,7 +1832,7 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) { | ||||
|                     errors + | ||||
|                     tr("<br><br>You can get all of these and dump all of your games easily by " | ||||
|                        "following <a href='https://yuzu-emu.org/help/quickstart/'>the " | ||||
|                        "quickstart guide</a>. Alternatively, you can use another method of dumping " | ||||
|                        "quickstart guide</a>. Alternatively, you can use another method of dumping" | ||||
|                        "to obtain all of your keys.")); | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -25,6 +25,7 @@ class GImageInfo; | ||||
| class GraphicsBreakPointsWidget; | ||||
| class GraphicsSurfaceWidget; | ||||
| class GRenderWindow; | ||||
| class LoadingScreen; | ||||
| class MicroProfileDialog; | ||||
| class ProfilerWidget; | ||||
| class QLabel; | ||||
| @@ -109,10 +110,10 @@ signals: | ||||
|     void WebBrowserFinishedBrowsing(); | ||||
|  | ||||
| public slots: | ||||
|     void OnLoadComplete(); | ||||
|     void ProfileSelectorSelectProfile(); | ||||
|     void SoftwareKeyboardGetText(const Core::Frontend::SoftwareKeyboardParameters& parameters); | ||||
|     void SoftwareKeyboardInvokeCheckDialog(std::u16string error_message); | ||||
|  | ||||
|     void WebBrowserOpenPage(std::string_view filename, std::string_view arguments); | ||||
|  | ||||
| private: | ||||
| @@ -212,6 +213,7 @@ private: | ||||
|  | ||||
|     GRenderWindow* render_window; | ||||
|     GameList* game_list; | ||||
|     LoadingScreen* loading_screen; | ||||
|  | ||||
|     // Status bar elements | ||||
|     QLabel* message_label = nullptr; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 bunnei
					bunnei