internal void InvalidateGraphics() { switch (_runKind) { case CssRunKind.BlockRun: { //TODO: review here again CssBlockRun blockRun = (CssBlockRun)this; CssLineBox ownerLine = blockRun.HostLine; Rectangle r = new Rectangle( (int)(this.Left + blockRun.Left), (int)(this.Top + blockRun.Top + ownerLine.CachedLineTop), (int)this.Width, (int)this.Height); CssBox ownerBox = ownerLine.OwnerBox; ownerBox.InvalidateGraphics(r); } break; default: //fine owner { CssLineBox ownerLine = this.HostLine; Rectangle r = new Rectangle( (int)(this.Left), (int)(this.Top + ownerLine.CachedLineTop), (int)this.Width, (int)this.Height); CssBox ownerBox = ownerLine.OwnerBox; ownerBox.InvalidateGraphics(r); } break; } }
public LineWalkVisitor(CssBlockRun startBlockRun) { float endElemX = 0, endElemY = 0; startBlockRun.ContentBox.GetGlobalLocation(out endElemX, out endElemY); this.globalX = endElemX; this.globalY = endElemY; this.startBlockRun = startBlockRun; }
public LineWalkVisitor(CssBlockRun startBlockRun) { PointF p = new PointF(); startBlockRun.ContentBox.GetGlobalLocationRelativeToRoot(ref p); _globalX = p.X; _globalY = p.Y; _startBlockRun = startBlockRun; //startBlockRun.ContentBox.GetGlobalLocation(out float endElemX, out float endElemY); //_globalX = endElemX; //_globalY = endElemY; //_startBlockRun = startBlockRun; }
internal void PaintRuns(PaintVisitor p) { List <CssRun> tmpRuns = _runs; int j = tmpRuns.Count; if (j < 1) { return; } //----------------------- //iterate from each words CssBox latestOwner = null; DrawBoard innerCanvas = p.InnerDrawBoard; RequestFont enterFont = innerCanvas.CurrentFont; Color enterColor = innerCanvas.CurrentTextColor; for (int i = 0; i < j; ++i) { //----------------- #if DEBUG dbugCounter.dbugRunPaintCount++; #endif //----------------- CssRun w = tmpRuns[i]; switch (w.Kind) { case CssRunKind.SolidContent: { w.OwnerBox.Paint(p, new RectangleF(w.Left, w.Top, w.Width, w.Height)); } break; case CssRunKind.BlockRun: { //Console.WriteLine("blockrun"); CssBlockRun blockRun = (CssBlockRun)w; int ox = p.CanvasOriginX; int oy = p.CanvasOriginY; //p.SetCanvasOrigin(ox + (int)(blockRun.Left + blockRun.ContentBox.LocalX), oy + (int)blockRun.Top); p.SetCanvasOrigin(ox + (int)(blockRun.Left), oy + (int)blockRun.Top); blockRun.ContentBox.Paint(p); p.SetCanvasOrigin(ox, oy); } break; case CssRunKind.Text: { if (latestOwner != w.OwnerBox) { //change latestOwner = w.OwnerBox; //change font when change owner p.InnerDrawBoard.CurrentFont = latestOwner.ResolvedFont; p.InnerDrawBoard.CurrentTextColor = latestOwner.ActualColor; } CssTextRun textRun = (CssTextRun)w; p.DrawText(CssBox.UnsafeGetTextBuffer(w.OwnerBox), textRun.TextStartIndex, textRun.TextLength, new PointF(w.Left, w.Top), new SizeF(w.Width, w.Height)); } break; default: { #if DEBUG // w.OwnerBox.dbugPaintTextWordArea(g, offset, w); #endif } break; } } innerCanvas.CurrentFont = enterFont; innerCanvas.CurrentTextColor = enterColor; }
void SetupEndHitPoint(CssBoxHitChain startChain, CssBoxHitChain endChain, ITextService textService) { //find global location of end point HitInfo endHit = endChain.GetLastHit(); int xposOnEndLine = 0; CssLineBox endline = null; //find endline first _endHitRunCharIndex = 0; _endHitRun = null; switch (endHit.hitObjectKind) { default: { throw new NotSupportedException(); } case HitObjectKind.Run: { CssRun endRun = (CssRun)endHit.hitObject; #if DEBUG //if (endRun.Text != null && endRun.Text.Contains("Jose")) //{ //} if (endHit.localX > 23) { } System.Diagnostics.Debug.WriteLine(endHit.localX); #endif endRun.FindSelectionPoint(textService, endHit.localX, out int run_sel_index, out int run_sel_offset); endline = endRun.HostLine; xposOnEndLine = (int)(endRun.Left + run_sel_offset); _endHitRunCharIndex = run_sel_index; #if DEBUG System.Diagnostics.Debug.WriteLine(_endHitRunCharIndex); #endif _endHitRun = endRun; } break; case HitObjectKind.LineBox: { endline = (CssLineBox)endHit.hitObject; xposOnEndLine = endHit.localX; } break; case HitObjectKind.CssBox: { CssBox hitBox = (CssBox)endHit.hitObject; endline = FindNearestLine(hitBox, endChain.RootGlobalY, 5); xposOnEndLine = endHit.localX; } break; } #if DEBUG if (xposOnEndLine == 0) { } #endif //---------------------------------- _selectedLines = new List <CssLineBox>(); if (_startHitHostLine == endline) { _selectedLines.Add(endline); _startHitHostLine.Select(_startLineBeginSelectionAtPixel, xposOnEndLine, _startHitRun, _startHitRunCharIndex, _endHitRun, _endHitRunCharIndex); return; //early exit here *** } //---------------------------------- //select on different line LineWalkVisitor lineWalkVisitor = null; if (FindCommonGround(startChain, endChain, out int breakAtLevel) && breakAtLevel > 0) { CssBlockRun hitBlockRun = endChain.GetHitInfo(breakAtLevel).hitObject as CssBlockRun; //multiple select //1. first part if (hitBlockRun != null) { _startHitHostLine.Select(_startLineBeginSelectionAtPixel, (int)hitBlockRun.Left, _startHitRun, _startHitRunCharIndex, _endHitRun, _endHitRunCharIndex); _selectedLines.Add(_startHitHostLine); lineWalkVisitor = new LineWalkVisitor(hitBlockRun); } else { _startHitHostLine.SelectPartialToEnd(_startLineBeginSelectionAtPixel, _startHitRun, _startHitRunCharIndex); _selectedLines.Add(_startHitHostLine); lineWalkVisitor = new LineWalkVisitor(_startHitHostLine); } } else { _startHitHostLine.SelectPartialToEnd(_startLineBeginSelectionAtPixel, _startHitRun, _startHitRunCharIndex); _selectedLines.Add(_startHitHostLine); lineWalkVisitor = new LineWalkVisitor(_startHitHostLine); } lineWalkVisitor.SetWalkTargetPosition(endChain.RootGlobalX, endChain.RootGlobalY); #if DEBUG int dbugExpectedId = 1; #endif lineWalkVisitor.Walk(endline, (lineCoverage, linebox, partialLineRun) => { #if DEBUG //System.Diagnostics.Debug.WriteLine("sel:" + linebox.dbugId); if (dbugExpectedId != linebox.dbugId) { } dbugExpectedId++; #endif switch (lineCoverage) { case LineCoverage.EndLine: { //found end line linebox.SelectPartialFromStart(xposOnEndLine, _endHitRun, _endHitRunCharIndex); _selectedLines.Add(linebox); } break; case LineCoverage.PartialLine: { linebox.SelectPartialFromStart((int)partialLineRun.Right, _endHitRun, _endHitRunCharIndex); _selectedLines.Add(linebox); } break; case LineCoverage.FullLine: { //check if hitpoint is in the line area linebox.SelectFull(); _selectedLines.Add(linebox); } break; } }); }
internal void PaintRuns(PaintVisitor p) { List <CssRun> tmpRuns = _runs; int j = tmpRuns.Count; if (j < 1) { return; } //----------------------- //iterate from each words CssBox latestOwner = null; DrawBoard innerCanvas = p.InnerDrawBoard; RequestFont enterFont = innerCanvas.CurrentFont; Color enterColor = innerCanvas.CurrentTextColor; for (int i = 0; i < j; ++i) { //----------------- #if DEBUG dbugCounter.dbugRunPaintCount++; #endif //----------------- CssRun w = tmpRuns[i]; switch (w.Kind) { case CssRunKind.SolidContent: { #if DEBUG //System.Diagnostics.Debug.WriteLine("ox,oy=" + p.CanvasOriginX + "," + p.CanvasOriginY); //System.Diagnostics.Debug.WriteLine("clip=" + p.CurrentClipRect); #endif Rectangle currentClipRect = p.CurrentClipRect; Rectangle wRect = new Rectangle((int)w.Left, (int)w.Top, (int)w.Width, (int)w.Height); wRect.Intersect(currentClipRect); #if DEBUG //System.Diagnostics.Debug.WriteLine("empty_clip=" + (wRect.Height == 0 || wRect.Width == 0)); #endif if (wRect.Height != 0 && wRect.Width != 0) { w.OwnerBox.Paint(p, new RectangleF(w.Left, w.Top, w.Width, w.Height)); } } break; case CssRunKind.BlockRun: { //Console.WriteLine("blockrun"); CssBlockRun blockRun = (CssBlockRun)w; int ox = p.CanvasOriginX; int oy = p.CanvasOriginY; //p.SetCanvasOrigin(ox + (int)(blockRun.Left + blockRun.ContentBox.LocalX), oy + (int)blockRun.Top); p.SetCanvasOrigin(ox + (int)(blockRun.Left), oy + (int)blockRun.Top); blockRun.ContentBox.Paint(p); p.SetCanvasOrigin(ox, oy); } break; case CssRunKind.Text: { if (latestOwner != w.OwnerBox) { //change latestOwner = w.OwnerBox; //change font when change owner p.InnerDrawBoard.CurrentFont = latestOwner.ResolvedFont; p.InnerDrawBoard.CurrentTextColor = latestOwner.ActualColor; } CssTextRun textRun = (CssTextRun)w; p.DrawText(CssBox.UnsafeGetTextBuffer(w.OwnerBox), textRun.TextStartIndex, textRun.TextLength, new PointF(w.Left, w.Top), new SizeF(w.Width, w.Height)); //RenderVxFormattedString formattedStr = CssTextRun.GetCachedFormatString(textRun); //if (formattedStr == null) //{ // formattedStr = p.CreateRenderVx(CssBox.UnsafeGetTextBuffer(w.OwnerBox), // textRun.TextStartIndex, // textRun.TextLength); // CssTextRun.SetCachedFormattedString(textRun, formattedStr); //} //if (formattedStr != null) //{ // p.DrawText(formattedStr, // new PointF(w.Left, w.Top), // new SizeF(w.Width, w.Height)); //} //else //{ // p.DrawText(CssBox.UnsafeGetTextBuffer(w.OwnerBox), // textRun.TextStartIndex, // textRun.TextLength, // new PointF(w.Left, w.Top), // new SizeF(w.Width, w.Height)); //} } break; default: { #if DEBUG // w.OwnerBox.dbugPaintTextWordArea(g, offset, w); #endif } break; } } innerCanvas.CurrentFont = enterFont; innerCanvas.CurrentTextColor = enterColor; }
/// <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); } }
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); }