Vuo  2.1.0
VuoSearchBox.cc
Go to the documentation of this file.
1 
10 #include "VuoSearchBox.hh"
11 #include "ui_VuoSearchBox.h"
12 
14 #include "VuoComment.hh"
15 #include "VuoComposition.hh"
16 #include "VuoRendererComment.hh"
17 #include "VuoEditor.hh"
18 #include "VuoEditorComposition.hh"
19 #include "VuoEditorUtilities.hh"
21 #include "VuoNodeClass.hh"
22 
23 extern "C" {
24 #include "VuoTextHtml.h"
25 }
26 
30 VuoSearchBox::VuoSearchBox(VuoEditorComposition *composition, QWidget *parent, Qt::WindowFlags flags) :
31  QDockWidget(parent, flags),
32  ui(new Ui::VuoSearchBox)
33 {
34  this->composition = composition;
35 
36  ui->setupUi(this);
37  setWindowTitle(tr("Find"));
38  setAllowedAreas(Qt::TopDockWidgetArea);
39  setFloating(false);
40 
41  ui->searchText->installEventFilter(this);
42 
43  ui->doneButton->setDefault(false);
44  ui->doneButton->setAutoDefault(false);
45 
46  connect(ui->doneButton, &QPushButton::clicked, this, &QDockWidget::close);
47  connect(ui->previousButton, &QPushButton::clicked, this, &VuoSearchBox::goToPreviousResult);
48  connect(ui->nextButton, &QPushButton::clicked, this, &VuoSearchBox::goToNextResult);
49  connect(ui->searchText, &QLineEdit::textChanged, this, &VuoSearchBox::searchForText);
50 
51  setTitleBarWidget(new QWidget()); // Disable the titlebar.
52 
53  currentResultIndex = 0;
54  noResultsText = QApplication::translate("VuoSearchBox", "No results");
55  resultCount = new QLabel(this);
56  int resultCountTextWidth = qMax(70, QFontMetrics(resultCount->font()).size(0,noResultsText).width());
57 
58  resultCount->setFixedSize(resultCountTextWidth, resultCount->height());
59  resultCount->setStyleSheet("QLabel { color : gray; }");
60  resultCount->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
61 
62  searchIcon = QIcon(QApplication::applicationDirPath().append("/../Resources/search-loupe.png"));
63  searchButton = new QToolButton(ui->searchText);
64  searchButton->setIcon(searchIcon);
65  searchButton->setCursor(Qt::ArrowCursor);
66  searchButton->setStyleSheet("QToolButton { border: none; padding: 2px 3px 3px 3px; }");
67 
68  const int leftPadding = searchButton->iconSize().width()+4;
69  setStyleSheet(VUO_QSTRINGIFY(
70  QLineEdit {
71  padding-left: %2px; // Space for search button
72  padding-right: %1px; // Space for result count
73  }
74  QLineEdit:focus {
75  padding-left: %2px; // Space for search button
76  padding-right: %1px; // Space for result count
77  }
78  )
79  .arg(resultCount->width()+10)
80  .arg(leftPadding)
81  );
82 
83  VuoEditor *editor = (VuoEditor *)qApp;
84  connect(editor, &VuoEditor::darkInterfaceToggled, this, &VuoSearchBox::updateColor);
85  updateColor(editor->isInterfaceDark());
86 }
87 
92 {
93  ui->searchText->setFocus();
94  ui->searchText->selectAll();
95  searchForText(ui->searchText->text());
96  QDockWidget::show();
97  repositionChildWidgets();
98 
99  setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed);
100  setMinimumHeight(geometry().height());
101  setMaximumHeight(geometry().height());
102 }
103 
107 void VuoSearchBox::updateSpotlightedItems()
108 {
109  // Don't interfere with node execution spotlighting while in "Show Events" mode.
110  if (composition->getShowEventsMode())
111  return;
112 
113  if (ui->searchText->text().isEmpty())
114  {
115  if (composition->getRenderNodeActivity())
116  composition->stopDisplayingActivity();
117  }
118  else
119  {
120  if (!composition->getRenderNodeActivity())
121  composition->beginDisplayingActivity(false);
122 
123  foreach (VuoNode *node, composition->getBase()->getNodes())
124  {
125  if (node->getRenderer()->getTimeLastExecutionEnded() == VuoRendererItem::activityInProgress)
126  node->getRenderer()->setExecutionEnded();
127  }
128 
129  foreach (QGraphicsItem *matchingItem, searchResults)
130  {
131  VuoRendererNode *matchingNode = dynamic_cast<VuoRendererNode *>(matchingItem);
132  if (matchingNode)
133  matchingNode->setExecutionBegun();
134  }
135 
136  // @todo https://b33p.net/kosada/node/9986 : Implement fade for comments?
137  }
138 }
139 
143 void VuoSearchBox::searchForText(const QString &searchText)
144 {
145  searchResults.clear();
146  currentResultIndex = 0;
147 
148  if (searchText.isEmpty())
149  {
150  composition->clearSelection();
151  updateSpotlightedItems();
152  updateResultCount();
153  return;
154  }
155 
156  searchResults = getCurrentSearchResults(searchText);
157 
158  composition->clearSelection();
159  if (searchResults.size() >= 1)
160  searchResults[currentResultIndex]->setSelected(true);
161 
162  updateSpotlightedItems();
163  updateResultCount();
164  updateViewportToFitResults();
165  emit searchPerformed();
166 }
167 
172 vector<QGraphicsItem *> VuoSearchBox::getCurrentSearchResults(const QString &searchText)
173 {
174  vector<QGraphicsItem *> searchResults;
175 
176  // Special handling for the "deprecated" search keyword: return all deprecated nodes in the composition.
177  if (searchText.toLower().trimmed() == "deprecated")
178  return findDeprecatedNodes();
179 
180  // Special handling for the "subcomposition" and ".vuo" search keywords: return all subcompositions.
181  if (searchText.toLower().trimmed() == "subcomposition" || searchText.toLower().trimmed() == ".vuo"
182  || searchText.toLower().trimmed() == "source:.vuo")
183  return findSubcompositionNodes();
184 
185  // Special handling for the ".fs" search keyword: return all shaders.
186  if (searchText.toLower().trimmed() == ".fs" || searchText.toLower().trimmed() == "source:.fs")
187  return findShaderNodes();
188 
189  // Special handling for the ".c" search keyword: return all C-language nodes.
190  if (searchText.toLower().trimmed() == ".c" || searchText.toLower().trimmed() == "source:.c")
191  return findCLanguageNodes();
192 
193  // Special handling for the ".vuonode" search keyword: return all pre-compiled 3rd-party nodes.
194  if (searchText.toLower().trimmed() == ".vuonode" || searchText.toLower().trimmed() == "source:.vuonode")
195  return find3rdPartyPrecompiledNodes();
196 
197  // Search nodes.
198  foreach (VuoNode *node, composition->getBase()->getNodes())
199  {
200  // Retrieve the node class name, as rendered on the canvas.
201  VuoNodeClass *nodeClass = node->getNodeClass();
202  QString nodeClassName = "";
203  if (nodeClass->hasCompiler() && dynamic_cast<VuoCompilerSpecializedNodeClass *>(nodeClass->getCompiler()))
204  nodeClassName = QString::fromUtf8(dynamic_cast<VuoCompilerSpecializedNodeClass *>(nodeClass->getCompiler())->getOriginalGenericNodeClassName().c_str());
205  else
206  nodeClassName = QString::fromUtf8(nodeClass->getClassName().c_str());
207 
208  // Check whether we're dealing with a collapsed typecast.
209  bool isCollapsedTypecast = false;
210  vector<VuoPort *> inputPorts = node->getInputPorts();
211  VuoPort *typecastInPort = (inputPorts.size() >= VuoNodeClass::unreservedInputPortStartIndex+1?
213  NULL);
214 
215  if (typecastInPort && typecastInPort->hasRenderer())
216  {
217  VuoRendererPort *typecastParent = typecastInPort->getRenderer()->getTypecastParentPort();
218  if (typecastParent)
219  isCollapsedTypecast = true;
220  }
221 
222  // Check whether we're dealing with another type of attachment.
223  bool isAttachment = false;
224  if (dynamic_cast<VuoRendererInputAttachment *>(node->getRenderer()))
225  isAttachment = true;
226 
227  bool nodeMatched = false;
228 
229  // Match against node title.
230  if (!isCollapsedTypecast && !isAttachment)
231  {
232  // Only match starting at the beginning of words within the title.
233  int index = QString(node->getTitle().c_str()).toLower().indexOf(searchText.toLower());
234  if (index == 0 || ((index > 0) && (QString(node->getTitle().c_str()).toLower().at(index-1) == ' ')))
235  {
236  searchResults.push_back(node->getRenderer());
237  nodeMatched = true;
238  }
239  }
240 
241  // Match against node class name.
242  if (!nodeMatched && !isCollapsedTypecast && !isAttachment)
243  {
244  // Only match starting at the beginning of '.'-delimited segments within the class name.
245  int index = nodeClassName.toLower().indexOf(searchText.toLower());
246  if (index == 0 || ((index > 0) && (nodeClassName.toLower().at(index-1) == '.')))
247  {
248  searchResults.push_back(node->getRenderer());
249  nodeMatched = true;
250  }
251  }
252 
253  // Match against port display name.
254  if (!nodeMatched)
255  {
256  foreach (VuoPort *port, node->getInputPorts())
257  {
258  if (nodeMatched)
259  break;
260 
261  if (node->getRefreshPort() == port)
262  continue;
263 
264  if (port && port->hasRenderer())
265  {
266  // Only match starting at the beginning of words within the display name.
267  int index = QString(port->getRenderer()->getPortNameToRenderWhenDisplayed().c_str()).toLower().indexOf(searchText.toLower());
268  if (index == 0 || ((index > 0) && (QString(port->getRenderer()->getPortNameToRenderWhenDisplayed().c_str()).toLower().at(index-1) == ' ')))
269  {
270  if (isCollapsedTypecast && typecastInPort->getRenderer()->getTypecastParentPort()->getRenderedParentNode())
271  searchResults.push_back(typecastInPort->getRenderer()->getTypecastParentPort()->getRenderedParentNode());
272  else
273  searchResults.push_back(node->getRenderer());
274  nodeMatched = true;
275  }
276  }
277  }
278 
279  foreach (VuoPort *port, node->getOutputPorts())
280  {
281  if (nodeMatched)
282  break;
283 
284  if (port && port->hasRenderer())
285  {
286  // Only match starting at the beginning of words within the display name.
287  int index = QString(port->getRenderer()->getPortNameToRenderWhenDisplayed().c_str()).toLower().indexOf(searchText.toLower());
288  if (index == 0 || ((index > 0) && (QString(port->getRenderer()->getPortNameToRenderWhenDisplayed().c_str()).toLower().at(index-1) == ' ')))
289  {
290  if (isCollapsedTypecast && typecastInPort->getRenderer()->getTypecastParentPort()->getRenderedParentNode())
291  searchResults.push_back(typecastInPort->getRenderer()->getTypecastParentPort()->getRenderedParentNode());
292  else
293  searchResults.push_back(node->getRenderer());
294  nodeMatched = true;
295  }
296  }
297  }
298  }
299 
300  // Match against port constants.
301  if (!nodeMatched)
302  {
303  foreach (VuoPort *port, node->getInputPorts())
304  {
305  if (nodeMatched)
306  break;
307 
308  if (port && port->hasRenderer() && port->getRenderer()->isConstant())
309  {
310  VuoText constantWithoutHtml = VuoText_removeHtml(port->getRenderer()->getConstantAsStringToRender().c_str());
311  VuoRetain(constantWithoutHtml);
312  if (QString(constantWithoutHtml).contains(searchText, Qt::CaseInsensitive))
313  {
314  if (isCollapsedTypecast && typecastInPort->getRenderer()->getTypecastParentPort()->getRenderedParentNode())
315  searchResults.push_back(typecastInPort->getRenderer()->getTypecastParentPort()->getRenderedParentNode());
316  else
317  searchResults.push_back(node->getRenderer());
318  nodeMatched = true;
319  }
320  VuoRelease(constantWithoutHtml);
321  }
322  }
323  }
324  }
325 
326  // Now search comments.
327  foreach (VuoComment *comment, composition->getBase()->getComments())
328  {
329  // Only match starting at the beginning of words within the comment text.
330  int index = QString(comment->getContent().c_str()).toLower().indexOf(searchText.toLower());
331  if (index == 0 || ((index > 0) && !(QString(comment->getContent().c_str()).toLower().at(index-1).isLetterOrNumber())))
332  searchResults.push_back(comment->getRenderer());
333  }
334 
335  sort(searchResults.begin(), searchResults.end(), itemLessThan);
336  searchResults.erase(std::unique(searchResults.begin(), searchResults.end()), searchResults.end());
337 
338  return searchResults;
339 }
340 
344 vector<QGraphicsItem *> VuoSearchBox::findDeprecatedNodes()
345 {
346  // Search nodes.
347  foreach (VuoNode *node, composition->getBase()->getNodes())
348  {
349  bool nodeIsDeprecated = node->getNodeClass()->getDeprecated();
350  if (nodeIsDeprecated && !excludeNodeFromSearchResults(node))
351  searchResults.push_back(node->getRenderer());
352  }
353 
354  sort(searchResults.begin(), searchResults.end(), itemLessThan);
355  searchResults.erase(std::unique(searchResults.begin(), searchResults.end()), searchResults.end());
356 
357  return searchResults;
358 }
359 
363 vector<QGraphicsItem *> VuoSearchBox::findSubcompositionNodes()
364 {
365  // Search nodes.
366  foreach (VuoNode *node, composition->getBase()->getNodes())
367  {
368 
369  bool nodeIsSubcomposition = node->getNodeClass()->hasCompiler()?
371  false;
372  if (nodeIsSubcomposition && !excludeNodeFromSearchResults(node))
373  searchResults.push_back(node->getRenderer());
374  }
375 
376  sort(searchResults.begin(), searchResults.end(), itemLessThan);
377  searchResults.erase(std::unique(searchResults.begin(), searchResults.end()), searchResults.end());
378 
379  return searchResults;
380 }
381 
385 vector<QGraphicsItem *> VuoSearchBox::findShaderNodes()
386 {
387  // Search nodes.
388  foreach (VuoNode *node, composition->getBase()->getNodes())
389  {
390 
391  bool nodeIsShader = node->getNodeClass()->hasCompiler()?
392  node->getNodeClass()->getCompiler()->isIsf() :
393  false;
394  if (nodeIsShader && !excludeNodeFromSearchResults(node))
395  searchResults.push_back(node->getRenderer());
396  }
397 
398  sort(searchResults.begin(), searchResults.end(), itemLessThan);
399  searchResults.erase(std::unique(searchResults.begin(), searchResults.end()), searchResults.end());
400 
401  return searchResults;
402 }
403 
407 vector<QGraphicsItem *> VuoSearchBox::findCLanguageNodes()
408 {
409  // Search nodes.
410  foreach (VuoNode *node, composition->getBase()->getNodes())
411  {
412  VuoNodeClass *nodeClass = node->getNodeClass();
413  QString actionText, sourcePath;
414  bool nodeIsEditable = VuoEditorUtilities::isNodeClassEditable(nodeClass, actionText, sourcePath);
415  bool nodeHasExternalSource = nodeIsEditable && !nodeClass->getCompiler()->isSubcomposition() && !nodeClass->getCompiler()->isIsf();
416 
417  if (nodeHasExternalSource && !excludeNodeFromSearchResults(node))
418  searchResults.push_back(node->getRenderer());
419  }
420 
421  sort(searchResults.begin(), searchResults.end(), itemLessThan);
422  searchResults.erase(std::unique(searchResults.begin(), searchResults.end()), searchResults.end());
423 
424  return searchResults;
425 }
426 
430 vector<QGraphicsItem *> VuoSearchBox::find3rdPartyPrecompiledNodes()
431 {
432  // Search nodes.
433  foreach (VuoNode *node, composition->getBase()->getNodes())
434  {
435  VuoNodeClass *nodeClass = node->getNodeClass();
436  QString actionText, sourcePath;
437  bool nodeIs3rdParty = node->getNodeClass()->hasCompiler()?
438  !nodeClass->getCompiler()->isBuiltIn():
439  false;
440  bool nodeIsEditable = VuoEditorUtilities::isNodeClassEditable(nodeClass, actionText, sourcePath);
441 
442  if (nodeIs3rdParty && !nodeIsEditable && !excludeNodeFromSearchResults(node))
443  searchResults.push_back(node->getRenderer());
444  }
445 
446  sort(searchResults.begin(), searchResults.end(), itemLessThan);
447  searchResults.erase(std::unique(searchResults.begin(), searchResults.end()), searchResults.end());
448 
449  return searchResults;
450 }
451 
456 bool VuoSearchBox::excludeNodeFromSearchResults(VuoNode *node)
457 {
458  // Check whether we're dealing with a collapsed typecast.
459  bool isCollapsedTypecast = false;
460  vector<VuoPort *> inputPorts = node->getInputPorts();
461  VuoPort *typecastInPort = (inputPorts.size() >= VuoNodeClass::unreservedInputPortStartIndex+1?
463  NULL);
464  if (typecastInPort && typecastInPort->hasRenderer() && typecastInPort->getRenderer()->getTypecastParentPort())
465  isCollapsedTypecast = true;
466 
467  // Check whether we're dealing with another type of attachment.
468  bool isAttachment = (dynamic_cast<VuoRendererInputAttachment *>(node->getRenderer()));
469 
470  return (isCollapsedTypecast || isAttachment);
471 }
472 
477 {
478  bool startingOver = false;
479  vector<QGraphicsItem *> newSearchResults = getCurrentSearchResults(ui->searchText->text());
480  if (newSearchResults != searchResults)
481  {
482  searchResults.clear();
483  startingOver = true;
484  searchResults = newSearchResults;
485  }
486 
487  composition->clearSelection();
488  updateSpotlightedItems();
489 
490  if (searchResults.size() > 0)
491  {
492  currentResultIndex = (startingOver? 0 : (currentResultIndex + 1) % searchResults.size());
493  searchResults[currentResultIndex]->setSelected(true);
494  }
495 
496  updateResultCount();
497  updateViewportToFitResults();
498 }
499 
504 {
505  bool startingOver = false;
506  vector<QGraphicsItem *> newSearchResults = getCurrentSearchResults(ui->searchText->text());
507  if (newSearchResults != searchResults)
508  {
509  searchResults.clear();
510  startingOver = true;
511  searchResults = newSearchResults;
512  }
513 
514  composition->clearSelection();
515  updateSpotlightedItems();
516 
517  if (searchResults.size() > 0)
518  {
519  currentResultIndex = (startingOver? 0 : currentResultIndex - 1);
520  if (currentResultIndex < 0)
521  currentResultIndex += searchResults.size();
522 
523  searchResults[currentResultIndex]->setSelected(true);
524  }
525 
526  updateResultCount();
527  updateViewportToFitResults();
528 }
529 
534 {
535  if (isHidden())
536  return;
537 
538  bool startingOver = false;
539  vector<QGraphicsItem *> newSearchResults = getCurrentSearchResults(ui->searchText->text());
540  if (newSearchResults != searchResults)
541  {
542  searchResults.clear();
543  startingOver = true;
544  searchResults = newSearchResults;
545  }
546 
547  composition->clearSelection();
548  updateSpotlightedItems();
549 
550  if (searchResults.size() > 0)
551  {
552  currentResultIndex = (startingOver? 0 : currentResultIndex);
553  searchResults[currentResultIndex]->setSelected(true);
554  }
555 
556  updateResultCount();
557  updateViewportToFitResults();
558 }
559 
560 
564 void VuoSearchBox::updateViewportToFitResults()
565 {
566  QRectF itemsTightBoundingRect = (!composition->selectedItems().isEmpty()? composition->internalSelectedItemsChildrenBoundingRect() :
567  composition->internalItemsBoundingRect());
568 
569  if (!composition->selectedItems().isEmpty() &&
570  !composition->views()[0]->mapToScene(composition->views()[0]->rect()).boundingRect().
571  contains(QRect(itemsTightBoundingRect.topLeft().toPoint(),
572  itemsTightBoundingRect.bottomRight().toPoint())))
573  {
574  composition->views()[0]->ensureVisible(itemsTightBoundingRect);
575  }
576 }
577 
582 void VuoSearchBox::updateResultCount()
583 {
584  if (ui->searchText->text().isEmpty())
585  resultCount->setText("");
586  else if (searchResults.size() == 0)
587  resultCount->setText(noResultsText);
588  else
589  resultCount->setText(QString("%1 of %2").arg(currentResultIndex+1).arg(searchResults.size()));
590 
591  ui->nextButton->setEnabled(searchResults.size() >= 1);
592  ui->nextButton->setAutoRaise(!ui->nextButton->isEnabled());
593  ui->previousButton->setEnabled(searchResults.size() >= 1);
594  ui->previousButton->setAutoRaise(!ui->previousButton->isEnabled());
595 }
596 
600 bool VuoSearchBox::eventFilter(QObject *object, QEvent *event)
601 {
602  if ((event->type() == QEvent::KeyPress) && (static_cast<QKeyEvent *>(event)->key() == Qt::Key_Return))
603  goToNextResult();
604  else if ((event->type() == QEvent::KeyPress) && (static_cast<QKeyEvent *>(event)->key() == Qt::Key_G)
605  && (static_cast<QKeyEvent *>(event)->modifiers() & Qt::ControlModifier)
606  && (!(static_cast<QKeyEvent *>(event)->modifiers() & Qt::ShiftModifier)))
607  goToNextResult();
608  else if ((event->type() == QEvent::KeyPress) && (static_cast<QKeyEvent *>(event)->key() == Qt::Key_G)
609  && (static_cast<QKeyEvent *>(event)->modifiers() & Qt::ControlModifier)
610  && (static_cast<QKeyEvent *>(event)->modifiers() & Qt::ShiftModifier))
612  else if ((event->type() == QEvent::KeyPress) && (static_cast<QKeyEvent *>(event)->key() == Qt::Key_Escape))
613  close();
614  else
615  {
616  object->removeEventFilter(this);
617  QApplication::sendEvent(object, event);
618  object->installEventFilter(this);
619  }
620 
621  return true;
622 }
623 
627 void VuoSearchBox::resizeEvent(QResizeEvent *event)
628 {
629  QDockWidget::resizeEvent(event);
630  repositionChildWidgets();
631 }
632 
636 void VuoSearchBox::closeEvent(QCloseEvent *event)
637 {
638  if (!composition->getShowEventsMode() && composition->getRenderNodeActivity())
639  composition->stopDisplayingActivity();
640 
641  QDockWidget::closeEvent(event);
642 }
643 
647 void VuoSearchBox::repositionChildWidgets()
648 {
649  ui->searchText->resize(widget()->geometry().width()-ui->previousButton->geometry().width()
650  -ui->nextButton->geometry().width()
651  -ui->doneButton->geometry().width()-5,
652  ui->searchText->height());
653  resultCount->move(ui->searchText->geometry().right()-resultCount->width()-5, ui->searchText->pos().y()-5);
654  ui->previousButton->move(ui->searchText->geometry().right()+5, ui->previousButton->y());
655  ui->nextButton->move(ui->previousButton->geometry().right()+2, ui->nextButton->y());
656  ui->doneButton->move(ui->nextButton->geometry().right(), ui->doneButton->y());
657  update();
658 }
659 
663 void VuoSearchBox::updateColor(bool isDark)
664 {
665  QString barBackgroundColor = isDark ? "#919191" : "#efefef";
666  QString pressedToolButtonBackgroundColor = isDark ? "#aaaaaa" : "#d4d4d4";
667  QString disabledToolButtonArrowColor = isDark ? "#707070" : "#c0c0c0";
668 
669  QString styleSheet = VuoDialogForInputEditor::getStyleSheet(isDark)
670  + VUO_QSTRINGIFY(
671  QWidget#searchBoxContents {
672  background-color: %1;
673  }
674  QToolButton#previousButton,
675  QToolButton#nextButton {
676  border: 1px;
677  background-color: %1;
678  border-radius: 4px;
679  }
680  QToolButton#previousButton:pressed,
681  QToolButton#nextButton:pressed {
682  background-color: %2;
683  }
684  QToolButton#previousButton:disabled,
685  QToolButton#nextButton:disabled {
686  color: %3;
687  }
688  )
689  .arg(barBackgroundColor)
690  .arg(pressedToolButtonBackgroundColor)
691  .arg(disabledToolButtonArrowColor);
692 
693  if (isDark)
694  {
695  QString focusRingColor = "#1d6ae5";
696  styleSheet += VUO_QSTRINGIFY(
697  QLineEdit {
698  border: 1px solid #919191;
699  background: #262626;
700  padding-top: 2px;
701  }
702  QLineEdit:focus {
703  border: 2px solid %1;
704  padding-top: 1px;
705  }
706 
707  QPushButton {
708  border: none;
709  background: #aaaaaa;
710  border-radius: 4px;
711  margin: 4px 7px 7px 5px;
712  }
713  QPushButton:pressed {
714  background: #cccccc;
715  }
716  QPushButton:focus {
717  border: 2px solid %1;
718  }
719  )
720  .arg(focusRingColor);
721  }
722 
723  ui->searchBoxContents->setStyleSheet(styleSheet);
724 
725  ui->searchText->setAttribute(Qt::WA_MacShowFocusRect, !isDark);
726 }
727 
733 bool VuoSearchBox::itemLessThan(QGraphicsItem *item1, QGraphicsItem *item2)
734 {
735  int item1RowNum = item1->scenePos().y()/VuoRendererComposition::majorGridLineSpacing;
736  int item2RowNum = item2->scenePos().y()/VuoRendererComposition::majorGridLineSpacing;
737 
738  if (item1RowNum < item2RowNum)
739  return true;
740  else if (item2RowNum < item1RowNum)
741  return false;
742  return (item1->scenePos().x() < item2->scenePos().x());
743 }
744 
749 {
750  return (ui->nextButton->isEnabled() && ui->previousButton->isEnabled());
751 }
752 
753 VuoSearchBox::~VuoSearchBox()
754 {
755  delete ui;
756 }