Example #1
0
        static CssLineBox FindNearestLine(CssBox startBox, int globalY, int yRange)
        {
            CssLineBox latestLine              = null;
            CssBox     latestLineBoxOwner      = null;
            float      latestLineBoxGlobalYPos = 0;

            foreach (CssLineBox lineBox in BoxHitUtils.GetDeepDownLineBoxIter(startBox))
            {
                if (lineBox.CacheLineHeight == 0)
                {
                    continue;
                }
                if (latestLineBoxOwner != lineBox.OwnerBox)
                {
                    //find global position of box
                    latestLineBoxOwner = lineBox.OwnerBox;
                    //TODO: review here , duplicate GetGlobalLocation
                    float gx, gy;
                    latestLineBoxOwner.GetGlobalLocation(out gx, out gy);
                    latestLineBoxGlobalYPos = gy;
                }

                float lineGlobalBottom = lineBox.CachedLineBottom + latestLineBoxGlobalYPos;
                if (lineGlobalBottom <= globalY)
                {
                    latestLine = lineBox;
                }
                else
                {
                    latestLine = lineBox;
                    break;
                }
            }
            return(latestLine);
        }
        public void MouseDown(UIMouseEventArgs e, CssBox startAt)
        {
            if (!_isBinded)
            {
                return;
            }
            if (startAt == null)
            {
                return;
            }
            //----------------------------------------------------
            ClearPreviousSelection();
            if (_latestMouseDownChain != null)
            {
                ReleaseHitChain(_latestMouseDownChain);
                _latestMouseDownChain = null;
            }
            this.lastDomLayoutVersion = this._htmlContainer.LayoutVersion;
            //----------------------------------------------------
            int x = e.X;
            int y = e.Y;

            this._mouseDownStartAt = startAt;
            this._mousedownX       = x;
            this._mousedownY       = y;
            CssBoxHitChain hitChain = GetFreeHitChain();

            hitChain.SetRootGlobalPosition(x, y);
            //1. hittest
            BoxHitUtils.HitTest(startAt, x, y, hitChain);
            //2. propagate events
            SetEventOrigin(e, hitChain);
            ForEachOnlyEventPortalBubbleUp(e, hitChain, (portal) =>
            {
                portal.PortalMouseDown(e);
                return(true);
            });
            if (!e.CancelBubbling)
            {
                var prevMouseDownElement = this.currentMouseDown;
                e.CurrentContextElement = this.currentMouseDown = null; //clear
                ForEachEventListenerBubbleUp(e, hitChain, () =>
                {
                    //TODO: check accept keyboard
                    this.currentMouseDown = e.CurrentContextElement;
                    e.CurrentContextElement.ListenMouseDown(e);
                    if (prevMouseDownElement != null &&
                        prevMouseDownElement != currentMouseDown)
                    {
                        prevMouseDownElement.ListenLostMouseFocus(e);
                    }

                    return(e.CancelBubbling);
                });
            }
            //----------------------------------
            //save mousedown hitchain
            this._latestMouseDownChain = hitChain;
        }
