GetTextLineVisualYPosition() public method

Gets the visual top from the specified text line.
public GetTextLineVisualYPosition ( System.Windows.Media.TextFormatting.TextLine textLine, VisualYPosition yPositionMode ) : double
textLine System.Windows.Media.TextFormatting.TextLine
yPositionMode VisualYPosition
return double
        public void Render(DrawingContext drawingContext, VisualLine line, Size pixelSize)
        {
            var lineMiddle = line.GetTextLineVisualYPosition(line.TextLines[0], VisualYPosition.TextMiddle) -
                                 _textView.VerticalOffset;

            // Draw the error line
            drawingContext.DrawLine(new Pen(Brushes.DarkGoldenrod, 4), new Point(0, lineMiddle), new Point(20, lineMiddle));
        }
		static IEnumerable<Rect> ProcessTextLines(TextView textView, VisualLine visualLine, int segmentStartVC, int segmentEndVC)
		{
			TextLine lastTextLine = visualLine.TextLines.Last();
			Vector scrollOffset = textView.ScrollOffset;
			
			for (int i = 0; i < visualLine.TextLines.Count; i++) {
				TextLine line = visualLine.TextLines[i];
				double y = visualLine.GetTextLineVisualYPosition(line, VisualYPosition.LineTop);
				int visualStartCol = visualLine.GetTextLineVisualStartColumn(line);
				int visualEndCol = visualStartCol + line.Length;
				if (line != lastTextLine)
					visualEndCol -= line.TrailingWhitespaceLength;
				
				if (segmentEndVC < visualStartCol)
					break;
				if (lastTextLine != line && segmentStartVC > visualEndCol)
					continue;
				int segmentStartVCInLine = Math.Max(segmentStartVC, visualStartCol);
				int segmentEndVCInLine = Math.Min(segmentEndVC, visualEndCol);
				y -= scrollOffset.Y;
				if (segmentStartVCInLine == segmentEndVCInLine) {
					// GetTextBounds crashes for length=0, so we'll handle this case with GetDistanceFromCharacterHit
					// We need to return a rectangle to ensure empty lines are still visible
					double pos = visualLine.GetTextLineVisualXPosition(line, segmentStartVCInLine);
					pos -= scrollOffset.X;
					// The following special cases are necessary to get rid of empty rectangles at the end of a TextLine if "Show Spaces" is active.
					// If not excluded once, the same rectangle is calculated (and added) twice (since the offset could be mapped to two visual positions; end/start of line), if there is no trailing whitespace.
					// Skip this TextLine segment, if it is at the end of this line and this line is not the last line of the VisualLine and the selection continues and there is no trailing whitespace.
					if (segmentEndVCInLine == visualEndCol && i < visualLine.TextLines.Count - 1 && segmentEndVC > segmentEndVCInLine && line.TrailingWhitespaceLength == 0)
						continue;
					if (segmentStartVCInLine == visualStartCol && i > 0 && segmentStartVC < segmentStartVCInLine && visualLine.TextLines[i - 1].TrailingWhitespaceLength == 0)
						continue;
					yield return new Rect(pos, y, 1, line.Height);
				} else {
					Rect lastRect = Rect.Empty;
					if (segmentStartVCInLine <= visualEndCol) {
						foreach (TextBounds b in line.GetTextBounds(segmentStartVCInLine, segmentEndVCInLine - segmentStartVCInLine)) {
							double left = b.Rectangle.Left - scrollOffset.X;
							double right = b.Rectangle.Right - scrollOffset.X;
							if (!lastRect.IsEmpty)
								yield return lastRect;
							// left>right is possible in RTL languages
							lastRect = new Rect(Math.Min(left, right), y, Math.Abs(right - left), line.Height);
						}
					}
					if (segmentEndVC >= visualLine.VisualLengthWithEndOfLineMarker) {
						double left = (segmentStartVC > visualLine.VisualLengthWithEndOfLineMarker ? visualLine.GetTextLineVisualXPosition(lastTextLine, segmentStartVC) : line.Width) - scrollOffset.X;
						double right = ((segmentEndVC == int.MaxValue || line != lastTextLine) ? Math.Max(((IScrollInfo)textView).ExtentWidth, ((IScrollInfo)textView).ViewportWidth) : visualLine.GetTextLineVisualXPosition(lastTextLine, segmentEndVC)) - scrollOffset.X;
						Rect extendSelection = new Rect(Math.Min(left, right), y, Math.Abs(right - left), line.Height);
						if (!lastRect.IsEmpty) {
							if (extendSelection.IntersectsWith(lastRect)) {
								lastRect.Union(extendSelection);
								yield return lastRect;
							} else {
								yield return lastRect;
								yield return extendSelection;
							}
						} else
							yield return extendSelection;
					} else
						yield return lastRect;
				}
			}
		}
