Upload via GUI (186 Dateien)

This commit is contained in:
2026-10-04 14:48:53 +00:00
parent c0b552ef39
commit 8efd5bc497
25 changed files with 2344 additions and 1015 deletions
+50 -41
View File
@@ -73,14 +73,30 @@ public static class FenceManager
App.Win?.RefreshFromFences();
}
/// <summary>Kategorie aus allen Boxen (außer except) entfernen – eine Kategorie soll nur in einer Box stehen.</summary>
public static void RemoveCategoryTab(string category, FenceDef? except)
/// <summary>
/// Kategorie aus den anderen Boxen derselben Seite entfernen – pro Seite steht eine Kategorie nur in einer Box.
/// Auf anderen Seiten darf sie zusätzlich liegen (z. B. „Internet“ auf „Arbeit“ und auf „Freizeit“).
/// </summary>
public static void RemoveCategoryTab(string category, FenceDef? except, int page)
{
foreach (var f in S.Fences.Where(f => f != except))
f.Tabs.RemoveAll(t => !t.IsFolder && string.Equals(t.Value, category, StringComparison.OrdinalIgnoreCase));
foreach (var f in S.Fences.Where(f => f != except && f.Page == page))
f.Tabs.RemoveAll(t => t.IsCategory && string.Equals(t.Value, category, StringComparison.OrdinalIgnoreCase));
S.Fences.RemoveAll(f => f != except && f.Tabs.Count == 0);
}
/// <summary>Nach Verschieben/Zusammenlegen: doppelte Kategorien auf einer Seite auflösen – „keep“ behält seine.</summary>
private static void ResolveDuplicates(int page, FenceDef? keep)
{
if (keep is not null)
foreach (var t in keep.Tabs.Where(t => t.IsCategory).ToList())
RemoveCategoryTab(t.Value, keep, page);
var seen = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
foreach (var f in S.Fences.Where(f => f.Page == page))
f.Tabs.RemoveAll(t => t.IsCategory && !seen.Add(t.Value));
S.Fences.RemoveAll(f => f.Tabs.Count == 0);
foreach (var f in S.Fences) f.ActiveTab = Math.Clamp(f.ActiveTab, 0, Math.Max(0, f.Tabs.Count - 1));
}
/// <summary>Tab als eigene Box daneben lösen.</summary>
public static void DetachTab(FenceDef def, int index)
{
@@ -117,18 +133,12 @@ public static class FenceManager
public static List<Rect> OpenRects() =>
_open.Where(w => w.IsVisible).Select(w => new Rect(w.Left, w.Top, w.ActualWidth, w.ActualHeight)).ToList();
/// <summary>Nach dem Aufziehen: Menü "Neue Box hier" an der Mausposition.</summary>
/// <summary>Nach dem Aufziehen: Auswahl "Neue Box hier" an der Mausposition.</summary>
public static void ShowCreateMenu(Rect area)
{
var menu = new ContextMenu { Placement = System.Windows.Controls.Primitives.PlacementMode.MousePoint };
var header = MainWindow.MenuItem("Neue Box hier für …", "\uE8A1", null, bold: true);
header.IsEnabled = false;
menu.Items.Add(header);
menu.Items.Add(new Separator());
void Create(FenceTab tab)
BoxPicker.Show(area, tab =>
{
if (!tab.IsFolder) RemoveCategoryTab(tab.Value, except: null);
if (tab.IsCategory) RemoveCategoryTab(tab.Value, except: null, S.CurrentPage);
S.Fences.Add(new FenceDef
{
Page = S.CurrentPage, Tabs = [tab],
@@ -138,35 +148,9 @@ public static class FenceManager
S.Save();
Sync();
App.Win?.RefreshFromFences();
}
foreach (var c in Storage.Categories())
{
var label = S.HasFence(c) ? $"{c} (aus anderer Box hierher holen)" : c;
menu.Items.Add(MainWindow.MenuItem(label, S.Categories.GetValueOrDefault(c)?.Glyph ?? "\uF12B",
() => Create(new FenceTab { Value = c })));
}
if (Storage.Categories().Count > 0) menu.Items.Add(new Separator());
menu.Items.Add(MainWindow.MenuItem("Neue Kategorie …", "\uE710", () =>
{
if (App.Win?.CreateCategoryInteractive() is { } name) Create(new FenceTab { Value = name });
}));
menu.Items.Add(MainWindow.MenuItem("Ordner-Portal …", "\uF12B", () =>
{
if (PickFolder() is { } folder) Create(new FenceTab { Kind = FenceTab.FolderKind, Value = folder });
}));
menu.Items.Add(new Separator());
menu.Items.Add(MainWindow.MenuItem("Abbrechen", "\uE711", () => { }));
// Damit das Menü bei Klick daneben zugeht, muss es im Vordergrund sein
menu.Opened += (_, _) =>
{
if (PresentationSource.FromVisual(menu) is System.Windows.Interop.HwndSource src) SetForegroundWindow(src.Handle);
};
menu.IsOpen = true;
});
}
[System.Runtime.InteropServices.DllImport("user32.dll")] private static extern bool SetForegroundWindow(IntPtr hwnd);
// =====================================================================
// Desktop-Seiten
// =====================================================================
@@ -186,6 +170,7 @@ public static class FenceManager
S.CurrentPage = index;
S.Save();
if (changed) Sync();
PageScheduler.ApplyWallpaper(index);
PageIndicator.Show(S.Pages[index], index, S.Pages.Count);
App.Win?.RefreshFromFences();
}
@@ -193,8 +178,24 @@ public static class FenceManager
public static void MoveToPage(FenceDef def, int page)
{
def.Page = Math.Clamp(page, 0, S.Pages.Count - 1);
ResolveDuplicates(def.Page, keep: def);
S.Save();
Sync();
App.Win?.RefreshFromFences();
}
/// <summary>Box mit gleichem Inhalt und Aussehen zusätzlich auf eine andere Seite legen.</summary>
public static void CopyToPage(FenceDef def, int page)
{
var copy = AppSettings.CloneFences([def])[0];
copy.Id = Guid.NewGuid().ToString("N");
copy.Page = Math.Clamp(page, 0, S.Pages.Count - 1);
S.Fences.Add(copy);
ResolveDuplicates(copy.Page, keep: copy);
S.Save();
Sync();
App.Win?.RefreshFromFences();
App.TrayIcon?.Notify("Box kopiert", $"Die Box liegt jetzt auch auf „{S.Pages[copy.Page]}“ – an derselben Stelle.");
}
/// <summary>Seite löschen: ihre Boxen wandern auf die Seite davor.</summary>
@@ -205,6 +206,8 @@ public static class FenceManager
foreach (var f in S.Fences)
if (f.Page == index) f.Page = Math.Max(0, index - 1);
else if (f.Page > index) f.Page--;
// Kategorien, die es auf beiden Seiten gab, nicht doppelt zeigen
ResolveDuplicates(Math.Max(0, index - 1), keep: null);
if (S.CurrentPage >= index && S.CurrentPage > 0) S.CurrentPage--;
S.Save();
CloseAll();
@@ -267,10 +270,16 @@ public static class FenceManager
v => def.IconSize = Enum.Parse<ViewMode>(v)),
});
panel.Children.Add(Label("Sortieren nach"));
panel.Children.Add(new TextBlock
{
Text = "„Eigene“: Icons in der Box einfach an die gewünschte Stelle ziehen.", FontSize = 12, Margin = new Thickness(0, -4, 0, 8),
TextWrapping = TextWrapping.Wrap, Foreground = (Brush)res["TextSecondaryBrush"],
});
panel.Children.Add(new Border
{
Style = (Style)res["SegmentGroup"], HorizontalAlignment = HorizontalAlignment.Left,
Child = Segments("sort", [("Name", "Name"), ("Typ", "Typ"), ("Datum", "Datum")], def.Sort, v => def.Sort = v),
Child = Segments("sort", [("Name", "Name"), ("Typ", "Typ"), ("Datum", "Datum"), ("Meistgenutzt", "Häufig"), ("Eigene", "Eigene")],
def.Sort, v => def.Sort = v),
});
// Icon-Tönung