Пример #1
0
 public int DrawText(Control control, int?reference, Rectangle bounds, string text, Font font, Color color,
                     HorizontalAlignment horizontalAlignment, VertictalAlignment vertictalAlignment, bool wordWrap = false,
                     TextOverflow textOverflow = TextOverflow.Clip)
 {
     return(RunDrawCall(control, reference, () => _renderer.DrawText(bounds, text, font, color,
                                                                     horizontalAlignment, vertictalAlignment, wordWrap, textOverflow)));
 }
Пример #2
0
        // TODO: LTR/RTL support
        public int DrawText(Rectangle bounds, string text, Font font, Color color,
                            HorizontalAlignment horizontalAlignment, VertictalAlignment vertictalAlignment,
                            bool wordWrap, TextOverflow textOverflow)
        {
            if (color.A == 0 || font.Size == 0 || string.IsNullOrWhiteSpace(text))
            {
                return(DenyDrawing());
            }

            FontGlyphs glyphs;

            try
            {
                glyphs = FontManager.Instance.GetFont(font);
            }
            catch (Exception ex)
            {
                var exception = new KeyNotFoundException("The given font is not available.", ex);
                exception.Data.Add("Font", font);
                throw exception;
            }

            var drawCommands    = new List <DrawCommand>(text.Length);
            var lines           = new Queue <TextLine>();
            var currentTextLine = new TextLine()
            {
                StartCommandIndex = 0,
                CommandCount      = 0,
                Width             = 0
            };
            int       x = bounds.X;
            int       y = bounds.Y + font.Size;
            int       lastWhiteSpacePosition = -1;
            int       widthToLastWhiteSpace  = 0;
            int       lastWhiteSpaceWidth    = 0;
            Rectangle?clipRect = textOverflow == TextOverflow.Allow ? (Rectangle?)null : bounds;

            void StartNextLine(TextLine?line = null)
            {
                AdjustLineX();
                x  = bounds.X;
                y += glyphs.LineHeight;
                lastWhiteSpacePosition = -1;
                lines.Enqueue(currentTextLine);
                currentTextLine = line ?? new TextLine()
                {
                    StartCommandIndex = currentTextLine.CurrentCommandIndex,
                    CommandCount      = 0,
                    Width             = 0
                };
            }

            void SplitLine()
            {
                var nextLine = new TextLine()
                {
                    // Note: The whitespace is not added as a draw command so the
                    // lastWhiteSpacePosition points to the draw command after
                    // the whitespace character.
                    StartCommandIndex = lastWhiteSpacePosition,
                    CommandCount      = currentTextLine.CommandCount - lastWhiteSpacePosition,
                    Width             = currentTextLine.Width - widthToLastWhiteSpace - lastWhiteSpaceWidth
                };

                currentTextLine.Width        = widthToLastWhiteSpace;
                currentTextLine.CommandCount = lastWhiteSpacePosition - currentTextLine.StartCommandIndex;
                StartNextLine(nextLine);
                x += nextLine.Width;

                // Reposition the glyphs in the line that has moved down.
                for (int i = 0; i < currentTextLine.CommandCount; ++i)
                {
                    drawCommands[currentTextLine.StartCommandIndex + i].Offset(-widthToLastWhiteSpace - lastWhiteSpaceWidth, glyphs.LineHeight);
                }
            }

            void AdjustLineX()
            {
                var lineDrawCommands = drawCommands.Skip(currentTextLine.StartCommandIndex).Take(currentTextLine.CommandCount);

                switch (horizontalAlignment)
                {
                case HorizontalAlignment.Left:
                default:
                    break;

                case HorizontalAlignment.Center:
                {
                    int offsetX = (bounds.Width - currentTextLine.Width) / 2;
                    if (offsetX != 0)
                    {
                        foreach (var drawCommand in lineDrawCommands)
                        {
                            drawCommand.Offset(offsetX, 0);
                        }
                    }
                    break;
                }

                case HorizontalAlignment.Right:
                {
                    int offsetX = bounds.Width - currentTextLine.Width;
                    if (offsetX != 0)
                    {
                        foreach (var drawCommand in lineDrawCommands)
                        {
                            drawCommand.Offset(offsetX, 0);
                        }
                    }
                    break;
                }

                case HorizontalAlignment.Justify:
                    if (currentTextLine.Width >= bounds.Width)
                    {
                        // If it doesn't fit into the bounds, we just center it.
                        int offsetX = (bounds.Width - currentTextLine.Width) / 2;
                        if (offsetX != 0)
                        {
                            foreach (var drawCommand in lineDrawCommands)
                            {
                                drawCommand.Offset(offsetX, 0);
                            }
                        }
                    }
                    else
                    {
                        // TODO: we don't have the whitespace here anymore :(
                        throw new NotImplementedException("Justify is not implemented yet.");
                    }
                    break;
                }
            }

            for (int i = 0; i < text.Length; ++i)
            {
                if (text[i] == '\n')
                {
                    StartNextLine();
                    continue;
                }
                else if (text[i] == '\r')
                {
                    if (i == text.Length - 1 || text[i + 1] != '\n')
                    {
                        StartNextLine();
                    }
                    continue;
                }

                int  lastX      = x;
                var  glyph      = DrawGlyph(ref x, y, text[i], color, glyphs, clipRect);
                int  glyphWidth = x - lastX;
                bool whiteSpace = text[i] <= 32 || char.IsWhiteSpace(text[i]);


                if (whiteSpace)
                {
                    lastWhiteSpacePosition = currentTextLine.CurrentCommandIndex;
                    widthToLastWhiteSpace  = currentTextLine.Width;
                    lastWhiteSpaceWidth    = glyphWidth;
                }

                int newLineWidth = currentTextLine.Width + glyphWidth;

                if (wordWrap && lastWhiteSpacePosition != -1 && newLineWidth > bounds.Width)
                {
                    // If this is a white space character we simply ignore it and break.
                    if (whiteSpace)
                    {
                        StartNextLine();
                        continue;
                    }
                    else
                    {
                        // Bring the current glyph to the right position.
                        glyph.Offset(-widthToLastWhiteSpace - lastWhiteSpaceWidth, glyphs.LineHeight);
                        SplitLine();
                        x += glyphWidth; // x has been reset in SplitLine!
                    }
                }

                if (glyph != null)
                {
                    drawCommands.Add(glyph);
                    ++currentTextLine.CommandCount;
                }

                currentTextLine.Width += glyphWidth;

                if (i == text.Length - 1)
                {
                    AdjustLineX();

                    lines.Enqueue(currentTextLine);

                    // Adjust y of all lines
                    int totalHeight = lines.Count * glyphs.LineHeight;

                    switch (vertictalAlignment)
                    {
                    case VertictalAlignment.Top:
                    default:
                        break;

                    case VertictalAlignment.Center:
                    {
                        int offsetY = (bounds.Height - totalHeight) / 2;
                        if (offsetY != 0)
                        {
                            foreach (var drawCommand in drawCommands)
                            {
                                drawCommand.Offset(0, offsetY);
                            }
                        }
                        break;
                    }

                    case VertictalAlignment.Bottom:
                    {
                        int offsetY = bounds.Height - totalHeight;
                        if (offsetY != 0)
                        {
                            foreach (var drawCommand in drawCommands)
                            {
                                drawCommand.Offset(0, offsetY);
                            }
                        }
                        break;
                    }
                    }
                }
            }

            if (drawCommands.Count == 0)
            {
                return(DenyDrawing()); // Nothing was drawn.
            }
            ++_displayLayer;

            return(AddDrawCommands(drawCommands.ToArray()));
        }