NSWindow subclass does not draw title bar on second rendering - objective-c

I'm writing some glue GUI code for a scripting language implementation, and need to be able to programmatically create windows and intercept the key and mouse commands on their views. I've subclassed both NSWindow and NSView.
When I open a window for the first time, I get a window with a title bar and controls. If I close this window and open another, the controls and title don't appear in the new window. However, if I click where the controls should be, the new window (with invisible controls) still closes.
Is there something I'm doing in my window initialization that could cause this?
+ (HMSLWindow*)hmslWindowWithTitle:(NSString *)title frame:(NSRect)frame {
HMSLWindow* hmslWindow = [[HMSLWindow alloc]
initWithContentRect: frame
styleMask: NSMiniaturizableWindowMask | NSTitledWindowMask | NSClosableWindowMask
backing: NSBackingStoreBuffered
defer: YES];
hmslWindow.title = [title retain];
[hmslWindow setContentView:[[HMSLView alloc] initWithFrame:frame]];
hmslWindow.delegate = [[HMSLWindowDelegate alloc] init];
[hmslWindow cascadeTopLeftFromPoint:NSZeroPoint];
[hmslWindow makeKeyAndOrderFront:self];
[[HMSLWindow windowDictionary] setObject:hmslWindow forKey:[NSNumber numberWithInteger:hmslWindow.windowNumber]];
return hmslWindow;
}
- (void)close {
[[HMSLWindow windowDictionary]
removeObjectForKey:[NSNumber numberWithInteger:self.windowNumber]];
[self.contentView autorelease];
[self.delegate autorelease];
[self.title autorelease];
[super close];
}
First window:
Second window (contentView with white background is correct, but the titlebar is now empty):

Related

Disable focus on second screen in objective-c

i have an app and i try it on multi screen. I have two screens and i want to disable focus on the second screen when the both sreens are enterFullscreen, i want to force the focus on the main screen.
I tried solutions i found here but doesn't change anything.
following code shows how i enterFullScreen for my mainWindow and my second Window
[self.window.contentView enterFullScreenMode:[[NSScreen screens] firstObject] withOptions:nil];
[windowArray insertObject:self.window atIndex:0];
NSRect screenRect;
NSArray *screenArray = [NSScreen screens];
for (NSInteger index = 1; index < [screenArray count]; index++)
{
NSScreen *screen = [screenArray objectAtIndex: index];
screenRect = CGRectMake(0, 0, screen.frame.size.width , screen.frame.size.height);
NSWindow *window = [[NSWindow alloc] initWithContentRect:screenRect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO screen:screen];
[window.contentView setWantsLayer:YES];
window.contentView.layer.backgroundColor = [NSColor blackColor].CGColor;
[window.contentView enterFullScreenMode:[[NSScreen screens] objectAtIndex:index] withOptions:nil];
[windowArray addObject:window];
}
When both screens are in fullScreen mode, when i click on the second window i have the focus on the second window (normal event) but i want to disable that and force to put the focus on my main Window. I tried to disable mouse event on the second screen but.. not working.
If someone can help me ! thanks in advance
I don't know if it's the best answer for you, but you can iterate through all the subviews of the content view, and call setEnabled: on all of them. You can see a better and complete answer here: https://stackoverflow.com/a/16336624/2923506
for (NSView *object in [self.window.contentView subviews]) {
// Check it out the object
// set enable to NO [(id)object setEnabled:NO];
}

How to override a fullscreen game with keywindow borderless window (overlay)?

I've created the app which sometimes shows up an overlay with label and textbox. It works nice, but I need it to work even with other apps are in full-screen mode and active.
For overlay, I've create custom window class and overridden canBecomeKeyWindow method to let borderless window become the key window (simply returns YES).
So it works, but when I run e.g. Minecraft, and then make it full screen, my overlay can override it. But I can't type in NSTextField in the overlay. How to fix it?
I'm creating an overlay like this:
[[NSWorkspace sharedWorkspace] hideOtherApplications];
NSRect frame = [[NSScreen mainScreen] frame];
_fadedWindow = [[CustonWindow alloc] initWithContentRect:frame
styleMask:NSBorderlessWindowMask
backing:NSBackingStoreBuffered
defer:NO];
[_fadedWindow setAcceptsMouseMovedEvents:YES];
[_fadedWindow setOpaque:NO];
[_fadedWindow setLevel:CGShieldingWindowLevel()];
[_fadedWindow setBackgroundColor:[NSColor colorWithDeviceRed:0.0 green:0.0 blue:0.0 alpha:0.8]];
NSApplicationPresentationOptions options = NSApplicationPresentationDisableProcessSwitching + NSApplicationPresentationHideDock + NSApplicationPresentationDisableForceQuit + NSApplicationPresentationDisableSessionTermination + NSApplicationPresentationDisableHideApplication;
[NSApp setPresentationOptions:options];
_fadedWindow.alphaValue = 0;
[_fadedWindow orderFrontRegardless];
[[_fadedWindow animator] setAlphaValue:1];
[_fadedWindow toggleFullScreen:self];
[_fadedWindow makeKeyAndOrderFront:self];
[NSApp activateIgnoringOtherApps:YES];
[_fadedWindow orderFront:self];
But still, I can't seem to populate overlay's NSTextField with keyboard input.
Try this. Create a subclass for _fadedWindow. Then put this in:
-(BOOL)canBecomeKeyWindow {
return YES;
}

Cocoa/OSX - NSWindow standardWindowButton behaving strangely once copied and added again

