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#ifndef QT_NO_CURSOR
299 // Keep cursor in sync
300 if (lastWidgetUnderMouse) {
301 QCursor widgetsCursor = lastWidgetUnderMouse->cursor();
302 if (q->cursor() != widgetsCursor)
303 q->setCursor(widgetsCursor);
304 }
305#endif
306
307 event->setAccepted(mouseEvent.isAccepted());
308}
309
310void QGraphicsProxyWidgetPrivate::sendWidgetKeyEvent(QKeyEvent *event)
311{
312 Q_Q(QGraphicsProxyWidget);
313 if (!event || !widget || !widget->isVisible())
314 return;
315
316 QPointer<QWidget> receiver = widget->focusWidget();
317 if (!receiver)
318 receiver = widget;
319 Q_ASSERT(receiver);
320
321 do {
322 bool res = QCoreApplication::sendEvent(receiver, event);
323 if ((res && event->isAccepted()) || (q->isWindow() && receiver == widget))
324 break;
325 receiver = receiver->parentWidget();
326 } while (receiver);
327}
328
329/*!
330 \internal
331*/
332void QGraphicsProxyWidgetPrivate::removeSubFocusHelper(QWidget *widget, Qt::FocusReason reason)
333{
334 QFocusEvent event(QEvent::FocusOut, reason);
335 QPointer<QWidget> widgetGuard = widget;
336 QCoreApplication::sendEvent(receiver: widget, event: &event);
337 if (widgetGuard && event.isAccepted())
338 QCoreApplication::sendEvent(receiver: widget->style(), event: &event);
339}
340
341/*!
342 \internal
343 Some of the logic is shared with QApplicationPrivate::focusNextPrevChild_helper
344*/
345QWidget *QGraphicsProxyWidgetPrivate::findFocusChild(QWidget *child, bool next) const
346{
347 if (!widget)
348 return nullptr;
349
350 // Run around the focus chain until we find a widget that can take tab focus.
351 if (!child) {
352 child = next ? widget.data() : widget->previousInFocusChain();
353 } else {
354 child = next ? child->nextInFocusChain() : child->previousInFocusChain();
355 if ((next && child == widget) || (!next && child == widget->previousInFocusChain())) {
356 return nullptr;
357 }
358 }
359
360 if (!child)
361 return nullptr;
362
363 QWidget *oldChild = child;
364 uint focus_flag = qt_tab_all_widgets() ? Qt::TabFocus : Qt::StrongFocus;
365 do {
366 if (child->isEnabled()
367 && child->isVisibleTo(widget)
368 && ((child->focusPolicy() & focus_flag) == focus_flag)
369 && !(child->d_func()->extra && child->d_func()->extra->focus_proxy)) {
370 return child;
371 }
372 child = next ? child->nextInFocusChain() : child->previousInFocusChain();
373 } while (child != oldChild && !(next && child == widget) && !(!next && child == widget->previousInFocusChain()));
374 return nullptr;
375}
376
377/*!
378 \internal
379*/
380void QGraphicsProxyWidgetPrivate::_q_removeWidgetSlot()
381{
382 Q_Q(QGraphicsProxyWidget);
383 if (!widget.isNull()) {
384 if (const auto &extra = widget->d_func()->extra)
385 extra->proxyWidget = nullptr;
386 }
387 widget = nullptr;
388 delete q;
389}
390
391/*!
392 \internal
393*/
394void QGraphicsProxyWidgetPrivate::updateWidgetGeometryFromProxy()
395{
396}
397
398/*!
399 \internal
400*/
401void QGraphicsProxyWidgetPrivate::updateProxyGeometryFromWidget()
402{
403 Q_Q(QGraphicsProxyWidget);
404 if (!widget)
405 return;
406
407 QRectF widgetGeometry = widget->geometry();
408 QWidget *parentWidget = widget->parentWidget();
409 if (widget->isWindow()) {
410 QGraphicsProxyWidget *proxyParent = nullptr;
411 if (parentWidget && (proxyParent = qobject_cast<QGraphicsProxyWidget *>(object: q->parentWidget()))) {
412 // Nested window proxy (e.g., combobox popup), map widget to the
413 // parent widget's global coordinates, and map that to the parent
414 // proxy's child coordinates.
415 widgetGeometry.moveTo(p: proxyParent->subWidgetRect(widget: parentWidget).topLeft()
416 + parentWidget->mapFromGlobal(widget->pos()));
417 }
418 }
419
420 // Adjust to size hint if the widget has never been resized.
421 if (!widget->size().isValid())
422 widgetGeometry.setSize(widget->sizeHint());
423
424 // Assign new geometry.
425 posChangeMode = QGraphicsProxyWidgetPrivate::WidgetToProxyMode;
426 sizeChangeMode = QGraphicsProxyWidgetPrivate::WidgetToProxyMode;
427 q->setGeometry(widgetGeometry);
428 posChangeMode = QGraphicsProxyWidgetPrivate::NoMode;
429 sizeChangeMode = QGraphicsProxyWidgetPrivate::NoMode;
430}
431
432/*!
433 \internal
434*/
435void QGraphicsProxyWidgetPrivate::updateProxyInputMethodAcceptanceFromWidget()
436{
437 Q_Q(QGraphicsProxyWidget);
438 if (!widget)
439 return;
440
441 QWidget *focusWidget = widget->focusWidget();
442 if (!focusWidget)
443 focusWidget = widget;
444 q->setFlag(flag: QGraphicsItem::ItemAcceptsInputMethod,
445 enabled: focusWidget->testAttribute(attribute: Qt::WA_InputMethodEnabled));
446}
447
448/*!
449 \internal
450
451 Embeds \a subWin as a subwindow of this proxy widget. \a subWin must be a top-level
452 widget and a descendant of the widget managed by this proxy. A separate subproxy
453 will be created as a child of this proxy widget to manage \a subWin.
454*/
455void QGraphicsProxyWidgetPrivate::embedSubWindow(QWidget *subWin)
456{
457 const auto &extra = subWin->d_func()->extra;
458 if (!extra || !extra->proxyWidget) {
459 QGraphicsProxyWidget *subProxy = new QGraphicsProxyWidget(q_func(), subWin->windowFlags());
460 subProxy->d_func()->setWidget_helper(widget: subWin, autoShow: false);
461 }
462}
463
464/*!
465 \internal
466
467 Removes ("unembeds") \a subWin and deletes the proxy holder item. This can
468 happen when QWidget::setParent() reparents the embedded window out of
469 "embedded space".
470*/
471void QGraphicsProxyWidgetPrivate::unembedSubWindow(QWidget *subWin)
472{
473 for (QGraphicsItem *child : std::as_const(t&: children)) {
474 if (child->isWidget()) {
475 if (QGraphicsProxyWidget *proxy = qobject_cast<QGraphicsProxyWidget *>(object: static_cast<QGraphicsWidget *>(child))) {
476 if (proxy->widget() == subWin) {
477 proxy->setWidget(nullptr);
478 scene->removeItem(item: proxy);
479 delete proxy;
480 return;
481 }
482 }
483 }
484 }
485}
486
487bool QGraphicsProxyWidgetPrivate::isProxyWidget() const
488{
489 return true;
490}
491
492/*!
493 \internal
494*/
495QPointF QGraphicsProxyWidgetPrivate::mapToReceiver(const QPointF &pos, const QWidget *receiver) const
496{
497 QPointF p = pos;
498 // Map event position from us to the receiver, preserving its
499 // precision (don't use QWidget::mapFrom here).
500 while (receiver && receiver != widget) {
501 p -= QPointF(receiver->pos());
502 receiver = receiver->parentWidget();
503 }
504 return p;
505}
506
507/*!
508 Constructs a new QGraphicsProxy widget. \a parent and \a wFlags are passed
509 to QGraphicsItem's constructor.
510*/
511QGraphicsProxyWidget::QGraphicsProxyWidget(QGraphicsItem *parent, Qt::WindowFlags wFlags)
512 : QGraphicsWidget(*new QGraphicsProxyWidgetPrivate, parent, wFlags)
513{
514 Q_D(QGraphicsProxyWidget);
515 d->init();
516}
517
518/*!
519 Destroys the proxy widget and any embedded widget.
520*/
521QGraphicsProxyWidget::~QGraphicsProxyWidget()
522{
523 Q_D(QGraphicsProxyWidget);
524 if (d->widget) {
525 d->widget->removeEventFilter(obj: this);
526 QObject::disconnect(sender: d->widget, SIGNAL(destroyed()), receiver: this, SLOT(_q_removeWidgetSlot()));
527 delete d->widget;
528 }
529}
530
531/*!
532 Embeds \a widget into this proxy widget. The embedded widget must reside
533 exclusively either inside or outside of Graphics View. You cannot embed a
534 widget as long as it is visible elsewhere in the UI, at the same time.
535
536 \a widget must be a top-level widget whose parent is \nullptr.
537
538 When the widget is embedded, its state (e.g., visible, enabled, geometry,
539 size hints) is copied into the proxy widget. If the embedded widget is
540 explicitly hidden or disabled, the proxy widget will become explicitly
541 hidden or disabled after embedding is complete. The class documentation
542 has a full overview over the shared state.
543
544 QGraphicsProxyWidget's window flags determine whether the widget, after
545 embedding, will be given window decorations or not.
546
547 After this function returns, QGraphicsProxyWidget will keep its state
548 synchronized with that of \a widget whenever possible.
549
550 If a widget is already embedded by this proxy when this function is called,
551 that widget will first be automatically unembedded. Passing \nullptr for
552 the \a widget argument will only unembed the widget, and the ownership of
553 the currently embedded widget will be passed on to the caller.
554 Every child widget that are embedded will also be embedded and their proxy
555 widget destroyed.
556
557 Note that widgets with the Qt::WA_PaintOnScreen widget attribute
558 set and widgets that wrap an external application or controller
559 cannot be embedded. Examples are QOpenGLWidget and QAxWidget.
560
561 \sa widget()
562*/
563void QGraphicsProxyWidget::setWidget(QWidget *widget)
564{
565 Q_D(QGraphicsProxyWidget);
566 d->setWidget_helper(widget, autoShow: true);
567}
568
569void QGraphicsProxyWidgetPrivate::setWidget_helper(QWidget *newWidget, bool autoShow)
570{
571 Q_Q(QGraphicsProxyWidget);
572 if (newWidget == widget)
573 return;
574 if (widget) {
575 QObject::disconnect(sender: widget, SIGNAL(destroyed()), receiver: q, SLOT(_q_removeWidgetSlot()));
576 widget->removeEventFilter(obj: q);
577 widget->setAttribute(Qt::WA_DontShowOnScreen, on: false);
578 widget->d_func()->extra->proxyWidget = nullptr;
579 resolveFont(inheritedMask: inheritedFontResolveMask);
580 resolvePalette(inheritedMask: inheritedPaletteResolveMask);
581 widget->update();
582
583 const auto childItems = q->childItems();
584 for (QGraphicsItem *child : childItems) {
585 if (child->d_ptr->isProxyWidget()) {
586 QGraphicsProxyWidget *childProxy = static_cast<QGraphicsProxyWidget *>(child);
587 QWidget *parent = childProxy->widget();
588 while (parent && parent->parentWidget()) {
589 if (parent == widget)
590 break;
591 parent = parent->parentWidget();
592 }
593 if (!childProxy->widget() || parent != widget)
594 continue;
595 childProxy->setWidget(nullptr);
596 delete childProxy;
597 }
598 }
599
600 widget = nullptr;
601#ifndef QT_NO_CURSOR
602 q->unsetCursor();
603#endif
604 q->setAcceptHoverEvents(false);
605 if (!newWidget)
606 q->update();
607 }
608 if (!newWidget)
609 return;
610 if (!newWidget->isWindow()) {
611 const auto &extra = newWidget->parentWidget()->d_func()->extra;
612 if (!extra || !extra->proxyWidget) {
613 qWarning(msg: "QGraphicsProxyWidget::setWidget: cannot embed widget %p "
614 "which is not a toplevel widget, and is not a child of an embedded widget", newWidget);
615 return;
616 }
617 }
618
619 // Register this proxy within the widget's private.
620 // ### This is a bit backdoorish
621 QWExtra *extra = newWidget->d_func()->extra.get();
622 if (!extra) {
623 newWidget->d_func()->createExtra();
624 extra = newWidget->d_func()->extra.get();
625 }
626 QGraphicsProxyWidget **proxyWidget = &extra->proxyWidget;
627 if (*proxyWidget) {
628 if (*proxyWidget != q) {
629 qWarning(msg: "QGraphicsProxyWidget::setWidget: cannot embed widget %p"
630 "; already embedded", newWidget);
631 }
632 return;
633 }
634 *proxyWidget = q;
635
636 newWidget->setAttribute(Qt::WA_DontShowOnScreen);
637 newWidget->ensurePolished();
638 // Do not wait for this widget to close before the app closes ###
639 // shouldn't this widget inherit the attribute?
640 newWidget->setAttribute(Qt::WA_QuitOnClose, on: false);
641 q->setAcceptHoverEvents(true);
642
643 if (newWidget->testAttribute(attribute: Qt::WA_NoSystemBackground))
644 q->setAttribute(attribute: Qt::WA_NoSystemBackground);
645 if (newWidget->testAttribute(attribute: Qt::WA_OpaquePaintEvent))
646 q->setAttribute(attribute: Qt::WA_OpaquePaintEvent);
647
648 widget = newWidget;
649
650 // Changes only go from the widget to the proxy.
651 enabledChangeMode = QGraphicsProxyWidgetPrivate::WidgetToProxyMode;
652 visibleChangeMode = QGraphicsProxyWidgetPrivate::WidgetToProxyMode;
653 posChangeMode = QGraphicsProxyWidgetPrivate::WidgetToProxyMode;
654 sizeChangeMode = QGraphicsProxyWidgetPrivate::WidgetToProxyMode;
655
656 if ((autoShow && !newWidget->testAttribute(attribute: Qt::WA_WState_ExplicitShowHide)) || !newWidget->testAttribute(attribute: Qt::WA_WState_Hidden)) {
657 newWidget->show();
658 }
659
660 // Copy the state from the widget onto the proxy.
661#ifndef QT_NO_CURSOR
662 if (newWidget->testAttribute(attribute: Qt::WA_SetCursor))
663 q->setCursor(widget->cursor());
664#endif
665 q->setEnabled(newWidget->isEnabled());
666 q->setVisible(newWidget->isVisible());
667 q->setLayoutDirection(newWidget->layoutDirection());
668 if (newWidget->testAttribute(attribute: Qt::WA_SetStyle))
669 q->setStyle(widget->style());
670
671 resolveFont(inheritedMask: inheritedFontResolveMask);
672 resolvePalette(inheritedMask: inheritedPaletteResolveMask);
673
674 if (!newWidget->testAttribute(attribute: Qt::WA_Resized))
675 newWidget->adjustSize();
676
677 q->setContentsMargins(newWidget->contentsMargins());
678 q->setWindowTitle(newWidget->windowTitle());
679
680 // size policies and constraints..
681 q->setSizePolicy(newWidget->sizePolicy());
682 QSize sz = newWidget->minimumSize();
683 q->setMinimumSize(sz.isNull() ? QSizeF() : QSizeF(sz));
684 sz = newWidget->maximumSize();
685 q->setMaximumSize(sz.isNull() ? QSizeF() : QSizeF(sz));
686
687 updateProxyGeometryFromWidget();
688
689 updateProxyInputMethodAcceptanceFromWidget();
690
691 // Hook up the event filter to keep the state up to date.
692 newWidget->installEventFilter(filterObj: q);
693 QObject::connect(sender: newWidget, SIGNAL(destroyed()), receiver: q, SLOT(_q_removeWidgetSlot()));
694
695 // Changes no longer go only from the widget to the proxy.
696 enabledChangeMode = QGraphicsProxyWidgetPrivate::NoMode;
697 visibleChangeMode = QGraphicsProxyWidgetPrivate::NoMode;
698 posChangeMode = QGraphicsProxyWidgetPrivate::NoMode;
699 sizeChangeMode = QGraphicsProxyWidgetPrivate::NoMode;
700}
701
702/*!
703 Returns a pointer to the embedded widget.
704
705 \sa setWidget()
706*/
707QWidget *QGraphicsProxyWidget::widget() const
708{
709 Q_D(const QGraphicsProxyWidget);
710 return d->widget;
711}
712
713/*!
714 Returns the rectangle for \a widget, which must be a descendant of
715 widget(), or widget() itself, in this proxy item's local coordinates.
716
717 If no widget is embedded, \a widget is \nullptr, or \a widget is not a
718 descendant of the embedded widget, this function returns an empty QRectF.
719
720 \sa widget()
721*/
722QRectF QGraphicsProxyWidget::subWidgetRect(const QWidget *widget) const
723{
724 Q_D(const QGraphicsProxyWidget);
725 if (!widget || !d->widget)
726 return QRectF();
727 if (d->widget == widget || d->widget->isAncestorOf(child: widget))
728 return QRectF(widget->mapTo(d->widget, QPoint(0, 0)), widget->size());
729 return QRectF();
730}
731
732/*!
733 \reimp
734*/
735void QGraphicsProxyWidget::setGeometry(const QRectF &rect)
736{
737 Q_D(QGraphicsProxyWidget);
738 bool proxyResizesWidget = !d->posChangeMode && !d->sizeChangeMode;
739 if (proxyResizesWidget) {
740 d->posChangeMode = QGraphicsProxyWidgetPrivate::ProxyToWidgetMode;
741 d->sizeChangeMode = QGraphicsProxyWidgetPrivate::ProxyToWidgetMode;
742 }
743 QGraphicsWidget::setGeometry(rect);
744 if (proxyResizesWidget) {
745 d->posChangeMode = QGraphicsProxyWidgetPrivate::NoMode;
746 d->sizeChangeMode = QGraphicsProxyWidgetPrivate::NoMode;
747 }
748}
749
750/*!
751 \reimp
752*/
753QVariant QGraphicsProxyWidget::itemChange(GraphicsItemChange change,
754 const QVariant &value)
755{
756 Q_D(QGraphicsProxyWidget);
757
758 switch (change) {
759 case ItemPositionChange:
760 // The item's position is either changed directly on the proxy, in
761 // which case the position change should propagate to the widget,
762 // otherwise it happens as a side effect when filtering QEvent::Move.
763 if (!d->posChangeMode)
764 d->posChangeMode = QGraphicsProxyWidgetPrivate::ProxyToWidgetMode;
765 break;
766 case ItemPositionHasChanged:
767 // Move the internal widget if we're in widget-to-proxy
768 // mode. Otherwise the widget has already moved.
769 if (d->widget && d->posChangeMode != QGraphicsProxyWidgetPrivate::WidgetToProxyMode)
770 d->widget->move(value.toPoint());
771 if (d->posChangeMode == QGraphicsProxyWidgetPrivate::ProxyToWidgetMode)
772 d->posChangeMode = QGraphicsProxyWidgetPrivate::NoMode;
773 break;
774 case ItemVisibleChange:
775 if (!d->visibleChangeMode)
776 d->visibleChangeMode = QGraphicsProxyWidgetPrivate::ProxyToWidgetMode;
777 break;
778 case ItemVisibleHasChanged:
779 if (d->widget && d->visibleChangeMode != QGraphicsProxyWidgetPrivate::WidgetToProxyMode)
780 d->widget->setVisible(isVisible());
781 if (d->visibleChangeMode == QGraphicsProxyWidgetPrivate::ProxyToWidgetMode)
782 d->visibleChangeMode = QGraphicsProxyWidgetPrivate::NoMode;
783 break;
784 case ItemEnabledChange:
785 if (!d->enabledChangeMode)
786 d->enabledChangeMode = QGraphicsProxyWidgetPrivate::ProxyToWidgetMode;
787 break;
788 case ItemEnabledHasChanged:
789 if (d->widget && d->enabledChangeMode != QGraphicsProxyWidgetPrivate::WidgetToProxyMode)
790 d->widget->setEnabled(isEnabled());
791 if (d->enabledChangeMode == QGraphicsProxyWidgetPrivate::ProxyToWidgetMode)
792 d->enabledChangeMode = QGraphicsProxyWidgetPrivate::NoMode;
793 break;
794 default:
795 break;
796 }
797 return QGraphicsWidget::itemChange(change, value);
798}
799
800/*!
801 \reimp
802*/
803bool QGraphicsProxyWidget::event(QEvent *event)
804{
805 Q_D(QGraphicsProxyWidget);
806 if (!d->widget)
807 return QGraphicsWidget::event(event);
808
809 switch (event->type()) {
810 case QEvent::WindowActivate:
811 case QEvent::WindowDeactivate:
812 QCoreApplication::sendEvent(receiver: d->widget, event);
813 break;
814 case QEvent::StyleChange:
815 // Propagate style changes to the embedded widget.
816 if (!d->styleChangeMode) {
817 d->styleChangeMode = QGraphicsProxyWidgetPrivate::ProxyToWidgetMode;
818 d->widget->setStyle(style());
819 d->styleChangeMode = QGraphicsProxyWidgetPrivate::NoMode;
820 }
821 break;
822 case QEvent::FontChange: {
823 // Propagate to widget.
824 QWidgetPrivate *wd = d->widget->d_func();
825 int mask = d->font.resolveMask() | d->inheritedFontResolveMask;
826 wd->inheritedFontResolveMask = mask;
827 wd->resolveFont();
828 break;
829 }
830 case QEvent::PaletteChange: {
831 // Propagate to widget.
832 QWidgetPrivate *wd = d->widget->d_func();
833 int mask = d->palette.resolveMask() | d->inheritedPaletteResolveMask;
834 wd->inheritedPaletteResolveMask = mask;
835 wd->resolvePalette();
836 break;
837 }
838 case QEvent::InputMethod: {
839 inputMethodEvent(event: static_cast<QInputMethodEvent *>(event));
840 if (event->isAccepted())
841 return true;
842 return false;
843 }
844 case QEvent::ShortcutOverride: {
845 QWidget *focusWidget = d->widget->focusWidget();
846 while (focusWidget) {
847 QCoreApplication::sendEvent(receiver: focusWidget, event);
848 if (event->isAccepted())
849 return true;
850 focusWidget = focusWidget->parentWidget();
851 }
852 return false;
853 }
854 case QEvent::KeyPress: {
855 QKeyEvent *k = static_cast<QKeyEvent *>(event);
856 if (k->key() == Qt::Key_Tab || k->key() == Qt::Key_Backtab) {
857 if (!(k->modifiers() & (Qt::ControlModifier | Qt::AltModifier))) { //### Add MetaModifier?
858 QWidget *focusWidget = d->widget->focusWidget();
859 while (focusWidget) {
860 const bool res = QCoreApplication::sendEvent(receiver: focusWidget, event);
861 if ((res && event->isAccepted()) || (isWindow() && focusWidget == d->widget)) {
862 event->accept();
863 break;
864 }
865 focusWidget = focusWidget->parentWidget();
866 }
867 return true;
868 }
869 }
870 break;
871 }
872#if QT_CONFIG(tooltip)
873 case QEvent::GraphicsSceneHelp: {
874 // Propagate the help event (for tooltip) to the widget under mouse
875 if (d->lastWidgetUnderMouse) {
876 QGraphicsSceneHelpEvent *he = static_cast<QGraphicsSceneHelpEvent *>(event);
877 QPoint pos = d->mapToReceiver(pos: mapFromScene(point: he->scenePos()), receiver: d->lastWidgetUnderMouse).toPoint();
878 QHelpEvent e(QEvent::ToolTip, pos, he->screenPos());
879 QCoreApplication::sendEvent(receiver: d->lastWidgetUnderMouse, event: &e);
880 event->setAccepted(e.isAccepted());
881 return e.isAccepted();
882 }
883 break;
884 }
885 case QEvent::ToolTipChange: {
886 // Propagate tooltip change to the widget
887 if (!d->tooltipChangeMode) {
888 d->tooltipChangeMode = QGraphicsProxyWidgetPrivate::ProxyToWidgetMode;
889 d->widget->setToolTip(toolTip());
890 d->tooltipChangeMode = QGraphicsProxyWidgetPrivate::NoMode;
891 }
892 break;
893 }
894#endif
895 case QEvent::TouchBegin:
896 case QEvent::TouchUpdate:
897 case QEvent::TouchEnd: {
898 QTouchEvent *touchEvent = static_cast<QTouchEvent *>(event);
899 bool res = QApplicationPrivate::translateRawTouchEvent(widget: d->widget, touchEvent);
900 if (res & touchEvent->isAccepted())
901 return true;
902
903 break;
904 }
905 default:
906 break;
907 }
908 return QGraphicsWidget::event(event);
909}
910
911/*!
912 \reimp
913*/
914bool QGraphicsProxyWidget::eventFilter(QObject *object, QEvent *event)
915{
916 Q_D(QGraphicsProxyWidget);
917
918 if (object == d->widget) {
919 switch (event->type()) {
920 case QEvent::LayoutRequest:
921 updateGeometry();
922 break;
923 case QEvent::Resize:
924 // If the widget resizes itself, we resize the proxy too.
925 // Prevent feed-back by checking the geometry change mode.
926 if (!d->sizeChangeMode)
927 d->updateProxyGeometryFromWidget();
928 break;
929 case QEvent::Move:
930 // If the widget moves itself, we move the proxy too. Prevent
931 // feed-back by checking the geometry change mode.
932 if (!d->posChangeMode)
933 d->updateProxyGeometryFromWidget();
934 break;
935 case QEvent::Hide:
936 case QEvent::Show:
937 // If the widget toggles its visible state, the proxy will follow.
938 if (!d->visibleChangeMode) {
939 d->visibleChangeMode = QGraphicsProxyWidgetPrivate::WidgetToProxyMode;
940 setVisible(event->type() == QEvent::Show);
941 d->visibleChangeMode = QGraphicsProxyWidgetPrivate::NoMode;
942 }
943 break;
944 case QEvent::EnabledChange:
945 // If the widget toggles its enabled state, the proxy will follow.
946 if (!d->enabledChangeMode) {
947 d->enabledChangeMode = QGraphicsProxyWidgetPrivate::WidgetToProxyMode;
948 setEnabled(d->widget->isEnabled());
949 d->enabledChangeMode = QGraphicsProxyWidgetPrivate::NoMode;
950 }
951 break;
952 case QEvent::StyleChange:
953 // Propagate style changes to the proxy.
954 if (!d->styleChangeMode) {
955 d->styleChangeMode = QGraphicsProxyWidgetPrivate::WidgetToProxyMode;
956 setStyle(d->widget->style());
957 d->styleChangeMode = QGraphicsProxyWidgetPrivate::NoMode;
958 }
959 break;
960#if QT_CONFIG(tooltip)
961 case QEvent::ToolTipChange:
962 // Propagate tooltip change to the proxy.
963 if (!d->tooltipChangeMode) {
964 d->tooltipChangeMode = QGraphicsProxyWidgetPrivate::WidgetToProxyMode;
965 setToolTip(d->widget->toolTip());
966 d->tooltipChangeMode = QGraphicsProxyWidgetPrivate::NoMode;
967 }
968 break;
969#endif
970 default:
971 break;
972 }
973 }
974 return QGraphicsWidget::eventFilter(watched: object, event);
975}
976
977/*!
978 \reimp
979*/
980void QGraphicsProxyWidget::showEvent(QShowEvent *event)
981{
982 Q_UNUSED(event);
983}
984
985/*!
986 \reimp
987*/
988void QGraphicsProxyWidget::hideEvent(QHideEvent *event)
989{
990 Q_UNUSED(event);
991}
992
993#ifndef QT_NO_CONTEXTMENU
994/*!
995 \reimp
996*/
997void QGraphicsProxyWidget::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
998{
999 Q_D(QGraphicsProxyWidget);
1000 if (!event || !d->widget || !d->widget->isVisible() || !hasFocus())
1001 return;
1002
1003 // Find widget position and receiver.
1004 QPointF pos = event->pos();
1005 QPointer<QWidget> alienWidget = d->widget->childAt(p: pos.toPoint());
1006 QPointer<QWidget> receiver = alienWidget ? alienWidget : d->widget;
1007
1008 // Map event position from us to the receiver
1009 pos = d->mapToReceiver(pos, receiver);
1010
1011 QPoint globalPos = receiver->mapToGlobal(pos.toPoint());
1012 //If the receiver by-pass the proxy its popups
1013 //will be top level QWidgets therefore they need
1014 //the screen position. mapToGlobal expect the widget to
1015 //have proper coordinates in regards of the windowing system
1016 //but it's not true because the widget is embedded.
1017 if (bypassGraphicsProxyWidget(p: receiver))
1018 globalPos = event->screenPos();
1019
1020 // Send mouse event. ### Doesn't propagate the event.
1021 QContextMenuEvent contextMenuEvent(QContextMenuEvent::Reason(event->reason()),
1022 pos.toPoint(), globalPos, event->modifiers());
1023 contextMenuEvent.setTimestamp(event->timestamp());
1024 QCoreApplication::sendEvent(receiver, event: &contextMenuEvent);
1025
1026 event->setAccepted(contextMenuEvent.isAccepted());
1027}
1028#endif // QT_NO_CONTEXTMENU
1029
1030#if QT_CONFIG(draganddrop)
1031/*!
1032 \reimp
1033*/
1034void QGraphicsProxyWidget::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
1035{
1036#if !QT_CONFIG(draganddrop)
1037 Q_UNUSED(event);
1038#else
1039 Q_D(QGraphicsProxyWidget);
1040 if (!d->widget)
1041 return;
1042
1043 QDragEnterEvent proxyDragEnter(event->pos().toPoint(), event->dropAction(), event->mimeData(), event->buttons(), event->modifiers());
1044 proxyDragEnter.setAccepted(event->isAccepted());
1045 QCoreApplication::sendEvent(receiver: d->widget, event: &proxyDragEnter);
1046 event->setAccepted(proxyDragEnter.isAccepted());
1047 if (proxyDragEnter.isAccepted()) // we discard answerRect
1048 event->setDropAction(proxyDragEnter.dropAction());
1049#endif
1050}
1051/*!
1052 \reimp
1053*/
1054void QGraphicsProxyWidget::dragLeaveEvent(QGraphicsSceneDragDropEvent *event)
1055{
1056 Q_UNUSED(event);
1057#if QT_CONFIG(draganddrop)
1058 Q_D(QGraphicsProxyWidget);
1059 if (!d->widget || !d->dragDropWidget)
1060 return;
1061 QDragLeaveEvent proxyDragLeave;
1062 QCoreApplication::sendEvent(receiver: d->dragDropWidget, event: &proxyDragLeave);
1063 d->dragDropWidget = nullptr;
1064#endif
1065}
1066
1067/*!
1068 \reimp
1069*/
1070void QGraphicsProxyWidget::dragMoveEvent(QGraphicsSceneDragDropEvent *event)
1071{
1072#if !QT_CONFIG(draganddrop)
1073 Q_UNUSED(event);
1074#else
1075 Q_D(QGraphicsProxyWidget);
1076 if (!d->widget)
1077 return;
1078 QPointF p = event->pos();
1079 event->ignore();
1080 QPointer<QWidget> subWidget = d->widget->childAt(p: p.toPoint());
1081 QPointer<QWidget> receiver = subWidget ? subWidget : d->widget;
1082 bool eventDelivered = false;
1083 for (; receiver; receiver = receiver->parentWidget()) {
1084 if (!receiver->isEnabled() || !receiver->acceptDrops())
1085 continue;
1086 // Map event position from us to the receiver
1087 QPoint receiverPos = d->mapToReceiver(pos: p, receiver).toPoint();
1088 if (receiver != d->dragDropWidget) {
1089 // Try to enter before we leave
1090 QDragEnterEvent dragEnter(receiverPos, event->possibleActions(), event->mimeData(), event->buttons(), event->modifiers());
1091 dragEnter.setDropAction(event->proposedAction());
1092 QCoreApplication::sendEvent(receiver, event: &dragEnter);
1093 event->setAccepted(dragEnter.isAccepted());
1094 event->setDropAction(dragEnter.dropAction());
1095 if (!event->isAccepted()) {
1096 // propagate to the parent widget
1097 continue;
1098 }
1099
1100 d->lastDropAction = event->dropAction();
1101
1102 if (d->dragDropWidget) {
1103 QDragLeaveEvent dragLeave;
1104 QCoreApplication::sendEvent(receiver: d->dragDropWidget, event: &dragLeave);
1105 }
1106 d->dragDropWidget = receiver;
1107 }
1108
1109 QDragMoveEvent dragMove(receiverPos, event->possibleActions(), event->mimeData(), event->buttons(), event->modifiers());
1110 event->setDropAction(d->lastDropAction);
1111 QCoreApplication::sendEvent(receiver, event: &dragMove);
1112 event->setAccepted(dragMove.isAccepted());
1113 event->setDropAction(dragMove.dropAction());
1114 if (event->isAccepted())
1115 d->lastDropAction = event->dropAction();
1116 eventDelivered = true;
1117 break;
1118 }
1119
1120 if (!eventDelivered) {
1121 if (d->dragDropWidget) {
1122 // Leave the last drag drop item
1123 QDragLeaveEvent dragLeave;
1124 QCoreApplication::sendEvent(receiver: d->dragDropWidget, event: &dragLeave);
1125 d->dragDropWidget = nullptr;
1126 }
1127 // Propagate
1128 event->setDropAction(Qt::IgnoreAction);
1129 }
1130#endif
1131}
1132
1133/*!
1134 \reimp
1135*/
1136void QGraphicsProxyWidget::dropEvent(QGraphicsSceneDragDropEvent *event)
1137{
1138#if !QT_CONFIG(draganddrop)
1139 Q_UNUSED(event);
1140#else
1141 Q_D(QGraphicsProxyWidget);
1142 if (d->widget && d->dragDropWidget) {
1143 QPoint widgetPos = d->mapToReceiver(pos: event->pos(), receiver: d->dragDropWidget).toPoint();
1144 QDropEvent dropEvent(widgetPos, event->possibleActions(), event->mimeData(), event->buttons(), event->modifiers());
1145 QCoreApplication::sendEvent(receiver: d->dragDropWidget, event: &dropEvent);
1146 event->setAccepted(dropEvent.isAccepted());
1147 d->dragDropWidget = nullptr;
1148 }
1149#endif
1150}
1151#endif
1152
1153/*!
1154 \reimp
1155*/
1156void QGraphicsProxyWidget::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
1157{
1158 Q_UNUSED(event);
1159}
1160
1161/*!
1162 \reimp
1163*/
1164void QGraphicsProxyWidget::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
1165{
1166 Q_UNUSED(event);
1167 Q_D(QGraphicsProxyWidget);
1168 // If hoverMove was compressed away, make sure we update properly here.
1169 if (d->lastWidgetUnderMouse) {
1170 QApplicationPrivate::dispatchEnterLeave(enter: nullptr, leave: d->lastWidgetUnderMouse, globalPosF: event->screenPos());
1171 d->lastWidgetUnderMouse = nullptr;
1172 }
1173}
1174
1175/*!
1176 \reimp
1177*/
1178void QGraphicsProxyWidget::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
1179{
1180 Q_D(QGraphicsProxyWidget);
1181#ifdef GRAPHICSPROXYWIDGET_DEBUG
1182 qDebug("QGraphicsProxyWidget::hoverMoveEvent");
1183#endif
1184 // Ignore events on the window frame.
1185 if (!d->widget || !rect().contains(p: event->pos())) {
1186 if (d->lastWidgetUnderMouse) {
1187 QApplicationPrivate::dispatchEnterLeave(enter: nullptr, leave: d->lastWidgetUnderMouse, globalPosF: event->screenPos());
1188 d->lastWidgetUnderMouse = nullptr;
1189 }
1190 return;
1191 }
1192
1193 d->embeddedMouseGrabber = nullptr;
1194 d->sendWidgetMouseEvent(event);
1195}
1196
1197/*!
1198 \reimp
1199*/
1200void QGraphicsProxyWidget::grabMouseEvent(QEvent *event)
1201{
1202 Q_UNUSED(event);
1203}
1204
1205/*!
1206 \reimp
1207*/
1208void QGraphicsProxyWidget::ungrabMouseEvent(QEvent *event)
1209{
1210 Q_D(QGraphicsProxyWidget);
1211 Q_UNUSED(event);
1212 d->embeddedMouseGrabber = nullptr;
1213}
1214
1215/*!
1216 \reimp
1217*/
1218void QGraphicsProxyWidget::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
1219{
1220 Q_D(QGraphicsProxyWidget);
1221#ifdef GRAPHICSPROXYWIDGET_DEBUG
1222 qDebug("QGraphicsProxyWidget::mouseMoveEvent");
1223#endif
1224 d->sendWidgetMouseEvent(event);
1225}
1226
1227/*!
1228 \reimp
1229*/
1230void QGraphicsProxyWidget::mousePressEvent(QGraphicsSceneMouseEvent *event)
1231{
1232 Q_D(QGraphicsProxyWidget);
1233#ifdef GRAPHICSPROXYWIDGET_DEBUG
1234 qDebug("QGraphicsProxyWidget::mousePressEvent");
1235#endif
1236 d->sendWidgetMouseEvent(event);
1237}
1238
1239/*!
1240 \reimp
1241*/
1242void QGraphicsProxyWidget::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
1243{
1244 Q_D(QGraphicsProxyWidget);
1245#ifdef GRAPHICSPROXYWIDGET_DEBUG
1246 qDebug("QGraphicsProxyWidget::mouseDoubleClickEvent");
1247#endif
1248 d->sendWidgetMouseEvent(event);
1249}
1250
1251/*!
1252 \reimp
1253*/
1254#if QT_CONFIG(wheelevent)
1255void QGraphicsProxyWidget::wheelEvent(QGraphicsSceneWheelEvent *event)
1256{
1257 Q_D(QGraphicsProxyWidget);
1258#ifdef GRAPHICSPROXYWIDGET_DEBUG
1259 qDebug("QGraphicsProxyWidget::wheelEvent");
1260#endif
1261 if (!d->widget)
1262 return;
1263
1264 QPointF pos = event->pos();
1265 QPointer<QWidget> receiver = d->widget->childAt(p: pos.toPoint());
1266 if (!receiver)
1267 receiver = d->widget;
1268
1269 // high precision event streams go to the grabber, which will be the
1270 // QGraphicsView's viewport. We need to change that temporarily, otherwise
1271 // the event we send to the receiver get grabbed by the viewport, resulting
1272 // in infinite recursion
1273 QPointer<QWidget> prev_grabber = QApplicationPrivate::wheel_widget;
1274 if (event->phase() == Qt::ScrollBegin) {
1275 QApplicationPrivate::wheel_widget = receiver;
1276 } else if (event->phase() != Qt::NoScrollPhase && QApplicationPrivate::wheel_widget != receiver) {
1277 // this event is part of a stream that didn't start here, so ignore
1278 event->ignore();
1279 return;
1280 }
1281
1282 // Map event position from us to the receiver
1283 pos = d->mapToReceiver(pos, receiver);
1284
1285 // Send mouse event.
1286 QPoint angleDelta;
1287 if (event->orientation() == Qt::Horizontal)
1288 angleDelta.setX(event->delta());
1289 else
1290 angleDelta.setY(event->delta());
1291 // pixelDelta, inverted, scrollPhase and source from the original QWheelEvent
1292 // were not preserved in the QGraphicsSceneWheelEvent unfortunately
1293 QWheelEvent wheelEvent(pos, event->screenPos(), event->pixelDelta(), angleDelta,
1294 event->buttons(), event->modifiers(), event->phase(),
1295 event->isInverted(), Qt::MouseEventSynthesizedByQt,
1296 QPointingDevice::primaryPointingDevice());
1297 QPointer<QWidget> focusWidget = d->widget->focusWidget();
1298 extern bool qt_sendSpontaneousEvent(QObject *, QEvent *);
1299 qt_sendSpontaneousEvent(receiver, &wheelEvent);
1300 event->setAccepted(wheelEvent.isAccepted());
1301
1302 if (event->phase() == Qt::ScrollBegin) {
1303 // reset the wheel grabber if the event wasn't accepted
1304 if (!wheelEvent.isAccepted())
1305 QApplicationPrivate::wheel_widget = prev_grabber;
1306 }
1307
1308 // ### Remove, this should be done by proper focusIn/focusOut events.
1309 if (focusWidget && !focusWidget->hasFocus()) {
1310 focusWidget->update();
1311 focusWidget = d->widget->focusWidget();
1312 if (focusWidget && focusWidget->hasFocus())
1313 focusWidget->update();
1314 }
1315}
1316#endif
1317
1318/*!
1319 \reimp
1320*/
1321void QGraphicsProxyWidget::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
1322{
1323 Q_D(QGraphicsProxyWidget);
1324#ifdef GRAPHICSPROXYWIDGET_DEBUG
1325 qDebug("QGraphicsProxyWidget::mouseReleaseEvent");
1326#endif
1327 d->sendWidgetMouseEvent(event);
1328}
1329
1330/*!
1331 \reimp
1332*/
1333void QGraphicsProxyWidget::keyPressEvent(QKeyEvent *event)
1334{
1335 Q_D(QGraphicsProxyWidget);
1336#ifdef GRAPHICSPROXYWIDGET_DEBUG
1337 qDebug("QGraphicsProxyWidget::keyPressEvent");
1338#endif
1339 d->sendWidgetKeyEvent(event);
1340}
1341
1342/*!
1343 \reimp
1344*/
1345void QGraphicsProxyWidget::keyReleaseEvent(QKeyEvent *event)
1346{
1347 Q_D(QGraphicsProxyWidget);
1348#ifdef GRAPHICSPROXYWIDGET_DEBUG
1349 qDebug("QGraphicsProxyWidget::keyReleaseEvent");
1350#endif
1351 d->sendWidgetKeyEvent(event);
1352}
1353
1354/*!
1355 \reimp
1356*/
1357void QGraphicsProxyWidget::focusInEvent(QFocusEvent *event)
1358{
1359#ifdef GRAPHICSPROXYWIDGET_DEBUG
1360 qDebug("QGraphicsProxyWidget::focusInEvent");
1361#endif
1362 Q_D(QGraphicsProxyWidget);
1363
1364 if (d->focusFromWidgetToProxy) {
1365 // Prevent recursion when the proxy autogains focus through the
1366 // embedded widget calling setFocus(). ### Could be done with event
1367 // filter on FocusIn instead?
1368 return;
1369 }
1370
1371 d->proxyIsGivingFocus = true;
1372
1373 switch (event->reason()) {
1374 case Qt::TabFocusReason: {
1375 if (QWidget *focusChild = d->findFocusChild(child: nullptr, next: true))
1376 focusChild->setFocus(event->reason());
1377 break;
1378 }
1379 case Qt::BacktabFocusReason:
1380 if (QWidget *focusChild = d->findFocusChild(child: nullptr, next: false))
1381 focusChild->setFocus(event->reason());
1382 break;
1383 default:
1384 if (d->widget && d->widget->focusWidget()) {
1385 d->widget->focusWidget()->setFocus(event->reason());
1386 }
1387 break;
1388 }
1389
1390 // QTBUG-88016
1391 if (d->widget && d->widget->focusWidget()
1392 && d->widget->focusWidget()->testAttribute(attribute: Qt::WA_InputMethodEnabled))
1393 QApplication::inputMethod()->reset();
1394
1395 d->proxyIsGivingFocus = false;
1396}
1397
1398/*!
1399 \reimp
1400*/
1401void QGraphicsProxyWidget::focusOutEvent(QFocusEvent *event)
1402{
1403#ifdef GRAPHICSPROXYWIDGET_DEBUG
1404 qDebug("QGraphicsProxyWidget::focusOutEvent");
1405#endif
1406 Q_D(QGraphicsProxyWidget);
1407 if (d->widget) {
1408 // We need to explicitly remove subfocus from the embedded widget's
1409 // focus widget.
1410 if (QWidget *focusWidget = d->widget->focusWidget()) {
1411 // QTBUG-88016 proxyWidget set QTextEdit(QLineEdit etc.) when input preview text,
1412 // inputMethod should be reset when proxyWidget lost focus
1413 if (focusWidget->testAttribute(attribute: Qt::WA_InputMethodEnabled))
1414 QApplication::inputMethod()->reset();
1415
1416 d->removeSubFocusHelper(widget: focusWidget, reason: event->reason());
1417 }
1418 }
1419}
1420
1421/*!
1422 \reimp
1423*/
1424bool QGraphicsProxyWidget::focusNextPrevChild(bool next)
1425{
1426 Q_D(QGraphicsProxyWidget);
1427 if (!d->widget || !d->scene)
1428 return QGraphicsWidget::focusNextPrevChild(next);
1429
1430 Qt::FocusReason reason = next ? Qt::TabFocusReason : Qt::BacktabFocusReason;
1431 QWidget *lastFocusChild = d->widget->focusWidget();
1432 if (QWidget *newFocusChild = d->findFocusChild(child: lastFocusChild, next)) {
1433 newFocusChild->setFocus(reason);
1434 return true;
1435 }
1436
1437 return QGraphicsWidget::focusNextPrevChild(next);
1438}
1439
1440/*!
1441 \reimp
1442*/
1443QVariant QGraphicsProxyWidget::inputMethodQuery(Qt::InputMethodQuery query) const
1444{
1445 Q_D(const QGraphicsProxyWidget);
1446
1447 if (!d->widget || !hasFocus())
1448 return QVariant();
1449
1450 QWidget *focusWidget = widget()->focusWidget();
1451 if (!focusWidget)
1452 focusWidget = d->widget;
1453 QVariant v = focusWidget->inputMethodQuery(query);
1454 QPointF focusWidgetPos = subWidgetRect(widget: focusWidget).topLeft();
1455 switch (v.userType()) {
1456 case QMetaType::QRectF:
1457 v = v.toRectF().translated(p: focusWidgetPos);
1458 break;
1459 case QMetaType::QPointF:
1460 v = v.toPointF() + focusWidgetPos;
1461 break;
1462 case QMetaType::QRect:
1463 v = v.toRect().translated(p: focusWidgetPos.toPoint());
1464 break;
1465 case QMetaType::QPoint:
1466 v = v.toPoint() + focusWidgetPos.toPoint();
1467 break;
1468 default:
1469 break;
1470 }
1471 return v;
1472}
1473
1474/*!
1475 \reimp
1476*/
1477void QGraphicsProxyWidget::inputMethodEvent(QInputMethodEvent *event)
1478{
1479 // Forward input method events if the focus widget enables input methods.
1480 Q_D(const QGraphicsProxyWidget);
1481 QWidget *focusWidget = d->widget->focusWidget();
1482 if (focusWidget && focusWidget->testAttribute(attribute: Qt::WA_InputMethodEnabled))
1483 QCoreApplication::sendEvent(receiver: focusWidget, event);
1484}
1485
1486/*!
1487 \reimp
1488*/
1489QSizeF QGraphicsProxyWidget::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
1490{
1491 Q_D(const QGraphicsProxyWidget);
1492 if (!d->widget)
1493 return QGraphicsWidget::sizeHint(which, constraint);
1494
1495 QSizeF sh;
1496 switch (which) {
1497 case Qt::PreferredSize:
1498 if (QLayout *l = d->widget->layout())
1499 sh = l->sizeHint();
1500 else
1501 sh = d->widget->sizeHint();
1502 break;
1503 case Qt::MinimumSize:
1504 if (QLayout *l = d->widget->layout())
1505 sh = l->minimumSize();
1506 else
1507 sh = d->widget->minimumSizeHint();
1508 break;
1509 case Qt::MaximumSize:
1510 if (QLayout *l = d->widget->layout())
1511 sh = l->maximumSize();
1512 else
1513 sh = QSizeF(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
1514 break;
1515 case Qt::MinimumDescent:
1516 sh = constraint;
1517 break;
1518 default:
1519 break;
1520 }
1521 return sh;
1522}
1523
1524/*!
1525 \reimp
1526*/
1527void QGraphicsProxyWidget::resizeEvent(QGraphicsSceneResizeEvent *event)
1528{
1529 Q_D(QGraphicsProxyWidget);
1530 if (d->widget) {
1531 if (d->sizeChangeMode != QGraphicsProxyWidgetPrivate::WidgetToProxyMode)
1532 d->widget->resize(event->newSize().toSize());
1533 }
1534 QGraphicsWidget::resizeEvent(event);
1535}
1536
1537/*!
1538 \reimp
1539*/
1540void QGraphicsProxyWidget::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
1541{
1542 Q_D(QGraphicsProxyWidget);
1543 Q_UNUSED(widget);
1544 if (!d->widget || !d->widget->isVisible())
1545 return;
1546
1547 // Filter out repaints on the window frame.
1548 const QRect exposedWidgetRect = (option->exposedRect & rect()).toAlignedRect();
1549 if (exposedWidgetRect.isEmpty())
1550 return;
1551
1552 // When rendering to pdf etc. painting may go outside widget boundaries unless clipped
1553 if (painter->device()->devType() != QInternal::Widget && (flags() & ItemClipsChildrenToShape))
1554 painter->setClipRect(d->widget->geometry(), op: Qt::IntersectClip);
1555
1556 d->widget->render(painter, targetOffset: exposedWidgetRect.topLeft(), sourceRegion: exposedWidgetRect);
1557}
1558
1559/*!
1560 \enum QGraphicsProxyWidget::anonymous
1561
1562 The value returned by the virtual type() function.
1563
1564 \value Type A graphics proxy widget
1565*/
1566
1567/*!
1568 \reimp
1569*/
1570int QGraphicsProxyWidget::type() const
1571{
1572 return Type;
1573}
1574
1575/*!
1576 \since 4.5
1577
1578 Creates a proxy widget for the given \a child of the widget
1579 contained in this proxy.
1580
1581 This function makes it possible to acquire proxies for
1582 non top-level widgets. For instance, you can embed a dialog,
1583 and then transform only one of its widgets.
1584
1585 If the widget is already embedded, return the existing proxy widget.
1586
1587 \sa newProxyWidget(), QGraphicsScene::addWidget()
1588*/
1589QGraphicsProxyWidget *QGraphicsProxyWidget::createProxyForChildWidget(QWidget *child)
1590{
1591 QGraphicsProxyWidget *proxy = child->graphicsProxyWidget();
1592 if (proxy)
1593 return proxy;
1594 if (!child->parentWidget()) {
1595 qWarning(msg: "QGraphicsProxyWidget::createProxyForChildWidget: top-level widget not in a QGraphicsScene");
1596 return nullptr;
1597 }
1598
1599 QGraphicsProxyWidget *parentProxy = createProxyForChildWidget(child: child->parentWidget());
1600 if (!parentProxy)
1601 return nullptr;
1602
1603 if (!QMetaObject::invokeMethod(obj: parentProxy, member: "newProxyWidget", c: Qt::DirectConnection,
1604 Q_RETURN_ARG(QGraphicsProxyWidget*, proxy), Q_ARG(const QWidget*, child)))
1605 return nullptr;
1606 proxy->setParent(parentProxy);
1607 proxy->setWidget(child);
1608 return proxy;
1609}
1610
1611/*!
1612 \fn QGraphicsProxyWidget *QGraphicsProxyWidget::newProxyWidget(const QWidget *child)
1613 \since 4.5
1614
1615 Creates a proxy widget for the given \a child of the widget contained in this
1616 proxy.
1617
1618 You should not call this function directly; use
1619 QGraphicsProxyWidget::createProxyForChildWidget() instead.
1620
1621 This function is a fake virtual slot that you can reimplement in
1622 your subclass in order to control how new proxy widgets are
1623 created. The default implementation returns a proxy created with
1624 the QGraphicsProxyWidget() constructor with this proxy widget as
1625 the parent.
1626
1627 \sa createProxyForChildWidget()
1628*/
1629QGraphicsProxyWidget *QGraphicsProxyWidget::newProxyWidget(const QWidget *)
1630{
1631 return new QGraphicsProxyWidget(this);
1632}
1633
1634
1635
1636QT_END_NAMESPACE
1637
1638#include "moc_qgraphicsproxywidget.cpp"
1639

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

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