Popular sites on the NTP: check that experiment group StartsWith (rather than IS...
[chromium-blink-merge.git] / chrome / browser / ui / cocoa / tab_contents / sad_tab_view_cocoa.mm
blob857881ddb6f9a89316459f16a3feb233b7a936fe
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 #include "chrome/browser/ui/cocoa/tab_contents/sad_tab_view_cocoa.h"
7 #include "base/logging.h"
8 #include "base/strings/sys_string_conversions.h"
9 #include "chrome/common/url_constants.h"
10 #include "chrome/grit/generated_resources.h"
11 #include "grit/theme_resources.h"
12 #import "third_party/google_toolbox_for_mac/src/AppKit/GTMUILocalizerAndLayoutTweaker.h"
13 #import "ui/base/cocoa/controls/blue_label_button.h"
14 #import "ui/base/cocoa/controls/hyperlink_text_view.h"
15 #import "ui/base/cocoa/nscolor_additions.h"
16 #include "ui/base/l10n/l10n_util.h"
17 #include "ui/base/l10n/l10n_util_mac.h"
18 #include "ui/base/resource/resource_bundle.h"
19 #include "ui/gfx/image/image.h"
21 // Maximum width used by page contents.
22 static const CGFloat kMaxContainerWidth = 600;
23 // Padding between icon and title.
24 static const CGFloat kIconTitleSpacing = 40;
25 // Padding between title and message.
26 static const CGFloat kTitleMessageSpacing = 18;
27 // Padding between message and link.
28 static const CGFloat kMessageLinkSpacing = 50;
29 // Padding between message and button.
30 static const CGFloat kMessageButtonSpacing = 44;
31 // Minimum margins on all sides.
32 static const CGFloat kTabMargin = 13;
33 // Maximum margin on top.
34 static const CGFloat kMaxTopMargin = 130;
36 @interface SadTabTextView : NSTextField
38 - (id)initWithStringResourceID:(int)stringResourceID;
40 @end
42 @implementation SadTabTextView
44 - (id)initWithStringResourceID:(int)stringResourceID {
45   if (self = [super init]) {
46     base::scoped_nsobject<NSMutableParagraphStyle> style(
47         [[NSMutableParagraphStyle alloc] init]);
48     [style setLineSpacing:6];
49     base::scoped_nsobject<NSAttributedString> title([[NSAttributedString alloc]
50         initWithString:l10n_util::GetNSString(stringResourceID)
51             attributes:@{ NSParagraphStyleAttributeName : style }]);
52     [self setAttributedStringValue:title];
54     [self setAlignment:NSLeftTextAlignment];
55     [self setEditable:NO];
56     [self setBezeled:NO];
57     [self setAutoresizingMask:NSViewWidthSizable|NSViewMaxYMargin];
58   }
59   return self;
62 - (BOOL)isOpaque {
63   return YES;
66 @end
68 @interface SadTabContainerView : NSView<NSTextViewDelegate> {
69  @private
70   base::scoped_nsobject<NSImageView> image_;
71   base::scoped_nsobject<NSTextField> title_;
72   base::scoped_nsobject<NSTextField> message_;
73   base::scoped_nsobject<HyperlinkTextView> help_;
74   base::scoped_nsobject<NSButton> button_;
77 - (instancetype)initWithBackgroundColor:(NSColor*)backgroundColor;
79 @property(readonly,nonatomic) NSButton* reloadButton;
81 // The height to fit the content elements within the current width.
82 @property(readonly,nonatomic) CGFloat contentHeight;
84 @end
86 @implementation SadTabContainerView
88 - (instancetype)initWithBackgroundColor:(NSColor*)backgroundColor {
89   if ((self = [super initWithFrame:NSZeroRect])) {
90     // Load resource for image and set it.
91     ResourceBundle& rb = ResourceBundle::GetSharedInstance();
92     NSImage* iconImage = rb.GetNativeImageNamed(IDR_SAD_TAB).ToNSImage();
93     NSRect imageFrame = NSZeroRect;
94     imageFrame.size = [iconImage size];
95     image_.reset([[NSImageView alloc] initWithFrame:imageFrame]);
96     [image_ setImage:iconImage];
97     [image_ setAutoresizingMask:NSViewMaxXMargin|NSViewMaxYMargin];
98     [self addSubview:image_];
100     // Set up the title.
101     title_.reset(
102         [[SadTabTextView alloc] initWithStringResourceID:IDS_SAD_TAB_TITLE]);
103     [title_ setFont:[NSFont systemFontOfSize:24]];
104     [title_ setBackgroundColor:backgroundColor];
105     [title_ setTextColor:[NSColor colorWithCalibratedWhite:38.0f/255.0f
106                                                      alpha:1.0]];
107     [title_ sizeToFit];
108     [title_ setFrameOrigin:
109         NSMakePoint(0, NSMaxY(imageFrame) + kIconTitleSpacing)];
110     [self addSubview:title_];
112     // Set up the message.
113     message_.reset(
114         [[SadTabTextView alloc] initWithStringResourceID:IDS_SAD_TAB_MESSAGE]);
115     [message_ setFont:[NSFont systemFontOfSize:14]];
116     [message_ setBackgroundColor:backgroundColor];
117     [message_ setTextColor:[NSColor colorWithCalibratedWhite:81.0f/255.0f
118                                                        alpha:1.0]];
119     [message_ setFrameOrigin:
120         NSMakePoint(0, NSMaxY([title_ frame]) + kTitleMessageSpacing)];
121     [self addSubview:message_];
123     [self initializeHelpText];
125     button_.reset([[BlueLabelButton alloc] init]);
126     [button_ setTitle:l10n_util::GetNSString(IDS_SAD_TAB_RELOAD_LABEL)];
127     [button_ sizeToFit];
128     [button_ setTarget:self];
129     [button_ setAction:@selector(reloadPage:)];
130     [self addSubview:button_];
131   }
132   return self;
135 - (BOOL)isFlipped {
136   return YES;
139 - (NSButton*)reloadButton {
140   return button_;
143 - (CGFloat)contentHeight {
144   return NSMaxY([button_ frame]);
147 - (void)resizeSubviewsWithOldSize:(NSSize)oldSize {
148   [super resizeSubviewsWithOldSize:oldSize];
150   // |message_| can wrap to variable number of lines.
151   [GTMUILocalizerAndLayoutTweaker sizeToFitFixedWidthTextField:message_];
153   [help_ setFrameOrigin:
154       NSMakePoint(0, NSMaxY([message_ frame]) + kMessageLinkSpacing)];
156   [button_ setFrameOrigin:
157       NSMakePoint(NSMaxX([self bounds]) - NSWidth([button_ frame]),
158                   NSMaxY([message_ frame]) + kMessageButtonSpacing)];
161 - (void)initializeHelpText {
162   // Programmatically create the help link. Note that the frame's initial
163   // height must be set for the programmatic resizing to work.
164   NSFont* helpFont = [message_ font];
165   NSRect helpFrame = NSMakeRect(0, 0, 1, [helpFont pointSize] + 4);
166   help_.reset([[HyperlinkTextView alloc] initWithFrame:helpFrame]);
167   [help_ setAutoresizingMask:NSViewWidthSizable|NSViewMaxYMargin];
168   [help_ setDrawsBackground:YES];
169   [help_ setBackgroundColor:[message_ backgroundColor]];
170   [[help_ textContainer] setLineFragmentPadding:2];  // To align with message_.
171   [self addSubview:help_];
172   [help_ setDelegate:self];
174   // Get the help text and link.
175   size_t linkOffset = 0;
176   const base::string16 helpLink =
177       l10n_util::GetStringUTF16(IDS_SAD_TAB_LEARN_MORE_LINK);
178   NSString* helpMessage(base::SysUTF16ToNSString(helpLink));
179   [help_ setMessage:helpMessage
180            withFont:helpFont
181        messageColor:[message_ textColor]];
182   [help_ addLinkRange:NSMakeRange(linkOffset, helpLink.length())
183              withName:@(chrome::kCrashReasonURL)
184             linkColor:[message_ textColor]];
185   [help_ setAlignment:NSLeftTextAlignment];
186   [help_ sizeToFit];
189 // Called when someone clicks on the embedded link.
190 - (BOOL)textView:(NSTextView*)textView
191    clickedOnLink:(id)link
192          atIndex:(NSUInteger)charIndex {
193   [NSApp sendAction:@selector(openLearnMoreAboutCrashLink:) to:nil from:self];
194   return YES;
197 @end
199 @implementation SadTabView
201 + (NSColor*)backgroundColor {
202   return [NSColor colorWithCalibratedWhite:245.0f/255.0f alpha:1.0];
205 - (instancetype)initWithFrame:(NSRect)frame {
206   if ((self = [super initWithFrame:frame])) {
207     [self setWantsLayer:YES];
209     container_.reset([[SadTabContainerView alloc]
210         initWithBackgroundColor:[SadTabView backgroundColor]]);
211     [self addSubview:container_];
212   }
213   return self;
216 - (CALayer*)makeBackingLayer {
217   CALayer* layer = [super makeBackingLayer];
218   [layer setBackgroundColor:[[SadTabView backgroundColor] cr_CGColor]];
219   return layer;
222 - (BOOL)isFlipped {
223   return YES;
226 - (void)resizeSubviewsWithOldSize:(NSSize)oldSize {
227   NSRect bounds = [self bounds];
229   // Set the container size first because its contentHeight will depend on its
230   // width.
231   NSSize frameSize = NSMakeSize(
232       std::min(NSWidth(bounds) - 2 * kTabMargin, kMaxContainerWidth),
233       NSHeight(bounds));
234   [container_ setFrameSize:frameSize];
236   // Center horizontally.
237   // Top margin is at least kTabMargin and at most kMaxTopMargin.
238   [container_ setFrameOrigin:NSMakePoint(
239       floor((NSWidth(bounds) - frameSize.width) / 2),
240       std::min(kMaxTopMargin, std::max(kTabMargin,
241           NSHeight(bounds) - [container_ contentHeight] - kTabMargin)))];
244 - (NSButton*)reloadButton {
245   return [container_ reloadButton];
248 @end