diff options
author | avi@chromium.org <avi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-08-26 20:20:51 +0000 |
---|---|---|
committer | avi@chromium.org <avi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-08-26 20:20:51 +0000 |
commit | 71f4c1dc80499f254b9f06e510002ff3b43a87e0 (patch) | |
tree | 6eeb8693a47e7990e5bb3995812e2fa641bcda09 /chrome/browser/cocoa | |
parent | 7cc6c0ba59df63f9f084f8cdfc695fa7b4ae1823 (diff) | |
download | chromium_src-71f4c1dc80499f254b9f06e510002ff3b43a87e0.zip chromium_src-71f4c1dc80499f254b9f06e510002ff3b43a87e0.tar.gz chromium_src-71f4c1dc80499f254b9f06e510002ff3b43a87e0.tar.bz2 |
Updates to clean up default theme and add hover states.
Path by Cole.
BUG=http://crbug.com/18573;http://crbug.com/18574;http://crbug.com/18360;http://crbug.com/18438
TEST=none
Review URL: http://codereview.chromium.org/165499
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@24512 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/cocoa')
21 files changed, 505 insertions, 152 deletions
diff --git a/chrome/browser/cocoa/background_gradient_view.mm b/chrome/browser/cocoa/background_gradient_view.mm index df3527a..de35705 100644 --- a/chrome/browser/cocoa/background_gradient_view.mm +++ b/chrome/browser/cocoa/background_gradient_view.mm @@ -5,7 +5,7 @@ #include "chrome/browser/cocoa/background_gradient_view.h" #define kToolbarTopOffset 12 -#define kToolbarMaxHeight 128 +#define kToolbarMaxHeight 100 @implementation BackgroundGradientView @synthesize showsDivider = showsDivider_; diff --git a/chrome/browser/cocoa/bookmark_bar_controller.mm b/chrome/browser/cocoa/bookmark_bar_controller.mm index 84cbbd3..ed45bcc 100644 --- a/chrome/browser/cocoa/bookmark_bar_controller.mm +++ b/chrome/browser/cocoa/bookmark_bar_controller.mm @@ -45,7 +45,7 @@ namespace { // Our height, when opened. -const int kBookmarkBarHeight = 30; +const int kBookmarkBarHeight = 28; // Magic numbers from Cole const CGFloat kDefaultBookmarkWidth = 150.0; diff --git a/chrome/browser/cocoa/browser_window_controller.mm b/chrome/browser/cocoa/browser_window_controller.mm index 03c6146..33e5b6b 100644 --- a/chrome/browser/cocoa/browser_window_controller.mm +++ b/chrome/browser/cocoa/browser_window_controller.mm @@ -96,6 +96,7 @@ willPositionSheet:(NSWindow*)sheet // Repositions the windows subviews. - (void)layoutSubviews; + @end @@ -927,8 +928,6 @@ willPositionSheet:(NSWindow*)sheet - (void)userChangedTheme { [self setTheme]; [self applyTheme]; - - [tabStripController_ userChangedTheme]; } - (GTMTheme *)gtm_themeForWindow:(NSWindow*)window { @@ -1133,6 +1132,7 @@ willPositionSheet:(NSWindow*)sheet [theme_ backgroundPatternColorForStyle:GTMThemeStyleWindow state:[[self window] isMainWindow]]; [[self window] setBackgroundColor:color]; + [tabStripController_ applyTheme]; } // Private method to layout browser window subviews. Positions the toolbar and @@ -1229,36 +1229,45 @@ willPositionSheet:(NSWindow*)sheet [theme setValue:frameImage forAttribute:@"backgroundImage" style:GTMThemeStyleWindow - state:YES]; + state:GTMThemeStateActiveWindow]; - NSColor* tabTextColor = [NSColor blackColor]; + NSColor* tabTextColor = + provider->GetNSColor(BrowserThemeProvider::COLOR_TAB_TEXT); [theme setValue:tabTextColor forAttribute:@"textColor" style:GTMThemeStyleToolBar - state:YES]; + state:GTMThemeStateActiveWindow]; - NSColor* tabInactiveTextColor = [NSColor grayColor]; + NSColor* tabInactiveTextColor = + provider->GetNSColor(BrowserThemeProvider::COLOR_BACKGROUND_TAB_TEXT); [theme setValue:tabInactiveTextColor forAttribute:@"textColor" - style:GTMThemeStyleToolBar - state:NO]; + style:GTMThemeStyleTabBarDeselected + state:GTMThemeStateActiveWindow]; - NSColor* bookmarkBarTextColor = [NSColor blackColor]; + NSColor* bookmarkBarTextColor = + provider->GetNSColor(BrowserThemeProvider::COLOR_BOOKMARK_TEXT); [theme setValue:bookmarkBarTextColor forAttribute:@"textColor" style:GTMThemeStyleBookmarksBarButton - state:YES]; + state:GTMThemeStateActiveWindow]; [theme setValue:frameInactiveImage forAttribute:@"backgroundImage" style:GTMThemeStyleWindow - state:NO]; + state:0]; NSImage* toolbarImage = provider->GetNSImageNamed(IDR_THEME_TOOLBAR); [theme setValue:toolbarImage forAttribute:@"backgroundImage" style:GTMThemeStyleToolBar - state:YES]; + state:GTMThemeStateActiveWindow]; + NSImage* toolbarBackgroundImage = + provider->GetNSImageNamed(IDR_THEME_TAB_BACKGROUND); + [theme setValue:toolbarBackgroundImage + forAttribute:@"backgroundImage" + style:GTMThemeStyleTabBarDeselected + state:GTMThemeStateActiveWindow]; NSImage* toolbarButtonImage = provider->GetNSImageNamed(IDR_THEME_BUTTON_BACKGROUND); @@ -1266,7 +1275,7 @@ willPositionSheet:(NSWindow*)sheet [theme setValue:toolbarButtonImage forAttribute:@"backgroundImage" style:GTMThemeStyleToolBarButton - state:YES]; + state:GTMThemeStateActiveWindow]; } else { NSColor* startColor = [NSColor colorWithCalibratedWhite:1.0 alpha:0.0]; NSColor* endColor = [NSColor colorWithCalibratedWhite:1.0 alpha:0.3]; @@ -1277,12 +1286,12 @@ willPositionSheet:(NSWindow*)sheet [theme setValue:gradient forAttribute:@"gradient" style:GTMThemeStyleToolBarButton - state:YES]; + state:GTMThemeStateActiveWindow]; [theme setValue:gradient forAttribute:@"gradient" style:GTMThemeStyleToolBarButton - state:NO]; + state:GTMThemeStateActiveWindow]; } NSColor* toolbarButtonIconColor = @@ -1290,13 +1299,20 @@ willPositionSheet:(NSWindow*)sheet [theme setValue:toolbarButtonIconColor forAttribute:@"iconColor" style:GTMThemeStyleToolBarButton - state:YES]; + state:GTMThemeStateActiveWindow]; NSColor* toolbarButtonBorderColor = toolbarButtonIconColor; [theme setValue:toolbarButtonBorderColor forAttribute:@"borderColor" style:GTMThemeStyleToolBar - state:YES]; + state:GTMThemeStateActiveWindow]; + + NSColor* toolbarBackgroundColor = + provider->GetNSColor(BrowserThemeProvider::COLOR_TOOLBAR); + [theme setValue:toolbarBackgroundColor + forAttribute:@"backgroundColor" + style:GTMThemeStyleToolBar + state:GTMThemeStateActiveWindow]; return theme; } diff --git a/chrome/browser/cocoa/browser_window_controller_unittest.mm b/chrome/browser/cocoa/browser_window_controller_unittest.mm index 743c90c..68273e6 100644 --- a/chrome/browser/cocoa/browser_window_controller_unittest.mm +++ b/chrome/browser/cocoa/browser_window_controller_unittest.mm @@ -192,7 +192,7 @@ TEST_F(BrowserWindowControllerTest, TestResizeViews) { [tabstrip setFrame:tabstripFrame]; // Make sure each view is as tall as we expect. - ASSERT_EQ(39, NSHeight([toolbar frame])); + ASSERT_EQ(36, NSHeight([toolbar frame])); ASSERT_EQ(0, NSHeight([infobar frame])); // Force a layout and check each view's frame. diff --git a/chrome/browser/cocoa/download_item_cell.mm b/chrome/browser/cocoa/download_item_cell.mm index 7c82b2b..3890807 100644 --- a/chrome/browser/cocoa/download_item_cell.mm +++ b/chrome/browser/cocoa/download_item_cell.mm @@ -333,8 +333,9 @@ const int kCompleteAnimationDuration = 2.5; controlView:controlView outerPath:buttonOuterPath innerPath:buttonInnerPath - showHighlightGradient:[self isMouseOverButtonPart] showClickedGradient:[self isButtonPartPressed] + showHighlightGradient:[self isMouseOverButtonPart] + hoverAlpha:0.0 active:active cellFrame:cellFrame]; @@ -342,8 +343,9 @@ const int kCompleteAnimationDuration = 2.5; controlView:controlView outerPath:dropdownOuterPath innerPath:dropdownInnerPath - showHighlightGradient:[self isMouseOverDropdownPart] showClickedGradient:[self isDropdownPartPressed] + showHighlightGradient:[self isMouseOverDropdownPart] + hoverAlpha:0.0 active:active cellFrame:cellFrame]; diff --git a/chrome/browser/cocoa/gradient_button_cell.h b/chrome/browser/cocoa/gradient_button_cell.h index e9b4e79..79be6ff 100644 --- a/chrome/browser/cocoa/gradient_button_cell.h +++ b/chrome/browser/cocoa/gradient_button_cell.h @@ -34,7 +34,8 @@ typedef NSInteger ButtonType; BOOL isMouseInside_; scoped_nsobject<NSTrackingArea> trackingArea_; BOOL shouldTheme_; - scoped_nsobject<NSGradient> gradient_; + CGFloat hoverAlpha_; // 0-1. Controls the alpha during mouse hover + NSTimeInterval lastHoverUpdate_; scoped_nsobject<NSImage> underlayImage_; } @@ -45,8 +46,9 @@ typedef NSInteger ButtonType; controlView:(NSView*)controlView outerPath:(NSBezierPath*)outerPath innerPath:(NSBezierPath*)innerPath - showHighlightGradient:(BOOL)showHighlightGradient showClickedGradient:(BOOL)showClickedGradient + showHighlightGradient:(BOOL)showHighlightGradient + hoverAlpha:(CGFloat)hoverAlpha active:(BOOL)active cellFrame:(NSRect)cellFrame; @@ -54,6 +56,11 @@ typedef NSInteger ButtonType; - (NSImage*)underlayImage; - (void)setUnderlayImage:(NSImage*)image; +// Let the view know when the mouse moves in and out. A timer will update +// the current hoverAlpha_ based on these events. +- (void)setMouseInside:(BOOL)flag animate:(BOOL)animate; + +@property(assign, nonatomic)CGFloat hoverAlpha; @end @interface GradientButtonCell(TestingAPI) diff --git a/chrome/browser/cocoa/gradient_button_cell.mm b/chrome/browser/cocoa/gradient_button_cell.mm index 17d4998..df2a7c9 100644 --- a/chrome/browser/cocoa/gradient_button_cell.mm +++ b/chrome/browser/cocoa/gradient_button_cell.mm @@ -2,9 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#import "base/scoped_nsobject.h" #include "chrome/browser/cocoa/gradient_button_cell.h" #import "third_party/GTM/AppKit/GTMTheme.h" -#import "base/scoped_nsobject.h" +#import "third_party/GTM/AppKit/GTMNSColor+Luminance.h" @interface GradientButtonCell (Private) - (void)sharedInit; @@ -12,8 +13,48 @@ inView:(NSView*)controlView; @end +static const NSTimeInterval kAnimationShowDuration = 0.2; +static const NSTimeInterval kAnimationHideDuration = 0.4; @implementation GradientButtonCell +@synthesize hoverAlpha = hoverAlpha_; + +- (void)adjustHoverValue { + NSTimeInterval thisUpdate = [NSDate timeIntervalSinceReferenceDate]; + + NSTimeInterval elapsed = thisUpdate - lastHoverUpdate_; + + CGFloat opacity = [self hoverAlpha]; + if (isMouseInside_) { + opacity += elapsed / kAnimationShowDuration; + } else { + opacity -= elapsed / kAnimationHideDuration; + } + + if (!isMouseInside_ && opacity < 0) { + opacity = 0; + } else if (isMouseInside_ && opacity > 1) { + opacity = 1; + } else { + [self performSelector:_cmd withObject:nil afterDelay:0.02]; + } + lastHoverUpdate_ = thisUpdate; + [self setHoverAlpha:opacity]; + + [[self controlView] setNeedsDisplay:YES]; +} + +- (void)setMouseInside:(BOOL)flag animate:(BOOL)animated { + isMouseInside_ = flag; + if (animated) { + lastHoverUpdate_ = [NSDate timeIntervalSinceReferenceDate]; + [self adjustHoverValue]; + } else { + [NSObject cancelPreviousPerformRequestsWithTarget:self]; + [self setHoverAlpha:flag ? 1.0 : 0.0]; + } + [[self controlView] setNeedsDisplay:YES]; +} // For nib instantiations - (id)initWithCoder:(NSCoder*)decoder { @@ -33,10 +74,6 @@ - (void)sharedInit { shouldTheme_ = YES; - NSColor* startColor = [NSColor colorWithCalibratedWhite:1.0 alpha:0.666]; - NSColor* endColor = [NSColor colorWithCalibratedWhite:1.0 alpha:0.333]; - gradient_.reset([[NSGradient alloc] - initWithColorsAndLocations:startColor, 0.33, endColor, 1.0, nil]); } - (void)setShouldTheme:(BOOL)shouldTheme { @@ -59,13 +96,11 @@ } - (void)mouseEntered:(NSEvent *)theEvent { - isMouseInside_ = YES; - [[self controlView] setNeedsDisplay:YES]; + [self setMouseInside:YES animate:YES]; } - (void)mouseExited:(NSEvent *)theEvent { - isMouseInside_ = NO; - [[self controlView] setNeedsDisplay:YES]; + [self setMouseInside:NO animate:YES]; } - (BOOL)isMouseInside { @@ -102,13 +137,11 @@ controlView:(NSView*)controlView outerPath:(NSBezierPath*)outerPath innerPath:(NSBezierPath*)innerPath - showHighlightGradient:(BOOL)showHighlightGradient showClickedGradient:(BOOL)showClickedGradient + showHighlightGradient:(BOOL)showHighlightGradient + hoverAlpha:(CGFloat)hoverAlpha active:(BOOL)active cellFrame:(NSRect)cellFrame { - [[NSColor colorWithCalibratedWhite:1.0 alpha:0.25] set]; - [outerPath stroke]; - NSImage* backgroundImage = [theme backgroundImageForStyle:GTMThemeStyleToolBarButton state:YES]; @@ -129,45 +162,68 @@ } } - if (!showClickedGradient && showHighlightGradient) { + BOOL isCustomTheme = backgroundImage != nil; + + if (!showClickedGradient && [self isEnabled]) { [NSGraphicsContext saveGraphicsState]; [innerPath addClip]; // Draw the inner glow. - [innerPath setLineWidth:2]; - [[NSColor colorWithCalibratedWhite:1.0 alpha:0.9] setStroke]; - [innerPath stroke]; - - [[NSColor colorWithCalibratedWhite:1.0 alpha:0.9] setStroke]; - [[NSColor colorWithCalibratedWhite:1.0 alpha:0.2] setFill]; + if (hoverAlpha > 0) { + [innerPath setLineWidth:2]; + [[NSColor colorWithCalibratedWhite:1.0 alpha:0.2 * hoverAlpha] setStroke]; + [innerPath stroke]; + } // Draw the top inner highlight. NSAffineTransform* highlightTransform = [NSAffineTransform transform]; [highlightTransform translateXBy:1 yBy:1]; scoped_nsobject<NSBezierPath> highlightPath([innerPath copy]); [highlightPath transformUsingAffineTransform:highlightTransform]; - + [[NSColor colorWithCalibratedWhite:1.0 alpha:0.2] setStroke]; [highlightPath stroke]; - [gradient_ drawInBezierPath:innerPath angle:90.0]; + CGFloat startAlpha = 0.6 + 0.3 * hoverAlpha; + CGFloat endAlpha = 0.333 * hoverAlpha; + + if (isCustomTheme) { + startAlpha = 0.2 + 0.35 * hoverAlpha; + endAlpha = 0.333 * hoverAlpha; + } + + NSColor* startColor = + [NSColor colorWithCalibratedWhite:1.0 + alpha:startAlpha]; + NSColor* endColor = + [NSColor colorWithCalibratedWhite:1.0 - 0.15 * hoverAlpha + alpha:endAlpha]; + scoped_nsobject<NSBezierPath> gradient([[NSGradient alloc] + initWithColorsAndLocations:startColor, hoverAlpha * 0.33, + endColor, 1.0, nil]); + + [gradient drawInBezierPath:innerPath angle:90.0]; [NSGraphicsContext restoreGraphicsState]; } + // Draw the outer stroke NSColor* stroke = [theme strokeColorForStyle:GTMThemeStyleToolBarButton state:active]; + + if (showClickedGradient) { + stroke = [NSColor colorWithCalibratedWhite:0.0 alpha:0.3]; + } [stroke setStroke]; [innerPath setLineWidth:1]; [innerPath stroke]; } - - (void)drawWithFrame:(NSRect)cellFrame inView:(NSView*)controlView { // Constants from Cole. Will kConstant them once the feedback loop // is complete. NSRect drawFrame = NSInsetRect(cellFrame, 1.5, 1.5); - NSRect innerFrame = NSInsetRect(cellFrame, 2, 2); + NSRect innerFrame = NSInsetRect(cellFrame, 2, 1); ButtonType type = [[(NSControl*)controlView cell] tag]; switch (type) { case kMiddleButtonType: @@ -212,8 +268,9 @@ controlView:controlView outerPath:outerPath innerPath:innerPath - showHighlightGradient:YES showClickedGradient:pressed + showHighlightGradient:[self isHighlighted] + hoverAlpha:[self hoverAlpha] active:active cellFrame:cellFrame]; } @@ -243,9 +300,14 @@ CGContextRef context = (CGContextRef)([[NSGraphicsContext currentContext] graphicsPort]); + NSColor* color = [theme iconColorForStyle:GTMThemeStyleToolBarButton + state:YES]; + if (isTemplate) { scoped_nsobject<NSShadow> shadow([[NSShadow alloc] init]); - [shadow setShadowColor:[NSColor whiteColor]]; + NSColor *shadowColor = [color gtm_legibleTextColor]; + shadowColor = [shadowColor colorWithAlphaComponent:0.25]; + [shadow setShadowColor:shadowColor]; [shadow setShadowOffset:NSMakeSize(0, -1.0)]; [shadow setShadowBlurRadius:1.0]; [shadow set]; @@ -256,14 +318,13 @@ CGContextBeginTransparencyLayer(context, 0); NSRect imageRect = NSZeroRect; imageRect.size = [[self image] size]; + NSRect drawRect = [self imageRectForBounds:cellFrame]; [[self image] setFlipped:[controlView isFlipped]]; - [[self image] drawInRect:[self imageRectForBounds:cellFrame] + [[self image] drawInRect:drawRect fromRect:imageRect operation:NSCompositeSourceOver fraction:[self isEnabled] ? 1.0 : 0.5]; if (isTemplate) { - NSColor* color = [theme iconColorForStyle:GTMThemeStyleToolBarButton - state:YES]; if (color) { [color set]; NSRectFillUsingOperation(cellFrame, NSCompositeSourceAtop); diff --git a/chrome/browser/cocoa/gradient_button_cell_unittest.mm b/chrome/browser/cocoa/gradient_button_cell_unittest.mm index 3e0f162..e9229a7 100644 --- a/chrome/browser/cocoa/gradient_button_cell_unittest.mm +++ b/chrome/browser/cocoa/gradient_button_cell_unittest.mm @@ -9,6 +9,10 @@ #import "chrome/browser/cocoa/cocoa_test_helper.h" #include "testing/gtest/include/gtest/gtest.h" +@interface GradientButtonCell (HoverValueTesting) +- (void)adjustHoverValue; +@end + namespace { class GradientButtonCellTest : public testing::Test { @@ -36,7 +40,25 @@ TEST_F(GradientButtonCellTest, AddRemove) { // Test drawing, mostly to ensure nothing leaks or crashes. TEST_F(GradientButtonCellTest, Display) { + [[view_ cell] setHoverAlpha:0.0]; + [view_ display]; + [[view_ cell] setHoverAlpha:0.5]; [view_ display]; + [[view_ cell] setHoverAlpha:1.0]; + [view_ display]; +} + +// Test drawing, mostly to ensure nothing leaks or crashes. +TEST_F(GradientButtonCellTest, Hover) { + GradientButtonCell* cell = [view_ cell]; + [cell setMouseInside:YES animate:NO]; + EXPECT_EQ([[view_ cell] hoverAlpha], 1.0); + + [cell setMouseInside:NO animate:YES]; + CGFloat alpha1 = [cell hoverAlpha]; + [cell adjustHoverValue]; + CGFloat alpha2 = [cell hoverAlpha]; + EXPECT_TRUE(alpha2 < alpha1); } // Tracking rects diff --git a/chrome/browser/cocoa/status_bubble_mac.mm b/chrome/browser/cocoa/status_bubble_mac.mm index 67b920b..a415421 100644 --- a/chrome/browser/cocoa/status_bubble_mac.mm +++ b/chrome/browser/cocoa/status_bubble_mac.mm @@ -9,6 +9,8 @@ #include "base/sys_string_conversions.h" #include "googleurl/src/gurl.h" #import "third_party/GTM/AppKit/GTMNSBezierPath+RoundRect.h" +#import "third_party/GTM/AppKit/GTMNSColor+Luminance.h" +#import "third_party/GTM/AppKit/GTMTheme.h" namespace { @@ -49,10 +51,12 @@ enum BubbleStyle { @private NSString* content_; BubbleStyle style_; + NSWindow* parent_; } - (void)setContent:(NSString*)content; - (void)setStyle:(BubbleStyle)style; +- (void)setParent:(NSWindow*)parent; - (NSFont*)font; @end @@ -220,6 +224,8 @@ void StatusBubbleMac::Create() { StatusBubbleViewCocoa* view = [[[StatusBubbleViewCocoa alloc] initWithFrame:NSZeroRect] autorelease]; + [view setParent:parent_]; + [window_ setContentView:view]; [parent_ addChildWindow:window_ ordered:NSWindowAbove]; @@ -263,6 +269,16 @@ void StatusBubbleMac::FadeOut() { [self setNeedsDisplay:YES]; } +- (void)setParent:(NSWindow*)parent { + [parent_ autorelease]; + parent_ = [parent retain]; + [self setNeedsDisplay:YES]; +} + +- (GTMTheme*)gtm_theme { + return [parent_ gtm_theme]; +} + - (NSFont*)font { return [NSFont systemFontOfSize:[NSFont smallSystemFontSize]]; } @@ -300,13 +316,24 @@ void StatusBubbleMac::FadeOut() { // Background / Edge NSRect bounds = [self bounds]; + bounds = NSInsetRect(bounds, 0.5, 0.5); NSBezierPath *border = [NSBezierPath gtm_bezierPathWithRoundRect:bounds topLeftCornerRadius:tl_radius topRightCornerRadius:tr_radius bottomLeftCornerRadius:bl_radius bottomRightCornerRadius:br_radius]; - [[NSColor colorWithDeviceWhite:kWindowFill alpha:1.0f] set]; + NSColor* color = + [[self gtm_theme] backgroundColorForStyle:GTMThemeStyleToolBar + state:GTMThemeStateActiveWindow]; + + // workaround for default theme + // TODO(alcor) next GTM update return nil for background color if not set; + if ([color isEqual:[NSColor colorWithCalibratedWhite:0.5 alpha:1.0]]) + color = nil; + if (!color) + color = [NSColor colorWithCalibratedWhite:0.9 alpha:1.0]; + [color set]; [border fill]; border = [NSBezierPath gtm_bezierPathWithRoundRect:bounds @@ -319,11 +346,11 @@ void StatusBubbleMac::FadeOut() { [border stroke]; // Text - + NSColor* textColor = [color gtm_legibleTextColor]; NSFont* textFont = [self font]; NSShadow* textShadow = [[[NSShadow alloc] init] autorelease]; - [textShadow setShadowBlurRadius:1.5f]; - [textShadow setShadowColor:[NSColor whiteColor]]; + [textShadow setShadowBlurRadius:0.0f]; + [textShadow setShadowColor:[textColor gtm_legibleTextColor]]; [textShadow setShadowOffset:NSMakeSize(0.0f, -1.0f)]; NSDictionary* textDict = [NSDictionary dictionaryWithObjectsAndKeys: diff --git a/chrome/browser/cocoa/status_bubble_mac_unittest.mm b/chrome/browser/cocoa/status_bubble_mac_unittest.mm index b0fa87b..b4697875 100644 --- a/chrome/browser/cocoa/status_bubble_mac_unittest.mm +++ b/chrome/browser/cocoa/status_bubble_mac_unittest.mm @@ -10,6 +10,16 @@ #include "chrome/browser/cocoa/status_bubble_mac.h" #include "googleurl/src/gurl.h" #include "testing/gtest/include/gtest/gtest.h" +#import "third_party/GTM/AppKit/GTMTheme.h" + +@interface StatusBubbleMacTestWindowDelegate : NSObject <GTMThemeDelegate>; +@end +@implementation StatusBubbleMacTestWindowDelegate +- (GTMTheme *)gtm_themeForWindow:(NSWindow *)window { + NSLog(@"gettheme"); + return [[[GTMTheme alloc] init] autorelease]; +} +@end class StatusBubbleMacTest : public testing::Test { public: @@ -29,11 +39,24 @@ class StatusBubbleMacTest : public testing::Test { NSString* GetURLText() { return bubble_->url_text_; } - + NSWindow* GetWindow() { + return bubble_->window_; + } + NSWindow* GetParent() { + return bubble_->parent_; + } CocoaTestHelper cocoa_helper_; // Inits Cocoa, creates window, etc... scoped_ptr<StatusBubbleMac> bubble_; }; +TEST_F(StatusBubbleMacTest, Theme) { + bubble_->SetStatus(L"Theme test"); // Creates the window + [GetParent() setDelegate: + [[[StatusBubbleMacTestWindowDelegate alloc] init] autorelease]]; + EXPECT_TRUE([GetParent() gtm_theme] != nil); + EXPECT_TRUE([[GetWindow() contentView] gtm_theme] != nil); +} + TEST_F(StatusBubbleMacTest, SetStatus) { bubble_->SetStatus(L""); bubble_->SetStatus(L"This is a test"); diff --git a/chrome/browser/cocoa/tab_cell.mm b/chrome/browser/cocoa/tab_cell.mm index 326f1f7..6823e3b 100644 --- a/chrome/browser/cocoa/tab_cell.mm +++ b/chrome/browser/cocoa/tab_cell.mm @@ -2,10 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#import "base/scoped_nsobject.h" #import "chrome/browser/cocoa/tab_cell.h" #import "third_party/GTM/AppKit/GTMTheme.h" #import "third_party/GTM/AppKit/GTMNSColor+Luminance.h" + @implementation TabCell - (id)initTextCell:(NSString *)aString { @@ -18,7 +20,7 @@ - (NSBackgroundStyle)interiorBackgroundStyle { return [[[self controlView] gtm_theme] - interiorBackgroundStyleForStyle:GTMThemeStyleTabBarSelected + interiorBackgroundStyleForStyle:GTMThemeStyleToolBar state:GTMThemeStateActiveWindow]; } @@ -37,6 +39,34 @@ inView:controlView]; } +- (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView *)controlView { + GTMTheme* theme = [[self controlView] gtm_theme]; + NSColor* textColor = [theme textColorForStyle:GTMThemeStyleToolBar + state:[self isHighlighted]]; + + scoped_nsobject<NSShadow> textShadow([[NSShadow alloc] init]); + [textShadow setShadowBlurRadius:0.0f]; + [textShadow setShadowColor:[textColor gtm_legibleTextColor]]; + [textShadow setShadowOffset:NSMakeSize(0.0f, -1.0f)]; + + NSDictionary* attributes = + [NSDictionary dictionaryWithObjectsAndKeys: + [self font], NSFontAttributeName, + textColor, NSForegroundColorAttributeName, + textShadow.get(), NSShadowAttributeName, + nil]; + + [[self title] drawInRect:[self titleRectForBounds:cellFrame] + withAttributes:attributes]; + + NSRect imageBounds = NSZeroRect; + imageBounds.size = [[self image] size]; + [[self image] drawInRect:[self imageRectForBounds:cellFrame] + fromRect:imageBounds + operation:NSCompositeSourceOver + fraction:1.0]; +} + - (void)highlight:(BOOL)flag withFrame:(NSRect)cellFrame inView:(NSView *)controlView { diff --git a/chrome/browser/cocoa/tab_controller.h b/chrome/browser/cocoa/tab_controller.h index bffdf7f..565c95c 100644 --- a/chrome/browser/cocoa/tab_controller.h +++ b/chrome/browser/cocoa/tab_controller.h @@ -35,8 +35,8 @@ enum TabLoadingState { @interface TabController : NSViewController { @private - IBOutlet NSButton* backgroundButton_; IBOutlet NSView* iconView_; + IBOutlet NSTextField* titleView_; IBOutlet NSMenu* contextMenu_; IBOutlet NSButton* closeButton_; @@ -74,6 +74,8 @@ enum TabLoadingState { - (void)setIconView:(NSView*)iconView; - (NSView*)iconView; +// (Re)apply the current theme. +- (void)applyTheme; @end @interface TabController(TestingAPI) diff --git a/chrome/browser/cocoa/tab_controller.mm b/chrome/browser/cocoa/tab_controller.mm index cff9f81..96d7e5d 100644 --- a/chrome/browser/cocoa/tab_controller.mm +++ b/chrome/browser/cocoa/tab_controller.mm @@ -7,6 +7,7 @@ #import "chrome/browser/cocoa/tab_controller.h" #import "chrome/browser/cocoa/tab_controller_target.h" #import "chrome/browser/cocoa/tab_view.h" +#import "third_party/GTM/AppKit/GTMTheme.h" @interface TabController(Private) - (void)updateVisibility; @@ -53,7 +54,7 @@ selected_ = selected; [(TabView *)[self view] setState:selected]; [self updateVisibility]; - [[self view] setNeedsDisplay:YES]; + [self applyTheme]; } // Called when the tab's nib is done loading and all outlets are hooked up. @@ -61,9 +62,6 @@ // Ensure we don't show favicon if the tab is already too small to begin with. [self updateVisibility]; [(id)iconView_ setImage:nsimage_cache::ImageNamed(@"nav.pdf")]; - [[self view] addSubview:backgroundButton_ - positioned:NSWindowBelow - relativeTo:nil]; [self internalSetSelected:selected_]; } @@ -91,7 +89,7 @@ } - (void)setTitle:(NSString *)title { - [backgroundButton_ setToolTip:title]; + [[self view] setToolTip:title]; [super setTitle:title]; } @@ -119,7 +117,7 @@ } - (NSString *)toolTip { - return [backgroundButton_ toolTip]; + return [[self view] toolTip]; } // Return a rough approximation of the number of icons we could fit in the @@ -168,4 +166,20 @@ [self updateVisibility]; } +- (void)applyTheme { + GTMTheme* theme = [[self view] gtm_theme]; + NSColor* color = nil; + if (!selected_) { + color = [theme textColorForStyle:GTMThemeStyleTabBarDeselected + state:GTMThemeStateActiveWindow]; + } + // Default to the selected text color unless told otherwise. + if (!color) { + color = [theme textColorForStyle:GTMThemeStyleToolBar + state:GTMThemeStateActiveWindow]; + } + + [titleView_ setTextColor:color ? color : [NSColor textColor]]; + [[self view] setNeedsDisplay:YES]; +} @end diff --git a/chrome/browser/cocoa/tab_strip_controller.h b/chrome/browser/cocoa/tab_strip_controller.h index 06db772..24fee3b 100644 --- a/chrome/browser/cocoa/tab_strip_controller.h +++ b/chrome/browser/cocoa/tab_strip_controller.h @@ -72,11 +72,9 @@ class ToolbarModel; // width should be used, this will have a value of |kUseFullAvailableWidth|. float availableResizeWidth_; // A tracking area that's the size of the tab strip used to be notified - // when the mouse leaves the tab strip. It's installed when the user clicks - // the close box of a tab and is removed when they move the mouse outside - // of the strip. When they do, we resize the tabs to use all available - // space. - scoped_nsobject<NSTrackingArea> closeTabTrackingArea_; + // when the mouse moves in the tab strip + scoped_nsobject<NSTrackingArea> trackingArea_; + TabView* hoveredTab_; // weak. Tab that the mouse is hovering over // Array of subviews which are permanent (and which should never be removed), // such as the new-tab button, but *not* the tabs themselves. @@ -129,8 +127,8 @@ class ToolbarModel; // Force the tabs to rearrange themselves to reflect the current model. - (void)layoutTabs; -// The user changed the theme. -- (void)userChangedTheme; +// The user changed the theme, or theme state changed. +- (void)applyTheme; // Default height for tabs. + (CGFloat)defaultTabHeight; diff --git a/chrome/browser/cocoa/tab_strip_controller.mm b/chrome/browser/cocoa/tab_strip_controller.mm index 466ec30..b4b41cf 100644 --- a/chrome/browser/cocoa/tab_strip_controller.mm +++ b/chrome/browser/cocoa/tab_strip_controller.mm @@ -47,7 +47,6 @@ static const float kUseFullAvailableWidth = -1.0; @end @interface TabStripController(Private) -- (void)installTrackingArea; - (BOOL)useFullWidthForLayout; - (void)addSubviewToPermanentList:(NSView*)aView; - (void)regenerateSubviewList; @@ -83,7 +82,6 @@ static const float kUseFullAvailableWidth = -1.0; [newTabButton_ setAction:@selector(commandDispatch:)]; [newTabButton_ setTag:IDC_NEW_TAB]; targetFrames_.reset([[NSMutableDictionary alloc] init]); - [tabView_ setWantsLayer:YES]; dragBlockingView_.reset([[TabStripControllerDragBlockingView alloc] initWithFrame:NSZeroRect]); [self addSubviewToPermanentList:dragBlockingView_]; @@ -100,13 +98,23 @@ static const float kUseFullAvailableWidth = -1.0; selector:@selector(tabViewFrameChanged:) name:NSViewFrameDidChangeNotification object:tabView_]; + + trackingArea_.reset([[NSTrackingArea alloc] + initWithRect:NSZeroRect // Ignored by NSTrackingInVisibleRect + options:NSTrackingMouseEnteredAndExited | + NSTrackingMouseMoved | + NSTrackingActiveAlways | + NSTrackingInVisibleRect + owner:self + userInfo:nil]); + [tabView_ addTrackingArea:trackingArea_.get()]; } return self; } - (void)dealloc { - if (closeTabTrackingArea_.get()) - [tabView_ removeTrackingArea:closeTabTrackingArea_.get()]; + if (trackingArea_.get()) + [tabView_ removeTrackingArea:trackingArea_.get()]; [[NSNotificationCenter defaultCenter] removeObserver:self]; [super dealloc]; } @@ -229,6 +237,9 @@ static const float kUseFullAvailableWidth = -1.0; // is the TabView that is potentially going away. - (void)closeTab:(id)sender { DCHECK([sender isKindOfClass:[NSView class]]); + if ([hoveredTab_ isEqual:sender]) { + hoveredTab_ = nil; + } int index = [self indexForTabView:sender]; if (tabModel_->ContainsIndex(index)) { TabContents* contents = tabModel_->GetTabContentsAt(index); @@ -240,7 +251,6 @@ static const float kUseFullAvailableWidth = -1.0; // TODO(pinkerton): re-visit when handling tab overflow. NSView* penultimateTab = [self viewAtIndex:[tabArray_ count] - 2]; availableResizeWidth_ = NSMaxX([penultimateTab frame]); - [self installTrackingArea]; tabModel_->CloseTabContentsAt(index); } else { // Use the standard window close if this is the last tab @@ -335,7 +345,7 @@ static const float kUseFullAvailableWidth = -1.0; for (TabController* tab in tabArray_.get()) { BOOL isPlaceholder = [[tab view] isEqual:placeholderTab_]; NSRect tabFrame = [[tab view] frame]; - tabFrame.size.height = [[self class] defaultTabHeight]; + tabFrame.size.height = [[self class] defaultTabHeight] + 1; tabFrame.origin.y = 0; tabFrame.origin.x = offset; @@ -563,6 +573,10 @@ static const float kUseFullAvailableWidth = -1.0; NSView* tab = [self viewAtIndex:index]; [tab removeFromSuperview]; + if ([hoveredTab_ isEqual:tab]) { + hoveredTab_ = nil; + } + NSValue *identifier = [NSValue valueWithPointer:tab]; [targetFrames_ removeObjectForKey:identifier]; @@ -747,9 +761,9 @@ static const float kUseFullAvailableWidth = -1.0; tabModel_->InsertTabContentsAt(index, contents, true, false); } -- (void)userChangedTheme { +- (void)applyTheme { for (TabController* tab in tabArray_.get()) { - [[tab view] setNeedsDisplay:YES]; + [tab applyTheme]; } } @@ -766,34 +780,38 @@ static const float kUseFullAvailableWidth = -1.0; return availableResizeWidth_ == kUseFullAvailableWidth; } -// Call to install a tracking area that reports mouseEnter/Exit messages so -// we can track when the mouse leaves the tab view after closing a tab with -// the mouse. Don't install another tracking rect if one is already there. -- (void)installTrackingArea { - if (closeTabTrackingArea_.get()) - return; - // Note that we pass |NSTrackingInVisibleRect| so the rect is actually - // ignored. - closeTabTrackingArea_.reset([[NSTrackingArea alloc] - initWithRect:[tabView_ bounds] - options:NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways | - NSTrackingInVisibleRect - owner:self - userInfo:nil]); - [tabView_ addTrackingArea:closeTabTrackingArea_.get()]; +- (void)mouseMoved:(NSEvent *)event { + // Use hit test to figure out what view we are hovering over. + TabView* targetView = (TabView*)[tabView_ hitTest:[event locationInWindow]]; + if (![targetView isKindOfClass:[TabView class]]) { + if ([[targetView superview] isKindOfClass:[TabView class]]) { + targetView = (TabView*)[targetView superview]; + } else { + targetView = nil; + } + } + + if (hoveredTab_ != targetView) { + [hoveredTab_ mouseExited:nil]; // We don't pass event because moved events + [targetView mouseEntered:nil]; // don't have valid tracking areas + hoveredTab_ = targetView; + } else { + [hoveredTab_ mouseMoved:event]; + } } - (void)mouseEntered:(NSEvent*)event { - // Do nothing. + [self mouseMoved:event]; } // Called when the tracking area is in effect which means we're tracking to // see if the user leaves the tab strip with their mouse. When they do, // reset layout to use all available width. - (void)mouseExited:(NSEvent*)event { - [tabView_ removeTrackingArea:closeTabTrackingArea_.get()]; - closeTabTrackingArea_.reset(nil); availableResizeWidth_ = kUseFullAvailableWidth; + + [hoveredTab_ mouseExited:event]; + hoveredTab_ = nil; [self layoutTabs]; } diff --git a/chrome/browser/cocoa/tab_strip_view.mm b/chrome/browser/cocoa/tab_strip_view.mm index 59b48e0..398e6a3 100644 --- a/chrome/browser/cocoa/tab_strip_view.mm +++ b/chrome/browser/cocoa/tab_strip_view.mm @@ -22,7 +22,7 @@ NSRect boundsRect = [self bounds]; NSRect borderRect, contentRect; NSDivideRect(boundsRect, &borderRect, &contentRect, 1, NSMinYEdge); - [[NSColor colorWithCalibratedWhite:0.0 alpha:0.3] set]; + [[NSColor colorWithCalibratedWhite:0.0 alpha:0.2] set]; NSRectFillUsingOperation(borderRect, NSCompositeSourceOver); } diff --git a/chrome/browser/cocoa/tab_view.h b/chrome/browser/cocoa/tab_view.h index fe6e567..2595ea2 100644 --- a/chrome/browser/cocoa/tab_view.h +++ b/chrome/browser/cocoa/tab_view.h @@ -24,7 +24,7 @@ IBOutlet NSButton* closeButton_; // Tracking area for close button mouseover images. - scoped_nsobject<NSTrackingArea> trackingArea_; + scoped_nsobject<NSTrackingArea> closeTrackingArea_; // All following variables are valid for the duration of a drag. // These are released on mouseUp: @@ -32,6 +32,10 @@ BOOL tabWasDragged_; // Has the tab been dragged? BOOL draggingWithinTabStrip_; // Did drag stay in the current tab strip? BOOL chromeIsVisible_; + BOOL isMouseInside_; // Is the mouse hovering over? + CGFloat hoverAlpha_; // How strong the mouse hover state is. + NSTimeInterval lastHoverUpdate_; // Time the hover value was last updated. + NSPoint hoverPoint_; // Current location of hover in view coords. NSTimeInterval tearTime_; // Time since tear happened NSPoint tearOrigin_; // Origin of the tear rect @@ -51,6 +55,7 @@ NSCellStateValue state_; } @property(assign) NSCellStateValue state; +@property(assign, nonatomic)CGFloat hoverAlpha; @end #endif // CHROME_BROWSER_COCOA_TAB_VIEW_H_ diff --git a/chrome/browser/cocoa/tab_view.mm b/chrome/browser/cocoa/tab_view.mm index c5bc791..62ae9b8 100644 --- a/chrome/browser/cocoa/tab_view.mm +++ b/chrome/browser/cocoa/tab_view.mm @@ -13,9 +13,13 @@ static const CGFloat kInsetMultiplier = 2.0/3.0; static const CGFloat kControlPoint1Multiplier = 1.0/3.0; static const CGFloat kControlPoint2Multiplier = 3.0/8.0; +static const NSTimeInterval kAnimationShowDuration = 0.2; +static const NSTimeInterval kAnimationHideDuration = 0.4; + @implementation TabView @synthesize state = state_; +@synthesize hoverAlpha = hoverAlpha_; - (id)initWithFrame:(NSRect)frame { self = [super initWithFrame:frame]; @@ -34,18 +38,18 @@ static const CGFloat kControlPoint2Multiplier = 3.0/8.0; // to the |closeButton_| view, but we'll handle the message ourself. // The mouseover is always enabled, because the close button works // regardless of key/main/active status. - trackingArea_.reset( + closeTrackingArea_.reset( [[NSTrackingArea alloc] initWithRect:[closeButton_ bounds] options:NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways owner:self userInfo:nil]); - [closeButton_ addTrackingArea:trackingArea_.get()]; + [closeButton_ addTrackingArea:closeTrackingArea_.get()]; } - (void)dealloc { // [self gtm_unregisterForThemeNotifications]; - [closeButton_ removeTrackingArea:trackingArea_.get()]; + [closeButton_ removeTrackingArea:closeTrackingArea_.get()]; [super dealloc]; } @@ -56,16 +60,57 @@ static const CGFloat kControlPoint2Multiplier = 3.0/8.0; return YES; } +- (void)adjustHoverValue { + NSTimeInterval thisUpdate = [NSDate timeIntervalSinceReferenceDate]; + + NSTimeInterval elapsed = thisUpdate - lastHoverUpdate_; + + CGFloat opacity = [self hoverAlpha]; + if (isMouseInside_) { + opacity += elapsed / kAnimationShowDuration; + } else { + opacity -= elapsed / kAnimationHideDuration; + } + + if (!isMouseInside_ && opacity < 0) { + opacity = 0; + } else if (isMouseInside_ && opacity > 1) { + opacity = 1; + } else { + [self performSelector:_cmd withObject:nil afterDelay:0.02]; + } + lastHoverUpdate_ = thisUpdate; + [self setHoverAlpha:opacity]; + + [self setNeedsDisplay:YES]; +} + - (void)mouseEntered:(NSEvent *)theEvent { - // We only set up one tracking area, so we know any mouseEntered: - // messages are for close button mouseovers. - [closeButton_ setImage:nsimage_cache::ImageNamed(@"close_bar_h.pdf")]; + if ([theEvent trackingArea] == closeTrackingArea_) { + [closeButton_ setImage:nsimage_cache::ImageNamed(@"close_bar_h.pdf")]; + } else { + lastHoverUpdate_ = [NSDate timeIntervalSinceReferenceDate]; + isMouseInside_ = YES; + [self adjustHoverValue]; + [self setNeedsDisplay:YES]; + } +} + +- (void)mouseMoved:(NSEvent *)theEvent { + hoverPoint_ = [self convertPoint:[theEvent locationInWindow] + fromView:nil]; + [self setNeedsDisplay:YES]; } - (void)mouseExited:(NSEvent *)theEvent { - // We only set up one tracking area, so we know any mouseExited: - // messages are for close button mouseovers. - [closeButton_ setImage:nsimage_cache::ImageNamed(@"close_bar.pdf")]; + if ([theEvent trackingArea] == closeTrackingArea_) { + [closeButton_ setImage:nsimage_cache::ImageNamed(@"close_bar.pdf")]; + } else { + lastHoverUpdate_ = [NSDate timeIntervalSinceReferenceDate]; + isMouseInside_ = NO; + [self adjustHoverValue]; + [self setNeedsDisplay:YES]; + } } // Determines which view a click in our frame actually hit. It's either this @@ -426,11 +471,11 @@ static const double kDragStartDistance = 3.0; // Inset by 0.5 in order to draw on pixels rather than on borders (which would // cause blurry pixels). Decrease height by 1 in order to move away from the // edge for the dark shadow. - rect = NSInsetRect(rect, 0.5, -0.5); + rect = NSInsetRect(rect, -0.5, -0.5); rect.origin.y -= 1; - NSPoint bottomLeft = NSMakePoint(NSMinX(rect), NSMinY(rect)); - NSPoint bottomRight = NSMakePoint(NSMaxX(rect), NSMinY(rect)); + NSPoint bottomLeft = NSMakePoint(NSMinX(rect), NSMinY(rect) + 2); + NSPoint bottomRight = NSMakePoint(NSMaxX(rect), NSMinY(rect) + 2); NSPoint topRight = NSMakePoint(NSMaxX(rect) - kInsetMultiplier * NSHeight(rect), NSMaxY(rect)); @@ -444,7 +489,7 @@ static const double kDragStartDistance = 3.0; // Outset many of these values by 1 to cause the fill to bleed outside the // clip area. NSBezierPath *path = [NSBezierPath bezierPath]; - [path moveToPoint:NSMakePoint(bottomLeft.x - 1, bottomLeft.y + 1)]; + [path moveToPoint:NSMakePoint(bottomLeft.x - 1, bottomLeft.y - 2)]; [path lineToPoint:NSMakePoint(bottomLeft.x - 1, bottomLeft.y)]; [path lineToPoint:bottomLeft]; [path curveToPoint:topLeft @@ -459,30 +504,7 @@ static const double kDragStartDistance = 3.0; controlPoint2:NSMakePoint(bottomRight.x - baseControlPointOutset, bottomRight.y)]; [path lineToPoint:NSMakePoint(bottomRight.x + 1, bottomRight.y)]; - [path lineToPoint:NSMakePoint(bottomRight.x + 1, bottomRight.y + 1)]; - - if (selected) { - // Stroke with a translucent black. - [[NSColor colorWithCalibratedWhite:0.0 alpha:active ? 0.5 : 0.3] set]; - [[NSGraphicsContext currentContext] saveGraphicsState]; - scoped_nsobject<NSShadow> shadow([[NSShadow alloc] init]); - [shadow setShadowOffset:NSMakeSize(2, -1)]; - [shadow setShadowBlurRadius:2.0]; - [path fill]; - [[NSGraphicsContext currentContext] restoreGraphicsState]; - } else { - // Stroke with a translucent black. - [[NSBezierPath bezierPathWithRect:NSOffsetRect([self bounds], 0, 1)] - addClip]; - - [[NSColor colorWithCalibratedWhite:0.0 alpha:active ? 0.3 : 0.1] set]; - } - - [[NSGraphicsContext currentContext] saveGraphicsState]; - [[NSColor colorWithCalibratedWhite:0.0 alpha:0.2] set]; - [path setLineWidth:selected ? 2.0 : 1.0]; - [path stroke]; - [[NSGraphicsContext currentContext] restoreGraphicsState]; + [path lineToPoint:NSMakePoint(bottomRight.x + 1, bottomRight.y - 2)]; GTMTheme *theme = [self gtm_theme]; @@ -491,34 +513,102 @@ static const double kDragStartDistance = 3.0; [theme backgroundPatternColorForStyle:GTMThemeStyleWindow state:GTMThemeStateActiveWindow]; if (windowColor) { - NSPoint phase = [self patternPhase]; [windowColor set]; + + [[NSGraphicsContext currentContext] setPatternPhase:[self patternPhase]]; + } else { + NSPoint phase = [self patternPhase]; + phase.y += 1; [[NSGraphicsContext currentContext] setPatternPhase:phase]; + [[NSColor windowBackgroundColor] set]; + } + + [path fill]; + + NSColor *tabColor = + [theme backgroundPatternColorForStyle:GTMThemeStyleTabBarDeselected + state:GTMThemeStateActiveWindow]; + if (tabColor) { + [tabColor set]; + [[NSGraphicsContext currentContext] setPatternPhase:[self patternPhase]]; } else { - [[NSColor colorWithCalibratedWhite:0.6 alpha:1.0] set]; + [[NSColor colorWithCalibratedWhite:1.0 alpha:0.3] set]; } [path fill]; + } - // Draw the background. [[NSGraphicsContext currentContext] saveGraphicsState]; - CGContextRef context = - (CGContextRef)([[NSGraphicsContext currentContext] graphicsPort]); - CGContextBeginTransparencyLayer(context, 0); - if (!selected) - CGContextSetAlpha(context, 0.5); [path addClip]; - [super drawRect:rect]; - CGContextEndTransparencyLayer(context); + if (selected || hoverAlpha_ > 0) { + // Draw the background. + CGFloat backgroundAlpha = hoverAlpha_ * 0.5; + [[NSGraphicsContext currentContext] saveGraphicsState]; + CGContextRef context = + (CGContextRef)([[NSGraphicsContext currentContext] graphicsPort]); + CGContextBeginTransparencyLayer(context, 0); + if (!selected) + CGContextSetAlpha(context, backgroundAlpha); + [path addClip]; + [super drawRect:rect]; + + // Draw a mouse hover gradient for the default themes + if (!selected) { + if (![theme backgroundImageForStyle:GTMThemeStyleTabBarDeselected + state:GTMThemeStateActiveWindow]) { + scoped_nsobject<NSGradient> glow([NSGradient alloc]); + [glow initWithStartingColor:[NSColor colorWithCalibratedWhite:1.0 + alpha:1.0 * + hoverAlpha_] + endingColor:[NSColor colorWithCalibratedWhite:1.0 + alpha:0.0]]; + + NSPoint point = hoverPoint_; + point.y = NSHeight(rect); + [glow drawFromCenter:point + radius:0 + toCenter:point + radius:NSWidth(rect)/3 + options:NSGradientDrawsBeforeStartingLocation]; + + [glow drawInBezierPath:path relativeCenterPosition:hoverPoint_]; + } + } + + CGContextEndTransparencyLayer(context); + [[NSGraphicsContext currentContext] restoreGraphicsState]; + } + + // Draw the top inner highlight. + NSAffineTransform* highlightTransform = [NSAffineTransform transform]; + [highlightTransform translateXBy:1 yBy:-1]; + scoped_nsobject<NSBezierPath> highlightPath([path copy]); + [highlightPath transformUsingAffineTransform:highlightTransform]; + [[NSColor colorWithCalibratedWhite:1.0 alpha:0.2 + 0.3 * hoverAlpha_] + setStroke]; + [highlightPath stroke]; + + [[NSGraphicsContext currentContext] restoreGraphicsState]; + + // Draw the top stroke. + [[NSGraphicsContext currentContext] saveGraphicsState]; + if (selected) { + [[NSColor colorWithDeviceWhite:0.0 alpha:active ? 0.3 : 0.15] set]; + } else { + [[NSColor colorWithDeviceWhite:0.0 alpha:active ? 0.2 : 0.15] set]; + [[NSBezierPath bezierPathWithRect:NSOffsetRect(rect, 0, 2.5)] addClip]; + } + [path setLineWidth:1.0]; + [path stroke]; [[NSGraphicsContext currentContext] restoreGraphicsState]; // Draw the bottom border. if (!selected) { [path addClip]; NSRect borderRect, contentRect; - NSDivideRect(rect, &borderRect, &contentRect, 1, NSMinYEdge); - [[NSColor colorWithCalibratedWhite:0.0 alpha:0.4] set]; + NSDivideRect(rect, &borderRect, &contentRect, 2.5, NSMinYEdge); + [[NSColor colorWithDeviceWhite:0.0 alpha:active ? 0.3 : 0.15] set]; NSRectFillUsingOperation(borderRect, NSCompositeSourceOver); } [[NSGraphicsContext currentContext] restoreGraphicsState]; diff --git a/chrome/browser/cocoa/tab_view_unittest.mm b/chrome/browser/cocoa/tab_view_unittest.mm index 74d5040..44cf629 100644 --- a/chrome/browser/cocoa/tab_view_unittest.mm +++ b/chrome/browser/cocoa/tab_view_unittest.mm @@ -33,6 +33,11 @@ TEST_F(TabViewTest, AddRemove) { // Test drawing, mostly to ensure nothing leaks or crashes. TEST_F(TabViewTest, Display) { + [view_ setHoverAlpha:0.0]; + [view_ display]; + [view_ setHoverAlpha:0.5]; + [view_ display]; + [view_ setHoverAlpha:1.0]; [view_ display]; } diff --git a/chrome/browser/cocoa/toolbar_controller.h b/chrome/browser/cocoa/toolbar_controller.h index 31aafc4..07eccac 100644 --- a/chrome/browser/cocoa/toolbar_controller.h +++ b/chrome/browser/cocoa/toolbar_controller.h @@ -62,6 +62,10 @@ class ToolbarView; // See comments in awakeFromNib for more info. scoped_nsobject<AutocompleteTextField> locationBarRetainer_; + // Tracking area for mouse enter/exit/moved in the toolbar. + scoped_nsobject<NSTrackingArea> trackingArea_; + NSButton* hoveredButton_; // weak. Button under the mouse cursor. + IBOutlet NSMenu* pageMenu_; IBOutlet NSMenu* wrenchMenu_; diff --git a/chrome/browser/cocoa/toolbar_controller.mm b/chrome/browser/cocoa/toolbar_controller.mm index 1bcf8a4..f21f790 100644 --- a/chrome/browser/cocoa/toolbar_controller.mm +++ b/chrome/browser/cocoa/toolbar_controller.mm @@ -25,10 +25,10 @@ static NSString* const kStarredImageName = @"starred.pdf"; // Height of the toolbar in pixels when the bookmark bar is closed. -static const float kBaseToolbarHeight = 39.0; +static const float kBaseToolbarHeight = 36.0; // Overlap (in pixels) between the toolbar and the bookmark bar. -static const float kBookmarkBarOverlap = 5.0; +static const float kBookmarkBarOverlap = 7.0; @interface ToolbarController(Private) - (void)initCommandStatus:(CommandUpdater*)commands; @@ -150,6 +150,35 @@ class PrefObserverBridge : public NotificationObserver { // the retain count of the location bar; use of the scoped object // helps us remember to release it. locationBarRetainer_.reset([locationBar_ retain]); + trackingArea_.reset( + [[NSTrackingArea alloc] initWithRect:NSZeroRect // Ignored + options:NSTrackingMouseMoved | + NSTrackingInVisibleRect | + NSTrackingMouseEnteredAndExited | + NSTrackingActiveAlways + owner:self + userInfo:nil]); + [[self view] addTrackingArea:trackingArea_.get()]; +} + +- (void)mouseExited:(NSEvent*)theEvent { + [[hoveredButton_ cell] setMouseInside:NO animate:YES]; + hoveredButton_ = nil; +} + +- (void)mouseMoved:(NSEvent *)theEvent { + NSButton *targetView = (NSButton *)[[self view] + hitTest:[theEvent locationInWindow]]; + if (![targetView isKindOfClass:[NSButton class]]) targetView = nil; + if (hoveredButton_ != targetView) { + [[hoveredButton_ cell] setMouseInside:NO animate:YES]; + [[targetView cell] setMouseInside:YES animate:YES]; + hoveredButton_ = targetView; + } +} + +- (void)mouseEntered:(NSEvent*)event { + [self mouseMoved:event]; } - (void)resizeView:(NSView*)view newHeight:(float)height { |