Update :hover state and mouse cursor and send mouse events when the position of the pointer changes due to scrolling or reflow. b=20022 r+sr=roc

This commit is contained in:
dbaron%dbaron.org 2004-06-22 04:32:52 +00:00
Родитель 3f57a3a635
Коммит 0ec49fbb44
8 изменённых файлов: 192 добавлений и 68 удалений

Просмотреть файл

@ -493,10 +493,22 @@ nsEventStateManager::PreHandleEvent(nsIPresContext* aPresContext,
SetClickCount(aPresContext, (nsMouseEvent*)aEvent, aStatus);
break;
case NS_MOUSE_MOVE:
// on the Mac, GenerateDragGesture() may not return until the drag has completed
// and so |aTargetFrame| may have been deleted (moving a bookmark, for example).
// If this is the case, however, we know that ClearFrameRefs() has been called
// and it cleared out |mCurrentTarget|. As a result, we should pass |mCurrentTarget|
if (IsTrackingDragGesture() &&
NS_STATIC_CAST(nsMouseEvent*, aEvent)->reason ==
nsMouseEvent::eSynthesized) {
// This is not a real mouse move, it's synthesized. Reset the
// starting point to the current point (since things moved), but
// otherwise let the user continue the drag.
StopTrackingDragGesture();
BeginTrackingDragGesture(aPresContext,
NS_STATIC_CAST(nsGUIEvent*, aEvent),
aTargetFrame);
}
// on the Mac, GenerateDragGesture() may not return until the drag
// has completed and so |aTargetFrame| may have been deleted (moving
// a bookmark, for example). If this is the case, however, we know
// that ClearFrameRefs() has been called and it cleared out
// |mCurrentTarget|. As a result, we should pass |mCurrentTarget|
// into UpdateCursor().
GenerateDragGesture(aPresContext, (nsGUIEvent*)aEvent);
UpdateCursor(aPresContext, aEvent, mCurrentTarget, aStatus);
@ -1358,6 +1370,8 @@ nsEventStateManager::BeginTrackingDragGesture(nsIPresContext* aPresContext,
nsGUIEvent* inDownEvent,
nsIFrame* inDownFrame)
{
// Note that |inDownEvent| could be either a mouse down event or a
// synthesized mouse move event.
mIsTrackingDragGesture = PR_TRUE;
mGestureDownPoint = inDownEvent->point;
mGestureDownRefPoint = inDownEvent->refPoint;
@ -1676,11 +1690,6 @@ nsEventStateManager::DoScrollText(nsIPresContext* aPresContext,
nsIScrollableView* sv = nsnull;
nsIFrame* focusFrame = nsnull;
// Create a mouseout event that we fire to the content before
// scrolling, to allow tooltips to disappear, etc.
nsMouseEvent mouseOutEvent(NS_MOUSE_EXIT, aEvent->widget);
nsIPresShell *presShell = aPresContext->PresShell();
// Otherwise, check for a focused content element
@ -1722,8 +1731,6 @@ nsEventStateManager::DoScrollText(nsIPresContext* aPresContext,
PRBool passToParent;
if (sv) {
GenerateMouseEnterExit(aPresContext, &mouseOutEvent);
// If we're already at the scroll limit for this view, scroll the
// parent view instead.
@ -3813,7 +3820,6 @@ NS_IMETHODIMP
nsEventStateManager::GetEventTarget(nsIFrame **aFrame)
{
if (!mCurrentTarget && mCurrentTargetContent) {
nsCOMPtr<nsIPresShell> shell;
if (mPresContext) {
nsIPresShell *shell = mPresContext->GetPresShell();
if (shell) {

Просмотреть файл

@ -4831,6 +4831,8 @@ PresShell::UnsuppressAndInvalidate()
if (focusController) // Unsuppress now that we've shown the new window and focused it.
focusController->SetSuppressFocus(PR_FALSE, "PresShell suppression on Web page loads");
mViewManager->SynthesizeMouseMove(PR_FALSE);
}
NS_IMETHODIMP
@ -5965,6 +5967,13 @@ PresShell::HandleEventWithTarget(nsEvent* aEvent, nsIFrame* aFrame, nsIContent*
return NS_OK;
}
inline PRBool
IsSynthesizedMouseMove(nsEvent* aEvent)
{
return aEvent->eventStructType == NS_MOUSE_EVENT &&
NS_STATIC_CAST(nsMouseEvent*, aEvent)->reason != nsMouseEvent::eReal;
}
nsresult
PresShell::HandleEventInternal(nsEvent* aEvent, nsIView *aView,
PRUint32 aFlags, nsEventStatus* aStatus)
@ -6002,7 +6011,10 @@ PresShell::HandleEventInternal(nsEvent* aEvent, nsIView *aView,
aStatus, aView);
// 2. Give event to the DOM for third party and JS use.
if ((GetCurrentEventFrame()) && NS_SUCCEEDED(rv)) {
if ((GetCurrentEventFrame()) && NS_SUCCEEDED(rv) &&
// We want synthesized mouse moves to cause mouseover and mouseout
// DOM events (PreHandleEvent above), but not mousemove DOM events.
!IsSynthesizedMouseMove(aEvent)) {
if (mCurrentEventContent) {
rv = mCurrentEventContent->HandleDOMEvent(mPresContext, aEvent, nsnull,
aFlags, aStatus);
@ -6252,6 +6264,10 @@ PresShell::DidDoReflow()
HandlePostedDOMEvents();
HandlePostedAttributeChanges();
HandlePostedReflowCallbacks();
// Null-check mViewManager in case this happens during Destroy. See
// bugs 244435 and 238546.
if (!mPaintingSuppressed && mViewManager)
mViewManager->SynthesizeMouseMove(PR_FALSE);
}
nsresult

Просмотреть файл

@ -4831,6 +4831,8 @@ PresShell::UnsuppressAndInvalidate()
if (focusController) // Unsuppress now that we've shown the new window and focused it.
focusController->SetSuppressFocus(PR_FALSE, "PresShell suppression on Web page loads");
mViewManager->SynthesizeMouseMove(PR_FALSE);
}
NS_IMETHODIMP
@ -5965,6 +5967,13 @@ PresShell::HandleEventWithTarget(nsEvent* aEvent, nsIFrame* aFrame, nsIContent*
return NS_OK;
}
inline PRBool
IsSynthesizedMouseMove(nsEvent* aEvent)
{
return aEvent->eventStructType == NS_MOUSE_EVENT &&
NS_STATIC_CAST(nsMouseEvent*, aEvent)->reason != nsMouseEvent::eReal;
}
nsresult
PresShell::HandleEventInternal(nsEvent* aEvent, nsIView *aView,
PRUint32 aFlags, nsEventStatus* aStatus)
@ -6002,7 +6011,10 @@ PresShell::HandleEventInternal(nsEvent* aEvent, nsIView *aView,
aStatus, aView);
// 2. Give event to the DOM for third party and JS use.
if ((GetCurrentEventFrame()) && NS_SUCCEEDED(rv)) {
if ((GetCurrentEventFrame()) && NS_SUCCEEDED(rv) &&
// We want synthesized mouse moves to cause mouseover and mouseout
// DOM events (PreHandleEvent above), but not mousemove DOM events.
!IsSynthesizedMouseMove(aEvent)) {
if (mCurrentEventContent) {
rv = mCurrentEventContent->HandleDOMEvent(mPresContext, aEvent, nsnull,
aFlags, aStatus);
@ -6252,6 +6264,10 @@ PresShell::DidDoReflow()
HandlePostedDOMEvents();
HandlePostedAttributeChanges();
HandlePostedReflowCallbacks();
// Null-check mViewManager in case this happens during Destroy. See
// bugs 244435 and 238546.
if (!mPaintingSuppressed && mViewManager)
mViewManager->SynthesizeMouseMove(PR_FALSE);
}
nsresult

Просмотреть файл

@ -501,6 +501,12 @@ public:
PRUint16 aMinTwips,
nsRectVisibility *aRectVisibility)=0;
/**
* Dispatch a mouse move event based on the most recent mouse
* position. This is used when the contents of the page moved
* (aFromScroll is false) or scrolled (aFromScroll is true).
*/
NS_IMETHOD SynthesizeMouseMove(PRBool aFromScroll)=0;
};
//update view now?

Просмотреть файл

@ -661,6 +661,8 @@ NS_IMETHODIMP nsScrollPortView::ScrollToImpl(nscoord aX, nscoord aY, PRUint32 aU
mOffsetY = aY;
Scroll(scrolledView, dxPx, dyPx, t2p, 0);
mViewManager->SynthesizeMouseMove(PR_TRUE);
// notify the listeners.
if (nsnull != mListeners) {

Просмотреть файл

@ -98,6 +98,8 @@ static NS_DEFINE_CID(kEventQueueServiceCID, NS_EVENTQUEUESERVICE_CID);
#define PUSH_FILTER 0x00000080
#define POP_FILTER 0x00000100
#define NSCOORD_NONE PR_INT32_MIN
#define SUPPORT_TRANSLUCENT_VIEWS
/*
@ -318,43 +320,29 @@ static void PrintZTreeNode(DisplayZTreeNode* aNode, PRInt32 aIndent)
//-------------- Begin Invalidate Event Definition ------------------------
struct nsInvalidateEvent : public PLEvent {
nsInvalidateEvent(nsViewManager* aViewManager);
~nsInvalidateEvent() { }
void HandleEvent() {
NS_ASSERTION(nsnull != mViewManager,"ViewManager is null");
// Search for valid view manager before trying to access it
// This is just a safety check. We should never have a circumstance
// where the view manager has been destroyed and the invalidate event
// which it owns is still around. The invalidate event should be destroyed
// by the RevokeEvent in the viewmanager's destructor.
PRBool found = PR_FALSE;
PRInt32 index;
PRInt32 count = nsViewManager::GetViewManagerCount();
const nsVoidArray* viewManagers = nsViewManager::GetViewManagerArray();
for (index = 0; index < count; index++) {
nsViewManager* vm = (nsViewManager*)viewManagers->ElementAt(index);
if (vm == mViewManager) {
found = PR_TRUE;
}
}
if (found) {
mViewManager->ProcessInvalidateEvent();
} else {
NS_ASSERTION(PR_FALSE, "bad view manager asked to process invalidate event");
}
};
nsViewManager* mViewManager; // Weak Reference. The viewmanager will destroy any pending
// invalidate events in it's destructor.
struct nsViewManagerEvent : public PLEvent {
nsViewManagerEvent(nsViewManager* aViewManager);
virtual void HandleEvent() = 0;
nsViewManager* ViewManager() {
// |owner| is a weak pointer, but the view manager will destroy any
// pending invalidate events in it's destructor.
return NS_STATIC_CAST(nsViewManager*, owner);
}
};
static void* PR_CALLBACK HandlePLEvent(PLEvent* aEvent)
{
NS_ASSERTION(nsnull != aEvent,"Event is null");
nsInvalidateEvent *event = NS_STATIC_CAST(nsInvalidateEvent*, aEvent);
nsViewManagerEvent *event = NS_STATIC_CAST(nsViewManagerEvent*, aEvent);
// Search for valid view manager before trying to access it. This
// is working around a bug in RevokeEvents.
const nsVoidArray *vmArray = nsViewManager::GetViewManagerArray();
NS_ENSURE_TRUE(vmArray && vmArray->IndexOf(event->ViewManager()) != -1,
nsnull);
event->HandleEvent();
return nsnull;
}
@ -362,17 +350,25 @@ static void* PR_CALLBACK HandlePLEvent(PLEvent* aEvent)
static void PR_CALLBACK DestroyPLEvent(PLEvent* aEvent)
{
NS_ASSERTION(nsnull != aEvent,"Event is null");
nsInvalidateEvent *event = NS_STATIC_CAST(nsInvalidateEvent*, aEvent);
nsViewManagerEvent *event = NS_STATIC_CAST(nsViewManagerEvent*, aEvent);
delete event;
}
nsInvalidateEvent::nsInvalidateEvent(nsViewManager* aViewManager)
nsViewManagerEvent::nsViewManagerEvent(nsViewManager* aViewManager)
{
NS_ASSERTION(aViewManager, "null parameter");
mViewManager = aViewManager; // Assign weak reference
PL_InitEvent(this, aViewManager, ::HandlePLEvent, ::DestroyPLEvent);
}
struct nsInvalidateEvent : public nsViewManagerEvent {
nsInvalidateEvent(nsViewManager* aViewManager)
: nsViewManagerEvent(aViewManager) { }
virtual void HandleEvent() {
ViewManager()->ProcessInvalidateEvent();
}
};
//-------------- End Invalidate Event Definition ---------------------------
// Compare two Z-index values taking into account topmost and
@ -419,6 +415,7 @@ nsVoidArray* nsViewManager::gViewManagers = nsnull;
PRUint32 nsViewManager::gLastUserEventTime = 0;
nsViewManager::nsViewManager()
: mMouseLocation(NSCOORD_NONE, NSCOORD_NONE)
{
if (gViewManagers == nsnull) {
NS_ASSERTION(mVMCount == 0, "View Manager count is incorrect");
@ -459,6 +456,7 @@ nsViewManager::~nsViewManager()
NS_ASSERTION(nsnull != eventQueue, "Event queue is null");
eventQueue->RevokeEvents(this);
mInvalidateEventQueue = nsnull;
mSynthMouseMoveEventQueue = nsnull;
mRootScrollable = nsnull;
@ -1910,26 +1908,40 @@ NS_IMETHODIMP nsViewManager::DispatchEvent(nsGUIEvent *aEvent, nsEventStatus *aS
}
if (nsnull != view) {
float t2p = mContext->AppUnitsToDevUnits();
float p2t = mContext->DevUnitsToAppUnits();
//Calculate the proper offset for the view we're going to
nsPoint offset(0, 0);
if (baseView != view) {
//Get offset from root of baseView
nsView *parent;
parent = baseView;
while (parent) {
parent->ConvertToParentCoords(&offset.x, &offset.y);
parent = parent->GetParent();
}
//Get offset from root of baseView
nsView *parent;
for (parent = baseView; parent; parent = parent->GetParent())
parent->ConvertToParentCoords(&offset.x, &offset.y);
//Subtract back offset from root of view
parent = view;
while (parent) {
parent->ConvertFromParentCoords(&offset.x, &offset.y);
parent = parent->GetParent();
}
if ((aEvent->message == NS_MOUSE_MOVE &&
NS_STATIC_CAST(nsMouseEvent*,aEvent)->reason ==
nsMouseEvent::eReal) ||
aEvent->message == NS_MOUSE_ENTER) {
mMouseLocation.MoveTo(NSTwipsToIntPixels(offset.x, t2p) +
aEvent->point.x,
NSTwipsToIntPixels(offset.y, t2p) +
aEvent->point.y);
} else if (aEvent->message == NS_MOUSE_EXIT) {
// Although we only care about the mouse moving into an area
// for which this view manager doesn't receive mouse move
// events, we don't check which view the mouse exit was for
// since this seems to vary by platform. Hopefully this
// won't matter at all since we'll get the mouse move or
// enter after the mouse exit when the mouse moves from one
// of our widgets into another.
mMouseLocation.MoveTo(NSCOORD_NONE, NSCOORD_NONE);
}
//Subtract back offset from root of view
for (parent = view; parent; parent = parent->GetParent())
parent->ConvertFromParentCoords(&offset.x, &offset.y);
//Dispatch the event
//Before we start mucking with coords, make sure we know our baseline
aEvent->refPoint.x = aEvent->point.x;
@ -1940,9 +1952,6 @@ NS_IMETHODIMP nsViewManager::DispatchEvent(nsGUIEvent *aEvent, nsEventStatus *aS
baseView->GetDimensions(baseViewDimensions);
}
float t2p = mContext->AppUnitsToDevUnits();
float p2t = mContext->DevUnitsToAppUnits();
aEvent->point.x = baseViewDimensions.x + NSIntPixelsToTwips(aEvent->point.x, p2t);
aEvent->point.y = baseViewDimensions.y + NSIntPixelsToTwips(aEvent->point.y, p2t);
@ -4008,3 +4017,63 @@ nsViewManager::GetLastUserEventTime(PRUint32& aTime)
aTime = gLastUserEventTime;
return NS_OK;
}
struct nsSynthMouseMoveEvent : public nsViewManagerEvent {
nsSynthMouseMoveEvent(nsViewManager *aViewManager, PRBool aFromScroll)
: nsViewManagerEvent(aViewManager),
mFromScroll(aFromScroll)
{
}
virtual void HandleEvent() {
ViewManager()->ProcessSynthMouseMoveEvent(mFromScroll);
}
PRBool mFromScroll;
};
NS_IMETHODIMP
nsViewManager::SynthesizeMouseMove(PRBool aFromScroll)
{
if (mMouseLocation == nsPoint(NSCOORD_NONE, NSCOORD_NONE))
return NS_OK;
nsCOMPtr<nsIEventQueue> eventQueue;
mEventQueueService->GetSpecialEventQueue(
nsIEventQueueService::UI_THREAD_EVENT_QUEUE, getter_AddRefs(eventQueue));
NS_ASSERTION(nsnull != eventQueue, "Event queue is null");
if (eventQueue != mSynthMouseMoveEventQueue) {
nsSynthMouseMoveEvent *ev = new nsSynthMouseMoveEvent(this, aFromScroll);
eventQueue->PostEvent(ev);
mSynthMouseMoveEventQueue = eventQueue;
}
return NS_OK;
}
void
nsViewManager::ProcessSynthMouseMoveEvent(PRBool aFromScroll)
{
// allow new event to be posted while handling this one only if the
// source of the event is a scroll (to prevent infinite reflow loops)
if (aFromScroll)
mSynthMouseMoveEventQueue = nsnull;
if (mMouseLocation == nsPoint(NSCOORD_NONE, NSCOORD_NONE) || !mRootView) {
mSynthMouseMoveEventQueue = nsnull;
return;
}
nsMouseEvent event(NS_MOUSE_MOVE, mRootView->GetWidget(),
nsMouseEvent::eSynthesized);
event.point = mMouseLocation;
event.time = PR_IntervalNow();
// XXX set event.isShift, event.isControl, event.isAlt, event.isMeta ?
nsEventStatus status;
DispatchEvent(&event, &status);
if (!aFromScroll)
mSynthMouseMoveEventQueue = nsnull;
}

Просмотреть файл

@ -228,6 +228,9 @@ public:
PRUint16 aMinTwips,
nsRectVisibility *aRectVisibility);
NS_IMETHOD SynthesizeMouseMove(PRBool aFromScroll);
void ProcessSynthMouseMoveEvent(PRBool aFromScroll);
protected:
virtual ~nsViewManager();
void ProcessPendingUpdates(nsView *aView);
@ -389,6 +392,7 @@ private:
nsIScrollableView *mRootScrollable;
PRInt32 mCachingWidgetChanges;
nscolor mDefaultBackgroundColor;
nsPoint mMouseLocation; // device units, relative to mRootView
nsHashtable mMapPlaceholderViewToZTreeNode;
nsCOMPtr<nsIBlender> mBlender;
nsISupportsArray *mCompositeListeners;
@ -396,6 +400,7 @@ private:
nsView *mRootView;
nsCOMPtr<nsIEventQueueService> mEventQueueService;
nsCOMPtr<nsIEventQueue> mInvalidateEventQueue;
nsCOMPtr<nsIEventQueue> mSynthMouseMoveEventQueue;
PRPackedBool mRefreshEnabled;
PRPackedBool mPainting;
PRPackedBool mRecursiveRefreshPending;

Просмотреть файл

@ -560,11 +560,14 @@ struct nsInputEvent : public nsGUIEvent
struct nsMouseEvent : public nsInputEvent
{
enum reasonType { eReal, eSynthesized };
nsMouseEvent(PRUint32 msg = 0,
nsIWidget *w = nsnull,
reasonType aReason = eReal,
PRUint8 structType = NS_MOUSE_EVENT)
: nsInputEvent(msg, w, structType),
clickCount(0), acceptActivation(PR_FALSE)
clickCount(0), acceptActivation(PR_FALSE), reason(aReason)
{
if (msg == NS_MOUSE_MOVE) {
flags |= NS_EVENT_FLAG_CANT_CANCEL;
@ -575,7 +578,8 @@ struct nsMouseEvent : public nsInputEvent
PRUint32 clickCount;
/// Special return code for MOUSE_ACTIVATE to signal
/// if the target accepts activation (1), or denies it (0)
PRBool acceptActivation;
PRPackedBool acceptActivation;
reasonType reason : 8;
};
/**