diff --git a/include/eepp/ui.hpp b/include/eepp/ui.hpp index dcc104fe6..c21ba522a 100644 --- a/include/eepp/ui.hpp +++ b/include/eepp/ui.hpp @@ -59,4 +59,7 @@ #include #include +#include +#include + #endif diff --git a/include/eepp/ui/doc/textdocument.hpp b/include/eepp/ui/doc/textdocument.hpp index c162ffb76..371d6d1c0 100644 --- a/include/eepp/ui/doc/textdocument.hpp +++ b/include/eepp/ui/doc/textdocument.hpp @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -35,7 +36,11 @@ class EE_API TextDocument { void loadFromPath( const std::string& path ); - void save( const std::string& path ); + bool save(); + + bool save( const std::string& path, const bool& utf8bom = false ); + + bool save( IOStreamFile& stream, const bool& utf8bom = false ); const std::string getFilename() const; @@ -194,18 +199,32 @@ class EE_API TextDocument { const SyntaxDefinition& getSyntaxDefinition() const; + Uint64 getCurrentChangeId() const; + + const std::string& getDefaultFileName() const; + + void setDefaultFileName( const std::string& defaultFileName ); + + const std::string& getFilePath() const; + + bool isDirty() const; protected: friend class UndoStack; UndoStack mUndoStack; - std::string mFilename; + std::string mFilePath; std::vector mLines; TextRange mSelection; std::unordered_set mClients; - bool mIsCLRF; + bool mIsCLRF{false}; + bool mIsBOM{false}; Uint32 mTabWidth{4}; IndentType mIndentType{IndentTabs}; Clock mTimer; SyntaxDefinition mSyntaxDefinition; + std::string mDefaultFileName; + Uint64 mCleanChangeId; + + void cleanChangeId(); void notifyTextChanged(); diff --git a/include/eepp/ui/doc/undostack.hpp b/include/eepp/ui/doc/undostack.hpp index 691e1f602..a06842a9e 100644 --- a/include/eepp/ui/doc/undostack.hpp +++ b/include/eepp/ui/doc/undostack.hpp @@ -18,22 +18,25 @@ enum class TextUndoCommandType { Insert, Remove, Selection }; class EE_API TextUndoCommand { public: - TextUndoCommand( const TextUndoCommandType& type, const Time& timestamp ); + TextUndoCommand( const Uint64& id, const TextUndoCommandType& type, const Time& timestamp ); virtual ~TextUndoCommand(); + const Uint64& getId() const; + const TextUndoCommandType& getType() const; const Time& getTimestamp() const; protected: + Uint64 mId; TextUndoCommandType mType; Time mTimestamp; }; class EE_API TextUndoCommandInsert : public TextUndoCommand { public: - TextUndoCommandInsert( const String& text, const TextPosition& position, + TextUndoCommandInsert( const Uint64& id, const String& text, const TextPosition& position, const Time& timestamp ); const String& getText() const; @@ -47,7 +50,7 @@ class EE_API TextUndoCommandInsert : public TextUndoCommand { class EE_API TextUndoCommandRemove : public TextUndoCommand { public: - TextUndoCommandRemove( const TextRange& range, const Time& timestamp ); + TextUndoCommandRemove( const Uint64& id, const TextRange& range, const Time& timestamp ); const TextRange& getRange() const; @@ -57,7 +60,7 @@ class EE_API TextUndoCommandRemove : public TextUndoCommand { class EE_API TextUndoCommandSelection : public TextUndoCommand { public: - TextUndoCommandSelection( const TextRange& selection, const Time& timestamp ); + TextUndoCommandSelection( const Uint64& id, const TextRange& selection, const Time& timestamp ); const TextRange& getSelection() const; @@ -71,6 +74,8 @@ class EE_API UndoStack { public: UndoStack( TextDocument* owner, const Uint32& maxStackSize = 1000 ); + void clear(); + void clearRedoStack(); void undo(); @@ -83,11 +88,14 @@ class EE_API UndoStack { void setMergeTimeout( const Time& mergeTimeout ); + Uint64 getCurrentChangeId() const; + protected: friend class TextDocument; TextDocument* mDoc; Uint32 mMaxStackSize; + Uint64 mChangeIdCounter; UndoStackContainer mUndoStack; UndoStackContainer mRedoStack; Time mMergeTimeout; diff --git a/include/eepp/ui/uicodeeditor.hpp b/include/eepp/ui/uicodeeditor.hpp index b84074be5..a4819e1c9 100644 --- a/include/eepp/ui/uicodeeditor.hpp +++ b/include/eepp/ui/uicodeeditor.hpp @@ -1,4 +1,4 @@ -#ifndef EE_UI_UICODEEDIT_HPP +#ifndef EE_UI_UICODEEDIT_HPP #define EE_UI_UICODEEDIT_HPP #include @@ -38,6 +38,12 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client { void loadFromFile( const std::string& path ); + bool save(); + + bool save( const std::string& path, const bool& utf8bom = false ); + + bool save( IOStreamFile& stream, const bool& utf8bom = false ); + Font* getFont() const; const UIFontStyleConfig& getFontStyleConfig() const; @@ -92,6 +98,12 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client { void setColorScheme( const SyntaxColorScheme& colorScheme ); + const Doc::TextDocument& getDocument() const; + + Doc::TextDocument& getDocument(); + + bool isDirty() const; + protected: Font* mFont; UIFontStyleConfig mFontStyleConfig; diff --git a/include/eepp/window/engine.hpp b/include/eepp/window/engine.hpp index df66d150a..97eea6f05 100644 --- a/include/eepp/window/engine.hpp +++ b/include/eepp/window/engine.hpp @@ -58,7 +58,7 @@ class EE_API Engine { Resizeable bool Backend SDL2 WinIcon The path to the window icon - WinCaption The window default title + WinTitle The window default title @param iniPath The ini file path @param iniKeyName The ini key name to search the properties @@ -74,7 +74,7 @@ class EE_API Engine { Resizeable bool Backend SDL2 WinIcon The path to the window icon - WinCaption The window default title + WinTitle The window default title @param ini The ini file instance @param iniKeyName The ini key name to search the properties diff --git a/include/eepp/window/window.hpp b/include/eepp/window/window.hpp index dec1a913e..e309b2871 100644 --- a/include/eepp/window/window.hpp +++ b/include/eepp/window/window.hpp @@ -42,7 +42,7 @@ enum class WindowBackend : Uint32 { SDL2, Default }; /** @brief WindowSettings A small class that contains the window settings */ class WindowSettings { public: - inline WindowSettings( Uint32 width, Uint32 height, const std::string& caption = std::string(), + inline WindowSettings( Uint32 width, Uint32 height, const std::string& title = std::string(), Uint32 style = WindowStyle::Default, WindowBackend backend = WindowBackend::Default, Uint32 bpp = 32, const std::string& icon = std::string(), const Float& pixelDensity = 1, @@ -52,7 +52,7 @@ class WindowSettings { Height( height ), BitsPerPixel( bpp ), Icon( icon ), - Caption( caption ), + Title( title ), Backend( backend ), PixelDensity( pixelDensity ), UseScreenKeyboard( useScreenKeyboard ) {} @@ -71,7 +71,7 @@ class WindowSettings { Uint32 Height; Uint32 BitsPerPixel; std::string Icon; - std::string Caption; + std::string Title; WindowBackend Backend; Float PixelDensity; bool UseScreenKeyboard; @@ -164,14 +164,14 @@ class EE_API Window { /** Toogle the screen to Fullscreen, if it's in fullscreen toogle to windowed mode. */ virtual void toggleFullscreen() = 0; - /** Set the window caption */ - virtual void setCaption( const std::string& setCaption ) = 0; + /** Set the window title */ + virtual void setTitle( const std::string& title ) = 0; - /** @return The caption of the titlebar */ - virtual std::string getCaption(); + /** @return The window title*/ + virtual std::string getTitle(); /** Set the Window icon */ - virtual bool setIcon( const std::string& Path ) = 0; + virtual bool setIcon( const std::string& path ) = 0; /** This will attempt to iconify/minimize the window. */ virtual void minimize(); diff --git a/premake4.lua b/premake4.lua index 6a0d9930d..41694fe70 100644 --- a/premake4.lua +++ b/premake4.lua @@ -1073,6 +1073,13 @@ solution "eepp" files { "src/tools/uieditor/*.cpp" } build_link_configuration( "eepp-UIEditor", true ) + project "eepp-codeeditor" + set_kind() + language "C++" + files { "src/tools/codeeditor/*.cpp" } + includedirs { "src/thirdparty" } + build_link_configuration( "eepp-codeeditor", true ) + project "eepp-texturepacker" kind "ConsoleApp" language "C++" diff --git a/premake5.lua b/premake5.lua index 152e72d33..d8fe6fe0a 100644 --- a/premake5.lua +++ b/premake5.lua @@ -823,6 +823,13 @@ workspace "eepp" filter { "system:not windows", "system:not haiku" } links { "pthread" } + project "eepp-codeeditor" + set_kind() + language "C++" + files { "src/tools/codeeditor/*.cpp" } + incdirs { "src/thirdparty" } + build_link_configuration( "eepp-codeeditor", true ) + project "eepp-texturepacker" kind "ConsoleApp" language "C++" diff --git a/projects/linux/ee.creator.user b/projects/linux/ee.creator.user index d2af17bf5..e18b3c96c 100644 --- a/projects/linux/ee.creator.user +++ b/projects/linux/ee.creator.user @@ -1,6 +1,6 @@ - + EnvironmentId @@ -89,7 +89,7 @@ {6d057187-158a-4883-8d5b-d470a6b6b025} 10 0 - 16 + 19 ../../make/linux @@ -1783,6 +1783,82 @@ %{buildDir}../../../bin/ + + dwarf + + cpu-cycles + + + 250 + + -e + cpu-cycles + --call-graph + dwarf,4096 + -F + 250 + + -F + true + 4096 + false + false + 0 + + true + + false + false + false + false + true + 0.01 + 10 + true + kcachegrind + 1 + 25 + + 1 + true + false + true + valgrind + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + + 2 + + %{buildDir}../../../bin/eepp-codeeditor-debug + eepp-codeeditor-debug + ProjectExplorer.CustomExecutableRunConfiguration + + /home/downloads/codeeditor.cpp + false + + true + false + false + false + true + false + %{buildDir}../../../bin/ + + dwarf @@ -2391,7 +2467,7 @@ %{buildDir}../../../bin/ - 19 + 20 diff --git a/projects/linux/ee.files b/projects/linux/ee.files index 245fbdb00..12a26e6ca 100644 --- a/projects/linux/ee.files +++ b/projects/linux/ee.files @@ -952,6 +952,7 @@ ../../src/thirdparty/SOIL2/src/SOIL2/stbi_pkm.h ../../src/thirdparty/SOIL2/src/SOIL2/stbi_pvr_c.h ../../src/thirdparty/SOIL2/src/SOIL2/stbi_pvr.h +../../src/tools/codeeditor/codeeditor.cpp ../../src/tools/mapeditor/mapeditor.cpp ../../src/tools/textureatlaseditor/textureatlaseditor.cpp ../../src/tools/texturepacker/texturepacker.cpp diff --git a/src/eepp/ui/doc/textdocument.cpp b/src/eepp/ui/doc/textdocument.cpp index dcac56ef1..e9d26ce8a 100644 --- a/src/eepp/ui/doc/textdocument.cpp +++ b/src/eepp/ui/doc/textdocument.cpp @@ -1,4 +1,6 @@ +#include #include +#include #include #include #include @@ -17,16 +19,19 @@ bool TextDocument::isNonWord( String::StringBaseType ch ) { return false; } -TextDocument::TextDocument() : mUndoStack( this ) { +TextDocument::TextDocument() : + mUndoStack( this ), mDefaultFileName( "untitled" ), mCleanChangeId( 0 ) { reset(); } void TextDocument::reset() { - mFilename = "unsaved"; + mFilePath = mDefaultFileName; mSelection.set( {0, 0}, {0, 0} ); mLines.clear(); mLines.emplace_back( String( "\n" ) ); mSyntaxDefinition = SyntaxDefinitionManager::instance()->getPlainStyle(); + mUndoStack.clear(); + cleanChangeId(); notifyTextChanged(); notifyCursorChanged(); notifySelectionChanged(); @@ -35,16 +40,18 @@ void TextDocument::reset() { void TextDocument::loadFromPath( const std::string& path ) { reset(); mLines.clear(); - mFilename = path; + mFilePath = path; mSyntaxDefinition = SyntaxDefinitionManager::instance()->getStyleByExtension( path ); + // TODO: Reimplement load without using getline, since the standard ignores the last \n and + // is inconsistent. std::string line; std::ifstream file( path ); while ( std::getline( file, line ) ) { - std::istringstream iss( line ); if ( mLines.empty() && line.size() >= 3 ) { // Check UTF-8 BOM header if ( (char)0xef == line[0] && (char)0xbb == line[1] && (char)0xbf == line[2] ) { line = line.substr( 3 ); + mIsBOM = true; } } // Check CLRF @@ -54,17 +61,59 @@ void TextDocument::loadFromPath( const std::string& path ) { } mLines.emplace_back( String( line + "\n" ) ); } + if ( mLines.empty() ) { mLines.emplace_back( String( "\n" ) ); - } else if ( mLines[mLines.size() - 1].at( mLines[mLines.size() - 1].size() - 1 ) == '\n' ) { - mLines.emplace_back( String( "\n" ) ); + } else if ( mLines[mLines.size() - 1][mLines[mLines.size() - 1].size() - 1] != '\n' ) { + mLines[mLines.size() - 1] += '\n'; } } -void TextDocument::save( const std::string& ) {} +bool TextDocument::save( const std::string& path, const bool& utf8bom ) { + if ( path.empty() || mDefaultFileName == path ) + return false; + IOStreamFile file( path, "wb" ); + if ( save( file, utf8bom ) ) { + mFilePath = path; + return true; + } + return false; +} + +bool TextDocument::save( IOStreamFile& stream, const bool& utf8bom ) { + if ( !stream.isOpen() || mLines.empty() ) + return false; + char nl = '\n'; + if ( utf8bom ) { + unsigned char bom[] = {0xEF, 0xBB, 0xBF}; + stream.write( (char*)bom, sizeof( bom ) ); + } + 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 ) { + // Last \n is added by the document but it's not part of the document. + utf8.pop_back(); + } + if ( mIsCLRF ) { + utf8[utf8.size() - 1] = '\r'; + stream.write( utf8.c_str(), utf8.size() ); + stream.write( &nl, 1 ); + } else { + stream.write( utf8.c_str(), utf8.size() ); + } + } + cleanChangeId(); + return true; +} + +bool TextDocument::save() { + return save( mFilePath, mIsBOM ); +} const std::string TextDocument::getFilename() const { - return mFilename; + return mFilePath; } void TextDocument::setSelection( TextPosition position ) { @@ -190,7 +239,16 @@ TextPosition TextDocument::insert( TextPosition position, const String::StringBa String newLine = line( position.line() ) .substr( position.column(), line( position.line() ).length() - position.column() ); - line( position.line() ) = line( position.line() ).substr( 0, position.column() ); + String& oldLine = line( position.line() ); + oldLine = 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'; + } + if ( newLine.empty() || newLine[newLine.size() - 1] != '\n' ) { + newLine += '\n'; + } mLines.insert( mLines.begin() + position.line() + 1, std::move( newLine ) ); return {position.line() + 1, 0}; } @@ -225,16 +283,24 @@ void TextDocument::remove( TextRange range, UndoStackContainer& undoStack, const if ( range.start().line() == range.end().line() ) { // Delete within same line. - auto& line = this->line( range.start().line() ); + String& line = this->line( range.start().line() ); bool wholeLineIsSelected = range.start().column() == 0 && range.end().column() == (Int64)line.length(); if ( wholeLineIsSelected ) { - line.clear(); + line = "\n"; } else { auto beforeSelection = line.substr( 0, range.start().column() ); auto afterSelection = - line.substr( range.end().column(), line.length() - range.end().column() ); + !line.empty() + ? line.substr( range.end().column(), line.length() - range.end().column() ) + : ""; + + if ( !beforeSelection.empty() && beforeSelection[beforeSelection.size() - 1] == '\n' ) + beforeSelection = beforeSelection.substr( 0, beforeSelection.size() - 1 ); + if ( afterSelection.empty() || afterSelection[afterSelection.size() - 1] != '\n' ) + afterSelection += '\n'; + line.assign( beforeSelection + afterSelection ); } } else { @@ -243,11 +309,16 @@ void TextDocument::remove( TextRange range, UndoStackContainer& undoStack, const auto& firstLine = line( range.start().line() ); auto& secondLine = line( range.end().line() ); 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'; - } + auto afterSelection = !secondLine.empty() + ? secondLine.substr( range.end().column(), + secondLine.length() - range.end().column() ) + : ""; + + if ( !beforeSelection.empty() && beforeSelection[beforeSelection.size() - 1] == '\n' ) + beforeSelection = beforeSelection.substr( 0, beforeSelection.size() - 1 ); + if ( afterSelection.empty() || afterSelection[afterSelection.size() - 1] != '\n' ) + afterSelection += '\n'; + firstLine.assign( beforeSelection + afterSelection ); mLines.erase( mLines.begin() + range.end().line() ); } @@ -706,6 +777,30 @@ const SyntaxDefinition& TextDocument::getSyntaxDefinition() const { return mSyntaxDefinition; } +Uint64 TextDocument::getCurrentChangeId() const { + return mUndoStack.getCurrentChangeId(); +} + +const std::string& TextDocument::getDefaultFileName() const { + return mDefaultFileName; +} + +void TextDocument::setDefaultFileName( const std::string& defaultFileName ) { + mDefaultFileName = defaultFileName; +} + +const std::string& TextDocument::getFilePath() const { + return mFilePath; +} + +bool TextDocument::isDirty() const { + return mCleanChangeId != getCurrentChangeId(); +} + +void TextDocument::cleanChangeId() { + mCleanChangeId = getCurrentChangeId(); +} + void TextDocument::notifyTextChanged() { for ( auto& client : mClients ) { client->onDocumentTextChanged(); diff --git a/src/eepp/ui/doc/undostack.cpp b/src/eepp/ui/doc/undostack.cpp index 97e7c90bd..aa73a46f4 100644 --- a/src/eepp/ui/doc/undostack.cpp +++ b/src/eepp/ui/doc/undostack.cpp @@ -5,11 +5,16 @@ 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 Uint64& id, const TextUndoCommandType& type, + const Time& timestamp ) : + mId( id ), mType( type ), mTimestamp( timestamp ) {} TextUndoCommand::~TextUndoCommand() {} +const Uint64& TextUndoCommand::getId() const { + return mId; +} + const TextUndoCommandType& TextUndoCommand::getType() const { return mType; } @@ -18,9 +23,10 @@ const Time& TextUndoCommand::getTimestamp() const { return mTimestamp; } -TextUndoCommandInsert::TextUndoCommandInsert( const String& text, const TextPosition& position, +TextUndoCommandInsert::TextUndoCommandInsert( const Uint64& id, const String& text, + const TextPosition& position, const Time& timestamp ) : - TextUndoCommand( TextUndoCommandType::Insert, timestamp ), + TextUndoCommand( id, TextUndoCommandType::Insert, timestamp ), mText( text ), mPosition( position ) {} @@ -32,23 +38,32 @@ const TextPosition& TextUndoCommandInsert::getPosition() const { return mPosition; } -TextUndoCommandRemove::TextUndoCommandRemove( const TextRange& range, const Time& timestamp ) : - TextUndoCommand( TextUndoCommandType::Remove, timestamp ), mRange( range ) {} +TextUndoCommandRemove::TextUndoCommandRemove( const Uint64& id, const TextRange& range, + const Time& timestamp ) : + TextUndoCommand( id, TextUndoCommandType::Remove, timestamp ), mRange( range ) {} const TextRange& TextUndoCommandRemove::getRange() const { return mRange; } -TextUndoCommandSelection::TextUndoCommandSelection( const TextRange& selection, +TextUndoCommandSelection::TextUndoCommandSelection( const Uint64& id, const TextRange& selection, const Time& timestamp ) : - TextUndoCommand( TextUndoCommandType::Selection, timestamp ), mSelection( selection ) {} + TextUndoCommand( id, 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 ) ) {} + mDoc( owner ), + mMaxStackSize( maxStackSize ), + mChangeIdCounter( 0 ), + mMergeTimeout( Milliseconds( 300.f ) ) {} + +void UndoStack::clear() { + mUndoStack.clear(); + clearRedoStack(); +} void UndoStack::clearRedoStack() { mRedoStack.clear(); @@ -63,17 +78,20 @@ void UndoStack::pushUndo( UndoStackContainer& undoStack, std::unique_ptr( string, position, time ) ); + pushUndo( undoStack, std::make_unique( ++mChangeIdCounter, string, + position, time ) ); } void UndoStack::pushRemove( UndoStackContainer& undoStack, const TextRange& range, const Time& time ) { - pushUndo( undoStack, std::make_unique( range, time ) ); + pushUndo( undoStack, + std::make_unique( ++mChangeIdCounter, range, time ) ); } void UndoStack::pushSelection( UndoStackContainer& undoStack, const TextRange& selection, const Time& time ) { - pushUndo( undoStack, std::make_unique( selection, time ) ); + pushUndo( undoStack, + std::make_unique( ++mChangeIdCounter, selection, time ) ); } void UndoStack::popUndo( UndoStackContainer& undoStack, UndoStackContainer& redoStack ) { @@ -130,6 +148,12 @@ void UndoStack::setMergeTimeout( const Time& mergeTimeout ) { mMergeTimeout = mergeTimeout; } +Uint64 UndoStack::getCurrentChangeId() const { + if ( mUndoStack.empty() ) + return 0; + return mUndoStack.back()->getId(); +} + UndoStackContainer& UndoStack::getUndoStackContainer() { return mUndoStack; } diff --git a/src/eepp/ui/uicodeeditor.cpp b/src/eepp/ui/uicodeeditor.cpp index 360e49708..ef75bc2f3 100644 --- a/src/eepp/ui/uicodeeditor.cpp +++ b/src/eepp/ui/uicodeeditor.cpp @@ -155,7 +155,7 @@ void UICodeEditor::draw() { primitives.drawRectangle( Rectf( screenStart, Sizef( lineNumberWidth, mSize.getHeight() ) ) ); for ( int i = lineRange.first; i <= lineRange.second; i++ ) { - Text line( String( String::toStr( i ) ).padLeft( lineNumberDigits, ' ' ), mFont, + Text line( String( String::toStr( i + 1 ) ).padLeft( lineNumberDigits, ' ' ), mFont, charSize ); line.setStyleConfig( mFontStyleConfig ); line.setColor( mLineNumberFontColor ); @@ -189,6 +189,18 @@ void UICodeEditor::loadFromFile( const std::string& path ) { invalidateEditor(); } +bool UICodeEditor::save() { + return mDoc.save(); +} + +bool UICodeEditor::save( const std::string& path, const bool& utf8bom ) { + return mDoc.save( path, utf8bom ); +} + +bool UICodeEditor::save( IOStreamFile& stream, const bool& utf8bom ) { + return mDoc.save( stream, utf8bom ); +} + Font* UICodeEditor::getFont() const { return mFont; } @@ -341,6 +353,18 @@ void UICodeEditor::setColorScheme( const SyntaxColorScheme& colorScheme ) { invalidateDraw(); } +const TextDocument& UICodeEditor::getDocument() const { + return mDoc; +} + +TextDocument& UICodeEditor::getDocument() { + return mDoc; +} + +bool UICodeEditor::isDirty() const { + return mDoc.isDirty(); +} + void UICodeEditor::invalidateEditor() { mDirtyEditor = true; } diff --git a/src/eepp/window/backend/SDL2/windowsdl2.cpp b/src/eepp/window/backend/SDL2/windowsdl2.cpp index b1a3f38d3..09eb2f976 100644 --- a/src/eepp/window/backend/SDL2/windowsdl2.cpp +++ b/src/eepp/window/backend/SDL2/windowsdl2.cpp @@ -268,7 +268,7 @@ bool WindowSDL::create( WindowSettings Settings, ContextSettings Context ) { mWindow.WindowConfig.Height *= mWindow.WindowConfig.PixelDensity; #endif - mSDLWindow = SDL_CreateWindow( mWindow.WindowConfig.Caption.c_str(), SDL_WINDOWPOS_UNDEFINED, + mSDLWindow = SDL_CreateWindow( mWindow.WindowConfig.Title.c_str(), SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, mWindow.WindowConfig.Width, mWindow.WindowConfig.Height, tmpFlags ); @@ -381,7 +381,7 @@ bool WindowSDL::create( WindowSettings Settings, ContextSettings Context ) { getMainContext(); - setCaption( mWindow.WindowConfig.Caption ); + setTitle( mWindow.WindowConfig.Title ); createView(); @@ -487,10 +487,10 @@ void WindowSDL::toggleFullscreen() { } } -void WindowSDL::setCaption( const std::string& Caption ) { - mWindow.WindowConfig.Caption = Caption; +void WindowSDL::setTitle( const std::string& title ) { + mWindow.WindowConfig.Title = title; - SDL_SetWindowTitle( mSDLWindow, Caption.c_str() ); + SDL_SetWindowTitle( mSDLWindow, title.c_str() ); } bool WindowSDL::isActive() { diff --git a/src/eepp/window/backend/SDL2/windowsdl2.hpp b/src/eepp/window/backend/SDL2/windowsdl2.hpp index a1faa8285..80844df03 100644 --- a/src/eepp/window/backend/SDL2/windowsdl2.hpp +++ b/src/eepp/window/backend/SDL2/windowsdl2.hpp @@ -27,7 +27,7 @@ class EE_API WindowSDL : public Window { void toggleFullscreen(); - void setCaption( const std::string& setCaption ); + void setTitle( const std::string& title ); bool setIcon( const std::string& Path ); diff --git a/src/eepp/window/engine.cpp b/src/eepp/window/engine.cpp index 56c4deda6..aaa5a3fe7 100644 --- a/src/eepp/window/engine.cpp +++ b/src/eepp/window/engine.cpp @@ -284,10 +284,10 @@ WindowSettings Engine::createWindowSettings( IniFile* ini, std::string iniKeyNam if ( Resizeable ) Style |= WindowStyle::Resize; - std::string Icon = ini->getValue( iniKeyName, "WinIcon", "" ); - std::string Caption = ini->getValue( iniKeyName, "WinCaption", "" ); + std::string icon = ini->getValue( iniKeyName, "WinIcon", "" ); + std::string title = ini->getValue( iniKeyName, "WinTitle", "" ); - WindowSettings WinSettings( Width, Height, Caption, Style, winBackend, BitColor, Icon, + WindowSettings WinSettings( Width, Height, title, Style, winBackend, BitColor, icon, pixelDensity, useScreenKeyboard ); return WinSettings; diff --git a/src/eepp/window/window.cpp b/src/eepp/window/window.cpp index c95f1c02b..dd5cf67e3 100644 --- a/src/eepp/window/window.cpp +++ b/src/eepp/window/window.cpp @@ -468,8 +468,8 @@ void Window::onCloseRequest() { close(); } -std::string Window::getCaption() { - return mWindow.WindowConfig.Caption; +std::string Window::getTitle() { + return mWindow.WindowConfig.Title; } eeWindowContex Window::getContext() const { diff --git a/src/examples/physics/physics.cpp b/src/examples/physics/physics.cpp index c266534ef..4adb7972b 100644 --- a/src/examples/physics/physics.cpp +++ b/src/examples/physics/physics.cpp @@ -129,7 +129,7 @@ void demo1Create() { createJointAndBody(); - mWindow->setCaption( "eepp - Physics - Logo Smash" ); + mWindow->setTitle( "eepp - Physics - Logo Smash" ); mSpace = Physics::Space::New(); mSpace->setIterations( 1 ); @@ -184,7 +184,7 @@ void demo2Create() { createJointAndBody(); - mWindow->setCaption( "eepp - Physics - Pyramid Stack" ); + mWindow->setTitle( "eepp - Physics - Pyramid Stack" ); Shape::resetShapeIdCounter(); @@ -313,7 +313,7 @@ void demo3Create() { createJointAndBody(); - mWindow->setCaption( "eepp - Physics - Sensor" ); + mWindow->setTitle( "eepp - Physics - Sensor" ); Shape::resetShapeIdCounter(); @@ -478,7 +478,7 @@ void demo4Create() { createJointAndBody(); - mWindow->setCaption( "eepp - Physics - Sticky collisions using the Arbiter data pointer." ); + mWindow->setTitle( "eepp - Physics - Sticky collisions using the Arbiter data pointer." ); mSpace = Space::New(); mSpace->setIterations( 10 ); diff --git a/src/examples/ui_hello_world/ui_hello_world.cpp b/src/examples/ui_hello_world/ui_hello_world.cpp index b563a46a8..5554bad6d 100644 --- a/src/examples/ui_hello_world/ui_hello_world.cpp +++ b/src/examples/ui_hello_world/ui_hello_world.cpp @@ -30,21 +30,14 @@ void mainLoop() { } EE_MAIN_FUNC int main( int, char** ) { - Display* currentDisplay = Engine::instance()->getDisplayManager()->getDisplayIndex( 0 ); - Float pixelDensity = currentDisplay->getPixelDensity(); - - win = Engine::instance()->createWindow( WindowSettings( 1024, 768, "eepp - UI Hello World" ), + win = Engine::instance()->createWindow( WindowSettings( 640, 480, "eepp - UI Hello World" ), ContextSettings( true ) ); if ( win->isOpen() ) { - PixelDensity::setPixelDensity( eemax( win->getScale(), pixelDensity ) ); - // Load a font to use as the default font in our UI. FontTrueType* font = FontTrueType::New( "NotoSans-Regular", "assets/fonts/NotoSans-Regular.ttf" ); - FontTrueType::New( "monospace", "assets/fonts/DejaVuSansMono.ttf" ); - // Create a new scene node to add our widgets. UISceneNode* uiSceneNode = UISceneNode::New(); @@ -68,11 +61,6 @@ EE_MAIN_FUNC int main( int, char** ) { layout_width="match_parent" layout_height="wrap_content" text="Hello, I am a PushButton" /> - )xml"; uiSceneNode->loadLayoutFromString( layout ); @@ -95,16 +83,6 @@ EE_MAIN_FUNC int main( int, char** ) { } )css" ); - UICodeEditor* codeEditor = uiSceneNode->find( "code_edit" ); - codeEditor->setFontSize( 11 ) - //->loadFromFile( "../docs/articles/cssspecification.md" ); - //->loadFromFile( "assets/ui/breeze.css" ); - ->loadFromFile( "assets/layouts/test_widgets.xml" ); - //->loadFromFile( "../src/eepp/ui/doc/textdocument.cpp" ); - //->loadFromFile( "../premake5.lua" ); - //->loadFromFile( "eepp-UIEditor-debug.js" ); - //->loadFromFile( "../docs/doxyrest/sphinx/doxyrest.py" ); - //->loadFromFile( "../projects/emscripten/make.sh" ); win->runMainLoop( &mainLoop ); } diff --git a/src/tests/test_all/test.cpp b/src/tests/test_all/test.cpp index 147867f0a..fd08a8dd7 100644 --- a/src/tests/test_all/test.cpp +++ b/src/tests/test_all/test.cpp @@ -137,7 +137,7 @@ void EETest::init() { if ( NULL != mWindow && mWindow->isOpen() ) { setScreen( StartScreen ); - mWindow->setCaption( "eepp - Test Application" ); + mWindow->setTitle( "eepp - Test Application" ); mWindow->pushResizeCallback( cb::Make1( this, &EETest::onWindowResize ) ); TF = TextureFactory::instance(); diff --git a/src/tools/codeeditor/codeeditor.cpp b/src/tools/codeeditor/codeeditor.cpp new file mode 100644 index 000000000..63418df66 --- /dev/null +++ b/src/tools/codeeditor/codeeditor.cpp @@ -0,0 +1,151 @@ +#include +#include + +EE::Window::Window* win = NULL; +UISceneNode* uiSceneNode = NULL; +UICodeEditor* codeEditor = NULL; +std::string curFile = "untitled"; +const std::string& windowTitle = "eepp - Code Editor"; +bool docDirtyState = false; + +void setAppTitle( const std::string& title ) { + win->setTitle( windowTitle + String( title.empty() ? "" : " - " + title ) ); +} + +void loadFileFromPath( const std::string& path ) { + codeEditor->loadFromFile( path ); + curFile = FileSystem::fileNameFromPath( path ); + setAppTitle( curFile ); +} + +void mainLoop() { + if ( codeEditor->isDirty() != docDirtyState ) { + docDirtyState = codeEditor->isDirty(); + setAppTitle( docDirtyState ? curFile + "*" : curFile ); + } + + win->getInput()->update(); + + if ( win->getInput()->isControlPressed() && win->getInput()->isKeyUp( KEY_S ) ) { + codeEditor->save(); + } + + if ( win->getInput()->isKeyUp( KEY_F6 ) ) { + uiSceneNode->setHighlightOver( !uiSceneNode->getHighlightOver() ); + } + + if ( win->getInput()->isKeyUp( KEY_F7 ) ) { + uiSceneNode->setDrawBoxes( !uiSceneNode->getDrawBoxes() ); + } + + if ( win->getInput()->isKeyUp( KEY_F8 ) ) { + uiSceneNode->setDrawDebugData( !uiSceneNode->getDrawDebugData() ); + } + + if ( win->getInput()->isKeyUp( KEY_ESCAPE ) ) { + win->close(); + } + + // Update the UI scene. + SceneManager::instance()->update(); + + // Check if the UI has been invalidated ( needs redraw ). + if ( SceneManager::instance()->getUISceneNode()->invalidated() ) { + win->clear(); + + // Redraw the UI scene. + SceneManager::instance()->draw(); + + win->display(); + } else { + Sys::sleep( Milliseconds( win->isVisible() ? 1 : 8 ) ); + } +} + +EE_MAIN_FUNC int main( int argc, char* argv[] ) { + args::ArgumentParser parser( "eepp Code Editor" ); + args::Positional file( parser, "file", "The file path" ); + + try { + parser.ParseCLI( argc, argv ); + } catch ( const args::Help& ) { + std::cout << parser; + return EXIT_SUCCESS; + } catch ( const args::ParseError& e ) { + std::cerr << e.what() << std::endl; + std::cerr << parser; + return EXIT_FAILURE; + } catch ( args::ValidationError& e ) { + std::cerr << e.what() << std::endl; + std::cerr << parser; + return EXIT_FAILURE; + } + + Display* currentDisplay = Engine::instance()->getDisplayManager()->getDisplayIndex( 0 ); + Float pixelDensity = currentDisplay->getPixelDensity(); + + win = Engine::instance()->createWindow( + WindowSettings( 1280, 720, windowTitle, WindowStyle::Default, WindowBackend::Default, 32, + "assets/icon/ee.png", pixelDensity ), + ContextSettings( true ) ); + + if ( win->isOpen() ) { + win->getInput()->pushCallback( []( InputEvent* event ) { + if ( NULL == codeEditor ) + return; + + if ( event->Type == InputEvent::FileDropped ) { + loadFileFromPath( event->file.file ); + } else if ( event->Type == InputEvent::TextDropped ) { + codeEditor->getDocument().textInput( event->textdrop.text ); + } + } ); + + PixelDensity::setPixelDensity( eemax( win->getScale(), pixelDensity ) ); + + uiSceneNode = UISceneNode::New(); + + uiSceneNode->getUIThemeManager()->setDefaultFont( + FontTrueType::New( "NotoSans-Regular", "assets/fonts/NotoSans-Regular.ttf" ) ); + + FontTrueType::New( "monospace", "assets/fonts/DejaVuSansMono.ttf" ); + + SceneManager::instance()->add( uiSceneNode ); + + StyleSheetParser cssParser; + if ( cssParser.loadFromFile( "assets/ui/breeze.css" ) ) { + uiSceneNode->setStyleSheet( cssParser.getStyleSheet() ); + } + + std::string layout = R"xml( + + + + )xml"; + uiSceneNode->loadLayoutFromString( layout ); + + uiSceneNode->bind( "code_edit", codeEditor ); + codeEditor->setFontSize( 11 ); + + 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 ); + } + + Engine::destroySingleton(); + MemoryManager::showResults(); + + return EXIT_SUCCESS; +}