Several fixes and improvements in TextDocument and UICodeEditor.

TextDocument now supports undo/redo (still testing, may have some bugs).
This commit is contained in:
Martín Lucas Golini
2020-05-22 04:36:17 -03:00
parent c462e9e7c9
commit 825626a9d2
9 changed files with 434 additions and 57 deletions

View File

@@ -2,11 +2,16 @@
#define EE_UI_DOC_TEXTDOCUMENT
#include <eepp/core/string.hpp>
#include <eepp/system/clock.hpp>
#include <eepp/system/time.hpp>
#include <eepp/ui/doc/textposition.hpp>
#include <eepp/ui/doc/textrange.hpp>
#include <eepp/ui/doc/undostack.hpp>
#include <unordered_set>
#include <vector>
using namespace EE::System;
namespace EE { namespace UI { namespace Doc {
class EE_API TextDocument {
@@ -61,8 +66,6 @@ class EE_API TextDocument {
TextPosition insert( const TextPosition& position, const String& text );
TextPosition insert( TextPosition position, const String::StringBaseType& text );
void remove( TextPosition position );
void remove( TextRange range );
@@ -98,13 +101,13 @@ class EE_API TextDocument {
Int64 getRelativeColumnOffset( TextPosition position ) const;
void deleteTo( TextPosition offset );
void deleteTo( TextPosition position );
void deleteTo( int offset );
void deleteSelection();
void selectTo( TextPosition offset );
void selectTo( TextPosition position );
void selectTo( int offset );
@@ -138,6 +141,10 @@ class EE_API TextDocument {
void deleteToNextChar();
void deleteToPreviousWord();
void deleteToNextWord();
void selectToPreviousChar();
void selectToNextChar();
@@ -180,7 +187,13 @@ class EE_API TextDocument {
void setIndentType( const IndentType& indentType );
void undo();
void redo();
protected:
friend class UndoStack;
UndoStack mUndoStack;
std::string mFilename;
std::vector<String> mLines;
TextRange mSelection;
@@ -188,6 +201,7 @@ class EE_API TextDocument {
bool mIsCLRF;
Uint32 mTabWidth{4};
IndentType mIndentType{IndentTabs};
Clock mTimer;
void notifyTextChanged();
@@ -198,6 +212,13 @@ class EE_API TextDocument {
void insertAtStartOfSelectedLines( String text, bool skipEmpty );
void removeFromStartOfSelectedLines( String text, bool skipEmpty );
void remove( TextRange range, UndoStackContainer& undoStack, const Time& time );
TextPosition insert( const TextPosition& position, const String& text,
UndoStackContainer& undoStack, const Time& time );
TextPosition insert( TextPosition position, const String::StringBaseType& text );
};
}}} // namespace EE::UI::Doc

View File

@@ -0,0 +1,114 @@
#ifndef EE_UI_DOC_UNDOSTACK_HPP
#define EE_UI_DOC_UNDOSTACK_HPP
#include <deque>
#include <eepp/config.hpp>
#include <eepp/core/string.hpp>
#include <eepp/system/time.hpp>
#include <eepp/ui/doc/textrange.hpp>
#include <memory>
using namespace EE::System;
namespace EE { namespace UI { namespace Doc {
class TextDocument;
enum class TextUndoCommandType { Insert, Remove, Selection };
class EE_API TextUndoCommand {
public:
TextUndoCommand( const TextUndoCommandType& type, const Time& timestamp );
virtual ~TextUndoCommand();
const TextUndoCommandType& getType() const;
const Time& getTimestamp() const;
protected:
TextUndoCommandType mType;
Time mTimestamp;
};
class EE_API TextUndoCommandInsert : public TextUndoCommand {
public:
TextUndoCommandInsert( const String& text, const TextPosition& position,
const Time& timestamp );
const String& getText() const;
const TextPosition& getPosition() const;
protected:
String mText;
TextPosition mPosition;
};
class EE_API TextUndoCommandRemove : public TextUndoCommand {
public:
TextUndoCommandRemove( const TextRange& range, const Time& timestamp );
const TextRange& getRange() const;
protected:
TextRange mRange;
};
class EE_API TextUndoCommandSelection : public TextUndoCommand {
public:
TextUndoCommandSelection( const TextRange& selection, const Time& timestamp );
const TextRange& getSelection() const;
protected:
TextRange mSelection;
};
using UndoStackContainer = std::deque<std::unique_ptr<TextUndoCommand>>;
class EE_API UndoStack {
public:
UndoStack( TextDocument* owner, const Uint32& maxStackSize = 1000 );
void clearRedoStack();
void undo();
void redo();
const Uint32& getMaxStackSize() const;
const Time& getMergeTimeout() const;
void setMergeTimeout( const Time& mergeTimeout );
protected:
friend class TextDocument;
TextDocument* mDoc;
Uint32 mMaxStackSize;
UndoStackContainer mUndoStack;
UndoStackContainer mRedoStack;
Time mMergeTimeout;
void pushUndo( UndoStackContainer& undoStack, std::unique_ptr<TextUndoCommand>&& cmd );
void pushInsert( UndoStackContainer& undoStack, const String& string,
const TextPosition& position, const Time& time );
void pushRemove( UndoStackContainer& undoStack, const TextRange& range, const Time& time );
void pushSelection( UndoStackContainer& undoStack, const TextRange& selection,
const Time& time );
UndoStackContainer& getUndoStackContainer();
UndoStackContainer& getRedoStackContainer();
void popUndo( UndoStackContainer& undoStack, UndoStackContainer& redoStack );
};
}}} // namespace EE::UI::Doc
#endif // EE_UI_DOC_UNDOSTACK_HPP

View File

@@ -88,6 +88,10 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client {
virtual Uint32 onMouseDoubleClick( const Vector2i& position, const Uint32& flags );
virtual Uint32 onMouseOver( const Vector2i& position, const Uint32& flags );
virtual Uint32 onMouseLeave( const Vector2i& position, const Uint32& flags );
virtual void onSizeChange();
virtual void onPaddingChange();

View File

@@ -318,6 +318,7 @@
../../include/eepp/ui/doc/textdocument.hpp
../../include/eepp/ui/doc/textposition.hpp
../../include/eepp/ui/doc/textrange.hpp
../../include/eepp/ui/doc/undostack.hpp
../../include/eepp/ui/keyboardshortcut.hpp
../../include/eepp/ui/marginmove/scale.hpp
../../include/eepp/ui/tools/textureatlaseditor.hpp
@@ -757,6 +758,7 @@
../../src/eepp/ui/css/stylesheetvariable.cpp
../../src/eepp/ui/css/transitiondefinition.cpp
../../src/eepp/ui/doc/textdocument.cpp
../../src/eepp/ui/doc/undostack.cpp
../../src/eepp/ui/tools/textureatlaseditor.cpp
../../src/eepp/ui/tools/textureatlasnew.cpp
../../src/eepp/ui/tools/textureatlasnew.hpp

View File

@@ -5,8 +5,5 @@
../../src/thirdparty/efsw/include
../../src/thirdparty/libvorbis/include
/usr/include/freetype2/
../../include/eepp/ui/css
../../src/eepp/ui/css
../../src/thirdparty/mbedtls/include
../../include/eepp/ui
../../docs/articles

View File

@@ -16,7 +16,7 @@ bool TextDocument::isNonWord( String::StringBaseType ch ) {
return false;
}
TextDocument::TextDocument() {}
TextDocument::TextDocument() : mUndoStack( this ) {}
void TextDocument::reset() {
mFilename = "unsaved";
@@ -124,8 +124,8 @@ bool TextDocument::hasSelection() const {
String TextDocument::getText( const TextRange& range ) const {
TextRange nrange = range.normalized();
if ( nrange.start().line() == nrange.end().line() ) {
return mLines[nrange.start().line()].substr( nrange.start().column(),
nrange.end().column() );
return mLines[nrange.start().line()].substr(
nrange.start().column(), nrange.end().column() - nrange.start().column() );
}
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++ ) {
@@ -145,9 +145,23 @@ String::StringBaseType TextDocument::getChar( const TextPosition& position ) con
}
TextPosition TextDocument::insert( const TextPosition& position, const String& text ) {
mUndoStack.clearRedoStack();
return insert( position, text, mUndoStack.getUndoStackContainer(), mTimer.getElapsedTime() );
}
TextPosition TextDocument::insert( const TextPosition& position, const String& text,
UndoStackContainer& undoStack, const Time& time ) {
TextPosition cursor = position;
for ( size_t i = 0; i < text.length(); ++i )
for ( size_t i = 0; i < text.length(); ++i ) {
cursor = insert( cursor, text[i] );
}
mUndoStack.pushSelection( undoStack, getSelection(), time );
mUndoStack.pushRemove( undoStack, {position, cursor}, time );
notifyTextChanged();
return cursor;
}
@@ -157,27 +171,26 @@ TextPosition TextDocument::insert( TextPosition position, const String::StringBa
bool atTail = position.column() == (Int64)line( position.line() ).length() - 1;
if ( ch == '\n' ) {
if ( atTail || atHead ) {
String newLineContents( "\n" );
size_t row = position.line();
String line_content;
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 ), newLineContents );
notifyTextChanged();
if ( atTail )
return {position.line() + 1, (Int64)line( position.line() + 1 ).length()};
return {position.line() + 1, 0};
mLines.insert( mLines.begin() + position.line() + ( atTail ? 1 : 0 ), String( "\n" ) );
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() );
line( position.line() ) = line( position.line() ).substr( 0, position.column() );
if ( newLine.empty() ) {
eePRINTL( "wtf" );
}
mLines.insert( mLines.begin() + position.line() + 1, std::move( newLine ) );
notifyTextChanged();
return {position.line() + 1, 0};
}
line( position.line() ).insert( line( position.line() ).begin() + position.column(), ch );
notifyTextChanged();
return {position.line(), position.column() + 1};
}
@@ -186,12 +199,19 @@ void TextDocument::remove( TextPosition position ) {
}
void TextDocument::remove( TextRange range ) {
if ( !range.isValid() )
return;
mUndoStack.clearRedoStack();
range = range.normalized();
range.setStart( sanitizePosition( range.start() ) );
range.setEnd( sanitizePosition( range.end() ) );
remove( range, mUndoStack.getUndoStackContainer(), mTimer.getElapsedTime() );
}
void TextDocument::remove( TextRange range, UndoStackContainer& undoStack, const Time& time ) {
if ( !range.isValid() )
return;
mUndoStack.pushSelection( undoStack, getSelection(), time );
mUndoStack.pushInsert( undoStack, getText( range ), range.start(), time );
// First delete all the lines in between the first and last one.
for ( auto i = range.start().line() + 1; i < range.end().line(); ) {
@@ -221,12 +241,15 @@ void TextDocument::remove( TextRange range ) {
auto beforeSelection = firstLine.substr( 0, range.start().column() );
auto afterSelection =
secondLine.substr( range.end().column(), secondLine.length() - range.end().column() );
if ( beforeSelection.empty() && afterSelection.empty() ) {
beforeSelection += '\n';
}
firstLine.assign( beforeSelection + afterSelection );
mLines.erase( mLines.begin() + range.end().line() );
}
if ( lines().empty() ) {
mLines.emplace_back( String() );
mLines.emplace_back( String( "\n" ) );
}
notifyTextChanged();
}
@@ -374,8 +397,8 @@ void TextDocument::deleteSelection() {
setSelection( cursorPos );
}
void TextDocument::selectTo( TextPosition offset ) {
setSelection( TextRange( sanitizePosition( offset ), getSelection().end() ) );
void TextDocument::selectTo( TextPosition position ) {
setSelection( TextRange( sanitizePosition( position ), getSelection().end() ) );
}
void TextDocument::selectTo( int offset ) {
@@ -483,6 +506,14 @@ void TextDocument::deleteToNextChar() {
deleteTo( 1 );
}
void TextDocument::deleteToPreviousWord() {
deleteTo( previousWordBoundary( getSelection().start() ) );
}
void TextDocument::deleteToNextWord() {
deleteTo( nextWordBoundary( getSelection().start() ) );
}
void TextDocument::selectToPreviousChar() {
selectTo( -1 );
}
@@ -626,13 +657,12 @@ void TextDocument::setTabWidth( const Uint32& tabWidth ) {
mTabWidth = tabWidth;
}
void TextDocument::deleteTo( TextPosition offset ) {
void TextDocument::deleteTo( TextPosition position ) {
TextPosition cursorPos = getSelection( true ).start();
if ( hasSelection() ) {
remove( getSelection() );
} else {
TextPosition delPos = positionOffset( cursorPos, offset );
TextRange range( cursorPos, delPos );
TextRange range( cursorPos, position );
remove( range );
range = range.normalized();
cursorPos = range.start();
@@ -660,7 +690,21 @@ void TextDocument::setIndentType( const IndentType& indentType ) {
mIndentType = indentType;
}
void TextDocument::undo() {
mUndoStack.undo();
}
void TextDocument::redo() {
mUndoStack.redo();
}
void TextDocument::notifyTextChanged() {
for ( size_t i = 0; i < mLines.size(); i++ ) {
if ( mLines[i].empty() ) {
eePRINTL( "wtf" );
}
}
for ( auto& client : mClients ) {
client->onDocumentTextChanged();
}

View File

@@ -0,0 +1,141 @@
#include <eepp/ui/doc/textdocument.hpp>
#include <eepp/ui/doc/undostack.hpp>
using namespace EE::System;
namespace EE { namespace UI { namespace Doc {
TextUndoCommand::TextUndoCommand( const TextUndoCommandType& type, const Time& timestamp ) :
mType( type ), mTimestamp( timestamp ) {}
TextUndoCommand::~TextUndoCommand() {}
const TextUndoCommandType& TextUndoCommand::getType() const {
return mType;
}
const Time& TextUndoCommand::getTimestamp() const {
return mTimestamp;
}
TextUndoCommandInsert::TextUndoCommandInsert( const String& text, const TextPosition& position,
const Time& timestamp ) :
TextUndoCommand( TextUndoCommandType::Insert, timestamp ),
mText( text ),
mPosition( position ) {}
const String& TextUndoCommandInsert::getText() const {
return mText;
}
const TextPosition& TextUndoCommandInsert::getPosition() const {
return mPosition;
}
TextUndoCommandRemove::TextUndoCommandRemove( const TextRange& range, const Time& timestamp ) :
TextUndoCommand( TextUndoCommandType::Remove, timestamp ), mRange( range ) {}
const TextRange& TextUndoCommandRemove::getRange() const {
return mRange;
}
TextUndoCommandSelection::TextUndoCommandSelection( const TextRange& selection,
const Time& timestamp ) :
TextUndoCommand( TextUndoCommandType::Selection, timestamp ), mSelection( selection ) {}
const TextRange& TextUndoCommandSelection::getSelection() const {
return mSelection;
}
UndoStack::UndoStack( TextDocument* owner, const Uint32& maxStackSize ) :
mDoc( owner ), mMaxStackSize( maxStackSize ), mMergeTimeout( Milliseconds( 300.f ) ) {}
void UndoStack::clearRedoStack() {
mRedoStack.clear();
}
void UndoStack::pushUndo( UndoStackContainer& undoStack, std::unique_ptr<TextUndoCommand>&& cmd ) {
undoStack.push_back( std::move( cmd ) );
while ( undoStack.size() > mMaxStackSize ) {
undoStack.pop_front();
}
}
void UndoStack::pushInsert( UndoStackContainer& undoStack, const String& string,
const TextPosition& position, const Time& time ) {
pushUndo( undoStack, std::make_unique<TextUndoCommandInsert>( string, position, time ) );
}
void UndoStack::pushRemove( UndoStackContainer& undoStack, const TextRange& range,
const Time& time ) {
pushUndo( undoStack, std::make_unique<TextUndoCommandRemove>( range, time ) );
}
void UndoStack::pushSelection( UndoStackContainer& undoStack, const TextRange& selection,
const Time& time ) {
pushUndo( undoStack, std::make_unique<TextUndoCommandSelection>( selection, time ) );
}
void UndoStack::popUndo( UndoStackContainer& undoStack, UndoStackContainer& redoStack ) {
if ( undoStack.empty() )
return;
auto cmd = std::move( undoStack.back() );
undoStack.pop_back();
switch ( cmd->getType() ) {
case TextUndoCommandType::Insert: {
TextUndoCommandInsert* insert = static_cast<TextUndoCommandInsert*>( cmd.get() );
mDoc->insert( insert->getPosition(), insert->getText(), redoStack,
cmd->getTimestamp() );
break;
}
case TextUndoCommandType::Remove: {
TextUndoCommandRemove* remove = static_cast<TextUndoCommandRemove*>( cmd.get() );
mDoc->remove( remove->getRange(), redoStack, cmd->getTimestamp() );
break;
}
case TextUndoCommandType::Selection: {
TextUndoCommandSelection* selection =
static_cast<TextUndoCommandSelection*>( cmd.get() );
mDoc->setSelection( selection->getSelection() );
break;
}
}
if ( !undoStack.empty() &&
eeabs( ( cmd->getTimestamp() - undoStack.back()->getTimestamp() ).asMilliseconds() ) <
mMergeTimeout.asMilliseconds() ) {
popUndo( undoStack, redoStack );
}
}
void UndoStack::undo() {
popUndo( mUndoStack, mRedoStack );
}
void UndoStack::redo() {
popUndo( mRedoStack, mUndoStack );
}
const Uint32& UndoStack::getMaxStackSize() const {
return mMaxStackSize;
}
const Time& UndoStack::getMergeTimeout() const {
return mMergeTimeout;
}
void UndoStack::setMergeTimeout( const Time& mergeTimeout ) {
mMergeTimeout = mergeTimeout;
}
UndoStackContainer& UndoStack::getUndoStackContainer() {
return mUndoStack;
}
UndoStackContainer& UndoStack::getRedoStackContainer() {
return mRedoStack;
}
}}} // namespace EE::UI::Doc

View File

@@ -25,6 +25,7 @@ UICodeEditor::UICodeEditor() :
mMouseWheelScroll( 50 ) {
clipEnable();
if ( NULL == mFont ) {
// TODO: Remove this.
mFont = FontTrueType::New( "monospace", "assets/fonts/DejaVuSansMono.ttf" );
}
mDoc.registerClient( *this );
@@ -45,7 +46,7 @@ bool UICodeEditor::isType( const Uint32& type ) const {
void UICodeEditor::setTheme( UITheme* Theme ) {
UIWidget::setTheme( Theme );
setThemeSkin( Theme, "textedit" );
setThemeSkin( Theme, "codeeditor" );
}
void UICodeEditor::draw() {
@@ -230,12 +231,23 @@ Uint32 UICodeEditor::onTextInput( const TextInputEvent& event ) {
Uint32 UICodeEditor::onKeyDown( const KeyEvent& event ) {
switch ( event.getKeyCode() ) {
case KEY_BACKSPACE: {
mDoc.deleteToPreviousChar();
updateLastColumnOffset();
if ( event.getMod() & KEYMOD_CTRL ) {
mDoc.deleteToPreviousWord();
updateLastColumnOffset();
} else {
mDoc.deleteToPreviousChar();
updateLastColumnOffset();
}
break;
}
case KEY_DELETE: {
mDoc.deleteToNextChar();
if ( event.getMod() & KEYMOD_CTRL ) {
mDoc.deleteToNextWord();
updateLastColumnOffset();
} else {
mDoc.deleteToNextChar();
updateLastColumnOffset();
}
break;
}
case KEY_KP_ENTER:
@@ -245,7 +257,10 @@ Uint32 UICodeEditor::onKeyDown( const KeyEvent& event ) {
break;
}
case KEY_UP: {
if ( event.getMod() & KEYMOD_LSHIFT ) {
if ( event.getMod() & KEYMOD_CTRL ) {
mScroll.y = eefloor( eemax<Float>( 0, mScroll.y - getLineHeight() ) );
invalidateDraw();
} else if ( event.getMod() & KEYMOD_SHIFT ) {
mDoc.selectToPreviousLine( mLastColOffset );
} else {
mDoc.moveToPreviousLine( mLastColOffset );
@@ -253,7 +268,11 @@ Uint32 UICodeEditor::onKeyDown( const KeyEvent& event ) {
break;
}
case KEY_DOWN: {
if ( event.getMod() & KEYMOD_LSHIFT ) {
if ( event.getMod() & KEYMOD_CTRL ) {
mScroll.y =
eefloor( eemin<Float>( getMaxScroll().y, mScroll.y + getLineHeight() ) );
invalidateDraw();
} else if ( event.getMod() & KEYMOD_SHIFT ) {
mDoc.selectToNextLine( mLastColOffset );
} else {
mDoc.moveToNextLine( mLastColOffset );
@@ -261,11 +280,11 @@ Uint32 UICodeEditor::onKeyDown( const KeyEvent& event ) {
break;
}
case KEY_LEFT: {
if ( ( event.getMod() & KEYMOD_LSHIFT ) && ( event.getMod() & KEYMOD_LCTRL ) ) {
if ( ( event.getMod() & KEYMOD_SHIFT ) && ( event.getMod() & KEYMOD_CTRL ) ) {
mDoc.selectToPreviousWord();
} else if ( event.getMod() & KEYMOD_LSHIFT ) {
} else if ( event.getMod() & KEYMOD_SHIFT ) {
mDoc.selectToPreviousChar();
} else if ( event.getMod() & KEYMOD_LCTRL ) {
} else if ( event.getMod() & KEYMOD_CTRL ) {
mDoc.moveToPreviousWord();
} else {
mDoc.moveToPreviousChar();
@@ -274,11 +293,11 @@ Uint32 UICodeEditor::onKeyDown( const KeyEvent& event ) {
break;
}
case KEY_RIGHT: {
if ( ( event.getMod() & KEYMOD_LSHIFT ) && ( event.getMod() & KEYMOD_LCTRL ) ) {
if ( ( event.getMod() & KEYMOD_SHIFT ) && ( event.getMod() & KEYMOD_CTRL ) ) {
mDoc.selectToNextWord();
} else if ( event.getMod() & KEYMOD_LSHIFT ) {
} else if ( event.getMod() & KEYMOD_SHIFT ) {
mDoc.selectToNextChar();
} else if ( event.getMod() & KEYMOD_LCTRL ) {
} else if ( event.getMod() & KEYMOD_CTRL ) {
mDoc.moveToNextWord();
} else {
mDoc.moveToNextChar();
@@ -287,10 +306,10 @@ Uint32 UICodeEditor::onKeyDown( const KeyEvent& event ) {
break;
}
case KEY_HOME: {
if ( event.getMod() & KEYMOD_LSHIFT ) {
if ( event.getMod() & KEYMOD_SHIFT ) {
mDoc.selectToStartOfLine();
updateLastColumnOffset();
} else if ( event.getMod() & KEYMOD_LCTRL ) {
} else if ( event.getMod() & KEYMOD_CTRL ) {
mScroll.y = 0;
mDoc.setSelection( {0, 0} );
invalidateDraw();
@@ -301,10 +320,10 @@ Uint32 UICodeEditor::onKeyDown( const KeyEvent& event ) {
break;
}
case KEY_END: {
if ( event.getMod() & KEYMOD_LSHIFT ) {
if ( event.getMod() & KEYMOD_SHIFT ) {
mDoc.selectToEndOfLine();
updateLastColumnOffset();
} else if ( event.getMod() & KEYMOD_LCTRL ) {
} else if ( event.getMod() & KEYMOD_CTRL ) {
mScroll.y = getMaxScroll().y;
mDoc.setSelection(
{static_cast<Int64>( mDoc.lineCount() - 1 ),
@@ -317,7 +336,7 @@ Uint32 UICodeEditor::onKeyDown( const KeyEvent& event ) {
break;
}
case KEY_PAGEUP: {
if ( event.getMod() & KEYMOD_LSHIFT ) {
if ( event.getMod() & KEYMOD_SHIFT ) {
mDoc.selectToPreviousPage( getVisibleLinesCount() );
updateLastColumnOffset();
} else {
@@ -327,7 +346,7 @@ Uint32 UICodeEditor::onKeyDown( const KeyEvent& event ) {
break;
}
case KEY_PAGEDOWN: {
if ( event.getMod() & KEYMOD_LSHIFT ) {
if ( event.getMod() & KEYMOD_SHIFT ) {
mDoc.selectToNextPage( getVisibleLinesCount() );
updateLastColumnOffset();
} else {
@@ -337,7 +356,7 @@ Uint32 UICodeEditor::onKeyDown( const KeyEvent& event ) {
break;
}
case KEY_TAB: {
if ( event.getMod() & KEYMOD_LSHIFT ) {
if ( event.getMod() & KEYMOD_SHIFT ) {
mDoc.unindent();
updateLastColumnOffset();
} else if ( !event.getMod() ) {
@@ -347,30 +366,47 @@ Uint32 UICodeEditor::onKeyDown( const KeyEvent& event ) {
break;
}
case KEY_V: {
if ( event.getMod() & KEYMOD_LCTRL ) {
if ( event.getMod() & KEYMOD_CTRL ) {
mDoc.textInput( getUISceneNode()->getWindow()->getClipboard()->getText() );
}
break;
}
case KEY_C: {
if ( event.getMod() & KEYMOD_LCTRL ) {
if ( event.getMod() & KEYMOD_CTRL ) {
getUISceneNode()->getWindow()->getClipboard()->setText( mDoc.getSelectedText() );
}
break;
}
case KEY_X: {
if ( event.getMod() & KEYMOD_LCTRL ) {
if ( event.getMod() & KEYMOD_CTRL ) {
getUISceneNode()->getWindow()->getClipboard()->setText( mDoc.getSelectedText() );
mDoc.deleteSelection();
}
break;
}
case KEY_A: {
if ( event.getMod() & KEYMOD_LCTRL ) {
if ( event.getMod() & KEYMOD_CTRL ) {
mDoc.selectAll();
}
break;
}
case KEY_Z: {
if ( ( event.getMod() & KEYMOD_CTRL ) && ( event.getMod() & KEYMOD_SHIFT ) ) {
mDoc.redo();
updateLastColumnOffset();
} else if ( event.getMod() & KEYMOD_CTRL ) {
mDoc.undo();
updateLastColumnOffset();
}
break;
}
case KEY_Y: {
if ( event.getMod() & KEYMOD_CTRL ) {
mDoc.redo();
updateLastColumnOffset();
}
break;
}
default:
break;
}
@@ -401,13 +437,18 @@ Sizef UICodeEditor::getMaxScroll() const {
eefloor( ( mSize.getWidth() - mRealPadding.Left - mRealPadding.Right ) / getGlyphWidth() ),
vplc.y > mDoc.lineCount() - 1
? 0.f
: ( mDoc.lineCount() - getViewPortLineCount().y ) * getLineHeight() );
: eefloor( mDoc.lineCount() - getViewPortLineCount().y ) * getLineHeight() );
}
Uint32 UICodeEditor::onMouseDown( const Vector2i& position, const Uint32& flags ) {
if ( !mMouseDown && ( flags & EE_BUTTON_LMASK ) ) {
mMouseDown = true;
mDoc.setSelection( resolveScreenPosition( position.asFloat() ) );
Input* input = getUISceneNode()->getWindow()->getInput();
if ( input->isShiftPressed() ) {
mDoc.selectTo( resolveScreenPosition( position.asFloat() ) );
} else {
mDoc.setSelection( resolveScreenPosition( position.asFloat() ) );
}
}
return UIWidget::onMouseDown( position, flags );
}
@@ -426,11 +467,11 @@ Uint32 UICodeEditor::onMouseUp( const Vector2i& position, const Uint32& flags )
mMouseDown = false;
} else if ( flags & EE_BUTTON_WDMASK ) {
mScroll.y += PixelDensity::dpToPx( mMouseWheelScroll );
mScroll.y = eemin( mScroll.y, getMaxScroll().y );
mScroll.y = eefloor( eemin( mScroll.y, getMaxScroll().y ) );
invalidateDraw();
} else if ( flags & EE_BUTTON_WUMASK ) {
mScroll.y -= PixelDensity::dpToPx( mMouseWheelScroll );
mScroll.y = eemax( mScroll.y, 0.f );
mScroll.y = eefloor( eemax( mScroll.y, 0.f ) );
invalidateDraw();
}
return UIWidget::onMouseUp( position, flags );
@@ -443,6 +484,16 @@ Uint32 UICodeEditor::onMouseDoubleClick( const Vector2i&, const Uint32& flags )
return 1;
}
Uint32 UICodeEditor::onMouseOver( const Vector2i& position, const Uint32& flags ) {
getUISceneNode()->setCursor( Cursor::IBeam );
return UIWidget::onMouseOver( position, flags );
}
Uint32 UICodeEditor::onMouseLeave( const Vector2i& Pos, const Uint32& Flags ) {
getUISceneNode()->setCursor( Cursor::Arrow );
return UIWidget::onMouseLeave( Pos, Flags );
}
void UICodeEditor::onSizeChange() {
UIWidget::onSizeChange();
mScroll = {0, 0};
@@ -492,7 +543,7 @@ void UICodeEditor::scrollToMakeVisible( const TextPosition& position ) {
Float min = lineHeight * ( eemax<Float>( 0, position.line() - 1 ) );
Float max = lineHeight * ( position.line() + 2 ) - mSize.getHeight();
mScroll.y = eemin( mScroll.y, min );
mScroll.y = eemax( mScroll.y, max );
mScroll.y = eefloor( eemax( mScroll.y, max ) );
invalidateDraw();
}
@@ -539,7 +590,7 @@ Float UICodeEditor::getCharacterSize() const {
}
Float UICodeEditor::getGlyphWidth() const {
return mFont->getGlyph( KEY_SPACE, getCharacterSize(), false ).advance;
return mFont->getGlyph( ' ', getCharacterSize(), false ).advance;
}
void UICodeEditor::updateLastColumnOffset() {

View File

@@ -68,6 +68,9 @@ void Input::processEvent( InputEvent* Event ) {
break;
}
case InputEvent::KeyUp: {
if ( Event->key.keysym.mod != eeINDEX_NOT_FOUND )
mInputMod = Event->key.keysym.mod;
if ( Event->key.keysym.sym > EE_KEYS_NUM )
break;