1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtWidgets module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
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 https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39
40 #include "qglobal.h"
41
42 #include "qgraphicslayout.h"
43 #include "qgraphicsproxywidget.h"
44 #include "private/qgraphicsproxywidget_p.h"
45 #include "private/qwidget_p.h"
46 #include "private/qapplication_p.h"
47
48 #include <QtCore/qdebug.h>
49 #include <QtGui/qevent.h>
50 #include <QtWidgets/qgraphicsscene.h>
51 #include <QtWidgets/qgraphicssceneevent.h>
52 #include <QtWidgets/qlayout.h>
53 #include <QtGui/qpainter.h>
54 #include <QtWidgets/qstyleoption.h>
55 #include <QtWidgets/qgraphicsview.h>
56 #if QT_CONFIG(lineedit)
57 #include <QtWidgets/qlineedit.h>
58 #endif
59 #if QT_CONFIG(textedit)
60 #include <QtWidgets/qtextedit.h>
61 #endif
62
63 QT_BEGIN_NAMESPACE
64
65 //#define GRAPHICSPROXYWIDGET_DEBUG
66
67 /*!
68 \class QGraphicsProxyWidget
69 \brief The QGraphicsProxyWidget class provides a proxy layer for embedding
70 a QWidget in a QGraphicsScene.
71 \since 4.4
72 \ingroup graphicsview-api
73 \inmodule QtWidgets
74
75 QGraphicsProxyWidget embeds QWidget-based widgets, for example, a
76 QPushButton, QFontComboBox, or even QFileDialog, into
77 QGraphicsScene. It forwards events between the two objects and
78 translates between QWidget's integer-based geometry and
79 QGraphicsWidget's qreal-based geometry. QGraphicsProxyWidget
80 supports all core features of QWidget, including tab focus,
81 keyboard input, Drag & Drop, and popups. You can also embed
82 complex widgets, e.g., widgets with subwidgets.
83
84 Example:
85
86 \snippet code/src_gui_graphicsview_qgraphicsproxywidget.cpp 0
87
88 QGraphicsProxyWidget takes care of automatically embedding popup children
89 of embedded widgets through creating a child proxy for each popup. This
90 means that when an embedded QComboBox shows its popup list, a new
91 QGraphicsProxyWidget is created automatically, embedding the popup, and
92 positioning it correctly. This only works if the popup is child of the
93 embedded widget (for example QToolButton::setMenu() requires the QMenu instance
94 to be child of the QToolButton).
95
96 \section1 Embedding a Widget with QGraphicsProxyWidget
97
98 There are two ways to embed a widget using QGraphicsProxyWidget. The most
99 common way is to pass a widget pointer to QGraphicsScene::addWidget()
100 together with any relevant \l Qt::WindowFlags. This function returns a
101 pointer to a QGraphicsProxyWidget. You can then choose to reparent or
102 position either the proxy, or the embedded widget itself.
103
104 For example, in the code snippet below, we embed a group box into the proxy:
105
106 \snippet code/src_gui_graphicsview_qgraphicsproxywidget.cpp 1
107
108 The image below is the output obtained with its contents margin and
109 contents rect labeled.
110
111 \image qgraphicsproxywidget-embed.png
112
113 Alternatively, you can start by creating a new QGraphicsProxyWidget item,
114 and then call setWidget() to embed a QWidget later. The widget() function
115 returns a pointer to the embedded widget. QGraphicsProxyWidget shares
116 ownership with QWidget, so if either of the two widgets are destroyed, the
117 other widget will be automatically destroyed as well.
118
119 \section1 Synchronizing Widget States
120
121 QGraphicsProxyWidget keeps its state in sync with the embedded widget. For
122 example, if the proxy is hidden or disabled, the embedded widget will be
123 hidden or disabled as well, and vice versa. When the widget is embedded by
124 calling addWidget(), QGraphicsProxyWidget copies the state from the widget
125 into the proxy, and after that, the two will stay synchronized where
126 possible. By default, when you embed a widget into a proxy, both the widget
127 and the proxy will be visible because a QGraphicsWidget is visible when
128 created (you do not have to call show()). If you explicitly hide the
129 embedded widget, the proxy will also become invisible.
130
131 Example:
132
133 \snippet code/src_gui_graphicsview_qgraphicsproxywidget.cpp 2
134
135 QGraphicsProxyWidget maintains symmetry for the following states:
136
137 \table
138 \header \li QWidget state \li QGraphicsProxyWidget state \li Notes
139 \row \li QWidget::enabled
140 \li QGraphicsProxyWidget::enabled
141 \li
142 \row \li QWidget::visible
143 \li QGraphicsProxyWidget::visible
144 \li The explicit state is also symmetric.
145 \row \li QWidget::geometry
146 \li QGraphicsProxyWidget::geometry
147 \li Geometry is only guaranteed to be symmetric while
148 the embedded widget is visible.
149 \row \li QWidget::layoutDirection
150 \li QGraphicsProxyWidget::layoutDirection
151 \li
152 \row \li QWidget::style
153 \li QGraphicsProxyWidget::style
154 \li
155 \row \li QWidget::palette
156 \li QGraphicsProxyWidget::palette
157 \li
158 \row \li QWidget::font
159 \li QGraphicsProxyWidget::font
160 \li
161 \row \li QWidget::cursor
162 \li QGraphicsProxyWidget::cursor
163 \li The embedded widget overrides the proxy widget
164 cursor. The proxy cursor changes depending on
165 which embedded subwidget is currently under the
166 mouse.
167 \row \li QWidget::sizeHint()
168 \li QGraphicsProxyWidget::sizeHint()
169 \li All size hint functionality from the embedded
170 widget is forwarded by the proxy.
171 \row \li QWidget::getContentsMargins()
172 \li QGraphicsProxyWidget::getContentsMargins()
173 \li Updated once by setWidget().
174 \row \li QWidget::windowTitle
175 \li QGraphicsProxyWidget::windowTitle
176 \li Updated once by setWidget().
177 \endtable
178
179 \note QGraphicsScene keeps the embedded widget in a special state that
180 prevents it from disturbing other widgets (both embedded and not embedded)
181 while the widget is embedded. In this state, the widget may differ slightly
182 in behavior from when it is not embedded.
183
184 \warning This class is provided for convenience when bridging
185 QWidgets and QGraphicsItems, it should not be used for
186 high-performance scenarios. In particular, embedding widgets into a scene
187 that is then displayed through a QGraphicsView that uses an OpenGL viewport
188 will not work for all combinations.
189
190 \sa QGraphicsScene::addWidget(), QGraphicsWidget
191 */
192
193 extern bool qt_sendSpontaneousEvent(QObject *, QEvent *);
194 Q_WIDGETS_EXPORT extern bool qt_tab_all_widgets();
195
196 /*!
197 \internal
198 */
QGraphicsProxyWidgetPrivate()199 QGraphicsProxyWidgetPrivate::QGraphicsProxyWidgetPrivate()
200 : QGraphicsWidgetPrivate(),
201 dragDropWidget(nullptr),
202 posChangeMode(NoMode),
203 sizeChangeMode(NoMode),
204 visibleChangeMode(NoMode),
205 enabledChangeMode(NoMode),
206 styleChangeMode(NoMode),
207 paletteChangeMode(NoMode),
208 tooltipChangeMode(NoMode),
209 focusFromWidgetToProxy(false),
210 proxyIsGivingFocus(false)
211 {
212 }
213
214 /*!
215 \internal
216 */
~QGraphicsProxyWidgetPrivate()217 QGraphicsProxyWidgetPrivate::~QGraphicsProxyWidgetPrivate()
218 {
219 }
220
221 /*!
222 \internal
223 */
init()224 void QGraphicsProxyWidgetPrivate::init()
225 {
226 Q_Q(QGraphicsProxyWidget);
227 q->setFocusPolicy(Qt::WheelFocus);
228 q->setAcceptDrops(true);
229 }
230
231 /*!
232 \internal
233 */
sendWidgetMouseEvent(QGraphicsSceneHoverEvent * event)234 void QGraphicsProxyWidgetPrivate::sendWidgetMouseEvent(QGraphicsSceneHoverEvent *event)
235 {
236 QGraphicsSceneMouseEvent mouseEvent(QEvent::GraphicsSceneMouseMove);
237 mouseEvent.setPos(event->pos());
238 mouseEvent.setScreenPos(event->screenPos());
239 mouseEvent.setButton(Qt::NoButton);
240 mouseEvent.setButtons({ });
241 mouseEvent.setModifiers(event->modifiers());
242 sendWidgetMouseEvent(&mouseEvent);
243 event->setAccepted(mouseEvent.isAccepted());
244 }
245
246 /*!
247 \internal
248 */
sendWidgetMouseEvent(QGraphicsSceneMouseEvent * event)249 void QGraphicsProxyWidgetPrivate::sendWidgetMouseEvent(QGraphicsSceneMouseEvent *event)
250 {
251 if (!event || !widget || !widget->isVisible())
252 return;
253 Q_Q(QGraphicsProxyWidget);
254
255 // Find widget position and receiver.
256 QPointF pos = event->pos();
257 QPointer<QWidget> alienWidget = widget->childAt(pos.toPoint());
258 QPointer<QWidget> receiver = alienWidget ? alienWidget : widget;
259
260 if (QWidgetPrivate::nearestGraphicsProxyWidget(receiver) != q)
261 return; //another proxywidget will handle the events
262
263 // Translate QGraphicsSceneMouse events to QMouseEvents.
264 QEvent::Type type = QEvent::None;
265 switch (event->type()) {
266 case QEvent::GraphicsSceneMousePress:
267 type = QEvent::MouseButtonPress;
268 if (!embeddedMouseGrabber)
269 embeddedMouseGrabber = receiver;
270 else
271 receiver = embeddedMouseGrabber;
272 break;
273 case QEvent::GraphicsSceneMouseRelease:
274 type = QEvent::MouseButtonRelease;
275 if (embeddedMouseGrabber)
276 receiver = embeddedMouseGrabber;
277 break;
278 case QEvent::GraphicsSceneMouseDoubleClick:
279 type = QEvent::MouseButtonDblClick;
280 if (!embeddedMouseGrabber)
281 embeddedMouseGrabber = receiver;
282 else
283 receiver = embeddedMouseGrabber;
284 break;
285 case QEvent::GraphicsSceneMouseMove:
286 type = QEvent::MouseMove;
287 if (embeddedMouseGrabber)
288 receiver = embeddedMouseGrabber;
289 break;
290 default:
291 Q_ASSERT_X(false, "QGraphicsProxyWidget", "internal error");
292 break;
293 }
294
295 if (!lastWidgetUnderMouse) {
296 QApplicationPrivate::dispatchEnterLeave(embeddedMouseGrabber ? embeddedMouseGrabber : receiver, nullptr, event->screenPos());
297 lastWidgetUnderMouse = receiver;
298 }
299
300 // Map event position from us to the receiver
301 pos = mapToReceiver(pos, receiver);
302
303 // Send mouse event.
304 QMouseEvent mouseEvent(type, pos, receiver->mapTo(receiver->topLevelWidget(), pos.toPoint()),
305 receiver->mapToGlobal(pos.toPoint()),
306 event->button(), event->buttons(), event->modifiers(), event->source());
307
308 QWidget *embeddedMouseGrabberPtr = (QWidget *)embeddedMouseGrabber;
309 QApplicationPrivate::sendMouseEvent(receiver, &mouseEvent, alienWidget, widget,
310 &embeddedMouseGrabberPtr, lastWidgetUnderMouse, event->spontaneous());
311 embeddedMouseGrabber = embeddedMouseGrabberPtr;
312
313 // Handle enter/leave events when last button is released from mouse
314 // grabber child widget.
315 if (embeddedMouseGrabber && type == QEvent::MouseButtonRelease && !event->buttons()) {
316 Q_Q(QGraphicsProxyWidget);
317 if (q->rect().contains(event->pos()) && q->acceptHoverEvents())
318 lastWidgetUnderMouse = alienWidget ? alienWidget : widget;
319 else // released on the frame our outside the item, or doesn't accept hover events.
320 lastWidgetUnderMouse = nullptr;
321
322 QApplicationPrivate::dispatchEnterLeave(lastWidgetUnderMouse, embeddedMouseGrabber, event->screenPos());
323 embeddedMouseGrabber = nullptr;
324
325 #ifndef QT_NO_CURSOR
326 // ### Restore the cursor, don't override it.
327 if (!lastWidgetUnderMouse)
328 q->unsetCursor();
329 #endif
330 }
331
332 event->setAccepted(mouseEvent.isAccepted());
333 }
334
sendWidgetKeyEvent(QKeyEvent * event)335 void QGraphicsProxyWidgetPrivate::sendWidgetKeyEvent(QKeyEvent *event)
336 {
337 Q_Q(QGraphicsProxyWidget);
338 if (!event || !widget || !widget->isVisible())
339 return;
340
341 QPointer<QWidget> receiver = widget->focusWidget();
342 if (!receiver)
343 receiver = widget;
344 Q_ASSERT(receiver);
345
346 do {
347 bool res = QCoreApplication::sendEvent(receiver, event);
348 if ((res && event->isAccepted()) || (q->isWindow() && receiver == widget))
349 break;
350 receiver = receiver->parentWidget();
351 } while (receiver);
352 }
353
354 /*!
355 \internal
356 */
removeSubFocusHelper(QWidget * widget,Qt::FocusReason reason)357 void QGraphicsProxyWidgetPrivate::removeSubFocusHelper(QWidget *widget, Qt::FocusReason reason)
358 {
359 QFocusEvent event(QEvent::FocusOut, reason);
360 QPointer<QWidget> widgetGuard = widget;
361 QCoreApplication::sendEvent(widget, &event);
362 if (widgetGuard && event.isAccepted())
363 QCoreApplication::sendEvent(widget->style(), &event);
364 }
365
366 /*!
367 \internal
368 Some of the logic is shared with QApplicationPrivate::focusNextPrevChild_helper
369 */
findFocusChild(QWidget * child,bool next) const370 QWidget *QGraphicsProxyWidgetPrivate::findFocusChild(QWidget *child, bool next) const
371 {
372 if (!widget)
373 return nullptr;
374
375 // Run around the focus chain until we find a widget that can take tab focus.
376 if (!child) {
377 child = next ? (QWidget *)widget : widget->d_func()->focus_prev;
378 } else {
379 child = next ? child->d_func()->focus_next : child->d_func()->focus_prev;
380 if ((next && child == widget) || (!next && child == widget->d_func()->focus_prev)) {
381 return nullptr;
382 }
383 }
384
385 if (!child)
386 return nullptr;
387
388 QWidget *oldChild = child;
389 uint focus_flag = qt_tab_all_widgets() ? Qt::TabFocus : Qt::StrongFocus;
390 do {
391 if (child->isEnabled()
392 && child->isVisibleTo(widget)
393 && ((child->focusPolicy() & focus_flag) == focus_flag)
394 && !(child->d_func()->extra && child->d_func()->extra->focus_proxy)) {
395 return child;
396 }
397 child = next ? child->d_func()->focus_next : child->d_func()->focus_prev;
398 } while (child != oldChild && !(next && child == widget) && !(!next && child == widget->d_func()->focus_prev));
399 return nullptr;
400 }
401
402 /*!
403 \internal
404 */
_q_removeWidgetSlot()405 void QGraphicsProxyWidgetPrivate::_q_removeWidgetSlot()
406 {
407 Q_Q(QGraphicsProxyWidget);
408 if (!widget.isNull()) {
409 if (const auto &extra = widget->d_func()->extra)
410 extra->proxyWidget = nullptr;
411 }
412 widget = nullptr;
413 delete q;
414 }
415
416 /*!
417 \internal
418 */
updateWidgetGeometryFromProxy()419 void QGraphicsProxyWidgetPrivate::updateWidgetGeometryFromProxy()
420 {
421 }
422
423 /*!
424 \internal
425 */
updateProxyGeometryFromWidget()426 void QGraphicsProxyWidgetPrivate::updateProxyGeometryFromWidget()
427 {
428 Q_Q(QGraphicsProxyWidget);
429 if (!widget)
430 return;
431
432 QRectF widgetGeometry = widget->geometry();
433 QWidget *parentWidget = widget->parentWidget();
434 if (widget->isWindow()) {
435 QGraphicsProxyWidget *proxyParent = nullptr;
436 if (parentWidget && (proxyParent = qobject_cast<QGraphicsProxyWidget *>(q->parentWidget()))) {
437 // Nested window proxy (e.g., combobox popup), map widget to the
438 // parent widget's global coordinates, and map that to the parent
439 // proxy's child coordinates.
440 widgetGeometry.moveTo(proxyParent->subWidgetRect(parentWidget).topLeft()
441 + parentWidget->mapFromGlobal(widget->pos()));
442 }
443 }
444
445 // Adjust to size hint if the widget has never been resized.
446 if (!widget->size().isValid())
447 widgetGeometry.setSize(widget->sizeHint());
448
449 // Assign new geometry.
450 posChangeMode = QGraphicsProxyWidgetPrivate::WidgetToProxyMode;
451 sizeChangeMode = QGraphicsProxyWidgetPrivate::WidgetToProxyMode;
452 q->setGeometry(widgetGeometry);
453 posChangeMode = QGraphicsProxyWidgetPrivate::NoMode;
454 sizeChangeMode = QGraphicsProxyWidgetPrivate::NoMode;
455 }
456
457 /*!
458 \internal
459 */
updateProxyInputMethodAcceptanceFromWidget()460 void QGraphicsProxyWidgetPrivate::updateProxyInputMethodAcceptanceFromWidget()
461 {
462 Q_Q(QGraphicsProxyWidget);
463 if (!widget)
464 return;
465
466 QWidget *focusWidget = widget->focusWidget();
467 if (!focusWidget)
468 focusWidget = widget;
469 q->setFlag(QGraphicsItem::ItemAcceptsInputMethod,
470 focusWidget->testAttribute(Qt::WA_InputMethodEnabled));
471 }
472
473 /*!
474 \internal
475
476 Embeds \a subWin as a subwindow of this proxy widget. \a subWin must be a top-level
477 widget and a descendant of the widget managed by this proxy. A separate subproxy
478 will be created as a child of this proxy widget to manage \a subWin.
479 */
embedSubWindow(QWidget * subWin)480 void QGraphicsProxyWidgetPrivate::embedSubWindow(QWidget *subWin)
481 {
482 const auto &extra = subWin->d_func()->extra;
483 if (!extra || !extra->proxyWidget) {
484 QGraphicsProxyWidget *subProxy = new QGraphicsProxyWidget(q_func(), subWin->windowFlags());
485 subProxy->d_func()->setWidget_helper(subWin, false);
486 }
487 }
488
489 /*!
490 \internal
491
492 Removes ("unembeds") \a subWin and deletes the proxy holder item. This can
493 happen when QWidget::setParent() reparents the embedded window out of
494 "embedded space".
495 */
unembedSubWindow(QWidget * subWin)496 void QGraphicsProxyWidgetPrivate::unembedSubWindow(QWidget *subWin)
497 {
498 foreach (QGraphicsItem *child, children) {
499 if (child->isWidget()) {
500 if (QGraphicsProxyWidget *proxy = qobject_cast<QGraphicsProxyWidget *>(static_cast<QGraphicsWidget *>(child))) {
501 if (proxy->widget() == subWin) {
502 proxy->setWidget(nullptr);
503 scene->removeItem(proxy);
504 delete proxy;
505 return;
506 }
507 }
508 }
509 }
510 }
511
isProxyWidget() const512 bool QGraphicsProxyWidgetPrivate::isProxyWidget() const
513 {
514 return true;
515 }
516
517 /*!
518 \internal
519 */
mapToReceiver(const QPointF & pos,const QWidget * receiver) const520 QPointF QGraphicsProxyWidgetPrivate::mapToReceiver(const QPointF &pos, const QWidget *receiver) const
521 {
522 QPointF p = pos;
523 // Map event position from us to the receiver, preserving its
524 // precision (don't use QWidget::mapFrom here).
525 while (receiver && receiver != widget) {
526 p -= QPointF(receiver->pos());
527 receiver = receiver->parentWidget();
528 }
529 return p;
530 }
531
532 /*!
533 Constructs a new QGraphicsProxy widget. \a parent and \a wFlags are passed
534 to QGraphicsItem's constructor.
535 */
QGraphicsProxyWidget(QGraphicsItem * parent,Qt::WindowFlags wFlags)536 QGraphicsProxyWidget::QGraphicsProxyWidget(QGraphicsItem *parent, Qt::WindowFlags wFlags)
537 : QGraphicsWidget(*new QGraphicsProxyWidgetPrivate, parent, wFlags)
538 {
539 Q_D(QGraphicsProxyWidget);
540 d->init();
541 }
542
543 /*!
544 Destroys the proxy widget and any embedded widget.
545 */
~QGraphicsProxyWidget()546 QGraphicsProxyWidget::~QGraphicsProxyWidget()
547 {
548 Q_D(QGraphicsProxyWidget);
549 if (d->widget) {
550 d->widget->removeEventFilter(this);
551 QObject::disconnect(d->widget, SIGNAL(destroyed()), this, SLOT(_q_removeWidgetSlot()));
552 delete d->widget;
553 }
554 }
555
556 /*!
557 Embeds \a widget into this proxy widget. The embedded widget must reside
558 exclusively either inside or outside of Graphics View. You cannot embed a
559 widget as long as it is is visible elsewhere in the UI, at the same time.
560
561 \a widget must be a top-level widget whose parent is \nullptr.
562
563 When the widget is embedded, its state (e.g., visible, enabled, geometry,
564 size hints) is copied into the proxy widget. If the embedded widget is
565 explicitly hidden or disabled, the proxy widget will become explicitly
566 hidden or disabled after embedding is complete. The class documentation
567 has a full overview over the shared state.
568
569 QGraphicsProxyWidget's window flags determine whether the widget, after
570 embedding, will be given window decorations or not.
571
572 After this function returns, QGraphicsProxyWidget will keep its state
573 synchronized with that of \a widget whenever possible.
574
575 If a widget is already embedded by this proxy when this function is called,
576 that widget will first be automatically unembedded. Passing \nullptr for
577 the \a widget argument will only unembed the widget, and the ownership of
578 the currently embedded widget will be passed on to the caller.
579 Every child widget that are embedded will also be embedded and their proxy
580 widget destroyed.
581
582 Note that widgets with the Qt::WA_PaintOnScreen widget attribute
583 set and widgets that wrap an external application or controller
584 cannot be embedded. Examples are QOpenGLWidget and QAxWidget.
585
586 \sa widget()
587 */
setWidget(QWidget * widget)588 void QGraphicsProxyWidget::setWidget(QWidget *widget)
589 {
590 Q_D(QGraphicsProxyWidget);
591 d->setWidget_helper(widget, true);
592 }
593
setWidget_helper(QWidget * newWidget,bool autoShow)594 void QGraphicsProxyWidgetPrivate::setWidget_helper(QWidget *newWidget, bool autoShow)
595 {
596 Q_Q(QGraphicsProxyWidget);
597 if (newWidget == widget)
598 return;
599 if (widget) {
600 QObject::disconnect(widget, SIGNAL(destroyed()), q, SLOT(_q_removeWidgetSlot()));
601 widget->removeEventFilter(q);
602 widget->setAttribute(Qt::WA_DontShowOnScreen, false);
603 widget->d_func()->extra->proxyWidget = nullptr;
604 resolveFont(inheritedFontResolveMask);
605 resolvePalette(inheritedPaletteResolveMask);
606 widget->update();
607
608 const auto childItems = q->childItems();
609 for (QGraphicsItem *child : childItems) {
610 if (child->d_ptr->isProxyWidget()) {
611 QGraphicsProxyWidget *childProxy = static_cast<QGraphicsProxyWidget *>(child);
612 QWidget *parent = childProxy->widget();
613 while (parent && parent->parentWidget()) {
614 if (parent == widget)
615 break;
616 parent = parent->parentWidget();
617 }
618 if (!childProxy->widget() || parent != widget)
619 continue;
620 childProxy->setWidget(nullptr);
621 delete childProxy;
622 }
623 }
624
625 widget = nullptr;
626 #ifndef QT_NO_CURSOR
627 q->unsetCursor();
628 #endif
629 q->setAcceptHoverEvents(false);
630 if (!newWidget)
631 q->update();
632 }
633 if (!newWidget)
634 return;
635 if (!newWidget->isWindow()) {
636 const auto &extra = newWidget->parentWidget()->d_func()->extra;
637 if (!extra || !extra->proxyWidget) {
638 qWarning("QGraphicsProxyWidget::setWidget: cannot embed widget %p "
639 "which is not a toplevel widget, and is not a child of an embedded widget", newWidget);
640 return;
641 }
642 }
643
644 // Register this proxy within the widget's private.
645 // ### This is a bit backdoorish
646 QWExtra *extra = newWidget->d_func()->extra.get();
647 if (!extra) {
648 newWidget->d_func()->createExtra();
649 extra = newWidget->d_func()->extra.get();
650 }
651 QGraphicsProxyWidget **proxyWidget = &extra->proxyWidget;
652 if (*proxyWidget) {
653 if (*proxyWidget != q) {
654 qWarning("QGraphicsProxyWidget::setWidget: cannot embed widget %p"
655 "; already embedded", newWidget);
656 }
657 return;
658 }
659 *proxyWidget = q;
660
661 newWidget->setAttribute(Qt::WA_DontShowOnScreen);
662 newWidget->ensurePolished();
663 // Do not wait for this widget to close before the app closes ###
664 // shouldn't this widget inherit the attribute?
665 newWidget->setAttribute(Qt::WA_QuitOnClose, false);
666 q->setAcceptHoverEvents(true);
667
668 if (newWidget->testAttribute(Qt::WA_NoSystemBackground))
669 q->setAttribute(Qt::WA_NoSystemBackground);
670 if (newWidget->testAttribute(Qt::WA_OpaquePaintEvent))
671 q->setAttribute(Qt::WA_OpaquePaintEvent);
672
673 widget = newWidget;
674
675 // Changes only go from the widget to the proxy.
676 enabledChangeMode = QGraphicsProxyWidgetPrivate::WidgetToProxyMode;
677 visibleChangeMode = QGraphicsProxyWidgetPrivate::WidgetToProxyMode;
678 posChangeMode = QGraphicsProxyWidgetPrivate::WidgetToProxyMode;
679 sizeChangeMode = QGraphicsProxyWidgetPrivate::WidgetToProxyMode;
680
681 if ((autoShow && !newWidget->testAttribute(Qt::WA_WState_ExplicitShowHide)) || !newWidget->testAttribute(Qt::WA_WState_Hidden)) {
682 newWidget->show();
683 }
684
685 // Copy the state from the widget onto the proxy.
686 #ifndef QT_NO_CURSOR
687 if (newWidget->testAttribute(Qt::WA_SetCursor))
688 q->setCursor(widget->cursor());
689 #endif
690 q->setEnabled(newWidget->isEnabled());
691 q->setVisible(newWidget->isVisible());
692 q->setLayoutDirection(newWidget->layoutDirection());
693 if (newWidget->testAttribute(Qt::WA_SetStyle))
694 q->setStyle(widget->style());
695
696 resolveFont(inheritedFontResolveMask);
697 resolvePalette(inheritedPaletteResolveMask);
698
699 if (!newWidget->testAttribute(Qt::WA_Resized))
700 newWidget->adjustSize();
701
702 q->setContentsMargins(newWidget->contentsMargins());
703 q->setWindowTitle(newWidget->windowTitle());
704
705 // size policies and constraints..
706 q->setSizePolicy(newWidget->sizePolicy());
707 QSize sz = newWidget->minimumSize();
708 q->setMinimumSize(sz.isNull() ? QSizeF() : QSizeF(sz));
709 sz = newWidget->maximumSize();
710 q->setMaximumSize(sz.isNull() ? QSizeF() : QSizeF(sz));
711
712 updateProxyGeometryFromWidget();
713
714 updateProxyInputMethodAcceptanceFromWidget();
715
716 // Hook up the event filter to keep the state up to date.
717 newWidget->installEventFilter(q);
718 QObject::connect(newWidget, SIGNAL(destroyed()), q, SLOT(_q_removeWidgetSlot()));
719
720 // Changes no longer go only from the widget to the proxy.
721 enabledChangeMode = QGraphicsProxyWidgetPrivate::NoMode;
722 visibleChangeMode = QGraphicsProxyWidgetPrivate::NoMode;
723 posChangeMode = QGraphicsProxyWidgetPrivate::NoMode;
724 sizeChangeMode = QGraphicsProxyWidgetPrivate::NoMode;
725 }
726
727 /*!
728 Returns a pointer to the embedded widget.
729
730 \sa setWidget()
731 */
widget() const732 QWidget *QGraphicsProxyWidget::widget() const
733 {
734 Q_D(const QGraphicsProxyWidget);
735 return d->widget;
736 }
737
738 /*!
739 Returns the rectangle for \a widget, which must be a descendant of
740 widget(), or widget() itself, in this proxy item's local coordinates.
741
742 If no widget is embedded, \a widget is \nullptr, or \a widget is not a
743 descendant of the embedded widget, this function returns an empty QRectF.
744
745 \sa widget()
746 */
subWidgetRect(const QWidget * widget) const747 QRectF QGraphicsProxyWidget::subWidgetRect(const QWidget *widget) const
748 {
749 Q_D(const QGraphicsProxyWidget);
750 if (!widget || !d->widget)
751 return QRectF();
752 if (d->widget == widget || d->widget->isAncestorOf(widget))
753 return QRectF(widget->mapTo(d->widget, QPoint(0, 0)), widget->size());
754 return QRectF();
755 }
756
757 /*!
758 \reimp
759 */
setGeometry(const QRectF & rect)760 void QGraphicsProxyWidget::setGeometry(const QRectF &rect)
761 {
762 Q_D(QGraphicsProxyWidget);
763 bool proxyResizesWidget = !d->posChangeMode && !d->sizeChangeMode;
764 if (proxyResizesWidget) {
765 d->posChangeMode = QGraphicsProxyWidgetPrivate::ProxyToWidgetMode;
766 d->sizeChangeMode = QGraphicsProxyWidgetPrivate::ProxyToWidgetMode;
767 }
768 QGraphicsWidget::setGeometry(rect);
769 if (proxyResizesWidget) {
770 d->posChangeMode = QGraphicsProxyWidgetPrivate::NoMode;
771 d->sizeChangeMode = QGraphicsProxyWidgetPrivate::NoMode;
772 }
773 }
774
775 /*!
776 \reimp
777 */
itemChange(GraphicsItemChange change,const QVariant & value)778 QVariant QGraphicsProxyWidget::itemChange(GraphicsItemChange change,
779 const QVariant &value)
780 {
781 Q_D(QGraphicsProxyWidget);
782
783 switch (change) {
784 case ItemPositionChange:
785 // The item's position is either changed directly on the proxy, in
786 // which case the position change should propagate to the widget,
787 // otherwise it happens as a side effect when filtering QEvent::Move.
788 if (!d->posChangeMode)
789 d->posChangeMode = QGraphicsProxyWidgetPrivate::ProxyToWidgetMode;
790 break;
791 case ItemPositionHasChanged:
792 // Move the internal widget if we're in widget-to-proxy
793 // mode. Otherwise the widget has already moved.
794 if (d->widget && d->posChangeMode != QGraphicsProxyWidgetPrivate::WidgetToProxyMode)
795 d->widget->move(value.toPoint());
796 if (d->posChangeMode == QGraphicsProxyWidgetPrivate::ProxyToWidgetMode)
797 d->posChangeMode = QGraphicsProxyWidgetPrivate::NoMode;
798 break;
799 case ItemVisibleChange:
800 if (!d->visibleChangeMode)
801 d->visibleChangeMode = QGraphicsProxyWidgetPrivate::ProxyToWidgetMode;
802 break;
803 case ItemVisibleHasChanged:
804 if (d->widget && d->visibleChangeMode != QGraphicsProxyWidgetPrivate::WidgetToProxyMode)
805 d->widget->setVisible(isVisible());
806 if (d->visibleChangeMode == QGraphicsProxyWidgetPrivate::ProxyToWidgetMode)
807 d->visibleChangeMode = QGraphicsProxyWidgetPrivate::NoMode;
808 break;
809 case ItemEnabledChange:
810 if (!d->enabledChangeMode)
811 d->enabledChangeMode = QGraphicsProxyWidgetPrivate::ProxyToWidgetMode;
812 break;
813 case ItemEnabledHasChanged:
814 if (d->widget && d->enabledChangeMode != QGraphicsProxyWidgetPrivate::WidgetToProxyMode)
815 d->widget->setEnabled(isEnabled());
816 if (d->enabledChangeMode == QGraphicsProxyWidgetPrivate::ProxyToWidgetMode)
817 d->enabledChangeMode = QGraphicsProxyWidgetPrivate::NoMode;
818 break;
819 default:
820 break;
821 }
822 return QGraphicsWidget::itemChange(change, value);
823 }
824
825 /*!
826 \reimp
827 */
event(QEvent * event)828 bool QGraphicsProxyWidget::event(QEvent *event)
829 {
830 Q_D(QGraphicsProxyWidget);
831 if (!d->widget)
832 return QGraphicsWidget::event(event);
833
834 switch (event->type()) {
835 case QEvent::StyleChange:
836 // Propagate style changes to the embedded widget.
837 if (!d->styleChangeMode) {
838 d->styleChangeMode = QGraphicsProxyWidgetPrivate::ProxyToWidgetMode;
839 d->widget->setStyle(style());
840 d->styleChangeMode = QGraphicsProxyWidgetPrivate::NoMode;
841 }
842 break;
843 case QEvent::FontChange: {
844 // Propagate to widget.
845 QWidgetPrivate *wd = d->widget->d_func();
846 int mask = d->font.resolve() | d->inheritedFontResolveMask;
847 wd->inheritedFontResolveMask = mask;
848 wd->resolveFont();
849 break;
850 }
851 case QEvent::PaletteChange: {
852 // Propagate to widget.
853 QWidgetPrivate *wd = d->widget->d_func();
854 int mask = d->palette.resolve() | d->inheritedPaletteResolveMask;
855 wd->inheritedPaletteResolveMask = mask;
856 wd->resolvePalette();
857 break;
858 }
859 case QEvent::InputMethod: {
860 inputMethodEvent(static_cast<QInputMethodEvent *>(event));
861 if (event->isAccepted())
862 return true;
863 return false;
864 }
865 case QEvent::ShortcutOverride: {
866 QWidget *focusWidget = d->widget->focusWidget();
867 while (focusWidget) {
868 QCoreApplication::sendEvent(focusWidget, event);
869 if (event->isAccepted())
870 return true;
871 focusWidget = focusWidget->parentWidget();
872 }
873 return false;
874 }
875 case QEvent::KeyPress: {
876 QKeyEvent *k = static_cast<QKeyEvent *>(event);
877 if (k->key() == Qt::Key_Tab || k->key() == Qt::Key_Backtab) {
878 if (!(k->modifiers() & (Qt::ControlModifier | Qt::AltModifier))) { //### Add MetaModifier?
879 QWidget *focusWidget = d->widget->focusWidget();
880 while (focusWidget) {
881 const bool res = QCoreApplication::sendEvent(focusWidget, event);
882 if ((res && event->isAccepted()) || (isWindow() && focusWidget == d->widget)) {
883 event->accept();
884 break;
885 }
886 focusWidget = focusWidget->parentWidget();
887 }
888 return true;
889 }
890 }
891 break;
892 }
893 #ifndef QT_NO_TOOLTIP
894 case QEvent::GraphicsSceneHelp: {
895 // Propagate the help event (for tooltip) to the widget under mouse
896 if (d->lastWidgetUnderMouse) {
897 QGraphicsSceneHelpEvent *he = static_cast<QGraphicsSceneHelpEvent *>(event);
898 QPoint pos = d->mapToReceiver(mapFromScene(he->scenePos()), d->lastWidgetUnderMouse).toPoint();
899 QHelpEvent e(QEvent::ToolTip, pos, he->screenPos());
900 QCoreApplication::sendEvent(d->lastWidgetUnderMouse, &e);
901 event->setAccepted(e.isAccepted());
902 return e.isAccepted();
903 }
904 break;
905 }
906 case QEvent::ToolTipChange: {
907 // Propagate tooltip change to the widget
908 if (!d->tooltipChangeMode) {
909 d->tooltipChangeMode = QGraphicsProxyWidgetPrivate::ProxyToWidgetMode;
910 d->widget->setToolTip(toolTip());
911 d->tooltipChangeMode = QGraphicsProxyWidgetPrivate::NoMode;
912 }
913 break;
914 }
915 #endif
916 case QEvent::TouchBegin:
917 case QEvent::TouchUpdate:
918 case QEvent::TouchEnd: {
919 if (event->spontaneous())
920 qt_sendSpontaneousEvent(d->widget, event);
921 else
922 QCoreApplication::sendEvent(d->widget, event);
923
924 if (event->isAccepted())
925 return true;
926
927 break;
928 }
929 default:
930 break;
931 }
932 return QGraphicsWidget::event(event);
933 }
934
935 /*!
936 \reimp
937 */
eventFilter(QObject * object,QEvent * event)938 bool QGraphicsProxyWidget::eventFilter(QObject *object, QEvent *event)
939 {
940 Q_D(QGraphicsProxyWidget);
941
942 if (object == d->widget) {
943 switch (event->type()) {
944 case QEvent::LayoutRequest:
945 updateGeometry();
946 break;
947 case QEvent::Resize:
948 // If the widget resizes itself, we resize the proxy too.
949 // Prevent feed-back by checking the geometry change mode.
950 if (!d->sizeChangeMode)
951 d->updateProxyGeometryFromWidget();
952 break;
953 case QEvent::Move:
954 // If the widget moves itself, we move the proxy too. Prevent
955 // feed-back by checking the geometry change mode.
956 if (!d->posChangeMode)
957 d->updateProxyGeometryFromWidget();
958 break;
959 case QEvent::Hide:
960 case QEvent::Show:
961 // If the widget toggles its visible state, the proxy will follow.
962 if (!d->visibleChangeMode) {
963 d->visibleChangeMode = QGraphicsProxyWidgetPrivate::WidgetToProxyMode;
964 setVisible(event->type() == QEvent::Show);
965 d->visibleChangeMode = QGraphicsProxyWidgetPrivate::NoMode;
966 }
967 break;
968 case QEvent::EnabledChange:
969 // If the widget toggles its enabled state, the proxy will follow.
970 if (!d->enabledChangeMode) {
971 d->enabledChangeMode = QGraphicsProxyWidgetPrivate::WidgetToProxyMode;
972 setEnabled(d->widget->isEnabled());
973 d->enabledChangeMode = QGraphicsProxyWidgetPrivate::NoMode;
974 }
975 break;
976 case QEvent::StyleChange:
977 // Propagate style changes to the proxy.
978 if (!d->styleChangeMode) {
979 d->styleChangeMode = QGraphicsProxyWidgetPrivate::WidgetToProxyMode;
980 setStyle(d->widget->style());
981 d->styleChangeMode = QGraphicsProxyWidgetPrivate::NoMode;
982 }
983 break;
984 #ifndef QT_NO_TOOLTIP
985 case QEvent::ToolTipChange:
986 // Propagate tooltip change to the proxy.
987 if (!d->tooltipChangeMode) {
988 d->tooltipChangeMode = QGraphicsProxyWidgetPrivate::WidgetToProxyMode;
989 setToolTip(d->widget->toolTip());
990 d->tooltipChangeMode = QGraphicsProxyWidgetPrivate::NoMode;
991 }
992 break;
993 #endif
994 default:
995 break;
996 }
997 }
998 return QGraphicsWidget::eventFilter(object, event);
999 }
1000
1001 /*!
1002 \reimp
1003 */
showEvent(QShowEvent * event)1004 void QGraphicsProxyWidget::showEvent(QShowEvent *event)
1005 {
1006 Q_UNUSED(event);
1007 }
1008
1009 /*!
1010 \reimp
1011 */
hideEvent(QHideEvent * event)1012 void QGraphicsProxyWidget::hideEvent(QHideEvent *event)
1013 {
1014 Q_UNUSED(event);
1015 }
1016
1017 #ifndef QT_NO_CONTEXTMENU
1018 /*!
1019 \reimp
1020 */
contextMenuEvent(QGraphicsSceneContextMenuEvent * event)1021 void QGraphicsProxyWidget::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
1022 {
1023 Q_D(QGraphicsProxyWidget);
1024 if (!event || !d->widget || !d->widget->isVisible() || !hasFocus())
1025 return;
1026
1027 // Find widget position and receiver.
1028 QPointF pos = event->pos();
1029 QPointer<QWidget> alienWidget = d->widget->childAt(pos.toPoint());
1030 QPointer<QWidget> receiver = alienWidget ? alienWidget : d->widget;
1031
1032 // Map event position from us to the receiver
1033 pos = d->mapToReceiver(pos, receiver);
1034
1035 QPoint globalPos = receiver->mapToGlobal(pos.toPoint());
1036 //If the receiver by-pass the proxy its popups
1037 //will be top level QWidgets therefore they need
1038 //the screen position. mapToGlobal expect the widget to
1039 //have proper coordinates in regards of the windowing system
1040 //but it's not true because the widget is embedded.
1041 if (bypassGraphicsProxyWidget(receiver))
1042 globalPos = event->screenPos();
1043
1044 // Send mouse event. ### Doesn't propagate the event.
1045 QContextMenuEvent contextMenuEvent(QContextMenuEvent::Reason(event->reason()),
1046 pos.toPoint(), globalPos, event->modifiers());
1047 QCoreApplication::sendEvent(receiver, &contextMenuEvent);
1048
1049 event->setAccepted(contextMenuEvent.isAccepted());
1050 }
1051 #endif // QT_NO_CONTEXTMENU
1052
1053 #if QT_CONFIG(draganddrop)
1054 /*!
1055 \reimp
1056 */
dragEnterEvent(QGraphicsSceneDragDropEvent * event)1057 void QGraphicsProxyWidget::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
1058 {
1059 #if !QT_CONFIG(draganddrop)
1060 Q_UNUSED(event);
1061 #else
1062 Q_D(QGraphicsProxyWidget);
1063 if (!d->widget)
1064 return;
1065
1066 QDragEnterEvent proxyDragEnter(event->pos().toPoint(), event->dropAction(), event->mimeData(), event->buttons(), event->modifiers());
1067 proxyDragEnter.setAccepted(event->isAccepted());
1068 QCoreApplication::sendEvent(d->widget, &proxyDragEnter);
1069 event->setAccepted(proxyDragEnter.isAccepted());
1070 if (proxyDragEnter.isAccepted()) // we discard answerRect
1071 event->setDropAction(proxyDragEnter.dropAction());
1072 #endif
1073 }
1074 /*!
1075 \reimp
1076 */
dragLeaveEvent(QGraphicsSceneDragDropEvent * event)1077 void QGraphicsProxyWidget::dragLeaveEvent(QGraphicsSceneDragDropEvent *event)
1078 {
1079 Q_UNUSED(event);
1080 #if QT_CONFIG(draganddrop)
1081 Q_D(QGraphicsProxyWidget);
1082 if (!d->widget || !d->dragDropWidget)
1083 return;
1084 QDragLeaveEvent proxyDragLeave;
1085 QCoreApplication::sendEvent(d->dragDropWidget, &proxyDragLeave);
1086 d->dragDropWidget = nullptr;
1087 #endif
1088 }
1089
1090 /*!
1091 \reimp
1092 */
dragMoveEvent(QGraphicsSceneDragDropEvent * event)1093 void QGraphicsProxyWidget::dragMoveEvent(QGraphicsSceneDragDropEvent *event)
1094 {
1095 #if !QT_CONFIG(draganddrop)
1096 Q_UNUSED(event);
1097 #else
1098 Q_D(QGraphicsProxyWidget);
1099 if (!d->widget)
1100 return;
1101 QPointF p = event->pos();
1102 event->ignore();
1103 QPointer<QWidget> subWidget = d->widget->childAt(p.toPoint());
1104 QPointer<QWidget> receiver = subWidget ? subWidget : d->widget;
1105 bool eventDelivered = false;
1106 for (; receiver; receiver = receiver->parentWidget()) {
1107 if (!receiver->isEnabled() || !receiver->acceptDrops())
1108 continue;
1109 // Map event position from us to the receiver
1110 QPoint receiverPos = d->mapToReceiver(p, receiver).toPoint();
1111 if (receiver != d->dragDropWidget) {
1112 // Try to enter before we leave
1113 QDragEnterEvent dragEnter(receiverPos, event->possibleActions(), event->mimeData(), event->buttons(), event->modifiers());
1114 dragEnter.setDropAction(event->proposedAction());
1115 QCoreApplication::sendEvent(receiver, &dragEnter);
1116 event->setAccepted(dragEnter.isAccepted());
1117 event->setDropAction(dragEnter.dropAction());
1118 if (!event->isAccepted()) {
1119 // propagate to the parent widget
1120 continue;
1121 }
1122
1123 d->lastDropAction = event->dropAction();
1124
1125 if (d->dragDropWidget) {
1126 QDragLeaveEvent dragLeave;
1127 QCoreApplication::sendEvent(d->dragDropWidget, &dragLeave);
1128 }
1129 d->dragDropWidget = receiver;
1130 }
1131
1132 QDragMoveEvent dragMove(receiverPos, event->possibleActions(), event->mimeData(), event->buttons(), event->modifiers());
1133 event->setDropAction(d->lastDropAction);
1134 QCoreApplication::sendEvent(receiver, &dragMove);
1135 event->setAccepted(dragMove.isAccepted());
1136 event->setDropAction(dragMove.dropAction());
1137 if (event->isAccepted())
1138 d->lastDropAction = event->dropAction();
1139 eventDelivered = true;
1140 break;
1141 }
1142
1143 if (!eventDelivered) {
1144 if (d->dragDropWidget) {
1145 // Leave the last drag drop item
1146 QDragLeaveEvent dragLeave;
1147 QCoreApplication::sendEvent(d->dragDropWidget, &dragLeave);
1148 d->dragDropWidget = nullptr;
1149 }
1150 // Propagate
1151 event->setDropAction(Qt::IgnoreAction);
1152 }
1153 #endif
1154 }
1155
1156 /*!
1157 \reimp
1158 */
dropEvent(QGraphicsSceneDragDropEvent * event)1159 void QGraphicsProxyWidget::dropEvent(QGraphicsSceneDragDropEvent *event)
1160 {
1161 #if !QT_CONFIG(draganddrop)
1162 Q_UNUSED(event);
1163 #else
1164 Q_D(QGraphicsProxyWidget);
1165 if (d->widget && d->dragDropWidget) {
1166 QPoint widgetPos = d->mapToReceiver(event->pos(), d->dragDropWidget).toPoint();
1167 QDropEvent dropEvent(widgetPos, event->possibleActions(), event->mimeData(), event->buttons(), event->modifiers());
1168 QCoreApplication::sendEvent(d->dragDropWidget, &dropEvent);
1169 event->setAccepted(dropEvent.isAccepted());
1170 d->dragDropWidget = nullptr;
1171 }
1172 #endif
1173 }
1174 #endif
1175
1176 /*!
1177 \reimp
1178 */
hoverEnterEvent(QGraphicsSceneHoverEvent * event)1179 void QGraphicsProxyWidget::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
1180 {
1181 Q_UNUSED(event);
1182 }
1183
1184 /*!
1185 \reimp
1186 */
hoverLeaveEvent(QGraphicsSceneHoverEvent * event)1187 void QGraphicsProxyWidget::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
1188 {
1189 Q_UNUSED(event);
1190 Q_D(QGraphicsProxyWidget);
1191 // If hoverMove was compressed away, make sure we update properly here.
1192 if (d->lastWidgetUnderMouse) {
1193 QApplicationPrivate::dispatchEnterLeave(nullptr, d->lastWidgetUnderMouse, event->screenPos());
1194 d->lastWidgetUnderMouse = nullptr;
1195 }
1196 }
1197
1198 /*!
1199 \reimp
1200 */
hoverMoveEvent(QGraphicsSceneHoverEvent * event)1201 void QGraphicsProxyWidget::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
1202 {
1203 Q_D(QGraphicsProxyWidget);
1204 #ifdef GRAPHICSPROXYWIDGET_DEBUG
1205 qDebug("QGraphicsProxyWidget::hoverMoveEvent");
1206 #endif
1207 // Ignore events on the window frame.
1208 if (!d->widget || !rect().contains(event->pos())) {
1209 if (d->lastWidgetUnderMouse) {
1210 QApplicationPrivate::dispatchEnterLeave(nullptr, d->lastWidgetUnderMouse, event->screenPos());
1211 d->lastWidgetUnderMouse = nullptr;
1212 }
1213 return;
1214 }
1215
1216 d->embeddedMouseGrabber = nullptr;
1217 d->sendWidgetMouseEvent(event);
1218 }
1219
1220 /*!
1221 \reimp
1222 */
grabMouseEvent(QEvent * event)1223 void QGraphicsProxyWidget::grabMouseEvent(QEvent *event)
1224 {
1225 Q_UNUSED(event);
1226 }
1227
1228 /*!
1229 \reimp
1230 */
ungrabMouseEvent(QEvent * event)1231 void QGraphicsProxyWidget::ungrabMouseEvent(QEvent *event)
1232 {
1233 Q_D(QGraphicsProxyWidget);
1234 Q_UNUSED(event);
1235 d->embeddedMouseGrabber = nullptr;
1236 }
1237
1238 /*!
1239 \reimp
1240 */
mouseMoveEvent(QGraphicsSceneMouseEvent * event)1241 void QGraphicsProxyWidget::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
1242 {
1243 Q_D(QGraphicsProxyWidget);
1244 #ifdef GRAPHICSPROXYWIDGET_DEBUG
1245 qDebug("QGraphicsProxyWidget::mouseMoveEvent");
1246 #endif
1247 d->sendWidgetMouseEvent(event);
1248 }
1249
1250 /*!
1251 \reimp
1252 */
mousePressEvent(QGraphicsSceneMouseEvent * event)1253 void QGraphicsProxyWidget::mousePressEvent(QGraphicsSceneMouseEvent *event)
1254 {
1255 Q_D(QGraphicsProxyWidget);
1256 #ifdef GRAPHICSPROXYWIDGET_DEBUG
1257 qDebug("QGraphicsProxyWidget::mousePressEvent");
1258 #endif
1259 d->sendWidgetMouseEvent(event);
1260 }
1261
1262 /*!
1263 \reimp
1264 */
mouseDoubleClickEvent(QGraphicsSceneMouseEvent * event)1265 void QGraphicsProxyWidget::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
1266 {
1267 Q_D(QGraphicsProxyWidget);
1268 #ifdef GRAPHICSPROXYWIDGET_DEBUG
1269 qDebug("QGraphicsProxyWidget::mouseDoubleClickEvent");
1270 #endif
1271 d->sendWidgetMouseEvent(event);
1272 }
1273
1274 /*!
1275 \reimp
1276 */
1277 #if QT_CONFIG(wheelevent)
wheelEvent(QGraphicsSceneWheelEvent * event)1278 void QGraphicsProxyWidget::wheelEvent(QGraphicsSceneWheelEvent *event)
1279 {
1280 Q_D(QGraphicsProxyWidget);
1281 #ifdef GRAPHICSPROXYWIDGET_DEBUG
1282 qDebug("QGraphicsProxyWidget::wheelEvent");
1283 #endif
1284 if (!d->widget)
1285 return;
1286
1287 QPointF pos = event->pos();
1288 QPointer<QWidget> receiver = d->widget->childAt(pos.toPoint());
1289 if (!receiver)
1290 receiver = d->widget;
1291
1292 // Map event position from us to the receiver
1293 pos = d->mapToReceiver(pos, receiver);
1294
1295 // Send mouse event.
1296 QPoint angleDelta;
1297 if (event->orientation() == Qt::Horizontal)
1298 angleDelta.setX(event->delta());
1299 else
1300 angleDelta.setY(event->delta());
1301 // pixelDelta, inverted, scrollPhase and source from the original QWheelEvent
1302 // were not preserved in the QGraphicsSceneWheelEvent unfortunately
1303 QWheelEvent wheelEvent(pos, event->screenPos(), QPoint(), angleDelta,
1304 event->buttons(), event->modifiers(), Qt::NoScrollPhase, false);
1305 QPointer<QWidget> focusWidget = d->widget->focusWidget();
1306 extern bool qt_sendSpontaneousEvent(QObject *, QEvent *);
1307 qt_sendSpontaneousEvent(receiver, &wheelEvent);
1308 event->setAccepted(wheelEvent.isAccepted());
1309
1310 // ### Remove, this should be done by proper focusIn/focusOut events.
1311 if (focusWidget && !focusWidget->hasFocus()) {
1312 focusWidget->update();
1313 focusWidget = d->widget->focusWidget();
1314 if (focusWidget && focusWidget->hasFocus())
1315 focusWidget->update();
1316 }
1317 }
1318 #endif
1319
1320 /*!
1321 \reimp
1322 */
mouseReleaseEvent(QGraphicsSceneMouseEvent * event)1323 void QGraphicsProxyWidget::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
1324 {
1325 Q_D(QGraphicsProxyWidget);
1326 #ifdef GRAPHICSPROXYWIDGET_DEBUG
1327 qDebug("QGraphicsProxyWidget::mouseReleaseEvent");
1328 #endif
1329 d->sendWidgetMouseEvent(event);
1330 }
1331
1332 /*!
1333 \reimp
1334 */
keyPressEvent(QKeyEvent * event)1335 void QGraphicsProxyWidget::keyPressEvent(QKeyEvent *event)
1336 {
1337 Q_D(QGraphicsProxyWidget);
1338 #ifdef GRAPHICSPROXYWIDGET_DEBUG
1339 qDebug("QGraphicsProxyWidget::keyPressEvent");
1340 #endif
1341 d->sendWidgetKeyEvent(event);
1342 }
1343
1344 /*!
1345 \reimp
1346 */
keyReleaseEvent(QKeyEvent * event)1347 void QGraphicsProxyWidget::keyReleaseEvent(QKeyEvent *event)
1348 {
1349 Q_D(QGraphicsProxyWidget);
1350 #ifdef GRAPHICSPROXYWIDGET_DEBUG
1351 qDebug("QGraphicsProxyWidget::keyReleaseEvent");
1352 #endif
1353 d->sendWidgetKeyEvent(event);
1354 }
1355
1356 /*!
1357 \reimp
1358 */
focusInEvent(QFocusEvent * event)1359 void QGraphicsProxyWidget::focusInEvent(QFocusEvent *event)
1360 {
1361 #ifdef GRAPHICSPROXYWIDGET_DEBUG
1362 qDebug("QGraphicsProxyWidget::focusInEvent");
1363 #endif
1364 Q_D(QGraphicsProxyWidget);
1365
1366 if (d->focusFromWidgetToProxy) {
1367 // Prevent recursion when the proxy autogains focus through the
1368 // embedded widget calling setFocus(). ### Could be done with event
1369 // filter on FocusIn instead?
1370 return;
1371 }
1372
1373 d->proxyIsGivingFocus = true;
1374
1375 switch (event->reason()) {
1376 case Qt::TabFocusReason: {
1377 if (QWidget *focusChild = d->findFocusChild(nullptr, true))
1378 focusChild->setFocus(event->reason());
1379 break;
1380 }
1381 case Qt::BacktabFocusReason:
1382 if (QWidget *focusChild = d->findFocusChild(nullptr, false))
1383 focusChild->setFocus(event->reason());
1384 break;
1385 default:
1386 if (d->widget && d->widget->focusWidget()) {
1387 d->widget->focusWidget()->setFocus(event->reason());
1388 }
1389 break;
1390 }
1391
1392 // QTBUG-88016
1393 if (d->widget && d->widget->focusWidget()
1394 && d->widget->focusWidget()->testAttribute(Qt::WA_InputMethodEnabled))
1395 QApplication::inputMethod()->reset();
1396
1397 d->proxyIsGivingFocus = false;
1398 }
1399
1400 /*!
1401 \reimp
1402 */
focusOutEvent(QFocusEvent * event)1403 void QGraphicsProxyWidget::focusOutEvent(QFocusEvent *event)
1404 {
1405 #ifdef GRAPHICSPROXYWIDGET_DEBUG
1406 qDebug("QGraphicsProxyWidget::focusOutEvent");
1407 #endif
1408 Q_D(QGraphicsProxyWidget);
1409 if (d->widget) {
1410 // We need to explicitly remove subfocus from the embedded widget's
1411 // focus widget.
1412 if (QWidget *focusWidget = d->widget->focusWidget()) {
1413 // QTBUG-88016 proxyWidget set QTextEdit(QLineEdit etc.) when input preview text,
1414 // inputMethod should be reset when proxyWidget lost focus
1415 if (focusWidget && focusWidget->testAttribute(Qt::WA_InputMethodEnabled))
1416 QApplication::inputMethod()->reset();
1417
1418 d->removeSubFocusHelper(focusWidget, event->reason());
1419 }
1420 }
1421 }
1422
1423 /*!
1424 \reimp
1425 */
focusNextPrevChild(bool next)1426 bool QGraphicsProxyWidget::focusNextPrevChild(bool next)
1427 {
1428 Q_D(QGraphicsProxyWidget);
1429 if (!d->widget || !d->scene)
1430 return QGraphicsWidget::focusNextPrevChild(next);
1431
1432 Qt::FocusReason reason = next ? Qt::TabFocusReason : Qt::BacktabFocusReason;
1433 QWidget *lastFocusChild = d->widget->focusWidget();
1434 if (QWidget *newFocusChild = d->findFocusChild(lastFocusChild, next)) {
1435 newFocusChild->setFocus(reason);
1436 return true;
1437 }
1438
1439 return QGraphicsWidget::focusNextPrevChild(next);
1440 }
1441
1442 /*!
1443 \reimp
1444 */
inputMethodQuery(Qt::InputMethodQuery query) const1445 QVariant QGraphicsProxyWidget::inputMethodQuery(Qt::InputMethodQuery query) const
1446 {
1447 Q_D(const QGraphicsProxyWidget);
1448
1449 if (!d->widget || !hasFocus())
1450 return QVariant();
1451
1452 QWidget *focusWidget = widget()->focusWidget();
1453 if (!focusWidget)
1454 focusWidget = d->widget;
1455 QVariant v = focusWidget->inputMethodQuery(query);
1456 QPointF focusWidgetPos = subWidgetRect(focusWidget).topLeft();
1457 switch (v.userType()) {
1458 case QMetaType::QRectF:
1459 v = v.toRectF().translated(focusWidgetPos);
1460 break;
1461 case QMetaType::QPointF:
1462 v = v.toPointF() + focusWidgetPos;
1463 break;
1464 case QMetaType::QRect:
1465 v = v.toRect().translated(focusWidgetPos.toPoint());
1466 break;
1467 case QMetaType::QPoint:
1468 v = v.toPoint() + focusWidgetPos.toPoint();
1469 break;
1470 default:
1471 break;
1472 }
1473 return v;
1474 }
1475
1476 /*!
1477 \reimp
1478 */
inputMethodEvent(QInputMethodEvent * event)1479 void QGraphicsProxyWidget::inputMethodEvent(QInputMethodEvent *event)
1480 {
1481 // Forward input method events if the focus widget enables input methods.
1482 Q_D(const QGraphicsProxyWidget);
1483 QWidget *focusWidget = d->widget->focusWidget();
1484 if (focusWidget && focusWidget->testAttribute(Qt::WA_InputMethodEnabled))
1485 QCoreApplication::sendEvent(focusWidget, event);
1486 }
1487
1488 /*!
1489 \reimp
1490 */
sizeHint(Qt::SizeHint which,const QSizeF & constraint) const1491 QSizeF QGraphicsProxyWidget::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
1492 {
1493 Q_D(const QGraphicsProxyWidget);
1494 if (!d->widget)
1495 return QGraphicsWidget::sizeHint(which, constraint);
1496
1497 QSizeF sh;
1498 switch (which) {
1499 case Qt::PreferredSize:
1500 if (QLayout *l = d->widget->layout())
1501 sh = l->sizeHint();
1502 else
1503 sh = d->widget->sizeHint();
1504 break;
1505 case Qt::MinimumSize:
1506 if (QLayout *l = d->widget->layout())
1507 sh = l->minimumSize();
1508 else
1509 sh = d->widget->minimumSizeHint();
1510 break;
1511 case Qt::MaximumSize:
1512 if (QLayout *l = d->widget->layout())
1513 sh = l->maximumSize();
1514 else
1515 sh = QSizeF(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
1516 break;
1517 case Qt::MinimumDescent:
1518 sh = constraint;
1519 break;
1520 default:
1521 break;
1522 }
1523 return sh;
1524 }
1525
1526 /*!
1527 \reimp
1528 */
resizeEvent(QGraphicsSceneResizeEvent * event)1529 void QGraphicsProxyWidget::resizeEvent(QGraphicsSceneResizeEvent *event)
1530 {
1531 Q_D(QGraphicsProxyWidget);
1532 if (d->widget) {
1533 if (d->sizeChangeMode != QGraphicsProxyWidgetPrivate::WidgetToProxyMode)
1534 d->widget->resize(event->newSize().toSize());
1535 }
1536 QGraphicsWidget::resizeEvent(event);
1537 }
1538
1539 /*!
1540 \reimp
1541 */
paint(QPainter * painter,const QStyleOptionGraphicsItem * option,QWidget * widget)1542 void QGraphicsProxyWidget::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
1543 {
1544 Q_D(QGraphicsProxyWidget);
1545 Q_UNUSED(widget);
1546 if (!d->widget || !d->widget->isVisible())
1547 return;
1548
1549 // Filter out repaints on the window frame.
1550 const QRect exposedWidgetRect = (option->exposedRect & rect()).toAlignedRect();
1551 if (exposedWidgetRect.isEmpty())
1552 return;
1553
1554 d->widget->render(painter, exposedWidgetRect.topLeft(), exposedWidgetRect);
1555 }
1556
1557 /*!
1558 \enum QGraphicsProxyWidget::anonymous
1559
1560 The value returned by the virtual type() function.
1561
1562 \value Type A graphics proxy widget
1563 */
1564
1565 /*!
1566 \reimp
1567 */
type() const1568 int QGraphicsProxyWidget::type() const
1569 {
1570 return Type;
1571 }
1572
1573 /*!
1574 \since 4.5
1575
1576 Creates a proxy widget for the given \a child of the widget
1577 contained in this proxy.
1578
1579 This function makes it possible to acquire proxies for
1580 non top-level widgets. For instance, you can embed a dialog,
1581 and then transform only one of its widgets.
1582
1583 If the widget is already embedded, return the existing proxy widget.
1584
1585 \sa newProxyWidget(), QGraphicsScene::addWidget()
1586 */
createProxyForChildWidget(QWidget * child)1587 QGraphicsProxyWidget *QGraphicsProxyWidget::createProxyForChildWidget(QWidget *child)
1588 {
1589 QGraphicsProxyWidget *proxy = child->graphicsProxyWidget();
1590 if (proxy)
1591 return proxy;
1592 if (!child->parentWidget()) {
1593 qWarning("QGraphicsProxyWidget::createProxyForChildWidget: top-level widget not in a QGraphicsScene");
1594 return nullptr;
1595 }
1596
1597 QGraphicsProxyWidget *parentProxy = createProxyForChildWidget(child->parentWidget());
1598 if (!parentProxy)
1599 return nullptr;
1600
1601 if (!QMetaObject::invokeMethod(parentProxy, "newProxyWidget", Qt::DirectConnection,
1602 Q_RETURN_ARG(QGraphicsProxyWidget*, proxy), Q_ARG(const QWidget*, child)))
1603 return nullptr;
1604 proxy->setParent(parentProxy);
1605 proxy->setWidget(child);
1606 return proxy;
1607 }
1608
1609 /*!
1610 \fn QGraphicsProxyWidget *QGraphicsProxyWidget::newProxyWidget(const QWidget *child)
1611 \since 4.5
1612
1613 Creates a proxy widget for the given \a child of the widget contained in this
1614 proxy.
1615
1616 You should not call this function directly; use
1617 QGraphicsProxyWidget::createProxyForChildWidget() instead.
1618
1619 This function is a fake virtual slot that you can reimplement in
1620 your subclass in order to control how new proxy widgets are
1621 created. The default implementation returns a proxy created with
1622 the QGraphicsProxyWidget() constructor with this proxy widget as
1623 the parent.
1624
1625 \sa createProxyForChildWidget()
1626 */
newProxyWidget(const QWidget *)1627 QGraphicsProxyWidget *QGraphicsProxyWidget::newProxyWidget(const QWidget *)
1628 {
1629 return new QGraphicsProxyWidget(this);
1630 }
1631
1632
1633
1634 QT_END_NAMESPACE
1635
1636 #include "moc_qgraphicsproxywidget.cpp"
1637