From 84c40649c8ed376ed911998cd943b9f7709e13d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Mon, 25 May 2020 05:18:38 -0300 Subject: [PATCH] Removed rx-cpp since it's problematic. Added System::LuaPatternMatcher in replacement. This should fix the Windows and macOS builds. Fixed UISceneNode size, now it's set in pixels instead of dp to allow to always be the same size as the window. Fixed styles and layouts not updating in time before calling draw. Fixed a double delete crash when using UIDropDownList and UIWinMenu. Now UI elements can be dragged even if the mouse cursor goes out of the window limits. UIScrollBar slider now can be dragged also from the horizontal edges that are not part of the button slider (this allows to scroll from the edge for example in maximized windows). Fixed TextDocument on emscripten. Disabled fullscreen window minimizing on focus loss (SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS). --- README.md | 6 +- include/eepp/system/luapatternmatcher.hpp | 42 ++ include/eepp/ui/doc/syntaxdefinition.hpp | 13 + .../eepp/ui/doc/syntaxdefinitionmanager.hpp | 7 +- include/eepp/ui/doc/syntaxhighlighter.hpp | 4 - include/eepp/ui/doc/textposition.hpp | 7 +- include/eepp/ui/doc/textrange.hpp | 4 + include/eepp/ui/uicodeeditor.hpp | 2 + include/eepp/ui/uihelper.hpp | 2 +- include/eepp/ui/uinode.hpp | 6 + include/eepp/ui/uiscenenode.hpp | 6 + include/eepp/ui/uislider.hpp | 2 + include/eepp/ui/uiwidgetcreator.hpp | 2 + premake4.lua | 13 +- premake5.lua | 13 +- projects/android-project/app/jni/eepp.mk | 5 +- projects/linux/ee.creator.user | 8 +- projects/linux/ee.files | 4 + src/eepp/scene/eventdispatcher.cpp | 8 + src/eepp/scene/scenenode.cpp | 13 +- src/eepp/system/lua-str.cpp | 440 ++++++++++++++++++ src/eepp/system/lua-str.hpp | 17 + src/eepp/system/luapatternmatcher.cpp | 69 +++ src/eepp/ui/doc/syntaxcolorscheme.cpp | 2 + src/eepp/ui/doc/syntaxdefinition.cpp | 29 ++ src/eepp/ui/doc/syntaxdefinitionmanager.cpp | 29 +- src/eepp/ui/doc/syntaxhighlighter.cpp | 8 - src/eepp/ui/doc/syntaxtokenizer.cpp | 18 +- src/eepp/ui/doc/textdocument.cpp | 3 + src/eepp/ui/uicodeeditor.cpp | 20 +- src/eepp/ui/uidropdownlist.cpp | 13 +- src/eepp/ui/uieventdispatcher.cpp | 10 +- src/eepp/ui/uilistbox.cpp | 4 +- src/eepp/ui/uinode.cpp | 45 +- src/eepp/ui/uipopupmenu.cpp | 4 +- src/eepp/ui/uiscenenode.cpp | 65 ++- src/eepp/ui/uislider.cpp | 22 +- src/eepp/ui/uiwidgetcreator.cpp | 9 + src/eepp/ui/uiwinmenu.cpp | 23 +- src/eepp/window/backend/SDL2/windowsdl2.cpp | 2 + src/thirdparty/rx-cpp/COPYING | 19 - src/thirdparty/rx-cpp/lua-str.c | 411 ---------------- src/thirdparty/rx-cpp/lua-str.h | 22 - src/thirdparty/rx-cpp/rx.cpp | 385 --------------- src/thirdparty/rx-cpp/rx.h | 243 ---------- src/tools/codeeditor/codeeditor.cpp | 42 +- 46 files changed, 901 insertions(+), 1220 deletions(-) create mode 100644 include/eepp/system/luapatternmatcher.hpp create mode 100644 src/eepp/system/lua-str.cpp create mode 100644 src/eepp/system/lua-str.hpp create mode 100644 src/eepp/system/luapatternmatcher.cpp delete mode 100644 src/thirdparty/rx-cpp/COPYING delete mode 100644 src/thirdparty/rx-cpp/lua-str.c delete mode 100644 src/thirdparty/rx-cpp/lua-str.h delete mode 100644 src/thirdparty/rx-cpp/rx.cpp delete mode 100644 src/thirdparty/rx-cpp/rx.h 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 ) );