Browse Source

Show startup progress dialog

PR #17389.
adaptive-webui-19844
Vladimir Golovnev 2 years ago committed by GitHub
parent
commit
e24aaa4ce1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 192
      src/app/application.cpp
  2. 4
      src/app/application.h
  3. 11
      src/base/bittorrent/session.cpp
  4. 1
      src/base/bittorrent/session.h
  5. 13
      src/gui/mainwindow.cpp
  6. 8
      src/gui/mainwindow.h

192
src/app/application.cpp

@ -53,6 +53,7 @@
#include <QMenu> #include <QMenu>
#include <QMessageBox> #include <QMessageBox>
#include <QPixmapCache> #include <QPixmapCache>
#include <QProgressDialog>
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
#include <QSessionManager> #include <QSessionManager>
#include <QSharedMemory> #include <QSharedMemory>
@ -644,6 +645,7 @@ void Application::processParams(const AddTorrentParams &params)
} }
int Application::exec(const QStringList &params) int Application::exec(const QStringList &params)
try
{ {
#if !defined(DISABLE_WEBUI) && defined(DISABLE_GUI) #if !defined(DISABLE_WEBUI) && defined(DISABLE_GUI)
const QString loadingStr = tr("WebUI will be started shortly after internal preparations. Please wait..."); const QString loadingStr = tr("WebUI will be started shortly after internal preparations. Please wait...");
@ -663,17 +665,107 @@ int Application::exec(const QStringList &params)
Net::DownloadManager::initInstance(); Net::DownloadManager::initInstance();
IconProvider::initInstance(); IconProvider::initInstance();
try
{
BitTorrent::Session::initInstance(); BitTorrent::Session::initInstance();
#ifndef DISABLE_GUI
UIThemeManager::initInstance();
m_desktopIntegration = new DesktopIntegration(this);
m_desktopIntegration->setToolTip(tr("Loading torrents..."));
#ifndef Q_OS_MACOS
auto *desktopIntegrationMenu = new QMenu;
auto *actionExit = new QAction(tr("E&xit"), desktopIntegrationMenu);
actionExit->setIcon(UIThemeManager::instance()->getIcon(u"application-exit"_qs));
actionExit->setMenuRole(QAction::QuitRole);
actionExit->setShortcut(Qt::CTRL | Qt::Key_Q);
connect(actionExit, &QAction::triggered, this, [this]()
{
QApplication::exit();
});
desktopIntegrationMenu->addAction(actionExit);
m_desktopIntegration->setMenu(desktopIntegrationMenu);
#endif
const auto *pref = Preferences::instance();
#ifndef Q_OS_MACOS
const bool isHidden = m_desktopIntegration->isActive() && pref->startMinimized() && pref->minimizeToTray();
#else
const bool isHidden = false;
#endif
if (!isHidden)
{
createStartupProgressDialog();
// Add a small delay to avoid "flashing" the progress dialog in case there are not many torrents to restore.
m_startupProgressDialog->setMinimumDuration(1000);
if (pref->startMinimized())
m_startupProgressDialog->setWindowState(Qt::WindowMinimized);
}
else
{
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
connect(m_desktopIntegration, &DesktopIntegration::activationRequested, this, &Application::createStartupProgressDialog);
#else
connect(m_desktopIntegration, &DesktopIntegration::activationRequested, this, &Application::createStartupProgressDialog, Qt::SingleShotConnection);
#endif
}
#endif
connect(BitTorrent::Session::instance(), &BitTorrent::Session::restored, this, [this]() connect(BitTorrent::Session::instance(), &BitTorrent::Session::restored, this, [this]()
{ {
connect(BitTorrent::Session::instance(), &BitTorrent::Session::torrentFinished, this, &Application::torrentFinished);
connect(BitTorrent::Session::instance(), &BitTorrent::Session::allTorrentsFinished, this, &Application::allTorrentsFinished, Qt::QueuedConnection);
Net::GeoIPManager::initInstance();
TorrentFilesWatcher::initInstance();
new RSS::Session; // create RSS::Session singleton
new RSS::AutoDownloader; // create RSS::AutoDownloader singleton
#ifndef DISABLE_GUI
const auto *btSession = BitTorrent::Session::instance();
connect(btSession, &BitTorrent::Session::fullDiskError, this
, [this](const BitTorrent::Torrent *torrent, const QString &msg)
{
m_desktopIntegration->showNotification(tr("I/O Error", "i.e: Input/Output Error")
, tr("An I/O error occurred for torrent '%1'.\n Reason: %2"
, "e.g: An error occurred for torrent 'xxx.avi'.\n Reason: disk is full.").arg(torrent->name(), msg));
});
connect(btSession, &BitTorrent::Session::loadTorrentFailed, this
, [this](const QString &error)
{
m_desktopIntegration->showNotification(tr("Error"), tr("Failed to add torrent: %1").arg(error));
});
connect(btSession, &BitTorrent::Session::torrentAdded, this
, [this](const BitTorrent::Torrent *torrent)
{
if (isTorrentAddedNotificationsEnabled())
m_desktopIntegration->showNotification(tr("Torrent added"), tr("'%1' was added.", "e.g: xxx.avi was added.").arg(torrent->name()));
});
connect(btSession, &BitTorrent::Session::torrentFinished, this
, [this](const BitTorrent::Torrent *torrent)
{
m_desktopIntegration->showNotification(tr("Download completed"), tr("'%1' has finished downloading.", "e.g: xxx.avi has finished downloading.").arg(torrent->name()));
});
connect(btSession, &BitTorrent::Session::downloadFromUrlFailed, this
, [this](const QString &url, const QString &reason)
{
m_desktopIntegration->showNotification(tr("URL download error")
, tr("Couldn't download file at URL '%1', reason: %2.").arg(url, reason));
});
disconnect(m_desktopIntegration, &DesktopIntegration::activationRequested, this, &Application::createStartupProgressDialog);
delete m_desktopIntegration->menu();
const MainWindow::State windowState = (!m_startupProgressDialog || (m_startupProgressDialog->windowState() & Qt::WindowMinimized))
? MainWindow::Minimized : MainWindow::Normal;
m_window = new MainWindow(this, windowState);
#endif // DISABLE_GUI
#ifndef DISABLE_WEBUI #ifndef DISABLE_WEBUI
m_webui = new WebUI(this); m_webui = new WebUI(this);
#ifdef DISABLE_GUI #ifdef DISABLE_GUI
if (m_webui->isErrored()) if (m_webui->isErrored())
QCoreApplication::exit(1); QCoreApplication::exit(EXIT_FAILURE);
connect(m_webui, &WebUI::fatalError, this, []() { QCoreApplication::exit(1); }); connect(m_webui, &WebUI::fatalError, this, []() { QCoreApplication::exit(EXIT_FAILURE); });
const Preferences *pref = Preferences::instance(); const Preferences *pref = Preferences::instance();
@ -698,14 +790,11 @@ int Application::exec(const QStringList &params)
processParams(params); processParams(params);
m_paramsQueue.clear(); m_paramsQueue.clear();
}); });
connect(BitTorrent::Session::instance(), &BitTorrent::Session::torrentFinished, this, &Application::torrentFinished);
connect(BitTorrent::Session::instance(), &BitTorrent::Session::allTorrentsFinished, this, &Application::allTorrentsFinished, Qt::QueuedConnection);
Net::GeoIPManager::initInstance(); if (!params.isEmpty())
TorrentFilesWatcher::initInstance(); m_paramsQueue.append(parseParams(params));
new RSS::Session; // create RSS::Session singleton return BaseApplication::exec();
new RSS::AutoDownloader; // create RSS::AutoDownloader singleton
} }
catch (const RuntimeError &err) catch (const RuntimeError &err)
{ {
@ -720,60 +809,65 @@ int Application::exec(const QStringList &params)
msgBox.move(Utils::Gui::screenCenter(&msgBox)); msgBox.move(Utils::Gui::screenCenter(&msgBox));
msgBox.exec(); msgBox.exec();
#endif #endif
return 1; return EXIT_FAILURE;
}
bool Application::isRunning()
{
return !m_instanceManager->isFirstInstance();
} }
#ifndef DISABLE_GUI #ifndef DISABLE_GUI
UIThemeManager::initInstance(); void Application::createStartupProgressDialog()
{
Q_ASSERT(m_desktopIntegration);
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
disconnect(m_desktopIntegration, &DesktopIntegration::activationRequested, this, &Application::createStartupProgressDialog);
#endif
const auto *btSession = BitTorrent::Session::instance(); m_startupProgressDialog = new QProgressDialog(tr("Loading torrents..."), tr("Exit"), 0, 100);
m_startupProgressDialog->setAttribute(Qt::WA_DeleteOnClose);
m_startupProgressDialog->setWindowFlag(Qt::WindowMinimizeButtonHint);
m_startupProgressDialog->setMinimumDuration(0); // Show dialog immediatelly by default
m_startupProgressDialog->setAutoReset(false);
m_startupProgressDialog->setAutoClose(false);
m_desktopIntegration = new DesktopIntegration(this); connect(m_startupProgressDialog, &QProgressDialog::canceled, this, []()
connect(btSession, &BitTorrent::Session::fullDiskError, this
, [this](const BitTorrent::Torrent *torrent, const QString &msg)
{
m_desktopIntegration->showNotification(tr("I/O Error", "i.e: Input/Output Error")
, tr("An I/O error occurred for torrent '%1'.\n Reason: %2"
, "e.g: An error occurred for torrent 'xxx.avi'.\n Reason: disk is full.").arg(torrent->name(), msg));
});
connect(btSession, &BitTorrent::Session::loadTorrentFailed, this
, [this](const QString &error)
{ {
m_desktopIntegration->showNotification(tr("Error"), tr("Failed to add torrent: %1").arg(error)); QApplication::exit();
}); });
connect(btSession, &BitTorrent::Session::torrentAdded, this
, [this](const BitTorrent::Torrent *torrent) connect(BitTorrent::Session::instance(), &BitTorrent::Session::startupProgressUpdated, m_startupProgressDialog, &QProgressDialog::setValue);
connect(BitTorrent::Session::instance(), &BitTorrent::Session::restored, m_startupProgressDialog, &QObject::deleteLater);
connect(m_desktopIntegration, &DesktopIntegration::activationRequested, m_startupProgressDialog, [this]()
{ {
if (isTorrentAddedNotificationsEnabled()) #ifdef Q_OS_MACOS
m_desktopIntegration->showNotification(tr("Torrent added"), tr("'%1' was added.", "e.g: xxx.avi was added.").arg(torrent->name())); if (!m_startupProgressDialog->isVisible())
});
connect(btSession, &BitTorrent::Session::torrentFinished, this
, [this](const BitTorrent::Torrent *torrent)
{ {
m_desktopIntegration->showNotification(tr("Download completed"), tr("'%1' has finished downloading.", "e.g: xxx.avi has finished downloading.").arg(torrent->name())); m_startupProgressDialog->show();
}); m_startupProgressDialog->activateWindow();
connect(btSession, &BitTorrent::Session::downloadFromUrlFailed, this m_startupProgressDialog->raise();
, [this](const QString &url, const QString &reason) }
#else
if (m_startupProgressDialog->isHidden())
{ {
m_desktopIntegration->showNotification(tr("URL download error") // Make sure the window is not minimized
, tr("Couldn't download file at URL '%1', reason: %2.").arg(url, reason)); m_startupProgressDialog->setWindowState((m_startupProgressDialog->windowState() & ~Qt::WindowMinimized) | Qt::WindowActive);
});
m_window = new MainWindow(this);
#endif // DISABLE_GUI
if (!params.isEmpty()) // Then show it
m_paramsQueue.append(parseParams(params)); m_startupProgressDialog->show();
m_startupProgressDialog->raise();
return BaseApplication::exec(); m_startupProgressDialog->activateWindow();
} }
else
bool Application::isRunning()
{ {
return !m_instanceManager->isFirstInstance(); m_startupProgressDialog->hide();
}
#endif
});
} }
#ifndef DISABLE_GUI
#ifdef Q_OS_MACOS #ifdef Q_OS_MACOS
bool Application::event(QEvent *ev) bool Application::event(QEvent *ev)
{ {

4
src/app/application.h

@ -67,6 +67,8 @@ namespace RSS
} }
#ifndef DISABLE_GUI #ifndef DISABLE_GUI
class QProgressDialog;
class DesktopIntegration; class DesktopIntegration;
class MainWindow; class MainWindow;
@ -166,6 +168,7 @@ private:
#endif #endif
#ifndef DISABLE_GUI #ifndef DISABLE_GUI
void createStartupProgressDialog();
#ifdef Q_OS_MACOS #ifdef Q_OS_MACOS
bool event(QEvent *) override; bool event(QEvent *) override;
#endif #endif
@ -203,6 +206,7 @@ private:
DesktopIntegration *m_desktopIntegration = nullptr; DesktopIntegration *m_desktopIntegration = nullptr;
MainWindow *m_window = nullptr; MainWindow *m_window = nullptr;
QProgressDialog *m_startupProgressDialog = nullptr;
#endif #endif
#ifndef DISABLE_WEBUI #ifndef DISABLE_WEBUI

11
src/base/bittorrent/session.cpp

@ -307,6 +307,8 @@ struct BitTorrent::Session::ResumeSessionContext final : public QObject
ResumeDataStorageType currentStorageType = ResumeDataStorageType::Legacy; ResumeDataStorageType currentStorageType = ResumeDataStorageType::Legacy;
QVector<LoadedResumeData> loadedResumeData; QVector<LoadedResumeData> loadedResumeData;
int processingResumeDataCount = 0; int processingResumeDataCount = 0;
int64_t totalResumeDataCount = 0;
int64_t finishedResumeDataCount = 0;
bool isLoadFinished = false; bool isLoadFinished = false;
bool isLoadedResumeDataHandlingEnqueued = false; bool isLoadedResumeDataHandlingEnqueued = false;
QSet<QString> recoveredCategories; QSet<QString> recoveredCategories;
@ -1102,6 +1104,7 @@ void Session::prepareStartup()
connect(context->startupStorage, &ResumeDataStorage::loadStarted, context connect(context->startupStorage, &ResumeDataStorage::loadStarted, context
, [this, context](const QVector<TorrentID> &torrents) , [this, context](const QVector<TorrentID> &torrents)
{ {
context->totalResumeDataCount = torrents.size();
#ifdef QBT_USES_LIBTORRENT2 #ifdef QBT_USES_LIBTORRENT2
context->indexedTorrents = QSet<TorrentID>(torrents.cbegin(), torrents.cend()); context->indexedTorrents = QSet<TorrentID>(torrents.cbegin(), torrents.cend());
#endif #endif
@ -1117,6 +1120,7 @@ void Session::prepareStartup()
connect(this, &Session::torrentsLoaded, context, [this, context](const QVector<Torrent *> &torrents) connect(this, &Session::torrentsLoaded, context, [this, context](const QVector<Torrent *> &torrents)
{ {
context->processingResumeDataCount -= torrents.count(); context->processingResumeDataCount -= torrents.count();
context->finishedResumeDataCount += torrents.count();
if (!context->isLoadedResumeDataHandlingEnqueued) if (!context->isLoadedResumeDataHandlingEnqueued)
{ {
QMetaObject::invokeMethod(this, [this, context]() { handleLoadedResumeData(context); }, Qt::QueuedConnection); QMetaObject::invokeMethod(this, [this, context]() { handleLoadedResumeData(context); }, Qt::QueuedConnection);
@ -1128,6 +1132,8 @@ void Session::prepareStartup()
m_nativeSession->post_torrent_updates(); m_nativeSession->post_torrent_updates();
m_refreshEnqueued = true; m_refreshEnqueued = true;
} }
emit startupProgressUpdated((context->finishedResumeDataCount * 100.) / context->totalResumeDataCount);
}); });
context->startupStorage->loadAll(); context->startupStorage->loadAll();
@ -1137,6 +1143,7 @@ void Session::handleLoadedResumeData(ResumeSessionContext *context)
{ {
context->isLoadedResumeDataHandlingEnqueued = false; context->isLoadedResumeDataHandlingEnqueued = false;
int count = context->processingResumeDataCount;
while (context->processingResumeDataCount < MAX_PROCESSING_RESUMEDATA_COUNT) while (context->processingResumeDataCount < MAX_PROCESSING_RESUMEDATA_COUNT)
{ {
if (context->loadedResumeData.isEmpty()) if (context->loadedResumeData.isEmpty())
@ -1161,7 +1168,10 @@ void Session::handleLoadedResumeData(ResumeSessionContext *context)
} }
processNextResumeData(context); processNextResumeData(context);
++count;
} }
context->finishedResumeDataCount += (count - context->processingResumeDataCount);
} }
void Session::processNextResumeData(ResumeSessionContext *context) void Session::processNextResumeData(ResumeSessionContext *context)
@ -1365,6 +1375,7 @@ void Session::endStartup(ResumeSessionContext *context)
} }
m_isRestored = true; m_isRestored = true;
emit startupProgressUpdated(100);
emit restored(); emit restored();
} }

