Vuo  2.0.3
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())
309  {
310  if (port->getRenderer()->isConstant() &&
311  QString(VuoText_removeHtml(port->getRenderer()->getConstantAsStringToRender().c_str())).contains(searchText, Qt::CaseInsensitive))
312  {
313  if (isCollapsedTypecast && typecastInPort->getRenderer()->getTypecastParentPort()->getRenderedParentNode())
314  searchResults.push_back(typecastInPort->getRenderer()->getTypecastParentPort()->getRenderedParentNode());
315  else
316  searchResults.push_back(node->getRenderer());
317  nodeMatched = true;
318  }
319  }
320  }
321  }
322  }
323 
324  // Now search comments.
325  foreach (VuoComment *comment, composition->getBase()->getComments())
326  {
327  // Only match starting at the beginning of words within the comment text.
328  int index = QString(comment->getContent().c_str()).toLower().indexOf(searchText.toLower());
329  if (index == 0 || ((index > 0) && !(QString(comment->getContent().c_str()).toLower().at(index-1).isLetterOrNumber())))
330  searchResults.push_back(comment->getRenderer());
331  }
332 
333  sort(searchResults.begin(), searchResults.end(), itemLessThan);
334  searchResults.erase(std::unique(searchResults.begin(), searchResults.end()), searchResults.end());
335 
336  return searchResults;
337 }
338 
342 vector<QGraphicsItem *> VuoSearchBox::findDeprecatedNodes()
343 {
344  // Search nodes.
345  foreach (VuoNode *node, composition->getBase()->getNodes())
346  {
347  bool nodeIsDeprecated = node->getNodeClass()->getDeprecated();
348  if (nodeIsDeprecated && !excludeNodeFromSearchResults(node))
349  searchResults.push_back(node->getRenderer());
350  }
351 
352  sort(searchResults.begin(), searchResults.end(), itemLessThan);
353  searchResults.erase(std::unique(searchResults.begin(), searchResults.end()), searchResults.end());
354 
355  return searchResults;
356 }
357 
361 vector<QGraphicsItem *> VuoSearchBox::findSubcompositionNodes()
362 {
363  // Search nodes.
364  foreach (VuoNode *node, composition->getBase()->getNodes())
365  {
366 
367  bool nodeIsSubcomposition = node->getNodeClass()->hasCompiler()?
369  false;
370  if (nodeIsSubcomposition && !excludeNodeFromSearchResults(node))
371  searchResults.push_back(node->getRenderer());
372  }
373 
374  sort(searchResults.begin(), searchResults.end(), itemLessThan);
375  searchResults.erase(std::unique(searchResults.begin(), searchResults.end()), searchResults.end());
376 
377  return searchResults;
378 }
379 
383 vector<QGraphicsItem *> VuoSearchBox::findShaderNodes()
384 {
385  // Search nodes.
386  foreach (VuoNode *node, composition->getBase()->getNodes())
387  {
388 
389  bool nodeIsShader = node->getNodeClass()->hasCompiler()?
390  node->getNodeClass()->getCompiler()->isIsf() :
391  false;
392  if (nodeIsShader && !excludeNodeFromSearchResults(node))
393  searchResults.push_back(node->getRenderer());
394  }
395 
396  sort(searchResults.begin(), searchResults.end(), itemLessThan);
397  searchResults.erase(std::unique(searchResults.begin(), searchResults.end()), searchResults.end());
398 
399  return searchResults;
400 }
401 
405 vector<QGraphicsItem *> VuoSearchBox::findCLanguageNodes()
406 {
407  // Search nodes.
408  foreach (VuoNode *node, composition->getBase()->getNodes())
409  {
410  VuoNodeClass *nodeClass = node->getNodeClass();
411  QString actionText, sourcePath;
412  bool nodeIsEditable = VuoEditorUtilities::isNodeClassEditable(nodeClass, actionText, sourcePath);
413  bool nodeHasExternalSource = nodeIsEditable && !nodeClass->getCompiler()->isSubcomposition() && !nodeClass->getCompiler()->isIsf();
414 
415  if (nodeHasExternalSource && !excludeNodeFromSearchResults(node))
416  searchResults.push_back(node->getRenderer());
417  }
418 
419  sort(searchResults.begin(), searchResults.end(), itemLessThan);
420  searchResults.erase(std::unique(searchResults.begin(), searchResults.end()), searchResults.end());
421 
422  return searchResults;
423 }
424 
428 vector<QGraphicsItem *> VuoSearchBox::find3rdPartyPrecompiledNodes()
429 {
430  // Search nodes.
431  foreach (VuoNode *node, composition->getBase()->getNodes())
432  {
433  VuoNodeClass *nodeClass = node->getNodeClass();
434  QString actionText, sourcePath;
435  bool nodeIs3rdParty = node->getNodeClass()->hasCompiler()?
436  !nodeClass->getCompiler()->isBuiltIn():
437  false;
438  bool nodeIsEditable = VuoEditorUtilities::isNodeClassEditable(nodeClass, actionText, sourcePath);
439 
440  if (nodeIs3rdParty && !nodeIsEditable && !excludeNodeFromSearchResults(node))
441  searchResults.push_back(node->getRenderer());
442  }
443 
444  sort(searchResults.begin(), searchResults.end(), itemLessThan);
445  searchResults.erase(std::unique(searchResults.begin(), searchResults.end()), searchResults.end());
446 
447  return searchResults;
448 }
449 
454 bool VuoSearchBox::excludeNodeFromSearchResults(VuoNode *node)
455 {
456  // Check whether we're dealing with a collapsed typecast.
457  bool isCollapsedTypecast = false;
458  vector<VuoPort *> inputPorts = node->getInputPorts();
459  VuoPort *typecastInPort = (inputPorts.size() >= VuoNodeClass::unreservedInputPortStartIndex+1?
461  NULL);
462  if (typecastInPort && typecastInPort->hasRenderer() && typecastInPort->getRenderer()->getTypecastParentPort())
463  isCollapsedTypecast = true;
464 
465  // Check whether we're dealing with another type of attachment.
466  bool isAttachment = (dynamic_cast<VuoRendererInputAttachment *>(node->getRenderer()));
467 
468  return (isCollapsedTypecast || isAttachment);
469 }
470 
475 {
476  bool startingOver = false;
477  vector<QGraphicsItem *> newSearchResults = getCurrentSearchResults(ui->searchText->text());
478  if (newSearchResults != searchResults)
479  {
480  searchResults.clear();
481  startingOver = true;
482  searchResults = newSearchResults;
483  }
484 
485  composition->clearSelection();
486  updateSpotlightedItems();
487 
488  if (searchResults.size() > 0)
489  {
490  currentResultIndex = (startingOver? 0 : (currentResultIndex + 1) % searchResults.size());
491  searchResults[currentResultIndex]->setSelected(true);
492  }
493 
494  updateResultCount();
495  updateViewportToFitResults();
496 }
497 
502 {
503  bool startingOver = false;
504  vector<QGraphicsItem *> newSearchResults = getCurrentSearchResults(ui->searchText->text());
505  if (newSearchResults != searchResults)
506  {
507  searchResults.clear();
508  startingOver = true;
509  searchResults = newSearchResults;
510  }
511 
512  composition->clearSelection();
513  updateSpotlightedItems();
514 
515  if (searchResults.size() > 0)
516  {
517  currentResultIndex = (startingOver? 0 : currentResultIndex - 1);
518  if (currentResultIndex < 0)
519  currentResultIndex += searchResults.size();
520 
521  searchResults[currentResultIndex]->setSelected(true);
522  }
523 
524  updateResultCount();
525  updateViewportToFitResults();
526 }
527 
532 {
533  if (isHidden())
534  return;
535 
536  bool startingOver = false;
537  vector<QGraphicsItem *> newSearchResults = getCurrentSearchResults(ui->searchText->text());
538  if (newSearchResults != searchResults)
539  {
540  searchResults.clear();
541  startingOver = true;
542  searchResults = newSearchResults;
543  }
544 
545  composition->clearSelection();
546  updateSpotlightedItems();
547 
548  if (searchResults.size() > 0)
549  {
550  currentResultIndex = (startingOver? 0 : currentResultIndex);
551  searchResults[currentResultIndex]->setSelected(true);
552  }
553 
554  updateResultCount();
555  updateViewportToFitResults();
556 }
557 
558 
562 void VuoSearchBox::updateViewportToFitResults()
563 {
564  QRectF itemsTightBoundingRect = (!composition->selectedItems().isEmpty()? composition->internalSelectedItemsChildrenBoundingRect() :
565  composition->internalItemsBoundingRect());
566 
567  if (!composition->selectedItems().isEmpty() &&
568  !composition->views()[0]->mapToScene(composition->views()[0]->rect()).boundingRect().
569  contains(QRect(itemsTightBoundingRect.topLeft().toPoint(),
570  itemsTightBoundingRect.bottomRight().toPoint())))
571  {
572  composition->views()[0]->ensureVisible(itemsTightBoundingRect);
573  }
574 }
575 
580 void VuoSearchBox::updateResultCount()
581 {
582  if (ui->searchText->text().isEmpty())
583  resultCount->setText("");
584  else if (searchResults.size() == 0)
585  resultCount->setText(noResultsText);
586  else
587  resultCount->setText(QString("%1 of %2").arg(currentResultIndex+1).arg(searchResults.size()));
588 
589  ui->nextButton->setEnabled(searchResults.size() >= 1);
590  ui->nextButton->setAutoRaise(!ui->nextButton->isEnabled());
591  ui->previousButton->setEnabled(searchResults.size() >= 1);
592  ui->previousButton->setAutoRaise(!ui->previousButton->isEnabled());
593 }
594 
598 bool VuoSearchBox::eventFilter(QObject *object, QEvent *event)
599 {
600  if ((event->type() == QEvent::KeyPress) && (static_cast<QKeyEvent *>(event)->key() == Qt::Key_Return))
601  goToNextResult();
602  else if ((event->type() == QEvent::KeyPress) && (static_cast<QKeyEvent *>(event)->key() == Qt::Key_G)
603  && (static_cast<QKeyEvent *>(event)->modifiers() & Qt::ControlModifier)
604  && (!(static_cast<QKeyEvent *>(event)->modifiers() & Qt::ShiftModifier)))
605  goToNextResult();
606  else if ((event->type() == QEvent::KeyPress) && (static_cast<QKeyEvent *>(event)->key() == Qt::Key_G)
607  && (static_cast<QKeyEvent *>(event)->modifiers() & Qt::ControlModifier)
608  && (static_cast<QKeyEvent *>(event)->modifiers() & Qt::ShiftModifier))
610  else if ((event->type() == QEvent::KeyPress) && (static_cast<QKeyEvent *>(event)->key() == Qt::Key_Escape))
611  close();
612  else
613  {
614  object->removeEventFilter(this);
615  QApplication::sendEvent(object, event);
616  object->installEventFilter(this);
617  }
618 
619  return true;
620 }
621 
625 void VuoSearchBox::resizeEvent(QResizeEvent *event)
626 {
627  QDockWidget::resizeEvent(event);
628  repositionChildWidgets();
629 }
630 
634 void VuoSearchBox::closeEvent(QCloseEvent *event)
635 {
636  if (!composition->getShowEventsMode() && composition->getRenderNodeActivity())
637  composition->stopDisplayingActivity();
638 
639  QDockWidget::closeEvent(event);
640 }
641 
645 void VuoSearchBox::repositionChildWidgets()
646 {
647  ui->searchText->resize(widget()->geometry().width()-ui->previousButton->geometry().width()
648  -ui->nextButton->geometry().width()
649  -ui->doneButton->geometry().width()-5,
650  ui->searchText->height());
651  resultCount->move(ui->searchText->geometry().right()-resultCount->width()-5, ui->searchText->pos().y()-5);
652  ui->previousButton->move(ui->searchText->geometry().right()+5, ui->previousButton->y());
653  ui->nextButton->move(ui->previousButton->geometry().right()+2, ui->nextButton->y());
654  ui->doneButton->move(ui->nextButton->geometry().right(), ui->doneButton->y());
655  update();
656 }
657 
661 void VuoSearchBox::updateColor(bool isDark)
662 {
663  QString barBackgroundColor = isDark ? "#919191" : "#efefef";
664  QString pressedToolButtonBackgroundColor = isDark ? "#aaaaaa" : "#d4d4d4";
665  QString disabledToolButtonArrowColor = isDark ? "#707070" : "#c0c0c0";
666 
667  QString styleSheet = VuoDialogForInputEditor::getStyleSheet(isDark)
668  + VUO_QSTRINGIFY(
669  QWidget#searchBoxContents {
670  background-color: %1;
671  }
672  QToolButton#previousButton,
673  QToolButton#nextButton {
674  border: 1px;
675  background-color: %1;
676  border-radius: 4px;
677  }
678  QToolButton#previousButton:pressed,
679  QToolButton#nextButton:pressed {
680  background-color: %2;
681  }
682  QToolButton#previousButton:disabled,
683  QToolButton#nextButton:disabled {
684  color: %3;
685  }
686  )
687  .arg(barBackgroundColor)
688  .arg(pressedToolButtonBackgroundColor)
689  .arg(disabledToolButtonArrowColor);
690 
691  if (isDark)
692  {
693  QString focusRingColor = "#1d6ae5";
694  styleSheet += VUO_QSTRINGIFY(
695  QLineEdit {
696  border: 1px solid #919191;
697  background: #262626;
698  padding-top: 2px;
699  }
700  QLineEdit:focus {
701  border: 2px solid %1;
702  padding-top: 1px;
703  }
704 
705  QPushButton {
706  border: none;
707  background: #aaaaaa;
708  border-radius: 4px;
709  margin: 4px 7px 7px 5px;
710  }
711  QPushButton:pressed {
712  background: #cccccc;
713  }
714  QPushButton:focus {
715  border: 2px solid %1;
716  }
717  )
718  .arg(focusRingColor);
719  }
720 
721  ui->searchBoxContents->setStyleSheet(styleSheet);
722 
723  ui->searchText->setAttribute(Qt::WA_MacShowFocusRect, !isDark);
724 }
725 
731 bool VuoSearchBox::itemLessThan(QGraphicsItem *item1, QGraphicsItem *item2)
732 {
733  int item1RowNum = item1->scenePos().y()/VuoRendererComposition::majorGridLineSpacing;
734  int item2RowNum = item2->scenePos().y()/VuoRendererComposition::majorGridLineSpacing;
735 
736  if (item1RowNum < item2RowNum)
737  return true;
738  else if (item2RowNum < item1RowNum)
739  return false;
740  return (item1->scenePos().x() < item2->scenePos().x());
741 }
742 
747 {
748  return (ui->nextButton->isEnabled() && ui->previousButton->isEnabled());
749 }
750 
751 VuoSearchBox::~VuoSearchBox()
752 {
753  delete ui;
754 }