2023-12-20 17:13:38 +00:00
|
|
|
#pragma once
|
|
|
|
#include <JuceHeader.h>
|
|
|
|
#include "../lua/LuaParser.h"
|
|
|
|
#include "../PluginProcessor.h"
|
|
|
|
|
2023-12-20 19:22:59 +00:00
|
|
|
class ErrorCodeEditorComponent : public juce::CodeEditorComponent, public ErrorListener, public juce::AsyncUpdater {
|
2023-12-20 17:13:38 +00:00
|
|
|
public:
|
2024-03-13 23:07:40 +00:00
|
|
|
ErrorCodeEditorComponent(juce::CodeDocument& document, juce::CodeTokeniser* codeTokeniser, juce::String id) : juce::CodeEditorComponent(document, codeTokeniser), id(id) {}
|
2023-12-20 17:13:38 +00:00
|
|
|
|
|
|
|
void paint(juce::Graphics& g) override {
|
|
|
|
juce::CodeEditorComponent::paint(g);
|
|
|
|
if (errorLine != -1) {
|
|
|
|
int firstVisibleLine = getFirstLineOnScreen();
|
|
|
|
int lastVisibleLine = firstVisibleLine + getNumLinesOnScreen();
|
|
|
|
if (errorLine < firstVisibleLine || errorLine > lastVisibleLine) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto lineBounds = getErrorLineBounds();
|
|
|
|
|
|
|
|
// draw error line
|
|
|
|
g.setColour(juce::Colours::red);
|
|
|
|
// draw red squiggly line
|
|
|
|
juce::Path path;
|
|
|
|
|
|
|
|
double xOffset = 0;
|
|
|
|
double yOffset = 2;
|
|
|
|
|
|
|
|
double lineIncrement = 2.5;
|
|
|
|
double squiggleHeight = 3;
|
|
|
|
|
2024-03-13 23:07:40 +00:00
|
|
|
juce::String line = getDocument().getLine(errorLine - 1);
|
2023-12-20 17:13:38 +00:00
|
|
|
// get number of leading whitespace characters
|
|
|
|
int leadingWhitespace = line.length() - line.trimStart().length();
|
|
|
|
double start = getCharWidth() * leadingWhitespace;
|
|
|
|
double width = getCharWidth() * line.length() - getCharWidth();
|
|
|
|
|
2023-12-20 17:47:58 +00:00
|
|
|
double squiggleMax = lineBounds.getY() + lineBounds.getHeight() - squiggleHeight + yOffset;
|
|
|
|
double squiggleMin = lineBounds.getY() + lineBounds.getHeight() + yOffset;
|
|
|
|
|
|
|
|
path.startNewSubPath(lineBounds.getX() + start + xOffset, squiggleMax);
|
2023-12-20 17:13:38 +00:00
|
|
|
|
|
|
|
for (double i = start; i < width - xOffset; i += 2 * lineIncrement) {
|
2023-12-20 17:47:58 +00:00
|
|
|
path.lineTo(lineBounds.getX() + i + xOffset, squiggleMax);
|
|
|
|
path.lineTo(lineBounds.getX() + i + lineIncrement + xOffset, squiggleMin);
|
2023-12-20 17:13:38 +00:00
|
|
|
}
|
|
|
|
g.strokePath(path, juce::PathStrokeType(1.0f));
|
|
|
|
|
|
|
|
|
|
|
|
if (errorLineHovered) {
|
|
|
|
// draw error text on line below the one being hovered
|
|
|
|
auto bounds = juce::Rectangle<int>(lineBounds.getX(), lineBounds.getY() + lineBounds.getHeight(), lineBounds.getWidth(), lineBounds.getHeight());
|
|
|
|
|
|
|
|
// draw over existing line
|
|
|
|
g.setColour(juce::Colours::red);
|
|
|
|
g.fillRect(bounds);
|
|
|
|
|
|
|
|
// draw text
|
|
|
|
g.setColour(juce::Colours::white);
|
|
|
|
g.setFont(12);
|
|
|
|
g.drawText(errorText, bounds, juce::Justification::centred);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void mouseMove(const juce::MouseEvent& event) override {
|
|
|
|
juce::CodeEditorComponent::mouseMove(event);
|
|
|
|
if (errorLine != -1) {
|
|
|
|
errorLineHovered = getErrorLineBounds().contains(event.getPosition());
|
2023-12-20 19:22:59 +00:00
|
|
|
repaint();
|
2023-12-20 17:13:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
juce::Rectangle<int> getErrorLineBounds() {
|
|
|
|
int firstVisibleLine = getFirstLineOnScreen();
|
|
|
|
double gutterWidth = 35;
|
|
|
|
|
|
|
|
return juce::Rectangle<int>(gutterWidth, getLineHeight() * (errorLine - 1 - firstVisibleLine), getWidth() - gutterWidth, getLineHeight());
|
|
|
|
}
|
|
|
|
|
2023-12-20 19:22:59 +00:00
|
|
|
void handleAsyncUpdate() override {
|
2023-12-20 17:13:38 +00:00
|
|
|
repaint();
|
|
|
|
}
|
|
|
|
|
|
|
|
void onError(int lineNumber, juce::String error) override {
|
2023-12-20 19:22:59 +00:00
|
|
|
int oldErrorLine = errorLine;
|
2023-12-20 17:13:38 +00:00
|
|
|
errorLine = lineNumber;
|
|
|
|
errorText = error;
|
2023-12-20 19:22:59 +00:00
|
|
|
if (errorLine != -1 || errorLine != oldErrorLine) {
|
|
|
|
triggerAsyncUpdate();
|
|
|
|
}
|
2023-12-20 17:13:38 +00:00
|
|
|
}
|
|
|
|
|
2024-03-13 23:07:40 +00:00
|
|
|
private:
|
|
|
|
|
|
|
|
juce::String getId() override {
|
|
|
|
return id;
|
2023-12-20 17:47:58 +00:00
|
|
|
}
|
|
|
|
|
2023-12-20 17:13:38 +00:00
|
|
|
int errorLine = -1;
|
2024-03-13 23:07:40 +00:00
|
|
|
juce::String id;
|
2023-12-20 17:13:38 +00:00
|
|
|
juce::String errorText;
|
|
|
|
bool errorLineHovered = false;
|
2024-03-13 23:07:40 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
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);
|
2024-03-29 21:53:48 +00:00
|
|
|
setColour(groupComponentBackgroundColourId, Colours::veryDark);
|
2024-03-13 23:07:40 +00:00
|
|
|
|
|
|
|
addAndMakeVisible(editor);
|
|
|
|
|
|
|
|
audioProcessor.addErrorListener(&editor);
|
|
|
|
}
|
|
|
|
|
|
|
|
~OscirenderCodeEditorComponent() override {
|
|
|
|
audioProcessor.removeErrorListener(&editor);
|
|
|
|
}
|
|
|
|
|
|
|
|
void resized() override {
|
|
|
|
auto bounds = getLocalBounds();
|
|
|
|
bounds.removeFromTop(30);
|
2024-03-29 21:53:48 +00:00
|
|
|
bounds.removeFromBottom(5);
|
2024-03-13 23:07:40 +00:00
|
|
|
editor.setBounds(bounds);
|
|
|
|
}
|
|
|
|
|
|
|
|
ErrorCodeEditorComponent& getEditor() {
|
|
|
|
return editor;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
|
|
ErrorCodeEditorComponent editor;
|
2023-12-20 17:13:38 +00:00
|
|
|
OscirenderAudioProcessor& audioProcessor;
|
|
|
|
};
|