Goal
Replace the current flat, ForeColor-driven border and the primitive bottom-clipped focus ring on VisualStylesMode.Net11 editable controls with a proper per-state stroke model: a WinUI-style elevation border with distinct Rest, Hover, Focused, Disabled, and ReadOnly treatments; colors derived independently of ForeColor via linear-light overlay compositing; a stronger and thicker bottom elevation edge; an accent focus edge at >= 2 DIP; and hover tracking. High Contrast uses system colors. This issue is the tracking item and definition of done for that work.
Current state (base af0c793)
Editable controls under Net11 (TextBox, MaskedTextBox, RichTextBox, all via TextBoxBase) paint their modern non-client frame in TextBoxBase.OnNcPaint:
- One flat stroke: the border color is the control's
ForeColor (adornerColor = ForeColor), one pen, one thickness on all four sides. No elevation edge.
- Focus is a bottom-clipped copy of the same rounded stroke at the same thickness, color animated
ForeColor -> accent (AnimatedFocusIndicatorRenderer.DrawRoundedFocusIndicator). FixedSingle uses repeated horizontal bottom lines.
- Rest, Hover, Disabled, and ReadOnly are visually identical (all
ForeColor); Hover is not tracked.
- The existing shared blend (
PopupButtonColorMath.Blend) interpolates encoded sRGB, so it cannot produce perceptually-correct overlays; new linear-light math is required.
Scope
TextBoxBase's Net11 non-client paint path: TextBox, MaskedTextBox, RichTextBox.
Out of scope (separate follow-ups)
- ComboBox: a WM_PAINT client-area adapter, not an NC-paint caller.
- UpDownBase / UpDownEdit: separate client-area implementation.
Coordinate a shared visual target with these so a TextBox does not diverge from an adjacent ComboBox or NumericUpDown.
Plan (one migration step at a time)
Behavior-preserving refactor first (Steps 1 and 2), then the one visible change (Step 3). The step and sub-step labels below are the reference scheme used across the linked PRs and comments.
Step 1: Introduce the model, wired to nothing. Add a ModernFieldStrokeState enum, a ModernFieldStroke record, a context, a GetStroke resolver (state precedence Disabled > Focused > ReadOnly > Hover > Rest), and a per-control cache, with a compatibility mapping that reproduces today's pixels, plus unit tests. No paint path calls it; zero visual diff.
Step 2: Route the paint through the resolver. Change TextBoxBase.OnNcPaint to obtain its stroke from GetStroke, with the compatibility mapping still on. Still zero visual diff (prove it with a pixel baseline captured before this step).
Step 3: Apply the real visuals. Deliverable 1 does this as a single commit; proposed here as three independently reviewable sub-steps:
- Step 3a (color model): remove ForeColor from the stroke, add linear-light overlay compositing, and apply the Rest, Disabled, and ReadOnly colors, plus the High Contrast bypass.
- Step 3b (focus): the accent-colored bottom edge and the DPI-scaled focus thickness (at least 2 DIP).
- Step 3c (hover): the new hover tracking and repaint, plus the Hover state color.
Open decisions
- High Contrast focus color: keep
SystemColors.Highlight (current) vs switch to SystemColors.ControlText. Accessibility-affecting; recommend keeping Highlight unless validated.
- Focus thickness:
max(2 DIP, configured focus metric); and whether the thickness change snaps or animates (color already animates 200 ms).
- Hover detection mechanism for the non-client border.
- Shared visual target with the out-of-scope ComboBox / UpDown.
Related
Goal
Replace the current flat, ForeColor-driven border and the primitive bottom-clipped focus ring on VisualStylesMode.Net11 editable controls with a proper per-state stroke model: a WinUI-style elevation border with distinct Rest, Hover, Focused, Disabled, and ReadOnly treatments; colors derived independently of ForeColor via linear-light overlay compositing; a stronger and thicker bottom elevation edge; an accent focus edge at >= 2 DIP; and hover tracking. High Contrast uses system colors. This issue is the tracking item and definition of done for that work.
Current state (base af0c793)
Editable controls under Net11 (TextBox, MaskedTextBox, RichTextBox, all via TextBoxBase) paint their modern non-client frame in
TextBoxBase.OnNcPaint:ForeColor(adornerColor = ForeColor), one pen, one thickness on all four sides. No elevation edge.ForeColor-> accent (AnimatedFocusIndicatorRenderer.DrawRoundedFocusIndicator). FixedSingle uses repeated horizontal bottom lines.ForeColor); Hover is not tracked.PopupButtonColorMath.Blend) interpolates encoded sRGB, so it cannot produce perceptually-correct overlays; new linear-light math is required.Scope
TextBoxBase's Net11 non-client paint path: TextBox, MaskedTextBox, RichTextBox.
Out of scope (separate follow-ups)
Coordinate a shared visual target with these so a TextBox does not diverge from an adjacent ComboBox or NumericUpDown.
Plan (one migration step at a time)
Behavior-preserving refactor first (Steps 1 and 2), then the one visible change (Step 3). The step and sub-step labels below are the reference scheme used across the linked PRs and comments.
Step 1: Introduce the model, wired to nothing. Add a
ModernFieldStrokeStateenum, aModernFieldStrokerecord, a context, aGetStrokeresolver (state precedence Disabled > Focused > ReadOnly > Hover > Rest), and a per-control cache, with a compatibility mapping that reproduces today's pixels, plus unit tests. No paint path calls it; zero visual diff.Step 2: Route the paint through the resolver. Change
TextBoxBase.OnNcPaintto obtain its stroke fromGetStroke, with the compatibility mapping still on. Still zero visual diff (prove it with a pixel baseline captured before this step).Step 3: Apply the real visuals. Deliverable 1 does this as a single commit; proposed here as three independently reviewable sub-steps:
Open decisions
SystemColors.Highlight(current) vs switch toSystemColors.ControlText. Accessibility-affecting; recommend keeping Highlight unless validated.max(2 DIP, configured focus metric); and whether the thickness change snaps or animates (color already animates 200 ms).Related
ForeColorwith a sharedModernControlColorMath.TextControlBorderColorin TextBoxBase, ComboBox.ModernComboAdapter, and UpDownBase. This is the first narrow step of this task and should land first; afterward, Step 1's compatibility mapping rebaselines its resting and disabled endpoints ontoTextControlBorderColor/GetDisabledBorderColor.