diff --git a/.gitmodules b/.gitmodules index 63c738607..7f697448a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -6,7 +6,7 @@ url = https://github.com/SpartanJ/soil2 [submodule "premake/premake-ninja"] path = premake/premake-ninja - url = ../../jimon/premake-ninja.git + url = https://github.com/jimon/premake-ninja.git [submodule "premake/premake-cmake"] path = premake/premake-cmake - url = ../../Jarod42/premake-cmake.git + url = https://github.com/Jarod42/premake-cmake.git diff --git a/include/eepp/ui/models/model.hpp b/include/eepp/ui/models/model.hpp index 1f20e6c05..f666b60a2 100644 --- a/include/eepp/ui/models/model.hpp +++ b/include/eepp/ui/models/model.hpp @@ -112,6 +112,8 @@ class EE_API Model { virtual bool classModelRoleEnabled() { return false; } + virtual bool tooltipModelRoleEnabled() { return false; } + void registerView( UIAbstractView* ); void unregisterView( UIAbstractView* ); diff --git a/include/eepp/ui/models/modelrole.hpp b/include/eepp/ui/models/modelrole.hpp index aa0681cf2..00e6f223e 100644 --- a/include/eepp/ui/models/modelrole.hpp +++ b/include/eepp/ui/models/modelrole.hpp @@ -3,7 +3,7 @@ namespace EE { namespace UI { namespace Models { -enum class ModelRole { Display, Icon, Sort, Class, Custom }; +enum class ModelRole { Display, Icon, Sort, Class, Tooltip, TooltipClass, Custom }; }}} // namespace EE::UI::Models diff --git a/include/eepp/ui/uitooltip.hpp b/include/eepp/ui/uitooltip.hpp index 1e48c094a..75cedf083 100644 --- a/include/eepp/ui/uitooltip.hpp +++ b/include/eepp/ui/uitooltip.hpp @@ -130,16 +130,18 @@ class EE_API UITooltip : public UIWidget { bool isWordWrap() const; protected: - Text* mTextCache; + Text* mTextCache{ nullptr }; UIFontStyleConfig mStyleConfig; Vector2f mAlignOffset; Time mTooltipTime; - UINode* mTooltipOf; + UINode* mTooltipOf{ nullptr }; String mStringBuffer; TextTransform::Value mTextTransform{ TextTransform::None }; bool mDontAutoHideOnMouseMove{ false }; bool mUsingCustomStyling{ false }; + virtual void onAlignChange(); + virtual void onAlphaChange(); virtual void onSizeChange(); diff --git a/src/eepp/system/process.cpp b/src/eepp/system/process.cpp index 2bbbb340a..09069c83a 100644 --- a/src/eepp/system/process.cpp +++ b/src/eepp/system/process.cpp @@ -42,6 +42,42 @@ static bool isFlatpakEnv() { #define PROCESS_PTR ( static_cast( mProcess ) ) +static std::vector parseArgs( const std::string& str ) { + bool inquote = false; + char quoteChar = 0; + std::vector res; + std::string curstr; + + for ( size_t i = 0; i < str.size(); ++i ) { + char c = str[i]; + if ( inquote ) { + if ( c == quoteChar ) { + inquote = false; + } else if ( c == '\\' && i + 1 < str.size() && str[i + 1] == quoteChar ) { + curstr += quoteChar; + ++i; + } else { + curstr += c; + } + } else if ( c == ' ' || c == '\t' ) { + if ( !curstr.empty() ) { + res.push_back( curstr ); + curstr.clear(); + } + } else if ( c == '\'' || c == '"' ) { + inquote = true; + quoteChar = c; + } else { + curstr += c; + } + } + + if ( !curstr.empty() ) + res.push_back( curstr ); + + return res; +} + Process::Process() {} Process::Process( const std::string& command, Uint32 options, @@ -76,8 +112,7 @@ bool Process::create( const std::string& command, Uint32 options, bool Process::create( const std::string& command, const std::string& args, Uint32 options, const std::unordered_map& environment, const std::string& workingDirectory ) { - return create( command, String::split( args, " ", "", "\"", true ), options, environment, - workingDirectory ); + return create( command, parseArgs( args ), options, environment, workingDirectory ); } bool Process::create( const std::string& command, const std::vector& cmdArr, diff --git a/src/eepp/ui/abstract/uiabstracttableview.cpp b/src/eepp/ui/abstract/uiabstracttableview.cpp index 0f054377d..1b5ce29ef 100644 --- a/src/eepp/ui/abstract/uiabstracttableview.cpp +++ b/src/eepp/ui/abstract/uiabstracttableview.cpp @@ -600,6 +600,18 @@ UIWidget* UIAbstractTableView::updateCell( const Vector2& posIndex, const cell->reportStyleStateChangeRecursive(); } + if ( getModel()->tooltipModelRoleEnabled() ) { + Variant tooltip( getModel()->data( index, ModelRole::Tooltip ) ); + if ( tooltip.isValid() ) { + if ( tooltip.is( Variant::Type::String ) ) + cell->setTooltipText( tooltip.asString() ); + else if ( tooltip.is( Variant::Type::StringPtr ) ) + cell->setTooltipText( tooltip.asStringPtr() ); + else + cell->setTooltipText( tooltip.toString() ); + } + } + Variant txt( getModel()->data( index, ModelRole::Display ) ); if ( txt.isValid() ) { if ( txt.is( Variant::Type::String ) ) diff --git a/src/eepp/ui/uitooltip.cpp b/src/eepp/ui/uitooltip.cpp index 8cfa83c2c..24e9c3ac2 100644 --- a/src/eepp/ui/uitooltip.cpp +++ b/src/eepp/ui/uitooltip.cpp @@ -62,11 +62,12 @@ Vector2f UITooltip::getTooltipPosition( UITooltip* toolip, const Vector2f& reque UITooltip::UITooltip() : UIWidget( "tooltip" ), mAlignOffset( 0.f, 0.f ), mTooltipTime( Time::Zero ), mTooltipOf() { - setFlags( UI_NODE_DEFAULT_FLAGS_CENTERED | UI_AUTO_PADDING | UI_AUTO_SIZE ); mTextCache = Text::New(); mEnabled = false; + setFlags( UI_NODE_DEFAULT_FLAGS_CENTERED | UI_AUTO_PADDING | UI_AUTO_SIZE ); + UITheme* theme = getUISceneNode()->getUIThemeManager()->getDefaultTheme(); if ( NULL != theme && NULL != theme->getDefaultFont() ) { @@ -258,6 +259,8 @@ void UITooltip::onAutoSize() { } void UITooltip::autoAlign() { + if ( mTextCache == nullptr ) return; + Uint32 Width = mSize.getWidth() - mPaddingPx.Left - mPaddingPx.Right; Uint32 Height = mSize.getHeight() - mPaddingPx.Top - mPaddingPx.Bottom; @@ -576,11 +579,11 @@ bool UITooltip::applyProperty( const StyleSheetProperty& attribute ) { case PropertyId::TextAlign: { std::string align = String::toLower( attribute.value() ); if ( align == "center" ) - setFlags( UI_HALIGN_CENTER ); + setHorizontalAlign( UI_HALIGN_CENTER ); else if ( align == "left" ) - setFlags( UI_HALIGN_LEFT ); + setHorizontalAlign( UI_HALIGN_LEFT ); else if ( align == "right" ) - setFlags( UI_HALIGN_RIGHT ); + setHorizontalAlign( UI_HALIGN_RIGHT ); break; } default: @@ -590,6 +593,11 @@ bool UITooltip::applyProperty( const StyleSheetProperty& attribute ) { return true; } +void UITooltip::onAlignChange() { + UIWidget::onAlignChange(); + autoAlign(); +} + void UITooltip::onAlphaChange() { Color color( mStyleConfig.FontColor ); color.a = mStyleConfig.FontColor.a * getAlpha() / 255.f; diff --git a/src/eepp/ui/uitreeview.cpp b/src/eepp/ui/uitreeview.cpp index 66abdbbc8..a21e3e09e 100644 --- a/src/eepp/ui/uitreeview.cpp +++ b/src/eepp/ui/uitreeview.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -255,6 +256,25 @@ UIWidget* UITreeView::updateCell( const Vector2& posIndex, const ModelInd cell->reportStyleStateChangeRecursive(); } + if ( getModel()->tooltipModelRoleEnabled() ) { + Variant cls( getModel()->data( index, ModelRole::TooltipClass ) ); + if ( cls.isValid() ) { + cell->createTooltip()->setClass( cls.toString() ); + } else { + cell->createTooltip()->resetClass(); + } + + Variant tooltip( getModel()->data( index, ModelRole::Tooltip ) ); + if ( tooltip.isValid() ) { + if ( tooltip.is( Variant::Type::String ) ) + cell->setTooltipText( tooltip.asString() ); + else if ( tooltip.is( Variant::Type::StringPtr ) ) + cell->setTooltipText( tooltip.asStringPtr() ); + else + cell->setTooltipText( tooltip.toString() ); + } + } + Variant txt( getModel()->data( index, ModelRole::Display ) ); if ( txt.isValid() ) { if ( txt.is( Variant::Type::String ) ) diff --git a/src/eepp/ui/uiwidget.cpp b/src/eepp/ui/uiwidget.cpp index 62477f4ef..908db7b31 100644 --- a/src/eepp/ui/uiwidget.cpp +++ b/src/eepp/ui/uiwidget.cpp @@ -844,16 +844,21 @@ UIWidget* UIWidget::resetClass() { } UIWidget* UIWidget::setClass( const std::string& cls ) { + size_t oldClassesCount = mClasses.size(); if ( mClasses.size() != 1 || mClasses[0] != cls ) { + bool isSet = false; mClasses.clear(); - mClasses.push_back( cls ); + if ( !cls.empty() ) { + mClasses.push_back( cls ); + isSet = true; - if ( !isSceneNodeLoading() && !isLoadingState() ) { - getUISceneNode()->invalidateStyle( this ); - getUISceneNode()->invalidateStyleState( this ); + if ( !isSceneNodeLoading() && !isLoadingState() ) { + getUISceneNode()->invalidateStyle( this ); + getUISceneNode()->invalidateStyleState( this ); + } } - - onClassChange(); + if ( oldClassesCount != mClasses.size() || isSet ) + onClassChange(); } return this; } diff --git a/src/tools/ecode/applayout.xml.hpp b/src/tools/ecode/applayout.xml.hpp index 54d755669..ef72b9fc6 100644 --- a/src/tools/ecode/applayout.xml.hpp +++ b/src/tools/ecode/applayout.xml.hpp @@ -489,6 +489,10 @@ Anchor.error:hover { .indent_tab_listbox_item combobox::dropdownlist::listbox::item { font-family: monospace; } +.git-stash-tooltip { + text-align: left; +} + @media (prefers-color-scheme: light) { .app_hint { diff --git a/src/tools/ecode/plugins/git/git.cpp b/src/tools/ecode/plugins/git/git.cpp index 12c61b38c..686b0f885 100644 --- a/src/tools/ecode/plugins/git/git.cpp +++ b/src/tools/ecode/plugins/git/git.cpp @@ -392,7 +392,7 @@ Git::Branch parseLocalBranch( const std::string_view& raw ) { std::string remote( std::string{ split[2] } ); std::string commitHash( std::string{ split[3] } ); auto ret = Git::Branch{ std::move( name ), std::move( remote ), Git::RefType::Head, - std::move( commitHash ) }; + std::move( commitHash ), "" }; if ( split.size() > 4 ) parseAheadBehind( split[4], ret ); return ret; @@ -406,7 +406,7 @@ static Git::Branch parseRemoteBranch( std::string_view raw ) { std::string remote( std::string{ split[1] } ); std::string commitHash( std::string{ split[3] } ); auto ret = Git::Branch{ std::move( name ), std::move( remote ), Git::RefType::Remote, - std::move( commitHash ) }; + std::move( commitHash ), "" }; if ( split.size() > 4 ) parseAheadBehind( split[4], ret ); return ret; @@ -460,22 +460,26 @@ std::vector Git::getAllBranchesAndTags( RefType ref, std::string_vi } ); } - if ( ( ref & RefType::Stash ) && EXIT_SUCCESS == git( "stash list", projectDir, buf ) ) { + if ( ( ref & RefType::Stash ) && + EXIT_SUCCESS == git( "stash list --date=format:\"%Y-%m-%d %H:%M\"", projectDir, buf ) ) { branches.reserve( branches.size() + StringHelper::countLines( buf ) ); - std::string ptrn( "(stash@{%d+}):%s(.*)" ); + std::string ptrn( "stash@{(.*)}:%s(.*)" ); LuaPattern pattern( ptrn ); + Uint64 id = 0; StringHelper::readBySeparator( buf, [&]( const std::string_view& line ) { PatternMatcher::Range matches[3]; if ( pattern.matches( line.data(), 0, matches, line.size() ) ) { - std::string id( + std::string date( line.substr( matches[1].start, matches[1].end - matches[1].start ) ); std::string name( line.substr( matches[2].start, matches[2].end - matches[2].start ) ); Git::Branch newBranch; newBranch.type = RefType::Stash; newBranch.name = std::move( name ); - newBranch.remote = std::move( id ); + newBranch.remote = String::format( "stash@{%llu}", id ); + newBranch.date = date; branches.emplace_back( std::move( newBranch ) ); + id++; } } ); } diff --git a/src/tools/ecode/plugins/git/git.hpp b/src/tools/ecode/plugins/git/git.hpp index 7832884db..9c104df49 100644 --- a/src/tools/ecode/plugins/git/git.hpp +++ b/src/tools/ecode/plugins/git/git.hpp @@ -211,6 +211,7 @@ class Git { } return nullptr; } + struct Branch { /** Branch name */ std::string name; @@ -220,6 +221,10 @@ class Git { RefType type = All; /** last commit on this branch, may be empty **/ std::string lastCommit; + + /** date string in yyyy-mm-dd hh:mn */ + std::string date; + /** if it's HEAD how much ahead and behind the current local branch is against remote */ int64_t ahead{ 0 }; int64_t behind{ 0 }; diff --git a/src/tools/ecode/plugins/git/gitbranchmodel.cpp b/src/tools/ecode/plugins/git/gitbranchmodel.cpp index 5f7fdd1bd..d87d1d156 100644 --- a/src/tools/ecode/plugins/git/gitbranchmodel.cpp +++ b/src/tools/ecode/plugins/git/gitbranchmodel.cpp @@ -8,7 +8,7 @@ size_t GitBranchModel::hashBranches( const std::vector& branches ) for ( const auto& branch : branches ) hash = hashCombine( hash, String::hash( branch.name ), String::hash( branch.remote ), String::hash( branch.lastCommit ), branch.type, branch.ahead, - branch.behind ); + branch.behind, String::hash( branch.date ) ); return hash; } @@ -105,6 +105,8 @@ Variant GitBranchModel::data( const ModelIndex& index, ModelRole role ) const { return Variant( String::format( "%s (-%ld)", branch.name, branch.behind ) ); } + } else if ( branch.type == Git::Stash ) { + return Variant( String::format( "%s: %s", branch.date, branch.name ) ); } return Variant( branch.name.c_str() ); } @@ -128,6 +130,32 @@ Variant GitBranchModel::data( const ModelIndex& index, ModelRole role ) const { case ModelRole::Icon: { return iconFor( index ); } + case ModelRole::Tooltip: { + if ( index.internalId() != -1 ) { + const Git::Branch& branch = mBranches[index.internalId()].data[index.row()]; + switch ( index.column() ) { + case Column::Name: { + if ( branch.type == Git::Stash ) { + return Variant( String::format( "%s:\n%s", branch.date, branch.name ) ); + } + } + } + } + return Variant( GIT_EMPTY ); + } + case ModelRole::TooltipClass: { + if ( index.internalId() != -1 ) { + const Git::Branch& branch = mBranches[index.internalId()].data[index.row()]; + switch ( index.column() ) { + case Column::Name: { + if ( branch.type == Git::Stash ) { + return Variant( GIT_STASH_TOOLTIP_CLASS ); + } + } + } + } + return Variant( GIT_EMPTY ); + } default: break; } diff --git a/src/tools/ecode/plugins/git/gitbranchmodel.hpp b/src/tools/ecode/plugins/git/gitbranchmodel.hpp index 0522d3410..9e1375e6e 100644 --- a/src/tools/ecode/plugins/git/gitbranchmodel.hpp +++ b/src/tools/ecode/plugins/git/gitbranchmodel.hpp @@ -50,7 +50,9 @@ class GitBranchModel : public Model { Variant data( const ModelIndex& index, ModelRole role ) const; - virtual bool classModelRoleEnabled() { return true; } + bool classModelRoleEnabled() { return true; } + + bool tooltipModelRoleEnabled() { return true; } size_t getHash() const { return mHash; } diff --git a/src/tools/ecode/plugins/git/gitplugin.cpp b/src/tools/ecode/plugins/git/gitplugin.cpp index 4caeb0764..d3ec62be5 100644 --- a/src/tools/ecode/plugins/git/gitplugin.cpp +++ b/src/tools/ecode/plugins/git/gitplugin.cpp @@ -503,8 +503,15 @@ void GitPlugin::onFileSystemEvent( const FileEvent& ev, const FileInfo& file ) { if ( mShuttingDown || isLoading() ) return; - if ( String::startsWith( file.getFilepath(), mGit->getGitFolder() ) && - ( file.getExtension() == "lock" || file.isDirectory() ) ) + if ( file.isDirectory() ) + return; + + auto inGitFolder = file.getFilepath().find( "/.git/" ) != std::string::npos; +#if EE_PLATFORM == EE_PLATFORM_WIN + inGitFolder |= file.getFilepath().find( "\\.git\\" ) != std::string::npos; +#endif + + if ( inGitFolder && file.getExtension() == "lock" ) return; updateUI(); diff --git a/src/tools/ecode/plugins/git/gitplugin.hpp b/src/tools/ecode/plugins/git/gitplugin.hpp index 1490510cb..bc8208f88 100644 --- a/src/tools/ecode/plugins/git/gitplugin.hpp +++ b/src/tools/ecode/plugins/git/gitplugin.hpp @@ -32,6 +32,7 @@ static constexpr const char* GIT_NOT_BOLD = "notbold"; static constexpr const char* GIT_TAG = "tag"; static constexpr const char* GIT_REPO = "repo"; static constexpr const char* GIT_STASH = "git-stash"; +static constexpr const char* GIT_STASH_TOOLTIP_CLASS = "git-stash-tooltip"; class GitPlugin : public PluginBase { public: