Bug 428071. Tie NS_ACTIVATE/NS_DEACTIVE state to a window's 'main' status. patch by Markus Stange and Stephen Michaud, r=josh,r=mstange,sr=roc,a=mconnor. God have mercy on us all

This commit is contained in:
roc+%cs.cmu.edu 2008-05-02 10:40:49 +00:00
Родитель b679bdf4e9
Коммит 7ca911fc71
4 изменённых файлов: 172 добавлений и 42 удалений

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

@ -101,12 +101,19 @@ typedef struct _nsCocoaWindowList {
@interface WindowDelegate : NSObject
{
nsCocoaWindow* mGeckoWindow; // [WEAK] (we are owned by the window)
// Used to avoid duplication when we send NS_ACTIVATE/NS_GOTFOCUS and
// NS_DEACTIVATE/NS_LOSTFOCUS to Gecko for toplevel widgets. Starts out
// PR_FALSE.
PRBool mToplevelActiveState;
}
+ (void)paintMenubarForWindow:(NSWindow*)aWindow;
- (id)initWithGeckoWindow:(nsCocoaWindow*)geckoWind;
- (void)windowDidResize:(NSNotification*)aNotification;
- (void)sendFocusEvent:(PRUint32)eventType;
- (nsCocoaWindow*)geckoWidget;
- (PRBool)toplevelActiveState;
- (void)sendToplevelActivateEvents;
- (void)sendToplevelDeactivateEvents;
@end

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

@ -46,6 +46,7 @@
#include "nsIRollupListener.h"
#include "nsCocoaUtils.h"
#include "nsChildView.h"
#include "nsWindowMap.h"
#include "nsIAppShell.h"
#include "nsIAppShellService.h"
#include "nsIBaseWindow.h"
@ -593,16 +594,14 @@ NS_IMETHODIMP nsCocoaWindow::Show(PRBool bState)
if (![mWindow isVisible]) {
mSheetNeedsShow = PR_FALSE;
mSheetWindowParent = topNonSheetWindow;
[[mSheetWindowParent delegate] sendFocusEvent:NS_LOSTFOCUS];
[[mSheetWindowParent delegate] sendFocusEvent:NS_DEACTIVATE];
[TopLevelWindowData deactivateInWindow:mSheetWindowParent];
[mWindow setAcceptsMouseMovedEvents:YES];
[NSApp beginSheet:mWindow
modalForWindow:mSheetWindowParent
modalDelegate:mDelegate
didEndSelector:@selector(didEndSheet:returnCode:contextInfo:)
contextInfo:mSheetWindowParent];
[[mWindow delegate] sendFocusEvent:NS_GOTFOCUS];
[[mWindow delegate] sendFocusEvent:NS_ACTIVATE];
[TopLevelWindowData activateInWindow:mWindow];
SendSetZLevelEvent();
}
}
@ -672,8 +671,7 @@ NS_IMETHODIMP nsCocoaWindow::Show(PRBool bState)
[mWindow setAcceptsMouseMovedEvents:NO];
[[mWindow delegate] sendFocusEvent:NS_LOSTFOCUS];
[[mWindow delegate] sendFocusEvent:NS_DEACTIVATE];
[TopLevelWindowData deactivateInWindow:mWindow];
nsCocoaWindow* siblingSheetToShow = nsnull;
PRBool parentIsSheet = PR_FALSE;
@ -1417,6 +1415,7 @@ NS_IMETHODIMP nsCocoaWindow::EndSecureKeyboardInput()
[super init];
mGeckoWindow = geckoWind;
mToplevelActiveState = PR_FALSE;
return self;
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
@ -1562,11 +1561,9 @@ NS_IMETHODIMP nsCocoaWindow::EndSecureKeyboardInput()
// Note: 'contextInfo' is the window that is the parent of the sheet,
// we set that in nsCocoaWindow::Show. 'contextInfo' is always the top-level
// window, not another sheet itself.
[[sheet delegate] sendFocusEvent:NS_LOSTFOCUS];
[[sheet delegate] sendFocusEvent:NS_DEACTIVATE];
[TopLevelWindowData deactivateInWindow:sheet];
[sheet orderOut:self];
[[(NSWindow*)contextInfo delegate] sendFocusEvent:NS_GOTFOCUS];
[[(NSWindow*)contextInfo delegate] sendFocusEvent:NS_ACTIVATE];
[TopLevelWindowData activateInWindow:(NSWindow*)contextInfo];
NS_OBJC_END_TRY_ABORT_BLOCK;
}
@ -1577,6 +1574,32 @@ NS_IMETHODIMP nsCocoaWindow::EndSecureKeyboardInput()
return mGeckoWindow;
}
- (PRBool)toplevelActiveState
{
return mToplevelActiveState;
}
- (void)sendToplevelActivateEvents
{
if (!mToplevelActiveState) {
[self sendFocusEvent:NS_GOTFOCUS];
[self sendFocusEvent:NS_ACTIVATE];
mToplevelActiveState = PR_TRUE;
}
}
- (void)sendToplevelDeactivateEvents
{
if (mToplevelActiveState) {
[self sendFocusEvent:NS_DEACTIVATE];
[self sendFocusEvent:NS_LOSTFOCUS];
mToplevelActiveState = PR_FALSE;
}
}
@end

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

