Show up a custom UI somewhere on screen - objective-c

I've programmed a custom UI which looks like this: http://www.youtube.com/watch?v=XLsrVVhEs94
Currently it is only working within a NSView itself but I want it to show up in every corner of my screen.
So I programmatically created an NSWindow like so [[NSWindow alloc] initWithContentRect:windowRect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered
defer:NO];
This works great but there is a problem: Every time I open this UI I can barley see that the NSWindow was just created. I can see a shadow that has the size of the NSWindow and after that it disappears (because of [NSColor clearColor]). I think that [NSColor clearColor] applies too slow to the just created NSWindow.
The NSWindow was set up with [window setOpaque:NO] so it is transparent.
Is there another way to display a custom UI somewhere on my screen?
- Timo

I think you want to set the defer to YES. Referring to the documentation, the defer property will either create the window immediately, or defer it until it is displayed on the screen. In this case you can then set all the window properties, add subviews, etc before showing on the screen.
NSWindow *myWin = [[NSWindow alloc] initWithContentRect:windowRect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:YES];
... do window setup here ...
[myWin orderFront:self];
Additionally if that still flickers, you can call 'display' on the window so it draws all the subviews into it's buffer first (including your clear), then call orderFront.

Related

NSView events randomly stop after several seconds when having a child window

I have a main window with main NSView handling its events. It works fine.
However, once I add a transparent child window, events stop being sent randomly after about 5 seconds, until I move the mouse. For example, if I scroll, it works fine for about 5 seconds, then the scrollWheel event is simply not triggered, until I move the mouse a bit.
(I need the transparent child window to display native elements on top of the Metal view).
Here's how I add the child window:
////
CGRect wRect = main_window.frame;
NSView *contentView = main_window.contentView;
CGRect cRect = contentView.frame;
CGRect rect = CGRectMake(wRect.origin.x, wRect.origin.y, cRect.size.width, cRect.size.height);
NSWindow *overlayWindow = [[NSWindow alloc]initWithContentRect:rect
styleMask:NSBorderlessWindowMask
backing:NSBackingStoreBuffered
defer:NO];
overlayWindow.backgroundColor = [[NSColor whiteColor] colorWithAlphaComponent:0];
[overlayWindow setOpaque:YES];
child_view = [[MyView2 alloc] init];
overlayWindow.contentView = child_view;
[main_window addChildWindow:overlayWindow ordered:NSWindowAbove];
///////////
[main_window center];
[main_window makeKeyAndOrderFront:nil];
If I remove the addChildWindow call, events work fine again.
Also it's quite interesting that if I don't set the child window's background to transparent via overlayWindow.backgroundColor = [[NSColor whiteColor] colorWithAlphaComponent:0];, no events are sent to the main window.
update
This applies only to mouse events (scroll, click). Keyboard events work fine.
update 2
I've noticed that all events stop immediately after a mouseExited event, and I have to move the cursor around randomly until I get a mouseEntered event, then the scrolling works again.
I solved this by adding a child view to the main view, instead of an entire new window:
[contentView addSubview:child_view];
I don't know what the issue with the child window was, could be a bug with transparent child windows.

Invisible Fullscreen View with Cocoa

I'm trying to create an application which change the mouse cursor, but to do that the cursor must be inside a NSView. To do this all the time, I would need to have an intangible and invisible fullscreen view.
I've created a subclass of NSView which implement the following code to became full screen in initWithFrame:
[self enterFullScreenMode:[NSScreen mainScreen] withOptions:nil];
And using that code, I can change the cursor inside the view:
- (void)resetCursorRects{
[self addCursorRect:[self bounds] cursor:appCursor];
}
The cursor changed successfully, but here comes the first problem: the view is in fullscreen, but it's visible, like a gray background. I've tried this method:
[[self window] setOpaque:NO];
And this one:
[[self window] setBackgroundColor:[NSColor colorWithWhite:1.0 alpha:0.0]];
But both of them just change the view color to black instead of gray. How can I fix that?

Is there Any way to imitate Lion's Launchpad?

