1 /****************************************************************************
2 **
3 ** Copyright (C) 2017 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL3$
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 http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free
28 ** Software Foundation and appearing in the file LICENSE.GPL included in
29 ** the packaging of this file. Please review the following information to
30 ** ensure the GNU General Public License version 2.0 requirements will be
31 ** met: http://www.gnu.org/licenses/gpl-2.0.html.
32 **
33 ** $QT_END_LICENSE$
34 **
35 ****************************************************************************/
36 
37 #include "qquickpopupitem_p_p.h"
38 #include "qquickapplicationwindow_p.h"
39 #include "qquickshortcutcontext_p_p.h"
40 #include "qquickpage_p_p.h"
41 #include "qquickcontentitem_p.h"
42 #include "qquickpopup_p_p.h"
43 #include "qquickdeferredexecute_p_p.h"
44 
45 #if QT_CONFIG(shortcut)
46 #  include <QtGui/private/qshortcutmap_p.h>
47 #endif
48 #include <QtGui/private/qguiapplication_p.h>
49 
50 #if QT_CONFIG(accessibility)
51 #include <QtQuick/private/qquickaccessibleattached_p.h>
52 #endif
53 
54 QT_BEGIN_NAMESPACE
55 
56 class QQuickPopupItemPrivate : public QQuickPagePrivate
57 {
58     Q_DECLARE_PUBLIC(QQuickPopupItem)
59 
60 public:
61     QQuickPopupItemPrivate(QQuickPopup *popup);
62 
63     void implicitWidthChanged() override;
64     void implicitHeightChanged() override;
65 
66     void resolveFont() override;
67     void resolvePalette() override;
68 
69     QQuickItem *getContentItem() override;
70 
71     void cancelContentItem() override;
72     void executeContentItem(bool complete = false) override;
73 
74     void cancelBackground() override;
75     void executeBackground(bool complete = false) override;
76 
77     int backId = 0;
78     int escapeId = 0;
79     QQuickPopup *popup = nullptr;
80 };
81 
QQuickPopupItemPrivate(QQuickPopup * popup)82 QQuickPopupItemPrivate::QQuickPopupItemPrivate(QQuickPopup *popup)
83     : popup(popup)
84 {
85     isTabFence = true;
86 }
87 
implicitWidthChanged()88 void QQuickPopupItemPrivate::implicitWidthChanged()
89 {
90     QQuickPagePrivate::implicitWidthChanged();
91     emit popup->implicitWidthChanged();
92 }
93 
implicitHeightChanged()94 void QQuickPopupItemPrivate::implicitHeightChanged()
95 {
96     QQuickPagePrivate::implicitHeightChanged();
97     emit popup->implicitHeightChanged();
98 }
99 
resolveFont()100 void QQuickPopupItemPrivate::resolveFont()
101 {
102     if (QQuickApplicationWindow *window = qobject_cast<QQuickApplicationWindow *>(popup->window()))
103         inheritFont(window->font());
104     else
105         inheritFont(QQuickTheme::font(QQuickTheme::System));
106 }
107 
resolvePalette()108 void QQuickPopupItemPrivate::resolvePalette()
109 {
110     if (QQuickApplicationWindow *window = qobject_cast<QQuickApplicationWindow *>(popup->window()))
111         inheritPalette(window->palette());
112     else
113         inheritPalette(QQuickTheme::palette(QQuickTheme::System));
114 }
115 
getContentItem()116 QQuickItem *QQuickPopupItemPrivate::getContentItem()
117 {
118     Q_Q(QQuickPopupItem);
119     if (QQuickItem *item = QQuickPagePrivate::getContentItem())
120         return item;
121 
122     return new QQuickContentItem(popup, q);
123 }
124 
contentItemName()125 static inline QString contentItemName() { return QStringLiteral("contentItem"); }
126 
cancelContentItem()127 void QQuickPopupItemPrivate::cancelContentItem()
128 {
129     quickCancelDeferred(popup, contentItemName());
130 }
131 
executeContentItem(bool complete)132 void QQuickPopupItemPrivate::executeContentItem(bool complete)
133 {
134     if (contentItem.wasExecuted())
135         return;
136 
137     if (!contentItem || complete)
138         quickBeginDeferred(popup, contentItemName(), contentItem);
139     if (complete)
140         quickCompleteDeferred(popup, contentItemName(), contentItem);
141 }
142 
backgroundName()143 static inline QString backgroundName() { return QStringLiteral("background"); }
144 
cancelBackground()145 void QQuickPopupItemPrivate::cancelBackground()
146 {
147     quickCancelDeferred(popup, backgroundName());
148 }
149 
executeBackground(bool complete)150 void QQuickPopupItemPrivate::executeBackground(bool complete)
151 {
152     if (background.wasExecuted())
153         return;
154 
155     if (!background || complete)
156         quickBeginDeferred(popup, backgroundName(), background);
157     if (complete)
158         quickCompleteDeferred(popup, backgroundName(), background);
159 }
160 
QQuickPopupItem(QQuickPopup * popup)161 QQuickPopupItem::QQuickPopupItem(QQuickPopup *popup)
162     : QQuickPage(*(new QQuickPopupItemPrivate(popup)), nullptr)
163 {
164     setParent(popup);
165     setFlag(ItemIsFocusScope);
166     setAcceptedMouseButtons(Qt::AllButtons);
167 #if QT_CONFIG(quicktemplates2_multitouch)
168     setAcceptTouchEvents(true);
169 #endif
170 #if QT_CONFIG(cursor)
171     setCursor(Qt::ArrowCursor);
172 #endif
173 
174 #if QT_CONFIG(quicktemplates2_hover)
175     // TODO: switch to QStyleHints::useHoverEffects in Qt 5.8
176     setHoverEnabled(true);
177     // setAcceptHoverEvents(QGuiApplication::styleHints()->useHoverEffects());
178     // connect(QGuiApplication::styleHints(), &QStyleHints::useHoverEffectsChanged, this, &QQuickItem::setAcceptHoverEvents);
179 #endif
180 }
181 
grabShortcut()182 void QQuickPopupItem::grabShortcut()
183 {
184 #if QT_CONFIG(shortcut)
185     Q_D(QQuickPopupItem);
186     QGuiApplicationPrivate *pApp = QGuiApplicationPrivate::instance();
187     if (!d->backId)
188         d->backId = pApp->shortcutMap.addShortcut(this, Qt::Key_Back, Qt::WindowShortcut, QQuickShortcutContext::matcher);
189     if (!d->escapeId)
190         d->escapeId = pApp->shortcutMap.addShortcut(this, Qt::Key_Escape, Qt::WindowShortcut, QQuickShortcutContext::matcher);
191 #endif
192 }
193 
ungrabShortcut()194 void QQuickPopupItem::ungrabShortcut()
195 {
196 #if QT_CONFIG(shortcut)
197     Q_D(QQuickPopupItem);
198     QGuiApplicationPrivate *pApp = QGuiApplicationPrivate::instance();
199     if (d->backId) {
200         pApp->shortcutMap.removeShortcut(d->backId, this);
201         d->backId = 0;
202     }
203     if (d->escapeId) {
204         pApp->shortcutMap.removeShortcut(d->escapeId, this);
205         d->escapeId = 0;
206     }
207 #endif
208 }
209 
updatePolish()210 void QQuickPopupItem::updatePolish()
211 {
212     Q_D(QQuickPopupItem);
213     return QQuickPopupPrivate::get(d->popup)->reposition();
214 }
215 
event(QEvent * event)216 bool QQuickPopupItem::event(QEvent *event)
217 {
218 #if QT_CONFIG(shortcut)
219     Q_D(QQuickPopupItem);
220     if (event->type() == QEvent::Shortcut) {
221         QShortcutEvent *se = static_cast<QShortcutEvent *>(event);
222         if (se->shortcutId() == d->escapeId || se->shortcutId() == d->backId) {
223             QQuickPopupPrivate *p = QQuickPopupPrivate::get(d->popup);
224             if (p->interactive) {
225                 p->closeOrReject();
226                 return true;
227             }
228         }
229     }
230 #endif
231     return QQuickItem::event(event);
232 }
233 
childMouseEventFilter(QQuickItem * child,QEvent * event)234 bool QQuickPopupItem::childMouseEventFilter(QQuickItem *child, QEvent *event)
235 {
236     Q_D(QQuickPopupItem);
237     return d->popup->childMouseEventFilter(child, event);
238 }
239 
focusInEvent(QFocusEvent * event)240 void QQuickPopupItem::focusInEvent(QFocusEvent *event)
241 {
242     Q_D(QQuickPopupItem);
243     d->popup->focusInEvent(event);
244 }
245 
focusOutEvent(QFocusEvent * event)246 void QQuickPopupItem::focusOutEvent(QFocusEvent *event)
247 {
248     Q_D(QQuickPopupItem);
249     d->popup->focusOutEvent(event);
250 }
251 
keyPressEvent(QKeyEvent * event)252 void QQuickPopupItem::keyPressEvent(QKeyEvent *event)
253 {
254     Q_D(QQuickPopupItem);
255     d->popup->keyPressEvent(event);
256 }
257 
keyReleaseEvent(QKeyEvent * event)258 void QQuickPopupItem::keyReleaseEvent(QKeyEvent *event)
259 {
260     Q_D(QQuickPopupItem);
261     d->popup->keyReleaseEvent(event);
262 }
263 
mousePressEvent(QMouseEvent * event)264 void QQuickPopupItem::mousePressEvent(QMouseEvent *event)
265 {
266     Q_D(QQuickPopupItem);
267     d->popup->mousePressEvent(event);
268 }
269 
mouseMoveEvent(QMouseEvent * event)270 void QQuickPopupItem::mouseMoveEvent(QMouseEvent *event)
271 {
272     Q_D(QQuickPopupItem);
273     d->popup->mouseMoveEvent(event);
274 }
275 
mouseReleaseEvent(QMouseEvent * event)276 void QQuickPopupItem::mouseReleaseEvent(QMouseEvent *event)
277 {
278     Q_D(QQuickPopupItem);
279     d->popup->mouseReleaseEvent(event);
280 }
281 
mouseDoubleClickEvent(QMouseEvent * event)282 void QQuickPopupItem::mouseDoubleClickEvent(QMouseEvent *event)
283 {
284     Q_D(QQuickPopupItem);
285     d->popup->mouseDoubleClickEvent(event);
286 }
287 
mouseUngrabEvent()288 void QQuickPopupItem::mouseUngrabEvent()
289 {
290     Q_D(QQuickPopupItem);
291     d->popup->mouseUngrabEvent();
292 }
293 
294 #if QT_CONFIG(quicktemplates2_multitouch)
touchEvent(QTouchEvent * event)295 void QQuickPopupItem::touchEvent(QTouchEvent *event)
296 {
297     Q_D(QQuickPopupItem);
298     d->popup->touchEvent(event);
299 }
300 
touchUngrabEvent()301 void QQuickPopupItem::touchUngrabEvent()
302 {
303     Q_D(QQuickPopupItem);
304     d->popup->touchUngrabEvent();
305 }
306 #endif
307 
308 #if QT_CONFIG(wheelevent)
wheelEvent(QWheelEvent * event)309 void QQuickPopupItem::wheelEvent(QWheelEvent *event)
310 {
311     Q_D(QQuickPopupItem);
312     d->popup->wheelEvent(event);
313 }
314 #endif
315 
contentItemChange(QQuickItem * newItem,QQuickItem * oldItem)316 void QQuickPopupItem::contentItemChange(QQuickItem *newItem, QQuickItem *oldItem)
317 {
318     Q_D(QQuickPopupItem);
319     QQuickPage::contentItemChange(newItem, oldItem);
320     d->popup->contentItemChange(newItem, oldItem);
321 }
322 
contentSizeChange(const QSizeF & newSize,const QSizeF & oldSize)323 void QQuickPopupItem::contentSizeChange(const QSizeF &newSize, const QSizeF &oldSize)
324 {
325     Q_D(QQuickPopupItem);
326     QQuickPage::contentSizeChange(newSize, oldSize);
327     d->popup->contentSizeChange(newSize, oldSize);
328 }
329 
fontChange(const QFont & newFont,const QFont & oldFont)330 void QQuickPopupItem::fontChange(const QFont &newFont, const QFont &oldFont)
331 {
332     Q_D(QQuickPopupItem);
333     QQuickPage::fontChange(newFont, oldFont);
334     d->popup->fontChange(newFont, oldFont);
335 }
336 
geometryChanged(const QRectF & newGeometry,const QRectF & oldGeometry)337 void QQuickPopupItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
338 {
339     Q_D(QQuickPopupItem);
340     QQuickPage::geometryChanged(newGeometry, oldGeometry);
341     d->popup->geometryChanged(newGeometry, oldGeometry);
342 }
343 
localeChange(const QLocale & newLocale,const QLocale & oldLocale)344 void QQuickPopupItem::localeChange(const QLocale &newLocale, const QLocale &oldLocale)
345 {
346     Q_D(QQuickPopupItem);
347     QQuickPage::localeChange(newLocale, oldLocale);
348     d->popup->localeChange(newLocale, oldLocale);
349 }
350 
mirrorChange()351 void QQuickPopupItem::mirrorChange()
352 {
353     Q_D(QQuickPopupItem);
354     emit d->popup->mirroredChanged();
355 }
356 
itemChange(ItemChange change,const ItemChangeData & data)357 void QQuickPopupItem::itemChange(ItemChange change, const ItemChangeData &data)
358 {
359     Q_D(QQuickPopupItem);
360     QQuickPage::itemChange(change, data);
361     d->popup->itemChange(change, data);
362 }
363 
paddingChange(const QMarginsF & newPadding,const QMarginsF & oldPadding)364 void QQuickPopupItem::paddingChange(const QMarginsF &newPadding, const QMarginsF &oldPadding)
365 {
366     Q_D(QQuickPopupItem);
367     QQuickPage::paddingChange(newPadding, oldPadding);
368     d->popup->paddingChange(newPadding, oldPadding);
369 }
370 
paletteChange(const QPalette & newPalette,const QPalette & oldPalette)371 void QQuickPopupItem::paletteChange(const QPalette &newPalette, const QPalette &oldPalette)
372 {
373     Q_D(QQuickPopupItem);
374     QQuickPage::paletteChange(newPalette, oldPalette);
375     d->popup->paletteChange(newPalette, oldPalette);
376 }
377 
enabledChange()378 void QQuickPopupItem::enabledChange()
379 {
380     Q_D(QQuickPopupItem);
381     // Just having QQuickPopup connect our QQuickItem::enabledChanged() signal
382     // to its enabledChanged() signal is enough for the enabled property to work,
383     // but we must also ensure that its paletteChanged() signal is emitted
384     // so that bindings to palette are re-evaluated, because QQuickControl::palette()
385     // returns a different palette depending on whether or not the control is enabled.
386     // To save a connection, we also emit enabledChanged here.
387     emit d->popup->enabledChanged();
388     emit d->popup->paletteChanged();
389 }
390 
defaultFont() const391 QFont QQuickPopupItem::defaultFont() const
392 {
393     Q_D(const QQuickPopupItem);
394     return d->popup->defaultFont();
395 }
396 
defaultPalette() const397 QPalette QQuickPopupItem::defaultPalette() const
398 {
399     Q_D(const QQuickPopupItem);
400     return d->popup->defaultPalette();
401 }
402 
403 #if QT_CONFIG(accessibility)
accessibleRole() const404 QAccessible::Role QQuickPopupItem::accessibleRole() const
405 {
406     Q_D(const QQuickPopupItem);
407     return d->popup->accessibleRole();
408 }
409 
accessibilityActiveChanged(bool active)410 void QQuickPopupItem::accessibilityActiveChanged(bool active)
411 {
412     Q_D(const QQuickPopupItem);
413     // Can't just use d->popup->accessibleName() here, because that refers to the accessible
414     // name of us, the popup item, which is not what we want.
415     const QQuickAccessibleAttached *popupAccessibleAttached = QQuickControlPrivate::accessibleAttached(d->popup);
416     const QString oldPopupName = popupAccessibleAttached ? popupAccessibleAttached->name() : QString();
417     const bool wasNameExplicitlySetOnPopup = popupAccessibleAttached && popupAccessibleAttached->wasNameExplicitlySet();
418 
419     QQuickPage::accessibilityActiveChanged(active);
420 
421     QQuickAccessibleAttached *accessibleAttached = QQuickControlPrivate::accessibleAttached(this);
422     const QString ourName = accessibleAttached ? accessibleAttached->name() : QString();
423     if (wasNameExplicitlySetOnPopup && accessibleAttached && ourName != oldPopupName) {
424         // The user set Accessible.name on the Popup. Since the Popup and its popup item
425         // have different accessible attached properties, the popup item doesn't know that
426         // a name was set on the Popup by the user, and that it should use that, rather than
427         // whatever QQuickPage sets. That's why we need to do it here.
428         // To avoid it being overridden by the call to accessibilityActiveChanged() below,
429         // we set it explicitly. It's safe to do this as the popup item is an internal implementation detail.
430         accessibleAttached->setName(oldPopupName);
431     }
432 
433     // This allows the different popup types to set a name on their popup item accordingly.
434     // For example: Dialog uses its title and ToolTip uses its text.
435     d->popup->accessibilityActiveChanged(active);
436 }
437 #endif
438 
439 QT_END_NAMESPACE
440