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