From 20deb14cbca1477c69e27100706c24b036306bca Mon Sep 17 00:00:00 2001 From: awaescher Date: Sun, 3 Dec 2023 12:36:07 +0100 Subject: [PATCH 1/8] Undo window hiding on app quit --- StageManager/Native/WindowsManager.cs | 92 +++++++++++++++++++++------ StageManager/SceneManager.cs | 8 +++ 2 files changed, 80 insertions(+), 20 deletions(-) diff --git a/StageManager/Native/WindowsManager.cs b/StageManager/Native/WindowsManager.cs index 81e3424..7cb098a 100644 --- a/StageManager/Native/WindowsManager.cs +++ b/StageManager/Native/WindowsManager.cs @@ -17,6 +17,7 @@ namespace StageManager.Native public class WindowsManager : IWindowsManager { + private bool _active; private IDictionary _windows; private WinEventDelegate _hookDelegate; @@ -73,7 +74,9 @@ public WindowsManager() public Task Start() { - var currentProcess = Process.GetCurrentProcess(); + _active = true; + + var currentProcess = Process.GetCurrentProcess(); _currentProcessId = currentProcess.Id; _currentProcessWindowHandle = currentProcess.MainWindowHandle; @@ -100,21 +103,30 @@ public Task Start() Win32.SetWindowsHookEx(Win32.WH_MOUSE_LL, _mouseHook, currentProcess.MainModule.BaseAddress, 0); Application.Run(); }); + thread.Name = "WindowsManager"; thread.Start(); return Task.CompletedTask; } - public IWindowsDeferPosHandle DeferWindowsPos(int count) + public void Stop() + { + _active = false; + } + + public IWindowsDeferPosHandle DeferWindowsPos(int count) { - var info = Win32.BeginDeferWindowPos(count); + var info = Win32.BeginDeferWindowPos(count); return new WindowsDeferPosHandle(info); } public void ToggleFocusedWindowTiling() { - var window = _windows.Values.FirstOrDefault(w => w.IsFocused); + if (!_active) + return; + + var window = _windows.Values.FirstOrDefault(w => w.IsFocused); if (window != null) { @@ -135,7 +147,7 @@ public void ToggleFocusedWindowTiling() private IntPtr MouseHook(int nCode, UIntPtr wParam, IntPtr lParam) { - if (nCode == 0 && (uint)wParam == Win32.WM_LBUTTONUP) + if (nCode == 0 && (uint)wParam == Win32.WM_LBUTTONUP) HandleWindowMoveEnd(); return Win32.CallNextHookEx(IntPtr.Zero, nCode, wParam, lParam); @@ -143,6 +155,9 @@ private IntPtr MouseHook(int nCode, UIntPtr wParam, IntPtr lParam) private void WindowHook(IntPtr hWinEventHook, Win32.EVENT_CONSTANTS eventType, IntPtr hwnd, Win32.OBJID idObject, int idChild, uint dwEventThread, uint dwmsEventTime) { + if (!_active) + return; + if (EventWindowIsValid(idChild, idObject, hwnd)) { switch (eventType) @@ -188,7 +203,10 @@ private bool EventWindowIsValid(int idChild, Win32.OBJID idObject, IntPtr hwnd) private void RegisterWindow(IntPtr handle, bool emitEvent = true) { - if (handle == _currentProcessWindowHandle) + if (!_active) + return; + + if (handle == _currentProcessWindowHandle) return; if (!_windows.ContainsKey(handle)) @@ -216,7 +234,10 @@ private void RegisterWindow(IntPtr handle, bool emitEvent = true) private void UnregisterWindow(IntPtr handle) { - if (_windows.ContainsKey(handle)) + if (!_active) + return; + + if (_windows.ContainsKey(handle)) { var window = _windows[handle]; _windows.Remove(handle); @@ -226,7 +247,10 @@ private void UnregisterWindow(IntPtr handle) private void UpdateWindow(IntPtr handle, WindowUpdateType type) { - if (type == WindowUpdateType.Show && _windows.ContainsKey(handle)) + if (!_active) + return; + + if (type == WindowUpdateType.Show && _windows.ContainsKey(handle)) { var window = _windows[handle]; WindowUpdated?.Invoke(window, type); @@ -259,7 +283,10 @@ private void UpdateWindow(IntPtr handle, WindowUpdateType type) private void StartWindowMove(IntPtr handle) { - if (_windows.ContainsKey(handle)) + if (!_active) + return; + + if (_windows.ContainsKey(handle)) { var window = _windows[handle]; window.StoreLastLocation(); @@ -271,7 +298,10 @@ private void StartWindowMove(IntPtr handle) private void EndWindowMove(IntPtr handle) { - if (_windows.ContainsKey(handle)) + if (!_active) + return; + + if (_windows.ContainsKey(handle)) { var window = _windows[handle]; @@ -282,7 +312,10 @@ private void EndWindowMove(IntPtr handle) private void WindowMove(IntPtr handle) { - if (_mouseMoveWindow != null && _windows.ContainsKey(handle)) + if (!_active) + return; + + if (_mouseMoveWindow != null && _windows.ContainsKey(handle)) { var window = _windows[handle]; if (_mouseMoveWindow == window) @@ -292,25 +325,35 @@ private void WindowMove(IntPtr handle) private void HandleWindowFocused(IWindow window) { - WindowFocused?.Invoke(window); + if (!_active) + return; + + WindowFocused?.Invoke(window); } private void HandleWindowUpdated(IWindow window) { - ExternalWindowUpdate?.Invoke(window); + if (!_active) + return; + + ExternalWindowUpdate?.Invoke(window); } private void HandleWindowClosed(IWindow window) { - ExternalWindowClosed?.Invoke(window); + if (!_active) + return; + + ExternalWindowClosed?.Invoke(window); } private void HandleWindowMoveStart(WindowsWindow window) { - if (_mouseMoveWindow != null) - { + if (!_active) + return; + + if (_mouseMoveWindow != null) _mouseMoveWindow.IsMouseMoving = false; - } _mouseMoveWindow = window; window.IsMouseMoving = true; @@ -318,7 +361,10 @@ private void HandleWindowMoveStart(WindowsWindow window) private void HandleWindowMoveEnd() { - lock (_mouseMoveLock) + if (!_active) + return; + + lock (_mouseMoveLock) { if (_mouseMoveWindow != null) { @@ -333,12 +379,18 @@ private void HandleWindowMoveEnd() private void HandleWindowAdd(IWindow window, bool firstCreate) { - WindowCreated?.Invoke(window, firstCreate); + if (!_active) + return; + + WindowCreated?.Invoke(window, firstCreate); } private void HandleWindowRemove(IWindow window) { - WindowDestroyed?.Invoke(window); + if (!_active) + return; + + WindowDestroyed?.Invoke(window); } } } diff --git a/StageManager/SceneManager.cs b/StageManager/SceneManager.cs index 7fb4524..cf55cd0 100644 --- a/StageManager/SceneManager.cs +++ b/StageManager/SceneManager.cs @@ -50,6 +50,14 @@ public async Task Start() internal void Stop() { + WindowsManager.Stop(); + + foreach (var scene in _scenes) + { + foreach (var w in scene.Windows) + WindowStrategy.Show(w); + } + _desktop.ShowIcons(); } From 8f9baee5258e287742a12bdbe2e942d9564ef13b Mon Sep 17 00:00:00 2001 From: awaescher Date: Sun, 3 Dec 2023 12:36:56 +0100 Subject: [PATCH 2/8] Improve window strategy handling --- StageManager/SceneManager.cs | 12 +++--- StageManager/Strategies/IWindowStrategy.cs | 4 +- .../NormalizeAndMinimizeWindowStrategy.cs | 18 +++++++++ .../Strategies/OpacityWindowStrategy.cs | 40 +++++++++++++++++++ .../Strategies/ScreenOffsetWindowStrategy.cs | 17 ++++++++ .../Strategies/ShowAndHideWindowStrategy.cs | 21 ++++++++++ StageManager/Strategies/WindowHideStrategy.cs | 22 ---------- .../Strategies/WindowMinimizeStrategy .cs | 13 ------ .../Strategies/WindowNormalizeStrategy.cs | 13 ------ StageManager/Strategies/WindowShowStrategy.cs | 12 ------ 10 files changed, 104 insertions(+), 68 deletions(-) create mode 100644 StageManager/Strategies/NormalizeAndMinimizeWindowStrategy.cs create mode 100644 StageManager/Strategies/OpacityWindowStrategy.cs create mode 100644 StageManager/Strategies/ScreenOffsetWindowStrategy.cs create mode 100644 StageManager/Strategies/ShowAndHideWindowStrategy.cs delete mode 100644 StageManager/Strategies/WindowHideStrategy.cs delete mode 100644 StageManager/Strategies/WindowMinimizeStrategy .cs delete mode 100644 StageManager/Strategies/WindowNormalizeStrategy.cs delete mode 100644 StageManager/Strategies/WindowShowStrategy.cs diff --git a/StageManager/SceneManager.cs b/StageManager/SceneManager.cs index cf55cd0..b75301c 100644 --- a/StageManager/SceneManager.cs +++ b/StageManager/SceneManager.cs @@ -23,8 +23,7 @@ public class SceneManager public event EventHandler CurrentSceneSelectionChanged; public event EventHandler RequestWindowPreviewUpdate; - private IWindowStrategy ShowStrategy { get; } = new WindowNormalizeStrategy(); - private IWindowStrategy HideStrategy { get; } = new WindowMinimizeStrategy(); + private IWindowStrategy WindowStrategy { get; } = new NormalizeAndMinimizeWindowStrategy(); // new WindowNormalizeStrategy/OpacityWindowStrategy/ShowAndHideWindowStrategy public WindowsManager WindowsManager { get; } @@ -113,7 +112,6 @@ private async void WindowsManager_WindowCreated(IWindow window, bool firstCreate SwitchToSceneByNewWindow(window).SafeFireAndForget(); } - private async Task SwitchToSceneByWindow(IWindow window) { var scene = FindSceneForWindow(window); @@ -205,11 +203,11 @@ public async Task SwitchTo(Scene? scene) if (scene is object) { foreach (var w in scene.Windows) - ShowStrategy.Invoke(w); + WindowStrategy.Show(w); } foreach (var o in otherWindows) - HideStrategy.Invoke(o); + WindowStrategy.Hide(o); CurrentSceneSelectionChanged?.Invoke(this, new CurrentSceneSelectionChangedEventArgs(prior, _current)); @@ -247,12 +245,12 @@ public Task MoveWindow(Scene sourceScene, IWindow window, Scene targetScene) if (targetScene.Equals(_current)) { - ShowStrategy.Invoke(window); + WindowStrategy.Show(window); window.Focus(); } else { - HideStrategy.Invoke(window); + WindowStrategy.Hide(window); // reset window position after move so that the window is back at the starting position on the new scene if (window is WindowsWindow w && w.PopLastLocation() is IWindowLocation l) diff --git a/StageManager/Strategies/IWindowStrategy.cs b/StageManager/Strategies/IWindowStrategy.cs index 64d1044..55cee7b 100644 --- a/StageManager/Strategies/IWindowStrategy.cs +++ b/StageManager/Strategies/IWindowStrategy.cs @@ -4,6 +4,8 @@ namespace StageManager.Strategies { internal interface IWindowStrategy { - void Invoke(IWindow window); + void Show(IWindow window); + + void Hide(IWindow window); } } \ No newline at end of file diff --git a/StageManager/Strategies/NormalizeAndMinimizeWindowStrategy.cs b/StageManager/Strategies/NormalizeAndMinimizeWindowStrategy.cs new file mode 100644 index 0000000..9ae1cfc --- /dev/null +++ b/StageManager/Strategies/NormalizeAndMinimizeWindowStrategy.cs @@ -0,0 +1,18 @@ +using StageManager.Native.PInvoke; +using StageManager.Native.Window; + +namespace StageManager.Strategies +{ + internal class NormalizeAndMinimizeWindowStrategy : IWindowStrategy + { + public void Show(IWindow window) + { + Win32.ShowWindow(window.Handle, Win32.SW.SW_SHOWNOACTIVATE); + } + + public void Hide(IWindow window) + { + Win32.ShowWindow(window.Handle, Win32.SW.SW_MINIMIZE); + } + } +} diff --git a/StageManager/Strategies/OpacityWindowStrategy.cs b/StageManager/Strategies/OpacityWindowStrategy.cs new file mode 100644 index 0000000..237a335 --- /dev/null +++ b/StageManager/Strategies/OpacityWindowStrategy.cs @@ -0,0 +1,40 @@ +using StageManager.Native.Window; +using System; +using System.Runtime.InteropServices; + +namespace StageManager.Strategies +{ + /// + /// Works well with opacity = 0, higher opacity will make the windows appear when clicked + /// Visual Studio cannot be hidden this way, might be the same with other windows + /// + internal class OpacityWindowStrategy : IWindowStrategy + { + [DllImport("user32.dll")] + static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong); + + [DllImport("user32.dll")] + static extern int GetWindowLong(IntPtr hWnd, int nIndex); + + [DllImport("user32.dll")] + public static extern bool SetLayeredWindowAttributes(IntPtr hwnd, uint crKey, byte bAlpha, uint dwFlags); + + public const int GWL_EXSTYLE = -20; + public const int WS_EX_LAYERED = 0x80000; + public const int LWA_ALPHA = 0x2; + + public void Show(IWindow window) + { + _ = SetWindowLong(window.Handle, GWL_EXSTYLE, GetWindowLong(window.Handle, GWL_EXSTYLE) | WS_EX_LAYERED); + SetLayeredWindowAttributes(window.Handle, 0, 255, LWA_ALPHA); + + window.BringToTop(); + } + + public void Hide(IWindow window) + { + _ = SetWindowLong(window.Handle, GWL_EXSTYLE, GetWindowLong(window.Handle, GWL_EXSTYLE) | WS_EX_LAYERED); + SetLayeredWindowAttributes(window.Handle, 0, 0, LWA_ALPHA); + } + } +} \ No newline at end of file diff --git a/StageManager/Strategies/ScreenOffsetWindowStrategy.cs b/StageManager/Strategies/ScreenOffsetWindowStrategy.cs new file mode 100644 index 0000000..727643c --- /dev/null +++ b/StageManager/Strategies/ScreenOffsetWindowStrategy.cs @@ -0,0 +1,17 @@ +using StageManager.Native.Window; + +namespace StageManager.Strategies +{ + internal class ScreenOffsetWindowStrategy : IWindowStrategy + { + public void Show(IWindow window) + { + throw new System.NotImplementedException(); + } + + public void Hide(IWindow window) + { + throw new System.NotImplementedException(); + } + } +} diff --git a/StageManager/Strategies/ShowAndHideWindowStrategy.cs b/StageManager/Strategies/ShowAndHideWindowStrategy.cs new file mode 100644 index 0000000..8e385b2 --- /dev/null +++ b/StageManager/Strategies/ShowAndHideWindowStrategy.cs @@ -0,0 +1,21 @@ +using StageManager.Native.Window; + +namespace StageManager.Strategies +{ + /// + /// Shows and hides windows, which makes them disappear completely until they are showed again. + /// The user cannot bring them back without some advanced tricks. + /// + internal class ShowAndHideWindowStrategy : IWindowStrategy + { + public void Show(IWindow window) + { + window.ShowInCurrentState(); + } + + public void Hide(IWindow window) + { + window.Hide(); + } + } +} diff --git a/StageManager/Strategies/WindowHideStrategy.cs b/StageManager/Strategies/WindowHideStrategy.cs deleted file mode 100644 index 82c4505..0000000 --- a/StageManager/Strategies/WindowHideStrategy.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using StageManager.Native.Window; - -namespace StageManager.Strategies -{ - internal class WindowHideStratgy : IWindowStrategy - { - public void Invoke(IWindow window) - { - window.Hide(); - } - - public bool IsRelevant(IWindow window) - { - return window.CanLayout; - } - } -} diff --git a/StageManager/Strategies/WindowMinimizeStrategy .cs b/StageManager/Strategies/WindowMinimizeStrategy .cs deleted file mode 100644 index dd7ca51..0000000 --- a/StageManager/Strategies/WindowMinimizeStrategy .cs +++ /dev/null @@ -1,13 +0,0 @@ -using StageManager.Native.PInvoke; -using StageManager.Native.Window; - -namespace StageManager.Strategies -{ - internal class WindowMinimizeStrategy : IWindowStrategy - { - public void Invoke(IWindow window) - { - Win32.ShowWindow(window.Handle, Win32.SW.SW_MINIMIZE); - } - } -} diff --git a/StageManager/Strategies/WindowNormalizeStrategy.cs b/StageManager/Strategies/WindowNormalizeStrategy.cs deleted file mode 100644 index e847ac9..0000000 --- a/StageManager/Strategies/WindowNormalizeStrategy.cs +++ /dev/null @@ -1,13 +0,0 @@ -using StageManager.Native.PInvoke; -using StageManager.Native.Window; - -namespace StageManager.Strategies -{ - internal class WindowNormalizeStrategy : IWindowStrategy - { - public void Invoke(IWindow window) - { - Win32.ShowWindow(window.Handle, Win32.SW.SW_SHOWNOACTIVATE); - } - } -} diff --git a/StageManager/Strategies/WindowShowStrategy.cs b/StageManager/Strategies/WindowShowStrategy.cs deleted file mode 100644 index 829417d..0000000 --- a/StageManager/Strategies/WindowShowStrategy.cs +++ /dev/null @@ -1,12 +0,0 @@ -using StageManager.Native.Window; - -namespace StageManager.Strategies -{ - internal class WindowShowStrategy : IWindowStrategy - { - public void Invoke(IWindow window) - { - window.ShowInCurrentState(); - } - } -} From 67d17b4928db767bac3ba8f1e7a986639c503c0f Mon Sep 17 00:00:00 2001 From: awaescher Date: Sun, 3 Dec 2023 12:37:26 +0100 Subject: [PATCH 3/8] Remove 3D visualization for DWM thumbails [WIP] --- StageManager/3D/Panel3D.cs | 349 ------------------ StageManager/3D/Panel3DAdorner.cs | 119 ------ StageManager/3D/Scene.xaml | 16 - StageManager/DwmThumbnail.cs | 145 ++++++++ StageManager/MainWindow.xaml | 34 +- StageManager/MainWindow.xaml.cs | 3 +- .../Native/Interop/DWMWINDOWATTRIBUTE.cs | 25 ++ .../Interop/DWM_THUMBNAIL_PROPERTIES.cs | 22 ++ StageManager/Native/Interop/DWM_TNP.cs | 22 ++ StageManager/Native/Interop/NativeMethods.cs | 24 ++ StageManager/Native/Interop/RECT.cs | 22 ++ StageManager/Themes/Generic.xaml | 19 + 12 files changed, 304 insertions(+), 496 deletions(-) delete mode 100644 StageManager/3D/Panel3D.cs delete mode 100644 StageManager/3D/Panel3DAdorner.cs delete mode 100644 StageManager/3D/Scene.xaml create mode 100644 StageManager/DwmThumbnail.cs create mode 100644 StageManager/Native/Interop/DWMWINDOWATTRIBUTE.cs create mode 100644 StageManager/Native/Interop/DWM_THUMBNAIL_PROPERTIES.cs create mode 100644 StageManager/Native/Interop/DWM_TNP.cs create mode 100644 StageManager/Native/Interop/NativeMethods.cs create mode 100644 StageManager/Native/Interop/RECT.cs create mode 100644 StageManager/Themes/Generic.xaml diff --git a/StageManager/3D/Panel3D.cs b/StageManager/3D/Panel3D.cs deleted file mode 100644 index 3f7f491..0000000 --- a/StageManager/3D/Panel3D.cs +++ /dev/null @@ -1,349 +0,0 @@ -// TAKEN FROM https://joshsmithonwpf.wordpress.com/2008/03/30/animating-images-in-a-3d-itemscontrol/ - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Documents; -using System.Windows.Media; -using System.Windows.Media.Animation; -using System.Windows.Media.Effects; -using System.Windows.Media.Media3D; - -namespace StageManager; - -/// -/// A Panel that displays its children in a -/// Viewport3D hosted in the adorner layer. -/// -public class Panel3D : Panel -{ - #region Data - - static readonly Point ORIGIN_POINT = new Point(0, 0); - - bool _isMovingItems; - int _registeredNameCounter = 0; - readonly Viewport3D _viewport; - readonly Dictionary _visualTo3DModelMap = new Dictionary(); - - #endregion // Data - - #region Constructor - - public Panel3D() - { - _viewport = Application.LoadComponent(new Uri("3D/Scene.xaml", UriKind.Relative)) as Viewport3D; - - this.Loaded += delegate - { - var adornerLayer = AdornerLayer.GetAdornerLayer(this); - var adorner = new Panel3DAdorner(this, _viewport); - - adornerLayer.IsHitTestVisible = false; - adorner.IsHitTestVisible = false; - - adornerLayer.Add(adorner); - }; - } - - protected override void OnInitialized(EventArgs e) - { - base.OnInitialized(e); - _viewport.DataContext = this.DataContext; - } - - protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e) - { - base.OnPropertyChanged(e); - - if (e.Property.Name == nameof(IsMouseOver)) - _viewport.Opacity = ((bool)e.NewValue) ? 1.0 : 0.8; - else if (e.Property.Name == nameof(IsVisible)) - _viewport.Visibility = ((bool)e.NewValue) ? Visibility.Visible : Visibility.Hidden; - } - - #endregion // Constructor - - #region MoveItems - - public void MoveItems(bool forward) - { - if (_isMovingItems) - return; - - // We cannot move items less than two items. - if (_viewport.Children.Count < 2) - return; - - #region Create Lists - - // Get a list of all the GeometryModel3D and TranslateTransform3D objects in the viewport. - List geometries = new List(); - List transforms = new List(); - foreach (ModelVisual3D model in _viewport.Children) - { - GeometryModel3D geo = model.Content as GeometryModel3D; - if (geo != null) - { - geometries.Add(geo); - transforms.Add(geo.Transform as TranslateTransform3D); - } - } - - #endregion // Create Lists - - #region Relocate Target Item - - // Move the first or last item to the opposite end of the list. - if (forward) - { - var firstGeo = geometries[0]; - geometries.RemoveAt(0); - geometries.Add(firstGeo); - - // The item at index 0 holds the scene's light - // so don't remove that, instead remove the first - // model that we added to the scene in code. - var firstChild = _viewport.Children[1]; - _viewport.Children.RemoveAt(1); - _viewport.Children.Add(firstChild); - } - else - { - int idx = geometries.Count - 1; - var lastGeo = geometries[idx]; - geometries.RemoveAt(idx); - geometries.Insert(0, lastGeo); - - idx = _viewport.Children.Count - 1; - var lastChild = _viewport.Children[idx]; - _viewport.Children.RemoveAt(idx); - _viewport.Children.Insert(1, lastChild); - } - - #endregion // Relocate Target Item - - #region Animate All Items to New Locations and Opacitys - - Storyboard story = new Storyboard(); - - // Apply the new transforms via animations. - for (int i = 0; i < transforms.Count; ++i) - { - double targetX = transforms[i].OffsetX; - double targetY = transforms[i].OffsetY; - double targetZ = transforms[i].OffsetZ; - - var trans = geometries[i].Transform as TranslateTransform3D; - - // In order to animate the transform it must have a name registered - // with the viewport. Since a transform does not have a Name property - // I just create an arbitrary name for each transform and register that. - string name = this.GetNextName(); - _viewport.RegisterName(name, trans); - - Duration duration = new Duration(TimeSpan.FromSeconds(1.25)); - - DoubleAnimation animX = new DoubleAnimation(); - animX.To = targetX; - animX.Duration = duration; - animX.AccelerationRatio = 0.1; - animX.DecelerationRatio = 0.9; - Storyboard.SetTargetProperty(animX, new PropertyPath("OffsetX")); - Storyboard.SetTargetName(animX, name); - story.Children.Add(animX); - - DoubleAnimation animY = new DoubleAnimation(); - animY.To = targetY; - animY.AccelerationRatio = 0.7; - animY.DecelerationRatio = 0.3; - animY.Duration = duration; - Storyboard.SetTargetProperty(animY, new PropertyPath("OffsetY")); - Storyboard.SetTargetName(animY, name); - story.Children.Add(animY); - - DoubleAnimation animZ = new DoubleAnimation(); - animZ.To = targetZ; - animZ.AccelerationRatio = 0.3; - animZ.DecelerationRatio = 0.7; - animZ.Duration = duration; - Storyboard.SetTargetProperty(animZ, new PropertyPath("OffsetZ")); - Storyboard.SetTargetName(animZ, name); - story.Children.Add(animZ); - - DoubleAnimation animOpacity = new DoubleAnimation(); - var material = geometries[i].Material as DiffuseMaterial; - var brush = material.Brush as VisualBrush; - name = this.GetNextName(); - _viewport.RegisterName(name, brush); - animOpacity.To = 1.0 - (i / (double)transforms.Count); - animOpacity.AccelerationRatio = 0.2; - animOpacity.DecelerationRatio = 0.8; - animOpacity.Duration = duration; - Storyboard.SetTargetProperty(animOpacity, new PropertyPath("Opacity")); - Storyboard.SetTargetName(animOpacity, name); - story.Children.Add(animOpacity); - - if (i == 0) - animX.Completed += delegate { _isMovingItems = false; }; - } - - _isMovingItems = true; - story.Begin(_viewport); - - #endregion // Animate All Items to New Locations and Opacitys - } - - string GetNextName() - { - return "name" + _registeredNameCounter++; - } - - #endregion // MoveItems - - #region DPs - - public static readonly DependencyProperty ElementHeightProperty = - DependencyProperty.Register("ElementHeight", typeof(double), typeof(Panel3D), - new FrameworkPropertyMetadata(30.0)); - - public static readonly DependencyProperty ElementWidthProperty = - DependencyProperty.Register("ElementWidth", typeof(double), typeof(Panel3D), - new FrameworkPropertyMetadata(40.0)); - - - public double ElementWidth - { - get { return (double)GetValue(ElementWidthProperty); } - set { SetValue(ElementWidthProperty, value); } - } - - public double ElementHeight - { - get { return (double)GetValue(ElementHeightProperty); } - set { SetValue(ElementHeightProperty, value); } - } - #endregion - - #region Layout Overrides - - protected override Size ArrangeOverride(Size finalSize) - { - Size size = new Size(ElementWidth, ElementHeight); - - // Arrange children so that their visualbrush has a valid width/height. - foreach (UIElement child in Children) - child.Arrange(new Rect(ORIGIN_POINT, size)); - - _viewport.Arrange(new Rect(ORIGIN_POINT, finalSize)); - - return finalSize; - } - - protected override Size MeasureOverride(Size availableSize) - { - Size size = new Size(ElementWidth, ElementHeight); - - foreach (UIElement child in Children) - child.Measure(size); - - _viewport.Measure(availableSize); - - if (availableSize.Width > 10000) - availableSize.Width = availableSize.Height; - - if (availableSize.Height > 10000) - availableSize.Height = availableSize.Width; - - return size; - } - - #endregion // Layout Overrides - - #region OnVisualChildrenChanged - - protected override void OnVisualChildrenChanged(DependencyObject visualAdded, DependencyObject visualRemoved) - { - base.OnVisualChildrenChanged(visualAdded, visualRemoved); - - bool add = visualAdded != null && !_visualTo3DModelMap.ContainsKey(visualAdded); - if (add) - { - // effects the PerspectiveCamera in Scene.xaml like for 20 here its Position="-3,0,3.5" and for 100 its Position="-1.7,0,2.1" - (visualAdded as ContentPresenter).Effect = new DropShadowEffect { BlurRadius = 30, Direction = 200 /* pretty leftish */ }; - var model = Build3DModel(visualAdded as Visual); - _visualTo3DModelMap.Add(visualAdded, model); - _viewport.Children.Add(model); - } - - bool remove = visualRemoved != null && _visualTo3DModelMap.ContainsKey(visualRemoved); - if (remove) - { - var model = _visualTo3DModelMap[visualRemoved]; - _viewport.Children.Remove(model); - _visualTo3DModelMap.Remove(visualRemoved); - } - } - - #endregion // OnVisualChildrenChanged - - #region Build3DModel - - ModelVisual3D Build3DModel(Visual visual) - { - double opacityDivisor = 1; - //_viewport.Children.Count > 0 ? - //_viewport.Children.Count : 1; - - var model = new ModelVisual3D - { - Content = new GeometryModel3D - { - Geometry = new MeshGeometry3D - { - TriangleIndices = new Int32Collection( - new int[] { 0, 1, 2, 2, 3, 0 }), - TextureCoordinates = new PointCollection( - new Point[] - { - new Point(0, 1), - new Point(1, 1), - new Point(1, 0), - new Point(0, 0) - }), - Positions = new Point3DCollection( - new Point3D[] - { - new Point3D(-1, -1, 0), - new Point3D(1, -1, 0), - new Point3D(1, 1, 0), - new Point3D(-1, 1, 0) - }) - }, - Material = new DiffuseMaterial - { - Brush = new VisualBrush - { - Visual = visual, - Opacity = 1.0 / opacityDivisor - } - }, - Transform = new TranslateTransform3D - { - OffsetX = _viewport.Children.Count * 0.1, - OffsetY = 0, - OffsetZ = _viewport.Children.Count * 0.2 - } - } - }; - - return model; - } - - #endregion // Build3DModel -} - diff --git a/StageManager/3D/Panel3DAdorner.cs b/StageManager/3D/Panel3DAdorner.cs deleted file mode 100644 index 85a2900..0000000 --- a/StageManager/3D/Panel3DAdorner.cs +++ /dev/null @@ -1,119 +0,0 @@ -using System; -using System.Collections; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Documents; -using System.Windows.Media; -using System.Windows.Media.Effects; - -namespace StageManager; - -/// -/// Hosts an opaque Viewport3D in the adorner layer above a Panel3D. -/// -public class Panel3DAdorner : Adorner -{ - #region Data - - private ArrayList _logicalChildren; - private readonly DockPanel _viewportHost = new DockPanel(); - - #endregion // Data - - #region Constructor - - public Panel3DAdorner(Panel3D adornedPanel3D, Viewport3D viewport) - : base(adornedPanel3D) - { - _viewportHost.Children.Add(viewport); - _viewportHost.Background = Brushes.Transparent; - base.AddLogicalChild(_viewportHost); - base.AddVisualChild(_viewportHost); - - - } - - #endregion // Constructor - - #region Measure/Arrange - - ///// - ///// Allows the control to determine how big it wants to be. - ///// - ///// A limiting size for the control. - //protected override Size MeasureOverride(Size constraint) - //{ - // //_viewport.Measure(constraint); - // //return _viewport.DesiredSize; - - // //if (constraint.Width > 10000) - // // constraint.Width = constraint.Height; - - // //if (constraint.Height > 10000) - // // constraint.Height = constraint.Width; - - // //return constraint; - - // return new Size(100, 100); - //} - - /// - /// Positions and sizes the control. - /// - /// The actual size of the control. - protected override Size ArrangeOverride(Size finalSize) - { - Rect rect = new Rect(new Point(), finalSize); - - _viewportHost.Arrange(rect); - - return finalSize; - } - - #endregion // Measure/Arrange - - #region Visual Children - - /// - /// Required for the element to be rendered. - /// - protected override int VisualChildrenCount - { - get { return 1; } - } - - /// - /// Required for the element to be rendered. - /// - protected override Visual GetVisualChild(int index) - { - if (index != 0) - throw new ArgumentOutOfRangeException("index"); - - return _viewportHost; - } - - #endregion // Visual Children - - #region Logical Children - - /// - /// Required for the displayed element to inherit property values - /// from the logical tree, such as FontSize. - /// - protected override IEnumerator LogicalChildren - { - get - { - if (_logicalChildren == null) - { - _logicalChildren = new ArrayList(); - _logicalChildren.Add(_viewportHost); - } - - return _logicalChildren.GetEnumerator(); - } - } - - #endregion // Logical Children -} \ No newline at end of file diff --git a/StageManager/3D/Scene.xaml b/StageManager/3D/Scene.xaml deleted file mode 100644 index 11ffdac..0000000 --- a/StageManager/3D/Scene.xaml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/StageManager/DwmThumbnail.cs b/StageManager/DwmThumbnail.cs new file mode 100644 index 0000000..f4bb499 --- /dev/null +++ b/StageManager/DwmThumbnail.cs @@ -0,0 +1,145 @@ +using StageManager.Native.Interop; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace StageManager +{ + /// + /// Follow steps 1a or 1b and then 2 to use this custom control in a XAML file. + /// + /// Step 1a) Using this custom control in a XAML file that exists in the current project. + /// Add this XmlNamespace attribute to the root element of the markup file where it is + /// to be used: + /// + /// xmlns:MyNamespace="clr-namespace:StageManager" + /// + /// + /// Step 1b) Using this custom control in a XAML file that exists in a different project. + /// Add this XmlNamespace attribute to the root element of the markup file where it is + /// to be used: + /// + /// xmlns:MyNamespace="clr-namespace:StageManager;assembly=StageManager" + /// + /// You will also need to add a project reference from the project where the XAML file lives + /// to this project and Rebuild to avoid compilation errors: + /// + /// Right click on the target project in the Solution Explorer and + /// "Add Reference"->"Projects"->[Browse to and select this project] + /// + /// + /// Step 2) + /// Go ahead and use your control in the XAML file. + /// + /// + /// + /// + public class DwmThumbnail : Control + { + static DwmThumbnail() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(DwmThumbnail), new FrameworkPropertyMetadata(typeof(DwmThumbnail))); + } + + private IntPtr _dwmThumbnail; + + public static readonly DependencyProperty PreviewHandleProperty = DependencyProperty.Register(nameof(PreviewHandle), + typeof(IntPtr), + typeof(DwmThumbnail), + new PropertyMetadata(IntPtr.Zero)); + + public IntPtr PreviewHandle + { + get { return (IntPtr)GetValue(PreviewHandleProperty); } + set { SetValue(PreviewHandleProperty, value); } + } + + private Point GetDpiScaleFactor() + { + var source = PresentationSource.FromVisual(this); + return source?.CompositionTarget != null ? new Point(source.CompositionTarget.TransformToDevice.M11, source.CompositionTarget.TransformToDevice.M22) : new Point(1.0d, 1.0d); + } + + protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e) + { + base.OnPropertyChanged(e); + + if (nameof(PreviewHandle).Equals(e.Property.Name)) + { + if ((IntPtr)e.OldValue == IntPtr.Zero && (IntPtr)e.NewValue != IntPtr.Zero) + StartCapture(); + + UpdateThumbnailProperties(); + } + + if (nameof(IsVisible).Equals(e.Property.Name) && !(bool)e.NewValue && _dwmThumbnail != IntPtr.Zero) + { + NativeMethods.DwmUnregisterThumbnail(_dwmThumbnail); + _dwmThumbnail = IntPtr.Zero; + } + } + + protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo) + { + base.OnRenderSizeChanged(sizeInfo); + + UpdateThumbnailProperties(); + } + + public static Rect BoundsRelativeTo(FrameworkElement element, Visual relativeTo) + { + return element.TransformToVisual(relativeTo) + .TransformBounds(System.Windows.Controls.Primitives.LayoutInformation.GetLayoutSlot(element)); + } + + private void StartCapture() + { + var windowHandle = new System.Windows.Interop.WindowInteropHelper(FindWindow()).Handle; + + var hr = NativeMethods.DwmRegisterThumbnail(windowHandle, PreviewHandle, out _dwmThumbnail); + if (hr != 0) + return; + } + + private Window FindWindow() => Window.GetWindow(this); + + private void UpdateThumbnailProperties() + { + if (_dwmThumbnail == IntPtr.Zero) + return; + + var dpi = GetDpiScaleFactor(); + + var previewBounds = BoundsRelativeTo(this, FindWindow()); + + var props = new DWM_THUMBNAIL_PROPERTIES + { + fVisible = true, + dwFlags = (int)(DWM_TNP.DWM_TNP_VISIBLE | DWM_TNP.DWM_TNP_OPACITY | DWM_TNP.DWM_TNP_RECTDESTINATION | DWM_TNP.DWM_TNP_SOURCECLIENTAREAONLY), + opacity = 255, + rcDestination = new RECT + { + top = (int)(previewBounds.Top * dpi.Y), + left = (int)(previewBounds.Left * dpi.X), + bottom = (int)(previewBounds.Bottom * dpi.Y), + right = (int)(previewBounds.Right * dpi.X) + }, + fSourceClientAreaOnly = true + }; + + NativeMethods.DwmUpdateThumbnailProperties(_dwmThumbnail, ref props); + } + } +} diff --git a/StageManager/MainWindow.xaml b/StageManager/MainWindow.xaml index 247a5de..01ed800 100644 --- a/StageManager/MainWindow.xaml +++ b/StageManager/MainWindow.xaml @@ -47,6 +47,7 @@ VerticalAlignment="Center"> @@ -63,32 +64,43 @@ + ItemsSource="{Binding Windows}" + > - + - + + + + + + + + + + Orientation="Vertical" /> diff --git a/StageManager/MainWindow.xaml.cs b/StageManager/MainWindow.xaml.cs index e0f1663..85ac675 100644 --- a/StageManager/MainWindow.xaml.cs +++ b/StageManager/MainWindow.xaml.cs @@ -4,6 +4,7 @@ using StageManager.Model; using StageManager.Native; using StageManager.Native.PInvoke; +using StageManager.Native.Interop; using StageManager.Native.Window; using System; using System.Collections.Generic; @@ -57,7 +58,7 @@ protected override void OnInitialized(EventArgs e) _thisHandle = new System.Windows.Interop.WindowInteropHelper(this).Handle; _lastWidth = Width; - StartHook(); + StartHook(); } protected override void OnClosed(EventArgs e) diff --git a/StageManager/Native/Interop/DWMWINDOWATTRIBUTE.cs b/StageManager/Native/Interop/DWMWINDOWATTRIBUTE.cs new file mode 100644 index 0000000..f857fc4 --- /dev/null +++ b/StageManager/Native/Interop/DWMWINDOWATTRIBUTE.cs @@ -0,0 +1,25 @@ +// ReSharper disable FieldCanBeMadeReadOnly.Global +// ReSharper disable InconsistentNaming +// ReSharper disable MemberCanBePrivate.Global + +namespace StageManager.Native.Interop +{ + public enum DWMWINDOWATTRIBUTE + { + NCRenderingEnabled = 1, + NCRenderingPolicy, + TransitionsForceDisabled, + AllowNCPaint, + CaptionButtonBounds, + NonClientRtlLayout, + ForceIconicRepresentation, + Flip3DPolicy, + ExtendedFrameBounds, + HasIconicBitmap, + DisallowPeek, + ExcludedFromPeek, + Cloak, + Cloaked, + FreezeRepresentation + } +} \ No newline at end of file diff --git a/StageManager/Native/Interop/DWM_THUMBNAIL_PROPERTIES.cs b/StageManager/Native/Interop/DWM_THUMBNAIL_PROPERTIES.cs new file mode 100644 index 0000000..7df6983 --- /dev/null +++ b/StageManager/Native/Interop/DWM_THUMBNAIL_PROPERTIES.cs @@ -0,0 +1,22 @@ +using System.Runtime.InteropServices; + +// ReSharper disable FieldCanBeMadeReadOnly.Global +// ReSharper disable InconsistentNaming +// ReSharper disable MemberCanBePrivate.Global + +namespace StageManager.Native.Interop +{ + [StructLayout(LayoutKind.Sequential)] + public struct DWM_THUMBNAIL_PROPERTIES + { + public int dwFlags; + public RECT rcDestination; + public RECT rcSource; + public byte opacity; + + [MarshalAs(UnmanagedType.Bool, SizeConst = 4)] + public bool fVisible; + + public bool fSourceClientAreaOnly; + } +} \ No newline at end of file diff --git a/StageManager/Native/Interop/DWM_TNP.cs b/StageManager/Native/Interop/DWM_TNP.cs new file mode 100644 index 0000000..c189616 --- /dev/null +++ b/StageManager/Native/Interop/DWM_TNP.cs @@ -0,0 +1,22 @@ +using System; + +// ReSharper disable FieldCanBeMadeReadOnly.Global +// ReSharper disable InconsistentNaming +// ReSharper disable MemberCanBePrivate.Global + +namespace StageManager.Native.Interop +{ + [Flags] + public enum DWM_TNP + { + DWM_TNP_RECTDESTINATION = 0x00000001, + + DWM_TNP_RECTSOURCE = 0x00000002, + + DWM_TNP_OPACITY = 0x00000004, + + DWM_TNP_VISIBLE = 0x00000008, + + DWM_TNP_SOURCECLIENTAREAONLY = 0x00000010 + } +} \ No newline at end of file diff --git a/StageManager/Native/Interop/NativeMethods.cs b/StageManager/Native/Interop/NativeMethods.cs new file mode 100644 index 0000000..b4f84aa --- /dev/null +++ b/StageManager/Native/Interop/NativeMethods.cs @@ -0,0 +1,24 @@ +using System; +using System.Runtime.InteropServices; +using System.Windows; + +namespace StageManager.Native.Interop +{ + public static class NativeMethods + { + [DllImport("dwmapi.dll", SetLastError = true)] + public static extern int DwmRegisterThumbnail(IntPtr dest, IntPtr src, out IntPtr thumb); + + [DllImport("dwmapi.dll")] + public static extern int DwmUnregisterThumbnail(IntPtr thumb); + + [DllImport("dwmapi.dll", PreserveSig = false)] + public static extern void DwmQueryThumbnailSourceSize(IntPtr hThumbnail, out Size size); + + [DllImport("dwmapi.dll", PreserveSig = true)] + public static extern int DwmUpdateThumbnailProperties(IntPtr hThumbnail, ref DWM_THUMBNAIL_PROPERTIES props); + + [DllImport("dwmapi.dll")] + public static extern int DwmGetWindowAttribute(IntPtr hWnd, DWMWINDOWATTRIBUTE dwAttribute, out bool pvAttribute, int cbAttribute); + } +} \ No newline at end of file diff --git a/StageManager/Native/Interop/RECT.cs b/StageManager/Native/Interop/RECT.cs new file mode 100644 index 0000000..364a8ea --- /dev/null +++ b/StageManager/Native/Interop/RECT.cs @@ -0,0 +1,22 @@ +using System.Runtime.InteropServices; + +// ReSharper disable FieldCanBeMadeReadOnly.Global +// ReSharper disable InconsistentNaming +// ReSharper disable MemberCanBePrivate.Global + +namespace StageManager.Native.Interop +{ + [StructLayout(LayoutKind.Sequential)] + public struct RECT + { + public int left; + public int top; + public int right; + public int bottom; + + public bool IsEmpty() + { + return left == 0 && top == 0 && right == 0 && bottom == 0; + } + } +} \ No newline at end of file diff --git a/StageManager/Themes/Generic.xaml b/StageManager/Themes/Generic.xaml new file mode 100644 index 0000000..8ec5966 --- /dev/null +++ b/StageManager/Themes/Generic.xaml @@ -0,0 +1,19 @@ + + + + + From 78f46262a281cc4d285ef464f568e907c107c660 Mon Sep 17 00:00:00 2001 From: awaescher Date: Sun, 3 Dec 2023 13:28:42 +0100 Subject: [PATCH 4/8] Improve dwm thumbnail arrangement --- StageManager/DwmThumbnail.xaml | 21 +++++++ .../{DwmThumbnail.cs => DwmThumbnail.xaml.cs} | 59 ++++--------------- StageManager/MainWindow.xaml | 31 ++++------ StageManager/Themes/Generic.xaml | 19 ------ 4 files changed, 43 insertions(+), 87 deletions(-) create mode 100644 StageManager/DwmThumbnail.xaml rename StageManager/{DwmThumbnail.cs => DwmThumbnail.xaml.cs} (61%) delete mode 100644 StageManager/Themes/Generic.xaml diff --git a/StageManager/DwmThumbnail.xaml b/StageManager/DwmThumbnail.xaml new file mode 100644 index 0000000..cde1d2d --- /dev/null +++ b/StageManager/DwmThumbnail.xaml @@ -0,0 +1,21 @@ + + + + + + + + + + diff --git a/StageManager/DwmThumbnail.cs b/StageManager/DwmThumbnail.xaml.cs similarity index 61% rename from StageManager/DwmThumbnail.cs rename to StageManager/DwmThumbnail.xaml.cs index f4bb499..98a9164 100644 --- a/StageManager/DwmThumbnail.cs +++ b/StageManager/DwmThumbnail.xaml.cs @@ -1,57 +1,20 @@ using StageManager.Native.Interop; using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; -using System.Windows.Data; -using System.Windows.Documents; -using System.Windows.Input; using System.Windows.Media; -using System.Windows.Media.Imaging; -using System.Windows.Navigation; -using System.Windows.Shapes; namespace StageManager { - /// - /// Follow steps 1a or 1b and then 2 to use this custom control in a XAML file. - /// - /// Step 1a) Using this custom control in a XAML file that exists in the current project. - /// Add this XmlNamespace attribute to the root element of the markup file where it is - /// to be used: - /// - /// xmlns:MyNamespace="clr-namespace:StageManager" - /// - /// - /// Step 1b) Using this custom control in a XAML file that exists in a different project. - /// Add this XmlNamespace attribute to the root element of the markup file where it is - /// to be used: - /// - /// xmlns:MyNamespace="clr-namespace:StageManager;assembly=StageManager" - /// - /// You will also need to add a project reference from the project where the XAML file lives - /// to this project and Rebuild to avoid compilation errors: - /// - /// Right click on the target project in the Solution Explorer and - /// "Add Reference"->"Projects"->[Browse to and select this project] - /// - /// - /// Step 2) - /// Go ahead and use your control in the XAML file. - /// - /// - /// - /// - public class DwmThumbnail : Control - { - static DwmThumbnail() - { - DefaultStyleKeyProperty.OverrideMetadata(typeof(DwmThumbnail), new FrameworkPropertyMetadata(typeof(DwmThumbnail))); - } + /// + /// Interaction logic for DwmThumbnail.xaml + /// + public partial class DwmThumbnail : UserControl + { + public DwmThumbnail() + { + InitializeComponent(); + } private IntPtr _dwmThumbnail; @@ -133,8 +96,8 @@ private void UpdateThumbnailProperties() { top = (int)(previewBounds.Top * dpi.Y), left = (int)(previewBounds.Left * dpi.X), - bottom = (int)(previewBounds.Bottom * dpi.Y), - right = (int)(previewBounds.Right * dpi.X) + bottom = (int)((previewBounds.Bottom - Margin.Top - Margin.Bottom) * dpi.Y) + 1, + right = (int)((previewBounds.Right - Margin.Left - Margin.Right) * dpi.X) + 1 }, fSourceClientAreaOnly = true }; diff --git a/StageManager/MainWindow.xaml b/StageManager/MainWindow.xaml index 01ed800..40a4567 100644 --- a/StageManager/MainWindow.xaml +++ b/StageManager/MainWindow.xaml @@ -63,35 +63,26 @@ CommandParameter="{Binding .}" /> + + ItemsSource="{Binding Windows}"> - + - - - - - - - - - + + diff --git a/StageManager/Themes/Generic.xaml b/StageManager/Themes/Generic.xaml deleted file mode 100644 index 8ec5966..0000000 --- a/StageManager/Themes/Generic.xaml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - From d48fada2bcb6ca210d0c5dfed7ff0cf3f05c2ae2 Mon Sep 17 00:00:00 2001 From: awaescher Date: Mon, 11 Dec 2023 22:37:51 +0100 Subject: [PATCH 5/8] Update DWM thumbnails on screen position change --- StageManager/DwmThumbnail.xaml.cs | 59 +++++++++++++++++++------------ 1 file changed, 36 insertions(+), 23 deletions(-) diff --git a/StageManager/DwmThumbnail.xaml.cs b/StageManager/DwmThumbnail.xaml.cs index 98a9164..5fa0b12 100644 --- a/StageManager/DwmThumbnail.xaml.cs +++ b/StageManager/DwmThumbnail.xaml.cs @@ -6,17 +6,20 @@ namespace StageManager { - /// - /// Interaction logic for DwmThumbnail.xaml - /// - public partial class DwmThumbnail : UserControl - { - public DwmThumbnail() - { - InitializeComponent(); - } + /// + /// Interaction logic for DwmThumbnail.xaml + /// + public partial class DwmThumbnail : UserControl + { + public DwmThumbnail() + { + InitializeComponent(); + LayoutUpdated += DwmThumbnail_LayoutUpdated; + } private IntPtr _dwmThumbnail; + private Window _window; + private Point? _dpiScaleFactor; public static readonly DependencyProperty PreviewHandleProperty = DependencyProperty.Register(nameof(PreviewHandle), typeof(IntPtr), @@ -31,8 +34,19 @@ public IntPtr PreviewHandle private Point GetDpiScaleFactor() { - var source = PresentationSource.FromVisual(this); - return source?.CompositionTarget != null ? new Point(source.CompositionTarget.TransformToDevice.M11, source.CompositionTarget.TransformToDevice.M22) : new Point(1.0d, 1.0d); + if (_dpiScaleFactor is null) + { + var source = PresentationSource.FromVisual(this); + _dpiScaleFactor = source?.CompositionTarget != null ? new Point(source.CompositionTarget.TransformToDevice.M11, source.CompositionTarget.TransformToDevice.M22) : new Point(1.0d, 1.0d); + } + + return _dpiScaleFactor.Value; + } + + protected override void OnDpiChanged(DpiScale oldDpi, DpiScale newDpi) + { + _dpiScaleFactor = null; + base.OnDpiChanged(oldDpi, newDpi); } protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e) @@ -54,10 +68,8 @@ protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e) } } - protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo) + private void DwmThumbnail_LayoutUpdated(object? sender, EventArgs e) { - base.OnRenderSizeChanged(sizeInfo); - UpdateThumbnailProperties(); } @@ -76,7 +88,7 @@ private void StartCapture() return; } - private Window FindWindow() => Window.GetWindow(this); + private Window FindWindow() => _window ??= Window.GetWindow(this); private void UpdateThumbnailProperties() { @@ -87,21 +99,22 @@ private void UpdateThumbnailProperties() var previewBounds = BoundsRelativeTo(this, FindWindow()); + var thumbnailRect = new RECT + { + top = (int)(previewBounds.Top * dpi.Y), + left = (int)(previewBounds.Left * dpi.X), + bottom = (int)((previewBounds.Bottom - Margin.Top - Margin.Bottom) * dpi.Y) + 1, + right = (int)((previewBounds.Right - Margin.Left - Margin.Right) * dpi.X) + 1 + }; + var props = new DWM_THUMBNAIL_PROPERTIES { fVisible = true, dwFlags = (int)(DWM_TNP.DWM_TNP_VISIBLE | DWM_TNP.DWM_TNP_OPACITY | DWM_TNP.DWM_TNP_RECTDESTINATION | DWM_TNP.DWM_TNP_SOURCECLIENTAREAONLY), opacity = 255, - rcDestination = new RECT - { - top = (int)(previewBounds.Top * dpi.Y), - left = (int)(previewBounds.Left * dpi.X), - bottom = (int)((previewBounds.Bottom - Margin.Top - Margin.Bottom) * dpi.Y) + 1, - right = (int)((previewBounds.Right - Margin.Left - Margin.Right) * dpi.X) + 1 - }, + rcDestination = thumbnailRect, fSourceClientAreaOnly = true }; - NativeMethods.DwmUpdateThumbnailProperties(_dwmThumbnail, ref props); } } From 9399b9ccba8dfd1bc335cfe42de3dc81b567bf1b Mon Sep 17 00:00:00 2001 From: awaescher Date: Mon, 11 Dec 2023 22:38:12 +0100 Subject: [PATCH 6/8] Improve window layout --- StageManager/MainWindow.xaml | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/StageManager/MainWindow.xaml b/StageManager/MainWindow.xaml index 40a4567..805d344 100644 --- a/StageManager/MainWindow.xaml +++ b/StageManager/MainWindow.xaml @@ -20,8 +20,8 @@ Name="thisWindow">