Vuo  2.0.2
VuoEditorGraphicsView.cc
Go to the documentation of this file.
1 
10 #include "VuoEditorGraphicsView.hh"
11 #include "VuoEditor.hh"
12 #include "VuoEditorCocoa.hh"
13 #include "VuoEditorComposition.hh"
14 #include "VuoEditorWindow.hh"
15 
20  : QGraphicsView(parent)
21 {
22  setOptimizationFlags(QGraphicsView::DontSavePainterState | QGraphicsView::DontAdjustForAntialiasing);
23 
24  // For large compositions, it's faster to redraw the entire viewport
25  // than to check hundreds of objects to determine whether we should render them.
26  setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
27 
28  framesRenderedSinceProfileLogged = 0;
29  renderDurationSinceProfileLogged = 0;
30  lastProfileLoggedTime = VuoLogGetElapsedTime();
31 
32  viewport()->setAttribute(Qt::WA_AcceptTouchEvents);
33  accumulatedScale = 1;
34  gestureDetected = false;
35 
36  QSettings settings;
37  ignorePinchesSmallerThanX = settings.value("canvas/ignorePinchesSmallerThanX", 0.25).toDouble();
38  ignorePinchesStartedLessThanXSecondsAfterDrag = settings.value("canvas/ignorePinchesStartedLessThanXSecondsAfterDrag", 0.5).toDouble();
39  ignorePinchesStartedMoreThanXSecondsAfterTouch = settings.value("canvas/ignorePinchesStartedMoreThanXSecondsAfterTouch", 0.25).toDouble();
40 
41  VuoEditor *editor = (VuoEditor *)qApp;
42  connect(editor, &VuoEditor::darkInterfaceToggled, this, &VuoEditorGraphicsView::updateColor);
43 }
44 
49 {
50  return gestureDetected;
51 }
52 
56 bool VuoEditorGraphicsView::event(QEvent * event)
57 {
58  if (event->type() == QEvent::KeyPress)
59  {
60  QKeyEvent *keyEvent = (QKeyEvent *)(event);
61  if (keyEvent->key() == Qt::Key_Return && (keyEvent->modifiers() & Qt::ControlModifier))
62  {
63  VuoEditor *e = (VuoEditor *)QCoreApplication::instance();
64  e->showNodeLibrary();
65  return true;
66  }
67 
68  if (keyEvent->key() == Qt::Key_Escape)
69  qApp->sendEvent(parentWidget(), keyEvent);
70  }
71 
72  else if (event->type() == QEvent::Resize)
73  emit viewResized();
74 
75  return QGraphicsView::event(event);
76 }
77 
81 void VuoEditorGraphicsView::paintEvent(QPaintEvent *event)
82 {
83  double t0 = VuoLogGetElapsedTime();
84  QGraphicsView::paintEvent(event);
85  double renderDuration = VuoLogGetElapsedTime() - t0;
86 
87  if (VuoIsDebugEnabled())
88  {
89  ++framesRenderedSinceProfileLogged;
90  renderDurationSinceProfileLogged += renderDuration;
91  const double profileSeconds = 5;
92  if (t0 > lastProfileLoggedTime + profileSeconds)
93  {
94  VUserLog("%4d items, average draw time %6.4f s (%6.1f fps), %6.1f draws per second, %4d MB cached, bspDepth %2d",
95  scene()->items().count(),
96  renderDurationSinceProfileLogged/framesRenderedSinceProfileLogged,
97  framesRenderedSinceProfileLogged/renderDurationSinceProfileLogged,
98  framesRenderedSinceProfileLogged/profileSeconds,
99  QPixmapCache::totalUsed()/1024,
100  scene()->bspTreeDepth());
101 #if 0
102  foreach (QGraphicsItem *i, scene()->items())
103  {
104  VuoRendererNode *node = dynamic_cast<VuoRendererNode *>(i);
105  VuoRendererPort *port = dynamic_cast<VuoRendererPort *>(i);
106  if (node)
107  VUserLog("\t%p node '%s'", i, node->getBase()->getTitle().c_str());
108  else if (port)
109  VUserLog("\t%p port '%s'", i, port->getBase()->getClass()->getName().c_str());
110  else
111  VUserLog("\t%p %s", i, typeid(*i).name());
112  }
113 #endif
114 
115  lastProfileLoggedTime = t0;
116  framesRenderedSinceProfileLogged = 0;
117  renderDurationSinceProfileLogged = 0;
118  }
119  }
120 }
121 
125 bool VuoEditorGraphicsView::viewportEvent(QEvent *event)
126 {
127  QEvent::Type eventType = event->type();
128  VuoEditorComposition *composition = static_cast<VuoEditorComposition *>(scene());
129 
130 
131  // See VuoEditorWindow::eventFilter.
132  // https://b33p.net/kosada/node/16688
133  if (eventType == QEvent::MouseButtonPress)
134  {
135  auto mbp = static_cast<QMouseEvent *>(event);
136  if (mbp->button() == Qt::RightButton)
137  {
138  QGraphicsSceneContextMenuEvent contextMenuEvent(QEvent::GraphicsSceneContextMenu);
139  contextMenuEvent.setScreenPos(mbp->globalPos());
140  contextMenuEvent.setScenePos(mapToScene(mbp->pos()));
141  contextMenuEvent.setReason(QGraphicsSceneContextMenuEvent::Mouse);
142  QApplication::sendEvent(scene(), &contextMenuEvent);
143  event->accept();
144  return true;
145  }
146  }
147 
148 
149  if (eventType == QEvent::TouchBegin
150  || eventType == QEvent::TouchUpdate
151  || eventType == QEvent::TouchEnd)
152  {
153  QTouchEvent *touchEvent = static_cast<QTouchEvent *>(event);
154  const QList<QTouchEvent::TouchPoint> &touchPoints = touchEvent->touchPoints();
155  const QTouchEvent::TouchPoint &touchPoint0 = touchPoints.first();
156  const QTouchEvent::TouchPoint &touchPoint1 = touchPoints.last();
157 
158  static double latestTouchBeganTime = 0;
159  if (touchPoint0.state() & Qt::TouchPointPressed
160  || touchPoint1.state() & Qt::TouchPointPressed)
161  latestTouchBeganTime = ((double)touchEvent->timestamp()) / 1000.;
162 
163  if (static_cast<VuoEditorWindow *>(window())->isScrollInProgress())
164  {
165  VDebugLog("ignoring pinch while scroll in progress");
166  return false;
167  }
168 
169  double secondsSinceLastDrag = VuoLogGetElapsedTime() - static_cast<VuoEditorWindow *>(window())->getLatestDragTime();
170  if (secondsSinceLastDrag < ignorePinchesStartedLessThanXSecondsAfterDrag)
171  {
172  VDebugLog("ignoring pinch that occurred just %.2fs (less than %gs) after the latest drag", secondsSinceLastDrag, ignorePinchesStartedLessThanXSecondsAfterDrag);
173  return false;
174  }
175 
176  if (static_cast<VuoEditorWindow *>(window())->isItemDragInProgress())
177  {
178  VDebugLog("ignoring pinch while item drag in progress");
179  return false;
180  }
181 
182  if (composition && composition->getCableInProgress())
183  {
184  VDebugLog("ignoring pinch while cable drag in progress");
185  return false;
186  }
187 
188  if (!rubberBandRect().isNull())
189  {
190  VDebugLog("ignoring pinch while rubberband in progress");
191  return false;
192  }
193 
194  if (touchPoints.count() == 2)
195  {
196  double currentLength = QLineF(touchPoint0.pos(), touchPoint1.pos()).length();
197 
198  static double detectedLength = 0;
199  if (!gestureDetected)
200  {
201  // Ignore tiny gestures (which probably aren't intended to be pinch gestures anyway).
202  double startLength = QLineF(touchPoint0.startPos(), touchPoint1.startPos()).length();
203  double scale = currentLength / startLength;
204  if (fabs(scale - 1) < ignorePinchesSmallerThanX)
205  {
206  VDebugLog("ignoring small pinch (%.2f < %g)", fabs(scale-1), ignorePinchesSmallerThanX);
207  return false;
208  }
209 
210  // Ignore pinch gestures that start long after both fingers are down
211  // (which probably aren't intended to be pinch gestures anyway).
212  double secondsSinceTouch = ((double)touchEvent->timestamp())/1000. - latestTouchBeganTime;
213  if (secondsSinceTouch > ignorePinchesStartedMoreThanXSecondsAfterTouch)
214  {
215  VDebugLog("ignoring pinch that started %.2fs (more than %gs) after touch", secondsSinceTouch, ignorePinchesStartedMoreThanXSecondsAfterTouch);
216  return false;
217  }
218 
219  detectedLength = QLineF(touchPoint0.pos(), touchPoint1.pos()).length();
220 
221  // At the start of each pinch gesture, use the view's current scale.
222  accumulatedScale = transform().m11();
223 
224  setInteractive(false);
225  gestureDetected = true;
226  }
227 
228  double scale = currentLength / detectedLength;
229 
230  if (touchEvent->touchPointStates() & Qt::TouchPointReleased)
231  {
232  accumulatedScale *= scale;
233  scale = 1;
234  setInteractive(true);
235  gestureDetected = false;
236  }
237 
238  else
239  {
240  // Skip rendering if we aren't keeping up.
241  double lag = VuoEditorCocoa_systemUptime() - touchEvent->timestamp()/1000.;
242  const double lagLimit = .1;
243  if (lag > lagLimit)
244  return true;
245  }
246 
247  VDebugLog("pinch zoomed to scale %g", accumulatedScale * scale);
248  setTransform(QTransform::fromScale(accumulatedScale * scale, accumulatedScale * scale));
249 
250  // After zooming, scroll the scene to the view's center.
251  centerOn(mapToScene(rect().center()));
252 
253  static_cast<VuoEditorWindow *>(window())->updateUI();
254  }
255  return true;
256  }
257 
258  return QGraphicsView::viewportEvent(event);
259 }
260 
264 void VuoEditorGraphicsView::updateColor(bool isDark)
265 {
266  VuoEditorComposition *composition = static_cast<VuoEditorComposition *>(scene());
267  composition->setColor(isDark);
268 }