Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
73 commits
Select commit Hold shift + click to select a range
15cf48a
Add feature promp,
KlausLoeffelmann May 30, 2026
30d5678
Factor system text scale lookup
KlausLoeffelmann Jul 2, 2026
de9f720
Add system text size APIs
KlausLoeffelmann Jul 2, 2026
5a0ef4d
Add system text size tests
KlausLoeffelmann Jul 2, 2026
cc3da37
Add WinRT UISettings accent-color interop to Core
KlausLoeffelmann Jul 8, 2026
aaea80d
Add Application.GetWindowsAccentColor()
KlausLoeffelmann Jul 8, 2026
ba8d3b5
Add TreeView.NodeLeading API
KlausLoeffelmann Jul 2, 2026
2fb3a97
Add TreeView.NodeLeading tests
KlausLoeffelmann Jul 2, 2026
a09e0ec
Add the prompts for the new Suspend-Positioning-Painting-Form-Apearan…
KlausLoeffelmann May 20, 2026
b990c30
Add flicker-free mutation API proposal prompt
KlausLoeffelmann May 20, 2026
eba1d0c
Add flicker-free UI mutation APIs
KlausLoeffelmann May 20, 2026
9f399c5
Add tests for flicker-free mutation APIs
KlausLoeffelmann May 20, 2026
9eea1a9
Remove DeferLocationChange; make suspend-mutation hooks explicit-inte…
KlausLoeffelmann Jul 2, 2026
90db3db
Rename FormAppearanceMode to FormRevealMode with Inherit ambient sent…
KlausLoeffelmann Jul 2, 2026
87e872d
Wire FormRevealMode into the VB Application Framework
KlausLoeffelmann Jul 2, 2026
df56079
Update agent skills: build.cmd build tenet, PublicAPI override tracki…
KlausLoeffelmann Jun 13, 2026
e399183
Update agent skills: build.cmd build tenet, PublicAPI override tracki…
KlausLoeffelmann Jun 13, 2026
93a34b9
Add Visual Styles (.NET 11): VisualStylesMode, animation timer, moder…
KlausLoeffelmann Jun 13, 2026
e5ca36a
Fix minor test issue.
KlausLoeffelmann Jun 14, 2026
0438a19
Patch XML docs for TextBoxBase' Padding property.
KlausLoeffelmann Jun 26, 2026
f0a19ce
Add work order for TextBoxBase NC painting port
KlausLoeffelmann Jun 30, 2026
f69de7b
Port TextBoxBase NC-painting + VisualStylesMode chrome onto VisualSty…
KlausLoeffelmann Jun 30, 2026
e47f7e5
Tighten OCE handling in HighPrecisionTimer callback dispatch
KlausLoeffelmann Jun 30, 2026
9b472f7
Add VisualStylesMode.Inherit ambient sentinel for designer serialization
KlausLoeffelmann Jun 30, 2026
5b83663
Add VisualStylesMode.LatestPreview support
KlausLoeffelmann Jul 2, 2026
a6d9673
Gate TextBoxBase padding to modern visual styles
KlausLoeffelmann Jul 2, 2026
a121b3c
Wire VisualStylesMode through VB application defaults
KlausLoeffelmann Jul 2, 2026
8cc1da3
Fix modern Visual Styles NC-paint erasing TextBox client area; refact…
KlausLoeffelmann Jul 4, 2026
b4e3b32
Tighten modern Standard button body/focus-ring spacing
KlausLoeffelmann Jul 8, 2026
28a4498
Replace Popup button style with animated concave key-cap renderer
KlausLoeffelmann Jul 9, 2026
9424ccf
Use modern owner-drawn renderers for Appearance.Button CheckBox/Radio…
KlausLoeffelmann Jul 9, 2026
fb364eb
Position modern toggle switch by CheckAlign instead of TextAlign
KlausLoeffelmann Jul 9, 2026
8aa9614
Add EffectiveVisualStylesMode for High Contrast support
KlausLoeffelmann Jul 10, 2026
4516071
Fix compile break from VisualStylesMode.LatestPreview removal
KlausLoeffelmann Jul 10, 2026
0d215d7
Make Control.VisualStylesMode virtual
KlausLoeffelmann Jul 10, 2026
0af2e37
Pin PropertyGrid hosted controls to classic visual styles
KlausLoeffelmann Jul 10, 2026
8c1a0a6
Honor Padding in RichTextBox under modern visual styles
KlausLoeffelmann Jul 10, 2026
29a8bdf
Add side-by-side up/down buttons for modern VisualStylesMode (A11y)
KlausLoeffelmann Jul 10, 2026
e7beb7e
Draw a unified modern frame around UpDownBase in modern VisualStylesMode
KlausLoeffelmann Jul 10, 2026
1ee2b58
Fix FormRevealMode public API tracking
KlausLoeffelmann Jul 10, 2026
ab1e3e5
Fix popup button release build diagnostics
KlausLoeffelmann Jul 10, 2026
0acbe3e
Cover NodeLeading DPI resynchronization
KlausLoeffelmann Jul 10, 2026
73eec97
Preserve painting suspension across handles
KlausLoeffelmann Jul 10, 2026
4a3188b
Harden modern visual styles behavior
KlausLoeffelmann Jul 10, 2026
173949b
Move component code files to component folder.
KlausLoeffelmann May 19, 2026
ba3f011
Introduce KioskModeManager component.
KlausLoeffelmann May 19, 2026
3c0ab4e
Expand KioskModeManager tests.
KlausLoeffelmann Jul 2, 2026
aa3f048
Harden KioskModeManager lifecycle behavior
KlausLoeffelmann Jul 10, 2026
564b160
Restore modern Visual Styles renderer follow-ups
KlausLoeffelmann Jul 12, 2026
6143cdc
Add LightCoral TextBox NC-paint diagnostic
KlausLoeffelmann Jul 12, 2026
6083654
Merge application API dependency into VisualStyles
KlausLoeffelmann Jul 12, 2026
3386f36
Address VisualStyles rendering follow-ups
KlausLoeffelmann Jul 12, 2026
1b114c4
Fix API tracking after dependency merge
KlausLoeffelmann Jul 12, 2026
7531529
Merge Net11 application APIs
KlausLoeffelmann Jul 12, 2026
30a8fb4
Merge TreeView NodeLeading API
KlausLoeffelmann Jul 12, 2026
82ed669
Merge control rendering APIs
KlausLoeffelmann Jul 12, 2026
ba2da46
Merge KioskModeManager API
KlausLoeffelmann Jul 12, 2026
d72c277
Merge VisualStyles APIs
KlausLoeffelmann Jul 12, 2026
7094137
Clean up integration API tracking
KlausLoeffelmann Jul 12, 2026
6d61f14
Refine modern VisualStyles rendering and layout
KlausLoeffelmann Jul 12, 2026
a24ce19
Adjust the default Paddings for modern VisualStyles.
KlausLoeffelmann Jul 13, 2026
5f6763b
Refine modern Visual Styles chrome geometry
KlausLoeffelmann Jul 13, 2026
2588493
Add flicker-free UI mutation APIs
KlausLoeffelmann May 20, 2026
7a344d2
Introduce KioskModeManager component.
KlausLoeffelmann May 19, 2026
1948232
Refactor KioskModeManager component and introduce
KlausLoeffelmann May 20, 2026
8d099b4
Update agent skills: build.cmd build tenet, PublicAPI override tracki…
KlausLoeffelmann Jun 13, 2026
9464e92
Add Visual Styles (.NET 11): VisualStylesMode, animation timer, moder…
KlausLoeffelmann Jun 13, 2026
0b0c436
Add VisualStylesMode.LatestPreview support
KlausLoeffelmann Jul 2, 2026
f011996
Wire VisualStylesMode through VB application defaults
KlausLoeffelmann Jul 2, 2026
738e5db
Refactor build.cmd for ARM64 and add build.cmd.ps1
KlausLoeffelmann Jul 3, 2026
c064884
Potential fix for pull request finding
KlausLoeffelmann Jul 9, 2026
deab71d
Integration cleanup: API tracking files
KlausLoeffelmann Jul 10, 2026
29e127b
Integration cleanup: resources
KlausLoeffelmann Jul 10, 2026
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
@@ -0,0 +1,180 @@
# Task: Create a new API — `Application` system-text-size awareness — and the respective API proposal

