diff --git a/appshell/CustomTitlebarView.h b/appshell/CustomTitlebarView.h new file mode 100644 index 000000000..4c46a4ca0 --- /dev/null +++ b/appshell/CustomTitlebarView.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + */ +#import + +@interface CustomTitlebarView : NSView +{ + NSString *titleString; +} + +@property (nonatomic, strong) NSString *titleString; + +@end diff --git a/appshell/CustomTitlebarView.m b/appshell/CustomTitlebarView.m new file mode 100644 index 000000000..51dc34efd --- /dev/null +++ b/appshell/CustomTitlebarView.m @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + */ +#import "CustomTitlebarView.h" +#import "client_colors_mac.h" + +#define titleTextHeight 13 +#define fudge 4 + +@implementation CustomTitlebarView + +@synthesize titleString; + +- (void)drawRect:(NSRect)dirtyRect +{ + NSColorSpace *sRGB = [NSColorSpace sRGBColorSpace]; + NSColor *fillColor = [NSColor colorWithColorSpace:sRGB components:fillComp count:4]; + NSRect windowFrame = [NSWindow frameRectForContentRect:[[[self window] contentView] bounds] styleMask:[[self window] styleMask]]; + NSRect contentBounds = [[[self window] contentView] bounds]; + + NSRect titlebarRect = NSMakeRect(0, 0, self.bounds.size.width, windowFrame.size.height - contentBounds.size.height); + titlebarRect.origin.y = self.bounds.size.height - titlebarRect.size.height; + + [[NSColor clearColor] set]; + NSRectFill( titlebarRect ); + + [fillColor set]; + + [NSGraphicsContext saveGraphicsState]; + //This constant matches the radius for other macosx apps. + //For some reason if we use the default value it is double that of safari etc. + float cornerRadius = 4.0f; + + // make a clip mask that is rounded on top and square on the bottom... + NSBezierPath* clipPath = [NSBezierPath bezierPath]; + [clipPath appendBezierPathWithRoundedRect:titlebarRect xRadius:cornerRadius yRadius:cornerRadius]; + [clipPath moveToPoint: NSMakePoint(titlebarRect.origin.x, titlebarRect.origin.y)]; + [clipPath appendBezierPathWithRect: NSMakeRect(titlebarRect.origin.x, titlebarRect.origin.y, titlebarRect.size.width, titlebarRect.size.height / 2)]; + [clipPath addClip]; + + // Fill in with the Dark UI color + NSRectFill(titlebarRect); + + + NSFont *titleFont = [NSFont titleBarFontOfSize:titleTextHeight]; + CGFloat stringWidth = [self widthOfString:titleString withFont:titleFont]; + NSColor *activeColor = [NSColor colorWithColorSpace:sRGB components:activeComp count:4]; + NSColor *inactiveColor = [NSColor colorWithColorSpace:sRGB components:inactiveComp count:4]; + + NSLayoutManager *lm = [[NSLayoutManager alloc] init]; + int height = [lm defaultLineHeightForFont:titleFont]; + + // Draw the title text + if (stringWidth) + { + NSRect textRect = NSMakeRect(titlebarRect.origin.x + ((titlebarRect.size.width / 2) - (stringWidth / 2)), + titlebarRect.origin.y + ((titlebarRect.size.height / 2) - (height / 2)) - fudge, + titlebarRect.size.width, + titlebarRect.size.height); + + [titleString drawInRect:textRect withAttributes:[NSDictionary dictionaryWithObjectsAndKeys: + titleFont, NSFontAttributeName, + [NSApp isActive] ? activeColor : inactiveColor, + NSForegroundColorAttributeName, + nil]]; + } + + [NSGraphicsContext restoreGraphicsState]; +} + +- (CGFloat)widthOfString:(NSString *)string withFont:(NSFont *)font +{ + if (string == nil || [string length] == 0) + return 0.0f; + + NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys:font, NSFontAttributeName, nil]; + return [[[NSAttributedString alloc] initWithString:string attributes:attributes] size].width; +} + +#pragma mark Property accessors + +- (NSString *)titleString +{ + return titleString; +} + +- (void)setTitleString:(NSString *)aString +{ + if ((!titleString && !aString) || (titleString && aString && [titleString isEqualToString:aString])) + return; + titleString = [aString copy]; + [self setNeedsDisplay:YES]; +} + + +@end diff --git a/appshell/cefclient_mac.mm b/appshell/cefclient_mac.mm index 5418261c7..4f86a374e 100644 --- a/appshell/cefclient_mac.mm +++ b/appshell/cefclient_mac.mm @@ -30,6 +30,7 @@ #include "FullScreenView.h" #include "FullScreenViewController.h" +#import "CustomTitlebarView.h" // Application startup time CFTimeInterval g_appStartupTime; @@ -107,6 +108,7 @@ - (NSWindow *) findTargetWindow { @end +// BOBNOTE: Consider moving the delegate interface into its own .h file @interface ClientMenuDelegate : NSObject { } - (void)menuWillOpen:(NSMenu *)menu; @@ -126,87 +128,15 @@ - (void)menuWillOpen:(NSMenu *)menu { @end - - -// Custom draw interface for NSThemeFrame -@interface NSView (UndocumentedAPI) -- (float)roundedCornerRadius; -- (CGRect)_titlebarTitleRect; -- (NSTextFieldCell*)titleCell; -- (void)_drawTitleStringIn:(struct CGRect)arg1 withColor:(id)color; -@end - -/** - * The patched implementation for drawRect that lets us tweak - * the title bar. - */ -void ShellWindowFrameDrawRect(id self, SEL _cmd, NSRect rect) { - // Clear to 0 alpha - [[NSColor clearColor] set]; - NSRectFill( rect ); - //Obtain reference to our NSThemeFrame view - NSRect windowRect = [self frame]; - windowRect.origin = NSMakePoint(0,0); - //This constant matches the radius for other macosx apps. - //For some reason if we use the default value it is double that of safari etc. - float cornerRadius = 4.0f; - - //Clip our title bar render - [[NSBezierPath bezierPathWithRoundedRect:windowRect - xRadius:cornerRadius - yRadius:cornerRadius] addClip]; - [[NSBezierPath bezierPathWithRect:rect] addClip]; - - - - NSColorSpace *sRGB = [NSColorSpace sRGBColorSpace]; - NSColor *fillColor = [NSColor colorWithColorSpace:sRGB components:fillComp count:4]; - [fillColor set]; - NSRectFill( rect ); - NSColor *activeColor = [NSColor colorWithColorSpace:sRGB components:activeComp count:4]; - NSColor *inactiveColor = [NSColor colorWithColorSpace:sRGB components:inactiveComp count:4]; - // Render our title text - [self _drawTitleStringIn:[self _titlebarTitleRect] - withColor:[NSApp isActive] ? - activeColor : inactiveColor]; - - - - -} - - - - -/** - * Create a custom class based on NSThemeFrame called - * ShellWindowFrame. ShellWindowFrame uses ShellWindowFrameDrawRect() - * as the implementation for the drawRect selector allowing us - * to draw the border/title bar the way we see fit. - */ -Class GetShellWindowFrameClass() { - // lazily change the class implementation if - // not done so already. - static Class k = NULL; - if (!k) { - // See http://cocoawithlove.com/2010/01/what-is-meta-class-in-objective-c.html - Class NSThemeFrame = NSClassFromString(@"NSThemeFrame"); - k = objc_allocateClassPair(NSThemeFrame, "ShellWindowFrame", 0); - Method m0 = class_getInstanceMethod(NSThemeFrame, @selector(drawRect:)); - class_addMethod(k, @selector(drawRect:), - (IMP)ShellWindowFrameDrawRect, method_getTypeEncoding(m0)); - objc_registerClassPair(k); - } - return k; -} - - +// BOBNOTE: Consider moving the delegate interface into its own .h file // Receives notifications from controls and the browser window. Will delete // itself when done. @interface ClientWindowDelegate : NSObject { - BOOL isReallyClosing; - NSString* savedTitle; - NSView* fullScreenButtonView; + BOOL isReallyClosing; + NSView* fullScreenButtonView; + NSView* trafficLightsView; + BOOL isReentering; + CustomTitlebarView *customTitlebar; } - (void)setIsReallyClosing; - (IBAction)handleMenuAction:(id)sender; @@ -218,15 +148,18 @@ - (void)notifyConsoleMessage:(id)object; - (void)notifyDownloadComplete:(id)object; - (void)notifyDownloadError:(id)object; - (void)setFullScreenButtonView:(NSView*)view; -- (void)addCustomDrawHook:(NSView*)contentView; +- (void)setTrafficLightsView:(NSView*)view; @end @implementation ClientWindowDelegate - (id) init { - [super init]; - savedTitle = nil; - isReallyClosing = false; - return self; + [super init]; + isReallyClosing = NO; + isReentering = NO; + customTitlebar = nil; + fullScreenButtonView = nil; + trafficLightsView = nil; + return self; } - (void)setIsReallyClosing { @@ -271,41 +204,20 @@ - (IBAction)quit:(id)sender { } -- (void)setFullScreenButtonView:(NSView *)view { +-(void)setFullScreenButtonView:(NSView *)view { fullScreenButtonView = view; } -- (void)addCustomDrawHook:(NSView*)contentView -{ - NSView* themeView = [contentView superview]; - - object_setClass(themeView, GetShellWindowFrameClass()); - -#ifdef LIGHT_CAPTION_TEXT - // Reset our frame view text cell background style - NSTextFieldCell * cell = [themeView titleCell]; - [cell setBackgroundStyle:NSBackgroundStyleLight]; -#endif -} -- (void)removeCustomDrawHook:(NSView*)contentView -{ - NSView* themeView = [contentView superview]; - - object_setClass(themeView, NULL); +-(void)setTrafficLightsView:(NSView *)view { + trafficLightsView = view; } -(void)windowTitleDidChange:(NSString*)title { #ifdef DARK_UI - savedTitle = [title copy]; -#endif -} - -- (void)windowWillEnterFullScreen:(NSNotification *)notification { -#ifdef DARK_UI - NSWindow* window = [notification object]; - savedTitle = [[window title] copy]; - [window setTitle:@""]; + if (customTitlebar) { + [customTitlebar setTitleString:title]; + } #endif } @@ -315,12 +227,21 @@ - (BOOL)isFullScreenSupported { return (version >= 0x1070); } +-(BOOL)needsFullScreenActivateHack { + SInt32 version; + Gestalt(gestaltSystemVersion, &version); + return (version >= 0x1090); +} -(void)windowDidResize:(NSNotification *)notification { + +// BOBNOTE: this should be moved into the CustomTitlebarView class #ifdef DARK_UI + NSWindow* window = [notification object]; + if ([self isFullScreenSupported]) { - NSWindow* window = [notification object]; + NSView* themeView = [[window contentView] superview]; NSRect parentFrame = [themeView frame]; @@ -330,55 +251,80 @@ -(void)windowDidResize:(NSNotification *)notification oldFrame.size.width, // width oldFrame.size.height); - [fullScreenButtonView setFrame:newFrame]; [themeView setNeedsDisplay:YES]; } #endif } -- (void)windowDidExitFullScreen:(NSNotification *)notification { - NSWindow* window = [notification object]; - NSView* contentView = [window contentView]; - NSView* themeView = [[window contentView] superview]; + +- (void)windowWillEnterFullScreen:(NSNotification *)notification { #ifdef DARK_UI - [self addCustomDrawHook: contentView]; - [window setTitle:savedTitle]; - [savedTitle release]; - [themeView setNeedsDisplay:YES]; -#endif - + if (fullScreenButtonView) { + [fullScreenButtonView removeFromSuperview]; + fullScreenButtonView = nil; + } + if (trafficLightsView) { + [trafficLightsView setHidden:YES]; + } + if (customTitlebar) { + [customTitlebar setHidden:YES]; + } - NSWindow* theWin = window; + if ([self needsFullScreenActivateHack]) { + [NSApp activateIgnoringOtherApps:YES]; + [NSApp unhide:nil]; + NSWindow* window = [notification object]; + NSView* contentView = [window contentView]; + [contentView setNeedsDisplay:YES]; + } +#endif +} + +- (void)windowDidEnterFullScreen:(NSNotification *)notification { +#ifdef DARK_UI + if ([self needsFullScreenActivateHack]) { + NSWindow* window = [notification object]; + NSView* contentView = [window contentView]; + + [contentView setNeedsDisplay:YES]; + } +#endif +} + +-(void)initUI:(NSWindow*)mainWindow { + NSView* contentView = [mainWindow contentView]; + NSView* themeView = [contentView superview]; NSRect parentFrame = [themeView frame]; NSButton *windowButton = nil; #ifdef CUSTOM_TRAFFIC_LIGHTS - //hide buttons - windowButton = [theWin standardWindowButton:NSWindowCloseButton]; - [windowButton setHidden:YES]; - windowButton = [theWin standardWindowButton:NSWindowMiniaturizeButton]; - [windowButton setHidden:YES]; - windowButton = [theWin standardWindowButton:NSWindowZoomButton]; - [windowButton setHidden:YES]; - - TrafficLightsViewController *controller = [[TrafficLightsViewController alloc] init]; - - if ([NSBundle loadNibNamed: @"TrafficLights" owner: controller]) - { - NSRect oldFrame = [controller.view frame]; - NSRect newFrame = NSMakeRect(kTrafficLightsViewX, // x position - parentFrame.size.height - oldFrame.size.height - kTrafficLightsViewY, // y position - oldFrame.size.width, // width - oldFrame.size.height); // height - [controller.view setFrame:newFrame]; - [themeView addSubview:controller.view]; + if (!trafficLightsView) { + windowButton = [mainWindow standardWindowButton:NSWindowCloseButton]; + [windowButton setHidden:YES]; + windowButton = [mainWindow standardWindowButton:NSWindowMiniaturizeButton]; + [windowButton setHidden:YES]; + windowButton = [mainWindow standardWindowButton:NSWindowZoomButton]; + [windowButton setHidden:YES]; + + TrafficLightsViewController *tvController = [[TrafficLightsViewController alloc] init]; + if ([NSBundle loadNibNamed: @"TrafficLights" owner: tvController]) + { + NSRect oldFrame = [tvController.view frame]; + NSRect newFrame = NSMakeRect(kTrafficLightsViewX, // x position + parentFrame.size.height - oldFrame.size.height - kTrafficLightsViewY, // y position + oldFrame.size.width, // width + oldFrame.size.height); // height + [tvController.view setFrame:newFrame]; + [themeView addSubview:tvController.view]; + [self setTrafficLightsView:tvController.view]; + } } -#endif +#endif #ifdef DARK_UI - if ([self isFullScreenSupported]) { - windowButton = [theWin standardWindowButton:NSWindowFullScreenButton]; + if ([self isFullScreenSupported] && !fullScreenButtonView) { + windowButton = [mainWindow standardWindowButton:NSWindowFullScreenButton]; [windowButton setHidden:YES]; FullScreenViewController *fsController = [[FullScreenViewController alloc] init]; @@ -396,10 +342,28 @@ - (void)windowDidExitFullScreen:(NSNotification *)notification { } #endif - [themeView setNeedsDisplay:YES]; + +} + +- (void)windowDidExitFullScreen:(NSNotification *)notification { + // This effectively recreates the full screen button in it's default \ + // state. Don't do this until after animation has completed or it will + // be in the wrong state and look funny... + NSWindow* window = [notification object]; + [self initUI:window]; } +-(void)windowWillExitFullScreen:(NSNotification *)notification { + // show the buttons and title bar so they appear during the + // transition from fullscreen back to normal + if (customTitlebar) { + [customTitlebar setHidden:NO]; + } + if (trafficLightsView) { + [trafficLightsView setHidden:NO]; + } +} - (void)alert:(NSString*)title withMessage:(NSString*)message { NSAlert *alert = [NSAlert alertWithMessageText:title @@ -436,6 +400,26 @@ - (void)notifyDownloadError:(id)object { } - (void)windowDidBecomeKey:(NSNotification*)notification { +#ifdef DARK_UI + if (!isReentering) + { + NSWindow *thisWindow = [notification object]; + NSView* contentView = [thisWindow contentView]; + NSRect bounds = [[contentView superview] bounds]; + + customTitlebar = [[CustomTitlebarView alloc] initWithFrame:bounds]; + + [customTitlebar setTitleString: [thisWindow title]]; + + [customTitlebar setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)]; + [[contentView superview] addSubview:customTitlebar positioned:NSWindowBelow relativeTo:[[[contentView superview] subviews] objectAtIndex:0]]; + + NSButton *windowButton = [thisWindow standardWindowButton:NSWindowFullScreenButton]; + [windowButton setHidden:YES]; + isReentering = YES; + } +#endif + if (g_handler.get() && g_handler->GetBrowserId()) { // Give focus to the browser window. g_handler->GetBrowser()->GetHost()->SetFocus(true); @@ -492,14 +476,16 @@ - (void)cleanup:(id)window { @end +// BOBNOTE: Consider moving the AppDelegate interface into its own .h file // Receives notifications from the application. Will delete itself when done. @interface ClientAppDelegate : NSObject - (void)createApp:(id)object; -- (void)addCustomDrawHook:(NSView*)contentView; - (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename; - (BOOL)application:(NSApplication *)theApplication openFiles:(NSArray *)filenames; @end + +// BOBNOTE: Consider moving the AppDelegate implementation into its own .m file @implementation ClientAppDelegate - (id) init { @@ -561,22 +547,8 @@ - (void)createApp:(id)object { backing:NSBackingStoreBuffered defer:NO]; - NSWindow* theWin = mainWnd; - NSButton *windowButton; - -#ifdef CUSTOM_TRAFFIC_LIGHTS - //hide buttons - windowButton = [theWin standardWindowButton:NSWindowCloseButton]; - [windowButton setHidden:YES]; - windowButton = [theWin standardWindowButton:NSWindowMiniaturizeButton]; - [windowButton setHidden:YES]; - windowButton = [theWin standardWindowButton:NSWindowZoomButton]; - [windowButton setHidden:YES]; -#endif - #ifdef DARK_UI NSColorSpace *sRGB = [NSColorSpace sRGBColorSpace]; - float fillComp[4] = {0.23137255f, 0.24705882f, 0.25490196f, 1.0}; // Background fill, solid for now. NSColor *fillColor = [NSColor colorWithColorSpace:sRGB components:fillComp count:4]; [mainWnd setMinSize:NSMakeSize(kMinWindowWidth, kMinWindowHeight)]; @@ -609,13 +581,6 @@ - (void)createApp:(id)object { [mainWnd setReleasedWhenClosed:NO]; NSView* contentView = [mainWnd contentView]; -#ifdef DARK_UI - // Register our custom title bar rendering hook. - [self addCustomDrawHook:contentView]; - windowButton = [theWin standardWindowButton:NSWindowFullScreenButton]; - [windowButton setHidden:YES]; -#endif - // Create the handler. g_handler = new ClientHandler(); @@ -643,46 +608,13 @@ - (void)createApp:(id)object { CefBrowserHost::CreateBrowserSync(window_info, g_handler.get(), [str UTF8String], settings); - NSView *themeView = [[mainWnd contentView] superview]; - NSRect parentFrame = [themeView frame]; - -#ifdef CUSTOM_TRAFFIC_LIGHTS - TrafficLightsViewController *tvController = [[TrafficLightsViewController alloc] init]; - if ([NSBundle loadNibNamed: @"TrafficLights" owner: tvController]) - { - NSRect oldFrame = [tvController.view frame]; - NSRect newFrame = NSMakeRect(kTrafficLightsViewX, // x position - parentFrame.size.height - oldFrame.size.height - kTrafficLightsViewY, // y position - oldFrame.size.width, // width - oldFrame.size.height); // height - [tvController.view setFrame:newFrame]; - [themeView addSubview:tvController.view]; - } - -#endif - -#ifdef DARK_UI - if ([delegate isFullScreenSupported]) { - FullScreenViewController *fsController = [[FullScreenViewController alloc] init]; - if ([NSBundle loadNibNamed: @"FullScreen" owner: fsController]) - { - NSRect oldFrame = [fsController.view frame]; - NSRect newFrame = NSMakeRect(parentFrame.size.width - oldFrame.size.width - 4, // x position - parentFrame.size.height - oldFrame.size.height - kTrafficLightsViewY, // y position - oldFrame.size.width, // width - oldFrame.size.height); // height - [fsController.view setFrame:newFrame]; - [themeView addSubview:fsController.view]; - [delegate setFullScreenButtonView:fsController.view]; - } - } -#endif - + [delegate initUI:mainWnd]; // Show the window. [mainWnd display]; [mainWnd makeKeyAndOrderFront: nil]; [NSApp requestUserAttention:NSInformationalRequest]; + [NSApp unhide:nil]; } @@ -724,20 +656,6 @@ - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)theAp return NSTerminateNow; } - -- (void)addCustomDrawHook:(NSView*)contentView -{ - NSView* themeView = [contentView superview]; - - object_setClass(themeView, GetShellWindowFrameClass()); - -#ifdef LIGHT_CAPTION_TEXT - // Reset our frame view text cell background style - NSTextFieldCell * cell = [themeView titleCell]; - [cell setBackgroundStyle:NSBackgroundStyleLight]; -#endif -} - - (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename { if (!pendingOpenFiles) { ClientApplication * clientApp = (ClientApplication *)theApplication; @@ -854,7 +772,7 @@ int main(int argc, char* argv[]) { if ([[NSFileManager defaultManager] fileExistsAtPath:devFile]) { startupUrl = [NSURL fileURLWithPath:devFile]; } - + if (startupUrl == nil) { // If the dev file wasn't found, look for /Contents/www/index.html NSString* indexFile = [bundlePath stringByAppendingString:@"/Contents/www/index.html"]; @@ -882,7 +800,7 @@ int main(int argc, char* argv[]) { // Create the application delegate and window. [delegate performSelectorOnMainThread:@selector(createApp:) withObject:nil - waitUntilDone:NO]; + waitUntilDone:YES]; // Run the application message loop. CefRunMessageLoop(); diff --git a/appshell/client_handler_mac.mm b/appshell/client_handler_mac.mm index 816ed883a..78bd996eb 100644 --- a/appshell/client_handler_mac.mm +++ b/appshell/client_handler_mac.mm @@ -18,18 +18,11 @@ #include "config.h" #include "client_colors_mac.h" - +#import "CustomTitlebarView.h" extern CefRefPtr g_handler; -// Custom draw interface for NSThemeFrame -@interface NSView (UndocumentedAPI) -- (float)roundedCornerRadius; -- (CGRect)_titlebarTitleRect; -- (NSTextFieldCell*)titleCell; -- (void)_drawTitleStringIn:(struct CGRect)arg1 withColor:(id)color; -@end // ClientHandler::ClientLifeSpanHandler implementation @@ -103,8 +96,9 @@ @interface PopupClientWindowDelegate : NSObject { CefRefPtr clientHandler; NSWindow* window; NSView* fullScreenButtonView; + NSView* trafficLightsView; BOOL isReallyClosing; - NSString* savedTitle; + CustomTitlebarView *customTitlebar; } - (IBAction)quit:(id)sender; - (IBAction)handleMenuAction:(id)sender; @@ -113,116 +107,29 @@ - (BOOL)windowShouldClose:(id)window; - (void)setClientHandler:(CefRefPtr)handler; - (void)setWindow:(NSWindow*)window; - (void)setFullScreenButtonView:(NSView*)view; -- (void)addCustomDrawHook:(NSView*)contentView; +- (void)setTrafficLightsView:(NSView*)view; - (BOOL)isFullScreenSupported; +- (void)makeDark; +- (void)initUI; @end -/** - * The patched implementation for drawRect that lets us tweak - * the title bar. - */ -void PopupWindowFrameDrawRect(id self, SEL _cmd, NSRect rect) { - // Clear to 0 alpha - [[NSColor clearColor] set]; - NSRectFill( rect ); - //Obtain reference to our NSThemeFrame view - NSRect windowRect = [self frame]; - windowRect.origin = NSMakePoint(0,0); - //This constant matches the radius for other macosx apps. - //For some reason if we use the default value it is double that of safari etc. - float cornerRadius = 4.0f; - - //Clip our title bar render - [[NSBezierPath bezierPathWithRoundedRect:windowRect - xRadius:cornerRadius - yRadius:cornerRadius] addClip]; - [[NSBezierPath bezierPathWithRect:rect] addClip]; - - - - NSColorSpace *sRGB = [NSColorSpace sRGBColorSpace]; - // Background fill, solid for now. - NSColor *fillColor = [NSColor colorWithColorSpace:sRGB components:fillComp count:4]; - [fillColor set]; - NSRectFill( rect ); - NSColor *activeColor = [NSColor colorWithColorSpace:sRGB components:activeComp count:4]; - NSColor *inactiveColor = [NSColor colorWithColorSpace:sRGB components:inactiveComp count:4]; - // Render our title text - [self _drawTitleStringIn:[self _titlebarTitleRect] - withColor:[NSApp isActive] ? - activeColor : inactiveColor]; - -} - - -/** - * Create a custom class based on NSThemeFrame called - * ShellWindowFrame. ShellWindowFrame uses ShellWindowFrameDrawRect() - * as the implementation for the drawRect selector allowing us - * to draw the border/title bar the way we see fit. - */ -Class GetPopuplWindowFrameClass() { - // lazily change the class implementation if - // not done so already. - static Class k = NULL; - if (!k) { - // See http://cocoawithlove.com/2010/01/what-is-meta-class-in-objective-c.html - Class NSThemeFrame = NSClassFromString(@"NSThemeFrame"); - k = objc_allocateClassPair(NSThemeFrame, "PopupWindowFrame", 0); - Method m0 = class_getInstanceMethod(NSThemeFrame, @selector(drawRect:)); - class_addMethod(k, @selector(drawRect:), - (IMP)PopupWindowFrameDrawRect, method_getTypeEncoding(m0)); - objc_registerClassPair(k); - } - return k; -} @implementation PopupClientWindowDelegate - (id) init { [super init]; - isReallyClosing = false; - savedTitle = nil; + isReallyClosing = NO; fullScreenButtonView = nil; + customTitlebar = nil; + trafficLightsView = nil; return self; } - (IBAction)quit:(id)sender { - /* - CefRefPtr browser; - - // If the main browser exists, send the command to that browser - if (clientHandler->GetBrowserId()) - browser = clientHandler->GetBrowser(); - - if (!browser) - browser = ClientHandler::GetBrowserForNativeWindow(window); - - // TODO: we should have a "get frontmost brackets window" command for this - - if (clientHandler && browser) { - clientHandler->SendJSCommand(browser, FILE_QUIT); - } else { - [NSApp terminate:nil]; - } - */ clientHandler->DispatchCloseToNextBrowser(); } -- (void)addCustomDrawHook:(NSView*)contentView -{ - NSView* themeView = [contentView superview]; - - object_setClass(themeView, GetPopuplWindowFrameClass()); - -#ifdef LIGHT_CAPTION_TEXT - // Reset our frame view text cell background style - NSTextFieldCell * cell = [themeView titleCell]; - [cell setBackgroundStyle:NSBackgroundStyleLight]; -#endif -} - - (IBAction)handleMenuAction:(id)sender { if (clientHandler.get() && clientHandler->GetBrowserId()) { CefRefPtr browser = ClientHandler::GetBrowserForNativeWindow(window); @@ -264,28 +171,49 @@ - (BOOL)isFullScreenSupported { return (version >= 0x1070); } -- (void)setFullScreenButtonView:(NSView *)view { + +-(BOOL)needsFullScreenActivateHack { + SInt32 version; + Gestalt(gestaltSystemVersion, &version); + return (version >= 0x1090); +} + +-(void)setFullScreenButtonView:(NSView *)view { fullScreenButtonView = view; } +-(void)setTrafficLightsView:(NSView *)view { + trafficLightsView = view; +} + -(void)windowTitleDidChange:(NSString*)title { #ifdef DARK_UI - savedTitle = [title copy]; + if (customTitlebar) { + [customTitlebar setTitleString:title]; + } #endif } -- (void)windowWillEnterFullScreen:(NSNotification *)notification { -#ifdef DARK_UI - savedTitle = [[window title] copy]; - [window setTitle:@""]; -#endif +-(void)makeDark { + if (!customTitlebar) + { + NSView* contentView = [window contentView]; + NSRect bounds = [[contentView superview] bounds]; + + customTitlebar = [[CustomTitlebarView alloc] initWithFrame:bounds]; + + [customTitlebar setTitleString: [window title]]; + + [customTitlebar setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)]; + [[contentView superview] addSubview:customTitlebar positioned:NSWindowBelow relativeTo:[[[contentView superview] subviews] objectAtIndex:0]]; + } } -(void)windowDidResize:(NSNotification *)notification { #ifdef DARK_UI - if ([self isFullScreenSupported]) { + if ([self isFullScreenSupported] && fullScreenButtonView) { NSView* themeView = [[window contentView] superview]; NSRect parentFrame = [themeView frame]; @@ -302,47 +230,42 @@ -(void)windowDidResize:(NSNotification *)notification #endif } -- (void)windowDidExitFullScreen:(NSNotification *)notification { -#ifdef DARK_UI - NSView* contentView = [window contentView]; - [self addCustomDrawHook: contentView]; - [window setTitle:savedTitle]; - [savedTitle release]; -#endif - - NSView * themeView = [[window contentView] superview]; - NSRect parentFrame = [themeView frame]; +- (void)initUI { NSWindow* theWin = window; - NSButton *windowButton = nil; + NSView* themeView = [[window contentView] superview]; + NSRect parentFrame = [themeView frame]; + NSButton* windowButton = nil; #ifdef CUSTOM_TRAFFIC_LIGHTS - //hide buttons - windowButton = [theWin standardWindowButton:NSWindowCloseButton]; - [windowButton setHidden:YES]; - windowButton = [theWin standardWindowButton:NSWindowMiniaturizeButton]; - [windowButton setHidden:YES]; - windowButton = [theWin standardWindowButton:NSWindowZoomButton]; - [windowButton setHidden:YES]; - - TrafficLightsViewController *controller = [[TrafficLightsViewController alloc] init]; - - if ([NSBundle loadNibNamed: @"TrafficLights" owner: controller]) - { - NSRect oldFrame = [controller.view frame]; - NSRect newFrame = NSMakeRect(kTrafficLightsViewX, // x position - parentFrame.size.height - oldFrame.size.height - kTrafficLightsViewY, // y position - oldFrame.size.width, // width - oldFrame.size.height); // height - [controller.view setFrame:newFrame]; - [themeView addSubview:controller.view]; + if (!trafficLightsView) { + windowButton = [theWin standardWindowButton:NSWindowCloseButton]; + [windowButton setHidden:YES]; + windowButton = [theWin standardWindowButton:NSWindowMiniaturizeButton]; + [windowButton setHidden:YES]; + windowButton = [theWin standardWindowButton:NSWindowZoomButton]; + [windowButton setHidden:YES]; + + TrafficLightsViewController *tlController = [[TrafficLightsViewController alloc] init]; + + if ([NSBundle loadNibNamed: @"TrafficLights" owner: tlController]) + { + NSRect oldFrame = [tlController.view frame]; + NSRect newFrame = NSMakeRect(kTrafficLightsViewX, // x position + parentFrame.size.height - oldFrame.size.height - 4, // y position + oldFrame.size.width, // width + oldFrame.size.height); // height + [tlController.view setFrame:newFrame]; + [themeView addSubview:tlController.view]; + [self setTrafficLightsView:tlController.view]; + } } #endif #ifdef DARK_UI - if ([self isFullScreenSupported]) { + if ([self isFullScreenSupported] && !fullScreenButtonView) { windowButton = [theWin standardWindowButton:NSWindowFullScreenButton]; [windowButton setHidden:YES]; - + FullScreenViewController *fsController = [[FullScreenViewController alloc] init]; if ([NSBundle loadNibNamed: @"FullScreen" owner: fsController]) { @@ -356,11 +279,69 @@ - (void)windowDidExitFullScreen:(NSNotification *)notification { [self setFullScreenButtonView:fsController.view]; } } + [self makeDark]; #endif + - [themeView setNeedsDisplay:YES]; } +-(void)windowWillExitFullScreen:(NSNotification *)notification { + // unhide these so they appear as the window + // transforms from full screen back to normal + if (customTitlebar) { + [customTitlebar setHidden:NO]; + } + if (trafficLightsView) { + [trafficLightsView setHidden:NO]; + } +} + + +-(void)windowDidExitFullScreen:(NSNotification *)notification { + // This effectively recreates the fs button + // but we have to wait until after the animation + // is complete to create the button. it will display + // in the wrong state if we do it sooner + [self initUI]; +} + + +- (void)windowWillEnterFullScreen:(NSNotification *)notification { +#ifdef DARK_UI + // hide all of the elements so the os can make our + // window's content view can take up the entire display surface + if (fullScreenButtonView) { + [fullScreenButtonView removeFromSuperview]; + fullScreenButtonView = nil; + } + if (trafficLightsView) { + [trafficLightsView setHidden:YES]; + } + if (customTitlebar) { + [customTitlebar setHidden:YES]; + } + if ([self needsFullScreenActivateHack]) { + // HACK to make sure that window is activate + // when going into full screen mode + [NSApp activateIgnoringOtherApps:YES]; + [NSApp unhide:nil]; + NSView* contentView = [window contentView]; + [contentView setNeedsDisplay:YES]; + } +#endif +} + + +- (void)windowDidEnterFullScreen:(NSNotification *)notification { +#ifdef DARK_UI + if ([self needsFullScreenActivateHack]) { + // HACK to make sure that window is activate + // when going into full screen mode + NSView* contentView = [window contentView]; + [contentView setNeedsDisplay:YES]; + } +#endif +} // Called when the window is about to close. Perform the self-destruction // sequence by getting rid of the window. By returning YES, we allow the window @@ -402,6 +383,10 @@ - (void)cleanup:(id)window { } - (void)windowDidBecomeKey:(NSNotification*)notification { +#ifdef DARK_UI + [self makeDark]; +#endif + CefRefPtr browser = ClientHandler::GetBrowserForNativeWindow([notification object]); if(browser) { // Give focus to the browser window. @@ -440,59 +425,7 @@ - (void)windowDidResignKey:(NSNotification *)notification { [delegate setClientHandler:this]; [delegate setWindow:window]; [window setDelegate:delegate]; -#ifdef DARK_UI - NSView* contentView = [window contentView]; - [delegate addCustomDrawHook: contentView]; -#endif - - NSWindow* theWin = window; - NSView* themeView = [[window contentView] superview]; - NSRect parentFrame = [themeView frame]; - NSButton* windowButton = nil; - -#ifdef CUSTOM_TRAFFIC_LIGHTS - windowButton = [theWin standardWindowButton:NSWindowCloseButton]; - [windowButton setHidden:YES]; - windowButton = [theWin standardWindowButton:NSWindowMiniaturizeButton]; - [windowButton setHidden:YES]; - windowButton = [theWin standardWindowButton:NSWindowZoomButton]; - [windowButton setHidden:YES]; - - TrafficLightsViewController *controller = [[TrafficLightsViewController alloc] init]; - - if ([NSBundle loadNibNamed: @"TrafficLights" owner: controller]) - { - NSRect oldFrame = [controller.view frame]; - NSRect newFrame = NSMakeRect(kTrafficLightsViewX, // x position - parentFrame.size.height - oldFrame.size.height - 4, // y position - oldFrame.size.width, // width - oldFrame.size.height); // height - [controller.view setFrame:newFrame]; - [themeView addSubview:controller.view]; - } -#endif - -#ifdef DARK_UI - if ([delegate isFullScreenSupported]) { - windowButton = [theWin standardWindowButton:NSWindowFullScreenButton]; - [windowButton setHidden:YES]; - - FullScreenViewController *fsController = [[FullScreenViewController alloc] init]; - if ([NSBundle loadNibNamed: @"FullScreen" owner: fsController]) - { - NSRect oldFrame = [fsController.view frame]; - NSRect newFrame = NSMakeRect(parentFrame.size.width - oldFrame.size.width - 4, // x position - parentFrame.size.height - oldFrame.size.height - kTrafficLightsViewY, // y position - oldFrame.size.width, // width - oldFrame.size.height); // height - [fsController.view setFrame:newFrame]; - [themeView addSubview:fsController.view]; - [delegate setFullScreenButtonView:fsController.view]; - } - } -#endif - - [themeView setNeedsDisplay:YES]; + [delegate initUI]; } } diff --git a/appshell/config.h b/appshell/config.h index a5b4e6817..c1331bd7e 100644 --- a/appshell/config.h +++ b/appshell/config.h @@ -63,11 +63,7 @@ #define REMOTE_DEBUGGING_PORT 9234 -#ifdef OS_WIN - // Comment out this line to enable OS themed drawing #define DARK_UI #define CUSTOM_TRAFFIC_LIGHTS #define LIGHT_CAPTION_TEXT - -#endif diff --git a/appshell_paths.gypi b/appshell_paths.gypi index 17e3596b0..7d747d934 100755 --- a/appshell_paths.gypi +++ b/appshell_paths.gypi @@ -149,6 +149,8 @@ 'appshell/TrafficLightsView.mm', 'appshell/TrafficLightsViewController.h', 'appshell/TrafficLightsViewController.mm', + 'appshell/CustomTitlebarView.h', + 'appshell/CustomTitlebarView.m', 'appshell/FullScreenButton.h', 'appshell/FullScreenButton.mm', 'appshell/FullScreenView.h', @@ -177,6 +179,8 @@ 'appshell/TrafficLightsView.mm', 'appshell/TrafficLightsViewController.h', 'appshell/TrafficLightsViewController.mm', + 'appshell/CustomTitlebarView.h', + 'appshell/CustomTitlebarView.m', 'appshell/FullScreenButton.h', 'appshell/FullScreenButton.mm', 'appshell/FullScreenView.h',