Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qgraphicsanchorlayout_p.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
6
7#include <QtWidgets/qwidget.h>
8#include <QtWidgets/qapplication.h>
9#include <QtCore/qstack.h>
10
11#ifdef QT_DEBUG
12#include <QtCore/qfile.h>
13#endif
14
15#include <numeric>
16
17QT_BEGIN_NAMESPACE
18
19using namespace Qt::StringLiterals;
20
21// To ensure that all variables inside the simplex solver are non-negative,
22// we limit the size of anchors in the interval [-limit, limit]. Then before
23// sending them to the simplex solver we add "limit" as an offset, so that
24// they are actually calculated in the interval [0, 2 * limit]
25// To avoid numerical errors in platforms where we use single precision,
26// we use a tighter limit for the variables range.
27const qreal g_offset = (sizeof(qreal) == sizeof(double)) ? QWIDGETSIZE_MAX : QWIDGETSIZE_MAX / 32;
28
29QGraphicsAnchorPrivate::QGraphicsAnchorPrivate(decltype(QObjectPrivateVersion) version)
30 : QObjectPrivate(version), layoutPrivate(nullptr), data(nullptr),
31 sizePolicy(QSizePolicy::Fixed), preferredSize(0),
32 hasSize(true)
33{
34}
35
37{
38 if (data) {
39 // The QGraphicsAnchor was already deleted at this moment. We must clean
40 // the dangling pointer to avoid double deletion in the AnchorData dtor.
41 data->graphicsAnchor = nullptr;
42
43 layoutPrivate->removeAnchor(data->from, data->to);
44 }
45}
46
47void QGraphicsAnchorPrivate::setSizePolicy(QSizePolicy::Policy policy)
48{
49 if (sizePolicy != policy) {
50 sizePolicy = policy;
51 layoutPrivate->q_func()->invalidate();
52 }
53}
54
56{
57 if (!data) {
58 qWarning("QGraphicsAnchor::setSpacing: The anchor does not exist.");
59 return;
60 }
61
62 if (hasSize && (preferredSize == value))
63 return;
64
65 // The anchor has an user-defined size
66 hasSize = true;
67 preferredSize = value;
68
69 layoutPrivate->q_func()->invalidate();
70}
71
73{
74 if (!data) {
75 qWarning("QGraphicsAnchor::setSpacing: The anchor does not exist.");
76 return;
77 }
78
79 // Return to standard direction
80 hasSize = false;
81
82 layoutPrivate->q_func()->invalidate();
83}
84
86{
87 if (!data) {
88 qWarning("QGraphicsAnchor::setSpacing: The anchor does not exist.");
89 return 0;
90 }
91
92 return preferredSize;
93}
94
95
96static void applySizePolicy(QSizePolicy::Policy policy,
97 qreal minSizeHint, qreal prefSizeHint, qreal maxSizeHint,
98 qreal *minSize, qreal *prefSize,
99 qreal *maxSize)
100{
101 // minSize, prefSize and maxSize are initialized
102 // with item's preferred Size: this is QSizePolicy::Fixed.
103 //
104 // Then we check each flag to find the resultant QSizePolicy,
105 // according to the following table:
106 //
107 // constant value
108 // QSizePolicy::Fixed 0
109 // QSizePolicy::Minimum GrowFlag
110 // QSizePolicy::Maximum ShrinkFlag
111 // QSizePolicy::Preferred GrowFlag | ShrinkFlag
112 // QSizePolicy::Ignored GrowFlag | ShrinkFlag | IgnoreFlag
113
114 if (policy & QSizePolicy::ShrinkFlag)
115 *minSize = minSizeHint;
116 else
117 *minSize = prefSizeHint;
118
119 if (policy & QSizePolicy::GrowFlag)
120 *maxSize = maxSizeHint;
121 else
122 *maxSize = prefSizeHint;
123
124 // Note that these two initializations are affected by the previous flags
125 if (policy & QSizePolicy::IgnoreFlag)
126 *prefSize = *minSize;
127 else
128 *prefSize = prefSizeHint;
129}
130
132{
133 if (graphicsAnchor) {
134 // Remove reference to ourself to avoid double removal in
135 // QGraphicsAnchorPrivate dtor.
136 QGraphicsAnchorPrivate::get(graphicsAnchor)->data = nullptr;
137
138 delete graphicsAnchor;
139 }
140}
141
142
144{
145 QSizePolicy::Policy policy;
146 qreal minSizeHint;
147 qreal prefSizeHint;
148 qreal maxSizeHint;
149
150 if (item) {
151 // It is an internal anchor, fetch size information from the item
152 if (isLayoutAnchor) {
153 minSize = 0;
154 prefSize = 0;
155 maxSize = QWIDGETSIZE_MAX;
156 if (isCenterAnchor)
157 maxSize /= 2;
158
159 minPrefSize = prefSize;
160 maxPrefSize = maxSize;
161 return;
162 } else {
163 if (!isVertical) {
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();
168 } else {
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();
173 }
174
175 if (isCenterAnchor) {
176 minSizeHint /= 2;
177 prefSizeHint /= 2;
178 maxSizeHint /= 2;
179 }
180 }
181 } else {
182 // It is a user-created anchor, fetch size information from the associated QGraphicsAnchor
183 Q_ASSERT(graphicsAnchor);
184 QGraphicsAnchorPrivate *anchorPrivate = QGraphicsAnchorPrivate::get(graphicsAnchor);
185
186 // Policy, min and max sizes are straightforward
187 policy = anchorPrivate->sizePolicy;
188 minSizeHint = 0;
189 maxSizeHint = QWIDGETSIZE_MAX;
190
191 // Preferred Size
192 if (anchorPrivate->hasSize) {
193 // Anchor has user-defined size
194 prefSizeHint = anchorPrivate->preferredSize;
195 } else if (styleInfo) {
196 // Fetch size information from style
197 const Qt::Orientation orient = QGraphicsAnchorLayoutPrivate::edgeOrientation(from->m_edge);
198 qreal s = styleInfo->defaultSpacing(orient);
199 if (s < 0) {
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);
203
204 // ### Currently we do not support negative anchors inside the graph.
205 // To avoid those being created by a negative style spacing, we must
206 // make this test.
207 if (s < 0)
208 s = 0;
209 }
210 prefSizeHint = s;
211 } else {
212 prefSizeHint = 0;
213 }
214 }
215
216 // Fill minSize, prefSize and maxSize based on policy and sizeHints
217 applySizePolicy(policy, minSizeHint, prefSizeHint, maxSizeHint,
218 &minSize, &prefSize, &maxSize);
219
220 minPrefSize = prefSize;
221 maxPrefSize = maxSize;
222
223 // Set the anchor effective sizes to preferred.
224 //
225 // Note: The idea here is that all items should remain at their
226 // preferred size unless where that's impossible. In cases where
227 // the item is subject to restrictions (anchored to the layout
228 // edges, for instance), the simplex solver will be run to
229 // recalculate and override the values we set here.
230 sizeAtMinimum = prefSize;
231 sizeAtPreferred = prefSize;
232 sizeAtMaximum = prefSize;
233}
234
236{
237 firstEdge->sizeAtMinimum = sizeAtMinimum;
238 firstEdge->sizeAtPreferred = sizeAtPreferred;
239 firstEdge->sizeAtMaximum = sizeAtMaximum;
240
241 if (secondForward()) {
242 secondEdge->sizeAtMinimum = sizeAtMinimum;
243 secondEdge->sizeAtPreferred = sizeAtPreferred;
244 secondEdge->sizeAtMaximum = sizeAtMaximum;
245 } else {
246 secondEdge->sizeAtMinimum = -sizeAtMinimum;
247 secondEdge->sizeAtPreferred = -sizeAtPreferred;
248 secondEdge->sizeAtMaximum = -sizeAtMaximum;
249 }
250
253}
254
255/*
256 \internal
257
258 Initialize the parallel anchor size hints using the sizeHint information from
259 its children.
260
261 Note that parallel groups can lead to unfeasibility, so during calculation, we can
262 find out one unfeasibility. Because of that this method return boolean. This can't
263 happen in sequential, so there the method is void.
264 */
266{
267 // Normalize second child sizes.
268 // A negative anchor of sizes min, minPref, pref, maxPref and max, is equivalent
269 // to a forward anchor of sizes -max, -maxPref, -pref, -minPref, -min
270 qreal secondMin;
271 qreal secondMinPref;
272 qreal secondPref;
273 qreal secondMaxPref;
274 qreal secondMax;
275
276 if (secondForward()) {
277 secondMin = secondEdge->minSize;
278 secondMinPref = secondEdge->minPrefSize;
279 secondPref = secondEdge->prefSize;
280 secondMaxPref = secondEdge->maxPrefSize;
281 secondMax = secondEdge->maxSize;
282 } else {
283 secondMin = -secondEdge->maxSize;
284 secondMinPref = -secondEdge->maxPrefSize;
285 secondPref = -secondEdge->prefSize;
286 secondMaxPref = -secondEdge->minPrefSize;
287 secondMax = -secondEdge->minSize;
288 }
289
290 minSize = qMax(firstEdge->minSize, secondMin);
291 maxSize = qMin(firstEdge->maxSize, secondMax);
292
293 // This condition means that the maximum size of one anchor being simplified is smaller than
294 // the minimum size of the other anchor. The consequence is that there won't be a valid size
295 // for this parallel setup.
296 if (minSize > maxSize) {
297 return false;
298 }
299
300 // Preferred size calculation
301 // The calculation of preferred size is done as follows:
302 //
303 // 1) Check whether one of the child anchors is the layout structural anchor
304 // If so, we can simply copy the preferred information from the other child,
305 // after bounding it to our minimum and maximum sizes.
306 // If not, then we proceed with the actual calculations.
307 //
308 // 2) The whole algorithm for preferred size calculation is based on the fact
309 // that, if a given anchor cannot remain at its preferred size, it'd rather
310 // grow than shrink.
311 //
312 // What happens though is that while this affirmative is true for simple
313 // anchors, it may not be true for sequential anchors that have one or more
314 // reversed anchors inside it. That happens because when a sequential anchor
315 // grows, any reversed anchors inside it may be required to shrink, something
316 // we try to avoid, as said above.
317 //
318 // To overcome this, besides their actual preferred size "prefSize", each anchor
319 // exports what we call "minPrefSize" and "maxPrefSize". These two values define
320 // a surrounding interval where, if required to move, the anchor would rather
321 // remain inside.
322 //
323 // For standard anchors, this area simply represents the region between
324 // prefSize and maxSize, which makes sense since our first affirmation.
325 // For composed anchors, these values are calculated as to reduce the global
326 // "damage", that is, to reduce the total deviation and the total amount of
327 // anchors that had to shrink.
328
329 if (firstEdge->isLayoutAnchor) {
330 prefSize = qBound(minSize, secondPref, maxSize);
331 minPrefSize = qBound(minSize, secondMinPref, maxSize);
332 maxPrefSize = qBound(minSize, secondMaxPref, maxSize);
333 } else if (secondEdge->isLayoutAnchor) {
334 prefSize = qBound(minSize, firstEdge->prefSize, maxSize);
335 minPrefSize = qBound(minSize, firstEdge->minPrefSize, maxSize);
336 maxPrefSize = qBound(minSize, firstEdge->maxPrefSize, maxSize);
337 } else {
338 // Calculate the intersection between the "preferred" regions of each child
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);
345
346 if (lowerBoundary < upperBoundary) {
347 // If there is an intersection between the two regions, this intersection
348 // will be used as the preferred region of the parallel anchor itself.
349 // The preferred size will be the bounded average between the two preferred
350 // sizes.
351 prefSize = qBound(lowerBoundary, prefMean, upperBoundary);
352 minPrefSize = lowerBoundary;
353 maxPrefSize = upperBoundary;
354 } else {
355 // If there is no intersection, we have to attribute "damage" to at least
356 // one of the children. The minimum total damage is achieved in points
357 // inside the region that extends from (1) the upper boundary of the lower
358 // region to (2) the lower boundary of the upper region.
359 // Then, we expose this region as _our_ preferred region and once again,
360 // use the bounded average as our preferred size.
361 prefSize = qBound(upperBoundary, prefMean, lowerBoundary);
362 minPrefSize = upperBoundary;
363 maxPrefSize = lowerBoundary;
364 }
365 }
366
367 // See comment in AnchorData::refreshSizeHints() about sizeAt* values
368 sizeAtMinimum = prefSize;
369 sizeAtPreferred = prefSize;
370 sizeAtMaximum = prefSize;
371
372 return true;
373}
374
375/*!
376 \internal
377 returns the factor in the interval [-1, 1].
378 -1 is at Minimum
379 0 is at Preferred
380 1 is at Maximum
381*/
383 qreal minPref, qreal pref,
384 qreal maxPref, qreal max)
385{
386 QGraphicsAnchorLayoutPrivate::Interval interval;
387 qreal lower;
388 qreal upper;
389
390 if (value < minPref) {
391 interval = QGraphicsAnchorLayoutPrivate::MinimumToMinPreferred;
392 lower = min;
393 upper = minPref;
394 } else if (value < pref) {
395 interval = QGraphicsAnchorLayoutPrivate::MinPreferredToPreferred;
396 lower = minPref;
397 upper = pref;
398 } else if (value < maxPref) {
399 interval = QGraphicsAnchorLayoutPrivate::PreferredToMaxPreferred;
400 lower = pref;
401 upper = maxPref;
402 } else {
403 interval = QGraphicsAnchorLayoutPrivate::MaxPreferredToMaximum;
404 lower = maxPref;
405 upper = max;
406 }
407
408 qreal progress;
409 if (upper == lower) {
410 progress = 0;
411 } else {
412 progress = (value - lower) / (upper - lower);
413 }
414
415 return std::pair(interval, progress);
416}
417
418static qreal interpolate(const std::pair<QGraphicsAnchorLayoutPrivate::Interval, qreal> &factor,
419 qreal min, qreal minPref, qreal pref, qreal maxPref, qreal max)
420{
421 qreal lower = 0;
422 qreal upper = 0;
423
424 switch (factor.first) {
425 case QGraphicsAnchorLayoutPrivate::MinimumToMinPreferred:
426 lower = min;
427 upper = minPref;
428 break;
429 case QGraphicsAnchorLayoutPrivate::MinPreferredToPreferred:
430 lower = minPref;
431 upper = pref;
432 break;
433 case QGraphicsAnchorLayoutPrivate::PreferredToMaxPreferred:
434 lower = pref;
435 upper = maxPref;
436 break;
437 case QGraphicsAnchorLayoutPrivate::MaxPreferredToMaximum:
438 lower = maxPref;
439 upper = max;
440 break;
441 }
442
443 return lower + factor.second * (upper - lower);
444}
445
447{
448 // Band here refers if the value is in the Minimum To Preferred
449 // band (the lower band) or the Preferred To Maximum (the upper band).
450
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);
457
458 // XXX This is not safe if Vertex simplification takes place after the sequential
459 // anchor is created. In that case, "prev" will be a group-vertex, different from
460 // "from" or "to", that _contains_ one of them.
461 AnchorVertex *prev = from;
462
463 for (AnchorData *e : m_edges) {
464 const bool edgeIsForward = (e->from == prev);
465 if (edgeIsForward) {
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);
472 prev = e->to;
473 } else {
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);
481 prev = e->from;
482 }
483
484 e->updateChildrenSizes();
485 }
486}
487
489{
490 minSize = 0;
491 prefSize = 0;
492 maxSize = 0;
493 minPrefSize = 0;
494 maxPrefSize = 0;
495
496 AnchorVertex *prev = from;
497
498 for (AnchorData *edge : m_edges) {
499 const bool edgeIsForward = (edge->from == prev);
500 if (edgeIsForward) {
501 minSize += edge->minSize;
502 prefSize += edge->prefSize;
503 maxSize += edge->maxSize;
504 minPrefSize += edge->minPrefSize;
505 maxPrefSize += edge->maxPrefSize;
506 prev = edge->to;
507 } else {
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;
514 prev = edge->from;
515 }
516 }
517
518 // See comment in AnchorData::refreshSizeHints() about sizeAt* values
519 sizeAtMinimum = prefSize;
520 sizeAtPreferred = prefSize;
521 sizeAtMaximum = prefSize;
522}
523
524#ifdef QT_DEBUG
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)
535 e->dump(indent + 2);
536 } else {
537 qDebug("%*s type: Normal:", indent, "");
538 }
539}
540
541#endif
542
544{
545 // Calculate
546 QSet<AnchorData *> cPositives;
547 QSet<AnchorData *> cNegatives;
548 QSet<AnchorData *> intersection;
549
550 cPositives = positives + path.negatives;
551 cNegatives = negatives + path.positives;
552
553 intersection = cPositives & cNegatives;
554
555 cPositives -= intersection;
556 cNegatives -= intersection;
557
558 // Fill
560 QSet<AnchorData *>::iterator i;
561 for (i = cPositives.begin(); i != cPositives.end(); ++i)
562 c->variables.insert(*i, 1.0);
563
564 for (i = cNegatives.begin(); i != cNegatives.end(); ++i)
565 c->variables.insert(*i, -1.0);
566
567 return c;
568}
569
570#ifdef QT_DEBUG
571QString GraphPath::toString() const
572{
573 QString string;
574 string += "Path: "_L1;
575
576 for (AnchorData *edge : positives)
577 string += QString::fromLatin1(" (+++) %1").arg(edge->toString());
578
579 for (AnchorData *edge : negatives)
580 string += QString::fromLatin1(" (---) %1").arg(edge->toString());
581
582 return string;
583}
584#endif
585
586QGraphicsAnchorLayoutPrivate::QGraphicsAnchorLayoutPrivate()
587 : calculateGraphCacheDirty(true), styleInfoDirty(true)
588{
589}
590
591Qt::AnchorPoint QGraphicsAnchorLayoutPrivate::oppositeEdge(Qt::AnchorPoint edge)
592{
593 switch (edge) {
594 case Qt::AnchorLeft:
595 edge = Qt::AnchorRight;
596 break;
597 case Qt::AnchorRight:
598 edge = Qt::AnchorLeft;
599 break;
600 case Qt::AnchorTop:
601 edge = Qt::AnchorBottom;
602 break;
603 case Qt::AnchorBottom:
604 edge = Qt::AnchorTop;
605 break;
606 default:
607 break;
608 }
609 return edge;
610}
611
612
613/*!
614 \internal
615
616 Adds \a newAnchor to the graph.
617
618 Returns the newAnchor itself if it could be added without further changes to the graph. If a
619 new parallel anchor had to be created, then returns the new parallel anchor. If a parallel anchor
620 had to be created and it results in an unfeasible setup, \a feasible is set to false, otherwise
621 true.
622
623 Note that in the case a new parallel anchor is created, it might also take over some constraints
624 from its children anchors.
625*/
626AnchorData *QGraphicsAnchorLayoutPrivate::addAnchorMaybeParallel(AnchorData *newAnchor, bool *feasible)
627{
628 const Qt::Orientation orientation = newAnchor->isVertical ? Qt::Vertical : Qt::Horizontal;
629 Graph<AnchorVertex, AnchorData> &g = graph[orientation];
630 *feasible = true;
631
632 // If already exists one anchor where newAnchor is supposed to be, we create a parallel
633 // anchor.
634 if (AnchorData *oldAnchor = g.takeEdge(newAnchor->from, newAnchor->to)) {
635 ParallelAnchorData *parallel = new ParallelAnchorData(oldAnchor, newAnchor);
636
637 // The parallel anchor will "replace" its children anchors in
638 // every center constraint that they appear.
639
640 // ### If the dependent (center) anchors had reference(s) to their constraints, we
641 // could avoid traversing all the itemCenterConstraints.
642 QList<QSimplexConstraint *> &constraints = itemCenterConstraints[orientation];
643
644 AnchorData *children[2] = { oldAnchor, newAnchor };
645 QList<QSimplexConstraint *> *childrenConstraints[2] = { &parallel->m_firstConstraints,
646 &parallel->m_secondConstraints };
647
648 for (int i = 0; i < 2; ++i) {
649 AnchorData *child = children[i];
650 QList<QSimplexConstraint *> *childConstraints = childrenConstraints[i];
651
652 // We need to fix the second child constraints if the parallel group will have the
653 // opposite direction of the second child anchor. For the point of view of external
654 // entities, this anchor was reversed. So if at some point we say that the parallel
655 // has a value of 20, this mean that the second child (when reversed) will be
656 // assigned -20.
657 const bool needsReverse = i == 1 && !parallel->secondForward();
658
659 if (!child->isCenterAnchor)
660 continue;
661
662 parallel->isCenterAnchor = true;
663
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);
669 if (needsReverse)
670 v *= -1;
671 c->variables.insert(parallel, v);
672 }
673 }
674 }
675
676 // At this point we can identify that the parallel anchor is not feasible, e.g. one
677 // anchor minimum size is bigger than the other anchor maximum size.
678 *feasible = parallel->calculateSizeHints();
679 newAnchor = parallel;
680 }
681
682 g.createEdge(newAnchor->from, newAnchor->to, newAnchor);
683 return newAnchor;
684}
685
686/*!
687 \internal
688
689 Takes the sequence of vertices described by (\a before, \a vertices, \a after) and removes
690 all anchors connected to the vertices in \a vertices, returning one simplified anchor between
691 \a before and \a after.
692
693 Note that this function doesn't add the created anchor to the graph. This should be done by
694 the caller.
695*/
696static AnchorData *createSequence(Graph<AnchorVertex, AnchorData> *graph, AnchorVertex *before,
697 const QList<AnchorVertex *> &vertices, AnchorVertex *after)
698{
699#if defined(QT_DEBUG) && 0
700 QString strVertices;
701 for (int i = 0; i < vertices.count(); ++i) {
702 strVertices += QString::fromLatin1("%1 - ").arg(vertices.at(i)->toString());
703 }
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()));
706#endif
707
708 AnchorVertex *prev = before;
709 QList<AnchorData *> edges;
710 edges.reserve(vertices.size() + 1);
711
712 const int numVertices = vertices.size();
713 edges.reserve(numVertices + 1);
714 // Take from the graph, the edges that will be simplificated
715 for (int i = 0; i < numVertices; ++i) {
716 AnchorVertex *next = vertices.at(i);
717 AnchorData *ad = graph->takeEdge(prev, next);
718 Q_ASSERT(ad);
719 edges.append(ad);
720 prev = next;
721 }
722
723 // Take the last edge (not covered in the loop above)
724 AnchorData *ad = graph->takeEdge(vertices.last(), after);
725 Q_ASSERT(ad);
726 edges.append(ad);
727
728 // Create sequence
729 SequentialAnchorData *sequence = new SequentialAnchorData(vertices, edges);
730 sequence->from = before;
731 sequence->to = after;
732
734
735 return sequence;
736}
737
738/*!
739 \internal
740
741 The purpose of this function is to simplify the graph.
742 Simplification serves two purposes:
743 1. Reduce the number of edges in the graph, (thus the number of variables to the equation
744 solver is reduced, and the solver performs better).
745 2. Be able to do distribution of sequences of edges more intelligently (esp. with sequential
746 anchors)
747
748 It is essential that it must be possible to restore simplified anchors back to their "original"
749 form. This is done by restoreSimplifiedAnchor().
750
751 There are two types of simplification that can be done:
752 1. Sequential simplification
753 Sequential simplification means that all sequences of anchors will be merged into one single
754 anchor. Only anhcors that points in the same direction will be merged.
755 2. Parallel simplification
756 If a simplified sequential anchor is about to be inserted between two vertices in the graph
757 and there already exist an anchor between those two vertices, a parallel anchor will be
758 created that serves as a placeholder for the sequential anchor and the anchor that was
759 already between the two vertices.
760
761 The process of simplification can be described as:
762
763 1. Simplify all sequences of anchors into one anchor.
764 If no further simplification was done, go to (3)
765 - If there already exist an anchor where the sequential anchor is supposed to be inserted,
766 take that anchor out of the graph
767 - Then create a parallel anchor that holds the sequential anchor and the anchor just taken
768 out of the graph.
769 2. Go to (1)
770 3. Done
771
772 When creating the parallel anchors, the algorithm might identify unfeasible situations. In this
773 case the simplification process stops and returns \c false. Otherwise returns \c true.
774*/
775bool QGraphicsAnchorLayoutPrivate::simplifyGraph(Qt::Orientation orientation)
776{
777 if (items.isEmpty())
778 return true;
779
780#if defined(QT_DEBUG) && 0
781 qDebug("Simplifying Graph for %s",
782 orientation == Horizontal ? "Horizontal" : "Vertical");
783
784 static int count = 0;
785 if (orientation == Horizontal) {
786 count++;
787 dumpGraph(QString::fromLatin1("%1-full").arg(count));
788 }
789#endif
790
791 // Vertex simplification
792 if (!simplifyVertices(orientation)) {
793 restoreVertices(orientation);
794 return false;
795 }
796
797 // Anchor simplification
798 bool dirty;
799 bool feasible = true;
800 do {
801 dirty = simplifyGraphIteration(orientation, &feasible);
802 } while (dirty && feasible);
803
804 // Note that if we are not feasible, we fallback and make sure that the graph is fully restored
805 if (!feasible) {
806 restoreSimplifiedGraph(orientation);
807 restoreVertices(orientation);
808 return false;
809 }
810
811#if defined(QT_DEBUG) && 0
812 dumpGraph(QString::fromLatin1("%1-simplified-%2").arg(count).arg(
813 QString::fromLatin1(orientation == Horizontal ? "Horizontal" : "Vertical")));
814#endif
815
816 return true;
817}
818
820{
821 AnchorVertex *other;
822 if (data->from == oldV) {
823 data->from = newV;
824 other = data->to;
825 } else {
826 data->to = newV;
827 other = data->from;
828 }
829 return other;
830}
831
832bool QGraphicsAnchorLayoutPrivate::replaceVertex(Qt::Orientation orientation, AnchorVertex *oldV,
833 AnchorVertex *newV, const QList<AnchorData *> &edges)
834{
835 Graph<AnchorVertex, AnchorData> &g = graph[orientation];
836 bool feasible = true;
837
838 for (int i = 0; i < edges.size(); ++i) {
839 AnchorData *ad = edges[i];
840 AnchorVertex *otherV = replaceVertex_helper(ad, oldV, newV);
841
842#if defined(QT_DEBUG)
843 ad->name = QString::fromLatin1("%1 --to--> %2").arg(ad->from->toString(), ad->to->toString());
844#endif
845
846 bool newFeasible;
847 AnchorData *newAnchor = addAnchorMaybeParallel(ad, &newFeasible);
848 feasible &= newFeasible;
849
850 if (newAnchor != ad) {
851 // A parallel was created, we mark that in the list of anchors created by vertex
852 // simplification. This is needed because we want to restore them in a separate step
853 // from the restoration of anchor simplification.
854 anchorsFromSimplifiedVertices[orientation].append(newAnchor);
855 }
856
857 g.takeEdge(oldV, otherV);
858 }
859
860 return feasible;
861}
862
863/*!
864 \internal
865*/
866bool QGraphicsAnchorLayoutPrivate::simplifyVertices(Qt::Orientation orientation)
867{
868 Q_Q(QGraphicsAnchorLayout);
869 Graph<AnchorVertex, AnchorData> &g = graph[orientation];
870
871 // We'll walk through vertices
872 QStack<AnchorVertex *> stack;
873 stack.push(layoutFirstVertex[orientation]);
874 QSet<AnchorVertex *> visited;
875
876 while (!stack.isEmpty()) {
877 AnchorVertex *v = stack.pop();
878 visited.insert(v);
879
880 // Each adjacent of 'v' is a possible vertex to be merged. So we traverse all of
881 // them. Since once a merge is made, we might add new adjacents, and we don't want to
882 // pass two times through one adjacent. The 'index' is used to track our position.
883 QList<AnchorVertex *> adjacents = g.adjacentVertices(v);
884 int index = 0;
885
886 while (index < adjacents.size()) {
887 AnchorVertex *next = adjacents.at(index);
888 index++;
889
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;
893
894 if (!bothLayoutVertices && zeroSized) {
895
896 // Create a new vertex pair, note that we keep a list of those vertices so we can
897 // easily process them when restoring the graph.
898 AnchorVertexPair *newV = new AnchorVertexPair(v, next, data);
899 simplifiedVertices[orientation].append(newV);
900
901 // Collect the anchors of both vertices, the new vertex pair will take their place
902 // in those anchors
903 const QList<AnchorVertex *> &vAdjacents = g.adjacentVertices(v);
904 const QList<AnchorVertex *> &nextAdjacents = g.adjacentVertices(next);
905
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);
911 }
912 }
913
914 for (int i = 0; i < nextAdjacents.size(); ++i) {
915 AnchorVertex *adjacent = nextAdjacents.at(i);
916 if (adjacent != v) {
917 AnchorData *ad = g.edgeData(next, adjacent);
918 newV->m_secondAnchors.append(ad);
919
920 // We'll also add new vertices to the adjacent list of the new 'v', to be
921 // created as a vertex pair and replace the current one.
922 if (!adjacents.contains(adjacent))
923 adjacents.append(adjacent);
924 }
925 }
926
927 // ### merge this loop into the ones that calculated m_firstAnchors/m_secondAnchors?
928 // Make newV take the place of v and next
929 bool feasible = replaceVertex(orientation, v, newV, newV->m_firstAnchors);
930 feasible &= replaceVertex(orientation, next, newV, newV->m_secondAnchors);
931
932 // Update the layout vertex information if one of the vertices is a layout vertex.
933 AnchorVertex *layoutVertex = nullptr;
934 if (v->m_item == q)
935 layoutVertex = v;
936 else if (next->m_item == q)
937 layoutVertex = next;
938
939 if (layoutVertex) {
940 // Layout vertices always have m_item == q...
941 newV->m_item = q;
942 changeLayoutVertex(orientation, layoutVertex, newV);
943 }
944
945 g.takeEdge(v, next);
946
947 // If a non-feasibility is found, we leave early and cancel the simplification
948 if (!feasible)
949 return false;
950
951 v = newV;
952 visited.insert(newV);
953
954 } else if (!visited.contains(next) && !stack.contains(next)) {
955 // If the adjacent is not fit for merge and it wasn't visited by the outermost
956 // loop, we add it to the stack.
957 stack.push(next);
958 }
959 }
960 }
961
962 return true;
963}
964
965/*!
966 \internal
967
968 One iteration of the simplification algorithm. Returns \c true if another iteration is needed.
969
970 The algorithm walks the graph in depth-first order, and only collects vertices that has two
971 edges connected to it. If the vertex does not have two edges or if it is a layout edge, it
972 will take all the previously collected vertices and try to create a simplified sequential
973 anchor representing all the previously collected vertices. Once the simplified anchor is
974 inserted, the collected list is cleared in order to find the next sequence to simplify.
975
976 Note that there are some catches to this that are not covered by the above explanation, see
977 the function comments for more details.
978*/
979bool QGraphicsAnchorLayoutPrivate::simplifyGraphIteration(Qt::Orientation orientation,
980 bool *feasible)
981{
982 Q_Q(QGraphicsAnchorLayout);
983 Graph<AnchorVertex, AnchorData> &g = graph[orientation];
984
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;
989
990 // Walk depth-first, in the stack we store start of the candidate sequence (beforeSequence)
991 // and the vertex to be visited.
992 while (!stack.isEmpty()) {
993 std::pair<AnchorVertex *, AnchorVertex *> pair = stack.pop();
994 AnchorVertex *beforeSequence = pair.first;
995 AnchorVertex *v = pair.second;
996
997 // The basic idea is to determine whether we found an end of sequence,
998 // if that's the case, we stop adding vertices to the candidate list
999 // and do a simplification step.
1000 //
1001 // A vertex can trigger an end of sequence if
1002 // (a) it is a layout vertex, we don't simplify away the layout vertices;
1003 // (b) it does not have exactly 2 adjacents;
1004 // (c) its next adjacent is already visited (a cycle in the graph).
1005 // (d) the next anchor is a center anchor.
1006
1007 const QList<AnchorVertex *> &adjacents = g.adjacentVertices(v);
1008 const bool isLayoutVertex = v->m_item == q;
1009 AnchorVertex *afterSequence = v;
1010 bool endOfSequence = false;
1011
1012 //
1013 // Identify the end cases.
1014 //
1015
1016 // Identifies cases (a) and (b)
1017 endOfSequence = isLayoutVertex || adjacents.size() != 2;
1018
1019 if (!endOfSequence) {
1020 // This is a tricky part. We peek at the next vertex to find out whether
1021 //
1022 // - we already visited the next vertex (c);
1023 // - the next anchor is a center (d).
1024 //
1025 // Those are needed to identify the remaining end of sequence cases. Note that unlike
1026 // (a) and (b), we preempt the end of sequence by looking into the next vertex.
1027
1028 // Peek at the next vertex
1029 AnchorVertex *after;
1030 if (candidates.isEmpty())
1031 after = (beforeSequence == adjacents.last() ? adjacents.first() : adjacents.last());
1032 else
1033 after = (candidates.constLast() == adjacents.last() ? adjacents.first() : adjacents.last());
1034
1035 // ### At this point we assumed that candidates will not contain 'after', this may not hold
1036 // when simplifying FLOATing anchors.
1037 Q_ASSERT(!candidates.contains(after));
1038
1039 const AnchorData *data = g.edgeData(v, after);
1040 Q_ASSERT(data);
1041 const bool cycleFound = visited.contains(after);
1042
1043 // Now cases (c) and (d)...
1044 endOfSequence = cycleFound || data->isCenterAnchor;
1045
1046 if (!endOfSequence) {
1047 // If it's not an end of sequence, then the vertex didn't trigger neither of the
1048 // previously three cases, so it can be added to the candidates list.
1049 candidates.append(v);
1050 } else if (cycleFound && (beforeSequence != after)) {
1051 afterSequence = after;
1052 candidates.append(v);
1053 }
1054 }
1055
1056 //
1057 // Add next non-visited vertices to the stack.
1058 //
1059 for (int i = 0; i < adjacents.size(); ++i) {
1060 AnchorVertex *next = adjacents.at(i);
1061 if (visited.contains(next))
1062 continue;
1063
1064 // If current vertex is an end of sequence, and it'll reset the candidates list. So
1065 // the next vertices will build candidates lists with the current vertex as 'before'
1066 // vertex. If it's not an end of sequence, we keep the original 'before' vertex,
1067 // since we are keeping the candidates list.
1068 if (endOfSequence)
1069 stack.push(std::pair(v, next));
1070 else
1071 stack.push(std::pair(beforeSequence, next));
1072 }
1073
1074 visited.insert(v);
1075
1076 if (!endOfSequence || candidates.isEmpty())
1077 continue;
1078
1079 //
1080 // Create a sequence for (beforeSequence, candidates, afterSequence).
1081 //
1082
1083 // One restriction we have is to not simplify half of an anchor and let the other half
1084 // unsimplified. So we remove center edges before and after the sequence.
1085 const AnchorData *firstAnchor = g.edgeData(beforeSequence, candidates.constFirst());
1086 if (firstAnchor->isCenterAnchor) {
1087 beforeSequence = candidates.constFirst();
1088 candidates.remove(0);
1089
1090 // If there's not candidates to be simplified, leave.
1091 if (candidates.isEmpty())
1092 continue;
1093 }
1094
1095 const AnchorData *lastAnchor = g.edgeData(candidates.constLast(), afterSequence);
1096 if (lastAnchor->isCenterAnchor) {
1097 afterSequence = candidates.constLast();
1098 candidates.remove(candidates.size() - 1);
1099
1100 if (candidates.isEmpty())
1101 continue;
1102 }
1103
1104 //
1105 // Add the sequence to the graph.
1106 //
1107
1108 AnchorData *sequence = createSequence(&g, beforeSequence, candidates, afterSequence);
1109
1110 // If 'beforeSequence' and 'afterSequence' already had an anchor between them, we'll
1111 // create a parallel anchor between the new sequence and the old anchor.
1112 bool newFeasible;
1113 AnchorData *newAnchor = addAnchorMaybeParallel(sequence, &newFeasible);
1114
1115 if (!newFeasible) {
1116 *feasible = false;
1117 return false;
1118 }
1119
1120 // When a new parallel anchor is create in the graph, we finish the iteration and return
1121 // true to indicate a new iteration is needed. This happens because a parallel anchor
1122 // changes the number of adjacents one vertex has, possibly opening up oportunities for
1123 // building candidate lists (when adjacents == 2).
1124 if (newAnchor != sequence)
1125 return true;
1126
1127 // If there was no parallel simplification, we'll keep walking the graph. So we clear the
1128 // candidates list to start again.
1129 candidates.clear();
1130 }
1131
1132 return false;
1133}
1134
1135void QGraphicsAnchorLayoutPrivate::restoreSimplifiedAnchor(AnchorData *edge)
1136{
1137 const Qt::Orientation orientation = edge->isVertical ? Qt::Vertical : Qt::Horizontal;
1138#if 0
1139 static const char *anchortypes[] = {"Normal",
1140 "Sequential",
1141 "Parallel"};
1142 qDebug("Restoring %s edge.", anchortypes[int(edge->type)]);
1143#endif
1144
1145 Graph<AnchorVertex, AnchorData> &g = graph[orientation];
1146
1147 if (edge->type == AnchorData::Normal) {
1148 g.createEdge(edge->from, edge->to, edge);
1149
1150 } else if (edge->type == AnchorData::Sequential) {
1151 const auto *sequence = static_cast<SequentialAnchorData *>(edge);
1152
1153 for (AnchorData *data : sequence->m_edges)
1154 restoreSimplifiedAnchor(data);
1155
1156 delete sequence;
1157
1158 } else if (edge->type == AnchorData::Parallel) {
1159
1160 // Skip parallel anchors that were created by vertex simplification, they will be processed
1161 // later, when restoring vertex simplification.
1162 // ### we could improve this check bit having a bit inside 'edge'
1163 if (anchorsFromSimplifiedVertices[orientation].contains(edge))
1164 return;
1165
1166 ParallelAnchorData* parallel = static_cast<ParallelAnchorData*>(edge);
1167 restoreSimplifiedConstraints(parallel);
1168
1169 // ### Because of the way parallel anchors are created in the anchor simplification
1170 // algorithm, we know that one of these will be a sequence, so it'll be safe if the other
1171 // anchor create an edge between the same vertices as the parallel.
1172 Q_ASSERT(parallel->firstEdge->type == AnchorData::Sequential
1173 || parallel->secondEdge->type == AnchorData::Sequential);
1174 restoreSimplifiedAnchor(parallel->firstEdge);
1175 restoreSimplifiedAnchor(parallel->secondEdge);
1176
1177 delete parallel;
1178 }
1179}
1180
1181void QGraphicsAnchorLayoutPrivate::restoreSimplifiedConstraints(ParallelAnchorData *parallel)
1182{
1183 if (!parallel->isCenterAnchor)
1184 return;
1185
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);
1191 }
1192
1193 // When restoring, we might have to revert constraints back. See comments on
1194 // addAnchorMaybeParallel().
1195 const bool needsReverse = !parallel->secondForward();
1196
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];
1200 if (needsReverse)
1201 v *= -1;
1202 c->variables.remove(parallel);
1203 c->variables.insert(parallel->secondEdge, v);
1204 }
1205}
1206
1207void QGraphicsAnchorLayoutPrivate::restoreSimplifiedGraph(Qt::Orientation orientation)
1208{
1209#if 0
1210 qDebug("Restoring Simplified Graph for %s",
1211 orientation == Horizontal ? "Horizontal" : "Vertical");
1212#endif
1213
1214 // Restore anchor simplification
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);
1221
1222 // We restore only sequential anchors and parallels that were not created by
1223 // vertex simplification.
1224 if (edge->type == AnchorData::Sequential
1225 || (edge->type == AnchorData::Parallel &&
1226 !anchorsFromSimplifiedVertices[orientation].contains(edge))) {
1227
1228 g.takeEdge(v1, v2);
1229 restoreSimplifiedAnchor(edge);
1230 }
1231 }
1232
1233 restoreVertices(orientation);
1234}
1235
1236void QGraphicsAnchorLayoutPrivate::restoreVertices(Qt::Orientation orientation)
1237{
1238 Q_Q(QGraphicsAnchorLayout);
1239
1240 Graph<AnchorVertex, AnchorData> &g = graph[orientation];
1241 QList<AnchorVertexPair *> &toRestore = simplifiedVertices[orientation];
1242
1243 // Since we keep a list of parallel anchors and vertices that were created during vertex
1244 // simplification, we can now iterate on those lists instead of traversing the graph
1245 // recursively.
1246
1247 // First, restore the constraints changed when we created parallel anchors. Note that this
1248 // works at this point because the constraints doesn't depend on vertex information and at
1249 // this point it's always safe to identify whether the second child is forward or backwards.
1250 // In the next step, we'll change the anchors vertices so that would not be possible anymore.
1251 QList<AnchorData *> &parallelAnchors = anchorsFromSimplifiedVertices[orientation];
1252
1253 for (int i = parallelAnchors.size() - 1; i >= 0; --i) {
1254 ParallelAnchorData *parallel = static_cast<ParallelAnchorData *>(parallelAnchors.at(i));
1255 restoreSimplifiedConstraints(parallel);
1256 }
1257
1258 // Then, we will restore the vertices in the inverse order of creation, this way we ensure that
1259 // the vertex being restored was not wrapped by another simplification.
1260 for (int i = toRestore.size() - 1; i >= 0; --i) {
1261 AnchorVertexPair *pair = toRestore.at(i);
1262 QList<AnchorVertex *> adjacents = g.adjacentVertices(pair);
1263
1264 // Restore the removed edge, this will also restore both vertices 'first' and 'second' to
1265 // the graph structure.
1266 AnchorVertex *first = pair->m_first;
1267 AnchorVertex *second = pair->m_second;
1268 g.createEdge(first, second, pair->m_removedAnchor);
1269
1270 // Restore the anchors for the first child vertex
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);
1274
1275 replaceVertex_helper(ad, pair, first);
1276 g.createEdge(ad->from, ad->to, ad);
1277 }
1278
1279 // Restore the anchors for the second child vertex
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);
1283
1284 replaceVertex_helper(ad, pair, second);
1285 g.createEdge(ad->from, ad->to, ad);
1286 }
1287
1288 for (int j = 0; j < adjacents.size(); ++j) {
1289 g.takeEdge(pair, adjacents.at(j));
1290 }
1291
1292 // The pair simplified a layout vertex, so place back the correct vertex in the variable
1293 // that track layout vertices
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);
1298 }
1299
1300 delete pair;
1301 }
1302 qDeleteAll(parallelAnchors);
1303 parallelAnchors.clear();
1304 toRestore.clear();
1305}
1306
1307Qt::Orientation
1308QGraphicsAnchorLayoutPrivate::edgeOrientation(Qt::AnchorPoint edge) noexcept
1309{
1310 return edge > Qt::AnchorRight ? Qt::Vertical : Qt::Horizontal;
1311}
1312
1313/*!
1314 \internal
1315
1316 Create internal anchors to connect the layout edges (Left to Right and
1317 Top to Bottom).
1318
1319 These anchors doesn't have size restrictions, that will be enforced by
1320 other anchors and items in the layout.
1321*/
1322void QGraphicsAnchorLayoutPrivate::createLayoutEdges()
1323{
1324 Q_Q(QGraphicsAnchorLayout);
1325 QGraphicsLayoutItem *layout = q;
1326
1327 // Horizontal
1328 AnchorData *data = new AnchorData;
1329 addAnchor_helper(layout, Qt::AnchorLeft, layout,
1330 Qt::AnchorRight, data);
1331 data->maxSize = QWIDGETSIZE_MAX;
1332
1333 // Save a reference to layout vertices
1334 layoutFirstVertex[Qt::Horizontal] = internalVertex(layout, Qt::AnchorLeft);
1335 layoutCentralVertex[Qt::Horizontal] = nullptr;
1336 layoutLastVertex[Qt::Horizontal] = internalVertex(layout, Qt::AnchorRight);
1337
1338 // Vertical
1339 data = new AnchorData;
1340 addAnchor_helper(layout, Qt::AnchorTop, layout,
1341 Qt::AnchorBottom, data);
1342 data->maxSize = QWIDGETSIZE_MAX;
1343
1344 // Save a reference to layout vertices
1345 layoutFirstVertex[Qt::Vertical] = internalVertex(layout, Qt::AnchorTop);
1346 layoutCentralVertex[Qt::Vertical] = nullptr;
1347 layoutLastVertex[Qt::Vertical] = internalVertex(layout, Qt::AnchorBottom);
1348}
1349
1350void QGraphicsAnchorLayoutPrivate::deleteLayoutEdges()
1351{
1352 Q_Q(QGraphicsAnchorLayout);
1353
1354 Q_ASSERT(!internalVertex(q, Qt::AnchorHorizontalCenter));
1355 Q_ASSERT(!internalVertex(q, Qt::AnchorVerticalCenter));
1356
1357 removeAnchor_helper(internalVertex(q, Qt::AnchorLeft),
1358 internalVertex(q, Qt::AnchorRight));
1359 removeAnchor_helper(internalVertex(q, Qt::AnchorTop),
1360 internalVertex(q, Qt::AnchorBottom));
1361}
1362
1363void QGraphicsAnchorLayoutPrivate::createItemEdges(QGraphicsLayoutItem *item)
1364{
1365 items.append(item);
1366
1367 // Create horizontal and vertical internal anchors for the item and
1368 // refresh its size hint / policy values.
1369 AnchorData *data = new AnchorData;
1370 addAnchor_helper(item, Qt::AnchorLeft, item, Qt::AnchorRight, data);
1371 data->refreshSizeHints();
1372
1373 data = new AnchorData;
1374 addAnchor_helper(item, Qt::AnchorTop, item, Qt::AnchorBottom, data);
1375 data->refreshSizeHints();
1376}
1377
1378/*!
1379 \internal
1380
1381 By default, each item in the layout is represented internally as
1382 a single anchor in each direction. For instance, from Left to Right.
1383
1384 However, to support anchorage of items to the center of items, we
1385 must split this internal anchor into two half-anchors. From Left
1386 to Center and then from Center to Right, with the restriction that
1387 these anchors must have the same time at all times.
1388*/
1389void QGraphicsAnchorLayoutPrivate::createCenterAnchors(
1390 QGraphicsLayoutItem *item, Qt::AnchorPoint centerEdge)
1391{
1392 Q_Q(QGraphicsAnchorLayout);
1393
1394 Qt::Orientation orientation;
1395 switch (centerEdge) {
1396 case Qt::AnchorHorizontalCenter:
1397 orientation = Qt::Horizontal;
1398 break;
1399 case Qt::AnchorVerticalCenter:
1400 orientation = Qt::Vertical;
1401 break;
1402 default:
1403 // Don't create center edges unless needed
1404 return;
1405 }
1406
1407 // Check if vertex already exists
1408 if (internalVertex(item, centerEdge))
1409 return;
1410
1411 // Orientation code
1412 Qt::AnchorPoint firstEdge;
1413 Qt::AnchorPoint lastEdge;
1414
1415 if (orientation == Qt::Horizontal) {
1416 firstEdge = Qt::AnchorLeft;
1417 lastEdge = Qt::AnchorRight;
1418 } else {
1419 firstEdge = Qt::AnchorTop;
1420 lastEdge = Qt::AnchorBottom;
1421 }
1422
1423 AnchorVertex *first = internalVertex(item, firstEdge);
1424 AnchorVertex *last = internalVertex(item, lastEdge);
1425 Q_ASSERT(first && last);
1426
1427 // Create new anchors
1428 QSimplexConstraint *c = new QSimplexConstraint;
1429
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();
1436
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();
1443
1444 itemCenterConstraints[orientation].append(c);
1445
1446 // Remove old one
1447 removeAnchor_helper(first, last);
1448
1449 if (item == q) {
1450 layoutCentralVertex[orientation] = internalVertex(q, centerEdge);
1451 }
1452}
1453
1454void QGraphicsAnchorLayoutPrivate::removeCenterAnchors(
1455 QGraphicsLayoutItem *item, Qt::AnchorPoint centerEdge,
1456 bool substitute)
1457{
1458 Q_Q(QGraphicsAnchorLayout);
1459
1460 Qt::Orientation orientation;
1461 switch (centerEdge) {
1462 case Qt::AnchorHorizontalCenter:
1463 orientation = Qt::Horizontal;
1464 break;
1465 case Qt::AnchorVerticalCenter:
1466 orientation = Qt::Vertical;
1467 break;
1468 default:
1469 // Don't remove edges that not the center ones
1470 return;
1471 }
1472
1473 // Orientation code
1474 Qt::AnchorPoint firstEdge;
1475 Qt::AnchorPoint lastEdge;
1476
1477 if (orientation == Qt::Horizontal) {
1478 firstEdge = Qt::AnchorLeft;
1479 lastEdge = Qt::AnchorRight;
1480 } else {
1481 firstEdge = Qt::AnchorTop;
1482 lastEdge = Qt::AnchorBottom;
1483 }
1484
1485 AnchorVertex *center = internalVertex(item, centerEdge);
1486 if (!center)
1487 return;
1488 AnchorVertex *first = internalVertex(item, firstEdge);
1489
1490 Q_ASSERT(first);
1491 Q_ASSERT(center);
1492
1493 Graph<AnchorVertex, AnchorData> &g = graph[orientation];
1494
1495
1496 AnchorData *oldData = g.edgeData(first, center);
1497 // Remove center constraint
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);
1501 break;
1502 }
1503 }
1504
1505 if (substitute) {
1506 // Create the new anchor that should substitute the left-center-right anchors.
1507 AnchorData *data = new AnchorData;
1508 addAnchor_helper(item, firstEdge, item, lastEdge, data);
1509 data->refreshSizeHints();
1510
1511 // Remove old anchors
1512 removeAnchor_helper(first, center);
1513 removeAnchor_helper(center, internalVertex(item, lastEdge));
1514
1515 } else {
1516 // this is only called from removeAnchors()
1517 // first, remove all non-internal anchors
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));
1523 }
1524 }
1525 // when all non-internal anchors is removed it will automatically merge the
1526 // center anchor into a left-right (or top-bottom) anchor. We must also delete that.
1527 // by this time, the center vertex is deleted and merged into a non-centered internal anchor
1528 removeAnchor_helper(first, internalVertex(item, lastEdge));
1529 }
1530
1531 if (item == q) {
1532 layoutCentralVertex[orientation] = nullptr;
1533 }
1534}
1535
1536
1537void QGraphicsAnchorLayoutPrivate::removeCenterConstraints(QGraphicsLayoutItem *item,
1538 Qt::Orientation orientation)
1539{
1540 // Remove the item center constraints associated to this item
1541 // ### This is a temporary solution. We should probably use a better
1542 // data structure to hold items and/or their associated constraints
1543 // so that we can remove those easily
1544
1545 AnchorVertex *first = internalVertex(item, orientation == Qt::Horizontal ?
1546 Qt::AnchorLeft :
1547 Qt::AnchorTop);
1548 AnchorVertex *center = internalVertex(item, orientation == Qt::Horizontal ?
1549 Qt::AnchorHorizontalCenter :
1550 Qt::AnchorVerticalCenter);
1551
1552 // Skip if no center constraints exist
1553 if (!center)
1554 return;
1555
1556 Q_ASSERT(first);
1557 AnchorData *internalAnchor = graph[orientation].edgeData(first, center);
1558
1559 // Look for our anchor in all item center constraints, then remove it
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);
1563 break;
1564 }
1565 }
1566}
1567
1568/*!
1569 * \internal
1570 * Implements the high level "addAnchor" feature. Called by the public API
1571 * addAnchor method.
1572 *
1573 * The optional \a spacing argument defines the size of the anchor. If not provided,
1574 * the anchor size is either 0 or not-set, depending on type of anchor created (see
1575 * matrix below).
1576 *
1577 * All anchors that remain with size not-set will assume the standard spacing,
1578 * set either by the layout style or through the "setSpacing" layout API.
1579 */
1580QGraphicsAnchor *QGraphicsAnchorLayoutPrivate::addAnchor(QGraphicsLayoutItem *firstItem,
1581 Qt::AnchorPoint firstEdge,
1582 QGraphicsLayoutItem *secondItem,
1583 Qt::AnchorPoint secondEdge,
1584 qreal *spacing)
1585{
1586 Q_Q(QGraphicsAnchorLayout);
1587 if ((firstItem == nullptr) || (secondItem == nullptr)) {
1588 qWarning("QGraphicsAnchorLayout::addAnchor(): "
1589 "Cannot anchor NULL items");
1590 return nullptr;
1591 }
1592
1593 if (firstItem == secondItem) {
1594 qWarning("QGraphicsAnchorLayout::addAnchor(): "
1595 "Cannot anchor the item to itself");
1596 return nullptr;
1597 }
1598
1599 if (edgeOrientation(secondEdge) != edgeOrientation(firstEdge)) {
1600 qWarning("QGraphicsAnchorLayout::addAnchor(): "
1601 "Cannot anchor edges of different orientations");
1602 return nullptr;
1603 }
1604
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.");
1609 return nullptr;
1610 }
1611
1612 // In QGraphicsAnchorLayout, items are represented in its internal
1613 // graph as four anchors that connect:
1614 // - Left -> HCenter
1615 // - HCenter-> Right
1616 // - Top -> VCenter
1617 // - VCenter -> Bottom
1618
1619 // Ensure that the internal anchors have been created for both items.
1620 if (firstItem != q && !items.contains(firstItem)) {
1621 createItemEdges(firstItem);
1622 addChildLayoutItem(firstItem);
1623 }
1624 if (secondItem != q && !items.contains(secondItem)) {
1625 createItemEdges(secondItem);
1626 addChildLayoutItem(secondItem);
1627 }
1628
1629 // Create center edges if needed
1630 createCenterAnchors(firstItem, firstEdge);
1631 createCenterAnchors(secondItem, secondEdge);
1632
1633 // Use heuristics to find out what the user meant with this anchor.
1634 correctEdgeDirection(firstItem, firstEdge, secondItem, secondEdge);
1635
1636 AnchorData *data = new AnchorData;
1637 QGraphicsAnchor *graphicsAnchor = acquireGraphicsAnchor(data);
1638
1639 addAnchor_helper(firstItem, firstEdge, secondItem, secondEdge, data);
1640
1641 if (spacing) {
1642 graphicsAnchor->setSpacing(*spacing);
1643 } else {
1644 // If firstItem or secondItem is the layout itself, the spacing will default to 0.
1645 // Otherwise, the following matrix is used (questionmark means that the spacing
1646 // is queried from the style):
1647 // from
1648 // to Left HCenter Right
1649 // Left 0 0 ?
1650 // HCenter 0 0 0
1651 // Right ? 0 0
1652 if (firstItem == q
1653 || secondItem == q
1654 || pickEdge(firstEdge, Qt::Horizontal) == Qt::AnchorHorizontalCenter
1655 || oppositeEdge(firstEdge) != secondEdge) {
1656 graphicsAnchor->setSpacing(0);
1657 } else {
1658 graphicsAnchor->unsetSpacing();
1659 }
1660 }
1661
1662 return graphicsAnchor;
1663}
1664
1665/*
1666 \internal
1667
1668 This method adds an AnchorData to the internal graph. It is responsible for doing
1669 the boilerplate part of such task.
1670
1671 If another AnchorData exists between the mentioned vertices, it is deleted and
1672 the new one is inserted.
1673*/
1674void QGraphicsAnchorLayoutPrivate::addAnchor_helper(QGraphicsLayoutItem *firstItem,
1675 Qt::AnchorPoint firstEdge,
1676 QGraphicsLayoutItem *secondItem,
1677 Qt::AnchorPoint secondEdge,
1678 AnchorData *data)
1679{
1680 Q_Q(QGraphicsAnchorLayout);
1681
1682 const Qt::Orientation orientation = edgeOrientation(firstEdge);
1683
1684 // Create or increase the reference count for the related vertices.
1685 AnchorVertex *v1 = addInternalVertex(firstItem, firstEdge);
1686 AnchorVertex *v2 = addInternalVertex(secondItem, secondEdge);
1687
1688 // Remove previous anchor
1689 if (graph[orientation].edgeData(v1, v2)) {
1690 removeAnchor_helper(v1, v2);
1691 }
1692
1693 // If its an internal anchor, set the associated item
1694 if (firstItem == secondItem)
1695 data->item = firstItem;
1696
1697 data->isVertical = orientation == Qt::Vertical;
1698
1699 // Create a bi-directional edge in the sense it can be transversed both
1700 // from v1 or v2. "data" however is shared between the two references
1701 // so we still know that the anchor direction is from 1 to 2.
1702 data->from = v1;
1703 data->to = v2;
1704#ifdef QT_DEBUG
1705 data->name = QString::fromLatin1("%1 --to--> %2").arg(v1->toString(), v2->toString());
1706#endif
1707 // ### bit to track internal anchors, since inside AnchorData methods
1708 // we don't have access to the 'q' pointer.
1709 data->isLayoutAnchor = (data->item == q);
1710
1711 graph[orientation].createEdge(v1, v2, data);
1712}
1713
1714QGraphicsAnchor *QGraphicsAnchorLayoutPrivate::getAnchor(QGraphicsLayoutItem *firstItem,
1715 Qt::AnchorPoint firstEdge,
1716 QGraphicsLayoutItem *secondItem,
1717 Qt::AnchorPoint secondEdge)
1718{
1719 // Do not expose internal anchors
1720 if (firstItem == secondItem)
1721 return nullptr;
1722
1723 const Qt::Orientation orientation = edgeOrientation(firstEdge);
1724 AnchorVertex *v1 = internalVertex(firstItem, firstEdge);
1725 AnchorVertex *v2 = internalVertex(secondItem, secondEdge);
1726
1727 QGraphicsAnchor *graphicsAnchor = nullptr;
1728
1729 AnchorData *data = graph[orientation].edgeData(v1, v2);
1730 if (data) {
1731 // We could use "acquireGraphicsAnchor" here, but to avoid a regression where
1732 // an internal anchor was wrongly exposed, I want to ensure no new
1733 // QGraphicsAnchor instances are created by this call.
1734 // This assumption must hold because anchors are either user-created (and already
1735 // have their public object created), or they are internal (and must not reach
1736 // this point).
1737 Q_ASSERT(data->graphicsAnchor);
1738 graphicsAnchor = data->graphicsAnchor;
1739 }
1740 return graphicsAnchor;
1741}
1742
1743/*!
1744 * \internal
1745 *
1746 * Implements the high level "removeAnchor" feature. Called by
1747 * the QAnchorData destructor.
1748 */
1749void QGraphicsAnchorLayoutPrivate::removeAnchor(AnchorVertex *firstVertex,
1750 AnchorVertex *secondVertex)
1751{
1752 Q_Q(QGraphicsAnchorLayout);
1753
1754 // Save references to items while it's safe to assume the vertices exist
1755 QGraphicsLayoutItem *firstItem = firstVertex->m_item;
1756 QGraphicsLayoutItem *secondItem = secondVertex->m_item;
1757
1758 // Delete the anchor (may trigger deletion of center vertices)
1759 removeAnchor_helper(firstVertex, secondVertex);
1760
1761 // Ensure no dangling pointer is left behind
1762 firstVertex = secondVertex = nullptr;
1763
1764 // Checking if the item stays in the layout or not
1765 bool keepFirstItem = false;
1766 bool keepSecondItem = false;
1767
1768 std::pair<AnchorVertex *, int> v;
1769 int refcount = -1;
1770
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)));
1774 if (v.first) {
1775 if (i == Qt::AnchorHorizontalCenter || i == Qt::AnchorVerticalCenter)
1776 refcount = 2;
1777 else
1778 refcount = 1;
1779
1780 if (v.second > refcount) {
1781 keepFirstItem = true;
1782 break;
1783 }
1784 }
1785 }
1786 } else
1787 keepFirstItem = true;
1788
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)));
1792 if (v.first) {
1793 if (i == Qt::AnchorHorizontalCenter || i == Qt::AnchorVerticalCenter)
1794 refcount = 2;
1795 else
1796 refcount = 1;
1797
1798 if (v.second > refcount) {
1799 keepSecondItem = true;
1800 break;
1801 }
1802 }
1803 }
1804 } else
1805 keepSecondItem = true;
1806
1807 if (!keepFirstItem)
1808 q->removeAt(items.indexOf(firstItem));
1809
1810 if (!keepSecondItem)
1811 q->removeAt(items.indexOf(secondItem));
1812
1813 // Removing anchors invalidates the layout
1814 q->invalidate();
1815}
1816
1817/*
1818 \internal
1819
1820 Implements the low level "removeAnchor" feature. Called by
1821 private methods.
1822*/
1823void QGraphicsAnchorLayoutPrivate::removeAnchor_helper(AnchorVertex *v1, AnchorVertex *v2)
1824{
1825 Q_ASSERT(v1 && v2);
1826
1827 // Remove edge from graph
1828 const Qt::Orientation o = edgeOrientation(v1->m_edge);
1829 graph[o].removeEdge(v1, v2);
1830
1831 // Decrease vertices reference count (may trigger a deletion)
1832 removeInternalVertex(v1->m_item, v1->m_edge);
1833 removeInternalVertex(v2->m_item, v2->m_edge);
1834}
1835
1836AnchorVertex *QGraphicsAnchorLayoutPrivate::addInternalVertex(QGraphicsLayoutItem *item,
1837 Qt::AnchorPoint edge)
1838{
1839 std::pair<QGraphicsLayoutItem *, Qt::AnchorPoint> pair(item, edge);
1840 std::pair<AnchorVertex *, int> v = m_vertexList.value(pair);
1841
1842 if (!v.first) {
1843 Q_ASSERT(v.second == 0);
1844 v.first = new AnchorVertex(item, edge);
1845 }
1846 v.second++;
1847 m_vertexList.insert(pair, v);
1848 return v.first;
1849}
1850
1851/**
1852 * \internal
1853 *
1854 * returns the AnchorVertex that was dereferenced, also when it was removed.
1855 * returns 0 if it did not exist.
1856 */
1857void QGraphicsAnchorLayoutPrivate::removeInternalVertex(QGraphicsLayoutItem *item,
1858 Qt::AnchorPoint edge)
1859{
1860 std::pair<QGraphicsLayoutItem *, Qt::AnchorPoint> pair(item, edge);
1861 std::pair<AnchorVertex *, int> v = m_vertexList.value(pair);
1862
1863 if (!v.first) {
1864 qWarning("This item with this edge is not in the graph");
1865 return;
1866 }
1867
1868 v.second--;
1869 if (v.second == 0) {
1870 // Remove reference and delete vertex
1871 m_vertexList.remove(pair);
1872 delete v.first;
1873 } else {
1874 // Update reference count
1875 m_vertexList.insert(pair, v);
1876
1877 if ((v.second == 2) &&
1878 ((edge == Qt::AnchorHorizontalCenter) ||
1879 (edge == Qt::AnchorVerticalCenter))) {
1880 removeCenterAnchors(item, edge, true);
1881 }
1882 }
1883}
1884
1885void QGraphicsAnchorLayoutPrivate::removeVertex(QGraphicsLayoutItem *item, Qt::AnchorPoint edge)
1886{
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);
1894 }
1895 }
1896}
1897
1898void QGraphicsAnchorLayoutPrivate::removeAnchors(QGraphicsLayoutItem *item)
1899{
1900 // remove the center anchor first!!
1901 removeCenterAnchors(item, Qt::AnchorHorizontalCenter, false);
1902 removeVertex(item, Qt::AnchorLeft);
1903 removeVertex(item, Qt::AnchorRight);
1904
1905 removeCenterAnchors(item, Qt::AnchorVerticalCenter, false);
1906 removeVertex(item, Qt::AnchorTop);
1907 removeVertex(item, Qt::AnchorBottom);
1908}
1909
1910/*!
1911 \internal
1912
1913 Use heuristics to determine the correct orientation of a given anchor.
1914
1915 After API discussions, we decided we would like expressions like
1916 anchor(A, Left, B, Right) to mean the same as anchor(B, Right, A, Left).
1917 The problem with this is that anchors could become ambiguous, for
1918 instance, what does the anchor A, B of size X mean?
1919
1920 "pos(B) = pos(A) + X" or "pos(A) = pos(B) + X" ?
1921
1922 To keep the API user friendly and at the same time, keep our algorithm
1923 deterministic, we use an heuristic to determine a direction for each
1924 added anchor and then keep it. The heuristic is based on the fact
1925 that people usually avoid overlapping items, therefore:
1926
1927 "A, RIGHT to B, LEFT" means that B is to the LEFT of A.
1928 "B, LEFT to A, RIGHT" is corrected to the above anchor.
1929
1930 Special correction is also applied when one of the items is the
1931 layout. We handle Layout Left as if it was another items's Right
1932 and Layout Right as another item's Left.
1933*/
1934void QGraphicsAnchorLayoutPrivate::correctEdgeDirection(QGraphicsLayoutItem *&firstItem,
1935 Qt::AnchorPoint &firstEdge,
1936 QGraphicsLayoutItem *&secondItem,
1937 Qt::AnchorPoint &secondEdge)
1938{
1939 Q_Q(QGraphicsAnchorLayout);
1940
1941 if ((firstItem != q) && (secondItem != q)) {
1942 // If connection is between widgets (not the layout itself)
1943 // Ensure that "right-edges" sit to the left of "left-edges".
1944 if (firstEdge < secondEdge) {
1945 qSwap(firstItem, secondItem);
1946 qSwap(firstEdge, secondEdge);
1947 }
1948 } else if (firstItem == q) {
1949 // If connection involves the right or bottom of a layout, ensure
1950 // the layout is the second item.
1951 if ((firstEdge == Qt::AnchorRight) || (firstEdge == Qt::AnchorBottom)) {
1952 qSwap(firstItem, secondItem);
1953 qSwap(firstEdge, secondEdge);
1954 }
1955 } else if ((secondEdge != Qt::AnchorRight) && (secondEdge != Qt::AnchorBottom)) {
1956 // If connection involves the left, center or top of layout, ensure
1957 // the layout is the first item.
1958 qSwap(firstItem, secondItem);
1959 qSwap(firstEdge, secondEdge);
1960 }
1961}
1962
1963QLayoutStyleInfo &QGraphicsAnchorLayoutPrivate::styleInfo() const
1964{
1965 if (styleInfoDirty) {
1966 Q_Q(const QGraphicsAnchorLayout);
1967 //### Fix this if QGV ever gets support for Metal style or different Aqua sizes.
1968 QWidget *wid = nullptr;
1969
1970 QGraphicsLayoutItem *parent = q->parentLayoutItem();
1971 while (parent && parent->isLayout()) {
1972 parent = parent->parentLayoutItem();
1973 }
1974 QGraphicsWidget *w = nullptr;
1975 if (parent) {
1976 QGraphicsItem *parentItem = parent->graphicsItem();
1977 if (parentItem && parentItem->isWidget())
1978 w = static_cast<QGraphicsWidget*>(parentItem);
1979 }
1980
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]);
1985
1986 styleInfoDirty = false;
1987 }
1988 return cachedStyleInfo;
1989}
1990
1991/*!
1992 \internal
1993
1994 Called on activation. Uses Linear Programming to define minimum, preferred
1995 and maximum sizes for the layout. Also calculates the sizes that each item
1996 should assume when the layout is in one of such situations.
1997*/
1998void QGraphicsAnchorLayoutPrivate::calculateGraphs()
1999{
2000 if (!calculateGraphCacheDirty)
2001 return;
2002 calculateGraphs(Qt::Horizontal);
2003 calculateGraphs(Qt::Vertical);
2004 calculateGraphCacheDirty = false;
2005}
2006
2007// ### Maybe getGraphParts could return the variables when traversing, at least
2008// for trunk...
2009QList<AnchorData *> getVariables(const QList<QSimplexConstraint *> &constraints)
2010{
2011 QSet<AnchorData *> variableSet;
2012 for (int i = 0; i < constraints.size(); ++i) {
2013 const QSimplexConstraint *c = constraints.at(i);
2014 for (auto it = c->variables.cbegin(), end = c->variables.cend(); it != end; ++it)
2015 variableSet.insert(static_cast<AnchorData *>(it.key()));
2016 }
2017 return variableSet.values();
2018}
2019
2020/*!
2021 \internal
2022
2023 Calculate graphs is the method that puts together all the helper routines
2024 so that the AnchorLayout can calculate the sizes of each item.
2025
2026 In a nutshell it should do:
2027
2028 1) Refresh anchor nominal sizes, that is, the size that each anchor would
2029 have if no other restrictions applied. This is done by querying the
2030 layout style and the sizeHints of the items belonging to the layout.
2031
2032 2) Simplify the graph by grouping together parallel and sequential anchors
2033 into "group anchors". These have equivalent minimum, preferred and maximum
2034 sizeHints as the anchors they replace.
2035
2036 3) Check if we got to a trivial case. In some cases, the whole graph can be
2037 simplified into a single anchor. If so, use this information. If not,
2038 then call the Simplex solver to calculate the anchors sizes.
2039
2040 4) Once the root anchors had its sizes calculated, propagate that to the
2041 anchors they represent.
2042*/
2043void QGraphicsAnchorLayoutPrivate::calculateGraphs(Qt::Orientation orientation)
2044{
2045#if defined(QT_DEBUG) || defined(QT_BUILD_INTERNAL)
2046 lastCalculationUsedSimplex[orientation] = false;
2047#endif
2048
2049 static bool simplificationEnabled = qEnvironmentVariableIsEmpty("QT_ANCHORLAYOUT_NO_SIMPLIFICATION");
2050
2051 // Reset the nominal sizes of each anchor based on the current item sizes
2052 refreshAllSizeHints(orientation);
2053
2054 // Simplify the graph
2055 if (simplificationEnabled && !simplifyGraph(orientation)) {
2056 qWarning("QGraphicsAnchorLayout: anchor setup is not feasible.");
2057 graphHasConflicts[orientation] = true;
2058 return;
2059 }
2060
2061 // Traverse all graph edges and store the possible paths to each vertex
2062 findPaths(orientation);
2063
2064 // From the paths calculated above, extract the constraints that the current
2065 // anchor setup impose, to our Linear Programming problem.
2066 constraintsFromPaths(orientation);
2067
2068 // Split the constraints and anchors into groups that should be fed to the
2069 // simplex solver independently. Currently we find two groups:
2070 //
2071 // 1) The "trunk", that is, the set of anchors (items) that are connected
2072 // to the two opposite sides of our layout, and thus need to stretch in
2073 // order to fit in the current layout size.
2074 //
2075 // 2) The floating or semi-floating anchors (items) that are those which
2076 // are connected to only one (or none) of the layout sides, thus are not
2077 // influenced by the layout size.
2078 const auto parts = getGraphParts(orientation);
2079
2080 // Now run the simplex solver to calculate Minimum, Preferred and Maximum sizes
2081 // of the "trunk" set of constraints and variables.
2082 // ### does trunk always exist? empty = trunk is the layout left->center->right
2083 const QList<AnchorData *> trunkVariables = getVariables(parts.trunkConstraints);
2084
2085 // For minimum and maximum, use the path between the two layout sides as the
2086 // objective function.
2087 AnchorVertex *v = layoutLastVertex[orientation];
2088 GraphPath trunkPath = graphPaths[orientation].value(v);
2089
2090 bool feasible = calculateTrunk(orientation, trunkPath, parts.trunkConstraints, trunkVariables);
2091
2092 // For the other parts that not the trunk, solve only for the preferred size
2093 // that is the size they will remain at, since they are not stretched by the
2094 // layout.
2095
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);
2100 }
2101
2102 // Propagate the new sizes down the simplified graph, ie. tell the
2103 // group anchors to set their children anchors sizes.
2104 updateAnchorSizes(orientation);
2105
2106 graphHasConflicts[orientation] = !feasible;
2107
2108 // Clean up our data structures. They are not needed anymore since
2109 // distribution uses just interpolation.
2110 qDeleteAll(constraints[orientation]);
2111 constraints[orientation].clear();
2112 graphPaths[orientation].clear(); // ###
2113
2114 if (simplificationEnabled)
2115 restoreSimplifiedGraph(orientation);
2116}
2117
2118/*!
2119 \internal
2120
2121 Shift all the constraints by a certain amount. This allows us to deal with negative values in
2122 the linear program if they are bounded by a certain limit. Functions should be careful to
2123 call it again with a negative amount, to shift the constraints back.
2124*/
2125static void shiftConstraints(const QList<QSimplexConstraint *> &constraints, qreal amount)
2126{
2127 for (int i = 0; i < constraints.size(); ++i) {
2128 QSimplexConstraint *c = constraints.at(i);
2129 const qreal multiplier = std::accumulate(c->variables.cbegin(), c->variables.cend(), qreal(0));
2130 c->constant += multiplier * amount;
2131 }
2132}
2133
2134/*!
2135 \internal
2136
2137 Calculate the sizes for all anchors which are part of the trunk. This works
2138 on top of a (possibly) simplified graph.
2139*/
2140bool QGraphicsAnchorLayoutPrivate::calculateTrunk(Qt::Orientation orientation, const GraphPath &path,
2141 const QList<QSimplexConstraint *> &constraints,
2142 const QList<AnchorData *> &variables)
2143{
2144 bool feasible = true;
2145 bool needsSimplex = !constraints.isEmpty();
2146
2147#if 0
2148 qDebug("Simplex %s for trunk of %s", needsSimplex ? "used" : "NOT used",
2149 orientation == Qt::Horizontal ? "Horizontal" : "Vertical");
2150#endif
2151
2152 if (needsSimplex) {
2153
2154 QList<QSimplexConstraint *> sizeHintConstraints = constraintsFromSizeHints(variables);
2155 QList<QSimplexConstraint *> allConstraints = constraints + sizeHintConstraints;
2156
2157 shiftConstraints(allConstraints, g_offset);
2158
2159 // Solve min and max size hints
2160 qreal min, max;
2161 feasible = solveMinMax(allConstraints, path, &min, &max);
2162
2163 if (feasible) {
2164 solvePreferred(constraints, variables);
2165
2166 // Calculate and set the preferred size for the layout,
2167 // from the edge sizes that were calculated above.
2168 qreal pref(0.0);
2169 for (const AnchorData *ad : path.positives)
2170 pref += ad->sizeAtPreferred;
2171 for (const AnchorData *ad : path.negatives)
2172 pref -= ad->sizeAtPreferred;
2173
2174 sizeHints[orientation][Qt::MinimumSize] = min;
2175 sizeHints[orientation][Qt::PreferredSize] = pref;
2176 sizeHints[orientation][Qt::MaximumSize] = max;
2177 }
2178
2179 qDeleteAll(sizeHintConstraints);
2180 shiftConstraints(constraints, -g_offset);
2181
2182 } else {
2183 // No Simplex is necessary because the path was simplified all the way to a single
2184 // anchor.
2185 Q_ASSERT(path.positives.size() == 1);
2186 Q_ASSERT(path.negatives.size() == 0);
2187
2188 AnchorData *ad = *path.positives.cbegin();
2189 ad->sizeAtMinimum = ad->minSize;
2190 ad->sizeAtPreferred = ad->prefSize;
2191 ad->sizeAtMaximum = ad->maxSize;
2192
2193 sizeHints[orientation][Qt::MinimumSize] = ad->sizeAtMinimum;
2194 sizeHints[orientation][Qt::PreferredSize] = ad->sizeAtPreferred;
2195 sizeHints[orientation][Qt::MaximumSize] = ad->sizeAtMaximum;
2196 }
2197
2198#if defined(QT_DEBUG) || defined(QT_BUILD_INTERNAL)
2199 lastCalculationUsedSimplex[orientation] = needsSimplex;
2200#endif
2201
2202 return feasible;
2203}
2204
2205/*!
2206 \internal
2207*/
2208bool QGraphicsAnchorLayoutPrivate::calculateNonTrunk(const QList<QSimplexConstraint *> &constraints,
2209 const QList<AnchorData *> &variables)
2210{
2211 shiftConstraints(constraints, g_offset);
2212 bool feasible = solvePreferred(constraints, variables);
2213
2214 if (feasible) {
2215 // Propagate size at preferred to other sizes. Semi-floats always will be
2216 // in their sizeAtPreferred.
2217 for (int j = 0; j < variables.size(); ++j) {
2218 AnchorData *ad = variables.at(j);
2219 Q_ASSERT(ad);
2220 ad->sizeAtMinimum = ad->sizeAtPreferred;
2221 ad->sizeAtMaximum = ad->sizeAtPreferred;
2222 }
2223 }
2224
2225 shiftConstraints(constraints, -g_offset);
2226 return feasible;
2227}
2228
2229/*!
2230 \internal
2231
2232 Traverse the graph refreshing the size hints. Edges will query their associated
2233 item or graphicsAnchor for their size hints.
2234*/
2235void QGraphicsAnchorLayoutPrivate::refreshAllSizeHints(Qt::Orientation orientation)
2236{
2237 Graph<AnchorVertex, AnchorData> &g = graph[orientation];
2238 QList<std::pair<AnchorVertex *, AnchorVertex *>> vertices = g.connections();
2239
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);
2244 }
2245}
2246
2247/*!
2248 \internal
2249
2250 This method walks the graph using a breadth-first search to find paths
2251 between the root vertex and each vertex on the graph. The edges
2252 directions in each path are considered and they are stored as a
2253 positive edge (left-to-right) or negative edge (right-to-left).
2254
2255 The list of paths is used later to generate a list of constraints.
2256 */
2257void QGraphicsAnchorLayoutPrivate::findPaths(Qt::Orientation orientation)
2258{
2259 QQueue<std::pair<AnchorVertex *, AnchorVertex *> > queue;
2260
2261 QSet<AnchorData *> visited;
2262
2263 AnchorVertex *root = layoutFirstVertex[orientation];
2264
2265 graphPaths[orientation].insert(root, GraphPath());
2266
2267 const auto adjacentVertices = graph[orientation].adjacentVertices(root);
2268 for (AnchorVertex *v : adjacentVertices)
2269 queue.enqueue(std::pair(root, v));
2270
2271 while(!queue.isEmpty()) {
2272 std::pair<AnchorVertex *, AnchorVertex *> pair = queue.dequeue();
2273 AnchorData *edge = graph[orientation].edgeData(pair.first, pair.second);
2274
2275 if (visited.contains(edge))
2276 continue;
2277
2278 visited.insert(edge);
2279 GraphPath current = graphPaths[orientation].value(pair.first);
2280
2281 if (edge->from == pair.first)
2282 current.positives.insert(edge);
2283 else
2284 current.negatives.insert(edge);
2285
2286 graphPaths[orientation].insert(pair.second, current);
2287
2288 const auto adjacentVertices = graph[orientation].adjacentVertices(pair.second);
2289 for (AnchorVertex *v : adjacentVertices)
2290 queue.enqueue(std::pair(pair.second, v));
2291 }
2292
2293 // We will walk through every reachable items (non-float) store them in a temporary set.
2294 // We them create a set of all items and subtract the non-floating items from the set in
2295 // order to get the floating items. The floating items is then stored in m_floatItems
2296 identifyFloatItems(visited, orientation);
2297}
2298
2299/*!
2300 \internal
2301
2302 Each vertex on the graph that has more than one path to it
2303 represents a contra int to the sizes of the items in these paths.
2304
2305 This method walks the list of paths to each vertex, generate
2306 the constraints and store them in a list so they can be used later
2307 by the Simplex solver.
2308*/
2309void QGraphicsAnchorLayoutPrivate::constraintsFromPaths(Qt::Orientation orientation)
2310{
2311 const auto vertices = graphPaths[orientation].uniqueKeys();
2312 for (AnchorVertex *vertex : vertices) {
2313 int valueCount = graphPaths[orientation].count(vertex);
2314 if (valueCount == 1)
2315 continue;
2316
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));
2321 }
2322 }
2323}
2324
2325/*!
2326 \internal
2327*/
2328void QGraphicsAnchorLayoutPrivate::updateAnchorSizes(Qt::Orientation orientation)
2329{
2330 Graph<AnchorVertex, AnchorData> &g = graph[orientation];
2331 const QList<std::pair<AnchorVertex *, AnchorVertex *>> &vertices = g.connections();
2332
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();
2336 }
2337}
2338
2339/*!
2340 \internal
2341
2342 Create LP constraints for each anchor based on its minimum and maximum
2343 sizes, as specified in its size hints
2344*/
2345QList<QSimplexConstraint *> QGraphicsAnchorLayoutPrivate::constraintsFromSizeHints(
2346 const QList<AnchorData *> &anchors)
2347{
2348 if (anchors.isEmpty())
2349 return QList<QSimplexConstraint *>();
2350
2351 // Look for the layout edge. That can be either the first half in case the
2352 // layout is split in two, or the whole layout anchor.
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]);
2357 } else {
2358 layoutEdge = graph[orient].edgeData(layoutFirstVertex[orient], layoutLastVertex[orient]);
2359 }
2360
2361 // If maxSize is less then "infinite", that means there are other anchors
2362 // grouped together with this one. We can't ignore its maximum value so we
2363 // set back the variable to NULL to prevent the continue condition from being
2364 // satisfied in the loop below.
2365 const qreal expectedMax = layoutCentralVertex[orient] ? QWIDGETSIZE_MAX / 2 : QWIDGETSIZE_MAX;
2366 qreal actualMax;
2367 if (layoutEdge->from == layoutFirstVertex[orient]) {
2368 actualMax = layoutEdge->maxSize;
2369 } else {
2370 actualMax = -layoutEdge->minSize;
2371 }
2372 if (actualMax != expectedMax) {
2373 layoutEdge = nullptr;
2374 }
2375
2376 // For each variable, create constraints based on size hints
2377 QList<QSimplexConstraint *> anchorConstraints;
2378 bool unboundedProblem = true;
2379 for (int i = 0; i < anchors.size(); ++i) {
2380 AnchorData *ad = anchors.at(i);
2381
2382 // Anchors that have their size directly linked to another one don't need constraints
2383 // For exammple, the second half of an item has exactly the same size as the first half
2384 // thus constraining the latter is enough.
2385 if (ad->dependency == AnchorData::Slave)
2386 continue;
2387
2388 // To use negative variables inside simplex, we shift them so the minimum negative value is
2389 // mapped to zero before solving. To make sure that it works, we need to guarantee that the
2390 // variables are all inside a certain boundary.
2391 qreal boundedMin = qBound(-g_offset, ad->minSize, g_offset);
2392 qreal boundedMax = qBound(-g_offset, ad->maxSize, g_offset);
2393
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;
2401 } else {
2402 QSimplexConstraint *c = new QSimplexConstraint;
2403 c->variables.insert(ad, 1.0);
2404 c->constant = boundedMin;
2405 c->ratio = QSimplexConstraint::MoreOrEqual;
2406 anchorConstraints += c;
2407
2408 // We avoid adding restrictions to the layout internal anchors. That's
2409 // to prevent unnecessary fair distribution from happening due to this
2410 // artificial restriction.
2411 if (ad == layoutEdge)
2412 continue;
2413
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;
2420 }
2421 }
2422
2423 // If no upper boundary restriction was added, add one to avoid unbounded problem
2424 if (unboundedProblem) {
2425 QSimplexConstraint *c = new QSimplexConstraint;
2426 c->variables.insert(layoutEdge, 1.0);
2427 // The maximum size that the layout can take
2428 c->constant = g_offset;
2429 c->ratio = QSimplexConstraint::LessOrEqual;
2430 anchorConstraints += c;
2431 }
2432
2433 return anchorConstraints;
2434}
2435
2436/*!
2437 \internal
2438*/
2439QGraphicsAnchorLayoutPrivate::GraphParts
2440QGraphicsAnchorLayoutPrivate::getGraphParts(Qt::Orientation orientation)
2441{
2442 GraphParts result;
2443
2444 Q_ASSERT(layoutFirstVertex[orientation] && layoutLastVertex[orientation]);
2445
2446 AnchorData *edgeL1 = nullptr;
2447 AnchorData *edgeL2 = nullptr;
2448
2449 // The layout may have a single anchor between Left and Right or two half anchors
2450 // passing through the center
2451 if (layoutCentralVertex[orientation]) {
2452 edgeL1 = graph[orientation].edgeData(layoutFirstVertex[orientation], layoutCentralVertex[orientation]);
2453 edgeL2 = graph[orientation].edgeData(layoutCentralVertex[orientation], layoutLastVertex[orientation]);
2454 } else {
2455 edgeL1 = graph[orientation].edgeData(layoutFirstVertex[orientation], layoutLastVertex[orientation]);
2456 }
2457
2458 result.nonTrunkConstraints = constraints[orientation] + itemCenterConstraints[orientation];
2459
2460 QSet<QSimplexVariable *> trunkVariables;
2461
2462 trunkVariables += edgeL1;
2463 if (edgeL2)
2464 trunkVariables += edgeL2;
2465
2466 bool dirty;
2467 auto end = result.nonTrunkConstraints.end();
2468 do {
2469 dirty = false;
2470
2471 auto isMatch = [&result, &trunkVariables](QSimplexConstraint *c) -> bool {
2472 bool match = false;
2473
2474 // Check if this constraint have some overlap with current
2475 // trunk variables...
2476 for (QSimplexVariable *ad : std::as_const(trunkVariables)) {
2477 if (c->variables.contains(ad)) {
2478 match = true;
2479 break;
2480 }
2481 }
2482
2483 // If so, we add it to trunk, and erase it from the
2484 // remaining constraints.
2485 if (match) {
2486 result.trunkConstraints += c;
2487 for (auto jt = c->variables.cbegin(), end = c->variables.cend(); jt != end; ++jt)
2488 trunkVariables.insert(jt.key());
2489 return true;
2490 } else {
2491 // Note that we don't erase the constraint if it's not
2492 // a match, since in a next iteration of a do-while we
2493 // can pass on it again and it will be a match.
2494 //
2495 // For example: if trunk share a variable with
2496 // remainingConstraints[1] and it shares with
2497 // remainingConstraints[0], we need a second iteration
2498 // of the do-while loop to match both.
2499 return false;
2500 }
2501 };
2502 const auto newEnd = std::remove_if(result.nonTrunkConstraints.begin(), end, isMatch);
2503 dirty = newEnd != end;
2504 end = newEnd;
2505 } while (dirty);
2506
2507 result.nonTrunkConstraints.erase(end, result.nonTrunkConstraints.end());
2508
2509 return result;
2510}
2511
2512/*!
2513 \internal
2514
2515 Use all visited Anchors on findPaths() so we can identify non-float Items.
2516*/
2517void QGraphicsAnchorLayoutPrivate::identifyFloatItems(const QSet<AnchorData *> &visited, Qt::Orientation orientation)
2518{
2519 QSet<QGraphicsLayoutItem *> nonFloating;
2520
2521 for (const AnchorData *ad : visited)
2522 identifyNonFloatItems_helper(ad, &nonFloating);
2523
2524 QSet<QGraphicsLayoutItem *> floatItems;
2525 for (QGraphicsLayoutItem *item : std::as_const(items)) {
2526 if (!nonFloating.contains(item))
2527 floatItems.insert(item);
2528 }
2529 m_floatItems[orientation] = std::move(floatItems);
2530}
2531
2532
2533/*!
2534 \internal
2535
2536 Given an anchor, if it is an internal anchor and Normal we must mark it's item as non-float.
2537 If the anchor is Sequential or Parallel, we must iterate on its children recursively until we reach
2538 internal anchors (items).
2539*/
2540void QGraphicsAnchorLayoutPrivate::identifyNonFloatItems_helper(const AnchorData *ad, QSet<QGraphicsLayoutItem *> *nonFloatingItemsIdentifiedSoFar)
2541{
2542 Q_Q(QGraphicsAnchorLayout);
2543
2544 switch(ad->type) {
2545 case AnchorData::Normal:
2546 if (ad->item && ad->item != q)
2547 nonFloatingItemsIdentifiedSoFar->insert(ad->item);
2548 break;
2549 case AnchorData::Sequential:
2550 for (const AnchorData *d : static_cast<const SequentialAnchorData *>(ad)->m_edges)
2551 identifyNonFloatItems_helper(d, nonFloatingItemsIdentifiedSoFar);
2552 break;
2553 case AnchorData::Parallel:
2554 identifyNonFloatItems_helper(static_cast<const ParallelAnchorData *>(ad)->firstEdge, nonFloatingItemsIdentifiedSoFar);
2555 identifyNonFloatItems_helper(static_cast<const ParallelAnchorData *>(ad)->secondEdge, nonFloatingItemsIdentifiedSoFar);
2556 break;
2557 }
2558}
2559
2560/*!
2561 \internal
2562
2563 Use the current vertices distance to calculate and set the geometry of
2564 each item.
2565*/
2566void QGraphicsAnchorLayoutPrivate::setItemsGeometries(const QRectF &geom)
2567{
2568 Q_Q(QGraphicsAnchorLayout);
2569 AnchorVertex *firstH, *secondH, *firstV, *secondV;
2570
2571 qreal top;
2572 qreal left;
2573 qreal right;
2574
2575 q->getContentsMargins(&left, &top, &right, nullptr);
2576 const Qt::LayoutDirection visualDir = visualDirection();
2577 if (visualDir == Qt::RightToLeft)
2578 qSwap(left, right);
2579
2580 left += geom.left();
2581 top += geom.top();
2582 right = geom.right() - right;
2583
2584 for (QGraphicsLayoutItem *item : std::as_const(items)) {
2585 QRectF newGeom;
2586 QSizeF itemPreferredSize = item->effectiveSizeHint(Qt::PreferredSize);
2587 if (m_floatItems[Qt::Horizontal].contains(item)) {
2588 newGeom.setLeft(0);
2589 newGeom.setRight(itemPreferredSize.width());
2590 } else {
2591 firstH = internalVertex(item, Qt::AnchorLeft);
2592 secondH = internalVertex(item, Qt::AnchorRight);
2593
2594 if (visualDir == Qt::LeftToRight) {
2595 newGeom.setLeft(left + firstH->distance);
2596 newGeom.setRight(left + secondH->distance);
2597 } else {
2598 newGeom.setLeft(right - secondH->distance);
2599 newGeom.setRight(right - firstH->distance);
2600 }
2601 }
2602
2603 if (m_floatItems[Qt::Vertical].contains(item)) {
2604 newGeom.setTop(0);
2605 newGeom.setBottom(itemPreferredSize.height());
2606 } else {
2607 firstV = internalVertex(item, Qt::AnchorTop);
2608 secondV = internalVertex(item, Qt::AnchorBottom);
2609
2610 newGeom.setTop(top + firstV->distance);
2611 newGeom.setBottom(top + secondV->distance);
2612 }
2613
2614 item->setGeometry(newGeom);
2615 }
2616}
2617
2618/*!
2619 \internal
2620
2621 Calculate the position of each vertex based on the paths to each of
2622 them as well as the current edges sizes.
2623*/
2624void QGraphicsAnchorLayoutPrivate::calculateVertexPositions(Qt::Orientation orientation)
2625{
2626 QQueue<std::pair<AnchorVertex *, AnchorVertex *> > queue;
2627 QSet<AnchorVertex *> visited;
2628
2629 // Get root vertex
2630 AnchorVertex *root = layoutFirstVertex[orientation];
2631
2632 root->distance = 0;
2633 visited.insert(root);
2634
2635 // Add initial edges to the queue
2636 const auto adjacentVertices = graph[orientation].adjacentVertices(root);
2637 for (AnchorVertex *v : adjacentVertices)
2638 queue.enqueue(std::pair(root, v));
2639
2640 // Do initial calculation required by "interpolateEdge()"
2641 setupEdgesInterpolation(orientation);
2642
2643 // Traverse the graph and calculate vertex positions
2644 while (!queue.isEmpty()) {
2645 std::pair<AnchorVertex *, AnchorVertex *> pair = queue.dequeue();
2646 AnchorData *edge = graph[orientation].edgeData(pair.first, pair.second);
2647
2648 if (visited.contains(pair.second))
2649 continue;
2650
2651 visited.insert(pair.second);
2652 interpolateEdge(pair.first, edge);
2653
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)));
2658 }
2659 }
2660}
2661
2662/*!
2663 \internal
2664
2665 Calculate interpolation parameters based on current Layout Size.
2666 Must be called once before calling "interpolateEdgeSize()" for
2667 the edges.
2668*/
2669void QGraphicsAnchorLayoutPrivate::setupEdgesInterpolation(
2670 Qt::Orientation orientation)
2671{
2672 Q_Q(QGraphicsAnchorLayout);
2673
2674 qreal current;
2675 current = (orientation == Qt::Horizontal) ? q->contentsRect().width() : q->contentsRect().height();
2676
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]);
2684
2685 interpolationInterval[orientation] = result.first;
2686 interpolationProgress[orientation] = result.second;
2687}
2688
2689/*!
2690 \internal
2691
2692 Calculate the current Edge size based on the current Layout size and the
2693 size the edge is supposed to have when the layout is at its:
2694
2695 - minimum size,
2696 - preferred size,
2697 - maximum size.
2698
2699 These three key values are calculated in advance using linear
2700 programming (more expensive) or the simplification algorithm, then
2701 subsequential resizes of the parent layout require a simple
2702 interpolation.
2703*/
2704void QGraphicsAnchorLayoutPrivate::interpolateEdge(AnchorVertex *base, AnchorData *edge)
2705{
2706 const Qt::Orientation orientation = edge->isVertical ? Qt::Vertical : Qt::Horizontal;
2707 const std::pair<Interval, qreal> factor(interpolationInterval[orientation],
2708 interpolationProgress[orientation]);
2709
2710 qreal edgeDistance = interpolate(factor, edge->sizeAtMinimum, edge->sizeAtPreferred,
2711 edge->sizeAtPreferred, edge->sizeAtPreferred,
2712 edge->sizeAtMaximum);
2713
2714 Q_ASSERT(edge->from == base || edge->to == base);
2715
2716 // Calculate the distance for the vertex opposite to the base
2717 if (edge->from == base) {
2718 edge->to->distance = base->distance + edgeDistance;
2719 } else {
2720 edge->from->distance = base->distance - edgeDistance;
2721 }
2722}
2723
2724bool QGraphicsAnchorLayoutPrivate::solveMinMax(const QList<QSimplexConstraint *> &constraints,
2725 const GraphPath &path, qreal *min, qreal *max)
2726{
2727 QSimplex simplex;
2728 bool feasible = simplex.setConstraints(constraints);
2729 if (feasible) {
2730 // Obtain the objective constraint
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);
2735
2736 for (iter = path.negatives.constBegin(); iter != path.negatives.constEnd(); ++iter)
2737 objective.variables.insert(*iter, -1.0);
2738
2739 const qreal objectiveOffset = (path.positives.size() - path.negatives.size()) * g_offset;
2740 simplex.setObjective(&objective);
2741
2742 // Calculate minimum values
2743 *min = simplex.solveMin() - objectiveOffset;
2744
2745 // Save sizeAtMinimum results
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;
2750 }
2751
2752 // Calculate maximum values
2753 *max = simplex.solveMax() - objectiveOffset;
2754
2755 // Save sizeAtMaximum results
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;
2759 }
2760 }
2761 return feasible;
2762}
2763
2764enum slackType { Grower = -1, Shrinker = 1 };
2765static auto createSlack(QSimplexConstraint *sizeConstraint, qreal interval, slackType type)
2766{
2767 struct R {
2768 QConcreteSimplexVariable *slack;
2769 QSimplexConstraint *limit;
2770 };
2771
2772 auto slack = new QConcreteSimplexVariable;
2773 sizeConstraint->variables.insert(slack, type);
2774
2776 limit->variables.insert(slack, 1.0);
2777 limit->ratio = QSimplexConstraint::LessOrEqual;
2778 limit->constant = interval;
2779
2780 return R{slack, limit};
2781}
2782
2783bool QGraphicsAnchorLayoutPrivate::solvePreferred(const QList<QSimplexConstraint *> &constraints,
2784 const QList<AnchorData *> &variables)
2785{
2786 QList<QSimplexConstraint *> preferredConstraints;
2787 QList<QConcreteSimplexVariable *> preferredVariables;
2788 QSimplexConstraint objective;
2789
2790 // Fill the objective coefficients for this variable. In the
2791 // end the objective function will be
2792 //
2793 // z = n * (A_shrinker_hard + A_grower_hard + B_shrinker_hard + B_grower_hard + ...) +
2794 // (A_shrinker_soft + A_grower_soft + B_shrinker_soft + B_grower_soft + ...)
2795 //
2796 // where n is the number of variables that have
2797 // slacks. Note that here we use the number of variables
2798 // as coefficient, this is to mark the "shrinker slack
2799 // variable" less likely to get value than the "grower
2800 // slack variable".
2801
2802 // This will fill the values for the structural constraints
2803 // and we now fill the values for the slack constraints (one per variable),
2804 // which have this form (the constant A_pref was set when creating the slacks):
2805 //
2806 // A + A_shrinker_hard + A_shrinker_soft - A_grower_hard - A_grower_soft = A_pref
2807 //
2808 for (int i = 0; i < variables.size(); ++i) {
2809 AnchorData *ad = variables.at(i);
2810
2811 // The layout original structure anchors are not relevant in preferred size calculation
2812 if (ad->isLayoutAnchor)
2813 continue;
2814
2815 // By default, all variables are equal to their preferred size. If they have room to
2816 // grow or shrink, such flexibility will be added by the additional variables below.
2817 QSimplexConstraint *sizeConstraint = new QSimplexConstraint;
2818 preferredConstraints += sizeConstraint;
2819 sizeConstraint->variables.insert(ad, 1.0);
2820 sizeConstraint->constant = ad->prefSize + g_offset;
2821
2822 // Can easily shrink
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;
2828
2829 // Add to objective with ratio == 1 (soft)
2830 objective.variables.insert(r.slack, 1.0);
2831 }
2832
2833 // Can easily grow
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;
2839
2840 // Add to objective with ratio == 1 (soft)
2841 objective.variables.insert(r.slack, 1.0);
2842 }
2843
2844 // Can shrink if really necessary
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;
2850
2851 // Add to objective with ratio == N (hard)
2852 objective.variables.insert(r.slack, variables.size());
2853 }
2854
2855 // Can grow if really necessary
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;
2861
2862 // Add to objective with ratio == N (hard)
2863 objective.variables.insert(r.slack, variables.size());
2864 }
2865 }
2866
2867 QSimplex *simplex = new QSimplex;
2868 bool feasible = simplex->setConstraints(constraints + preferredConstraints);
2869 if (feasible) {
2870 simplex->setObjective(&objective);
2871
2872 // Calculate minimum values
2873 simplex->solveMin();
2874
2875 // Save sizeAtPreferred results
2876 for (int i = 0; i < variables.size(); ++i) {
2877 AnchorData *ad = variables.at(i);
2878 ad->sizeAtPreferred = ad->result - g_offset;
2879 }
2880 }
2881
2882 // Make sure we delete the simplex solver -before- we delete the
2883 // constraints used by it.
2884 delete simplex;
2885
2886 // Delete constraints and variables we created.
2887 qDeleteAll(preferredConstraints);
2888 qDeleteAll(preferredVariables);
2889
2890 return feasible;
2891}
2892
2893/*!
2894 \internal
2895 Returns \c true if there are no arrangement that satisfies all constraints.
2896 Otherwise returns \c false.
2897
2898 \sa addAnchor()
2899*/
2900bool QGraphicsAnchorLayoutPrivate::hasConflicts() const
2901{
2902 QGraphicsAnchorLayoutPrivate *that = const_cast<QGraphicsAnchorLayoutPrivate*>(this);
2903 that->calculateGraphs();
2904
2905 bool floatConflict = !m_floatItems[Qt::Horizontal].isEmpty() || !m_floatItems[Qt::Vertical].isEmpty();
2906
2907 return graphHasConflicts[Qt::Horizontal] || graphHasConflicts[Qt::Vertical] || floatConflict;
2908}
2909
2910#ifdef QT_DEBUG
2911void QGraphicsAnchorLayoutPrivate::dumpGraph(const QString &name)
2912{
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()));
2916
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());
2921
2922 file.close();
2923}
2924#endif
2925
2926QT_END_NAMESPACE
void setSizePolicy(QSizePolicy::Policy policy)
QGraphicsAnchorLayoutPrivate * layoutPrivate
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)
const qreal g_offset
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)
void refreshSizeHints(const QLayoutStyleInfo *styleInfo=nullptr)