Unable to focus on NSTextFields from Modal window - objective-c

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;
}

Related

NSPopover loses first responder

I have a NSPopover in my application with various buttons. One button allows the user to tweet using the NSSharingService:
NSArray* array = #[ #"Tweet something"];
NSSharingService* sharingServiceFB = [NSSharingService sharingServiceNamed:NSSharingServiceNamePostOnTwitter];
[sharingServiceFB performWithItems:array];
This works well, but when the tweet has been sent or cancelled, the focus is returned to the main application (main window) and not the NSPopover. How can I return focus to the NSPopover?
My initial approach was to observe NSWindowDidBecomeKeyNotificationwhich calls a method when notification has been received that does the following
if (self.sheetPopover!=nil){
[self.sheetPopover becomeFirstResponder];
}
However, this did not work as expected and I still have to click twice on the NSPopover to regain focus. Any suggestions as to how to fix this? Thanks.
I found a solution that worked for me. I made my NSPopover a delegate of NSSharingServiceDelegate, and implemented two of the delegate methods where I reset the first responder when the twitter view had closed. Here, self is the NSPopover view.
- (void)sharingService:(NSSharingService *)sharingService didShareItems:(NSArray *)items
{
[self becomeFirstResponder];
[[self window] becomeKeyWindow];
[[self window] becomeMainWindow];
}
- (void)sharingService:(NSSharingService *)sharingService didFailToShareItems:(nonnull NSArray *)items error:(nonnull NSError *)error
{
[self becomeFirstResponder];
[[self window] becomeKeyWindow];
[[self window] becomeMainWindow];
}
Comments as to whether this is the best approach are very welcome.

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];

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.

View not working when window is closed - how to fix this issue?

Hello I have a view which is shown when a button is pressed this works fine, if I open a second window the view works fine and I can go back the prior window and use the view as I have used removeFromSuperview.
My problem is however if I close the last opened window the view no longer works on the first window, however if I open a new window it works on the first window again.
How do I get the view to continue working when one of the windows have been closed?
Thanks for your help!
Here is the code I am using:
-(IBAction) ShowView:(id) sender{
[myView setHidden:FALSE];
if ([myView isInFullScreenMode]){
[myView exitFullScreenModeWithOptions:nil];
}
else{
[myView enterFullScreenMode:[[myView window] screen] withOptions:nil];
for (NSView *view in [NSArray arrayWithArray:[myView subviews]]){
[view removeFromSuperview];
[myView addSubview:view];
}
}
}

NSWindow windowDidResignKey not getting invoked after redisplaying window

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];