@ -88,6 +88,10 @@
}
- (id)initWithWindow:(NSWindow*)inWindow;
+ (void)activateInWindow:(NSWindow*)aWindow;
+ (void)deactivateInWindow:(NSWindow*)aWindow;
+ (void)activateInWindowViews:(NSWindow*)aWindow;
+ (void)deactivateInWindowViews:(NSWindow*)aWindow;
@end

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

@ -156,6 +156,16 @@
name:NSWindowDidResignKeyNotification
object:inWindow];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(windowBecameMain:)
name:NSWindowDidBecomeMainNotification
object:inWindow];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(windowResignedMain:)
name:NSWindowDidResignMainNotification
object:inWindow];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(windowWillClose:)
name:NSWindowWillCloseNotification
@ -178,9 +188,10 @@
// As best I can tell, if the notification's object has a corresponding
// top-level widget (an nsCocoaWindow object), it has a delegate (set in
// nsCocoaWindow::StandardCreate()) that responds to sendFocusEvent:, and
// otherwise not (Camino doesn't use top-level widgets (nsCocoaWindow
// objects) -- only child widgets (nsChildView objects)).
// nsCocoaWindow::StandardCreate()) of class WindowDelegate, and otherwise
// not (Camino doesn't use top-level widgets (nsCocoaWindow objects) --
// only child widgets (nsChildView objects)). (The notification is sent
// to windowBecameKey: or windowBecameMain: below.)
//
// If we're using top-level widgets, we need to send them both kinds of
// focus event (NS_GOTFOCUS and NS_ACTIVATE, which by convention are sent in
@ -198,34 +209,35 @@
// top-level widgets -- so we do the same here. Not sending them directly
// to child widgets also avoids "win is null" assertions on debug builds
// (see bug 354768 comments 55 and 58).
- (void)windowBecameKey:(NSNotification*)inNotification
{
//
// For use with clients that (like Firefox) do use top-level widgets (and
// have NSWindow delegates of class WindowDelegate).
+ (void)activateInWindow:(NSWindow*)aWindow
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
NSWindow* window = (NSWindow*)[inNotification object];
id delegate = [window delegate];
if (delegate && [delegate respondsToSelector:@selector(sendFocusEvent:)]) {
[delegate sendFocusEvent:NS_GOTFOCUS];
[delegate sendFocusEvent:NS_ACTIVATE];
id firstResponder = [window firstResponder];
if ([firstResponder isKindOfClass:[ChildView class]]) {
BOOL isMozWindow = [window respondsToSelector:@selector(setSuppressMakeKeyFront:)];
if (isMozWindow)
[window setSuppressMakeKeyFront:YES];
[firstResponder sendFocusEvent:NS_GOTFOCUS];
if (isMozWindow)
[window setSuppressMakeKeyFront:NO];
}
} else {
id firstResponder = [window firstResponder];
if ([firstResponder isKindOfClass:[ChildView class]])
[firstResponder viewsWindowDidBecomeKey];
WindowDelegate* delegate = (WindowDelegate*) [aWindow delegate];
if (!delegate || ![delegate isKindOfClass:[WindowDelegate class]])
return;
if ([delegate toplevelActiveState])
return;
[delegate sendToplevelActivateEvents];
id firstResponder = [aWindow firstResponder];
if ([firstResponder isKindOfClass:[ChildView class]]) {
BOOL isMozWindow = [aWindow respondsToSelector:@selector(setSuppressMakeKeyFront:)];
if (isMozWindow)
[aWindow setSuppressMakeKeyFront:YES];
[firstResponder sendFocusEvent:NS_GOTFOCUS];
if (isMozWindow)
[aWindow setSuppressMakeKeyFront:NO];
}
NS_OBJC_END_TRY_ABORT_BLOCK;
}
// See comments above windowBecameKey:
// See comments above activateInWindow:
//
// If we're using top-level widgets (nsCocoaWindow objects), we send them
// NS_DEACTIVATE events (which propagate to child widgets (nsChildView
@ -242,23 +254,107 @@
// text input fields. Since we also need to send NS_LOSTFOCUS events and
// call nsTSMManager::CommitIME(), we just always call through to ChildView
// viewsWindowDidResignKey (whether or not we're using top-level widgets).
- (void)windowResignedKey:(NSNotification*)inNotification
//
// For use with clients that (like Firefox) do use top-level widgets (and
// have NSWindow delegates of class WindowDelegate).
+ (void)deactivateInWindow:(NSWindow*)aWindow
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
NSWindow* window = (NSWindow*)[inNotification object];
id delegate = [window delegate];
if (delegate && [delegate respondsToSelector:@selector(sendFocusEvent:)]) {
[delegate sendFocusEvent:NS_DEACTIVATE];
[delegate sendFocusEvent:NS_LOSTFOCUS];
}
id firstResponder = [window firstResponder];
WindowDelegate* delegate = (WindowDelegate*) [aWindow delegate];
if (!delegate || ![delegate isKindOfClass:[WindowDelegate class]])
return;
if (![delegate toplevelActiveState])
return;
[delegate sendToplevelDeactivateEvents];
id firstResponder = [aWindow firstResponder];
if ([firstResponder isKindOfClass:[ChildView class]])
[firstResponder viewsWindowDidResignKey];
NS_OBJC_END_TRY_ABORT_BLOCK;
}
// For use with clients that (like Camino) don't use top-level widgets (and
// don't have NSWindow delegates of class WindowDelegate).
+ (void)activateInWindowViews:(NSWindow*)aWindow
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
id firstResponder = [aWindow firstResponder];
if ([firstResponder isKindOfClass:[ChildView class]])
[firstResponder viewsWindowDidBecomeKey];
NS_OBJC_END_TRY_ABORT_BLOCK;
}
// For use with clients that (like Camino) don't use top-level widgets (and
// don't have NSWindow delegates of class WindowDelegate).
+ (void)deactivateInWindowViews:(NSWindow*)aWindow
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
id firstResponder = [aWindow firstResponder];
if ([firstResponder isKindOfClass:[ChildView class]])
[firstResponder viewsWindowDidResignKey];
NS_OBJC_END_TRY_ABORT_BLOCK;
}
// We make certain exceptions for top-level windows in non-embedders (see
// comment above windowBecameMain below). And we need (elsewhere) to guard
// against sending duplicate events. But in general NS_ACTIVATE and
// NS_GOTFOCUS events should be sent when a native window becomes key, and
// NS_LOSTFOCUS and NS_DEACTIVATE events should be sent when it resignes key.
- (void)windowBecameKey:(NSNotification*)inNotification
{
NSWindow* window = (NSWindow*)[inNotification object];
id delegate = [window delegate];
if (!delegate || ![delegate isKindOfClass:[WindowDelegate class]]) {
[TopLevelWindowData activateInWindowViews:window];
} else if ([window isSheet]) {
[TopLevelWindowData activateInWindow:window];
}
}
- (void)windowResignedKey:(NSNotification*)inNotification
{
NSWindow* window = (NSWindow*)[inNotification object];
id delegate = [window delegate];
if (!delegate || ![delegate isKindOfClass:[WindowDelegate class]]) {
[TopLevelWindowData deactivateInWindowViews:window];
} else if ([window isSheet]) {
[TopLevelWindowData deactivateInWindow:window];
}
}
// The appearance of a top-level window depends on its main state (not its key
// state). So (for non-embedders) we need to ensure that a top-level window
// is main when an NS_ACTIVATE event is sent to Gecko for it.
- (void)windowBecameMain:(NSNotification*)inNotification
{
NSWindow* window = (NSWindow*)[inNotification object];
id delegate = [window delegate];
// Don't send events to a top-level window that has a sheet open above it --
// as far as Gecko is concerned, it's inactive, and stays so until the sheet
// closes.
if (delegate && [delegate isKindOfClass:[WindowDelegate class]] && ![window attachedSheet])
[TopLevelWindowData activateInWindow:window];
}
- (void)windowResignedMain:(NSNotification*)inNotification
{
NSWindow* window = (NSWindow*)[inNotification object];
id delegate = [window delegate];
if (delegate && [delegate isKindOfClass:[WindowDelegate class]] && ![window attachedSheet])
[TopLevelWindowData deactivateInWindow:window];
}
- (void)windowWillClose:(NSNotification*)inNotification
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;