Exemple #1
0
        private Size doPaintOrMeasure(Graphics g, EggsNode node, Font initialFont, Color initialForeColor, int constrainingWidth,
            List<renderingInfo> renderings = null, List<locationInfo> locations = null)
        {
            var glyphOverhang = TextRenderer.MeasureText(g, "Wg", initialFont, _dummySize) - TextRenderer.MeasureText(g, "Wg", initialFont, _dummySize, TextFormatFlags.NoPadding);
            int x = glyphOverhang.Width / 2, y = glyphOverhang.Height / 2;
            int wrapWidth = WordWrap ? Math.Max(1, constrainingWidth - glyphOverhang.Width) : int.MaxValue;
            int hangingIndent = _hangingIndent * (_hangingIndentUnit == IndentUnit.Spaces ? measure(initialFont, " ", g).Width : 1);
            bool atBeginningOfLine = false;

            // STEP 1: Run the word-wrapping as if TextAlign were TopLeft

            int actualWidth = EggsML.WordWrap(node, new renderState(initialFont, initialForeColor), wrapWidth,
                (state, text) => measure(state.Font, text, g).Width,
                (state, text, width) =>
                {
                    if (state.Mnemonic && !string.IsNullOrWhiteSpace(text))
                        state.ActiveLocations.OfType<linkLocationInfo>().FirstOrDefault().NullOr(link => { link.Mnemonic = char.ToLowerInvariant(text.Trim()[0]); return link; });

                    if (renderings != null && !string.IsNullOrEmpty(text))
                    {
                        renderingInfo info;
                        if (!atBeginningOfLine && renderings.Count > 0 && (info = renderings[renderings.Count - 1]).State == state)
                        {
                            info.Text += text;
                            var rect = info.Rectangle;
                            rect.Width += width;
                            info.Rectangle = rect;
                        }
                        else
                        {
                            info = new renderingInfo(text, new Rectangle(x, y, width, measure(state.Font, " ", g).Height), state);
                            renderings.Add(info);
                        }
                        foreach (var location in state.ActiveLocations)
                        {
                            if (location.Rectangles.Count == 0 || location.Rectangles[location.Rectangles.Count - 1].Y != info.Rectangle.Y)
                                location.Rectangles.Add(info.Rectangle);
                            else
                            {
                                var rect = location.Rectangles[location.Rectangles.Count - 1];
                                rect.Width += width;
                                location.Rectangles[location.Rectangles.Count - 1] = rect;
                            }
                        }
                    }
                    atBeginningOfLine = false;
                    x += width;
                },
                (state, newParagraph, indent) =>
                {
                    atBeginningOfLine = true;
                    var sh = measure(state.Font, " ", g).Height;
                    y += sh;
                    if (newParagraph && _paragraphSpacing > 0)
                        y += (int) (_paragraphSpacing * sh);
                    var newIndent = state.BlockIndent + indent;
                    if (!newParagraph)
                        newIndent += hangingIndent;
                    x = newIndent + glyphOverhang.Width / 2;
                    return newIndent;
                },
                (state, tag, parameter) =>
                {
                    var font = state.Font;
                    switch (tag)
                    {
                        // ITALICS
                        case '/': return Tuple.Create(state.ChangeFont(new Font(font, font.Style | FontStyle.Italic)), 0);

                        // BOLD
                        case '*': return Tuple.Create(state.ChangeFont(new Font(font, font.Style | FontStyle.Bold)), 0);

                        // UNDERLINE
                        case '_': return Tuple.Create(state.ChangeFont(new Font(font, font.Style | FontStyle.Underline)), 0);

                        // MNEMONICS
                        case '&': return Tuple.Create(state.SetMnemonic(), 0);

                        // BULLET POINT
                        case '[':
                            var bulletSize = measure(font, _bullet, g);
                            var advance = bulletSize.Width;
                            if (renderings != null)
                                renderings.Add(new renderingInfo(_bullet, new Rectangle(x, y, advance, bulletSize.Height), new renderState(font, state.Color)));
                            x += advance;
                            return Tuple.Create(state.ChangeBlockIndent(state.BlockIndent + advance), advance);

                        // LINK (e.g. <link target>{link text}, link target may be omitted)
                        case '{':
                            if (locations == null)
                                break;
                            var linkLocation = new linkLocationInfo { LinkID = parameter };
                            locations.Add(linkLocation);
                            return Tuple.Create(state.ChangeColor(Enabled ? LinkColor : SystemColors.GrayText).AddActiveLocation(linkLocation), 0);

                        // TOOLTIP (e.g. <tooltip text>#main text#)
                        case '#':
                            if (string.IsNullOrWhiteSpace(parameter) || locations == null)
                                break;
                            var tooltipLocation = new tooltipLocationInfo { Tooltip = parameter };
                            locations.Add(tooltipLocation);
                            return Tuple.Create(state.AddActiveLocation(tooltipLocation), 0);

                        // COLOUR (e.g. <colour>=coloured text=, revert to default colour if no <colour> specified)
                        case '=':
                            var color = parameter == null ? initialForeColor : (Color) (_colorConverter ?? (_colorConverter = new ColorConverter())).ConvertFromString(parameter);
                            return Tuple.Create(state.ChangeColor(color), 0);
                    }
                    return Tuple.Create(state, 0);
                });

            var totalSize = new Size(actualWidth + glyphOverhang.Width, y + measure(initialFont, " ", g).Height + glyphOverhang.Height);

            // STEP 2: Fix everything according to TextAlign.
            if (renderings != null)
            {
                // 2a: vertical alignment
                int offsetY = 0;
                switch (_textAlign)
                {
                    case ContentAlignment.TopCenter:
                    case ContentAlignment.TopLeft:
                    case ContentAlignment.TopRight:
                        // Already top-aligned: nothing to do
                        break;

                    case ContentAlignment.MiddleCenter:
                    case ContentAlignment.MiddleLeft:
                    case ContentAlignment.MiddleRight:
                        offsetY = ClientSize.Height / 2 - totalSize.Height / 2;
                        goto default;

                    case ContentAlignment.BottomCenter:
                    case ContentAlignment.BottomLeft:
                    case ContentAlignment.BottomRight:
                        offsetY = ClientSize.Height - totalSize.Height;
                        goto default;

                    default:
                        foreach (var inf in renderings)
                        {
                            var rect = inf.Rectangle;
                            rect.Y += offsetY;
                            inf.Rectangle = rect;
                        }
                        break;
                }

                // 2b: horizontal alignment
                foreach (var group in renderings.GroupConsecutiveBy(inf => inf.Rectangle.Y))
                {
                    var width = group.Max(elem => elem.Rectangle.Right);
                    int offsetX = 0;
                    switch (_textAlign)
                    {
                        case ContentAlignment.TopLeft:
                        case ContentAlignment.MiddleLeft:
                        case ContentAlignment.BottomLeft:
                            // Already left-aligned: nothing to do
                            break;

                        case ContentAlignment.TopCenter:
                        case ContentAlignment.MiddleCenter:
                        case ContentAlignment.BottomCenter:
                            offsetX = ClientSize.Width / 2 - width / 2;
                            goto default;

                        case ContentAlignment.TopRight:
                        case ContentAlignment.MiddleRight:
                        case ContentAlignment.BottomRight:
                            offsetX = ClientSize.Width - width;
                            goto default;

                        default:
                            foreach (var inf in group)
                            {
                                var rect = inf.Rectangle;
                                rect.X += offsetX;
                                inf.Rectangle = rect;
                            }
                            break;
                    }
                }
            }

            return totalSize;
        }