## What to do

Create a **new API proposal / API review issue in the upstream `dotnet/winforms` repo**
(`origin` = my fork, `upstream` = the Microsoft repo where this lands). Write it per the
WinForms repo conventions and the relevant skills for authoring new-API issues. Apply the
skills; don't ask me for boilerplate.

This proposal introduces **runtime awareness of the Windows Accessibility text-size
setting** at the `Application` level, plus a per-`Form` change notification. It is the
**foundation** proposal; a companion `TreeView.NodeLeading` proposal references this one.

## Before you implement anything

**Verify every premise below against current source before committing to the design.**
Verify, don't trust. If any premise is wrong, stop and tell me. Key files (VMR @
`96982699e0dd8c046f397541dc0eb235ea8a4958`):

- `src/winforms/src/System.Windows.Forms.Primitives/src/System/Windows/Forms/Internals/ScaleHelper.cs`
- `src/winforms/src/System.Windows.Forms/System/Windows/Forms/Application.cs`
- `src/winforms/src/System.Windows.Forms/System/Windows/Forms/Application.ThreadContext.cs`
- `src/winforms/src/System.Windows.Forms/System/Windows/Forms/Application.ParkingWindow.cs`
- `src/runtime/src/libraries/Microsoft.Win32.SystemEvents/...` (SystemEvents / UserPreferenceCategory)

## Rationale — surface and complete an existing partial implementation

This is **not** a net-new feature; it **finishes a half-built one**. WinForms already reads
the Accessibility text-size setting, but only once and only for the default font:

`ScaleHelper.ScaleToSystemTextSize(Font?)` reads
`HKEY_CURRENT_USER\Software\Microsoft\Accessibility` → value `TextScaleFactor`
(REG_DWORD, clamped 100–225), and returns the font scaled by `TextScaleFactor / 100`
(or `null` if 100, if the font `IsSystemFont`, or if the OS is < Windows 10 1507). It is
documented in-source as the **Settings → Display → Make Text Bigger** setting.

The gaps: (1) the value is **never surfaced** to developers; (2) it is read **once**, at
default-font construction, and the app **never reacts** when the user changes the setting at
runtime; (3) there is **no notification** mechanism. This proposal fills those three gaps.

## Three-knob disambiguation (MANDATORY callout — reviewers will conflate these)

Windows surfaces three *different* sizing mechanisms; the Settings UI even puts two on one
page (`System → Display → Custom scaling` shows a "Custom scaling 100–500%" box AND a
"Text size" link). The proposal MUST state plainly which one it targets:

1. **Display / Custom scaling (100–500%)** → this is **DPI**. Already handled by
`HighDpiMode` and the DPI events (`WM_DPICHANGED`, `Control.DpiChanged*`). **NOT** this
proposal.
2. **Accessibility → Text size (100–225%)** → registry `TextScaleFactor` under
`HKCU\Software\Microsoft\Accessibility`; WinRT `UISettings.TextScaleFactor` /
`TextScaleFactorChanged`. **THIS is the target.** Independent of DPI.
3. **Legacy pre-Win10 per-element text sizing** (title bars/menus) → **removed** in Windows
10 1703; the Accessibility slider replaced it. Mentioned only to close the loop.

State explicitly that `Application.SystemTextSize` reflects **#2 only**, and is orthogonal to
DPI (#1).

## Proposed API

### `Application` (process-static)

- **`public static double SystemTextSize { get; }`** — the current Accessibility text-scale
factor (1.0–2.25; i.e. `TextScaleFactor / 100`). **Live getter** — re-reads the value, does
not cache, because it is a system setting that changes at runtime. Well-defined regardless
of whether any `Form` exists or how many UI threads are running (it is process-global).
- **`public static SystemTextSizeAwareness SystemTextSizeAwareness { get; set; }`** — the
mode. **Enum, not bool**, deliberately, to reserve room for a future `Automatic`:
- `Unaware` (default) — no notification raised; fully back-compatible, nothing changes.
- `Notify` — raise change notifications (see below); the app decides how to respond.
- *(reserved, NOT implemented now: `Automatic` — framework re-flows for you. Reserving the
enum slot now avoids a future breaking bool→enum change, the same lesson `HighDpiMode`
learned.)*
- **`public static event EventHandler? SystemTextSizeChanged`** — fires once per process when
the setting changes (only when awareness is `Notify`).

### `Form` (instance)

- **`public event EventHandler? SystemTextSizeChanged`** — instance event, raised on each
top-level `Form` when the setting changes.
- **`protected virtual void OnSystemTextSizeChanged(EventArgs e)`** — overridable, fires the
instance event via the `EventHandlerList` pattern (`Events[s_systemTextSizeChangedEvent]`).

## The trigger architecture (the leak-critical part — get this exactly right)

A naive design — a static `Application.SystemTextSizeChanged` that `Form`s/`Control`s
subscribe to — **leaks**: the static event strongly roots every subscriber, so no `Form`
that subscribes is ever collected. Avoid this by **mirroring the DPI architecture**, where
`Control`/`Form` learn of DPI changes from their **own `WndProc`** (`WM_DPICHANGED` →
`OnDpiChanged` → instance event via `EventHandlerList`), **not** from a static subscription.

Verified facts that constrain the design:

- **`WM_SETTINGCHANGE` is broadcast** (`HWND_BROADCAST`) and delivered directly to top-level
windows' `WndProc`s — it bypasses the thread message queue, so **`IMessageFilter` does NOT
see it.** Do not use a message filter.
- **The WinForms parking window is message-only** (`CreateParams.Parent = HWND_MESSAGE`).
Message-only windows are **excluded from broadcasts**, so the parking window **cannot**
receive `WM_SETTINGCHANGE`. Do not use it.
- **`Application` has no `MainForm`.** The main form lives on `ApplicationContext` (per-run,
per-UI-thread), can be `null` (tray/loop-only apps), and is mutable (splash→main handoff).
So the main form is **not** a reliable receiver. Do not anchor the app-level event to it.
- **`SystemEvents` already owns a hidden top-level broadcast-receiving window** (its
`.NET-BroadcastEventWindow`), and exposes `UserPreferenceChanged` with a
`UserPreferenceCategory` (the relevant value is `Accessibility`, which is **coarse** — it
covers any accessibility change, so you must re-read `TextScaleFactor` and diff to confirm
it was text-scale).

**Resulting design:**

- **App-level:** `Application` makes **one internal, process-lifetime** subscription to
`SystemEvents.UserPreferenceChanged`, filters `Category == Accessibility`, re-reads
`TextScaleFactor`, diffs against the cached value, and if changed raises the static
`SystemTextSizeChanged`. This is a single framework-static→framework-static link — it does
**not** root any user object, so it is **not** the leak hazard. Reuses the existing hidden
broadcast window; **no new HWND** required.
- **Form-level:** each top-level `Form` handles `WM_SETTINGCHANGE` in its **own `WndProc`**
(it is a broadcast — every top-level window receives it), re-reads + diffs, and raises its
**instance** `SystemTextSizeChanged` via `OnSystemTextSizeChanged`. `Form`s do **not**
subscribe to `Application` — no rooting, lifetime = the window.

Caveat to document: there is **no dedicated `WM_TEXTSCALECHANGED`** message. Both paths must
recognize a *relevant* change by re-reading `TextScaleFactor` and comparing, not by the
message alone.

## Aids vs. leave-it-to-the-user

**Notify-only. No automatic re-layout / font-rescaling aids in this proposal.** Reasons:
text scale interacts with `AutoScaleMode`, anchored/docked layout, and explicitly-set fonts
in app-specific ways; a generic "scale all fonts by the factor" helper breaks more than it
fixes. The reserved `Automatic` enum value is exactly where such behavior would live later.
`Notify` gives the developer the factor and the event; they decide. (Consistent with the
companion `NodeLeading` proposal's conservative-default philosophy.)

## Why this matters across controls (evidence — include the matrix)

Multiple text-measuring controls derive item/row/tile extents from a `Font` that today only
reacts to the text-size setting once at startup (via `ScaleToSystemTextSize` on the default
font) and never again. So **any single cached height scalar is wrong the moment text size
changes at runtime** — which is the core argument for runtime awareness:

- **ListBox / ComboBox** — have `MeasureItem` + `OwnerDrawVariable` (a real per-item measure
hatch) and `ItemHeight`. Their gap is the legacy default base calc + the missing runtime
text-scale reaction — i.e. exactly this proposal.
- **TreeView** — has neither `MeasureItem` nor a wrapped native height API; only a uniform
native item height. Worst-positioned for a managed fix; addressed by the companion
`TreeView.NodeLeading` proposal, which depends on this one.
- **ListView (Details)** — no `MeasureItem`, no native row-height message; row height is
comctl-computed from `SmallImageList` + control font, while per-item/subitem fonts are
honored via `NM_CUSTOMDRAW` (`CDRF_NEWFONT`). Userland workarounds (phantom `SmallImageList`;
`LVS_OWNERDRAWFIXED` + one-shot reflected `WM_MEASUREITEM`; "inflate control font / shrink
item fonts") each have holes (header leak, set-once, exhaustive per-item font setting,
owner-draw-all). **No clean complete userland solution exists** — strengthening the case
that text-size reaction belongs in the framework.

## XML doc requirements

- Document that `SystemTextSize` is the **Accessibility text-size** factor (Settings →
Display → Make Text Bigger), **not** DPI/display scaling, and is process-global / live.
- Document the `Unaware`/`Notify` semantics and that `Automatic` is reserved for future use.
- Document the no-rooting design note on the static event (so consumers understand instance
vs. static).

## Open questions for review

- Should `SystemTextSize` be `double` (1.0–2.25) or expose the raw int percent (100–225)?
- Behavior on OS < Windows 10 1507 (where `ScaleToSystemTextSize` no-ops): `SystemTextSize`
returns 1.0 and no events fire?
- Whether to also expose the value/event on `Application` only, leaving `Form` consumers to
use their own `WndProc` override — or provide the `Form` instance event as proposed
(recommended, for parity with the DPI event model).

## Output

The upstream issue per the skills: summary; the "complete a partial implementation"
rationale; the mandatory three-knob disambiguation; the proposed API; the leak-safe trigger
architecture with the four verified constraints (broadcast vs. IMessageFilter, message-only
parking window, no MainForm on Application, SystemEvents reuse); Notify-only stance; the
cross-control matrix; XML-doc requirements; open questions. Flag anything the source
contradicts.
Loading
Loading