How to detect when a NSPanel closes as a result of losing focus? E.g. A user clicking outside of the NSPanel (Window?) - objective-c

I have a generic NSPanel window that I am using as a preferences window in my app. I have a selector that I call every time the window closes. The purpose of that selector is to save the state of the users chosen preferences (there is no "save" button).
I have an NSButton ("CLOSE") which I easily setup to call my closing selector.
I set it up so that my selector is also called when the user clicks the RED X in the top left corner of the NSPanel by doing:
NSButton *closeButton = [[self window] standardWindowButton:NSWindowCloseButton];
[closeButton setTarget:self];
[closeButton setAction:#selector(myCloseSelector:)];
This works perfectly. My problem though? The window also closes if the user clicks outside of the NSPanel. E.g. If they take their mouse and click on their browser window below the NSPanel that popped up. This also closes the window.
How do I catch my NSPanel losing focus and closing ? I need to ensure that when this happens I also get my selector called.
Thanks!

Made my NSWindowController a delegate for NSWindowDelegate.
myWindowController.h
#interface myWindowController : NSWindowController <NSWindowDelegate>
and then set myWindowController as the delegate for my NSPanel.
Now I can implement:
- (void) windowDidResignKey:(NSNotification *)notification {
NSLog(#"Houston...we lost a panel.");
}
and everything works swimmingly!

Related

How do you make an NSWindow visible and in focus that was declared in Interface Builder?

I have an NSWindow designed in Interface Builder, and I want this window to be appear programmatically and be given focus. In my case the window appears, allows itself to be configured with titles, but the window stubbornly refuses to take focus, refuses to resize, and refuses to respond to any kind of input, from pressing the button, to moving the vertical slider, to allowing the window to close.
The NSWindowController is defined as follows:
#interface SignTextTab : NSWindowController <NSWindowDelegate>
- (id)processMessage:(id)message messageUuid:(id)messageUuid messageId:(id)messageId;
#end
and is instantiated as follows:
tab = [[SignTextTab alloc] init];
The NSWindowController loads the NIB as follows:
- (id) init
{
return [super initWithWindowNibName:#"SignTextTab" owner:self];
}
and the window is made visible as follows:
self.window.level = NSModalPanelWindowLevel;
[self.window makeKeyAndOrderFront:self];
Querying the status of the NSWindow directly after the calls above leads to this:
NSWindowStyleMask styleMask = self.window.styleMask;
BOOL isKey = self.window.isKeyWindow;
BOOL canKey = self.window.canBecomeKeyWindow;
The variable isKey is NO, because makeKeyAndOrderFront did not appear to do anything. The variable canKey is YES.
The styleMask is as follows:
NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskResizable
The application is a Safari Web Extension.
Many people have asked the question, but none of these have answers that translate into concrete actions relevant in Xcode 13.
How do I open an NSWindow and have the window selected and in focus?
Connect Window Controller Outlet to Window
How to give focus to NSWindow loaded from NIB?
The file's owner is configured as follows.
Does anyone know what concrete steps need to be taken to make sure the NSWindow can gain focus, and provide concrete screenshots of what Interface Builder is supposed to look like when it is configured correctly?

NSPanel Sheet Not Receiving Mouse Events

SIMPLIFIED:
I have the following:
NSWindow (this is the main window of the app)
NSPanel (this is presented as a sheet, over the main NSWindow, using [NSApp beginSheet:)
The main NSWindow has a subclassed NSButton that uses mouse events, including mouseEntered, mouseExited. This changes the background color of the button as I hover over it.
I present my NSPanel, this also has an NSButton of the same subclass, to change color on hover.
Mouse events still occur in the main NSWindow while my NSPanel is presented. I can tell this, as hovering over the buttons still works.
However, hovering over the buttons in the presented NSPanel does not work, despite them being no different from the other buttons. I can click on them and they receive an action. But they do not call the mouse events.
ORIGINAL:
I have a simple setup in my Mac app. I have a main NSWindow, as any usual mac app, then I run the following code which displays my detail view.
My detail view is an NSWindowController subclass and I am displaying it as a sheet. This is setup as an NSPanel in the xib and just displays a detail view for me over my main window.
APDetailWindowController *vc = [[APDetailWindowController alloc] initWithWindowNibName:#"APDetailWindowController"];
self.detailWindowController = vc;
self.detailWindowController.delegate = self;
APAppDelegate *appDelegate = [[NSApplication sharedApplication] delegate];
[NSApp beginSheet:self.detailWindowController.window modalForWindow:appDelegate.window modalDelegate:self didEndSelector:nil contextInfo:nil];
In my main window (of the AppDelegate), I have my views played out as any app, including some NSButtons. These buttons are subclassed so that when they receive mouseEntered and mouseExited events, they change colour. Basically creating a rollover effect.
In my detail view, I also have buttons of the same subclass. However, they are not receiving the mouse events at all. I can see when moving my mouse over them nothing happens. Even when the detail view is displayed, the rollover works on the main window buttons in the background, but not in my detail view.
I am thinking maybe some sort of focus is still on the main window and needs to be set to the detail window?

How to open an NSWindowController from another NSWindowController in Cocoa

I'm developing an app in cocoa for MacOSX in Xcode5 and I want to open another window from my current window by pressing a button, this is my code:
- (IBAction)openWindow:(id)sender {
WindowController *controllerWindow = [[WindowController alloc] initWithWindowNibName:#"WindowController"];
[controllerWindow showWindow:nil];
[[controllerWindow window] makeMainWindow];
}
so far I can see the window appearing by one second and then this just dissappear, how to do this correctly???
Neither the window nor the window controller have a strong reference anywhere outside the scope of this method.
So after that, they're getting released.
Normally, you would add your window controller to some container like an array in your app delegate.
The array will retain the window controller.
The window controller can hang on to the window.
It also makes sense for the action method to be in the app delegate. You button should just send a selector up the responder chain.
use this..
Create a new .h & .m files which yo need to open, as NewWindowController (for eg.) along with its .xib
And on any button click, to open the newly defined window, just allocate, instantiate and present..
NewWindowController *controllerWindow = [[NewWindowController alloc] initWithWindowNibName:#"NewWindowController"];
[controllerWindow showWindow:self];

Cast NSWindow to NSPanel

I am accessing a Virtual keyboard (external application - in Adobe Flex).
I want that keyboard should be non focusable. So I have to apply
styleMask:NSNonactivatingPanelMask
But am accessing the keyboard as
NSWindow *myMainWindow = [[[NSApplication sharedApplication] windows] objectAtIndex:0];
as NSNonactivatingPanelMask can only be applied to the NSPanel only
If I can type cast the NSWindow to NSPanel (?) then it possible.
----------My Previous question------------
Return focus to Editor after clicking a button in floating window of MAC
An NSPanel is an NSWindow, the reverse is not true - inheritance doesn't work both ways!
Furthermore casting an object reference from one type to another does not change the actual type of the reference object, so even if you cast an A * to a B * then invoking a method gets you exactly the same method as without the cast - the cast serves to inform the compiler that you know the actual object referenced is a different type and so quietens the compiler when you invoke a B method.
Even if you could get past all that, you state you want the keyboard to be non-focusable, which is not the same as non-activating - the former is about being an applications main window, the latter is about accepting input without activating an application.
The main window of an application is the one which is focussed, its frame highlighted in some way, etc. The key window of an application is the one which is accepting user input. They are often the same window, but need not be. It sounds like you want your keyboard to by the key window without being the main window - i.e. behave like a panel.
NSWindow has methods canBecomeMainWindow and canBecomeKeyWindow which determine whether a window can become main or key respectively. While you cannot change what these return for an NSWindow instance you can subclass NSWindow and override these methods - this is what NSPanel does - see the NSWindow documentation for these methods. So if you, say, define KeyboardWindow as an NSWindow subclass and override canBecomeMainWindow to return NO. Do this and you have a window which will not become main (focussed) but can accept input.
HTH
AS provided by #ashirbad
NSWindow *mainWindow = [[NSApp windows] objectAtIndex: 0];
NSView *mainContentView = [mainWindow contentView];
NSPanel *mainPanel = [[NSPanel alloc] initWithContentRect:[mainContentView frame]
styleMask:NSBorderlessWindowMask | NSNonactivatingPanelMask
backing:NSBackingStoreBuffered defer:YES];
[mainPanel setContentView:mainContentView];
[mainPanel setLevel:NSScreenSaverWindowLevel];
[mainPanel makeKeyAndOrderFront:nil];
[mainContentView orderOut:nil]; //error
This solved my problem.
But I'm getting an error orderOut is not defined in NSView.
If the top app is a Mac application then it's ok But if it is a flex app (in this case). The application not responding at this line.
[mainPanel setContentView:mainContentView];

Open and close NSWindow with NSButton through NSWindowController?

I've got a custom NSButton that I'm actually sticking in an NSStatusItem. When the NSButton is clicked, it launches my window. When the NSButton is clicked again, the window should close.
If the window is open, it appears as if the NSButton stops responding (or doesn't receive) click events! Here's the relevant code:
[statusItem setView:myCustomButton];
[myCustomButton setAction:#selector(showWindow:)];
- (void)showWindow:(id)sender {
if(!myWindowController) {
myWindowController = [[MyWindowController alloc] initWithWindowNibName:#"MyWindow"];
}
[myWindowController showWindow:statusItem];
[myWindowController.window orderFront:nil];
}
Am I doing something crazy? If I set a breakpoint in the above, it is hit when the button is clicked the first time but is not hit when the button is clicked again.
I'm guessing that you're not setting a target on the button. If a button has an action but no target, it gets sent up the responder chain. When the new window is shown, the responder chain is probably being changed, which means your action is being sent to a different place.
tl;dr: try setting a target on the button.