Added NSWindow as child of another window, but not persisting - objective-c

My goal is to position an NSWindow relative to another NSWindow, such that when the parent is dragged around, the child moves along with it.
When I poke around with the debugger, I can see the NSWindow parenting relationship getting established correctly. However, in a later part of my code, that same NSWindow returns nil when I try to fetch the parent. I figured only an explicit call to removeChildWindow would eliminate the parenting, unless there's something else going on here like I'm setting up the parenting incorrectly. Are there other ways for an NSWindow to lose its parent/child relationship?
someFunction(NSWindow* parentWindow) {
NSWindow* wnd = [[NSWindow alloc] initWithContentRect:frame
styleMask:NSBorderlessWindowMask
backing:NSBackingStoreBuffered
defer:NO];
[parentWindow addChildWindow:wnd ordered:NSWindowAbove];
}
later:
anotherFunction(NSWindow* window) {
NSWindow* parentWindow = [window parentWindow]; // debugger shows nil
}

It seems I had a call to change the window visibility to false:
[wnd setIsVisible:false];
And when I tried reading its parent again, that ends up being nil. Who would have guessed it, this doesn't appear to be documented.

Related

NSWindow closing and controlling issues in Mac OS X

I met the issues of NSWindow regarding closing it when the application starts. There are plenty of examples, however, I can not get the proper effect, perhaps I missing something.
Firstly, in many examples there is the recommendation to use "[self window]" but I get the error like "No visible #interface for 'ViewController' declares the selector 'window'".
Then I use the round way: "[[self view] window]". Anyway, the window is not closed after the execution of the code:
NSWindow *win = [[self view] window];
[win performClose:self];
or
NSWindow *win = [[self view] window];
[win close];
The next one also does not give any results as I expect, according to the documentation:
[win orderOut:self];
The code compiles but I can see the window. Of course, I tried:
NSLog(#"%#", [win.windowController windowShouldClose:self] ? #"YES" : #"NO" );
It outputs "NO", so, it means that the window, which appear when I run my application, cannot be closed? Is there any way how to work around it? Why I cannot control that main window following the way the documentation suggests?
I checked for the import "#import AppKit/AppKit.h;" as well.
All of this suggests that win is nil. The view of your view controller is not in a window.

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

Unexpected behavior of NSTextView (because of titlebar-less NSWindow?)

I have a NSWindow (my main window) and a child window (positioned NSWindowBelow the main window) that has a NSTextView. The child window doesn't have a title bar, nor shadow and its transparent.
Here's the code I use to set up my child window to make it transparent:
- (id) initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)aStyle backing:(NSBackingStoreType)bufferingType defer:(BOOL)flag{
if (![super initWithContentRect: contentRect styleMask:NSBorderlessWindowMask backing: bufferingType defer:NO]) return nil;
[self setBackgroundColor: [NSColor clearColor]];
[self setOpaque:NO];
return self;
}
But when I try to select the text in it, here's what happens (the black stuff above the child window is the main window):
It looks like the NSTextView is not focused, because the selection is not blue. I've tried calling: [[_childWindow textView] becomeFirstResponder]; but the outcome is the same. Another thing, is that when I scroll it, sometimes it's very laggy and "breaky".
Do you guys have any ideas on whats causing this and how to fix it? I suspect it's because the window doesn't have a title bar, but I'm not sure.
Thanks!
From the NSWindow docs:
canBecomeKeyWindow
Indicates whether the window can become the key window.
- (BOOL)canBecomeKeyWindow
Return Value
YES if the window can become the key window, otherwise, NO.
Discussion
Attempts to make the window the key window are abandoned if this method returns
NO. The NSWindow implementation returns YES if the window has a title bar or a
resize bar, or NO otherwise.
Try overriding -canBecomeKeyWindow and returning YES.

Show Window without activating (keep application below it active)

