finished implementation of virtual space
This commit is contained in:
Родитель
3b5da7ab5f
Коммит
387d37066e
|
@ -281,14 +281,14 @@ namespace ICSharpCode.AvalonEdit.Editing
|
|||
|
||||
int caretOffset = textView.Document.GetOffset(position);
|
||||
int firstDocumentLineOffset = visualLine.FirstDocumentLine.Offset;
|
||||
position.VisualColumn = visualLine.ValidateVisualColumn(position, textArea.Options.EnableVirtualSpace);
|
||||
position.VisualColumn = visualLine.ValidateVisualColumn(position, textArea.Selection.EnableVirtualSpace);
|
||||
|
||||
// search possible caret positions
|
||||
int newVisualColumnForwards = visualLine.GetNextCaretPosition(position.VisualColumn - 1, LogicalDirection.Forward, CaretPositioningMode.Normal);
|
||||
int newVisualColumnForwards = visualLine.GetNextCaretPosition(position.VisualColumn - 1, LogicalDirection.Forward, CaretPositioningMode.Normal, textArea.Selection.EnableVirtualSpace);
|
||||
// If position.VisualColumn was valid, we're done with validation.
|
||||
if (newVisualColumnForwards != position.VisualColumn) {
|
||||
// also search backwards so that we can pick the better match
|
||||
int newVisualColumnBackwards = visualLine.GetNextCaretPosition(position.VisualColumn + 1, LogicalDirection.Backward, CaretPositioningMode.Normal);
|
||||
int newVisualColumnBackwards = visualLine.GetNextCaretPosition(position.VisualColumn + 1, LogicalDirection.Backward, CaretPositioningMode.Normal, textArea.Selection.EnableVirtualSpace);
|
||||
|
||||
if (newVisualColumnForwards < 0 && newVisualColumnBackwards < 0)
|
||||
throw ThrowUtil.NoValidCaretPosition();
|
||||
|
|
|
@ -180,7 +180,7 @@ namespace ICSharpCode.AvalonEdit.Editing
|
|||
#region Home/End
|
||||
static void MoveCaretToStartOfLine(TextArea textArea, VisualLine visualLine)
|
||||
{
|
||||
int newVC = visualLine.GetNextCaretPosition(-1, LogicalDirection.Forward, CaretPositioningMode.WordStart);
|
||||
int newVC = visualLine.GetNextCaretPosition(-1, LogicalDirection.Forward, CaretPositioningMode.WordStart, textArea.Selection.EnableVirtualSpace);
|
||||
if (newVC < 0)
|
||||
throw ThrowUtil.NoValidCaretPosition();
|
||||
// when the caret is already at the start of the text, jump to start before whitespace
|
||||
|
@ -201,7 +201,7 @@ namespace ICSharpCode.AvalonEdit.Editing
|
|||
#region By-character / By-word movement
|
||||
static void MoveCaretRight(TextArea textArea, TextViewPosition caretPosition, VisualLine visualLine, CaretPositioningMode mode)
|
||||
{
|
||||
int pos = visualLine.GetNextCaretPosition(caretPosition.VisualColumn, LogicalDirection.Forward, mode);
|
||||
int pos = visualLine.GetNextCaretPosition(caretPosition.VisualColumn, LogicalDirection.Forward, mode, textArea.Selection.EnableVirtualSpace);
|
||||
if (pos >= 0) {
|
||||
SetCaretPosition(textArea, pos, visualLine.GetRelativeOffset(pos) + visualLine.FirstDocumentLine.Offset);
|
||||
} else {
|
||||
|
@ -209,7 +209,7 @@ namespace ICSharpCode.AvalonEdit.Editing
|
|||
DocumentLine nextDocumentLine = visualLine.LastDocumentLine.NextLine;
|
||||
if (nextDocumentLine != null) {
|
||||
VisualLine nextLine = textArea.TextView.GetOrConstructVisualLine(nextDocumentLine);
|
||||
pos = nextLine.GetNextCaretPosition(-1, LogicalDirection.Forward, mode);
|
||||
pos = nextLine.GetNextCaretPosition(-1, LogicalDirection.Forward, mode, textArea.Selection.EnableVirtualSpace);
|
||||
if (pos < 0)
|
||||
throw ThrowUtil.NoValidCaretPosition();
|
||||
SetCaretPosition(textArea, pos, nextLine.GetRelativeOffset(pos) + nextLine.FirstDocumentLine.Offset);
|
||||
|
@ -223,7 +223,7 @@ namespace ICSharpCode.AvalonEdit.Editing
|
|||
|
||||
static void MoveCaretLeft(TextArea textArea, TextViewPosition caretPosition, VisualLine visualLine, CaretPositioningMode mode)
|
||||
{
|
||||
int pos = visualLine.GetNextCaretPosition(caretPosition.VisualColumn, LogicalDirection.Backward, mode);
|
||||
int pos = visualLine.GetNextCaretPosition(caretPosition.VisualColumn, LogicalDirection.Backward, mode, textArea.Selection.EnableVirtualSpace);
|
||||
if (pos >= 0) {
|
||||
SetCaretPosition(textArea, pos, visualLine.GetRelativeOffset(pos) + visualLine.FirstDocumentLine.Offset);
|
||||
} else {
|
||||
|
@ -231,7 +231,7 @@ namespace ICSharpCode.AvalonEdit.Editing
|
|||
DocumentLine previousDocumentLine = visualLine.FirstDocumentLine.PreviousLine;
|
||||
if (previousDocumentLine != null) {
|
||||
VisualLine previousLine = textArea.TextView.GetOrConstructVisualLine(previousDocumentLine);
|
||||
pos = previousLine.GetNextCaretPosition(previousLine.VisualLength + 1, LogicalDirection.Backward, mode);
|
||||
pos = previousLine.GetNextCaretPosition(previousLine.VisualLength + 1, LogicalDirection.Backward, mode, textArea.Selection.EnableVirtualSpace);
|
||||
if (pos < 0)
|
||||
throw ThrowUtil.NoValidCaretPosition();
|
||||
SetCaretPosition(textArea, pos, previousLine.GetRelativeOffset(pos) + previousLine.FirstDocumentLine.Offset);
|
||||
|
@ -307,7 +307,7 @@ namespace ICSharpCode.AvalonEdit.Editing
|
|||
}
|
||||
if (targetLine != null) {
|
||||
double yPos = targetVisualLine.GetTextLineVisualYPosition(targetLine, VisualYPosition.LineMiddle);
|
||||
int newVisualColumn = targetVisualLine.GetVisualColumn(new Point(xPos, yPos));
|
||||
int newVisualColumn = targetVisualLine.GetVisualColumn(new Point(xPos, yPos), textArea.Selection.EnableVirtualSpace);
|
||||
SetCaretPosition(textArea, targetVisualLine, targetLine, newVisualColumn, false);
|
||||
textArea.Caret.DesiredXPos = xPos;
|
||||
}
|
||||
|
|
|
@ -398,7 +398,7 @@ namespace ICSharpCode.AvalonEdit.Editing
|
|||
if (textArea.ReadOnlySectionProvider.CanInsert(currentLine.Offset)) {
|
||||
textArea.Document.Insert(currentLine.Offset, text);
|
||||
}
|
||||
} else if (rectangular && textArea.Selection.IsEmpty) {
|
||||
} else if (rectangular && textArea.Selection.IsEmpty && !(textArea.Selection is RectangleSelection)) {
|
||||
if (!RectangleSelection.PerformRectangularPaste(textArea, textArea.Caret.Position, text, false))
|
||||
textArea.ReplaceSelectionWithText(text);
|
||||
} else {
|
||||
|
|
|
@ -8,6 +8,7 @@ using System.Linq;
|
|||
using System.Text;
|
||||
using System.Windows;
|
||||
using System.Windows.Media.TextFormatting;
|
||||
|
||||
using ICSharpCode.AvalonEdit.Document;
|
||||
using ICSharpCode.AvalonEdit.Rendering;
|
||||
using ICSharpCode.AvalonEdit.Utils;
|
||||
|
@ -42,8 +43,8 @@ namespace ICSharpCode.AvalonEdit.Editing
|
|||
InitDocument();
|
||||
this.startLine = start.Line;
|
||||
this.endLine = end.Line;
|
||||
this.startXPos = GetXPos(start);
|
||||
this.endXPos = GetXPos(end);
|
||||
this.startXPos = GetXPos(textArea, start);
|
||||
this.endXPos = GetXPos(textArea, end);
|
||||
this.startOffset = document.GetOffset(start);
|
||||
this.endOffset = document.GetOffset(end);
|
||||
CalculateSegments();
|
||||
|
@ -56,7 +57,7 @@ namespace ICSharpCode.AvalonEdit.Editing
|
|||
this.startLine = startLine;
|
||||
this.endLine = end.Line;
|
||||
this.startXPos = startXPos;
|
||||
this.endXPos = GetXPos(end);
|
||||
this.endXPos = GetXPos(textArea, end);
|
||||
this.startOffset = startOffset;
|
||||
this.endOffset = document.GetOffset(end);
|
||||
CalculateSegments();
|
||||
|
@ -68,19 +69,26 @@ namespace ICSharpCode.AvalonEdit.Editing
|
|||
InitDocument();
|
||||
this.startLine = start.Line;
|
||||
this.endLine = endLine;
|
||||
this.startXPos = GetXPos(start);
|
||||
this.startXPos = GetXPos(textArea, start);
|
||||
this.endXPos = endXPos;
|
||||
this.startOffset = document.GetOffset(start);
|
||||
this.endOffset = endOffset;
|
||||
CalculateSegments();
|
||||
}
|
||||
|
||||
double GetXPos(TextViewPosition pos)
|
||||
static double GetXPos(TextArea textArea, TextViewPosition pos)
|
||||
{
|
||||
DocumentLine documentLine = document.GetLineByNumber(pos.Line);
|
||||
DocumentLine documentLine = textArea.Document.GetLineByNumber(pos.Line);
|
||||
VisualLine visualLine = textArea.TextView.GetOrConstructVisualLine(documentLine);
|
||||
TextLine textLine = visualLine.GetTextLine(pos.VisualColumn);
|
||||
return visualLine.GetTextLineVisualXPosition(textLine, pos.VisualColumn);
|
||||
int vc = visualLine.ValidateVisualColumn(pos, true);
|
||||
TextLine textLine = visualLine.GetTextLine(vc);
|
||||
return visualLine.GetTextLineVisualXPosition(textLine, vc);
|
||||
}
|
||||
|
||||
int GetVisualColumnFromXPos(int line, double xPos)
|
||||
{
|
||||
var vl = textArea.TextView.GetOrConstructVisualLine(textArea.Document.GetLineByNumber(line));
|
||||
return vl.GetVisualColumn(new Point(xPos, 0), true);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
@ -108,6 +116,11 @@ namespace ICSharpCode.AvalonEdit.Editing
|
|||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool EnableVirtualSpace {
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override ISegment SurroundingSegment {
|
||||
get {
|
||||
|
@ -120,6 +133,7 @@ namespace ICSharpCode.AvalonEdit.Editing
|
|||
get { return segments; }
|
||||
}
|
||||
|
||||
|
||||
void CalculateSegments()
|
||||
{
|
||||
DocumentLine nextLine = document.GetLineByNumber(Math.Min(startLine, endLine));
|
||||
|
@ -131,10 +145,10 @@ namespace ICSharpCode.AvalonEdit.Editing
|
|||
int baseOffset = vl.FirstDocumentLine.Offset;
|
||||
int startOffset = baseOffset + vl.GetRelativeOffset(startVC);
|
||||
int endOffset = baseOffset + vl.GetRelativeOffset(endVC);
|
||||
segments.Add(new SelectionSegment(startOffset, startVC, endOffset, endVC, true));
|
||||
segments.Add(new SelectionSegment(startOffset, startVC, endOffset, endVC));
|
||||
|
||||
nextLine = vl.LastDocumentLine.NextLine;
|
||||
} while (nextLine.LineNumber <= Math.Max(startLine, endLine));
|
||||
} while (nextLine != null && nextLine.LineNumber <= Math.Max(startLine, endLine));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
@ -156,97 +170,81 @@ namespace ICSharpCode.AvalonEdit.Editing
|
|||
/// <inheritdoc/>
|
||||
public override Selection SetEndpoint(TextViewPosition endPosition)
|
||||
{
|
||||
return new RectangleSelection(textArea, startLine, startXPos, startOffset, endPosition);
|
||||
var r = new RectangleSelection(textArea, startLine, startXPos, startOffset, endPosition);
|
||||
return r;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override Selection UpdateOnDocumentChange(DocumentChangeEventArgs e)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
// return new RectangleSelection(textArea,
|
||||
// e.GetNewOffset(StartOffset, AnchorMovementType.AfterInsertion),
|
||||
// e.GetNewOffset(EndOffset, AnchorMovementType.BeforeInsertion));
|
||||
TextLocation newStartLocation = textArea.Document.GetLocation(e.GetNewOffset(startOffset, AnchorMovementType.AfterInsertion));
|
||||
TextLocation newEndLocation = textArea.Document.GetLocation(e.GetNewOffset(endOffset, AnchorMovementType.BeforeInsertion));
|
||||
|
||||
return new RectangleSelection(textArea,
|
||||
new TextViewPosition(newStartLocation, GetVisualColumnFromXPos(newStartLocation.Line, startXPos)),
|
||||
new TextViewPosition(newEndLocation, GetVisualColumnFromXPos(newEndLocation.Line, endXPos)));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void ReplaceSelectionWithText(string newText)
|
||||
{
|
||||
throw new NotImplementedException(); /*
|
||||
if (newText == null)
|
||||
throw new ArgumentNullException("newText");
|
||||
using (textArea.Document.RunUpdate()) {
|
||||
TextLocation start = document.GetLocation(StartOffset);
|
||||
TextLocation end = document.GetLocation(EndOffset);
|
||||
int editColumn = Math.Min(start.Column, end.Column);
|
||||
TextViewPosition start = new TextViewPosition(document.GetLocation(startOffset), GetVisualColumnFromXPos(startLine, startXPos));
|
||||
TextViewPosition end = new TextViewPosition(document.GetLocation(endOffset), GetVisualColumnFromXPos(endLine, endXPos));
|
||||
int insertionLength;
|
||||
int totalInsertionLength = 0;
|
||||
int firstInsertionLength = 0;
|
||||
int editOffset = Math.Min(startOffset, endOffset);
|
||||
if (NewLineFinder.NextNewLine(newText, 0) == SimpleSegment.Invalid) {
|
||||
// insert same text into every line
|
||||
foreach (ISegment lineSegment in this.Segments.Reverse()) {
|
||||
ReplaceSingleLineText(textArea, lineSegment, newText);
|
||||
foreach (SelectionSegment lineSegment in this.Segments.Reverse()) {
|
||||
ReplaceSingleLineText(textArea, lineSegment, newText, out insertionLength);
|
||||
totalInsertionLength += insertionLength;
|
||||
firstInsertionLength = insertionLength;
|
||||
}
|
||||
|
||||
TextLocation newStart = new TextLocation(start.Line, editColumn + newText.Length);
|
||||
TextLocation newEnd = new TextLocation(end.Line, editColumn + newText.Length);
|
||||
textArea.Caret.Location = newEnd;
|
||||
textArea.Selection = new RectangleSelection(document, document.GetOffset(newStart), document.GetOffset(newEnd));
|
||||
int newEndOffset = editOffset + totalInsertionLength;
|
||||
TextViewPosition pos = new TextViewPosition(document.GetLocation(editOffset + firstInsertionLength));
|
||||
|
||||
textArea.Selection = new RectangleSelection(textArea, pos, Math.Max(startLine, endLine), GetXPos(textArea, pos), newEndOffset);
|
||||
textArea.Caret.Position = textArea.TextView.GetPosition(new Point(GetXPos(textArea, pos), textArea.TextView.GetVisualTopByDocumentLine(Math.Max(startLine, endLine)))).GetValueOrDefault();
|
||||
} else {
|
||||
// convert all segment start/ends to anchors
|
||||
var segments = this.Segments.Select(s => new AnchorSegment(this.document, s)).ToList();
|
||||
SimpleSegment ds = NewLineFinder.NextNewLine(newText, 0);
|
||||
// we'll check whether all lines have the same length. If so, we can continue using a rectangular selection.
|
||||
int commonLength = -1;
|
||||
// now insert lines into rectangular selection
|
||||
int lastDelimiterEnd = 0;
|
||||
bool isAtEnd = false;
|
||||
int i;
|
||||
for (i = 0; i < segments.Count; i++) {
|
||||
string lineText;
|
||||
if (ds == SimpleSegment.Invalid || (i == segments.Count - 1)) {
|
||||
lineText = newText.Substring(lastDelimiterEnd);
|
||||
isAtEnd = true;
|
||||
// if we have more lines to insert than this selection is long, we cannot continue using a rectangular selection
|
||||
if (ds != SimpleSegment.Invalid)
|
||||
commonLength = -1;
|
||||
} else {
|
||||
lineText = newText.Substring(lastDelimiterEnd, ds.Offset - lastDelimiterEnd);
|
||||
}
|
||||
if (i == 0) {
|
||||
commonLength = lineText.Length;
|
||||
} else if (commonLength != lineText.Length) {
|
||||
commonLength = -1;
|
||||
}
|
||||
ReplaceSingleLineText(textArea, segments[i], lineText);
|
||||
if (isAtEnd)
|
||||
break;
|
||||
lastDelimiterEnd = ds.EndOffset;
|
||||
ds = NewLineFinder.NextNewLine(newText, lastDelimiterEnd);
|
||||
}
|
||||
if (commonLength >= 0) {
|
||||
TextLocation newStart = new TextLocation(start.Line, editColumn + commonLength);
|
||||
TextLocation newEnd = new TextLocation(start.Line + i, editColumn + commonLength);
|
||||
textArea.Selection = new RectangleSelection(document, document.GetOffset(newStart), document.GetOffset(newEnd));
|
||||
} else {
|
||||
textArea.Selection = Selection.Empty;
|
||||
string[] lines = newText.Split(new[] { "\r\n", "\r", "\n" }, segments.Count, StringSplitOptions.None);
|
||||
int line = Math.Min(startLine, endLine);
|
||||
for (int i = lines.Length - 1; i >= 0; i--) {
|
||||
ReplaceSingleLineText(textArea, segments[i], lines[i], out insertionLength);
|
||||
firstInsertionLength = insertionLength;
|
||||
}
|
||||
TextViewPosition pos = new TextViewPosition(document.GetLocation(editOffset + firstInsertionLength));
|
||||
textArea.ClearSelection();
|
||||
textArea.Caret.Position = textArea.TextView.GetPosition(new Point(GetXPos(textArea, pos), textArea.TextView.GetVisualTopByDocumentLine(Math.Max(startLine, endLine)))).GetValueOrDefault();
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
static void ReplaceSingleLineText(TextArea textArea, ISegment lineSegment, string newText)
|
||||
void ReplaceSingleLineText(TextArea textArea, SelectionSegment lineSegment, string newText, out int insertionLength)
|
||||
{
|
||||
if (lineSegment.Length == 0) {
|
||||
if (newText.Length > 0 && textArea.ReadOnlySectionProvider.CanInsert(lineSegment.Offset)) {
|
||||
textArea.Document.Insert(lineSegment.Offset, newText);
|
||||
if (newText.Length > 0 && textArea.ReadOnlySectionProvider.CanInsert(lineSegment.StartOffset)) {
|
||||
newText = AddSpacesIfRequired(newText, new TextViewPosition(document.GetLocation(lineSegment.StartOffset), lineSegment.StartVisualColumn));
|
||||
textArea.Document.Insert(lineSegment.StartOffset, newText);
|
||||
}
|
||||
} else {
|
||||
ISegment[] segmentsToDelete = textArea.GetDeletableSegments(lineSegment);
|
||||
for (int i = segmentsToDelete.Length - 1; i >= 0; i--) {
|
||||
if (i == segmentsToDelete.Length - 1) {
|
||||
if (segmentsToDelete[i].Offset == SurroundingSegment.Offset && segmentsToDelete[i].Length == SurroundingSegment.Length) {
|
||||
newText = AddSpacesIfRequired(newText, new TextViewPosition(document.GetLocation(lineSegment.StartOffset), lineSegment.StartVisualColumn));
|
||||
}
|
||||
textArea.Document.Replace(segmentsToDelete[i], newText);
|
||||
} else {
|
||||
textArea.Document.Remove(segmentsToDelete[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
insertionLength = newText.Length;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -258,12 +256,12 @@ namespace ICSharpCode.AvalonEdit.Editing
|
|||
throw new ArgumentNullException("textArea");
|
||||
if (text == null)
|
||||
throw new ArgumentNullException("text");
|
||||
int newLineCount = text.Count(c => c == '\n');
|
||||
int newLineCount = text.Count(c => c == '\n'); // TODO might not work in all cases, but single \r line endings are really rare today.
|
||||
TextLocation endLocation = new TextLocation(startPosition.Line + newLineCount, startPosition.Column);
|
||||
if (endLocation.Line <= textArea.Document.LineCount) {
|
||||
int endOffset = textArea.Document.GetOffset(endLocation);
|
||||
if (textArea.Document.GetLocation(endOffset) == endLocation) {
|
||||
RectangleSelection rsel = new RectangleSelection(textArea, startPosition, new TextViewPosition(endLocation));
|
||||
if (textArea.Selection.EnableVirtualSpace || textArea.Document.GetLocation(endOffset) == endLocation) {
|
||||
RectangleSelection rsel = new RectangleSelection(textArea, startPosition, endLocation.Line, GetXPos(textArea, startPosition), endOffset);
|
||||
rsel.ReplaceSelectionWithText(text);
|
||||
if (selectInsertedText && textArea.Selection is RectangleSelection) {
|
||||
RectangleSelection sel = (RectangleSelection)textArea.Selection;
|
||||
|
@ -296,7 +294,7 @@ namespace ICSharpCode.AvalonEdit.Editing
|
|||
{
|
||||
// It's possible that ToString() gets called on old (invalid) selections, e.g. for "change from... to..." debug message
|
||||
// make sure we don't crash even when the desired locations don't exist anymore.
|
||||
return "[RectangleSelection " + startOffset + " to " + endOffset + "]";
|
||||
return string.Format("[RectangleSelection {0} {1} {2} to {3} {4} {5}]", startLine, startOffset, startXPos, endLine, endOffset, endXPos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -81,7 +81,7 @@ namespace ICSharpCode.AvalonEdit.Editing
|
|||
|
||||
internal string AddSpacesIfRequired(string newText, TextViewPosition pos)
|
||||
{
|
||||
if (textArea.Options.EnableVirtualSpace && !string.IsNullOrEmpty(newText)) {
|
||||
if (EnableVirtualSpace && !string.IsNullOrEmpty(newText) && newText != "\r\n" && newText != "\n" && newText != "\r") {
|
||||
var line = textArea.Document.GetLineByNumber(pos.Line);
|
||||
string lineText = textArea.Document.GetText(line);
|
||||
var vLine = textArea.TextView.GetOrConstructVisualLine(line);
|
||||
|
@ -112,6 +112,10 @@ namespace ICSharpCode.AvalonEdit.Editing
|
|||
get { return Length == 0; }
|
||||
}
|
||||
|
||||
public virtual bool EnableVirtualSpace {
|
||||
get { return textArea.Options.EnableVirtualSpace; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the selection length.
|
||||
/// </summary>
|
||||
|
|
|
@ -28,19 +28,24 @@ namespace ICSharpCode.AvalonEdit.Editing
|
|||
int lineStartOffset = context.VisualLine.FirstDocumentLine.Offset;
|
||||
int lineEndOffset = context.VisualLine.LastDocumentLine.Offset + context.VisualLine.LastDocumentLine.TotalLength;
|
||||
|
||||
foreach (var segment in textArea.Selection.Segments) {
|
||||
foreach (SelectionSegment segment in textArea.Selection.Segments) {
|
||||
int segmentStart = segment.StartOffset;
|
||||
int segmentEnd = segment.EndOffset;
|
||||
if (segmentEnd <= lineStartOffset)
|
||||
continue;
|
||||
if (segmentStart >= lineEndOffset)
|
||||
continue;
|
||||
int startColumn = segment.StartVisualColumn;
|
||||
int endColumn = segment.EndVisualColumn;
|
||||
if (startColumn < 0)
|
||||
startColumn = context.VisualLine.GetVisualColumn(Math.Max(0, segmentStart - lineStartOffset));
|
||||
if (endColumn < 0)
|
||||
endColumn = context.VisualLine.GetVisualColumn(segmentEnd - lineStartOffset);
|
||||
int startColumn;
|
||||
if (segmentStart < lineStartOffset)
|
||||
startColumn = 0;
|
||||
else
|
||||
startColumn = context.VisualLine.ValidateVisualColumn(segment.StartOffset, segment.StartVisualColumn, textArea.Selection.EnableVirtualSpace);
|
||||
|
||||
int endColumn;
|
||||
if (segmentEnd > lineEndOffset)
|
||||
endColumn = textArea.Selection.EnableVirtualSpace ? int.MaxValue : context.VisualLine.VisualLengthWithEndOfLineMarker;
|
||||
else
|
||||
endColumn = context.VisualLine.ValidateVisualColumn(segment.EndOffset, segment.EndVisualColumn, textArea.Selection.EnableVirtualSpace);
|
||||
|
||||
ChangeVisualElements(
|
||||
startColumn, endColumn,
|
||||
|
|
|
@ -39,7 +39,7 @@ namespace ICSharpCode.AvalonEdit.Editing
|
|||
|
||||
BackgroundGeometryBuilder geoBuilder = new BackgroundGeometryBuilder();
|
||||
geoBuilder.AlignToMiddleOfPixels = true;
|
||||
geoBuilder.ExtendToFullWidthAtLineEnd = textArea.Options.EnableVirtualSpace;
|
||||
geoBuilder.ExtendToFullWidthAtLineEnd = textArea.Selection.EnableVirtualSpace;
|
||||
geoBuilder.CornerRadius = textArea.SelectionCornerRadius;
|
||||
foreach (var segment in textArea.Selection.Segments) {
|
||||
geoBuilder.AddSegment(textView, segment);
|
||||
|
|
|
@ -9,8 +9,8 @@ using System.Runtime.InteropServices;
|
|||
using System.Windows;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media.TextFormatting;
|
||||
using System.Windows.Threading;
|
||||
|
||||
using ICSharpCode.AvalonEdit.Document;
|
||||
using ICSharpCode.AvalonEdit.Rendering;
|
||||
using ICSharpCode.AvalonEdit.Utils;
|
||||
|
@ -454,11 +454,11 @@ namespace ICSharpCode.AvalonEdit.Editing
|
|||
pos += textView.ScrollOffset;
|
||||
VisualLine line = textView.GetVisualLineFromVisualTop(pos.Y);
|
||||
if (line != null) {
|
||||
int visualColumn = line.GetVisualColumn(pos);
|
||||
int wordStartVC = line.GetNextCaretPosition(visualColumn + 1, LogicalDirection.Backward, CaretPositioningMode.WordStartOrSymbol);
|
||||
int visualColumn = line.GetVisualColumn(pos, textArea.Selection.EnableVirtualSpace);
|
||||
int wordStartVC = line.GetNextCaretPosition(visualColumn + 1, LogicalDirection.Backward, CaretPositioningMode.WordStartOrSymbol, textArea.Selection.EnableVirtualSpace);
|
||||
if (wordStartVC == -1)
|
||||
wordStartVC = 0;
|
||||
int wordEndVC = line.GetNextCaretPosition(wordStartVC, LogicalDirection.Forward, CaretPositioningMode.WordBorderOrSymbol);
|
||||
int wordEndVC = line.GetNextCaretPosition(wordStartVC, LogicalDirection.Forward, CaretPositioningMode.WordBorderOrSymbol, textArea.Selection.EnableVirtualSpace);
|
||||
if (wordEndVC == -1)
|
||||
wordEndVC = line.VisualLength;
|
||||
int relOffset = line.FirstDocumentLine.Offset;
|
||||
|
@ -507,7 +507,27 @@ namespace ICSharpCode.AvalonEdit.Editing
|
|||
pos.Y = textView.DocumentHeight - ExtensionMethods.Epsilon;
|
||||
VisualLine line = textView.GetVisualLineFromVisualTop(pos.Y);
|
||||
if (line != null) {
|
||||
visualColumn = line.GetVisualColumn(pos);
|
||||
visualColumn = line.GetVisualColumn(pos, textArea.Selection.EnableVirtualSpace);
|
||||
return line.GetRelativeOffset(visualColumn) + line.FirstDocumentLine.Offset;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int GetOffsetFromMousePositionFirstTextLineOnly(Point positionRelativeToTextView, out int visualColumn)
|
||||
{
|
||||
visualColumn = 0;
|
||||
TextView textView = textArea.TextView;
|
||||
Point pos = positionRelativeToTextView;
|
||||
if (pos.Y < 0)
|
||||
pos.Y = 0;
|
||||
if (pos.Y > textView.ActualHeight)
|
||||
pos.Y = textView.ActualHeight;
|
||||
pos += textView.ScrollOffset;
|
||||
if (pos.Y > textView.DocumentHeight)
|
||||
pos.Y = textView.DocumentHeight - ExtensionMethods.Epsilon;
|
||||
VisualLine line = textView.GetVisualLineFromVisualTop(pos.Y);
|
||||
if (line != null) {
|
||||
visualColumn = line.GetVisualColumn(line.TextLines.First(), positionRelativeToTextView.X, textArea.Selection.EnableVirtualSpace);
|
||||
return line.GetRelativeOffset(visualColumn) + line.FirstDocumentLine.Offset;
|
||||
}
|
||||
return -1;
|
||||
|
@ -548,7 +568,11 @@ namespace ICSharpCode.AvalonEdit.Editing
|
|||
void SetCaretOffsetToMousePosition(MouseEventArgs e, ISegment allowedSegment)
|
||||
{
|
||||
int visualColumn;
|
||||
int offset = GetOffsetFromMousePosition(e, out visualColumn);
|
||||
int offset;
|
||||
if (mode == SelectionMode.Rectangular)
|
||||
offset = GetOffsetFromMousePositionFirstTextLineOnly(e.GetPosition(textArea.TextView), out visualColumn);
|
||||
else
|
||||
offset = GetOffsetFromMousePosition(e, out visualColumn);
|
||||
if (allowedSegment != null) {
|
||||
offset = offset.CoerceValue(allowedSegment.Offset, allowedSegment.EndOffset);
|
||||
}
|
||||
|
|
|
@ -14,8 +14,9 @@ namespace ICSharpCode.AvalonEdit.Editing
|
|||
readonly int startOffset, endOffset;
|
||||
readonly int startVC, endVC;
|
||||
|
||||
public bool AllowVirtualSpace { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a SelectionSegment from two offsets.
|
||||
/// </summary>
|
||||
public SelectionSegment(int startOffset, int endOffset)
|
||||
{
|
||||
this.startOffset = Math.Min(startOffset, endOffset);
|
||||
|
@ -23,9 +24,12 @@ namespace ICSharpCode.AvalonEdit.Editing
|
|||
this.startVC = this.endVC = -1;
|
||||
}
|
||||
|
||||
public SelectionSegment(int startOffset, int startVC, int endOffset, int endVC, bool allowVirtualSpace)
|
||||
/// <summary>
|
||||
/// Creates a SelectionSegment from two offsets and visual columns.
|
||||
/// </summary>
|
||||
public SelectionSegment(int startOffset, int startVC, int endOffset, int endVC)
|
||||
{
|
||||
if (startOffset <= endOffset) {
|
||||
if (startOffset < endOffset || (startOffset == endOffset && startVC <= endVC)) {
|
||||
this.startOffset = startOffset;
|
||||
this.startVC = startVC;
|
||||
this.endOffset = endOffset;
|
||||
|
@ -36,25 +40,37 @@ namespace ICSharpCode.AvalonEdit.Editing
|
|||
this.endOffset = startOffset;
|
||||
this.endVC = startVC;
|
||||
}
|
||||
this.AllowVirtualSpace = allowVirtualSpace;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the start offset.
|
||||
/// </summary>
|
||||
public int StartOffset {
|
||||
get { return startOffset; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the end offset.
|
||||
/// </summary>
|
||||
public int EndOffset {
|
||||
get { return endOffset; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the start visual column.
|
||||
/// </summary>
|
||||
public int StartVisualColumn {
|
||||
get { return startVC; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the end visual column.
|
||||
/// </summary>
|
||||
public int EndVisualColumn {
|
||||
get { return endVC; }
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
int ISegment.Offset {
|
||||
get { return startOffset; }
|
||||
}
|
||||
|
@ -64,6 +80,7 @@ namespace ICSharpCode.AvalonEdit.Editing
|
|||
get { return endOffset - startOffset; }
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("[SelectionSegment StartOffset={0}, EndOffset={1}, StartVC={2}, EndVC={3}]", startOffset, endOffset, startVC, endVC);
|
||||
|
|
|
@ -33,7 +33,7 @@ namespace ICSharpCode.AvalonEdit.Editing
|
|||
/// <inheritdoc/>
|
||||
public override IEnumerable<SelectionSegment> Segments {
|
||||
get {
|
||||
return ExtensionMethods.Sequence<SelectionSegment>(new SelectionSegment(startOffset, start.VisualColumn, endOffset, end.VisualColumn, textArea.Options.EnableVirtualSpace));
|
||||
return ExtensionMethods.Sequence<SelectionSegment>(new SelectionSegment(startOffset, start.VisualColumn, endOffset, end.VisualColumn));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -378,6 +378,7 @@ namespace ICSharpCode.AvalonEdit.Editing
|
|||
/// <summary>
|
||||
/// Gets/Sets the selection in this text area.
|
||||
/// </summary>
|
||||
|
||||
public Selection Selection {
|
||||
get { return selection; }
|
||||
set {
|
||||
|
@ -390,7 +391,7 @@ namespace ICSharpCode.AvalonEdit.Editing
|
|||
if (textView != null) {
|
||||
ISegment oldSegment = selection.SurroundingSegment;
|
||||
ISegment newSegment = value.SurroundingSegment;
|
||||
if (!Options.EnableVirtualSpace && (selection is SimpleSelection && value is SimpleSelection && oldSegment != null && newSegment != null)) {
|
||||
if (!Selection.EnableVirtualSpace && (selection is SimpleSelection && value is SimpleSelection && oldSegment != null && newSegment != null)) {
|
||||
// perf optimization:
|
||||
// When a simple selection changes, don't redraw the whole selection, but only the changed parts.
|
||||
int oldSegmentOffset = oldSegment.Offset;
|
||||
|
@ -870,19 +871,19 @@ namespace ICSharpCode.AvalonEdit.Editing
|
|||
}
|
||||
}
|
||||
|
||||
internal void RemoveSelectedText()
|
||||
{
|
||||
if (this.Document == null)
|
||||
throw ThrowUtil.NoDocumentAssigned();
|
||||
selection.ReplaceSelectionWithText(string.Empty);
|
||||
#if DEBUG
|
||||
if (!selection.IsEmpty) {
|
||||
foreach (ISegment s in selection.Segments) {
|
||||
Debug.Assert(this.ReadOnlySectionProvider.GetDeletableSegments(s).Count() == 0);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
internal void RemoveSelectedText()
|
||||
{
|
||||
if (this.Document == null)
|
||||
throw ThrowUtil.NoDocumentAssigned();
|
||||
selection.ReplaceSelectionWithText(string.Empty);
|
||||
#if DEBUG
|
||||
if (!selection.IsEmpty) {
|
||||
foreach (ISegment s in selection.Segments) {
|
||||
Debug.Assert(this.ReadOnlySectionProvider.GetDeletableSegments(s).Count() == 0);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
internal void ReplaceSelectionWithText(string newText)
|
||||
{
|
||||
|
|
|
@ -262,7 +262,6 @@
|
|||
</Compile>
|
||||
<Compile Include="Rendering\LinkElementGenerator.cs" />
|
||||
<Compile Include="Rendering\MouseHoverLogic.cs" />
|
||||
<Compile Include="Rendering\NewLineElementGenerator.cs" />
|
||||
<Compile Include="Rendering\SimpleTextSource.cs">
|
||||
<DependentUpon>FormattedTextElement.cs</DependentUpon>
|
||||
</Compile>
|
||||
|
@ -340,7 +339,9 @@
|
|||
</Compile>
|
||||
<Compile Include="TextViewPosition.cs" />
|
||||
<Compile Include="Utils\Boxes.cs" />
|
||||
<Compile Include="Utils\BusyManager.cs" />
|
||||
<Compile Include="Utils\BusyManager.cs">
|
||||
<DependentUpon>ObserveAddRemoveCollection.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Utils\CharRope.cs" />
|
||||
<Compile Include="Utils\CompressingTreeList.cs" />
|
||||
<Compile Include="Utils\Constants.cs" />
|
||||
|
|
|
@ -124,7 +124,7 @@ namespace ICSharpCode.AvalonEdit.Rendering
|
|||
|
||||
int segmentEndVC;
|
||||
if (segmentEnd > vlEndOffset)
|
||||
segmentEndVC = extendToFullWidthAtLineEnd ? int.MaxValue : vl.VisualLength;
|
||||
segmentEndVC = extendToFullWidthAtLineEnd ? int.MaxValue : vl.VisualLengthWithEndOfLineMarker;
|
||||
else
|
||||
segmentEndVC = vl.ValidateVisualColumn(end, extendToFullWidthAtLineEnd);
|
||||
|
||||
|
@ -170,8 +170,8 @@ namespace ICSharpCode.AvalonEdit.Rendering
|
|||
lastRect = new Rect(Math.Min(left, right), y, Math.Abs(right - left), line.Height);
|
||||
}
|
||||
}
|
||||
if (segmentEndVC >= vl.VisualLength) {
|
||||
double left = (segmentStartVC > vl.VisualLength ? vl.GetTextLineVisualXPosition(lastTextLine, segmentStartVC) : line.Width) - scrollOffset.X;
|
||||
if (segmentEndVC >= vl.VisualLengthWithEndOfLineMarker) {
|
||||
double left = (segmentStartVC > vl.VisualLengthWithEndOfLineMarker ? vl.GetTextLineVisualXPosition(lastTextLine, segmentStartVC) : line.Width) - scrollOffset.X;
|
||||
double right = ((segmentEndVC == int.MaxValue || line != lastTextLine) ? Math.Max(((IScrollInfo)textView).ExtentWidth, ((IScrollInfo)textView).ViewportWidth) : vl.GetTextLineVisualXPosition(lastTextLine, segmentEndVC)) - scrollOffset.X;
|
||||
Rect extendSelection = new Rect(Math.Min(left, right), y, Math.Abs(right - left), line.Height);
|
||||
if (!lastRect.IsEmpty) {
|
||||
|
|
|
@ -1,88 +0,0 @@
|
|||
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
||||
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
|
||||
|
||||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.TextFormatting;
|
||||
using ICSharpCode.AvalonEdit.Document;
|
||||
using ICSharpCode.AvalonEdit.Utils;
|
||||
|
||||
namespace ICSharpCode.AvalonEdit.Rendering
|
||||
{
|
||||
// This class is internal because it does not need to be accessed by the user - it can be configured using TextEditorOptions.
|
||||
|
||||
/// <summary>
|
||||
/// Elements generator that displays "¶" at the end of lines.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This element generator can be easily enabled and configured using the
|
||||
/// <see cref="TextEditorOptions"/>.
|
||||
/// </remarks>
|
||||
sealed class NewLineElementGenerator : VisualLineElementGenerator, IBuiltinElementGenerator
|
||||
{
|
||||
void IBuiltinElementGenerator.FetchOptions(TextEditorOptions options)
|
||||
{
|
||||
}
|
||||
|
||||
public override int GetFirstInterestedOffset(int startOffset)
|
||||
{
|
||||
DocumentLine lastDocumentLine = CurrentContext.VisualLine.LastDocumentLine;
|
||||
if (lastDocumentLine.DelimiterLength > 0)
|
||||
return lastDocumentLine.Offset + lastDocumentLine.Length;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
public override VisualLineElement ConstructElement(int offset)
|
||||
{
|
||||
string newlineText;
|
||||
DocumentLine lastDocumentLine = CurrentContext.VisualLine.LastDocumentLine;
|
||||
if (lastDocumentLine.DelimiterLength == 2) {
|
||||
newlineText = "\u00B6";
|
||||
} else if (lastDocumentLine.DelimiterLength == 1) {
|
||||
char newlineChar = CurrentContext.Document.GetCharAt(lastDocumentLine.Offset + lastDocumentLine.Length);
|
||||
if (newlineChar == '\r')
|
||||
newlineText = "\\r";
|
||||
else if (newlineChar == '\n')
|
||||
newlineText = "\\n";
|
||||
else
|
||||
newlineText = "?";
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
return new NewLineTextElement(CurrentContext.TextView.cachedElements.GetTextForNonPrintableCharacter(newlineText, CurrentContext));
|
||||
}
|
||||
|
||||
sealed class NewLineTextElement : FormattedTextElement
|
||||
{
|
||||
public NewLineTextElement(TextLine text) : base(text, 0)
|
||||
{
|
||||
BreakBefore = LineBreakCondition.BreakPossible;
|
||||
BreakAfter = LineBreakCondition.BreakRestrained;
|
||||
}
|
||||
|
||||
public override int GetNextCaretPosition(int visualColumn, LogicalDirection direction, CaretPositioningMode mode)
|
||||
{
|
||||
// only place a caret stop before the newline, no caret stop after it
|
||||
if (visualColumn > this.VisualColumn && direction == LogicalDirection.Backward ||
|
||||
visualColumn < this.VisualColumn && direction == LogicalDirection.Forward)
|
||||
{
|
||||
return this.VisualColumn;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool IsWhitespace(int visualColumn)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool HandlesLineBorders {
|
||||
get { return true; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -263,7 +263,7 @@ namespace ICSharpCode.AvalonEdit.Rendering
|
|||
#endregion
|
||||
|
||||
#region Builtin ElementGenerators
|
||||
NewLineElementGenerator newLineElementGenerator;
|
||||
// NewLineElementGenerator newLineElementGenerator;
|
||||
SingleCharacterElementGenerator singleCharacterElementGenerator;
|
||||
LinkElementGenerator linkElementGenerator;
|
||||
MailLinkElementGenerator mailLinkElementGenerator;
|
||||
|
@ -272,7 +272,7 @@ namespace ICSharpCode.AvalonEdit.Rendering
|
|||
{
|
||||
TextEditorOptions options = this.Options;
|
||||
|
||||
AddRemoveDefaultElementGeneratorOnDemand(ref newLineElementGenerator, options.ShowEndOfLine);
|
||||
// AddRemoveDefaultElementGeneratorOnDemand(ref newLineElementGenerator, options.ShowEndOfLine);
|
||||
AddRemoveDefaultElementGeneratorOnDemand(ref singleCharacterElementGenerator, options.ShowBoxForControlCharacters || options.ShowSpaces || options.ShowTabs);
|
||||
AddRemoveDefaultElementGeneratorOnDemand(ref linkElementGenerator, options.EnableHyperlinks);
|
||||
AddRemoveDefaultElementGeneratorOnDemand(ref mailLinkElementGenerator, options.EnableEmailHyperlinks);
|
||||
|
@ -1013,7 +1013,7 @@ namespace ICSharpCode.AvalonEdit.Rendering
|
|||
var textLines = new List<TextLine>();
|
||||
paragraphProperties.indent = 0;
|
||||
paragraphProperties.firstLineInParagraph = true;
|
||||
while (textOffset <= visualLine.VisualLength) {
|
||||
while (textOffset <= visualLine.VisualLengthWithEndOfLineMarker) {
|
||||
TextLine textLine = formatter.FormatLine(
|
||||
textSource,
|
||||
textOffset,
|
||||
|
@ -1025,7 +1025,7 @@ namespace ICSharpCode.AvalonEdit.Rendering
|
|||
textOffset += textLine.Length;
|
||||
|
||||
// exit loop so that we don't do the indentation calculation if there's only a single line
|
||||
if (textOffset >= visualLine.VisualLength)
|
||||
if (textOffset >= visualLine.VisualLengthWithEndOfLineMarker)
|
||||
break;
|
||||
|
||||
if (paragraphProperties.firstLineInParagraph) {
|
||||
|
|
|
@ -65,6 +65,14 @@ namespace ICSharpCode.AvalonEdit.Rendering
|
|||
/// </summary>
|
||||
public int VisualLength { get; private set; }
|
||||
|
||||
public int VisualLengthWithEndOfLineMarker {
|
||||
get {
|
||||
int length = VisualLength;
|
||||
if (textView.Options.ShowEndOfLine && LastDocumentLine.NextLine != null) length++;
|
||||
return length;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the height of the visual line in device-independent pixels.
|
||||
/// </summary>
|
||||
|
@ -226,7 +234,7 @@ namespace ICSharpCode.AvalonEdit.Rendering
|
|||
{
|
||||
if (visualColumn < 0)
|
||||
throw new ArgumentOutOfRangeException("visualColumn");
|
||||
if (visualColumn >= VisualLength)
|
||||
if (visualColumn >= VisualLengthWithEndOfLineMarker)
|
||||
return TextLines[TextLines.Count - 1];
|
||||
foreach (TextLine line in TextLines) {
|
||||
if (visualColumn < line.Length)
|
||||
|
@ -322,9 +330,9 @@ namespace ICSharpCode.AvalonEdit.Rendering
|
|||
if (textLine == null)
|
||||
throw new ArgumentNullException("textLine");
|
||||
double xPos = textLine.GetDistanceFromCharacterHit(
|
||||
new CharacterHit(Math.Min(visualColumn, VisualLength), 0));
|
||||
if (visualColumn > VisualLength) {
|
||||
xPos += (visualColumn - VisualLength) * textView.WideSpaceWidth;
|
||||
new CharacterHit(Math.Min(visualColumn, VisualLengthWithEndOfLineMarker), 0));
|
||||
if (visualColumn > VisualLengthWithEndOfLineMarker) {
|
||||
xPos += (visualColumn - VisualLengthWithEndOfLineMarker) * textView.WideSpaceWidth;
|
||||
}
|
||||
return xPos;
|
||||
}
|
||||
|
@ -340,35 +348,43 @@ namespace ICSharpCode.AvalonEdit.Rendering
|
|||
|
||||
public int GetVisualColumn(Point point, bool allowVirtualSpace)
|
||||
{
|
||||
TextLine textLine = GetTextLineByVisualYPosition(point.Y);
|
||||
if (point.X > textLine.WidthIncludingTrailingWhitespace) {
|
||||
return GetVisualColumn(GetTextLineByVisualYPosition(point.Y), point.X, allowVirtualSpace);
|
||||
}
|
||||
|
||||
public int GetVisualColumn(TextLine textLine, double xPos, bool allowVirtualSpace)
|
||||
{
|
||||
if (xPos > textLine.WidthIncludingTrailingWhitespace) {
|
||||
if (allowVirtualSpace && textLine == TextLines[TextLines.Count - 1]) {
|
||||
int virtualX = (int)Math.Round((point.X - textLine.WidthIncludingTrailingWhitespace) / textView.WideSpaceWidth);
|
||||
return VisualLength + virtualX;
|
||||
int virtualX = (int)Math.Round((xPos - textLine.WidthIncludingTrailingWhitespace) / textView.WideSpaceWidth);
|
||||
return VisualLengthWithEndOfLineMarker + virtualX;
|
||||
}
|
||||
}
|
||||
CharacterHit ch = textLine.GetCharacterHitFromDistance(point.X);
|
||||
CharacterHit ch = textLine.GetCharacterHitFromDistance(xPos);
|
||||
return ch.FirstCharacterIndex + ch.TrailingLength;
|
||||
}
|
||||
|
||||
public int ValidateVisualColumn(TextViewPosition position, bool allowVirtualSpace)
|
||||
{
|
||||
int offset = Document.GetOffset(position);
|
||||
return ValidateVisualColumn(Document.GetOffset(position), position.VisualColumn, allowVirtualSpace);
|
||||
}
|
||||
|
||||
public int ValidateVisualColumn(int offset, int visualColumn, bool allowVirtualSpace)
|
||||
{
|
||||
int firstDocumentLineOffset = this.FirstDocumentLine.Offset;
|
||||
if (position.VisualColumn < 0) {
|
||||
if (visualColumn < 0) {
|
||||
return GetVisualColumn(offset - firstDocumentLineOffset);
|
||||
} else {
|
||||
int offsetFromVisualColumn = GetRelativeOffset(position.VisualColumn);
|
||||
int offsetFromVisualColumn = GetRelativeOffset(visualColumn);
|
||||
offsetFromVisualColumn += firstDocumentLineOffset;
|
||||
if (offsetFromVisualColumn != offset) {
|
||||
return GetVisualColumn(offset - firstDocumentLineOffset);
|
||||
} else {
|
||||
if (position.VisualColumn > VisualLength && !allowVirtualSpace) {
|
||||
if (visualColumn > VisualLength && !allowVirtualSpace) {
|
||||
return VisualLength;
|
||||
}
|
||||
}
|
||||
}
|
||||
return position.VisualColumn;
|
||||
return visualColumn;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -387,7 +403,7 @@ namespace ICSharpCode.AvalonEdit.Rendering
|
|||
if (allowVirtualSpace && textLine == TextLines[TextLines.Count - 1]) {
|
||||
// clicking virtual space in the last line
|
||||
int virtualX = (int)((point.X - textLine.WidthIncludingTrailingWhitespace) / textView.WideSpaceWidth);
|
||||
return VisualLength + virtualX;
|
||||
return VisualLengthWithEndOfLineMarker + virtualX;
|
||||
} else {
|
||||
// GetCharacterHitFromDistance returns a hit with FirstCharacterIndex=last character in line
|
||||
// and TrailingLength=1 when clicking behind the line, so the floor function needs to handle this case
|
||||
|
@ -407,11 +423,11 @@ namespace ICSharpCode.AvalonEdit.Rendering
|
|||
/// <summary>
|
||||
/// Gets the next possible caret position after visualColumn, or -1 if there is no caret position.
|
||||
/// </summary>
|
||||
public int GetNextCaretPosition(int visualColumn, LogicalDirection direction, CaretPositioningMode mode)
|
||||
public int GetNextCaretPosition(int visualColumn, LogicalDirection direction, CaretPositioningMode mode, bool allowVirtualSpace)
|
||||
{
|
||||
if (elements.Count == 0) {
|
||||
// special handling for empty visual lines:
|
||||
if (textView.Options.EnableVirtualSpace) {
|
||||
if (allowVirtualSpace) {
|
||||
if (direction == LogicalDirection.Forward)
|
||||
return Math.Max(0, visualColumn + 1);
|
||||
else if (visualColumn > 0)
|
||||
|
@ -436,7 +452,7 @@ namespace ICSharpCode.AvalonEdit.Rendering
|
|||
// If the last element doesn't handle line borders, return the line end as caret stop
|
||||
|
||||
if (visualColumn > this.VisualLength && !elements[elements.Count-1].HandlesLineBorders && HasImplicitStopAtLineEnd(mode)) {
|
||||
if (textView.Options.EnableVirtualSpace)
|
||||
if (allowVirtualSpace)
|
||||
return visualColumn - 1;
|
||||
else
|
||||
return this.VisualLength;
|
||||
|
@ -478,10 +494,10 @@ namespace ICSharpCode.AvalonEdit.Rendering
|
|||
}
|
||||
// if we've found nothing, and the last element doesn't handle line borders,
|
||||
// return the line end as caret stop
|
||||
if (!elements[elements.Count-1].HandlesLineBorders && HasImplicitStopAtLineEnd(mode)) {
|
||||
if ((allowVirtualSpace || !elements[elements.Count-1].HandlesLineBorders) && HasImplicitStopAtLineEnd(mode)) {
|
||||
if (visualColumn < this.VisualLength)
|
||||
return this.VisualLength;
|
||||
else if (textView.Options.EnableVirtualSpace)
|
||||
else if (allowVirtualSpace)
|
||||
return visualColumn + 1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,12 +49,33 @@ namespace ICSharpCode.AvalonEdit.Rendering
|
|||
return run;
|
||||
}
|
||||
}
|
||||
if (TextView.Options.ShowEndOfLine && textSourceCharacterIndex == VisualLine.VisualLength) {
|
||||
return CreateTextRunForNewLine();
|
||||
}
|
||||
return new TextEndOfParagraph(1);
|
||||
} catch (Exception ex) {
|
||||
Debug.WriteLine(ex.ToString());
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
TextRun CreateTextRunForNewLine()
|
||||
{
|
||||
string newlineText = "";
|
||||
DocumentLine lastDocumentLine = VisualLine.LastDocumentLine;
|
||||
if (lastDocumentLine.DelimiterLength == 2) {
|
||||
newlineText = "¶";
|
||||
} else if (lastDocumentLine.DelimiterLength == 1) {
|
||||
char newlineChar = Document.GetCharAt(lastDocumentLine.Offset + lastDocumentLine.Length);
|
||||
if (newlineChar == '\r')
|
||||
newlineText = "\\r";
|
||||
else if (newlineChar == '\n')
|
||||
newlineText = "\\n";
|
||||
else
|
||||
newlineText = "?";
|
||||
}
|
||||
return new FormattedTextRun(new FormattedTextElement(TextView.cachedElements.GetTextForNonPrintableCharacter(newlineText, this), 0), GlobalTextRunProperties);
|
||||
}
|
||||
|
||||
public override TextSpan<CultureSpecificCharacterBufferRange> GetPrecedingText(int textSourceCharacterIndexLimit)
|
||||
{
|
||||
|
|
|
@ -192,5 +192,15 @@ namespace ICSharpCode.AvalonEdit.Utils
|
|||
if (f != null && !f.IsFrozen)
|
||||
Debug.WriteLine("Performance warning: Not frozen: " + f.ToString());
|
||||
}
|
||||
|
||||
[Conditional("DEBUG")]
|
||||
public static void Log(bool condition, string format, params object[] args)
|
||||
{
|
||||
if (condition) {
|
||||
string output = DateTime.Now.ToString("hh:MM:ss") + ": " + string.Format(format, args) + Environment.NewLine + Environment.StackTrace;
|
||||
Console.WriteLine(output);
|
||||
Debug.WriteLine(output);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче