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

Side by Side 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, 9 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 unified diff | Download patch
OLDNEW
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #import "chrome/browser/ui/cocoa/toolbar/toolbar_button_cocoa.h" 5 #import "chrome/browser/ui/cocoa/toolbar/toolbar_button_cocoa.h"
6 6
7 #include "base/mac/foundation_util.h"
8 #include "base/mac/sdk_forward_declarations.h"
9 #import "chrome/browser/ui/cocoa/image_button_cell.h"
10 #import "chrome/browser/ui/cocoa/themed_window.h"
11 #import "chrome/browser/ui/cocoa/view_id_util.h"
12 #include "skia/ext/skia_utils_mac.h"
13 #import "ui/base/cocoa/nsview_additions.h"
14 #include "ui/base/material_design/material_design_controller.h"
15 #include "ui/base/theme_provider.h"
16 #include "ui/gfx/image/image_skia_util_mac.h"
17 #include "ui/gfx/paint_vector_icon.h"
18
19 namespace {
20
21 // The bounds of toolbar buttons in Material Design.
22 const NSRect kMDButtonBounds = NSMakeRect(0, 0, 28, 28);
23
24 // The size of a toolbar button icon in Material Design. A toolbar button image
25 // consists of a border and background, with a centered icon.
26 const NSSize kMDButtonIconSize = NSMakeSize(16, 16);
27
28 CGFloat LineWidthFromContext(CGContextRef context) {
29 CGRect unitRect = CGRectMake(0.0, 0.0, 1.0, 1.0);
30 CGRect deviceRect = CGContextConvertRectToDeviceSpace(context, unitRect);
31 return 1.0 / deviceRect.size.height;
32 }
33
34 } // namespace
35
36 // An NSCustomImageRep subclass that creates the "three dots" image of the
37 // Material Design browser tools icon.
38 @interface BrowserToolsImageRep : NSCustomImageRep
39 @property (retain, nonatomic) NSColor* fillColor;
40 // NSCustomImageRep delegate method that performs the drawing.
41 + (void)drawBrowserToolsIcon:(BrowserToolsImageRep*)imageRep;
42 @end
43
44 @implementation BrowserToolsImageRep
45
46 @synthesize fillColor = fillColor_;
47
48 - (void)dealloc {
49 [fillColor_ release];
50 [super dealloc];
51 }
52
53 + (void)drawBrowserToolsIcon:(BrowserToolsImageRep*)imageRep {
54 [imageRep.fillColor set];
55 NSBezierPath* dotPath =
56 [NSBezierPath bezierPathWithOvalInRect:NSMakeRect(6.5, 1.5, 3, 3)];
57 CGContextRef context = static_cast<CGContextRef>(
58 [[NSGraphicsContext currentContext] graphicsPort]);
59 // Draw the three dots by drawing |dotPath| in three different locations.
60 for (NSUInteger i = 0; i < 3; i++) {
61 [dotPath fill];
62 CGContextTranslateCTM(context, 0, 5);
63 }
64 }
65
66 @end
67
68 // An NSCustomImageRep subclass that draws a Material Design background behind
69 // and border around a centered icon image.
70 @interface ToolbarButtonImageRep : NSCustomImageRep
71 @property (retain, nonatomic) NSImage* icon;
72 @property (assign, nonatomic) ToolbarButtonImageBackgroundStyle style;
73 // Returns a color containing the gradient used to draw the hover style border.
74 + (NSColor*)hoverBorderGradientColor;
75 // NSCustomImageRep delegate method that performs the drawing.
76 + (void)drawImage:(ToolbarButtonImageRep*)imageRep;
77 @end
78
79 @implementation ToolbarButtonImageRep
80
81 @synthesize icon = icon_;
82 @synthesize style = style_;
83
84 - (void)dealloc {
85 [icon_ release];
86 [super dealloc];
87 }
88
89 + (NSColor*)hoverBorderGradientColor {
90 // Create a temporary image.
91 base::scoped_nsobject<NSImage> tmpImage(
92 [[NSImage alloc] initWithSize:kMDButtonBounds.size]);
93
94 // Set up the gradient.
95 NSColor* startingColor = [NSColor colorWithCalibratedWhite:0 alpha:0.11];
96 NSColor* endingColor = [NSColor colorWithCalibratedWhite:0 alpha:0.31];
97 base::scoped_nsobject<NSGradient> gradient(
98 [[NSGradient alloc] initWithStartingColor:startingColor
99 endingColor:endingColor]);
100
101 // Fill the temporary image with the gradient.
102 [tmpImage lockFocus];
103 [gradient drawInRect:kMDButtonBounds angle:270];
104 [tmpImage unlockFocus];
105
106 // Return the gradient image as a pattern-based color.
107 return [NSColor colorWithPatternImage:tmpImage];
108 }
109
110 + (void)drawImage:(ToolbarButtonImageRep*)imageRep {
111 // Inset the bounds by the line width to get a crisp line.
112 CGContextRef context = static_cast<CGContextRef>(
113 [[NSGraphicsContext currentContext] graphicsPort]);
114 CGFloat lineWidth = LineWidthFromContext(context);
115 NSRect destRect =
116 NSInsetRect(kMDButtonBounds, lineWidth * 1.5, lineWidth * 1.5);
117 // Create the bezier used to draw the border.
118 NSBezierPath* roundedRectPath =
119 [NSBezierPath bezierPathWithRoundedRect:destRect xRadius:3 yRadius:3];
120 [roundedRectPath setLineWidth:lineWidth / 2.];
121
122 // Compute the stroke and fill colors.
123 NSColor* strokeColor = nil;
124 NSColor* fillColor = nil;
125 switch (imageRep.style) {
126 case ToolbarButtonImageBackgroundStyle::HOVER:
127 strokeColor = [self hoverBorderGradientColor];
128 strokeColor = [NSColor grayColor];
129 fillColor = [NSColor colorWithCalibratedWhite:1 alpha:0.9];
130 break;
131 case ToolbarButtonImageBackgroundStyle::HOVER_THEMED:
132 strokeColor = [self hoverBorderGradientColor];
133 fillColor = [NSColor colorWithCalibratedWhite:1 alpha:0.2];
134 case ToolbarButtonImageBackgroundStyle::PRESSED:
135 strokeColor = [NSColor colorWithCalibratedWhite:0 alpha:0.22];
136 fillColor = [NSColor colorWithCalibratedWhite:0 alpha:0.05];
137 break;
138 case ToolbarButtonImageBackgroundStyle::PRESSED_THEMED:
139 strokeColor = [NSColor colorWithCalibratedWhite:0 alpha:0.22];
140 fillColor = [NSColor colorWithCalibratedWhite:0 alpha:0.1];
141 break;
142 }
143
144 // Fill and stroke.
145 [fillColor set];
146 [roundedRectPath fill];
147 [strokeColor set];
148 [roundedRectPath stroke];
149
150 // Compute the icon's location and draw it there.
151 CGFloat iconInset =
152 (kMDButtonBounds.size.width - kMDButtonIconSize.width) / 2;
153 NSRect iconDestRect = NSInsetRect(kMDButtonBounds, iconInset, iconInset);
154 [imageRep.icon drawInRect:iconDestRect
155 fromRect:NSZeroRect
156 operation:NSCompositeSourceOver
157 fraction:1];
158 }
159
160 @end
161
162 @interface ToolbarButton ()
163 // Returns an image that draws the browser tools button icon using vector
164 // commands.
165 - (NSImage*)browserToolsIconForFillColor:(SkColor)fillColor;
166 // Returns an button image by combining |iconImage| with the specified button
167 // background.
168 - (NSImage*)imageForIcon:(NSImage*)iconImage
169 withBackgroundStyle:(ToolbarButtonImageBackgroundStyle)style;
170 // Creates and assigns images for the button's various states (pressed, hover,
171 // etc.).
172 - (void)setImagesFromIconId:(gfx::VectorIconId)iconId;
173 // Implemented to set the button's icon when added to the browser window. We
174 // can't set the image before this because its appearance depends upon the
175 // browser window's theme.
176 - (void)viewDidMoveToWindow;
177
178 @end
179
180
7 @implementation ToolbarButton 181 @implementation ToolbarButton
8 182
9 @synthesize handleMiddleClick = handleMiddleClick_; 183 @synthesize handleMiddleClick = handleMiddleClick_;
10 184
11 - (void)otherMouseDown:(NSEvent*)theEvent { 185 - (void)otherMouseDown:(NSEvent*)theEvent {
12 if (![self shouldHandleEvent:theEvent]) { 186 if (![self shouldHandleEvent:theEvent]) {
13 [super otherMouseDown:theEvent]; 187 [super otherMouseDown:theEvent];
14 return; 188 return;
15 } 189 }
16 190
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
48 return handleMiddleClick_ && [theEvent buttonNumber] == 2; 222 return handleMiddleClick_ && [theEvent buttonNumber] == 2;
49 } 223 }
50 224
51 - (void)drawFocusRingMask { 225 - (void)drawFocusRingMask {
52 // Match the hover image's bezel. 226 // Match the hover image's bezel.
53 [[NSBezierPath bezierPathWithRoundedRect:NSInsetRect([self bounds], 2, 2) 227 [[NSBezierPath bezierPathWithRoundedRect:NSInsetRect([self bounds], 2, 2)
54 xRadius:2 228 xRadius:2
55 yRadius:2] fill]; 229 yRadius:2] fill];
56 } 230 }
57 231
232 - (SkColor)defaultColor:(BOOL)isDarkTheme {
233 const SkColor defaultGrayColor = SkColorSetARGB(0xFF, 0x5A, 0x5A, 0x5A);
234 const SkColor defaultWhiteColor = SkColorSetRGB(0xFF, 0xFF, 0xFF);
235 return isDarkTheme ? defaultWhiteColor : defaultGrayColor;
236 }
237
238 - (SkColor)disabledColor:(BOOL)isDarkTheme {
239 const SkColor disabledGrayColor = SkColorSetARGB(0x33, 0x5A, 0x5A, 0x5A);
240 const SkColor disabledWhiteColor = SkColorSetARGB(0x33, 0xFF, 0xFF, 0xFF);
241 return isDarkTheme ? disabledWhiteColor : disabledGrayColor;
242 }
243
244 - (SkColor)pressedColor:(BOOL)isDarkTheme {
245 const SkColor pressedBlueColor = SkColorSetARGB(0xFF, 0x42, 0x85, 0xF4);
246 const SkColor pressedWhiteColor = SkColorSetARGB(0x7F, 0xFF, 0xFF, 0xFF);
247 return isDarkTheme ? pressedWhiteColor : pressedBlueColor;
248 }
249
250 - (NSImage*)browserToolsIconForFillColor:(SkColor)fillColor {
251 // Create a |BrowserToolsImageRep| to draw the browser tools icon using
252 // the provided fill color.
253 base::scoped_nsobject<BrowserToolsImageRep> imageRep =
254 [[BrowserToolsImageRep alloc]
255 initWithDrawSelector:@selector(drawBrowserToolsIcon:)
256 delegate:[BrowserToolsImageRep class]];
257 [imageRep setFillColor:skia::SkColorToCalibratedNSColor(fillColor)];
258
259 // Create the image from the image rep.
260 NSImage* browserToolsIcon =
261 [[[NSImage alloc] initWithSize:kMDButtonIconSize] autorelease];
262 [browserToolsIcon setCacheMode:NSImageCacheAlways];
263 [browserToolsIcon addRepresentation:imageRep];
264
265 return browserToolsIcon;
266 }
267
268 - (NSImage*)imageForIcon:(NSImage*)iconImage
269 withBackgroundStyle:(ToolbarButtonImageBackgroundStyle)style {
270 // Create a |ToolbarButtonImageRep| to draw the button image using
271 // the provided icon and background style.
272 base::scoped_nsobject<ToolbarButtonImageRep> imageRep =
273 [[ToolbarButtonImageRep alloc]
274 initWithDrawSelector:@selector(drawImage:)
275 delegate:[ToolbarButtonImageRep class]];
276 [imageRep setIcon:iconImage];
277 [imageRep setStyle:style];
278
279 // Create the image from the image rep.
280 NSImage* image =
281 [[[NSImage alloc] initWithSize:kMDButtonBounds.size] autorelease];
282 [image setCacheMode:NSImageCacheAlways];
283 [image addRepresentation:imageRep];
284
285 return image;
286 }
287
288 - (void)setImagesFromIconId:(gfx::VectorIconId)iconId {
289 // Compute the default, disabled, and hover icon colors.
290 BOOL isDarkTheme = [[self window] hasDarkTheme];
291 SkColor defaultColor = [self defaultColor:isDarkTheme];
292 SkColor disabledColor = [self disabledColor:isDarkTheme];
293 SkColor pressedColor = [self pressedColor:isDarkTheme];
294
295 // Create the default, pressed, and disabled state icons. These icons are
296 // always the same shape, but use a different color.
297 NSImage* defaultIcon = nil;
298 NSImage* pressedIcon = nil;
299 NSImage* disabledIcon = nil;
300 if (iconId == gfx::VectorIconId::BROWSER_TOOLS) {
301 defaultIcon = [self browserToolsIconForFillColor:defaultColor];
302 pressedIcon = [self browserToolsIconForFillColor:pressedColor];
303 disabledIcon = [self browserToolsIconForFillColor:pressedColor];
304 } else {
305 defaultIcon = NSImageFromImageSkia(
306 gfx::CreateVectorIcon(iconId, kMDButtonIconSize.width, defaultColor));
307 pressedIcon = NSImageFromImageSkia(
308 gfx::CreateVectorIcon(iconId, kMDButtonIconSize.width, pressedColor));
309 disabledIcon = NSImageFromImageSkia(
310 gfx::CreateVectorIcon(iconId, kMDButtonIconSize.width, disabledColor));
311 }
312
313 ImageButtonCell* theCell = base::mac::ObjCCast<ImageButtonCell>([self cell]);
314 // Set the image for the default state, which is just the icon.
315 [theCell setImage:defaultIcon
316 forButtonState:image_button_cell::kDefaultState];
317
318 // Determine the appropriate image background style for the hover and pressed
319 // states.
320 ToolbarButtonImageBackgroundStyle hoverStyle;
321 ToolbarButtonImageBackgroundStyle pressedStyle;
322 const ui::ThemeProvider* themeProvider = [[self window] themeProvider];
323 bool isNonIncognitoSystemTheme = themeProvider &&
324 !themeProvider->InIncognitoMode() && themeProvider->UsingSystemTheme();
325 // Use the regular style only when using the system (i.e. non-custom) theme
326 // and not in Incognito mode.
327 if (isNonIncognitoSystemTheme) {
328 hoverStyle = ToolbarButtonImageBackgroundStyle::HOVER;
329 pressedStyle = ToolbarButtonImageBackgroundStyle::PRESSED;
330 } else {
331 hoverStyle = ToolbarButtonImageBackgroundStyle::HOVER_THEMED;
332 pressedStyle = ToolbarButtonImageBackgroundStyle::PRESSED_THEMED;
333 }
334
335 // Create and set the image for the hover state.
336 NSImage* hoverImage =
337 [self imageForIcon:defaultIcon withBackgroundStyle:hoverStyle];
338 [theCell setImage:hoverImage
339 forButtonState:image_button_cell::kHoverState];
340
341 // Create and set the image for the pressed state.
342 NSImage* pressedImage =
343 [self imageForIcon:pressedIcon withBackgroundStyle:pressedStyle];
344 [theCell setImage:pressedImage
345 forButtonState:image_button_cell::kPressedState];
346
347 // Set the image for the disabled state, which is just the disabled icon,
348 // except if this is the home button.
349 if (iconId == gfx::VectorIconId::NAVIGATE_RELOAD) {
350 [theCell setImage:nil forButtonState:image_button_cell::kDisabledState];
351 } else {
352 [theCell setImage:disabledIcon
353 forButtonState:image_button_cell::kDisabledState];
354 }
355 [self setNeedsDisplay:YES];
356 }
357
358 - (void)resetIcons {
359 // Compute the button's icon.
360 gfx::VectorIconId icon_id = gfx::VectorIconId::VECTOR_ICON_NONE;
361 switch ([self viewID]) {
362 case VIEW_ID_BACK_BUTTON:
363 icon_id = gfx::VectorIconId::NAVIGATE_BACK;
364 break;
365 case VIEW_ID_FORWARD_BUTTON:
366 icon_id = gfx::VectorIconId::NAVIGATE_FORWARD;
367 break;
368 case VIEW_ID_HOME_BUTTON:
369 icon_id = gfx::VectorIconId::NAVIGATE_HOME;
370 break;
371 case VIEW_ID_APP_MENU:
372 icon_id = gfx::VectorIconId::BROWSER_TOOLS;
373 break;
374 default:
375 break;
376 }
377
378 // Set it.
379 if (icon_id != gfx::VectorIconId::VECTOR_ICON_NONE) {
380 [self setImagesFromIconId:icon_id];
381 }
382 }
383
384 - (void)viewDidMoveToWindow {
385 // In Material Design we want to catch when the button is attached to its
386 // window so that we can configure its appearance based on the window's
387 // theme.
388 if ([self window] && ui::MaterialDesignController::IsModeMaterial()) {
389 [self resetIcons];
390 }
391 }
392
393 // ThemedWindowDrawing implementation.
394
395 - (void)windowDidChangeTheme {
396 [self resetIcons];
397 }
398
399 - (void)windowDidChangeActive {
400 }
401
58 @end 402 @end
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698