diff --git a/scwx-qt/scwx-qt.cmake b/scwx-qt/scwx-qt.cmake index 0468e4f6..eb94efd3 100644 --- a/scwx-qt/scwx-qt.cmake +++ b/scwx-qt/scwx-qt.cmake @@ -221,6 +221,7 @@ set(HDR_UI source/scwx/qt/ui/about_dialog.hpp source/scwx/qt/ui/animation_dock_widget.hpp source/scwx/qt/ui/collapsible_group.hpp source/scwx/qt/ui/county_dialog.hpp + source/scwx/qt/ui/download_dialog.hpp source/scwx/qt/ui/flow_layout.hpp source/scwx/qt/ui/imgui_debug_dialog.hpp source/scwx/qt/ui/imgui_debug_widget.hpp @@ -232,6 +233,7 @@ set(HDR_UI source/scwx/qt/ui/about_dialog.hpp source/scwx/qt/ui/open_url_dialog.hpp source/scwx/qt/ui/placefile_dialog.hpp source/scwx/qt/ui/placefile_settings_widget.hpp + source/scwx/qt/ui/progress_dialog.hpp source/scwx/qt/ui/radar_site_dialog.hpp source/scwx/qt/ui/settings_dialog.hpp source/scwx/qt/ui/update_dialog.hpp) @@ -241,6 +243,7 @@ set(SRC_UI source/scwx/qt/ui/about_dialog.cpp source/scwx/qt/ui/animation_dock_widget.cpp source/scwx/qt/ui/collapsible_group.cpp source/scwx/qt/ui/county_dialog.cpp + source/scwx/qt/ui/download_dialog.cpp source/scwx/qt/ui/flow_layout.cpp source/scwx/qt/ui/imgui_debug_dialog.cpp source/scwx/qt/ui/imgui_debug_widget.cpp @@ -252,6 +255,7 @@ set(SRC_UI source/scwx/qt/ui/about_dialog.cpp source/scwx/qt/ui/open_url_dialog.cpp source/scwx/qt/ui/placefile_dialog.cpp source/scwx/qt/ui/placefile_settings_widget.cpp + source/scwx/qt/ui/progress_dialog.cpp source/scwx/qt/ui/radar_site_dialog.cpp source/scwx/qt/ui/settings_dialog.cpp source/scwx/qt/ui/update_dialog.cpp) @@ -266,6 +270,7 @@ set(UI_UI source/scwx/qt/ui/about_dialog.ui source/scwx/qt/ui/open_url_dialog.ui source/scwx/qt/ui/placefile_dialog.ui source/scwx/qt/ui/placefile_settings_widget.ui + source/scwx/qt/ui/progress_dialog.ui source/scwx/qt/ui/radar_site_dialog.ui source/scwx/qt/ui/settings_dialog.ui source/scwx/qt/ui/update_dialog.ui) diff --git a/scwx-qt/source/scwx/qt/ui/download_dialog.cpp b/scwx-qt/source/scwx/qt/ui/download_dialog.cpp new file mode 100644 index 00000000..30763f4e --- /dev/null +++ b/scwx-qt/source/scwx/qt/ui/download_dialog.cpp @@ -0,0 +1,134 @@ +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace scwx +{ +namespace qt +{ +namespace ui +{ + +class DownloadDialog::Impl +{ +public: + explicit Impl(DownloadDialog* self) : self_ {self} + { + updateTimer_.setSingleShot(true); + updateTimer_.setInterval(0); + + QObject::connect(&updateTimer_, + &QTimer::timeout, + self_, + [this]() { UpdateProgress(); }); + }; + ~Impl() = default; + + void UpdateProgress(); + + DownloadDialog* self_; + + boost::timer::cpu_timer timer_ {}; + QTimer updateTimer_ {}; + + std::ptrdiff_t downloadedBytes_ {}; + std::ptrdiff_t totalBytes_ {}; +}; + +DownloadDialog::DownloadDialog(QWidget* parent) : + ProgressDialog(parent), p {std::make_unique(this)} +{ + auto buttonBox = button_box(); + buttonBox->setStandardButtons(QDialogButtonBox::StandardButton::Ok | + QDialogButtonBox::StandardButton::Cancel); + buttonBox->button(QDialogButtonBox::StandardButton::Ok) + ->setText("Install Now"); + + SetRange(0, 100); +} + +DownloadDialog::~DownloadDialog() {} + +void DownloadDialog::set_filename(const std::string& filename) +{ + QString label = tr("Downloading %1...").arg(filename.c_str()); + SetTopLabelText(label); +} + +void DownloadDialog::StartDownload() +{ + // Hide the OK button until the download is finished + button_box() + ->button(QDialogButtonBox::StandardButton::Ok) + ->setVisible(false); + + SetBottomLabelText(tr("Waiting for download to begin...")); + p->timer_.start(); + show(); +} + +void DownloadDialog::UpdateProgress(std::ptrdiff_t downloadedBytes, + std::ptrdiff_t totalBytes) +{ + p->downloadedBytes_ = downloadedBytes; + p->totalBytes_ = totalBytes; + + // Use a one-shot timer to trigger an update, preventing multiple updates per + // frame + p->updateTimer_.start(); +} + +void DownloadDialog::FinishDownload() +{ + button_box()->button(QDialogButtonBox::StandardButton::Ok)->setVisible(true); +} + +void DownloadDialog::CancelDownload() +{ + SetValue(0); + SetBottomLabelText(tr("Error occurred while downloading")); +} + +void DownloadDialog::Impl::UpdateProgress() +{ + using namespace std::chrono_literals; + + const std::ptrdiff_t downloadedBytes = downloadedBytes_; + const std::ptrdiff_t totalBytes = totalBytes_; + + const std::chrono::nanoseconds elapsed {timer_.elapsed().wall}; + + const double percentComplete = + (totalBytes > 0.0) ? static_cast(downloadedBytes) / totalBytes : + 0.0; + const int progressValue = static_cast(percentComplete * 100.0); + + self_->SetValue(progressValue); + + const std::chrono::seconds timeRemaining = + (percentComplete > 0.0) ? + std::chrono::duration_cast( + elapsed / percentComplete - elapsed) : + 0s; + const std::chrono::hours hoursRemaining = + std::chrono::duration_cast(timeRemaining); + + const std::string progressText = + fmt::format("{} of {} downloaded ({}:{:%M:%S} remaining)", + util::BytesToString(downloadedBytes), + util::BytesToString(totalBytes), + hoursRemaining.count(), + timeRemaining); + + self_->SetBottomLabelText(QString::fromStdString(progressText)); +} + +} // namespace ui +} // namespace qt +} // namespace scwx diff --git a/scwx-qt/source/scwx/qt/ui/download_dialog.hpp b/scwx-qt/source/scwx/qt/ui/download_dialog.hpp new file mode 100644 index 00000000..ecf4a0c0 --- /dev/null +++ b/scwx-qt/source/scwx/qt/ui/download_dialog.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include + +#include + +namespace scwx +{ +namespace qt +{ +namespace ui +{ +class DownloadDialog : public ProgressDialog +{ + Q_OBJECT + Q_DISABLE_COPY_MOVE(DownloadDialog) + +public: + explicit DownloadDialog(QWidget* parent = nullptr); + ~DownloadDialog(); + + void set_filename(const std::string& filename); + +public slots: + void StartDownload(); + void UpdateProgress(std::ptrdiff_t downloadedBytes, + std::ptrdiff_t totalBytes); + void FinishDownload(); + void CancelDownload(); + +private: + class Impl; + std::unique_ptr p; +}; + +} // namespace ui +} // namespace qt +} // namespace scwx diff --git a/scwx-qt/source/scwx/qt/ui/progress_dialog.cpp b/scwx-qt/source/scwx/qt/ui/progress_dialog.cpp new file mode 100644 index 00000000..70486805 --- /dev/null +++ b/scwx-qt/source/scwx/qt/ui/progress_dialog.cpp @@ -0,0 +1,66 @@ +#include "progress_dialog.hpp" +#include "ui_progress_dialog.h" + +namespace scwx +{ +namespace qt +{ +namespace ui +{ + +class ProgressDialog::Impl +{ +public: + explicit Impl() = default; + ~Impl() = default; +}; + +ProgressDialog::ProgressDialog(QWidget* parent) : + QDialog(parent), p {std::make_unique()}, ui(new Ui::ProgressDialog) +{ + ui->setupUi(this); +} + +ProgressDialog::~ProgressDialog() +{ + delete ui; +} + +QDialogButtonBox* ProgressDialog::button_box() const +{ + return ui->buttonBox; +} + +void ProgressDialog::SetTopLabelText(const QString& text) +{ + ui->topLabel->setText(text); +} + +void ProgressDialog::SetBottomLabelText(const QString& text) +{ + ui->bottomLabel->setText(text); +} + +void ProgressDialog::SetMinimum(int minimum) +{ + ui->progressBar->setMinimum(minimum); +} + +void ProgressDialog::SetMaximum(int maximum) +{ + ui->progressBar->setMaximum(maximum); +} + +void ProgressDialog::SetRange(int minimum, int maximum) +{ + ui->progressBar->setRange(minimum, maximum); +} + +void ProgressDialog::SetValue(int value) +{ + ui->progressBar->setValue(value); +} + +} // namespace ui +} // namespace qt +} // namespace scwx diff --git a/scwx-qt/source/scwx/qt/ui/progress_dialog.hpp b/scwx-qt/source/scwx/qt/ui/progress_dialog.hpp new file mode 100644 index 00000000..71e4ac26 --- /dev/null +++ b/scwx-qt/source/scwx/qt/ui/progress_dialog.hpp @@ -0,0 +1,46 @@ +#pragma once + +#include + +class QDialogButtonBox; + +namespace Ui +{ +class ProgressDialog; +} + +namespace scwx +{ +namespace qt +{ +namespace ui +{ +class ProgressDialog : public QDialog +{ + Q_OBJECT + Q_DISABLE_COPY_MOVE(ProgressDialog) + +public: + explicit ProgressDialog(QWidget* parent = nullptr); + ~ProgressDialog(); + +protected: + QDialogButtonBox* button_box() const; + +public slots: + void SetTopLabelText(const QString& text); + void SetBottomLabelText(const QString& text); + void SetMinimum(int minimum); + void SetMaximum(int maximum); + void SetRange(int minimum, int maximum); + void SetValue(int value); + +private: + class Impl; + std::unique_ptr p; + Ui::ProgressDialog* ui; +}; + +} // namespace ui +} // namespace qt +} // namespace scwx diff --git a/scwx-qt/source/scwx/qt/ui/progress_dialog.ui b/scwx-qt/source/scwx/qt/ui/progress_dialog.ui new file mode 100644 index 00000000..b9b091b3 --- /dev/null +++ b/scwx-qt/source/scwx/qt/ui/progress_dialog.ui @@ -0,0 +1,85 @@ + + + ProgressDialog + + + + 0 + 0 + 394 + 116 + + + + Dialog + + + + + + Downloading supercell-wx-v0.4.4-windows-x64.msi... + + + + + + + 24 + + + + + + + 25.3 MB of 69.1 MB downloaded (00:00:04 remaining) + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + ProgressDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + ProgressDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + +