56 toggleInputPortSidebarAction =
nullptr;
57 toggleDocumentationSidebarAction =
nullptr;
58 zoom11Action =
nullptr;
59 zoomInAction =
nullptr;
60 zoomOutAction =
nullptr;
63 restartAction =
nullptr;
64 reloadAction =
nullptr;
67 recentFileMenu =
nullptr;
69 includeInRecentFileMenu =
true;
70 publishedInputsModified =
false;
71 metadataModified =
false;
85 raiseDocumentAction =
new QAction(
this);
86 raiseDocumentAction->setCheckable(
true);
91 setUnifiedTitleAndToolBarOnMac(
true);
94 addDockWidget(Qt::LeftDockWidgetArea, inputPortSidebar);
98 setSourcePath(sourcePath);
100 if (!isNewUnsavedDocument())
107 bringNodeClassInSyncWithSourceCode();
110 documentationSidebar->setVisible(
static_cast<VuoEditor *
>(qApp)->getGlobalShaderDocumentationVisibility());
111 connect(documentationSidebar, &VuoDocumentationSidebar::visibilityChanged,
this, &VuoCodeWindow::updateDocumentationSidebarMenuItem);
113 addDockWidget(Qt::RightDockWidgetArea, documentationSidebar);
116 setMinimumWidth(650);
117 stages->setMinimumWidth(200);
118 resizeDocks({documentationSidebar}, {250}, Qt::Horizontal);
120 this->setCorner(Qt::BottomLeftCorner, Qt::LeftDockWidgetArea);
124 inputEditorSession =
nullptr;
125 connect(inputPortSidebar, &VuoPublishedPortSidebar::visibilityChanged,
this, &VuoCodeWindow::updateInputPortSidebarMenuItem);
132 metadataEditor =
new VuoMetadataEditor(wrapperComposition,
this, Qt::Sheet,
true);
133 metadataEditor->setWindowModality(Qt::WindowModal);
134 connect(metadataEditor, &VuoMetadataEditor::finished,
this, &VuoCodeWindow::changeMetadata);
137 addDockWidget(Qt::BottomDockWidgetArea,
issueList);
150 updateCanvasOpacity();
151 updateModifiedIndicator();
153 static_cast<VuoEditor *
>(qApp)->registerOpenDocument(
this);
156 VuoCodeWindow::~VuoCodeWindow(
void)
158 relinquishSourcePath();
162 delete wrapperComposition;
168 void VuoCodeWindow::populateMenus(
void)
172 QMenu *m =
new QMenu(menuBar());
173 m->setSeparatorsCollapsible(
false);
174 m->setTitle(tr(
"&File"));
181 QMenu *mm =
new QMenu(tr(
"New Composition from Template"));
182 mm->setSeparatorsCollapsible(
false);
184 static_cast<VuoEditor *
>(qApp)->populateNewCompositionWithTemplateMenu(mm);
191 QMenu *mm =
new QMenu(tr(
"New Shader"));
192 mm->setSeparatorsCollapsible(
false);
194 static_cast<VuoEditor *
>(qApp)->populateNewShaderMenu(mm);
208 m->addMenu(recentFileMenu);
212 saveAction = m->addAction(tr(
"&Save"),
this, &VuoCodeWindow::save, QKeySequence(
"Ctrl+S"));
213 m->addAction(tr(
"Save As…"),
this, &VuoCodeWindow::saveAs, QKeySequence(
"Ctrl+Shift+S"));
216 m->addAction(tr(
"Close"),
this, &VuoCodeWindow::close, QKeySequence(
"Ctrl+W"));
222 menuBar()->addAction(m->menuAction());
227 QMenu *m =
new QMenu(menuBar());
228 m->setSeparatorsCollapsible(
false);
229 m->setTitle(tr(
"Edit"));
232 m->addAction(tr(
"Undo"),
stages->
currentEditor(), &VuoCodeEditor::undo, QKeySequence(
"Ctrl+Z"));
233 m->addAction(tr(
"Redo"),
stages->
currentEditor(), &VuoCodeEditor::redo, QKeySequence(
"Ctrl+Shift+Z"));
236 m->addAction(tr(
"Cut"),
stages->
currentEditor(), &VuoCodeEditor::cut, QKeySequence(
"Ctrl+X"));
237 m->addAction(tr(
"Copy"),
this, &VuoCodeWindow::copy, QKeySequence(
"Ctrl+C"));
238 m->addAction(tr(
"Paste"),
stages->
currentEditor(), &VuoCodeEditor::paste, QKeySequence(
"Ctrl+V"));
239 m->addAction(tr(
"Delete"), [
this](){
stages->
currentEditor()->textCursor().removeSelectedText(); });
242 m->addAction(tr(
"Select All"),
stages->
currentEditor(), &VuoCodeEditor::selectAll, QKeySequence(
"Ctrl+A"));
245 m->addAction(tr(
"Composition Information…"), [
this](){ metadataEditor->
show(); }, QKeySequence(
"Ctrl+I"));
247 menuBar()->addAction(m->menuAction());
252 QMenu *m =
new QMenu(menuBar());
253 m->setSeparatorsCollapsible(
false);
254 m->setTitle(tr(
"&View"));
256 toggleInputPortSidebarAction = m->addAction(tr(
"Hide Published Ports"),
this, &VuoCodeWindow::toggleInputPortSidebarVisibility, QKeySequence(
"Ctrl+4"));
257 toggleDocumentationSidebarAction = m->addAction(
"",
this, &VuoCodeWindow::toggleDocumentationSidebarVisibility, QKeySequence(
"Ctrl+5"));
258 updateDocumentationSidebarMenuItem();
261 zoom11Action = m->addAction(tr(
"Actual Size"),
this, &VuoCodeWindow::zoom11, QKeySequence(
"Ctrl+0"));
262 zoomInAction = m->addAction(tr(
"Zoom In"),
this, &VuoCodeWindow::zoomIn, QKeySequence(
"Ctrl+="));
263 zoomOutAction = m->addAction(tr(
"Zoom Out"),
this, &VuoCodeWindow::zoomOut, QKeySequence(
"Ctrl+-"));
269 QMenu *mm =
new QMenu(menuBar());
270 mm->setSeparatorsCollapsible(
false);
271 mm->setTitle(tr(
"&Canvas Transparency"));
273 static_cast<VuoEditor *
>(qApp)->populateCanvasTransparencyMenu(mm);
279 menuBar()->addAction(m->menuAction());
284 QMenu *m =
new QMenu(menuBar());
285 m->setSeparatorsCollapsible(
false);
286 m->setTitle(tr(
"Run"));
293 reloadAction = m->addAction(tr(
"Reload"), [=]{
295 bringNodeClassInSyncWithSourceCode();
297 reloadAction->setShortcuts({ QKeySequence(
"Ctrl+Return"), QKeySequence(
"Alt+Return") });
299 stopAction->setEnabled(
false);
300 restartAction->setEnabled(
false);
303 menuBar()->addAction(m->menuAction());
308 QMenu *m =
new QMenu(menuBar());
309 m->setSeparatorsCollapsible(
false);
310 m->setTitle(tr(
"Tools"));
315 menuBar()->addAction(m->menuAction());
320 QMenu *m =
new QMenu(menuBar());
321 m->setSeparatorsCollapsible(
false);
322 m->setTitle(tr(
"&Window"));
324 static_cast<VuoEditor *
>(qApp)->populateWindowMenu(m,
this);
326 connect(windowMenu, &QMenu::aboutToShow,
this, &VuoCodeWindow::updateWindowMenu);
328 menuBar()->addAction(m->menuAction());
333 QMenu *m =
new QMenu(menuBar());
334 m->setSeparatorsCollapsible(
false);
335 m->setTitle(tr(
"&Help"));
337 static_cast<VuoEditor *
>(qApp)->populateHelpMenu(m);
339 menuBar()->addAction(m->menuAction());
348 QString selectedTemplate =
static_cast<QString
>(sender->data().value<QString>());
363 void VuoCodeWindow::save()
365 if (isNewUnsavedDocument())
370 saveToPath(windowFilePath());
377 void VuoCodeWindow::saveAs()
379 QFileDialog d(
this, Qt::Sheet);
380 d.setAcceptMode(QFileDialog::AcceptSave);
382 if (isNewUnsavedDocument())
383 d.selectFile(
static_cast<VuoEditor *
>(qApp)->getSubcompositionPrefix() +
".shader.fs");
385 d.setDirectory(windowFilePath());
387 if (d.exec() == QDialog::Accepted)
389 string dir, file, ext;
392 saveToPath(d.selectedFiles()[0]);
400 void VuoCodeWindow::saveToPath(QString savePath)
402 bool saveAborted =
false;
403 QString failureDetails =
"";
404 QString expectedFileExtension =
".fs";
405 if (! savePath.endsWith(expectedFileExtension))
407 savePath.append(expectedFileExtension);
411 failureDetails =
"A file or folder with the same name already exists.";
415 bool saveSucceeded =
false;
420 bringStoredShaderInSyncWithSourceCode();
421 shaderFile->
save(savePath.toStdString());
422 saveSucceeded =
true;
426 failureDetails = e.
what();
433 setSourcePath(savePath.toStdString());
436 publishedInputsModified =
false;
437 metadataModified =
false;
438 updateModifiedIndicator();
440 if (includeInRecentFileMenu)
441 static_cast<VuoEditor *
>(qApp)->addFileToAllOpenRecentFileMenus(savePath);
445 QMessageBox fileSaveFailureDialog(
this);
446 fileSaveFailureDialog.setWindowFlags(Qt::Sheet);
447 fileSaveFailureDialog.setWindowModality(Qt::WindowModal);
448 fileSaveFailureDialog.setText(tr(
"The shader could not be saved at “%1”.").arg(savePath));
449 fileSaveFailureDialog.setStyleSheet(
"#qt_msgbox_informativelabel { font-weight: normal; font-size: 11pt; }");
450 fileSaveFailureDialog.setInformativeText(failureDetails);
451 fileSaveFailureDialog.setStandardButtons(QMessageBox::Save | QMessageBox::Cancel);
452 fileSaveFailureDialog.setButtonText(QMessageBox::Save, tr(
"Save As…"));
453 fileSaveFailureDialog.setIcon(QMessageBox::Warning);
455 switch(fileSaveFailureDialog.exec()) {
456 case QMessageBox::Save:
459 case QMessageBox::Cancel:
470 bool VuoCodeWindow::isNewUnsavedDocument()
472 string dir, file, ext;
484 this->includeInRecentFileMenu = include;
490 void VuoCodeWindow::closeEvent(QCloseEvent *event)
500 if (inputEditorSession
508 auto closeAndContinueQuit = [
this](){
509 if (isNewUnsavedDocument())
512 static_cast<VuoEditor *
>(qApp)->addFileToRecentlyClosedList(windowFilePath());
526 static_cast<VuoEditor *
>(qApp)->continueQuit(
this);
531 auto mb =
new QMessageBox(
this);
532 mb->setWindowFlags(Qt::Sheet);
533 mb->setWindowModality(Qt::WindowModal);
535 mb->setText(tr(
"Do you want to save the changes made to the document “%1”?").arg(windowTitle()));
536 mb->setStyleSheet(
"#qt_msgbox_informativelabel { font-weight: normal; font-size: 11pt; }");
537 mb->setInformativeText(tr(
"Your changes will be lost if you don’t save them."));
540 mb->setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel);
541 mb->setDefaultButton(QMessageBox::Save);
543 static_cast<QPushButton *
>(mb->button(QMessageBox::Discard))->setAutoDefault(
false);
544 mb->button(QMessageBox::Discard)->setFocus();
546 connect(mb, &QMessageBox::buttonClicked,
this, [=](QAbstractButton *button){
547 auto role = mb->buttonRole(button);
548 if (role == QMessageBox::AcceptRole)
553 static_cast<VuoEditor *
>(qApp)->cancelQuit();
555 closeAndContinueQuit();
557 else if (role == QMessageBox::DestructiveRole)
558 closeAndContinueQuit();
560 static_cast<VuoEditor *
>(qApp)->cancelQuit();
570 closeAndContinueQuit();
586 bringNodeClassInSyncWithSourceCode();
589 wrapperComposition->
run(snapshot);
599 showStopActivityIndicator();
601 wrapperComposition->
stop();
617 void VuoCodeWindow::relinquishSourcePath(
void)
620 shaderFile =
nullptr;
624 string oldSourcePath = windowFilePath().toStdString();
625 if (! oldSourcePath.empty())
638 if (isNewUnsavedDocument())
647 void VuoCodeWindow::setSourcePath(
const string &sourcePath)
649 relinquishSourcePath();
651 string dir, file, ext;
661 delete wrapperComposition;
665 setWindowFilePath(QString::fromStdString(sourcePath));
668 if (isNewUnsavedDocument())
670 set<string> takenTitles;
672 takenTitles.insert(openWindow->windowTitle().toStdString());
673 string preferredTitle =
"Untitled " + shaderFile->
typeName();
678 title = file +
"." + ext;
680 setWindowTitle(QString::fromStdString(title));
682 toolbar->updateTitle();
685 raiseDocumentAction->setText(windowTitle());
690 updateWrapperComposition();
707 void VuoCodeWindow::updateWrapperComposition(
void)
709 string oldSnapshot = wrapperComposition->
takeSnapshot();
710 wrapperComposition->
clear();
713 wrapperComposition->
addNode(node);
725 bool ok = !wrapperComposition->
getBase()->getPublishedInputPortWithName(name);
727 if (shaderFile->
type() == VuoShaderFile::GLSLImageFilter)
728 ok &= name !=
"image";
729 else if (shaderFile->
type() == VuoShaderFile::GLSLImageGenerator)
730 ok &= name !=
"width"
733 ok &= name !=
"time";
739 if (shaderFile->
type() == VuoShaderFile::GLSLImageFilter)
741 if (publishedName ==
"inputImage")
742 publishedName =
"image";
744 else if (shaderFile->
type() == VuoShaderFile::GLSLImageGenerator)
746 if (publishedName ==
"vuoWidth")
747 publishedName =
"width";
748 else if (publishedName ==
"vuoHeight")
749 publishedName =
"height";
751 if (publishedName ==
"vuoTime")
752 publishedName =
"time";
795 publishedName =
"outputImage";
806 if (shaderFile->
type() == VuoShaderFile::GLSLImageFilter)
808 else if (shaderFile->
type() == VuoShaderFile::GLSLImageGenerator)
810 else if (shaderFile->
type() == VuoShaderFile::GLSLImageTransition)
819 string newSnapshot = wrapperComposition->
takeSnapshot();
820 if (oldSnapshot != newSnapshot)
831 void VuoCodeWindow::bringNodeClassInSyncWithSourceCode(
void)
833 bringStoredShaderInSyncWithSourceCode();
841 void VuoCodeWindow::bringStoredShaderInSyncWithSourceCode(
void)
850 const pair<string, string> &publishedInputRenamed,
851 const string &publishedInputRemoved)
853 vector<VuoShaderFile::Port> shaderInputs = shaderFile->
inputPorts();
855 for (
auto i = shaderInputs.begin(); i != shaderInputs.end(); )
857 if (i->key == publishedInputRemoved)
858 i = shaderInputs.erase(i);
865 if (shaderInput.key == publishedInputRenamed.first)
866 shaderInput.key = publishedInputRenamed.second;
872 json_object *oldDetails = shaderInput.vuoPortDetails;
873 shaderInput.vuoPortDetails = compilerPublishedInput->
getDetails(
true);
875 if (json_object_object_get_ex(oldDetails,
"scaleToSamplerRect", &scaleToSamplerRectValue))
876 json_object_object_add(shaderInput.vuoPortDetails,
"scaleToSamplerRect", scaleToSamplerRectValue);
880 if (publishedInputAdded)
885 addedInput.vuoPortDetails = publishedInputAdded->
getDetails(
true);
886 shaderInputs.push_back(addedInput);
901 issues->
addIssue(VuoShaderFile::Fragment, compilerIssue.getLineNumber(), compilerIssue.getDetails(
false));
924 void VuoCodeWindow::updateModifiedIndicator()
926 bool modified =
stages->
modified() || publishedInputsModified || metadataModified;
927 setWindowModified(modified);
929 bool saveEnabled = modified || isNewUnsavedDocument();
930 saveAction->setEnabled(saveEnabled);
933 void VuoCodeWindow::updateColor()
935 bool isDark =
static_cast<VuoEditor *
>(qApp)->isInterfaceDark();
938 QString menuStyle = VUO_QSTRINGIFY(
942 background-color: #404040;
950 QMenu::item:disabled {
953 QMenu::item:selected {
954 background-color: #1060d0;
960 QMenu::indicator:checked {
961 image: url(:/Icons/checkmark.svg);
964 QMenu::indicator:checked,
969 QMenu::icon:unchecked {
974 setStyleSheet(VUO_QSTRINGIFY(
976 QMainWindow::separator {
981 background: transparent;
988 + (isDark ? menuStyle :
""));
994 void VuoCodeWindow::updateCanvasOpacity()
996 int opacity =
static_cast<VuoEditor *
>(qApp)->getCanvasOpacity();
1000 void VuoCodeWindow::updateToolbar()
1005 void VuoCodeWindow::resizeEvent(QResizeEvent *event)
1009 toolbar->updateTitle();
1012 QMainWindow::resizeEvent(event);
1020 return raiseDocumentAction;
1028 return zoom11Action;
1036 return zoomInAction;
1044 return zoomOutAction;
1060 return recentFileMenu;
1080 void VuoCodeWindow::updateWindowMenu()
1082 windowMenu->clear();
1083 static_cast<VuoEditor *
>(qApp)->populateWindowMenu(windowMenu,
this);
1091 __block
bool isRunning = wrapperComposition->
isRunning();
1094 string sourcePath = windowFilePath().toStdString();
1095 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToAllOtherTopLevelCompositions(
nullptr, ^(
VuoEditorComposition *topLevelComposition)
1102 reloadAction->setEnabled(isRunning);
1108 void VuoCodeWindow::toggleInputPortSidebarVisibility()
1110 bool becomingVisible = ! inputPortSidebar->isVisible();
1111 inputPortSidebar->setVisible(becomingVisible);
1112 updateInputPortSidebarMenuItem();
1118 void VuoCodeWindow::updateInputPortSidebarMenuItem()
1120 toggleInputPortSidebarAction->setText(inputPortSidebar->isVisible() ? tr(
"Hide Published Ports") : tr(
"Show Published Ports"));
1126 void VuoCodeWindow::toggleDocumentationSidebarVisibility()
1128 bool becomingVisible = ! documentationSidebar->isVisible();
1129 documentationSidebar->setVisible(becomingVisible);
1130 updateDocumentationSidebarMenuItem();
1136 void VuoCodeWindow::updateDocumentationSidebarMenuItem()
1138 toggleDocumentationSidebarAction->setText(documentationSidebar->isVisible() ? tr(
"Hide GLSL/ISF Quick Reference") : tr(
"Show GLSL/ISF Quick Reference"));
1148 inputEditorSession =
new VuoInputEditorSession(inputEditorManager, wrapperComposition, inputPortSidebar,
this);
1149 map<VuoRendererPort *, pair<string, string> > originalAndFinalValueForPort = inputEditorSession->
execute(port,
true);
1151 delete inputEditorSession;
1152 inputEditorSession =
nullptr;
1154 bool valueChanged =
false;
1155 for (
auto i : originalAndFinalValueForPort)
1158 string originalEditingSessionValue = i.second.first;
1159 string finalEditingSessionValue = i.second.second;
1161 if (finalEditingSessionValue != originalEditingSessionValue)
1163 valueChanged =
true;
1169 finalEditingSessionValue.c_str());
1180 bringStoredShaderInSyncWithPublishedInputPorts();
1181 publishedInputsModified =
true;
1182 updateModifiedIndicator();
1193 VUserLog(
"%s: Set published port '%s' details to %s",
1196 json_object_to_json_string(newDetails));
1200 bringStoredShaderInSyncWithPublishedInputPorts();
1201 publishedInputsModified =
true;
1202 updateModifiedIndicator();
1216 if (originalName != newName)
1217 changePublishedPortName(port, newName);
1227 string oldSnapshot = wrapperComposition->
takeSnapshot();
1235 VUserLog(
"%s: Rename published port %s to %s",
1240 bringStoredShaderInSyncWithPublishedInputPorts(
nullptr, {oldName, newName});
1241 publishedInputsModified =
true;
1242 updateModifiedIndicator();
1244 string newSnapshot = wrapperComposition->
takeSnapshot();
1253 void VuoCodeWindow::addPublishedPort(
string typeName,
bool isInput)
1255 string oldSnapshot = wrapperComposition->
takeSnapshot();
1260 VUserLog(
"%s: Add published %s port %s %s",
1262 isInput ?
"input" :
"output",
1263 typeName.empty() ?
"event" : typeName.c_str(),
1270 bringStoredShaderInSyncWithPublishedInputPorts(publishedPort);
1271 publishedInputsModified =
true;
1272 updateModifiedIndicator();
1274 string newSnapshot = wrapperComposition->
takeSnapshot();
1280 if (portName != newName)
1281 changePublishedPortName(rpp, newName);
1291 VUserLog(
"%s: Remove published input port %s",
1295 string oldSnapshot = wrapperComposition->
takeSnapshot();
1298 wrapperComposition->
removeCable(cable->getRenderer());
1303 bringStoredShaderInSyncWithPublishedInputPorts(
nullptr, {}, portName);
1304 publishedInputsModified =
true;
1305 updateModifiedIndicator();
1307 string newSnapshot = wrapperComposition->
takeSnapshot();
1316 void VuoCodeWindow::changeMetadata(
int dialogResult)
1318 if (dialogResult == QDialog::Accepted)
1325 metadataModified =
true;
1326 updateModifiedIndicator();
1328 VUserLog(
"%s: Set metadata to:\n%s",
1337 void VuoCodeWindow::showBuildActivityIndicator()
1342 runAction->setEnabled(
false);
1343 stopAction->setEnabled(
true);
1344 restartAction->setEnabled(
true);
1350 void VuoCodeWindow::hideBuildActivityIndicator(QString buildError)
1355 runAction->setEnabled(
false);
1356 stopAction->setEnabled(
true);
1357 restartAction->setEnabled(
true);
1364 void VuoCodeWindow::showStopActivityIndicator()
1370 stopAction->setEnabled(
false);
1371 restartAction->setEnabled(
false);
1377 void VuoCodeWindow::hideStopActivityIndicator()
1382 runAction->setEnabled(
true);
1383 stopAction->setEnabled(
false);
1384 restartAction->setEnabled(
false);
1391 void VuoCodeWindow::zoom11()
1395 zoom11Action->setEnabled(
false);
1396 toolbar->
update(
false,
true,
false);
1402 void VuoCodeWindow::zoomIn()
1407 zoom11Action->setEnabled(! isActualSize);
1408 toolbar->
update(
false, isActualSize,
false);
1414 void VuoCodeWindow::zoomOut()
1419 zoom11Action->setEnabled(! isActualSize);
1420 toolbar->
update(
false, isActualSize,
false);
1426 void VuoCodeWindow::copy()
1430 else if (documentationSidebar->isAncestorOf(qApp->focusWidget()))
1431 QGuiApplication::clipboard()->setText(documentationSidebar->
getSelectedText());