Fill the dark canvas buffer and keep one scene connection

QGraphicsView::render() paints only what the scene draws, so a scene
without a background brush left the off-screen buffer uninitialized
and the inversion turned that memory into noise. The buffer is now
filled white first, which the inversion turns into the Base color.

listenToScene() connected a new receiver on every setScene() call and
never dropped the previous scene's. It now keeps a single connection
and replaces it.

Test in tst_qetpalette: paletteViewFillsWhatTheSceneLeavesBlank.
This commit is contained in:
Jeff Patterson
2026-09-19 16:07:36 -05:00
parent 50792ba1ad
commit 3e34a6d55f
3 changed files with 37 additions and 3 deletions
+10 -3
View File
@@ -72,13 +72,17 @@ void PaletteGraphicsView::paintingInverted(bool inverted)
/**
@brief PaletteGraphicsView::listenToScene
Connect a receiver to the scene's changed() signal. Any receiver does;
this one has nothing to do. The connection dies with the view.
Connect a receiver to the scene's changed() signal, dropping the one
on the previous scene. Any receiver does; this one has nothing to do.
The connection dies with the view.
*/
void PaletteGraphicsView::listenToScene(QGraphicsScene *scene)
{
disconnect(m_scene_connection);
m_scene_connection = QMetaObject::Connection();
if (scene)
connect(scene, &QGraphicsScene::changed, this, [](const QList<QRectF> &) {});
m_scene_connection = connect(scene, &QGraphicsScene::changed,
this, [](const QList<QRectF> &) {});
}
/**
@@ -129,6 +133,9 @@ void PaletteGraphicsView::paintInverted(const QRect &area)
QImage buffer(qCeil(rect.width() * ratio), qCeil(rect.height() * ratio),
QImage::Format_RGB32);
buffer.setDevicePixelRatio(ratio);
// render() paints only what the scene draws; what it leaves blank is
// the white sheet, which the inversion turns into the Base color.
buffer.fill(Qt::white);
QPainter buffer_painter(&buffer);
buffer_painter.setRenderHints(renderHints());
+2
View File
@@ -66,6 +66,8 @@ class PaletteGraphicsView : public QGraphicsView
void listenToScene(QGraphicsScene *scene);
void paintInverted(const QRect &area);
void drawRubberBand(QPainter &painter);
QMetaObject::Connection m_scene_connection;
};
#endif
+25
View File
@@ -83,6 +83,7 @@ class tst_qetpalette : public QObject
void paletteViewKeepsSceneUpdatesFlowing();
void paletteViewDrawsTheRubberBand();
void paletteViewFollowsTheApplicationUnderAStyleSheet();
void paletteViewFillsWhatTheSceneLeavesBlank();
private:
static void addPaletteRows();
@@ -776,6 +777,30 @@ void tst_qetpalette::paletteViewFollowsTheApplicationUnderAStyleSheet()
QCOMPARE(sheetColor(view->viewport()->grab().toImage()), QColor(Qt::white));
}
/**
render() paints only what the scene draws. A scene without a
background brush leaves the rest of the buffer untouched, so the view
fills it first: on the dark palette the blank area comes out as Base,
not as whatever the memory held.
*/
void tst_qetpalette::paletteViewFillsWhatTheSceneLeavesBlank()
{
QApplication::setStyle(QStyleFactory::create("Fusion"));
QApplication::setPalette(QET::Palette::fusionDark());
QGraphicsScene scene(0, 0, 200, 120);
scene.addLine(10, 60, 190, 60, QPen(Qt::black, 2));
ProbeView view(&scene);
showAsSheet(view);
QVERIFY(QTest::qWaitForWindowExposed(&view));
const QImage image = view.viewport()->grab().toImage();
const QColor base = QET::Palette::fusionDark().color(QPalette::Active, QPalette::Base);
QCOMPARE(sheetColor(image), base);
QCOMPARE(image.pixelColor(100, 20), base);
QCOMPARE(image.pixelColor(100, 100), base);
}
/**
The inversion runs on every repaint of the folio, so a 4K viewport
has to cost a few milliseconds. Reported, not asserted: the bound