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

Side by Side Diff: chrome/browser/ui/cocoa/autofill/autofill_overlay_controller.mm

Issue 23674004: [rAC, OSX] Add overlay shield for interstitials/waits. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Allow animated overlays. Created 7 years, 3 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 | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #import "chrome/browser/ui/cocoa/autofill/autofill_overlay_controller.h"
6
7 #include "base/logging.h"
8 #include "base/strings/sys_string_conversions.h"
9 #include "base/timer/timer.h"
10 #include "chrome/browser/ui/autofill/autofill_dialog_types.h"
11 #include "chrome/browser/ui/autofill/autofill_dialog_view_delegate.h"
12 #include "chrome/browser/ui/cocoa/autofill/autofill_dialog_constants.h"
13 #include "skia/ext/skia_utils_mac.h"
14 #include "ui/base/animation/animation_delegate.h"
15 #include "ui/base/animation/multi_animation.h"
16
17 namespace {
18
19 // Spacing between lines of text in the overlay view.
20 const int kOverlayTextInterlineSpacing = 10;
sail 2013/09/04 20:04:06 CGFloat?, same below
groby-ooo-7-16 2013/09/04 20:41:45 Hm. Technically, they'll be shared with the views
21
22 // Spacing below image and above text messages in overlay view.
23 const int kOverlayImageBottomMargin = 50;
24
25 // TODO(groby): Unify colors with Views.
26 // Slight shading for mouse hover and legal document background.
27 SkColor kShadingColor = 0xfff2f2f2;
28
29 // A border color for the legal document view.
30 SkColor kSubtleBorderColor = 0xffdfdfdf;
31
32 // Shorten a few long types.
33 typedef ui::MultiAnimation::Part Part;
34 typedef ui::MultiAnimation::Parts Parts;
35
36 } // namespace
37
38 // Bridges Objective C and C++ delegate interfaces.
39 class AnimationDelegateBridge : public ui::AnimationDelegate {
40 public:
41 AnimationDelegateBridge(id<ChromiumAnimationDelegate>);
sail 2013/09/04 21:18:51 argument name?
groby-ooo-7-16 2013/09/04 22:19:01 Done.
42
43 protected:
44 // AnimationDelegate implementation.
45 virtual void AnimationProgressed(const ui::Animation* animation) OVERRIDE;
46 virtual void AnimationEnded(const ui::Animation* animation) OVERRIDE;
47
48 private:
49 id<ChromiumAnimationDelegate> delegate_; // Not owned. Owns DelegateBridge.
50 };
sail 2013/09/04 21:18:51 DISALLOW_...
groby-ooo-7-16 2013/09/04 22:19:01 Done.
51
52 AnimationDelegateBridge::AnimationDelegateBridge(
53 id<ChromiumAnimationDelegate> delegate) : delegate_(delegate) {}
sail 2013/09/04 21:18:51 Instead of using a generic delegate protocol you c
groby-ooo-7-16 2013/09/04 22:19:01 There are more animations coming - this will be ho
sail 2013/09/04 23:08:25 ok
54
55 void AnimationDelegateBridge::AnimationProgressed(
56 const ui::Animation* animation) {
57 [delegate_ animationProgressed:animation];
58 }
59
60 void AnimationDelegateBridge::AnimationEnded(
61 const ui::Animation* animation) {
62 [delegate_ animationEnded:animation];
63 }
64
65 class OverlayTimerBridge {
66 public:
67 OverlayTimerBridge(AutofillOverlayController* controller);
68 void SetExpiry(const base::TimeDelta& delta);
69
70 private:
71 void UpdateOverlayState();
72
73 base::Timer refresh_timer_; // Controls when it's time to refresh overlay.
sail 2013/09/04 21:18:51 Use OneShotTimer instead?
groby-ooo-7-16 2013/09/04 22:19:01 Done.
74
75 // not owned, |overlay_view_controller_| owns |this|.
76 AutofillOverlayController* overlay_view_controller_;
77 };
sail 2013/09/04 21:18:51 DISALLOW_...
groby-ooo-7-16 2013/09/04 22:19:01 Done.
78
79 OverlayTimerBridge::OverlayTimerBridge(AutofillOverlayController* controller)
80 : refresh_timer_(false, false),
81 overlay_view_controller_(controller) {
82 }
83
84 void OverlayTimerBridge::SetExpiry(const base::TimeDelta& expiry) {
85 if (expiry != base::TimeDelta()) {
86 refresh_timer_.Start(FROM_HERE,
87 expiry,
88 base::Bind(&OverlayTimerBridge::UpdateOverlayState,
89 base::Unretained(this)));
90 } else {
91 refresh_timer_.Stop();
92 }
93 }
94
95 void OverlayTimerBridge::UpdateOverlayState() {
96 [overlay_view_controller_ updateState];
97 }
98
99 // An NSView encapsulating the message stack and its custom drawn elements.
100 @interface AutofillMessageStackView : NSView<AutofillLayout>
101 - (CGFloat)getHeightForWidth:(CGFloat)width;
102 - (void)setMessages:
103 (const std::vector<autofill::DialogOverlayString>&)messages;
104 @end
105
106
107 @implementation AutofillMessageStackView
108
109 - (void)drawRect:(NSRect)dirtyRect {
110 NSColor* shadingColor = gfx::SkColorToCalibratedNSColor(kShadingColor);
111 NSColor* borderColor = gfx::SkColorToCalibratedNSColor(kSubtleBorderColor);
112
113 CGFloat arrowHalfWidth = kArrowWidth / 2.0;
sail 2013/09/04 20:04:06 I know this was already there but could we put kAr
groby-ooo-7-16 2013/09/04 20:41:45 Will do in follow-up CL, since I'd otherwise have
114 NSRect bounds = [self bounds];
115 CGFloat y = NSMaxY(bounds) - kArrowHeight;
116
117 NSBezierPath* arrow = [NSBezierPath bezierPath];
118 // Note that we purposely draw slightly outside of |bounds| so that the
119 // stroke is hidden on the sides.
sail 2013/09/04 21:18:51 "sides" -> "sides and bottom" ?
groby-ooo-7-16 2013/09/04 22:19:01 Done.
120 [arrow moveToPoint:NSMakePoint(NSMinX(bounds) - 1.0, y)];
sail 2013/09/04 21:18:51 Instead of adding and subtracting 1, how about doi
groby-ooo-7-16 2013/09/04 22:19:01 Done.
121 [arrow relativeLineToPoint:NSMakePoint(NSMidX(bounds) - arrowHalfWidth, 0)];
sail 2013/09/04 21:18:51 NSMidX(bounds) is absolute. To make it relative yo
groby-ooo-7-16 2013/09/04 22:19:01 Done.
122 [arrow relativeLineToPoint:NSMakePoint(arrowHalfWidth, kArrowHeight)];
123 [arrow relativeLineToPoint:NSMakePoint(arrowHalfWidth, -kArrowHeight)];
124 [arrow lineToPoint:NSMakePoint(NSMaxX(bounds) + 1.0, y)];
125 [arrow lineToPoint:NSMakePoint(NSMaxX(bounds) + 1.0, NSMinY(bounds) - 1.0)];
126 [arrow lineToPoint:NSMakePoint(NSMinX(bounds) - 1.0, NSMinY(bounds) - 1.0)];
127 [arrow closePath];
128
129 [shadingColor setFill];
130 [arrow fill];
131 [borderColor setStroke];
132 [arrow stroke];
133
134 [super drawRect:dirtyRect];
sail 2013/09/04 21:18:51 don't need
groby-ooo-7-16 2013/09/04 22:19:01 Done.
135 }
136
137 - (CGFloat)getHeightForWidth:(CGFloat)width {
138 CGFloat height = kOverlayTextInterlineSpacing;
139 for (NSTextView* label in [self subviews]) {
140 height += NSHeight([label frame]);
141 height += kOverlayTextInterlineSpacing;
142 }
143 return height + kArrowHeight;
144 }
145
146 - (void)setMessages:
147 (const std::vector<autofill::DialogOverlayString>&) messages {
148 // We probably want to look at other multi-line messages somewhere.
149 base::scoped_nsobject<NSMutableArray> labels(
150 [[NSMutableArray alloc] initWithCapacity:messages.size()]);
151 for (size_t i = 0; i < messages.size(); ++i) {
152 base::scoped_nsobject<NSTextField> label(
153 [[NSTextField alloc] initWithFrame:NSZeroRect]);
154
155 NSFont* labelFont = messages[i].font.GetNativeFont();
156 [label setEditable:NO];
157 [label setBordered:NO];
158 [label setDrawsBackground:NO];
159 [label setFont:labelFont];
160 [label setStringValue:base::SysUTF16ToNSString(messages[i].text)];
161 [label setTextColor:gfx::SkColorToDeviceNSColor(messages[i].text_color)];
162 DCHECK(messages[i].alignment == gfx::ALIGN_CENTER);
sail 2013/09/04 21:18:51 DCHECK_EQ?
groby-ooo-7-16 2013/09/04 22:19:01 Done.
163 [label setAlignment:NSCenterTextAlignment];
164 [label sizeToFit];
165
166 [labels addObject:label];
167 }
168 [self setSubviews:labels];
169 [self setHidden:([labels count] == 0)];
170 }
171
172 - (void)performLayout {
173 CGFloat y =
174 NSMaxY([self bounds]) - kArrowHeight - kOverlayTextInterlineSpacing;
175 for (NSTextView* label in [self subviews]) {
176 CGFloat labelHeight = NSHeight([label frame]);
sail 2013/09/04 21:18:51 DCHECK([label isKindOfClass:[NSTextField class])?
groby-ooo-7-16 2013/09/04 22:19:01 I suppose. Do we usually do that when iterating ov
sail 2013/09/04 23:08:25 Not usually. I'm just worried about the subviews b
groby-ooo-7-16 2013/09/05 00:44:20 Done.
177 [label setFrame:NSMakeRect(0, y - labelHeight,
178 NSWidth([self bounds]), labelHeight)];
179 y = NSMinY([label frame]) - kOverlayTextInterlineSpacing;
180 }
181 DCHECK_GT(0.0, y);
182 }
183
184 - (NSSize)preferredSize {
185 NOTREACHED();
186 return NSZeroSize;
187 }
188
189 @end
190
191
192 @implementation AutofillOverlayController
193
194 - (id)initWithDelegate:(autofill::AutofillDialogViewDelegate*)delegate {
195 if (self = [super init]) {
196 delegate_ = delegate;
197 refreshTimer_.reset(new OverlayTimerBridge(self));
198
199 view_.reset([[NSBox alloc] initWithFrame:NSZeroRect]);
200 [view_ setBoxType:NSBoxCustom];
201 [view_ setBorderType:NSNoBorder];
202 [view_ setContentViewMargins:NSZeroSize];
203 [view_ setTitlePosition:NSNoTitle];
204
205 childView_.reset([[NSView alloc] initWithFrame:NSZeroRect]);
206 messageStackView_.reset(
207 [[AutofillMessageStackView alloc] initWithFrame:NSZeroRect]);
208 imageView_.reset([[NSImageView alloc] initWithFrame:NSZeroRect]);
209 [imageView_ setImageAlignment:NSImageAlignCenter];
210
211 [childView_ setSubviews:@[messageStackView_, imageView_]];
212 [view_ addSubview:childView_];
213
214 [self setView:view_];
215 }
216 return self;
217 }
218
219 - (void)updateState {
220 const autofill::DialogOverlayState& state = delegate_->GetDialogOverlay();
sail 2013/09/04 21:18:51 move this below the if statement?
groby-ooo-7-16 2013/09/04 22:19:01 Done.
221
222 // Don't update anything if we're still fading out the old state.
223 if (fadeOutAnimation_)
224 return;
225
226 if (state.image.IsEmpty()) {
227 [view_ setHidden:YES];
228 return;
229 }
230
231 [view_ setFillColor:[[view_ window] backgroundColor]];
232 [view_ setAlphaValue:1];
233 [childView_ setAlphaValue:1];
234 [imageView_ setImage:state.image.ToNSImage()];
235 [messageStackView_ setMessages:state.strings];
236 [childView_ setHidden:NO];
237 [view_ setHidden:NO];
238
239 id delegate = [[[self view] window] windowController];
sail 2013/09/04 21:18:51 You can use the explicit type here since you're do
groby-ooo-7-16 2013/09/04 22:19:01 Done.
240 if ([delegate respondsToSelector:@selector(requestRelayout)])
sail 2013/09/04 21:18:51 I know that this pattern already existed in this c
groby-ooo-7-16 2013/09/04 22:19:01 Hm. I've been planning to actually have an Autofil
sail 2013/09/04 23:08:25 Ok, makes sense.
241 [delegate performSelector:@selector(requestRelayout)];
242
243 refreshTimer_->SetExpiry(state.expiry);
244 }
245
246 - (void)beginFadeOut {
247 // Remove first responders, since focus rings show on top of overlay view.
248 // TODO(groby): Figure out to do that less hacky. Ideally, the focus ring
249 // should be part of the controls fading in, not appear at the end.
250 [[self view] setNextResponder:[[[self view] window] firstResponder]];
251 [[[self view] window] makeFirstResponder:[self view]];
252
253 Parts parts;
254 // For this part of the animation, simply show the splash image.
255 parts.push_back(Part(autofill::kSplashDisplayDurationMs, ui::Tween::ZERO));
256 // For this part of the animation, fade out the splash image.
257 parts.push_back(
258 Part(autofill::kSplashFadeOutDurationMs, ui::Tween::EASE_IN));
259 // For this part of the animation, fade out |this| (fade in the dialog).
260 parts.push_back(
261 Part(autofill::kSplashFadeInDialogDurationMs, ui::Tween::EASE_OUT));
262 fadeOutAnimation_.reset(
263 new ui::MultiAnimation(parts,
264 ui::MultiAnimation::GetDefaultTimerInterval()));
265 animationDelegate_.reset(new AnimationDelegateBridge(self));
266 fadeOutAnimation_->set_delegate(animationDelegate_.get());
267 fadeOutAnimation_->set_continuous(false);
268 fadeOutAnimation_->Start();
269 }
270
271 - (int)getHeightForWidth:(int) width {
272 // 0 means "no preference". Image-only overlays fit the container.
273 if ([messageStackView_ isHidden])
274 return 0;
275
276 // Overlays with text messages express a size preference.
277 return kOverlayImageBottomMargin +
278 [messageStackView_ getHeightForWidth:width] +
279 NSHeight([imageView_ frame]);
280
281 return 0;
sail 2013/09/04 21:18:51 remove?
groby-ooo-7-16 2013/09/04 22:19:01 Indeed. Surprised clang didn't yell at me. Thanks
282 }
283
284 - (NSSize)preferredSize {
285 NOTREACHED(); // Only implemented as part of AutofillLayout protocol.
286 return NSZeroSize;
287 }
288
289 - (void)performLayout {
290 NSRect bounds = [view_ bounds];
291 [childView_ setFrame:bounds];
292 if ([messageStackView_ isHidden]) {
293 [imageView_ setFrame:bounds];
294 return;
295 }
296
297 int messageHeight = [messageStackView_ getHeightForWidth:NSWidth(bounds)];
298 [messageStackView_ setFrame:
299 NSMakeRect(0, 0, NSWidth(bounds), messageHeight)];
300 [messageStackView_ performLayout];
301
302 NSSize imageSize = [[imageView_ image] size];
303 [imageView_ setFrame:NSMakeRect(
304 0, NSMaxY([messageStackView_ frame]) + kOverlayImageBottomMargin,
305 NSWidth(bounds), imageSize.height)];
306 }
307
308 - (void)animationProgressed:(const ui::Animation*) animation {
sail 2013/09/04 21:18:51 no space before animation
groby-ooo-7-16 2013/09/04 22:19:01 Done.
309 DCHECK_EQ(animation, fadeOutAnimation_.get());
310
311 // Fade out children in stage 1.
312 if (fadeOutAnimation_->current_part_index() == 1) {
313 [childView_ setAlphaValue:(1 - fadeOutAnimation_->GetCurrentValue())];
314 }
315
316 // Fade out background in stage 2(i.e. fade in what's behind |this|).
317 if (fadeOutAnimation_->current_part_index() == 2) {
318 [view_ setAlphaValue: (1 - fadeOutAnimation_->GetCurrentValue())];
319 [childView_ setHidden:YES];
320 }
321
322 // If any fading was done, refresh display.
323 if (fadeOutAnimation_->current_part_index() != 0) {
324 [view_ setNeedsDisplay:YES];
325 }
326 }
327
328 - (void)animationEnded:(const ui::Animation*)animation {
329 DCHECK_EQ(animation, fadeOutAnimation_.get());
330 [[self view] setHidden:YES];
331 fadeOutAnimation_.reset();
332 }
333
334 @end
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698