2
0

UI improvements to the Mac app.

This commit is contained in:
Maarten Billemont 2014-06-24 20:30:15 -04:00
parent 651a398798
commit 6422084fa0
5 changed files with 269 additions and 242 deletions

2
External/Pearl vendored

@ -1 +1 @@
Subproject commit e50cad1411aaafe8083b3863e48b53a1f722c239 Subproject commit a3f33c5f0cbafd6910ae55fb313764c18b48ad69

View File

@ -26,7 +26,7 @@
@property(nonatomic, strong) NSWindowController *initialWindow; @property(nonatomic, strong) NSWindowController *initialWindow;
@end @end
@implementation MPMacAppDelegate { NSWindow *_window; } @implementation MPMacAppDelegate
#pragma clang diagnostic push #pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wfour-char-constants" #pragma clang diagnostic ignored "-Wfour-char-constants"
@ -66,71 +66,157 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
return eventNotHandledErr; return eventNotHandledErr;
} }
- (void)updateUsers { #pragma mark - Life
[[[self.usersItem submenu] itemArray] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { - (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
if (idx > 2)
[[self.usersItem submenu] removeItem:obj]; // Setup delegates and listeners.
[MPConfig get].delegate = self;
__weak id weakSelf = self;
[self addObserverBlock:^(NSString *keyPath, id object, NSDictionary *change, void *context) {
dispatch_async( dispatch_get_main_queue(), ^{
[weakSelf updateMenuItems];
} );
} forKeyPath:@"key" options:0 context:nil];
[self addObserverBlock:^(NSString *keyPath, id object, NSDictionary *change, void *context) {
dispatch_async( dispatch_get_main_queue(), ^{
[weakSelf updateMenuItems];
} );
} forKeyPath:@"activeUser" options:0 context:nil];
[self addObserverBlock:^(NSString *keyPath, id object, NSDictionary *change, void *context) {
dispatch_async( dispatch_get_main_queue(), ^{
[weakSelf updateMenuItems];
} );
} forKeyPath:@"storeManager.cloudAvailable" options:0 context:nil];
// Status item.
self.statusView = [[RHStatusItemView alloc] initWithStatusBarItem:
[[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength]];
self.statusView.image = [NSImage imageNamed:@"menu-icon"];
self.statusView.menu = self.statusMenu;
self.statusView.target = self;
self.statusView.action = @selector( showMenu );
[[NSNotificationCenter defaultCenter] addObserverForName:USMStoreDidChangeNotification object:nil
queue:[NSOperationQueue mainQueue] usingBlock:
^(NSNotification *note) {
[self updateUsers];
}]; }];
[[NSNotificationCenter defaultCenter] addObserverForName:USMStoreDidImportChangesNotification object:nil
queue:[NSOperationQueue mainQueue] usingBlock:
^(NSNotification *note) {
[self updateUsers];
}];
[[NSNotificationCenter defaultCenter] addObserverForName:MPCheckConfigNotification object:nil
queue:[NSOperationQueue mainQueue] usingBlock:
^(NSNotification *note) {
NSString *key = note.object;
if (!key || [key isEqualToString:NSStringFromSelector( @selector( rememberLogin ) )])
self.rememberPasswordItem.state = [[MPConfig get].rememberLogin boolValue]? NSOnState: NSOffState;
if (!key || [key isEqualToString:NSStringFromSelector( @selector( dialogStyleHUD ) )]) {
self.dialogStyleRegular.state = ![[MPMacConfig get].dialogStyleHUD boolValue]? NSOnState: NSOffState;
self.dialogStyleHUD.state = [[MPMacConfig get].dialogStyleHUD boolValue]? NSOnState: NSOffState;
if (![self.passwordWindow.window isVisible])
self.passwordWindow = nil;
else {
[self.passwordWindow close];
self.passwordWindow = nil;
[self showPasswordWindow:nil];
}
}
}];
[self updateUsers];
// Global hotkey.
EventHotKeyRef hotKeyRef;
EventTypeSpec hotKeyEvents[1] = { { .eventClass = kEventClassKeyboard, .eventKind = kEventHotKeyPressed } };
OSStatus status = InstallApplicationEventHandler( NewEventHandlerUPP( MPHotKeyHander ), GetEventTypeCount( hotKeyEvents ),
hotKeyEvents, (__bridge void *)self, NULL );
if (status != noErr)
err( @"Error installing application event handler: %i", (int)status );
status = RegisterEventHotKey( 35 /* p */, controlKey + cmdKey, MPShowHotKey, GetApplicationEventTarget(), 0, &hotKeyRef );
if (status != noErr)
err( @"Error registering 'show' hotkey: %i", (int)status );
status = RegisterEventHotKey( 35 /* p */, controlKey + optionKey + cmdKey, MPLockHotKey, GetApplicationEventTarget(), 0, &hotKeyRef );
if (status != noErr)
err( @"Error registering 'lock' hotkey: %i", (int)status );
// Initial display.
[NSApp activateIgnoringOtherApps:YES];
if ([[MPMacConfig get].firstRun boolValue]) {
self.initialWindow = [[NSWindowController alloc] initWithWindowNibName:@"MPInitialWindow" owner:self];
[self.initialWindow.window setLevel:NSFloatingWindowLevel];
[self.initialWindow showWindow:self];
}
}
- (void)applicationWillResignActive:(NSNotification *)notification {
if (![[MPConfig get].rememberLogin boolValue])
[self lock:nil];
}
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender {
// Save changes in the application's managed object context before the application terminates.
NSManagedObjectContext *context = [MPMacAppDelegate managedObjectContextForMainThreadIfReady]; NSManagedObjectContext *context = [MPMacAppDelegate managedObjectContextForMainThreadIfReady];
if (!context) { if (!context)
self.createUserItem.title = @"New User (Not ready)"; return NSTerminateNow;
self.createUserItem.enabled = NO;
self.createUserItem.toolTip = @"Please wait until the app is fully loaded.";
self.deleteUserItem.title = @"Delete User (Not ready)";
self.deleteUserItem.enabled = NO;
self.deleteUserItem.toolTip = @"Please wait until the app is fully loaded.";
[self.usersItem.submenu addItemWithTitle:@"Loading..." action:NULL keyEquivalent:@""].enabled = NO;
return; if (![context commitEditing])
return NSTerminateCancel;
if (![context hasChanges])
return NSTerminateNow;
[context saveToStore];
return NSTerminateNow;
} }
MPUserEntity *activeUser = [self activeUserInContext:context]; #pragma mark - State
self.createUserItem.title = @"New User"; - (void)setActiveUser:(MPUserEntity *)activeUser {
self.createUserItem.enabled = YES;
self.createUserItem.toolTip = nil;
self.deleteUserItem.title = activeUser? @"Delete User": @"Delete User (None Selected)"; [super setActiveUser:activeUser];
self.deleteUserItem.enabled = activeUser != nil;
self.deleteUserItem.toolTip = activeUser? nil: @"First select the user to delete.";
NSError *error = nil; [MPMacConfig get].usedUserName = activeUser.name;
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPUserEntity class] )];
fetchRequest.sortDescriptors = @[ [NSSortDescriptor sortDescriptorWithKey:@"lastUsed" ascending:NO] ];
NSArray *users = [context executeFetchRequest:fetchRequest error:&error];
if (!users)
err( @"Failed to load users: %@", error );
if (![users count]) { PearlMainQueue( ^{
NSMenuItem *noUsersItem = [self.usersItem.submenu addItemWithTitle:@"No users" action:NULL keyEquivalent:@""]; [self updateUsers];
noUsersItem.enabled = NO; } );
noUsersItem.toolTip = @"Use the iOS app to create users and make sure iCloud is enabled in its preferences as well. "
@"Then give iCloud some time to sync the new user to your Mac.";
} }
self.usersItem.state = NSMixedState; - (void)setLoginItemEnabled:(BOOL)enabled {
for (MPUserEntity *user in users) {
NSMenuItem *userItem = [[NSMenuItem alloc] initWithTitle:user.name action:@selector( selectUser: ) keyEquivalent:@""];
[userItem setTarget:self];
[userItem setRepresentedObject:[user objectID]];
[[self.usersItem submenu] addItem:userItem];
if (!activeUser && [user.name isEqualToString:[MPMacConfig get].usedUserName]) BOOL loginItemEnabled = [self loginItemEnabled];
[super setActiveUser:activeUser = user]; if (loginItemEnabled != enabled) {
if (SMLoginItemSetEnabled( (__bridge CFStringRef)LOGIN_HELPER_BUNDLE_ID, (Boolean)enabled ) == true)
if ([activeUser isEqual:user]) { loginItemEnabled = enabled;
userItem.state = NSOnState;
self.usersItem.state = NSOffState;
}
else else
userItem.state = NSOffState; wrn( @"Failed to set login item." );
} }
[self updateMenuItems]; self.openAtLoginItem.state = loginItemEnabled? NSOnState: NSOffState;
self.openAtLoginButton.state = loginItemEnabled? NSOnState: NSOffState;
} }
- (BOOL)loginItemEnabled {
// The easy and sane method (SMJobCopyDictionary) can pose problems when the app is sandboxed. -_-
NSArray *jobs = (__bridge_transfer NSArray *)SMCopyAllJobDictionaries( kSMDomainUserLaunchd );
for (NSDictionary *job in jobs)
if ([LOGIN_HELPER_BUNDLE_ID isEqualToString:[job objectForKey:@"Label"]]) {
dbg( @"loginItemEnabled: %@", @([[job objectForKey:@"OnDemand"] boolValue]) );
return [[job objectForKey:@"OnDemand"] boolValue];
}
dbg( @"loginItemEnabled: not found" );
return NO;
}
#pragma mark - Actions
- (void)selectUser:(NSMenuItem *)item { - (void)selectUser:(NSMenuItem *)item {
[self signOutAnimated:NO]; [self signOutAnimated:NO];
@ -143,13 +229,6 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
err( @"While looking up selected user: %@", error ); err( @"While looking up selected user: %@", error );
} }
- (void)showMenu {
[self updateMenuItems];
[self.statusView popUpMenu];
}
- (IBAction)togglePreference:(id)sender { - (IBAction)togglePreference:(id)sender {
if (sender == self.enableCloudButton) { if (sender == self.enableCloudButton) {
@ -278,104 +357,99 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
self.initialWindow = nil; self.initialWindow = nil;
} }
- (void)didUpdateConfigForKey:(SEL)configKey fromValue:(id)oldValue { - (IBAction)showPasswordWindow:(id)sender {
[[NSNotificationCenter defaultCenter] postNotificationName:MPCheckConfigNotification object:NSStringFromSelector( configKey )];
}
#pragma mark - NSApplicationDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Setup delegates and listeners.
[MPConfig get].delegate = self;
__weak id weakSelf = self;
[self addObserverBlock:^(NSString *keyPath, id object, NSDictionary *change, void *context) {
dispatch_async( dispatch_get_main_queue(), ^{
[weakSelf updateMenuItems];
} );
} forKeyPath:@"key" options:0 context:nil];
[self addObserverBlock:^(NSString *keyPath, id object, NSDictionary *change, void *context) {
dispatch_async( dispatch_get_main_queue(), ^{
[weakSelf updateMenuItems];
} );
} forKeyPath:@"activeUser" options:0 context:nil];
[self addObserverBlock:^(NSString *keyPath, id object, NSDictionary *change, void *context) {
dispatch_async( dispatch_get_main_queue(), ^{
[weakSelf updateMenuItems];
} );
} forKeyPath:@"storeManager.cloudAvailable" options:0 context:nil];
// Status item.
self.statusView = [[RHStatusItemView alloc] initWithStatusBarItem:
[[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength]];
self.statusView.image = [NSImage imageNamed:@"menu-icon"];
self.statusView.menu = self.statusMenu;
self.statusView.target = self;
self.statusView.action = @selector( showMenu );
[[NSNotificationCenter defaultCenter] addObserverForName:USMStoreDidChangeNotification object:nil
queue:[NSOperationQueue mainQueue] usingBlock:
^(NSNotification *note) {
[self updateUsers];
}];
[[NSNotificationCenter defaultCenter] addObserverForName:USMStoreDidImportChangesNotification object:nil
queue:[NSOperationQueue mainQueue] usingBlock:
^(NSNotification *note) {
[self updateUsers];
}];
[[NSNotificationCenter defaultCenter] addObserverForName:MPCheckConfigNotification object:nil
queue:[NSOperationQueue mainQueue] usingBlock:
^(NSNotification *note) {
NSString *key = note.object;
if (!key || [key isEqualToString:NSStringFromSelector( @selector( rememberLogin ) )])
self.rememberPasswordItem.state = [[MPConfig get].rememberLogin boolValue]? NSOnState: NSOffState;
if (!key || [key isEqualToString:NSStringFromSelector( @selector( dialogStyleHUD ) )]) {
self.dialogStyleRegular.state = ![[MPMacConfig get].dialogStyleHUD boolValue]? NSOnState: NSOffState;
self.dialogStyleHUD.state = [[MPMacConfig get].dialogStyleHUD boolValue]? NSOnState: NSOffState;
if (![self.passwordWindow.window isVisible])
self.passwordWindow = nil;
else {
[self.passwordWindow close];
self.passwordWindow = nil;
[self showPasswordWindow:nil];
}
}
}];
[self updateUsers];
// Global hotkey.
EventHotKeyRef hotKeyRef;
EventTypeSpec hotKeyEvents[1] = { { .eventClass = kEventClassKeyboard, .eventKind = kEventHotKeyPressed } };
OSStatus status = InstallApplicationEventHandler( NewEventHandlerUPP( MPHotKeyHander ), GetEventTypeCount( hotKeyEvents ),
hotKeyEvents, (__bridge void *)self, NULL );
if (status != noErr)
err( @"Error installing application event handler: %i", (int)status );
status = RegisterEventHotKey( 35 /* p */, controlKey + cmdKey, MPShowHotKey, GetApplicationEventTarget(), 0, &hotKeyRef );
if (status != noErr)
err( @"Error registering 'show' hotkey: %i", (int)status );
status = RegisterEventHotKey( 35 /* p */, controlKey + optionKey + cmdKey, MPLockHotKey, GetApplicationEventTarget(), 0, &hotKeyRef );
if (status != noErr)
err( @"Error registering 'lock' hotkey: %i", (int)status );
// Initial display.
[NSApp activateIgnoringOtherApps:YES]; [NSApp activateIgnoringOtherApps:YES];
if ([[MPMacConfig get].firstRun boolValue]) {
self.initialWindow = [[NSWindowController alloc] initWithWindowNibName:@"MPInitialWindow" owner:self]; // If no user, can't activate.
[self.initialWindow.window setLevel:NSFloatingWindowLevel]; if (![self activeUserForMainThread]) {
[self.initialWindow showWindow:self]; [[NSAlert alertWithMessageText:@"No User Selected" defaultButton:[PearlStrings get].commonButtonOkay alternateButton:nil
} otherButton:nil informativeTextWithFormat:
@"Begin by selecting or creating your user from the status menu (●●●|) next to the clock."]
runModal];
[self.statusView popUpMenu];
return;
} }
- (void)setActiveUser:(MPUserEntity *)activeUser { // Don't show window if we weren't already running (ie. if we haven't been activated before).
if (!self.passwordWindow)
self.passwordWindow = [[MPPasswordWindowController alloc] initWithWindowNibName:@"MPPasswordWindowController"];
[super setActiveUser:activeUser]; [self.passwordWindow showWindow:self];
}
[MPMacConfig get].usedUserName = activeUser.name; #pragma mark - Private
PearlMainQueue( ^{ - (void)updateUsers {
[self updateUsers];
} ); [[[self.usersItem submenu] itemArray] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if (idx > 2)
[[self.usersItem submenu] removeItem:obj];
}];
NSManagedObjectContext *context = [MPMacAppDelegate managedObjectContextForMainThreadIfReady];
if (!context) {
self.createUserItem.title = @"New User (Not ready)";
self.createUserItem.enabled = NO;
self.createUserItem.toolTip = @"Please wait until the app is fully loaded.";
self.deleteUserItem.title = @"Delete User (Not ready)";
self.deleteUserItem.enabled = NO;
self.deleteUserItem.toolTip = @"Please wait until the app is fully loaded.";
[self.usersItem.submenu addItemWithTitle:@"Loading..." action:NULL keyEquivalent:@""].enabled = NO;
return;
}
MPUserEntity *activeUser = [self activeUserInContext:context];
self.createUserItem.title = @"New User";
self.createUserItem.enabled = YES;
self.createUserItem.toolTip = nil;
self.deleteUserItem.title = activeUser? @"Delete User": @"Delete User (None Selected)";
self.deleteUserItem.enabled = activeUser != nil;
self.deleteUserItem.toolTip = activeUser? nil: @"First select the user to delete.";
NSError *error = nil;
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPUserEntity class] )];
fetchRequest.sortDescriptors = @[ [NSSortDescriptor sortDescriptorWithKey:@"lastUsed" ascending:NO] ];
NSArray *users = [context executeFetchRequest:fetchRequest error:&error];
if (!users)
err( @"Failed to load users: %@", error );
if (![users count]) {
NSMenuItem *noUsersItem = [self.usersItem.submenu addItemWithTitle:@"No users" action:NULL keyEquivalent:@""];
noUsersItem.enabled = NO;
noUsersItem.toolTip = @"Use the iOS app to create users and make sure iCloud is enabled in its preferences as well. "
@"Then give iCloud some time to sync the new user to your Mac.";
}
self.usersItem.state = NSMixedState;
for (MPUserEntity *user in users) {
NSMenuItem *userItem = [[NSMenuItem alloc] initWithTitle:user.name action:@selector( selectUser: ) keyEquivalent:@""];
[userItem setTarget:self];
[userItem setRepresentedObject:[user objectID]];
[[self.usersItem submenu] addItem:userItem];
if (!activeUser && [user.name isEqualToString:[MPMacConfig get].usedUserName])
[super setActiveUser:activeUser = user];
if ([activeUser isEqual:user]) {
userItem.state = NSOnState;
self.usersItem.state = NSOffState;
}
else
userItem.state = NSOffState;
}
[self updateMenuItems];
}
- (void)showMenu {
[self updateMenuItems];
[self.statusView popUpMenu];
} }
- (void)updateMenuItems { - (void)updateMenuItems {
@ -440,77 +514,11 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
} }
} }
- (IBAction)showPasswordWindow:(id)sender { #pragma mark - PearlConfigDelegate
[NSApp activateIgnoringOtherApps:YES]; - (void)didUpdateConfigForKey:(SEL)configKey fromValue:(id)oldValue {
// If no user, can't activate. [[NSNotificationCenter defaultCenter] postNotificationName:MPCheckConfigNotification object:NSStringFromSelector( configKey )];
if (![self activeUserForMainThread]) {
[[NSAlert alertWithMessageText:@"No User Selected" defaultButton:[PearlStrings get].commonButtonOkay alternateButton:nil
otherButton:nil informativeTextWithFormat:
@"Begin by selecting or creating your user from the status menu (●●●|) next to the clock."]
runModal];
[self.statusView popUpMenu];
return;
}
// Don't show window if we weren't already running (ie. if we haven't been activated before).
if (!self.passwordWindow)
self.passwordWindow = [[MPPasswordWindowController alloc] initWithWindowNibName:@"MPPasswordWindowController"];
[self.passwordWindow showWindow:self];
}
- (void)setLoginItemEnabled:(BOOL)enabled {
BOOL loginItemEnabled = [self loginItemEnabled];
if (loginItemEnabled != enabled) {
if (SMLoginItemSetEnabled( (__bridge CFStringRef)LOGIN_HELPER_BUNDLE_ID, (Boolean)enabled ) == true)
loginItemEnabled = enabled;
else
wrn( @"Failed to set login item." );
}
self.openAtLoginItem.state = loginItemEnabled? NSOnState: NSOffState;
self.openAtLoginButton.state = loginItemEnabled? NSOnState: NSOffState;
}
- (BOOL)loginItemEnabled {
// The easy and sane method (SMJobCopyDictionary) can pose problems when the app is sandboxed. -_-
NSArray *jobs = (__bridge_transfer NSArray *)SMCopyAllJobDictionaries( kSMDomainUserLaunchd );
for (NSDictionary *job in jobs)
if ([LOGIN_HELPER_BUNDLE_ID isEqualToString:[job objectForKey:@"Label"]]) {
dbg( @"loginItemEnabled: %@", @([[job objectForKey:@"OnDemand"] boolValue]) );
return [[job objectForKey:@"OnDemand"] boolValue];
}
dbg( @"loginItemEnabled: not found" );
return NO;
}
- (void)applicationWillResignActive:(NSNotification *)notification {
if (![[MPConfig get].rememberLogin boolValue])
[self lock:nil];
}
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender {
// Save changes in the application's managed object context before the application terminates.
NSManagedObjectContext *context = [MPMacAppDelegate managedObjectContextForMainThreadIfReady];
if (!context)
return NSTerminateNow;
if (![context commitEditing])
return NSTerminateCancel;
if (![context hasChanges])
return NSTerminateNow;
[context saveToStore];
return NSTerminateNow;
} }
@end @end

