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 */