How would I develop something like DeskLock from Deskshade? - objective-c

Sorry to be a nuisance, but I have yet ANOTHER question. How would I do something like DeskLock from macrabbit's Deskshade app? I've made the little window and that's as far as I've come. I know how to "lock" the screen in 10.6 with PresentationOptions, but I don't want to risk it because last time it wouldn't let me back in ;]
EDIT: The DeskShade app actually is meant to cover your desktop, hiding all icons. It also allows you to randomize wallpaper patterns with several fade/swipes. There is one extra feature called DeskLock that actually presents a translucent black bevel (similar to AppSwitcher build into Mac) with a lock icon, and you can place personal text. When you click the lock icon, it presents a modal that asks for a password you can set. You can also just type this password without pressing anything, followed by the Enter key, and it unlocks the screen. This uses the DeskShade feature of hiding the desktop as well.
Thanks!

To create the overlay window you have to subclass NSWindow and set its style mask and background color:
#implementation BigTransparentWindow
- (id)initWithContentRect:(NSRect)contentRect
styleMask:(NSUInteger)windowStyle
backing:(NSBackingStoreType)bufferingType
defer:(BOOL)deferCreation
{
self = [super initWithContentRect:contentRect
styleMask:NSBorderlessWindowMask //this makes the window transparent
backing:bufferingType
defer:deferCreation];
if(self)
{
[self setOpaque:NO];
[self setHasShadow:NO];
[self setBackgroundColor:[[NSColor blackColor] colorWithAlphaComponent:0.5]];
}
return self;
}
#end
You then need to set the window's frame so that it covers all screens, and you need to set its window level appropriately:
- (IBAction)showWindow:(id)sender
{
//set the window so it covers all available screens
NSRect screensRect = NSZeroRect;
for(NSScreen* screen in [NSScreen screens])
{
screensRect = NSUnionRect(screensRect,[screen frame]);
}
[yourWindow setFrame:screensRect display:YES];
if(coverScreen)
{
//set the window so it is above all other windows
[yourWindow setLevel:kCGMaximumWindowLevel];
}
else
{
//set the window so it sits just above the desktop icons
[yourWindow setLevel:kCGDesktopIconWindowLevel + 1];
}
}
As you've mentioned, you can use the NSApplicationPresentationOptions settings for NSApp to control how the user can interact with the system. An easy way to test this without locking yourself out is to set an NSTimer that calls a method that pulls the app out of kiosk mode after a timeout period.

Related

How to resize Window to dimension in VIew Controller for a OSX app?

I'm fairly new to Mac OSX Apps, but I'm trying to build an app with no status bar, and the dimensions are smaller. So what I did in my AppDelegate.m is:
-(void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
self.mainViewController = [[MainViewController alloc] initWithNibName:#"MainViewController" bundle:nil];
[self.window.contentView addSubview:self.mainViewController.view];
[self.window setOpaque:NO];
[self.window setStyleMask:NSBorderlessWindowMask];
[self.window setBackgroundColor:[NSColor clearColor]];
self.mainViewController.view.frame = ((NSView*)self.window.contentView).bounds;
}
Now, what I really want to do, is to get my window to look like, what I've built in my interface builder (can't post pictures yet). I've tried shutting off all the autosizing elements but when I run the simulator I get a clipped version of my interface (the size changes every run). It seem like the frame size is getting miscalculated, but I've done everything from the interface builder. I can programmatically set the shapes of the view, but is there a way to shut off all resizing and simply portray the xib file as what I see in the interface builder?
There are two ways you could approach this problem.
You add an AutoresizingMask to your ViewController:
[self.mainViewController setAutoresizingMask:NSViewAutoresizingFlexibleWidth|NSViewAutoresizingFlexibleHeigt];
You disable window resizing in the Interface builder like by setting its minimum and maximum size. This can also be done in code.
Edit: Thanks to trojanfoe for pointing out that you can also uncheck Resize in the Attributes Inspector.
You can easily resize your window programatically. Here's how:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Insert code here to initialize your application
NSRect frame;
frame.size.height = 200;
frame.size.width = 200;
[window setFrame:frame display:YES animate:YES];
}

How to differentiate the mouseDown event from mouseDragged in a Transparent NSWindow

I have a Transparent NSWindow with an simple icon in it that can be dragged around the screen.
My code is:
.h:
#interface CustomView : NSWindow{
}
#property (assign) NSPoint initialLocation;
.m
#synthesize initialLocation;
- (id) initWithContentRect: (NSRect) contentRect
styleMask: (NSUInteger) aStyle
backing: (NSBackingStoreType) bufferingType
defer: (BOOL) flag{
if (![super initWithContentRect: contentRect
styleMask: NSBorderlessWindowMask
backing: bufferingType
defer: flag]) return nil;
[self setBackgroundColor: [NSColor clearColor]];
[self setOpaque:NO];
[NSApp activateIgnoringOtherApps:YES];
return self;
}
- (void)mouseDragged:(NSEvent *)theEvent {
NSRect screenVisibleFrame = [[NSScreen mainScreen] visibleFrame];
NSRect windowFrame = [self frame];
NSPoint newOrigin = windowFrame.origin;
// Get the mouse location in window coordinates.
NSPoint currentLocation = [theEvent locationInWindow];
// Update the origin with the difference between the new mouse location and the old mouse location.
newOrigin.x += (currentLocation.x - initialLocation.x);
newOrigin.y += (currentLocation.y - initialLocation.y);
// Don't let window get dragged up under the menu bar
if ((newOrigin.y + windowFrame.size.height) > (screenVisibleFrame.origin.y + screenVisibleFrame.size.height)) {
newOrigin.y = screenVisibleFrame.origin.y + (screenVisibleFrame.size.height - windowFrame.size.height);
}
// Move the window to the new location
[self setFrameOrigin:newOrigin];
}
- (void)mouseDown:(NSEvent *)theEvent {
// Get the mouse location in window coordinates.
self.initialLocation = [theEvent locationInWindow];
}
I want to display a NSPopover when the users clicks the image of the transparent window. But, as you can see in the code, the mouseDown event is used to get the mouse location (the code above was taken from an example).
What can i do to know when the user clicks the icon just to drag it around or simply clicked it to display the NSPopover?
Thank you
This is the classic situation of receiving the defining event after you need it in order to begin the action. Specifically, you can't know if the mouseDown is the beginning of a drag until after the drag starts. However, you want to act upon that mouseDown if a drag doesn't start.
In iOS (I realize that's not directly relevant to the code here, but it is instructional), there's an entire API built around letting iOS attempt to make these kinds of decisions for you. The entire Gesture system is based on the idea that the user starts to do something that might be one of many different actions, and thus needs to be resolved over time, possibly resulting in cancelled actions during the tracking period.
On OS X, we don't have many systems to help out with this, so if you have something that needs to handle a click and a drag differentially, you will need to defer your next action until a guard time has passed, and if that passes, you can perform the original action. In this case, you will likely want to do the following:
In the mouseDown, begin an NSTimer set for an appropriate guard time (not so long that people will accidentally move the pointer, and not so short that you'll trigger before the user drags) in order to call you back later to trigger the popover.
In the mouseDragged, use a guard area to make sure that if the user just twitches a little, it doesn't count as a drag. This can be irritating, as it sometimes results in needing to drag something farther than it seems necessary in order to begin a drag, so you'll want to either find a magic constant somewhere, or do some experimentation. When the guard area is exceeded, then begin your legitimate drag operation by canceling the NSTimer with [timer invalidate] and do your drag.
In the callback for the timer, display your popover. If the user dragged, the NSTimer will have been invalidated, causing it not to fire, and so the popover won't be displayed.

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

NSWindow draw on top of Desktop icons

I'm writing an application that is looking to draw a basic NSWindow on top of the Desktop icons (Mac OSX 10.7 Lion).
NSWindow *systemInfoWindow = [[NSWindow alloc] initWithContentRect:frame
styleMask:NSBorderlessWindowMask
backing:NSBackingStoreBuffered
defer:NO];
systemInfoWindow.delegate = self;
[systemInfoWindow setCollectionBehavior:NSWindowCollectionBehaviorCanJoinAllSpaces];
[systemInfoWindow setBackgroundColor:backgroundColorSpace];
[systemInfoWindow makeKeyAndOrderFront:NSApp];
[systemInfoWindow setLevel:kCGDesktopIconWindowLevel];
This part of code works quite well and the NSWindow gets draw on top of the icons.
But if I switch between two spaces my NSWindow stay behind the icons.
/* === Update === */
I think i have problems with my window Level. For better understanding I have uploaded two Screenshots.
Space 1 - window stays like I want on top of the Icons
http://dl.dropbox.com/u/1503795/Space1.png
Space 2 - the window is behind the Desktop icons. They should also stay on top
http://dl.dropbox.com/u/1503795/Space2.png
A further problem I found. If I activated the App and stay on the first Space and everything looks good like on my first Screenshot. If I select the Files on the Desktop the window also jumps to the back:
Sorry, can`t post a nother Screenshot
I hope my problem is a bit more understandably.
I dont`t know how to fix this. Has anybody an idea?
Thanks!
You probably need to register for the space change notification:
NSNotificationCenter* nc = [[NSWorkspace sharedWorkspace] notificationCenter];
[nc addObserver:self
selector:#selector(activeSpaceDidChange:)
name:NSWorkspaceActiveSpaceDidChangeNotification
object:nil];
You can then respond to the notification and update your window:
- (void) activeSpaceDidChange:(NSNotification*)aNotification
{
[[self window] orderFront:self];
}

MPMoviePlayerController adding UIButton to view that fades with controls

I am trying to add a UIButton to the view of a MPMoviePlayerController along with the standard controls. The button appears over the video and works as expected receiving touch events, but I would like to have it fade in and out with the standard controls in response to user touches.
I know I could accomplish this by rolling my own custom player controls, but it seems silly since I am just trying to add one button.
EDIT
If you recursively traverse the view hierarchy of the MPMoviePlayerController's view eventually you will come to a view class called MPInlineVideoOverlay. You can add any additional controls easily to this view to achieve the auto fade in/out behavior.
There are a few gotchas though, it can sometimes take awhile (up to a second in my experience) after you have created the MPMoviePlayerController and added it to a view before it has initialized fully and created it's MPInlineVideoOverlay layer. Because of this I had to create an instance variable called controlView in the code below because sometimes it doesn't exist when this code runs. This is why I have the last bit of code where the function calls itself again in 0.1 seconds if it isn't found. I couldn't notice any delay in the button appearing on my interface despite this delay.
-(void)setupAdditionalControls {
//Call after you have initialized your MPMoviePlayerController (probably viewDidLoad)
controlView = nil;
[self recursiveViewTraversal:movie.view counter:0];
//check to see if we found it, if we didn't we need to do it again in 0.1 seconds
if(controlView) {
UIButton *backButton = [UIButton buttonWithType:UIButtonTypeCustom];
[controlView addSubview:backButton];
} else {
[self performSelector:#selector(setupAdditionalControls) withObject:nil afterDelay:0.1];
}
}
-(void)recursiveViewTraversal:(UIView*)view counter:(int)counter {
NSLog(#"Depth %d - %#", counter, view); //For debug
if([view isKindOfClass:NSClassFromString(#"MPInlineVideoOverlay")]) {
//Add any additional controls you want to have fade with the standard controls here
controlView = view;
} else {
for(UIView *child in [view subviews]) {
[self recursiveViewTraversal:child counter:counter+1];
}
}
}
It isn't the best solution, but I am posting it in case someone else is trying to do the same thing. If Apple was to change the view structure or class names internal to the control overlay it would break. I am also assuming you aren't playing the video full screen (although you can play it fullscreen with embeded controls). I also had to disable the fullscreen button using the technique described here because the MPInlineVideoOverlay view gets removed and released when it is pressed: iPad MPMoviePlayerController - Disable Fullscreen
Calling setupAdditionalControls when you receive the fullscreen notifications described above will re-add your additional controls to the UI.
Would love a more elegant solution if anyone can suggest something other than this hackery I have come up with.
My solution to the same problem was:
Add the button as a child of the MPMoviePlayerController's view;
fade the button in and out using animation of its alpha property, with the proper durations;
handle the player controller's touchesBegan, and use that to toggle the button's visibility (using its alpha);
use a timer to determine when to hide the button again.
By trial-and-error, I determined that the durations that matched the (current) iOS ones are:
fade in: 0.1s
fade out: 0.2s
duration on screen: 5.0s (extend that each time the view is touched)
Of course this is still fragile; if the built-in delays change, mine will look wrong, but the code will still run.