Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 Loading... | |
| 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 Loading... | |
| 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 | |
|
upolat
2016/05/09 23:16:45
typo: THe
shrike
2016/05/09 23:42:37
Done.
| |
| 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 Loading... | |
| 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 } else { |
| 521 drawFrame = NSInsetRect(cellFrame, 1.5 * kLineWidth, 1.5 * kLineWidth); | |
| 522 cornerRadius = 3; | |
| 523 } | |
| 472 | 524 |
| 473 ButtonType type = [[(NSControl*)controlView cell] tag]; | 525 ButtonType type = [[(NSControl*)controlView cell] tag]; |
| 474 switch (type) { | 526 switch (type) { |
| 475 case kMiddleButtonType: | 527 case kMiddleButtonType: |
| 476 drawFrame.size.width += 20; | 528 drawFrame.size.width += 20; |
| 477 innerFrame.size.width += 2; | 529 innerFrame.size.width += 2; |
| 478 // Fallthrough | 530 // Fallthrough |
| 479 case kRightButtonType: | 531 case kRightButtonType: |
| 480 drawFrame.origin.x -= 20; | 532 drawFrame.origin.x -= 20; |
| 481 innerFrame.origin.x -= 2; | 533 innerFrame.origin.x -= 2; |
| 482 // Fallthrough | 534 // Fallthrough |
| 483 case kLeftButtonType: | 535 case kLeftButtonType: |
| 484 case kLeftButtonWithShadowType: | 536 case kLeftButtonWithShadowType: |
| 485 drawFrame.size.width += 20; | 537 drawFrame.size.width += 20; |
| 486 innerFrame.size.width += 2; | 538 innerFrame.size.width += 2; |
| 487 default: | 539 default: |
| 488 break; | 540 break; |
| 489 } | 541 } |
| 490 if (type == kLeftButtonWithShadowType) | 542 if (type == kLeftButtonWithShadowType) |
| 491 innerFrame.size.width -= 1.0; | 543 innerFrame.size.width -= 1.0; |
| 492 | 544 |
| 493 // Return results if |return...| not null. | 545 // Return results if |return...| not null. |
| 494 if (returnInnerFrame) | 546 if (returnInnerFrame) |
| 495 *returnInnerFrame = innerFrame; | 547 *returnInnerFrame = innerFrame; |
| 496 if (returnInnerPath) { | 548 if (returnInnerPath) { |
| 497 DCHECK(*returnInnerPath == nil); | 549 DCHECK(*returnInnerPath == nil); |
| 498 *returnInnerPath = [NSBezierPath bezierPathWithRoundedRect:drawFrame | 550 *returnInnerPath = [NSBezierPath bezierPathWithRoundedRect:drawFrame |
| 499 xRadius:radius | 551 xRadius:cornerRadius |
| 500 yRadius:radius]; | 552 yRadius:cornerRadius]; |
| 501 [*returnInnerPath setLineWidth:lineWidth]; | 553 [*returnInnerPath setLineWidth:kLineWidth]; |
| 502 } | 554 } |
| 503 if (returnClipPath) { | 555 if (returnClipPath) { |
| 504 DCHECK(*returnClipPath == nil); | 556 DCHECK(*returnClipPath == nil); |
| 505 NSRect clipPathRect = | 557 NSRect clipPathRect = |
| 506 NSInsetRect(drawFrame, -halfLineWidth, -halfLineWidth); | 558 NSInsetRect(drawFrame, -kHalfLineWidth, -kHalfLineWidth); |
| 507 *returnClipPath = [NSBezierPath | 559 *returnClipPath = [NSBezierPath |
| 508 bezierPathWithRoundedRect:clipPathRect | 560 bezierPathWithRoundedRect:clipPathRect |
| 509 xRadius:radius + halfLineWidth | 561 xRadius:cornerRadius + kHalfLineWidth |
| 510 yRadius:radius + halfLineWidth]; | 562 yRadius:cornerRadius + kHalfLineWidth]; |
| 511 } | 563 } |
| 512 } | 564 } |
| 513 | 565 |
| 514 // TODO(viettrungluu): clean this up. | 566 // TODO(viettrungluu): clean this up. |
| 515 - (void)drawWithFrame:(NSRect)cellFrame inView:(NSView*)controlView { | 567 - (void)drawWithFrame:(NSRect)cellFrame inView:(NSView*)controlView { |
| 516 NSRect innerFrame; | 568 NSRect innerFrame; |
| 517 NSBezierPath* innerPath = nil; | 569 NSBezierPath* innerPath = nil; |
| 518 [self getDrawParamsForFrame:cellFrame | 570 [self getDrawParamsForFrame:cellFrame |
| 519 inView:controlView | 571 inView:controlView |
| 520 innerFrame:&innerFrame | 572 innerFrame:&innerFrame |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 558 ThemeProperties::COLOR_TOOLBAR_BUTTON_STROKE_INACTIVE) : | 610 ThemeProperties::COLOR_TOOLBAR_BUTTON_STROKE_INACTIVE) : |
| 559 [NSColor blackColor]; | 611 [NSColor blackColor]; |
| 560 | 612 |
| 561 [[stroke colorWithAlphaComponent:0.2] set]; | 613 [[stroke colorWithAlphaComponent:0.2] set]; |
| 562 NSRectFillUsingOperation(NSInsetRect(borderRect, 0, 2), | 614 NSRectFillUsingOperation(NSInsetRect(borderRect, 0, 2), |
| 563 NSCompositeSourceOver); | 615 NSCompositeSourceOver); |
| 564 } | 616 } |
| 565 [self drawInteriorWithFrame:innerFrame inView:controlView]; | 617 [self drawInteriorWithFrame:innerFrame inView:controlView]; |
| 566 } | 618 } |
| 567 | 619 |
| 620 - (CGFloat)textStartXOffset { | |
| 621 // 11 is the magic number needed to make this match the native | |
| 622 // NSButtonCell's label display. | |
| 623 return [[self image] size].width + 11; | |
| 624 } | |
| 625 | |
| 568 - (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView*)controlView { | 626 - (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView*)controlView { |
| 569 const CGFloat lineWidth = [controlView cr_lineWidth]; | 627 const CGFloat lineWidth = [controlView cr_lineWidth]; |
| 570 | 628 |
| 571 if (shouldTheme_) { | 629 if (shouldTheme_) { |
| 572 BOOL isTemplate = [[self image] isTemplate]; | 630 BOOL isTemplate = [[self image] isTemplate]; |
| 573 | 631 |
| 574 gfx::ScopedNSGraphicsContextSaveGState scopedGState; | 632 gfx::ScopedNSGraphicsContextSaveGState scopedGState; |
| 575 | 633 |
| 576 CGContextRef context = | 634 CGContextRef context = |
| 577 (CGContextRef)([[NSGraphicsContext currentContext] graphicsPort]); | 635 (CGContextRef)([[NSGraphicsContext currentContext] graphicsPort]); |
| (...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 658 NSDivideRect(cellFrame, &gradientPart, &solidPart, gradientWidth, NSMaxXEdge); | 716 NSDivideRect(cellFrame, &gradientPart, &solidPart, gradientWidth, NSMaxXEdge); |
| 659 | 717 |
| 660 // Draw non-gradient part without transparency layer, as light text on a dark | 718 // Draw non-gradient part without transparency layer, as light text on a dark |
| 661 // background looks bad with a gradient layer. | 719 // background looks bad with a gradient layer. |
| 662 NSPoint textOffset = NSZeroPoint; | 720 NSPoint textOffset = NSZeroPoint; |
| 663 { | 721 { |
| 664 gfx::ScopedNSGraphicsContextSaveGState scopedGState; | 722 gfx::ScopedNSGraphicsContextSaveGState scopedGState; |
| 665 if (clipping) | 723 if (clipping) |
| 666 [NSBezierPath clipRect:solidPart]; | 724 [NSBezierPath clipRect:solidPart]; |
| 667 | 725 |
| 668 // 11 is the magic number needed to make this match the native | 726 CGFloat textLeft = [self textStartXOffset]; |
| 669 // NSButtonCell's label display. | |
| 670 CGFloat textLeft = [[self image] size].width + 11; | |
| 671 | 727 |
| 672 // For some reason, the height of cellFrame as passed in is totally bogus. | 728 // 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 | 729 // For vertical centering purposes, we need the bounds of the containing |
| 674 // view. | 730 // view. |
| 675 NSRect buttonFrame = [[self controlView] frame]; | 731 NSRect buttonFrame = [[self controlView] frame]; |
| 676 | 732 |
| 677 // Call the vertical offset to match native NSButtonCell's version. | 733 // Call the vertical offset to match native NSButtonCell's version. |
| 678 textOffset = NSMakePoint(textLeft, | 734 textOffset = NSMakePoint(textLeft, |
| 679 (NSHeight(buttonFrame) - size.height) / 2 + | 735 (NSHeight(buttonFrame) - size.height) / 2 + |
| 680 [self verticalTextOffset]); | 736 [self verticalTextOffset]); |
| (...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 759 options:options | 815 options:options |
| 760 owner:self | 816 owner:self |
| 761 userInfo:nil]); | 817 userInfo:nil]); |
| 762 if (isMouseInside_ != mouseInView) { | 818 if (isMouseInside_ != mouseInView) { |
| 763 [self setMouseInside:mouseInView animate:NO]; | 819 [self setMouseInside:mouseInView animate:NO]; |
| 764 [controlView setNeedsDisplay:YES]; | 820 [controlView setNeedsDisplay:YES]; |
| 765 } | 821 } |
| 766 } | 822 } |
| 767 | 823 |
| 768 @end | 824 @end |
| OLD | NEW |