QPainterStateGuard: make movable, add test coverage

There's no real reason not to be able to move a state guard.

Since a moved-from QPainterStateGuard must not have a restore-
level count, we have to implement the move-constructor explicily.
Add a test case, which verifies that we don't end up with an
un-balanced save/restore count, and that verifies that we would
trigger the Q_ASSERT if we do.

Address comment from header review,
amends 9ecf47a8a8d11227ecf192246d7df7c2c4dc9105.

Task-number: QTBUG-132090
Change-Id: I1db135bf48c0fa0a7bac4fdae7b7263c356b5eb6
Reviewed-by: Juha Vuolle <juha.vuolle@qt.io>
(cherry picked from commit ec3a5f4994a2bafc65fa8e01fb0861219580f622)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
Volker Hilsheimer 2024-12-17 11:34:54 +01:00 committed by Qt Cherry-pick Bot
parent 55a46ec005
commit 43ab4d5c7b
5 changed files with 120 additions and 1 deletions

View File

@ -37,6 +37,25 @@ QT_BEGIN_NAMESPACE
QPainter's state.
*/
/*!
\fn QPainterStateGuard::QPainterStateGuard(QPainterStateGuard &&other)
Move-constructs a painter state guard from \a other.
*/
/*!
\fn QPainterStateGuard &QPainterStateGuard::operator=(QPainterStateGuard &&other)
Move-assigns \a other to this painter state guard.
*/
/*!
\fn void QPainterStateGuard::swap(QPainterStateGuard &other)
Swaps the \a other with this painter state guard. This operation is very
fast and never fails.
*/
/*!
\fn QPainterStateGuard::~QPainterStateGuard()
Destroys the QPainterStateGuard instance and calls restore() as often as save()

View File

@ -11,13 +11,24 @@ QT_BEGIN_NAMESPACE
class QPainterStateGuard
{
Q_DISABLE_COPY_MOVE(QPainterStateGuard)
Q_DISABLE_COPY(QPainterStateGuard)
public:
enum class InitialState : quint8 {
Save,
NoSave,
};
QPainterStateGuard(QPainterStateGuard &&other) noexcept
: m_painter(std::exchange(other.m_painter, nullptr))
, m_level(std::exchange(other.m_level, 0))
{}
QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QPainterStateGuard)
void swap(QPainterStateGuard &other) noexcept
{
qt_ptr_swap(m_painter, other.m_painter);
std::swap(m_level, other.m_level);
}
Q_NODISCARD_CTOR
explicit QPainterStateGuard(QPainter *painter, InitialState state = InitialState::Save)
: m_painter(painter)

View File

@ -10,6 +10,7 @@ add_subdirectory(qpagelayout)
add_subdirectory(qpageranges)
add_subdirectory(qpagesize)
add_subdirectory(qpainter)
add_subdirectory(qpainterstateguard)
if (QT_FEATURE_pdf)
add_subdirectory(qpdfwriter)
endif()

View File

@ -0,0 +1,19 @@
# Copyright (C) 2024 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qpainterstateguard Test:
#####################################################################
if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
project(tst_qpainterstateguard LANGUAGES CXX)
find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
endif()
qt_internal_add_test(tst_qpainterstateguard
SOURCES
tst_qpainterstateguard.cpp
LIBRARIES
Qt::Gui
)

View File

@ -0,0 +1,69 @@
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#define Q_ASSERT(cond) ((cond) ? static_cast<void>(0) : qCritical(#cond))
#include <QTest>
#include <QtGui/qpixmap.h>
#include <QtGui/qpainter.h>
#include <QtGui/qpainterstateguard.h>
class tst_QPainterStateGuard : public QObject
{
Q_OBJECT
private slots:
void basics_data();
void basics();
};
void tst_QPainterStateGuard::basics_data()
{
QTest::addColumn<QPainterStateGuard::InitialState>("initialState");
QTest::addColumn<bool>("expectWarning");
QTest::addRow("Save") << QPainterStateGuard::InitialState::Save << false;
QTest::addRow("NoSave") << QPainterStateGuard::InitialState::NoSave << true;
}
void tst_QPainterStateGuard::basics()
{
QFETCH(const QPainterStateGuard::InitialState, initialState);
QFETCH(const bool, expectWarning);
if (expectWarning) {
QTest::ignoreMessage(QtCriticalMsg, "m_level > 0");
QTest::ignoreMessage(QtWarningMsg, "QPainter::restore: Unbalanced save/restore");
} else {
QTest::failOnWarning("QPainter::restore: Unbalanced save/restore");
}
QPixmap pixmap(100, 100);
QPainter painter(&pixmap);
const QPen defaultPen = painter.pen();
{
auto makeGuard = [initialState](QPainter *p) -> QPainterStateGuard {
return QPainterStateGuard(p, initialState);
};
QPainterStateGuard guard = makeGuard(&painter);
painter.setPen(Qt::red);
QCOMPARE(painter.pen().color(), Qt::red);
QPainterStateGuard guard2 = std::move(guard);
QCOMPARE(painter.pen().color(), Qt::red);
guard2.restore();
}
if (expectWarning)
QCOMPARE_NE(painter.pen(), defaultPen);
else
QCOMPARE(painter.pen(), defaultPen);
}
QTEST_MAIN(tst_QPainterStateGuard)
#include "tst_qpainterstateguard.moc"