Exemplo n.º 1
0
        /// <summary>Lets the renderer know that the given parent element has finished
        /// packing all of its kids. This allows alignment to occur next.</summary>
        /// <param name="renderable">The element that is done packing.</param>
        public void EndLines(LineBoxMeta lineZone, ComputedStyle computed, LayoutBox box)
        {
            // Pop the box:
            BoxStack.Pop();

            // Complete the last line:
            lineZone.CompleteLine(LineBreakMode.NoBreak | LineBreakMode.Last);

            // If inline-block or float, clear:
            if (box.DisplayMode == DisplayMode.InlineBlock || box.FloatMode != FloatMode.None)
            {
                // Must clear:
                lineZone.ClearFloat(FloatMode.Both);

                lineZone.PenY   += lineZone.ClearY_;
                lineZone.ClearY_ = 0f;
            }

            // block, inline-block
            if (lineZone is BlockBoxMeta)
            {
                bool heightChange = false;

                if (box.InnerHeight == -1f)
                {
                    heightChange    = true;
                    box.InnerHeight = lineZone.PenY;

                    // Clip height now:
                    box.InnerHeight = computed.ClipHeight(box.DisplayMode, box.InnerHeight);
                }

                box.ContentHeight = lineZone.PenY;
                box.ContentWidth  = lineZone.LargestLineWidth;

                // If it's inline then we set the line width.
                if (box.InnerWidth == -1f)
                {
                    box.InnerWidth = lineZone.LargestLineWidth;

                    // Apply valid width/height:
                    box.SetDimensions(false, false);
                }
                else if (heightChange)
                {
                    // Apply valid width/height:
                    box.SetDimensions(false, false);
                }

                bool inFlow = (box.PositionMode & PositionMode.InFlow) != 0;

                // Update position of the top-of-stack pen:
                LineBoxMeta tos = TopOfStackSafe;

                if (tos == null)
                {
                    LastBlockBox = null;
                }
                else
                {
                    if (inFlow)
                    {
                        // Advance the pen:
                        tos.AdvancePen(box);
                    }

                    // Restore previous block:
                    LastBlockBox = tos as BlockBoxMeta;

                    if (LastBlockBox == null)
                    {
                        // Rare - block inside inline.
                        LastBlockBox = (tos as InlineBoxMeta).HostBlock;
                    }
                }
            }
        }
        public override void Reflow(Renderman renderer)
        {
            // Get the text renderer (or create it):
            Css.TextRenderingProperty text = RequireTextProperty();

            if (text.Characters == null || text.AllEmpty)
            {
                return;
            }

            LayoutBox box = null;

            // Get the baseline offset:
            float baseline = text.FontSize * text.FontToDraw.Descender;

            // Compute our line boxes based on text.Characters and the available space.
            // Safely ignore direction here because either way the selected characters are the same.
            // Note that things like first-letter are also considered.

            // Get the top of the stack:
            LineBoxMeta lbm           = renderer.TopOfStack;
            float       cssLineHeight = lbm.CssLineHeight;

            // Is it justify (if so, every word is its own box):
            bool breakAllWords = (lbm.HorizontalAlign == HorizontalAlignMode.Justify);

            // Offset the baseline:
            float lineHeightOffset = (cssLineHeight - text.FontSize) / 2f;

            text.LineHeightOffset = lineHeightOffset;
            baseline += lineHeightOffset;

            float wordWidth        = 0f;
            float boxWidth         = 0f;
            bool  wrappable        = ((lbm.WhiteSpace & WhiteSpaceMode.Wrappable) != 0);
            int   i                = 0;
            int   latestBreakpoint = -1;
            // bidi text direction (0 = Not set yet, UnicodeBidiMode.LeftwardsNormal, UnicodeBidiMode.RightwardsNormal)
            int direction = 0;
            // Direction before the current block of weak chars.
            int beforeWeak = 0;


            while (i < text.Characters.Length)
            {
                // Get the glyph:
                InfiniText.Glyph glyph = text.Characters[i];

                if (glyph == null)
                {
                    // Skip!
                    i++;
                    continue;
                }

                // The glyph's width is..
                float width = (glyph.AdvanceWidth * text.FontSize) + text.LetterSpacing;

                if (box == null)
                {
                    // The box always has an inner height of 'font size':
                    if (renderer.FirstLetter != null)
                    {
                        // Clear FL immediately (so it can't go recursive):
                        SparkInformerNode firstLetter = renderer.FirstLetter;
                        renderer.FirstLetter = null;

                        // Update its internal text node:
                        RenderableTextNode textNode = firstLetter.firstChild as RenderableTextNode;

                        // Note that we have to do it this way as the node might
                        // change the *font*.
                        textNode.characterData_ = ((char)glyph.Charcode) + "";
                        Css.TextRenderingProperty textData = textNode.RenderData.RequireTextProperty();
                        textData.Dirty = true;

                        // Ask it to reflow right now (must ask the node so it correctly takes the style into account):
                        firstLetter.RenderData.UpdateCss(renderer);
                        firstLetter.RenderData.Reflow(renderer);

                        i++;
                        continue;
                    }

                    // Create the box now:
                    box = new LayoutBox();
                    box.PositionMode = PositionMode.Static;
                    box.DisplayMode  = DisplayMode.Inline;
                    box.Baseline     = baseline;
                    box.TextStart    = i;

                    // Adopt current direction:
                    box.UnicodeBidi = direction;

                    if (FirstBox == null)
                    {
                        FirstBox = box;
                        LastBox  = box;
                    }
                    else
                    {
                        // add to this element:
                        LastBox.NextInElement = box;
                        LastBox = box;
                    }

                    // line-height is the inner height here:
                    box.InnerHeight = cssLineHeight;
                    boxWidth        = 0f;
                    wordWidth       = 0f;
                }

                // Are we breaking this word?
                bool implicitNewline = (glyph.Charcode == (int)'\n');
                int  breakMode       = implicitNewline ? 1 : 0;

                // direction:
                int dir = glyph.Directionality;

                if (dir == InfiniText.BidiBlock.Rightwards)
                {
                    // Rightwards (includes weak ones):

                    // Was the previous one 'weak'?
                    if (dir == InfiniText.BidiBlock.WeakLeftToRight)
                    {
                        // Update before weak:
                        beforeWeak = direction;
                    }
                    else
                    {
                        // Not weak:
                        beforeWeak = 0;
                    }

                    if (direction != UnicodeBidiMode.RightwardsNormal)
                    {
                        if (direction == 0)
                        {
                            // Not set yet - Set the mode into the box:
                            box.UnicodeBidi = UnicodeBidiMode.RightwardsNormal;
                        }
                        else
                        {
                            // Changed from leftwards to rightwards. Break the boxes here.
                            breakMode = 3;
                        }

                        // Update dir:
                        direction = UnicodeBidiMode.RightwardsNormal;
                    }
                }
                else if (dir == InfiniText.BidiBlock.RightToLeft)
                {
                    // Leftwards

                    if (direction != UnicodeBidiMode.LeftwardsNormal)
                    {
                        if (direction == 0)
                        {
                            // Not set yet - Set the mode into the box:
                            box.UnicodeBidi = UnicodeBidiMode.LeftwardsNormal;
                        }
                        else
                        {
                            // Changed from rightwards to leftwards. Break the boxes here.
                            breakMode = 3;
                        }

                        // Update dir:
                        direction = UnicodeBidiMode.LeftwardsNormal;
                    }

                    // Never weak:
                    beforeWeak = 0;
                }
                else if (beforeWeak != 0)
                {
                    // Neutral otherwise
                    // (adopts whatever the current is, unless the previous one was *weak*,
                    // in which case, it adopts whatever the direction was before that):
                    if (direction != beforeWeak)
                    {
                        // Change direction!
                        direction = beforeWeak;
                        breakMode = 3;
                    }
                }

                // Got a space?
                bool space = (glyph.Charcode == (int)' ');

                if (space)
                {
                    // Advance width:
                    width += text.WordSpacing;

                    if (breakAllWords)
                    {
                        breakMode = 3;
                    }
                    else
                    {
                        latestBreakpoint = i;
                    }

                    // Lock in the previous text:
                    boxWidth += wordWidth + width;
                    wordWidth = 0f;
                }
                else
                {
                    // Advance word width now:
                    wordWidth += width;
                }

                // Word wrapping next:
                if (breakMode == 0 && wrappable && i != box.TextStart)
                {
                    // Test if we can break here:
                    breakMode = lbm.GetLineSpace(wordWidth, boxWidth);

                    // Return to the previous space if we should.
                    if (breakMode == 2 && text.OverflowWrapActive)
                    {
                        // The word doesn't fit at all (2) and we're supposed to break it.
                        boxWidth += wordWidth - width;
                        wordWidth = 0f;
                    }
                    else if (breakMode != 0)
                    {
                        if (latestBreakpoint == -1)
                        {
                            // Isn't a previous space!

                            if (breakMode == 2)
                            {
                                // Instead, we'll try and break a parent.
                                // This typically happens with inline elements
                                // which are right on the end of the host line.
                                lbm.TryBreakParent();

                                // Don't break the node:
                                breakMode = 0;
                            }
                            else if (breakMode == 3)
                            {
                                // Break but no newline - just advance to the following char:
                                i++;
                            }
                            else if (lbm.PenX != lbm.LineStart)
                            {
                                // Newline:
                                lbm.CompleteLine(LineBreakMode.Normal);

                                // Don't break the node:
                                breakMode = 0;
                            }
                            else
                            {
                                // Don't break the node:
                                breakMode = 0;
                            }
                        }
                        else
                        {
                            i = latestBreakpoint + 1;
                        }
                    }
                }

                if (breakMode != 0)
                {
                    // We're breaking!
                    box.InnerWidth   = boxWidth;
                    box.TextEnd      = i;
                    latestBreakpoint = i;

                    // If the previous glyph is a space, update EndSpaceSize:
                    if (space)
                    {
                        // Update ending spaces:
                        box.EndSpaceSize = width;
                    }

                    // Ensure dimensions are set:
                    box.SetDimensions(false, false);

                    // Add the box to the line:
                    lbm.AddToLine(box);

                    // Update dim's:
                    lbm.AdvancePen(box);

                    // Clear:
                    box      = null;
                    boxWidth = 0f;

                    if (breakMode != 3)
                    {
                        // Newline:
                        if (implicitNewline)
                        {
                            // Also the last line:
                            lbm.CompleteLine(LineBreakMode.Normal | LineBreakMode.Last);
                        }
                        else
                        {
                            // Normal:
                            lbm.CompleteLine(LineBreakMode.Normal);

                            // Process it again.
                            continue;
                        }
                    }
                }

                // Next character:
                i++;
            }

            if (box != null)
            {
                // Always apply inner width:
                box.InnerWidth = boxWidth + wordWidth;
                box.TextEnd    = text.Characters.Length;

                // Ensure dimensions are set:
                box.SetDimensions(false, false);

                // Add the box to the line:
                lbm.AddToLine(box);

                // Update dim's:
                lbm.AdvancePen(box);
            }
        }