ux: improve change list selection

This commit is contained in:
leo 2024-07-16 17:00:08 +08:00
parent 2f5f98770f
commit ef0c904e91
No known key found for this signature in database
5 changed files with 132 additions and 88 deletions

View file

@ -164,7 +164,7 @@ namespace SourceGit.Models
private void OnRepositoryChanged(object o, FileSystemEventArgs e) private void OnRepositoryChanged(object o, FileSystemEventArgs e)
{ {
if (string.IsNullOrEmpty(e.Name)) if (string.IsNullOrEmpty(e.Name) || e.Name.EndsWith(".lock", StringComparison.Ordinal))
return; return;
var name = e.Name.Replace("\\", "/"); var name = e.Name.Replace("\\", "/");

View file

@ -8,15 +8,18 @@ namespace SourceGit.ViewModels
{ {
public List<ChangeTreeNode> Tree { get; set; } = new List<ChangeTreeNode>(); public List<ChangeTreeNode> Tree { get; set; } = new List<ChangeTreeNode>();
public AvaloniaList<ChangeTreeNode> Rows { get; set; } = new AvaloniaList<ChangeTreeNode>(); public AvaloniaList<ChangeTreeNode> Rows { get; set; } = new AvaloniaList<ChangeTreeNode>();
public AvaloniaList<ChangeTreeNode> SelectedRows { get; set; } = new AvaloniaList<ChangeTreeNode>();
} }
public class ChangeCollectionAsGrid public class ChangeCollectionAsGrid
{ {
public AvaloniaList<Models.Change> Changes { get; set; } = new AvaloniaList<Models.Change>(); public AvaloniaList<Models.Change> Changes { get; set; } = new AvaloniaList<Models.Change>();
public AvaloniaList<Models.Change> SelectedChanges { get; set; } = new AvaloniaList<Models.Change>();
} }
public class ChangeCollectionAsList public class ChangeCollectionAsList
{ {
public AvaloniaList<Models.Change> Changes { get; set; } = new AvaloniaList<Models.Change>(); public AvaloniaList<Models.Change> Changes { get; set; } = new AvaloniaList<Models.Change>();
public AvaloniaList<Models.Change> SelectedChanges { get; set; } = new AvaloniaList<Models.Change>();
} }
} }

View file

@ -301,11 +301,13 @@ namespace SourceGit.ViewModels
public void StageSelected() public void StageSelected()
{ {
StageChanges(_selectedUnstaged); StageChanges(_selectedUnstaged);
SelectedUnstaged = [];
} }
public void StageAll() public void StageAll()
{ {
StageChanges(_unstaged); StageChanges(_unstaged);
SelectedUnstaged = [];
} }
public async void StageChanges(List<Models.Change> changes) public async void StageChanges(List<Models.Change> changes)
@ -337,11 +339,13 @@ namespace SourceGit.ViewModels
public void UnstageSelected() public void UnstageSelected()
{ {
UnstageChanges(_selectedStaged); UnstageChanges(_selectedStaged);
SelectedStaged = [];
} }
public void UnstageAll() public void UnstageAll()
{ {
UnstageChanges(_staged); UnstageChanges(_staged);
SelectedStaged = [];
} }
public async void UnstageChanges(List<Models.Change> changes) public async void UnstageChanges(List<Models.Change> changes)

View file

