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

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: Final tweaks. 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/mac/foundation_util.h"
9 #include "base/strings/sys_string_conversions.h"
10 #include "base/timer/timer.h"
11 #include "chrome/browser/ui/autofill/autofill_dialog_types.h"
12 #include "chrome/browser/ui/autofill/autofill_dialog_view_delegate.h"
13 #include "chrome/browser/ui/cocoa/autofill/autofill_dialog_constants.h"
14 #include "skia/ext/skia_utils_mac.h"
15 #include "ui/base/animation/animation_delegate.h"
16 #include "ui/base/animation/multi_animation.h"
17
18 namespace {
19
20 // Spacing between lines of text in the overlay view.
21 const CGFloat kOverlayTextInterlineSpacing = 10;
22
23 // Spacing below image and above text messages in overlay view.
24 const CGFloat kOverlayImageBottomMargin = 50;
25
26 // TODO(groby): Unify colors with Views.
27 // Slight shading for mouse hover and legal document background.
28 SkColor kShadingColor = 0xfff2f2f2;
29
30 // A border color for the legal document view.
31 SkColor kSubtleBorderColor = 0xffdfdfdf;
32
33 // Shorten a few long types.
34 typedef ui::MultiAnimation::Part Part;
35 typedef ui::MultiAnimation::Parts Parts;
36
37 } // namespace
38
39 // Bridges Objective C and C++ delegate interfaces.
40 class AnimationDelegateBridge : public ui::AnimationDelegate {
41 public:
42 AnimationDelegateBridge(id<AnimationDelegate> delegate);
43
44 protected:
45 // AnimationDelegate implementation.
46 virtual void AnimationProgressed(const ui::Animation* animation) OVERRIDE;
47 virtual void AnimationEnded(const ui::Animation* animation) OVERRIDE;
48
49 private:
50 id<AnimationDelegate> delegate_; // Not owned. Owns DelegateBridge.
51 DISALLOW_COPY_AND_ASSIGN(AnimationDelegateBridge);
52 };
53
54 AnimationDelegateBridge::AnimationDelegateBridge(
55 id<AnimationDelegate> delegate) : delegate_(delegate) {}
56
57 void AnimationDelegateBridge::AnimationProgressed(
58 const ui::Animation* animation) {
59 [delegate_ animationProgressed:animation];
60 }
61
62 void AnimationDelegateBridge::AnimationEnded(
63 const ui::Animation* animation) {
64 [delegate_ animationEnded:animation];
65 }
66
67 class OverlayTimerBridge {
68 public:
69 OverlayTimerBridge(AutofillOverlayController* controller);
70 void SetExpiry(const base::TimeDelta& delta);
71
72 private:
73 void UpdateOverlayState();
74
75 // Controls when the overlay should request a status update.
76 base::OneShotTimer<OverlayTimerBridge> refresh_timer_;
77
78 // not owned, |overlay_view_controller_| owns |this|.
79 AutofillOverlayController* overlay_view_controller_;
80
81 DISALLOW_COPY_AND_ASSIGN(OverlayTimerBridge);
82 };
83
84 OverlayTimerBridge::OverlayTimerBridge(AutofillOverlayController* controller)
85 : overlay_view_controller_(controller) {
86 }
87
88 void OverlayTimerBridge::SetExpiry(const base::TimeDelta& expiry) {
89 if (expiry != base::TimeDelta()) {
90 refresh_timer_.Start(FROM_HERE,
91 expiry,
92 this,
93 &OverlayTimerBridge::UpdateOverlayState);
94 } else {
95 refresh_timer_.Stop();
96 }
97 }
98
99 void OverlayTimerBridge::UpdateOverlayState() {
100 [overlay_view_controller_ updateState];
101 }
102
103 // An NSView encapsulating the message stack and its custom drawn elements.
104 @interface AutofillMessageStackView : NSView<AutofillLayout>
105 - (CGFloat)heightForWidth:(CGFloat)width;
106 - (void)setMessages:
107 (const std::vector<autofill::DialogOverlayString>&)messages;
108 @end
109
110
111 @implementation AutofillMessageStackView
112
113 - (void)drawRect:(NSRect)dirtyRect {
114 NSColor* shadingColor = gfx::SkColorToCalibratedNSColor(kShadingColor);
115 NSColor* borderColor = gfx::SkColorToCalibratedNSColor(kSubtleBorderColor);
116
117 CGFloat arrowHalfWidth = kArrowWidth / 2.0;
118 NSRect bounds = [self bounds];
119 CGFloat y = NSMaxY(bounds) - kArrowHeight;
120
121 NSBezierPath* arrow = [NSBezierPath bezierPath];
122 // Note that we purposely draw slightly outside of |bounds| so that the
123 // stroke is hidden on the sides and bottom.
124 NSRect arrowBounds = NSInsetRect(bounds, -1, -1);
125 arrowBounds.size.height--;
126 [arrow moveToPoint:NSMakePoint(NSMinX(arrowBounds), y)];
127 [arrow lineToPoint:
128 NSMakePoint(NSMidX(arrowBounds) - arrowHalfWidth, y)];
129 [arrow relativeLineToPoint:NSMakePoint(arrowHalfWidth, kArrowHeight)];
130 [arrow relativeLineToPoint:NSMakePoint(arrowHalfWidth, -kArrowHeight)];
131 [arrow lineToPoint:NSMakePoint(NSMaxX(arrowBounds), y)];
132 [arrow lineToPoint:NSMakePoint(NSMaxX(arrowBounds), NSMinY(arrowBounds))];
133 [arrow lineToPoint:NSMakePoint(NSMinX(arrowBounds), NSMinY(arrowBounds))];
134 [arrow closePath];
135
136 [shadingColor setFill];
137 [arrow fill];
138 [borderColor setStroke];
139 [arrow stroke];
140 }
141
142 - (CGFloat)heightForWidth:(CGFloat)width {
143 CGFloat height = kOverlayTextInterlineSpacing;
144 for (NSTextView* label in [self subviews]) {
145 height += NSHeight([label frame]);
146 height += kOverlayTextInterlineSpacing;
147 }
148 return height + kArrowHeight;
149 }
150
151 - (void)setMessages:
152 (const std::vector<autofill::DialogOverlayString>&) messages {
153 // We probably want to look at other multi-line messages somewhere.
154 base::scoped_nsobject<NSMutableArray> labels(
155 [[NSMutableArray alloc] initWithCapacity:messages.size()]);
156 for (size_t i = 0; i < messages.size(); ++i) {
157 base::scoped_nsobject<NSTextField> label(
158 [[NSTextField alloc] initWithFrame:NSZeroRect]);
159
160 NSFont* labelFont = messages[i].font.GetNativeFont();
161 [label setEditable:NO];
162 [label setBordered:NO];
163 [label setDrawsBackground:NO];
164 [label setFont:labelFont];
165 [label setStringValue:base::SysUTF16ToNSString(messages[i].text)];
166 [label setTextColor:gfx::SkColorToDeviceNSColor(messages[i].text_color)];
167 DCHECK_EQ(messages[i].alignment, gfx::ALIGN_CENTER);
168 [label setAlignment:NSCenterTextAlignment];
169 [label sizeToFit];
170
171 [labels addObject:label];
172 }
173 [self setSubviews:labels];
174 [self setHidden:([labels count] == 0)];
175 }
176
177 - (void)performLayout {
178 CGFloat y =
179 NSMaxY([self bounds]) - kArrowHeight - kOverlayTextInterlineSpacing;
180 for (NSTextView* label in [self subviews]) {
181 DCHECK([label isKindOfClass:[NSTextView class]]);
182 CGFloat labelHeight = NSHeight([label frame]);
183 [label setFrame:NSMakeRect(0, y - labelHeight,
184 NSWidth([self bounds]), labelHeight)];
185 y = NSMinY([label frame]) - kOverlayTextInterlineSpacing;
186 }
187 DCHECK_GT(0.0, y);
188 }
189
190 - (NSSize)preferredSize {
191 NOTREACHED();
192 return NSZeroSize;
193 }
194
195 @end
196
197
198 @implementation AutofillOverlayController
199
200 - (id)initWithDelegate:(autofill::AutofillDialogViewDelegate*)delegate {
201 if (self = [super initWithNibName:nil bundle:nil]) {
202 delegate_ = delegate;
203 refreshTimer_.reset(new OverlayTimerBridge(self));
204
205 base::scoped_nsobject<NSBox> view(
206 [[NSBox alloc] initWithFrame:NSZeroRect]);
207 [view setBoxType:NSBoxCustom];
208 [view setBorderType:NSNoBorder];
209 [view setContentViewMargins:NSZeroSize];
210 [view setTitlePosition:NSNoTitle];
211
212 childView_.reset([[NSView alloc] initWithFrame:NSZeroRect]);
213 messageStackView_.reset(
214 [[AutofillMessageStackView alloc] initWithFrame:NSZeroRect]);
215 imageView_.reset([[NSImageView alloc] initWithFrame:NSZeroRect]);
216 [imageView_ setImageAlignment:NSImageAlignCenter];
217
218 [childView_ setSubviews:@[messageStackView_, imageView_]];
219 [view addSubview:childView_];
220 [self setView:view];
221 }
222 return self;
223 }
224
225 - (void)updateState {
226 [self setState:delegate_->GetDialogOverlay()];
227 }
228
229 - (void)setState:(const autofill::DialogOverlayState&)state {
230 // Don't update anything if we're still fading out the old state.
231 if (fadeOutAnimation_)
232 return;
233
234 if (state.image.IsEmpty()) {
235 [[self view] setHidden:YES];
236 return;
237 }
238
239 NSBox* view = base::mac::ObjCCastStrict<NSBox>([self view]);
240 [view setFillColor:[[view window] backgroundColor]];
241 [view setAlphaValue:1];
242 [childView_ setAlphaValue:1];
243 [imageView_ setImage:state.image.ToNSImage()];
244 [messageStackView_ setMessages:state.strings];
245 [childView_ setHidden:NO];
246 [view setHidden:NO];
247
248 NSWindowController* delegate = [[[self view] window] windowController];
249 if ([delegate respondsToSelector:@selector(requestRelayout)])
250 [delegate performSelector:@selector(requestRelayout)];
251
252 refreshTimer_->SetExpiry(state.expiry);
253 }
254
255 - (void)beginFadeOut {
256 // Remove first responders, since focus rings show on top of overlay view.
257 // TODO(groby): Figure out to do that less hacky. Ideally, the focus ring
258 // should be part of the controls fading in, not appear at the end.
259 [[self view] setNextResponder:[[[self view] window] firstResponder]];
260 [[[self view] window] makeFirstResponder:[self view]];
261
262 Parts parts;
263 // For this part of the animation, simply show the splash image.
264 parts.push_back(Part(autofill::kSplashDisplayDurationMs, ui::Tween::ZERO));
265 // For this part of the animation, fade out the splash image.
266 parts.push_back(
267 Part(autofill::kSplashFadeOutDurationMs, ui::Tween::EASE_IN));
268 // For this part of the animation, fade out |this| (fade in the dialog).
269 parts.push_back(
270 Part(autofill::kSplashFadeInDialogDurationMs, ui::Tween::EASE_OUT));
271 fadeOutAnimation_.reset(
272 new ui::MultiAnimation(parts,
273 ui::MultiAnimation::GetDefaultTimerInterval()));
274 animationDelegate_.reset(new AnimationDelegateBridge(self));
275 fadeOutAnimation_->set_delegate(animationDelegate_.get());
276 fadeOutAnimation_->set_continuous(false);
277 fadeOutAnimation_->Start();
278 }
279
280 - (CGFloat)heightForWidth:(int) width {
281 // 0 means "no preference". Image-only overlays fit the container.
282 if ([messageStackView_ isHidden])
283 return 0;
284
285 // Overlays with text messages express a size preference.
286 return kOverlayImageBottomMargin +
287 [messageStackView_ heightForWidth:width] +
288 NSHeight([imageView_ frame]);
289 }
290
291 - (NSSize)preferredSize {
292 NOTREACHED(); // Only implemented as part of AutofillLayout protocol.
293 return NSZeroSize;
294 }
295
296 - (void)performLayout {
297 NSRect bounds = [[self view] bounds];
298 [childView_ setFrame:bounds];
299 if ([messageStackView_ isHidden]) {
300 [imageView_ setFrame:bounds];
301 return;
302 }
303
304 int messageHeight = [messageStackView_ heightForWidth:NSWidth(bounds)];
305 [messageStackView_ setFrame:
306 NSMakeRect(0, 0, NSWidth(bounds), messageHeight)];
307 [messageStackView_ performLayout];
308
309 NSSize imageSize = [[imageView_ image] size];
310 [imageView_ setFrame:NSMakeRect(
311 0, NSMaxY([messageStackView_ frame]) + kOverlayImageBottomMargin,
312 NSWidth(bounds), imageSize.height)];
313 }
314
315 - (void)animationProgressed:(const ui::Animation*)animation {
316 DCHECK_EQ(animation, fadeOutAnimation_.get());
317
318 // Fade out children in stage 1.
319 if (fadeOutAnimation_->current_part_index() == 1) {
320 [childView_ setAlphaValue:(1 - fadeOutAnimation_->GetCurrentValue())];
321 }
322
323 // Fade out background in stage 2(i.e. fade in what's behind |this|).
324 if (fadeOutAnimation_->current_part_index() == 2) {
325 [[self view] setAlphaValue: (1 - fadeOutAnimation_->GetCurrentValue())];
326 [childView_ setHidden:YES];
327 }
328
329 // If any fading was done, refresh display.
330 if (fadeOutAnimation_->current_part_index() != 0) {
331 [[self view] setNeedsDisplay:YES];
332 }
333 }
334
335 - (void)animationEnded:(const ui::Animation*)animation {
336 DCHECK_EQ(animation, fadeOutAnimation_.get());
337 [[self view] setHidden:YES];
338 fadeOutAnimation_.reset();
339 }
340
341 @end
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698