Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -165,19 +165,6 @@ private ModernComboTargetState ComputeModernComboTargetState()
{
int topInset = chromeInsets.Top + Padding.Top;
int bottomInset = chromeInsets.Bottom + Padding.Bottom;
editBounds.Y += topInset;

editBounds.Height = Math.Max(
1,
editBounds.Height - topInset - bottomInset);

editBounds.Height = Math.Max(
1,
Math.Min(
editBounds.Height,
ClientRectangle.Bottom
- bottomInset
- editBounds.Top));

// Inset the edit window horizontally so its rectangular corners clear the rounded
// field arcs, and reserve the (now wider) drop-down button on the button side.
Expand All @@ -189,30 +176,91 @@ private ModernComboTargetState ComputeModernComboTargetState()
ModernControlVisualStyles.ComboBoxButtonExtraWidth,
DeviceDpiInternal);

if (RightToLeft == RightToLeft.Yes)
if (DropDownStyle == ComboBoxStyle.Simple)
{
// In RTL the drop-down button sits on the left, so its reservation goes there.
editBounds.X += rightInset + extraButtonWidth;
int dividerThickness = Math.Max(
1,
ScaleHelper.ScaleToDpi(
ModernControlVisualStyles.BorderThickness,
DeviceDpiInternal));
const int bottomCropLogicalPixels = 2;
int simpleBottomShrink = ScaleHelper.ScaleToDpi(bottomCropLogicalPixels, DeviceDpiInternal);
int simpleListBottom = ClientRectangle.Bottom - simpleBottomShrink - dividerThickness;
int simpleEditTop = topInset + ScaleHelper.ScaleToDpi(1, DeviceDpiInternal);
int selectionFieldHeight = Math.Max(
1,
(int)PInvokeCore.SendMessage(
this,
PInvoke.CB_GETITEMHEIGHT,
(WPARAM)(-1)));
int minimumReadableHeight = Math.Max(
1,
FontHeight + ScaleHelper.ScaleToDpi(2, DeviceDpiInternal));
int preferredSimpleEditHeight = Math.Max(selectionFieldHeight, minimumReadableHeight)
+ ScaleHelper.ScaleToDpi(2, DeviceDpiInternal);
int maxSimpleEditHeight = Math.Max(
1,
simpleListBottom - dividerThickness - simpleEditTop);

// Keep the editor in the top field lane with enough height for full glyph rendering,
// draw an accent divider below it, and let the permanent list consume the remaining
// space down to the rounded field border.
editBounds.Y = simpleEditTop;
editBounds.Height = Math.Min(preferredSimpleEditHeight, maxSimpleEditHeight);
editBounds.X = leftInset;
editBounds.Width = Math.Max(
1,
ClientRectangle.Width - leftInset - rightInset);

if (!simpleListBounds.IsEmpty)
{
simpleListBounds.X = editBounds.Left;
simpleListBounds.Width = editBounds.Width;
simpleListBounds.Y = editBounds.Bottom + dividerThickness;
simpleListBounds.Height = Math.Max(
1,
simpleListBottom - simpleListBounds.Y);
}
}
else
{
editBounds.X += leftInset;
}

editBounds.Width = Math.Max(
1,
editBounds.Width - leftInset - rightInset - extraButtonWidth);

if (!simpleListBounds.IsEmpty)
{
int simpleListBottom = GetCurrentSimpleListBottom(
simpleListBounds.Bottom);
editBounds.Y += topInset;

simpleListBounds.Y = editBounds.Bottom + bottomInset;
editBounds.Height = Math.Max(
1,
editBounds.Height - topInset - bottomInset);

simpleListBounds.Height = Math.Max(
editBounds.Height = Math.Max(
1,
Math.Min(
editBounds.Height,
ClientRectangle.Bottom
- bottomInset
- editBounds.Top));

if (RightToLeft == RightToLeft.Yes)
{
// In RTL the drop-down button sits on the left, so its reservation goes there.
editBounds.X += rightInset + extraButtonWidth;
}
else
{
editBounds.X += leftInset;
}

editBounds.Width = Math.Max(
1,
simpleListBottom - simpleListBounds.Y);
editBounds.Width - leftInset - rightInset - extraButtonWidth);

if (!simpleListBounds.IsEmpty)
{
int simpleListBottom = GetCurrentSimpleListBottom(
simpleListBounds.Bottom);
simpleListBounds.Y = editBounds.Bottom + bottomInset;
simpleListBounds.Height = Math.Max(
1,
simpleListBottom - simpleListBounds.Y);
}
}
}

Expand Down Expand Up @@ -281,6 +329,60 @@ private void GetAdjustedNativeChildBounds(
}
}

private void ConfigureModernSimpleListSurface(HWND listHandle)
{
if (DropDownStyle != ComboBoxStyle.Simple
|| !UsesModernComboAdapter
|| listHandle.IsNull)
{
return;
}

WINDOW_STYLE style = (WINDOW_STYLE)PInvokeCore.GetWindowLong(
listHandle,
WINDOW_LONG_PTR_INDEX.GWL_STYLE);
WINDOW_EX_STYLE exStyle = (WINDOW_EX_STYLE)PInvokeCore.GetWindowLong(
listHandle,
WINDOW_LONG_PTR_INDEX.GWL_EXSTYLE);

WINDOW_STYLE updatedStyle = style & ~WINDOW_STYLE.WS_BORDER;
WINDOW_EX_STYLE updatedExStyle = exStyle & ~WINDOW_EX_STYLE.WS_EX_CLIENTEDGE;

if (updatedStyle == style && updatedExStyle == exStyle)
{
return;
}

if (updatedStyle != style)
{
PInvokeCore.SetWindowLong(
listHandle,
WINDOW_LONG_PTR_INDEX.GWL_STYLE,
(nint)updatedStyle);
}

if (updatedExStyle != exStyle)
{
PInvokeCore.SetWindowLong(
listHandle,
WINDOW_LONG_PTR_INDEX.GWL_EXSTYLE,
(nint)updatedExStyle);
}

PInvoke.SetWindowPos(
listHandle,
HWND.Null,
0,
0,
0,
0,
SET_WINDOW_POS_FLAGS.SWP_NOMOVE
| SET_WINDOW_POS_FLAGS.SWP_NOSIZE
| SET_WINDOW_POS_FLAGS.SWP_NOZORDER
| SET_WINDOW_POS_FLAGS.SWP_NOACTIVATE
| SET_WINDOW_POS_FLAGS.SWP_FRAMECHANGED);
}

