Popular sites on the NTP: check that experiment group StartsWith (rather than IS...
[chromium-blink-merge.git] / chrome / browser / ui / cocoa / constrained_window / constrained_window_alert.mm
blob7d48561e03193cfaa14aaf37aad1b5b23a47ab04
1 // Copyright (c) 2012 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/constrained_window/constrained_window_alert.h"
7 #import "base/logging.h"
8 #import "chrome/browser/ui/chrome_style.h"
9 #import "chrome/browser/ui/cocoa/constrained_window/constrained_window_button.h"
10 #import "chrome/browser/ui/cocoa/constrained_window/constrained_window_control_utils.h"
11 #import "chrome/browser/ui/cocoa/constrained_window/constrained_window_custom_window.h"
12 #import "chrome/browser/ui/cocoa/hover_close_button.h"
13 #include "skia/ext/skia_utils_mac.h"
14 #include "third_party/google_toolbox_for_mac/src/AppKit/GTMUILocalizerAndLayoutTweaker.h"
15 #include "ui/base/cocoa/controls/hyperlink_button_cell.h"
16 #include "ui/base/cocoa/window_size_constants.h"
18 namespace {
20 const CGFloat kWindowMinWidth = 500;
21 const CGFloat kButtonGap = 6;
23 }  // namespace
25 @interface ConstrainedWindowAlert ()
26 // Position the alert buttons within the given window width.
27 - (void)layoutButtonsWithWindowWidth:(CGFloat)windowWidth;
28 // Resize the given text field to fit within the window and position it starting
29 // at |yPos|. Returns the new value of yPos.
30 - (CGFloat)layoutTextField:(NSTextField*)textField
31                       yPos:(CGFloat)yPos
32                windowWidth:(CGFloat)windowWidth;
33 // If a link has been set, resizes the link to fit within the window and
34 // position it starting at |yPos|. Returns the new value of yPos.
35 - (CGFloat)layoutLinkAtYPos:(CGFloat)yPos
36                 windowWidth:(CGFloat)windowWidth;
37 // Positions the accessory view starting at yPos. Returns the new value of yPos.
38 - (CGFloat)layoutAccessoryViewAtYPos:(CGFloat)yPos;
39 // Update the position of the close button.
40 - (void)layoutCloseButtonWithWindowWidth:(CGFloat)windowWidth
41                             windowHeight:(CGFloat)windowHeight;
42 @end
44 @implementation ConstrainedWindowAlert
46 - (id)init {
47   if ((self = [super init])) {
48     window_.reset([[ConstrainedWindowCustomWindow alloc]
49         initWithContentRect:ui::kWindowSizeDeterminedLater]);
50     NSView* contentView = [window_ contentView];
52     informativeTextField_.reset([constrained_window::CreateLabel() retain]);
53     [contentView addSubview:informativeTextField_];
54     messageTextField_.reset([constrained_window::CreateLabel() retain]);
55     [contentView addSubview:messageTextField_];
57     closeButton_.reset(
58         [[WebUIHoverCloseButton alloc] initWithFrame:NSZeroRect]);
59     [contentView addSubview:closeButton_];
60   }
61   return self;
64 - (NSString*)informativeText {
65   return [informativeTextField_ stringValue];
68 - (void)setInformativeText:(NSString*)string {
69   [informativeTextField_ setAttributedStringValue:
70       constrained_window::GetAttributedLabelString(
71           string,
72           chrome_style::kTextFontStyle,
73           NSNaturalTextAlignment,
74           NSLineBreakByWordWrapping)];
77 - (NSString*)messageText {
78   return [messageTextField_ stringValue];
81 - (void)setMessageText:(NSString*)string {
82   [messageTextField_ setAttributedStringValue:
83       constrained_window::GetAttributedLabelString(
84           string,
85           chrome_style::kTitleFontStyle,
86           NSNaturalTextAlignment,
87           NSLineBreakByWordWrapping)];
90 - (void)setLinkText:(NSString*)text target:(id)target action:(SEL)action {
91   if (![text length]) {
92     [linkView_ removeFromSuperview];
93     linkView_.reset(nil);
94     return;
95   }
97   if (!linkView_.get()) {
98     linkView_.reset(
99         [[HyperlinkButtonCell buttonWithString:[NSString string]] retain]);
100     [[window_ contentView] addSubview:linkView_];
101   }
103   [linkView_ setTitle:text];
104   [linkView_ setTarget:target];
105   [linkView_ setAction:action];
108 - (NSView*)accessoryView {
109   return accessoryView_;
112 - (void)setAccessoryView:(NSView*)accessoryView {
113   [accessoryView_ removeFromSuperview];
114   accessoryView_.reset([accessoryView retain]);
115   [[window_ contentView] addSubview:accessoryView_];
118 - (NSArray*)buttons {
119   return buttons_;
122 - (NSButton*)closeButton {
123   return closeButton_;
126 - (NSWindow*)window {
127   return window_;
130 - (void)addButtonWithTitle:(NSString*)title
131              keyEquivalent:(NSString*)keyEquivalent
132                     target:(id)target
133                     action:(SEL)action {
134   if (!buttons_.get())
135     buttons_.reset([[NSMutableArray alloc] init]);
136   base::scoped_nsobject<NSButton> button(
137       [[ConstrainedWindowButton alloc] initWithFrame:NSZeroRect]);
138   [button setTitle:title];
139   [button setKeyEquivalent:keyEquivalent];
140   [button setTarget:target];
141   [button setAction:action];
142   [buttons_ addObject:button];
143   [[window_ contentView] addSubview:button];
146 - (void)layout {
147   // Button width.
148   CGFloat buttonWidth = 0;
149   for (NSButton* button in buttons_.get()) {
150     [button sizeToFit];
151     NSSize size = [button frame].size;
152     buttonWidth += size.width;
153   }
154   if ([buttons_ count])
155     buttonWidth += ([buttons_ count] - 1) * kButtonGap;
157   // Window width.
158   CGFloat windowWidth = buttonWidth;
159   if (accessoryView_.get())
160     windowWidth = std::max(windowWidth, NSWidth([accessoryView_ frame]));
161   windowWidth += chrome_style::kHorizontalPadding * 2;
162   windowWidth = std::max(windowWidth, kWindowMinWidth);
164   // Layout controls.
165   [self layoutButtonsWithWindowWidth:windowWidth];
166   CGFloat curY = [buttons_ count] ? NSMaxY([[buttons_ lastObject] frame])
167       : chrome_style::kClientBottomPadding;
168   CGFloat availableMessageWidth =
169       windowWidth - chrome_style::GetCloseButtonSize() - kButtonGap;
170   curY = [self layoutLinkAtYPos:curY
171                     windowWidth:availableMessageWidth];
172   curY = [self layoutAccessoryViewAtYPos:curY];
173   curY = [self layoutTextField:informativeTextField_
174                           yPos:curY
175                    windowWidth:windowWidth];
176   curY = [self layoutTextField:messageTextField_
177                           yPos:curY
178                    windowWidth:availableMessageWidth];
180   CGFloat windowHeight = curY + chrome_style::kTitleTopPadding;
181   [self layoutCloseButtonWithWindowWidth:windowWidth
182                             windowHeight:windowHeight];
184   // Update window frame.
185   NSRect windowFrame = NSMakeRect(0, 0, windowWidth, windowHeight);
186   windowFrame = [window_ frameRectForContentRect:windowFrame];
187   [window_ setFrame:windowFrame display:NO];
190 - (void)layoutButtonsWithWindowWidth:(CGFloat)windowWidth {
191   // Layout first 2 button right to left.
192   CGFloat curX = windowWidth - chrome_style::kHorizontalPadding;
193   const int buttonCount = [buttons_ count];
194   for (int i = 0; i < std::min(2, buttonCount); ++i) {
195     NSButton* button = [buttons_ objectAtIndex:i];
196     NSRect rect = [button frame];
197     rect.origin.x = curX - NSWidth(rect);
198     rect.origin.y = chrome_style::kClientBottomPadding;
199     [button setFrameOrigin:rect.origin];
200     curX = NSMinX(rect) - kButtonGap;
201   }
203   // Layout remaining buttons left to right.
204   curX = chrome_style::kHorizontalPadding;
205   for (int i = buttonCount - 1; i >= 2; --i) {
206     NSButton* button = [buttons_ objectAtIndex:i];
207     [button setFrameOrigin:
208         NSMakePoint(curX, chrome_style::kClientBottomPadding)];
209     curX += NSMaxX([button frame]) + kButtonGap;
210   }
213 - (CGFloat)layoutTextField:(NSTextField*)textField
214                       yPos:(CGFloat)yPos
215                windowWidth:(CGFloat)windowWidth {
216   if (![[textField stringValue] length]) {
217     [textField setHidden:YES];
218     return yPos;
219   }
221   [textField setHidden:NO];
222   NSRect rect;
223   rect.origin.y = yPos + chrome_style::kRowPadding;
224   rect.origin.x = chrome_style::kHorizontalPadding;
225   rect.size.width = windowWidth -
226       chrome_style::kHorizontalPadding * 2;
227   rect.size.height = 1;
228   [textField setFrame:rect];
229   [GTMUILocalizerAndLayoutTweaker sizeToFitFixedWidthTextField:textField];
230   return NSMaxY([textField frame]);
233 - (CGFloat)layoutLinkAtYPos:(CGFloat)yPos
234                 windowWidth:(CGFloat)windowWidth {
235   if (!linkView_.get())
236     return yPos;
238   NSRect availableBounds = NSMakeRect(
239       0,
240       0,
241       windowWidth - chrome_style::kHorizontalPadding * 2,
242       CGFLOAT_MAX);
243   NSSize size = [[linkView_ cell] cellSizeForBounds:availableBounds];
245   NSRect rect;
246   rect.origin.y = yPos + chrome_style::kRowPadding;
247   rect.origin.x = chrome_style::kHorizontalPadding;
248   rect.size = size;
249   [linkView_ setFrame:rect];
250   return NSMaxY([linkView_ frame]);
253 - (CGFloat)layoutAccessoryViewAtYPos:(CGFloat)yPos {
254   if (!accessoryView_.get())
255     return yPos;
256   NSRect frame = [accessoryView_ frame];
257   frame.origin.y = yPos + chrome_style::kRowPadding;
258   frame.origin.x = chrome_style::kHorizontalPadding;
259   [accessoryView_ setFrameOrigin:frame.origin];
260   return NSMaxY(frame);
263 - (void)layoutCloseButtonWithWindowWidth:(CGFloat)windowWidth
264                             windowHeight:(CGFloat)windowHeight {
265   NSRect frame;
266   frame.size.width = chrome_style::GetCloseButtonSize();
267   frame.size.height = chrome_style::GetCloseButtonSize();
268   frame.origin.x = windowWidth -
269       chrome_style::kCloseButtonPadding - NSWidth(frame);
270   frame.origin.y = windowHeight -
271       chrome_style::kCloseButtonPadding - NSHeight(frame);
272   [closeButton_ setFrame:frame];
275 - (NSButton*)linkView {
276   return linkView_;
279 @end