@ -31,6 +31,7 @@
<UserControl.DataTemplates> <UserControl.DataTemplates>
<DataTemplate DataType="vm:ChangeCollectionAsTree"> <DataTemplate DataType="vm:ChangeCollectionAsTree">
<v:ChangeCollectionContainer ItemsSource="{Binding Rows}" <v:ChangeCollectionContainer ItemsSource="{Binding Rows}"
SelectedItems="{Binding SelectedRows, Mode=TwoWay}"
SelectionMode="{Binding #ThisControl.SelectionMode}" SelectionMode="{Binding #ThisControl.SelectionMode}"
SelectionChanged="OnRowSelectionChanged"> SelectionChanged="OnRowSelectionChanged">
<ListBox.ItemTemplate> <ListBox.ItemTemplate>
@ -65,6 +66,7 @@
<DataTemplate DataType="vm:ChangeCollectionAsGrid"> <DataTemplate DataType="vm:ChangeCollectionAsGrid">
<v:ChangeCollectionContainer ItemsSource="{Binding Changes}" <v:ChangeCollectionContainer ItemsSource="{Binding Changes}"
SelectedItems="{Binding SelectedChanges, Mode=TwoWay}"
SelectionMode="{Binding #ThisControl.SelectionMode}" SelectionMode="{Binding #ThisControl.SelectionMode}"
SelectionChanged="OnRowSelectionChanged"> SelectionChanged="OnRowSelectionChanged">
<ListBox.ItemTemplate> <ListBox.ItemTemplate>
@ -93,6 +95,7 @@
<DataTemplate DataType="vm:ChangeCollectionAsList"> <DataTemplate DataType="vm:ChangeCollectionAsList">
<v:ChangeCollectionContainer ItemsSource="{Binding Changes}" <v:ChangeCollectionContainer ItemsSource="{Binding Changes}"
SelectedItems="{Binding SelectedChanges, Mode=TwoWay}"
SelectionMode="{Binding #ThisControl.SelectionMode}" SelectionMode="{Binding #ThisControl.SelectionMode}"
SelectionChanged="OnRowSelectionChanged"> SelectionChanged="OnRowSelectionChanged">
<ListBox.ItemTemplate> <ListBox.ItemTemplate>

View file

