From 3effffd16f50726c205cf6b28afd19f39dd95a69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Thu, 10 Sep 2026 23:15:46 -0300 Subject: [PATCH] Add shared controls for multi-file diff views - Introduce UIMultiDiffView as a first-class container for multi-file diffs, with a compact toolbar for expanding or collapsing all files, switching between unified and split views, and displaying aggregate file and line-change statistics. - Calculate addition and removal totals during diff preparation so asynchronously prepared diffs do not need to rescan their contents on the UI thread. Cache the child diff views and expose the multi-diff state through the new component. - Use UIMultiDiffView for regular diff tabs and Git commit details, removing the duplicated commit-specific toolbar, styling logic, tokenizer state, and view-mode handling. Preserve the original Git commit toolbar appearance, spacing, icons, tooltips, and colored changed-files summary. - Keep the existing UIDiffView multi-file factory available for compatibility and add regression coverage for aggregate statistics, toolbar visibility, unified/split propagation, collapse state, prepared diffs, and large patches. - Add localized changed-files summaries to all ecode translation catalogs. - Fix the secondary-window UI test to use the appropriate std::string assertion helper. --- bin/assets/i18n/de.xml | 1 + bin/assets/i18n/en.xml | 1 + bin/assets/i18n/fr.xml | 1 + bin/assets/i18n/zh.xml | 1 + include/eepp/ui/tools/uidiffview.hpp | 66 +++++++ src/eepp/ui/tools/uidiffview.cpp | 207 ++++++++++++++++++++- src/tests/unit_tests/uidiffview_tests.cpp | 21 ++- src/tests/unit_tests/uiscenenode_tests.cpp | 2 +- src/tools/ecode/ecode.cpp | 33 ++-- src/tools/ecode/plugins/git/gitplugin.cpp | 109 ++--------- src/tools/ecode/plugins/git/gitplugin.hpp | 13 +- 11 files changed, 318 insertions(+), 137 deletions(-) diff --git a/bin/assets/i18n/de.xml b/bin/assets/i18n/de.xml index 02ddbf361..d5861e19e 100644 --- a/bin/assets/i18n/de.xml +++ b/bin/assets/i18n/de.xml @@ -1292,6 +1292,7 @@ Für sichtbare Änderung ecode neu starten. Keine weiteren Commits Im Git-Verlauf anzeigen Git-Commit-Verlauf – %s + Geänderte Dateien (%zu) +%zu -%zu Commit-SHA Commit-SHA kopieren %s Zur geteilten Diff-Ansicht wechseln diff --git a/bin/assets/i18n/en.xml b/bin/assets/i18n/en.xml index 586295b0e..a2b7d6e32 100644 --- a/bin/assets/i18n/en.xml +++ b/bin/assets/i18n/en.xml @@ -1277,6 +1277,7 @@ Restart ecode to see the changes. No additional commits Show in Git History Git Commit History - %s + Changed files (%zu) +%zu -%zu Commit SHA Copy Commit SHA %s Switch to split diff view diff --git a/bin/assets/i18n/fr.xml b/bin/assets/i18n/fr.xml index de1ce5a2d..e68a81114 100644 --- a/bin/assets/i18n/fr.xml +++ b/bin/assets/i18n/fr.xml @@ -1276,6 +1276,7 @@ Redémarrer ecode pour voir les changements. Aucun commit supplémentaire Afficher dans l’historique Git Historique des commits Git – %s + Fichiers modifiés (%zu) +%zu -%zu SHA du commit Copier le SHA du commit %s Passer à la vue des différences séparée diff --git a/bin/assets/i18n/zh.xml b/bin/assets/i18n/zh.xml index de3b2502c..684bd63bb 100644 --- a/bin/assets/i18n/zh.xml +++ b/bin/assets/i18n/zh.xml @@ -1081,6 +1081,7 @@ file in the directory tree. 没有其他提交 在 Git 历史记录中显示 Git 提交历史记录 - %s + 已更改文件(%zu) +%zu -%zu 提交 SHA 复制提交 SHA %s 切换到拆分差异视图 diff --git a/include/eepp/ui/tools/uidiffview.hpp b/include/eepp/ui/tools/uidiffview.hpp index a00b917ec..c9185c0ee 100644 --- a/include/eepp/ui/tools/uidiffview.hpp +++ b/include/eepp/ui/tools/uidiffview.hpp @@ -17,11 +17,14 @@ class Sprite; namespace UI { class UIScrollView; +class UIPushButton; +class UITextView; namespace Tools { class UIImageViewer; class UIDiffEditorPlugin; +class UIMultiDiffView; class EE_API UIDiffView : public UIWidget, public WidgetCommandExecuter { public: @@ -182,6 +185,8 @@ class EE_API UIDiffView : public UIWidget, public WidgetCommandExecuter { std::string mImageDiffOldPath; std::string mImageDiffNewPath; + friend class UIMultiDiffView; + UIDiffView(); virtual void onSizePolicyChange() override; @@ -231,6 +236,67 @@ class EE_API UIDiffView : public UIWidget, public WidgetCommandExecuter { void updateFileHeaderInfo(); }; +class EE_API UIMultiDiffView : public UILinearLayout { + public: + static UIMultiDiffView* New( const std::string& patchText, const std::string& repoPath = "", + UIDiffView::ViewMode viewMode = UIDiffView::ViewMode::Unified, + bool interactiveFileHeaders = false ); + + static UIMultiDiffView* New( std::shared_ptr preparedDiff, + const std::string& repoPath = "", + UIDiffView::ViewMode viewMode = UIDiffView::ViewMode::Unified, + bool interactiveFileHeaders = false ); + + const std::vector& getDiffViews() const { return mDiffViews; } + + UIScrollView* getScrollView() const { return mScrollView; } + + void setViewMode( UIDiffView::ViewMode mode ); + + UIDiffView::ViewMode getViewMode() const { return mViewMode; } + + void setCollapsed( bool collapsed ); + + bool isCollapsed() const { return mCollapsed; } + + void setToolbarVisible( bool visible ); + + bool isToolbarVisible() const; + + size_t getFileCount() const { return mFileCount; } + + size_t getAddedLines() const { return mAddedLines; } + + size_t getRemovedLines() const { return mRemovedLines; } + + protected: + UILinearLayout* mToolbar{ nullptr }; + UIPushButton* mFilesToggle{ nullptr }; + UIPushButton* mModeToggle{ nullptr }; + UITextView* mFilesStatus{ nullptr }; + UIScrollView* mScrollView{ nullptr }; + std::vector mDiffViews; + UIDiffView::ViewMode mViewMode{ UIDiffView::ViewMode::Unified }; + size_t mFileCount{ 0 }; + size_t mAddedLines{ 0 }; + size_t mRemovedLines{ 0 }; + bool mCollapsed{ false }; + + UIMultiDiffView(); + + void load( std::shared_ptr preparedDiff, + const std::string& repoPath, UIDiffView::ViewMode viewMode, + bool interactiveFileHeaders ); + + void updateFilesToggle(); + + void updateModeToggle(); + + void updateStatus(); + + virtual void onThemeLoaded() override; +}; + } // namespace Tools } // namespace UI } // namespace EE diff --git a/src/eepp/ui/tools/uidiffview.cpp b/src/eepp/ui/tools/uidiffview.cpp index b7b69e6cc..4e12b4577 100644 --- a/src/eepp/ui/tools/uidiffview.cpp +++ b/src/eepp/ui/tools/uidiffview.cpp @@ -14,10 +14,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include @@ -38,6 +40,8 @@ struct UIDiffView::PreparedPatch { class UIDiffView::PreparedMultiFileDiff { public: std::vector patches; + size_t addedLines{ 0 }; + size_t removedLines{ 0 }; }; static bool imagesHaveSameDimensions( const std::string& oldFilePath, @@ -160,6 +164,201 @@ void UIDiffView::setMultiFileCollapsed( UIScrollView* multiDiff, bool collapsed diff->setCollapsed( collapsed ); } +UIMultiDiffView* UIMultiDiffView::New( const std::string& patchText, const std::string& repoPath, + UIDiffView::ViewMode viewMode, + bool interactiveFileHeaders ) { + return New( UIDiffView::prepareMultiFileDiff( patchText ), repoPath, viewMode, + interactiveFileHeaders ); +} + +UIMultiDiffView* +UIMultiDiffView::New( std::shared_ptr preparedDiff, + const std::string& repoPath, UIDiffView::ViewMode viewMode, + bool interactiveFileHeaders ) { + if ( !preparedDiff ) + return nullptr; + auto* view = eeNew( UIMultiDiffView, () ); + view->load( std::move( preparedDiff ), repoPath, viewMode, interactiveFileHeaders ); + return view; +} + +UIMultiDiffView::UIMultiDiffView() : UILinearLayout( "multidiffview", UIOrientation::Vertical ) { + beginAttributesTransaction(); + + setLayoutSizePolicy( SizePolicy::MatchParent, SizePolicy::MatchParent ); + + mToolbar = UILinearLayout::NewHorizontal(); + mToolbar->setParent( this ); + mToolbar->setLayoutSizePolicy( SizePolicy::MatchParent, SizePolicy::WrapContent ); + mToolbar->setPadding( Rectf( 8, 4, 8, 4 ) ); + + mFilesToggle = UIPushButton::New(); + mFilesToggle->setParent( mToolbar ); + mFilesToggle->addClass( "git_commit_btn" ); + mFilesToggle->setLayoutSizePolicy( SizePolicy::WrapContent, SizePolicy::WrapContent ); + mFilesToggle->onClick( [this]( const Event* ) { setCollapsed( !mCollapsed ); } ); + + mModeToggle = UIPushButton::New(); + mModeToggle->setParent( mToolbar ); + mModeToggle->addClass( "git_commit_btn" ); + mModeToggle->setLayoutSizePolicy( SizePolicy::WrapContent, SizePolicy::WrapContent ); + mModeToggle->setLayoutMarginLeft( 4 ); + mModeToggle->setTextAsFallback( true ); + mModeToggle->onClick( [this]( const Event* ) { + setViewMode( mViewMode == UIDiffView::ViewMode::Unified ? UIDiffView::ViewMode::SideBySide + : UIDiffView::ViewMode::Unified ); + } ); + + mFilesStatus = UITextView::New(); + mFilesStatus->setParent( mToolbar ); + mFilesStatus->setLayoutSizePolicy( SizePolicy::Fixed, SizePolicy::WrapContent ); + mFilesStatus->setLayoutWeight( 1 ); + mFilesStatus->setLayoutMarginLeft( 8 ); + mFilesStatus->setGravity( UI_VALIGN_CENTER ); + mFilesStatus->setLayoutGravity( UI_VALIGN_CENTER ); + mFilesStatus->setUsingCustomStyling( true ); + + mScrollView = UIScrollView::New(); + mScrollView->setParent( this ); + mScrollView->setLayoutSizePolicy( SizePolicy::MatchParent, SizePolicy::Fixed ); + mScrollView->setLayoutWeight( 1 ); + + endAttributesTransaction(); +} + +void UIMultiDiffView::load( std::shared_ptr preparedDiff, + const std::string& repoPath, UIDiffView::ViewMode viewMode, + bool interactiveFileHeaders ) { + auto* uiSceneNode = SceneManager::instance()->getUISceneNode(); + const bool wasLoading = uiSceneNode && uiSceneNode->isLoading(); + if ( uiSceneNode ) + uiSceneNode->setIsLoading( true ); + + mViewMode = viewMode; + mFileCount = preparedDiff->patches.size(); + mAddedLines = preparedDiff->addedLines; + mRemovedLines = preparedDiff->removedLines; + auto* content = UILinearLayout::NewVertical(); + content->setParent( mScrollView ); + content->setLayoutSizePolicy( SizePolicy::MatchParent, SizePolicy::WrapContent ); + mDiffViews.reserve( preparedDiff->patches.size() ); + for ( auto& patch : preparedDiff->patches ) { + auto* diffView = UIDiffView::New(); + diffView->setViewMode( viewMode ); + diffView->setLayoutSizePolicy( SizePolicy::MatchParent, SizePolicy::WrapContent ); + diffView->setParent( content ); + diffView->setHeadersVisible( true ); + diffView->setViewModeToggleVisible( false ); + diffView->setCompleteViewToggleVisible( false ); + diffView->setInteractiveFileHeader( interactiveFileHeaders ); + diffView->loadPreparedPatch( std::move( patch ), "", "", repoPath ); + mDiffViews.emplace_back( diffView ); + } + + updateFilesToggle(); + updateModeToggle(); + updateStatus(); + if ( uiSceneNode ) { + uiSceneNode->setIsLoading( wasLoading ); + if ( !wasLoading ) { + uiSceneNode->invalidateStyle( this, true ); + uiSceneNode->invalidateStyleState( this, true, true ); + } + } +} + +void UIMultiDiffView::setViewMode( UIDiffView::ViewMode mode ) { + if ( mViewMode == mode ) + return; + mViewMode = mode; + for ( auto* diff : mDiffViews ) + diff->setViewMode( mode ); + updateModeToggle(); +} + +void UIMultiDiffView::setCollapsed( bool collapsed ) { + if ( mCollapsed == collapsed ) + return; + mCollapsed = collapsed; + for ( auto* diff : mDiffViews ) + diff->setCollapsed( collapsed ); + updateFilesToggle(); +} + +void UIMultiDiffView::setToolbarVisible( bool visible ) { + mToolbar->setVisible( visible ); +} + +bool UIMultiDiffView::isToolbarVisible() const { + return mToolbar->isVisible(); +} + +void UIMultiDiffView::updateFilesToggle() { + const String text = mCollapsed ? i18n( "git_expand_all_files", "Expand All Files" ) + : i18n( "git_collapse_all_files", "Collapse All Files" ); + mFilesToggle->setTooltipText( text ); + if ( auto* scene = getUISceneNode() ) { + if ( auto* icon = scene->findIcon( mCollapsed ? "expand-all" : "collapse-all" ) ) + mFilesToggle->setIcon( icon->createDrawable( PixelDensity::dpToPxI( 12 ) ) ); + } + mFilesToggle->setText( mFilesToggle->hasIcon() ? String{} : text ); +} + +void UIMultiDiffView::updateModeToggle() { + const bool unified = mViewMode == UIDiffView::ViewMode::Unified; + mModeToggle->setText( unified ? i18n( "git_split_diff", "Split" ) + : i18n( "git_unified_diff", "Unified" ) ); + mModeToggle->setTooltipText( + unified ? i18n( "git_switch_to_split_diff", "Switch to split diff view" ) + : i18n( "git_switch_to_unified_diff", "Switch to unified diff view" ) ); + if ( auto* scene = getUISceneNode() ) { + if ( auto* icon = scene->findIcon( unified ? "split-horizontal" : "layout" ) ) + mModeToggle->setIcon( icon->createDrawable( PixelDensity::dpToPxI( 12 ) ) ); + } +} + +void UIMultiDiffView::updateStatus() { + mFilesStatus->setText( String::format( + i18n( "git_changed_files_summary", "Changed files (%zu) +%zu -%zu" ).toUtf8(), mFileCount, + mAddedLines, mRemovedLines ) ); + if ( auto* scene = getUISceneNode(); + scene && scene->getRoot() && scene->getRoot()->getUIStyle() ) { + auto* root = scene->getRoot(); + auto font = root->getUIStyle()->getVariable( "--font" ); + auto warning = root->getUIStyle()->getVariable( "--theme-warning" ); + auto success = root->getUIStyle()->getVariable( "--theme-success" ); + auto error = root->getUIStyle()->getVariable( "--theme-error" ); + std::vector patterns; + patterns.emplace_back( SyntaxPattern( { ".*%((%d+)%)%s+(%+%d+)%s+(%-%d+)" }, + { "normal", "warning", "keyword", "type" } ) ); + SyntaxDefinition definition( "multi_diff_files_status", {}, std::move( patterns ) ); + SyntaxColorScheme scheme( + "multi_diff_files_status", + { { "normal"_sst, + { font.isEmpty() ? mFilesStatus->getFontColor() + : Color::fromString( font.getValue() ) } }, + { "warning"_sst, + { warning.isEmpty() ? Color( 220, 170, 0 ) + : Color::fromString( warning.getValue() ) } }, + { "keyword"_sst, + { success.isEmpty() ? Color( 0, 180, 60 ) + : Color::fromString( success.getValue() ) } }, + { "type"_sst, + { error.isEmpty() ? Color( 220, 50, 70 ) + : Color::fromString( error.getValue() ) } } }, + {} ); + SyntaxTokenizer::tokenizeText( definition, scheme, mFilesStatus->getTextCache() ); + mFilesStatus->invalidateDraw(); + } +} + +void UIMultiDiffView::onThemeLoaded() { + UILinearLayout::onThemeLoaded(); + updateFilesToggle(); + updateModeToggle(); + updateStatus(); +} + class UIDiffEditorPlugin : public UICodeEditorPlugin { public: UIDiffEditorPlugin( UIDiffView* view ) : mView( view ) {} @@ -1410,8 +1609,12 @@ UIDiffView::prepareMultiFileDiff( const std::string& patchText, for ( const auto& diff : diffs ) { if ( cancelled && cancelled->load( std::memory_order_relaxed ) ) return {}; - prepared->patches.emplace_back( - preparePatch( diff, "", SubLineDiffAlgorithm::LCS, cancelled ) ); + auto patch = preparePatch( diff, "", SubLineDiffAlgorithm::LCS, cancelled ); + for ( const auto& line : patch.lines ) { + prepared->addedLines += line.type == DiffLineType::Added; + prepared->removedLines += line.type == DiffLineType::Removed; + } + prepared->patches.emplace_back( std::move( patch ) ); } if ( cancelled && cancelled->load( std::memory_order_relaxed ) ) diff --git a/src/tests/unit_tests/uidiffview_tests.cpp b/src/tests/unit_tests/uidiffview_tests.cpp index e500b9022..4d4781efb 100644 --- a/src/tests/unit_tests/uidiffview_tests.cpp +++ b/src/tests/unit_tests/uidiffview_tests.cpp @@ -125,10 +125,13 @@ diff --git a/second.txt b/second.txt +after )patch"; - auto* viewer = - UIDiffView::NewMultiFileDiffViewer( patchText, "", UIDiffView::ViewMode::SideBySide, true ); - auto diffViews = viewer->findAllByType( UI_TYPE_DIFF_VIEW ); + auto* viewer = UIMultiDiffView::New( patchText, "", UIDiffView::ViewMode::SideBySide, true ); + const auto& diffViews = viewer->getDiffViews(); ASSERT_EQ( size_t{ 2 }, diffViews.size() ); + EXPECT_EQ( size_t{ 2 }, viewer->getFileCount() ); + EXPECT_EQ( size_t{ 2 }, viewer->getAddedLines() ); + EXPECT_EQ( size_t{ 2 }, viewer->getRemovedLines() ); + EXPECT_TRUE( viewer->isToolbarVisible() ); for ( const auto* diffView : diffViews ) { EXPECT_EQ( UIDiffView::ViewMode::SideBySide, diffView->getViewMode() ); EXPECT_FALSE( diffView->isViewModeToggleVisible() ); @@ -136,14 +139,16 @@ diff --git a/second.txt b/second.txt EXPECT_TRUE( diffView->isInteractiveFileHeader() ); } - UIDiffView::setMultiFileViewMode( viewer, UIDiffView::ViewMode::Unified ); - UIDiffView::setMultiFileCollapsed( viewer, true ); + viewer->setViewMode( UIDiffView::ViewMode::Unified ); + viewer->setCollapsed( true ); + EXPECT_EQ( UIDiffView::ViewMode::Unified, viewer->getViewMode() ); + EXPECT_TRUE( viewer->isCollapsed() ); for ( const auto* diffView : diffViews ) { EXPECT_EQ( UIDiffView::ViewMode::Unified, diffView->getViewMode() ); EXPECT_TRUE( diffView->isCollapsed() ); EXPECT_TRUE( diffView->getViewLines().empty() ); } - UIDiffView::setMultiFileCollapsed( viewer, false ); + viewer->setCollapsed( false ); for ( const auto* diffView : diffViews ) { EXPECT_FALSE( diffView->isCollapsed() ); EXPECT_FALSE( diffView->getViewLines().empty() ); @@ -202,8 +207,8 @@ UTEST( UIDiffView, MultiFileViewerHandlesLargePatches ) { "\n+++ b/" + fileName + "\n@@ -1 +1 @@\n-old\n+new\n"; } - auto* viewer = UIDiffView::NewMultiFileDiffViewer( patchText ); - EXPECT_EQ( fileCount, viewer->findAllByType( UI_TYPE_DIFF_VIEW ).size() ); + auto* viewer = UIMultiDiffView::New( patchText ); + EXPECT_EQ( fileCount, viewer->getDiffViews().size() ); EXPECT_TRUE( viewer->findAllByType( UI_TYPE_IMAGE_VIEWER ).empty() ); EXPECT_FALSE( viewer->getUISceneNode()->isLoading() ); diff --git a/src/tests/unit_tests/uiscenenode_tests.cpp b/src/tests/unit_tests/uiscenenode_tests.cpp index 26b9ebfdc..b5d28f40a 100644 --- a/src/tests/unit_tests/uiscenenode_tests.cpp +++ b/src/tests/unit_tests/uiscenenode_tests.cpp @@ -140,7 +140,7 @@ UTEST( UIApplication, CreatesSecondaryWindowWithoutChangingAmbientScene ) { secondaryUI->getWindow()->getInput()->beginInputFrame(); primaryWindow->getInput()->processEventForWindow( &textEvent ); secondaryUI->getWindow()->getInput()->endInputFrame(); - EXPECT_STREQ( textInput->getText().toUtf8().c_str(), "x" ); + EXPECT_STDSTREQ( textInput->getText().toUtf8(), "x" ); } EXPECT_EQ( SceneManager::instance()->getUISceneNode(), app.getUI() ); diff --git a/src/tools/ecode/ecode.cpp b/src/tools/ecode/ecode.cpp index 8a79ddb2e..393851c28 100644 --- a/src/tools/ecode/ecode.cpp +++ b/src/tools/ecode/ecode.cpp @@ -2663,21 +2663,16 @@ void App::loadDiffFromMemory( const std::string& content, const std::string& ori if ( !icon ) icon = getUISceneNode()->findIcon( "file" ); - auto scrollView = UIDiffView::NewMultiFileDiffViewer( - content, repoPath, mConfig.editor.diffViewMode, interactiveFileHeaders ); - auto [tab, iv] = getSplitter()->createWidget( scrollView, diffViewTitle ); + auto multiDiff = UIMultiDiffView::New( content, repoPath, mConfig.editor.diffViewMode, + interactiveFileHeaders ); + auto [tab, iv] = getSplitter()->createWidget( multiDiff, diffViewTitle ); if ( icon ) tab->setIcon( icon->createDrawable( getMenuIconSize() ) ); tab->setText( diffViewTitle ); - auto diffView = scrollView->getFirstChild()->asType()->getFirstChild(); - - while ( diffView ) { - if ( diffView->isType( UI_TYPE_DIFF_VIEW ) ) { - configureDiffView( diffView->asType() ); - diffView->asType()->setSyntaxColorScheme( *getCurrentColorScheme() ); - } - diffView = diffView->getNextNode(); + for ( auto* diffView : multiDiff->getDiffViews() ) { + configureDiffView( diffView ); + diffView->setSyntaxColorScheme( *getCurrentColorScheme() ); } return; } @@ -2719,21 +2714,15 @@ void App::loadDiffFromPath( const std::string& path ) { if ( !icon ) icon = getUISceneNode()->findIcon( "file" ); - auto scrollView = - UIDiffView::NewMultiFileDiffViewer( content, "", mConfig.editor.diffViewMode ); - auto [tab, iv] = getSplitter()->createWidget( scrollView, diffViewTitle ); + auto multiDiff = UIMultiDiffView::New( content, "", mConfig.editor.diffViewMode ); + auto [tab, iv] = getSplitter()->createWidget( multiDiff, diffViewTitle ); if ( icon ) tab->setIcon( icon->createDrawable( getMenuIconSize() ) ); tab->setText( diffViewTitle ); - auto diffView = scrollView->getFirstChild()->asType()->getFirstChild(); - - while ( diffView ) { - if ( diffView->isType( UI_TYPE_DIFF_VIEW ) ) { - configureDiffView( diffView->asType() ); - diffView->asType()->setSyntaxColorScheme( *getCurrentColorScheme() ); - } - diffView = diffView->getNextNode(); + for ( auto* diffView : multiDiff->getDiffViews() ) { + configureDiffView( diffView ); + diffView->setSyntaxColorScheme( *getCurrentColorScheme() ); } return; } diff --git a/src/tools/ecode/plugins/git/gitplugin.cpp b/src/tools/ecode/plugins/git/gitplugin.cpp index 6bfb22c98..8e72b9b52 100644 --- a/src/tools/ecode/plugins/git/gitplugin.cpp +++ b/src/tools/ecode/plugins/git/gitplugin.cpp @@ -646,28 +646,6 @@ void GitPlugin::updateStatusBarSync() { mStatusButton->invalidateDraw(); } -void GitPlugin::styleCommitFilesStatus( UITextView* status ) { - if ( !status ) - return; - status->setUsingCustomStyling( true ); - if ( !mCommitStatusCustomTokenizer.has_value() ) { - std::vector patterns; - patterns.emplace_back( SyntaxPattern( { ".*%((%d+)%)%s+(%+%d+)%s+(%-%d+)" }, - { "normal", "warning", "keyword", "type" } ) ); - SyntaxDefinition syntaxDef( "git_commit_files_status", {}, std::move( patterns ) ); - SyntaxColorScheme scheme( "git_commit_files_status", - { { "normal"_sst, { getVarColor( "--font" ) } }, - { "warning"_sst, { getVarColor( "--theme-warning" ) } }, - { "keyword"_sst, { getVarColor( "--theme-success" ) } }, - { "type"_sst, { getVarColor( "--theme-error" ) } } }, - {} ); - mCommitStatusCustomTokenizer = { std::move( syntaxDef ), std::move( scheme ) }; - } - SyntaxTokenizer::tokenizeText( mCommitStatusCustomTokenizer->def, - mCommitStatusCustomTokenizer->scheme, status->getTextCache() ); - status->invalidateDraw(); -} - void GitPlugin::updateStatus( bool force ) { if ( !mGit || !mGitFound ) return; @@ -843,9 +821,6 @@ PluginRequestHandle GitPlugin::processMessage( const PluginMessage& msg ) { } case ecode::PluginMessageType::UIThemeReloaded: { mStatusCustomTokenizer.reset(); - mCommitStatusCustomTokenizer.reset(); - styleCommitFilesStatus( mCommitDetails.status ); - styleCommitFilesStatus( mDetachedHistory.details.status ); updateUINow( true ); break; } @@ -3060,19 +3035,8 @@ void GitPlugin::CommitDetailsState::openCommitDetails( GitPlugin& plugin, const - - - - - + )xml" ); @@ -3086,8 +3050,6 @@ void GitPlugin::CommitDetailsState::openCommitDetails( GitPlugin& plugin, const view->bind( "git_commit_message", message ); view->bind( "git_commit_files_status", status ); view->bind( "git_commit_message_toggle", messageToggle ); - view->bind( "git_commit_files_toggle", filesToggle ); - view->bind( "git_commit_mode_toggle", modeToggle ); view->bind( "git_commit_github", gitHub ); view->bind( "git_commit_diff", diffContainer ); messageToggle->onClick( [owner, state]( const Event* ) { @@ -3099,34 +3061,6 @@ void GitPlugin::CommitDetailsState::openCommitDetails( GitPlugin& plugin, const "Collapse Commit Description" ) : owner->i18n( "git_expand_commit_description", "Expand Commit Description" ) ); } ); - filesToggle->onClick( [owner, state]( const Event* ) { - state->filesCollapsed = !state->filesCollapsed; - UIDiffView::setMultiFileCollapsed( state->diff, state->filesCollapsed ); - state->filesToggle->setTooltipText( - state->filesCollapsed - ? owner->i18n( "git_expand_all_files", "Expand All Files" ) - : owner->i18n( "git_collapse_all_files", "Collapse All Files" ) ); - if ( auto* icon = - owner->findIcon( state->filesCollapsed ? "expand-all" : "collapse-all" ) ) - state->filesToggle->setIcon( icon->createDrawable( PixelDensity::dpToPxI( 12 ) ) ); - } ); - modeToggle->onClick( [owner, state]( const Event* ) { - state->viewMode = state->viewMode == UIDiffView::ViewMode::Unified - ? UIDiffView::ViewMode::SideBySide - : UIDiffView::ViewMode::Unified; - UIDiffView::setMultiFileViewMode( state->diff, state->viewMode ); - state->modeToggle->setText( state->viewMode == UIDiffView::ViewMode::Unified - ? owner->i18n( "git_split_diff", "Split" ) - : owner->i18n( "git_unified_diff", "Unified" ) ); - state->modeToggle->setTooltipText( - state->viewMode == UIDiffView::ViewMode::Unified - ? owner->i18n( "git_switch_to_split_diff", "Switch to split diff view" ) - : owner->i18n( "git_switch_to_unified_diff", "Switch to unified diff view" ) ); - if ( auto* icon = owner->findIcon( state->viewMode == UIDiffView::ViewMode::Unified - ? "split-horizontal" - : "layout" ) ) - state->modeToggle->setIcon( icon->createDrawable( PixelDensity::dpToPxI( 12 ) ) ); - } ); gitHub->onClick( [state]( const Event* ) { if ( !state->url.empty() ) Engine::instance()->openURI( state->url ); @@ -3158,11 +3092,7 @@ void GitPlugin::CommitDetailsState::openCommitDetails( GitPlugin& plugin, const message->setVisible( false ); messageToggle->setVisible( false ); status->setText( plugin.i18n( "git_loading_changed_files", "Loading changed files..." ) ); - plugin.styleCommitFilesStatus( status ); - filesCollapsed = false; - filesToggle->setTooltipText( plugin.i18n( "git_collapse_all_files", "Collapse All Files" ) ); - if ( auto* icon = plugin.findIcon( "collapse-all" ) ) - filesToggle->setIcon( icon->createDrawable( PixelDensity::dpToPxI( 12 ) ) ); + status->setVisible( true ); url.clear(); gitHub->setVisible( false ); view->find( "git_commit_sha" )->setVisible( !isWorkingTree ); @@ -3233,7 +3163,6 @@ void GitPlugin::CommitDetailsState::loadCommitFiles( GitPlugin& plugin, bool det details.status->setText( plugin->i18n( "git_changed_files_error", "Could not load changed files" ) + ( result.result.empty() ? "" : ": " + result.result ) ); - plugin->styleCommitFilesStatus( details.status ); return; } std::string message = std::move( result.message ); @@ -3258,42 +3187,28 @@ void GitPlugin::CommitDetailsState::loadCommitFiles( GitPlugin& plugin, bool det details.messageToggle->setText( plugin->i18n( "git_expand_commit_description", "Expand Commit Description" ) ); - int totalInserts = 0; - int totalDeletes = 0; - for ( const auto& file : result.files ) { - totalInserts += file.inserts; - totalDeletes += file.deletes; - } if ( result.files.empty() ) { details.status->setText( plugin->i18n( "git_no_changed_files", "No changed files" ) ); } else { - details.status->setText( String::format( - plugin->i18n( "git_changed_files_summary", "Changed files (%zu) +%d -%d" ) - .toUtf8(), - result.files.size(), totalInserts, totalDeletes ) ); + details.status->setVisible( false ); } - plugin->styleCommitFilesStatus( details.status ); details.url = std::move( result.commitURL ); details.gitHub->setVisible( !details.url.empty() ); details.diffContainer->closeAllChildren(); details.diff = nullptr; if ( preparedDiff ) { - details.diff = UIDiffView::NewMultiFileDiffViewer( std::move( preparedDiff ), repo, - details.viewMode ); + details.diff = UIMultiDiffView::New( std::move( preparedDiff ), repo, + UIDiffView::ViewMode::Unified, true ); details.diff->setLayoutSizePolicy( SizePolicy::MatchParent, SizePolicy::MatchParent ); details.diff->setParent( details.diffContainer ); - for ( auto* diff : UIDiffView::multiFileDiffViews( details.diff ) ) { - diff->setInteractiveFileHeader( true ); + for ( auto* diff : details.diff->getDiffViews() ) { if ( const auto* scheme = plugin->getPluginContext()->getCurrentColorScheme() ) diff->setSyntaxColorScheme( *scheme ); } } - const bool hasDiff = details.diff != nullptr; - details.filesToggle->setVisible( hasDiff ); - details.modeToggle->setVisible( hasDiff ); } ); } ); } @@ -3496,6 +3411,16 @@ void GitPlugin::buildSidePanelTab() { #git_commit_details .git_commit_btn:hover { border-color: var(--primary); } + multidiffview .git_commit_btn { + lw: 20dp; + lh: 20dp; + padding: 0; + background-color: var(--list-back); + border-color: transparent; + } + multidiffview .git_commit_btn:hover { + border-color: var(--primary); + } #git_commit_details #git_commit_author { font-size: 11dp; text-stroke-width: 1dp; diff --git a/src/tools/ecode/plugins/git/gitplugin.hpp b/src/tools/ecode/plugins/git/gitplugin.hpp index 078880554..69a8c22cb 100644 --- a/src/tools/ecode/plugins/git/gitplugin.hpp +++ b/src/tools/ecode/plugins/git/gitplugin.hpp @@ -178,11 +178,9 @@ class GitPlugin : public PluginBase { UITextView* message{ nullptr }; UITextView* status{ nullptr }; UIPushButton* messageToggle{ nullptr }; - UIPushButton* filesToggle{ nullptr }; - UIPushButton* modeToggle{ nullptr }; UIPushButton* gitHub{ nullptr }; UIWidget* diffContainer{ nullptr }; - UIScrollView* diff{ nullptr }; + Tools::UIMultiDiffView* diff{ nullptr }; std::string messageBody; std::string url; Git::Commit commit; @@ -190,9 +188,7 @@ class GitPlugin : public PluginBase { std::atomic generation{ 0 }; std::shared_ptr diffPreparationCancelled; EventConnection closeConnection; - Tools::UIDiffView::ViewMode viewMode{ Tools::UIDiffView::ViewMode::Unified }; bool messageExpanded{ false }; - bool filesCollapsed{ false }; bool workingTree{ false }; void openCommitDetails( GitPlugin& plugin, const Git::Commit& commit, bool detached, @@ -216,8 +212,6 @@ class GitPlugin : public PluginBase { message = nullptr; status = nullptr; messageToggle = nullptr; - filesToggle = nullptr; - modeToggle = nullptr; gitHub = nullptr; diffContainer = nullptr; diff = nullptr; @@ -225,9 +219,7 @@ class GitPlugin : public PluginBase { url.clear(); commit = {}; repo.clear(); - viewMode = Tools::UIDiffView::ViewMode::Unified; messageExpanded = false; - filesCollapsed = false; workingTree = false; } }; @@ -278,7 +270,6 @@ class GitPlugin : public PluginBase { SyntaxColorScheme scheme; }; std::optional mStatusCustomTokenizer; - std::optional mCommitStatusCustomTokenizer; std::optional mTooltipCustomSyntaxDef; Uint32 mModelChangedId{ 0 }; Uint32 mModelStylerId{ 0 }; @@ -365,8 +356,6 @@ class GitPlugin : public PluginBase { void updateStatusBarSync(); - void styleCommitFilesStatus( UITextView* status ); - void updateUI(); void updateUINow( bool force = false );