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 QtWebView 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 "qquickviewcontroller_p.h"
38#include "qwebview_p.h"
39
40#include <QtGui/QWindow>
41#include <QtQuick/QQuickWindow>
42
43#include <QtQuick/qquickrendercontrol.h>
44#include <QtQuick/private/qquickitem_p.h>
45#include <QtQuick/private/qquickitemchangelistener_p.h>
46
47QT_BEGIN_NAMESPACE
48
49static const QQuickItemPrivate::ChangeTypes changeMask = QQuickItemPrivate::Geometry
50 | QQuickItemPrivate::Children
51 | QQuickItemPrivate::Parent;
52
53class QQuickViewChangeListener : public QQuickItemChangeListener
54{
55public:
56 explicit QQuickViewChangeListener(QQuickViewController *item);
57 ~QQuickViewChangeListener();
58
59 inline void itemGeometryChanged(QQuickItem *,
60 QQuickGeometryChange,
61 const QRectF &) Q_DECL_OVERRIDE;
62 void itemChildRemoved(QQuickItem *item, QQuickItem *child) Q_DECL_OVERRIDE;
63 void itemParentChanged(QQuickItem *item, QQuickItem *parent) Q_DECL_OVERRIDE;
64
65private:
66 Q_DISABLE_COPY(QQuickViewChangeListener)
67 QQuickViewController *m_item;
68 void addAncestorListeners(QQuickItem *item, QQuickItemPrivate::ChangeTypes changeType);
69 void removeAncestorListeners(QQuickItem *item, QQuickItemPrivate::ChangeTypes changeType);
70 bool isAncestor(QQuickItem *item);
71};
72
73QQuickViewChangeListener::QQuickViewChangeListener(QQuickViewController *item)
74 : m_item(item)
75{
76 // Only listen for parent changes on the view controller item.
77 QQuickItemPrivate::get(item)->addItemChangeListener(listener: this, types: QQuickItemPrivate::Parent);
78 // Listen to all changes, that are relevant, on all ancestors.
79 addAncestorListeners(item: item->parentItem(), changeType: changeMask);
80}
81
82QQuickViewChangeListener::~QQuickViewChangeListener()
83{
84 if (m_item == 0)
85 return;
86
87 QQuickItemPrivate::get(item: m_item)->removeItemChangeListener(this, types: QQuickItemPrivate::Parent);
88 removeAncestorListeners(item: m_item->parentItem(), changeType: changeMask);
89}
90
91void QQuickViewChangeListener::itemGeometryChanged(QQuickItem *, QQuickGeometryChange, const QRectF &)
92{
93 m_item->polish();
94}
95
96void QQuickViewChangeListener::itemChildRemoved(QQuickItem *item, QQuickItem *child)
97{
98 Q_UNUSED(item)
99 Q_ASSERT(item != m_item);
100
101 const bool remove = (child == m_item) || isAncestor(item: child);
102
103 // if the child isn't the view item or its ancestor, then we don't care.
104 if (!remove)
105 return;
106
107 // Remove any listener we attached to the child and its ancestors.
108 removeAncestorListeners(item, changeType: changeMask);
109}
110
111void QQuickViewChangeListener::itemParentChanged(QQuickItem *item, QQuickItem *newParent)
112{
113 removeAncestorListeners(item: item->parentItem(), changeType: changeMask);
114 // Adds this as a listener for newParent and its ancestors.
115 addAncestorListeners(item: newParent, changeType: changeMask);
116}
117
118void QQuickViewChangeListener::addAncestorListeners(QQuickItem *item,
119 QQuickItemPrivate::ChangeTypes changeType)
120{
121 QQuickItem *p = item;
122 while (p != 0) {
123 QQuickItemPrivate::get(item: p)->addItemChangeListener(listener: this, types: changeType);
124 p = p->parentItem();
125 }
126}
127
128void QQuickViewChangeListener::removeAncestorListeners(QQuickItem *item,
129 QQuickItemPrivate::ChangeTypes changeType)
130{
131 QQuickItem *p = item;
132 while (p != 0) {
133 QQuickItemPrivate::get(item: p)->removeItemChangeListener(this, types: changeType);
134 p = p->parentItem();
135 }
136}
137
138bool QQuickViewChangeListener::isAncestor(QQuickItem *item)
139{
140 Q_ASSERT(m_item != 0);
141
142 if (item == 0)
143 return false;
144
145 QQuickItem *p = m_item->parentItem();
146 while (p != 0) {
147 if (p == item)
148 return true;
149 p = p->parentItem();
150 }
151
152 return false;
153}
154
155///
156/// \brief QQuickViewController::QQuickViewController
157/// \param parent
158///
159
160QQuickViewController::QQuickViewController(QQuickItem *parent)
161 : QQuickItem(parent)
162 , m_view(0)
163 , m_changeListener(new QQuickViewChangeListener(this))
164{
165 connect(sender: this, signal: &QQuickViewController::windowChanged, receiver: this, slot: &QQuickViewController::onWindowChanged);
166 connect(sender: this, signal: &QQuickViewController::visibleChanged, receiver: this, slot: &QQuickViewController::onVisibleChanged);
167}
168
169QQuickViewController::~QQuickViewController()
170{
171}
172
173void QQuickViewController::componentComplete()
174{
175 QQuickItem::componentComplete();
176 m_view->init();
177 m_view->setVisibility(QWindow::Windowed);
178}
179
180void QQuickViewController::updatePolish()
181{
182 if (m_view == 0)
183 return;
184
185 QSize itemSize = QSize(width(), height());
186 if (!itemSize.isValid())
187 return;
188
189 QQuickWindow *w = window();
190 if (w == 0)
191 return;
192
193 // Find this item's geometry in the scene.
194 QRect itemGeometry = mapRectToScene(rect: QRect(QPoint(0, 0), itemSize)).toRect();
195 // Check if we should be clipped to our parent's shape
196 // Note: This is crude but it should give an acceptable result on all platforms.
197 QQuickItem *p = parentItem();
198 const bool clip = p != 0 ? p->clip() : false;
199 if (clip) {
200 const QSize &parentSize = QSize(p->width(), p->height());
201 const QRect &parentGeometry = p->mapRectToScene(rect: QRect(QPoint(0, 0), parentSize)).toRect();
202 itemGeometry &= parentGeometry;
203 itemSize = itemGeometry.size();
204 }
205
206 // Find the top left position of this item, in global coordinates.
207 const QPoint &tl = w->mapToGlobal(pos: itemGeometry.topLeft());
208 // Get the actual render window, in case we're rendering into a off-screen window.
209 QWindow *rw = QQuickRenderControl::renderWindowFor(win: w);
210
211 m_view->setGeometry(rw ? QRect(rw->mapFromGlobal(pos: tl), itemSize) : itemGeometry);
212 m_view->setVisible(isVisible());
213}
214
215void QQuickViewController::setView(QNativeViewController *view)
216{
217 Q_ASSERT(m_view == 0);
218 m_view = view;
219}
220
221void QQuickViewController::scheduleUpdatePolish()
222{
223 polish();
224}
225
226void QQuickViewController::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
227{
228 QQuickItem::geometryChanged(newGeometry, oldGeometry);
229 if (newGeometry.isValid())
230 polish();
231}
232
233void QQuickViewController::onWindowChanged(QQuickWindow* window)
234{
235 QQuickWindow *oldParent = qobject_cast<QQuickWindow *>(object: m_view->parentView());
236 if (oldParent)
237 oldParent->disconnect(receiver: this);
238
239 if (!window) {
240 m_view->setParentView(nullptr);
241 return;
242 }
243
244 // Check if there's an actual native window available.
245 QWindow *rw = QQuickRenderControl::renderWindowFor(win: window);
246
247 if (rw) {
248 connect(sender: rw, signal: &QWindow::widthChanged, receiver: this, slot: &QQuickViewController::scheduleUpdatePolish);
249 connect(sender: rw, signal: &QWindow::heightChanged, receiver: this, slot: &QQuickViewController::scheduleUpdatePolish);
250 connect(sender: rw, signal: &QWindow::xChanged, receiver: this, slot: &QQuickViewController::scheduleUpdatePolish);
251 connect(sender: rw, signal: &QWindow::yChanged, receiver: this, slot: &QQuickViewController::scheduleUpdatePolish);
252 connect(sender: rw, signal: &QWindow::visibleChanged, context: this, slot: [this](bool visible) { m_view->setVisible(visible); });
253 connect(sender: window, signal: &QQuickWindow::sceneGraphInitialized, receiver: this, slot: &QQuickViewController::scheduleUpdatePolish);
254 connect(sender: window, signal: &QQuickWindow::sceneGraphInvalidated, receiver: this, slot: &QQuickViewController::onSceneGraphInvalidated);
255 m_view->setParentView(rw);
256 } else {
257 connect(sender: window, signal: &QWindow::widthChanged, receiver: this, slot: &QQuickViewController::scheduleUpdatePolish);
258 connect(sender: window, signal: &QWindow::heightChanged, receiver: this, slot: &QQuickViewController::scheduleUpdatePolish);
259 connect(sender: window, signal: &QWindow::xChanged, receiver: this, slot: &QQuickViewController::scheduleUpdatePolish);
260 connect(sender: window, signal: &QWindow::yChanged, receiver: this, slot: &QQuickViewController::scheduleUpdatePolish);
261 connect(sender: window, signal: &QQuickWindow::sceneGraphInitialized, receiver: this, slot: &QQuickViewController::scheduleUpdatePolish);
262 connect(sender: window, signal: &QQuickWindow::sceneGraphInvalidated, receiver: this, slot: &QQuickViewController::onSceneGraphInvalidated);
263 connect(sender: window, signal: &QWindow::visibilityChanged, context: this, slot: [this](QWindow::Visibility visibility)
264 { m_view->setVisible(visibility != QWindow::Hidden); });
265 m_view->setVisible(window->visibility() != QWindow::Hidden);
266 m_view->setParentView(window);
267 }
268}
269
270void QQuickViewController::onVisibleChanged()
271{
272 m_view->setVisible(isVisible());
273}
274
275void QQuickViewController::onSceneGraphInvalidated()
276{
277 if (m_view == 0)
278 return;
279
280 m_view->setVisible(false);
281}
282
283QT_END_NAMESPACE
284

source code of qtwebview/src/webview/qquickviewcontroller.cpp