/// <summary> /// Ensure TopLine, caret position, etc in visible area /// </summary> public void Validate() { int numVisibleLines = GetVisibleLines(); // ensure the line where caret is placed is visible var lineMetrics = TextLayout.GetLineMetrics(); int linePosition; int line; GetLineFromPosition(lineMetrics, m_caretPosition, out line, out linePosition); if (line > TopLine + numVisibleLines) { int lines = line - TopLine - numVisibleLines + 1; TopLine = TopLine + lines; } // update Topline: because topline + visiblelines <= lineCount, so toplineMax= lineCount-visiblelines int maxTopLine = TextLayout.LineCount - numVisibleLines; if (TopLine > maxTopLine) { TopLine = maxTopLine; } //Trace.TraceInformation("Validate topline: {0} current line: {1}, caret {2} ", TopLine, line, m_caretPosition); }
public double[] GetLineHeights(FormattedText text, Size constraint) { using (TextLayout layout = GetTextLayout(this.factory, text, constraint)) { return(layout.GetLineMetrics().Select(x => (double)x.Height).ToArray()); } }
/// <summary> /// Get i-th line y-offset</summary> /// <param name="line">Line index</param> /// <returns>Line y-offset for given line index</returns> public float GetLineYOffset(int line) { if (line > TextLayout.LineCount - 1) { return(0.0f); } float totalHeight = 0; var lineMetrics = TextLayout.GetLineMetrics(); for (int i = 0; i < line; ++i) { totalHeight += lineMetrics[i].Height; } return(totalHeight); }
/// <summary> /// Return how many lines can be displayed within the desired LayoutHeight.</summary> /// <returns>Number of lines that can be displayed within constraints</returns> public int GetVisibleLines() { var lineMetrics = TextLayout.GetLineMetrics(); int numVisibleLines = 0; float totalHeight = 0; for (int i = 0; i < lineMetrics.Length; ++i) { totalHeight += lineMetrics[i].Height; if (totalHeight > TextLayout.LayoutHeight) { break; } ++numVisibleLines; } return(numVisibleLines); }
public void Evaluate(int SpreadMax) { if (this.FInText.IsConnected == false) { this.metricsCount.SliceCount = 0; this.length.SliceCount = 0; this.trailingWhitespaceLength.SliceCount = 0; this.newlineLength.SliceCount = 0; this.height.SliceCount = 0; this.baseline.SliceCount = 0; this.isTrimmed.SliceCount = 0; return; } if (this.FInText.IsChanged) { this.metricsCount.SliceCount = SpreadMax; cm.Clear(); for (int i = 0; i < SpreadMax; i++) { TextLayout tl = this.FInText[i]; LineMetrics[] cms = tl.GetLineMetrics(); this.metricsCount[i] = cms.Length; cm.AddRange(cms); } this.length.SliceCount = cm.Count; this.trailingWhitespaceLength.SliceCount = cm.Count; this.newlineLength.SliceCount = cm.Count; this.height.SliceCount = cm.Count; this.baseline.SliceCount = cm.Count; this.isTrimmed.SliceCount = cm.Count; for (int i = 0; i < cm.Count; i++) { LineMetrics c = cm[i]; this.length[i] = c.Length; this.trailingWhitespaceLength[i] = c.TrailingWhitespaceLength; this.newlineLength[i] = c.NewlineLength; this.height[i] = c.Height; this.baseline[i] = c.Baseline; this.isTrimmed[i] = c.IsTrimmed; } } }
public UTextInfo NewTextInfo(TextLayout textLayout) { UTextInfo ret = new UTextInfo(); // get size of the render float minHeight = 0; ret.numLines = 0; ret.lineLengths = new int[textLayout.Metrics.LineCount]; ret.lineNewLineLength = new int[textLayout.Metrics.LineCount]; int i = 0; foreach (var tlm in textLayout.GetLineMetrics()) { minHeight += tlm.Height; ret.numLines++; ret.lineLengths[i] = tlm.Length; ret.lineNewLineLength[i] = tlm.NewlineLength; i++; } ret.minSize = new Size(textLayout.DetermineMinWidth(), minHeight); return(ret); }
/// <summary> /// Sets text selection. This may possibly only move the caret, not selecting characters.</summary> /// <param name="moveMode">Text selection mode</param> /// <param name="advance">Number of characters to advance or start selection</param> /// <param name="extendSelection">Whether to extend current selection to additional selection</param> /// <param name="updateCaretFormat">Whether to update caret format based on selection</param> /// <returns>True iff caret changed position as result of selection</returns> public bool SetSelection(SelectionMode moveMode, int advance, bool extendSelection, bool updateCaretFormat) { // Moves the caret relatively or absolutely, optionally extending the // selection range (for example, when shift is held). int line = int.MaxValue; // current line number, needed by a few modes int absolutePosition = m_caretPosition + m_caretPositionOffset; int oldAbsolutePosition = absolutePosition; int oldCaretAnchor = m_caretAnchor; switch (moveMode) { case SelectionMode.Left: m_caretPosition += m_caretPositionOffset; if (m_caretPosition > 0) { --m_caretPosition; AlignCaretToNearestCluster(false, true); // special check for CR/LF pair absolutePosition = m_caretPosition + m_caretPositionOffset; if (absolutePosition >= 1 && absolutePosition < TextLayout.Text.Length && TextLayout.Text[absolutePosition - 1] == '\r' && TextLayout.Text[absolutePosition] == '\n') { m_caretPosition = absolutePosition - 1; AlignCaretToNearestCluster(false, true); } } break; case SelectionMode.Right: m_caretPosition = absolutePosition; AlignCaretToNearestCluster(true, true); // special check for CR/LF pair absolutePosition = m_caretPosition + m_caretPositionOffset; if (absolutePosition >= 1 && absolutePosition < TextLayout.Text.Length && TextLayout.Text[absolutePosition - 1] == '\r' && TextLayout.Text[absolutePosition] == '\n') { m_caretPosition = absolutePosition + 1; AlignCaretToNearestCluster(false, true); } break; case SelectionMode.LeftChar: m_caretPosition = absolutePosition; m_caretPosition -= Math.Min(advance, absolutePosition); m_caretPositionOffset = 0; break; case SelectionMode.RightChar: m_caretPosition = absolutePosition + advance; m_caretPositionOffset = 0; { // Use hit-testing to limit text position. HitTestMetrics hitTestMetrics = TextLayout.HitTestTextPosition( m_caretPosition, false); m_caretPosition = Math.Min(m_caretPosition, hitTestMetrics.TextPosition + hitTestMetrics.Length); } break; case SelectionMode.Up: case SelectionMode.Down: { // Retrieve the line metrics to figure out what line we are on. var lineMetrics = TextLayout.GetLineMetrics(); int linePosition; GetLineFromPosition(lineMetrics, m_caretPosition, out line, out linePosition); // Move up a line or down if (moveMode == SelectionMode.Up) { if (line <= 0) { break; // already top line } line--; linePosition -= lineMetrics[line].Length; if (line <= TopLine) { TopLine = TopLine - 1 >= 0 ? TopLine - 1 : 0; // scroll down a line of text } } else { linePosition += lineMetrics[line].Length; line++; if (line >= lineMetrics.Length) { break; // already bottom line } // scroll up a line of text TopLine = TopLine + 1; } // To move up or down, we need three hit-testing calls to determine: // 1. The x of where we currently are. // 2. The y of the new line. // 3. New text position from the determined x and y. // This is because the characters are variable size. float caretX, caretY; // Get x of current text position var hitTestMetrics = TextLayout.HitTestTextPosition( m_caretPosition, m_caretPositionOffset > 0 // trailing if nonzero, else leading edge ); caretX = hitTestMetrics.Point.X; // Get y of new position hitTestMetrics = TextLayout.HitTestTextPosition( linePosition, false // leading edge ); caretY = hitTestMetrics.Point.Y; // Now get text position of new x,y hitTestMetrics = TextLayout.HitTestPoint(caretX, caretY); m_caretPosition = hitTestMetrics.TextPosition; m_caretPositionOffset = hitTestMetrics.IsTrailingHit ? (hitTestMetrics.Length > 0) ? 1 : 0 : 0; } break; case SelectionMode.LeftWord: case SelectionMode.RightWord: { // To navigate by whole words, we look for the canWrapLineAfter // flag in the cluster metrics. // Now we actually read them. var clusterMetrics = TextLayout.GetClusterMetrics(); if (clusterMetrics.Length == 0) { break; } m_caretPosition = absolutePosition; int clusterPosition = 0; int oldCaretPosition = m_caretPosition; if (moveMode == SelectionMode.LeftWord) { // Read through the clusters, keeping track of the farthest valid // stopping point just before the old position. m_caretPosition = 0; m_caretPositionOffset = 0; // leading edge for (int cluster = 0; cluster < clusterMetrics.Length; ++cluster) { clusterPosition += clusterMetrics[cluster].Length; if (clusterMetrics[cluster].CanWrapLineAfter) { if (clusterPosition >= oldCaretPosition) { break; } // Update in case we pass this point next loop. m_caretPosition = clusterPosition; } } } else // SetSelectionModeRightWord { // Read through the clusters, looking for the first stopping point // after the old position. for (int cluster = 0; cluster < clusterMetrics.Length; ++cluster) { int clusterLength = clusterMetrics[cluster].Length; m_caretPosition = clusterPosition; m_caretPositionOffset = clusterLength; // trailing edge if (clusterPosition >= oldCaretPosition && clusterMetrics[cluster].CanWrapLineAfter) { break; // first stopping point after old position. } clusterPosition += clusterLength; } } } break; case SelectionMode.SingleWord: { var clusterMetrics = TextLayout.GetClusterMetrics(); if (clusterMetrics.Length == 0) { break; } // Left of word m_caretPosition = absolutePosition; int clusterPosition = 0; int oldCaretPosition = m_caretPosition; // Read through the clusters, keeping track of the farthest valid // stopping point just before the old position. m_caretPosition = 0; m_caretPositionOffset = 0; // leading edge for (int cluster = 0; cluster < clusterMetrics.Length; ++cluster) { clusterPosition += clusterMetrics[cluster].Length; if (clusterMetrics[cluster].CanWrapLineAfter) { if (clusterPosition >= oldCaretPosition) { break; } // Update in case we pass this point next loop. m_caretPosition = clusterPosition; } } int leftOfWord = m_caretPosition; // Right of word // Read through the clusters, looking for the first stopping point // after the old position. for (int cluster = 0; cluster < clusterMetrics[cluster].Length; ++cluster) { int clusterLength = clusterMetrics[cluster].Length; m_caretPosition = clusterPosition; m_caretPositionOffset = clusterLength; // trailing edge if (clusterPosition >= oldCaretPosition && clusterMetrics[cluster].CanWrapLineAfter) { break; // first stopping point after old position. } clusterPosition += clusterLength; } int rightOfWord = m_caretPosition - 1; m_caretPositionOffset = 0; m_caretAnchor = leftOfWord; //while (rightOfWord > leftOfWord) //{ // char c = TextLayout.Text[rightOfWord]; // if (!(char.IsWhiteSpace(c) || char.IsPunctuation(c))) // break; // --rightOfWord; //} m_caretPosition = rightOfWord; } break; case SelectionMode.Home: case SelectionMode.End: { // Retrieve the line metrics to know first and last position // on the current line. var lineMetrics = TextLayout.GetLineMetrics(); int linePosition; GetLineFromPosition(lineMetrics, m_caretPosition, out line, out linePosition); m_caretPosition = linePosition; m_caretPositionOffset = 0; if (moveMode == SelectionMode.End) { // Place the caret at the last character on the line, // excluding line breaks. In the case of wrapped lines, // newlineLength will be 0. int lineLength = lineMetrics[line].Length - lineMetrics[line].NewlineLength; m_caretPositionOffset = Math.Min(lineLength, 1); m_caretPosition += lineLength - m_caretPositionOffset; AlignCaretToNearestCluster(true, false); } } break; case SelectionMode.First: m_caretPosition = 0; m_caretPositionOffset = 0; break; case SelectionMode.All: m_caretAnchor = 0; extendSelection = true; goto fallthrough; case SelectionMode.Last: fallthrough: m_caretPosition = int.MaxValue; m_caretPositionOffset = 0; AlignCaretToNearestCluster(true, false); break; case SelectionMode.AbsoluteLeading: m_caretPosition = advance; m_caretPositionOffset = 0; break; case SelectionMode.AbsoluteTrailing: m_caretPosition = advance; AlignCaretToNearestCluster(true, false); break; } absolutePosition = m_caretPosition + m_caretPositionOffset; if (!extendSelection) { m_caretAnchor = absolutePosition; } bool caretMoved = (absolutePosition != oldAbsolutePosition) || (m_caretAnchor != oldCaretAnchor); if (caretMoved) { // scroll the text automatically to avoid caret lost var lineMetrics = TextLayout.GetLineMetrics(); int linePosition; GetLineFromPosition(lineMetrics, m_caretPosition, out line, out linePosition); if (line < TopLine) { TopLine = line; } float visibleLines = TextLayout.Height / lineMetrics[0].Height; if (line > TopLine + visibleLines) { TopLine = TopLine + 1; } //RectF rect; GetCaretRect(); //UpdateSystemCaret(rect); //UpdateSelectionRange(); } //Trace.TraceInformation("caretMoved {0} caretPosition {1} caretPositionOffset {2} caretAnchor {3} ", // caretMoved, m_caretPosition, m_caretPositionOffset, m_caretAnchor); Validate(); return(caretMoved); }
// Update the metrics of this line. public override void UpdateMetrics(TextLayout layout, bool force) { if(force || !valid) { // get the metrics information for this line size = layout.GetLineMetrics (start.Offset, end.Offset); // flag that the metrics information is now valid valid = true; } }