-
Notifications
You must be signed in to change notification settings - Fork 1k
DarkMode (b) DarkModeButtonRenderer #13408
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
DarkMode (b) DarkModeButtonRenderer #13408
Conversation
57f4d2f
to
3b45170
Compare
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #13408 +/- ##
===================================================
+ Coverage 75.42791% 76.47110% +1.04319%
===================================================
Files 3230 3240 +10
Lines 639213 640319 +1106
Branches 47303 47410 +107
===================================================
+ Hits 482145 489659 +7514
+ Misses 148044 147077 -967
+ Partials 9024 3583 -5441
Flags with carried forward coverage won't be shown. Click here to find out more. 🚀 New features to boost your workflow:
|
918e60d
to
b83e93b
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR introduces a new dark-mode–aware button rendering system alongside minor tweaks to the toolstrip renderer.
- Adds four sub-renderers (
Standard
,Flat
,Popup
,System
) underButtonDarkModeRenderer
for WinForms buttons in dark mode. - Updates
ButtonBaseAdapter
/ButtonBase
to select and use the new dark-mode adapter when dark mode is enabled. - Toggles a flag in
ToolStripSystemRenderer
to use the alternative dark-mode path and consolidates pen creation in its dark-mode border renderer.
Reviewed Changes
Copilot reviewed 18 out of 18 changed files in this pull request and generated 4 comments.
Show a summary per file
File | Description |
---|---|
src/.../ToolStripSystemRenderer.cs | Flip isSystemDefaultAlternative flag to true |
src/.../ToolStripSystemDarkModeRenderer.cs | Hoist borderPen creation out of branches |
src/.../Controls/Buttons/DarkModeRenderer/* | New dark-mode renderer classes & factory |
src/.../Controls/Buttons/DarkModeRenderer/ButtonDarkModeRenderer.cs | Core dispatcher for dark-mode button rendering |
src/.../ButtonInternal/ButtonDarkModeAdapter.cs | New adapter to route RenderButton calls |
src/.../ButtonInternal/ButtonBaseAdapter.cs & ButtonBase.cs | Hook up dark-mode adapter via OwnerDraw and CreateDarkModeAdapter |
Winforms.sln | (Unrelated) added AI-Prompts and GDI solution items |
Comments suppressed due to low confidence (1)
Winforms.sln:212
- Solution file now includes AI-Prompts and GDI entries unrelated to WinForms code. Remove these to avoid polluting the main solution.
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AI-Prompts"...
...ndows.Forms/System/Windows/Forms/Controls/Buttons/DarkModeRenderer/ButtonDarkModeRenderer.cs
Outdated
Show resolved
Hide resolved
...ndows.Forms/System/Windows/Forms/Controls/Buttons/DarkModeRenderer/ButtonDarkModeRenderer.cs
Outdated
Show resolved
Hide resolved
private const int DefaultButtonBorderThickness = 0; | ||
private const int NonDefaultButtonBorderThickness = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] Both border-thickness constants are zero, making the switch on isDefault
a no-op. Either consolidate into one constant or clarify if different values are intended.
private const int DefaultButtonBorderThickness = 0; | |
private const int NonDefaultButtonBorderThickness = 0; | |
private const int ButtonBorderThickness = 0; |
Copilot uses AI. Check for mistakes.
...s.Forms/System/Windows/Forms/Controls/Buttons/DarkModeRenderer/FlatButtonDarkModeRenderer.cs
Outdated
Show resolved
Hide resolved
b83e93b
to
c505a15
Compare
b999aad
to
56a437d
Compare
61aefaa
to
23ff881
Compare
86d8e0b
to
b1bfd78
Compare
b1bfd78
to
f0e353c
Compare
35093c5
to
499a4d8
Compare
c0170e5
to
1528a35
Compare
499a4d8
to
5619f0b
Compare
b993552
to
4a291d5
Compare
5619f0b
to
19fc0c4
Compare
bb6f045
to
6e80645
Compare
2b83abf
to
8a6ad4a
Compare
8a6ad4a
to
1cc912f
Compare
e9a373a
to
08204aa
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is more feedback to address (maybe an hour or two of work), but nothing that blocks getting this in for the next preview. Approving with the understanding that we'll address with a follow up issue to track.
/// <term><see cref="FlatStyle.Standard"/></term> | ||
/// <description> | ||
/// The default style. The button is not wrapping the system button. It is rendered using the StandardButton adapter. | ||
/// VisualStyleRenderer from the OS is used for certain parts, which may have issues in high-resolution scenarios. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cref for VisualStyleRenderer?
/// <term><see cref="FlatStyle.System"/></term> | ||
/// <description> | ||
/// The button wraps the system button and is not owner-drawn. | ||
/// No <c>OnPaint</c>, <c>OnPaintBackground</c>, or adapter is involved. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would be better as a cref for the events.
/// <description> | ||
/// The default style. The button is not wrapping the system button. It is rendered using the StandardButton adapter. | ||
/// VisualStyleRenderer from the OS is used for certain parts, which may have issues in high-resolution scenarios. | ||
/// Dark mode works to some extent, but improvements are needed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What improvements are needed? As public documentation it would be better to say something like.
/// Dark mode works to some extent, but improvements are needed. | |
/// Partially supports dark mode. |
And then make sure we have tracking issues around what we might be able to address.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// <description> | ||
/// The button is fully owner-drawn. No rendering is delegated to the OS, not even VisualStyleRenderer. | ||
/// This style works well in dark mode and is fully controlled by the application. | ||
/// 3D effects are expected but may not be rendered; consider revisiting for meaningful styling. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does this mean for the user? Does it really work well if 3d effects don't render? I don't really understand this, but I would guess you might be saying something like:
"Generally, works well in dark mode, with the exception of ..."
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note, that this is describing a status quo, which has been changing over the time up to a point where the docs often no longer reflect the presents. The docs are still stating things like certain Borderstyle effects (not only for buttons), which are no longer true in general. For example, many controls have a BorderStyle 3D, which no longer resemble any 3D effect. But then we have control like the RichTextBox, which still does that in the same way as under Windows 95.
That was the original motivation behind the Visual Styles, which became then strongly connected to the dark mode changes, and the reason, I would argue to revisit the VisualStyles
approach, should it come to it (resources, mandate).
/// <description> | ||
/// The button wraps the system button and is not owner-drawn. | ||
/// No <c>OnPaint</c>, <c>OnPaintBackground</c>, or adapter is involved. | ||
/// In dark mode, this style is used as a fallback for Standard-style buttons. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What are standard style buttons? Is there a cref that could be used here? Or another link that users can follow to understand this from docs?
@@ -64,6 +64,9 @@ _ when color.GetBrightness() > 0.5 => ControlPaint.Dark(color, 0.2f), | |||
}; | |||
|
|||
/// <summary> | |||
/// Creates a dark mode compatible brush. Important: | |||
/// Always do: `using var brush = GetDarkModeBrush(color)`, | |||
/// since you're dealing with a cached brush => scope, really! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comment is duplicated. Please do not use !
in comments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, damn. My bad. Noted.
@@ -68,7 +68,9 @@ protected override void OnPaint(PaintEventArgs pevent) | |||
{ | |||
base.OnPaint(pevent); | |||
|
|||
if (Application.RenderWithVisualStyles & _useComboBoxTheme) | |||
#pragma warning disable WFO5001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. | |||
if (!Application.IsDarkModeEnabled |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A comment why we're skipping for dark mode would be useful.
@@ -96,6 +96,9 @@ public Appearance Appearance | |||
else | |||
{ | |||
UpdateStyles(); | |||
|
|||
// UpdateStyles should also update the UserDraw flag, but it doesn't. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"Should" seems to imply that there is a follow up here. Is there something we should be tracking?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's maybe phrased a bit awkwardly. We need to update the docs rather. They imply that it should. It's now added to the list, and I ping @adegeo for it and talk to him!
graphics.DrawPath(focusPen, focusPath); | ||
} | ||
|
||
/// <summary> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Shouldn't need to add comments to overrides unless you need to change the docs. The less copies of documentation we have around, the less likely we are to have some copies lacking updates.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added that rule to the Copilot-Instructions. It should not happen again (if you use that to generate the XML), and I will be working to get that into account for pure refactotoring purposes.
/// <summary> | ||
/// Cache of colors for different button states in dark mode. | ||
/// </summary> | ||
internal class DarkModeButtonColors |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where is this overridden? If it isn't it should be sealed and virtuals removed. In either case, the constructor should not be public.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's internal, and I kept the extensibility for us internally in mind. At that time, I did not know, if sealing a class would be final, but if I understood correctly, it is not. Added to the list!
Add correlating DarkMode Button renderers to the existing Button renderers.
This way, we are becoming completely independent of the XP-based VisualStyle rendering (and most of all incomplete theming for certain situations, like the Button rendering for 96DPI/100% scaling scenarios).
Fixes #11949.
Note:
For Preview 5, we are very defensively activating the new Renderers, and do a more intensive testing for Preview 6. This means:
Nothing changes, if we're in LightMode. We're not picking up any new Win11 compatible styles for now.
In contrast to the documentation, we're rendering EVERY
FlatMode
ourselves (and have so all along) - also the defaultFlatMode
. That means, ifVisualStyles
are enabled, we're still controlling the paint-pipeline (handlingOnPaint
), we're just delegating certain parts down to theVisualStyleRenderer
(which does not work reliably for dark mode and does not work in 96 DPI altogether.) Our initial assumption that the native Win32 System button doesn't render dark mode correctly is wrong: It does, and it does so in every HighDpi mode (scenario tested by CTI), even with animation support. We are just not using it (and never have), when the user is not explicitly opting in by setting theFlatStyle
toSystem
. This is what the docs need to be updated for.For that reason, for DarkMode and
FlatStyle.Standard
, we're picking up the internalSystemRenderer
, except for cases when we're rendering with images, as theSystemRenderer
is NOT able to render images (and never was). In case of using Images, we're using the new DarkMode FlatRenderer, because that is style-compatible with the SystemRenderer.For the other styles, we're using the new DarkMode renderers respectively (Again, LightMode is not affected), we're not introducing any new Win11 styles for Preview 5.