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

source code of qtbase/src/widgets/graphicsview/qgraphicsanchorlayout_p.h