Como faço com que minha GUI se comporte bem quando o tamanho de fonte do Windows é maior que 100%

Ao escolher tamanhos de fonte grandes no painel de controle do Windows (como 125% ou 150%), há problemas em um aplicativo VCL, sempre que algo foi definido como pixel.

Pegue o TStatusBar.Panel . Eu configurei sua largura para que ela contenha exatamente um label, agora com fonts grandes o label “estourou”. O mesmo problema com outros componentes.

Alguns novos laptops da Dell já vêm com 125% como configuração padrão, então, no passado, esse problema era bastante raro agora, é realmente importante.

O que pode ser feito para superar esse problema?

Nota: Por favor, veja as outras respostas, pois elas contêm técnicas muito valiosas. Minha resposta aqui apenas fornece advertências e precauções contra supor que a consciência de DPI é fácil.

Eu geralmente evito dimensionamento com TForm.Scaled = True DPI com TForm.Scaled = True . A conscientização do DPI só é importante para mim quando se torna importante para os clientes que me ligam e estão dispostos a pagar por isso. A razão técnica por trás desse ponto de vista é que a percepção de DPI ou não, você está abrindo uma janela para um mundo de sofrimento. Muitos controles VCL padrão e de terceiros não funcionam bem em alta DPI. A notável exceção é que as partes VCL que envolvem o Windows Common Controls funcionam notavelmente bem em alta DPI. Um grande número de controles personalizados Delphi VCL de terceiros e internos não funciona bem, ou de maneira alguma, em alta DPI. Se você planeja ativar o TForm.Scaled, certifique-se de testar em 96, 125 e 150 DPI para cada formulário do seu projeto e para cada controle de terceiros e interno que você usa.

Delphi em si é escrito em Delphi. Ele tem o sinalizador de alerta de alta DPI ativado, para a maioria dos formulários, embora, mesmo recentemente no Delphi XE2, os próprios autores do IDE decidiram NÃO ativar o sinalizador de manifesto de alta percepção de DPI. Observe que, no Delphi XE4 e posterior, o sinalizador de alerta HIGH DPI é ativado e o IDE parece ser bom.

Eu sugiro que você não use TForm.Scaled = true (que é um padrão no Delphi, a menos que você tenha modificado, a maioria dos seus formulários tem Scaled = true) com os flags de Alta DPI Aware (como mostrado nas respostas de David) com Aplicativos VCL que são construídos usando o designer de formulários delphi integrado.

Eu tentei no passado fazer uma amostra mínima do tipo de quebra que você pode esperar ver quando o TForm.Scaled é verdadeiro, e quando o escalonamento de formulários do Delphi tem uma falha. Essas falhas não são sempre e apenas acionadas por um valor de DPI diferente de 96. Eu não consegui determinar uma lista completa de outras coisas, que inclui alterações de tamanho de fonte do Windows XP. Mas como a maioria dessas falhas aparecem apenas em meus próprios aplicativos, em situações bastante complexas, decidi mostrar algumas evidências que podem ser verificadas.

O Delphi XE se parece com isso quando você define o DPI Scaling como “Fonts @ 200%” no Windows 7, e o Delphi XE2 é quebrado do mesmo modo no Windows 7 e 8, mas essas falhas parecem ser corrigidas no Delphi XE4:

insira a descrição da imagem aqui

insira a descrição da imagem aqui

Estes são principalmente controles VCL padrão que estão se comportando mal em alta DPI. Observe que a maioria das coisas não foi dimensionada, portanto, os desenvolvedores do Delphi IDE decidiram ignorar o reconhecimento de DPI, bem como desativar a virtualização de DPI. Uma escolha tão interessante.

Desative a virtualização de DPI apenas se desejar essa nova fonte adicional de problemas e escolhas difíceis. Eu sugiro que você deixe isso em paz. Observe que os controles comuns do Windows parecem funcionar bem. Observe que o controle de exploração de dados do Delphi é um wrapper do C # WinForms em torno de um controle comum padrão da Árvore do Windows. Isso é um problema puro da Microsoft, e consertá-lo pode exigir que a Embarcadero reescreva um controle de tree nativo .Net puro para seu explorador de dados ou escreva algum código de propriedades de verificação e modificação de DPI para alterar as alturas de item no controle. Nem mesmo o Microsoft WinForms pode lidar com alta DPI de forma limpa, automática e sem código de kludge personalizado.

