7#include <QtWidgets/qwidget.h>
8#include <QtWidgets/qapplication.h>
9#include <QtCore/qstack.h>
12#include <QtCore/qfile.h>
19using namespace Qt::StringLiterals;
27const qreal g_offset = (
sizeof(qreal) ==
sizeof(
double)) ? QWIDGETSIZE_MAX : QWIDGETSIZE_MAX / 32;
31 sizePolicy(QSizePolicy::Fixed), preferredSize(0),
41 data->graphicsAnchor =
nullptr;
49 if (sizePolicy != policy) {
58 qWarning(
"QGraphicsAnchor::setSpacing: The anchor does not exist.");
62 if (hasSize && (preferredSize == value))
67 preferredSize = value;
75 qWarning(
"QGraphicsAnchor::setSpacing: The anchor does not exist.");
88 qWarning(
"QGraphicsAnchor::setSpacing: The anchor does not exist.");
97 qreal minSizeHint, qreal prefSizeHint, qreal maxSizeHint,
98 qreal *minSize, qreal *prefSize,
114 if (policy & QSizePolicy::ShrinkFlag)
115 *minSize = minSizeHint;
117 *minSize = prefSizeHint;
119 if (policy & QSizePolicy::GrowFlag)
120 *maxSize = maxSizeHint;
122 *maxSize = prefSizeHint;
125 if (policy & QSizePolicy::IgnoreFlag)
126 *prefSize = *minSize;
128 *prefSize = prefSizeHint;
133 if (graphicsAnchor) {
136 QGraphicsAnchorPrivate::get(graphicsAnchor)->data =
nullptr;
138 delete graphicsAnchor;
145 QSizePolicy::Policy policy;
152 if (isLayoutAnchor) {
155 maxSize = QWIDGETSIZE_MAX;
159 minPrefSize = prefSize;
160 maxPrefSize = maxSize;
164 policy =
item->sizePolicy().horizontalPolicy();
165 minSizeHint = item->effectiveSizeHint(Qt::MinimumSize).width();
166 prefSizeHint = item->effectiveSizeHint(Qt::PreferredSize).width();
167 maxSizeHint = item->effectiveSizeHint(Qt::MaximumSize).width();
169 policy =
item->sizePolicy().verticalPolicy();
170 minSizeHint = item->effectiveSizeHint(Qt::MinimumSize).height();
171 prefSizeHint = item->effectiveSizeHint(Qt::PreferredSize).height();
172 maxSizeHint = item->effectiveSizeHint(Qt::MaximumSize).height();
175 if (isCenterAnchor) {
183 Q_ASSERT(graphicsAnchor);
187 policy = anchorPrivate->sizePolicy;
189 maxSizeHint = QWIDGETSIZE_MAX;
192 if (anchorPrivate->hasSize) {
194 prefSizeHint = anchorPrivate->preferredSize;
195 }
else if (styleInfo) {
197 const Qt::Orientation orient = QGraphicsAnchorLayoutPrivate::edgeOrientation(from->m_edge);
198 qreal s = styleInfo->defaultSpacing(orient);
200 QSizePolicy::ControlType controlTypeFrom =
from->m_item->sizePolicy().controlType();
201 QSizePolicy::ControlType controlTypeTo =
to->m_item->sizePolicy().controlType();
202 s = styleInfo->perItemSpacing(controlTypeFrom, controlTypeTo, orient);
217 applySizePolicy(policy, minSizeHint, prefSizeHint, maxSizeHint,
218 &minSize, &prefSize, &maxSize);
220 minPrefSize = prefSize;
221 maxPrefSize = maxSize;
230 sizeAtMinimum = prefSize;
231 sizeAtPreferred = prefSize;
232 sizeAtMaximum = prefSize;
237 firstEdge->sizeAtMinimum = sizeAtMinimum;
238 firstEdge->sizeAtPreferred = sizeAtPreferred;
239 firstEdge->sizeAtMaximum = sizeAtMaximum;
242 secondEdge->sizeAtMinimum = sizeAtMinimum;
243 secondEdge->sizeAtPreferred = sizeAtPreferred;
244 secondEdge->sizeAtMaximum = sizeAtMaximum;
246 secondEdge->sizeAtMinimum = -sizeAtMinimum;
247 secondEdge->sizeAtPreferred = -sizeAtPreferred;
248 secondEdge->sizeAtMaximum = -sizeAtMaximum;
256
257
258
259
260
261
262
263
264
290 minSize = qMax(firstEdge->minSize, secondMin);
291 maxSize = qMin(firstEdge->maxSize, secondMax);
296 if (minSize > maxSize) {
330 prefSize = qBound(minSize, secondPref, maxSize);
331 minPrefSize = qBound(minSize, secondMinPref, maxSize);
332 maxPrefSize = qBound(minSize, secondMaxPref, maxSize);
334 prefSize = qBound(minSize, firstEdge->prefSize, maxSize);
335 minPrefSize = qBound(minSize, firstEdge->minPrefSize, maxSize);
336 maxPrefSize = qBound(minSize, firstEdge->maxPrefSize, maxSize);
339 const qreal lowerBoundary =
340 qBound(minSize, qMax(firstEdge->minPrefSize, secondMinPref), maxSize);
341 const qreal upperBoundary =
342 qBound(minSize, qMin(firstEdge->maxPrefSize, secondMaxPref), maxSize);
343 const qreal prefMean =
344 qBound(minSize, (firstEdge->prefSize + secondPref) / 2, maxSize);
346 if (lowerBoundary < upperBoundary) {
351 prefSize = qBound(lowerBoundary, prefMean, upperBoundary);
352 minPrefSize = lowerBoundary;
353 maxPrefSize = upperBoundary;
361 prefSize = qBound(upperBoundary, prefMean, lowerBoundary);
362 minPrefSize = upperBoundary;
363 maxPrefSize = lowerBoundary;
368 sizeAtMinimum = prefSize;
369 sizeAtPreferred = prefSize;
370 sizeAtMaximum = prefSize;
376
377
378
379
380
381
383 qreal minPref, qreal pref,
384 qreal maxPref, qreal max)
386 QGraphicsAnchorLayoutPrivate::Interval interval;
390 if (value < minPref) {
391 interval = QGraphicsAnchorLayoutPrivate::MinimumToMinPreferred;
394 }
else if (value < pref) {
395 interval = QGraphicsAnchorLayoutPrivate::MinPreferredToPreferred;
398 }
else if (value < maxPref) {
399 interval = QGraphicsAnchorLayoutPrivate::PreferredToMaxPreferred;
403 interval = QGraphicsAnchorLayoutPrivate::MaxPreferredToMaximum;
409 if (upper == lower) {
412 progress = (value - lower) / (upper - lower);
415 return std::pair(interval, progress);
418static qreal interpolate(
const std::pair<QGraphicsAnchorLayoutPrivate::Interval, qreal> &factor,
419 qreal min, qreal minPref, qreal pref, qreal maxPref, qreal max)
424 switch (factor.first) {
425 case QGraphicsAnchorLayoutPrivate::MinimumToMinPreferred:
429 case QGraphicsAnchorLayoutPrivate::MinPreferredToPreferred:
433 case QGraphicsAnchorLayoutPrivate::PreferredToMaxPreferred:
437 case QGraphicsAnchorLayoutPrivate::MaxPreferredToMaximum:
443 return lower + factor.second * (upper - lower);
451 const std::pair<QGraphicsAnchorLayoutPrivate::Interval, qreal> minFactor =
452 getFactor(sizeAtMinimum, minSize, minPrefSize, prefSize, maxPrefSize, maxSize);
453 const std::pair<QGraphicsAnchorLayoutPrivate::Interval, qreal> prefFactor =
454 getFactor(sizeAtPreferred, minSize, minPrefSize, prefSize, maxPrefSize, maxSize);
455 const std::pair<QGraphicsAnchorLayoutPrivate::Interval, qreal> maxFactor =
456 getFactor(sizeAtMaximum, minSize, minPrefSize, prefSize, maxPrefSize, maxSize);
463 for (AnchorData *e : m_edges) {
464 const bool edgeIsForward = (e->from == prev);
466 e->sizeAtMinimum = interpolate(minFactor, e->minSize, e->minPrefSize,
467 e->prefSize, e->maxPrefSize, e->maxSize);
468 e->sizeAtPreferred = interpolate(prefFactor, e->minSize, e->minPrefSize,
469 e->prefSize, e->maxPrefSize, e->maxSize);
470 e->sizeAtMaximum = interpolate(maxFactor, e->minSize, e->minPrefSize,
471 e->prefSize, e->maxPrefSize, e->maxSize);
474 Q_ASSERT(prev == e->to);
475 e->sizeAtMinimum = interpolate(minFactor, e->maxSize, e->maxPrefSize,
476 e->prefSize, e->minPrefSize, e->minSize);
477 e->sizeAtPreferred = interpolate(prefFactor, e->maxSize, e->maxPrefSize,
478 e->prefSize, e->minPrefSize, e->minSize);
479 e->sizeAtMaximum = interpolate(maxFactor, e->maxSize, e->maxPrefSize,
480 e->prefSize, e->minPrefSize, e->minSize);
484 e->updateChildrenSizes();
498 for (AnchorData *edge : m_edges) {
499 const bool edgeIsForward = (edge->from == prev);
501 minSize += edge->minSize;
502 prefSize += edge->prefSize;
503 maxSize += edge->maxSize;
504 minPrefSize += edge->minPrefSize;
505 maxPrefSize += edge->maxPrefSize;
508 Q_ASSERT(prev == edge->to);
509 minSize -= edge->maxSize;
510 prefSize -= edge->prefSize;
511 maxSize -= edge->minSize;
512 minPrefSize -= edge->maxPrefSize;
513 maxPrefSize -= edge->minPrefSize;
519 sizeAtMinimum = prefSize;
520 sizeAtPreferred = prefSize;
521 sizeAtMaximum = prefSize;
525void AnchorData::dump(
int indent) {
526 if (type == Parallel) {
527 qDebug(
"%*s type: parallel:", indent,
"");
528 ParallelAnchorData *p =
static_cast<ParallelAnchorData *>(
this);
529 p->firstEdge->dump(indent+2);
530 p->secondEdge->dump(indent+2);
531 }
else if (type == Sequential) {
532 const auto *s =
static_cast<SequentialAnchorData *>(
this);
533 qDebug(
"%*s type: sequential(%lld):", indent,
"", qint64(s->m_edges.size()));
534 for (AnchorData *e : s->m_edges)
537 qDebug(
"%*s type: Normal:", indent,
"");
546 QSet<AnchorData *> cPositives;
547 QSet<AnchorData *> cNegatives;
548 QSet<AnchorData *> intersection;
550 cPositives = positives + path.negatives;
551 cNegatives = negatives + path.positives;
553 intersection = cPositives & cNegatives;
555 cPositives -= intersection;
556 cNegatives -= intersection;
560 QSet<AnchorData *>::iterator i;
561 for (i = cPositives.begin(); i != cPositives.end(); ++i)
562 c->variables.insert(*i, 1.0);
564 for (i = cNegatives.begin(); i != cNegatives.end(); ++i)
565 c->variables.insert(*i, -1.0);
571QString GraphPath::toString()
const
574 string +=
"Path: "_L1;
576 for (AnchorData *edge : positives)
577 string += QString::fromLatin1(
" (+++) %1").arg(edge->toString());
579 for (AnchorData *edge : negatives)
580 string += QString::fromLatin1(
" (---) %1").arg(edge->toString());
586QGraphicsAnchorLayoutPrivate::QGraphicsAnchorLayoutPrivate()
587 : calculateGraphCacheDirty(
true), styleInfoDirty(
true)
591Qt::AnchorPoint QGraphicsAnchorLayoutPrivate::oppositeEdge(Qt::AnchorPoint edge)
595 edge = Qt::AnchorRight;
597 case Qt::AnchorRight:
598 edge = Qt::AnchorLeft;
601 edge = Qt::AnchorBottom;
603 case Qt::AnchorBottom:
604 edge = Qt::AnchorTop;
614
615
616
617
618
619
620
621
622
623
624
625
626AnchorData *QGraphicsAnchorLayoutPrivate::addAnchorMaybeParallel(AnchorData *newAnchor,
bool *feasible)
628 const Qt::Orientation orientation = newAnchor->isVertical ? Qt::Vertical : Qt::Horizontal;
629 Graph<AnchorVertex, AnchorData> &g = graph[orientation];
634 if (AnchorData *oldAnchor = g.takeEdge(newAnchor->from, newAnchor->to)) {
635 ParallelAnchorData *parallel =
new ParallelAnchorData(oldAnchor, newAnchor);
642 QList<QSimplexConstraint *> &constraints = itemCenterConstraints[orientation];
644 AnchorData *children[2] = { oldAnchor, newAnchor };
645 QList<QSimplexConstraint *> *childrenConstraints[2] = { ¶llel->m_firstConstraints,
646 ¶llel->m_secondConstraints };
648 for (
int i = 0; i < 2; ++i) {
649 AnchorData *child = children[i];
650 QList<QSimplexConstraint *> *childConstraints = childrenConstraints[i];
657 const bool needsReverse = i == 1 && !parallel->secondForward();
659 if (!child->isCenterAnchor)
662 parallel->isCenterAnchor =
true;
664 for (
int j = 0; j < constraints.size(); ++j) {
665 QSimplexConstraint *c = constraints[j];
666 if (c->variables.contains(child)) {
667 childConstraints->append(c);
668 qreal v = c->variables.take(child);
671 c->variables.insert(parallel, v);
678 *feasible = parallel->calculateSizeHints();
679 newAnchor = parallel;
682 g.createEdge(newAnchor->from, newAnchor->to, newAnchor);
687
688
689
690
691
692
693
694
695
697 const QList<AnchorVertex *> &vertices,
AnchorVertex *after)
699#if defined(QT_DEBUG) && 0
701 for (
int i = 0; i < vertices.count(); ++i) {
702 strVertices += QString::fromLatin1(
"%1 - ").arg(vertices.at(i)->toString());
704 QString strPath = QString::fromLatin1(
"%1 - %2%3").arg(before->toString(), strVertices, after->toString());
705 qDebug(
"simplifying [%s] to [%s - %s]", qPrintable(strPath), qPrintable(before->toString()), qPrintable(after->toString()));
709 QList<AnchorData *> edges;
710 edges.reserve(vertices.size() + 1);
712 const int numVertices = vertices.size();
713 edges.reserve(numVertices + 1);
715 for (
int i = 0; i < numVertices; ++i) {
724 AnchorData *ad = graph->takeEdge(vertices.last(), after);
731 sequence
->to = after;
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775bool QGraphicsAnchorLayoutPrivate::simplifyGraph(Qt::Orientation orientation)
780#if defined(QT_DEBUG) && 0
781 qDebug(
"Simplifying Graph for %s",
782 orientation == Horizontal ?
"Horizontal" :
"Vertical");
784 static int count = 0;
785 if (orientation == Horizontal) {
787 dumpGraph(QString::fromLatin1(
"%1-full").arg(count));
792 if (!simplifyVertices(orientation)) {
793 restoreVertices(orientation);
799 bool feasible =
true;
801 dirty = simplifyGraphIteration(orientation, &feasible);
802 }
while (dirty && feasible);
806 restoreSimplifiedGraph(orientation);
807 restoreVertices(orientation);
811#if defined(QT_DEBUG) && 0
812 dumpGraph(QString::fromLatin1(
"%1-simplified-%2").arg(count).arg(
813 QString::fromLatin1(orientation == Horizontal ?
"Horizontal" :
"Vertical")));
832bool QGraphicsAnchorLayoutPrivate::replaceVertex(Qt::Orientation orientation, AnchorVertex *oldV,
833 AnchorVertex *newV,
const QList<AnchorData *> &edges)
835 Graph<AnchorVertex, AnchorData> &g = graph[orientation];
836 bool feasible =
true;
838 for (
int i = 0; i < edges.size(); ++i) {
839 AnchorData *ad = edges[i];
840 AnchorVertex *otherV = replaceVertex_helper(ad, oldV, newV);
843 ad->name = QString::fromLatin1(
"%1 --to--> %2").arg(ad->from->toString(), ad->to->toString());
847 AnchorData *newAnchor = addAnchorMaybeParallel(ad, &newFeasible);
848 feasible &= newFeasible;
850 if (newAnchor != ad) {
854 anchorsFromSimplifiedVertices[orientation].append(newAnchor);
857 g.takeEdge(oldV, otherV);
864
865
866bool QGraphicsAnchorLayoutPrivate::simplifyVertices(Qt::Orientation orientation)
868 Q_Q(QGraphicsAnchorLayout);
869 Graph<AnchorVertex, AnchorData> &g = graph[orientation];
872 QStack<AnchorVertex *> stack;
873 stack.push(layoutFirstVertex[orientation]);
874 QSet<AnchorVertex *> visited;
876 while (!stack.isEmpty()) {
877 AnchorVertex *v = stack.pop();
883 QList<AnchorVertex *> adjacents = g.adjacentVertices(v);
886 while (index < adjacents.size()) {
887 AnchorVertex *next = adjacents.at(index);
890 AnchorData *data = g.edgeData(v, next);
891 const bool bothLayoutVertices = v->m_item == q && next->m_item == q;
892 const bool zeroSized = !data->minSize && !data->maxSize;
894 if (!bothLayoutVertices && zeroSized) {
898 AnchorVertexPair *newV =
new AnchorVertexPair(v, next, data);
899 simplifiedVertices[orientation].append(newV);
903 const QList<AnchorVertex *> &vAdjacents = g.adjacentVertices(v);
904 const QList<AnchorVertex *> &nextAdjacents = g.adjacentVertices(next);
906 for (
int i = 0; i < vAdjacents.size(); ++i) {
907 AnchorVertex *adjacent = vAdjacents.at(i);
908 if (adjacent != next) {
909 AnchorData *ad = g.edgeData(v, adjacent);
910 newV->m_firstAnchors.append(ad);
914 for (
int i = 0; i < nextAdjacents.size(); ++i) {
915 AnchorVertex *adjacent = nextAdjacents.at(i);
917 AnchorData *ad = g.edgeData(next, adjacent);
918 newV->m_secondAnchors.append(ad);
922 if (!adjacents.contains(adjacent))
923 adjacents.append(adjacent);
929 bool feasible = replaceVertex(orientation, v, newV, newV->m_firstAnchors);
930 feasible &= replaceVertex(orientation, next, newV, newV->m_secondAnchors);
933 AnchorVertex *layoutVertex =
nullptr;
936 else if (next->m_item == q)
942 changeLayoutVertex(orientation, layoutVertex, newV);
952 visited.insert(newV);
954 }
else if (!visited.contains(next) && !stack.contains(next)) {
966
967
968
969
970
971
972
973
974
975
976
977
978
979bool QGraphicsAnchorLayoutPrivate::simplifyGraphIteration(Qt::Orientation orientation,
982 Q_Q(QGraphicsAnchorLayout);
983 Graph<AnchorVertex, AnchorData> &g = graph[orientation];
985 QSet<AnchorVertex *> visited;
986 QStack<std::pair<AnchorVertex *, AnchorVertex *> > stack;
987 stack.push(std::pair(
static_cast<AnchorVertex *>(
nullptr), layoutFirstVertex[orientation]));
988 QList<AnchorVertex *> candidates;
992 while (!stack.isEmpty()) {
993 std::pair<AnchorVertex *, AnchorVertex *> pair = stack.pop();
994 AnchorVertex *beforeSequence = pair.first;
995 AnchorVertex *v = pair.second;
1007 const QList<AnchorVertex *> &adjacents = g.adjacentVertices(v);
1008 const bool isLayoutVertex = v->m_item == q;
1009 AnchorVertex *afterSequence = v;
1010 bool endOfSequence =
false;
1017 endOfSequence = isLayoutVertex || adjacents.size() != 2;
1019 if (!endOfSequence) {
1029 AnchorVertex *after;
1030 if (candidates.isEmpty())
1031 after = (beforeSequence == adjacents.last() ? adjacents.first() : adjacents.last());
1033 after = (candidates.constLast() == adjacents.last() ? adjacents.first() : adjacents.last());
1037 Q_ASSERT(!candidates.contains(after));
1039 const AnchorData *data = g.edgeData(v, after);
1041 const bool cycleFound = visited.contains(after);
1044 endOfSequence = cycleFound || data->isCenterAnchor;
1046 if (!endOfSequence) {
1049 candidates.append(v);
1050 }
else if (cycleFound && (beforeSequence != after)) {
1051 afterSequence = after;
1052 candidates.append(v);
1059 for (
int i = 0; i < adjacents.size(); ++i) {
1060 AnchorVertex *next = adjacents.at(i);
1061 if (visited.contains(next))
1069 stack.push(std::pair(v, next));
1071 stack.push(std::pair(beforeSequence, next));
1076 if (!endOfSequence || candidates.isEmpty())
1085 const AnchorData *firstAnchor = g.edgeData(beforeSequence, candidates.constFirst());
1086 if (firstAnchor->isCenterAnchor) {
1087 beforeSequence = candidates.constFirst();
1088 candidates.remove(0);
1091 if (candidates.isEmpty())
1095 const AnchorData *lastAnchor = g.edgeData(candidates.constLast(), afterSequence);
1096 if (lastAnchor->isCenterAnchor) {
1097 afterSequence = candidates.constLast();
1098 candidates.remove(candidates.size() - 1);
1100 if (candidates.isEmpty())
1108 AnchorData *sequence = createSequence(&g, beforeSequence, candidates, afterSequence);
1113 AnchorData *newAnchor = addAnchorMaybeParallel(sequence, &newFeasible);
1124 if (newAnchor != sequence)
1135void QGraphicsAnchorLayoutPrivate::restoreSimplifiedAnchor(AnchorData *edge)
1137 const Qt::Orientation orientation = edge->isVertical ? Qt::Vertical : Qt::Horizontal;
1139 static const char *anchortypes[] = {
"Normal",
1142 qDebug(
"Restoring %s edge.", anchortypes[
int(edge->type)]);
1145 Graph<AnchorVertex, AnchorData> &g = graph[orientation];
1147 if (edge->type == AnchorData::Normal) {
1148 g.createEdge(edge->from, edge->to, edge);
1150 }
else if (edge->type == AnchorData::Sequential) {
1151 const auto *sequence =
static_cast<SequentialAnchorData *>(edge);
1153 for (AnchorData *data : sequence->m_edges)
1154 restoreSimplifiedAnchor(data);
1158 }
else if (edge->type == AnchorData::Parallel) {
1163 if (anchorsFromSimplifiedVertices[orientation].contains(edge))
1166 ParallelAnchorData* parallel =
static_cast<ParallelAnchorData*>(edge);
1167 restoreSimplifiedConstraints(parallel);
1172 Q_ASSERT(parallel->firstEdge->type == AnchorData::Sequential
1173 || parallel->secondEdge->type == AnchorData::Sequential);
1174 restoreSimplifiedAnchor(parallel->firstEdge);
1175 restoreSimplifiedAnchor(parallel->secondEdge);
1181void QGraphicsAnchorLayoutPrivate::restoreSimplifiedConstraints(ParallelAnchorData *parallel)
1183 if (!parallel->isCenterAnchor)
1186 for (
int i = 0; i < parallel->m_firstConstraints.size(); ++i) {
1187 QSimplexConstraint *c = parallel->m_firstConstraints.at(i);
1188 qreal v = c->variables[parallel];
1189 c->variables.remove(parallel);
1190 c->variables.insert(parallel->firstEdge, v);
1195 const bool needsReverse = !parallel->secondForward();
1197 for (
int i = 0; i < parallel->m_secondConstraints.size(); ++i) {
1198 QSimplexConstraint *c = parallel->m_secondConstraints.at(i);
1199 qreal v = c->variables[parallel];
1202 c->variables.remove(parallel);
1203 c->variables.insert(parallel->secondEdge, v);
1207void QGraphicsAnchorLayoutPrivate::restoreSimplifiedGraph(Qt::Orientation orientation)
1210 qDebug(
"Restoring Simplified Graph for %s",
1211 orientation == Horizontal ?
"Horizontal" :
"Vertical");
1215 Graph<AnchorVertex, AnchorData> &g = graph[orientation];
1216 QList<std::pair<AnchorVertex *, AnchorVertex *>> connections = g.connections();
1217 for (
int i = 0; i < connections.size(); ++i) {
1218 AnchorVertex *v1 = connections.at(i).first;
1219 AnchorVertex *v2 = connections.at(i).second;
1220 AnchorData *edge = g.edgeData(v1, v2);
1224 if (edge->type == AnchorData::Sequential
1225 || (edge->type == AnchorData::Parallel &&
1226 !anchorsFromSimplifiedVertices[orientation].contains(edge))) {
1229 restoreSimplifiedAnchor(edge);
1233 restoreVertices(orientation);
1236void QGraphicsAnchorLayoutPrivate::restoreVertices(Qt::Orientation orientation)
1238 Q_Q(QGraphicsAnchorLayout);
1240 Graph<AnchorVertex, AnchorData> &g = graph[orientation];
1241 QList<AnchorVertexPair *> &toRestore = simplifiedVertices[orientation];
1251 QList<AnchorData *> ¶llelAnchors = anchorsFromSimplifiedVertices[orientation];
1253 for (
int i = parallelAnchors.size() - 1; i >= 0; --i) {
1254 ParallelAnchorData *parallel =
static_cast<ParallelAnchorData *>(parallelAnchors.at(i));
1255 restoreSimplifiedConstraints(parallel);
1260 for (
int i = toRestore.size() - 1; i >= 0; --i) {
1261 AnchorVertexPair *pair = toRestore.at(i);
1262 QList<AnchorVertex *> adjacents = g.adjacentVertices(pair);
1266 AnchorVertex *first = pair->m_first;
1267 AnchorVertex *second = pair->m_second;
1268 g.createEdge(first, second, pair->m_removedAnchor);
1271 for (
int j = 0; j < pair->m_firstAnchors.size(); ++j) {
1272 AnchorData *ad = pair->m_firstAnchors.at(j);
1273 Q_ASSERT(ad->from == pair || ad->to == pair);
1275 replaceVertex_helper(ad, pair, first);
1276 g.createEdge(ad->from, ad->to, ad);
1280 for (
int j = 0; j < pair->m_secondAnchors.size(); ++j) {
1281 AnchorData *ad = pair->m_secondAnchors.at(j);
1282 Q_ASSERT(ad->from == pair || ad->to == pair);
1284 replaceVertex_helper(ad, pair, second);
1285 g.createEdge(ad->from, ad->to, ad);
1288 for (
int j = 0; j < adjacents.size(); ++j) {
1289 g.takeEdge(pair, adjacents.at(j));
1294 if (pair->m_item == q) {
1295 AnchorVertex *layoutVertex = first->m_item == q ? first : second;
1296 Q_ASSERT(layoutVertex->m_item == q);
1297 changeLayoutVertex(orientation, pair, layoutVertex);
1302 qDeleteAll(parallelAnchors);
1303 parallelAnchors.clear();
1308QGraphicsAnchorLayoutPrivate::edgeOrientation(Qt::AnchorPoint edge)
noexcept
1310 return edge > Qt::AnchorRight ? Qt::Vertical : Qt::Horizontal;
1314
1315
1316
1317
1318
1319
1320
1321
1322void QGraphicsAnchorLayoutPrivate::createLayoutEdges()
1324 Q_Q(QGraphicsAnchorLayout);
1325 QGraphicsLayoutItem *layout = q;
1328 AnchorData *data =
new AnchorData;
1329 addAnchor_helper(layout, Qt::AnchorLeft, layout,
1330 Qt::AnchorRight, data);
1331 data->maxSize = QWIDGETSIZE_MAX;
1334 layoutFirstVertex[Qt::Horizontal] = internalVertex(layout, Qt::AnchorLeft);
1335 layoutCentralVertex[Qt::Horizontal] =
nullptr;
1336 layoutLastVertex[Qt::Horizontal] = internalVertex(layout, Qt::AnchorRight);
1339 data =
new AnchorData;
1340 addAnchor_helper(layout, Qt::AnchorTop, layout,
1341 Qt::AnchorBottom, data);
1342 data->maxSize = QWIDGETSIZE_MAX;
1345 layoutFirstVertex[Qt::Vertical] = internalVertex(layout, Qt::AnchorTop);
1346 layoutCentralVertex[Qt::Vertical] =
nullptr;
1347 layoutLastVertex[Qt::Vertical] = internalVertex(layout, Qt::AnchorBottom);
1350void QGraphicsAnchorLayoutPrivate::deleteLayoutEdges()
1352 Q_Q(QGraphicsAnchorLayout);
1354 Q_ASSERT(!internalVertex(q, Qt::AnchorHorizontalCenter));
1355 Q_ASSERT(!internalVertex(q, Qt::AnchorVerticalCenter));
1357 removeAnchor_helper(internalVertex(q, Qt::AnchorLeft),
1358 internalVertex(q, Qt::AnchorRight));
1359 removeAnchor_helper(internalVertex(q, Qt::AnchorTop),
1360 internalVertex(q, Qt::AnchorBottom));
1363void QGraphicsAnchorLayoutPrivate::createItemEdges(QGraphicsLayoutItem *item)
1369 AnchorData *data =
new AnchorData;
1370 addAnchor_helper(item, Qt::AnchorLeft, item, Qt::AnchorRight, data);
1371 data->refreshSizeHints();
1373 data =
new AnchorData;
1374 addAnchor_helper(item, Qt::AnchorTop, item, Qt::AnchorBottom, data);
1375 data->refreshSizeHints();
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389void QGraphicsAnchorLayoutPrivate::createCenterAnchors(
1390 QGraphicsLayoutItem *item, Qt::AnchorPoint centerEdge)
1392 Q_Q(QGraphicsAnchorLayout);
1394 Qt::Orientation orientation;
1395 switch (centerEdge) {
1396 case Qt::AnchorHorizontalCenter:
1397 orientation = Qt::Horizontal;
1399 case Qt::AnchorVerticalCenter:
1400 orientation = Qt::Vertical;
1408 if (internalVertex(item, centerEdge))
1412 Qt::AnchorPoint firstEdge;
1413 Qt::AnchorPoint lastEdge;
1415 if (orientation == Qt::Horizontal) {
1416 firstEdge = Qt::AnchorLeft;
1417 lastEdge = Qt::AnchorRight;
1419 firstEdge = Qt::AnchorTop;
1420 lastEdge = Qt::AnchorBottom;
1423 AnchorVertex *first = internalVertex(item, firstEdge);
1424 AnchorVertex *last = internalVertex(item, lastEdge);
1425 Q_ASSERT(first && last);
1428 QSimplexConstraint *c =
new QSimplexConstraint;
1430 AnchorData *data =
new AnchorData;
1431 c->variables.insert(data, 1.0);
1432 addAnchor_helper(item, firstEdge, item, centerEdge, data);
1433 data->isCenterAnchor =
true;
1434 data->dependency = AnchorData::Master;
1435 data->refreshSizeHints();
1437 data =
new AnchorData;
1438 c->variables.insert(data, -1.0);
1439 addAnchor_helper(item, centerEdge, item, lastEdge, data);
1440 data->isCenterAnchor =
true;
1441 data->dependency = AnchorData::Slave;
1442 data->refreshSizeHints();
1444 itemCenterConstraints[orientation].append(c);
1447 removeAnchor_helper(first, last);
1450 layoutCentralVertex[orientation] = internalVertex(q, centerEdge);
1454void QGraphicsAnchorLayoutPrivate::removeCenterAnchors(
1455 QGraphicsLayoutItem *item, Qt::AnchorPoint centerEdge,
1458 Q_Q(QGraphicsAnchorLayout);
1460 Qt::Orientation orientation;
1461 switch (centerEdge) {
1462 case Qt::AnchorHorizontalCenter:
1463 orientation = Qt::Horizontal;
1465 case Qt::AnchorVerticalCenter:
1466 orientation = Qt::Vertical;
1474 Qt::AnchorPoint firstEdge;
1475 Qt::AnchorPoint lastEdge;
1477 if (orientation == Qt::Horizontal) {
1478 firstEdge = Qt::AnchorLeft;
1479 lastEdge = Qt::AnchorRight;
1481 firstEdge = Qt::AnchorTop;
1482 lastEdge = Qt::AnchorBottom;
1485 AnchorVertex *center = internalVertex(item, centerEdge);
1488 AnchorVertex *first = internalVertex(item, firstEdge);
1493 Graph<AnchorVertex, AnchorData> &g = graph[orientation];
1496 AnchorData *oldData = g.edgeData(first, center);
1498 for (
int i = itemCenterConstraints[orientation].size() - 1; i >= 0; --i) {
1499 if (itemCenterConstraints[orientation].at(i)->variables.contains(oldData)) {
1500 delete itemCenterConstraints[orientation].takeAt(i);
1507 AnchorData *data =
new AnchorData;
1508 addAnchor_helper(item, firstEdge, item, lastEdge, data);
1509 data->refreshSizeHints();
1512 removeAnchor_helper(first, center);
1513 removeAnchor_helper(center, internalVertex(item, lastEdge));
1518 QList<AnchorVertex*> adjacents = g.adjacentVertices(center);
1519 for (
int i = 0; i < adjacents.size(); ++i) {
1520 AnchorVertex *v = adjacents.at(i);
1521 if (v->m_item != item) {
1522 removeAnchor_helper(center, internalVertex(v->m_item, v->m_edge));
1528 removeAnchor_helper(first, internalVertex(item, lastEdge));
1532 layoutCentralVertex[orientation] =
nullptr;
1537void QGraphicsAnchorLayoutPrivate::removeCenterConstraints(QGraphicsLayoutItem *item,
1538 Qt::Orientation orientation)
1545 AnchorVertex *first = internalVertex(item, orientation == Qt::Horizontal ?
1548 AnchorVertex *center = internalVertex(item, orientation == Qt::Horizontal ?
1549 Qt::AnchorHorizontalCenter :
1550 Qt::AnchorVerticalCenter);
1557 AnchorData *internalAnchor = graph[orientation].edgeData(first, center);
1560 for (
int i = 0; i < itemCenterConstraints[orientation].size(); ++i) {
1561 if (itemCenterConstraints[orientation].at(i)->variables.contains(internalAnchor)) {
1562 delete itemCenterConstraints[orientation].takeAt(i);
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580QGraphicsAnchor *QGraphicsAnchorLayoutPrivate::addAnchor(QGraphicsLayoutItem *firstItem,
1581 Qt::AnchorPoint firstEdge,
1582 QGraphicsLayoutItem *secondItem,
1583 Qt::AnchorPoint secondEdge,
1586 Q_Q(QGraphicsAnchorLayout);
1587 if ((firstItem ==
nullptr) || (secondItem ==
nullptr)) {
1588 qWarning(
"QGraphicsAnchorLayout::addAnchor(): "
1589 "Cannot anchor NULL items");
1593 if (firstItem == secondItem) {
1594 qWarning(
"QGraphicsAnchorLayout::addAnchor(): "
1595 "Cannot anchor the item to itself");
1599 if (edgeOrientation(secondEdge) != edgeOrientation(firstEdge)) {
1600 qWarning(
"QGraphicsAnchorLayout::addAnchor(): "
1601 "Cannot anchor edges of different orientations");
1605 const QGraphicsLayoutItem *parentWidget = q->parentLayoutItem();
1606 if (firstItem == parentWidget || secondItem == parentWidget) {
1607 qWarning(
"QGraphicsAnchorLayout::addAnchor(): "
1608 "You cannot add the parent of the layout to the layout.");
1620 if (firstItem != q && !items.contains(firstItem)) {
1621 createItemEdges(firstItem);
1622 addChildLayoutItem(firstItem);
1624 if (secondItem != q && !items.contains(secondItem)) {
1625 createItemEdges(secondItem);
1626 addChildLayoutItem(secondItem);
1630 createCenterAnchors(firstItem, firstEdge);
1631 createCenterAnchors(secondItem, secondEdge);
1634 correctEdgeDirection(firstItem, firstEdge, secondItem, secondEdge);
1636 AnchorData *data =
new AnchorData;
1637 QGraphicsAnchor *graphicsAnchor = acquireGraphicsAnchor(data);
1639 addAnchor_helper(firstItem, firstEdge, secondItem, secondEdge, data);
1642 graphicsAnchor->setSpacing(*spacing);
1654 || pickEdge(firstEdge, Qt::Horizontal) == Qt::AnchorHorizontalCenter
1655 || oppositeEdge(firstEdge) != secondEdge) {
1656 graphicsAnchor->setSpacing(0);
1658 graphicsAnchor->unsetSpacing();
1662 return graphicsAnchor;
1666
1667
1668
1669
1670
1671
1672
1673
1674void QGraphicsAnchorLayoutPrivate::addAnchor_helper(QGraphicsLayoutItem *firstItem,
1675 Qt::AnchorPoint firstEdge,
1676 QGraphicsLayoutItem *secondItem,
1677 Qt::AnchorPoint secondEdge,
1680 Q_Q(QGraphicsAnchorLayout);
1682 const Qt::Orientation orientation = edgeOrientation(firstEdge);
1685 AnchorVertex *v1 = addInternalVertex(firstItem, firstEdge);
1686 AnchorVertex *v2 = addInternalVertex(secondItem, secondEdge);
1689 if (graph[orientation].edgeData(v1, v2)) {
1690 removeAnchor_helper(v1, v2);
1694 if (firstItem == secondItem)
1695 data->item = firstItem;
1697 data->isVertical = orientation == Qt::Vertical;
1705 data->name = QString::fromLatin1(
"%1 --to--> %2").arg(v1->toString(), v2->toString());
1709 data->isLayoutAnchor = (data->item == q);
1711 graph[orientation].createEdge(v1, v2, data);
1714QGraphicsAnchor *QGraphicsAnchorLayoutPrivate::getAnchor(QGraphicsLayoutItem *firstItem,
1715 Qt::AnchorPoint firstEdge,
1716 QGraphicsLayoutItem *secondItem,
1717 Qt::AnchorPoint secondEdge)
1720 if (firstItem == secondItem)
1723 const Qt::Orientation orientation = edgeOrientation(firstEdge);
1724 AnchorVertex *v1 = internalVertex(firstItem, firstEdge);
1725 AnchorVertex *v2 = internalVertex(secondItem, secondEdge);
1727 QGraphicsAnchor *graphicsAnchor =
nullptr;
1729 AnchorData *data = graph[orientation].edgeData(v1, v2);
1737 Q_ASSERT(data->graphicsAnchor);
1738 graphicsAnchor = data->graphicsAnchor;
1740 return graphicsAnchor;
1744
1745
1746
1747
1748
1749void QGraphicsAnchorLayoutPrivate::removeAnchor(AnchorVertex *firstVertex,
1750 AnchorVertex *secondVertex)
1752 Q_Q(QGraphicsAnchorLayout);
1755 QGraphicsLayoutItem *firstItem = firstVertex->m_item;
1756 QGraphicsLayoutItem *secondItem = secondVertex->m_item;
1759 removeAnchor_helper(firstVertex, secondVertex);
1762 firstVertex = secondVertex =
nullptr;
1765 bool keepFirstItem =
false;
1766 bool keepSecondItem =
false;
1768 std::pair<AnchorVertex *,
int> v;
1771 if (firstItem != q) {
1772 for (
int i = Qt::AnchorLeft; i <= Qt::AnchorBottom; ++i) {
1773 v = m_vertexList.value(std::pair(firstItem,
static_cast<Qt::AnchorPoint>(i)));
1775 if (i == Qt::AnchorHorizontalCenter || i == Qt::AnchorVerticalCenter)
1780 if (v.second > refcount) {
1781 keepFirstItem =
true;
1787 keepFirstItem =
true;
1789 if (secondItem != q) {
1790 for (
int i = Qt::AnchorLeft; i <= Qt::AnchorBottom; ++i) {
1791 v = m_vertexList.value(std::pair(secondItem,
static_cast<Qt::AnchorPoint>(i)));
1793 if (i == Qt::AnchorHorizontalCenter || i == Qt::AnchorVerticalCenter)
1798 if (v.second > refcount) {
1799 keepSecondItem =
true;
1805 keepSecondItem =
true;
1808 q->removeAt(items.indexOf(firstItem));
1810 if (!keepSecondItem)
1811 q->removeAt(items.indexOf(secondItem));
1818
1819
1820
1821
1822
1823void QGraphicsAnchorLayoutPrivate::removeAnchor_helper(AnchorVertex *v1, AnchorVertex *v2)
1828 const Qt::Orientation o = edgeOrientation(v1->m_edge);
1829 graph[o].removeEdge(v1, v2);
1832 removeInternalVertex(v1->m_item, v1->m_edge);
1833 removeInternalVertex(v2->m_item, v2->m_edge);
1836AnchorVertex *QGraphicsAnchorLayoutPrivate::addInternalVertex(QGraphicsLayoutItem *item,
1837 Qt::AnchorPoint edge)
1839 std::pair<QGraphicsLayoutItem *, Qt::AnchorPoint> pair(item, edge);
1840 std::pair<AnchorVertex *,
int> v = m_vertexList.value(pair);
1843 Q_ASSERT(v.second == 0);
1844 v.first =
new AnchorVertex(item, edge);
1847 m_vertexList.insert(pair, v);
1852
1853
1854
1855
1856
1857void QGraphicsAnchorLayoutPrivate::removeInternalVertex(QGraphicsLayoutItem *item,
1858 Qt::AnchorPoint edge)
1860 std::pair<QGraphicsLayoutItem *, Qt::AnchorPoint> pair(item, edge);
1861 std::pair<AnchorVertex *,
int> v = m_vertexList.value(pair);
1864 qWarning(
"This item with this edge is not in the graph");
1869 if (v.second == 0) {
1871 m_vertexList.remove(pair);
1875 m_vertexList.insert(pair, v);
1877 if ((v.second == 2) &&
1878 ((edge == Qt::AnchorHorizontalCenter) ||
1879 (edge == Qt::AnchorVerticalCenter))) {
1880 removeCenterAnchors(item, edge,
true);
1885void QGraphicsAnchorLayoutPrivate::removeVertex(QGraphicsLayoutItem *item, Qt::AnchorPoint edge)
1887 if (AnchorVertex *v = internalVertex(item, edge)) {
1888 Graph<AnchorVertex, AnchorData> &g = graph[edgeOrientation(edge)];
1889 const auto allVertices = g.adjacentVertices(v);
1890 for (
auto *v2 : allVertices) {
1891 g.removeEdge(v, v2);
1892 removeInternalVertex(item, edge);
1893 removeInternalVertex(v2->m_item, v2->m_edge);
1898void QGraphicsAnchorLayoutPrivate::removeAnchors(QGraphicsLayoutItem *item)
1901 removeCenterAnchors(item, Qt::AnchorHorizontalCenter,
false);
1902 removeVertex(item, Qt::AnchorLeft);
1903 removeVertex(item, Qt::AnchorRight);
1905 removeCenterAnchors(item, Qt::AnchorVerticalCenter,
false);
1906 removeVertex(item, Qt::AnchorTop);
1907 removeVertex(item, Qt::AnchorBottom);
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934void QGraphicsAnchorLayoutPrivate::correctEdgeDirection(QGraphicsLayoutItem *&firstItem,
1935 Qt::AnchorPoint &firstEdge,
1936 QGraphicsLayoutItem *&secondItem,
1937 Qt::AnchorPoint &secondEdge)
1939 Q_Q(QGraphicsAnchorLayout);
1941 if ((firstItem != q) && (secondItem != q)) {
1944 if (firstEdge < secondEdge) {
1945 qSwap(firstItem, secondItem);
1946 qSwap(firstEdge, secondEdge);
1948 }
else if (firstItem == q) {
1951 if ((firstEdge == Qt::AnchorRight) || (firstEdge == Qt::AnchorBottom)) {
1952 qSwap(firstItem, secondItem);
1953 qSwap(firstEdge, secondEdge);
1955 }
else if ((secondEdge != Qt::AnchorRight) && (secondEdge != Qt::AnchorBottom)) {
1958 qSwap(firstItem, secondItem);
1959 qSwap(firstEdge, secondEdge);
1963QLayoutStyleInfo &QGraphicsAnchorLayoutPrivate::styleInfo()
const
1965 if (styleInfoDirty) {
1966 Q_Q(
const QGraphicsAnchorLayout);
1968 QWidget *wid =
nullptr;
1970 QGraphicsLayoutItem *parent = q->parentLayoutItem();
1971 while (parent && parent->isLayout()) {
1972 parent = parent->parentLayoutItem();
1974 QGraphicsWidget *w =
nullptr;
1976 QGraphicsItem *parentItem = parent->graphicsItem();
1977 if (parentItem && parentItem->isWidget())
1978 w =
static_cast<QGraphicsWidget*>(parentItem);
1981 QStyle *style = w ? w->style() : QApplication::style();
1982 cachedStyleInfo = QLayoutStyleInfo(style, wid);
1983 cachedStyleInfo.setDefaultSpacing(Qt::Horizontal, spacings[Qt::Horizontal]);
1984 cachedStyleInfo.setDefaultSpacing(Qt::Vertical, spacings[Qt::Vertical]);
1986 styleInfoDirty =
false;
1988 return cachedStyleInfo;
1992
1993
1994
1995
1996
1997
1998void QGraphicsAnchorLayoutPrivate::calculateGraphs()
2000 if (!calculateGraphCacheDirty)
2002 calculateGraphs(Qt::Horizontal);
2003 calculateGraphs(Qt::Vertical);
2004 calculateGraphCacheDirty =
false;
2011 QSet<AnchorData *> variableSet;
2012 for (
int i = 0; i < constraints.size(); ++i) {
2014 for (
auto it = c->variables.cbegin(), end = c->variables.cend(); it != end; ++it)
2015 variableSet.insert(
static_cast<
AnchorData *>(it.key()));
2017 return variableSet.values();
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043void QGraphicsAnchorLayoutPrivate::calculateGraphs(Qt::Orientation orientation)
2045#if defined(QT_DEBUG) || defined(QT_BUILD_INTERNAL)
2046 lastCalculationUsedSimplex[orientation] =
false;
2049 static bool simplificationEnabled = qEnvironmentVariableIsEmpty(
"QT_ANCHORLAYOUT_NO_SIMPLIFICATION");
2052 refreshAllSizeHints(orientation);
2055 if (simplificationEnabled && !simplifyGraph(orientation)) {
2056 qWarning(
"QGraphicsAnchorLayout: anchor setup is not feasible.");
2057 graphHasConflicts[orientation] =
true;
2062 findPaths(orientation);
2066 constraintsFromPaths(orientation);
2078 const auto parts = getGraphParts(orientation);
2083 const QList<AnchorData *> trunkVariables = getVariables(parts.trunkConstraints);
2087 AnchorVertex *v = layoutLastVertex[orientation];
2088 GraphPath trunkPath = graphPaths[orientation].value(v);
2090 bool feasible = calculateTrunk(orientation, trunkPath, parts.trunkConstraints, trunkVariables);
2096 if (feasible && !parts.nonTrunkConstraints.isEmpty()) {
2097 const QList<AnchorData *> partVariables = getVariables(parts.nonTrunkConstraints);
2098 Q_ASSERT(!partVariables.isEmpty());
2099 feasible = calculateNonTrunk(parts.nonTrunkConstraints, partVariables);
2104 updateAnchorSizes(orientation);
2106 graphHasConflicts[orientation] = !feasible;
2110 qDeleteAll(constraints[orientation]);
2111 constraints[orientation].clear();
2112 graphPaths[orientation].clear();
2114 if (simplificationEnabled)
2115 restoreSimplifiedGraph(orientation);
2119
2120
2121
2122
2123
2124
2127 for (
int i = 0; i < constraints.size(); ++i) {
2129 const qreal multiplier =
std::accumulate(c->variables.cbegin(), c->variables.cend(), qreal(0));
2130 c->constant += multiplier * amount;
2135
2136
2137
2138
2139
2140bool QGraphicsAnchorLayoutPrivate::calculateTrunk(Qt::Orientation orientation,
const GraphPath &path,
2141 const QList<QSimplexConstraint *> &constraints,
2142 const QList<AnchorData *> &variables)
2144 bool feasible =
true;
2145 bool needsSimplex = !constraints.isEmpty();
2148 qDebug(
"Simplex %s for trunk of %s", needsSimplex ?
"used" :
"NOT used",
2149 orientation == Qt::Horizontal ?
"Horizontal" :
"Vertical");
2154 QList<QSimplexConstraint *> sizeHintConstraints = constraintsFromSizeHints(variables);
2155 QList<QSimplexConstraint *> allConstraints = constraints + sizeHintConstraints;
2157 shiftConstraints(allConstraints, g_offset);
2161 feasible = solveMinMax(allConstraints, path, &min, &max);
2164 solvePreferred(constraints, variables);
2169 for (
const AnchorData *ad : path.positives)
2170 pref += ad->sizeAtPreferred;
2171 for (
const AnchorData *ad : path.negatives)
2172 pref -= ad->sizeAtPreferred;
2174 sizeHints[orientation][Qt::MinimumSize] = min;
2175 sizeHints[orientation][Qt::PreferredSize] = pref;
2176 sizeHints[orientation][Qt::MaximumSize] = max;
2179 qDeleteAll(sizeHintConstraints);
2180 shiftConstraints(constraints, -g_offset);
2185 Q_ASSERT(path.positives.size() == 1);
2186 Q_ASSERT(path.negatives.size() == 0);
2188 AnchorData *ad = *path.positives.cbegin();
2189 ad->sizeAtMinimum = ad->minSize;
2190 ad->sizeAtPreferred = ad->prefSize;
2191 ad->sizeAtMaximum = ad->maxSize;
2193 sizeHints[orientation][Qt::MinimumSize] = ad->sizeAtMinimum;
2194 sizeHints[orientation][Qt::PreferredSize] = ad->sizeAtPreferred;
2195 sizeHints[orientation][Qt::MaximumSize] = ad->sizeAtMaximum;
2198#if defined(QT_DEBUG) || defined(QT_BUILD_INTERNAL)
2199 lastCalculationUsedSimplex[orientation] = needsSimplex;
2206
2207
2208bool QGraphicsAnchorLayoutPrivate::calculateNonTrunk(
const QList<QSimplexConstraint *> &constraints,
2209 const QList<AnchorData *> &variables)
2211 shiftConstraints(constraints, g_offset);
2212 bool feasible = solvePreferred(constraints, variables);
2217 for (
int j = 0; j < variables.size(); ++j) {
2218 AnchorData *ad = variables.at(j);
2220 ad->sizeAtMinimum = ad->sizeAtPreferred;
2221 ad->sizeAtMaximum = ad->sizeAtPreferred;
2225 shiftConstraints(constraints, -g_offset);
2230
2231
2232
2233
2234
2235void QGraphicsAnchorLayoutPrivate::refreshAllSizeHints(Qt::Orientation orientation)
2237 Graph<AnchorVertex, AnchorData> &g = graph[orientation];
2238 QList<std::pair<AnchorVertex *, AnchorVertex *>> vertices = g.connections();
2240 QLayoutStyleInfo styleInf = styleInfo();
2241 for (
int i = 0; i < vertices.size(); ++i) {
2242 AnchorData *data = g.edgeData(vertices.at(i).first, vertices.at(i).second);
2243 data->refreshSizeHints(&styleInf);
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257void QGraphicsAnchorLayoutPrivate::findPaths(Qt::Orientation orientation)
2259 QQueue<std::pair<AnchorVertex *, AnchorVertex *> > queue;
2261 QSet<AnchorData *> visited;
2263 AnchorVertex *root = layoutFirstVertex[orientation];
2265 graphPaths[orientation].insert(root, GraphPath());
2267 const auto adjacentVertices = graph[orientation].adjacentVertices(root);
2268 for (AnchorVertex *v : adjacentVertices)
2269 queue.enqueue(std::pair(root, v));
2271 while(!queue.isEmpty()) {
2272 std::pair<AnchorVertex *, AnchorVertex *> pair = queue.dequeue();
2273 AnchorData *edge = graph[orientation].edgeData(pair.first, pair.second);
2275 if (visited.contains(edge))
2278 visited.insert(edge);
2279 GraphPath current = graphPaths[orientation].value(pair.first);
2281 if (edge->from == pair.first)
2282 current.positives.insert(edge);
2284 current.negatives.insert(edge);
2286 graphPaths[orientation].insert(pair.second, current);
2288 const auto adjacentVertices = graph[orientation].adjacentVertices(pair.second);
2289 for (AnchorVertex *v : adjacentVertices)
2290 queue.enqueue(std::pair(pair.second, v));
2296 identifyFloatItems(visited, orientation);
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309void QGraphicsAnchorLayoutPrivate::constraintsFromPaths(Qt::Orientation orientation)
2311 const auto vertices = graphPaths[orientation].uniqueKeys();
2312 for (AnchorVertex *vertex : vertices) {
2313 int valueCount = graphPaths[orientation].count(vertex);
2314 if (valueCount == 1)
2317 QList<GraphPath> pathsToVertex = graphPaths[orientation].values(vertex);
2318 for (
int i = 1; i < valueCount; ++i) {
2319 constraints[orientation] +=
2320 pathsToVertex[0].constraint(pathsToVertex.at(i));
2326
2327
2328void QGraphicsAnchorLayoutPrivate::updateAnchorSizes(Qt::Orientation orientation)
2330 Graph<AnchorVertex, AnchorData> &g = graph[orientation];
2331 const QList<std::pair<AnchorVertex *, AnchorVertex *>> &vertices = g.connections();
2333 for (
int i = 0; i < vertices.size(); ++i) {
2334 AnchorData *ad = g.edgeData(vertices.at(i).first, vertices.at(i).second);
2335 ad->updateChildrenSizes();
2340
2341
2342
2343
2344
2345QList<QSimplexConstraint *> QGraphicsAnchorLayoutPrivate::constraintsFromSizeHints(
2346 const QList<AnchorData *> &anchors)
2348 if (anchors.isEmpty())
2349 return QList<QSimplexConstraint *>();
2353 const Qt::Orientation orient = anchors.first()->isVertical ? Qt::Vertical : Qt::Horizontal;
2354 AnchorData *layoutEdge =
nullptr;
2355 if (layoutCentralVertex[orient]) {
2356 layoutEdge = graph[orient].edgeData(layoutFirstVertex[orient], layoutCentralVertex[orient]);
2358 layoutEdge = graph[orient].edgeData(layoutFirstVertex[orient], layoutLastVertex[orient]);
2365 const qreal expectedMax = layoutCentralVertex[orient] ? QWIDGETSIZE_MAX / 2 : QWIDGETSIZE_MAX;
2367 if (layoutEdge->from == layoutFirstVertex[orient]) {
2368 actualMax = layoutEdge->maxSize;
2370 actualMax = -layoutEdge->minSize;
2372 if (actualMax != expectedMax) {
2373 layoutEdge =
nullptr;
2377 QList<QSimplexConstraint *> anchorConstraints;
2378 bool unboundedProblem =
true;
2379 for (
int i = 0; i < anchors.size(); ++i) {
2380 AnchorData *ad = anchors.at(i);
2385 if (ad->dependency == AnchorData::Slave)
2391 qreal boundedMin = qBound(-g_offset, ad->minSize, g_offset);
2392 qreal boundedMax = qBound(-g_offset, ad->maxSize, g_offset);
2394 if ((boundedMin == boundedMax) || qFuzzyCompare(boundedMin, boundedMax)) {
2395 QSimplexConstraint *c =
new QSimplexConstraint;
2396 c->variables.insert(ad, 1.0);
2397 c->constant = boundedMin;
2398 c->ratio = QSimplexConstraint::Equal;
2399 anchorConstraints += c;
2400 unboundedProblem =
false;
2402 QSimplexConstraint *c =
new QSimplexConstraint;
2403 c->variables.insert(ad, 1.0);
2404 c->constant = boundedMin;
2405 c->ratio = QSimplexConstraint::MoreOrEqual;
2406 anchorConstraints += c;
2411 if (ad == layoutEdge)
2414 c =
new QSimplexConstraint;
2415 c->variables.insert(ad, 1.0);
2416 c->constant = boundedMax;
2417 c->ratio = QSimplexConstraint::LessOrEqual;
2418 anchorConstraints += c;
2419 unboundedProblem =
false;
2424 if (unboundedProblem) {
2425 QSimplexConstraint *c =
new QSimplexConstraint;
2426 c->variables.insert(layoutEdge, 1.0);
2428 c->constant = g_offset;
2429 c->ratio = QSimplexConstraint::LessOrEqual;
2430 anchorConstraints += c;
2433 return anchorConstraints;
2437
2438
2439QGraphicsAnchorLayoutPrivate::GraphParts
2440QGraphicsAnchorLayoutPrivate::getGraphParts(Qt::Orientation orientation)
2444 Q_ASSERT(layoutFirstVertex[orientation] && layoutLastVertex[orientation]);
2446 AnchorData *edgeL1 =
nullptr;
2447 AnchorData *edgeL2 =
nullptr;
2451 if (layoutCentralVertex[orientation]) {
2452 edgeL1 = graph[orientation].edgeData(layoutFirstVertex[orientation], layoutCentralVertex[orientation]);
2453 edgeL2 = graph[orientation].edgeData(layoutCentralVertex[orientation], layoutLastVertex[orientation]);
2455 edgeL1 = graph[orientation].edgeData(layoutFirstVertex[orientation], layoutLastVertex[orientation]);
2458 result.nonTrunkConstraints = constraints[orientation] + itemCenterConstraints[orientation];
2460 QSet<QSimplexVariable *> trunkVariables;
2462 trunkVariables += edgeL1;
2464 trunkVariables += edgeL2;
2467 auto end = result.nonTrunkConstraints.end();
2471 auto isMatch = [&result, &trunkVariables](QSimplexConstraint *c) ->
bool {
2476 for (QSimplexVariable *ad : std::as_const(trunkVariables)) {
2477 if (c->variables.contains(ad)) {
2486 result.trunkConstraints += c;
2487 for (
auto jt = c->variables.cbegin(), end = c->variables.cend(); jt != end; ++jt)
2488 trunkVariables.insert(jt.key());
2502 const auto newEnd = std::remove_if(result.nonTrunkConstraints.begin(), end, isMatch);
2503 dirty = newEnd != end;
2507 result.nonTrunkConstraints.erase(end, result.nonTrunkConstraints.end());
2513
2514
2515
2516
2517void QGraphicsAnchorLayoutPrivate::identifyFloatItems(
const QSet<AnchorData *> &visited, Qt::Orientation orientation)
2519 QSet<QGraphicsLayoutItem *> nonFloating;
2521 for (
const AnchorData *ad : visited)
2522 identifyNonFloatItems_helper(ad, &nonFloating);
2524 QSet<QGraphicsLayoutItem *> floatItems;
2525 for (QGraphicsLayoutItem *item : std::as_const(items)) {
2526 if (!nonFloating.contains(item))
2527 floatItems.insert(item);
2529 m_floatItems[orientation] = std::move(floatItems);
2534
2535
2536
2537
2538
2539
2540void QGraphicsAnchorLayoutPrivate::identifyNonFloatItems_helper(
const AnchorData *ad, QSet<QGraphicsLayoutItem *> *nonFloatingItemsIdentifiedSoFar)
2542 Q_Q(QGraphicsAnchorLayout);
2545 case AnchorData::Normal:
2546 if (ad->item && ad->item != q)
2547 nonFloatingItemsIdentifiedSoFar->insert(ad->item);
2549 case AnchorData::Sequential:
2550 for (
const AnchorData *d :
static_cast<
const SequentialAnchorData *>(ad)->m_edges)
2551 identifyNonFloatItems_helper(d, nonFloatingItemsIdentifiedSoFar);
2553 case AnchorData::Parallel:
2554 identifyNonFloatItems_helper(
static_cast<
const ParallelAnchorData *>(ad)->firstEdge, nonFloatingItemsIdentifiedSoFar);
2555 identifyNonFloatItems_helper(
static_cast<
const ParallelAnchorData *>(ad)->secondEdge, nonFloatingItemsIdentifiedSoFar);
2561
2562
2563
2564
2565
2566void QGraphicsAnchorLayoutPrivate::setItemsGeometries(
const QRectF &geom)
2568 Q_Q(QGraphicsAnchorLayout);
2569 AnchorVertex *firstH, *secondH, *firstV, *secondV;
2575 q->getContentsMargins(&left, &top, &right,
nullptr);
2576 const Qt::LayoutDirection visualDir = visualDirection();
2577 if (visualDir == Qt::RightToLeft)
2580 left += geom.left();
2582 right = geom.right() - right;
2584 for (QGraphicsLayoutItem *item : std::as_const(items)) {
2586 QSizeF itemPreferredSize = item->effectiveSizeHint(Qt::PreferredSize);
2587 if (m_floatItems[Qt::Horizontal].contains(item)) {
2589 newGeom.setRight(itemPreferredSize.width());
2591 firstH = internalVertex(item, Qt::AnchorLeft);
2592 secondH = internalVertex(item, Qt::AnchorRight);
2594 if (visualDir == Qt::LeftToRight) {
2595 newGeom.setLeft(left + firstH->distance);
2596 newGeom.setRight(left + secondH->distance);
2598 newGeom.setLeft(right - secondH->distance);
2599 newGeom.setRight(right - firstH->distance);
2603 if (m_floatItems[Qt::Vertical].contains(item)) {
2605 newGeom.setBottom(itemPreferredSize.height());
2607 firstV = internalVertex(item, Qt::AnchorTop);
2608 secondV = internalVertex(item, Qt::AnchorBottom);
2610 newGeom.setTop(top + firstV->distance);
2611 newGeom.setBottom(top + secondV->distance);
2614 item->setGeometry(newGeom);
2619
2620
2621
2622
2623
2624void QGraphicsAnchorLayoutPrivate::calculateVertexPositions(Qt::Orientation orientation)
2626 QQueue<std::pair<AnchorVertex *, AnchorVertex *> > queue;
2627 QSet<AnchorVertex *> visited;
2630 AnchorVertex *root = layoutFirstVertex[orientation];
2633 visited.insert(root);
2636 const auto adjacentVertices = graph[orientation].adjacentVertices(root);
2637 for (AnchorVertex *v : adjacentVertices)
2638 queue.enqueue(std::pair(root, v));
2641 setupEdgesInterpolation(orientation);
2644 while (!queue.isEmpty()) {
2645 std::pair<AnchorVertex *, AnchorVertex *> pair = queue.dequeue();
2646 AnchorData *edge = graph[orientation].edgeData(pair.first, pair.second);
2648 if (visited.contains(pair.second))
2651 visited.insert(pair.second);
2652 interpolateEdge(pair.first, edge);
2654 QList<AnchorVertex *> adjacents = graph[orientation].adjacentVertices(pair.second);
2655 for (
int i = 0; i < adjacents.size(); ++i) {
2656 if (!visited.contains(adjacents.at(i)))
2657 queue.enqueue(std::pair(pair.second, adjacents.at(i)));
2663
2664
2665
2666
2667
2668
2669void QGraphicsAnchorLayoutPrivate::setupEdgesInterpolation(
2670 Qt::Orientation orientation)
2672 Q_Q(QGraphicsAnchorLayout);
2675 current = (orientation == Qt::Horizontal) ? q->contentsRect().width() : q->contentsRect().height();
2677 std::pair<Interval, qreal> result;
2678 result = getFactor(current,
2679 sizeHints[orientation][Qt::MinimumSize],
2680 sizeHints[orientation][Qt::PreferredSize],
2681 sizeHints[orientation][Qt::PreferredSize],
2682 sizeHints[orientation][Qt::PreferredSize],
2683 sizeHints[orientation][Qt::MaximumSize]);
2685 interpolationInterval[orientation] = result.first;
2686 interpolationProgress[orientation] = result.second;
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704void QGraphicsAnchorLayoutPrivate::interpolateEdge(AnchorVertex *base, AnchorData *edge)
2706 const Qt::Orientation orientation = edge->isVertical ? Qt::Vertical : Qt::Horizontal;
2707 const std::pair<Interval, qreal> factor(interpolationInterval[orientation],
2708 interpolationProgress[orientation]);
2710 qreal edgeDistance = interpolate(factor, edge->sizeAtMinimum, edge->sizeAtPreferred,
2711 edge->sizeAtPreferred, edge->sizeAtPreferred,
2712 edge->sizeAtMaximum);
2714 Q_ASSERT(edge->from == base || edge->to == base);
2717 if (edge->from == base) {
2718 edge->to->distance = base->distance + edgeDistance;
2720 edge->from->distance = base->distance - edgeDistance;
2724bool QGraphicsAnchorLayoutPrivate::solveMinMax(
const QList<QSimplexConstraint *> &constraints,
2725 const GraphPath &path, qreal *min, qreal *max)
2728 bool feasible = simplex.setConstraints(constraints);
2731 QSimplexConstraint objective;
2732 QSet<AnchorData *>::const_iterator iter;
2733 for (iter = path.positives.constBegin(); iter != path.positives.constEnd(); ++iter)
2734 objective.variables.insert(*iter, 1.0);
2736 for (iter = path.negatives.constBegin(); iter != path.negatives.constEnd(); ++iter)
2737 objective.variables.insert(*iter, -1.0);
2739 const qreal objectiveOffset = (path.positives.size() - path.negatives.size()) * g_offset;
2740 simplex.setObjective(&objective);
2743 *min = simplex.solveMin() - objectiveOffset;
2746 QList<AnchorData *> variables = getVariables(constraints);
2747 for (
int i = 0; i < variables.size(); ++i) {
2748 AnchorData *ad =
static_cast<AnchorData *>(variables.at(i));
2749 ad->sizeAtMinimum = ad->result - g_offset;
2753 *max = simplex.solveMax() - objectiveOffset;
2756 for (
int i = 0; i < variables.size(); ++i) {
2757 AnchorData *ad =
static_cast<AnchorData *>(variables.at(i));
2758 ad->sizeAtMaximum = ad->result - g_offset;
2768 QConcreteSimplexVariable *slack;
2772 auto slack =
new QConcreteSimplexVariable;
2773 sizeConstraint->variables.insert(slack, type);
2776 limit->variables.insert(slack, 1.0);
2777 limit->ratio = QSimplexConstraint::LessOrEqual;
2778 limit->constant = interval;
2780 return R{slack, limit};
2783bool QGraphicsAnchorLayoutPrivate::solvePreferred(
const QList<QSimplexConstraint *> &constraints,
2784 const QList<AnchorData *> &variables)
2786 QList<QSimplexConstraint *> preferredConstraints;
2787 QList<QConcreteSimplexVariable *> preferredVariables;
2788 QSimplexConstraint objective;
2808 for (
int i = 0; i < variables.size(); ++i) {
2809 AnchorData *ad = variables.at(i);
2812 if (ad->isLayoutAnchor)
2817 QSimplexConstraint *sizeConstraint =
new QSimplexConstraint;
2818 preferredConstraints += sizeConstraint;
2819 sizeConstraint->variables.insert(ad, 1.0);
2820 sizeConstraint->constant = ad->prefSize + g_offset;
2823 const qreal softShrinkInterval = ad->prefSize - ad->minPrefSize;
2824 if (softShrinkInterval) {
2825 auto r = createSlack(sizeConstraint, softShrinkInterval, Shrinker);
2826 preferredVariables += r.slack;
2827 preferredConstraints += r.limit;
2830 objective.variables.insert(r.slack, 1.0);
2834 const qreal softGrowInterval = ad->maxPrefSize - ad->prefSize;
2835 if (softGrowInterval) {
2836 auto r = createSlack(sizeConstraint, softGrowInterval, Grower);
2837 preferredVariables += r.slack;
2838 preferredConstraints += r.limit;
2841 objective.variables.insert(r.slack, 1.0);
2845 const qreal hardShrinkInterval = ad->minPrefSize - ad->minSize;
2846 if (hardShrinkInterval) {
2847 auto r = createSlack(sizeConstraint, hardShrinkInterval, Shrinker);
2848 preferredVariables += r.slack;
2849 preferredConstraints += r.limit;
2852 objective.variables.insert(r.slack, variables.size());
2856 const qreal hardGrowInterval = ad->maxSize - ad->maxPrefSize;
2857 if (hardGrowInterval) {
2858 auto r = createSlack(sizeConstraint, hardGrowInterval, Grower);
2859 preferredVariables += r.slack;
2860 preferredConstraints += r.limit;
2863 objective.variables.insert(r.slack, variables.size());
2867 QSimplex *simplex =
new QSimplex;
2868 bool feasible = simplex->setConstraints(constraints + preferredConstraints);
2870 simplex->setObjective(&objective);
2873 simplex->solveMin();
2876 for (
int i = 0; i < variables.size(); ++i) {
2877 AnchorData *ad = variables.at(i);
2878 ad->sizeAtPreferred = ad->result - g_offset;
2887 qDeleteAll(preferredConstraints);
2888 qDeleteAll(preferredVariables);
2894
2895
2896
2897
2898
2899
2900bool QGraphicsAnchorLayoutPrivate::hasConflicts()
const
2902 QGraphicsAnchorLayoutPrivate *that =
const_cast<QGraphicsAnchorLayoutPrivate*>(
this);
2903 that->calculateGraphs();
2905 bool floatConflict = !m_floatItems[Qt::Horizontal].isEmpty() || !m_floatItems[Qt::Vertical].isEmpty();
2907 return graphHasConflicts[Qt::Horizontal] || graphHasConflicts[Qt::Vertical] || floatConflict;
2911void QGraphicsAnchorLayoutPrivate::dumpGraph(
const QString &name)
2913 QFile file(QString::fromLatin1(
"anchorlayout.%1.dot").arg(name));
2914 if (!file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate))
2915 qWarning(
"Could not write to %ls", qUtf16Printable(file.fileName()));
2917 QString str = QString::fromLatin1(
"digraph anchorlayout {\nnode [shape=\"rect\"]\n%1}");
2918 QString dotContents = graph[Qt::Horizontal].serializeToDot();
2919 dotContents += graph[Qt::Vertical].serializeToDot();
2920 file.write(str.arg(dotContents).toLocal8Bit());
void setSizePolicy(QSizePolicy::Policy policy)
~QGraphicsAnchorPrivate()
QGraphicsAnchorLayoutPrivate * layoutPrivate
void setSpacing(qreal value)
QSimplexConstraint * constraint(const GraphPath &path) const
static auto createSlack(QSimplexConstraint *sizeConstraint, qreal interval, slackType type)
static AnchorData * createSequence(Graph< AnchorVertex, AnchorData > *graph, AnchorVertex *before, const QList< AnchorVertex * > &vertices, AnchorVertex *after)
static void applySizePolicy(QSizePolicy::Policy policy, qreal minSizeHint, qreal prefSizeHint, qreal maxSizeHint, qreal *minSize, qreal *prefSize, qreal *maxSize)
static AnchorVertex * replaceVertex_helper(AnchorData *data, AnchorVertex *oldV, AnchorVertex *newV)
static void shiftConstraints(const QList< QSimplexConstraint * > &constraints, qreal amount)
static qreal interpolate(const std::pair< QGraphicsAnchorLayoutPrivate::Interval, qreal > &factor, qreal min, qreal minPref, qreal pref, qreal maxPref, qreal max)
static std::pair< QGraphicsAnchorLayoutPrivate::Interval, qreal > getFactor(qreal value, qreal min, qreal minPref, qreal pref, qreal maxPref, qreal max)
QList< AnchorData * > getVariables(const QList< QSimplexConstraint * > &constraints)
virtual void updateChildrenSizes()
void refreshSizeHints(const QLayoutStyleInfo *styleInfo=nullptr)
QGraphicsLayoutItem * item
QGraphicsLayoutItem * m_item
virtual void updateChildrenSizes() override
bool calculateSizeHints()
bool secondForward() const
virtual void updateChildrenSizes() override
void calculateSizeHints()