blob: b415eb23c8821e3eda24661dfd6a76809969587c (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
|
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "chrome/browser/cocoa/info_bubble_window.h"
#include "base/logging.h"
#include "base/scoped_nsobject.h"
#import "third_party/GTM/AppKit/GTMNSAnimation+Duration.h"
namespace {
const CGFloat kOrderInSlideOffset = 10;
const NSTimeInterval kOrderInAnimationDuration = 0.2;
const NSTimeInterval kOrderOutAnimationDuration = 0.15;
}
@interface InfoBubbleWindow (Private)
- (void)finishCloseAfterAnimation;
@end
// A delegate object for watching the alphaValue animation on InfoBubbleWindows.
// An InfoBubbleWindow instance cannot be the delegate for its own animation
// because CAAnimations retain their delegates, and since the InfoBubbleWindow
// retains its animations a retain loop would be formed.
@interface InfoBubbleWindowCloser : NSObject {
@private
InfoBubbleWindow* window_; // Weak. Window to close.
}
- (id)initWithWindow:(InfoBubbleWindow*)window;
@end
@implementation InfoBubbleWindowCloser
- (id)initWithWindow:(InfoBubbleWindow*)window {
if ((self = [super init])) {
window_ = window;
}
return self;
}
// Callback for the alpha animation. Closes window_ if appropriate.
- (void)animationDidStop:(CAAnimation*)anim finished:(BOOL)flag {
// When alpha reaches zero, close window_.
if ([window_ alphaValue] == 0.0) {
[window_ finishCloseAfterAnimation];
}
}
@end
@implementation InfoBubbleWindow
- (id)initWithContentRect:(NSRect)contentRect
styleMask:(NSUInteger)aStyle
backing:(NSBackingStoreType)bufferingType
defer:(BOOL)flag {
if ((self = [super initWithContentRect:contentRect
styleMask:NSBorderlessWindowMask
backing:bufferingType
defer:flag])) {
[self setBackgroundColor:[NSColor clearColor]];
[self setExcludedFromWindowsMenu:YES];
[self setOpaque:NO];
[self setHasShadow:YES];
// Start invisible. Will be made visible when ordered front.
[self setAlphaValue:0.0];
// Set up alphaValue animation so that self is delegate for the animation.
// Setting up the delegate is required so that the
// animationDidStop:finished: callback can be handled.
// Notice that only the alphaValue Animation is replaced in case
// superclasses set up animations.
CAAnimation* alphaAnimation = [CABasicAnimation animation];
scoped_nsobject<InfoBubbleWindowCloser> delegate(
[[InfoBubbleWindowCloser alloc] initWithWindow:self]);
[alphaAnimation setDelegate:delegate];
NSMutableDictionary* animations =
[NSMutableDictionary dictionaryWithDictionary:[self animations]];
[animations setObject:alphaAnimation forKey:@"alphaValue"];
[self setAnimations:animations];
}
return self;
}
// According to
// http://www.cocoabuilder.com/archive/message/cocoa/2006/6/19/165953,
// NSBorderlessWindowMask windows cannot become key or main. In this
// case, this is not a desired behavior. As an example, the bubble could have
// buttons.
- (BOOL)canBecomeKeyWindow {
return YES;
}
- (void)close {
// Block the window from receiving events while it fades out.
closing_ = YES;
// Apply animations to hide self.
[NSAnimationContext beginGrouping];
[[NSAnimationContext currentContext]
gtm_setDuration:kOrderOutAnimationDuration];
[[self animator] setAlphaValue:0.0];
[NSAnimationContext endGrouping];
}
// Called by InfoBubbleWindowCloser when the window is to be really closed
// after the fading animation is complete.
- (void)finishCloseAfterAnimation {
if (closing_) {
[super close];
}
}
// Adds animation for info bubbles being ordered to the front.
- (void)orderWindow:(NSWindowOrderingMode)orderingMode
relativeTo:(NSInteger)otherWindowNumber {
// According to the documentation '0' is the otherWindowNumber when the window
// is ordered front.
if (orderingMode == NSWindowAbove && otherWindowNumber == 0) {
// Order self appropriately assuming that its alpha is zero as set up
// in the designated initializer.
[super orderWindow:orderingMode relativeTo:otherWindowNumber];
// Set up frame so it can be adjust down by a few pixels.
NSRect frame = [self frame];
NSPoint newOrigin = frame.origin;
newOrigin.y += kOrderInSlideOffset;
[self setFrameOrigin:newOrigin];
// Apply animations to show and move self.
[NSAnimationContext beginGrouping];
[[NSAnimationContext currentContext]
gtm_setDuration:kOrderInAnimationDuration];
[[self animator] setAlphaValue:1.0];
[[self animator] setFrame:frame display:YES];
[NSAnimationContext endGrouping];
} else {
[super orderWindow:orderingMode relativeTo:otherWindowNumber];
}
}
// If the window is currently animating a close, block all UI events to the
// window.
- (void)sendEvent:(NSEvent*)theEvent {
if (!closing_) {
[super sendEvent:theEvent];
}
}
- (BOOL)isClosing {
return closing_;
}
@end
|