texto - como cambiar la fuente de windows 10 sin programas




¿Cómo hago para que mi GUI se comporte bien cuando la escala de la fuente de Windows es mayor al 100%? (3)

Al elegir tamaños de letra grandes en el panel de control de Windows (como 125%, o 150%), entonces hay problemas en una aplicación VCL, cada vez que algo se ha configurado en píxeles.

Tome el TStatusBar.Panel . He configurado su ancho para que contenga exactamente una etiqueta, ahora con letras grandes, la etiqueta "se desborda". Mismo problema con otros componentes.

Algunas computadoras portátiles nuevas de Dell ya vienen con un 125% como configuración predeterminada, por lo que, si bien en el pasado este problema era bastante raro, ahora es realmente importante.

¿Qué se puede hacer para superar este problema?


Aquí está mi regalo. Una función que puede ayudarlo con el posicionamiento horizontal de los elementos en sus diseños de GUI. Gratuita 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;

Nota: Por favor, vea las otras respuestas, ya que contienen técnicas muy valiosas. Mi respuesta aquí solo proporciona advertencias y precauciones en contra de asumir DPI-awareness es fácil.

Por lo general, evito el escalado con TForm.Scaled = True DPI con TForm.Scaled = True . La conciencia DPI es solo importante para mí cuando se vuelve importante para los clientes que me llaman y están dispuestos a pagar por ello. La razón técnica detrás de ese punto de vista es que, con conocimiento de DPI o no, está abriendo una ventana a un mundo de dolor. Muchos controles de VCL estándar y de terceros no funcionan bien en DPI alto. La notable excepción es que las partes de VCL que envuelven los Controles Comunes de Windows funcionan extraordinariamente bien con DPI alto. Una gran cantidad de controles personalizados Delphi VCL de terceros y integrados no funcionan bien, o en absoluto, a un alto nivel de DPI. Si planea activar TForm.Scaled, asegúrese de realizar una prueba a 96, 125 y 150 ppp para cada formulario en su proyecto y para cada tercero y control integrado que utilice.

Delphi en sí está escrito en Delphi. Tiene activada la bandera de reconocimiento Alto DPI, para la mayoría de los formularios, aunque incluso tan recientemente como en Delphi XE2, los propios autores IDE decidieron NO activar ese indicador de manifiesto de Alto DPI. Tenga en cuenta que en Delphi XE4 y posterior, la bandera de conciencia de HIGH DPI está activada, y el IDE se ve bien.

Sugiero que no uses TForm.Scaled = true (que es un valor predeterminado en Delphi, a menos que lo hayas modificado, la mayoría de tus formularios tengan Scaled = true) con las banderas de High DPI Aware (como se muestra en las respuestas de David) con Las aplicaciones VCL que se crean utilizando el diseñador de formularios delphi incorporado.

He intentado en el pasado hacer una muestra mínima del tipo de rotura que puedes esperar cuando TForm.Scaled es verdadero, y cuando Delphi forma escalado tiene una falla. Estos fallos técnicos no siempre se desencadenan solo por un valor de DPI distinto de 96. No he podido determinar una lista completa de otras cosas, que incluye cambios en el tamaño de fuente de Windows XP. Pero dado que la mayoría de estos fallos técnicos solo aparecen en mis propias aplicaciones, en situaciones bastante complejas, he decidido mostrarles algunas pruebas de que pueden verificarse.

Delphi XE tiene este aspecto cuando configuras DPI Scaling en "Fonts @ 200%" en Windows 7, y Delphi XE2 también se rompe en Windows 7 y 8, pero estas fallas técnicas parecen estar solucionadas a partir de Delphi XE4:

Estos son en su mayoría controles de VCL estándar que se comportan mal a un nivel alto de DPI. Tenga en cuenta que la mayoría de las cosas no han sido escaladas en absoluto, por lo que los desarrolladores de Delphi IDE han decidido ignorar el conocimiento de DPI, así como también desactivar la virtualización de DPI. Una elección tan interesante.

Desactive la virtualización DPI solo si desea esta nueva fuente adicional de dolor y opciones difíciles. Te sugiero que lo dejes en paz. Tenga en cuenta que los controles comunes de Windows parecen funcionar bien. Tenga en cuenta que el control Delphi data-explorer es un contenedor C # WinForms alrededor de un control común estándar de Windows Tree. Esa es una falla pura de microsoft, y corregirlo podría requerir que Embarcadero reescriba un control de árbol nativo .Net para su explorador de datos, o escribir algún código DPI-check-and-modify-properties para cambiar las alturas de los elementos en el control. Ni siquiera microsoft WinForms puede manejar DPI alta de manera limpia, automática y sin código de kludge personalizado.

Actualización: Factoid interesante: si bien el delphi IDE parece no estar "virtualizado", no está utilizando el contenido de manifiesto que muestra David para lograr "virtualización sin DPI". Tal vez esté usando alguna función API en tiempo de ejecución.

Actualización 2: en respuesta a cómo apoyaría 100% / 125% DPI, se me ocurriría un plan de dos fases. La fase 1 es inventariar mi código para controles personalizados que deben ser reparados para DPI alto, y luego hacer un plan para solucionarlos o eliminarlos gradualmente. La fase 2 consistiría en tomar algunas áreas de mi código que están diseñadas como formularios sin gestión de diseño y cambiarlas a formularios que utilizan algún tipo de gestión de diseño para que los DPI o los cambios de altura de fuente puedan funcionar sin recorte. Sospecho que este trabajo de diseño "intercontroles" sería mucho más complejo en la mayoría de las aplicaciones que el trabajo "intracontrolar".

