From 6863b3761890cf0fffeb5064a6899fbf550dcbe2 Mon Sep 17 00:00:00 2001 From: blancoberg Date: Sun, 8 Dec 2024 18:14:38 +0100 Subject: [PATCH 01/14] code editor search Code editor search --- src/surge-xt/gui/overlays/LuaEditors.cpp | 383 ++++++++++++++++++++--- src/surge-xt/gui/overlays/LuaEditors.h | 57 +++- 2 files changed, 387 insertions(+), 53 deletions(-) diff --git a/src/surge-xt/gui/overlays/LuaEditors.cpp b/src/surge-xt/gui/overlays/LuaEditors.cpp index 8651cbafb5c..ec27cd2b84c 100644 --- a/src/surge-xt/gui/overlays/LuaEditors.cpp +++ b/src/surge-xt/gui/overlays/LuaEditors.cpp @@ -41,83 +41,349 @@ namespace Surge namespace Overlays { -struct SurgeCodeEditorComponent : public juce::CodeEditorComponent +/* + --------------------------------------- + Search box + --------------------------------------- +*/ + +CodeEditorSearch::CodeEditorSearch(juce::CodeEditorComponent &editor, Surge::GUI::Skin::ptr_t skin) + : juce::TextEditor(), juce::TextEditor::Listener(), Surge::GUI::SkinConsumingComponent() { - SurgeCodeEditorComponent(juce::CodeDocument &d, juce::CodeTokeniser *t) - : juce::CodeEditorComponent(d, t) + ed = &editor; + currentSkin = skin; + + juce::Rectangle boundsLabel = juce::Rectangle(95, 2, 50, 20); + + labelResult = std::make_unique(); + + labelResult->setBounds(boundsLabel); + labelResult->setFont(juce::FontOptions(10)); + labelResult->setJustificationType(juce::Justification::right); + labelResult->setColour(juce::Label::textColourId, skin->getColor(Colors::Dialog::Button::Text)); + + addAndMakeVisible(*labelResult); + + setBorder(juce::BorderSize(2, 5, 2, 5)); + setFont(juce::FontOptions(12)); + setColour(juce::TextEditor::ColourIds::textColourId, + skin->getColor(Colors::Dialog::Button::Text)); + setColour(juce::TextEditor::backgroundColourId, + skin->getColor(Colors::Dialog::Button::Background)); + + setTitle(""); + setText(""); + + setEscapeAndReturnKeysConsumed(true); + + addListener(this); + + juce::Rectangle bounds = juce::Rectangle(0, 0, 150, 20); + setBounds(bounds); + + setVisible(false); +} + +void CodeEditorSearch::paint(juce::Graphics &g) +{ + juce::TextEditor::paint(g); + + // magnifying glass + + if (associatedBitmapStore != nullptr) { + // auto magnifyingGlass = associatedBitmapStore->getImage(IDB_SEARCH_BUTTON); + // magnifyingGlass->drawAt(g, 0, 0, 1); + // g.drawImage(magnifyingGlass->asJuceImage(), 0, 0, 20, 20, 0, 0, 20, 20, false); } - void handleEscapeKey() override + // todo draw all matches +} + +bool CodeEditorSearch::isActive() { return active; } + +void CodeEditorSearch::mouseDown(const juce::MouseEvent &event) { saveCaretStartPosition(true); } + +void CodeEditorSearch::saveCaretStartPosition(bool onlyReadCaretPosition) +{ + + if (!saveCaretStartPositionLock && + onlyReadCaretPosition == false) // prevent caretMoved feedback loop { - juce::Component *c = this; - while (c) + saveCaretStartPositionLock = true; + auto sel = ed->getHighlightedRegion(); + + if (sel.getEnd() - sel.getStart() != 0) { - if (auto fm = dynamic_cast(c)) + + // move caret to beginning of selected + if (ed->getCaretPosition() > sel.getStart()) { - fm->escapeKeyPressed(); - return; + juce::CodeDocument::Position pos = + juce::CodeDocument::Position(ed->getDocument(), sel.getStart()); + ed->moveCaretTo(pos, false); } - c = c->getParentComponent(); } + startCaretPosition = ed->getCaretPos(); + + saveCaretStartPositionLock = false; } - // Handles auto indentation + if (onlyReadCaretPosition && !saveCaretStartPositionLock) + { + startCaretPosition = ed->getCaretPos(); + } +} + +void CodeEditorSearch::focusLost(FocusChangeType) +{ + removeHighlightColors(); + ed->repaint(); +} + +void CodeEditorSearch::setHighlightColors() +{ + auto color = skin->getColor(Colors::FormulaEditor::Background); + + ed->setColour(juce::CodeEditorComponent::highlightColourId, + color.interpolatedWith(juce::Colour(108, 147, 25), 0.5)); +} - void handleReturnKey() override +void CodeEditorSearch::removeHighlightColors() +{ + ed->setColour(juce::CodeEditorComponent::highlightColourId, + skin->getColor(Colors::FormulaEditor::Highlight)); +} + +void CodeEditorSearch::show() +{ + // set selected text as search query unless it includes a newline character + auto sel = ed->getHighlightedRegion(); + juce::String txt = ed->getTextInRange(sel); + + saveCaretStartPosition(false); + + if (!txt.containsChar('\n') && sel.getLength() != 0) { + setText(txt); + } + + moveCaretToStartOfLine(false); + moveCaretToEndOfLine(true); + + setVisible(true); + search(); + grabKeyboardFocus(); + ed->repaint(); // force update selection color +} + +void CodeEditorSearch::textEditorEscapeKeyPressed(TextEditor &) { hide(); } +void CodeEditorSearch::textEditorReturnKeyPressed(TextEditor &) {} - auto pos = this->getCaretPos(); - auto txt = pos.getLineText(); - int tabs = 0; +bool CodeEditorSearch::keyPressed(const juce::KeyPress &key) +{ + juce::TextEditor::keyPressed(key); - for (int i = 0; i < txt.length(); i++) + if (key.getKeyCode() == key.returnKey) + { + if (key.getModifiers().isShiftDown()) { - if (txt.substring(i, i + 1) == " ") + showResult(-1, true); + } + else + { + showResult(1, true); + } + } + + if (key.getKeyCode() == key.escapeKey) + { + hide(); + return true; + } + + if (key.getModifiers().isCommandDown() && key.getKeyCode() == 70) + { + return true; + } + + return true; +} + +void CodeEditorSearch::hide() +{ + removeHighlightColors(); + setVisible(false); +} + +void CodeEditorSearch::textEditorTextChanged(juce::TextEditor &textEditor) { search(); } + +void CodeEditorSearch::showResult(int increment, bool moveCaret) +{ + int id = resultCurrent + 1; + if (resultTotal == 0) + { + removeHighlightColors(); + id = 0; + setColour(juce::TextEditor::focusedOutlineColourId, juce::Colour(204, 70, 70)); + setColour(juce::TextEditor::outlineColourId, + skin->getColor(Colors::FormulaEditor::Debugger::Text)); + } + else + { + setHighlightColors(); + setColour(juce::TextEditor::focusedOutlineColourId, + skin->getColor(Colors::FormulaEditor::Debugger::Text)); + setColour(juce::TextEditor::outlineColourId, + skin->getColor(Colors::FormulaEditor::Debugger::Text)); + } + + labelResult->setText(juce::String(std::to_string(id) + '/' + std::to_string(resultTotal)), + juce::NotificationType::dontSendNotification); + + repaint(); + + if (resultTotal == 0) + return; + + resultCurrent = (resultCurrent + increment + resultTotal) % resultTotal; + + saveCaretStartPositionLock = true; + ed->setHighlightedRegion( + juce::Range(result[resultCurrent], result[resultCurrent] + getTotalNumChars())); + saveCaretStartPositionLock = false; +} + +void CodeEditorSearch::resize() +{ + juce::Rectangle bounds = juce::Rectangle(ed->getBounds().getWidth() - 150 - 10, 6, 150, 24); + setBounds(bounds); +} + +void CodeEditorSearch::search() +{ + // move to start pos + saveCaretStartPositionLock = true; + ed->moveCaretTo(startCaretPosition, false); + saveCaretStartPositionLock = false; + + auto caret = ed->getCaretPos(); + + int caretPos = caret.getPosition(); + + juce::String txt = ed->getDocument().getAllContent(); + int pos = 0; + int count = 0; + int res = txt.indexOfIgnoreCase(pos, getText()); + resultCurrent = 0; + bool firstFound = false; + while (res != -1 && count < 128) + { + + result[count] = res; + if (caretPos <= res && !firstFound) + { + resultCurrent = count; + firstFound = true; + } + + pos = res + 1; + res = txt.indexOfIgnoreCase(pos, getText()); + + count++; + } + + resultTotal = count; + showResult(0, true); +} + +/* + --------------------------------------- + end search + --------------------------------------- +*/ + +SurgeCodeEditorComponent::SurgeCodeEditorComponent(juce::CodeDocument &d, juce::CodeTokeniser *t) + : juce::CodeEditorComponent(d, t) +{ +} + +void SurgeCodeEditorComponent::setSearch(CodeEditorSearch &s) { search = &s; } + +void SurgeCodeEditorComponent::handleEscapeKey() +{ + if (search->isVisible()) + { + search->hide(); + return; + } + juce::Component *c = this; + while (c) + { + if (auto fm = dynamic_cast(c)) + { + fm->escapeKeyPressed(); + return; + } + c = c->getParentComponent(); + } +} + +void SurgeCodeEditorComponent::caretPositionMoved() { search->saveCaretStartPosition(true); } + +// Handles auto indentation + +void SurgeCodeEditorComponent::handleReturnKey() +{ + + auto pos = this->getCaretPos(); + auto txt = pos.getLineText(); + int tabs = 0; + + for (int i = 0; i < txt.length(); i++) + { + if (txt.substring(i, i + 1) == " ") + { + tabs++; + } + else if (txt.substring(i, i + 1) == "\t") + { + tabs += this->getTabSize(); + } + else + { + bool indent = false; + auto trimmedTxt = txt.trim(); + + if (txt.substring(i, i + 8) == "function") { - tabs++; + + indent = true; } - else if (txt.substring(i, i + 1) == "\t") + else if (txt.substring(i, i + 2) == "if" && + trimmedTxt.substring(trimmedTxt.length() - 4, trimmedTxt.length()) == "then") { - tabs += this->getTabSize(); + indent = true; } - else + else if (trimmedTxt.substring(0, 4) == "else") { - bool indent = false; - auto trimmedTxt = txt.trim(); - - if (txt.substring(i, i + 8) == "function") - { - - indent = true; - } - else if (txt.substring(i, i + 2) == "if" && - trimmedTxt.substring(trimmedTxt.length() - 4, trimmedTxt.length()) == - "then") - { - indent = true; - } - else if (trimmedTxt.substring(0, 4) == "else") - { - indent = true; - } - else if (trimmedTxt.substring(trimmedTxt.length() - 2, trimmedTxt.length()) == - "do" || - trimmedTxt.substring(0, 5) == "while") - { - indent = true; - } + indent = true; + } + else if (trimmedTxt.substring(trimmedTxt.length() - 2, trimmedTxt.length()) == "do" || + trimmedTxt.substring(0, 5) == "while") + { + indent = true; + } - tabs += indent == true ? this->getTabSize() : 0; + tabs += indent == true ? this->getTabSize() : 0; - break; - } + break; } - - this->insertTextAtCaret("\n"); - this->insertTextAtCaret(std::string(tabs, ' ')); } -}; + + this->insertTextAtCaret("\n"); + this->insertTextAtCaret(std::string(tabs, ' ')); +} struct EditorColors { @@ -171,12 +437,16 @@ CodeEditorContainerWithApply::CodeEditorContainerWithApply(SurgeGUIEditor *ed, S mainEditor = std::make_unique(*mainDocument, tokenizer.get()); mainEditor->setTabSize(4, true); mainEditor->addKeyListener(this); + EditorColors::setColorsFromSkin(mainEditor.get(), skin); + search = std::make_unique(*mainEditor, skin); + mainEditor->setSearch(*search); if (addComponents) { addAndMakeVisible(applyButton.get()); addAndMakeVisible(mainEditor.get()); + addChildComponent(search.get()); } applyButton->setEnabled(false); @@ -193,6 +463,7 @@ void CodeEditorContainerWithApply::buttonClicked(juce::Button *button) void CodeEditorContainerWithApply::onSkinChanged() { mainEditor->setFont(skin->getFont(Fonts::LuaEditor::Code)); + search->setSkin(skin); EditorColors::setColorsFromSkin(mainEditor.get(), skin); } @@ -285,6 +556,8 @@ bool CodeEditorContainerWithApply::keyPressed(const juce::KeyPress &key, juce::C // search else if (key.getModifiers().isCommandDown() && keyCode == 70) { + search->show(); + /* search for characters and use getCharacterBounds to get its screen position */ @@ -909,6 +1182,7 @@ FormulaModulatorEditor::FormulaModulatorEditor(SurgeGUIEditor *ed, SurgeStorage controlArea = std::make_unique(this, editor); addAndMakeVisible(*controlArea); addAndMakeVisible(*mainEditor); + addChildComponent(*search); addChildComponent(*preludeDisplay); debugPanel = std::make_unique(this); @@ -1005,6 +1279,8 @@ void FormulaModulatorEditor::resized() height - 4 - controlHeight); } controlArea->setBounds(0, height - controlHeight, width, controlHeight); + + search->resize(); } void FormulaModulatorEditor::showModulatorCode() @@ -2002,6 +2278,8 @@ WavetableScriptEditor::WavetableScriptEditor(SurgeGUIEditor *ed, SurgeStorage *s controlArea = std::make_unique(this, editor); addAndMakeVisible(*controlArea); addAndMakeVisible(*mainEditor); + addChildComponent(*search); + addChildComponent(*preludeDisplay); rendererComponent = std::make_unique(this, editor, skin); @@ -2103,6 +2381,7 @@ void WavetableScriptEditor::resized() rendererComponent->setBounds(2, height - rendererHeight - controlHeight - 2, width - 2, rendererHeight); + search->resize(); rerenderFromUIState(); } diff --git a/src/surge-xt/gui/overlays/LuaEditors.h b/src/surge-xt/gui/overlays/LuaEditors.h index 12f4bce6b3f..37d37c34ab1 100644 --- a/src/surge-xt/gui/overlays/LuaEditors.h +++ b/src/surge-xt/gui/overlays/LuaEditors.h @@ -44,6 +44,59 @@ namespace Surge namespace Overlays { +class CodeEditorSearch : public juce::TextEditor, + public juce::TextEditor::Listener, + public Surge::GUI::SkinConsumingComponent +{ + private: + virtual void setHighlightColors(); + virtual void removeHighlightColors(); + juce::CodeEditorComponent *ed; + bool active = false; + int result[128]; + int resultCurrent; + int resultTotal; + bool saveCaretStartPositionLock; + Surge::GUI::Skin::ptr_t currentSkin; + + std::unique_ptr labelResult; + + juce::CodeDocument::Position startCaretPosition; + + virtual void search(); + + public: + virtual bool isActive(); + virtual void show(); + virtual void hide(); + virtual void resize(); + virtual void textEditorTextChanged(juce::TextEditor &textEditor); + virtual void mouseDown(const juce::MouseEvent &event) override; + virtual void focusLost(FocusChangeType) override; + + CodeEditorSearch(juce::CodeEditorComponent &editor, Surge::GUI::Skin::ptr_t); + virtual void textEditorEscapeKeyPressed(TextEditor &) override; + virtual void textEditorReturnKeyPressed(TextEditor &) override; + virtual bool keyPressed(const juce::KeyPress &key) override; + virtual void saveCaretStartPosition(bool onlyReadCaretPosition); + virtual void showResult(int increase, bool moveCaret); + virtual void paint(juce::Graphics &g) override; +}; + +class SurgeCodeEditorComponent : public juce::CodeEditorComponent +{ + public: + virtual void handleEscapeKey() override; + virtual void handleReturnKey() override; + virtual void caretPositionMoved() override; + + virtual void setSearch(CodeEditorSearch &s); + SurgeCodeEditorComponent(juce::CodeDocument &d, juce::CodeTokeniser *t); + + private: + CodeEditorSearch *search; +}; + /* * This is a base class that provides you an apply button, an editor, a document * a tokenizer, etc... which you need to layout with yoru other components by @@ -60,9 +113,11 @@ class CodeEditorContainerWithApply : public OverlayComponent, CodeEditorContainerWithApply(SurgeGUIEditor *ed, SurgeStorage *s, Surge::GUI::Skin::ptr_t sk, bool addComponents = false); std::unique_ptr mainDocument; - std::unique_ptr mainEditor; + std::unique_ptr mainEditor; std::unique_ptr applyButton; std::unique_ptr tokenizer; + std::unique_ptr search; + void buttonClicked(juce::Button *button) override; void codeDocumentTextDeleted(int startIndex, int endIndex) override; void codeDocumentTextInserted(const juce::String &newText, int insertIndex) override; From a7f2501bf92584f7c154354ceabc9e7eef5dd9b8 Mon Sep 17 00:00:00 2001 From: blancoberg Date: Sun, 8 Dec 2024 19:25:55 +0100 Subject: [PATCH 02/14] add override Missed a override --- src/surge-xt/gui/overlays/LuaEditors.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/surge-xt/gui/overlays/LuaEditors.h b/src/surge-xt/gui/overlays/LuaEditors.h index 37d37c34ab1..f1fd6629332 100644 --- a/src/surge-xt/gui/overlays/LuaEditors.h +++ b/src/surge-xt/gui/overlays/LuaEditors.h @@ -70,7 +70,7 @@ class CodeEditorSearch : public juce::TextEditor, virtual void show(); virtual void hide(); virtual void resize(); - virtual void textEditorTextChanged(juce::TextEditor &textEditor); + virtual void textEditorTextChanged(juce::TextEditor &textEditor) override; virtual void mouseDown(const juce::MouseEvent &event) override; virtual void focusLost(FocusChangeType) override; From 4a68e3ece878af725a29e53ddc03e568ffb33270 Mon Sep 17 00:00:00 2001 From: blancoberg Date: Mon, 9 Dec 2024 16:13:21 +0100 Subject: [PATCH 03/14] lua editor tab selection tab with selected text will add indentation to all lines in that selection --- src/surge-xt/gui/overlays/LuaEditors.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/surge-xt/gui/overlays/LuaEditors.cpp b/src/surge-xt/gui/overlays/LuaEditors.cpp index ec27cd2b84c..0540585140e 100644 --- a/src/surge-xt/gui/overlays/LuaEditors.cpp +++ b/src/surge-xt/gui/overlays/LuaEditors.cpp @@ -492,7 +492,16 @@ bool CodeEditorContainerWithApply::keyPressed(const juce::KeyPress &key, juce::C } else { - mainEditor->insertTabAtCaret(); + auto sel = mainEditor->getHighlightedRegion(); + + if (sel.getLength() == 0) + { + mainEditor->insertTabAtCaret(); + } + else + { + mainEditor->indentSelection(); + } } return true; @@ -558,9 +567,6 @@ bool CodeEditorContainerWithApply::keyPressed(const juce::KeyPress &key, juce::C { search->show(); - /* - search for characters and use getCharacterBounds to get its screen position - */ return true; } From 67fa38f617136452fd259fc8a9020697df74276c Mon Sep 17 00:00:00 2001 From: blancoberg Date: Mon, 9 Dec 2024 22:13:04 +0100 Subject: [PATCH 04/14] editor prelude fix Fixes editor prelude crash + some prep work for adding buttons to the searchbar --- src/surge-xt/gui/overlays/LuaEditors.cpp | 131 ++++++++++++----------- src/surge-xt/gui/overlays/LuaEditors.h | 15 +-- 2 files changed, 76 insertions(+), 70 deletions(-) diff --git a/src/surge-xt/gui/overlays/LuaEditors.cpp b/src/surge-xt/gui/overlays/LuaEditors.cpp index 0540585140e..2b6365ad770 100644 --- a/src/surge-xt/gui/overlays/LuaEditors.cpp +++ b/src/surge-xt/gui/overlays/LuaEditors.cpp @@ -48,12 +48,15 @@ namespace Overlays */ CodeEditorSearch::CodeEditorSearch(juce::CodeEditorComponent &editor, Surge::GUI::Skin::ptr_t skin) - : juce::TextEditor(), juce::TextEditor::Listener(), Surge::GUI::SkinConsumingComponent() + : juce::Component(), juce::TextEditor::Listener(), juce::KeyListener(), + Surge::GUI::SkinConsumingComponent() { ed = &editor; currentSkin = skin; - juce::Rectangle boundsLabel = juce::Rectangle(95, 2, 50, 20); + juce::Rectangle boundsLabel = juce::Rectangle(95, 2, 80, 20); + + textfield = std::make_unique(); labelResult = std::make_unique(); @@ -64,41 +67,31 @@ CodeEditorSearch::CodeEditorSearch(juce::CodeEditorComponent &editor, Surge::GUI addAndMakeVisible(*labelResult); - setBorder(juce::BorderSize(2, 5, 2, 5)); - setFont(juce::FontOptions(12)); - setColour(juce::TextEditor::ColourIds::textColourId, - skin->getColor(Colors::Dialog::Button::Text)); - setColour(juce::TextEditor::backgroundColourId, - skin->getColor(Colors::Dialog::Button::Background)); + textfield->setBorder(juce::BorderSize(2, 5, 2, 5)); + textfield->setFont(juce::FontOptions(12)); + textfield->setColour(juce::TextEditor::ColourIds::textColourId, + skin->getColor(Colors::Dialog::Button::Text)); + textfield->setColour(juce::TextEditor::backgroundColourId, + skin->getColor(Colors::Dialog::Button::Background)); + + addAndMakeVisible(*textfield); - setTitle(""); - setText(""); + textfield->setTitle(""); + textfield->setText(""); - setEscapeAndReturnKeysConsumed(true); + textfield->setEscapeAndReturnKeysConsumed(true); - addListener(this); + textfield->addListener(this); + textfield->addKeyListener(this); juce::Rectangle bounds = juce::Rectangle(0, 0, 150, 20); setBounds(bounds); setVisible(false); + resize(); } -void CodeEditorSearch::paint(juce::Graphics &g) -{ - juce::TextEditor::paint(g); - - // magnifying glass - - if (associatedBitmapStore != nullptr) - { - // auto magnifyingGlass = associatedBitmapStore->getImage(IDB_SEARCH_BUTTON); - // magnifyingGlass->drawAt(g, 0, 0, 1); - // g.drawImage(magnifyingGlass->asJuceImage(), 0, 0, 20, 20, 0, 0, 20, 20, false); - } - - // todo draw all matches -} +void CodeEditorSearch::paint(juce::Graphics &g) {} bool CodeEditorSearch::isActive() { return active; } @@ -107,31 +100,34 @@ void CodeEditorSearch::mouseDown(const juce::MouseEvent &event) { saveCaretStart void CodeEditorSearch::saveCaretStartPosition(bool onlyReadCaretPosition) { - if (!saveCaretStartPositionLock && - onlyReadCaretPosition == false) // prevent caretMoved feedback loop + if (!ed->isReadOnly()) { - saveCaretStartPositionLock = true; - auto sel = ed->getHighlightedRegion(); - - if (sel.getEnd() - sel.getStart() != 0) + if (!saveCaretStartPositionLock && + onlyReadCaretPosition == false) // prevent caretMoved feedback loop { + saveCaretStartPositionLock = true; + auto sel = ed->getHighlightedRegion(); - // move caret to beginning of selected - if (ed->getCaretPosition() > sel.getStart()) + if (sel.getEnd() - sel.getStart() != 0) { - juce::CodeDocument::Position pos = - juce::CodeDocument::Position(ed->getDocument(), sel.getStart()); - ed->moveCaretTo(pos, false); + + // move caret to beginning of selected + if (ed->getCaretPosition() > sel.getStart()) + { + juce::CodeDocument::Position pos = + juce::CodeDocument::Position(ed->getDocument(), sel.getStart()); + ed->moveCaretTo(pos, false); + } } - } - startCaretPosition = ed->getCaretPos(); + startCaretPosition = ed->getCaretPos(); - saveCaretStartPositionLock = false; - } + saveCaretStartPositionLock = false; + } - if (onlyReadCaretPosition && !saveCaretStartPositionLock) - { - startCaretPosition = ed->getCaretPos(); + if (onlyReadCaretPosition && !saveCaretStartPositionLock) + { + startCaretPosition = ed->getCaretPos(); + } } } @@ -165,24 +161,24 @@ void CodeEditorSearch::show() if (!txt.containsChar('\n') && sel.getLength() != 0) { - setText(txt); + textfield->setText(txt); } - moveCaretToStartOfLine(false); - moveCaretToEndOfLine(true); + textfield->moveCaretToStartOfLine(false); + textfield->moveCaretToEndOfLine(true); setVisible(true); search(); - grabKeyboardFocus(); + textfield->grabKeyboardFocus(); ed->repaint(); // force update selection color } -void CodeEditorSearch::textEditorEscapeKeyPressed(TextEditor &) { hide(); } -void CodeEditorSearch::textEditorReturnKeyPressed(TextEditor &) {} +void CodeEditorSearch::textEditorEscapeKeyPressed(juce::TextEditor &) { hide(); } +void CodeEditorSearch::textEditorReturnKeyPressed(juce::TextEditor &) {} -bool CodeEditorSearch::keyPressed(const juce::KeyPress &key) +bool CodeEditorSearch::keyPressed(const juce::KeyPress &key, juce::Component *originatingComponent) { - juce::TextEditor::keyPressed(key); + originatingComponent->keyPressed(key); if (key.getKeyCode() == key.returnKey) { @@ -225,17 +221,17 @@ void CodeEditorSearch::showResult(int increment, bool moveCaret) { removeHighlightColors(); id = 0; - setColour(juce::TextEditor::focusedOutlineColourId, juce::Colour(204, 70, 70)); - setColour(juce::TextEditor::outlineColourId, - skin->getColor(Colors::FormulaEditor::Debugger::Text)); + textfield->setColour(juce::TextEditor::focusedOutlineColourId, juce::Colour(204, 70, 70)); + textfield->setColour(juce::TextEditor::outlineColourId, + skin->getColor(Colors::FormulaEditor::Debugger::Text)); } else { setHighlightColors(); - setColour(juce::TextEditor::focusedOutlineColourId, - skin->getColor(Colors::FormulaEditor::Debugger::Text)); - setColour(juce::TextEditor::outlineColourId, - skin->getColor(Colors::FormulaEditor::Debugger::Text)); + textfield->setColour(juce::TextEditor::focusedOutlineColourId, + skin->getColor(Colors::FormulaEditor::Debugger::Text)); + textfield->setColour(juce::TextEditor::outlineColourId, + skin->getColor(Colors::FormulaEditor::Debugger::Text)); } labelResult->setText(juce::String(std::to_string(id) + '/' + std::to_string(resultTotal)), @@ -250,7 +246,7 @@ void CodeEditorSearch::showResult(int increment, bool moveCaret) saveCaretStartPositionLock = true; ed->setHighlightedRegion( - juce::Range(result[resultCurrent], result[resultCurrent] + getTotalNumChars())); + juce::Range(result[resultCurrent], result[resultCurrent] + textfield->getTotalNumChars())); saveCaretStartPositionLock = false; } @@ -258,6 +254,7 @@ void CodeEditorSearch::resize() { juce::Rectangle bounds = juce::Rectangle(ed->getBounds().getWidth() - 150 - 10, 6, 150, 24); setBounds(bounds); + textfield->setBounds(0, 0, 150, 24); } void CodeEditorSearch::search() @@ -274,7 +271,7 @@ void CodeEditorSearch::search() juce::String txt = ed->getDocument().getAllContent(); int pos = 0; int count = 0; - int res = txt.indexOfIgnoreCase(pos, getText()); + int res = txt.indexOfIgnoreCase(pos, textfield->getText()); resultCurrent = 0; bool firstFound = false; while (res != -1 && count < 128) @@ -288,7 +285,7 @@ void CodeEditorSearch::search() } pos = res + 1; - res = txt.indexOfIgnoreCase(pos, getText()); + res = txt.indexOfIgnoreCase(pos, textfield->getText()); count++; } @@ -329,7 +326,12 @@ void SurgeCodeEditorComponent::handleEscapeKey() } } -void SurgeCodeEditorComponent::caretPositionMoved() { search->saveCaretStartPosition(true); } +void SurgeCodeEditorComponent::caretPositionMoved() +{ + + if (search != nullptr) + search->saveCaretStartPosition(true); +} // Handles auto indentation @@ -441,6 +443,7 @@ CodeEditorContainerWithApply::CodeEditorContainerWithApply(SurgeGUIEditor *ed, S EditorColors::setColorsFromSkin(mainEditor.get(), skin); search = std::make_unique(*mainEditor, skin); + mainEditor->setSearch(*search); if (addComponents) { diff --git a/src/surge-xt/gui/overlays/LuaEditors.h b/src/surge-xt/gui/overlays/LuaEditors.h index f1fd6629332..29dea34cb7e 100644 --- a/src/surge-xt/gui/overlays/LuaEditors.h +++ b/src/surge-xt/gui/overlays/LuaEditors.h @@ -44,8 +44,9 @@ namespace Surge namespace Overlays { -class CodeEditorSearch : public juce::TextEditor, +class CodeEditorSearch : public juce::Component, public juce::TextEditor::Listener, + public juce::KeyListener, public Surge::GUI::SkinConsumingComponent { private: @@ -59,6 +60,7 @@ class CodeEditorSearch : public juce::TextEditor, bool saveCaretStartPositionLock; Surge::GUI::Skin::ptr_t currentSkin; + std::unique_ptr textfield; std::unique_ptr labelResult; juce::CodeDocument::Position startCaretPosition; @@ -75,12 +77,13 @@ class CodeEditorSearch : public juce::TextEditor, virtual void focusLost(FocusChangeType) override; CodeEditorSearch(juce::CodeEditorComponent &editor, Surge::GUI::Skin::ptr_t); - virtual void textEditorEscapeKeyPressed(TextEditor &) override; - virtual void textEditorReturnKeyPressed(TextEditor &) override; - virtual bool keyPressed(const juce::KeyPress &key) override; + virtual void textEditorEscapeKeyPressed(juce::TextEditor &); + virtual void textEditorReturnKeyPressed(juce::TextEditor &); + virtual bool keyPressed(const juce::KeyPress &key, + juce::Component *originatingComponent) override; virtual void saveCaretStartPosition(bool onlyReadCaretPosition); virtual void showResult(int increase, bool moveCaret); - virtual void paint(juce::Graphics &g) override; + virtual void paint(juce::Graphics &g); }; class SurgeCodeEditorComponent : public juce::CodeEditorComponent @@ -94,7 +97,7 @@ class SurgeCodeEditorComponent : public juce::CodeEditorComponent SurgeCodeEditorComponent(juce::CodeDocument &d, juce::CodeTokeniser *t); private: - CodeEditorSearch *search; + CodeEditorSearch *search = nullptr; }; /* From bc3ae5c23b05962fac1efa5eebe9980be12af2c2 Mon Sep 17 00:00:00 2001 From: blancoberg Date: Mon, 9 Dec 2024 22:31:10 +0100 Subject: [PATCH 05/14] override missed a couple of overrides. again --- src/surge-xt/gui/overlays/LuaEditors.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/surge-xt/gui/overlays/LuaEditors.h b/src/surge-xt/gui/overlays/LuaEditors.h index 29dea34cb7e..bf25dff33fb 100644 --- a/src/surge-xt/gui/overlays/LuaEditors.h +++ b/src/surge-xt/gui/overlays/LuaEditors.h @@ -77,13 +77,13 @@ class CodeEditorSearch : public juce::Component, virtual void focusLost(FocusChangeType) override; CodeEditorSearch(juce::CodeEditorComponent &editor, Surge::GUI::Skin::ptr_t); - virtual void textEditorEscapeKeyPressed(juce::TextEditor &); - virtual void textEditorReturnKeyPressed(juce::TextEditor &); + virtual void textEditorEscapeKeyPressed(juce::TextEditor &) override; + virtual void textEditorReturnKeyPressed(juce::TextEditor &) override; virtual bool keyPressed(const juce::KeyPress &key, juce::Component *originatingComponent) override; virtual void saveCaretStartPosition(bool onlyReadCaretPosition); virtual void showResult(int increase, bool moveCaret); - virtual void paint(juce::Graphics &g); + virtual void paint(juce::Graphics &g) override; }; class SurgeCodeEditorComponent : public juce::CodeEditorComponent From 3b23d9572c8d9999c78c18eb74215d871f209679 Mon Sep 17 00:00:00 2001 From: blancoberg Date: Thu, 12 Dec 2024 15:48:22 +0100 Subject: [PATCH 06/14] editor addons * All search results are now highlighted * icon buttons for case sensitivity, whole word, next and previous result. * Moved code out of search into a base class that we can later use for creating similar text popups. --- src/surge-xt/gui/overlays/LuaEditors.cpp | 388 +++++++++++++++++++++-- src/surge-xt/gui/overlays/LuaEditors.h | 130 +++++++- 2 files changed, 467 insertions(+), 51 deletions(-) diff --git a/src/surge-xt/gui/overlays/LuaEditors.cpp b/src/surge-xt/gui/overlays/LuaEditors.cpp index 2b6365ad770..b2058ff1ec4 100644 --- a/src/surge-xt/gui/overlays/LuaEditors.cpp +++ b/src/surge-xt/gui/overlays/LuaEditors.cpp @@ -42,48 +42,178 @@ namespace Overlays { /* - --------------------------------------- - Search box - --------------------------------------- + TextfieldPopup + Basic class that can be used for creating other textfield popups like the search */ +TextfieldButton::TextfieldButton(juce::String &svg) : juce::Component() +{ -CodeEditorSearch::CodeEditorSearch(juce::CodeEditorComponent &editor, Surge::GUI::Skin::ptr_t skin) + xml = juce::XmlDocument::parse(svg); + // std::cout << "xml to string " << xml->toString() << "\n"; + + svgGraphics = juce::Drawable::createFromSVG(*xml); + + // std::cout << "svg created : " << (svgGraphics == nullptr); + setBounds(juce::Rectangle(0, 0, TextfieldPopup::STYLE_BUTTON_SIZE, + TextfieldPopup::STYLE_BUTTON_SIZE)); + + // svgGraphics->setBounds(juce::Rectangle(0, 0, 50, 50)); + + addAndMakeVisible(*svgGraphics); + const juce::Rectangle bounds = + juce::Rectangle(0.f, 0.f, (float)TextfieldPopup::STYLE_BUTTON_SIZE, + (float)TextfieldPopup::STYLE_BUTTON_SIZE); + + svgGraphics->setTransformToFit(bounds, juce::RectanglePlacement::yTop); + // setSelectable(); +} + +void TextfieldButton::paint(juce::Graphics &g) +{ + + Component::paint(g); + auto bounds = getBounds(); + + float alpha = isMouseOver && enabled ? 0.2 : 0; + + g.setFillType(juce::FillType(juce::Colour(255, 255, 255).withAlpha(alpha))); + + g.fillRoundedRectangle(0, 0, TextfieldPopup::STYLE_BUTTON_SIZE, + TextfieldPopup::STYLE_BUTTON_SIZE, 2); +} + +void TextfieldButton::setEnabled(bool v) +{ + enabled = v; + updateGraphicState(); + repaint(); +} + +void TextfieldButton::mouseEnter(const juce::MouseEvent &event) +{ + isMouseOver = true; + repaint(); +} + +void TextfieldButton::mouseExit(const juce::MouseEvent &event) +{ + isMouseOver = false; + repaint(); +} + +void TextfieldButton::mouseDown(const juce::MouseEvent &event) { isMouseDown = true; } + +void TextfieldButton::mouseUp(const juce::MouseEvent &event) +{ + + if (isSelectable) + { + select(isSelected() == false ? true : false); + } + + if (isMouseDown) + { + onClick(); + } + + isMouseDown = false; +} + +void TextfieldButton::updateGraphicState() +{ + svgGraphics->setAlpha(enabled ? (isSelectable && selected) || (!isSelectable) ? 1 : 0.5 : 0.15); +} + +void TextfieldButton::setSelectable() +{ + isSelectable = true; + updateGraphicState(); + repaint(); +} + +void TextfieldButton::select(bool v) +{ + selected = v; + updateGraphicState(); + repaint(); +} + +Textfield::Textfield() : juce::TextEditor() {} +void Textfield::paint(juce::Graphics &g) +{ + + juce::TextEditor::paint(g); + + if (isEmpty()) + { + // draw prefix text + g.setColour(colour.withAlpha(0.85f)); + + g.setFont(getFont()); + auto bounds = getBounds(); + int border = getBorder().getLeft(); + auto textBounds = + juce::Rectangle(getLeftIndent() + border, 0, bounds.getWidth(), bounds.getHeight()); + + g.drawText(header, textBounds, juce::Justification::verticallyCentred, true); + } +} + +void Textfield::setHeader(juce::String h) { header = h; } + +void Textfield::setColour(int colourID, juce::Colour newColour) +{ + Component::setColour(colourID, newColour); + colour = newColour; +} + +TextfieldPopup::TextfieldPopup(juce::CodeEditorComponent &editor, Surge::GUI::Skin::ptr_t skin) : juce::Component(), juce::TextEditor::Listener(), juce::KeyListener(), Surge::GUI::SkinConsumingComponent() { + ed = &editor; currentSkin = skin; juce::Rectangle boundsLabel = juce::Rectangle(95, 2, 80, 20); - textfield = std::make_unique(); + textfield = std::make_unique(); labelResult = std::make_unique(); labelResult->setBounds(boundsLabel); labelResult->setFont(juce::FontOptions(10)); - labelResult->setJustificationType(juce::Justification::right); - labelResult->setColour(juce::Label::textColourId, skin->getColor(Colors::Dialog::Button::Text)); + labelResult->setJustificationType(juce::Justification::left); + // labelResult->setColour(juce::Label::textColourId, + // skin->getColor(Colors::Dialog::Button::Text)); + labelResult->setColour(juce::Label::textColourId, juce::Colour(255, 255, 255)); addAndMakeVisible(*labelResult); - textfield->setBorder(juce::BorderSize(2, 5, 2, 5)); + textfield->setBorder(juce::BorderSize(0, 5, 0, 5)); textfield->setFont(juce::FontOptions(12)); textfield->setColour(juce::TextEditor::ColourIds::textColourId, skin->getColor(Colors::Dialog::Button::Text)); textfield->setColour(juce::TextEditor::backgroundColourId, - skin->getColor(Colors::Dialog::Button::Background)); + skin->getColor(Colors::FormulaEditor::Background)); + + textfield->setColour(juce::TextEditor::focusedOutlineColourId, + skin->getColor(Colors::FormulaEditor::Background).brighter(0.05)); + textfield->setColour(juce::TextEditor::outlineColourId, + skin->getColor(Colors::FormulaEditor::Background).brighter(0)); addAndMakeVisible(*textfield); - textfield->setTitle(""); + textfield->setHeader("Search..."); textfield->setText(""); - + // textfield->setAlpha(0.5); textfield->setEscapeAndReturnKeysConsumed(true); textfield->addListener(this); textfield->addKeyListener(this); + setPaintingIsUnclipped(true); + juce::Rectangle bounds = juce::Rectangle(0, 0, 150, 20); setBounds(bounds); @@ -91,7 +221,135 @@ CodeEditorSearch::CodeEditorSearch(juce::CodeEditorComponent &editor, Surge::GUI resize(); } -void CodeEditorSearch::paint(juce::Graphics &g) {} +void TextfieldPopup::paint(juce::Graphics &g) +{ + + auto bounds = getBounds(); + auto rect = juce::Rectangle(0, 0, bounds.getWidth(), bounds.getHeight()); + + auto c = skin->getColor(Colors::FormulaEditor::Background).darker(1.3); + auto col = juce::Colour(c.getRed(), c.getGreen(), c.getBlue()); + g.setFillType(juce::FillType(col)); + g.fillRect(rect); + juce::Component::paint(g); +} +void TextfieldPopup::onClick(std::unique_ptr &btn) +{ + std::cout << "click button\n"; +} + +void TextfieldPopup::createButton(juce::String svg) +{ + + button[buttonCount] = std::make_unique(svg); + auto btn = &button[buttonCount]; + + auto callback = [this, btn]() { onClick(*btn); }; + button[buttonCount]->onClick = callback; + addAndMakeVisible(*button[buttonCount]); + + buttonCount++; +} + +void TextfieldPopup::setTextWidth(int w) { textWidth = w; } + +void TextfieldPopup::setHeader(juce::String t) { textfield->setHeader(t); } + +void TextfieldPopup::resize() +{ + + int totalWidth = STYLE_MARGIN + textWidth + STYLE_MARGIN + + STYLE_MARGIN_BETWEEN_TEXT_AND_BUTTONS + STYLE_MARGIN + + (STYLE_BUTTON_SIZE + STYLE_BUTTON_MARGIN) * buttonCount + STYLE_MARGIN; + int totalHeight = STYLE_TEXT_HEIGHT + STYLE_MARGIN * 2; + + juce::Rectangle bounds = + juce::Rectangle(ed->getWidth() - totalWidth + 2, 2, totalWidth, totalHeight); + + setBounds(bounds); + + auto boundsLabel = labelResult->getBounds(); + labelResult->setBounds(STYLE_MARGIN + textWidth + STYLE_MARGIN, + totalHeight * 0.5 - boundsLabel.getHeight() * 0.5, + boundsLabel.getWidth(), boundsLabel.getHeight()); + + textfield->setBounds(STYLE_MARGIN, + (STYLE_TEXT_HEIGHT + STYLE_MARGIN * 2) / 2 - STYLE_TEXT_HEIGHT * 0.5, + textWidth, STYLE_TEXT_HEIGHT); + + int buttonY = totalHeight / 2 - STYLE_BUTTON_SIZE * 0.5; + + for (int i = 0; i < buttonCount; i++) + { + auto bounds = juce::Rectangle(STYLE_MARGIN + textWidth + STYLE_MARGIN + + STYLE_MARGIN_BETWEEN_TEXT_AND_BUTTONS + STYLE_MARGIN + + (STYLE_BUTTON_SIZE + STYLE_BUTTON_MARGIN) * i, + buttonY, STYLE_BUTTON_SIZE, STYLE_BUTTON_SIZE); + button[i]->setBounds(bounds); + } +} + +/* +--------------------------------------- +Search box +--------------------------------------- +*/ + +CodeEditorSearch::CodeEditorSearch(juce::CodeEditorComponent &editor, Surge::GUI::Skin::ptr_t skin) + : TextfieldPopup(editor, skin) +{ + + // Case sensitivity + createButton( + {R"()"}); + button[0]->setSelectable(); + + // whole word match + createButton( + {R"()"}); + button[1]->setSelectable(); + + // arrow up + createButton( + {R"()"}); + + // arrow down + createButton({R"( +)"}); + + button[1]->setEnabled(false); + setHeader("Search.."); + repaint(); +} + +void CodeEditorSearch::onClick(std::unique_ptr &btn) +{ + // case sensitive + if (btn == button[0]) + { + search(); + } + // whole word + if (btn == button[1]) + { + search(); + } + + if (btn == button[2]) + { + showResult(1, true); + } + + if (btn == button[3]) + { + showResult(-1, true); + } +} + +int *CodeEditorSearch::getResult() { return result; } +int CodeEditorSearch::getResultTotal() { return resultTotal; } + +// void CodeEditorSearch::paint(juce::Graphics &g) {} bool CodeEditorSearch::isActive() { return active; } @@ -141,8 +399,9 @@ void CodeEditorSearch::setHighlightColors() { auto color = skin->getColor(Colors::FormulaEditor::Background); - ed->setColour(juce::CodeEditorComponent::highlightColourId, - color.interpolatedWith(juce::Colour(108, 147, 25), 0.5)); + ed->setColour( + juce::CodeEditorComponent::highlightColourId, + color.interpolatedWith(juce::Colour(COLOR_MATCH[0], COLOR_MATCH[1], COLOR_MATCH[3]), 0.6)); } void CodeEditorSearch::removeHighlightColors() @@ -173,6 +432,13 @@ void CodeEditorSearch::show() ed->repaint(); // force update selection color } +void CodeEditorSearch::hide() +{ + removeHighlightColors(); + setVisible(false); + ed->repaint(); +} + void CodeEditorSearch::textEditorEscapeKeyPressed(juce::TextEditor &) { hide(); } void CodeEditorSearch::textEditorReturnKeyPressed(juce::TextEditor &) {} @@ -206,39 +472,30 @@ bool CodeEditorSearch::keyPressed(const juce::KeyPress &key, juce::Component *or return true; } -void CodeEditorSearch::hide() -{ - removeHighlightColors(); - setVisible(false); -} - void CodeEditorSearch::textEditorTextChanged(juce::TextEditor &textEditor) { search(); } void CodeEditorSearch::showResult(int increment, bool moveCaret) { int id = resultCurrent + 1; + + button[2]->setEnabled(resultTotal < 2 ? false : true); + button[3]->setEnabled(resultTotal < 2 ? false : true); + if (resultTotal == 0) { removeHighlightColors(); id = 0; - textfield->setColour(juce::TextEditor::focusedOutlineColourId, juce::Colour(204, 70, 70)); - textfield->setColour(juce::TextEditor::outlineColourId, - skin->getColor(Colors::FormulaEditor::Debugger::Text)); } else { setHighlightColors(); - textfield->setColour(juce::TextEditor::focusedOutlineColourId, - skin->getColor(Colors::FormulaEditor::Debugger::Text)); - textfield->setColour(juce::TextEditor::outlineColourId, - skin->getColor(Colors::FormulaEditor::Debugger::Text)); } labelResult->setText(juce::String(std::to_string(id) + '/' + std::to_string(resultTotal)), juce::NotificationType::dontSendNotification); repaint(); - + ed->repaint(); if (resultTotal == 0) return; @@ -248,14 +505,17 @@ void CodeEditorSearch::showResult(int increment, bool moveCaret) ed->setHighlightedRegion( juce::Range(result[resultCurrent], result[resultCurrent] + textfield->getTotalNumChars())); saveCaretStartPositionLock = false; + + // std::cout << "show result " << resultTotal << "\n"; } +/* void CodeEditorSearch::resize() { juce::Rectangle bounds = juce::Rectangle(ed->getBounds().getWidth() - 150 - 10, 6, 150, 24); setBounds(bounds); textfield->setBounds(0, 0, 150, 24); -} +}*/ void CodeEditorSearch::search() { @@ -271,12 +531,16 @@ void CodeEditorSearch::search() juce::String txt = ed->getDocument().getAllContent(); int pos = 0; int count = 0; - int res = txt.indexOfIgnoreCase(pos, textfield->getText()); + int res = !button[0]->isSelected() ? txt.indexOfIgnoreCase(pos, textfield->getText()) + : txt.indexOf(pos, textfield->getText()); resultCurrent = 0; bool firstFound = false; - while (res != -1 && count < 128) + while (res != -1 && count < 512) { - + // whole word + if (button[1]->isSelected()) + { + } result[count] = res; if (caretPos <= res && !firstFound) { @@ -285,7 +549,8 @@ void CodeEditorSearch::search() } pos = res + 1; - res = txt.indexOfIgnoreCase(pos, textfield->getText()); + res = !button[0]->isSelected() ? txt.indexOfIgnoreCase(pos, textfield->getText()) + : txt.indexOf(pos, textfield->getText()); count++; } @@ -294,6 +559,8 @@ void CodeEditorSearch::search() showResult(0, true); } +juce::String CodeEditorSearch::getSearchQuery() { return textfield->getText(); } + /* --------------------------------------- end search @@ -307,6 +574,59 @@ SurgeCodeEditorComponent::SurgeCodeEditorComponent(juce::CodeDocument &d, juce:: void SurgeCodeEditorComponent::setSearch(CodeEditorSearch &s) { search = &s; } +void SurgeCodeEditorComponent::paint(juce::Graphics &g) +{ + // draw background + + juce::Colour bgColor = findColour(juce::CodeEditorComponent::backgroundColourId).withAlpha(1.f); + + auto bounds = getBounds(); + bounds.translate(0, -2); + + g.setFillType(juce::FillType(bgColor)); + g.fillRect(bounds); + // Draw search matches + + if (search != nullptr && search->isVisible() && search->getResultTotal() > 0) + { + auto result = search->getResult(); + int resultTotal = search->getResultTotal(); + + int firstLine = getFirstLineOnScreen(); + int lastLine = firstLine + getNumLinesOnScreen(); + + auto highlightColor = bgColor.interpolatedWith( + juce::Colour(CodeEditorSearch::COLOR_MATCH[0], CodeEditorSearch::COLOR_MATCH[1], + CodeEditorSearch::COLOR_MATCH[2]), + 0.4); + + for (int i = 0; i < resultTotal; i++) + { + + auto pos = juce::CodeDocument::Position(getDocument(), result[i]); + auto line = pos.getLineNumber(); + + if (line >= firstLine && line <= lastLine) + { + + auto posEnd = juce::CodeDocument::Position( + getDocument(), result[i] + search->getSearchQuery().length()); + + auto bounds = getCharacterBounds(pos); + auto boundsEnd = getCharacterBounds(posEnd); + + g.setFillType(juce::FillType(highlightColor)); + int width = boundsEnd.getX() - bounds.getX(); + int height = bounds.getHeight(); + + g.fillRect(bounds.getX(), bounds.getY(), width, height); + } + } + } + + juce::CodeEditorComponent::paint(g); +} + void SurgeCodeEditorComponent::handleEscapeKey() { if (search->isVisible()) @@ -408,7 +728,7 @@ struct EditorColors comp->setColourScheme(cs); comp->setColour(juce::CodeEditorComponent::backgroundColourId, - skin->getColor(Colors::FormulaEditor::Background)); + skin->getColor(Colors::FormulaEditor::Background).withMultipliedAlpha(0)); comp->setColour(juce::CodeEditorComponent::highlightColourId, skin->getColor(Colors::FormulaEditor::Highlight)); comp->setColour(juce::CodeEditorComponent::defaultTextColourId, diff --git a/src/surge-xt/gui/overlays/LuaEditors.h b/src/surge-xt/gui/overlays/LuaEditors.h index bf25dff33fb..82debf29c7a 100644 --- a/src/surge-xt/gui/overlays/LuaEditors.h +++ b/src/surge-xt/gui/overlays/LuaEditors.h @@ -44,46 +44,141 @@ namespace Surge namespace Overlays { -class CodeEditorSearch : public juce::Component, - public juce::TextEditor::Listener, - public juce::KeyListener, - public Surge::GUI::SkinConsumingComponent +class TextfieldButton : public juce::Component +{ + + protected: + std::unique_ptr xml; + bool isSelectable = false; + bool selected = false; + bool isMouseDown = false; + bool isMouseOver = false; + bool enabled = true; + void (*callback)(TextfieldButton &f); + + public: + // void setCallback(std::function btn){callback = btn}; + bool isSelected() { return selected; }; + TextfieldButton(juce::String &svg); + void loadSVG(juce::String &svg); + void setSelectable(); + void select(bool v); + void setEnabled(bool v); + + void updateGraphicState(); + void paint(juce::Graphics &g) override; + void mouseDown(const juce::MouseEvent &event) override; + void mouseUp(const juce::MouseEvent &event) override; + void mouseEnter(const juce::MouseEvent &event) override; + void mouseExit(const juce::MouseEvent &event) override; + // void onClick(TextfieldButton &btn); + std::function onClick; + + private: + std::unique_ptr svgGraphics; +}; + +class Textfield : public juce::TextEditor +{ + + private: + juce::Colour colour; + juce::String header; + + protected: + juce::String title; + + public: + Textfield(); + void paint(juce::Graphics &g) override; + void setColour(int colourID, juce::Colour newColour); + void setHeader(juce::String h); +}; + +class TextfieldPopup : public juce::Component, + public juce::TextEditor::Listener, + public juce::KeyListener, + public Surge::GUI::SkinConsumingComponent +{ + public: + static inline int STYLE_MARGIN = 5; + static inline int STYLE_TEXT_HEIGHT = 20; + static inline int STYLE_BUTTON_MARGIN = 5; + static inline int STYLE_BUTTON_SIZE = 14; + static inline int STYLE_MARGIN_BETWEEN_TEXT_AND_BUTTONS = 40; + + protected: + juce::CodeEditorComponent *ed; + Surge::GUI::Skin::ptr_t currentSkin; + std::unique_ptr textfield; + std::unique_ptr labelResult; + std::unique_ptr button[8]; + // TextfieldButton button[8]; + juce::String header; + int buttonCount = 0; + + private: + int textWidth = 120; + + public: + void paint(juce::Graphics &g) override; + virtual void onClick(std::unique_ptr &btn); + TextfieldPopup(juce::CodeEditorComponent &editor, Surge::GUI::Skin::ptr_t); + void resize(); + void setHeader(juce::String); + void createButton(juce::String svg); + void setTextWidth(int w); +}; + +class CodeEditorSearch : public TextfieldPopup { private: virtual void setHighlightColors(); virtual void removeHighlightColors(); - juce::CodeEditorComponent *ed; + // juce::CodeEditorComponent *ed; bool active = false; - int result[128]; - int resultCurrent; - int resultTotal; + + int result[512]; + int resultCurrent = 0; + int resultTotal = 0; bool saveCaretStartPositionLock; - Surge::GUI::Skin::ptr_t currentSkin; - std::unique_ptr textfield; - std::unique_ptr labelResult; + // Surge::GUI::Skin::ptr_t currentSkin; + + // std::unique_ptr textfield; + // std::unique_ptr labelResult; juce::CodeDocument::Position startCaretPosition; virtual void search(); public: + static constexpr int COLOR_MATCH[3] = {255, 98, 165}; + static constexpr int COLOR_HIGHLIGHT[3] = {25, 187, 105}; + static constexpr int COLOR_ALERT[3] = {255, 0, 1}; + + virtual juce::String getSearchQuery(); virtual bool isActive(); virtual void show(); virtual void hide(); - virtual void resize(); + // virtual void resize(); + + virtual void onClick(std::unique_ptr &btn) override; virtual void textEditorTextChanged(juce::TextEditor &textEditor) override; virtual void mouseDown(const juce::MouseEvent &event) override; virtual void focusLost(FocusChangeType) override; - - CodeEditorSearch(juce::CodeEditorComponent &editor, Surge::GUI::Skin::ptr_t); - virtual void textEditorEscapeKeyPressed(juce::TextEditor &) override; - virtual void textEditorReturnKeyPressed(juce::TextEditor &) override; virtual bool keyPressed(const juce::KeyPress &key, juce::Component *originatingComponent) override; + virtual void textEditorEscapeKeyPressed(juce::TextEditor &) override; + virtual void textEditorReturnKeyPressed(juce::TextEditor &) override; + + CodeEditorSearch(juce::CodeEditorComponent &editor, Surge::GUI::Skin::ptr_t); + virtual void saveCaretStartPosition(bool onlyReadCaretPosition); virtual void showResult(int increase, bool moveCaret); - virtual void paint(juce::Graphics &g) override; + // virtual void paint(juce::Graphics &g) override; + virtual int *getResult(); + virtual int getResultTotal(); }; class SurgeCodeEditorComponent : public juce::CodeEditorComponent @@ -93,6 +188,7 @@ class SurgeCodeEditorComponent : public juce::CodeEditorComponent virtual void handleReturnKey() override; virtual void caretPositionMoved() override; + virtual void paint(juce::Graphics &) override; virtual void setSearch(CodeEditorSearch &s); SurgeCodeEditorComponent(juce::CodeDocument &d, juce::CodeTokeniser *t); From 349ac0f21f5f8678c961ba9ebde824cdb01a4002 Mon Sep 17 00:00:00 2001 From: blancoberg Date: Thu, 12 Dec 2024 16:03:34 +0100 Subject: [PATCH 07/14] fix fix static variable --- src/surge-xt/gui/overlays/LuaEditors.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/surge-xt/gui/overlays/LuaEditors.cpp b/src/surge-xt/gui/overlays/LuaEditors.cpp index b2058ff1ec4..1c40efc2659 100644 --- a/src/surge-xt/gui/overlays/LuaEditors.cpp +++ b/src/surge-xt/gui/overlays/LuaEditors.cpp @@ -399,9 +399,11 @@ void CodeEditorSearch::setHighlightColors() { auto color = skin->getColor(Colors::FormulaEditor::Background); - ed->setColour( - juce::CodeEditorComponent::highlightColourId, - color.interpolatedWith(juce::Colour(COLOR_MATCH[0], COLOR_MATCH[1], COLOR_MATCH[3]), 0.6)); + ed->setColour(juce::CodeEditorComponent::highlightColourId, + color.interpolatedWith(juce::Colour(CodeEditorSearch::COLOR_MATCH[0], + CodeEditorSearch::COLOR_MATCH[1], + CodeEditorSearch::COLOR_MATCH[3]), + 0.6)); } void CodeEditorSearch::removeHighlightColors() From c883987930cc16f63e4d03821c9f650ab8acee70 Mon Sep 17 00:00:00 2001 From: blancoberg Date: Thu, 12 Dec 2024 16:15:06 +0100 Subject: [PATCH 08/14] fix array array index out of bounds --- src/surge-xt/gui/overlays/LuaEditors.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/surge-xt/gui/overlays/LuaEditors.cpp b/src/surge-xt/gui/overlays/LuaEditors.cpp index 1c40efc2659..85f3b1354c1 100644 --- a/src/surge-xt/gui/overlays/LuaEditors.cpp +++ b/src/surge-xt/gui/overlays/LuaEditors.cpp @@ -402,7 +402,7 @@ void CodeEditorSearch::setHighlightColors() ed->setColour(juce::CodeEditorComponent::highlightColourId, color.interpolatedWith(juce::Colour(CodeEditorSearch::COLOR_MATCH[0], CodeEditorSearch::COLOR_MATCH[1], - CodeEditorSearch::COLOR_MATCH[3]), + CodeEditorSearch::COLOR_MATCH[2]), 0.6)); } From 3690a3b253eadf376823224a771c64058234c355 Mon Sep 17 00:00:00 2001 From: blancoberg Date: Thu, 12 Dec 2024 17:37:47 +0100 Subject: [PATCH 09/14] fix 3 * remove static variables, * changed array to color type * Use skin color for highlighting --- src/surge-xt/gui/overlays/LuaEditors.cpp | 43 ++++++++++++------------ src/surge-xt/gui/overlays/LuaEditors.h | 8 ++--- 2 files changed, 26 insertions(+), 25 deletions(-) diff --git a/src/surge-xt/gui/overlays/LuaEditors.cpp b/src/surge-xt/gui/overlays/LuaEditors.cpp index 85f3b1354c1..684d054971a 100644 --- a/src/surge-xt/gui/overlays/LuaEditors.cpp +++ b/src/surge-xt/gui/overlays/LuaEditors.cpp @@ -319,6 +319,9 @@ CodeEditorSearch::CodeEditorSearch(juce::CodeEditorComponent &editor, Surge::GUI button[1]->setEnabled(false); setHeader("Search.."); + + COLOR_MATCH = skin->getColor(Colors::FormulaEditor::Lua::Keyword); + repaint(); } @@ -400,10 +403,7 @@ void CodeEditorSearch::setHighlightColors() auto color = skin->getColor(Colors::FormulaEditor::Background); ed->setColour(juce::CodeEditorComponent::highlightColourId, - color.interpolatedWith(juce::Colour(CodeEditorSearch::COLOR_MATCH[0], - CodeEditorSearch::COLOR_MATCH[1], - CodeEditorSearch::COLOR_MATCH[2]), - 0.6)); + color.interpolatedWith(COLOR_MATCH, 0.6)); } void CodeEditorSearch::removeHighlightColors() @@ -511,14 +511,6 @@ void CodeEditorSearch::showResult(int increment, bool moveCaret) // std::cout << "show result " << resultTotal << "\n"; } -/* -void CodeEditorSearch::resize() -{ - juce::Rectangle bounds = juce::Rectangle(ed->getBounds().getWidth() - 150 - 10, 6, 150, 24); - setBounds(bounds); - textfield->setBounds(0, 0, 150, 24); -}*/ - void CodeEditorSearch::search() { // move to start pos @@ -569,9 +561,11 @@ juce::String CodeEditorSearch::getSearchQuery() { return textfield->getText(); } --------------------------------------- */ -SurgeCodeEditorComponent::SurgeCodeEditorComponent(juce::CodeDocument &d, juce::CodeTokeniser *t) +SurgeCodeEditorComponent::SurgeCodeEditorComponent(juce::CodeDocument &d, juce::CodeTokeniser *t, + Surge::GUI::Skin::ptr_t &skin) : juce::CodeEditorComponent(d, t) { + currentSkin = &skin; } void SurgeCodeEditorComponent::setSearch(CodeEditorSearch &s) { search = &s; } @@ -597,10 +591,7 @@ void SurgeCodeEditorComponent::paint(juce::Graphics &g) int firstLine = getFirstLineOnScreen(); int lastLine = firstLine + getNumLinesOnScreen(); - auto highlightColor = bgColor.interpolatedWith( - juce::Colour(CodeEditorSearch::COLOR_MATCH[0], CodeEditorSearch::COLOR_MATCH[1], - CodeEditorSearch::COLOR_MATCH[2]), - 0.4); + auto highlightColor = bgColor.interpolatedWith(search->COLOR_MATCH, 0.5); for (int i = 0; i < resultTotal; i++) { @@ -618,10 +609,18 @@ void SurgeCodeEditorComponent::paint(juce::Graphics &g) auto boundsEnd = getCharacterBounds(posEnd); g.setFillType(juce::FillType(highlightColor)); + // g.setFillType(juce::FillType()) + int width = boundsEnd.getX() - bounds.getX(); int height = bounds.getHeight(); - g.fillRect(bounds.getX(), bounds.getY(), width, height); + juce::Path path; + auto rect = juce::Rectangle(bounds.getX(), bounds.getY(), width, height); + path.addRectangle(rect); + juce::PathStrokeType strokeType(1.2f); + g.setColour(highlightColor); + g.strokePath(path, strokeType); + // g.drawRect(bounds.getX(), bounds.getY(), width, height); } } } @@ -758,7 +757,7 @@ CodeEditorContainerWithApply::CodeEditorContainerWithApply(SurgeGUIEditor *ed, S mainDocument->setNewLineCharacters("\n"); tokenizer = std::make_unique(); - mainEditor = std::make_unique(*mainDocument, tokenizer.get()); + mainEditor = std::make_unique(*mainDocument, tokenizer.get(), skin); mainEditor->setTabSize(4, true); mainEditor->addKeyListener(this); @@ -1502,7 +1501,8 @@ FormulaModulatorEditor::FormulaModulatorEditor(SurgeGUIEditor *ed, SurgeStorage preludeDocument = std::make_unique(); preludeDocument->insertText(0, Surge::LuaSupport::getFormulaPrelude()); - preludeDisplay = std::make_unique(*preludeDocument, tokenizer.get()); + preludeDisplay = + std::make_unique(*preludeDocument, tokenizer.get(), skin); preludeDisplay->setTabSize(4, true); preludeDisplay->setReadOnly(true); preludeDisplay->setScrollbarThickness(8); @@ -2598,7 +2598,8 @@ WavetableScriptEditor::WavetableScriptEditor(SurgeGUIEditor *ed, SurgeStorage *s preludeDocument = std::make_unique(); preludeDocument->insertText(0, Surge::LuaSupport::getWTSEPrelude()); - preludeDisplay = std::make_unique(*preludeDocument, tokenizer.get()); + preludeDisplay = + std::make_unique(*preludeDocument, tokenizer.get(), skin); preludeDisplay->setTabSize(4, true); preludeDisplay->setReadOnly(true); preludeDisplay->setScrollbarThickness(8); diff --git a/src/surge-xt/gui/overlays/LuaEditors.h b/src/surge-xt/gui/overlays/LuaEditors.h index 82debf29c7a..6481c665f5d 100644 --- a/src/surge-xt/gui/overlays/LuaEditors.h +++ b/src/surge-xt/gui/overlays/LuaEditors.h @@ -153,9 +153,7 @@ class CodeEditorSearch : public TextfieldPopup virtual void search(); public: - static constexpr int COLOR_MATCH[3] = {255, 98, 165}; - static constexpr int COLOR_HIGHLIGHT[3] = {25, 187, 105}; - static constexpr int COLOR_ALERT[3] = {255, 0, 1}; + juce::Colour COLOR_MATCH; virtual juce::String getSearchQuery(); virtual bool isActive(); @@ -190,9 +188,11 @@ class SurgeCodeEditorComponent : public juce::CodeEditorComponent virtual void paint(juce::Graphics &) override; virtual void setSearch(CodeEditorSearch &s); - SurgeCodeEditorComponent(juce::CodeDocument &d, juce::CodeTokeniser *t); + SurgeCodeEditorComponent(juce::CodeDocument &d, juce::CodeTokeniser *t, + Surge::GUI::Skin::ptr_t &skin); private: + Surge::GUI::Skin::ptr_t *currentSkin; CodeEditorSearch *search = nullptr; }; From 563e9ed838e9c47b33c4794dd7be4def20d8ef13 Mon Sep 17 00:00:00 2001 From: blancoberg Date: Fri, 13 Dec 2024 16:12:05 +0100 Subject: [PATCH 10/14] code editor features * Whole word search * Goto line module ( ctrl + g ) * Textfield prefix text ( text when textfield is empty) * Search results are updated on code editor change ( if searchfield is active) * Arrow buttons were reversed --- src/surge-xt/gui/overlays/LuaEditors.cpp | 247 ++++++++++++++++++++--- src/surge-xt/gui/overlays/LuaEditors.h | 40 +++- 2 files changed, 248 insertions(+), 39 deletions(-) diff --git a/src/surge-xt/gui/overlays/LuaEditors.cpp b/src/surge-xt/gui/overlays/LuaEditors.cpp index 684d054971a..40533a03c06 100644 --- a/src/surge-xt/gui/overlays/LuaEditors.cpp +++ b/src/surge-xt/gui/overlays/LuaEditors.cpp @@ -85,6 +85,7 @@ void TextfieldButton::paint(juce::Graphics &g) void TextfieldButton::setEnabled(bool v) { enabled = v; + updateGraphicState(); repaint(); } @@ -147,7 +148,7 @@ void Textfield::paint(juce::Graphics &g) if (isEmpty()) { // draw prefix text - g.setColour(colour.withAlpha(0.85f)); + g.setColour(colour.withAlpha(0.5f)); g.setFont(getFont()); auto bounds = getBounds(); @@ -161,6 +162,8 @@ void Textfield::paint(juce::Graphics &g) void Textfield::setHeader(juce::String h) { header = h; } +void Textfield::setHeaderColor(juce::Colour &c) { colour = c; } + void Textfield::setColour(int colourID, juce::Colour newColour) { Component::setColour(colourID, newColour); @@ -202,9 +205,11 @@ TextfieldPopup::TextfieldPopup(juce::CodeEditorComponent &editor, Surge::GUI::Sk textfield->setColour(juce::TextEditor::outlineColourId, skin->getColor(Colors::FormulaEditor::Background).brighter(0)); + textfield->setHeaderColor(skin->getColor(Colors::Dialog::Button::Text)); + addAndMakeVisible(*textfield); - textfield->setHeader("Search..."); + // textfield->setHeader("Search..."); textfield->setText(""); // textfield->setAlpha(0.5); textfield->setEscapeAndReturnKeysConsumed(true); @@ -227,12 +232,25 @@ void TextfieldPopup::paint(juce::Graphics &g) auto bounds = getBounds(); auto rect = juce::Rectangle(0, 0, bounds.getWidth(), bounds.getHeight()); - auto c = skin->getColor(Colors::FormulaEditor::Background).darker(1.3); + auto c = currentSkin->getColor(Colors::FormulaEditor::Background).darker(1.3); auto col = juce::Colour(c.getRed(), c.getGreen(), c.getBlue()); g.setFillType(juce::FillType(col)); g.fillRect(rect); + juce::Component::paint(g); } + +void TextfieldPopup::show() +{ + + setVisible(true); + textfield->grabKeyboardFocus(); +} + +void TextfieldPopup::hide() { setVisible(false); } + +void TextfieldPopup::textEditorEscapeKeyPressed(juce::TextEditor &) { hide(); } + void TextfieldPopup::onClick(std::unique_ptr &btn) { std::cout << "click button\n"; @@ -255,16 +273,26 @@ void TextfieldPopup::setTextWidth(int w) { textWidth = w; } void TextfieldPopup::setHeader(juce::String t) { textfield->setHeader(t); } +bool TextfieldPopup::keyPressed(const juce::KeyPress &key, juce::Component *originatingComponent) +{ + return originatingComponent->keyPressed(key); + // return juce::KeyListener::keyPressed(key, originatingComponent); +} + void TextfieldPopup::resize() { - int totalWidth = STYLE_MARGIN + textWidth + STYLE_MARGIN + - STYLE_MARGIN_BETWEEN_TEXT_AND_BUTTONS + STYLE_MARGIN + - (STYLE_BUTTON_SIZE + STYLE_BUTTON_MARGIN) * buttonCount + STYLE_MARGIN; + int marginBetweenTextAndButtons = buttonCount > 0 ? STYLE_MARGIN_BETWEEN_TEXT_AND_BUTTONS : 0; + + int totalWidth = STYLE_MARGIN + textWidth + marginBetweenTextAndButtons + STYLE_MARGIN + + (STYLE_BUTTON_SIZE + STYLE_BUTTON_MARGIN) * buttonCount; + + totalWidth += buttonCount > 0 ? STYLE_MARGIN * 2 : 0; + int totalHeight = STYLE_TEXT_HEIGHT + STYLE_MARGIN * 2; - juce::Rectangle bounds = - juce::Rectangle(ed->getWidth() - totalWidth + 2, 2, totalWidth, totalHeight); + juce::Rectangle bounds = juce::Rectangle( + ed->getWidth() - totalWidth - ed->getScrollbarThickness() + 2, 2, totalWidth, totalHeight); setBounds(bounds); @@ -281,14 +309,82 @@ void TextfieldPopup::resize() for (int i = 0; i < buttonCount; i++) { - auto bounds = juce::Rectangle(STYLE_MARGIN + textWidth + STYLE_MARGIN + - STYLE_MARGIN_BETWEEN_TEXT_AND_BUTTONS + STYLE_MARGIN + - (STYLE_BUTTON_SIZE + STYLE_BUTTON_MARGIN) * i, - buttonY, STYLE_BUTTON_SIZE, STYLE_BUTTON_SIZE); + auto bounds = + juce::Rectangle(STYLE_MARGIN + textWidth + STYLE_MARGIN + marginBetweenTextAndButtons + + STYLE_MARGIN + (STYLE_BUTTON_SIZE + STYLE_BUTTON_MARGIN) * i, + buttonY, STYLE_BUTTON_SIZE, STYLE_BUTTON_SIZE); button[i]->setBounds(bounds); } } +/* +--------------------------------------- +Goto line +--------------------------------------- +*/ + +GotoLine::GotoLine(juce::CodeEditorComponent &editor, Surge::GUI::Skin::ptr_t skin) + : TextfieldPopup(editor, skin) +{ + setHeader("Goto line"); + setTextWidth(80); +} + +bool GotoLine::keyPressed(const juce::KeyPress &key, juce::Component *originatingComponent) +{ + originatingComponent->keyPressed(key); + if (key.getKeyCode() == key.returnKey) + { + hide(); + ed->grabKeyboardFocus(); + } + else if (key.getKeyCode() == key.escapeKey) + { + hide(); + ed->moveCaretTo(startCaretPosition, false); + ed->scrollToLine(startScroll); + ed->grabKeyboardFocus(); + } + + else + { + + int line = std::max(0, textfield->getText().getIntValue() - 1); + line = std::min(ed->getDocument().getNumLines(), line); + + int numLines = ed->getNumLinesOnScreen(); + currentLine = line; + + ed->scrollToLine(std::max(0, line - int(numLines * 0.5))); + + auto textLineLength = ed->getDocument().getLine(line).length(); + auto pos = juce::CodeDocument::Position(ed->getDocument(), line, textLineLength); + ed->moveCaretTo(pos, false); + } + + ed->repaint(); + + return true; +} + +void GotoLine::show() +{ + currentLine = ed->getCaretPos().getLineNumber(); + textfield->setText(""); + TextfieldPopup::show(); + startCaretPosition = ed->getCaretPos(); + startScroll = ed->getFirstLineOnScreen(); +} + +void GotoLine::hide() +{ + TextfieldPopup::hide(); + ed->repaint(); +} + +void GotoLine::focusLost(FocusChangeType) { hide(); } +int GotoLine::getCurrentLine() { return currentLine; } + /* --------------------------------------- Search box @@ -317,7 +413,7 @@ CodeEditorSearch::CodeEditorSearch(juce::CodeEditorComponent &editor, Surge::GUI createButton({R"( )"}); - button[1]->setEnabled(false); + // button[1]->setEnabled(false); setHeader("Search.."); COLOR_MATCH = skin->getColor(Colors::FormulaEditor::Lua::Keyword); @@ -330,22 +426,22 @@ void CodeEditorSearch::onClick(std::unique_ptr &btn) // case sensitive if (btn == button[0]) { - search(); + search(true); } // whole word if (btn == button[1]) { - search(); + search(true); } if (btn == button[2]) { - showResult(1, true); + showResult(-1, true); } if (btn == button[3]) { - showResult(-1, true); + showResult(1, true); } } @@ -414,6 +510,7 @@ void CodeEditorSearch::removeHighlightColors() void CodeEditorSearch::show() { + TextfieldPopup::show(); // set selected text as search query unless it includes a newline character auto sel = ed->getHighlightedRegion(); juce::String txt = ed->getTextInRange(sel); @@ -428,16 +525,17 @@ void CodeEditorSearch::show() textfield->moveCaretToStartOfLine(false); textfield->moveCaretToEndOfLine(true); - setVisible(true); - search(); + // setVisible(true); + search(true); textfield->grabKeyboardFocus(); ed->repaint(); // force update selection color } void CodeEditorSearch::hide() { + TextfieldPopup::hide(); removeHighlightColors(); - setVisible(false); + // setVisible(false); ed->repaint(); } @@ -474,7 +572,7 @@ bool CodeEditorSearch::keyPressed(const juce::KeyPress &key, juce::Component *or return true; } -void CodeEditorSearch::textEditorTextChanged(juce::TextEditor &textEditor) { search(); } +void CodeEditorSearch::textEditorTextChanged(juce::TextEditor &textEditor) { search(true); } void CodeEditorSearch::showResult(int increment, bool moveCaret) { @@ -504,18 +602,23 @@ void CodeEditorSearch::showResult(int increment, bool moveCaret) resultCurrent = (resultCurrent + increment + resultTotal) % resultTotal; saveCaretStartPositionLock = true; - ed->setHighlightedRegion( - juce::Range(result[resultCurrent], result[resultCurrent] + textfield->getTotalNumChars())); + if (moveCaret) + { + ed->setHighlightedRegion(juce::Range( + result[resultCurrent], result[resultCurrent] + textfield->getTotalNumChars())); + } + saveCaretStartPositionLock = false; // std::cout << "show result " << resultTotal << "\n"; } -void CodeEditorSearch::search() +void CodeEditorSearch::search(bool moveCaret) { // move to start pos saveCaretStartPositionLock = true; - ed->moveCaretTo(startCaretPosition, false); + if (moveCaret) + ed->moveCaretTo(startCaretPosition, false); saveCaretStartPositionLock = false; auto caret = ed->getCaretPos(); @@ -525,32 +628,54 @@ void CodeEditorSearch::search() juce::String txt = ed->getDocument().getAllContent(); int pos = 0; int count = 0; + + // case sensitivity int res = !button[0]->isSelected() ? txt.indexOfIgnoreCase(pos, textfield->getText()) : txt.indexOf(pos, textfield->getText()); resultCurrent = 0; bool firstFound = false; + bool skip = false; while (res != -1 && count < 512) { - // whole word + // whole word search if (button[1]->isSelected()) { + auto posBefore = (std::max(0, res - 1)); + auto posAfter = std::min(ed->getDocument().getNumCharacters() - 1, + res + textfield->getTotalNumChars()); + + auto strBefore = posBefore == 0 ? 0 : txt[posBefore]; + auto strAfter = txt[posAfter]; + + if (!((strBefore >= 65 && strBefore <= 90) || !(strBefore >= 97 && strBefore <= 122)) || + !((strAfter >= 65 && strAfter <= 90) || !(strAfter >= 97 && strAfter <= 122))) + skip = true; + + // std::cout << "before: " << txt[posBefore] << " after: " << txt[posAfter] + //<< " skip:" << skip << "\n"; } - result[count] = res; - if (caretPos <= res && !firstFound) + + if (caretPos <= res && !firstFound && !skip) { resultCurrent = count; firstFound = true; } + if (!skip) + { + result[count] = res; + count++; + } + pos = res + 1; res = !button[0]->isSelected() ? txt.indexOfIgnoreCase(pos, textfield->getText()) : txt.indexOf(pos, textfield->getText()); - count++; + skip = false; } resultTotal = count; - showResult(0, true); + showResult(0, moveCaret); } juce::String CodeEditorSearch::getSearchQuery() { return textfield->getText(); } @@ -568,8 +693,23 @@ SurgeCodeEditorComponent::SurgeCodeEditorComponent(juce::CodeDocument &d, juce:: currentSkin = &skin; } -void SurgeCodeEditorComponent::setSearch(CodeEditorSearch &s) { search = &s; } +bool SurgeCodeEditorComponent::keyPressed(const juce::KeyPress &key) +{ + + bool code = CodeEditorComponent::keyPressed(key); + + // update search results + if (search != nullptr) + { + if (search->isVisible()) + search->search(false); + } + return code; +} + +void SurgeCodeEditorComponent::setSearch(CodeEditorSearch &s) { search = &s; } +void SurgeCodeEditorComponent::setGotoLine(GotoLine &s) { gotoLine = &s; } void SurgeCodeEditorComponent::paint(juce::Graphics &g) { // draw background @@ -581,8 +721,25 @@ void SurgeCodeEditorComponent::paint(juce::Graphics &g) g.setFillType(juce::FillType(bgColor)); g.fillRect(bounds); - // Draw search matches + // draw the current line for gotoLine module + if (gotoLine != nullptr && gotoLine->isVisible()) + { + auto currentLine = gotoLine->getCurrentLine(); + auto topLine = getFirstLineOnScreen(); + auto numLines = getNumLinesOnScreen(); + auto lineHeight = getLineHeight(); + + if (currentLine >= topLine && currentLine < topLine + numLines) + { + auto highlightColor = bgColor.interpolatedWith(search->COLOR_MATCH, 0.3); + auto y = (currentLine - topLine) * lineHeight; + g.setFillType(juce::FillType(highlightColor)); + g.fillRect(0, y, getWidth() - getScrollbarThickness(), lineHeight); + } + } + + // Draw search matches if (search != nullptr && search->isVisible() && search->getResultTotal() > 0) { auto result = search->getResult(); @@ -635,6 +792,12 @@ void SurgeCodeEditorComponent::handleEscapeKey() search->hide(); return; } + + if (gotoLine->isVisible()) + { + gotoLine->hide(); + return; + } juce::Component *c = this; while (c) { @@ -763,14 +926,19 @@ CodeEditorContainerWithApply::CodeEditorContainerWithApply(SurgeGUIEditor *ed, S EditorColors::setColorsFromSkin(mainEditor.get(), skin); + // modules + gotoLine = std::make_unique(*mainEditor, skin); search = std::make_unique(*mainEditor, skin); mainEditor->setSearch(*search); + mainEditor->setGotoLine(*gotoLine); + if (addComponents) { addAndMakeVisible(applyButton.get()); addAndMakeVisible(mainEditor.get()); addChildComponent(search.get()); + addChildComponent(gotoLine.get()); } applyButton->setEnabled(false); @@ -889,11 +1057,24 @@ bool CodeEditorContainerWithApply::keyPressed(const juce::KeyPress &key, juce::C // search else if (key.getModifiers().isCommandDown() && keyCode == 70) { + gotoLine->hide(); search->show(); return true; } + // search + else if (key.getModifiers().isCommandDown() && keyCode == 71) + { + + bool isgoto = (gotoLine == nullptr); + + std::cout << "gotoLine " << isgoto << "\n"; + search->hide(); + gotoLine->show(); + return true; + } + // handle string completion else if (key.getTextCharacter() == 34) @@ -1514,6 +1695,7 @@ FormulaModulatorEditor::FormulaModulatorEditor(SurgeGUIEditor *ed, SurgeStorage addAndMakeVisible(*controlArea); addAndMakeVisible(*mainEditor); addChildComponent(*search); + addChildComponent(*gotoLine); addChildComponent(*preludeDisplay); debugPanel = std::make_unique(this); @@ -1612,6 +1794,7 @@ void FormulaModulatorEditor::resized() controlArea->setBounds(0, height - controlHeight, width, controlHeight); search->resize(); + gotoLine->resize(); } void FormulaModulatorEditor::showModulatorCode() diff --git a/src/surge-xt/gui/overlays/LuaEditors.h b/src/surge-xt/gui/overlays/LuaEditors.h index 6481c665f5d..e28b3de163e 100644 --- a/src/surge-xt/gui/overlays/LuaEditors.h +++ b/src/surge-xt/gui/overlays/LuaEditors.h @@ -93,6 +93,7 @@ class Textfield : public juce::TextEditor void paint(juce::Graphics &g) override; void setColour(int colourID, juce::Colour newColour); void setHeader(juce::String h); + void setHeaderColor(juce::Colour &c); }; class TextfieldPopup : public juce::Component, @@ -108,12 +109,13 @@ class TextfieldPopup : public juce::Component, static inline int STYLE_MARGIN_BETWEEN_TEXT_AND_BUTTONS = 40; protected: + static TextfieldPopup *lastPopup; juce::CodeEditorComponent *ed; Surge::GUI::Skin::ptr_t currentSkin; std::unique_ptr textfield; std::unique_ptr labelResult; std::unique_ptr button[8]; - // TextfieldButton button[8]; + juce::String header; int buttonCount = 0; @@ -121,13 +123,18 @@ class TextfieldPopup : public juce::Component, int textWidth = 120; public: - void paint(juce::Graphics &g) override; + virtual bool keyPressed(const juce::KeyPress &key, + juce::Component *originatingComponent) override; + virtual void paint(juce::Graphics &g) override; virtual void onClick(std::unique_ptr &btn); TextfieldPopup(juce::CodeEditorComponent &editor, Surge::GUI::Skin::ptr_t); + virtual void textEditorEscapeKeyPressed(juce::TextEditor &) override; void resize(); void setHeader(juce::String); void createButton(juce::String svg); void setTextWidth(int w); + virtual void show(); + virtual void hide(); }; class CodeEditorSearch : public TextfieldPopup @@ -150,15 +157,13 @@ class CodeEditorSearch : public TextfieldPopup juce::CodeDocument::Position startCaretPosition; - virtual void search(); - public: juce::Colour COLOR_MATCH; - + virtual void search(bool moveCaret); virtual juce::String getSearchQuery(); virtual bool isActive(); - virtual void show(); - virtual void hide(); + virtual void show() override; + virtual void hide() override; // virtual void resize(); virtual void onClick(std::unique_ptr &btn) override; @@ -179,21 +184,41 @@ class CodeEditorSearch : public TextfieldPopup virtual int getResultTotal(); }; +class GotoLine : public TextfieldPopup +{ + public: + virtual bool keyPressed(const juce::KeyPress &key, + juce::Component *originatingComponent) override; + GotoLine(juce::CodeEditorComponent &editor, Surge::GUI::Skin::ptr_t); + virtual void show() override; + virtual void hide() override; + void focusLost(FocusChangeType) override; + int currentLine; + int getCurrentLine(); + + private: + int startScroll; + juce::CodeDocument::Position startCaretPosition; +}; + class SurgeCodeEditorComponent : public juce::CodeEditorComponent { public: + bool keyPressed(const juce::KeyPress &key) override; virtual void handleEscapeKey() override; virtual void handleReturnKey() override; virtual void caretPositionMoved() override; virtual void paint(juce::Graphics &) override; virtual void setSearch(CodeEditorSearch &s); + virtual void setGotoLine(GotoLine &s); SurgeCodeEditorComponent(juce::CodeDocument &d, juce::CodeTokeniser *t, Surge::GUI::Skin::ptr_t &skin); private: Surge::GUI::Skin::ptr_t *currentSkin; CodeEditorSearch *search = nullptr; + GotoLine *gotoLine = nullptr; }; /* @@ -216,6 +241,7 @@ class CodeEditorContainerWithApply : public OverlayComponent, std::unique_ptr applyButton; std::unique_ptr tokenizer; std::unique_ptr search; + std::unique_ptr gotoLine; void buttonClicked(juce::Button *button) override; void codeDocumentTextDeleted(int startIndex, int endIndex) override; From 125f5aacc3b2e52aa88984a8546d577b5b3e808f Mon Sep 17 00:00:00 2001 From: blancoberg Date: Fri, 13 Dec 2024 16:23:03 +0100 Subject: [PATCH 11/14] update wt editor goto line module was not visible in the wt editor --- src/surge-xt/gui/overlays/LuaEditors.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/surge-xt/gui/overlays/LuaEditors.cpp b/src/surge-xt/gui/overlays/LuaEditors.cpp index 40533a03c06..afb0f822458 100644 --- a/src/surge-xt/gui/overlays/LuaEditors.cpp +++ b/src/surge-xt/gui/overlays/LuaEditors.cpp @@ -2794,6 +2794,7 @@ WavetableScriptEditor::WavetableScriptEditor(SurgeGUIEditor *ed, SurgeStorage *s addAndMakeVisible(*controlArea); addAndMakeVisible(*mainEditor); addChildComponent(*search); + addChildComponent(*gotoLine); addChildComponent(*preludeDisplay); @@ -2897,6 +2898,7 @@ void WavetableScriptEditor::resized() rendererHeight); search->resize(); + gotoLine->resize(); rerenderFromUIState(); } From c4b1d201bdfa837ee0c5f911132054d4ff8b7bcf Mon Sep 17 00:00:00 2001 From: blancoberg Date: Fri, 13 Dec 2024 16:36:35 +0100 Subject: [PATCH 12/14] param fix --- src/surge-xt/gui/overlays/LuaEditors.cpp | 2 +- src/surge-xt/gui/overlays/LuaEditors.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/surge-xt/gui/overlays/LuaEditors.cpp b/src/surge-xt/gui/overlays/LuaEditors.cpp index afb0f822458..f5aa60eabc2 100644 --- a/src/surge-xt/gui/overlays/LuaEditors.cpp +++ b/src/surge-xt/gui/overlays/LuaEditors.cpp @@ -162,7 +162,7 @@ void Textfield::paint(juce::Graphics &g) void Textfield::setHeader(juce::String h) { header = h; } -void Textfield::setHeaderColor(juce::Colour &c) { colour = c; } +void Textfield::setHeaderColor(juce::Colour c) { colour = c; } void Textfield::setColour(int colourID, juce::Colour newColour) { diff --git a/src/surge-xt/gui/overlays/LuaEditors.h b/src/surge-xt/gui/overlays/LuaEditors.h index e28b3de163e..17567080c85 100644 --- a/src/surge-xt/gui/overlays/LuaEditors.h +++ b/src/surge-xt/gui/overlays/LuaEditors.h @@ -93,7 +93,7 @@ class Textfield : public juce::TextEditor void paint(juce::Graphics &g) override; void setColour(int colourID, juce::Colour newColour); void setHeader(juce::String h); - void setHeaderColor(juce::Colour &c); + void setHeaderColor(juce::Colour c); }; class TextfieldPopup : public juce::Component, From 160e93ad3cc64766d1cf2ee6290e2069a63cab42 Mon Sep 17 00:00:00 2001 From: blancoberg Date: Sat, 14 Dec 2024 12:34:12 +0100 Subject: [PATCH 13/14] more editor stuff * fixed infinite indentation bug * ALT + mousewheel scroll at 4x speed --- src/surge-xt/gui/overlays/LuaEditors.cpp | 28 +++++++++++++++++++----- src/surge-xt/gui/overlays/LuaEditors.h | 1 + 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/src/surge-xt/gui/overlays/LuaEditors.cpp b/src/surge-xt/gui/overlays/LuaEditors.cpp index e8ae74b3cf3..9a89092eb53 100644 --- a/src/surge-xt/gui/overlays/LuaEditors.cpp +++ b/src/surge-xt/gui/overlays/LuaEditors.cpp @@ -791,15 +791,27 @@ void SurgeCodeEditorComponent::caretPositionMoved() } } +void SurgeCodeEditorComponent::mouseWheelMove(const juce::MouseEvent &e, + const juce::MouseWheelDetails &wheel) +{ + auto modifiedMouseWheel = juce::MouseWheelDetails(); + juce::MouseWheelDetails w(wheel); + if (e.mods.isAltDown()) + w.deltaY *= 4; + + CodeEditorComponent::mouseWheelMove(e, w); +} + // Handles auto indentation void SurgeCodeEditorComponent::handleReturnKey() { - auto pos = this->getCaretPos(); auto txt = pos.getLineText(); int tabs = 0; - + int indexInLine = pos.getIndexInLine(); + int actualCharactersBeforeCaret = 0; + bool indent = false; for (int i = 0; i < txt.length(); i++) { if (txt.substring(i, i + 1) == " ") @@ -812,7 +824,7 @@ void SurgeCodeEditorComponent::handleReturnKey() } else { - bool indent = false; + auto trimmedTxt = txt.trim(); if (txt.substring(i, i + 8) == "function") @@ -839,10 +851,17 @@ void SurgeCodeEditorComponent::handleReturnKey() break; } + + if (i < indexInLine) + { + actualCharactersBeforeCaret = tabs; + } } this->insertTextAtCaret("\n"); - this->insertTextAtCaret(std::string(tabs, ' ')); + this->insertTextAtCaret(std::string( + std::min(actualCharactersBeforeCaret + (indent == true ? this->getTabSize() : 0), tabs), + ' ')); } struct EditorColors @@ -1135,7 +1154,6 @@ bool CodeEditorContainerWithApply::autoCompleteStringDeclaration(juce::String st mainEditor->insertTextAtCaret(str); } return true; - // sdfsd } void CodeEditorContainerWithApply::paint(juce::Graphics &g) { g.fillAll(juce::Colours::black); } diff --git a/src/surge-xt/gui/overlays/LuaEditors.h b/src/surge-xt/gui/overlays/LuaEditors.h index 4e6f842da6c..75771744a01 100644 --- a/src/surge-xt/gui/overlays/LuaEditors.h +++ b/src/surge-xt/gui/overlays/LuaEditors.h @@ -199,6 +199,7 @@ class SurgeCodeEditorComponent : public juce::CodeEditorComponent virtual void paint(juce::Graphics &) override; virtual void setSearch(CodeEditorSearch &s); virtual void setGotoLine(GotoLine &s); + void mouseWheelMove(const juce::MouseEvent &e, const juce::MouseWheelDetails &d) override; SurgeCodeEditorComponent(juce::CodeDocument &d, juce::CodeTokeniser *t, Surge::GUI::Skin::ptr_t &skin); From b7a6c408713b4e3856fce13b63d589892b3b98a1 Mon Sep 17 00:00:00 2001 From: EvilDragon Date: Sat, 14 Dec 2024 15:18:49 +0100 Subject: [PATCH 14/14] Hardcode 4x mousewheel factor without holding Alt --- src/surge-xt/gui/overlays/LuaEditors.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/surge-xt/gui/overlays/LuaEditors.cpp b/src/surge-xt/gui/overlays/LuaEditors.cpp index 9a89092eb53..9f5353dff8e 100644 --- a/src/surge-xt/gui/overlays/LuaEditors.cpp +++ b/src/surge-xt/gui/overlays/LuaEditors.cpp @@ -794,11 +794,8 @@ void SurgeCodeEditorComponent::caretPositionMoved() void SurgeCodeEditorComponent::mouseWheelMove(const juce::MouseEvent &e, const juce::MouseWheelDetails &wheel) { - auto modifiedMouseWheel = juce::MouseWheelDetails(); juce::MouseWheelDetails w(wheel); - if (e.mods.isAltDown()) - w.deltaY *= 4; - + w.deltaY *= 4; CodeEditorComponent::mouseWheelMove(e, w); } @@ -3021,4 +3018,4 @@ WavetableScriptEditor::getPreCloseChickenBoxMessage() } } // namespace Overlays -} // namespace Surge \ No newline at end of file +} // namespace Surge