summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorshrike <shrike@chromium.org>2016-02-29 17:57:30 -0800
committerCommit bot <commit-bot@chromium.org>2016-03-01 01:58:39 +0000
commit945c1250b48a4d5c5a4499069c44053f83a54688 (patch)
treeda126e4baf9412242609982040fbc2780046a55a
parent5121a34129f274edc61a288f73f99b39eb06e1e7 (diff)
downloadchromium_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}
-rw-r--r--base/mac/sdk_forward_declarations.h12
-rw-r--r--chrome/app/theme/theme_resources.grd1
-rw-r--r--chrome/browser/themes/browser_theme_pack.cc2
-rw-r--r--chrome/browser/themes/theme_properties.cc79
-rw-r--r--chrome/browser/themes/theme_properties.h7
-rw-r--r--chrome/browser/themes/theme_service.cc8
-rw-r--r--chrome/browser/themes/theme_service.h9
-rw-r--r--chrome/browser/themes/theme_service_mac.mm112
-rw-r--r--chrome/browser/ui/cocoa/bookmarks/bookmark_bar_controller_unittest.mm2
-rw-r--r--chrome/browser/ui/cocoa/browser_window_utils.mm26
-rw-r--r--chrome/browser/ui/cocoa/download/background_theme.h2
-rw-r--r--chrome/browser/ui/cocoa/download/background_theme.mm10
-rw-r--r--chrome/browser/ui/cocoa/framed_browser_window.mm6
-rw-r--r--chrome/browser/ui/cocoa/hover_close_button.mm72
-rw-r--r--chrome/browser/ui/cocoa/new_tab_button.h11
-rw-r--r--chrome/browser/ui/cocoa/new_tab_button.mm430
-rw-r--r--chrome/browser/ui/cocoa/profiles/avatar_icon_controller.mm25
-rw-r--r--chrome/browser/ui/cocoa/tabs/tab_controller.mm39
-rw-r--r--chrome/browser/ui/cocoa/tabs/tab_strip_controller.mm147
-rw-r--r--chrome/browser/ui/cocoa/tabs/tab_strip_view.mm165
-rw-r--r--chrome/browser/ui/cocoa/tabs/tab_view.h1
-rw-r--r--chrome/browser/ui/cocoa/tabs/tab_view.mm266
-rw-r--r--chrome/browser/ui/cocoa/tabs/tab_window_controller.mm45
-rw-r--r--ui/base/cocoa/three_part_image.h12
-rw-r--r--ui/base/cocoa/three_part_image.mm16
-rw-r--r--ui/base/cocoa/three_part_image_unittest.mm28
-rw-r--r--ui/base/default_theme_provider.h2
-rw-r--r--ui/base/default_theme_provider_mac.mm8
-rw-r--r--ui/base/theme_provider.h6
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;