Atualização: factóide interessante: Embora o IDE do delphi não pareça ser “virtualizado”, ele não está usando o conteúdo manifesto mostrado por David para alcançar a “virtualização não-DPI”. Talvez esteja usando alguma function da API em tempo de execução.

Atualização 2: Em resposta a como eu apoiaria 100% / 125% DPI, eu criaria um plano de duas fases. A fase 1 é inventariar meu código para controles personalizados que precisam ser corrigidos para alta DPI e, em seguida, fazer um plano para corrigi-los ou eliminá-los. Fase 2 seria tomar algumas áreas do meu código que são projetadas como formulários sem gerenciamento de layout e alterá-las para formulários que usam algum tipo de gerenciamento de layout para que as alterações de DPI ou altura da fonte possam funcionar sem recorte. Eu suspeito que esse trabalho de layout “inter-controle” seria muito mais complexo na maioria das aplicações do que o trabalho “intra-controle”.

Atualização: Em 2016, o último Delphi 10.1 Berlin está funcionando bem em minha estação de trabalho de 150 dpi.

Suas configurações no arquivo .dfm serão dimensionadas corretamente, desde que Scaled seja True .

Se você estiver definindo dimensões no código, precisará escalá-las por Screen.PixelsPerInch dividido por Form.PixelsPerInch . Use o MulDiv para fazer isso.

 function TMyForm.ScaleDimension(const X: Integer): Integer; begin Result := MulDiv(X, Screen.PixelsPerInch, PixelsPerInch); end; 

Isso é o que o framework de persistência de formulário faz quando Scaled for True .

Na verdade, você pode criar um argumento convincente para replace essa function por uma versão que codifique um valor de 96 para o denominador. Isso permite usar valores de dimensão absolutos e não se preocupar com a mudança de significado se você alterar o dimensionamento de fonte na sua máquina de desenvolvimento e salvar novamente o arquivo .dfm. A razão que importa é que a propriedade PixelsPerInch armazenada no arquivo .dfm é o valor da máquina na qual o arquivo .dfm foi salvo pela última vez.

 const SmallFontsPixelsPerInch = 96; function ScaleFromSmallFontsDimension(const X: Integer): Integer; begin Result := MulDiv(X, Screen.PixelsPerInch, SmallFontsPixelsPerInch); end; 

Assim, continuando o tema, outra coisa a ser cautelosa é que, se seu projeto for desenvolvido em várias máquinas com diferentes valores de DPI, você descobrirá que o dimensionamento que o Delphi usa ao salvar arquivos .dfm resulta em controles vagando por uma série de edições . No meu local de trabalho, para evitar isso, temos uma política rígida que os formulários são editados em 96dpi (100% de escala).

Na verdade, minha versão do ScaleFromSmallFontsDimension também permite a possibilidade de a fonte do formulário diferir no tempo de execução daquele conjunto no tempo de design. Em máquinas XP, os formulários do meu aplicativo usam 8pt Tahoma. No Vista e acima 9pt Segoe UI é usado. Isso proporciona ainda outro grau de liberdade. O dimensionamento deve responder por isso, porque os valores de dimensão absoluta usados ​​no código-fonte são considerados relativos à linha de base de 8pt Tahoma em 96dpi.

Se você usar imagens ou glifos na interface do usuário, eles também precisarão ser dimensionados. Um exemplo comum seria os glifos usados ​​em barras de ferramentas e menus. Você desejará fornecer esses glifos como resources de ícone vinculados ao seu executável. Cada ícone deve conter vários tamanhos e, em tempo de execução, você escolhe o tamanho mais apropriado e o carrega em uma lista de imagens. Alguns detalhes sobre esse tópico podem ser encontrados aqui: Como carregar icons de um recurso sem sofrer alias?

