Refactor code editor visibility, add title to code editor, significantly improve performance when resizing interface

pull/229/head
James Ball 2024-03-13 23:07:40 +00:00 zatwierdzone przez James H Ball
rodzic 35cf8d7942
commit 4c5852f9f0
10 zmienionych plików z 93 dodań i 44 usunięć

Wyświetl plik

@ -44,6 +44,8 @@ MainComponent::MainComponent(OscirenderAudioProcessor& p, OscirenderAudioProcess
pluginEditor.fileUpdated(audioProcessor.getCurrentFileName()); pluginEditor.fileUpdated(audioProcessor.getCurrentFileName());
}; };
closeFileButton.setTooltip("Close the currently open file.");
addAndMakeVisible(inputEnabled); addAndMakeVisible(inputEnabled);
inputEnabled.onClick = [this] { inputEnabled.onClick = [this] {
audioProcessor.inputEnabled->setBoolValueNotifyingHost(!audioProcessor.inputEnabled->getBoolValue()); audioProcessor.inputEnabled->setBoolValueNotifyingHost(!audioProcessor.inputEnabled->getBoolValue());

Wyświetl plik

@ -146,16 +146,16 @@ void OscirenderAudioProcessorEditor::paint(juce::Graphics& g) {
} }
for (int i = 0; i < codeEditors.size(); i++) { 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()); ds.drawForRectangle(g, codeEditors[i]->getBounds());
} }
} }
if (lua.getBounds().getWidth() > 0 && lua.getBounds().getHeight() > 0) { if (lua.isVisible()) {
ds.drawForRectangle(g, lua.getBounds()); ds.drawForRectangle(g, lua.getBounds());
} }
if (console.getBounds().getWidth() > 0 && console.getBounds().getHeight() > 0) { if (console.isVisible()) {
ds.drawForRectangle(g, console.getBounds()); ds.drawForRectangle(g, console.getBounds());
} }
} }
@ -184,7 +184,12 @@ void OscirenderAudioProcessorEditor::resized() {
int originalIndex = audioProcessor.getCurrentFileIndex(); int originalIndex = audioProcessor.getCurrentFileIndex();
int index = editingCustomFunction ? 0 : audioProcessor.getCurrentFileIndex() + 1; 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()) { if (codeEditors[index]->isVisible()) {
editorVisible = true; editorVisible = true;
@ -218,26 +223,25 @@ void OscirenderAudioProcessorEditor::resized() {
console.setBounds(dummy3Bounds.removeFromBottom(console.getConsoleOpen() ? 200 : 30)); console.setBounds(dummy3Bounds.removeFromBottom(console.getConsoleOpen() ? 200 : 30));
dummy3Bounds.removeFromBottom(RESIZER_BAR_SIZE); dummy3Bounds.removeFromBottom(RESIZER_BAR_SIZE);
codeEditors[index]->setBounds(dummy3Bounds); codeEditors[index]->setBounds(dummy3Bounds);
luaFileOpen = true;
} else { } else {
codeEditors[index]->setBounds(dummy2Bounds); 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 { } 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)); 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) { if (editorVisible) {
@ -258,7 +262,7 @@ void OscirenderAudioProcessorEditor::addCodeEditor(int index) {
int originalIndex = index; int originalIndex = index;
index++; index++;
std::shared_ptr<juce::CodeDocument> codeDocument; std::shared_ptr<juce::CodeDocument> codeDocument;
std::shared_ptr<ErrorCodeEditorComponent> editor; std::shared_ptr<OscirenderCodeEditorComponent> editor;
if (index == 0) { if (index == 0) {
codeDocument = customFunctionCodeDocument; codeDocument = customFunctionCodeDocument;
@ -272,7 +276,7 @@ void OscirenderAudioProcessorEditor::addCodeEditor(int index) {
} else if (extension == ".svg") { } else if (extension == ".svg") {
tokeniser = &xmlTokeniser; tokeniser = &xmlTokeniser;
} }
editor = std::make_shared<ErrorCodeEditorComponent>(*codeDocument, tokeniser, audioProcessor, audioProcessor.getFileId(originalIndex)); editor = std::make_shared<OscirenderCodeEditorComponent>(*codeDocument, tokeniser, audioProcessor, audioProcessor.getFileId(originalIndex), audioProcessor.getFileName(originalIndex));
} }
codeDocuments.insert(codeDocuments.begin() + index, codeDocument); codeDocuments.insert(codeDocuments.begin() + index, codeDocument);
@ -282,7 +286,7 @@ void OscirenderAudioProcessorEditor::addCodeEditor(int index) {
editor->setAccessible(false); editor->setAccessible(false);
// listen for changes to the code editor // listen for changes to the code editor
codeDocument->addListener(this); codeDocument->addListener(this);
editor->setColourScheme(colourScheme); editor->getEditor().setColourScheme(colourScheme);
} }
void OscirenderAudioProcessorEditor::removeCodeEditor(int index) { void OscirenderAudioProcessorEditor::removeCodeEditor(int index) {
@ -315,9 +319,9 @@ void OscirenderAudioProcessorEditor::updateCodeEditor() {
// message thread, this is safe. // message thread, this is safe.
updatingDocumentsWithParserLock = true; updatingDocumentsWithParserLock = true;
if (index == 0) { if (index == 0) {
codeEditors[index]->loadContent(audioProcessor.customEffect->getCode()); codeEditors[index]->getEditor().loadContent(audioProcessor.customEffect->getCode());
} else { } else {
codeEditors[index]->loadContent(juce::MemoryInputStream(*audioProcessor.getFileBlock(originalIndex), false).readEntireStreamAsString()); codeEditors[index]->getEditor().loadContent(juce::MemoryInputStream(*audioProcessor.getFileBlock(originalIndex), false).readEntireStreamAsString());
} }
updatingDocumentsWithParserLock = false; updatingDocumentsWithParserLock = false;
} }
@ -374,8 +378,8 @@ void OscirenderAudioProcessorEditor::editCustomFunction(bool enable) {
editingCustomFunction = enable; editingCustomFunction = enable;
juce::SpinLock::ScopedLockType lock1(audioProcessor.parsersLock); juce::SpinLock::ScopedLockType lock1(audioProcessor.parsersLock);
juce::SpinLock::ScopedLockType lock2(audioProcessor.effectsLock); juce::SpinLock::ScopedLockType lock2(audioProcessor.effectsLock);
codeEditors[0]->setVisible(enable);
updateCodeEditor(); updateCodeEditor();
codeEditors[0]->setVisible(enable);
} }
// parsersLock AND effectsLock must be locked before calling this function // parsersLock AND effectsLock must be locked before calling this function

Wyświetl plik

@ -57,13 +57,13 @@ public:
LuaConsole console; LuaConsole console;
std::vector<std::shared_ptr<juce::CodeDocument>> codeDocuments; std::vector<std::shared_ptr<juce::CodeDocument>> codeDocuments;
std::vector<std::shared_ptr<ErrorCodeEditorComponent>> codeEditors; std::vector<std::shared_ptr<OscirenderCodeEditorComponent>> codeEditors;
juce::CodeEditorComponent::ColourScheme colourScheme; juce::CodeEditorComponent::ColourScheme colourScheme;
juce::LuaTokeniser luaTokeniser; juce::LuaTokeniser luaTokeniser;
juce::XmlTokeniser xmlTokeniser; juce::XmlTokeniser xmlTokeniser;
juce::ShapeButton collapseButton; juce::ShapeButton collapseButton;
std::shared_ptr<juce::CodeDocument> customFunctionCodeDocument = std::make_shared<juce::CodeDocument>(); std::shared_ptr<juce::CodeDocument> customFunctionCodeDocument = std::make_shared<juce::CodeDocument>();
std::shared_ptr<ErrorCodeEditorComponent> customFunctionCodeEditor = std::make_shared<ErrorCodeEditorComponent>(*customFunctionCodeDocument, &luaTokeniser, audioProcessor, CustomEffect::FILE_NAME); std::shared_ptr<OscirenderCodeEditorComponent> customFunctionCodeEditor = std::make_shared<OscirenderCodeEditorComponent>(*customFunctionCodeDocument, &luaTokeniser, audioProcessor, CustomEffect::UNIQUE_ID, CustomEffect::FILE_NAME);
std::unique_ptr<juce::FileChooser> chooser; std::unique_ptr<juce::FileChooser> chooser;
MainMenuBarModel menuBarModel{*this}; MainMenuBarModel menuBarModel{*this};

Wyświetl plik

@ -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); juce::SpinLock::ScopedLockType lock(errorListenersLock);
for (auto listener : errorListeners) { for (auto listener : errorListeners) {
if (listener->getFileName() == fileName) { if (listener->getId() == id) {
listener->onError(lineNumber, error); listener->onError(lineNumber, error);
} }
} }

