mirror of
https://github.com/SpartanJ/eepp.git
synced 2026-06-04 20:46:29 +03:00
Multi cursor WIP.
This commit is contained in:
@@ -31,6 +31,36 @@ struct DocumentContentChange {
|
||||
String text;
|
||||
};
|
||||
|
||||
class EE_API TextRanges : public std::vector<TextRange> {
|
||||
public:
|
||||
bool isValid() {
|
||||
for ( const auto& selection : *this ) {
|
||||
if ( !selection.isValid() )
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool exists( const TextRange& range ) {
|
||||
for ( const auto& r : *this )
|
||||
if ( range == r )
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
TextRanges& merge() {
|
||||
if ( size() <= 1 )
|
||||
return *this;
|
||||
TextRanges newRanges;
|
||||
newRanges.emplace_back( ( *this )[0] );
|
||||
for ( size_t i = 1; i < size(); ++i )
|
||||
if ( !newRanges.exists( ( *this )[i] ) )
|
||||
newRanges.emplace_back( ( *this )[i] );
|
||||
*this = newRanges;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
class EE_API TextDocument {
|
||||
public:
|
||||
typedef std::function<void()> DocumentCommand;
|
||||
@@ -78,6 +108,8 @@ class EE_API TextDocument {
|
||||
|
||||
void reset();
|
||||
|
||||
void resetCursor();
|
||||
|
||||
LoadStatus loadFromStream( IOStream& path );
|
||||
|
||||
LoadStatus loadFromFile( const std::string& path );
|
||||
@@ -116,16 +148,23 @@ class EE_API TextDocument {
|
||||
|
||||
std::string getFilename() const;
|
||||
|
||||
void setSelection( TextPosition position );
|
||||
void setSelection( const TextPosition& position );
|
||||
|
||||
void setSelection( const size_t& cursorIdx, TextPosition start, TextPosition end,
|
||||
bool swap = false );
|
||||
|
||||
void setSelection( TextPosition start, TextPosition end, bool swap = false );
|
||||
|
||||
void setSelection( TextRange range );
|
||||
void setSelection( const TextRange& range );
|
||||
|
||||
TextRange getSelection( bool sort ) const;
|
||||
|
||||
const std::vector<TextRange>& getSelections() const;
|
||||
|
||||
const TextRange& getSelection() const;
|
||||
|
||||
const TextRange& getSelectionIndex( const size_t& index ) const;
|
||||
|
||||
TextDocumentLine& line( const size_t& index );
|
||||
|
||||
const TextDocumentLine& line( const size_t& index ) const;
|
||||
@@ -416,6 +455,8 @@ class EE_API TextDocument {
|
||||
|
||||
TextRange sanitizeRange( const TextRange& range ) const;
|
||||
|
||||
TextRanges sanitizeRange( const TextRanges& ranges ) const;
|
||||
|
||||
bool getAutoCloseBrackets() const;
|
||||
|
||||
void setAutoCloseBrackets( bool autoCloseBrackets );
|
||||
@@ -451,14 +492,43 @@ class EE_API TextDocument {
|
||||
|
||||
const std::string& getLoadingFilePath() const;
|
||||
|
||||
void setSelection( const TextRanges& selection );
|
||||
|
||||
std::vector<TextRange> getSelectionsSorted() const;
|
||||
|
||||
void addCursorAbove();
|
||||
|
||||
void addCursorBelow();
|
||||
|
||||
void addCursor( const TextRange& cursor );
|
||||
|
||||
TextRange getTopMostCursor();
|
||||
|
||||
TextRange getBottomMostCursor();
|
||||
|
||||
void moveTo( const size_t& cursorIdx, TextPosition offset );
|
||||
|
||||
void moveTo( const size_t& cursorIdx, int columnOffset );
|
||||
|
||||
void setSelection( const size_t& cursorIdx, const TextPosition& position );
|
||||
|
||||
void mergeSelection();
|
||||
|
||||
void selectTo( const size_t& cursorIdx, TextPosition position );
|
||||
|
||||
void selectTo( const size_t& cursorIdx, int offset );
|
||||
|
||||
void setSelection( const size_t& cursorIdx, const TextRange& range );
|
||||
|
||||
protected:
|
||||
friend class UndoStack;
|
||||
|
||||
UndoStack mUndoStack;
|
||||
std::string mFilePath;
|
||||
std::string mLoadingFilePath;
|
||||
FileInfo mFileRealPath;
|
||||
std::vector<TextDocumentLine> mLines;
|
||||
TextRange mSelection;
|
||||
TextRanges mSelection;
|
||||
std::unordered_set<Client*> mClients;
|
||||
Mutex mClientsMutex;
|
||||
LineEnding mLineEnding{ LineEnding::LF };
|
||||
|
||||
@@ -28,6 +28,13 @@ class EE_API TextRange {
|
||||
|
||||
TextRange normalized() const { return TextRange( normalizedStart(), normalizedEnd() ); }
|
||||
|
||||
TextRange& normalize() {
|
||||
auto normalize( normalized() );
|
||||
mStart = normalize.start();
|
||||
mEnd = normalize.end();
|
||||
return *this;
|
||||
}
|
||||
|
||||
TextRange reversed() { return TextRange( mEnd, mStart ); }
|
||||
|
||||
void setStart( const TextPosition& position ) { mStart = position; }
|
||||
@@ -47,6 +54,22 @@ class EE_API TextRange {
|
||||
return mStart != other.mStart || mEnd != other.mEnd;
|
||||
}
|
||||
|
||||
bool operator<( const TextRange& other ) const {
|
||||
return mStart < other.mStart && mEnd < other.mEnd;
|
||||
}
|
||||
|
||||
bool operator>( const TextRange& other ) const {
|
||||
return mStart > other.mStart && mEnd > other.mEnd;
|
||||
}
|
||||
|
||||
bool operator<=( const TextRange& other ) const {
|
||||
return mStart <= other.mStart && mEnd <= other.mEnd;
|
||||
}
|
||||
|
||||
bool operator>=( const TextRange& other ) const {
|
||||
return mStart >= other.mStart && mEnd >= other.mEnd;
|
||||
}
|
||||
|
||||
bool contains( const TextPosition& position ) const {
|
||||
if ( !( position.line() > mStart.line() ||
|
||||
( position.line() == mStart.line() && position.column() >= mStart.column() ) ) )
|
||||
|
||||
@@ -386,7 +386,8 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client {
|
||||
void setFindLongestLineWidthUpdateFrequency( const Time& findLongestLineWidthUpdateFrequency );
|
||||
|
||||
/** Doc commands executed in this editor. */
|
||||
TextPosition moveToLineOffset( const TextPosition& position, int offset );
|
||||
TextPosition moveToLineOffset( const TextPosition& position, int offset,
|
||||
const size_t& cursorIdx = 0 );
|
||||
|
||||
void moveToPreviousLine();
|
||||
|
||||
@@ -568,8 +569,8 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client {
|
||||
|
||||
protected:
|
||||
struct LastXOffset {
|
||||
TextPosition position;
|
||||
Float offset;
|
||||
TextPosition position{ 0, 0 };
|
||||
Float offset{ 0.f };
|
||||
};
|
||||
Font* mFont;
|
||||
UIFontStyleConfig mFontStyleConfig;
|
||||
@@ -631,7 +632,7 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client {
|
||||
SyntaxHighlighter mHighlighter;
|
||||
UIScrollBar* mVScrollBar;
|
||||
UIScrollBar* mHScrollBar;
|
||||
LastXOffset mLastXOffset{ { 0, 0 }, 0.f };
|
||||
std::map<size_t, LastXOffset> mLastXOffset;
|
||||
KeyBindings mKeyBindings;
|
||||
std::unordered_set<std::string> mUnlockedCmd;
|
||||
Clock mLastDoubleClick;
|
||||
|
||||
@@ -57,17 +57,23 @@ bool TextDocument::isEmpty() {
|
||||
}
|
||||
|
||||
void TextDocument::reset() {
|
||||
auto oldSelection = sanitizeRange( mSelection );
|
||||
mFilePath = mDefaultFileName;
|
||||
mFileRealPath = FileInfo();
|
||||
mSelection.set( { 0, 0 }, { 0, 0 } );
|
||||
mSelection.clear();
|
||||
mSelection.push_back( { { 0, 0 }, { 0, 0 } } );
|
||||
mLines.clear();
|
||||
mLines.emplace_back( String( "\n" ) );
|
||||
mSyntaxDefinition = SyntaxDefinitionManager::instance()->getPlainStyle();
|
||||
mUndoStack.clear();
|
||||
cleanChangeId();
|
||||
if ( oldSelection.isValid() )
|
||||
notifyTextChanged( { { oldSelection.end(), oldSelection.start() }, "" } );
|
||||
notifyCursorChanged();
|
||||
notifySelectionChanged();
|
||||
}
|
||||
|
||||
void TextDocument::resetCursor() {
|
||||
auto cursor = sanitizeRange( mSelection.front() );
|
||||
mSelection.clear();
|
||||
mSelection.push_back( cursor );
|
||||
notifyCursorChanged();
|
||||
notifySelectionChanged();
|
||||
}
|
||||
@@ -222,6 +228,10 @@ void TextDocument::guessIndentType() {
|
||||
mIndentWidth = 4;
|
||||
}
|
||||
|
||||
void TextDocument::mergeSelection() {
|
||||
mSelection.merge();
|
||||
}
|
||||
|
||||
bool TextDocument::hasSyntaxDefinition() const {
|
||||
return !mSyntaxDefinition.getPatterns().empty();
|
||||
}
|
||||
@@ -463,7 +473,7 @@ TextDocument::LoadStatus TextDocument::reload() {
|
||||
: FileInfo( mFilePath );
|
||||
resetSyntax();
|
||||
notifyDocumentReloaded();
|
||||
setSelection( sanitizePosition( selection.start() ) );
|
||||
setSelection( sanitizeRange( selection ) );
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -564,11 +574,12 @@ bool TextDocument::save() {
|
||||
}
|
||||
|
||||
void TextDocument::sanitizeCurrentSelection() {
|
||||
auto newSelection =
|
||||
TextRange( sanitizePosition( mSelection.start() ), sanitizePosition( mSelection.end() ) );
|
||||
|
||||
if ( mSelection != newSelection )
|
||||
setSelection( newSelection );
|
||||
for ( size_t i = 0; i < mSelection.size(); ++i ) {
|
||||
auto& selection = mSelection[i];
|
||||
auto newSelection = sanitizeRange( selection );
|
||||
if ( selection != newSelection )
|
||||
setSelection( i, newSelection );
|
||||
}
|
||||
}
|
||||
|
||||
bool TextDocument::isLoading() const {
|
||||
@@ -587,13 +598,31 @@ std::string TextDocument::getFilename() const {
|
||||
return FileSystem::fileNameFromPath( mFilePath );
|
||||
}
|
||||
|
||||
void TextDocument::setSelection( TextPosition position ) {
|
||||
void TextDocument::setSelection( const TextRanges& selection ) {
|
||||
for ( size_t i = 0; i < selection.size(); ++i ) {
|
||||
if ( i >= mSelection.size() )
|
||||
mSelection.push_back( selection[i] );
|
||||
setSelection( i, selection[i].start(), selection[i].end(), false );
|
||||
}
|
||||
}
|
||||
|
||||
void TextDocument::setSelection( const TextPosition& position ) {
|
||||
setSelection( position, position );
|
||||
}
|
||||
|
||||
void TextDocument::setSelection( TextPosition start, TextPosition end, bool swap ) {
|
||||
if ( ( start == mSelection.start() && end == mSelection.end() && !swap ) ||
|
||||
( start == mSelection.end() && end == mSelection.start() && swap ) )
|
||||
void TextDocument::setSelection( const size_t& cursorIdx, const TextPosition& position ) {
|
||||
setSelection( cursorIdx, position, position );
|
||||
}
|
||||
|
||||
void TextDocument::setSelection( const size_t& cursorIdx, TextPosition start, TextPosition end,
|
||||
bool swap ) {
|
||||
eeASSERT( cursorIdx < mSelection.size() );
|
||||
if ( cursorIdx >= mSelection.size() )
|
||||
return;
|
||||
|
||||
if ( ( start == mSelection[cursorIdx].start() && end == mSelection[cursorIdx].end() &&
|
||||
!swap ) ||
|
||||
( start == mSelection[cursorIdx].end() && end == mSelection[cursorIdx].start() && swap ) )
|
||||
return;
|
||||
|
||||
if ( swap ) {
|
||||
@@ -609,23 +638,47 @@ void TextDocument::setSelection( TextPosition start, TextPosition end, bool swap
|
||||
end = sanitizePosition( end );
|
||||
}
|
||||
|
||||
if ( mSelection != TextRange( start, end ) ) {
|
||||
mSelection.set( start, end );
|
||||
if ( mSelection[cursorIdx] != TextRange( start, end ) ) {
|
||||
mSelection[cursorIdx].set( start, end );
|
||||
notifyCursorChanged();
|
||||
notifySelectionChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void TextDocument::setSelection( TextRange range ) {
|
||||
void TextDocument::setSelection( TextPosition start, TextPosition end, bool swap ) {
|
||||
setSelection( 0, start, end, swap );
|
||||
}
|
||||
|
||||
void TextDocument::setSelection( const TextRange& range ) {
|
||||
setSelection( range.start(), range.end() );
|
||||
}
|
||||
|
||||
void TextDocument::setSelection( const size_t& cursorIdx, const TextRange& range ) {
|
||||
setSelection( cursorIdx, range.start(), range.end() );
|
||||
}
|
||||
|
||||
TextRange TextDocument::getSelection( bool sort ) const {
|
||||
return sort ? mSelection.normalized() : mSelection;
|
||||
return sort ? mSelection.front().normalized() : mSelection.front();
|
||||
}
|
||||
|
||||
const std::vector<TextRange>& TextDocument::getSelections() const {
|
||||
return mSelection;
|
||||
}
|
||||
|
||||
std::vector<TextRange> TextDocument::getSelectionsSorted() const {
|
||||
std::vector<TextRange> selections( mSelection );
|
||||
for ( auto& selection : selections )
|
||||
selection.normalize();
|
||||
return selections;
|
||||
}
|
||||
|
||||
const TextRange& TextDocument::getSelectionIndex( const size_t& index ) const {
|
||||
eeASSERT( index < mSelection.size() );
|
||||
return mSelection[index];
|
||||
}
|
||||
|
||||
const TextRange& TextDocument::getSelection() const {
|
||||
return mSelection;
|
||||
return mSelection.front();
|
||||
}
|
||||
|
||||
TextDocumentLine& TextDocument::line( const size_t& index ) {
|
||||
@@ -641,7 +694,7 @@ size_t TextDocument::linesCount() const {
|
||||
}
|
||||
|
||||
const TextDocumentLine& TextDocument::getCurrentLine() const {
|
||||
return mLines[mSelection.start().line()];
|
||||
return mLines[mSelection.front().start().line()];
|
||||
}
|
||||
|
||||
std::vector<TextDocumentLine>& TextDocument::lines() {
|
||||
@@ -649,7 +702,7 @@ std::vector<TextDocumentLine>& TextDocument::lines() {
|
||||
}
|
||||
|
||||
bool TextDocument::hasSelection() const {
|
||||
return mSelection.start() != mSelection.end();
|
||||
return mSelection.front().start() != mSelection.front().end();
|
||||
}
|
||||
|
||||
String TextDocument::getText( const TextRange& range ) const {
|
||||
@@ -1010,6 +1063,16 @@ void TextDocument::selectTo( int offset ) {
|
||||
setSelection( TextRange( posOffset, range.end() ) );
|
||||
}
|
||||
|
||||
void TextDocument::selectTo( const size_t& cursorIdx, TextPosition position ) {
|
||||
setSelection( cursorIdx, TextRange( sanitizePosition( position ), getSelection().end() ) );
|
||||
}
|
||||
|
||||
void TextDocument::selectTo( const size_t& cursorIdx, int offset ) {
|
||||
const TextRange& range = getSelection();
|
||||
TextPosition posOffset = positionOffset( range.start(), offset );
|
||||
setSelection( cursorIdx, TextRange( posOffset, range.end() ) );
|
||||
}
|
||||
|
||||
void TextDocument::moveTo( TextPosition offset ) {
|
||||
setSelection( offset );
|
||||
}
|
||||
@@ -1018,6 +1081,14 @@ void TextDocument::moveTo( int columnOffset ) {
|
||||
setSelection( positionOffset( getSelection().start(), columnOffset ) );
|
||||
}
|
||||
|
||||
void TextDocument::moveTo( const size_t& cursorIdx, TextPosition offset ) {
|
||||
setSelection( cursorIdx, offset );
|
||||
}
|
||||
|
||||
void TextDocument::moveTo( const size_t& cursorIdx, int columnOffset ) {
|
||||
setSelection( cursorIdx, positionOffset( getSelection().start(), columnOffset ) );
|
||||
}
|
||||
|
||||
void TextDocument::textInput( const String& text ) {
|
||||
if ( mAutoCloseBrackets && 1 == text.size() ) {
|
||||
size_t pos = 0xFFFFFFFF;
|
||||
@@ -1070,47 +1141,65 @@ void TextDocument::unregisterClient( Client* client ) {
|
||||
}
|
||||
|
||||
void TextDocument::moveToPreviousChar() {
|
||||
if ( hasSelection() ) {
|
||||
setSelection( getSelection( true ).start() );
|
||||
} else {
|
||||
setSelection( positionOffset( getSelection().start(), -1 ) );
|
||||
for ( size_t i = 0; i < mSelection.size(); ++i ) {
|
||||
if ( mSelection[i].hasSelection() ) {
|
||||
setSelection( i, mSelection[i].normalize().start() );
|
||||
} else {
|
||||
setSelection( i, positionOffset( mSelection[i].start(), -1 ) );
|
||||
}
|
||||
}
|
||||
mergeSelection();
|
||||
}
|
||||
|
||||
void TextDocument::moveToNextChar() {
|
||||
if ( hasSelection() ) {
|
||||
setSelection( getSelection( true ).end() );
|
||||
} else {
|
||||
setSelection( positionOffset( getSelection().start(), 1 ) );
|
||||
for ( size_t i = 0; i < mSelection.size(); ++i ) {
|
||||
if ( mSelection[i].hasSelection() ) {
|
||||
setSelection( i, mSelection[i].normalize().end() );
|
||||
} else {
|
||||
setSelection( i, positionOffset( mSelection[i].start(), 1 ) );
|
||||
}
|
||||
}
|
||||
mergeSelection();
|
||||
}
|
||||
|
||||
void TextDocument::moveToPreviousWord() {
|
||||
if ( hasSelection() ) {
|
||||
setSelection( getSelection( true ).start() );
|
||||
} else {
|
||||
setSelection( previousWordBoundary( getSelection().start() ) );
|
||||
for ( size_t i = 0; i < mSelection.size(); ++i ) {
|
||||
if ( mSelection[i].hasSelection() ) {
|
||||
setSelection( i, mSelection[i].normalize().start() );
|
||||
} else {
|
||||
setSelection( i, previousWordBoundary( mSelection[i].start() ) );
|
||||
}
|
||||
}
|
||||
mergeSelection();
|
||||
}
|
||||
|
||||
void TextDocument::moveToNextWord() {
|
||||
if ( hasSelection() ) {
|
||||
setSelection( getSelection( true ).end() );
|
||||
} else {
|
||||
setSelection( nextWordBoundary( getSelection().start() ) );
|
||||
for ( size_t i = 0; i < mSelection.size(); ++i ) {
|
||||
if ( mSelection[i].hasSelection() ) {
|
||||
setSelection( i, mSelection[i].normalize().end() );
|
||||
} else {
|
||||
setSelection( i, nextWordBoundary( mSelection[i].start() ) );
|
||||
}
|
||||
}
|
||||
mergeSelection();
|
||||
}
|
||||
|
||||
void TextDocument::moveToPreviousLine() {
|
||||
TextPosition pos = getSelection().start();
|
||||
pos.setLine( pos.line() - 1 );
|
||||
setSelection( pos );
|
||||
for ( size_t i = 0; i < mSelection.size(); ++i ) {
|
||||
TextPosition pos = mSelection[i].start();
|
||||
pos.setLine( pos.line() - 1 );
|
||||
setSelection( i, pos, pos );
|
||||
}
|
||||
mergeSelection();
|
||||
}
|
||||
|
||||
void TextDocument::moveToNextLine() {
|
||||
TextPosition pos = getSelection().start();
|
||||
pos.setLine( pos.line() + 1 );
|
||||
setSelection( pos );
|
||||
for ( size_t i = 0; i < mSelection.size(); ++i ) {
|
||||
TextPosition pos = mSelection[i].start();
|
||||
pos.setLine( pos.line() + 1 );
|
||||
setSelection( i, pos, pos );
|
||||
}
|
||||
mergeSelection();
|
||||
}
|
||||
|
||||
void TextDocument::moveToPreviousPage( Int64 pageSize ) {
|
||||
@@ -1126,10 +1215,12 @@ void TextDocument::moveToNextPage( Int64 pageSize ) {
|
||||
}
|
||||
|
||||
void TextDocument::moveToStartOfDoc() {
|
||||
resetCursor();
|
||||
setSelection( startOfDoc() );
|
||||
}
|
||||
|
||||
void TextDocument::moveToEndOfDoc() {
|
||||
resetCursor();
|
||||
setSelection( endOfDoc() );
|
||||
}
|
||||
|
||||
@@ -1426,6 +1517,17 @@ TextRange TextDocument::sanitizeRange( const TextRange& range ) const {
|
||||
return { sanitizePosition( range.start() ), sanitizePosition( range.end() ) };
|
||||
}
|
||||
|
||||
TextRanges TextDocument::sanitizeRange( const TextRanges& ranges ) const {
|
||||
TextRanges sanitizedRanges;
|
||||
for ( const auto& range : ranges ) {
|
||||
if ( !range.isValid() )
|
||||
return sanitizedRanges;
|
||||
sanitizedRanges.push_back(
|
||||
{ sanitizePosition( range.start() ), sanitizePosition( range.end() ) } );
|
||||
}
|
||||
return sanitizedRanges;
|
||||
}
|
||||
|
||||
bool TextDocument::getAutoCloseBrackets() const {
|
||||
return mAutoCloseBrackets;
|
||||
}
|
||||
@@ -2125,6 +2227,55 @@ void TextDocument::initializeCommands() {
|
||||
mCommands["toggle-line-comments"] = [&] { toggleLineComments(); };
|
||||
mCommands["selection-to-upper"] = [&] { toUpperSelection(); };
|
||||
mCommands["selection-to-lower"] = [&] { toLowerSelection(); };
|
||||
mCommands["reset-cursor"] = [&] { resetCursor(); };
|
||||
mCommands["add-cursor-above"] = [&] { addCursorAbove(); };
|
||||
mCommands["add-cursor-below"] = [&] { addCursorBelow(); };
|
||||
}
|
||||
|
||||
TextRange TextDocument::getTopMostCursor() {
|
||||
if ( mSelection.size() == 1 )
|
||||
return mSelection.front();
|
||||
TextRange topMost( mSelection[0] );
|
||||
for ( size_t i = 1; i < mSelection.size(); ++i ) {
|
||||
if ( mSelection[i] < topMost )
|
||||
topMost = mSelection[i];
|
||||
}
|
||||
return topMost;
|
||||
}
|
||||
|
||||
TextRange TextDocument::getBottomMostCursor() {
|
||||
if ( mSelection.size() == 1 )
|
||||
return mSelection.front();
|
||||
TextRange bottomMost( mSelection[0] );
|
||||
for ( size_t i = 1; i < mSelection.size(); ++i ) {
|
||||
if ( mSelection[i] > bottomMost )
|
||||
bottomMost = mSelection[i];
|
||||
}
|
||||
return bottomMost;
|
||||
}
|
||||
|
||||
void TextDocument::addCursor( const TextRange& cursor ) {
|
||||
mSelection.emplace_back( cursor );
|
||||
}
|
||||
|
||||
void TextDocument::addCursorAbove() {
|
||||
auto curPos( getTopMostCursor().normalize().start() );
|
||||
if ( curPos.line() == 0 )
|
||||
return;
|
||||
curPos.setLine( curPos.line() - 1 );
|
||||
curPos = sanitizePosition( curPos );
|
||||
addCursor( { curPos, curPos } );
|
||||
notifyCursorChanged();
|
||||
}
|
||||
|
||||
void TextDocument::addCursorBelow() {
|
||||
auto curPos( getBottomMostCursor().normalize().start() );
|
||||
if ( curPos.line() >= (Int64)linesCount() - 1 )
|
||||
return;
|
||||
curPos.setLine( curPos.line() + 1 );
|
||||
curPos = sanitizePosition( curPos );
|
||||
addCursor( { curPos, curPos } );
|
||||
notifyCursorChanged();
|
||||
}
|
||||
|
||||
TextDocument::Client::~Client() {}
|
||||
|
||||
@@ -98,6 +98,10 @@ const std::map<KeyBindings::Shortcut, std::string> UICodeEditor::getDefaultKeybi
|
||||
{ { KEY_UP, KEYMOD_CTRL | KEYMOD_LALT | KEYMOD_SHIFT }, "selection-to-upper" },
|
||||
{ { KEY_DOWN, KEYMOD_CTRL | KEYMOD_LALT | KEYMOD_SHIFT }, "selection-to-lower" },
|
||||
{ { KEY_F, KeyMod::getDefaultModifier() }, "find-replace" },
|
||||
{ { KEY_D, KeyMod::getDefaultModifier() }, "select-word" },
|
||||
{ { KEY_UP, KEYMOD_LALT }, "add-cursor-above" },
|
||||
{ { KEY_DOWN, KEYMOD_LALT }, "add-cursor-below" },
|
||||
{ { KEY_ESCAPE }, "reset-cursor" },
|
||||
};
|
||||
}
|
||||
|
||||
@@ -235,10 +239,13 @@ void UICodeEditor::draw() {
|
||||
}
|
||||
|
||||
if ( !mLocked && mHighlightCurrentLine ) {
|
||||
primitives.setColor( Color( mCurrentLineBackgroundColor ).blendAlpha( mAlpha ) );
|
||||
primitives.drawRectangle( Rectf(
|
||||
Vector2f( startScroll.x + mScroll.x, startScroll.y + cursor.line() * lineHeight ),
|
||||
Sizef( mSize.getWidth(), lineHeight ) ) );
|
||||
for ( const auto& cursor : mDoc->getSelections() ) {
|
||||
primitives.setColor( Color( mCurrentLineBackgroundColor ).blendAlpha( mAlpha ) );
|
||||
primitives.drawRectangle(
|
||||
Rectf( Vector2f( startScroll.x + mScroll.x,
|
||||
startScroll.y + cursor.start().line() * lineHeight ),
|
||||
Sizef( mSize.getWidth(), lineHeight ) ) );
|
||||
}
|
||||
}
|
||||
|
||||
if ( mLineBreakingColumn ) {
|
||||
@@ -260,8 +267,11 @@ void UICodeEditor::draw() {
|
||||
}
|
||||
|
||||
if ( mDoc->hasSelection() ) {
|
||||
drawTextRange( mDoc->getSelection( true ), lineRange, startScroll, lineHeight,
|
||||
mFontStyleConfig.getFontSelectionBackColor() );
|
||||
auto selections = mDoc->getSelectionsSorted();
|
||||
for ( const auto& sel : selections ) {
|
||||
drawTextRange( sel, lineRange, startScroll, lineHeight,
|
||||
mFontStyleConfig.getFontSelectionBackColor() );
|
||||
}
|
||||
}
|
||||
|
||||
if ( mHighlightSelectionMatch && mDoc->hasSelection() && mDoc->getSelection().inSameLine() ) {
|
||||
@@ -306,7 +316,8 @@ void UICodeEditor::draw() {
|
||||
}
|
||||
}
|
||||
|
||||
drawCursor( startScroll, lineHeight, cursor );
|
||||
for ( const auto& cursor : mDoc->getSelections() )
|
||||
drawCursor( startScroll, lineHeight, cursor.start() );
|
||||
|
||||
if ( mShowLineNumber ) {
|
||||
drawLineNumbers( lineRange, startScroll,
|
||||
@@ -1467,8 +1478,14 @@ void UICodeEditor::scrollToCursor( bool centered ) {
|
||||
|
||||
void UICodeEditor::updateEditor() {
|
||||
mDoc->setPageSize( getVisibleLinesCount() );
|
||||
if ( mDirtyScroll && mDoc->getActiveClient() == this )
|
||||
scrollTo( mDoc->getSelection().start() );
|
||||
if ( mDirtyScroll && mDoc->getActiveClient() == this ) {
|
||||
if ( mDoc->getSelections().size() == 1 ) {
|
||||
scrollTo( mDoc->getSelection().start() );
|
||||
} else {
|
||||
scrollTo( mDoc->getBottomMostCursor().normalize().end() );
|
||||
scrollTo( mDoc->getTopMostCursor().normalize().start() );
|
||||
}
|
||||
}
|
||||
updateScrollBar();
|
||||
mDirtyEditor = false;
|
||||
mDirtyScroll = false;
|
||||
@@ -2270,8 +2287,9 @@ void UICodeEditor::resetCursor() {
|
||||
mBlinkTimer.restart();
|
||||
}
|
||||
|
||||
TextPosition UICodeEditor::moveToLineOffset( const TextPosition& position, int offset ) {
|
||||
auto& xo = mLastXOffset;
|
||||
TextPosition UICodeEditor::moveToLineOffset( const TextPosition& position, int offset,
|
||||
const size_t& cursorIdx ) {
|
||||
auto& xo = mLastXOffset[cursorIdx];
|
||||
if ( xo.position != position )
|
||||
xo.offset = getXOffsetColSanitized( position );
|
||||
xo.position.setLine( position.line() + offset );
|
||||
@@ -2280,31 +2298,49 @@ TextPosition UICodeEditor::moveToLineOffset( const TextPosition& position, int o
|
||||
}
|
||||
|
||||
void UICodeEditor::moveToPreviousLine() {
|
||||
TextPosition position = mDoc->getSelection().start();
|
||||
if ( position.line() == 0 )
|
||||
return mDoc->moveToStartOfDoc();
|
||||
mDoc->moveTo( moveToLineOffset( position, -1 ) );
|
||||
for ( size_t i = 0; i < mDoc->getSelections().size(); ++i ) {
|
||||
TextPosition position = mDoc->getSelections()[i].start();
|
||||
if ( position.line() == 0 ) {
|
||||
mDoc->setSelection( i, mDoc->startOfDoc(), mDoc->startOfDoc() );
|
||||
} else {
|
||||
mDoc->moveTo( i, moveToLineOffset( position, -1, i ) );
|
||||
}
|
||||
}
|
||||
mDoc->mergeSelection();
|
||||
}
|
||||
|
||||
void UICodeEditor::moveToNextLine() {
|
||||
TextPosition position = mDoc->getSelection().start();
|
||||
if ( position.line() == (Int64)mDoc->linesCount() - 1 )
|
||||
return mDoc->moveToEndOfDoc();
|
||||
mDoc->moveTo( moveToLineOffset( position, 1 ) );
|
||||
for ( size_t i = 0; i < mDoc->getSelections().size(); ++i ) {
|
||||
TextPosition position = mDoc->getSelections()[i].start();
|
||||
if ( position.line() == (Int64)mDoc->linesCount() - 1 ) {
|
||||
mDoc->setSelection( i, mDoc->endOfDoc(), mDoc->endOfDoc() );
|
||||
} else {
|
||||
mDoc->moveTo( i, moveToLineOffset( position, 1, i ) );
|
||||
}
|
||||
}
|
||||
mDoc->mergeSelection();
|
||||
}
|
||||
|
||||
void UICodeEditor::selectToPreviousLine() {
|
||||
TextPosition position = mDoc->getSelection().start();
|
||||
if ( position.line() == 0 )
|
||||
return mDoc->selectToStartOfDoc();
|
||||
mDoc->selectTo( moveToLineOffset( position, -1 ) );
|
||||
for ( size_t i = 0; i < mDoc->getSelections().size(); ++i ) {
|
||||
TextPosition position = mDoc->getSelectionIndex( i ).start();
|
||||
if ( position.line() == 0 ) {
|
||||
mDoc->selectTo( i, mDoc->startOfDoc() );
|
||||
} else {
|
||||
mDoc->selectTo( i, moveToLineOffset( position, -1 ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UICodeEditor::selectToNextLine() {
|
||||
TextPosition position = mDoc->getSelection().start();
|
||||
if ( position.line() == (Int64)mDoc->linesCount() - 1 )
|
||||
return mDoc->selectToEndOfDoc();
|
||||
mDoc->selectTo( moveToLineOffset( position, 1 ) );
|
||||
for ( size_t i = 0; i < mDoc->getSelections().size(); ++i ) {
|
||||
TextPosition position = mDoc->getSelectionIndex( i ).start();
|
||||
if ( position.line() == (Int64)mDoc->linesCount() - 1 ) {
|
||||
mDoc->selectTo( i, mDoc->endOfDoc() );
|
||||
} else {
|
||||
mDoc->selectTo( i, moveToLineOffset( position, 1 ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UICodeEditor::moveScrollUp() {
|
||||
|
||||
Reference in New Issue
Block a user