using System; using System.Collections.Generic; using CommunityToolkit.Mvvm.ComponentModel; namespace SourceGit.ViewModels { public class FileTreeNode : ObservableObject { public string FullPath { get; set; } = string.Empty; public bool IsFolder { get; set; } = false; public object Backend { get; set; } = null; public List Children { get; set; } = new List(); public bool IsExpanded { get => _isExpanded; set => SetProperty(ref _isExpanded, value); } public static List Build(List changes) { var nodes = new List(); var folders = new Dictionary(); var expanded = changes.Count <= 50; foreach (var c in changes) { var sepIdx = c.Path.IndexOf('/', StringComparison.Ordinal); if (sepIdx == -1) { nodes.Add(new FileTreeNode() { FullPath = c.Path, Backend = c, IsFolder = false, IsExpanded = false }); } else { FileTreeNode lastFolder = null; var start = 0; while (sepIdx != -1) { var folder = c.Path.Substring(0, sepIdx); if (folders.TryGetValue(folder, out var value)) { lastFolder = value; } else if (lastFolder == null) { lastFolder = new FileTreeNode() { FullPath = folder, Backend = null, IsFolder = true, IsExpanded = expanded }; nodes.Add(lastFolder); folders.Add(folder, lastFolder); } else { var cur = new FileTreeNode() { FullPath = folder, Backend = null, IsFolder = true, IsExpanded = expanded }; folders.Add(folder, cur); lastFolder.Children.Add(cur); lastFolder = cur; } start = sepIdx + 1; sepIdx = c.Path.IndexOf('/', start); } lastFolder.Children.Add(new FileTreeNode() { FullPath = c.Path, Backend = c, IsFolder = false, IsExpanded = false }); } } folders.Clear(); Sort(nodes); return nodes; } public static List Build(List files) { var nodes = new List(); var folders = new Dictionary(); var expanded = files.Count <= 50; foreach (var f in files) { var sepIdx = f.Path.IndexOf('/', StringComparison.Ordinal); if (sepIdx == -1) { nodes.Add(new FileTreeNode() { FullPath = f.Path, Backend = f, IsFolder = false, IsExpanded = false }); } else { FileTreeNode lastFolder = null; var start = 0; while (sepIdx != -1) { var folder = f.Path.Substring(0, sepIdx); if (folders.TryGetValue(folder, out var value)) { lastFolder = value; } else if (lastFolder == null) { lastFolder = new FileTreeNode() { FullPath = folder, Backend = null, IsFolder = true, IsExpanded = expanded }; nodes.Add(lastFolder); folders.Add(folder, lastFolder); } else { var cur = new FileTreeNode() { FullPath = folder, Backend = null, IsFolder = true, IsExpanded = expanded }; folders.Add(folder, cur); lastFolder.Children.Add(cur); lastFolder = cur; } start = sepIdx + 1; sepIdx = f.Path.IndexOf('/', start); } lastFolder.Children.Add(new FileTreeNode() { FullPath = f.Path, Backend = f, IsFolder = false, IsExpanded = false }); } } folders.Clear(); Sort(nodes); return nodes; } public static FileTreeNode SelectByPath(List nodes, string path) { foreach (var node in nodes) { if (node.FullPath == path) return node; if (node.IsFolder && path.StartsWith(node.FullPath + "/", StringComparison.Ordinal)) { var foundInChildren = SelectByPath(node.Children, path); if (foundInChildren != null) { node.IsExpanded = true; } return foundInChildren; } } return null; } private static void Sort(List nodes) { nodes.Sort((l, r) => { if (l.IsFolder == r.IsFolder) { return l.FullPath.CompareTo(r.FullPath); } else { return l.IsFolder ? -1 : 1; } }); foreach (var node in nodes) { if (node.Children.Count > 1) Sort(node.Children); } } private bool _isExpanded = true; } }