Skip to content

Conversation

@TomEdwardsEnscape
Copy link
Contributor

@TomEdwardsEnscape TomEdwardsEnscape commented Dec 16, 2025

Many platforms support text scaling, an accessibility feature which allows the user to request that text be drawn larger (or on some platforms smaller) than normal, without altering other UI elements. Avalonia currently has no support for this feature. This PR adds support for iOS and Windows, and creates the infrastructure necessary to extend support to more platforms in the future.

The feature is disabled by default, so there are no breaking changes. It has been enabled for the Control Gallery.

Only TextBlock, Inline, and TextPresenter scale text by themselves. There is no change to low-level text measurement or rendering.

What is the updated/expected behavior with this PR?

The Control Gallery now looks like this with 150% text scaling on Windows:

Screenshot 2025-12-16 130913

Note that the window, tiles, scrollbars and other non-text elements are the same size as before. Only the text is larger.

How was the solution implemented (if it's not obvious)?

Text scaling is provided via IPlatformSettings. TextBlock, Inline, and TextPresenter get the platform settings object from their TopLevel and ask it to adjust their assigned FontSize when they are building their text layout objects. The value of FontSize does not change; platform text scaling is an extra layer on top which affects only measurement and rendering. This is how WinUI3 handles the feature.

Each individual control can configure the inherited TextElement.IsPlatformTextScalingEnabled property to control whether text scaling is applied to it and its children. Inline elements use the value of their host.

Breaking changes

None.

Obsoletions / Deprecations

None.

@TomEdwardsEnscape TomEdwardsEnscape force-pushed the feature/platform-text-scaling branch from 7cc5c10 to f164333 Compare December 16, 2025 11:10
@MrJul MrJul added feature needs-api-review The PR adds new public APIs that should be reviewed. labels Dec 16, 2025
@TomEdwardsEnscape TomEdwardsEnscape changed the title Added support for platform text scaling Platform accessibility text scaling Dec 16, 2025
@MrJul
Copy link
Member

MrJul commented Dec 16, 2025

API diff for review:

Avalonia.Base (net10.0, net8.0)

  namespace Avalonia.Platform
  {
      public class DefaultPlatformSettings : Avalonia.Platform.IPlatformSettings
      {
-         protected void OnColorValuesChanged(Avalonia.Platform.PlatformColorValues colorValues);
+         protected virtual void OnColorValuesChanged(Avalonia.Platform.PlatformColorValues colorValues);
+         public event System.EventHandler<System.EventArgs>? TextScalingChanged { add; remove; }
+         public virtual double GetScaledFontSize(double baseFontSize);
+         protected virtual void OnTextScaleChanged();
      }
      public interface IPlatformSettings
      {
+         event System.EventHandler<System.EventArgs>? TextScalingChanged;
+         double GetScaledFontSize(double baseFontSize);
      }
  }

Avalonia.Controls (net10.0, net8.0)

  namespace Avalonia.Controls
  {
      public class TextBlock : Avalonia.Controls.Control, Avalonia.LogicalTree.ILogical, Avalonia.Controls.IPlatformTextScaleable
      {
+         public static readonly Avalonia.StyledProperty<bool> IsPlatformTextScalingEnabledProperty;
+         protected double EffectiveFontSize { get; }
+         public bool IsPlatformTextScalingEnabled { get; set; }
      }
+     public interface IPlatformTextScaleable
+     {
+         double GetScaledFontSize(double baseFontSize);
+         void OnPlatformTextScalingChanged();
+         bool IsPlatformTextScalingEnabled { get; }
+     }
  }
  namespace Avalonia.Controls.Documents
  {
      public abstract class TextElement : Avalonia.StyledElement
      {
+         public static readonly Avalonia.AttachedProperty<bool> IsPlatformTextScalingEnabledProperty;
+         public static bool GetIsPlatformTextScalingEnabled(Avalonia.Controls.Control control);
+         public static void SetIsPlatformTextScalingEnabled(Avalonia.Controls.Control control, bool value);
+         public bool IsPlatformTextScalingEnabled { get; set; }
      }
  }
  namespace Avalonia.Controls.Presenters
  {
      public class TextPresenter : Avalonia.Controls.Control, Avalonia.Controls.IPlatformTextScaleable
      {
+         public static readonly Avalonia.StyledProperty<bool> IsPlatformTextScalingEnabledProperty;
+         protected double EffectiveFontSize { get; }
+         public bool IsPlatformTextScalingEnabled { get; set; }
      }
  }

Only TextBlock, Inline, and TextPresenter scale text by themselves
No change to low-level text rendering or FontSize values
Implemented for iOS and Windows
Windows scaling is currently uniform regardless of base font size, which is incorrect
@TomEdwardsEnscape TomEdwardsEnscape force-pushed the feature/platform-text-scaling branch from f164333 to bf2a577 Compare December 16, 2025 12:13
Fixed TextBox.MaxLines and TextBox.MinLines calculations when text scaling is active
Fixed TextBox not updating when LineSpacing changes
Removed EffectiveFontSize, since it's no longer the only property which scales
@MrJul
Copy link
Member

MrJul commented Dec 17, 2025

Notes from the API review meeting:

To be more generic, we'd like the text scale factor to be configurable and inherited, with its default coming from the platform. Proposed name: TextScaleFactor. This should replace IsPlatformTextScalingEnabled.

We're fine with making the platform scaling the default in v12. This PR won't be backported to v11.

@MrJul MrJul added api-change-requested The new public APIs need some changes. and removed needs-api-review The PR adds new public APIs that should be reviewed. labels Dec 17, 2025
@TomEdwardsEnscape
Copy link
Contributor Author

To be more generic, we'd like the text scale factor to be configurable and inherited, with its default coming from the platform. Proposed name: TextScaleFactor. This should replace IsPlatformTextScalingEnabled.

This can't be done. Text scaling is not a factor but a per-platform algorithm. On Windows this algorthim is a complicated mathematical function, while on iOS it's an absolute point increase or decrease, plus a minimum size. Android may do something else.

We're fine with making the platform scaling the default in v12. This PR won't be backported to v11.

Alright, but my iOS project targets v11, is due for release in Q1 next year, and we would like it to support text scaling. We'll probably make a cheeky custom build by opening a backport PR despite this decision.

@grokys
Copy link
Member

grokys commented Dec 17, 2025

Alright, but my iOS project targets v11, is due for release in Q1 next year, and we would like it to support text scaling.

We may want to consider backporting it, but having it turned off by default?

@MrJul
Copy link
Member

MrJul commented Dec 19, 2025

This can't be done. Text scaling is not a factor but a per-platform algorithm. On Windows this algorthim is a complicated mathematical function, while on iOS it's an absolute point increase or decrease, plus a minimum size. Android may do something else.

@TomEdwardsEnscape Indeed. Sorry, by doing the API review without having looked at the implementation in details, we were under the wrong assumption that there was only one (non-linear) algorithm, with a factor determined per platform.

Still, we want to let the user customize the scale factor. Flutter had a textScaleFactor but deprecated it in favor of a TextScaler. We think we could go for a similar approach.

Have a TextScaler inherited attached property and a TextScaler abstract class (exact shape tbd). By default, the underlying platform could provide one per top level, that the user can easily override.

This would also allow each TextScaler implementation to cache values, as there were concerns about calling the native API for each single TextBlock during the already expensive layout pass.

Added ITextScaler to support custom text scaling
Calculate a font scale factor and use this to adjust height and spacing values
Fixed TextBox not updating when LineHeight changes and MaxLines or MinLines are active
@TomEdwardsEnscape
Copy link
Contributor Author

I implemented the TextScaler abstract class as requested, see e1dd366.

However, I didn't like this approach. It's opaque and hard to use. It doesn't match Flutter's implementation either: they have a parent item in the hierarchy, like LayoutTransformControl, not an inherited object. An inherited, configurable object is a bad design because it's easy to inadvertently change the configuration of your parent.

So what I've pushed to the PR branch is a reinterpretation of Flutter's solution in Avalonia's style. There are four attached properties:

  • TextScaling.IsEnabled
  • TextScaling.MinFontSize
  • TextScaling.MaxFontSize
  • TextScaling.CustomTextScaler

The last of these is a read-only interface that can provide custom scaling (if null, platform scaling is used). The rest provide built-in functionality for the common use cases identified by Flutter.

Flutter's documentation states that "it's rarely needed to create a custom TextScaler subclass". I agree, and the properties listed above enable Avalonia to provide the same built-in functionality that Flutter offers.

@MrJul MrJul added needs-api-review The PR adds new public APIs that should be reviewed. and removed api-change-requested The new public APIs need some changes. labels Dec 22, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature needs-api-review The PR adds new public APIs that should be reviewed.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants