| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #import "chrome/browser/ui/cocoa/constrained_window/constrained_window_alert.h" |
| |
| #import "base/logging.h" |
| #import "chrome/browser/ui/chrome_style.h" |
| #import "chrome/browser/ui/cocoa/constrained_window/constrained_window_button.h" |
| #import "chrome/browser/ui/cocoa/constrained_window/constrained_window_control_utils.h" |
| #import "chrome/browser/ui/cocoa/constrained_window/constrained_window_custom_window.h" |
| #import "chrome/browser/ui/cocoa/hover_close_button.h" |
| #include "skia/ext/skia_utils_mac.h" |
| #include "third_party/google_toolbox_for_mac/src/AppKit/GTMUILocalizerAndLayoutTweaker.h" |
| #include "ui/base/cocoa/controls/hyperlink_button_cell.h" |
| #include "ui/base/cocoa/window_size_constants.h" |
| |
| namespace { |
| |
| const CGFloat kWindowMinWidth = 500; |
| const CGFloat kButtonGap = 6; |
| |
| } // namespace |
| |
| @interface ConstrainedWindowAlert () |
| // Position the alert buttons within the given window width. |
| - (void)layoutButtonsWithWindowWidth:(CGFloat)windowWidth; |
| // Resize the given text field to fit within the window and position it starting |
| // at |yPos|. Returns the new value of yPos. |
| - (CGFloat)layoutTextField:(NSTextField*)textField |
| yPos:(CGFloat)yPos |
| windowWidth:(CGFloat)windowWidth; |
| // If a link has been set, resizes the link to fit within the window and |
| // position it starting at |yPos|. Returns the new value of yPos. |
| - (CGFloat)layoutLinkAtYPos:(CGFloat)yPos |
| windowWidth:(CGFloat)windowWidth; |
| // Positions the accessory view starting at yPos. Returns the new value of yPos. |
| - (CGFloat)layoutAccessoryViewAtYPos:(CGFloat)yPos; |
| // Update the position of the close button. |
| - (void)layoutCloseButtonWithWindowWidth:(CGFloat)windowWidth |
| windowHeight:(CGFloat)windowHeight; |
| @end |
| |
| @implementation ConstrainedWindowAlert |
| |
| - (id)init { |
| if ((self = [super init])) { |
| window_.reset([[ConstrainedWindowCustomWindow alloc] |
| initWithContentRect:ui::kWindowSizeDeterminedLater]); |
| NSView* contentView = [window_ contentView]; |
| |
| informativeTextField_.reset([constrained_window::CreateLabel() retain]); |
| [contentView addSubview:informativeTextField_]; |
| messageTextField_.reset([constrained_window::CreateLabel() retain]); |
| [contentView addSubview:messageTextField_]; |
| |
| closeButton_.reset( |
| [[WebUIHoverCloseButton alloc] initWithFrame:NSZeroRect]); |
| [contentView addSubview:closeButton_]; |
| } |
| return self; |
| } |
| |
| - (NSString*)informativeText { |
| return [informativeTextField_ stringValue]; |
| } |
| |
| - (void)setInformativeText:(NSString*)string { |
| [informativeTextField_ setAttributedStringValue: |
| constrained_window::GetAttributedLabelString( |
| string, |
| chrome_style::kTextFontStyle, |
| NSNaturalTextAlignment, |
| NSLineBreakByWordWrapping)]; |
| } |
| |
| - (NSString*)messageText { |
| return [messageTextField_ stringValue]; |
| } |
| |
| - (void)setMessageText:(NSString*)string { |
| [messageTextField_ setAttributedStringValue: |
| constrained_window::GetAttributedLabelString( |
| string, |
| chrome_style::kTitleFontStyle, |
| NSNaturalTextAlignment, |
| NSLineBreakByWordWrapping)]; |
| } |
| |
| - (void)setLinkText:(NSString*)text target:(id)target action:(SEL)action { |
| if (![text length]) { |
| [linkView_ removeFromSuperview]; |
| linkView_.reset(nil); |
| return; |
| } |
| |
| if (!linkView_.get()) { |
| linkView_.reset( |
| [[HyperlinkButtonCell buttonWithString:[NSString string]] retain]); |
| [[window_ contentView] addSubview:linkView_]; |
| } |
| |
| [linkView_ setTitle:text]; |
| [linkView_ setTarget:target]; |
| [linkView_ setAction:action]; |
| } |
| |
| - (NSView*)accessoryView { |
| return accessoryView_; |
| } |
| |
| - (void)setAccessoryView:(NSView*)accessoryView { |
| [accessoryView_ removeFromSuperview]; |
| accessoryView_.reset([accessoryView retain]); |
| [[window_ contentView] addSubview:accessoryView_]; |
| } |
| |
| - (NSArray*)buttons { |
| return buttons_; |
| } |
| |
| - (NSButton*)closeButton { |
| return closeButton_; |
| } |
| |
| - (NSWindow*)window { |
| return window_; |
| } |
| |
| - (void)addButtonWithTitle:(NSString*)title |
| keyEquivalent:(NSString*)keyEquivalent |
| target:(id)target |
| action:(SEL)action { |
| if (!buttons_.get()) |
| buttons_.reset([[NSMutableArray alloc] init]); |
| base::scoped_nsobject<NSButton> button( |
| [[ConstrainedWindowButton alloc] initWithFrame:NSZeroRect]); |
| [button setTitle:title]; |
| [button setKeyEquivalent:keyEquivalent]; |
| [button setTarget:target]; |
| [button setAction:action]; |
| [buttons_ addObject:button]; |
| [[window_ contentView] addSubview:button]; |
| } |
| |
| - (void)layout { |
| // Button width. |
| CGFloat buttonWidth = 0; |
| for (NSButton* button in buttons_.get()) { |
| [button sizeToFit]; |
| NSSize size = [button frame].size; |
| buttonWidth += size.width; |
| } |
| if ([buttons_ count]) |
| buttonWidth += ([buttons_ count] - 1) * kButtonGap; |
| |
| // Window width. |
| CGFloat windowWidth = buttonWidth; |
| if (accessoryView_.get()) |
| windowWidth = std::max(windowWidth, NSWidth([accessoryView_ frame])); |
| windowWidth += chrome_style::kHorizontalPadding * 2; |
| windowWidth = std::max(windowWidth, kWindowMinWidth); |
| |
| // Layout controls. |
| [self layoutButtonsWithWindowWidth:windowWidth]; |
| CGFloat curY = [buttons_ count] ? NSMaxY([[buttons_ lastObject] frame]) |
| : chrome_style::kClientBottomPadding; |
| CGFloat availableMessageWidth = |
| windowWidth - chrome_style::GetCloseButtonSize() - kButtonGap; |
| curY = [self layoutLinkAtYPos:curY |
| windowWidth:availableMessageWidth]; |
| curY = [self layoutAccessoryViewAtYPos:curY]; |
| curY = [self layoutTextField:informativeTextField_ |
| yPos:curY |
| windowWidth:windowWidth]; |
| curY = [self layoutTextField:messageTextField_ |
| yPos:curY |
| windowWidth:availableMessageWidth]; |
| |
| CGFloat windowHeight = curY + chrome_style::kTitleTopPadding; |
| [self layoutCloseButtonWithWindowWidth:windowWidth |
| windowHeight:windowHeight]; |
| |
| // Update window frame. |
| NSRect windowFrame = NSMakeRect(0, 0, windowWidth, windowHeight); |
| windowFrame = [window_ frameRectForContentRect:windowFrame]; |
| [window_ setFrame:windowFrame display:NO]; |
| } |
| |
| - (void)layoutButtonsWithWindowWidth:(CGFloat)windowWidth { |
| // Layout first 2 button right to left. |
| CGFloat curX = windowWidth - chrome_style::kHorizontalPadding; |
| const int buttonCount = [buttons_ count]; |
| for (int i = 0; i < std::min(2, buttonCount); ++i) { |
| NSButton* button = [buttons_ objectAtIndex:i]; |
| NSRect rect = [button frame]; |
| rect.origin.x = curX - NSWidth(rect); |
| rect.origin.y = chrome_style::kClientBottomPadding; |
| [button setFrameOrigin:rect.origin]; |
| curX = NSMinX(rect) - kButtonGap; |
| } |
| |
| // Layout remaining buttons left to right. |
| curX = chrome_style::kHorizontalPadding; |
| for (int i = buttonCount - 1; i >= 2; --i) { |
| NSButton* button = [buttons_ objectAtIndex:i]; |
| [button setFrameOrigin: |
| NSMakePoint(curX, chrome_style::kClientBottomPadding)]; |
| curX += NSMaxX([button frame]) + kButtonGap; |
| } |
| } |
| |
| - (CGFloat)layoutTextField:(NSTextField*)textField |
| yPos:(CGFloat)yPos |
| windowWidth:(CGFloat)windowWidth { |
| if (![[textField stringValue] length]) { |
| [textField setHidden:YES]; |
| return yPos; |
| } |
| |
| [textField setHidden:NO]; |
| NSRect rect; |
| rect.origin.y = yPos + chrome_style::kRowPadding; |
| rect.origin.x = chrome_style::kHorizontalPadding; |
| rect.size.width = windowWidth - |
| chrome_style::kHorizontalPadding * 2; |
| rect.size.height = 1; |
| [textField setFrame:rect]; |
| [GTMUILocalizerAndLayoutTweaker sizeToFitFixedWidthTextField:textField]; |
| return NSMaxY([textField frame]); |
| } |
| |
| - (CGFloat)layoutLinkAtYPos:(CGFloat)yPos |
| windowWidth:(CGFloat)windowWidth { |
| if (!linkView_.get()) |
| return yPos; |
| |
| NSRect availableBounds = NSMakeRect( |
| 0, |
| 0, |
| windowWidth - chrome_style::kHorizontalPadding * 2, |
| CGFLOAT_MAX); |
| NSSize size = [[linkView_ cell] cellSizeForBounds:availableBounds]; |
| |
| NSRect rect; |
| rect.origin.y = yPos + chrome_style::kRowPadding; |
| rect.origin.x = chrome_style::kHorizontalPadding; |
| rect.size = size; |
| [linkView_ setFrame:rect]; |
| return NSMaxY([linkView_ frame]); |
| } |
| |
| - (CGFloat)layoutAccessoryViewAtYPos:(CGFloat)yPos { |
| if (!accessoryView_.get()) |
| return yPos; |
| NSRect frame = [accessoryView_ frame]; |
| frame.origin.y = yPos + chrome_style::kRowPadding; |
| frame.origin.x = chrome_style::kHorizontalPadding; |
| [accessoryView_ setFrameOrigin:frame.origin]; |
| return NSMaxY(frame); |
| } |
| |
| - (void)layoutCloseButtonWithWindowWidth:(CGFloat)windowWidth |
| windowHeight:(CGFloat)windowHeight { |
| NSRect frame; |
| frame.size.width = chrome_style::GetCloseButtonSize(); |
| frame.size.height = chrome_style::GetCloseButtonSize(); |
| frame.origin.x = windowWidth - |
| chrome_style::kCloseButtonPadding - NSWidth(frame); |
| frame.origin.y = windowHeight - |
| chrome_style::kCloseButtonPadding - NSHeight(frame); |
| [closeButton_ setFrame:frame]; |
| } |
| |
| - (NSButton*)linkView { |
| return linkView_; |
| } |
| |
| @end |