mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-09-20 15:24:14 +02:00
Keep scene updates flowing on the dark canvas and soften its grid
On a dark palette the folio view paints through QGraphicsView::render() instead of onto its viewport. In that case QGraphicsView never clears the scene's "update everything" flag, and while the flag is set every further QGraphicsScene::update() and item update is dropped: from the second Diagram::update() on, the grid toggle, the white/gray toggle and even a selection waited for an unrelated repaint. With a receiver on QGraphicsScene::changed() the scene clears the flag before it emits, so DiagramView now connects an empty receiver in its constructor. While the view paints for inversion, Diagram draws the grid dots a third of the way from the sheet color to black, so they come out as a soft gray on the dark sheet instead of as bright as the ink. Printing and export never take that path. Test in tst_qetpalette: sceneUpdatesReachARenderedView.
This commit is contained in:
+12
-2
@@ -283,10 +283,20 @@ void Diagram::drawBackground(QPainter *p, const QRectF &r) {
|
||||
* if background color is black,
|
||||
* then grid spots shall be white,
|
||||
* else they shall be black in color.
|
||||
* A view that shows the sheet with its lightness inverted
|
||||
* would turn black dots as bright as the ink, so it gets
|
||||
* dots a third of the way from the sheet color to black,
|
||||
* which come out as a soft gray.
|
||||
*/
|
||||
QPen pen;
|
||||
Diagram::background_color == Qt::black? pen.setColor(Qt::white)
|
||||
: pen.setColor(Qt::black);
|
||||
if (Diagram::background_color == Qt::black)
|
||||
pen.setColor(Qt::white);
|
||||
else if (m_inverted_lightness)
|
||||
pen.setColor(QColor(Diagram::background_color.red() * 2 / 3,
|
||||
Diagram::background_color.green() * 2 / 3,
|
||||
Diagram::background_color.blue() * 2 / 3));
|
||||
else
|
||||
pen.setColor(Qt::black);
|
||||
pen.setCosmetic(true);
|
||||
p->setPen(pen);
|
||||
|
||||
|
||||
@@ -123,6 +123,7 @@ class Diagram : public QGraphicsScene
|
||||
qreal diagram_qet_version_;
|
||||
|
||||
bool draw_grid_;
|
||||
bool m_inverted_lightness = false;
|
||||
bool use_border_;
|
||||
bool draw_guides_;
|
||||
QList<Diagram::Guide> m_guides_list;
|
||||
@@ -222,6 +223,7 @@ class Diagram : public QGraphicsScene
|
||||
ExportProperties applyProperties(const ExportProperties &);
|
||||
void setDisplayGrid(bool);
|
||||
bool displayGrid();
|
||||
void setInvertedLightness(bool);
|
||||
void setDisplayGuides(bool);
|
||||
bool displayGuides();
|
||||
void updateProjectGuides(const QList<GuideProperties> &guides);
|
||||
@@ -355,6 +357,18 @@ inline void Diagram::setDisplayGrid(bool dg) {
|
||||
draw_grid_ = dg;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Diagram::setInvertedLightness
|
||||
Tell the diagram whether the view painting it will show the result
|
||||
with its lightness inverted (DiagramView::paintInverted on a dark
|
||||
palette). drawBackground draws a softer grid in that case. Printing
|
||||
and export never set this.
|
||||
@param inverted
|
||||
*/
|
||||
inline void Diagram::setInvertedLightness(bool inverted) {
|
||||
m_inverted_lightness = inverted;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Diagram::displayGrid
|
||||
@return draw_grid_ true if the grid is drawn, false otherwise.
|
||||
|
||||
@@ -108,6 +108,17 @@ DiagramView::DiagramView(Diagram *diagram, QWidget *parent) :
|
||||
|
||||
connect(m_diagram, &Diagram::showDiagram, this, &DiagramView::showDiagram);
|
||||
connect(m_diagram, &QGraphicsScene::sceneRectChanged, this, &DiagramView::adjustSceneRect);
|
||||
/* On a dark palette this view paints the scene into an image (see
|
||||
* paintInverted). QGraphicsView delivers scene updates straight to
|
||||
* its viewport when nobody listens to QGraphicsScene::changed(),
|
||||
* and in that mode the scene clears its "update everything" flag
|
||||
* only when the items are painted straight onto the viewport,
|
||||
* which never happens here: from the second update on, grid and
|
||||
* background toggles and even a selection would wait for an
|
||||
* unrelated repaint. With a receiver connected the scene sends its
|
||||
* updates through the signal, and clears the flag before it
|
||||
* emits. Any receiver does; this one has nothing to do. */
|
||||
connect(m_diagram, &QGraphicsScene::changed, this, [](const QList<QRectF> &) {});
|
||||
connect(&(m_diagram -> border_and_titleblock), &BorderTitleBlock::informationChanged, this, &DiagramView::updateWindowTitle);
|
||||
connect(diagram, &Diagram::findElementRequired, this, &DiagramView::findElementRequired);
|
||||
|
||||
@@ -1120,8 +1131,10 @@ void DiagramView::paintInverted(const QRect &area)
|
||||
|
||||
QPainter buffer_painter(&buffer);
|
||||
buffer_painter.setRenderHints(renderHints());
|
||||
m_diagram->setInvertedLightness(true);
|
||||
render(&buffer_painter, QRectF(QPointF(0, 0), QSizeF(rect.size())),
|
||||
rect, Qt::IgnoreAspectRatio);
|
||||
m_diagram->setInvertedLightness(false);
|
||||
buffer_painter.end();
|
||||
|
||||
QET::Palette::invertLightness(buffer, palette().color(QPalette::Base),
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <QApplication>
|
||||
#include <QCheckBox>
|
||||
#include <QComboBox>
|
||||
#include <QGraphicsItem>
|
||||
#include <QGraphicsScene>
|
||||
#include <QGraphicsView>
|
||||
#include <QHBoxLayout>
|
||||
@@ -75,6 +76,7 @@ class tst_qetpalette : public QObject
|
||||
void invertLightnessMapsSheetAndInk();
|
||||
void invertedViewReadsOnDarkSheet();
|
||||
void invertLightnessSpeed();
|
||||
void sceneUpdatesReachARenderedView();
|
||||
|
||||
private:
|
||||
static void addPaletteRows();
|
||||
@@ -529,6 +531,67 @@ void tst_qetpalette::invertedViewReadsOnDarkSheet()
|
||||
qPrintable(QString("the blue box became %1").arg(box.name())));
|
||||
}
|
||||
|
||||
namespace {
|
||||
/**
|
||||
A view that paints the way DiagramView does on a dark palette:
|
||||
the exposed rectangle goes through QGraphicsView::render() into an
|
||||
image, which is then blitted, so Qt never paints the items straight
|
||||
onto the viewport.
|
||||
*/
|
||||
class RenderedView : public QGraphicsView
|
||||
{
|
||||
public:
|
||||
int paints = 0;
|
||||
using QGraphicsView::QGraphicsView;
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *event) override
|
||||
{
|
||||
++paints;
|
||||
const QRect rect = event->rect().intersected(viewport()->rect());
|
||||
QImage buffer(rect.size(), QImage::Format_RGB32);
|
||||
QPainter buffer_painter(&buffer);
|
||||
render(&buffer_painter, QRectF(QPointF(0, 0), QSizeF(rect.size())), rect);
|
||||
buffer_painter.end();
|
||||
QPainter painter(viewport());
|
||||
painter.drawImage(rect.topLeft(), buffer);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
QGraphicsView clears the scene's "update everything" flag only when
|
||||
it paints the items straight onto its viewport, and while the flag is
|
||||
set every further QGraphicsScene::update() and item update is dropped.
|
||||
A view that paints through render() therefore needs a receiver on
|
||||
QGraphicsScene::changed(), which makes the scene clear the flag before
|
||||
it emits. This checks that with the receiver, three whole-scene
|
||||
updates and a selection each repaint the view.
|
||||
*/
|
||||
void tst_qetpalette::sceneUpdatesReachARenderedView()
|
||||
{
|
||||
QGraphicsScene scene(0, 0, 100, 100);
|
||||
QGraphicsRectItem *item = scene.addRect(10, 10, 30, 30, QPen(Qt::black), QBrush(Qt::white));
|
||||
item->setFlag(QGraphicsItem::ItemIsSelectable);
|
||||
QObject::connect(&scene, &QGraphicsScene::changed, &scene, [](const QList<QRectF> &) {});
|
||||
|
||||
RenderedView view(&scene);
|
||||
view.resize(120, 120);
|
||||
view.show();
|
||||
QVERIFY(QTest::qWaitForWindowExposed(&view));
|
||||
QTRY_VERIFY(view.paints >= 1);
|
||||
|
||||
for (int round = 1; round <= 3; ++round)
|
||||
{
|
||||
const int before = view.paints;
|
||||
scene.update();
|
||||
QTRY_VERIFY2(view.paints > before, qPrintable(QString("scene update %1 was dropped").arg(round)));
|
||||
}
|
||||
|
||||
const int before = view.paints;
|
||||
item->setSelected(true);
|
||||
QTRY_VERIFY2(view.paints > before, "the selection change was dropped");
|
||||
}
|
||||
|
||||
/**
|
||||
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