Home | History | Annotate | Download | only in constrained_window
      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.
      4 
      5 #import "chrome/browser/ui/cocoa/constrained_window/constrained_window_alert.h"
      6 
      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"
     17 
     18 namespace {
     19 
     20 const CGFloat kWindowMinWidth = 500;
     21 const CGFloat kButtonGap = 6;
     22 
     23 }  // namespace
     24 
     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
     43 
     44 @implementation ConstrainedWindowAlert
     45 
     46 - (id)init {
     47   if ((self = [super init])) {
     48     window_.reset([[ConstrainedWindowCustomWindow alloc]
     49         initWithContentRect:ui::kWindowSizeDeterminedLater]);
     50     NSView* contentView = [window_ contentView];
     51 
     52     informativeTextField_.reset([constrained_window::CreateLabel() retain]);
     53     [contentView addSubview:informativeTextField_];
     54     messageTextField_.reset([constrained_window::CreateLabel() retain]);
     55     [contentView addSubview:messageTextField_];
     56 
     57     closeButton_.reset(
     58         [[WebUIHoverCloseButton alloc] initWithFrame:NSZeroRect]);
     59     [contentView addSubview:closeButton_];
     60   }
     61   return self;
     62 }
     63 
     64 - (NSString*)informativeText {
     65   return [informativeTextField_ stringValue];
     66 }
     67 
     68 - (void)setInformativeText:(NSString*)string {
     69   [informativeTextField_ setAttributedStringValue:
     70       constrained_window::GetAttributedLabelString(
     71           string,
     72           chrome_style::kTextFontStyle,
     73           NSNaturalTextAlignment,
     74           NSLineBreakByWordWrapping)];
     75 }
     76 
     77 - (NSString*)messageText {
     78   return [messageTextField_ stringValue];
     79 }
     80 
     81 - (void)setMessageText:(NSString*)string {
     82   [messageTextField_ setAttributedStringValue:
     83       constrained_window::GetAttributedLabelString(
     84           string,
     85           chrome_style::kTitleFontStyle,
     86           NSNaturalTextAlignment,
     87           NSLineBreakByWordWrapping)];
     88 }
     89 
     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   }
     96 
     97   if (!linkView_.get()) {
     98     linkView_.reset(
     99         [[HyperlinkButtonCell buttonWithString:[NSString string]] retain]);
    100     [[window_ contentView] addSubview:linkView_];
    101   }
    102 
    103   [linkView_ setTitle:text];
    104   [linkView_ setTarget:target];
    105   [linkView_ setAction:action];
    106 }
    107 
    108 - (NSView*)accessoryView {
    109   return accessoryView_;
    110 }
    111 
    112 - (void)setAccessoryView:(NSView*)accessoryView {
    113   [accessoryView_ removeFromSuperview];
    114   accessoryView_.reset([accessoryView retain]);
    115   [[window_ contentView] addSubview:accessoryView_];
    116 }
    117 
    118 - (NSArray*)buttons {
    119   return buttons_;
    120 }
    121 
    122 - (NSButton*)closeButton {
    123   return closeButton_;
    124 }
    125 
    126 - (NSWindow*)window {
    127   return window_;
    128 }
    129 
    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];
    144 }
    145 
    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;
    156 
    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);
    163 
    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];
    179 
    180   CGFloat windowHeight = curY + chrome_style::kTitleTopPadding;
    181   [self layoutCloseButtonWithWindowWidth:windowWidth
    182                             windowHeight:windowHeight];
    183 
    184   // Update window frame.
    185   NSRect windowFrame = NSMakeRect(0, 0, windowWidth, windowHeight);
    186   windowFrame = [window_ frameRectForContentRect:windowFrame];
    187   [window_ setFrame:windowFrame display:NO];
    188 }
    189 
    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   }
    202 
    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   }
    211 }
    212 
    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   }
    220 
    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]);
    231 }
    232 
    233 - (CGFloat)layoutLinkAtYPos:(CGFloat)yPos
    234                 windowWidth:(CGFloat)windowWidth {
    235   if (!linkView_.get())
    236     return yPos;
    237 
    238   NSRect availableBounds = NSMakeRect(
    239       0,
    240       0,
    241       windowWidth - chrome_style::kHorizontalPadding * 2,
    242       CGFLOAT_MAX);
    243   NSSize size = [[linkView_ cell] cellSizeForBounds:availableBounds];
    244 
    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]);
    251 }
    252 
    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);
    261 }
    262 
    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];
    273 }
    274 
    275 - (NSButton*)linkView {
    276   return linkView_;
    277 }
    278 
    279 @end
    280