コード例 #1
0
        /// <summary>
        /// Updates the <see cref="VisualText"/> including text wrapping.
        /// </summary>
        /// <param name="text">The text.</param>
        /// <param name="textWidth">The available width.</param>
        /// <param name="font">The font.</param>
        private void UpdateVisualText(string text, float textWidth, SpriteFont font)
        {
            VisualText.Clear();
            WrapText(textWidth, font);

            if (_lineStarts == null || _lineStarts.Count == 0)
            {
                // Single-line: Just add all text. The renderer has to clip the text.
                VisualText.Append(text);
            }
            else
            {
                // Multiline: Add line by line.
                for (int i = 0; i < _lineStarts.Count; i++)
                {
                    int start = _lineStarts[i];
                    int end   = (i + 1 < _lineStarts.Count) ? _lineStarts[i + 1] : -text.Length;

                    VisualText.Append(text, Math.Abs(start), Math.Abs(end) - Math.Abs(start));

                    // A positive index indicates that a newline character needs to be added
                    // for text wrapping.
                    if (end > 0)
                    {
                        VisualText.Append('\n');
                    }
                }
            }
        }
コード例 #2
0
ファイル: TextBlock.cs プロジェクト: DireAussie/MinimalRune
        protected override Vector2F OnMeasure(Vector2F availableSize)
        {
            // This control can only measure itself if it is in a screen because it needs a font.
            var screen = Screen;

            if (screen == null)
            {
                return(base.OnMeasure(availableSize));
            }

            // Clear old renderer info.
            VisualClip = false;
            VisualText.Clear();

            string text      = Text;
            float  width     = Width;
            float  height    = Height;
            bool   hasWidth  = Numeric.IsPositiveFinite(width);
            bool   hasHeight = Numeric.IsPositiveFinite(height);

            if (string.IsNullOrEmpty(text))
            {
                // No text --> Abort.
                return(new Vector2F(
                           hasWidth ? width : 0,
                           hasHeight ? height : 0));
            }

            // Limit constraint size by user-defined width and height.
            if (hasWidth && width < availableSize.X)
            {
                availableSize.X = width;
            }
            if (hasHeight && height < availableSize.Y)
            {
                availableSize.Y = height;
            }

            // Remove padding from constraint size.
            Vector4  padding     = Padding;
            Vector2F contentSize = availableSize;

            if (Numeric.IsPositiveFinite(availableSize.X))
            {
                contentSize.X -= padding.X + padding.Z;
            }
            if (Numeric.IsPositiveFinite(availableSize.Y))
            {
                contentSize.Y -= padding.Y + padding.W;
            }

            // Measure text size.
            var      font = screen.Renderer.GetFont(Font);
            Vector2F size = (Vector2F)font.MeasureString(text);

            if (size < contentSize)
            {
                // All text is visible. (VisualText is equal to Text.)
                VisualText.Append(text);
                return(new Vector2F(
                           hasWidth ? width : size.X + padding.X + padding.Z,
                           hasHeight ? height : size.Y + padding.Y + padding.W));
            }

            // Get number of lines.
            int numberOfLines = 1;

            for (int i = 0; i < text.Length - 1; i++)
            {
                if (text[i] == '\n')
                {
                    numberOfLines++;
                }
            }

            if (numberOfLines == 1 && size.Y > contentSize.Y)
            {
                // Not enough space for a single line height. --> Keep all text and use clipping.
                VisualText.Append(text);
                VisualClip = true;
                return(new Vector2F(
                           hasWidth ? width : size.X + padding.X + padding.Z,
                           hasHeight ? height : size.Y + padding.Y + padding.W));
            }

            if (!WrapText)
            {
                // Not using word wrapping.

                // Compute desired size.
                Vector2F desiredSize = new Vector2F(
                    hasWidth ? width : availableSize.X,
                    hasHeight ? height : availableSize.Y);

                desiredSize.X = Math.Min(desiredSize.X, size.X + padding.X + padding.Z);
                desiredSize.Y = Math.Min(desiredSize.Y, size.Y + padding.Y + padding.W);

                if (numberOfLines > 1 ||    // 2 or more lines?
                    !UseEllipsis ||         // No ellipsis needed?
                    size.Y > contentSize.Y) // Single line is already to high?
                {
                    // Just clip the text.
                    VisualClip = true;
                    VisualText.Append(text);
                }
                else
                {
                    // 1 line that is too long and we have to insert an ellipsis.
                    VisualText.Append(text);
                    TrimText(VisualText, font, contentSize.X);
                }

                return(desiredSize);
            }

            // Get words.
            var words = SplitText();

            // Note: We can compute line heights without font.MeasureString(). But we cannot compute
            // line widths without font.MeasureString() because we do not have kerning information.
            int lineHeight = font.LineSpacing;

            Debug.Assert(lineHeight <= contentSize.Y, "At least one line must fit into content");
            float currentHeight = lineHeight;

            // Add words to string builder until space runs out.
            // In each loop iteration one line is built.
            int index         = 0;
            int lineWordCount = 0;
            var line          = new StringBuilder();

            while (currentHeight < contentSize.Y && // Room for one more line?
                   index < words.Count)     // Words left?
            {
                if (index > 0)
                {
                    VisualText.Append(line.ToString()); // Add line of last iteration.
                    VisualText.Append("\n");

                    // If the next word is a newline, then we can skip it because we start a new line
                    // anyways.
                    if (words[index] == null)
                    {
                        index++;
                    }
                }

                // Start with empty line.
                line.Remove(0, line.Length);
                lineWordCount = 0;

                // Build line.
                while (index < words.Count &&  // As long as we have words.
                       words[index] != null)   // And as long the next word is not a newline.
                {
                    // Add spaces after first word.
                    if (lineWordCount > 0)
                    {
                        line.Append(" ");
                    }

                    // Add next word.
                    line.Append(words[index]);
                    lineWordCount++;
                    index++;

                    float lineWidth = font.MeasureString(line).X;
                    if (lineWidth > contentSize.X)
                    {
                        // Line is too long! Remove last word + blank. But keep at least one word per line.
                        if (lineWordCount > 1)
                        {
                            index--;
                            lineWordCount--;
                            int wordLength = words[index].Length;
                            line.Remove(line.Length - wordLength - 1, wordLength + 1);
                        }
                        else
                        {
                            VisualClip = true; // A single word is too long and must be clipped.
                        }

                        break;
                    }
                }

                currentHeight += lineHeight;
            }

            // Nearly all visible text is in VisualText.
            // The last line is in "line" and was not yet added to VisualText.
            if (UseEllipsis)
            {
                if ((index < words.Count ||                       // Not enough space to print all words.
                     font.MeasureString(line).X > contentSize.X)) // The last line is too long and needs to be trimmed.
                {
                    // Trim the last line and add an ellipsis.
                    line.Append("�");
                    while (lineWordCount > 0 && font.MeasureString(line).X > contentSize.X)
                    {
                        index--;

                        // We have to remove one word before the ellipsis.
                        int wordLength = words[index].Length;
                        line.Remove(line.Length - 1 - wordLength, wordLength);

                        // Remove ' ' before ellipsis.
                        int indexBeforeEllipsis = line.Length - 2;
                        if (indexBeforeEllipsis > 0 && line[indexBeforeEllipsis] == ' ')
                        {
                            line.Remove(indexBeforeEllipsis, 1);
                        }

                        lineWordCount--;
                    }
                }
            }

            // Add last line.
            VisualText.Append(line);

            size = (Vector2F)font.MeasureString(VisualText);
            return(new Vector2F(
                       hasWidth ? width : size.X + padding.X + padding.Z,
                       hasHeight ? height : size.Y + padding.Y + padding.W));
        }