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.
5 #import "ui/message_center/cocoa/settings_entry_view.h"
9 #include "base/strings/sys_string_conversions.h"
10 #include "grit/ui_resources.h"
11 #include "skia/ext/skia_utils_mac.h"
12 #include "ui/base/resource/resource_bundle.h"
13 #import "ui/message_center/cocoa/settings_controller.h"
14 #include "ui/message_center/message_center_style.h"
16 using message_center::settings::kEntryIconSize;
17 using message_center::settings::kInternalHorizontalSpacing;
19 // Size of the widget rendered for us by Cocoa.
20 const int kCocoaCheckboxSize = 14;
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
28 const int kIntrinsicTextLeftPadding = 1;
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;
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) /
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;
49 // The amount of space we want, based on the spec and the intrinsic text space
51 const int kCorrectedIconTextPadding =
52 kInternalHorizontalSpacing - kIntrinsicTextLeftPadding;
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;
59 @interface MCSettingsButtonCell : NSButtonCell {
60 // A checkbox's regular image is the checkmark image. This additional image
61 // is used for the favicon or app icon shown next to the checkmark.
62 base::scoped_nsobject<NSImage> extraImage_;
64 - (void)setExtraImage:(NSImage*)extraImage;
67 @implementation MCSettingsButtonCell
68 - (void)setExtraImage:(NSImage*)extraImage {
69 extraImage_.reset([extraImage retain]);
72 - (NSRect)drawTitle:(NSAttributedString*)title
73 withFrame:(NSRect)frame
74 inView:(NSView*)controlView {
75 CGFloat inset = kCorrectedCheckmarkRightPadding;
76 // drawTitle:withFrame:inView: draws the checkmark image. Draw the extra
77 // image as part of the checkbox's text.
79 NSRect imageRect = frame;
80 imageRect.origin.x += inset;
81 // Center the image vertically.
82 if (NSHeight(frame) > kEntryIconSize)
83 imageRect.origin.y += (NSHeight(frame) - kEntryIconSize) / 2;
84 imageRect.size = NSMakeSize(kEntryIconSize, kEntryIconSize);
85 [extraImage_ drawInRect:imageRect
87 operation:NSCompositeSourceOver
92 inset += kEntryIconSize + kCorrectedIconTextPadding;
94 frame.origin.x += inset;
95 frame.size.width -= inset;
96 return [super drawTitle:title withFrame:frame inView:controlView];
99 - (NSSize)cellSizeForBounds:(NSRect)aRect {
100 NSSize size = [super cellSizeForBounds:aRect];
101 size.width += kCorrectedCheckmarkRightPadding;
103 size.width += kEntryIconSize + kCorrectedIconTextPadding;
104 size.height = std::max(static_cast<CGFloat>(kEntryIconSize), size.height);
109 - (NSUInteger)hitTestForEvent:(NSEvent*)event
110 inRect:(NSRect)cellFrame
111 ofView:(NSView*)controlView {
113 [super hitTestForEvent:event inRect:cellFrame ofView:controlView];
114 if (result == NSCellHitNone) {
115 // The default button cell does hit testing on the attributed string. Since
116 // this cell draws additional spacing and an icon in front of the string,
117 // tweak the hit testing result.
119 [controlView convertPoint:[event locationInWindow] fromView:nil];
121 NSRect rect = [self titleRectForBounds:[controlView bounds]];
122 rect.size = [[self attributedTitle] size];
123 rect.size.width += kCorrectedCheckmarkRightPadding;
126 rect.size.width += kEntryIconSize + kCorrectedIconTextPadding;
128 if (NSPointInRect(point, rect))
129 result = NSCellHitContentArea | NSCellHitTrackableArea;
135 @implementation MCSettingsEntryView
137 - (id)initWithController:(MCSettingsController*)controller
138 notifier:(message_center::Notifier*)notifier
140 hasSeparator:(BOOL)hasSeparator {
141 if ((self = [super initWithFrame:frame])) {
142 [self setBoxType:NSBoxCustom];
143 [self setBorderType:NSNoBorder];
144 [self setTitlePosition:NSNoTitle];
145 [self setContentViewMargins:NSZeroSize];
147 hasSeparator_ = hasSeparator;
148 controller_ = controller;
149 notifier_ = notifier;
150 if (!notifier->icon.IsEmpty())
151 notifierIcon_.reset(notifier->icon.CopyNSImage());
157 - (void)setNotifierIcon:(NSImage*)notifierIcon {
158 notifierIcon_.reset([notifierIcon retain]);
162 - (NSButton*)checkbox {
168 [controller_ notifierHasAdvancedSettings:notifier_->notifier_id];
170 // Now calculate the space available for the checkbox button.
171 NSRect checkboxFrame = [self bounds];
172 checkboxFrame.origin.x += kCorrectedCheckmarkLeftPadding;
173 checkboxFrame.size.width -=
174 kCorrectedCheckmarkLeftPadding + kCorrectedEntryRightPadding;
176 NSRect learnMoreFrame =
177 NSMakeRect(checkboxFrame.origin.x + checkboxFrame.size.width,
178 checkboxFrame.origin.y,
180 checkboxFrame.size.height);
182 // Initially place the learn more button right-aligned.
184 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
185 NSImage* defaultImage =
186 rb.GetNativeImageNamed(IDR_NOTIFICATION_ADVANCED_SETTINGS).ToNSImage();
187 NSSize defaultImageSize = [defaultImage size];
188 learnMoreFrame.size.width = defaultImageSize.width;
189 learnMoreFrame.origin.x -= defaultImageSize.width;
191 // May need to center the image if it's shorter than the entry.
192 if (defaultImageSize.height < learnMoreFrame.size.height) {
193 learnMoreFrame.origin.y +=
194 (learnMoreFrame.size.height - defaultImageSize.height) / 2;
195 learnMoreFrame.size.height = defaultImageSize.height;
198 // Since we have an image then we need to ensure that the text from the
199 // checkbox doesn't overlap with the learn more image.
200 checkboxFrame.size.width -=
201 kCorrectedIconTextPadding + learnMoreFrame.size.width;
203 if (!learnMoreButton_.get()) {
204 learnMoreButton_.reset(
205 [[HoverImageButton alloc] initWithFrame:learnMoreFrame]);
206 [self addSubview:learnMoreButton_];
208 [learnMoreButton_ setFrame:learnMoreFrame];
210 [learnMoreButton_ setDefaultImage:defaultImage];
211 [learnMoreButton_ setHoverImage:rb.GetNativeImageNamed(
212 IDR_NOTIFICATION_ADVANCED_SETTINGS_HOVER).ToNSImage()];
213 [learnMoreButton_ setPressedImage:rb.GetNativeImageNamed(
214 IDR_NOTIFICATION_ADVANCED_SETTINGS_PRESSED).ToNSImage()];
215 [learnMoreButton_ setBordered:NO];
216 [learnMoreButton_ setTarget:self];
217 [learnMoreButton_ setAction:@selector(learnMoreClicked:)];
220 if (!checkbox_.get()) {
221 checkbox_.reset([[NSButton alloc] initWithFrame:checkboxFrame]);
222 [self addSubview:checkbox_];
224 [checkbox_ setFrame:checkboxFrame];
227 base::scoped_nsobject<MCSettingsButtonCell> cell([[MCSettingsButtonCell alloc]
228 initTextCell:base::SysUTF16ToNSString(notifier_->name)]);
229 if ([notifierIcon_ isValid])
230 [cell setExtraImage:notifierIcon_];
232 [checkbox_ setCell:cell];
233 [checkbox_ setButtonType:NSSwitchButton];
234 [checkbox_ setState:notifier_->enabled ? NSOnState : NSOffState];
235 [checkbox_ setTarget:self];
236 [checkbox_ setAction:@selector(checkboxClicked:)];
239 NSRect separatorRect = [self bounds];
240 separatorRect.size.height = 1;
241 if (!separator_.get()) {
242 separator_.reset([[NSBox alloc] initWithFrame:separatorRect]);
243 [separator_ setBoxType:NSBoxCustom];
244 [separator_ setBorderType:NSLineBorder];
245 [separator_ setBorderColor:gfx::SkColorToCalibratedNSColor(
246 message_center::settings::kEntrySeparatorColor)];
247 [separator_ setTitlePosition:NSNoTitle];
248 [separator_ setContentViewMargins:NSZeroSize];
249 [self addSubview:separator_];
251 [separator_ setFrame:separatorRect];
256 - (void)checkboxClicked:(id)sender {
257 BOOL enabled = [sender state] == NSOnState;
258 [controller_ setSettingsNotifier:notifier_ enabled:enabled];
261 - (void)learnMoreClicked:(id)sender {
262 [controller_ learnMoreClicked:notifier_];
265 // Testing API /////////////////////////////////////////////////////////////////
267 - (void)clickLearnMore {
268 [learnMoreButton_ performClick:nil];
273 ///////////////////////////////////////////////////////////////////////////////