static IEnumerable <CssLineBox> GetLineWalkDownIter(LineWalkVisitor visitor, CssBox box) { //recursive float y = visitor._globalY; if (box.LineBoxCount > 0) { foreach (CssLineBox linebox in box.GetLineBoxIter()) { visitor._globalY = y + linebox.CachedLineTop; yield return(linebox); } } else { //element based foreach (CssBox childbox in box.GetChildBoxIter()) { visitor._globalY = y + childbox.LocalY; //recursive foreach (var linebox in GetLineWalkDownIter(visitor, childbox)) { yield return(linebox); } } } visitor._globalY = y; }
internal static CssLineBox GetNearestLine(CssBox a, Point point, out bool found) { if (a.LineBoxCount > 0) { CssLineBox latestLine = null; int y = point.Y; found = false; foreach (CssLineBox linebox in a.GetLineBoxIter()) { if (linebox.CachedLineBottom < y) { latestLine = linebox; } else { if (latestLine != null) { found = true; } break; } } return(latestLine); } else { bool foundExact = false; CssLineBox lastLine = null; foreach (CssBox cbox in a.GetChildBoxIter()) { CssLineBox candidateLine = GetNearestLine(cbox, point, out foundExact); if (candidateLine != null) { if (foundExact) { found = true; return(lastLine); } //not exact lastLine = candidateLine; } else { if (lastLine != null) { found = false; return(lastLine); } } } found = foundExact; } return(null); }
internal static IEnumerable <CssLineBox> GetDeepDownLineBoxIter(CssBox box) { if (box.LineBoxCount > 0) { foreach (CssLineBox linebox in box.GetLineBoxIter()) { yield return(linebox); } } else { if (box.ChildCount > 0) { foreach (CssBox child in box.GetChildBoxIter()) { foreach (CssLineBox linebox in GetDeepDownLineBoxIter(child)) { yield return(linebox); } } } } }
public static bool HitTest(CssBox box, float x, float y, CssBoxHitChain hitChain) { //-------------------------------------- float boxHitLocalX = x - box.LocalX; float boxHitLocalY = y - box.LocalY; bool isPointInArea = box.IsPointInArea(x, y); //---------------------------------------------------------------------- if (isPointInArea) { hitChain.AddHit(box, (int)boxHitLocalX, (int)boxHitLocalY); } //check absolute layer first *** if (box.HasAbsoluteLayer) { hitChain.PushContextBox(box); foreach (var absBox in box.GetAbsoluteChildBoxBackwardIter()) { if (HitTest(absBox, boxHitLocalX, boxHitLocalY, hitChain)) { //found hit hitChain.PopContextBox(box); return(true); } } hitChain.PopContextBox(box); } //---------------------------------------------------------------------- if (!isPointInArea) { switch (box.CssDisplay) { case Css.CssDisplay.TableRow: { foreach (var childBox in box.GetChildBoxIter()) { if (HitTest(childBox, x, y, hitChain)) { return(true); } } } break; } //exit return(false); } //---------------------------------------------------------------------- //at here point is in the area*** hitChain.PushContextBox(box); if (box.IsCustomCssBox) { //custom css box //return true= stop here if (box.CustomContentHitTest(x, y, hitChain)) { hitChain.PopContextBox(box); return(true); } } if (box.LineBoxCount > 0) { bool foundSomeLine = false; foreach (var lineBox in box.GetLineBoxIter()) { //line box not overlap if (lineBox.HitTest(boxHitLocalX, boxHitLocalY)) { foundSomeLine = true; float lineBoxLocalY = boxHitLocalY - lineBox.CachedLineTop; //2. hitChain.AddHit(lineBox, (int)boxHitLocalX, (int)lineBoxLocalY); var foundRun = BoxHitUtils.GetCssRunOnLocation(lineBox, (int)boxHitLocalX, (int)lineBoxLocalY); if (foundRun != null) { //3. hitChain.AddHit(foundRun, (int)(boxHitLocalX - foundRun.Left), (int)lineBoxLocalY); //4. go deeper for block run if (foundRun.Kind == CssRunKind.BlockRun) { var blockRun = (CssBlockRun)foundRun; CssLineBox hostLine = blockRun.HostLine; //adjust with hostline HitTest(((CssBlockRun)foundRun).ContentBox, (int)(boxHitLocalX - foundRun.Left), boxHitLocalY - hostLine.CachedLineTop, hitChain); } } //found line box hitChain.PopContextBox(box); return(true); } else if (foundSomeLine) { return(false); } } } else { //iterate in child foreach (var childBox in box.GetChildBoxIter()) { if (HitTest(childBox, boxHitLocalX, boxHitLocalY, hitChain)) { //recursive hitChain.PopContextBox(box); return(true); } } } hitChain.PopContextBox(box); return(true); }
public static bool HitTest(CssBox box, float x, float y, CssBoxHitChain hitChain) { //-------------------------------------- float boxHitLocalX = x - box.LocalX; float boxHitLocalY = y - box.LocalY; bool isPointInArea = box.IsPointInArea(x, y); //---------------------------------------------------------------------- if (isPointInArea) { hitChain.AddHit(box, (int)boxHitLocalX, (int)boxHitLocalY); } //check absolute layer first *** if (box.HasAbsoluteLayer) { hitChain.PushContextBox(box); foreach (var absBox in box.GetAbsoluteChildBoxBackwardIter()) { if (HitTest(absBox, boxHitLocalX, boxHitLocalY, hitChain)) { //found hit hitChain.PopContextBox(box); return true; } } hitChain.PopContextBox(box); } //---------------------------------------------------------------------- if (!isPointInArea) { switch (box.CssDisplay) { case Css.CssDisplay.TableRow: { foreach (var childBox in box.GetChildBoxIter()) { if (HitTest(childBox, x, y, hitChain)) { return true; } } } break; } //exit return false; } //---------------------------------------------------------------------- //at here point is in the area*** hitChain.PushContextBox(box); if (box.IsCustomCssBox) { //custom css box //return true= stop here if (box.CustomContentHitTest(x, y, hitChain)) { hitChain.PopContextBox(box); return true; } } if (box.LineBoxCount > 0) { bool foundSomeLine = false; foreach (var lineBox in box.GetLineBoxIter()) { //line box not overlap if (lineBox.HitTest(boxHitLocalX, boxHitLocalY)) { foundSomeLine = true; float lineBoxLocalY = boxHitLocalY - lineBox.CachedLineTop; //2. hitChain.AddHit(lineBox, (int)boxHitLocalX, (int)lineBoxLocalY); var foundRun = BoxHitUtils.GetCssRunOnLocation(lineBox, (int)boxHitLocalX, (int)lineBoxLocalY); if (foundRun != null) { //3. hitChain.AddHit(foundRun, (int)(boxHitLocalX - foundRun.Left), (int)lineBoxLocalY); //4. go deeper for block run if (foundRun.Kind == CssRunKind.BlockRun) { var blockRun = (CssBlockRun)foundRun; CssLineBox hostLine = blockRun.HostLine; //adjust with hostline HitTest(((CssBlockRun)foundRun).ContentBox, (int)(boxHitLocalX - foundRun.Left), boxHitLocalY - hostLine.CachedLineTop, hitChain); } } //found line box hitChain.PopContextBox(box); return true; } else if (foundSomeLine) { return false; } } } else { //iterate in child foreach (var childBox in box.GetChildBoxIter()) { if (HitTest(childBox, boxHitLocalX, boxHitLocalY, hitChain)) { //recursive hitChain.PopContextBox(box); return true; } } } hitChain.PopContextBox(box); return true; }
internal static CssLineBox GetNearestLine(CssBox a, Point point, out bool found) { if (a.LineBoxCount > 0) { CssLineBox latestLine = null; int y = point.Y; found = false; foreach (CssLineBox linebox in a.GetLineBoxIter()) { if (linebox.CachedLineBottom < y) { latestLine = linebox; } else { if (latestLine != null) { found = true; } break; } } return latestLine; } else { bool foundExact = false; CssLineBox lastLine = null; foreach (CssBox cbox in a.GetChildBoxIter()) { CssLineBox candidateLine = GetNearestLine(cbox, point, out foundExact); if (candidateLine != null) { if (foundExact) { found = true; return lastLine; } //not exact lastLine = candidateLine; } else { if (lastLine != null) { found = false; return lastLine; } } } found = foundExact; } return null; }
internal static IEnumerable<CssLineBox> GetDeepDownLineBoxIter(CssBox box) { if (box.LineBoxCount > 0) { foreach (CssLineBox linebox in box.GetLineBoxIter()) { yield return linebox; } } else { if (box.ChildCount > 0) { foreach (CssBox child in box.GetChildBoxIter()) { foreach (CssLineBox linebox in GetDeepDownLineBoxIter(child)) { yield return linebox; } } } } }
static IEnumerable<CssLineBox> GetLineWalkDownIter(LineWalkVisitor visitor, CssBox box) { //recursive float y = visitor.globalY; if (box.LineBoxCount > 0) { foreach (var linebox in box.GetLineBoxIter()) { visitor.globalY = y + linebox.CachedLineTop; yield return linebox; } } else { //element based foreach (var childbox in box.GetChildBoxIter()) { visitor.globalY = y + childbox.LocalY; //recursive foreach (var linebox in GetLineWalkDownIter(visitor, childbox)) { yield return linebox; } } } visitor.globalY = y; }
static void ForEachCellInTable(CssBox table, Action<CssBox> cellAction) { foreach (var c1 in table.GetChildBoxIter()) { foreach (var c2 in c1.GetChildBoxIter()) { if (c2.CssDisplay == CssDisplay.TableCell) { cellAction(c2); } else { foreach (var c3 in c2.GetChildBoxIter()) { cellAction(c3); } } } } }
/// <summary> /// Applies special vertical alignment for table-cells /// </summary> /// <param name="g"></param> /// <param name="cell"></param> static void ApplyCellVerticalAlignment(CssBox cell, float tableBoxOffset) { float dist = 0f; switch (cell.VerticalAlign) { case CssVerticalAlign.Bottom: dist = cell.GetClientHeight() - cell.CalculateInnerContentHeight(); break; case CssVerticalAlign.Middle: dist = (cell.GetClientHeight() - cell.CalculateInnerContentHeight()) / 2; break; default: return; } if (dist > CssBoxConstConfig.TABLE_VERT_OFFSET_THESHOLD) { //more than our threshold if (cell.LineBoxCount > 0) { var linebox = cell.GetFirstLineBox(); while (linebox != null) { linebox.OffsetTop(dist); linebox = linebox.NextLine; } } else { foreach (CssBox b in cell.GetChildBoxIter()) { b.OffsetLocalTop(dist); } } } }
/// <summary> /// Recursively measures run inside the box /// </summary> /// <param name="box">the box to measure</param> /// <param name="g">Device to use</param> static void S1_RecursiveMeasureRunContentSize(CssBox box, LayoutVisitor lay) { //recursive if (box != null) { float box_fontsize = box.ResolvedFont.SizeInPixels; var ifonts = lay.SampleIFonts; foreach (var childBox in box.GetChildBoxIter()) { childBox.ReEvaluateFont(ifonts, box_fontsize); childBox.MeasureRunsSize(lay); S1_RecursiveMeasureRunContentSize(childBox, lay); //recursive } } }
/// <summary> /// Get the table cells spacing for all the cells in the table.<br/> /// Used to calculate the spacing the table has in addition to regular padding and borders. /// </summary> /// <param name="tableBox">the table box to calculate the spacing for</param> /// <returns>the calculated spacing</returns> static float CalculateTableSpacing(CssBox tableBox) { int count = 0; int columns = 0; foreach (var box in tableBox.GetChildBoxIter()) { switch (box.CssDisplay) { case CssDisplay.TableColumn: { columns += box.ColSpan; } break; case CssDisplay.TableRowGroup: { foreach (CssBox cr in tableBox.GetChildBoxIter()) { count++; if (cr.CssDisplay == CssDisplay.TableRow) { columns = Math.Max(columns, cr.ChildCount); } } } break; case CssDisplay.TableRow: { count++; columns = Math.Max(columns, box.ChildCount); } break; } // limit the amount of rows to process for performance ? if (count > MAX_COL_AT_THIS_VERSION) { break; } } // +1 columns because padding is between the cell and table borders return (columns + 1) * GetHorizontalSpacing(tableBox); }
/// <summary> /// Get the <paramref name="min"/> and <paramref name="maxSum"/> of the box words content and <paramref name="paddingSum"/>.<br/> /// </summary> /// <param name="box">the box to calculate for</param> /// <param name="min">the width that allows for each word to fit (width of the longest word)</param> /// <param name="maxSum">the max width a single line of words can take without wrapping</param> /// <param name="paddingSum">the total amount of padding the content has </param> /// <param name="marginSum"></param> /// <returns></returns> static void CalculateMinMaxSumWords( CssBox box, CssBox cbBox, IFonts iFonts, ref float min, ref float maxSum, ref float paddingSum, ref float marginSum) { //recursive float? oldSum = null; // not inline (block) boxes start a new line so we need to reset the max sum if (box.CssDisplay != CssDisplay.Inline && box.CssDisplay != CssDisplay.TableCell && box.WhiteSpace != CssWhiteSpace.NoWrap) { oldSum = maxSum; maxSum = marginSum; } // add the padding paddingSum += box.ActualBorderLeftWidth + box.ActualBorderRightWidth + box.ActualPaddingRight + box.ActualPaddingLeft; // for tables the padding also contains the spacing between cells if (box.CssDisplay == CssDisplay.Table) { paddingSum += CssTableLayoutEngine.CalculateTableSpacing(box); } if (box.HasOnlyRuns) { // calculate the min and max sum for all the words in the box foreach (CssRun run in box.GetRunIter()) { maxSum += run.Width; min = Math.Max(min, run.Width); } } else { // recursively on all the child boxes //if this box has containing property if (box.HasContainingBlockProperty) { foreach (CssBox childBox in box.GetChildBoxIter()) { if (childBox.NeedComputedValueEvaluation) { childBox.ReEvaluateComputedValues(iFonts, box); } float msum = childBox.ActualMarginLeft + childBox.ActualMarginRight; marginSum += msum; //recursive CalculateMinMaxSumWords(childBox, box, iFonts, ref min, ref maxSum, ref paddingSum, ref marginSum); marginSum -= msum; } } else { foreach (CssBox childBox in box.GetChildBoxIter()) { if (childBox.NeedComputedValueEvaluation) { childBox.ReEvaluateComputedValues(iFonts, cbBox); } float msum = childBox.ActualMarginLeft + childBox.ActualMarginRight; marginSum += msum; //recursive CalculateMinMaxSumWords(childBox, cbBox, iFonts, ref min, ref maxSum, ref paddingSum, ref marginSum); marginSum -= msum; } } } // max sum is max of all the lines in the box if (oldSum.HasValue) { maxSum = Math.Max(maxSum, oldSum.Value); } }
public static bool HitTest(CssBox box, float x, float y, CssBoxHitChain hitChain) { #if DEBUG //if (box.ViewportY != 0 && hitChain.debugEventPhase == CssBoxHitChain.dbugEventPhase.MouseDown) //{ //} #endif //-------------------------------------- bool isPointInArea = box.IsPointInArea(x, y); float boxHitLocalX = x - box.LocalX; //** float boxHitLocalY = y - box.LocalY; //** //---------------------------------------------------------------------- if (isPointInArea) { hitChain.AddHit(box, (int)boxHitLocalX, (int)boxHitLocalY); } //---------------------------------------------------------------------- //enter children space -> offset with its viewport boxHitLocalX += box.ViewportX; boxHitLocalY += box.ViewportY; //check absolute layer first *** if (box.HasAbsoluteLayer) { hitChain.PushContextBox(box); foreach (CssBox absBox in box.GetAbsoluteChildBoxBackwardIter()) { if (HitTest(absBox, boxHitLocalX, boxHitLocalY, hitChain)) { //found hit hitChain.PopContextBox(box); return(true); } } hitChain.PopContextBox(box); } //---------------------------------------------------------------------- if (!isPointInArea) { switch (box.CssDisplay) { case Css.CssDisplay.TableRow: { foreach (CssBox childBox in box.GetChildBoxIter()) { if (HitTest(childBox, boxHitLocalX, boxHitLocalY, hitChain)) { return(true); } } } break; } //exit return(false); } //---------------------------------------------------------------------- //at here point is in the area*** hitChain.PushContextBox(box); if (box.IsCustomCssBox) { //custom css box //return true= stop here if (box.CustomContentHitTest(boxHitLocalX, boxHitLocalY, hitChain)) { hitChain.PopContextBox(box); return(true); } } if (box.LineBoxCount > 0) { bool foundSomeLine = false; foreach (CssLineBox lineBox in box.GetLineBoxIter()) { //line box not overlap if (lineBox.HitTest(boxHitLocalX, boxHitLocalY)) { foundSomeLine = true; float lineBoxLocalY = boxHitLocalY - lineBox.CachedLineTop; //2. hitChain.AddHit(lineBox, (int)boxHitLocalX, (int)lineBoxLocalY); CssRun foundRun = BoxHitUtils.GetCssRunOnLocation(lineBox, (int)boxHitLocalX, (int)lineBoxLocalY); //CssRun foundRun = BoxHitUtils.GetCssRunOnLocation(lineBox, (int)x, (int)y); if (foundRun != null) { //3. hitChain.AddHit(foundRun, (int)(boxHitLocalX - foundRun.Left), (int)lineBoxLocalY); //4. go deeper for block run if (foundRun.Kind == CssRunKind.BlockRun) { CssBlockRun blockRun = (CssBlockRun)foundRun; CssLineBox hostLine = blockRun.HostLine; //adjust with hostline HitTest(blockRun.ContentBox, (int)(boxHitLocalX + blockRun.ContentBox.LocalX - foundRun.Left), (int)(boxHitLocalY - hostLine.CachedLineTop), hitChain); } } //found line box hitChain.PopContextBox(box); return(true); } else if (foundSomeLine) { return(false); } } } else { //iterate in child //foreach (var childBox in box.GetChildBoxIter()) foreach (CssBox childBox in box.GetChildBoxBackwardIter()) { if (HitTest(childBox, boxHitLocalX, boxHitLocalY, hitChain)) { //recursive hitChain.PopContextBox(box); return(true); } } } hitChain.PopContextBox(box); return(true); }