diff --git a/README.md b/README.md index 56295d3b8..f62aa68ee 100644 --- a/README.md +++ b/README.md @@ -584,11 +584,15 @@ Probably deprecate the Maps module, since I will focus my efforts on the UI syst * Michael R. P. Ragazzon [RmlUI](https://github.com/mikke89/RmlUi) +* rxi for [lite](https://github.com/rxi/lite) + +* Andreas Kling for [SerenityOS](https://github.com/SerenityOS/serenity) + * Ryan C. Gordon for [mojoAL](https://icculus.org/mojoAL/) * David Reid for [dr_libs](https://github.com/mackron/dr_libs) -* Lion (Lieff) for [minimp3](https://github.com/lieff/minimp3) and more. +* Lion (Lieff) for [minimp3](https://github.com/lieff/minimp3) and more * Lewis Van Winkle for [PlusCallback](https://github.com/codeplea/pluscallback) diff --git a/include/eepp/system/luapatternmatcher.hpp b/include/eepp/system/luapatternmatcher.hpp new file mode 100644 index 000000000..605d1472c --- /dev/null +++ b/include/eepp/system/luapatternmatcher.hpp @@ -0,0 +1,42 @@ +#ifndef EE_SYSTEM_LUAPATTERNMATCHER_HPP +#define EE_SYSTEM_LUAPATTERNMATCHER_HPP + +#include +#include + +namespace EE { namespace System { + +class EE_API LuaPatternMatcher { + public: + struct Match { + int start; + int end; + }; + + LuaPatternMatcher( const std::string& pattern ); + + bool matches( const char* stringSearch, int stringStartOffset, + LuaPatternMatcher::Match* matches, size_t stringLength ); + + bool find( const char* stringSearch, int stringStartOffset, int& startMatch, int& endMatch, + int stringLength = 0, int returnMatchIndex = 0 ); + + bool find( const std::string& s, int offset, int& startMatch, int& endMatch, + int returnedMatchIndex = 0 ) { + return find( s.c_str(), offset, startMatch, endMatch, s.size(), returnedMatchIndex ); + } + + int getNumMatches(); + + bool range( int indexGet, int& startMatch, int& endMatch, + LuaPatternMatcher::Match* returnedMatched ); + + protected: + std::string mErr; + std::string mPattern; + int mMatchNum; +}; + +}} // namespace EE::System + +#endif // EE_SYSTEM_LUAPATTERNMATCHER_HPP diff --git a/include/eepp/ui/doc/syntaxdefinition.hpp b/include/eepp/ui/doc/syntaxdefinition.hpp index eae2cbebd..d19a9dd1a 100644 --- a/include/eepp/ui/doc/syntaxdefinition.hpp +++ b/include/eepp/ui/doc/syntaxdefinition.hpp @@ -32,6 +32,19 @@ class EE_API SyntaxDefinition { std::string getSymbol( const std::string& symbol ) const; + /** Accepts lua patterns and file extensions. */ + SyntaxDefinition& addFileType( const std::string& fileType ); + + SyntaxDefinition& addPattern( const SyntaxPattern& pattern ); + + SyntaxDefinition& addSymbol( const std::string& symbolName, const std::string& typeName ); + + SyntaxDefinition& addSymbols( const std::vector& symbolNames, + const std::string& typeName ); + + /** Sets the comment string used for auto-comment functionality. */ + SyntaxDefinition& setComment( const std::string& comment ); + protected: std::vector mFiles; std::vector mPatterns; diff --git a/include/eepp/ui/doc/syntaxdefinitionmanager.hpp b/include/eepp/ui/doc/syntaxdefinitionmanager.hpp index ba12d5a51..40e562842 100644 --- a/include/eepp/ui/doc/syntaxdefinitionmanager.hpp +++ b/include/eepp/ui/doc/syntaxdefinitionmanager.hpp @@ -11,16 +11,19 @@ namespace EE { namespace UI { namespace Doc { class EE_API SyntaxDefinitionManager { SINGLETON_DECLARE_HEADERS( SyntaxDefinitionManager ) public: - void add( SyntaxDefinition&& syntaxStyle ); + SyntaxDefinition& add( SyntaxDefinition&& syntaxStyle ); const SyntaxDefinition& getPlainStyle() const; - const SyntaxDefinition& getStyleByExtension( const std::string& fileName ) const; + const SyntaxDefinition& getStyleByExtension( const std::string& filePath ) const; + + SyntaxDefinition& getStyleByExtensionRef( const std::string& filePath ); protected: SyntaxDefinitionManager(); std::vector mStyles; + SyntaxDefinition mEmptyDefinition; }; }}} // namespace EE::UI::Doc diff --git a/include/eepp/ui/doc/syntaxhighlighter.hpp b/include/eepp/ui/doc/syntaxhighlighter.hpp index 0a127a4e1..d6e48be3e 100644 --- a/include/eepp/ui/doc/syntaxhighlighter.hpp +++ b/include/eepp/ui/doc/syntaxhighlighter.hpp @@ -20,14 +20,10 @@ class EE_API SyntaxHighlighter { void reset(); - void invalidate( const size_t& line ); - const std::vector& getLine( const size_t& index ); protected: TextDocument* mDoc; - size_t mFirstInvalidLine{0}; - size_t mMaxWantedLine{0}; std::map mLines; TokenizedLine tokenizeLine( const size_t& line, const int& state ); }; diff --git a/include/eepp/ui/doc/textposition.hpp b/include/eepp/ui/doc/textposition.hpp index 418184552..de7ede0f5 100644 --- a/include/eepp/ui/doc/textposition.hpp +++ b/include/eepp/ui/doc/textposition.hpp @@ -2,6 +2,7 @@ #define EE_UI_DOC_TEXTPOSITION_HPP #include +#include #include namespace EE { namespace UI { namespace Doc { @@ -18,9 +19,9 @@ class EE_API TextPosition { Int64 column() const { return mColumn; } - void setLine( size_t line ) { mLine = line; } + void setLine( Int64 line ) { mLine = line; } - void setColumn( size_t column ) { mColumn = column; } + void setColumn( Int64 column ) { mColumn = column; } bool operator==( const TextPosition& other ) const { return mLine == other.mLine && mColumn == other.mColumn; @@ -50,6 +51,8 @@ class EE_API TextPosition { return {mLine - other.line(), mColumn - other.column()}; } + std::string toString() { return String::format( "L%lld,C%lld", mLine, mColumn ); } + private: Int64 mLine{0xffffffff}; Int64 mColumn{0xffffffff}; diff --git a/include/eepp/ui/doc/textrange.hpp b/include/eepp/ui/doc/textrange.hpp index 42a2de0eb..708aac283 100644 --- a/include/eepp/ui/doc/textrange.hpp +++ b/include/eepp/ui/doc/textrange.hpp @@ -54,6 +54,10 @@ class EE_API TextRange { return true; } + std::string toString() { + return String::format( "%s - %s", mStart.toString().c_str(), mEnd.toString().c_str() ); + } + private: TextPosition mStart; TextPosition mEnd; diff --git a/include/eepp/ui/uicodeeditor.hpp b/include/eepp/ui/uicodeeditor.hpp index 8a612d279..762aa9861 100644 --- a/include/eepp/ui/uicodeeditor.hpp +++ b/include/eepp/ui/uicodeeditor.hpp @@ -78,6 +78,8 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client { const Float& getLineNumberPaddingRight() const; + size_t getLineNumberDigits() const; + Float getLineNumberWidth() const; const bool& getShowLineNumber() const; diff --git a/include/eepp/ui/uihelper.hpp b/include/eepp/ui/uihelper.hpp index 578b50e03..5ed9b6835 100644 --- a/include/eepp/ui/uihelper.hpp +++ b/include/eepp/ui/uihelper.hpp @@ -36,7 +36,7 @@ enum UIFlag { UI_TEXT_SELECTION_ENABLED = ( 1 << 19 ), UI_ATTRIBUTE_CHANGED = ( 1 << 20 ), UI_CHECKED = ( 1 << 21 ), - UI_OWNS_CHILDS_POSITION = ( 1 << 22 ) + UI_OWNS_CHILDS_POSITION = ( 1 << 22 ), }; enum UINodeType { diff --git a/include/eepp/ui/uinode.hpp b/include/eepp/ui/uinode.hpp index f4591d65f..1e7f232d5 100644 --- a/include/eepp/ui/uinode.hpp +++ b/include/eepp/ui/uinode.hpp @@ -202,6 +202,8 @@ class EE_API UINode : public Node { void setDragging( const bool& dragging ); + void startDragging( const Vector2f& position ); + bool ownsChildPosition() const; const Vector2f& getDragPoint() const; @@ -256,6 +258,10 @@ class EE_API UINode : public Node { const Sizef& getMinSize() const; + bool isTabStop() const; + + Rectf getLocalDpBounds() const; + protected: Vector2f mDpPos; Sizef mDpSize; diff --git a/include/eepp/ui/uiscenenode.hpp b/include/eepp/ui/uiscenenode.hpp index 50ab1c774..e583af02f 100644 --- a/include/eepp/ui/uiscenenode.hpp +++ b/include/eepp/ui/uiscenenode.hpp @@ -30,6 +30,10 @@ class EE_API UISceneNode : public SceneNode { virtual Node* setSize( const Float& Width, const Float& Height ); + UISceneNode* setPixelsSize( const Sizef& size ); + + UISceneNode* setPixelsSize( const Float& x, const Float& y ); + const Sizef& getSize() const; virtual void update( const Time& elapsed ); @@ -126,6 +130,8 @@ class EE_API UISceneNode : public SceneNode { virtual void setFocus(); + void setInternalPixelsSize( const Sizef& size ); + void setActiveWindow( UIWindow* window ); void setFocusLastWindow( UIWindow* window ); diff --git a/include/eepp/ui/uislider.hpp b/include/eepp/ui/uislider.hpp index fea2245bc..ccba994c6 100644 --- a/include/eepp/ui/uislider.hpp +++ b/include/eepp/ui/uislider.hpp @@ -102,6 +102,8 @@ class EE_API UISlider : public UIWidget { virtual void onPaddingChange(); + virtual Uint32 onMouseDown( const Vector2i& position, const Uint32& flags );; + void fixSliderPos(); void adjustSliderPos(); diff --git a/include/eepp/ui/uiwidgetcreator.hpp b/include/eepp/ui/uiwidgetcreator.hpp index e0085e96c..615d69ef7 100644 --- a/include/eepp/ui/uiwidgetcreator.hpp +++ b/include/eepp/ui/uiwidgetcreator.hpp @@ -30,6 +30,8 @@ class EE_API UIWidgetCreator { static const RegisteredWidgetCallbackMap& getRegisteredWidgets(); + static std::vector getWidgetNames(); + protected: static RegisteredWidgetCallbackMap registeredWidget; diff --git a/premake4.lua b/premake4.lua index 41694fe70..9e1d7108c 100644 --- a/premake4.lua +++ b/premake4.lua @@ -536,8 +536,7 @@ function add_static_links() "zlib-static", "imageresampler-static", "pugixml-static", - "vorbis-static", - "rx-cpp-static" + "vorbis-static" } if _OPTIONS["with-mojoal"] then @@ -871,14 +870,6 @@ solution "eepp" includedirs { "src/thirdparty/freetype2/include" } build_base_configuration( "freetype" ) - project "rx-cpp-static" - kind "StaticLib" - language "C++" - set_targetdir("libs/" .. os.get_real() .. "/thirdparty/") - files { "src/thirdparty/rx-cpp/*.cpp", "src/thirdparty/rx-cpp/*.c" } - includedirs { "src/thirdparty/rx-cpp" } - build_base_cpp_configuration( "rx-cpp" ) - project "chipmunk-static" kind "StaticLib" @@ -1078,7 +1069,7 @@ solution "eepp" language "C++" files { "src/tools/codeeditor/*.cpp" } includedirs { "src/thirdparty" } - build_link_configuration( "eepp-codeeditor", true ) + build_link_configuration( "eepp-CodeEditor", true ) project "eepp-texturepacker" kind "ConsoleApp" diff --git a/premake5.lua b/premake5.lua index d8fe6fe0a..10add26f9 100644 --- a/premake5.lua +++ b/premake5.lua @@ -309,8 +309,7 @@ function add_static_links() "zlib-static", "imageresampler-static", "pugixml-static", - "vorbis-static", - "rx-cpp-static" + "vorbis-static" } if _OPTIONS["with-mojoal"] then @@ -632,14 +631,6 @@ workspace "eepp" incdirs { "src/thirdparty/freetype2/include" } build_base_configuration( "freetype" ) - project "rx-cpp-static" - kind "StaticLib" - language "C++" - targetdir("libs/" .. os.target() .. "/thirdparty/") - files { "src/thirdparty/rx-cpp/*.cpp", "src/thirdparty/rx-cpp/*.c" } - incdirs { "src/thirdparty/rx-cpp" } - build_base_cpp_configuration( "rx-cpp" ) - project "chipmunk-static" kind "StaticLib" targetdir("libs/" .. os.target() .. "/thirdparty/") @@ -828,7 +819,7 @@ workspace "eepp" language "C++" files { "src/tools/codeeditor/*.cpp" } incdirs { "src/thirdparty" } - build_link_configuration( "eepp-codeeditor", true ) + build_link_configuration( "eepp-CodeEditor", true ) project "eepp-texturepacker" kind "ConsoleApp" diff --git a/projects/android-project/app/jni/eepp.mk b/projects/android-project/app/jni/eepp.mk index d8050c191..793e6d5e0 100644 --- a/projects/android-project/app/jni/eepp.mk +++ b/projects/android-project/app/jni/eepp.mk @@ -19,8 +19,7 @@ EEPP_C_INCLUDES := \ $(EEPP_THIRD_PARTY_PATH)/libvorbis/include \ $(EEPP_THIRD_PARTY_PATH)/libogg/include \ $(EEPP_THIRD_PARTY_PATH)/mbedtls/include \ - $(EEPP_THIRD_PARTY_PATH)/mojoAL \ - $(EEPP_THIRD_PARTY_PATH)/rx-cpp + $(EEPP_THIRD_PARTY_PATH)/mojoAL EEPP_C_FLAGS := \ -Wl,--undefined=Java_org_libsdl_app_SDLActivity_nativeInit \ @@ -62,8 +61,6 @@ CODE_SRCS := \ ../thirdparty/libvorbis/lib/*.c \ ../thirdparty/mbedtls/library/*.c \ ../thirdparty/mojoAL/*.c \ - ../thirdparty/rx-cpp/*.c \ - ../thirdparty/rx-cpp/*.cpp \ system/*.cpp \ system/platform/posix/*.cpp \ network/*.cpp \ diff --git a/projects/linux/ee.creator.user b/projects/linux/ee.creator.user index fef1021c9..168a32995 100644 --- a/projects/linux/ee.creator.user +++ b/projects/linux/ee.creator.user @@ -1,6 +1,6 @@ - + EnvironmentId @@ -1843,11 +1843,11 @@ 2 - %{buildDir}../../../bin/eepp-codeeditor-debug - eepp-codeeditor-debug + %{buildDir}../../../bin/eepp-CodeEditor-debug + eepp-CodeEditor-debug ProjectExplorer.CustomExecutableRunConfiguration - ../src/tools/codeeditor/codeeditor.cpp + ../src/eepp/ui/doc/textdocument.cpp false true diff --git a/projects/linux/ee.files b/projects/linux/ee.files index 12a26e6ca..b06e2e1a5 100644 --- a/projects/linux/ee.files +++ b/projects/linux/ee.files @@ -245,6 +245,7 @@ ../../include/eepp/system/iostreamzip.hpp ../../include/eepp/system/lock.hpp ../../include/eepp/system/log.hpp +../../include/eepp/system/luapatternmatcher.hpp ../../include/eepp/system/md5.hpp ../../include/eepp/system/mutex.hpp ../../include/eepp/system/pack.hpp @@ -703,6 +704,9 @@ ../../src/eepp/system/iostreamzip.cpp ../../src/eepp/system/lock.cpp ../../src/eepp/system/log.cpp +../../src/eepp/system/lua-str.cpp +../../src/eepp/system/lua-str.hpp +../../src/eepp/system/luapatternmatcher.cpp ../../src/eepp/system/md5.cpp ../../src/eepp/system/mutex.cpp ../../src/eepp/system/objectloader.cpp diff --git a/src/eepp/scene/eventdispatcher.cpp b/src/eepp/scene/eventdispatcher.cpp index 79919e5a7..834941a56 100644 --- a/src/eepp/scene/eventdispatcher.cpp +++ b/src/eepp/scene/eventdispatcher.cpp @@ -147,6 +147,14 @@ void EventDispatcher::update( const Time& time ) { } mLastMousePos = mMousePos; + + // While dragging and object we want to be able to continue dragging even if the mouse cursor + // moves outside the window. Capturing the mouse allows this. + if ( !wasDraggingNode && isNodeDragging() ) { + mInput->captureMouse( true ); + } else if ( wasDraggingNode && !isNodeDragging() ) { + mInput->captureMouse( false ); + } } Input* EventDispatcher::getInput() const { diff --git a/src/eepp/scene/scenenode.cpp b/src/eepp/scene/scenenode.cpp index 8e6bcacff..249a68510 100644 --- a/src/eepp/scene/scenenode.cpp +++ b/src/eepp/scene/scenenode.cpp @@ -216,11 +216,16 @@ void SceneNode::addToCloseQueue( Node* node ) { void SceneNode::checkClose() { if ( !mCloseList.empty() ) { - for ( auto it = mCloseList.begin(); it != mCloseList.end(); ++it ) { - eeDelete( *it ); - } - + // First we need to create a temporal copy of the close list because it can change its + // content while deleting the elements, since the elements can call to any node close() + // at any moment (and in this case during the deletion of a node). + // Once copied we need to clear the close list and start the new close list for the next + // check. + CloseList closeListCopy( mCloseList ); mCloseList.clear(); + + for ( Node* node : closeListCopy ) + eeDelete( node ); } } diff --git a/src/eepp/system/lua-str.cpp b/src/eepp/system/lua-str.cpp new file mode 100644 index 000000000..17a3d4fb7 --- /dev/null +++ b/src/eepp/system/lua-str.cpp @@ -0,0 +1,440 @@ +#include "lua-str.hpp" +#include +#include +#include +#include +#include +#include +#include +#include + +// Taken from rx-cpp (https://github.com/stevedonovan/rx-cpp/) that took this code from the +// Lua source code. + +static LuaFailFun s_fail_fun; + +void lua_str_fail_func( LuaFailFun f ) { + s_fail_fun = f; +} + +/* macro to `unsign' a character */ +#define uchar( c ) ( (unsigned char)( c ) ) + +/* +** {====================================================== +** PATTERN MATCHING +** ======================================================= +*/ + +#define LUA_MAXCAPTURES 32 + +/* maximum recursion depth for 'match' */ +#define MAXCCALLS 200 + +#define CAP_UNFINISHED ( -1 ) +#define CAP_POSITION ( -2 ) + +typedef struct MatchState { + int matchdepth; /* control for recursive depth (to avoid C stack overflow) */ + const char* src_init; /* init of source string */ + const char* src_end; /* end ('\0') of source string */ + const char* p_end; /* end ('\0') of pattern */ + int level; /* total number of captures (finished or unfinished) */ + struct { + const char* init; + ptrdiff_t len; + } capture[LUA_MAXCAPTURES]; +} MatchState; + +/* recursive function */ +static const char* match( MatchState* ms, const char* s, const char* p ); + +#define L_ESC '%' +#define SPECIALS "^$*+?.([%-" + +// error handling, hm?? NB + +static int throw_error( const char* fmt, ... ) { + char buff[1024]; + va_list ap; + va_start( ap, fmt ); + vsnprintf( buff, sizeof( buff ), fmt, ap ); + va_end( ap ); + if ( !s_fail_fun ) { + fprintf( stderr, "%s\n", buff ); + exit( 1 ); + } else { + s_fail_fun( buff ); + } + return 0; +} + +static int check_capture( MatchState* ms, int l ) { + l -= '1'; + if ( l < 0 || l >= ms->level || ms->capture[l].len == CAP_UNFINISHED ) + return throw_error( "invalid capture index %%%d", l + 1 ); + return l; +} + +static int capture_to_close( MatchState* ms ) { + int level = ms->level; + for ( level--; level >= 0; level-- ) + if ( ms->capture[level].len == CAP_UNFINISHED ) + return level; + return throw_error( "invalid pattern capture" ); +} + +static const char* classend( MatchState* ms, const char* p ) { + switch ( *p++ ) { + case L_ESC: { + if ( p == ms->p_end ) + throw_error( "malformed pattern (ends with '%')" ); + return p + 1; + } + case '[': { + if ( *p == '^' ) + p++; + do { /* look for a `]' */ + if ( p == ms->p_end ) + throw_error( "malformed pattern (missing ']')" ); + if ( *( p++ ) == L_ESC && p < ms->p_end ) + p++; /* skip escapes (e.g. `%]') */ + } while ( *p != ']' ); + return p + 1; + } + default: { + return p; + } + } +} + +static int match_class( int c, int cl ) { + int res; + switch ( tolower( cl ) ) { + case 'a': + res = isalpha( c ); + break; + case 'c': + res = iscntrl( c ); + break; + case 'd': + res = isdigit( c ); + break; + case 'g': + res = isgraph( c ); + break; + case 'l': + res = islower( c ); + break; + case 'p': + res = ispunct( c ); + break; + case 's': + res = isspace( c ); + break; + case 'u': + res = isupper( c ); + break; + case 'w': + res = isalnum( c ); + break; + case 'x': + res = isxdigit( c ); + break; + case 'z': + res = ( c == 0 ); + break; /* deprecated option */ + default: + return ( cl == c ); + } + return ( islower( cl ) ? res : !res ); +} + +static int matchbracketclass( int c, const char* p, const char* ec ) { + int sig = 1; + if ( *( p + 1 ) == '^' ) { + sig = 0; + p++; /* skip the `^' */ + } + while ( ++p < ec ) { + if ( *p == L_ESC ) { + p++; + if ( match_class( c, uchar( *p ) ) ) + return sig; + } else if ( ( *( p + 1 ) == '-' ) && ( p + 2 < ec ) ) { + p += 2; + if ( uchar( *( p - 2 ) ) <= c && c <= uchar( *p ) ) + return sig; + } else if ( uchar( *p ) == c ) + return sig; + } + return !sig; +} + +static int singlematch( MatchState* ms, const char* s, const char* p, const char* ep ) { + if ( s >= ms->src_end ) + return 0; + else { + int c = uchar( *s ); + switch ( *p ) { + case '.': + return 1; /* matches any char */ + case L_ESC: + return match_class( c, uchar( *( p + 1 ) ) ); + case '[': + return matchbracketclass( c, p, ep - 1 ); + default: + return ( uchar( *p ) == c ); + } + } +} + +static const char* matchbalance( MatchState* ms, const char* s, const char* p ) { + if ( p >= ms->p_end - 1 ) + throw_error( "malformed pattern " + "(missing arguments to '%b')" ); + if ( *s != *p ) + return NULL; + else { + int b = *p; + int e = *( p + 1 ); + int cont = 1; + while ( ++s < ms->src_end ) { + if ( *s == e ) { + if ( --cont == 0 ) + return s + 1; + } else if ( *s == b ) + cont++; + } + } + return NULL; /* string ends out of balance */ +} + +static const char* max_expand( MatchState* ms, const char* s, const char* p, const char* ep ) { + ptrdiff_t i = 0; /* counts maximum expand for item */ + while ( singlematch( ms, s + i, p, ep ) ) + i++; + /* keeps trying to match with the maximum repetitions */ + while ( i >= 0 ) { + const char* res = match( ms, ( s + i ), ep + 1 ); + if ( res ) + return res; + i--; /* else didn't match; reduce 1 repetition to try again */ + } + return NULL; +} + +static const char* min_expand( MatchState* ms, const char* s, const char* p, const char* ep ) { + for ( ;; ) { + const char* res = match( ms, s, ep + 1 ); + if ( res != NULL ) + return res; + else if ( singlematch( ms, s, p, ep ) ) + s++; /* try with one more repetition */ + else + return NULL; + } +} + +static const char* start_capture( MatchState* ms, const char* s, const char* p, int what ) { + const char* res; + int level = ms->level; + if ( level >= LUA_MAXCAPTURES ) + throw_error( "too many captures" ); + ms->capture[level].init = s; + ms->capture[level].len = what; + ms->level = level + 1; + if ( ( res = match( ms, s, p ) ) == NULL ) /* match failed? */ + ms->level--; /* undo capture */ + return res; +} + +static const char* end_capture( MatchState* ms, const char* s, const char* p ) { + int l = capture_to_close( ms ); + const char* res; + ms->capture[l].len = s - ms->capture[l].init; /* close capture */ + if ( ( res = match( ms, s, p ) ) == NULL ) /* match failed? */ + ms->capture[l].len = CAP_UNFINISHED; /* undo capture */ + return res; +} + +static const char* match_capture( MatchState* ms, const char* s, int l ) { + size_t len; + l = check_capture( ms, l ); + len = ms->capture[l].len; + if ( ( size_t )( ms->src_end - s ) >= len && memcmp( ms->capture[l].init, s, len ) == 0 ) + return s + len; + else + return NULL; +} + +static const char* match( MatchState* ms, const char* s, const char* p ) { + if ( ms->matchdepth-- == 0 ) + throw_error( "pattern too complex" ); +init: /* using goto's to optimize tail recursion */ + if ( p != ms->p_end ) { /* end of pattern? */ + switch ( *p ) { + case '(': { /* start capture */ + if ( *( p + 1 ) == ')' ) /* position capture? */ + s = start_capture( ms, s, p + 2, CAP_POSITION ); + else + s = start_capture( ms, s, p + 1, CAP_UNFINISHED ); + break; + } + case ')': { /* end capture */ + s = end_capture( ms, s, p + 1 ); + break; + } + case '$': { + if ( ( p + 1 ) != ms->p_end ) /* is the `$' the last char in pattern? */ + goto dflt; /* no; go to default */ + s = ( s == ms->src_end ) ? s : NULL; /* check end of string */ + break; + } + case L_ESC: { /* escaped sequences not in the format class[*+?-]? */ + switch ( *( p + 1 ) ) { + case 'b': { /* balanced string? */ + s = matchbalance( ms, s, p + 2 ); + if ( s != NULL ) { + p += 4; + goto init; /* return match(ms, s, p + 4); */ + } /* else fail (s == NULL) */ + break; + } + case 'f': { /* frontier? */ + const char* ep; + char previous; + p += 2; + if ( *p != '[' ) + throw_error( "missing '[' after '%f' in pattern" ); + ep = classend( ms, p ); /* points to what is next */ + previous = ( s == ms->src_init ) ? '\0' : *( s - 1 ); + if ( !matchbracketclass( uchar( previous ), p, ep - 1 ) && + matchbracketclass( uchar( *s ), p, ep - 1 ) ) { + p = ep; + goto init; /* return match(ms, s, ep); */ + } + s = NULL; /* match failed */ + break; + } + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': { /* capture results (%0-%9)? */ + s = match_capture( ms, s, uchar( *( p + 1 ) ) ); + if ( s != NULL ) { + p += 2; + goto init; /* return match(ms, s, p + 2) */ + } + break; + } + default: + goto dflt; + } + break; + } + default: + dflt : { /* pattern class plus optional suffix */ + const char* ep = classend( ms, p ); /* points to optional suffix */ + /* does not match at least once? */ + if ( !singlematch( ms, s, p, ep ) ) { + if ( *ep == '*' || *ep == '?' || *ep == '-' ) { /* accept empty? */ + p = ep + 1; + goto init; /* return match(ms, s, ep + 1); */ + } else /* '+' or no suffix */ + s = NULL; /* fail */ + } else { /* matched once */ + switch ( *ep ) { /* handle optional suffix */ + case '?': { /* optional */ + const char* res; + if ( ( res = match( ms, s + 1, ep + 1 ) ) != NULL ) + s = res; + else { + p = ep + 1; + goto init; /* else return match(ms, s, ep + 1); */ + } + break; + } + case '+': /* 1 or more repetitions */ + s++; /* 1 match already done */ + /* go through */ + case '*': /* 0 or more repetitions */ + s = max_expand( ms, s, p, ep ); + break; + case '-': /* 0 or more repetitions (minimum) */ + s = min_expand( ms, s, p, ep ); + break; + default: /* no suffix */ + s++; + p = ep; + goto init; /* return match(ms, s + 1, ep); */ + } + } + break; + } + } + } + ms->matchdepth++; + return s; +} + +static void push_onecapture( MatchState* ms, int i, const char* s, const char* e, LuaMatch* mm ) { + if ( i >= ms->level ) { + if ( i == 0 ) { /* ms->level == 0, too */ + mm->start = 0; + mm->end = e - s; + // lua_pushlstring(ms->L, s, e - s); /* add whole match */ + } else + throw_error( "invalid capture index" ); + } else { + ptrdiff_t l = ms->capture[i].len; + if ( l == CAP_UNFINISHED ) + throw_error( "unfinished capture" ); + if ( l == CAP_POSITION ) { + mm[i].start = ms->capture[i].init - ms->src_init + 1; + mm[i].end = mm[i].start; + } else { + mm[i].start = ms->capture[i].init - ms->src_init; + mm[i].end = mm[i].start + l; + } + } +} + +static int push_captures( MatchState* ms, const char* s, const char* e, LuaMatch* mm ) { + int i; + int nlevels = ( ms->level == 0 && s ) ? 1 : ms->level; + for ( i = 0; i < nlevels; i++ ) + push_onecapture( ms, i, s, e, mm ); + return nlevels; /* number of strings pushed */ +} + +int lua_str_match( const char* s, int offset, size_t ls, const char* p, LuaMatch* mm ) { + size_t lp = strlen( p ); + const char* s1 = s + offset; + MatchState ms; + int anchor = ( *p == '^' ); + if ( anchor ) { + p++; + lp--; /* skip anchor character */ + } + ms.matchdepth = MAXCCALLS; + ms.src_init = s; + ms.src_end = s + ls; + ms.p_end = p + lp; + do { + const char* res; + ms.level = 0; + if ( ( res = match( &ms, s1, p ) ) != NULL ) { + mm[0].start = s1 - s; /* start */ + mm[0].end = res - s; /* end */ + return push_captures( &ms, NULL, 0, mm + 1 ) + 1; + } + } while ( s1++ < ms.src_end && !anchor ); + return 0; +} diff --git a/src/eepp/system/lua-str.hpp b/src/eepp/system/lua-str.hpp new file mode 100644 index 000000000..6bd350c17 --- /dev/null +++ b/src/eepp/system/lua-str.hpp @@ -0,0 +1,17 @@ +#ifndef EE_SYSTEM_LUA_STR_HPP +#define EE_SYSTEM_LUA_STR_HPP + +#include + +typedef void ( *LuaFailFun )( const char* msg ); + +void lua_str_fail_func( LuaFailFun f ); + +struct LuaMatch { + int start; + int end; +}; + +int lua_str_match( const char* text, int offset, size_t len, const char* pattern, LuaMatch* mm ); + +#endif // EE_SYSTEM_LUA_STR_HPP diff --git a/src/eepp/system/luapatternmatcher.cpp b/src/eepp/system/luapatternmatcher.cpp new file mode 100644 index 000000000..467b1b717 --- /dev/null +++ b/src/eepp/system/luapatternmatcher.cpp @@ -0,0 +1,69 @@ +#include +#include +#include + +namespace EE { namespace System { + +// Implementation based on the rx-cpp (https://github.com/stevedonovan/rx-cpp/). + +const int MAX_DEFAULT_MATCHES = 12; +static bool sFailHandlerInitialized = false; + +static void failHandler( const char* msg ) { + throw std::string( msg ); +} + +LuaPatternMatcher::LuaPatternMatcher( const std::string& pattern ) : mPattern( pattern ) { + if ( !sFailHandlerInitialized ) { + sFailHandlerInitialized = true; + lua_str_fail_func( failHandler ); + } +} + +bool LuaPatternMatcher::matches( const char* stringSearch, int stringStartOffset, + LuaPatternMatcher::Match* matches, size_t stringLength ) { + LuaPatternMatcher::Match matchesBuffer[MAX_DEFAULT_MATCHES]; + if ( matches == NULL ) + matches = matchesBuffer; + if ( stringLength == 0 ) + stringLength = strlen( stringSearch ); + try { + mMatchNum = lua_str_match( stringSearch, stringStartOffset, stringLength, mPattern.c_str(), + (LuaMatch*)matches ); + } catch ( const std::string& patternError ) { + mErr = std::move( patternError ); + mMatchNum = 0; + } + return mMatchNum == 0 ? false : true; +} + +bool LuaPatternMatcher::find( const char* stringSearch, int stringStartOffset, int& startMatch, + int& endMatch, int stringLength, int returnMatchIndex ) { + LuaPatternMatcher::Match matchesBuffer[MAX_DEFAULT_MATCHES]; + if ( matches( stringSearch, stringStartOffset, matchesBuffer, stringLength ) ) { + range( returnMatchIndex, startMatch, endMatch, matchesBuffer ); + return true; + } else { + startMatch = -1; + endMatch = -1; + return false; + } +} +bool LuaPatternMatcher::range( int indexGet, int& startMatch, int& endMatch, + LuaPatternMatcher::Match* returnedMatched ) { + if ( indexGet == -1 ) { + indexGet = getNumMatches() > 1 ? 1 : 0; + } + if ( indexGet >= 0 && indexGet < getNumMatches() ) { + startMatch = returnedMatched[indexGet].start; + endMatch = returnedMatched[indexGet].end; + return true; + } + return false; +} + +int LuaPatternMatcher::getNumMatches() { + return mMatchNum; +} + +}} // namespace EE::System diff --git a/src/eepp/ui/doc/syntaxcolorscheme.cpp b/src/eepp/ui/doc/syntaxcolorscheme.cpp index 7b3163a5d..39670dd1d 100644 --- a/src/eepp/ui/doc/syntaxcolorscheme.cpp +++ b/src/eepp/ui/doc/syntaxcolorscheme.cpp @@ -2,6 +2,8 @@ namespace EE { namespace UI { namespace Doc { +// Color schemes are compatible with the lite (https://github.com/rxi/lite) color schemes. + SyntaxColorScheme SyntaxColorScheme::getDefault() { return {"lite-theme", { diff --git a/src/eepp/ui/doc/syntaxdefinition.cpp b/src/eepp/ui/doc/syntaxdefinition.cpp index 5bbc78416..25fe91522 100644 --- a/src/eepp/ui/doc/syntaxdefinition.cpp +++ b/src/eepp/ui/doc/syntaxdefinition.cpp @@ -34,4 +34,33 @@ std::string SyntaxDefinition::getSymbol( const std::string& symbol ) const { return ""; } +SyntaxDefinition& SyntaxDefinition::addFileType( const std::string& fileType ) { + mFiles.push_back( fileType ); + return *this; +} + +SyntaxDefinition& SyntaxDefinition::addPattern( const SyntaxPattern& pattern ) { + mPatterns.push_back( pattern ); + return *this; +} + +SyntaxDefinition& SyntaxDefinition::addSymbol( const std::string& symbolName, + const std::string& typeName ) { + mSymbols[symbolName] = typeName; + return *this; +} + +SyntaxDefinition& SyntaxDefinition::addSymbols( const std::vector& symbolNames, + const std::string& typeName ) { + for ( auto& symbol : symbolNames ) { + addSymbol( symbol, typeName ); + } + return *this; +} + +SyntaxDefinition& SyntaxDefinition::setComment( const std::string& comment ) { + mComment = comment; + return *this; +} + }}} // namespace EE::UI::Doc diff --git a/src/eepp/ui/doc/syntaxdefinitionmanager.cpp b/src/eepp/ui/doc/syntaxdefinitionmanager.cpp index eeeb8c974..ce8b3d143 100644 --- a/src/eepp/ui/doc/syntaxdefinitionmanager.cpp +++ b/src/eepp/ui/doc/syntaxdefinitionmanager.cpp @@ -1,7 +1,8 @@ #include #include +#include #include -#include +#include using namespace EE::System; @@ -9,11 +10,14 @@ namespace EE { namespace UI { namespace Doc { SINGLETON_DECLARE_IMPLEMENTATION( SyntaxDefinitionManager ) +// Syntax definitions can be directly converted from the lite (https://github.com/rxi/lite) and +// lite-plugins (https://github.com/rxi/lite-plugins) supported languages. + SyntaxDefinitionManager::SyntaxDefinitionManager() { // Register some languages support. // XML - HTML - add( {{"%.xml$", "%.html?$"}, + add( {{"%.xml$", "%.html?$", "%.svg?$"}, { {{""}, "comment"}, {{"%f[^>][^<]", "%f[<]"}, "normal"}, @@ -47,7 +51,8 @@ SyntaxDefinitionManager::SyntaxDefinitionManager() { {{"@[%a][%w_-]*"}, "keyword2"}, {{"%.[%a][%w_-]*"}, "keyword2"}, {{"[{}:]"}, "operator"}, - }} ); + }} ) + .addSymbols( UIWidgetCreator::getWidgetNames(), "keyword2" ); // Markdown add( {{"%.md$", "%.markdown$"}, @@ -394,14 +399,17 @@ SyntaxDefinitionManager::SyntaxDefinitionManager() { "//"} ); } -void SyntaxDefinitionManager::add( SyntaxDefinition&& syntaxStyle ) { +SyntaxDefinition& SyntaxDefinitionManager::add( SyntaxDefinition&& syntaxStyle ) { mStyles.emplace_back( std::move( syntaxStyle ) ); + return mStyles.back(); } -const SyntaxDefinition PLAIN_STYLE = SyntaxDefinition(); - const SyntaxDefinition& SyntaxDefinitionManager::getPlainStyle() const { - return PLAIN_STYLE; + return mEmptyDefinition; +} + +SyntaxDefinition& SyntaxDefinitionManager::getStyleByExtensionRef( const std::string& filePath ) { + return const_cast( getStyleByExtension( filePath ) ); } const SyntaxDefinition& @@ -410,8 +418,8 @@ SyntaxDefinitionManager::getStyleByExtension( const std::string& filePath ) cons if ( !extension.empty() ) { for ( auto style = mStyles.rbegin(); style != mStyles.rend(); ++style ) { for ( auto ext : style->getFiles() ) { - if ( String::startsWith( ext, "%." ) ) { - textutil::Rxl words( ext ); + if ( String::startsWith( ext, "%." ) || String::endsWith( ext, "$" ) ) { + LuaPatternMatcher words( ext ); int start, end; if ( words.find( filePath, 0, start, end ) ) { return *style; @@ -422,6 +430,7 @@ SyntaxDefinitionManager::getStyleByExtension( const std::string& filePath ) cons } } } - return PLAIN_STYLE; + return mEmptyDefinition; } + }}} // namespace EE::UI::Doc diff --git a/src/eepp/ui/doc/syntaxhighlighter.cpp b/src/eepp/ui/doc/syntaxhighlighter.cpp index dd06b5ff8..ae77e5854 100644 --- a/src/eepp/ui/doc/syntaxhighlighter.cpp +++ b/src/eepp/ui/doc/syntaxhighlighter.cpp @@ -9,13 +9,6 @@ SyntaxHighlighter::SyntaxHighlighter( TextDocument* doc ) : mDoc( doc ) { void SyntaxHighlighter::reset() { mLines.clear(); - mFirstInvalidLine = 0; - mMaxWantedLine = 0; -} - -void SyntaxHighlighter::invalidate( const size_t& line ) { - mFirstInvalidLine = line; - mMaxWantedLine = eemin( mMaxWantedLine, mDoc->linesCount() ); } TokenizedLine SyntaxHighlighter::tokenizeLine( const size_t& line, const int& state ) { @@ -40,7 +33,6 @@ const std::vector& SyntaxHighlighter::getLine( const size_t& index } } mLines[index] = tokenizeLine( index, prevState ); - mMaxWantedLine = eemax( mMaxWantedLine, index ); return mLines[index].tokens; } return it->second.tokens; diff --git a/src/eepp/ui/doc/syntaxtokenizer.cpp b/src/eepp/ui/doc/syntaxtokenizer.cpp index 372d2276c..92bb8dd3d 100644 --- a/src/eepp/ui/doc/syntaxtokenizer.cpp +++ b/src/eepp/ui/doc/syntaxtokenizer.cpp @@ -1,11 +1,15 @@ +#include #include #include -#include -using namespace textutil; +using namespace EE::System; namespace EE { namespace UI { namespace Doc { +// This tokenizer is a direct conversion to C++ from the lite (https://github.com/rxi/lite) +// tokenizer. This allows eepp to support the same color schemes and syntax definitions from +// lite. Making much easier to implement a complete code editor. + static bool allSpaces( const std::string& str ) { for ( auto& chr : str ) if ( ' ' != chr ) @@ -38,7 +42,7 @@ bool isScaped( const std::string& text, const size_t& startIndex, const std::str std::pair findNonEscaped( const std::string& text, const std::string& pattern, int offset, const std::string& escapeStr ) { while ( true ) { - Rxl words( pattern ); + LuaPatternMatcher words( pattern ); int start, end; if ( words.find( text, offset, start, end ) ) { if ( !escapeStr.empty() && isScaped( text, start, escapeStr ) ) { @@ -67,9 +71,9 @@ std::pair, int> SyntaxTokenizer::tokenize( const Syntax while ( i < text.size() ) { if ( retState != SYNTAX_TOKENIZER_STATE_NONE ) { const SyntaxPattern& pattern = syntax.getPatterns()[retState]; - std::pair range = findNonEscaped( - text, pattern.patterns[1], i, pattern.patterns.size() >= 3 ? pattern.patterns[2] - : "" ); + std::pair range = + findNonEscaped( text, pattern.patterns[1], i, + pattern.patterns.size() >= 3 ? pattern.patterns[2] : "" ); if ( range.first != -1 ) { pushToken( tokens, pattern.type, text.substr( i, range.second - i ) ); retState = SYNTAX_TOKENIZER_STATE_NONE; @@ -86,7 +90,7 @@ std::pair, int> SyntaxTokenizer::tokenize( const Syntax patternIndex++ ) { const SyntaxPattern& pattern = syntax.getPatterns()[patternIndex]; const std::string& patternStr( "^" + pattern.patterns.at( 0 ) ); - Rxl words( patternStr ); + LuaPatternMatcher words( patternStr ); int start, end = 0; if ( words.find( text, i, start, end ) ) { std::string patternText( text.substr( start, end - start ) ); diff --git a/src/eepp/ui/doc/textdocument.cpp b/src/eepp/ui/doc/textdocument.cpp index b0e18895b..9fad4b1cc 100644 --- a/src/eepp/ui/doc/textdocument.cpp +++ b/src/eepp/ui/doc/textdocument.cpp @@ -8,6 +8,9 @@ namespace EE { namespace UI { namespace Doc { +// Text document is loosely based on the SerenityOS (https://github.com/SerenityOS/serenity) +// TextDocument and the lite editor (https://github.com/rxi/lite) implementations. + const char NON_WORD_CHARS[] = " \t\n/\\()\"':,.;<>~!@#$%^&*|+=[]{}`?-"; bool TextDocument::isNonWord( String::StringBaseType ch ) { diff --git a/src/eepp/ui/uicodeeditor.cpp b/src/eepp/ui/uicodeeditor.cpp index 89db52123..b92fc35dd 100644 --- a/src/eepp/ui/uicodeeditor.cpp +++ b/src/eepp/ui/uicodeeditor.cpp @@ -35,6 +35,7 @@ UICodeEditor::UICodeEditor() : mCaretColor( "#93DDFA" ), mColorScheme( SyntaxColorScheme::getDefault() ), mHighlighter( &mDoc ) { + mFlags |= UI_TAB_STOP; setBackgroundColor( Color::fromString( "#2e2e32" ) ); setFontColor( Color::fromString( "#e1e1e6" ) ); mFontStyleConfig.setFontSelectionBackColor( Color::fromString( "#48484f" ) ); @@ -86,7 +87,7 @@ void UICodeEditor::draw() { std::pair lineRange = getVisibleLineRange(); Float charSize = PixelDensity::pxToDp( getCharacterSize() ); Float lineHeight = getLineHeight(); - int lineNumberDigits = Math::countDigits( mDoc.linesCount() ); + int lineNumberDigits = getLineNumberDigits(); Float lineNumberWidth = mShowLineNumber ? getLineNumberWidth() : 0.f; Vector2f screenStart( mScreenPos.x + mRealPadding.Left, mScreenPos.y + mRealPadding.Top ); Vector2f start( screenStart.x + lineNumberWidth, screenStart.y ); @@ -185,6 +186,7 @@ void UICodeEditor::scheduledUpdate( const Time& ) { if ( mMouseDown && !( getUISceneNode()->getWindow()->getInput()->getPressTrigger() & EE_BUTTON_LMASK ) ) { mMouseDown = false; + getUISceneNode()->getWindow()->getInput()->captureMouse( false ); } } @@ -307,8 +309,12 @@ const Float& UICodeEditor::getLineNumberPaddingRight() const { return mLineNumberPaddingRight; } +size_t UICodeEditor::getLineNumberDigits() const { + return eemax( 2UL, Math::countDigits( mDoc.linesCount() ) ); +} + Float UICodeEditor::getLineNumberWidth() const { - return Math::countDigits( mDoc.linesCount() ) * getGlyphWidth() + getLineNumberPaddingLeft() + + return getLineNumberDigits() * getGlyphWidth() + getLineNumberPaddingLeft() + getLineNumberPaddingRight(); } @@ -611,8 +617,8 @@ TextPosition UICodeEditor::resolveScreenPosition( const Vector2f& position ) con localPos += mScroll; localPos.x -= mRealPadding.Left + ( mShowLineNumber ? getLineNumberWidth() : 0.f ); localPos.y -= mRealPadding.Top; - Int64 line = - eeclamp( (Int64)eefloor( localPos.y / getLineHeight() ), 0, mDoc.linesCount() - 1 ); + Int64 line = eeclamp( (Int64)eefloor( localPos.y / getLineHeight() ), 0, + ( Int64 )( mDoc.linesCount() - 1 ) ); return TextPosition( line, getColFromXOffset( line, localPos.x ) ); } @@ -635,6 +641,7 @@ Uint32 UICodeEditor::onMouseDown( const Vector2i& position, const Uint32& flags !mMouseDown && ( flags & EE_BUTTON_LMASK ) ) { mMouseDown = true; Input* input = getUISceneNode()->getWindow()->getInput(); + input->captureMouse( true ); if ( input->isShiftPressed() ) { mDoc.selectTo( resolveScreenPosition( position.asFloat() ) ); } else { @@ -660,6 +667,7 @@ Uint32 UICodeEditor::onMouseUp( const Vector2i& position, const Uint32& flags ) if ( flags & EE_BUTTON_LMASK ) { mMouseDown = false; + getUISceneNode()->getWindow()->getInput()->captureMouse( false ); } else if ( flags & EE_BUTTON_WDMASK ) { if ( getUISceneNode()->getWindow()->getInput()->isControlPressed() ) { mFontStyleConfig.CharacterSize = eemax( 4, mFontStyleConfig.CharacterSize - 1 ); @@ -713,7 +721,7 @@ 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->setPageStep( getViewPortLineCount().y / (float)mDoc.linesCount() ); mVScrollBar->setClickStep( 0.2f ); mVScrollBar->setEnabled( notVisibleLineCount > 0 ); mVScrollBar->setVisible( notVisibleLineCount > 0 ); @@ -835,7 +843,7 @@ Int64 UICodeEditor::getColFromXOffset( Int64 lineNumber, const Float& offset ) c return i; } } - return line.size() - 1; + return static_cast( line.size() ) - 1; } Float UICodeEditor::getLineHeight() const { diff --git a/src/eepp/ui/uidropdownlist.cpp b/src/eepp/ui/uidropdownlist.cpp index e73fcd199..89cdbde9b 100644 --- a/src/eepp/ui/uidropdownlist.cpp +++ b/src/eepp/ui/uidropdownlist.cpp @@ -46,9 +46,6 @@ UIDropDownList::UIDropDownList( const std::string& tag ) : mListBox->addEventListener( Event::KeyDown, cb::Make1( this, &UIDropDownList::onItemKeyDown ) ); mListBox->addEventListener( Event::OnControlClear, cb::Make1( this, &UIDropDownList::onControlClear ) ); - mListBox->addEventListener( Event::OnClose, [&]( const Event*) { - mListBox = NULL; - } ); } UIDropDownList::~UIDropDownList() { @@ -138,7 +135,7 @@ void UIDropDownList::showList() { if ( !mStyleConfig.PopUpToRoot ) mListBox->setParent( getWindowContainer() ); else - mListBox->setParent( mSceneNode ); + mListBox->setParent( getUISceneNode()->getRoot() ); mListBox->toFront(); @@ -234,8 +231,7 @@ void UIDropDownList::onListBoxFocusLoss( const Event* ) { if ( NULL == getEventDispatcher() ) return; - bool frienIsFocus = - NULL != mFriendCtrl && mFriendCtrl == getEventDispatcher()->getFocusNode(); + bool frienIsFocus = NULL != mFriendCtrl && mFriendCtrl == getEventDispatcher()->getFocusNode(); bool isChildFocus = isChild( getEventDispatcher()->getFocusNode() ); if ( getEventDispatcher()->getFocusNode() != this && !isChildFocus && !frienIsFocus ) { @@ -313,8 +309,9 @@ Uint32 UIDropDownList::onKeyDown( const KeyEvent& Event ) { } void UIDropDownList::destroyListBox() { - if ( !SceneManager::instance()->isShootingDown() && NULL != mListBox ) { - mListBox->close(); + if ( !SceneManager::instance()->isShootingDown() && NULL != mListBox && + mListBox->getParent() != this ) { + mListBox->setParent( this ); } } diff --git a/src/eepp/ui/uieventdispatcher.cpp b/src/eepp/ui/uieventdispatcher.cpp index 72aa66eff..6c490f4e5 100644 --- a/src/eepp/ui/uieventdispatcher.cpp +++ b/src/eepp/ui/uieventdispatcher.cpp @@ -37,10 +37,14 @@ void UIEventDispatcher::checkTabPress( const Uint32& KeyCode ) { Window::Window* win = mFocusNode->getSceneNode()->getWindow(); if ( KeyCode == KEY_TAB && mFocusNode->isUINode() && NULL != win && win->isActive() && !mJustGainedFocus ) { - Node* Ctrl = static_cast( mFocusNode )->getNextWidget(); + UINode* uiNode = static_cast( mFocusNode ); - if ( NULL != Ctrl ) - Ctrl->setFocus(); + if ( !uiNode->isTabStop() ) { + Node* Ctrl = static_cast( mFocusNode )->getNextWidget(); + + if ( NULL != Ctrl ) + Ctrl->setFocus(); + } } } diff --git a/src/eepp/ui/uilistbox.cpp b/src/eepp/ui/uilistbox.cpp index a84170a69..6c230623a 100644 --- a/src/eepp/ui/uilistbox.cpp +++ b/src/eepp/ui/uilistbox.cpp @@ -84,9 +84,7 @@ UIListBox::UIListBox( const std::string& tag ) : UIListBox::UIListBox() : UIListBox( "listbox" ) {} -UIListBox::~UIListBox() { - onClose(); -} +UIListBox::~UIListBox() {} Uint32 UIListBox::getType() const { return UI_TYPE_LISTBOX; diff --git a/src/eepp/ui/uinode.cpp b/src/eepp/ui/uinode.cpp index 2bdd55ff4..e346152df 100644 --- a/src/eepp/ui/uinode.cpp +++ b/src/eepp/ui/uinode.cpp @@ -202,10 +202,19 @@ Node* UINode::setSize( const Float& Width, const Float& Height ) { } UINode* UINode::setPixelsSize( const Sizef& size ) { - if ( size != mSize ) { - Vector2f sizeChange( size.x - mSize.x, size.y - mSize.y ); + Sizef s( size ); + Sizef pMinSize( PixelDensity::dpToPx( mMinSize ) ); - setInternalPixelsSize( size ); + if ( s.x < pMinSize.x ) + s.x = pMinSize.x; + + if ( s.y < pMinSize.y ) + s.y = pMinSize.y; + + if ( s != mSize ) { + Vector2f sizeChange( s.x - mSize.x, s.y - mSize.y ); + + setInternalPixelsSize( s ); onSizeChange(); @@ -254,6 +263,10 @@ const Sizef& UINode::getMinSize() const { return mMinSize; } +bool UINode::isTabStop() const { + return ( mFlags & UI_TAB_STOP ) != 0; +} + void UINode::updateOriginPoint() { Node::updateOriginPoint(); @@ -389,21 +402,16 @@ void UINode::draw() { } } -Uint32 UINode::onMouseDown( const Vector2i& Pos, const Uint32& Flags ) { +Uint32 UINode::onMouseDown( const Vector2i& position, const Uint32& flags ) { if ( NULL != getEventDispatcher() && !getEventDispatcher()->isNodeDragging() && !( getEventDispatcher()->getLastPressTrigger() & mDragButton ) && - ( Flags & mDragButton ) && isDragEnabled() && !isDragging() ) { - setDragging( true ); - - if ( NULL != getEventDispatcher() ) - getEventDispatcher()->setNodeDragging( this ); - - mDragPoint = Vector2f( Pos.x, Pos.y ); + ( flags & mDragButton ) && isDragEnabled() && !isDragging() ) { + startDragging( position.asFloat() ); } pushState( UIState::StatePressed ); - return Node::onMouseDown( Pos, Flags ); + return Node::onMouseDown( position, flags ); } Uint32 UINode::onMouseUp( const Vector2i& Pos, const Uint32& Flags ) { @@ -1126,6 +1134,15 @@ void UINode::setDragging( const bool& dragging ) { } } +void UINode::startDragging( const Vector2f& position ) { + setDragging( true ); + + if ( NULL != getEventDispatcher() ) + getEventDispatcher()->setNodeDragging( this ); + + mDragPoint = position; +} + bool UINode::ownsChildPosition() const { return 0 != ( mFlags & UI_OWNS_CHILDS_POSITION ); } @@ -1338,4 +1355,8 @@ UISceneNode* UINode::getUISceneNode() { return mUISceneNode; } +Rectf UINode::getLocalDpBounds() const { + return Rectf( 0, 0, mDpSize.getWidth(), mDpSize.getHeight() ); +} + }} // namespace EE::UI diff --git a/src/eepp/ui/uipopupmenu.cpp b/src/eepp/ui/uipopupmenu.cpp index 7ba2dfe0a..7fe102710 100644 --- a/src/eepp/ui/uipopupmenu.cpp +++ b/src/eepp/ui/uipopupmenu.cpp @@ -17,9 +17,7 @@ UIPopUpMenu::UIPopUpMenu() : UIMenu() { applyDefaultTheme(); } -UIPopUpMenu::~UIPopUpMenu() { - onClose(); -} +UIPopUpMenu::~UIPopUpMenu() {} Uint32 UIPopUpMenu::getType() const { return UI_TYPE_POPUPMENU; diff --git a/src/eepp/ui/uiscenenode.cpp b/src/eepp/ui/uiscenenode.cpp index 8ebf34c9a..ee3df9d77 100644 --- a/src/eepp/ui/uiscenenode.cpp +++ b/src/eepp/ui/uiscenenode.cpp @@ -40,6 +40,11 @@ UISceneNode::UISceneNode( EE::Window::Window* window ) : #endif mUpdatingLayouts( false ), mUIThemeManager( UIThemeManager::New() ) { + // Reset size since the SceneNode already set it but needs to set the size from zero to emmit + // the required events to its childs. + mSize = Sizef(); + mDpSize = Sizef(); + // Update only UI elements that requires it. setUpdateAllChilds( false ); @@ -48,7 +53,7 @@ UISceneNode::UISceneNode( EE::Window::Window* window ) : setEventDispatcher( UIEventDispatcher::New( this ) ); mRoot = UIWidget::NewWithTag( ":root" ); - mRoot->setParent( this )->setPosition( 0, 0 ); + mRoot->setParent( this )->setPosition( 0, 0 )->setId( "uiscenenode_root_node" ); mRoot->enableReportSizeChangeToChilds(); resizeControl( mWindow ); @@ -63,8 +68,7 @@ UISceneNode::~UISceneNode() { } void UISceneNode::resizeControl( EE::Window::Window* ) { - setSize( eefloor( mWindow->getWidth() / PixelDensity::getPixelDensity() ), - eefloor( mWindow->getHeight() / PixelDensity::getPixelDensity() ) ); + setPixelsSize( mWindow->getSize().asFloat() ); onMediaChanged(); sendMsg( this, NodeMessage::WindowResize ); } @@ -386,11 +390,13 @@ UIWidget* UISceneNode::loadLayoutFromPack( Pack* pack, const std::string& FilePa } void UISceneNode::setInternalSize( const Sizef& size ) { - mDpSize = size; - mSize = PixelDensity::dpToPx( size ); - updateCenter(); - sendCommonEvent( Event::OnSizeChange ); - invalidateDraw(); + if ( size != mDpSize ) { + mDpSize = size; + mSize = PixelDensity::dpToPx( size ); + updateCenter(); + sendCommonEvent( Event::OnSizeChange ); + invalidateDraw(); + } } Node* UISceneNode::setSize( const Sizef& Size ) { @@ -417,18 +423,47 @@ const Sizef& UISceneNode::getSize() const { return mDpSize; } +UISceneNode* UISceneNode::setPixelsSize( const Sizef& size ) { + if ( size != mSize ) { + Vector2f sizeChange( size.x - mSize.x, size.y - mSize.y ); + + setInternalPixelsSize( size ); + + onSizeChange(); + + if ( reportSizeChangeToChilds() ) { + sendParentSizeChange( PixelDensity::pxToDp( sizeChange ) ); + } + } + + return this; +} + +UISceneNode* UISceneNode::setPixelsSize( const Float& x, const Float& y ) { + return setPixelsSize( Sizef( x, y ) ); +} + void UISceneNode::update( const Time& elapsed ) { UISceneNode* uiSceneNode = SceneManager::instance()->getUISceneNode(); SceneManager::instance()->setCurrentUISceneNode( this ); + updateDirtyStyles(); + updateDirtyStyleStates(); updateDirtyLayouts(); SceneNode::update( elapsed ); + // We process again all the dirty states since the update could have created new dirty states + // that we want to process BEFORE drawing the scene, since we can avoid some resizes/animations + // glitches. Also after the SceneNode::update (having run updated the actions, responded to + // events, and updating the nodes means that new nodes could have been added and need to be + // ready before being drawn. Also the reverse case could happen, we need to have the styles and + // layouts updated before and after the update to avoid weird issues. The cost of doing this is + // minimal and the benefit is huge and simplifies implementation. updateDirtyStyles(); - updateDirtyStyleStates(); + updateDirtyLayouts(); SceneManager::instance()->setCurrentUISceneNode( uiSceneNode ); } @@ -774,4 +809,16 @@ bool UISceneNode::removeShortcut( const Uint32& KeyCode, const Uint32& Mod ) { return false; } +void UISceneNode::setInternalPixelsSize( const Sizef& size ) { + Sizef s( size ); + if ( s != mSize ) { + mDpSize = PixelDensity::pxToDp( s ).ceil(); + mSize = s; + mNodeFlags |= NODE_FLAG_POLYGON_DIRTY; + updateCenter(); + sendCommonEvent( Event::OnSizeChange ); + invalidateDraw(); + } +} + }} // namespace EE::UI diff --git a/src/eepp/ui/uislider.cpp b/src/eepp/ui/uislider.cpp index 5d91f17cd..5d71d40cb 100644 --- a/src/eepp/ui/uislider.cpp +++ b/src/eepp/ui/uislider.cpp @@ -1,5 +1,7 @@ #include +#include #include +#include #include #include @@ -52,7 +54,7 @@ UISlider::UISlider( const std::string& tag, const UIOrientation& orientation ) : mSlider = UIWidget::NewWithTag( mTag + "::vbutton" ); } - auto cb = [&]( const Event* event ) { + auto cb = [&]( const Event* ) { if ( !mUpdating ) adjustChilds(); }; @@ -68,8 +70,7 @@ UISlider::UISlider( const std::string& tag, const UIOrientation& orientation ) : mSlider->setDragEnabled( true ); mSlider->setSize( 4, 4 ); mSlider->setPosition( 0, 0 ); - mSlider->addEventListener( Event::OnPositionChange, - [&]( const Event* event ) { fixSliderPos(); } ); + mSlider->addEventListener( Event::OnPositionChange, [&]( const Event* ) { fixSliderPos(); } ); if ( UIOrientation::Horizontal == mOrientation ) mSlider->centerVertical(); @@ -126,6 +127,21 @@ void UISlider::onPaddingChange() { UIWidget::onPaddingChange(); } +Uint32 UISlider::onMouseDown( const Vector2i& position, const Uint32& flags ) { + Vector2f mouseDownInitPos( + getUISceneNode()->getEventDispatcher()->getMouseDownPos().asFloat() ); + worldToNode( mouseDownInitPos ); + if ( getLocalDpBounds().contains( mouseDownInitPos ) ) { + Vector2f localPos( position.asFloat() ); + worldToNode( localPos ); + if ( localPos.y >= mSlider->getPosition().y && + localPos.y <= mSlider->getPosition().y + mSlider->getSize().getHeight() ) { + mSlider->startDragging( position.asFloat() ); + } + } + return UIWidget::onMouseDown( position, flags ); +} + void UISlider::adjustChilds() { mUpdating = true; diff --git a/src/eepp/ui/uiwidgetcreator.cpp b/src/eepp/ui/uiwidgetcreator.cpp index 541810496..30f1372f1 100644 --- a/src/eepp/ui/uiwidgetcreator.cpp +++ b/src/eepp/ui/uiwidgetcreator.cpp @@ -140,4 +140,13 @@ const UIWidgetCreator::RegisteredWidgetCallbackMap& UIWidgetCreator::getRegister return registeredWidget; } +std::vector UIWidgetCreator::getWidgetNames() { + std::vector names; + createBaseWidgetList(); + for ( auto& widgetIt : registeredWidget ) { + names.push_back( widgetIt.first ); + } + return names; +} + }} // namespace EE::UI diff --git a/src/eepp/ui/uiwinmenu.cpp b/src/eepp/ui/uiwinmenu.cpp index a7f0ce16c..e43445822 100644 --- a/src/eepp/ui/uiwinmenu.cpp +++ b/src/eepp/ui/uiwinmenu.cpp @@ -25,6 +25,18 @@ UIWinMenu::~UIWinMenu() { destroyMenues(); } +void UIWinMenu::destroyMenues() { + if ( !SceneManager::instance()->isShootingDown() ) { + for ( WinMenuList::iterator it = mButtons.begin(); it != mButtons.end(); ++it ) { + if ( it->second->getParent() != this ) { + // Changing the parent ensures that the menu will be destroyed when the win menu is + // destroyed + it->second->setParent( this ); + } + } + } +} + Uint32 UIWinMenu::getType() const { return UI_TYPE_WINMENU; } @@ -41,8 +53,7 @@ void UIWinMenu::addMenuButton( const String& ButtonText, UIPopUpMenu* Menu ) { Button->setText( ButtonText ); Button->setVisible( true ); Button->setEnabled( true ); - Button->addEventListener( Event::OnSizeChange, - [&]( const Event* event ) { refreshButtons(); } ); + Button->addEventListener( Event::OnSizeChange, [&]( const Event* ) { refreshButtons(); } ); Menu->setVisible( false ); Menu->setEnabled( false ); @@ -297,14 +308,6 @@ void UIWinMenu::onWidgetFocusLoss() { unselectButtons(); } -void UIWinMenu::destroyMenues() { - if ( !SceneManager::instance()->isShootingDown() ) { - for ( WinMenuList::iterator it = mButtons.begin(); it != mButtons.end(); ++it ) { - it->second->close(); - } - } -} - void UIWinMenu::autoHeight() { if ( 0 == mMenuHeight && NULL != getSkin() ) { mMenuHeight = getSkinSize().getHeight(); diff --git a/src/eepp/window/backend/SDL2/windowsdl2.cpp b/src/eepp/window/backend/SDL2/windowsdl2.cpp index 09eb2f976..91a20c9ca 100644 --- a/src/eepp/window/backend/SDL2/windowsdl2.cpp +++ b/src/eepp/window/backend/SDL2/windowsdl2.cpp @@ -403,6 +403,8 @@ bool WindowSDL::create( WindowSettings Settings, ContextSettings Context ) { logSuccessfulInit( getVersion() ); + SDL_SetHint( SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "0" ); + return true; } diff --git a/src/thirdparty/rx-cpp/COPYING b/src/thirdparty/rx-cpp/COPYING deleted file mode 100644 index f36b930d9..000000000 --- a/src/thirdparty/rx-cpp/COPYING +++ /dev/null @@ -1,19 +0,0 @@ -textutil-cpp: Copyright © 2015 Steve Donovan. -Lua: Copyright © 1994-2015 Lua.org, PUC-Rio. - -Permission is hereby granted, free of charge, to any person obtaining a copy of this -software and associated documentation files (the "Software"), to deal in the Software -without restriction, including without limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons -to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or -substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR -OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, -ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/src/thirdparty/rx-cpp/lua-str.c b/src/thirdparty/rx-cpp/lua-str.c deleted file mode 100644 index 732cbefb0..000000000 --- a/src/thirdparty/rx-cpp/lua-str.c +++ /dev/null @@ -1,411 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include "lua-str.h" - -static FailFun s_fail_fun; - -void str_fail_func(FailFun f) { - s_fail_fun = f; -} - -/* macro to `unsign' a character */ -#define uchar(c) ((unsigned char)(c)) - - -/* -** {====================================================== -** PATTERN MATCHING -** ======================================================= -*/ - -#define LUA_MAXCAPTURES 32 - -/* maximum recursion depth for 'match' */ -#define MAXCCALLS 200 - -#define CAP_UNFINISHED (-1) -#define CAP_POSITION (-2) - -typedef struct MatchState { - int matchdepth; /* control for recursive depth (to avoid C stack overflow) */ - const char *src_init; /* init of source string */ - const char *src_end; /* end ('\0') of source string */ - const char *p_end; /* end ('\0') of pattern */ - int level; /* total number of captures (finished or unfinished) */ - struct { - const char *init; - ptrdiff_t len; - } capture[LUA_MAXCAPTURES]; -} MatchState; - -/* recursive function */ -static const char *match (MatchState *ms, const char *s, const char *p); - -#define L_ESC '%' -#define SPECIALS "^$*+?.([%-" - -// error handling, hm?? NB - -static int throw_error(const char *fmt,...) { - char buff[1024]; - va_list ap; - va_start(ap,fmt); - vsnprintf(buff,sizeof(buff),fmt,ap); - va_end(ap); - if (! s_fail_fun) { - fprintf(stderr,"%s\n",buff); - exit(1); - } else { - s_fail_fun(buff); - } - return 0; -} - -static int check_capture (MatchState *ms, int l) { - l -= '1'; - if (l < 0 || l >= ms->level || ms->capture[l].len == CAP_UNFINISHED) - return throw_error("invalid capture index %%%d", l + 1); - return l; -} - -static int capture_to_close (MatchState *ms) { - int level = ms->level; - for (level--; level>=0; level--) - if (ms->capture[level].len == CAP_UNFINISHED) return level; - return throw_error("invalid pattern capture"); -} - - -static const char *classend (MatchState *ms, const char *p) { - switch (*p++) { - case L_ESC: { - if (p == ms->p_end) - throw_error("malformed pattern (ends with '%')"); - return p+1; - } - case '[': { - if (*p == '^') p++; - do { /* look for a `]' */ - if (p == ms->p_end) - throw_error("malformed pattern (missing ']')"); - if (*(p++) == L_ESC && p < ms->p_end) - p++; /* skip escapes (e.g. `%]') */ - } while (*p != ']'); - return p+1; - } - default: { - return p; - } - } -} - - -static int match_class (int c, int cl) { - int res; - switch (tolower(cl)) { - case 'a' : res = isalpha(c); break; - case 'c' : res = iscntrl(c); break; - case 'd' : res = isdigit(c); break; - case 'g' : res = isgraph(c); break; - case 'l' : res = islower(c); break; - case 'p' : res = ispunct(c); break; - case 's' : res = isspace(c); break; - case 'u' : res = isupper(c); break; - case 'w' : res = isalnum(c); break; - case 'x' : res = isxdigit(c); break; - case 'z' : res = (c == 0); break; /* deprecated option */ - default: return (cl == c); - } - return (islower(cl) ? res : !res); -} - - -static int matchbracketclass (int c, const char *p, const char *ec) { - int sig = 1; - if (*(p+1) == '^') { - sig = 0; - p++; /* skip the `^' */ - } - while (++p < ec) { - if (*p == L_ESC) { - p++; - if (match_class(c, uchar(*p))) - return sig; - } - else if ((*(p+1) == '-') && (p+2 < ec)) { - p+=2; - if (uchar(*(p-2)) <= c && c <= uchar(*p)) - return sig; - } - else if (uchar(*p) == c) return sig; - } - return !sig; -} - - -static int singlematch (MatchState *ms, const char *s, const char *p, - const char *ep) { - if (s >= ms->src_end) - return 0; - else { - int c = uchar(*s); - switch (*p) { - case '.': return 1; /* matches any char */ - case L_ESC: return match_class(c, uchar(*(p+1))); - case '[': return matchbracketclass(c, p, ep-1); - default: return (uchar(*p) == c); - } - } -} - - -static const char *matchbalance (MatchState *ms, const char *s, - const char *p) { - if (p >= ms->p_end - 1) - throw_error("malformed pattern " - "(missing arguments to '%b')"); - if (*s != *p) return NULL; - else { - int b = *p; - int e = *(p+1); - int cont = 1; - while (++s < ms->src_end) { - if (*s == e) { - if (--cont == 0) return s+1; - } - else if (*s == b) cont++; - } - } - return NULL; /* string ends out of balance */ -} - - -static const char *max_expand (MatchState *ms, const char *s, - const char *p, const char *ep) { - ptrdiff_t i = 0; /* counts maximum expand for item */ - while (singlematch(ms, s + i, p, ep)) - i++; - /* keeps trying to match with the maximum repetitions */ - while (i>=0) { - const char *res = match(ms, (s+i), ep+1); - if (res) return res; - i--; /* else didn't match; reduce 1 repetition to try again */ - } - return NULL; -} - - -static const char *min_expand (MatchState *ms, const char *s, - const char *p, const char *ep) { - for (;;) { - const char *res = match(ms, s, ep+1); - if (res != NULL) - return res; - else if (singlematch(ms, s, p, ep)) - s++; /* try with one more repetition */ - else return NULL; - } -} - - -static const char *start_capture (MatchState *ms, const char *s, - const char *p, int what) { - const char *res; - int level = ms->level; - if (level >= LUA_MAXCAPTURES) throw_error("too many captures"); - ms->capture[level].init = s; - ms->capture[level].len = what; - ms->level = level+1; - if ((res=match(ms, s, p)) == NULL) /* match failed? */ - ms->level--; /* undo capture */ - return res; -} - - -static const char *end_capture (MatchState *ms, const char *s, - const char *p) { - int l = capture_to_close(ms); - const char *res; - ms->capture[l].len = s - ms->capture[l].init; /* close capture */ - if ((res = match(ms, s, p)) == NULL) /* match failed? */ - ms->capture[l].len = CAP_UNFINISHED; /* undo capture */ - return res; -} - - -static const char *match_capture (MatchState *ms, const char *s, int l) { - size_t len; - l = check_capture(ms, l); - len = ms->capture[l].len; - if ((size_t)(ms->src_end-s) >= len && - memcmp(ms->capture[l].init, s, len) == 0) - return s+len; - else return NULL; -} - -static const char *match (MatchState *ms, const char *s, const char *p) { - if (ms->matchdepth-- == 0) - throw_error("pattern too complex"); - init: /* using goto's to optimize tail recursion */ - if (p != ms->p_end) { /* end of pattern? */ - switch (*p) { - case '(': { /* start capture */ - if (*(p + 1) == ')') /* position capture? */ - s = start_capture(ms, s, p + 2, CAP_POSITION); - else - s = start_capture(ms, s, p + 1, CAP_UNFINISHED); - break; - } - case ')': { /* end capture */ - s = end_capture(ms, s, p + 1); - break; - } - case '$': { - if ((p + 1) != ms->p_end) /* is the `$' the last char in pattern? */ - goto dflt; /* no; go to default */ - s = (s == ms->src_end) ? s : NULL; /* check end of string */ - break; - } - case L_ESC: { /* escaped sequences not in the format class[*+?-]? */ - switch (*(p + 1)) { - case 'b': { /* balanced string? */ - s = matchbalance(ms, s, p + 2); - if (s != NULL) { - p += 4; goto init; /* return match(ms, s, p + 4); */ - } /* else fail (s == NULL) */ - break; - } - case 'f': { /* frontier? */ - const char *ep; char previous; - p += 2; - if (*p != '[') - throw_error("missing '[' after '%f' in pattern"); - ep = classend(ms, p); /* points to what is next */ - previous = (s == ms->src_init) ? '\0' : *(s - 1); - if (!matchbracketclass(uchar(previous), p, ep - 1) && - matchbracketclass(uchar(*s), p, ep - 1)) { - p = ep; goto init; /* return match(ms, s, ep); */ - } - s = NULL; /* match failed */ - break; - } - case '0': case '1': case '2': case '3': - case '4': case '5': case '6': case '7': - case '8': case '9': { /* capture results (%0-%9)? */ - s = match_capture(ms, s, uchar(*(p + 1))); - if (s != NULL) { - p += 2; goto init; /* return match(ms, s, p + 2) */ - } - break; - } - default: goto dflt; - } - break; - } - default: dflt: { /* pattern class plus optional suffix */ - const char *ep = classend(ms, p); /* points to optional suffix */ - /* does not match at least once? */ - if (!singlematch(ms, s, p, ep)) { - if (*ep == '*' || *ep == '?' || *ep == '-') { /* accept empty? */ - p = ep + 1; goto init; /* return match(ms, s, ep + 1); */ - } - else /* '+' or no suffix */ - s = NULL; /* fail */ - } - else { /* matched once */ - switch (*ep) { /* handle optional suffix */ - case '?': { /* optional */ - const char *res; - if ((res = match(ms, s + 1, ep + 1)) != NULL) - s = res; - else { - p = ep + 1; goto init; /* else return match(ms, s, ep + 1); */ - } - break; - } - case '+': /* 1 or more repetitions */ - s++; /* 1 match already done */ - /* go through */ - case '*': /* 0 or more repetitions */ - s = max_expand(ms, s, p, ep); - break; - case '-': /* 0 or more repetitions (minimum) */ - s = min_expand(ms, s, p, ep); - break; - default: /* no suffix */ - s++; p = ep; goto init; /* return match(ms, s + 1, ep); */ - } - } - break; - } - } - } - ms->matchdepth++; - return s; -} - - - -static void push_onecapture (MatchState *ms, int i, const char *s, - const char *e, LuaMatch *mm) { - if (i >= ms->level) { - if (i == 0) { /* ms->level == 0, too */ - mm->start = 0; - mm->end = e - s ; - //lua_pushlstring(ms->L, s, e - s); /* add whole match */ - } else - throw_error("invalid capture index"); - } - else { - ptrdiff_t l = ms->capture[i].len; - if (l == CAP_UNFINISHED) throw_error("unfinished capture"); - if (l == CAP_POSITION) { - mm[i].start = ms->capture[i].init - ms->src_init + 1; - mm[i].end = mm[i].start; - } else { - mm[i].start = ms->capture[i].init - ms->src_init; - mm[i].end = mm[i].start + l; - } - } -} - - -static int push_captures (MatchState *ms, const char *s, const char *e, LuaMatch *mm) { - int i; - int nlevels = (ms->level == 0 && s) ? 1 : ms->level; - for (i = 0; i < nlevels; i++) - push_onecapture(ms, i, s, e, mm); - return nlevels; /* number of strings pushed */ -} - - -int str_match (const char *s, int offset, size_t ls, const char *p, LuaMatch *mm) { - size_t lp=strlen(p); - const char *s1 = s + offset; - MatchState ms; - int anchor = (*p == '^'); - if (anchor) { - p++; lp--; /* skip anchor character */ - } - ms.matchdepth = MAXCCALLS; - ms.src_init = s; - ms.src_end = s + ls; - ms.p_end = p + lp; - do { - const char *res; - ms.level = 0; - if ((res=match(&ms, s1, p)) != NULL) { - mm[0].start = s1 - s; /* start */ - mm[0].end = res - s; /* end */ - return push_captures(&ms, NULL, 0, mm+1) + 1; - } - } while (s1++ < ms.src_end && !anchor); - return 0; -} - diff --git a/src/thirdparty/rx-cpp/lua-str.h b/src/thirdparty/rx-cpp/lua-str.h deleted file mode 100644 index 99a5a582a..000000000 --- a/src/thirdparty/rx-cpp/lua-str.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef _LUA_STR_H -#define _LUA_STR_H - -#ifdef __cplusplus -extern "C" { -#endif - -typedef void (*FailFun)(const char *msg); -void str_fail_func(FailFun f); - -typedef struct LuaMatch { - int start; - int end; -} LuaMatch; - -int str_match (const char *text, int offset, size_t len, const char *pattern, LuaMatch *mm); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/src/thirdparty/rx-cpp/rx.cpp b/src/thirdparty/rx-cpp/rx.cpp deleted file mode 100644 index 8217ab2ab..000000000 --- a/src/thirdparty/rx-cpp/rx.cpp +++ /dev/null @@ -1,385 +0,0 @@ -// wrapping POSIX regexes -// Steve Donovan, (c) 2015 -// MIT license - -#include - -#include "rx.h" -using namespace std; - -const int MAX_DEFAULT_MATCHES = 12; - -#ifndef NO_POSIX - -static string percent_subst(string pattern) { - string res; - const char *p = pattern.c_str(); - bool inside_bracket = false; - while (*p) { - char ch = *p; - if (ch == '%') { - string klass; - ++p; // eat special char - switch(*p) { - case 's': klass = "space"; break; - case 'd': klass = "digit"; break; - case 'x': klass = "xdigit"; break; - case 'a': klass = "alpha"; break; - case 'w': klass = "alnum"; break; - case 'u': klass = "upper"; break; - case 'l': klass = "lower"; break; - case 'c': klass = "cntrl"; break; - case 'p': klass = "punct"; break; - case '%': - ch = '%'; - break; - default: - ch = '\\'; - // special case: not a bracket! - if (*p == '[' || *p == ']') { - res += '\\'; - ch = *p; - } else { - --p; // wasn't special after all... - } - } - if (klass.size() > 0) { - klass = "[:" + klass + ":]"; - if (! inside_bracket) - klass = '[' + klass + ']'; - res += klass; - } else { - res += ch; - } - } else { - if (ch == '[') { - inside_bracket = true; - } else - if (ch == ']') { - inside_bracket = false; - } - res += ch; - } - p++; - } - return res; -} -#endif - -namespace textutil { - -Rx::Rx (regex_t *R) : rx(R) { -} - -Rx::Rx (Rx&& other) { - rx = other.rx; - other.rx = NULL; -} - -// move assignment as well... -Rx& Rx::operator= (Rx&& other) { - rx = other.rx; - other.rx = NULL; - return *this; -} - -// -1 is a special match index: it means, try to pick the first submatch, otherwise -// fall back to the full match. This is the default behaviour for substitutions. -bool Rx::range(int idx, int& i1, int& i2, regmatch_t *r_matches) { - if (idx == -1) { - idx = n_matches() > 1 ? 1 : 0; - } - if (idx >= 0 && idx < n_matches()) { - i1 = r_matches[idx].rm_so; - i2 = r_matches[idx].rm_eo; - return true; - } - return false; -} - -// like Lua's string.find; also returns the range matched -bool Rx::find (const char *str, int& i1, int& i2, int idx) { - regmatch_t match_buff[MAX_DEFAULT_MATCHES]; - if (matches(str,match_buff)){ - range(idx,i1,i2,match_buff); - return true; - } else { - i1 = -1; - i2 = -1; - return false; - } -} - -Rx::match Rx::gmatch(const char *s) &{ - return Rx::match(*this,s,false); -} - -Rx::match Rx::gmatch(const std::string& s) & { - return Rx::match(*this,s.c_str(),false); -} - -// some voodoo needed here; this is how we tell whether we were -// created from a _temporary_; in which case, zero out the regex_t since -// otherwise it will die with the temporary. The match will make special -// arrangements in this case! -Rx::match Rx::gmatch(const char *s) &&{ - Rx::match M(*this,s,true); - rx = NULL; - return M; -} - -Rx::match Rx::gmatch(const std::string& s) && { - Rx::match M(*this,s.c_str(),true); - rx = NULL; - return M; -} - -string Rx::gsub(const char *text, const char *repl) { - Rx::match ms (*this,text); - string res; - while (ms.subst(res)) { - for (const char *P = repl; *P; ++P) { - if (*P == '%') { - ++P; - int ngroup = (int)*P - (int)'0'; - res += ms.group(ngroup); - } else { - res += *P; - } - } - ms.next(); - } - return res; -} - -// a match state looks after the regexp object (a thin wrapper around a regex_t pointer) -// and keeps a buffer for storing the resulting matches. -// If constructed from a temporary Rx, we create our own Rx using its regex_t pointer. -Rx::match_state::match_state(Rx* pr, bool own_rx) : ref_count(1), own_rx(own_rx) { - r_matches = new regmatch_t[10]; // pr->n_matches() - if (own_rx) { - this->pr = new Rx(pr->regexp()); - } else { - this->pr = pr; - } -} - -bool Rx::match_state::range(int idx, int &i1, int &i2) { - return pr->range(idx,i1,i2,r_matches); -} - -bool Rx::match_state::matches(const char *s, size_t len) { - return pr->matches(s,r_matches,len); -} - -Rx::match_state::~match_state() { - delete[] r_matches; - if (own_rx) - delete pr; -} - -// A match object keeps a char pointer and a ref-counted match state object -Rx::match::match(Rx& r, const char *s, bool own_rx) : s(s) { - len = strlen(s); - state = new Rx::match_state(&r,own_rx); -} - -Rx::match::match(Rx& r, const string& s, bool own_rx) { - state = new Rx::match_state(&r,own_rx); - this->s = s.c_str(); - len = s.size(); -} - -// the match state only dies when there's no state left holding it -Rx::match::~match() { - --state->ref_count; - if (state->ref_count == 0) { - delete state; - } -} - -// so each copied match shares state by incrementing the ref count -Rx::match::match(const Rx::match& other) : state(other.state), s(other.s), len(other.len) { - ++state->ref_count; -} - -Rx::match& Rx::match::operator= (const match& other) { - ++state->ref_count; - return *this; -} - -// the match operations are expressed in terms of the basic match_state operations -// matches() & range() - -// this moves the char pointer just past the end of the current match -void Rx::match::next() { - int m1,m2; - state->range(0,m1,m2); - s += m2; - len -= m2; -} - -string Rx::match::group(int idx) const { - int m1,m2; - if (state->range(idx,m1,m2)) { - return string(s+m1,m2-m1); - } - return ""; -} - -bool Rx::match::subst(string& res) { - if (! matches()) { // copy remaining tail - res.append(s); - return false; - } - int m1,m2; - state->range(0,m1,m2); - if (m1 == 0) - return true; - res.append(s,m1); - return true; -} - -#ifndef NO_POSIX - -// Rxp just wraps a regex_t struct -Rxp::Rxp (string pat, int cflags) : Rx() { - rx = new regex_t; - int flags = REG_EXTENDED; - if (cflags & icase) - flags |= REG_ICASE; - if (cflags & newline) - flags |= REG_NEWLINE; - if (cflags & lua) - pat = percent_subst(pat); - rc = regcomp(rx, pat.c_str(), flags); -} - -Rxp::~Rxp() { - if (rx) { - regfree(rx); - delete rx; - } -} - -// if the regexp compilation fails, then use this; -// if (! R) { do_something_with(R.error()); } -string Rxp::error() { - char buff[512]; - regerror(rc,rx,buff,sizeof(buff)); - return buff; -} - -// basic match operation! -bool Rxp::matches (const char *ps, regmatch_t *r_matches, size_t len) { - regmatch_t match_buff[MAX_DEFAULT_MATCHES]; - if (r_matches == NULL) - r_matches = match_buff; - int res = regexec(rx,ps, n_matches(), r_matches,0); - return res != 0 ? false : true; -} - -int Rxp::n_matches() { - return rx->re_nsub+1; -} - -#endif - -#ifndef NO_LUA - -static bool s_handler_initialized; - -static void fail_handler (const char *msg) { - throw string(msg); -} - -static const char *copy_str(const string& s) { - char *out = new char[s.size()+1]; - strcpy(out,s.c_str()); - return out; -} - -// Rxp just wraps a pattern string -Rxl::Rxl (string pat) : Rx(), err(nullptr), pat(pat) { - //rx = (regex_t*)copy_str(pat); // PASOP - rx = (regex_t*)pat.c_str(); - if (! s_handler_initialized) { - s_handler_initialized = true; - str_fail_func(fail_handler); - } - n_match = 10; -} - -Rxl::~Rxl() { - //delete[] rx; - if (err) - delete[] err; -} - -// if the regexp compilation fails, then use this; -// if (! R) { do_something_with(R.error()); } -string Rxl::error() { - return err; -} - -// basic match operation! -bool Rxl::matches (const char *ps, regmatch_t *r_matches, size_t len) { - regmatch_t match_buff[MAX_DEFAULT_MATCHES]; - if (r_matches == NULL) - r_matches = match_buff; - if (len == 0) - len = strlen(ps); - // if there's an error in the pattern, it will throw and set error state - // LuaMatch and regmatch_t are just the same under the hood - try { - n_match = str_match(ps,0,len,pat.c_str(), (LuaMatch*) r_matches); - } catch (const string& pattern_error) { - err = copy_str(pattern_error); - n_match = 0; - } - return n_match == 0 ? false : true; -} - -bool Rxl::matches (const char *ps, int offset, regmatch_t *r_matches, size_t len) { - regmatch_t match_buff[MAX_DEFAULT_MATCHES]; - if (r_matches == NULL) - r_matches = match_buff; - if (len == 0) - len = strlen(ps); - // if there's an error in the pattern, it will throw and set error state - // LuaMatch and regmatch_t are just the same under the hood - try { - n_match = str_match(ps, offset,len,pat.c_str(), (LuaMatch*) r_matches); - } catch (const string& pattern_error) { - err = copy_str(pattern_error); - n_match = 0; - } - return n_match == 0 ? false : true; -} - -bool Rxl::find(const char* str, int offset, int& i1, int& i2, int len, int idx) { - regmatch_t match_buff[MAX_DEFAULT_MATCHES]; - if (matches(str,offset,match_buff,len)){ - range(idx,i1,i2,match_buff); - return true; - } else { - i1 = -1; - i2 = -1; - return false; - } -} - -int Rxl::n_matches() { - return n_match; -} - -#endif - - -} - -// overload to_string so that we can use it in gsub with any convertable type -namespace std { - string to_string(const string& s) { return s; } -} - diff --git a/src/thirdparty/rx-cpp/rx.h b/src/thirdparty/rx-cpp/rx.h deleted file mode 100644 index 0b4add9da..000000000 --- a/src/thirdparty/rx-cpp/rx.h +++ /dev/null @@ -1,243 +0,0 @@ -#ifndef __RX_H -#define __RX_H - -#include -#ifndef NO_POSIX -#include -#else -typedef struct regmatch_t { - int rm_so; - int rm_eo; -} regmatch_t; -typedef const char regex_t; -#endif -#ifndef NO_LUA -#include "lua-str.h" -#endif -#include - -namespace std { - string to_string(const string& s); -} - -namespace textutil { - -typedef const std::string& S; -template inline T from_string(S s) { return s; } -template<> inline int from_string(S s) { return std::stoi(s); } -template<> inline long from_string(S s) { return std::stoi(s); } -template<> inline unsigned long from_string(S s) { return std::stoul(s); } -template<> inline double from_string(S s) { return std::stod(s); } - -class Rx { -protected: - regex_t *rx; - -public: - - enum { - icase = 1, lua = 2, newline = 4 - }; - - struct match_state { - Rx* pr; - regmatch_t *r_matches; - size_t ref_count; - bool own_rx; - - match_state(Rx* pr, bool own_rx); - ~match_state(); - - bool range(int idx, int &i1, int &i2); - bool matches(const char *s, size_t len); - }; - - struct match { - match_state *state; - const char *s; - size_t len; - - match(Rx& r, const char *s, bool own_rx=false); - match(Rx& r, const std::string& s, bool own_rx=false); - match(const Rx::match& other); - match& operator= (const match& other); - ~match(); - - bool matches() { return state->matches(s,len); } - bool subst(std::string& res); - void next(); - std::string group(int idx = -1) const; - bool range(int idx, int &i1,int &i2) const { return state->range(idx,i1,i2); } - std::string operator[] (int idx) const { return group(idx); } - - template - void append_to(C& c) { - typedef typename C::value_type value_type; - while(matches()) { - c.push_back(from_string(group())); - next(); - } - } - - template - void fill_map(M& m) { - while(matches()) { - m[group(1)] = from_string(group(2)); - next(); - } - } - - struct iterator { - match *pm; - - iterator(match *pm) : pm(pm) { - if (pm != NULL) { - if (! pm->matches()) - pm = NULL; - } - } - - bool operator != (const iterator& other) { - return pm != other.pm; - } - - bool operator == (const iterator& other) { - return pm == other.pm; - } - - const match& operator* () const { return *pm; } - - iterator& operator ++() { - pm->next(); - if (! pm->matches()) { - pm = NULL; - } - return *this; - } - }; - - - iterator begin() { return iterator(this);} - iterator end() { return iterator(NULL); } - }; - - - Rx() : rx(nullptr) { } - Rx (Rx&& other); - Rx (regex_t *R); - Rx& operator= (Rx&& other); - - regex_t *regexp() { return rx; } - - // the above ctor may fail to compile the regex. - // The suggested idiom is 'if (! R) do_something_with(R.error());' - virtual bool operator! () { return true; } - - virtual ~Rx() {} - virtual std::string error() { return ""; } - virtual int n_matches() { return 0; } - virtual bool matches (const char *ps, regmatch_t *r_matches = NULL, size_t len = 0) { return false; } - - virtual bool matches (const std::string& s, regmatch_t *r_matches = NULL) { - return matches(s.c_str(),r_matches, s.size()); - } - - // like Lua's std::string.find; also returns the range matched - bool find (const char *str, int& i1, int& i2, int idx = 0); - bool find (const std::string& s, int& i1, int& i2, int idx = 0) { - return find(s.c_str(),i1,i2,idx); - } - - bool range(int idx, int& i1, int& i2, regmatch_t *r_matches); - - std::string gsub(const char *text, const char *repl); - std::string gsub(const std::string& text, const std::string& repl) { - return gsub(text.c_str(),repl.c_str()); - } - - match gmatch(const char *s) &; - match gmatch(const char *s) &&; - match gmatch(const std::string& s) &; - match gmatch(const std::string& s) &&; - - template - std::string gsub(const char *text, M& map_object) { - Rx::match ms (*this,text); - std::string res; - while (ms.subst(res)) { - res.append(std::to_string(map_object[ms.group()])); - ms.next(); - } - return res; - } - - template - std::string gsub_fun(const char *text, F fun_object) { - Rx::match ms (*this,text); - std::string res; - while (ms.subst(res)) { - res.append(std::to_string(fun_object(ms))); - ms.next(); - } - return res; - } - -}; - -#ifndef NO_POSIX -class Rxp: public Rx { - int rc; -public: - Rxp (std::string pat, int cflags = 0); - Rxp (Rxp&& other) { - rx = other.rx; - other.rx = NULL; - } - - virtual bool operator! () { return rc != 0; } - virtual ~Rxp(); - virtual std::string error(); - virtual int n_matches(); - virtual bool matches (const char *ps, regmatch_t *r_matches = NULL,size_t len=0); -}; - -inline Rx operator"" _R (const char *pat, size_t) { return Rxp(pat); } - -#endif - -#ifndef NO_LUA -class Rxl: public Rx { - const char *err; - std::string pat; - int n_match; - -public: - Rxl (std::string pat); - Rxl (Rxl&& other) { - rx = other.rx; - other.rx = NULL; - } - - virtual bool operator! () { return err != nullptr; } - virtual ~Rxl(); - virtual std::string error(); - virtual int n_matches(); - virtual bool matches (const char *ps, regmatch_t *r_matches = NULL,size_t len=0); - virtual bool matches (const std::string& s, regmatch_t *r_matches = NULL) { - return matches(s.c_str(),r_matches, s.size()); - } - bool matches (const char *ps, int offset, regmatch_t *r_matches, size_t len); - bool find (const char *str, int offset, int& i1, int& i2, int len = 0, int idx = 0); - bool find (const std::string& s, int offset, int& i1, int& i2, int idx = 0) { - return find(s.c_str(),offset,i1,i2,s.size(),idx); - } -}; - -inline Rx operator"" _L (const char *pat, size_t) { return Rxl(pat); } - -#endif - - - -} -#endif diff --git a/src/tools/codeeditor/codeeditor.cpp b/src/tools/codeeditor/codeeditor.cpp index c0e508838..edf255ccf 100644 --- a/src/tools/codeeditor/codeeditor.cpp +++ b/src/tools/codeeditor/codeeditor.cpp @@ -14,8 +14,7 @@ bool onCloseRequestCallback( EE::Window::Window* ) { 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::MsgBoxConfirmClick, []( const Event* ) { win->close(); } ); MsgBox->addEventListener( Event::OnClose, []( const Event* ) { MsgBox = NULL; } ); MsgBox->setTitle( "Close Code Editor?" ); MsgBox->center(); @@ -36,45 +35,60 @@ void loadFileFromPath( const std::string& path ) { setAppTitle( curFile ); } +void openFileDialog() { + UICommonDialog* TGDialog = UICommonDialog::New( UI_CDL_DEFAULT_FLAGS, "*.xml;*.css;*.svg" ); + TGDialog->setWinFlags( UI_WIN_DEFAULT_FLAGS | UI_WIN_MAXIMIZE_BUTTON | UI_WIN_MODAL ); + TGDialog->setTitle( "Open layout..." ); + TGDialog->addEventListener( Event::OpenFile, []( const Event* event ) { + loadFileFromPath( event->getNode()->asType()->getFullPath() ); + } ); + TGDialog->center(); + TGDialog->show(); +} + void mainLoop() { if ( codeEditor->isDirty() != docDirtyState ) { docDirtyState = codeEditor->isDirty(); setAppTitle( docDirtyState ? curFile + "*" : curFile ); } - win->getInput()->update(); + Input* input = win->getInput(); - if ( win->getInput()->isControlPressed() && win->getInput()->isKeyUp( KEY_S ) ) { + input->update(); + + if ( ( input->isControlPressed() && input->isKeyUp( KEY_O ) ) || input->isKeyUp( KEY_F2 ) ) { + openFileDialog(); + } + + if ( input->isControlPressed() && input->isKeyUp( KEY_S ) ) { codeEditor->save(); } - if ( win->getInput()->isKeyUp( KEY_F6 ) ) { + if ( input->isKeyUp( KEY_F6 ) ) { uiSceneNode->setHighlightOver( !uiSceneNode->getHighlightOver() ); } - if ( win->getInput()->isKeyUp( KEY_F7 ) ) { + if ( input->isKeyUp( KEY_F7 ) ) { uiSceneNode->setDrawBoxes( !uiSceneNode->getDrawBoxes() ); } - if ( win->getInput()->isKeyUp( KEY_F8 ) ) { + if ( input->isKeyUp( KEY_F8 ) ) { uiSceneNode->setDrawDebugData( !uiSceneNode->getDrawDebugData() ); } - if ( win->getInput()->isKeyUp( KEY_ESCAPE ) && NULL == MsgBox && - onCloseRequestCallback( win ) ) { + if ( input->isKeyUp( KEY_ESCAPE ) && NULL == MsgBox && onCloseRequestCallback( win ) ) { win->close(); } - // Update the UI scene. + if ( input->isAltPressed() && input->isKeyUp( KEY_RETURN ) ) { + win->toggleFullscreen(); + } + 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 ) );