Add support for snapping to pixel grid
This enables us to do more intelligent distribution than simply doing the rounding on each individual items geometry (which often leads to larger spacings than specified). Instead of doing the rounding on the output geometries, we now do the snapping inside the layout engine. This allows us to do more intelligent distribution of items, and spacings should always be respected. There are some cases where items with fractional size hints might overlap with less than a pixel. This was also the case before this patch. Those cases are impossible to fix properly, since fractional size hints conflicts with the snapping in some cases. (Fractional size hints is normal for Text items.) Task-number: QTBUG-41216 Change-Id: I01a8bc3529f0b8b028d6eb0a530c751b67ac6f4e Reviewed-by: Paul Olav Tvete <paul.tvete@theqtcompany.com>
This commit is contained in:
parent
18aae36a90
commit
68293395ed
@ -154,7 +154,7 @@ void QGridLayoutRowData::reset(int count)
|
||||
hasIgnoreFlag = false;
|
||||
}
|
||||
|
||||
void QGridLayoutRowData::distributeMultiCells(const QGridLayoutRowInfo &rowInfo)
|
||||
void QGridLayoutRowData::distributeMultiCells(const QGridLayoutRowInfo &rowInfo, bool snapToPixelGrid)
|
||||
{
|
||||
MultiCellMap::const_iterator i = multiCellMap.constBegin();
|
||||
for (; i != multiCellMap.constEnd(); ++i) {
|
||||
@ -173,7 +173,7 @@ void QGridLayoutRowData::distributeMultiCells(const QGridLayoutRowInfo &rowInfo)
|
||||
qreal extra = compare(box, totalBox, j);
|
||||
if (extra > 0.0) {
|
||||
calculateGeometries(start, end, box.q_sizes(j), dummy.data(), newSizes.data(),
|
||||
0, totalBox, rowInfo);
|
||||
0, totalBox, rowInfo, snapToPixelGrid);
|
||||
|
||||
for (int k = 0; k < span; ++k)
|
||||
extras[k].q_sizes(j) = newSizes[k];
|
||||
@ -188,11 +188,19 @@ void QGridLayoutRowData::distributeMultiCells(const QGridLayoutRowInfo &rowInfo)
|
||||
}
|
||||
multiCellMap.clear();
|
||||
}
|
||||
namespace {
|
||||
|
||||
// does not return int
|
||||
static inline qreal qround(qreal f)
|
||||
{
|
||||
return floor(f + 0.5);
|
||||
}
|
||||
|
||||
}
|
||||
void QGridLayoutRowData::calculateGeometries(int start, int end, qreal targetSize, qreal *positions,
|
||||
qreal *sizes, qreal *descents,
|
||||
const QGridLayoutBox &totalBox,
|
||||
const QGridLayoutRowInfo &rowInfo)
|
||||
const QGridLayoutRowInfo &rowInfo, bool snapToPixelGrid)
|
||||
{
|
||||
Q_ASSERT(end > start);
|
||||
|
||||
@ -335,17 +343,19 @@ void QGridLayoutRowData::calculateGeometries(int start, int end, qreal targetSiz
|
||||
|
||||
bool keepGoing = somethingHasAMaximumSize;
|
||||
while (keepGoing) {
|
||||
//sumCurrentAvailable is so large that something *might* reach its maximum size
|
||||
keepGoing = false;
|
||||
|
||||
for (int i = 0; i < n; ++i) {
|
||||
if (newSizes[i] >= 0.0)
|
||||
continue;
|
||||
|
||||
qreal maxBoxSize;
|
||||
if (isLargerThanMaximum)
|
||||
maxBoxSize = rowInfo.boxes.value(start + i).q_maximumSize;
|
||||
else
|
||||
maxBoxSize = boxes.at(start + i).q_maximumSize;
|
||||
const QVector<QGridLayoutBox> &rBoxes = isLargerThanMaximum ? rowInfo.boxes : boxes;
|
||||
const QGridLayoutBox &box = rBoxes.value(start + i);
|
||||
qreal maxBoxSize = box.q_maximumSize;
|
||||
|
||||
if (snapToPixelGrid)
|
||||
maxBoxSize = qMax(box.q_minimumSize, floor(maxBoxSize));
|
||||
|
||||
qreal avail = sumCurrentAvailable * factors[i] / sumFactors;
|
||||
if (sizes[i] + avail >= maxBoxSize) {
|
||||
@ -358,7 +368,6 @@ void QGridLayoutRowData::calculateGeometries(int start, int end, qreal targetSiz
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < n; ++i) {
|
||||
if (newSizes[i] < 0.0) {
|
||||
qreal delta = (sumFactors == 0.0) ? 0.0
|
||||
@ -402,6 +411,10 @@ void QGridLayoutRowData::calculateGeometries(int start, int end, qreal targetSiz
|
||||
Q_ASSERT(surplus == 0);
|
||||
#endif
|
||||
}
|
||||
if (snapToPixelGrid) {
|
||||
for (int i = 0; i < n; ++i)
|
||||
positions[i] = qround(positions[i]);
|
||||
}
|
||||
|
||||
if (descents) {
|
||||
for (int i = 0; i < n; ++i) {
|
||||
@ -753,10 +766,11 @@ void QGridLayoutRowInfo::dump(int indent) const
|
||||
}
|
||||
#endif
|
||||
|
||||
QGridLayoutEngine::QGridLayoutEngine(Qt::Alignment defaultAlignment)
|
||||
QGridLayoutEngine::QGridLayoutEngine(Qt::Alignment defaultAlignment, bool snapToPixelGrid)
|
||||
{
|
||||
m_visualDirection = Qt::LeftToRight;
|
||||
m_defaultAlignment = defaultAlignment;
|
||||
m_snapToPixelGrid = snapToPixelGrid;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ -1007,8 +1021,17 @@ void QGridLayoutEngine::setGeometries(const QRectF &contentsGeometry, const QAbs
|
||||
if (item->rowSpan() != 1)
|
||||
height += q_yy[item->lastRow()] - y;
|
||||
|
||||
const Qt::Alignment align = effectiveAlignment(item);
|
||||
QRectF geom = item->geometryWithin(contentsGeometry.x() + x, contentsGeometry.y() + y,
|
||||
width, height, q_descents[item->lastRow()], effectiveAlignment(item));
|
||||
width, height, q_descents[item->lastRow()], align);
|
||||
if (m_snapToPixelGrid) {
|
||||
// x and y should already be rounded, but the call to geometryWithin() above might
|
||||
// result in a geom with x,y at half-pixels (due to centering within the cell)
|
||||
geom.setX(qround(geom.x()));
|
||||
// Do not snap baseline aligned items, since that might cause the baselines to not be aligned.
|
||||
if (align != Qt::AlignBaseline)
|
||||
geom.setY(qround(geom.y()));
|
||||
}
|
||||
visualRect(&geom, visualDirection(), contentsGeometry);
|
||||
item->setGeometry(geom);
|
||||
}
|
||||
@ -1061,7 +1084,7 @@ QSizeF QGridLayoutEngine::sizeHint(Qt::SizeHint which, const QSizeF &constraint,
|
||||
//Calculate column widths and positions, and put results in q_xx.data() and q_widths.data() so that we can use this information as
|
||||
//constraints to find the row heights
|
||||
q_columnData.calculateGeometries(0, columnCount(), width, sizehint_xx.data(), sizehint_widths.data(),
|
||||
0, sizehint_totalBoxes[Hor], q_infos[Hor]);
|
||||
0, sizehint_totalBoxes[Hor], q_infos[Hor], m_snapToPixelGrid);
|
||||
ensureColumnAndRowData(&q_rowData, &sizehint_totalBoxes[Ver], sizehint_xx.data(), sizehint_widths.data(), Qt::Vertical, styleInfo);
|
||||
sizeHintCalculated = true;
|
||||
}
|
||||
@ -1078,7 +1101,7 @@ QSizeF QGridLayoutEngine::sizeHint(Qt::SizeHint which, const QSizeF &constraint,
|
||||
//Calculate row heights and positions, and put results in q_yy.data() and q_heights.data() so that we can use this information as
|
||||
//constraints to find the column widths
|
||||
q_rowData.calculateGeometries(0, rowCount(), height, sizehint_yy.data(), sizehint_heights.data(),
|
||||
0, sizehint_totalBoxes[Ver], q_infos[Ver]);
|
||||
0, sizehint_totalBoxes[Ver], q_infos[Ver], m_snapToPixelGrid);
|
||||
ensureColumnAndRowData(&q_columnData, &sizehint_totalBoxes[Hor], sizehint_yy.data(), sizehint_heights.data(), Qt::Horizontal, styleInfo);
|
||||
sizeHintCalculated = true;
|
||||
}
|
||||
@ -1533,7 +1556,7 @@ void QGridLayoutEngine::ensureColumnAndRowData(QGridLayoutRowData *rowData, QGri
|
||||
rowData->reset(rowCount(orientation));
|
||||
fillRowData(rowData, colPositions, colSizes, orientation, styleInfo);
|
||||
const QGridLayoutRowInfo &rowInfo = q_infos[orientation == Qt::Vertical];
|
||||
rowData->distributeMultiCells(rowInfo);
|
||||
rowData->distributeMultiCells(rowInfo, m_snapToPixelGrid);
|
||||
*totalBox = rowData->totalBox(0, rowCount(orientation));
|
||||
|
||||
if (totalBox != &q_totalBoxes[o])
|
||||
@ -1605,22 +1628,22 @@ void QGridLayoutEngine::ensureGeometries(const QSizeF &size,
|
||||
//Calculate column widths and positions, and put results in q_xx.data() and q_widths.data() so that we can use this information as
|
||||
//constraints to find the row heights
|
||||
q_columnData.calculateGeometries(0, columnCount(), size.width(), q_xx.data(), q_widths.data(),
|
||||
0, q_totalBoxes[Hor], q_infos[Hor] );
|
||||
0, q_totalBoxes[Hor], q_infos[Hor], m_snapToPixelGrid);
|
||||
ensureColumnAndRowData(&q_rowData, &q_totalBoxes[Ver], q_xx.data(), q_widths.data(), Qt::Vertical, styleInfo);
|
||||
//Calculate row heights and positions, and put results in q_yy.data() and q_heights.data()
|
||||
q_rowData.calculateGeometries(0, rowCount(), size.height(), q_yy.data(), q_heights.data(),
|
||||
q_descents.data(), q_totalBoxes[Ver], q_infos[Ver]);
|
||||
q_descents.data(), q_totalBoxes[Ver], q_infos[Ver], m_snapToPixelGrid);
|
||||
} else {
|
||||
//We have items whose width depends on their height (WFH)
|
||||
ensureColumnAndRowData(&q_rowData, &q_totalBoxes[Ver], NULL, NULL, Qt::Vertical, styleInfo);
|
||||
//Calculate row heights and positions, and put results in q_yy.data() and q_heights.data() so that we can use this information as
|
||||
//constraints to find the column widths
|
||||
q_rowData.calculateGeometries(0, rowCount(), size.height(), q_yy.data(), q_heights.data(),
|
||||
q_descents.data(), q_totalBoxes[Ver], q_infos[Ver]);
|
||||
q_descents.data(), q_totalBoxes[Ver], q_infos[Ver], m_snapToPixelGrid);
|
||||
ensureColumnAndRowData(&q_columnData, &q_totalBoxes[Hor], q_yy.data(), q_heights.data(), Qt::Horizontal, styleInfo);
|
||||
//Calculate row heights and positions, and put results in q_yy.data() and q_heights.data()
|
||||
q_columnData.calculateGeometries(0, columnCount(), size.width(), q_xx.data(), q_widths.data(),
|
||||
0, q_totalBoxes[Hor], q_infos[Hor]);
|
||||
0, q_totalBoxes[Hor], q_infos[Hor], m_snapToPixelGrid);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -226,10 +226,10 @@ class QGridLayoutRowData
|
||||
{
|
||||
public:
|
||||
void reset(int count);
|
||||
void distributeMultiCells(const QGridLayoutRowInfo &rowInfo);
|
||||
void distributeMultiCells(const QGridLayoutRowInfo &rowInfo, bool snapToPixelGrid);
|
||||
void calculateGeometries(int start, int end, qreal targetSize, qreal *positions, qreal *sizes,
|
||||
qreal *descents, const QGridLayoutBox &totalBox,
|
||||
const QGridLayoutRowInfo &rowInfo);
|
||||
const QGridLayoutRowInfo &rowInfo, bool snapToPixelGrid);
|
||||
QGridLayoutBox totalBox(int start, int end) const;
|
||||
void stealBox(int start, int end, int which, qreal *positions, qreal *sizes);
|
||||
|
||||
@ -331,7 +331,7 @@ private:
|
||||
class Q_GUI_EXPORT QGridLayoutEngine
|
||||
{
|
||||
public:
|
||||
QGridLayoutEngine(Qt::Alignment defaultAlignment = Qt::Alignment(0));
|
||||
QGridLayoutEngine(Qt::Alignment defaultAlignment = Qt::Alignment(0), bool snapToPixelGrid = false);
|
||||
inline ~QGridLayoutEngine() { qDeleteAll(q_items); }
|
||||
|
||||
int rowCount(Qt::Orientation orientation) const;
|
||||
@ -436,7 +436,10 @@ private:
|
||||
QLayoutParameter<qreal> q_defaultSpacings[NOrientations];
|
||||
QGridLayoutRowInfo q_infos[NOrientations];
|
||||
Qt::LayoutDirection m_visualDirection;
|
||||
|
||||
// Configuration
|
||||
Qt::Alignment m_defaultAlignment;
|
||||
unsigned m_snapToPixelGrid : 1;
|
||||
|
||||
// Lazily computed from the above user input
|
||||
mutable int q_cachedEffectiveFirstRows[NOrientations];
|
||||
|
Loading…
Reference in New Issue
Block a user