diff --git a/src/AvaloniaEdit.Demo/MainWindow.xaml b/src/AvaloniaEdit.Demo/MainWindow.xaml
index a7240b9e..e7e88234 100644
--- a/src/AvaloniaEdit.Demo/MainWindow.xaml
+++ b/src/AvaloniaEdit.Demo/MainWindow.xaml
@@ -3,6 +3,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
MinWidth="500"
MinHeight="300"
+ Width="950"
Title="AvaloniaEdit Demo"
x:Class="AvaloniaEdit.Demo.MainWindow"
Background="#1E1E1E">
@@ -17,6 +18,9 @@
+
+
+
("addControlBtn");
_addControlBtn.Click += _addControlBtn_Click;
@@ -85,6 +88,8 @@ public MainWindow()
_textEditor.Document = new TextDocument(ResourceLoader.LoadSampleFile(scopeName));
_textMateInstallation.SetGrammarByLanguageId(csharpLanguage.Id);
+ _statusTextBlock = this.Find("StatusText");
+
this.AddHandler(PointerWheelChangedEvent, (o, i) =>
{
if (i.KeyModifiers != KeyModifiers.Control) return;
@@ -93,6 +98,11 @@ public MainWindow()
}, RoutingStrategies.Bubble, true);
}
+ private void Caret_PositionChanged(object sender, EventArgs e)
+ {
+ _statusTextBlock.Text = String.Format("Line {0} Column {1}", _textEditor.TextArea.Caret.Line, _textEditor.TextArea.Caret.Column);
+ }
+
protected override void OnClosed(EventArgs e)
{
base.OnClosed(e);
diff --git a/src/AvaloniaEdit.TextMate/AvaloniaEdit.TextMate.csproj b/src/AvaloniaEdit.TextMate/AvaloniaEdit.TextMate.csproj
index 6d82808a..f55bd65f 100644
--- a/src/AvaloniaEdit.TextMate/AvaloniaEdit.TextMate.csproj
+++ b/src/AvaloniaEdit.TextMate/AvaloniaEdit.TextMate.csproj
@@ -19,7 +19,7 @@
-
+
diff --git a/src/AvaloniaEdit/Rendering/VisualLine.cs b/src/AvaloniaEdit/Rendering/VisualLine.cs
index efd674fd..38ff6c9d 100644
--- a/src/AvaloniaEdit/Rendering/VisualLine.cs
+++ b/src/AvaloniaEdit/Rendering/VisualLine.cs
@@ -37,6 +37,8 @@ namespace AvaloniaEdit.Rendering
///
public sealed class VisualLine
{
+ public const int LENGTH_LIMIT = 3000;
+
private enum LifetimePhase : byte
{
Generating,
@@ -155,6 +157,7 @@ internal void ConstructVisualElements(ITextRunConstructionContext context, Visua
private void PerformVisualElementConstruction(VisualLineElementGenerator[] generators)
{
var document = Document;
+ var lineLength = FirstDocumentLine.Length;
var offset = FirstDocumentLine.Offset;
var currentLineEnd = offset + FirstDocumentLine.Length;
LastDocumentLine = FirstDocumentLine;
@@ -164,7 +167,7 @@ private void PerformVisualElementConstruction(VisualLineElementGenerator[] gener
var textPieceEndOffset = currentLineEnd;
foreach (var g in generators)
{
- g.CachedInterest = g.GetFirstInterestedOffset(offset + askInterestOffset);
+ g.CachedInterest = (lineLength > LENGTH_LIMIT) ? -1: g.GetFirstInterestedOffset(offset + askInterestOffset);
if (g.CachedInterest != -1)
{
if (g.CachedInterest < offset)
@@ -179,7 +182,21 @@ private void PerformVisualElementConstruction(VisualLineElementGenerator[] gener
if (textPieceEndOffset > offset)
{
var textPieceLength = textPieceEndOffset - offset;
- _elements.Add(new VisualLineText(this, textPieceLength));
+ int remaining = textPieceLength;
+ while (true)
+ {
+ if (remaining > LENGTH_LIMIT)
+ {
+ // split in chunks of LENGTH_LIMIT
+ _elements.Add(new VisualLineText(this, LENGTH_LIMIT));
+ remaining -= LENGTH_LIMIT;
+ }
+ else
+ {
+ _elements.Add(new VisualLineText(this, remaining));
+ break;
+ }
+ }
offset = textPieceEndOffset;
}
// If no elements constructed / only zero-length elements constructed:
diff --git a/src/AvaloniaEdit/Text/TextLineImpl.cs b/src/AvaloniaEdit/Text/TextLineImpl.cs
index 449066dc..cfde944b 100644
--- a/src/AvaloniaEdit/Text/TextLineImpl.cs
+++ b/src/AvaloniaEdit/Text/TextLineImpl.cs
@@ -9,8 +9,6 @@ namespace AvaloniaEdit.Text
{
internal sealed class TextLineImpl : TextLine
{
- private const int MaxCharactersPerLine = 10000;
-
private readonly TextLineRun[] _runs;
public override int FirstIndex { get; }
@@ -49,11 +47,6 @@ internal static TextLineImpl Create(TextParagraphProperties paragraphProperties,
visibleLength += AddRunReturnVisibleLength(runs, prevRun);
}
- if (visibleLength >= MaxCharactersPerLine)
- {
- throw new NotSupportedException("Too many characters per line");
- }
-
while (true)
{
visibleLength += AddRunReturnVisibleLength(runs, run);
diff --git a/src/AvaloniaEdit/Text/TextLineRun.cs b/src/AvaloniaEdit/Text/TextLineRun.cs
index 8136f96c..e30038eb 100644
--- a/src/AvaloniaEdit/Text/TextLineRun.cs
+++ b/src/AvaloniaEdit/Text/TextLineRun.cs
@@ -1,8 +1,12 @@
using System;
+using System.Linq;
+
using Avalonia;
using Avalonia.Media;
using Avalonia.Media.TextFormatting;
+using AvaloniaEdit.Rendering;
+
namespace AvaloniaEdit.Text
{
internal sealed class TextLineRun
@@ -11,8 +15,7 @@ internal sealed class TextLineRun
private FormattedText _formattedText;
private Size _formattedTextSize;
- private double[] _glyphWidths;
-
+ private GlyphWidths _glyphWidths;
public StringRange StringRange { get; private set; }
public int Length { get; set; }
@@ -109,7 +112,10 @@ private static TextLineRun Create(TextSource textSource, StringRange stringRange
return new TextLineRun(textRun.Length, textRun)
{
IsEmbedded = true,
- _glyphWidths = new double[] { width },
+ _glyphWidths = new GlyphWidths(
+ stringRange,
+ textRun.Properties.Typeface.GlyphTypeface,
+ textRun.Properties.FontSize),
// Embedded objects must propagate their width to the container.
// Otherwise text runs after the embedded object are drawn at the same x position.
Width = width
@@ -162,7 +168,10 @@ private static TextLineRun CreateRunForTab(TextRun textRun)
Width = 40
};
- run.SetGlyphWidths();
+ run._glyphWidths = new GlyphWidths(
+ run.StringRange,
+ run.Typeface.GlyphTypeface,
+ run.FontSize);
return run;
}
@@ -192,7 +201,10 @@ internal static TextLineRun CreateRunForText(StringRange stringRange, TextRun te
run.Width = size.Width;
- run.SetGlyphWidths();
+ run._glyphWidths = new GlyphWidths(
+ run.StringRange,
+ run.Typeface.GlyphTypeface,
+ run.FontSize);
return run;
}
@@ -203,27 +215,6 @@ private TextLineRun(int length, TextRun textRun)
TextRun = textRun;
}
- private void SetGlyphWidths()
- {
- var result = new double[StringRange.Length];
-
- for (var i = 0; i < StringRange.Length; i++)
- {
- // TODO: is there a better way of getting glyph metrics?
- var tf = Typeface;
- var size = new FormattedText
- {
- Text = StringRange[i].ToString(),
- Typeface = new Typeface(tf.FontFamily, tf.Style, tf.Weight),
- FontSize = FontSize
- }.Bounds.Size;
-
- result[i] = size.Width;
- }
-
- _glyphWidths = result;
- }
-
public void Draw(DrawingContext drawingContext, double x, double y)
{
if (IsEmbedded)
@@ -290,7 +281,7 @@ public bool UpdateTrailingInfo(TrailingInfo trailing)
{
while (index > 0 && IsSpace(StringRange[index - 1]))
{
- trailing.SpaceWidth += _glyphWidths[index - 1];
+ trailing.SpaceWidth += _glyphWidths.GetAt(index - 1);
index--;
trailing.Count++;
}
@@ -313,7 +304,7 @@ public double GetDistanceFromCharacter(int index)
double distance = 0;
for (var i = 0; i < index; i++)
{
- distance += _glyphWidths[i];
+ distance += _glyphWidths.GetAt(i);
}
return distance;
@@ -332,7 +323,7 @@ public double GetDistanceFromCharacter(int index)
double width = 0;
for (; index < Length; index++)
{
- width = IsTab ? Width / Length : _glyphWidths[index];
+ width = IsTab ? Width / Length : _glyphWidths.GetAt(index);
if (distance < width)
{
break;
@@ -350,5 +341,55 @@ private static bool IsSpace(char ch)
{
return ch == ' ' || ch == '\u00a0';
}
+
+ class GlyphWidths
+ {
+ private const double NOT_CALCULATED_YET = -1;
+ private double[] _glyphWidths;
+ private GlyphTypeface _typeFace;
+ private StringRange _range;
+ private double _scale;
+
+ internal GlyphWidths(StringRange range, GlyphTypeface typeFace, double fontSize)
+ {
+ _range = range;
+ _typeFace = typeFace;
+ _scale = fontSize / _typeFace.DesignEmHeight;
+
+ InitGlyphWidths();
+ }
+
+ internal double GetAt(int index)
+ {
+ if (_glyphWidths[index] == NOT_CALCULATED_YET)
+ _glyphWidths[index] = MeasureGlyphAt(index);
+
+ return _glyphWidths[index];
+ }
+
+ double MeasureGlyphAt(int index)
+ {
+ return _typeFace.GetGlyphAdvance(
+ _typeFace.GetGlyph(_range[index])) * _scale;
+ }
+
+ void InitGlyphWidths()
+ {
+ int capacity = _range.Length;
+
+ bool useCheapGlyphMeasurement =
+ capacity >= VisualLine.LENGTH_LIMIT &&
+ _typeFace.IsFixedPitch;
+
+ if (useCheapGlyphMeasurement)
+ {
+ double size = MeasureGlyphAt(0);
+ _glyphWidths = Enumerable.Repeat(size, capacity).ToArray();
+ return;
+ }
+
+ _glyphWidths = Enumerable.Repeat(NOT_CALCULATED_YET, capacity).ToArray();
+ }
+ }
}
}
\ No newline at end of file