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