fix multiple bugs related to mouse event handling. b=407876, 396952, 408319 r=smichaud sr=roc

This commit is contained in:
joshmoz@gmail.com 2007-12-17 20:44:30 -08:00
parent 66002db1fa
commit e79f8c4bac
2 changed files with 62 additions and 106 deletions

View File

@ -2319,13 +2319,8 @@ NSEvent* gLastDragEvent = nil;
#endif
// Events should always go to the window that is directly under the point where
// the event happened, with one exception. If there is no window under the event,
// mouse moved events should go to the rollup widget if it exists. The return value
// of this method indicates whether or not the event was supposed to be sent to this
// view. If the return value is YES, then this view should continue to process the
// event. If the return value is NO, the event was rerouted and this view should not
// process the event.
// We sometimes need to reroute events when there is a rollup widget and the
// event isn't targeted at it.
//
// Rerouting may be needed when the user tries to navigate a context menu while
// keeping the mouse-button down (left or right mouse button) -- the OS thinks this
@ -2337,85 +2332,59 @@ NSEvent* gLastDragEvent = nil;
// context menu.
- (BOOL)ensureCorrectMouseEventTarget:(NSEvent*)anEvent
{
NSWindow* windowUnderMouse = nsCocoaUtils::FindWindowUnderPoint(nsCocoaUtils::ScreenLocationForEvent(anEvent));
if (windowUnderMouse == mWindow)
// If there is no rollup widget we assume the OS routed the event correctly.
if (!gRollupWidget)
return YES;
if (!windowUnderMouse) {
if ([anEvent type] == NSMouseMoved) {
if (gRollupWidget) {
// If a mouse moved event is not over any window and there is a rollup widget, the event
// should go to the rollup widget.
NSWindow* rollupWindow = (NSWindow*)gRollupWidget->GetNativeData(NS_NATIVE_WINDOW);
if (mWindow == rollupWindow)
return YES;
else
windowUnderMouse = rollupWindow;
}
else {
// If the event is not over a window and is a mouse moved event but there is no rollup widget,
// then we don't want it to get handled. Essentially, reroute it to nowhere.
return NO;
}
}
else {
// If the event is not over a window and is not a mouse moved event, then we don't
// want it to get handled. Essentially, reroute it to nowhere.
return NO;
}
}
// If this is the rollup widget and the event is not a mouse move then trust the OS routing.
// The reason for this trust is complicated.
//
// There are three types of mouse events that can legitimately need to be targeted at a window
// that they are not over. Mouse moves, mouse drags, and mouse ups. Anything else our app wouldn't
// handle (if the mouse was not over any window) or it would go to the appropriate window.
//
// We need to do manual event rerouting for mouse moves because we know that in some cases, like
// when there is a submenu opened from a popup window, the OS will route mouse move events to the
// submenu even if the mouse is over the parent. Mouse move events are never tied to a particular
// window because of some originating action like the starting point of a drag for drag events or
// a mouse down event for mouse up events, so it is always safe to do our own routing on them here.
//
// As for mouse drags and mouse ups, they have originating actions that tie them to windows they
// may no longer be over. If there is a rollup window present when one of these events is getting
// processed but we are not it, we are probably the window where the action originated, and that
// action must have caused the rollup window to come into existence. In that case, we might need
// to reroute the event if it is over the rollup window. That is why if we're not the rollup window
// we don't return YES here.
NSWindow* rollupWindow = (NSWindow*)gRollupWidget->GetNativeData(NS_NATIVE_WINDOW);
if (mWindow == rollupWindow && [anEvent type] != NSMouseMoved)
return YES;
NSEventType type = [anEvent type];
NSPoint newWindowLocation = nsCocoaUtils::EventLocationForWindow(anEvent, windowUnderMouse);
NSEvent *newEvent = nil;
// Find the window that the event is over.
NSWindow* targetWindow = nsCocoaUtils::FindWindowUnderPoint(nsCocoaUtils::ScreenLocationForEvent(anEvent));
// If anEvent is a mouseUp event, send an extra mouseDown event before
// sending a mouseUp event -- this is needed to support selection by
// dragging the mouse to a menu item and then releasing it. We retain
// the window in case it gets destroyed as a result of the extra
// mouseDown (and release it below).
BOOL sendSynthMouseDown = gRollupWidget && (type == NSLeftMouseUp || type == NSRightMouseUp);
if (sendSynthMouseDown) {
[windowUnderMouse retain];
NSEventType extraEventType;
switch (type) {
case NSLeftMouseUp:
extraEventType = NSLeftMouseDown;
break;
case NSRightMouseUp:
extraEventType = NSRightMouseDown;
break;
default:
extraEventType = (NSEventType) 0;
break;
}
newEvent = [NSEvent mouseEventWithType:extraEventType
location:newWindowLocation
modifierFlags:[anEvent modifierFlags]
timestamp:GetCurrentEventTime()
windowNumber:[windowUnderMouse windowNumber]
context:nil
eventNumber:0
clickCount:1
pressure:0.0];
[windowUnderMouse sendEvent:newEvent];
}
// If the event was not over any window, send it to the rollup window.
if (!targetWindow)
targetWindow = rollupWindow;
newEvent = [NSEvent mouseEventWithType:type
location:newWindowLocation
modifierFlags:[anEvent modifierFlags]
timestamp:GetCurrentEventTime()
windowNumber:[windowUnderMouse windowNumber]
context:nil
eventNumber:0
clickCount:1
pressure:0.0];
[windowUnderMouse sendEvent:newEvent];
// At this point we've resolved a target window, if we are it then just return
// yes so we handle it. No need to redirect.
if (targetWindow == mWindow)
return YES;
if (sendSynthMouseDown)
[windowUnderMouse release];
// Send the event to its new destination.
NSPoint newWindowLocation = nsCocoaUtils::EventLocationForWindow(anEvent, targetWindow);
NSEvent *newEvent = [NSEvent mouseEventWithType:[anEvent type]
location:newWindowLocation
modifierFlags:[anEvent modifierFlags]
timestamp:GetCurrentEventTime()
windowNumber:[targetWindow windowNumber]
context:nil
eventNumber:0
clickCount:1
pressure:0.0];
[targetWindow sendEvent:newEvent];
// Return NO because we just sent the event somewhere else.
return NO;
}

