FormattedTextImpl.cs 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. // Copyright (c) The Avalonia Project. All rights reserved.
  2. // Licensed under the MIT license. See licence.md file in the project root for full license information.
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using Avalonia.Media;
  6. using Avalonia.Platform;
  7. using DWrite = SharpDX.DirectWrite;
  8. namespace Avalonia.Direct2D1.Media
  9. {
  10. internal class FormattedTextImpl : IFormattedTextImpl
  11. {
  12. public FormattedTextImpl(
  13. string text,
  14. Typeface typeface,
  15. TextAlignment textAlignment,
  16. TextWrapping wrapping,
  17. Size constraint,
  18. IReadOnlyList<FormattedTextStyleSpan> spans)
  19. {
  20. Text = text;
  21. using (var textFormat = Direct2D1FontCollectionCache.GetTextFormat(typeface))
  22. {
  23. textFormat.WordWrapping =
  24. wrapping == TextWrapping.Wrap ? DWrite.WordWrapping.Wrap : DWrite.WordWrapping.NoWrap;
  25. TextLayout = new DWrite.TextLayout(
  26. Direct2D1Platform.DirectWriteFactory,
  27. Text ?? string.Empty,
  28. textFormat,
  29. (float)constraint.Width,
  30. (float)constraint.Height)
  31. {
  32. TextAlignment = textAlignment.ToDirect2D()
  33. };
  34. }
  35. if (spans != null)
  36. {
  37. foreach (var span in spans)
  38. {
  39. ApplySpan(span);
  40. }
  41. }
  42. Bounds = Measure();
  43. }
  44. public Size Constraint => new Size(TextLayout.MaxWidth, TextLayout.MaxHeight);
  45. public Rect Bounds { get; }
  46. public string Text { get; }
  47. public DWrite.TextLayout TextLayout { get; }
  48. public IEnumerable<FormattedTextLine> GetLines()
  49. {
  50. var result = TextLayout.GetLineMetrics();
  51. return from line in result select new FormattedTextLine(line.Length, line.Height);
  52. }
  53. public TextHitTestResult HitTestPoint(Point point)
  54. {
  55. var result = TextLayout.HitTestPoint(
  56. (float)point.X,
  57. (float)point.Y,
  58. out var isTrailingHit,
  59. out var isInside);
  60. return new TextHitTestResult
  61. {
  62. IsInside = isInside,
  63. TextPosition = result.TextPosition,
  64. IsTrailing = isTrailingHit,
  65. };
  66. }
  67. public Rect HitTestTextPosition(int index)
  68. {
  69. var result = TextLayout.HitTestTextPosition(index, false, out _, out _);
  70. return new Rect(result.Left, result.Top, result.Width, result.Height);
  71. }
  72. public IEnumerable<Rect> HitTestTextRange(int index, int length)
  73. {
  74. var result = TextLayout.HitTestTextRange(index, length, 0, 0);
  75. return result.Select(x => new Rect(x.Left, x.Top, x.Width, x.Height));
  76. }
  77. private void ApplySpan(FormattedTextStyleSpan span)
  78. {
  79. if (span.Length > 0)
  80. {
  81. if (span.ForegroundBrush != null)
  82. {
  83. TextLayout.SetDrawingEffect(
  84. new BrushWrapper(span.ForegroundBrush.ToImmutable()),
  85. new DWrite.TextRange(span.StartIndex, span.Length));
  86. }
  87. }
  88. }
  89. private Rect Measure()
  90. {
  91. var metrics = TextLayout.Metrics;
  92. var width = metrics.WidthIncludingTrailingWhitespace;
  93. if (float.IsNaN(width))
  94. {
  95. width = metrics.Width;
  96. }
  97. return new Rect(
  98. TextLayout.Metrics.Left,
  99. TextLayout.Metrics.Top,
  100. width,
  101. TextLayout.Metrics.Height);
  102. }
  103. }
  104. }