summaryrefslogtreecommitdiff
path: root/Source/Core/DolphinQt/QtUtils/ParallelProgressDialog.h
blob: 393a1bbabf2d962076770386bc5fad8322d43ae2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
// Copyright 2020 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <utility>

#include <QObject>
#include <QProgressDialog>
#include <QString>

#include "Common/Flag.h"

class ParallelProgressDialog final : public QObject
{
  Q_OBJECT

public:
  ParallelProgressDialog(const ParallelProgressDialog&) = delete;
  ParallelProgressDialog& operator=(const ParallelProgressDialog&) = delete;
  ParallelProgressDialog(ParallelProgressDialog&&) = delete;
  ParallelProgressDialog& operator=(ParallelProgressDialog&&) = delete;

  // Only use this from the main thread
  template <typename... Args>
  ParallelProgressDialog(Args&&... args) : m_dialog{std::forward<Args>(args)...}
  {
    setParent(m_dialog.parent());
    ConnectSignalsAndSlots();
  }

  // Only use this from the main thread
  QProgressDialog* GetRaw() { return &m_dialog; }

  // All of the following can be called from any thread
  void Cancel() { emit CancelSignal(); }
  void Reset() { emit ResetSignal(); }
  void SetCancelButtonText(const QString& text) { emit SetCancelButtonTextSignal(text); }
  void SetLabelText(const QString& text) { emit SetLabelTextSignal(text); }
  void SetMaximum(int maximum) { emit SetMaximumSignal(maximum); }
  void SetMinimum(int minimum) { emit SetMinimumSignal(minimum); }
  void SetMinimumDuration(int ms) { emit SetMinimumDurationSignal(ms); }
  void SetRange(int minimum, int maximum) { emit SetRangeSignal(minimum, maximum); }
  void SetValue(int progress) { emit SetValueSignal(progress); }

  // Can be called from any thread
  bool WasCanceled() const { return m_was_cancelled.IsSet(); }

signals:
  void CancelSignal();
  void ResetSignal();
  void SetCancelButtonTextSignal(const QString& cancel_button_text);
  void SetLabelTextSignal(const QString& text);
  void SetMaximumSignal(int maximum);
  void SetMinimumSignal(int minimum);
  void SetMinimumDurationSignal(int ms);
  void SetRangeSignal(int minimum, int maximum);
  void SetValueSignal(int progress);

  void Canceled();
  void Finished(int result);

private slots:
  void OnCancelled() { m_was_cancelled.Set(); }

  void SetValueSlot(int progress)
  {
    // Normally we would've been able to just call setValue instead of having this wrapper
    // around it, but due to the https://bugreports.qt.io/browse/QTBUG-10561 stack overflow,
    // we can't. Short summary of the stack overflow: setValue calls processEvents,
    // which calls SetValueSlot if there is another SetValueSlot event in the queue,
    // which would call setValue if it wasn't for the check below, and so on.

    m_last_received_progress = progress;

    if (m_is_setting_value)
      return;

    m_is_setting_value = true;

    int last_set_progress;
    do
    {
      last_set_progress = m_last_received_progress;
      m_dialog.setValue(m_last_received_progress);
    } while (m_last_received_progress != last_set_progress);

    m_is_setting_value = false;
  }

private:
  template <typename Func1, typename Func2>
  void ConnectSignal(Func1 signal, Func2 slot)
  {
    QObject::connect(this, signal, &m_dialog, slot);
  }

  template <typename Func1, typename Func2>
  void ConnectSlot(Func1 signal, Func2 slot)
  {
    QObject::connect(&m_dialog, signal, this, slot);
  }

  void ConnectSignalsAndSlots()
  {
    ConnectSignal(&ParallelProgressDialog::CancelSignal, &QProgressDialog::cancel);
    ConnectSignal(&ParallelProgressDialog::ResetSignal, &QProgressDialog::reset);
    ConnectSignal(&ParallelProgressDialog::SetCancelButtonTextSignal,
                  &QProgressDialog::setCancelButtonText);
    ConnectSignal(&ParallelProgressDialog::SetLabelTextSignal, &QProgressDialog::setLabelText);
    ConnectSignal(&ParallelProgressDialog::SetMaximumSignal, &QProgressDialog::setMaximum);
    ConnectSignal(&ParallelProgressDialog::SetMinimumSignal, &QProgressDialog::setMinimum);
    ConnectSignal(&ParallelProgressDialog::SetMinimumDurationSignal,
                  &QProgressDialog::setMinimumDuration);
    ConnectSignal(&ParallelProgressDialog::SetRangeSignal, &QProgressDialog::setRange);

    QObject::connect(this, &ParallelProgressDialog::SetValueSignal, this,
                     &ParallelProgressDialog::SetValueSlot);

    ConnectSlot(&QProgressDialog::canceled, &ParallelProgressDialog::OnCancelled);

    ConnectSlot(&QProgressDialog::canceled, &ParallelProgressDialog::Canceled);
    ConnectSlot(&QProgressDialog::finished, &ParallelProgressDialog::Finished);
  }

  QProgressDialog m_dialog;
  Common::Flag m_was_cancelled;
  int m_last_received_progress = 0;
  bool m_is_setting_value = false;
};