View File

@ -1715,7 +1715,8 @@ void patternDraw(void* aInfo, CGContextRef aContext)
// events for a given NSWindow object go through its sendEvent: method.)
- (void)sendEvent:(NSEvent *)anEvent
{
NSView *target = nil, *contentView = nil;
NSView *target = nil;
NSView *contentView = nil;
NSEventType type = [anEvent type];
NSPoint windowLocation = NSZeroPoint;
switch (type) {
@ -1730,10 +1731,14 @@ void patternDraw(void* aInfo, CGContextRef aContext)
case NSLeftMouseDragged:
case NSRightMouseDragged:
case NSOtherMouseDragged:
if ((contentView = [self contentView]) != nil) {
if ((contentView = [self contentView])) {
// Since [anEvent window] might not be us, we can't use [anEvent locationInWindow].
windowLocation = nsCocoaUtils::EventLocationForWindow(anEvent, self);
target = [contentView hitTest:[contentView convertPoint:windowLocation fromView:nil]];
// If the hit test failed, the event is targeted here but is not over the window.
// Target it at the first responder.
if (!target)
target = (NSView*)[self firstResponder];
}
break;
default:
@ -1747,17 +1752,17 @@ void patternDraw(void* aInfo, CGContextRef aContext)
case NSLeftMouseDown:
[target mouseDown:anEvent];
// If we're in a context menu we don't want the OS to send the coming
// leftMouseUp event to NSApp via the window server, but we do want
// our ChildView to receive a leftMouseUp event (and to send a Gecko
// NSLeftMouseUp event to NSApp via the window server, but we do want
// our ChildView to receive an NSLeftMouseUp event (and to send a Gecko
// NS_MOUSE_BUTTON_UP event to the corresponding nsChildView object).
// If our NSApp isn't active (i.e. if we're in a context menu raised
// by a rightMouseDown event) when it receives the coming leftMouseUp
// via the window server, our browser will (in effect) become partially
// by a right mouse down event) when it receives the coming NSLeftMouseUp
// via the window server, our app will (in effect) become partially
// activated, which has strange side effects: For example, if another
// app's window had the focus, that window will lose the focus and the
// other app's main menu will be completely disabled (though it will
// continue to be displayed).
// A side effect of not allowing the coming leftMouseUp event to be
// A side effect of not allowing the coming NSLeftMouseUp event to be
// sent to NSApp via the window server is that our custom context
// menus will roll up whenever the user left-clicks on them, whether
// or not the left-click hit an active menu item. This is how native
@ -1765,8 +1770,8 @@ void patternDraw(void* aInfo, CGContextRef aContext)
// behaved previously (on the trunk or e.g. in Firefox 2.0.0.4).
// If our ChildView's corresponding nsChildView object doesn't
// dispatch an NS_MOUSE_BUTTON_UP event, none of our active menu items
// will "work" on a leftMouseDown.
if (mIsContextMenu) {
// will "work" on an NSLeftMouseUp.
if (mIsContextMenu && ![NSApp isActive]) {
NSEvent *newEvent = [NSEvent mouseEventWithType:NSLeftMouseUp
location:windowLocation
modifierFlags:[anEvent modifierFlags]
@ -1812,24 +1817,6 @@ void patternDraw(void* aInfo, CGContextRef aContext)
break;
}
} else {
// Sometimes more than one popup window can be visible at the same time
// (e.g. nested non-native context menus, or the test case (attachment
// 276885) for bmo bug 392389, which displays a non-native combo-box in
// a non-native popup window). In these cases the "active" popup window
// (the one that corresponds to the current gRollupWidget) should receive
// all mouse events that happen over it. So if anEvent wasn't processed
// here, if there's a current gRollupWidget, and if its NSWindow object
// isn't us, we send anEvent to the gRollupWidget's NSWindow object, then
// return. Other code (in nsChildView.mm's ChildView class) will redirect
// events that happen over us but should be redirected to the current
// gRollupWidget.
if (gRollupWidget) {
NSWindow *rollupWindow = (NSWindow*)gRollupWidget->GetNativeData(NS_NATIVE_WINDOW);
if (rollupWindow && ![rollupWindow isEqual:self]) {
[rollupWindow sendEvent:anEvent];
return;
}
}
[super sendEvent:anEvent];
}
}