Outro truque útil é definir dimensões em unidades relativas, relativas a TextWidth ou TextHeight . Então, se você quer algo com cerca de 10 linhas verticais de tamanho, você pode usar 10*Canvas.TextHeight('Ag') . Esta é uma métrica muito aproximada e pronta, porque não permite o espaçamento entre linhas e assim por diante. No entanto, muitas vezes tudo que você precisa fazer é conseguir que a GUI seja dimensionada corretamente com o PixelsPerInch .

Você também deve marcar seu aplicativo como estando ciente de alta DPI . A melhor maneira de fazer isso é através do manifesto do aplicativo. Como as ferramentas de compilation do Delphi não permitem que você personalize o manifesto que você usa, isso força você a vincular seu próprio recurso de manifesto.

     true    

O script de resources é assim:

 1 24 "Manifest.txt" 

onde Manifest.txt contém o manifesto real. Você também precisa include a seção comctl32 v6 e definir requestedExecutionLevel como asInvoker . Em seguida, você vincula esse recurso compilado ao seu aplicativo e garante que o Delphi não tente fazer o mesmo com seu manifesto. No moderno Delphi você consegue isso configurando a opção de projeto Runtime Themes como None.

O manifesto é o caminho certo para declarar seu aplicativo com alto DPI. Se você quiser apenas experimentá-lo rapidamente sem mexer com seu manifesto, chame SetProcessDPIAware . Faça isso como a primeira coisa que você faz quando seu aplicativo é executado. De preferência em uma das primeiras seções de boot da unidade, ou como a primeira coisa no seu arquivo .dpr.

Se você não declarar seu aplicativo com alto DPI, o Vista e o up o renderão em um modo legado para qualquer escala de fonte acima de 125%. Isso parece bastante terrível. Tente evitar cair nessa armadilha.

Windows 8.1 por atualização do monitor de DPI

