diff --git a/src/surge-xt/gui/overlays/LuaEditors.cpp b/src/surge-xt/gui/overlays/LuaEditors.cpp index 9f5353dff8e..b262d7a4f88 100644 --- a/src/surge-xt/gui/overlays/LuaEditors.cpp +++ b/src/surge-xt/gui/overlays/LuaEditors.cpp @@ -45,8 +45,9 @@ namespace Overlays TextfieldPopup Base class that can be used for creating other textfield popups like the search */ -TextfieldButton::TextfieldButton(juce::String &svg) : juce::Component() +TextfieldButton::TextfieldButton(juce::String &svg, int rowToAddTo) : juce::Component() { + row = rowToAddTo; xml = juce::XmlDocument::parse(svg); svgGraphics = juce::Drawable::createFromSVG(*xml); @@ -131,6 +132,8 @@ void TextfieldButton::select(bool v) repaint(); } +Textfield::Textfield(int r) : juce::TextEditor() { row = r; }; + void Textfield::paint(juce::Graphics &g) { juce::TextEditor::paint(g); @@ -169,49 +172,29 @@ TextfieldPopup::TextfieldPopup(juce::CodeEditorComponent &editor, Surge::GUI::Sk juce::Rectangle boundsLabel = juce::Rectangle(95, 2, 80, 20); - textfield = std::make_unique(); + createTextfield(0); labelResult = std::make_unique(); labelResult->setBounds(boundsLabel); labelResult->setFont(juce::FontOptions(10)); 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(0, 4, 0, 4)); - 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::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)); - - textfield->setHeaderColor(skin->getColor(Colors::Dialog::Button::Text)); - - addAndMakeVisible(*textfield); - - textfield->setText(""); - textfield->setEscapeAndReturnKeysConsumed(true); - - textfield->addListener(this); - textfield->addKeyListener(this); - - setPaintingIsUnclipped(true); - setBounds(juce::Rectangle(0, 0, 150, 20)); setVisible(false); resize(); } +void TextfieldPopup::showRows(int r) +{ + rowsVisible = r; + resize(); +} + void TextfieldPopup::paint(juce::Graphics &g) { const auto bounds = getBounds(); @@ -228,7 +211,7 @@ void TextfieldPopup::paint(juce::Graphics &g) void TextfieldPopup::show() { setVisible(true); - textfield->grabKeyboardFocus(); + textfield[0]->grabKeyboardFocus(); } void TextfieldPopup::hide() { setVisible(false); } @@ -237,9 +220,9 @@ void TextfieldPopup::textEditorEscapeKeyPressed(juce::TextEditor &) { hide(); } void TextfieldPopup::onClick(std::unique_ptr &btn) {} -void TextfieldPopup::createButton(juce::String svg) +void TextfieldPopup::createButton(juce::String svg, int row) { - button[buttonCount] = std::make_unique(svg); + button[buttonCount] = std::make_unique(svg, row); auto btn = &button[buttonCount]; auto callback = [this, btn]() { onClick(*btn); }; @@ -249,23 +232,75 @@ void TextfieldPopup::createButton(juce::String svg) buttonCount++; } +void TextfieldPopup::createTextfield(int row) +{ + textfield[textfieldCount] = std::make_unique(row); + + textfield[textfieldCount]->setBorder(juce::BorderSize(1, 4, 0, 4)); + + // textfield[textfieldCount]->setFont(juce::FontOptions(12)); + textfield[textfieldCount]->setFont(currentSkin->getFont(Fonts::LuaEditor::Code)); + + textfield[textfieldCount]->setColour(juce::TextEditor::ColourIds::textColourId, + currentSkin->getColor(Colors::Dialog::Button::Text)); + textfield[textfieldCount]->setColour( + juce::TextEditor::backgroundColourId, + currentSkin->getColor(Colors::FormulaEditor::Background).darker(0.4)); + + textfield[textfieldCount]->setColour( + juce::TextEditor::focusedOutlineColourId, + currentSkin->getColor(Colors::FormulaEditor::Background).brighter(0.03)); + textfield[textfieldCount]->setColour( + juce::TextEditor::outlineColourId, + currentSkin->getColor(Colors::FormulaEditor::Background).brighter(0)); + + textfield[textfieldCount]->setHeaderColor(currentSkin->getColor(Colors::Dialog::Button::Text)); + + textfield[textfieldCount]->addListener(this); + textfield[textfieldCount]->addKeyListener(this); + + addAndMakeVisible(*textfield[textfieldCount]); + + textfield[textfieldCount]->setText(""); + textfield[textfieldCount]->setEscapeAndReturnKeysConsumed(true); + + textfieldCount++; +} + void TextfieldPopup::setTextWidth(int w) { textWidth = w; } -void TextfieldPopup::setHeader(juce::String t) { textfield->setHeader(t); } +void TextfieldPopup::setHeader(juce::String t) { textfield[0]->setHeader(t); } bool TextfieldPopup::keyPressed(const juce::KeyPress &key, juce::Component *originatingComponent) { return originatingComponent->keyPressed(key); } +void TextfieldPopup::setButtonOffsetAtRow(int row, int offset) { buttonOffset[row] = offset; } + void TextfieldPopup::resize() { - int marginBetweenTextAndButtons = buttonCount > 0 ? STYLE_MARGIN_BETWEEN_TEXT_AND_BUTTONS : 0; + int maxLeft[4] = {0}; + + for (int i = 0; i < buttonCount; i++) + { + maxLeft[button[i]->row] += 1; + } + + int buttonTotalInRow = *std::max_element(maxLeft, maxLeft + 4); + + int marginBetweenTextAndButtons = + buttonTotalInRow > 0 ? STYLE_MARGIN_BETWEEN_TEXT_AND_BUTTONS : 0; int totalWidth = STYLE_MARGIN + textWidth + marginBetweenTextAndButtons + STYLE_MARGIN + - (STYLE_BUTTON_SIZE + STYLE_BUTTON_MARGIN) * buttonCount; - int totalHeight = STYLE_TEXT_HEIGHT + STYLE_MARGIN * 2; + (STYLE_BUTTON_SIZE + STYLE_BUTTON_MARGIN) * buttonTotalInRow; + + int textHeight = rowsVisible <= 1 + ? STYLE_TEXT_HEIGHT + : STYLE_TEXT_HEIGHT * rowsVisible + ((rowsVisible - 1) * STYLE_ROW_MARGIN); + + int totalHeight = textHeight + STYLE_MARGIN * 2; - totalWidth += buttonCount > 0 ? STYLE_MARGIN * 2 : 0; + totalWidth += buttonTotalInRow > 0 ? STYLE_MARGIN * 2 : 0; const auto bounds = juce::Rectangle( ed->getWidth() - totalWidth - ed->getScrollbarThickness() + 2, 2, totalWidth, totalHeight); @@ -275,22 +310,30 @@ void TextfieldPopup::resize() const auto boundsLabel = labelResult->getBounds(); labelResult->setBounds(STYLE_MARGIN + textWidth + STYLE_MARGIN, - totalHeight * 0.5 - boundsLabel.getHeight() * 0.5, + STYLE_MARGIN + STYLE_TEXT_HEIGHT * 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); + for (int i = 0; i < textfieldCount; i++) + { + auto y = STYLE_MARGIN + (STYLE_ROW_MARGIN + STYLE_TEXT_HEIGHT) * i; + textfield[i]->setBounds(STYLE_MARGIN, y, textWidth, STYLE_TEXT_HEIGHT); + } - const int buttonY = totalHeight / 2 - STYLE_BUTTON_SIZE * 0.5; + int x[4] = {0}; for (int i = 0; i < buttonCount; i++) { - 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); + int buttonY = STYLE_MARGIN + button[i]->row * (STYLE_TEXT_HEIGHT + STYLE_ROW_MARGIN) + + STYLE_TEXT_HEIGHT * 0.5 - STYLE_BUTTON_SIZE * 0.5; + + int buttonX = x[button[i]->row] + (buttonOffset[button[i]->row]); + + auto bounds = juce::Rectangle(STYLE_MARGIN + textWidth + STYLE_MARGIN + + marginBetweenTextAndButtons + STYLE_MARGIN + buttonX, + buttonY, STYLE_BUTTON_SIZE, STYLE_BUTTON_SIZE); button[i]->setBounds(bounds); + + x[button[i]->row] += (STYLE_BUTTON_SIZE + STYLE_BUTTON_MARGIN); } } @@ -300,12 +343,15 @@ GotoLine::GotoLine(juce::CodeEditorComponent &editor, Surge::GUI::Skin::ptr_t sk : TextfieldPopup(editor, skin) { setHeader("Go to line..."); - setTextWidth(80); + setTextWidth(130); } bool GotoLine::keyPressed(const juce::KeyPress &key, juce::Component *originatingComponent) { - originatingComponent->keyPressed(key); + auto command = key.getModifiers().isCommandDown(); + + if (!command) + originatingComponent->keyPressed(key); if (key.getKeyCode() == key.returnKey) { hide(); @@ -321,7 +367,7 @@ bool GotoLine::keyPressed(const juce::KeyPress &key, juce::Component *originatin else { - int line = std::max(0, textfield->getText().getIntValue() - 1); + int line = std::max(0, textfield[0]->getText().getIntValue() - 1); line = std::min(ed->getDocument().getNumLines(), line); int numLines = ed->getNumLinesOnScreen(); @@ -336,13 +382,13 @@ bool GotoLine::keyPressed(const juce::KeyPress &key, juce::Component *originatin ed->repaint(); - return true; + return command == false; } void GotoLine::show() { currentLine = ed->getCaretPos().getLineNumber(); - textfield->setText(""); + textfield[0]->setText(""); TextfieldPopup::show(); startCaretPosition = ed->getCaretPos(); startScroll = ed->getFirstLineOnScreen(); @@ -367,27 +413,46 @@ CodeEditorSearch::CodeEditorSearch(juce::CodeEditorComponent &editor, Surge::GUI : TextfieldPopup(editor, skin) { + setButtonOffsetAtRow(1, -STYLE_MARGIN_BETWEEN_TEXT_AND_BUTTONS); + // Case sensitivity createButton( - {R"()"}); + {R"()"}, 0); button[0]->setSelectable(); // whole word match createButton( - {R"()"}); + {R"()"}, + 0); button[1]->setSelectable(); // arrow up createButton( - {R"()"}); + {R"()"}, + 0); // arrow down createButton({R"( -)"}); +)"}, + 0); + + // replace one + createButton({R"( +)"}, + 1); + + // replace all + createButton({R"( +)"}, + 1); - // button[1]->setEnabled(false); setHeader("Find..."); + createTextfield(1); + textfield[1]->setHeader("Replace.."); + + // showRows(2); + repaint(); } @@ -398,21 +463,36 @@ void CodeEditorSearch::onClick(std::unique_ptr &btn) { search(true); } + // whole word if (btn == button[1]) { search(true); } + // previous if (btn == button[2]) { showResult(-1, true); } + // next if (btn == button[3]) { showResult(1, true); } + + // replace + if (btn == button[4]) + { + replaceResults(false); + } + + // replace all + if (btn == button[5]) + { + replaceResults(true); + } } int *CodeEditorSearch::getResult() { return result; } @@ -489,14 +569,14 @@ void CodeEditorSearch::show() if (!txt.containsChar('\n') && sel.getLength() != 0) { - textfield->setText(txt); + textfield[0]->setText(txt); } - textfield->moveCaretToStartOfLine(false); - textfield->moveCaretToEndOfLine(true); + textfield[0]->moveCaretToStartOfLine(false); + textfield[0]->moveCaretToEndOfLine(true); search(true); - textfield->grabKeyboardFocus(); + textfield[0]->grabKeyboardFocus(); ed->repaint(); // force update selection color } @@ -512,18 +592,55 @@ void CodeEditorSearch::textEditorReturnKeyPressed(juce::TextEditor &) {} bool CodeEditorSearch::keyPressed(const juce::KeyPress &key, juce::Component *originatingComponent) { - originatingComponent->keyPressed(key); + + auto keyCode = key.getKeyCode(); + + if (key.getKeyCode() == key.tabKey && rowsVisible > 1) + { + if (originatingComponent == textfield[0].get()) + { + textfield[1]->grabKeyboardFocus(); + textfield[1]->moveCaretToEnd(); + textfield[1]->moveCaretToStartOfLine(false); + textfield[1]->moveCaretToEndOfLine(true); + } + else + { + textfield[0]->grabKeyboardFocus(); + textfield[0]->moveCaretToStartOfLine(false); + textfield[0]->moveCaretToEndOfLine(true); + } + return true; + } if (key.getKeyCode() == key.returnKey) { - if (key.getModifiers().isShiftDown()) + // next / previous search + if (originatingComponent == textfield[0].get()) { - showResult(-1, true); + + if (key.getModifiers().isShiftDown()) + { + showResult(-1, true); + } + else + { + showResult(1, true); + } } + // replace next / all else { - showResult(1, true); + if (key.getModifiers().isShiftDown()) + { + replaceResults(true); + } + else + { + replaceResults(false); + } } + return true; } if (key.getKeyCode() == key.escapeKey) @@ -532,30 +649,92 @@ bool CodeEditorSearch::keyPressed(const juce::KeyPress &key, juce::Component *or return true; } - if (key.getModifiers().isCommandDown() && key.getKeyCode() == 70) + return false; +} + +void CodeEditorSearch::replaceResults(bool all) +{ + if (resultTotal > 0) { - return true; + if (all) + { + int iterationCount = 0; + int iterationMax = resultTotal; + + while (resultTotal > 0) + { + replaceCurrentResult(textfield[1]->getText()); + } + } + else + { + replaceCurrentResult(textfield[1]->getText()); + } } +} - return true; +void CodeEditorSearch::replaceCurrentResult(juce::String txt) +{ + + if (resultTotal != 0) + { + ed->getDocument().replaceSection( + result[resultCurrent], result[resultCurrent] + textfield[0]->getTotalNumChars(), txt); + + int diff = txt.length() - latestSearch.length(); + + for (int i = 0; i < resultTotal; i++) + { + if (i >= resultCurrent) + { + int nextResult = i + 1 < 512 ? result[i + 1] : 0; + result[i] = nextResult; + result[i] += diff; + } + } + + resultTotal--; + + showResult(0, true); + } } -void CodeEditorSearch::textEditorTextChanged(juce::TextEditor &textEditor) { search(true); } +void CodeEditorSearch::textEditorTextChanged(juce::TextEditor &textEditor) +{ + + juce::TextEditor *other = dynamic_cast(textfield[0].get()); + + if (&textEditor == other) + search(true); +} void CodeEditorSearch::showResult(int increment, bool moveCaret) { int id = resultCurrent + 1; + // up / down button[2]->setEnabled(resultTotal < 2 ? false : true); button[3]->setEnabled(resultTotal < 2 ? false : true); + // replace + button[4]->setEnabled(resultTotal == 0 ? false : true); + button[5]->setEnabled(resultTotal == 0 ? false : true); + if (resultTotal == 0) { + + auto bgColor = currentSkin->getColor(Colors::FormulaEditor::Background).darker(1.3); + + labelResult->setColour(juce::Label::textColourId, + currentSkin->getColor(Colors::FormulaEditor::Lua::Error) + .interpolatedWith(bgColor, 0.2)); + removeHighlightColors(); id = 0; } else { + labelResult->setColour(juce::Label::textColourId, juce::Colour(255, 255, 255)); setHighlightColors(); } @@ -577,9 +756,13 @@ void CodeEditorSearch::showResult(int increment, bool moveCaret) if (moveCaret) { ed->setHighlightedRegion(juce::Range( - result[resultCurrent], result[resultCurrent] + textfield->getTotalNumChars())); + result[resultCurrent], result[resultCurrent] + textfield[0]->getTotalNumChars())); } + id = resultCurrent + 1; + labelResult->setText(juce::String(std::to_string(id) + '/' + std::to_string(resultTotal)), + juce::NotificationType::dontSendNotification); + saveCaretStartPositionLock = false; } @@ -600,8 +783,8 @@ void CodeEditorSearch::search(bool moveCaret) int count = 0; // case sensitivity - int res = !button[0]->isSelected() ? txt.indexOfIgnoreCase(pos, textfield->getText()) - : txt.indexOf(pos, textfield->getText()); + int res = !button[0]->isSelected() ? txt.indexOfIgnoreCase(pos, textfield[0]->getText()) + : txt.indexOf(pos, textfield[0]->getText()); resultCurrent = 0; bool firstFound = false; @@ -614,7 +797,7 @@ void CodeEditorSearch::search(bool moveCaret) { auto posBefore = (std::max(0, res - 1)); auto posAfter = std::min(ed->getDocument().getNumCharacters() - 1, - res + textfield->getTotalNumChars()); + res + textfield[0]->getTotalNumChars()); auto strBefore = posBefore == 0 ? 0 : txt[posBefore]; auto strAfter = txt[posAfter]; @@ -637,17 +820,40 @@ void CodeEditorSearch::search(bool moveCaret) } pos = res + 1; - res = !button[0]->isSelected() ? txt.indexOfIgnoreCase(pos, textfield->getText()) - : txt.indexOf(pos, textfield->getText()); + res = !button[0]->isSelected() ? txt.indexOfIgnoreCase(pos, textfield[0]->getText()) + : txt.indexOf(pos, textfield[0]->getText()); skip = false; } - + latestSearch = textfield[0]->getText(); resultTotal = count; showResult(0, moveCaret); } -juce::String CodeEditorSearch::getSearchQuery() { return textfield->getText(); } +juce::String CodeEditorSearch::getSearchQuery() { return textfield[0]->getText(); } + +void CodeEditorSearch::showReplace(bool showReplaceRow) +{ + + if (showReplaceRow) + { + showRows(2); + textfield[1]->grabKeyboardFocus(); + textfield[1]->moveCaretToStartOfLine(false); + textfield[1]->moveCaretToEndOfLine(true); + + textfield[0]->moveCaretToEndOfLine(false); + } + else + { + showRows(1); + textfield[0]->grabKeyboardFocus(); + textfield[0]->moveCaretToStartOfLine(false); + textfield[0]->moveCaretToEndOfLine(true); + + textfield[1]->moveCaretToEndOfLine(false); + } +} // --------------------------------------- @@ -658,6 +864,44 @@ SurgeCodeEditorComponent::SurgeCodeEditorComponent(juce::CodeDocument &d, juce:: currentSkin = skin; } +void SurgeCodeEditorComponent::addPopupMenuItems(juce::PopupMenu &menuToAddTo, + const juce::MouseEvent *mouseClickEvent) +{ + juce::CodeEditorComponent::addPopupMenuItems(menuToAddTo, mouseClickEvent); + +#if MAC + std::string commandStr = "CMD"; + +#else + std::string commandStr = "CTRL"; +#endif + + auto find = juce::PopupMenu::Item("Find").setAction([this]() { + search->show(); + search->showReplace(false); + gotoLine->hide(); + }); + find.shortcutKeyDescription = commandStr + "+F"; + + auto replace = juce::PopupMenu::Item("Replace").setAction([this]() { + search->show(); + search->showReplace(true); + gotoLine->hide(); + }); + replace.shortcutKeyDescription = commandStr + "+H"; + + auto gotoline = juce::PopupMenu::Item("Go to line").setAction([this]() { + search->hide(); + gotoLine->show(); + }); + gotoline.shortcutKeyDescription = commandStr + "+G"; + + menuToAddTo.addSeparator(); + menuToAddTo.addItem(find); + menuToAddTo.addItem(replace); + menuToAddTo.addItem(gotoline); +} + bool SurgeCodeEditorComponent::keyPressed(const juce::KeyPress &key) { @@ -717,8 +961,9 @@ void SurgeCodeEditorComponent::paint(juce::Graphics &g) int firstLine = getFirstLineOnScreen(); int lastLine = firstLine + getNumLinesOnScreen(); - auto highlightColor = bgColor.interpolatedWith( - currentSkin->getColor(Colors::FormulaEditor::Lua::Keyword), 0.5); + auto c = Colors::FormulaEditor::Lua::Keyword; + + auto highlightColor = bgColor.interpolatedWith(currentSkin->getColor(c), 0.5); for (int i = 0; i < resultTotal; i++) { @@ -809,7 +1054,7 @@ void SurgeCodeEditorComponent::handleReturnKey() int indexInLine = pos.getIndexInLine(); int actualCharactersBeforeCaret = 0; bool indent = false; - for (int i = 0; i < txt.length(); i++) + for (int i = 0; i < indexInLine; i++) { if (txt.substring(i, i + 1) == " ") { @@ -918,7 +1163,10 @@ CodeEditorContainerWithApply::CodeEditorContainerWithApply(SurgeGUIEditor *ed, S // modules gotoLine = std::make_unique(*mainEditor, skin); + gotoLine->addKeyListener(this); + search = std::make_unique(*mainEditor, skin); + search->addKeyListener(this); mainEditor->setSearch(*search); mainEditor->setGotoLine(*gotoLine); @@ -1048,7 +1296,18 @@ bool CodeEditorContainerWithApply::keyPressed(const juce::KeyPress &key, juce::C else if (key.getModifiers().isCommandDown() && keyCode == 70) { gotoLine->hide(); + search->show(); + search->showReplace(false); + return true; + } + // find & replace + else if (key.getModifiers().isCommandDown() && keyCode == 72) + { + gotoLine->hide(); + + search->show(); + search->showReplace(true); return true; } @@ -1664,6 +1923,8 @@ FormulaModulatorEditor::FormulaModulatorEditor(SurgeGUIEditor *ed, SurgeStorage mainEditor->setDescription("Formula Modulator Code"); mainDocument->insertText(0, fs->formulaString); + mainDocument->clearUndoHistory(); + mainDocument->setSavePoint(); preludeDocument = std::make_unique(); preludeDocument->insertText(0, Surge::LuaSupport::getFormulaPrelude()); @@ -2764,6 +3025,9 @@ WavetableScriptEditor::WavetableScriptEditor(SurgeGUIEditor *ed, SurgeStorage *s mainDocument->insertText(0, osc->wavetable_formula); } + mainDocument->clearUndoHistory(); + mainDocument->setSavePoint(); + preludeDocument = std::make_unique(); preludeDocument->insertText(0, Surge::LuaSupport::getWTSEPrelude()); diff --git a/src/surge-xt/gui/overlays/LuaEditors.h b/src/surge-xt/gui/overlays/LuaEditors.h index 75771744a01..acd6b223293 100644 --- a/src/surge-xt/gui/overlays/LuaEditors.h +++ b/src/surge-xt/gui/overlays/LuaEditors.h @@ -58,7 +58,7 @@ class TextfieldButton : public juce::Component public: bool isSelected() { return selected; }; - TextfieldButton(juce::String &svg); + TextfieldButton(juce::String &svg, int r); void loadSVG(juce::String &svg); void setSelectable(); void select(bool v); @@ -71,6 +71,7 @@ class TextfieldButton : public juce::Component void mouseEnter(const juce::MouseEvent &event) override; void mouseExit(const juce::MouseEvent &event) override; std::function onClick; + int row; private: std::unique_ptr svgGraphics; @@ -87,10 +88,12 @@ class Textfield : public juce::TextEditor juce::String title; public: + Textfield(int r); void paint(juce::Graphics &g) override; void setColour(int colourID, juce::Colour newColour); void setHeader(juce::String h); void setHeaderColor(juce::Colour c); + int row; }; class TextfieldPopup : public juce::Component, @@ -100,7 +103,10 @@ class TextfieldPopup : public juce::Component, { public: static constexpr int STYLE_MARGIN = 4; - static constexpr int STYLE_TEXT_HEIGHT = 20; + + static constexpr int STYLE_ROW_MARGIN = 4; + static constexpr int STYLE_TEXT_HEIGHT = 19; + static constexpr int STYLE_BUTTON_MARGIN = 2; static constexpr int STYLE_BUTTON_SIZE = 14; static constexpr int STYLE_MARGIN_BETWEEN_TEXT_AND_BUTTONS = 40; @@ -108,17 +114,22 @@ class TextfieldPopup : public juce::Component, protected: juce::CodeEditorComponent *ed; Surge::GUI::Skin::ptr_t currentSkin; - std::unique_ptr textfield; + // std::unique_ptr *textfield; std::unique_ptr labelResult; + std::unique_ptr button[8]; + std::unique_ptr textfield[8]; + int buttonOffset[8] = {0}; juce::String header; int buttonCount = 0; + int textfieldCount = 0; private: int textWidth = 120; public: + int rowsVisible = 1; virtual bool keyPressed(const juce::KeyPress &key, juce::Component *originatingComponent) override; virtual void paint(juce::Graphics &g) override; @@ -127,7 +138,11 @@ class TextfieldPopup : public juce::Component, virtual void textEditorEscapeKeyPressed(juce::TextEditor &) override; void resize(); void setHeader(juce::String); - void createButton(juce::String svg); + void createButton(juce::String svg, int row); + void createTextfield(int row); + void showRows(int rows); + void setButtonOffsetAtRow(int row, int offset); + void setTextWidth(int w); virtual void show(); virtual void hide(); @@ -140,11 +155,11 @@ class CodeEditorSearch : public TextfieldPopup virtual void removeHighlightColors(); bool active = false; - int result[512]; + int result[512] = {0}; int resultCurrent = 0; int resultTotal = 0; bool saveCaretStartPositionLock; - + juce::String latestSearch; juce::CodeDocument::Position startCaretPosition; public: @@ -162,6 +177,8 @@ class CodeEditorSearch : public TextfieldPopup juce::Component *originatingComponent) override; virtual void textEditorEscapeKeyPressed(juce::TextEditor &) override; virtual void textEditorReturnKeyPressed(juce::TextEditor &) override; + virtual void replaceResults(bool all); + virtual void replaceCurrentResult(juce::String newText); CodeEditorSearch(juce::CodeEditorComponent &editor, Surge::GUI::Skin::ptr_t); @@ -169,6 +186,7 @@ class CodeEditorSearch : public TextfieldPopup virtual void showResult(int increase, bool moveCaret); virtual int *getResult(); virtual int getResultTotal(); + virtual void showReplace(bool b); }; class GotoLine : public TextfieldPopup @@ -199,6 +217,9 @@ class SurgeCodeEditorComponent : public juce::CodeEditorComponent virtual void paint(juce::Graphics &) override; virtual void setSearch(CodeEditorSearch &s); virtual void setGotoLine(GotoLine &s); + virtual void addPopupMenuItems(juce::PopupMenu &menuToAddTo, + const juce::MouseEvent *mouseClickEvent) override; + void mouseWheelMove(const juce::MouseEvent &e, const juce::MouseWheelDetails &d) override; SurgeCodeEditorComponent(juce::CodeDocument &d, juce::CodeTokeniser *t, Surge::GUI::Skin::ptr_t &skin);