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);
97 setSourcePath(sourcePath);
99 if (!isNewUnsavedDocument())
106 bringNodeClassInSyncWithSourceCode();
109 documentationSidebar->setVisible(
static_cast<VuoEditor *
>(qApp)->getGlobalShaderDocumentationVisibility());
110 connect(documentationSidebar, &VuoDocumentationSidebar::visibilityChanged,
this, &VuoCodeWindow::updateDocumentationSidebarMenuItem);
112 addDockWidget(Qt::RightDockWidgetArea, documentationSidebar);
115 setMinimumWidth(650);
116 stages->setMinimumWidth(200);
117 resizeDocks({documentationSidebar}, {250}, Qt::Horizontal);
119 this->setCorner(Qt::BottomLeftCorner, Qt::LeftDockWidgetArea);
123 connect(inputPortSidebar, &VuoPublishedPortSidebar::visibilityChanged,
this, &VuoCodeWindow::updateInputPortSidebarMenuItem);
130 metadataEditor =
new VuoMetadataEditor(wrapperComposition,
this, Qt::Sheet,
true);
131 metadataEditor->setWindowModality(Qt::WindowModal);
132 connect(metadataEditor, &VuoMetadataEditor::finished,
this, &VuoCodeWindow::changeMetadata);
135 addDockWidget(Qt::BottomDockWidgetArea,
issueList);
148 updateCanvasOpacity();
149 updateModifiedIndicator();
151 static_cast<VuoEditor *
>(qApp)->registerOpenDocument(
this);
154 VuoCodeWindow::~VuoCodeWindow(
void)
156 relinquishSourcePath();
160 delete wrapperComposition;
166 void VuoCodeWindow::populateMenus(
void)
170 QMenu *m =
new QMenu(menuBar());
171 m->setSeparatorsCollapsible(
false);
172 m->setTitle(tr(
"&File"));
179 QMenu *mm =
new QMenu(tr(
"New Composition from Template"));
180 mm->setSeparatorsCollapsible(
false);
182 static_cast<VuoEditor *
>(qApp)->populateNewCompositionWithTemplateMenu(mm);
189 QMenu *mm =
new QMenu(tr(
"New Shader"));
190 mm->setSeparatorsCollapsible(
false);
192 static_cast<VuoEditor *
>(qApp)->populateNewShaderMenu(mm);
206 m->addMenu(recentFileMenu);
210 saveAction = m->addAction(tr(
"&Save"),
this, &VuoCodeWindow::save, QKeySequence(
"Ctrl+S"));
211 m->addAction(tr(
"Save As…"),
this, &VuoCodeWindow::saveAs, QKeySequence(
"Ctrl+Shift+S"));
214 m->addAction(tr(
"Close"),
this, &VuoCodeWindow::close, QKeySequence(
"Ctrl+W"));
220 menuBar()->addAction(m->menuAction());
225 QMenu *m =
new QMenu(menuBar());
226 m->setSeparatorsCollapsible(
false);
227 m->setTitle(tr(
"Edit"));
230 m->addAction(tr(
"Undo"),
stages->
currentEditor(), &VuoCodeEditor::undo, QKeySequence(
"Ctrl+Z"));
231 m->addAction(tr(
"Redo"),
stages->
currentEditor(), &VuoCodeEditor::redo, QKeySequence(
"Ctrl+Shift+Z"));
234 m->addAction(tr(
"Cut"),
stages->
currentEditor(), &VuoCodeEditor::cut, QKeySequence(
"Ctrl+X"));
235 m->addAction(tr(
"Copy"),
this, &VuoCodeWindow::copy, QKeySequence(
"Ctrl+C"));
236 m->addAction(tr(
"Paste"),
stages->
currentEditor(), &VuoCodeEditor::paste, QKeySequence(
"Ctrl+V"));
237 m->addAction(tr(
"Delete"), [
this](){
stages->
currentEditor()->textCursor().removeSelectedText(); });
240 m->addAction(tr(
"Select All"),
stages->
currentEditor(), &VuoCodeEditor::selectAll, QKeySequence(
"Ctrl+A"));
243 m->addAction(tr(
"Composition Information…"), [
this](){ metadataEditor->
show(); }, QKeySequence(
"Ctrl+I"));
245 menuBar()->addAction(m->menuAction());
250 QMenu *m =
new QMenu(menuBar());
251 m->setSeparatorsCollapsible(
false);
252 m->setTitle(tr(
"&View"));
254 toggleInputPortSidebarAction = m->addAction(tr(
"Hide Published Ports"),
this, &VuoCodeWindow::toggleInputPortSidebarVisibility, QKeySequence(
"Ctrl+4"));
255 toggleDocumentationSidebarAction = m->addAction(
"",
this, &VuoCodeWindow::toggleDocumentationSidebarVisibility, QKeySequence(
"Ctrl+5"));
256 updateDocumentationSidebarMenuItem();
259 zoom11Action = m->addAction(tr(
"Actual Size"),
this, &VuoCodeWindow::zoom11, QKeySequence(
"Ctrl+0"));
260 zoomInAction = m->addAction(tr(
"Zoom In"),
this, &VuoCodeWindow::zoomIn, QKeySequence(
"Ctrl+="));
261 zoomOutAction = m->addAction(tr(
"Zoom Out"),
this, &VuoCodeWindow::zoomOut, QKeySequence(
"Ctrl+-"));
267 QMenu *mm =
new QMenu(menuBar());
268 mm->setSeparatorsCollapsible(
false);
269 mm->setTitle(tr(
"&Canvas Transparency"));
271 static_cast<VuoEditor *
>(qApp)->populateCanvasTransparencyMenu(mm);
277 menuBar()->addAction(m->menuAction());
282 QMenu *m =
new QMenu(menuBar());
283 m->setSeparatorsCollapsible(
false);
284 m->setTitle(tr(
"Run"));
291 reloadAction = m->addAction(tr(
"Reload"), [=]{
293 bringNodeClassInSyncWithSourceCode();
295 reloadAction->setShortcuts({ QKeySequence(
"Ctrl+Return"), QKeySequence(
"Alt+Return") });
297 stopAction->setEnabled(
false);
298 restartAction->setEnabled(
false);
301 menuBar()->addAction(m->menuAction());
306 QMenu *m =
new QMenu(menuBar());
307 m->setSeparatorsCollapsible(
false);
308 m->setTitle(tr(
"Tools"));
313 menuBar()->addAction(m->menuAction());
318 QMenu *m =
new QMenu(menuBar());
319 m->setSeparatorsCollapsible(
false);
320 m->setTitle(tr(
"&Window"));
322 static_cast<VuoEditor *
>(qApp)->populateWindowMenu(m,
this);
324 connect(windowMenu, &QMenu::aboutToShow,
this, &VuoCodeWindow::updateWindowMenu);
326 menuBar()->addAction(m->menuAction());
331 QMenu *m =
new QMenu(menuBar());
332 m->setSeparatorsCollapsible(
false);
333 m->setTitle(tr(
"&Help"));
335 static_cast<VuoEditor *
>(qApp)->populateHelpMenu(m);
337 menuBar()->addAction(m->menuAction());
346 QString selectedTemplate =
static_cast<QString
>(sender->data().value<QString>());
361 void VuoCodeWindow::save()
363 if (isNewUnsavedDocument())
368 saveToPath(windowFilePath());
375 void VuoCodeWindow::saveAs()
377 QFileDialog d(
this, Qt::Sheet);
378 d.setAcceptMode(QFileDialog::AcceptSave);
380 if (isNewUnsavedDocument())
381 d.selectFile(
static_cast<VuoEditor *
>(qApp)->getSubcompositionPrefix() +
".shader.fs");
383 d.setDirectory(windowFilePath());
385 if (d.exec() == QDialog::Accepted)
387 string dir, file, ext;
390 saveToPath(d.selectedFiles()[0]);
398 void VuoCodeWindow::saveToPath(QString savePath)
400 bool saveAborted =
false;
401 QString failureDetails =
"";
402 QString expectedFileExtension =
".fs";
403 if (! savePath.endsWith(expectedFileExtension))
405 savePath.append(expectedFileExtension);
409 failureDetails =
"A file or folder with the same name already exists.";
413 bool saveSucceeded =
false;
418 bringStoredShaderInSyncWithSourceCode();
419 shaderFile->
save(savePath.toStdString());
420 saveSucceeded =
true;
424 failureDetails = e.
what();
431 setSourcePath(savePath.toStdString());
434 publishedInputsModified =
false;
435 metadataModified =
false;
436 updateModifiedIndicator();
438 if (includeInRecentFileMenu)
439 static_cast<VuoEditor *
>(qApp)->addFileToAllOpenRecentFileMenus(savePath);
443 QMessageBox fileSaveFailureDialog(
this);
444 fileSaveFailureDialog.setWindowFlags(Qt::Sheet);
445 fileSaveFailureDialog.setWindowModality(Qt::WindowModal);
446 fileSaveFailureDialog.setText(tr(
"The shader could not be saved at “%1”.").arg(savePath));
447 fileSaveFailureDialog.setStyleSheet(
"#qt_msgbox_informativelabel { font-weight: normal; font-size: 11pt; }");
448 fileSaveFailureDialog.setInformativeText(failureDetails);
449 fileSaveFailureDialog.setStandardButtons(QMessageBox::Save | QMessageBox::Cancel);
450 fileSaveFailureDialog.setButtonText(QMessageBox::Save, tr(
"Save As…"));
451 fileSaveFailureDialog.setIcon(QMessageBox::Warning);
453 switch(fileSaveFailureDialog.exec()) {
454 case QMessageBox::Save:
457 case QMessageBox::Cancel:
468 bool VuoCodeWindow::isNewUnsavedDocument()
470 string dir, file, ext;
482 this->includeInRecentFileMenu = include;
488 void VuoCodeWindow::closeEvent(QCloseEvent *event)
496 auto closeAndContinueQuit = [
this](){
497 if (isNewUnsavedDocument())
500 static_cast<VuoEditor *
>(qApp)->addFileToRecentlyClosedList(windowFilePath());
514 static_cast<VuoEditor *
>(qApp)->continueQuit(
this);
519 auto mb =
new QMessageBox(
this);
520 mb->setWindowFlags(Qt::Sheet);
521 mb->setWindowModality(Qt::WindowModal);
523 mb->setText(tr(
"Do you want to save the changes made to the document “%1”?").arg(windowTitle()));
524 mb->setStyleSheet(
"#qt_msgbox_informativelabel { font-weight: normal; font-size: 11pt; }");
525 mb->setInformativeText(tr(
"Your changes will be lost if you don’t save them."));
528 mb->setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel);
529 mb->setDefaultButton(QMessageBox::Save);
531 static_cast<QPushButton *
>(mb->button(QMessageBox::Discard))->setAutoDefault(
false);
532 mb->button(QMessageBox::Discard)->setFocus();
534 connect(mb, &QMessageBox::buttonClicked,
this, [=](QAbstractButton *button){
535 auto role = mb->buttonRole(button);
536 if (role == QMessageBox::AcceptRole)
541 static_cast<VuoEditor *
>(qApp)->cancelQuit();
543 closeAndContinueQuit();
545 else if (role == QMessageBox::DestructiveRole)
546 closeAndContinueQuit();
548 static_cast<VuoEditor *
>(qApp)->cancelQuit();
558 closeAndContinueQuit();
574 bringNodeClassInSyncWithSourceCode();
577 wrapperComposition->
run(snapshot);
587 showStopActivityIndicator();
589 wrapperComposition->
stop();
605 void VuoCodeWindow::relinquishSourcePath(
void)
608 shaderFile =
nullptr;
612 string oldSourcePath = windowFilePath().toStdString();
613 if (! oldSourcePath.empty())
626 if (isNewUnsavedDocument())
635 void VuoCodeWindow::setSourcePath(
const string &sourcePath)
637 relinquishSourcePath();
639 string dir, file, ext;
649 delete wrapperComposition;
653 setWindowFilePath(QString::fromStdString(sourcePath));
656 if (isNewUnsavedDocument())
658 set<string> takenTitles;
660 takenTitles.insert(openWindow->windowTitle().toStdString());
661 string preferredTitle =
"Untitled " + shaderFile->
typeName();
666 title = file +
"." + ext;
668 setWindowTitle(QString::fromStdString(title));
670 toolbar->updateTitle();
673 raiseDocumentAction->setText(windowTitle());
678 updateWrapperComposition();
695 void VuoCodeWindow::updateWrapperComposition(
void)
697 string oldSnapshot = wrapperComposition->
takeSnapshot();
698 wrapperComposition->
clear();
701 wrapperComposition->
addNode(node);
713 bool ok = !wrapperComposition->
getBase()->getPublishedInputPortWithName(name);
715 if (shaderFile->
type() == VuoShaderFile::GLSLImageFilter)
716 ok &= name !=
"image";
717 else if (shaderFile->
type() == VuoShaderFile::GLSLImageGenerator)
718 ok &= name !=
"width"
721 ok &= name !=
"time";
727 if (shaderFile->
type() == VuoShaderFile::GLSLImageFilter)
729 if (publishedName ==
"inputImage")
730 publishedName =
"image";
732 else if (shaderFile->
type() == VuoShaderFile::GLSLImageGenerator)
734 if (publishedName ==
"vuoWidth")
735 publishedName =
"width";
736 else if (publishedName ==
"vuoHeight")
737 publishedName =
"height";
739 if (publishedName ==
"vuoTime")
740 publishedName =
"time";
783 publishedName =
"outputImage";
794 if (shaderFile->
type() == VuoShaderFile::GLSLImageFilter)
796 else if (shaderFile->
type() == VuoShaderFile::GLSLImageGenerator)
798 else if (shaderFile->
type() == VuoShaderFile::GLSLImageTransition)
807 string newSnapshot = wrapperComposition->
takeSnapshot();
808 if (oldSnapshot != newSnapshot)
819 void VuoCodeWindow::bringNodeClassInSyncWithSourceCode(
void)
821 bringStoredShaderInSyncWithSourceCode();
829 void VuoCodeWindow::bringStoredShaderInSyncWithSourceCode(
void)
838 const pair<string, string> &publishedInputRenamed,
839 const string &publishedInputRemoved)
841 vector<VuoShaderFile::Port> shaderInputs = shaderFile->
inputPorts();
843 for (
auto i = shaderInputs.begin(); i != shaderInputs.end(); )
845 if (i->key == publishedInputRemoved)
846 i = shaderInputs.erase(i);
853 if (shaderInput.key == publishedInputRenamed.first)
854 shaderInput.key = publishedInputRenamed.second;
860 json_object *oldDetails = shaderInput.vuoPortDetails;
861 shaderInput.vuoPortDetails = compilerPublishedInput->
getDetails(
true);
863 if (json_object_object_get_ex(oldDetails,
"scaleToSamplerRect", &scaleToSamplerRectValue))
864 json_object_object_add(shaderInput.vuoPortDetails,
"scaleToSamplerRect", scaleToSamplerRectValue);
868 if (publishedInputAdded)
873 addedInput.vuoPortDetails = publishedInputAdded->
getDetails(
true);
874 shaderInputs.push_back(addedInput);
889 issues->
addIssue(VuoShaderFile::Fragment, compilerIssue.getLineNumber(), compilerIssue.getDetails(
false));
912 void VuoCodeWindow::updateModifiedIndicator()
914 bool modified =
stages->
modified() || publishedInputsModified || metadataModified;
915 setWindowModified(modified);
917 bool saveEnabled = modified || isNewUnsavedDocument();
918 saveAction->setEnabled(saveEnabled);
921 void VuoCodeWindow::updateColor()
923 bool isDark =
static_cast<VuoEditor *
>(qApp)->isInterfaceDark();
926 QString menuStyle = VUO_QSTRINGIFY(
930 background-color: #404040;
938 QMenu::item:disabled {
941 QMenu::item:selected {
942 background-color: #1060d0;
948 QMenu::indicator:checked {
949 image: url(:/Icons/checkmark.svg);
952 QMenu::indicator:checked,
957 QMenu::icon:unchecked {
962 setStyleSheet(VUO_QSTRINGIFY(
964 QMainWindow::separator {
969 background: transparent;
976 + (isDark ? menuStyle :
""));
982 void VuoCodeWindow::updateCanvasOpacity()
984 int opacity =
static_cast<VuoEditor *
>(qApp)->getCanvasOpacity();
988 void VuoCodeWindow::updateToolbar()
993 void VuoCodeWindow::resizeEvent(QResizeEvent *event)
997 toolbar->updateTitle();
1000 QMainWindow::resizeEvent(event);
1008 return raiseDocumentAction;
1016 return zoom11Action;
1024 return zoomInAction;
1032 return zoomOutAction;
1048 return recentFileMenu;
1068 void VuoCodeWindow::updateWindowMenu()
1070 windowMenu->clear();
1071 static_cast<VuoEditor *
>(qApp)->populateWindowMenu(windowMenu,
this);
1079 __block
bool isRunning = wrapperComposition->
isRunning();
1082 string sourcePath = windowFilePath().toStdString();
1083 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToAllOtherTopLevelCompositions(
nullptr, ^(
VuoEditorComposition *topLevelComposition)
1090 reloadAction->setEnabled(isRunning);
1096 void VuoCodeWindow::toggleInputPortSidebarVisibility()
1098 bool becomingVisible = ! inputPortSidebar->isVisible();
1099 inputPortSidebar->setVisible(becomingVisible);
1100 updateInputPortSidebarMenuItem();
1106 void VuoCodeWindow::updateInputPortSidebarMenuItem()
1108 toggleInputPortSidebarAction->setText(inputPortSidebar->isVisible() ? tr(
"Hide Published Ports") : tr(
"Show Published Ports"));
1114 void VuoCodeWindow::toggleDocumentationSidebarVisibility()
1116 bool becomingVisible = ! documentationSidebar->isVisible();
1117 documentationSidebar->setVisible(becomingVisible);
1118 updateDocumentationSidebarMenuItem();
1124 void VuoCodeWindow::updateDocumentationSidebarMenuItem()
1126 toggleDocumentationSidebarAction->setText(documentationSidebar->isVisible() ? tr(
"Hide GLSL/ISF Quick Reference") : tr(
"Show GLSL/ISF Quick Reference"));
1137 map<VuoRendererPort *, pair<string, string> > originalAndFinalValueForPort = inputEditorSession->
execute(port,
true);
1139 delete inputEditorSession;
1140 inputEditorSession =
nullptr;
1142 bool valueChanged =
false;
1143 for (
auto i : originalAndFinalValueForPort)
1146 string originalEditingSessionValue = i.second.first;
1147 string finalEditingSessionValue = i.second.second;
1149 if (finalEditingSessionValue != originalEditingSessionValue)
1151 valueChanged =
true;
1157 finalEditingSessionValue.c_str());
1168 bringStoredShaderInSyncWithPublishedInputPorts();
1169 publishedInputsModified =
true;
1170 updateModifiedIndicator();
1181 VUserLog(
"%s: Set published port '%s' details to %s",
1184 json_object_to_json_string(newDetails));
1188 bringStoredShaderInSyncWithPublishedInputPorts();
1189 publishedInputsModified =
true;
1190 updateModifiedIndicator();
1204 if (originalName != newName)
1205 changePublishedPortName(port, newName);
1215 string oldSnapshot = wrapperComposition->
takeSnapshot();
1223 VUserLog(
"%s: Rename published port %s to %s",
1228 bringStoredShaderInSyncWithPublishedInputPorts(
nullptr, {oldName, newName});
1229 publishedInputsModified =
true;
1230 updateModifiedIndicator();
1232 string newSnapshot = wrapperComposition->
takeSnapshot();
1241 void VuoCodeWindow::addPublishedPort(
string typeName,
bool isInput)
1243 string oldSnapshot = wrapperComposition->
takeSnapshot();
1248 VUserLog(
"%s: Add published %s port %s %s",
1250 isInput ?
"input" :
"output",
1251 typeName.empty() ?
"event" : typeName.c_str(),
1258 bringStoredShaderInSyncWithPublishedInputPorts(publishedPort);
1259 publishedInputsModified =
true;
1260 updateModifiedIndicator();
1262 string newSnapshot = wrapperComposition->
takeSnapshot();
1268 if (portName != newName)
1269 changePublishedPortName(rpp, newName);
1279 VUserLog(
"%s: Remove published input port %s",
1283 string oldSnapshot = wrapperComposition->
takeSnapshot();
1286 wrapperComposition->
removeCable(cable->getRenderer());
1291 bringStoredShaderInSyncWithPublishedInputPorts(
nullptr, {}, portName);
1292 publishedInputsModified =
true;
1293 updateModifiedIndicator();
1295 string newSnapshot = wrapperComposition->
takeSnapshot();
1304 void VuoCodeWindow::changeMetadata(
int dialogResult)
1306 if (dialogResult == QDialog::Accepted)
1313 metadataModified =
true;
1314 updateModifiedIndicator();
1316 VUserLog(
"%s: Set metadata to:\n%s",
1325 void VuoCodeWindow::showBuildActivityIndicator()
1330 runAction->setEnabled(
false);
1331 stopAction->setEnabled(
true);
1332 restartAction->setEnabled(
true);
1338 void VuoCodeWindow::hideBuildActivityIndicator(QString buildError)
1343 runAction->setEnabled(
false);
1344 stopAction->setEnabled(
true);
1345 restartAction->setEnabled(
true);
1352 void VuoCodeWindow::showStopActivityIndicator()
1358 stopAction->setEnabled(
false);
1359 restartAction->setEnabled(
false);
1365 void VuoCodeWindow::hideStopActivityIndicator()
1370 runAction->setEnabled(
true);
1371 stopAction->setEnabled(
false);
1372 restartAction->setEnabled(
false);
1379 void VuoCodeWindow::zoom11()
1383 zoom11Action->setEnabled(
false);
1384 toolbar->
update(
false,
true,
false);
1390 void VuoCodeWindow::zoomIn()
1395 zoom11Action->setEnabled(! isActualSize);
1396 toolbar->
update(
false, isActualSize,
false);
1402 void VuoCodeWindow::zoomOut()
1407 zoom11Action->setEnabled(! isActualSize);
1408 toolbar->
update(
false, isActualSize,
false);
1414 void VuoCodeWindow::copy()
1418 else if (documentationSidebar->isAncestorOf(qApp->focusWidget()))
1419 QGuiApplication::clipboard()->setText(documentationSidebar->
getSelectedText());