mirror of
https://github.com/sourcegit-scm/sourcegit.git
synced 2024-12-22 20:37:19 -08:00
refactor: use cutom RevisionFileTreeView
instead of TreeDataGrid
This commit is contained in:
parent
b07aa9e63f
commit
7f228385f9
5 changed files with 376 additions and 178 deletions
|
@ -1070,15 +1070,15 @@
|
||||||
</Style>
|
</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="8" />
|
<Setter Property="Width" Value="10" />
|
||||||
<Setter Property="Height" Value="8" />
|
<Setter Property="Height" Value="10" />
|
||||||
<Setter Property="Background" Value="Transparent"/>
|
<Setter Property="Background" Value="Transparent"/>
|
||||||
<Setter Property="Template">
|
<Setter Property="Template">
|
||||||
<ControlTemplate>
|
<ControlTemplate>
|
||||||
<Border Background="Transparent"
|
<Border Background="Transparent"
|
||||||
Width="{TemplateBinding Width}"
|
Width="{TemplateBinding Width}"
|
||||||
Height="{TemplateBinding Height}"
|
Height="{TemplateBinding Height}"
|
||||||
HorizontalAlignment="Center"
|
HorizontalAlignment="Left"
|
||||||
VerticalAlignment="Center">
|
VerticalAlignment="Center">
|
||||||
<Path x:Name="ChevronPath"
|
<Path x:Name="ChevronPath"
|
||||||
Data="M 4 0 L 8 4 L 4 8 Z"
|
Data="M 4 0 L 8 4 L 4 8 Z"
|
||||||
|
|
50
src/Views/RevisionFileTreeView.axaml
Normal file
50
src/Views/RevisionFileTreeView.axaml
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
<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:c="using:SourceGit.Converters"
|
||||||
|
xmlns:v="using:SourceGit.Views"
|
||||||
|
xmlns:vm="using:SourceGit.ViewModels"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
|
x:Class="SourceGit.Views.RevisionFileTreeView"
|
||||||
|
x:Name="ThisControl">
|
||||||
|
<ListBox ItemsSource="{Binding #ThisControl.Rows}"
|
||||||
|
Background="Transparent"
|
||||||
|
SelectionMode="Single"
|
||||||
|
SelectionChanged="OnRowsSelectionChanged"
|
||||||
|
ScrollViewer.HorizontalScrollBarVisibility="Auto"
|
||||||
|
ScrollViewer.VerticalScrollBarVisibility="Auto">
|
||||||
|
<ListBox.ItemsPanel>
|
||||||
|
<ItemsPanelTemplate>
|
||||||
|
<VirtualizingStackPanel Orientation="Vertical"/>
|
||||||
|
</ItemsPanelTemplate>
|
||||||
|
</ListBox.ItemsPanel>
|
||||||
|
|
||||||
|
<ListBox.Styles>
|
||||||
|
<Style Selector="ListBoxItem">
|
||||||
|
<Setter Property="Height" Value="24"/>
|
||||||
|
<Setter Property="Margin" Value="0"/>
|
||||||
|
<Setter Property="Padding" Value="0"/>
|
||||||
|
</Style>
|
||||||
|
</ListBox.Styles>
|
||||||
|
|
||||||
|
<ListBox.ItemTemplate>
|
||||||
|
<DataTemplate DataType="v:RevisionFileTreeNode">
|
||||||
|
<Grid ColumnDefinitions="16,Auto,Auto,*"
|
||||||
|
Margin="{Binding Depth, Converter={x:Static c:IntConverters.ToTreeMargin}}"
|
||||||
|
Background="Transparent"
|
||||||
|
DoubleTapped="OnTreeNodeDoubleTapped"
|
||||||
|
ContextRequested="OnTreeNodeContextRequested">
|
||||||
|
<v:RevisionFileTreeNodeToggleButton Grid.Column="0"
|
||||||
|
Classes="tree_expander"
|
||||||
|
Focusable="False"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
IsChecked="{Binding IsExpanded, Mode=OneWay}"
|
||||||
|
IsVisible="{Binding IsFolder}"/>
|
||||||
|
<v:RevisionTreeNodeIcon Grid.Column="1" Width="14" Height="14" Node="{Binding}" IsExpanded="{Binding IsExpanded, Mode=OneWay}"/>
|
||||||
|
<TextBlock Grid.Column="2" Classes="monospace" Text="{Binding Name}" Margin="6,0,0,0"/>
|
||||||
|
</Grid>
|
||||||
|
</DataTemplate>
|
||||||
|
</ListBox.ItemTemplate>
|
||||||
|
</ListBox>
|
||||||
|
</UserControl>
|
322
src/Views/RevisionFileTreeView.axaml.cs
Normal file
322
src/Views/RevisionFileTreeView.axaml.cs
Normal file
|
@ -0,0 +1,322 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Collections;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Controls.Primitives;
|
||||||
|
using Avalonia.Input;
|
||||||
|
using Avalonia.Layout;
|
||||||
|
using Avalonia.Media;
|
||||||
|
using Avalonia.VisualTree;
|
||||||
|
|
||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
|
||||||
|
namespace SourceGit.Views
|
||||||
|
{
|
||||||
|
public class RevisionFileTreeNode : ObservableObject
|
||||||
|
{
|
||||||
|
public Models.Object Backend { get; set; } = null;
|
||||||
|
public int Depth { get; set; } = 0;
|
||||||
|
public List<RevisionFileTreeNode> Children { get; set; } = new List<RevisionFileTreeNode>();
|
||||||
|
|
||||||
|
public string Name
|
||||||
|
{
|
||||||
|
get => Backend == null ? string.Empty : Path.GetFileName(Backend.Path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsFolder
|
||||||
|
{
|
||||||
|
get => Backend != null && Backend.Type == Models.ObjectType.Tree;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsExpanded
|
||||||
|
{
|
||||||
|
get => _isExpanded;
|
||||||
|
set => SetProperty(ref _isExpanded, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool _isExpanded = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class RevisionFileTreeNodeToggleButton : ToggleButton
|
||||||
|
{
|
||||||
|
protected override Type StyleKeyOverride => typeof(ToggleButton);
|
||||||
|
|
||||||
|
protected override void OnPointerPressed(PointerPressedEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed &&
|
||||||
|
DataContext is RevisionFileTreeNode { IsFolder: true } node)
|
||||||
|
{
|
||||||
|
var tree = this.FindAncestorOfType<RevisionFileTreeView>();
|
||||||
|
tree.ToggleNodeIsExpanded(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class RevisionTreeNodeIcon : UserControl
|
||||||
|
{
|
||||||
|
public static readonly StyledProperty<RevisionFileTreeNode> NodeProperty =
|
||||||
|
AvaloniaProperty.Register<RevisionTreeNodeIcon, RevisionFileTreeNode>(nameof(Node));
|
||||||
|
|
||||||
|
public RevisionFileTreeNode Node
|
||||||
|
{
|
||||||
|
get => GetValue(NodeProperty);
|
||||||
|
set => SetValue(NodeProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly StyledProperty<bool> IsExpandedProperty =
|
||||||
|
AvaloniaProperty.Register<RevisionTreeNodeIcon, bool>(nameof(IsExpanded));
|
||||||
|
|
||||||
|
public bool IsExpanded
|
||||||
|
{
|
||||||
|
get => GetValue(IsExpandedProperty);
|
||||||
|
set => SetValue(IsExpandedProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static RevisionTreeNodeIcon()
|
||||||
|
{
|
||||||
|
NodeProperty.Changed.AddClassHandler<RevisionTreeNodeIcon>((icon, _) => icon.UpdateContent());
|
||||||
|
IsExpandedProperty.Changed.AddClassHandler<RevisionTreeNodeIcon>((icon, _) => icon.UpdateContent());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateContent()
|
||||||
|
{
|
||||||
|
var node = Node;
|
||||||
|
if (node == null || node.Backend == null)
|
||||||
|
{
|
||||||
|
Content = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var obj = node.Backend;
|
||||||
|
if (obj.Type == Models.ObjectType.Blob)
|
||||||
|
{
|
||||||
|
CreateContent(14, new Thickness(0, 0, 0, 0), "Icons.File");
|
||||||
|
}
|
||||||
|
else if (obj.Type == Models.ObjectType.Commit)
|
||||||
|
{
|
||||||
|
CreateContent(14, new Thickness(0, 0, 0, 0), "Icons.Submodule");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (node.IsExpanded)
|
||||||
|
CreateContent(14, new Thickness(0, 2, 0, 0), "Icons.Folder.Open", Brushes.Goldenrod);
|
||||||
|
else
|
||||||
|
CreateContent(14, new Thickness(0, 2, 0, 0), "Icons.Folder.Fill", Brushes.Goldenrod);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CreateContent(double size, Thickness margin, string iconKey, IBrush fill = null)
|
||||||
|
{
|
||||||
|
var geo = this.FindResource(iconKey) as StreamGeometry;
|
||||||
|
if (geo == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var icon = new Avalonia.Controls.Shapes.Path()
|
||||||
|
{
|
||||||
|
Width = size,
|
||||||
|
Height = size,
|
||||||
|
HorizontalAlignment = HorizontalAlignment.Left,
|
||||||
|
VerticalAlignment = VerticalAlignment.Center,
|
||||||
|
Margin = margin,
|
||||||
|
Data = geo,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (fill != null)
|
||||||
|
icon.Fill = fill;
|
||||||
|
|
||||||
|
Content = icon;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public partial class RevisionFileTreeView : UserControl
|
||||||
|
{
|
||||||
|
public static readonly StyledProperty<string> RevisionProperty =
|
||||||
|
AvaloniaProperty.Register<RevisionFileTreeView, string>(nameof(Revision), null);
|
||||||
|
|
||||||
|
public string Revision
|
||||||
|
{
|
||||||
|
get => GetValue(RevisionProperty);
|
||||||
|
set => SetValue(RevisionProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AvaloniaList<RevisionFileTreeNode> Rows
|
||||||
|
{
|
||||||
|
get => _rows;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RevisionFileTreeView()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ToggleNodeIsExpanded(RevisionFileTreeNode node)
|
||||||
|
{
|
||||||
|
_disableSelectionChangingEvent = true;
|
||||||
|
node.IsExpanded = !node.IsExpanded;
|
||||||
|
|
||||||
|
var depth = node.Depth;
|
||||||
|
var idx = _rows.IndexOf(node);
|
||||||
|
if (idx == -1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (node.IsExpanded)
|
||||||
|
{
|
||||||
|
var subtree = GetChildrenOfTreeNode(node);
|
||||||
|
if (subtree != null && subtree.Count > 0)
|
||||||
|
{
|
||||||
|
var subrows = new List<RevisionFileTreeNode>();
|
||||||
|
MakeRows(subrows, node.Children, depth + 1);
|
||||||
|
_rows.InsertRange(idx + 1, subrows);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var removeCount = 0;
|
||||||
|
for (int i = idx + 1; i < _rows.Count; i++)
|
||||||
|
{
|
||||||
|
var row = _rows[i];
|
||||||
|
if (row.Depth <= depth)
|
||||||
|
break;
|
||||||
|
|
||||||
|
removeCount++;
|
||||||
|
}
|
||||||
|
_rows.RemoveRange(idx + 1, removeCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
_disableSelectionChangingEvent = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
|
||||||
|
{
|
||||||
|
base.OnPropertyChanged(change);
|
||||||
|
|
||||||
|
if (change.Property == RevisionProperty)
|
||||||
|
{
|
||||||
|
_tree.Clear();
|
||||||
|
_rows.Clear();
|
||||||
|
|
||||||
|
var vm = DataContext as ViewModels.CommitDetail;
|
||||||
|
if (vm == null || vm.Commit == null)
|
||||||
|
{
|
||||||
|
GC.Collect();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var objects = vm.GetRevisionFilesUnderFolder(null);
|
||||||
|
if (objects == null || objects.Count == 0)
|
||||||
|
{
|
||||||
|
GC.Collect();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var obj in objects)
|
||||||
|
_tree.Add(new RevisionFileTreeNode { Backend = obj });
|
||||||
|
|
||||||
|
_tree.Sort((l, r) =>
|
||||||
|
{
|
||||||
|
if (l.IsFolder == r.IsFolder)
|
||||||
|
return l.Name.CompareTo(r.Name);
|
||||||
|
return l.IsFolder ? -1 : 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
var topTree = new List<RevisionFileTreeNode>();
|
||||||
|
MakeRows(topTree, _tree, 0);
|
||||||
|
_rows.AddRange(topTree);
|
||||||
|
GC.Collect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnTreeNodeContextRequested(object sender, ContextRequestedEventArgs e)
|
||||||
|
{
|
||||||
|
if (DataContext is ViewModels.CommitDetail vm && sender is Grid { DataContext: RevisionFileTreeNode { Backend: Models.Object obj } } grid)
|
||||||
|
{
|
||||||
|
if (obj.Type != Models.ObjectType.Tree)
|
||||||
|
{
|
||||||
|
var menu = vm.CreateRevisionFileContextMenu(obj);
|
||||||
|
grid.OpenContextMenu(menu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnTreeNodeDoubleTapped(object sender, TappedEventArgs e)
|
||||||
|
{
|
||||||
|
if (sender is Grid { DataContext: RevisionFileTreeNode { IsFolder: true } node })
|
||||||
|
{
|
||||||
|
var posX = e.GetPosition(this).X;
|
||||||
|
if (posX < node.Depth * 16 + 16)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ToggleNodeIsExpanded(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnRowsSelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||||
|
{
|
||||||
|
if (_disableSelectionChangingEvent)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (sender is ListBox list && DataContext is ViewModels.CommitDetail vm)
|
||||||
|
{
|
||||||
|
var node = list.SelectedItem as RevisionFileTreeNode;
|
||||||
|
if (node != null && !node.IsFolder)
|
||||||
|
vm.ViewRevisionFile(node.Backend);
|
||||||
|
else
|
||||||
|
vm.ViewRevisionFile(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<RevisionFileTreeNode> GetChildrenOfTreeNode(RevisionFileTreeNode node)
|
||||||
|
{
|
||||||
|
if (!node.IsFolder)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
if (node.Children.Count > 0)
|
||||||
|
return node.Children;
|
||||||
|
|
||||||
|
var vm = DataContext as ViewModels.CommitDetail;
|
||||||
|
if (vm == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var objects = vm.GetRevisionFilesUnderFolder(node.Backend.Path + "/");
|
||||||
|
if (objects == null || objects.Count == 0)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
foreach (var obj in objects)
|
||||||
|
node.Children.Add(new RevisionFileTreeNode() { Backend = obj });
|
||||||
|
|
||||||
|
node.Children.Sort((l, r) =>
|
||||||
|
{
|
||||||
|
if (l.IsFolder == r.IsFolder)
|
||||||
|
return l.Name.CompareTo(r.Name);
|
||||||
|
return l.IsFolder ? -1 : 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
return node.Children;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void MakeRows(List<RevisionFileTreeNode> rows, List<RevisionFileTreeNode> nodes, int depth)
|
||||||
|
{
|
||||||
|
foreach (var node in nodes)
|
||||||
|
{
|
||||||
|
node.Depth = depth;
|
||||||
|
rows.Add(node);
|
||||||
|
|
||||||
|
if (!node.IsExpanded || !node.IsFolder)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
MakeRows(rows, node.Children, depth + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<RevisionFileTreeNode> _tree = new List<RevisionFileTreeNode>();
|
||||||
|
private AvaloniaList<RevisionFileTreeNode> _rows = new AvaloniaList<RevisionFileTreeNode>();
|
||||||
|
private bool _disableSelectionChangingEvent = false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,17 +17,7 @@
|
||||||
|
|
||||||
<!-- File Tree -->
|
<!-- File Tree -->
|
||||||
<Border Grid.Column="0" BorderBrush="{DynamicResource Brush.Border2}" BorderThickness="1" Background="{DynamicResource Brush.Contents}">
|
<Border Grid.Column="0" BorderBrush="{DynamicResource Brush.Border2}" BorderThickness="1" Background="{DynamicResource Brush.Contents}">
|
||||||
<v:RevisionFileTreeView Revision="{Binding Commit.SHA}" ContextRequested="OnRevisionFileTreeViewContextRequested">
|
<v:RevisionFileTreeView Revision="{Binding Commit.SHA}"/>
|
||||||
<v:RevisionFileTreeView.Resources>
|
|
||||||
<DataTemplate x:Key="RevisionFileTreeNodeTemplate" DataType="v:RevisionFileTreeNode">
|
|
||||||
<Grid HorizontalAlignment="Stretch" Height="24" ColumnDefinitions="Auto,*">
|
|
||||||
<Path Grid.Column="0" Classes="folder_icon" Width="14" Height="14" Margin="0,2,0,0" IsVisible="{Binding IsFolder}" Fill="Goldenrod" VerticalAlignment="Center"/>
|
|
||||||
<Path Grid.Column="0" Width="14" Height="14" IsVisible="{Binding !IsFolder}" Data="{StaticResource Icons.File}" VerticalAlignment="Center"/>
|
|
||||||
<TextBlock Grid.Column="1" Classes="monospace" Text="{Binding Name}" Margin="6,0,0,0"/>
|
|
||||||
</Grid>
|
|
||||||
</DataTemplate>
|
|
||||||
</v:RevisionFileTreeView.Resources>
|
|
||||||
</v:RevisionFileTreeView>
|
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
<GridSplitter Grid.Column="1"
|
<GridSplitter Grid.Column="1"
|
||||||
|
|
|
@ -1,12 +1,8 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
|
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Controls.Models.TreeDataGrid;
|
|
||||||
using Avalonia.Controls.Primitives;
|
using Avalonia.Controls.Primitives;
|
||||||
using Avalonia.Controls.Templates;
|
|
||||||
using Avalonia.Interactivity;
|
using Avalonia.Interactivity;
|
||||||
using Avalonia.Media;
|
using Avalonia.Media;
|
||||||
|
|
||||||
|
@ -16,152 +12,6 @@ using AvaloniaEdit.Editing;
|
||||||
|
|
||||||
namespace SourceGit.Views
|
namespace SourceGit.Views
|
||||||
{
|
{
|
||||||
public class RevisionFileTreeNode
|
|
||||||
{
|
|
||||||
public Models.Object Backend { get; set; } = null;
|
|
||||||
public bool IsExpanded { get; set; } = false;
|
|
||||||
public List<RevisionFileTreeNode> Children { get; set; } = new List<RevisionFileTreeNode>();
|
|
||||||
|
|
||||||
public bool IsFolder => Backend != null && Backend.Type == Models.ObjectType.Tree;
|
|
||||||
public string Name => Backend != null ? Path.GetFileName(Backend.Path) : string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class RevisionFileTreeView : UserControl
|
|
||||||
{
|
|
||||||
public static readonly StyledProperty<string> RevisionProperty =
|
|
||||||
AvaloniaProperty.Register<RevisionFileTreeView, string>(nameof(Revision), null);
|
|
||||||
|
|
||||||
public string Revision
|
|
||||||
{
|
|
||||||
get => GetValue(RevisionProperty);
|
|
||||||
set => SetValue(RevisionProperty, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Models.Object SelectedObject
|
|
||||||
{
|
|
||||||
get;
|
|
||||||
private set;
|
|
||||||
} = null;
|
|
||||||
|
|
||||||
protected override Type StyleKeyOverride => typeof(UserControl);
|
|
||||||
|
|
||||||
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
|
|
||||||
{
|
|
||||||
base.OnPropertyChanged(change);
|
|
||||||
|
|
||||||
if (change.Property == RevisionProperty)
|
|
||||||
{
|
|
||||||
SelectedObject = null;
|
|
||||||
|
|
||||||
if (Content is TreeDataGrid tree && tree.Source is IDisposable disposable)
|
|
||||||
disposable.Dispose();
|
|
||||||
|
|
||||||
var vm = DataContext as ViewModels.CommitDetail;
|
|
||||||
if (vm == null || vm.Commit == null)
|
|
||||||
{
|
|
||||||
Content = null;
|
|
||||||
GC.Collect();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var objects = vm.GetRevisionFilesUnderFolder(null);
|
|
||||||
if (objects == null || objects.Count == 0)
|
|
||||||
{
|
|
||||||
Content = null;
|
|
||||||
GC.Collect();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var toplevelObjects = new List<RevisionFileTreeNode>();
|
|
||||||
foreach (var obj in objects)
|
|
||||||
toplevelObjects.Add(new RevisionFileTreeNode() { Backend = obj });
|
|
||||||
|
|
||||||
toplevelObjects.Sort((l, r) =>
|
|
||||||
{
|
|
||||||
if (l.IsFolder == r.IsFolder)
|
|
||||||
return l.Name.CompareTo(r.Name);
|
|
||||||
return l.IsFolder ? -1 : 1;
|
|
||||||
});
|
|
||||||
|
|
||||||
var template = this.FindResource("RevisionFileTreeNodeTemplate") as IDataTemplate;
|
|
||||||
var source = new HierarchicalTreeDataGridSource<RevisionFileTreeNode>(toplevelObjects)
|
|
||||||
{
|
|
||||||
Columns =
|
|
||||||
{
|
|
||||||
new HierarchicalExpanderColumn<RevisionFileTreeNode>(
|
|
||||||
new TemplateColumn<RevisionFileTreeNode>(null, template, null, GridLength.Auto),
|
|
||||||
GetChildrenOfTreeNode,
|
|
||||||
x => x.IsFolder,
|
|
||||||
x => x.IsExpanded)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var selection = new Models.TreeDataGridSelectionModel<RevisionFileTreeNode>(source, GetChildrenOfTreeNode);
|
|
||||||
selection.SingleSelect = true;
|
|
||||||
selection.SelectionChanged += (s, _) =>
|
|
||||||
{
|
|
||||||
if (s is Models.TreeDataGridSelectionModel<RevisionFileTreeNode> model)
|
|
||||||
{
|
|
||||||
var node = model.SelectedItem;
|
|
||||||
var detail = DataContext as ViewModels.CommitDetail;
|
|
||||||
|
|
||||||
if (node != null && !node.IsFolder)
|
|
||||||
{
|
|
||||||
SelectedObject = node.Backend;
|
|
||||||
detail.ViewRevisionFile(node.Backend);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
SelectedObject = null;
|
|
||||||
detail.ViewRevisionFile(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
source.Selection = selection;
|
|
||||||
Content = new TreeDataGrid()
|
|
||||||
{
|
|
||||||
AutoDragDropRows = false,
|
|
||||||
ShowColumnHeaders = false,
|
|
||||||
CanUserResizeColumns = false,
|
|
||||||
CanUserSortColumns = false,
|
|
||||||
Source = source,
|
|
||||||
};
|
|
||||||
|
|
||||||
GC.Collect();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<RevisionFileTreeNode> GetChildrenOfTreeNode(RevisionFileTreeNode node)
|
|
||||||
{
|
|
||||||
if (!node.IsFolder)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
if (node.Children.Count > 0)
|
|
||||||
return node.Children;
|
|
||||||
|
|
||||||
var vm = DataContext as ViewModels.CommitDetail;
|
|
||||||
if (vm == null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
var objects = vm.GetRevisionFilesUnderFolder(node.Backend.Path + "/");
|
|
||||||
if (objects == null || objects.Count == 0)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
foreach (var obj in objects)
|
|
||||||
node.Children.Add(new RevisionFileTreeNode() { Backend = obj });
|
|
||||||
|
|
||||||
node.Children.Sort((l, r) =>
|
|
||||||
{
|
|
||||||
if (l.IsFolder == r.IsFolder)
|
|
||||||
return l.Name.CompareTo(r.Name);
|
|
||||||
return l.IsFolder ? -1 : 1;
|
|
||||||
});
|
|
||||||
|
|
||||||
return node.Children;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class RevisionTextFileView : TextEditor
|
public class RevisionTextFileView : TextEditor
|
||||||
{
|
{
|
||||||
protected override Type StyleKeyOverride => typeof(TextEditor);
|
protected override Type StyleKeyOverride => typeof(TextEditor);
|
||||||
|
@ -241,19 +91,5 @@ namespace SourceGit.Views
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnRevisionFileTreeViewContextRequested(object sender, ContextRequestedEventArgs e)
|
|
||||||
{
|
|
||||||
if (DataContext is ViewModels.CommitDetail vm && sender is RevisionFileTreeView view)
|
|
||||||
{
|
|
||||||
if (view.SelectedObject != null && view.SelectedObject.Type != Models.ObjectType.Tree)
|
|
||||||
{
|
|
||||||
var menu = vm.CreateRevisionFileContextMenu(view.SelectedObject);
|
|
||||||
view.OpenContextMenu(menu);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
e.Handled = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue