mirror of
				https://github.com/ciphervance/supercell-wx.git
				synced 2025-10-31 03:50:05 +00:00 
			
		
		
		
	Add generic progress dialog, and derived download dialog
This commit is contained in:
		
							parent
							
								
									4ac2626b65
								
							
						
					
					
						commit
						2f397106f9
					
				
					 6 changed files with 374 additions and 0 deletions
				
			
		
							
								
								
									
										134
									
								
								scwx-qt/source/scwx/qt/ui/download_dialog.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										134
									
								
								scwx-qt/source/scwx/qt/ui/download_dialog.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,134 @@ | |||
| #include <scwx/qt/ui/download_dialog.hpp> | ||||
| #include <scwx/util/strings.hpp> | ||||
| 
 | ||||
| #include <boost/timer/timer.hpp> | ||||
| #include <fmt/chrono.h> | ||||
| #include <fmt/format.h> | ||||
| #include <QDialogButtonBox> | ||||
| #include <QPushButton> | ||||
| #include <QTimer> | ||||
| 
 | ||||
| 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<Impl>(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<double>(downloadedBytes) / totalBytes : | ||||
|                            0.0; | ||||
|    const int progressValue = static_cast<int>(percentComplete * 100.0); | ||||
| 
 | ||||
|    self_->SetValue(progressValue); | ||||
| 
 | ||||
|    const std::chrono::seconds timeRemaining = | ||||
|       (percentComplete > 0.0) ? | ||||
|          std::chrono::duration_cast<std::chrono::seconds>( | ||||
|             elapsed / percentComplete - elapsed) : | ||||
|          0s; | ||||
|    const std::chrono::hours hoursRemaining = | ||||
|       std::chrono::duration_cast<std::chrono::hours>(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
 | ||||
							
								
								
									
										38
									
								
								scwx-qt/source/scwx/qt/ui/download_dialog.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								scwx-qt/source/scwx/qt/ui/download_dialog.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,38 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <scwx/qt/ui/progress_dialog.hpp> | ||||
| 
 | ||||
| #include <cstddef> | ||||
| 
 | ||||
| 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<Impl> p; | ||||
| }; | ||||
| 
 | ||||
| } // namespace ui
 | ||||
| } // namespace qt
 | ||||
| } // namespace scwx
 | ||||
							
								
								
									
										66
									
								
								scwx-qt/source/scwx/qt/ui/progress_dialog.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								scwx-qt/source/scwx/qt/ui/progress_dialog.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -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<Impl>()}, 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
 | ||||
							
								
								
									
										46
									
								
								scwx-qt/source/scwx/qt/ui/progress_dialog.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								scwx-qt/source/scwx/qt/ui/progress_dialog.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,46 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <QDialog> | ||||
| 
 | ||||
| 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<Impl> p; | ||||
|    Ui::ProgressDialog*   ui; | ||||
| }; | ||||
| 
 | ||||
| } // namespace ui
 | ||||
| } // namespace qt
 | ||||
| } // namespace scwx
 | ||||
							
								
								
									
										85
									
								
								scwx-qt/source/scwx/qt/ui/progress_dialog.ui
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								scwx-qt/source/scwx/qt/ui/progress_dialog.ui
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,85 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <ui version="4.0"> | ||||
|  <class>ProgressDialog</class> | ||||
|  <widget class="QDialog" name="ProgressDialog"> | ||||
|   <property name="geometry"> | ||||
|    <rect> | ||||
|     <x>0</x> | ||||
|     <y>0</y> | ||||
|     <width>394</width> | ||||
|     <height>116</height> | ||||
|    </rect> | ||||
|   </property> | ||||
|   <property name="windowTitle"> | ||||
|    <string>Dialog</string> | ||||
|   </property> | ||||
|   <layout class="QVBoxLayout" name="verticalLayout"> | ||||
|    <item> | ||||
|     <widget class="QLabel" name="topLabel"> | ||||
|      <property name="text"> | ||||
|       <string>Downloading supercell-wx-v0.4.4-windows-x64.msi...</string> | ||||
|      </property> | ||||
|     </widget> | ||||
|    </item> | ||||
|    <item> | ||||
|     <widget class="QProgressBar" name="progressBar"> | ||||
|      <property name="value"> | ||||
|       <number>24</number> | ||||
|      </property> | ||||
|     </widget> | ||||
|    </item> | ||||
|    <item> | ||||
|     <widget class="QLabel" name="bottomLabel"> | ||||
|      <property name="text"> | ||||
|       <string>25.3 MB of 69.1 MB downloaded (00:00:04 remaining)</string> | ||||
|      </property> | ||||
|     </widget> | ||||
|    </item> | ||||
|    <item> | ||||
|     <widget class="QDialogButtonBox" name="buttonBox"> | ||||
|      <property name="orientation"> | ||||
|       <enum>Qt::Horizontal</enum> | ||||
|      </property> | ||||
|      <property name="standardButtons"> | ||||
|       <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> | ||||
|      </property> | ||||
|     </widget> | ||||
|    </item> | ||||
|   </layout> | ||||
|  </widget> | ||||
|  <resources/> | ||||
|  <connections> | ||||
|   <connection> | ||||
|    <sender>buttonBox</sender> | ||||
|    <signal>accepted()</signal> | ||||
|    <receiver>ProgressDialog</receiver> | ||||
|    <slot>accept()</slot> | ||||
|    <hints> | ||||
|     <hint type="sourcelabel"> | ||||
|      <x>248</x> | ||||
|      <y>254</y> | ||||
|     </hint> | ||||
|     <hint type="destinationlabel"> | ||||
|      <x>157</x> | ||||
|      <y>274</y> | ||||
|     </hint> | ||||
|    </hints> | ||||
|   </connection> | ||||
|   <connection> | ||||
|    <sender>buttonBox</sender> | ||||
|    <signal>rejected()</signal> | ||||
|    <receiver>ProgressDialog</receiver> | ||||
|    <slot>reject()</slot> | ||||
|    <hints> | ||||
|     <hint type="sourcelabel"> | ||||
|      <x>316</x> | ||||
|      <y>260</y> | ||||
|     </hint> | ||||
|     <hint type="destinationlabel"> | ||||
|      <x>286</x> | ||||
|      <y>274</y> | ||||
|     </hint> | ||||
|    </hints> | ||||
|   </connection> | ||||
|  </connections> | ||||
| </ui> | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Dan Paulat
						Dan Paulat