In my app I change the position of the standardWindowButtons close / miniturize / expand like so:
//Create the buttons
NSButton *minitButton = [NSWindow standardWindowButton:NSWindowMiniaturizeButton forStyleMask:window.styleMask];
NSButton *closeButton = [NSWindow standardWindowButton:NSWindowCloseButton forStyleMask:window.styleMask];
NSButton *fullScreenButton = [NSWindow standardWindowButton:NSWindowZoomButton forStyleMask:window.styleMask];
//set their location
[closeButton setFrame:CGRectMake(7+70, window.frame.size.height - 22 - 52, closeButton.frame.size.width, closeButton.frame.size.height)];
[fullScreenButton setFrame:CGRectMake(47+70, window.frame.size.height - 22 -52, fullScreenButton.frame.size.width, fullScreenButton.frame.size.height)];
[minitButton setFrame:CGRectMake(27+70, window.frame.size.height - 22 - 52, minitButton.frame.size.width, minitButton.frame.size.height)];
//add them to the window
[window.contentView addSubview:closeButton];
[window.contentView addSubview:fullScreenButton];
[window.contentView addSubview:minitButton];
Now when the window appears with the buttons there is two problems:
1. They are grey and not their correct color
2. when the mouse is over them they do not show the + - or x sign
can anyone tell me what I am doing wrong. Thanks.
Here is the mechanics of this hover magic: Before drawing itself standard circled button (such as NSWindowMiniaturizeButton) calls their superview undocumented method _mouseInGroup:. If this method returns YES circled button draws itself with icon inside. That's all.
If you place these buttons inside your own view, you can simply implement this method and control this mouse-hover-appearance as you want. If you just move or relayout these buttons and they still be subviews of NSThemeFrame (or something similar), you have to swizzle method _mouseInGroup: for this class, and probably it doesn't worth it because we have perfectly simple previous method.
In my case I have custom NSView that contains my standard buttons as subviews and this code makes all described above magic:
- (void)updateTrackingAreas
{
NSTrackingArea *const trackingArea = [[NSTrackingArea alloc] initWithRect:NSZeroRect options:(NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways | NSTrackingInVisibleRect) owner:self userInfo:nil];
[self addTrackingArea:trackingArea];
}
- (void)mouseEntered:(NSEvent *)event
{
[super mouseEntered:event];
self.mouseInside = YES;
[self setNeedsDisplayForStandardWindowButtons];
}
- (void)mouseExited:(NSEvent *)event
{
[super mouseExited:event];
self.mouseInside = NO;
[self setNeedsDisplayForStandardWindowButtons];
}
- (BOOL)_mouseInGroup:(NSButton *)button
{
return self.mouseInside;
}
- (void)setNeedsDisplayForStandardWindowButtons
{
[self.closeButtonView setNeedsDisplay];
[self.miniaturizeButtonView setNeedsDisplay];
[self.zoomButtonView setNeedsDisplay];
}
I'm fully aware that this question is old and Valentin Shergin's answer is correct. It prevents the utilize of any Private API, unlike Google did in Chrome. Just wanted to share a method for those who don't feel like subclass NSView just to put those buttons in an existed view (such as self.window.contentView).
As I just wanted to reposition the NSWindowButtons via setFrame:, I found out that once the window was resized, the tracking areas seems to "fix" themselves automagically, without any Private API usage (at least in 10.11).
Thus, you can do things like the following to apply "fake resize" to the window that you repositioned your buttons:
NSRect frame = [self.window frame];
frame.size = NSMakeSize(frame.size.width, frame.size.height+1.f);
[self.window setFrame:frame display:NO animate:NO];
frame.size = NSMakeSize(frame.size.width, frame.size.height-1.f);
[self.window setFrame:frame display:NO animate:YES];
(I did it within my main window's NSWindowDelegate windowDidBecomeMain:. Should work as long as the window is loaded and visible.)
You're not adding them again. You're moving them to contentView. The buttons are originally in window.contentView.superview.
[window.contentView.superview addSubview:closeButton];
[window.contentView.superview addSubview:fullScreenButton];
[window.contentView.superview addSubview:minitButton];
Should get you the correct behaviour without requiring a trackingArea.
Call [button highlight:yes] for each button.

Dismiss Custom Window for NSStatusItem

I've got a custom window popping up when an NSStatusItem is clicked. The code is based on MAAtachedwindow. Everything is working great but I can't figure out a way to dismiss the window when the user clicks on something else like another status bar item, or another app.
Here's my code for creating the window:
statusItem = [[[NSStatusBar systemStatusBar] statusItemWithLength:width] retain];
//setup custom status menu view
CGFloat height = [[NSStatusBar systemStatusBar] thickness];
NSRect viewFrame = NSMakeRect(0.0f, 0.0f, width, height);
statusMenuView = [[[_ISStatusMenuView alloc] initWithFrame:viewFrame] retain];
statusMenuView.offset = aOffset;
statusItem.view = statusMenuView;
//setup the window to show when clicked
NSRect contentRect = NSZeroRect;
contentRect.size = aView.frame.size;
statusMenuWindow = [[[NSWindow alloc] initWithContentRect:contentRect
styleMask:NSBorderlessWindowMask
backing:NSBackingStoreBuffered
defer:NO] retain];
[statusMenuWindow setLevel:NSPopUpMenuWindowLevel];
[statusMenuWindow setBackgroundColor:[NSColor clearColor]];
[statusMenuWindow setMovableByWindowBackground:NO];
[statusMenuWindow setExcludedFromWindowsMenu:YES];
[statusMenuWindow setOpaque:NO];
[statusMenuWindow setHasShadow:NO];
[statusMenuWindow useOptimizedDrawing:YES];
[[statusMenuWindow contentView] addSubview:aView];
[statusMenuWindow setDelegate:self];
statusMenuView.statusMenuWindow = statusMenuWindow;
And here is how I'm showing the window:
- (void)centerView{
NSRect menuFrame = self.window.frame;
NSRect windowFrame = self.statusMenuWindow.frame;
NSPoint menuPoint = NSMakePoint(NSMidX(menuFrame), NSMinY(menuFrame));
menuPoint.x -= windowFrame.size.width*0.5f;
menuPoint.y -= windowFrame.size.height+self.offset;
[self.statusMenuWindow setFrameOrigin:menuPoint];
[self.statusMenuWindow makeKeyAndOrderFront:self];
}
I was hoping the windowDidResignKey delegate method would do the trick but it doesn't go off with this configuration. The delegate is working because windowDidMove does run.
- (void)windowDidResignKey:(NSNotification *)notification{
NSLog(#"windowDidResignKey");
[statusMenuView hideView];
}
- (void)windowDidResignMain:(NSNotification *)notification{
NSLog(#"windowDidResignMain");
}
- (void)windowDidMove:(NSNotification *)notification{
NSLog(#"windowDidMove");
}
So to recap, how can I hide my custom window when the user clicks on anything else, the way the standard status bar menus work?
Edit
After looking at the Popup example the only thing I was missing was I had to subclass NSPanel and make it so it could become the key window.
#interface Panel : NSPanel
#end
#implementation Panel
- (BOOL)canBecomeKeyWindow{
return YES;
}
#end
You need to make sure your window can become the key window, and call your window's orderOut: method when it resigns key. You should be using a custom NSWindowController, if you are not already, in which case you would just call its "close" method to dismiss your window.
Instead of posting a bunch of code, I would suggest you just look at this excellent, recently-posted example of attaching a window to a status item:
Shpakovski Popup Window Example

NSOpenPanel above a fullscreen NSWindow?

I open a window with the following:
NSRect screenRect = [[NSScreen mainScreen] frame];
[super initWithContentRect:screenRect
styleMask:NSBorderlessWindowMask
backing:NSBackingStoreBuffered
defer:NO];
int windowLevel = CGShieldingWindowLevel();
[self setLevel:windowLevel];
... so the window is fullscreen & above all other window levels (including modal windows). I later want to display an open panel, however the following opens the dialog below the window I created above (it seems that the runModal stuff overrides the requested window level I try to set):
NSOpenPanel *OP = [NSOpenPanel openPanel];
int windowLevel = CGShieldingWindowLevel();
[OP setLevel:windowLevel];
int returnCode = [OP runModal];
... and the following opens a sheet on the window created above (good), however it also winds up showing the menu bar, which I had previously hidden (not what I want):
NSOpenPanel *OP = [NSOpenPanel openPanel];
[OP beginSheetModalForWindow:[self window]
completionHandler:^(NSInteger returnCode) {
NSLog(#"completionHandler called with %d", returnCode);
}];
... so my questions are:
Does anyone know how to open a modal window above the CGShieldingWindowLevel ?
Is there any way to get the menu bar to not show up on the sheet solution I'm trying above ?
Thanks all :-)
OK, here's an even better option - missed this one completely when I was reviewing the docs:
NSOpenPanel *OP = [NSOpenPanel openPanel];
[OP setLevel:CGShieldingWindowLevel()];
[OP beginWithCompletionHandler:^(NSInteger returnCode) {
NSLog(#"completionHandler called with %d", returnCode);
}];
... ie: open the panel as it's own window, which was exactly what I wanted to do in the fist place (duh!)
You can create a category of NSSavePanel like this :
#implementation NSSavePanel (SavePanelSetLevel)
- (void)setLevel:(NSInteger)newLevel
{
[super setLevel:CGShieldingWindowLevel()] ; // NSWindow implementation call
}
#end
because runModal reset the level previously set !
OK, 5 years later, I sorta can make this work - the trick is to open up a second window, promote it to the CGShieldingWindowLevel, make it key & order front, then attach the open sheet to it - the sheet magically appears from wherever the second window is, and although not perfect, it looks a lot better than the solution I came up with originally. Here's the change:
NSOpenPanel *OP = [NSOpenPanel openPanel];
// this is the new bit - make the window 1x1 # the location of your liking
NSRect windowRect = NSMakeRect(0, 1000, 1, 1);
NSWindow *OPW = [[NSWindow alloc] initWithContentRect:windowRect
styleMask:NSBorderlessWindowMask
backing:NSBackingStoreBuffered
defer:NO];
int windowLevel = CGShieldingWindowLevel();
[OPW setLevel:windowLevel];
[OPW makeKeyAndOrderFront:nil];
// end of new bit, apart from passing OPW for beginSheetModalForWindow
// instead of [self window]
[OP beginSheetModalForWindow:OPW
completionHandler:^(NSInteger returnCode) {
NSLog(#"completionHandler called with %d", returnCode);
}];
... the only think to watch out for is that with the below you can wind up opening up several open dialogs, since the sheet is modal for a window other than the main window - the main window can still accept mouse click events ...