Bug 41708, support scrolling when hovering over the edge of a scrollframe while dragging, r=smaug

This commit is contained in:
Neil Deakin 2016-12-20 13:39:30 -05:00
parent 87a8f718ea
commit 3bcbbdcf40
4 changed files with 89 additions and 0 deletions

View File

@ -3331,6 +3331,20 @@ EventStateManager::PostHandleEvent(nsPresContext* aPresContext,
{
NS_ASSERTION(aEvent->mClass == eDragEventClass, "Expected a drag event");
// Check if the drag is occurring inside a scrollable area. If so, scroll
// the area when the mouse is near the edges.
if (mCurrentTarget && aEvent->mMessage == eDragOver) {
nsIFrame* checkFrame = mCurrentTarget;
while (checkFrame) {
nsIScrollableFrame* scrollFrame = do_QueryFrame(checkFrame);
// Break out so only the innermost scrollframe is scrolled.
if (scrollFrame && scrollFrame->DragScroll(aEvent)) {
break;
}
checkFrame = checkFrame->GetParent();
}
}
nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
if (!dragSession)
break;

View File

@ -6159,3 +6159,60 @@ ScrollFrameHelper::UsesContainerScrolling() const
}
return false;
}
bool
ScrollFrameHelper::DragScroll(WidgetEvent* aEvent)
{
// Dragging is allowed while within a 20 pixel border. Note that device pixels
// are used so that the same margin is used even when zoomed in or out.
nscoord margin = 20 * mOuter->PresContext()->AppUnitsPerDevPixel();
// Don't drag scroll for small scrollareas.
if (mScrollPort.width < margin * 2 || mScrollPort.height < margin * 2) {
return false;
}
// If willScroll is computed as false, then the frame is already scrolled as
// far as it can go in both directions. Return false so that an ancestor
// scrollframe can scroll instead.
bool willScroll = false;
nsPoint pnt = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, mOuter);
nsPoint scrollPoint = GetScrollPosition();
nsRect rangeRect = GetScrollRangeForClamping();
// Only drag scroll when a scrollbar is present.
nsPoint offset;
if (mHasHorizontalScrollbar) {
if (pnt.x >= mScrollPort.x && pnt.x <= mScrollPort.x + margin) {
offset.x = -margin;
if (scrollPoint.x > 0) {
willScroll = true;
}
} else if (pnt.x >= mScrollPort.XMost() - margin && pnt.x <= mScrollPort.XMost()) {
offset.x = margin;
if (scrollPoint.x < rangeRect.width) {
willScroll = true;
}
}
}
if (mHasVerticalScrollbar) {
if (pnt.y >= mScrollPort.y && pnt.y <= mScrollPort.y + margin) {
offset.y = -margin;
if (scrollPoint.y > 0) {
willScroll = true;
}
} else if (pnt.y >= mScrollPort.YMost() - margin && pnt.y <= mScrollPort.YMost()) {
offset.y = margin;
if (scrollPoint.y < rangeRect.height) {
willScroll = true;
}
}
}
if (offset.x || offset.y) {
ScrollTo(GetScrollPosition() + offset, nsIScrollableFrame::NORMAL);
}
return willScroll;
}

View File

@ -460,6 +460,8 @@ public:
return mSuppressScrollbarRepaints;
}
bool DragScroll(WidgetEvent* aEvent);
// owning references to the nsIAnonymousContentCreator-built content
nsCOMPtr<nsIContent> mHScrollbarContent;
nsCOMPtr<nsIContent> mVScrollbarContent;
@ -1033,6 +1035,10 @@ public:
return mHelper.GetScrollSnapInfo();
}
virtual bool DragScroll(mozilla::WidgetEvent* aEvent) override {
return mHelper.DragScroll(aEvent);
}
#ifdef DEBUG_FRAME_DUMP
virtual nsresult GetFrameName(nsAString& aResult) const override;
#endif
@ -1456,6 +1462,10 @@ public:
return mHelper.GetScrollSnapInfo();
}
virtual bool DragScroll(mozilla::WidgetEvent* aEvent) override {
return mHelper.DragScroll(aEvent);
}
#ifdef DEBUG_FRAME_DUMP
virtual nsresult GetFrameName(nsAString& aResult) const override;
#endif

View File

@ -475,6 +475,14 @@ public:
virtual ScrollSnapInfo GetScrollSnapInfo() const = 0;
virtual void SetScrollsClipOnUnscrolledOutOfFlow() = 0;
/**
* Given the drag event aEvent, determine whether the mouse is near the edge
* of the scrollable area, and scroll the view in the direction of that edge
* if so. If scrolling occurred, true is returned. When false is returned, the
* caller should look for an ancestor to scroll.
*/
virtual bool DragScroll(mozilla::WidgetEvent* aEvent) = 0;
};
#endif