@ -6,6 +6,7 @@ using Avalonia.Controls;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.Threading;
using Avalonia.VisualTree; using Avalonia.VisualTree;
namespace SourceGit.Views namespace SourceGit.Views
@ -101,7 +102,7 @@ namespace SourceGit.Views
public void ToggleNodeIsExpanded(ViewModels.ChangeTreeNode node) public void ToggleNodeIsExpanded(ViewModels.ChangeTreeNode node)
{ {
if (_displayContext is ViewModels.ChangeCollectionAsTree tree) if (Content is ViewModels.ChangeCollectionAsTree tree)
{ {
node.IsExpanded = !node.IsExpanded; node.IsExpanded = !node.IsExpanded;
@ -136,92 +137,12 @@ namespace SourceGit.Views
{ {
base.OnPropertyChanged(change); base.OnPropertyChanged(change);
if (change.Property == ViewModeProperty || change.Property == ChangesProperty) if (change.Property == ViewModeProperty)
{ UpdateDataSource(false);
_disableSelectionChangingEvent = change.Property == ChangesProperty; else if (change.Property == ChangesProperty)
var changes = Changes; UpdateDataSource(true);
if (changes == null || changes.Count == 0)
{
Content = null;
_displayContext = null;
_disableSelectionChangingEvent = false;
return;
}
if (ViewMode == Models.ChangeViewMode.Tree)
{
HashSet<string> oldFolded = new HashSet<string>();
if (_displayContext is ViewModels.ChangeCollectionAsTree oldTree)
{
foreach (var row in oldTree.Rows)
{
if (row.IsFolder && !row.IsExpanded)
oldFolded.Add(row.FullPath);
}
}
var tree = new ViewModels.ChangeCollectionAsTree();
tree.Tree = ViewModels.ChangeTreeNode.Build(changes, oldFolded);
var rows = new List<ViewModels.ChangeTreeNode>();
MakeTreeRows(rows, tree.Tree);
tree.Rows.AddRange(rows);
_displayContext = tree;
}
else if (ViewMode == Models.ChangeViewMode.Grid)
{
var grid = new ViewModels.ChangeCollectionAsGrid();
grid.Changes.AddRange(changes);
_displayContext = grid;
}
else
{
var list = new ViewModels.ChangeCollectionAsList();
list.Changes.AddRange(changes);
_displayContext = list;
}
Content = _displayContext;
_disableSelectionChangingEvent = false;
}
else if (change.Property == SelectedChangesProperty) else if (change.Property == SelectedChangesProperty)
{ UpdateSelection();
if (_disableSelectionChangingEvent)
return;
var list = this.FindDescendantOfType<ChangeCollectionContainer>();
if (list == null)
return;
_disableSelectionChangingEvent = true;
var selected = SelectedChanges;
if (selected == null || selected.Count == 0)
{
list.SelectedItem = null;
}
else if (_displayContext is ViewModels.ChangeCollectionAsTree tree)
{
var sets = new HashSet<Models.Change>();
foreach (var c in selected)
sets.Add(c);
var nodes = new List<ViewModels.ChangeTreeNode>();
foreach (var row in tree.Rows)
{
if (row.Change != null && sets.Contains(row.Change))
nodes.Add(row);
}
list.SelectedItems = nodes;
}
else
{
list.SelectedItems = selected;
}
_disableSelectionChangingEvent = false;
}
} }
private void OnRowDoubleTapped(object sender, TappedEventArgs e) private void OnRowDoubleTapped(object sender, TappedEventArgs e)
@ -283,6 +204,120 @@ namespace SourceGit.Views
} }
} }
private void UpdateDataSource(bool disableEvents)
{
_disableSelectionChangingEvent = disableEvents;
var changes = Changes;
if (changes == null || changes.Count == 0)
{
Content = null;
_disableSelectionChangingEvent = false;
return;
}
var selected = SelectedChanges ?? [];
if (ViewMode == Models.ChangeViewMode.Tree)
{
HashSet<string> oldFolded = new HashSet<string>();
if (Content is ViewModels.ChangeCollectionAsTree oldTree)
{
foreach (var row in oldTree.Rows)
{
if (row.IsFolder && !row.IsExpanded)
oldFolded.Add(row.FullPath);
}
}
var tree = new ViewModels.ChangeCollectionAsTree();
tree.Tree = ViewModels.ChangeTreeNode.Build(changes, oldFolded);
var rows = new List<ViewModels.ChangeTreeNode>();
MakeTreeRows(rows, tree.Tree);
tree.Rows.AddRange(rows);
if (selected.Count > 0)
{
var sets = new HashSet<Models.Change>();
foreach (var c in selected)
sets.Add(c);
var nodes = new List<ViewModels.ChangeTreeNode>();
foreach (var row in tree.Rows)
{
if (row.Change != null && sets.Contains(row.Change))
nodes.Add(row);
}
tree.SelectedRows.AddRange(nodes);
}
Content = tree;
}
else if (ViewMode == Models.ChangeViewMode.Grid)
{
var grid = new ViewModels.ChangeCollectionAsGrid();
grid.Changes.AddRange(changes);
if (selected.Count > 0)
grid.SelectedChanges.AddRange(selected);
Content = grid;
}
else
{
var list = new ViewModels.ChangeCollectionAsList();
list.Changes.AddRange(changes);
if (selected.Count > 0)
list.SelectedChanges.AddRange(selected);
Content = list;
}
_disableSelectionChangingEvent = false;
}
private void UpdateSelection()
{
if (_disableSelectionChangingEvent)
return;
_disableSelectionChangingEvent = true;
var selected = SelectedChanges ?? [];
if (Content is ViewModels.ChangeCollectionAsTree tree)
{
tree.SelectedRows.Clear();
if (selected.Count > 0)
{
var sets = new HashSet<Models.Change>();
foreach (var c in selected)
sets.Add(c);
var nodes = new List<ViewModels.ChangeTreeNode>();
foreach (var row in tree.Rows)
{
if (row.Change != null && sets.Contains(row.Change))
nodes.Add(row);
}
tree.SelectedRows.AddRange(nodes);
}
}
else if (Content is ViewModels.ChangeCollectionAsGrid grid)
{
grid.SelectedChanges.Clear();
if (selected.Count > 0)
grid.SelectedChanges.AddRange(selected);
}
else if (Content is ViewModels.ChangeCollectionAsList list)
{
list.SelectedChanges.Clear();
if (selected.Count > 0)
list.SelectedChanges.AddRange(selected);
}
_disableSelectionChangingEvent = false;
}
private void CollectChangesInNode(List<Models.Change> outs, ViewModels.ChangeTreeNode node) private void CollectChangesInNode(List<Models.Change> outs, ViewModels.ChangeTreeNode node)
{ {
if (node.IsFolder) if (node.IsFolder)
@ -324,6 +359,5 @@ namespace SourceGit.Views
} }
private bool _disableSelectionChangingEvent = false; private bool _disableSelectionChangingEvent = false;
private object _displayContext = null;
} }
} }