Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qquickpopuppositioner.cpp
Go to the documentation of this file.
1// Copyright (C) 2017 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#include "qquickoverlay_p.h"
8#include "qquickpopup_p_p.h"
9
10#include <QtCore/qloggingcategory.h>
11#include <QtQml/qqmlinfo.h>
12#include <QtQuick/private/qquickitem_p.h>
13
15
16Q_LOGGING_CATEGORY(lcPopupPositioner, "qt.quick.controls.popuppositioner")
17
18static const QQuickItemPrivate::ChangeTypes AncestorChangeTypes = QQuickItemPrivate::Geometry
19 | QQuickItemPrivate::Parent
20 | QQuickItemPrivate::Children;
21
22static const QQuickItemPrivate::ChangeTypes ItemChangeTypes = QQuickItemPrivate::Geometry
23 | QQuickItemPrivate::Parent;
24
26 : m_popup(popup)
27{
28}
29
31{
32 if (m_parentItem) {
35 }
36}
37
39{
40 return m_popup;
41}
42
44{
45 return m_parentItem;
46}
47
49{
50 if (m_parentItem == parent)
51 return;
52
53 if (m_parentItem) {
56 }
57
59
60 if (!parent)
61 return;
62
64 addAncestorListeners(parent->parentItem());
65 // Store the scale property so the end result of any transition that could effect the scale
66 // does not influence the top left of the final popup, so it doesn't appear to flip from one
67 // position to another as a result
68 m_popupScale = m_popup->popupItem()->scale();
69 if (m_popup->popupItem()->isVisible())
71}
72
74{
75 QQuickItem *popupItem = m_popup->popupItem();
76 if (!popupItem->isVisible())
77 return;
78
79 if (m_positioning) {
80 popupItem->polish();
81 return;
82 }
83
84 qCDebug(lcPopupPositioner) << "reposition called for" << m_popup;
85
86 const qreal w = popupItem->width() * m_popupScale;
87 const qreal h = popupItem->height() * m_popupScale;
88 const qreal iw = popupItem->implicitWidth() * m_popupScale;
89 const qreal ih = popupItem->implicitHeight() * m_popupScale;
90
91 bool widthAdjusted = false;
92 bool heightAdjusted = false;
94
95 const QQuickItem *centerInParent = p->anchors ? p->getAnchors()->centerIn() : nullptr;
96 const QQuickOverlay *centerInOverlay = qobject_cast<const QQuickOverlay*>(centerInParent);
97 QRectF rect(!centerInParent ? p->allowHorizontalMove ? p->x : popupItem->x() : 0,
98 !centerInParent ? p->allowVerticalMove ? p->y : popupItem->y() : 0,
99 !p->hasWidth && iw > 0 ? iw : w,
100 !p->hasHeight && ih > 0 ? ih : h);
101 if (m_parentItem) {
102 // m_parentItem is the parent that the popup should open in,
103 // and popupItem()->parentItem() is the overlay, so the mapToItem() calls below
104 // effectively map the rect to scene coordinates.
105 if (centerInParent) {
106 if (centerInParent != parentItem() && !centerInOverlay) {
107 qmlWarning(m_popup) << "Popup can only be centered within its immediate parent or Overlay.overlay";
108 return;
109 }
110
111 if (centerInOverlay) {
112 rect.moveCenter(QPointF(qRound(centerInOverlay->width() / 2.0), qRound(centerInOverlay->height() / 2.0)));
113 } else {
114 const QPointF parentItemCenter = QPointF(qRound(m_parentItem->width() / 2), qRound(m_parentItem->height() / 2));
115 rect.moveCenter(m_parentItem->mapToItem(popupItem->parentItem(), parentItemCenter));
116 }
117 } else {
118 rect.moveTopLeft(m_parentItem->mapToItem(popupItem->parentItem(), rect.topLeft()));
119 }
120
121 if (p->window) {
122 const QMarginsF margins = p->getMargins();
123 QRectF bounds(qMax<qreal>(0.0, margins.left()),
124 qMax<qreal>(0.0, margins.top()),
125 p->window->width() - qMax<qreal>(0.0, margins.left()) - qMax<qreal>(0.0, margins.right()),
126 p->window->height() - qMax<qreal>(0.0, margins.top()) - qMax<qreal>(0.0, margins.bottom()));
127 if (p->window->contentOrientation() == Qt::LandscapeOrientation || p->window->contentOrientation() == Qt::InvertedLandscapeOrientation)
128 bounds = bounds.transposed();
129
130 // if the popup doesn't fit horizontally inside the window, try flipping it around (left <-> right)
131 if (p->allowHorizontalFlip && (rect.left() < bounds.left() || rect.right() > bounds.right())) {
132 const QPointF newTopLeft(m_parentItem->width() - p->x - rect.width(), p->y);
133 const QRectF flipped(m_parentItem->mapToItem(popupItem->parentItem(), newTopLeft),
134 rect.size());
135 if (flipped.intersected(bounds).width() > rect.intersected(bounds).width())
136 rect.moveLeft(flipped.left());
137 }
138
139 // if the popup doesn't fit vertically inside the window, try flipping it around (above <-> below)
140 if (p->allowVerticalFlip && (rect.top() < bounds.top() || rect.bottom() > bounds.bottom())) {
141 const QPointF newTopLeft(p->x, m_parentItem->height() - p->y - rect.height());
142 const QRectF flipped(m_parentItem->mapToItem(popupItem->parentItem(), newTopLeft),
143 rect.size());
144 if (flipped.intersected(bounds).height() > rect.intersected(bounds).height())
145 rect.moveTop(flipped.top());
146 }
147
148 // push inside the margins if specified
149 if (p->allowVerticalMove) {
150 if (margins.top() >= 0 && rect.top() < bounds.top())
151 rect.moveTop(margins.top());
152 if (margins.bottom() >= 0 && rect.bottom() > bounds.bottom())
153 rect.moveBottom(bounds.bottom());
154 }
155 if (p->allowHorizontalMove) {
156 if (margins.left() >= 0 && rect.left() < bounds.left())
157 rect.moveLeft(margins.left());
158 if (margins.right() >= 0 && rect.right() > bounds.right())
159 rect.moveRight(bounds.right());
160 }
161
162 if (iw > 0 && (rect.left() < bounds.left() || rect.right() > bounds.right())) {
163 // neither the flipped or pushed geometry fits inside the window, choose
164 // whichever side (left vs. right) fits larger part of the popup
165 if (p->allowHorizontalMove && p->allowHorizontalFlip) {
166 if (rect.left() < bounds.left() && bounds.left() + rect.width() <= bounds.right())
167 rect.moveLeft(bounds.left());
168 else if (rect.right() > bounds.right() && bounds.right() - rect.width() >= bounds.left())
169 rect.moveRight(bounds.right());
170 }
171
172 // as a last resort, adjust the width to fit the window
173 if (p->allowHorizontalResize) {
174 if (rect.left() < bounds.left()) {
175 rect.setLeft(bounds.left());
176 widthAdjusted = true;
177 }
178 if (rect.right() > bounds.right()) {
179 rect.setRight(bounds.right());
180 widthAdjusted = true;
181 }
182 }
183 } else if (iw > 0 && rect.left() >= bounds.left() && rect.right() <= bounds.right()
184 && iw != w) {
185 // restore original width
186 rect.setWidth(iw);
187 widthAdjusted = true;
188 }
189
190 if (ih > 0 && (rect.top() < bounds.top() || rect.bottom() > bounds.bottom())) {
191 // neither the flipped or pushed geometry fits inside the window, choose
192 // whichever side (above vs. below) fits larger part of the popup
193 if (p->allowVerticalMove && p->allowVerticalFlip) {
194 if (rect.top() < bounds.top() && bounds.top() + rect.height() <= bounds.bottom())
195 rect.moveTop(bounds.top());
196 else if (rect.bottom() > bounds.bottom() && bounds.bottom() - rect.height() >= bounds.top())
197 rect.moveBottom(bounds.bottom());
198 }
199
200 // as a last resort, adjust the height to fit the window
201 if (p->allowVerticalResize) {
202 if (rect.top() < bounds.top()) {
203 rect.setTop(bounds.top());
204 heightAdjusted = true;
205 }
206 if (rect.bottom() > bounds.bottom()) {
207 rect.setBottom(bounds.bottom());
208 heightAdjusted = true;
209 }
210 }
211 } else if (ih > 0 && rect.top() >= bounds.top() && rect.bottom() <= bounds.bottom()
212 && ih != h) {
213 // restore original height
214 rect.setHeight(ih);
215 heightAdjusted = true;
216 }
217 }
218 }
219
220 m_positioning = true;
221
222 popupItem->setPosition(rect.topLeft());
223
224 // If the popup was assigned a parent, rect will be in scene coordinates,
225 // so we need to map its top left back to item coordinates.
226 // However, if centering within the overlay, the coordinates will be relative
227 // to the window, so we don't need to do anything.
228 const QPointF effectivePos = m_parentItem && !centerInOverlay ? m_parentItem->mapFromScene(rect.topLeft()) : rect.topLeft();
229 if (!qFuzzyCompare(p->effectiveX, effectivePos.x())) {
230 p->effectiveX = effectivePos.x();
232 }
233 if (!qFuzzyCompare(p->effectiveY, effectivePos.y())) {
234 p->effectiveY = effectivePos.y();
236 }
237
238 if (!p->hasWidth && widthAdjusted && rect.width() > 0) {
239 popupItem->setWidth(rect.width() / m_popupScale);
240 // The popup doesn't have an explicit width, so we should respect that by not
241 // making our call above an explicit assignment. If we don't, the popup won't
242 // resize after being repositioned in some cases.
243 QQuickItemPrivate::get(popupItem)->widthValidFlag = false;
244 }
245 if (!p->hasHeight && heightAdjusted && rect.height() > 0) {
246 popupItem->setHeight(rect.height() / m_popupScale);
247 QQuickItemPrivate::get(popupItem)->heightValidFlag = false;
248 }
249 m_positioning = false;
250
251 qCDebug(lcPopupPositioner) << "- new popupItem geometry:"
252 << popupItem->x() << popupItem->y() << popupItem->width() << popupItem->height();
253}
254
256{
257 if (m_parentItem && m_popup->popupItem()->isVisible())
259}
260
262{
264}
265
267{
268 if (child == m_parentItem || child->isAncestorOf(m_parentItem))
270}
271
273{
274 if (item == m_parentItem)
275 return;
276
277 QQuickItem *p = item;
278 while (p) {
280 p = p->parentItem();
281 }
282}
283
285{
286 if (item == m_parentItem)
287 return;
288
289 QQuickItem *p = item;
290 while (p) {
292 p = p->parentItem();
293 }
294}
295
\inmodule QtCore
Definition qmargins.h:274
constexpr qreal right() const noexcept
Returns the right margin.
Definition qmargins.h:370
constexpr qreal left() const noexcept
Returns the left margin.
Definition qmargins.h:364
constexpr qreal top() const noexcept
Returns the top margin.
Definition qmargins.h:367
constexpr qreal bottom() const noexcept
Returns the bottom margin.
Definition qmargins.h:373
\inmodule QtCore\reentrant
Definition qpoint.h:214
constexpr qreal x() const noexcept
Returns the x coordinate of this point.
Definition qpoint.h:333
constexpr qreal y() const noexcept
Returns the y coordinate of this point.
Definition qpoint.h:338
void removeItemChangeListener(QQuickItemChangeListener *, ChangeTypes types)
void updateOrAddItemChangeListener(QQuickItemChangeListener *listener, ChangeTypes types)
void addItemChangeListener(QQuickItemChangeListener *listener, ChangeTypes types)
static QQuickItemPrivate * get(QQuickItem *item)
The QQuickItem class provides the most basic of all visual items in \l {Qt Quick}.
Definition qquickitem.h:64
qreal implicitWidth
Definition qquickitem.h:113
Q_INVOKABLE QPointF mapToItem(const QQuickItem *item, const QPointF &point) const
Maps the given point in this item's coordinate system to the equivalent point within item's coordinat...
qreal x
\qmlproperty real QtQuick::Item::x \qmlproperty real QtQuick::Item::y \qmlproperty real QtQuick::Item...
Definition qquickitem.h:73
QPointF mapFromScene(const QPointF &point) const
Maps the given point in the scene's coordinate system to the equivalent point within this item's coor...
qreal y
Defines the item's y position relative to its parent.
Definition qquickitem.h:74
bool isVisible() const
void setHeight(qreal)
qreal width
This property holds the width of this item.
Definition qquickitem.h:76
QQuickItem * parentItem() const
qreal implicitHeight
Definition qquickitem.h:114
qreal height
This property holds the height of this item.
Definition qquickitem.h:77
void setPosition(const QPointF &)
void setWidth(qreal)
qreal scale
\qmlproperty real QtQuick::Item::scale This property holds the scale factor for this item.
Definition qquickitem.h:106
void polish()
Schedules a polish event for this item.
void itemChildRemoved(QQuickItem *, QQuickItem *child) override
void setParentItem(QQuickItem *parent)
QQuickItem * parentItem() const
void itemParentChanged(QQuickItem *, QQuickItem *parent) override
void itemGeometryChanged(QQuickItem *, QQuickGeometryChange, const QRectF &) override
void addAncestorListeners(QQuickItem *item)
QQuickPopup * popup() const
void removeAncestorListeners(QQuickItem *item)
static QQuickPopupPrivate * get(QQuickPopup *popup)
void xChanged()
void yChanged()
\inmodule QtCore\reentrant
Definition qrect.h:483
constexpr qreal bottom() const noexcept
Returns the y-coordinate of the rectangle's bottom edge.
Definition qrect.h:499
constexpr qreal height() const noexcept
Returns the height of the rectangle.
Definition qrect.h:718
constexpr qreal width() const noexcept
Returns the width of the rectangle.
Definition qrect.h:715
constexpr qreal left() const noexcept
Returns the x-coordinate of the rectangle's left edge.
Definition qrect.h:496
constexpr qreal top() const noexcept
Returns the y-coordinate of the rectangle's top edge.
Definition qrect.h:497
constexpr QRectF transposed() const noexcept
Definition qrect.h:756
QRectF intersected(const QRectF &other) const noexcept
Definition qrect.h:833
constexpr qreal right() const noexcept
Returns the x-coordinate of the rectangle's right edge.
Definition qrect.h:498
rect
[4]
Combined button and popup list for selecting options.
@ InvertedLandscapeOrientation
Definition qnamespace.h:275
@ LandscapeOrientation
Definition qnamespace.h:273
bool qFuzzyCompare(qfloat16 p1, qfloat16 p2) noexcept
Definition qfloat16.h:287
int qRound(qfloat16 d) noexcept
Definition qfloat16.h:281
#define Q_LOGGING_CATEGORY(name,...)
#define qCDebug(category,...)
GLfloat GLfloat GLfloat w
[0]
GLfloat GLfloat GLfloat GLfloat h
GLfloat GLfloat p
[1]
Q_QML_EXPORT QQmlInfo qmlWarning(const QObject *me)
static const QQuickItemPrivate::ChangeTypes ItemChangeTypes
static QT_BEGIN_NAMESPACE const QQuickItemPrivate::ChangeTypes AncestorChangeTypes
#define emit
double qreal
Definition qtypes.h:92
QGraphicsItem * item
QLayoutItem * child
[0]
IUIAutomationTreeWalker __RPC__deref_out_opt IUIAutomationElement ** parent