diff --git a/Source/MainComponent.cpp b/Source/MainComponent.cpp index 9d0305b..0a5345d 100644 --- a/Source/MainComponent.cpp +++ b/Source/MainComponent.cpp @@ -44,6 +44,8 @@ MainComponent::MainComponent(OscirenderAudioProcessor& p, OscirenderAudioProcess pluginEditor.fileUpdated(audioProcessor.getCurrentFileName()); }; + closeFileButton.setTooltip("Close the currently open file."); + addAndMakeVisible(inputEnabled); inputEnabled.onClick = [this] { audioProcessor.inputEnabled->setBoolValueNotifyingHost(!audioProcessor.inputEnabled->getBoolValue()); diff --git a/Source/PluginEditor.cpp b/Source/PluginEditor.cpp index 4aacdad..dde7a98 100644 --- a/Source/PluginEditor.cpp +++ b/Source/PluginEditor.cpp @@ -146,16 +146,16 @@ void OscirenderAudioProcessorEditor::paint(juce::Graphics& g) { } for (int i = 0; i < codeEditors.size(); i++) { - if (codeEditors[i]->getBounds().getWidth() > 0 && codeEditors[i]->getBounds().getHeight() > 0) { + if (codeEditors[i]->isVisible()) { ds.drawForRectangle(g, codeEditors[i]->getBounds()); } } - if (lua.getBounds().getWidth() > 0 && lua.getBounds().getHeight() > 0) { + if (lua.isVisible()) { ds.drawForRectangle(g, lua.getBounds()); } - if (console.getBounds().getWidth() > 0 && console.getBounds().getHeight() > 0) { + if (console.isVisible()) { ds.drawForRectangle(g, console.getBounds()); } } @@ -184,7 +184,12 @@ void OscirenderAudioProcessorEditor::resized() { int originalIndex = audioProcessor.getCurrentFileIndex(); int index = editingCustomFunction ? 0 : audioProcessor.getCurrentFileIndex() + 1; - if (originalIndex != -1 || editingCustomFunction) { + + bool ableToEditFile = originalIndex != -1 || editingCustomFunction; + bool fileOpen = false; + bool luaFileOpen = false; + + if (ableToEditFile) { if (codeEditors[index]->isVisible()) { editorVisible = true; @@ -218,26 +223,25 @@ void OscirenderAudioProcessorEditor::resized() { console.setBounds(dummy3Bounds.removeFromBottom(console.getConsoleOpen() ? 200 : 30)); dummy3Bounds.removeFromBottom(RESIZER_BAR_SIZE); codeEditors[index]->setBounds(dummy3Bounds); + luaFileOpen = true; } else { codeEditors[index]->setBounds(dummy2Bounds); - console.setBounds(0, 0, 0, 0); - luaResizerBar.setBounds(0, 0, 0, 0); - lua.setBounds(0, 0, 0, 0); } + + fileOpen = true; } else { - codeEditors[index]->setBounds(0, 0, 0, 0); - resizerBar.setBounds(0, 0, 0, 0); - luaResizerBar.setBounds(0, 0, 0, 0); - lua.setBounds(0, 0, 0, 0); - console.setBounds(0, 0, 0, 0); collapseButton.setBounds(area.removeFromRight(20)); } - } else { - collapseButton.setBounds(0, 0, 0, 0); - luaResizerBar.setBounds(0, 0, 0, 0); - lua.setBounds(0, 0, 0, 0); - console.setBounds(0, 0, 0, 0); } + + collapseButton.setVisible(ableToEditFile); + + codeEditors[index]->setVisible(fileOpen); + resizerBar.setVisible(fileOpen); + + console.setVisible(luaFileOpen); + luaResizerBar.setVisible(luaFileOpen); + lua.setVisible(luaFileOpen); } if (editorVisible) { @@ -258,7 +262,7 @@ void OscirenderAudioProcessorEditor::addCodeEditor(int index) { int originalIndex = index; index++; std::shared_ptr codeDocument; - std::shared_ptr editor; + std::shared_ptr editor; if (index == 0) { codeDocument = customFunctionCodeDocument; @@ -272,7 +276,7 @@ void OscirenderAudioProcessorEditor::addCodeEditor(int index) { } else if (extension == ".svg") { tokeniser = &xmlTokeniser; } - editor = std::make_shared(*codeDocument, tokeniser, audioProcessor, audioProcessor.getFileId(originalIndex)); + editor = std::make_shared(*codeDocument, tokeniser, audioProcessor, audioProcessor.getFileId(originalIndex), audioProcessor.getFileName(originalIndex)); } codeDocuments.insert(codeDocuments.begin() + index, codeDocument); @@ -282,7 +286,7 @@ void OscirenderAudioProcessorEditor::addCodeEditor(int index) { editor->setAccessible(false); // listen for changes to the code editor codeDocument->addListener(this); - editor->setColourScheme(colourScheme); + editor->getEditor().setColourScheme(colourScheme); } void OscirenderAudioProcessorEditor::removeCodeEditor(int index) { @@ -315,9 +319,9 @@ void OscirenderAudioProcessorEditor::updateCodeEditor() { // message thread, this is safe. updatingDocumentsWithParserLock = true; if (index == 0) { - codeEditors[index]->loadContent(audioProcessor.customEffect->getCode()); + codeEditors[index]->getEditor().loadContent(audioProcessor.customEffect->getCode()); } else { - codeEditors[index]->loadContent(juce::MemoryInputStream(*audioProcessor.getFileBlock(originalIndex), false).readEntireStreamAsString()); + codeEditors[index]->getEditor().loadContent(juce::MemoryInputStream(*audioProcessor.getFileBlock(originalIndex), false).readEntireStreamAsString()); } updatingDocumentsWithParserLock = false; } @@ -374,8 +378,8 @@ void OscirenderAudioProcessorEditor::editCustomFunction(bool enable) { editingCustomFunction = enable; juce::SpinLock::ScopedLockType lock1(audioProcessor.parsersLock); juce::SpinLock::ScopedLockType lock2(audioProcessor.effectsLock); - codeEditors[0]->setVisible(enable); updateCodeEditor(); + codeEditors[0]->setVisible(enable); } // parsersLock AND effectsLock must be locked before calling this function diff --git a/Source/PluginEditor.h b/Source/PluginEditor.h index 82c47c4..e4e1c57 100644 --- a/Source/PluginEditor.h +++ b/Source/PluginEditor.h @@ -57,13 +57,13 @@ public: LuaConsole console; std::vector> codeDocuments; - std::vector> codeEditors; + std::vector> codeEditors; juce::CodeEditorComponent::ColourScheme colourScheme; juce::LuaTokeniser luaTokeniser; juce::XmlTokeniser xmlTokeniser; juce::ShapeButton collapseButton; std::shared_ptr customFunctionCodeDocument = std::make_shared(); - std::shared_ptr customFunctionCodeEditor = std::make_shared(*customFunctionCodeDocument, &luaTokeniser, audioProcessor, CustomEffect::FILE_NAME); + std::shared_ptr customFunctionCodeEditor = std::make_shared(*customFunctionCodeDocument, &luaTokeniser, audioProcessor, CustomEffect::UNIQUE_ID, CustomEffect::FILE_NAME); std::unique_ptr chooser; MainMenuBarModel menuBarModel{*this}; diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp index 63e8958..280bebc 100644 --- a/Source/PluginProcessor.cpp +++ b/Source/PluginProcessor.cpp @@ -488,10 +488,10 @@ void OscirenderAudioProcessor::changeSound(ShapeSound::Ptr sound) { } } -void OscirenderAudioProcessor::notifyErrorListeners(int lineNumber, juce::String fileName, juce::String error) { +void OscirenderAudioProcessor::notifyErrorListeners(int lineNumber, juce::String id, juce::String error) { juce::SpinLock::ScopedLockType lock(errorListenersLock); for (auto listener : errorListeners) { - if (listener->getFileName() == fileName) { + if (listener->getId() == id) { listener->onError(lineNumber, error); } } diff --git a/Source/PluginProcessor.h b/Source/PluginProcessor.h index 6c65d1f..2419f33 100644 --- a/Source/PluginProcessor.h +++ b/Source/PluginProcessor.h @@ -234,7 +234,7 @@ public: void setObjectServerRendering(bool enabled); void addErrorListener(ErrorListener* listener); void removeErrorListener(ErrorListener* listener); - void notifyErrorListeners(int lineNumber, juce::String fileName, juce::String error); + void notifyErrorListeners(int lineNumber, juce::String id, juce::String error); private: std::atomic volume = 1.0; std::atomic threshold = 1.0; diff --git a/Source/audio/CustomEffect.cpp b/Source/audio/CustomEffect.cpp index 9184aa9..639b487 100644 --- a/Source/audio/CustomEffect.cpp +++ b/Source/audio/CustomEffect.cpp @@ -2,7 +2,8 @@ #include #include "../MathUtil.h" -const juce::String CustomEffect::FILE_NAME = "6a3580b0-c5fc-4b28-a33e-e26a487f052f"; +const juce::String CustomEffect::UNIQUE_ID = "6a3580b0-c5fc-4b28-a33e-e26a487f052f"; +const juce::String CustomEffect::FILE_NAME = "Custom Lua Effect"; CustomEffect::CustomEffect(std::function errorCallback, double (&luaValues)[26]) : errorCallback(errorCallback), luaValues(luaValues) { vars.isEffect = true; diff --git a/Source/audio/CustomEffect.h b/Source/audio/CustomEffect.h index df6ec82..f78aeb4 100644 --- a/Source/audio/CustomEffect.h +++ b/Source/audio/CustomEffect.h @@ -10,6 +10,7 @@ public: ~CustomEffect(); // arbitrary UUID + static const juce::String UNIQUE_ID; static const juce::String FILE_NAME; Point apply(int index, Point input, const std::vector& values, double sampleRate) override; diff --git a/Source/components/ErrorCodeEditorComponent.h b/Source/components/ErrorCodeEditorComponent.h index ddb042f..b9c0bae 100644 --- a/Source/components/ErrorCodeEditorComponent.h +++ b/Source/components/ErrorCodeEditorComponent.h @@ -5,13 +5,7 @@ class ErrorCodeEditorComponent : public juce::CodeEditorComponent, public ErrorListener, public juce::AsyncUpdater { public: - ErrorCodeEditorComponent(juce::CodeDocument& document, juce::CodeTokeniser* codeTokeniser, OscirenderAudioProcessor& p, juce::String fileName) : juce::CodeEditorComponent(document, codeTokeniser), audioProcessor(p), document(document), fileName(fileName) { - audioProcessor.addErrorListener(this); - } - - ~ErrorCodeEditorComponent() override { - audioProcessor.removeErrorListener(this); - } + ErrorCodeEditorComponent(juce::CodeDocument& document, juce::CodeTokeniser* codeTokeniser, juce::String id) : juce::CodeEditorComponent(document, codeTokeniser), id(id) {} void paint(juce::Graphics& g) override { juce::CodeEditorComponent::paint(g); @@ -35,7 +29,7 @@ class ErrorCodeEditorComponent : public juce::CodeEditorComponent, public ErrorL double lineIncrement = 2.5; double squiggleHeight = 3; - juce::String line = document.getLine(errorLine - 1); + juce::String line = getDocument().getLine(errorLine - 1); // get number of leading whitespace characters int leadingWhitespace = line.length() - line.trimStart().length(); double start = getCharWidth() * leadingWhitespace; @@ -88,7 +82,6 @@ class ErrorCodeEditorComponent : public juce::CodeEditorComponent, public ErrorL repaint(); } -private: void onError(int lineNumber, juce::String error) override { int oldErrorLine = errorLine; errorLine = lineNumber; @@ -98,14 +91,45 @@ private: } } - juce::String getFileName() override { - return fileName; +private: + + juce::String getId() override { + return id; } - juce::CodeDocument& document; - juce::String fileName; int errorLine = -1; + juce::String id; juce::String errorText; bool errorLineHovered = false; +}; + +class OscirenderCodeEditorComponent : public juce::GroupComponent { +public: + OscirenderCodeEditorComponent(juce::CodeDocument& document, juce::CodeTokeniser* codeTokeniser, OscirenderAudioProcessor& p, juce::String id, juce::String fileName) : editor(document, codeTokeniser, id), audioProcessor(p) { + setText(fileName); + setTextLabelPosition(juce::Justification::centred); + + addAndMakeVisible(editor); + + audioProcessor.addErrorListener(&editor); + } + + ~OscirenderCodeEditorComponent() override { + audioProcessor.removeErrorListener(&editor); + } + + void resized() override { + auto bounds = getLocalBounds(); + bounds.removeFromTop(30); + editor.setBounds(bounds); + } + + ErrorCodeEditorComponent& getEditor() { + return editor; + } + +private: + + ErrorCodeEditorComponent editor; OscirenderAudioProcessor& audioProcessor; }; diff --git a/Source/components/VListBox.cpp b/Source/components/VListBox.cpp index c5242f2..0206a44 100644 --- a/Source/components/VListBox.cpp +++ b/Source/components/VListBox.cpp @@ -180,6 +180,16 @@ public: return (row >= firstIndex && row < firstIndex + rows.size()) ? getComponentForRow (row) : nullptr; } + void updateAllRows() { + // TODO: Refactor this class so that this function is called to + // update all rows, including those offscreen, so that we never + // need to recalculate the row components. + // + // This makes VListBoxes that don't have many components much + // faster, but it would be slower for VListBoxes with many + // components. + } + int getRowNumberOfComponent (Component* const rowComponent) const noexcept { const int index = getViewedComponent()->getIndexOfChildComponent (rowComponent); @@ -259,9 +269,15 @@ public: if (auto* rowComp = getComponentForRow (row)) { rowComp->setBounds (0, owner.getPositionForRow (row), w, owner.getRowHeight (row)); - rowComp->update (row, owner.isRowSelected (row)); + if (firstIndex != prevFirstIndex || lastRow != prevLastRow) { + // This is the slowest part of the UI code. Any other tricks to call this less often would help a lot. + rowComp->update(row, owner.isRowSelected(row)); + } } } + + prevFirstIndex = firstIndex; + prevLastRow = lastRow; } else { @@ -347,6 +363,7 @@ public: private: ListBox& owner; OwnedArray rows; + int prevFirstIndex = -1, prevLastRow = -1; int firstIndex = 0, firstWholeIndex = 0, lastWholeIndex = 0; bool hasUpdated = false; diff --git a/Source/lua/LuaParser.h b/Source/lua/LuaParser.h index 4cc3e08..3232b0a 100644 --- a/Source/lua/LuaParser.h +++ b/Source/lua/LuaParser.h @@ -7,7 +7,7 @@ class ErrorListener { public: virtual void onError(int lineNumber, juce::String error) = 0; - virtual juce::String getFileName() = 0; + virtual juce::String getId() = 0; }; const int NUM_SLIDERS = 26;