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 #ifndef QGRAPHICSANCHORLAYOUT_P_H
41 #define QGRAPHICSANCHORLAYOUT_P_H
42 
43 //
44 //  W A R N I N G
45 //  -------------
46 //
47 // This file is not part of the Qt API.  It exists purely as an
48 // implementation detail.  This header file may change from version to
49 // version without notice, or even be removed.
50 //
51 // We mean it.
52 //
53 
54 #include <QtWidgets/private/qtwidgetsglobal_p.h>
55 #include <QGraphicsWidget>
56 #include <private/qobject_p.h>
57 
58 #include "qgraphicslayout_p.h"
59 #include "qgraphicsanchorlayout.h"
60 #include "qgraph_p.h"
61 #include "qsimplex_p.h"
62 
63 QT_REQUIRE_CONFIG(graphicsview);
64 
65 QT_BEGIN_NAMESPACE
66 
67 /*
68   The public QGraphicsAnchorLayout interface represents an anchorage point
69   as a pair of a <QGraphicsLayoutItem *> and a <Qt::AnchorPoint>.
70 
71   Internally though, it has a graph of anchorage points (vertices) and
72   anchors (edges), represented by the AnchorVertex and AnchorData structs
73   respectively.
74 */
75 
76 namespace QtGraphicsAnchorLayout {
77 /*!
78   \internal
79 
80   Represents a vertex (anchorage point) in the internal graph
81 */
82 struct AnchorVertex
83 {
AnchorVertexAnchorVertex84     AnchorVertex(QGraphicsLayoutItem *item, Qt::AnchorPoint edge)
85         : m_item(item), m_edge(edge) {}
86 
AnchorVertexAnchorVertex87     AnchorVertex()
88         : m_item(nullptr), m_edge(Qt::AnchorPoint(0)) {}
89 
90     virtual ~AnchorVertex() = default;
91 
92 #ifdef QT_DEBUG
93     virtual inline QString toString() const;
94 #endif
95 
96     QGraphicsLayoutItem *m_item;
97     Qt::AnchorPoint m_edge;
98 
99     // Current distance from this vertex to the layout edge (Left or Top)
100     // Value is calculated from the current anchors sizes.
101     qreal distance;
102 };
103 
104 /*!
105   \internal
106 
107   Represents an edge (anchor) in the internal graph.
108 */
109 struct AnchorData : public QSimplexVariable {
110     enum Type {
111         Normal = 0,
112         Sequential,
113         Parallel
114     };
115 
116     enum Dependency {
117         Independent = 0,
118         Master,
119         Slave
120     };
121 
AnchorDataAnchorData122     AnchorData()
123         : QSimplexVariable(), from(nullptr), to(nullptr),
124           minSize(0), prefSize(0), maxSize(0),
125           minPrefSize(0), maxPrefSize(0),
126           sizeAtMinimum(0), sizeAtPreferred(0),
127           sizeAtMaximum(0), item(nullptr), graphicsAnchor(nullptr),
128           type(Normal), isLayoutAnchor(false),
129           isCenterAnchor(false), orientation(0),
130           dependency(Independent) {}
131     virtual ~AnchorData();
132 
updateChildrenSizesAnchorData133     virtual void updateChildrenSizes() {}
134     void refreshSizeHints(const QLayoutStyleInfo *styleInfo = nullptr);
135 
136 #ifdef QT_DEBUG
137     void dump(int indent = 2);
138     inline QString toString() const;
139     QString name;
140 #endif
141 
142     // Anchor is semantically directed
143     AnchorVertex *from;
144     AnchorVertex *to;
145 
146     // Nominal sizes
147     // These are the intrinsic size restrictions for a given item. They are
148     // used as input for the calculation of the actual sizes.
149     // These values are filled by the refreshSizeHints method, based on the
150     // anchor size policy, the size hints of the item it (possibly) represents
151     // and the layout spacing information.
152     qreal minSize;
153     qreal prefSize;
154     qreal maxSize;
155 
156     qreal minPrefSize;
157     qreal maxPrefSize;
158 
159     // Calculated sizes
160     // These attributes define which sizes should that anchor be in when the
161     // layout is at its minimum, preferred or maximum sizes. Values are
162     // calculated by the Simplex solver based on the current layout setup.
163     qreal sizeAtMinimum;
164     qreal sizeAtPreferred;
165     qreal sizeAtMaximum;
166 
167     // References to the classes that represent this anchor in the public world
168     // An anchor may represent a LayoutItem, it may also be acessible externally
169     // through a GraphicsAnchor "handler".
170     QGraphicsLayoutItem *item;
171     QGraphicsAnchor *graphicsAnchor;
172 
173     uint type : 2;            // either Normal, Sequential or Parallel
174     uint isLayoutAnchor : 1;  // if this anchor is an internal layout anchor
175     uint isCenterAnchor : 1;
176     uint orientation : 1;
177     uint dependency : 2;      // either Independent, Master or Slave
178 };
179 
180 #ifdef QT_DEBUG
toString()181 inline QString AnchorData::toString() const
182 {
183     return QString::fromLatin1("Anchor(%1)").arg(name);
184 }
185 #endif
186 
187 struct SequentialAnchorData : public AnchorData
188 {
SequentialAnchorDataSequentialAnchorData189     SequentialAnchorData(const QVector<AnchorVertex *> &vertices, const QVector<AnchorData *> &edges)
190         : AnchorData(), m_children(vertices), m_edges(edges)
191     {
192         type = AnchorData::Sequential;
193         orientation = m_edges.at(0)->orientation;
194 #ifdef QT_DEBUG
195         name = QString::fromLatin1("%1 -- %2").arg(vertices.first()->toString(), vertices.last()->toString());
196 #endif
197     }
198 
199     virtual void updateChildrenSizes() override;
200     void calculateSizeHints();
201 
202     QVector<AnchorVertex*> m_children;          // list of vertices in the sequence
203     QVector<AnchorData*> m_edges;               // keep the list of edges too.
204 };
205 
206 struct ParallelAnchorData : public AnchorData
207 {
ParallelAnchorDataParallelAnchorData208     ParallelAnchorData(AnchorData *first, AnchorData *second)
209         : AnchorData(), firstEdge(first), secondEdge(second)
210     {
211         type = AnchorData::Parallel;
212         orientation = first->orientation;
213 
214         // This assert whether the child anchors share their vertices
215         Q_ASSERT(((first->from == second->from) && (first->to == second->to)) ||
216                  ((first->from == second->to) && (first->to == second->from)));
217 
218         // Our convention will be that the parallel group anchor will have the same
219         // direction as the first anchor.
220         from = first->from;
221         to = first->to;
222 #ifdef QT_DEBUG
223         name = QString::fromLatin1("%1 | %2").arg(first->toString(), second->toString());
224 #endif
225     }
226 
227     virtual void updateChildrenSizes() override;
228     bool calculateSizeHints();
229 
secondForwardParallelAnchorData230     bool secondForward() const {
231         // We have the convention that the first children will define the direction of the
232         // pararell group. Note that we can't rely on 'this->from' or 'this->to'  because they
233         // might be changed by vertex simplification.
234         return firstEdge->from == secondEdge->from;
235     }
236 
237     AnchorData* firstEdge;
238     AnchorData* secondEdge;
239 
240     QList<QSimplexConstraint *> m_firstConstraints;
241     QList<QSimplexConstraint *> m_secondConstraints;
242 };
243 
244 struct AnchorVertexPair : public AnchorVertex {
AnchorVertexPairAnchorVertexPair245     AnchorVertexPair(AnchorVertex *v1, AnchorVertex *v2, AnchorData *data)
246         : AnchorVertex(), m_first(v1), m_second(v2), m_removedAnchor(data)
247     {
248     }
249 
250     AnchorVertex *m_first;
251     AnchorVertex *m_second;
252 
253     AnchorData *m_removedAnchor;
254     QList<AnchorData *> m_firstAnchors;
255     QList<AnchorData *> m_secondAnchors;
256 
257 #ifdef QT_DEBUG
toStringAnchorVertexPair258     inline QString toString() const override
259     {
260         return QString::fromLatin1("(%1, %2)").arg(m_first->toString(), m_second->toString());
261     }
262 #endif
263 };
264 
265 #ifdef QT_DEBUG
toString()266 inline QString AnchorVertex::toString() const
267 {
268     if (!m_item)
269         return QString::fromLatin1("NULL_%1").arg(quintptr(this));
270 
271     QString edge;
272     switch (m_edge) {
273     case Qt::AnchorLeft:
274         edge = QLatin1String("Left");
275         break;
276     case Qt::AnchorHorizontalCenter:
277         edge = QLatin1String("HorizontalCenter");
278         break;
279     case Qt::AnchorRight:
280         edge = QLatin1String("Right");
281         break;
282     case Qt::AnchorTop:
283         edge = QLatin1String("Top");
284         break;
285     case Qt::AnchorVerticalCenter:
286         edge = QLatin1String("VerticalCenter");
287         break;
288     case Qt::AnchorBottom:
289         edge = QLatin1String("Bottom");
290         break;
291     default:
292         edge = QLatin1String("None");
293         break;
294     }
295     QString itemName;
296     if (m_item->isLayout()) {
297         itemName = QLatin1String("layout");
298     } else {
299         if (QGraphicsItem *item = m_item->graphicsItem()) {
300             itemName = item->data(0).toString();
301         }
302     }
303     edge.insert(0, QLatin1String("%1_"));
304     return edge.arg(itemName);
305 }
306 #endif
307 
308 /*!
309   \internal
310 
311   Representation of a valid path for a given vertex in the graph.
312   In this struct, "positives" is the set of anchors that have been
313   traversed in the forward direction, while "negatives" is the set
314   with the ones walked backwards.
315 
316   This paths are compared against each other to produce LP Constraints,
317   the exact order in which the anchors were traversed is not relevant.
318 */
319 class GraphPath
320 {
321 public:
GraphPath()322     GraphPath() {}
323 
324     QSimplexConstraint *constraint(const GraphPath &path) const;
325 #ifdef QT_DEBUG
326     QString toString() const;
327 #endif
328     QSet<AnchorData *> positives;
329     QSet<AnchorData *> negatives;
330 };
331 } // namespace QtGraphicsAnchorLayout
332 using namespace QtGraphicsAnchorLayout;
333 
334 Q_DECLARE_TYPEINFO(GraphPath, Q_MOVABLE_TYPE);
335 
336 class QGraphicsAnchorLayoutPrivate;
337 /*!
338     \internal
339 */
340 class QGraphicsAnchorPrivate : public QObjectPrivate
341 {
342     Q_DECLARE_PUBLIC(QGraphicsAnchor)
343 
344 public:
345     explicit QGraphicsAnchorPrivate(int version = QObjectPrivateVersion);
346     ~QGraphicsAnchorPrivate();
347 
348     void setSpacing(qreal value);
349     void unsetSpacing();
350     qreal spacing() const;
351 
352     void setSizePolicy(QSizePolicy::Policy policy);
353 
get(QGraphicsAnchor * q)354     static QGraphicsAnchorPrivate *get(QGraphicsAnchor *q)
355     { return q->d_func(); }
356 
357     QGraphicsAnchorLayoutPrivate *layoutPrivate;
358     AnchorData *data;
359 
360     // Size information for user controlled anchor
361     QSizePolicy::Policy sizePolicy;
362     qreal preferredSize;
363 
364     uint hasSize : 1;         // if false, get size from style.
365 };
366 
367 
368 
369 
370 /*!
371   \internal
372 
373   QGraphicsAnchorLayout private methods and attributes.
374 */
375 class Q_AUTOTEST_EXPORT QGraphicsAnchorLayoutPrivate : public QGraphicsLayoutPrivate
376 {
377     Q_DECLARE_PUBLIC(QGraphicsAnchorLayout)
378 
379 public:
380     // When the layout geometry is different from its Minimum, Preferred
381     // or Maximum values, interpolation is used to calculate the geometries
382     // of the items.
383     //
384     // Interval represents which interpolation interval are we operating in.
385     enum Interval {
386         MinimumToMinPreferred = 0,
387         MinPreferredToPreferred,
388         PreferredToMaxPreferred,
389         MaxPreferredToMaximum
390     };
391 
392     // Several structures internal to the layout are duplicated to handle
393     // both Horizontal and Vertical restrictions.
394     //
395     // Orientation is used to reference the right structure in each context
396     enum Orientation {
397         Horizontal = 0,
398         Vertical,
399         NOrientations
400     };
401 
402     QGraphicsAnchorLayoutPrivate();
403 
get(QGraphicsAnchorLayout * q)404     static QGraphicsAnchorLayoutPrivate *get(QGraphicsAnchorLayout *q)
405     {
406         return q ? q->d_func() : nullptr;
407     }
408 
409     static Qt::AnchorPoint oppositeEdge(
410         Qt::AnchorPoint edge);
411 
412     static Orientation edgeOrientation(Qt::AnchorPoint edge);
413 
pickEdge(Qt::AnchorPoint edge,Orientation orientation)414     static Qt::AnchorPoint pickEdge(Qt::AnchorPoint edge, Orientation orientation)
415     {
416         if (orientation == Vertical && int(edge) <= 2)
417             return (Qt::AnchorPoint)(edge + 3);
418         else if (orientation == Horizontal && int(edge) >= 3) {
419             return (Qt::AnchorPoint)(edge - 3);
420         }
421         return edge;
422     }
423 
424     // Init methods
425     void createLayoutEdges();
426     void deleteLayoutEdges();
427     void createItemEdges(QGraphicsLayoutItem *item);
428     void createCenterAnchors(QGraphicsLayoutItem *item, Qt::AnchorPoint centerEdge);
429     void removeCenterAnchors(QGraphicsLayoutItem *item, Qt::AnchorPoint centerEdge, bool substitute = true);
430     void removeCenterConstraints(QGraphicsLayoutItem *item, Orientation orientation);
431 
acquireGraphicsAnchor(AnchorData * data)432     QGraphicsAnchor *acquireGraphicsAnchor(AnchorData *data)
433     {
434         Q_Q(QGraphicsAnchorLayout);
435         if (!data->graphicsAnchor) {
436             data->graphicsAnchor = new QGraphicsAnchor(q);
437             data->graphicsAnchor->d_func()->data = data;
438         }
439         return data->graphicsAnchor;
440     }
441 
442     // function used by the 4 API functions
443     QGraphicsAnchor *addAnchor(QGraphicsLayoutItem *firstItem,
444                             Qt::AnchorPoint firstEdge,
445                             QGraphicsLayoutItem *secondItem,
446                             Qt::AnchorPoint secondEdge,
447                             qreal *spacing = nullptr);
448 
449     // Helper for Anchor Manipulation methods
450     void addAnchor_helper(QGraphicsLayoutItem *firstItem,
451                    Qt::AnchorPoint firstEdge,
452                    QGraphicsLayoutItem *secondItem,
453                    Qt::AnchorPoint secondEdge,
454                    AnchorData *data);
455 
456     QGraphicsAnchor *getAnchor(QGraphicsLayoutItem *firstItem, Qt::AnchorPoint firstEdge,
457                                QGraphicsLayoutItem *secondItem, Qt::AnchorPoint secondEdge);
458 
459     void removeAnchor(AnchorVertex *firstVertex, AnchorVertex *secondVertex);
460     void removeAnchor_helper(AnchorVertex *v1, AnchorVertex *v2);
461 
462     void removeAnchors(QGraphicsLayoutItem *item);
463 
464     void removeVertex(QGraphicsLayoutItem *item, Qt::AnchorPoint edge);
465 
466     void correctEdgeDirection(QGraphicsLayoutItem *&firstItem,
467                               Qt::AnchorPoint &firstEdge,
468                               QGraphicsLayoutItem *&secondItem,
469                               Qt::AnchorPoint &secondEdge);
470 
471     QLayoutStyleInfo &styleInfo() const;
472 
473     AnchorData *addAnchorMaybeParallel(AnchorData *newAnchor, bool *feasible);
474 
475     // Activation
476     void calculateGraphs();
477     void calculateGraphs(Orientation orientation);
478 
479     // Simplification
480     bool simplifyGraph(Orientation orientation);
481     bool simplifyVertices(Orientation orientation);
482     bool simplifyGraphIteration(Orientation orientation, bool *feasible);
483 
484     bool replaceVertex(Orientation orientation, AnchorVertex *oldV,
485                        AnchorVertex *newV, const QList<AnchorData *> &edges);
486 
487 
488     void restoreSimplifiedGraph(Orientation orientation);
489     void restoreSimplifiedAnchor(AnchorData *edge);
490     void restoreSimplifiedConstraints(ParallelAnchorData *parallel);
491     void restoreVertices(Orientation orientation);
492 
493     bool calculateTrunk(Orientation orientation, const GraphPath &trunkPath,
494                         const QList<QSimplexConstraint *> &constraints,
495                         const QList<AnchorData *> &variables);
496     bool calculateNonTrunk(const QList<QSimplexConstraint *> &constraints,
497                            const QList<AnchorData *> &variables);
498 
499     // Support functions for calculateGraph()
500     void refreshAllSizeHints(Orientation orientation);
501     void findPaths(Orientation orientation);
502     void constraintsFromPaths(Orientation orientation);
503     void updateAnchorSizes(Orientation orientation);
504     QList<QSimplexConstraint *> constraintsFromSizeHints(const QList<AnchorData *> &anchors);
505     struct GraphParts {
506         QList<QSimplexConstraint *> trunkConstraints;
507         QList<QSimplexConstraint *> nonTrunkConstraints;
508     };
509     GraphParts getGraphParts(Orientation orientation);
510     void identifyFloatItems(const QSet<AnchorData *> &visited, Orientation orientation);
511     void identifyNonFloatItems_helper(const AnchorData *ad, QSet<QGraphicsLayoutItem *> *nonFloatingItemsIdentifiedSoFar);
512 
internalVertex(const QPair<QGraphicsLayoutItem *,Qt::AnchorPoint> & itemEdge)513     inline AnchorVertex *internalVertex(const QPair<QGraphicsLayoutItem*, Qt::AnchorPoint> &itemEdge) const
514     {
515         return m_vertexList.value(itemEdge).first;
516     }
517 
internalVertex(const QGraphicsLayoutItem * item,Qt::AnchorPoint edge)518     inline AnchorVertex *internalVertex(const QGraphicsLayoutItem *item, Qt::AnchorPoint edge) const
519     {
520         return internalVertex(qMakePair(const_cast<QGraphicsLayoutItem *>(item), edge));
521     }
522 
changeLayoutVertex(Orientation orientation,AnchorVertex * oldV,AnchorVertex * newV)523     inline void changeLayoutVertex(Orientation orientation, AnchorVertex *oldV, AnchorVertex *newV)
524     {
525         if (layoutFirstVertex[orientation] == oldV)
526             layoutFirstVertex[orientation] = newV;
527         else if (layoutCentralVertex[orientation] == oldV)
528             layoutCentralVertex[orientation] = newV;
529         else if (layoutLastVertex[orientation] == oldV)
530             layoutLastVertex[orientation] = newV;
531     }
532 
533 
534     AnchorVertex *addInternalVertex(QGraphicsLayoutItem *item, Qt::AnchorPoint edge);
535     void removeInternalVertex(QGraphicsLayoutItem *item, Qt::AnchorPoint edge);
536 
537     // Geometry interpolation methods
538     void setItemsGeometries(const QRectF &geom);
539 
540     void calculateVertexPositions(Orientation orientation);
541     void setupEdgesInterpolation(Orientation orientation);
542     void interpolateEdge(AnchorVertex *base, AnchorData *edge);
543 
544     // Linear Programming solver methods
545     bool solveMinMax(const QList<QSimplexConstraint *> &constraints,
546                      const GraphPath &path, qreal *min, qreal *max);
547     bool solvePreferred(const QList<QSimplexConstraint *> &constraints,
548                         const QList<AnchorData *> &variables);
549     bool hasConflicts() const;
550 
551 #ifdef QT_DEBUG
552     void dumpGraph(const QString &name = QString());
553 #endif
554 
555 
556     qreal spacings[NOrientations];
557     // Size hints from simplex engine
558     qreal sizeHints[2][3];
559 
560     // Items
561     QVector<QGraphicsLayoutItem *> items;
562 
563     // Mapping between high level anchorage points (Item, Edge) to low level
564     // ones (Graph Vertices)
565 
566     QHash<QPair<QGraphicsLayoutItem*, Qt::AnchorPoint>, QPair<AnchorVertex *, int> > m_vertexList;
567 
568     // Internal graph of anchorage points and anchors, for both orientations
569     Graph<AnchorVertex, AnchorData> graph[2];
570 
571     AnchorVertex *layoutFirstVertex[2];
572     AnchorVertex *layoutCentralVertex[2];
573     AnchorVertex *layoutLastVertex[2];
574 
575     // Combined anchors in order of creation
576     QList<AnchorVertexPair *> simplifiedVertices[2];
577     QList<AnchorData *> anchorsFromSimplifiedVertices[2];
578 
579     // Graph paths and constraints, for both orientations
580     QMultiHash<AnchorVertex *, GraphPath> graphPaths[2];
581     QList<QSimplexConstraint *> constraints[2];
582     QList<QSimplexConstraint *> itemCenterConstraints[2];
583 
584     // The interpolation interval and progress based on the current size
585     // as well as the key values (minimum, preferred and maximum)
586     Interval interpolationInterval[2];
587     qreal interpolationProgress[2];
588 
589     bool graphHasConflicts[2];
590     QSet<QGraphicsLayoutItem *> m_floatItems[2];
591 
592 #if defined(QT_DEBUG) || defined(QT_BUILD_INTERNAL)
593     bool lastCalculationUsedSimplex[2];
594 #endif
595 
596     uint calculateGraphCacheDirty : 1;
597     mutable uint styleInfoDirty : 1;
598     mutable QLayoutStyleInfo cachedStyleInfo;
599 
600     friend class QGraphicsAnchorPrivate;
601 };
602 
603 QT_END_NAMESPACE
604 
605 #endif
606