Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1001)

Unified Diff: chrome/browser/ui/cocoa/toolbar/toolbar_button_cocoa.mm

Issue 1680773006: Implement Material Design for Mac toolbar. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@md_master
Patch Set: Ready for review. Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/ui/cocoa/toolbar/toolbar_button_cocoa.mm
diff --git a/chrome/browser/ui/cocoa/toolbar/toolbar_button_cocoa.mm b/chrome/browser/ui/cocoa/toolbar/toolbar_button_cocoa.mm
index d8c3acc5b34c3984d33bcc6cc0fad7ff4524aac1..b6d3282dd7bde405d26a0c53719e654b013ec677 100644
--- a/chrome/browser/ui/cocoa/toolbar/toolbar_button_cocoa.mm
+++ b/chrome/browser/ui/cocoa/toolbar/toolbar_button_cocoa.mm
@@ -4,6 +4,180 @@
#import "chrome/browser/ui/cocoa/toolbar/toolbar_button_cocoa.h"
+#include "base/mac/foundation_util.h"
+#include "base/mac/sdk_forward_declarations.h"
+#import "chrome/browser/ui/cocoa/image_button_cell.h"
+#import "chrome/browser/ui/cocoa/themed_window.h"
+#import "chrome/browser/ui/cocoa/view_id_util.h"
+#include "skia/ext/skia_utils_mac.h"
+#import "ui/base/cocoa/nsview_additions.h"
+#include "ui/base/material_design/material_design_controller.h"
+#include "ui/base/theme_provider.h"
+#include "ui/gfx/image/image_skia_util_mac.h"
+#include "ui/gfx/paint_vector_icon.h"
+
+namespace {
+
+// The bounds of toolbar buttons in Material Design.
+const NSRect kMDButtonBounds = NSMakeRect(0, 0, 28, 28);
+
+// The size of a toolbar button icon in Material Design. A toolbar button image
+// consists of a border and background, with a centered icon.
+const NSSize kMDButtonIconSize = NSMakeSize(16, 16);
+
+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
+
+// An NSCustomImageRep subclass that creates the "three dots" image of the
+// Material Design browser tools icon.
+@interface BrowserToolsImageRep : NSCustomImageRep
+@property (retain, nonatomic) NSColor* fillColor;
+// NSCustomImageRep delegate method that performs the drawing.
++ (void)drawBrowserToolsIcon:(BrowserToolsImageRep*)imageRep;
+@end
+
+@implementation BrowserToolsImageRep
+
+@synthesize fillColor = fillColor_;
+
+- (void)dealloc {
+ [fillColor_ release];
+ [super dealloc];
+}
+
++ (void)drawBrowserToolsIcon:(BrowserToolsImageRep*)imageRep {
+ [imageRep.fillColor set];
+ NSBezierPath* dotPath =
+ [NSBezierPath bezierPathWithOvalInRect:NSMakeRect(6.5, 1.5, 3, 3)];
+ CGContextRef context = static_cast<CGContextRef>(
+ [[NSGraphicsContext currentContext] graphicsPort]);
+ // Draw the three dots by drawing |dotPath| in three different locations.
+ for (NSUInteger i = 0; i < 3; i++) {
+ [dotPath fill];
+ CGContextTranslateCTM(context, 0, 5);
+ }
+}
+
+@end
+
+// An NSCustomImageRep subclass that draws a Material Design background behind
+// and border around a centered icon image.
+@interface ToolbarButtonImageRep : NSCustomImageRep
+@property (retain, nonatomic) NSImage* icon;
+@property (assign, nonatomic) ToolbarButtonImageBackgroundStyle style;
+// Returns a color containing the gradient used to draw the hover style border.
++ (NSColor*)hoverBorderGradientColor;
+// NSCustomImageRep delegate method that performs the drawing.
++ (void)drawImage:(ToolbarButtonImageRep*)imageRep;
+@end
+
+@implementation ToolbarButtonImageRep
+
+@synthesize icon = icon_;
+@synthesize style = style_;
+
+- (void)dealloc {
+ [icon_ release];
+ [super dealloc];
+}
+
++ (NSColor*)hoverBorderGradientColor {
+ // Create a temporary image.
+ base::scoped_nsobject<NSImage> tmpImage(
+ [[NSImage alloc] initWithSize:kMDButtonBounds.size]);
+
+ // Set up the gradient.
+ NSColor* startingColor = [NSColor colorWithCalibratedWhite:0 alpha:0.11];
+ NSColor* endingColor = [NSColor colorWithCalibratedWhite:0 alpha:0.31];
+ base::scoped_nsobject<NSGradient> gradient(
+ [[NSGradient alloc] initWithStartingColor:startingColor
+ endingColor:endingColor]);
+
+ // Fill the temporary image with the gradient.
+ [tmpImage lockFocus];
+ [gradient drawInRect:kMDButtonBounds angle:270];
+ [tmpImage unlockFocus];
+
+ // Return the gradient image as a pattern-based color.
+ return [NSColor colorWithPatternImage:tmpImage];
+}
+
++ (void)drawImage:(ToolbarButtonImageRep*)imageRep {
+ // Inset the bounds by the line width to get a crisp line.
+ CGContextRef context = static_cast<CGContextRef>(
+ [[NSGraphicsContext currentContext] graphicsPort]);
+ CGFloat lineWidth = LineWidthFromContext(context);
+ NSRect destRect =
+ NSInsetRect(kMDButtonBounds, lineWidth * 1.5, lineWidth * 1.5);
+ // Create the bezier used to draw the border.
+ NSBezierPath* roundedRectPath =
+ [NSBezierPath bezierPathWithRoundedRect:destRect xRadius:3 yRadius:3];
+ [roundedRectPath setLineWidth:lineWidth / 2.];
+
+ // Compute the stroke and fill colors.
+ NSColor* strokeColor = nil;
+ NSColor* fillColor = nil;
+ switch (imageRep.style) {
+ case ToolbarButtonImageBackgroundStyle::HOVER:
+ strokeColor = [self hoverBorderGradientColor];
+ strokeColor = [NSColor grayColor];
+ fillColor = [NSColor colorWithCalibratedWhite:1 alpha:0.9];
+ break;
+ case ToolbarButtonImageBackgroundStyle::HOVER_THEMED:
+ strokeColor = [self hoverBorderGradientColor];
+ fillColor = [NSColor colorWithCalibratedWhite:1 alpha:0.2];
+ case ToolbarButtonImageBackgroundStyle::PRESSED:
+ strokeColor = [NSColor colorWithCalibratedWhite:0 alpha:0.22];
+ fillColor = [NSColor colorWithCalibratedWhite:0 alpha:0.05];
+ break;
+ case ToolbarButtonImageBackgroundStyle::PRESSED_THEMED:
+ strokeColor = [NSColor colorWithCalibratedWhite:0 alpha:0.22];
+ fillColor = [NSColor colorWithCalibratedWhite:0 alpha:0.1];
+ break;
+ }
+
+ // Fill and stroke.
+ [fillColor set];
+ [roundedRectPath fill];
+ [strokeColor set];
+ [roundedRectPath stroke];
+
+ // Compute the icon's location and draw it there.
+ CGFloat iconInset =
+ (kMDButtonBounds.size.width - kMDButtonIconSize.width) / 2;
+ NSRect iconDestRect = NSInsetRect(kMDButtonBounds, iconInset, iconInset);
+ [imageRep.icon drawInRect:iconDestRect
+ fromRect:NSZeroRect
+ operation:NSCompositeSourceOver
+ fraction:1];
+}
+
+@end
+
+@interface ToolbarButton ()
+// Returns an image that draws the browser tools button icon using vector
+// commands.
+- (NSImage*)browserToolsIconForFillColor:(SkColor)fillColor;
+// Returns an button image by combining |iconImage| with the specified button
+// background.
+- (NSImage*)imageForIcon:(NSImage*)iconImage
+ withBackgroundStyle:(ToolbarButtonImageBackgroundStyle)style;
+// Creates and assigns images for the button's various states (pressed, hover,
+// etc.).
+- (void)setImagesFromIconId:(gfx::VectorIconId)iconId;
+// Implemented to set the button's icon when added to the browser window. We
+// can't set the image before this because its appearance depends upon the
+// browser window's theme.
+- (void)viewDidMoveToWindow;
+
+@end
+
+
@implementation ToolbarButton
@synthesize handleMiddleClick = handleMiddleClick_;
@@ -55,4 +229,174 @@
yRadius:2] fill];
}
+- (SkColor)defaultColor:(BOOL)isDarkTheme {
+ const SkColor defaultGrayColor = SkColorSetARGB(0xFF, 0x5A, 0x5A, 0x5A);
+ const SkColor defaultWhiteColor = SkColorSetRGB(0xFF, 0xFF, 0xFF);
+ return isDarkTheme ? defaultWhiteColor : defaultGrayColor;
+}
+
+- (SkColor)disabledColor:(BOOL)isDarkTheme {
+ const SkColor disabledGrayColor = SkColorSetARGB(0x33, 0x5A, 0x5A, 0x5A);
+ const SkColor disabledWhiteColor = SkColorSetARGB(0x33, 0xFF, 0xFF, 0xFF);
+ return isDarkTheme ? disabledWhiteColor : disabledGrayColor;
+}
+
+- (SkColor)pressedColor:(BOOL)isDarkTheme {
+ const SkColor pressedBlueColor = SkColorSetARGB(0xFF, 0x42, 0x85, 0xF4);
+ const SkColor pressedWhiteColor = SkColorSetARGB(0x7F, 0xFF, 0xFF, 0xFF);
+ return isDarkTheme ? pressedWhiteColor : pressedBlueColor;
+}
+
+- (NSImage*)browserToolsIconForFillColor:(SkColor)fillColor {
+ // Create a |BrowserToolsImageRep| to draw the browser tools icon using
+ // the provided fill color.
+ base::scoped_nsobject<BrowserToolsImageRep> imageRep =
+ [[BrowserToolsImageRep alloc]
+ initWithDrawSelector:@selector(drawBrowserToolsIcon:)
+ delegate:[BrowserToolsImageRep class]];
+ [imageRep setFillColor:skia::SkColorToCalibratedNSColor(fillColor)];
+
+ // Create the image from the image rep.
+ NSImage* browserToolsIcon =
+ [[[NSImage alloc] initWithSize:kMDButtonIconSize] autorelease];
+ [browserToolsIcon setCacheMode:NSImageCacheAlways];
+ [browserToolsIcon addRepresentation:imageRep];
+
+ return browserToolsIcon;
+}
+
+- (NSImage*)imageForIcon:(NSImage*)iconImage
+ withBackgroundStyle:(ToolbarButtonImageBackgroundStyle)style {
+ // Create a |ToolbarButtonImageRep| to draw the button image using
+ // the provided icon and background style.
+ base::scoped_nsobject<ToolbarButtonImageRep> imageRep =
+ [[ToolbarButtonImageRep alloc]
+ initWithDrawSelector:@selector(drawImage:)
+ delegate:[ToolbarButtonImageRep class]];
+ [imageRep setIcon:iconImage];
+ [imageRep setStyle:style];
+
+ // Create the image from the image rep.
+ NSImage* image =
+ [[[NSImage alloc] initWithSize:kMDButtonBounds.size] autorelease];
+ [image setCacheMode:NSImageCacheAlways];
+ [image addRepresentation:imageRep];
+
+ return image;
+}
+
+- (void)setImagesFromIconId:(gfx::VectorIconId)iconId {
+ // Compute the default, disabled, and hover icon colors.
+ BOOL isDarkTheme = [[self window] hasDarkTheme];
+ SkColor defaultColor = [self defaultColor:isDarkTheme];
+ SkColor disabledColor = [self disabledColor:isDarkTheme];
+ SkColor pressedColor = [self pressedColor:isDarkTheme];
+
+ // Create the default, pressed, and disabled state icons. These icons are
+ // always the same shape, but use a different color.
+ NSImage* defaultIcon = nil;
+ NSImage* pressedIcon = nil;
+ NSImage* disabledIcon = nil;
+ if (iconId == gfx::VectorIconId::BROWSER_TOOLS) {
+ defaultIcon = [self browserToolsIconForFillColor:defaultColor];
+ pressedIcon = [self browserToolsIconForFillColor:pressedColor];
+ disabledIcon = [self browserToolsIconForFillColor:pressedColor];
+ } else {
+ defaultIcon = NSImageFromImageSkia(
+ gfx::CreateVectorIcon(iconId, kMDButtonIconSize.width, defaultColor));
+ pressedIcon = NSImageFromImageSkia(
+ gfx::CreateVectorIcon(iconId, kMDButtonIconSize.width, pressedColor));
+ disabledIcon = NSImageFromImageSkia(
+ gfx::CreateVectorIcon(iconId, kMDButtonIconSize.width, disabledColor));
+ }
+
+ ImageButtonCell* theCell = base::mac::ObjCCast<ImageButtonCell>([self cell]);
+ // Set the image for the default state, which is just the icon.
+ [theCell setImage:defaultIcon
+ forButtonState:image_button_cell::kDefaultState];
+
+ // Determine the appropriate image background style for the hover and pressed
+ // states.
+ ToolbarButtonImageBackgroundStyle hoverStyle;
+ ToolbarButtonImageBackgroundStyle pressedStyle;
+ const ui::ThemeProvider* themeProvider = [[self window] themeProvider];
+ bool isNonIncognitoSystemTheme = themeProvider &&
+ !themeProvider->InIncognitoMode() && themeProvider->UsingSystemTheme();
+ // Use the regular style only when using the system (i.e. non-custom) theme
+ // and not in Incognito mode.
+ if (isNonIncognitoSystemTheme) {
+ hoverStyle = ToolbarButtonImageBackgroundStyle::HOVER;
+ pressedStyle = ToolbarButtonImageBackgroundStyle::PRESSED;
+ } else {
+ hoverStyle = ToolbarButtonImageBackgroundStyle::HOVER_THEMED;
+ pressedStyle = ToolbarButtonImageBackgroundStyle::PRESSED_THEMED;
+ }
+
+ // Create and set the image for the hover state.
+ NSImage* hoverImage =
+ [self imageForIcon:defaultIcon withBackgroundStyle:hoverStyle];
+ [theCell setImage:hoverImage
+ forButtonState:image_button_cell::kHoverState];
+
+ // Create and set the image for the pressed state.
+ NSImage* pressedImage =
+ [self imageForIcon:pressedIcon withBackgroundStyle:pressedStyle];
+ [theCell setImage:pressedImage
+ forButtonState:image_button_cell::kPressedState];
+
+ // Set the image for the disabled state, which is just the disabled icon,
+ // except if this is the home button.
+ if (iconId == gfx::VectorIconId::NAVIGATE_RELOAD) {
+ [theCell setImage:nil forButtonState:image_button_cell::kDisabledState];
+ } else {
+ [theCell setImage:disabledIcon
+ forButtonState:image_button_cell::kDisabledState];
+ }
+ [self setNeedsDisplay:YES];
+}
+
+- (void)resetIcons {
+ // Compute the button's icon.
+ gfx::VectorIconId icon_id = gfx::VectorIconId::VECTOR_ICON_NONE;
+ switch ([self viewID]) {
+ case VIEW_ID_BACK_BUTTON:
+ icon_id = gfx::VectorIconId::NAVIGATE_BACK;
+ break;
+ case VIEW_ID_FORWARD_BUTTON:
+ icon_id = gfx::VectorIconId::NAVIGATE_FORWARD;
+ break;
+ case VIEW_ID_HOME_BUTTON:
+ icon_id = gfx::VectorIconId::NAVIGATE_HOME;
+ break;
+ case VIEW_ID_APP_MENU:
+ icon_id = gfx::VectorIconId::BROWSER_TOOLS;
+ break;
+ default:
+ break;
+ }
+
+ // Set it.
+ if (icon_id != gfx::VectorIconId::VECTOR_ICON_NONE) {
+ [self setImagesFromIconId:icon_id];
+ }
+}
+
+- (void)viewDidMoveToWindow {
+ // In Material Design we want to catch when the button is attached to its
+ // window so that we can configure its appearance based on the window's
+ // theme.
+ if ([self window] && ui::MaterialDesignController::IsModeMaterial()) {
+ [self resetIcons];
+ }
+}
+
+// ThemedWindowDrawing implementation.
+
+- (void)windowDidChangeTheme {
+ [self resetIcons];
+}
+
+- (void)windowDidChangeActive {
+}
+
@end

Powered by Google App Engine
This is Rietveld 408576698