refactor: move codes from Views.FilterModeSwitchButton to ViewModels.Repository

Signed-off-by: leo <longshuang@msn.cn>
This commit is contained in:
leo 2024-11-15 10:19:39 +08:00
parent 66842b1d0d
commit a5606e80d4
No known key found for this signature in database
7 changed files with 185 additions and 231 deletions

View file

@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.Text; using System.Text;
using Avalonia.Collections; using Avalonia.Collections;
using Avalonia.Threading;
namespace SourceGit.Models namespace SourceGit.Models
{ {
@ -218,10 +217,26 @@ namespace SourceGit.Models
return true; return true;
} }
public void RemoveChildrenBranchFilters(string pattern)
{
var dirty = new List<Filter>();
var prefix = $"{pattern}/";
foreach (var filter in HistoriesFilters)
{
if (filter.Type == FilterType.Tag)
continue;
if (filter.Pattern.StartsWith(prefix, StringComparison.Ordinal))
dirty.Add(filter);
}
foreach (var filter in dirty)
HistoriesFilters.Remove(filter);
}
public string BuildHistoriesFilter() public string BuildHistoriesFilter()
{ {
var builder = new StringBuilder();
var excludedBranches = new List<string>(); var excludedBranches = new List<string>();
var excludedRemotes = new List<string>(); var excludedRemotes = new List<string>();
var excludedTags = new List<string>(); var excludedTags = new List<string>();
@ -276,14 +291,11 @@ namespace SourceGit.Models
} }
} }
foreach (var b in excludedBranches) bool hasIncluded = includedBranches.Count > 0 || includedRemotes.Count > 0 || includedTags.Count > 0;
{ bool hasExcluded = excludedBranches.Count > 0 || excludedRemotes.Count > 0 || excludedTags.Count > 0;
builder.Append("--exclude=");
builder.Append(b);
builder.Append(' ');
}
if (includedBranches.Count > 0) var builder = new StringBuilder();
if (hasIncluded)
{ {
foreach (var b in includedBranches) foreach (var b in includedBranches)
{ {
@ -291,44 +303,14 @@ namespace SourceGit.Models
builder.Append(b); builder.Append(b);
builder.Append(' '); 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) foreach (var r in includedRemotes)
{ {
builder.Append("--remotes="); builder.Append("--remotes=");
builder.Append(r); builder.Append(r);
builder.Append(' '); 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) foreach (var t in includedTags)
{ {
builder.Append("--tags="); builder.Append("--tags=");
@ -336,8 +318,42 @@ namespace SourceGit.Models
builder.Append(' '); builder.Append(' ');
} }
} }
else if (excludedTags.Count > 0 || (includedBranches.Count == 0 && includedRemotes.Count == 0)) else if (hasExcluded)
{ {
if (excludedBranches.Count > 0)
{
foreach (var b in excludedBranches)
{
builder.Append("--exclude=");
builder.Append(b);
builder.Append(' ');
}
}
builder.Append("--exclude=HEA[D] --branches ");
if (excludedRemotes.Count > 0)
{
foreach (var r in excludedRemotes)
{
builder.Append("--exclude=");
builder.Append(r);
builder.Append(' ');
}
}
builder.Append("--exclude=origin/HEA[D] --remotes ");
if (excludedTags.Count > 0)
{
foreach (var t in excludedTags)
{
builder.Append("--exclude=");
builder.Append(t);
builder.Append(' ');
}
}
builder.Append("--tags "); builder.Append("--tags ");
} }

View file

@ -64,8 +64,8 @@ namespace SourceGit.ViewModels
CallUIThread(() => CallUIThread(() =>
{ {
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.HistoriesFilterMode == Models.FilterMode.Included)
_repo.UpdateHistoriesFilterAfterCheckout(b); _repo.Settings.UpdateHistoriesFilter(b.FullName, Models.FilterType.LocalBranch, Models.FilterMode.Included);
_repo.MarkBranchesDirtyManually(); _repo.MarkBranchesDirtyManually();
_repo.SetWatcherEnabled(true); _repo.SetWatcherEnabled(true);

View file

@ -125,14 +125,8 @@ namespace SourceGit.ViewModels
CallUIThread(() => CallUIThread(() =>
{ {
if (CheckoutAfterCreated) if (CheckoutAfterCreated && _repo.HistoriesFilterMode == Models.FilterMode.Included)
{ _repo.Settings.UpdateHistoriesFilter($"refs/heads/{_name}", Models.FilterType.LocalBranch, Models.FilterMode.Included);
_repo.UpdateHistoriesFilterAfterCheckout(new Models.Branch()
{
FullName = $"refs/heads/{_name}",
Upstream = BasedOn is Models.Branch { IsLocal: false } remoteBranch ? remoteBranch.FullName : string.Empty,
});
}
_repo.MarkBranchesDirtyManually(); _repo.MarkBranchesDirtyManually();
_repo.SetWatcherEnabled(true); _repo.SetWatcherEnabled(true);

View file

@ -46,6 +46,12 @@ namespace SourceGit.ViewModels
get => _settings; get => _settings;
} }
public Models.FilterMode HistoriesFilterMode
{
get => _historiesFilterMode;
private set => SetProperty(ref _historiesFilterMode, value);
}
public bool HasAllowedSignersFile public bool HasAllowedSignersFile
{ {
get => _hasAllowedSignersFile; get => _hasAllowedSignersFile;
@ -383,6 +389,11 @@ namespace SourceGit.ViewModels
App.RaiseException(string.Empty, $"Failed to start watcher for repository: '{_fullpath}'. You may need to press 'F5' to refresh repository manually!\n\nReason: {ex.Message}"); App.RaiseException(string.Empty, $"Failed to start watcher for repository: '{_fullpath}'. You may need to press 'F5' to refresh repository manually!\n\nReason: {ex.Message}");
} }
if (_settings.HistoriesFilters.Count > 0)
_historiesFilterMode = _settings.HistoriesFilters[0].Mode;
else
_historiesFilterMode = Models.FilterMode.None;
_histories = new Histories(this); _histories = new Histories(this);
_workingCopy = new WorkingCopy(this); _workingCopy = new WorkingCopy(this);
_stashesPage = new StashesPage(this); _stashesPage = new StashesPage(this);
@ -407,6 +418,7 @@ namespace SourceGit.ViewModels
// Ignore // Ignore
} }
_settings = null; _settings = null;
_historiesFilterMode = Models.FilterMode.None;
_autoFetchTimer.Dispose(); _autoFetchTimer.Dispose();
_autoFetchTimer = null; _autoFetchTimer = null;
@ -670,49 +682,92 @@ namespace SourceGit.ViewModels
public void ClearHistoriesFilter() public void ClearHistoriesFilter()
{ {
_settings.HistoriesFilters.Clear(); _settings.HistoriesFilters.Clear();
HistoriesFilterMode = Models.FilterMode.None;
ResetBranchTreeFilterMode(LocalBranchTrees); ResetBranchTreeFilterMode(LocalBranchTrees);
ResetBranchTreeFilterMode(RemoteBranchTrees); ResetBranchTreeFilterMode(RemoteBranchTrees);
ResetTagFilterMode(); ResetTagFilterMode();
Task.Run(RefreshCommits); Task.Run(RefreshCommits);
} }
public void MarkHistoriesFilterDirty() public void SetTagFilterMode(Models.Tag tag, Models.FilterMode mode)
{ {
UpdateBranchTreeFilterMode(LocalBranchTrees, true); var changed = _settings.UpdateHistoriesFilter(tag.Name, Models.FilterType.Tag, mode);
UpdateBranchTreeFilterMode(RemoteBranchTrees, false); if (changed)
UpdateTagFilterMode(); {
Task.Run(RefreshCommits); if (mode != Models.FilterMode.None || _settings.HistoriesFilters.Count == 0)
HistoriesFilterMode = mode;
RefreshHistoriesFilters();
}
} }
public void UpdateHistoriesFilterAfterCheckout(Models.Branch local) public void SetBranchFilterMode(BranchTreeNode node, Models.FilterMode mode)
{ {
var hasIncludedBranch = false; var isLocal = node.Path.StartsWith("refs/heads/", StringComparison.Ordinal);
foreach (var filter in _settings.HistoriesFilters) var tree = isLocal ? _localBranchTrees : _remoteBranchTrees;
if (node.Backend is Models.Branch branch)
{ {
if (filter.Type == Models.FilterType.LocalBranch) var type = isLocal ? Models.FilterType.LocalBranch : Models.FilterType.RemoteBranch;
{ var changed = _settings.UpdateHistoriesFilter(node.Path, type, mode);
if (filter.Pattern.Equals(local.FullName, StringComparison.Ordinal)) if (!changed)
return; return;
hasIncludedBranch |= filter.Mode == Models.FilterMode.Included; if (isLocal && !string.IsNullOrEmpty(branch.Upstream) && mode != Models.FilterMode.Excluded)
}
else if (filter.Type == Models.FilterType.LocalBranchFolder)
{ {
if (local.FullName.StartsWith(filter.Pattern, StringComparison.Ordinal)) var upstream = branch.Upstream;
return; var canUpdateUpstream = true;
foreach (var filter in _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);
hasIncludedBranch |= filter.Mode == Models.FilterMode.Included; if (matched && filter.Mode == Models.FilterMode.Excluded)
} {
else if (filter.Type == Models.FilterType.RemoteBranch || filter.Type == Models.FilterType.RemoteBranchFolder) canUpdateUpstream = false;
{ break;
hasIncludedBranch |= filter.Mode == Models.FilterMode.Included; }
}
if (canUpdateUpstream)
_settings.UpdateHistoriesFilter(upstream, Models.FilterType.RemoteBranch, mode);
} }
} }
else
{
var type = isLocal ? Models.FilterType.LocalBranchFolder : Models.FilterType.RemoteBranchFolder;
var changed = _settings.UpdateHistoriesFilter(node.Path, type, mode);
if (!changed)
return;
if (!hasIncludedBranch) _settings.RemoveChildrenBranchFilters(node.Path);
return; }
_settings.UpdateHistoriesFilter(local.FullName, Models.FilterType.LocalBranch, Models.FilterMode.Included); 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;
_settings.UpdateHistoriesFilter(parent.Path, parentType, Models.FilterMode.None);
cur = parent;
} while (true);
if (mode != Models.FilterMode.None || _settings.HistoriesFilters.Count == 0)
HistoriesFilterMode = mode;
RefreshHistoriesFilters();
} }
public void StashAll(bool autoStart) public void StashAll(bool autoStart)
@ -2023,6 +2078,14 @@ namespace SourceGit.ViewModels
return visible; return visible;
} }
private void RefreshHistoriesFilters()
{
UpdateBranchTreeFilterMode(LocalBranchTrees, true);
UpdateBranchTreeFilterMode(RemoteBranchTrees, false);
UpdateTagFilterMode();
Task.Run(RefreshCommits);
}
private void UpdateBranchTreeFilterMode(List<BranchTreeNode> nodes, bool isLocal) private void UpdateBranchTreeFilterMode(List<BranchTreeNode> nodes, bool isLocal)
{ {
foreach (var node in nodes) foreach (var node in nodes)
@ -2061,6 +2124,24 @@ namespace SourceGit.ViewModels
tag.FilterMode = Models.FilterMode.None; tag.FilterMode = Models.FilterMode.None;
} }
private BranchTreeNode FindBranchNode(List<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;
}
private void UpdateCurrentRevisionFilesForSearchSuggestion() private void UpdateCurrentRevisionFilesForSearchSuggestion()
{ {
_revisionFiles.Clear(); _revisionFiles.Clear();
@ -2124,6 +2205,7 @@ namespace SourceGit.ViewModels
private string _fullpath = string.Empty; private string _fullpath = string.Empty;
private string _gitDir = string.Empty; private string _gitDir = string.Empty;
private Models.RepositorySettings _settings = null; private Models.RepositorySettings _settings = null;
private Models.FilterMode _historiesFilterMode = Models.FilterMode.None;
private bool _hasAllowedSignersFile = false; private bool _hasAllowedSignersFile = false;
private Models.Watcher _watcher = null; private Models.Watcher _watcher = null;

View file

@ -1,6 +1,3 @@
using System;
using System.Collections.Generic;
using Avalonia; using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Interactivity; using Avalonia.Interactivity;
@ -82,7 +79,7 @@ namespace SourceGit.Views
unset.Header = App.Text("Repository.FilterCommits.Default"); unset.Header = App.Text("Repository.FilterCommits.Default");
unset.Click += (_, ev) => unset.Click += (_, ev) =>
{ {
UpdateTagFilterMode(repo, tag, Models.FilterMode.None); repo.SetTagFilterMode(tag, Models.FilterMode.None);
ev.Handled = true; ev.Handled = true;
}; };
@ -96,7 +93,7 @@ namespace SourceGit.Views
include.IsEnabled = mode != Models.FilterMode.Included; include.IsEnabled = mode != Models.FilterMode.Included;
include.Click += (_, ev) => include.Click += (_, ev) =>
{ {
UpdateTagFilterMode(repo, tag, Models.FilterMode.Included); repo.SetTagFilterMode(tag, Models.FilterMode.Included);
ev.Handled = true; ev.Handled = true;
}; };
@ -106,7 +103,7 @@ namespace SourceGit.Views
exclude.IsEnabled = mode != Models.FilterMode.Excluded; exclude.IsEnabled = mode != Models.FilterMode.Excluded;
exclude.Click += (_, ev) => exclude.Click += (_, ev) =>
{ {
UpdateTagFilterMode(repo, tag, Models.FilterMode.Excluded); repo.SetTagFilterMode(tag, Models.FilterMode.Excluded);
ev.Handled = true; ev.Handled = true;
}; };
@ -123,7 +120,7 @@ namespace SourceGit.Views
unset.Header = App.Text("Repository.FilterCommits.Default"); unset.Header = App.Text("Repository.FilterCommits.Default");
unset.Click += (_, ev) => unset.Click += (_, ev) =>
{ {
UpdateBranchFilterMode(repo, node, Models.FilterMode.None); repo.SetBranchFilterMode(node, Models.FilterMode.None);
ev.Handled = true; ev.Handled = true;
}; };
@ -137,7 +134,7 @@ namespace SourceGit.Views
include.IsEnabled = mode != Models.FilterMode.Included; include.IsEnabled = mode != Models.FilterMode.Included;
include.Click += (_, ev) => include.Click += (_, ev) =>
{ {
UpdateBranchFilterMode(repo, node, Models.FilterMode.Included); repo.SetBranchFilterMode(node, Models.FilterMode.Included);
ev.Handled = true; ev.Handled = true;
}; };
@ -147,7 +144,7 @@ namespace SourceGit.Views
exclude.IsEnabled = mode != Models.FilterMode.Excluded; exclude.IsEnabled = mode != Models.FilterMode.Excluded;
exclude.Click += (_, ev) => exclude.Click += (_, ev) =>
{ {
UpdateBranchFilterMode(repo, node, Models.FilterMode.Excluded); repo.SetBranchFilterMode(node, Models.FilterMode.Excluded);
ev.Handled = true; ev.Handled = true;
}; };
@ -164,113 +161,6 @@ namespace SourceGit.Views
menu.Open(button); menu.Open(button);
e.Handled = true; 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);
repo.MarkHistoriesFilterDirty();
}
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;
// Try to update its upstream.
if (isLocal && !string.IsNullOrEmpty(branch.Upstream) && mode != Models.FilterMode.Excluded)
{
var upstream = branch.Upstream;
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)
repo.Settings.UpdateHistoriesFilter(upstream, Models.FilterType.RemoteBranch, mode);
}
}
else
{
var changed = repo.Settings.UpdateHistoriesFilter(node.Path, isLocal ? Models.FilterType.LocalBranchFolder : Models.FilterType.RemoteBranchFolder, mode);
if (!changed)
return;
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);
cur = parent;
} while (true);
repo.MarkHistoriesFilterDirty();
}
private void ResetChildrenBranchNodeFilterMode(ViewModels.Repository repo, ViewModels.BranchTreeNode node, bool isLocal)
{
foreach (var child in node.Children)
{
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

@ -575,12 +575,19 @@
<Grid Height="28" ColumnDefinitions="Auto,*,Auto"> <Grid Height="28" ColumnDefinitions="Auto,*,Auto">
<Path Grid.Column="0" <Path Grid.Column="0"
x:Name="HistoriesFilterModeIcon"
Margin="8,0,0,0" Margin="8,0,0,0"
Width="14" Height="14" Width="12" Height="12"
Fill="{DynamicResource Brush.FG2}"/> Data="{StaticResource Icons.Filter}"
Fill="{DynamicResource Brush.FG2}"
IsVisible="{Binding HistoriesFilterMode, Converter={x:Static ObjectConverters.Equal}, ConverterParameter={x:Static m:FilterMode.Included}}"/>
<Path Grid.Column="0"
Margin="8,0,0,0"
Width="12" Height="12"
Data="{StaticResource Icons.EyeClose}"
Fill="{DynamicResource Brush.FG2}"
IsVisible="{Binding HistoriesFilterMode, Converter={x:Static ObjectConverters.Equal}, ConverterParameter={x:Static m:FilterMode.Excluded}}"/>
<ItemsControl Grid.Column="1" Margin="8,0,0,0" ItemsSource="{Binding Settings.HistoriesFilters}" LayoutUpdated="OnHistoriesFiltersLayoutUpdated"> <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"/>

View file

@ -395,40 +395,5 @@ namespace SourceGit.Views
} }
e.Handled = true; e.Handled = true;
} }
private void OnHistoriesFiltersLayoutUpdated(object sender, EventArgs e)
{
var repo = DataContext as ViewModels.Repository;
if (repo == null)
return;
var filters = repo.Settings.HistoriesFilters;
if (filters.Count == 0)
return;
var mode = filters[0].Mode;
if (mode == _lastFilterMode)
return;
_lastFilterMode = mode;
var icon = null as StreamGeometry;
switch (mode)
{
case Models.FilterMode.Included:
icon = this.FindResource("Icons.Filter") as StreamGeometry;
break;
case Models.FilterMode.Excluded:
icon = this.FindResource("Icons.EyeClose") as StreamGeometry;
break;
default:
break;
}
if (icon != null)
HistoriesFilterModeIcon.Data = icon;
}
private Models.FilterMode _lastFilterMode = Models.FilterMode.None;
} }
} }