Browse Source

CodeEditorComponent improvements and TextEditor menu refactoring.

tags/2021-05-28
jules 13 years ago
parent
commit
6758ce0bc9
10 changed files with 438 additions and 188 deletions
  1. +2
    -2
      extras/Introjucer/Source/Application/jucer_Application.h
  2. +0
    -2
      extras/Introjucer/Source/Application/jucer_CommandIDs.h
  3. +1
    -1
      extras/Introjucer/Source/Application/jucer_OpenDocumentManager.cpp
  4. +15
    -28
      extras/Introjucer/Source/Code Editor/jucer_SourceCodeEditor.cpp
  5. +88
    -12
      extras/Introjucer/Source/Code Editor/jucer_SourceCodeEditor.h
  6. +6
    -0
      modules/juce_gui_basics/commands/juce_ApplicationCommandID.h
  7. +39
    -49
      modules/juce_gui_basics/widgets/juce_TextEditor.cpp
  8. +2
    -3
      modules/juce_gui_basics/widgets/juce_TextEditor.h
  9. +224
    -76
      modules/juce_gui_extra/code_editor/juce_CodeEditorComponent.cpp
  10. +61
    -15
      modules/juce_gui_extra/code_editor/juce_CodeEditorComponent.h

+ 2
- 2
extras/Introjucer/Source/Application/jucer_Application.h View File

@@ -183,8 +183,8 @@ public:
}
else if (topLevelMenuIndex == 1) // "Edit" menu
{
menu.addCommandItem (commandManager, CommandIDs::undo);
menu.addCommandItem (commandManager, CommandIDs::redo);
menu.addCommandItem (commandManager, StandardApplicationCommandIDs::undo);
menu.addCommandItem (commandManager, StandardApplicationCommandIDs::redo);
menu.addSeparator();
menu.addCommandItem (commandManager, StandardApplicationCommandIDs::cut);
menu.addCommandItem (commandManager, StandardApplicationCommandIDs::copy);


+ 0
- 2
extras/Introjucer/Source/Application/jucer_CommandIDs.h View File

@@ -43,8 +43,6 @@ namespace CommandIDs
static const int showAppearanceSettings = 0x200077;
static const int saveAll = 0x200080;
static const int undo = 0x200090;
static const int redo = 0x2000a0;
static const int closeWindow = 0x201001;
static const int closeAllDocuments = 0x201000;


+ 1
- 1
extras/Introjucer/Source/Application/jucer_OpenDocumentManager.cpp View File

@@ -28,7 +28,7 @@
#include "../Code Editor/jucer_SourceCodeEditor.h"
//==============================================================================
Component* SourceCodeDocument::createEditor() { return SourceCodeEditor::createFor (this, codeDoc); }
Component* SourceCodeDocument::createEditor() { return new SourceCodeEditor (this, codeDoc); }
//==============================================================================


+ 15
- 28
extras/Introjucer/Source/Code Editor/jucer_SourceCodeEditor.cpp View File

@@ -29,23 +29,21 @@
//==============================================================================
SourceCodeEditor::SourceCodeEditor (OpenDocumentManager::Document* document_,
CodeDocument& codeDocument,
CodeTokeniser* const codeTokeniser)
: DocumentEditorComponent (document_),
editor (codeDocument, codeTokeniser)
CodeDocument& codeDocument)
: DocumentEditorComponent (document_)
{
addAndMakeVisible (&editor);
addAndMakeVisible (editor = createEditor (codeDocument));
#if JUCE_MAC
Font font (13.0f);
font.setTypefaceName ("Menlo");
#else
Font font (10.0f);
Font font (12.0f);
font.setTypefaceName (Font::getDefaultMonospacedFontName());
#endif
editor.setFont (font);
editor->setFont (font);
editor.setTabSize (4, true);
editor->setTabSize (4, true);
updateColourScheme();
getAppSettings().appearance.settings.addListener (this);
@@ -56,36 +54,25 @@ SourceCodeEditor::~SourceCodeEditor()
getAppSettings().appearance.settings.removeListener (this);
}
void SourceCodeEditor::resized()
CodeEditorComponent* SourceCodeEditor::createEditor (CodeDocument& codeDocument)
{
editor.setBounds (getLocalBounds());
}
if (document->getFile().hasFileExtension (sourceOrHeaderFileExtensions))
return new CppCodeEditorComponent (codeDocument);
CodeTokeniser* SourceCodeEditor::getTokeniserFor (const File& file)
{
if (file.hasFileExtension (sourceOrHeaderFileExtensions))
{
static CPlusPlusCodeTokeniser cppTokeniser;
return &cppTokeniser;
}
return nullptr;
return new CodeEditorComponent (codeDocument, nullptr);
}
SourceCodeEditor* SourceCodeEditor::createFor (OpenDocumentManager::Document* document,
CodeDocument& codeDocument)
//==============================================================================
void SourceCodeEditor::resized()
{
return new SourceCodeEditor (document, codeDocument, getTokeniserFor (document->getFile()));
editor->setBounds (getLocalBounds());
}
void SourceCodeEditor::updateColourScheme() { getAppSettings().appearance.applyToCodeEditor (*editor); }
void SourceCodeEditor::valueTreePropertyChanged (ValueTree&, const Identifier&) { updateColourScheme(); }
void SourceCodeEditor::valueTreeChildAdded (ValueTree&, ValueTree&) { updateColourScheme(); }
void SourceCodeEditor::valueTreeChildRemoved (ValueTree&, ValueTree&) { updateColourScheme(); }
void SourceCodeEditor::valueTreeChildOrderChanged (ValueTree&) { updateColourScheme(); }
void SourceCodeEditor::valueTreeParentChanged (ValueTree&) { updateColourScheme(); }
void SourceCodeEditor::valueTreeRedirected (ValueTree&) { updateColourScheme(); }
void SourceCodeEditor::updateColourScheme()
{
getAppSettings().appearance.applyToCodeEditor (editor);
}

