mirror of
https://github.com/SpartanJ/eepp.git
synced 2026-05-28 17:16:29 +03:00
More improvements to the UICodeEditor and TextDocument.
Improved the SyntaxHighligther.
This commit is contained in:
@@ -614,6 +614,8 @@ Probably deprecate the Maps module, since I will focus my efforts on the UI syst
|
||||
|
||||
* Jason Perkins for [premake](https://premake.github.io/)
|
||||
|
||||
* Daniel Bahr for [CRC++](https://github.com/d-bahr/CRCpp)
|
||||
|
||||
* Martín Lucas Golini ( me ) and all the several contributors for [SOIL2](https://github.com/SpartanJ/SOIL2) and [efsw](https://github.com/SpartanJ/efsw)
|
||||
|
||||
* The Xiph open source community for [libogg](https://xiph.org/ogg/) and [libvorbis](https://xiph.org/vorbis/)
|
||||
|
||||
@@ -449,7 +449,7 @@ class EE_API String {
|
||||
** @param index Index of the character to get
|
||||
** @return Character at position \a index
|
||||
**/
|
||||
StringBaseType operator[]( std::size_t index ) const;
|
||||
const StringBaseType& operator[]( std::size_t index ) const;
|
||||
|
||||
/** @brief Overload of [] operator to access a character by its position
|
||||
** This function provides read and write access to characters.
|
||||
@@ -465,7 +465,7 @@ class EE_API String {
|
||||
*actual position in the string.
|
||||
** @return The character at position pos in the string.
|
||||
*/
|
||||
StringBaseType at( std::size_t index ) const;
|
||||
const StringBaseType& at( std::size_t index ) const;
|
||||
|
||||
/** @brief clear the string
|
||||
** This function removes all the characters from the string.
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace EE { namespace UI { namespace Doc {
|
||||
|
||||
struct TokenizedLine {
|
||||
int initState;
|
||||
String text;
|
||||
Uint32 hash;
|
||||
std::vector<SyntaxToken> tokens;
|
||||
int state;
|
||||
};
|
||||
@@ -20,11 +20,21 @@ class EE_API SyntaxHighlighter {
|
||||
|
||||
void reset();
|
||||
|
||||
void invalidate( Int64 lineIndex );
|
||||
|
||||
const std::vector<SyntaxToken>& getLine( const size_t& index );
|
||||
|
||||
Int64 getFirstInvalidLine() const;
|
||||
|
||||
Int64 getMaxWantedLine() const;
|
||||
|
||||
bool updateDirty( int visibleLinesCount = 40 );
|
||||
|
||||
protected:
|
||||
TextDocument* mDoc;
|
||||
std::map<size_t, TokenizedLine> mLines;
|
||||
Int64 mFirstInvalidLine;
|
||||
Int64 mMaxWantedLine;
|
||||
TokenizedLine tokenizeLine( const size_t& line, const int& state );
|
||||
};
|
||||
|
||||
|
||||
@@ -16,6 +16,63 @@ using namespace EE::System;
|
||||
|
||||
namespace EE { namespace UI { namespace Doc {
|
||||
|
||||
class EE_API TextDocumentLine {
|
||||
public:
|
||||
TextDocumentLine( const String& text ) : mText( text ) { updateHash(); }
|
||||
|
||||
void setText( const String& text ) {
|
||||
mText = text;
|
||||
updateHash();
|
||||
}
|
||||
|
||||
const String& getText() const { return mText; }
|
||||
|
||||
void operator=( const std::string& right ) { setText( right ); }
|
||||
|
||||
String::StringBaseType operator[]( std::size_t index ) const { return mText[index]; }
|
||||
|
||||
void insertChar( const unsigned int& pos, const String::StringBaseType& tchar ) {
|
||||
mText.insert( mText.begin() + pos, tchar );
|
||||
updateHash();
|
||||
}
|
||||
|
||||
void append( const String& text ) {
|
||||
mText.append( text );
|
||||
updateHash();
|
||||
}
|
||||
|
||||
void append( const String::StringBaseType& code ) {
|
||||
mText.append( code );
|
||||
updateHash();
|
||||
}
|
||||
|
||||
String substr( std::size_t pos = 0, std::size_t n = String::StringType::npos ) const {
|
||||
return mText.substr( pos, n );
|
||||
}
|
||||
|
||||
String::Iterator insert( String::Iterator p, const String::StringBaseType& c ) {
|
||||
auto it = mText.insert( p, c );
|
||||
updateHash();
|
||||
return it;
|
||||
}
|
||||
|
||||
bool empty() const { return mText.empty(); }
|
||||
|
||||
size_t size() const { return mText.size(); }
|
||||
|
||||
size_t length() const { return mText.length(); }
|
||||
|
||||
const Uint32& getHash() const { return mHash; }
|
||||
|
||||
std::string toUtf8() const { return mText.toUtf8(); }
|
||||
|
||||
protected:
|
||||
String mText;
|
||||
Uint32 mHash;
|
||||
|
||||
void updateHash() { mHash = mText.getHash(); }
|
||||
};
|
||||
|
||||
class EE_API TextDocument {
|
||||
public:
|
||||
class EE_API Client {
|
||||
@@ -26,6 +83,7 @@ class EE_API TextDocument {
|
||||
virtual void onDocumentSelectionChange( const TextRange& ) = 0;
|
||||
virtual void onDocumentLineCountChange( const size_t& lastCount,
|
||||
const size_t& newCount ) = 0;
|
||||
virtual void onDocumentLineChanged( const Int64& lineIndex ) = 0;
|
||||
};
|
||||
|
||||
enum IndentType { IndentSpaces, IndentTabs };
|
||||
@@ -56,13 +114,13 @@ class EE_API TextDocument {
|
||||
|
||||
const TextRange& getSelection() const;
|
||||
|
||||
String& line( const size_t& index );
|
||||
TextDocumentLine& line( const size_t& index );
|
||||
|
||||
const String& line( const size_t& index ) const;
|
||||
const TextDocumentLine& line( const size_t& index ) const;
|
||||
|
||||
size_t linesCount() const;
|
||||
|
||||
std::vector<String>& lines();
|
||||
std::vector<TextDocumentLine>& lines();
|
||||
|
||||
bool hasSelection() const;
|
||||
|
||||
@@ -219,7 +277,7 @@ class EE_API TextDocument {
|
||||
friend class UndoStack;
|
||||
UndoStack mUndoStack;
|
||||
std::string mFilePath;
|
||||
std::vector<String> mLines;
|
||||
std::vector<TextDocumentLine> mLines;
|
||||
TextRange mSelection;
|
||||
std::unordered_set<Client*> mClients;
|
||||
bool mIsCLRF{false};
|
||||
@@ -241,9 +299,11 @@ class EE_API TextDocument {
|
||||
|
||||
void notifyLineCountChanged( const size_t& lastCount, const size_t& newCount );
|
||||
|
||||
void insertAtStartOfSelectedLines( String text, bool skipEmpty );
|
||||
void notifyLineChanged( const Int64& lineIndex );
|
||||
|
||||
void removeFromStartOfSelectedLines( String text, bool skipEmpty );
|
||||
void insertAtStartOfSelectedLines( const String& text, bool skipEmpty );
|
||||
|
||||
void removeFromStartOfSelectedLines( const String& text, bool skipEmpty );
|
||||
|
||||
void remove( TextRange range, UndoStackContainer& undoStack, const Time& time );
|
||||
|
||||
|
||||
@@ -112,6 +112,10 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client {
|
||||
|
||||
virtual Float getColXOffset( TextPosition position );
|
||||
|
||||
const bool& isLocked() const;
|
||||
|
||||
void setLocked( bool locked );
|
||||
|
||||
protected:
|
||||
struct LastXOffset {
|
||||
TextPosition position;
|
||||
@@ -126,6 +130,7 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client {
|
||||
bool mCursorVisible;
|
||||
bool mMouseDown;
|
||||
bool mShowLineNumber;
|
||||
bool mLocked;
|
||||
Uint32 mTabWidth;
|
||||
Int64 mLastColOffset;
|
||||
Vector2f mScroll;
|
||||
@@ -176,6 +181,8 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client {
|
||||
|
||||
void onDocumentLineCountChange( const size_t& lastCount, const size_t& newCount );
|
||||
|
||||
void onDocumentLineChanged( const Int64& lineIndex );
|
||||
|
||||
std::pair<int, int> getVisibleLineRange();
|
||||
|
||||
int getVisibleLinesCount();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE QtCreatorProject>
|
||||
<!-- Written by QtCreator 4.12.0, 2020-05-25T19:14:39. -->
|
||||
<!-- Written by QtCreator 4.12.0, 2020-05-26T05:47:23. -->
|
||||
<qtcreator>
|
||||
<data>
|
||||
<variable>EnvironmentId</variable>
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
#include <eepp/core/string.hpp>
|
||||
#include <eepp/core/utf.hpp>
|
||||
#include <iterator>
|
||||
#include <crc/CRC.h>
|
||||
|
||||
static CRC::Table<std::uint32_t, 32> CRC_TABLE(CRC::CRC_32());
|
||||
|
||||
namespace EE {
|
||||
|
||||
@@ -29,7 +32,7 @@ Uint32 String::hash( const std::string& str ) {
|
||||
}
|
||||
|
||||
Uint32 String::hash( const String& str ) {
|
||||
return String::hash( (const Uint8*)str.c_str() );
|
||||
return CRC::Calculate( (void*)str.c_str(), sizeof(StringBaseType)*str.size(), CRC_TABLE );
|
||||
}
|
||||
|
||||
bool String::isCharacter( const int& value ) {
|
||||
@@ -594,7 +597,7 @@ String& String::operator+=( const StringBaseType& right ) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
String::StringBaseType String::operator[]( std::size_t index ) const {
|
||||
const String::StringBaseType& String::operator[]( std::size_t index ) const {
|
||||
return mString[index];
|
||||
}
|
||||
|
||||
@@ -602,7 +605,7 @@ String::StringBaseType& String::operator[]( std::size_t index ) {
|
||||
return mString[index];
|
||||
}
|
||||
|
||||
String::StringBaseType String::at( std::size_t index ) const {
|
||||
const String::StringBaseType& String::at( std::size_t index ) const {
|
||||
return mString.at( index );
|
||||
}
|
||||
|
||||
|
||||
@@ -3,29 +3,37 @@
|
||||
|
||||
namespace EE { namespace UI { namespace Doc {
|
||||
|
||||
SyntaxHighlighter::SyntaxHighlighter( TextDocument* doc ) : mDoc( doc ) {
|
||||
SyntaxHighlighter::SyntaxHighlighter( TextDocument* doc ) :
|
||||
mDoc( doc ), mFirstInvalidLine( 0 ), mMaxWantedLine( 0 ) {
|
||||
reset();
|
||||
}
|
||||
|
||||
void SyntaxHighlighter::reset() {
|
||||
mLines.clear();
|
||||
mFirstInvalidLine = 0;
|
||||
mMaxWantedLine = 0;
|
||||
}
|
||||
|
||||
void SyntaxHighlighter::invalidate( Int64 lineIndex ) {
|
||||
mFirstInvalidLine = lineIndex;
|
||||
mMaxWantedLine = eemin<Int64>( mMaxWantedLine, (Int64)mDoc->linesCount() - 1 );
|
||||
}
|
||||
|
||||
TokenizedLine SyntaxHighlighter::tokenizeLine( const size_t& line, const int& state ) {
|
||||
TokenizedLine tokenizedLine;
|
||||
tokenizedLine.initState = state;
|
||||
tokenizedLine.text = mDoc->line( line );
|
||||
tokenizedLine.hash = mDoc->line( line ).getHash();
|
||||
std::pair<std::vector<SyntaxToken>, int> res = SyntaxTokenizer::tokenize(
|
||||
mDoc->getSyntaxDefinition(), tokenizedLine.text.toUtf8(), state );
|
||||
mDoc->getSyntaxDefinition(), mDoc->line( line ).toUtf8(), state );
|
||||
tokenizedLine.tokens = std::move( res.first );
|
||||
tokenizedLine.state = std::move( res.second );
|
||||
return tokenizedLine;
|
||||
}
|
||||
|
||||
const std::vector<SyntaxToken>& SyntaxHighlighter::getLine( const size_t& index ) {
|
||||
auto it = mLines.find( index );
|
||||
const auto& it = mLines.find( index );
|
||||
if ( it == mLines.end() ||
|
||||
( index < mDoc->linesCount() && mDoc->line( index ) != it->second.text ) ) {
|
||||
( index < mDoc->linesCount() && mDoc->line( index ).getHash() != it->second.hash ) ) {
|
||||
int prevState = SYNTAX_TOKENIZER_STATE_NONE;
|
||||
if ( index > 0 ) {
|
||||
auto prevIt = mLines.find( index - 1 );
|
||||
@@ -36,7 +44,44 @@ const std::vector<SyntaxToken>& SyntaxHighlighter::getLine( const size_t& index
|
||||
mLines[index] = tokenizeLine( index, prevState );
|
||||
return mLines[index].tokens;
|
||||
}
|
||||
mMaxWantedLine = eemax<Int64>( mMaxWantedLine, index );
|
||||
return it->second.tokens;
|
||||
}
|
||||
|
||||
Int64 SyntaxHighlighter::getFirstInvalidLine() const {
|
||||
return mFirstInvalidLine;
|
||||
}
|
||||
|
||||
Int64 SyntaxHighlighter::getMaxWantedLine() const {
|
||||
return mMaxWantedLine;
|
||||
}
|
||||
|
||||
bool SyntaxHighlighter::updateDirty( int visibleLinesCount ) {
|
||||
if ( mFirstInvalidLine > mMaxWantedLine ) {
|
||||
mMaxWantedLine = 0;
|
||||
} else {
|
||||
bool changed = false;
|
||||
Int64 max = eemin( mFirstInvalidLine + visibleLinesCount, mMaxWantedLine );
|
||||
|
||||
for ( Int64 index = mFirstInvalidLine; index <= max; index++ ) {
|
||||
int state = SYNTAX_TOKENIZER_STATE_NONE;
|
||||
if ( index > 0 ) {
|
||||
auto prevIt = mLines.find( index - 1 );
|
||||
if ( prevIt != mLines.end() ) {
|
||||
state = prevIt->second.state;
|
||||
}
|
||||
}
|
||||
const auto& it = mLines.find( index );
|
||||
if ( it != mLines.end() && it->second.initState != state ) {
|
||||
mLines[index] = tokenizeLine( index, state );
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
mFirstInvalidLine = max + 1;
|
||||
return changed;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}}} // namespace EE::UI::Doc
|
||||
|
||||
@@ -62,13 +62,13 @@ void TextDocument::loadFromPath( const std::string& path ) {
|
||||
line = line.substr( 0, line.size() - 1 );
|
||||
mIsCLRF = true;
|
||||
}
|
||||
mLines.emplace_back( String( line + "\n" ) );
|
||||
mLines.emplace_back( TextDocumentLine( line + "\n" ) );
|
||||
}
|
||||
|
||||
if ( mLines.empty() ) {
|
||||
mLines.emplace_back( String( "\n" ) );
|
||||
mLines.emplace_back( TextDocumentLine( "\n" ) );
|
||||
} else if ( mLines[mLines.size() - 1][mLines[mLines.size() - 1].size() - 1] != '\n' ) {
|
||||
mLines[mLines.size() - 1] += '\n';
|
||||
mLines[mLines.size() - 1].append( '\n' );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,9 +93,8 @@ bool TextDocument::save( IOStreamFile& stream, const bool& utf8bom ) {
|
||||
}
|
||||
size_t lastLine = mLines.size() - 1;
|
||||
for ( size_t i = 0; i <= lastLine; i++ ) {
|
||||
String& line = mLines[i];
|
||||
std::string utf8( line.toUtf8() );
|
||||
if ( i == lastLine && line.size() > 1 ) {
|
||||
std::string utf8( mLines[i].toUtf8() );
|
||||
if ( i == lastLine && utf8.size() > 1 && utf8[utf8.size() - 1] == '\n' ) {
|
||||
// Last \n is added by the document but it's not part of the document.
|
||||
utf8.pop_back();
|
||||
}
|
||||
@@ -160,11 +159,11 @@ const TextRange& TextDocument::getSelection() const {
|
||||
return mSelection;
|
||||
}
|
||||
|
||||
String& TextDocument::line( const size_t& index ) {
|
||||
TextDocumentLine& TextDocument::line( const size_t& index ) {
|
||||
return mLines[index];
|
||||
}
|
||||
|
||||
const String& TextDocument::line( const size_t& index ) const {
|
||||
const TextDocumentLine& TextDocument::line( const size_t& index ) const {
|
||||
return mLines[index];
|
||||
}
|
||||
|
||||
@@ -172,7 +171,7 @@ size_t TextDocument::linesCount() const {
|
||||
return mLines.size();
|
||||
}
|
||||
|
||||
std::vector<String>& TextDocument::lines() {
|
||||
std::vector<TextDocumentLine>& TextDocument::lines() {
|
||||
return mLines;
|
||||
}
|
||||
|
||||
@@ -188,7 +187,7 @@ String TextDocument::getText( const TextRange& range ) const {
|
||||
}
|
||||
std::vector<String> lines = {mLines[nrange.start().line()].substr( nrange.start().column() )};
|
||||
for ( auto i = nrange.start().line() + 1; i <= nrange.end().line() - 1; i++ ) {
|
||||
lines.emplace_back( mLines[i] );
|
||||
lines.emplace_back( mLines[i].getText() );
|
||||
}
|
||||
lines.emplace_back( mLines[nrange.end().line()].substr( 0, nrange.end().column() ) );
|
||||
return String::join( lines, -1 );
|
||||
@@ -240,27 +239,30 @@ TextPosition TextDocument::insert( TextPosition position, const String::StringBa
|
||||
for ( size_t i = position.column(); i < line( row ).length(); i++ )
|
||||
line_content.append( line( row )[i] );
|
||||
mLines.insert( mLines.begin() + position.line() + ( atTail ? 1 : 0 ), String( "\n" ) );
|
||||
notifyLineChanged( position.line() );
|
||||
return atTail
|
||||
? TextPosition( position.line() + 1, line( position.line() + 1 ).length() )
|
||||
: TextPosition( position.line() + 1, 0 );
|
||||
}
|
||||
String newLine =
|
||||
line( position.line() )
|
||||
.substr( position.column(), line( position.line() ).length() - position.column() );
|
||||
String& oldLine = line( position.line() );
|
||||
oldLine = line( position.line() ).substr( 0, position.column() );
|
||||
TextDocumentLine newLine( line( position.line() )
|
||||
.substr( position.column(), line( position.line() ).length() -
|
||||
position.column() ) );
|
||||
TextDocumentLine& oldLine = line( position.line() );
|
||||
oldLine.setText( line( position.line() ).substr( 0, position.column() ) );
|
||||
// TODO: Investigate why this is needed when undo is used.
|
||||
// This fixes the case when a line ends up without an \n at the end of it.
|
||||
if ( oldLine.empty() || oldLine[oldLine.size() - 1] != '\n' ) {
|
||||
oldLine += '\n';
|
||||
oldLine.append( '\n' );
|
||||
}
|
||||
if ( newLine.empty() || newLine[newLine.size() - 1] != '\n' ) {
|
||||
newLine += '\n';
|
||||
newLine.append( '\n' );
|
||||
}
|
||||
mLines.insert( mLines.begin() + position.line() + 1, std::move( newLine ) );
|
||||
notifyLineChanged( position.line() );
|
||||
return {position.line() + 1, 0};
|
||||
}
|
||||
line( position.line() ).insert( line( position.line() ).begin() + position.column(), ch );
|
||||
line( position.line() ).insertChar( position.column(), ch );
|
||||
notifyLineChanged( position.line() );
|
||||
return {position.line(), position.column() + 1};
|
||||
}
|
||||
|
||||
@@ -295,7 +297,7 @@ void TextDocument::remove( TextRange range, UndoStackContainer& undoStack, const
|
||||
|
||||
if ( range.start().line() == range.end().line() ) {
|
||||
// Delete within same line.
|
||||
String& line = this->line( range.start().line() );
|
||||
TextDocumentLine& line = this->line( range.start().line() );
|
||||
bool wholeLineIsSelected =
|
||||
range.start().column() == 0 && range.end().column() == (Int64)line.length();
|
||||
|
||||
@@ -313,13 +315,13 @@ void TextDocument::remove( TextRange range, UndoStackContainer& undoStack, const
|
||||
if ( afterSelection.empty() || afterSelection[afterSelection.size() - 1] != '\n' )
|
||||
afterSelection += '\n';
|
||||
|
||||
line.assign( beforeSelection + afterSelection );
|
||||
line.setText( beforeSelection + afterSelection );
|
||||
}
|
||||
} else {
|
||||
// Delete across a newline, merging lines.
|
||||
eeASSERT( range.start().line() == range.end().line() - 1 );
|
||||
auto& firstLine = line( range.start().line() );
|
||||
auto& secondLine = line( range.end().line() );
|
||||
TextDocumentLine& firstLine = line( range.start().line() );
|
||||
TextDocumentLine& secondLine = line( range.end().line() );
|
||||
auto beforeSelection = firstLine.substr( 0, range.start().column() );
|
||||
auto afterSelection = !secondLine.empty()
|
||||
? secondLine.substr( range.end().column(),
|
||||
@@ -331,7 +333,7 @@ void TextDocument::remove( TextRange range, UndoStackContainer& undoStack, const
|
||||
if ( afterSelection.empty() || afterSelection[afterSelection.size() - 1] != '\n' )
|
||||
afterSelection += '\n';
|
||||
|
||||
firstLine.assign( beforeSelection + afterSelection );
|
||||
firstLine.setText( beforeSelection + afterSelection );
|
||||
mLines.erase( mLines.begin() + range.end().line() );
|
||||
}
|
||||
|
||||
@@ -339,6 +341,7 @@ void TextDocument::remove( TextRange range, UndoStackContainer& undoStack, const
|
||||
mLines.emplace_back( String( "\n" ) );
|
||||
}
|
||||
notifyTextChanged();
|
||||
notifyLineChanged( range.start().line() );
|
||||
}
|
||||
|
||||
TextPosition TextDocument::positionOffset( TextPosition position, int columnOffset ) const {
|
||||
@@ -647,7 +650,7 @@ void TextDocument::newLine() {
|
||||
String input( "\n" );
|
||||
TextPosition start = getSelection().start();
|
||||
if ( start.line() >= 0 && start.line() < (Int64)mLines.size() ) {
|
||||
String& ln = line( start.line() );
|
||||
const String& ln = line( start.line() ).getText();
|
||||
size_t to = eemin<size_t>( ln.size(), start.column() );
|
||||
int indent = 0;
|
||||
for ( size_t i = 0; i < to; i++ ) {
|
||||
@@ -664,12 +667,12 @@ void TextDocument::newLine() {
|
||||
textInput( input );
|
||||
}
|
||||
|
||||
void TextDocument::insertAtStartOfSelectedLines( String text, bool skipEmpty ) {
|
||||
void TextDocument::insertAtStartOfSelectedLines( const String& text, bool skipEmpty ) {
|
||||
TextPosition prevStart = getSelection().start();
|
||||
TextRange range = getSelection( true );
|
||||
bool swap = prevStart != range.start();
|
||||
for ( auto i = range.start().line(); i <= range.end().line(); i++ ) {
|
||||
const String& line = this->line( i );
|
||||
const String& line = this->line( i ).getText();
|
||||
if ( !skipEmpty || line.length() != 1 ) {
|
||||
insert( {i, 0}, text );
|
||||
}
|
||||
@@ -678,12 +681,12 @@ void TextDocument::insertAtStartOfSelectedLines( String text, bool skipEmpty ) {
|
||||
TextPosition( range.end().line(), range.end().column() + text.size() ), swap );
|
||||
}
|
||||
|
||||
void TextDocument::removeFromStartOfSelectedLines( String text, bool skipEmpty ) {
|
||||
void TextDocument::removeFromStartOfSelectedLines( const String& text, bool skipEmpty ) {
|
||||
TextPosition prevStart = getSelection().start();
|
||||
TextRange range = getSelection( true );
|
||||
bool swap = prevStart != range.start();
|
||||
for ( auto i = range.start().line(); i <= range.end().line(); i++ ) {
|
||||
const String& line = this->line( i );
|
||||
const String& line = this->line( i ).getText();
|
||||
if ( !skipEmpty || line.length() != 1 ) {
|
||||
if ( line.substr( 0, text.length() ) == text ) {
|
||||
remove( {{i, 0}, {i, static_cast<Int64>( text.length() )}} );
|
||||
@@ -814,6 +817,12 @@ void TextDocument::notifyLineCountChanged( const size_t& lastCount, const size_t
|
||||
}
|
||||
}
|
||||
|
||||
void TextDocument::notifyLineChanged( const Int64& lineIndex ) {
|
||||
for ( auto& client : mClients ) {
|
||||
client->onDocumentLineChanged( lineIndex );
|
||||
}
|
||||
}
|
||||
|
||||
TextDocument::Client::~Client() {}
|
||||
|
||||
}}} // namespace EE::UI::Doc
|
||||
|
||||
@@ -23,6 +23,7 @@ UICodeEditor::UICodeEditor() :
|
||||
mCursorVisible( false ),
|
||||
mMouseDown( false ),
|
||||
mShowLineNumber( true ),
|
||||
mLocked( false ),
|
||||
mTabWidth( 4 ),
|
||||
mLastColOffset( 0 ),
|
||||
mMouseWheelScroll( 50 ),
|
||||
@@ -95,10 +96,12 @@ void UICodeEditor::draw() {
|
||||
Primitives primitives;
|
||||
TextPosition cursor( mDoc.getSelection().start() );
|
||||
|
||||
primitives.setColor( mCurrentLineBackgroundColor );
|
||||
primitives.drawRectangle(
|
||||
Rectf( Vector2f( startScroll.x + mScroll.x, startScroll.y + cursor.line() * lineHeight ),
|
||||
Sizef( mSize.getWidth(), lineHeight ) ) );
|
||||
if ( !mLocked ) {
|
||||
primitives.setColor( mCurrentLineBackgroundColor );
|
||||
primitives.drawRectangle( Rectf(
|
||||
Vector2f( startScroll.x + mScroll.x, startScroll.y + cursor.line() * lineHeight ),
|
||||
Sizef( mSize.getWidth(), lineHeight ) ) );
|
||||
}
|
||||
|
||||
if ( mDoc.hasSelection() ) {
|
||||
primitives.setColor( mFontStyleConfig.getFontSelectionBackColor() );
|
||||
@@ -109,7 +112,7 @@ void UICodeEditor::draw() {
|
||||
int endLine = eemin<int>( lineRange.second, selection.end().line() );
|
||||
|
||||
for ( auto ln = startLine; ln <= endLine; ln++ ) {
|
||||
const String& line = mDoc.line( ln );
|
||||
const String& line = mDoc.line( ln ).getText();
|
||||
Rectf selRect;
|
||||
selRect.Top = startScroll.y + ln * lineHeight;
|
||||
selRect.Bottom = selRect.Top + lineHeight;
|
||||
@@ -137,21 +140,23 @@ void UICodeEditor::draw() {
|
||||
for ( int i = lineRange.first; i <= lineRange.second; i++ ) {
|
||||
Vector2f curPos( startScroll.x, startScroll.y + lineHeight * i );
|
||||
auto& tokens = mHighlighter.getLine( i );
|
||||
Text line( "", mFont, charSize );
|
||||
line.setStyleConfig( mFontStyleConfig );
|
||||
for ( auto& token : tokens ) {
|
||||
Float textWidth = getTextWidth( token.text );
|
||||
if ( curPos.x + textWidth >= mScreenPos.x &&
|
||||
curPos.x <= mScreenPos.x + mSize.getWidth() ) {
|
||||
Text line( "", mFont, charSize );
|
||||
line.setStyleConfig( mFontStyleConfig );
|
||||
line.setString( token.text );
|
||||
line.setColor( mColorScheme.getColor( token.type ) );
|
||||
line.draw( curPos.x, curPos.y );
|
||||
} else if ( curPos.x > mScreenPos.x + mSize.getWidth() ) {
|
||||
break;
|
||||
}
|
||||
curPos.x += textWidth;
|
||||
}
|
||||
}
|
||||
|
||||
if ( mCursorVisible ) {
|
||||
if ( mCursorVisible && !mLocked ) {
|
||||
Vector2f cursorPos( startScroll.x + getXOffsetCol( cursor ),
|
||||
startScroll.y + cursor.line() * lineHeight );
|
||||
|
||||
@@ -188,6 +193,10 @@ void UICodeEditor::scheduledUpdate( const Time& ) {
|
||||
mMouseDown = false;
|
||||
getUISceneNode()->getWindow()->getInput()->captureMouse( false );
|
||||
}
|
||||
|
||||
if ( mHighlighter.updateDirty( getVisibleLinesCount() ) ) {
|
||||
invalidateDraw();
|
||||
}
|
||||
}
|
||||
|
||||
void UICodeEditor::reset() {
|
||||
@@ -391,7 +400,7 @@ Uint32 UICodeEditor::onFocusLoss() {
|
||||
}
|
||||
|
||||
Uint32 UICodeEditor::onTextInput( const TextInputEvent& event ) {
|
||||
if ( NULL == mFont )
|
||||
if ( mLocked || NULL == mFont )
|
||||
return 1;
|
||||
|
||||
if ( !getUISceneNode()->getWindow()->getInput()->isControlPressed() ) {
|
||||
@@ -404,6 +413,14 @@ Uint32 UICodeEditor::onKeyDown( const KeyEvent& event ) {
|
||||
if ( NULL == mFont )
|
||||
return 1;
|
||||
|
||||
// Allow copy selection on locked mode
|
||||
if ( mLocked ) {
|
||||
if ( event.getKeyCode() == KEY_C && ( event.getMod() & KEYMOD_CTRL ) ) {
|
||||
getUISceneNode()->getWindow()->getClipboard()->setText( mDoc.getSelectedText() );
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
switch ( event.getKeyCode() ) {
|
||||
case KEY_BACKSPACE: {
|
||||
if ( event.getMod() & KEYMOD_CTRL ) {
|
||||
@@ -662,7 +679,7 @@ Uint32 UICodeEditor::onMouseUp( const Vector2i& position, const Uint32& flags )
|
||||
}
|
||||
|
||||
Uint32 UICodeEditor::onMouseDoubleClick( const Vector2i&, const Uint32& flags ) {
|
||||
if ( NULL == mFont )
|
||||
if ( !mLocked || NULL == mFont )
|
||||
return 1;
|
||||
|
||||
if ( flags & EE_BUTTON_LMASK ) {
|
||||
@@ -672,7 +689,7 @@ Uint32 UICodeEditor::onMouseDoubleClick( const Vector2i&, const Uint32& flags )
|
||||
}
|
||||
|
||||
Uint32 UICodeEditor::onMouseOver( const Vector2i& position, const Uint32& flags ) {
|
||||
getUISceneNode()->setCursor( Cursor::IBeam );
|
||||
getUISceneNode()->setCursor( !mLocked ? Cursor::IBeam : Cursor::Arrow );
|
||||
return UIWidget::onMouseOver( position, flags );
|
||||
}
|
||||
|
||||
@@ -728,6 +745,10 @@ void UICodeEditor::onDocumentLineCountChange( const size_t&, const size_t& ) {
|
||||
updateScrollBar();
|
||||
}
|
||||
|
||||
void UICodeEditor::onDocumentLineChanged( const Int64& lineIndex ) {
|
||||
mHighlighter.invalidate( lineIndex );
|
||||
}
|
||||
|
||||
std::pair<int, int> UICodeEditor::getVisibleLineRange() {
|
||||
Float lineHeight = getLineHeight();
|
||||
Float minLine = eemax( 0.f, eefloor( mScroll.y / lineHeight ) );
|
||||
@@ -775,7 +796,7 @@ void UICodeEditor::setScrollY( const Float& val, bool emmitEvent ) {
|
||||
}
|
||||
|
||||
Float UICodeEditor::getXOffsetCol( const TextPosition& position ) const {
|
||||
const String& line = mDoc.line( position.line() );
|
||||
const String& line = mDoc.line( position.line() ).getText();
|
||||
Float glyphWidth = getGlyphWidth();
|
||||
Float x = 0;
|
||||
for ( auto i = 0; i < position.column(); i++ ) {
|
||||
@@ -792,37 +813,42 @@ Float UICodeEditor::getTextWidth( const String& line ) const {
|
||||
Float glyphWidth = getGlyphWidth();
|
||||
size_t len = line.length();
|
||||
Float x = 0;
|
||||
for ( size_t i = 0; i < len; i++ ) {
|
||||
if ( line[i] == '\t' ) {
|
||||
x += glyphWidth * mTabWidth;
|
||||
} else if ( line[i] != '\n' && line[i] != '\r' ) {
|
||||
x += glyphWidth;
|
||||
}
|
||||
}
|
||||
for ( size_t i = 0; i < len; i++ )
|
||||
x += ( line[i] == '\t' ) ? glyphWidth * mTabWidth : glyphWidth;
|
||||
return x;
|
||||
}
|
||||
|
||||
Float UICodeEditor::getColXOffset( TextPosition position ) {
|
||||
position = mDoc.sanitizePosition( position );
|
||||
position.setLine( eeclamp<Int64>( position.line(), 0L, mDoc.linesCount() - 1 ) );
|
||||
// This is different from sanitizePosition, sinze allows the last character.
|
||||
position.setColumn( eeclamp<Int64>( position.column(), 0L,
|
||||
eemax<Int64>( 0, mDoc.line( position.line() ).size() ) ) );
|
||||
return getTextWidth( mDoc.line( position.line() ).substr( 0, position.column() ) );
|
||||
}
|
||||
|
||||
const bool& UICodeEditor::isLocked() const {
|
||||
return mLocked;
|
||||
}
|
||||
|
||||
void UICodeEditor::setLocked( bool locked ) {
|
||||
if ( mLocked != locked ) {
|
||||
mLocked = locked;
|
||||
invalidateDraw();
|
||||
}
|
||||
}
|
||||
|
||||
Int64 UICodeEditor::getColFromXOffset( Int64 lineNumber, const Float& offset ) const {
|
||||
if ( offset <= 0 )
|
||||
return 0;
|
||||
TextPosition pos = mDoc.sanitizePosition( TextPosition( lineNumber, 0 ) );
|
||||
const String& line = mDoc.line( pos.line() );
|
||||
const String& line = mDoc.line( pos.line() ).getText();
|
||||
size_t len = line.length();
|
||||
Float glyphWidth = getGlyphWidth();
|
||||
Float x = 0;
|
||||
for ( size_t i = 0; i < line.size(); i++ ) {
|
||||
if ( line[i] == '\t' ) {
|
||||
x += glyphWidth * mTabWidth;
|
||||
} else if ( line[i] != '\n' && line[i] != '\r' ) {
|
||||
x += glyphWidth;
|
||||
}
|
||||
if ( x >= offset ) {
|
||||
for ( size_t i = 0; i < len; i++ ) {
|
||||
x += ( line[i] == '\t' ) ? glyphWidth * mTabWidth : glyphWidth;
|
||||
if ( x >= offset )
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return static_cast<Int64>( line.size() ) - 1;
|
||||
}
|
||||
|
||||
1705
src/thirdparty/crc/CRC.h
vendored
Normal file
1705
src/thirdparty/crc/CRC.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@@ -4,6 +4,7 @@
|
||||
EE::Window::Window* win = NULL;
|
||||
UISceneNode* uiSceneNode = NULL;
|
||||
UICodeEditor* codeEditor = NULL;
|
||||
Console* console = NULL;
|
||||
std::string curFile = "untitled";
|
||||
const std::string& windowTitle = "eepp - Code Editor";
|
||||
bool docDirtyState = false;
|
||||
@@ -47,41 +48,52 @@ void openFileDialog() {
|
||||
}
|
||||
|
||||
void mainLoop() {
|
||||
if ( codeEditor->isDirty() != docDirtyState ) {
|
||||
docDirtyState = codeEditor->isDirty();
|
||||
setAppTitle( docDirtyState ? curFile + "*" : curFile );
|
||||
}
|
||||
|
||||
Input* input = win->getInput();
|
||||
|
||||
input->update();
|
||||
|
||||
if ( ( input->isControlPressed() && input->isKeyUp( KEY_O ) ) || input->isKeyUp( KEY_F2 ) ) {
|
||||
openFileDialog();
|
||||
}
|
||||
if ( win->isActive() ) {
|
||||
if ( codeEditor->isDirty() != docDirtyState ) {
|
||||
docDirtyState = codeEditor->isDirty();
|
||||
setAppTitle( docDirtyState ? curFile + "*" : curFile );
|
||||
}
|
||||
|
||||
if ( input->isControlPressed() && input->isKeyUp( KEY_S ) ) {
|
||||
codeEditor->save();
|
||||
}
|
||||
if ( ( input->isControlPressed() && input->isKeyUp( KEY_O ) ) ||
|
||||
input->isKeyUp( KEY_F2 ) ) {
|
||||
openFileDialog();
|
||||
}
|
||||
|
||||
if ( input->isKeyUp( KEY_F6 ) ) {
|
||||
uiSceneNode->setHighlightOver( !uiSceneNode->getHighlightOver() );
|
||||
}
|
||||
if ( input->isControlPressed() && input->isKeyUp( KEY_S ) ) {
|
||||
codeEditor->save();
|
||||
}
|
||||
|
||||
if ( input->isKeyUp( KEY_F7 ) ) {
|
||||
uiSceneNode->setDrawBoxes( !uiSceneNode->getDrawBoxes() );
|
||||
}
|
||||
if ( input->isKeyUp( KEY_F6 ) ) {
|
||||
uiSceneNode->setHighlightOver( !uiSceneNode->getHighlightOver() );
|
||||
}
|
||||
|
||||
if ( input->isKeyUp( KEY_F8 ) ) {
|
||||
uiSceneNode->setDrawDebugData( !uiSceneNode->getDrawDebugData() );
|
||||
}
|
||||
if ( input->isKeyUp( KEY_F7 ) ) {
|
||||
uiSceneNode->setDrawBoxes( !uiSceneNode->getDrawBoxes() );
|
||||
}
|
||||
|
||||
if ( input->isKeyUp( KEY_ESCAPE ) && NULL == MsgBox && onCloseRequestCallback( win ) ) {
|
||||
win->close();
|
||||
}
|
||||
if ( input->isKeyUp( KEY_F8 ) ) {
|
||||
uiSceneNode->setDrawDebugData( !uiSceneNode->getDrawDebugData() );
|
||||
}
|
||||
|
||||
if ( input->isAltPressed() && input->isKeyUp( KEY_RETURN ) ) {
|
||||
win->toggleFullscreen();
|
||||
if ( input->isKeyUp( KEY_ESCAPE ) && NULL == MsgBox && onCloseRequestCallback( win ) ) {
|
||||
win->close();
|
||||
}
|
||||
|
||||
if ( input->isAltPressed() && input->isKeyUp( KEY_RETURN ) ) {
|
||||
win->toggleFullscreen();
|
||||
}
|
||||
|
||||
if ( input->isKeyUp( KEY_F3 ) ) {
|
||||
console->toggle();
|
||||
}
|
||||
|
||||
if ( input->isControlPressed() && input->isKeyUp( KEY_L ) ) {
|
||||
codeEditor->setLocked( !codeEditor->isLocked() );
|
||||
}
|
||||
}
|
||||
|
||||
SceneManager::instance()->update();
|
||||
@@ -89,9 +101,10 @@ void mainLoop() {
|
||||
if ( SceneManager::instance()->getUISceneNode()->invalidated() ) {
|
||||
win->clear();
|
||||
SceneManager::instance()->draw();
|
||||
console->draw();
|
||||
win->display();
|
||||
} else {
|
||||
Sys::sleep( Milliseconds( win->isVisible() ? 1 : 8 ) );
|
||||
Sys::sleep( Milliseconds( win->isVisible() ? 1 : 16 ) );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,7 +156,7 @@ EE_MAIN_FUNC int main( int argc, char* argv[] ) {
|
||||
uiSceneNode->getUIThemeManager()->setDefaultFont(
|
||||
FontTrueType::New( "NotoSans-Regular", "assets/fonts/NotoSans-Regular.ttf" ) );
|
||||
|
||||
FontTrueType::New( "monospace", "assets/fonts/DejaVuSansMono.ttf" );
|
||||
Font* fontMono = FontTrueType::New( "monospace", "assets/fonts/DejaVuSansMono.ttf" );
|
||||
|
||||
SceneManager::instance()->add( uiSceneNode );
|
||||
|
||||
@@ -171,9 +184,13 @@ EE_MAIN_FUNC int main( int argc, char* argv[] ) {
|
||||
loadFileFromPath( file.Get() );
|
||||
}
|
||||
|
||||
console = eeNew( Console, ( fontMono, true, true, 1024 * 1000, 0, win ) );
|
||||
|
||||
win->runMainLoop( &mainLoop );
|
||||
}
|
||||
|
||||
eeSAFE_DELETE( console );
|
||||
|
||||
Engine::destroySingleton();
|
||||
MemoryManager::showResults();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user