|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Drawing;
using System.Windows.Forms.Layout;
using System.Windows.Forms.VisualStyles;
namespace System.Windows.Forms.ButtonInternal;
internal class ButtonStandardAdapter : ButtonBaseAdapter
{
private const int BorderWidth = 2;
internal ButtonStandardAdapter(ButtonBase control) : base(control) { }
private PushButtonState DetermineState(bool up)
{
PushButtonState state = PushButtonState.Normal;
if (!up)
{
state = PushButtonState.Pressed;
}
else if (Control.MouseIsOver)
{
state = PushButtonState.Hot;
}
else if (!Control.Enabled)
{
state = PushButtonState.Disabled;
}
else if (Control.Focused || Control.IsDefault)
{
state = PushButtonState.Default;
}
return state;
}
internal override void PaintUp(PaintEventArgs e, CheckState state) => PaintWorker(e, up: true, state);
internal override void PaintDown(PaintEventArgs e, CheckState state) => PaintWorker(e, up: false, state);
internal override void PaintOver(PaintEventArgs e, CheckState state) => PaintUp(e, state);
private void PaintThemedButtonBackground(PaintEventArgs e, Rectangle bounds, bool up)
{
PushButtonState pbState = DetermineState(up);
// First handle transparent case.
if (ButtonRenderer.IsBackgroundPartiallyTransparent(pbState))
{
ButtonRenderer.DrawParentBackground(e, bounds, Control);
}
ButtonRenderer.DrawButtonForHandle(
e,
Control.ClientRectangle,
focused: false,
pbState,
ScaleHelper.IsScalingRequirementMet ? Control.HWNDInternal : HWND.Null);
// Now overlay the background image or color (the former overrides the latter), leaving a margin.
// We hardcode this margin for now since GetThemeMargins returns 0 all the time.
//
// Changing this because GetThemeMargins simply does not work in some cases.
bounds.Inflate(-ButtonBorderSize, -ButtonBorderSize);
// Only paint if the user said not to use the themed background color.
if (!Control.UseVisualStyleBackColor)
{
bool isHighContrastHighlighted = up && IsHighContrastHighlighted();
Color color = isHighContrastHighlighted ? SystemColors.Highlight : Control.BackColor;
if (color.HasTransparency())
{
using var brush = color.GetCachedSolidBrushScope();
e.GraphicsInternal.FillRectangle(brush, bounds);
}
else
{
using DeviceContextHdcScope hdc = new(e);
hdc.FillRectangle(
bounds,
isHighContrastHighlighted
? PInvokeCore.GetSysColorBrush(SYS_COLOR_INDEX.COLOR_HIGHLIGHT)
: Control.BackColorBrush);
}
}
// This code is mostly taken from the non-themed rendering code path.
if (Control.BackgroundImage is not null && !DisplayInformation.HighContrast)
{
ControlPaint.DrawBackgroundImage(
e.GraphicsInternal,
Control.BackgroundImage,
Color.Transparent,
Control.BackgroundImageLayout,
Control.ClientRectangle,
bounds,
Control.DisplayRectangle.Location,
Control.RightToLeft);
}
}
private void PaintWorker(PaintEventArgs e, bool up, CheckState state)
{
up = up && state == CheckState.Unchecked;
ColorData colors = PaintRender(e).Calculate();
LayoutData layout;
if (Application.RenderWithVisualStyles)
{
// Don't have the text-pressed-down effect when we use themed painting to be consistent with Win32.
layout = PaintLayout(up: true).Layout();
}
else
{
layout = PaintLayout(up).Layout();
}
_ = Control as Button;
if (Application.RenderWithVisualStyles)
{
PaintThemedButtonBackground(e, Control.ClientRectangle, up);
}
else
{
Brush? backgroundBrush = null;
if (state == CheckState.Indeterminate)
{
backgroundBrush = CreateDitherBrush(colors.Highlight, colors.ButtonFace);
}
try
{
Rectangle bounds = Control.ClientRectangle;
if (up)
{
// We are going to draw a 2 pixel border
bounds.Inflate(-BorderWidth, -BorderWidth);
}
else
{
// We are going to draw a 1 pixel border.
bounds.Inflate(-1, -1);
}
PaintButtonBackground(e, bounds, backgroundBrush);
}
finally
{
backgroundBrush?.Dispose();
}
}
PaintImage(e, layout);
// Inflate the focus rectangle to be consistent with the behavior of Win32 app
if (Application.RenderWithVisualStyles && Control.FlatStyle != FlatStyle.Standard)
{
layout.Focus.Inflate(1, 1);
}
if (up & IsHighContrastHighlighted())
{
Color highlightTextColor = SystemColors.HighlightText;
PaintField(e, layout, colors, highlightTextColor, drawFocus: false);
if (Control.Focused && Control.ShowFocusCues)
{
// Drawing focus rectangle of HighlightText color
ControlPaint.DrawHighContrastFocusRectangle(e.GraphicsInternal, layout.Focus, highlightTextColor);
}
}
else if (up & IsHighContrastHighlighted())
{
PaintField(e, layout, colors, SystemColors.HighlightText, drawFocus: true);
}
else
{
PaintField(e, layout, colors, colors.WindowText, drawFocus: true);
}
if (!Application.RenderWithVisualStyles)
{
Rectangle r = Control.ClientRectangle;
if (Control.IsDefault)
{
r.Inflate(-1, -1);
}
DrawDefaultBorder(e, r, colors.WindowFrame, Control.IsDefault);
if (up)
{
Draw3DBorder(e, r, colors, raised: up);
}
else
{
// Not Draw3DBorder(..., raised: false);
ControlPaint.DrawBorderSimple(e, r, colors.ButtonShadow);
}
}
}
protected override LayoutOptions Layout(PaintEventArgs e)
{
LayoutOptions layout = PaintLayout(up: false);
Debug.Assert(
layout.GetPreferredSizeCore(LayoutUtils.s_maxSize) == PaintLayout(up: true).GetPreferredSizeCore(LayoutUtils.s_maxSize),
"The state of up should not effect PreferredSize");
return layout;
}
private LayoutOptions PaintLayout(bool up)
{
LayoutOptions layout = CommonLayout();
layout.TextOffset = !up;
layout.DotNetOneButtonCompat = !Application.RenderWithVisualStyles;
return layout;
}
}
|