summaryrefslogtreecommitdiffstats
path: root/ui/message_center
diff options
context:
space:
mode:
authordewittj@chromium.org <dewittj@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-11-02 03:37:56 +0000
committerdewittj@chromium.org <dewittj@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-11-02 03:37:56 +0000
commit550168f4d786aaff146cfcc983d6d24746b6b265 (patch)
tree77482c471509ca19ad36bd5bc0a63a631a299f09 /ui/message_center
parent6a4dd280784952cc513fe5999d9bdcae7d93b017 (diff)
downloadchromium_src-550168f4d786aaff146cfcc983d6d24746b6b265.zip
chromium_src-550168f4d786aaff146cfcc983d6d24746b6b265.tar.gz
chromium_src-550168f4d786aaff146cfcc983d6d24746b6b265.tar.bz2
Reland 232439: Add a way for notifications to be linked from settings dialog.
Adds an event, onShowSettings, that is fired when the link is clicked. The link is only shown when the event is subscribed to. TBR=miket@chromium.org,oshima@chromium.org,rsesek@chromium.org,mukai@chromium.org BUG=304208, 286608 Committed: https://src.chromium.org/viewvc/chrome?view=rev&revision=232469 Review URL: https://codereview.chromium.org/54673015 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@232569 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ui/message_center')
-rw-r--r--ui/message_center/cocoa/settings_controller.h13
-rw-r--r--ui/message_center/cocoa/settings_controller.mm232
-rw-r--r--ui/message_center/cocoa/settings_controller_unittest.mm53
-rw-r--r--ui/message_center/cocoa/settings_entry_view.h58
-rw-r--r--ui/message_center/cocoa/settings_entry_view.mm273
-rw-r--r--ui/message_center/fake_notifier_settings_provider.cc29
-rw-r--r--ui/message_center/fake_notifier_settings_provider.h19
-rw-r--r--ui/message_center/message_center.gyp13
-rw-r--r--ui/message_center/message_center_style.h16
-rw-r--r--ui/message_center/notifier_settings.h10
-rw-r--r--ui/message_center/views/message_center_button_bar.cc8
-rw-r--r--ui/message_center/views/message_center_focus_border.cc25
-rw-r--r--ui/message_center/views/message_center_focus_border.h30
-rw-r--r--ui/message_center/views/message_center_view.cc39
-rw-r--r--ui/message_center/views/notifier_settings_view.cc396
-rw-r--r--ui/message_center/views/notifier_settings_view.h43
-rw-r--r--ui/message_center/views/notifier_settings_view_unittest.cc124
17 files changed, 1092 insertions, 289 deletions
diff --git a/ui/message_center/cocoa/settings_controller.h b/ui/message_center/cocoa/settings_controller.h
index 7bce5ab..522e7e7 100644
--- a/ui/message_center/cocoa/settings_controller.h
+++ b/ui/message_center/cocoa/settings_controller.h
@@ -9,6 +9,7 @@
#import "base/mac/scoped_nsobject.h"
#include "base/memory/scoped_ptr.h"
+#import "ui/message_center/cocoa/settings_entry_view.h"
#include "ui/message_center/message_center_export.h"
#include "ui/message_center/notifier_settings.h"
@@ -37,7 +38,6 @@ class NotifierSettingsObserverMac : public NotifierSettingsObserver {
} // namespace message_center
-
// The view controller responsible for the settings sheet in the center.
MESSAGE_CENTER_EXPORT
@interface MCSettingsController : NSViewController {
@@ -65,6 +65,17 @@ MESSAGE_CENTER_EXPORT
- (id)initWithProvider:(message_center::NotifierSettingsProvider*)provider
trayViewController:(MCTrayViewController*)trayViewController;
+// Returns whether |provider_| has an advanced settings handler for the given
+// notifier; i.e. we should show the "Learn More" button.
+- (BOOL)notifierHasAdvancedSettings:(const message_center::NotifierId&)id;
+
+// Handler when a checkbox is enabled/disabled.
+- (void)setSettingsNotifier:(message_center::Notifier*)notifier
+ enabled:(BOOL)enabled;
+
+// Handler when the learn more link is clicked.
+- (void)learnMoreClicked:(message_center::Notifier*)notifier;
+
@end
// Testing API /////////////////////////////////////////////////////////////////
diff --git a/ui/message_center/cocoa/settings_controller.mm b/ui/message_center/cocoa/settings_controller.mm
index 8865dd9..82f2c46 100644
--- a/ui/message_center/cocoa/settings_controller.mm
+++ b/ui/message_center/cocoa/settings_controller.mm
@@ -4,6 +4,8 @@
#import "ui/message_center/cocoa/settings_controller.h"
+#include <algorithm>
+
#include "base/mac/foundation_util.h"
#import "base/mac/scoped_nsobject.h"
#include "base/stl_util.h"
@@ -11,100 +13,59 @@
#include "grit/ui_strings.h"
#include "skia/ext/skia_utils_mac.h"
#include "ui/base/l10n/l10n_util.h"
-#include "ui/base/resource/resource_bundle.h"
+#import "ui/message_center/cocoa/settings_entry_view.h"
#import "ui/message_center/cocoa/tray_view_controller.h"
#include "ui/message_center/message_center_style.h"
-const int kMarginWidth = 16;
-const int kEntryHeight = 38;
-const int kIconSize = 16;
-const int kIconTextPadding = 8;
-const int kCheckmarkIconPadding = 16;
-
-const int kIntrinsicCheckmarkPadding = 4; // Padding already provided by Cocoa.
-const int kCorrectedCheckmarkPadding =
- kCheckmarkIconPadding - kIntrinsicCheckmarkPadding;
-
-@interface MCSettingsButtonCell : NSButtonCell {
- // A checkbox's regular image is the checkmark image. This additional image
- // is used for the favicon or app icon shown next to the checkmark.
- base::scoped_nsobject<NSImage> extraImage_;
-}
-- (void)setExtraImage:(NSImage*)extraImage;
-@end
-
-@implementation MCSettingsButtonCell
-- (void)setExtraImage:(NSImage*)extraImage {
- extraImage_.reset([extraImage retain]);
-}
-
-- (NSRect)drawTitle:(NSAttributedString*)title
- withFrame:(NSRect)frame
- inView:(NSView*)controlView {
- CGFloat inset = kCorrectedCheckmarkPadding;
- // drawTitle:withFrame:inView: draws the checkmark image. Draw the extra
- // image as part of the checkbox's text.
- if (extraImage_) {
- NSRect imageRect = frame;
- imageRect.origin.x += inset;
- imageRect.size = NSMakeSize(kIconSize, kIconSize);
- [extraImage_ drawInRect:imageRect
- fromRect:NSZeroRect
- operation:NSCompositeSourceOver
- fraction:1.0
- respectFlipped:YES
- hints:nil];
-
- inset += kIconSize + kIconTextPadding;
- }
- frame.origin.x += inset;
- frame.size.width -= inset;
- return [super drawTitle:title withFrame:frame inView:controlView];
-}
-
-- (NSUInteger)hitTestForEvent:(NSEvent*)event
- inRect:(NSRect)cellFrame
- ofView:(NSView*)controlView {
- NSUInteger result =
- [super hitTestForEvent:event inRect:cellFrame ofView:controlView];
- if (result == NSCellHitNone) {
- // The default button cell does hit testing on the attributed string. Since
- // this cell draws additional spacing and an icon in front of the string,
- // tweak the hit testing result.
- NSPoint point =
- [controlView convertPoint:[event locationInWindow] fromView:nil];
-
- NSRect rect = [self titleRectForBounds:[controlView bounds]];
- rect.size = [[self attributedTitle] size];
- rect.size.width += kCorrectedCheckmarkPadding;
-
- if (extraImage_) {
- rect.size.width +=
- kIconSize + kIconTextPadding + kCorrectedCheckmarkPadding;
- }
-
- if (NSPointInRect(point, rect))
- result = NSCellHitContentArea | NSCellHitTrackableArea;
- }
- return result;
-}
-@end
+using message_center::settings::kHorizontalMargin;
+using message_center::settings::kEntryHeight;
+
+// Intrinsic padding pixels out of our control.
+const int kIntrinsicHeaderTextTopPadding = 3;
+const int kIntrinsicSubheaderTextTopPadding = 5;
+const int kIntrinsicSubheaderTextBottomPadding = 3;
+const int kIntrinsicDropDownVerticalPadding = 2;
+const int kIntrinsicDropDownHorizontalPadding = 3;
+
+// Corrected padding values used in layout.
+// Calculated additional blank space above the header text, including
+// the intrinsic blank space above the header label.
+const int kCorrectedHeaderTextTopPadding =
+ message_center::settings::kTopMargin - kIntrinsicHeaderTextTopPadding;
+
+// Calculated additional blank space above the subheader text, including
+// the intrinsic blank space above the subheader label.
+const int kCorrectedSubheaderTextTopPadding =
+ message_center::settings::kTitleToDescriptionSpace -
+ kIntrinsicSubheaderTextTopPadding;
+
+// Calcoulated additional vertical padding for the drop-down, including the
+// blank space included with the drop-down control.
+const int kCorrectedDropDownTopPadding =
+ message_center::settings::kDescriptionToSwitcherSpace -
+ kIntrinsicDropDownVerticalPadding - kIntrinsicSubheaderTextBottomPadding;
+
+// Calculated additional horizontal blank space for the drop down, including
+// the blank space included with the drop-down control.
+const int kCorrectedDropDownMargin =
+ kHorizontalMargin - kIntrinsicDropDownHorizontalPadding;
@interface MCSettingsController (Private)
// Sets the icon on the checkbox corresponding to |notifiers_[index]|.
- (void)setIcon:(NSImage*)icon forNotifierIndex:(size_t)index;
- (void)setIcon:(NSImage*)icon
- forNotifierId:(const message_center::NotifierId&)id;
+ forNotifierId:(const message_center::NotifierId&)id;
// Returns the NSButton corresponding to the checkbox for |notifiers_[index]|.
-- (NSButton*)buttonForNotifierAtIndex:(size_t)index;
+- (MCSettingsEntryView*)entryForNotifierAtIndex:(size_t)index;
// Update the contents view.
- (void)updateView;
// Handler for the notifier group dropdown menu.
- (void)notifierGroupSelectionChanged:(id)sender;
+
@end
namespace message_center {
@@ -175,32 +136,36 @@ void NotifierSettingsObserverMac::NotifierGroupChanged() {
[self setView:view];
// "Settings" text.
- NSRect headerFrame = NSMakeRect(
- kMarginWidth, kMarginWidth, NSWidth(fullFrame), NSHeight(fullFrame));
+ NSRect headerFrame = NSMakeRect(kHorizontalMargin,
+ kHorizontalMargin,
+ NSWidth(fullFrame),
+ NSHeight(fullFrame));
settingsText_.reset([self newLabelWithFrame:headerFrame]);
[settingsText_ setAutoresizingMask:NSViewMinYMargin];
- [settingsText_ setTextColor:gfx::SkColorToCalibratedNSColor(
- message_center::kRegularTextColor)];
- [settingsText_ setFont:
- [NSFont messageFontOfSize:message_center::kTitleFontSize]];
+ [settingsText_ setTextColor:
+ gfx::SkColorToCalibratedNSColor(message_center::kRegularTextColor)];
+ [settingsText_
+ setFont:[NSFont messageFontOfSize:message_center::kTitleFontSize]];
[settingsText_ setStringValue:
- l10n_util::GetNSString(IDS_MESSAGE_CENTER_SETTINGS_BUTTON_LABEL)];
+ l10n_util::GetNSString(IDS_MESSAGE_CENTER_SETTINGS_BUTTON_LABEL)];
[settingsText_ sizeToFit];
headerFrame = [settingsText_ frame];
- headerFrame.origin.y =
- NSMaxY(fullFrame) - kMarginWidth - NSHeight(headerFrame);
+ headerFrame.origin.y = NSMaxY(fullFrame) - kCorrectedHeaderTextTopPadding -
+ NSHeight(headerFrame);
[[self view] addSubview:settingsText_];
// Subheader.
- NSRect subheaderFrame = NSMakeRect(
- kMarginWidth, kMarginWidth, NSWidth(fullFrame), NSHeight(fullFrame));
+ NSRect subheaderFrame = NSMakeRect(kHorizontalMargin,
+ kHorizontalMargin,
+ NSWidth(fullFrame),
+ NSHeight(fullFrame));
detailsText_.reset([self newLabelWithFrame:subheaderFrame]);
[detailsText_ setAutoresizingMask:NSViewMinYMargin];
- [detailsText_ setTextColor:gfx::SkColorToCalibratedNSColor(
- message_center::kDimTextColor)];
- [detailsText_ setFont:
- [NSFont messageFontOfSize:message_center::kMessageFontSize]];
+ [detailsText_ setTextColor:
+ gfx::SkColorToCalibratedNSColor(message_center::kDimTextColor)];
+ [detailsText_
+ setFont:[NSFont messageFontOfSize:message_center::kMessageFontSize]];
size_t groupCount = provider_->GetNotifierGroupCount();
[detailsText_ setStringValue:l10n_util::GetNSString(
@@ -209,15 +174,17 @@ void NotifierSettingsObserverMac::NotifierGroupChanged() {
[detailsText_ sizeToFit];
subheaderFrame = [detailsText_ frame];
subheaderFrame.origin.y =
- NSMinY(headerFrame) - message_center::kTextTopPadding -
+ NSMinY(headerFrame) - kCorrectedSubheaderTextTopPadding -
NSHeight(subheaderFrame);
[[self view] addSubview:detailsText_];
// Profile switcher is only needed for more than one profile.
NSRect dropDownButtonFrame = subheaderFrame;
if (groupCount > 1) {
- dropDownButtonFrame = NSMakeRect(
- kMarginWidth, kMarginWidth, NSWidth(fullFrame), NSHeight(fullFrame));
+ dropDownButtonFrame = NSMakeRect(kCorrectedDropDownMargin,
+ kHorizontalMargin,
+ NSWidth(fullFrame),
+ NSHeight(fullFrame));
groupDropDownButton_.reset(
[[NSPopUpButton alloc] initWithFrame:dropDownButtonFrame
pullsDown:YES]);
@@ -240,9 +207,10 @@ void NotifierSettingsObserverMac::NotifierGroupChanged() {
[groupDropDownButton_ sizeToFit];
dropDownButtonFrame = [groupDropDownButton_ frame];
dropDownButtonFrame.origin.y =
- NSMinY(subheaderFrame) - message_center::kTextTopPadding -
+ NSMinY(subheaderFrame) - kCorrectedDropDownTopPadding -
NSHeight(dropDownButtonFrame);
- dropDownButtonFrame.size.width = NSWidth(fullFrame) - 2 * kMarginWidth;
+ dropDownButtonFrame.size.width =
+ NSWidth(fullFrame) - 2 * kCorrectedDropDownMargin;
[[self view] addSubview:groupDropDownButton_];
}
@@ -251,40 +219,32 @@ void NotifierSettingsObserverMac::NotifierGroupChanged() {
NSRect documentFrame = NSMakeRect(0, 0, NSWidth(fullFrame), 0);
base::scoped_nsobject<NSView> documentView(
[[NSView alloc] initWithFrame:documentFrame]);
- for (int i = notifiers_.size() - 1; i >= 0; --i) {
+ int notifierCount = notifiers_.size();
+ for (int i = notifierCount - 1; i >= 0; --i) {
message_center::Notifier* notifier = notifiers_[i];
-
// TODO(thakis): Use a custom button cell.
- NSRect frame = NSMakeRect(
- kMarginWidth, y, NSWidth(documentFrame) - kMarginWidth, kEntryHeight);
- base::scoped_nsobject<NSButton> button(
- [[NSButton alloc] initWithFrame:frame]);
- base::scoped_nsobject<MCSettingsButtonCell> cell(
- [[MCSettingsButtonCell alloc]
- initTextCell:base::SysUTF16ToNSString(notifier->name)]);
- if (!notifier->icon.IsEmpty())
- [cell setExtraImage:notifier->icon.AsNSImage()];
- [button setCell:cell];
- [button setButtonType:NSSwitchButton];
-
- [button setState:notifier->enabled ? NSOnState : NSOffState];
- [button setTag:i];
- [button setTarget:self];
- [button setAction:@selector(checkboxClicked:)];
-
- [documentView addSubview:button.release()];
-
+ NSRect frame = NSMakeRect(kHorizontalMargin,
+ y,
+ NSWidth(documentFrame) - kHorizontalMargin * 2,
+ kEntryHeight);
+
+ base::scoped_nsobject<MCSettingsEntryView> entryView(
+ [[MCSettingsEntryView alloc]
+ initWithController:self
+ notifier:notifier
+ frame:frame
+ hasSeparator:(i != notifierCount - 1)]);
+ [documentView addSubview:entryView];
y += NSHeight(frame);
}
- documentFrame.size.height = y;
+
+ documentFrame.size.height = y - kIntrinsicDropDownVerticalPadding;
[documentView setFrame:documentFrame];
// Scroll view for the notifier settings.
NSRect scrollFrame = documentFrame;
- scrollFrame.origin.y = kMarginWidth;
- CGFloat remainingHeight =
- NSMinY(dropDownButtonFrame) - message_center::kTextTopPadding -
- NSMinY(scrollFrame);
+ scrollFrame.origin.y = 0;
+ CGFloat remainingHeight = NSMinY(dropDownButtonFrame) - NSMinY(scrollFrame);
if (NSHeight(documentFrame) < remainingHeight) {
// Everything fits without scrolling.
@@ -320,9 +280,13 @@ void NotifierSettingsObserverMac::NotifierGroupChanged() {
[groupDropDownButton_ setFrame:dropDownButtonFrame];
}
-- (void)checkboxClicked:(id)sender {
- provider_->SetNotifierEnabled(*notifiers_[[sender tag]],
- [sender state] == NSOnState);
+- (void)setSettingsNotifier:(message_center::Notifier*)notifier
+ enabled:(BOOL)enabled {
+ provider_->SetNotifierEnabled(*notifier, enabled);
+}
+
+- (void)learnMoreClicked:(message_center::Notifier*)notifier {
+ provider_->OnNotifierAdvancedSettingsRequested(notifier->notifier_id, NULL);
}
// Testing API /////////////////////////////////////////////////////////////////
@@ -338,13 +302,12 @@ void NotifierSettingsObserverMac::NotifierGroupChanged() {
// Private API /////////////////////////////////////////////////////////////////
- (void)setIcon:(NSImage*)icon forNotifierIndex:(size_t)index {
- NSButton* button = [self buttonForNotifierAtIndex:index];
- [[button cell] setExtraImage:icon];
- [button setNeedsDisplay:YES];
+ MCSettingsEntryView* entry = [self entryForNotifierAtIndex:index];
+ [entry setNotifierIcon:icon];
}
- (void)setIcon:(NSImage*)icon
- forNotifierId:(const message_center::NotifierId&)id {
+ forNotifierId:(const message_center::NotifierId&)id {
for (size_t i = 0; i < notifiers_.size(); ++i) {
if (notifiers_[i]->notifier_id == id) {
[self setIcon:icon forNotifierIndex:i];
@@ -353,12 +316,13 @@ void NotifierSettingsObserverMac::NotifierGroupChanged() {
}
}
-- (NSButton*)buttonForNotifierAtIndex:(size_t)index {
+- (MCSettingsEntryView*)entryForNotifierAtIndex:(size_t)index {
NSArray* subviews = [[scrollView_ documentView] subviews];
// The checkboxes are in bottom-top order, the checkbox for notifiers_[0] is
// last.
+ DCHECK_LT(notifiers_.size() - 1 - index, [subviews count]);
NSView* view = [subviews objectAtIndex:notifiers_.size() - 1 - index];
- return base::mac::ObjCCastStrict<NSButton>(view);
+ return base::mac::ObjCCastStrict<MCSettingsEntryView>(view);
}
- (void)notifierGroupSelectionChanged:(id)sender {
@@ -368,4 +332,8 @@ void NotifierSettingsObserverMac::NotifierGroupChanged() {
provider_->SwitchToNotifierGroup([button indexOfSelectedItem] - 1);
}
+- (BOOL)notifierHasAdvancedSettings:(const message_center::NotifierId&)id {
+ return provider_->NotifierHasAdvancedSettings(id);
+}
+
@end
diff --git a/ui/message_center/cocoa/settings_controller_unittest.mm b/ui/message_center/cocoa/settings_controller_unittest.mm
index 2e4a491..74f9f07 100644
--- a/ui/message_center/cocoa/settings_controller_unittest.mm
+++ b/ui/message_center/cocoa/settings_controller_unittest.mm
@@ -11,15 +11,16 @@
@implementation MCSettingsController (TestingInterface)
- (NSInteger)profileSwitcherListCount {
// Subtract the dummy item.
- return [self groupDropDownButton] ?
- [[self groupDropDownButton] numberOfItems] - 1 : 0;
+ return [self groupDropDownButton]
+ ? [[self groupDropDownButton] numberOfItems] - 1
+ : 0;
}
- (NSUInteger)scrollViewItemCount {
return [[[[self scrollView] documentView] subviews] count];
}
-- (NSButton*)bottomMostButton {
+- (MCSettingsEntryView*)bottomMostButton {
// The checkboxes are created bottom-to-top, so the first object is the
// bottom-most.
return [[[[self scrollView] documentView] subviews] objectAtIndex:0];
@@ -52,8 +53,8 @@ Notifier* NewNotifier(const std::string& id,
TEST_F(CocoaTest, Basic) {
// Notifiers are owned by settings controller.
std::vector<Notifier*> notifiers;
- notifiers.push_back(NewNotifier("id", "title", /*enabled=*/true));
- notifiers.push_back(NewNotifier("id2", "other title", /*enabled=*/false));
+ notifiers.push_back(NewNotifier("id", "title", /*enabled=*/ true));
+ notifiers.push_back(NewNotifier("id2", "other title", /*enabled=*/ false));
FakeNotifierSettingsProvider provider(notifiers);
@@ -68,8 +69,8 @@ TEST_F(CocoaTest, Basic) {
TEST_F(CocoaTest, Toggle) {
// Notifiers are owned by settings controller.
std::vector<Notifier*> notifiers;
- notifiers.push_back(NewNotifier("id", "title", /*enabled=*/true));
- notifiers.push_back(NewNotifier("id2", "other title", /*enabled=*/false));
+ notifiers.push_back(NewNotifier("id", "title", /*enabled=*/ true));
+ notifiers.push_back(NewNotifier("id2", "other title", /*enabled=*/ false));
FakeNotifierSettingsProvider provider(notifiers);
@@ -78,7 +79,8 @@ TEST_F(CocoaTest, Toggle) {
trayViewController:nil]);
[controller view];
- NSButton* toggleSecond = [controller bottomMostButton];
+ MCSettingsEntryView* toggleView = [controller bottomMostButton];
+ NSButton* toggleSecond = [toggleView checkbox];
[toggleSecond performClick:nil];
EXPECT_TRUE(provider.WasEnabled(*notifiers.back()));
@@ -94,8 +96,8 @@ TEST_F(CocoaTest, Toggle) {
TEST_F(CocoaTest, SingleProfile) {
// Notifiers are owned by settings controller.
std::vector<Notifier*> notifiers;
- notifiers.push_back(NewNotifier("id", "title", /*enabled=*/true));
- notifiers.push_back(NewNotifier("id2", "other title", /*enabled=*/false));
+ notifiers.push_back(NewNotifier("id", "title", /*enabled=*/ true));
+ notifiers.push_back(NewNotifier("id2", "other title", /*enabled=*/ false));
FakeNotifierSettingsProvider provider(notifiers);
@@ -110,13 +112,13 @@ TEST_F(CocoaTest, SingleProfile) {
TEST_F(CocoaTest, MultiProfile) {
FakeNotifierSettingsProvider provider;
std::vector<Notifier*> group1_notifiers;
- group1_notifiers.push_back(NewNotifier("id", "title", /*enabled=*/true));
- group1_notifiers.push_back(NewNotifier("id2", "title2", /*enabled=*/false));
+ group1_notifiers.push_back(NewNotifier("id", "title", /*enabled=*/ true));
+ group1_notifiers.push_back(NewNotifier("id2", "title2", /*enabled=*/ false));
provider.AddGroup(NewGroup("Group1", "GroupId1"), group1_notifiers);
std::vector<Notifier*> group2_notifiers;
- group2_notifiers.push_back(NewNotifier("id3", "title3", /*enabled=*/true));
- group2_notifiers.push_back(NewNotifier("id4", "title4", /*enabled=*/false));
- group2_notifiers.push_back(NewNotifier("id5", "title5", /*enabled=*/false));
+ group2_notifiers.push_back(NewNotifier("id3", "title3", /*enabled=*/ true));
+ group2_notifiers.push_back(NewNotifier("id4", "title4", /*enabled=*/ false));
+ group2_notifiers.push_back(NewNotifier("id5", "title5", /*enabled=*/ false));
provider.AddGroup(NewGroup("Group2", "GroupId2"), group2_notifiers);
base::scoped_nsobject<MCSettingsController> controller(
@@ -127,4 +129,25 @@ TEST_F(CocoaTest, MultiProfile) {
EXPECT_EQ(2, [controller profileSwitcherListCount]);
}
+TEST_F(CocoaTest, LearnMoreButton) {
+ std::vector<Notifier*> notifiers;
+ notifiers.push_back(NewNotifier("id", "title", /*enabled=*/ true));
+ notifiers.push_back(NewNotifier("id2", "title2", /*enabled=*/ false));
+
+ FakeNotifierSettingsProvider provider(notifiers);
+ EXPECT_EQ(0u, provider.settings_requested_count());
+ NotifierId has_settings_handler_notifier =
+ NotifierId(NotifierId::APPLICATION, "id2");
+ provider.SetNotifierHasAdvancedSettings(has_settings_handler_notifier);
+
+ base::scoped_nsobject<MCSettingsController> controller(
+ [[MCSettingsController alloc] initWithProvider:&provider
+ trayViewController:nil]);
+ [controller view];
+
+ [[controller bottomMostButton] clickLearnMore];
+
+ EXPECT_EQ(1u, provider.settings_requested_count());
+}
+
} // namespace message_center
diff --git a/ui/message_center/cocoa/settings_entry_view.h b/ui/message_center/cocoa/settings_entry_view.h
new file mode 100644
index 0000000..e8902c6
--- /dev/null
+++ b/ui/message_center/cocoa/settings_entry_view.h
@@ -0,0 +1,58 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_MESSAGE_CENTER_COCOA_SETTINGS_ENTRY_VIEW_H_
+#define UI_MESSAGE_CENTER_COCOA_SETTINGS_ENTRY_VIEW_H_
+
+#import <Cocoa/Cocoa.h>
+
+#import "base/mac/scoped_nsobject.h"
+#import "ui/base/cocoa/hover_image_button.h"
+#include "ui/message_center/notifier_settings.h"
+
+@class MCSettingsController;
+
+// The view that renders individual notifiers in the settings sheet. This
+// includes an enable/disable checkbox, icon, name, and learn more button,
+// and an optional horizontal separator line.
+@interface MCSettingsEntryView : NSBox {
+ @private
+ // Weak. Owns us.
+ MCSettingsController* controller_;
+
+ // Weak, owned by MCSettingsController.
+ message_center::Notifier* notifier_;
+
+ // The image that will be displayed next to the notifier name, loaded
+ // asynchronously.
+ base::scoped_nsobject<NSImage> notifierIcon_;
+
+ // The button that can be displayed after the notifier name, that when
+ // clicked on causes an event to be fired for more information.
+ base::scoped_nsobject<HoverImageButton> learnMoreButton_;
+
+ // The button that contains the label, notifier icon and checkbox.
+ base::scoped_nsobject<NSButton> checkbox_;
+
+ // The border on the bottom of the view.
+ BOOL hasSeparator_;
+ base::scoped_nsobject<NSBox> separator_;
+}
+
+- (id)initWithController:(MCSettingsController*)controller
+ notifier:(message_center::Notifier*)notifier
+ frame:(NSRect)frame
+ hasSeparator:(BOOL)hasSeparator;
+
+- (void)setNotifierIcon:(NSImage*)notifierIcon;
+
+- (NSButton*)checkbox;
+
+@end
+
+@interface MCSettingsEntryView (TestingAPI)
+- (void)clickLearnMore;
+@end
+
+#endif // UI_MESSAGE_CENTER_COCOA_SETTINGS_ENTRY_VIEW_H_
diff --git a/ui/message_center/cocoa/settings_entry_view.mm b/ui/message_center/cocoa/settings_entry_view.mm
new file mode 100644
index 0000000..0607247
--- /dev/null
+++ b/ui/message_center/cocoa/settings_entry_view.mm
@@ -0,0 +1,273 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ui/message_center/cocoa/settings_entry_view.h"
+
+#include <algorithm>
+
+#include "base/strings/sys_string_conversions.h"
+#include "grit/ui_resources.h"
+#include "skia/ext/skia_utils_mac.h"
+#include "ui/base/resource/resource_bundle.h"
+#import "ui/message_center/cocoa/settings_controller.h"
+#include "ui/message_center/message_center_style.h"
+
+using message_center::settings::kEntryIconSize;
+using message_center::settings::kInternalHorizontalSpacing;
+
+// Size of the widget rendered for us by Cocoa.
+const int kCocoaCheckboxSize = 14;
+
+// Intrinsic padding pixels out of our control.
+// Cocoa gives the checkmark some blank space on either side.
+const int kIntrinsicCheckmarkLeftPadding = 2;
+const int kIntrinsicCheckmarkRightPadding = 4;
+// Labels have a bit of whitespace to the left, which can throw
+// off measurements.
+const int kIntrinsicTextLeftPadding = 1;
+
+// The learn more image is bigger than the actual size of the learn more
+// pixels, this represents the difference.
+const int kIntrinsicLearnMorePadding = 2;
+
+// Corrected padding values used in layout.
+// This computes the amout of padding based on the area reserved for the
+// checkbox and the actual checkbox size in pixels.
+const int kCheckmarkPaddingNecessary =
+ (message_center::settings::kCheckboxSizeWithPadding - kCocoaCheckboxSize) /
+ 2;
+
+// These represent the additional padding that we must give the checkmark
+// control based on the required padding and the intrinsic padding.
+const int kCorrectedCheckmarkLeftPadding =
+ kCheckmarkPaddingNecessary - kIntrinsicCheckmarkLeftPadding;
+const int kCorrectedCheckmarkRightPadding =
+ kCheckmarkPaddingNecessary + kInternalHorizontalSpacing -
+ kIntrinsicCheckmarkRightPadding;
+
+// The amount of space we want, based on the spec and the intrinsic text space
+// included by Cocoa.
+const int kCorrectedIconTextPadding =
+ kInternalHorizontalSpacing - kIntrinsicTextLeftPadding;
+
+// We want a certain amount of space to the right of the learn more button,
+// this metric incorporates the intrinsic learn more blank space to compute it.
+const int kCorrectedEntryRightPadding =
+ kInternalHorizontalSpacing - kIntrinsicLearnMorePadding;
+
+@interface MCSettingsButtonCell : NSButtonCell {
+ // A checkbox's regular image is the checkmark image. This additional image
+ // is used for the favicon or app icon shown next to the checkmark.
+ base::scoped_nsobject<NSImage> extraImage_;
+}
+- (void)setExtraImage:(NSImage*)extraImage;
+@end
+
+@implementation MCSettingsButtonCell
+- (void)setExtraImage:(NSImage*)extraImage {
+ extraImage_.reset([extraImage retain]);
+}
+
+- (NSRect)drawTitle:(NSAttributedString*)title
+ withFrame:(NSRect)frame
+ inView:(NSView*)controlView {
+ CGFloat inset = kCorrectedCheckmarkRightPadding;
+ // drawTitle:withFrame:inView: draws the checkmark image. Draw the extra
+ // image as part of the checkbox's text.
+ if (extraImage_) {
+ NSRect imageRect = frame;
+ imageRect.origin.x += inset;
+ // Center the image vertically.
+ if (NSHeight(frame) > kEntryIconSize)
+ imageRect.origin.y += (NSHeight(frame) - kEntryIconSize) / 2;
+ imageRect.size = NSMakeSize(kEntryIconSize, kEntryIconSize);
+ [extraImage_ drawInRect:imageRect
+ fromRect:NSZeroRect
+ operation:NSCompositeSourceOver
+ fraction:1.0
+ respectFlipped:YES
+ hints:nil];
+
+ inset += kEntryIconSize + kCorrectedIconTextPadding;
+ }
+ frame.origin.x += inset;
+ frame.size.width -= inset;
+ return [super drawTitle:title withFrame:frame inView:controlView];
+}
+
+- (NSSize)cellSizeForBounds:(NSRect)aRect {
+ NSSize size = [super cellSizeForBounds:aRect];
+ size.width += kCorrectedCheckmarkRightPadding;
+ if (extraImage_) {
+ size.width += kEntryIconSize + kCorrectedIconTextPadding;
+ size.height = std::max(static_cast<CGFloat>(kEntryIconSize), size.height);
+ }
+ return size;
+}
+
+- (NSUInteger)hitTestForEvent:(NSEvent*)event
+ inRect:(NSRect)cellFrame
+ ofView:(NSView*)controlView {
+ NSUInteger result =
+ [super hitTestForEvent:event inRect:cellFrame ofView:controlView];
+ if (result == NSCellHitNone) {
+ // The default button cell does hit testing on the attributed string. Since
+ // this cell draws additional spacing and an icon in front of the string,
+ // tweak the hit testing result.
+ NSPoint point =
+ [controlView convertPoint:[event locationInWindow] fromView:nil];
+
+ NSRect rect = [self titleRectForBounds:[controlView bounds]];
+ rect.size = [[self attributedTitle] size];
+ rect.size.width += kCorrectedCheckmarkRightPadding;
+
+ if (extraImage_)
+ rect.size.width += kEntryIconSize + kCorrectedIconTextPadding;
+
+ if (NSPointInRect(point, rect))
+ result = NSCellHitContentArea | NSCellHitTrackableArea;
+ }
+ return result;
+}
+@end
+
+@implementation MCSettingsEntryView
+
+- (id)initWithController:(MCSettingsController*)controller
+ notifier:(message_center::Notifier*)notifier
+ frame:(NSRect)frame
+ hasSeparator:(BOOL)hasSeparator {
+ if ((self = [super initWithFrame:frame])) {
+ [self setBoxType:NSBoxCustom];
+ [self setBorderType:NSNoBorder];
+ [self setTitlePosition:NSNoTitle];
+ [self setContentViewMargins:NSZeroSize];
+
+ hasSeparator_ = hasSeparator;
+ controller_ = controller;
+ notifier_ = notifier;
+ if (!notifier->icon.IsEmpty())
+ notifierIcon_.reset(notifier->icon.CopyNSImage());
+ [self layout];
+ }
+ return self;
+}
+
+- (void)setNotifierIcon:(NSImage*)notifierIcon {
+ notifierIcon_.reset([notifierIcon retain]);
+ [self layout];
+}
+
+- (NSButton*)checkbox {
+ return checkbox_;
+}
+
+- (void)layout {
+ BOOL hasLearnMore =
+ [controller_ notifierHasAdvancedSettings:notifier_->notifier_id];
+
+ // Now calculate the space available for the checkbox button.
+ NSRect checkboxFrame = [self bounds];
+ checkboxFrame.origin.x += kCorrectedCheckmarkLeftPadding;
+ checkboxFrame.size.width -=
+ kCorrectedCheckmarkLeftPadding + kCorrectedEntryRightPadding;
+
+ NSRect learnMoreFrame =
+ NSMakeRect(checkboxFrame.origin.x + checkboxFrame.size.width,
+ checkboxFrame.origin.y,
+ 0,
+ checkboxFrame.size.height);
+
+ // Initially place the learn more button right-aligned.
+ if (hasLearnMore) {
+ ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
+ NSImage* defaultImage =
+ rb.GetNativeImageNamed(IDR_NOTIFICATION_ADVANCED_SETTINGS).ToNSImage();
+ NSSize defaultImageSize = [defaultImage size];
+ learnMoreFrame.size.width = defaultImageSize.width;
+ learnMoreFrame.origin.x -= defaultImageSize.width;
+
+ // May need to center the image if it's shorter than the entry.
+ if (defaultImageSize.height < learnMoreFrame.size.height) {
+ learnMoreFrame.origin.y +=
+ (learnMoreFrame.size.height - defaultImageSize.height) / 2;
+ learnMoreFrame.size.height = defaultImageSize.height;
+ }
+
+ // Since we have an image then we need to ensure that the text from the
+ // checkbox doesn't overlap with the learn more image.
+ checkboxFrame.size.width -=
+ kCorrectedIconTextPadding + learnMoreFrame.size.width;
+
+ if (!learnMoreButton_.get()) {
+ learnMoreButton_.reset(
+ [[HoverImageButton alloc] initWithFrame:learnMoreFrame]);
+ [self addSubview:learnMoreButton_];
+ } else {
+ [learnMoreButton_ setFrame:learnMoreFrame];
+ }
+ [learnMoreButton_ setDefaultImage:defaultImage];
+ [learnMoreButton_ setHoverImage:rb.GetNativeImageNamed(
+ IDR_NOTIFICATION_ADVANCED_SETTINGS_HOVER).ToNSImage()];
+ [learnMoreButton_ setPressedImage:rb.GetNativeImageNamed(
+ IDR_NOTIFICATION_ADVANCED_SETTINGS_PRESSED).ToNSImage()];
+ [learnMoreButton_ setBordered:NO];
+ [learnMoreButton_ setTarget:self];
+ [learnMoreButton_ setAction:@selector(learnMoreClicked:)];
+ }
+
+ if (!checkbox_.get()) {
+ checkbox_.reset([[NSButton alloc] initWithFrame:checkboxFrame]);
+ [self addSubview:checkbox_];
+ } else {
+ [checkbox_ setFrame:checkboxFrame];
+ }
+
+ base::scoped_nsobject<MCSettingsButtonCell> cell([[MCSettingsButtonCell alloc]
+ initTextCell:base::SysUTF16ToNSString(notifier_->name)]);
+ if ([notifierIcon_ isValid])
+ [cell setExtraImage:notifierIcon_];
+
+ [checkbox_ setCell:cell];
+ [checkbox_ setButtonType:NSSwitchButton];
+ [checkbox_ setState:notifier_->enabled ? NSOnState : NSOffState];
+ [checkbox_ setTarget:self];
+ [checkbox_ setAction:@selector(checkboxClicked:)];
+
+ if (hasSeparator_) {
+ NSRect separatorRect = [self bounds];
+ separatorRect.size.height = 1;
+ if (!separator_.get()) {
+ separator_.reset([[NSBox alloc] initWithFrame:separatorRect]);
+ [separator_ setBoxType:NSBoxCustom];
+ [separator_ setBorderType:NSLineBorder];
+ [separator_ setBorderColor:gfx::SkColorToCalibratedNSColor(
+ message_center::settings::kEntrySeparatorColor)];
+ [separator_ setTitlePosition:NSNoTitle];
+ [separator_ setContentViewMargins:NSZeroSize];
+ [self addSubview:separator_];
+ } else {
+ [separator_ setFrame:separatorRect];
+ }
+ }
+}
+
+- (void)checkboxClicked:(id)sender {
+ BOOL enabled = [sender state] == NSOnState;
+ [controller_ setSettingsNotifier:notifier_ enabled:enabled];
+}
+
+- (void)learnMoreClicked:(id)sender {
+ [controller_ learnMoreClicked:notifier_];
+}
+
+// Testing API /////////////////////////////////////////////////////////////////
+
+- (void)clickLearnMore {
+ [learnMoreButton_ performClick:nil];
+}
+
+@end
+
+///////////////////////////////////////////////////////////////////////////////
diff --git a/ui/message_center/fake_notifier_settings_provider.cc b/ui/message_center/fake_notifier_settings_provider.cc
index f593ed9..c25f7a2 100644
--- a/ui/message_center/fake_notifier_settings_provider.cc
+++ b/ui/message_center/fake_notifier_settings_provider.cc
@@ -17,13 +17,14 @@ FakeNotifierSettingsProvider::NotifierGroupItem::~NotifierGroupItem() {
FakeNotifierSettingsProvider::FakeNotifierSettingsProvider()
: closed_called_count_(0),
- active_item_index_(0) {
-}
+ active_item_index_(0),
+ notifier_settings_requested_count_(0u) { }
FakeNotifierSettingsProvider::FakeNotifierSettingsProvider(
const std::vector<Notifier*>& notifiers)
: closed_called_count_(0),
- active_item_index_(0) {
+ active_item_index_(0),
+ notifier_settings_requested_count_(0u) {
NotifierGroupItem item;
item.group = new NotifierGroup(gfx::Image(),
UTF8ToUTF16("Fake name"),
@@ -78,6 +79,19 @@ void FakeNotifierSettingsProvider::OnNotifierSettingsClosing() {
closed_called_count_++;
}
+bool FakeNotifierSettingsProvider::NotifierHasAdvancedSettings(
+ const message_center::NotifierId& notifier_id) const {
+ if (!notifier_id_with_settings_handler_)
+ return false;
+ return *notifier_id_with_settings_handler_ == notifier_id;
+}
+
+void FakeNotifierSettingsProvider::OnNotifierAdvancedSettingsRequested(
+ const NotifierId& notifier_id,
+ const std::string* notification_id) {
+ notifier_settings_requested_count_++;
+}
+
void FakeNotifierSettingsProvider::AddObserver(
NotifierSettingsObserver* observer) {
}
@@ -98,8 +112,17 @@ void FakeNotifierSettingsProvider::AddGroup(
items_.push_back(item);
}
+void FakeNotifierSettingsProvider::SetNotifierHasAdvancedSettings(
+ const NotifierId& notifier_id) {
+ notifier_id_with_settings_handler_.reset(new NotifierId(notifier_id));
+}
+
int FakeNotifierSettingsProvider::closed_called_count() {
return closed_called_count_;
}
+size_t FakeNotifierSettingsProvider::settings_requested_count() const {
+ return notifier_settings_requested_count_;
+}
+
} // namespace message_center
diff --git a/ui/message_center/fake_notifier_settings_provider.h b/ui/message_center/fake_notifier_settings_provider.h
index b71a7e9..08f4d34 100644
--- a/ui/message_center/fake_notifier_settings_provider.h
+++ b/ui/message_center/fake_notifier_settings_provider.h
@@ -5,6 +5,7 @@
#ifndef UI_MESSAGE_CENTER_FAKE_NOTIFIER_SETTINGS_PROVIDER_H_
#define UI_MESSAGE_CENTER_FAKE_NOTIFIER_SETTINGS_PROVIDER_H_
+#include "base/memory/scoped_ptr.h"
#include "ui/message_center/notifier_settings.h"
namespace message_center {
@@ -19,12 +20,10 @@ class FakeNotifierSettingsProvider : public NotifierSettingsProvider {
virtual ~FakeNotifierSettingsProvider();
virtual size_t GetNotifierGroupCount() const OVERRIDE;
- virtual const message_center::NotifierGroup& GetNotifierGroupAt(
- size_t index) const OVERRIDE;
+ virtual const NotifierGroup& GetNotifierGroupAt(size_t index) const OVERRIDE;
virtual bool IsNotifierGroupActiveAt(size_t index) const OVERRIDE;
virtual void SwitchToNotifierGroup(size_t index) OVERRIDE;
- virtual const message_center::NotifierGroup& GetActiveNotifierGroup() const
- OVERRIDE;
+ virtual const NotifierGroup& GetActiveNotifierGroup() const OVERRIDE;
virtual void GetNotifierList(std::vector<Notifier*>* notifiers) OVERRIDE;
@@ -32,6 +31,11 @@ class FakeNotifierSettingsProvider : public NotifierSettingsProvider {
bool enabled) OVERRIDE;
virtual void OnNotifierSettingsClosing() OVERRIDE;
+ virtual bool NotifierHasAdvancedSettings(const NotifierId& notifier_id) const
+ OVERRIDE;
+ virtual void OnNotifierAdvancedSettingsRequested(
+ const NotifierId& notifier_id,
+ const std::string* notification_id) OVERRIDE;
virtual void AddObserver(NotifierSettingsObserver* observer) OVERRIDE;
virtual void RemoveObserver(NotifierSettingsObserver* observer) OVERRIDE;
@@ -40,6 +44,11 @@ class FakeNotifierSettingsProvider : public NotifierSettingsProvider {
void AddGroup(NotifierGroup* group, const std::vector<Notifier*>& notifiers);
+ // For testing, this method can be used to specify a notifier that has a learn
+ // more button. This class only saves the last one that was set.
+ void SetNotifierHasAdvancedSettings(const NotifierId& notifier_id);
+ size_t settings_requested_count() const;
+
private:
struct NotifierGroupItem {
NotifierGroup* group;
@@ -53,6 +62,8 @@ class FakeNotifierSettingsProvider : public NotifierSettingsProvider {
std::vector<NotifierGroupItem> items_;
int closed_called_count_;
size_t active_item_index_;
+ scoped_ptr<NotifierId> notifier_id_with_settings_handler_;
+ size_t notifier_settings_requested_count_;
};
} // namespace message_center
diff --git a/ui/message_center/message_center.gyp b/ui/message_center/message_center.gyp
index e8ceb78..39e9813 100644
--- a/ui/message_center/message_center.gyp
+++ b/ui/message_center/message_center.gyp
@@ -34,6 +34,8 @@
'cocoa/popup_controller.mm',
'cocoa/settings_controller.h',
'cocoa/settings_controller.mm',
+ 'cocoa/settings_entry_view.h',
+ 'cocoa/settings_entry_view.mm',
'cocoa/status_item_view.h',
'cocoa/status_item_view.mm',
'cocoa/tray_controller.h',
@@ -77,6 +79,8 @@
'views/message_center_bubble.h',
'views/message_center_button_bar.cc',
'views/message_center_button_bar.h',
+ 'views/message_center_focus_border.h',
+ 'views/message_center_focus_border.cc',
'views/message_center_view.cc',
'views/message_center_view.h',
'views/message_popup_collection.cc',
@@ -162,6 +166,7 @@
'dependencies': [
'../../base/base.gyp:base',
'../../base/base.gyp:test_support_base',
+ '../../chrome/chrome_resources.gyp:packed_resources',
'../../skia/skia.gyp:skia',
'../../testing/gtest.gyp:gtest',
'../../url/url.gyp:url_lib',
@@ -169,6 +174,8 @@
'../gfx/gfx.gyp:gfx',
'../ui.gyp:ui',
'../ui_unittests.gyp:run_ui_unittests',
+ '../ui.gyp:ui_resources',
+ '../../url/url.gyp:url_lib',
'message_center',
'message_center_test_support',
],
@@ -186,6 +193,11 @@
'test/run_all_unittests.cc',
],
'conditions': [
+ ['use_glib == 1 or OS == "ios"', {
+ 'dependencies': [
+ '../base/strings/ui_strings.gyp:ui_unittest_strings',
+ ],
+ }],
['OS=="mac"', {
'dependencies': [
'../ui_unittests.gyp:ui_test_support',
@@ -203,6 +215,7 @@
'views/bounded_label_unittest.cc',
'views/message_center_view_unittest.cc',
'views/message_popup_collection_unittest.cc',
+ 'views/notifier_settings_view_unittest.cc',
],
}],
['notifications==0', { # Android and iOS.
diff --git a/ui/message_center/message_center_style.h b/ui/message_center/message_center_style.h
index 18dd340..e3dfdc9 100644
--- a/ui/message_center/message_center_style.h
+++ b/ui/message_center/message_center_style.h
@@ -33,6 +33,22 @@ MESSAGE_CENTER_EXPORT extern const int kNotificationWidth; // H size of the
MESSAGE_CENTER_EXPORT extern const SkColor kMessageCenterBorderColor;
MESSAGE_CENTER_EXPORT extern const SkColor kMessageCenterShadowColor;
+// Settings dialog constants.
+namespace settings {
+
+const SkColor kEntrySeparatorColor = SkColorSetARGB(0.1 * 255, 0, 0, 0);
+const int kEntryHeight = 45;
+const int kEntrySeparatorHeight = 1;
+const int kHorizontalMargin = 10;
+const int kTopMargin = 20;
+const int kTitleToDescriptionSpace = 20;
+const int kEntryIconSize = 16;
+const int kDescriptionToSwitcherSpace = 15;
+const int kInternalHorizontalSpacing = 10;
+const int kCheckboxSizeWithPadding = 24;
+
+} // namespace settings
+
// Within a notification ///////////////////////////////////////////////////////
// DIP dimensions (H = horizontal, V = vertical).
diff --git a/ui/message_center/notifier_settings.h b/ui/message_center/notifier_settings.h
index 6188599..9111427 100644
--- a/ui/message_center/notifier_settings.h
+++ b/ui/message_center/notifier_settings.h
@@ -157,6 +157,16 @@ class MESSAGE_CENTER_EXPORT NotifierSettingsProvider {
// Called when the settings window is closed.
virtual void OnNotifierSettingsClosing() = 0;
+
+ // Called to determine if a particular notifier can respond to a request for
+ // more information.
+ virtual bool NotifierHasAdvancedSettings(const NotifierId& notifier_id)
+ const = 0;
+
+ // Called upon request for more information about a particular notifier.
+ virtual void OnNotifierAdvancedSettingsRequested(
+ const NotifierId& notifier_id,
+ const std::string* notification_id) = 0;
};
} // namespace message_center
diff --git a/ui/message_center/views/message_center_button_bar.cc b/ui/message_center/views/message_center_button_bar.cc
index 149c3ed..dc440d5 100644
--- a/ui/message_center/views/message_center_button_bar.cc
+++ b/ui/message_center/views/message_center_button_bar.cc
@@ -30,6 +30,8 @@ namespace message_center {
namespace {
const int kButtonSize = 40;
const int kChevronWidth = 8;
+const int kFooterTopMargin = 6;
+const int kFooterBottomMargin = 3;
const int kFooterLeftMargin = 20;
const int kFooterRightMargin = 14;
} // namespace
@@ -183,8 +185,10 @@ void MessageCenterButtonBar::ViewVisibilityChanged() {
int image_margin = std::max(0, (kButtonSize - settings_image->width()) / 2);
views::GridLayout* layout = new views::GridLayout(this);
SetLayoutManager(layout);
- layout->SetInsets(
- 0, kFooterLeftMargin, 0, std::max(0, kFooterRightMargin - image_margin));
+ layout->SetInsets(kFooterTopMargin,
+ kFooterLeftMargin,
+ kFooterBottomMargin,
+ std::max(0, kFooterRightMargin - image_margin));
views::ColumnSet* column = layout->AddColumnSet(0);
if (title_arrow_->visible()) {
// Column for the left-arrow used to back out of settings.
diff --git a/ui/message_center/views/message_center_focus_border.cc b/ui/message_center/views/message_center_focus_border.cc
new file mode 100644
index 0000000..90a4630
--- /dev/null
+++ b/ui/message_center/views/message_center_focus_border.cc
@@ -0,0 +1,25 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/message_center/views/message_center_focus_border.h"
+
+#include "ui/gfx/canvas.h"
+#include "ui/gfx/rect.h"
+#include "ui/message_center/message_center_style.h"
+#include "ui/views/view.h"
+
+namespace message_center {
+
+MessageCenterFocusBorder::MessageCenterFocusBorder() {}
+
+MessageCenterFocusBorder::~MessageCenterFocusBorder() {}
+
+void MessageCenterFocusBorder::Paint(const views::View& view,
+ gfx::Canvas* canvas) const {
+ gfx::Rect rect(view.GetLocalBounds());
+ rect.Inset(2, 1, 2, 3);
+ canvas->DrawRect(rect, kFocusBorderColor);
+}
+
+} // namespace message_center
diff --git a/ui/message_center/views/message_center_focus_border.h b/ui/message_center/views/message_center_focus_border.h
new file mode 100644
index 0000000..de49bae
--- /dev/null
+++ b/ui/message_center/views/message_center_focus_border.h
@@ -0,0 +1,30 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_MESSAGE_CENTER_VIEWS_MESSAGE_CENTER_FOCUS_BORDER_H_
+#define UI_MESSAGE_CENTER_VIEWS_MESSAGE_CENTER_FOCUS_BORDER_H_
+
+#include "ui/views/focus_border.h"
+#include "ui/views/view.h"
+
+namespace message_center {
+
+// Focus border class that draws a 1px blue border inset more on the bottom than
+// on the sides.
+class MessageCenterFocusBorder : public views::FocusBorder {
+ public:
+ MessageCenterFocusBorder();
+ virtual ~MessageCenterFocusBorder();
+
+ private:
+ // views::FocusBorder overrides:
+ virtual void Paint(const views::View& view, gfx::Canvas* canvas) const
+ OVERRIDE;
+
+ DISALLOW_COPY_AND_ASSIGN(MessageCenterFocusBorder);
+};
+
+} // namespace message_center
+
+#endif // UI_MESSAGE_CENTER_VIEWS_MESSAGE_CENTER_FOCUS_BORDER_H_
diff --git a/ui/message_center/views/message_center_view.cc b/ui/message_center/views/message_center_view.cc
index 4be6574..7225ea4 100644
--- a/ui/message_center/views/message_center_view.cc
+++ b/ui/message_center/views/message_center_view.cc
@@ -253,10 +253,10 @@ MessageListView::MessageListView(MessageCenterView* message_center_view,
set_background(views::Background::CreateSolidBackground(
kMessageCenterBackgroundColor));
set_border(views::Border::CreateEmptyBorder(
- kMarginBetweenItems - shadow_insets.top(), /* top */
- kMarginBetweenItems - shadow_insets.left(), /* left */
- kMarginBetweenItems - shadow_insets.bottom(), /* bottom */
- kMarginBetweenItems - shadow_insets.right() /* right */ ));
+ top_down ? 0 : kMarginBetweenItems - shadow_insets.top(), /* top */
+ kMarginBetweenItems - shadow_insets.left(), /* left */
+ top_down ? kMarginBetweenItems - shadow_insets.bottom() : 0, /* bottom */
+ kMarginBetweenItems - shadow_insets.right() /* right */));
}
MessageListView::~MessageListView() {
@@ -753,12 +753,13 @@ void MessageCenterView::Layout() {
int button_height = button_bar_->GetHeightForWidth(width()) +
button_bar_->GetInsets().height();
// Skip unnecessary re-layout of contents during the resize animation.
- if (settings_transition_animation_ &&
- settings_transition_animation_->is_animating() &&
- settings_transition_animation_->current_part_index() == 0) {
- if (!top_down_)
+ bool animating = settings_transition_animation_ &&
+ settings_transition_animation_->is_animating();
+ if (animating && settings_transition_animation_->current_part_index() == 0) {
+ if (!top_down_) {
button_bar_->SetBounds(
0, height() - button_height, width(), button_height);
+ }
return;
}
@@ -777,18 +778,16 @@ void MessageCenterView::Layout() {
else
is_scrollable = settings_view_->IsScrollable();
- if (is_scrollable && !button_bar_->border()) {
- // Draw separator line on the top of the button bar if it is on the bottom
- // or draw it at the bottom if the bar is on the top.
- button_bar_->set_border(views::Border::CreateSolidSidedBorder(
- top_down_ ? 0 : 1,
- 0,
- top_down_ ? 1 : 0,
- 0,
- kFooterDelimiterColor));
- button_bar_->SchedulePaint();
- } else if (!is_scrollable && button_bar_->border()) {
- button_bar_->set_border(NULL);
+ if (!animating) {
+ if (is_scrollable) {
+ // Draw separator line on the top of the button bar if it is on the bottom
+ // or draw it at the bottom if the bar is on the top.
+ button_bar_->set_border(views::Border::CreateSolidSidedBorder(
+ top_down_ ? 0 : 1, 0, top_down_ ? 1 : 0, 0, kFooterDelimiterColor));
+ } else {
+ button_bar_->set_border(views::Border::CreateEmptyBorder(
+ top_down_ ? 0 : 1, 0, top_down_ ? 1 : 0, 0));
+ }
button_bar_->SchedulePaint();
}
button_bar_->SetBounds(0,
diff --git a/ui/message_center/views/notifier_settings_view.cc b/ui/message_center/views/notifier_settings_view.cc
index 63e72a4..b43552a 100644
--- a/ui/message_center/views/notifier_settings_view.cc
+++ b/ui/message_center/views/notifier_settings_view.cc
@@ -21,6 +21,7 @@
#include "ui/gfx/image/image.h"
#include "ui/gfx/size.h"
#include "ui/message_center/message_center_style.h"
+#include "ui/message_center/views/message_center_focus_border.h"
#include "ui/message_center/views/message_center_view.h"
#include "ui/views/background.h"
#include "ui/views/border.h"
@@ -30,6 +31,8 @@
#include "ui/views/controls/button/menu_button.h"
#include "ui/views/controls/image_view.h"
#include "ui/views/controls/label.h"
+#include "ui/views/controls/link.h"
+#include "ui/views/controls/link_listener.h"
#include "ui/views/controls/menu/menu_runner.h"
#include "ui/views/controls/scroll_view.h"
#include "ui/views/controls/scrollbar/overlay_scroll_bar.h"
@@ -43,44 +46,94 @@
#endif
namespace message_center {
+namespace settings {
+
+// Additional views-specific parameters.
+
+// The width of the settings pane in pixels.
+const int kWidth = 360;
+
+// The width of the learn more icon in pixels.
+const int kLearnMoreSize = 12;
+
+// The width of the click target that contains the learn more button in pixels.
+const int kLearnMoreTargetWidth = 28;
+
+// The height of the click target that contains the learn more button in pixels.
+const int kLearnMoreTargetHeight = 40;
+
+// The minimum height of the settings pane in pixels.
+const int kMinimumHeight = 480;
+
+// The horizontal margin of the title area of the settings pane in addition to
+// the standard margin from settings::kHorizontalMargin.
+const int kTitleMargin = 10;
+
+} // namespace settings
+
namespace {
+
+// The amount of built-in padding for the notifier group switcher.
const int kButtonPainterInsets = 5;
-// We really want the margin to be 20px, but various views are padded by
-// whitespace.
-const int kDesiredMargin = 20;
-// The MenuButton has 2px whitespace built-in.
+
+// Menu button metrics to make the text line up.
const int kMenuButtonInnateMargin = 2;
-const int kMinimumHorizontalMargin = kDesiredMargin - kMenuButtonInnateMargin;
-// The EntryViews' leftmost view is a checkbox with 1px whitespace built in, so
-// the margin for entry views should be one less than the target margin.
-const int kCheckboxInnateMargin = 1;
-const int kEntryMargin = kDesiredMargin - kCheckboxInnateMargin;
const int kMenuButtonLeftPadding = 12;
const int kMenuButtonRightPadding = 13;
const int kMenuButtonVerticalPadding = 9;
+
+// Used to place the context menu correctly.
const int kMenuWhitespaceOffset = 2;
-const int kMinimumWindowHeight = 480;
-const int kMinimumWindowWidth = 320;
-const int kSettingsTitleBottomMargin = 12;
-const int kSettingsTitleTopMargin = 15;
-const int kSpaceInButtonComponents = 16;
-const int kTitleVerticalMargin = 1;
-const int kTitleElementSpacing = 10;
-const int kEntryHeight = kMinimumWindowHeight / 10;
+
+// The innate vertical blank space in the label for the title of the settings
+// pane.
+const int kInnateTitleBottomMargin = 1;
+const int kInnateTitleTopMargin = 7;
+
+// The innate top blank space in the label for the description of the settings
+// pane.
+const int kInnateDescriptionTopMargin = 2;
+
+// Checkboxes have some built-in right padding blank space.
+const int kInnateCheckboxRightPadding = 2;
+
+// Spec defines the checkbox size; the innate padding throws this measurement
+// off so we need to compute a slightly different area for the checkbox to
+// inhabit.
+const int kComputedCheckboxSize =
+ settings::kCheckboxSizeWithPadding - kInnateCheckboxRightPadding;
+
+// The menubutton has innate margin, so we need to compensate for that when
+// figuring the margin of the title area.
+const int kComputedContentsTitleMargin = 0 - kMenuButtonInnateMargin;
+
+// The spec doesn't include the bottom blank area of the title bar or the innate
+// blank area in the description label, so we'll use this as the space between
+// the title and description.
+const int kComputedTitleBottomMargin = settings::kDescriptionToSwitcherSpace -
+ kInnateTitleBottomMargin -
+ kInnateDescriptionTopMargin;
+
+// The blank space above the title needs to be adjusted by the amount of blank
+// space included in the title label.
+const int kComputedTitleTopMargin =
+ settings::kTopMargin - kInnateTitleTopMargin;
+
+// The switcher has a lot of blank space built in so we should include that when
+// spacing the title area vertically.
+const int kComputedTitleElementSpacing =
+ settings::kDescriptionToSwitcherSpace - kButtonPainterInsets - 1;
// The view to guarantee the 48px height and place the contents at the
// middle. It also guarantee the left margin.
class EntryView : public views::View {
public:
EntryView(views::View* contents);
- virtual ~EntryView();
-
- // Overridden from views::View:
+ virtual ~EntryView(); // Overridden from views::View:
virtual void Layout() OVERRIDE;
virtual gfx::Size GetPreferredSize() OVERRIDE;
virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE;
virtual void OnFocus() OVERRIDE;
- virtual void OnPaintFocusBorder(gfx::Canvas* canvas) OVERRIDE;
virtual bool OnKeyPressed(const ui::KeyEvent& event) OVERRIDE;
virtual bool OnKeyReleased(const ui::KeyEvent& event) OVERRIDE;
@@ -89,25 +142,25 @@ class EntryView : public views::View {
};
EntryView::EntryView(views::View* contents) {
+ set_focus_border(new MessageCenterFocusBorder());
AddChildView(contents);
}
-EntryView::~EntryView() {
-}
+EntryView::~EntryView() {}
void EntryView::Layout() {
DCHECK_EQ(1, child_count());
views::View* content = child_at(0);
- int content_width = width() - kEntryMargin * 2;
+ int content_width = width();
int content_height = content->GetHeightForWidth(content_width);
int y = std::max((height() - content_height) / 2, 0);
- content->SetBounds(kEntryMargin, y, content_width, content_height);
+ content->SetBounds(0, y, content_width, content_height);
}
gfx::Size EntryView::GetPreferredSize() {
DCHECK_EQ(1, child_count());
gfx::Size size = child_at(0)->GetPreferredSize();
- size.SetToMax(gfx::Size(kMinimumWindowWidth, kEntryHeight));
+ size.SetToMax(gfx::Size(settings::kWidth, settings::kEntryHeight));
return size;
}
@@ -121,13 +174,6 @@ void EntryView::OnFocus() {
ScrollRectToVisible(GetLocalBounds());
}
-void EntryView::OnPaintFocusBorder(gfx::Canvas* canvas) {
- if (HasFocus() && (focusable() || IsAccessibilityFocusable())) {
- canvas->DrawRect(gfx::Rect(2, 1, width() - 4, height() - 3),
- kFocusBorderColor);
- }
-}
-
bool EntryView::OnKeyPressed(const ui::KeyEvent& event) {
return child_at(0)->OnKeyPressed(event);
}
@@ -183,9 +229,9 @@ class NotifierGroupMenuModel : public ui::SimpleMenuModel,
// Overridden from ui::SimpleMenuModel::Delegate:
virtual bool IsCommandIdChecked(int command_id) const OVERRIDE;
virtual bool IsCommandIdEnabled(int command_id) const OVERRIDE;
- virtual bool GetAcceleratorForCommandId(
- int command_id,
- ui::Accelerator* accelerator) OVERRIDE;
+ virtual bool GetAcceleratorForCommandId(int command_id,
+ ui::Accelerator* accelerator)
+ OVERRIDE;
virtual void ExecuteCommand(int command_id, int event_flags) OVERRIDE;
private:
@@ -243,78 +289,182 @@ void NotifierGroupMenuModel::ExecuteCommand(int command_id, int event_flags) {
// We do not use views::Checkbox class directly because it doesn't support
// showing 'icon'.
-class NotifierSettingsView::NotifierButton : public views::CustomButton,
- public views::ButtonListener {
- public:
- NotifierButton(Notifier* notifier, views::ButtonListener* listener)
- : views::CustomButton(listener),
- notifier_(notifier),
- icon_view_(NULL),
- checkbox_(new views::Checkbox(string16())) {
- DCHECK(notifier);
- SetLayoutManager(new views::BoxLayout(
- views::BoxLayout::kHorizontal, 0, 0, kSpaceInButtonComponents));
- checkbox_->SetChecked(notifier_->enabled);
- checkbox_->set_listener(this);
- checkbox_->set_focusable(false);
- checkbox_->SetAccessibleName(notifier_->name);
- AddChildView(checkbox_);
- UpdateIconImage(notifier_->icon);
- AddChildView(new views::Label(notifier_->name));
+NotifierSettingsView::NotifierButton::NotifierButton(
+ NotifierSettingsProvider* provider,
+ Notifier* notifier,
+ views::ButtonListener* listener)
+ : views::CustomButton(listener),
+ provider_(provider),
+ notifier_(notifier),
+ icon_view_(new views::ImageView()),
+ name_view_(new views::Label(notifier_->name)),
+ checkbox_(new views::Checkbox(string16())),
+ learn_more_(NULL) {
+ DCHECK(provider);
+ DCHECK(notifier);
+
+ checkbox_->SetChecked(notifier_->enabled);
+ checkbox_->set_listener(this);
+ checkbox_->set_focusable(false);
+ checkbox_->SetAccessibleName(notifier_->name);
+
+ if (ShouldHaveLearnMoreButton()) {
+ // Create a more-info button that will be right-aligned.
+ learn_more_ = new views::ImageButton(this);
+ learn_more_->set_focus_border(new MessageCenterFocusBorder());
+ learn_more_->set_request_focus_on_press(false);
+ learn_more_->set_focusable(true);
+
+ ui::ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+ learn_more_->SetImage(
+ views::Button::STATE_NORMAL,
+ rb.GetImageSkiaNamed(IDR_NOTIFICATION_ADVANCED_SETTINGS));
+ learn_more_->SetImage(
+ views::Button::STATE_HOVERED,
+ rb.GetImageSkiaNamed(IDR_NOTIFICATION_ADVANCED_SETTINGS_HOVER));
+ learn_more_->SetImage(
+ views::Button::STATE_PRESSED,
+ rb.GetImageSkiaNamed(IDR_NOTIFICATION_ADVANCED_SETTINGS_PRESSED));
+ learn_more_->SetState(views::Button::STATE_NORMAL);
+ int learn_more_border_width =
+ (settings::kLearnMoreTargetWidth - settings::kLearnMoreSize) / 2;
+ int learn_more_border_height =
+ (settings::kLearnMoreTargetHeight - settings::kLearnMoreSize) / 2;
+ // The image itself is quite small, this large invisible border creates a
+ // much bigger click target.
+ learn_more_->set_border(
+ views::Border::CreateEmptyBorder(learn_more_border_height,
+ learn_more_border_width,
+ learn_more_border_height,
+ learn_more_border_width));
+ learn_more_->SetImageAlignment(views::ImageButton::ALIGN_CENTER,
+ views::ImageButton::ALIGN_MIDDLE);
}
- void UpdateIconImage(const gfx::Image& icon) {
- notifier_->icon = icon;
- if (icon.IsEmpty()) {
- delete icon_view_;
- icon_view_ = NULL;
- } else {
- if (!icon_view_) {
- icon_view_ = new views::ImageView();
- AddChildViewAt(icon_view_, 1);
- }
- icon_view_->SetImage(icon.ToImageSkia());
- icon_view_->SetImageSize(gfx::Size(kSettingsIconSize, kSettingsIconSize));
- }
- Layout();
- SchedulePaint();
- }
+ UpdateIconImage(notifier_->icon);
+}
- void SetChecked(bool checked) {
- checkbox_->SetChecked(checked);
- notifier_->enabled = checked;
- }
+NotifierSettingsView::NotifierButton::~NotifierButton() {}
- bool checked() const {
- return checkbox_->checked();
- }
+void NotifierSettingsView::NotifierButton::UpdateIconImage(
+ const gfx::Image& icon) {
+ bool has_icon_view = false;
- const Notifier& notifier() const {
- return *notifier_.get();
+ notifier_->icon = icon;
+ if (!icon.IsEmpty()) {
+ icon_view_->SetImage(icon.ToImageSkia());
+ icon_view_->SetImageSize(
+ gfx::Size(settings::kEntryIconSize, settings::kEntryIconSize));
+ has_icon_view = true;
}
+ GridChanged(ShouldHaveLearnMoreButton(), has_icon_view);
+}
- private:
- // Overridden from views::ButtonListener:
- virtual void ButtonPressed(views::Button* button,
- const ui::Event& event) OVERRIDE {
- DCHECK(button == checkbox_);
+void NotifierSettingsView::NotifierButton::SetChecked(bool checked) {
+ checkbox_->SetChecked(checked);
+ notifier_->enabled = checked;
+}
+
+bool NotifierSettingsView::NotifierButton::checked() const {
+ return checkbox_->checked();
+}
+
+bool NotifierSettingsView::NotifierButton::has_learn_more() const {
+ return learn_more_ != NULL;
+}
+
+const Notifier& NotifierSettingsView::NotifierButton::notifier() const {
+ return *notifier_.get();
+}
+
+void NotifierSettingsView::NotifierButton::SendLearnMorePressedForTest() {
+ if (learn_more_ == NULL)
+ return;
+ gfx::Point point(110, 120);
+ ui::MouseEvent pressed(
+ ui::ET_MOUSE_PRESSED, point, point, ui::EF_LEFT_MOUSE_BUTTON);
+ ButtonPressed(learn_more_, pressed);
+}
+
+void NotifierSettingsView::NotifierButton::ButtonPressed(
+ views::Button* button,
+ const ui::Event& event) {
+ if (button == checkbox_) {
// The checkbox state has already changed at this point, but we'll update
// the state on NotifierSettingsView::ButtonPressed() too, so here change
// back to the previous state.
checkbox_->SetChecked(!checkbox_->checked());
CustomButton::NotifyClick(event);
+ } else if (button == learn_more_) {
+ DCHECK(provider_);
+ provider_->OnNotifierAdvancedSettingsRequested(notifier_->notifier_id,
+ NULL);
}
+}
- virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE {
- static_cast<views::View*>(checkbox_)->GetAccessibleState(state);
+void NotifierSettingsView::NotifierButton::GetAccessibleState(
+ ui::AccessibleViewState* state) {
+ static_cast<views::View*>(checkbox_)->GetAccessibleState(state);
+}
+
+bool NotifierSettingsView::NotifierButton::ShouldHaveLearnMoreButton() const {
+ if (!provider_)
+ return false;
+
+ return provider_->NotifierHasAdvancedSettings(notifier_->notifier_id);
+}
+
+void NotifierSettingsView::NotifierButton::GridChanged(bool has_learn_more,
+ bool has_icon_view) {
+ using views::ColumnSet;
+ using views::GridLayout;
+
+ GridLayout* layout = new GridLayout(this);
+ SetLayoutManager(layout);
+ ColumnSet* cs = layout->AddColumnSet(0);
+ // Add a column for the checkbox.
+ cs->AddPaddingColumn(0, kInnateCheckboxRightPadding);
+ cs->AddColumn(GridLayout::CENTER,
+ GridLayout::CENTER,
+ 0,
+ GridLayout::FIXED,
+ kComputedCheckboxSize,
+ 0);
+ cs->AddPaddingColumn(0, settings::kInternalHorizontalSpacing);
+
+ if (has_icon_view) {
+ // Add a column for the icon.
+ cs->AddColumn(GridLayout::CENTER,
+ GridLayout::CENTER,
+ 0,
+ GridLayout::FIXED,
+ settings::kEntryIconSize,
+ 0);
+ cs->AddPaddingColumn(0, settings::kInternalHorizontalSpacing);
}
- scoped_ptr<Notifier> notifier_;
- views::ImageView* icon_view_;
- views::Checkbox* checkbox_;
+ // Add a column for the name.
+ cs->AddColumn(
+ GridLayout::LEADING, GridLayout::CENTER, 0, GridLayout::USE_PREF, 0, 0);
- DISALLOW_COPY_AND_ASSIGN(NotifierButton);
-};
+ // Add a padding column which contains expandable blank space.
+ cs->AddPaddingColumn(1, 0);
+
+ // Add a column for the learn more button if necessary.
+ if (has_learn_more) {
+ cs->AddPaddingColumn(0, settings::kInternalHorizontalSpacing);
+ cs->AddColumn(
+ GridLayout::CENTER, GridLayout::CENTER, 0, GridLayout::USE_PREF, 0, 0);
+ }
+
+ layout->StartRow(0, 0);
+ layout->AddView(checkbox_);
+ if (has_icon_view)
+ layout->AddView(icon_view_);
+ layout->AddView(name_view_);
+ if (has_learn_more)
+ layout->AddView(learn_more_);
+}
NotifierSettingsView::NotifierSettingsView(NotifierSettingsProvider* provider)
: title_arrow_(NULL),
@@ -328,8 +478,8 @@ NotifierSettingsView::NotifierSettingsView(NotifierSettingsProvider* provider)
set_focusable(true);
set_focus_border(NULL);
- set_background(views::Background::CreateSolidBackground(
- kMessageCenterBackgroundColor));
+ set_background(
+ views::Background::CreateSolidBackground(kMessageCenterBackgroundColor));
if (get_use_acceleration_when_possible())
SetPaintToLayer(true);
@@ -341,10 +491,10 @@ NotifierSettingsView::NotifierSettingsView(NotifierSettingsProvider* provider)
title_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
title_label_->SetMultiLine(true);
title_label_->set_border(
- views::Border::CreateEmptyBorder(kSettingsTitleTopMargin,
- kDesiredMargin,
- kSettingsTitleBottomMargin,
- kDesiredMargin));
+ views::Border::CreateEmptyBorder(kComputedTitleTopMargin,
+ settings::kTitleMargin,
+ kComputedTitleBottomMargin,
+ settings::kTitleMargin));
AddChildView(title_label_);
@@ -372,7 +522,8 @@ bool NotifierSettingsView::IsScrollable() {
void NotifierSettingsView::UpdateIconImage(const NotifierId& notifier_id,
const gfx::Image& icon) {
for (std::set<NotifierButton*>::iterator iter = buttons_.begin();
- iter != buttons_.end(); ++iter) {
+ iter != buttons_.end();
+ ++iter) {
if ((*iter)->notifier().notifier_id == notifier_id) {
(*iter)->UpdateIconImage(icon);
return;
@@ -393,15 +544,15 @@ void NotifierSettingsView::UpdateContentsView(
buttons_.clear();
views::View* contents_view = new views::View();
- contents_view->SetLayoutManager(
- new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0));
+ contents_view->SetLayoutManager(new views::BoxLayout(
+ views::BoxLayout::kVertical, settings::kHorizontalMargin, 0, 0));
views::View* contents_title_view = new views::View();
contents_title_view->SetLayoutManager(
new views::BoxLayout(views::BoxLayout::kVertical,
- kMinimumHorizontalMargin,
- kTitleVerticalMargin,
- kTitleElementSpacing));
+ kComputedContentsTitleMargin,
+ 0,
+ kComputedTitleElementSpacing));
bool need_account_switcher =
provider_ && provider_->GetNotifierGroupCount() > 1;
@@ -415,7 +566,10 @@ void NotifierSettingsView::UpdateContentsView(
top_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
top_label->SetMultiLine(true);
top_label->set_border(views::Border::CreateEmptyBorder(
- 0, kMenuButtonInnateMargin, 0, kMenuButtonInnateMargin));
+ 0,
+ settings::kTitleMargin + kMenuButtonInnateMargin,
+ 0,
+ settings::kTitleMargin + kMenuButtonInnateMargin));
contents_title_view->AddChildView(top_label);
if (need_account_switcher) {
@@ -433,9 +587,26 @@ void NotifierSettingsView::UpdateContentsView(
contents_view->AddChildView(contents_title_view);
- for (size_t i = 0; i < notifiers.size(); ++i) {
- NotifierButton* button = new NotifierButton(notifiers[i], this);
+ size_t notifier_count = notifiers.size();
+ for (size_t i = 0; i < notifier_count; ++i) {
+ NotifierButton* button = new NotifierButton(provider_, notifiers[i], this);
EntryView* entry = new EntryView(button);
+
+ // This code emulates separators using borders. We will create an invisible
+ // border on the last notifier, as the spec leaves a space for it.
+ scoped_ptr<views::Border> entry_border;
+ if (i == notifier_count - 1) {
+ entry_border.reset(views::Border::CreateEmptyBorder(
+ 0, 0, settings::kEntrySeparatorHeight, 0));
+ } else {
+ entry_border.reset(views::Border::CreateSolidSidedBorder(
+ 0,
+ 0,
+ settings::kEntrySeparatorHeight,
+ 0,
+ settings::kEntrySeparatorColor));
+ }
+ entry->set_border(entry_border.release());
entry->set_focusable(true);
contents_view->AddChildView(entry);
buttons_.insert(button);
@@ -449,7 +620,10 @@ void NotifierSettingsView::UpdateContentsView(
void NotifierSettingsView::Layout() {
int title_height = title_label_->GetHeightForWidth(width());
- title_label_->SetBounds(0, 0, width(), title_height);
+ title_label_->SetBounds(settings::kTitleMargin,
+ 0,
+ width() - settings::kTitleMargin * 2,
+ title_height);
views::View* contents_view = scroller_->contents();
int content_width = width();
@@ -463,10 +637,10 @@ void NotifierSettingsView::Layout() {
}
gfx::Size NotifierSettingsView::GetMinimumSize() {
- gfx::Size size(kMinimumWindowWidth, kMinimumWindowHeight);
+ gfx::Size size(settings::kWidth, settings::kMinimumHeight);
int total_height = title_label_->GetPreferredSize().height() +
scroller_->contents()->GetPreferredSize().height();
- if (total_height > kMinimumWindowHeight)
+ if (total_height > settings::kMinimumHeight)
size.Enlarge(scroller_->GetScrollBarWidth(), 0);
return size;
}
@@ -500,8 +674,8 @@ void NotifierSettingsView::ButtonPressed(views::Button* sender,
return;
}
- std::set<NotifierButton*>::iterator iter = buttons_.find(
- static_cast<NotifierButton*>(sender));
+ std::set<NotifierButton*>::iterator iter =
+ buttons_.find(static_cast<NotifierButton*>(sender));
if (iter == buttons_.end())
return;
diff --git a/ui/message_center/views/notifier_settings_view.h b/ui/message_center/views/notifier_settings_view.h
index 5e64ab9..1705807 100644
--- a/ui/message_center/views/notifier_settings_view.h
+++ b/ui/message_center/views/notifier_settings_view.h
@@ -11,8 +11,10 @@
#include "ui/message_center/message_center_export.h"
#include "ui/message_center/notifier_settings.h"
#include "ui/message_center/views/message_bubble_base.h"
+#include "ui/views/controls/button/checkbox.h"
#include "ui/views/controls/button/image_button.h"
#include "ui/views/controls/button/menu_button_listener.h"
+#include "ui/views/controls/image_view.h"
#include "ui/views/view.h"
namespace views {
@@ -47,7 +49,44 @@ class MESSAGE_CENTER_EXPORT NotifierSettingsView
}
private:
- class NotifierButton;
+ FRIEND_TEST_ALL_PREFIXES(NotifierSettingsViewTest, TestLearnMoreButton);
+
+ class MESSAGE_CENTER_EXPORT NotifierButton : public views::CustomButton,
+ public views::ButtonListener {
+ public:
+ NotifierButton(NotifierSettingsProvider* provider,
+ Notifier* notifier,
+ views::ButtonListener* listener);
+ virtual ~NotifierButton();
+
+ void UpdateIconImage(const gfx::Image& icon);
+ void SetChecked(bool checked);
+ bool checked() const;
+ bool has_learn_more() const;
+ const Notifier& notifier() const;
+
+ void SendLearnMorePressedForTest();
+
+ private:
+ // Overridden from views::ButtonListener:
+ virtual void ButtonPressed(views::Button* button,
+ const ui::Event& event) OVERRIDE;
+ virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE;
+
+ bool ShouldHaveLearnMoreButton() const;
+ // Helper function to reset the layout when the view has substantially
+ // changed.
+ void GridChanged(bool has_learn_more, bool has_icon_view);
+
+ NotifierSettingsProvider* provider_; // Weak.
+ const scoped_ptr<Notifier> notifier_;
+ views::ImageView* icon_view_;
+ views::Label* name_view_;
+ views::Checkbox* checkbox_;
+ views::ImageButton* learn_more_;
+
+ DISALLOW_COPY_AND_ASSIGN(NotifierButton);
+ };
// Given a new list of notifiers, updates the view to reflect it.
void UpdateContentsView(const std::vector<Notifier*>& notifiers);
@@ -62,6 +101,8 @@ class MESSAGE_CENTER_EXPORT NotifierSettingsView
// Overridden from views::ButtonListener:
virtual void ButtonPressed(views::Button* sender,
const ui::Event& event) OVERRIDE;
+
+ // Overridden from views::MenuButtonListener:
virtual void OnMenuButtonClicked(views::View* source,
const gfx::Point& point) OVERRIDE;
diff --git a/ui/message_center/views/notifier_settings_view_unittest.cc b/ui/message_center/views/notifier_settings_view_unittest.cc
new file mode 100644
index 0000000..331bd957
--- /dev/null
+++ b/ui/message_center/views/notifier_settings_view_unittest.cc
@@ -0,0 +1,124 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/strings/utf_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/message_center/fake_notifier_settings_provider.h"
+#include "ui/message_center/views/notifier_settings_view.h"
+
+namespace message_center {
+
+namespace {
+
+Notifier* NewNotifier(const std::string& id,
+ const std::string& title,
+ bool enabled) {
+ NotifierId notifier_id(NotifierId::APPLICATION, id);
+ return new Notifier(notifier_id, base::UTF8ToUTF16(title), enabled);
+}
+
+// A class used by NotifierSettingsView to integrate with a setting system
+// for the clients of this module.
+class TestingNotifierSettingsProvider
+ : public FakeNotifierSettingsProvider {
+ public:
+ TestingNotifierSettingsProvider(const std::vector<Notifier*>& notifiers,
+ const NotifierId& settings_handler_id)
+ : FakeNotifierSettingsProvider(notifiers),
+ settings_handler_id_(settings_handler_id),
+ request_count_(0u) {}
+ virtual ~TestingNotifierSettingsProvider() {}
+
+ virtual bool NotifierHasAdvancedSettings(const NotifierId& notifier_id) const
+ OVERRIDE {
+ return notifier_id == settings_handler_id_;
+ }
+
+ virtual void OnNotifierAdvancedSettingsRequested(
+ const NotifierId& notifier_id,
+ const std::string* notification_id) OVERRIDE {
+ request_count_++;
+ last_notifier_id_settings_requested_.reset(new NotifierId(notifier_id));
+ }
+
+ size_t request_count() const { return request_count_; }
+ const NotifierId* last_requested_notifier_id() const {
+ return last_notifier_id_settings_requested_.get();
+ }
+
+ private:
+ NotifierId settings_handler_id_;
+ size_t request_count_;
+ scoped_ptr<NotifierId> last_notifier_id_settings_requested_;
+};
+
+} // namespace
+
+class NotifierSettingsViewTest : public testing::Test {
+ public:
+ NotifierSettingsViewTest();
+ virtual ~NotifierSettingsViewTest();
+
+ virtual void SetUp() OVERRIDE;
+ virtual void TearDown() OVERRIDE;
+
+ NotifierSettingsView* GetView() const;
+ TestingNotifierSettingsProvider* settings_provider() const {
+ return settings_provider_.get();
+ }
+
+ private:
+ scoped_ptr<TestingNotifierSettingsProvider> settings_provider_;
+ scoped_ptr<NotifierSettingsView> notifier_settings_view_;
+
+ DISALLOW_COPY_AND_ASSIGN(NotifierSettingsViewTest);
+};
+
+NotifierSettingsViewTest::NotifierSettingsViewTest() {}
+
+NotifierSettingsViewTest::~NotifierSettingsViewTest() {}
+
+void NotifierSettingsViewTest::SetUp() {
+ std::vector<Notifier*> notifiers;
+ notifiers.push_back(NewNotifier("id", "title", /*enabled=*/true));
+ notifiers.push_back(NewNotifier("id2", "other title", /*enabled=*/false));
+ settings_provider_.reset(new TestingNotifierSettingsProvider(
+ notifiers, NotifierId(NotifierId::APPLICATION, "id")));
+ notifier_settings_view_.reset(
+ new NotifierSettingsView(settings_provider_.get()));
+}
+
+void NotifierSettingsViewTest::TearDown() {
+ notifier_settings_view_.reset();
+ settings_provider_.reset();
+}
+
+NotifierSettingsView* NotifierSettingsViewTest::GetView() const {
+ return notifier_settings_view_.get();
+}
+
+TEST_F(NotifierSettingsViewTest, TestLearnMoreButton) {
+ const std::set<NotifierSettingsView::NotifierButton*> buttons =
+ GetView()->buttons_;
+ EXPECT_EQ(2u, buttons.size());
+ size_t number_of_settings_buttons = 0;
+ std::set<NotifierSettingsView::NotifierButton*>::iterator iter =
+ buttons.begin();
+ for (; iter != buttons.end(); ++iter) {
+ if ((*iter)->has_learn_more()) {
+ ++number_of_settings_buttons;
+ (*iter)->SendLearnMorePressedForTest();
+ }
+ }
+
+ EXPECT_EQ(1u, number_of_settings_buttons);
+ EXPECT_EQ(1u, settings_provider()->request_count());
+ const NotifierId* last_settings_button_id =
+ settings_provider()->last_requested_notifier_id();
+ ASSERT_FALSE(last_settings_button_id == NULL);
+ EXPECT_EQ(NotifierId(NotifierId::APPLICATION, "id"),
+ *last_settings_button_id);
+}
+
+} // namespace message_center