File indexing completed on 2024-05-12 15:56:59

0001 /*
0002  *  SPDX-FileCopyrightText: 2013 Dmitry Kazakov <dimula73@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #ifndef __KIS_SIGNAL_COMPRESSOR_H
0008 #define __KIS_SIGNAL_COMPRESSOR_H
0009 
0010 #include <QObject>
0011 #include "kritaglobal_export.h"
0012 
0013 #include <QElapsedTimer>
0014 #include <functional>
0015 
0016 class QTimer;
0017 
0018 /**
0019  * Sets a timer to delay or throttle activation of a Qt slot. One example of
0020  * where this is used is to limit the amount of expensive redraw activity on the
0021  * canvas.
0022  *
0023  * There are four behaviors to choose from.
0024  *
0025  * POSTPONE resets the timer after each call. Therefore if the calls are made
0026  * quickly enough, the timer will never be activated.
0027  *
0028  * FIRST_ACTIVE_POSTPONE_NEXT emits the first signal and postpones all
0029  * the other actions like in POSTPONE. This mode is
0030  * used e.g. in move/remove layer functionality. If you remove a
0031  * single layer, you'll see the result immediately. But if you want to
0032  * remove multiple layers, you should wait until all the actions are
0033  * finished.
0034  *
0035  * FIRST_ACTIVE emits the timeout() event immediately and sets a timer of
0036  * duration \p delay. If the compressor is triggered during this time, it will
0037  * wait until the end of the delay period to fire the signal. Further events are
0038  * ignored until the timer elapses. Think of it as a queue with size 1, and
0039  * where the leading element is popped every \p delay ms.
0040  *
0041  * FIRST_INACTIVE emits the timeout() event at the end of a timer of duration \p
0042  * delay ms. The compressor becomes inactive and all events are ignored until
0043  * the timer has elapsed.
0044  *
0045  * The current implementation allows the timeout() to be delayed by up to 2 times
0046  * \p delay in certain situations (for details see cpp file).
0047  */
0048 class KRITAGLOBAL_EXPORT KisSignalCompressor : public QObject
0049 {
0050     Q_OBJECT
0051 
0052 public:
0053     enum Mode {
0054         POSTPONE, /* Calling start() resets the timer to \p delay ms */
0055         FIRST_ACTIVE_POSTPONE_NEXT, /* emits the first signal and postpones all the next ones */
0056         FIRST_ACTIVE, /* Emit timeout() signal immediately. Throttle further timeout() to rate of one per \p delay ms */
0057         FIRST_INACTIVE, /* Set a timer \p delay ms, emit timeout() when it elapses. Ignore all events meanwhile. */
0058         UNDEFINED /* KisSignalCompressor is created without an explicit mode */
0059     };
0060 
0061     enum SlowHandlerMode {
0062         PRECISE_INTERVAL, /* Interval of timeout is forced to \p delay ms, whatever time the handler of timeout() takes */
0063         ADDITIVE_INTERVAL /* When the handler of timeout() is slow, the timeout delay is increased to the (delay + handler_time) */
0064     };
0065 
0066 public:
0067     KisSignalCompressor();
0068     KisSignalCompressor(int delay, Mode mode, QObject *parent = 0);
0069     KisSignalCompressor(int delay, Mode mode, SlowHandlerMode slowHandlerMode, QObject *parent = 0);
0070     bool isActive() const;
0071     void setMode(Mode mode);
0072 
0073     int delay() const;
0074 
0075     void setDelay(std::function<bool()> idleCallback, int idleDelay, int timeout);
0076 
0077 public Q_SLOTS:
0078     void setDelay(int delay);
0079     void start();
0080     void stop();
0081 
0082 private Q_SLOTS:
0083     void slotTimerExpired();
0084 
0085 Q_SIGNALS:
0086     void timeout();
0087 
0088 private:
0089     bool tryEmitOnTick(bool isFromTimer);
0090     bool tryEmitSignalSafely();
0091     void setDelayImpl(int delay);
0092 
0093 private:
0094     QTimer *m_timer = 0;
0095     Mode m_mode = UNDEFINED;
0096     SlowHandlerMode m_slowHandlerMode = PRECISE_INTERVAL;
0097     bool m_signalsPending = false;
0098     QElapsedTimer m_lastEmittedTimer;
0099     int m_isEmitting = 0;
0100     int m_timeout = 0;
0101     std::function<bool()> m_idleCallback;
0102 };
0103 
0104 #endif /* __KIS_SIGNAL_COMPRESSOR_H */