Skip to content

Commit

Permalink
Some NC paint research
Browse files Browse the repository at this point in the history
Also add guidelines to editor.config.
  • Loading branch information
JeremyKuhne committed Aug 10, 2024
1 parent 2b4dd62 commit d17a035
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 7 deletions.
3 changes: 3 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -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]

Expand Down
6 changes: 3 additions & 3 deletions src/samples/Petzold/5th/Clover/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
50 changes: 50 additions & 0 deletions src/thirtytwo/Messages/Message.NonClientPaint.cs
Original file line number Diff line number Diff line change
@@ -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
{
/// <summary>
/// Wrapper for the <see href="https://learn.microsoft.com/windows/win32/gdi/wm-ncpaint">frame painting message</see>.
/// </summary>
public readonly unsafe ref struct NonClientPaint(WPARAM wParam)
{
/// <summary>
/// Update region clipped to the window frame.
/// </summary>
/// <remarks>
/// <para>
/// This may sometimes be <see cref="HRGN.Full"/> to indicate the entire window.
/// </para>
/// <para>
/// This is effectively <see cref="Interop.GetWindowRect(HWND, RECT*)"/>. If the window has a clipping region
/// set via <see cref="Interop.SetWindowRgn(HWND, HRGN, BOOL)"/>, that will be intersected with the rect.
/// This isn't very likely as having a clipping region
/// <see href="https://learn.microsoft.com/windows/win32/controls/cookbook-overview#when-visual-styles-are-not-applied">
/// prevents theming</see>.
/// </para>
/// </remarks>
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);
}
}
}
1 change: 1 addition & 0 deletions src/thirtytwo/NativeMethods.txt
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ GetCurrentThreadId
GetCursorPos
GetDC
GetDCBrushColor
GetDCEx
GetDeviceCaps
GetDialogBaseUnits
GetDialogBaseUnits
Expand Down
39 changes: 35 additions & 4 deletions src/thirtytwo/Win32/Graphics/Gdi/HRGN.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ public unsafe partial struct HRGN : IDisposable
{
public void Dispose()
{
if (IsFull)
{
return;
}

if (!IsNull)
{
Interop.DeleteObject(this);
Expand All @@ -18,6 +23,18 @@ public void Dispose()
Unsafe.AsRef(in this) = default;
}

/// <summary>
/// Special <see cref="HRGN"/> sent during WM_NCPAINT to indicate the entire window.
/// </summary>
public static HRGN Full { get; } = (HRGN)1;

/// <summary>
/// Is special <see cref="HRGN.Full"/> value.
/// </summary>
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);

Expand All @@ -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();
Expand Down

0 comments on commit d17a035

Please sign in to comment.