Ian Boyd
Ian Boyd

Reputation: 256999

Delphi: Handling user's font preference

I've come up with the call that gets the user's UI font preference (as opposed to Borland's hard-coded choice of "MS Sans Serif").

Let's pretend the user's font preference is:

Segoe Print, 15pt

I set the font of all items, on all forms, in all applications to:

Segoe Print, 15pt

Problem is that things are now cut off. Buttons are too small - too narrow, too short. Text in labels is cut off, etc..

The form has it's Scaled property, but that doesn't change depending on font sizes. The scaled property scaled the form when it is serialized in based on the height of the numeral "0".

I can't find anything in the help for how Borland intended me to support the user's Windows application preferences.

How do I handle user font preferences?

Note: I cross posted this from Embargadero's news group server, since Embargadero's news server seems to be dying, or censoring, or broken, or requiring a login.


Update 1

i'm talking about a user's font preference, not DPI settings. i.e.: imagine the following language neutral pseudo-code:

procedure TForm1.FormCreate(Sender: TObject);
var
    FontFace: string;
    FontHeight: Integer;
begin
    GetUserFontPreference(out FontFace, out FontHeight);
    Self.Font.Name := FontFace;
    Self.Font.Height := FontHeight;
end;

Note: This isn't my actual code (it is language neutral pseudo-code after all). But additionally, you need to recursivly go through every control on the form, changing the font when it needs to be changed. When a font has a different style applied than its parent (e.g. bold), and no longer inherits from its parent, it needs to be manually set.


As per lkessler's request, here's the code to retrieve the user's UI font preference from Windows:

procedure GetUserFontPreference(out FaceName: string; out PixelHeight: Integer);
var
    lf: LOGFONT;
begin
    ZeroMemory(@lf, SizeOf(lf));
     //Yes IconTitleFont (not SPI_GETNONCLIENTMETRICS MessageFont)
    if SystemParametersInfo(SPI_GETICONTITLELOGFONT, SizeOf(lf), @lf, 0) then
    begin
        FaceName := PChar(Addr(lf.lfFaceName[0]));
        PixelHeight := lf.lfHeight;
    end
    else
    begin
        {
            If we can't get it, then assume the same non-user preferences that
            everyone else does.
        }
        FaceName := 'MS Shell Dlg 2';
        PixelHeight := 8;
    end;
end;

Related questions

Upvotes: 11

Views: 6941

Answers (6)

user55581
user55581

Reputation:

I like using this function. It's based on Graphics.GetFontData

procedure SystemFont(Font: TFont);
var
  LogFont: TLogFont;
begin
  if SystemParametersInfo(SPI_GETICONTITLELOGFONT, SizeOf(TLogFont), @LogFont, 0) then
    begin
      Font.Height := LogFont.lfHeight;
      Font.Orientation := LogFont.lfOrientation;
      Font.Charset := TFontCharset(LogFont.lfCharSet);
      Font.Name := PChar(@LogFont.lfFaceName);

      Font.Style := [];

      if LogFont.lfWeight >= FW_BOLD then
        Font.Style := Font.Style + [fsBold];

      if LogFont.lfItalic = 1 then
        Font.Style := Font.Style + [fsItalic];

      if LogFont.lfUnderline = 1 then
        Font.Style := Font.Style + [fsUnderline];

      if LogFont.lfStrikeOut = 1 then
        Font.Style := Font.Style + [fsStrikeOut];

      case LogFont.lfPitchAndFamily and $F of
        VARIABLE_PITCH: Font.Pitch := fpVariable;
        FIXED_PITCH: Font.Pitch := fpFixed;
        else Font.Pitch := fpDefault;
      end;
    end;
end;

You simply use it on all TForm.OnCreate events. An alternative could be to create a new class that does this on creation or looping Screen.Forms.

If you change some of the properties of the default font of any of the controls on the form they will still use the old font. If you want custom properties on some controls you will have to adjust them after you call SystemFont.

Changing Graphics.DefFontData at runtime could help if the form designer only wrote the changed properties to the .dfm files.

Upvotes: 3

mj2008
mj2008

Reputation: 6757

I'd say this was similar to translation. In our app, we have made the buttons, labels, edits, everything that little bit bigger so that longer words that are needed by some languages can be easily accommodated. It doesn't hurt the design a bit.

Upvotes: 0

lkessler
lkessler

Reputation: 20132

Kogus had the right idea, but left out the function you need to use. It would be the Windows GetTextExtentPoint32 routine. You pass it a string and it calculates the width and height of the string using the currently selected font. You'll find it in Windows.pas.

You might want to calculate in advance what the maximum space you would need for fonts you allow.

Or you might use the function to dynamically adjust the size of the area displaying the text so that it fits.

This would be fine for one or two controls, but either method would be a lot of work if you're trying to do this on your whole user interface.

Upvotes: 1

JosephStyons
JosephStyons

Reputation: 58775

To do this right, I think you would have to:

1 - Load the user's font preference

2 - Apply it as you've described

3 - Determine the width and height of all captions (in pixels) and compare that to the control's width & height, and adjust accordingly.

Unfortunately, the "adjust accordingly" part is hard. You could widen a button, but then you'd have to check whether that is overlapping with another control.

Your best bet might be to create slightly oversized controls, and hope the user doesn't choose a 32-point font size.

Upvotes: 2

Nick Hodges
Nick Hodges

Reputation: 17138

First, just so we are clear, Borland doesn't own Delphi anymore. Embarcadero now owns Delphi, and we are in safe, secure hands now.

Okay, on to your question.

The trick is to set TForm.AutoScroll to False AND make sure that your development machine is set to Small fonts. Leaving TForm.Scaled alone (it's default value is True).

That's how we do it internally here, and the IDE handles everything just fine.

Upvotes: 9

mghie
mghie

Reputation: 32334

I'm feeling with you. But in all fairness: Proper GUI layout simply can not be created with a pixel-based layout mechanism as employed by the VCL. What is needed is a dynamic layout engine, which lays out controls only after

  1. the proper font has been set for each control (this depends on Windows version, user preferences, and last but not least on the type of control); and

  2. the text in the control has been translated to the current locale, because this may decrease or increase the space necessary for the control.

Since this is all depending on run-time properties creating dialogs by placing controls can not work. Layout mechanisms like those of GTK and QT or the sizers in wxWidgets are better suited.

I know of no such thing for Delphi programs. What I do in some programs is manual sizing and positioning of controls after font setting and translation of text. A lot of work, but depending on your audience it may be worth it.

You could also look into the code Jordan Russell has written for Inno Setup. He does not use the Scaled property of forms, but has written code for custom scaling of controls. Maybe it will also work for very large fonts on high DPI screens; I notice that at least on my 124 DPI laptop screen setup dialogs look quite good.

Upvotes: 3

Related Questions