Wyświetl plik

@ -234,7 +234,7 @@ public:
void setObjectServerRendering(bool enabled); void setObjectServerRendering(bool enabled);
void addErrorListener(ErrorListener* listener); void addErrorListener(ErrorListener* listener);
void removeErrorListener(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: private:
std::atomic<double> volume = 1.0; std::atomic<double> volume = 1.0;
std::atomic<double> threshold = 1.0; std::atomic<double> threshold = 1.0;

Wyświetl plik

@ -2,7 +2,8 @@
#include <numbers> #include <numbers>
#include "../MathUtil.h" #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<void(int, juce::String, juce::String)> errorCallback, double (&luaValues)[26]) : errorCallback(errorCallback), luaValues(luaValues) { CustomEffect::CustomEffect(std::function<void(int, juce::String, juce::String)> errorCallback, double (&luaValues)[26]) : errorCallback(errorCallback), luaValues(luaValues) {
vars.isEffect = true; vars.isEffect = true;

Wyświetl plik

@ -10,6 +10,7 @@ public:
~CustomEffect(); ~CustomEffect();
// arbitrary UUID // arbitrary UUID
static const juce::String UNIQUE_ID;
static const juce::String FILE_NAME; static const juce::String FILE_NAME;
Point apply(int index, Point input, const std::vector<double>& values, double sampleRate) override; Point apply(int index, Point input, const std::vector<double>& values, double sampleRate) override;

Wyświetl plik

@ -5,13 +5,7 @@
class ErrorCodeEditorComponent : public juce::CodeEditorComponent, public ErrorListener, public juce::AsyncUpdater { class ErrorCodeEditorComponent : public juce::CodeEditorComponent, public ErrorListener, public juce::AsyncUpdater {
public: public:
ErrorCodeEditorComponent(juce::CodeDocument& document, juce::CodeTokeniser* codeTokeniser, OscirenderAudioProcessor& p, juce::String fileName) : juce::CodeEditorComponent(document, codeTokeniser), audioProcessor(p), document(document), fileName(fileName) { ErrorCodeEditorComponent(juce::CodeDocument& document, juce::CodeTokeniser* codeTokeniser, juce::String id) : juce::CodeEditorComponent(document, codeTokeniser), id(id) {}
audioProcessor.addErrorListener(this);
}
~ErrorCodeEditorComponent() override {
audioProcessor.removeErrorListener(this);
}
void paint(juce::Graphics& g) override { void paint(juce::Graphics& g) override {
juce::CodeEditorComponent::paint(g); juce::CodeEditorComponent::paint(g);
@ -35,7 +29,7 @@ class ErrorCodeEditorComponent : public juce::CodeEditorComponent, public ErrorL
double lineIncrement = 2.5; double lineIncrement = 2.5;
double squiggleHeight = 3; double squiggleHeight = 3;
juce::String line = document.getLine(errorLine - 1); juce::String line = getDocument().getLine(errorLine - 1);
// get number of leading whitespace characters // get number of leading whitespace characters
int leadingWhitespace = line.length() - line.trimStart().length(); int leadingWhitespace = line.length() - line.trimStart().length();
double start = getCharWidth() * leadingWhitespace; double start = getCharWidth() * leadingWhitespace;
@ -88,7 +82,6 @@ class ErrorCodeEditorComponent : public juce::CodeEditorComponent, public ErrorL
repaint(); repaint();
} }
private:
void onError(int lineNumber, juce::String error) override { void onError(int lineNumber, juce::String error) override {
int oldErrorLine = errorLine; int oldErrorLine = errorLine;
errorLine = lineNumber; errorLine = lineNumber;
@ -98,14 +91,45 @@ private:
} }
} }
juce::String getFileName() override { private:
return fileName;
juce::String getId() override {
return id;
} }
juce::CodeDocument& document;
juce::String fileName;
int errorLine = -1; int errorLine = -1;
juce::String id;
juce::String errorText; juce::String errorText;
bool errorLineHovered = false; 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; OscirenderAudioProcessor& audioProcessor;
}; };

