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