diff --git a/hi_tools/hi_multipage/InputComponents.cpp b/hi_tools/hi_multipage/InputComponents.cpp index 5c486423c..00ccd9c24 100644 --- a/hi_tools/hi_multipage/InputComponents.cpp +++ b/hi_tools/hi_multipage/InputComponents.cpp @@ -759,272 +759,7 @@ void ColourChooser::createEditor(Dialog::PageInfo& info) } #endif -struct TextInput::Autocomplete: public Component, - public ScrollBar::Listener, - public ComponentMovementWatcher -{ - struct Item - { - String displayString; - }; - - ScrollBar sb; - ScrollbarFader fader; - - static constexpr int ItemHeight = 28; - - void mouseWheelMove(const MouseEvent& e, const MouseWheelDetails& details) - { - sb.mouseWheelMove(e, details); - } - - void scrollBarMoved (ScrollBar* scrollBarThatHasMoved, - double newRangeStart) override - { - repaint(); - } - - Font f; - - void componentMovedOrResized (bool wasMoved, bool wasResized) override - { - dismiss(); - } - - /** This callback happens when the component's top-level peer is changed. */ - void componentPeerChanged() {}; - - /** This callback happens when the component's visibility state changes, possibly due to - one of its parents being made visible or invisible. - */ - void componentVisibilityChanged() override - { - if(getComponent()->isShowing()) - dismiss(); - } - - Autocomplete(TextInput& p): - ComponentMovementWatcher(&p), - parent(&p), - sb(true) - { - f = Dialog::getDefaultFont(*parent).first; - - sb.addListener(this); - addAndMakeVisible(sb); - fader.addScrollBarToAnimate(sb); - - for(auto& i: p.autocompleteItems) - allItems.add({i}); - - sb.setSingleStepSize(0.2); - - auto& ed = parent->getComponent(); - - update(ed.getText()); - - setSize(ed.getWidth() + 20, ItemHeight * 4 + 5 + 20); - - setWantsKeyboardFocus(true); - - auto top = TopLevelWindowWithOptionalOpenGL::findRoot(parent); - if(top == nullptr) - top = parent->getTopLevelComponent(); - - top->addChildComponent(this); - auto topLeft = ed.getTopLevelComponent()->getLocalArea(&ed, ed.getLocalBounds()).getTopLeft(); - - setTopLeftPosition(topLeft.getX() - 10, topLeft.getY() + ed.getHeight()); - -#if JUCE_DEBUG - setVisible(true); -#else - Desktop::getInstance().getAnimator().fadeIn(this, 150); -#endif - - } - - ~Autocomplete() - { - setComponentEffect(nullptr); - } - - int selectedIndex = 0; - - void mouseDown(const MouseEvent& e) override - { - auto newIndex = sb.getCurrentRangeStart() + (e.getPosition().getY() - 15) / ItemHeight; - - if(isPositiveAndBelow(newIndex, items.size())) - setSelectedIndex(newIndex); - } - - void mouseDoubleClick(const MouseEvent& e) override - { - setAndDismiss(); - } - - bool inc(bool next) - { - auto newIndex = selectedIndex + (next ? 1 : -1); - - if(isPositiveAndBelow(newIndex, items.size())) - { - setSelectedIndex(newIndex); - return true; - } - - return false; - } - - bool keyPressed(const KeyPress& k) - { - if(k == KeyPress::upKey) - return inc(false); - if(k == KeyPress::downKey) - return inc(true); - if(k == KeyPress::escapeKey) - return dismiss(); - if(k == KeyPress::returnKey || - k == KeyPress::tabKey) - return setAndDismiss(); - - return false; - } - - void setSelectedIndex(int index) - { - - - selectedIndex = index; - - if(!sb.getCurrentRange().contains(selectedIndex)) - { - if(sb.getCurrentRange().getStart() > selectedIndex) - sb.setCurrentRangeStart(selectedIndex); - else - sb.setCurrentRangeStart(selectedIndex - 3); - } - - repaint(); - } - - void resized() override - { - sb.setBounds(getLocalBounds().reduced(10).removeFromRight(16).reduced(1)); - } - - void paint(Graphics& g) override - { - auto b = getLocalBounds().toFloat(); - - b = b.reduced(10.0f); - - DropShadow sh; - sh.colour = Colours::black.withAlpha(0.7f); - sh.radius = 10; - sh.drawForRectangle(g, b.toNearestInt()); - - b.reduced(2.0f); - g.setColour(Colour(0xFF222222)); - g.fillRoundedRectangle(b, 5.0f); - g.setColour(Colours::white.withAlpha(0.3f)); - g.drawRoundedRectangle(b, 5.0f, 2.0f); - - auto r = sb.getCurrentRange(); - - b.reduced(5.0f); - b.removeFromLeft(10.0f); - b.removeFromRight(sb.getWidth()); - - b.removeFromTop(2.5f); - - g.setFont(f); - - if(items.isEmpty()) - { - g.setColour(Colours::white.withAlpha(0.1f)); - g.drawText("No items found", getLocalBounds().toFloat(), Justification::centred); - } - else - { - for(int i = 0; i < 4; i++) - { - g.setColour(Colours::white.withAlpha(0.6f)); - auto tb = b.removeFromTop(ItemHeight); - - auto idx = i + roundToInt(r.getStart()); - - if(idx == selectedIndex) - { - g.fillRoundedRectangle(tb.withX(10.0f).reduced(3.0f, 1.0f), 3.0f); - g.setColour(Colours::black.withAlpha(0.8f)); - } - - g.drawText(items[idx].displayString, tb, Justification::left); - } - } - } - - bool setAndDismiss() - { - auto newTextAfterComma = items[selectedIndex].displayString; - auto& ed = parent->getComponent(); - - String nt = ed.getText(); - - if(nt.containsChar(',')) - { - nt = nt.upToLastOccurrenceOf(",", false, false); - nt << ", " << newTextAfterComma; - } - else - nt = newTextAfterComma; - - ed.setText(nt, true); - - return dismiss(); - } - - bool dismiss() - { - SafeAsyncCall::call(*parent, [](TextInput& ti) - { - ti.dismissAutocomplete(); - ti.getComponent().grabKeyboardFocusAsync(); - }); - - return true; - } - - void update(const String& currentText) - { - auto search = currentText.fromLastOccurrenceOf(",", false, false).toLowerCase().trim(); - - items.clear(); - - for(const auto& i: allItems) - { - if(search.isEmpty() || i.displayString.toLowerCase().contains(search)) - { - items.add(i); - } - } - - sb.setRangeLimits(0.0, (double)items.size()); - sb.setCurrentRange(0.0, 4.0); - setSelectedIndex(0); - - if(items.isEmpty()) - dismiss(); - } - - Array allItems; - Array items; - - WeakReference parent; -}; @@ -1033,19 +768,12 @@ void TextInput::timerCallback() if(callOnEveryChange) callOnValueChange("change"); - if(Component::getCurrentlyFocusedComponent() == &getComponent()) - showAutocomplete(getComponent().getText()); - - stopTimer(); + TextEditorWithAutocompleteComponent::timerCallback(); } void TextInput::textEditorReturnKeyPressed(TextEditor& e) { - if(currentAutocomplete != nullptr) - currentAutocomplete->setAndDismiss(); - - - + TextEditorWithAutocompleteComponent::textEditorReturnKeyPressed(e); rootDialog.grabKeyboardFocusAsync(); callOnValueChange("submit"); @@ -1053,28 +781,21 @@ void TextInput::textEditorReturnKeyPressed(TextEditor& e) void TextInput::textEditorTextChanged(TextEditor& e) { - //check(MultiPageDialog::getGlobalState(*this, id, var())); - if(parseInputAsArray) writeState(Dialog::parseCommaList(e.getText())); else writeState(e.getText()); - startTimer(400); + TextEditorWithAutocompleteComponent::textEditorTextChanged(e); } -void TextInput::textEditorEscapeKeyPressed(TextEditor& e) -{ - if(currentAutocomplete != nullptr) - dismissAutocomplete(); - else - currentAutocomplete = new Autocomplete(*this); -} + TextInput::TextInput(Dialog& r, int width, const var& obj): - LabelledComponent(r, width, obj, new TextEditor()), - navigator(*this) + LabelledComponent(r, width, obj, new TextEditor()) { + initEditor(); + parseInputAsArray = obj[mpid::ParseArray]; auto& editor = getComponent(); @@ -1088,7 +809,7 @@ TextInput::TextInput(Dialog& r, int width, const var& obj): setWantsKeyboardFocus(false); - editor.addKeyListener(&navigator); + editor.setSelectAllWhenFocused(false); editor.setIgnoreUpDownKeysWhenSingleLine(true); editor.setTabKeyUsedAsCharacter(false); @@ -1107,29 +828,6 @@ TextInput::TextInput(Dialog& r, int width, const var& obj): resized(); } -bool TextInput::AutocompleteNavigator::keyPressed(const KeyPress& k, Component* originatingComponent) -{ - if(k == KeyPress::tabKey) - { - if(parent.currentAutocomplete != nullptr) - parent.dismissAutocomplete(); - - parent.getComponent().moveKeyboardFocusToSibling(true); - return true; - } - - if(parent.currentAutocomplete == nullptr) - return false; - - if(k == KeyPress::upKey) - return parent.currentAutocomplete->inc(false); - if(k == KeyPress::downKey) - return parent.currentAutocomplete->inc(true); - - - - return false; -} void TextInput::postInit() { @@ -1333,41 +1031,6 @@ Result TextInput::loadFromInfoObject(const var& obj) return ok; } -void TextInput::showAutocomplete(const String& currentText) -{ - if(useDynamicAutocomplete) - { - if(auto hd = findParentComponentOfClass()) - autocompleteItems = hd->getAutocompleteItems(id); - else - autocompleteItems = {}; - } - - if(!autocompleteItems.isEmpty() && currentAutocomplete == nullptr && currentText.isNotEmpty()) - { - currentAutocomplete = new Autocomplete(*this); - } - else - { - if(currentText.isEmpty()) - currentAutocomplete = nullptr; - else if (currentAutocomplete != nullptr) - currentAutocomplete->update(currentText); - } -} - -void TextInput::dismissAutocomplete() -{ - stopTimer(); -#if JUCE_DEBUG - if(currentAutocomplete != nullptr) - currentAutocomplete->setVisible(false); -#else - if(currentAutocomplete != nullptr) - Desktop::getInstance().getAnimator().fadeOut(currentAutocomplete, 150); -#endif - currentAutocomplete = nullptr; -} #if HISE_MULTIPAGE_INCLUDE_EDIT diff --git a/hi_tools/hi_multipage/InputComponents.h b/hi_tools/hi_multipage/InputComponents.h index 082a6aca4..7447a5c9f 100644 --- a/hi_tools/hi_multipage/InputComponents.h +++ b/hi_tools/hi_multipage/InputComponents.h @@ -72,8 +72,8 @@ struct LabelledComponent: public Dialog::PageBase struct TextInput: public LabelledComponent, - public TextEditor::Listener, - public Timer + public TextEditorWithAutocompleteComponent + { DEFAULT_PROPERTIES(TextInput) { @@ -89,22 +89,14 @@ struct TextInput: public LabelledComponent, TextInput(Dialog& r, int width, const var& obj);; - struct AutocompleteNavigator: public KeyListener - { - AutocompleteNavigator(TextInput& parent_): - parent(parent_) - {} - bool keyPressed (const KeyPress& key, - Component* originatingComponent) override; - - TextInput& parent; - } navigator; - void timerCallback() override; void textEditorReturnKeyPressed(TextEditor& e); void textEditorTextChanged(TextEditor& e); - void textEditorEscapeKeyPressed(TextEditor& e); + + TextEditor* getTextEditor() override { return &getComponent(); } + + Identifier getIdForAutocomplete() const override { return id; } void postInit() override; Result checkGlobalState(var globalState) override; @@ -114,19 +106,9 @@ struct TextInput: public LabelledComponent, private: - bool useDynamicAutocomplete = false; - bool callOnEveryChange = false; String emptyText; - - void showAutocomplete(const String& currentText); - void dismissAutocomplete(); - - struct Autocomplete; - - ScopedPointer currentAutocomplete; - StringArray autocompleteItems; bool parseInputAsArray = false; JUCE_DECLARE_WEAK_REFERENCEABLE(TextInput); diff --git a/hi_tools/hi_multipage/State.h b/hi_tools/hi_multipage/State.h index 5ce0dc61e..6ea5f9f45 100644 --- a/hi_tools/hi_multipage/State.h +++ b/hi_tools/hi_multipage/State.h @@ -560,12 +560,12 @@ struct MonolithData InputStream* input; }; -struct HardcodedDialogWithStateBase +struct HardcodedDialogWithStateBase: public TextEditorWithAutocompleteComponent::Parent { virtual ~HardcodedDialogWithStateBase() {}; /** Override this method and return an item list for the autocomplete popup for the given id*/ - virtual StringArray getAutocompleteItems(const Identifier& textEditorId) { return {}; }; + StringArray getAutocompleteItems(const Identifier& textEditorId) override { return {}; }; }; struct HardcodedDialogWithState: public Component, diff --git a/hi_tools/hi_standalone_components/AdvancedCodeEditor.cpp b/hi_tools/hi_standalone_components/AdvancedCodeEditor.cpp index 85028d195..df7700767 100644 --- a/hi_tools/hi_standalone_components/AdvancedCodeEditor.cpp +++ b/hi_tools/hi_standalone_components/AdvancedCodeEditor.cpp @@ -34,6 +34,332 @@ namespace hise { using namespace juce; +struct TextEditorWithAutocompleteComponent::Autocomplete: public Component, + public ScrollBar::Listener, + public ComponentMovementWatcher +{ + struct Item + { + String displayString; + }; + + ScrollBar sb; + ScrollbarFader fader; + + + + void mouseWheelMove(const MouseEvent& e, const MouseWheelDetails& details) + { + sb.mouseWheelMove(e, details); + } + + void scrollBarMoved (ScrollBar* scrollBarThatHasMoved, + double newRangeStart) override + { + repaint(); + } + + Font f; + + void componentMovedOrResized (bool wasMoved, bool wasResized) override + { + dismiss(); + } + + /** This callback happens when the component's top-level peer is changed. */ + void componentPeerChanged() {}; + + /** This callback happens when the component's visibility state changes, possibly due to + one of its parents being made visible or invisible. + */ + void componentVisibilityChanged() override + { + if(getComponent()->isShowing()) + dismiss(); + } + + Autocomplete(TextEditorWithAutocompleteComponent& p): + ComponentMovementWatcher(dynamic_cast(&p)), + parent(&p), + sb(true) + { + f = parent->getTextEditor()->getFont(); + + sb.addListener(this); + addAndMakeVisible(sb); + fader.addScrollBarToAnimate(sb); + + for(auto& i: p.autocompleteItems) + allItems.add({i}); + + sb.setSingleStepSize(0.2); + + auto& ed = *parent->getTextEditor(); + + update(ed.getText()); + + setSize(jmax(300, ed.getWidth() + 20), TextEditorWithAutocompleteComponent::ItemHeight * 4 + 5 + 20); + + setWantsKeyboardFocus(true); + + Component* top = dynamic_cast(getParentAsComponent()->findParentComponentOfClass()); + + if(top == nullptr) + top = TopLevelWindowWithOptionalOpenGL::findRoot(getParentAsComponent()); + + if(top == nullptr) + top = getParentAsComponent()->getTopLevelComponent(); + + top->addChildComponent(this); + auto topLeft = top->getLocalArea(&ed, ed.getLocalBounds()).getTopLeft(); + + setTopLeftPosition(topLeft.getX() - 10, topLeft.getY() + ed.getHeight()); + +#if JUCE_DEBUG + setVisible(true); +#else + Desktop::getInstance().getAnimator().fadeIn(this, 150); +#endif + + } + + ~Autocomplete() + { + setComponentEffect(nullptr); + } + + int selectedIndex = 0; + + void mouseDown(const MouseEvent& e) override + { + auto newIndex = sb.getCurrentRangeStart() + (e.getPosition().getY() - 15) / ItemHeight; + + if(isPositiveAndBelow(newIndex, items.size())) + setSelectedIndex(newIndex); + } + + void mouseDoubleClick(const MouseEvent& e) override + { + setAndDismiss(); + } + + bool inc(bool next) + { + auto newIndex = selectedIndex + (next ? 1 : -1); + + if(isPositiveAndBelow(newIndex, items.size())) + { + setSelectedIndex(newIndex); + return true; + } + + return false; + } + + bool keyPressed(const KeyPress& k) + { + if(k == KeyPress::upKey) + return inc(false); + if(k == KeyPress::downKey) + return inc(true); + if(k == KeyPress::escapeKey) + return dismiss(); + if(k == KeyPress::returnKey || + k == KeyPress::tabKey) + return setAndDismiss(); + + return false; + } + + void setSelectedIndex(int index) + { + + + selectedIndex = index; + + if(!sb.getCurrentRange().contains(selectedIndex)) + { + if(sb.getCurrentRange().getStart() > selectedIndex) + sb.setCurrentRangeStart(selectedIndex); + else + sb.setCurrentRangeStart(selectedIndex - 3); + } + + repaint(); + } + + void resized() override + { + sb.setBounds(getLocalBounds().reduced(10).removeFromRight(16).reduced(1)); + } + + + + LookAndFeelMethods laf; + + void paint(Graphics& g) override + { + auto r = sb.getCurrentRange(); + auto offset = roundToInt(r.getStart()); + auto thisIndex = selectedIndex - offset; + + StringArray thisItems; + + if(!items.isEmpty()) + { + for(int i = 0; i < 4; i++) + { + thisItems.add(items[i + offset].displayString); + } + } + + laf.drawAutocompleteBackground(g, *parent->getTextEditor(), getLocalBounds().toFloat(), thisItems, thisIndex); + } + + bool setAndDismiss() + { + auto newTextAfterComma = items[selectedIndex].displayString; + auto ed = parent->getTextEditor(); + + String nt = ed->getText(); + + if(nt.containsChar(',')) + { + nt = nt.upToLastOccurrenceOf(",", false, false); + nt << ", " << newTextAfterComma; + } + else + nt = newTextAfterComma; + + ed->setText(nt, true); + + return dismiss(); + } + + bool dismiss() + { + SafeAsyncCall::call(*parent, [](TextEditorWithAutocompleteComponent& ti) + { + ti.dismissAutocomplete(); + ti.getTextEditor()->grabKeyboardFocusAsync(); + }); + + return true; + } + + Component* getParentAsComponent() + { + return dynamic_cast(parent.get()); + } + + void update(const String& currentText) + { + auto search = currentText.fromLastOccurrenceOf(",", false, false).toLowerCase().trim(); + + items.clear(); + + for(const auto& i: allItems) + { + if(search.isEmpty() || i.displayString.toLowerCase().contains(search)) + { + items.add(i); + } + } + + sb.setRangeLimits(0.0, (double)items.size()); + sb.setCurrentRange(0.0, 4.0); + setSelectedIndex(0); + + if(items.isEmpty()) + dismiss(); + } + + Array allItems; + Array items; + + WeakReference parent; +}; + +void TextEditorWithAutocompleteComponent::textEditorReturnKeyPressed(TextEditor& e) +{ + if(auto ac = getCurrentAutocomplete()) + ac->setAndDismiss(); +} + +void TextEditorWithAutocompleteComponent::textEditorEscapeKeyPressed(TextEditor& e) +{ + if(currentAutocomplete != nullptr) + dismissAutocomplete(); + else + currentAutocomplete = new Autocomplete(*this); +} + +bool TextEditorWithAutocompleteComponent::AutocompleteNavigator::keyPressed(const KeyPress& k, Component* originatingComponent) +{ + if(k == KeyPress::tabKey) + { + if(parent.currentAutocomplete != nullptr) + parent.dismissAutocomplete(); + + parent.getTextEditor()->moveKeyboardFocusToSibling(true); + return true; + } + + if(parent.currentAutocomplete == nullptr) + return false; + + if(auto ac = parent.getCurrentAutocomplete()) + { + if(k == KeyPress::upKey) + return ac->inc(false); + if(k == KeyPress::downKey) + return ac->inc(true); + } + + return false; +} + +TextEditorWithAutocompleteComponent::Autocomplete* TextEditorWithAutocompleteComponent::getCurrentAutocomplete() +{ + return dynamic_cast(currentAutocomplete.get()); +} + +void TextEditorWithAutocompleteComponent::showAutocomplete(const String& currentText) +{ + if(useDynamicAutocomplete) + { + if(auto hd = dynamic_cast(this)->findParentComponentOfClass()) + autocompleteItems = hd->getAutocompleteItems(getIdForAutocomplete()); + else + autocompleteItems = {}; + } + + if(!autocompleteItems.isEmpty() && currentAutocomplete == nullptr) + { + currentAutocomplete = new Autocomplete(*this); + } + else + { + if(currentText.isEmpty()) + currentAutocomplete = nullptr; + else if (auto ac = getCurrentAutocomplete()) + ac->update(currentText); + } +} + +void TextEditorWithAutocompleteComponent::dismissAutocomplete() +{ + stopTimer(); +#if JUCE_DEBUG + if(currentAutocomplete != nullptr) + currentAutocomplete->setVisible(false); +#else + if(currentAutocomplete != nullptr) + Desktop::getInstance().getAnimator().fadeOut(currentAutocomplete, 150); +#endif + currentAutocomplete = nullptr; +} + JavascriptCodeEditor::JavascriptCodeEditor(CodeDocument &document, CodeTokeniser *codeTokeniser, ApiProviderBase::Holder *holder, const Identifier& snippetId_) : CodeEditorComponent(document, codeTokeniser), ApiComponentBase(holder), diff --git a/hi_tools/hi_standalone_components/AdvancedCodeEditor.h b/hi_tools/hi_standalone_components/AdvancedCodeEditor.h index f6932fbae..586454263 100644 --- a/hi_tools/hi_standalone_components/AdvancedCodeEditor.h +++ b/hi_tools/hi_standalone_components/AdvancedCodeEditor.h @@ -35,8 +35,132 @@ namespace hise { using namespace juce; - - +/** A interface class for a component that has a text editor that should show a autocomplete popup. */ +struct TextEditorWithAutocompleteComponent: public Timer, + public TextEditor::Listener +{ + static constexpr int ItemHeight = 28; + + /** This is the main top level component (or any other component) that will show the autocomplete. */ + struct Parent + { + virtual ~Parent() {}; + + /** Overwrite this and return a dynamic list of autocomplete items. */ + virtual StringArray getAutocompleteItems(const Identifier& id) = 0; + }; + + TextEditorWithAutocompleteComponent(): + navigator(*this) + {}; + + /** Call this from your subclass. */ + void initEditor() + { + getTextEditor()->addListener(this); + getTextEditor()->addKeyListener(&navigator); + } + + virtual ~TextEditorWithAutocompleteComponent() {}; + + /** Overwrite this and return the text editor that should be used by the autocomplete popup. */ + virtual TextEditor* getTextEditor() = 0; + + void timerCallback() override + { + if(Component::getCurrentlyFocusedComponent() == getTextEditor()) + showAutocomplete(getTextEditor()->getText()); + + stopTimer(); + } + + struct AutocompleteNavigator: public KeyListener + { + AutocompleteNavigator(TextEditorWithAutocompleteComponent& parent_): + parent(parent_) + {} + bool keyPressed (const KeyPress& key, + Component* originatingComponent) override; + + TextEditorWithAutocompleteComponent& parent; + } navigator; + + struct LookAndFeelMethods + { + virtual void drawAutocompleteBackground(Graphics& g, TextEditor& te, Rectangle b, const StringArray& itemToShow, int selectedIndex) + { + b = b.reduced(10.0f); + + DropShadow sh; + sh.colour = Colours::black.withAlpha(0.7f); + sh.radius = 10; + sh.drawForRectangle(g, b.toNearestInt()); + + b.reduced(2.0f); + g.setColour(Colour(0xFF222222)); + g.fillRoundedRectangle(b, 5.0f); + g.setColour(Colours::white.withAlpha(0.3f)); + g.drawRoundedRectangle(b, 5.0f, 2.0f); + + + + b.reduced(5.0f); + b.removeFromLeft(10.0f); + + + b.removeFromTop(2.5f); + + g.setFont(te.getFont()); + + if(itemToShow.isEmpty()) + { + g.setColour(Colours::white.withAlpha(0.1f)); + g.drawText("No items found", b, Justification::centred); + } + else + { + for(int i = 0; i < 4; i++) + { + g.setColour(Colours::white.withAlpha(0.6f)); + auto tb = b.removeFromTop(ItemHeight); + + if(i == selectedIndex) + { + g.fillRoundedRectangle(tb.withX(10.0f).reduced(3.0f, 1.0f), 3.0f); + g.setColour(Colours::black.withAlpha(0.8f)); + } + + g.drawText(itemToShow[i], tb, Justification::left); + } + } + } + }; + + void textEditorTextChanged(TextEditor&) override + { + startTimer(400); + } + + void textEditorReturnKeyPressed(TextEditor& e) override; + + void textEditorEscapeKeyPressed(TextEditor& e) override; + + void showAutocomplete(const String& currentText); + void dismissAutocomplete(); + + virtual Identifier getIdForAutocomplete() const = 0; + + struct Autocomplete; + + Autocomplete* getCurrentAutocomplete(); + + ScopedPointer currentAutocomplete; + StringArray autocompleteItems; + + bool useDynamicAutocomplete = false; + + JUCE_DECLARE_WEAK_REFERENCEABLE(TextEditorWithAutocompleteComponent); +}; /** A subclass of CodeEditorComponent which improves working with Javascript scripts.