Beispiel #3
0
        Rect CalcCaretRectangle(VisualLine visualLine)
        {
            if (!visualColumnValid) {
                RevalidateVisualColumn(visualLine);
            }

            TextLine textLine = visualLine.GetTextLine(position.VisualColumn);
            double xPos = visualLine.GetTextLineVisualXPosition(textLine, position.VisualColumn);
            double lineTop = visualLine.GetTextLineVisualYPosition(textLine, VisualYPosition.TextTop);
            double lineBottom = visualLine.GetTextLineVisualYPosition(textLine, VisualYPosition.LineBottom);

            return new Rect(xPos,
                            lineTop,
                            SystemParameters.CaretWidth,
                            lineBottom - lineTop);
        }
Beispiel #4
0
 double GetVisualPos(VisualLine vl, TextLine tl)
 {
     double pos = vl.GetTextLineVisualYPosition(tl, VisualYPosition.LineTop) + tl.Height / 2 - TextView.VerticalOffset;
     return Math.Round(pos) + 0.5;
 }
Beispiel #5
0
		Rect CalcCaretOverstrikeRectangle(VisualLine visualLine)
		{
			if (!visualColumnValid) {
				RevalidateVisualColumn(visualLine);
			}
			
			int currentPos = position.VisualColumn;
			// The text being overwritten in overstrike mode is everything up to the next normal caret stop
			int nextPos = visualLine.GetNextCaretPosition(currentPos, LogicalDirection.Forward, CaretPositioningMode.Normal, true);
			TextLine textLine = visualLine.GetTextLine(currentPos);
			
			Rect r;
			if (currentPos < visualLine.VisualLength) {
				// If the caret is within the text, use GetTextBounds() for the text being overwritten.
				// This is necessary to ensure the rectangle is calculated correctly in bidirectional text.
				var textBounds = textLine.GetTextBounds(currentPos, nextPos - currentPos)[0];
				r = textBounds.Rectangle;
				r.Y += visualLine.GetTextLineVisualYPosition(textLine, VisualYPosition.LineTop);
			} else {
				// If the caret is at the end of the line (or in virtual space),
				// use the visual X position of currentPos and nextPos (one or more of which will be in virtual space)
				double xPos = visualLine.GetTextLineVisualXPosition(textLine, currentPos);
				double xPos2 = visualLine.GetTextLineVisualXPosition(textLine, nextPos);
				double lineTop = visualLine.GetTextLineVisualYPosition(textLine, VisualYPosition.TextTop);
				double lineBottom = visualLine.GetTextLineVisualYPosition(textLine, VisualYPosition.TextBottom);
				r = new Rect(xPos, lineTop, xPos2 - xPos, lineBottom - lineTop);
			}
			// If the caret is too small (e.g. in front of zero-width character), ensure it's still visible
			if (r.Width < SystemParameters.CaretWidth)
				r.Width = SystemParameters.CaretWidth;
			return r;
		}
        static IEnumerable <Rect> ProcessTextLines(TextView textView, VisualLine visualLine, int segmentStartVC, int segmentEndVC)
        {
            TextLine lastTextLine = visualLine.TextLines.Last();
            Vector   scrollOffset = textView.ScrollOffset;

            for (int i = 0; i < visualLine.TextLines.Count; i++)
            {
                TextLine line           = visualLine.TextLines[i];
                double   y              = visualLine.GetTextLineVisualYPosition(line, VisualYPosition.LineTop);
                int      visualStartCol = visualLine.GetTextLineVisualStartColumn(line);
                int      visualEndCol   = visualStartCol + line.Length;
                if (line != lastTextLine)
                {
                    visualEndCol -= line.TrailingWhitespaceLength;
                }

                if (segmentEndVC < visualStartCol)
                {
                    break;
                }
                if (lastTextLine != line && segmentStartVC > visualEndCol)
                {
                    continue;
                }
                int segmentStartVCInLine = Math.Max(segmentStartVC, visualStartCol);
                int segmentEndVCInLine   = Math.Min(segmentEndVC, visualEndCol);
                y -= scrollOffset.Y;
                if (segmentStartVCInLine == segmentEndVCInLine)
                {
                    // GetTextBounds crashes for length=0, so we'll handle this case with GetDistanceFromCharacterHit
                    // We need to return a rectangle to ensure empty lines are still visible
                    double pos = visualLine.GetTextLineVisualXPosition(line, segmentStartVCInLine);
                    pos -= scrollOffset.X;
                    // The following special cases are necessary to get rid of empty rectangles at the end of a TextLine if "Show Spaces" is active.
                    // If not excluded once, the same rectangle is calculated (and added) twice (since the offset could be mapped to two visual positions; end/start of line), if there is no trailing whitespace.
                    // Skip this TextLine segment, if it is at the end of this line and this line is not the last line of the VisualLine and the selection continues and there is no trailing whitespace.
                    if (segmentEndVCInLine == visualEndCol && i < visualLine.TextLines.Count - 1 && segmentEndVC > segmentEndVCInLine && line.TrailingWhitespaceLength == 0)
                    {
                        continue;
                    }
                    if (segmentStartVCInLine == visualStartCol && i > 0 && segmentStartVC < segmentStartVCInLine && visualLine.TextLines[i - 1].TrailingWhitespaceLength == 0)
                    {
                        continue;
                    }
                    yield return(new Rect(pos, y, textView.EmptyLineSelectionWidth, line.Height));
                }
                else
                {
                    Rect lastRect = Rect.Empty;
                    if (segmentStartVCInLine <= visualEndCol)
                    {
                        foreach (TextBounds b in line.GetTextBounds(segmentStartVCInLine, segmentEndVCInLine - segmentStartVCInLine))
                        {
                            double left  = b.Rectangle.Left - scrollOffset.X;
                            double right = b.Rectangle.Right - scrollOffset.X;
                            if (!lastRect.IsEmpty)
                            {
                                yield return(lastRect);
                            }
                            // left>right is possible in RTL languages
                            lastRect = new Rect(Math.Min(left, right), y, Math.Abs(right - left), line.Height);
                        }
                    }
                    if (segmentEndVC >= visualLine.VisualLengthWithEndOfLineMarker)
                    {
                        double left            = (segmentStartVC > visualLine.VisualLengthWithEndOfLineMarker ? visualLine.GetTextLineVisualXPosition(lastTextLine, segmentStartVC) : line.WidthIncludingTrailingWhitespace) - scrollOffset.X;
                        double right           = ((segmentEndVC == int.MaxValue || line != lastTextLine) ? Math.Max(((IScrollInfo)textView).ExtentWidth, ((IScrollInfo)textView).ViewportWidth) : visualLine.GetTextLineVisualXPosition(lastTextLine, segmentEndVC)) - scrollOffset.X;
                        Rect   extendSelection = new Rect(Math.Min(left, right), y, Math.Abs(right - left), line.Height);
                        if (!lastRect.IsEmpty)
                        {
                            if (extendSelection.IntersectsWith(lastRect))
                            {
                                lastRect.Union(extendSelection);
                                yield return(lastRect);
                            }
                            else
                            {
                                yield return(lastRect);

                                yield return(extendSelection);
                            }
                        }
                        else
                        {
                            yield return(extendSelection);
                        }
                    }
                    else
                    {
                        yield return(lastRect);
                    }
                }
            }
        }
 static void MoveCaretUpDown(TextArea textArea, CaretMovementType direction, VisualLine visualLine, TextLine textLine, int caretVisualColumn)
 {
     // moving up/down happens using the desired visual X position
     double xPos = textArea.Caret.DesiredXPos;
     if (double.IsNaN(xPos))
         xPos = textLine.GetDistanceFromCharacterHit(new CharacterHit(caretVisualColumn, 0));
     // now find the TextLine+VisualLine where the caret will end up in
     VisualLine targetVisualLine = visualLine;
     TextLine targetLine;
     int textLineIndex = visualLine.TextLines.IndexOf(textLine);
     switch (direction) {
         case CaretMovementType.LineUp:
             {
                 // Move up: move to the previous TextLine in the same visual line
                 // or move to the last TextLine of the previous visual line
                 int prevLineNumber = visualLine.FirstDocumentLine.LineNumber - 1;
                 if (textLineIndex > 0) {
                     targetLine = visualLine.TextLines[textLineIndex - 1];
                 } else if (prevLineNumber >= 1) {
                     DocumentLine prevLine = textArea.Document.GetLineByNumber(prevLineNumber);
                     targetVisualLine = textArea.TextView.GetOrConstructVisualLine(prevLine);
                     targetLine = targetVisualLine.TextLines[targetVisualLine.TextLines.Count - 1];
                 } else {
                     targetLine = null;
                 }
                 break;
             }
         case CaretMovementType.LineDown:
             {
                 // Move down: move to the next TextLine in the same visual line
                 // or move to the first TextLine of the next visual line
                 int nextLineNumber = visualLine.LastDocumentLine.LineNumber + 1;
                 if (textLineIndex < visualLine.TextLines.Count - 1) {
                     targetLine = visualLine.TextLines[textLineIndex + 1];
                 } else if (nextLineNumber <= textArea.Document.LineCount) {
                     DocumentLine nextLine = textArea.Document.GetLineByNumber(nextLineNumber);
                     targetVisualLine = textArea.TextView.GetOrConstructVisualLine(nextLine);
                     targetLine = targetVisualLine.TextLines[0];
                 } else {
                     targetLine = null;
                 }
                 break;
             }
         case CaretMovementType.PageUp:
         case CaretMovementType.PageDown:
             {
                 // Page up/down: find the target line using its visual position
                 double yPos = visualLine.GetTextLineVisualYPosition(textLine, VisualYPosition.LineMiddle);
                 if (direction == CaretMovementType.PageUp)
                     yPos -= textArea.TextView.RenderSize.Height;
                 else
                     yPos += textArea.TextView.RenderSize.Height;
                 DocumentLine newLine = textArea.TextView.GetDocumentLineByVisualTop(yPos);
                 targetVisualLine = textArea.TextView.GetOrConstructVisualLine(newLine);
                 targetLine = targetVisualLine.GetTextLineByVisualYPosition(yPos);
                 break;
             }
         default:
             throw new NotSupportedException(direction.ToString());
     }
     if (targetLine != null) {
         CharacterHit ch = targetLine.GetCharacterHitFromDistance(xPos);
         SetCaretPosition(textArea, targetVisualLine, targetLine, ch, false);
         textArea.Caret.DesiredXPos = xPos;
     }
 }
