#ifndef REFRESHDEFERRER_H #define REFRESHDEFERRER_H #include class IaitoDockWidget; class RefreshDeferrer; using RefreshDeferrerParams = void *; using RefreshDeferrerParamsResult = void *; /** * @brief Abstract class for accumulating params in RefreshDeferrer */ class RefreshDeferrerAccumulator { friend class RefreshDeferrer; public: virtual ~RefreshDeferrerAccumulator() = default; protected: /** * @brief Add a new param to the accumulator */ virtual void accumulate(RefreshDeferrerParams params) = 0; /** * @brief Ignore the incoming params. Useful for freeing if necessary. */ virtual void ignoreParams(RefreshDeferrerParams params) = 0; /** * @brief Clear the current accumulator */ virtual void clear() = 0; /** * @brief Return the final result of the accumulation */ 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 ReplacingRefreshDeferrerAccumulator : public RefreshDeferrerAccumulator { private: T *value = nullptr; bool replaceIfNull; public: /** * \param Determines whether, if nullptr is passed, the current value should * be replaced or kept. */ explicit ReplacingRefreshDeferrerAccumulator(bool replaceIfNull = true) : replaceIfNull(replaceIfNull) {} ~ReplacingRefreshDeferrerAccumulator() override { delete value; } protected: void accumulate(RefreshDeferrerParams params) override { if (!replaceIfNull && !params) { return; } delete value; value = static_cast(params); } void ignoreParams(RefreshDeferrerParams params) override { delete static_cast(params); } void clear() override { delete value; value = nullptr; } virtual RefreshDeferrerParamsResult result() override { return value; } }; /** * @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 * IaitoDockWidget): * ``` * // 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 { Q_OBJECT private: IaitoDockWidget *dockWidget = nullptr; RefreshDeferrerAccumulator *acc; bool dirty = false; public: /** * @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); void registerFor(IaitoDockWidget *dockWidget); signals: void refreshNow(const RefreshDeferrerParamsResult paramsResult); }; #endif // REFRESHDEFERRER_H