diff options
author | shrike <shrike@chromium.org> | 2016-02-29 17:57:30 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2016-03-01 01:58:39 +0000 |
commit | 945c1250b48a4d5c5a4499069c44053f83a54688 (patch) | |
tree | da126e4baf9412242609982040fbc2780046a55a | |
parent | 5121a34129f274edc61a288f73f99b39eb06e1e7 (diff) | |
download | chromium_src-945c1250b48a4d5c5a4499069c44053f83a54688.zip chromium_src-945c1250b48a4d5c5a4499069c44053f83a54688.tar.gz chromium_src-945c1250b48a4d5c5a4499069c44053f83a54688.tar.bz2 |
Reworks the Mac tab strip to use the Material Design style.
BUG=585674
Review URL: https://codereview.chromium.org/1411783012
Cr-Commit-Position: refs/heads/master@{#378367}
29 files changed, 1269 insertions, 280 deletions
diff --git a/base/mac/sdk_forward_declarations.h b/base/mac/sdk_forward_declarations.h index 99abc909..f752de2 100644 --- a/base/mac/sdk_forward_declarations.h +++ b/base/mac/sdk_forward_declarations.h @@ -276,6 +276,7 @@ BASE_EXPORT extern NSString* const CBAdvertisementDataIsConnectable; MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_10 BASE_EXPORT extern NSString* const NSUserActivityTypeBrowsingWeb; BASE_EXPORT extern NSString* const NSAppearanceNameVibrantDark; +BASE_EXPORT extern NSString* const NSAppearanceNameVibrantLight; #endif // MAC_OS_X_VERSION_10_10 } // extern "C" @@ -481,6 +482,7 @@ BASE_EXPORT extern "C" void NSAccessibilityPostNotificationWithUserInfo( @interface NSView (MavericksSDK) - (void)setCanDrawSubviewsIntoLayer:(BOOL)flag; +- (void)setAppearance:(NSAppearance*)appearance; - (NSAppearance*)effectiveAppearance; @end @@ -496,6 +498,12 @@ BASE_EXPORT extern "C" void NSAccessibilityPostNotificationWithUserInfo( @property(readonly, nonatomic) NSUUID* identifier; @end +@interface NSVisualEffectView (MavericksSDK) +- (void)setState:(NSVisualEffectState)state; +@end + +@class NSVisualEffectView; + #endif // MAC_OS_X_VERSION_10_9 // Once Chrome no longer supports OSX 10.9, everything within this preprocessor @@ -523,6 +531,10 @@ BASE_EXPORT extern "C" void NSAccessibilityPostNotificationWithUserInfo( - (void)viewDidLoad; @end +@interface NSWindow (YosemiteSDK) +- (void)setTitlebarAppearsTransparent:(BOOL)flag; +@end + #endif // MAC_OS_X_VERSION_10_10 // Once Chrome no longer supports OSX 10.10.2, everything within this diff --git a/chrome/app/theme/theme_resources.grd b/chrome/app/theme/theme_resources.grd index e29d0e1..b9f9c08 100644 --- a/chrome/app/theme/theme_resources.grd +++ b/chrome/app/theme/theme_resources.grd @@ -521,6 +521,7 @@ </if> </if> <if expr="is_macosx or is_ios"> + <!-- This image is not used in Mac Material Design and can be removed when that mode is default. --> <structure type="chrome_scaled_image" name="IDR_OTR_ICON" file="mac/otr_icon.png" /> </if> <if expr="is_macosx"> diff --git a/chrome/browser/themes/browser_theme_pack.cc b/chrome/browser/themes/browser_theme_pack.cc index 089f219..931fd46 100644 --- a/chrome/browser/themes/browser_theme_pack.cc +++ b/chrome/browser/themes/browser_theme_pack.cc @@ -49,7 +49,7 @@ namespace { // Version number of the current theme pack. We just throw out and rebuild // theme packs that aren't int-equal to this. Increment this number if you // change default theme assets. -const int kThemePackVersion = 39; +const int kThemePackVersion = 40; // IDs that are in the DataPack won't clash with the positive integer // uint16_t. kHeaderID should always have the maximum value because we want the diff --git a/chrome/browser/themes/theme_properties.cc b/chrome/browser/themes/theme_properties.cc index 70b7b5c..37764f7 100644 --- a/chrome/browser/themes/theme_properties.cc +++ b/chrome/browser/themes/theme_properties.cc @@ -30,8 +30,10 @@ const SkColor kDefaultColorFrame[] = { const SkColor kDefaultColorFrameInactive[] = { SkColorSetRGB(0xCD, 0xCD, 0xCE), SkColorSetRGB(220, 220, 220)}; #elif defined(OS_MACOSX) -const SkColor kDefaultColorFrame = SkColorSetRGB(224, 224, 224); -const SkColor kDefaultColorFrameInactive = SkColorSetRGB(246, 246, 246); +const SkColor kDefaultColorFrame[] = { + SkColorSetRGB(224, 224, 224), SkColorSetRGB(204, 204, 204)}; +const SkColor kDefaultColorFrameInactive[] = { + SkColorSetRGB(246, 246, 246), SkColorSetRGB(246, 246, 246)}; #else const SkColor kDefaultColorFrame = SkColorSetRGB(66, 116, 201); const SkColor kDefaultColorFrameInactive = SkColorSetRGB(161, 182, 228); @@ -44,6 +46,11 @@ const SkColor kDefaultColorFrameIncognito[] = {SkColorSetRGB(0xA0, 0xA0, 0xA4), SkColorSetRGB(0x28, 0x2B, 0x2D)}; const SkColor kDefaultColorFrameIncognitoInactive[] = { SkColorSetRGB(0xAA, 0xAA, 0xAE), SkColorSetRGB(0x14, 0x17, 0x19)}; +#elif defined(OS_MACOSX) +const SkColor kDefaultColorFrameIncognito[] = { + SkColorSetRGB(255, 0, 0), SkColorSetARGB(230, 20, 22, 24)}; +const SkColor kDefaultColorFrameIncognitoInactive[] = { + SkColorSetRGB(255, 0, 0), SkColorSetRGB(30, 30, 30)}; #else const SkColor kDefaultColorFrameIncognito[] = {SkColorSetRGB(83, 106, 139), SkColorSetRGB(0x28, 0x2B, 0x2D)}; @@ -52,7 +59,10 @@ const SkColor kDefaultColorFrameIncognitoInactive[] = { #endif #if defined(OS_MACOSX) -const SkColor kDefaultColorToolbar = SkColorSetRGB(230, 230, 230); +const SkColor kDefaultColorToolbar[] = { + SkColorSetRGB(230, 230, 230), SkColorSetRGB(242, 242, 242)}; +const SkColor kDefaultColorToolbarIncognito[] = { + SkColorSetRGB(230, 230, 230), SkColorSetRGB(0x50, 0x50, 0x50)}; #else const SkColor kDefaultColorToolbar[] = { SkColorSetRGB(223, 223, 223), SkColorSetRGB(242, 242, 242)}; @@ -68,7 +78,10 @@ const SkColor kDefaultColorTabText = SK_ColorBLACK; const SkColor kDefaultColorTabTextIncognito[] = {SK_ColorBLACK, SK_ColorWHITE}; #if defined(OS_MACOSX) -const SkColor kDefaultColorBackgroundTabText = SK_ColorBLACK; +const SkColor kDefaultColorBackgroundTabText[] = { + SK_ColorBLACK, SK_ColorBLACK }; +const SkColor kDefaultColorBackgroundTabTextIncognito[] = { + SK_ColorBLACK, SK_ColorWHITE }; #else const SkColor kDefaultColorBackgroundTabText[] = { SkColorSetRGB(64, 64, 64), SK_ColorBLACK }; @@ -109,7 +122,11 @@ const color_utils::HSL kDefaultTintFrame = {-1, -1, -1}; const color_utils::HSL kDefaultTintFrameInactive = {-1, -1, 0.75}; const color_utils::HSL kDefaultTintFrameIncognito = {-1, 0.2, 0.35}; const color_utils::HSL kDefaultTintFrameIncognitoInactive = {-1, 0.3, 0.6}; -const color_utils::HSL kDefaultTintBackgroundTab = {-1, -1, 0.75}; +#if defined(OS_MACOSX) +const color_utils::HSL kDefaultTintBackgroundTab = { -1, -1, 0.4496875 }; +#else + const color_utils::HSL kDefaultTintBackgroundTab = {-1, -1, 0.75}; +#endif // OS_MACOSX // ---------------------------------------------------------------------------- // Defaults for properties which are not stored in the browser theme pack. @@ -122,14 +139,30 @@ const SkColor kDefaultDetachedBookmarkBarSeparatorIncognito[] = { const SkColor kDefaultToolbarTopSeparator = SkColorSetA(SK_ColorBLACK, 0x40); #if defined(OS_MACOSX) +const SkColor kDefaultColorFrameVibrancyOverlay[] = { + SkColorSetARGB(25, 0, 0, 0), SkColorSetARGB(230, 20, 22, 24)}; +const SkColor kDefaultColorToolbarInactive[] = { + SkColorSetRGB(255, 0, 0), SkColorSetRGB(246, 246, 246)}; +const SkColor kDefaultColorToolbarInactiveIncognito[] = { + SkColorSetRGB(255, 0, 0), SkColorSetRGB(45, 45, 45)}; +const SkColor kDefaultColorTabBackgroundInactive[] = { + SkColorSetRGB(255, 0, 0), SkColorSetRGB(236, 236, 236)}; +const SkColor kDefaultColorTabBackgroundInactiveIncognito[] = { + SkColorSetRGB(255, 0, 0), SkColorSetRGB(40, 40, 40)}; const SkColor kDefaultColorToolbarButtonStroke = SkColorSetARGB(75, 81, 81, 81); const SkColor kDefaultColorToolbarButtonStrokeInactive = SkColorSetARGB(75, 99, 99, 99); const SkColor kDefaultColorToolbarBezel = SkColorSetRGB(204, 204, 204); -const SkColor kDefaultColorToolbarStroke = SkColorSetRGB(103, 103, 103); +const SkColor kDefaultColorToolbarStroke[] = { + SkColorSetRGB(103, 103, 103), SkColorSetARGB(37, 0, 0, 0)}; const SkColor kDefaultColorToolbarStrokeInactive = SkColorSetRGB(163, 163, 163); +const SkColor kDefaultColorToolbarIncognitoStroke[] = { + SkColorSetRGB(255, 0, 0), SkColorSetARGB(63, 0, 0, 0)}; +const SkColor kDefaultColorToolbarStrokeTheme = + SkColorSetARGB(102, 255, 255, 255); +const SkColor kDefaultColorToolbarStrokeThemeInactive = + SkColorSetARGB(102, 76, 76, 76); #endif // OS_MACOSX - // ---------------------------------------------------------------------------- // Strings used in alignment properties. @@ -277,37 +310,28 @@ SkColor ThemeProperties::GetDefaultColor(int id, bool otr) { case COLOR_FRAME: if (otr) return kDefaultColorFrameIncognito[mode]; -#if defined(OS_CHROMEOS) +#if defined(OS_CHROMEOS) || defined(OS_MACOSX) return kDefaultColorFrame[mode]; #else return kDefaultColorFrame; -#endif // OS_CHROMEOS +#endif // OS_CHROMEOS || OS_MACOSX case COLOR_FRAME_INACTIVE: if (otr) return kDefaultColorFrameIncognitoInactive[mode]; -#if defined(OS_CHROMEOS) +#if defined(OS_CHROMEOS) || defined(OS_MACOSX) return kDefaultColorFrameInactive[mode]; #else return kDefaultColorFrameInactive; -#endif // OS_CHROMEOS -#if defined(OS_MACOSX) - case COLOR_TOOLBAR: - return kDefaultColorToolbar; -#else +#endif // OS_CHROMEOS || OS_MACOSX case COLOR_TOOLBAR: return otr ? kDefaultColorToolbarIncognito[mode] : kDefaultColorToolbar[mode]; -#endif // OS_MACOSX case COLOR_TAB_TEXT: return otr ? kDefaultColorTabTextIncognito[mode] : kDefaultColorTabText; case COLOR_BACKGROUND_TAB_TEXT: -#if defined(OS_MACOSX) - return kDefaultColorBackgroundTabText; -#else return otr ? kDefaultColorBackgroundTabTextIncognito[mode] : kDefaultColorBackgroundTabText[mode]; -#endif // OS_MACOSX case COLOR_BOOKMARK_TEXT: return otr ? kDefaultColorBookmarkTextIncognito[mode] : kDefaultColorBookmarkText; @@ -345,6 +369,14 @@ SkColor ThemeProperties::GetDefaultColor(int id, bool otr) { case COLOR_TOOLBAR_TOP_SEPARATOR: return kDefaultToolbarTopSeparator; #if defined(OS_MACOSX) + case COLOR_FRAME_VIBRANCY_OVERLAY: + return kDefaultColorFrameVibrancyOverlay[otr]; + case COLOR_TOOLBAR_INACTIVE: + return otr ? kDefaultColorToolbarInactiveIncognito[mode] + : kDefaultColorToolbarInactive[mode]; + case COLOR_BACKGROUND_TAB_INACTIVE: + return otr ? kDefaultColorTabBackgroundInactiveIncognito[mode] + : kDefaultColorTabBackgroundInactive[mode]; case COLOR_TOOLBAR_BUTTON_STROKE: return kDefaultColorToolbarButtonStroke; case COLOR_TOOLBAR_BUTTON_STROKE_INACTIVE: @@ -352,9 +384,14 @@ SkColor ThemeProperties::GetDefaultColor(int id, bool otr) { case COLOR_TOOLBAR_BEZEL: return kDefaultColorToolbarBezel; case COLOR_TOOLBAR_STROKE: - return kDefaultColorToolbarStroke; + return otr ? kDefaultColorToolbarIncognitoStroke[mode] + : kDefaultColorToolbarStroke[mode]; case COLOR_TOOLBAR_STROKE_INACTIVE: return kDefaultColorToolbarStrokeInactive; + case COLOR_TOOLBAR_STROKE_THEME: + return kDefaultColorToolbarStrokeTheme; + case COLOR_TOOLBAR_STROKE_THEME_INACTIVE: + return kDefaultColorToolbarStrokeThemeInactive; #endif case COLOR_FRAME_INCOGNITO: case COLOR_FRAME_INCOGNITO_INACTIVE: diff --git a/chrome/browser/themes/theme_properties.h b/chrome/browser/themes/theme_properties.h index c7bf271..a872a5d 100644 --- a/chrome/browser/themes/theme_properties.h +++ b/chrome/browser/themes/theme_properties.h @@ -22,7 +22,7 @@ class ThemeProperties { // The int values of OverwritableByUserThemeProperties, Alignment, and Tiling // are used as a key to store the property in the browser theme pack. If you // modify any of these enums, increment the version number in - // browser_theme_pack.h + // browser_theme_pack.cc. enum OverwritableByUserThemeProperty { COLOR_FRAME, @@ -128,9 +128,14 @@ class ThemeProperties { COLOR_STATUS_BAR_TEXT, #if defined(OS_MACOSX) + COLOR_FRAME_VIBRANCY_OVERLAY, + COLOR_TOOLBAR_INACTIVE, + COLOR_BACKGROUND_TAB_INACTIVE, COLOR_TOOLBAR_BEZEL, COLOR_TOOLBAR_STROKE, COLOR_TOOLBAR_STROKE_INACTIVE, + COLOR_TOOLBAR_STROKE_THEME, + COLOR_TOOLBAR_STROKE_THEME_INACTIVE, // The color of a toolbar button's border. COLOR_TOOLBAR_BUTTON_STROKE, COLOR_TOOLBAR_BUTTON_STROKE_INACTIVE, diff --git a/chrome/browser/themes/theme_service.cc b/chrome/browser/themes/theme_service.cc index 18f0a4f..351c50c08 100644 --- a/chrome/browser/themes/theme_service.cc +++ b/chrome/browser/themes/theme_service.cc @@ -757,15 +757,7 @@ ThemeSyncableService* ThemeService::GetThemeSyncableService() const { const ui::ThemeProvider& ThemeService::GetThemeProviderForProfile( Profile* profile) { ThemeService* service = ThemeServiceFactory::GetForProfile(profile); -#if defined(OS_MACOSX) - // TODO(estade): this doesn't work for OSX yet; fall back to normal theming - // in incognito. Since the OSX version of ThemeService caches colors, and - // both ThemeProviders use the same ThemeService some code needs to be - // rearranged. - bool incognito = false; -#else bool incognito = profile->GetProfileType() == Profile::INCOGNITO_PROFILE; -#endif return incognito ? service->incognito_theme_provider_ : service->original_theme_provider_; } diff --git a/chrome/browser/themes/theme_service.h b/chrome/browser/themes/theme_service.h index cabfefe..dda8c2e 100644 --- a/chrome/browser/themes/theme_service.h +++ b/chrome/browser/themes/theme_service.h @@ -188,6 +188,8 @@ class ThemeService : public base::NonThreadSafe, const override; #if defined(OS_MACOSX) bool UsingSystemTheme() const override; + bool InIncognitoMode() const override; + bool HasCustomColor(int id) const override; NSImage* GetNSImageNamed(int id) const override; NSColor* GetNSImageColorNamed(int id) const override; NSColor* GetNSColor(int id) const override; @@ -216,9 +218,10 @@ class ThemeService : public base::NonThreadSafe, base::RefCountedMemory* GetRawData(int id, ui::ScaleFactor scale_factor) const; #if defined(OS_MACOSX) - NSImage* GetNSImageNamed(int id) const; - NSColor* GetNSImageColorNamed(int id) const; - NSColor* GetNSColor(int id) const; + NSImage* GetNSImageNamed(int id, bool incognito) const; + NSColor* GetNSImageColorNamed(int id, bool incognito) const; + bool HasCustomColor(int id) const; + NSColor* GetNSColor(int id, bool incognito) const; NSColor* GetNSColorTint(int id) const; NSGradient* GetNSGradient(int id) const; #endif diff --git a/chrome/browser/themes/theme_service_mac.mm b/chrome/browser/themes/theme_service_mac.mm index 2e10bef..56e7f5f 100644 --- a/chrome/browser/themes/theme_service_mac.mm +++ b/chrome/browser/themes/theme_service_mac.mm @@ -9,8 +9,10 @@ #include "base/logging.h" #include "chrome/browser/themes/browser_theme_pack.h" #include "chrome/browser/themes/theme_properties.h" +#include "grit/theme_resources.h" #include "skia/ext/skia_utils_mac.h" #import "third_party/google_toolbox_for_mac/src/AppKit/GTMNSColor+Luminance.h" +#include "ui/base/material_design/material_design_controller.h" #include "ui/base/resource/resource_bundle.h" #include "ui/gfx/color_utils.h" #include "ui/gfx/image/image.h" @@ -20,6 +22,8 @@ NSString* const kBrowserThemeDidChangeNotification = typedef ThemeProperties Properties; +const int kMaterialDesignIdOffset = 1000000; + namespace { void HSLToHSB(const color_utils::HSL& hsl, CGFloat* h, CGFloat* s, CGFloat* b) { @@ -34,9 +38,24 @@ void HSLToHSB(const color_utils::HSL& hsl, CGFloat* h, CGFloat* s, CGFloat* b) { } // namespace -NSImage* ThemeService::GetNSImageNamed(int id) const { +NSImage* ThemeService::GetNSImageNamed(int id, bool incognito) const { DCHECK(CalledOnValidThread()); + bool is_tab_or_toolbar_color = + id == IDR_THEME_TAB_BACKGROUND_INACTIVE || + id == IDR_THEME_TOOLBAR_INACTIVE || + id == IDR_THEME_TAB_BACKGROUND || + id == IDR_THEME_TOOLBAR; + bool isModeMaterial = ui::MaterialDesignController::IsModeMaterial(); + + // In Material Design, Incognito mode draws tabs and the toolbar using colors + // that are different from non-Incognito mode. If in MD, offset these ids so + // that they don't conflict with the non-MD colors in the cache. + int original_id = id; + if (isModeMaterial && incognito && is_tab_or_toolbar_color) { + id += kMaterialDesignIdOffset; + } + // Check to see if we already have the image in the cache. NSImageMap::const_iterator nsimage_iter = nsimage_cache_.find(id); if (nsimage_iter != nsimage_cache_.end()) @@ -48,14 +67,50 @@ NSImage* ThemeService::GetNSImageNamed(int id) const { // - To get the generated tinted images. NSImage* nsimage = nil; if (theme_supplier_.get()) { - gfx::Image image = theme_supplier_->GetImageNamed(id); + gfx::Image image = theme_supplier_->GetImageNamed(original_id); if (!image.IsEmpty()) nsimage = image.ToNSImage(); } - // If the theme didn't override this image then load it from the resource - // bundle. - if (!nsimage) { + if (!nsimage && isModeMaterial && is_tab_or_toolbar_color) { + // If there's no custom image, fall back on the default color. On the + // surface it might seem that clients should call GetNSColor(id) directly. + // The problem is custom themes get a chance to provide their own versions + // of these resources, and they do so by supplying an image. So clients + // interested in these resources must call GetNSImageColorNamed(), which in + // turn calls GetNSImageNamed(). It also means we can't return a color + // directly but instead have to generate a color swatch. + NSColor* defaultColor = [NSColor redColor]; + switch (original_id) { + case IDR_THEME_TOOLBAR: + defaultColor = GetNSColor(ThemeProperties::COLOR_TOOLBAR, incognito); + break; + + case IDR_THEME_TAB_BACKGROUND: + defaultColor = GetNSColor(ThemeProperties::COLOR_BACKGROUND_TAB, + incognito); + break; + + case IDR_THEME_TOOLBAR_INACTIVE: + defaultColor = GetNSColor(ThemeProperties::COLOR_TOOLBAR_INACTIVE, + incognito); + break; + + case IDR_THEME_TAB_BACKGROUND_INACTIVE: + defaultColor = GetNSColor( + ThemeProperties::COLOR_BACKGROUND_TAB_INACTIVE, incognito); + break; + } + + NSSize imageSize = NSMakeSize(200, 100); + nsimage = [[[NSImage alloc] initWithSize:imageSize] autorelease]; + [nsimage lockFocus]; + [defaultColor set]; + NSRectFill(NSMakeRect(0, 0, imageSize.width, imageSize.height)); + [nsimage unlockFocus]; + } else if (!nsimage) { + // If the theme didn't override this image then load it from the resource + // bundle. nsimage = rb_.GetNativeImageNamed(id).ToNSImage(); } @@ -84,15 +139,30 @@ NSImage* ThemeService::GetNSImageNamed(int id) const { return empty_image; } -NSColor* ThemeService::GetNSImageColorNamed(int id) const { +NSColor* ThemeService::GetNSImageColorNamed(int id, bool incognito) const { DCHECK(CalledOnValidThread()); + bool is_tab_or_toolbar_color = + id == IDR_THEME_TAB_BACKGROUND_INACTIVE || + id == IDR_THEME_TOOLBAR_INACTIVE || + id == IDR_THEME_TAB_BACKGROUND || + id == IDR_THEME_TOOLBAR; + bool isModeMaterial = ui::MaterialDesignController::IsModeMaterial(); + + // In Material Design, Incognito mode draws tabs and the toolbar using colors + // that are different from non-Incognito mode. If in MD, offset these ids so + // that they don't clash with the non-MD colors in the cache. + int original_id = id; + if (isModeMaterial && incognito && is_tab_or_toolbar_color) { + id += kMaterialDesignIdOffset; + } + // Check to see if we already have the color in the cache. NSColorMap::const_iterator nscolor_iter = nscolor_cache_.find(id); if (nscolor_iter != nscolor_cache_.end()) return nscolor_iter->second; - NSImage* image = GetNSImageNamed(id); + NSImage* image = GetNSImageNamed(original_id, incognito); if (!image) return nil; NSColor* image_color = [NSColor colorWithPatternImage:image]; @@ -104,15 +174,25 @@ NSColor* ThemeService::GetNSImageColorNamed(int id) const { return image_color; } -NSColor* ThemeService::GetNSColor(int id) const { +bool ThemeService::HasCustomColor(int id) const { + SkColor color; + return theme_supplier_ && theme_supplier_->GetColor(id, &color); +} + +NSColor* ThemeService::GetNSColor(int id, bool incognito) const { DCHECK(CalledOnValidThread()); + int original_id = id; + if (ui::MaterialDesignController::IsModeMaterial() && incognito) { + id += kMaterialDesignIdOffset; + } + // Check to see if we already have the color in the cache. NSColorMap::const_iterator nscolor_iter = nscolor_cache_.find(id); if (nscolor_iter != nscolor_cache_.end()) return nscolor_iter->second; - SkColor sk_color = GetColor(id, false); + SkColor sk_color = GetColor(original_id, incognito); NSColor* color = skia::SkColorToCalibratedNSColor(sk_color); // We loaded successfully. Cache the color. @@ -287,21 +367,29 @@ void ThemeService::FreePlatformCaches() { nsgradient_cache_.clear(); } +bool ThemeService::BrowserThemeProvider::InIncognitoMode() const { + return incognito_; +} + bool ThemeService::BrowserThemeProvider::UsingSystemTheme() const { return theme_service_.UsingSystemTheme(); } +bool ThemeService::BrowserThemeProvider::HasCustomColor(int id) const { + return theme_service_.HasCustomColor(id); +} + NSImage* ThemeService::BrowserThemeProvider::GetNSImageNamed(int id) const { - return theme_service_.GetNSImageNamed(id); + return theme_service_.GetNSImageNamed(id, incognito_); } NSColor* ThemeService::BrowserThemeProvider::GetNSImageColorNamed( int id) const { - return theme_service_.GetNSImageColorNamed(id); + return theme_service_.GetNSImageColorNamed(id, incognito_); } NSColor* ThemeService::BrowserThemeProvider::GetNSColor(int id) const { - return theme_service_.GetNSColor(id); + return theme_service_.GetNSColor(id, incognito_); } NSColor* ThemeService::BrowserThemeProvider::GetNSColorTint(int id) const { diff --git a/chrome/browser/ui/cocoa/bookmarks/bookmark_bar_controller_unittest.mm b/chrome/browser/ui/cocoa/bookmarks/bookmark_bar_controller_unittest.mm index a90c716..864d720 100644 --- a/chrome/browser/ui/cocoa/bookmarks/bookmark_bar_controller_unittest.mm +++ b/chrome/browser/ui/cocoa/bookmarks/bookmark_bar_controller_unittest.mm @@ -231,6 +231,8 @@ class FakeTheme : public ui::ThemeProvider { const override { return NULL; } + bool InIncognitoMode() const override { return false; } + bool HasCustomColor(int id) const override { return false; } NSImage* GetNSImageNamed(int id) const override { return nil; } NSColor* GetNSImageColorNamed(int id) const override { return nil; } NSColor* GetNSColor(int id) const override { return color_.get(); } diff --git a/chrome/browser/ui/cocoa/browser_window_utils.mm b/chrome/browser/ui/cocoa/browser_window_utils.mm index 2a63941..9a68193 100644 --- a/chrome/browser/ui/cocoa/browser_window_utils.mm +++ b/chrome/browser/ui/cocoa/browser_window_utils.mm @@ -13,9 +13,24 @@ #import "chrome/browser/ui/cocoa/chrome_event_processing_window.h" #import "chrome/browser/ui/cocoa/tabs/tab_strip_controller.h" #include "content/public/browser/native_web_keyboard_event.h" +#include "ui/base/material_design/material_design_controller.h" using content::NativeWebKeyboardEvent; +namespace { + +CGFloat GetPatternVerticalOffsetWithTabStrip(bool tabStripVisible) { + // Without tab strip, offset an extra pixel (determined by experimentation). + if (!ui::MaterialDesignController::IsModeMaterial()) { + return tabStripVisible ? 2 : 3; + } + + return tabStripVisible ? -1 : 0; +} + +} // namespace + + @implementation BrowserWindowUtils + (BOOL)shouldHandleKeyboardEvent:(const NativeWebKeyboardEvent&)event { if (event.skip_in_browser || event.type == NativeWebKeyboardEvent::Char) @@ -85,9 +100,6 @@ using content::NativeWebKeyboardEvent; // because of the differing heights between window top and tab top, but this has // been approved by UI. const CGFloat kPatternHorizontalOffset = -5; -// Without tab strip, offset an extra pixel (determined by experimentation). -const CGFloat kPatternVerticalOffset = 2; -const CGFloat kPatternVerticalOffsetNoTabStrip = 3; + (NSPoint)themeImagePositionFor:(NSView*)windowView withTabStrip:(NSView*)tabStripView @@ -95,7 +107,7 @@ const CGFloat kPatternVerticalOffsetNoTabStrip = 3; if (!tabStripView) { return NSMakePoint(kPatternHorizontalOffset, NSHeight([windowView bounds]) + - kPatternVerticalOffsetNoTabStrip); + GetPatternVerticalOffsetWithTabStrip(false)); } NSPoint position = @@ -112,15 +124,15 @@ const CGFloat kPatternVerticalOffsetNoTabStrip = 3; // The theme image is lined up with the top of the tab which is below the // top of the tab strip. return NSMakePoint(kPatternHorizontalOffset, - [TabStripController defaultTabHeight] + - kPatternVerticalOffset); + [TabStripController defaultTabHeight] + + GetPatternVerticalOffsetWithTabStrip(true)); } // The theme image is lined up with the top of the tab strip (as opposed to // the top of the tab above). This is the same as lining up with the top of // the window's root view when not in presentation mode. return NSMakePoint(kPatternHorizontalOffset, NSHeight([tabStripView bounds]) + - kPatternVerticalOffsetNoTabStrip); + GetPatternVerticalOffsetWithTabStrip(false)); } + (void)activateWindowForController:(NSWindowController*)controller { diff --git a/chrome/browser/ui/cocoa/download/background_theme.h b/chrome/browser/ui/cocoa/download/background_theme.h index 06a6899..2941407 100644 --- a/chrome/browser/ui/cocoa/download/background_theme.h +++ b/chrome/browser/ui/cocoa/download/background_theme.h @@ -24,6 +24,8 @@ class BackgroundTheme : public ui::ThemeProvider { bool HasCustomImage(int id) const override; base::RefCountedMemory* GetRawData(int id, ui::ScaleFactor scale_factor) const override; + bool InIncognitoMode() const override; + bool HasCustomColor(int id) const override; NSImage* GetNSImageNamed(int id) const override; NSColor* GetNSImageColorNamed(int id) const override; NSColor* GetNSColor(int id) const override; diff --git a/chrome/browser/ui/cocoa/download/background_theme.mm b/chrome/browser/ui/cocoa/download/background_theme.mm index f5d8b22..b642e7a 100644 --- a/chrome/browser/ui/cocoa/download/background_theme.mm +++ b/chrome/browser/ui/cocoa/download/background_theme.mm @@ -31,6 +31,14 @@ bool BackgroundTheme::UsingSystemTheme() const { return true; } +bool BackgroundTheme::InIncognitoMode() const { + return false; +} + +bool BackgroundTheme::HasCustomColor(int id) const { + return false; +} + gfx::ImageSkia* BackgroundTheme::GetImageSkiaNamed(int id) const { return NULL; } @@ -88,5 +96,3 @@ NSGradient* BackgroundTheme::GetNSGradient(int id) const { return provider_->GetNSGradient(id); } } - - diff --git a/chrome/browser/ui/cocoa/framed_browser_window.mm b/chrome/browser/ui/cocoa/framed_browser_window.mm index 898b419..0b1c319 100644 --- a/chrome/browser/ui/cocoa/framed_browser_window.mm +++ b/chrome/browser/ui/cocoa/framed_browser_window.mm @@ -19,6 +19,7 @@ #include "grit/theme_resources.h" #include "ui/base/cocoa/nsgraphics_context_additions.h" #import "ui/base/cocoa/nsview_additions.h" +#include "ui/base/material_design/material_design_controller.h" // Implementer's note: Moving the window controls is tricky. When altering the // code, ensure that: @@ -282,9 +283,10 @@ const CGFloat kWindowGradientHeight = 24.0; themeImageColor = themeProvider->GetNSImageColorNamed(themeImageID); } - // If no theme image, use a gradient if incognito. + // If not Material Design, use a gradient if Incognito and no theme image. NSGradient* gradient = nil; - if (!themeImageColor && incognito) + if (!ui::MaterialDesignController::IsModeMaterial() && + !themeImageColor && incognito) gradient = themeProvider->GetNSGradient( active ? ThemeProperties::GRADIENT_FRAME_INCOGNITO : ThemeProperties::GRADIENT_FRAME_INCOGNITO_INACTIVE); diff --git a/chrome/browser/ui/cocoa/hover_close_button.mm b/chrome/browser/ui/cocoa/hover_close_button.mm index bef45de..ba5e9be 100644 --- a/chrome/browser/ui/cocoa/hover_close_button.mm +++ b/chrome/browser/ui/cocoa/hover_close_button.mm @@ -4,19 +4,29 @@ #import "chrome/browser/ui/cocoa/hover_close_button.h" +#include "base/mac/foundation_util.h" #include "base/strings/sys_string_conversions.h" +#include "chrome/browser/themes/theme_properties.h" +#include "chrome/browser/themes/theme_service.h" +#import "chrome/browser/ui/cocoa/browser_window_controller.h" +#import "chrome/browser/ui/cocoa/tabs/tab_view.h" #include "chrome/grit/generated_resources.h" #include "grit/components_strings.h" #include "grit/theme_resources.h" #import "third_party/google_toolbox_for_mac/src/AppKit/GTMKeyValueAnimation.h" #include "ui/base/cocoa/animation_utils.h" #include "ui/base/l10n/l10n_util.h" +#include "ui/base/material_design/material_design_controller.h" #include "ui/base/resource/resource_bundle.h" +#include "ui/gfx/image/image_skia_util_mac.h" +#include "ui/gfx/paint_vector_icon.h" +#include "ui/gfx/vector_icons_public.h" #include "ui/resources/grit/ui_resources.h" namespace { const CGFloat kFramesPerSecond = 16; // Determined experimentally to look good. const CGFloat kCloseAnimationDuration = 0.1; +const int kTabCloseButtonSize = 16; // Strings that are used for all close buttons. Set up in +initialize. NSString* gBasicAccessibilityTitle = nil; @@ -143,21 +153,73 @@ NSString* const kFadeOutValueKeyPath = @"fadeOutValue"; [self setNeedsDisplay]; } +- (TabView *)tabView { + return base::mac::ObjCCast<TabView>([self superview]); +} + - (NSImage*)imageForHoverState:(HoverState)hoverState { int imageID = IDR_CLOSE_1; + + if (!ui::MaterialDesignController::IsModeMaterial()) { + switch (hoverState) { + case kHoverStateNone: + imageID = IDR_CLOSE_1; + break; + case kHoverStateMouseOver: + imageID = IDR_CLOSE_1_H; + break; + case kHoverStateMouseDown: + imageID = IDR_CLOSE_1_P; + break; + } + ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); + return bundle.GetNativeImageNamed(imageID).ToNSImage(); + } + + gfx::VectorIconId vectorIconID; + SkColor vectorIconColor; + const ui::ThemeProvider* themeProvider = nullptr; + TabView* tabView; + switch (hoverState) { case kHoverStateNone: - imageID = IDR_CLOSE_1; + vectorIconID = gfx::VectorIconId::TAB_CLOSE_NORMAL; + // Determine the vector icon color, which for this icon is the color + // of the "x". + themeProvider = [[self window] themeProvider]; + if (themeProvider) { + if (themeProvider->InIncognitoMode()) { + vectorIconColor = SkColorSetARGB(0xFF, 0xC4, 0xC4, 0xC4); + } else { + tabView = [self tabView]; + bool use_active_tab_text_color = !tabView || [tabView isActiveTab]; + + const SkColor titleColor = use_active_tab_text_color ? + themeProvider->GetColor(ThemeProperties::COLOR_TAB_TEXT) : + themeProvider->GetColor( + ThemeProperties::COLOR_BACKGROUND_TAB_TEXT); + vectorIconColor = SkColorSetA(titleColor, 0xA0); + } + } else { + // 0x000000 is the default COLOR_TAB_TEXT color. + vectorIconColor = SkColorSetARGB(0xA0, 0x00, 0x00, 0x00); + } break; case kHoverStateMouseOver: - imageID = IDR_CLOSE_1_H; + // For mouse over, the icon color is the fill color of the circle. + vectorIconID = gfx::VectorIconId::TAB_CLOSE_HOVERED_PRESSED; + vectorIconColor = SkColorSetARGB(0xFF, 0xDB, 0x44, 0x37); break; case kHoverStateMouseDown: - imageID = IDR_CLOSE_1_P; + // For mouse pressed, the icon color is the fill color of the circle. + vectorIconID = gfx::VectorIconId::TAB_CLOSE_HOVERED_PRESSED; + vectorIconColor = SkColorSetARGB(0xFF, 0xA8, 0x35, 0x2A); break; } - ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); - return bundle.GetNativeImageNamed(imageID).ToNSImage(); + + return NSImageFromImageSkia( + gfx::CreateVectorIcon(vectorIconID, kTabCloseButtonSize, + vectorIconColor)); } - (void)setHoverState:(HoverState)state { diff --git a/chrome/browser/ui/cocoa/new_tab_button.h b/chrome/browser/ui/cocoa/new_tab_button.h index 8b2ddcf..0bb61d3 100644 --- a/chrome/browser/ui/cocoa/new_tab_button.h +++ b/chrome/browser/ui/cocoa/new_tab_button.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CHROME_BROWSER_UI_COCOA_NEW_TAB_BUTTON -#define CHROME_BROWSER_UI_COCOA_NEW_TAB_BUTTON +#ifndef CHROME_BROWSER_UI_COCOA_NEW_TAB_BUTTON_H_ +#define CHROME_BROWSER_UI_COCOA_NEW_TAB_BUTTON_H_ #import <Cocoa/Cocoa.h> @@ -19,6 +19,11 @@ // Returns YES if the given point is over the button. |point| is in the // superview's coordinate system. - (BOOL)pointIsOverButton:(NSPoint)point; + +// Sets the images shown by the NewTabButton's different states. +- (void)setImages; + @end -#endif // CHROME_BROWSER_UI_COCOA_NEW_TAB_BUTTON + +#endif // CHROME_BROWSER_UI_COCOA_NEW_TAB_BUTTON_H_ diff --git a/chrome/browser/ui/cocoa/new_tab_button.mm b/chrome/browser/ui/cocoa/new_tab_button.mm index dbdd56e..a5fd4bd 100644 --- a/chrome/browser/ui/cocoa/new_tab_button.mm +++ b/chrome/browser/ui/cocoa/new_tab_button.mm @@ -4,19 +4,152 @@ #import "chrome/browser/ui/cocoa/new_tab_button.h" +#include "base/mac/foundation_util.h" +#include "base/mac/sdk_forward_declarations.h" #import "chrome/browser/ui/cocoa/image_button_cell.h" +#include "chrome/browser/ui/cocoa/tabs/tab_view.h" #include "grit/theme_resources.h" +#include "ui/base/cocoa/nsgraphics_context_additions.h" +#include "ui/base/material_design/material_design_controller.h" #include "ui/base/resource/resource_bundle.h" +#include "ui/base/theme_provider.h" + +@class NewTabButtonCell; namespace { -NSImage* GetMaskImage() { - ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); - return bundle.GetNativeImageNamed(IDR_NEWTAB_BUTTON_MASK).ToNSImage(); +enum class RenderingOption { + NORMAL, + OVERLAY_LIGHTEN, + OVERLAY_LIGHTEN_INCOGNITO, + OVERLAY_DARKEN, + INLAY_LIGHTEN, +}; + +NSImage* GetMaskImageFromCell(NewTabButtonCell* aCell) { + if (!ui::MaterialDesignController::IsModeMaterial()) { + ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); + return bundle.GetNativeImageNamed(IDR_NEWTAB_BUTTON_MASK).ToNSImage(); + } + return [aCell imageForState:image_button_cell::kDefaultState view:nil]; +} + +// Creates an NSImage with size |size| and bitmap image representations for both +// 1x and 2x scale factors. |drawingHandler| is called once for every scale +// factor. This is similar to -[NSImage imageWithSize:flipped:drawingHandler:], +// but this function always evaluates drawingHandler eagerly, and it works on +// 10.6 and 10.7. +NSImage* CreateImageWithSize(NSSize size, + void (^drawingHandler)(NSSize)) { + base::scoped_nsobject<NSImage> result([[NSImage alloc] initWithSize:size]); + [NSGraphicsContext saveGraphicsState]; + for (ui::ScaleFactor scale_factor : ui::GetSupportedScaleFactors()) { + float scale = GetScaleForScaleFactor(scale_factor); + NSBitmapImageRep *bmpImageRep = [[[NSBitmapImageRep alloc] + initWithBitmapDataPlanes:NULL + pixelsWide:size.width * scale + pixelsHigh:size.height * scale + bitsPerSample:8 + samplesPerPixel:4 + hasAlpha:YES + isPlanar:NO + colorSpaceName:NSDeviceRGBColorSpace + bytesPerRow:0 + bitsPerPixel:0] autorelease]; + [bmpImageRep setSize:size]; + [NSGraphicsContext setCurrentContext: + [NSGraphicsContext graphicsContextWithBitmapImageRep:bmpImageRep]]; + drawingHandler(size); + [result addRepresentation:bmpImageRep]; + } + [NSGraphicsContext restoreGraphicsState]; + + return result.release(); } +// Takes a normal bitmap and a mask image and returns an image the size of the +// mask that has pixels from |image| but alpha information from |mask|. +NSImage* ApplyMask(NSImage* image, NSImage* mask) { + return [CreateImageWithSize([mask size], ^(NSSize size) { + // Skip a few pixels from the top of the tab background gradient, because + // the new tab button is not drawn at the very top of the browser window. + const int kYOffset = 10; + CGFloat width = size.width; + CGFloat height = size.height; + + // In some themes, the tab background image is narrower than the + // new tab button, so tile the background image. + CGFloat x = 0; + // The floor() is to make sure images with odd widths don't draw to the + // same pixel twice on retina displays. (Using NSDrawThreePartImage() + // caused a startup perf regression, so that cannot be used.) + CGFloat tileWidth = floor(std::min(width, [image size].width)); + while (x < width) { + [image drawAtPoint:NSMakePoint(x, 0) + fromRect:NSMakeRect(0, + [image size].height - height - kYOffset, + tileWidth, + height) + operation:NSCompositeCopy + fraction:1.0]; + x += tileWidth; + } + + [mask drawAtPoint:NSZeroPoint + fromRect:NSMakeRect(0, 0, width, height) + operation:NSCompositeDestinationIn + fraction:1.0]; + }) autorelease]; +} + +// Paints |overlay| on top of |ground|. +NSImage* Overlay(NSImage* ground, NSImage* overlay, CGFloat alpha) { + DCHECK_EQ([ground size].width, [overlay size].width); + DCHECK_EQ([ground size].height, [overlay size].height); + + return [CreateImageWithSize([ground size], ^(NSSize size) { + CGFloat width = size.width; + CGFloat height = size.height; + [ground drawAtPoint:NSZeroPoint + fromRect:NSMakeRect(0, 0, width, height) + operation:NSCompositeCopy + fraction:1.0]; + [overlay drawAtPoint:NSZeroPoint + fromRect:NSMakeRect(0, 0, width, height) + operation:NSCompositeSourceOver + fraction:alpha]; + }) autorelease]; +} + +CGFloat LineWidthFromContext(CGContextRef context) { + CGRect unitRect = CGRectMake(0.0, 0.0, 1.0, 1.0); + CGRect deviceRect = CGContextConvertRectToDeviceSpace(context, unitRect); + return 1.0 / deviceRect.size.height; +} + +} // namespace + +@interface NewTabButtonCustomImageRep : NSCustomImageRep +@property (assign, nonatomic) NSView* destView; +@property (copy, nonatomic) NSColor* fillColor; +@property (assign, nonatomic) NSPoint patternPhasePosition; +@property (assign, nonatomic) RenderingOption renderingOption; +@end + +@implementation NewTabButtonCustomImageRep + +@synthesize destView = destView_; +@synthesize fillColor = fillColor_; +@synthesize patternPhasePosition = patternPhasePosition_; +@synthesize renderingOption = renderingOption_; + +- (void)dealloc { + [fillColor_ release]; + [super dealloc]; } +@end + // A simple override of the ImageButtonCell to disable handling of // -mouseEntered. @interface NewTabButtonCell : ImageButtonCell @@ -33,7 +166,7 @@ NSImage* GetMaskImage() { - (void)drawFocusRingMaskWithFrame:(NSRect)cellFrame inView:(NSView*)view { // Match the button's shape. - [self drawImage:GetMaskImage() withFrame:cellFrame inView:view]; + [self drawImage:GetMaskImageFromCell(self) withFrame:cellFrame inView:view]; } @end @@ -48,11 +181,13 @@ NSImage* GetMaskImage() { - (BOOL)pointIsOverButton:(NSPoint)point { NSPoint localPoint = [self convertPoint:point fromView:[self superview]]; NSRect pointRect = NSMakeRect(localPoint.x, localPoint.y, 1, 1); - NSImage* buttonMask = GetMaskImage(); + NSImage* buttonMask = GetMaskImageFromCell([self cell]); + NSRect bounds = self.bounds; + NSSize buttonMaskSize = [buttonMask size]; NSRect destinationRect = NSMakeRect( - (NSWidth(self.bounds) - [buttonMask size].width) / 2, - (NSHeight(self.bounds) - [buttonMask size].height) / 2, - [buttonMask size].width, [buttonMask size].height); + (NSWidth(bounds) - buttonMaskSize.width) / 2, + (NSHeight(bounds) - buttonMaskSize.height) / 2, + buttonMaskSize.width, buttonMaskSize.height); return [buttonMask hitTestRect:pointRect withImageDestinationRect:destinationRect context:nil @@ -78,4 +213,283 @@ NSImage* GetMaskImage() { [self setNeedsDisplay:YES]; } +- (void)viewDidMoveToWindow { + NewTabButtonCell* cell = base::mac::ObjCCast<NewTabButtonCell>([self cell]); + + if ([self window] && + ![cell imageForState:image_button_cell::kDefaultState view:self]) { + [self setImages]; + } +} + +- (void)setImages { + const ui::ThemeProvider* theme = [[self window] themeProvider]; + if (!theme) { + return; + } + + // The old way of doing things. + if (!ui::MaterialDesignController::IsModeMaterial()) { + ResourceBundle& rb = ResourceBundle::GetSharedInstance(); + NSImage* mask = rb.GetNativeImageNamed(IDR_NEWTAB_BUTTON_MASK).ToNSImage(); + NSImage* normal = rb.GetNativeImageNamed(IDR_NEWTAB_BUTTON).ToNSImage(); + NSImage* hover = rb.GetNativeImageNamed(IDR_NEWTAB_BUTTON_H).ToNSImage(); + NSImage* pressed = rb.GetNativeImageNamed(IDR_NEWTAB_BUTTON_P).ToNSImage(); + + NSImage* foreground = ApplyMask( + theme->GetNSImageNamed(IDR_THEME_TAB_BACKGROUND), mask); + + [[self cell] setImage:Overlay(foreground, normal, 1.0) + forButtonState:image_button_cell::kDefaultState]; + [[self cell] setImage:Overlay(foreground, hover, 1.0) + forButtonState:image_button_cell::kHoverState]; + [[self cell] setImage:Overlay(foreground, pressed, 1.0) + forButtonState:image_button_cell::kPressedState]; + + // IDR_THEME_TAB_BACKGROUND_INACTIVE is only used with the default theme. + if (theme->UsingSystemTheme()) { + const CGFloat alpha = tabs::kImageNoFocusAlpha; + NSImage* background = ApplyMask( + theme->GetNSImageNamed(IDR_THEME_TAB_BACKGROUND_INACTIVE), mask); + [[self cell] setImage:Overlay(background, normal, alpha) + forButtonState: + image_button_cell::kDefaultStateBackground]; + [[self cell] setImage:Overlay(background, hover, alpha) + forButtonState:image_button_cell::kHoverStateBackground]; + } else { + [[self cell] setImage:nil + forButtonState: + image_button_cell::kDefaultStateBackground]; + [[self cell] setImage:nil + forButtonState:image_button_cell::kHoverStateBackground]; + } + return; + } + + NSImage* mask = [self imageWithFillColor:[NSColor whiteColor]]; + NSImage* normal = + [self imageForState:image_button_cell::kDefaultState theme:theme]; + NSImage* hover = + [self imageForState:image_button_cell::kHoverState theme:theme]; + NSImage* pressed = + [self imageForState:image_button_cell::kPressedState theme:theme]; + NSImage* normalBackground = nil; + NSImage* hoverBackground = nil; + + // If using a custom theme, overlay the default image with the theme's custom + // tab background image. + if (!theme->UsingSystemTheme()) { + NSImage* foreground = + ApplyMask(theme->GetNSImageNamed(IDR_THEME_TAB_BACKGROUND), mask); + normal = Overlay(foreground, normal, 1.0); + hover = Overlay(foreground, hover, 1.0); + pressed = Overlay(foreground, pressed, 1.0); + + NSImage* background = ApplyMask( + theme->GetNSImageNamed(IDR_THEME_TAB_BACKGROUND_INACTIVE), mask); + normalBackground = Overlay(background, normal, tabs::kImageNoFocusAlpha); + hoverBackground = Overlay(background, hover, tabs::kImageNoFocusAlpha); + } + + NewTabButtonCell* cell = base::mac::ObjCCast<NewTabButtonCell>([self cell]); + [cell setImage:normal forButtonState:image_button_cell::kDefaultState]; + [cell setImage:hover forButtonState:image_button_cell::kHoverState]; + [cell setImage:pressed forButtonState:image_button_cell::kPressedState]; + [cell setImage:normalBackground + forButtonState:image_button_cell::kDefaultStateBackground]; + [cell setImage:hoverBackground + forButtonState:image_button_cell::kHoverStateBackground]; +} + +- (NSImage*)imageForState:(image_button_cell::ButtonState)state + theme:(const ui::ThemeProvider*)theme { + NSColor* fillColor = nil; + RenderingOption renderingOption = RenderingOption::NORMAL; + + switch (state) { + case image_button_cell::kDefaultState: + fillColor = theme->GetNSImageColorNamed(IDR_THEME_TAB_BACKGROUND); + break; + + case image_button_cell::kHoverState: + fillColor = theme->GetNSImageColorNamed(IDR_THEME_TAB_BACKGROUND); + // When a custom theme highlight the entire area, otherwise only + // highlight the interior and not the border. + if (theme->HasCustomImage(IDR_THEME_TAB_BACKGROUND)) { + renderingOption = RenderingOption::OVERLAY_LIGHTEN; + } else if (theme->InIncognitoMode()) { + renderingOption = RenderingOption::OVERLAY_LIGHTEN_INCOGNITO; + } else { + renderingOption = RenderingOption::INLAY_LIGHTEN; + } + + break; + + case image_button_cell::kPressedState: + fillColor = theme->GetNSImageColorNamed(IDR_THEME_TAB_BACKGROUND); + break; + + case image_button_cell::kDefaultStateBackground: + case image_button_cell::kHoverStateBackground: + fillColor = + theme->GetNSImageColorNamed(IDR_THEME_TAB_BACKGROUND_INACTIVE); + break; + + default: + fillColor = [NSColor redColor]; + // All states should be accounted for above. + NOTREACHED(); + } + + base::scoped_nsobject<NewTabButtonCustomImageRep> imageRep = + [[NewTabButtonCustomImageRep alloc] + initWithDrawSelector:@selector(drawNewTabButtonImage:) + delegate:[NewTabButton class]]; + [imageRep setDestView:self]; + [imageRep setFillColor:fillColor]; + [imageRep setPatternPhasePosition: + [[self window] + themeImagePositionForAlignment:THEME_IMAGE_ALIGN_WITH_TAB_STRIP]]; + [imageRep setRenderingOption:renderingOption]; + + NSImage* newTabButtonImage = + [[[NSImage alloc] initWithSize:NSMakeSize(34, 17)] autorelease]; + [newTabButtonImage setCacheMode:NSImageCacheAlways]; + [newTabButtonImage addRepresentation:imageRep]; + + return newTabButtonImage; +} + ++ (NSBezierPath*)newTabButtonBezierPathWithInset:(CGFloat)inset { + NSBezierPath* bezierPath = [NSBezierPath bezierPath]; + + // Bottom edge. + [bezierPath moveToPoint:NSMakePoint(19 - inset, 1.2825 + inset)]; + [bezierPath lineToPoint:NSMakePoint(10.45 + inset, 1.2825 + inset)]; + + // Lower-left corner. + [bezierPath curveToPoint:NSMakePoint(6.08 + inset, 2.85 + inset) + controlPoint1:NSMakePoint(10.1664 + inset, 1.3965 + inset) + controlPoint2:NSMakePoint(7.89222 + inset, 0.787708 + inset)]; + + // Left side. + [bezierPath lineToPoint:NSMakePoint(0.7125 + inset, 14.25 - inset)]; + + // Upper-left corner. + [bezierPath curveToPoint:NSMakePoint(1.71 + inset, 16.2688 - inset) + controlPoint1:NSMakePoint(0.246496 + inset, 15.2613 - inset) + controlPoint2:NSMakePoint(0.916972 + inset, 16.3489 - inset)]; + + // Top edge. + [bezierPath lineToPoint:NSMakePoint(23.275 - inset, 16.2688 - inset)]; + + // Upper right corner. + [bezierPath curveToPoint:NSMakePoint(27.645 - inset, 14.7012 - inset) + controlPoint1:NSMakePoint(26.4376 - inset, 16.3305 - inset) + controlPoint2:NSMakePoint(26.9257 - inset, 15.8059 - inset)]; + + // Right side. + [bezierPath lineToPoint:NSMakePoint(32.9543 - inset, 3.62561 + inset)]; + + // Lower right corner. + [bezierPath curveToPoint:NSMakePoint(32.015 - inset, 1.2825 + inset) + controlPoint1:NSMakePoint(34.069 - inset, 1.45303 + inset) + controlPoint2:NSMakePoint(31.0348 - inset, 1.31455 + inset)]; + + [bezierPath closePath]; + + return bezierPath; +} + ++ (void)drawNewTabButtonImage:(NewTabButtonCustomImageRep*)imageRep { + [[NSGraphicsContext currentContext] + cr_setPatternPhase:[imageRep patternPhasePosition] + forView:[imageRep destView]]; + + NSBezierPath* bezierPath = [self newTabButtonBezierPathWithInset:0]; + CGContextRef context = static_cast<CGContextRef>( + [[NSGraphicsContext currentContext] graphicsPort]); + CGFloat lineWidth = LineWidthFromContext(context); + [bezierPath setLineWidth:lineWidth]; + + if ([imageRep fillColor]) { + [[imageRep fillColor] set]; + [bezierPath fill]; + } + + static NSColor* strokeColor = + [[NSColor colorWithCalibratedWhite:0 alpha:0.4] retain]; + [strokeColor set]; + [bezierPath stroke]; + + // Bottom edge. + bezierPath = [NSBezierPath bezierPath]; + [bezierPath moveToPoint:NSMakePoint(31, 1.2825)]; + [bezierPath lineToPoint:NSMakePoint(9, 1.2825)]; + static NSColor* bottomEdgeColor = + [[NSColor colorWithCalibratedWhite:0.25 alpha:0.3] retain]; + [bottomEdgeColor set]; + [bezierPath setLineWidth:lineWidth]; + [bezierPath setLineCapStyle:NSRoundLineCapStyle]; + [bezierPath stroke]; + + // Shadow beneath the bottom edge. + NSAffineTransform* translateTransform = [NSAffineTransform transform]; + [translateTransform translateXBy:0 yBy:-lineWidth]; + [bezierPath transformUsingAffineTransform:translateTransform]; + static NSColor* shadowColor = + [[NSColor colorWithCalibratedWhite:0.5 alpha:0.3] retain]; + [shadowColor set]; + [bezierPath stroke]; + + static NSColor* lightColor = + [[NSColor colorWithCalibratedWhite:1 alpha:0.35] retain]; + static NSColor* lightIncognitoColor = + [[NSColor colorWithCalibratedWhite:1 alpha:0.15] retain]; + static NSColor* darkColor = + [[NSColor colorWithCalibratedWhite:0 alpha:0.08] retain]; + + CGFloat inset = -1; + switch ([imageRep renderingOption]) { + case RenderingOption::OVERLAY_LIGHTEN: + [lightColor set]; + inset = 0; + break; + + case RenderingOption::OVERLAY_LIGHTEN_INCOGNITO: + [lightIncognitoColor set]; + inset = 0; + break; + + case RenderingOption::OVERLAY_DARKEN: + [darkColor set]; + NSRectFillUsingOperation(NSMakeRect(0, 0, 34, 17), NSCompositeSourceAtop); + break; + + case RenderingOption::INLAY_LIGHTEN: + [lightColor set]; + inset = 1; + break; + + case RenderingOption::NORMAL: + break; + } + if (inset != -1) { + bezierPath = [self newTabButtonBezierPathWithInset:inset]; + [bezierPath setLineWidth:lineWidth]; + [bezierPath fill]; + } +} + +- (NSImage*)imageWithFillColor:(NSColor*)fillColor { + NSImage* image = + [[[NSImage alloc] initWithSize:NSMakeSize(34, 17)] autorelease]; + + [image lockFocus]; + [fillColor set]; + [[NewTabButton newTabButtonBezierPathWithInset:0] fill]; + [image unlockFocus]; + return image; +} + @end diff --git a/chrome/browser/ui/cocoa/profiles/avatar_icon_controller.mm b/chrome/browser/ui/cocoa/profiles/avatar_icon_controller.mm index ca9c0d7..841a796 100644 --- a/chrome/browser/ui/cocoa/profiles/avatar_icon_controller.mm +++ b/chrome/browser/ui/cocoa/profiles/avatar_icon_controller.mm @@ -14,9 +14,13 @@ #include "chrome/grit/generated_resources.h" #include "grit/theme_resources.h" #include "ui/base/l10n/l10n_util_mac.h" +#include "ui/base/material_design/material_design_controller.h" #include "ui/base/resource/resource_bundle.h" #include "ui/gfx/image/image.h" +#include "ui/gfx/image/image_skia_util_mac.h" +#include "ui/gfx/paint_vector_icon.h" #include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h" +#include "ui/gfx/vector_icons_public.h" @interface AvatarIconController (Private) - (void)setButtonEnabled:(BOOL)flag; @@ -36,8 +40,13 @@ [container setWantsLayer:YES]; [self setView:container]; - button_.reset([[NSButton alloc] initWithFrame:NSMakeRect( - 0, 0, profiles::kAvatarIconWidth, profiles::kAvatarIconHeight)]); + bool isModeMaterial = ui::MaterialDesignController::IsModeMaterial(); + NSRect frameRect = NSMakeRect(4, 5, profiles::kAvatarIconWidth, + profiles::kAvatarIconHeight); + if (!isModeMaterial) { + frameRect.origin = NSZeroPoint; + } + button_.reset([[NSButton alloc] initWithFrame:frameRect]); NSButtonCell* cell = [button_ cell]; [button_ setButtonType:NSMomentaryLightButton]; @@ -71,9 +80,15 @@ l10n_util::GetNSString(IDS_PROFILES_BUBBLE_ACCESSIBLE_DESCRIPTION) forAttribute:NSAccessibilityDescriptionAttribute]; - NSImage* icon = ResourceBundle::GetSharedInstance().GetNativeImageNamed( - IDR_OTR_ICON).ToNSImage(); - [button_ setImage:[self compositeImageWithShadow:icon]]; + if (isModeMaterial) { + NSImage* icon = NSImageFromImageSkia(gfx::CreateVectorIcon( + gfx::VectorIconId::INCOGNITO, 24, SK_ColorWHITE)); + [button_ setImage:icon]; + } else { + NSImage* icon = ResourceBundle::GetSharedInstance().GetNativeImageNamed( + IDR_OTR_ICON).ToNSImage(); + [button_ setImage:[self compositeImageWithShadow:icon]]; + } [button_ setEnabled:NO]; [[self view] addSubview:button_]; diff --git a/chrome/browser/ui/cocoa/tabs/tab_controller.mm b/chrome/browser/ui/cocoa/tabs/tab_controller.mm index b645a2a..209d18e 100644 --- a/chrome/browser/ui/cocoa/tabs/tab_controller.mm +++ b/chrome/browser/ui/cocoa/tabs/tab_controller.mm @@ -19,6 +19,7 @@ #include "content/public/browser/user_metrics.h" #import "extensions/common/extension.h" #import "ui/base/cocoa/menu_controller.h" +#include "ui/base/material_design/material_design_controller.h" @implementation TabController @@ -63,7 +64,12 @@ class MenuDelegate : public ui::SimpleMenuModel::Delegate { } // TabControllerInternal namespace -+ (CGFloat)defaultTabHeight { return 26; } ++ (CGFloat)defaultTabHeight { + if (!ui::MaterialDesignController::IsModeMaterial()) { + return 26; + } + return 29; +} // The min widths is the smallest number at which the right edge of the right // tab border image is not visibly clipped. It is a bit smaller than the sum @@ -71,7 +77,13 @@ class MenuDelegate : public ui::SimpleMenuModel::Delegate { // pixels on the side. The selected tab width includes the close button width. + (CGFloat)minTabWidth { return 36; } + (CGFloat)minActiveTabWidth { return 52; } -+ (CGFloat)maxTabWidth { return 214; } ++ (CGFloat)maxTabWidth { + if (!ui::MaterialDesignController::IsModeMaterial()) { + return 214; + } + return 246; +} + + (CGFloat)pinnedTabWidth { return 58; } - (TabView*)tabView { @@ -81,10 +93,14 @@ class MenuDelegate : public ui::SimpleMenuModel::Delegate { - (id)init { if ((self = [super init])) { + bool isModeMaterial = ui::MaterialDesignController::IsModeMaterial(); // Icon. // Remember the icon's frame, so that if the icon is ever removed, a new // one can later replace it in the proper location. - originalIconFrame_ = NSMakeRect(19, 5, 16, 16); + originalIconFrame_ = NSMakeRect(18, 6, 16, 16); + if (!isModeMaterial) { + originalIconFrame_ = NSMakeRect(19, 5, 16, 16); + } iconView_.reset([[SpriteView alloc] initWithFrame:originalIconFrame_]); [iconView_ setAutoresizingMask:NSViewMaxXMargin | NSViewMinYMargin]; @@ -94,11 +110,18 @@ class MenuDelegate : public ui::SimpleMenuModel::Delegate { // and contract the title frame under those conditions. We don't have to // explicilty save the offset between the title and the close button since // we can just get that value for the close button's frame. - NSRect titleFrame = NSMakeRect(35, 6, 92, 14); + NSRect titleFrame = NSMakeRect(35, 6, 92, 17); + if (!isModeMaterial) { + titleFrame.size.height = 14; + } // Close button. + NSRect closeButtonFrame = NSMakeRect(129, 6, 16, 16); + if (!isModeMaterial) { + closeButtonFrame = NSMakeRect(127, 4, 18, 18); + } closeButton_.reset([[HoverCloseButton alloc] initWithFrame: - NSMakeRect(127, 4, 18, 18)]); + closeButtonFrame]); [closeButton_ setAutoresizingMask:NSViewMinXMargin]; [closeButton_ setTarget:self]; [closeButton_ setAction:@selector(closeTab:)]; @@ -399,7 +422,11 @@ class MenuDelegate : public ui::SimpleMenuModel::Delegate { newTitleFrame.origin.y = oldTitleFrame.origin.y; if (newShowIcon) { - newTitleFrame.origin.x = NSMaxX([iconView_ frame]); + newTitleFrame.origin.x = NSMaxX([iconView_ frame]) + 4; + + if (!ui::MaterialDesignController::IsModeMaterial()) { + newTitleFrame.origin.x -= 4; + } } else { newTitleFrame.origin.x = originalIconFrame_.origin.x; } diff --git a/chrome/browser/ui/cocoa/tabs/tab_strip_controller.mm b/chrome/browser/ui/cocoa/tabs/tab_strip_controller.mm index 67f1277..82d05ca 100644 --- a/chrome/browser/ui/cocoa/tabs/tab_strip_controller.mm +++ b/chrome/browser/ui/cocoa/tabs/tab_strip_controller.mm @@ -64,6 +64,7 @@ #include "ui/base/cocoa/animation_utils.h" #import "ui/base/cocoa/tracking_area.h" #include "ui/base/l10n/l10n_util.h" +#include "ui/base/material_design/material_design_controller.h" #include "ui/base/models/list_selection_model.h" #include "ui/base/resource/resource_bundle.h" #include "ui/base/theme_provider.h" @@ -87,13 +88,15 @@ const CGFloat kUseFullAvailableWidth = -1.0; // the throbber is painted, the throbber's invalidation will also invalidate // parts of the tab to the left, and two tabs's backgrounds need to be painted // on each throbber frame instead of one. -const CGFloat kTabOverlap = 19.0; +const CGFloat kTabOverlap = 18.0; +const CGFloat kTabOverlapNonMD = 19.0; // The amount by which pinned tabs are separated from normal tabs. const CGFloat kLastPinnedTabSpacing = 2.0; // The amount by which the new tab button is offset (from the tabs). -const CGFloat kNewTabButtonOffset = 8.0; +const CGFloat kNewTabButtonOffset = 10.0; +const CGFloat kNewTabButtonOffsetNonMD = 8.0; // Time (in seconds) in which tabs animate to their final position. const NSTimeInterval kAnimationDuration = 0.125; @@ -139,93 +142,6 @@ private: DISALLOW_COPY_AND_ASSIGN(ScopedNSAnimationContextGroup); }; -// Creates an NSImage with size |size| and bitmap image representations for both -// 1x and 2x scale factors. |drawingHandler| is called once for every scale -// factor. This is similar to -[NSImage imageWithSize:flipped:drawingHandler:], -// but this function always evaluates drawingHandler eagerly, and it works on -// 10.6 and 10.7. -NSImage* CreateImageWithSize(NSSize size, - void (^drawingHandler)(NSSize)) { - base::scoped_nsobject<NSImage> result([[NSImage alloc] initWithSize:size]); - [NSGraphicsContext saveGraphicsState]; - for (ui::ScaleFactor scale_factor : ui::GetSupportedScaleFactors()) { - float scale = GetScaleForScaleFactor(scale_factor); - NSBitmapImageRep *bmpImageRep = [[[NSBitmapImageRep alloc] - initWithBitmapDataPlanes:NULL - pixelsWide:size.width * scale - pixelsHigh:size.height * scale - bitsPerSample:8 - samplesPerPixel:4 - hasAlpha:YES - isPlanar:NO - colorSpaceName:NSDeviceRGBColorSpace - bytesPerRow:0 - bitsPerPixel:0] autorelease]; - [bmpImageRep setSize:size]; - [NSGraphicsContext setCurrentContext: - [NSGraphicsContext graphicsContextWithBitmapImageRep:bmpImageRep]]; - drawingHandler(size); - [result addRepresentation:bmpImageRep]; - } - [NSGraphicsContext restoreGraphicsState]; - - return result.release(); -} - -// Takes a normal bitmap and a mask image and returns an image the size of the -// mask that has pixels from |image| but alpha information from |mask|. -NSImage* ApplyMask(NSImage* image, NSImage* mask) { - return [CreateImageWithSize([mask size], ^(NSSize size) { - // Skip a few pixels from the top of the tab background gradient, because - // the new tab button is not drawn at the very top of the browser window. - const int kYOffset = 10; - CGFloat width = size.width; - CGFloat height = size.height; - - // In some themes, the tab background image is narrower than the - // new tab button, so tile the background image. - CGFloat x = 0; - // The floor() is to make sure images with odd widths don't draw to the - // same pixel twice on retina displays. (Using NSDrawThreePartImage() - // caused a startup perf regression, so that cannot be used.) - CGFloat tileWidth = floor(std::min(width, [image size].width)); - while (x < width) { - [image drawAtPoint:NSMakePoint(x, 0) - fromRect:NSMakeRect(0, - [image size].height - height - kYOffset, - tileWidth, - height) - operation:NSCompositeCopy - fraction:1.0]; - x += tileWidth; - } - - [mask drawAtPoint:NSZeroPoint - fromRect:NSMakeRect(0, 0, width, height) - operation:NSCompositeDestinationIn - fraction:1.0]; - }) autorelease]; -} - -// Paints |overlay| on top of |ground|. -NSImage* Overlay(NSImage* ground, NSImage* overlay, CGFloat alpha) { - DCHECK_EQ([ground size].width, [overlay size].width); - DCHECK_EQ([ground size].height, [overlay size].height); - - return [CreateImageWithSize([ground size], ^(NSSize size) { - CGFloat width = size.width; - CGFloat height = size.height; - [ground drawAtPoint:NSZeroPoint - fromRect:NSMakeRect(0, 0, width, height) - operation:NSCompositeCopy - fraction:1.0]; - [overlay drawAtPoint:NSZeroPoint - fromRect:NSMakeRect(0, 0, width, height) - operation:NSCompositeSourceOver - fraction:alpha]; - }) autorelease]; -} - } // namespace @interface TabStripController (Private) @@ -252,7 +168,6 @@ NSImage* Overlay(NSImage* ground, NSImage* overlay, CGFloat alpha) { disposition:(WindowOpenDisposition*)disposition; - (void)setNewTabButtonHoverState:(BOOL)showHover; - (void)themeDidChangeNotification:(NSNotification*)notification; -- (void)setNewTabImages; - (BOOL)doesAnyOtherWebContents:(content::WebContents*)selected haveMediaState:(TabMediaState)state; @end @@ -513,7 +428,6 @@ NSImage* Overlay(NSImage* ground, NSImage* overlay, CGFloat alpha) { [newTabButton_ setTarget:self]; [newTabButton_ setAction:@selector(clickNewTabButton:)]; - [self setNewTabImages]; newTabButtonShowingHoverImage_ = NO; newTabTrackingArea_.reset( [[CrTrackingArea alloc] initWithRect:[newTabButton_ bounds] @@ -1012,9 +926,14 @@ NSImage* Overlay(NSImage* ground, NSImage* overlay, CGFloat alpha) { availableSpace = NSWidth([tabStripView_ frame]); // Account for the width of the new tab button. - availableSpace -= - NSWidth([newTabButton_ frame]) + kNewTabButtonOffset - kTabOverlap; - + if (!ui::MaterialDesignController::IsModeMaterial()) { + availableSpace -= + NSWidth([newTabButton_ frame]) + kNewTabButtonOffsetNonMD - + kTabOverlapNonMD; + } else { + availableSpace -= + NSWidth([newTabButton_ frame]) + kNewTabButtonOffset - kTabOverlap; + } // Account for the right-side controls if not in rapid closure mode. // (In rapid closure mode, the available width is set based on the // position of the rightmost tab, not based on the width of the tab strip, @@ -2372,45 +2291,7 @@ NSImage* Overlay(NSImage* ground, NSImage* overlay, CGFloat alpha) { } - (void)themeDidChangeNotification:(NSNotification*)notification { - [self setNewTabImages]; -} - -- (void)setNewTabImages { - const ui::ThemeProvider* theme = [[tabStripView_ window] themeProvider]; - if (!theme) - return; - - ResourceBundle& rb = ResourceBundle::GetSharedInstance(); - NSImage* mask = rb.GetNativeImageNamed(IDR_NEWTAB_BUTTON_MASK).ToNSImage(); - NSImage* normal = rb.GetNativeImageNamed(IDR_NEWTAB_BUTTON).ToNSImage(); - NSImage* hover = rb.GetNativeImageNamed(IDR_NEWTAB_BUTTON_H).ToNSImage(); - NSImage* pressed = rb.GetNativeImageNamed(IDR_NEWTAB_BUTTON_P).ToNSImage(); - - NSImage* foreground = ApplyMask( - theme->GetNSImageNamed(IDR_THEME_TAB_BACKGROUND), mask); - - [[newTabButton_ cell] setImage:Overlay(foreground, normal, 1.0) - forButtonState:image_button_cell::kDefaultState]; - [[newTabButton_ cell] setImage:Overlay(foreground, hover, 1.0) - forButtonState:image_button_cell::kHoverState]; - [[newTabButton_ cell] setImage:Overlay(foreground, pressed, 1.0) - forButtonState:image_button_cell::kPressedState]; - - // IDR_THEME_TAB_BACKGROUND_INACTIVE is only used with the default theme. - if (theme->UsingSystemTheme()) { - const CGFloat alpha = tabs::kImageNoFocusAlpha; - NSImage* background = ApplyMask( - theme->GetNSImageNamed(IDR_THEME_TAB_BACKGROUND_INACTIVE), mask); - [[newTabButton_ cell] setImage:Overlay(background, normal, alpha) - forButtonState:image_button_cell::kDefaultStateBackground]; - [[newTabButton_ cell] setImage:Overlay(background, hover, alpha) - forButtonState:image_button_cell::kHoverStateBackground]; - } else { - [[newTabButton_ cell] setImage:nil - forButtonState:image_button_cell::kDefaultStateBackground]; - [[newTabButton_ cell] setImage:nil - forButtonState:image_button_cell::kHoverStateBackground]; - } + [newTabButton_ setImages]; } @end diff --git a/chrome/browser/ui/cocoa/tabs/tab_strip_view.mm b/chrome/browser/ui/cocoa/tabs/tab_strip_view.mm index a90e057..0b26910 100644 --- a/chrome/browser/ui/cocoa/tabs/tab_strip_view.mm +++ b/chrome/browser/ui/cocoa/tabs/tab_strip_view.mm @@ -7,6 +7,10 @@ #include <cmath> // floor #include "base/logging.h" +#include "base/mac/foundation_util.h" +#include "base/mac/mac_util.h" +#include "base/mac/sdk_forward_declarations.h" +#include "chrome/browser/themes/theme_properties.h" #include "chrome/browser/themes/theme_service.h" #import "chrome/browser/ui/cocoa/browser_window_controller.h" #import "chrome/browser/ui/cocoa/new_tab_button.h" @@ -19,6 +23,7 @@ #import "ui/base/cocoa/nsgraphics_context_additions.h" #import "ui/base/cocoa/nsview_additions.h" #include "ui/base/l10n/l10n_util_mac.h" +#include "ui/base/material_design/material_design_controller.h" #include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h" @implementation TabStripView @@ -47,35 +52,97 @@ return self; } -// Draw bottom border bitmap. Each tab is responsible for mimicking this bottom -// border, unless it's the selected tab. -- (void)drawBorder:(NSRect)dirtyRect { +// Draw the bottom edge of the tab strip. Each tab is responsible for mimicking +// this bottom border, unless it's the selected tab. +- (void)drawBottomEdge:(NSRect)dirtyRect { NSWindow* window = [self window]; const ui::ThemeProvider* themeProvider = [window themeProvider]; if (!themeProvider) return; - // First draw the toolbar bitmap, so that theme colors can shine through. - NSRect backgroundRect = [self bounds]; - backgroundRect.size.height = 2 * [self cr_lineWidth]; - if (NSIntersectsRect(backgroundRect, dirtyRect)) - [self drawBackground:backgroundRect]; - - // Draw the border bitmap, which is partially transparent. - NSImage* image = themeProvider->GetNSImageNamed(IDR_TOOLBAR_SHADE_TOP); - NSRect borderRect = backgroundRect; - borderRect.size.height = [image size].height; - if (NSIntersectsRect(borderRect, dirtyRect)) { - BOOL focused = [window isMainWindow]; - NSDrawThreePartImage(borderRect, nil, image, nil, /*vertical=*/ NO, - NSCompositeSourceOver, - focused ? 1.0 : tabs::kImageNoFocusAlpha, - /*flipped=*/ NO); + if (!ui::MaterialDesignController::IsModeMaterial()) { + // First draw the toolbar bitmap, so that theme colors can shine through. + NSRect backgroundRect = [self bounds]; + backgroundRect.size.height = 2 * [self cr_lineWidth]; + if (NSIntersectsRect(backgroundRect, dirtyRect)) + [self drawBackground:backgroundRect]; + + // Draw the border bitmap, which is partially transparent. + NSImage* image = themeProvider->GetNSImageNamed(IDR_TOOLBAR_SHADE_TOP); + NSRect borderRect = backgroundRect; + borderRect.size.height = [image size].height; + if (NSIntersectsRect(borderRect, dirtyRect)) { + BOOL focused = [window isMainWindow]; + NSDrawThreePartImage(borderRect, nil, image, nil, /*vertical=*/ NO, + NSCompositeSourceOver, + focused ? 1.0 : tabs::kImageNoFocusAlpha, + /*flipped=*/ NO); + } + + return; } + + if (themeProvider->HasCustomImage(IDR_THEME_TOOLBAR) || + themeProvider->HasCustomColor(ThemeProperties::COLOR_TOOLBAR)) { + // First draw the toolbar bitmap, so that theme colors can shine through. + NSRect backgroundRect = [self bounds]; + backgroundRect.size.height = [self cr_lineWidth]; + if (NSIntersectsRect(backgroundRect, dirtyRect)) { + [self drawBackground:backgroundRect]; + } + + // Pre-MD the IDR_TOOLBAR_SHADE_TOP image would lay down a light highlight + // which helped dark toolbars stand out from dark frames. Lay down a thin + // highlight in MD also. + if ([window isMainWindow]) { + [themeProvider->GetNSColor( + ThemeProperties::COLOR_TOOLBAR_STROKE_THEME) set]; + } else { + [themeProvider->GetNSColor( + ThemeProperties::COLOR_TOOLBAR_STROKE_THEME_INACTIVE) set]; + } + } else { + [themeProvider->GetNSColor(ThemeProperties::COLOR_TOOLBAR_STROKE) set]; + } + NSRect borderRect = NSMakeRect(0.0, 0.0, self.bounds.size.width, + [self cr_lineWidth]); + NSRectFillUsingOperation(NSIntersectionRect(dirtyRect, borderRect), + NSCompositeSourceOver); } - (void)drawRect:(NSRect)dirtyRect { - [self drawBorder:dirtyRect]; + const ui::ThemeProvider* themeProvider = [[self window] themeProvider]; + bool hasCustomThemeImage = themeProvider && + themeProvider->HasCustomImage(IDR_THEME_FRAME); + bool isModeMaterial = ui::MaterialDesignController::IsModeMaterial(); + BOOL supportsVibrancy = [self visualEffectView] != nil; + BOOL isMainWindow = [[self window] isMainWindow]; + + if (themeProvider && !hasCustomThemeImage && isModeMaterial) { + NSColor* theColor = nil; + if (isMainWindow) { + // The vibrancy overlay makes the Incognito NSVisualEffectView + // somewhat darker, and the non-Incognito NSVisualEffectView much darker. + if (supportsVibrancy && + !themeProvider->HasCustomColor(ThemeProperties::COLOR_FRAME)) { + theColor = themeProvider->GetNSColor( + ThemeProperties::COLOR_FRAME_VIBRANCY_OVERLAY); + } else { + theColor = themeProvider->GetNSColor(ThemeProperties::COLOR_FRAME); + } + } else { + // Inactive MD windows always draw a solid color. + theColor = themeProvider->GetNSColor( + ThemeProperties::COLOR_FRAME_INACTIVE); + } + + if (theColor) { + [theColor set]; + NSRectFillUsingOperation(dirtyRect, NSCompositeSourceOver); + } + } + + [self drawBottomEdge:dirtyRect]; // Draw drop-indicator arrow (if appropriate). // TODO(viettrungluu): this is all a stop-gap measure. @@ -276,14 +343,72 @@ newTabButton_.reset([button retain]); } +- (NSVisualEffectView*)visualEffectView { + // NSVisualEffectView is only used in Material Design, and only available on + // OS X 10.10 and higher. + if (!ui::MaterialDesignController::IsModeMaterial() || + !base::mac::IsOSYosemiteOrLater()) { + return nil; + } + + NSView* rootView = [[[self window] contentView] superview]; + Class nsVisualEffectViewClass = NSClassFromString(@"NSVisualEffectView"); + DCHECK(nsVisualEffectViewClass); + for (NSView* view in [rootView subviews]) { + if ([view isKindOfClass:nsVisualEffectViewClass]) { + return base::mac::ObjCCast<NSVisualEffectView>(view); + } + } + return nil; +} + - (void)setController:(TabStripController*)controller { controller_ = controller; + // If tearing down the browser window, there's nothing more to do. + if (!controller_) { + return; + } + + // Finish configuring the NSVisualEffectView so that it matches the window's + // theme. + NSVisualEffectView* visualEffectView = [self visualEffectView]; + const ui::ThemeProvider* themeProvider = [[self window] themeProvider]; + if (!visualEffectView || !themeProvider) { + return; + } + + // Themes with custom frame images don't use vibrancy. Otherwise, if Incognito + // use Material Dark. + if (themeProvider->HasCustomImage(IDR_THEME_FRAME) || + themeProvider->HasCustomColor(ThemeProperties::COLOR_FRAME)) { + [visualEffectView setState:NSVisualEffectStateInactive]; + } else if (themeProvider->InIncognitoMode()) { + [visualEffectView setMaterial:NSVisualEffectMaterialDark]; + [visualEffectView setAppearance: + [NSAppearance appearanceNamed:NSAppearanceNameVibrantDark]]; + } } // ThemedWindowDrawing implementation. - (void)windowDidChangeTheme { [self setNeedsDisplay:YES]; + + // Configure the NSVisualEffectView so that it does nothing if the user has + // switched to a custom theme, or uses vibrancy if the user has switched back + // to the default theme. + NSVisualEffectView* visualEffectView = [self visualEffectView]; + const ui::ThemeProvider* themeProvider = [[self window] themeProvider]; + if (!visualEffectView || !themeProvider) { + return; + } + + if (themeProvider->HasCustomImage(IDR_THEME_FRAME) || + themeProvider->HasCustomColor(ThemeProperties::COLOR_FRAME)) { + [visualEffectView setState:NSVisualEffectStateInactive]; + } else { + [visualEffectView setState:NSVisualEffectStateFollowsWindowActiveState]; + } } - (void)windowDidChangeActive { diff --git a/chrome/browser/ui/cocoa/tabs/tab_view.h b/chrome/browser/ui/cocoa/tabs/tab_view.h index b4605a6..3f4f8b3 100644 --- a/chrome/browser/ui/cocoa/tabs/tab_view.h +++ b/chrome/browser/ui/cocoa/tabs/tab_view.h @@ -77,6 +77,7 @@ const CGFloat kImageNoFocusAlpha = 0.65; base::scoped_nsobject<NSString> toolTipText_; } +@property(readonly, nonatomic) BOOL isActiveTab; @property(retain, nonatomic) NSString* title; @property(assign, nonatomic) NSRect titleFrame; @property(retain, nonatomic) NSColor* titleColor; diff --git a/chrome/browser/ui/cocoa/tabs/tab_view.mm b/chrome/browser/ui/cocoa/tabs/tab_view.mm index 5b83ef9..98d1e5b 100644 --- a/chrome/browser/ui/cocoa/tabs/tab_view.mm +++ b/chrome/browser/ui/cocoa/tabs/tab_view.mm @@ -8,6 +8,7 @@ #include "base/logging.h" #include "base/mac/sdk_forward_declarations.h" #include "base/strings/sys_string_conversions.h" +#include "chrome/browser/themes/theme_properties.h" #include "chrome/browser/themes/theme_service.h" #import "chrome/browser/ui/cocoa/tabs/media_indicator_button_cocoa.h" #import "chrome/browser/ui/cocoa/tabs/tab_controller.h" @@ -16,17 +17,17 @@ #import "chrome/browser/ui/cocoa/view_id_util.h" #include "chrome/grit/generated_resources.h" #include "grit/theme_resources.h" +#include "skia/ext/skia_utils_mac.h" #import "third_party/google_toolbox_for_mac/src/AppKit/GTMFadeTruncatingTextFieldCell.h" #import "ui/base/cocoa/nsgraphics_context_additions.h" #import "ui/base/cocoa/nsview_additions.h" #include "ui/base/cocoa/three_part_image.h" #include "ui/base/l10n/l10n_util.h" +#include "ui/base/material_design/material_design_controller.h" #include "ui/base/resource/resource_bundle.h" #include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h" -const int kFillHeight = 25; // Height of the "mask on" part of the mask bitmap. - // The amount of time in seconds during which each type of glow increases, holds // steady, and decreases, respectively. const NSTimeInterval kHoverShowDuration = 0.2; @@ -44,23 +45,82 @@ const NSTimeInterval kGlowUpdateInterval = 0.025; // has moved less than the threshold, we want to close the tab. const CGFloat kRapidCloseDist = 2.5; +@interface TabView(MaterialDesign) ++ (void)drawTabLeftMaskImage; ++ (void)drawTabRightMaskImage; ++ (void)drawTabLeftEdgeImage; ++ (void)drawTabMiddleEdgeImage; ++ (void)drawTabRightEdgeImage; +@end + namespace { +NSImage* imageForResourceID(int resource_id) { + if (!ui::MaterialDesignController::IsModeMaterial()) { + ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); + return [rb.GetNativeImageNamed(resource_id).CopyNSImage() autorelease]; + } + + CGFloat imageWidth = resource_id == IDR_TAB_ACTIVE_CENTER ? 1 : 18; + SEL theSelector = 0; + switch (resource_id) { + case IDR_TAB_ACTIVE_LEFT: + theSelector = @selector(drawTabLeftEdgeImage); + break; + + case IDR_TAB_ACTIVE_CENTER: + theSelector = @selector(drawTabMiddleEdgeImage); + break; + + case IDR_TAB_ACTIVE_RIGHT: + theSelector = @selector(drawTabRightEdgeImage); + break; + + case IDR_TAB_ALPHA_LEFT: + theSelector = @selector(drawTabLeftMaskImage); + break; + + case IDR_TAB_ALPHA_RIGHT: + theSelector = @selector(drawTabRightMaskImage); + break; + } + DCHECK(theSelector); + + base::scoped_nsobject<NSCustomImageRep> imageRep = + [[NSCustomImageRep alloc] + initWithDrawSelector:theSelector + delegate:[TabView class]]; + + NSImage* newTabButtonImage = + [[[NSImage alloc] initWithSize:NSMakeSize(imageWidth, 29)] autorelease]; + [newTabButtonImage setCacheMode:NSImageCacheAlways]; + [newTabButtonImage addRepresentation:imageRep]; + + return newTabButtonImage; +} + ui::ThreePartImage& GetMaskImage() { CR_DEFINE_STATIC_LOCAL(ui::ThreePartImage, mask, - (IDR_TAB_ALPHA_LEFT, 0, IDR_TAB_ALPHA_RIGHT)); + (imageForResourceID(IDR_TAB_ALPHA_LEFT), nullptr, + imageForResourceID(IDR_TAB_ALPHA_RIGHT))); + return mask; } -ui::ThreePartImage& GetStrokeImage(bool active) { +ui::ThreePartImage& GetStrokeImage() { CR_DEFINE_STATIC_LOCAL( - ui::ThreePartImage, activeStroke, - (IDR_TAB_ACTIVE_LEFT, IDR_TAB_ACTIVE_CENTER, IDR_TAB_ACTIVE_RIGHT)); - CR_DEFINE_STATIC_LOCAL( - ui::ThreePartImage, inactiveStroke, - (IDR_TAB_INACTIVE_LEFT, IDR_TAB_INACTIVE_CENTER, IDR_TAB_INACTIVE_RIGHT)); + ui::ThreePartImage, stroke, + (imageForResourceID(IDR_TAB_ACTIVE_LEFT), + imageForResourceID(IDR_TAB_ACTIVE_CENTER), + imageForResourceID(IDR_TAB_ACTIVE_RIGHT))); + + return stroke; +} - return active ? activeStroke : inactiveStroke; +CGFloat LineWidthFromContext(CGContextRef context) { + CGRect unitRect = CGRectMake(0.0, 0.0, 1.0, 1.0); + CGRect deviceRect = CGContextConvertRectToDeviceSpace(context, unitRect); + return 1.0 / deviceRect.size.height; } } // namespace @@ -80,6 +140,11 @@ ui::ThreePartImage& GetStrokeImage(bool active) { @synthesize alertAlpha = alertAlpha_; @synthesize closing = closing_; ++ (CGFloat)maskImageFillHeight { + // Return the height of the "mask on" part of the mask bitmap. + return [TabController defaultTabHeight] - 1; +} + - (id)initWithFrame:(NSRect)frame controller:(TabController*)controller closeButton:(HoverCloseButton*)closeButton { @@ -96,7 +161,11 @@ ui::ThreePartImage& GetStrokeImage(bool active) { base::scoped_nsobject<GTMFadeTruncatingTextFieldCell> labelCell( [[GTMFadeTruncatingTextFieldCell alloc] initTextCell:@"Label"]); [labelCell setControlSize:NSSmallControlSize]; - CGFloat fontSize = [NSFont systemFontSizeForControlSize:NSSmallControlSize]; + // Font size is 12, per Material Design spec. + CGFloat fontSize = 12; + if (!ui::MaterialDesignController::IsModeMaterial()) { + fontSize = [NSFont systemFontSizeForControlSize:NSSmallControlSize]; + } [labelCell setFont:[NSFont systemFontOfSize:fontSize]]; [titleView_ setCell:labelCell]; titleViewCell_ = labelCell; @@ -178,7 +247,7 @@ ui::ThreePartImage& GetStrokeImage(bool active) { NSPoint viewPoint = [self convertPoint:aPoint fromView:[self superview]]; NSRect maskRect = [self bounds]; - maskRect.size.height = kFillHeight; + maskRect.size.height = [TabView maskImageFillHeight]; return GetMaskImage().HitTest(viewPoint, maskRect) ? self : nil; } @@ -307,12 +376,16 @@ ui::ThreePartImage& GetStrokeImage(bool active) { NSRect bounds = [self bounds]; NSRect clippingRect = bounds; - clippingRect.size.height = kFillHeight; + clippingRect.size.height = [TabView maskImageFillHeight]; if (state_ != NSOnState) { // Background tabs should not paint over the tab strip separator, which is - // two pixels high in both lodpi and hidpi. - clippingRect.origin.y = 2 * [self cr_lineWidth]; - clippingRect.size.height -= clippingRect.origin.y; + // two pixels high in both lodpi and hidpi, and one pixel high in MD. + CGFloat tabStripSeparatorLineWidth = [self cr_lineWidth]; + if (!ui::MaterialDesignController::IsModeMaterial()) { + tabStripSeparatorLineWidth *= 2; + } + clippingRect.origin.y = tabStripSeparatorLineWidth; + clippingRect.size.height -= tabStripSeparatorLineWidth; } NSRectClip(clippingRect); @@ -345,7 +418,7 @@ ui::ThreePartImage& GetStrokeImage(bool active) { if (hoverAlpha > 0 || alertAlpha > 0) { CGContextBeginTransparencyLayer(cgContext, 0); - // The alert glow overlay is like the selected state but at most at most 80% + // The alert glow overlay is like the selected state but at most 80% // opaque. The hover glow brings up the overlay's opacity at most 50%. CGFloat backgroundAlpha = 0.8 * alertAlpha; backgroundAlpha += (1 - backgroundAlpha) * 0.5 * hoverAlpha; @@ -365,10 +438,16 @@ ui::ThreePartImage& GetStrokeImage(bool active) { // Draw a mouse hover gradient for the default themes. if (hoverAlpha > 0) { if (themeProvider && !hasCustomTheme) { + CGFloat whiteValue = 1; + // In MD Incognito mode, give the glow a darker value. + if (ui::MaterialDesignController::IsModeMaterial() && themeProvider + && themeProvider->InIncognitoMode()) { + whiteValue = 0.5; + } base::scoped_nsobject<NSGradient> glow([NSGradient alloc]); - [glow initWithStartingColor:[NSColor colorWithCalibratedWhite:1.0 + [glow initWithStartingColor:[NSColor colorWithCalibratedWhite:whiteValue alpha:1.0 * hoverAlpha] - endingColor:[NSColor colorWithCalibratedWhite:1.0 + endingColor:[NSColor colorWithCalibratedWhite:whiteValue alpha:0.0]]; NSRect rect = [self bounds]; NSPoint point = hoverPoint_; @@ -388,8 +467,17 @@ ui::ThreePartImage& GetStrokeImage(bool active) { // Draws the tab outline. - (void)drawStroke:(NSRect)dirtyRect { CGFloat alpha = [[self window] isMainWindow] ? 1.0 : tabs::kImageNoFocusAlpha; - GetStrokeImage(state_ == NSOnState) - .DrawInRect([self bounds], NSCompositeSourceOver, alpha); + NSRect bounds = [self bounds]; + if (ui::MaterialDesignController::IsModeMaterial()) { + // In Material Design the tab strip separator is always 1 pixel high - + // add a clip rect to avoid drawing the tab edge over it. + NSRect clipRect = bounds; + clipRect.origin.y += [self cr_lineWidth]; + NSRectClip(clipRect); + // In MD, the tab stroke is always opaque. + alpha = 1; + } + GetStrokeImage().DrawInRect(bounds, NSCompositeSourceOver, alpha); } - (void)drawRect:(NSRect)dirtyRect { @@ -435,6 +523,10 @@ ui::ThreePartImage& GetStrokeImage(bool active) { } } +- (BOOL)isActiveTab { + return [controller_ active]; +} + - (NSString*)title { return [titleView_ stringValue]; } @@ -711,3 +803,135 @@ ui::ThreePartImage& GetStrokeImage(bool active) { } @end // @implementation TabView(Private) + + +@implementation TabView(MaterialDesign) + ++ (NSBezierPath*)tabLeftEdgeBezierPathForContext:(CGContextRef)context { + NSBezierPath* bezierPath = [NSBezierPath bezierPath]; + + [bezierPath moveToPoint:NSMakePoint(-2, 0)]; + [bezierPath curveToPoint:NSMakePoint(2.5, 2) + controlPoint1:NSMakePoint(1.805, -0.38) + controlPoint2:NSMakePoint(2.17, 1.415)]; + + [bezierPath lineToPoint:NSMakePoint(14, 27)]; + [bezierPath curveToPoint:NSMakePoint(16, 29) + controlPoint1:NSMakePoint(14.25, 27.25) + controlPoint2:NSMakePoint(14.747467, 29.118899)]; + + [bezierPath lineToPoint:NSMakePoint(18, 29)]; + + if (!context) { + return bezierPath; + } + + // The line width is always 1px. + CGFloat lineWidth = LineWidthFromContext(context); + [bezierPath setLineWidth:lineWidth]; + + // Screen pixels lay between integral coordinates in user space. If you draw + // a line from (16, 29) to (18, 29), Core Graphics maps that line to the + // pixels that lay along y=28.5. In order to achieve a line that appears to + // along y=29, CG will perform dithering. To get a crisp line, you have to + // specify y=28.5. Translating the bezier path by the 1-pixel line width + // creates the crisp line we want. + // On a Retina display, there are pixels at y=28.25 and y=28.75, so + // translating the path down by one line width lights up the pixels at 28.75 + // and leaves a gap along y=28.25. To fix this for the general case we'll + // translate up from 28 by one line width. + NSAffineTransform* translationTransform = [NSAffineTransform transform]; + [translationTransform translateXBy:0 yBy:-1 + lineWidth / 2.]; + [bezierPath transformUsingAffineTransform:translationTransform]; + + return bezierPath; +} + ++ (void)setTabEdgeStrokeColor { + static NSColor* strokeColor = + [skia::SkColorToCalibratedNSColor(SkColorSetARGB(76, 0, 0, 0)) retain]; + [strokeColor set]; +} + ++ (void)drawTabLeftEdgeImage { + CGContextRef context = static_cast<CGContextRef>( + [[NSGraphicsContext currentContext] graphicsPort]); + + [TabView setTabEdgeStrokeColor]; + [[self tabLeftEdgeBezierPathForContext:context] stroke]; +} + ++ (void)drawTabMiddleEdgeImage { + NSBezierPath* middleEdgePath = [NSBezierPath bezierPath]; + [middleEdgePath moveToPoint:NSMakePoint(0, 29)]; + [middleEdgePath lineToPoint:NSMakePoint(1, 29)]; + [middleEdgePath setLineCapStyle:NSSquareLineCapStyle]; + + CGContextRef context = static_cast<CGContextRef>( + [[NSGraphicsContext currentContext] graphicsPort]); + CGFloat lineWidth = LineWidthFromContext(context); + + // Line width is always 1px. + [middleEdgePath setLineWidth:lineWidth]; + + // Align to device pixels. + NSAffineTransform* translationTransform = [NSAffineTransform transform]; + [translationTransform translateXBy:0 yBy:-1 + lineWidth / 2.]; + [middleEdgePath transformUsingAffineTransform:translationTransform]; + + [TabView setTabEdgeStrokeColor]; + [middleEdgePath stroke]; +} + ++ (void)drawTabRightEdgeImage { + CGContextRef context = static_cast<CGContextRef>( + [[NSGraphicsContext currentContext] graphicsPort]); + + NSBezierPath* leftEdgePath = [self tabLeftEdgeBezierPathForContext:context]; + + // Draw the right edge path by flipping the left edge path vertically. + NSAffineTransform* transform = [NSAffineTransform transform]; + [transform scaleXBy:-1 yBy:1]; + [transform translateXBy:-18 yBy:0]; + [leftEdgePath transformUsingAffineTransform:transform]; + + [TabView setTabEdgeStrokeColor]; + [leftEdgePath stroke]; +} + ++ (NSBezierPath*)tabLeftMaskBezierPath { + NSBezierPath* bezierPath = [self tabLeftEdgeBezierPathForContext:nullptr]; + + // Box in the open edges. + [bezierPath lineToPoint:NSMakePoint(18, 0)]; + [bezierPath lineToPoint:NSMakePoint(0, 0)]; + + [bezierPath closePath]; + + return bezierPath; +} + ++ (void)drawTabLeftMaskImage { + NSBezierPath* bezierPath = [self tabLeftMaskBezierPath]; + NSAffineTransform* translationTransform = [NSAffineTransform transform]; + [translationTransform translateXBy:0.5 yBy:-0.25]; + [bezierPath transformUsingAffineTransform:translationTransform]; + + [[NSColor whiteColor] set]; + [bezierPath fill]; +} + ++ (void)drawTabRightMaskImage { + // Create the right mask image by flipping the left mask path along the + // vettical axis. + NSBezierPath* bezierPath = [self tabLeftMaskBezierPath]; + NSAffineTransform* transform = [NSAffineTransform transform]; + [transform scaleXBy:-1 yBy:1]; + [transform translateXBy:-17.5 yBy:-0.25]; + [bezierPath transformUsingAffineTransform:transform]; + + [[NSColor whiteColor] set]; + [bezierPath fill]; +} + +@end // @implementation TabView(MaterialDesign) diff --git a/chrome/browser/ui/cocoa/tabs/tab_window_controller.mm b/chrome/browser/ui/cocoa/tabs/tab_window_controller.mm index 6c9dbbb..db17dac 100644 --- a/chrome/browser/ui/cocoa/tabs/tab_window_controller.mm +++ b/chrome/browser/ui/cocoa/tabs/tab_window_controller.mm @@ -5,13 +5,16 @@ #import "chrome/browser/ui/cocoa/tabs/tab_window_controller.h" #include "base/logging.h" +#import "base/mac/sdk_forward_declarations.h" #import "chrome/browser/ui/cocoa/browser_window_layout.h" #import "chrome/browser/ui/cocoa/fast_resize_view.h" #import "chrome/browser/ui/cocoa/framed_browser_window.h" #import "chrome/browser/ui/cocoa/tabs/tab_strip_background_view.h" #import "chrome/browser/ui/cocoa/tabs/tab_strip_view.h" #import "chrome/browser/ui/cocoa/themed_window.h" +#include "grit/theme_resources.h" #import "ui/base/cocoa/focus_tracker.h" +#include "ui/base/material_design/material_design_controller.h" #include "ui/base/theme_provider.h" @interface TabWindowController () @@ -330,9 +333,49 @@ - (void)insertTabStripBackgroundViewIntoWindow:(NSWindow*)window { DCHECK(tabStripBackgroundView_); NSView* rootView = [[window contentView] superview]; - [rootView addSubview:tabStripBackgroundView_ + + // In Material Design on 10.10 and higher, the top portion of the window is + // blurred using an NSVisualEffectView. + Class nsVisualEffectViewClass = NSClassFromString(@"NSVisualEffectView"); + if (!ui::MaterialDesignController::IsModeMaterial() || + !nsVisualEffectViewClass) { + [rootView addSubview:tabStripBackgroundView_ + positioned:NSWindowBelow + relativeTo:nil]; + return; + } + + base::scoped_nsobject<NSVisualEffectView> visualEffectView( + [[nsVisualEffectViewClass alloc] + initWithFrame:[tabStripBackgroundView_ frame]]); + DCHECK(visualEffectView); + + [visualEffectView setAutoresizingMask: + [tabStripBackgroundView_ autoresizingMask]]; + [tabStripBackgroundView_ + setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; + + // Set to a default appearance and material. If this is an Incognito window + // the material and vibrancy should be dark but this method gets called at + // the start of -[BrowserWindowController initWithBrowser:takeOwnership:], + // before the |browser_| ivar has been set. Without a browser object we + // can't check the window's theme. The final setup happens in + // -[TabStripView setController:], at which point we have access to the theme. + [visualEffectView setAppearance: + [NSAppearance appearanceNamed:NSAppearanceNameVibrantLight]]; + [visualEffectView setMaterial:NSVisualEffectMaterialLight]; + [visualEffectView setBlendingMode:NSVisualEffectBlendingModeBehindWindow]; + [visualEffectView setState:NSVisualEffectStateFollowsWindowActiveState]; + + [window setTitlebarAppearsTransparent:YES]; + + [rootView addSubview:visualEffectView positioned:NSWindowBelow relativeTo:nil]; + + // Make the |tabStripBackgroundView_| a child of the NSVisualEffectView. + [tabStripBackgroundView_ setFrame:[visualEffectView bounds]]; + [visualEffectView addSubview:tabStripBackgroundView_]; } // Called when the size of the window content area has changed. Override to diff --git a/ui/base/cocoa/three_part_image.h b/ui/base/cocoa/three_part_image.h index ebeef5f..72da5ca 100644 --- a/ui/base/cocoa/three_part_image.h +++ b/ui/base/cocoa/three_part_image.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef UI_BASE_COCOA_THREE_PART_IMAGE_H -#define UI_BASE_COCOA_THREE_PART_IMAGE_H +#ifndef UI_BASE_COCOA_THREE_PART_IMAGE_H_ +#define UI_BASE_COCOA_THREE_PART_IMAGE_H_ #import <Cocoa/Cocoa.h> @@ -18,9 +18,9 @@ namespace ui { // Vertical orientation is not currently supported. class UI_BASE_EXPORT ThreePartImage { public: - // |left_id|, |middle_id|, and |right_id| are ResourceBundle image - // identifiers. Specify 0 for |middle_id| if there is no middle image. - ThreePartImage(int left_id, int middle_id, int right_id); + // Create a ThreePartImage from existing NSImages. Specify nil for |middle| if + // there is no middle image. + ThreePartImage(NSImage* left, NSImage* middle, NSImage* right); ~ThreePartImage(); // Returns the image rects if drawn in |bounds|. @@ -51,4 +51,4 @@ class UI_BASE_EXPORT ThreePartImage { } // namespace ui -#endif // UI_BASE_COCOA_THREE_PART_IMAGE_H +#endif // UI_BASE_COCOA_THREE_PART_IMAGE_H_ diff --git a/ui/base/cocoa/three_part_image.mm b/ui/base/cocoa/three_part_image.mm index 5fb8916..18ec2dd 100644 --- a/ui/base/cocoa/three_part_image.mm +++ b/ui/base/cocoa/three_part_image.mm @@ -9,17 +9,17 @@ namespace ui { -ThreePartImage::ThreePartImage(int left_id, int middle_id, int right_id) { - DCHECK(left_id); - DCHECK(right_id); - ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); - leftImage_.reset(rb.GetNativeImageNamed(left_id).CopyNSImage()); - rightImage_.reset(rb.GetNativeImageNamed(right_id).CopyNSImage()); +ThreePartImage::ThreePartImage(NSImage* left, NSImage* middle, NSImage* right) { + DCHECK(left); + DCHECK(right); + leftImage_.reset([left retain]); + rightImage_.reset([right retain]); leftSize_ = [leftImage_ size]; rightSize_ = [rightImage_ size]; - if (middle_id) - middleImage_.reset(rb.GetNativeImageNamed(middle_id).CopyNSImage()); + if (middle) { + middleImage_.reset([middle retain]); + } } ThreePartImage::~ThreePartImage() { diff --git a/ui/base/cocoa/three_part_image_unittest.mm b/ui/base/cocoa/three_part_image_unittest.mm index 43db298..efccbef 100644 --- a/ui/base/cocoa/three_part_image_unittest.mm +++ b/ui/base/cocoa/three_part_image_unittest.mm @@ -6,6 +6,7 @@ #include "base/memory/scoped_ptr.h" #include "testing/gtest_mac.h" +#include "ui/base/resource/resource_bundle.h" #import "ui/gfx/test/ui_cocoa_test_helper.h" #include "ui/resources/grit/ui_resources.h" @@ -13,9 +14,14 @@ namespace ui { namespace test { TEST(ThreePartImageTest, GetRects) { - ThreePartImage image(IDR_BROWSER_ACTION_BADGE_LEFT, - IDR_BROWSER_ACTION_BADGE_CENTER, - IDR_BROWSER_ACTION_BADGE_RIGHT); + ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); + base::scoped_nsobject<NSImage> leftImage( + rb.GetNativeImageNamed(IDR_BROWSER_ACTION_BADGE_LEFT).CopyNSImage()); + base::scoped_nsobject<NSImage> middleImage( + rb.GetNativeImageNamed(IDR_BROWSER_ACTION_BADGE_CENTER).CopyNSImage()); + base::scoped_nsobject<NSImage> rightImage( + rb.GetNativeImageNamed(IDR_BROWSER_ACTION_BADGE_RIGHT).CopyNSImage()); + ThreePartImage image(leftImage, middleImage, rightImage); NSRect bounds = NSMakeRect(0, 0, 20, 11); EXPECT_NSRECT_EQ(NSMakeRect(0, 0, 4, 11), image.GetLeftRect(bounds)); EXPECT_NSRECT_EQ(NSMakeRect(4, 0, 12, 11), image.GetMiddleRect(bounds)); @@ -23,9 +29,12 @@ TEST(ThreePartImageTest, GetRects) { } TEST(ThreePartImageTest, GetRectsWithoutMiddle) { - ThreePartImage image(IDR_BROWSER_ACTION_BADGE_LEFT, - 0, - IDR_BROWSER_ACTION_BADGE_RIGHT); + ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); + base::scoped_nsobject<NSImage> leftImage( + rb.GetNativeImageNamed(IDR_BROWSER_ACTION_BADGE_LEFT).CopyNSImage()); + base::scoped_nsobject<NSImage> rightImage( + rb.GetNativeImageNamed(IDR_BROWSER_ACTION_BADGE_RIGHT).CopyNSImage()); + ThreePartImage image(leftImage, nullptr, rightImage); NSRect bounds = NSMakeRect(0, 0, 20, 11); EXPECT_NSRECT_EQ(NSMakeRect(0, 0, 4, 11), image.GetLeftRect(bounds)); EXPECT_NSRECT_EQ(NSMakeRect(4, 0, 12, 11), image.GetMiddleRect(bounds)); @@ -33,7 +42,12 @@ TEST(ThreePartImageTest, GetRectsWithoutMiddle) { } TEST(ThreePartImageTest, HitTest) { - ThreePartImage image(IDR_BACK_ARROW, 0, IDR_FORWARD_ARROW); + ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); + base::scoped_nsobject<NSImage> leftImage( + rb.GetNativeImageNamed(IDR_BACK_ARROW).CopyNSImage()); + base::scoped_nsobject<NSImage> rightImage( + rb.GetNativeImageNamed(IDR_FORWARD_ARROW).CopyNSImage()); + ThreePartImage image(leftImage, nullptr, rightImage); NSRect bounds = NSMakeRect(0, 0, 512, 128); // The middle of the arrows are hits. diff --git a/ui/base/default_theme_provider.h b/ui/base/default_theme_provider.h index 35d47b2..91ee8d8 100644 --- a/ui/base/default_theme_provider.h +++ b/ui/base/default_theme_provider.h @@ -35,6 +35,8 @@ class UI_BASE_EXPORT DefaultThemeProvider : public ThemeProvider { #if defined(OS_MACOSX) bool UsingSystemTheme() const override; + bool InIncognitoMode() const override; + bool HasCustomColor(int id) const override; NSImage* GetNSImageNamed(int id) const override; NSColor* GetNSImageColorNamed(int id) const override; NSColor* GetNSColor(int id) const override; diff --git a/ui/base/default_theme_provider_mac.mm b/ui/base/default_theme_provider_mac.mm index 71b0f8e..74acb3b 100644 --- a/ui/base/default_theme_provider_mac.mm +++ b/ui/base/default_theme_provider_mac.mm @@ -14,6 +14,14 @@ bool DefaultThemeProvider::UsingSystemTheme() const { return true; } +bool DefaultThemeProvider::InIncognitoMode() const { + return false; +} + +bool DefaultThemeProvider::HasCustomColor(int id) const { + return false; +} + NSImage* DefaultThemeProvider::GetNSImageNamed(int id) const { return ResourceBundle::GetSharedInstance(). GetNativeImageNamed(id).ToNSImage(); diff --git a/ui/base/theme_provider.h b/ui/base/theme_provider.h index fa86e43..ce98246 100644 --- a/ui/base/theme_provider.h +++ b/ui/base/theme_provider.h @@ -80,9 +80,15 @@ class UI_BASE_EXPORT ThemeProvider { // ThemeProvider, but it's used in many places on OSX. virtual bool UsingSystemTheme() const = 0; + // Returns whether or not theme is in Incognito mode. + virtual bool InIncognitoMode() const = 0; + // Gets the NSImage with the specified |id|. virtual NSImage* GetNSImageNamed(int id) const = 0; + // Returns true if the theme has defined a custom color for color |id|. + virtual bool HasCustomColor(int id) const = 0; + // Gets the NSImage that GetNSImageNamed (above) would return, but returns it // as a pattern color. virtual NSColor* GetNSImageColorNamed(int id) const = 0; |