I need to show a window (without title bar) above third party applications without my window taking focus.
I have tried using an NSPanel and setting enabling non-activating, but that didn't help.
I tried orderFront:self, but that didn't help either.
I always needed to add [NSApp activateIgnoringOtherApps:YES]; because the window wouldn't show otherwise.
I have here a sample project for just this functionality:
http://users.telenet.be/prullen/TopW2.zip
UIElement is set to true in the application's plist file, so there is no dock. You can activate the window by pressing ALT + SPACE at the same time. You will see that the app below it looses focus. Any thoughts on how to fix this? I've seen other apps do it so I know it's possible.
Edit: here's the code so far. Remember the window is a non-activating NSPanel.
I still need that last NSApp activateIgnoringOtherApps line or otherwise it doesn't display. But of course that makes the window the active one.
_windowController = [[MyWindowController alloc] initWithWindowNibName:#"MyWindowController"];
[[_windowController window] setLevel:NSNormalWindowLevel+1];
[[_windowController window] orderFrontRegardless];
[_windowController showWindow:self];
[NSApp activateIgnoringOtherApps:YES];
I've also subclassed NSPanel and added two methods:
- (BOOL)canBecomeKeyWindow
{
return YES;
}
- (BOOL)canBecomeMainWindow
{
return YES;
}
Edit: OK, unchecking setHidesOnDeactivate fixes this, but now the window will never hide. I need it to hide when the user presses the app below it or switches to another app.
Edit 2: OK, this seems to fix the above issue:
- (void)awakeFromNib
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(hideWindow) name:NSWindowDidResignKeyNotification object:nil];
}
- (void)hideWindow {
[self setHidesOnDeactivate:YES];
}
Not sure if there's a better way.
And for those that want to know how to display the window:
[[_windowController window] setLevel:NSPopUpMenuWindowLevel];
[[_windowController window] orderFrontRegardless];
[[_windowController window] makeKeyWindow];
[_windowController showWindow:self];
Either one of these should do the trick:
Use -[NSWindow orderFrontRegardless] to get a normal level window to the front without activating the corresponding app, or
Use -[NSWindow setLevel:] to increase the window level to something higher than NSNormalWindowLevel
Not to take away from #puzzle's useful answer, but it sounds like your problem has something to do with using an NSPanel instead of an NSWindow.
The "How Panels Work" docs say:
Onscreen panels, except for alert dialogs, are removed from the screen when the application isn’t active and are restored when the application again becomes active. This reduces screen clutter.
Specifically, the NSWindow implementation of the hidesOnDeactivate method returns NO, but the NSPanel implementation of the same method returns YES.
So perhaps you could override hidesOnDeactivate to return NO, or change to NSWindow

NSView's context NSMenu is never shown even though all the right methods are being called

I have an NSCollectionView with a bunch of NSViews in it, stacked vertically, to make it look a bit like UIKit's UITableView. Everything works as expected, except for one thing:
When right-clicking any one of the NSViews, I expect the NSMenu I set to be view's menu to be shown, but alas - nothing happens.
The crazy part is all the right methods are being called, exactly as could be expected: -rightMouseDown:, -menuForEvent: and finally -menu.
When I set up any object as the NSMenu's delegate, menuWillOpen: is not called, so it seems to me something fails over on Apple's side of things, just in between asking for the menu, and actually showing it.
Would anyone be able to shed a light on this?
Thanks in advance.
PS. For what it's worth, NSMenus I present manually (without relying on Apple's right-click handling) using popUpMenuPositioningItem:atLocation:inView: are shown.
Edit / Update / Clarification
The NSCollectionView in question is inside an NSWindow that's being shown when an NSStatusItem is clicked, like CoverSutra/TicToc/what have you. Some code from the MyWindow NSWindow subclass:
- (void)awakeFromNib {
[self setStyleMask:NSBorderlessWindowMask];
[self setExcludedFromWindowsMenu:YES];
}
- (BOOL)canBecomeMainWindow {
return YES;
}
- (BOOL)canBecomeKeyWindow {
return YES;
}
- (BOOL)isMovable {
return NO;
}
- (void)presentFromPoint:(NSPoint)point {
point.y -= self.frame.size.height;
point.x -= self.frame.size.width / 2;
[self setFrameOrigin:point];
[self makeMainWindow];
[self makeKeyAndOrderFront:self];
}
presentFromPoint: is the method I use to present it from any point I like, in my case from just below the NSStatusItem. (Not really relevant to this problem)
My application has LSUIElement in its Info.plist set to YES by the way, so it doesn't show a menu bar or a Dock icon. It lives in the status bar, and has a window that's shown when the NSStatusItem is clicked.
The view hierarchy is as follows:
MyWindow => contentView => NSScrollView => NSCollectionView
The NSCollectionView has an NSCollectionViewItem subclass connected to its itemPrototype property, and the NSCollectionViewItem subclass has an NSView subclass connected to its view property.
The NSView subclass, in turn, has an NSMenu connected to its menu property.
And last but not least: This NSMenu has one NSMenuItem sitting inside it.
Both the NSCollectionViewItem subclass and the NSView subclass do nothing interesting as of now, they're just empty subclasses.
The NSMenu connected to the NSView's menu property is what should be shown when the NSView is right-clicked, but as I hope I have made clear: It isn't actually shown.
Update
I still have no idea what caused this problem, but I've decided to 'move on' from NSCollectionView, as it wasn't really fit for what I was trying to do anyway, and I am now using TDListView which works like a charm.