Home | History | Annotate | Download | only in views
      1 // Copyright 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/ui/views/profile_chooser_view.h"
      6 
      7 #include <vector>
      8 
      9 #include "chrome/browser/browser_process.h"
     10 #include "chrome/browser/profiles/avatar_menu_model.h"
     11 #include "chrome/browser/profiles/profile_info_util.h"
     12 #include "chrome/browser/profiles/profile_manager.h"
     13 #include "chrome/browser/ui/singleton_tabs.h"
     14 #include "chrome/common/url_constants.h"
     15 #include "grit/generated_resources.h"
     16 #include "ui/base/l10n/l10n_util.h"
     17 #include "ui/base/resource/resource_bundle.h"
     18 #include "ui/gfx/image/image.h"
     19 #include "ui/gfx/image/image_skia.h"
     20 #include "ui/views/controls/button/label_button.h"
     21 #include "ui/views/controls/image_view.h"
     22 #include "ui/views/controls/label.h"
     23 #include "ui/views/controls/link.h"
     24 #include "ui/views/controls/separator.h"
     25 #include "ui/views/layout/box_layout.h"
     26 #include "ui/views/layout/grid_layout.h"
     27 #include "ui/views/layout/layout_constants.h"
     28 #include "ui/views/widget/widget.h"
     29 
     30 
     31 // static
     32 ProfileChooserView* ProfileChooserView::profile_bubble_ = NULL;
     33 bool ProfileChooserView::close_on_deactivate_ = true;
     34 
     35 // static
     36 void ProfileChooserView::ShowBubble(
     37     views::View* anchor_view,
     38     views::BubbleBorder::Arrow arrow,
     39     views::BubbleBorder::BubbleAlignment border_alignment,
     40     const gfx::Rect& anchor_rect,
     41     Browser* browser) {
     42   if (IsShowing())
     43     // TODO(bcwhite): handle case where we should show on different window
     44     return;
     45 
     46   profile_bubble_ = new ProfileChooserView(
     47       anchor_view, arrow, anchor_rect, browser);
     48   views::BubbleDelegateView::CreateBubble(profile_bubble_);
     49   profile_bubble_->set_close_on_deactivate(close_on_deactivate_);
     50   profile_bubble_->SetAlignment(border_alignment);
     51   profile_bubble_->GetWidget()->Show();
     52 }
     53 
     54 // static
     55 bool ProfileChooserView::IsShowing() {
     56   return profile_bubble_ != NULL;
     57 }
     58 
     59 // static
     60 void ProfileChooserView::Hide() {
     61   if (IsShowing())
     62     profile_bubble_->GetWidget()->Close();
     63 }
     64 
     65 ProfileChooserView::ProfileChooserView(
     66     views::View* anchor_view,
     67     views::BubbleBorder::Arrow arrow,
     68     const gfx::Rect& anchor_rect,
     69     Browser* browser)
     70     : BubbleDelegateView(anchor_view, arrow),
     71       browser_(browser),
     72       current_profile_view_(NULL),
     73       guest_button_view_(NULL),
     74       users_button_view_(NULL),
     75       other_profiles_view_(NULL),
     76       signout_current_profile_view_(NULL) {
     77   avatar_menu_model_.reset(new AvatarMenuModel(
     78       &g_browser_process->profile_manager()->GetProfileInfoCache(),
     79       this, browser_));
     80 }
     81 
     82 ProfileChooserView::~ProfileChooserView() {
     83 }
     84 
     85 void ProfileChooserView::Init() {
     86   // Build the menu for the first time.
     87   OnAvatarMenuModelChanged(avatar_menu_model_.get());
     88 }
     89 
     90 void ProfileChooserView::WindowClosing() {
     91   DCHECK_EQ(profile_bubble_, this);
     92   profile_bubble_ = NULL;
     93 }
     94 
     95 bool ProfileChooserView::OnMousePressed(const ui::MouseEvent& event) {
     96   return true;  // Won't get "released" event if we don't return "true" here.
     97 }
     98 
     99 void ProfileChooserView::OnMouseReleased(const ui::MouseEvent& event) {
    100   views::View* sender = GetEventHandlerForPoint(event.location());
    101   ViewIndexes::const_iterator match;
    102 
    103   match = open_other_profile_indexes_map_.find(sender);
    104   if (match != open_other_profile_indexes_map_.end()) {
    105     avatar_menu_model_->SwitchToProfile(
    106         match->second,
    107         ui::DispositionFromEventFlags(event.flags()) == NEW_WINDOW);
    108   }
    109 }
    110 
    111 void ProfileChooserView::ButtonPressed(views::Button* sender,
    112                                        const ui::Event& event) {
    113   // Disable button after clicking so that it doesn't get clicked twice and
    114   // start a second action... which can crash Chrome.  But don't disable if it
    115   // has no parent (like in tests) because that will also crash.
    116   if (sender->parent())
    117     sender->SetEnabled(false);
    118 
    119   if (sender == guest_button_view_) {
    120     avatar_menu_model_->SwitchToGuestProfileWindow(browser_);
    121   } else if (sender == users_button_view_) {
    122     chrome::ShowSingletonTab(browser_, GURL(chrome::kChromeUIUserManagerURL));
    123   } else {
    124     DCHECK_EQ(sender, signout_current_profile_view_);
    125     avatar_menu_model_->BeginSignOut();
    126   }
    127 }
    128 
    129 void ProfileChooserView::OnAvatarMenuModelChanged(
    130     AvatarMenuModel* avatar_menu_model) {
    131   // Unset all our child view references and call RemoveAllChildViews() which
    132   // will actually delete them.
    133   current_profile_view_ = NULL;
    134   guest_button_view_ = NULL;
    135   users_button_view_ = NULL;
    136   open_other_profile_indexes_map_.clear();
    137   other_profiles_view_ = NULL;
    138   signout_current_profile_view_ = NULL;
    139   RemoveAllChildViews(true);
    140 
    141   views::GridLayout* layout = new views::GridLayout(this);
    142   SetLayoutManager(layout);
    143 
    144   views::ColumnSet* columns = layout->AddColumnSet(0);
    145   columns->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 0,
    146                      views::GridLayout::USE_PREF, 0, 0);
    147 
    148   // Seprate items into active and alternatives.
    149   Indexes other_profiles;
    150   for (size_t i = 0; i < avatar_menu_model->GetNumberOfItems(); ++i) {
    151     const AvatarMenuModel::Item& item = avatar_menu_model->GetItemAt(i);
    152     if (item.active) {
    153       DCHECK(!current_profile_view_);
    154       current_profile_view_ = CreateCurrentProfileView(i);
    155     } else {
    156       other_profiles.push_back(i);
    157     }
    158   }
    159   DCHECK(current_profile_view_);
    160 
    161   layout->StartRow(1, 0);
    162   layout->AddView(current_profile_view_);
    163   layout->AddPaddingRow(0, views::kRelatedControlSmallVerticalSpacing);
    164 
    165   layout->StartRow(0, 0);
    166   layout->AddView(new views::Separator(views::Separator::HORIZONTAL));
    167 
    168   other_profiles_view_ = CreateOtherProfilesView(other_profiles);
    169   layout->StartRow(1, 0);
    170   layout->AddView(other_profiles_view_);
    171 
    172   layout->StartRow(0, 0);
    173   layout->AddView(new views::Separator(views::Separator::HORIZONTAL));
    174 
    175   views::View* option_buttons_view = CreateOptionsView();
    176   option_buttons_view->SetSize(current_profile_view_->GetPreferredSize());
    177   layout->StartRow(0, 0);
    178   layout->AddView(option_buttons_view);
    179 
    180   layout->StartRow(0, 0);
    181   layout->AddView(new views::Separator(views::Separator::HORIZONTAL));
    182 
    183   // If the bubble has already been shown then resize and reposition the bubble.
    184   Layout();
    185 }
    186 
    187 views::View* ProfileChooserView::CreateProfileImageView(const gfx::Image& icon,
    188                                                         int side) {
    189   views::ImageView* view = new views::ImageView();
    190 
    191   gfx::Image image = profiles::GetSizedAvatarIconWithBorder(
    192       icon, true,
    193       side + profiles::kAvatarIconBorder,
    194       side + profiles::kAvatarIconBorder);
    195   // TODO(bcwhite): image alterations
    196   view->SetImage(image.ToImageSkia());
    197 
    198   return view;
    199 }
    200 
    201 views::View* ProfileChooserView::CreateProfileCardView(size_t avatar_to_show) {
    202   views::View* view = new views::View();
    203 
    204   ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
    205   const AvatarMenuModel::Item& avatar_item =
    206       avatar_menu_model_->GetItemAt(avatar_to_show);
    207 
    208   const int kLargeImageSide = 64;
    209   views::View* photo_image =
    210       CreateProfileImageView(avatar_item.icon, kLargeImageSide);
    211   view->SetBoundsRect(photo_image->bounds());
    212 
    213   views::Label* name_label =
    214       new views::Label(avatar_item.name,
    215                        rb.GetFont(ui::ResourceBundle::MediumFont));
    216   name_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
    217 
    218   views::Label* email_label = new views::Label(avatar_item.sync_state);
    219   if (avatar_item.signed_in)
    220     email_label->SetElideBehavior(views::Label::ELIDE_AS_EMAIL);
    221   email_label->SetFont(rb.GetFont(ui::ResourceBundle::SmallFont));
    222   email_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
    223 
    224   views::GridLayout* layout = new views::GridLayout(view);
    225   view->SetLayoutManager(layout);
    226 
    227   views::ColumnSet* columns = layout->AddColumnSet(0);
    228   columns->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 0,
    229                      views::GridLayout::USE_PREF, 0, 0);
    230   columns->AddPaddingColumn(0, views::kUnrelatedControlHorizontalSpacing);
    231   columns->AddColumn(views::GridLayout::LEADING, views::GridLayout::TRAILING, 1,
    232                      views::GridLayout::USE_PREF, 0, 0);
    233 
    234   layout->StartRow(1, 0);
    235   layout->AddView(photo_image, 1, 3);
    236   layout->AddView(name_label);
    237   layout->StartRow(1, 0);
    238   layout->SkipColumns(1);
    239   layout->AddView(email_label, 1, 1,
    240                   views::GridLayout::LEADING, views::GridLayout::LEADING);
    241   layout->StartRow(1, 0);
    242   layout->SkipColumns(1);
    243 
    244   return view;
    245 }
    246 
    247 views::View* ProfileChooserView::CreateCurrentProfileView(
    248     size_t avatar_to_show) {
    249   views::View* view = new views::View();
    250 
    251   views::View* card_view = CreateProfileCardView(avatar_to_show);
    252 
    253   views::LabelButton* signout_button = new views::LabelButton(
    254       this,
    255       l10n_util::GetStringUTF16(IDS_PROFILES_PROFILE_SIGNOUT_BUTTON));
    256   signout_button->SetStyle(views::Button::STYLE_BUTTON);
    257   DCHECK(!signout_current_profile_view_);
    258   signout_current_profile_view_ = signout_button;
    259 
    260   views::GridLayout* layout = new views::GridLayout(view);
    261   view->SetLayoutManager(layout);
    262 
    263   views::ColumnSet* columns = layout->AddColumnSet(0);
    264   columns->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 0,
    265                      views::GridLayout::USE_PREF, 0, 0);
    266   columns->AddPaddingColumn(0, 30);
    267   columns->AddColumn(views::GridLayout::FILL, views::GridLayout::CENTER, 1,
    268                      views::GridLayout::USE_PREF, 0, 0);
    269   columns->AddPaddingColumn(0, 10);
    270 
    271   layout->StartRow(1, 0);
    272   layout->AddView(card_view, 1, 1);
    273   layout->AddView(signout_button);
    274 
    275   return view;
    276 }
    277 
    278 views::View* ProfileChooserView::CreateOtherProfilesView(
    279     const Indexes& avatars_to_show) {
    280   views::View* view = new views::View();
    281 
    282   views::BoxLayout* layout = new views::BoxLayout(
    283       views::BoxLayout::kHorizontal, 0,
    284       views::kRelatedControlSmallVerticalSpacing,
    285       views::kRelatedButtonHSpacing);
    286   view->SetLayoutManager(layout);
    287 
    288   const int kSmallImageSide = 32;
    289   for (Indexes::const_iterator iter = avatars_to_show.begin();
    290        iter != avatars_to_show.end();
    291        ++iter) {
    292     const size_t index = *iter;
    293     views::View* image = CreateProfileImageView(
    294         avatar_menu_model_->GetItemAt(index).icon, kSmallImageSide);
    295     open_other_profile_indexes_map_[image] = index;
    296     view->AddChildView(image);
    297   }
    298 
    299   return view;
    300 }
    301 
    302 views::View* ProfileChooserView::CreateOptionsView() {
    303   users_button_view_ = new views::LabelButton(
    304       this,
    305       l10n_util::GetStringUTF16(IDS_PROFILES_PROFILE_USERS_BUTTON));
    306   users_button_view_->SetHorizontalAlignment(gfx::ALIGN_CENTER);
    307   users_button_view_->set_tag(IDS_PROFILES_PROFILE_USERS_BUTTON);
    308 
    309  guest_button_view_ = new views::LabelButton(
    310       this,
    311       l10n_util::GetStringUTF16(IDS_PROFILES_PROFILE_GUEST_BUTTON));
    312   guest_button_view_->SetHorizontalAlignment(gfx::ALIGN_CENTER);
    313   guest_button_view_->set_tag(IDS_PROFILES_PROFILE_GUEST_BUTTON);
    314 
    315   views::View* view = new views::View();
    316   views::GridLayout* layout = new views::GridLayout(view);
    317   view->SetLayoutManager(layout);
    318 
    319   views::ColumnSet* columns = layout->AddColumnSet(0);
    320   columns->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 1,
    321                      views::GridLayout::USE_PREF, 0, 0);
    322   columns->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 0,
    323                      views::GridLayout::USE_PREF, 0, 0);
    324   columns->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 1,
    325                      views::GridLayout::USE_PREF, 0, 0);
    326   columns->LinkColumnSizes(0, 2, -1);
    327 
    328   const int kButtonHeight = 40;
    329   layout->StartRow(0, 0);
    330   layout->AddView(users_button_view_, 1, 1,
    331                   views::GridLayout::FILL, views::GridLayout::FILL,
    332                   0, kButtonHeight);
    333   layout->AddView(new views::Separator(views::Separator::VERTICAL));
    334   layout->AddView(guest_button_view_, 1, 1,
    335                   views::GridLayout::FILL, views::GridLayout::FILL,
    336                   0, kButtonHeight);
    337 
    338   return view;
    339 }
    340