1
src/base/bittorrent/session.h

@ -532,6 +532,7 @@ namespace BitTorrent
, const Path &downloadPath, const PathList &filePaths = {}) const; , const Path &downloadPath, const PathList &filePaths = {}) const;
signals: signals:
void startupProgressUpdated(int progress);
void allTorrentsFinished(); void allTorrentsFinished();
void categoryAdded(const QString &categoryName); void categoryAdded(const QString &categoryName);
void categoryRemoved(const QString &categoryName); void categoryRemoved(const QString &categoryName);

13
src/gui/mainwindow.cpp

@ -119,9 +119,8 @@ namespace
} }
} }
MainWindow::MainWindow(IGUIApplication *app, QWidget *parent) MainWindow::MainWindow(IGUIApplication *app, const State initialState)
: QMainWindow(parent) : GUIApplicationComponent(app)
, GUIApplicationComponent(app)
, m_ui(new Ui::MainWindow) , m_ui(new Ui::MainWindow)
, m_storeExecutionLogEnabled(EXECUTIONLOG_SETTINGS_KEY(u"Enabled"_qs)) , m_storeExecutionLogEnabled(EXECUTIONLOG_SETTINGS_KEY(u"Enabled"_qs))
, m_storeDownloadTrackerFavicon(SETTINGS_KEY(u"DownloadTrackerFavicon"_qs)) , m_storeDownloadTrackerFavicon(SETTINGS_KEY(u"DownloadTrackerFavicon"_qs))
@ -381,7 +380,7 @@ MainWindow::MainWindow(IGUIApplication *app, QWidget *parent)
#ifdef Q_OS_MACOS #ifdef Q_OS_MACOS
// Make sure the Window is visible if we don't have a tray icon // Make sure the Window is visible if we don't have a tray icon
if (pref->startMinimized()) if (initialState == Minimized)
{ {
showMinimized(); showMinimized();
} }
@ -394,13 +393,13 @@ MainWindow::MainWindow(IGUIApplication *app, QWidget *parent)
#else #else
if (app->desktopIntegration()->isActive()) if (app->desktopIntegration()->isActive())
{ {
if (!(pref->startMinimized() || m_uiLocked)) if ((initialState != Minimized) && !m_uiLocked)
{ {
show(); show();
activateWindow(); activateWindow();
raise(); raise();
} }
else if (pref->startMinimized()) else if (initialState == Minimized)
{ {
showMinimized(); showMinimized();
if (pref->minimizeToTray()) if (pref->minimizeToTray())
@ -417,7 +416,7 @@ MainWindow::MainWindow(IGUIApplication *app, QWidget *parent)
else else
{ {
// Make sure the Window is visible if we don't have a tray icon // Make sure the Window is visible if we don't have a tray icon
if (pref->startMinimized()) if (initialState == Minimized)
{ {
showMinimized(); showMinimized();
} }

8
src/gui/mainwindow.h

@ -79,7 +79,13 @@ class MainWindow final : public QMainWindow, public GUIApplicationComponent
Q_DISABLE_COPY_MOVE(MainWindow) Q_DISABLE_COPY_MOVE(MainWindow)
public: public:
explicit MainWindow(IGUIApplication *app, QWidget *parent = nullptr); enum State
{
Normal,
Minimized
};
explicit MainWindow(IGUIApplication *app, State initialState = Normal);
~MainWindow() override; ~MainWindow() override;
QWidget *currentTabWidget() const; QWidget *currentTabWidget() const;

Loading…
Cancel
Save