diff --git a/.editorconfig b/.editorconfig index 2b7e98d..b6c3966 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,6 +1,9 @@ # Remove the line below if you want to inherit .editorconfig settings from higher directories root = true +[*] +guidelines = 120 1px dotted 504CFF00, 150 1px dotted 50FF6A00 + # Test .cs files [**/*Tests.cs] diff --git a/src/samples/Petzold/5th/Clover/Program.cs b/src/samples/Petzold/5th/Clover/Program.cs index aa5d0f1..3cd5a3f 100644 --- a/src/samples/Petzold/5th/Clover/Program.cs +++ b/src/samples/Petzold/5th/Clover/Program.cs @@ -44,9 +44,9 @@ protected override LRESULT WindowProcedure(HWND window, MessageType message, WPA using HRGN region1 = HRGN.FromEllipse(_cxClient / 2, _cyClient / 3, _cxClient, 2 * _cyClient / 3); using HRGN region2 = HRGN.FromEllipse(_cxClient / 3, 0, 2 * _cxClient / 3, _cyClient / 2); using HRGN region3 = HRGN.FromEllipse(_cxClient / 3, _cyClient / 2, 2 * _cxClient / 3, _cyClient); - using HRGN region4 = HRGN.CombineRegion(region0, region1, RegionCombineMode.Or); - using HRGN region5 = HRGN.CombineRegion(region2, region3, RegionCombineMode.Or); - _hrgnClip = HRGN.CombineRegion(region4, region5, RegionCombineMode.Xor); + using HRGN region4 = region0.Combine(region1, RegionCombineMode.Or); + using HRGN region5 = region2.Combine(region3, RegionCombineMode.Or); + _hrgnClip = region4.Combine(region5, RegionCombineMode.Xor); return (LRESULT)0; } diff --git a/src/thirtytwo/Messages/Message.NonClientPaint.cs b/src/thirtytwo/Messages/Message.NonClientPaint.cs new file mode 100644 index 0000000..107166a --- /dev/null +++ b/src/thirtytwo/Messages/Message.NonClientPaint.cs @@ -0,0 +1,50 @@ +// Copyright (c) Jeremy W. Kuhne. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Windows; + +public static partial class Message +{ + /// + /// Wrapper for the frame painting message. + /// + public readonly unsafe ref struct NonClientPaint(WPARAM wParam) + { + /// + /// Update region clipped to the window frame. + /// + /// + /// + /// This may sometimes be to indicate the entire window. + /// + /// + /// This is effectively . If the window has a clipping region + /// set via , that will be intersected with the rect. + /// This isn't very likely as having a clipping region + /// + /// prevents theming. + /// + /// + public HRGN UpdateRegion => (HRGN)(nint)wParam; + + public HDC GetDC(HWND window, bool copyRegion = true) + { + // GetDCEx will take ownership of the region. + HRGN updateRegion = UpdateRegion; + if (updateRegion.IsFull) + { + updateRegion = HRGN.Null; + } + + HRGN region = copyRegion && !updateRegion.IsNull ? updateRegion.Copy() : updateRegion; + + // GetDC (as opposed to GetDCEx) will call GetDCEx with a special flag that will set flags based + // on the window's style. WS_CLIPCHILDREN will set DCX_CLIPCHILDREN for example. + + return Interop.GetDCEx( + window, + region, + region.IsNull ? GET_DCX_FLAGS.DCX_WINDOW : GET_DCX_FLAGS.DCX_WINDOW | GET_DCX_FLAGS.DCX_INTERSECTRGN); + } + } +} \ No newline at end of file diff --git a/src/thirtytwo/NativeMethods.txt b/src/thirtytwo/NativeMethods.txt index 0cb8d22..dd5ba7c 100644 --- a/src/thirtytwo/NativeMethods.txt +++ b/src/thirtytwo/NativeMethods.txt @@ -144,6 +144,7 @@ GetCurrentThreadId GetCursorPos GetDC GetDCBrushColor +GetDCEx GetDeviceCaps GetDialogBaseUnits GetDialogBaseUnits diff --git a/src/thirtytwo/Win32/Graphics/Gdi/HRGN.cs b/src/thirtytwo/Win32/Graphics/Gdi/HRGN.cs index dc22df3..b486336 100644 --- a/src/thirtytwo/Win32/Graphics/Gdi/HRGN.cs +++ b/src/thirtytwo/Win32/Graphics/Gdi/HRGN.cs @@ -10,6 +10,11 @@ public unsafe partial struct HRGN : IDisposable { public void Dispose() { + if (IsFull) + { + return; + } + if (!IsNull) { Interop.DeleteObject(this); @@ -18,6 +23,18 @@ public void Dispose() Unsafe.AsRef(in this) = default; } + /// + /// Special sent during WM_NCPAINT to indicate the entire window. + /// + public static HRGN Full { get; } = (HRGN)1; + + /// + /// Is special value. + /// + public bool IsFull => Value == Full.Value; + + // There is also a special HRGN_MONITOR (2) that is used when maximized. Not sure if this escapes. + public static HRGN FromRectangle(Rectangle rectangle) => Interop.CreateRectRgn(rectangle.X, rectangle.Y, rectangle.Right, rectangle.Bottom); @@ -30,13 +47,27 @@ public static HRGN FromEllipse(Rectangle bounds) => public static HRGN CreateEmpty() => Interop.CreateRectRgn(0, 0, 0, 0); - public static HRGN CombineRegion(HRGN first, HRGN second, RegionCombineMode combineMode) => - CombineRegion(first, second, combineMode, out _); + public HRGN Combine(HRGN region, RegionCombineMode combineMode) => + Combine(region, combineMode, out _); + + public HRGN Combine(HRGN region, RegionCombineMode combineMode, out RegionType type) + { + HRGN hrgn = CreateEmpty(); + type = (RegionType)Interop.CombineRgn(hrgn, this, region, (RGN_COMBINE_MODE)combineMode); + if (type == RegionType.Error) + { + hrgn.Dispose(); + } + + return hrgn; + } + + public HRGN Copy() => Copy(out _); - public static HRGN CombineRegion(HRGN first, HRGN second, RegionCombineMode combineMode, out RegionType type) + public HRGN Copy(out RegionType type) { HRGN hrgn = CreateEmpty(); - type = (RegionType)Interop.CombineRgn(hrgn, first, second, (RGN_COMBINE_MODE)combineMode); + type = (RegionType)Interop.CombineRgn(hrgn, this, default, (RGN_COMBINE_MODE)RegionCombineMode.Copy); if (type == RegionType.Error) { hrgn.Dispose();