DeskCore 1.0.0
Desktop-Icons in eigene Kategorien sortieren: Windows-11-Design (hell/dunkel), Desktop-Boxen, automatisches Einsortieren nach Regeln, Rückgängig, Suche, Infobereich mit Tastenkürzel, Sicherung als ZIP und Installer ohne Adminrechte. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+105
@@ -0,0 +1,105 @@
|
||||
using System.IO;
|
||||
|
||||
namespace DeskCore;
|
||||
|
||||
/// <summary>Eine rückgängig machbare Aktion, bestehend aus mehreren Einzelschritten.</summary>
|
||||
public class UndoEntry(string description)
|
||||
{
|
||||
public string Description { get; } = description;
|
||||
private readonly List<Func<string?>> _steps = [];
|
||||
|
||||
/// <summary>Elemente, die beim Löschen zwischengeparkt wurden – gehen später in den Papierkorb.</summary>
|
||||
public List<string> HeldPaths { get; } = [];
|
||||
|
||||
public bool IsEmpty => _steps.Count == 0;
|
||||
|
||||
/// <summary>Schritt zum Rückgängigmachen; gibt eine Fehlermeldung oder null zurück.</summary>
|
||||
public void Add(Func<string?> undo) => _steps.Add(undo);
|
||||
|
||||
public void AddMove(string from, string to) => Add(() =>
|
||||
{
|
||||
if (!File.Exists(to) && !Directory.Exists(to)) return $"„{Path.GetFileName(to)}“ gibt es nicht mehr.";
|
||||
var dir = Path.GetDirectoryName(from)!;
|
||||
Directory.CreateDirectory(dir);
|
||||
var back = File.Exists(from) || Directory.Exists(from) ? Storage.UniquePath(dir, Path.GetFileName(from)) : from;
|
||||
Storage.MoveAny(to, back);
|
||||
return null;
|
||||
});
|
||||
|
||||
public void AddCopy(string original, string copy) => Add(() =>
|
||||
{
|
||||
if (!File.Exists(copy) && !Directory.Exists(copy)) return null;
|
||||
// Original noch da → Kopie weg; sonst Kopie dorthin zurück, wo das Original lag
|
||||
if (File.Exists(original) || Directory.Exists(original)) Storage.Recycle(copy);
|
||||
else
|
||||
{
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(original)!);
|
||||
Storage.MoveAny(copy, original);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
public void AddDelete(string original, string held)
|
||||
{
|
||||
HeldPaths.Add(held);
|
||||
AddMove(original, held);
|
||||
}
|
||||
|
||||
public List<string> Run()
|
||||
{
|
||||
var errors = new List<string>();
|
||||
for (int i = _steps.Count - 1; i >= 0; i--)
|
||||
{
|
||||
try { if (_steps[i]() is { } err) errors.Add(err); }
|
||||
catch (Exception ex) { errors.Add(ex.Message); }
|
||||
}
|
||||
HeldPaths.Clear(); // wurden zurückgeholt
|
||||
return errors;
|
||||
}
|
||||
}
|
||||
|
||||
public class UndoManager
|
||||
{
|
||||
private const int Limit = 30;
|
||||
private readonly LinkedList<UndoEntry> _stack = new();
|
||||
|
||||
public event Action? Changed;
|
||||
|
||||
public bool CanUndo => _stack.Count > 0;
|
||||
public string? NextDescription => _stack.Last?.Value.Description;
|
||||
|
||||
public void Push(UndoEntry entry)
|
||||
{
|
||||
if (entry.IsEmpty) return;
|
||||
_stack.AddLast(entry);
|
||||
while (_stack.Count > Limit)
|
||||
{
|
||||
Flush(_stack.First!.Value);
|
||||
_stack.RemoveFirst();
|
||||
}
|
||||
Changed?.Invoke();
|
||||
}
|
||||
|
||||
public (string Description, List<string> Errors)? Undo()
|
||||
{
|
||||
if (_stack.Last is not { } last) return null;
|
||||
_stack.RemoveLast();
|
||||
var errors = last.Value.Run();
|
||||
Changed?.Invoke();
|
||||
return (last.Value.Description, errors);
|
||||
}
|
||||
|
||||
/// <summary>Beim Beenden: alles Zwischengeparkte in den Papierkorb.</summary>
|
||||
public void FlushAll()
|
||||
{
|
||||
foreach (var e in _stack) Flush(e);
|
||||
_stack.Clear();
|
||||
Storage.FlushHolding();
|
||||
}
|
||||
|
||||
private static void Flush(UndoEntry e)
|
||||
{
|
||||
foreach (var p in e.HeldPaths) Storage.Recycle(p);
|
||||
e.HeldPaths.Clear();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user