QtBase  v6.3.1
tst_qgraphicsproxywidget.cpp
Go to the documentation of this file.
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 test suite of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:GPL-EXCEPT$
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 General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21 ** included in the packaging of this file. Please review the following
22 ** information to ensure the GNU General Public License requirements will
23 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24 **
25 ** $QT_END_LICENSE$
26 **
27 ****************************************************************************/
28 
29 
30 #include <QTest>
31 #include <QtGui>
32 #include <QtWidgets>
33 #include <QSignalSpy>
34 #include <private/qgraphicsproxywidget_p.h>
35 #include <private/qlayoutengine_p.h> // qSmartMin functions...
36 
37 Q_LOGGING_CATEGORY(lcTests, "qt.widgets.tests")
38 
39 /*
40  Notes:
41 
42  1) The proxy and the widget geometries are linked.
43  proxy resize => widget resize => stop (no livelock)
44  widget resize => proxy resize => stop (no livelock)
45 
46  2) As far as possible, the properties are linked.
47  proxy enable => widget enable => stop
48  widget disabled => proxy disabled => stop
49 
50  3) Windowed state is linked
51  Windowed proxy state => windowed widget state => stop
52  Windowed widget state => windowed proxy state => stop
53 */
54 
55 class EventSpy : public QObject
56 {
57 public:
58  EventSpy(QObject *receiver)
59  {
60  receiver->installEventFilter(this);
61  }
62 
64 
65 protected:
66  bool eventFilter(QObject *, QEvent *event) override
67  {
68  ++counts[event->type()];
69  return false;
70  }
71 };
72 
74 {
75  Q_OBJECT
76 
77 private slots:
78  void initTestCase();
79  void cleanup();
80  void qgraphicsproxywidget();
81  void paint();
82  void paint_2();
83  void setWidget_data();
84  void setWidget();
85  void testEventFilter_data();
86  void testEventFilter();
87  void focusInEvent_data();
88  void focusInEvent();
89  void focusInEventNoWidget();
90  void focusNextPrevChild_data();
91  void focusNextPrevChild();
92  void focusOutEvent_data();
93  void focusOutEvent();
94  void focusProxy_QTBUG_51856();
95  void hoverEnterLeaveEvent_data();
96  void hoverEnterLeaveEvent();
97  void keyPressEvent_data();
98  void keyPressEvent();
99  void keyReleaseEvent_data();
100  void keyReleaseEvent();
101  void mouseDoubleClickEvent();
102  void mousePressReleaseEvent_data();
103  void mousePressReleaseEvent();
104  void resizeEvent_data();
105  void resizeEvent();
106  void paintEvent();
107 #if QT_CONFIG(wheelevent)
108  void wheelEvent();
109 #endif
110  void sizePolicy();
111  void minimumSize();
112  void maximumSize();
113  void scrollUpdate();
114  void setWidget_simple();
115  void setWidget_ownership();
116  void resize_simple_data();
117  void resize_simple();
118  void symmetricMove();
119  void symmetricResize();
120  void symmetricEnabled();
121  void symmetricVisible();
122  void tabFocus_simpleWidget();
123  void tabFocus_simpleTwoWidgets();
124  void tabFocus_complexWidget();
125  void tabFocus_complexTwoWidgets();
126  void setFocus_simpleWidget();
127  void setFocus_simpleTwoWidgets();
128  void setFocus_complexTwoWidgets();
129  void popup_basic();
130  void popup_subwidget();
131  void changingCursor_basic();
132  void tooltip_basic();
133  void childPos_data();
134  void childPos();
135  void autoShow();
136  void windowOpacity();
137  void stylePropagation();
138  void palettePropagation();
139  void fontPropagation();
140  void dontCrashWhenDie();
141  void dontCrashNoParent();
142  void createProxyForChildWidget();
143 #ifndef QT_NO_CONTEXTMENU
144  void actionsContextMenu();
145 #endif // QT_NO_CONTEXTMENU
146  void actionsContextMenu_data();
147  void deleteProxyForChildWidget();
148  void bypassGraphicsProxyWidget_data();
149  void bypassGraphicsProxyWidget();
150  void dragDrop();
151  void windowFlags_data();
152  void windowFlags();
153  void comboboxWindowFlags();
154  void updateAndDelete();
155  void inputMethod();
156  void clickFocus();
157  void windowFrameMargins();
158  void QTBUG_6986_sendMouseEventToAlienWidget();
159  void mapToGlobal();
160  void mapToGlobalWithoutScene();
161  void QTBUG_43780_visibility();
162 #if QT_CONFIG(wheelevent)
163  void wheelEventPropagation();
164 #endif
165  void forwardTouchEvent();
166  void touchEventPropagation();
167 };
168 
169 // Subclass that exposes the protected functions.
171 {
173 public:
175 
176  void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override {
177  paintCount++;
179  }
180 
182  {
183  focusOut++;
185  }
186 
187  bool eventFilter(QObject *object, QEvent *event) override {
188  if (event->type() == QEvent::KeyPress && object == widget())
189  keyPress++;
191  }
192  int paintCount = 0;
193  int keyPress = 0;
194  int focusOut = 0;
195 };
196 
197 #if QT_CONFIG(wheelevent)
198 class WheelWidget : public QWidget
199 {
200 public:
201  WheelWidget() : wheelEventCalled(false) { setFocusPolicy(Qt::WheelFocus); }
202 
203  virtual void wheelEvent(QWheelEvent *event) override { event->accept(); wheelEventCalled = true; }
204 
205  bool wheelEventCalled;
206 };
207 #endif // QT_CONFIG(wheelevent)
208 
209 // This will be called before the first test function is executed.
210 // It is only called once.
211 void tst_QGraphicsProxyWidget::initTestCase()
212 {
213  // Disable menu animations to prevent the alpha widget from getting in the way
214  // in actionsContextMenu().
216  // Disable combo for QTBUG_43780_visibility()/Windows Vista.
219 }
220 
221 // This will be called after every test function.
222 void tst_QGraphicsProxyWidget::cleanup()
223 {
225 }
226 
227 void tst_QGraphicsProxyWidget::qgraphicsproxywidget()
228 {
230  proxy.paint(0, 0, 0);
231  proxy.setWidget(0);
233  QVERIFY(!proxy.widget());
235  proxy.eventFilter(0, &event);
236  QFocusEvent focusEvent(QEvent::FocusIn);
237  focusEvent.ignore();
238  proxy.focusInEvent(&focusEvent);
239  QCOMPARE(focusEvent.isAccepted(), false);
240  QCOMPARE(proxy.focusNextPrevChild(false), false);
241  QCOMPARE(proxy.focusNextPrevChild(true), false);
242  proxy.focusOutEvent(&focusEvent);
243  QHideEvent hideEvent;
244  proxy.hideEvent(&hideEvent);
245  QGraphicsSceneHoverEvent hoverEvent;
246  proxy.hoverEnterEvent(&hoverEvent);
247  proxy.hoverLeaveEvent(&hoverEvent);
248  proxy.hoverMoveEvent(&hoverEvent);
250  proxy.keyPressEvent(&keyEvent);
251  proxy.keyReleaseEvent(&keyEvent);
252  QGraphicsSceneMouseEvent mouseEvent;
253  proxy.mouseDoubleClickEvent(&mouseEvent);
254  proxy.mouseMoveEvent(&mouseEvent);
255  proxy.mousePressEvent(&mouseEvent);
256  proxy.mouseReleaseEvent(&mouseEvent);
257  QGraphicsSceneResizeEvent resizeEvent;
258  proxy.resizeEvent(&resizeEvent);
259  QShowEvent showEvent;
260  proxy.showEvent(&showEvent);
261  proxy.sizeHint(Qt::PreferredSize, QSizeF());
262 }
263 
264 // public void paint(QPainter* painter, QStyleOptionGraphicsItem const* option, QWidget* widget)
265 void tst_QGraphicsProxyWidget::paint()
266 {
268 
269  proxy.paint(0, 0, 0);
270 }
271 
272 class MyProxyWidget : public QGraphicsProxyWidget
273 {
274 public:
276  {
277  // Make sure QGraphicsProxyWidget::paint does not modify the render hints set on the painter.
280  const QPainter::RenderHints oldRenderHints = painter->renderHints();
282  QCOMPARE(painter->renderHints(), oldRenderHints);
283  }
284 };
285 
286 void tst_QGraphicsProxyWidget::paint_2()
287 {
288  MyProxyWidget *proxyWidget = new MyProxyWidget;
289  proxyWidget->setWidget(new QLineEdit);
290 
292  scene.addItem(proxyWidget);
294 
295  // Trigger repaint.
298  scene.render(&painter);
299 }
300 
301 void tst_QGraphicsProxyWidget::setWidget_data()
302 {
303  QTest::addColumn<bool>("widgetExists");
304  QTest::addColumn<bool>("insertWidget");
305  QTest::addColumn<bool>("hasParent");
306  QTest::addColumn<bool>("proxyHasParent");
307 
308  QTest::newRow("setWidget(0)") << false << false << false << false;
309  QTest::newRow("setWidget(widget)") << false << true << false << false;
310  QTest::newRow("setWidget(widgetWParent)") << false << true << true << false;
311  QTest::newRow("setWidget(1), setWidget(0)") << true << false << false << false;
312  QTest::newRow("setWidget(1), setWidget(widget)") << true << true << false << false;
313  QTest::newRow("setWidget(1), setWidget(widgetWParent)") << true << true << true << false;
314  QTest::newRow("p setWidget(0)") << false << false << false << true;
315  QTest::newRow("p setWidget(widget)") << false << true << false << true;
316  QTest::newRow("p setWidget(widgetWParent)") << false << true << true << true;
317  QTest::newRow("p setWidget(1), setWidget(0)") << true << false << false << true;
318  QTest::newRow("p setWidget(1), setWidget(widget)") << true << true << false << true;
319  QTest::newRow("p setWidget(1), setWidget(widgetWParent)") << true << true << true << true;
320 }
321 
322 // public void setWidget(QWidget* widget)
323 void tst_QGraphicsProxyWidget::setWidget()
324 {
325  QFETCH(bool, widgetExists);
326  QFETCH(bool, insertWidget);
327  QFETCH(bool, hasParent);
328  QFETCH(bool, proxyHasParent);
329 
332  view.show();
334  if (style.isNull())
335  QSKIP("This test requires the Fusion style");
338  SubQGraphicsProxyWidget parentProxy;
340  scene.addItem(&parentProxy);
341  if (proxyHasParent)
342  proxy->setParent(&parentProxy);
343  QPointer<QWidget> existingSubWidget = new QWidget;
344  proxy->setVisible(false);
345  proxy->setEnabled(false);
346 
347  if (widgetExists) {
348  existingSubWidget->setAttribute(Qt::WA_QuitOnClose, true);
349  proxy->setWidget(existingSubWidget);
350  }
351 
352  QWidget *widget = new QWidget;
353 #ifndef QT_NO_CURSOR
355 #endif
358  widget->setStyle(style.data());
359  widget->setFont(QFont("Times"));
360  widget->setVisible(true);
363  widget->setEnabled(true);
364  widget->resize(325, 241);
365  widget->setMinimumSize(100, 200);
366  widget->setMaximumSize(1000, 2000);
368  widget->setContentsMargins(10, 29, 19, 81);
370  widget->setAcceptDrops(true);
373 
374  QWidget parentWidget;
375  if (hasParent)
376  widget->setParent(&parentWidget);
377 
378  QWidget *subWidget = insertWidget ? widget : 0;
379  bool shouldBeInsertable = !hasParent && subWidget;
380  if (shouldBeInsertable)
381  subWidget->setAttribute(Qt::WA_QuitOnClose, true);
382 
383  if (hasParent) {
385  "QGraphicsProxyWidget::setWidget: cannot embed widget .* which is not a toplevel widget, and is not a child of an embedded widget"));
386  }
387  proxy->setWidget(subWidget);
388 
389  if (shouldBeInsertable) {
390  QCOMPARE(proxy->widget(), subWidget);
393  QCOMPARE(proxy->acceptHoverEvents(), true);
394 #ifndef QT_NO_CURSOR
395  QVERIFY(proxy->hasCursor());
396 
397  // These should match
398  QCOMPARE(proxy->cursor().shape(), widget->cursor().shape());
399 #endif
400  //###QCOMPARE(proxy->palette(), widget->palette());
401  QCOMPARE(proxy->layoutDirection(), widget->layoutDirection());
402  QCOMPARE(proxy->style(), widget->style());
403  QCOMPARE(proxy->isVisible(), widget->isVisible());
404  QCOMPARE(proxy->isEnabled(), widget->isEnabled());
406  QCOMPARE(proxy->effectiveSizeHint(Qt::MinimumSize).toSize(),
408  QCOMPARE(proxy->minimumSize().toSize(),
410  QCOMPARE(proxy->maximumSize().toSize(), QSize(1000, 2000));
411  QCOMPARE(proxy->effectiveSizeHint(Qt::PreferredSize).toSize(),
413  QCOMPARE(proxy->size().toSize(), widget->size());
414  QCOMPARE(proxy->rect().toRect(), widget->rect());
415  QCOMPARE(proxy->focusPolicy(), Qt::WheelFocus);
416  QVERIFY(proxy->acceptDrops());
417  QCOMPARE(proxy->acceptHoverEvents(), true); // to get widget enter events
418  const QMarginsF margins = QMarginsF{widget->contentsMargins()};
419  qreal rleft, rtop, rright, rbottom;
420  proxy->getContentsMargins(&rleft, &rtop, &rright, &rbottom);
421  QCOMPARE(margins.left(), rleft);
422  QCOMPARE(margins.top(), rtop);
423  QCOMPARE(margins.right(), rright);
424  QCOMPARE(margins.bottom(), rbottom);
425  } else {
426  // proxy shouldn't mess with the widget if it can't insert it.
427  QCOMPARE(proxy->widget(), nullptr);
428  QCOMPARE(proxy->acceptHoverEvents(), false);
429  if (subWidget) {
432  // reset
433  subWidget->setAttribute(Qt::WA_QuitOnClose, false);
434  }
435  }
436 
437  if (widgetExists) {
438  QCOMPARE(existingSubWidget->parent(), nullptr);
439  QVERIFY(!existingSubWidget->testAttribute(Qt::WA_DontShowOnScreen));
440  QVERIFY(!existingSubWidget->testAttribute(Qt::WA_QuitOnClose));
441  }
442 
443  if (hasParent)
444  widget->setParent(0);
445 
446  delete widget;
447  if (shouldBeInsertable)
448  QVERIFY(!proxy);
449  delete existingSubWidget;
450  if (!shouldBeInsertable) {
451  QVERIFY(proxy);
452  delete proxy;
453  }
454  QVERIFY(!proxy);
455 }
456 
458 void tst_QGraphicsProxyWidget::testEventFilter_data()
459 {
460  QTest::addColumn<QEvent::Type>("eventType");
461  QTest::addColumn<bool>("fromObject"); // big grin evil
462  QTest::newRow("none") << QEvent::None << false;
463  for (int i = 0; i < 2; ++i) {
464  bool fromObject = (i == 0);
465  const char fromObjectC = fromObject ? '1' : '0';
466  QTest::newRow((QByteArrayLiteral("resize ") + fromObjectC).constData()) << QEvent::Resize << fromObject;
467  QTest::newRow((QByteArrayLiteral("move ") + fromObjectC).constData()) << QEvent::Move << fromObject;
468  QTest::newRow((QByteArrayLiteral("hide ") + fromObjectC).constData()) << QEvent::Hide << fromObject;
469  QTest::newRow((QByteArrayLiteral("show ") + fromObjectC).constData()) << QEvent::Show << fromObject;
470  QTest::newRow((QByteArrayLiteral("enabled ") + fromObjectC).constData()) << QEvent::EnabledChange << fromObject;
471  QTest::newRow((QByteArrayLiteral("focusIn ") + fromObjectC).constData()) << QEvent::FocusIn << fromObject;
472  QTest::newRow((QByteArrayLiteral("focusOut ") + fromObjectC).constData()) << QEvent::FocusOut << fromObject;
473  QTest::newRow((QByteArrayLiteral("keyPress ") + fromObjectC).constData()) << QEvent::KeyPress << fromObject;
474  }
475 }
476 
477 // protected bool eventFilter(QObject* object, QEvent* event)
478 void tst_QGraphicsProxyWidget::testEventFilter()
479 {
480  QFETCH(QEvent::Type, eventType);
481  QFETCH(bool, fromObject);
482 
484  QEvent windowActivate(QEvent::WindowActivate);
485  qApp->sendEvent(&scene, &windowActivate);
486 
489 
492  widget->resize(10, 10);
493  widget->show();
494  proxy->setWidget(widget);
495  proxy->show();
496 
497  // mirror whatever is happening to the widget
498  // don't get in a loop
499  switch (eventType) {
500  case QEvent::None: {
502  proxy->eventFilter(widget, &event);
503  break;
504  }
505  case QEvent::Resize: {
506  QSize newSize = QSize(100, 100);
507  if (fromObject) {
508  widget->resize(newSize);
509  } else {
510  proxy->resize(newSize);
511  }
512  QCOMPARE(proxy->size().toSize(), newSize);
513  QCOMPARE(widget->size(), newSize);
514  break;
515  }
516  case QEvent::Move: {
517  QPoint newPoint = QPoint(100, 100);
518  if (fromObject) {
519  widget->move(newPoint);
520  } else {
521  proxy->setPos(newPoint);
522  }
523  QCOMPARE(proxy->pos().toPoint(), newPoint);
524  QCOMPARE(widget->pos(), newPoint);
525  break;
526  }
527  case QEvent::Hide: {
528  // A hide event can only come from a widget
529  if (fromObject) {
531  widget->hide();
532  } else {
534  proxy->eventFilter(widget, &event);
535  }
536  QCOMPARE(proxy->isVisible(), false);
537  break;
538  }
539  case QEvent::Show: {
540  // A show event can either come from a widget or somewhere else
541  widget->hide();
542  if (fromObject) {
543  widget->show();
544  } else {
546  proxy->eventFilter(widget, &event);
547  }
548  QCOMPARE(proxy->isVisible(), true);
549  break;
550  }
551  case QEvent::EnabledChange: {
552  widget->setEnabled(false);
553  proxy->setEnabled(false);
554  if (fromObject) {
555  widget->setEnabled(true);
556  QCOMPARE(proxy->isEnabled(), true);
557  widget->setEnabled(false);
558  QCOMPARE(proxy->isEnabled(), false);
559  } else {
561  proxy->eventFilter(widget, &event);
562  // match the widget not the event
563  QCOMPARE(proxy->isEnabled(), false);
564  }
565  break;
566  }
567  case QEvent::FocusIn: {
568  if (fromObject) {
570  QVERIFY(proxy->hasFocus());
571  }
572  break;
573  }
574  case QEvent::FocusOut: {
576  QVERIFY(proxy->hasFocus());
577  QVERIFY(widget->hasFocus());
578  if (fromObject) {
579  widget->clearFocus();
580  QVERIFY(!proxy->hasFocus());
581  }
582  break;
583  }
584  case QEvent::KeyPress: {
585  if (fromObject) {
586  QTest::keyPress(widget, Qt::Key_A, Qt::NoModifier);
587  } else {
589  proxy->eventFilter(widget, &event);
590  }
591  QCOMPARE(proxy->keyPress, 1);
592  break;
593  }
594  default:
595  break;
596  }
597 }
598 
599 void tst_QGraphicsProxyWidget::focusInEvent_data()
600 {
601  QTest::addColumn<bool>("widgetHasFocus");
602  QTest::addColumn<bool>("widgetCanHaveFocus");
603  QTest::newRow("no focus, can't get") << false << false;
604  QTest::newRow("no focus, can get") << false << true;
605  QTest::newRow("has focus, can't get") << true << false;
606  QTest::newRow("has focus, can get") << true << true;
607  // ### add test for widget having a focusNextPrevChild
608 }
609 
610 // protected void focusInEvent(QFocusEvent* event)
611 void tst_QGraphicsProxyWidget::focusInEvent()
612 {
613 #ifdef Q_OS_WIN
614  // Fails on Windows due QPlatformWindow::isActive() check required for embedded native widgets.
615  // Since the test is apparently broken anyway, just skip it.
616  QSKIP("Broken test.");
617 #endif
618 
619  // ### This test is just plain old broken
620  QFETCH(bool, widgetHasFocus);
621  QFETCH(bool, widgetCanHaveFocus);
622 
624  QEvent windowActivate(QEvent::WindowActivate);
625  qApp->sendEvent(&scene, &windowActivate);
626 
628  proxy->setEnabled(true);
630 
631  QWidget *widget = new QWidget;
632  widget->resize(100, 100);
633  if (widgetCanHaveFocus)
635  widget->show();
636 
637  if (widgetHasFocus)
639 
640  proxy->setWidget(widget);
641  proxy->setFlag(QGraphicsItem::ItemIsFocusable, true); // <- shouldn't need to do this
642 
643  // ### This test is just plain old broken - sending a focus in event
644  // does not cause items to gain input focus. The widget has focus
645  // because the proxy has focus, not because it got this event.
646 
648  event.ignore();
649  proxy->focusInEvent(&event);
650  QTRY_COMPARE(widget->hasFocus(), widgetCanHaveFocus);
651 }
652 
653 void tst_QGraphicsProxyWidget::focusInEventNoWidget()
654 {
658  proxy->setEnabled(true);
660  view.show();
661 
662  proxy->setFlag(QGraphicsItem::ItemIsFocusable, true); // <- shouldn't need to do this
664  event.ignore();
665  //should not crash
666  proxy->focusInEvent(&event);
667 }
668 
669 void tst_QGraphicsProxyWidget::focusNextPrevChild_data()
670 {
671  QTest::addColumn<bool>("hasWidget");
672  QTest::addColumn<bool>("hasScene");
673  QTest::addColumn<bool>("next");
674  QTest::addColumn<bool>("focusNextPrevChild");
675 
676  for (int i = 0; i < 2; ++i) {
677  for (int j = 0; j < 2; ++j) {
678  for (int k = 0; k < 2; ++k) {
679  bool next = (i == 0);
680  bool hasWidget = (j == 0);
681  bool hasScene = (k == 0);
682  bool result = hasScene && hasWidget;
683  QByteArray name = QByteArrayLiteral("Forward: ") + (next ? '1' : '0')
684  + ", hasWidget: " + (hasWidget ? '1' : '0') + ", hasScene: "
685  + (hasScene ? '1' : '0') + ", result: " + (result ? '1' : '0');
686  QTest::newRow(name.constData()) << hasWidget << hasScene << next << result;
687  }
688  }
689  }
690 }
691 
692 // protected bool focusNextPrevChild(bool next)
693 void tst_QGraphicsProxyWidget::focusNextPrevChild()
694 {
695  QFETCH(bool, next);
696  QFETCH(bool, focusNextPrevChild);
697  QFETCH(bool, hasWidget);
698  QFETCH(bool, hasScene);
699 
700  // If a widget has its own focusNextPrevChild we need to respect it
701  // otherwise respect the scene
702  // Respect the widget over the scene!
703 
704  std::unique_ptr<SubQGraphicsProxyWidget> proxyGuard(new SubQGraphicsProxyWidget);
705  auto *proxy = proxyGuard.get();
706 
707  if (hasWidget) {
708  QLabel *widget = new QLabel;
709  widget->setText(R"(
710  <html>
711  <head>
712  <meta name=\"qrichtext\" content=\"1\" />
713  <style type=\"text/css\">
714  p, li { white-space: pre-wrap; }
715  </style>
716  </head>
717  <body>
718  <p>
719  <a href=\"http://www.slashdot.org\">
720  <span style=\" text-decoration: underline; color:#0000ff;\">old</span>
721  </a> foo
722  <a href=\"http://www.reddit.org\">
723  <span style=\" text-decoration: underline; color:#0000ff;\">new</span>
724  </a>
725  </p>
726  </body>
727  </html>)");
728  widget->setTextInteractionFlags(Qt::TextBrowserInteraction);
729  proxy->setWidget(widget);
730  }
731 
734  view.show();
737  if (hasScene) {
738  scene.addItem(proxyGuard.release());
739  proxy->show();
740 
741  // widget should take precedence over scene so make scene.focusNextPrevChild return false
742  // so we know that a true can only come from the widget
743  if (!(hasWidget && hasScene)) {
745  item->setTextInteractionFlags(Qt::TextBrowserInteraction);
746  scene.addItem(item);
747  item->setPos(50, 40);
748  }
750  QVERIFY(proxy->hasFocus());
751  }
752 
753  QCOMPARE(proxy->focusNextPrevChild(next), focusNextPrevChild);
754 }
755 
756 void tst_QGraphicsProxyWidget::focusOutEvent_data()
757 {
758  QTest::addColumn<bool>("hasWidget");
759  QTest::addColumn<bool>("call");
760  QTest::newRow("no widget, focus to other widget") << false << false;
761  QTest::newRow("no widget, focusOutCalled") << false << true;
762  QTest::newRow("widget, focus to other widget") << true << false;
763  QTest::newRow("widget, focusOutCalled") << true << true;
764 }
765 
766 // protected void focusOutEvent(QFocusEvent* event)
767 void tst_QGraphicsProxyWidget::focusOutEvent()
768 {
769  QFETCH(bool, hasWidget);
770  QFETCH(bool, call);
771 
776  view.show();
778  view.activateWindow();
779  view.setFocus();
781  QTRY_VERIFY(view.isVisible());
783 
784  std::unique_ptr<QWidget> widgetGuard(new QWidget);
785  QWidget *widget = widgetGuard.get();
786  widgetGuard->setFocusPolicy(Qt::WheelFocus);
787  if (hasWidget)
788  proxy->setWidget(widgetGuard.release());
789  proxy->show();
790  proxy->setFocus();
791  QVERIFY(proxy->hasFocus());
792  QEXPECT_FAIL("widget, focus to other widget", "Widget should have focus but doesn't", Continue);
793  QEXPECT_FAIL("widget, focusOutCalled", "Widget should have focus but doesn't", Continue);
794  QCOMPARE(widget->hasFocus(), hasWidget);
795 
796  if (!call) {
797  QWidget *other = new QLineEdit(&view);
798  other->show();
799  QTRY_VERIFY(other->isVisible());
800  other->setFocus();
801  QTRY_VERIFY(other->hasFocus());
802  QTRY_COMPARE(proxy->hasFocus(), false);
803  QVERIFY(proxy->focusOut);
804  QCOMPARE(widget->hasFocus(), false);
805  }
806 }
807 
808 void tst_QGraphicsProxyWidget::focusProxy_QTBUG_51856()
809 {
810  // QSpinBox has an internal QLineEdit; this QLineEdit has the spinbox
811  // as its focus proxy.
812  struct FocusedSpinBox : QSpinBox
813  {
814  int focusCount = 0;
815 
816  bool event(QEvent *event) override
817  {
818  switch (event->type()) {
819  case QEvent::FocusIn:
820  ++focusCount;
821  break;
822  case QEvent::FocusOut:
823  --focusCount;
824  break;
825  default:
826  break;
827  }
828  return QSpinBox::event(event);
829  }
830  };
831 
836  view.show();
837  view.raise();
838  view.activateWindow();
840 
841  FocusedSpinBox *spinBox = new FocusedSpinBox;
842 
843  proxy->setWidget(spinBox);
844  proxy->show();
845  proxy->setFocus();
846  QVERIFY(proxy->hasFocus());
847  QEXPECT_FAIL("", "Widget should have focus but doesn't", Continue);
849  QEXPECT_FAIL("", "Widget should have focus but doesn't", Continue);
850  QCOMPARE(spinBox->focusCount, 1);
851 
852  enum { Count = 10 };
853 
854  for (int i = 0; i < Count; ++i) {
855  for (int clickCount = 0; clickCount < Count; ++clickCount) {
856  auto proxyCenter = proxy->boundingRect().center();
857  auto proxyCenterInScene = proxy->mapToScene(proxyCenter);
858  auto proxyCenterInView = view.mapFromScene(proxyCenterInScene);
859 
860  QTest::mouseClick(view.viewport(), Qt::LeftButton, {}, proxyCenterInView);
861  QTRY_COMPARE(spinBox->focusCount, 1);
862  }
863 
864  QLineEdit *edit = new QLineEdit(&view);
865  edit->show();
867  edit->setFocus();
869  QTRY_VERIFY(!proxy->hasFocus());
870  QTRY_COMPARE(proxy->focusOut, i + 1);
872  QTRY_COMPARE(spinBox->focusCount, 0);
873  delete edit;
874  }
875 }
876 
877 class EventLogger : public QWidget
878 {
879 public:
881  hoverEnter(0), hoverLeave(0), hoverMove(0)
882  {
883  installEventFilter(this);
884  }
885 
886  void enterEvent(QEnterEvent *event) override
887  {
888  enterCount++;
890  }
891 
892  void leaveEvent(QEvent *event ) override
893  {
894  leaveCount++;
896  }
897 
899  {
900  event->setAccepted(true);
901  moveCount++;
903  }
904 
908 
912 protected:
913  bool eventFilter(QObject *object, QEvent *event) override
914  {
915  switch (event->type()) {
916  case QEvent::HoverEnter:
917  hoverEnter++;
918  break;
919  case QEvent::HoverLeave:
920  hoverLeave++;
921  break;
922  case QEvent::HoverMove:
923  hoverMove++;
924  break;
925  default:
926  break;
927  }
928  return QWidget::eventFilter(object, event);
929  }
930 };
931 
932 void tst_QGraphicsProxyWidget::hoverEnterLeaveEvent_data()
933 {
934  QTest::addColumn<bool>("hasWidget");
935  QTest::addColumn<bool>("hoverEnabled");
936  QTest::newRow("widget, no hover") << true << false;
937  QTest::newRow("no widget, no hover") << false << false;
938  QTest::newRow("widget, hover") << true << true;
939  QTest::newRow("no widget, hover") << false << true;
940 }
941 
942 // protected void hoverEnterEvent(QGraphicsSceneHoverEvent* event)
943 void tst_QGraphicsProxyWidget::hoverEnterLeaveEvent()
944 {
945  QFETCH(bool, hasWidget);
946  QFETCH(bool, hoverEnabled);
947 
948  // proxy should translate this into events that the widget would expect
949 
952  view.show();
955 
957  std::unique_ptr<EventLogger> widgetGuard(new EventLogger);
958  EventLogger *widget = widgetGuard.get();
959  widget->resize(50, 50);
960  widget->setAttribute(Qt::WA_Hover, hoverEnabled);
961  widget->setMouseTracking(true);
962  view.resize(100, 100);
963  if (hasWidget)
964  proxy->setWidget(widgetGuard.release());
965  proxy->setPos(50, 0);
966  QSignalSpy sceneChangedSpy(&scene, &QGraphicsScene::changed);
968  QTRY_VERIFY(sceneChangedSpy.count() > 0);
969 
970  // outside graphics item
971  QTest::mouseMove(&view, QPoint(10, 10));
972 
974  QCOMPARE(widget->enterCount, 0);
975  QCOMPARE(widget->hoverEnter, 0);
976  // over graphics item
977  QTest::mouseMove(&view, QPoint(50, 50));
979  QCOMPARE(widget->enterCount, hasWidget ? 1 : 0);
980  QCOMPARE(widget->hoverEnter, (hasWidget && hoverEnabled) ? 1 : 0);
981 
982  QTRY_COMPARE(widget->leaveCount, 0);
983  QTRY_COMPARE(widget->hoverLeave, 0);
984  // outside graphics item
985  QTest::mouseMove(&view, QPoint(10, 10));
987  QTRY_COMPARE(widget->leaveCount, hasWidget ? 1 : 0);
988  QTRY_COMPARE(widget->hoverLeave, (hasWidget && hoverEnabled) ? 1 : 0);
989 }
990 
991 void tst_QGraphicsProxyWidget::keyPressEvent_data()
992 {
993  QTest::addColumn<bool>("hasWidget");
994  QTest::newRow("widget") << true;
995  QTest::newRow("no widget") << false;
996 }
997 
998 // protected void keyPressEvent(QKeyEvent* event)
999 void tst_QGraphicsProxyWidget::keyPressEvent()
1000 {
1001  QFETCH(bool, hasWidget);
1002 
1005  view.show();
1006  view.viewport()->setFocus();
1010 
1012  proxy->setFlag(QGraphicsItem::ItemIsFocusable, true); // ### remove me!!!
1013 
1014  std::unique_ptr<QLineEdit> widgetGuard(new QLineEdit);
1015  QLineEdit *widget = widgetGuard.get();
1016  widget->resize(50, 50);
1017  view.resize(100, 100);
1018  if (hasWidget)
1019  proxy->setWidget(widgetGuard.release());
1020 
1021  scene.addItem(proxy);
1022  proxy->show();
1023  proxy->setPos(50, 0);
1024  proxy->setFocus();
1025 
1026  QTest::keyPress(view.viewport(), 'x');
1027 
1028  QTRY_COMPARE(widget->text(), hasWidget ? QString("x") : QString());
1029 }
1030 
1031 void tst_QGraphicsProxyWidget::keyReleaseEvent_data()
1032 {
1033  QTest::addColumn<bool>("hasWidget");
1034  QTest::newRow("widget") << true;
1035  QTest::newRow("no widget") << false;
1036 }
1037 
1038 // protected void keyReleaseEvent(QKeyEvent* event)
1039 void tst_QGraphicsProxyWidget::keyReleaseEvent()
1040 {
1041  QFETCH(bool, hasWidget);
1042 
1045  view.show();
1049 
1050 
1052  proxy->setFlag(QGraphicsItem::ItemIsFocusable, true); // ### remove me!!!
1053  std::unique_ptr<QPushButton> widgetGuard(new QPushButton);
1054  QSignalSpy spy(widgetGuard.get(), SIGNAL(clicked()));
1055  widgetGuard->resize(50, 50);
1056  view.resize(100, 100);
1057  if (hasWidget) {
1058  proxy->setWidget(widgetGuard.release());
1059  proxy->show();
1060  }
1061  proxy->setPos(50, 0);
1062  scene.addItem(proxy);
1063  proxy->setFocus();
1064 
1065  QTest::keyPress(view.viewport(), Qt::Key_Space);
1066  QTRY_COMPARE(spy.count(), 0);
1067  QTest::keyRelease(view.viewport(), Qt::Key_Space);
1068  QTRY_COMPARE(spy.count(), hasWidget ? 1 : 0);
1069 }
1070 
1071 // protected void mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event)
1072 void tst_QGraphicsProxyWidget::mouseDoubleClickEvent()
1073 {
1076 
1078  proxy->setFlag(QGraphicsItem::ItemIsFocusable, true); // ### remove me!!!
1079  QLineEdit *widget = new QLineEdit;
1080  widget->setText("foo");
1081  widget->resize(50, 50);
1082  proxy->setWidget(widget);
1083 
1084  proxy->setPos(50, 0);
1085  QSignalSpy sceneChangedSpy(&scene, &QGraphicsScene::changed);
1086  scene.addItem(proxy);
1087  proxy->setFocus();
1088 
1089  view.resize(100, 100);
1090  view.show();
1091 
1095  // wait for scene to be updated before doing any coordinate mappings on it
1096  QTRY_VERIFY(sceneChangedSpy.count() > 0);
1097 
1098  QPoint pointInLineEdit = view.mapFromScene(proxy->mapToScene(15, proxy->boundingRect().center().y()));
1099  QTest::mousePress(view.viewport(), Qt::LeftButton, {}, pointInLineEdit);
1100  QTest::mouseRelease(view.viewport(), Qt::LeftButton, {}, pointInLineEdit);
1101  QTest::mouseDClick(view.viewport(), Qt::LeftButton, {}, pointInLineEdit);
1102 
1103  QTRY_COMPARE(widget->selectedText(), QString("foo"));
1104 }
1105 
1106 void tst_QGraphicsProxyWidget::mousePressReleaseEvent_data()
1107 {
1108  QTest::addColumn<bool>("hasWidget");
1109  QTest::newRow("widget") << true;
1110  QTest::newRow("no widget") << false;
1111 }
1112 
1113 // protected void mousePressEvent(QGraphicsSceneMouseEvent* event)
1114 void tst_QGraphicsProxyWidget::mousePressReleaseEvent()
1115 {
1116  QFETCH(bool, hasWidget);
1117 
1120  view.resize(500, 500);
1121  view.show();
1123 
1125  proxy->setFlag(QGraphicsItem::ItemIsFocusable, true); // ### remove me!!!
1126  std::unique_ptr<QPushButton> widgetGuard(new QPushButton);
1127  QSignalSpy spy(widgetGuard.get(), SIGNAL(clicked()));
1128  widgetGuard->resize(50, 50);
1129  if (hasWidget)
1130  proxy->setWidget(widgetGuard.release());
1131  proxy->setPos(50, 0);
1132  QSignalSpy sceneChangedSpy(&scene, &QGraphicsScene::changed);
1133  scene.addItem(proxy);
1134  proxy->setFocus();
1135 
1136  // wait for scene to be updated before doing any coordinate mappings on it
1137  QTRY_VERIFY(sceneChangedSpy.count() > 0);
1138 
1139  QPoint buttonCenter = view.mapFromScene(proxy->mapToScene(proxy->boundingRect().center()));
1140  QTest::mousePress(view.viewport(), Qt::LeftButton, {}, buttonCenter);
1141  QTRY_COMPARE(spy.count(), 0);
1142  QTest::mouseRelease(view.viewport(), Qt::LeftButton, {}, buttonCenter);
1143  QTRY_COMPARE(spy.count(), hasWidget ? 1 : 0);
1144 }
1145 
1146 void tst_QGraphicsProxyWidget::resizeEvent_data()
1147 {
1148  QTest::addColumn<bool>("hasWidget");
1149  QTest::newRow("widget") << true;
1150  QTest::newRow("no widget") << false;
1151 }
1152 
1153 // protected void resizeEvent(QGraphicsSceneResizeEvent* event)
1154 void tst_QGraphicsProxyWidget::resizeEvent()
1155 {
1156  QFETCH(bool, hasWidget);
1157 
1159 
1160  if (hasWidget)
1161  proxy.setWidget(new QWidget);
1162 
1163  QSize newSize(100, 100);
1165  event.setOldSize(QSize(10, 10));
1166  event.setNewSize(newSize);
1167  proxy.resizeEvent(&event);
1168  if (hasWidget)
1169  QCOMPARE(proxy.widget()->size(), newSize);
1170 }
1171 
1172 void tst_QGraphicsProxyWidget::paintEvent()
1173 {
1174  //we test that calling update on a widget inside a QGraphicsView is triggering a repaint
1177  view.show();
1180  QVERIFY(view.isActiveWindow());
1181 
1183 
1184  QWidget *w = new QWidget;
1185  //showing the widget here seems to create a bug in Graphics View
1186  //this bug prevents the widget from being updated
1187 
1188  w->show();
1190  proxy.setWidget(w);
1191  QSignalSpy sceneChangedSpy(&scene, &QGraphicsScene::changed);
1192  scene.addItem(&proxy);
1193 
1194  QTRY_VERIFY(sceneChangedSpy.count() > 0); // make sure the scene is ready
1195 
1196  proxy.paintCount = 0;
1197  w->update();
1198  QTRY_VERIFY(proxy.paintCount >= 1); //the widget should have been painted now
1199 }
1200 
1201 
1202 #if QT_CONFIG(wheelevent)
1203 void tst_QGraphicsProxyWidget::wheelEvent()
1204 {
1207  view.show();
1209 
1210  WheelWidget *wheelWidget = new WheelWidget();
1211  wheelWidget->setFixedSize(400, 400);
1212 
1213  QGraphicsProxyWidget *proxy = scene.addWidget(wheelWidget);
1214  proxy->setVisible(true);
1215 
1217  event.setScenePos(QPoint(50, 50));
1218  event.setAccepted(false);
1219  wheelWidget->wheelEventCalled = false;
1220 
1222 
1223  QVERIFY(event.isAccepted());
1224  QVERIFY(wheelWidget->wheelEventCalled);
1225 }
1226 #endif // QT_CONFIG(wheelevent)
1227 
1228 void tst_QGraphicsProxyWidget::sizePolicy()
1229 {
1230  for (int p = 0; p < 2; ++p) {
1231  bool hasWidget = (p == 0);
1233  std::unique_ptr<QWidget> widgetGuard(new QWidget);
1234  QWidget *widget = widgetGuard.get();
1236  proxy.setSizePolicy(proxyPol);
1238  widget->setSizePolicy(widgetPol);
1239 
1240  QCOMPARE(proxy.sizePolicy(), proxyPol);
1241  QCOMPARE(widget->sizePolicy(), widgetPol);
1242  if (hasWidget) {
1243  proxy.setWidget(widgetGuard.release());
1244  QCOMPARE(proxy.sizePolicy(), widgetPol);
1245  } else {
1246  QCOMPARE(proxy.sizePolicy(), proxyPol);
1247  }
1248  QCOMPARE(widget->sizePolicy(), widgetPol);
1249 
1250  proxy.setSizePolicy(widgetPol);
1251  widget->setSizePolicy(proxyPol);
1252  if (hasWidget)
1253  QCOMPARE(proxy.sizePolicy(), proxyPol);
1254  else
1255  QCOMPARE(proxy.sizePolicy(), widgetPol);
1256  }
1257 }
1258 
1259 void tst_QGraphicsProxyWidget::minimumSize()
1260 {
1262  std::unique_ptr<QWidget> widgetGuard(new QWidget);
1263  QSize minSize(50, 50);
1264  widgetGuard->setMinimumSize(minSize);
1265  proxy.resize(30, 30);
1266  widgetGuard->resize(30,30);
1267  QCOMPARE(proxy.size(), QSizeF(30, 30));
1268  proxy.setWidget(widgetGuard.release());
1269  QCOMPARE(proxy.size().toSize(), minSize);
1270  QCOMPARE(proxy.minimumSize().toSize(), minSize);
1271  proxy.widget()->setMinimumSize(70, 70);
1272  QCOMPARE(proxy.minimumSize(), QSizeF(70, 70));
1273  QCOMPARE(proxy.size(), QSizeF(70, 70));
1274 }
1275 
1276 void tst_QGraphicsProxyWidget::maximumSize()
1277 {
1279  std::unique_ptr<QWidget> widgetGuard(new QWidget);
1280  QSize maxSize(150, 150);
1281  widgetGuard->setMaximumSize(maxSize);
1282  proxy.resize(200, 200);
1283  widgetGuard->resize(200,200);
1284  QCOMPARE(proxy.size(), QSizeF(200, 200));
1285  proxy.setWidget(widgetGuard.release());
1286  QCOMPARE(proxy.size().toSize(), maxSize);
1287  QCOMPARE(proxy.maximumSize().toSize(), maxSize);
1288  proxy.widget()->setMaximumSize(70, 70);
1289  QCOMPARE(proxy.maximumSize(), QSizeF(70, 70));
1290  QCOMPARE(proxy.size(), QSizeF(70, 70));
1291 }
1292 
1293 class View : public QGraphicsView
1294 {
1295 public:
1296  View(QGraphicsScene *scene, QWidget *parent = nullptr)
1298  { }
1300  int npaints;
1301 protected:
1302  void paintEvent(QPaintEvent *event) override
1303  {
1304  ++npaints;
1305  paintEventRegion += event->region();
1307  }
1308 };
1309 
1310 class ScrollWidget : public QWidget
1311 {
1312  Q_OBJECT
1313 public:
1315  {
1316  resize(200, 200);
1317  }
1319  int npaints;
1320 
1321 public slots:
1323  {
1324  update(0, 0, 200, 10);
1325  scroll(0, 10, QRect(0, 0, 100, 20));
1326  }
1327 
1328 protected:
1329  void paintEvent(QPaintEvent *event) override
1330  {
1331  ++npaints;
1332  paintEventRegion += event->region();
1333  QPainter painter(this);
1334  painter.fillRect(event->rect(), Qt::blue);
1335  }
1336 };
1337 
1338 // ### work around missing QList ctor from iterator pair:
1339 static QList<QRect> rects(const QRegion &region)
1340 {
1342  for (QRect r : region)
1343  result.push_back(r);
1344  return result;
1345 }
1346 
1347 void tst_QGraphicsProxyWidget::scrollUpdate()
1348 {
1350 
1353 
1354  View view(&scene);
1355  view.show();
1357  QTRY_VERIFY(view.npaints >= 1);
1358  QTest::qWait(150);
1359  widget->paintEventRegion = QRegion();
1360  widget->npaints = 0;
1361  view.paintEventRegion = QRegion();
1362  view.npaints = 0;
1363  QTimer::singleShot(0, widget, SLOT(updateScroll()));
1364  QTRY_COMPARE(view.npaints, 2);
1365  // QRect(0, 0, 200, 12) is the first update, expanded (-2, -2, 2, 2)
1366  // QRect(0, 12, 102, 10) is the scroll update, expanded (-2, -2, 2, 2),
1367  // intersected with the above update.
1368  QCOMPARE(rects(view.paintEventRegion),
1369  QList<QRect>() << QRect(0, 0, 200, 12) << QRect(0, 12, 102, 10));
1370  QCOMPARE(widget->npaints, 2);
1371  QCOMPARE(rects(widget->paintEventRegion),
1372  QList<QRect>() << QRect(0, 0, 200, 12) << QRect(0, 12, 102, 10));
1373 }
1374 
1375 void tst_QGraphicsProxyWidget::setWidget_simple()
1376 {
1378  QLineEdit *lineEdit = new QLineEdit;
1379  proxy.setWidget(lineEdit);
1380 
1382  // Size hints
1383  // ### size hints are tested in a different test
1384  // QCOMPARE(proxy.effectiveSizeHint(Qt::MinimumSize).toSize(), lineEdit->minimumSizeHint());
1385  // QCOMPARE(proxy.effectiveSizeHint(Qt::MaximumSize).toSize(), lineEdit->maximumSize());
1386  // QCOMPARE(proxy.effectiveSizeHint(Qt::PreferredSize).toSize(), lineEdit->sizeHint());
1387  QCOMPARE(proxy.size().toSize(), lineEdit->minimumSizeHint().expandedTo(lineEdit->size()));
1388  QRect rect = lineEdit->rect();
1389  rect.setSize(rect.size().expandedTo(lineEdit->minimumSizeHint()));
1390  QCOMPARE(proxy.rect().toRect(), rect);
1391 
1392  // Properties
1393  // QCOMPARE(proxy.focusPolicy(), lineEdit->focusPolicy());
1394  // QCOMPARE(proxy.palette(), lineEdit->palette());
1395 #ifndef QT_NO_CURSOR
1396  QCOMPARE(proxy.cursor().shape(), lineEdit->cursor().shape());
1397 #endif
1398  QCOMPARE(proxy.layoutDirection(), lineEdit->layoutDirection());
1399  QCOMPARE(proxy.style(), lineEdit->style());
1400  QCOMPARE(proxy.font(), lineEdit->font());
1401  QCOMPARE(proxy.isEnabled(), lineEdit->isEnabled());
1402  QCOMPARE(proxy.isVisible(), lineEdit->isVisible());
1403 }
1404 
1405 void tst_QGraphicsProxyWidget::setWidget_ownership()
1406 {
1408  QPointer<QLineEdit> lineEdit2 = new QLineEdit;
1409  QVERIFY(lineEdit);
1410  {
1411  // Create a proxy and transfer ownership to it
1413  proxy.setWidget(lineEdit);
1414  QCOMPARE(proxy.widget(), (QWidget *)lineEdit);
1415 
1416  // Remove the widget without destroying it.
1417  proxy.setWidget(0);
1418  QVERIFY(!proxy.widget());
1419  QVERIFY(lineEdit);
1420 
1421  // Assign the widget again and switch to another widget.
1422  proxy.setWidget(lineEdit);
1423  proxy.setWidget(lineEdit2);
1424  QCOMPARE(proxy.widget(), (QWidget *)lineEdit2);
1425 
1426  // Assign the first widget, and destroy the proxy.
1427  proxy.setWidget(lineEdit);
1428  }
1429  QVERIFY(!lineEdit);
1430  QVERIFY(lineEdit2);
1431 
1434 
1435  delete lineEdit2;
1436  QVERIFY(!proxy);
1437 }
1438 
1439 void tst_QGraphicsProxyWidget::resize_simple_data()
1440 {
1441  QTest::addColumn<QSizeF>("size");
1442 
1443  QTest::newRow("200, 200") << QSizeF(200, 200);
1444 #if !defined(Q_PROCESSOR_ARM)
1445  QTest::newRow("1000, 1000") << QSizeF(1000, 1000);
1446  // Since 4.5, 10000x10000 runs out of memory.
1447  // QTest::newRow("10000, 10000") << QSizeF(10000, 10000);
1448 #endif
1449 }
1450 
1451 void tst_QGraphicsProxyWidget::resize_simple()
1452 {
1453  QFETCH(QSizeF, size);
1454 
1456  QWidget *widget = new QWidget;
1457  widget->setGeometry(0, 0, (int)size.width(), (int)size.height());
1458  proxy.setWidget(widget);
1459  widget->show();
1460  QCOMPARE(widget->pos(), QPoint());
1461 
1462  // The proxy resizes itself, the line edit follows
1463  proxy.resize(size);
1464  QCOMPARE(proxy.size(), size);
1465  QCOMPARE(proxy.size().toSize(), widget->size());
1466 
1467  // The line edit resizes itself, the proxy follows (no loopback/live lock)
1468  QSize doubleSize = size.toSize() * 2;
1469  widget->resize(doubleSize);
1470  QCOMPARE(widget->size(), doubleSize);
1471  QCOMPARE(widget->size(), proxy.size().toSize());
1472 }
1473 
1474 void tst_QGraphicsProxyWidget::symmetricMove()
1475 {
1477  QLineEdit *lineEdit = new QLineEdit;
1478  proxy.setWidget(lineEdit);
1479  lineEdit->show();
1480 
1481  proxy.setPos(10, 10);
1482  QCOMPARE(proxy.pos(), QPointF(10, 10));
1483  QCOMPARE(lineEdit->pos(), QPoint(10, 10));
1484 
1485  lineEdit->move(5, 5);
1486  QCOMPARE(proxy.pos(), QPointF(5, 5));
1487  QCOMPARE(lineEdit->pos(), QPoint(5, 5));
1488 }
1489 
1490 void tst_QGraphicsProxyWidget::symmetricResize()
1491 {
1493  QLineEdit *lineEdit = new QLineEdit;
1494  proxy.setWidget(lineEdit);
1495  lineEdit->show();
1496 
1497  proxy.resize(256, 256);
1498  QCOMPARE(proxy.size(), QSizeF(256, 256));
1499  QCOMPARE(lineEdit->size(), QSize(256, 256));
1500 
1501  lineEdit->resize(512, 512);
1502  QCOMPARE(proxy.size(), QSizeF(512, 512));
1503  QCOMPARE(lineEdit->size(), QSize(512, 512));
1504 }
1505 
1506 void tst_QGraphicsProxyWidget::symmetricVisible()
1507 {
1509  QLineEdit *lineEdit = new QLineEdit;
1510  proxy.setWidget(lineEdit);
1511  lineEdit->show();
1512 
1513  QCOMPARE(proxy.isVisible(), lineEdit->isVisible());
1514 
1515  proxy.hide();
1516  QCOMPARE(proxy.isVisible(), lineEdit->isVisible());
1517  proxy.show();
1518  QCOMPARE(proxy.isVisible(), lineEdit->isVisible());
1519  lineEdit->hide();
1520  QCOMPARE(proxy.isVisible(), lineEdit->isVisible());
1521  lineEdit->show();
1522  QCOMPARE(proxy.isVisible(), lineEdit->isVisible());
1523  }
1524 
1525 void tst_QGraphicsProxyWidget::symmetricEnabled()
1526 {
1528  QLineEdit *lineEdit = new QLineEdit;
1529  proxy.setWidget(lineEdit);
1530  lineEdit->show();
1531 
1532  QCOMPARE(proxy.isEnabled(), lineEdit->isEnabled());
1533  proxy.setEnabled(false);
1534  QCOMPARE(proxy.isEnabled(), lineEdit->isEnabled());
1535  proxy.setEnabled(true);
1536  QCOMPARE(proxy.isEnabled(), lineEdit->isEnabled());
1537  lineEdit->setEnabled(false);
1538  QCOMPARE(proxy.isEnabled(), lineEdit->isEnabled());
1539  lineEdit->setEnabled(true);
1540  QCOMPARE(proxy.isEnabled(), lineEdit->isEnabled());
1541 }
1542 
1543 void tst_QGraphicsProxyWidget::tabFocus_simpleWidget()
1544 {
1546  QLineEdit *edit = new QLineEdit;
1547  QGraphicsProxyWidget *editProxy = scene.addWidget(edit);
1548  editProxy->show();
1549 
1550  QDial *leftDial = new QDial;
1551  QDial *rightDial = new QDial;
1553 
1554  QWidget window;
1556  layout->addWidget(leftDial);
1557  layout->addWidget(view);
1558  layout->addWidget(rightDial);
1559  window.setLayout(layout);
1560 
1561  window.show();
1563  window.activateWindow();
1565 
1566  leftDial->setFocus();
1568  QTRY_VERIFY(leftDial->hasFocus());
1569 
1570  EventSpy eventSpy(edit);
1571 
1572  // Tab into line edit
1573  QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab);
1575  QTRY_VERIFY(!leftDial->hasFocus());
1576  QTRY_VERIFY(view->hasFocus());
1577  QVERIFY(view->viewport()->hasFocus());
1578  QVERIFY(scene.hasFocus());
1579  QVERIFY(editProxy->hasFocus());
1580  QVERIFY(edit->hasFocus());
1581  QCOMPARE(eventSpy.counts[QEvent::FocusIn], 1);
1582  QCOMPARE(eventSpy.counts[QEvent::FocusOut], 0);
1583 
1584  // Tab into right dial
1585  QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab);
1587  QTRY_VERIFY(!view->hasFocus());
1588  QVERIFY(!view->viewport()->hasFocus());
1589  QVERIFY(!scene.hasFocus());
1590  QVERIFY(!editProxy->hasFocus());
1591  QVERIFY(!edit->hasFocus());
1592  QTRY_VERIFY(rightDial->hasFocus());
1593  QCOMPARE(eventSpy.counts[QEvent::FocusIn], 1);
1594  QCOMPARE(eventSpy.counts[QEvent::FocusOut], 1);
1595 
1596  // Backtab into line edit
1597  QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab);
1599  QTRY_VERIFY(view->hasFocus());
1600  QVERIFY(view->viewport()->hasFocus());
1602  QVERIFY(editProxy->hasFocus());
1603  QVERIFY(edit->hasFocus());
1604  QVERIFY(!rightDial->hasFocus());
1605  QCOMPARE(eventSpy.counts[QEvent::FocusIn], 2);
1606  QCOMPARE(eventSpy.counts[QEvent::FocusOut], 1);
1607 
1608  // Backtab into left dial
1609  QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab);
1611  QTRY_VERIFY(!view->hasFocus());
1612  QVERIFY(!view->viewport()->hasFocus());
1613  QVERIFY(!scene.hasFocus());
1614  QVERIFY(!editProxy->hasFocus());
1615  QVERIFY(!edit->hasFocus());
1616  QTRY_VERIFY(leftDial->hasFocus());
1617  QCOMPARE(eventSpy.counts[QEvent::FocusIn], 2);
1618  QCOMPARE(eventSpy.counts[QEvent::FocusOut], 2);
1619 
1620  delete view;
1621 }
1622 
1623 void tst_QGraphicsProxyWidget::tabFocus_simpleTwoWidgets()
1624 {
1626  QLineEdit *edit = new QLineEdit;
1627  QLineEdit *edit2 = new QLineEdit;
1628  QGraphicsProxyWidget *editProxy = scene.addWidget(edit);
1629  editProxy->show();
1630  QGraphicsProxyWidget *editProxy2 = scene.addWidget(edit2);
1631  editProxy2->show();
1632  editProxy2->setPos(0, editProxy->rect().height() * 1.1);
1633 
1634  QDial *leftDial = new QDial;
1635  QDial *rightDial = new QDial;
1637 
1638  QWidget window;
1640  layout->addWidget(leftDial);
1641  layout->addWidget(view);
1642  layout->addWidget(rightDial);
1643  window.setLayout(layout);
1644 
1645  window.show();
1647  window.activateWindow();
1649 
1650  leftDial->setFocus();
1652  QTRY_VERIFY(leftDial->hasFocus());
1653 
1654  EventSpy eventSpy(edit);
1655  EventSpy eventSpy2(edit2);
1656 
1657  // Tab into line edit
1658  QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab);
1660  QVERIFY(!leftDial->hasFocus());
1661  QVERIFY(view->hasFocus());
1662  QVERIFY(view->viewport()->hasFocus());
1663  QVERIFY(scene.hasFocus());
1664  QVERIFY(editProxy->hasFocus());
1665  QVERIFY(edit->hasFocus());
1666  QCOMPARE(eventSpy.counts[QEvent::FocusIn], 1);
1667  QCOMPARE(eventSpy.counts[QEvent::FocusOut], 0);
1668 
1669  // Tab into second line edit
1670  QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab);
1672  QVERIFY(view->hasFocus());
1673  QVERIFY(view->viewport()->hasFocus());
1674  QVERIFY(scene.hasFocus());
1675  QVERIFY(!editProxy->hasFocus());
1676  QVERIFY(!edit->hasFocus());
1677  QVERIFY(editProxy2->hasFocus());
1678  QVERIFY(edit2->hasFocus());
1679  QCOMPARE(eventSpy.counts[QEvent::FocusIn], 1);
1680  QCOMPARE(eventSpy.counts[QEvent::FocusOut], 1);
1681  QCOMPARE(eventSpy2.counts[QEvent::FocusIn], 1);
1682  QCOMPARE(eventSpy2.counts[QEvent::FocusOut], 0);
1683 
1684  // Tab into right dial
1685  QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab);
1687  QVERIFY(!view->hasFocus());
1688  QVERIFY(!view->viewport()->hasFocus());
1689  QVERIFY(!scene.hasFocus());
1690  QVERIFY(!editProxy->hasFocus());
1691  QVERIFY(!edit->hasFocus());
1692  QVERIFY(!editProxy2->hasFocus());
1693  QVERIFY(!edit2->hasFocus());
1694  QVERIFY(rightDial->hasFocus());
1695  QCOMPARE(eventSpy.counts[QEvent::FocusIn], 1);
1696  QCOMPARE(eventSpy.counts[QEvent::FocusOut], 1);
1697  QCOMPARE(eventSpy2.counts[QEvent::FocusIn], 1);
1698  QCOMPARE(eventSpy2.counts[QEvent::FocusOut], 1);
1699 
1700  // Backtab into line edit 2
1701  QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab);
1703  QVERIFY(view->hasFocus());
1704  QVERIFY(view->viewport()->hasFocus());
1705  QVERIFY(scene.hasFocus());
1706  QVERIFY(!editProxy->hasFocus());
1707  QVERIFY(!edit->hasFocus());
1708  QVERIFY(editProxy2->hasFocus());
1709  QVERIFY(edit2->hasFocus());
1710  QVERIFY(!rightDial->hasFocus());
1711  QCOMPARE(eventSpy.counts[QEvent::FocusIn], 1);
1712  QCOMPARE(eventSpy.counts[QEvent::FocusOut], 1);
1713  QCOMPARE(eventSpy2.counts[QEvent::FocusIn], 2);
1714  QCOMPARE(eventSpy2.counts[QEvent::FocusOut], 1);
1715 
1716  // Backtab into line edit 1
1717  QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab);
1719  QVERIFY(view->hasFocus());
1720  QVERIFY(view->viewport()->hasFocus());
1721  QVERIFY(scene.hasFocus());
1722  QVERIFY(editProxy->hasFocus());
1723  QVERIFY(edit->hasFocus());
1724  QVERIFY(!editProxy2->hasFocus());
1725  QVERIFY(!edit2->hasFocus());
1726  QVERIFY(!rightDial->hasFocus());
1727  QCOMPARE(eventSpy.counts[QEvent::FocusIn], 2);
1728  QCOMPARE(eventSpy.counts[QEvent::FocusOut], 1);
1729  QCOMPARE(eventSpy2.counts[QEvent::FocusIn], 2);
1730  QCOMPARE(eventSpy2.counts[QEvent::FocusOut], 2);
1731 
1732  // Backtab into left dial
1733  QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab);
1735  QVERIFY(!view->hasFocus());
1736  QVERIFY(!view->viewport()->hasFocus());
1737  QVERIFY(!scene.hasFocus());
1738  QVERIFY(!editProxy->hasFocus());
1739  QVERIFY(!edit->hasFocus());
1740  QVERIFY(leftDial->hasFocus());
1741  QCOMPARE(eventSpy.counts[QEvent::FocusIn], 2);
1742  QCOMPARE(eventSpy.counts[QEvent::FocusOut], 2);
1743 
1744  delete view;
1745 }
1746 
1747 void tst_QGraphicsProxyWidget::tabFocus_complexWidget()
1748 {
1750 
1751  QLineEdit *edit1 = new QLineEdit;
1752  edit1->setText("QLineEdit 1");
1753  QLineEdit *edit2 = new QLineEdit;
1754  edit2->setText("QLineEdit 2");
1755  QVBoxLayout *vlayout = new QVBoxLayout;
1756  vlayout->addWidget(edit1);
1757  vlayout->addWidget(edit2);
1758 
1759  QGroupBox *box = new QGroupBox("QGroupBox");
1760  box->setCheckable(true);
1761  box->setChecked(true);
1762  box->setLayout(vlayout);
1763 
1765  proxy->show();
1766 
1767  QDial *leftDial = new QDial;
1768  QDial *rightDial = new QDial;
1770 
1771  QWidget window;
1773  layout->addWidget(leftDial);
1774  layout->addWidget(view);
1775  layout->addWidget(rightDial);
1776  window.setLayout(layout);
1777 
1778  window.show();
1780  window.activateWindow();
1782 
1783  leftDial->setFocus();
1785  QTRY_VERIFY(leftDial->hasFocus());
1786 
1787  EventSpy eventSpy(edit1);
1788  EventSpy eventSpy2(edit2);
1789  EventSpy eventSpyBox(box);
1790 
1791  // Tab into group box
1792  QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab);
1794  QVERIFY(!leftDial->hasFocus());
1795  QVERIFY(view->hasFocus());
1796  QVERIFY(view->viewport()->hasFocus());
1797  QVERIFY(scene.hasFocus());
1798  QVERIFY(proxy->hasFocus());
1799  QVERIFY(box->hasFocus());
1800 
1801  // Tab into line edit
1802  QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab);
1804  edit1->hasFocus();
1805  QVERIFY(!box->hasFocus());
1806  QCOMPARE(eventSpy.counts[QEvent::FocusIn], 1);
1807  QCOMPARE(eventSpy.counts[QEvent::FocusOut], 0);
1808 
1809  // Tab into line edit 2
1810  QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab);
1812  edit2->hasFocus();
1813  QVERIFY(!edit1->hasFocus());
1814  QCOMPARE(eventSpy.counts[QEvent::FocusIn], 1);
1815  QCOMPARE(eventSpy.counts[QEvent::FocusOut], 1);
1816  QCOMPARE(eventSpy2.counts[QEvent::FocusIn], 1);
1817  QCOMPARE(eventSpy2.counts[QEvent::FocusOut], 0);
1818 
1819  // Tab into right dial
1820  QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab);
1822  QVERIFY(!edit2->hasFocus());
1823  rightDial->hasFocus();
1824  QCOMPARE(eventSpy2.counts[QEvent::FocusIn], 1);
1825  QCOMPARE(eventSpy2.counts[QEvent::FocusOut], 1);
1826 
1827  // Backtab into line edit 2
1828  QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab);
1830  QVERIFY(!rightDial->hasFocus());
1831  edit2->hasFocus();
1832  QCOMPARE(eventSpy2.counts[QEvent::FocusIn], 2);
1833  QCOMPARE(eventSpy2.counts[QEvent::FocusOut], 1);
1834 
1835  // Backtab into line edit 1
1836  QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab);
1838  QVERIFY(!edit2->hasFocus());
1839  edit1->hasFocus();
1840  QCOMPARE(eventSpy2.counts[QEvent::FocusOut], 2);
1841  QCOMPARE(eventSpy.counts[QEvent::FocusIn], 2);
1842 
1843  // Backtab into line box
1844  QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab);
1846  QVERIFY(!edit1->hasFocus());
1847  box->hasFocus();
1848  QCOMPARE(eventSpy.counts[QEvent::FocusOut], 2);
1849 
1850  // Backtab into left dial
1851  QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab);
1853  QVERIFY(!box->hasFocus());
1854  leftDial->hasFocus();
1855 
1856  delete view;
1857 }
1858 
1859 void tst_QGraphicsProxyWidget::tabFocus_complexTwoWidgets()
1860 {
1861  // ### add event spies to this test.
1863 
1864  QLineEdit *edit1 = new QLineEdit;
1865  edit1->setText("QLineEdit 1");
1866  QLineEdit *edit2 = new QLineEdit;
1867  edit2->setText("QLineEdit 2");
1868  QFontComboBox *fontComboBox = new QFontComboBox;
1869  QVBoxLayout *vlayout = new QVBoxLayout;
1870  vlayout->addWidget(edit1);
1871  vlayout->addWidget(fontComboBox);
1872  vlayout->addWidget(edit2);
1873 
1874  QGroupBox *box = new QGroupBox("QGroupBox");
1875  box->setCheckable(true);
1876  box->setChecked(true);
1877  box->setLayout(vlayout);
1878 
1879  QLineEdit *edit1_2 = new QLineEdit;
1880  edit1_2->setText("QLineEdit 1_2");
1881  QLineEdit *edit2_2 = new QLineEdit;
1882  edit2_2->setText("QLineEdit 2_2");
1883  QFontComboBox *fontComboBox2 = new QFontComboBox;
1884  vlayout = new QVBoxLayout;
1885  vlayout->addWidget(edit1_2);
1886  vlayout->addWidget(fontComboBox2);
1887  vlayout->addWidget(edit2_2);
1888 
1889  QGroupBox *box_2 = new QGroupBox("QGroupBox 2");
1890  box_2->setCheckable(true);
1891  box_2->setChecked(true);
1892  box_2->setLayout(vlayout);
1893 
1895  proxy->show();
1896 
1897  QGraphicsProxyWidget *proxy_2 = scene.addWidget(box_2);
1898  proxy_2->setPos(proxy->boundingRect().width() * 1.2, 0);
1899  proxy_2->show();
1900 
1901  QDial *leftDial = new QDial;
1902  QDial *rightDial = new QDial;
1904  view->setRenderHint(QPainter::Antialiasing);
1905  view->rotate(45);
1906  view->scale(0.5, 0.5);
1907 
1908  QWidget window;
1910  layout->addWidget(leftDial);
1911  layout->addWidget(view);
1912  layout->addWidget(rightDial);
1913  window.setLayout(layout);
1914 
1915  window.show();
1917  window.activateWindow();
1919 
1920  leftDial->setFocus();
1922  QTRY_VERIFY(leftDial->hasFocus());
1923 
1924  EventSpy eventSpy(edit1);
1925  EventSpy eventSpy2(edit2);
1926  EventSpy eventSpy3(fontComboBox);
1927  EventSpy eventSpy1_2(edit1_2);
1928  EventSpy eventSpy2_2(edit2_2);
1929  EventSpy eventSpy2_3(fontComboBox2);
1930  EventSpy eventSpyBox(box);
1931 
1932  // Tab into group box
1933  QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab);
1935  QVERIFY(!leftDial->hasFocus());
1936  QVERIFY(view->hasFocus());
1937  QVERIFY(view->viewport()->hasFocus());
1938  QVERIFY(scene.hasFocus());
1939  QVERIFY(proxy->hasFocus());
1940  QVERIFY(box->hasFocus());
1941 
1942  // Tab into line edit
1943  QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab);
1945  edit1->hasFocus();
1946  QVERIFY(!box->hasFocus());
1947  QCOMPARE(eventSpy.counts[QEvent::FocusIn], 1);
1948  QCOMPARE(eventSpy.counts[QEvent::FocusOut], 0);
1949 
1950  // Tab to the font combobox
1951  QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab);
1953  fontComboBox->hasFocus();
1954  QVERIFY(!edit2->hasFocus());
1955  QCOMPARE(eventSpy3.counts[QEvent::FocusIn], 1);
1956  QCOMPARE(eventSpy3.counts[QEvent::FocusOut], 0);
1957  QCOMPARE(eventSpy.counts[QEvent::FocusIn], 1);
1958  QCOMPARE(eventSpy.counts[QEvent::FocusOut], 1);
1959 
1960  // Tab into line edit 2
1961  QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab);
1963  edit2->hasFocus();
1964  QVERIFY(!edit1->hasFocus());
1965  QCOMPARE(eventSpy2.counts[QEvent::FocusIn], 1);
1966  QCOMPARE(eventSpy2.counts[QEvent::FocusOut], 0);
1967  QCOMPARE(eventSpy3.counts[QEvent::FocusOut], 1);
1968  QCOMPARE(eventSpy.counts[QEvent::FocusIn], 1);
1969  QCOMPARE(eventSpy.counts[QEvent::FocusOut], 1);
1970 
1971  // Tab into right box
1972  QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab);
1974  QVERIFY(!edit2->hasFocus());
1975  box_2->hasFocus();
1976 
1977  // Tab into right top line edit
1978  QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab);
1980  QVERIFY(!box_2->hasFocus());
1981  edit1_2->hasFocus();
1982  QCOMPARE(eventSpy1_2.counts[QEvent::FocusIn], 1);
1983  QCOMPARE(eventSpy1_2.counts[QEvent::FocusOut], 0);
1984 
1985  // Tab into right font combobox
1986  QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab);
1988  QVERIFY(!edit1_2->hasFocus());
1989  fontComboBox2->hasFocus();
1990  QCOMPARE(eventSpy1_2.counts[QEvent::FocusIn], 1);
1991  QCOMPARE(eventSpy1_2.counts[QEvent::FocusOut], 1);
1992  QCOMPARE(eventSpy2_3.counts[QEvent::FocusIn], 1);
1993  QCOMPARE(eventSpy2_3.counts[QEvent::FocusOut], 0);
1994 
1995  // Tab into right bottom line edit
1996  QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab);
1998  QVERIFY(!edit1_2->hasFocus());
1999  edit2_2->hasFocus();
2000  QCOMPARE(eventSpy1_2.counts[QEvent::FocusIn], 1);
2001  QCOMPARE(eventSpy1_2.counts[QEvent::FocusOut], 1);
2002  QCOMPARE(eventSpy2_3.counts[QEvent::FocusIn], 1);
2003  QCOMPARE(eventSpy2_3.counts[QEvent::FocusOut], 1);
2004  QCOMPARE(eventSpy2_2.counts[QEvent::FocusIn], 1);
2005  QCOMPARE(eventSpy2_2.counts[QEvent::FocusOut], 0);
2006 
2007  // Tab into right dial
2008  QTest::keyPress(QApplication::focusWidget(), Qt::Key_Tab);
2010  QVERIFY(!edit2->hasFocus());
2011  rightDial->hasFocus();
2012  QCOMPARE(eventSpy2_2.counts[QEvent::FocusOut], 1);
2013 
2014  // Backtab into line edit 2
2015  QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab);
2017  QVERIFY(!rightDial->hasFocus());
2018  edit2_2->hasFocus();
2019 
2020  // Backtab into the right font combobox
2021  QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab);
2023  QVERIFY(!edit2_2->hasFocus());
2024  fontComboBox2->hasFocus();
2025 
2026  // Backtab into line edit 1
2027  QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab);
2029  QVERIFY(!edit2_2->hasFocus());
2030  edit1_2->hasFocus();
2031 
2032  // Backtab into line box
2033  QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab);
2035  QVERIFY(!edit1_2->hasFocus());
2036  box_2->hasFocus();
2037 
2038  // Backtab into line edit 2
2039  QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab);
2041  QVERIFY(!rightDial->hasFocus());
2042  edit2->hasFocus();
2043 
2044  // Backtab into the font combobox
2045  QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab);
2047  QVERIFY(!edit2->hasFocus());
2048  fontComboBox->hasFocus();
2049 
2050  // Backtab into line edit 1
2051  QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab);
2053  QVERIFY(!fontComboBox->hasFocus());
2054  edit1->hasFocus();
2055 
2056  // Backtab into line box
2057  QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab);
2059  QVERIFY(!edit1->hasFocus());
2060  box->hasFocus();
2061 
2062  // Backtab into left dial
2063  QTest::keyPress(QApplication::focusWidget(), Qt::Key_Backtab);
2065  QVERIFY(!box->hasFocus());
2066  leftDial->hasFocus();
2067 
2068  delete view;
2069 }
2070 
2071 void tst_QGraphicsProxyWidget::setFocus_simpleWidget()
2072 {
2074  QLineEdit *edit = new QLineEdit;
2075  QGraphicsProxyWidget *editProxy = scene.addWidget(edit);
2076  editProxy->show();
2077 
2078  QDial *leftDial = new QDial;
2079  QDial *rightDial = new QDial;
2081 
2082  QWidget window;
2084  layout->addWidget(leftDial);
2085  layout->addWidget(view);
2086  layout->addWidget(rightDial);
2087  window.setLayout(layout);
2088 
2089  window.show();
2091  window.activateWindow();
2094 
2095  leftDial->setFocus();
2097  QTRY_VERIFY(leftDial->hasFocus());
2098 
2099  EventSpy eventSpy(edit);
2100 
2101  // Calling setFocus for the line edit when the view doesn't have input
2102  // focus does not steal focus from the dial. The edit, proxy and scene
2103  // have focus but only in their own little space.
2104  edit->setFocus();
2105  QVERIFY(scene.hasFocus());
2106  QVERIFY(edit->hasFocus());
2107  QVERIFY(!view->hasFocus());
2108  QVERIFY(!view->viewport()->hasFocus());
2109  QVERIFY(leftDial->hasFocus());
2110 
2111  // Clearing focus is a local operation.
2112  edit->clearFocus();
2113  QVERIFY(scene.hasFocus());
2114  QVERIFY(!edit->hasFocus());
2115  QVERIFY(!view->hasFocus());
2116  QVERIFY(leftDial->hasFocus());
2117 
2118  view->setFocus();
2119  QVERIFY(scene.hasFocus());
2120  QVERIFY(view->hasFocus());
2121  QVERIFY(!leftDial->hasFocus());
2122  QVERIFY(!edit->hasFocus());
2123 
2124  scene.clearFocus();
2125  QVERIFY(!scene.hasFocus());
2126 
2127  edit->setFocus();
2128  QVERIFY(scene.hasFocus());
2129  QVERIFY(edit->hasFocus());
2130  QVERIFY(editProxy->hasFocus());
2131 
2132  // Symmetry
2133  editProxy->clearFocus();
2134  QVERIFY(!edit->hasFocus());
2135 
2136  delete view;
2137 }
2138 
2139 void tst_QGraphicsProxyWidget::setFocus_simpleTwoWidgets()
2140 {
2142  QLineEdit *edit = new QLineEdit;
2143  QGraphicsProxyWidget *editProxy = scene.addWidget(edit);
2144  editProxy->show();
2145  QLineEdit *edit2 = new QLineEdit;
2146  QGraphicsProxyWidget *edit2Proxy = scene.addWidget(edit2);
2147  edit2Proxy->show();
2148  edit2Proxy->setPos(editProxy->boundingRect().width() * 1.01, 0);
2149 
2150  QDial *leftDial = new QDial;
2151  QDial *rightDial = new QDial;
2153 
2154  QWidget window;
2156  layout->addWidget(leftDial);
2157  layout->addWidget(view);
2158  layout->addWidget(rightDial);
2159  window.setLayout(layout);
2160 
2161  window.show();
2163  window.activateWindow();
2166 
2167  leftDial->setFocus();
2169  QTRY_VERIFY(leftDial->hasFocus());
2170 
2171  EventSpy eventSpy(edit);
2172 
2173  view->setFocus();
2174  QVERIFY(!edit->hasFocus());
2175 
2176  edit->setFocus();
2177  QVERIFY(scene.hasFocus());
2178  QVERIFY(edit->hasFocus());
2179  QVERIFY(editProxy->hasFocus());
2180 
2181  edit2->setFocus();
2182  QVERIFY(scene.hasFocus());
2183  QVERIFY(!edit->hasFocus());
2184  QVERIFY(!editProxy->hasFocus());
2185  QVERIFY(edit2->hasFocus());
2186  QVERIFY(edit2Proxy->hasFocus());
2187 
2188  delete view;
2189 }
2190 
2191 void tst_QGraphicsProxyWidget::setFocus_complexTwoWidgets()
2192 {
2193  // ### add event spies to this test.
2195 
2196  QLineEdit *edit1 = new QLineEdit;
2197  edit1->setText("QLineEdit 1");
2198  QLineEdit *edit2 = new QLineEdit;
2199  edit2->setText("QLineEdit 2");
2200  QVBoxLayout *vlayout = new QVBoxLayout;
2201  vlayout->addWidget(edit1);
2202  vlayout->addWidget(edit2);
2203 
2204  QGroupBox *box = new QGroupBox("QGroupBox");
2205  box->setCheckable(true);
2206  box->setChecked(true);
2207  box->setLayout(vlayout);
2208 
2209  QLineEdit *edit1_2 = new QLineEdit;
2210  edit1_2->setText("QLineEdit 1_2");
2211  QLineEdit *edit2_2 = new QLineEdit;
2212  edit2_2->setText("QLineEdit 2_2");
2213  vlayout = new QVBoxLayout;
2214  vlayout->addWidget(edit1_2);
2215  vlayout->addWidget(edit2_2);
2216 
2217  QGroupBox *box_2 = new QGroupBox("QGroupBox 2");
2218  box_2->setCheckable(true);
2219  box_2->setChecked(true);
2220  box_2->setLayout(vlayout);
2221 
2223  proxy->show();
2224 
2225  QGraphicsProxyWidget *proxy_2 = scene.addWidget(box_2);
2226  proxy_2->setPos(proxy->boundingRect().width() * 1.2, 0);
2227  proxy_2->show();
2228 
2229  QDial *leftDial = new QDial;
2230  QDial *rightDial = new QDial;
2232 
2233  QWidget window;
2235  layout->addWidget(leftDial);
2236  layout->addWidget(view);
2237  layout->addWidget(rightDial);
2238  window.setLayout(layout);
2239 
2240  window.show();
2242  window.activateWindow();
2245 
2246  leftDial->setFocus();
2248  QTRY_VERIFY(leftDial->hasFocus());
2249 
2250  EventSpy eventSpy(edit1);
2251  EventSpy eventSpy2(edit2);
2252  EventSpy eventSpyBox(box);
2253  EventSpy eventSpy_2(edit1_2);
2254  EventSpy eventSpy2_2(edit2_2);
2255  EventSpy eventSpyBox_2(box_2);
2256 
2257  view->setFocus();
2258 
2259  QCOMPARE(eventSpy.counts[QEvent::FocusIn], 0);
2260 
2261  edit1->setFocus();
2263  QVERIFY(scene.hasFocus());
2264  QVERIFY(edit1->hasFocus());
2265  QVERIFY(!box->hasFocus());
2266  QCOMPARE(eventSpy.counts[QEvent::FocusIn], 1);
2267  QCOMPARE(eventSpyBox.counts[QEvent::FocusIn], 0);
2268 
2269  edit2_2->setFocus();
2271  QVERIFY(!edit1->hasFocus());
2272  QVERIFY(!box_2->hasFocus());
2273  QVERIFY(edit2_2->hasFocus());
2274  QCOMPARE(eventSpy.counts[QEvent::FocusIn], 1);
2275  QCOMPARE(eventSpy.counts[QEvent::FocusOut], 1);
2276  QCOMPARE(eventSpy2_2.counts[QEvent::FocusIn], 1);
2277  QCOMPARE(eventSpyBox.counts[QEvent::FocusIn], 0);
2278  QCOMPARE(eventSpyBox_2.counts[QEvent::FocusIn], 0);
2279 
2280  box->setFocus();
2282  QVERIFY(!edit2_2->hasFocus());
2283  QVERIFY(!edit1->hasFocus());
2284  QVERIFY(box->hasFocus());
2285  QCOMPARE(eventSpy.counts[QEvent::FocusIn], 1);
2286  QCOMPARE(eventSpy.counts[QEvent::FocusOut], 1);
2287  QCOMPARE(eventSpy2_2.counts[QEvent::FocusIn], 1);
2288  QCOMPARE(eventSpy2_2.counts[QEvent::FocusOut], 1);
2289  QCOMPARE(eventSpyBox.counts[QEvent::FocusIn], 1);
2290  QCOMPARE(eventSpyBox.counts[QEvent::FocusOut], 0);
2291  QCOMPARE(eventSpyBox_2.counts[QEvent::FocusIn], 0);
2292  QCOMPARE(eventSpyBox_2.counts[QEvent::FocusOut], 0);
2293 
2294  edit2_2->setFocus();
2296  QVERIFY(edit2_2->hasFocus());
2297  QVERIFY(!edit1->hasFocus());
2298  QVERIFY(!box->hasFocus());
2299  QVERIFY(!box_2->hasFocus());
2300  QCOMPARE(eventSpy.counts[QEvent::FocusIn], 1);
2301  QCOMPARE(eventSpy.counts[QEvent::FocusOut], 1);
2302  QCOMPARE(eventSpy2_2.counts[QEvent::FocusIn], 2);
2303  QCOMPARE(eventSpy2_2.counts[QEvent::FocusOut], 1);
2304  QCOMPARE(eventSpyBox.counts[QEvent::FocusIn], 1);
2305  QCOMPARE(eventSpyBox.counts[QEvent::FocusOut], 1);
2306  QCOMPARE(eventSpyBox_2.counts[QEvent::FocusIn], 0);
2307  QCOMPARE(eventSpyBox_2.counts[QEvent::FocusOut], 0);
2308 
2309  delete view;
2310 }
2311 
2312 void tst_QGraphicsProxyWidget::popup_basic()
2313 {
2314  std::unique_ptr<QComboBox> boxGuard(new QComboBox);
2316  opt.initFrom(boxGuard.get());
2317  opt.editable = boxGuard->isEditable();
2318  if (boxGuard->style()->styleHint(QStyle::SH_ComboBox_Popup, &opt))
2319  QSKIP("Does not work due to SH_Combobox_Popup");
2320 
2321  // ProxyWidget should automatically create proxy's when the widget creates a child
2324  view.setAlignment(Qt::AlignLeft | Qt::AlignTop);
2325  view.setGeometry(0, 100, 480, 500);
2326  view.show();
2327 
2328  QComboBox *box = boxGuard.get();
2330  box->setGeometry(0, 0, 320, 40);
2331  box->addItems(QStringList() << "monday" << "tuesday" << "wednesday"
2332  << "thursday" << "saturday" << "sunday");
2333  QCOMPARE(proxy->childItems().count(), 0);
2334  proxy->setWidget(boxGuard.release());
2335  proxy->show();
2336  scene.addItem(proxy);
2337 
2338  QCOMPARE(box->pos(), QPoint());
2339  QCOMPARE(proxy->pos(), QPointF());
2340 
2342  QTest::qWait(125);
2344 
2345  QTest::mousePress(view.viewport(), Qt::LeftButton, {},
2346  view.mapFromScene(proxy->mapToScene(proxy->boundingRect().center())));
2347 
2348  QTRY_COMPARE(box->pos(), QPoint());
2349 
2350  QCOMPARE(proxy->childItems().count(), 1);
2351  QGraphicsProxyWidget *child = (QGraphicsProxyWidget*)(proxy->childItems())[0];
2352  QVERIFY(child->isWidget());
2353  QVERIFY(child->widget());
2354  QCOMPARE(child->widget()->parent(), box);
2355 
2356  QTRY_COMPARE(proxy->pos(), QPointF(box->pos()));
2357  QCOMPARE(child->x(), qreal(box->x()));
2358  QCOMPARE(child->y(), qreal(box->rect().bottom()));
2359 #ifndef Q_OS_WIN
2360  // The popup's coordinates on Windows are in global space,
2361  // regardless.
2362  QCOMPARE(child->widget()->x(), box->x());
2363  QCOMPARE(child->widget()->y(), box->rect().bottom());
2364  QCOMPARE(child->geometry().toRect(), child->widget()->geometry());
2365 #endif
2366  QTest::qWait(12);
2367 }
2368 
2369 void tst_QGraphicsProxyWidget::popup_subwidget()
2370 {
2371  QGroupBox *groupBox = new QGroupBox;
2372  groupBox->setTitle("GroupBox");
2373  groupBox->setCheckable(true);
2374 
2375  QComboBox *box = new QComboBox;
2376  box->addItems(QStringList() << "monday" << "tuesday" << "wednesday"
2377  << "thursday" << "saturday" << "sunday");
2378 
2380  layout->addWidget(new QLineEdit("QLineEdit"));
2381  layout->addWidget(box);
2382  layout->addWidget(new QLineEdit("QLineEdit"));
2384 
2386  QGraphicsProxyWidget *groupBoxProxy = scene.addWidget(groupBox);
2387  groupBox->show();
2388  groupBox->move(10000, 10000);
2389  QCOMPARE(groupBox->pos(), QPoint(10000, 10000));
2390  QCOMPARE(groupBoxProxy->pos(), QPointF(10000, 10000));
2391 
2393  view.show();
2395 
2396  box->showPopup();
2397 
2398  QVERIFY(!groupBoxProxy->childItems().isEmpty());
2399 
2401  opt.initFrom(box);
2402  opt.editable = box->isEditable();
2403  if (box->style()->styleHint(QStyle::SH_ComboBox_Popup, &opt))
2404  QSKIP("Does not work due to SH_Combobox_Popup");
2405  QGraphicsProxyWidget *child = (QGraphicsProxyWidget*)(groupBoxProxy->childItems())[0];
2406  QWidget *popup = child->widget();
2407  QCOMPARE(popup->parent(), static_cast<QObject*>(box));
2408  QCOMPARE(popup->x(), box->mapToGlobal(QPoint()).x());
2409  QCOMPARE(popup->y(), QRect(box->mapToGlobal(QPoint()), box->size()).bottom());
2410  QCOMPARE(popup->size(), child->size().toSize());
2411 }
2412 
2413 void tst_QGraphicsProxyWidget::changingCursor_basic()
2414 {
2415 #if !QT_CONFIG(cursor)
2416  QSKIP("This test requires the QCursor API");
2417 #else
2418  // Confirm that mouse events are working properly by checking that
2419  // when moving the mouse over a line edit it will change the cursor into the I
2422  view.show();
2424 
2426  QLineEdit *widget = new QLineEdit;
2427  proxy->setWidget(widget);
2428  QSignalSpy sceneChangedSpy(&scene, &QGraphicsScene::changed);
2429  scene.addItem(proxy);
2430  QTRY_VERIFY(sceneChangedSpy.count() > 0); // make sure the scene is ready
2431 
2432  // in
2433  QTest::mouseMove(view.viewport(), view.mapFromScene(proxy->mapToScene(proxy->boundingRect().center())));
2434  QTRY_COMPARE(view.viewport()->cursor().shape(), Qt::IBeamCursor);
2435 
2436  // out
2437  QTest::mouseMove(view.viewport(), QPoint(1, 1));
2438  QTRY_COMPARE(view.viewport()->cursor().shape(), Qt::ArrowCursor);
2439 #endif // !QT_CONFIG(cursor)
2440 }
2441 
2442 static bool findViewAndTipLabel(const QWidget *view)
2443 {
2444  bool foundView = false;
2445  bool foundTipLabel = false;
2446  const QWidgetList &topLevels = QApplication::topLevelWidgets();
2447  for (const QWidget *widget : topLevels) {
2448  if (widget == view)
2449  foundView = true;
2450  if (widget->inherits("QTipLabel"))
2451  foundTipLabel = true;
2452  if (foundView && foundTipLabel)
2453  return true;
2454  }
2455  return false;
2456 }
2457 
2458 void tst_QGraphicsProxyWidget::tooltip_basic()
2459 {
2460  QString toolTip = "Qt rocks!";
2461  QString toolTip2 = "Qt rocks even more!";
2462 
2463  QPushButton *button = new QPushButton("button");
2466  proxy->setWidget(button);
2467  proxyd->lastWidgetUnderMouse = button; // force widget under mouse
2468 
2469  QVERIFY(button->toolTip().isEmpty());
2470  QVERIFY(proxy->toolTip().isEmpty());
2471  // Check that setting the tooltip on the proxy also set it on the widget
2472  proxy->setToolTip(toolTip);
2473  QCOMPARE(proxy->toolTip(), toolTip);
2474  QCOMPARE(button->toolTip(), toolTip);
2475  // Check that setting the tooltip on the widget also set it on the proxy
2476  button->setToolTip(toolTip2);
2477  QCOMPARE(proxy->toolTip(), toolTip2);
2478  QCOMPARE(button->toolTip(), toolTip2);
2479 
2481  scene.addItem(proxy);
2482 
2484  view.setFixedSize(200, 200);
2485  view.show();
2488  {
2489  QHelpEvent helpEvent(QEvent::ToolTip, view.viewport()->rect().topLeft(),
2490  view.viewport()->mapToGlobal(view.viewport()->rect().topLeft()));
2491  QApplication::sendEvent(view.viewport(), &helpEvent);
2492  QTest::qWait(350);
2493 
2494  bool foundView = false;
2495  bool foundTipLabel = false;
2497  if (widget == &view)
2498  foundView = true;
2499  if (widget->inherits("QTipLabel"))
2500  foundTipLabel = true;
2501  }
2502  QVERIFY(foundView);
2503  QVERIFY(!foundTipLabel);
2504  }
2505 
2506  {
2507  QHelpEvent helpEvent(QEvent::ToolTip, view.mapFromScene(proxy->boundingRect().center()),
2508  view.viewport()->mapToGlobal(view.mapFromScene(proxy->boundingRect().center())));
2509  QApplication::sendEvent(view.viewport(), &helpEvent);
2510  QTRY_VERIFY(findViewAndTipLabel(&view));
2511  }
2512 }
2513 
2514 void tst_QGraphicsProxyWidget::childPos_data()
2515 {
2516  QTest::addColumn<bool>("moveCombo");
2517  QTest::addColumn<QPoint>("comboPos");
2518  QTest::addColumn<QPointF>("proxyPos");
2519 
2520  QTest::newRow("0") << true << QPoint() << QPointF();
2521  QTest::newRow("1") << true << QPoint(10, 0) << QPointF(10, 0);
2522  QTest::newRow("2") << true << QPoint(100, 0) << QPointF(100, 0);
2523  QTest::newRow("3") << true << QPoint(1000, 0) << QPointF(1000, 0);
2524  QTest::newRow("4") << true << QPoint(10000, 0) << QPointF(10000, 0);
2525  QTest::newRow("5") << true << QPoint(-10000, 0) << QPointF(-10000, 0);
2526  QTest::newRow("6") << true << QPoint(-1000, 0) << QPointF(-1000, 0);
2527  QTest::newRow("7") << true << QPoint(-100, 0) << QPointF(-100, 0);
2528  QTest::newRow("8") << true << QPoint(-10, 0) << QPointF(-10, 0);
2529  QTest::newRow("0-") << false << QPoint() << QPointF();
2530  QTest::newRow("1-") << false << QPoint(10, 0) << QPointF(10, 0);
2531  QTest::newRow("2-") << false << QPoint(100, 0) << QPointF(100, 0);
2532  QTest::newRow("3-") << false << QPoint(1000, 0) << QPointF(1000, 0);
2533  QTest::newRow("4-") << false << QPoint(10000, 0) << QPointF(10000, 0);
2534  QTest::newRow("5-") << false << QPoint(-10000, 0) << QPointF(-10000, 0);
2535  QTest::newRow("6-") << false << QPoint(-1000, 0) << QPointF(-1000, 0);
2536  QTest::newRow("7-") << false << QPoint(-100, 0) << QPointF(-100, 0);
2537  QTest::newRow("8-") << false << QPoint(-10, 0) << QPointF(-10, 0);
2538 }
2539 
2540 void tst_QGraphicsProxyWidget::childPos()
2541 {
2542  QFETCH(bool, moveCombo);
2543  QFETCH(QPoint, comboPos);
2544  QFETCH(QPointF, proxyPos);
2545 
2547 
2548  QComboBox box;
2549  box.addItem("Item 1");
2550  box.addItem("Item 2");
2551  box.addItem("Item 3");
2552  box.addItem("Item 4");
2553 
2554  if (moveCombo)
2555  box.move(comboPos);
2556 
2558  proxy->show();
2559  QVERIFY(proxy->isVisible());
2560  QVERIFY(box.isVisible());
2561 
2562  if (!moveCombo)
2563  proxy->setPos(proxyPos);
2564 
2565  QCOMPARE(proxy->pos(), proxyPos);
2566  QCOMPARE(box.pos(), comboPos);
2567 
2568  for (int i = 0; i < 2; ++i) {
2569  box.showPopup();
2570  QWidget *menu = box.findChild<QWidget *>();
2571  QVERIFY(menu);
2574 
2575  QCOMPARE(proxy->childItems().size(), 1);
2576  QGraphicsProxyWidget *proxyChild = qobject_cast<QGraphicsProxyWidget *>(
2577  static_cast<QGraphicsWidget *>(proxy->childItems().first()));
2578  QVERIFY(proxyChild);
2579  QVERIFY(proxyChild->isVisible());
2580  qreal expectedXPosition = 0.0;
2581 
2582  // The Mac style wants the popup to show up at QPoint(4 - 11, 1).
2583  // See QMacStyle::subControlRect SC_ComboBoxListBoxPopup.
2584  if (QApplication::style()->inherits("QMacStyle"))
2585  expectedXPosition = qreal(4 - 11);
2586 
2587  QTRY_COMPARE(proxyChild->pos().x(), expectedXPosition);
2588  menu->hide();
2589  }
2590 }
2591 
2592 void tst_QGraphicsProxyWidget::autoShow()
2593 {
2596 
2597  QGraphicsProxyWidget *proxy1 = scene.addWidget(new QPushButton("Button1"));
2598 
2599  QPushButton *button2 = new QPushButton("Button2");
2600  button2->hide();
2602  proxy2->setWidget(button2);
2603  scene.addItem(proxy2);
2604 
2605  view.show();
2607 
2608  QCOMPARE(proxy1->isVisible(), true);
2609  QCOMPARE(proxy2->isVisible(), false);
2610 
2611 }
2612 
2613 void tst_QGraphicsProxyWidget::windowOpacity()
2614 {
2617 
2618  QWidget *widget = new QWidget;
2619  widget->resize(100, 100);
2622 
2624  view.show();
2626  QVERIFY(view.isActiveWindow());
2627 
2628  qRegisterMetaType<QList<QRectF> >("QList<QRectF>");
2629  QSignalSpy signalSpy(&scene, SIGNAL(changed(QList<QRectF>)));
2630 
2631  EventSpy eventSpy(widget);
2632  QVERIFY(widget->isVisible());
2633 
2634  widget->setWindowOpacity(0.5);
2635 
2636  // Make sure setWindowOpacity triggers an update on the scene,
2637  // and not on the widget or the proxy itself. The entire proxy needs an update
2638  // in case it has a window decoration. Update: QGraphicsItem::CacheMode is
2639  // disabled on platforms without alpha channel support in QPixmap (e.g.,
2640  // X11 without XRender). On macOS, we always get a paint event.
2641  int paints = 0;
2642 #ifdef Q_OS_MACOS
2643  paints = 1;
2644 #endif
2645  QTRY_COMPARE(eventSpy.counts[QEvent::UpdateRequest], 0);
2646  QTRY_COMPARE(eventSpy.counts[QEvent::Paint], paints);
2647 
2648  QTRY_COMPARE(signalSpy.count(), 1);
2649  const QList<QVariant> arguments = signalSpy.takeFirst();
2650  const QList<QRectF> updateRects = qvariant_cast<QList<QRectF> >(arguments.at(0));
2651  QCOMPARE(updateRects.size(), 1);
2652  QCOMPARE(updateRects.at(0), proxy->sceneBoundingRect());
2653 }
2654 
2655 void tst_QGraphicsProxyWidget::stylePropagation()
2656 {
2657  QPointer<QStyle> windowsStyle = QStyleFactory::create("windows");
2658 
2659  QLineEdit *edit = new QLineEdit;
2661  proxy.setWidget(edit);
2662 
2663  EventSpy editSpy(edit);
2664  EventSpy proxySpy(&proxy);
2665 
2666  // Widget to proxy
2667  QCOMPARE(proxy.style(), QApplication::style());
2668  edit->setStyle(windowsStyle);
2669  QCOMPARE(editSpy.counts[QEvent::StyleChange], 1);
2670  QCOMPARE(proxySpy.counts[QEvent::StyleChange], 1);
2671  QCOMPARE(proxy.style(), (QStyle *)windowsStyle);
2672  edit->setStyle(0);
2673  QCOMPARE(editSpy.counts[QEvent::StyleChange], 2);
2674  QCOMPARE(proxySpy.counts[QEvent::StyleChange], 2);
2675  QCOMPARE(proxy.style(), QApplication::style());
2677  QVERIFY(windowsStyle); // not deleted
2678 
2679  // Proxy to widget
2680  proxy.setStyle(windowsStyle);
2681  QCOMPARE(editSpy.counts[QEvent::StyleChange], 3);
2682  QCOMPARE(proxySpy.counts[QEvent::StyleChange], 3);
2683  QCOMPARE(edit->style(), (QStyle *)windowsStyle);
2684  proxy.setStyle(0);
2685  QCOMPARE(editSpy.counts[QEvent::StyleChange], 4);
2686  QCOMPARE(proxySpy.counts[QEvent::StyleChange], 4);
2687  QCOMPARE(proxy.style(), QApplication::style());
2689  QVERIFY(windowsStyle); // not deleted
2690 
2691  delete windowsStyle;
2692 }
2693 
2694 void tst_QGraphicsProxyWidget::palettePropagation()
2695 {
2696  // Construct a palette with an unlikely setup
2697  QPalette lineEditPalette = QApplication::palette("QLineEdit");
2698  QPalette palette = lineEditPalette;
2699  palette.setBrush(QPalette::Text, Qt::red);
2700 
2701  QLineEdit *edit = new QLineEdit;
2703  proxy.setWidget(edit);
2704 
2705  EventSpy editSpy(edit);
2706  EventSpy proxySpy(&proxy);
2707 
2708  // Widget to proxy (no change)
2711  QCOMPARE(editSpy.counts[QEvent::PaletteChange], 1);
2712  QCOMPARE(proxySpy.counts[QEvent::PaletteChange], 0);
2714  QVERIFY(!proxy.testAttribute(Qt::WA_SetPalette));
2715  QCOMPARE(proxy.palette(), QPalette());
2716  edit->setPalette(QPalette());
2717  QCOMPARE(editSpy.counts[QEvent::PaletteChange], 2);
2718  QCOMPARE(proxySpy.counts[QEvent::PaletteChange], 0);
2720  QVERIFY(!proxy.testAttribute(Qt::WA_SetPalette));
2721  QCOMPARE(proxy.palette(), QPalette());
2722 
2723  // Proxy to widget
2724  proxy.setPalette(palette);
2725  QVERIFY(proxy.testAttribute(Qt::WA_SetPalette));
2726  QCOMPARE(editSpy.counts[QEvent::PaletteChange], 3);
2727  QCOMPARE(proxySpy.counts[QEvent::PaletteChange], 1);
2729  if (edit->palette() != palette)
2730  QEXPECT_FAIL("", "Test case fails unless run alone", Abort);
2732  QCOMPARE(edit->palette(), proxy.palette());
2734  proxy.setPalette(QPalette());
2735  QVERIFY(!proxy.testAttribute(Qt::WA_SetPalette));
2737  QCOMPARE(editSpy.counts[QEvent::PaletteChange], 4);
2738  QCOMPARE(proxySpy.counts[QEvent::PaletteChange], 2);
2739  QCOMPARE(edit->palette(), lineEditPalette);
2740 }
2741 
2742 void tst_QGraphicsProxyWidget::fontPropagation()
2743 {
2744  // Construct a font with an unlikely setup
2746  QFont lineEditFont = QApplication::font("QLineEdit");
2747  QFont font = lineEditFont;
2748  font.setPointSize(43);
2749 
2750  QLineEdit *edit = new QLineEdit;
2752  proxy.setWidget(edit);
2753 
2754  scene.addItem(&proxy);
2755  EventSpy editSpy(edit);
2756  EventSpy proxySpy(&proxy);
2757 
2758  // Widget to proxy (no change)
2760  edit->setFont(font);
2761  QCOMPARE(editSpy.counts[QEvent::FontChange], 1);
2762  QCOMPARE(proxySpy.counts[QEvent::FontChange], 0);
2764  QVERIFY(!proxy.testAttribute(Qt::WA_SetFont));
2765  QCOMPARE(proxy.font(), lineEditFont);
2766  edit->setFont(QFont());
2767  QCOMPARE(editSpy.counts[QEvent::FontChange], 2);
2768  QCOMPARE(proxySpy.counts[QEvent::FontChange], 0);
2770  QVERIFY(!proxy.testAttribute(Qt::WA_SetFont));
2771  QCOMPARE(proxy.font(), lineEditFont);
2772 
2773  // Proxy to widget
2774  proxy.setFont(font);
2775  QApplication::processEvents(); // wait for QEvent::Polish
2776  QVERIFY(proxy.testAttribute(Qt::WA_SetFont));
2777  QCOMPARE(editSpy.counts[QEvent::FontChange], 3);
2778  QCOMPARE(proxySpy.counts[QEvent::FontChange], 1);
2780  QCOMPARE(edit->font(), font);
2781  QCOMPARE(edit->font(), proxy.font());
2782  QCOMPARE(edit->font().pointSize(), 43);
2783  proxy.setFont(QFont());
2784  QVERIFY(!proxy.testAttribute(Qt::WA_SetFont));
2786  QCOMPARE(editSpy.counts[QEvent::FontChange], 4);
2787  QCOMPARE(proxySpy.counts[QEvent::FontChange], 2);
2788  QCOMPARE(edit->font(), lineEditFont);
2789 
2790  proxy.setFont(font);
2791  QLineEdit *edit2 = new QLineEdit;
2792  proxy.setWidget(edit2);
2793  delete edit;
2794  QCOMPARE(edit2->font().pointSize(), 43);
2795 }
2796 
2797 class MainWidget : public QMainWindow
2798 {
2799 Q_OBJECT
2800 public:
2802  view = new QGraphicsView(this);
2803  scene = new QGraphicsScene(this);
2804  item = new QGraphicsWidget();
2807  layout->addItem(widget);
2808  item->setLayout(layout);
2809  button = new QPushButton("Push me");
2812  scene->addItem(item);
2813  view->setScene(scene);
2815  layout->activate();
2816  }
2823 };
2824 
2825 void tst_QGraphicsProxyWidget::dontCrashWhenDie()
2826 {
2827  MainWidget *w = new MainWidget();
2828  w->show();
2830 
2831  QTest::mouseMove(w->view->viewport(), w->view->mapFromScene(w->widget->mapToScene(w->widget->boundingRect().center())));
2832  delete w->item;
2833 
2835  delete w;
2836  // This leaves an invisible proxy widget behind.
2838 }
2839 
2840 void tst_QGraphicsProxyWidget::dontCrashNoParent() // QTBUG-15442
2841 {
2844  QScopedPointer<QLabel> label0(new QLabel);
2845  QScopedPointer<QLabel> label1(new QLabel);
2846 
2847  child->setParentItem(parent);
2848  // Set the first label as the proxied widget.
2849  parent->setWidget(label0.data());
2850  // If we attempt to change the proxied widget we get a crash.
2851  parent->setWidget(label1.data());
2852 }
2853 
2854 void tst_QGraphicsProxyWidget::createProxyForChildWidget()
2855 {
2857 
2858  QLineEdit *edit1 = new QLineEdit;
2859  edit1->setText("QLineEdit 1");
2860  QLineEdit *edit2 = new QLineEdit;
2861  edit2->setText("QLineEdit 2");
2862  QCheckBox *checkbox = new QCheckBox("QCheckBox");
2863  QVBoxLayout *vlayout = new QVBoxLayout;
2864 
2865  vlayout->addWidget(edit1);
2866  vlayout->addWidget(edit2);
2867  vlayout->addWidget(checkbox);
2868  vlayout->insertStretch(-1);
2869 
2870  QGroupBox *box = new QGroupBox("QGroupBox");
2871  box->setCheckable(true);
2872  box->setChecked(true);
2873  box->setLayout(vlayout);
2874 
2875  QDial *leftDial = new QDial;
2876  QDial *rightDial = new QDial;
2877 
2878  QWidget window;
2880  layout->addWidget(leftDial);
2881  layout->addWidget(box);
2882  layout->addWidget(rightDial);
2883  window.setLayout(layout);
2884 
2885  QVERIFY(!window.graphicsProxyWidget());
2886  QVERIFY(!checkbox->graphicsProxyWidget());
2887 
2888  QGraphicsProxyWidget *windowProxy = scene.addWidget(&window);
2890  view.show();
2891  view.resize(500,500);
2892 
2893  QCOMPARE(window.graphicsProxyWidget(), windowProxy);
2894  QVERIFY(!box->graphicsProxyWidget());
2895  QVERIFY(!checkbox->graphicsProxyWidget());
2896 
2898 
2899  QGraphicsProxyWidget *boxProxy = box->graphicsProxyWidget();
2900 
2901  QVERIFY(boxProxy);
2902  QCOMPARE(checkbox->graphicsProxyWidget(), checkboxProxy.data());
2903  QCOMPARE(checkboxProxy->parentItem(), boxProxy);
2904  QCOMPARE(boxProxy->parentItem(), windowProxy);
2905 
2906  QVERIFY(checkboxProxy->mapToScene(QPointF()) == checkbox->mapTo(&window, QPoint()));
2907  QCOMPARE(checkboxProxy->size().toSize(), checkbox->size());
2908  QCOMPARE(boxProxy->size().toSize(), box->size());
2909 
2910  window.resize(500,500);
2911  QCOMPARE(windowProxy->size().toSize(), QSize(500,500));
2912  QVERIFY(checkboxProxy->mapToScene(QPointF()) == checkbox->mapTo(&window, QPoint()));
2913  QCOMPARE(checkboxProxy->size().toSize(), checkbox->size());
2914  QCOMPARE(boxProxy->size().toSize(), box->size());
2915 
2916  QTest::qWait(10);
2917 
2918 
2919  QSignalSpy spy(checkbox, SIGNAL(clicked()));
2920 
2921  QTest::mousePress(view.viewport(), Qt::LeftButton, {},
2922  view.mapFromScene(checkboxProxy->mapToScene(QPointF(8,8))));
2923  QTRY_COMPARE(spy.count(), 0);
2924  QTest::mouseRelease(view.viewport(), Qt::LeftButton, {},
2925  view.mapFromScene(checkboxProxy->mapToScene(QPointF(8,8))));
2926  QTRY_COMPARE(spy.count(), 1);
2927 
2928 
2929 
2930  boxProxy->setWidget(0);
2931 
2932  QVERIFY(!checkbox->graphicsProxyWidget());
2933  QVERIFY(!box->graphicsProxyWidget());
2934  QVERIFY(checkboxProxy.isNull());
2935 
2936  delete boxProxy;
2937 }
2938 
2939 #ifndef QT_NO_CONTEXTMENU
2941 {
2942  Q_OBJECT
2943 public:
2945  : QLabel(QStringLiteral("ContextMenuWidget"))
2946  , embeddedPopup(false)
2948  , m_embeddedPopupSet(false)
2949  , m_timer(0)
2950  { }
2953 protected:
2954  bool event(QEvent *event) override
2955  {
2956  if (event->type() == QEvent::ContextMenu) {
2957  if (!m_timer) {
2958  m_timer = new QTimer(this);
2959  m_timer->setInterval(10);
2960  connect(m_timer, SIGNAL(timeout()), this, SLOT(checkMenu()));
2961  m_timer->start();
2962  }
2963  }
2964  return QWidget::event(event);
2965  }
2967  {
2968  gotContextMenuEvent = true;
2969  }
2970 
2971 private slots:
2972  void checkMenu()
2973  {
2974  QMenu *menu = findChild<QMenu *>();
2975  if (!m_embeddedPopupSet) {
2976  m_embeddedPopupSet = true;
2977  embeddedPopup = menu != 0;
2978  }
2979  if (menu && menu->isVisible())
2980  menu->hide();
2981  hide();
2982  }
2983 
2984 private:
2985  bool m_embeddedPopupSet;
2986  QTimer *m_timer;
2987 };
2988 #endif // QT_NO_CONTEXTMENU
2989 
2990 void tst_QGraphicsProxyWidget::actionsContextMenu_data()
2991 {
2992  QTest::addColumn<bool>("actionsContextMenu");
2993  QTest::addColumn<bool>("hasFocus");
2994 
2995  QTest::newRow("without actionsContextMenu and with focus") << false << true;
2996  QTest::newRow("without actionsContextMenu and without focus") << false << false;
2997  QTest::newRow("with actionsContextMenu and focus") << true << true;
2998  QTest::newRow("with actionsContextMenu without focus") << true << false;
2999 }
3000 
3001 #ifndef QT_NO_CONTEXTMENU
3002 void tst_QGraphicsProxyWidget::actionsContextMenu()
3003 {
3004  QFETCH(bool, hasFocus);
3005  QFETCH(bool, actionsContextMenu);
3006 
3008  if (actionsContextMenu) {
3009  widget->addAction(new QAction("item 1", widget));
3010  widget->addAction(new QAction("item 2", widget));
3011  widget->addAction(new QAction("item 3", widget));
3013  }
3015  QGraphicsProxyWidget *proxyWidget = scene.addWidget(widget);
3017  view.setWindowTitle(QStringLiteral("actionsContextMenu"));
3018  view.resize(200, 200);
3019  view.move(QGuiApplication::primaryScreen()->geometry().center() - QPoint(100, 100));
3020  view.show();
3023  view.setFocus();
3024  QTRY_VERIFY(view.hasFocus());
3025 
3026  if (hasFocus)
3027  proxyWidget->setFocus();
3028  else
3029  proxyWidget->clearFocus();
3030 
3032 
3034  view.viewport()->rect().center(),
3035  view.viewport()->mapToGlobal(view.viewport()->rect().center()));
3036  contextMenuEvent.accept();
3037  qApp->sendEvent(view.viewport(), &contextMenuEvent);
3038 
3039  if (hasFocus) {
3040  if (actionsContextMenu) {
3041  //actionsContextMenu embedded popup but no contextMenuEvent (widget has focus)
3042  QVERIFY(widget->embeddedPopup);
3043  QVERIFY(!widget->gotContextMenuEvent);
3044  } else {
3045  //no embedded popup but contextMenuEvent (widget has focus)
3046  QVERIFY(!widget->embeddedPopup);
3047  QVERIFY(widget->gotContextMenuEvent);
3048  }
3049  } else {
3050  //qgraphicsproxywidget doesn't have the focus, the widget must not receive any contextMenuEvent and must not create any QMenu
3051  QVERIFY(!widget->embeddedPopup);
3052  QVERIFY(!widget->gotContextMenuEvent);
3053  }
3054 
3055 }
3056 #endif // QT_NO_CONTEXTMENU
3057 
3058 void tst_QGraphicsProxyWidget::deleteProxyForChildWidget()
3059 {
3060  QDialog dialog;
3061  dialog.resize(320, 120);
3062  dialog.move(80, 40);
3063 
3065  scene.setSceneRect(QRectF(0, 0, 640, 480));
3067  QComboBox *combo = new QComboBox;
3068  combo->addItems(QStringList() << "red" << "green" << "blue" << "white" << "black" << "yellow" << "cyan" << "magenta");
3070  dialog.layout()->addWidget(combo);
3071 
3073  view.show();
3074 
3075  proxy->setWidget(0);
3076  //just don't crash
3078  delete combo;
3079 }
3080 
3081 void tst_QGraphicsProxyWidget::bypassGraphicsProxyWidget_data()
3082 {
3083  QTest::addColumn<bool>("bypass");
3084 
3085  QTest::newRow("autoembed") << false;
3086  QTest::newRow("bypass") << true;
3087 }
3088 
3089 void tst_QGraphicsProxyWidget::bypassGraphicsProxyWidget()
3090 {
3091  QFETCH(bool, bypass);
3092 
3093  std::unique_ptr<QWidget> widgetGuard(new QWidget);
3094  QWidget *widget = widgetGuard.get();
3095  widget->resize(100, 100);
3096 
3099  view.show();
3102 
3103  QGraphicsProxyWidget *proxy = scene.addWidget(widgetGuard.release());
3104 
3105  QCOMPARE(proxy->widget(), widget);
3106  QVERIFY(proxy->childItems().isEmpty());
3107 
3108  Qt::WindowFlags flags;
3109  flags |= Qt::Dialog;
3110  if (bypass)
3114  dialog->show();
3116 
3117  QCOMPARE(proxy->childItems().size(), bypass ? 0 : 1);
3118  if (!bypass)
3119  QCOMPARE(((QGraphicsProxyWidget *)proxy->childItems().first())->widget(), (QWidget *)dialog);
3120 
3121  dialog->hide();
3123 }
3124 
3125 static void makeDndEvent(QGraphicsSceneDragDropEvent *event, QGraphicsView *view, const QPointF &pos)
3126 {
3127  event->setScenePos(pos);
3128  event->setScreenPos(view->mapToGlobal(view->mapFromScene(pos)));
3129  event->setButtons(Qt::LeftButton);
3130  event->setModifiers({});
3131  event->setPossibleActions(Qt::CopyAction);
3132  event->setProposedAction(Qt::CopyAction);
3133  event->setDropAction(Qt::CopyAction);
3134  event->setWidget(view->viewport());
3135  event->setSource(view->viewport());
3136  event->ignore();
3137 }
3138 
3139 void tst_QGraphicsProxyWidget::dragDrop()
3140 {
3141  QPushButton *button = new QPushButton; // acceptDrops(false)
3142  QLineEdit *edit = new QLineEdit; // acceptDrops(true)
3143 
3145  QGraphicsProxyWidget *buttonProxy = scene.addWidget(button);
3146  QGraphicsProxyWidget *editProxy = scene.addWidget(edit);
3147  QVERIFY(buttonProxy->acceptDrops());
3148  QVERIFY(editProxy->acceptDrops());
3149 
3150  button->setGeometry(0, 0, 100, 50);
3151  edit->setGeometry(0, 60, 100, 50);
3152 
3154  view.show();
3156 
3157  QMimeData data;
3158  data.setText("hei");
3159  {
3161  makeDndEvent(&event, &view, QPointF(50, 25));
3162  event.setMimeData(&data);
3163  qApp->sendEvent(&scene, &event);
3164  QVERIFY(event.isAccepted());
3165  }
3166  {
3168  makeDndEvent(&event, &view, QPointF(50, 25));
3169  event.setMimeData(&data);
3170  qApp->sendEvent(&scene, &event);
3171  QVERIFY(!event.isAccepted());
3172  }
3173  {
3175  makeDndEvent(&event, &view, QPointF(50, 75));
3176  event.setMimeData(&data);
3177  qApp->sendEvent(&scene, &event);
3178  QVERIFY(event.isAccepted());
3179  }
3180  {
3182  makeDndEvent(&event, &view, QPointF(50, 75));
3183  event.setMimeData(&data);
3184  qApp->sendEvent(&scene, &event);
3185  QVERIFY(event.isAccepted());
3186  }
3187  QCOMPARE(edit->text(), QString("hei"));
3188 }
3189 
3190 void tst_QGraphicsProxyWidget::windowFlags_data()
3191 {
3192  QTest::addColumn<int>("proxyFlags");
3193  QTest::addColumn<int>("widgetFlags");
3194  QTest::addColumn<int>("resultingProxyFlags");
3195  QTest::addColumn<int>("resultingWidgetFlags");
3196 
3197  QTest::newRow("proxy(0) widget(0)") << 0 << 0 << 0 << int(Qt::Window);
3198  QTest::newRow("proxy(window)") << int(Qt::Window) << 0 << int(Qt::Window) << int(Qt::Window);
3199  QTest::newRow("proxy(window) widget(window)") << int(Qt::Window) << int(Qt::Window) << int(Qt::Window) << int(Qt::Window);
3200  QTest::newRow("proxy(0) widget(window)") << int(0) << int(Qt::Window) << int(0) << int(Qt::Window);
3201 }
3202 
3203 void tst_QGraphicsProxyWidget::windowFlags()
3204 {
3205  QFETCH(int, proxyFlags);
3206  QFETCH(int, widgetFlags);
3207  QFETCH(int, resultingProxyFlags);
3208  QFETCH(int, resultingWidgetFlags);
3209  Qt::WindowFlags proxyWFlags = Qt::WindowFlags(proxyFlags);
3210  Qt::WindowFlags widgetWFlags = Qt::WindowFlags(widgetFlags);
3211  Qt::WindowFlags resultingProxyWFlags = Qt::WindowFlags(resultingProxyFlags);
3212  Qt::WindowFlags resultingWidgetWFlags = Qt::WindowFlags(resultingWidgetFlags);
3213 
3214  QGraphicsProxyWidget proxy(0, proxyWFlags);
3215  QVERIFY((proxy.windowFlags() & proxyWFlags) == proxyWFlags);
3216 
3217  QWidget *widget = new QWidget(0, widgetWFlags);
3218  QVERIFY((widget->windowFlags() & widgetWFlags) == widgetWFlags);
3219 
3220  proxy.setWidget(widget);
3221 
3222  if (resultingProxyFlags == 0)
3223  QVERIFY(!proxy.windowFlags());
3224  else
3225  QVERIFY((proxy.windowFlags() & resultingProxyWFlags) == resultingProxyWFlags);
3226  QVERIFY((widget->windowFlags() & resultingWidgetWFlags) == resultingWidgetWFlags);
3227 }
3228 
3229 void tst_QGraphicsProxyWidget::comboboxWindowFlags()
3230 {
3231  QComboBox *comboBox = new QComboBox;
3232  comboBox->addItem("Item 1");
3233  comboBox->addItem("Item 2");
3234  comboBox->addItem("Item 3");
3235  QWidget *embedWidget = comboBox;
3236 
3238  QGraphicsProxyWidget *proxy = scene.addWidget(embedWidget);
3239  proxy->setWindowFlags(Qt::Window);
3240  QVERIFY(embedWidget->isWindow());
3241  QVERIFY(proxy->isWindow());
3242 
3243  comboBox->showPopup();
3244 
3245  QCOMPARE(proxy->childItems().size(), 1);
3246  QGraphicsItem *popupProxy = proxy->childItems().first();
3247  QVERIFY(popupProxy->isWindow());
3248  QVERIFY((static_cast<QGraphicsWidget *>(popupProxy)->windowFlags() & Qt::Popup) == Qt::Popup);
3249 }
3250 
3251 void tst_QGraphicsProxyWidget::updateAndDelete()
3252 {
3253 #ifdef Q_OS_MAC
3254  QSKIP("Test case unstable on this platform, QTBUG-23700");
3255 #endif
3257  QGraphicsProxyWidget *proxy = scene.addWidget(new QPushButton("Hello World"));
3258  View view(&scene);
3259  view.show();
3261  QTRY_VERIFY(view.npaints > 0);
3262  // Wait a bit to clear all pending paint events
3263  QTest::qWait(10);
3264 
3265  const QRect itemDeviceBoundingRect = proxy->deviceTransform(view.viewportTransform())
3266  .mapRect(proxy->boundingRect()).toRect();
3267  const QRegion expectedRegion = itemDeviceBoundingRect.adjusted(-2, -2, 2, 2);
3268 
3269  view.npaints = 0;
3270  view.paintEventRegion = QRegion();
3271 
3272  // Update and hide.
3273  proxy->update();
3274  proxy->hide();
3275  QTRY_COMPARE(view.npaints, 1);
3276  QCOMPARE(view.paintEventRegion, expectedRegion);
3277 
3278  proxy->show();
3279  QTest::qWait(50);
3280  view.npaints = 0;
3281  view.paintEventRegion = QRegion();
3282 
3283  // Update and delete.
3284  proxy->update();
3285  delete proxy;
3286  QTRY_COMPARE(view.npaints, 1);
3287  QCOMPARE(view.paintEventRegion, expectedRegion);
3288 }
3289 
3291 {
3292  bool event(QEvent *e) override
3293  {
3294  if (e->type() == QEvent::InputMethod)
3296  return QLineEdit::event(e);
3297  }
3298 public:
3300 };
3301 
3302 void tst_QGraphicsProxyWidget::inputMethod()
3303 {
3305 
3306  // check that the proxy is initialized with the correct input method sensitivity
3307  for (int i = 0; i < 2; ++i)
3308  {
3309  QLineEdit *lineEdit = new QLineEdit;
3313  }
3314 
3315  // check that input method events are only forwarded to widgets with focus
3316  for (int i = 0; i < 2; ++i)
3317  {
3321 
3322  if (i)
3323  lineEdit->setFocus();
3324 
3325  lineEdit->inputMethodEvents = 0;
3327  qApp->sendEvent(proxy, &event);
3328  QCOMPARE(lineEdit->inputMethodEvents, i);
3329  }
3330 
3331  scene.clear();
3333  QWidget *w = new QWidget;
3334  w->setLayout(new QVBoxLayout(w));
3335  QLineEdit *lineEdit = new QLineEdit;
3337  w->layout()->addWidget(lineEdit);
3340  view.show();
3343  lineEdit->setFocus();
3345 }
3346 
3347 void tst_QGraphicsProxyWidget::clickFocus()
3348 {
3351  QLineEdit *le1 = new QLineEdit;
3353 
3355 
3356  {
3357  EventSpy proxySpy(proxy);
3358  EventSpy widgetSpy(proxy->widget());
3359 
3360  view.setFrameStyle(0);
3361  view.resize(300, 300);
3362  view.show();
3365 
3366  QVERIFY(!proxy->hasFocus());
3367  QVERIFY(!proxy->widget()->hasFocus());
3368 
3369  QCOMPARE(proxySpy.counts[QEvent::FocusIn], 0);
3370  QCOMPARE(proxySpy.counts[QEvent::FocusOut], 0);
3371  QCOMPARE(widgetSpy.counts[QEvent::FocusIn], 0);
3372  QCOMPARE(widgetSpy.counts[QEvent::FocusOut], 0);
3373 
3374  QPointF lineEditCenter = proxy->mapToScene(proxy->boundingRect().center());
3375  // Spontaneous mouse click sets focus on a clickable widget.
3376  for (int retry = 0; retry < 50 && !proxy->hasFocus(); retry++)
3377  QTest::mouseClick(view.viewport(), Qt::LeftButton, {}, view.mapFromScene(lineEditCenter));
3378  QVERIFY(proxy->hasFocus());
3379  QVERIFY(proxy->widget()->hasFocus());
3380  QCOMPARE(proxySpy.counts[QEvent::FocusIn], 1);
3381  QCOMPARE(widgetSpy.counts[QEvent::FocusIn], 1);
3382 
3383  scene.setFocusItem(0);
3384  QVERIFY(!proxy->hasFocus());
3385  QVERIFY(!proxy->widget()->hasFocus());
3386  QCOMPARE(proxySpy.counts[QEvent::FocusOut], 1);
3387  QCOMPARE(widgetSpy.counts[QEvent::FocusOut], 1);
3388 
3389  // Non-spontaneous mouse click sets focus if the widget has been clicked before
3390  {
3392  event.setScenePos(lineEditCenter);
3393  event.setButton(Qt::LeftButton);
3394  qApp->sendEvent(&scene, &event);
3395  QVERIFY(proxy->hasFocus());
3396  QVERIFY(proxy->widget()->hasFocus());
3397  QCOMPARE(proxySpy.counts[QEvent::FocusIn], 2);
3398  QCOMPARE(widgetSpy.counts[QEvent::FocusIn], 2);
3399  }
3400  }
3401 
3402  scene.setFocusItem(0);
3403  proxy->setWidget(new QLineEdit); // resets focusWidget
3404  delete le1;
3405 
3406  {
3407  QPointF lineEditCenter = proxy->mapToScene(proxy->boundingRect().center());
3408  EventSpy proxySpy(proxy);
3409  EventSpy widgetSpy(proxy->widget());
3410  QVERIFY(!proxy->hasFocus());
3411  QVERIFY(!proxy->widget()->hasFocus());
3412  QCOMPARE(proxySpy.counts[QEvent::FocusOut], 0);
3413  QCOMPARE(widgetSpy.counts[QEvent::FocusOut], 0);
3414 
3415  // Non-spontaneous mouse click does not set focus on the embedded widget.
3416  {
3418  event.setScenePos(lineEditCenter);
3419  event.setButton(Qt::LeftButton);
3420  qApp->sendEvent(&scene, &event);
3421  QVERIFY(!proxy->hasFocus());
3422  QVERIFY(!proxy->widget()->hasFocus());
3423  QCOMPARE(proxySpy.counts[QEvent::FocusIn], 0);
3424  QCOMPARE(widgetSpy.counts[QEvent::FocusIn], 0);
3425  }
3426 
3427  scene.setFocusItem(0);
3428  QVERIFY(!proxy->hasFocus());
3429  QVERIFY(!proxy->widget()->hasFocus());
3430  QCOMPARE(proxySpy.counts[QEvent::FocusOut], 0);
3431  QCOMPARE(widgetSpy.counts[QEvent::FocusOut], 0);
3432 
3433  // Spontaneous click on non-clickable widget does not give focus.
3434  proxy->widget()->setFocusPolicy(Qt::NoFocus);
3435  QTest::mouseClick(view.viewport(), Qt::LeftButton, {}, view.mapFromScene(lineEditCenter));
3436  QVERIFY(!proxy->hasFocus());
3437  QVERIFY(!proxy->widget()->hasFocus());
3438 
3439  // Multiple clicks should only result in one FocusIn.
3440  proxy->widget()->setFocusPolicy(Qt::StrongFocus);
3441  scene.setFocusItem(0);
3442  QVERIFY(!proxy->hasFocus());
3443  QVERIFY(!proxy->widget()->hasFocus());
3444  QTest::mouseClick(view.viewport(), Qt::LeftButton, {}, view.mapFromScene(lineEditCenter));
3445  QTest::mouseClick(view.viewport(), Qt::LeftButton, {}, view.mapFromScene(lineEditCenter));
3446  QVERIFY(proxy->hasFocus());
3447  QVERIFY(proxy->widget()->hasFocus());
3448  QCOMPARE(widgetSpy.counts[QEvent::FocusIn], 1);
3449  QCOMPARE(proxySpy.counts[QEvent::FocusIn], 1);
3450  }
3451 }
3452 
3453 void tst_QGraphicsProxyWidget::windowFrameMargins()
3454 {
3455  // Make sure the top margin is non-zero when passing Qt::Window.
3457 
3458  qreal left, top, right, bottom;
3459  proxy->getWindowFrameMargins(&left, &top, &right, &bottom);
3460  QVERIFY(top > 0);
3461 
3462  proxy->setWidget(new QPushButton("testtest"));
3463  proxy->getWindowFrameMargins(&left, &top, &right, &bottom);
3464  QVERIFY(top > 0);
3465 
3467  scene.addItem(proxy);
3468  proxy->getWindowFrameMargins(&left, &top, &right, &bottom);
3469  QVERIFY(top > 0);
3470 
3471  proxy->unsetWindowFrameMargins();
3472  proxy->getWindowFrameMargins(&left, &top, &right, &bottom);
3473  QVERIFY(top > 0);
3474 }
3475 
3476 void tst_QGraphicsProxyWidget::QTBUG_6986_sendMouseEventToAlienWidget()
3477 {
3478  struct HoverButton : public QPushButton
3479  {
3481  bool hoverLeaveReceived = false;
3482 
3483  bool event(QEvent* e) override
3484  {
3485  if (QEvent::HoverLeave == e->type())
3486  hoverLeaveReceived = true;
3487  return QPushButton::event(e);
3488  }
3489  };
3490 
3492  QWidget *background = new QWidget;
3493  background->setGeometry(0, 0, 500, 500);
3494  HoverButton *hoverButton = new HoverButton(background);
3495  hoverButton->setText("Second button");
3496  hoverButton->setGeometry(10, 10, 200, 50);
3497  scene.addWidget(background);
3498 
3499  QPushButton *hideButton = new QPushButton("I'm a button with a very very long text");
3500  hideButton->setGeometry(10, 10, 400, 50);
3501  QGraphicsProxyWidget *topButton = scene.addWidget(hideButton);
3502  connect(hideButton, &QPushButton::clicked, &scene, [&]() { topButton->hide(); });
3503  topButton->setFocus();
3504 
3506  view.resize(600, 600);
3508  view.show();
3510 
3511  QPoint topButtonTopLeftCorner = view.mapFromScene(topButton->scenePos());
3512  QTest::mouseClick(view.viewport(), Qt::LeftButton, {}, topButtonTopLeftCorner);
3513  // move to the bottom right corner (buttons are placed in the top left corner)
3514  QCOMPARE(hoverButton->hoverLeaveReceived, false);
3515  QTest::mouseMove(view.viewport(), view.viewport()->rect().bottomRight());
3516  if (QGuiApplication::platformName() == QLatin1String("cocoa")) {
3517  // The "Second button" does not receive QEvent::HoverLeave
3518  QEXPECT_FAIL("", "This test fails only on Cocoa. Investigate why. See QTBUG-69219", Continue);
3519  }
3520  QTRY_COMPARE(hoverButton->hoverLeaveReceived, true);
3521 }
3522 
3523 static QByteArray msgPointMismatch(const QPoint &actual, const QPoint &expected)
3524 {
3525  QString result;
3526  QDebug(&result) << actual << " != " << expected << " manhattanLength="
3527  << (expected - actual).manhattanLength();
3528  return result.toLocal8Bit();
3529 }
3530 
3531 void tst_QGraphicsProxyWidget::mapToGlobal() // QTBUG-41135
3532 {
3533  const QRect availableGeometry = QGuiApplication::primaryScreen()->availableGeometry();
3534  const QSize size = availableGeometry.size() / 4;
3537  view.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
3538  view.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
3539  view.setTransform(QTransform::fromScale(2, 2)); // QTBUG-50136, use transform.
3540  view.setWindowTitle(QTest::currentTestFunction());
3541  view.resize(size);
3542  view.move(availableGeometry.bottomRight() - QPoint(size.width(), size.height()) - QPoint(100, 100));
3543  QWidget *embeddedWidget = new QGroupBox(QLatin1String("Embedded"));
3544  embeddedWidget->setStyleSheet(QLatin1String("background-color: \"yellow\"; "));
3545  embeddedWidget->setFixedSize((size - QSize(10, 10)) / 2);
3546  QWidget *childWidget = new QGroupBox(QLatin1String("Child"), embeddedWidget);
3547  childWidget->setStyleSheet(QLatin1String("background-color: \"red\"; "));
3548  childWidget->resize(embeddedWidget->size() / 2);
3549  childWidget->move(embeddedWidget->width() / 4, embeddedWidget->height() / 4); // center in embeddedWidget
3550  scene.addWidget(embeddedWidget);
3552  view.show();
3554  const QPoint embeddedCenter = embeddedWidget->rect().center();
3555  const QPoint embeddedCenterGlobal = embeddedWidget->mapToGlobal(embeddedCenter);
3556  QCOMPARE(embeddedWidget->mapFromGlobal(embeddedCenterGlobal), embeddedCenter);
3557  // This should be equivalent to the view center give or take rounding
3558  // errors due to odd window margins
3559  const QPoint viewCenter = view.geometry().center();
3560  QVERIFY2((viewCenter - embeddedCenterGlobal).manhattanLength() <= 3,
3561  msgPointMismatch(embeddedCenterGlobal, viewCenter).constData());
3562 
3563  // Same test with child centered on embeddedWidget. Also make sure
3564  // the roundtrip maptoGlobal()/mapFromGlobal() returns the same
3565  // point since that is important for mouse event handling (QTBUG-50030,
3566  // QTBUG-50136).
3567  const QPoint childCenter = childWidget->rect().center();
3568  const QPoint childCenterGlobal = childWidget->mapToGlobal(childCenter);
3569  QCOMPARE(childWidget->mapFromGlobal(childCenterGlobal), childCenter);
3570  QVERIFY2((viewCenter - childCenterGlobal).manhattanLength() <= 4,
3571  msgPointMismatch(childCenterGlobal, viewCenter).constData());
3572 }
3573 
3574 void tst_QGraphicsProxyWidget::mapToGlobalWithoutScene() // QTBUG-44509
3575 {
3576  QGraphicsProxyWidget proxyWidget;
3577  QWidget *embeddedWidget = new QWidget;
3578  proxyWidget.setWidget(embeddedWidget);
3579  const QPoint localPos(0, 0);
3580  const QPoint globalPos = embeddedWidget->mapToGlobal(localPos);
3581  QCOMPARE(embeddedWidget->mapFromGlobal(globalPos), localPos);
3582 }
3583 
3584 // QTBUG_43780: Embedded widgets have isWindow()==true but showing them should not
3585 // trigger the top-level widget code path of show() that closes all popups
3586 // (for example combo popups).
3587 void tst_QGraphicsProxyWidget::QTBUG_43780_visibility()
3588 {
3589  const QRect availableGeometry = QGuiApplication::primaryScreen()->availableGeometry();
3590  const QSize size = availableGeometry.size() / 4;
3593  QComboBox *combo = new QComboBox(&mainWindow);
3594  combo->addItems(QStringList() << "i1" << "i2" << "i3");
3595  layout->addWidget(combo);
3598  layout->addWidget(view);
3601  mainWindow.move(availableGeometry.topLeft()
3602  + QPoint(availableGeometry.width() - size.width(),
3603  availableGeometry.height() - size.height()) / 2);
3605  scene->addWidget(label);
3606  label->hide();
3607  mainWindow.show();
3608  combo->setFocus();
3611  combo->showPopup();
3612  QWidget *comboPopup = combo->view()->window();
3613  QVERIFY(comboPopup);
3615  label->show();
3616  QTRY_VERIFY(label->isVisible());
3617  QVERIFY(comboPopup->isVisible());
3618 }
3619 
3620 class TouchWidget : public QWidget
3621 {
3622 public:
3624 
3625  bool event(QEvent *event) override
3626  {
3627  switch (event->type()) {
3628  case QEvent::TouchBegin:
3629  case QEvent::TouchUpdate:
3630  case QEvent::TouchEnd:
3631  event->accept();
3632  return true;
3633  default:
3634  break;
3635  }
3636 
3637  return QWidget::event(event);
3638  }
3639 };
3640 
3641 #if QT_CONFIG(wheelevent)
3654 void tst_QGraphicsProxyWidget::wheelEventPropagation()
3655 {
3656  QGraphicsScene scene(0, 0, 600, 600);
3657 
3658  QWidget *label = new QLabel("Direct");
3659  label->setFixedSize(300, 30);
3660  QGraphicsProxyWidget *labelProxy = scene.addWidget(label);
3661  labelProxy->setPos(0, 50);
3662  labelProxy->show();
3663 
3664  class NestedWidget : public QWidget
3665  {
3666  public:
3667  NestedWidget(const QString &text)
3668  {
3669  setObjectName("Nested Label");
3670  QLabel *nested = new QLabel(text);
3671  QHBoxLayout *hbox = new QHBoxLayout;
3672  hbox->addWidget(nested);
3673  setLayout(hbox);
3674  }
3675 
3676  int wheelEventCount = 0;
3677  protected:
3678  void wheelEvent(QWheelEvent *) override
3679  {
3680  ++wheelEventCount;
3681  }
3682  };
3683  NestedWidget *nestedWidget = new NestedWidget("Nested");
3684  nestedWidget->setFixedSize(300, 60);
3685  QGraphicsProxyWidget *nestedProxy = scene.addWidget(nestedWidget);
3686  nestedProxy->setPos(0, 120);
3687  nestedProxy->show();
3688 
3690  view.setFixedHeight(200);
3691  view.show();
3692 
3694  QVERIFY(view.verticalScrollBar()->isVisible());
3695 
3696  view.verticalScrollBar()->setValue(0);
3697  QSignalSpy scrollSpy(view.verticalScrollBar(), &QScrollBar::valueChanged);
3698 
3699  const QPoint wheelPosition(50, 25);
3700  auto wheelUp = [&view, wheelPosition](Qt::ScrollPhase phase) {
3701  const QPoint global = view.mapToGlobal(wheelPosition);
3702  const QPoint pixelDelta(0, -25);
3703  const QPoint angleDelta(0, -120);
3704  QWindowSystemInterface::handleWheelEvent(view.windowHandle(), wheelPosition, global,
3705  pixelDelta, angleDelta, Qt::NoModifier,
3706  phase);
3708  };
3709 
3710  int scrollCount = 0;
3711  // test non-kinetic events; they are not grabbed, and should scroll the view unless
3712  // accepted by the embedded widget
3713  QCOMPARE(view.itemAt(wheelPosition), nullptr);
3714  wheelUp(Qt::NoScrollPhase);
3715  QCOMPARE(scrollSpy.count(), ++scrollCount);
3716 
3717  // wheeling on the label, which ignores the event, should scroll the view
3718  QCOMPARE(view.itemAt(wheelPosition), labelProxy);
3719  wheelUp(Qt::NoScrollPhase);
3720  QCOMPARE(scrollSpy.count(), ++scrollCount);
3721  QCOMPARE(view.itemAt(wheelPosition), labelProxy);
3722  wheelUp(Qt::NoScrollPhase);
3723  QCOMPARE(scrollSpy.count(), ++scrollCount);
3724 
3725  // left the widget
3726  QCOMPARE(view.itemAt(wheelPosition), nullptr);
3727  wheelUp(Qt::NoScrollPhase);
3728  QCOMPARE(scrollSpy.count(), ++scrollCount);
3729 
3730  // reached the nested widget, which accepts the wheel event, so no more scrolling
3731  QCOMPARE(view.itemAt(wheelPosition), nestedProxy);
3732  // remember this position for later
3733  const int scrollBarValueOnNestedProxy = view.verticalScrollBar()->value();
3734  wheelUp(Qt::NoScrollPhase);
3735  QCOMPARE(scrollSpy.count(), scrollCount);
3736  QCOMPARE(nestedWidget->wheelEventCount, 1);
3737 
3738  // reset, try with kinetic events
3739  view.verticalScrollBar()->setValue(0);
3740  ++scrollCount;
3741 
3742  // starting a scroll outside any widget and scrolling through the widgets should work,
3743  // no matter if the widget accepts wheel events - the view has the grab
3744  QCOMPARE(view.itemAt(wheelPosition), nullptr);
3745  wheelUp(Qt::ScrollBegin);
3746  QCOMPARE(scrollSpy.count(), ++scrollCount);
3747  for (int i = 0; i < 5; ++i) {
3748  wheelUp(Qt::ScrollUpdate);
3749  QCOMPARE(scrollSpy.count(), ++scrollCount);
3750  }
3751  wheelUp(Qt::ScrollEnd);
3752  QCOMPARE(scrollSpy.count(), ++scrollCount);
3753 
3754  // reset
3755  view.verticalScrollBar()->setValue(0);
3756  scrollCount = scrollSpy.count();
3757 
3758  // starting a scroll on a widget that doesn't accept wheel events
3759  // should also scroll the view, which still gets the grab
3760  wheelUp(Qt::NoScrollPhase);
3761  scrollCount = scrollSpy.count();
3762 
3763  QCOMPARE(view.itemAt(wheelPosition), labelProxy);
3764  wheelUp(Qt::ScrollBegin);
3765  QCOMPARE(scrollSpy.count(), ++scrollCount);
3766  for (int i = 0; i < 5; ++i) {
3767  wheelUp(Qt::ScrollUpdate);
3768  QCOMPARE(scrollSpy.count(), ++scrollCount);
3769  }
3770  wheelUp(Qt::ScrollEnd);
3771  QCOMPARE(scrollSpy.count(), ++scrollCount);
3772 
3773  // starting a scroll on a widget that does accept wheel events
3774  // should not scroll the view
3775  view.verticalScrollBar()->setValue(scrollBarValueOnNestedProxy);
3776  scrollCount = scrollSpy.count();
3777 
3778  QCOMPARE(view.itemAt(wheelPosition), nestedProxy);
3779  wheelUp(Qt::ScrollBegin);
3780  QCOMPARE(scrollSpy.count(), scrollCount);
3781 }
3782 #endif // QT_CONFIG(wheelevent)
3783 
3784 // QTBUG_45737
3785 void tst_QGraphicsProxyWidget::forwardTouchEvent()
3786 {
3788 
3791 
3793  proxy->setAcceptTouchEvents(true);
3794 
3796  view.show();
3798 
3799  EventSpy eventSpy(widget);
3800 
3802 
3803  QVERIFY(device);
3804  QCOMPARE(eventSpy.counts[QEvent::TouchBegin], 0);
3805  QCOMPARE(eventSpy.counts[QEvent::TouchUpdate], 0);
3806  QCOMPARE(eventSpy.counts[QEvent::TouchEnd], 0);
3807 
3808  QTest::touchEvent(&view, device).press(0, QPoint(10, 10), &view);
3809  QTest::touchEvent(&view, device).move(0, QPoint(15, 15), &view);
3810  QTest::touchEvent(&view, device).move(0, QPoint(16, 16), &view);
3811  QTest::touchEvent(&view, device).release(0, QPoint(15, 15), &view);
3812 
3814 
3815  QCOMPARE(eventSpy.counts[QEvent::TouchBegin], 1);
3816  QCOMPARE(eventSpy.counts[QEvent::TouchUpdate], 2);
3817  QCOMPARE(eventSpy.counts[QEvent::TouchEnd], 1);
3818 }
3819 
3820 void tst_QGraphicsProxyWidget::touchEventPropagation()
3821 {
3822  QGraphicsScene scene(0, 0, 300, 200);
3823  QWidget *simpleWidget = new QWidget;
3824  simpleWidget->setObjectName("simpleWidget");
3825  simpleWidget->setAttribute(Qt::WA_AcceptTouchEvents, true);
3826  QGraphicsProxyWidget *simpleProxy = scene.addWidget(simpleWidget);
3827  simpleProxy->setAcceptTouchEvents(true);
3828  simpleProxy->setGeometry(QRectF(0, 0, 30, 30));
3829 
3830  QWidget *formWidget = new QWidget;
3831  formWidget->setObjectName("formWidget");
3832  formWidget->setAttribute(Qt::WA_AcceptTouchEvents, true);
3833  QPushButton *pushButton1 = new QPushButton("One");
3834  pushButton1->setObjectName("pushButton1");
3835  pushButton1->setAttribute(Qt::WA_AcceptTouchEvents, true);
3836  QPushButton *pushButton2 = new QPushButton("Two");
3837  pushButton2->setObjectName("pushButton2");
3838  pushButton2->setAttribute(Qt::WA_AcceptTouchEvents, true);
3839  TouchWidget *touchWidget1 = new TouchWidget;
3840  touchWidget1->setObjectName("touchWidget1");
3841  touchWidget1->setAttribute(Qt::WA_AcceptTouchEvents, true);
3842  touchWidget1->setFixedSize(pushButton1->sizeHint());
3843  TouchWidget *touchWidget2 = new TouchWidget;
3844  touchWidget2->setObjectName("touchWidget2");
3845  touchWidget2->setAttribute(Qt::WA_AcceptTouchEvents, true);
3846  touchWidget2->setFixedSize(pushButton2->sizeHint());
3847  QVBoxLayout *vbox = new QVBoxLayout;
3848  vbox->addWidget(pushButton1);
3849  vbox->addWidget(pushButton2);
3850  vbox->addWidget(touchWidget1);
3851  vbox->addWidget(touchWidget2);
3852  formWidget->setLayout(vbox);
3853  QGraphicsProxyWidget *formProxy = scene.addWidget(formWidget);
3854  formProxy->setAcceptTouchEvents(true);
3855  formProxy->setGeometry(QRectF(50, 50, 200, 160));
3856 
3858  view.setFixedSize(scene.width(), scene.height());
3859  view.verticalScrollBar()->setValue(0);
3860  view.horizontalScrollBar()->setValue(0);
3861  view.viewport()->setObjectName("GraphicsView's Viewport");
3862  view.show();
3864 
3865  class TouchEventSpy : public QObject
3866  {
3867  public:
3868  using QObject::QObject;
3869 
3870  struct TouchRecord {
3871  QObject *receiver;
3872  QEvent::Type eventType;
3873  QPointF position;
3874  };
3876  QWidget *mousePressReceiver = nullptr;
3877 
3878  int count(int id = 0) const { return records.value(id).count(); }
3879  TouchRecord at(int i, int id = 0) const { return records.value(id).at(i); }
3880  void clear()
3881  {
3882  records.clear();
3883  mousePressReceiver = nullptr;
3884  }
3885  protected:
3886  bool eventFilter(QObject *receiver, QEvent *event) override
3887  {
3888  switch (event->type()) {
3889  case QEvent::TouchBegin:
3890  case QEvent::TouchUpdate:
3891  case QEvent::TouchCancel:
3892  case QEvent::TouchEnd: {
3893  QTouchEvent *touchEvent = static_cast<QTouchEvent *>(event);
3894  // instead of detaching each QEventPoint, just store the relative positions
3895  for (const auto &touchPoint : touchEvent->points())
3896  records[touchPoint.id()] << TouchRecord{receiver, event->type(), touchPoint.position()};
3897  qCDebug(lcTests) << "Recording" << event << receiver;
3898  break;
3899  }
3901  mousePressReceiver = qobject_cast<QWidget*>(receiver);
3902  break;
3903  default:
3904  break;
3905  }
3906  return QObject::eventFilter(receiver, event);
3907  }
3908  } eventSpy;
3909  qApp->installEventFilter(&eventSpy);
3910 
3911  auto touchDevice = QTest::createTouchDevice();
3912  const QPointF simpleCenter = simpleProxy->geometry().center();
3913 
3914  // On systems without double conversion we might get different rounding behavior.
3915  // One pixel off in any direction is acceptable for this test.
3916  constexpr auto closeEnough = [](QPointF exp, QPointF act) -> bool {
3917  const QRectF expArea(exp - QPointF(1., 1.), exp + QPointF(1., 1.));
3918  const bool contains = expArea.contains(act);
3919  if (!contains)
3920  qWarning() << act << "not in" << exp;
3921  return contains;
3922  };
3923 
3924  // verify that the embedded widget gets the correctly translated event
3925  QTest::touchEvent(&view, touchDevice).press(0, simpleCenter.toPoint());
3926  // window, viewport, scene, simpleProxy, simpleWidget
3927  QCOMPARE(eventSpy.count(), 5);
3928  QCOMPARE(eventSpy.at(0).receiver, view.windowHandle());
3929  QCOMPARE(eventSpy.at(1).receiver, view.viewport());
3930  QCOMPARE(eventSpy.at(2).receiver, &scene);
3931  QCOMPARE(eventSpy.at(3).receiver, simpleProxy);
3932  auto record = eventSpy.at(4);
3933  QCOMPARE(record.receiver, simpleWidget);
3934  QCOMPARE(record.eventType, QEvent::TouchBegin);
3935  QVERIFY(closeEnough(record.position, simpleCenter));
3936  eventSpy.clear();
3937 
3938  // verify that the layout of formWidget is how we expect it to be
3939  QCOMPARE(formWidget->childAt(QPoint(5, 5)), nullptr);
3940  const QPoint pb1Center = pushButton1->rect().center();
3941  QCOMPARE(formWidget->childAt(pushButton1->pos() + pb1Center), pushButton1);
3942  const QPoint pb2Center = pushButton2->rect().center();
3943  QCOMPARE(formWidget->childAt(pushButton2->pos() + pb2Center), pushButton2);
3944  const QPoint tw1Center = touchWidget1->rect().center();
3945  QCOMPARE(formWidget->childAt(touchWidget1->pos() + tw1Center), touchWidget1);
3946  const QPoint tw2Center = touchWidget2->rect().center();
3947  QCOMPARE(formWidget->childAt(touchWidget2->pos() + tw2Center), touchWidget2);
3948 
3949  // touch events are sent to the view, in view coordinates
3950  const QPoint formProxyPox = view.mapFromScene(formProxy->pos().toPoint());
3951  const QPoint pb1TouchPos = pushButton1->pos() + pb1Center + formProxyPox;
3952  const QPoint pb2TouchPos = pushButton2->pos() + pb2Center + formProxyPox;
3953  const QPoint tw1TouchPos = touchWidget1->pos() + tw1Center + formProxyPox;
3954  const QPoint tw2TouchPos = touchWidget2->pos() + tw2Center + formProxyPox;
3955 
3956  QSignalSpy clickedSpy(pushButton1, &QPushButton::clicked);
3957  // Single touch point to nested widget not accepting event.
3958  // Event should bubble up and translate correctly, TouchUpdate and TouchEnd events
3959  // stop at the window since nobody accepted the TouchBegin. A mouse event will be generated.
3960  QTest::touchEvent(&view, touchDevice).press(0, pb1TouchPos);
3961  QTest::touchEvent(&view, touchDevice).move(0, pb1TouchPos + QPoint(1, 1));
3962  QTest::touchEvent(&view, touchDevice).release(0, pb1TouchPos + QPoint(1, 1));
3963  // ..., formProxy, pushButton1, formWidget, window, window
3964  QCOMPARE(eventSpy.count(), 8);
3965  QCOMPARE(eventSpy.at(3).receiver, formProxy); // formProxy dispatches to the right subwidget
3966  record = eventSpy.at(4);
3967  QCOMPARE(record.receiver, pushButton1);
3968  QVERIFY(closeEnough(record.position, pb1Center));
3969  QCOMPARE(record.eventType, QEvent::TouchBegin);
3970  // pushButton doesn't accept the point, so the TouchBegin propagates to parent
3971  record = eventSpy.at(5);
3972  QCOMPARE(record.receiver, formWidget);
3973  QVERIFY(closeEnough(record.position, pushButton1->pos() + pb1Center));
3974  QCOMPARE(record.eventType, QEvent::TouchBegin);
3975  record = eventSpy.at(6);
3976  QCOMPARE(record.receiver, view.windowHandle());
3977  QCOMPARE(record.eventType, QEvent::TouchUpdate);
3978  record = eventSpy.at(7);
3979  QCOMPARE(record.receiver, view.windowHandle());
3980  QCOMPARE(record.eventType, QEvent::TouchEnd);
3981  QCOMPARE(eventSpy.mousePressReceiver, pushButton1);
3982  QCOMPARE(clickedSpy.count(), 1);
3983  eventSpy.clear();
3984  clickedSpy.clear();
3985 
3986  // Single touch point to nested widget accepting event.
3987  QTest::touchEvent(&view, touchDevice).press(0, tw1TouchPos);
3988  QTest::touchEvent(&view, touchDevice).move(0, tw1TouchPos + QPoint(5, 5));
3989  QTest::touchEvent(&view, touchDevice).release(0, tw1TouchPos + QPoint(5, 5));
3990  // Press: ..., formProxy, touchWidget1 (5)
3991  // Move: window, touchWidget1 (2)
3992  // Release: window, touchWidget1 (2)
3993  QCOMPARE(eventSpy.count(), 9);
3994  QCOMPARE(eventSpy.at(3).receiver, formProxy); // form proxy dispatches TouchBegin to the right widget
3995  record = eventSpy.at(4);
3996  QCOMPARE(record.receiver, touchWidget1);
3997  QVERIFY(closeEnough(record.position, tw1Center));
3998  QCOMPARE(record.eventType, QEvent::TouchBegin);
3999  QCOMPARE(eventSpy.at(5).receiver, view.windowHandle()); // QWidgetWindow dispatches TouchUpdate
4000  record = eventSpy.at(6);
4001  QCOMPARE(record.receiver, touchWidget1);
4002  QVERIFY(closeEnough(record.position, tw1Center + QPoint(5, 5)));
4003  QCOMPARE(record.eventType, QEvent::TouchUpdate);
4004  QCOMPARE(eventSpy.at(7).receiver, view.windowHandle()); // QWidgetWindow dispatches TouchEnd
4005  record = eventSpy.at(8);
4006  QCOMPARE(record.receiver, touchWidget1);
4007  QVERIFY(closeEnough(record.position, tw1Center + QPoint(5, 5)));
4008  QCOMPARE(record.eventType, QEvent::TouchEnd);
4009  eventSpy.clear();
4010 
4011  // to simplify the remaining test, install the event spy explicitly on the target widgets
4012  qApp->removeEventFilter(&eventSpy);
4013  formWidget->installEventFilter(&eventSpy);
4014  pushButton1->installEventFilter(&eventSpy);
4015  pushButton2->installEventFilter(&eventSpy);
4016  touchWidget1->installEventFilter(&eventSpy);
4017  touchWidget2->installEventFilter(&eventSpy);
4018 
4019  // multi-touch to different widgets, some do and some don't accept the event
4020  QTest::touchEvent(&view, touchDevice)
4021  .press(0, pb1TouchPos)
4022  .press(1, tw1TouchPos)
4023  .press(2, pb2TouchPos)
4024  .press(3, tw2TouchPos);
4025  QTest::touchEvent(&view, touchDevice)
4026  .move(0, pb1TouchPos + QPoint(1, 1))
4027  .move(1, tw1TouchPos + QPoint(1, 1))
4028  .move(2, pb2TouchPos + QPoint(1, 1))
4029  .move(3, tw2TouchPos + QPoint(1, 1));
4030  QTest::touchEvent(&view, touchDevice)
4031  .release(0, pb1TouchPos + QPoint(1, 1))
4032  .release(1, tw1TouchPos + QPoint(1, 1))
4033  .release(2, pb2TouchPos + QPoint(1, 1))
4034  .release(3, tw2TouchPos + QPoint(1, 1));
4035 
4036  QCOMPARE(eventSpy.count(0), 2); // Begin never accepted, so move up and then stop
4037  QCOMPARE(eventSpy.count(1), 3); // Begin accepted, so not propagated and update/end received
4038  QCOMPARE(eventSpy.count(2), 2); // Begin never accepted
4039  QCOMPARE(eventSpy.count(3), 3); // Begin accepted
4040  QCOMPARE(eventSpy.at(0, 0).receiver, pushButton1);
4041  QCOMPARE(eventSpy.at(1, 0).receiver, formWidget);
4042  QCOMPARE(eventSpy.at(0, 1).receiver, touchWidget1);
4043  QCOMPARE(eventSpy.at(1, 1).receiver, touchWidget1);
4044  QCOMPARE(eventSpy.at(2, 1).receiver, touchWidget1);
4045  QCOMPARE(eventSpy.at(0, 2).receiver, pushButton2);
4046  QCOMPARE(eventSpy.at(1, 2).receiver, formWidget);
4047  QCOMPARE(eventSpy.at(0, 3).receiver, touchWidget2);
4048  QCOMPARE(eventSpy.at(1, 3).receiver, touchWidget2);
4049  QCOMPARE(eventSpy.at(2, 3).receiver, touchWidget2);
4050  QCOMPARE(clickedSpy.count(), 0); // multi-touch event does not synthesize a mouse event
4051 }
4052 
4054 #include "tst_qgraphicsproxywidget.moc"
small capitals from c petite p scientific i
[1]
Definition: afcover.h:80
Arabic default style
Definition: afstyles.h:94
bool event(QEvent *event) override
void contextMenuEvent(QContextMenuEvent *) override
void leaveEvent(QEvent *event) override
bool eventFilter(QObject *object, QEvent *event) override
void enterEvent(QEnterEvent *event) override
void mouseMoveEvent(QMouseEvent *event) override
QMap< QEvent::Type, int > counts
bool eventFilter(QObject *, QEvent *event) override
EventSpy(QObject *receiver)
QGraphicsLinearLayout * layout
QGraphicsProxyWidget * widget
QGraphicsScene * scene
QGraphicsWidget * item
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override
void clicked(bool checked=false)
void valueChanged(int value)
The QAction class provides an abstraction for user commands that can be added to different user inter...
Definition: qaction.h:65
static QStyle * style()
static QPalette palette()
static QWidget * focusWidget()
static QWidgetList topLevelWidgets()
static QFont font()
static void setEffectEnabled(Qt::UIEffect, bool enable=true)
static QWidget * activeWindow()
static void setActiveWindow(QWidget *act)
void insertStretch(int index, int stretch=0)
Definition: qboxlayout.cpp:892
void addWidget(QWidget *, int stretch=0, Qt::Alignment alignment=Qt::Alignment())
The QByteArray class provides an array of bytes.
Definition: qbytearray.h:85
The QCheckBox widget provides a checkbox with a text label.
Definition: qcheckbox.h:55
The QColor class provides colors based on RGB, HSV or CMYK values.
Definition: qcolor.h:67
The QComboBox widget is a combined button and popup list.
Definition: qcombobox.h:60
QAbstractItemView * view() const
Definition: qcombobox.cpp:2439
void addItem(const QString &text, const QVariant &userData=QVariant())
Definition: qcombobox.h:260
void addItems(const QStringList &texts)
Definition: qcombobox.h:171
virtual void showPopup()
Definition: qcombobox.cpp:2580
The QContextMenuEvent class contains parameters that describe a context menu event....
Definition: qevent.h:665
static bool sendEvent(QObject *receiver, QEvent *event)
static void processEvents(QEventLoop::ProcessEventsFlags flags=QEventLoop::AllEvents)
static void setAttribute(Qt::ApplicationAttribute attribute, bool on=true)
[95]
Qt::CursorShape shape() const
Definition: qcursor.cpp:535
The QDebug class provides an output stream for debugging information.
Definition: qdebug.h:65
The QDial class provides a rounded range control (like a speedometer or potentiometer).
Definition: qdial.h:56
The QDialog class is the base class of dialog windows.
Definition: qdialog.h:55
The QEnterEvent class contains parameters that describe an enter event.
Definition: qevent.h:197
The QEvent class is the base class of all event classes. Event objects contain event parameters.
Definition: qcoreevent.h:58
@ None
Definition: qcoreevent.h:71
@ EnabledChange
Definition: qcoreevent.h:147
@ ToolTip
Definition: qcoreevent.h:160
@ Hide
Definition: qcoreevent.h:90
@ GraphicsSceneDragEnter
Definition: qcoreevent.h:211
@ GraphicsSceneDragMove
Definition: qcoreevent.h:212
@ FocusOut
Definition: qcoreevent.h:80
@ InputMethod
Definition: qcoreevent.h:133
@ GraphicsSceneMousePress
Definition: qcoreevent.h:203
@ StyleChange
Definition: qcoreevent.h:149
@ FontChange
Definition: qcoreevent.h:146
@ KeyPress
Definition: qcoreevent.h:77
@ Show
Definition: qcoreevent.h:89
@ Paint
Definition: qcoreevent.h:84
@ Resize
Definition: qcoreevent.h:86
@ FocusIn
Definition: qcoreevent.h:79
@ TouchCancel
Definition: qcoreevent.h:277
@ MouseButtonPress
Definition: qcoreevent.h:73
@ TouchEnd
Definition: qcoreevent.h:256
@ TouchUpdate
Definition: qcoreevent.h:255
@ TouchBegin
Definition: qcoreevent.h:254
@ HoverLeave
Definition: qcoreevent.h:189
@ HoverEnter
Definition: qcoreevent.h:188
@ WindowActivate
Definition: qcoreevent.h:96
@ GraphicsSceneWheel
Definition: qcoreevent.h:215
@ UpdateRequest
Definition: qcoreevent.h:126
@ GraphicsSceneDrop
Definition: qcoreevent.h:214
@ PaletteChange
Definition: qcoreevent.h:107
@ HoverMove
Definition: qcoreevent.h:190
@ Move
Definition: qcoreevent.h:85
@ ContextMenu
Definition: qcoreevent.h:132
The QFileDialog class provides a dialog that allow users to select files or directories....
Definition: qfiledialog.h:64
@ DontUseNativeDialog
Definition: qfiledialog.h:88
void setOption(Option option, bool on=true)
The QFocusEvent class contains event parameters for widget focus events. \inmodule QtGui.
Definition: qevent.h:520
The QFontComboBox widget is a combobox that lets the user select a font family.
Definition: qfontcombobox.h:54
The QFont class specifies a query for a font used for drawing text.
Definition: qfont.h:56
void setPointSize(int)
Definition: qfont.cpp:1006
int pointSize() const
Definition: qfont.cpp:899
The QGraphicsItem class is the base class for all graphical items in a QGraphicsScene.
Definition: qgraphicsitem.h:83
QPointF scenePos() const
bool acceptDrops() const
bool isWindow() const
QList< QGraphicsItem * > childItems() const
void setPos(const QPointF &pos)
bool hasFocus() const
QGraphicsItem * parentItem() const
bool isVisible() const
void setAcceptTouchEvents(bool enabled)
void setFocus(Qt::FocusReason focusReason=Qt::OtherFocusReason)
static const QGraphicsItemPrivate * get(const QGraphicsItem *item)
The QGraphicsLinearLayout class provides a horizontal or vertical layout for managing widgets in Grap...
void addItem(QGraphicsLayoutItem *item)
QPointF pos
the position of the item
The QGraphicsProxyWidget class provides a proxy layer for embedding a QWidget in a QGraphicsScene.
QGraphicsProxyWidget * createProxyForChildWidget(QWidget *child)
QGraphicsProxyWidget(QGraphicsItem *parent=nullptr, Qt::WindowFlags wFlags=Qt::WindowFlags())
void setWidget(QWidget *widget)
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override
bool eventFilter(QObject *object, QEvent *event) override
void setGeometry(const QRectF &rect) override
void focusOutEvent(QFocusEvent *event) override
QPointer< QWidget > lastWidgetUnderMouse
The QGraphicsSceneDragDropEvent class provides events for drag and drop in the graphics view framewor...
The QGraphicsSceneHoverEvent class provides hover events in the graphics view framework.
The QGraphicsScene class provides a surface for managing a large number of 2D graphical items.
bool hasFocus() const
void addItem(QGraphicsItem *item)
void changed(const QList< QRectF > &region)
qreal height() const
QGraphicsProxyWidget * addWidget(QWidget *widget, Qt::WindowFlags wFlags=Qt::WindowFlags())
void setFocusItem(QGraphicsItem *item, Qt::FocusReason focusReason=Qt::OtherFocusReason)
qreal width() const
QRectF sceneRect
the scene rectangle; the bounding rectangle of the scene
QRectF itemsBoundingRect() const
void setItemIndexMethod(ItemIndexMethod method)
void setSceneRect(const QRectF &rect)
void render(QPainter *painter, const QRectF &target=QRectF(), const QRectF &source=QRectF(), Qt::AspectRatioMode aspectRatioMode=Qt::KeepAspectRatio)
The QGraphicsSceneMouseEvent class provides mouse events in the graphics view framework.
The QGraphicsSceneResizeEvent class provides events for widget resizing in the graphics view framewor...
The QGraphicsSceneWheelEvent class provides wheel events in the graphics view framework.
The QGraphicsTextItem class provides a text item that you can add to a QGraphicsScene to display form...
The QGraphicsView class provides a widget for displaying the contents of a QGraphicsScene.
Definition: qgraphicsview.h:60
void paintEvent(QPaintEvent *event) override
void setScene(QGraphicsScene *scene)
The QGraphicsWidget class is the base class for all widget items in a QGraphicsScene.
void setLayout(QGraphicsLayout *layout)
QRectF geometry
the geometry of the widget
QRectF rect() const
QRectF boundingRect() const override
QSizeF size
the size of the widget
The QGroupBox widget provides a group box frame with a title.
Definition: qgroupbox.h:53
void setChecked(bool checked)
Definition: qgroupbox.cpp:633
void setCheckable(bool checkable)
Definition: qgroupbox.cpp:553
void setTitle(const QString &title)
Definition: qgroupbox.cpp:223
QScreen * primaryScreen
the primary (or default) screen of the application.
QString platformName
The name of the underlying platform plugin.
The QHBoxLayout class lines up widgets horizontally.
Definition: qboxlayout.h:114
The QHash class is a template class that provides a hash-table-based dictionary.
Definition: qhash.h:773
T value(const Key &key) const noexcept
Definition: qhash.h:997
void clear() noexcept(std::is_nothrow_destructible< Node >::value)
Definition: qhash.h:904
The QHelpEvent class provides an event that is used to request helpful information about a particular...
Definition: qevent.h:873
The QHideEvent class provides an event which is sent after a widget is hidden.
Definition: qevent.h:656
The QInputMethodEvent class provides parameters for input method events. \inmodule QtGui.
Definition: qevent.h:696
The QKeyEvent class describes a key event.
Definition: qevent.h:471
The QLabel widget provides a text or image display.
Definition: qlabel.h:56
The QLatin1String class provides a thin wrapper around an US-ASCII/Latin-1 encoded string literal.
Definition: qstring.h:84
void addWidget(QWidget *w)
Definition: qlayout.cpp:223
virtual QRect geometry() const =0
virtual QWidget * widget() const
The QLineEdit widget is a one-line text editor.
Definition: qlineedit.h:64
QSize minimumSizeHint() const override
Definition: qlineedit.cpp:715
bool event(QEvent *) override
Definition: qlineedit.cpp:1458
void setText(const QString &)
Definition: qlineedit.cpp:316
void setEchoMode(EchoMode)
Definition: qlineedit.cpp:572
QString text
the line edit's text.
Definition: qlineedit.h:68
qsizetype size() const noexcept
Definition: qlist.h:414
bool isEmpty() const noexcept
Definition: qlist.h:418
const_reference at(qsizetype i) const noexcept
Definition: qlist.h:457
value_type takeFirst()
Definition: qlist.h:564
qsizetype count() const noexcept
Definition: qlist.h:415
The QMainWindow class provides a main application window.\inmodule QtWidgets.
Definition: qmainwindow.h:61
void setCentralWidget(QWidget *widget)
The QMarginsF class defines the four margins of a rectangle.
Definition: qmargins.h:301
constexpr qreal right() const noexcept
Definition: qmargins.h:397
constexpr qreal left() const noexcept
Definition: qmargins.h:391
constexpr qreal top() const noexcept
Definition: qmargins.h:394
constexpr qreal bottom() const noexcept
Definition: qmargins.h:400
The QMenu class provides a menu widget for use in menu bars, context menus, and other popup menus.
Definition: qmenu.h:62
The QMimeData class provides a container for data that records information about its MIME type.
Definition: qmimedata.h:52
The QMouseEvent class contains parameters that describe a mouse event.
Definition: qevent.h:231
QNetworkProxy::ProxyType type() const
The QObject class is the base class of all Qt objects.
Definition: qobject.h:125
Q_INVOKABLE QObject(QObject *parent=nullptr)
Definition: qobject.cpp:913
void installEventFilter(QObject *filterObj)
Definition: qobject.cpp:2235
QObject * parent() const
Definition: qobject.h:409
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
Definition: qobject.cpp:2772
virtual bool event(QEvent *event)
Definition: qobject.cpp:1329
virtual bool eventFilter(QObject *watched, QEvent *event)
Definition: qobject.cpp:1484
friend class QWidget
Definition: qobject.h:445
void setObjectName(const QString &name)
Definition: qobject.cpp:1261
bool inherits(const char *classname) const
Definition: qobject.h:411
The QPaintEvent class contains event parameters for paint events. \inmodule QtGui.
Definition: qevent.h:539
The QPainter class performs low-level painting on widgets and other paint devices.
Definition: qpainter.h:82
RenderHints renderHints() const
Definition: qpainter.cpp:6897
@ SmoothPixmapTransform
Definition: qpainter.h:90
@ Antialiasing
Definition: qpainter.h:88
@ TextAntialiasing
Definition: qpainter.h:89
void setRenderHints(RenderHints hints, bool on=true)
Definition: qpainter.cpp:6871
void fillRect(const QRectF &, const QBrush &)
Definition: qpainter.cpp:6644
The QPalette class contains color groups for each widget state.
Definition: qpalette.h:55
const QColor & color(ColorGroup cg, ColorRole cr) const
Definition: qpalette.h:101
@ Text
Definition: qpalette.h:87
The QPixmap class is an off-screen image representation that can be used as a paint device.
Definition: qpixmap.h:63
The QPointF class defines a point in the plane using floating point precision.
Definition: qpoint.h:242
constexpr qreal x() const noexcept
Definition: qpoint.h:361
constexpr QPoint toPoint() const
Definition: qpoint.h:420
The QPoint class defines a point in the plane using integer precision.
Definition: qpoint.h:52
The QPointer class is a template class that provides guarded pointers to QObject.
Definition: qpointer.h:54
T * data() const
Definition: qpointer.h:76
bool isNull() const
Definition: qpointer.h:87
The QPointingDevice class describes a device from which mouse, touch or tablet events originate.
The QPushButton widget provides a command button.
Definition: qpushbutton.h:56
QPushButton(QWidget *parent=nullptr)
QSize sizeHint() const override
bool event(QEvent *e) override
The QRectF class defines a finite rectangle in the plane using floating point precision.
Definition: qrect.h:511
constexpr qreal height() const noexcept
Definition: qrect.h:741
constexpr qreal width() const noexcept
Definition: qrect.h:738
constexpr QPointF center() const noexcept
Definition: qrect.h:708
constexpr QRect toRect() const noexcept
Definition: qrect.h:866
The QRect class defines a rectangle in the plane using integer precision.
Definition: qrect.h:59
constexpr int height() const noexcept
Definition: qrect.h:266
constexpr int bottom() const noexcept
Definition: qrect.h:209
constexpr QPoint topLeft() const noexcept
Definition: qrect.h:248
constexpr QRect adjusted(int x1, int y1, int x2, int y2) const noexcept
Definition: qrect.h:397
constexpr QSize size() const noexcept
Definition: qrect.h:269
constexpr QPoint bottomRight() const noexcept
Definition: qrect.h:251
constexpr int width() const noexcept
Definition: qrect.h:263
constexpr QPoint center() const noexcept
Definition: qrect.h:260
The QRegion class specifies a clip region for a painter.
Definition: qregion.h:63
The QRegularExpression class provides pattern matching using regular expressions.
The QScopedPointer class stores a pointer to a dynamically allocated object, and deletes it upon dest...
QRect availableGeometry
the screen's available geometry in pixels
Definition: qscreen.h:82
The QShowEvent class provides an event that is sent when a widget is shown.
Definition: qevent.h:647
The QSizeF class defines the size of a two-dimensional object using floating point precision.
Definition: qsize.h:235
constexpr QSize toSize() const noexcept
Definition: qsize.h:418
The QSize class defines the size of a two-dimensional object using integer point precision.
Definition: qsize.h:55
constexpr QSize expandedTo(const QSize &) const noexcept
Definition: qsize.h:219
The QSizePolicy class is a layout attribute describing horizontal and vertical resizing policy.
Definition: qsizepolicy.h:54
The QSpinBox class provides a spin box widget.
Definition: qspinbox.h:52
bool event(QEvent *event) override
Definition: qspinbox.cpp:1458
The QString class provides a Unicode character string.
Definition: qstring.h:388
static QStyle * create(const QString &)
The QStyle class is an abstract base class that encapsulates the look and feel of a GUI.
Definition: qstyle.h:65
@ SH_ComboBox_Popup
Definition: qstyle.h:644
an OR combination of the tool button's features
Definition: qstyleoption.h:608
The QStyleOptionGraphicsItem class is used to describe the parameters needed to draw a QGraphicsItem.
Definition: qstyleoption.h:684
void initFrom(const QWidget *w)
QTouchEventSequence & move(int touchId, const QPoint &pt, QWindow *window=nullptr)
QMap< int, QEventPoint > points
QTouchEventSequence & press(int touchId, const QPoint &pt, QWindow *window=nullptr)
QTouchEventSequence & release(int touchId, const QPoint &pt, QWindow *window=nullptr)
The QTimer class provides repetitive and single-shot timers.
Definition: qtimer.h:58
void start(int msec)
Definition: qtimer.cpp:259
void setInterval(int msec)
Definition: qtimer.cpp:757
bool singleShot
whether the timer is a single-shot timer
Definition: qtimer.h:60
The QTouchEvent class contains parameters that describe a touch event.
Definition: qevent.h:1020
static QTransform fromScale(qreal dx, qreal dy)
Definition: qtransform.cpp:503
The QVBoxLayout class lines up widgets vertically.
Definition: qboxlayout.h:127
The QWidget class is the base class of all user interface objects.
Definition: qwidget.h:133
void setLayout(QLayout *)
Definition: qwidget.cpp:10146
void setAttribute(Qt::WidgetAttribute, bool on=true)
Definition: qwidget.cpp:11088
void setGeometry(int x, int y, int w, int h)
Definition: qwidget.h:919
Qt::LayoutDirection layoutDirection
the layout direction for this widget.
Definition: qwidget.h:204
Qt::WindowFlags windowFlags() const
Definition: qwidget.h:836
void scroll(int dx, int dy)
Definition: qwidget.cpp:10815
virtual void leaveEvent(QEvent *event)
Definition: qwidget.cpp:9632
virtual void mouseMoveEvent(QMouseEvent *event)
Definition: qwidget.cpp:9362
void setContentsMargins(int left, int top, int right, int bottom)
Definition: qwidget.cpp:7545
void setParent(QWidget *parent)
Definition: qwidget.cpp:10512
void setMinimumSize(const QSize &)
Definition: qwidget.h:865
void setSizePolicy(QSizePolicy)
void setStyle(QStyle *)
Definition: qwidget.cpp:2642
QSize size
the size of the widget excluding any window frame
Definition: qwidget.h:147
void clearFocus()
Definition: qwidget.cpp:6665
QPointF mapToGlobal(const QPointF &) const
void setEnabled(bool)
Definition: qwidget.cpp:3368
void setWindowOpacity(qreal level)
Definition: qwidget.cpp:11330
QRect geometry
the geometry of the widget relative to its parent and excluding the window frame
Definition: qwidget.h:140
QLayout * layout() const
Definition: qwidget.cpp:10115
void setPalette(const QPalette &)
Definition: qwidget.cpp:4536
QPalette palette
the widget's palette
Definition: qwidget.h:166
int width
the width of the widget excluding any window frame
Definition: qwidget.h:148
void setMouseTracking(bool enable)
Definition: qwidget.h:886
QWidget * childAt(int x, int y) const
Definition: qwidget.h:831
QPoint pos
the position of the widget within its parent widget
Definition: qwidget.h:145
void move(int x, int y)
Definition: qwidget.h:913
void setContextMenuPolicy(Qt::ContextMenuPolicy policy)
Definition: qwidget.cpp:7741
void setAcceptDrops(bool on)
Definition: qwidget.cpp:3446
QSizePolicy sizePolicy
the default layout behavior of the widget
Definition: qwidget.h:153
void setFocusPolicy(Qt::FocusPolicy policy)
Definition: qwidget.cpp:7773
void hide()
Definition: qwidget.cpp:8078
int height
the height of the widget excluding any window frame
Definition: qwidget.h:149
QRect rect
the internal geometry of the widget excluding any window frame
Definition: qwidget.h:150
void setFocus()
Definition: qwidget.h:454
int y
the y coordinate of the widget relative to its parent and including any window frame
Definition: qwidget.h:144
void setLayoutDirection(Qt::LayoutDirection direction)
Definition: qwidget.cpp:4885
void show()
Definition: qwidget.cpp:7825
virtual void setVisible(bool visible)
Definition: qwidget.cpp:8198
int x
the x coordinate of the widget relative to its parent including any window frame
Definition: qwidget.h:143
void setMaximumSize(const QSize &)
Definition: qwidget.h:868
virtual void enterEvent(QEnterEvent *event)
Definition: qwidget.cpp:9616
bool isEnabled() const
Definition: qwidget.h:847
void update()
Definition: qwidget.cpp:10977
void setWindowTitle(const QString &)
Definition: qwidget.cpp:6119
bool event(QEvent *event) override
Definition: qwidget.cpp:8772
QStyle * style() const
Definition: qwidget.cpp:2612
void setFont(const QFont &)
Definition: qwidget.cpp:4673
QFont font
the font currently set for the widget
Definition: qwidget.h:167
friend class QGraphicsProxyWidget
Definition: qwidget.h:788
void resize(int w, int h)
Definition: qwidget.h:916
bool hasFocus() const
Definition: qwidget.cpp:6430
QCursor cursor
the cursor shape for this widget
Definition: qwidget.h:169
QPointF mapTo(const QWidget *, const QPointF &) const
bool isWindow() const
Definition: qwidget.h:844
void activateWindow()
Definition: qwidget.cpp:12708
bool isActiveWindow
whether this widget's window is the active window
Definition: qwidget.h:173
void setStyleSheet(const QString &styleSheet)
Definition: qwidget.cpp:2567
void setFixedSize(const QSize &)
QMargins contentsMargins() const
The contentsMargins function returns the widget's contents margins.
Definition: qwidget.cpp:7609
bool isVisible() const
Definition: qwidget.h:907
QPointF mapFromGlobal(const QPointF &) const
void addAction(QAction *action)
Definition: qwidget.cpp:3129
void setCursor(const QCursor &)
Definition: qwidget.cpp:4966
bool testAttribute(Qt::WidgetAttribute) const
Definition: qwidget.h:943
static bool handleWheelEvent(QWindow *window, const QPointF &local, const QPointF &global, QPoint pixelDelta, QPoint angleDelta, Qt::KeyboardModifiers mods=Qt::NoModifier, Qt::ScrollPhase phase=Qt::NoScrollPhase, Qt::MouseEventSource source=Qt::MouseEventNotSynthesized)
void paintEvent(QPaintEvent *event) override
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget=nullptr) override
void focusOutEvent(QFocusEvent *event) override
bool eventFilter(QObject *object, QEvent *event) override
TouchWidget(QWidget *parent=nullptr)
bool event(QEvent *event) override
[0]
Definition: view.h:62
View(QGraphicsScene *scene, QWidget *parent=nullptr)
void paintEvent(QPaintEvent *event) override
QPushButton
[1]
QOpenGLWidget * widget
[1]
b clear()
QPushButton * button
[2]
qDeleteAll(list.begin(), list.end())
double e
set contains("Julia")
QSignalSpy spy(myCustomObject, SIGNAL(mySignal(int, QString, double)))
[0]
QList< QVariant > arguments
QCOMPARE(spy.count(), 1)
rect
[4]
QStyleOptionButton opt
palette
short next
Definition: keywords.cpp:454
QHighDpiScaling::Point position(T, QHighDpiScaling::Point::Kind)
Q_TESTLIB_EXPORT QTestData & newRow(const char *dataTag)
Definition: qtestcase.cpp:2658
Q_TESTLIB_EXPORT const char * currentTestFunction()
Definition: qtestcase.cpp:2749
Q_GUI_EXPORT QPointingDevice * createTouchDevice(QInputDevice::DeviceType devType=QInputDevice::DeviceType::TouchScreen, QInputDevice::Capabilities caps=QInputDevice::Capability::Position)
Q_GUI_EXPORT bool qWaitForWindowActive(QWindow *window, int timeout=5000)
Q_GUI_EXPORT bool qWaitForWindowExposed(QWindow *window, int timeout=5000)
void mouseMove(QWindow *window, QPoint pos=QPoint(), int delay=-1)
Definition: qtestmouse.h:175
Q_TESTLIB_EXPORT void ignoreMessage(QtMsgType type, const char *message)
Definition: qtestcase.cpp:2292
void mouseDClick(QWindow *window, Qt::MouseButton button, Qt::KeyboardModifiers stateKey=Qt::KeyboardModifiers(), QPoint pos=QPoint(), int delay=-1)
Definition: qtestmouse.h:171
void mouseRelease(QWindow *window, Qt::MouseButton button, Qt::KeyboardModifiers stateKey=Qt::KeyboardModifiers(), QPoint pos=QPoint(), int delay=-1)
Definition: qtestmouse.h:163
void mouseClick(QWindow *window, Qt::MouseButton button, Qt::KeyboardModifiers stateKey=Qt::KeyboardModifiers(), QPoint pos=QPoint(), int delay=-1)
Definition: qtestmouse.h:167
void mousePress(QWindow *window, Qt::MouseButton button, Qt::KeyboardModifiers stateKey=Qt::KeyboardModifiers(), QPoint pos=QPoint(), int delay=-1)
Definition: qtestmouse.h:159
Q_CORE_EXPORT void qWait(int ms)
QTouchEventSequence touchEvent(QWindow *window, QPointingDevice *device, bool autoCommit=true)
Definition: qtesttouch.h:78
@ AlignTop
Definition: qnamespace.h:178
@ AlignLeft
Definition: qnamespace.h:169
@ LeftButton
Definition: qnamespace.h:83
@ TextBrowserInteraction
Definition: qnamespace.h:1578
@ WA_AcceptTouchEvents
Definition: qnamespace.h:429
@ WA_UnderMouse
Definition: qnamespace.h:309
@ WA_SetPalette
Definition: qnamespace.h:328
@ WA_QuitOnClose
Definition: qnamespace.h:367
@ WA_Hover
Definition: qnamespace.h:365
@ WA_DontShowOnScreen
Definition: qnamespace.h:408
@ WA_SetFont
Definition: qnamespace.h:329
@ WA_InputMethodEnabled
Definition: qnamespace.h:320
@ RightToLeft
Definition: qnamespace.h:1464
@ WheelFocus
Definition: qnamespace.h:136
@ NoFocus
Definition: qnamespace.h:132
@ TabFocus
Definition: qnamespace.h:133
@ StrongFocus
Definition: qnamespace.h:135
@ ArrowCursor
Definition: qnamespace.h:1177
@ IBeamCursor
Definition: qnamespace.h:1181
@ blue
Definition: qnamespace.h:68
@ magenta
Definition: qnamespace.h:70
@ red
Definition: qnamespace.h:66
@ UI_AnimateCombo
Definition: qnamespace.h:1170
@ UI_AnimateMenu
Definition: qnamespace.h:1168
@ Key_Tab
Definition: qnamespace.h:685
@ Key_Space
Definition: qnamespace.h:538
@ Key_Backtab
Definition: qnamespace.h:686
@ Key_A
Definition: qnamespace.h:572
@ ScrollBarAlwaysOff
Definition: qnamespace.h:1278
@ NoModifier
Definition: qnamespace.h:1074
@ AA_DontUseNativeDialogs
Definition: qnamespace.h:483
@ CopyAction
Definition: qnamespace.h:1485
QTextStream & center(QTextStream &stream)
ScrollPhase
Definition: qnamespace.h:1695
@ ScrollBegin
Definition: qnamespace.h:1697
@ ScrollUpdate
Definition: qnamespace.h:1698
@ NoScrollPhase
Definition: qnamespace.h:1696
@ ScrollEnd
Definition: qnamespace.h:1699
@ FramelessWindowHint
Definition: qnamespace.h:250
@ BypassGraphicsProxyWidget
Definition: qnamespace.h:268
@ Popup
Definition: qnamespace.h:236
@ Window
Definition: qnamespace.h:232
@ Dialog
Definition: qnamespace.h:233
@ ActionsContextMenu
Definition: qnamespace.h:1375
@ PreferredSize
Definition: qnamespace.h:1591
@ MinimumSize
Definition: qnamespace.h:1590
@ TabFocusReason
Definition: qnamespace.h:1362
#define QString()
Definition: parse-defines.h:51
#define QByteArrayLiteral(str)
Definition: qbytearray.h:80
QList< QString > QStringList
Definition: qcontainerfwd.h:64
#define qApp
QT_END_INCLUDE_NAMESPACE typedef double qreal
Definition: qglobal.h:341
@ text
Q_WIDGETS_EXPORT QSize qSmartMinSize(const QSize &sizeHint, const QSize &minSizeHint, const QSize &minSize, const QSize &maxSize, const QSizePolicy &sizePolicy)
@ QtWarningMsg
Definition: qlogging.h:62
#define qWarning
Definition: qlogging.h:179
#define Q_LOGGING_CATEGORY(name,...)
#define qCDebug(category,...)
#define Q_DECLARE_METATYPE(TYPE)
Definition: qmetatype.h:1417
#define SLOT(a)
Definition: qobjectdefs.h:87
#define SIGNAL(a)
Definition: qobjectdefs.h:88
GLint GLint GLint GLint GLint x
[0]
GLboolean r
[2]
GLfloat GLfloat GLfloat w
[0]
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLdouble GLdouble GLdouble GLdouble top
GLenum GLenum GLsizei count
GLbitfield GLuint64 timeout
[4]
GLdouble GLdouble right
GLint left
GLuint GLsizei const GLchar * label
[43]
GLint GLint bottom
GLbitfield flags
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLuint name
GLsizei const GLint * box
struct _cl_event * event
Definition: qopenglext.h:2998
GLuint64EXT * result
[6]
Definition: qopenglext.h:10932
GLfloat GLfloat p
[1]
Definition: qopenglext.h:12698
GLuint GLenum option
Definition: qopenglext.h:5929
#define QStringLiteral(str)
#define QTEST_MAIN(TestObject)
Definition: qtest.h:664
#define QSKIP(statement,...)
Definition: qtestcase.h:222
#define QFETCH(Type, name)
Definition: qtestcase.h:230
#define QEXPECT_FAIL(dataIndex, comment, mode)
Definition: qtestcase.h:224
#define QTRY_COMPARE(expr, expected)
Definition: qtestcase.h:214
#define QVERIFY(statement)
Definition: qtestcase.h:64
#define QTRY_VERIFY(expr)
Definition: qtestcase.h:196
#define QVERIFY2(statement, description)
Definition: qtestcase.h:76
#define Q_OBJECT
Definition: qtmetamacros.h:158
#define slots
Definition: qtmetamacros.h:76
QWidget * qobject_cast< QWidget * >(QObject *o)
Definition: qwidget.h:819
QVBoxLayout * layout
QLineEdit * lineEdit
MyRecord record(int row) const
[0]
QSharedPointer< T > other(t)
[5]
QFileDialog dialog(this)
[1]
QGraphicsScene scene
[0]
form setLayout(layout)
QGraphicsItem * item
QLayoutItem * child
[0]
widget render & pixmap
QPainter painter(this)
[7]
aWidget window() -> setWindowTitle("New Window Title")
[2]
QSpinBox * spinBox
[0]
QCheckBox * checkbox
[0]
QMenu menu
[5]
QAction * at
QNetworkProxy proxy
[0]
QQuickView * view
[0]
Definition: jquant2.c:237
Global global
Definition: main.cpp:114