Fortschritt, .NET-Prüfung, Updates, Fenster merken, Box-Funktionen

- Große Kopien laufen im Hintergrund mit Fortschritt und Abbrechen;
  abgebrochene Kopien werden aufgeräumt, das Original bleibt unverändert
- Sichern/Wiederherstellen mit Fortschritt, keine halben ZIP-Dateien
- Installer auf .NET Framework 4.8: erkennt fehlende .NET 9 Desktop Runtime
  und installiert sie auf Wunsch (Microsoft-Signatur wird geprüft)
- Update-Hinweis über Gitea-Releases, Update mit automatischem Neustart
- Fenstergröße und -position werden gemerkt
- Desktop-Boxen: umbenennen, löschen, verschieben, herausziehen
- Fenster und Boxen kehren auf sichtbare Bildschirme zurück
- README ergänzt

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-01 20:46:48 +02:00
co-authored by Claude Opus 5.5
parent 77bb3e6b9d
commit 1ae6e1abc2
19 changed files with 1098 additions and 95 deletions
+78 -10
View File
@@ -51,15 +51,34 @@ public partial class FenceWindow : Window
{
_applying = true;
var m = Meta;
var wa = SystemParameters.WorkArea;
Width = Math.Max(MinWidth, m.FenceW);
Height = m.FenceCollapsed ? CollapsedHeight : Math.Max(120, m.FenceH);
Left = Math.Clamp(m.FenceX ?? wa.Left + 24, wa.Left - Width + 80, wa.Right - 80);
Top = Math.Clamp(m.FenceY ?? wa.Top + 24, wa.Top, wa.Bottom - 40);
var wa = Screens.PrimaryWorkArea;
double left = m.FenceX ?? wa.Right - Width - 24, top = m.FenceY ?? wa.Top + 24;
// Bildschirm abgesteckt? → zurück auf den Hauptbildschirm (oben rechts)
if (!Screens.IsReachable(new Rect(left, top, Width, Height)))
{
left = wa.Right - Width - 24;
top = wa.Top + 24;
m.FenceX = left;
m.FenceY = top;
}
Left = left;
Top = top;
Body.Visibility = m.FenceCollapsed ? Visibility.Collapsed : Visibility.Visible;
_applying = false;
}
/// <summary>Nach Bildschirm-Änderungen: Box wieder sichtbar machen.</summary>
public void EnsureOnScreen()
{
if (Screens.IsReachable(new Rect(Left, Top, ActualWidth, ActualHeight))) return;
Meta.FenceX = null;
Meta.FenceY = null;
ApplyBounds();
AppSettings.Current.Save();
}
private void RememberBounds()
{
if (_applying || !IsLoaded) return;
@@ -114,20 +133,40 @@ public partial class FenceWindow : Window
private void Items_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter) Selected.ForEach(i => Open(i.Path));
var sel = Selected;
if (sel.Count == 0 || App.Win is not { } win) return;
switch (e.Key)
{
case Key.Enter: sel.ForEach(i => Open(i.Path)); e.Handled = true; break;
case Key.Delete: win.DeleteIcons(sel); e.Handled = true; break;
case Key.F2 when sel.Count == 1: win.RenameIcon(sel[0]); e.Handled = true; break;
}
}
private void Items_ContextMenuOpening(object sender, ContextMenuEventArgs e)
{
var sel = Selected;
if (sel.Count == 0) { e.Handled = true; return; }
if (sel.Count == 0 || App.Win is not { } win) { e.Handled = true; return; }
ItemMenu.Items.Clear();
ItemMenu.Items.Add(MainWindow.MenuItem("Öffnen", "", () => sel.ForEach(i => Open(i.Path)), bold: true));
ItemMenu.Items.Add(MainWindow.MenuItem("Öffnen", "\uE8A7", () => sel.ForEach(i => Open(i.Path)), bold: true, gesture: "Enter"));
if (sel.Count == 1)
ItemMenu.Items.Add(MainWindow.MenuItem("Dateispeicherort öffnen", "", () =>
{
ItemMenu.Items.Add(MainWindow.MenuItem("Dateispeicherort öffnen", "\uED25", () =>
Process.Start("explorer.exe", $"/select,\"{sel[0].Path}\"")));
ItemMenu.Items.Add(MainWindow.MenuItem("Umbenennen", "\uE70F", () => win.RenameIcon(sel[0]), gesture: "F2"));
}
ItemMenu.Items.Add(new Separator());
ItemMenu.Items.Add(MainWindow.MenuItem("In DeskCore zeigen", "", ShowInApp));
var moveTo = MainWindow.MenuItem("Verschieben nach", "\uE8DE", null);
foreach (var c in Storage.Categories().Where(c => !string.Equals(c, Category, StringComparison.OrdinalIgnoreCase)))
moveTo.Items.Add(MainWindow.MenuItem(c, AppSettings.Current.Categories.GetValueOrDefault(c)?.Glyph ?? "\uF12B",
() => win.TransferAll(sel.Select(i => i.Path), Storage.CategoryPath(c))));
if (moveTo.Items.Count > 0) ItemMenu.Items.Add(moveTo);
ItemMenu.Items.Add(MainWindow.MenuItem("Zurück auf den Desktop legen", "\uE7F4",
() => win.TransferAll(sel.Select(i => i.Path), Storage.UserDesktop)));
ItemMenu.Items.Add(MainWindow.MenuItem("Löschen", "\uE74D", () => win.DeleteIcons(sel), gesture: "Entf"));
ItemMenu.Items.Add(new Separator());
ItemMenu.Items.Add(MainWindow.MenuItem("In DeskCore zeigen", "\uE8A7", ShowInApp));
}
private void MenuButton_Click(object sender, RoutedEventArgs e)
@@ -149,9 +188,33 @@ public partial class FenceWindow : Window
private void ShowInApp() => App.Win?.ShowCategory(Category);
private Point _dragStart;
private bool _dragging;
private void Items_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) => _dragStart = e.GetPosition(null);
private void Items_PreviewMouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton != MouseButtonState.Pressed || _dragging) return;
var d = e.GetPosition(null) - _dragStart;
if (Math.Abs(d.X) < SystemParameters.MinimumHorizontalDragDistance &&
Math.Abs(d.Y) < SystemParameters.MinimumVerticalDragDistance) return;
if (MainWindow.FindParent<ListBoxItem>(e.OriginalSource as DependencyObject) is null) return;
var paths = Selected.Select(i => i.Path).ToArray();
if (paths.Length == 0) return;
_dragging = true;
try
{
// nur "Kopieren" erlauben: der Explorer soll die Datei nicht aus der Kategorie entfernen.
// In eine andere Box/Kategorie gezogen, verschiebt DeskCore selbst.
DragDrop.DoDragDrop(Items, new DataObject(DataFormats.FileDrop, paths), DragDropEffects.Copy);
}
finally { _dragging = false; }
}
private void Items_DragOver(object sender, DragEventArgs e)
{
e.Effects = e.Data.GetDataPresent(DataFormats.FileDrop)
e.Effects = e.Data.GetDataPresent(DataFormats.FileDrop) && !_dragging
? (e.AllowedEffects.HasFlag(DragDropEffects.Copy) ? DragDropEffects.Copy : DragDropEffects.Move)
: DragDropEffects.None;
e.Handled = true;
@@ -159,7 +222,7 @@ public partial class FenceWindow : Window
private void Items_Drop(object sender, DragEventArgs e)
{
if (e.Data.GetData(DataFormats.FileDrop) is string[] files)
if (!_dragging && e.Data.GetData(DataFormats.FileDrop) is string[] files)
App.Win?.TransferAll(files, Storage.CategoryPath(Category));
}
@@ -265,6 +328,11 @@ public static class FenceManager
foreach (var f in _open) f.Refresh();
}
public static void EnsureOnScreen()
{
foreach (var f in _open) f.EnsureOnScreen();
}
public static void Renamed(string oldName, string newName)
{
_open.FirstOrDefault(x => string.Equals(x.Category, oldName, StringComparison.OrdinalIgnoreCase))?.Rename(newName);