diff --git a/samples/XamlTestApplicationPcl/TestScrollable.cs b/samples/XamlTestApplicationPcl/TestScrollable.cs index 9d4c1d9b29..6a608b3465 100644 --- a/samples/XamlTestApplicationPcl/TestScrollable.cs +++ b/samples/XamlTestApplicationPcl/TestScrollable.cs @@ -62,17 +62,16 @@ namespace XamlTestApplication for (var i = (int)_offset.Y; i < itemCount; ++i) { - using (var line = new FormattedText( + var line = new FormattedText( "Item " + (i + 1), TextBlock.GetFontFamily(this), TextBlock.GetFontSize(this), + Size.Infinity, TextBlock.GetFontStyle(this), TextAlignment.Left, - TextBlock.GetFontWeight(this))) - { - context.DrawText(Brushes.Black, new Point(-_offset.X, y), line); - y += _lineSize.Height; - } + TextBlock.GetFontWeight(this)); + context.DrawText(Brushes.Black, new Point(-_offset.X, y), line); + y += _lineSize.Height; } } @@ -88,18 +87,17 @@ namespace XamlTestApplication protected override Size MeasureOverride(Size availableSize) { - using (var line = new FormattedText( + var line = new FormattedText( "Item 100", TextBlock.GetFontFamily(this), TextBlock.GetFontSize(this), + Size.Infinity, TextBlock.GetFontStyle(this), TextAlignment.Left, - TextBlock.GetFontWeight(this))) - { - line.Constraint = availableSize; - _lineSize = line.Measure(); - return new Size(_lineSize.Width, _lineSize.Height * itemCount); - } + TextBlock.GetFontWeight(this)); + line.Constraint = availableSize; + _lineSize = line.Measure(); + return new Size(_lineSize.Width, _lineSize.Height * itemCount); } protected override Size ArrangeOverride(Size finalSize) diff --git a/src/Avalonia.Base/Avalonia.Base.csproj b/src/Avalonia.Base/Avalonia.Base.csproj index 63f500270d..518f5b8f2e 100644 --- a/src/Avalonia.Base/Avalonia.Base.csproj +++ b/src/Avalonia.Base/Avalonia.Base.csproj @@ -72,7 +72,6 @@ - diff --git a/src/Avalonia.Base/AvaloniaDisposable.cs b/src/Avalonia.Base/AvaloniaDisposable.cs deleted file mode 100644 index 9e43a65f26..0000000000 --- a/src/Avalonia.Base/AvaloniaDisposable.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Avalonia.Platform; - -namespace Avalonia -{ - public abstract class AvaloniaDisposable : IDisposable - { -#if DEBUG_DISPOSE - public string DisposedAt { get; private set; } -#endif - - - public bool IsDisposed { get; private set; } - - public void Dispose() - { - IsDisposed = true; -#if DEBUG_DISPOSE - DisposedAt = AvaloniaLocator.Current.GetService().GetStackTrace(); -#endif - DoDispose(); - } - - protected void CheckDisposed() - { - if (IsDisposed) - throw new ObjectDisposedException(GetType().FullName -#if DEBUG_DISPOSE - , "Disposed at: \n" + DisposedAt -#endif - - ); - } - - protected abstract void DoDispose(); - } -} diff --git a/src/Avalonia.Controls/Presenters/TextPresenter.cs b/src/Avalonia.Controls/Presenters/TextPresenter.cs index d3cf4e5509..d29a6aaaef 100644 --- a/src/Avalonia.Controls/Presenters/TextPresenter.cs +++ b/src/Avalonia.Controls/Presenters/TextPresenter.cs @@ -226,16 +226,14 @@ namespace Avalonia.Controls.Presenters else { // TODO: Pretty sure that measuring "X" isn't the right way to do this... - using (var formattedText = new FormattedText( + return new FormattedText( "X", FontFamily, FontSize, + availableSize, FontStyle, TextAlignment, - FontWeight)) - { - return formattedText.Measure(); - } + FontWeight).Measure(); } } diff --git a/src/Avalonia.Controls/Primitives/AccessText.cs b/src/Avalonia.Controls/Primitives/AccessText.cs index 42a6f9123b..29032ae430 100644 --- a/src/Avalonia.Controls/Primitives/AccessText.cs +++ b/src/Avalonia.Controls/Primitives/AccessText.cs @@ -89,6 +89,7 @@ namespace Avalonia.Controls.Primitives StripAccessKey(Text), FontFamily, FontSize, + constraint, FontStyle, TextAlignment, FontWeight); diff --git a/src/Avalonia.Controls/TextBlock.cs b/src/Avalonia.Controls/TextBlock.cs index 028df729a5..1c8ed01e47 100644 --- a/src/Avalonia.Controls/TextBlock.cs +++ b/src/Avalonia.Controls/TextBlock.cs @@ -354,6 +354,7 @@ namespace Avalonia.Controls Text ?? string.Empty, FontFamily, FontSize, + constraint, FontStyle, TextAlignment, FontWeight, @@ -370,7 +371,6 @@ namespace Avalonia.Controls if (_formattedText != null) { _constraint = _formattedText.Constraint; - _formattedText.Dispose(); _formattedText = null; } diff --git a/src/Avalonia.HtmlRenderer/Adapters/GraphicsAdapter.cs b/src/Avalonia.HtmlRenderer/Adapters/GraphicsAdapter.cs index c9dd2a786c..72dd1d603c 100644 --- a/src/Avalonia.HtmlRenderer/Adapters/GraphicsAdapter.cs +++ b/src/Avalonia.HtmlRenderer/Adapters/GraphicsAdapter.cs @@ -117,7 +117,7 @@ namespace TheArtOfDev.HtmlRenderer.Avalonia.Adapters FormattedText GetText(string str, RFont font) { var f = ((FontAdapter)font); - return new FormattedText(str, f.Name, font.Size, f.FontStyle, TextAlignment.Left, f.Weight); + return new FormattedText(str, f.Name, font.Size, Size.Infinity, f.FontStyle, TextAlignment.Left, f.Weight); } public override void MeasureString(string str, RFont font, double maxWidth, out int charFit, out double charFitWidth) diff --git a/src/Avalonia.Visuals/Media/FormattedText.cs b/src/Avalonia.Visuals/Media/FormattedText.cs index f6aeaf5f5f..1308091b2f 100644 --- a/src/Avalonia.Visuals/Media/FormattedText.cs +++ b/src/Avalonia.Visuals/Media/FormattedText.cs @@ -10,7 +10,7 @@ namespace Avalonia.Media /// /// Represents a piece of text with formatting. /// - public class FormattedText : AvaloniaDisposable + public class FormattedText { /// /// Initializes a new instance of the class. @@ -18,6 +18,7 @@ namespace Avalonia.Media /// The text. /// The font family. /// The font size. + /// The text layout constraints. /// The font style. /// The text alignment. /// The font weight. @@ -26,6 +27,7 @@ namespace Avalonia.Media string text, string fontFamilyName, double fontSize, + Size constraint, FontStyle fontStyle = FontStyle.Normal, TextAlignment textAlignment = TextAlignment.Left, FontWeight fontWeight = FontWeight.Normal, @@ -66,7 +68,8 @@ namespace Avalonia.Media fontStyle, textAlignment, fontWeight, - wrapping); + wrapping, + constraint); } /// @@ -74,16 +77,8 @@ namespace Avalonia.Media /// public Size Constraint { - get - { - CheckDisposed(); - return PlatformImpl.Constraint; - } - set - { - CheckDisposed(); - PlatformImpl.Constraint = value; - } + get { return PlatformImpl.Constraint; } + set { PlatformImpl = PlatformImpl.WithConstraint(value); } } /// @@ -114,7 +109,7 @@ namespace Avalonia.Media /// /// Gets platform-specific platform implementation. /// - public IFormattedTextImpl PlatformImpl { get; } + public IFormattedTextImpl PlatformImpl { get; private set; } /// /// Gets the text alignment. @@ -126,14 +121,6 @@ namespace Avalonia.Media /// public TextWrapping Wrapping { get; } - /// - /// Disposes of unmanaged resources associated with the formatted text. - /// - protected override void DoDispose() - { - PlatformImpl.Dispose(); - } - /// /// Gets the lines in the text. /// @@ -142,7 +129,6 @@ namespace Avalonia.Media /// public IEnumerable GetLines() { - CheckDisposed(); return PlatformImpl.GetLines(); } @@ -155,7 +141,6 @@ namespace Avalonia.Media /// public TextHitTestResult HitTestPoint(Point point) { - CheckDisposed(); return PlatformImpl.HitTestPoint(point); } @@ -166,7 +151,6 @@ namespace Avalonia.Media /// The character bounds. public Rect HitTestTextPosition(int index) { - CheckDisposed(); return PlatformImpl.HitTestTextPosition(index); } @@ -178,7 +162,6 @@ namespace Avalonia.Media /// The character bounds. public IEnumerable HitTestTextRange(int index, int length) { - CheckDisposed(); return PlatformImpl.HitTestTextRange(index, length); } @@ -188,8 +171,7 @@ namespace Avalonia.Media /// The bounds box of the text. public Size Measure() { - CheckDisposed(); - return PlatformImpl.Measure(); + return PlatformImpl.Size; } /// @@ -200,7 +182,6 @@ namespace Avalonia.Media /// The length of the text range. public void SetForegroundBrush(IBrush brush, int startIndex, int length) { - CheckDisposed(); PlatformImpl.SetForegroundBrush(brush, startIndex, length); } } diff --git a/src/Avalonia.Visuals/Platform/IFormattedTextImpl.cs b/src/Avalonia.Visuals/Platform/IFormattedTextImpl.cs index e3a44c437e..9ffa3d6228 100644 --- a/src/Avalonia.Visuals/Platform/IFormattedTextImpl.cs +++ b/src/Avalonia.Visuals/Platform/IFormattedTextImpl.cs @@ -10,12 +10,17 @@ namespace Avalonia.Platform /// /// Defines the platform-specific interface for . /// - public interface IFormattedTextImpl : IDisposable + public interface IFormattedTextImpl { /// - /// Gets or sets the constraint of the text. + /// Gets the constraint of the text. /// - Size Constraint { get; set; } + Size Constraint { get; } + + /// + /// The measured size of the text. + /// + Size Size { get; } /// /// Gets the text. @@ -54,12 +59,6 @@ namespace Avalonia.Platform /// The character bounds. IEnumerable HitTestTextRange(int index, int length); - /// - /// Gets the size of the text, taking into account. - /// - /// The bounds box of the text. - Size Measure(); - /// /// Sets the foreground brush for the specified text range. /// @@ -67,5 +66,12 @@ namespace Avalonia.Platform /// The start of the text range. /// The length of the text range. void SetForegroundBrush(IBrush brush, int startIndex, int length); + + /// + /// Makes a clone of the formatted text with the specified constraint. + /// + /// The constraint. + /// The cloned formatted text. + IFormattedTextImpl WithConstraint(Size constraint); } } diff --git a/src/Avalonia.Visuals/Platform/IPlatformRenderInterface.cs b/src/Avalonia.Visuals/Platform/IPlatformRenderInterface.cs index c129cbd905..5cd5f9dadf 100644 --- a/src/Avalonia.Visuals/Platform/IPlatformRenderInterface.cs +++ b/src/Avalonia.Visuals/Platform/IPlatformRenderInterface.cs @@ -21,6 +21,7 @@ namespace Avalonia.Platform /// The text alignment. /// The font weight. /// The text wrapping mode. + /// The text layout constraints. /// An . IFormattedTextImpl CreateFormattedText( string text, @@ -29,7 +30,8 @@ namespace Avalonia.Platform FontStyle fontStyle, TextAlignment textAlignment, FontWeight fontWeight, - TextWrapping wrapping); + TextWrapping wrapping, + Size constraint); /// /// Creates a stream geometry implementation. diff --git a/src/Avalonia.Visuals/Rendering/DeferredRenderer.cs b/src/Avalonia.Visuals/Rendering/DeferredRenderer.cs index 2c5b31e63e..a7315fbb39 100644 --- a/src/Avalonia.Visuals/Rendering/DeferredRenderer.cs +++ b/src/Avalonia.Visuals/Rendering/DeferredRenderer.cs @@ -105,17 +105,15 @@ namespace Avalonia.Rendering } var pt = new Point(40, 40); - using ( - var txt = new FormattedText($"Frame #{_totalFrames} FPS: {_fps} Updates: {count}", "Arial", 18, - FontStyle.Normal, - TextAlignment.Left, - FontWeight.Normal, - TextWrapping.NoWrap)) - { - context.Transform = Matrix.Identity; - context.FillRectangle(Brushes.White, new Rect(pt, txt.Measure())); - context.DrawText(Brushes.Black, pt, txt.PlatformImpl); - } + var txt = new FormattedText($"Frame #{_totalFrames} FPS: {_fps} Updates: {count}", "Arial", 18, + Size.Infinity, + FontStyle.Normal, + TextAlignment.Left, + FontWeight.Normal, + TextWrapping.NoWrap); + context.Transform = Matrix.Identity; + context.FillRectangle(Brushes.White, new Rect(pt, txt.Measure())); + context.DrawText(Brushes.Black, pt, txt.PlatformImpl); } private void UpdateScene() diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/TextNode.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/TextNode.cs index 749b44eb9d..3c59960c52 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/TextNode.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/TextNode.cs @@ -11,7 +11,7 @@ namespace Avalonia.Rendering.SceneGraph { public TextNode(Matrix transform, IBrush foreground, Point origin, IFormattedTextImpl text) { - Bounds = new Rect(origin, text.Measure()) * transform; + Bounds = new Rect(origin, text.Size) * transform; Transform = transform; Foreground = foreground; Origin = origin; diff --git a/src/Gtk/Avalonia.Cairo/CairoPlatform.cs b/src/Gtk/Avalonia.Cairo/CairoPlatform.cs index e59bcfffa1..4fc177d777 100644 --- a/src/Gtk/Avalonia.Cairo/CairoPlatform.cs +++ b/src/Gtk/Avalonia.Cairo/CairoPlatform.cs @@ -45,9 +45,10 @@ namespace Avalonia.Cairo FontStyle fontStyle, TextAlignment textAlignment, Avalonia.Media.FontWeight fontWeight, - TextWrapping wrapping) + TextWrapping wrapping, + Size constraint) { - return new FormattedTextImpl(s_pangoContext, text, fontFamily, fontSize, fontStyle, textAlignment, fontWeight); + return new FormattedTextImpl(s_pangoContext, text, fontFamily, fontSize, fontStyle, textAlignment, fontWeight, constraint); } public IRenderTarget CreateRenderTarget(IPlatformHandle handle) diff --git a/src/Gtk/Avalonia.Cairo/Media/FormattedTextImpl.cs b/src/Gtk/Avalonia.Cairo/Media/FormattedTextImpl.cs index 506773aaa0..1d1bde004f 100644 --- a/src/Gtk/Avalonia.Cairo/Media/FormattedTextImpl.cs +++ b/src/Gtk/Avalonia.Cairo/Media/FormattedTextImpl.cs @@ -12,7 +12,7 @@ namespace Avalonia.Cairo.Media { public class FormattedTextImpl : IFormattedTextImpl { - private Size _size; + private Size _constraint; static double CorrectScale(double input) { @@ -26,51 +26,37 @@ namespace Avalonia.Cairo.Media double fontSize, FontStyle fontStyle, TextAlignment textAlignment, - FontWeight fontWeight) + FontWeight fontWeight, + Size constraint) { Contract.Requires(context != null); Contract.Requires(text != null); - Layout = new Pango.Layout(context); - Text = text; - Layout.SetText(text); - Layout.FontDescription = new Pango.FontDescription - { - Family = fontFamily, - Size = Pango.Units.FromDouble(CorrectScale(fontSize)), - Style = (Pango.Style)fontStyle, - Weight = fontWeight.ToCairo() - }; - Layout.Alignment = textAlignment.ToCairo(); - Layout.Attributes = new Pango.AttrList(); + Layout = Create( + context, + text, + fontFamily, + fontSize, + (Pango.Style)fontStyle, + textAlignment.ToCairo(), + fontWeight.ToCairo(), + constraint); + Size = Measure(); } - public string Text { get; } - - public Size Constraint + public FormattedTextImpl(Pango.Layout layout) { - get - { - return _size; - } - - set - { - _size = value; - Layout.Width = double.IsPositiveInfinity(value.Width) ? - -1 : Pango.Units.FromDouble(value.Width); - } + Layout = layout; + Size = Measure(); } - public Pango.Layout Layout - { - get; - } + public string Text => Layout.Text; - public void Dispose() - { - Layout.Dispose(); - } + public Size Constraint => _constraint; + + public Size Size { get; } + + public Pango.Layout Layout { get; } public IEnumerable GetLines() { @@ -125,15 +111,6 @@ namespace Avalonia.Cairo.Media return ranges; } - public Size Measure() - { - int width; - int height; - Layout.GetPixelSize(out width, out height); - - return new Size(width, height); - } - public void SetForegroundBrush(IBrush brush, int startIndex, int count) { var scb = brush as SolidColorBrush; @@ -150,5 +127,57 @@ namespace Avalonia.Cairo.Media Layout.Attributes.Insert(brushAttr); } } + + public IFormattedTextImpl WithConstraint(Size constraint) + { + return new FormattedTextImpl(Create( + Layout.Context, + Layout.Text, + Layout.FontDescription.Family, + Layout.FontDescription.Size, + Layout.FontDescription.Style, + Layout.Alignment, + Layout.FontDescription.Weight, + constraint)); + } + + private Pango.Layout Create( + Pango.Context context, + string text, + string fontFamily, + double fontSize, + Pango.Style fontStyle, + Pango.Alignment textAlignment, + Pango.Weight fontWeight, + Size constraint) + { + Contract.Requires(context != null); + Contract.Requires(text != null); + var result = new Pango.Layout(context); + + result.SetText(text); + + result.FontDescription = new Pango.FontDescription + { + Family = fontFamily, + Size = Pango.Units.FromDouble(CorrectScale(fontSize)), + Style = (Pango.Style)fontStyle, + Weight = fontWeight + }; + + result.Alignment = textAlignment; + result.Attributes = new Pango.AttrList(); + result.Width = double.IsPositiveInfinity(constraint.Width) ? -1 : Pango.Units.FromDouble(constraint.Width); + return result; + } + + private Size Measure() + { + int width; + int height; + Layout.GetPixelSize(out width, out height); + + return new Size(width, height); + } } } diff --git a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs index c3e7ce9d57..39445652bc 100644 --- a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs +++ b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs @@ -281,7 +281,7 @@ namespace Avalonia.Skia public void DrawText(IBrush foreground, Point origin, IFormattedTextImpl text) { - using (var paint = CreatePaint(foreground, text.Measure())) + using (var paint = CreatePaint(foreground, text.Size)) { var textImpl = text as FormattedTextImpl; textImpl.Draw(this, Canvas, origin.ToSKPoint(), paint); diff --git a/src/Skia/Avalonia.Skia/FormattedTextImpl.cs b/src/Skia/Avalonia.Skia/FormattedTextImpl.cs index f0bacb801f..95ae6c3195 100644 --- a/src/Skia/Avalonia.Skia/FormattedTextImpl.cs +++ b/src/Skia/Avalonia.Skia/FormattedTextImpl.cs @@ -13,7 +13,7 @@ namespace Avalonia.Skia public class FormattedTextImpl : IFormattedTextImpl { public FormattedTextImpl(string text, string fontFamilyName, double fontSize, FontStyle fontStyle, - TextAlignment textAlignment, FontWeight fontWeight, TextWrapping wrapping) + TextAlignment textAlignment, FontWeight fontWeight, TextWrapping wrapping, Size constraint) { Text = text ?? string.Empty; @@ -36,27 +36,14 @@ namespace Avalonia.Skia _paint.TextAlign = textAlignment.ToSKTextAlign(); _wrapping = wrapping; + _constraint = constraint; Rebuild(); } - public Size Constraint - { - get { return _constraint; } - set - { - if (_constraint == value) - return; + public Size Constraint => _constraint; - _constraint = value; - - Rebuild(); - } - } - - public void Dispose() - { - } + public Size Size => _size; public IEnumerable GetLines() { @@ -159,11 +146,6 @@ namespace Avalonia.Skia return result; } - public Size Measure() - { - return _size; - } - public void SetForegroundBrush(IBrush brush, int startIndex, int length) { var key = new FBrushRange(startIndex, length); @@ -185,6 +167,11 @@ namespace Avalonia.Skia return Text; } + public IFormattedTextImpl WithConstraint(Size constraint) + { + throw new NotImplementedException(); + } + internal void Draw(DrawingContextImpl context, SKCanvas canvas, SKPoint origin, DrawingContextImpl.PaintWrapper foreground) diff --git a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs index f8558c7428..d389808c1f 100644 --- a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs +++ b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs @@ -15,9 +15,9 @@ namespace Avalonia.Skia } public IFormattedTextImpl CreateFormattedText(string text, string fontFamilyName, double fontSize, FontStyle fontStyle, - TextAlignment textAlignment, FontWeight fontWeight, TextWrapping wrapping) + TextAlignment textAlignment, FontWeight fontWeight, TextWrapping wrapping, Size constraint) { - return new FormattedTextImpl(text, fontFamilyName, fontSize, fontStyle, textAlignment, fontWeight, wrapping); + return new FormattedTextImpl(text, fontFamilyName, fontSize, fontStyle, textAlignment, fontWeight, wrapping, constraint); } public IStreamGeometryImpl CreateStreamGeometry() diff --git a/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs b/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs index 8d8c175359..6b261d5f1a 100644 --- a/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs +++ b/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs @@ -37,13 +37,16 @@ namespace Avalonia.Direct2D1 private static readonly SharpDX.WIC.ImagingFactory s_imagingFactory = new SharpDX.WIC.ImagingFactory(); - public static void Initialize() => AvaloniaLocator.CurrentMutable - .Bind().ToConstant(s_instance) - .Bind().ToConstant(s_instance) - .BindToSelf(s_d2D1Factory) - .BindToSelf(s_dwfactory) - .BindToSelf(s_imagingFactory); + public static void Initialize() + { + AvaloniaLocator.CurrentMutable + .Bind().ToConstant(s_instance) + .Bind().ToConstant(s_instance) + .BindToSelf(s_d2D1Factory) + .BindToSelf(s_dwfactory) + .BindToSelf(s_imagingFactory); SharpDX.Configuration.EnableReleaseOnFinalizer = true; + } public IBitmapImpl CreateBitmap(int width, int height) { @@ -57,9 +60,18 @@ namespace Avalonia.Direct2D1 FontStyle fontStyle, TextAlignment textAlignment, FontWeight fontWeight, - TextWrapping wrapping) + TextWrapping wrapping, + Size constraint) { - return new FormattedTextImpl(text, fontFamily, fontSize, fontStyle, textAlignment, fontWeight, wrapping); + return new FormattedTextImpl( + text, + fontFamily, + fontSize, + fontStyle, + textAlignment, + fontWeight, + wrapping, + constraint); } public IRenderer CreateRenderer(IRenderRoot root, IRenderLoop renderLoop) diff --git a/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs index 2747ad7ca0..14006f2c6f 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs @@ -194,7 +194,7 @@ namespace Avalonia.Direct2D1.Media { var impl = (FormattedTextImpl)text; - using (var brush = CreateBrush(foreground, impl.Measure())) + using (var brush = CreateBrush(foreground, impl.Size)) using (var renderer = new AvaloniaTextRenderer(this, _renderTarget, brush.PlatformBrush)) { if (brush.PlatformBrush != null) diff --git a/src/Windows/Avalonia.Direct2D1/Media/FormattedTextImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/FormattedTextImpl.cs index b8e295e8ab..ec33f3e2e1 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/FormattedTextImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/FormattedTextImpl.cs @@ -19,47 +19,34 @@ namespace Avalonia.Direct2D1.Media FontStyle fontStyle, TextAlignment textAlignment, FontWeight fontWeight, - TextWrapping wrapping) + TextWrapping wrapping, + Size constraint) { - var factory = AvaloniaLocator.Current.GetService(); - Text = text; - - using (var format = new DWrite.TextFormat( - factory, + TextLayout = Create( + text, fontFamily, - (DWrite.FontWeight)fontWeight, + fontSize, (DWrite.FontStyle)fontStyle, - (float)fontSize)) - { - format.WordWrapping = wrapping == TextWrapping.Wrap ? - DWrite.WordWrapping.Wrap : DWrite.WordWrapping.NoWrap; - - TextLayout = new DWrite.TextLayout( - factory, - text ?? string.Empty, - format, - float.MaxValue, - float.MaxValue); - } - - TextLayout.TextAlignment = textAlignment.ToDirect2D(); + (DWrite.TextAlignment)textAlignment, + (DWrite.FontWeight)fontWeight, + wrapping == TextWrapping.Wrap ? DWrite.WordWrapping.Wrap : DWrite.WordWrapping.NoWrap, + (float)constraint.Width, + (float)constraint.Height); + Size = Measure(); } - public Size Constraint + public FormattedTextImpl(string text, DWrite.TextLayout textLayout) { - get - { - return new Size(TextLayout.MaxWidth, TextLayout.MaxHeight); - } - - set - { - TextLayout.MaxWidth = (float)value.Width; - TextLayout.MaxHeight = (float)value.Height; - } + Text = text; + TextLayout = textLayout; + Size = Measure(); } + public Size Constraint => new Size(TextLayout.MaxWidth, TextLayout.MaxHeight); + + public Size Size { get; } + public string Text { get; } public DWrite.TextLayout TextLayout { get; } @@ -114,7 +101,62 @@ namespace Avalonia.Direct2D1.Media return result.Select(x => new Rect(x.Left, x.Top, x.Width, x.Height)); } - public Size Measure() + public void SetForegroundBrush(IBrush brush, int startIndex, int count) + { + TextLayout.SetDrawingEffect( + new BrushWrapper(brush), + new DWrite.TextRange(startIndex, count)); + } + + public IFormattedTextImpl WithConstraint(Size constraint) + { + var factory = AvaloniaLocator.Current.GetService(); + return new FormattedTextImpl(Text, Create( + Text, + TextLayout.FontFamilyName, + TextLayout.FontSize, + TextLayout.FontStyle, + TextLayout.TextAlignment, + TextLayout.FontWeight, + TextLayout.WordWrapping, + (float)constraint.Width, + (float)constraint.Height)); + } + + private static DWrite.TextLayout Create( + string text, + string fontFamily, + double fontSize, + DWrite.FontStyle fontStyle, + DWrite.TextAlignment textAlignment, + DWrite.FontWeight fontWeight, + DWrite.WordWrapping wrapping, + float constraintX, + float constraintY) + { + var factory = AvaloniaLocator.Current.GetService(); + + using (var format = new DWrite.TextFormat( + factory, + fontFamily, + fontWeight, + fontStyle, + (float)fontSize)) + { + format.WordWrapping = wrapping; + + var result = new DWrite.TextLayout( + factory, + text ?? string.Empty, + format, + constraintX, + constraintY); + result.TextAlignment = textAlignment; + return result; + } + } + + private Size Measure() { var metrics = TextLayout.Metrics; var width = metrics.WidthIncludingTrailingWhitespace; @@ -126,12 +168,5 @@ namespace Avalonia.Direct2D1.Media return new Size(width, TextLayout.Metrics.Height); } - - public void SetForegroundBrush(IBrush brush, int startIndex, int count) - { - TextLayout.SetDrawingEffect( - new BrushWrapper(brush), - new DWrite.TextRange(startIndex, count)); - } } } diff --git a/tests/Avalonia.RenderTests/Media/FormattedTextImplTests.cs b/tests/Avalonia.RenderTests/Media/FormattedTextImplTests.cs index 395e6f8fd8..b2885b5adf 100644 --- a/tests/Avalonia.RenderTests/Media/FormattedTextImplTests.cs +++ b/tests/Avalonia.RenderTests/Media/FormattedTextImplTests.cs @@ -59,7 +59,8 @@ namespace Avalonia.Direct2D1.RenderTests.Media fontStyle, textAlignment, fontWeight, - wrapping); + wrapping, + Size.Infinity); } private IFormattedTextImpl Create(string text, double fontSize) @@ -109,17 +110,15 @@ namespace Avalonia.Direct2D1.RenderTests.Media //4.55273438 for font 12 size double heightCorr = 0.3793945*fontSize; #endif - using (var fmt = Create(input, fontSize)) - { - var size = fmt.Measure(); + var fmt = Create(input, fontSize); + var size = fmt.Size; - Assert.Equal(expWidth, size.Width, 2); - Assert.Equal(expHeight + heightCorr, size.Height, 2); + Assert.Equal(expWidth, size.Width, 2); + Assert.Equal(expHeight + heightCorr, size.Height, 2); - var linesHeight = fmt.GetLines().Sum(l => l.Height); + var linesHeight = fmt.GetLines().Sum(l => l.Height); - Assert.Equal(expHeight, linesHeight, 2); - } + Assert.Equal(expHeight, linesHeight, 2); } #if AVALONIA_CAIRO @@ -143,16 +142,16 @@ namespace Avalonia.Direct2D1.RenderTests.Media double widthConstraint, TextWrapping wrap) { - using (var fmt = Create(input, FontSize, wrap)) - { - if (widthConstraint != -1) - { - fmt.Constraint = new Size(widthConstraint, 10000); - } + var fmt = Create(input, FontSize, wrap); + var constrained = fmt; - var lines = fmt.GetLines().ToArray(); - Assert.Equal(linesCount, lines.Count()); + if (widthConstraint != -1) + { + constrained = fmt.WithConstraint(new Size(widthConstraint, 10000)); } + + var lines = constrained.GetLines().ToArray(); + Assert.Equal(linesCount, lines.Count()); } #if AVALONIA_CAIRO @@ -186,14 +185,12 @@ namespace Avalonia.Direct2D1.RenderTests.Media double x, double y, bool isInside, bool isTrailing, int pos) { - using (var fmt = Create(input, FontSize)) - { - var htRes = fmt.HitTestPoint(new Point(x, y)); + var fmt = Create(input, FontSize); + var htRes = fmt.HitTestPoint(new Point(x, y)); - Assert.Equal(pos, htRes.TextPosition); - Assert.Equal(isInside, htRes.IsInside); - Assert.Equal(isTrailing, htRes.IsTrailing); - } + Assert.Equal(pos, htRes.TextPosition); + Assert.Equal(isInside, htRes.IsInside); + Assert.Equal(isTrailing, htRes.IsTrailing); } #if AVALONIA_CAIRO @@ -213,15 +210,13 @@ namespace Avalonia.Direct2D1.RenderTests.Media public void Should_HitTestPosition_Correctly(string input, int index, double x, double y, double width, double height) { - using (var fmt = Create(input, FontSize)) - { - var r = fmt.HitTestTextPosition(index); + var fmt = Create(input, FontSize); + var r = fmt.HitTestTextPosition(index); - Assert.Equal(x, r.X, 2); - Assert.Equal(y, r.Y, 2); - Assert.Equal(width, r.Width, 2); - Assert.Equal(height, r.Height, 2); - } + Assert.Equal(x, r.X, 2); + Assert.Equal(y, r.Y, 2); + Assert.Equal(width, r.Width, 2); + Assert.Equal(height, r.Height, 2); } #if AVALONIA_CAIRO @@ -237,17 +232,20 @@ namespace Avalonia.Direct2D1.RenderTests.Media double x, double y, double width, double height) { //parse expected - using (var fmt = Create(input, FontSize, TextAlignment.Right)) + var fmt = Create(input, FontSize, TextAlignment.Right); + var constrained = fmt; + + if (widthConstraint != -1) { - fmt.Constraint = new Size(widthConstraint, 100); + constrained = fmt.WithConstraint(new Size(widthConstraint, 100)); + } - var r = fmt.HitTestTextPosition(index); + var r = constrained.HitTestTextPosition(index); - Assert.Equal(x, r.X, 2); - Assert.Equal(y, r.Y, 2); - Assert.Equal(width, r.Width, 2); - Assert.Equal(height, r.Height, 2); - } + Assert.Equal(x, r.X, 2); + Assert.Equal(y, r.Y, 2); + Assert.Equal(width, r.Width, 2); + Assert.Equal(height, r.Height, 2); } #if AVALONIA_CAIRO @@ -263,17 +261,20 @@ namespace Avalonia.Direct2D1.RenderTests.Media double x, double y, double width, double height) { //parse expected - using (var fmt = Create(input, FontSize, TextAlignment.Center)) + var fmt = Create(input, FontSize, TextAlignment.Center); + var constrained = fmt; + + if (widthConstraint != -1) { - fmt.Constraint = new Size(widthConstraint, 100); + constrained = fmt.WithConstraint(new Size(widthConstraint, 100)); + } - var r = fmt.HitTestTextPosition(index); + var r = constrained.HitTestTextPosition(index); - Assert.Equal(x, r.X, 2); - Assert.Equal(y, r.Y, 2); - Assert.Equal(width, r.Width, 2); - Assert.Equal(height, r.Height, 2); - } + Assert.Equal(x, r.X, 2); + Assert.Equal(y, r.Y, 2); + Assert.Equal(width, r.Width, 2); + Assert.Equal(height, r.Height, 2); } #if AVALONIA_CAIRO @@ -299,22 +300,20 @@ namespace Avalonia.Direct2D1.RenderTests.Media return new Rect(v[0], v[1], v[2], v[3]); }).ToArray(); - using (var fmt = Create(input, FontSize)) - { - var htRes = fmt.HitTestTextRange(index, length).ToArray(); + var fmt = Create(input, FontSize); + var htRes = fmt.HitTestTextRange(index, length).ToArray(); - Assert.Equal(rects.Length, htRes.Length); + Assert.Equal(rects.Length, htRes.Length); - for (int i = 0; i < rects.Length; i++) - { - var exr = rects[i]; - var r = htRes[i]; + for (int i = 0; i < rects.Length; i++) + { + var exr = rects[i]; + var r = htRes[i]; - Assert.Equal(exr.X, r.X, 2); - Assert.Equal(exr.Y, r.Y, 2); - Assert.Equal(exr.Width, r.Width, 2); - Assert.Equal(exr.Height, r.Height, 2); - } + Assert.Equal(exr.X, r.X, 2); + Assert.Equal(exr.Y, r.Y, 2); + Assert.Equal(exr.Width, r.Width, 2); + Assert.Equal(exr.Height, r.Height, 2); } } } diff --git a/tests/Avalonia.UnitTests/TestServices.cs b/tests/Avalonia.UnitTests/TestServices.cs index c53e4b44ca..6593216a10 100644 --- a/tests/Avalonia.UnitTests/TestServices.cs +++ b/tests/Avalonia.UnitTests/TestServices.cs @@ -163,6 +163,9 @@ namespace Avalonia.UnitTests private static IPlatformRenderInterface CreateRenderInterfaceMock() { + var formattedTextImpl = new Mock(); + formattedTextImpl.Setup(x => x.WithConstraint(It.IsAny())).Returns(() => formattedTextImpl.Object); + return Mock.Of(x => x.CreateFormattedText( It.IsAny(), @@ -171,7 +174,8 @@ namespace Avalonia.UnitTests It.IsAny(), It.IsAny(), It.IsAny(), - It.IsAny()) == Mock.Of() && + It.IsAny(), + It.IsAny()) == formattedTextImpl.Object && x.CreateStreamGeometry() == Mock.Of( y => y.Open() == Mock.Of())); } diff --git a/tests/Avalonia.Visuals.UnitTests/Media/FormattedTextTests.cs b/tests/Avalonia.Visuals.UnitTests/Media/FormattedTextTests.cs index 648595fa2b..f7b9c137ea 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/FormattedTextTests.cs +++ b/tests/Avalonia.Visuals.UnitTests/Media/FormattedTextTests.cs @@ -12,7 +12,8 @@ namespace Avalonia.Visuals.UnitTests.Media Assert.Throws(() => new FormattedText( "foo", "Ariel", - 0)); + 0, + Size.Infinity)); } [Fact] @@ -22,6 +23,7 @@ namespace Avalonia.Visuals.UnitTests.Media "foo", "Ariel", 12, + Size.Infinity, fontWeight: 0)); } } diff --git a/tests/Avalonia.Visuals.UnitTests/VisualTree/MockRenderInterface.cs b/tests/Avalonia.Visuals.UnitTests/VisualTree/MockRenderInterface.cs index 713d55a14a..1b373d93a0 100644 --- a/tests/Avalonia.Visuals.UnitTests/VisualTree/MockRenderInterface.cs +++ b/tests/Avalonia.Visuals.UnitTests/VisualTree/MockRenderInterface.cs @@ -15,7 +15,8 @@ namespace Avalonia.Visuals.UnitTests.VisualTree FontStyle fontStyle, TextAlignment textAlignment, FontWeight fontWeight, - TextWrapping wrapping) + TextWrapping wrapping, + Size constraint) { throw new NotImplementedException(); }