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

Side by Side Diff: chrome/browser/ui/cocoa/gradient_button_cell.mm

Issue 1987893002: Reland of [Mac][Material Design] Update bookmarks bar to Material Design. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 7 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
« no previous file with comments | « chrome/browser/ui/cocoa/gradient_button_cell.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 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 #include "chrome/browser/ui/cocoa/gradient_button_cell.h" 5 #include "chrome/browser/ui/cocoa/gradient_button_cell.h"
6 6
7 #include <cmath> 7 #include <cmath>
8 8
9 #include "base/logging.h" 9 #include "base/logging.h"
10 #import "base/mac/scoped_nsobject.h" 10 #import "base/mac/scoped_nsobject.h"
(...skipping 188 matching lines...) Expand 10 before | Expand all | Expand 10 after
199 YES : NO; 199 YES : NO;
200 } 200 }
201 201
202 #if 1 202 #if 1
203 // If we are not continuously pulsing, perform a pulse animation to 203 // If we are not continuously pulsing, perform a pulse animation to
204 // reflect our new state. 204 // reflect our new state.
205 - (void)setMouseInside:(BOOL)flag animate:(BOOL)animated { 205 - (void)setMouseInside:(BOOL)flag animate:(BOOL)animated {
206 isMouseInside_ = flag; 206 isMouseInside_ = flag;
207 if (pulseState_ != gradient_button_cell::kPulsingContinuous) { 207 if (pulseState_ != gradient_button_cell::kPulsingContinuous) {
208 if (animated) { 208 if (animated) {
209 // In Material Design, if the button is already fully on, don't pulse it
210 // on again if the mouse is within its bounds.
211 if ([self tag] == kMaterialStandardButtonTypeWithLimitedClickFeedback &&
212 isMouseInside_ && pulseState_ == gradient_button_cell::kPulsedOn) {
213 return;
214 }
215
209 [self setPulseState:(isMouseInside_ ? gradient_button_cell::kPulsingOn : 216 [self setPulseState:(isMouseInside_ ? gradient_button_cell::kPulsingOn :
210 gradient_button_cell::kPulsingOff)]; 217 gradient_button_cell::kPulsingOff)];
211 } else { 218 } else {
212 [self setPulseState:(isMouseInside_ ? gradient_button_cell::kPulsedOn : 219 [self setPulseState:(isMouseInside_ ? gradient_button_cell::kPulsedOn :
213 gradient_button_cell::kPulsedOff)]; 220 gradient_button_cell::kPulsedOff)];
214 } 221 }
215 } 222 }
216 } 223 }
217 #else 224 #else
218 225
(...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after
312 } 319 }
313 320
314 - (void)mouseExited:(NSEvent*)theEvent { 321 - (void)mouseExited:(NSEvent*)theEvent {
315 [self setMouseInside:NO animate:YES]; 322 [self setMouseInside:NO animate:YES];
316 } 323 }
317 324
318 - (BOOL)isMouseInside { 325 - (BOOL)isMouseInside {
319 return trackingArea_ && isMouseInside_; 326 return trackingArea_ && isMouseInside_;
320 } 327 }
321 328
329 - (BOOL)startTrackingAt:(NSPoint)startPoint inView:(NSView *)controlView {
330 if ([self tag] == kMaterialStandardButtonTypeWithLimitedClickFeedback) {
331 // The user has just clicked down in the button. In Material Design, set the
332 // pulsed (hover) state to off now so that if the user keeps the mouse held
333 // down while dragging it out of the button's bounds, the button will draw
334 // itself in its normal state. This is unrelated to dragging the button
335 // in the button bar, which takes a different path through the code.
336 [self setPulseState:gradient_button_cell::kPulsedOff];
337 }
338
339 return [super startTrackingAt:startPoint inView:controlView];
340 }
341
322 // Since we have our own drawWithFrame:, we need to also have our own 342 // Since we have our own drawWithFrame:, we need to also have our own
323 // logic for determining when the mouse is inside for honoring this 343 // logic for determining when the mouse is inside for honoring this
324 // request. 344 // request.
325 - (void)setShowsBorderOnlyWhileMouseInside:(BOOL)showOnly { 345 - (void)setShowsBorderOnlyWhileMouseInside:(BOOL)showOnly {
326 [super setShowsBorderOnlyWhileMouseInside:showOnly]; 346 [super setShowsBorderOnlyWhileMouseInside:showOnly];
327 if (showOnly) { 347 if (showOnly) {
328 [self updateTrackingAreas]; 348 [self updateTrackingAreas];
329 } else { 349 } else {
330 if (trackingArea_) { 350 if (trackingArea_) {
331 [[self controlView] removeTrackingArea:trackingArea_]; 351 [[self controlView] removeTrackingArea:trackingArea_];
332 trackingArea_.reset(nil); 352 trackingArea_.reset(nil);
333 if (isMouseInside_) { 353 if (isMouseInside_) {
334 isMouseInside_ = NO; 354 isMouseInside_ = NO;
335 [[self controlView] setNeedsDisplay:YES]; 355 [[self controlView] setNeedsDisplay:YES];
336 } 356 }
337 } 357 }
338 } 358 }
339 } 359 }
340 360
341 // TODO(viettrungluu): clean up/reorganize. 361 // TODO(viettrungluu): clean up/reorganize.
342 - (void)drawBorderAndFillForTheme:(const ui::ThemeProvider*)themeProvider 362 - (void)drawBorderAndFillForTheme:(const ui::ThemeProvider*)themeProvider
343 controlView:(NSView*)controlView 363 controlView:(NSView*)controlView
344 innerPath:(NSBezierPath*)innerPath 364 innerPath:(NSBezierPath*)innerPath
345 showClickedGradient:(BOOL)showClickedGradient 365 showClickedGradient:(BOOL)showClickedGradient
346 showHighlightGradient:(BOOL)showHighlightGradient 366 showHighlightGradient:(BOOL)showHighlightGradient
347 hoverAlpha:(CGFloat)hoverAlpha 367 hoverAlpha:(CGFloat)hoverAlpha
348 active:(BOOL)active 368 active:(BOOL)active
349 cellFrame:(NSRect)cellFrame 369 cellFrame:(NSRect)cellFrame
350 defaultGradient:(NSGradient*)defaultGradient { 370 defaultGradient:(NSGradient*)defaultGradient {
371 // For Material Design, draw a solid rounded rect behind the button, based on
372 // the hover and pressed states.
373 if ([self tag] == kMaterialStandardButtonTypeWithLimitedClickFeedback) {
374 const CGFloat kEightPercentAlpha = 0.08;
375 const CGFloat kFourPercentAlpha = 0.04;
376
377 // The alpha is always at least 8%. Default the color to black.
378 CGFloat alpha = kEightPercentAlpha;
379 CGFloat color = 0.0;
380 // If a dark theme, incrase the opacity slightly and use white.
381 if ([[controlView window] hasDarkTheme]) {
382 alpha += kFourPercentAlpha;
383 color = 1.0;
384 }
385 // If clicked or highlighted, the background is slightly more opaque. If not
386 // clicked or highlighted, adjust the alpha by the animation fade in
387 // percentage.
388 if (showClickedGradient || showHighlightGradient) {
389 alpha += kFourPercentAlpha;
390 } else {
391 alpha *= hoverAlpha;
392 }
393
394 // Fill the path.
395 [[NSColor colorWithCalibratedWhite:color alpha:alpha] set];
396 [innerPath fill];
397 return;
398 }
399
351 BOOL isFlatButton = [self showsBorderOnlyWhileMouseInside]; 400 BOOL isFlatButton = [self showsBorderOnlyWhileMouseInside];
352 401
353 // For flat (unbordered when not hovered) buttons, never use the toolbar 402 // For flat (unbordered when not hovered) buttons, never use the toolbar
354 // button background image, but the modest gradient used for themed buttons. 403 // button background image, but the modest gradient used for themed buttons.
355 // To make things even more modest, scale the hover alpha down by 40 percent 404 // To make things even more modest, scale the hover alpha down by 40 percent
356 // unless clicked. 405 // unless clicked.
357 NSColor* backgroundImageColor; 406 NSColor* backgroundImageColor;
358 BOOL useThemeGradient; 407 BOOL useThemeGradient;
359 if (isFlatButton) { 408 if (isFlatButton) {
360 backgroundImageColor = nil; 409 backgroundImageColor = nil;
(...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after
454 [innerPath stroke]; 503 [innerPath stroke];
455 } 504 }
456 505
457 // TODO(viettrungluu): clean this up. 506 // TODO(viettrungluu): clean this up.
458 // (Private) 507 // (Private)
459 - (void)getDrawParamsForFrame:(NSRect)cellFrame 508 - (void)getDrawParamsForFrame:(NSRect)cellFrame
460 inView:(NSView*)controlView 509 inView:(NSView*)controlView
461 innerFrame:(NSRect*)returnInnerFrame 510 innerFrame:(NSRect*)returnInnerFrame
462 innerPath:(NSBezierPath**)returnInnerPath 511 innerPath:(NSBezierPath**)returnInnerPath
463 clipPath:(NSBezierPath**)returnClipPath { 512 clipPath:(NSBezierPath**)returnClipPath {
464 const CGFloat lineWidth = [controlView cr_lineWidth]; 513 const CGFloat kLineWidth = [controlView cr_lineWidth];
465 const CGFloat halfLineWidth = lineWidth / 2.0; 514 const CGFloat kHalfLineWidth = kLineWidth / 2.0;
466 515
467 // Constants from Cole. Will kConstant them once the feedback loop 516 NSRect drawFrame = cellFrame;
468 // is complete. 517 NSRect innerFrame = NSInsetRect(cellFrame, kLineWidth, kLineWidth);
469 NSRect drawFrame = NSInsetRect(cellFrame, 1.5 * lineWidth, 1.5 * lineWidth); 518 CGFloat cornerRadius = 2;
470 NSRect innerFrame = NSInsetRect(cellFrame, lineWidth, lineWidth); 519 if ([self tag] != kMaterialStandardButtonTypeWithLimitedClickFeedback) {
471 const CGFloat radius = 3; 520 drawFrame = NSInsetRect(cellFrame, 1.5 * kLineWidth, 1.5 * kLineWidth);
521 cornerRadius = 3;
522 }
472 523
473 ButtonType type = [[(NSControl*)controlView cell] tag]; 524 ButtonType type = [[(NSControl*)controlView cell] tag];
474 switch (type) { 525 switch (type) {
475 case kMiddleButtonType: 526 case kMiddleButtonType:
476 drawFrame.size.width += 20; 527 drawFrame.size.width += 20;
477 innerFrame.size.width += 2; 528 innerFrame.size.width += 2;
478 // Fallthrough 529 // Fallthrough
479 case kRightButtonType: 530 case kRightButtonType:
480 drawFrame.origin.x -= 20; 531 drawFrame.origin.x -= 20;
481 innerFrame.origin.x -= 2; 532 innerFrame.origin.x -= 2;
482 // Fallthrough 533 // Fallthrough
483 case kLeftButtonType: 534 case kLeftButtonType:
484 case kLeftButtonWithShadowType: 535 case kLeftButtonWithShadowType:
485 drawFrame.size.width += 20; 536 drawFrame.size.width += 20;
486 innerFrame.size.width += 2; 537 innerFrame.size.width += 2;
487 default: 538 default:
488 break; 539 break;
489 } 540 }
490 if (type == kLeftButtonWithShadowType) 541 if (type == kLeftButtonWithShadowType)
491 innerFrame.size.width -= 1.0; 542 innerFrame.size.width -= 1.0;
492 543
493 // Return results if |return...| not null. 544 // Return results if |return...| not null.
494 if (returnInnerFrame) 545 if (returnInnerFrame)
495 *returnInnerFrame = innerFrame; 546 *returnInnerFrame = innerFrame;
496 if (returnInnerPath) { 547 if (returnInnerPath) {
497 DCHECK(*returnInnerPath == nil); 548 DCHECK(*returnInnerPath == nil);
498 *returnInnerPath = [NSBezierPath bezierPathWithRoundedRect:drawFrame 549 *returnInnerPath = [NSBezierPath bezierPathWithRoundedRect:drawFrame
499 xRadius:radius 550 xRadius:cornerRadius
500 yRadius:radius]; 551 yRadius:cornerRadius];
501 [*returnInnerPath setLineWidth:lineWidth]; 552 [*returnInnerPath setLineWidth:kLineWidth];
502 } 553 }
503 if (returnClipPath) { 554 if (returnClipPath) {
504 DCHECK(*returnClipPath == nil); 555 DCHECK(*returnClipPath == nil);
505 NSRect clipPathRect = 556 NSRect clipPathRect =
506 NSInsetRect(drawFrame, -halfLineWidth, -halfLineWidth); 557 NSInsetRect(drawFrame, -kHalfLineWidth, -kHalfLineWidth);
507 *returnClipPath = [NSBezierPath 558 *returnClipPath = [NSBezierPath
508 bezierPathWithRoundedRect:clipPathRect 559 bezierPathWithRoundedRect:clipPathRect
509 xRadius:radius + halfLineWidth 560 xRadius:cornerRadius + kHalfLineWidth
510 yRadius:radius + halfLineWidth]; 561 yRadius:cornerRadius + kHalfLineWidth];
511 } 562 }
512 } 563 }
513 564
514 // TODO(viettrungluu): clean this up. 565 // TODO(viettrungluu): clean this up.
515 - (void)drawWithFrame:(NSRect)cellFrame inView:(NSView*)controlView { 566 - (void)drawWithFrame:(NSRect)cellFrame inView:(NSView*)controlView {
516 NSRect innerFrame; 567 NSRect innerFrame;
517 NSBezierPath* innerPath = nil; 568 NSBezierPath* innerPath = nil;
518 [self getDrawParamsForFrame:cellFrame 569 [self getDrawParamsForFrame:cellFrame
519 inView:controlView 570 inView:controlView
520 innerFrame:&innerFrame 571 innerFrame:&innerFrame
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
558 ThemeProperties::COLOR_TOOLBAR_BUTTON_STROKE_INACTIVE) : 609 ThemeProperties::COLOR_TOOLBAR_BUTTON_STROKE_INACTIVE) :
559 [NSColor blackColor]; 610 [NSColor blackColor];
560 611
561 [[stroke colorWithAlphaComponent:0.2] set]; 612 [[stroke colorWithAlphaComponent:0.2] set];
562 NSRectFillUsingOperation(NSInsetRect(borderRect, 0, 2), 613 NSRectFillUsingOperation(NSInsetRect(borderRect, 0, 2),
563 NSCompositeSourceOver); 614 NSCompositeSourceOver);
564 } 615 }
565 [self drawInteriorWithFrame:innerFrame inView:controlView]; 616 [self drawInteriorWithFrame:innerFrame inView:controlView];
566 } 617 }
567 618
619 - (CGFloat)textStartXOffset {
620 // 11 is the magic number needed to make this match the native
621 // NSButtonCell's label display.
622 return [[self image] size].width + 11;
623 }
624
568 - (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView*)controlView { 625 - (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView*)controlView {
569 const CGFloat lineWidth = [controlView cr_lineWidth]; 626 const CGFloat lineWidth = [controlView cr_lineWidth];
570 627
571 if (shouldTheme_) { 628 if (shouldTheme_) {
572 BOOL isTemplate = [[self image] isTemplate]; 629 BOOL isTemplate = [[self image] isTemplate];
573 630
574 gfx::ScopedNSGraphicsContextSaveGState scopedGState; 631 gfx::ScopedNSGraphicsContextSaveGState scopedGState;
575 632
576 CGContextRef context = 633 CGContextRef context =
577 (CGContextRef)([[NSGraphicsContext currentContext] graphicsPort]); 634 (CGContextRef)([[NSGraphicsContext currentContext] graphicsPort]);
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after
638 // get it to work with NSButtonCell. 695 // get it to work with NSButtonCell.
639 // TODO(jeremy): Move this to GTM. 696 // TODO(jeremy): Move this to GTM.
640 - (NSRect)drawTitle:(NSAttributedString*)title 697 - (NSRect)drawTitle:(NSAttributedString*)title
641 withFrame:(NSRect)cellFrame 698 withFrame:(NSRect)cellFrame
642 inView:(NSView*)controlView { 699 inView:(NSView*)controlView {
643 NSSize size = [title size]; 700 NSSize size = [title size];
644 701
645 // Empirically, Cocoa will draw an extra 2 pixels past NSWidth(cellFrame) 702 // Empirically, Cocoa will draw an extra 2 pixels past NSWidth(cellFrame)
646 // before it clips the text. 703 // before it clips the text.
647 const CGFloat kOverflowBeforeClip = 2; 704 const CGFloat kOverflowBeforeClip = 2;
648 BOOL clipping = YES; 705 BOOL isModeMaterial =
706 [self tag] == kMaterialStandardButtonTypeWithLimitedClickFeedback;
707 // For Material Design we don't want to clip the text. For all other button
708 // cell modes, we do.
709 BOOL shouldClipTheTitle = !isModeMaterial;
649 if (std::floor(size.width) <= (NSWidth(cellFrame) + kOverflowBeforeClip)) { 710 if (std::floor(size.width) <= (NSWidth(cellFrame) + kOverflowBeforeClip)) {
650 cellFrame.origin.y += ([self verticalTextOffset] - 1); 711 cellFrame.origin.y += ([self verticalTextOffset] - 1);
651 clipping = NO; 712 shouldClipTheTitle = NO;
652 } 713 }
653 714
654 // Gradient is about twice our line height long. 715 // Gradient is about twice our line height long.
655 CGFloat gradientWidth = MIN(size.height * 2, NSWidth(cellFrame) / 4); 716 CGFloat gradientWidth = MIN(size.height * 2, NSWidth(cellFrame) / 4);
656 717
657 NSRect solidPart, gradientPart; 718 NSRect solidPart, gradientPart;
658 NSDivideRect(cellFrame, &gradientPart, &solidPart, gradientWidth, NSMaxXEdge); 719 NSDivideRect(cellFrame, &gradientPart, &solidPart, gradientWidth, NSMaxXEdge);
659 720
660 // Draw non-gradient part without transparency layer, as light text on a dark 721 // Draw non-gradient part without transparency layer, as light text on a dark
661 // background looks bad with a gradient layer. 722 // background looks bad with a gradient layer.
662 NSPoint textOffset = NSZeroPoint; 723 NSPoint textOffset = NSZeroPoint;
663 { 724 {
664 gfx::ScopedNSGraphicsContextSaveGState scopedGState; 725 gfx::ScopedNSGraphicsContextSaveGState scopedGState;
665 if (clipping) 726 if (shouldClipTheTitle)
666 [NSBezierPath clipRect:solidPart]; 727 [NSBezierPath clipRect:solidPart];
667 728
668 // 11 is the magic number needed to make this match the native 729 CGFloat textLeft = [self textStartXOffset];
669 // NSButtonCell's label display.
670 CGFloat textLeft = [[self image] size].width + 11;
671 730
672 // For some reason, the height of cellFrame as passed in is totally bogus. 731 // For some reason, the height of cellFrame as passed in is totally bogus.
673 // For vertical centering purposes, we need the bounds of the containing 732 // For vertical centering purposes, we need the bounds of the containing
674 // view. 733 // view.
675 NSRect buttonFrame = [[self controlView] frame]; 734 NSRect buttonFrame = [[self controlView] frame];
676 735
677 // Call the vertical offset to match native NSButtonCell's version. 736 // Call the vertical offset to match native NSButtonCell's version.
678 textOffset = NSMakePoint(textLeft, 737 textOffset = NSMakePoint(textLeft,
679 (NSHeight(buttonFrame) - size.height) / 2 + 738 (NSHeight(buttonFrame) - size.height) / 2 +
680 [self verticalTextOffset]); 739 [self verticalTextOffset]);
681 [title drawAtPoint:textOffset]; 740 // WIth Material Design we want an ellipsis if the title is too long to fit,
741 // so have to use drawInRect: instead of drawAtPoint:.
742 if (isModeMaterial) {
743 NSRect textBounds = NSMakeRect(textOffset.x,
744 textOffset.y,
745 NSWidth(buttonFrame) - textOffset.x,
746 NSHeight(buttonFrame));
747 [title drawInRect:textBounds];
748 } else {
749 [title drawAtPoint:textOffset];
750 }
682 } 751 }
683 752
684 if (!clipping) 753 if (!shouldClipTheTitle)
685 return cellFrame; 754 return cellFrame;
686 755
687 // Draw the gradient part with a transparency layer. This makes the text look 756 // Draw the gradient part with a transparency layer. This makes the text look
688 // suboptimal, but since it fades out, that's ok. 757 // suboptimal, but since it fades out, that's ok.
689 gfx::ScopedNSGraphicsContextSaveGState scopedGState; 758 gfx::ScopedNSGraphicsContextSaveGState scopedGState;
690 [NSBezierPath clipRect:gradientPart]; 759 [NSBezierPath clipRect:gradientPart];
691 CGContextRef context = static_cast<CGContextRef>( 760 CGContextRef context = static_cast<CGContextRef>(
692 [[NSGraphicsContext currentContext] graphicsPort]); 761 [[NSGraphicsContext currentContext] graphicsPort]);
693 CGContextBeginTransparencyLayerWithRect(context, 762 CGContextBeginTransparencyLayerWithRect(context,
694 NSRectToCGRect(gradientPart), 0); 763 NSRectToCGRect(gradientPart), 0);
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after
759 options:options 828 options:options
760 owner:self 829 owner:self
761 userInfo:nil]); 830 userInfo:nil]);
762 if (isMouseInside_ != mouseInView) { 831 if (isMouseInside_ != mouseInView) {
763 [self setMouseInside:mouseInView animate:NO]; 832 [self setMouseInside:mouseInView animate:NO];
764 [controlView setNeedsDisplay:YES]; 833 [controlView setNeedsDisplay:YES];
765 } 834 }
766 } 835 }
767 836
768 @end 837 @end
OLDNEW
« no previous file with comments | « chrome/browser/ui/cocoa/gradient_button_cell.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698