forked from microsoft/react-native-windows
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
prototype(FastText): Add option to use TextBlock for simple text
This is Eric Rozell's [PR](microsoft#1256) with a couple fixes to make it work with S4L. This optimization [gives](aka.ms/V3mz2) a 10% speedup and reduces memory usage by 10%. Simple text, i.e., text with only a single raw text node as a child, can be transformed into a TextBlock with a Text property, instead of a RichTextBlock with a Paragraph of Inlines. This change detects in the React Native Text node has a single raw text child, and if so, uses the FastText native component. To test if your text nodes are "fast", set DebugSettings.IsTextPerformanceVisualizationEnabled to true while debugging your app; it changes optimized text color to green. Related work items: #1164947
- Loading branch information
Showing
6 changed files
with
418 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
281 changes: 281 additions & 0 deletions
281
ReactWindows/ReactNative/Views/Text/ReactFastTextShadowNode.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,281 @@ | ||
using Facebook.Yoga; | ||
using ReactNative.Reflection; | ||
using ReactNative.UIManager; | ||
using ReactNative.UIManager.Annotations; | ||
using System; | ||
using Windows.Foundation; | ||
using Windows.UI.Text; | ||
using Windows.UI.Xaml; | ||
using Windows.UI.Xaml.Controls; | ||
using Windows.UI.Xaml.Media; | ||
|
||
namespace ReactNative.Views.Text | ||
{ | ||
/// <summary> | ||
/// The shadow node implementation for text views. | ||
/// </summary> | ||
public class ReactFastTextShadowNode : LayoutShadowNode | ||
{ | ||
private int _letterSpacing; | ||
private int _numberOfLines; | ||
|
||
private double? _fontSize; | ||
private double _lineHeight; | ||
|
||
private bool _allowFontScaling; | ||
|
||
private FontStyle? _fontStyle; | ||
private FontWeight? _fontWeight; | ||
private TextAlignment _textAlignment = TextAlignment.DetectFromContent; | ||
|
||
private string _fontFamily; | ||
|
||
private string _text; | ||
|
||
/// <summary> | ||
/// Instantiates a <see cref="ReactTextShadowNode"/>. | ||
/// </summary> | ||
public ReactFastTextShadowNode() | ||
{ | ||
MeasureFunction = (node, width, widthMode, height, heightMode) => | ||
MeasureText(this, node, width, widthMode, height, heightMode); | ||
} | ||
|
||
/// <summary> | ||
/// The view text. | ||
/// </summary> | ||
public string Text | ||
{ | ||
get | ||
{ | ||
return _text; | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Sets the text on the view. | ||
/// </summary> | ||
/// <param name="text">The text.</param> | ||
[ReactProp("text")] | ||
public void SetText(string text) | ||
{ | ||
_text = text ?? ""; | ||
MarkUpdated(); | ||
} | ||
|
||
/// <summary> | ||
/// Sets the font size for the node. | ||
/// </summary> | ||
/// <param name="fontSize">The font size.</param> | ||
[ReactProp(ViewProps.FontSize)] | ||
public void SetFontSize(double? fontSize) | ||
{ | ||
if (_fontSize != fontSize) | ||
{ | ||
_fontSize = fontSize; | ||
MarkUpdated(); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Sets the font family for the node. | ||
/// </summary> | ||
/// <param name="fontFamily">The font family.</param> | ||
[ReactProp(ViewProps.FontFamily)] | ||
public void SetFontFamily(string fontFamily) | ||
{ | ||
if (_fontFamily != fontFamily) | ||
{ | ||
_fontFamily = fontFamily; | ||
MarkUpdated(); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Sets the font weight for the node. | ||
/// </summary> | ||
/// <param name="fontWeightValue">The font weight string.</param> | ||
[ReactProp(ViewProps.FontWeight)] | ||
public void SetFontWeight(string fontWeightValue) | ||
{ | ||
var fontWeight = FontStyleHelpers.ParseFontWeight(fontWeightValue); | ||
if (_fontWeight.HasValue != fontWeight.HasValue || | ||
(_fontWeight.HasValue && fontWeight.HasValue && | ||
_fontWeight.Value.Weight != fontWeight.Value.Weight)) | ||
{ | ||
_fontWeight = fontWeight; | ||
MarkUpdated(); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Sets the font style for the node. | ||
/// </summary> | ||
/// <param name="fontStyleValue">The font style string.</param> | ||
[ReactProp(ViewProps.FontStyle)] | ||
public void SetFontStyle(string fontStyleValue) | ||
{ | ||
var fontStyle = EnumHelpers.ParseNullable<FontStyle>(fontStyleValue); | ||
if (_fontStyle != fontStyle) | ||
{ | ||
_fontStyle = fontStyle; | ||
MarkUpdated(); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Sets the letter spacing for the node. | ||
/// </summary> | ||
/// <param name="letterSpacing">The letter spacing.</param> | ||
[ReactProp(ViewProps.LetterSpacing)] | ||
public void SetLetterSpacing(int letterSpacing) | ||
{ | ||
var spacing = 50 * letterSpacing; // TODO: Find exact multiplier (50) to match iOS | ||
|
||
if (_letterSpacing != spacing) | ||
{ | ||
_letterSpacing = spacing; | ||
MarkUpdated(); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Sets the line height. | ||
/// </summary> | ||
/// <param name="lineHeight">The line height.</param> | ||
[ReactProp(ViewProps.LineHeight)] | ||
public virtual void SetLineHeight(double lineHeight) | ||
{ | ||
if (_lineHeight != lineHeight) | ||
{ | ||
_lineHeight = lineHeight; | ||
MarkUpdated(); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Sets the maximum number of lines. | ||
/// </summary> | ||
/// <param name="numberOfLines">Max number of lines.</param> | ||
[ReactProp(ViewProps.NumberOfLines)] | ||
public virtual void SetNumberOfLines(int numberOfLines) | ||
{ | ||
if (_numberOfLines != numberOfLines) | ||
{ | ||
_numberOfLines = numberOfLines; | ||
MarkUpdated(); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Sets the text alignment. | ||
/// </summary> | ||
/// <param name="textAlign">The text alignment string.</param> | ||
[ReactProp(ViewProps.TextAlign)] | ||
public void SetTextAlign(string textAlign) | ||
{ | ||
var textAlignment = textAlign == "auto" || textAlign == null ? | ||
TextAlignment.DetectFromContent : | ||
EnumHelpers.Parse<TextAlignment>(textAlign); | ||
|
||
if (_textAlignment != textAlignment) | ||
{ | ||
_textAlignment = textAlignment; | ||
MarkUpdated(); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Set fontScaling | ||
/// </summary> | ||
/// <param name="allowFontScaling">Max number of lines.</param> | ||
[ReactProp(ViewProps.AllowFontScaling)] | ||
public virtual void SetAllowFontScaling(bool allowFontScaling) | ||
{ | ||
if (_allowFontScaling != allowFontScaling) | ||
{ | ||
_allowFontScaling = allowFontScaling; | ||
MarkUpdated(); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Called after a layout step at the end of a UI batch from | ||
/// <see cref="UIManagerModule"/>. May be used to enqueue additional UI | ||
/// operations for the native view. Will only be called on nodes marked | ||
/// as updated. | ||
/// </summary> | ||
/// <param name="uiViewOperationQueue"> | ||
/// Interface for enqueueing UI operations. | ||
/// </param> | ||
public override void OnCollectExtraUpdates(UIViewOperationQueue uiViewOperationQueue) | ||
{ | ||
base.OnCollectExtraUpdates(uiViewOperationQueue); | ||
uiViewOperationQueue.EnqueueUpdateExtraData(ReactTag, this); | ||
} | ||
|
||
/// <summary> | ||
/// Marks a node as updated. | ||
/// </summary> | ||
protected override void MarkUpdated() | ||
{ | ||
base.MarkUpdated(); | ||
dirty(); | ||
} | ||
|
||
private static YogaSize MeasureText(ReactFastTextShadowNode textNode, YogaNode node, float width, YogaMeasureMode widthMode, float height, YogaMeasureMode heightMode) | ||
{ | ||
// TODO: Measure text with DirectWrite or other API that does not | ||
// require dispatcher access. Currently, we're instantiating a | ||
// second CoreApplicationView (that is never activated) and using | ||
// its Dispatcher thread to calculate layout. | ||
var textBlock = new TextBlock | ||
{ | ||
TextWrapping = TextWrapping.Wrap, | ||
TextAlignment = TextAlignment.DetectFromContent, | ||
TextTrimming = TextTrimming.CharacterEllipsis, | ||
}; | ||
|
||
textNode.UpdateTextBlockCore(textBlock, true); | ||
|
||
var normalizedWidth = YogaConstants.IsUndefined(width) ? double.PositiveInfinity : width; | ||
var normalizedHeight = YogaConstants.IsUndefined(height) ? double.PositiveInfinity : height; | ||
textBlock.Measure(new Size(normalizedWidth, normalizedHeight)); | ||
return MeasureOutput.Make( | ||
(float)Math.Ceiling(textBlock.DesiredSize.Width), | ||
(float)Math.Ceiling(textBlock.DesiredSize.Height)); | ||
} | ||
|
||
/// <summary> | ||
/// Updates the properties of a <see cref="RichTextBlock"/> view. | ||
/// </summary> | ||
/// <param name="textBlock">The view.</param> | ||
public void UpdateTextBlock(TextBlock textBlock) | ||
{ | ||
UpdateTextBlockCore(textBlock, false); | ||
} | ||
|
||
private void UpdateTextBlockCore(TextBlock textBlock, bool measureOnly) | ||
{ | ||
textBlock.CharacterSpacing = _letterSpacing; | ||
textBlock.LineHeight = _lineHeight; | ||
textBlock.MaxLines = _numberOfLines; | ||
textBlock.TextAlignment = _textAlignment; | ||
textBlock.FontFamily = _fontFamily != null ? new FontFamily(_fontFamily) : FontFamily.XamlAutoFontFamily; | ||
textBlock.FontSize = _fontSize ?? 15; | ||
textBlock.FontStyle = _fontStyle ?? FontStyle.Normal; | ||
textBlock.FontWeight = _fontWeight ?? FontWeights.Normal; | ||
textBlock.IsTextScaleFactorEnabled = _allowFontScaling; | ||
textBlock.Text = _text; | ||
|
||
if (!measureOnly) | ||
{ | ||
textBlock.Padding = new Thickness( | ||
GetPadding(YogaEdge.Left), | ||
GetPadding(YogaEdge.Top), | ||
0, | ||
0); | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.