コード例 #1
0
ファイル: TextEditor.cs プロジェクト: zoombapup/ATF
        private void AlignCaretToNearestCluster(bool isTrailingHit, bool skipZeroWidth)
        {
            // Uses hit-testing to align the current caret position to a whole cluster,
            // rather than residing in the middle of a base character + diacritic,
            // surrogate pair, or character + UVS.

            // Align the caret to the nearest whole cluster.
            HitTestMetrics hitTestMetrics = TextLayout.HitTestTextPosition(m_caretPosition, false);

            // The caret position itself is always the leading edge.
            // An additional offset indicates a trailing edge when non-zero.
            // This offset comes from the number of code-units in the
            // selected cluster or surrogate pair.
            m_caretPosition       = hitTestMetrics.TextPosition;
            m_caretPositionOffset = (isTrailingHit) ? hitTestMetrics.Length : 0;

            // For invisible, zero-width characters (like line breaks
            // and formatting characters), force leading edge of the
            // next position.
            if (skipZeroWidth && hitTestMetrics.Width == 0)
            {
                m_caretPosition      += m_caretPositionOffset;
                m_caretPositionOffset = 0;
            }
        }
コード例 #2
0
ファイル: TextEditor.cs プロジェクト: zoombapup/ATF
        /// <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);
        }