Skip to content

Commit d17a035

Browse files
committed
Some NC paint research
Also add guidelines to editor.config.
1 parent 2b4dd62 commit d17a035

File tree

5 files changed

+92
-7
lines changed

5 files changed

+92
-7
lines changed

.editorconfig

+3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
# Remove the line below if you want to inherit .editorconfig settings from higher directories
22
root = true
33

4+
[*]
5+
guidelines = 120 1px dotted 504CFF00, 150 1px dotted 50FF6A00
6+
47
# Test .cs files
58
[**/*Tests.cs]
69

src/samples/Petzold/5th/Clover/Program.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,9 @@ protected override LRESULT WindowProcedure(HWND window, MessageType message, WPA
4444
using HRGN region1 = HRGN.FromEllipse(_cxClient / 2, _cyClient / 3, _cxClient, 2 * _cyClient / 3);
4545
using HRGN region2 = HRGN.FromEllipse(_cxClient / 3, 0, 2 * _cxClient / 3, _cyClient / 2);
4646
using HRGN region3 = HRGN.FromEllipse(_cxClient / 3, _cyClient / 2, 2 * _cxClient / 3, _cyClient);
47-
using HRGN region4 = HRGN.CombineRegion(region0, region1, RegionCombineMode.Or);
48-
using HRGN region5 = HRGN.CombineRegion(region2, region3, RegionCombineMode.Or);
49-
_hrgnClip = HRGN.CombineRegion(region4, region5, RegionCombineMode.Xor);
47+
using HRGN region4 = region0.Combine(region1, RegionCombineMode.Or);
48+
using HRGN region5 = region2.Combine(region3, RegionCombineMode.Or);
49+
_hrgnClip = region4.Combine(region5, RegionCombineMode.Xor);
5050

5151
return (LRESULT)0;
5252
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// Copyright (c) Jeremy W. Kuhne. All rights reserved.
2+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3+
4+
namespace Windows;
5+
6+
public static partial class Message
7+
{
8+
/// <summary>
9+
/// Wrapper for the <see href="https://learn.microsoft.com/windows/win32/gdi/wm-ncpaint">frame painting message</see>.
10+
/// </summary>
11+
public readonly unsafe ref struct NonClientPaint(WPARAM wParam)
12+
{
13+
/// <summary>
14+
/// Update region clipped to the window frame.
15+
/// </summary>
16+
/// <remarks>
17+
/// <para>
18+
/// This may sometimes be <see cref="HRGN.Full"/> to indicate the entire window.
19+
/// </para>
20+
/// <para>
21+
/// This is effectively <see cref="Interop.GetWindowRect(HWND, RECT*)"/>. If the window has a clipping region
22+
/// set via <see cref="Interop.SetWindowRgn(HWND, HRGN, BOOL)"/>, that will be intersected with the rect.
23+
/// This isn't very likely as having a clipping region
24+
/// <see href="https://learn.microsoft.com/windows/win32/controls/cookbook-overview#when-visual-styles-are-not-applied">
25+
/// prevents theming</see>.
26+
/// </para>
27+
/// </remarks>
28+
public HRGN UpdateRegion => (HRGN)(nint)wParam;
29+
30+
public HDC GetDC(HWND window, bool copyRegion = true)
31+
{
32+
// GetDCEx will take ownership of the region.
33+
HRGN updateRegion = UpdateRegion;
34+
if (updateRegion.IsFull)
35+
{
36+
updateRegion = HRGN.Null;
37+
}
38+
39+
HRGN region = copyRegion && !updateRegion.IsNull ? updateRegion.Copy() : updateRegion;
40+
41+
// GetDC (as opposed to GetDCEx) will call GetDCEx with a special flag that will set flags based
42+
// on the window's style. WS_CLIPCHILDREN will set DCX_CLIPCHILDREN for example.
43+
44+
return Interop.GetDCEx(
45+
window,
46+
region,
47+
region.IsNull ? GET_DCX_FLAGS.DCX_WINDOW : GET_DCX_FLAGS.DCX_WINDOW | GET_DCX_FLAGS.DCX_INTERSECTRGN);
48+
}
49+
}
50+
}

src/thirtytwo/NativeMethods.txt

+1
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ GetCurrentThreadId
144144
GetCursorPos
145145
GetDC
146146
GetDCBrushColor
147+
GetDCEx
147148
GetDeviceCaps
148149
GetDialogBaseUnits
149150
GetDialogBaseUnits

src/thirtytwo/Win32/Graphics/Gdi/HRGN.cs

+35-4
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ public unsafe partial struct HRGN : IDisposable
1010
{
1111
public void Dispose()
1212
{
13+
if (IsFull)
14+
{
15+
return;
16+
}
17+
1318
if (!IsNull)
1419
{
1520
Interop.DeleteObject(this);
@@ -18,6 +23,18 @@ public void Dispose()
1823
Unsafe.AsRef(in this) = default;
1924
}
2025

26+
/// <summary>
27+
/// Special <see cref="HRGN"/> sent during WM_NCPAINT to indicate the entire window.
28+
/// </summary>
29+
public static HRGN Full { get; } = (HRGN)1;
30+
31+
/// <summary>
32+
/// Is special <see cref="HRGN.Full"/> value.
33+
/// </summary>
34+
public bool IsFull => Value == Full.Value;
35+
36+
// There is also a special HRGN_MONITOR (2) that is used when maximized. Not sure if this escapes.
37+
2138
public static HRGN FromRectangle(Rectangle rectangle) =>
2239
Interop.CreateRectRgn(rectangle.X, rectangle.Y, rectangle.Right, rectangle.Bottom);
2340

@@ -30,13 +47,27 @@ public static HRGN FromEllipse(Rectangle bounds) =>
3047

3148
public static HRGN CreateEmpty() => Interop.CreateRectRgn(0, 0, 0, 0);
3249

33-
public static HRGN CombineRegion(HRGN first, HRGN second, RegionCombineMode combineMode) =>
34-
CombineRegion(first, second, combineMode, out _);
50+
public HRGN Combine(HRGN region, RegionCombineMode combineMode) =>
51+
Combine(region, combineMode, out _);
52+
53+
public HRGN Combine(HRGN region, RegionCombineMode combineMode, out RegionType type)
54+
{
55+
HRGN hrgn = CreateEmpty();
56+
type = (RegionType)Interop.CombineRgn(hrgn, this, region, (RGN_COMBINE_MODE)combineMode);
57+
if (type == RegionType.Error)
58+
{
59+
hrgn.Dispose();
60+
}
61+
62+
return hrgn;
63+
}
64+
65+
public HRGN Copy() => Copy(out _);
3566

36-
public static HRGN CombineRegion(HRGN first, HRGN second, RegionCombineMode combineMode, out RegionType type)
67+
public HRGN Copy(out RegionType type)
3768
{
3869
HRGN hrgn = CreateEmpty();
39-
type = (RegionType)Interop.CombineRgn(hrgn, first, second, (RGN_COMBINE_MODE)combineMode);
70+
type = (RegionType)Interop.CombineRgn(hrgn, this, default, (RGN_COMBINE_MODE)RegionCombineMode.Copy);
4071
if (type == RegionType.Error)
4172
{
4273
hrgn.Dispose();

0 commit comments

Comments
 (0)