Beispiel #8
0
		double GetVisualPos(VisualLine vl, TextLine tl, double pixelHeight)
		{
			double pos = vl.GetTextLineVisualYPosition(tl, VisualYPosition.TextMiddle) - TextView.VerticalOffset;
			return PixelSnapHelpers.PixelAlign(pos, pixelHeight);
		}
        static IEnumerable <Rect> ProcessTextLines(TextView textView, VisualLine visualLine, int segmentStartVC, int segmentEndVC)
        {
            TextLine lastTextLine = visualLine.TextLines.Last();
            Vector   scrollOffset = textView.ScrollOffset;

            for (int i = 0; i < visualLine.TextLines.Count; i++)
            {
                TextLine line           = visualLine.TextLines[i];
                double   y              = visualLine.GetTextLineVisualYPosition(line, VisualYPosition.LineTop);
                int      visualStartCol = visualLine.GetTextLineVisualStartColumn(line);
                int      visualEndCol   = visualStartCol + line.Length;
                if (line == lastTextLine)
                {
                    visualEndCol -= 1;                     // 1 position for the TextEndOfParagraph
                }
                else
                {
                    visualEndCol -= line.TrailingWhitespaceLength;
                }

                if (segmentEndVC < visualStartCol)
                {
                    break;
                }
                if (lastTextLine != line && segmentStartVC > visualEndCol)
                {
                    continue;
                }
                int segmentStartVCInLine = Math.Max(segmentStartVC, visualStartCol);
                int segmentEndVCInLine   = Math.Min(segmentEndVC, visualEndCol);
                y -= scrollOffset.Y;
                Rect lastRect = Rect.Empty;
                if (segmentStartVCInLine == segmentEndVCInLine)
                {
                    // GetTextBounds crashes for length=0, so we'll handle this case with GetDistanceFromCharacterHit
                    // We need to return a rectangle to ensure empty lines are still visible
                    double pos = visualLine.GetTextLineVisualXPosition(line, segmentStartVCInLine);
                    pos -= scrollOffset.X;
                    // The following special cases are necessary to get rid of empty rectangles at the end of a TextLine if "Show Spaces" is active.
                    // If not excluded once, the same rectangle is calculated (and added) twice (since the offset could be mapped to two visual positions; end/start of line), if there is no trailing whitespace.
                    // Skip this TextLine segment, if it is at the end of this line and this line is not the last line of the VisualLine and the selection continues and there is no trailing whitespace.
                    if (segmentEndVCInLine == visualEndCol && i < visualLine.TextLines.Count - 1 && segmentEndVC > segmentEndVCInLine && line.TrailingWhitespaceLength == 0)
                    {
                        continue;
                    }
                    if (segmentStartVCInLine == visualStartCol && i > 0 && segmentStartVC < segmentStartVCInLine && visualLine.TextLines[i - 1].TrailingWhitespaceLength == 0)
                    {
                        continue;
                    }
                    lastRect = new Rect(pos, y, textView.EmptyLineSelectionWidth, line.Height);
                }
                else
                {
                    if (segmentStartVCInLine <= visualEndCol)
                    {
                        foreach (TextBounds b in line.GetTextBounds(segmentStartVCInLine, segmentEndVCInLine - segmentStartVCInLine))
                        {
                            double left  = b.Rectangle.Left - scrollOffset.X;
                            double right = b.Rectangle.Right - scrollOffset.X;
                            if (!lastRect.IsEmpty)
                            {
                                yield return(lastRect);
                            }
                            // left>right is possible in RTL languages
                            lastRect = new Rect(Math.Min(left, right), y, Math.Abs(right - left), line.Height);
                        }
                    }
                }
                // If the segment ends in virtual space, extend the last rectangle with the rectangle the portion of the selection
                // after the line end.
                // Also, when word-wrap is enabled and the segment continues into the next line, extend lastRect up to the end of the line.
                if (segmentEndVC > visualEndCol)
                {
                    double left, right;
                    if (segmentStartVC > visualLine.VisualLengthWithEndOfLineMarker)
                    {
                        // segmentStartVC is in virtual space
                        left = visualLine.GetTextLineVisualXPosition(lastTextLine, segmentStartVC);
                    }
                    else
                    {
                        // Otherwise, we already processed the rects from segmentStartVC up to visualEndCol,
                        // so we only need to do the remainder starting at visualEndCol.
                        // For word-wrapped lines, visualEndCol doesn't include the whitespace hidden by the wrap,
                        // so we'll need to include it here.
                        // For the last line, visualEndCol already includes the whitespace.
                        left = (line == lastTextLine ? line.WidthIncludingTrailingWhitespace : line.Width);
                    }
                    if (line != lastTextLine || segmentEndVC == int.MaxValue)
                    {
                        // If word-wrap is enabled and the segment continues into the next line,
                        // or if the extendToFullWidthAtLineEnd option is used (segmentEndVC == int.MaxValue),
                        // we select the full width of the viewport.
                        right = Math.Max(((IScrollInfo)textView).ExtentWidth, ((IScrollInfo)textView).ViewportWidth);
                    }
                    else
                    {
                        right = visualLine.GetTextLineVisualXPosition(lastTextLine, segmentEndVC);
                    }
                    Rect extendSelection = new Rect(Math.Min(left, right), y, Math.Abs(right - left), line.Height);
                    if (!lastRect.IsEmpty)
                    {
                        if (extendSelection.IntersectsWith(lastRect))
                        {
                            lastRect.Union(extendSelection);
                            yield return(lastRect);
                        }
                        else
                        {
                            // If the end of the line is in an RTL segment, keep lastRect and extendSelection separate.
                            yield return(lastRect);

                            yield return(extendSelection);
                        }
                    }
                    else
                    {
                        yield return(extendSelection);
                    }
                }
                else
                {
                    yield return(lastRect);
                }
            }
        }
		static TextViewPosition GetUpDownCaretPosition(TextView textView, TextViewPosition caretPosition, CaretMovementType direction, VisualLine visualLine, TextLine textLine, bool enableVirtualSpace, ref double xPos)
		{
			// moving up/down happens using the desired visual X position
			if (double.IsNaN(xPos))
				xPos = visualLine.GetTextLineVisualXPosition(textLine, caretPosition.VisualColumn);
			// now find the TextLine+VisualLine where the caret will end up in
			VisualLine targetVisualLine = visualLine;
			TextLine targetLine;
			int textLineIndex = visualLine.TextLines.IndexOf(textLine);
			switch (direction) {
				case CaretMovementType.LineUp:
					{
						// Move up: move to the previous TextLine in the same visual line
						// or move to the last TextLine of the previous visual line
						int prevLineNumber = visualLine.FirstDocumentLine.LineNumber - 1;
						if (textLineIndex > 0) {
							targetLine = visualLine.TextLines[textLineIndex - 1];
						} else if (prevLineNumber >= 1) {
							DocumentLine prevLine = textView.Document.GetLineByNumber(prevLineNumber);
							targetVisualLine = textView.GetOrConstructVisualLine(prevLine);
							targetLine = targetVisualLine.TextLines[targetVisualLine.TextLines.Count - 1];
						} else {
							targetLine = null;
						}
						break;
					}
				case CaretMovementType.LineDown:
					{
						// Move down: move to the next TextLine in the same visual line
						// or move to the first TextLine of the next visual line
						int nextLineNumber = visualLine.LastDocumentLine.LineNumber + 1;
						if (textLineIndex < visualLine.TextLines.Count - 1) {
							targetLine = visualLine.TextLines[textLineIndex + 1];
						} else if (nextLineNumber <= textView.Document.LineCount) {
							DocumentLine nextLine = textView.Document.GetLineByNumber(nextLineNumber);
							targetVisualLine = textView.GetOrConstructVisualLine(nextLine);
							targetLine = targetVisualLine.TextLines[0];
						} else {
							targetLine = null;
						}
						break;
					}
				case CaretMovementType.PageUp:
				case CaretMovementType.PageDown:
					{
						// Page up/down: find the target line using its visual position
						double yPos = visualLine.GetTextLineVisualYPosition(textLine, VisualYPosition.LineMiddle);
						if (direction == CaretMovementType.PageUp)
							yPos -= textView.RenderSize.Height;
						else
							yPos += textView.RenderSize.Height;
						DocumentLine newLine = textView.GetDocumentLineByVisualTop(yPos);
						targetVisualLine = textView.GetOrConstructVisualLine(newLine);
						targetLine = targetVisualLine.GetTextLineByVisualYPosition(yPos);
						break;
					}
				default:
					throw new NotSupportedException(direction.ToString());
			}
			if (targetLine != null) {
				double yPos = targetVisualLine.GetTextLineVisualYPosition(targetLine, VisualYPosition.LineMiddle);
				int newVisualColumn = targetVisualLine.GetVisualColumn(new Point(xPos, yPos), enableVirtualSpace);
				
				// prevent wrapping to the next line; TODO: could 'IsAtEnd' help here?
				int targetLineStartCol = targetVisualLine.GetTextLineVisualStartColumn(targetLine);
				if (newVisualColumn >= targetLineStartCol + targetLine.Length) {
					if (newVisualColumn <= targetVisualLine.VisualLength)
						newVisualColumn = targetLineStartCol + targetLine.Length - 1;
				}
				return targetVisualLine.GetTextViewPosition(newVisualColumn);
			} else {
				return caretPosition;
			}
		}
		static IEnumerable<Rect> ProcessTextLines(TextView textView, VisualLine visualLine, int segmentStartVC, int segmentEndVC)
		{
			TextLine lastTextLine = visualLine.TextLines.Last();
			Vector scrollOffset = textView.ScrollOffset;
			
			for (int i = 0; i < visualLine.TextLines.Count; i++) {
				TextLine line = visualLine.TextLines[i];
				double y = visualLine.GetTextLineVisualYPosition(line, VisualYPosition.LineTop);
				int visualStartCol = visualLine.GetTextLineVisualStartColumn(line);
				int visualEndCol = visualStartCol + line.Length;
				if (line == lastTextLine)
					visualEndCol -= 1; // 1 position for the TextEndOfParagraph
				else
					visualEndCol -= line.TrailingWhitespaceLength;
				
				if (segmentEndVC < visualStartCol)
					break;
				if (lastTextLine != line && segmentStartVC > visualEndCol)
					continue;
				int segmentStartVCInLine = Math.Max(segmentStartVC, visualStartCol);
				int segmentEndVCInLine = Math.Min(segmentEndVC, visualEndCol);
				y -= scrollOffset.Y;
				Rect lastRect = Rect.Empty;
				if (segmentStartVCInLine == segmentEndVCInLine) {
					// GetTextBounds crashes for length=0, so we'll handle this case with GetDistanceFromCharacterHit
					// We need to return a rectangle to ensure empty lines are still visible
					double pos = visualLine.GetTextLineVisualXPosition(line, segmentStartVCInLine);
					pos -= scrollOffset.X;
					// The following special cases are necessary to get rid of empty rectangles at the end of a TextLine if "Show Spaces" is active.
					// If not excluded once, the same rectangle is calculated (and added) twice (since the offset could be mapped to two visual positions; end/start of line), if there is no trailing whitespace.
					// Skip this TextLine segment, if it is at the end of this line and this line is not the last line of the VisualLine and the selection continues and there is no trailing whitespace.
					if (segmentEndVCInLine == visualEndCol && i < visualLine.TextLines.Count - 1 && segmentEndVC > segmentEndVCInLine && line.TrailingWhitespaceLength == 0)
						continue;
					if (segmentStartVCInLine == visualStartCol && i > 0 && segmentStartVC < segmentStartVCInLine && visualLine.TextLines[i - 1].TrailingWhitespaceLength == 0)
						continue;
					lastRect = new Rect(pos, y, textView.EmptyLineSelectionWidth, line.Height);
				} else {
					if (segmentStartVCInLine <= visualEndCol) {
						foreach (TextBounds b in line.GetTextBounds(segmentStartVCInLine, segmentEndVCInLine - segmentStartVCInLine)) {
							double left = b.Rectangle.Left - scrollOffset.X;
							double right = b.Rectangle.Right - scrollOffset.X;
							if (!lastRect.IsEmpty)
								yield return lastRect;
							// left>right is possible in RTL languages
							lastRect = new Rect(Math.Min(left, right), y, Math.Abs(right - left), line.Height);
						}
					}
				}
				// If the segment ends in virtual space, extend the last rectangle with the rectangle the portion of the selection
				// after the line end.
				// Also, when word-wrap is enabled and the segment continues into the next line, extend lastRect up to the end of the line.
				if (segmentEndVC > visualEndCol) {
					double left, right;
					if (segmentStartVC > visualLine.VisualLengthWithEndOfLineMarker) {
						// segmentStartVC is in virtual space
						left = visualLine.GetTextLineVisualXPosition(lastTextLine, segmentStartVC);
					} else {
						// Otherwise, we already processed the rects from segmentStartVC up to visualEndCol,
						// so we only need to do the remainder starting at visualEndCol.
						// For word-wrapped lines, visualEndCol doesn't include the whitespace hidden by the wrap,
						// so we'll need to include it here.
						// For the last line, visualEndCol already includes the whitespace.
						left = (line == lastTextLine ? line.WidthIncludingTrailingWhitespace : line.Width);
					}
					if (line != lastTextLine || segmentEndVC == int.MaxValue) {
						// If word-wrap is enabled and the segment continues into the next line,
						// or if the extendToFullWidthAtLineEnd option is used (segmentEndVC == int.MaxValue),
						// we select the full width of the viewport.
						right = Math.Max(((IScrollInfo)textView).ExtentWidth, ((IScrollInfo)textView).ViewportWidth);
					} else {
						right = visualLine.GetTextLineVisualXPosition(lastTextLine, segmentEndVC);
					}
					Rect extendSelection = new Rect(Math.Min(left, right), y, Math.Abs(right - left), line.Height);
					if (!lastRect.IsEmpty) {
						if (extendSelection.IntersectsWith(lastRect)) {
							lastRect.Union(extendSelection);
							yield return lastRect;
						} else {
							// If the end of the line is in an RTL segment, keep lastRect and extendSelection separate.
							yield return lastRect;
							yield return extendSelection;
						}
					} else
						yield return extendSelection;
				} else
					yield return lastRect;
			}
		}