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
63QT_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
193extern bool qt_sendSpontaneousEvent(QObject *, QEvent *);
194Q_WIDGETS_EXPORT extern bool qt_tab_all_widgets();
195
196/*!
197 \internal
198*/
199QGraphicsProxyWidgetPrivate::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*/
217QGraphicsProxyWidgetPrivate::~QGraphicsProxyWidgetPrivate()
218{
219}
220
221/*!
222 \internal
223*/
224void QGraphicsProxyWidgetPrivate::init()
225{
226 Q_Q(QGraphicsProxyWidget);
227 q->setFocusPolicy(Qt::WheelFocus);
228 q->setAcceptDrops(true);
229}
230
231/*!
232 \internal
233*/
234void 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(event: &mouseEvent);
243 event->setAccepted(mouseEvent.isAccepted());
244}
245
246/*!
247 \internal
248*/
249void 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(p: pos.toPoint());
258 QPointer<QWidget> receiver = alienWidget ? alienWidget : widget;
259
260 if (QWidgetPrivate::nearestGraphicsProxyWidget(origin: 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(enter: embeddedMouseGrabber ? embeddedMouseGrabber : receiver, leave: nullptr, globalPosF: 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, event: &mouseEvent, alienWidget, native: widget,
310 buttonDown: &embeddedMouseGrabberPtr, lastMouseReceiver&: lastWidgetUnderMouse, spontaneous: 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(p: 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(enter: lastWidgetUnderMouse, leave: embeddedMouseGrabber, globalPosF: 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
335void 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*/
357void QGraphicsProxyWidgetPrivate::removeSubFocusHelper(QWidget *widget, Qt::FocusReason reason)
358{
359 QFocusEvent event(QEvent::FocusOut, reason);
360 QPointer<QWidget> widgetGuard = widget;
361 QCoreApplication::sendEvent(receiver: widget, event: &event);
362 if (widgetGuard && event.isAccepted())
363 QCoreApplication::sendEvent(receiver: widget->style(), event: &event);
364}
365
366/*!
367 \internal
368 Some of the logic is shared with QApplicationPrivate::focusNextPrevChild_helper
369*/
370QWidget *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*/
405void 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*/
419void QGraphicsProxyWidgetPrivate::updateWidgetGeometryFromProxy()
420{
421}
422
423/*!
424 \internal
425*/
426void 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 *>(object: 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(p: proxyParent->subWidgetRect(widget: 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*/
460void 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(flag: QGraphicsItem::ItemAcceptsInputMethod,
470 enabled: focusWidget->testAttribute(attribute: 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*/
480void 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(widget: subWin, autoShow: 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*/
496void QGraphicsProxyWidgetPrivate::unembedSubWindow(QWidget *subWin)
497{
498 foreach (QGraphicsItem *child, children) {
499 if (child->isWidget()) {
500 if (QGraphicsProxyWidget *proxy = qobject_cast<QGraphicsProxyWidget *>(object: static_cast<QGraphicsWidget *>(child))) {
501 if (proxy->widget() == subWin) {
502 proxy->setWidget(nullptr);
503 scene->removeItem(item: proxy);
504 delete proxy;
505 return;
506 }
507 }
508 }
509 }
510}
511
512bool QGraphicsProxyWidgetPrivate::isProxyWidget() const
513{
514 return true;
515}
516
517/*!
518 \internal
519*/
520QPointF 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*/
536QGraphicsProxyWidget::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*/
546QGraphicsProxyWidget::~QGraphicsProxyWidget()
547{
548 Q_D(QGraphicsProxyWidget);
549 if (d->widget) {
550 d->widget->removeEventFilter(obj: this);
551 QObject::disconnect(sender: d->widget, SIGNAL(destroyed()), receiver: 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*/
588void QGraphicsProxyWidget::setWidget(QWidget *widget)
589{
590 Q_D(QGraphicsProxyWidget);
591 d->setWidget_helper(widget, autoShow: true);
592}
593
594void QGraphicsProxyWidgetPrivate::setWidget_helper(QWidget *newWidget, bool autoShow)
595{
596 Q_Q(QGraphicsProxyWidget);
597 if (newWidget == widget)
598 return;
599 if (widget) {
600 QObject::disconnect(sender: widget, SIGNAL(destroyed()), receiver: q, SLOT(_q_removeWidgetSlot()));
601 widget->removeEventFilter(obj: q);
602 widget->setAttribute(Qt::WA_DontShowOnScreen, on: false);
603 widget->d_func()->extra->proxyWidget = nullptr;
604 resolveFont(inheritedMask: inheritedFontResolveMask);
605 resolvePalette(inheritedMask: 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(msg: "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(msg: "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, on: false);
666 q->setAcceptHoverEvents(true);
667
668 if (newWidget->testAttribute(attribute: Qt::WA_NoSystemBackground))
669 q->setAttribute(attribute: Qt::WA_NoSystemBackground);
670 if (newWidget->testAttribute(attribute: Qt::WA_OpaquePaintEvent))
671 q->setAttribute(attribute: 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(attribute: Qt::WA_WState_ExplicitShowHide)) || !newWidget->testAttribute(attribute: 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(attribute: 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(attribute: Qt::WA_SetStyle))
694 q->setStyle(widget->style());
695
696 resolveFont(inheritedMask: inheritedFontResolveMask);
697 resolvePalette(inheritedMask: inheritedPaletteResolveMask);
698
699 if (!newWidget->testAttribute(attribute: 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(filterObj: q);
718 QObject::connect(sender: newWidget, SIGNAL(destroyed()), receiver: 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*/
732QWidget *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*/
747QRectF 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(child: widget))
753 return QRectF(widget->mapTo(d->widget, QPoint(0, 0)), widget->size());
754 return QRectF();
755}
756
757/*!
758 \reimp
759*/
760void 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*/
778QVariant 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*/
828bool 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::WindowActivate:
836 case QEvent::WindowDeactivate:
837 QCoreApplication::sendEvent(receiver: d->widget, event);
838 break;
839 case QEvent::StyleChange:
840 // Propagate style changes to the embedded widget.
841 if (!d->styleChangeMode) {
842 d->styleChangeMode = QGraphicsProxyWidgetPrivate::ProxyToWidgetMode;
843 d->widget->setStyle(style());
844 d->styleChangeMode = QGraphicsProxyWidgetPrivate::NoMode;
845 }
846 break;
847 case QEvent::FontChange: {
848 // Propagate to widget.
849 QWidgetPrivate *wd = d->widget->d_func();
850 int mask = d->font.resolve() | d->inheritedFontResolveMask;
851 wd->inheritedFontResolveMask = mask;
852 wd->resolveFont();
853 break;
854 }
855 case QEvent::PaletteChange: {
856 // Propagate to widget.
857 QWidgetPrivate *wd = d->widget->d_func();
858 int mask = d->palette.resolve() | d->inheritedPaletteResolveMask;
859 wd->inheritedPaletteResolveMask = mask;
860 wd->resolvePalette();
861 break;
862 }
863 case QEvent::InputMethod: {
864 inputMethodEvent(event: static_cast<QInputMethodEvent *>(event));
865 if (event->isAccepted())
866 return true;
867 return false;
868 }
869 case QEvent::ShortcutOverride: {
870 QWidget *focusWidget = d->widget->focusWidget();
871 while (focusWidget) {
872 QCoreApplication::sendEvent(receiver: focusWidget, event);
873 if (event->isAccepted())
874 return true;
875 focusWidget = focusWidget->parentWidget();
876 }
877 return false;
878 }
879 case QEvent::KeyPress: {
880 QKeyEvent *k = static_cast<QKeyEvent *>(event);
881 if (k->key() == Qt::Key_Tab || k->key() == Qt::Key_Backtab) {
882 if (!(k->modifiers() & (Qt::ControlModifier | Qt::AltModifier))) { //### Add MetaModifier?
883 QWidget *focusWidget = d->widget->focusWidget();
884 while (focusWidget) {
885 const bool res = QCoreApplication::sendEvent(receiver: focusWidget, event);
886 if ((res && event->isAccepted()) || (isWindow() && focusWidget == d->widget)) {
887 event->accept();
888 break;
889 }
890 focusWidget = focusWidget->parentWidget();
891 }
892 return true;
893 }
894 }
895 break;
896 }
897#ifndef QT_NO_TOOLTIP
898 case QEvent::GraphicsSceneHelp: {
899 // Propagate the help event (for tooltip) to the widget under mouse
900 if (d->lastWidgetUnderMouse) {
901 QGraphicsSceneHelpEvent *he = static_cast<QGraphicsSceneHelpEvent *>(event);
902 QPoint pos = d->mapToReceiver(pos: mapFromScene(point: he->scenePos()), receiver: d->lastWidgetUnderMouse).toPoint();
903 QHelpEvent e(QEvent::ToolTip, pos, he->screenPos());
904 QCoreApplication::sendEvent(receiver: d->lastWidgetUnderMouse, event: &e);
905 event->setAccepted(e.isAccepted());
906 return e.isAccepted();
907 }
908 break;
909 }
910 case QEvent::ToolTipChange: {
911 // Propagate tooltip change to the widget
912 if (!d->tooltipChangeMode) {
913 d->tooltipChangeMode = QGraphicsProxyWidgetPrivate::ProxyToWidgetMode;
914 d->widget->setToolTip(toolTip());
915 d->tooltipChangeMode = QGraphicsProxyWidgetPrivate::NoMode;
916 }
917 break;
918 }
919#endif
920 case QEvent::TouchBegin:
921 case QEvent::TouchUpdate:
922 case QEvent::TouchEnd: {
923 if (event->spontaneous())
924 qt_sendSpontaneousEvent(d->widget, event);
925 else
926 QCoreApplication::sendEvent(receiver: d->widget, event);
927
928 if (event->isAccepted())
929 return true;
930
931 break;
932 }
933 default:
934 break;
935 }
936 return QGraphicsWidget::event(event);
937}
938
939/*!
940 \reimp
941*/
942bool QGraphicsProxyWidget::eventFilter(QObject *object, QEvent *event)
943{
944 Q_D(QGraphicsProxyWidget);
945
946 if (object == d->widget) {
947 switch (event->type()) {
948 case QEvent::LayoutRequest:
949 updateGeometry();
950 break;
951 case QEvent::Resize:
952 // If the widget resizes itself, we resize the proxy too.
953 // Prevent feed-back by checking the geometry change mode.
954 if (!d->sizeChangeMode)
955 d->updateProxyGeometryFromWidget();
956 break;
957 case QEvent::Move:
958 // If the widget moves itself, we move the proxy too. Prevent
959 // feed-back by checking the geometry change mode.
960 if (!d->posChangeMode)
961 d->updateProxyGeometryFromWidget();
962 break;
963 case QEvent::Hide:
964 case QEvent::Show:
965 // If the widget toggles its visible state, the proxy will follow.
966 if (!d->visibleChangeMode) {
967 d->visibleChangeMode = QGraphicsProxyWidgetPrivate::WidgetToProxyMode;
968 setVisible(event->type() == QEvent::Show);
969 d->visibleChangeMode = QGraphicsProxyWidgetPrivate::NoMode;
970 }
971 break;
972 case QEvent::EnabledChange:
973 // If the widget toggles its enabled state, the proxy will follow.
974 if (!d->enabledChangeMode) {
975 d->enabledChangeMode = QGraphicsProxyWidgetPrivate::WidgetToProxyMode;
976 setEnabled(d->widget->isEnabled());
977 d->enabledChangeMode = QGraphicsProxyWidgetPrivate::NoMode;
978 }
979 break;
980 case QEvent::StyleChange:
981 // Propagate style changes to the proxy.
982 if (!d->styleChangeMode) {
983 d->styleChangeMode = QGraphicsProxyWidgetPrivate::WidgetToProxyMode;
984 setStyle(d->widget->style());
985 d->styleChangeMode = QGraphicsProxyWidgetPrivate::NoMode;
986 }
987 break;
988#ifndef QT_NO_TOOLTIP
989 case QEvent::ToolTipChange:
990 // Propagate tooltip change to the proxy.
991 if (!d->tooltipChangeMode) {
992 d->tooltipChangeMode = QGraphicsProxyWidgetPrivate::WidgetToProxyMode;
993 setToolTip(d->widget->toolTip());
994 d->tooltipChangeMode = QGraphicsProxyWidgetPrivate::NoMode;
995 }
996 break;
997#endif
998 default:
999 break;
1000 }
1001 }
1002 return QGraphicsWidget::eventFilter(watched: object, event);
1003}
1004
1005/*!
1006 \reimp
1007*/
1008void QGraphicsProxyWidget::showEvent(QShowEvent *event)
1009{
1010 Q_UNUSED(event);
1011}
1012
1013/*!
1014 \reimp
1015*/
1016void QGraphicsProxyWidget::hideEvent(QHideEvent *event)
1017{
1018 Q_UNUSED(event);
1019}
1020
1021#ifndef QT_NO_CONTEXTMENU
1022/*!
1023 \reimp
1024*/
1025void QGraphicsProxyWidget::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
1026{
1027 Q_D(QGraphicsProxyWidget);
1028 if (!event || !d->widget || !d->widget->isVisible() || !hasFocus())
1029 return;
1030
1031 // Find widget position and receiver.
1032 QPointF pos = event->pos();
1033 QPointer<QWidget> alienWidget = d->widget->childAt(p: pos.toPoint());
1034 QPointer<QWidget> receiver = alienWidget ? alienWidget : d->widget;
1035
1036 // Map event position from us to the receiver
1037 pos = d->mapToReceiver(pos, receiver);
1038
1039 QPoint globalPos = receiver->mapToGlobal(pos.toPoint());
1040 //If the receiver by-pass the proxy its popups
1041 //will be top level QWidgets therefore they need
1042 //the screen position. mapToGlobal expect the widget to
1043 //have proper coordinates in regards of the windowing system
1044 //but it's not true because the widget is embedded.
1045 if (bypassGraphicsProxyWidget(p: receiver))
1046 globalPos = event->screenPos();
1047
1048 // Send mouse event. ### Doesn't propagate the event.
1049 QContextMenuEvent contextMenuEvent(QContextMenuEvent::Reason(event->reason()),
1050 pos.toPoint(), globalPos, event->modifiers());
1051 QCoreApplication::sendEvent(receiver, event: &contextMenuEvent);
1052
1053 event->setAccepted(contextMenuEvent.isAccepted());
1054}
1055#endif // QT_NO_CONTEXTMENU
1056
1057#if QT_CONFIG(draganddrop)
1058/*!
1059 \reimp
1060*/
1061void QGraphicsProxyWidget::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
1062{
1063#if !QT_CONFIG(draganddrop)
1064 Q_UNUSED(event);
1065#else
1066 Q_D(QGraphicsProxyWidget);
1067 if (!d->widget)
1068 return;
1069
1070 QDragEnterEvent proxyDragEnter(event->pos().toPoint(), event->dropAction(), event->mimeData(), event->buttons(), event->modifiers());
1071 proxyDragEnter.setAccepted(event->isAccepted());
1072 QCoreApplication::sendEvent(receiver: d->widget, event: &proxyDragEnter);
1073 event->setAccepted(proxyDragEnter.isAccepted());
1074 if (proxyDragEnter.isAccepted()) // we discard answerRect
1075 event->setDropAction(proxyDragEnter.dropAction());
1076#endif
1077}
1078/*!
1079 \reimp
1080*/
1081void QGraphicsProxyWidget::dragLeaveEvent(QGraphicsSceneDragDropEvent *event)
1082{
1083 Q_UNUSED(event);
1084#if QT_CONFIG(draganddrop)
1085 Q_D(QGraphicsProxyWidget);
1086 if (!d->widget || !d->dragDropWidget)
1087 return;
1088 QDragLeaveEvent proxyDragLeave;
1089 QCoreApplication::sendEvent(receiver: d->dragDropWidget, event: &proxyDragLeave);
1090 d->dragDropWidget = nullptr;
1091#endif
1092}
1093
1094/*!
1095 \reimp
1096*/
1097void QGraphicsProxyWidget::dragMoveEvent(QGraphicsSceneDragDropEvent *event)
1098{
1099#if !QT_CONFIG(draganddrop)
1100 Q_UNUSED(event);
1101#else
1102 Q_D(QGraphicsProxyWidget);
1103 if (!d->widget)
1104 return;
1105 QPointF p = event->pos();
1106 event->ignore();
1107 QPointer<QWidget> subWidget = d->widget->childAt(p: p.toPoint());
1108 QPointer<QWidget> receiver = subWidget ? subWidget : d->widget;
1109 bool eventDelivered = false;
1110 for (; receiver; receiver = receiver->parentWidget()) {
1111 if (!receiver->isEnabled() || !receiver->acceptDrops())
1112 continue;
1113 // Map event position from us to the receiver
1114 QPoint receiverPos = d->mapToReceiver(pos: p, receiver).toPoint();
1115 if (receiver != d->dragDropWidget) {
1116 // Try to enter before we leave
1117 QDragEnterEvent dragEnter(receiverPos, event->possibleActions(), event->mimeData(), event->buttons(), event->modifiers());
1118 dragEnter.setDropAction(event->proposedAction());
1119 QCoreApplication::sendEvent(receiver, event: &dragEnter);
1120 event->setAccepted(dragEnter.isAccepted());
1121 event->setDropAction(dragEnter.dropAction());
1122 if (!event->isAccepted()) {
1123 // propagate to the parent widget
1124 continue;
1125 }
1126
1127 d->lastDropAction = event->dropAction();
1128
1129 if (d->dragDropWidget) {
1130 QDragLeaveEvent dragLeave;
1131 QCoreApplication::sendEvent(receiver: d->dragDropWidget, event: &dragLeave);
1132 }
1133 d->dragDropWidget = receiver;
1134 }
1135
1136 QDragMoveEvent dragMove(receiverPos, event->possibleActions(), event->mimeData(), event->buttons(), event->modifiers());
1137 event->setDropAction(d->lastDropAction);
1138 QCoreApplication::sendEvent(receiver, event: &dragMove);
1139 event->setAccepted(dragMove.isAccepted());
1140 event->setDropAction(dragMove.dropAction());
1141 if (event->isAccepted())
1142 d->lastDropAction = event->dropAction();
1143 eventDelivered = true;
1144 break;
1145 }
1146
1147 if (!eventDelivered) {
1148 if (d->dragDropWidget) {
1149 // Leave the last drag drop item
1150 QDragLeaveEvent dragLeave;
1151 QCoreApplication::sendEvent(receiver: d->dragDropWidget, event: &dragLeave);
1152 d->dragDropWidget = nullptr;
1153 }
1154 // Propagate
1155 event->setDropAction(Qt::IgnoreAction);
1156 }
1157#endif
1158}
1159
1160/*!
1161 \reimp
1162*/
1163void QGraphicsProxyWidget::dropEvent(QGraphicsSceneDragDropEvent *event)
1164{
1165#if !QT_CONFIG(draganddrop)
1166 Q_UNUSED(event);
1167#else
1168 Q_D(QGraphicsProxyWidget);
1169 if (d->widget && d->dragDropWidget) {
1170 QPoint widgetPos = d->mapToReceiver(pos: event->pos(), receiver: d->dragDropWidget).toPoint();
1171 QDropEvent dropEvent(widgetPos, event->possibleActions(), event->mimeData(), event->buttons(), event->modifiers());
1172 QCoreApplication::sendEvent(receiver: d->dragDropWidget, event: &dropEvent);
1173 event->setAccepted(dropEvent.isAccepted());
1174 d->dragDropWidget = nullptr;
1175 }
1176#endif
1177}
1178#endif
1179
1180/*!
1181 \reimp
1182*/
1183void QGraphicsProxyWidget::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
1184{
1185 Q_UNUSED(event);
1186}
1187
1188/*!
1189 \reimp
1190*/
1191void QGraphicsProxyWidget::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
1192{
1193 Q_UNUSED(event);
1194 Q_D(QGraphicsProxyWidget);
1195 // If hoverMove was compressed away, make sure we update properly here.
1196 if (d->lastWidgetUnderMouse) {
1197 QApplicationPrivate::dispatchEnterLeave(enter: nullptr, leave: d->lastWidgetUnderMouse, globalPosF: event->screenPos());
1198 d->lastWidgetUnderMouse = nullptr;
1199 }
1200}
1201
1202/*!
1203 \reimp
1204*/
1205void QGraphicsProxyWidget::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
1206{
1207 Q_D(QGraphicsProxyWidget);
1208#ifdef GRAPHICSPROXYWIDGET_DEBUG
1209 qDebug("QGraphicsProxyWidget::hoverMoveEvent");
1210#endif
1211 // Ignore events on the window frame.
1212 if (!d->widget || !rect().contains(p: event->pos())) {
1213 if (d->lastWidgetUnderMouse) {
1214 QApplicationPrivate::dispatchEnterLeave(enter: nullptr, leave: d->lastWidgetUnderMouse, globalPosF: event->screenPos());
1215 d->lastWidgetUnderMouse = nullptr;
1216 }
1217 return;
1218 }
1219
1220 d->embeddedMouseGrabber = nullptr;
1221 d->sendWidgetMouseEvent(event);
1222}
1223
1224/*!
1225 \reimp
1226*/
1227void QGraphicsProxyWidget::grabMouseEvent(QEvent *event)
1228{
1229 Q_UNUSED(event);
1230}
1231
1232/*!
1233 \reimp
1234*/
1235void QGraphicsProxyWidget::ungrabMouseEvent(QEvent *event)
1236{
1237 Q_D(QGraphicsProxyWidget);
1238 Q_UNUSED(event);
1239 d->embeddedMouseGrabber = nullptr;
1240}
1241
1242/*!
1243 \reimp
1244*/
1245void QGraphicsProxyWidget::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
1246{
1247 Q_D(QGraphicsProxyWidget);
1248#ifdef GRAPHICSPROXYWIDGET_DEBUG
1249 qDebug("QGraphicsProxyWidget::mouseMoveEvent");
1250#endif
1251 d->sendWidgetMouseEvent(event);
1252}
1253
1254/*!
1255 \reimp
1256*/
1257void QGraphicsProxyWidget::mousePressEvent(QGraphicsSceneMouseEvent *event)
1258{
1259 Q_D(QGraphicsProxyWidget);
1260#ifdef GRAPHICSPROXYWIDGET_DEBUG
1261 qDebug("QGraphicsProxyWidget::mousePressEvent");
1262#endif
1263 d->sendWidgetMouseEvent(event);
1264}
1265
1266/*!
1267 \reimp
1268*/
1269void QGraphicsProxyWidget::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
1270{
1271 Q_D(QGraphicsProxyWidget);
1272#ifdef GRAPHICSPROXYWIDGET_DEBUG
1273 qDebug("QGraphicsProxyWidget::mouseDoubleClickEvent");
1274#endif
1275 d->sendWidgetMouseEvent(event);
1276}
1277
1278/*!
1279 \reimp
1280*/
1281#if QT_CONFIG(wheelevent)
1282void QGraphicsProxyWidget::wheelEvent(QGraphicsSceneWheelEvent *event)
1283{
1284 Q_D(QGraphicsProxyWidget);
1285#ifdef GRAPHICSPROXYWIDGET_DEBUG
1286 qDebug("QGraphicsProxyWidget::wheelEvent");
1287#endif
1288 if (!d->widget)
1289 return;
1290
1291 QPointF pos = event->pos();
1292 QPointer<QWidget> receiver = d->widget->childAt(p: pos.toPoint());
1293 if (!receiver)
1294 receiver = d->widget;
1295
1296 // Map event position from us to the receiver
1297 pos = d->mapToReceiver(pos, receiver);
1298
1299 // Send mouse event.
1300 QPoint angleDelta;
1301 if (event->orientation() == Qt::Horizontal)
1302 angleDelta.setX(event->delta());
1303 else
1304 angleDelta.setY(event->delta());
1305 // pixelDelta, inverted, scrollPhase and source from the original QWheelEvent
1306 // were not preserved in the QGraphicsSceneWheelEvent unfortunately
1307 QWheelEvent wheelEvent(pos, event->screenPos(), QPoint(), angleDelta,
1308 event->buttons(), event->modifiers(), Qt::NoScrollPhase, false);
1309 QPointer<QWidget> focusWidget = d->widget->focusWidget();
1310 extern bool qt_sendSpontaneousEvent(QObject *, QEvent *);
1311 qt_sendSpontaneousEvent(receiver, &wheelEvent);
1312 event->setAccepted(wheelEvent.isAccepted());
1313
1314 // ### Remove, this should be done by proper focusIn/focusOut events.
1315 if (focusWidget && !focusWidget->hasFocus()) {
1316 focusWidget->update();
1317 focusWidget = d->widget->focusWidget();
1318 if (focusWidget && focusWidget->hasFocus())
1319 focusWidget->update();
1320 }
1321}
1322#endif
1323
1324/*!
1325 \reimp
1326*/
1327void QGraphicsProxyWidget::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
1328{
1329 Q_D(QGraphicsProxyWidget);
1330#ifdef GRAPHICSPROXYWIDGET_DEBUG
1331 qDebug("QGraphicsProxyWidget::mouseReleaseEvent");
1332#endif
1333 d->sendWidgetMouseEvent(event);
1334}
1335
1336/*!
1337 \reimp
1338*/
1339void QGraphicsProxyWidget::keyPressEvent(QKeyEvent *event)
1340{
1341 Q_D(QGraphicsProxyWidget);
1342#ifdef GRAPHICSPROXYWIDGET_DEBUG
1343 qDebug("QGraphicsProxyWidget::keyPressEvent");
1344#endif
1345 d->sendWidgetKeyEvent(event);
1346}
1347
1348/*!
1349 \reimp
1350*/
1351void QGraphicsProxyWidget::keyReleaseEvent(QKeyEvent *event)
1352{
1353 Q_D(QGraphicsProxyWidget);
1354#ifdef GRAPHICSPROXYWIDGET_DEBUG
1355 qDebug("QGraphicsProxyWidget::keyReleaseEvent");
1356#endif
1357 d->sendWidgetKeyEvent(event);
1358}
1359
1360/*!
1361 \reimp
1362*/
1363void QGraphicsProxyWidget::focusInEvent(QFocusEvent *event)
1364{
1365#ifdef GRAPHICSPROXYWIDGET_DEBUG
1366 qDebug("QGraphicsProxyWidget::focusInEvent");
1367#endif
1368 Q_D(QGraphicsProxyWidget);
1369
1370 if (d->focusFromWidgetToProxy) {
1371 // Prevent recursion when the proxy autogains focus through the
1372 // embedded widget calling setFocus(). ### Could be done with event
1373 // filter on FocusIn instead?
1374 return;
1375 }
1376
1377 d->proxyIsGivingFocus = true;
1378
1379 switch (event->reason()) {
1380 case Qt::TabFocusReason: {
1381 if (QWidget *focusChild = d->findFocusChild(child: nullptr, next: true))
1382 focusChild->setFocus(event->reason());
1383 break;
1384 }
1385 case Qt::BacktabFocusReason:
1386 if (QWidget *focusChild = d->findFocusChild(child: nullptr, next: false))
1387 focusChild->setFocus(event->reason());
1388 break;
1389 default:
1390 if (d->widget && d->widget->focusWidget()) {
1391 d->widget->focusWidget()->setFocus(event->reason());
1392 }
1393 break;
1394 }
1395
1396 // QTBUG-88016
1397 if (d->widget && d->widget->focusWidget()
1398 && d->widget->focusWidget()->testAttribute(attribute: Qt::WA_InputMethodEnabled))
1399 QApplication::inputMethod()->reset();
1400
1401 d->proxyIsGivingFocus = false;
1402}
1403
1404/*!
1405 \reimp
1406*/
1407void QGraphicsProxyWidget::focusOutEvent(QFocusEvent *event)
1408{
1409#ifdef GRAPHICSPROXYWIDGET_DEBUG
1410 qDebug("QGraphicsProxyWidget::focusOutEvent");
1411#endif
1412 Q_D(QGraphicsProxyWidget);
1413 if (d->widget) {
1414 // We need to explicitly remove subfocus from the embedded widget's
1415 // focus widget.
1416 if (QWidget *focusWidget = d->widget->focusWidget()) {
1417 // QTBUG-88016 proxyWidget set QTextEdit(QLineEdit etc.) when input preview text,
1418 // inputMethod should be reset when proxyWidget lost focus
1419 if (focusWidget && focusWidget->testAttribute(attribute: Qt::WA_InputMethodEnabled))
1420 QApplication::inputMethod()->reset();
1421
1422 d->removeSubFocusHelper(widget: focusWidget, reason: event->reason());
1423 }
1424 }
1425}
1426
1427/*!
1428 \reimp
1429*/
1430bool QGraphicsProxyWidget::focusNextPrevChild(bool next)
1431{
1432 Q_D(QGraphicsProxyWidget);
1433 if (!d->widget || !d->scene)
1434 return QGraphicsWidget::focusNextPrevChild(next);
1435
1436 Qt::FocusReason reason = next ? Qt::TabFocusReason : Qt::BacktabFocusReason;
1437 QWidget *lastFocusChild = d->widget->focusWidget();
1438 if (QWidget *newFocusChild = d->findFocusChild(child: lastFocusChild, next)) {
1439 newFocusChild->setFocus(reason);
1440 return true;
1441 }
1442
1443 return QGraphicsWidget::focusNextPrevChild(next);
1444}
1445
1446/*!
1447 \reimp
1448*/
1449QVariant QGraphicsProxyWidget::inputMethodQuery(Qt::InputMethodQuery query) const
1450{
1451 Q_D(const QGraphicsProxyWidget);
1452
1453 if (!d->widget || !hasFocus())
1454 return QVariant();
1455
1456 QWidget *focusWidget = widget()->focusWidget();
1457 if (!focusWidget)
1458 focusWidget = d->widget;
1459 QVariant v = focusWidget->inputMethodQuery(query);
1460 QPointF focusWidgetPos = subWidgetRect(widget: focusWidget).topLeft();
1461 switch (v.userType()) {
1462 case QMetaType::QRectF:
1463 v = v.toRectF().translated(p: focusWidgetPos);
1464 break;
1465 case QMetaType::QPointF:
1466 v = v.toPointF() + focusWidgetPos;
1467 break;
1468 case QMetaType::QRect:
1469 v = v.toRect().translated(p: focusWidgetPos.toPoint());
1470 break;
1471 case QMetaType::QPoint:
1472 v = v.toPoint() + focusWidgetPos.toPoint();
1473 break;
1474 default:
1475 break;
1476 }
1477 return v;
1478}
1479
1480/*!
1481 \reimp
1482*/
1483void QGraphicsProxyWidget::inputMethodEvent(QInputMethodEvent *event)
1484{
1485 // Forward input method events if the focus widget enables input methods.
1486 Q_D(const QGraphicsProxyWidget);
1487 QWidget *focusWidget = d->widget->focusWidget();
1488 if (focusWidget && focusWidget->testAttribute(attribute: Qt::WA_InputMethodEnabled))
1489 QCoreApplication::sendEvent(receiver: focusWidget, event);
1490}
1491
1492/*!
1493 \reimp
1494*/
1495QSizeF QGraphicsProxyWidget::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
1496{
1497 Q_D(const QGraphicsProxyWidget);
1498 if (!d->widget)
1499 return QGraphicsWidget::sizeHint(which, constraint);
1500
1501 QSizeF sh;
1502 switch (which) {
1503 case Qt::PreferredSize:
1504 if (QLayout *l = d->widget->layout())
1505 sh = l->sizeHint();
1506 else
1507 sh = d->widget->sizeHint();
1508 break;
1509 case Qt::MinimumSize:
1510 if (QLayout *l = d->widget->layout())
1511 sh = l->minimumSize();
1512 else
1513 sh = d->widget->minimumSizeHint();
1514 break;
1515 case Qt::MaximumSize:
1516 if (QLayout *l = d->widget->layout())
1517 sh = l->maximumSize();
1518 else
1519 sh = QSizeF(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
1520 break;
1521 case Qt::MinimumDescent:
1522 sh = constraint;
1523 break;
1524 default:
1525 break;
1526 }
1527 return sh;
1528}
1529
1530/*!
1531 \reimp
1532*/
1533void QGraphicsProxyWidget::resizeEvent(QGraphicsSceneResizeEvent *event)
1534{
1535 Q_D(QGraphicsProxyWidget);
1536 if (d->widget) {
1537 if (d->sizeChangeMode != QGraphicsProxyWidgetPrivate::WidgetToProxyMode)
1538 d->widget->resize(event->newSize().toSize());
1539 }
1540 QGraphicsWidget::resizeEvent(event);
1541}
1542
1543/*!
1544 \reimp
1545*/
1546void QGraphicsProxyWidget::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
1547{
1548 Q_D(QGraphicsProxyWidget);
1549 Q_UNUSED(widget);
1550 if (!d->widget || !d->widget->isVisible())
1551 return;
1552
1553 // Filter out repaints on the window frame.
1554 const QRect exposedWidgetRect = (option->exposedRect & rect()).toAlignedRect();
1555 if (exposedWidgetRect.isEmpty())
1556 return;
1557
1558 // When rendering to pdf etc. painting may go outside widget boundaries unless clipped
1559 if (painter->device()->devType() != QInternal::Widget && (flags() & ItemClipsChildrenToShape))
1560 painter->setClipRect(d->widget->geometry(), op: Qt::IntersectClip);
1561
1562 d->widget->render(painter, targetOffset: exposedWidgetRect.topLeft(), sourceRegion: exposedWidgetRect);
1563}
1564
1565/*!
1566 \enum QGraphicsProxyWidget::anonymous
1567
1568 The value returned by the virtual type() function.
1569
1570 \value Type A graphics proxy widget
1571*/
1572
1573/*!
1574 \reimp
1575*/
1576int QGraphicsProxyWidget::type() const
1577{
1578 return Type;
1579}
1580
1581/*!
1582 \since 4.5
1583
1584 Creates a proxy widget for the given \a child of the widget
1585 contained in this proxy.
1586
1587 This function makes it possible to acquire proxies for
1588 non top-level widgets. For instance, you can embed a dialog,
1589 and then transform only one of its widgets.
1590
1591 If the widget is already embedded, return the existing proxy widget.
1592
1593 \sa newProxyWidget(), QGraphicsScene::addWidget()
1594*/
1595QGraphicsProxyWidget *QGraphicsProxyWidget::createProxyForChildWidget(QWidget *child)
1596{
1597 QGraphicsProxyWidget *proxy = child->graphicsProxyWidget();
1598 if (proxy)
1599 return proxy;
1600 if (!child->parentWidget()) {
1601 qWarning(msg: "QGraphicsProxyWidget::createProxyForChildWidget: top-level widget not in a QGraphicsScene");
1602 return nullptr;
1603 }
1604
1605 QGraphicsProxyWidget *parentProxy = createProxyForChildWidget(child: child->parentWidget());
1606 if (!parentProxy)
1607 return nullptr;
1608
1609 if (!QMetaObject::invokeMethod(obj: parentProxy, member: "newProxyWidget", Qt::DirectConnection,
1610 Q_RETURN_ARG(QGraphicsProxyWidget*, proxy), Q_ARG(const QWidget*, child)))
1611 return nullptr;
1612 proxy->setParent(parentProxy);
1613 proxy->setWidget(child);
1614 return proxy;
1615}
1616
1617/*!
1618 \fn QGraphicsProxyWidget *QGraphicsProxyWidget::newProxyWidget(const QWidget *child)
1619 \since 4.5
1620
1621 Creates a proxy widget for the given \a child of the widget contained in this
1622 proxy.
1623
1624 You should not call this function directly; use
1625 QGraphicsProxyWidget::createProxyForChildWidget() instead.
1626
1627 This function is a fake virtual slot that you can reimplement in
1628 your subclass in order to control how new proxy widgets are
1629 created. The default implementation returns a proxy created with
1630 the QGraphicsProxyWidget() constructor with this proxy widget as
1631 the parent.
1632
1633 \sa createProxyForChildWidget()
1634*/
1635QGraphicsProxyWidget *QGraphicsProxyWidget::newProxyWidget(const QWidget *)
1636{
1637 return new QGraphicsProxyWidget(this);
1638}
1639
1640
1641
1642QT_END_NAMESPACE
1643
1644#include "moc_qgraphicsproxywidget.cpp"
1645

source code of qtbase/src/widgets/graphicsview/qgraphicsproxywidget.cpp