Example #3
0
            ///// walk down and up
            ///// </summary>
            ///// <param name="startLine"></param>
            ///// <returns></returns>
            //static IEnumerable<CssLineBox> GetLineWalkDownAndUpIter(LineWalkVisitor visitor, CssLineBox startLine)
            //{
            //    float sx, sy;
            //    startLine.OwnerBox.GetGlobalLocation(out sx, out sy);
            //    CssLineBox curLine = startLine;
            //    //walk up and down the tree
            //    CssLineBox nextline = curLine.NextLine;
            //    while (nextline != null)
            //    {
            //        visitor._globalY = sy + startLine.CachedLineTop;
            //        yield return nextline;
            //        nextline = nextline.NextLine;
            //    }
            //    //--------------------
            //    //no next line
            //    //then walk up  ***
            //    CssBox curBox = startLine.OwnerBox;

            //    RETRY://***
            //    CssBox level1Sibling = BoxHitUtils.GetNextSibling(curBox);
            //    while (level1Sibling != null)
            //    {
            //        level1Sibling.GetGlobalLocation(out sx, out sy);
            //        visitor._globalY = sy;
            //        //walk down
            //        foreach (CssLineBox line in GetLineWalkDownIter(visitor, level1Sibling))
            //        {
            //            yield return line;
            //        }

            //        level1Sibling = BoxHitUtils.GetNextSibling(level1Sibling);
            //    }
            //    //--------------------
            //    //other further sibling
            //    //then step to parent of lineOwner
            //    if (curBox.ParentBox != null)
            //    {
            //        //if has parent
            //        //walk up***
            //        curBox = curBox.ParentBox;
            //        goto RETRY;
            //    }
            //}



            /// walk down and up
            /// </summary>
            /// <param name="startLine"></param>
            /// <returns></returns>
            static IEnumerable <CssLineBox> GetLineWalkDownAndUpIter(LineWalkVisitor visitor, CssLineBox startLine)
            {
                PointF p = new PointF();

                startLine.OwnerBox.GetGlobalLocationRelativeToRoot(ref p);
                CssLineBox curLine = startLine;
                //walk up and down the tree
                CssLineBox nextline = curLine.NextLine;

                while (nextline != null)
                {
                    visitor._globalY = p.Y + startLine.CachedLineTop;
                    yield return(nextline);

                    nextline = nextline.NextLine;
                }
                //--------------------
                //no next line
                //then walk up  ***
                CssBox curBox = startLine.OwnerBox;

RETRY:          //***
                CssBox level1Sibling = BoxHitUtils.GetNextSibling(curBox);

                while (level1Sibling != null)
                {
                    p = new PointF();
                    level1Sibling.GetGlobalLocationRelativeToRoot(ref p);
                    visitor._globalY = p.Y;
                    //walk down
                    foreach (CssLineBox line in GetLineWalkDownIter(visitor, level1Sibling))
                    {
                        yield return(line);
                    }

                    level1Sibling = BoxHitUtils.GetNextSibling(level1Sibling);
                }
                //--------------------
                //other further sibling
                //then step to parent of lineOwner
                if (curBox.ParentBox != null)
                {
                    //if has parent
                    //walk up***
                    curBox = curBox.ParentBox;
                    goto RETRY;
                }
            }
