Document RefreshDeferrer

This commit is contained in:
Florian Märkl 2019-01-13 15:26:55 +01:00 committed by xarkes
parent 9ef971263f
commit bbd5ad6b38
3 changed files with 93 additions and 5 deletions

View File

@ -18,7 +18,9 @@ bool RefreshDeferrer::attemptRefresh(RefreshDeferrerParams params)
return true; return true;
} else { } else {
dirty = true; dirty = true;
acc->accumulate(params); if (acc) {
acc->accumulate(params);
}
return false; return false;
} }
} }
@ -28,8 +30,10 @@ void RefreshDeferrer::registerFor(CutterDockWidget *dockWidget)
this->dockWidget = dockWidget; this->dockWidget = dockWidget;
connect(dockWidget, &CutterDockWidget::becameVisibleToUser, this, [this]() { connect(dockWidget, &CutterDockWidget::becameVisibleToUser, this, [this]() {
if(dirty) { if(dirty) {
emit refreshNow(acc->result()); emit refreshNow(acc ? acc->result() : nullptr);
acc->clear(); if (acc) {
acc->clear();
}
dirty = false; dirty = false;
} }
}); });

View File

@ -10,6 +10,9 @@ class RefreshDeferrer;
using RefreshDeferrerParams = void *; using RefreshDeferrerParams = void *;
using RefreshDeferrerParamsResult = void *; using RefreshDeferrerParamsResult = void *;
/*!
* \brief Abstract class for accumulating params in RefreshDeferrer
*/
class RefreshDeferrerAccumulator class RefreshDeferrerAccumulator
{ {
friend class RefreshDeferrer; friend class RefreshDeferrer;
@ -18,12 +21,34 @@ public:
virtual ~RefreshDeferrerAccumulator() = default; virtual ~RefreshDeferrerAccumulator() = default;
protected: protected:
/*!
* \brief Add a new param to the accumulator
*/
virtual void accumulate(RefreshDeferrerParams params) =0; virtual void accumulate(RefreshDeferrerParams params) =0;
/*!
* \brief Ignore the incoming params. Useful for freeing if necessary.
*/
virtual void ignoreParams(RefreshDeferrerParams params) =0; virtual void ignoreParams(RefreshDeferrerParams params) =0;
/*!
* \brief Clear the current accumulator
*/
virtual void clear() =0; virtual void clear() =0;
/*!
* \brief Return the final result of the accumulation
*/
virtual RefreshDeferrerParamsResult result() =0; virtual RefreshDeferrerParamsResult result() =0;
}; };
/*!
* \brief Accumulator which simply replaces the current value by an incoming new one
* \tparam T The type of the param to store
*
* This accumulator takes the ownership of all params passed to it and deletes them automatically if not needed anymore!
*/
template<class T> template<class T>
class ReplacingRefreshDeferrerAccumulator: public RefreshDeferrerAccumulator class ReplacingRefreshDeferrerAccumulator: public RefreshDeferrerAccumulator
{ {
@ -32,6 +57,9 @@ private:
bool replaceIfNull; bool replaceIfNull;
public: public:
/*!
* \param Determines whether, if nullptr is passed, the current value should be replaced or kept.
*/
explicit ReplacingRefreshDeferrerAccumulator(bool replaceIfNull = true) explicit ReplacingRefreshDeferrerAccumulator(bool replaceIfNull = true)
: replaceIfNull(replaceIfNull) {} : replaceIfNull(replaceIfNull) {}
@ -67,6 +95,39 @@ protected:
} }
}; };
/*!
* \brief Helper class for deferred refreshing in Widgets
*
* This class can handle the logic necessary to defer the refreshing of widgets when they are not visible.
* It contains an optional RefreshDeferrerAccumulator, which can be used to accumulate incoming events while
* refreshing is deferred.
*
* Example (don't write it like this in practice, use the convenience methods in CutterDockWidget):
* ```
* // in the constructor of a widget
* this->refreshDeferrer = new RefreshDeferrer(new ReplacingRefreshDeferrerAccumulator(false), this);
* this->refreshDeferrer->registerFor(this);
* connect(this->refreshDeferrer, &RefreshDeferrer::refreshNow, this, [this](MyParam *param) {
* // We attempted a refresh some time before, but it got deferred.
* // Now the RefreshDeferrer tells us to do the refresh and gives us the accumulated param.
* this->doRefresh(*param);
* }
*
* // ...
*
* void MyWidget::doRefresh(MyParam param)
* {
* if (!this->refreshDeferrer->attemptRefresh(new MyParam(param))) {
* // We shouldn't refresh right now.
* // The RefreshDeferrer takes over the param we passed it in attemptRefresh()
* // and gives it to the ReplacingRefreshDeferrerAccumulator.
* return;
* }
* // do the actual refresh depending on param
* }
* ```
*
*/
class RefreshDeferrer : public QObject class RefreshDeferrer : public QObject
{ {
Q_OBJECT Q_OBJECT
@ -77,8 +138,11 @@ private:
bool dirty = false; bool dirty = false;
public: public:
RefreshDeferrer(RefreshDeferrerAccumulator *acc, QObject *parent = nullptr); /*!
virtual ~RefreshDeferrer(); * \param acc The accumulator (can be nullptr). The RefreshDeferrer takes the ownership!
*/
explicit RefreshDeferrer(RefreshDeferrerAccumulator *acc, QObject *parent = nullptr);
~RefreshDeferrer() override;
bool attemptRefresh(RefreshDeferrerParams params); bool attemptRefresh(RefreshDeferrerParams params);
void registerFor(CutterDockWidget *dockWidget); void registerFor(CutterDockWidget *dockWidget);

View File

@ -32,6 +32,26 @@ private:
protected: protected:
void closeEvent(QCloseEvent *event) override; void closeEvent(QCloseEvent *event) override;
/*!
* \brief Convenience method for creating and registering a RefreshDeferrer without any parameters
* \param refreshNowFunc lambda taking no parameters, called when a refresh should occur
*/
template<typename Func>
RefreshDeferrer *createRefreshDeferrer(Func refreshNowFunc)
{
auto *deferrer = new RefreshDeferrer(nullptr, this);
deferrer->registerFor(this);
connect(deferrer, &RefreshDeferrer::refreshNow, this, [refreshNowFunc](const RefreshDeferrerParamsResult) {
refreshNowFunc();
});
return deferrer;
}
/*!
* \brief Convenience method for creating and registering a RefreshDeferrer with a replacing Accumulator
* \param replaceIfNull passed to the ReplacingRefreshDeferrerAccumulator
* \param refreshNowFunc lambda taking a single parameter of type ParamResult, called when a refresh should occur
*/
template<class ParamResult, typename Func> template<class ParamResult, typename Func>
RefreshDeferrer *createReplacingRefreshDeferrer(bool replaceIfNull, Func refreshNowFunc) RefreshDeferrer *createReplacingRefreshDeferrer(bool replaceIfNull, Func refreshNowFunc)
{ {