Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qgraphicsanchorlayout.cpp
Go to the documentation of this file.
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// Qt-Security score:significant reason:default
4
5/*!
6 \class QGraphicsAnchorLayout
7 \brief The QGraphicsAnchorLayout class provides a layout where one can anchor widgets
8 together in Graphics View.
9 \since 4.6
10 \ingroup appearance
11 \ingroup geomanagement
12 \ingroup graphicsview-api
13 \inmodule QtWidgets
14
15 The anchor layout allows developers to specify how widgets should be placed relative to
16 each other, and to the layout itself. The specification is made by adding anchors to the
17 layout by calling addAnchor(), addAnchors() or addCornerAnchors().
18
19 Existing anchors in the layout can be accessed with the anchor() function.
20 Items that are anchored are automatically added to the layout, and if items
21 are removed, all their anchors will be automatically removed.
22
23 \div {class="float-left"}
24 \inlineimage simpleanchorlayout-example.png
25 {Three widgets using anchor layout to align to the widget edges}
26 \caption Using an anchor layout to align simple colored widgets.
27 \enddiv
28
29 Anchors are always set up between edges of an item, where the "center" is also considered to
30 be an edge. Consider the following example:
31
32 \snippet graphicsview/simpleanchorlayout/main.cpp adding anchors
33
34 Here, the right edge of item \c a is anchored to the left edge of item \c b and the bottom
35 edge of item \c a is anchored to the top edge of item \c b, with the result that
36 item \c b will be placed diagonally to the right and below item \c b.
37
38 The addCornerAnchors() function provides a simpler way of anchoring the corners
39 of two widgets than the two individual calls to addAnchor() shown in the code
40 above. Here, we see how a widget can be anchored to the top-left corner of the enclosing
41 layout:
42
43 \snippet graphicsview/simpleanchorlayout/main.cpp adding a corner anchor
44
45 In cases where anchors are used to match the widths or heights of widgets, it is
46 convenient to use the addAnchors() function. As with the other functions for specifying
47 anchors, it can also be used to anchor a widget to a layout.
48
49 \section1 Size Hints and Size Policies in an Anchor Layout
50
51 QGraphicsAnchorLayout respects each item's size hints and size policies.
52 Note that there are some properties of QSizePolicy that are \l{Known issues}{not respected}.
53
54 \section1 Spacing within an Anchor Layout
55
56 The layout may distribute some space between the items. If the spacing has not been
57 explicitly specified, the actual amount of space will usually be 0.
58
59 However, if the first edge is the \e opposite of the second edge (e.g., the right edge
60 of the first widget is anchored to the left edge of the second widget), the size of the
61 anchor will be queried from the style through a pixel metric:
62 \l{QStyle::}{PM_LayoutHorizontalSpacing} for horizontal anchors and
63 \l{QStyle::}{PM_LayoutVerticalSpacing} for vertical anchors.
64
65 If the spacing is negative, the items will overlap to some extent.
66
67
68 \section1 Known Issues
69 There are some features that QGraphicsAnchorLayout currently does not support.
70 This might change in the future, so avoid using these features if you want to
71 avoid any future regressions in behaviour:
72 \list
73
74 \li Stretch factors are not respected.
75
76 \li QSizePolicy::ExpandFlag is not respected.
77
78 \li Height for width is not respected.
79
80 \endlist
81
82 \sa QGraphicsLinearLayout, QGraphicsGridLayout, QGraphicsLayout
83*/
84
85/*!
86 \class QGraphicsAnchor
87 \brief The QGraphicsAnchor class represents an anchor between two items in a
88 QGraphicsAnchorLayout.
89 \since 4.6
90 \ingroup appearance
91 \ingroup geomanagement
92 \ingroup graphicsview-api
93 \inmodule QtWidgets
94
95 The graphics anchor provides an API that enables you to query and manipulate the
96 properties an anchor has. When an anchor is added to the layout with
97 QGraphicsAnchorLayout::addAnchor(), a QGraphicsAnchor instance is returned where the properties
98 are initialized to their default values. The properties can then be further changed, and they
99 will be picked up the next time the layout is activated.
100
101 \sa QGraphicsAnchorLayout::anchor()
102
103*/
105
107
108QGraphicsAnchor::QGraphicsAnchor(QGraphicsAnchorLayout *parentLayout)
109 : QObject(*(new QGraphicsAnchorPrivate))
110{
111 Q_D(QGraphicsAnchor);
112 Q_ASSERT(parentLayout);
113 d->layoutPrivate = parentLayout->d_func();
114}
115
116/*!
117 Removes the QGraphicsAnchor object from the layout and destroys it.
118*/
119QGraphicsAnchor::~QGraphicsAnchor()
120{
121}
122
123/*!
124 \property QGraphicsAnchor::sizePolicy
125 \brief the size policy for the QGraphicsAnchor.
126
127 By setting the size policy on an anchor you can configure how the anchor can resize itself
128 from its preferred spacing. For instance, if the anchor has the size policy
129 QSizePolicy::Minimum, the spacing is the minimum size of the anchor. However, its size
130 can grow up to the anchors maximum size. If the default size policy is QSizePolicy::Fixed,
131 the anchor can neither grow or shrink, which means that the only size the anchor can have
132 is the spacing. QSizePolicy::Fixed is the default size policy.
133 QGraphicsAnchor always has a minimum spacing of 0 and a very large maximum spacing.
134
135 \sa QGraphicsAnchor::spacing
136*/
137
138void QGraphicsAnchor::setSizePolicy(QSizePolicy::Policy policy)
139{
140 Q_D(QGraphicsAnchor);
141 d->setSizePolicy(policy);
142}
143
144QSizePolicy::Policy QGraphicsAnchor::sizePolicy() const
145{
146 Q_D(const QGraphicsAnchor);
147 return d->sizePolicy;
148}
149
150/*!
151 \property QGraphicsAnchor::spacing
152 \brief the preferred space between items in the QGraphicsAnchorLayout.
153
154 Depending on the anchor type, the default spacing is either
155 0 or a value returned from the style.
156
157 \sa QGraphicsAnchorLayout::addAnchor()
158*/
159void QGraphicsAnchor::setSpacing(qreal spacing)
160{
161 Q_D(QGraphicsAnchor);
162 d->setSpacing(spacing);
163}
164
165qreal QGraphicsAnchor::spacing() const
166{
167 Q_D(const QGraphicsAnchor);
168 return d->spacing();
169}
170
171void QGraphicsAnchor::unsetSpacing()
172{
173 Q_D(QGraphicsAnchor);
174 d->unsetSpacing();
175}
176
177/*!
178 Constructs a QGraphicsAnchorLayout instance. \a parent is passed to
179 QGraphicsLayout's constructor.
180 */
181QGraphicsAnchorLayout::QGraphicsAnchorLayout(QGraphicsLayoutItem *parent)
182 : QGraphicsLayout(*new QGraphicsAnchorLayoutPrivate(), parent)
183{
184 Q_D(QGraphicsAnchorLayout);
185 d->createLayoutEdges();
186}
187
188/*!
189 Destroys the QGraphicsAnchorLayout object.
190*/
191QGraphicsAnchorLayout::~QGraphicsAnchorLayout()
192{
193 Q_D(QGraphicsAnchorLayout);
194
195 for (int i = count() - 1; i >= 0; --i) {
196 QGraphicsLayoutItem *item = d->items.at(i);
197 removeAt(i);
198 if (item) {
199 if (item->ownedByLayout())
200 delete item;
201 }
202 }
203
204 d->removeCenterConstraints(this, Qt::Horizontal);
205 d->removeCenterConstraints(this, Qt::Vertical);
206 d->deleteLayoutEdges();
207
208 Q_ASSERT(d->itemCenterConstraints[Qt::Horizontal].isEmpty());
209 Q_ASSERT(d->itemCenterConstraints[Qt::Vertical].isEmpty());
210 Q_ASSERT(d->items.isEmpty());
211 Q_ASSERT(d->m_vertexList.isEmpty());
212}
213
214/*!
215 Creates an anchor between the edge \a firstEdge of item \a firstItem and the edge \a secondEdge
216 of item \a secondItem. The spacing of the anchor is picked up from the style. Anchors
217 between a layout edge and an item edge will have a size of 0.
218 If there is already an anchor between the edges, the new anchor will replace the old one.
219
220 \a firstItem and \a secondItem are automatically added to the layout if they are not part
221 of the layout. This means that count() can increase by up to 2.
222
223 The spacing an anchor will get depends on the type of anchor. For instance, anchors from the
224 Right edge of one item to the Left edge of another (or vice versa) will use the default
225 horizontal spacing. The same behaviour applies to Bottom to Top anchors, (but they will use
226 the default vertical spacing). For all other anchor combinations, the spacing will be 0.
227 All anchoring functions will follow this rule.
228
229 The spacing can also be set manually by using QGraphicsAnchor::setSpacing() method.
230
231 Calling this function where \a firstItem or \a secondItem are ancestors of the layout have
232 undefined behaviour.
233
234 \sa addAnchors(), addCornerAnchors()
235 */
236QGraphicsAnchor *
237QGraphicsAnchorLayout::addAnchor(QGraphicsLayoutItem *firstItem, Qt::AnchorPoint firstEdge,
238 QGraphicsLayoutItem *secondItem, Qt::AnchorPoint secondEdge)
239{
240 Q_D(QGraphicsAnchorLayout);
241 QGraphicsAnchor *a = d->addAnchor(firstItem, firstEdge, secondItem, secondEdge);
242 invalidate();
243 return a;
244}
245
246/*!
247 Returns the anchor between the anchor points defined by \a firstItem and \a firstEdge and
248 \a secondItem and \a secondEdge. If there is no such anchor, the function will return 0.
249*/
250QGraphicsAnchor *
251QGraphicsAnchorLayout::anchor(QGraphicsLayoutItem *firstItem, Qt::AnchorPoint firstEdge,
252 QGraphicsLayoutItem *secondItem, Qt::AnchorPoint secondEdge)
253{
254 Q_D(QGraphicsAnchorLayout);
255 return d->getAnchor(firstItem, firstEdge, secondItem, secondEdge);
256}
257
258/*!
259 Creates two anchors between \a firstItem and \a secondItem specified by the corners,
260 \a firstCorner and \a secondCorner, where one is for the horizontal edge and another
261 one for the vertical edge.
262
263 This is a convenience function, since anchoring corners can be expressed as anchoring
264 two edges. For instance:
265
266 \snippet graphicsview/simpleanchorlayout/main.cpp adding a corner anchor in two steps
267
268 This can also be achieved with the following line of code:
269
270 \snippet graphicsview/simpleanchorlayout/main.cpp adding a corner anchor
271
272 If there is already an anchor between the edge pairs, it will be replaced by the anchors that
273 this function specifies.
274
275 \a firstItem and \a secondItem are automatically added to the layout if they are not part of the
276 layout. This means that count() can increase by up to 2.
277
278 \sa addAnchor(), addAnchors()
279*/
280void QGraphicsAnchorLayout::addCornerAnchors(QGraphicsLayoutItem *firstItem,
281 Qt::Corner firstCorner,
282 QGraphicsLayoutItem *secondItem,
283 Qt::Corner secondCorner)
284{
285 Q_D(QGraphicsAnchorLayout);
286
287 // Horizontal anchor
288 Qt::AnchorPoint firstEdge = (firstCorner & 1 ? Qt::AnchorRight: Qt::AnchorLeft);
289 Qt::AnchorPoint secondEdge = (secondCorner & 1 ? Qt::AnchorRight: Qt::AnchorLeft);
290 if (d->addAnchor(firstItem, firstEdge, secondItem, secondEdge)) {
291 // Vertical anchor
292 firstEdge = (firstCorner & 2 ? Qt::AnchorBottom: Qt::AnchorTop);
293 secondEdge = (secondCorner & 2 ? Qt::AnchorBottom: Qt::AnchorTop);
294 d->addAnchor(firstItem, firstEdge, secondItem, secondEdge);
295
296 invalidate();
297 }
298}
299
300/*!
301 Anchors two or four edges of \a firstItem with the corresponding
302 edges of \a secondItem, so that \a firstItem has the same size as
303 \a secondItem in the dimensions specified by \a orientations.
304
305 For example, the following example anchors the left and right edges of two items
306 to match their widths:
307
308 \snippet graphicsview/simpleanchorlayout/main.cpp adding anchors to match sizes in two steps
309
310 This can also be achieved using the following line of code:
311
312 \snippet graphicsview/simpleanchorlayout/main.cpp adding anchors to match sizes
313
314 \sa addAnchor(), addCornerAnchors()
315*/
316void QGraphicsAnchorLayout::addAnchors(QGraphicsLayoutItem *firstItem,
317 QGraphicsLayoutItem *secondItem,
318 Qt::Orientations orientations)
319{
320 bool ok = true;
321 if (orientations & Qt::Horizontal) {
322 // Currently, if the first is ok, then the rest of the calls should be ok
323 ok = addAnchor(secondItem, Qt::AnchorLeft, firstItem, Qt::AnchorLeft) != nullptr;
324 if (ok)
325 addAnchor(firstItem, Qt::AnchorRight, secondItem, Qt::AnchorRight);
326 }
327 if (orientations & Qt::Vertical && ok) {
328 addAnchor(secondItem, Qt::AnchorTop, firstItem, Qt::AnchorTop);
329 addAnchor(firstItem, Qt::AnchorBottom, secondItem, Qt::AnchorBottom);
330 }
331}
332
333/*!
334 Sets the default horizontal spacing for the anchor layout to \a spacing.
335
336 \sa horizontalSpacing(), setVerticalSpacing(), setSpacing()
337*/
338void QGraphicsAnchorLayout::setHorizontalSpacing(qreal spacing)
339{
340 Q_D(QGraphicsAnchorLayout);
341
342 d->spacings[Qt::Horizontal] = spacing;
343 invalidate();
344}
345
346/*!
347 Sets the default vertical spacing for the anchor layout to \a spacing.
348
349 \sa verticalSpacing(), setHorizontalSpacing(), setSpacing()
350*/
351void QGraphicsAnchorLayout::setVerticalSpacing(qreal spacing)
352{
353 Q_D(QGraphicsAnchorLayout);
354
355 d->spacings[Qt::Vertical] = spacing;
356 invalidate();
357}
358
359/*!
360 Sets the default horizontal and the default vertical spacing for the anchor layout to \a spacing.
361
362 If an item is anchored with no spacing associated with the anchor, it will use the default
363 spacing.
364
365 QGraphicsAnchorLayout does not support negative spacings. Setting a negative value will unset the
366 previous spacing and make the layout use the spacing provided by the current widget style.
367
368 \sa setHorizontalSpacing(), setVerticalSpacing()
369*/
370void QGraphicsAnchorLayout::setSpacing(qreal spacing)
371{
372 Q_D(QGraphicsAnchorLayout);
373
374 d->spacings = {spacing, spacing};
375 invalidate();
376}
377
378/*!
379 Returns the default horizontal spacing for the anchor layout.
380
381 \sa verticalSpacing(), setHorizontalSpacing()
382*/
383qreal QGraphicsAnchorLayout::horizontalSpacing() const
384{
385 Q_D(const QGraphicsAnchorLayout);
386 return d->styleInfo().defaultSpacing(Qt::Horizontal);
387}
388
389/*!
390 Returns the default vertical spacing for the anchor layout.
391
392 \sa horizontalSpacing(), setVerticalSpacing()
393*/
394qreal QGraphicsAnchorLayout::verticalSpacing() const
395{
396 Q_D(const QGraphicsAnchorLayout);
397 return d->styleInfo().defaultSpacing(Qt::Vertical);
398}
399
400/*!
401 \reimp
402*/
403void QGraphicsAnchorLayout::setGeometry(const QRectF &geom)
404{
405 Q_D(QGraphicsAnchorLayout);
406
407 QGraphicsLayout::setGeometry(geom);
408 d->calculateVertexPositions(Qt::Horizontal);
409 d->calculateVertexPositions(Qt::Vertical);
410 d->setItemsGeometries(geom);
411}
412
413/*!
414 Removes the layout item at \a index without destroying it. Ownership of
415 the item is transferred to the caller.
416
417 Removing an item will also remove any of the anchors associated with it.
418
419 \sa itemAt(), count()
420*/
421void QGraphicsAnchorLayout::removeAt(int index)
422{
423 Q_D(QGraphicsAnchorLayout);
424 QGraphicsLayoutItem *item = d->items.value(index);
425
426 if (!item)
427 return;
428
429 // Removing an item affects both horizontal and vertical graphs
430 d->removeCenterConstraints(item, Qt::Horizontal);
431 d->removeCenterConstraints(item, Qt::Vertical);
432 d->removeAnchors(item);
433 d->items.remove(index);
434
435 item->setParentLayoutItem(nullptr);
436 invalidate();
437}
438
439/*!
440 \reimp
441*/
442int QGraphicsAnchorLayout::count() const
443{
444 Q_D(const QGraphicsAnchorLayout);
445 return d->items.size();
446}
447
448/*!
449 \reimp
450*/
451QGraphicsLayoutItem *QGraphicsAnchorLayout::itemAt(int index) const
452{
453 Q_D(const QGraphicsAnchorLayout);
454 return d->items.value(index);
455}
456
457/*!
458 \reimp
459*/
460void QGraphicsAnchorLayout::invalidate()
461{
462 Q_D(QGraphicsAnchorLayout);
463 QGraphicsLayout::invalidate();
464 d->calculateGraphCacheDirty = true;
465 d->styleInfoDirty = true;
466}
467
468/*!
469 \reimp
470*/
471QSizeF QGraphicsAnchorLayout::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
472{
473 Q_UNUSED(constraint);
474 Q_D(const QGraphicsAnchorLayout);
475
476 // Some setup calculations are delayed until the information is
477 // actually needed, avoiding unnecessary recalculations when
478 // adding multiple anchors.
479
480 // sizeHint() / effectiveSizeHint() already have a cache
481 // mechanism, using invalidate() to force recalculation. However
482 // sizeHint() is called three times after invalidation (for max,
483 // min and pref), but we just need do our setup once.
484
485 const_cast<QGraphicsAnchorLayoutPrivate *>(d)->calculateGraphs();
486
487 // ### apply constraint!
488 QSizeF engineSizeHint{d->sizeHints[Qt::Horizontal][which],
489 d->sizeHints[Qt::Vertical][which]};
490
491 qreal left, top, right, bottom;
492 getContentsMargins(&left, &top, &right, &bottom);
493
494 return engineSizeHint + QSizeF(left + right, top + bottom);
495}
496
497QT_END_NAMESPACE
498
499#include "moc_qgraphicsanchorlayout.cpp"