Exemple #2
0
        private Size doPaintOrMeasure(Graphics g, EggsNode node, Font initialFont, Color initialForeColor, int constrainingWidth,
                                      List <renderingInfo> renderings = null, List <locationInfo> locations = null)
        {
            var  glyphOverhang = TextRenderer.MeasureText(g, "Wg", initialFont, _dummySize) - TextRenderer.MeasureText(g, "Wg", initialFont, _dummySize, TextFormatFlags.NoPadding);
            int  x = glyphOverhang.Width / 2, y = glyphOverhang.Height / 2;
            int  wrapWidth         = WordWrap ? Math.Max(1, constrainingWidth - glyphOverhang.Width) : int.MaxValue;
            int  hangingIndent     = _hangingIndent * (_hangingIndentUnit == IndentUnit.Spaces ? measure(initialFont, " ", g).Width : 1);
            bool atBeginningOfLine = false;

            // STEP 1: Run the word-wrapping as if TextAlign were TopLeft

            int actualWidth = EggsML.WordWrap(node, new renderState(initialFont, initialForeColor), wrapWidth,
                                              (state, text) => measure(state.Font, text, g).Width,
                                              (state, text, width) =>
            {
                if (state.Mnemonic && !string.IsNullOrWhiteSpace(text))
                {
                    state.ActiveLocations.OfType <linkLocationInfo>().FirstOrDefault().NullOr(link => { link.Mnemonic = char.ToLowerInvariant(text.Trim()[0]); return(link); });
                }

                if (renderings != null && !string.IsNullOrEmpty(text))
                {
                    renderingInfo info;
                    if (!atBeginningOfLine && renderings.Count > 0 && (info = renderings[renderings.Count - 1]).State == state)
                    {
                        info.Text     += text;
                        var rect       = info.Rectangle;
                        rect.Width    += width;
                        info.Rectangle = rect;
                    }
                    else
                    {
                        info = new renderingInfo(text, new Rectangle(x, y, width, measure(state.Font, " ", g).Height), state);
                        renderings.Add(info);
                    }
                    foreach (var location in state.ActiveLocations)
                    {
                        if (location.Rectangles.Count == 0 || location.Rectangles[location.Rectangles.Count - 1].Y != info.Rectangle.Y)
                        {
                            location.Rectangles.Add(info.Rectangle);
                        }
                        else
                        {
                            var rect    = location.Rectangles[location.Rectangles.Count - 1];
                            rect.Width += width;
                            location.Rectangles[location.Rectangles.Count - 1] = rect;
                        }
                    }
                }
                atBeginningOfLine = false;
                x += width;
            },
                                              (state, newParagraph, indent) =>
            {
                atBeginningOfLine = true;
                var sh            = measure(state.Font, " ", g).Height;
                y += sh;
                if (newParagraph && _paragraphSpacing > 0)
                {
                    y += (int)(_paragraphSpacing * sh);
                }
                var newIndent = state.BlockIndent + indent;
                if (!newParagraph)
                {
                    newIndent += hangingIndent;
                }
                x = newIndent + glyphOverhang.Width / 2;
                return(newIndent);
            },
                                              (state, tag, parameter) =>
            {
                var font = state.Font;
                switch (tag)
                {
                // ITALICS
                case '/': return(state.ChangeFont(new Font(font, font.Style | FontStyle.Italic)), 0);