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; /// Gemeinsame Hilfen für klickdurchlässige Overlay-Fenster. 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; } /// Wie die Boxen: Besitzer = Desktop, immer ganz unten (über den Desktop-Icons, unter allen Fenstern). 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(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(); } /// Rechteck in Bildschirm-Pixeln. 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 // ========================================================================= /// /// 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. /// public static class DesktopTint { private static Window? _w; private static Canvas? _canvas; private static string _signature = ""; private static AppSettings S => AppSettings.Current; /// Schicht neu aufbauen, wenn sich Icons, Boxen oder Einstellungen geändert haben. 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(); } }