Wyświetl plik

@ -180,6 +180,16 @@ public:
return (row >= firstIndex && row < firstIndex + rows.size()) ? getComponentForRow (row) : nullptr; 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 int getRowNumberOfComponent (Component* const rowComponent) const noexcept
{ {
const int index = getViewedComponent()->getIndexOfChildComponent (rowComponent); const int index = getViewedComponent()->getIndexOfChildComponent (rowComponent);
@ -259,10 +269,16 @@ public:
if (auto* rowComp = getComponentForRow (row)) if (auto* rowComp = getComponentForRow (row))
{ {
rowComp->setBounds (0, owner.getPositionForRow (row), w, owner.getRowHeight (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 else
{ {
rows.clear(); rows.clear();
@ -347,6 +363,7 @@ public:
private: private:
ListBox& owner; ListBox& owner;
OwnedArray<RowComponent> rows; OwnedArray<RowComponent> rows;
int prevFirstIndex = -1, prevLastRow = -1;
int firstIndex = 0, firstWholeIndex = 0, lastWholeIndex = 0; int firstIndex = 0, firstWholeIndex = 0, lastWholeIndex = 0;
bool hasUpdated = false; bool hasUpdated = false;

Wyświetl plik

@ -7,7 +7,7 @@
class ErrorListener { class ErrorListener {
public: public:
virtual void onError(int lineNumber, juce::String error) = 0; virtual void onError(int lineNumber, juce::String error) = 0;
virtual juce::String getFileName() = 0; virtual juce::String getId() = 0;
}; };
const int NUM_SLIDERS = 26; const int NUM_SLIDERS = 26;