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"
20 const CGFloat kWindowMinWidth = 500;
21 const CGFloat kButtonGap = 6;
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
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;
44 @implementation ConstrainedWindowAlert
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_];
58 [[WebUIHoverCloseButton alloc] initWithFrame:NSZeroRect]);
59 [contentView addSubview:closeButton_];
64 - (NSString*)informativeText {
65 return [informativeTextField_ stringValue];
68 - (void)setInformativeText:(NSString*)string {
69 [informativeTextField_ setAttributedStringValue:
70 constrained_window::GetAttributedLabelString(
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(
85 chrome_style::kTitleFontStyle,
86 NSNaturalTextAlignment,
87 NSLineBreakByWordWrapping)];
90 - (void)setLinkText:(NSString*)text target:(id)target action:(SEL)action {
92 [linkView_ removeFromSuperview];
97 if (!linkView_.get()) {
99 [[HyperlinkButtonCell buttonWithString:[NSString string]] retain]);
100 [[window_ contentView] addSubview:linkView_];
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 {
122 - (NSButton*)closeButton {
126 - (NSWindow*)window {
130 - (void)addButtonWithTitle:(NSString*)title
131 keyEquivalent:(NSString*)keyEquivalent
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];
148 CGFloat buttonWidth = 0;
149 for (NSButton* button in buttons_.get()) {
151 NSSize size = [button frame].size;
152 buttonWidth += size.width;
154 if ([buttons_ count])
155 buttonWidth += ([buttons_ count] - 1) * kButtonGap;
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);
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_
175 windowWidth:windowWidth];
176 curY = [self layoutTextField:messageTextField_
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;
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;
213 - (CGFloat)layoutTextField:(NSTextField*)textField
215 windowWidth:(CGFloat)windowWidth {
216 if (![[textField stringValue] length]) {
217 [textField setHidden:YES];
221 [textField setHidden:NO];
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())
238 NSRect availableBounds = NSMakeRect(
241 windowWidth - chrome_style::kHorizontalPadding * 2,
243 NSSize size = [[linkView_ cell] cellSizeForBounds:availableBounds];
246 rect.origin.y = yPos + chrome_style::kRowPadding;
247 rect.origin.x = chrome_style::kHorizontalPadding;
249 [linkView_ setFrame:rect];
250 return NSMaxY([linkView_ frame]);
253 - (CGFloat)layoutAccessoryViewAtYPos:(CGFloat)yPos {
254 if (!accessoryView_.get())
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 {
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 {