mirror of
https://github.com/SpartanJ/eepp.git
synced 2026-10-01 18:50:16 +03:00
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.
This commit is contained in:
+11
-22
@@ -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<UILinearLayout>()->getFirstChild();
|
||||
|
||||
while ( diffView ) {
|
||||
if ( diffView->isType( UI_TYPE_DIFF_VIEW ) ) {
|
||||
configureDiffView( diffView->asType<UIDiffView>() );
|
||||
diffView->asType<UIDiffView>()->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<UILinearLayout>()->getFirstChild();
|
||||
|
||||
while ( diffView ) {
|
||||
if ( diffView->isType( UI_TYPE_DIFF_VIEW ) ) {
|
||||
configureDiffView( diffView->asType<UIDiffView>() );
|
||||
diffView->asType<UIDiffView>()->setSyntaxColorScheme( *getCurrentColorScheme() );
|
||||
}
|
||||
diffView = diffView->getNextNode();
|
||||
for ( auto* diffView : multiDiff->getDiffViews() ) {
|
||||
configureDiffView( diffView );
|
||||
diffView->setSyntaxColorScheme( *getCurrentColorScheme() );
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -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<SyntaxPattern> 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
|
||||
<TextView id="git_commit_message" lw="mp" lh="wc" word-wrap="true"
|
||||
focusable="false" visible="false" />
|
||||
</vbox>
|
||||
<hbox lw="mp" lh="wc" padding-left="8dp" padding-right="8dp"
|
||||
padding-top="4dp" padding-bottom="4dp">
|
||||
<PushButton id="git_commit_files_toggle"
|
||||
tooltip="@string(git_collapse_all_files, Collapse All Files)"
|
||||
icon="icon(collapse-all, 12dp)" class="git_commit_btn" />
|
||||
<PushButton id="git_commit_mode_toggle"
|
||||
text="@string(git_split_diff, Split)"
|
||||
tooltip="@string(git_switch_to_split_diff, Switch to split diff view)"
|
||||
icon="icon(split-horizontal, 12dp)" text-as-fallback="true"
|
||||
margin-left="4dp" class="git_commit_btn" />
|
||||
<TextView id="git_commit_files_status" lw="0" lw8="1" lh="wc"
|
||||
margin-left="8dp" layout_gravity="center_vertical" focusable="false" />
|
||||
</hbox>
|
||||
<TextView id="git_commit_files_status" lw="mp" lh="wc" padding="8dp"
|
||||
focusable="false" />
|
||||
<vbox id="git_commit_diff" lw="mp" lh="0" lw8="1" />
|
||||
</vbox>
|
||||
)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;
|
||||
|
||||
@@ -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<Uint64> generation{ 0 };
|
||||
std::shared_ptr<std::atomic_bool> 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<CustomTokenizer> mStatusCustomTokenizer;
|
||||
std::optional<CustomTokenizer> mCommitStatusCustomTokenizer;
|
||||
std::optional<SyntaxDefinition> 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 );
|
||||
|
||||
Reference in New Issue
Block a user