Example #4
0
            /// walk down and up
            /// </summary>
            /// <param name="startLine"></param>
            /// <returns></returns>
            static IEnumerable <CssLineBox> GetLineWalkDownAndUpIter(LineWalkVisitor visitor, CssLineBox startLine)
            {
                float sx, sy;

                startLine.OwnerBox.GetGlobalLocation(out sx, out sy);
                CssLineBox curLine = startLine;
                //walk up and down the tree
                CssLineBox nextline = curLine.NextLine;

                while (nextline != null)
                {
                    visitor.globalY = sy + startLine.CachedLineTop;
                    yield return(nextline);

                    nextline = nextline.NextLine;
                }
                //--------------------
                //no next line
                //then walk up  ***
                CssBox curBox = startLine.OwnerBox;

RETRY:
                CssBox level1Sibling = BoxHitUtils.GetNextSibling(curBox);

                while (level1Sibling != null)
                {
                    level1Sibling.GetGlobalLocation(out sx, out sy);
                    visitor.globalY = sy;

                    //walk down
                    foreach (var line in GetLineWalkDownIter(visitor, level1Sibling))
                    {
                        yield return(line);
                    }

                    level1Sibling = BoxHitUtils.GetNextSibling(level1Sibling);
                }
                //--------------------
                //other further sibling
                //then step to parent of lineOwner
                if (curBox.ParentBox != null)
                {
                    //if has parent
                    //walk up***
                    curBox = curBox.ParentBox;
                    goto RETRY;
                }
            }
        public void MouseUp(UIMouseEventArgs e, CssBox startAt)
        {
            if (!_isBinded)
            {
                return;
            }
            if (startAt == null)
            {
                return;
            }
            //----------------------------------------------------

            DateTime snapMouseUpTime   = DateTime.Now;
            TimeSpan timediff          = snapMouseUpTime - lastimeMouseUp;
            bool     isAlsoDoubleClick = timediff.Milliseconds < DOUBLE_CLICK_SENSE;

            this.lastimeMouseUp = snapMouseUpTime;
            //-----------------------------------------
            CssBoxHitChain hitChain = GetFreeHitChain();

            hitChain.SetRootGlobalPosition(e.X, e.Y);
            //1. prob hit chain only
            BoxHitUtils.HitTest(startAt, e.X, e.Y, hitChain);
            SetEventOrigin(e, hitChain);
            //2. invoke css event and script event
            ForEachOnlyEventPortalBubbleUp(e, hitChain, (portal) =>
            {
                portal.PortalMouseUp(e);
                return(true);
            });
            if (!e.CancelBubbling)
            {
                ForEachEventListenerBubbleUp(e, hitChain, () =>
                {
                    e.CurrentContextElement.ListenMouseUp(e);
                    return(e.CancelBubbling);
                });
            }

            if (!e.IsCanceled)
            {
                //--------------------
                //click or double click
                //--------------------
                if (isAlsoDoubleClick)
                {
                    ForEachEventListenerBubbleUp(e, hitChain, () =>
                    {
                        e.CurrentContextElement.ListenMouseDoubleClick(e);
                        return(e.CancelBubbling);
                    });
                }
                else
                {
                    ForEachEventListenerBubbleUp(e, hitChain, () =>
                    {
                        e.CurrentContextElement.ListenMouseClick(e);
                        return(e.CancelBubbling);
                    });
                }
            }

            ReleaseHitChain(hitChain);
            if (this._latestMouseDownChain != null)
            {
                this._latestMouseDownChain.Clear();
                //Console.WriteLine(dbugNN++);
                this._latestMouseDownChain = null;
            }
        }
        public void MouseMove(UIMouseEventArgs e, CssBox startAt)
        {
            if (!_isBinded)
            {
                return;
            }
            if (startAt == null)
            {
                return;
            }
            //-----------------------------------------
            int x = e.X;
            int y = e.Y;

            if (e.IsDragging && _latestMouseDownChain != null)
            {
                //dragging *** , if changed
                if (this._mousedownX != x || this._mousedownY != y)
                {
                    //handle mouse drag
                    CssBoxHitChain hitChain = GetFreeHitChain();
                    hitChain.SetRootGlobalPosition(x, y);
                    BoxHitUtils.HitTest(startAt, x, y, hitChain);
                    SetEventOrigin(e, hitChain);
                    //---------------------------------------------------------
                    //propagate mouse drag
                    ForEachOnlyEventPortalBubbleUp(e, hitChain, (portal) =>
                    {
                        portal.PortalMouseMove(e);
                        return(true);
                    });
                    //---------------------------------------------------------
                    if (!e.CancelBubbling)
                    {
                        ClearPreviousSelection();
                        if (_latestMouseDownChain.Count > 0 && hitChain.Count > 0)
                        {
                            if (this._htmlContainer.LayoutVersion != this.lastDomLayoutVersion)
                            {
                                //the dom has been changed so...
                                //need to evaluate hitchain at mousedown position again
                                int lastRootGlobalX = _latestMouseDownChain.RootGlobalX;
                                int lastRootGlobalY = _latestMouseDownChain.RootGlobalY;
                                _latestMouseDownChain.Clear();
                                _latestMouseDownChain.SetRootGlobalPosition(lastRootGlobalX, lastRootGlobalY);
                                BoxHitUtils.HitTest(_mouseDownStartAt, lastRootGlobalX, lastRootGlobalY, _latestMouseDownChain);
                            }

                            //create selection range
                            var newSelectionRange = new SelectionRange(
                                _latestMouseDownChain,
                                hitChain,
                                this.ifonts);
                            if (newSelectionRange.IsValid)
                            {
                                this._htmlContainer.SetSelection(newSelectionRange);
                            }
                            else
                            {
                                this._htmlContainer.SetSelection(null);
                            }
                        }
                        else
                        {
                            this._htmlContainer.SetSelection(null);
                        }

                        ForEachEventListenerBubbleUp(e, hitChain, () =>
                        {
                            e.CurrentContextElement.ListenMouseMove(e);
                            return(true);
                        });
                    }

                    //---------------------------------------------------------
                    ReleaseHitChain(hitChain);
                }
            }
            else
            {
                //mouse move
                //---------------------------------------------------------
                CssBoxHitChain hitChain = GetFreeHitChain();
                hitChain.SetRootGlobalPosition(x, y);
                BoxHitUtils.HitTest(startAt, x, y, hitChain);
                SetEventOrigin(e, hitChain);
                //---------------------------------------------------------

                ForEachOnlyEventPortalBubbleUp(e, hitChain, (portal) =>
                {
                    portal.PortalMouseMove(e);
                    return(true);
                });
                //---------------------------------------------------------
                if (!e.CancelBubbling)
                {
                    ForEachEventListenerBubbleUp(e, hitChain, () =>
                    {
                        e.CurrentContextElement.ListenMouseMove(e);
                        return(true);
                    });
                }
                ReleaseHitChain(hitChain);
            }
        }
        public void MouseDown(UIMouseDownEventArgs e, CssBox startAt)
        {
            if (!_isBinded)
            {
                return;
            }
            if (startAt == null)
            {
                return;
            }
            //----------------------------------------------------
            if (!e.Shift)
            {
                ClearPreviousSelection();
            }

            if (_latestMouseDownChain != null)
            {
                ReleaseHitChain(_latestMouseDownChain);
                _latestMouseDownChain = null;
            }
            _lastDomLayoutVersion = _htmlVisualRoot.LayoutVersion;
            //----------------------------------------------------
            int x = e.X;
            int y = e.Y;

            _mouseDownStartAt = startAt;
            _mousedownX       = x;
            _mousedownY       = y;
            CssBoxHitChain hitChain = GetFreeHitChain();

#if DEBUG
            hitChain.debugEventPhase = CssBoxHitChain.dbugEventPhase.MouseDown;
#endif
            hitChain.SetRootGlobalPosition(x, y);
            //1. hittest
            BoxHitUtils.HitTest(startAt, x, y, hitChain);
            //2. propagate events
            SetEventOrigin(e, hitChain);
            ForEachOnlyEventPortalBubbleUp(e, hitChain, portal =>
            {
                portal.PortalMouseDown(e);
                return(true);
            });
            if (!e.CancelBubbling)
            {
                IUIEventListener prevMouseDownElement = _currentMouseDown;
                _currentMouseDown = null; //clear
                e.SetCurrentContextElement(null);
                ForEachEventListenerBubbleUp(e, hitChain, () =>
                {
                    //TODO: check accept keyboard
                    _currentMouseDown = e.CurrentContextElement;
                    e.CurrentContextElement.ListenMouseDown(e);
                    if (prevMouseDownElement != null &&
                        prevMouseDownElement != _currentMouseDown)
                    {
                        prevMouseDownElement.ListenLostMouseFocus(_mouseLostFocus);
                    }

                    return(e.CancelBubbling);
                });
            }
            //----------------------------------
            //save mousedown hitchain
            _latestMouseDownChain = hitChain;
        }
        public void MouseMove(UIMouseMoveEventArgs e, CssBox startAt)
        {
            if (!_isBinded)
            {
                return;
            }
            if (startAt == null)
            {
                return;
            }
            //-----------------------------------------
            int x = e.X;
            int y = e.Y;


            if (e.IsDragging && _latestMouseDownChain != null)
            {
                //dragging *** , if changed
                if (_mousedownX != x || _mousedownY != y)
                {
                    //handle mouse drag
                    CssBoxHitChain hitChain = GetFreeHitChain();
#if DEBUG
                    hitChain.debugEventPhase = CssBoxHitChain.dbugEventPhase.MouseMove;
#endif
                    hitChain.SetRootGlobalPosition(x, y);
                    BoxHitUtils.HitTest(startAt, x, y, hitChain);
                    SetEventOrigin(e, hitChain);
                    //---------------------------------------------------------
                    //propagate mouse drag
                    ForEachOnlyEventPortalBubbleUp(e, hitChain, (portal) =>
                    {
                        portal.PortalMouseMove(e);
                        return(true);
                    });
                    //---------------------------------------------------------
                    if (!e.CancelBubbling)
                    {
                        ClearPreviousSelection();
                        if (_latestMouseDownChain.Count > 0 && hitChain.Count > 0)
                        {
                            if (_htmlVisualRoot.LayoutVersion != _lastDomLayoutVersion)
                            {
                                //the dom has been changed so...
                                //need to evaluate hitchain at mousedown position again
                                int lastRootGlobalX = _latestMouseDownChain.RootGlobalX;
                                int lastRootGlobalY = _latestMouseDownChain.RootGlobalY;
                                _latestMouseDownChain.Clear();
                                _latestMouseDownChain.SetRootGlobalPosition(lastRootGlobalX, lastRootGlobalY);
                                BoxHitUtils.HitTest(_mouseDownStartAt, lastRootGlobalX, lastRootGlobalY, _latestMouseDownChain);
                            }

                            //create selection range
                            var newSelectionRange = new SelectionRange(
                                _latestMouseDownChain,
                                hitChain,
                                _textService);
                            if (newSelectionRange.IsValid)
                            {
                                _htmlVisualRoot.SetSelection(newSelectionRange);
                            }
                            else
                            {
                                _htmlVisualRoot.SetSelection(null);
                            }
                        }
                        else
                        {
                            _htmlVisualRoot.SetSelection(null);
                        }

                        ForEachEventListenerBubbleUp(e, hitChain, () =>
                        {
                            e.CurrentContextElement.ListenMouseMove(e);
                            return(true);
                        });
                    }

                    //---------------------------------------------------------
                    ReleaseHitChain(hitChain);
                }
            }
            else
            {
                //mouse move
                //---------------------------------------------------------
                CssBoxHitChain hitChain = GetFreeHitChain();
#if DEBUG
                hitChain.debugEventPhase = CssBoxHitChain.dbugEventPhase.MouseMove;
#endif
                hitChain.SetRootGlobalPosition(x, y);
                BoxHitUtils.HitTest(startAt, x, y, hitChain);
                SetEventOrigin(e, hitChain);
                //---------------------------------------------------------

                ForEachOnlyEventPortalBubbleUp(e, hitChain, (portal) =>
                {
                    portal.PortalMouseMove(e);
                    return(true);
                });
                //---------------------------------------------------------
                if (!e.CancelBubbling)
                {
                    ForEachEventListenerBubbleUp(e, hitChain, () =>
                    {
                        e.CurrentContextElement.ListenMouseMove(e);
                        return(true);
                    });
                }

                var cssbox = e.ExactHitObject as HtmlBoxes.CssBox;
                if (cssbox != null)
                {
                    switch (cssbox.CursorName)
                    {
                    case Css.CssCursorName.IBeam:
                        if (e.MouseCursorStyle != MouseCursorStyle.IBeam)
                        {
                            e.MouseCursorStyle = MouseCursorStyle.IBeam;
                        }
                        break;

                    case Css.CssCursorName.Hand:
                    case Css.CssCursorName.Pointer:
                        if (e.MouseCursorStyle != MouseCursorStyle.Pointer)
                        {
                            e.MouseCursorStyle = MouseCursorStyle.Pointer;
                        }
                        break;

                    case Css.CssCursorName.Default:
                        if (e.MouseCursorStyle != MouseCursorStyle.Default)
                        {
                            e.MouseCursorStyle = MouseCursorStyle.Default;
                        }
                        break;
                    }
                }
                else
                {
                    var cssspan = e.ExactHitObject as HtmlBoxes.CssTextRun;
                    if (cssspan != null)
                    {
                        cssbox = cssspan.OwnerBox;
                        switch (cssbox.CursorName)
                        {
                        default:
                            e.MouseCursorStyle = MouseCursorStyle.IBeam;
                            break;

                        case Css.CssCursorName.Hand:
                        case Css.CssCursorName.Pointer:
                            if (e.MouseCursorStyle != MouseCursorStyle.Pointer)
                            {
                                e.MouseCursorStyle = MouseCursorStyle.Pointer;
                            }
                            break;
                        }
                    }
                }
                ReleaseHitChain(hitChain);
            }
        }
Example #9
0
        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);
        }
Example #10
0
        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);
        }