| // 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 "chrome/browser/ui/views/profile_chooser_view.h" |
| |
| #include <vector> |
| |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/profiles/avatar_menu_model.h" |
| #include "chrome/browser/profiles/profile_info_util.h" |
| #include "chrome/browser/profiles/profile_manager.h" |
| #include "chrome/browser/ui/singleton_tabs.h" |
| #include "chrome/common/url_constants.h" |
| #include "grit/generated_resources.h" |
| #include "ui/base/l10n/l10n_util.h" |
| #include "ui/base/resource/resource_bundle.h" |
| #include "ui/gfx/image/image.h" |
| #include "ui/gfx/image/image_skia.h" |
| #include "ui/views/controls/button/label_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/separator.h" |
| #include "ui/views/layout/box_layout.h" |
| #include "ui/views/layout/grid_layout.h" |
| #include "ui/views/layout/layout_constants.h" |
| #include "ui/views/widget/widget.h" |
| |
| |
| // static |
| ProfileChooserView* ProfileChooserView::profile_bubble_ = NULL; |
| bool ProfileChooserView::close_on_deactivate_ = true; |
| |
| // static |
| void ProfileChooserView::ShowBubble( |
| views::View* anchor_view, |
| views::BubbleBorder::Arrow arrow, |
| views::BubbleBorder::BubbleAlignment border_alignment, |
| const gfx::Rect& anchor_rect, |
| Browser* browser) { |
| if (IsShowing()) |
| // TODO(bcwhite): handle case where we should show on different window |
| return; |
| |
| profile_bubble_ = new ProfileChooserView( |
| anchor_view, arrow, anchor_rect, browser); |
| views::BubbleDelegateView::CreateBubble(profile_bubble_); |
| profile_bubble_->set_close_on_deactivate(close_on_deactivate_); |
| profile_bubble_->SetAlignment(border_alignment); |
| profile_bubble_->GetWidget()->Show(); |
| } |
| |
| // static |
| bool ProfileChooserView::IsShowing() { |
| return profile_bubble_ != NULL; |
| } |
| |
| // static |
| void ProfileChooserView::Hide() { |
| if (IsShowing()) |
| profile_bubble_->GetWidget()->Close(); |
| } |
| |
| ProfileChooserView::ProfileChooserView( |
| views::View* anchor_view, |
| views::BubbleBorder::Arrow arrow, |
| const gfx::Rect& anchor_rect, |
| Browser* browser) |
| : BubbleDelegateView(anchor_view, arrow), |
| browser_(browser), |
| current_profile_view_(NULL), |
| guest_button_view_(NULL), |
| users_button_view_(NULL), |
| other_profiles_view_(NULL), |
| signout_current_profile_view_(NULL) { |
| avatar_menu_model_.reset(new AvatarMenuModel( |
| &g_browser_process->profile_manager()->GetProfileInfoCache(), |
| this, browser_)); |
| } |
| |
| ProfileChooserView::~ProfileChooserView() { |
| } |
| |
| void ProfileChooserView::Init() { |
| // Build the menu for the first time. |
| OnAvatarMenuModelChanged(avatar_menu_model_.get()); |
| } |
| |
| void ProfileChooserView::WindowClosing() { |
| DCHECK_EQ(profile_bubble_, this); |
| profile_bubble_ = NULL; |
| } |
| |
| bool ProfileChooserView::OnMousePressed(const ui::MouseEvent& event) { |
| return true; // Won't get "released" event if we don't return "true" here. |
| } |
| |
| void ProfileChooserView::OnMouseReleased(const ui::MouseEvent& event) { |
| views::View* sender = GetEventHandlerForPoint(event.location()); |
| ViewIndexes::const_iterator match; |
| |
| match = open_other_profile_indexes_map_.find(sender); |
| if (match != open_other_profile_indexes_map_.end()) { |
| avatar_menu_model_->SwitchToProfile( |
| match->second, |
| ui::DispositionFromEventFlags(event.flags()) == NEW_WINDOW); |
| } |
| } |
| |
| void ProfileChooserView::ButtonPressed(views::Button* sender, |
| const ui::Event& event) { |
| // Disable button after clicking so that it doesn't get clicked twice and |
| // start a second action... which can crash Chrome. But don't disable if it |
| // has no parent (like in tests) because that will also crash. |
| if (sender->parent()) |
| sender->SetEnabled(false); |
| |
| if (sender == guest_button_view_) { |
| avatar_menu_model_->SwitchToGuestProfileWindow(browser_); |
| } else if (sender == users_button_view_) { |
| chrome::ShowSingletonTab(browser_, GURL(chrome::kChromeUIUserManagerURL)); |
| } else { |
| DCHECK_EQ(sender, signout_current_profile_view_); |
| avatar_menu_model_->BeginSignOut(); |
| } |
| } |
| |
| void ProfileChooserView::OnAvatarMenuModelChanged( |
| AvatarMenuModel* avatar_menu_model) { |
| // Unset all our child view references and call RemoveAllChildViews() which |
| // will actually delete them. |
| current_profile_view_ = NULL; |
| guest_button_view_ = NULL; |
| users_button_view_ = NULL; |
| open_other_profile_indexes_map_.clear(); |
| other_profiles_view_ = NULL; |
| signout_current_profile_view_ = NULL; |
| RemoveAllChildViews(true); |
| |
| views::GridLayout* layout = new views::GridLayout(this); |
| SetLayoutManager(layout); |
| |
| views::ColumnSet* columns = layout->AddColumnSet(0); |
| columns->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 0, |
| views::GridLayout::USE_PREF, 0, 0); |
| |
| // Seprate items into active and alternatives. |
| Indexes other_profiles; |
| for (size_t i = 0; i < avatar_menu_model->GetNumberOfItems(); ++i) { |
| const AvatarMenuModel::Item& item = avatar_menu_model->GetItemAt(i); |
| if (item.active) { |
| DCHECK(!current_profile_view_); |
| current_profile_view_ = CreateCurrentProfileView(i); |
| } else { |
| other_profiles.push_back(i); |
| } |
| } |
| DCHECK(current_profile_view_); |
| |
| layout->StartRow(1, 0); |
| layout->AddView(current_profile_view_); |
| layout->AddPaddingRow(0, views::kRelatedControlSmallVerticalSpacing); |
| |
| layout->StartRow(0, 0); |
| layout->AddView(new views::Separator(views::Separator::HORIZONTAL)); |
| |
| other_profiles_view_ = CreateOtherProfilesView(other_profiles); |
| layout->StartRow(1, 0); |
| layout->AddView(other_profiles_view_); |
| |
| layout->StartRow(0, 0); |
| layout->AddView(new views::Separator(views::Separator::HORIZONTAL)); |
| |
| views::View* option_buttons_view = CreateOptionsView(); |
| option_buttons_view->SetSize(current_profile_view_->GetPreferredSize()); |
| layout->StartRow(0, 0); |
| layout->AddView(option_buttons_view); |
| |
| layout->StartRow(0, 0); |
| layout->AddView(new views::Separator(views::Separator::HORIZONTAL)); |
| |
| // If the bubble has already been shown then resize and reposition the bubble. |
| Layout(); |
| } |
| |
| views::View* ProfileChooserView::CreateProfileImageView(const gfx::Image& icon, |
| int side) { |
| views::ImageView* view = new views::ImageView(); |
| |
| gfx::Image image = profiles::GetSizedAvatarIconWithBorder( |
| icon, true, |
| side + profiles::kAvatarIconBorder, |
| side + profiles::kAvatarIconBorder); |
| // TODO(bcwhite): image alterations |
| view->SetImage(image.ToImageSkia()); |
| |
| return view; |
| } |
| |
| views::View* ProfileChooserView::CreateProfileCardView(size_t avatar_to_show) { |
| views::View* view = new views::View(); |
| |
| ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); |
| const AvatarMenuModel::Item& avatar_item = |
| avatar_menu_model_->GetItemAt(avatar_to_show); |
| |
| const int kLargeImageSide = 64; |
| views::View* photo_image = |
| CreateProfileImageView(avatar_item.icon, kLargeImageSide); |
| view->SetBoundsRect(photo_image->bounds()); |
| |
| views::Label* name_label = |
| new views::Label(avatar_item.name, |
| rb.GetFont(ui::ResourceBundle::MediumFont)); |
| name_label->SetHorizontalAlignment(gfx::ALIGN_LEFT); |
| |
| views::Label* email_label = new views::Label(avatar_item.sync_state); |
| if (avatar_item.signed_in) |
| email_label->SetElideBehavior(views::Label::ELIDE_AS_EMAIL); |
| email_label->SetFont(rb.GetFont(ui::ResourceBundle::SmallFont)); |
| email_label->SetHorizontalAlignment(gfx::ALIGN_LEFT); |
| |
| views::GridLayout* layout = new views::GridLayout(view); |
| view->SetLayoutManager(layout); |
| |
| views::ColumnSet* columns = layout->AddColumnSet(0); |
| columns->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 0, |
| views::GridLayout::USE_PREF, 0, 0); |
| columns->AddPaddingColumn(0, views::kUnrelatedControlHorizontalSpacing); |
| columns->AddColumn(views::GridLayout::LEADING, views::GridLayout::TRAILING, 1, |
| views::GridLayout::USE_PREF, 0, 0); |
| |
| layout->StartRow(1, 0); |
| layout->AddView(photo_image, 1, 3); |
| layout->AddView(name_label); |
| layout->StartRow(1, 0); |
| layout->SkipColumns(1); |
| layout->AddView(email_label, 1, 1, |
| views::GridLayout::LEADING, views::GridLayout::LEADING); |
| layout->StartRow(1, 0); |
| layout->SkipColumns(1); |
| |
| return view; |
| } |
| |
| views::View* ProfileChooserView::CreateCurrentProfileView( |
| size_t avatar_to_show) { |
| views::View* view = new views::View(); |
| |
| views::View* card_view = CreateProfileCardView(avatar_to_show); |
| |
| views::LabelButton* signout_button = new views::LabelButton( |
| this, |
| l10n_util::GetStringUTF16(IDS_PROFILES_PROFILE_SIGNOUT_BUTTON)); |
| signout_button->SetStyle(views::Button::STYLE_BUTTON); |
| DCHECK(!signout_current_profile_view_); |
| signout_current_profile_view_ = signout_button; |
| |
| views::GridLayout* layout = new views::GridLayout(view); |
| view->SetLayoutManager(layout); |
| |
| views::ColumnSet* columns = layout->AddColumnSet(0); |
| columns->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 0, |
| views::GridLayout::USE_PREF, 0, 0); |
| columns->AddPaddingColumn(0, 30); |
| columns->AddColumn(views::GridLayout::FILL, views::GridLayout::CENTER, 1, |
| views::GridLayout::USE_PREF, 0, 0); |
| columns->AddPaddingColumn(0, 10); |
| |
| layout->StartRow(1, 0); |
| layout->AddView(card_view, 1, 1); |
| layout->AddView(signout_button); |
| |
| return view; |
| } |
| |
| views::View* ProfileChooserView::CreateOtherProfilesView( |
| const Indexes& avatars_to_show) { |
| views::View* view = new views::View(); |
| |
| views::BoxLayout* layout = new views::BoxLayout( |
| views::BoxLayout::kHorizontal, 0, |
| views::kRelatedControlSmallVerticalSpacing, |
| views::kRelatedButtonHSpacing); |
| view->SetLayoutManager(layout); |
| |
| const int kSmallImageSide = 32; |
| for (Indexes::const_iterator iter = avatars_to_show.begin(); |
| iter != avatars_to_show.end(); |
| ++iter) { |
| const size_t index = *iter; |
| views::View* image = CreateProfileImageView( |
| avatar_menu_model_->GetItemAt(index).icon, kSmallImageSide); |
| open_other_profile_indexes_map_[image] = index; |
| view->AddChildView(image); |
| } |
| |
| return view; |
| } |
| |
| views::View* ProfileChooserView::CreateOptionsView() { |
| users_button_view_ = new views::LabelButton( |
| this, |
| l10n_util::GetStringUTF16(IDS_PROFILES_PROFILE_USERS_BUTTON)); |
| users_button_view_->SetHorizontalAlignment(gfx::ALIGN_CENTER); |
| users_button_view_->set_tag(IDS_PROFILES_PROFILE_USERS_BUTTON); |
| |
| guest_button_view_ = new views::LabelButton( |
| this, |
| l10n_util::GetStringUTF16(IDS_PROFILES_PROFILE_GUEST_BUTTON)); |
| guest_button_view_->SetHorizontalAlignment(gfx::ALIGN_CENTER); |
| guest_button_view_->set_tag(IDS_PROFILES_PROFILE_GUEST_BUTTON); |
| |
| views::View* view = new views::View(); |
| views::GridLayout* layout = new views::GridLayout(view); |
| view->SetLayoutManager(layout); |
| |
| views::ColumnSet* columns = layout->AddColumnSet(0); |
| columns->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 1, |
| views::GridLayout::USE_PREF, 0, 0); |
| columns->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 0, |
| views::GridLayout::USE_PREF, 0, 0); |
| columns->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 1, |
| views::GridLayout::USE_PREF, 0, 0); |
| columns->LinkColumnSizes(0, 2, -1); |
| |
| const int kButtonHeight = 40; |
| layout->StartRow(0, 0); |
| layout->AddView(users_button_view_, 1, 1, |
| views::GridLayout::FILL, views::GridLayout::FILL, |
| 0, kButtonHeight); |
| layout->AddView(new views::Separator(views::Separator::VERTICAL)); |
| layout->AddView(guest_button_view_, 1, 1, |
| views::GridLayout::FILL, views::GridLayout::FILL, |
| 0, kButtonHeight); |
| |
| return view; |
| } |