refactor: rewrite the histories filter function to supports both include and exclude modes (#690)

Signed-off-by: leo <longshuang@msn.cn>
This commit is contained in:
leo 2024-11-13 21:45:28 +08:00
parent e3ffe3ef6c
commit ca5bc4b4df
No known key found for this signature in database
27 changed files with 767 additions and 309 deletions

View file

@ -0,0 +1,22 @@
using Avalonia.Data.Converters;
using Avalonia.Media;
namespace SourceGit.Converters
{
public static class FilterModeConverters
{
public static readonly FuncValueConverter<Models.FilterMode, IBrush> ToBorderBrush =
new FuncValueConverter<Models.FilterMode, IBrush>(v =>
{
switch (v)
{
case Models.FilterMode.Included:
return Brushes.Green;
case Models.FilterMode.Excluded:
return Brushes.Red;
default:
return Brushes.Transparent;
}
});
}
}

60
src/Models/Filter.cs Normal file
View file

@ -0,0 +1,60 @@
using CommunityToolkit.Mvvm.ComponentModel;
namespace SourceGit.Models
{
public enum FilterType
{
LocalBranch = 0,
LocalBranchFolder,
RemoteBranch,
RemoteBranchFolder,
Tag,
}
public enum FilterMode
{
None = 0,
Included,
Excluded,
}
public class Filter : ObservableObject
{
public string Pattern
{
get => _pattern;
set => SetProperty(ref _pattern, value);
}
public FilterType Type
{
get;
set;
} = FilterType.LocalBranch;
public FilterMode Mode
{
get => _mode;
set => SetProperty(ref _mode, value);
}
public bool IsBranch
{
get => Type != FilterType.Tag;
}
public Filter()
{
}
public Filter(string pattern, FilterType type, FilterMode mode)
{
_pattern = pattern;
_mode = mode;
Type = type;
}
private string _pattern = string.Empty;
private FilterMode _mode = FilterMode.None;
}
}

View file

@ -1,4 +1,9 @@
using Avalonia.Collections; using System;
using System.Collections.Generic;
using System.Text;
using Avalonia.Collections;
using Avalonia.Threading;
namespace SourceGit.Models namespace SourceGit.Models
{ {
@ -76,11 +81,11 @@ namespace SourceGit.Models
set; set;
} = true; } = true;
public AvaloniaList<string> Filters public AvaloniaList<Filter> HistoriesFilters
{ {
get; get;
set; set;
} = new AvaloniaList<string>(); } = new AvaloniaList<Filter>();
public AvaloniaList<CommitTemplate> CommitTemplates public AvaloniaList<CommitTemplate> CommitTemplates
{ {
@ -148,6 +153,179 @@ namespace SourceGit.Models
set; set;
} = "---"; } = "---";
public FilterMode GetHistoriesFilterMode(string pattern, FilterType type)
{
foreach (var filter in HistoriesFilters)
{
if (filter.Type != type)
continue;
if (filter.Pattern.Equals(pattern, StringComparison.Ordinal))
return filter.Mode;
}
return FilterMode.None;
}
public bool UpdateHistoriesFilter(string pattern, FilterType type, FilterMode mode)
{
for (int i = 0; i < HistoriesFilters.Count; i++)
{
var filter = HistoriesFilters[i];
if (filter.Type != type)
continue;
if (filter.Pattern.Equals(pattern, StringComparison.Ordinal))
{
if (mode == FilterMode.None)
{
HistoriesFilters.RemoveAt(i);
return true;
}
if (mode != filter.Mode)
{
filter.Mode = mode;
return true;
}
}
}
if (mode != FilterMode.None)
{
HistoriesFilters.Add(new Filter(pattern, type, mode));
return true;
}
return false;
}
public string BuildHistoriesFilter()
{
var builder = new StringBuilder();
var excludedBranches = new List<string>();
var excludedRemotes = new List<string>();
var excludedTags = new List<string>();
var includedBranches = new List<string>();
var includedRemotes = new List<string>();
var includedTags = new List<string>();
foreach (var filter in HistoriesFilters)
{
if (filter.Type == FilterType.LocalBranch)
{
var name = filter.Pattern.Substring(11);
var b = $"{name.Substring(0, name.Length - 1)}[{name[^1]}]";
if (filter.Mode == FilterMode.Included)
includedBranches.Add(b);
else if (filter.Mode == FilterMode.Excluded)
excludedBranches.Add(b);
}
else if (filter.Type == FilterType.LocalBranchFolder)
{
if (filter.Mode == FilterMode.Included)
includedBranches.Add($"{filter.Pattern.Substring(11)}/*");
else if (filter.Mode == FilterMode.Excluded)
excludedBranches.Add($"{filter.Pattern.Substring(11)}/*");
}
else if (filter.Type == FilterType.RemoteBranch)
{
var name = filter.Pattern.Substring(13);
var r = $"{name.Substring(0, name.Length - 1)}[{name[^1]}]";
if (filter.Mode == FilterMode.Included)
includedRemotes.Add(r);
else if (filter.Mode == FilterMode.Excluded)
excludedRemotes.Add(r);
}
else if (filter.Type == FilterType.RemoteBranchFolder)
{
if (filter.Mode == FilterMode.Included)
includedRemotes.Add($"{filter.Pattern.Substring(13)}/*");
else if (filter.Mode == FilterMode.Excluded)
excludedRemotes.Add($"{filter.Pattern.Substring(13)}/*");
}
else if (filter.Type == FilterType.Tag)
{
var name = filter.Pattern;
var t = $"{name.Substring(0, name.Length - 1)}[{name[^1]}]";
if (filter.Mode == FilterMode.Included)
includedTags.Add(t);
else if (filter.Mode == FilterMode.Excluded)
excludedTags.Add(t);
}
}
foreach (var b in excludedBranches)
{
builder.Append("--exclude=");
builder.Append(b);
builder.Append(' ');
}
if (includedBranches.Count > 0)
{
foreach (var b in includedBranches)
{
builder.Append("--branches=");
builder.Append(b);
builder.Append(' ');
}
}
else if (excludedBranches.Count > 0 || (includedRemotes.Count == 0 && includedTags.Count == 0))
{
builder.Append("--exclude=HEA[D] ");
builder.Append("--branches ");
}
foreach (var r in excludedRemotes)
{
builder.Append("--exclude=");
builder.Append(r);
builder.Append(' ');
}
if (includedRemotes.Count > 0)
{
foreach (var r in includedRemotes)
{
builder.Append("--remotes=");
builder.Append(r);
builder.Append(' ');
}
}
else if (excludedRemotes.Count > 0 || (includedBranches.Count == 0 && includedTags.Count == 0))
{
builder.Append("--exclude=origin/HEA[D] ");
builder.Append("--remotes ");
}
foreach (var t in excludedTags)
{
builder.Append("--exclude=");
builder.Append(t);
builder.Append(' ');
}
if (includedTags.Count > 0)
{
foreach (var t in includedTags)
{
builder.Append("--tags=");
builder.Append(t);
builder.Append(' ');
}
}
else if (excludedTags.Count > 0 || (includedBranches.Count == 0 && includedRemotes.Count == 0))
{
builder.Append("--tags ");
}
return builder.ToString();
}
public void PushCommitMessage(string message) public void PushCommitMessage(string message)
{ {
var existIdx = CommitMessages.IndexOf(message); var existIdx = CommitMessages.IndexOf(message);

View file

@ -1,10 +1,19 @@
namespace SourceGit.Models using CommunityToolkit.Mvvm.ComponentModel;
namespace SourceGit.Models
{ {
public class Tag public class Tag : ObservableObject
{ {
public string Name { get; set; } public string Name { get; set; } = string.Empty;
public string SHA { get; set; } public string SHA { get; set; } = string.Empty;
public string Message { get; set; } public string Message { get; set; } = string.Empty;
public bool IsFiltered { get; set; }
public FilterMode FilterMode
{
get => _filterMode;
set => SetProperty(ref _filterMode, value);
}
private FilterMode _filterMode = FilterMode.None;
} }
} }

View file

@ -113,22 +113,11 @@ namespace SourceGit.Models
if (_updateTags > 0) if (_updateTags > 0)
{ {
_updateTags = 0; _updateTags = 0;
Task.Run(() => Task.Run(_repo.RefreshTags);
{
_repo.RefreshTags();
_repo.RefreshBranches();
_repo.RefreshCommits();
});
}
else
{
Task.Run(() =>
{
_repo.RefreshBranches();
_repo.RefreshCommits();
});
} }
Task.Run(_repo.RefreshBranches);
Task.Run(_repo.RefreshCommits);
Task.Run(_repo.RefreshWorkingCopyChanges); Task.Run(_repo.RefreshWorkingCopyChanges);
Task.Run(_repo.RefreshWorktrees); Task.Run(_repo.RefreshWorktrees);
} }

View file

@ -292,7 +292,6 @@
<x:String x:Key="Text.FileHistory" xml:space="preserve">Datei Historie</x:String> <x:String x:Key="Text.FileHistory" xml:space="preserve">Datei Historie</x:String>
<x:String x:Key="Text.FileHistory.FileContent" xml:space="preserve">INHALT</x:String> <x:String x:Key="Text.FileHistory.FileContent" xml:space="preserve">INHALT</x:String>
<x:String x:Key="Text.FileHistory.FileChange" xml:space="preserve">ÄNDERUNGEN</x:String> <x:String x:Key="Text.FileHistory.FileChange" xml:space="preserve">ÄNDERUNGEN</x:String>
<x:String x:Key="Text.Filter" xml:space="preserve">FILTER</x:String>
<x:String x:Key="Text.GitFlow" xml:space="preserve">Git-Flow</x:String> <x:String x:Key="Text.GitFlow" xml:space="preserve">Git-Flow</x:String>
<x:String x:Key="Text.GitFlow.DevelopBranch" xml:space="preserve">Development-Branch:</x:String> <x:String x:Key="Text.GitFlow.DevelopBranch" xml:space="preserve">Development-Branch:</x:String>
<x:String x:Key="Text.GitFlow.Feature" xml:space="preserve">Feature:</x:String> <x:String x:Key="Text.GitFlow.Feature" xml:space="preserve">Feature:</x:String>
@ -544,7 +543,7 @@
<x:String x:Key="Text.Repository.EnableReflog" xml:space="preserve">Aktiviere '--reflog' Option</x:String> <x:String x:Key="Text.Repository.EnableReflog" xml:space="preserve">Aktiviere '--reflog' Option</x:String>
<x:String x:Key="Text.Repository.Explore" xml:space="preserve">Öffne im Datei-Browser</x:String> <x:String x:Key="Text.Repository.Explore" xml:space="preserve">Öffne im Datei-Browser</x:String>
<x:String x:Key="Text.Repository.Filter" xml:space="preserve">Suche Branches/Tags/Submodule</x:String> <x:String x:Key="Text.Repository.Filter" xml:space="preserve">Suche Branches/Tags/Submodule</x:String>
<x:String x:Key="Text.Repository.FilterCommitPrefix" xml:space="preserve">GEFILTERT:</x:String> <x:String x:Key="Text.Repository.FilterCommits.Prefix" xml:space="preserve">GEFILTERT:</x:String>
<x:String x:Key="Text.Repository.LocalBranches" xml:space="preserve">LOKALE BRANCHES</x:String> <x:String x:Key="Text.Repository.LocalBranches" xml:space="preserve">LOKALE BRANCHES</x:String>
<x:String x:Key="Text.Repository.NavigateToCurrentHead" xml:space="preserve">Zum HEAD wechseln</x:String> <x:String x:Key="Text.Repository.NavigateToCurrentHead" xml:space="preserve">Zum HEAD wechseln</x:String>
<x:String x:Key="Text.Repository.FirstParentFilterToggle" xml:space="preserve">Aktiviere '--first-parent' Option</x:String> <x:String x:Key="Text.Repository.FirstParentFilterToggle" xml:space="preserve">Aktiviere '--first-parent' Option</x:String>

View file

@ -289,7 +289,6 @@
<x:String x:Key="Text.FileHistory" xml:space="preserve">File History</x:String> <x:String x:Key="Text.FileHistory" xml:space="preserve">File History</x:String>
<x:String x:Key="Text.FileHistory.FileContent" xml:space="preserve">CONTENT</x:String> <x:String x:Key="Text.FileHistory.FileContent" xml:space="preserve">CONTENT</x:String>
<x:String x:Key="Text.FileHistory.FileChange" xml:space="preserve">CHANGE</x:String> <x:String x:Key="Text.FileHistory.FileChange" xml:space="preserve">CHANGE</x:String>
<x:String x:Key="Text.Filter" xml:space="preserve">FILTER</x:String>
<x:String x:Key="Text.GitFlow" xml:space="preserve">Git-Flow</x:String> <x:String x:Key="Text.GitFlow" xml:space="preserve">Git-Flow</x:String>
<x:String x:Key="Text.GitFlow.DevelopBranch" xml:space="preserve">Development Branch:</x:String> <x:String x:Key="Text.GitFlow.DevelopBranch" xml:space="preserve">Development Branch:</x:String>
<x:String x:Key="Text.GitFlow.Feature" xml:space="preserve">Feature:</x:String> <x:String x:Key="Text.GitFlow.Feature" xml:space="preserve">Feature:</x:String>
@ -541,7 +540,10 @@
<x:String x:Key="Text.Repository.EnableReflog" xml:space="preserve">Enable '--reflog' Option</x:String> <x:String x:Key="Text.Repository.EnableReflog" xml:space="preserve">Enable '--reflog' Option</x:String>
<x:String x:Key="Text.Repository.Explore" xml:space="preserve">Open in File Browser</x:String> <x:String x:Key="Text.Repository.Explore" xml:space="preserve">Open in File Browser</x:String>
<x:String x:Key="Text.Repository.Filter" xml:space="preserve">Search Branches/Tags/Submodules</x:String> <x:String x:Key="Text.Repository.Filter" xml:space="preserve">Search Branches/Tags/Submodules</x:String>
<x:String x:Key="Text.Repository.FilterCommitPrefix" xml:space="preserve">FILTERED BY:</x:String> <x:String x:Key="Text.Repository.FilterCommits.Default" xml:space="preserve">Unset (Default)</x:String>
<x:String x:Key="Text.Repository.FilterCommits.Exclude" xml:space="preserve">Hide in commit graph</x:String>
<x:String x:Key="Text.Repository.FilterCommits.Include" xml:space="preserve">Filter in commit graph</x:String>
<x:String x:Key="Text.Repository.FilterCommits.Prefix" xml:space="preserve">FILTERED BY:</x:String>
<x:String x:Key="Text.Repository.LocalBranches" xml:space="preserve">LOCAL BRANCHES</x:String> <x:String x:Key="Text.Repository.LocalBranches" xml:space="preserve">LOCAL BRANCHES</x:String>
<x:String x:Key="Text.Repository.NavigateToCurrentHead" xml:space="preserve">Navigate to HEAD</x:String> <x:String x:Key="Text.Repository.NavigateToCurrentHead" xml:space="preserve">Navigate to HEAD</x:String>
<x:String x:Key="Text.Repository.FirstParentFilterToggle" xml:space="preserve">Enable '--first-parent' Option</x:String> <x:String x:Key="Text.Repository.FirstParentFilterToggle" xml:space="preserve">Enable '--first-parent' Option</x:String>

View file

@ -294,7 +294,6 @@
<x:String x:Key="Text.FileHistory" xml:space="preserve">Historial de Archivos</x:String> <x:String x:Key="Text.FileHistory" xml:space="preserve">Historial de Archivos</x:String>
<x:String x:Key="Text.FileHistory.FileContent" xml:space="preserve">CONTENIDO</x:String> <x:String x:Key="Text.FileHistory.FileContent" xml:space="preserve">CONTENIDO</x:String>
<x:String x:Key="Text.FileHistory.FileChange" xml:space="preserve">CAMBIO</x:String> <x:String x:Key="Text.FileHistory.FileChange" xml:space="preserve">CAMBIO</x:String>
<x:String x:Key="Text.Filter" xml:space="preserve">FILTRO</x:String>
<x:String x:Key="Text.GitFlow" xml:space="preserve">Git-Flow</x:String> <x:String x:Key="Text.GitFlow" xml:space="preserve">Git-Flow</x:String>
<x:String x:Key="Text.GitFlow.DevelopBranch" xml:space="preserve">Rama de Desarrollo:</x:String> <x:String x:Key="Text.GitFlow.DevelopBranch" xml:space="preserve">Rama de Desarrollo:</x:String>
<x:String x:Key="Text.GitFlow.Feature" xml:space="preserve">Feature:</x:String> <x:String x:Key="Text.GitFlow.Feature" xml:space="preserve">Feature:</x:String>
@ -542,7 +541,7 @@
<x:String x:Key="Text.Repository.EnableReflog" xml:space="preserve">Habilitar Opción '--reflog'</x:String> <x:String x:Key="Text.Repository.EnableReflog" xml:space="preserve">Habilitar Opción '--reflog'</x:String>
<x:String x:Key="Text.Repository.Explore" xml:space="preserve">Abrir en el Explorador</x:String> <x:String x:Key="Text.Repository.Explore" xml:space="preserve">Abrir en el Explorador</x:String>
<x:String x:Key="Text.Repository.Filter" xml:space="preserve">Buscar Ramas/Etiquetas/Submódulos</x:String> <x:String x:Key="Text.Repository.Filter" xml:space="preserve">Buscar Ramas/Etiquetas/Submódulos</x:String>
<x:String x:Key="Text.Repository.FilterCommitPrefix" xml:space="preserve">FILTRAR POR:</x:String> <x:String x:Key="Text.Repository.FilterCommits.Prefix" xml:space="preserve">FILTRAR POR:</x:String>
<x:String x:Key="Text.Repository.LocalBranches" xml:space="preserve">RAMAS LOCALES</x:String> <x:String x:Key="Text.Repository.LocalBranches" xml:space="preserve">RAMAS LOCALES</x:String>
<x:String x:Key="Text.Repository.NavigateToCurrentHead" xml:space="preserve">Navegar a HEAD</x:String> <x:String x:Key="Text.Repository.NavigateToCurrentHead" xml:space="preserve">Navegar a HEAD</x:String>
<x:String x:Key="Text.Repository.FirstParentFilterToggle" xml:space="preserve">Habilitar Opción '--first-parent'</x:String> <x:String x:Key="Text.Repository.FirstParentFilterToggle" xml:space="preserve">Habilitar Opción '--first-parent'</x:String>

View file

@ -292,7 +292,6 @@
<x:String x:Key="Text.FileHistory" xml:space="preserve">Historique du fichier</x:String> <x:String x:Key="Text.FileHistory" xml:space="preserve">Historique du fichier</x:String>
<x:String x:Key="Text.FileHistory.FileContent" xml:space="preserve">CONTENU</x:String> <x:String x:Key="Text.FileHistory.FileContent" xml:space="preserve">CONTENU</x:String>
<x:String x:Key="Text.FileHistory.FileChange" xml:space="preserve">MODIFICATION</x:String> <x:String x:Key="Text.FileHistory.FileChange" xml:space="preserve">MODIFICATION</x:String>
<x:String x:Key="Text.Filter" xml:space="preserve">FILTRER</x:String>
<x:String x:Key="Text.GitFlow" xml:space="preserve">Git-Flow</x:String> <x:String x:Key="Text.GitFlow" xml:space="preserve">Git-Flow</x:String>
<x:String x:Key="Text.GitFlow.DevelopBranch" xml:space="preserve">Branche de développement :</x:String> <x:String x:Key="Text.GitFlow.DevelopBranch" xml:space="preserve">Branche de développement :</x:String>
<x:String x:Key="Text.GitFlow.Feature" xml:space="preserve">Feature:</x:String> <x:String x:Key="Text.GitFlow.Feature" xml:space="preserve">Feature:</x:String>
@ -543,7 +542,7 @@
<x:String x:Key="Text.Repository.EnableReflog" xml:space="preserve">Activer l'option '--reflog'</x:String> <x:String x:Key="Text.Repository.EnableReflog" xml:space="preserve">Activer l'option '--reflog'</x:String>
<x:String x:Key="Text.Repository.Explore" xml:space="preserve">Ouvrir dans l'explorateur de fichiers</x:String> <x:String x:Key="Text.Repository.Explore" xml:space="preserve">Ouvrir dans l'explorateur de fichiers</x:String>
<x:String x:Key="Text.Repository.Filter" xml:space="preserve">Rechercher Branches/Tags/Submodules</x:String> <x:String x:Key="Text.Repository.Filter" xml:space="preserve">Rechercher Branches/Tags/Submodules</x:String>
<x:String x:Key="Text.Repository.FilterCommitPrefix" xml:space="preserve">FILTRE PAR :</x:String> <x:String x:Key="Text.Repository.FilterCommits.Prefix" xml:space="preserve">FILTRE PAR :</x:String>
<x:String x:Key="Text.Repository.LocalBranches" xml:space="preserve">BRANCHES LOCALES</x:String> <x:String x:Key="Text.Repository.LocalBranches" xml:space="preserve">BRANCHES LOCALES</x:String>
<x:String x:Key="Text.Repository.NavigateToCurrentHead" xml:space="preserve">Naviguer vers le HEAD</x:String> <x:String x:Key="Text.Repository.NavigateToCurrentHead" xml:space="preserve">Naviguer vers le HEAD</x:String>
<x:String x:Key="Text.Repository.FirstParentFilterToggle" xml:space="preserve">Activer l'option '--first-parent'</x:String> <x:String x:Key="Text.Repository.FirstParentFilterToggle" xml:space="preserve">Activer l'option '--first-parent'</x:String>

View file

@ -317,7 +317,6 @@
<x:String x:Key="Text.FileHistory" xml:space="preserve">Histórico de Arquivos</x:String> <x:String x:Key="Text.FileHistory" xml:space="preserve">Histórico de Arquivos</x:String>
<x:String x:Key="Text.FileHistory.FileContent" xml:space="preserve">CONTEUDO</x:String> <x:String x:Key="Text.FileHistory.FileContent" xml:space="preserve">CONTEUDO</x:String>
<x:String x:Key="Text.FileHistory.FileChange" xml:space="preserve">MUDANÇA</x:String> <x:String x:Key="Text.FileHistory.FileChange" xml:space="preserve">MUDANÇA</x:String>
<x:String x:Key="Text.Filter" xml:space="preserve">FILTRO</x:String>
<x:String x:Key="Text.GitFlow.DevelopBranch" xml:space="preserve">Branch de Desenvolvimento:</x:String> <x:String x:Key="Text.GitFlow.DevelopBranch" xml:space="preserve">Branch de Desenvolvimento:</x:String>
<x:String x:Key="Text.GitFlow.Feature" xml:space="preserve">Feature:</x:String> <x:String x:Key="Text.GitFlow.Feature" xml:space="preserve">Feature:</x:String>
<x:String x:Key="Text.GitFlow.FeaturePrefix" xml:space="preserve">Prefixo da Feature:</x:String> <x:String x:Key="Text.GitFlow.FeaturePrefix" xml:space="preserve">Prefixo da Feature:</x:String>
@ -566,7 +565,7 @@
<x:String x:Key="Text.Repository.EnableReflog" xml:space="preserve">Habilitar opção '--reflog'</x:String> <x:String x:Key="Text.Repository.EnableReflog" xml:space="preserve">Habilitar opção '--reflog'</x:String>
<x:String x:Key="Text.Repository.Explore" xml:space="preserve">Abrir no Navegador de Arquivos</x:String> <x:String x:Key="Text.Repository.Explore" xml:space="preserve">Abrir no Navegador de Arquivos</x:String>
<x:String x:Key="Text.Repository.Filter" xml:space="preserve">Pesquisar Branches/Tags/Submódulos</x:String> <x:String x:Key="Text.Repository.Filter" xml:space="preserve">Pesquisar Branches/Tags/Submódulos</x:String>
<x:String x:Key="Text.Repository.FilterCommitPrefix" xml:space="preserve">FILTRADO POR:</x:String> <x:String x:Key="Text.Repository.FilterCommits.Prefix" xml:space="preserve">FILTRADO POR:</x:String>
<x:String x:Key="Text.Repository.FirstParentFilterToggle" xml:space="preserve">Habilitar opção '--first-parent'</x:String> <x:String x:Key="Text.Repository.FirstParentFilterToggle" xml:space="preserve">Habilitar opção '--first-parent'</x:String>
<x:String x:Key="Text.Repository.LocalBranches" xml:space="preserve">BRANCHES LOCAIS</x:String> <x:String x:Key="Text.Repository.LocalBranches" xml:space="preserve">BRANCHES LOCAIS</x:String>
<x:String x:Key="Text.Repository.NavigateToCurrentHead" xml:space="preserve">Navegar para HEAD</x:String> <x:String x:Key="Text.Repository.NavigateToCurrentHead" xml:space="preserve">Navegar para HEAD</x:String>

View file

@ -293,7 +293,6 @@
<x:String x:Key="Text.FileHistory" xml:space="preserve">История файлов</x:String> <x:String x:Key="Text.FileHistory" xml:space="preserve">История файлов</x:String>
<x:String x:Key="Text.FileHistory.FileContent" xml:space="preserve">СОДЕРЖИМОЕ</x:String> <x:String x:Key="Text.FileHistory.FileContent" xml:space="preserve">СОДЕРЖИМОЕ</x:String>
<x:String x:Key="Text.FileHistory.FileChange" xml:space="preserve">ИЗМЕНИТЬ</x:String> <x:String x:Key="Text.FileHistory.FileChange" xml:space="preserve">ИЗМЕНИТЬ</x:String>
<x:String x:Key="Text.Filter" xml:space="preserve">ФИЛЬТР</x:String>
<x:String x:Key="Text.GitFlow" xml:space="preserve">Git-поток</x:String> <x:String x:Key="Text.GitFlow" xml:space="preserve">Git-поток</x:String>
<x:String x:Key="Text.GitFlow.DevelopBranch" xml:space="preserve">Ветка разработчика:</x:String> <x:String x:Key="Text.GitFlow.DevelopBranch" xml:space="preserve">Ветка разработчика:</x:String>
<x:String x:Key="Text.GitFlow.Feature" xml:space="preserve">Свойство:</x:String> <x:String x:Key="Text.GitFlow.Feature" xml:space="preserve">Свойство:</x:String>
@ -545,7 +544,7 @@
<x:String x:Key="Text.Repository.EnableReflog" xml:space="preserve">Разрешить опцию --reflog</x:String> <x:String x:Key="Text.Repository.EnableReflog" xml:space="preserve">Разрешить опцию --reflog</x:String>
<x:String x:Key="Text.Repository.Explore" xml:space="preserve">Открыть в файловом менеджере</x:String> <x:String x:Key="Text.Repository.Explore" xml:space="preserve">Открыть в файловом менеджере</x:String>
<x:String x:Key="Text.Repository.Filter" xml:space="preserve">Поиск веток, меток и подмодулей</x:String> <x:String x:Key="Text.Repository.Filter" xml:space="preserve">Поиск веток, меток и подмодулей</x:String>
<x:String x:Key="Text.Repository.FilterCommitPrefix" xml:space="preserve">ОТФИЛЬТРОВАНО:</x:String> <x:String x:Key="Text.Repository.FilterCommits.Prefix" xml:space="preserve">ОТФИЛЬТРОВАНО:</x:String>
<x:String x:Key="Text.Repository.LocalBranches" xml:space="preserve">ЛОКАЛЬНЫЕ ВЕТКИ</x:String> <x:String x:Key="Text.Repository.LocalBranches" xml:space="preserve">ЛОКАЛЬНЫЕ ВЕТКИ</x:String>
<x:String x:Key="Text.Repository.NavigateToCurrentHead" xml:space="preserve">Навигация по заголовку</x:String> <x:String x:Key="Text.Repository.NavigateToCurrentHead" xml:space="preserve">Навигация по заголовку</x:String>
<x:String x:Key="Text.Repository.FirstParentFilterToggle" xml:space="preserve">Включить опцию --first-parent</x:String> <x:String x:Key="Text.Repository.FirstParentFilterToggle" xml:space="preserve">Включить опцию --first-parent</x:String>

View file

@ -292,7 +292,6 @@
<x:String x:Key="Text.FileHistory" xml:space="preserve">文件历史</x:String> <x:String x:Key="Text.FileHistory" xml:space="preserve">文件历史</x:String>
<x:String x:Key="Text.FileHistory.FileContent" xml:space="preserve">文件内容</x:String> <x:String x:Key="Text.FileHistory.FileContent" xml:space="preserve">文件内容</x:String>
<x:String x:Key="Text.FileHistory.FileChange" xml:space="preserve">文件变更</x:String> <x:String x:Key="Text.FileHistory.FileChange" xml:space="preserve">文件变更</x:String>
<x:String x:Key="Text.Filter" xml:space="preserve">过滤</x:String>
<x:String x:Key="Text.GitFlow" xml:space="preserve">GIT工作流</x:String> <x:String x:Key="Text.GitFlow" xml:space="preserve">GIT工作流</x:String>
<x:String x:Key="Text.GitFlow.DevelopBranch" xml:space="preserve">开发分支 </x:String> <x:String x:Key="Text.GitFlow.DevelopBranch" xml:space="preserve">开发分支 </x:String>
<x:String x:Key="Text.GitFlow.Feature" xml:space="preserve">特性分支 </x:String> <x:String x:Key="Text.GitFlow.Feature" xml:space="preserve">特性分支 </x:String>
@ -545,7 +544,10 @@
<x:String x:Key="Text.Repository.EnableReflog" xml:space="preserve">启用 --reflog 选项</x:String> <x:String x:Key="Text.Repository.EnableReflog" xml:space="preserve">启用 --reflog 选项</x:String>
<x:String x:Key="Text.Repository.Explore" xml:space="preserve">在文件浏览器中打开</x:String> <x:String x:Key="Text.Repository.Explore" xml:space="preserve">在文件浏览器中打开</x:String>
<x:String x:Key="Text.Repository.Filter" xml:space="preserve">快速查找分支/标签/子模块</x:String> <x:String x:Key="Text.Repository.Filter" xml:space="preserve">快速查找分支/标签/子模块</x:String>
<x:String x:Key="Text.Repository.FilterCommitPrefix" xml:space="preserve">过滤规则 </x:String> <x:String x:Key="Text.Repository.FilterCommits.Default" xml:space="preserve">未指定(默认行为)</x:String>
<x:String x:Key="Text.Repository.FilterCommits.Exclude" xml:space="preserve">在提交列表中隐藏</x:String>
<x:String x:Key="Text.Repository.FilterCommits.Include" xml:space="preserve">使用其对提交列表过滤</x:String>
<x:String x:Key="Text.Repository.FilterCommits.Prefix" xml:space="preserve">过滤规则 </x:String>
<x:String x:Key="Text.Repository.LocalBranches" xml:space="preserve">本地分支</x:String> <x:String x:Key="Text.Repository.LocalBranches" xml:space="preserve">本地分支</x:String>
<x:String x:Key="Text.Repository.NavigateToCurrentHead" xml:space="preserve">定位HEAD</x:String> <x:String x:Key="Text.Repository.NavigateToCurrentHead" xml:space="preserve">定位HEAD</x:String>
<x:String x:Key="Text.Repository.FirstParentFilterToggle" xml:space="preserve">启用 --first-parent 过滤选项</x:String> <x:String x:Key="Text.Repository.FirstParentFilterToggle" xml:space="preserve">启用 --first-parent 过滤选项</x:String>

View file

@ -292,7 +292,6 @@
<x:String x:Key="Text.FileHistory" xml:space="preserve">檔案歷史</x:String> <x:String x:Key="Text.FileHistory" xml:space="preserve">檔案歷史</x:String>
<x:String x:Key="Text.FileHistory.FileContent" xml:space="preserve">檔案内容</x:String> <x:String x:Key="Text.FileHistory.FileContent" xml:space="preserve">檔案内容</x:String>
<x:String x:Key="Text.FileHistory.FileChange" xml:space="preserve">檔案變更</x:String> <x:String x:Key="Text.FileHistory.FileChange" xml:space="preserve">檔案變更</x:String>
<x:String x:Key="Text.Filter" xml:space="preserve">篩選</x:String>
<x:String x:Key="Text.GitFlow" xml:space="preserve">Git 工作流</x:String> <x:String x:Key="Text.GitFlow" xml:space="preserve">Git 工作流</x:String>
<x:String x:Key="Text.GitFlow.DevelopBranch" xml:space="preserve">開發分支:</x:String> <x:String x:Key="Text.GitFlow.DevelopBranch" xml:space="preserve">開發分支:</x:String>
<x:String x:Key="Text.GitFlow.Feature" xml:space="preserve">功能分支:</x:String> <x:String x:Key="Text.GitFlow.Feature" xml:space="preserve">功能分支:</x:String>
@ -544,7 +543,10 @@
<x:String x:Key="Text.Repository.EnableReflog" xml:space="preserve">啟用 [--reflog] 選項</x:String> <x:String x:Key="Text.Repository.EnableReflog" xml:space="preserve">啟用 [--reflog] 選項</x:String>
<x:String x:Key="Text.Repository.Explore" xml:space="preserve">在檔案瀏覽器中開啟</x:String> <x:String x:Key="Text.Repository.Explore" xml:space="preserve">在檔案瀏覽器中開啟</x:String>
<x:String x:Key="Text.Repository.Filter" xml:space="preserve">快速搜尋分支/標籤/子模組</x:String> <x:String x:Key="Text.Repository.Filter" xml:space="preserve">快速搜尋分支/標籤/子模組</x:String>
<x:String x:Key="Text.Repository.FilterCommitPrefix" xml:space="preserve">篩選規則:</x:String> <x:String x:Key="Text.Repository.FilterCommits.Default" xml:space="preserve">未指定 (預設)</x:String>
<x:String x:Key="Text.Repository.FilterCommits.Exclude" xml:space="preserve">在提交清單中隱藏</x:String>
<x:String x:Key="Text.Repository.FilterCommits.Include" xml:space="preserve">使用其來篩選提交清單</x:String>
<x:String x:Key="Text.Repository.FilterCommits.Prefix" xml:space="preserve">篩選規則:</x:String>
<x:String x:Key="Text.Repository.LocalBranches" xml:space="preserve">本機分支</x:String> <x:String x:Key="Text.Repository.LocalBranches" xml:space="preserve">本機分支</x:String>
<x:String x:Key="Text.Repository.NavigateToCurrentHead" xml:space="preserve">回到 HEAD</x:String> <x:String x:Key="Text.Repository.NavigateToCurrentHead" xml:space="preserve">回到 HEAD</x:String>
<x:String x:Key="Text.Repository.FirstParentFilterToggle" xml:space="preserve">啟用 [--first-parent] 選項</x:String> <x:String x:Key="Text.Repository.FirstParentFilterToggle" xml:space="preserve">啟用 [--first-parent] 選項</x:String>

View file

@ -1038,35 +1038,6 @@
<Setter Property="Background" Value="Transparent"/> <Setter Property="Background" Value="Transparent"/>
</Style> </Style>
<Style Selector="ToggleButton.filter">
<Setter Property="Margin" Value="0"/>
<Setter Property="Padding" Value="0"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="VerticalAlignment" Value="Stretch"/>
<Setter Property="Template">
<ControlTemplate>
<Border Background="Transparent">
<Path x:Name="PART_IndicatorIcon"
Width="12"
Data="{StaticResource Icons.Filter}"
Fill="Transparent"
StrokeThickness="1"
Stroke="{DynamicResource Brush.FG2}"
HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter>
</Style>
<Style Selector="ToggleButton.filter:pressed">
<Setter Property="RenderTransform" Value="scale(1)"/>
</Style>
<Style Selector="ToggleButton.filter:checked /template/ Path#PART_IndicatorIcon">
<Setter Property="Fill" Value="{DynamicResource Brush.FG2}"/>
</Style>
<Style Selector="ToggleButton.filter:pointerover /template/ Path#PART_IndicatorIcon">
<Setter Property="Stroke" Value="{DynamicResource Brush.Accent}"/>
</Style>
<Style Selector="ToggleButton.tree_expander"> <Style Selector="ToggleButton.tree_expander">
<Setter Property="Margin" Value="0" /> <Setter Property="Margin" Value="0" />
<Setter Property="Width" Value="9" /> <Setter Property="Width" Value="9" />

View file

@ -1,9 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using Avalonia; using Avalonia;
using Avalonia.Collections;
using Avalonia.Media; using Avalonia.Media;
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
@ -13,15 +11,16 @@ namespace SourceGit.ViewModels
public class BranchTreeNode : ObservableObject public class BranchTreeNode : ObservableObject
{ {
public string Name { get; private set; } = string.Empty; public string Name { get; private set; } = string.Empty;
public string Path { get; private set; } = string.Empty;
public object Backend { get; private set; } = null; public object Backend { get; private set; } = null;
public int Depth { get; set; } = 0; public int Depth { get; set; } = 0;
public bool IsSelected { get; set; } = false; public bool IsSelected { get; set; } = false;
public List<BranchTreeNode> Children { get; private set; } = new List<BranchTreeNode>(); public List<BranchTreeNode> Children { get; private set; } = new List<BranchTreeNode>();
public bool IsFiltered public Models.FilterMode FilterMode
{ {
get => _isFiltered; get => _filterMode;
set => SetProperty(ref _isFiltered, value); set => SetProperty(ref _filterMode, value);
} }
public bool IsExpanded public bool IsExpanded
@ -51,7 +50,7 @@ namespace SourceGit.ViewModels
get => Backend is Models.Branch b ? b.FriendlyName : null; get => Backend is Models.Branch b ? b.FriendlyName : null;
} }
private bool _isFiltered = false; private Models.FilterMode _filterMode = Models.FilterMode.None;
private bool _isExpanded = false; private bool _isExpanded = false;
private CornerRadius _cornerRadius = new CornerRadius(4); private CornerRadius _cornerRadius = new CornerRadius(4);
@ -60,18 +59,25 @@ namespace SourceGit.ViewModels
public List<BranchTreeNode> Locals => _locals; public List<BranchTreeNode> Locals => _locals;
public List<BranchTreeNode> Remotes => _remotes; public List<BranchTreeNode> Remotes => _remotes;
public Builder(Models.RepositorySettings settings)
{
_settings = settings;
}
public void Run(List<Models.Branch> branches, List<Models.Remote> remotes, bool bForceExpanded) public void Run(List<Models.Branch> branches, List<Models.Remote> remotes, bool bForceExpanded)
{ {
var folders = new Dictionary<string, BranchTreeNode>(); var folders = new Dictionary<string, BranchTreeNode>();
foreach (var remote in remotes) foreach (var remote in remotes)
{ {
var path = $"remote/{remote.Name}"; var path = $"refs/remotes/{remote.Name}";
var node = new BranchTreeNode() var node = new BranchTreeNode()
{ {
Name = remote.Name, Name = remote.Name,
Path = path,
Backend = remote, Backend = remote,
IsExpanded = bForceExpanded || _expanded.Contains(path), IsExpanded = bForceExpanded || _expanded.Contains(path),
FilterMode = _settings.GetHistoriesFilterMode(path, Models.FilterType.RemoteBranchFolder)
}; };
folders.Add(path, node); folders.Add(path, node);
@ -80,16 +86,15 @@ namespace SourceGit.ViewModels
foreach (var branch in branches) foreach (var branch in branches)
{ {
var isFiltered = _filters.Contains(branch.FullName);
if (branch.IsLocal) if (branch.IsLocal)
{ {
MakeBranchNode(branch, _locals, folders, "local", isFiltered, bForceExpanded); MakeBranchNode(branch, _locals, folders, "refs/heads", bForceExpanded);
} }
else else
{ {
var remote = _remotes.Find(x => x.Name == branch.Remote); var remote = _remotes.Find(x => x.Name == branch.Remote);
if (remote != null) if (remote != null)
MakeBranchNode(branch, remote.Children, folders, $"remote/{remote.Name}", isFiltered, bForceExpanded); MakeBranchNode(branch, remote.Children, folders, $"refs/remotes/{remote.Name}", bForceExpanded);
} }
} }
@ -98,42 +103,36 @@ namespace SourceGit.ViewModels
SortNodes(_remotes); SortNodes(_remotes);
} }
public void SetFilters(AvaloniaList<string> filters) public void CollectExpandedNodes(List<BranchTreeNode> nodes)
{
_filters.AddRange(filters);
}
public void CollectExpandedNodes(List<BranchTreeNode> nodes, bool isLocal)
{
CollectExpandedNodes(nodes, isLocal ? "local" : "remote");
}
private void CollectExpandedNodes(List<BranchTreeNode> nodes, string prefix)
{ {
foreach (var node in nodes) foreach (var node in nodes)
{ {
if (node.Backend is Models.Branch) if (node.Backend is Models.Branch)
continue; continue;
var path = prefix + "/" + node.Name;
if (node.IsExpanded) if (node.IsExpanded)
_expanded.Add(path); _expanded.Add(node.Path);
CollectExpandedNodes(node.Children, path); CollectExpandedNodes(node.Children);
} }
} }
private void MakeBranchNode(Models.Branch branch, List<BranchTreeNode> roots, Dictionary<string, BranchTreeNode> folders, string prefix, bool isFiltered, bool bForceExpanded) private void MakeBranchNode(Models.Branch branch, List<BranchTreeNode> roots, Dictionary<string, BranchTreeNode> folders, string prefix, bool bForceExpanded)
{ {
var fullpath = $"{prefix}/{branch.Name}";
var branchFilterType = branch.IsLocal ? Models.FilterType.LocalBranch : Models.FilterType.RemoteBranch;
var folderFilterType = branch.IsLocal ? Models.FilterType.LocalBranchFolder : Models.FilterType.RemoteBranchFolder;
var sepIdx = branch.Name.IndexOf('/', StringComparison.Ordinal); var sepIdx = branch.Name.IndexOf('/', StringComparison.Ordinal);
if (sepIdx == -1 || branch.IsDetachedHead) if (sepIdx == -1 || branch.IsDetachedHead)
{ {
roots.Add(new BranchTreeNode() roots.Add(new BranchTreeNode()
{ {
Name = branch.Name, Name = branch.Name,
Path = fullpath,
Backend = branch, Backend = branch,
IsExpanded = false, IsExpanded = false,
IsFiltered = isFiltered, FilterMode = _settings.GetHistoriesFilterMode(fullpath, branchFilterType),
}); });
return; return;
} }
@ -156,7 +155,9 @@ namespace SourceGit.ViewModels
lastFolder = new BranchTreeNode() lastFolder = new BranchTreeNode()
{ {
Name = name, Name = name,
Path = folder,
IsExpanded = bForceExpanded || branch.IsCurrent || _expanded.Contains(folder), IsExpanded = bForceExpanded || branch.IsCurrent || _expanded.Contains(folder),
FilterMode = _settings.GetHistoriesFilterMode(folder, folderFilterType),
}; };
roots.Add(lastFolder); roots.Add(lastFolder);
folders.Add(folder, lastFolder); folders.Add(folder, lastFolder);
@ -166,7 +167,9 @@ namespace SourceGit.ViewModels
var cur = new BranchTreeNode() var cur = new BranchTreeNode()
{ {
Name = name, Name = name,
Path = folder,
IsExpanded = bForceExpanded || branch.IsCurrent || _expanded.Contains(folder), IsExpanded = bForceExpanded || branch.IsCurrent || _expanded.Contains(folder),
FilterMode = _settings.GetHistoriesFilterMode(folder, folderFilterType),
}; };
lastFolder.Children.Add(cur); lastFolder.Children.Add(cur);
folders.Add(folder, cur); folders.Add(folder, cur);
@ -179,10 +182,11 @@ namespace SourceGit.ViewModels
lastFolder?.Children.Add(new BranchTreeNode() lastFolder?.Children.Add(new BranchTreeNode()
{ {
Name = Path.GetFileName(branch.Name), Name = System.IO.Path.GetFileName(branch.Name),
Path = fullpath,
Backend = branch, Backend = branch,
IsExpanded = false, IsExpanded = false,
IsFiltered = isFiltered, FilterMode = _settings.GetHistoriesFilterMode(fullpath, branchFilterType),
}); });
} }
@ -203,10 +207,10 @@ namespace SourceGit.ViewModels
SortNodes(node.Children); SortNodes(node.Children);
} }
private readonly Models.RepositorySettings _settings = null;
private readonly List<BranchTreeNode> _locals = new List<BranchTreeNode>(); private readonly List<BranchTreeNode> _locals = new List<BranchTreeNode>();
private readonly List<BranchTreeNode> _remotes = new List<BranchTreeNode>(); private readonly List<BranchTreeNode> _remotes = new List<BranchTreeNode>();
private readonly HashSet<string> _expanded = new HashSet<string>(); private readonly HashSet<string> _expanded = new HashSet<string>();
private readonly List<string> _filters = new List<string>();
} }
} }
} }

