NSWindow windowDidResignKey not getting invoked after redisplaying window - objective-c

I have a custom NSWindow subclass that the user can toggle the display of with the click of a button. I'd also like the window to disappear when the window resigns key status (e.g. by the user clicking outside the window).
I have a delegate that implements windowDidResignKey: but I find that this delegate method is only invoked the first time the window resigns key.
Here's how I toggle the display of the window (via user action or windowDidResignKey):
- (void) toggleWindowAtPoint:(NSPoint)point
{
// Attach/detach window.
if (!attachedWindow)
{
attachedWindow = [[CustomWindow alloc] attachedToPoint:point];
attachedWindow.delegate = self;
[attachedWindow setLevel:NSMainMenuWindowLevel+1]; // show window in front of all other apps on desktop
[attachedWindow makeKeyAndOrderFront:self];
}
else
{
attachedWindow.delegate = nil;
[attachedWindow orderOut:self];
[attachedWindow release];
attachedWindow = nil;
}
}
Here's my implementation of windowDidResignKey:
- (void) windowDidResignKey:(NSNotification *)note
{
[self toggleWindowAtPoint:NSMakePoint(0, 0)];
}
I'm finding that the first time the custom window is displayed, windowDidResignKey: gets called. Every time the custom window is re-displayed after that, windowDidResignKey: is not getting invoked.

