// Token: 0x06002EE3 RID: 12003 RVA: 0x000D3B5C File Offset: 0x000D1D5C internal override ITextPointer GetNextCaretUnitPosition(ITextPointer position, LogicalDirection direction) { FixedTextPointer ftp = this.Container.VerifyPosition(position); FixedPosition fixedPosition; if (this._GetFixedPosition(ftp, out fixedPosition)) { DependencyObject element = this.FixedPage.GetElement(fixedPosition.Node); if (element is Glyphs) { Glyphs glyphs = (Glyphs)element; GlyphRun glyphRun = glyphs.ToGlyphRun(); int num = (glyphRun.Characters == null) ? 0 : glyphRun.Characters.Count; CharacterHit characterHit = (fixedPosition.Offset == num) ? new CharacterHit(fixedPosition.Offset - 1, 1) : new CharacterHit(fixedPosition.Offset, 0); CharacterHit obj = (direction == LogicalDirection.Forward) ? glyphRun.GetNextCaretCharacterHit(characterHit) : glyphRun.GetPreviousCaretCharacterHit(characterHit); if (!characterHit.Equals(obj)) { LogicalDirection edge = LogicalDirection.Forward; if (obj.TrailingLength > 0) { edge = LogicalDirection.Backward; } int offset = obj.FirstCharacterIndex + obj.TrailingLength; return(this._CreateTextPointer(new FixedPosition(fixedPosition.Node, offset), edge)); } } } ITextPointer textPointer = position.CreatePointer(); textPointer.MoveToNextInsertionPosition(direction); return(textPointer); }
/// <summary> /// Gets the visual column from a document position (relative to top left of the document). /// </summary> public int GetVisualColumn(Point point) { TextLine textLine = GetTextLineByVisualYPosition(point.Y); CharacterHit ch = textLine.GetCharacterHitFromDistance(point.X); return(ch.FirstCharacterIndex + ch.TrailingLength); }
internal Span GetAvalonTextElementSpan(Int32 bufferPosition) { if (!this.ContainsPosition(bufferPosition)) { throw new ArgumentOutOfRangeException("bufferPosition"); } if ((bufferPosition < this.LineSpan.Start) || (bufferPosition > this.LineSpan.End)) { throw new ArgumentOutOfRangeException("bufferPosition"); } if (bufferPosition > (this.LineSpan.End - this.NewlineLength)) { return(new Span(this.LineSpan.End, 0)); } if (bufferPosition == (this.LineSpan.End - this.NewlineLength)) { return(new Span(this.LineSpan.End - this.NewlineLength, this.NewlineLength)); } Int32 lineRelativePosition = this.GetLineRelativePosition(bufferPosition); if (lineRelativePosition < 0) { lineRelativePosition = 0; } Int32 indexOfLineContaining = this.GetIndexOfLineContaining(lineRelativePosition); TextLine line = _textLines[indexOfLineContaining]; CharacterHit nextCaretCharacterHit = line.GetNextCaretCharacterHit(new CharacterHit(lineRelativePosition, 0)); Int32 bufferRelativePosition = this.GetBufferRelativePosition(nextCaretCharacterHit.FirstCharacterIndex + nextCaretCharacterHit.TrailingLength); nextCaretCharacterHit = line.GetPreviousCaretCharacterHit(new CharacterHit(this.GetLineRelativePosition(bufferRelativePosition), 0)); Int32 start = this.GetBufferRelativePosition(nextCaretCharacterHit.FirstCharacterIndex); return(new Span(start, bufferRelativePosition - start)); }
/// <inheritdoc/> public override double GetDistanceFromCharacterHit(CharacterHit characterHit) { var characterIndex = characterHit.FirstCharacterIndex + (characterHit.TrailingLength != 0 ? 1 : 0); if (characterIndex > TextRange.End) { if (NewLineLength > 0) { return(Start + Width); } return(Start + WidthIncludingTrailingWhitespace); } var currentDistance = Start; foreach (var textRun in _shapedTextRuns) { if (characterIndex > textRun.Text.End) { currentDistance += textRun.Size.Width; continue; } return(currentDistance + textRun.GlyphRun.GetDistanceFromCharacterHit(new CharacterHit(characterIndex))); } return(currentDistance); }
internal int GetVisualColumnFloor(Point point, bool allowVirtualSpace, out bool isAtEndOfLine) { TextLine textLine = GetTextLineByVisualYPosition(point.Y); if (point.X > textLine.WidthIncludingTrailingWhitespace) { isAtEndOfLine = true; if (allowVirtualSpace && textLine == TextLines[TextLines.Count - 1]) { // clicking virtual space in the last line int virtualX = (int)((point.X - textLine.WidthIncludingTrailingWhitespace) / textView.WideSpaceWidth); 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 // specially and return the line's end column instead. return(GetTextLineVisualStartColumn(textLine) + textLine.Length); } } else { isAtEndOfLine = false; } CharacterHit ch = textLine.GetCharacterHitFromDistance(point.X); return(ch.FirstCharacterIndex); }
/// <summary> /// Tries to find the previous character hit. /// </summary> /// <param name="characterHit">The current character hit.</param> /// <param name="previousCharacterHit">The previous character hit.</param> /// <returns></returns> private bool TryFindPreviousCharacterHit(CharacterHit characterHit, out CharacterHit previousCharacterHit) { previousCharacterHit = characterHit; var codepointIndex = characterHit.FirstCharacterIndex + characterHit.TrailingLength; if (codepointIndex < TextRange.Start) { return(false); // Cannot go backward anymore. } var runIndex = GetRunIndexAtCodepointIndex(codepointIndex); while (runIndex >= 0) { var run = _textRuns[runIndex]; previousCharacterHit = run.GlyphRun.FindNearestCharacterHit(characterHit.FirstCharacterIndex - 1, out _); if (previousCharacterHit.FirstCharacterIndex < codepointIndex) { return(true); } runIndex--; } return(false); }
/// <inheritdoc/> public override CharacterHit GetNextCaretCharacterHit(CharacterHit characterHit) { int nextVisibleCp; bool navigableCpFound; if (characterHit.TrailingLength == 0) { navigableCpFound = FindNextCodepointIndex(characterHit.FirstCharacterIndex, out nextVisibleCp); if (navigableCpFound) { // Move from leading to trailing edge return(new CharacterHit(nextVisibleCp, 1)); } } navigableCpFound = FindNextCodepointIndex(characterHit.FirstCharacterIndex + 1, out nextVisibleCp); if (navigableCpFound) { // Move from trailing edge of current character to trailing edge of next return(new CharacterHit(nextVisibleCp, 1)); } // Can't move, we're after the last character return(characterHit); }
public override double GetDistanceFromCharacterHit(CharacterHit characterHit) { double distance = 0; int index = 0; foreach (var entry in entries) { if (entry.Item2 == null) { if (index == characterHit.FirstCharacterIndex) { return(distance); } distance += entry.Item4; index += entry.Item1.Length; continue; } index = getCharOffset(entry.Item1.CharacterBufferReference); var widthList = entry.Item2.AdvanceWidths; for (int i = 0; i < widthList.Count; i++) { if (index == characterHit.FirstCharacterIndex) { return(distance); } distance += widthList[i]; index++; } } return(distance); }
/// <inheritdoc/> public override CharacterHit GetCharacterHitFromDistance(double distance) { if (distance < 0) { // hit happens before the line, return the first position return(new CharacterHit(TextRange.Start)); } // process hit that happens within the line var characterHit = new CharacterHit(); foreach (var run in _textRuns) { characterHit = run.GlyphRun.GetCharacterHitFromDistance(distance, out _); if (distance <= run.Size.Width) { break; } distance -= run.Size.Width; } return(characterHit); }
/// <summary> /// Tries to find the next character hit. /// </summary> /// <param name="characterHit">The current character hit.</param> /// <param name="nextCharacterHit">The next character hit.</param> /// <returns></returns> private bool TryFindNextCharacterHit(CharacterHit characterHit, out CharacterHit nextCharacterHit) { nextCharacterHit = characterHit; var codepointIndex = characterHit.FirstCharacterIndex + characterHit.TrailingLength; if (codepointIndex >= TextRange.Start + TextRange.Length) { return(false); // Cannot go forward anymore } var runIndex = GetRunIndexAtCodepointIndex(codepointIndex); while (runIndex < TextRuns.Count) { var run = _textRuns[runIndex]; nextCharacterHit = run.GlyphRun.FindNearestCharacterHit(characterHit.FirstCharacterIndex + characterHit.TrailingLength, out _); if (codepointIndex <= nextCharacterHit.FirstCharacterIndex + nextCharacterHit.TrailingLength) { return(true); } runIndex++; } return(false); }
public void Should_Get_Next_Caret_CharacterHit(string text) { using (Start()) { var defaultProperties = new GenericTextRunProperties(Typeface.Default); var textSource = new SingleBufferTextSource(text, defaultProperties); var formatter = new TextFormatterImpl(); var textLine = formatter.FormatLine(textSource, 0, double.PositiveInfinity, new GenericTextParagraphProperties(defaultProperties)); var clusters = textLine.TextRuns.Cast <ShapedTextCharacters>().SelectMany(x => x.GlyphRun.GlyphClusters) .ToArray(); var nextCharacterHit = new CharacterHit(0); for (var i = 1; i < clusters.Length; i++) { nextCharacterHit = textLine.GetNextCaretCharacterHit(nextCharacterHit); Assert.Equal(clusters[i], nextCharacterHit.FirstCharacterIndex + nextCharacterHit.TrailingLength); } } }
public ICaretPosition MoveCaretToLocation(Double horizontalDistance) { Double offset = horizontalDistance - this.HorizontalOffset; if (offset < 0) { return(_textView.Caret.MoveTo(this.LineSpan.Start, CaretPlacement.LeftOfCharacter)); } if (this.NewlineLength == 0) { if (offset >= this.Width) { return(_textView.Caret.MoveTo(this.LineSpan.End, CaretPlacement.LeftOfCharacter)); } } else if (offset >= (this.Width - 7)) { return(_textView.Caret.MoveTo(this.LineSpan.End - this.NewlineLength, CaretPlacement.LeftOfCharacter)); } Int32 indexOfTextLineAtDistance = this.GetIndexOfTextLineAtDistance(offset); TextLine line = _textLines[indexOfTextLineAtDistance]; Double num3 = _textLineDistances[indexOfTextLineAtDistance]; CharacterHit characterHitFromDistance = line.GetCharacterHitFromDistance(offset - num3); Int32 bufferRelativePosition = this.GetBufferRelativePosition(characterHitFromDistance.FirstCharacterIndex); if (bufferRelativePosition > (this.LineSpan.End - this.NewlineLength)) { return(_textView.Caret.MoveTo(this.LineSpan.End - this.NewlineLength, CaretPlacement.LeftOfCharacter)); } return(_textView.Caret.MoveTo(bufferRelativePosition, (characterHitFromDistance.TrailingLength == 0) ? CaretPlacement.LeftOfCharacter : CaretPlacement.RightOfCharacter)); }
// Token: 0x06003510 RID: 13584 RVA: 0x000F0934 File Offset: 0x000EEB34 private int GlyphRunHitTest(GlyphRun run, double xoffset, bool LTR) { double distance = LTR ? (xoffset - run.BaselineOrigin.X) : (run.BaselineOrigin.X - xoffset); bool flag; CharacterHit caretCharacterHitFromDistance = run.GetCaretCharacterHitFromDistance(distance, out flag); return(caretCharacterHitFromDistance.FirstCharacterIndex + caretCharacterHitFromDistance.TrailingLength); }
//Returns the character offset in a GlyphRun given an X position private int GlyphRunHitTest(GlyphRun run, double xoffset, bool LTR) { bool isInside; double distance = LTR ? xoffset - run.BaselineOrigin.X : run.BaselineOrigin.X - xoffset; CharacterHit hit = run.GetCaretCharacterHitFromDistance(distance, out isInside); return(hit.FirstCharacterIndex + hit.TrailingLength); }
/// <inheritdoc/> public override CharacterHit GetPreviousCaretCharacterHit(CharacterHit characterHit) { if (TryFindPreviousCharacterHit(characterHit, out var previousCharacterHit)) { return(previousCharacterHit); } return(new CharacterHit(TextRange.Start)); // Can't move, we're before the first character }
/// <inheritdoc/> public override CharacterHit GetNextCaretCharacterHit(CharacterHit characterHit) { if (TryFindNextCharacterHit(characterHit, out var nextCharacterHit)) { return(nextCharacterHit); } return(new CharacterHit(TextRange.End)); // Can't move, we're after the last character }
/// <summary> /// Tries to find the next character hit. /// </summary> /// <param name="characterHit">The current character hit.</param> /// <param name="nextCharacterHit">The next character hit.</param> /// <returns></returns> private bool TryFindNextCharacterHit(CharacterHit characterHit, out CharacterHit nextCharacterHit) { nextCharacterHit = characterHit; var codepointIndex = characterHit.FirstCharacterIndex + characterHit.TrailingLength; if (codepointIndex >= TextRange.End) { return(false); // Cannot go forward anymore } if (codepointIndex < TextRange.Start) { codepointIndex = TextRange.Start; } var runIndex = GetRunIndexAtCharacterIndex(codepointIndex, LogicalDirection.Forward); while (runIndex < _textRuns.Count) { var run = _textRuns[runIndex]; var foundCharacterHit = run.GlyphRun.FindNearestCharacterHit(characterHit.FirstCharacterIndex + characterHit.TrailingLength, out _); var isAtEnd = foundCharacterHit.FirstCharacterIndex + foundCharacterHit.TrailingLength == TextRange.Length; if (isAtEnd && !run.GlyphRun.IsLeftToRight) { nextCharacterHit = foundCharacterHit; return(true); } var characterIndex = codepointIndex - run.Text.Start; if (characterIndex < 0 && run.ShapedBuffer.IsLeftToRight) { foundCharacterHit = new CharacterHit(foundCharacterHit.FirstCharacterIndex); } nextCharacterHit = isAtEnd || characterHit.TrailingLength != 0 ? foundCharacterHit : new CharacterHit(foundCharacterHit.FirstCharacterIndex + foundCharacterHit.TrailingLength); if (isAtEnd || nextCharacterHit.FirstCharacterIndex > characterHit.FirstCharacterIndex) { return(true); } runIndex++; } return(false); }
/// <summary> /// Calculates the offset for the corresponding TextPointer from a CharacterHit /// </summary> /// <remarks> /// This is necessary to ensure that we don't try to create a position at an offset greater than TextContainer's symbol count. /// This may happen when a line is collapsed with ellipsis and we hit-test at the trailing edge of ellipsis, the trailing length /// returned for the CharacterHit is the length of all collapsed characters, including the synthetic EOP. If we try to /// create a position at this trailing length we can exceed TextContainer's symbol count. /// </remarks> private int CalcPositionOffset(CharacterHit charHit) { int offset = charHit.FirstCharacterIndex + charHit.TrailingLength; if (this.EndOfParagraph) { offset = Math.Min(_dcp + this.Length, offset); } return(offset); }
// Token: 0x0600660A RID: 26122 RVA: 0x001CB238 File Offset: 0x001C9438 private int CalcPositionOffset(CharacterHit charHit) { int num = charHit.FirstCharacterIndex + charHit.TrailingLength; if (base.EndOfParagraph) { num = Math.Min(this._dcp + base.Length, num); } return(num); }
public void Should_Get_Distance_From_CharacterHit(double[] advances, ushort[] clusters, int start, int trailingLength, double expectedDistance) { using (var glyphRun = CreateGlyphRun(advances, clusters)) { var characterHit = new CharacterHit(start, trailingLength); var distance = glyphRun.GetDistanceFromCharacterHit(characterHit); Assert.Equal(expectedDistance, distance); } }
public void Should_Get_Distance_From_CharacterHit(double[] advances, int[] clusters, int start, int trailingLength, double expectedDistance) { using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface)) using (var glyphRun = CreateGlyphRun(advances, clusters)) { var characterHit = new CharacterHit(start, trailingLength); var distance = glyphRun.GetDistanceFromCharacterHit(characterHit); Assert.Equal(expectedDistance, distance); } }
static void SetCaretPosition(TextArea textArea, VisualLine targetVisualLine, TextLine targetLine, CharacterHit ch, bool allowWrapToNextLine) { int newVisualColumn = ch.FirstCharacterIndex + ch.TrailingLength; int targetLineStartCol = targetVisualLine.GetTextLineVisualStartColumn(targetLine); if (!allowWrapToNextLine && newVisualColumn >= targetLineStartCol + targetLine.Length) { newVisualColumn = targetLineStartCol + targetLine.Length - 1; } int newOffset = targetVisualLine.GetRelativeOffset(newVisualColumn) + targetVisualLine.FirstDocumentLine.Offset; SetCaretPosition(textArea, newVisualColumn, newOffset); }
/// <summary> /// Gets the visual column from a document position (relative to top left of the document). /// If the user clicks between two visual columns, rounds to the nearest column. /// </summary> 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((xPos - textLine.WidthIncludingTrailingWhitespace) / textView.WideSpaceWidth); return(VisualLengthWithEndOfLineMarker + virtualX); } } CharacterHit ch = textLine.GetCharacterHitFromDistance(xPos); return(ch.FirstCharacterIndex + ch.TrailingLength); }
/// <inheritdoc/> public override CharacterHit GetPreviousCaretCharacterHit(CharacterHit characterHit) { if (TryFindPreviousCharacterHit(characterHit, out var previousCharacterHit)) { return(previousCharacterHit); } if (characterHit.FirstCharacterIndex <= FirstTextSourceIndex) { characterHit = new CharacterHit(FirstTextSourceIndex); } return(characterHit); // Can't move, we're before the first character }
public void Should_Get_Previous_Caret_CharacterHit_Bidi() { const string text = "אבג 1 ABC"; using (Start()) { var defaultProperties = new GenericTextRunProperties(Typeface.Default); var textSource = new SingleBufferTextSource(text, defaultProperties); var formatter = new TextFormatterImpl(); var textLine = formatter.FormatLine(textSource, 0, double.PositiveInfinity, new GenericTextParagraphProperties(defaultProperties)); var clusters = new List <int>(); foreach (var textRun in textLine.TextRuns.OrderBy(x => x.Text.Start)) { var shapedRun = (ShapedTextCharacters)textRun; clusters.AddRange(shapedRun.IsReversed ? shapedRun.ShapedBuffer.GlyphClusters.Reverse() : shapedRun.ShapedBuffer.GlyphClusters); } clusters.Reverse(); var nextCharacterHit = new CharacterHit(text.Length - 1); foreach (var cluster in clusters) { var currentCaretIndex = nextCharacterHit.FirstCharacterIndex + nextCharacterHit.TrailingLength; Assert.Equal(cluster, currentCaretIndex); nextCharacterHit = textLine.GetPreviousCaretCharacterHit(nextCharacterHit); } var lastCharacterHit = nextCharacterHit; nextCharacterHit = textLine.GetPreviousCaretCharacterHit(lastCharacterHit); Assert.Equal(lastCharacterHit.FirstCharacterIndex, nextCharacterHit.FirstCharacterIndex); Assert.Equal(lastCharacterHit.TrailingLength, nextCharacterHit.TrailingLength); } }
/// <summary> /// Gets the visual column from a document position (relative to top left of the document). /// If the user clicks between two visual columns, returns the first of those columns. /// </summary> public int GetVisualColumnFloor(Point point) { TextLine textLine = GetTextLineByVisualYPosition(point.Y); if (point.X > textLine.WidthIncludingTrailingWhitespace) { // GetCharacterHitFromDistance returns a hit with FirstCharacterIndex=last character inline // and TrailingLength=1 when clicking behind the line, so the floor function needs to handle this case // specially end return the line's end column instead. return(GetTextLineVisualStartColumn(textLine) + textLine.Length); } CharacterHit ch = textLine.GetCharacterHitFromDistance(point.X); return(ch.FirstCharacterIndex); }
/// <summary> /// Tries to find the previous character hit. /// </summary> /// <param name="characterHit">The current character hit.</param> /// <param name="previousCharacterHit">The previous character hit.</param> /// <returns></returns> private bool TryFindPreviousCharacterHit(CharacterHit characterHit, out CharacterHit previousCharacterHit) { var characterIndex = characterHit.FirstCharacterIndex + characterHit.TrailingLength; if (characterIndex == TextRange.Start) { previousCharacterHit = new CharacterHit(TextRange.Start); return(true); } previousCharacterHit = characterHit; if (characterIndex < TextRange.Start) { return(false); // Cannot go backward anymore. } var runIndex = GetRunIndexAtCharacterIndex(characterIndex, LogicalDirection.Backward); while (runIndex >= 0) { var run = _textRuns[runIndex]; var foundCharacterHit = run.GlyphRun.FindNearestCharacterHit(characterHit.FirstCharacterIndex - 1, out _); if (foundCharacterHit.FirstCharacterIndex + foundCharacterHit.TrailingLength < characterIndex) { previousCharacterHit = foundCharacterHit; return(true); } previousCharacterHit = characterHit.TrailingLength != 0 ? foundCharacterHit : new CharacterHit(foundCharacterHit.FirstCharacterIndex); if (previousCharacterHit != characterHit) { return(true); } runIndex--; } return(false); }
/// <inheritdoc/> public override CharacterHit GetNextCaretCharacterHit(CharacterHit characterHit) { if (TryFindNextCharacterHit(characterHit, out var nextCharacterHit)) { return(nextCharacterHit); } // Can't move, we're after the last character var runIndex = GetRunIndexAtCharacterIndex(TextRange.End, LogicalDirection.Forward); var textRun = _textRuns[runIndex]; characterHit = textRun.GlyphRun.GetNextCaretCharacterHit(characterHit); return(characterHit); }
/// <summary> /// Tries to find the next character hit. /// </summary> /// <param name="characterHit">The current character hit.</param> /// <param name="nextCharacterHit">The next character hit.</param> /// <returns></returns> private bool TryFindNextCharacterHit(CharacterHit characterHit, out CharacterHit nextCharacterHit) { nextCharacterHit = characterHit; var codepointIndex = characterHit.FirstCharacterIndex + characterHit.TrailingLength; if (codepointIndex > TextRange.End) { return(false); // Cannot go forward anymore } var runIndex = GetRunIndexAtCodepointIndex(codepointIndex); while (runIndex < TextRuns.Count) { var run = _textRuns[runIndex]; var foundCharacterHit = run.GlyphRun.FindNearestCharacterHit(characterHit.FirstCharacterIndex + characterHit.TrailingLength, out _); var isAtEnd = foundCharacterHit.FirstCharacterIndex + foundCharacterHit.TrailingLength == TextRange.Length; var characterIndex = codepointIndex - run.Text.Start; var codepoint = Codepoint.ReadAt(run.GlyphRun.Characters, characterIndex, out _); if (codepoint.IsBreakChar) { foundCharacterHit = run.GlyphRun.FindNearestCharacterHit(codepointIndex - 1, out _); isAtEnd = true; } nextCharacterHit = isAtEnd || characterHit.TrailingLength != 0 ? foundCharacterHit : new CharacterHit(foundCharacterHit.FirstCharacterIndex + foundCharacterHit.TrailingLength); if (isAtEnd || nextCharacterHit.FirstCharacterIndex > characterHit.FirstCharacterIndex) { return(true); } runIndex++; } return(false); }
/// <summary> /// Validate the input character hit /// </summary> internal static void VerifyCaretCharacterHit( CharacterHit characterHit, int cpFirst, int cchLength ) { if (characterHit.FirstCharacterIndex < cpFirst || characterHit.FirstCharacterIndex > cpFirst + cchLength) { throw new ArgumentOutOfRangeException("cpFirst", SR.Get(SRID.ParameterMustBeBetween, cpFirst, cpFirst + cchLength)); } if (characterHit.TrailingLength < 0) { throw new ArgumentOutOfRangeException("cchLength", SR.Get(SRID.ParameterCannotBeNegative)); } }
/// <summary> /// Client to get the next character hit for caret navigation /// </summary> /// <param name="characterHit">the current character hit</param> /// <returns>the next character hit</returns> public override CharacterHit GetNextCaretCharacterHit( CharacterHit characterHit ) { TextFormatterImp.VerifyCaretCharacterHit(characterHit, _cpFirst, _cpLength); int nextVisisbleCp; bool navigableCpFound; if (characterHit.TrailingLength == 0) { navigableCpFound = FindNextVisibleCp(characterHit.FirstCharacterIndex, out nextVisisbleCp); if (navigableCpFound) { // Move from leading to trailing edge return new CharacterHit(nextVisisbleCp, 1); } } navigableCpFound = FindNextVisibleCp(characterHit.FirstCharacterIndex + 1, out nextVisisbleCp); if (navigableCpFound) { // Move from trailing edge of current character to trailing edge of next return new CharacterHit(nextVisisbleCp, 1); } // Can't move, we're after the last character return characterHit; }
/// <summary> /// Client to get the previous character hit for caret navigation /// </summary> /// <param name="characterHit">the current character hit</param> /// <returns>the previous character hit</returns> public override CharacterHit GetPreviousCaretCharacterHit( CharacterHit characterHit ) { TextFormatterImp.VerifyCaretCharacterHit(characterHit, _cpFirst, _cpLength); int previousVisisbleCp; bool navigableCpFound; int cpHit = characterHit.FirstCharacterIndex; bool trailingHit = (characterHit.TrailingLength != 0); // Input can be right after the end of the current line. Snap it to be at the end of the line. if (cpHit >= _cpFirst + _cpLength) { cpHit = _cpFirst + _cpLength - 1; trailingHit = true; } if (trailingHit) { navigableCpFound = FindPreviousVisibleCp(cpHit, out previousVisisbleCp); if (navigableCpFound) { // Move from trailing to leading edge return new CharacterHit(previousVisisbleCp, 0); } } navigableCpFound = FindPreviousVisibleCp(cpHit - 1, out previousVisisbleCp); if (navigableCpFound) { // Move from leading edge of current character to leading edge of previous return new CharacterHit(previousVisisbleCp, 0); } // Can't move, we're before the first character return characterHit; }
private ITextPointer BackspaceCaretUnitPositionFromDcpSimpleLines( int dcp, ITextPointer position, ref PTS.FSTEXTDETAILSFULL textDetails) { ErrorHandler.Assert(!PTS.ToBoolean(textDetails.fDropCapPresent), ErrorHandler.NotSupportedDropCap); // Get list of lines PTS.FSLINEDESCRIPTIONSINGLE[] arrayLineDesc; PtsHelper.LineListSimpleFromTextPara(PtsContext, _paraHandle.Value, ref textDetails, out arrayLineDesc); // Declare backspace position and set it to initial position ITextPointer backspaceCaretPosition = position; // First iterate through lines for (int index = 0; index < arrayLineDesc.Length; index++) { PTS.FSLINEDESCRIPTIONSINGLE lineDesc = arrayLineDesc[index]; // 'dcp' needs to be within line range. If position points to dcpLim, // it means that the next line starts from such position, hence go to the next line. if (((lineDesc.dcpFirst <= dcp) && (lineDesc.dcpLim > dcp)) || ((lineDesc.dcpLim == dcp) && (index == arrayLineDesc.Length - 1))) { if (dcp == lineDesc.dcpFirst) { // Go to previous line if (index == 0) { return position; } else { // Update dcp, lineDesc Debug.Assert(index > 0); --index; lineDesc = arrayLineDesc[index]; } } // Create and format line Line line = new Line(Paragraph.StructuralCache.TextFormatterHost, this, Paragraph.ParagraphStartCharacterPosition); Line.FormattingContext ctx = new Line.FormattingContext(false, PTS.ToBoolean(lineDesc.fClearOnLeft), PTS.ToBoolean(lineDesc.fClearOnRight), TextParagraph.TextRunCache); if(IsOptimalParagraph) { ctx.LineFormatLengthTarget = lineDesc.dcpLim - lineDesc.dcpFirst; } TextParagraph.FormatLineCore(line, lineDesc.pfsbreakreclineclient, ctx, lineDesc.dcpFirst, lineDesc.dur, PTS.ToBoolean(lineDesc.fTreatedAsFirst), lineDesc.dcpFirst); // Assert that number of characters in Text line is the same as our expected length Invariant.Assert(line.SafeLength == lineDesc.dcpLim - lineDesc.dcpFirst, "Line length is out of [....]"); // Create CharacterHit and get backspace index from line API CharacterHit textSourceCharacterIndex = new CharacterHit(dcp, 0); CharacterHit backspaceCharacterHit = line.GetBackspaceCaretCharacterHit(textSourceCharacterIndex); LogicalDirection logicalDirection; if (backspaceCharacterHit.FirstCharacterIndex + backspaceCharacterHit.TrailingLength == lineDesc.dcpFirst) { // Going forward brought us to the start of a line, context must be backward for previous line if (index == 0) { // First line, so we will stay forward logicalDirection = LogicalDirection.Forward; } else { logicalDirection = LogicalDirection.Backward; } } else { logicalDirection = (backspaceCharacterHit.TrailingLength > 0) ? LogicalDirection.Backward : LogicalDirection.Forward; } backspaceCaretPosition = GetTextPosition(backspaceCharacterHit.FirstCharacterIndex + backspaceCharacterHit.TrailingLength, logicalDirection); // Dispose the line line.Dispose(); break; } } Debug.Assert(backspaceCaretPosition != null); return backspaceCaretPosition; }
private bool IsAtCaretUnitBoundaryFromDcpSimpleLines( int dcp, ITextPointer position, ref PTS.FSTEXTDETAILSFULL textDetails) { ErrorHandler.Assert(!PTS.ToBoolean(textDetails.fDropCapPresent), ErrorHandler.NotSupportedDropCap); // Get list of lines PTS.FSLINEDESCRIPTIONSINGLE[] arrayLineDesc; PtsHelper.LineListSimpleFromTextPara(PtsContext, _paraHandle.Value, ref textDetails, out arrayLineDesc); bool isAtCaretUnitBoundary = false; // First iterate through lines for (int index = 0; index < arrayLineDesc.Length; index++) { PTS.FSLINEDESCRIPTIONSINGLE lineDesc = arrayLineDesc[index]; // 'dcp' needs to be within line range. If position points to dcpLim, // it means that the next line starts from such position, hence go to the next line. if (((lineDesc.dcpFirst <= dcp) && (lineDesc.dcpLim > dcp)) || ((lineDesc.dcpLim == dcp) && (index == arrayLineDesc.Length - 1))) { CharacterHit charHit = new CharacterHit(); if (dcp >= lineDesc.dcpLim - 1 && index == arrayLineDesc.Length - 1) { // Special case: last line has additional character to mark the end of paragraph. // We should not try and check for next source character index // But just return true in this case return true; } if (position.LogicalDirection == LogicalDirection.Backward) { if (lineDesc.dcpFirst == dcp) { if (index == 0) { // First position of first line does not have a trailing edge. Return false. return false; } else { // Get the trailing edge of the last character on the previous line, at dcp - 1 index--; lineDesc = arrayLineDesc[index]; Invariant.Assert(dcp > 0); charHit = new CharacterHit(dcp - 1, 1); } } else { // Get CharacterHit at trailing edge of previous position Invariant.Assert(dcp > 0); charHit = new CharacterHit(dcp - 1, 1); } } else if (position.LogicalDirection == LogicalDirection.Forward) { // Get character hit at leading edge charHit = new CharacterHit(dcp, 0); } // Create and format line Line line = new Line(Paragraph.StructuralCache.TextFormatterHost, this, Paragraph.ParagraphStartCharacterPosition); Line.FormattingContext ctx = new Line.FormattingContext(false, PTS.ToBoolean(lineDesc.fClearOnLeft), PTS.ToBoolean(lineDesc.fClearOnRight), TextParagraph.TextRunCache); if(IsOptimalParagraph) { ctx.LineFormatLengthTarget = lineDesc.dcpLim - lineDesc.dcpFirst; } TextParagraph.FormatLineCore(line, lineDesc.pfsbreakreclineclient, ctx, lineDesc.dcpFirst, lineDesc.dur, PTS.ToBoolean(lineDesc.fTreatedAsFirst), lineDesc.dcpFirst); // Assert that number of characters in Text line is the same as our expected length Invariant.Assert(line.SafeLength == lineDesc.dcpLim - lineDesc.dcpFirst, "Line length is out of [....]"); isAtCaretUnitBoundary = line.IsAtCaretCharacterHit(charHit); // Dispose the line line.Dispose(); break; } } return isAtCaretUnitBoundary; }
/// <summary> /// Determines if the given position is at the edge of a caret unit /// in the specified direction, and returns true if it is and false otherwise. /// Used by the ITextView.IsCaretAtUnitBoundary(ITextPointer position) in /// TextParagraphView /// </summary> /// <param name="position"> /// Position to test. /// </param> /// <param name="dcp"> /// Offset of the current position from start of TextContainer /// </param> /// <param name="lineIndex"> /// Index of line in which position is found /// </param> internal bool IsAtCaretUnitBoundary(ITextPointer position, int dcp, int lineIndex) { Invariant.Assert(IsLayoutDataValid); // Line props may be invalid, even if Measure/Arrange is valid - rendering only props are changing. LineProperties lineProperties = GetLineProperties(); EnsureComplexContent(); TextRunCache textRunCache = new TextRunCache(); bool isAtCaretUnitBoundary = false; int characterIndex = _complexContent.TextContainer.Start.GetOffsetToPosition(position); CharacterHit charHit = new CharacterHit(); if (position.LogicalDirection == LogicalDirection.Backward) { if (characterIndex > dcp) { // Go to trailing edge of previous character charHit = new CharacterHit(characterIndex - 1, 1); } else { // We should not be at line's start dcp with backward context, except in case this is the first line. This is not // a unit boundary return false; } } else if (position.LogicalDirection == LogicalDirection.Forward) { // Get leading edge of this character index charHit = new CharacterHit(characterIndex, 0); } LineMetrics lineMetrics = GetLine(lineIndex); double wrappingWidth = CalcWrappingWidth(RenderSize.Width); using(Line line = CreateLine(lineProperties)) { // Format line. Set showParagraphEllipsis flag to false since we are not using information about // ellipsis to change line offsets in this case. line.Format(dcp, wrappingWidth, GetLineProperties(lineIndex == 0, lineProperties), lineMetrics.TextLineBreak, textRunCache, false); // Check consistency of line formatting MS.Internal.Invariant.Assert(lineMetrics.Length == line.Length, "Line length is out of [....]"); isAtCaretUnitBoundary = line.IsAtCaretCharacterHit(charHit); } return isAtCaretUnitBoundary; }
//------------------------------------------------------------------- // Retrieve text position for backspace caret position // // index: CharacterHit for current position // // Returns: Text position index. //------------------------------------------------------------------- internal CharacterHit GetBackspaceCaretCharacterHit(CharacterHit index) { return _line.GetBackspaceCaretCharacterHit(index); }
/// <summary> /// Client to get the previous character hit for caret navigation /// </summary> /// <param name="characterHit">the current character hit</param> /// <returns>the previous character hit</returns> public abstract CharacterHit GetPreviousCaretCharacterHit( CharacterHit characterHit );
/// <summary> /// Client to get the previous character hit after backspacing /// </summary> /// <param name="characterHit">the current character hit</param> /// <returns>the character hit after backspacing</returns> public abstract CharacterHit GetBackspaceCaretCharacterHit( CharacterHit characterHit );
static void SetCaretPosition(TextArea textArea, VisualLine targetVisualLine, TextLine targetLine, CharacterHit ch, bool allowWrapToNextLine) { int newVisualColumn = ch.FirstCharacterIndex + ch.TrailingLength; int targetLineStartCol = targetVisualLine.GetTextLineVisualStartColumn(targetLine); if (!allowWrapToNextLine && newVisualColumn >= targetLineStartCol + targetLine.Length) newVisualColumn = targetLineStartCol + targetLine.Length - 1; int newOffset = targetVisualLine.GetRelativeOffset(newVisualColumn) + targetVisualLine.FirstDocumentLine.Offset; SetCaretPosition(textArea, newVisualColumn, newOffset); }
internal bool IsAtCaretCharacterHit(CharacterHit characterHit, int cpFirst) { // TrailingLength is used as a flag to indicate whether the character // hit is on the leading or trailing edge of the character. if (characterHit.TrailingLength == 0) { CharacterHit nextHit = GetNextCaretCharacterHit(characterHit); if (nextHit == characterHit) { // At this point we only know that no caret stop is available // after the input, we caliberate the input to the end of the line. nextHit = new CharacterHit(cpFirst + Length - 1, 1); } CharacterHit previousHit = GetPreviousCaretCharacterHit(nextHit); return previousHit == characterHit; } else { CharacterHit previousHit = GetPreviousCaretCharacterHit(characterHit); CharacterHit nextHit = GetNextCaretCharacterHit(previousHit); return nextHit == characterHit; } }
/// <summary> /// Client to get the distance from the beginning of the line from the specified /// character hit. /// </summary> /// <param name="characterHit">character hit of the character to query the distance.</param> /// <returns>distance in text flow direction from the beginning of the line.</returns> public abstract double GetDistanceFromCharacterHit( CharacterHit characterHit );
/// <summary> /// Client to get the next character hit for caret navigation /// </summary> /// <param name="characterHit">the current character hit</param> /// <returns>the next character hit</returns> public abstract CharacterHit GetNextCaretCharacterHit( CharacterHit characterHit );
/// <summary> /// <see cref="ITextView.IsAtCaretUnitBoundary"/> /// </summary> bool ITextView.IsAtCaretUnitBoundary(ITextPointer position) { Invariant.Assert(this.IsLayoutValid); Invariant.Assert(Contains(position)); bool boundary = false; int lineIndex = GetLineIndexFromPosition(position); CharacterHit sourceCharacterHit = new CharacterHit(); if (position.LogicalDirection == LogicalDirection.Forward) { // Forward context, go to leading edge of position offset sourceCharacterHit = new CharacterHit(position.Offset, 0); } else if (position.LogicalDirection == LogicalDirection.Backward) { if (position.Offset > _lineMetrics[lineIndex].Offset) { // For backward context, go to trailing edge of previous character sourceCharacterHit = new CharacterHit(position.Offset - 1, 1); } else { // There is no previous trailing edge on this line. We don't consider this a unit boundary. return false; } } using (TextBoxLine line = GetFormattedLine(lineIndex)) { boundary = line.IsAtCaretCharacterHit(sourceCharacterHit); } return boundary; }
/// <summary> /// <see cref="ITextView.GetNextCaretUnitPosition"/> /// </summary> ITextPointer ITextView.GetNextCaretUnitPosition(ITextPointer position, LogicalDirection direction) { Invariant.Assert(this.IsLayoutValid); Invariant.Assert(Contains(position)); // Special case document start/end. if (position.Offset == 0 && direction == LogicalDirection.Backward) { return position.GetFrozenPointer(LogicalDirection.Forward); } else if (position.Offset == _host.TextContainer.SymbolCount && direction == LogicalDirection.Forward) { return position.GetFrozenPointer(LogicalDirection.Backward); } int lineIndex = GetLineIndexFromPosition(position); CharacterHit sourceCharacterHit = new CharacterHit(position.Offset, 0); CharacterHit nextCharacterHit; using (TextBoxLine line = GetFormattedLine(lineIndex)) { if (direction == LogicalDirection.Forward) { // Get the next caret position from the line nextCharacterHit = line.GetNextCaretCharacterHit(sourceCharacterHit); } else { // Get previous caret position from the line nextCharacterHit = line.GetPreviousCaretCharacterHit(sourceCharacterHit); } } // Determine logical direction for next caret index and create TextPointer from it. LogicalDirection logicalDirection; if (nextCharacterHit.FirstCharacterIndex + nextCharacterHit.TrailingLength == _lineMetrics[lineIndex].EndOffset && direction == LogicalDirection.Forward) { // Going forward brought us to the end of a line, context must be forward for next line. if (lineIndex == _lineMetrics.Count - 1) { // Last line so context must stay backward. logicalDirection = LogicalDirection.Backward; } else { logicalDirection = LogicalDirection.Forward; } } else if (nextCharacterHit.FirstCharacterIndex + nextCharacterHit.TrailingLength == _lineMetrics[lineIndex].Offset && direction == LogicalDirection.Backward) { // Going backward brought us to the start of a line, context must be backward for previous line. if (lineIndex == 0) { // First line, so we will stay forward. logicalDirection = LogicalDirection.Forward; } else { logicalDirection = LogicalDirection.Backward; } } else { logicalDirection = (nextCharacterHit.TrailingLength > 0) ? LogicalDirection.Backward : LogicalDirection.Forward; } ITextPointer nextCaretUnitPosition = _host.TextContainer.CreatePointerAtOffset(nextCharacterHit.FirstCharacterIndex + nextCharacterHit.TrailingLength, logicalDirection); nextCaretUnitPosition.Freeze(); return nextCaretUnitPosition; }
//------------------------------------------------------------------- // Retrieve text position for previous caret position // // index: CharacterHit for current position // // Returns: Text position index. //------------------------------------------------------------------- internal CharacterHit GetPreviousCaretCharacterHit(CharacterHit index) { return _line.GetPreviousCaretCharacterHit(index); }
/// <summary> /// Client to get the distance from the beginning of the line from the specified character hit. /// </summary> /// <param name="characterHit">index to text source's character store</param> /// <returns>distance in text flow direction from the beginning of the line.</returns> public override double GetDistanceFromCharacterHit( CharacterHit characterHit ) { if ((_statusFlags & StatusFlags.IsDisposed) != 0) { throw new ObjectDisposedException(SR.Get(SRID.TextLineHasBeenDisposed)); } TextFormatterImp.VerifyCaretCharacterHit(characterHit, _cpFirst, _metrics._cchLength); return _metrics._formatter.IdealToReal(LSLineUToParagraphU(DistanceFromCharacterHit(characterHit))); }
/// <summary> /// Returns true of char hit is at caret unit boundary. /// </summary> /// <param name="charHit"> /// CharacterHit to be tested. /// </param> internal bool IsAtCaretCharacterHit(CharacterHit charHit) { return _line.IsAtCaretCharacterHit(charHit, _dcp); }
/// <summary> /// Get hittest distance relative to line start from specified character hit /// </summary> private int DistanceFromCharacterHit(CharacterHit characterHit) { int hitTestDistance = 0; if (_ploline.Value == IntPtr.Zero) { // Returning start of the line for empty line return hitTestDistance; } if (characterHit.FirstCharacterIndex >= _cpFirst + _metrics._cchLength) { // Returning line width for character hit beyond the last caret stop return _metrics._textStart + _metrics._textWidthAtTrailing; } if ( HasCollapsed && _collapsedRange != null && characterHit.FirstCharacterIndex >= _collapsedRange.TextSourceCharacterIndex ) { // The current character hit is beyond the beginning of the collapsed range int lineEndDistance = _metrics._textStart + _metrics._textWidthAtTrailing; if ( characterHit.FirstCharacterIndex >= _collapsedRange.TextSourceCharacterIndex + _collapsedRange.Length || characterHit.TrailingLength != 0 || _collapsingSymbol == null ) { // The current character hit either hits outside, // or it's at the trailing edge of the collapsed range return lineEndDistance; } return lineEndDistance - TextFormatterImp.RealToIdeal(_collapsingSymbol.Width); } int actualSublineCount; LsTextCell lsTextCell; LsQSubInfo[] sublineInfo = new LsQSubInfo[_depthQueryMax]; int lscpCurrent = GetInternalCp(characterHit.FirstCharacterIndex); QueryLineCpPpoint( lscpCurrent, sublineInfo, out actualSublineCount, out lsTextCell ); if (actualSublineCount > 0) { return lsTextCell.pointUvStartCell.x + GetDistanceInsideTextCell( lscpCurrent, characterHit.TrailingLength != 0, sublineInfo, actualSublineCount, ref lsTextCell ); } return hitTestDistance; }
/// <summary> /// Finds and returns the position after backspace at the edge of a caret unit in /// specified direction. /// </summary> /// <param name="position"> /// Initial text position of an object/character. /// </param> /// <param name="dcp"> /// Offset of the current position from start of TextContainer /// </param> /// <param name="lineIndex"> /// Index of line in which position is found /// </param> internal ITextPointer GetBackspaceCaretUnitPosition(ITextPointer position, int dcp, int lineIndex) { Invariant.Assert(IsLayoutDataValid); // Line props may be invalid, even if Measure/Arrange is valid - rendering only props are changing. LineProperties lineProperties = GetLineProperties(); EnsureComplexContent(); // Get character index for position int characterIndex = _complexContent.TextContainer.Start.GetOffsetToPosition(position); // Process special cases if (characterIndex == dcp) { if (lineIndex == 0) { // Cannot go back any further return position; } else { // Change lineIndex and dcp to previous line Debug.Assert(lineIndex > 0); --lineIndex; dcp -= GetLine(lineIndex).Length; Debug.Assert(dcp >= 0); } } double wrappingWidth = CalcWrappingWidth(RenderSize.Width); CharacterHit textSourceCharacterIndex = new CharacterHit(characterIndex, 0); CharacterHit backspaceCharacterHit; LineMetrics lineMetrics = GetLine(lineIndex); TextRunCache textRunCache = new TextRunCache(); // Create and Format line using(Line line = CreateLine(lineProperties)) { // Format line. Set showParagraphEllipsis flag to false since we are not using information about // ellipsis to change line offsets in this case. line.Format(dcp, wrappingWidth, GetLineProperties(lineIndex == 0, lineProperties), lineMetrics.TextLineBreak, textRunCache, false); // Check consistency of line formatting MS.Internal.Invariant.Assert(lineMetrics.Length == line.Length, "Line length is out of [....]"); backspaceCharacterHit = line.GetBackspaceCaretCharacterHit(textSourceCharacterIndex); } // Get CharacterHit and call line API // Determine logical direction for next caret index and create TextPointer from it LogicalDirection logicalDirection; if (backspaceCharacterHit.FirstCharacterIndex + backspaceCharacterHit.TrailingLength == dcp) { // Going forward brought us to the start of a line, context must be backward for previous line if (dcp == 0) { // First line, so we will stay forward logicalDirection = LogicalDirection.Forward; } else { logicalDirection = LogicalDirection.Backward; } } else { logicalDirection = (backspaceCharacterHit.TrailingLength > 0) ? LogicalDirection.Backward : LogicalDirection.Forward; } ITextPointer backspaceCaretPosition = _complexContent.TextContainer.Start.CreatePointer(backspaceCharacterHit.FirstCharacterIndex + backspaceCharacterHit.TrailingLength, logicalDirection); // Return backspaceCaretPosition return backspaceCaretPosition; }
/// <summary> /// Client to get the next character hit for caret navigation /// </summary> /// <param name="characterHit">the current character hit</param> /// <returns>the next character hit</returns> public override CharacterHit GetNextCaretCharacterHit( CharacterHit characterHit ) { if ((_statusFlags & StatusFlags.IsDisposed) != 0) { throw new ObjectDisposedException(SR.Get(SRID.TextLineHasBeenDisposed)); } TextFormatterImp.VerifyCaretCharacterHit(characterHit, _cpFirst, _metrics._cchLength); if (_ploline.Value == System.IntPtr.Zero) { return characterHit; } int caretStopIndex; int offsetToNextCaretStopIndex; bool found = GetNextOrPreviousCaretStop( characterHit.FirstCharacterIndex, CaretDirection.Forward, out caretStopIndex, out offsetToNextCaretStopIndex ); if (!found) { // The current index is beyond the last caret stop. return characterHit; } if (caretStopIndex <= characterHit.FirstCharacterIndex && characterHit.TrailingLength != 0) { // We treat trailing length of the current character hit as a flag on the way in. // A non-zero value indicates that it is on the trailing edge of the current // caret stop. At this point, the current caret stop fully encloses the input index, // and the input is at the trailing edge. In this case, we move it to the trailing // edge of the next caret stop. found = GetNextOrPreviousCaretStop( caretStopIndex + offsetToNextCaretStopIndex, CaretDirection.Forward, out caretStopIndex, out offsetToNextCaretStopIndex ); if (!found) { // This current index is beyond the last caret stop return characterHit; } return new CharacterHit(caretStopIndex, offsetToNextCaretStopIndex); } // If the current character hit is at the leading edge, // move it to trailing edge of the current caret stop. return new CharacterHit(caretStopIndex, offsetToNextCaretStopIndex); }
private bool IsAtCaretUnitBoundaryFromDcpCompositeLines( int dcp, ITextPointer position, ref PTS.FSTEXTDETAILSFULL textDetails) { ErrorHandler.Assert(!PTS.ToBoolean(textDetails.fDropCapPresent), ErrorHandler.NotSupportedDropCap); // Get list of lines PTS.FSLINEDESCRIPTIONCOMPOSITE[] arrayLineDesc; PtsHelper.LineListCompositeFromTextPara(PtsContext, _paraHandle.Value, ref textDetails, out arrayLineDesc); bool isAtCaretUnitBoundary = false; // First iterate through lines for (int index = 0; index < arrayLineDesc.Length; index++) { PTS.FSLINEDESCRIPTIONCOMPOSITE lineDesc = arrayLineDesc[index]; if (lineDesc.cElements == 0) { continue; } // Get list of line elements. PTS.FSLINEELEMENT[] arrayLineElement; PtsHelper.LineElementListFromCompositeLine(PtsContext, ref lineDesc, out arrayLineElement); for (int elIndex = 0; elIndex < arrayLineElement.Length; elIndex++) { PTS.FSLINEELEMENT element = arrayLineElement[elIndex]; // 'dcp' needs to be within line range. If position points to dcpLim, // it means that the next line starts from such position, hence go to the next line. if (((element.dcpFirst <= dcp) && (element.dcpLim > dcp)) || ((element.dcpLim == dcp) && (elIndex == arrayLineElement.Length - 1) && (index == arrayLineDesc.Length - 1))) { CharacterHit charHit = new CharacterHit(); if (dcp >= element.dcpLim - 1 && elIndex == arrayLineElement.Length - 1 && index == arrayLineDesc.Length - 1) { // Special case: at the end of the last line there is a special character that // does not belong to the line. Return true for this case return true; } if (position.LogicalDirection == LogicalDirection.Backward) { // Beginning of element. if (dcp == element.dcpFirst) { if (elIndex > 0) { // Beginning of element, but not of line. Create char hit at last dcp of previous element, trailing edge. --elIndex; element = arrayLineElement[elIndex]; charHit = new CharacterHit(dcp - 1, 1); } else { // Beginning of line if (index == 0) { // Backward context at start position of first line is not considered a unit boundary return false; } else { // Go to previous line --index; lineDesc = arrayLineDesc[index]; if (lineDesc.cElements == 0) { return false; } else { // Get list of line elements. PtsHelper.LineElementListFromCompositeLine(PtsContext, ref lineDesc, out arrayLineElement); element = arrayLineElement[arrayLineElement.Length - 1]; charHit = new CharacterHit(dcp - 1, 1); } } } } else { // Get trailing edge of previous dcp Invariant.Assert(dcp > 0); charHit = new CharacterHit(dcp - 1, 1); } } else if (position.LogicalDirection == LogicalDirection.Forward) { // Create character hit at leading edge charHit = new CharacterHit(dcp, 0); } // Create and format line Line line = new Line(Paragraph.StructuralCache.TextFormatterHost, this, Paragraph.ParagraphStartCharacterPosition); Line.FormattingContext ctx = new Line.FormattingContext(false, PTS.ToBoolean(element.fClearOnLeft), PTS.ToBoolean(element.fClearOnRight), TextParagraph.TextRunCache); if(IsOptimalParagraph) { ctx.LineFormatLengthTarget = element.dcpLim - element.dcpFirst; } TextParagraph.FormatLineCore(line, element.pfsbreakreclineclient, ctx, element.dcpFirst, element.dur, PTS.ToBoolean(lineDesc.fTreatedAsFirst), element.dcpFirst); // Assert that number of characters in Text line is the same as our expected length Invariant.Assert(line.SafeLength == element.dcpLim - element.dcpFirst, "Line length is out of [....]"); isAtCaretUnitBoundary = line.IsAtCaretCharacterHit(charHit); // Dispose the line line.Dispose(); return isAtCaretUnitBoundary; } } } return isAtCaretUnitBoundary; }
/// <summary> /// Client to get the previous character hit after backspacing /// </summary> /// <param name="characterHit">the current character hit</param> /// <returns>the character hit after backspacing</returns> public override CharacterHit GetBackspaceCaretCharacterHit( CharacterHit characterHit ) { return GetPreviousCaretCharacterHitByBehavior(characterHit, CaretDirection.Backspace); }
private ITextPointer BackspaceCaretUnitPositionFromDcpCompositeLines( int dcp, ITextPointer position, ref PTS.FSTEXTDETAILSFULL textDetails) { ErrorHandler.Assert(!PTS.ToBoolean(textDetails.fDropCapPresent), ErrorHandler.NotSupportedDropCap); // Get list of lines PTS.FSLINEDESCRIPTIONCOMPOSITE[] arrayLineDesc; PtsHelper.LineListCompositeFromTextPara(PtsContext, _paraHandle.Value, ref textDetails, out arrayLineDesc); // Declare backspace position and set it to initial position ITextPointer backspaceCaretPosition = position; // First iterate through lines for (int index = 0; index < arrayLineDesc.Length; index++) { PTS.FSLINEDESCRIPTIONCOMPOSITE lineDesc = arrayLineDesc[index]; if (lineDesc.cElements == 0) { continue; } // Get list of line elements. PTS.FSLINEELEMENT[] arrayLineElement; PtsHelper.LineElementListFromCompositeLine(PtsContext, ref lineDesc, out arrayLineElement); for (int elIndex = 0; elIndex < arrayLineElement.Length; elIndex++) { PTS.FSLINEELEMENT element = arrayLineElement[elIndex]; // 'dcp' needs to be within line range. If position points to dcpLim, // it means that the next line starts from such position, hence go to the next line. if (((element.dcpFirst <= dcp) && (element.dcpLim > dcp)) || ((element.dcpLim == dcp) && (elIndex == arrayLineElement.Length - 1) && (index == arrayLineDesc.Length - 1))) { if (dcp == element.dcpFirst) { // Beginning of element. if (dcp == 0) { // Assert that this is first elment on first line Debug.Assert(index == 0); Debug.Assert(elIndex == 0); return position; } else { if (elIndex > 0) { // Beginning of element, but not of line --elIndex; element = arrayLineElement[elIndex]; } else { // There must be at least one line above this Debug.Assert(index > 0); // Go to previous line --index; lineDesc = arrayLineDesc[index]; if (lineDesc.cElements == 0) { // Stay in same position return position; } else { // Get list of line elements. PtsHelper.LineElementListFromCompositeLine(PtsContext, ref lineDesc, out arrayLineElement); element = arrayLineElement[arrayLineElement.Length - 1]; } } } } // Create and format line Line line = new Line(Paragraph.StructuralCache.TextFormatterHost, this, Paragraph.ParagraphStartCharacterPosition); Line.FormattingContext ctx = new Line.FormattingContext(false, PTS.ToBoolean(element.fClearOnLeft), PTS.ToBoolean(element.fClearOnRight), TextParagraph.TextRunCache); if(IsOptimalParagraph) { ctx.LineFormatLengthTarget = element.dcpLim - element.dcpFirst; } TextParagraph.FormatLineCore(line, element.pfsbreakreclineclient, ctx, element.dcpFirst, element.dur, PTS.ToBoolean(lineDesc.fTreatedAsFirst), element.dcpFirst); // Assert that number of characters in Text line is the same as our expected length Invariant.Assert(line.SafeLength == element.dcpLim - element.dcpFirst, "Line length is out of [....]"); // Create CharacterHit from dcp, and get backspace CharacterHit charHit = new CharacterHit(dcp, 0); CharacterHit backspaceCharacterHit = line.GetBackspaceCaretCharacterHit(charHit); LogicalDirection logicalDirection; if (backspaceCharacterHit.FirstCharacterIndex + backspaceCharacterHit.TrailingLength == element.dcpFirst) { // Going forward brought us to the start of a line, context must be backward for previous line if (index == 0) { // First line, so we will stay forward logicalDirection = LogicalDirection.Forward; } else { logicalDirection = LogicalDirection.Backward; } } else { logicalDirection = (backspaceCharacterHit.TrailingLength > 0) ? LogicalDirection.Backward : LogicalDirection.Forward; } backspaceCaretPosition = GetTextPosition(backspaceCharacterHit.FirstCharacterIndex + backspaceCharacterHit.TrailingLength, logicalDirection); // Dispose the line line.Dispose(); return backspaceCaretPosition; } } } return backspaceCaretPosition; }
/// <summary> /// Calculate previous caret character hit based on the caret action /// </summary> private CharacterHit GetPreviousCaretCharacterHitByBehavior( CharacterHit characterHit, CaretDirection direction ) { Debug.Assert(direction == CaretDirection.Backward || direction == CaretDirection.Backspace); if ((_statusFlags & StatusFlags.IsDisposed) != 0) { throw new ObjectDisposedException(SR.Get(SRID.TextLineHasBeenDisposed)); } TextFormatterImp.VerifyCaretCharacterHit(characterHit, _cpFirst, _metrics._cchLength); if (_ploline.Value == IntPtr.Zero) { return characterHit; } if ( characterHit.FirstCharacterIndex == _cpFirst && characterHit.TrailingLength == 0) { // We are already at the beginning of the line return characterHit; } int caretStopIndex; int offsetToNextCaretStopIndex; bool found = GetNextOrPreviousCaretStop( characterHit.FirstCharacterIndex, direction, out caretStopIndex, out offsetToNextCaretStopIndex ); if (!found) { // The current index is before the 1st caret stop. return characterHit; } if ( offsetToNextCaretStopIndex != 0 && characterHit.TrailingLength == 0 && caretStopIndex != _cpFirst && caretStopIndex >= characterHit.FirstCharacterIndex ) { // If the current character hit is at the leading edge and it is not at the first caret stop, // move it to leading edge of the previous caret stop. At this point, the current character stop // fully encloses the input index and the input is at the leading edge. found = GetNextOrPreviousCaretStop( caretStopIndex - 1, // position at the character immediately preceding the current caret stop direction, out caretStopIndex, out offsetToNextCaretStopIndex ); if (!found) { // The current index is before the 1st caret stop. return characterHit; } } // The current chracter hit is either beyond the last caret stop, // or it's at the trailing edge of the current caret stop, // or the current index is at the leading edge of the first caret stop. // // In such cases, move to the leading edge of the closest caret stop. return new CharacterHit(caretStopIndex, 0); }
/// <summary> /// Get character hit from specified hittest distance relative to line start /// </summary> private CharacterHit CharacterHitFromDistance(int hitTestDistance) { // assuming the first cp of the line CharacterHit characterHit = new CharacterHit(_cpFirst, 0); if(_ploline.Value == IntPtr.Zero) { // Returning the first cp for the empty line return characterHit; } if ( HasCollapsed && _collapsedRange != null && _collapsingSymbol != null ) { int lineEndDistance = _metrics._textStart + _metrics._textWidthAtTrailing; int rangeWidth = TextFormatterImp.RealToIdeal(_collapsingSymbol.Width); if (hitTestDistance >= lineEndDistance - rangeWidth) { if (lineEndDistance - hitTestDistance < rangeWidth / 2) { // The hit-test distance is within the trailing edge of the collapsed range, // return the character hit at the beginning of the range. return new CharacterHit(_collapsedRange.TextSourceCharacterIndex, _collapsedRange.Length); } // The hit-test distance is within the leading edge of the collapsed range, // return the character hit at the beginning of the range. return new CharacterHit(_collapsedRange.TextSourceCharacterIndex, 0); } } LsTextCell lsTextCell; LsQSubInfo[] sublineInfo = new LsQSubInfo[_depthQueryMax]; int actualSublineCount; QueryLinePointPcp( new Point(hitTestDistance, 0), sublineInfo, out actualSublineCount, out lsTextCell ); if (actualSublineCount > 0 && lsTextCell.dupCell > 0) { // the last subline contains the run that owns the querying lscp LSRun lsrun = GetRun((Plsrun)sublineInfo[actualSublineCount - 1].plsrun); // Assuming caret stops at every codepoint. // // LsTextCell.lscpEndCell is the index to the last lscp still in the cell. // The number of LSCP within the text cell is equal to the number of CP. int caretStopCount = lsTextCell.lscpEndCell + 1 - lsTextCell.lscpStartCell; int codepointsToNextCaretStop = lsrun.IsHitTestable ? 1 : lsrun.Length; if ( lsrun.IsHitTestable && ( lsrun.HasExtendedCharacter || lsrun.NeedsCaretInfo) ) { // A hit-testable run with caret stops at every cluster boundaries, // e.g. run with combining mark, with extended characters or complex scripts such as Thai codepointsToNextCaretStop = caretStopCount; caretStopCount = 1; } // All the UV coordinate in subline are in main direction. If the last subline where // we hittest runs in the opposite direction, the logical advance from text cell start cp // will be negative value. int direction = (sublineInfo[actualSublineCount - 1].lstflowSubLine == sublineInfo[0].lstflowSubLine) ? 1 : -1; hitTestDistance = (hitTestDistance - lsTextCell.pointUvStartCell.x) * direction; Invariant.Assert(caretStopCount > 0); int wholeAdvance = lsTextCell.dupCell / caretStopCount; int remainingAdvance = lsTextCell.dupCell % caretStopCount; for (int i = 0; i < caretStopCount; i++) { int caretAdvance = wholeAdvance; if (remainingAdvance > 0) { caretAdvance++; remainingAdvance--; } if (hitTestDistance <= caretAdvance) { if (hitTestDistance > caretAdvance / 2) { // hittest at the trailing edge of the current caret stop return new CharacterHit(GetExternalCp(lsTextCell.lscpStartCell) + i, codepointsToNextCaretStop); } // hittest at the leading edge of the current caret stop return new CharacterHit(GetExternalCp(lsTextCell.lscpStartCell) + i, 0); } hitTestDistance -= caretAdvance; } // hittest beyond the last caret stop, return the trailing edge of the last caret stop return new CharacterHit(GetExternalCp(lsTextCell.lscpStartCell) + caretStopCount - 1, codepointsToNextCaretStop); } return characterHit; }
/// <summary> /// Client to get the previous character hit after backspacing /// </summary> /// <param name="characterHit">the current character hit</param> /// <returns>the character hit after backspacing</returns> public override CharacterHit GetBackspaceCaretCharacterHit( CharacterHit characterHit ) { // same operation as move-to-previous return GetPreviousCaretCharacterHit(characterHit); }
/// <summary> /// Client to get the distance from the beginning of the line from the specified /// character hit. /// </summary> /// <param name="characterHit">character hit of the character to query the distance.</param> /// <returns>distance in text flow direction from the beginning of the line.</returns> public override double GetDistanceFromCharacterHit( CharacterHit characterHit ) { TextFormatterImp.VerifyCaretCharacterHit(characterHit, _cpFirst, _cpLength); return DistanceFromCp(characterHit.FirstCharacterIndex + (characterHit.TrailingLength != 0 ? 1 : 0)); }
//------------------------------------------------------------------- // Retrieve text position for next caret position // // index: CharacterHit for current position // // Returns: Text position index. //------------------------------------------------------------------- internal CharacterHit GetNextCaretCharacterHit(CharacterHit index) { return _line.GetNextCaretCharacterHit(index); }
/// <summary> /// Calculates the offset for the corresponding TextPointer from a CharacterHit /// </summary> /// <remarks> /// This is necessary to ensure that we don't try to create a position at an offset greater than TextContainer's symbol count. /// This may happen when a line is collapsed with ellipsis and we hit-test at the trailing edge of ellipsis, the trailing length /// returned for the CharacterHit is the length of all collapsed characters, including the synthetic EOP. If we try to /// create a position at this trailing length we can exceed TextContainer's symbol count. /// </remarks> private int CalcPositionOffset(CharacterHit charHit) { int offset = charHit.FirstCharacterIndex + charHit.TrailingLength; if (this.EndOfParagraph) { offset = Math.Min(_dcp + this.Length, offset); } return offset; }
/// <summary> /// <see cref="ITextView.GetBackspaceCaretUnitPosition"/> /// </summary> ITextPointer ITextView.GetBackspaceCaretUnitPosition(ITextPointer position) { Invariant.Assert(this.IsLayoutValid); Invariant.Assert(Contains(position)); // Special case document start. if (position.Offset == 0) { return position.GetFrozenPointer(LogicalDirection.Forward); } int lineIndex = GetLineIndexFromPosition(position, LogicalDirection.Backward); CharacterHit sourceCharacterHit = new CharacterHit(position.Offset, 0); CharacterHit backspaceCharacterHit; using (TextBoxLine line = GetFormattedLine(lineIndex)) { backspaceCharacterHit = line.GetBackspaceCaretCharacterHit(sourceCharacterHit); } LogicalDirection logicalDirection; if (backspaceCharacterHit.FirstCharacterIndex + backspaceCharacterHit.TrailingLength == _lineMetrics[lineIndex].Offset) { // Going backward brought us to the start of a line, context must be backward for previous line if (lineIndex == 0) { // First line, so we will stay forward. logicalDirection = LogicalDirection.Forward; } else { logicalDirection = LogicalDirection.Backward; } } else { logicalDirection = (backspaceCharacterHit.TrailingLength > 0) ? LogicalDirection.Backward : LogicalDirection.Forward; } ITextPointer backspaceUnitPosition = _host.TextContainer.CreatePointerAtOffset(backspaceCharacterHit.FirstCharacterIndex + backspaceCharacterHit.TrailingLength, logicalDirection); backspaceUnitPosition.Freeze(); return backspaceUnitPosition; }