No Windows 8.1, agora há suporte do SO para configurações de DPI por monitor ( http://msdn.microsoft.com/pt-br/magazine/dn574798.aspx ). Este é um grande problema para os dispositivos modernos, que podem ter canvass diferentes conectadas com resources muito diferentes. Você pode ter uma canvas de laptop DPI muito alta e um projetor externo de baixa DPI. O suporte a esse cenário exige ainda mais trabalho do que o descrito acima.

Também é importante observar que honrar o DPI do usuário é apenas um subconjunto do seu trabalho real:

honrando o tamanho da fonte do usuário

Por décadas, o Windows resolveu esse problema com a noção de executar layout usando Unidades de Diálogo , em vez de pixels. Uma “unidade de diálogo” é definida para que o caractere médio da fonte seja

  • 4 unidades de diálogo (dlus) de largura e
  • 8 unidades de diálogo (clus) altas

insira a descrição da imagem aqui

O Delphi vem com uma noção (com bugs) de Scaled , onde um formulário tenta se ajustar automaticamente com base no

  • Configurações de DPI do Windows do usuário, versos
  • a configuração de DPI na máquina do desenvolvedor que salvou o formulário pela última vez

Isso não resolve o problema quando o usuário usa uma fonte diferente da que você criou com o formulário, por exemplo:

  • desenvolvedor projetou o formulário com MS Sans Serif 8pt (em que o caractere médio é de 6.21px x 13.00px , a 96 dpi)
  • usuário executando com Tahoma 8pt (onde o caractere médio é 5.94px x 13.00px , a 96dpi)

    Como foi o caso de qualquer um que desenvolvesse um aplicativo para o Windows 2000 ou o Windows XP.

ou

  • desenvolvedor criou o formulário com ** Tahoma 8pt * (onde o caractere médio é de 5.94px x 13.00px , a 96dpi)
  • um usuário executando com Segoe UI 9pt (onde o caractere médio é 6.67px x 15px , a 96 dpi)

Como um bom desenvolvedor, você vai honrar as preferências de fonte do seu usuário. Isso significa que você também precisa dimensionar todos os controles do formulário para que correspondam ao novo tamanho da fonte:

  • expandir tudo horizontalmente em 12,29% (6,67 / 5,94)
  • esticar tudo verticalmente em 15,38% (15/13)

Scaled não vai lidar com isso para você.

Fica pior quando:

  • projetou seu formulário na Segoe UI 9pt ( padrão do Windows Vista, Windows 7, Windows 8)
  • usuário está executando Segoe UI 14pt , (por exemplo, minha preferência), que é 10.52px x 25px

Agora você tem que escalar tudo

  • horizontalmente por 57.72%
  • verticalmente em 66,66%

Scaled não vai lidar com isso para você.


Se você for esperto, poderá ver como o reconhecimento de DPI é irrelavente:

  • formulário projetado com Segoe UI 9pt @ 96dpi (6.67px x 15px)
  • usuário executando com Segoe UI 9pt @ 150dpi (10,52px x 25px)

Você não deve estar olhando para a configuração de DPI do usuário, você deve estar olhando para o tamanho da fonte . Dois usuários executando

  • Segoe UI 14pt @ 96dpi (10,52 px x 25 px)
  • Segoe UI 9pt @ 150dpi (10,52px x 25px)

estão executando a mesma fonte . O DPI é apenas uma coisa que afeta o tamanho da fonte; as preferências do usuário são o outro.

StandardizeFormFont

Clovis notou que eu faço referência a uma function StandardizeFormFont que corrige a fonte em um formulário e a escala para o novo tamanho da fonte. Não é uma function padrão, mas todo um conjunto de funções que realizam a tarefa simples que a Borland nunca manipulou.

 function StandardizeFormFont(AForm: TForm): Real; var preferredFontName: string; preferredFontHeight: Integer; begin GetUserFontPreference({out}preferredFontName, {out}preferredFontHeight); //eg "Segoe UI", Result := Toolkit.StandardizeFormFont(AForm, PreferredFontName, PreferredFontHeight); end; 

O Windows tem 6 fonts diferentes; não há uma única “configuração de fonte” no Windows.
Mas sabemos por experiência que nossos formulários devem seguir a configuração de fonte do título do ícone

 procedure GetUserFontPreference(out FaceName: string; out PixelHeight: Integer); var font: TFont; begin font := Toolkit.GetIconTitleFont; try FaceName := font.Name; //eg "Segoe UI" //Dogfood testing: use a larger font than we're used to; to force us to actually test it if IsDebuggerPresent then font.Size := font.Size+1; PixelHeight := font.Height; //eg -16 finally font.Free; end; end; 

Assim que soubermos o tamanho da fonte, escalaremos o formulário para obter a altura atual da fonte do formulário ( em pixels ) e aumentá-la por esse fator.

Por exemplo, se eu estiver configurando o formulário para -16 e o formulário estiver atualmente em -11 , será necessário dimensionar o formulário inteiro:

 -16 / -11 = 1.45454% 

A padronização acontece em duas fases. Primeiro dimensione o formulário pela proporção do novo: tamanhos de fonte antigos. Então realmente mude os controles (recursivamente) para usar a nova fonte.

 function StandardizeFormFont(AForm: TForm; FontName: string; FontHeight: Integer): Real; var oldHeight: Integer; begin Assert(Assigned(AForm)); if (AForm.Scaled) then begin OutputDebugString(PChar('WARNING: StandardizeFormFont: Form "'+GetControlName(AForm)+'" is set to Scaled. Proper form scaling requires VCL scaling to be disabled, unless you implement scaling by overriding the protected ChangeScale() method of the form.')); end; if (AForm.AutoScroll) then begin if AForm.WindowState = wsNormal then begin OutputDebugString(PChar('WARNING: StandardizeFormFont: Form "'+GetControlName(AForm)+'" is set to AutoScroll. Form designed size will be suseptable to changes in Windows form caption height (eg 2000 vs XP).')); if IsDebuggerPresent then Windows.DebugBreak; //Some forms would like it (to fix maximizing problem) end; end; if (not AForm.ShowHint) then begin AForm.ShowHint := True; OutputDebugString(PChar('INFORMATION: StandardizeFormFont: Turning on form "'+GetControlName(AForm)+'" hints. (ShowHint := True)')); if IsDebuggerPresent then Windows.DebugBreak; //Some forms would like it (to fix maximizing problem) end; oldHeight := AForm.Font.Height; //Scale the form to the new font size // if (FontHeight <> oldHeight) then For compatibility, it's safer to trigger a call to ChangeScale, since a lot of people will be assuming it always is called begin ScaleForm(AForm, FontHeight, oldHeight); end; //Now change all controls to actually use the new font Toolkit.StandardizeFont_ControlCore(AForm, g_ForceClearType, FontName, FontHeight, AForm.Font.Name, AForm.Font.Size); //Return the scaling ratio, so any hard-coded values can be multiplied Result := FontHeight / oldHeight; end; 

Aqui está o trabalho de realmente escalar um formulário. Ele trabalha em torno de erros no próprio método Form.ScaleBy da Borland. Primeiro, é necessário desabilitar todas as âncoras no formulário, depois executar o dimensionamento e reativar as âncoras:

 TAnchorsArray = array of TAnchors; procedure ScaleForm(const AForm: TForm; const M, D: Integer); var aAnchorStorage: TAnchorsArray; RectBefore, RectAfter: TRect; x, y: Integer; monitorInfo: TMonitorInfo; workArea: TRect; begin if (M = 0) and (D = 0) then Exit; RectBefore := AForm.BoundsRect; SetLength(aAnchorStorage, 0); aAnchorStorage := DisableAnchors(AForm); try AForm.ScaleBy(M, D); finally EnableAnchors(AForm, aAnchorStorage); end; RectAfter := AForm.BoundsRect; case AForm.Position of poScreenCenter, poDesktopCenter, poMainFormCenter, poOwnerFormCenter, poDesigned: //i think i really want everything else to also follow the nudging rules...why did i exclude poDesigned begin //This was only nudging by one quarter the difference, rather than one half the difference // x := RectAfter.Left - ((RectAfter.Right-RectBefore.Right) div 2); // y := RectAfter.Top - ((RectAfter.Bottom-RectBefore.Bottom) div 2); x := RectAfter.Left - ((RectAfter.Right-RectAfter.Left) - (RectBefore.Right-RectBefore.Left)) div 2; y := RectAfter.Top - ((RectAfter.Bottom-RectAfter.Top)-(RectBefore.Bottom-RectBefore.Top)) div 2; end; else //poDesigned, poDefault, poDefaultPosOnly, poDefaultSizeOnly: x := RectAfter.Left; y := RectAfter.Top; end; if AForm.Monitor <> nil then begin monitorInfo.cbSize := SizeOf(monitorInfo); if GetMonitorInfo(AForm.Monitor.Handle, @monitorInfo) then workArea := monitorInfo.rcWork else begin OutputDebugString(PChar(SysErrorMessage(GetLastError))); workArea := Rect(AForm.Monitor.Left, AForm.Monitor.Top, AForm.Monitor.Left+AForm.Monitor.Width, AForm.Monitor.Top+AForm.Monitor.Height); end; // If the form is off the right or bottom of the screen then we need to pull it back if RectAfter.Right > workArea.Right then x := workArea.Right - (RectAfter.Right-RectAfter.Left); //rightEdge - widthOfForm if RectAfter.Bottom > workArea.Bottom then y := workArea.Bottom - (RectAfter.Bottom-RectAfter.Top); //bottomEdge - heightOfForm x := Max(x, workArea.Left); //don't go beyond left edge y := Max(y, workArea.Top); //don't go above top edge end else begin x := Max(x, 0); //don't go beyond left edge y := Max(y, 0); //don't go above top edge end; AForm.SetBounds(x, y, RectAfter.Right-RectAfter.Left, //Width RectAfter.Bottom-RectAfter.Top); //Height end; 

e então temos que recursivamente usar a nova fonte:

 procedure StandardizeFont_ControlCore(AControl: TControl; ForceClearType: Boolean; FontName: string; FontSize: Integer; ForceFontIfName: string; ForceFontIfSize: Integer); const CLEARTYPE_QUALITY = 5; var i: Integer; RunComponent: TComponent; AControlFont: TFont; begin if not Assigned(AControl) then Exit; if (AControl is TStatusBar) then begin TStatusBar(AControl).UseSystemFont := False; //force... TStatusBar(AControl).UseSystemFont := True; //...it end else begin AControlFont := Toolkit.GetControlFont(AControl); if not Assigned(AControlFont) then Exit; StandardizeFont_ControlFontCore(AControlFont, ForceClearType, FontName, FontSize, ForceFontIfName, ForceFontIfSize); end; { If a panel has a toolbar on it, the toolbar won't paint properly. So this idea won't work. if (not Toolkit.IsRemoteSession) and (AControl is TWinControl) and (not (AControl is TToolBar)) then TWinControl(AControl).DoubleBuffered := True; } //Iterate children for i := 0 to AControl.ComponentCount-1 do begin RunComponent := AControl.Components[i]; if RunComponent is TControl then StandardizeFont_ControlCore( TControl(RunComponent), ForceClearType, FontName, FontSize, ForceFontIfName, ForceFontIfSize); end; end; 

Com as âncoras sendo desativadas recursivamente:

 function DisableAnchors(ParentControl: TWinControl): TAnchorsArray; var StartingIndex: Integer; begin StartingIndex := 0; DisableAnchors_Core(ParentControl, Result, StartingIndex); end; procedure DisableAnchors_Core(ParentControl: TWinControl; var aAnchorStorage: TAnchorsArray; var StartingIndex: Integer); var iCounter: integer; ChildControl: TControl; begin if (StartingIndex+ParentControl.ControlCount+1) > (Length(aAnchorStorage)) then SetLength(aAnchorStorage, StartingIndex+ParentControl.ControlCount+1); for iCounter := 0 to ParentControl.ControlCount - 1 do begin ChildControl := ParentControl.Controls[iCounter]; aAnchorStorage[StartingIndex] := ChildControl.Anchors; //doesn't work for set of stacked top-aligned panels // if ([akRight, akBottom ] * ChildControl.Anchors) <> [] then // ChildControl.Anchors := [akLeft, akTop]; if (ChildControl.Anchors) <> [akTop, akLeft] then ChildControl.Anchors := [akLeft, akTop]; // if ([akTop, akBottom] * ChildControl.Anchors) = [akTop, akBottom] then // ChildControl.Anchors := ChildControl.Anchors - [akBottom]; Inc(StartingIndex); end; //Add children for iCounter := 0 to ParentControl.ControlCount - 1 do begin ChildControl := ParentControl.Controls[iCounter]; if ChildControl is TWinControl then DisableAnchors_Core(TWinControl(ChildControl), aAnchorStorage, StartingIndex); end; end; 

E âncoras sendo recursivamente reativadas:

 procedure EnableAnchors(ParentControl: TWinControl; aAnchorStorage: TAnchorsArray); var StartingIndex: Integer; begin StartingIndex := 0; EnableAnchors_Core(ParentControl, aAnchorStorage, StartingIndex); end; procedure EnableAnchors_Core(ParentControl: TWinControl; aAnchorStorage: TAnchorsArray; var StartingIndex: Integer); var iCounter: integer; ChildControl: TControl; begin for iCounter := 0 to ParentControl.ControlCount - 1 do begin ChildControl := ParentControl.Controls[iCounter]; ChildControl.Anchors := aAnchorStorage[StartingIndex]; Inc(StartingIndex); end; //Restore children for iCounter := 0 to ParentControl.ControlCount - 1 do begin ChildControl := ParentControl.Controls[iCounter]; if ChildControl is TWinControl then EnableAnchors_Core(TWinControl(ChildControl), aAnchorStorage, StartingIndex); end; end; 

Com o trabalho de realmente mudar uma fonte de controles para:

 procedure StandardizeFont_ControlFontCore(AControlFont: TFont; ForceClearType: Boolean; FontName: string; FontSize: Integer; ForceFontIfName: string; ForceFontIfSize: Integer); const CLEARTYPE_QUALITY = 5; var CanChangeName: Boolean; CanChangeSize: Boolean; lf: TLogFont; begin if not Assigned(AControlFont) then Exit; {$IFDEF ForceClearType} ForceClearType := True; {$ELSE} if g_ForceClearType then ForceClearType := True; {$ENDIF} //Standardize the font if it's currently // "MS Shell Dlg 2" (meaning whoever it was opted into the 'change me' system // "MS Sans Serif" (the Delphi default) // "Tahoma" (when they wanted to match the OS, but "MS Shell Dlg 2" should have been used) // "MS Shell Dlg" (the 9x name) CanChangeName := (FontName <> '') and (AControlFont.Name <> FontName) and ( ( (ForceFontIfName <> '') and (AControlFont.Name = ForceFontIfName) ) or ( (ForceFontIfName = '') and ( (AControlFont.Name = 'MS Sans Serif') or (AControlFont.Name = 'Tahoma') or (AControlFont.Name = 'MS Shell Dlg 2') or (AControlFont.Name = 'MS Shell Dlg') ) ) ); CanChangeSize := ( //there is a font size (FontSize <> 0) and ( //the font is at it's default size, or we're specifying what it's default size is (AControlFont.Size = 8) or ((ForceFontIfSize <> 0) and (AControlFont.Size = ForceFontIfSize)) ) and //the font size (or height) is not equal ( //negative for height (px) ((FontSize < 0) and (AControlFont.Height <> FontSize)) or //positive for size (pt) ((FontSize > 0) and (AControlFont.Size <> FontSize)) ) and //no point in using default font's size if they're not using the face ( (AControlFont.Name = FontName) or CanChangeName ) ); if CanChangeName or CanChangeSize or ForceClearType then begin if GetObject(AControlFont.Handle, SizeOf(TLogFont), @lf) <> 0 then begin //Change the font attributes and put it back if CanChangeName then StrPLCopy(Addr(lf.lfFaceName[0]), FontName, LF_FACESIZE); if CanChangeSize then lf.lfHeight := FontSize; if ForceClearType then lf.lfQuality := CLEARTYPE_QUALITY; AControlFont.Handle := CreateFontIndirect(lf); end else begin if CanChangeName then AControlFont.Name := FontName; if CanChangeSize then begin if FontSize > 0 then AControlFont.Size := FontSize else if FontSize < 0 then AControlFont.Height := FontSize; end; end; end; end; 

Isso é muito mais código do que você pensava que seria; eu sei. O triste é que não há nenhum desenvolvedor Delphi no mundo, exceto eu, que realmente faz suas aplicações corretas.

Prezado desenvolvedor do Delphi : Defina sua fonte do Windows para o Segoe UI 14pt e corrija seu aplicativo com bugs

Nota : Qualquer código é liberado para o domínio público. Nenhuma atribuição é necessária.

Aqui está meu presente. Uma function que pode ajudá-lo com o posicionamento horizontal dos elementos em seus layouts de GUI. Livre para todos.

 function CenterInParent(Place,NumberOfPlaces,ObjectWidth,ParentWidth,CropPercent: Integer): Integer; {returns formated centered position of an object relative to parent. Place - P order number of an object beeing centered NumberOfPlaces - NOP total number of places available for object beeing centered ObjectWidth - OW width of an object beeing centered ParentWidth - PW width of an parent CropPercent - CP percentage of safe margin on both sides which we want to omit from calculation +-----------------------------------------------------+ | | | +--------+ +---+ +--------+ | | | | | | | | | | +--------+ +---+ +--------+ | | | | | | | +-----------------------------------------------------+ | |<---------------------A----------------->| | |<-C->|<------B----->|<-----B----->|<-----B---->|<-C->| | |<-D>| |<----------E------------>| A = PW-C B = A/NOP C=(CP*PW)/100 D = (B-OW)/2 E = C+(P-1)*B+D } var A, B, C, D: Integer; begin C := Trunc((CropPercent*ParentWidth)/100); A := ParentWidth - C; B := Trunc(A/NumberOfPlaces); D := Trunc((B-ObjectWidth)/2); Result := C+(Place-1)*B+D; end;