Commit bcd1b81c authored by jrg@chromium.org's avatar jrg@chromium.org

Pulse new bookmarks (as triggered by bookmark bubble). If not possible,

pulse the topmost parent that is visible (e.g. "Other Bookmarks").

BUG=http://crbug.com/42028

TEST=\
Repeat this test with the following themes:
1) None, 2) Maria Carey, 3) Ocean Pacific, 4) American Apparel:
.
New profile.  Click star to 'mark something.  See bookmark pulse in bar.  Cancel.
Do that again, but set parent to "Other Bookmarks" and hit OK.
New page; click star.  Default parent is Other Bookmarks; see Other Bookmarks folder pulse.
Add enough bookmarks on bar so something falls in overflow menu (chevron appears).
Go to one of them, then click star.  (You can't see chevron pulse.. it's hidden.)
Create a folder.  Add a bookmark to the folder.  Go that page.
Click on star; see folder pulse.


Review URL: http://codereview.chromium.org/2805099

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@53912 0039d316-1c4b-4281-b951-d872f2087c98
parent 4777f90e
......@@ -228,12 +228,16 @@ const NSTimeInterval kBookmarkBarAnimationDuration = 0.12;
folderImage_.reset([rb.GetNSImageNamed(IDR_BOOKMARK_BAR_FOLDER) retain]);
defaultImage_.reset([rb.GetNSImageNamed(IDR_DEFAULT_FAVICON) retain]);
// Register for theme changes.
// Register for theme changes, bookmark button pulsing, ...
NSNotificationCenter* defaultCenter = [NSNotificationCenter defaultCenter];
[defaultCenter addObserver:self
selector:@selector(themeDidChangeNotification:)
name:kBrowserThemeDidChangeNotification
object:nil];
[defaultCenter addObserver:self
selector:@selector(pulseBookmarkNotification:)
name:bookmark_button::kPulseBookmarkButtonNotification
object:nil];
// This call triggers an awakeFromNib, which builds the bar, which
// might uses folderImage_. So make sure it happens after
......@@ -243,6 +247,40 @@ const NSTimeInterval kBookmarkBarAnimationDuration = 0.12;
return self;
}
- (void)pulseBookmarkNotification:(NSNotification*)notification {
NSDictionary* dict = [notification userInfo];
const BookmarkNode* node = NULL;
NSValue *value = [dict objectForKey:bookmark_button::kBookmarkKey];
DCHECK(value);
if (value)
node = static_cast<const BookmarkNode*>([value pointerValue]);
NSNumber* number = [dict
objectForKey:bookmark_button::kBookmarkPulseFlagKey];
DCHECK(number);
BOOL doPulse = number ? [number boolValue] : NO;
// 3 cases:
// button on the bar: flash it
// button in "other bookmarks" folder: flash other bookmarks
// button in "off the side" folder: flash the chevron
for (BookmarkButton* button in [self buttons]) {
if ([button bookmarkNode] == node) {
[button setIsContinuousPulsing:doPulse];
return;
}
}
if ([otherBookmarksButton_ bookmarkNode] == node) {
[otherBookmarksButton_ setIsContinuousPulsing:doPulse];
return;
}
if (node->GetParent() == bookmarkModel_->GetBookmarkBarNode()) {
[offTheSideButton_ setIsContinuousPulsing:doPulse];
return;
}
NOTREACHED() << "no bookmark button found to pulse!";
}
- (void)dealloc {
// We better stop any in-flight animation if we're being killed.
[[self animatableView] stopAnimation];
......
......@@ -2026,4 +2026,39 @@ TEST_F(BookmarkBarControllerDragDropTest, DropPositionIndicator) {
EXPECT_CGFLOAT_EQ(expected, actual);
}
TEST_F(BookmarkBarControllerDragDropTest, PulseButton) {
BookmarkModel* model = helper_.profile()->GetBookmarkModel();
const BookmarkNode* root = model->GetBookmarkBarNode();
GURL gurl("http://www.google.com");
const BookmarkNode* node = model->AddURL(root, root->GetChildCount(),
L"title", gurl);
BookmarkButton* button = [[bar_ buttons] objectAtIndex:0];
EXPECT_FALSE([button isContinuousPulsing]);
NSValue *value = [NSValue valueWithPointer:node];
NSDictionary *dict = [NSDictionary
dictionaryWithObjectsAndKeys:value,
bookmark_button::kBookmarkKey,
[NSNumber numberWithBool:YES],
bookmark_button::kBookmarkPulseFlagKey,
nil];
[[NSNotificationCenter defaultCenter]
postNotificationName:bookmark_button::kPulseBookmarkButtonNotification
object:nil
userInfo:dict];
EXPECT_TRUE([button isContinuousPulsing]);
dict = [NSDictionary dictionaryWithObjectsAndKeys:value,
bookmark_button::kBookmarkKey,
[NSNumber numberWithBool:NO],
bookmark_button::kBookmarkPulseFlagKey,
nil];
[[NSNotificationCenter defaultCenter]
postNotificationName:bookmark_button::kPulseBookmarkButtonNotification
object:nil
userInfo:dict];
EXPECT_FALSE([button isContinuousPulsing]);
}
} // namespace
......@@ -26,6 +26,9 @@ class BookmarkNode;
BookmarkModel* model_; // weak
const BookmarkNode* node_; // weak
// The bookmark node whose button we asked to pulse.
const BookmarkNode* pulsingBookmarkNode_; // weak
BOOL alreadyBookmarked_;
// Ping me when the bookmark model changes out from under us.
......
......@@ -7,6 +7,7 @@
#include "base/mac_util.h"
#include "base/sys_string_conversions.h"
#include "chrome/browser/bookmarks/bookmark_model.h"
#import "chrome/browser/cocoa/bookmark_button.h"
#import "chrome/browser/cocoa/browser_window_controller.h"
#import "chrome/browser/cocoa/info_bubble_view.h"
#include "chrome/browser/metrics/user_metrics.h"
......@@ -108,6 +109,48 @@ void BookmarkBubbleNotificationBridge::Observe(
[super dealloc];
}
// If this is a new bookmark somewhere visible (e.g. on the bookmark
// bar), pulse it. Else, call ourself recursively with our parent
// until we find something visible to pulse.
- (void)startPulsingBookmarkButton:(const BookmarkNode*)node {
while (node) {
if ((node->GetParent() == model_->GetBookmarkBarNode()) ||
(node == model_->other_node())) {
pulsingBookmarkNode_ = node;
NSValue *value = [NSValue valueWithPointer:node];
NSDictionary *dict = [NSDictionary
dictionaryWithObjectsAndKeys:value,
bookmark_button::kBookmarkKey,
[NSNumber numberWithBool:YES],
bookmark_button::kBookmarkPulseFlagKey,
nil];
[[NSNotificationCenter defaultCenter]
postNotificationName:bookmark_button::kPulseBookmarkButtonNotification
object:self
userInfo:dict];
return;
}
node = node->GetParent();
}
}
- (void)stopPulsingBookmarkButton {
if (!pulsingBookmarkNode_)
return;
NSValue *value = [NSValue valueWithPointer:pulsingBookmarkNode_];
pulsingBookmarkNode_ = NULL;
NSDictionary *dict = [NSDictionary
dictionaryWithObjectsAndKeys:value,
bookmark_button::kBookmarkKey,
[NSNumber numberWithBool:NO],
bookmark_button::kBookmarkPulseFlagKey,
nil];
[[NSNotificationCenter defaultCenter]
postNotificationName:bookmark_button::kPulseBookmarkButtonNotification
object:self
userInfo:dict];
}
// Close the bookmark bubble without changing anything. Unlike a
// typical dialog's OK/Cancel, where Cancel is "do nothing", all
// buttons on the bubble have the capacity to change the bookmark
......@@ -126,6 +169,7 @@ void BookmarkBubbleNotificationBridge::Observe(
[[NSNotificationCenter defaultCenter] removeObserver:self];
bookmark_observer_.reset(NULL);
chrome_observer_.reset(NULL);
[self stopPulsingBookmarkButton];
[self autorelease];
}
......@@ -173,6 +217,9 @@ void BookmarkBubbleNotificationBridge::Observe(
chrome_observer_.reset(new BookmarkBubbleNotificationBridge(
self, @selector(dismissWithoutEditing:)));
// Pulse something interesting on the bookmark bar.
[self startPulsingBookmarkButton:node_];
[window makeKeyAndOrderFront:self];
}
......@@ -209,6 +256,7 @@ void BookmarkBubbleNotificationBridge::Observe(
}
- (IBAction)ok:(id)sender {
[self stopPulsingBookmarkButton]; // before parent changes
[self updateBookmarkNode];
[self close];
}
......@@ -226,6 +274,7 @@ void BookmarkBubbleNotificationBridge::Observe(
}
- (IBAction)remove:(id)sender {
[self stopPulsingBookmarkButton];
model_->SetURLStarred(node_->GetURL(), node_->GetTitle(), false);
UserMetrics::RecordAction(UserMetricsAction("BookmarkBubble_Unstar"),
model_->profile());
......
......@@ -15,6 +15,41 @@
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/platform_test.h"
// Watch for bookmark pulse notifications so we can confirm they were sent.
@interface BookmarkPulseObserver : NSObject {
int notifications_;
}
@property (assign, nonatomic) int notifications;
@end
@implementation BookmarkPulseObserver
@synthesize notifications = notifications_;
- (id)init {
if ((self = [super init])) {
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(pulseBookmarkNotification:)
name:bookmark_button::kPulseBookmarkButtonNotification
object:nil];
}
return self;
}
- (void)pulseBookmarkNotification:(NSNotificationCenter *)notification {
notifications_++;
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
[super dealloc];
}
@end
namespace {
class BookmarkBubbleControllerTest : public CocoaTest {
......@@ -187,6 +222,7 @@ TEST_F(BookmarkBubbleControllerTest, TestEdit) {
}
// CallClose; bubble gets closed.
// Also confirm pulse notifications get sent.
TEST_F(BookmarkBubbleControllerTest, TestClose) {
BookmarkModel* model = GetBookmarkModel();
const BookmarkNode* node = model->AddURL(model->GetBookmarkBarNode(),
......@@ -195,12 +231,17 @@ TEST_F(BookmarkBubbleControllerTest, TestClose) {
GURL("http://www.google.com"));
EXPECT_EQ(edits_, 0);
scoped_nsobject<BookmarkPulseObserver> observer([[BookmarkPulseObserver alloc]
init]);
EXPECT_EQ([observer notifications], 0);
BookmarkBubbleController* controller = ControllerForNode(node);
EXPECT_TRUE(controller);
EXPECT_FALSE(IsWindowClosing());
EXPECT_EQ([observer notifications], 1);
[controller ok:controller];
EXPECT_EQ(edits_, 0);
EXPECT_TRUE(IsWindowClosing());
EXPECT_EQ([observer notifications], 2);
}
// User changes title and parent folder in the UI
......
......@@ -198,6 +198,13 @@ class ThemeProvider;
// http://crbug.com/35967
- (BOOL)isEmpty;
// Turn on or off pulsing of a bookmark button.
// Triggered by the bookmark bubble.
- (void)setIsContinuousPulsing:(BOOL)flag;
// Return continuous pulse state.
- (BOOL)isContinuousPulsing;
@end // @interface BookmarkButton
......@@ -205,3 +212,17 @@ class ThemeProvider;
- (void)beginDrag:(NSEvent*)event;
@end
namespace bookmark_button {
// Notifications for pulsing of bookmarks.
extern const NSString* kPulseBookmarkButtonNotification;
// Key for userInfo dict of a kPulseBookmarkButtonNotification.
// Value is a [NSValue valueWithPointer:]; pointer is a (const BookmarkNode*).
extern const NSString* kBookmarkKey;
// Key for userInfo dict of a kPulseBookmarkButtonNotification.
// Value is a [NSNumber numberWithBool:] to turn pulsing on or off.
extern const NSString* kBookmarkPulseFlagKey;
};
......@@ -13,6 +13,16 @@
// The opacity of the bookmark button drag image.
static const CGFloat kDragImageOpacity = 0.7;
namespace bookmark_button {
const NSString* kPulseBookmarkButtonNotification =
@"PulseBookmarkButtonNotification";
const NSString* kBookmarkKey = @"BookmarkKey";
const NSString* kBookmarkPulseFlagKey = @"BookmarkPulseFlagKey";
};
@interface BookmarkButton(Private)
// Make a drag image for the button.
......@@ -35,6 +45,8 @@ static const CGFloat kDragImageOpacity = 0.7;
}
- (void)dealloc {
if ([[self cell] respondsToSelector:@selector(safelyStopPulsing)])
[[self cell] safelyStopPulsing];
view_id_util::UnsetID(self);
[super dealloc];
}
......@@ -52,6 +64,14 @@ static const CGFloat kDragImageOpacity = 0.7;
return [self bookmarkNode] ? NO : YES;
}
- (void)setIsContinuousPulsing:(BOOL)flag {
[[self cell] setIsContinuousPulsing:flag];
}
- (BOOL)isContinuousPulsing {
return [[self cell] isContinuousPulsing];
}
// By default, NSButton ignores middle-clicks.
// But we want them.
- (void)otherMouseUp:(NSEvent*)event {
......
......@@ -32,6 +32,23 @@ enum {
};
typedef NSInteger ButtonType;
namespace gradient_button_cell {
// Pulsing state for this button.
typedef enum {
// Stable states.
kPulsedOn,
kPulsedOff,
// In motion which will end in a stable state.
kPulsingOn,
kPulsingOff,
// In continuous motion.
kPulsingContinuous,
} PulseState;
};
@interface GradientButtonCell : NSButtonCell {
@private
// Custom drawing means we need to perform our own mouse tracking if
......@@ -42,6 +59,9 @@ typedef NSInteger ButtonType;
CGFloat hoverAlpha_; // 0-1. Controls the alpha during mouse hover
NSTimeInterval lastHoverUpdate_;
scoped_nsobject<NSGradient> gradient_;
gradient_button_cell::PulseState pulseState_;
CGFloat pulseMultiplier_; // for selecting pulse direction when continuous.
CGFloat outerStrokeAlphaMult_; // For pulsing.
scoped_nsobject<NSImage> overlayImage_;
}
......@@ -68,15 +88,33 @@ typedef NSInteger ButtonType;
- (NSBezierPath*)clipPathForFrame:(NSRect)cellFrame
inView:(NSView*)controlView;
// Turn on or off continuous pulsing. When turning off continuous
// pulsing, leave our pulse state in the correct ending position for
// our isMouseInside_ property. Public since it's called from the
// bookmark bubble.
- (void)setIsContinuousPulsing:(BOOL)continuous;
// Returns continuous pulse state.
- (BOOL)isContinuousPulsing;
// Safely stop continuous pulsing by turning off all timers.
// May leave the cell in an odd state.
// Needed by an owning control's dealloc routine.
- (void)safelyStopPulsing;
@property(assign, nonatomic) CGFloat hoverAlpha;
// An image that will be drawn after the normal content of the button cell,
// overlaying it. Never themed.
@property(retain, nonatomic) NSImage* overlayImage;
@end
@interface GradientButtonCell(TestingAPI)
- (BOOL)isMouseInside;
- (BOOL)pulsing;
- (gradient_button_cell::PulseState)pulseState;
- (void)setPulseState:(gradient_button_cell::PulseState)pstate;
@end
#endif // CHROME_BROWSER_COCOA_GRADIENT_BUTTON_CELL_H_
......@@ -27,14 +27,188 @@
innerFrame:(NSRect*)returnInnerFrame
innerPath:(NSBezierPath**)returnInnerPath
clipPath:(NSBezierPath**)returnClipPath;
@end
static const NSTimeInterval kAnimationShowDuration = 0.2;
// Note: due to a bug (?), drawWithFrame:inView: does not call
// drawBorderAndFillForTheme::::: unless the mouse is inside. The net
// effect is that our "fade out" when the mouse leaves becaumes
// instantaneous. When I "fixed" it things looked horrible; the
// hover-overed bookmark button would stay highlit for 0.4 seconds
// which felt like latency/lag. I'm leaving the "bug" in place for
// now so we don't suck. -jrg
static const NSTimeInterval kAnimationHideDuration = 0.4;
static const NSTimeInterval kAnimationContinuousCycleDuration = 0.4;
@implementation GradientButtonCell
@synthesize hoverAlpha = hoverAlpha_;
// For nib instantiations
- (id)initWithCoder:(NSCoder*)decoder {
if ((self = [super initWithCoder:decoder])) {
[self sharedInit];
}
return self;
}
// For programmatic instantiations
- (id)initTextCell:(NSString*)string {
if ((self = [super initTextCell:string])) {
[self sharedInit];
}
return self;
}
- (void)dealloc {
if (trackingArea_) {
[[self controlView] removeTrackingArea:trackingArea_];
trackingArea_.reset();
}
[super dealloc];
}
// Return YES if we are pulsing (towards another state or continuously).
- (BOOL)pulsing {
if ((pulseState_ == gradient_button_cell::kPulsingOn) ||
(pulseState_ == gradient_button_cell::kPulsingOff) ||
(pulseState_ == gradient_button_cell::kPulsingContinuous))
return YES;
return NO;
}
// Perform one pulse step when animating a pulse.
- (void)performOnePulseStep {
NSTimeInterval thisUpdate = [NSDate timeIntervalSinceReferenceDate];
NSTimeInterval elapsed = thisUpdate - lastHoverUpdate_;
CGFloat opacity = [self hoverAlpha];
// Update opacity based on state.
// Adjust state if we have finished.
switch (pulseState_) {
case gradient_button_cell::kPulsingOn:
opacity += elapsed / kAnimationShowDuration;
if (opacity > 1.0) {
[self setPulseState:gradient_button_cell::kPulsedOn];
return;
}
break;
case gradient_button_cell::kPulsingOff:
opacity -= elapsed / kAnimationHideDuration;
if (opacity < 0.0) {
[self setPulseState:gradient_button_cell::kPulsedOff];
return;
}
break;
case gradient_button_cell::kPulsingContinuous:
opacity += elapsed / kAnimationContinuousCycleDuration * pulseMultiplier_;
if (opacity > 1.0) {
opacity = 1.0;
pulseMultiplier_ *= -1.0;
} else if (opacity < 0.0) {
opacity = 0.0;
pulseMultiplier_ *= -1.0;
}
outerStrokeAlphaMult_ = opacity;
break;
default:
NOTREACHED() << "unknown pulse state";
}
// Update our control.
lastHoverUpdate_ = thisUpdate;
[self setHoverAlpha:opacity];
[[self controlView] setNeedsDisplay:YES];
// If our state needs it, keep going.
if ([self pulsing]) {
[self performSelector:_cmd withObject:nil afterDelay:0.02];
}
}
- (gradient_button_cell::PulseState)pulseState {
return pulseState_;
}
// Set the pulsing state. This can either set the pulse to on or off
// immediately (e.g. kPulsedOn, kPulsedOff) or initiate an animated
// state change.
- (void)setPulseState:(gradient_button_cell::PulseState)pstate {
pulseState_ = pstate;
pulseMultiplier_ = 0.0;
[NSObject cancelPreviousPerformRequestsWithTarget:self];
lastHoverUpdate_ = [NSDate timeIntervalSinceReferenceDate];
switch (pstate) {
case gradient_button_cell::kPulsedOn:
case gradient_button_cell::kPulsedOff:
outerStrokeAlphaMult_ = 1.0;
[self setHoverAlpha:((pulseState_ == gradient_button_cell::kPulsedOn) ?
1.0 : 0.0)];
[[self controlView] setNeedsDisplay:YES];
break;
case gradient_button_cell::kPulsingOn:
case gradient_button_cell::kPulsingOff:
outerStrokeAlphaMult_ = 1.0;
// Set initial value then engage timer.
[self setHoverAlpha:((pulseState_ == gradient_button_cell::kPulsingOn) ?
0.0 : 1.0)];
[self performOnePulseStep];
break;
case gradient_button_cell::kPulsingContinuous:
// Semantics of continuous pulsing are that we pulse independent
// of mouse position.
pulseMultiplier_ = 1.0;
[self performOnePulseStep];
break;
default:
CHECK(0);
break;
}
}
- (void)safelyStopPulsing {
[NSObject cancelPreviousPerformRequestsWithTarget:self];
}
- (void)setIsContinuousPulsing:(BOOL)continuous {
if (!continuous && pulseState_ != gradient_button_cell::kPulsingContinuous)
return;
if (continuous) {
[self setPulseState:gradient_button_cell::kPulsingContinuous];
} else {
[self setPulseState:(isMouseInside_ ? gradient_button_cell::kPulsedOn :
gradient_button_cell::kPulsedOff)];
}
}
- (BOOL)isContinuousPulsing {
return (pulseState_ == gradient_button_cell::kPulsingContinuous) ?
YES : NO;
}
#if 1
// If we are not continuously pulsing, perform a pulse animation to
// reflect our new state.
- (void)setMouseInside:(BOOL)flag animate:(BOOL)animated {
isMouseInside_ = flag;
if (pulseState_ != gradient_button_cell::kPulsingContinuous) {
if (animated) {
[self setPulseState:(isMouseInside_ ? gradient_button_cell::kPulsingOn :
gradient_button_cell::kPulsingOff)];
} else {
[self setPulseState:(isMouseInside_ ? gradient_button_cell::kPulsedOn :
gradient_button_cell::kPulsedOff)];
}
}
}
#else
- (void)adjustHoverValue {
NSTimeInterval thisUpdate = [NSDate timeIntervalSinceReferenceDate];
......@@ -72,29 +246,9 @@ static const NSTimeInterval kAnimationHideDuration = 0.4;
[[self controlView] setNeedsDisplay:YES];
}
// For nib instantiations
- (id)initWithCoder:(NSCoder*)decoder {
if ((self = [super initWithCoder:decoder])) {
[self sharedInit];
}
return self;
}
// For programmatic instantiations
- (id)initTextCell:(NSString*)string {
if ((self = [super initTextCell:string])) {
[self sharedInit];
}
return self;
}
- (void)dealloc {
if (trackingArea_) {
[[self controlView] removeTrackingArea:trackingArea_];
trackingArea_.reset();
}
[super dealloc];
}
#endif
- (NSGradient*)gradientForHoverAlpha:(CGFloat)hoverAlpha
isThemed:(BOOL)themed {
......@@ -121,6 +275,9 @@ static const NSTimeInterval kAnimationHideDuration = 0.4;
- (void)sharedInit {
shouldTheme_ = YES;
pulseState_ = gradient_button_cell::kPulsedOff;
pulseMultiplier_ = 1.0;
outerStrokeAlphaMult_ = 1.0;
gradient_.reset([[self gradientForHoverAlpha:0.0 isThemed:NO] retain]);
}
......@@ -283,12 +440,15 @@ static const NSTimeInterval kAnimationHideDuration = 0.4;
// Draw the outer stroke.
NSColor* strokeColor = nil;
if (showClickedGradient) {
strokeColor = [NSColor colorWithCalibratedWhite:0.0 alpha:0.3];
strokeColor = [NSColor
colorWithCalibratedWhite:0.0
alpha:0.3 * outerStrokeAlphaMult_];
} else {
strokeColor = themeProvider ? themeProvider->GetNSColor(
active ? BrowserThemeProvider::COLOR_TOOLBAR_BUTTON_STROKE :
BrowserThemeProvider::COLOR_TOOLBAR_BUTTON_STROKE_INACTIVE,
true) : [NSColor colorWithCalibratedWhite:0.0 alpha:0.6];
true) : [NSColor colorWithCalibratedWhite:0.0
alpha:0.6 * outerStrokeAlphaMult_];
}
[strokeColor setStroke];
......@@ -366,12 +526,17 @@ static const NSTimeInterval kAnimationHideDuration = 0.4;
// the only time we want to draw the inner gradient is if we're highlighted.
if (([self isBordered] && ![self showsBorderOnlyWhileMouseInside]) ||
pressed ||
[self isMouseInside]) {
[self isMouseInside] ||
[self isContinuousPulsing]) {
// When pulsing we want the bookmark to stand out a little more.
BOOL showClickedGradient = pressed ||
(pulseState_ == gradient_button_cell::kPulsingContinuous);
[self drawBorderAndFillForTheme:themeProvider
controlView:controlView
innerPath:innerPath
showClickedGradient:pressed
showClickedGradient:showClickedGradient
showHighlightGradient:[self isHighlighted]
hoverAlpha:[self hoverAlpha]
active:active
......
......@@ -11,7 +11,7 @@
#include "testing/platform_test.h"
@interface GradientButtonCell (HoverValueTesting)
- (void)adjustHoverValue;
- (void)performOnePulseStep;
@end
namespace {
......@@ -51,7 +51,7 @@ TEST_F(GradientButtonCellTest, Hover) {
[cell setMouseInside:NO animate:YES];
CGFloat alpha1 = [cell hoverAlpha];
[cell adjustHoverValue];
[cell performOnePulseStep];
CGFloat alpha2 = [cell hoverAlpha];
EXPECT_TRUE(alpha2 < alpha1);
}
......@@ -77,4 +77,36 @@ TEST_F(GradientButtonCellTest, TrackingRects) {
[cell setShowsBorderOnlyWhileMouseInside:NO];
}
TEST_F(GradientButtonCellTest, ContinuousPulseOnOff) {
GradientButtonCell* cell = [view_ cell];
// On/off
EXPECT_FALSE([cell isContinuousPulsing]);
[cell setIsContinuousPulsing:YES];
EXPECT_TRUE([cell isContinuousPulsing]);
EXPECT_TRUE([cell pulsing]);
[cell setIsContinuousPulsing:NO];
EXPECT_FALSE([cell isContinuousPulsing]);
// On/safeOff
[cell setIsContinuousPulsing:YES];
EXPECT_TRUE([cell isContinuousPulsing]);
[cell safelyStopPulsing];
}
// More for valgrind; we don't confirm state change does anything useful.
TEST_F(GradientButtonCellTest, PulseState) {
GradientButtonCell* cell = [view_ cell];
[cell setMouseInside:YES animate:YES];
// Allow for immediate state changes to keep test unflaky
EXPECT_TRUE(([cell pulseState] == gradient_button_cell::kPulsingOn) ||
([cell pulseState] == gradient_button_cell::kPulsedOn));
[cell setMouseInside:NO animate:YES];
// Allow for immediate state changes to keep test unflaky
EXPECT_TRUE(([cell pulseState] == gradient_button_cell::kPulsingOff) ||
([cell pulseState] == gradient_button_cell::kPulsedOff));
}
} // namespace
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment