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