Cast: Stop logging kVideoFrameSentToEncoder and rename a couple events.
[chromium-blink-merge.git] / chrome / browser / ui / cocoa / autofill / autofill_main_container.mm
blob1407faf8cd4004fd35b12639eec3422a98cef561
1 // Copyright (c) 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.
5 #import "chrome/browser/ui/cocoa/autofill/autofill_main_container.h"
7 #include <algorithm>
8 #include <cmath>
10 #include "base/mac/foundation_util.h"
11 #include "base/strings/sys_string_conversions.h"
12 #include "chrome/browser/ui/autofill/autofill_dialog_view_delegate.h"
13 #include "chrome/browser/ui/chrome_style.h"
14 #include "chrome/browser/ui/cocoa/autofill/autofill_dialog_constants.h"
15 #import "chrome/browser/ui/cocoa/constrained_window/constrained_window_button.h"
16 #import "chrome/browser/ui/cocoa/autofill/autofill_details_container.h"
17 #import "chrome/browser/ui/cocoa/autofill/autofill_notification_container.h"
18 #import "chrome/browser/ui/cocoa/autofill/autofill_tooltip_controller.h"
19 #import "chrome/browser/ui/cocoa/hyperlink_text_view.h"
20 #import "chrome/browser/ui/cocoa/key_equivalent_constants.h"
21 #include "grit/generated_resources.h"
22 #include "grit/theme_resources.h"
23 #include "skia/ext/skia_utils_mac.h"
24 #import "third_party/google_toolbox_for_mac/src/AppKit/GTMUILocalizerAndLayoutTweaker.h"
25 #import "ui/base/cocoa/controls/blue_label_button.h"
26 #include "ui/base/cocoa/window_size_constants.h"
27 #include "ui/base/l10n/l10n_util.h"
28 #include "ui/gfx/range/range.h"
30 namespace {
32 // Padding between buttons and the last suggestion or details view. The mock
33 // has a total of 30px - but 10px are already provided by details/suggestions.
34 const CGFloat kButtonVerticalPadding = 20.0;
36 // Padding around the text for the legal documents.
37 const CGFloat kLegalDocumentsPadding = 20.0;
39 // The font color for the legal documents text. Set to match the Views
40 // implementation.
41 const SkColor kLegalDocumentsTextColor = SkColorSetRGB(102, 102, 102);
43 }  // namespace
45 @interface AutofillMainContainer (Private)
46 - (void)buildWindowButtons;
47 - (void)layoutButtons;
48 - (void)updateButtons;
49 - (NSSize)preferredLegalDocumentSizeForWidth:(CGFloat)width;
50 @end
53 @implementation AutofillMainContainer
55 @synthesize target = target_;
57 - (id)initWithDelegate:(autofill::AutofillDialogViewDelegate*)delegate {
58   if (self = [super init]) {
59     delegate_ = delegate;
60   }
61   return self;
64 - (void)loadView {
65   [self buildWindowButtons];
67   base::scoped_nsobject<NSView> view([[NSView alloc] initWithFrame:NSZeroRect]);
68   [view setAutoresizesSubviews:YES];
69   [view setSubviews:@[buttonContainer_]];
70   [self setView:view];
72   // Set up Wallet icon.
73   buttonStripImage_.reset([[NSImageView alloc] initWithFrame:NSZeroRect]);
74   [self updateWalletIcon];
75   [[self view] addSubview:buttonStripImage_];
77   // Set up "Save in Chrome" checkbox.
78   saveInChromeCheckbox_.reset([[NSButton alloc] initWithFrame:NSZeroRect]);
79   [saveInChromeCheckbox_ setButtonType:NSSwitchButton];
80   [saveInChromeCheckbox_ setTitle:
81       base::SysUTF16ToNSString(delegate_->SaveLocallyText())];
82   [saveInChromeCheckbox_ sizeToFit];
83   [[self view] addSubview:saveInChromeCheckbox_];
85   saveInChromeTooltip_.reset(
86       [[AutofillTooltipController alloc]
87             initWithArrowLocation:info_bubble::kTopCenter]);
88   [saveInChromeTooltip_ setImage:
89       ui::ResourceBundle::GetSharedInstance().GetNativeImageNamed(
90           IDR_AUTOFILL_TOOLTIP_ICON).ToNSImage()];
91   [saveInChromeTooltip_ setMessage:
92       base::SysUTF16ToNSString(delegate_->SaveLocallyTooltip())];
93   [[self view] addSubview:[saveInChromeTooltip_ view]];
94   [self updateSaveInChrome];
96   detailsContainer_.reset(
97       [[AutofillDetailsContainer alloc] initWithDelegate:delegate_]);
98   NSSize frameSize = [[detailsContainer_ view] frame].size;
99   [[detailsContainer_ view] setFrameOrigin:
100       NSMakePoint(0, NSHeight([buttonContainer_ frame]))];
101   frameSize.height += NSHeight([buttonContainer_ frame]);
102   [[self view] setFrameSize:frameSize];
103   [[self view] addSubview:[detailsContainer_ view]];
105   legalDocumentsView_.reset(
106       [[HyperlinkTextView alloc] initWithFrame:NSZeroRect]);
107   [legalDocumentsView_ setEditable:NO];
108   [legalDocumentsView_ setBackgroundColor:
109       [NSColor colorWithCalibratedRed:0.96
110                                 green:0.96
111                                  blue:0.96
112                                 alpha:1.0]];
113   [legalDocumentsView_ setDrawsBackground:YES];
114   [legalDocumentsView_ setTextContainerInset:
115       NSMakeSize(kLegalDocumentsPadding, kLegalDocumentsPadding)];
116   [legalDocumentsView_ setHidden:YES];
117   [legalDocumentsView_ setDelegate:self];
118   legalDocumentsSizeDirty_ = YES;
119   [[self view] addSubview:legalDocumentsView_];
121   notificationContainer_.reset(
122       [[AutofillNotificationContainer alloc] initWithDelegate:delegate_]);
123   [[self view] addSubview:[notificationContainer_ view]];
126 // Called when embedded links are clicked.
127 - (BOOL)textView:(NSTextView*)textView
128     clickedOnLink:(id)link
129           atIndex:(NSUInteger)charIndex {
130   int index = [base::mac::ObjCCastStrict<NSNumber>(link) intValue];
131   delegate_->LegalDocumentLinkClicked(
132       delegate_->LegalDocumentLinks()[index]);
133   return YES;
136 - (NSSize)decorationSizeForWidth:(CGFloat)width {
137   NSSize buttonSize = [buttonContainer_ frame].size;
138   NSSize buttonStripImageSize = [buttonStripImage_ frame].size;
139   NSSize buttonStripSize =
140       NSMakeSize(buttonSize.width + chrome_style::kHorizontalPadding +
141                      buttonStripImageSize.width,
142                  std::max(buttonSize.height + kButtonVerticalPadding,
143                           buttonStripImageSize.height) +
144                      chrome_style::kClientBottomPadding);
146   NSSize size = NSMakeSize(std::max(buttonStripSize.width, width),
147                            buttonStripSize.height);
148   if (![legalDocumentsView_ isHidden]) {
149     NSSize legalDocumentSize =
150         [self preferredLegalDocumentSizeForWidth:width];
151     size.height += legalDocumentSize.height + autofill::kVerticalSpacing;
152   }
154   NSSize notificationSize =
155       [notificationContainer_ preferredSizeForWidth:width];
156   size.height += notificationSize.height;
158   return size;
161 - (NSSize)preferredSize {
162   NSSize detailsSize = [detailsContainer_ preferredSize];
163   NSSize decorationSize = [self decorationSizeForWidth:detailsSize.width];
165   NSSize size = NSMakeSize(std::max(decorationSize.width, detailsSize.width),
166                            decorationSize.height + detailsSize.height);
167   size.height += autofill::kDetailVerticalPadding;
169   return size;
172 - (void)performLayout {
173   NSRect bounds = [[self view] bounds];
175   CGFloat currentY = 0.0;
176   if (![legalDocumentsView_ isHidden]) {
177     [legalDocumentsView_ setFrameSize:
178         [self preferredLegalDocumentSizeForWidth:NSWidth(bounds)]];
179     currentY = NSMaxY([legalDocumentsView_ frame]) + autofill::kVerticalSpacing;
180   }
182   NSRect buttonFrame = [buttonContainer_ frame];
183   buttonFrame.origin.y = currentY + chrome_style::kClientBottomPadding;
184   [buttonContainer_ setFrameOrigin:buttonFrame.origin];
185   currentY = NSMaxY(buttonFrame) + kButtonVerticalPadding;
187   NSPoint walletIconOrigin =
188       NSMakePoint(chrome_style::kHorizontalPadding, buttonFrame.origin.y);
189   [buttonStripImage_ setFrameOrigin:walletIconOrigin];
190   currentY = std::max(currentY, NSMaxY([buttonStripImage_ frame]));
192   NSRect checkboxFrame = [saveInChromeCheckbox_ frame];
193   [saveInChromeCheckbox_ setFrameOrigin:
194       NSMakePoint(chrome_style::kHorizontalPadding,
195                   NSMidY(buttonFrame) - NSHeight(checkboxFrame) / 2.0)];
197   NSRect tooltipFrame = [[saveInChromeTooltip_ view] frame];
198   [[saveInChromeTooltip_ view] setFrameOrigin:
199       NSMakePoint(NSMaxX([saveInChromeCheckbox_ frame]) + autofill::kButtonGap,
200                   NSMidY(buttonFrame) - (NSHeight(tooltipFrame) / 2.0))];
202   NSRect notificationFrame = NSZeroRect;
203   notificationFrame.size = [notificationContainer_ preferredSizeForWidth:
204       NSWidth(bounds)];
206   // Buttons/checkbox/legal take up lower part of view, notifications the
207   // upper part. Adjust the detailsContainer to take up the remainder.
208   CGFloat remainingHeight =
209       NSHeight(bounds) - currentY - NSHeight(notificationFrame);
210   NSRect containerFrame =
211       NSMakeRect(0, currentY, NSWidth(bounds), remainingHeight);
212   [[detailsContainer_ view] setFrame:containerFrame];
213   [detailsContainer_ performLayout];
215   notificationFrame.origin = NSMakePoint(0, NSMaxY(containerFrame));
216   [[notificationContainer_ view] setFrame:notificationFrame];
217   [notificationContainer_ performLayout];
220 - (void)buildWindowButtons {
221   if (buttonContainer_.get())
222     return;
224   buttonContainer_.reset([[GTMWidthBasedTweaker alloc] initWithFrame:
225       ui::kWindowSizeDeterminedLater]);
226   [buttonContainer_
227       setAutoresizingMask:(NSViewMinXMargin | NSViewMaxYMargin)];
229   base::scoped_nsobject<NSButton> button(
230       [[ConstrainedWindowButton alloc] initWithFrame:NSZeroRect]);
231   [button setKeyEquivalent:kKeyEquivalentEscape];
232   [button setTarget:target_];
233   [button setAction:@selector(cancel:)];
234   [button sizeToFit];
235   [buttonContainer_ addSubview:button];
237   CGFloat nextX = NSMaxX([button frame]) + autofill::kButtonGap;
238   button.reset([[BlueLabelButton alloc] initWithFrame:NSZeroRect]);
239   [button setFrameOrigin:NSMakePoint(nextX, 0)];
240   [button setKeyEquivalent:kKeyEquivalentReturn];
241   [button setTarget:target_];
242   [button setAction:@selector(accept:)];
243   [buttonContainer_ addSubview:button];
244   [self updateButtons];
246   NSRect frame = NSMakeRect(
247       -NSMaxX([button frame]) - chrome_style::kHorizontalPadding, 0,
248       NSMaxX([button frame]), NSHeight([button frame]));
249   [buttonContainer_ setFrame:frame];
252 - (void)layoutButtons {
253   base::scoped_nsobject<GTMUILocalizerAndLayoutTweaker> layoutTweaker(
254       [[GTMUILocalizerAndLayoutTweaker alloc] init]);
255   [layoutTweaker tweakUI:buttonContainer_];
257   // Now ensure both buttons have the same height. The second button is
258   // known to be the larger one.
259   CGFloat buttonHeight =
260       NSHeight([[[buttonContainer_ subviews] objectAtIndex:1] frame]);
262   // Force first button to be the same height.
263   NSView* button = [[buttonContainer_ subviews] objectAtIndex:0];
264   NSSize buttonSize = [button frame].size;
265   buttonSize.height = buttonHeight;
266   [button setFrameSize:buttonSize];
269 - (void)updateButtons {
270   NSButton* button = base::mac::ObjCCastStrict<NSButton>(
271       [[buttonContainer_ subviews] objectAtIndex:0]);
272   [button setTitle:base::SysUTF16ToNSString(delegate_->CancelButtonText())];
273   button = base::mac::ObjCCastStrict<NSButton>(
274     [[buttonContainer_ subviews] objectAtIndex:1]);
275   [button setTitle:base::SysUTF16ToNSString(delegate_->ConfirmButtonText())];
276   [self layoutButtons];
279 // Compute the preferred size for the legal documents text, given a width.
280 - (NSSize)preferredLegalDocumentSizeForWidth:(CGFloat)width {
281   // Only recompute if necessary (On text or frame width change).
282   if (!legalDocumentsSizeDirty_ &&
283       std::abs(legalDocumentsSize_.width - width) < 1.0) {
284     return legalDocumentsSize_;
285   }
287   // There's no direct API to compute desired sizes - use layouting instead.
288   // Layout in a rect with fixed width and "infinite" height.
289   NSRect currentFrame = [legalDocumentsView_ frame];
290   [legalDocumentsView_ setFrame:NSMakeRect(0, 0, width, CGFLOAT_MAX)];
292   // Now use the layout manager to compute layout.
293   NSLayoutManager* layoutManager = [legalDocumentsView_ layoutManager];
294   NSTextContainer* textContainer = [legalDocumentsView_ textContainer];
295   [layoutManager ensureLayoutForTextContainer:textContainer];
296   NSRect newFrame = [layoutManager usedRectForTextContainer:textContainer];
298   // And finally, restore old frame.
299   [legalDocumentsView_ setFrame:currentFrame];
300   newFrame.size.width = width;
302   // Account for the padding around the text.
303   newFrame.size.height += 2 * kLegalDocumentsPadding;
305   legalDocumentsSizeDirty_ = NO;
306   legalDocumentsSize_ = newFrame.size;
307   return legalDocumentsSize_;
310 - (AutofillSectionContainer*)sectionForId:(autofill::DialogSection)section {
311   return [detailsContainer_ sectionForId:section];
314 - (void)modelChanged {
315   [self updateSaveInChrome];
316   [self updateWalletIcon];
317   [self updateButtons];
318   [detailsContainer_ modelChanged];
321 - (BOOL)saveDetailsLocally {
322   return [saveInChromeCheckbox_ state] == NSOnState;
325 - (void)updateLegalDocuments {
326   NSString* text = base::SysUTF16ToNSString(delegate_->LegalDocumentsText());
328   if ([text length]) {
329     NSFont* font =
330         [NSFont labelFontOfSize:[[legalDocumentsView_ font] pointSize]];
331     NSColor* color = gfx::SkColorToCalibratedNSColor(kLegalDocumentsTextColor);
332     [legalDocumentsView_ setMessage:text withFont:font messageColor:color];
334     const std::vector<gfx::Range>& link_ranges =
335         delegate_->LegalDocumentLinks();
336     for (size_t i = 0; i < link_ranges.size(); ++i) {
337       NSRange range = link_ranges[i].ToNSRange();
338       [legalDocumentsView_ addLinkRange:range
339                                withName:@(i)
340                               linkColor:[NSColor blueColor]];
341     }
342     legalDocumentsSizeDirty_ = YES;
343   }
344   [legalDocumentsView_ setHidden:[text length] == 0];
346   // Always request re-layout on state change.
347   id delegate = [[[self view] window] windowController];
348   if ([delegate respondsToSelector:@selector(requestRelayout)])
349     [delegate performSelector:@selector(requestRelayout)];
352 - (void)updateNotificationArea {
353   [notificationContainer_ setNotifications:delegate_->CurrentNotifications()];
354   id delegate = [[[self view] window] windowController];
355   if ([delegate respondsToSelector:@selector(requestRelayout)])
356     [delegate performSelector:@selector(requestRelayout)];
359 - (void)setAnchorView:(NSView*)anchorView {
360   [notificationContainer_ setAnchorView:anchorView];
363 - (BOOL)validate {
364   return [detailsContainer_ validate];
367 - (void)updateSaveInChrome {
368   [saveInChromeCheckbox_ setHidden:!delegate_->ShouldOfferToSaveInChrome()];
369   [[saveInChromeTooltip_ view] setHidden:[saveInChromeCheckbox_ isHidden]];
370   [saveInChromeCheckbox_ setState:
371       (delegate_->ShouldSaveInChrome() ? NSOnState : NSOffState)];
374 - (void)makeFirstInvalidInputFirstResponder {
375   NSView* field = [detailsContainer_ firstInvalidField];
376   if (!field)
377     return;
379   [detailsContainer_ scrollToView:field];
380   [[[self view] window] makeFirstResponder:field];
383 - (void)updateWalletIcon {
384   gfx::Image image = delegate_->ButtonStripImage();
385   [buttonStripImage_ setHidden:image.IsEmpty()];
386   if (![buttonStripImage_ isHidden]) {
387     [buttonStripImage_ setImage:image.ToNSImage()];
388     [buttonStripImage_ setFrameSize:[[buttonStripImage_ image] size]];
389   }
392 - (void)scrollInitialEditorIntoViewAndMakeFirstResponder {
393   // Try to focus on the first invalid field. If there isn't one, focus on the
394   // first editable field instead.
395   NSView* field = [detailsContainer_ firstInvalidField];
396   if (!field)
397     field = [detailsContainer_ firstVisibleField];
398   if (!field)
399     return;
401   [detailsContainer_ scrollToView:field];
402   [[[self view] window] makeFirstResponder:field];
405 - (void)updateErrorBubble {
406   [detailsContainer_ updateErrorBubble];
409 @end
412 @implementation AutofillMainContainer (Testing)
414 - (NSButton*)saveInChromeCheckboxForTesting {
415   return saveInChromeCheckbox_.get();
418 - (NSImageView*)buttonStripImageForTesting {
419   return buttonStripImage_.get();
422 - (NSButton*)saveInChromeTooltipForTesting {
423   return base::mac::ObjCCast<NSButton>([saveInChromeTooltip_ view]);
426 @end