| OLD | NEW |
| (Empty) |
| 1 // Copyright 2013 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 "ui/message_center/cocoa/settings_entry_view.h" | |
| 6 | |
| 7 #include <algorithm> | |
| 8 | |
| 9 #include "base/strings/sys_string_conversions.h" | |
| 10 #include "skia/ext/skia_utils_mac.h" | |
| 11 #include "ui/base/resource/resource_bundle.h" | |
| 12 #import "ui/message_center/cocoa/settings_controller.h" | |
| 13 #include "ui/message_center/message_center_style.h" | |
| 14 #include "ui/resources/grit/ui_resources.h" | |
| 15 | |
| 16 using message_center::settings::kEntryIconSize; | |
| 17 using message_center::settings::kInternalHorizontalSpacing; | |
| 18 | |
| 19 // Size of the widget rendered for us by Cocoa. | |
| 20 const int kCocoaCheckboxSize = 14; | |
| 21 | |
| 22 // Intrinsic padding pixels out of our control. | |
| 23 // Cocoa gives the checkmark some blank space on either side. | |
| 24 const int kIntrinsicCheckmarkLeftPadding = 2; | |
| 25 const int kIntrinsicCheckmarkRightPadding = 4; | |
| 26 // Labels have a bit of whitespace to the left, which can throw | |
| 27 // off measurements. | |
| 28 const int kIntrinsicTextLeftPadding = 1; | |
| 29 | |
| 30 // The learn more image is bigger than the actual size of the learn more | |
| 31 // pixels, this represents the difference. | |
| 32 const int kIntrinsicLearnMorePadding = 2; | |
| 33 | |
| 34 // Corrected padding values used in layout. | |
| 35 // This computes the amout of padding based on the area reserved for the | |
| 36 // checkbox and the actual checkbox size in pixels. | |
| 37 const int kCheckmarkPaddingNecessary = | |
| 38 (message_center::settings::kCheckboxSizeWithPadding - kCocoaCheckboxSize) / | |
| 39 2; | |
| 40 | |
| 41 // These represent the additional padding that we must give the checkmark | |
| 42 // control based on the required padding and the intrinsic padding. | |
| 43 const int kCorrectedCheckmarkLeftPadding = | |
| 44 kCheckmarkPaddingNecessary - kIntrinsicCheckmarkLeftPadding; | |
| 45 const int kCorrectedCheckmarkRightPadding = | |
| 46 kCheckmarkPaddingNecessary + kInternalHorizontalSpacing - | |
| 47 kIntrinsicCheckmarkRightPadding; | |
| 48 | |
| 49 // The amount of space we want, based on the spec and the intrinsic text space | |
| 50 // included by Cocoa. | |
| 51 const int kCorrectedIconTextPadding = | |
| 52 kInternalHorizontalSpacing - kIntrinsicTextLeftPadding; | |
| 53 | |
| 54 // We want a certain amount of space to the right of the learn more button, | |
| 55 // this metric incorporates the intrinsic learn more blank space to compute it. | |
| 56 const int kCorrectedEntryRightPadding = | |
| 57 kInternalHorizontalSpacing - kIntrinsicLearnMorePadding; | |
| 58 | |
| 59 //////////////////////////////////////////////////////////////////////////////// | |
| 60 | |
| 61 @interface MCSettingsButton : NSButton | |
| 62 @end | |
| 63 | |
| 64 @implementation MCSettingsButton | |
| 65 // drawRect: needs to fill the button with a background, otherwise we don't get | |
| 66 // subpixel antialiasing. | |
| 67 - (void)drawRect:(NSRect)dirtyRect { | |
| 68 NSColor* color = gfx::SkColorToCalibratedNSColor( | |
| 69 message_center::kMessageCenterBackgroundColor); | |
| 70 [color set]; | |
| 71 NSRectFill(dirtyRect); | |
| 72 [super drawRect:dirtyRect]; | |
| 73 } | |
| 74 @end | |
| 75 | |
| 76 @interface MCSettingsButtonCell : NSButtonCell { | |
| 77 // A checkbox's regular image is the checkmark image. This additional image | |
| 78 // is used for the favicon or app icon shown next to the checkmark. | |
| 79 base::scoped_nsobject<NSImage> extraImage_; | |
| 80 } | |
| 81 - (void)setExtraImage:(NSImage*)extraImage; | |
| 82 @end | |
| 83 | |
| 84 @implementation MCSettingsButtonCell | |
| 85 - (BOOL)isOpaque { | |
| 86 return YES; | |
| 87 } | |
| 88 | |
| 89 - (void)setExtraImage:(NSImage*)extraImage { | |
| 90 extraImage_.reset([extraImage retain]); | |
| 91 } | |
| 92 | |
| 93 - (NSRect)drawTitle:(NSAttributedString*)title | |
| 94 withFrame:(NSRect)frame | |
| 95 inView:(NSView*)controlView { | |
| 96 CGFloat inset = kCorrectedCheckmarkRightPadding; | |
| 97 // drawTitle:withFrame:inView: draws the checkmark image. Draw the extra | |
| 98 // image as part of the checkbox's text. | |
| 99 if (extraImage_) { | |
| 100 NSRect imageRect = frame; | |
| 101 imageRect.origin.x += inset; | |
| 102 // Center the image vertically. | |
| 103 if (NSHeight(frame) > kEntryIconSize) | |
| 104 imageRect.origin.y += (NSHeight(frame) - kEntryIconSize) / 2; | |
| 105 imageRect.size = NSMakeSize(kEntryIconSize, kEntryIconSize); | |
| 106 [extraImage_ drawInRect:imageRect | |
| 107 fromRect:NSZeroRect | |
| 108 operation:NSCompositeSourceOver | |
| 109 fraction:1.0 | |
| 110 respectFlipped:YES | |
| 111 hints:nil]; | |
| 112 | |
| 113 inset += kEntryIconSize + kCorrectedIconTextPadding; | |
| 114 } | |
| 115 frame.origin.x += inset; | |
| 116 frame.size.width -= inset; | |
| 117 return [super drawTitle:title withFrame:frame inView:controlView]; | |
| 118 } | |
| 119 | |
| 120 - (NSSize)cellSizeForBounds:(NSRect)aRect { | |
| 121 NSSize size = [super cellSizeForBounds:aRect]; | |
| 122 size.width += kCorrectedCheckmarkRightPadding; | |
| 123 if (extraImage_) { | |
| 124 size.width += kEntryIconSize + kCorrectedIconTextPadding; | |
| 125 size.height = std::max(static_cast<CGFloat>(kEntryIconSize), size.height); | |
| 126 } | |
| 127 return size; | |
| 128 } | |
| 129 | |
| 130 - (NSUInteger)hitTestForEvent:(NSEvent*)event | |
| 131 inRect:(NSRect)cellFrame | |
| 132 ofView:(NSView*)controlView { | |
| 133 NSUInteger result = | |
| 134 [super hitTestForEvent:event inRect:cellFrame ofView:controlView]; | |
| 135 if (result == NSCellHitNone) { | |
| 136 // The default button cell does hit testing on the attributed string. Since | |
| 137 // this cell draws additional spacing and an icon in front of the string, | |
| 138 // tweak the hit testing result. | |
| 139 NSPoint point = | |
| 140 [controlView convertPoint:[event locationInWindow] fromView:nil]; | |
| 141 | |
| 142 NSRect rect = [self titleRectForBounds:[controlView bounds]]; | |
| 143 rect.size = [[self attributedTitle] size]; | |
| 144 rect.size.width += kCorrectedCheckmarkRightPadding; | |
| 145 | |
| 146 if (extraImage_) | |
| 147 rect.size.width += kEntryIconSize + kCorrectedIconTextPadding; | |
| 148 | |
| 149 if (NSPointInRect(point, rect)) | |
| 150 result = NSCellHitContentArea | NSCellHitTrackableArea; | |
| 151 } | |
| 152 return result; | |
| 153 } | |
| 154 @end | |
| 155 | |
| 156 @implementation MCSettingsEntryView | |
| 157 - (id)initWithController:(MCSettingsController*)controller | |
| 158 notifier:(message_center::Notifier*)notifier | |
| 159 frame:(NSRect)frame | |
| 160 hasSeparator:(BOOL)hasSeparator { | |
| 161 if ((self = [super initWithFrame:frame])) { | |
| 162 [self setBoxType:NSBoxCustom]; | |
| 163 [self setBorderType:NSNoBorder]; | |
| 164 [self setTitlePosition:NSNoTitle]; | |
| 165 [self setContentViewMargins:NSZeroSize]; | |
| 166 | |
| 167 hasSeparator_ = hasSeparator; | |
| 168 controller_ = controller; | |
| 169 notifier_ = notifier; | |
| 170 if (!notifier->icon.IsEmpty()) | |
| 171 notifierIcon_.reset(notifier->icon.CopyNSImage()); | |
| 172 [self layoutEntryView]; | |
| 173 } | |
| 174 return self; | |
| 175 } | |
| 176 | |
| 177 - (void)setNotifierIcon:(NSImage*)notifierIcon { | |
| 178 notifierIcon_.reset([notifierIcon retain]); | |
| 179 [self layoutEntryView]; | |
| 180 } | |
| 181 | |
| 182 - (NSButton*)checkbox { | |
| 183 return checkbox_; | |
| 184 } | |
| 185 | |
| 186 - (void)layoutEntryView { | |
| 187 BOOL hasLearnMore = | |
| 188 [controller_ notifierHasAdvancedSettings:notifier_->notifier_id]; | |
| 189 | |
| 190 // Now calculate the space available for the checkbox button. | |
| 191 NSRect checkboxFrame = [self bounds]; | |
| 192 checkboxFrame.origin.x += kCorrectedCheckmarkLeftPadding; | |
| 193 checkboxFrame.size.width -= | |
| 194 kCorrectedCheckmarkLeftPadding + kCorrectedEntryRightPadding; | |
| 195 | |
| 196 NSRect learnMoreFrame = | |
| 197 NSMakeRect(checkboxFrame.origin.x + checkboxFrame.size.width, | |
| 198 checkboxFrame.origin.y, | |
| 199 0, | |
| 200 checkboxFrame.size.height); | |
| 201 | |
| 202 // Initially place the learn more button right-aligned. | |
| 203 if (hasLearnMore) { | |
| 204 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); | |
| 205 NSImage* defaultImage = | |
| 206 rb.GetNativeImageNamed(IDR_NOTIFICATION_ADVANCED_SETTINGS).ToNSImage(); | |
| 207 NSSize defaultImageSize = [defaultImage size]; | |
| 208 learnMoreFrame.size.width = defaultImageSize.width; | |
| 209 learnMoreFrame.origin.x -= defaultImageSize.width; | |
| 210 | |
| 211 // May need to center the image if it's shorter than the entry. | |
| 212 if (defaultImageSize.height < learnMoreFrame.size.height) { | |
| 213 learnMoreFrame.origin.y += | |
| 214 (learnMoreFrame.size.height - defaultImageSize.height) / 2; | |
| 215 learnMoreFrame.size.height = defaultImageSize.height; | |
| 216 } | |
| 217 | |
| 218 // Since we have an image then we need to ensure that the text from the | |
| 219 // checkbox doesn't overlap with the learn more image. | |
| 220 checkboxFrame.size.width -= | |
| 221 kCorrectedIconTextPadding + learnMoreFrame.size.width; | |
| 222 | |
| 223 if (!learnMoreButton_.get()) { | |
| 224 learnMoreButton_.reset( | |
| 225 [[HoverImageButton alloc] initWithFrame:learnMoreFrame]); | |
| 226 [self addSubview:learnMoreButton_]; | |
| 227 } else { | |
| 228 [learnMoreButton_ setFrame:learnMoreFrame]; | |
| 229 } | |
| 230 [learnMoreButton_ setDefaultImage:defaultImage]; | |
| 231 [learnMoreButton_ setHoverImage:rb.GetNativeImageNamed( | |
| 232 IDR_NOTIFICATION_ADVANCED_SETTINGS_HOVER).ToNSImage()]; | |
| 233 [learnMoreButton_ setPressedImage:rb.GetNativeImageNamed( | |
| 234 IDR_NOTIFICATION_ADVANCED_SETTINGS_PRESSED).ToNSImage()]; | |
| 235 [learnMoreButton_ setBordered:NO]; | |
| 236 [learnMoreButton_ setTarget:self]; | |
| 237 [learnMoreButton_ setAction:@selector(learnMoreClicked:)]; | |
| 238 } | |
| 239 | |
| 240 if (!checkbox_.get()) { | |
| 241 checkbox_.reset([[MCSettingsButton alloc] initWithFrame:checkboxFrame]); | |
| 242 [self addSubview:checkbox_]; | |
| 243 } else { | |
| 244 [checkbox_ setFrame:checkboxFrame]; | |
| 245 } | |
| 246 | |
| 247 base::scoped_nsobject<MCSettingsButtonCell> cell([[MCSettingsButtonCell alloc] | |
| 248 initTextCell:base::SysUTF16ToNSString(notifier_->name)]); | |
| 249 if ([notifierIcon_ isValid]) | |
| 250 [cell setExtraImage:notifierIcon_]; | |
| 251 | |
| 252 [checkbox_ setCell:cell]; | |
| 253 [checkbox_ setButtonType:NSSwitchButton]; | |
| 254 [checkbox_ setState:notifier_->enabled ? NSOnState : NSOffState]; | |
| 255 [checkbox_ setTarget:self]; | |
| 256 [checkbox_ setAction:@selector(checkboxClicked:)]; | |
| 257 | |
| 258 if (hasSeparator_) { | |
| 259 NSRect separatorRect = [self bounds]; | |
| 260 separatorRect.size.height = 1; | |
| 261 if (!separator_.get()) { | |
| 262 separator_.reset([[NSBox alloc] initWithFrame:separatorRect]); | |
| 263 [separator_ setBoxType:NSBoxCustom]; | |
| 264 [separator_ setBorderType:NSLineBorder]; | |
| 265 [separator_ setBorderColor:gfx::SkColorToCalibratedNSColor( | |
| 266 message_center::settings::kEntrySeparatorColor)]; | |
| 267 [separator_ setTitlePosition:NSNoTitle]; | |
| 268 [separator_ setContentViewMargins:NSZeroSize]; | |
| 269 [self addSubview:separator_]; | |
| 270 } else { | |
| 271 [separator_ setFrame:separatorRect]; | |
| 272 } | |
| 273 } | |
| 274 } | |
| 275 | |
| 276 - (void)checkboxClicked:(id)sender { | |
| 277 BOOL enabled = [sender state] == NSOnState; | |
| 278 [controller_ setSettingsNotifier:notifier_ enabled:enabled]; | |
| 279 } | |
| 280 | |
| 281 - (void)learnMoreClicked:(id)sender { | |
| 282 [controller_ learnMoreClicked:notifier_]; | |
| 283 } | |
| 284 | |
| 285 // Testing API ///////////////////////////////////////////////////////////////// | |
| 286 | |
| 287 - (void)clickLearnMore { | |
| 288 [learnMoreButton_ performClick:nil]; | |
| 289 } | |
| 290 | |
| 291 @end | |
| 292 | |
| 293 /////////////////////////////////////////////////////////////////////////////// | |
| OLD | NEW |