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