Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/ui/cocoa/tab_contents/sad_tab_view_cocoa.h" | 5 #include "chrome/browser/ui/cocoa/tab_contents/sad_tab_view_cocoa.h" |
| 6 | 6 |
| 7 #include <stddef.h> | |
| 8 | |
| 9 #include "base/logging.h" | |
| 10 #include "base/strings/sys_string_conversions.h" | |
| 11 #include "chrome/common/url_constants.h" | |
| 12 #include "chrome/grit/generated_resources.h" | |
| 13 #include "components/grit/components_scaled_resources.h" | 7 #include "components/grit/components_scaled_resources.h" |
| 14 #include "components/strings/grit/components_strings.h" | |
| 15 #import "third_party/google_toolbox_for_mac/src/AppKit/GTMUILocalizerAndLayoutTw eaker.h" | 8 #import "third_party/google_toolbox_for_mac/src/AppKit/GTMUILocalizerAndLayoutTw eaker.h" |
| 16 #import "ui/base/cocoa/controls/blue_label_button.h" | 9 #import "ui/base/cocoa/controls/blue_label_button.h" |
| 17 #import "ui/base/cocoa/controls/hyperlink_text_view.h" | |
| 18 #import "ui/base/cocoa/nscolor_additions.h" | 10 #import "ui/base/cocoa/nscolor_additions.h" |
| 19 #include "ui/base/l10n/l10n_util.h" | |
| 20 #include "ui/base/l10n/l10n_util_mac.h" | 11 #include "ui/base/l10n/l10n_util_mac.h" |
| 21 #include "ui/base/resource/resource_bundle.h" | 12 #include "ui/base/resource/resource_bundle.h" |
| 22 #include "ui/gfx/image/image.h" | 13 |
| 14 namespace { | |
| 23 | 15 |
| 24 // Maximum width used by page contents. | 16 // Maximum width used by page contents. |
| 25 static const CGFloat kMaxContainerWidth = 600; | 17 const CGFloat kMaxContainerWidth = 600; |
| 26 // Padding between icon and title. | 18 // Padding between icon and title. |
| 27 static const CGFloat kIconTitleSpacing = 40; | 19 const CGFloat kIconTitleSpacing = 40; |
| 28 // Padding between title and message. | 20 // Padding between title and message. |
| 29 static const CGFloat kTitleMessageSpacing = 18; | 21 const CGFloat kTitleMessageSpacing = 18; |
| 30 // Padding between message and link. | 22 // Padding between message and link. |
| 31 static const CGFloat kMessageLinkSpacing = 50; | 23 const CGFloat kMessageLinkSpacing = 50; |
| 32 // Padding between message and button. | 24 // Padding between message and button. |
| 33 static const CGFloat kMessageButtonSpacing = 44; | 25 const CGFloat kMessageButtonSpacing = 44; |
| 34 // Minimum margins on all sides. | 26 // Minimum margins on all sides. |
| 35 static const CGFloat kTabMargin = 13; | 27 const CGFloat kTabMargin = 13; |
| 36 // Maximum margin on top. | 28 // Maximum margin on top. |
| 37 static const CGFloat kMaxTopMargin = 130; | 29 const CGFloat kMaxTopMargin = 130; |
| 38 | 30 |
| 39 @interface SadTabTextView : NSTextField | 31 NSTextField* MakeLabelTextField(NSRect frame) { |
| 32 NSTextField* ret = [[[NSTextField alloc] initWithFrame:frame] autorelease]; | |
| 33 ret.autoresizingMask = NSViewWidthSizable | NSViewMaxYMargin; | |
| 34 ret.editable = NO; | |
| 35 ret.drawsBackground = NO; | |
| 36 ret.bezeled = NO; | |
| 37 return ret; | |
| 38 } | |
| 40 | 39 |
| 41 - (id)initWithStringResourceID:(int)stringResourceID; | 40 } // namespace |
| 42 | 41 |
| 42 @interface SadTabContainerView : NSView | |
| 43 @end | 43 @end |
| 44 | 44 |
| 45 @implementation SadTabTextView | 45 @implementation SadTabContainerView |
| 46 - (BOOL)isFlipped { | |
| 47 return YES; | |
| 48 } | |
| 49 @end | |
| 46 | 50 |
| 47 - (id)initWithStringResourceID:(int)stringResourceID { | 51 @interface SadTabView ()<NSTextViewDelegate> |
| 48 if (self = [super init]) { | 52 @end |
| 49 base::scoped_nsobject<NSMutableParagraphStyle> style( | |
| 50 [[NSMutableParagraphStyle alloc] init]); | |
| 51 [style setLineSpacing:6]; | |
| 52 base::scoped_nsobject<NSAttributedString> title([[NSAttributedString alloc] | |
| 53 initWithString:l10n_util::GetNSString(stringResourceID) | |
| 54 attributes:@{ NSParagraphStyleAttributeName : style }]); | |
| 55 [self setAttributedStringValue:title]; | |
| 56 | 53 |
| 57 [self setAlignment:NSLeftTextAlignment]; | 54 @implementation SadTabView { |
| 58 [self setEditable:NO]; | 55 NSView* container_; |
| 59 [self setBezeled:NO]; | 56 NSImageView* icon_; |
| 60 [self setAutoresizingMask:NSViewWidthSizable|NSViewMaxYMargin]; | 57 NSTextField* title_; |
| 58 NSTextField* message_; | |
| 59 HyperlinkTextView* help_; | |
| 60 NSButton* button_; | |
| 61 } | |
| 62 | |
| 63 @synthesize delegate = delegate_; | |
| 64 | |
| 65 - (instancetype)initWithFrame:(NSRect)frame { | |
| 66 if ((self = [super initWithFrame:frame])) { | |
| 67 self.wantsLayer = YES; | |
| 68 self.layer.backgroundColor = | |
| 69 [NSColor colorWithCalibratedWhite:245.0f / 255.0f alpha:1.0].cr_CGColor; | |
| 70 container_ = [[SadTabContainerView new] autorelease]; | |
| 71 | |
| 72 NSImage* iconImage = ResourceBundle::GetSharedInstance() | |
| 73 .GetNativeImageNamed(IDR_CRASH_SAD_TAB) | |
| 74 .ToNSImage(); | |
| 75 icon_ = [[NSImageView new] autorelease]; | |
| 76 icon_.image = iconImage; | |
| 77 icon_.frameSize = iconImage.size; | |
| 78 icon_.autoresizingMask = NSViewMaxXMargin | NSViewMaxYMargin; | |
| 79 [container_ addSubview:icon_]; | |
| 80 | |
| 81 CGFloat width = self.bounds.size.width; | |
| 82 title_ = MakeLabelTextField( | |
| 83 NSMakeRect(0, NSMaxY(icon_.frame) + kIconTitleSpacing, width, 0)); | |
| 84 title_.font = [NSFont systemFontOfSize:24]; | |
| 85 title_.textColor = | |
| 86 [NSColor colorWithCalibratedWhite:38.0f / 255.0f alpha:1.0]; | |
| 87 [title_ sizeToFit]; | |
| 88 [container_ addSubview:title_]; | |
| 89 | |
| 90 message_ = | |
| 91 MakeLabelTextField(NSMakeRect(0, NSMaxY(title_.frame), width, 0)); | |
| 92 ((NSCell*)message_.cell).wraps = YES; | |
|
Avi (use Gerrit)
2016/08/14 04:54:28
base::mac::ObjCCast
Sidney San Martín
2016/08/15 03:40:29
Done. ObjCCast really surprises me though — it ret
Avi (use Gerrit)
2016/08/15 03:43:59
Interesting way of thinking about it. Talk to Mark
erikchen
2016/08/15 21:03:03
I think that downcasting is an appropriate use cas
| |
| 93 message_.font = [NSFont systemFontOfSize:14]; | |
| 94 message_.textColor = | |
| 95 [NSColor colorWithCalibratedWhite:81.0f / 255.0f alpha:1.0]; | |
| 96 [container_ addSubview:message_]; | |
| 97 | |
| 98 help_ = [[[HyperlinkTextView alloc] | |
| 99 initWithFrame:NSMakeRect(0, 0, 1, message_.font.pointSize + 4)] | |
| 100 autorelease]; | |
| 101 help_.delegate = self; | |
| 102 help_.autoresizingMask = NSViewWidthSizable | NSViewMaxYMargin; | |
| 103 help_.textContainer.lineFragmentPadding = 2; // To align with message_. | |
| 104 [container_ addSubview:help_]; | |
| 105 | |
| 106 button_ = [[BlueLabelButton new] autorelease]; | |
| 107 button_.target = self; | |
| 108 button_.action = @selector(buttonClicked); | |
| 109 [container_ addSubview:button_]; | |
| 110 | |
| 111 [self addSubview:container_]; | |
| 61 } | 112 } |
| 62 return self; | 113 return self; |
| 63 } | 114 } |
| 64 | |
| 65 - (BOOL)isOpaque { | |
| 66 return YES; | |
| 67 } | |
| 68 | |
| 69 @end | |
| 70 | |
| 71 @interface SadTabContainerView : NSView<NSTextViewDelegate> { | |
| 72 @private | |
| 73 base::scoped_nsobject<NSImageView> image_; | |
| 74 base::scoped_nsobject<NSTextField> title_; | |
| 75 base::scoped_nsobject<NSTextField> message_; | |
| 76 base::scoped_nsobject<HyperlinkTextView> help_; | |
| 77 base::scoped_nsobject<NSButton> button_; | |
| 78 } | |
| 79 | |
| 80 - (instancetype)initWithBackgroundColor:(NSColor*)backgroundColor; | |
| 81 | |
| 82 @property(readonly,nonatomic) NSButton* reloadButton; | |
| 83 | |
| 84 // The height to fit the content elements within the current width. | |
| 85 @property(readonly,nonatomic) CGFloat contentHeight; | |
| 86 | |
| 87 @end | |
| 88 | |
| 89 @implementation SadTabContainerView | |
| 90 | |
| 91 - (instancetype)initWithBackgroundColor:(NSColor*)backgroundColor { | |
| 92 if ((self = [super initWithFrame:NSZeroRect])) { | |
| 93 // Load resource for image and set it. | |
| 94 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); | |
| 95 NSImage* iconImage = rb.GetNativeImageNamed(IDR_CRASH_SAD_TAB).ToNSImage(); | |
| 96 NSRect imageFrame = NSZeroRect; | |
| 97 imageFrame.size = [iconImage size]; | |
| 98 image_.reset([[NSImageView alloc] initWithFrame:imageFrame]); | |
| 99 [image_ setImage:iconImage]; | |
| 100 [image_ setAutoresizingMask:NSViewMaxXMargin|NSViewMaxYMargin]; | |
| 101 [self addSubview:image_]; | |
| 102 | |
| 103 // Set up the title. | |
| 104 title_.reset( | |
| 105 [[SadTabTextView alloc] initWithStringResourceID:IDS_SAD_TAB_TITLE]); | |
| 106 [title_ setFont:[NSFont systemFontOfSize:24]]; | |
| 107 [title_ setBackgroundColor:backgroundColor]; | |
| 108 [title_ setTextColor:[NSColor colorWithCalibratedWhite:38.0f/255.0f | |
| 109 alpha:1.0]]; | |
| 110 [title_ sizeToFit]; | |
| 111 [title_ setFrameOrigin: | |
| 112 NSMakePoint(0, NSMaxY(imageFrame) + kIconTitleSpacing)]; | |
| 113 [self addSubview:title_]; | |
| 114 | |
| 115 // Set up the message. | |
| 116 message_.reset( | |
| 117 [[SadTabTextView alloc] initWithStringResourceID:IDS_SAD_TAB_MESSAGE]); | |
| 118 [message_ setFont:[NSFont systemFontOfSize:14]]; | |
| 119 [message_ setBackgroundColor:backgroundColor]; | |
| 120 [message_ setTextColor:[NSColor colorWithCalibratedWhite:81.0f/255.0f | |
| 121 alpha:1.0]]; | |
| 122 [message_ setFrameOrigin: | |
| 123 NSMakePoint(0, NSMaxY([title_ frame]) + kTitleMessageSpacing)]; | |
| 124 [self addSubview:message_]; | |
| 125 | |
| 126 [self initializeHelpText]; | |
| 127 | |
| 128 button_.reset([[BlueLabelButton alloc] init]); | |
| 129 [button_ setTitle:l10n_util::GetNSString(IDS_SAD_TAB_RELOAD_LABEL)]; | |
| 130 [button_ sizeToFit]; | |
| 131 [button_ setTarget:self]; | |
| 132 [button_ setAction:@selector(reloadPage:)]; | |
| 133 [self addSubview:button_]; | |
| 134 } | |
| 135 return self; | |
| 136 } | |
| 137 | 115 |
| 138 - (BOOL)isFlipped { | 116 - (BOOL)isFlipped { |
| 139 return YES; | 117 return YES; |
| 140 } | 118 } |
| 141 | 119 |
| 142 - (NSButton*)reloadButton { | |
| 143 return button_; | |
| 144 } | |
| 145 | |
| 146 - (CGFloat)contentHeight { | |
| 147 return NSMaxY([button_ frame]); | |
| 148 } | |
| 149 | |
| 150 - (void)resizeSubviewsWithOldSize:(NSSize)oldSize { | 120 - (void)resizeSubviewsWithOldSize:(NSSize)oldSize { |
| 151 [super resizeSubviewsWithOldSize:oldSize]; | 121 [super resizeSubviewsWithOldSize:oldSize]; |
| 152 | 122 |
| 123 NSSize size = self.bounds.size; | |
| 124 NSSize containerSize = NSMakeSize( | |
| 125 std::min(size.width - 2 * kTabMargin, kMaxContainerWidth), size.height); | |
| 126 | |
| 127 // Set the container's size first because text wrapping depends on its width. | |
| 128 container_.frameSize = containerSize; | |
| 129 | |
| 153 // |message_| can wrap to variable number of lines. | 130 // |message_| can wrap to variable number of lines. |
| 154 [GTMUILocalizerAndLayoutTweaker sizeToFitFixedWidthTextField:message_]; | 131 [GTMUILocalizerAndLayoutTweaker sizeToFitFixedWidthTextField:message_]; |
| 155 | 132 |
| 156 [help_ setFrameOrigin: | 133 message_.frameOrigin = |
| 157 NSMakePoint(0, NSMaxY([message_ frame]) + kMessageLinkSpacing)]; | 134 NSMakePoint(0, NSMaxY(title_.frame) + kTitleMessageSpacing); |
| 135 help_.frameOrigin = | |
| 136 NSMakePoint(0, NSMaxY(message_.frame) + kMessageLinkSpacing); | |
| 158 | 137 |
| 159 [button_ setFrameOrigin: | 138 button_.frameOrigin = |
| 160 NSMakePoint(NSMaxX([self bounds]) - NSWidth([button_ frame]), | 139 NSMakePoint(containerSize.width - NSWidth(button_.bounds), |
| 161 NSMaxY([message_ frame]) + kMessageButtonSpacing)]; | 140 NSMaxY(message_.frame) + kMessageButtonSpacing); |
| 141 | |
| 142 containerSize.height = NSMaxY(button_.frame); | |
| 143 container_.frameSize = containerSize; | |
| 144 | |
| 145 // Center. Top margin is must be between kTabMargin and kMaxTopMargin. | |
| 146 container_.frameOrigin = NSMakePoint( | |
| 147 floor((size.width - containerSize.width) / 2), | |
| 148 std::min(kMaxTopMargin, | |
| 149 std::max(kTabMargin, | |
| 150 size.height - containerSize.height - kTabMargin))); | |
| 162 } | 151 } |
| 163 | 152 |
| 164 - (void)initializeHelpText { | 153 - (void)setTitle:(int)title { |
| 165 // Programmatically create the help link. Note that the frame's initial | 154 NSMutableParagraphStyle* style = [[NSMutableParagraphStyle new] autorelease]; |
| 166 // height must be set for the programmatic resizing to work. | 155 style.lineSpacing = 6; |
| 167 NSFont* helpFont = [message_ font]; | |
| 168 NSRect helpFrame = NSMakeRect(0, 0, 1, [helpFont pointSize] + 4); | |
| 169 help_.reset([[HyperlinkTextView alloc] initWithFrame:helpFrame]); | |
| 170 [help_ setAutoresizingMask:NSViewWidthSizable|NSViewMaxYMargin]; | |
| 171 [help_ setDrawsBackground:YES]; | |
| 172 [help_ setBackgroundColor:[message_ backgroundColor]]; | |
| 173 [[help_ textContainer] setLineFragmentPadding:2]; // To align with message_. | |
| 174 [self addSubview:help_]; | |
| 175 [help_ setDelegate:self]; | |
| 176 | 156 |
| 177 // Get the help text and link. | 157 title_.attributedStringValue = [[[NSAttributedString alloc] |
| 178 size_t linkOffset = 0; | 158 initWithString:l10n_util::GetNSString(title) |
| 179 const base::string16 helpLink = | 159 attributes:@{NSParagraphStyleAttributeName : style}] autorelease]; |
| 180 l10n_util::GetStringUTF16(IDS_SAD_TAB_LEARN_MORE_LINK); | 160 } |
| 181 NSString* helpMessage(base::SysUTF16ToNSString(helpLink)); | 161 |
| 182 [help_ setMessage:helpMessage | 162 - (void)setMessage:(int)message { |
| 183 withFont:helpFont | 163 message_.stringValue = l10n_util::GetNSString(message); |
| 184 messageColor:[message_ textColor]]; | 164 } |
| 185 [help_ addLinkRange:NSMakeRange(linkOffset, helpLink.length()) | 165 |
| 186 withURL:@(chrome::kCrashReasonURL) | 166 - (void)setButtonTitle:(int)buttonTitle { |
| 187 linkColor:[message_ textColor]]; | 167 button_.title = l10n_util::GetNSString(buttonTitle); |
| 188 [help_ setAlignment:NSLeftTextAlignment]; | 168 [button_ sizeToFit]; |
| 169 } | |
| 170 | |
| 171 - (void)setHelpLinkTitle:(int)helpLinkTitle URL:(NSString*)url { | |
| 172 NSString* title = l10n_util::GetNSString(helpLinkTitle); | |
| 173 [help_ setMessage:title | |
| 174 withFont:message_.font | |
| 175 messageColor:message_.textColor]; | |
| 176 [help_ addLinkRange:NSMakeRange(0, title.length) | |
| 177 withURL:url | |
| 178 linkColor:message_.textColor]; | |
| 189 [help_ sizeToFit]; | 179 [help_ sizeToFit]; |
| 190 } | 180 } |
| 191 | 181 |
| 182 - (void)buttonClicked { | |
| 183 [delegate_ sadTabViewButtonClicked:self]; | |
| 184 } | |
| 185 | |
| 192 // Called when someone clicks on the embedded link. | 186 // Called when someone clicks on the embedded link. |
| 193 - (BOOL)textView:(NSTextView*)textView | 187 - (BOOL)textView:(NSTextView*)textView |
| 194 clickedOnLink:(id)link | 188 clickedOnLink:(id)link |
| 195 atIndex:(NSUInteger)charIndex { | 189 atIndex:(NSUInteger)charIndex { |
| 196 [NSApp sendAction:@selector(openLearnMoreAboutCrashLink:) to:nil from:self]; | 190 [delegate_ sadTabView:self helpLinkClickedWithURL:(NSString*)link]; |
| 197 return YES; | 191 return YES; |
| 198 } | 192 } |
| 199 | 193 |
| 200 @end | 194 @end |
| 201 | |
| 202 @implementation SadTabView | |
| 203 | |
| 204 + (NSColor*)backgroundColor { | |
| 205 return [NSColor colorWithCalibratedWhite:245.0f/255.0f alpha:1.0]; | |
| 206 } | |
| 207 | |
| 208 - (instancetype)initWithFrame:(NSRect)frame { | |
| 209 if ((self = [super initWithFrame:frame])) { | |
| 210 [self setWantsLayer:YES]; | |
| 211 | |
| 212 container_.reset([[SadTabContainerView alloc] | |
| 213 initWithBackgroundColor:[SadTabView backgroundColor]]); | |
| 214 [self addSubview:container_]; | |
| 215 } | |
| 216 return self; | |
| 217 } | |
| 218 | |
| 219 - (CALayer*)makeBackingLayer { | |
| 220 CALayer* layer = [super makeBackingLayer]; | |
| 221 [layer setBackgroundColor:[[SadTabView backgroundColor] cr_CGColor]]; | |
| 222 return layer; | |
| 223 } | |
| 224 | |
| 225 - (BOOL)isFlipped { | |
| 226 return YES; | |
| 227 } | |
| 228 | |
| 229 - (void)resizeSubviewsWithOldSize:(NSSize)oldSize { | |
| 230 NSRect bounds = [self bounds]; | |
| 231 | |
| 232 // Set the container size first because its contentHeight will depend on its | |
| 233 // width. | |
| 234 NSSize frameSize = NSMakeSize( | |
| 235 std::min(NSWidth(bounds) - 2 * kTabMargin, kMaxContainerWidth), | |
| 236 NSHeight(bounds)); | |
| 237 [container_ setFrameSize:frameSize]; | |
| 238 | |
| 239 // Center horizontally. | |
| 240 // Top margin is at least kTabMargin and at most kMaxTopMargin. | |
| 241 [container_ setFrameOrigin:NSMakePoint( | |
| 242 floor((NSWidth(bounds) - frameSize.width) / 2), | |
| 243 std::min(kMaxTopMargin, std::max(kTabMargin, | |
| 244 NSHeight(bounds) - [container_ contentHeight] - kTabMargin)))]; | |
| 245 } | |
| 246 | |
| 247 - (NSButton*)reloadButton { | |
| 248 return [container_ reloadButton]; | |
| 249 } | |
| 250 | |
| 251 @end | |
| OLD | NEW |