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

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