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