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;
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];
57 [self setAutoresizingMask:NSViewWidthSizable|NSViewMaxYMargin];
68 @interface SadTabContainerView : NSView<NSTextViewDelegate> {
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;
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_];
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
108 [title_ setFrameOrigin:
109 NSMakePoint(0, NSMaxY(imageFrame) + kIconTitleSpacing)];
110 [self addSubview:title_];
112 // Set up the message.
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
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)];
128 [button_ setTarget:self];
129 [button_ setAction:@selector(reloadPage:)];
130 [self addSubview:button_];
139 - (NSButton*)reloadButton {
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
181 messageColor:[message_ textColor]];
182 [help_ addLinkRange:NSMakeRange(linkOffset, helpLink.length())
183 withName:@(chrome::kCrashReasonURL)
184 linkColor:[message_ textColor]];
185 [help_ setAlignment:NSLeftTextAlignment];
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];
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_];
216 - (CALayer*)makeBackingLayer {
217 CALayer* layer = [super makeBackingLayer];
218 [layer setBackgroundColor:[[SadTabView backgroundColor] cr_CGColor]];
226 - (void)resizeSubviewsWithOldSize:(NSSize)oldSize {
227 NSRect bounds = [self bounds];
229 // Set the container size first because its contentHeight will depend on its
231 NSSize frameSize = NSMakeSize(
232 std::min(NSWidth(bounds) - 2 * kTabMargin, kMaxContainerWidth),
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];