+ 88
- 12
extras/Introjucer/Source/Code Editor/jucer_SourceCodeEditor.h View File

@@ -34,24 +34,16 @@ class SourceCodeEditor : public DocumentEditorComponent,
private ValueTree::Listener
{
public:
//==============================================================================
SourceCodeEditor (OpenDocumentManager::Document* document,
CodeDocument& codeDocument,
CodeTokeniser* const codeTokeniser);
CodeDocument& codeDocument);
~SourceCodeEditor();
static SourceCodeEditor* createFor (OpenDocumentManager::Document* document,
CodeDocument& codeDocument);
static CodeTokeniser* getTokeniserFor (const File& file);
private:
ScopedPointer<CodeEditorComponent> editor;
//==============================================================================
CodeEditorComponent* createEditor (CodeDocument&);
void resized();
CodeEditorComponent editor;
private:
void valueTreePropertyChanged (ValueTree&, const Identifier&);
void valueTreeChildAdded (ValueTree&, ValueTree&);
void valueTreeChildRemoved (ValueTree&, ValueTree&);
@@ -65,4 +57,88 @@ private:
};
//==============================================================================
class CppCodeEditorComponent : public CodeEditorComponent
{
public:
CppCodeEditorComponent (CodeDocument& codeDocument)
: CodeEditorComponent (codeDocument, getCppTokeniser())
{
}
void handleReturnKey()
{
CodeEditorComponent::handleReturnKey();
const CodeDocument::Position pos (getCaretPos());
if (pos.getLineNumber() > 0 && pos.getLineText().trim().isEmpty())
{
String indent (getIndentForCurrentBlock (pos));
const String previousLine (pos.movedByLines (-1).getLineText());
const String trimmedPreviousLine (previousLine.trim());
const String leadingWhitespace (getLeadingWhitespace (previousLine));
insertTextAtCaret (leadingWhitespace);
if (trimmedPreviousLine.endsWithChar ('{')
|| ((trimmedPreviousLine.startsWith ("if ")
|| trimmedPreviousLine.startsWith ("for ")
|| trimmedPreviousLine.startsWith ("while "))
&& trimmedPreviousLine.endsWithChar (')')))
insertTabAtCaret();
}
}
void insertTextAtCaret (const String& newText)
{
if (getHighlightedRegion().isEmpty())
{
const CodeDocument::Position pos (getCaretPos());
if ((newText == "{" || newText == "}") && pos.getLineNumber() > 0)
{
moveCaretToStartOfLine (true);
CodeEditorComponent::insertTextAtCaret (getIndentForCurrentBlock (pos));
if (newText == "{")
insertTabAtCaret();
}
}
CodeEditorComponent::insertTextAtCaret (newText);
}
private:
static CPlusPlusCodeTokeniser* getCppTokeniser()
{
static CPlusPlusCodeTokeniser cppTokeniser;
return &cppTokeniser;
}
static String getLeadingWhitespace (String line)
{
line = line.removeCharacters ("\r\n");
const String::CharPointerType endOfLeadingWS (line.getCharPointer().findEndOfWhitespace());
return String (line.getCharPointer(), endOfLeadingWS);
}
static String getIndentForCurrentBlock (CodeDocument::Position pos)
{
while (pos.getLineNumber() > 0)
{
pos = pos.movedByLines (-1);
const String line (pos.getLineText());
const String trimmedLine (line.trimStart());
if (trimmedLine.startsWithChar ('{'))
return getLeadingWhitespace (line);
}
return String::empty;
}
};
#endif // __JUCER_SOURCECODEEDITOR_JUCEHEADER__

