TAdvGlowButton: cached form PPI is never refreshed when ParentFont = False, so captions are painted at the wrong size after a DPI change
Environment
- TMS VCL UI Pack 13.6.5.1
- RAD Studio / Delphi 13.1, VCL, Win32 and Win64
- Windows 11 (build 26200)
- Application manifest: Per-Monitor V2 (
<AppDPIAwarenessMode>PerMonitorV2</AppDPIAwarenessMode>) - Two monitors: primary 1280x800 at 125% (120 dpi), secondary 1920x1080 at 100% (96 dpi)
Line numbers below refer to AdvGlowButton.pas of 13.6.5.1. The surrounding code is unmodified.
Summary
TAdvCustomGlowButton caches the PPI of its parent form in FParentFormPPI and uses that cached value — not Font.Height — to decide the size at which the caption is painted. The cache is filled in CreateWnd and refreshed during Paint only when ParentFont = True.
For a button whose font comes from the DFM (ParentFont = False, which is the normal case), the cache stays frozen with the DPI of the monitor where the window was created. After moving the window to a monitor with a different DPI, the VCL correctly rescales the button and its font, but the component keeps painting the caption at the old size.
Steps to reproduce
- Two monitors with different scaling (we use 125% and 100%).
- A form with
TAdvGlowButtoncontrols that haveParentFont = Falseand their ownFontin the DFM, with captions long enough to nearly fill the button width. - Start the application on the 125% monitor.
- Drag the window to the 100% monitor.
Expected
The caption is painted with the font size the VCL has just scaled the button to, and keeps fitting inside the button.
Actual
The caption is painted at the size of the original monitor and no longer fits. Measured on our POS screen:
| Button width | Font.Height |
Painted at | |
|---|---|---|---|
| At 120 dpi (window created here) | 88 px | -15 | -15 (correct) |
| After dragging to 96 dpi | 70 px (correctly rescaled) | -12 (correctly rescaled) | -15 (wrong) |
The visible result is that exactly the last letter of each caption is lost: "Cantidad" is painted as "Cantida", "Referencia" as "Referenci", "Albarán" as "Albará". Dragging the window back restores them.
Note the truncation is not caused by the button being too narrow: it is GDI+ SetTrimming applied to text that is being painted larger than it should be. Making the buttons wider does not fix it.
Starting the application directly on the 100% monitor does not show the problem, because then the cache is created with the right value.
Root cause
The cache is filled in TAdvCustomGlowButton.CreateWnd (~3576):
FParentForm := GetParentForm(Self);
{$IFDEF DELPHIXE15_LVL}
FParentFormPPI := FParentForm.CurrentPPI;
{$ENDIF}
...
if ParentFont then
FParentFormPPI := 0;
and refreshed in TAdvCustomGlowButton.Paint (~4755) — but only in the ParentFont case:
FParentForm := GetParentForm(Self);
{$IFDEF DELPHIXE15_LVL}
if ParentFont then
FParentFormPPI := FParentForm.CurrentPPI;
{$ENDIF}
Paint passes it to DrawVistaButton as the FormPPI parameter (~5241), where the actual font height is computed from Font.Size in points and that PPI:
if FormPPI > 0 then
fh := -MulDiv(AFont.Size, FormPPI, 72);
So with ParentFont = True the value is refreshed on every paint and everything works; with ParentFont = False it is written once at window creation and never updated, including across WM_DPICHANGED. TAdvCustomGlowButton.ChangeScale does not touch it.
GetButtonSize (~7716) uses the same cached value, so measuring is affected as well.
Suggested fix
Refresh the cache on a real DPI change, in TAdvCustomGlowButton.ChangeScale, right after inherited:
{$IFDEF DELPHIXE10_LVL}
if isDpiChange and (M > 0) and (FParentFormPPI > 0) then
FParentFormPPI := M;
{$ENDIF}
The FParentFormPPI > 0 guard keeps the ParentFont = True case untouched (there the value is 0 by design and the painting path does not use it), and the order with respect to CreateWnd does not matter: if the handle does not exist yet the value is 0, the guard skips it, and CreateWnd will set it correctly afterwards.
We have been running this change since August 2026 and it fixes the symptom on both monitors and while dragging the window between them.
An equivalent fix would be to refresh it unconditionally in Paint (dropping the if ParentFont guard there), which would also cover any path that changes the form DPI without going through ChangeScale.
Related
While investigating this we also hit the issue reported in Issue with changing screen dpi TADVGlowButton — ChangeScale doing Font.Size := MulDiv(Font.Size, M, D) after inherited has already scaled the font, which scales it a second time when ParentFont = False (x1.5625 instead of x1.25 at 125%). That one is already acknowledged in that thread, so we are not reporting it again; we mention it because both problems live in the same method and a fix for one should not undo the other. They are independent: the double scaling affects Font.Size, this report is about the cached PPI the painting code uses instead of it.
Why the usual workaround is not an option for us
Setting the manifest to System Aware, which is the advice we found in the support center for high-DPI trouble, would mean giving up Per-Monitor V2 for the whole application. Our application is fully Per-Monitor V2 and the rest of the VCL behaves correctly under it.
Appearance.SystemFont = False was suggested in the thread linked above; in 13.6.5.1 that property only forces the font name (Segoe UI / Tahoma) in Paint, so it has no effect on this.