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
+263 -261
View File
@@ -1,261 +1,263 @@
using System.IO;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Threading;
namespace DeskCore;
/// <summary>Gemeinsame Hilfen für klickdurchlässige Overlay-Fenster.</summary>
internal static class Overlay
{
public static Window Create(bool topmost)
{
var w = new Window
{
WindowStyle = WindowStyle.None, AllowsTransparency = true, Background = Brushes.Transparent,
ShowInTaskbar = false, ShowActivated = false, Topmost = topmost, ResizeMode = ResizeMode.NoResize,
Focusable = false, IsHitTestVisible = false,
};
w.SourceInitialized += (_, _) =>
{
var hwnd = new WindowInteropHelper(w).Handle;
long ex = GetWindowLongPtr(hwnd, GWL_EXSTYLE).ToInt64();
SetWindowLongPtr(hwnd, GWL_EXSTYLE, new IntPtr(ex | WS_EX_TRANSPARENT | WS_EX_LAYERED | WS_EX_TOOLWINDOW | WS_EX_NOACTIVATE));
};
return w;
}
/// <summary>Wie die Boxen: Besitzer = Desktop, immer ganz unten (über den Desktop-Icons, unter allen Fenstern).</summary>
public static void PinToDesktop(Window w)
{
var hwnd = new WindowInteropHelper(w).Handle;
var progman = FindWindow("Progman", null);
if (progman != IntPtr.Zero) SetWindowLongPtr(hwnd, GWLP_HWNDPARENT, progman);
HwndSource.FromHwnd(hwnd).AddHook((IntPtr h, int msg, IntPtr wp, IntPtr lp, ref bool handled) =>
{
if (msg == WM_WINDOWPOSCHANGING)
{
var pos = Marshal.PtrToStructure<WINDOWPOS>(lp);
pos.hwndInsertAfter = new IntPtr(1); // HWND_BOTTOM
pos.flags &= ~SWP_NOZORDER;
Marshal.StructureToPtr(pos, lp, false);
}
return IntPtr.Zero;
});
}
private const int GWL_EXSTYLE = -20, GWLP_HWNDPARENT = -8, WM_WINDOWPOSCHANGING = 0x46;
private const long WS_EX_TRANSPARENT = 0x20, WS_EX_LAYERED = 0x80000, WS_EX_TOOLWINDOW = 0x80, WS_EX_NOACTIVATE = 0x8000000;
private const uint SWP_NOZORDER = 0x4;
[StructLayout(LayoutKind.Sequential)]
private struct WINDOWPOS { public IntPtr hwnd, hwndInsertAfter; public int x, y, cx, cy; public uint flags; }
[DllImport("user32.dll", CharSet = CharSet.Unicode)] private static extern IntPtr FindWindow(string? cls, string? title);
[DllImport("user32.dll")] private static extern IntPtr GetWindowLongPtr(IntPtr h, int i);
[DllImport("user32.dll")] private static extern IntPtr SetWindowLongPtr(IntPtr h, int i, IntPtr v);
}
// =========================================================================
// Seitenanzeige beim Wechseln der Desktop-Seite
// =========================================================================
public static class PageIndicator
{
private static Window? _w;
private static TextBlock? _name;
private static StackPanel? _dots;
private static DispatcherTimer? _hide;
public static void Show(string name, int index, int count)
{
if (_w is null)
{
_w = Overlay.Create(topmost: true);
_w.SizeToContent = SizeToContent.WidthAndHeight;
_name = new TextBlock { FontSize = 15, FontWeight = FontWeights.SemiBold, HorizontalAlignment = HorizontalAlignment.Center };
_dots = new StackPanel { Orientation = Orientation.Horizontal, HorizontalAlignment = HorizontalAlignment.Center, Margin = new Thickness(0, 8, 0, 0) };
_w.Content = new Border
{
Padding = new Thickness(22, 12, 22, 12), CornerRadius = new CornerRadius(10), Margin = new Thickness(12),
Child = new StackPanel { Children = { _name, _dots } },
};
_hide = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(1300) };
_hide.Tick += (_, _) =>
{
_hide.Stop();
var fade = new DoubleAnimation(0, TimeSpan.FromMilliseconds(350));
fade.Completed += (_, _) => { if (_w.Opacity == 0) _w.Hide(); };
_w.BeginAnimation(UIElement.OpacityProperty, fade);
};
}
var res = Application.Current.Resources;
var border = (Border)_w.Content;
border.Background = (Brush)res["FlyoutBrush"];
border.BorderBrush = (Brush)res["FlyoutStrokeBrush"];
border.BorderThickness = new Thickness(1);
border.Effect = (System.Windows.Media.Effects.Effect)res["FlyoutShadow"];
_name!.Text = name;
_name.Foreground = (Brush)res["TextBrush"];
_dots!.Children.Clear();
for (int i = 0; i < count; i++)
_dots.Children.Add(new Ellipse
{
Width = 7, Height = 7, Margin = new Thickness(4, 0, 4, 0),
Fill = (Brush)res[i == index ? "AccentBrush" : "ControlStrongStrokeBrush"],
});
_w.BeginAnimation(UIElement.OpacityProperty, null);
_w.Opacity = 1;
_w.Show();
_w.UpdateLayout();
var wa = Screens.PrimaryWorkArea;
_w.Left = wa.Left + (wa.Width - _w.ActualWidth) / 2;
_w.Top = wa.Bottom - _w.ActualHeight - 40;
_hide!.Stop();
_hide.Start();
}
}
// =========================================================================
// Rahmen beim Aufziehen einer neuen Box (rechte Maustaste auf dem Desktop)
// =========================================================================
public static class DrawBoxOverlay
{
private static Window? _w;
public static void Begin()
{
if (_w is null)
{
_w = Overlay.Create(topmost: true);
var res = Application.Current.Resources;
var accent = ((SolidColorBrush)res["AccentBrush"]).Color;
_w.Content = new Grid
{
Children =
{
new Rectangle
{
RadiusX = 8, RadiusY = 8, StrokeThickness = 2, StrokeDashArray = [4, 3],
Stroke = new SolidColorBrush(accent), Fill = new SolidColorBrush(Color.FromArgb(0x38, accent.R, accent.G, accent.B)),
},
new TextBlock
{
Text = "Neue Box", Foreground = Brushes.White, FontSize = 14, FontWeight = FontWeights.SemiBold,
HorizontalAlignment = HorizontalAlignment.Center, VerticalAlignment = VerticalAlignment.Center,
Effect = new System.Windows.Media.Effects.DropShadowEffect { BlurRadius = 6, ShadowDepth = 0, Opacity = 0.8 },
},
},
};
}
_w.Width = _w.Height = 1;
_w.Show();
}
/// <summary>Rechteck in Bildschirm-Pixeln.</summary>
public static void Update(Rect px)
{
if (_w is null) return;
double s = Screens.Scale;
_w.Left = px.X / s;
_w.Top = px.Y / s;
_w.Width = Math.Max(1, px.Width / s);
_w.Height = Math.Max(1, px.Height / s);
}
public static void Finish(Rect px)
{
_w?.Hide();
double s = Screens.Scale;
var dip = new Rect(px.X / s, px.Y / s, px.Width / s, px.Height / s);
if (dip.Width < 120 || dip.Height < 60) return; // zu klein – war wohl nur ein Versehen
FenceManager.ShowCreateMenu(dip);
}
}
// =========================================================================
// Tönung der echten Desktop-Icons
// =========================================================================
/// <summary>
/// Legt eine klickdurchlässige Farbschicht exakt über die Bilder der Desktop-Icons. Die Positionen stammen aus der
/// offiziellen Shell-Schnittstelle; am Explorer selbst wird nichts verändert. Icons unter Boxen bleiben ausgespart.
/// </summary>
public static class DesktopTint
{
private static Window? _w;
private static Canvas? _canvas;
private static string _signature = "";
private static AppSettings S => AppSettings.Current;
/// <summary>Schicht neu aufbauen, wenn sich Icons, Boxen oder Einstellungen geändert haben.</summary>
public static void Refresh(bool force = false)
{
bool on = S.DesktopTint is not null && !FenceManager.Hidden && DesktopToggle.IconsVisible();
if (!on)
{
_w?.Hide();
_signature = "";
return;
}
var icons = DesktopIcons.Cached.Where(i => i.Path.Length > 0).ToList();
var fences = FenceManager.OpenRects();
var sig = $"{S.DesktopTint}|{S.DesktopTintStrength}|{DesktopIcons.IconTopOffset}|" +
string.Join(";", icons.Select(i => $"{i.X},{i.Y},{i.IconSize}")) + "|" + string.Join(";", fences.Select(r => r.ToString()));
if (!force && sig == _signature && _w?.IsVisible == true) return;
_signature = sig;
if (_w is null)
{
_w = Overlay.Create(topmost: false);
_canvas = new Canvas();
_w.Content = _canvas;
_w.SourceInitialized += (_, _) => Overlay.PinToDesktop(_w);
}
// Fenster über die gesamte Desktop-Fläche (alle Bildschirme)
double s = Screens.Scale;
var vs = new Rect(SystemParameters.VirtualScreenLeft, SystemParameters.VirtualScreenTop,
SystemParameters.VirtualScreenWidth, SystemParameters.VirtualScreenHeight);
_w.Left = vs.Left;
_w.Top = vs.Top;
_w.Width = vs.Width;
_w.Height = vs.Height;
var tint = CategoryColors.Brush(S.DesktopTint) ?? Brushes.Transparent;
_canvas!.Children.Clear();
foreach (var icon in icons)
{
var (l, t, size) = DesktopIcons.IconRect(icon);
var r = new Rect(l / s, t / s, size / s, size / s);
if (fences.Any(f => f.IntersectsWith(r))) continue; // liegt unter einer Box
var item = new IconItem { Path = icon.Path };
var rect = new Rectangle
{
Width = r.Width, Height = r.Height, Fill = tint, Opacity = Math.Clamp(S.DesktopTintStrength, 0.1, 0.95),
Visibility = Visibility.Hidden, // erst zeigen, wenn die Form (das Icon) geladen ist
};
void Apply()
{
if (item.Icon is null) return;
rect.OpacityMask = new ImageBrush(item.Icon) { Stretch = Stretch.Fill };
rect.Visibility = Visibility.Visible;
}
item.PropertyChanged += (_, _) => Apply();
Canvas.SetLeft(rect, r.X - vs.Left);
Canvas.SetTop(rect, r.Y - vs.Top);
_canvas.Children.Add(rect);
IconLoader.Load(item, size);
Apply();
}
if (!_w.IsVisible) _w.Show();
}
}
using System.IO;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Threading;
namespace DeskCore;
/// <summary>Gemeinsame Hilfen für klickdurchlässige Overlay-Fenster.</summary>
internal static class Overlay
{
public static Window Create(bool topmost)
{
var w = new Window
{
WindowStyle = WindowStyle.None, AllowsTransparency = true, Background = Brushes.Transparent,
ShowInTaskbar = false, ShowActivated = false, Topmost = topmost, ResizeMode = ResizeMode.NoResize,
Focusable = false, IsHitTestVisible = false,
};
w.SourceInitialized += (_, _) =>
{
var hwnd = new WindowInteropHelper(w).Handle;
long ex = GetWindowLongPtr(hwnd, GWL_EXSTYLE).ToInt64();
SetWindowLongPtr(hwnd, GWL_EXSTYLE, new IntPtr(ex | WS_EX_TRANSPARENT | WS_EX_LAYERED | WS_EX_TOOLWINDOW | WS_EX_NOACTIVATE));
};
return w;
}
/// <summary>Wie die Boxen: Besitzer = Desktop, immer ganz unten (über den Desktop-Icons, unter allen Fenstern).</summary>
public static void PinToDesktop(Window w)
{
var hwnd = new WindowInteropHelper(w).Handle;
var progman = FindWindow("Progman", null);
if (progman != IntPtr.Zero) SetWindowLongPtr(hwnd, GWLP_HWNDPARENT, progman);
HwndSource.FromHwnd(hwnd).AddHook((IntPtr h, int msg, IntPtr wp, IntPtr lp, ref bool handled) =>
{
if (msg == WM_WINDOWPOSCHANGING)
{
var pos = Marshal.PtrToStructure<WINDOWPOS>(lp);
pos.hwndInsertAfter = new IntPtr(1); // HWND_BOTTOM
pos.flags &= ~SWP_NOZORDER;
Marshal.StructureToPtr(pos, lp, false);
}
return IntPtr.Zero;
});
}
private const int GWL_EXSTYLE = -20, GWLP_HWNDPARENT = -8, WM_WINDOWPOSCHANGING = 0x46;
private const long WS_EX_TRANSPARENT = 0x20, WS_EX_LAYERED = 0x80000, WS_EX_TOOLWINDOW = 0x80, WS_EX_NOACTIVATE = 0x8000000;
private const uint SWP_NOZORDER = 0x4;
[StructLayout(LayoutKind.Sequential)]
private struct WINDOWPOS { public IntPtr hwnd, hwndInsertAfter; public int x, y, cx, cy; public uint flags; }
[DllImport("user32.dll", CharSet = CharSet.Unicode)] private static extern IntPtr FindWindow(string? cls, string? title);
[DllImport("user32.dll")] private static extern IntPtr GetWindowLongPtr(IntPtr h, int i);
[DllImport("user32.dll")] private static extern IntPtr SetWindowLongPtr(IntPtr h, int i, IntPtr v);
}
// =========================================================================
// Seitenanzeige beim Wechseln der Desktop-Seite
// =========================================================================
public static class PageIndicator
{
private static Window? _w;
private static TextBlock? _name;
private static StackPanel? _dots;
private static DispatcherTimer? _hide;
public static void Show(string name, int index, int count)
{
if (_w is null)
{
_w = Overlay.Create(topmost: true);
_w.SizeToContent = SizeToContent.WidthAndHeight;
_name = new TextBlock { FontSize = 15, FontWeight = FontWeights.SemiBold, HorizontalAlignment = HorizontalAlignment.Center };
_dots = new StackPanel { Orientation = Orientation.Horizontal, HorizontalAlignment = HorizontalAlignment.Center, Margin = new Thickness(0, 8, 0, 0) };
_w.Content = new Border
{
Padding = new Thickness(22, 12, 22, 12), CornerRadius = new CornerRadius(10), Margin = new Thickness(12),
Child = new StackPanel { Children = { _name, _dots } },
};
_hide = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(1300) };
_hide.Tick += (_, _) =>
{
_hide.Stop();
var fade = new DoubleAnimation(0, TimeSpan.FromMilliseconds(350));
fade.Completed += (_, _) => { if (_w.Opacity == 0) _w.Hide(); };
_w.BeginAnimation(UIElement.OpacityProperty, fade);
};
}
var res = Application.Current.Resources;
var border = (Border)_w.Content;
border.Background = (Brush)res["FlyoutBrush"];
border.BorderBrush = (Brush)res["FlyoutStrokeBrush"];
border.BorderThickness = new Thickness(1);
border.Effect = (System.Windows.Media.Effects.Effect)res["FlyoutShadow"];
_name!.Text = name;
_name.Foreground = (Brush)res["TextBrush"];
_dots!.Children.Clear();
for (int i = 0; i < count; i++)
_dots.Children.Add(new Ellipse
{
Width = 7, Height = 7, Margin = new Thickness(4, 0, 4, 0),
Fill = (Brush)res[i == index ? "AccentBrush" : "ControlStrongStrokeBrush"],
});
_w.BeginAnimation(UIElement.OpacityProperty, null);
_w.Opacity = 1;
_w.Show();
_w.UpdateLayout();
var wa = Screens.PrimaryWorkArea;
_w.Left = wa.Left + (wa.Width - _w.ActualWidth) / 2;
_w.Top = wa.Bottom - _w.ActualHeight - 40;
_hide!.Stop();
_hide.Start();
}
}
// =========================================================================
// Rahmen beim Aufziehen einer neuen Box (rechte Maustaste auf dem Desktop)
// =========================================================================
public static class DrawBoxOverlay
{
private static Window? _w;
public static void Begin()
{
if (_w is null)
{
_w = Overlay.Create(topmost: true);
var res = Application.Current.Resources;
var accent = ((SolidColorBrush)res["AccentBrush"]).Color;
_w.Content = new Grid
{
Children =
{
new Rectangle
{
RadiusX = 8, RadiusY = 8, StrokeThickness = 2, StrokeDashArray = [4, 3],
Stroke = new SolidColorBrush(accent), Fill = new SolidColorBrush(Color.FromArgb(0x38, accent.R, accent.G, accent.B)),
},
new TextBlock
{
Text = "Neue Box", Foreground = Brushes.White, FontSize = 14, FontWeight = FontWeights.SemiBold,
HorizontalAlignment = HorizontalAlignment.Center, VerticalAlignment = VerticalAlignment.Center,
Effect = new System.Windows.Media.Effects.DropShadowEffect { BlurRadius = 6, ShadowDepth = 0, Opacity = 0.8 },
},
},
};
}
_w.Width = _w.Height = 1;
_w.Show();
}
/// <summary>Rechteck in Bildschirm-Pixeln.</summary>
public static void Update(Rect px)
{
if (_w is null) return;
double s = Screens.Scale;
_w.Left = px.X / s;
_w.Top = px.Y / s;
_w.Width = Math.Max(1, px.Width / s);
_w.Height = Math.Max(1, px.Height / s);
}
public static void Finish(Rect px)
{
double s = Screens.Scale;
var dip = new Rect(px.X / s, px.Y / s, px.Width / s, px.Height / s);
if (dip.Width < 120 || dip.Height < 60) { Hide(); return; } // zu klein – war wohl nur ein Versehen
Update(px); // Rahmen bleibt stehen, bis etwas ausgewählt ist
FenceManager.ShowCreateMenu(dip);
}
public static void Hide() => _w?.Hide();
}
// =========================================================================
// Tönung der echten Desktop-Icons
// =========================================================================
/// <summary>
/// Legt eine klickdurchlässige Farbschicht exakt über die Bilder der Desktop-Icons. Die Positionen stammen aus der
/// offiziellen Shell-Schnittstelle; am Explorer selbst wird nichts verändert. Icons unter Boxen bleiben ausgespart.
/// </summary>
public static class DesktopTint
{
private static Window? _w;
private static Canvas? _canvas;
private static string _signature = "";
private static AppSettings S => AppSettings.Current;
/// <summary>Schicht neu aufbauen, wenn sich Icons, Boxen oder Einstellungen geändert haben.</summary>
public static void Refresh(bool force = false)
{
bool on = S.DesktopTint is not null && !FenceManager.Hidden && DesktopToggle.IconsVisible();
if (!on)
{
_w?.Hide();
_signature = "";
return;
}
var icons = DesktopIcons.Cached.Where(i => i.Path.Length > 0).ToList();
var fences = FenceManager.OpenRects();
var sig = $"{S.DesktopTint}|{S.DesktopTintStrength}|{DesktopIcons.IconTopOffset}|" +
string.Join(";", icons.Select(i => $"{i.X},{i.Y},{i.IconSize}")) + "|" + string.Join(";", fences.Select(r => r.ToString()));
if (!force && sig == _signature && _w?.IsVisible == true) return;
_signature = sig;
if (_w is null)
{
_w = Overlay.Create(topmost: false);
_canvas = new Canvas();
_w.Content = _canvas;
_w.SourceInitialized += (_, _) => Overlay.PinToDesktop(_w);
}
// Fenster über die gesamte Desktop-Fläche (alle Bildschirme)
double s = Screens.Scale;
var vs = new Rect(SystemParameters.VirtualScreenLeft, SystemParameters.VirtualScreenTop,
SystemParameters.VirtualScreenWidth, SystemParameters.VirtualScreenHeight);
_w.Left = vs.Left;
_w.Top = vs.Top;
_w.Width = vs.Width;
_w.Height = vs.Height;
var tint = CategoryColors.Brush(S.DesktopTint) ?? Brushes.Transparent;
_canvas!.Children.Clear();
foreach (var icon in icons)
{
var (l, t, size) = DesktopIcons.IconRect(icon);
var r = new Rect(l / s, t / s, size / s, size / s);
if (fences.Any(f => f.IntersectsWith(r))) continue; // liegt unter einer Box
var item = new IconItem { Path = icon.Path };
var rect = new Rectangle
{
Width = r.Width, Height = r.Height, Fill = tint, Opacity = Math.Clamp(S.DesktopTintStrength, 0.1, 0.95),
Visibility = Visibility.Hidden, // erst zeigen, wenn die Form (das Icon) geladen ist
};
void Apply()
{
if (item.Icon is null) return;
rect.OpacityMask = new ImageBrush(item.Icon) { Stretch = Stretch.Fill };
rect.Visibility = Visibility.Visible;
}
item.PropertyChanged += (_, _) => Apply();
Canvas.SetLeft(rect, r.X - vs.Left);
Canvas.SetTop(rect, r.Y - vs.Top);
_canvas.Children.Add(rect);
IconLoader.Load(item, size);
Apply();
}
if (!_w.IsVisible) _w.Show();
}
}