Added vertical scrollbar to the UICodeEditor.

Some minor fixes.
This commit is contained in:
Martín Lucas Golini
2020-05-24 06:14:54 -03:00
parent 2c05f3f579
commit 472edd7e41
12 changed files with 142 additions and 54 deletions

View File

@@ -24,6 +24,8 @@ class EE_API TextDocument {
virtual void onDocumentTextChanged() = 0;
virtual void onDocumentCursorChange( const TextPosition& ) = 0;
virtual void onDocumentSelectionChange( const TextRange& ) = 0;
virtual void onDocumentLineCountChange( const size_t& lastCount,
const size_t& newCount ) = 0;
};
enum IndentType { IndentSpaces, IndentTabs };
@@ -208,6 +210,7 @@ class EE_API TextDocument {
const std::string& getFilePath() const;
bool isDirty() const;
protected:
friend class UndoStack;
UndoStack mUndoStack;
@@ -232,6 +235,8 @@ class EE_API TextDocument {
void notifySelectionChanged();
void notifyLineCountChanged( const size_t& lastCount, const size_t& newCount );
void insertAtStartOfSelectedLines( String text, bool skipEmpty );
void removeFromStartOfSelectedLines( String text, bool skipEmpty );

View File

@@ -16,6 +16,8 @@ class Font;
namespace EE { namespace UI {
class UIScrollBar;
class EE_API UICodeEditor : public UIWidget, public TextDocument::Client {
public:
static UICodeEditor* New();
@@ -127,6 +129,7 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client {
Color mCaretColor;
SyntaxColorScheme mColorScheme;
SyntaxHighlighter mHighlighter;
UIScrollBar* mVScrollBar;
void invalidateEditor();
@@ -160,12 +163,16 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client {
void onDocumentSelectionChange( const TextRange& );
void onDocumentLineCountChange( const size_t& lastCount, const size_t& newCount );
std::pair<int, int> getVisibleLineRange();
int getVisibleLinesCount();
void scrollToMakeVisible( const TextPosition& position );
void setScrollY( const Float& val, bool emmitEvent = true );
Float getXOffsetCol( const TextPosition& position ) const;
Float getTextWidth( const String& text ) const;
@@ -187,6 +194,8 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client {
Vector2f getViewPortLineCount() const;
Sizef getMaxScroll() const;
void updateScrollBar();
};
}} // namespace EE::UI

View File

@@ -24,7 +24,7 @@ class EE_API UIScrollBar : public UIWidget {
virtual bool isType( const Uint32& type ) const;
virtual void setValue( Float Val );
virtual void setValue( Float val, const bool& emmitEvent = true );
const Float& getValue() const;
@@ -81,6 +81,8 @@ class EE_API UIScrollBar : public UIWidget {
virtual void onAutoSize();
virtual Uint32 onMouseOver( const Vector2i& position, const Uint32& flags );
void adjustChilds();
void onValueChangeCb( const Event* Event );

View File

@@ -30,7 +30,7 @@ class EE_API UISlider : public UIWidget {
virtual void setTheme( UITheme* Theme );
virtual void setValue( Float Val );
virtual void setValue( Float val, bool emmitEvent = true );
const Float& getValue() const;

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 4.12.0, 2020-05-24T02:52:40. -->
<!-- Written by QtCreator 4.12.0, 2020-05-24T06:14:21. -->
<qtcreator>
<data>
<variable>EnvironmentId</variable>
@@ -1847,7 +1847,7 @@
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">eepp-codeeditor-debug</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.CustomExecutableRunConfiguration</value>
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey"></value>
<value type="QString" key="RunConfiguration.Arguments">/home/downloads/codeeditor.cpp</value>
<value type="QString" key="RunConfiguration.Arguments">../src/tools/codeeditor/codeeditor.cpp</value>
<value type="bool" key="RunConfiguration.Arguments.multi">false</value>
<value type="QString" key="RunConfiguration.OverrideDebuggerStartup"></value>
<value type="bool" key="RunConfiguration.UseCppDebugger">true</value>

View File

@@ -224,7 +224,7 @@ SyntaxDefinitionManager::SyntaxDefinitionManager() {
{
{{"//.-\n"}, "comment"},
{{"/%*", "%*/"}, "comment"},
{{"#", "[^\\]\n"}, "comment"},
{{"#", "[^\\]\n"}, "keyword2"},
{{"\"", "\"", "\\"}, "string"},
{{"'", "'", "\\"}, "string"},
{{"-?0x%x+"}, "number"},

View File

@@ -208,6 +208,7 @@ TextPosition TextDocument::insert( const TextPosition& position, const String& t
TextPosition TextDocument::insert( const TextPosition& position, const String& text,
UndoStackContainer& undoStack, const Time& time ) {
TextPosition cursor = position;
size_t lineCount = mLines.size();
for ( size_t i = 0; i < text.length(); ++i ) {
cursor = insert( cursor, text[i] );
@@ -218,6 +219,10 @@ TextPosition TextDocument::insert( const TextPosition& position, const String& t
notifyTextChanged();
if ( lineCount != mLines.size() ) {
notifyLineCountChanged( lineCount, mLines.size() );
}
return cursor;
}
@@ -261,11 +266,15 @@ void TextDocument::remove( TextPosition position ) {
}
void TextDocument::remove( TextRange range ) {
size_t lineCount = mLines.size();
mUndoStack.clearRedoStack();
range = range.normalized();
range.setStart( sanitizePosition( range.start() ) );
range.setEnd( sanitizePosition( range.end() ) );
remove( range, mUndoStack.getUndoStackContainer(), mTimer.getElapsedTime() );
if ( lineCount != mLines.size() ) {
notifyLineCountChanged( lineCount, mLines.size() );
}
}
void TextDocument::remove( TextRange range, UndoStackContainer& undoStack, const Time& time ) {
@@ -819,6 +828,12 @@ void TextDocument::notifySelectionChanged() {
}
}
void TextDocument::notifyLineCountChanged( const size_t& lastCount, const size_t& newCount ) {
for ( auto& client : mClients ) {
client->onDocumentLineCountChange( lastCount, newCount );
}
}
TextDocument::Client::~Client() {}
}}} // namespace EE::UI::Doc

View File

@@ -5,6 +5,7 @@
#include <eepp/ui/doc/syntaxdefinitionmanager.hpp>
#include <eepp/ui/uicodeeditor.hpp>
#include <eepp/ui/uiscenenode.hpp>
#include <eepp/ui/uiscrollbar.hpp>
#include <eepp/window/clipboard.hpp>
#include <eepp/window/input.hpp>
#include <eepp/window/window.hpp>
@@ -38,6 +39,14 @@ UICodeEditor::UICodeEditor() :
setFontColor( Color::fromString( "#e1e1e6" ) );
mFontStyleConfig.setFontSelectionBackColor( Color::fromString( "#48484f" ) );
mVScrollBar = UIScrollBar::NewVertical();
mVScrollBar->setParent( this );
mVScrollBar->addEventListener( Event::OnSizeChange,
[&]( const Event* ) { updateScrollBar(); } );
mVScrollBar->addEventListener( Event::OnValueChange, [&]( const Event* ) {
setScrollY( mVScrollBar->getValue() * getMaxScroll().y, false );
} );
if ( NULL == mFont )
eePRINTL( "A monospace font must be loaded to be able to use the code editor.\nTry loading "
"a font with the name \"monospace\"" );
@@ -418,8 +427,7 @@ Uint32 UICodeEditor::onKeyDown( const KeyEvent& event ) {
}
case KEY_UP: {
if ( event.getMod() & KEYMOD_CTRL ) {
mScroll.y = eefloor( eemax<Float>( 0, mScroll.y - getLineHeight() ) );
invalidateDraw();
setScrollY( mScroll.y - getLineHeight() );
} else if ( event.getMod() & KEYMOD_SHIFT ) {
mDoc.selectToPreviousLine( mLastColOffset );
} else {
@@ -429,9 +437,7 @@ Uint32 UICodeEditor::onKeyDown( const KeyEvent& event ) {
}
case KEY_DOWN: {
if ( event.getMod() & KEYMOD_CTRL ) {
mScroll.y =
eefloor( eemin<Float>( getMaxScroll().y, mScroll.y + getLineHeight() ) );
invalidateDraw();
setScrollY( mScroll.y + getLineHeight() );
} else if ( event.getMod() & KEYMOD_SHIFT ) {
mDoc.selectToNextLine( mLastColOffset );
} else {
@@ -470,9 +476,8 @@ Uint32 UICodeEditor::onKeyDown( const KeyEvent& event ) {
mDoc.selectToStartOfLine();
updateLastColumnOffset();
} else if ( event.getMod() & KEYMOD_CTRL ) {
mScroll.y = 0;
setScrollY( 0 );
mDoc.setSelection( {0, 0} );
invalidateDraw();
} else {
mDoc.setSelection( mDoc.startOfLine( mDoc.getSelection().start() ) );
updateLastColumnOffset();
@@ -484,7 +489,7 @@ Uint32 UICodeEditor::onKeyDown( const KeyEvent& event ) {
mDoc.selectToEndOfLine();
updateLastColumnOffset();
} else if ( event.getMod() & KEYMOD_CTRL ) {
mScroll.y = getMaxScroll().y;
setScrollY( getMaxScroll().y );
mDoc.setSelection(
{static_cast<Int64>( mDoc.linesCount() - 1 ),
static_cast<Int64>( mDoc.line( mDoc.linesCount() - 1 ).length() )} );
@@ -612,8 +617,8 @@ TextPosition UICodeEditor::resolveScreenPosition( const Vector2f& position ) con
}
Vector2f UICodeEditor::getViewPortLineCount() const {
return Vector2f( eefloor( mSize.getWidth() / getGlyphWidth() ),
eefloor( mSize.getHeight() / getLineHeight() ) );
return Vector2f( eefloor( ( mSize.getWidth() - mRealPadding.Left ) / getGlyphWidth() ),
eefloor( ( mSize.getHeight() - mRealPadding.Top ) / getLineHeight() ) );
}
Sizef UICodeEditor::getMaxScroll() const {
@@ -626,7 +631,8 @@ Sizef UICodeEditor::getMaxScroll() const {
}
Uint32 UICodeEditor::onMouseDown( const Vector2i& position, const Uint32& flags ) {
if ( NULL != mFont && !mMouseDown && ( flags & EE_BUTTON_LMASK ) ) {
if ( !getUISceneNode()->getEventDispatcher()->isNodeDragging() && NULL != mFont &&
!mMouseDown && ( flags & EE_BUTTON_LMASK ) ) {
mMouseDown = true;
Input* input = getUISceneNode()->getWindow()->getInput();
if ( input->isShiftPressed() ) {
@@ -639,7 +645,8 @@ Uint32 UICodeEditor::onMouseDown( const Vector2i& position, const Uint32& flags
}
Uint32 UICodeEditor::onMouseMove( const Vector2i& position, const Uint32& flags ) {
if ( NULL != mFont && mMouseDown && ( flags & EE_BUTTON_LMASK ) ) {
if ( !getUISceneNode()->getEventDispatcher()->isNodeDragging() && hasFocus() && NULL != mFont &&
mMouseDown && ( flags & EE_BUTTON_LMASK ) ) {
TextRange selection = mDoc.getSelection();
selection.setStart( resolveScreenPosition( position.asFloat() ) );
mDoc.setSelection( selection );
@@ -657,16 +664,14 @@ Uint32 UICodeEditor::onMouseUp( const Vector2i& position, const Uint32& flags )
if ( getUISceneNode()->getWindow()->getInput()->isControlPressed() ) {
mFontStyleConfig.CharacterSize = eemax<Float>( 4, mFontStyleConfig.CharacterSize - 1 );
} else {
mScroll.y += PixelDensity::dpToPx( mMouseWheelScroll );
mScroll.y = eefloor( eemin( mScroll.y, getMaxScroll().y ) );
setScrollY( mScroll.y + PixelDensity::dpToPx( mMouseWheelScroll ) );
}
invalidateDraw();
} else if ( flags & EE_BUTTON_WUMASK ) {
if ( getUISceneNode()->getWindow()->getInput()->isControlPressed() ) {
mFontStyleConfig.CharacterSize = eemin<Float>( 96, mFontStyleConfig.CharacterSize + 1 );
} else {
mScroll.y -= PixelDensity::dpToPx( mMouseWheelScroll );
mScroll.y = eefloor( eemax( mScroll.y, 0.f ) );
setScrollY( mScroll.y - PixelDensity::dpToPx( mMouseWheelScroll ) );
}
invalidateDraw();
}
@@ -688,9 +693,9 @@ Uint32 UICodeEditor::onMouseOver( const Vector2i& position, const Uint32& flags
return UIWidget::onMouseOver( position, flags );
}
Uint32 UICodeEditor::onMouseLeave( const Vector2i& Pos, const Uint32& Flags ) {
Uint32 UICodeEditor::onMouseLeave( const Vector2i& position, const Uint32& flags ) {
getUISceneNode()->setCursor( Cursor::Arrow );
return UIWidget::onMouseLeave( Pos, Flags );
return UIWidget::onMouseLeave( position, flags );
}
void UICodeEditor::onSizeChange() {
@@ -704,8 +709,20 @@ void UICodeEditor::onPaddingChange() {
invalidateEditor();
}
void UICodeEditor::updateScrollBar() {
mVScrollBar->setPixelsSize( 0, mSize.getHeight() );
mVScrollBar->setPixelsPosition( mSize.getWidth() - mVScrollBar->getPixelsSize().getWidth(), 0 );
int notVisibleLineCount = (int)mDoc.linesCount() - (int)getViewPortLineCount().y;
mVScrollBar->setPageStep( getViewPortLineCount().y / (float)mDoc.linesCount() );
mVScrollBar->setClickStep( 0.2f );
mVScrollBar->setEnabled( notVisibleLineCount > 0 );
mVScrollBar->setVisible( notVisibleLineCount > 0 );
setScrollY( mScroll.y );
}
void UICodeEditor::updateEditor() {
scrollToMakeVisible( mDoc.getSelection().start() );
updateScrollBar();
mDirtyEditor = false;
}
@@ -724,6 +741,10 @@ void UICodeEditor::onDocumentSelectionChange( const Doc::TextRange& ) {
invalidateDraw();
}
void UICodeEditor::onDocumentLineCountChange( const size_t&, const size_t& ) {
updateScrollBar();
}
std::pair<int, int> UICodeEditor::getVisibleLineRange() {
Float lineHeight = getLineHeight();
Float minLine = eemax( 0.f, eefloor( mScroll.y / lineHeight ) );
@@ -742,8 +763,9 @@ void UICodeEditor::scrollToMakeVisible( const TextPosition& position ) {
Float lineHeight = getLineHeight();
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 = eefloor( eemax( mScroll.y, max ) );
Float scrollY = eemin( mScroll.y, min );
scrollY = eefloor( eemax( mScroll.y, max ) );
setScrollY( scrollY );
// Horizontal Scroll
Float offsetX = getXOffsetCol( position );
@@ -758,6 +780,16 @@ void UICodeEditor::scrollToMakeVisible( const TextPosition& position ) {
invalidateDraw();
}
void UICodeEditor::setScrollY( const Float& val, bool emmitEvent ) {
Float oldVal = mScroll.y;
mScroll.y = eefloor( eeclamp<Float>( val, 0, getMaxScroll().y ) );
if ( oldVal != mScroll.y ) {
invalidateDraw();
if ( emmitEvent )
mVScrollBar->setValue( mScroll.y / getMaxScroll().y, false );
}
}
Float UICodeEditor::getXOffsetCol( const TextPosition& position ) const {
const String& line = mDoc.line( position.line() );
Float glyphWidth = getGlyphWidth();

View File

@@ -143,12 +143,14 @@ void UINode::setInternalSize( const Sizef& size ) {
if ( s.y < mMinSize.y )
s.y = mMinSize.y;
mDpSize = size;
mSize = PixelDensity::dpToPx( s );
mNodeFlags |= NODE_FLAG_POLYGON_DIRTY;
updateCenter();
sendCommonEvent( Event::OnSizeChange );
invalidateDraw();
if ( s != mDpSize ) {
mDpSize = size;
mSize = PixelDensity::dpToPx( s );
mNodeFlags |= NODE_FLAG_POLYGON_DIRTY;
updateCenter();
sendCommonEvent( Event::OnSizeChange );
invalidateDraw();
}
}
void UINode::setInternalPixelsSize( const Sizef& size ) {
@@ -161,12 +163,14 @@ void UINode::setInternalPixelsSize( const Sizef& size ) {
if ( s.y < pMinSize.y )
s.y = pMinSize.y;
mDpSize = PixelDensity::pxToDp( s ).ceil();
mSize = s;
mNodeFlags |= NODE_FLAG_POLYGON_DIRTY;
updateCenter();
sendCommonEvent( Event::OnSizeChange );
invalidateDraw();
if ( s != mSize ) {
mDpSize = PixelDensity::pxToDp( s ).ceil();
mSize = s;
mNodeFlags |= NODE_FLAG_POLYGON_DIRTY;
updateCenter();
sendCommonEvent( Event::OnSizeChange );
invalidateDraw();
}
}
Node* UINode::setSize( const Sizef& Size ) {

View File

@@ -169,6 +169,10 @@ void UIScrollBar::onAutoSize() {
}
}
Uint32 UIScrollBar::onMouseOver( const Vector2i&, const Uint32& ) {
return 1;
}
void UIScrollBar::onSizeChange() {
onAutoSize();
@@ -263,8 +267,8 @@ Uint32 UIScrollBar::onMessage( const NodeMessage* Msg ) {
return 0;
}
void UIScrollBar::setValue( Float Val ) {
mSlider->setValue( Val );
void UIScrollBar::setValue( Float val, const bool& emmitEvent ) {
mSlider->setValue( val, emmitEvent );
}
const Float& UIScrollBar::getValue() const {

View File

@@ -319,17 +319,17 @@ void UISlider::adjustSliderPos() {
mOnPosChange = false;
}
void UISlider::setValue( Float Val ) {
if ( Val < mMinValue )
Val = mMinValue;
if ( Val > mMaxValue )
Val = mMaxValue;
void UISlider::setValue( Float val, bool emmitEvent ) {
if ( val < mMinValue )
val = mMinValue;
if ( val > mMaxValue )
val = mMaxValue;
if ( mValue == Val )
if ( mValue == val )
return;
if ( Val >= mMinValue && Val <= mMaxValue ) {
mValue = Val;
if ( val >= mMinValue && val <= mMaxValue ) {
mValue = val;
if ( !mOnPosChange ) {
mOnPosChange = true;
@@ -339,7 +339,8 @@ void UISlider::setValue( Float Val ) {
mOnPosChange = false;
}
onValueChange();
if ( emmitEvent )
onValueChange();
}
}

View File

@@ -7,6 +7,24 @@ UICodeEditor* codeEditor = NULL;
std::string curFile = "untitled";
const std::string& windowTitle = "eepp - Code Editor";
bool docDirtyState = false;
UIMessageBox* MsgBox = NULL;
bool onCloseRequestCallback( EE::Window::Window* ) {
if ( NULL != codeEditor && codeEditor->isDirty() ) {
MsgBox = UIMessageBox::New(
UIMessageBox::OK_CANCEL,
"Do you really want to close the code editor?\nAll changes will be lost." );
MsgBox->addEventListener( Event::MsgBoxConfirmClick,
[]( const Event* ) { win->close(); } );
MsgBox->addEventListener( Event::OnClose, []( const Event* ) { MsgBox = NULL; } );
MsgBox->setTitle( "Close Code Editor?" );
MsgBox->center();
MsgBox->show();
return false;
} else {
return true;
}
}
void setAppTitle( const std::string& title ) {
win->setTitle( windowTitle + String( title.empty() ? "" : " - " + title ) );
@@ -42,7 +60,8 @@ void mainLoop() {
uiSceneNode->setDrawDebugData( !uiSceneNode->getDrawDebugData() );
}
if ( win->getInput()->isKeyUp( KEY_ESCAPE ) ) {
if ( win->getInput()->isKeyUp( KEY_ESCAPE ) && NULL == MsgBox &&
onCloseRequestCallback( win ) ) {
win->close();
}
@@ -90,6 +109,8 @@ EE_MAIN_FUNC int main( int argc, char* argv[] ) {
ContextSettings( true ) );
if ( win->isOpen() ) {
win->setCloseRequestCallback( cb::Make1( onCloseRequestCallback ) );
win->getInput()->pushCallback( []( InputEvent* event ) {
if ( NULL == codeEditor )
return;
@@ -134,11 +155,6 @@ EE_MAIN_FUNC int main( int argc, char* argv[] ) {
if ( file ) {
loadFileFromPath( file.Get() );
} else {
/*UIMessageBox::New( UIMessageBox::OK,
"To load a file add the file path as a command argument or\n"
"drop any text file into the window." )
->show();*/
}
win->runMainLoop( &mainLoop );