1 // Copyright (c) 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 #include "ui/message_center/views/notifier_settings_view.h" 6 7 #include <set> 8 #include <string> 9 10 #include "base/strings/string16.h" 11 #include "base/strings/utf_string_conversions.h" 12 #include "grit/ui_resources.h" 13 #include "grit/ui_strings.h" 14 #include "skia/ext/image_operations.h" 15 #include "third_party/skia/include/core/SkColor.h" 16 #include "ui/base/l10n/l10n_util.h" 17 #include "ui/base/models/simple_menu_model.h" 18 #include "ui/base/resource/resource_bundle.h" 19 #include "ui/events/keycodes/keyboard_codes.h" 20 #include "ui/gfx/canvas.h" 21 #include "ui/gfx/image/image.h" 22 #include "ui/gfx/size.h" 23 #include "ui/message_center/message_center_style.h" 24 #include "ui/message_center/views/message_center_view.h" 25 #include "ui/views/background.h" 26 #include "ui/views/border.h" 27 #include "ui/views/controls/button/checkbox.h" 28 #include "ui/views/controls/button/custom_button.h" 29 #include "ui/views/controls/button/label_button_border.h" 30 #include "ui/views/controls/button/menu_button.h" 31 #include "ui/views/controls/button/text_button.h" 32 #include "ui/views/controls/image_view.h" 33 #include "ui/views/controls/label.h" 34 #include "ui/views/controls/link.h" 35 #include "ui/views/controls/link_listener.h" 36 #include "ui/views/controls/menu/menu_runner.h" 37 #include "ui/views/controls/scroll_view.h" 38 #include "ui/views/controls/scrollbar/overlay_scroll_bar.h" 39 #include "ui/views/layout/box_layout.h" 40 #include "ui/views/layout/fill_layout.h" 41 #include "ui/views/layout/grid_layout.h" 42 #include "ui/views/painter.h" 43 #include "ui/views/widget/widget.h" 44 45 namespace message_center { 46 namespace settings { 47 48 // Additional views-specific parameters. 49 50 // The width of the settings pane in pixels. 51 const int kWidth = 360; 52 53 // The width of the learn more icon in pixels. 54 const int kLearnMoreSize = 12; 55 56 // The width of the click target that contains the learn more button in pixels. 57 const int kLearnMoreTargetWidth = 28; 58 59 // The height of the click target that contains the learn more button in pixels. 60 const int kLearnMoreTargetHeight = 40; 61 62 // The minimum height of the settings pane in pixels. 63 const int kMinimumHeight = 480; 64 65 // The horizontal margin of the title area of the settings pane in addition to 66 // the standard margin from settings::kHorizontalMargin. 67 const int kTitleMargin = 10; 68 69 } // namespace settings 70 71 namespace { 72 73 // The amount of built-in padding for the notifier group switcher. 74 const int kButtonPainterInsets = 5; 75 76 // Menu button metrics to make the text line up. 77 const int kMenuButtonInnateMargin = 2; 78 const int kMenuButtonLeftPadding = 12; 79 const int kMenuButtonRightPadding = 13; 80 const int kMenuButtonVerticalPadding = 9; 81 82 // Used to place the context menu correctly. 83 const int kMenuWhitespaceOffset = 2; 84 85 // The innate vertical blank space in the label for the title of the settings 86 // pane. 87 const int kInnateTitleBottomMargin = 1; 88 const int kInnateTitleTopMargin = 7; 89 90 // The innate top blank space in the label for the description of the settings 91 // pane. 92 const int kInnateDescriptionTopMargin = 2; 93 94 // Checkboxes have some built-in right padding blank space. 95 const int kInnateCheckboxRightPadding = 2; 96 97 // Spec defines the checkbox size; the innate padding throws this measurement 98 // off so we need to compute a slightly different area for the checkbox to 99 // inhabit. 100 const int kComputedCheckboxSize = 101 settings::kCheckboxSizeWithPadding - kInnateCheckboxRightPadding; 102 103 // The menubutton has innate margin, so we need to compensate for that when 104 // figuring the margin of the title area. 105 const int kComputedContentsTitleMargin = 0 - kMenuButtonInnateMargin; 106 107 // The spec doesn't include the bottom blank area of the title bar or the innate 108 // blank area in the description label, so we'll use this as the space between 109 // the title and description. 110 const int kComputedTitleBottomMargin = settings::kDescriptionToSwitcherSpace - 111 kInnateTitleBottomMargin - 112 kInnateDescriptionTopMargin; 113 114 // The blank space above the title needs to be adjusted by the amount of blank 115 // space included in the title label. 116 const int kComputedTitleTopMargin = 117 settings::kTopMargin - kInnateTitleTopMargin; 118 119 // The switcher has a lot of blank space built in so we should include that when 120 // spacing the title area vertically. 121 const int kComputedTitleElementSpacing = 122 settings::kDescriptionToSwitcherSpace - kButtonPainterInsets - 1; 123 124 // A function to create a focus border. 125 scoped_ptr<views::Painter> CreateFocusPainter() { 126 return views::Painter::CreateSolidFocusPainter(kFocusBorderColor, 127 gfx::Insets(1, 2, 3, 2)); 128 } 129 130 // EntryView ------------------------------------------------------------------ 131 132 // The view to guarantee the 48px height and place the contents at the 133 // middle. It also guarantee the left margin. 134 class EntryView : public views::View { 135 public: 136 explicit EntryView(views::View* contents); 137 virtual ~EntryView(); 138 139 // views::View: 140 virtual void Layout() OVERRIDE; 141 virtual gfx::Size GetPreferredSize() const OVERRIDE; 142 virtual void GetAccessibleState(ui::AXViewState* state) OVERRIDE; 143 virtual void OnFocus() OVERRIDE; 144 virtual bool OnKeyPressed(const ui::KeyEvent& event) OVERRIDE; 145 virtual bool OnKeyReleased(const ui::KeyEvent& event) OVERRIDE; 146 virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; 147 virtual void OnBlur() OVERRIDE; 148 149 private: 150 scoped_ptr<views::Painter> focus_painter_; 151 152 DISALLOW_COPY_AND_ASSIGN(EntryView); 153 }; 154 155 EntryView::EntryView(views::View* contents) 156 : focus_painter_(CreateFocusPainter()) { 157 AddChildView(contents); 158 } 159 160 EntryView::~EntryView() {} 161 162 void EntryView::Layout() { 163 DCHECK_EQ(1, child_count()); 164 views::View* content = child_at(0); 165 int content_width = width(); 166 int content_height = content->GetHeightForWidth(content_width); 167 int y = std::max((height() - content_height) / 2, 0); 168 content->SetBounds(0, y, content_width, content_height); 169 } 170 171 gfx::Size EntryView::GetPreferredSize() const { 172 DCHECK_EQ(1, child_count()); 173 gfx::Size size = child_at(0)->GetPreferredSize(); 174 size.SetToMax(gfx::Size(settings::kWidth, settings::kEntryHeight)); 175 return size; 176 } 177 178 void EntryView::GetAccessibleState(ui::AXViewState* state) { 179 DCHECK_EQ(1, child_count()); 180 child_at(0)->GetAccessibleState(state); 181 } 182 183 void EntryView::OnFocus() { 184 views::View::OnFocus(); 185 ScrollRectToVisible(GetLocalBounds()); 186 // We render differently when focused. 187 SchedulePaint(); 188 } 189 190 bool EntryView::OnKeyPressed(const ui::KeyEvent& event) { 191 return child_at(0)->OnKeyPressed(event); 192 } 193 194 bool EntryView::OnKeyReleased(const ui::KeyEvent& event) { 195 return child_at(0)->OnKeyReleased(event); 196 } 197 198 void EntryView::OnPaint(gfx::Canvas* canvas) { 199 View::OnPaint(canvas); 200 views::Painter::PaintFocusPainter(this, canvas, focus_painter_.get()); 201 } 202 203 void EntryView::OnBlur() { 204 View::OnBlur(); 205 // We render differently when focused. 206 SchedulePaint(); 207 } 208 209 } // namespace 210 211 212 // NotifierGroupMenuModel ----------------------------------------------------- 213 214 class NotifierGroupMenuModel : public ui::SimpleMenuModel, 215 public ui::SimpleMenuModel::Delegate { 216 public: 217 NotifierGroupMenuModel(NotifierSettingsProvider* notifier_settings_provider); 218 virtual ~NotifierGroupMenuModel(); 219 220 // ui::SimpleMenuModel::Delegate: 221 virtual bool IsCommandIdChecked(int command_id) const OVERRIDE; 222 virtual bool IsCommandIdEnabled(int command_id) const OVERRIDE; 223 virtual bool GetAcceleratorForCommandId( 224 int command_id, 225 ui::Accelerator* accelerator) OVERRIDE; 226 virtual void ExecuteCommand(int command_id, int event_flags) OVERRIDE; 227 228 private: 229 NotifierSettingsProvider* notifier_settings_provider_; 230 231 DISALLOW_COPY_AND_ASSIGN(NotifierGroupMenuModel); 232 }; 233 234 NotifierGroupMenuModel::NotifierGroupMenuModel( 235 NotifierSettingsProvider* notifier_settings_provider) 236 : ui::SimpleMenuModel(this), 237 notifier_settings_provider_(notifier_settings_provider) { 238 if (!notifier_settings_provider_) 239 return; 240 241 size_t num_menu_items = notifier_settings_provider_->GetNotifierGroupCount(); 242 for (size_t i = 0; i < num_menu_items; ++i) { 243 const NotifierGroup& group = 244 notifier_settings_provider_->GetNotifierGroupAt(i); 245 246 AddCheckItem(i, group.login_info.empty() ? group.name : group.login_info); 247 } 248 } 249 250 NotifierGroupMenuModel::~NotifierGroupMenuModel() {} 251 252 bool NotifierGroupMenuModel::IsCommandIdChecked(int command_id) const { 253 // If there's no provider, assume only one notifier group - the active one. 254 return !notifier_settings_provider_ || 255 notifier_settings_provider_->IsNotifierGroupActiveAt(command_id); 256 } 257 258 bool NotifierGroupMenuModel::IsCommandIdEnabled(int command_id) const { 259 return true; 260 } 261 262 bool NotifierGroupMenuModel::GetAcceleratorForCommandId( 263 int command_id, 264 ui::Accelerator* accelerator) { 265 return false; 266 } 267 268 void NotifierGroupMenuModel::ExecuteCommand(int command_id, int event_flags) { 269 if (!notifier_settings_provider_) 270 return; 271 272 size_t notifier_group_index = static_cast<size_t>(command_id); 273 size_t num_notifier_groups = 274 notifier_settings_provider_->GetNotifierGroupCount(); 275 if (notifier_group_index >= num_notifier_groups) 276 return; 277 278 notifier_settings_provider_->SwitchToNotifierGroup(notifier_group_index); 279 } 280 281 282 // NotifierSettingsView::NotifierButton --------------------------------------- 283 284 // We do not use views::Checkbox class directly because it doesn't support 285 // showing 'icon'. 286 NotifierSettingsView::NotifierButton::NotifierButton( 287 NotifierSettingsProvider* provider, 288 Notifier* notifier, 289 views::ButtonListener* listener) 290 : views::CustomButton(listener), 291 provider_(provider), 292 notifier_(notifier), 293 icon_view_(new views::ImageView()), 294 name_view_(new views::Label(notifier_->name)), 295 checkbox_(new views::Checkbox(base::string16())), 296 learn_more_(NULL) { 297 DCHECK(provider); 298 DCHECK(notifier); 299 300 // Since there may never be an icon (but that could change at a later time), 301 // we own the icon view here. 302 icon_view_->set_owned_by_client(); 303 304 checkbox_->SetChecked(notifier_->enabled); 305 checkbox_->set_listener(this); 306 checkbox_->SetFocusable(false); 307 checkbox_->SetAccessibleName(notifier_->name); 308 309 if (ShouldHaveLearnMoreButton()) { 310 // Create a more-info button that will be right-aligned. 311 learn_more_ = new views::ImageButton(this); 312 learn_more_->SetFocusPainter(CreateFocusPainter()); 313 learn_more_->set_request_focus_on_press(false); 314 learn_more_->SetFocusable(true); 315 316 ui::ResourceBundle& rb = ResourceBundle::GetSharedInstance(); 317 learn_more_->SetImage( 318 views::Button::STATE_NORMAL, 319 rb.GetImageSkiaNamed(IDR_NOTIFICATION_ADVANCED_SETTINGS)); 320 learn_more_->SetImage( 321 views::Button::STATE_HOVERED, 322 rb.GetImageSkiaNamed(IDR_NOTIFICATION_ADVANCED_SETTINGS_HOVER)); 323 learn_more_->SetImage( 324 views::Button::STATE_PRESSED, 325 rb.GetImageSkiaNamed(IDR_NOTIFICATION_ADVANCED_SETTINGS_PRESSED)); 326 learn_more_->SetState(views::Button::STATE_NORMAL); 327 int learn_more_border_width = 328 (settings::kLearnMoreTargetWidth - settings::kLearnMoreSize) / 2; 329 int learn_more_border_height = 330 (settings::kLearnMoreTargetHeight - settings::kLearnMoreSize) / 2; 331 // The image itself is quite small, this large invisible border creates a 332 // much bigger click target. 333 learn_more_->SetBorder( 334 views::Border::CreateEmptyBorder(learn_more_border_height, 335 learn_more_border_width, 336 learn_more_border_height, 337 learn_more_border_width)); 338 learn_more_->SetImageAlignment(views::ImageButton::ALIGN_CENTER, 339 views::ImageButton::ALIGN_MIDDLE); 340 } 341 342 UpdateIconImage(notifier_->icon); 343 } 344 345 NotifierSettingsView::NotifierButton::~NotifierButton() { 346 } 347 348 void NotifierSettingsView::NotifierButton::UpdateIconImage( 349 const gfx::Image& icon) { 350 bool has_icon_view = false; 351 352 notifier_->icon = icon; 353 if (!icon.IsEmpty()) { 354 icon_view_->SetImage(icon.ToImageSkia()); 355 icon_view_->SetImageSize( 356 gfx::Size(settings::kEntryIconSize, settings::kEntryIconSize)); 357 has_icon_view = true; 358 } 359 GridChanged(ShouldHaveLearnMoreButton(), has_icon_view); 360 } 361 362 void NotifierSettingsView::NotifierButton::SetChecked(bool checked) { 363 checkbox_->SetChecked(checked); 364 notifier_->enabled = checked; 365 } 366 367 bool NotifierSettingsView::NotifierButton::checked() const { 368 return checkbox_->checked(); 369 } 370 371 bool NotifierSettingsView::NotifierButton::has_learn_more() const { 372 return learn_more_ != NULL; 373 } 374 375 const Notifier& NotifierSettingsView::NotifierButton::notifier() const { 376 return *notifier_.get(); 377 } 378 379 void NotifierSettingsView::NotifierButton::SendLearnMorePressedForTest() { 380 if (learn_more_ == NULL) 381 return; 382 gfx::Point point(110, 120); 383 ui::MouseEvent pressed( 384 ui::ET_MOUSE_PRESSED, point, point, ui::EF_LEFT_MOUSE_BUTTON, 385 ui::EF_LEFT_MOUSE_BUTTON); 386 ButtonPressed(learn_more_, pressed); 387 } 388 389 void NotifierSettingsView::NotifierButton::ButtonPressed( 390 views::Button* button, 391 const ui::Event& event) { 392 if (button == checkbox_) { 393 // The checkbox state has already changed at this point, but we'll update 394 // the state on NotifierSettingsView::ButtonPressed() too, so here change 395 // back to the previous state. 396 checkbox_->SetChecked(!checkbox_->checked()); 397 CustomButton::NotifyClick(event); 398 } else if (button == learn_more_) { 399 DCHECK(provider_); 400 provider_->OnNotifierAdvancedSettingsRequested(notifier_->notifier_id, 401 NULL); 402 } 403 } 404 405 void NotifierSettingsView::NotifierButton::GetAccessibleState( 406 ui::AXViewState* state) { 407 static_cast<views::View*>(checkbox_)->GetAccessibleState(state); 408 } 409 410 bool NotifierSettingsView::NotifierButton::ShouldHaveLearnMoreButton() const { 411 if (!provider_) 412 return false; 413 414 return provider_->NotifierHasAdvancedSettings(notifier_->notifier_id); 415 } 416 417 void NotifierSettingsView::NotifierButton::GridChanged(bool has_learn_more, 418 bool has_icon_view) { 419 using views::ColumnSet; 420 using views::GridLayout; 421 422 GridLayout* layout = new GridLayout(this); 423 SetLayoutManager(layout); 424 ColumnSet* cs = layout->AddColumnSet(0); 425 // Add a column for the checkbox. 426 cs->AddPaddingColumn(0, kInnateCheckboxRightPadding); 427 cs->AddColumn(GridLayout::CENTER, 428 GridLayout::CENTER, 429 0, 430 GridLayout::FIXED, 431 kComputedCheckboxSize, 432 0); 433 cs->AddPaddingColumn(0, settings::kInternalHorizontalSpacing); 434 435 if (has_icon_view) { 436 // Add a column for the icon. 437 cs->AddColumn(GridLayout::CENTER, 438 GridLayout::CENTER, 439 0, 440 GridLayout::FIXED, 441 settings::kEntryIconSize, 442 0); 443 cs->AddPaddingColumn(0, settings::kInternalHorizontalSpacing); 444 } 445 446 // Add a column for the name. 447 cs->AddColumn( 448 GridLayout::LEADING, GridLayout::CENTER, 0, GridLayout::USE_PREF, 0, 0); 449 450 // Add a padding column which contains expandable blank space. 451 cs->AddPaddingColumn(1, 0); 452 453 // Add a column for the learn more button if necessary. 454 if (has_learn_more) { 455 cs->AddPaddingColumn(0, settings::kInternalHorizontalSpacing); 456 cs->AddColumn( 457 GridLayout::CENTER, GridLayout::CENTER, 0, GridLayout::USE_PREF, 0, 0); 458 } 459 460 layout->StartRow(0, 0); 461 layout->AddView(checkbox_); 462 if (has_icon_view) 463 layout->AddView(icon_view_.get()); 464 layout->AddView(name_view_); 465 if (has_learn_more) 466 layout->AddView(learn_more_); 467 468 Layout(); 469 } 470 471 472 // NotifierSettingsView ------------------------------------------------------- 473 474 NotifierSettingsView::NotifierSettingsView(NotifierSettingsProvider* provider) 475 : title_arrow_(NULL), 476 title_label_(NULL), 477 notifier_group_selector_(NULL), 478 scroller_(NULL), 479 provider_(provider) { 480 // |provider_| may be NULL in tests. 481 if (provider_) 482 provider_->AddObserver(this); 483 484 SetFocusable(true); 485 set_background( 486 views::Background::CreateSolidBackground(kMessageCenterBackgroundColor)); 487 SetPaintToLayer(true); 488 489 title_label_ = new views::Label( 490 l10n_util::GetStringUTF16(IDS_MESSAGE_CENTER_SETTINGS_BUTTON_LABEL), 491 ui::ResourceBundle::GetSharedInstance().GetFontList( 492 ui::ResourceBundle::MediumFont)); 493 title_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT); 494 title_label_->SetMultiLine(true); 495 title_label_->SetBorder( 496 views::Border::CreateEmptyBorder(kComputedTitleTopMargin, 497 settings::kTitleMargin, 498 kComputedTitleBottomMargin, 499 settings::kTitleMargin)); 500 501 AddChildView(title_label_); 502 503 scroller_ = new views::ScrollView(); 504 scroller_->SetVerticalScrollBar(new views::OverlayScrollBar(false)); 505 AddChildView(scroller_); 506 507 std::vector<Notifier*> notifiers; 508 if (provider_) 509 provider_->GetNotifierList(¬ifiers); 510 511 UpdateContentsView(notifiers); 512 } 513 514 NotifierSettingsView::~NotifierSettingsView() { 515 // |provider_| may be NULL in tests. 516 if (provider_) 517 provider_->RemoveObserver(this); 518 } 519 520 bool NotifierSettingsView::IsScrollable() { 521 return scroller_->height() < scroller_->contents()->height(); 522 } 523 524 void NotifierSettingsView::UpdateIconImage(const NotifierId& notifier_id, 525 const gfx::Image& icon) { 526 for (std::set<NotifierButton*>::iterator iter = buttons_.begin(); 527 iter != buttons_.end(); 528 ++iter) { 529 if ((*iter)->notifier().notifier_id == notifier_id) { 530 (*iter)->UpdateIconImage(icon); 531 return; 532 } 533 } 534 } 535 536 void NotifierSettingsView::NotifierGroupChanged() { 537 std::vector<Notifier*> notifiers; 538 if (provider_) 539 provider_->GetNotifierList(¬ifiers); 540 541 UpdateContentsView(notifiers); 542 } 543 544 void NotifierSettingsView::NotifierEnabledChanged(const NotifierId& notifier_id, 545 bool enabled) {} 546 547 void NotifierSettingsView::UpdateContentsView( 548 const std::vector<Notifier*>& notifiers) { 549 buttons_.clear(); 550 551 views::View* contents_view = new views::View(); 552 contents_view->SetLayoutManager(new views::BoxLayout( 553 views::BoxLayout::kVertical, settings::kHorizontalMargin, 0, 0)); 554 555 views::View* contents_title_view = new views::View(); 556 contents_title_view->SetLayoutManager( 557 new views::BoxLayout(views::BoxLayout::kVertical, 558 kComputedContentsTitleMargin, 559 0, 560 kComputedTitleElementSpacing)); 561 562 bool need_account_switcher = 563 provider_ && provider_->GetNotifierGroupCount() > 1; 564 int top_label_resource_id = 565 need_account_switcher ? IDS_MESSAGE_CENTER_SETTINGS_DESCRIPTION_MULTIUSER 566 : IDS_MESSAGE_CENTER_SETTINGS_DIALOG_DESCRIPTION; 567 568 views::Label* top_label = 569 new views::Label(l10n_util::GetStringUTF16(top_label_resource_id)); 570 571 top_label->SetHorizontalAlignment(gfx::ALIGN_LEFT); 572 top_label->SetMultiLine(true); 573 top_label->SetBorder(views::Border::CreateEmptyBorder( 574 0, 575 settings::kTitleMargin + kMenuButtonInnateMargin, 576 0, 577 settings::kTitleMargin + kMenuButtonInnateMargin)); 578 contents_title_view->AddChildView(top_label); 579 580 if (need_account_switcher) { 581 const NotifierGroup& active_group = provider_->GetActiveNotifierGroup(); 582 base::string16 notifier_group_text = active_group.login_info.empty() ? 583 active_group.name : active_group.login_info; 584 notifier_group_selector_ = 585 new views::MenuButton(NULL, notifier_group_text, this, true); 586 scoped_ptr<views::TextButtonDefaultBorder> selector_border( 587 new views::TextButtonDefaultBorder()); 588 ui::ResourceBundle* rb = &ResourceBundle::GetSharedInstance(); 589 gfx::Insets painter_insets(kButtonPainterInsets, kButtonPainterInsets, 590 kButtonPainterInsets, kButtonPainterInsets); 591 selector_border->set_normal_painter(views::Painter::CreateImagePainter( 592 *rb->GetImageSkiaNamed(IDR_BUTTON_NORMAL), painter_insets)); 593 selector_border->set_hot_painter(views::Painter::CreateImagePainter( 594 *rb->GetImageSkiaNamed(IDR_BUTTON_HOVER), painter_insets)); 595 selector_border->set_pushed_painter(views::Painter::CreateImagePainter( 596 *rb->GetImageSkiaNamed(IDR_BUTTON_PRESSED), painter_insets)); 597 selector_border->SetInsets(gfx::Insets( 598 kMenuButtonVerticalPadding, kMenuButtonLeftPadding, 599 kMenuButtonVerticalPadding, kMenuButtonRightPadding)); 600 notifier_group_selector_->SetBorder( 601 selector_border.PassAs<views::Border>()); 602 notifier_group_selector_->SetFocusPainter(scoped_ptr<views::Painter>()); 603 notifier_group_selector_->set_animate_on_state_change(false); 604 notifier_group_selector_->SetFocusable(true); 605 contents_title_view->AddChildView(notifier_group_selector_); 606 } 607 608 contents_view->AddChildView(contents_title_view); 609 610 size_t notifier_count = notifiers.size(); 611 for (size_t i = 0; i < notifier_count; ++i) { 612 NotifierButton* button = new NotifierButton(provider_, notifiers[i], this); 613 EntryView* entry = new EntryView(button); 614 615 // This code emulates separators using borders. We will create an invisible 616 // border on the last notifier, as the spec leaves a space for it. 617 scoped_ptr<views::Border> entry_border; 618 if (i == notifier_count - 1) { 619 entry_border = views::Border::CreateEmptyBorder( 620 0, 0, settings::kEntrySeparatorHeight, 0); 621 } else { 622 entry_border = 623 views::Border::CreateSolidSidedBorder(0, 624 0, 625 settings::kEntrySeparatorHeight, 626 0, 627 settings::kEntrySeparatorColor); 628 } 629 entry->SetBorder(entry_border.Pass()); 630 entry->SetFocusable(true); 631 contents_view->AddChildView(entry); 632 buttons_.insert(button); 633 } 634 635 scroller_->SetContents(contents_view); 636 637 contents_view->SetBoundsRect(gfx::Rect(contents_view->GetPreferredSize())); 638 InvalidateLayout(); 639 } 640 641 void NotifierSettingsView::Layout() { 642 int title_height = title_label_->GetHeightForWidth(width()); 643 title_label_->SetBounds(settings::kTitleMargin, 644 0, 645 width() - settings::kTitleMargin * 2, 646 title_height); 647 648 views::View* contents_view = scroller_->contents(); 649 int content_width = width(); 650 int content_height = contents_view->GetHeightForWidth(content_width); 651 if (title_height + content_height > height()) { 652 content_width -= scroller_->GetScrollBarWidth(); 653 content_height = contents_view->GetHeightForWidth(content_width); 654 } 655 contents_view->SetBounds(0, 0, content_width, content_height); 656 scroller_->SetBounds(0, title_height, width(), height() - title_height); 657 } 658 659 gfx::Size NotifierSettingsView::GetMinimumSize() const { 660 gfx::Size size(settings::kWidth, settings::kMinimumHeight); 661 int total_height = title_label_->GetPreferredSize().height() + 662 scroller_->contents()->GetPreferredSize().height(); 663 if (total_height > settings::kMinimumHeight) 664 size.Enlarge(scroller_->GetScrollBarWidth(), 0); 665 return size; 666 } 667 668 gfx::Size NotifierSettingsView::GetPreferredSize() const { 669 gfx::Size preferred_size; 670 gfx::Size title_size = title_label_->GetPreferredSize(); 671 gfx::Size content_size = scroller_->contents()->GetPreferredSize(); 672 return gfx::Size(std::max(title_size.width(), content_size.width()), 673 title_size.height() + content_size.height()); 674 } 675 676 bool NotifierSettingsView::OnKeyPressed(const ui::KeyEvent& event) { 677 if (event.key_code() == ui::VKEY_ESCAPE) { 678 GetWidget()->Close(); 679 return true; 680 } 681 682 return scroller_->OnKeyPressed(event); 683 } 684 685 bool NotifierSettingsView::OnMouseWheel(const ui::MouseWheelEvent& event) { 686 return scroller_->OnMouseWheel(event); 687 } 688 689 void NotifierSettingsView::ButtonPressed(views::Button* sender, 690 const ui::Event& event) { 691 if (sender == title_arrow_) { 692 MessageCenterView* center_view = static_cast<MessageCenterView*>(parent()); 693 center_view->SetSettingsVisible(!center_view->settings_visible()); 694 return; 695 } 696 697 std::set<NotifierButton*>::iterator iter = 698 buttons_.find(static_cast<NotifierButton*>(sender)); 699 700 if (iter == buttons_.end()) 701 return; 702 703 (*iter)->SetChecked(!(*iter)->checked()); 704 if (provider_) 705 provider_->SetNotifierEnabled((*iter)->notifier(), (*iter)->checked()); 706 } 707 708 void NotifierSettingsView::OnMenuButtonClicked(views::View* source, 709 const gfx::Point& point) { 710 notifier_group_menu_model_.reset(new NotifierGroupMenuModel(provider_)); 711 notifier_group_menu_runner_.reset( 712 new views::MenuRunner(notifier_group_menu_model_.get())); 713 gfx::Rect menu_anchor = source->GetBoundsInScreen(); 714 menu_anchor.Inset( 715 gfx::Insets(0, kMenuWhitespaceOffset, 0, kMenuWhitespaceOffset)); 716 if (views::MenuRunner::MENU_DELETED == 717 notifier_group_menu_runner_->RunMenuAt(GetWidget(), 718 notifier_group_selector_, 719 menu_anchor, 720 views::MENU_ANCHOR_BUBBLE_ABOVE, 721 ui::MENU_SOURCE_MOUSE, 722 views::MenuRunner::CONTEXT_MENU)) 723 return; 724 MessageCenterView* center_view = static_cast<MessageCenterView*>(parent()); 725 center_view->OnSettingsChanged(); 726 } 727 728 } // namespace message_center 729