Actualización: en 2016, la última Delphi 10.1 Berlin está funcionando bien en mi estación de trabajo de 150 ppp.


También es importante tener en cuenta que respetar el DPI del usuario es solo un subconjunto de su trabajo real:

honrando el tamaño de fuente del usuario

Durante décadas, Windows ha resuelto este problema con la idea de realizar diseño utilizando unidades de diálogo , en lugar de píxeles. Se define una "unidad de diálogo" para que el carácter promedio de la fuente sea

  • 4 unidades de diálogo (dlus) de ancho, y
  • 8 unidades de diálogo (clus) alto

Delphi se envía con una noción (con errores) de Scaled , donde una forma intenta ajustar automáticamente en función de la

  • Configuración de Windows DPI del usuario, versos
  • la configuración de DPI en la máquina del desarrollador que guardó por última vez el formulario

Eso no resuelve el problema cuando el usuario usa una fuente diferente a la que diseñó el formulario, por ejemplo:

  • el desarrollador diseñó el formulario con MS Sans Serif 8pt (donde el carácter promedio es 6.21px x 13.00px , a 96dpi)
  • usuario corriendo con Tahoma 8pt (donde el personaje promedio es 5.94px x 13.00px , a 96dpi)

    Como fue el caso con cualquier persona que desarrolla una aplicación para Windows 2000 o Windows XP.

o

  • el desarrollador diseñó el formulario con ** Tahoma 8pt * (donde el personaje promedio es 5.94px x 13.00px , a 96dpi)
  • un usuario corriendo con Segoe UI 9pt (donde el personaje promedio es 6.67px x 15px , a 96dpi)

Como buen desarrollador, respetará las preferencias de fuente de su usuario. Esto significa que también necesita escalar todos los controles en su formulario para que coincida con el nuevo tamaño de fuente:

  • expanda todo horizontalmente en 12.29% (6.67 / 5.94)
  • estirar todo verticalmente en 15.38% (15/13)

Scaled no manejará esto por ti.

Empeora cuando:

  • diseñó su formulario en Segoe UI 9pt (Windows Vista, Windows 7, Windows 8 por defecto)
  • el usuario está ejecutando Segoe UI 14pt , (por ejemplo, mi preferencia) que es 10.52px x 25px

Ahora tienes que escalar todo

  • horizontalmente por 57.72%
  • verticalmente por 66.66%

Scaled no manejará esto por ti.

Si eres inteligente, puedes ver cómo honrar DPI es irrelavent:

  • formulario diseñado con Segoe UI 9pt @ 96dpi (6.67px x 15px)
  • usuario corriendo con Segoe UI 9pt @ 150dpi (10.52px x 25px)

No debería mirar la configuración de PPP del usuario, debería ver su tamaño de fuente . Dos usuarios corriendo

  • IU de Segoe 14pt a 96dpi (10.52px x 25px)
  • Segoe UI 9pt @ 150dpi (10.52px x 25px)

están ejecutando la misma fuente . DPI es solo una cosa que afecta el tamaño de fuente; las preferencias del usuario son la otra.

StandardizeFormFont

Clovis notó que hago referencia a una función StandardizeFormFont que corrige la fuente en un formulario, y lo escala al nuevo tamaño de fuente. No es una función estándar, sino un conjunto completo de funciones que logran la tarea simple que Borland nunca manejó.

function StandardizeFormFont(AForm: TForm): Real;
var
    preferredFontName: string;
    preferredFontHeight: Integer;
begin
    GetUserFontPreference({out}preferredFontName, {out}preferredFontHeight);

    //e.g. "Segoe UI",     
    Result := Toolkit.StandardizeFormFont(AForm, PreferredFontName, PreferredFontHeight);
end;

Windows tiene 6 fuentes diferentes; no hay una sola "configuración de fuente" en Windows.
Pero sabemos por experiencia que nuestros formularios deben seguir la configuración de Fuente de título de icono

procedure GetUserFontPreference(out FaceName: string; out PixelHeight: Integer);
var
   font: TFont;
begin
   font := Toolkit.GetIconTitleFont;
   try
      FaceName := font.Name; //e.g. "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; //e.g. -16
   finally
      font.Free;
   end;
end;

Una vez que conozcamos el tamaño de la fuente, escalaremos el formulario, obtenemos la altura de la fuente actual del formulario ( en píxeles ) y aumentamos ese factor.

Por ejemplo, si estoy configurando el formulario en -16 , y el formulario está actualmente en -11 , entonces tenemos que escalar el formulario completo de la siguiente manera:

-16 / -11 = 1.45454%

La estandarización ocurre en dos fases. Primero escale la forma por la proporción de los tamaños de fuente nuevos: antiguos. Entonces cambie los controles (recursivamente) para usar la nueva fuente.

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 (e.g. 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;

Este es el trabajo de escalar realmente un formulario. Funciona alrededor de errores en el propio método Form.ScaleBy Borland. Primero tiene que deshabilitar todos los anclajes en el formulario, luego realizar la escala y luego volver a habilitar los anclajes:

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;

y luego tenemos que recursivamente usar la nueva fuente:

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;

Con los anclajes siendo deshabilitados 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;

Y los anclajes se vuelven a habilitar recursivamente:

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;

Con el trabajo de cambiar realmente una fuente de controles a la izquierda 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;

Eso es mucho más código de lo que pensaste que iba a ser; Lo sé. Lo triste es que no hay un desarrollador de Delphi en la tierra, a excepción de mí, que realmente hace que sus aplicaciones sean correctas.

Estimado desarrollador de Delphi : configure su fuente de Windows para Segoe UI 14pt y solucione su error de aplicación

Nota : Cualquier código se libera en el dominio público. No se requiere atribución.







windows-7