mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-10-13 16:34:14 +02:00
Keep the dark canvas on QGraphicsView's own update path
Laurent found that moving an element on a #954 build left its terminals' help lines behind at every step, on both palettes. The view listened to QGraphicsScene::changed() so that render() would keep the scene's updates flowing, and any receiver on that signal puts the scene on its Qt 4.4 compatibility path, which erases a moved item's own old rect only: children bigger than their parent stay on screen. Master hides that with FullViewportUpdate, which repaints the whole viewport on every change. Paint the inverted folio through QGraphicsView::paintEvent() instead, with IndirectPainting set for that call and the draw hooks painting into a viewport-sized image, and hand the scene the viewport when the items are drawn so it records where each item was painted, including a child whose geometry is set while its parent paints. The listener and the full-viewport update go. Two tests move a parent with a sheet-wide child, read the backing store, and require the repaint to be a partial one.
This commit is contained in:
@@ -29,6 +29,7 @@
|
||||
#include <QPushButton>
|
||||
#include <QPainter>
|
||||
#include <QRadioButton>
|
||||
#include <QScreen>
|
||||
#include <QStandardItemModel>
|
||||
#include <QTreeView>
|
||||
#include <QStyleFactory>
|
||||
@@ -85,6 +86,10 @@ class tst_qetpalette : public QObject
|
||||
void paletteViewDrawsTheRubberBand();
|
||||
void paletteViewFollowsTheApplicationUnderAStyleSheet();
|
||||
void paletteViewFillsWhatTheSceneLeavesBlank();
|
||||
void paletteViewErasesMovedChildren_data();
|
||||
void paletteViewErasesMovedChildren();
|
||||
void paletteViewErasesChildrenMovedWhilePainting_data();
|
||||
void paletteViewErasesChildrenMovedWhilePainting();
|
||||
void styleSheetWidgetsFollowPaletteChange();
|
||||
|
||||
private:
|
||||
@@ -573,6 +578,37 @@ namespace {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
A parent that, like a Terminal with its help lines, gives its child
|
||||
line a new geometry from inside paint(): the line always runs
|
||||
across the sheet at the parent's height, whatever the parent's
|
||||
position. Where the child is painted is therefore only known once
|
||||
the parent has been painted.
|
||||
*/
|
||||
class PaintTimeHelpLine : public QGraphicsRectItem
|
||||
{
|
||||
public:
|
||||
explicit PaintTimeHelpLine(const QRectF &sheet) :
|
||||
QGraphicsRectItem(0, 0, 20, 20),
|
||||
m_sheet(sheet),
|
||||
m_line(new QGraphicsLineItem(this))
|
||||
{
|
||||
setPen(Qt::NoPen);
|
||||
setBrush(Qt::black);
|
||||
m_line->setPen(QPen(Qt::black, 2));
|
||||
}
|
||||
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override
|
||||
{
|
||||
QGraphicsRectItem::paint(painter, option, widget);
|
||||
const qreal y = scenePos().y() + 10;
|
||||
m_line->setLine(QLineF(m_line->mapFromScene(QPointF(m_sheet.left(), y)),
|
||||
m_line->mapFromScene(QPointF(m_sheet.right(), y))));
|
||||
}
|
||||
private:
|
||||
QRectF m_sheet;
|
||||
QGraphicsLineItem *m_line;
|
||||
};
|
||||
|
||||
/**
|
||||
A small folio: a white sheet with a black line and a red box.
|
||||
Returns the box, which is selectable.
|
||||
@@ -803,6 +839,112 @@ void tst_qetpalette::paletteViewFillsWhatTheSceneLeavesBlank()
|
||||
QCOMPARE(image.pixelColor(100, 100), base);
|
||||
}
|
||||
|
||||
/**
|
||||
Moving an item must erase it where it was, children included, even a
|
||||
child far bigger than its parent: a terminal's help lines span the
|
||||
whole sheet. QGraphicsView does that on its default update path by
|
||||
remembering where each item was last painted. A receiver on
|
||||
QGraphicsScene::changed() switches the scene to its Qt 4.4
|
||||
compatibility path, which erases only the parent's own old rect and
|
||||
leaves the children's trails behind (reported on #954). The check
|
||||
reads the window's backing store, not a fresh rendering, and makes
|
||||
sure the repaint after the move was a partial one: a full repaint of
|
||||
the viewport would hide the bug, not fix it.
|
||||
*/
|
||||
void tst_qetpalette::paletteViewErasesMovedChildren_data()
|
||||
{
|
||||
addPaletteRows();
|
||||
}
|
||||
|
||||
void tst_qetpalette::paletteViewErasesMovedChildren()
|
||||
{
|
||||
QFETCH(QPalette, palette);
|
||||
QApplication::setStyle(QStyleFactory::create("Fusion"));
|
||||
QApplication::setPalette(palette);
|
||||
|
||||
QGraphicsScene scene(0, 0, 200, 120);
|
||||
scene.setBackgroundBrush(Qt::white);
|
||||
QGraphicsRectItem *parent = scene.addRect(0, 0, 20, 20, QPen(Qt::NoPen), QBrush(Qt::black));
|
||||
parent->setPos(20, 20);
|
||||
QGraphicsLineItem *line = new QGraphicsLineItem(-10, 10, 170, 10, parent);
|
||||
line->setPen(QPen(Qt::black, 2));
|
||||
|
||||
ProbeView view(&scene);
|
||||
showAsSheet(view);
|
||||
QVERIFY(QTest::qWaitForWindowExposed(&view));
|
||||
QTRY_VERIFY(view.paints >= 1);
|
||||
QScreen *screen = view.screen();
|
||||
const WId window = view.window()->winId();
|
||||
const QImage before = screen->grabWindow(window, 0, 0, 200, 120).toImage();
|
||||
const QColor sheet = sheetColor(before);
|
||||
QVERIFY2(before.pixelColor(120, 30) != sheet, "the child line is not drawn");
|
||||
QVERIFY2(before.pixelColor(30, 30) != sheet, "the parent is not drawn");
|
||||
|
||||
const int from = view.paint_rects.size();
|
||||
parent->setPos(20, 70);
|
||||
QTRY_VERIFY(view.paint_rects.size() > from);
|
||||
QVERIFY2(!view.fullyRepaintedSince(from), "the move repainted the whole viewport");
|
||||
|
||||
const QImage after = screen->grabWindow(window, 0, 0, 200, 120).toImage();
|
||||
QVERIFY2(after.pixelColor(120, 30) == sheet,
|
||||
qPrintable(QString("the child line left a trail: %1").arg(after.pixelColor(120, 30).name())));
|
||||
QVERIFY2(after.pixelColor(30, 30) == sheet,
|
||||
qPrintable(QString("the parent left a trail: %1").arg(after.pixelColor(30, 30).name())));
|
||||
QVERIFY2(after.pixelColor(120, 80) != sheet, "the child line is missing at its new place");
|
||||
QVERIFY2(after.pixelColor(30, 80) != sheet, "the parent is missing at its new place");
|
||||
}
|
||||
|
||||
/**
|
||||
The same, for a child whose geometry is set while its parent is
|
||||
painted, as a Terminal does with its help lines: the view must record
|
||||
where the child was painted, not where it was before the paint, or
|
||||
the next move erases the wrong place. Two moves, because the first
|
||||
paint after a move is where the child gets its new geometry.
|
||||
*/
|
||||
void tst_qetpalette::paletteViewErasesChildrenMovedWhilePainting_data()
|
||||
{
|
||||
addPaletteRows();
|
||||
}
|
||||
|
||||
void tst_qetpalette::paletteViewErasesChildrenMovedWhilePainting()
|
||||
{
|
||||
QFETCH(QPalette, palette);
|
||||
QApplication::setStyle(QStyleFactory::create("Fusion"));
|
||||
QApplication::setPalette(palette);
|
||||
|
||||
QGraphicsScene scene(0, 0, 200, 120);
|
||||
scene.setBackgroundBrush(Qt::white);
|
||||
PaintTimeHelpLine *parent = new PaintTimeHelpLine(scene.sceneRect());
|
||||
scene.addItem(parent);
|
||||
parent->setPos(20, 20);
|
||||
|
||||
ProbeView view(&scene);
|
||||
showAsSheet(view);
|
||||
QVERIFY(QTest::qWaitForWindowExposed(&view));
|
||||
QTRY_VERIFY(view.paints >= 1);
|
||||
QScreen *screen = view.screen();
|
||||
const WId window = view.window()->winId();
|
||||
const QColor sheet = sheetColor(screen->grabWindow(window, 0, 0, 200, 120).toImage());
|
||||
|
||||
int from = view.paint_rects.size();
|
||||
parent->setPos(20, 50);
|
||||
QTRY_VERIFY(view.paint_rects.size() > from);
|
||||
QTRY_VERIFY2(screen->grabWindow(window, 0, 0, 200, 120).toImage().pixelColor(120, 60) != sheet,
|
||||
"the line did not follow the parent");
|
||||
|
||||
from = view.paint_rects.size();
|
||||
parent->setPos(20, 80);
|
||||
QTRY_VERIFY(view.paint_rects.size() > from);
|
||||
QVERIFY2(!view.fullyRepaintedSince(from), "the move repainted the whole viewport");
|
||||
QImage after;
|
||||
QTRY_VERIFY2((after = screen->grabWindow(window, 0, 0, 200, 120).toImage()).pixelColor(120, 90) != sheet,
|
||||
"the line is missing at its new place");
|
||||
QVERIFY2(after.pixelColor(120, 60) == sheet,
|
||||
qPrintable(QString("the line left a trail: %1").arg(after.pixelColor(120, 60).name())));
|
||||
QVERIFY2(after.pixelColor(120, 30) == sheet,
|
||||
qPrintable(QString("the first line was never erased: %1").arg(after.pixelColor(120, 30).name())));
|
||||
}
|
||||
|
||||
/**
|
||||
The inversion runs on every repaint of the folio, so a 4K viewport
|
||||
has to cost a few milliseconds. Reported, not asserted: the bound
|
||||
|
||||
Reference in New Issue
Block a user