I have been struggling to imitate Launchpad.
At the beginning I thought about making NSWindow bgcolor transparent:
//make NSWindow's bgcolor transparent
[window setOpaque:NO];
[window setBackgroundColor:[NSColor clearColor]];
But now I realized it's way more ideal to
capture wallpaper
blur it and make it bg-image for NSWindow or a view
Rather than hiding all the opened windows and menubar, which was the first idea I had have come with (Still not sure with better, if you had any better idea...).
Capture & blur wallpaper used by a user
Make it background image for nswindow or a view
Fade-in to fullscreen view
Click somewhere blank or press ESC to fade-out
Are those possible to achieve without using private APIs?
Sorry if it's not clear my poor English.
Simply I'm trying to imitate Launchpad-styled full screen.
Thanks for any advice.
To get an image of the desktop background, use:
NSURL *imageURL = [[NSWorkspace sharedWorkspace] desktopImageuRLForScreen:[NSScreen mainScreen]
NSImage *theDekstopImage = [[NSImage alloc] initWithContentsOfURL:imageURL];
You can blur the image using CIFilter. Here's a Apple doc describing how to apply filters.
And then you can load that image into a color and set that as the background color for the window. Additionally, set the window to have no style mask (no close buttons, title frame, etc.), cover the screen, and be in front of everything except the dock:
[yourWindow setBackgroundColor:[NSColor colorWithPatternImage:theDesktopImage]];
[yourWindow setStyleMask:NSBorderlessWindowMask];
[yourWindow setLevel:kCGDockWindowLevel - 1];
[yourWindow setFrame:[[NSScreen mainScreen] frame] display:YES];
To have the window fade in, you can use NSWindow's animator proxy. (Replace 1.0 with 0.0 to make it fade out.)
[[yourWindow animator] setAlphaValue:1.0];
Of course you could customize that a bit more with things like CoreAnimation, but this should work just fine.
To handle background clicking, I suggest making a subclass of NSView where you -orderOut: your window on -mouseDown:. Then put an instance of that subclass that spans the entire frame of your window.
Also, NSViews sometimes don't respond to key events, so you can add an event listener to detect any time the escape key is pressed while your app is active:
[NSEvent addLocalMonitorForEventsMatchingMask:NSKeyDownMask handler:(NSEvent *ev)^ {
if([ev keyCode] == 0x53) {
[yourWindow orderOut:self];
}
return ev;
}

NSScrollView in a NSWindow

I have an NSScrollView inside an NSWindow, but it seems to be disabled. It looks like it would work, but the scrollbars are unresponsive to the mouse or the scroll wheel.
When I put the exact same NSScrollView inside a window on a new XCode project, it works perfect. There is something about the way I am making the window that is preventing the scroll from working.
I've been able to simplify it to this example:
//Make a window
NSWindow* myWindow = [[NSWindow alloc] initWithContentRect:NSMakeRect(300, 300, 300, 300)
styleMask:NSTitledWindowMask
backing:NSBackingStoreRetained
defer:NO];
//Make a scroll view
NSScrollView *scrollview = [[NSScrollView alloc] initWithFrame:NSMakeRect(0, 0, 300, 300)];
[scrollview setHasVerticalScroller:YES];
[scrollview setAcceptsTouchEvents:YES];
[myWindow setContentView:scrollview];
//Add something big to the scroll view
NSButton* btn = [[[NSButton alloc] initWithFrame:NSMakeRect(0, 0, 600, 900)] autorelease];
[scrollview setDocumentView:btn];
//Show the window
[NSApp arrangeInFront:self];
[myWindow makeKeyAndOrderFront:self];
[NSApp activateIgnoringOtherApps:YES];
Any ideas?
Your problem, based on some experimentation I just did, seems to be with specifying NSBackingStoreRetained. The docs say:
You should not use this mode. It combines the limitations of NSBackingStoreNonretained with the memory use of NSBackingStoreBuffered.
They also say:
In Mac OS X 10.5 and later, requests for retained windows will result in the window system creating a buffered window, as that better matches actual use.
This does not seem to be accurate; switching the buffer: argument to NSBackingStoreBuffered made the window and scroll view behave as expected for me. (The docs also say not to use NSBackingStoreNonRetained, and indeed, it seemed to have problems similar to NSBackingStoreRetained.)

Drawing a Window at Coordinates or Drawing outside of NSStatus Item

I am trying to display something off the side of a NSStatusItem. I think I could do this in two ways:
Display a transparent window with the image I need at the coordinates of the mouse cursor.
OR
Use a custom NSStatusItem and move the controls/images in the view to the left so they are actually off the status item
The problem is, setting the NSRect frame negative (-200,0,100,50) doesn't seem to actually work. So, how can I render things outside of the bounds of the status item (think the CSS overflow property) or render a transparent window at specific coordinates?
The system will prevent you from drawing outside the status item, but using a transparent window will work.
NSRect rect; //The location of the window
NSWindow *win = [[NSWindow alloc] initWithContentRect:rect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:YES];
[win setOpaque:NO];
[win setBackgroundColor:[NSColor clearColor]];
//or
[win setContentView:myView];
Here, myView is a custom view which will be the background of the window. In order for the window to be transparent, you either have to set the background color to clear or use a custom content view which only draws where it is not transparent. You will probably want to use a floating window so that it stays on top. Be careful not to cover up something important because your window could intercept events intended for something underneath it.