1 
2 #ifndef REFRESHDEFERRER_H
3 #define REFRESHDEFERRER_H
4 
5 #include <QObject>
6 
7 class CutterDockWidget;
8 class RefreshDeferrer;
9 
10 using RefreshDeferrerParams = void *;
11 using RefreshDeferrerParamsResult = void *;
12 
13 /**
14  * @brief Abstract class for accumulating params in RefreshDeferrer
15  */
16 class RefreshDeferrerAccumulator
17 {
18     friend class RefreshDeferrer;
19 
20 public:
21     virtual ~RefreshDeferrerAccumulator() = default;
22 
23 protected:
24     /**
25      * @brief Add a new param to the accumulator
26      */
27     virtual void accumulate(RefreshDeferrerParams params) =0;
28 
29     /**
30      * @brief Ignore the incoming params. Useful for freeing if necessary.
31      */
32     virtual void ignoreParams(RefreshDeferrerParams params) =0;
33 
34     /**
35      * @brief Clear the current accumulator
36      */
37     virtual void clear() =0;
38 
39     /**
40      * @brief Return the final result of the accumulation
41      */
42     virtual RefreshDeferrerParamsResult result() =0;
43 };
44 
45 
46 /**
47  * @brief Accumulator which simply replaces the current value by an incoming new one
48  * @tparam T The type of the param to store
49  *
50  * This accumulator takes the ownership of all params passed to it and deletes them automatically if not needed anymore!
51  */
52 template<class T>
53 class ReplacingRefreshDeferrerAccumulator: public RefreshDeferrerAccumulator
54 {
55 private:
56     T *value = nullptr;
57     bool replaceIfNull;
58 
59 public:
60     /**
61      * \param Determines whether, if nullptr is passed, the current value should be replaced or kept.
62      */
63     explicit ReplacingRefreshDeferrerAccumulator(bool replaceIfNull = true)
replaceIfNull(replaceIfNull)64         : replaceIfNull(replaceIfNull) {}
65 
~ReplacingRefreshDeferrerAccumulator()66     ~ReplacingRefreshDeferrerAccumulator() override
67     {
68         delete value;
69     }
70 
71 protected:
accumulate(RefreshDeferrerParams params)72     void accumulate(RefreshDeferrerParams params) override
73     {
74         if (!replaceIfNull && !params) {
75             return;
76         }
77         delete value;
78         value = static_cast<T *>(params);
79     }
80 
ignoreParams(RefreshDeferrerParams params)81     void ignoreParams(RefreshDeferrerParams params) override
82     {
83         delete static_cast<T *>(params);
84     }
85 
clear()86     void clear() override
87     {
88         delete value;
89         value = nullptr;
90     }
91 
result()92     virtual RefreshDeferrerParamsResult result() override
93     {
94         return value;
95     }
96 };
97 
98 /**
99  * @brief Helper class for deferred refreshing in Widgets
100  *
101  * This class can handle the logic necessary to defer the refreshing of widgets when they are not visible.
102  * It contains an optional RefreshDeferrerAccumulator, which can be used to accumulate incoming events while
103  * refreshing is deferred.
104  *
105  * Example (don't write it like this in practice, use the convenience methods in CutterDockWidget):
106  * ```
107  * // in the constructor of a widget
108  * this->refreshDeferrer = new RefreshDeferrer(new ReplacingRefreshDeferrerAccumulator(false), this);
109  * this->refreshDeferrer->registerFor(this);
110  * connect(this->refreshDeferrer, &RefreshDeferrer::refreshNow, this, [this](MyParam *param) {
111  *      // We attempted a refresh some time before, but it got deferred.
112  *      // Now the RefreshDeferrer tells us to do the refresh and gives us the accumulated param.
113  *      this->doRefresh(*param);
114  * }
115  *
116  * // ...
117  *
118  * void MyWidget::doRefresh(MyParam param)
119  * {
120  *      if (!this->refreshDeferrer->attemptRefresh(new MyParam(param))) {
121  *          // We shouldn't refresh right now.
122  *          // The RefreshDeferrer takes over the param we passed it in attemptRefresh()
123  *          // and gives it to the ReplacingRefreshDeferrerAccumulator.
124  *          return;
125  *      }
126  *      // do the actual refresh depending on param
127  * }
128  * ```
129  *
130  */
131 class RefreshDeferrer : public QObject
132 {
133     Q_OBJECT
134 
135 private:
136     CutterDockWidget *dockWidget = nullptr;
137     RefreshDeferrerAccumulator *acc;
138     bool dirty = false;
139 
140 public:
141     /**
142      * @param acc The accumulator (can be nullptr). The RefreshDeferrer takes the ownership!
143      */
144     explicit RefreshDeferrer(RefreshDeferrerAccumulator *acc, QObject *parent = nullptr);
145     ~RefreshDeferrer() override;
146 
147     bool attemptRefresh(RefreshDeferrerParams params);
148     void registerFor(CutterDockWidget *dockWidget);
149 
150 signals:
151     void refreshNow(const RefreshDeferrerParamsResult paramsResult);
152 };
153 
154 #endif //REFRESHDEFERRER_H
155