View file

@ -65,7 +65,7 @@ namespace SourceGit.ViewModels
{ {
var b = _repo.Branches.Find(x => x.IsLocal && x.Name == Branch); var b = _repo.Branches.Find(x => x.IsLocal && x.Name == Branch);
if (b != null) if (b != null)
_repo.AutoAddBranchFilterPostCheckout(b); _repo.UpdateHistoriesFilterAfterCheckout(b);
_repo.MarkBranchesDirtyManually(); _repo.MarkBranchesDirtyManually();
_repo.SetWatcherEnabled(true); _repo.SetWatcherEnabled(true);

View file

@ -127,7 +127,7 @@ namespace SourceGit.ViewModels
{ {
if (CheckoutAfterCreated) if (CheckoutAfterCreated)
{ {
_repo.AutoAddBranchFilterPostCheckout(new Models.Branch() _repo.UpdateHistoriesFilterAfterCheckout(new Models.Branch()
{ {
FullName = $"refs/heads/{_name}", FullName = $"refs/heads/{_name}",
Upstream = BasedOn is Models.Branch { IsLocal: false } remoteBranch ? remoteBranch.FullName : string.Empty, Upstream = BasedOn is Models.Branch { IsLocal: false } remoteBranch ? remoteBranch.FullName : string.Empty,

View file

@ -1,4 +1,5 @@
using System.ComponentModel.DataAnnotations; using System;
using System.ComponentModel.DataAnnotations;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace SourceGit.ViewModels namespace SourceGit.ViewModels
@ -57,10 +58,17 @@ namespace SourceGit.ViewModels
var succ = Commands.Branch.Rename(_repo.FullPath, Target.Name, _name); var succ = Commands.Branch.Rename(_repo.FullPath, Target.Name, _name);
CallUIThread(() => CallUIThread(() =>
{ {
if (succ && _repo.Settings.Filters.Contains(oldName)) if (succ)
{ {
_repo.Settings.Filters.Remove(oldName); foreach (var filter in _repo.Settings.HistoriesFilters)
_repo.Settings.Filters.Add($"refs/heads/{_name}"); {
if (filter.Type == Models.FilterType.LocalBranch &&
filter.Pattern.Equals(oldName, StringComparison.Ordinal))
{
filter.Pattern = $"refs/heads/{_name}";
break;
}
}
} }
_repo.MarkBranchesDirtyManually(); _repo.MarkBranchesDirtyManually();

View file

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Text;
using System.Text.Json; using System.Text.Json;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -455,13 +456,9 @@ namespace SourceGit.ViewModels
_hasAllowedSignersFile = !string.IsNullOrEmpty(allowedSignersFile); _hasAllowedSignersFile = !string.IsNullOrEmpty(allowedSignersFile);
}); });
Task.Run(() => Task.Run(RefreshBranches);
{ Task.Run(RefreshTags);
RefreshBranches(); Task.Run(RefreshCommits);
RefreshTags();
RefreshCommits();
});
Task.Run(RefreshSubmodules); Task.Run(RefreshSubmodules);
Task.Run(RefreshWorktrees); Task.Run(RefreshWorktrees);
Task.Run(RefreshWorkingCopyChanges); Task.Run(RefreshWorkingCopyChanges);
@ -587,18 +584,6 @@ namespace SourceGit.ViewModels
Filter = string.Empty; Filter = string.Empty;
} }
public void ClearHistoriesFilter()
{
_settings.Filters.Clear();
Task.Run(() =>
{
RefreshBranches();
RefreshTags();
RefreshCommits();
});
}
public void ClearSearchCommitFilter() public void ClearSearchCommitFilter()
{ {
SearchCommitFilter = string.Empty; SearchCommitFilter = string.Empty;
@ -653,12 +638,8 @@ namespace SourceGit.ViewModels
{ {
if (_watcher == null) if (_watcher == null)
{ {
Task.Run(() => Task.Run(RefreshBranches);
{ Task.Run(RefreshCommits);
RefreshBranches();
RefreshCommits();
});
Task.Run(RefreshWorkingCopyChanges); Task.Run(RefreshWorkingCopyChanges);
Task.Run(RefreshWorktrees); Task.Run(RefreshWorktrees);
} }
@ -696,53 +677,48 @@ namespace SourceGit.ViewModels
NavigateToCommit(_currentBranch.Head); NavigateToCommit(_currentBranch.Head);
} }
public void AutoAddBranchFilterPostCheckout(Models.Branch local) public void ClearHistoriesFilter()
{ {
if (_settings.Filters.Count == 0 || _settings.Filters.Contains(local.FullName)) _settings.HistoriesFilters.Clear();
return;
var hasLeft = false; var builder = BuildBranchTree(_branches, _remotes);
foreach (var b in _branches) LocalBranchTrees = builder.Locals;
{ RemoteBranchTrees = builder.Remotes;
if (!b.FullName.Equals(local.FullName, StringComparison.Ordinal) && foreach (var tag in VisibleTags)
!b.FullName.Equals(local.Upstream, StringComparison.Ordinal) && tag.FilterMode = Models.FilterMode.None;
!_settings.Filters.Contains(b.FullName))
{
hasLeft = true;
break;
}
}
if (!hasLeft) Task.Run(RefreshCommits);
_settings.Filters.Clear();
else if (string.IsNullOrEmpty(local.Upstream) || _settings.Filters.Contains(local.Upstream))
_settings.Filters.Add(local.FullName);
else
_settings.Filters.AddRange([local.FullName, local.Upstream]);
} }
public void UpdateFilters(List<string> filters, bool toggle) public void UpdateHistoriesFilterAfterCheckout(Models.Branch local)
{ {
var changed = false; var hasIncludedBranch = false;
if (toggle) foreach (var filter in _settings.HistoriesFilters)
{ {
foreach (var filter in filters) if (filter.Type == Models.FilterType.LocalBranch)
{ {
if (!_settings.Filters.Contains(filter)) if (filter.Pattern.Equals(local.FullName, StringComparison.Ordinal))
{ return;
_settings.Filters.Add(filter);
changed = true; hasIncludedBranch |= filter.Mode == Models.FilterMode.Included;
} }
else if (filter.Type == Models.FilterType.LocalBranchFolder)
{
if (local.FullName.StartsWith(filter.Pattern, StringComparison.Ordinal))
return;
hasIncludedBranch |= filter.Mode == Models.FilterMode.Included;
}
else if (filter.Type == Models.FilterType.RemoteBranch || filter.Type == Models.FilterType.RemoteBranchFolder)
{
hasIncludedBranch |= filter.Mode == Models.FilterMode.Included;
} }
} }
else
{
foreach (var filter in filters)
changed |= _settings.Filters.Remove(filter);
}
if (changed) if (!hasIncludedBranch)
Task.Run(RefreshCommits); return;
_settings.UpdateHistoriesFilter(local.FullName, Models.FilterType.LocalBranch, Models.FilterMode.Included);
} }
public void StashAll(bool autoStart) public void StashAll(bool autoStart)
@ -834,7 +810,7 @@ namespace SourceGit.ViewModels
{ {
var tags = new Commands.QueryTags(_fullpath).Result(); var tags = new Commands.QueryTags(_fullpath).Result();
foreach (var tag in tags) foreach (var tag in tags)
tag.IsFiltered = _settings.Filters.Contains(tag.Name); tag.FilterMode = _settings.GetHistoriesFilterMode(tag.Name, Models.FilterType.Tag);
Dispatcher.UIThread.Invoke(() => Dispatcher.UIThread.Invoke(() =>
{ {
@ -847,49 +823,21 @@ namespace SourceGit.ViewModels
{ {
Dispatcher.UIThread.Invoke(() => _histories.IsLoading = true); Dispatcher.UIThread.Invoke(() => _histories.IsLoading = true);
var limits = $"-{Preference.Instance.MaxHistoryCommits} "; var builder = new StringBuilder();
builder.Append($"-{Preference.Instance.MaxHistoryCommits} ");
if (_enableReflog) if (_enableReflog)
limits += "--reflog "; builder.Append("--reflog ");
if (_enableFirstParentInHistories) if (_enableFirstParentInHistories)
limits += "--first-parent "; builder.Append("--first-parent ");
var validFilters = new List<string>(); var invalidFilters = new List<Models.Filter>();
foreach (var filter in _settings.Filters) var filters = _settings.BuildHistoriesFilter();
{ if (string.IsNullOrEmpty(filters))
if (filter.StartsWith("refs/", StringComparison.Ordinal)) builder.Append("--branches --remotes --tags");
{
if (_branches.FindIndex(x => x.FullName == filter) >= 0)
validFilters.Add(filter);
}
else
{
if (_tags.FindIndex(t => t.Name == filter) >= 0)
validFilters.Add(filter);
}
}
if (validFilters.Count > 0)
{
limits += string.Join(" ", validFilters);
if (_settings.Filters.Count != validFilters.Count)
{
Dispatcher.UIThread.Invoke(() =>
{
_settings.Filters.Clear();
_settings.Filters.AddRange(validFilters);
});
}
}
else else
{ builder.Append(filters);
if (_settings.Filters.Count != 0)
Dispatcher.UIThread.Invoke(() => _settings.Filters.Clear());
limits += "--exclude=refs/stash --all"; var commits = new Commands.QueryCommits(_fullpath, builder.ToString()).Result();
}
var commits = new Commands.QueryCommits(_fullpath, limits).Result();
var graph = Models.CommitGraph.Parse(commits, _enableFirstParentInHistories); var graph = Models.CommitGraph.Parse(commits, _enableFirstParentInHistories);
Dispatcher.UIThread.Invoke(() => Dispatcher.UIThread.Invoke(() =>
@ -2061,13 +2009,11 @@ namespace SourceGit.ViewModels
private BranchTreeNode.Builder BuildBranchTree(List<Models.Branch> branches, List<Models.Remote> remotes) private BranchTreeNode.Builder BuildBranchTree(List<Models.Branch> branches, List<Models.Remote> remotes)
{ {
var builder = new BranchTreeNode.Builder(); var builder = new BranchTreeNode.Builder(_settings);
builder.SetFilters(_settings.Filters);
if (string.IsNullOrEmpty(_filter)) if (string.IsNullOrEmpty(_filter))
{ {
builder.CollectExpandedNodes(_localBranchTrees, true); builder.CollectExpandedNodes(_localBranchTrees);
builder.CollectExpandedNodes(_remoteBranchTrees, false); builder.CollectExpandedNodes(_remoteBranchTrees);
builder.Run(branches, remotes, false); builder.Run(branches, remotes, false);
} }
else else

View file

@ -22,16 +22,6 @@ namespace SourceGit.ViewModels
get => Tag == null; get => Tag == null;
} }
public bool IsFiltered
{
get => Tag?.IsFiltered ?? false;
set
{
if (Tag != null)
Tag.IsFiltered = value;
}
}
public bool IsExpanded public bool IsExpanded
{ {
get => _isExpanded; get => _isExpanded;

View file

@ -24,6 +24,10 @@
<Style Selector="ListBoxItem" x:DataType="vm:BranchTreeNode"> <Style Selector="ListBoxItem" x:DataType="vm:BranchTreeNode">
<Setter Property="CornerRadius" Value="{Binding CornerRadius}"/> <Setter Property="CornerRadius" Value="{Binding CornerRadius}"/>
</Style> </Style>
<Style Selector="ListBoxItem:pointerover v|FilterModeSwitchButton">
<Setter Property="IsNoneVisible" Value="True"/>
</Style>
</ListBox.Styles> </ListBox.Styles>
<ListBox.ItemTemplate> <ListBox.ItemTemplate>
@ -67,15 +71,10 @@
Foreground="{DynamicResource Brush.BadgeFG}" Foreground="{DynamicResource Brush.BadgeFG}"
Background="{DynamicResource Brush.Badge}"/> Background="{DynamicResource Brush.Badge}"/>
<!-- Filter Toggle Button --> <!-- Filter Mode Switcher -->
<ToggleButton Grid.Column="3" <v:FilterModeSwitchButton Grid.Column="3"
Classes="filter" Margin="0,0,8,0"
Margin="0,0,8,0" Mode="{Binding FilterMode}"/>
Background="Transparent"
IsVisible="{Binding IsBranch}"
IsChecked="{Binding IsFiltered}"
Click="OnToggleFilterClicked"
ToolTip.Tip="{DynamicResource Text.Filter}"/>
</Grid> </Grid>
</Grid> </Grid>
</DataTemplate> </DataTemplate>

View file

@ -428,28 +428,6 @@ namespace SourceGit.Views
} }
} }
private void OnToggleFilterClicked(object sender, RoutedEventArgs e)
{
if (DataContext is ViewModels.Repository repo &&
sender is ToggleButton toggle &&
toggle.DataContext is ViewModels.BranchTreeNode { Backend: Models.Branch branch } node)
{
bool filtered = toggle.IsChecked == true;
List<string> filters = [branch.FullName];
if (branch.IsLocal && !string.IsNullOrEmpty(branch.Upstream))
{
filters.Add(branch.Upstream);
node.IsFiltered = filtered;
UpdateUpstreamFilterState(repo.RemoteBranchTrees, branch.Upstream, filtered);
}
repo.UpdateFilters(filters, filtered);
}
e.Handled = true;
}
private void MakeRows(List<ViewModels.BranchTreeNode> rows, List<ViewModels.BranchTreeNode> nodes, int depth) private void MakeRows(List<ViewModels.BranchTreeNode> rows, List<ViewModels.BranchTreeNode> nodes, int depth)
{ {
foreach (var node in nodes) foreach (var node in nodes)
@ -477,23 +455,6 @@ namespace SourceGit.Views
CollectBranchesInNode(outs, sub); CollectBranchesInNode(outs, sub);
} }
private bool UpdateUpstreamFilterState(List<ViewModels.BranchTreeNode> collection, string upstream, bool isFiltered)
{
foreach (var node in collection)
{
if (node.Backend is Models.Branch b && b.FullName == upstream)
{
node.IsFiltered = isFiltered;
return true;
}
if (node.Backend is Models.Remote r && upstream.StartsWith($"refs/remotes/{r.Name}/", StringComparison.Ordinal))
return UpdateUpstreamFilterState(node.Children, upstream, isFiltered);
}
return false;
}
private bool _disableSelectionChangingEvent = false; private bool _disableSelectionChangingEvent = false;
} }
} }

View file

@ -0,0 +1,32 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:m="using:SourceGit.Models"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="SourceGit.Views.FilterModeSwitchButton"
x:Name="ThisControl">
<Button Classes="icon_button"
Width="12" Height="12"
Padding="0"
Background="Transparent"
VerticalContentAlignment="Center"
Click="OnChangeFilterModeButtonClicked">
<Grid>
<Path Width="12" Height="12"
Data="{StaticResource Icons.Eye}"
Fill="{DynamicResource Brush.FG2}"
IsVisible="{Binding #ThisControl.Mode, Converter={x:Static ObjectConverters.Equal}, ConverterParameter={x:Static m:FilterMode.None}}"/>
<Path Width="12" Height="12"
Data="{StaticResource Icons.Filter}"
Fill="{DynamicResource Brush.Accent}"
IsVisible="{Binding #ThisControl.Mode, Converter={x:Static ObjectConverters.Equal}, ConverterParameter={x:Static m:FilterMode.Included}}"/>
<Path Width="12" Height="12"
Data="{StaticResource Icons.EyeClose}"
Fill="{DynamicResource Brush.Accent}"
IsVisible="{Binding #ThisControl.Mode, Converter={x:Static ObjectConverters.Equal}, ConverterParameter={x:Static m:FilterMode.Excluded}}"/>
</Grid>
</Button>
</UserControl>

View file

@ -0,0 +1,299 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.VisualTree;
namespace SourceGit.Views
{
public partial class FilterModeSwitchButton : UserControl
{
public static readonly StyledProperty<Models.FilterMode> ModeProperty =
AvaloniaProperty.Register<FilterModeSwitchButton, Models.FilterMode>(nameof(Mode));
public Models.FilterMode Mode
{
get => GetValue(ModeProperty);
set => SetValue(ModeProperty, value);
}
public static readonly StyledProperty<bool> IsNoneVisibleProperty =
AvaloniaProperty.Register<FilterModeSwitchButton, bool>(nameof(IsNoneVisible));
public bool IsNoneVisible
{
get => GetValue(IsNoneVisibleProperty);
set => SetValue(IsNoneVisibleProperty, value);
}
public static readonly StyledProperty<bool> IsContextMenuOpeningProperty =
AvaloniaProperty.Register<FilterModeSwitchButton, bool>(nameof(IsContextMenuOpening));
public bool IsContextMenuOpening
{
get => GetValue(IsContextMenuOpeningProperty);
set => SetValue(IsContextMenuOpeningProperty, value);
}
public FilterModeSwitchButton()
{
IsVisible = false;
InitializeComponent();
}
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
base.OnPropertyChanged(change);
if (change.Property == ModeProperty ||
change.Property == IsNoneVisibleProperty ||
change.Property == IsContextMenuOpeningProperty)
{
var visible = (Mode != Models.FilterMode.None || IsNoneVisible || IsContextMenuOpening);
SetCurrentValue(IsVisibleProperty, visible);
}
}
private void OnChangeFilterModeButtonClicked(object sender, RoutedEventArgs e)
{
var repoView = this.FindAncestorOfType<Repository>();
if (repoView == null)
return;
var repo = repoView.DataContext as ViewModels.Repository;
if (repo == null)
return;
var button = sender as Button;
if (button == null)
return;
if (DataContext is Models.Tag tag)
{
var mode = tag.FilterMode;
var none = new MenuItem();
none.Icon = App.CreateMenuIcon("Icons.Eye");
none.Header = App.Text("Repository.FilterCommits.Default");
none.IsEnabled = mode != Models.FilterMode.None;
none.Click += (_, ev) =>
{
UpdateTagFilterMode(repo, tag, Models.FilterMode.None);
ev.Handled = true;
};
var include = new MenuItem();
include.Icon = App.CreateMenuIcon("Icons.Filter");
include.Header = App.Text("Repository.FilterCommits.Include");
include.IsEnabled = mode != Models.FilterMode.Included;
include.Click += (_, ev) =>
{
UpdateTagFilterMode(repo, tag, Models.FilterMode.Included);
ev.Handled = true;
};
var exclude = new MenuItem();
exclude.Icon = App.CreateMenuIcon("Icons.EyeClose");
exclude.Header = App.Text("Repository.FilterCommits.Exclude");
exclude.IsEnabled = mode != Models.FilterMode.Excluded;
exclude.Click += (_, ev) =>
{
UpdateTagFilterMode(repo, tag, Models.FilterMode.Excluded);
ev.Handled = true;
};
var menu = new ContextMenu();
menu.Items.Add(none);
menu.Items.Add(include);
menu.Items.Add(exclude);
if (mode == Models.FilterMode.None)
{
IsContextMenuOpening = true;
menu.Closed += (_, _) => IsContextMenuOpening = false;
}
menu.Open(button);
}
else if (DataContext is ViewModels.BranchTreeNode node)
{
var mode = node.FilterMode;
var none = new MenuItem();
none.Icon = App.CreateMenuIcon("Icons.Eye");
none.Header = App.Text("Repository.FilterCommits.Default");
none.IsEnabled = mode != Models.FilterMode.None;
none.Click += (_, ev) =>
{
UpdateBranchFilterMode(repo, node, Models.FilterMode.None);
ev.Handled = true;
};
var include = new MenuItem();
include.Icon = App.CreateMenuIcon("Icons.Filter");
include.Header = App.Text("Repository.FilterCommits.Include");
include.IsEnabled = mode != Models.FilterMode.Included;
include.Click += (_, ev) =>
{
UpdateBranchFilterMode(repo, node, Models.FilterMode.Included);
ev.Handled = true;
};
var exclude = new MenuItem();
exclude.Icon = App.CreateMenuIcon("Icons.EyeClose");
exclude.Header = App.Text("Repository.FilterCommits.Exclude");
exclude.IsEnabled = mode != Models.FilterMode.Excluded;
exclude.Click += (_, ev) =>
{
UpdateBranchFilterMode(repo, node, Models.FilterMode.Excluded);
ev.Handled = true;
};
var menu = new ContextMenu();
menu.Items.Add(none);
menu.Items.Add(include);
menu.Items.Add(exclude);
if (mode == Models.FilterMode.None)
{
IsContextMenuOpening = true;
menu.Closed += (_, _) => IsContextMenuOpening = false;
}
menu.Open(button);
}
e.Handled = true;
}
private void UpdateTagFilterMode(ViewModels.Repository repo, Models.Tag tag, Models.FilterMode mode)
{
var changed = repo.Settings.UpdateHistoriesFilter(tag.Name, Models.FilterType.Tag, mode);
if (changed)
{
tag.FilterMode = mode;
Task.Run(repo.RefreshCommits);
}
}
private void UpdateBranchFilterMode(ViewModels.Repository repo, ViewModels.BranchTreeNode node, Models.FilterMode mode)
{
var isLocal = node.Path.StartsWith("refs/heads/", StringComparison.Ordinal);
var type = isLocal ? Models.FilterType.LocalBranch : Models.FilterType.RemoteBranch;
var tree = isLocal ? repo.LocalBranchTrees : repo.RemoteBranchTrees;
if (node.Backend is Models.Branch branch)
{
var changed = repo.Settings.UpdateHistoriesFilter(node.Path, type, mode);
if (!changed)
return;
node.FilterMode = mode;
// Try to update its upstream.
if (isLocal && !string.IsNullOrEmpty(branch.Upstream) && mode != Models.FilterMode.Excluded)
{
var upstream = branch.Upstream;
var upstreamNode = FindBranchNode(repo.RemoteBranchTrees, upstream);
if (upstreamNode != null)
{
var canUpdateUpstream = true;
foreach (var filter in repo.Settings.HistoriesFilters)
{
bool matched = false;
if (filter.Type == Models.FilterType.RemoteBranch)
matched = filter.Pattern.Equals(upstream, StringComparison.Ordinal);
else if (filter.Type == Models.FilterType.RemoteBranchFolder)
matched = upstream.StartsWith(filter.Pattern, StringComparison.Ordinal);
if (matched && filter.Mode == Models.FilterMode.Excluded)
{
canUpdateUpstream = false;
break;
}
}
if (canUpdateUpstream)
{
changed = repo.Settings.UpdateHistoriesFilter(upstream, Models.FilterType.RemoteBranch, mode);
if (changed)
upstreamNode.FilterMode = mode;
}
}
}
}
else
{
var changed = repo.Settings.UpdateHistoriesFilter(node.Path, isLocal ? Models.FilterType.LocalBranchFolder : Models.FilterType.RemoteBranchFolder, mode);
if (!changed)
return;
node.FilterMode = mode;
ResetChildrenBranchNodeFilterMode(repo, node, isLocal);
}
var parentType = isLocal ? Models.FilterType.LocalBranchFolder : Models.FilterType.RemoteBranchFolder;
var cur = node;
do
{
var lastSepIdx = cur.Path.LastIndexOf('/');
if (lastSepIdx <= 0)
break;
var parentPath = cur.Path.Substring(0, lastSepIdx);
var parent = FindBranchNode(tree, parentPath);
if (parent == null)
break;
repo.Settings.UpdateHistoriesFilter(parent.Path, parentType, Models.FilterMode.None);
parent.FilterMode = Models.FilterMode.None;
cur = parent;
} while (true);
Task.Run(repo.RefreshCommits);
}
private void ResetChildrenBranchNodeFilterMode(ViewModels.Repository repo, ViewModels.BranchTreeNode node, bool isLocal)
{
foreach (var child in node.Children)
{
child.FilterMode = Models.FilterMode.None;
if (child.IsBranch)
{
var type = isLocal ? Models.FilterType.LocalBranch : Models.FilterType.RemoteBranch;
repo.Settings.UpdateHistoriesFilter(child.Path, type, Models.FilterMode.None);
}
else
{
var type = isLocal ? Models.FilterType.LocalBranchFolder : Models.FilterType.RemoteBranchFolder;
repo.Settings.UpdateHistoriesFilter(child.Path, type, Models.FilterMode.None);
ResetChildrenBranchNodeFilterMode(repo, child, isLocal);
}
}
}
private ViewModels.BranchTreeNode FindBranchNode(List<ViewModels.BranchTreeNode> nodes, string path)
{
foreach (var node in nodes)
{
if (node.Path.Equals(path, StringComparison.Ordinal))
return node;
if (path.StartsWith(node.Path, StringComparison.Ordinal))
{
var founded = FindBranchNode(node.Children, path);
if (founded != null)
return founded;
}
}
return null;
}
}
}

View file

@ -577,14 +577,14 @@
<Border.IsVisible> <Border.IsVisible>
<MultiBinding Converter="{x:Static BoolConverters.And}"> <MultiBinding Converter="{x:Static BoolConverters.And}">
<Binding Path="SelectedViewIndex" Converter="{x:Static c:IntConverters.IsZero}"/> <Binding Path="SelectedViewIndex" Converter="{x:Static c:IntConverters.IsZero}"/>
<Binding Path="Settings.Filters.Count" Converter="{x:Static c:IntConverters.IsGreaterThanZero}"/> <Binding Path="Settings.HistoriesFilters.Count" Converter="{x:Static c:IntConverters.IsGreaterThanZero}"/>
</MultiBinding> </MultiBinding>
</Border.IsVisible> </Border.IsVisible>
<Grid Height="28" ColumnDefinitions="Auto,*,Auto"> <Grid Height="28" ColumnDefinitions="Auto,*,Auto">
<TextBlock Grid.Column="0" Margin="8,0,0,0" Classes="table_header" Text="{DynamicResource Text.Repository.FilterCommitPrefix}"/> <TextBlock Grid.Column="0" Margin="8,0,0,0" Classes="table_header" Text="{DynamicResource Text.Repository.FilterCommits.Prefix}"/>
<ItemsControl Grid.Column="1" Margin="8,0,0,0" ItemsSource="{Binding Settings.Filters}"> <ItemsControl Grid.Column="1" Margin="8,0,0,0" ItemsSource="{Binding Settings.HistoriesFilters}">
<ItemsControl.ItemsPanel> <ItemsControl.ItemsPanel>
<ItemsPanelTemplate> <ItemsPanelTemplate>
<VirtualizingStackPanel Orientation="Horizontal" VerticalAlignment="Center"/> <VirtualizingStackPanel Orientation="Horizontal" VerticalAlignment="Center"/>
@ -592,9 +592,17 @@
</ItemsControl.ItemsPanel> </ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate> <ItemsControl.ItemTemplate>
<DataTemplate> <DataTemplate DataType="m:Filter">
<Border Height="20" Margin="0,0,6,0" BorderThickness="1" BorderBrush="{DynamicResource Brush.Border2}" CornerRadius="12"> <Border Height="20"
<TextBlock Classes="primary" Text="{Binding Converter={x:Static c:StringConverters.TrimRefsPrefix}}" Margin="8,0"/> Margin="0,0,6,0"
CornerRadius="12"
BorderThickness="1"
BorderBrush="{Binding Mode, Converter={x:Static c:FilterModeConverters.ToBorderBrush}}">
<StackPanel Orientation="Horizontal" Margin="8,0">
<Path Width="10" Height="10" Data="{StaticResource Icons.Branch}" IsVisible="{Binding IsBranch}"/>
<Path Width="10" Height="10" Data="{StaticResource Icons.Tag}" IsVisible="{Binding !IsBranch}"/>
<TextBlock Classes="primary" Text="{Binding Pattern, Converter={x:Static c:StringConverters.TrimRefsPrefix}}" Margin="4,0,0,0"/>
</StackPanel>
</Border> </Border>
</DataTemplate> </DataTemplate>
</ItemsControl.ItemTemplate> </ItemsControl.ItemTemplate>

View file

@ -12,6 +12,10 @@
<Style Selector="ListBoxItem"> <Style Selector="ListBoxItem">
<Setter Property="CornerRadius" Value="4"/> <Setter Property="CornerRadius" Value="4"/>
</Style> </Style>
<Style Selector="ListBoxItem:pointerover v|FilterModeSwitchButton">
<Setter Property="IsNoneVisible" Value="True"/>
</Style>
</UserControl.Styles> </UserControl.Styles>
<UserControl.DataTemplates> <UserControl.DataTemplates>
@ -43,15 +47,14 @@
Classes="primary" Classes="primary"
Text="{Binding FullPath, Converter={x:Static c:PathConverters.PureFileName}}" Text="{Binding FullPath, Converter={x:Static c:PathConverters.PureFileName}}"
Margin="8,0,0,0"/> Margin="8,0,0,0"/>
<ToggleButton Grid.Column="3" <ContentControl Grid.Column="3" Content="{Binding Tag}">
Classes="filter" <ContentControl.DataTemplates>
Margin="0,0,8,0" <DataTemplate DataType="m:Tag">
Background="Transparent" <v:FilterModeSwitchButton Mode="{Binding FilterMode}"/>
Click="OnToggleFilterClicked" </DataTemplate>
IsChecked="{Binding IsFiltered, Mode=TwoWay}" </ContentControl.DataTemplates>
IsVisible="{Binding !IsFolder}" </ContentControl>
ToolTip.Tip="{DynamicResource Text.Filter}"/>
</Grid> </Grid>
</DataTemplate> </DataTemplate>
</ListBox.ItemTemplate> </ListBox.ItemTemplate>
@ -60,33 +63,28 @@
<DataTemplate DataType="vm:TagCollectionAsList"> <DataTemplate DataType="vm:TagCollectionAsList">
<ListBox Classes="repo_left_content_list" <ListBox Classes="repo_left_content_list"
Margin="12,0,0,0" Margin="8,0,0,0"
ItemsSource="{Binding Tags}" ItemsSource="{Binding Tags}"
SelectionMode="Single" SelectionMode="Single"
SelectionChanged="OnRowSelectionChanged"> SelectionChanged="OnRowSelectionChanged">
<ListBox.ItemTemplate> <ListBox.ItemTemplate>
<DataTemplate DataType="m:Tag"> <DataTemplate DataType="m:Tag">
<Grid ColumnDefinitions="Auto,*,Auto" <Grid ColumnDefinitions="Auto,*,20"
Background="Transparent" Background="Transparent"
ContextRequested="OnRowContextRequested" ContextRequested="OnRowContextRequested"
ToolTip.Tip="{Binding Message}"> ToolTip.Tip="{Binding Message}">
<Path Grid.Column="0" <Path Grid.Column="0"
Margin="4,0,0,0" Margin="8,0,0,0"
Width="12" Height="12" Width="12" Height="12"
Data="{StaticResource Icons.Tag}"/> Data="{StaticResource Icons.Tag}"/>
<TextBlock Grid.Column="1" <TextBlock Grid.Column="1"
Classes="primary" Classes="primary"
Text="{Binding Name}" Text="{Binding Name}"
Margin="8,0,0,0"/> Margin="8,0,0,0"
TextTrimming="CharacterEllipsis"/>
<ToggleButton Grid.Column="2" <v:FilterModeSwitchButton Grid.Column="2" Mode="{Binding FilterMode}"/>
Classes="filter"
Margin="0,0,8,0"
Background="Transparent"
Click="OnToggleFilterClicked"
IsChecked="{Binding IsFiltered, Mode=TwoWay}"
ToolTip.Tip="{DynamicResource Text.Filter}"/>
</Grid> </Grid>
</DataTemplate> </DataTemplate>
</ListBox.ItemTemplate> </ListBox.ItemTemplate>

View file

@ -247,23 +247,6 @@ namespace SourceGit.Views
} }
} }
private void OnToggleFilterClicked(object sender, RoutedEventArgs e)
{
if (sender is ToggleButton toggle && DataContext is ViewModels.Repository repo)
{
var target = null as Models.Tag;
if (toggle.DataContext is ViewModels.TagTreeNode node)
target = node.Tag;
else if (toggle.DataContext is Models.Tag tag)
target = tag;
if (target != null)
repo.UpdateFilters([target.Name], toggle.IsChecked == true);
}
e.Handled = true;
}
private void MakeTreeRows(List<ViewModels.TagTreeNode> rows, List<ViewModels.TagTreeNode> nodes) private void MakeTreeRows(List<ViewModels.TagTreeNode> rows, List<ViewModels.TagTreeNode> nodes)
{ {
foreach (var node in nodes) foreach (var node in nodes)