Most of the VCL components like the TAdvTool* but also simple components as the
TAdvGlowButton are fundamentally broken with mixed DPI usage.
I'll elaborate the issues exemplarily using a TAdvGlowButton as this is a simpler one to fix,
but it perfectivly shows the root of all the DPI related flaws.
For starter to reproduce the issue, you'll have to have at least two screens (virtual should to too).
The important thing is that both need to be set to a different DPI setting (you can do this by changing screen scale to a different value than 100%).
Now take a VCL TButton and a TAdvGlowButton and place them onto a form.
Starting the application with either screen as primary seems to work, due to "fixes" being made.
We will see something like this:
Now as we move the form to the other screen it will look like this:
And now moving it back everything breaks down:
The fundamental flaw is that the components mutate the size values of the Fonts, which is inherently wrong.
You by no means cant ever do this for several reasons. giving few here:
- It is a user (in the context the programmer) controlled value which only he sets and mutates and not a derived value. Here the relative font size he wishes to have.
- You will always potentially have data loss! Think
(x div 3) * 3
you'll lose the remainder! - You can have side effects down the line, think of the Font instance capping its further calculcated value.
If we want derived values, we have to either store and cache them seperatley (TFont can do that already for us btw!) or calculate it on the fly as needed (which you seemed to have tried at some locations).
Now let's dive a little deeper into TAdvGlowButton and fix this one exemplarily.
At around line 4.4k we find:
$IFDEF DELPHI_UNICODE}
{$IFDEF DELPHIXE10_LVL}
procedure TAdvCustomGlowButton.ChangeScale(M, D: Integer; isDpiChange: Boolean);
{$ELSE}
procedure TAdvCustomGlowButton.ChangeScale(M, D: Integer);
{$ENDIF}
begin
inherited;
{$IFDEF DELPHIXE15_LVL}
if not (csDesigning in ComponentState) and not (csLoading in ComponentState) {and not ParentFont} then
begin
Font.Size := MulDiv(Font.Size, M, D);
end;
{$ENDIF}
NotesFont.Size := MulDiv(NotesFont.Size,M,D);
end;
{$ENDIF}
Which exposes the mentioned data loss (leading to the catastrophic failure when moving forth and back).
Fortunateley, as we work with TFont, we have an easy solution here and ask it to take care of the calculating derived (scaled) values. We simple can do this:
{$IFDEF DELPHI_UNICODE}
{$IFDEF DELPHIXE10_LVL}
procedure TAdvCustomGlowButton.ChangeScale(M, D: Integer; isDpiChange: Boolean);
begin
inherited;
{$IFDEF DELPHIXE15_LVL}
if not (csDesigning in ComponentState) and not (csLoading in ComponentState) {and not ParentFont} then
begin
Font.ChangeScale(M, D, isDpiChange);
end;
{$ENDIF}
NotesFont.ChangeScale(M, D, isDpiChange);
end;
{$ENDIF}
(Note for simplicity only the DELPHIXE10_LVL
path is given).
Furthermore, when mapping the font to a custom one we need to apply fixes and change:
fh := -MulDiv(NotesFont.Size, GetDeviceCaps(Canvas.Handle, LOGPIXELSY), 72);
fh := -MulDiv(AFont.Size, FormPPI, 72);
to
fh := -MulDiv(NotesFont.Size, NotesFont.PixelsPerInch, 72);
fh := -MulDiv(AFont.Size, AFont.PixelsPerInch, 72);
at the three locations it is used.
Tats all to fix the button!
Now unfortunateley fixing the TAdvToolBar* is not as straight forward as everything is quite scattered around, there have been attempts to "remedy" the effects of data loss by simply "add one to hopefully make it work again" etc.
Ontop of all comes the whole styling concept.
Theres basically the following ways to deal about this:
- Use the builtin TFont capabilities. This requires however that no TFont instances are shared between two different screens. This might break the Styler approach?
- Cache data yourself (store the derived = scaled values)
- Derive the data right where it is needed ie. do the scaled font size calc right when it is needed.
As it is now it's definitley in an totally unusable state in multi monitor environments with different scales. You'll figure really much bigger issues on the office sample demo depending how bad the different monitor settings mismatch.
This is also the root cause of issues like: Issue with changing screen dpi TADVGlowButton
Kind regards.