Skip to content

Commit

Permalink
Add QPainterStateGuard
Browse files Browse the repository at this point in the history
Add QPainterStateGuard as a small RAII helper class to avoid unbalanced
QPainter::save()/restore() calls.

Change-Id: I618a5d720b7bc9267a0fbac7dcc90e61f5d9cefd
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
Reviewed-by: David Faure <david.faure@kdab.com>
  • Loading branch information
chehrlic committed Nov 28, 2024
1 parent b2f471e commit 9ecf47a
Show file tree
Hide file tree
Showing 4 changed files with 167 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/gui/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ qt_internal_add_module(Gui
painting/qpaintengine_raster.cpp painting/qpaintengine_raster_p.h
painting/qpaintengineex.cpp painting/qpaintengineex_p.h
painting/qpainter.cpp painting/qpainter.h painting/qpainter_p.h
painting/qpainterstateguard.cpp painting/qpainterstateguard.h
painting/qpainterpath.cpp painting/qpainterpath.h painting/qpainterpath_p.h
painting/qpathclipper.cpp painting/qpathclipper_p.h
painting/qpathsimplifier.cpp painting/qpathsimplifier_p.h
Expand Down
54 changes: 54 additions & 0 deletions src/gui/doc/snippets/code/src_gui_painting_qpainterstateguard.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright (C) 2024 Christian Ehrlicher <ch.ehrlicher@gmx.de>
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QPaintEvent>
#include <QPainter>
#include <QPainterStateGuard>
#include <QWidget>


namespace src_gui_painting_qpainterstateguard {
struct MyWidget : public QWidget
{
void paintEvent(QPaintEvent *) override;
bool drawText = true;
QLine line;
};
struct MyGuardWidget : public QWidget
{
void paintEvent(QPaintEvent *) override;
bool drawText = true;
QLine line;
};

//! [0]
void MyWidget::paintEvent(QPaintEvent *)
{
QPainter painter(this);
painter.setPen(Qt::red);
if (drawText) {
painter.save();
painter.setPen(Qt::blue);
painter.setFont(QFont("Arial", 30));
painter.drawText(rect(), Qt::AlignCenter, "Qt");
painter.restore(); // don't forget to restore previous painter state
}
painter.drawLine(line);
}
//! [0]

//! [1]
void MyGuardWidget::paintEvent(QPaintEvent *)
{
QPainter painter(this);
painter.setPen(Qt::red);
if (drawText) {
QPainterStateGuard guard(&painter)
painter.setPen(Qt::blue);
painter.setFont(QFont("Arial", 30));
painter.drawText(rect(), Qt::AlignCenter, "Qt");
}
painter.drawLine(line);
}
//! [1]

} // src_gui_painting_qpainterstateguard
55 changes: 55 additions & 0 deletions src/gui/painting/qpainterstateguard.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Copyright (C) 2024 Christian Ehrlicher <ch.ehrlicher@gmx.de>
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only

#include "qpainterstateguard.h"

QT_BEGIN_NAMESPACE

/*!
\class QPainterStateGuard
\brief The QPainterStateGuard is a RAII convenience class for balanced
QPainter::save() and QPainter::restore() calls.
\since 6.9
\inmodule QtGui
\ingroup painting
\reentrant
\sa QPainter
QPainterStateGuard should be used everywhere as a replacement for QPainter::save()
to make sure that the corresponding QPainter::restore() is called upon finishing
of the painting routine to avoid unbalanced calls between those two functions.
Example with QPainter::save()/QPainter::restore():
\snippet code/src_gui_painting_qpainterstateguard.cpp 0
Example with QPainterStateGuard:
\snippet code/src_gui_painting_qpainterstateguard.cpp 1
*/

/*!
\fn QPainterStateGuard::QPainterStateGuard(QPainter *painter, InitialState state = Save)
Constructs a QPainterStateGuard and calls save() on \a painter if \a state
is \c Save (which is the default). When QPainterStateGuard is destroyed, restore()
is called as often as save() was called to restore the QPainter's state.
*/

/*!
\fn QPainterStateGuard::~QPainterStateGuard()
Destroys the QPainterStateGuard instance and calls restore() as often as save()
was called to restore the QPainter's state.
*/

/*!
\fn void QPainterStateGuard::save()
Calls QPainter::save() and increases the internal save/restore counter by one.
*/

/*!
\fn void QPainterStateGuard::restore()
Calls QPainter::restore() if the internal save/restore counter is greater than zero.
*/

QT_END_NAMESPACE
57 changes: 57 additions & 0 deletions src/gui/painting/qpainterstateguard.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Copyright (C) 2024 Christian Ehrlicher <ch.ehrlicher@gmx.de>
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only

#ifndef QPAINTERSTATEGUARD_H
#define QPAINTERSTATEGUARD_H

#include <QtCore/qtclasshelpermacros.h>
#include <QtGui/qpainter.h>

QT_BEGIN_NAMESPACE

class QPainterStateGuard
{
Q_DISABLE_COPY_MOVE(QPainterStateGuard)
public:
enum InitialState
{
Save,
NoSave,
};

Q_NODISCARD_CTOR
explicit QPainterStateGuard(QPainter *painter, InitialState state = Save)
: m_painter(painter)
{
Q_ASSERT(painter);
if (state == InitialState::Save)
save();
}

~QPainterStateGuard()
{
while (m_level > 0)
restore();
}

void save()
{
m_painter->save();
++m_level;
}

void restore()
{
Q_ASSERT(m_level > 0);
--m_level;
m_painter->restore();
}

private:
QPainter *m_painter;
int m_level = 0;
};

QT_END_NAMESPACE

#endif // QPAINTERSTATEGUARD_H

0 comments on commit 9ecf47a

Please sign in to comment.