mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-20 01:14:20 +00:00
Added line-numbering to the CodeEditorComponent.
This commit is contained in:
parent
f824e99f2d
commit
d3bb7fb9cc
7 changed files with 155 additions and 32 deletions
|
|
@ -32,6 +32,8 @@ AppearanceSettings::AppearanceSettings (const CodeEditorComponent& editor)
|
|||
: settings ("COLOUR_SCHEME")
|
||||
{
|
||||
getColourValue ("Background") = editor.findColour (CodeEditorComponent::backgroundColourId).toString();
|
||||
getColourValue ("Line Number Bkgd") = editor.findColour (CodeEditorComponent::lineNumberBackgroundId).toString();
|
||||
getColourValue ("Line Numbers") = editor.findColour (CodeEditorComponent::lineNumberTextId).toString();
|
||||
getColourValue ("Plain Text") = editor.findColour (CodeEditorComponent::defaultTextColourId).toString();
|
||||
getColourValue ("Selected Background") = editor.findColour (CodeEditorComponent::highlightColourId).toString();
|
||||
|
||||
|
|
@ -48,19 +50,32 @@ AppearanceSettings::AppearanceSettings (const CodeEditorComponent& editor)
|
|||
getCodeFontValue() = f.toString();
|
||||
}
|
||||
|
||||
bool AppearanceSettings::readFromFile (const File& file)
|
||||
bool AppearanceSettings::readFromXML (const XmlElement& xml)
|
||||
{
|
||||
const ScopedPointer<XmlElement> xml (XmlDocument::parse (file));
|
||||
|
||||
if (xml != nullptr && xml->hasTagName (settings.getType().toString()))
|
||||
if (xml.hasTagName (settings.getType().toString()))
|
||||
{
|
||||
settings = ValueTree::fromXml (*xml);
|
||||
ValueTree newSettings (ValueTree::fromXml (xml));
|
||||
|
||||
for (int i = settings.getNumChildren(); --i >= 0;)
|
||||
{
|
||||
const ValueTree c (settings.getChild (i));
|
||||
if (! newSettings.getChildWithProperty (Ids::name, c.getProperty (Ids::name)).isValid())
|
||||
newSettings.addChild (c.createCopy(), 0, nullptr);
|
||||
}
|
||||
|
||||
settings = newSettings;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AppearanceSettings::readFromFile (const File& file)
|
||||
{
|
||||
const ScopedPointer<XmlElement> xml (XmlDocument::parse (file));
|
||||
return xml != nullptr && readFromXML (*xml);
|
||||
}
|
||||
|
||||
bool AppearanceSettings::writeToFile (const File& file) const
|
||||
{
|
||||
const ScopedPointer<XmlElement> xml (settings.createXml());
|
||||
|
|
@ -97,6 +112,8 @@ void AppearanceSettings::applyToCodeEditor (CodeEditorComponent& editor) const
|
|||
Colour col;
|
||||
if (getColour ("Plain Text", col)) editor.setColour (CodeEditorComponent::defaultTextColourId, col);
|
||||
if (getColour ("Selected Background", col)) editor.setColour (CodeEditorComponent::highlightColourId, col);
|
||||
if (getColour ("Line Number Bkgd", col)) editor.setColour (CodeEditorComponent::lineNumberBackgroundId, col);
|
||||
if (getColour ("Line Numbers", col)) editor.setColour (CodeEditorComponent::lineNumberTextId, col);
|
||||
|
||||
if (getColour ("Background", col))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ public:
|
|||
AppearanceSettings (const CodeEditorComponent& editorToCopyFrom);
|
||||
|
||||
bool readFromFile (const File& file);
|
||||
bool readFromXML (const XmlElement&);
|
||||
bool writeToFile (const File& file) const;
|
||||
|
||||
void applyToCodeEditor (CodeEditorComponent& editor) const;
|
||||
|
|
|
|||
|
|
@ -130,7 +130,7 @@ void StoredSettings::reload()
|
|||
|
||||
const ScopedPointer<XmlElement> xml (props->getXmlValue ("editorColours"));
|
||||
if (xml != nullptr)
|
||||
appearance.settings = ValueTree::fromXml (*xml);
|
||||
appearance.readFromXML (*xml);
|
||||
|
||||
loadSwatchColours();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -239,6 +239,8 @@ LookAndFeel::LookAndFeel()
|
|||
0x1004500, /*CodeEditorComponent::backgroundColourId*/ 0xffffffff,
|
||||
0x1004502, /*CodeEditorComponent::highlightColourId*/ textHighlightColour,
|
||||
0x1004503, /*CodeEditorComponent::defaultTextColourId*/ 0xff000000,
|
||||
0x1004504, /*CodeEditorComponent::lineNumberBackgroundId*/ 0x44999999,
|
||||
0x1004505, /*CodeEditorComponent::lineNumberTextId*/ 0x44000000,
|
||||
|
||||
0x1007000, /*ColourSelector::backgroundColourId*/ 0xffe5e5e5,
|
||||
0x1007001, /*ColourSelector::labelTextColourId*/ 0xff000000,
|
||||
|
|
|
|||
|
|
@ -333,6 +333,46 @@ namespace CppTokeniser
|
|||
}
|
||||
}
|
||||
|
||||
static void skipPreprocessorLine (CodeDocument::Iterator& source) noexcept
|
||||
{
|
||||
bool lastWasBackslash = false;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
const juce_wchar c = source.peekNextChar();
|
||||
|
||||
if (c == '"')
|
||||
{
|
||||
skipQuotedString (source);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c == '/')
|
||||
{
|
||||
const juce_wchar c2 = source.peekNextChar();
|
||||
|
||||
if (c2 == '/' || c2 == '*')
|
||||
return;
|
||||
}
|
||||
|
||||
if (c == 0)
|
||||
break;
|
||||
|
||||
if (c == '\n' || c == '\r')
|
||||
{
|
||||
source.skipToEndOfLine();
|
||||
|
||||
if (lastWasBackslash)
|
||||
skipPreprocessorLine (source);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
lastWasBackslash = (c == '\\');
|
||||
source.skip();
|
||||
}
|
||||
}
|
||||
|
||||
static void skipIfNextCharMatches (CodeDocument::Iterator& source, const juce_wchar c) noexcept
|
||||
{
|
||||
if (source.peekNextChar() == c)
|
||||
|
|
@ -478,7 +518,7 @@ int CPlusPlusCodeTokeniser::readNextToken (CodeDocument::Iterator& source)
|
|||
|
||||
case '#':
|
||||
result = tokenType_preprocessor;
|
||||
source.skipToEndOfLine();
|
||||
skipPreprocessorLine (source);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -226,12 +226,44 @@ private:
|
|||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class CodeEditorComponent::GutterComponent : public Component
|
||||
{
|
||||
public:
|
||||
GutterComponent() {}
|
||||
|
||||
void paint (Graphics& g)
|
||||
{
|
||||
jassert (dynamic_cast <CodeEditorComponent*> (getParentComponent()) != nullptr);
|
||||
const CodeEditorComponent& editor = *static_cast <CodeEditorComponent*> (getParentComponent());
|
||||
|
||||
g.fillAll (editor.findColour (lineNumberBackgroundId));
|
||||
|
||||
const Rectangle<int> clip (g.getClipBounds());
|
||||
const int lineHeight = editor.lineHeight;
|
||||
const int firstLineToDraw = jmax (0, clip.getY() / lineHeight);
|
||||
const int lastLineToDraw = jmin (editor.lines.size(), clip.getBottom() / lineHeight + 1);
|
||||
|
||||
const Font lineNumberFont (editor.getFont().withHeight (lineHeight * 0.8f));
|
||||
const float y = (lineHeight - lineNumberFont.getHeight()) / 2.0f + lineNumberFont.getAscent();
|
||||
const float w = getWidth() - 2.0f;
|
||||
|
||||
GlyphArrangement ga;
|
||||
for (int i = firstLineToDraw; i < lastLineToDraw; ++i)
|
||||
ga.addJustifiedText (lineNumberFont, String (editor.firstLineOnScreen + i + 1),
|
||||
0.0f, y + (lineHeight * i), w, Justification::centredRight);
|
||||
|
||||
g.setColour (editor.findColour (lineNumberTextId));
|
||||
ga.draw (g);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
CodeEditorComponent::CodeEditorComponent (CodeDocument& document_,
|
||||
CodeTokeniser* const codeTokeniser_)
|
||||
: document (document_),
|
||||
firstLineOnScreen (0),
|
||||
gutter (5),
|
||||
spacesPerTab (4),
|
||||
lineHeight (0),
|
||||
linesOnScreen (0),
|
||||
|
|
@ -239,6 +271,7 @@ CodeEditorComponent::CodeEditorComponent (CodeDocument& document_,
|
|||
scrollbarThickness (16),
|
||||
columnToTryToMaintain (-1),
|
||||
useSpacesForTabs (false),
|
||||
showLineNumbers (false),
|
||||
xOffset (0),
|
||||
verticalScrollBar (true),
|
||||
horizontalScrollBar (false),
|
||||
|
|
@ -272,6 +305,8 @@ CodeEditorComponent::CodeEditorComponent (CodeDocument& document_,
|
|||
if (codeTokeniser != nullptr)
|
||||
setColourScheme (codeTokeniser->getDefaultColourScheme());
|
||||
|
||||
setLineNumbersShown (true);
|
||||
|
||||
verticalScrollBar.addListener (this);
|
||||
horizontalScrollBar.addListener (this);
|
||||
document.addListener (this);
|
||||
|
|
@ -282,6 +317,11 @@ CodeEditorComponent::~CodeEditorComponent()
|
|||
document.removeListener (this);
|
||||
}
|
||||
|
||||
int CodeEditorComponent::getGutterSize() const noexcept
|
||||
{
|
||||
return showLineNumbers ? 35 : 5;
|
||||
}
|
||||
|
||||
void CodeEditorComponent::loadContent (const String& newContent)
|
||||
{
|
||||
clearCachedIterators (0);
|
||||
|
|
@ -309,6 +349,20 @@ Rectangle<int> CodeEditorComponent::getCaretRectangle()
|
|||
return getLocalArea (caret, caret->getLocalBounds());
|
||||
}
|
||||
|
||||
void CodeEditorComponent::setLineNumbersShown (const bool shouldBeShown)
|
||||
{
|
||||
if (showLineNumbers != shouldBeShown)
|
||||
{
|
||||
showLineNumbers = shouldBeShown;
|
||||
gutter = nullptr;
|
||||
|
||||
if (shouldBeShown)
|
||||
addAndMakeVisible (gutter = new GutterComponent(), 0);
|
||||
|
||||
resized();
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void CodeEditorComponent::codeDocumentChanged (const CodeDocument::Position& affectedTextStart,
|
||||
const CodeDocument::Position& affectedTextEnd)
|
||||
|
|
@ -339,8 +393,14 @@ void CodeEditorComponent::resized()
|
|||
rebuildLineTokens();
|
||||
updateCaretPosition();
|
||||
|
||||
verticalScrollBar.setBounds (getWidth() - scrollbarThickness, 0, scrollbarThickness, getHeight() - scrollbarThickness);
|
||||
horizontalScrollBar.setBounds (gutter, getHeight() - scrollbarThickness, getWidth() - scrollbarThickness - gutter, scrollbarThickness);
|
||||
if (gutter != nullptr)
|
||||
gutter->setBounds (0, 0, getGutterSize() - 2, getHeight());
|
||||
|
||||
verticalScrollBar.setBounds (getWidth() - scrollbarThickness, 0,
|
||||
scrollbarThickness, getHeight() - scrollbarThickness);
|
||||
|
||||
horizontalScrollBar.setBounds (getGutterSize(), getHeight() - scrollbarThickness,
|
||||
getWidth() - scrollbarThickness - getGutterSize(), scrollbarThickness);
|
||||
updateScrollBars();
|
||||
}
|
||||
|
||||
|
|
@ -350,24 +410,22 @@ void CodeEditorComponent::paint (Graphics& g)
|
|||
|
||||
g.fillAll (findColour (CodeEditorComponent::backgroundColourId));
|
||||
|
||||
const int gutter = getGutterSize();
|
||||
g.reduceClipRegion (gutter, 0, verticalScrollBar.getX() - gutter, horizontalScrollBar.getY());
|
||||
|
||||
g.setFont (font);
|
||||
const int baselineOffset = (int) font.getAscent();
|
||||
const Colour defaultColour (findColour (CodeEditorComponent::defaultTextColourId));
|
||||
const Colour highlightColour (findColour (CodeEditorComponent::highlightColourId));
|
||||
|
||||
const Rectangle<int> clip (g.getClipBounds());
|
||||
const int firstLineToDraw = jmax (0, clip.getY() / lineHeight);
|
||||
const int lastLineToDraw = jmin (lines.size(), clip.getBottom() / lineHeight + 1);
|
||||
|
||||
for (int j = firstLineToDraw; j < lastLineToDraw; ++j)
|
||||
{
|
||||
lines.getUnchecked(j)->draw (*this, g, font,
|
||||
for (int i = firstLineToDraw; i < lastLineToDraw; ++i)
|
||||
lines.getUnchecked(i)->draw (*this, g, font,
|
||||
(float) (gutter - xOffset * charWidth),
|
||||
lineHeight * j, baselineOffset, lineHeight,
|
||||
lineHeight * i, baselineOffset, lineHeight,
|
||||
highlightColour);
|
||||
}
|
||||
}
|
||||
|
||||
void CodeEditorComponent::setScrollbarThickness (const int thickness)
|
||||
|
|
@ -422,11 +480,8 @@ void CodeEditorComponent::rebuildLineTokens()
|
|||
}
|
||||
|
||||
if (minLineToRepaint <= maxLineToRepaint)
|
||||
{
|
||||
repaint (gutter, lineHeight * minLineToRepaint - 1,
|
||||
verticalScrollBar.getX() - gutter,
|
||||
lineHeight * (1 + maxLineToRepaint - minLineToRepaint) + 2);
|
||||
}
|
||||
repaint (0, lineHeight * minLineToRepaint - 1,
|
||||
verticalScrollBar.getX(), lineHeight * (1 + maxLineToRepaint - minLineToRepaint) + 2);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
|
@ -568,7 +623,7 @@ void CodeEditorComponent::scrollToKeepCaretOnScreen()
|
|||
|
||||
Rectangle<int> CodeEditorComponent::getCharacterBounds (const CodeDocument::Position& pos) const
|
||||
{
|
||||
return Rectangle<int> (roundToInt ((gutter - xOffset * charWidth) + indexToColumn (pos.getLineNumber(), pos.getIndexInLine()) * charWidth),
|
||||
return Rectangle<int> (roundToInt ((getGutterSize() - xOffset * charWidth) + indexToColumn (pos.getLineNumber(), pos.getIndexInLine()) * charWidth),
|
||||
(pos.getLineNumber() - firstLineOnScreen) * lineHeight,
|
||||
roundToInt (charWidth),
|
||||
lineHeight);
|
||||
|
|
@ -577,7 +632,7 @@ Rectangle<int> CodeEditorComponent::getCharacterBounds (const CodeDocument::Posi
|
|||
CodeDocument::Position CodeEditorComponent::getPositionAt (int x, int y)
|
||||
{
|
||||
const int line = y / lineHeight + firstLineOnScreen;
|
||||
const int column = roundToInt ((x - (gutter - xOffset * charWidth)) / charWidth);
|
||||
const int column = roundToInt ((x - (getGutterSize() - xOffset * charWidth)) / charWidth);
|
||||
const int index = columnToIndex (line, column);
|
||||
|
||||
return CodeDocument::Position (&document, line, index);
|
||||
|
|
|
|||
|
|
@ -110,6 +110,9 @@ public:
|
|||
*/
|
||||
CodeDocument::Position getPositionAt (int x, int y);
|
||||
|
||||
/** Enables or disables the line-number display in the gutter. */
|
||||
void setLineNumbersShown (bool shouldBeShown);
|
||||
|
||||
//==============================================================================
|
||||
bool moveCaretLeft (bool moveInWholeWordSteps, bool selecting);
|
||||
bool moveCaretRight (bool moveInWholeWordSteps, bool selecting);
|
||||
|
|
@ -213,10 +216,10 @@ public:
|
|||
enum ColourIds
|
||||
{
|
||||
backgroundColourId = 0x1004500, /**< A colour to use to fill the editor's background. */
|
||||
highlightColourId = 0x1004502, /**< The colour to use for the highlighted background under
|
||||
selected text. */
|
||||
defaultTextColourId = 0x1004503 /**< The colour to use for text when no syntax colouring is
|
||||
enabled. */
|
||||
highlightColourId = 0x1004502, /**< The colour to use for the highlighted background under selected text. */
|
||||
defaultTextColourId = 0x1004503, /**< The colour to use for text when no syntax colouring is enabled. */
|
||||
lineNumberBackgroundId = 0x1004504, /**< The colour to use for filling the background of the line-number gutter. */
|
||||
lineNumberTextId = 0x1004505, /**< The colour to use for drawing the line numbers. */
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
|
|
@ -250,12 +253,11 @@ public:
|
|||
/** @internal */
|
||||
void timerCallback();
|
||||
/** @internal */
|
||||
void scrollBarMoved (ScrollBar*, double newRangeStart);
|
||||
void scrollBarMoved (ScrollBar*, double);
|
||||
/** @internal */
|
||||
void handleAsyncUpdate();
|
||||
/** @internal */
|
||||
void codeDocumentChanged (const CodeDocument::Position& affectedTextStart,
|
||||
const CodeDocument::Position& affectedTextEnd);
|
||||
void codeDocumentChanged (const CodeDocument::Position&, const CodeDocument::Position&);
|
||||
/** @internal */
|
||||
bool isTextInputActive() const;
|
||||
/** @internal */
|
||||
|
|
@ -266,11 +268,11 @@ private:
|
|||
CodeDocument& document;
|
||||
|
||||
Font font;
|
||||
int firstLineOnScreen, gutter, spacesPerTab;
|
||||
int firstLineOnScreen, spacesPerTab;
|
||||
float charWidth;
|
||||
int lineHeight, linesOnScreen, columnsOnScreen;
|
||||
int scrollbarThickness, columnToTryToMaintain;
|
||||
bool useSpacesForTabs;
|
||||
bool useSpacesForTabs, showLineNumbers;
|
||||
double xOffset;
|
||||
|
||||
CodeDocument::Position caretPos;
|
||||
|
|
@ -279,6 +281,11 @@ private:
|
|||
ScopedPointer<CaretComponent> caret;
|
||||
ScrollBar verticalScrollBar, horizontalScrollBar;
|
||||
|
||||
class GutterComponent;
|
||||
friend class GutterComponent;
|
||||
friend class ScopedPointer<GutterComponent>;
|
||||
ScopedPointer<GutterComponent> gutter;
|
||||
|
||||
enum DragType
|
||||
{
|
||||
notDragging,
|
||||
|
|
@ -301,6 +308,7 @@ private:
|
|||
void updateCachedIterators (int maxLineNum);
|
||||
void getIteratorForPosition (int position, CodeDocument::Iterator& result);
|
||||
void moveLineDelta (int delta, bool selecting);
|
||||
int getGutterSize() const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
void updateCaretPosition();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue