Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
b014750
Add feature promp,
KlausLoeffelmann May 30, 2026
91e42a2
Factor system text scale lookup
KlausLoeffelmann Jul 2, 2026
d29effa
Add system text size APIs
KlausLoeffelmann Jul 2, 2026
a16e387
Add system text size tests
KlausLoeffelmann Jul 2, 2026
781716c
Add WinRT UISettings accent-color interop to Core
KlausLoeffelmann Jul 8, 2026
f6eb188
Add Application.GetWindowsAccentColor()
KlausLoeffelmann Jul 8, 2026
3c120aa
Add TreeView.NodeLeading API
KlausLoeffelmann Jul 2, 2026
59869f7
Add TreeView.NodeLeading tests
KlausLoeffelmann Jul 2, 2026
d85ab15
Add the prompts for the new Suspend-Positioning-Painting-Form-Apearan…
KlausLoeffelmann May 20, 2026
ba281ad
Add flicker-free mutation API proposal prompt
KlausLoeffelmann May 20, 2026
e4e0948
Add flicker-free UI mutation APIs
KlausLoeffelmann May 20, 2026
3c2ddf9
Add tests for flicker-free mutation APIs
KlausLoeffelmann May 20, 2026
93a068e
Remove DeferLocationChange; make suspend-mutation hooks explicit-inte…
KlausLoeffelmann Jul 2, 2026
5d4cb8b
Rename FormAppearanceMode to FormRevealMode with Inherit ambient sent…
KlausLoeffelmann Jul 2, 2026
3fc0c2c
Wire FormRevealMode into the VB Application Framework
KlausLoeffelmann Jul 2, 2026
b0e8702
Update agent skills: build.cmd build tenet, PublicAPI override tracki…
KlausLoeffelmann Jun 13, 2026
5355ae0
Move component code files to component folder.
KlausLoeffelmann May 19, 2026
5207f4d
Introduce KioskModeManager component.
KlausLoeffelmann May 19, 2026
297579f
Refactor KioskModeManager component and introduce
KlausLoeffelmann May 20, 2026
b236582
Make BuildAssist locate vswhere.exe by absolute path
KlausLoeffelmann Jun 14, 2026
436d0b9
Improve and streamline APIs.
KlausLoeffelmann Jun 20, 2026
4a7dbf8
Refine KioskModeManager behavior.
KlausLoeffelmann Jul 2, 2026
fa0ee13
Expand KioskModeManager tests.
KlausLoeffelmann Jul 2, 2026
77e387f
Update agent skills: build.cmd build tenet, PublicAPI override tracki…
KlausLoeffelmann Jun 13, 2026
0863a77
Add Visual Styles (.NET 11): VisualStylesMode, animation timer, moder…
KlausLoeffelmann Jun 13, 2026
9cae2d9
Fix minor test issue.
KlausLoeffelmann Jun 14, 2026
5cb2581
Patch XML docs for TextBoxBase' Padding property.
KlausLoeffelmann Jun 26, 2026
5162aca
Add work order for TextBoxBase NC painting port
KlausLoeffelmann Jun 30, 2026
3c18a82
Port TextBoxBase NC-painting + VisualStylesMode chrome onto VisualSty…
KlausLoeffelmann Jun 30, 2026
92ee420
Tighten OCE handling in HighPrecisionTimer callback dispatch
KlausLoeffelmann Jun 30, 2026
f6db591
Add VisualStylesMode.Inherit ambient sentinel for designer serialization
KlausLoeffelmann Jun 30, 2026
8d93e9d
Add VisualStylesMode.LatestPreview support
KlausLoeffelmann Jul 2, 2026
68ca9cc
Gate TextBoxBase padding to modern visual styles
KlausLoeffelmann Jul 2, 2026
90c1db5
Wire VisualStylesMode through VB application defaults
KlausLoeffelmann Jul 2, 2026
c74bd14
Fix modern Visual Styles NC-paint erasing TextBox client area; refact…
KlausLoeffelmann Jul 4, 2026
c5ec118
Tighten modern Standard button body/focus-ring spacing
KlausLoeffelmann Jul 8, 2026
ef471ab
Replace Popup button style with animated concave key-cap renderer
KlausLoeffelmann Jul 9, 2026
fde31a6
Use modern owner-drawn renderers for Appearance.Button CheckBox/Radio…
KlausLoeffelmann Jul 9, 2026
b853cea
Position modern toggle switch by CheckAlign instead of TextAlign
KlausLoeffelmann Jul 9, 2026
0fa360b
Refactor build.cmd for ARM64 and add build.cmd.ps1
KlausLoeffelmann Jul 3, 2026
6473a66
Potential fix for pull request finding
KlausLoeffelmann Jul 9, 2026
1cdb159
Add EffectiveVisualStylesMode for High Contrast support
KlausLoeffelmann Jul 10, 2026
666210e
Fix compile break from VisualStylesMode.LatestPreview removal
KlausLoeffelmann Jul 10, 2026
223eacf
Make Control.VisualStylesMode virtual
KlausLoeffelmann Jul 10, 2026
e97cb71
Pin PropertyGrid hosted controls to classic visual styles
KlausLoeffelmann Jul 10, 2026
b38cdcb
Honor Padding in RichTextBox under modern visual styles
KlausLoeffelmann Jul 10, 2026
ea1f572
Add side-by-side up/down buttons for modern VisualStylesMode (A11y)
KlausLoeffelmann Jul 10, 2026
89121da
Draw a unified modern frame around UpDownBase in modern VisualStylesMode
KlausLoeffelmann Jul 10, 2026
196936c
Integration cleanup: API tracking files
KlausLoeffelmann Jul 10, 2026
f156625
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