private unsafe int GetCurrentSimpleListBottom(
int fallback)
{
Expand Down Expand Up @@ -464,24 +566,63 @@ private void ApplyEditMargins(

private void ApplyChildBounds(HWND childHandle, Rectangle targetBounds)
{
bool isModernSimpleList = UsesModernComboAdapter
&& DropDownStyle == ComboBoxStyle.Simple
&& childHandle == _childListBox?.HWND;

if (childHandle.IsNull
|| targetBounds.IsEmpty
|| GetChildBounds(childHandle) == targetBounds)
|| targetBounds.IsEmpty)
{
return;
}

PInvoke.SetWindowPos(
childHandle,
HWND.Null,
targetBounds.Left,
targetBounds.Top,
if (GetChildBounds(childHandle) != targetBounds)
{
PInvoke.SetWindowPos(
childHandle,
HWND.Null,
targetBounds.Left,
targetBounds.Top,
targetBounds.Width,
targetBounds.Height,
SET_WINDOW_POS_FLAGS.SWP_NOACTIVATE
| SET_WINDOW_POS_FLAGS.SWP_NOZORDER);

_modernComboLayoutWriteCount++;
}

if (isModernSimpleList)
{
ConfigureModernSimpleListClipRegion(
Comment thread
SimonZhao888 marked this conversation as resolved.
childHandle,
targetBounds);
}
}

private void ConfigureModernSimpleListClipRegion(
HWND listHandle,
Rectangle targetBounds)
{
if (listHandle.IsNull)
{
return;
}

int bottomShrink = ScaleHelper.ScaleToDpi(2, DeviceDpiInternal);
Comment thread
SimonZhao888 marked this conversation as resolved.
Outdated
int visibleHeight = Math.Max(1, targetBounds.Height - bottomShrink);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The bottom crop is still applied twice. ComputeModernComboTargetState already subtracts ModernSimpleBottomCropLogicalPixels (and the divider thickness) when calculating simpleListBottom, then this region removes the same crop again. At 96 DPI the intended ~2px crop becomes roughly 5px (2 + divider + 2), potentially clipping the last row or the bottom of the scrollbar. Please apply the crop in only one place.

using RegionScope region = new(
0,
0,
targetBounds.Width,
targetBounds.Height,
SET_WINDOW_POS_FLAGS.SWP_NOACTIVATE
| SET_WINDOW_POS_FLAGS.SWP_NOZORDER);
visibleHeight);

_modernComboLayoutWriteCount++;
if (PInvoke.SetWindowRgn(
listHandle,
region,
fRedraw: true) != 0)
{
region.RelinquishOwnership();
}
}

private Rectangle GetChildBounds(HWND child)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ public override void DrawFlatCombo(
graphics,
clientBounds);
DrawDropDownButton(comboBox, graphics);
DrawSimpleEditListDivider(comboBox, graphics);

switch (_flatStyle)
{
Expand Down Expand Up @@ -395,6 +396,45 @@ private static void CutOutRoundedCorners(
parentColor);
}

private static void DrawSimpleEditListDivider(
ComboBox comboBox,
Graphics graphics)
{
if (comboBox.DropDownStyle != ComboBoxStyle.Simple
|| comboBox._childListBox is null)
{
return;
}

Rectangle listBounds = comboBox.GetChildBounds(comboBox._childListBox.HWND);
if (listBounds.IsEmpty)
{
return;
}

int dividerThickness = Math.Max(
1,
ScaleHelper.ScaleToDpi(
ModernControlVisualStyles.BorderThickness,
comboBox.DeviceDpiInternal));
int lineY = listBounds.Top - dividerThickness;
if (lineY < 0)
{
return;
}

Padding chromeInsets = comboBox.GetModernChromeInsets();
int left = chromeInsets.Left + comboBox.Padding.Left;
int right = comboBox.ClientRectangle.Right - chromeInsets.Right - comboBox.Padding.Right - 1;
if (right < left)
{
return;
}

using var pen = Application.SystemVisualSettings.AccentColor.GetCachedPenScope(dividerThickness);
graphics.DrawLine(pen, left, lineY, right, lineY);
}

private static void DrawRoundedBorder(
ComboBox comboBox,
Graphics graphics,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,9 @@ protected override CreateParams CreateParams
cp.Style |= (int)WINDOW_STYLE.WS_VSCROLL | PInvoke.CBS_HASSTRINGS | PInvoke.CBS_AUTOHSCROLL;
cp.ExStyle |= (int)WINDOW_EX_STYLE.WS_EX_CLIENTEDGE;

if (!_integralHeight)
if (!_integralHeight
|| (UsesModernComboAdapter
&& DropDownStyle == ComboBoxStyle.Simple))
Comment thread
SimonZhao888 marked this conversation as resolved.
Outdated
{
cp.Style |= PInvoke.CBS_NOINTEGRALHEIGHT;
}
Expand Down Expand Up @@ -2408,6 +2410,12 @@ protected override unsafe void OnHandleCreated(EventArgs e)
bool hasComboBoxInfo = PInvoke.GetComboBoxInfo(
HWND,
ref comboBoxInfo);

if (hasComboBoxInfo)
{
ConfigureModernSimpleListSurface(comboBoxInfo.hwndList);
Comment thread
SimonZhao888 marked this conversation as resolved.
}

if (Application.IsDarkModeEnabled)
{
// Style the ComboBox Open-Button:
Expand Down Expand Up @@ -3725,14 +3733,11 @@ protected override unsafe void WndProc(ref Message m)
{
switch (m.MsgInternal)
{
// Modern VisualStyles: expand the client area to the full window so the drop-down
// button, which the themed ComboBox otherwise reserves as non-client (outside
// ClientRectangle), becomes part of the client and is covered by our rounded field.
// Simple combos have no drop-down button (their client already spans the full width
// and hosts a permanent list), so they must never be expanded.
// Modern VisualStyles: expand the client area to the full window so themed non-client
// reservations (for example the ComboBox-hosted scroll strip in Simple mode) are folded
// into client painting and can be normalized by modern layout.
case PInvokeCore.WM_NCCALCSIZE:
if (UsesModernComboAdapter
&& DropDownStyle != ComboBoxStyle.Simple
&& m.WParamInternal != 0u)
Comment thread
SimonZhao888 marked this conversation as resolved.
{
RECT* ncRects = (RECT*)(nint)m.LParamInternal;
Expand All @@ -3745,15 +3750,12 @@ protected override unsafe void WndProc(ref Message m)
base.WndProc(ref m);
break;

// Modern VisualStyles: comctl32 still reports the drop-down button region as a
// non-client hit (HTVSCROLL) even though WM_NCCALCSIZE folded it into our client
// area, so a button click would arrive as WM_NCLBUTTONDOWN and never reach the
// WM_LBUTTONDOWN hit-test below. Force HTCLIENT across the expanded client so the
// button click is delivered as a normal client message we can act on.
// Modern VisualStyles: after WM_NCCALCSIZE expands the client area, comctl32 can still
// report the folded strip as non-client (HTVSCROLL). Force HTCLIENT across the expanded
// client so interaction remains consistent with the modern layout.
case PInvokeCore.WM_NCHITTEST:
base.WndProc(ref m);
if (UsesModernComboAdapter
&& DropDownStyle != ComboBoxStyle.Simple
&& m.ResultInternal != PInvoke.HTCLIENT)
{
Point hitPoint = PointToClient(PARAM.ToPoint(m.LParamInternal));
Expand Down
Loading