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 | |
27 | QT_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 | |
157 | extern bool qt_sendSpontaneousEvent(QObject *, QEvent *); |
158 | Q_WIDGETS_EXPORT extern bool qt_tab_all_widgets(); |
159 | |
160 | /*! |
161 | \internal |
162 | */ |
163 | QGraphicsProxyWidgetPrivate::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 | */ |
181 | QGraphicsProxyWidgetPrivate::~QGraphicsProxyWidgetPrivate() |
182 | { |
183 | } |
184 | |
185 | /*! |
186 | \internal |
187 | */ |
188 | void QGraphicsProxyWidgetPrivate::init() |
189 | { |
190 | Q_Q(QGraphicsProxyWidget); |
191 | q->setFocusPolicy(Qt::WheelFocus); |
192 | q->setAcceptDrops(true); |
193 | } |
194 | |
195 | /*! |
196 | \internal |
197 | */ |
198 | void 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 | */ |
214 | void 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 | |
301 | void 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 | */ |
323 | void 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 | */ |
336 | QWidget *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 | */ |
371 | void QGraphicsProxyWidgetPrivate::_q_removeWidgetSlot() |
372 | { |
373 | Q_Q(QGraphicsProxyWidget); |
374 | if (!widget.isNull()) { |
375 | if (const auto & = widget->d_func()->extra) |
376 | extra->proxyWidget = nullptr; |
377 | } |
378 | widget = nullptr; |
379 | delete q; |
380 | } |
381 | |
382 | /*! |
383 | \internal |
384 | */ |
385 | void QGraphicsProxyWidgetPrivate::updateWidgetGeometryFromProxy() |
386 | { |
387 | } |
388 | |
389 | /*! |
390 | \internal |
391 | */ |
392 | void 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 | */ |
426 | void 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 | */ |
446 | void QGraphicsProxyWidgetPrivate::embedSubWindow(QWidget *subWin) |
447 | { |
448 | const auto & = 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 | */ |
462 | void 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 | |
478 | bool QGraphicsProxyWidgetPrivate::isProxyWidget() const |
479 | { |
480 | return true; |
481 | } |
482 | |
483 | /*! |
484 | \internal |
485 | */ |
486 | QPointF 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 | */ |
502 | QGraphicsProxyWidget::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 | */ |
512 | QGraphicsProxyWidget::~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 | */ |
554 | void QGraphicsProxyWidget::setWidget(QWidget *widget) |
555 | { |
556 | Q_D(QGraphicsProxyWidget); |
557 | d->setWidget_helper(widget, autoShow: true); |
558 | } |
559 | |
560 | void 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 & = 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 * = 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 | */ |
698 | QWidget *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 | */ |
713 | QRectF 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 | */ |
726 | void 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 | */ |
744 | QVariant 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 | */ |
794 | bool 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 | */ |
905 | bool 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 | */ |
971 | void QGraphicsProxyWidget::showEvent(QShowEvent *event) |
972 | { |
973 | Q_UNUSED(event); |
974 | } |
975 | |
976 | /*! |
977 | \reimp |
978 | */ |
979 | void QGraphicsProxyWidget::hideEvent(QHideEvent *event) |
980 | { |
981 | Q_UNUSED(event); |
982 | } |
983 | |
984 | #ifndef QT_NO_CONTEXTMENU |
985 | /*! |
986 | \reimp |
987 | */ |
988 | void QGraphicsProxyWidget::(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 (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 | */ |
1025 | void 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 | */ |
1045 | void 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 | */ |
1061 | void 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 | */ |
1127 | void 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 | */ |
1147 | void QGraphicsProxyWidget::hoverEnterEvent(QGraphicsSceneHoverEvent *event) |
1148 | { |
1149 | Q_UNUSED(event); |
1150 | } |
1151 | |
1152 | /*! |
1153 | \reimp |
1154 | */ |
1155 | void 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 | */ |
1169 | void 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 | */ |
1191 | void QGraphicsProxyWidget::grabMouseEvent(QEvent *event) |
1192 | { |
1193 | Q_UNUSED(event); |
1194 | } |
1195 | |
1196 | /*! |
1197 | \reimp |
1198 | */ |
1199 | void QGraphicsProxyWidget::ungrabMouseEvent(QEvent *event) |
1200 | { |
1201 | Q_D(QGraphicsProxyWidget); |
1202 | Q_UNUSED(event); |
1203 | d->embeddedMouseGrabber = nullptr; |
1204 | } |
1205 | |
1206 | /*! |
1207 | \reimp |
1208 | */ |
1209 | void 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 | */ |
1221 | void 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 | */ |
1233 | void 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) |
1246 | void 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 | */ |
1312 | void 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 | */ |
1324 | void 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 | */ |
1336 | void 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 | */ |
1348 | void 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 | */ |
1392 | void 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 | */ |
1415 | bool 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 | */ |
1434 | QVariant 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 | */ |
1468 | void 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 | */ |
1480 | QSizeF 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 | */ |
1518 | void 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 | */ |
1531 | void 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 | */ |
1561 | int 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 | */ |
1580 | QGraphicsProxyWidget *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 | */ |
1620 | QGraphicsProxyWidget *QGraphicsProxyWidget::newProxyWidget(const QWidget *) |
1621 | { |
1622 | return new QGraphicsProxyWidget(this); |
1623 | } |
1624 | |
1625 | |
1626 | |
1627 | QT_END_NAMESPACE |
1628 | |
1629 | #include "moc_qgraphicsproxywidget.cpp" |
1630 | |