示例#1
0
        /// <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);
        }
示例#2
0
 public double[] GetLineHeights(FormattedText text, Size constraint)
 {
     using (TextLayout layout = GetTextLayout(this.factory, text, constraint))
     {
         return(layout.GetLineMetrics().Select(x => (double)x.Height).ToArray());
     }
 }
示例#3
0
        /// <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);
        }
示例#4
0
        /// <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;
                }
            }
        }
示例#6
0
        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);
        }
示例#7
0
        /// <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;
						}
					}