|
@@ -1,8 +1,10 @@
|
|
using System;
|
|
using System;
|
|
using System.Collections;
|
|
using System.Collections;
|
|
|
|
+using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.ComponentModel;
|
|
using System.Diagnostics;
|
|
using System.Diagnostics;
|
|
using System.Globalization;
|
|
using System.Globalization;
|
|
|
|
+using Avalonia.Controls;
|
|
using Avalonia.Media.TextFormatting;
|
|
using Avalonia.Media.TextFormatting;
|
|
using Avalonia.Utilities;
|
|
using Avalonia.Utilities;
|
|
|
|
|
|
@@ -654,14 +656,16 @@ namespace Avalonia.Media
|
|
|
|
|
|
// line break before _currentLine, needed in case we have to reformat it with collapsing symbol
|
|
// line break before _currentLine, needed in case we have to reformat it with collapsing symbol
|
|
private TextLineBreak? _previousLineBreak;
|
|
private TextLineBreak? _previousLineBreak;
|
|
|
|
+ private int _position;
|
|
|
|
+ private int _length;
|
|
|
|
|
|
internal LineEnumerator(FormattedText text)
|
|
internal LineEnumerator(FormattedText text)
|
|
{
|
|
{
|
|
_previousHeight = 0;
|
|
_previousHeight = 0;
|
|
- Length = 0;
|
|
|
|
|
|
+ _length = 0;
|
|
_previousLineBreak = null;
|
|
_previousLineBreak = null;
|
|
|
|
|
|
- Position = 0;
|
|
|
|
|
|
+ _position = 0;
|
|
_lineCount = 0;
|
|
_lineCount = 0;
|
|
_totalHeight = 0;
|
|
_totalHeight = 0;
|
|
Current = null;
|
|
Current = null;
|
|
@@ -678,9 +682,17 @@ namespace Avalonia.Media
|
|
_nextLine = null;
|
|
_nextLine = null;
|
|
}
|
|
}
|
|
|
|
|
|
- private int Position { get; set; }
|
|
|
|
|
|
+ public int Position
|
|
|
|
+ {
|
|
|
|
+ get => _position;
|
|
|
|
+ private set => _position = value;
|
|
|
|
+ }
|
|
|
|
|
|
- private int Length { get; set; }
|
|
|
|
|
|
+ public int Length
|
|
|
|
+ {
|
|
|
|
+ get => _length;
|
|
|
|
+ private set => _length = value;
|
|
|
|
+ }
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
/// Gets the current text line in the collection
|
|
/// Gets the current text line in the collection
|
|
@@ -1292,6 +1304,92 @@ namespace Avalonia.Media
|
|
return accumulatedGeometry;
|
|
return accumulatedGeometry;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// Builds a highlight geometry object.
|
|
|
|
+ /// </summary>
|
|
|
|
+ /// <param name="origin">The origin of the highlight region</param>
|
|
|
|
+ /// <returns>Geometry that surrounds the text.</returns>
|
|
|
|
+ public Geometry? BuildHighlightGeometry(Point origin)
|
|
|
|
+ {
|
|
|
|
+ return BuildHighlightGeometry(origin, 0, _text.Length);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// Builds a highlight geometry object for a given character range.
|
|
|
|
+ /// </summary>
|
|
|
|
+ /// <param name="origin">The origin of the highlight region.</param>
|
|
|
|
+ /// <param name="startIndex">The start index of initial character the bounds should be obtained for.</param>
|
|
|
|
+ /// <param name="count">The number of characters the bounds should be obtained for.</param>
|
|
|
|
+ /// <returns>Geometry that surrounds the specified character range.</returns>
|
|
|
|
+ public Geometry? BuildHighlightGeometry(Point origin, int startIndex, int count)
|
|
|
|
+ {
|
|
|
|
+ ValidateRange(startIndex, count);
|
|
|
|
+
|
|
|
|
+ Geometry? accumulatedBounds = null;
|
|
|
|
+
|
|
|
|
+ using (var enumerator = GetEnumerator())
|
|
|
|
+ {
|
|
|
|
+ var lineOrigin = origin;
|
|
|
|
+
|
|
|
|
+ while (enumerator.MoveNext())
|
|
|
|
+ {
|
|
|
|
+ var currentLine = enumerator.Current!;
|
|
|
|
+
|
|
|
|
+ int x0 = Math.Max(enumerator.Position, startIndex);
|
|
|
|
+ int x1 = Math.Min(enumerator.Position + enumerator.Length, startIndex + count);
|
|
|
|
+
|
|
|
|
+ // check if this line is intersects with the specified character range
|
|
|
|
+ if (x0 < x1)
|
|
|
|
+ {
|
|
|
|
+ var highlightBounds = currentLine.GetTextBounds(x0,x1 - x0);
|
|
|
|
+
|
|
|
|
+ if (highlightBounds != null)
|
|
|
|
+ {
|
|
|
|
+ foreach (var bound in highlightBounds)
|
|
|
|
+ {
|
|
|
|
+ var rect = bound.Rectangle;
|
|
|
|
+
|
|
|
|
+ if (FlowDirection == FlowDirection.RightToLeft)
|
|
|
|
+ {
|
|
|
|
+ // Convert logical units (which extend leftward from the right edge
|
|
|
|
+ // of the paragraph) to physical units.
|
|
|
|
+ //
|
|
|
|
+ // Note that since rect is in logical units, rect.Right corresponds to
|
|
|
|
+ // the visual *left* edge of the rectangle in the RTL case. Specifically,
|
|
|
|
+ // is the distance leftward from the right edge of the formatting rectangle
|
|
|
|
+ // whose width is the paragraph width passed to FormatLine.
|
|
|
|
+ //
|
|
|
|
+ rect = rect.WithX(enumerator.CurrentParagraphWidth - rect.Right);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ rect = new Rect(new Point(rect.X + lineOrigin.X, rect.Y + lineOrigin.Y), rect.Size);
|
|
|
|
+
|
|
|
|
+ RectangleGeometry rectangleGeometry = new RectangleGeometry(rect);
|
|
|
|
+
|
|
|
|
+ if (accumulatedBounds == null)
|
|
|
|
+ {
|
|
|
|
+ accumulatedBounds = rectangleGeometry;
|
|
|
|
+ }
|
|
|
|
+ else
|
|
|
|
+ {
|
|
|
|
+ accumulatedBounds = Geometry.Combine(accumulatedBounds, rectangleGeometry, GeometryCombineMode.Union);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ AdvanceLineOrigin(ref lineOrigin, currentLine);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (accumulatedBounds?.PlatformImpl == null || accumulatedBounds.PlatformImpl.Bounds.IsEmpty)
|
|
|
|
+ {
|
|
|
|
+ return null;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return accumulatedBounds;
|
|
|
|
+ }
|
|
|
|
+
|
|
/// <summary>
|
|
/// <summary>
|
|
/// Draws the text object
|
|
/// Draws the text object
|
|
/// </summary>
|
|
/// </summary>
|