Files
I150/srcs/RobotNet10/Components/RobotNet10.ScriptEditor/Models/ScriptFolder.cs
2026-07-03 16:37:12 +07:00

277 lines
10 KiB
C#

using RobotNet10.ScriptEngine.Shared;
using static System.Runtime.InteropServices.JavaScript.JSType;
namespace RobotNet10.ScriptEditor.Models;
public class ScriptFolder(ScriptFolderDto data, ScriptFolder? parent = null) : IHierarchyItem
{
public ScriptFolder? Parent => parent;
public string Path => System.IO.Path.Combine(parent?.Path ?? "", Name);
public int Level { get; } = data.Level;
public bool IsModified { get; private set; }
public int WarningCount { get; private set; }
public int ErrorCount { get; private set; }
public bool IsExpanded { get; set; } = false;
public event Action? ChildrenChanged;
public event Action? Modified;
public event Action? NameChanged;
public event Action<int, int>? DiagnosticsChanged;
public IEnumerable<ScriptFolder> Folders => WorkspaceFolders;
public IEnumerable<ScriptFile> Files => WorkspaceFiles;
private string _name = data.Name;
public string Name
{
get => _name;
set
{
if (string.IsNullOrEmpty(value)) throw new ArgumentNullException($"Tên file {_name} không được để trống");
if (_name == value) return;
_name = value;
NameChanged?.Invoke();
}
}
private readonly List<ScriptFolder> WorkspaceFolders = [];
private readonly List<ScriptFile> WorkspaceFiles = [];
public void AddFiles(params IEnumerable<ScriptFile> files)
{
WorkspaceFiles.AddRange(files);
WorkspaceFiles.Sort((a, b) => string.Compare(a.Name, b.Name, StringComparison.Ordinal));
// Recalculate totals including new files
var totalWarningCount = Files.Sum(file => file.WarningCount) + Folders.Sum(folder => folder.WarningCount);
var totalErrorCount = Files.Sum(file => file.ErrorCount) + Folders.Sum(folder => folder.ErrorCount);
var newIsModified = Files.Any(file => file.IsModified) || Folders.Any(folder => folder.IsModified);
var warningChanged = WarningCount != totalWarningCount;
var errorChanged = ErrorCount != totalErrorCount;
var modifiedChanged = IsModified != newIsModified;
WarningCount = totalWarningCount;
ErrorCount = totalErrorCount;
IsModified = newIsModified;
foreach (var file in files)
{
file.Modified += UpdateModified;
file.DiagnosticsChanged += OnDiagnosticsChanged;
}
// Notify changes
if (warningChanged || errorChanged)
{
DiagnosticsChanged?.Invoke(WarningCount, ErrorCount);
}
if (modifiedChanged)
{
Modified?.Invoke();
}
ChildrenChanged?.Invoke();
}
public void AddFolders(params IEnumerable<ScriptFolder> folders)
{
WorkspaceFolders.AddRange(folders);
WorkspaceFolders.Sort((a, b) => string.Compare(a.Name, b.Name, StringComparison.Ordinal));
// Recalculate totals including new folders
var totalWarningCount = Files.Sum(file => file.WarningCount) + Folders.Sum(folder => folder.WarningCount);
var totalErrorCount = Files.Sum(file => file.ErrorCount) + Folders.Sum(folder => folder.ErrorCount);
var newIsModified = Files.Any(file => file.IsModified) || Folders.Any(folder => folder.IsModified);
var warningChanged = WarningCount != totalWarningCount;
var errorChanged = ErrorCount != totalErrorCount;
var modifiedChanged = IsModified != newIsModified;
WarningCount = totalWarningCount;
ErrorCount = totalErrorCount;
IsModified = newIsModified;
foreach (var folder in folders)
{
folder.Modified += UpdateModified;
folder.DiagnosticsChanged += OnDiagnosticsChanged;
}
// Notify changes
if (warningChanged || errorChanged)
{
DiagnosticsChanged?.Invoke(WarningCount, ErrorCount);
}
if (modifiedChanged)
{
Modified?.Invoke();
}
ChildrenChanged?.Invoke();
}
public void RemoveFile(ScriptFile file)
{
if (WorkspaceFiles.Remove(file))
{
// Unsubscribe from file events
file.Modified -= UpdateModified;
file.DiagnosticsChanged -= OnDiagnosticsChanged;
// Recalculate totals after removal
var totalWarningCount = Files.Sum(f => f.WarningCount) + Folders.Sum(folder => folder.WarningCount);
var totalErrorCount = Files.Sum(f => f.ErrorCount) + Folders.Sum(folder => folder.ErrorCount);
var newIsModified = Files.Any(f => f.IsModified) || Folders.Any(folder => folder.IsModified);
var warningChanged = WarningCount != totalWarningCount;
var errorChanged = ErrorCount != totalErrorCount;
var modifiedChanged = IsModified != newIsModified;
WarningCount = totalWarningCount;
ErrorCount = totalErrorCount;
IsModified = newIsModified;
// Dispose the removed file
file.Dispose();
// Notify changes
if (warningChanged || errorChanged)
{
DiagnosticsChanged?.Invoke(WarningCount, ErrorCount);
}
if (modifiedChanged)
{
Modified?.Invoke();
}
ChildrenChanged?.Invoke();
}
}
public void RemoveFolder(ScriptFolder folder)
{
if (WorkspaceFolders.Remove(folder))
{
// Unsubscribe from folder events
folder.Modified -= UpdateModified;
folder.DiagnosticsChanged -= OnDiagnosticsChanged;
// Recalculate totals after removal
var totalWarningCount = Files.Sum(file => file.WarningCount) + Folders.Sum(f => f.WarningCount);
var totalErrorCount = Files.Sum(file => file.ErrorCount) + Folders.Sum(f => f.ErrorCount);
var newIsModified = Files.Any(file => file.IsModified) || Folders.Any(f => f.IsModified);
var warningChanged = WarningCount != totalWarningCount;
var errorChanged = ErrorCount != totalErrorCount;
var modifiedChanged = IsModified != newIsModified;
WarningCount = totalWarningCount;
ErrorCount = totalErrorCount;
IsModified = newIsModified;
// Dispose the removed folder (will handle its children)
folder.Dispose();
// Notify changes
if (warningChanged || errorChanged)
{
DiagnosticsChanged?.Invoke(WarningCount, ErrorCount);
}
if (modifiedChanged)
{
Modified?.Invoke();
}
ChildrenChanged?.Invoke();
}
}
private void UpdateModified()
{
var newIsModified = Files.Any(file => file.IsModified) || Folders.Any(folder => folder.IsModified);
// Only update and notify if the value actually changed
if (IsModified != newIsModified)
{
IsModified = newIsModified;
Modified?.Invoke();
}
}
private void OnDiagnosticsChanged(int warningCount, int errorCount)
{
// Recalculate totals from all children
// Note: The parameters (warningCount, errorCount) are the absolute values from the child that changed,
// but we recalculate from all children to ensure accuracy, especially when multiple files change simultaneously
var totalWarningCount = Files.Sum(file => file.WarningCount) + Folders.Sum(folder => folder.WarningCount);
var totalErrorCount = Files.Sum(file => file.ErrorCount) + Folders.Sum(folder => folder.ErrorCount);
if (WarningCount != totalWarningCount || ErrorCount != totalErrorCount)
{
WarningCount = totalWarningCount;
ErrorCount = totalErrorCount;
DiagnosticsChanged?.Invoke(WarningCount, ErrorCount);
}
}
/// <summary>
/// Recalculates WarningCount, ErrorCount, and IsModified from all children.
/// This is useful after diagnostics have been updated for all children.
/// </summary>
internal void RecalculateTotals()
{
// First, recalculate all child folders recursively
foreach (var subFolder in Folders)
{
subFolder.RecalculateTotals();
}
// Then calculate totals from all children
var totalWarningCount = Files.Sum(f => f.WarningCount) + Folders.Sum(f => f.WarningCount);
var totalErrorCount = Files.Sum(f => f.ErrorCount) + Folders.Sum(f => f.ErrorCount);
var isModified = Files.Any(f => f.IsModified) || Folders.Any(f => f.IsModified);
// Update properties if changed
var warningChanged = WarningCount != totalWarningCount;
var errorChanged = ErrorCount != totalErrorCount;
var modifiedChanged = IsModified != isModified;
if (warningChanged || errorChanged || modifiedChanged)
{
WarningCount = totalWarningCount;
ErrorCount = totalErrorCount;
IsModified = isModified;
if (warningChanged || errorChanged)
{
DiagnosticsChanged?.Invoke(WarningCount, ErrorCount);
}
if (modifiedChanged)
{
Modified?.Invoke();
}
}
}
public void Dispose()
{
foreach (var file in WorkspaceFiles)
{
file.Dispose();
}
WorkspaceFiles.Clear();
foreach (var folder in Folders)
{
folder.Dispose();
}
WorkspaceFolders.Clear();
GC.SuppressFinalize(this);
}
}