diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp index 50d4583deaa..5880b96f32a 100644 --- a/src/corelib/kernel/qobject.cpp +++ b/src/corelib/kernel/qobject.cpp @@ -529,6 +529,27 @@ void QMetaCallEvent::placeMetaCall(QObject *object) Calls \a{object}.blockSignals(true). */ +/*! + \fn QSignalBlocker::QSignalBlocker(QSignalBlocker &&other) + + Move-constructs a signal blocker from \a other. \a other will have + a no-op destructor, while repsonsibility for restoring the + QObject::signalsBlocked() state is transferred to the new object. +*/ + +/*! + \fn QSignalBlocker &QSignalBlocker::operator=(QSignalBlocker &&other) + + Move-assigns this signal blocker from \a other. \a other will have + a no-op destructor, while repsonsibility for restoring the + QObject::signalsBlocked() state is transferred to this object. + + The object's signals this signal blocker was blocking prior to + being moved to, if any, are unblocked \em except in the case where + both instances block the same object's signals and \c *this is + unblocked while \a other is not, at the time of the move. +*/ + /*! \fn QSignalBlocker::~QSignalBlocker() diff --git a/src/corelib/kernel/qobject.h b/src/corelib/kernel/qobject.h index f9239bf5756..5197d98f192 100644 --- a/src/corelib/kernel/qobject.h +++ b/src/corelib/kernel/qobject.h @@ -556,11 +556,16 @@ public: inline explicit QSignalBlocker(QObject &o); inline ~QSignalBlocker(); +#ifdef Q_COMPILER_RVALUE_REFS + inline QSignalBlocker(QSignalBlocker &&other); + inline QSignalBlocker &operator=(QSignalBlocker &&other); +#endif + inline void reblock(); inline void unblock(); private: Q_DISABLE_COPY(QSignalBlocker) - QObject * const m_o; + QObject * m_o; bool m_blocked; bool m_inhibited; }; @@ -577,6 +582,32 @@ QSignalBlocker::QSignalBlocker(QObject &o) m_inhibited(false) {} +#ifdef Q_COMPILER_RVALUE_REFS +QSignalBlocker::QSignalBlocker(QSignalBlocker &&other) + : m_o(other.m_o), + m_blocked(other.m_blocked), + m_inhibited(other.m_inhibited) +{ + other.m_o = 0; +} + +QSignalBlocker &QSignalBlocker::operator=(QSignalBlocker &&other) +{ + if (this != &other) { + // if both *this and other block the same object's signals: + // unblock *this iff our dtor would unblock, but other's wouldn't + if (m_o != other.m_o || (!m_inhibited && other.m_inhibited)) + unblock(); + m_o = other.m_o; + m_blocked = other.m_blocked; + m_inhibited = other.m_inhibited; + // disable other: + other.m_o = 0; + } + return *this; +} +#endif + QSignalBlocker::~QSignalBlocker() { if (m_o && !m_inhibited) diff --git a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp index c6fb1025ace..4776bdbbc96 100644 --- a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp +++ b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp @@ -103,6 +103,7 @@ private slots: void recursiveSignalEmission(); #endif void signalBlocking(); + void signalBlockingMoveAssignment(); void blockingQueuedConnection(); void childEvents(); void installEventFilter(); @@ -3030,6 +3031,105 @@ void tst_QObject::signalBlocking() receiver.reset(); } +void tst_QObject::signalBlockingMoveAssignment() +{ +#ifdef Q_COMPILER_RVALUE_REFS + QObject o1, o2; + + // move-assignment: both block other objects + { + QSignalBlocker b(&o1); + QVERIFY(o1.signalsBlocked()); + + QVERIFY(!o2.signalsBlocked()); + b = QSignalBlocker(&o2); + QVERIFY(!o1.signalsBlocked()); + QVERIFY(o2.signalsBlocked()); + } + + QVERIFY(!o1.signalsBlocked()); + QVERIFY(!o2.signalsBlocked()); + + // move-assignment: from inert other + { + QSignalBlocker b(&o1); + QVERIFY(o1.signalsBlocked()); + b = QSignalBlocker(0); + } + + QVERIFY(!o1.signalsBlocked()); + QVERIFY(!o2.signalsBlocked()); + + // move-assignment: to inert *this + { + QSignalBlocker b(0); + QVERIFY(!o1.signalsBlocked()); + { + QSignalBlocker inner(&o1); + QVERIFY(o1.signalsBlocked()); + b = std::move(inner); + } + QVERIFY(o1.signalsBlocked()); + } + + QVERIFY(!o1.signalsBlocked()); + QVERIFY(!o2.signalsBlocked()); + + // move-assignment: both block the same object, neither is unblocked + { + QSignalBlocker b(&o1); + QVERIFY(o1.signalsBlocked()); + { + b.unblock(); // make sure inner.m_blocked = false + QVERIFY(!o1.signalsBlocked()); + QSignalBlocker inner(&o1); + QVERIFY(o1.signalsBlocked()); + b.reblock(); + QVERIFY(o1.signalsBlocked()); + b = std::move(inner); + } + QVERIFY(o1.signalsBlocked()); + } + + QVERIFY(!o1.signalsBlocked()); + QVERIFY(!o2.signalsBlocked()); + + // move-assignment: both block the same object, but *this is unblocked + { + QSignalBlocker b(&o1); + QVERIFY(o1.signalsBlocked()); + b.unblock(); + QVERIFY(!o1.signalsBlocked()); + b = QSignalBlocker(&o1); + QVERIFY(o1.signalsBlocked()); + } + + QVERIFY(!o1.signalsBlocked()); + QVERIFY(!o2.signalsBlocked()); + + // move-assignment: both block the same object, but other is unblocked + { + QSignalBlocker b(&o1); + { + QVERIFY(o1.signalsBlocked()); + QSignalBlocker inner(&o1); + QVERIFY(o1.signalsBlocked()); + inner.unblock(); + QVERIFY(o1.signalsBlocked()); + b = std::move(inner); + QVERIFY(!o1.signalsBlocked()); + } + QVERIFY(!o1.signalsBlocked()); + } + + QVERIFY(!o1.signalsBlocked()); + QVERIFY(!o2.signalsBlocked()); + +#else + QSKIP("This compiler is not in C++11 mode or doesn't support move semantics"); +#endif // Q_COMPILER_RVALUE_REFS +} + void tst_QObject::blockingQueuedConnection() { {