The issue was that in some cases, the custom window was not actually becoming the key window after calling [attachedWindow makeKeyAndOrderFront:self].
I fixed this by adding the following line before re-creating the window:
[[NSApplication sharedApplication] activateIgnoringOtherApps:YES];
In the context of the code snippet above:
- (void) toggleWindowAtPoint:(NSPoint)point
{
// Attach/detach window.
if (!attachedWindow)
{
[[NSApplication sharedApplication] activateIgnoringOtherApps:YES];
attachedWindow = [[CustomWindow alloc] attachedToPoint:point];
....

Have you tried calling [attachedWindow makeFirstResponder:attachedWindow] in your toggle method?

If you want to activate a window without using activateIgnoringOtherApps: you should use a NSPanel with a NSNonactivatingPanelMask:
[[CustomPanel alloc]
initWithContentRect: NSZeroRect
styleMask: NSNonactivatingPanelMask
backing: NSBackingStoreBuffered
defer: NO];

Related

Unable to focus on NSTextFields from Modal window

I currently have a window open which was opened via:
// FirstWindowController
[self showWindow:self];
[[self window] makeKeyAndOrderFront:self];
[NSApp runModalForWindow:[self window]];
And upon click of a button I'd like to hide FirstWindowController via:
// FirstWindowController
[self.window orderOut:self];
And then show my second window:
// SecondWindowController
[self showWindow:self];
[[self window] makeKeyAndOrderFront:self];
[NSApp runModalForWindow:[self window]];
The first window disappears correctly, and the second window appears. But I can't actually use the NSTextFields in the input. But I can click the cancel button to hide SecondWindowController and give focus back to FirstWindowController.
Why can't I click any of the NSTextField elements?
I had the same problem. It worked when the window had a title bar and otherwise not. It seems like that a window needs to have a title to become a keyWindow.
The workaround for this is to make a subclass of NSWindow and override -canBecomeKeyWindow:
(BOOL)canBecomeKeyWindow {
return YES;
}

NSNotification addObserver selector could not open a NSBeginAlertSheet sheet on window

I have two different window Controllers. First is a custom panel window controller and another is main window controller. In panel window there is a panel window and there are button on that panel. On click of those buttons I am posting notification like :
In PanelWindowController:
-(IBAction)okAndCancelButtonClicked:(id)sender
{
[self postNotification:sender];
}
-(void)postNotification:(id)sender
{
if([sender tag]!=2){
[[self window] endSheet:self.panel returnCode:NSModalResponseCancel];
[self.panel orderOut:self];
}
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInteger:[sender tag]],#"value",nil];
[[NSNotificationCenter defaultCenter] postNotificationName:#"PanelButtonClickedNotification" object:self userInfo:dict];
}
Now, in my main window controller, I am trying to open a NSBeginAlertSheet in the selector of addObserver of NSNotificationCenter. Following is the addObserver selector declaration in init method of my main window controller:
MainWindowController
-(id) init{
..// some code here
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(okButtonClicked:) name:#"PanelButtonClickedNotification" object:[self panelClass]];
return self;
}
the implementation of okButtonClicked is the following :
- (void) okButtonClicked:(NSNotification*)notification
{
if ([[notification object] isEqualTo:[self panelClass]])
{
if([[[notification userInfo] objectForKey:#"value"] integerValue] == 1)
{
// Yes Button in the Panel is clicked
}
else if([[[notification userInfo] objectForKey:#"value"] integerValue] == 0)
{
// No Button in the Panel is clicked
NSBeginAlertSheet(#"Alert", #"Ok", nil, nil, [[self view] window], self,nil, nil,nil,#"Alert is being shown on window.");
}
}
}
When user clicks No button on Panel, an alert on the window should be displayed. But the alert is never shown. I also tried [NSApp keyWindow] and [NSApp mainWindow] instead of [[self view] window].
And, If I run the alert independently of window, it is displayed:
NSAlert *alert = [[NSAlert alloc] init];
[alert setMessageText:#"Alert"];
[alert addButtonWithTitle:#"OK"];
NSImage *icon=[NSImage imageNamed:#"warning.png"];
[alert setIcon:icon];
[alert runModal];
Please let me know if I am missing anything here.
The alert is not displayed in any of the methods called after the notification is received.
PFA my sample project : https://www.dropbox.com/s/0xfe4bk17v9girj/PanelApplication.zip?dl=0
It is a little difficult to tell, but my best guess would be, that the object that reacts to the notification, in the okButtonClicked: function, does not have a (valid) reference to your window. And then, the alert don't know on what window to display.
If you can send a reference to your window in the notification object, then you should be able to get the alert shown on the window you want.
example code:
[[NSNotificationCenter defaultCenter] postNotificationName:#"PanelButtonClickedNotification" object:[self window]];
And:
- (void) okButtonClicked:(NSNotification*)notification
{
NSBeginAlertSheet(#"Alert", #"Ok", nil, nil, [notification object], self,nil, nil,nil,#"Alert is being shown on window.");
}
this works in my test-project.
The problem was not with the notifications, it was the panel which was causing problems:
I was opening the panel in my main window controller:
[[self window] beginSheet:self.panelClass.panel completionHandler:nil];
And the closing action of the panel was written in the Panel window controller. Because of this reason no NSBeginAlertSheet was not being shown once the custom panel was loaded/ unloaded on the main window.
Therefore moving the following piece of code from panel window controller to main window controller solved the problem:
[[self window] endSheet:self.panelClass.panel returnCode:NSModalResponseCancel];
[self.panelClass.panel orderOut:self];

Binding a window created with the Cocoa Interface builder to a programmatically created one

I have an Objective C application which starts with loading a window created with Interface Builder:
//in main()
[NSApplication sharedApplication];
[NSBundle loadNibNamed:#"MainMenu" owner:NSApp];
[NSApp run];
In MainMenu.xib I have a window with a button. I want to create programmatically the second window when that button is pressed.
//in MainMenu.xib Controller.h
#class SecondWindowController;
#interface Controller: NSWindowController {
#private
SecondWindowController *sw;
}
- (IBAction)onButtonPress:(id)object;
#end
//in MainMenu.xib Controller.m
#import "SecondWindowController.h"
#implementation Controller
- (IBAction)onButtonPress:(id)object {
sw = [[SecondWindowController alloc] initWithWindowNibName:#"SecondWindow"];
[sw showWindow:self];
[[self window] orderOut:self];
}
#end
Where SecondWindowController inherits from NSWindowController. In SecondWindowController.h I have:
- (id)initWithWindow:(NSWindow*)window {
self = [super initWithWindow:window];
if (self) {
NSRect window_rect = { {custom_height1, custom_width1},
{custom_height2, custom_width2} };
NSWindow* secondWindow = [[NSWindow alloc]
initWithContentRect:window_rect
styleMask: ...
backing: NSBackingStoreBuffered
defer:NO];
}
return self;
}
And in the SecondWindow.xib I have nothing. When the button of the first window is pressed the first window disappears and the application closes. The reason I don't want to use the Interface builder for the second window is that I want to programmatically initialize it. Is this possible and if so what is the right way to accomplish this?
OK, I was initially confused with your use of initWithWindowNibName:#"SecondWindow" which will attempt to load the window from a NIB file, which you later mention you don't want to do.
Please use this to create your window:
- (IBAction)onButtonPress:(id)object {
if (!sw)
sw = [[SecondWindowController alloc] init];
[sw showWindow:self];
[[self window] orderOut:self];
}
Which will avoid creating multiple copies of the window controller, which you don't want (if you do then you'll need to store them in an array). Note the name sw is incorrect by convention; use either _sw or create setter/getter methods and use self.sw.
Initialize SecondWindowController like this:
- (id)init {
NSRect window_rect = NSMakeRect(custom_x, custom_y,
custom_width, custom_height);
NSWindow* secondWindow = [[NSWindow alloc]
initWithContentRect:window_rect
styleMask: ...
backing: NSBackingStoreBuffered
defer:NO];
self = [super initWithWindow:secondWindow];
if (self) {
// other stuff
}
return self;
}
Note: your variable names for origin/size of the new window were wrong; please review them.

Non modal dialog has to appear as modal window

I can show my custom NSWindowController as modal window by code:
TSAppDelegate* appDelegate = (TSAppDelegate*) [[NSApplication sharedApplication] delegate];
NSWindow* mainWindow = appDelegate.window;
[NSApp beginSheet: [self window]
modalForWindow: mainWindow
modalDelegate: NULL
didEndSelector: NULL
contextInfo: NULL];
int acceptedModal = (int)[NSApp runModalForWindow: [self window]];
[NSApp endSheet: [self window]];
[[self window] close];
It works. But I need non modal window. It has to appear as modal (see pic) and be NON MODAL.
I tried
TSAppDelegate* appDelegate = (TSAppDelegate*) [[NSApplication sharedApplication] delegate];
NSWindow* mainWindow = appDelegate.window;
[[self window] setParentWindow: mainWindow];
or
[mainWindow addChildWindow: [self window] ordered: NSWindowAbove];
It works as non modal but appears as normal popup window.
Is it possible?
It seems that you just want a window to come down over your view but not be modal.
Rather than use a sheet, you could just use a separate view that you animate into and out of position.
You'll have to do some work yourself: setting the correct position, animating the view, responding to events, etc.

How to change NSCarbonWindow visibility?

I'm using ::RunAppModalLoopForWindow(WindowPtr) to run a carbon window as modal. But at some point I need to show/hide all application's windows with:
NSArray* windowNumbers = [NSWindow windowNumbersWithOptions:0];
for (NSNumber* windowNumber in windowNumbers)
{
NSWindow* window = [[NSApplication sharedApplication] windowWithWindowNumber:[windowNumber integerValue]];
[window setAlphaValue:CGFloat(showFlag)];
}
But window is NSCarbonWindow* that does not respond to setAlphaValue, and thus, the window is not changing its visibility.
Also, I don't know where NSCarbonWindow is defined so I can cast from NSWindow to NSCarbonWindow.
How about:
if ([window respondsToSelector: #selector(setAlphaValue:)])
[window setAlphaValue:CGFloat(showFlag)];
else if (showFlag)
ShowWindow( [window windowRef] );
else
HideWindow( [window windowRef] );