123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465 |
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Runtime.InteropServices;
- using System.Windows;
- using System.Windows.Controls;
- using System.Windows.Interop;
- using System.Windows.Media;
- using System.Windows.Threading;
- using HandyControl.Data;
- using HandyControl.Expression.Drawing;
- using HandyControl.Tools;
- using HandyControl.Tools.Interop;
- namespace HandyControl.Controls;
- public class GlowWindow : Window
- {
- internal int DeferGlowChangesCount;
- private readonly GlowEdge[] _glowEdges = new GlowEdge[4];
- private DispatcherTimer _makeGlowVisibleTimer;
- private bool _isGlowVisible;
- private bool _useLogicalSizeForRestore;
- private bool _updatingZOrder;
- private Rect _logicalSizeForRestore = Rect.Empty;
- public static readonly DependencyProperty ActiveGlowColorProperty = DependencyProperty.Register(
- nameof(ActiveGlowColor), typeof(Color), typeof(GlowWindow), new PropertyMetadata(default(Color), OnGlowColorChanged));
- public Color ActiveGlowColor
- {
- get => (Color) GetValue(ActiveGlowColorProperty);
- set => SetValue(ActiveGlowColorProperty, value);
- }
- public static readonly DependencyProperty InactiveGlowColorProperty = DependencyProperty.Register(
- nameof(InactiveGlowColor), typeof(Color), typeof(GlowWindow), new PropertyMetadata(default(Color), OnGlowColorChanged));
- public Color InactiveGlowColor
- {
- get => (Color) GetValue(InactiveGlowColorProperty);
- set => SetValue(InactiveGlowColorProperty, value);
- }
- #region internal
- internal void EndDeferGlowChanges()
- {
- foreach (var current in LoadedGlowWindows) current.CommitChanges();
- }
- #endregion
- #region protected
- protected virtual bool ShouldShowGlow
- {
- get
- {
- var handle = this.GetHandle();
- return InteropMethods.IsWindowVisible(handle) && !InteropMethods.IsIconic(handle) &&
- !InteropMethods.IsZoomed(handle) && ResizeMode != ResizeMode.NoResize;
- }
- }
- private IntPtr HwndSourceHook(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
- {
- if (msg <= InteropValues.WM_WINDOWPOSCHANGED)
- {
- if (msg == InteropValues.WM_ACTIVATE)
- {
- return IntPtr.Zero;
- }
- if (msg != InteropValues.WM_QUIT)
- {
- switch (msg)
- {
- case InteropValues.WM_WINDOWPOSCHANGING:
- WmWindowPosChanging(lParam);
- return IntPtr.Zero;
- case InteropValues.WM_WINDOWPOSCHANGED:
- WmWindowPosChanged(lParam);
- return IntPtr.Zero;
- default:
- return IntPtr.Zero;
- }
- }
- }
- else
- {
- if (msg <= InteropValues.WM_NCRBUTTONDBLCLK)
- {
- switch (msg)
- {
- case InteropValues.WM_SETICON:
- break;
- case InteropValues.WM_NCCREATE:
- case InteropValues.WM_NCDESTROY:
- return IntPtr.Zero;
- case InteropValues.WM_NCACTIVATE:
- handled = true;
- return WmNcActivate(hWnd, wParam);
- default:
- switch (msg)
- {
- case InteropValues.WM_NCRBUTTONDOWN:
- case InteropValues.WM_NCRBUTTONUP:
- case InteropValues.WM_NCRBUTTONDBLCLK:
- handled = true;
- return IntPtr.Zero;
- default:
- return IntPtr.Zero;
- }
- }
- }
- else
- {
- switch (msg)
- {
- case InteropValues.WM_NCUAHDRAWCAPTION:
- case InteropValues.WM_NCUAHDRAWFRAME:
- handled = true;
- return IntPtr.Zero;
- default:
- if (msg != InteropValues.WM_SYSCOMMAND) return IntPtr.Zero;
- WmSysCommand(hWnd, wParam);
- return IntPtr.Zero;
- }
- }
- }
- handled = true;
- return CallDefWindowProcWithoutVisibleStyle(hWnd, msg, wParam, lParam);
- }
- protected override void OnActivated(EventArgs e)
- {
- UpdateGlowActiveState();
- base.OnActivated(e);
- }
- protected override void OnDeactivated(EventArgs e)
- {
- UpdateGlowActiveState();
- base.OnDeactivated(e);
- }
- protected override void OnClosed(EventArgs e)
- {
- StopTimer();
- DestroyGlowWindows();
- base.OnClosed(e);
- }
- protected override void OnSourceInitialized(EventArgs e)
- {
- base.OnSourceInitialized(e);
- var hwndSource = this.GetHwndSource();
- if (hwndSource != null)
- {
- hwndSource.AddHook(HwndSourceHook);
- CreateGlowWindowHandles();
- }
- }
- #endregion
- #region private
- private IEnumerable<GlowEdge> LoadedGlowWindows => from w in _glowEdges where w != null select w;
- private bool IsGlowVisible
- {
- get => _isGlowVisible;
- set
- {
- if (_isGlowVisible != value)
- {
- _isGlowVisible = value;
- for (var i = 0; i < _glowEdges.Length; i++)
- {
- GetOrCreateGlowWindow(i).IsVisible = value;
- }
- }
- }
- }
- private GlowEdge GetOrCreateGlowWindow(int direction)
- {
- return _glowEdges[direction] ?? (_glowEdges[direction] = new GlowEdge(this, (Dock) direction)
- {
- ActiveGlowColor = ActiveGlowColor,
- InactiveGlowColor = InactiveGlowColor,
- IsActive = IsActive
- });
- }
- private static void OnResizeModeChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
- {
- var customChromeWindow = (GlowWindow) obj;
- customChromeWindow.UpdateGlowVisibility(false);
- }
- private static void OnGlowColorChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) => ((GlowWindow) obj).UpdateGlowColors();
- private void UpdateGlowColors()
- {
- using (DeferGlowChanges())
- {
- foreach (var current in LoadedGlowWindows)
- {
- current.ActiveGlowColor = ActiveGlowColor;
- current.InactiveGlowColor = InactiveGlowColor;
- }
- }
- }
- private void UpdateGlowVisibility(bool delayIfNecessary)
- {
- var shouldShowGlow = ShouldShowGlow;
- if (shouldShowGlow != IsGlowVisible)
- {
- if (SystemParameters.MinimizeAnimation && shouldShowGlow && delayIfNecessary)
- {
- if (_makeGlowVisibleTimer != null)
- {
- _makeGlowVisibleTimer.Stop();
- }
- else
- {
- _makeGlowVisibleTimer = new DispatcherTimer
- {
- Interval = TimeSpan.FromMilliseconds(200.0)
- };
- _makeGlowVisibleTimer.Tick += OnDelayedVisibilityTimerTick;
- }
- _makeGlowVisibleTimer.Start();
- return;
- }
- StopTimer();
- IsGlowVisible = shouldShowGlow;
- }
- }
- private void StopTimer()
- {
- if (_makeGlowVisibleTimer != null)
- {
- _makeGlowVisibleTimer.Stop();
- _makeGlowVisibleTimer.Tick -= OnDelayedVisibilityTimerTick;
- _makeGlowVisibleTimer = null;
- }
- }
- private void OnDelayedVisibilityTimerTick(object sender, EventArgs e)
- {
- StopTimer();
- UpdateGlowWindowPositions(false);
- }
- private void UpdateGlowWindowPositions(bool delayIfNecessary)
- {
- using (DeferGlowChanges())
- {
- UpdateGlowVisibility(delayIfNecessary);
- foreach (var current in LoadedGlowWindows) current.UpdateWindowPos();
- }
- }
- private IDisposable DeferGlowChanges() => new ChangeScope(this);
- private void UpdateGlowActiveState()
- {
- using (DeferGlowChanges())
- {
- foreach (var current in LoadedGlowWindows)
- {
- current.IsActive = IsActive;
- }
- }
- }
- private void DestroyGlowWindows()
- {
- for (var i = 0; i < _glowEdges.Length; i++)
- {
- using (_glowEdges[i])
- {
- _glowEdges[i] = null;
- }
- }
- }
- private void WmWindowPosChanging(IntPtr lParam)
- {
- var windowpos = (InteropValues.WINDOWPOS) Marshal.PtrToStructure(lParam, typeof(InteropValues.WINDOWPOS));
- if ((windowpos.flags & 2u) == 0u && (windowpos.flags & 1u) == 0u && windowpos.cx > 0 && windowpos.cy > 0)
- {
- var rect = new Rect(windowpos.x, windowpos.y, windowpos.cx, windowpos.cy);
- rect = rect.DeviceToLogicalUnits();
- if (_useLogicalSizeForRestore)
- {
- rect = _logicalSizeForRestore;
- _logicalSizeForRestore = Rect.Empty;
- _useLogicalSizeForRestore = false;
- }
- var logicalRect = GetOnScreenPosition(rect);
- logicalRect = logicalRect.LogicalToDeviceUnits();
- windowpos.x = (int) logicalRect.X;
- windowpos.y = (int) logicalRect.Y;
- Marshal.StructureToPtr(windowpos, lParam, true);
- }
- }
- private void UpdateZOrderOfOwner(IntPtr hwndOwner)
- {
- var lastOwnedWindow = IntPtr.Zero;
- InteropMethods.EnumThreadWindows(InteropMethods.GetCurrentThreadId(), delegate (IntPtr hwnd, IntPtr lParam)
- {
- if (InteropMethods.GetWindow(hwnd, 4) == hwndOwner) lastOwnedWindow = hwnd;
- return true;
- }, IntPtr.Zero);
- if (lastOwnedWindow != IntPtr.Zero && InteropMethods.GetWindow(hwndOwner, 3) != lastOwnedWindow)
- InteropMethods.SetWindowPos(hwndOwner, lastOwnedWindow, 0, 0, 0, 0, 19);
- }
- private void UpdateZOrderOfThisAndOwner()
- {
- if (_updatingZOrder) return;
- try
- {
- _updatingZOrder = true;
- var windowInteropHelper = new WindowInteropHelper(this);
- var handle = windowInteropHelper.Handle;
- foreach (var current in LoadedGlowWindows)
- {
- var window = InteropMethods.GetWindow(current.Handle, 3);
- if (window != handle) InteropMethods.SetWindowPos(current.Handle, handle, 0, 0, 0, 0, 19);
- handle = current.Handle;
- }
- var owner = windowInteropHelper.Owner;
- if (owner != IntPtr.Zero) UpdateZOrderOfOwner(owner);
- }
- finally
- {
- _updatingZOrder = false;
- }
- }
- private void WmWindowPosChanged(IntPtr lParam)
- {
- try
- {
- var windowpos = (InteropValues.WINDOWPOS) Marshal.PtrToStructure(lParam, typeof(InteropValues.WINDOWPOS));
- UpdateGlowWindowPositions((windowpos.flags & 64u) == 0u);
- UpdateZOrderOfThisAndOwner();
- }
- catch
- {
- // ignored
- }
- }
- private Rect GetOnScreenPosition(Rect floatRect)
- {
- var result = floatRect;
- floatRect = floatRect.LogicalToDeviceUnits();
- ScreenHelper.FindMaximumSingleMonitorRectangle(floatRect, out _, out var rect2);
- if (!floatRect.IntersectsWith(rect2))
- {
- ScreenHelper.FindMonitorRectsFromPoint(InteropMethods.GetCursorPos(), out _, out rect2);
- rect2 = rect2.DeviceToLogicalUnits();
- if (result.Width > rect2.Width) result.Width = rect2.Width;
- if (result.Height > rect2.Height) result.Height = rect2.Height;
- if (rect2.Right <= result.X) result.X = rect2.Right - result.Width;
- if (rect2.Left > result.X + result.Width) result.X = rect2.Left;
- if (rect2.Bottom <= result.Y) result.Y = rect2.Bottom - result.Height;
- if (rect2.Top > result.Y + result.Height) result.Y = rect2.Top;
- }
- return result;
- }
- private static InteropValues.MONITORINFO MonitorInfoFromWindow(IntPtr hWnd)
- {
- var hMonitor = InteropMethods.MonitorFromWindow(hWnd, 2);
- var result = default(InteropValues.MONITORINFO);
- result.cbSize = (uint) Marshal.SizeOf(typeof(InteropValues.MONITORINFO));
- InteropMethods.GetMonitorInfo(hMonitor, ref result);
- return result;
- }
- private IntPtr WmNcActivate(IntPtr hWnd, IntPtr wParam) => InteropMethods.DefWindowProc(hWnd, InteropValues.WM_NCACTIVATE, wParam, InteropMethods.HRGN_NONE);
- private bool IsAeroSnappedToMonitor(IntPtr hWnd)
- {
- var monitorinfo = MonitorInfoFromWindow(hWnd);
- var logicalRect = new Rect(Left, Top, Width, Height);
- logicalRect = logicalRect.LogicalToDeviceUnits();
- return MathHelper.AreClose(monitorinfo.rcWork.Height, logicalRect.Height) && MathHelper.AreClose(monitorinfo.rcWork.Top, logicalRect.Top);
- }
- private void WmSysCommand(IntPtr hWnd, IntPtr wParam)
- {
- var num = InteropMethods.GET_SC_WPARAM(wParam);
- if (num == InteropValues.SC_MOVE)
- {
- InteropMethods.RedrawWindow(hWnd, IntPtr.Zero, IntPtr.Zero,
- InteropValues.RedrawWindowFlags.Invalidate | InteropValues.RedrawWindowFlags.NoChildren |
- InteropValues.RedrawWindowFlags.UpdateNow | InteropValues.RedrawWindowFlags.Frame);
- }
- if ((num == InteropValues.SC_MAXIMIZE || num == InteropValues.SC_MINIMIZE || num == InteropValues.SC_MOVE ||
- num == InteropValues.SC_SIZE) && WindowState == WindowState.Normal && !IsAeroSnappedToMonitor(hWnd))
- {
- _logicalSizeForRestore = new Rect(Left, Top, Width, Height);
- }
- if (num == InteropValues.SC_MOVE && WindowState == WindowState.Maximized && _logicalSizeForRestore == Rect.Empty)
- {
- _logicalSizeForRestore = new Rect(Left, Top, Width, Height);
- }
- if (num == InteropValues.SC_RESTORE && WindowState != WindowState.Minimized &&
- _logicalSizeForRestore.Width > 0.0 && _logicalSizeForRestore.Height > 0.0)
- {
- Left = _logicalSizeForRestore.Left;
- Top = _logicalSizeForRestore.Top;
- Width = _logicalSizeForRestore.Width;
- Height = _logicalSizeForRestore.Height;
- _useLogicalSizeForRestore = true;
- }
- }
- private IntPtr CallDefWindowProcWithoutVisibleStyle(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam)
- {
- var flag = VisualHelper.ModifyStyle(hWnd, InteropValues.WS_VISIBLE, 0);
- var result = InteropMethods.DefWindowProc(hWnd, msg, wParam, lParam);
- if (flag) VisualHelper.ModifyStyle(hWnd, 0, InteropValues.WS_VISIBLE);
- return result;
- }
- private void CreateGlowWindowHandles()
- {
- for (var i = 0; i < _glowEdges.Length; i++) GetOrCreateGlowWindow(i).EnsureHandle();
- }
- #endregion
- }
|