/// <summary> /// Recursively flows the content of the box using the inline model /// </summary> /// <param name="lay"></param> /// <param name="hostBox"></param> /// <param name="srcBox"></param> /// <param name="limitLocalRight"></param> /// <param name="firstRunStartX"></param> /// <param name="hostLine"></param> /// <param name="cx"></param> static void FlowBoxContentIntoHostLineFmtContext( LayoutVisitor lay, CssBox hostBox, //target host box that contains line formatting context CssBox srcBox, //src that has runs /splitable content) to flow into hostBox line model float limitLocalRight, float firstRunStartX, ref CssLineBox hostLine, ref float cx, ref FloatFormattingContext floatCtx) { //recursive *** //-------------------------------------------------------------------- var oX = cx; if (srcBox.HasOnlyRuns) { //condition 3 FlowRunsIntoHost(lay, hostBox, srcBox, srcBox, 0, limitLocalRight, firstRunStartX, 0, 0, CssBox.UnsafeGetRunList(srcBox), ref hostLine, ref cx ); } else { int childNumber = 0; var ifonts = lay.SampleIFonts; CssBoxCollection children = CssBox.UnsafeGetChildren(srcBox); var cNode = children.GetFirstLinkedNode(); while (cNode != null) { float leftMostSpace = 0, rightMostSpace = 0; CssBox b = cNode.Value; //if b has absolute pos then it is removed from the flow if (b.NeedComputedValueEvaluation) { b.ReEvaluateComputedValues(ifonts, hostBox); } b.MeasureRunsSize(lay); #if DEBUG if (b.Position == CssPosition.Absolute) { //should not found here! throw new NotSupportedException(); } #endif cx += leftMostSpace; if (b.CssDisplayInside == CssDisplayInside.FlowRoot)//eg. inline block { //-------- // if inside display is FlowRoot *** //--------- //outside -> inline //inside -> flow-root //can't split //create 'block-run' CssLayoutEngine.PerformContentLayout(b, lay); CssBlockRun blockRun = b.JustBlockRun; if (blockRun == null) { blockRun = new CssBlockRun(b); blockRun.SetOwner(srcBox); b.JustBlockRun = blockRun; } if (b.Width.IsEmptyOrAuto) { blockRun.SetSize(CssBox.GetLatestCachedMinWidth(b), b.VisualHeight); } else { blockRun.SetSize(b.VisualWidth, b.VisualHeight); } b.SetLocation(b.LocalX, 0); //because of inline*** FlowRunsIntoHost(lay, hostBox, srcBox, b, childNumber, limitLocalRight, firstRunStartX, leftMostSpace, rightMostSpace, new List<CssRun>() { b.JustBlockRun }, ref hostLine, ref cx); } else if (b.CssDisplayOutside == CssDisplayOutside.Block) { //warning : this code block not follow w3c spec *** CssLayoutEngine.PerformContentLayout(b, lay); CssBlockRun blockRun = b.JustBlockRun; if (blockRun == null) { blockRun = new CssBlockRun(b); blockRun.SetOwner(srcBox); b.JustBlockRun = blockRun; } //set width to full *** blockRun.SetSize(hostBox.GetClientWidth(), b.VisualHeight); b.SetLocation(b.LocalX, 0); //because of inline*** FlowRunsIntoHost(lay, hostBox, srcBox, b, childNumber, limitLocalRight, firstRunStartX, leftMostSpace, rightMostSpace, new List<CssRun>() { b.JustBlockRun }, ref hostLine, ref cx); } else if (b.HasOnlyRuns) { switch (b.Float) { default: case CssFloat.None: { FlowRunsIntoHost(lay, hostBox, srcBox, b, childNumber, limitLocalRight, firstRunStartX, leftMostSpace, rightMostSpace, CssBox.UnsafeGetRunList(b), ref hostLine, ref cx); } break; case CssFloat.Left: { //float is out of flow item //1. current line is shortening //2. add 'b' to special container *** var newAnonBlock = new CssFloatContainerBox( CssBox.UnsafeGetBoxSpec(b), b.RootGfx, CssDisplay.Block); newAnonBlock.ReEvaluateComputedValues(ifonts, hostBox); //add to abs layer hostBox.AppendToAbsoluteLayer(newAnonBlock); newAnonBlock.ResetLineBoxes(); float localX1 = 0; var line = new CssLineBox(newAnonBlock); newAnonBlock.AddLineBox(line); var newFloatCtx = new FloatFormattingContext(); FlowBoxContentIntoHostLineFmtContext(lay, newAnonBlock, b, limitLocalRight, 0, ref line, ref localX1, ref newFloatCtx); float localY = 0; int interlineSpace = 0; float maxLineWidth = 0; CssTextAlign textAlign = newAnonBlock.CssTextAlign; foreach (CssLineBox linebox in newAnonBlock.GetLineBoxIter()) { ApplyAlignment(linebox, textAlign, lay); linebox.CloseLine(lay); //*** linebox.CachedLineTop = localY; localY += linebox.CacheLineHeight + interlineSpace; if (maxLineWidth < linebox.CachedExactContentWidth) { maxLineWidth = linebox.CachedExactContentWidth; } } float hostSizeW = hostBox.VisualWidth; SetFinalInnerContentSize(newAnonBlock, maxLineWidth, localY, lay); //need to adjust line box //TODO: review here!, if (hostLine.CanDoMoreLeftOffset(newAnonBlock.InnerContentWidth, limitLocalRight)) { hostLine.DoLeftOffset(newAnonBlock.InnerContentWidth); cx = hostLine.GetRightOfLastRun(); newAnonBlock.SetLocation(floatCtx.lineLeftOffset, floatCtx.offsetFloatTop); //TODO: review top location again floatCtx.lineLeftOffset = newAnonBlock.LocalX + newAnonBlock.InnerContentWidth; } else { //newline newAnonBlock.SetLocation(hostBox.GetClientLeft(), hostLine.CalculateLineHeight()); floatCtx.offsetFloatTop = newAnonBlock.LocalY; } } break; case CssFloat.Right: { //float is out of flow item //1. create new block box and then //flow content in to this new box var newAnonBlock = new CssFloatContainerBox( CssBox.UnsafeGetBoxSpec(b), b.RootGfx, CssDisplay.Block); newAnonBlock.ReEvaluateComputedValues(ifonts, hostBox); //add to abs layer hostBox.AppendToAbsoluteLayer(newAnonBlock); newAnonBlock.ResetLineBoxes(); float localX1 = 0; var line = new CssLineBox(newAnonBlock); newAnonBlock.AddLineBox(line); var newFloatCtx = new FloatFormattingContext(); FlowBoxContentIntoHostLineFmtContext(lay, newAnonBlock, b, limitLocalRight, 0, ref line, ref localX1, ref newFloatCtx); float localY = 0; int interlineSpace = 0; float maxLineWidth = 0; CssTextAlign textAlign = newAnonBlock.CssTextAlign; foreach (CssLineBox linebox in newAnonBlock.GetLineBoxIter()) { ApplyAlignment(linebox, textAlign, lay); linebox.CloseLine(lay); //*** linebox.CachedLineTop = localY; localY += linebox.CacheLineHeight + interlineSpace; if (maxLineWidth < linebox.CachedExactContentWidth) { maxLineWidth = linebox.CachedExactContentWidth; } } SetFinalInnerContentSize(newAnonBlock, maxLineWidth, localY, lay); //todo: review here float hostSizeW = hostBox.VisualWidth; var rightOfLastRun = hostLine.GetRightOfLastRun(); if (!floatCtx.floatingOutOfLine) { if (rightOfLastRun + maxLineWidth < hostSizeW - floatCtx.lineRightOffset) { float newX = hostSizeW - (maxLineWidth + floatCtx.lineRightOffset); newAnonBlock.SetLocation(newX, floatCtx.offsetFloatTop); floatCtx.lineRightOffset = newX; floatCtx.rightFloatBox = newAnonBlock; floatCtx.floatingOutOfLine = true; } else { //start newline float newX = hostSizeW - maxLineWidth; newAnonBlock.SetLocation(newX, floatCtx.offsetFloatTop + hostLine.CalculateLineHeight()); floatCtx.lineRightOffset = newX; floatCtx.rightFloatBox = newAnonBlock; floatCtx.floatingOutOfLine = true; floatCtx.offsetFloatTop = newAnonBlock.LocalY; } } else { //out-of-line mode if (floatCtx.rightFloatBox != null) { float newX = floatCtx.rightFloatBox.LocalX - maxLineWidth; if (newX > 0) { newAnonBlock.SetLocation(newX, floatCtx.offsetFloatTop); floatCtx.lineRightOffset = newX; floatCtx.rightFloatBox = newAnonBlock; floatCtx.offsetFloatTop = newAnonBlock.LocalY; } else { //start new line newX = hostSizeW - maxLineWidth; newAnonBlock.SetLocation(newX, floatCtx.rightFloatBox.LocalY + floatCtx.rightFloatBox.InnerContentHeight); floatCtx.lineRightOffset = newX; floatCtx.rightFloatBox = newAnonBlock; floatCtx.offsetFloatTop = newAnonBlock.LocalY + newAnonBlock.InnerContentHeight; } } else { throw new NotSupportedException(); } } } break; } } else { //go deeper //recursive *** //not new lineFormatting context FlowBoxContentIntoHostLineFmtContext(lay, hostBox, b, limitLocalRight, firstRunStartX, ref hostLine, ref cx, ref floatCtx); } cx += rightMostSpace; childNumber++; //--------------------- cNode = cNode.Next; } } if (srcBox.Position == CssPosition.Relative) { //offset content relative to it 'flow' position' var left = CssValueParser.ConvertToPx(srcBox.Left, hostBox.VisualWidth, srcBox); var top = CssValueParser.ConvertToPx(srcBox.Top, hostBox.VisualWidth, srcBox); srcBox.SetLocation(srcBox.LocalX + left, srcBox.LocalY + top); } }