+ 6
- 0
modules/juce_gui_basics/commands/juce_ApplicationCommandID.h View File

@@ -77,6 +77,12 @@ namespace StandardApplicationCommandIDs
/** The command ID that should be used to send a "Deselect all" command. */
static const CommandID deselectAll = 0x1007;
/** The command ID that should be used to send a "undo" command. */
static const CommandID undo = 0x1008;
/** The command ID that should be used to send a "redo" command. */
static const CommandID redo = 0x1009;
}


+ 39
- 49
modules/juce_gui_basics/widgets/juce_TextEditor.cpp View File

@@ -1755,12 +1755,51 @@ void TextEditor::paintOverChildren (Graphics& g)
}
//==============================================================================
void TextEditor::addPopupMenuItems (PopupMenu& m, const MouseEvent*)
{
const bool writable = ! isReadOnly();
if (passwordCharacter == 0)
{
m.addItem (StandardApplicationCommandIDs::cut, TRANS("Cut"), writable);
m.addItem (StandardApplicationCommandIDs::copy, TRANS("Copy"), ! selection.isEmpty());
m.addItem (StandardApplicationCommandIDs::paste, TRANS("Paste"), writable);
}
m.addItem (StandardApplicationCommandIDs::del, TRANS("Delete"), writable);
m.addSeparator();
m.addItem (StandardApplicationCommandIDs::selectAll, TRANS("Select All"));
m.addSeparator();
if (getUndoManager() != nullptr)
{
m.addItem (StandardApplicationCommandIDs::undo, TRANS("Undo"), undoManager.canUndo());
m.addItem (StandardApplicationCommandIDs::redo, TRANS("Redo"), undoManager.canRedo());
}
}
void TextEditor::performPopupMenuAction (const int menuItemID)
{
switch (menuItemID)
{
case StandardApplicationCommandIDs::cut: cutToClipboard(); break;
case StandardApplicationCommandIDs::copy: copyToClipboard(); break;
case StandardApplicationCommandIDs::paste: pasteFromClipboard(); break;
case StandardApplicationCommandIDs::del: cut(); break;
case StandardApplicationCommandIDs::selectAll: selectAll(); break;
case StandardApplicationCommandIDs::undo: undo(); break;
case StandardApplicationCommandIDs::redo: redo(); break;
default: break;
}
}
static void textEditorMenuCallback (int menuResult, TextEditor* editor)
{
if (editor != nullptr && menuResult != 0)
editor->performPopupMenuAction (menuResult);
}
//==============================================================================
void TextEditor::mouseDown (const MouseEvent& e)
{
beginDragAutoRepeat (100);
@@ -1788,12 +1827,8 @@ void TextEditor::mouseDown (const MouseEvent& e)
void TextEditor::mouseDrag (const MouseEvent& e)
{
if (wasFocused || ! selectAllTextWhenFocused)
{
if (! (popupMenuEnabled && e.mods.isPopupMenu()))
{
moveCaretTo (getTextIndexAt (e.x, e.y), true);
}
}
}
void TextEditor::mouseUp (const MouseEvent& e)
@@ -1802,12 +1837,8 @@ void TextEditor::mouseUp (const MouseEvent& e)
textHolder->restartTimer();
if (wasFocused || ! selectAllTextWhenFocused)
{
if (e.mouseWasClicked() && ! (popupMenuEnabled && e.mods.isPopupMenu()))
{
moveCaret (getTextIndexAt (e.x, e.y));
}
}
wasFocused = true;
}
@@ -2094,47 +2125,6 @@ bool TextEditor::keyStateChanged (const bool isKeyDown)
return ! ModifierKeys::getCurrentModifiers().isCommandDown();
}
//==============================================================================
const int baseMenuItemID = 0x7fff0000;
void TextEditor::addPopupMenuItems (PopupMenu& m, const MouseEvent*)
{
const bool writable = ! isReadOnly();
if (passwordCharacter == 0)
{
m.addItem (baseMenuItemID + 1, TRANS("cut"), writable);
m.addItem (baseMenuItemID + 2, TRANS("copy"), ! selection.isEmpty());
m.addItem (baseMenuItemID + 3, TRANS("paste"), writable);
}
m.addItem (baseMenuItemID + 4, TRANS("delete"), writable);
m.addSeparator();
m.addItem (baseMenuItemID + 5, TRANS("select all"));
m.addSeparator();
if (getUndoManager() != nullptr)
{
m.addItem (baseMenuItemID + 6, TRANS("undo"), undoManager.canUndo());
m.addItem (baseMenuItemID + 7, TRANS("redo"), undoManager.canRedo());
}
}
void TextEditor::performPopupMenuAction (const int menuItemID)
{
switch (menuItemID)
{
case baseMenuItemID + 1: cutToClipboard(); break;
case baseMenuItemID + 2: copyToClipboard(); break;
case baseMenuItemID + 3: pasteFromClipboard(); break;
case baseMenuItemID + 4: cut(); break;
case baseMenuItemID + 5: selectAll(); break;
case baseMenuItemID + 6: undo(); break;
case baseMenuItemID + 7: redo(); break;
default: break;
}
}
//==============================================================================
void TextEditor::focusGained (FocusChangeType)
{


+ 2
- 3
modules/juce_gui_basics/widgets/juce_TextEditor.h View File

@@ -566,9 +566,8 @@ public:
When the menu has been shown, performPopupMenuAction() will be called to
perform the item that the user has chosen.
The default menu items will be added using item IDs in the range
0x7fff0000 - 0x7fff1000, so you should avoid those values for your own
menu IDs.
The default menu items will be added using item IDs from the
StandardApplicationCommandIDs namespace.
If this was triggered by a mouse-click, the mouseClickEvent parameter will be
a pointer to the info about it, or may be null if the menu is being triggered


+ 224
- 76
modules/juce_gui_extra/code_editor/juce_CodeEditorComponent.cpp View File

@@ -26,8 +26,7 @@
class CodeEditorComponent::CodeEditorLine
{
public:
CodeEditorLine() noexcept
: highlightColumnStart (0), highlightColumnEnd (0)
CodeEditorLine() noexcept : highlightColumnStart (0), highlightColumnEnd (0)
{
}
@@ -196,7 +195,7 @@ private:
for (;;)
{
int tabPos = t.text.indexOfChar ('\t');
const int tabPos = t.text.indexOfChar ('\t');
if (tabPos < 0)
break;
@@ -226,6 +225,26 @@ private:
}
};
namespace CodeEditorHelpers
{
static int findFirstNonWhitespaceChar (const String& line) noexcept
{
String::CharPointerType t (line.getCharPointer());
int i = 0;
while (! t.isEmpty())
{
if (! t.isWhitespace())
return i;
++t;
++i;
}
return 0;
}
}
//==============================================================================
class CodeEditorComponent::GutterComponent : public Component
{
@@ -640,6 +659,11 @@ CodeDocument::Position CodeEditorComponent::getPositionAt (int x, int y)
//==============================================================================
void CodeEditorComponent::insertTextAtCaret (const String& newText)
{
insertText (newText);
}
void CodeEditorComponent::insertText (const String& newText)
{
document.deleteSection (selectionStart, selectionEnd);
@@ -661,17 +685,89 @@ void CodeEditorComponent::insertTabAtCaret()
{
const int caretCol = indexToColumn (caretPos.getLineNumber(), caretPos.getIndexInLine());
const int spacesNeeded = spacesPerTab - (caretCol % spacesPerTab);
insertTextAtCaret (String::repeatedString (" ", spacesNeeded));
insertText (String::repeatedString (" ", spacesNeeded));
}
else
{
insertTextAtCaret ("\t");
insertText ("\t");
}
}
bool CodeEditorComponent::deleteWhitespaceBackwardsToTabStop()
{
if (! getHighlightedRegion().isEmpty())
return false;
for (;;)
{
const int currentColumn = indexToColumn (caretPos.getLineNumber(), caretPos.getIndexInLine());
if (currentColumn <= 0 || (currentColumn % spacesPerTab) == 0)
break;
moveCaretLeft (false, true);
}
const String selected (getTextInRange (getHighlightedRegion()));
if (selected.isNotEmpty() && selected.trim().isEmpty())
{
cut();
return true;
}
return false;
}
void CodeEditorComponent::indentSelection() { indentSelectedLines ( spacesPerTab); }
void CodeEditorComponent::unindentSelection() { indentSelectedLines (-spacesPerTab); }
void CodeEditorComponent::indentSelectedLines (const int spacesToAdd)
{
newTransaction();
CodeDocument::Position oldSelectionStart (selectionStart), oldSelectionEnd (selectionEnd), oldCaret (caretPos);
oldSelectionStart.setPositionMaintained (true);
oldSelectionEnd.setPositionMaintained (true);
oldCaret.setPositionMaintained (true);
const int lineStart = selectionStart.getLineNumber();
int lineEnd = selectionEnd.getLineNumber();
if (lineEnd > lineStart && selectionEnd.getIndexInLine() == 0)
--lineEnd;
for (int line = lineStart; line <= lineEnd; ++line)
{
const String lineText (document.getLine (line));
const int nonWhitespaceStart = CodeEditorHelpers::findFirstNonWhitespaceChar (lineText);
if (nonWhitespaceStart > 0 || lineText.trimStart().isNotEmpty())
{
const CodeDocument::Position wsStart (&document, line, 0);
const CodeDocument::Position wsEnd (&document, line, nonWhitespaceStart);
const int numLeadingSpaces = indexToColumn (line, wsEnd.getIndexInLine());
const int newNumLeadingSpaces = jmax (0, numLeadingSpaces + spacesToAdd);
if (newNumLeadingSpaces != numLeadingSpaces)
{
document.deleteSection (wsStart, wsEnd);
document.insertText (wsStart, String::repeatedString (useSpacesForTabs ? " " : "\t",
useSpacesForTabs ? newNumLeadingSpaces
: (newNumLeadingSpaces / spacesPerTab)));
}
}
}
selectionStart = oldSelectionStart;
selectionEnd = oldSelectionEnd;
caretPos = oldCaret;
}
void CodeEditorComponent::cut()
{
insertTextAtCaret (String::empty);
insertText (String::empty);
}
bool CodeEditorComponent::copyToClipboard()
@@ -700,7 +796,7 @@ bool CodeEditorComponent::pasteFromClipboard()
const String clip (SystemClipboard::getTextFromClipboard());
if (clip.isNotEmpty())
insertTextAtCaret (clip);
insertText (clip);
newTransaction();
return true;
@@ -814,26 +910,6 @@ bool CodeEditorComponent::moveCaretToTop (const bool selecting)
return true;
}
namespace CodeEditorHelpers
{
static int findFirstNonWhitespaceChar (const String& line) noexcept
{
String::CharPointerType t (line.getCharPointer());
int i = 0;
while (! t.isEmpty())
{
if (! t.isWhitespace())
return i;
++t;
++i;
}
return 0;
}
}
bool CodeEditorComponent::moveCaretToStartOfLine (const bool selecting)
{
newTransaction();
@@ -956,21 +1032,28 @@ bool CodeEditorComponent::keyPressed (const KeyPress& key)
{
if (key == KeyPress::tabKey || key.getTextCharacter() == '\t')
{
insertTabAtCaret();
handleTabKey();
}
else if (key == KeyPress::returnKey)
{
newTransaction();
insertTextAtCaret (document.getNewLineCharacters());
handleReturnKey();
}
else if (key.isKeyCode (KeyPress::escapeKey))
{
newTransaction();
handleEscapeKey();
}
else if (key.getTextCharacter() >= ' ')
{
insertTextAtCaret (String::charToString (key.getTextCharacter()));
}
else if (key == KeyPress ('[', ModifierKeys::commandModifier, 0))
{
unindentSelection();
}
else if (key == KeyPress (']', ModifierKeys::commandModifier, 0))
{
indentSelection();
}
else
{
return false;
@@ -980,26 +1063,90 @@ bool CodeEditorComponent::keyPressed (const KeyPress& key)
return true;
}
void CodeEditorComponent::handleReturnKey()
{
insertText (document.getNewLineCharacters());
}
void CodeEditorComponent::handleTabKey()
{
insertTabAtCaret();
}
void CodeEditorComponent::handleEscapeKey()
{
newTransaction();
}
//==============================================================================
void CodeEditorComponent::addPopupMenuItems (PopupMenu& m, const MouseEvent*)
{
m.addItem (StandardApplicationCommandIDs::cut, TRANS("Cut"));
m.addItem (StandardApplicationCommandIDs::copy, TRANS("Copy"), ! getHighlightedRegion().isEmpty());
m.addItem (StandardApplicationCommandIDs::paste, TRANS("Paste"));
m.addItem (StandardApplicationCommandIDs::del, TRANS("Delete"));
m.addSeparator();
m.addItem (StandardApplicationCommandIDs::selectAll, TRANS("Select All"));
m.addSeparator();
m.addItem (StandardApplicationCommandIDs::undo, TRANS("Undo"), document.getUndoManager().canUndo());
m.addItem (StandardApplicationCommandIDs::redo, TRANS("Redo"), document.getUndoManager().canRedo());
}
void CodeEditorComponent::performPopupMenuAction (const int menuItemID)
{
switch (menuItemID)
{
case StandardApplicationCommandIDs::cut: cutToClipboard(); break;
case StandardApplicationCommandIDs::copy: copyToClipboard(); break;
case StandardApplicationCommandIDs::paste: pasteFromClipboard(); break;
case StandardApplicationCommandIDs::del: cut(); break;
case StandardApplicationCommandIDs::selectAll: selectAll(); break;
case StandardApplicationCommandIDs::undo: undo(); break;
case StandardApplicationCommandIDs::redo: redo(); break;
default: break;
}
}
static void codeEditorMenuCallback (int menuResult, CodeEditorComponent* editor)
{
if (editor != nullptr && menuResult != 0)
editor->performPopupMenuAction (menuResult);
}
//==============================================================================
void CodeEditorComponent::mouseDown (const MouseEvent& e)
{
newTransaction();
dragType = notDragging;
if (! e.mods.isPopupMenu())
if (e.mods.isPopupMenu())
{
beginDragAutoRepeat (100);
moveCaretTo (getPositionAt (e.x, e.y), e.mods.isShiftDown());
if (getHighlightedRegion().isEmpty())
{
const CodeDocument::Position pos (getPositionAt (e.x, e.y));
const String line (pos.getLineText());
const int index = pos.getIndexInLine();
const int lineLen = line.length();
if (index > 0 && index < lineLen - 2)
{
moveCaretTo (pos, false);
moveCaretLeft (true, false);
moveCaretRight (true, true);
}
}
PopupMenu m;
m.setLookAndFeel (&getLookAndFeel());
addPopupMenuItems (m, &e);
m.showMenuAsync (PopupMenu::Options(),
ModalCallbackFunction::forComponent (codeEditorMenuCallback, this));
}
else
{
/*PopupMenu m;
addPopupMenuItems (m, &e);
const int result = m.show();
if (result != 0)
performPopupMenuAction (result);
*/
beginDragAutoRepeat (100);
moveCaretTo (getPositionAt (e.x, e.y), e.mods.isShiftDown());
}
}
@@ -1147,6 +1294,7 @@ void CodeEditorComponent::ColourScheme::set (const String& name, const Colour& c
for (int i = 0; i < types.size(); ++i)
{
TokenType& tt = types.getReference(i);
if (tt.name == name)
{
tt.colour = colour;
@@ -1191,57 +1339,57 @@ void CodeEditorComponent::updateCachedIterators (int maxLineNum)
if (cachedIterators.size() == 0)
cachedIterators.add (new CodeDocument::Iterator (&document));
if (codeTokeniser == nullptr)
return;
for (;;)
if (codeTokeniser != nullptr)
{
CodeDocument::Iterator* last = cachedIterators.getLast();
if (last->getLine() >= maxLineNum)
break;
CodeDocument::Iterator* t = new CodeDocument::Iterator (*last);
cachedIterators.add (t);
const int targetLine = last->getLine() + linesBetweenCachedSources;
for (;;)
{
codeTokeniser->readNextToken (*t);
CodeDocument::Iterator* const last = cachedIterators.getLast();
if (t->getLine() >= targetLine)
if (last->getLine() >= maxLineNum)
break;
if (t->isEOF())
return;
CodeDocument::Iterator* t = new CodeDocument::Iterator (*last);
cachedIterators.add (t);
const int targetLine = last->getLine() + linesBetweenCachedSources;
for (;;)
{
codeTokeniser->readNextToken (*t);
if (t->getLine() >= targetLine)
break;
if (t->isEOF())
return;
}
}
}
}
void CodeEditorComponent::getIteratorForPosition (int position, CodeDocument::Iterator& source)
{
if (codeTokeniser == nullptr)
return;
for (int i = cachedIterators.size(); --i >= 0;)
if (codeTokeniser != nullptr)
{
CodeDocument::Iterator* t = cachedIterators.getUnchecked (i);
if (t->getPosition() <= position)
for (int i = cachedIterators.size(); --i >= 0;)
{
source = *t;
break;
CodeDocument::Iterator* t = cachedIterators.getUnchecked (i);
if (t->getPosition() <= position)
{
source = *t;
break;
}
}
}
while (source.getPosition() < position)
{
const CodeDocument::Iterator original (source);
codeTokeniser->readNextToken (source);
if (source.getPosition() > position || source.isEOF())
while (source.getPosition() < position)
{
source = original;
break;
const CodeDocument::Iterator original (source);
codeTokeniser->readNextToken (source);
if (source.getPosition() > position || source.isEOF())
{
source = original;
break;
}
}
}
}

+ 61
- 15
modules/juce_gui_extra/code_editor/juce_CodeEditorComponent.h View File

@@ -38,16 +38,16 @@ class CodeTokeniser;
*/
class JUCE_API CodeEditorComponent : public Component,
public TextInputTarget,
public Timer,
public ScrollBar::Listener,
public CodeDocument::Listener,
public AsyncUpdater
private Timer,
private ScrollBar::Listener,
private CodeDocument::Listener,
private AsyncUpdater
{
public:
//==============================================================================
/** Creates an editor for a document.
The tokeniser object is optional - pass 0 to disable syntax highlighting.
The tokeniser object is optional - pass nullptr to disable syntax highlighting.
The object that you pass in is not owned or deleted by the editor - you must
make sure that it doesn't get deleted while this component is still using it.
@@ -128,6 +128,7 @@ public:
bool moveCaretToEndOfLine (bool selecting);
bool deleteBackwards (bool moveInWholeWordSteps);
bool deleteForwards (bool moveInWholeWordSteps);
bool deleteWhitespaceBackwardsToTabStop();
bool copyToClipboard();
bool cutToClipboard();
bool pasteFromClipboard();
@@ -145,6 +146,9 @@ public:
void insertTextAtCaret (const String& textToInsert);
void insertTabAtCaret();
void indentSelection();
void unindentSelection();
//==============================================================================
Range<int> getHighlightedRegion() const;
void setHighlightedRegion (const Range<int>& newRange);
@@ -230,11 +234,53 @@ public:
int getScrollbarThickness() const noexcept { return scrollbarThickness; }
//==============================================================================
/** @internal */
void resized();
/** Called when the return key is pressed - this can be overridden for custom behaviour. */
virtual void handleReturnKey();
/** Called when the tab key is pressed - this can be overridden for custom behaviour. */
virtual void handleTabKey();
/** Called when the escape key is pressed - this can be overridden for custom behaviour. */
virtual void handleEscapeKey();
//==============================================================================
/** This adds the items to the popup menu.
By default it adds the cut/copy/paste items, but you can override this if
you need to replace these with your own items.
If you want to add your own items to the existing ones, you can override this,
call the base class's addPopupMenuItems() method, then append your own items.
When the menu has been shown, performPopupMenuAction() will be called to
perform the item that the user has chosen.
If this was triggered by a mouse-click, the mouseClickEvent parameter will be
a pointer to the info about it, or may be null if the menu is being triggered
by some other means.
@see performPopupMenuAction, setPopupMenuEnabled, isPopupMenuEnabled
*/
virtual void addPopupMenuItems (PopupMenu& menuToAddTo,
const MouseEvent* mouseClickEvent);
/** This is called to perform one of the items that was shown on the popup menu.
If you've overridden addPopupMenuItems(), you should also override this
to perform the actions that you've added.
If you've overridden addPopupMenuItems() but have still left the default items
on the menu, remember to call the superclass's performPopupMenuAction()
so that it can perform the default actions if that's what the user clicked on.
@see addPopupMenuItems, setPopupMenuEnabled, isPopupMenuEnabled
*/
virtual void performPopupMenuAction (int menuItemID);
//==============================================================================
/** @internal */
void paint (Graphics&);
/** @internal */
void resized();
/** @internal */
bool keyPressed (const KeyPress&);
/** @internal */
void mouseDown (const MouseEvent&);
@@ -251,14 +297,6 @@ public:
/** @internal */
void focusLost (FocusChangeType);
/** @internal */
void timerCallback();
/** @internal */
void scrollBarMoved (ScrollBar*, double);
/** @internal */
void handleAsyncUpdate();
/** @internal */
void codeDocumentChanged (const CodeDocument::Position&, const CodeDocument::Position&);
/** @internal */
bool isTextInputActive() const;
/** @internal */
void setTemporaryUnderlining (const Array <Range<int> >&);
@@ -307,16 +345,24 @@ private:
void clearCachedIterators (int firstLineToBeInvalid);
void updateCachedIterators (int maxLineNum);
void getIteratorForPosition (int position, CodeDocument::Iterator& result);
void timerCallback();
void scrollBarMoved (ScrollBar*, double);
void handleAsyncUpdate();
void codeDocumentChanged (const CodeDocument::Position&, const CodeDocument::Position&);
void moveLineDelta (int delta, bool selecting);
int getGutterSize() const noexcept;
//==============================================================================
void insertText (const String& textToInsert);
void updateCaretPosition();
void updateScrollBars();
void scrollToLineInternal (int line);
void scrollToColumnInternal (double column);
void newTransaction();
void cut();
void indentSelectedLines (int spacesToAdd);
int indexToColumn (int line, int index) const noexcept;
int columnToIndex (int line, int column) const noexcept;


Loading…
Cancel
Save