Child Windows Not Being Added Properly - macos

I'm trying to add a secondary window to bottom of my main window in an OS X application, with the hopes of eventually making it so it animates out from underneath the window when a toggle button is pressed. As far as I can tell, the best way to do this with the SDK is to add a window as a child window using - [NSWindow addChildWindow:ordered:]. However, when I call that function, although the secondary window is displayed, it isn't added as a child window. Here's the function called when I press the button:
- (IBAction)childToggleButtonPressed:(id)sender {
[self.window addChildWindow:_secondaryWindowController.window ordered:NSWindowBelow];
NSLog(#"Child Windows: %#", [[self.window childWindows] count]);
}
(I haven't added the code to dismiss it yet because I'm making sure it shows up in the first place first.)
And here's the output to the console:
2011-08-31 12:37:25.312 Playground[1712:707] Child Windows: (null)
Does anyone know why the window isn't being added as a child and what I can do to fix this?
Some additional context that might help is that I'm drawing a custom window using an image as the background for both the window itself and the title bar. The code I'm modifying can be found at http://cocoawithlove.com/2008/12/drawing-custom-window-on-mac-os-x.html.
Thank you!
EDIT: I tried overriding the - [NSWindow addChildWindow:ordered:] function and logging any information I could find, and it turns out the window itself isn't passed to the function. Hopefully this will help someone find out the problem!

I ended up solving this problem by making it so that the child windows did not have NSWindowControllers. Apparently you can't assign the window of an NSWindowController to be a child window. As soon as I turned them into NSWindow subclasses, I could add them as child windows using the code I showed above (replacing _secondaryWindowController.window with the window, of course).
In short, don't use an NSWindowController's window as a child window, use just an NSWindow.

Related

Re-open an NSWindow after it has been closed?

I have a subclass of an NSWindowController called UpgradeWindowController.
So far this works for the first click; the window launches. However if you close that window, and click the button again to show upgrade window, nothing happens.
- (IBAction)showUpgradeWindow:(id)sender {
if (!self.upgradeController){
self.upgradeController = [[UpgradeWindowController alloc] initWithWindowNibName:#"UpgradeWindow"];
}
[self.upgradeController showWindow:self];
}
Any ideas? Thanks
Ok, the problem was the XIB. The file's owner needed to be connected to the Window.
Give you a suggestion, the window can be objects stored in a variable or array, although this is closed, but is actually hidden, the next time we need to use it, can go to check the window if the object is loaded, if loaded to call and display it. This can be more interesting and convenient.

How do I prevent the menu bar from moving down when my popover is open?

I have an app with a popover that appears on a status bar item. The thing is, when you click on the icon while you're in a full screen app, then move the mouse away from the menu bar to click on something in the popup, the menu bar moves up, and so does the popup. It's annoying.
Anyone know of any way to solve this? I've tried attaching an invisible menu to the popup, but I can't get the menu to be invisible.
Screenshot for clarity, the annoying part is where I wave my mouse around:
The popover window is moving because its parent window is the status item window, and when the parent window moves, the child moves with it. (Before I investigated this, I didn't even know Cocoa had parent and child windows.) I solved the problem with this code immediately after showing the popover:
NSWindow *popoverWindow = self.popup.contentViewController.view.window;
[popoverWindow.parentWindow removeChildWindow:popoverWindow];
Now, the menu bar still moves up, but at least the popup stays in the same place.
Either use Carbon events or watch for things happening to the menu bar (window of type NSStatusBarWindow):
Notifications of type
NSWindowDidChangeOcclusionStateNotification
NSWindowDidMoveNotification
NSWindowWillCloseNotification
NSWindowDidCloseNotification
with an object of class NSStatusBarWindow should give you enough information about the menu bar showing or hiding to add proper handling.
Super-hacky approach:
Custom window with some super-high window level to make it appear over the menu bar, then add a transparent custom view to the new window that catches and handles/blocks mouse clicks according to your needs.
Or:
Get the window instance the popover is using to display and track/handle NSWindowWillMoveNotification / NSWindowDidMoveNotification.
I converted #tbodt's answer to Swift 4 and confirmed that is resolves this issue:
let popoverWindow = popup.contentViewController.view.window as? NSWindow
popoverWindow?.parent?.removeChildWindow(popoverWindow!)

NSStatusWindowLevel disabled when presenting window sheet

I'm using an NSWindow and set it's level to NSStatusWindowLevel :
[aWindow setLevel:NSStatusWindowLevel];
Now, when I present a sheet (self is my sheet window)
modalAnswer = 0;
[NSApp beginSheet:self modalForWindow:aWindow modalDelegate:self didEndSelector:NULL contextInfo:nil];
[self makeKeyAndOrderFront:nil];
then the window doesn't show up above the dock, it hides underneath (meaning that NSStatusWindowLevel doesn't work anymore).
I've tried changing the level of the sheet window, but it won't work.
What's causing this issue?
I asked for technical support and they found it was a silly mistake : sheets normally are modal, but I was making it modal myself too.
The combination of beginModalSessionForWindow and runModalSession is the issue here.
It appears to be servicing the default run loop which is causing the menu bar and Dock to appear over your window.
I'm a bit confused as to why you are calling beginSheet:modalForWindow, and then using NSApp's beginModalSessionForWindow. Why do you manually add a modal session? Sheets are automatically modal to whatever window it is attached to. I am used to seeing the use of "beginModalSessionForWindow" without sheets on top of them.

Make NSView in NSPanel first responder without key window status

Is it possible to give an NSView inside an NSPanel first responder status without giving the NSPanel key window status (making the main application window resign key)?
Thanks.
Well, I ended up figuring this one out, but it took a lot of research so I'll post the details here in case anyone else runs into the same problem. First of all, a few basics:
It's impossible to have 2 windows actually be key at the same time
It's possible to fake a window into thinking it's key by overriding -isKeyWindow but that won't give the views contained in the window first responder status.
My Scenario:
I added a child window containing an NSTableView into my main application window (the reason is irrelavant). The child window was an NSPanel with NSBorderlessWindowMask. I wanted to give the NSTableView first responder status without making the panel the key window because it took away focus from the main window (and the whole point of the child window illusion was to make the child window look like it was part of the main window).
The first thing I tried was fooling the table view into thinking that it was inside the key window by overriding isKeyWindow to return YES. This made the table view draw as if it were the first responder, but still did not give it first responder status.
The Solution:
So by default, NSBorderlessWindowMask will not allow the window to become key. To make the table view first responder, the window had to be key so I overrode canBecomeKeyWindow in the borderless window subclass to return YES. This, of course, took away key status from the main window, which was one of the things I wanted to avoid. To fix this, I subclassed my main window and overrode the following methods:
- (BOOL)isMainWindow
{
return YES;
}
- (BOOL)isKeyWindow
{
return ([NSApp isActive]) ? YES : [super isKeyWindow];
}
This subclass checks if the application is active, and if it is, it always returns YES so that no matter what window is active in your application, the main window will always behave as if it is still key. This sort of gives the illusion that you can have multiple windows be key at the same time and enables you to shift key window status to another window without losing it on your main window. Hope this helps!

Cocoa/Objective-C - Child window with text input without main window becoming inactive

I have a need to spawn a window that will hover just above my main window in a cocoa application. I want this main window to allow the user to enter some text in an input box. All is well until the text input box actually gains focus. The main window becomes "deactivated." This window is borderless and is a slightly custom shape -- its more like a hover card than anything else, I suppose.
Basically, I'd like this thing to work almost exactly like Spotlight (Apple + Space) -- you can enter text, but this is such an an ancillary operation that in the context of the greater UX, you don't want the jarring effect of the main window graying out (becoming inactive). You'll notice when you have some application open and in-focus, spotlight will not cause the window of that application to become inactive.
This problem arises because text input seems to REQUIRE that the child window become the key window (it will not let you place the cursor in the text input field). When it becomes key, the main window becomes inactive.
So far I've tried:
Subclassing NSWindow for my main application and overriding isKeyWindow such that it only loses key when the application is no longer the users focus (as opposed to the window). This had the unintended effect of colliding with key status of the child window and having very strange effects on the keyboard input (some keys are not captured, like delete)
Creating a view instead of a window. Doesn't work because of this problem -- you cannot draw over a Webkit WebView these days.
Anybody Cocoa/OSX wizards have any ideas? I've become a little obsessed with this one. An itch I can't scratch.
Edit: have also tried overriding the following on the child window. When clicked, the window makes the main application window become inactive.
- (BOOL)canBecomeKeyWindow {
return YES;
}
- (BOOL)canBecomeMainWindow {
return NO;
}
Edit 2:
After screwing about with NSMenu for a while, I scrapped that approach. It seems I found something, however. In NSPanel there is a window style mask called:
NSNonactivatingPanelMask
The panel can receive keyboard input without activating the owning application.
Valid only for an instance of NSPanel or its subclasses; not valid for a window.
Trying this out now...
Edit 3: NSNonactivatingPanelMask did not do the trick. No ideas.
What you want is a window that can become the key window but which cannot become the main window. You could implement such a class yourself, but this is basically what NSPanel is for, so you might try that first.
I think this can help you:
[self.childWindow makeKeyAndOrderFront:self];

Resources