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

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

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