View File

@ -22,6 +22,7 @@
#import "MPAppDelegate_Store.h" #import "MPAppDelegate_Store.h"
#import "MPElementModel.h" #import "MPElementModel.h"
#import "MPAppDelegate_Key.h" #import "MPAppDelegate_Key.h"
#import "PearlProfiler.h"
#define MPAlertIncorrectMP @"MPAlertIncorrectMP" #define MPAlertIncorrectMP @"MPAlertIncorrectMP"
#define MPAlertCreateSite @"MPAlertCreateSite" #define MPAlertCreateSite @"MPAlertCreateSite"
@ -357,12 +358,15 @@
if ([self.window isOnActiveSpace] && self.window.alphaValue) if ([self.window isOnActiveSpace] && self.window.alphaValue)
return; return;
PearlProfiler *profiler = [PearlProfiler new];
CGWindowID windowID = (CGWindowID)[self.window windowNumber]; CGWindowID windowID = (CGWindowID)[self.window windowNumber];
CGImageRef capturedImage = CGWindowListCreateImage( CGRectInfinite, kCGWindowListOptionOnScreenBelowWindow, windowID, CGImageRef capturedImage = CGWindowListCreateImage( CGRectInfinite, kCGWindowListOptionOnScreenBelowWindow, windowID,
kCGWindowImageBoundsIgnoreFraming ); kCGWindowImageBoundsIgnoreFraming );
[profiler finishJob:@"captured window: %d, on screen: %@", windowID, self.window.screen];
NSImage *screenImage = [[NSImage alloc] initWithCGImage:capturedImage size:NSMakeSize( NSImage *screenImage = [[NSImage alloc] initWithCGImage:capturedImage size:NSMakeSize(
CGImageGetWidth( capturedImage ) / self.window.backingScaleFactor, CGImageGetWidth( capturedImage ) / self.window.backingScaleFactor,
CGImageGetHeight( capturedImage ) / self.window.backingScaleFactor )]; CGImageGetHeight( capturedImage ) / self.window.backingScaleFactor )];
[profiler finishJob:@"image size: %@, bytes: %ld", NSStringFromSize( screenImage.size ), screenImage.TIFFRepresentation.length ];
NSImage *smallImage = [[NSImage alloc] initWithSize:NSMakeSize( NSImage *smallImage = [[NSImage alloc] initWithSize:NSMakeSize(
CGImageGetWidth( capturedImage ) / 20, CGImageGetWidth( capturedImage ) / 20,
@ -373,12 +377,16 @@
operation:NSCompositeSourceOver operation:NSCompositeSourceOver
fraction:1.0]; fraction:1.0];
[smallImage unlockFocus]; [smallImage unlockFocus];
[profiler finishJob:@"small image size: %@, bytes: %ld", NSStringFromSize( screenImage.size ), screenImage.TIFFRepresentation.length];
self.blurView.image = smallImage; self.blurView.image = smallImage;
[profiler finishJob:@"assigned image"];
[self.window setFrame:self.window.screen.frame display:YES]; [self.window setFrame:self.window.screen.frame display:YES];
[profiler finishJob:@"assigned frame"];
[[NSAnimationContext currentContext] setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn]]; [[NSAnimationContext currentContext] setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn]];
[[self.window animator] setAlphaValue:1.0]; [[self.window animator] setAlphaValue:1.0];
[profiler finishJob:@"animating window"];
} }
- (void)fadeOut { - (void)fadeOut {

View File

@ -42,25 +42,15 @@
<real key="inputRadius" value="20"/> <real key="inputRadius" value="20"/>
</configuration> </configuration>
</ciFilter> </ciFilter>
<ciFilter name="CIColorControls"> <ciFilter name="CIPhotoEffectProcess">
<configuration> <configuration>
<real key="inputBrightness" value="-0.29999999999999999"/>
<real key="inputContrast" value="1"/>
<null key="inputImage"/>
<real key="inputSaturation" value="0.0"/>
</configuration>
</ciFilter>
<ciFilter name="CIFalseColor">
<configuration>
<ciColor key="inputColor0" red="0.70196080207824707" green="0.70196080207824707" blue="0.70196080207824707" alpha="1"/>
<ciColor key="inputColor1" red="0.0" green="0.25098040699958801" blue="0.50196081399917603" alpha="1"/>
<null key="inputImage"/> <null key="inputImage"/>
</configuration> </configuration>
</ciFilter> </ciFilter>
</contentFilters> </contentFilters>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyUpOrDown" image="small-screen" id="ArA-2w-I56"/> <imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyUpOrDown" image="small-screen" id="ArA-2w-I56"/>
</imageView> </imageView>
<secureTextField focusRingType="none" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="iGR-wo-ual" userLabel="Password Field"> <secureTextField hidden="YES" focusRingType="none" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="iGR-wo-ual" userLabel="Password Field">
<rect key="frame" x="18" y="243" width="612" height="44"/> <rect key="frame" x="18" y="243" width="612" height="44"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<shadow key="shadow"> <shadow key="shadow">
@ -113,9 +103,9 @@
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="pTl-fc-MSY"> <textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="pTl-fc-MSY">
<rect key="frame" x="-2" y="0.0" width="516" height="29"/> <rect key="frame" x="-2" y="0.0" width="516" height="29"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
<shadow key="shadow"> <shadow key="shadow" blurRadius="1">
<size key="offset" width="0.0" height="1"/> <size key="offset" width="0.0" height="1"/>
<color key="color" white="0.0" alpha="1" colorSpace="calibratedWhite"/> <color key="color" white="0.0" alpha="0.80000000000000004" colorSpace="calibratedWhite"/>
</shadow> </shadow>
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" alignment="center" title="apple.com" id="5Ug-eU-C9Y"> <textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" alignment="center" title="apple.com" id="5Ug-eU-C9Y">
<font key="font" size="24" name="HelveticaNeue-Thin"/> <font key="font" size="24" name="HelveticaNeue-Thin"/>
@ -170,9 +160,9 @@
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="OnR-s6-d4P" userLabel="Input Label"> <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="OnR-s6-d4P" userLabel="Input Label">
<rect key="frame" x="213" y="295" width="223" height="20"/> <rect key="frame" x="213" y="295" width="223" height="20"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<shadow key="shadow"> <shadow key="shadow" blurRadius="1">
<size key="offset" width="0.0" height="1"/> <size key="offset" width="0.0" height="1"/>
<color key="color" white="0.0" alpha="1" colorSpace="calibratedWhite"/> <color key="color" red="0.0" green="0.0" blue="0.0" alpha="0.80000000000000004" colorSpace="calibratedRGB"/>
</shadow> </shadow>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="center" title="Maarten Billemont's password for:" id="1Lb-d0-fQD"> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="center" title="Maarten Billemont's password for:" id="1Lb-d0-fQD">
<font key="font" size="14" name="HelveticaNeue"/> <font key="font" size="14" name="HelveticaNeue"/>
@ -186,9 +176,9 @@
<constraints> <constraints>
<constraint firstAttribute="width" constant="512" id="rW7-Vq-4Xy"/> <constraint firstAttribute="width" constant="512" id="rW7-Vq-4Xy"/>
</constraints> </constraints>
<shadow key="shadow"> <shadow key="shadow" blurRadius="1">
<size key="offset" width="0.0" height="1"/> <size key="offset" width="0.0" height="1"/>
<color key="color" white="0.0" alpha="1" colorSpace="calibratedWhite"/> <color key="color" red="0.0" green="0.0" blue="0.0" alpha="0.80000000000000004" colorSpace="calibratedRGB"/>
</shadow> </shadow>
<searchFieldCell key="cell" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" focusRingType="none" alignment="center" placeholderString="Site Name" id="ppl-2c-1E9"> <searchFieldCell key="cell" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" focusRingType="none" alignment="center" placeholderString="Site Name" id="ppl-2c-1E9">
<font key="font" size="36" name="HelveticaNeue-Thin"/> <font key="font" size="36" name="HelveticaNeue-Thin"/>
@ -328,6 +318,19 @@
</configuration> </configuration>
</ciFilter> </ciFilter>
</backgroundFilters> </backgroundFilters>
<animations>
<caTransition key="subviews">
<mutableData key="keyedArchiveRepresentation">
YnBsaXN0MDDUAQIDBAUGICFYJHZlcnNpb25YJG9iamVjdHNZJGFyY2hpdmVyVCR0b3ASAAGGoKcHCA8Q
CRUbVSRudWxs0wkKCwwNDlR0eXBlViRjbGFzc18QEl9fQ0FDb2RpbmdDb250ZW50c4ACgAaAA1RmYWRl
0hEKEhRaTlMub2JqZWN0c6ETgASABdIWFxgZWiRjbGFzc25hbWVYJGNsYXNzZXNXTlNBcnJheaIYGlhO
U09iamVjdNIWFxwdXENBVHJhbnNpdGlvbqMeHxpcQ0FUcmFuc2l0aW9uW0NBQW5pbWF0aW9uXxAPTlNL
ZXllZEFyY2hpdmVy0SIjVHJvb3SAAQAIABEAGgAjAC0AMgA3AD8ARQBMAFEAWABtAG8AcQBzAHgAfQCI
AIoAjACOAJMAngCnAK8AsgC7AMAAzQDRAN4A6gD8AP8BBAAAAAAAAAIBAAAAAAAAACQAAAAAAAAAAAAA
AAAAAAEGA
</mutableData>
</caTransition>
</animations>
</view> </view>
</window> </window>
<userDefaultsController representsSharedInstance="YES" id="yy2-3W-Ocj"/> <userDefaultsController representsSharedInstance="YES" id="yy2-3W-Ocj"/>

View File

@ -10,9 +10,11 @@
93D390C676DF52DA7E459F19 /* MPPasswordWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39D9D0061FF1159998F06 /* MPPasswordWindow.m */; }; 93D390C676DF52DA7E459F19 /* MPPasswordWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39D9D0061FF1159998F06 /* MPPasswordWindow.m */; };
93D392EC39DA43C46C692C12 /* NSDictionary+Indexing.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D393B97158D7BE9332EA53 /* NSDictionary+Indexing.h */; }; 93D392EC39DA43C46C692C12 /* NSDictionary+Indexing.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D393B97158D7BE9332EA53 /* NSDictionary+Indexing.h */; };
93D395F08A087F8A24689347 /* NSArray+Indexing.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39067C0AFDC581794E2B8 /* NSArray+Indexing.m */; }; 93D395F08A087F8A24689347 /* NSArray+Indexing.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39067C0AFDC581794E2B8 /* NSArray+Indexing.m */; };
93D3970BCF85F7902E611168 /* PearlProfiler.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39DB3A8ADED08C39A6228 /* PearlProfiler.m */; };
93D39C34FE35830EF5BE1D2A /* NSArray+Indexing.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D396D04E57792A54D437AC /* NSArray+Indexing.h */; }; 93D39C34FE35830EF5BE1D2A /* NSArray+Indexing.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D396D04E57792A54D437AC /* NSArray+Indexing.h */; };
93D39C5789EFA607CF788082 /* MPElementModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39E73BF5CBF8E5B005CD3 /* MPElementModel.m */; }; 93D39C5789EFA607CF788082 /* MPElementModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39E73BF5CBF8E5B005CD3 /* MPElementModel.m */; };
93D39C7C2BE7C0E0763B0177 /* MPElementCollectionView.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D394495528B10D1B61A2C3 /* MPElementCollectionView.m */; }; 93D39C7C2BE7C0E0763B0177 /* MPElementCollectionView.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D394495528B10D1B61A2C3 /* MPElementCollectionView.m */; };
93D39D304F73B3BBA031522A /* PearlProfiler.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D394EEFF5BF555A55AF361 /* PearlProfiler.h */; };
93D39E281E3658B30550CB55 /* NSDictionary+Indexing.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39AA1EE2E1E7B81372240 /* NSDictionary+Indexing.m */; }; 93D39E281E3658B30550CB55 /* NSDictionary+Indexing.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39AA1EE2E1E7B81372240 /* NSDictionary+Indexing.m */; };
93D39F833DEC1C89B2F795AC /* MPPasswordWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39A57A7823DE98A0FF83C /* MPPasswordWindowController.m */; }; 93D39F833DEC1C89B2F795AC /* MPPasswordWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39A57A7823DE98A0FF83C /* MPPasswordWindowController.m */; };
DA0933CC1747AD2D00DE1CEF /* shot-laptop-leaning-iphone.png in Resources */ = {isa = PBXBuildFile; fileRef = DA0933CB1747AD2D00DE1CEF /* shot-laptop-leaning-iphone.png */; }; DA0933CC1747AD2D00DE1CEF /* shot-laptop-leaning-iphone.png in Resources */ = {isa = PBXBuildFile; fileRef = DA0933CB1747AD2D00DE1CEF /* shot-laptop-leaning-iphone.png */; };
@ -309,12 +311,14 @@
93D392C3918763B3B72CF366 /* MPPasswordWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordWindowController.h; sourceTree = "<group>"; }; 93D392C3918763B3B72CF366 /* MPPasswordWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordWindowController.h; sourceTree = "<group>"; };
93D393B97158D7BE9332EA53 /* NSDictionary+Indexing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDictionary+Indexing.h"; sourceTree = "<group>"; }; 93D393B97158D7BE9332EA53 /* NSDictionary+Indexing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDictionary+Indexing.h"; sourceTree = "<group>"; };
93D394495528B10D1B61A2C3 /* MPElementCollectionView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPElementCollectionView.m; sourceTree = "<group>"; }; 93D394495528B10D1B61A2C3 /* MPElementCollectionView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPElementCollectionView.m; sourceTree = "<group>"; };
93D394EEFF5BF555A55AF361 /* PearlProfiler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PearlProfiler.h; path = ../../../External/Pearl/Pearl/PearlProfiler.h; sourceTree = "<group>"; };
93D3960D320FF8A072B092E3 /* MPElementCollectionView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPElementCollectionView.h; sourceTree = "<group>"; }; 93D3960D320FF8A072B092E3 /* MPElementCollectionView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPElementCollectionView.h; sourceTree = "<group>"; };
93D396D04E57792A54D437AC /* NSArray+Indexing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSArray+Indexing.h"; sourceTree = "<group>"; }; 93D396D04E57792A54D437AC /* NSArray+Indexing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSArray+Indexing.h"; sourceTree = "<group>"; };
93D3977484534E99F9BA579D /* MPPasswordWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordWindow.h; sourceTree = "<group>"; }; 93D3977484534E99F9BA579D /* MPPasswordWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordWindow.h; sourceTree = "<group>"; };
93D39A57A7823DE98A0FF83C /* MPPasswordWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPasswordWindowController.m; sourceTree = "<group>"; }; 93D39A57A7823DE98A0FF83C /* MPPasswordWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPasswordWindowController.m; sourceTree = "<group>"; };
93D39AA1EE2E1E7B81372240 /* NSDictionary+Indexing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDictionary+Indexing.m"; sourceTree = "<group>"; }; 93D39AA1EE2E1E7B81372240 /* NSDictionary+Indexing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDictionary+Indexing.m"; sourceTree = "<group>"; };
93D39D9D0061FF1159998F06 /* MPPasswordWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPasswordWindow.m; sourceTree = "<group>"; }; 93D39D9D0061FF1159998F06 /* MPPasswordWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPasswordWindow.m; sourceTree = "<group>"; };
93D39DB3A8ADED08C39A6228 /* PearlProfiler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PearlProfiler.m; path = ../../../External/Pearl/Pearl/PearlProfiler.m; sourceTree = "<group>"; };
93D39E73BF5CBF8E5B005CD3 /* MPElementModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPElementModel.m; sourceTree = "<group>"; }; 93D39E73BF5CBF8E5B005CD3 /* MPElementModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPElementModel.m; sourceTree = "<group>"; };
DA0933C91747A56A00DE1CEF /* MPInitialWindow.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MPInitialWindow.xib; sourceTree = "<group>"; }; DA0933C91747A56A00DE1CEF /* MPInitialWindow.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MPInitialWindow.xib; sourceTree = "<group>"; };
DA0933CB1747AD2D00DE1CEF /* shot-laptop-leaning-iphone.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "shot-laptop-leaning-iphone.png"; sourceTree = "<group>"; }; DA0933CB1747AD2D00DE1CEF /* shot-laptop-leaning-iphone.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "shot-laptop-leaning-iphone.png"; sourceTree = "<group>"; };
@ -702,6 +706,8 @@
DACA22121705DDC5002C6C22 /* External */, DACA22121705DDC5002C6C22 /* External */,
DA5BFA47147E415C00F98B1E /* Frameworks */, DA5BFA47147E415C00F98B1E /* Frameworks */,
DA5BFA45147E415C00F98B1E /* Products */, DA5BFA45147E415C00F98B1E /* Products */,
93D39DB3A8ADED08C39A6228 /* PearlProfiler.m */,
93D394EEFF5BF555A55AF361 /* PearlProfiler.h */,
); );
sourceTree = "<group>"; sourceTree = "<group>";
}; };
@ -1343,6 +1349,7 @@
DAEB93E718AB0FFD000490CC /* crypto.h in Headers */, DAEB93E718AB0FFD000490CC /* crypto.h in Headers */,
DAEB941318AB0FFD000490CC /* ssl2.h in Headers */, DAEB941318AB0FFD000490CC /* ssl2.h in Headers */,
DAEB940D18AB0FFD000490CC /* ripemd.h in Headers */, DAEB940D18AB0FFD000490CC /* ripemd.h in Headers */,
93D39D304F73B3BBA031522A /* PearlProfiler.h in Headers */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@ -1715,6 +1722,7 @@
93D395F08A087F8A24689347 /* NSArray+Indexing.m in Sources */, 93D395F08A087F8A24689347 /* NSArray+Indexing.m in Sources */,
93D39E281E3658B30550CB55 /* NSDictionary+Indexing.m in Sources */, 93D39E281E3658B30550CB55 /* NSDictionary+Indexing.m in Sources */,
DA3509FF15F101A500C14A8E /* PearlQueue.m in Sources */, DA3509FF15F101A500C14A8E /* PearlQueue.m in Sources */,
93D3970BCF85F7902E611168 /* PearlProfiler.m in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };