Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion samples/ControlCatalog/MainView.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
xmlns:models="using:ControlCatalog.Models"
xmlns:pages="using:ControlCatalog.Pages"
xmlns:viewModels="using:ControlCatalog.ViewModels"
x:DataType="viewModels:MainWindowViewModel">
x:DataType="viewModels:MainWindowViewModel"
TextScaling.IsEnabled="True">
<Grid>
<Grid.Styles>
<Style Selector="TextBlock.h2">
Expand Down
16 changes: 16 additions & 0 deletions src/Avalonia.Base/ITextScaler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System;

namespace Avalonia;

/// <summary>
/// Represents an object which can algoritmically scale text.
/// </summary>
public interface ITextScaler
{
double GetScaledFontSize(Visual target, double baseFontSize);

/// <summary>
/// Raised when the text scaling algorithm has changed. Indicates that all text should be rescaled.
/// </summary>
event EventHandler<EventArgs>? TextScalingChanged;
}
10 changes: 7 additions & 3 deletions src/Avalonia.Base/Platform/DefaultPlatformSettings.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
using System;
using Avalonia.Input;
using Avalonia.Input.Platform;
using Avalonia.Media;
using Avalonia.Metadata;
using Avalonia.Threading;
using Avalonia.VisualTree;

namespace Avalonia.Platform
{
Expand Down Expand Up @@ -49,10 +47,16 @@ public virtual PlatformColorValues GetColorValues()

public virtual event EventHandler<PlatformColorValues>? ColorValuesChanged;

protected void OnColorValuesChanged(PlatformColorValues colorValues)
protected virtual void OnColorValuesChanged(PlatformColorValues colorValues)
{
Dispatcher.UIThread.Send(
_ => ColorValuesChanged?.Invoke(this, colorValues));
}

public event EventHandler<EventArgs>? TextScalingChanged;

protected virtual void OnTextScaleChanged() => Dispatcher.UIThread.Send(_ => TextScalingChanged?.Invoke(this, EventArgs.Empty));

public virtual double GetScaledFontSize(Visual target, double baseFontSize) => baseFontSize;
}
}
4 changes: 2 additions & 2 deletions src/Avalonia.Base/Platform/IPlatformSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace Avalonia.Platform
/// Some of these settings might be changed by used globally in the OS in runtime.
/// </summary>
[NotClientImplementable]
public interface IPlatformSettings
public interface IPlatformSettings : ITextScaler
{
/// <summary>
/// The size of the rectangle around the location of a pointer down that a pointer up
Expand Down Expand Up @@ -42,7 +42,7 @@ public interface IPlatformSettings
/// Get a configuration for platform-specific hotkeys in an Avalonia application.
/// </summary>
PlatformHotkeyConfiguration HotkeyConfiguration { get; }

/// <summary>
/// Gets current system color values including dark mode and accent colors.
/// </summary>
Expand Down
2 changes: 1 addition & 1 deletion src/Avalonia.Controls/Documents/IInlineHost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

namespace Avalonia.Controls.Documents
{
internal interface IInlineHost : ILogical
internal interface IInlineHost : ILogical, ITextScaleable
{
void Invalidate();

Expand Down
2 changes: 1 addition & 1 deletion src/Avalonia.Controls/Documents/Inline.cs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ protected TextRunProperties CreateTextRunProperties()

return new GenericTextRunProperties(
typeface,
FontSize,
InlineHost is Visual hostVisual ? TextScaling.GetScaledFontSize(hostVisual, FontSize) : FontSize,
TextDecorations,
Foreground,
parentOrSelfBackground,
Expand Down
8 changes: 7 additions & 1 deletion src/Avalonia.Controls/Documents/TextElement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,12 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs chang
{
base.OnPropertyChanged(change);

if (change.Property.OwnerType == typeof(TextScaling))
{
InlineHost?.Invalidate();
return;
}

switch (change.Property.Name)
{
case nameof(Background):
Expand All @@ -363,7 +369,7 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs chang
case nameof(FontWeight):
case nameof(FontStretch):
case nameof(Foreground):
InlineHost?.Invalidate();
InlineHost?.Invalidate();
break;
}
}
Expand Down
13 changes: 13 additions & 0 deletions src/Avalonia.Controls/ITextScaleable.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace Avalonia.Controls;

/// <summary>
/// Represents an object which particpates in <see cref="TextScaling"/>.
/// </summary>
/// <seealso cref="ITextScaler"/>
public interface ITextScaleable
{
/// <summary>
/// Called when the active text scaling algorithm for this object changes.
/// </summary>
void OnTextScalingChanged();
}
37 changes: 23 additions & 14 deletions src/Avalonia.Controls/Presenters/TextPresenter.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
using System;
using System.Collections.Generic;
using System.Data;
using Avalonia.Controls.Documents;
using Avalonia.Controls.Primitives;
using Avalonia.Interactivity;
using Avalonia.Layout;
using Avalonia.Media;
using Avalonia.Media.Immutable;
Expand All @@ -15,7 +13,7 @@

namespace Avalonia.Controls.Presenters
{
public class TextPresenter : Control
public class TextPresenter : Control, ITextScaleable
{
public static readonly StyledProperty<bool> ShowSelectionHighlightProperty =
AvaloniaProperty.Register<TextPresenter, bool>(nameof(ShowSelectionHighlight), defaultValue: true);
Expand Down Expand Up @@ -58,7 +56,7 @@ public class TextPresenter : Control
/// </summary>
public static readonly StyledProperty<string?> PreeditTextProperty =
AvaloniaProperty.Register<TextPresenter, string?>(nameof(PreeditText));

/// <summary>
/// Defines the <see cref="PreeditText"/> property.
/// </summary>
Expand Down Expand Up @@ -148,7 +146,7 @@ public string? PreeditText
get => GetValue(PreeditTextProperty);
set => SetValue(PreeditTextProperty, value);
}

public int? PreeditTextCursorPosition
{
get => GetValue(PreeditTextCursorPositionProperty);
Expand Down Expand Up @@ -335,6 +333,8 @@ public int SelectionEnd

internal TextSelectionHandleCanvas? TextSelectionHandleCanvas { get; set; }

void ITextScaleable.OnTextScalingChanged() => InvalidateMeasure();

/// <summary>
/// Creates the <see cref="TextLayout"/> used to render the text.
/// </summary>
Expand All @@ -349,10 +349,13 @@ private TextLayout CreateTextLayoutInternal(Size constraint, string? text, Typef
var maxWidth = MathUtilities.IsZero(constraint.Width) ? double.PositiveInfinity : constraint.Width;
var maxHeight = MathUtilities.IsZero(constraint.Height) ? double.PositiveInfinity : constraint.Height;

var effectiveFontSize = TextScaling.GetScaledFontSize(this, FontSize);
var fontScaleFactor = effectiveFontSize / FontSize;

var textLayout = new TextLayout(
text,
typeface,
FontSize,
effectiveFontSize,
Foreground,
TextAlignment,
TextWrapping,
Expand All @@ -361,8 +364,8 @@ private TextLayout CreateTextLayoutInternal(Size constraint, string? text, Typef
FlowDirection,
maxWidth,
maxHeight,
LineHeight,
LetterSpacing,
LineHeight * fontScaleFactor,
LetterSpacing * fontScaleFactor,
0,
FontFeatures,
textStyleOverrides);
Expand Down Expand Up @@ -554,6 +557,7 @@ protected virtual TextLayout CreateTextLayout()
var preeditText = PreeditText;
var text = GetCombinedText(Text, caretIndex, preeditText);
var typeface = new Typeface(FontFamily, FontStyle, FontWeight, FontStretch);
var effectiveFontSize = TextScaling.GetScaledFontSize(this, FontSize);
var selectionStart = SelectionStart;
var selectionEnd = SelectionEnd;
var start = Math.Min(selectionStart, selectionEnd);
Expand All @@ -568,7 +572,7 @@ protected virtual TextLayout CreateTextLayout()
var preeditHighlight = new ValueSpan<TextRunProperties>(caretIndex, preeditText.Length,
new GenericTextRunProperties(
typeface,
FontSize,
effectiveFontSize,
TextDecorations.Underline,
foreground,
fontFeatures: FontFeatures));
Expand All @@ -587,7 +591,7 @@ protected virtual TextLayout CreateTextLayout()
new ValueSpan<TextRunProperties>(start, length,
new GenericTextRunProperties(
typeface,
FontSize,
effectiveFontSize,
foregroundBrush: SelectionForegroundBrush,
fontFeatures: FontFeatures))
};
Expand Down Expand Up @@ -766,7 +770,7 @@ public void MoveCaretVertical(LogicalDirection direction = LogicalDirection.Forw

CaretChanged();
}

private void EnsureCaretTimer()
{
if (_caretTimer == null)
Expand All @@ -792,7 +796,7 @@ private void ResetCaretTimer()
_caretTimer = null;
}

if (CaretBlinkInterval.TotalMilliseconds > 0)
if (CaretBlinkInterval.TotalMilliseconds > 0)
{
_caretTimer = new DispatcherTimer { Interval = CaretBlinkInterval };
_caretTimer.Tick += CaretTimerTick;
Expand Down Expand Up @@ -989,7 +993,7 @@ protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e
_caretTimer.Tick -= CaretTimerTick;
}
}

private void OnPreeditChanged(string? preeditText, int? cursorPosition)
{
if (string.IsNullOrEmpty(preeditText))
Expand Down Expand Up @@ -1020,7 +1024,7 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs chang
{
OnPreeditChanged(change.NewValue as string, PreeditTextCursorPosition);
}

if(change.Property == PreeditTextCursorPositionProperty)
{
OnPreeditChanged(PreeditText, PreeditTextCursorPosition);
Expand All @@ -1047,6 +1051,11 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs chang
ResetCaretTimer();
}

if (change.Property.OwnerType == typeof(TextScaling))
{
InvalidateTextLayout();
}

switch (change.Property.Name)
{
case nameof(PreeditText):
Expand Down
6 changes: 2 additions & 4 deletions src/Avalonia.Controls/Primitives/TextSelectionCanvas.cs
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ private void DragSelectionHandle(TextSelectionHandle handle)
}

var point = ToPresenter(handle.IndicatorPosition);
point = point.WithY(point.Y - _presenter.FontSize / 2);
point = point.WithY(point.Y - TextScaling.GetScaledFontSize(_presenter, _presenter.FontSize) / 2);
var hit = _presenter.TextLayout.HitTestPoint(point);
var position = hit.CharacterHit.FirstCharacterIndex + hit.CharacterHit.TrailingLength;

Expand Down Expand Up @@ -327,9 +327,6 @@ internal bool ShowContextMenu()
{
if (_textBox.ContextFlyout is PopupFlyoutBase flyout)
{
var verticalOffset = (double.IsNaN(_textBox.LineHeight) ? _textBox.FontSize : _textBox.LineHeight) +
ContextMenuPadding;

TextSelectionHandle? handle = null;

if (_textBox.SelectionStart != _textBox.SelectionEnd)
Expand All @@ -349,6 +346,7 @@ internal bool ShowContextMenu()

if (handle != null)
{
var verticalOffset = (_textBox.GetLineHeight() ?? 0) + ContextMenuPadding;
var topLeft = ToTextBox(handle.GetTopLeft());
flyout.VerticalOffset = topLeft.Y - verticalOffset;
flyout.HorizontalOffset = topLeft.X;
Expand Down
12 changes: 7 additions & 5 deletions src/Avalonia.Controls/SelectableTextBlock.cs
Original file line number Diff line number Diff line change
Expand Up @@ -183,18 +183,20 @@ protected override void OnLostFocus(RoutedEventArgs e)
protected override TextLayout CreateTextLayout(string? text)
{
var typeface = new Typeface(FontFamily, FontStyle, FontWeight, FontStretch);
var effectiveFontSize = TextScaling.GetScaledFontSize(this, FontSize);
var fontScaleFactor = effectiveFontSize / FontSize;

var defaultProperties = new GenericTextRunProperties(
typeface,
FontSize,
effectiveFontSize,
TextDecorations,
Foreground,
fontFeatures: FontFeatures);

var paragraphProperties = new GenericTextParagraphProperties(FlowDirection, TextAlignment, true, false,
defaultProperties, TextWrapping, LineHeight, 0, LetterSpacing)
defaultProperties, TextWrapping, LineHeight * fontScaleFactor, 0, LetterSpacing * fontScaleFactor)
{
LineSpacing = LineSpacing
LineSpacing = LineSpacing * fontScaleFactor,
};

List<ValueSpan<TextRunProperties>>? textStyleOverrides = null;
Expand Down Expand Up @@ -235,7 +237,7 @@ protected override TextLayout CreateTextLayout(string? text)
overlapLength,
new GenericTextRunProperties(
textRun.Properties?.Typeface ?? typeface,
FontSize,
effectiveFontSize,
foregroundBrush: SelectionForegroundBrush,
fontFeatures: textRun.Properties?.FontFeatures ?? FontFeatures)));

Expand All @@ -249,7 +251,7 @@ protected override TextLayout CreateTextLayout(string? text)
new ValueSpan<TextRunProperties>(start, length,
new GenericTextRunProperties(
typeface,
FontSize,
effectiveFontSize,
foregroundBrush: SelectionForegroundBrush,
fontFeatures: FontFeatures))
];
Expand Down
Loading
Loading