summaryrefslogtreecommitdiffstats
path: root/chrome/browser
diff options
context:
space:
mode:
authorsail@chromium.org <sail@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-08-11 02:33:34 +0000
committersail@chromium.org <sail@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-08-11 02:33:34 +0000
commit465862a036acee434df5d8fa85a462c75f37b8c7 (patch)
tree49899565b7499796155197e1a384ae16038627d8 /chrome/browser
parent85043a91f7201b7de4af66d9f899347c1743f40a (diff)
downloadchromium_src-465862a036acee434df5d8fa85a462c75f37b8c7.zip
chromium_src-465862a036acee434df5d8fa85a462c75f37b8c7.tar.gz
chromium_src-465862a036acee434df5d8fa85a462c75f37b8c7.tar.bz2
Multi-Profiles: Change avatar menu to bubble view
BUG= TEST=Ran on views and verified that things looked ok. Review URL: http://codereview.chromium.org/7566050 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@96306 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser')
-rw-r--r--chrome/browser/profiles/avatar_menu_model.cc4
-rw-r--r--chrome/browser/profiles/avatar_menu_model.h2
-rw-r--r--chrome/browser/profiles/avatar_menu_model_observer.h4
-rw-r--r--chrome/browser/ui/views/avatar_menu.cc199
-rw-r--r--chrome/browser/ui/views/avatar_menu.h38
-rw-r--r--chrome/browser/ui/views/avatar_menu_bubble_view.cc264
-rw-r--r--chrome/browser/ui/views/avatar_menu_bubble_view.h62
-rw-r--r--chrome/browser/ui/views/avatar_menu_button.cc20
8 files changed, 346 insertions, 247 deletions
diff --git a/chrome/browser/profiles/avatar_menu_model.cc b/chrome/browser/profiles/avatar_menu_model.cc
index 512c1ce..408728d 100644
--- a/chrome/browser/profiles/avatar_menu_model.cc
+++ b/chrome/browser/profiles/avatar_menu_model.cc
@@ -70,7 +70,7 @@ AvatarMenuModel::Item::Item(size_t model_index, const gfx::Image& icon)
AvatarMenuModel::Item::~Item() {
}
-void AvatarMenuModel::SwichToProfile(size_t index) {
+void AvatarMenuModel::SwitchToProfile(size_t index) {
const Item& item = GetItemAt(index);
FilePath path = profile_info_->GetPathOfProfileAtIndex(item.model_index);
@@ -119,7 +119,7 @@ void AvatarMenuModel::RebuildMenu() {
items_.push_back(item);
}
- observer_->OnAvatarMenuModelChanged();
+ observer_->OnAvatarMenuModelChanged(this);
}
void AvatarMenuModel::ClearMenu() {
diff --git a/chrome/browser/profiles/avatar_menu_model.h b/chrome/browser/profiles/avatar_menu_model.h
index 7de6379..b371f2b 100644
--- a/chrome/browser/profiles/avatar_menu_model.h
+++ b/chrome/browser/profiles/avatar_menu_model.h
@@ -59,7 +59,7 @@ class AvatarMenuModel : public NotificationObserver {
// model:
// Opens a Browser with the specified profile in response to the user
// selecting an item.
- void SwichToProfile(size_t index);
+ void SwitchToProfile(size_t index);
// Opens the profile settings in response to clicking the edit button next to
// an item.
void EditProfile(size_t index);
diff --git a/chrome/browser/profiles/avatar_menu_model_observer.h b/chrome/browser/profiles/avatar_menu_model_observer.h
index 0643622..e6586c3 100644
--- a/chrome/browser/profiles/avatar_menu_model_observer.h
+++ b/chrome/browser/profiles/avatar_menu_model_observer.h
@@ -5,13 +5,15 @@
#ifndef CHROME_BROWSER_PROFILES_AVATAR_MENU_MODEL_OBSERVER_H_
#define CHROME_BROWSER_PROFILES_AVATAR_MENU_MODEL_OBSERVER_H_
+class AvatarMenuModel;
+
// Delegate interface for objects that want to be notified when the
// AvatarMenuModel changes.
class AvatarMenuModelObserver {
public:
virtual ~AvatarMenuModelObserver() {}
- virtual void OnAvatarMenuModelChanged() = 0;
+ virtual void OnAvatarMenuModelChanged(AvatarMenuModel* avatar_menu_model) = 0;
};
#endif // CHROME_BROWSER_PROFILES_AVATAR_MENU_MODEL_OBSERVER_H_
diff --git a/chrome/browser/ui/views/avatar_menu.cc b/chrome/browser/ui/views/avatar_menu.cc
deleted file mode 100644
index 8fdcb24..0000000
--- a/chrome/browser/ui/views/avatar_menu.cc
+++ /dev/null
@@ -1,199 +0,0 @@
-// Copyright (c) 2011 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/avatar_menu.h"
-
-#include "base/string_number_conversions.h"
-#include "base/utf_string_conversions.h"
-#include "chrome/app/chrome_command_ids.h"
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/profiles/profile_info_cache.h"
-#include "chrome/browser/profiles/profile_manager.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/profile_menu_model.h"
-#include "grit/chromium_strings.h"
-#include "grit/generated_resources.h"
-#include "grit/theme_resources.h"
-#include "ui/base/l10n/l10n_util.h"
-#include "ui/base/resource/resource_bundle.h"
-#include "ui/gfx/canvas.h"
-#include "ui/gfx/image/image.h"
-#include "views/controls/button/image_button.h"
-#include "views/controls/button/menu_button.h"
-#include "views/controls/menu/menu_item_view.h"
-#include "views/controls/menu/submenu_view.h"
-#include "views/widget/widget.h"
-
-namespace {
-
-const int kCellWidth = 38;
-const int kCellHeight = 31;
-const int kCellPaddingX = 5;
-const int kCellPaddingY = 5;
-const int kGridMaxCol = 3;
-
-static inline int Round(double x) {
- return static_cast<int>(x + 0.5);
-}
-
-// This is an button that scales its image to fit its bounds.
-class ScaledImageButton : public views::ImageButton {
- public:
- explicit ScaledImageButton(views::ButtonListener* listener);
- virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(ScaledImageButton);
-};
-
-// A view that displays avatar icons in a grid.
-class AvatarIconGridView : public views::View, public views::ButtonListener {
- public:
- AvatarIconGridView(Profile* profile, views::MenuItemView* menu);
- virtual ~AvatarIconGridView();
-
- virtual void Layout() OVERRIDE;
- virtual gfx::Size GetPreferredSize() OVERRIDE;
- virtual void ButtonPressed(views::Button* sender,
- const views::Event& event) OVERRIDE;
-
- private:
- // Sets the profile's avatar icon to the icon at the given index.
- void SetAvatarIconToIndex(int icon_index);
-
- typedef std::map<int, views::View*> IconIndexToButtonMap;
- IconIndexToButtonMap icon_index_to_button_map_;
- Profile* profile_;
- views::MenuItemView* menu_;
-
- DISALLOW_COPY_AND_ASSIGN(AvatarIconGridView);
-};
-
-ScaledImageButton::ScaledImageButton(views::ButtonListener* listener)
- : views::ImageButton(listener) {
-}
-
-void ScaledImageButton::OnPaint(gfx::Canvas* canvas) {
- if (state() == views::CustomButton::BS_HOT)
- canvas->FillRectInt(SkColorSetARGB(20, 0, 0, 0), 0, 0, width(), height());
- else if (state() == views::CustomButton::BS_PUSHED)
- canvas->FillRectInt(SkColorSetARGB(40, 0, 0, 0), 0, 0, width(), height());
-
- const SkBitmap& icon = GetImageToPaint();
- if (icon.isNull())
- return;
-
- int dst_width;
- int dst_height;
- if (icon.width() > icon.height()) {
- dst_width = std::min(width(), icon.width());
- float scale = static_cast<float>(dst_width) /
- static_cast<float>(icon.width());
- dst_height = Round(icon.height() * scale);
- } else {
- dst_height = std::min(height(), icon.height());
- float scale = static_cast<float>(dst_height) /
- static_cast<float>(icon.height());
- dst_width = Round(icon.width() * scale);
- }
- int dst_x = (width() - dst_width) / 2;
- int dst_y = (height() - dst_height) / 2;
- canvas->DrawBitmapInt(icon, 0, 0, icon.width(), icon.height(),
- dst_x, dst_y, dst_width, dst_height, false);
-}
-
-AvatarIconGridView::AvatarIconGridView(Profile* profile,
- views::MenuItemView* menu)
- : profile_(profile),
- menu_(menu) {
- ResourceBundle& rb = ResourceBundle::GetSharedInstance();
- for (size_t i = 0; i < ProfileInfoCache::GetDefaultAvatarIconCount(); ++i) {
- int resource_id =
- ProfileInfoCache::GetDefaultAvatarIconResourceIDAtIndex(i);
- views::ImageButton* button = new ScaledImageButton(this);
- button->SetImage(views::CustomButton::BS_NORMAL,
- rb.GetImageNamed(resource_id));
- button->SetAccessibleName(
- l10n_util::GetStringFUTF16Int(IDS_NUMBERED_AVATAR_NAME,
- static_cast<int>(i + 1)));
- AddChildView(button);
- icon_index_to_button_map_[i] = button;
- }
-}
-
-AvatarIconGridView::~AvatarIconGridView() {
-}
-
-void AvatarIconGridView::Layout() {
- for (IconIndexToButtonMap::const_iterator it =
- icon_index_to_button_map_.begin();
- it != icon_index_to_button_map_.end(); ++it) {
- views::View* view = it->second;
- int icon_index = it->first;
- int col = icon_index % kGridMaxCol;
- int row = icon_index / kGridMaxCol;
- int x = col * kCellWidth + col * kCellPaddingX;
- int y = row * kCellHeight + row * kCellPaddingY;
- view->SetBounds(x, y, kCellWidth, kCellHeight);
- }
-}
-
-gfx::Size AvatarIconGridView::GetPreferredSize() {
- int cols = kGridMaxCol;
- int rows = ProfileInfoCache::GetDefaultAvatarIconCount() / cols;
- if (ProfileInfoCache::GetDefaultAvatarIconCount() % cols != 0)
- rows++;
- return gfx::Size(cols * kCellWidth + (cols - 1) * kCellPaddingX,
- rows * kCellHeight + (rows - 1) * kCellPaddingY);
-}
-
-void AvatarIconGridView::ButtonPressed(views::Button* sender,
- const views::Event& event) {
- for (IconIndexToButtonMap::const_iterator it =
- icon_index_to_button_map_.begin();
- it != icon_index_to_button_map_.end(); ++it) {
- if (it->second == sender) {
- SetAvatarIconToIndex(it->first);
- menu_->Cancel();
- return;
- }
- }
- NOTREACHED();
-}
-
-void AvatarIconGridView::SetAvatarIconToIndex(int icon_index) {
- ProfileInfoCache& cache =
- g_browser_process->profile_manager()->GetProfileInfoCache();
- size_t profile_index = cache.GetIndexOfProfileWithPath(profile_->GetPath());
- cache.SetAvatarIconOfProfileAtIndex(profile_index, icon_index);
-}
-
-} // namespace
-
-AvatarMenu::AvatarMenu(ui::MenuModel* model, Profile* profile)
- : MenuModelAdapter(model),
- profile_(profile) {
- root_.reset(new views::MenuItemView(this));
- BuildMenu(root_.get());
- views::MenuItemView* item =
- root_->GetMenuItemByID(ProfileMenuModel::COMMAND_CHOOSE_AVATAR_ICON);
- item->AddChildView(new AvatarIconGridView(profile_, root_.get()));
-}
-
-AvatarMenu::~AvatarMenu() {
-}
-
-void AvatarMenu::RunMenu(views::MenuButton* host) {
- // Up the ref count while the menu is displaying. This way if the window is
- // deleted while we're running we won't prematurely delete the menu.
- // TODO(sky): fix this, the menu should really take ownership of the menu
- // (57890).
- scoped_refptr<AvatarMenu> dont_delete_while_running(this);
- gfx::Point screen_loc;
- views::View::ConvertPointToScreen(host, &screen_loc);
- gfx::Rect bounds(screen_loc, host->size());
- root_->RunMenuAt(host->GetWidget(), host, bounds,
- views::MenuItemView::TOPLEFT, true);
-}
diff --git a/chrome/browser/ui/views/avatar_menu.h b/chrome/browser/ui/views/avatar_menu.h
deleted file mode 100644
index 38d4c6a..0000000
--- a/chrome/browser/ui/views/avatar_menu.h
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright (c) 2011 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 CHROME_BROWSER_UI_VIEWS_AVATAR_MENU_H_
-#define CHROME_BROWSER_UI_VIEWS_AVATAR_MENU_H_
-#pragma once
-
-#include <map>
-#include <utility>
-
-#include "base/memory/ref_counted.h"
-#include "base/memory/scoped_ptr.h"
-#include "ui/base/models/menu_model.h"
-#include "views/controls/menu/menu_model_adapter.h"
-
-class Profile;
-
-class AvatarMenu : public base::RefCounted<AvatarMenu>,
- public views::MenuModelAdapter {
- public:
- AvatarMenu(ui::MenuModel* model, Profile* profile);
- virtual ~AvatarMenu();
-
- // Shows the menu relative to the specified view.
- void RunMenu(views::MenuButton* host);
-
- private:
- // The views menu.
- scoped_ptr<views::MenuItemView> root_;
-
- // Profile the menu is being shown for.
- Profile* profile_;
-
- DISALLOW_COPY_AND_ASSIGN(AvatarMenu);
-};
-
-#endif // CHROME_BROWSER_UI_VIEWS_AVATAR_MENU_H_
diff --git a/chrome/browser/ui/views/avatar_menu_bubble_view.cc b/chrome/browser/ui/views/avatar_menu_bubble_view.cc
new file mode 100644
index 0000000..1ce7ce3
--- /dev/null
+++ b/chrome/browser/ui/views/avatar_menu_bubble_view.cc
@@ -0,0 +1,264 @@
+// Copyright (c) 2011 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/avatar_menu_bubble_view.h"
+
+#include "base/utf_string_conversions.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/profiles/avatar_menu_model.h"
+#include "chrome/browser/profiles/profile_info_cache.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "grit/generated_resources.h"
+#include "grit/theme_resources.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/gfx/canvas.h"
+#include "ui/gfx/font.h"
+#include "ui/gfx/image/image.h"
+#include "views/controls/button/image_button.h"
+
+namespace {
+
+const int kBubbleViewMinWidth = 175;
+const int kBubbleViewMaxWidth = 800;
+const int kItemHeight = 32;
+const int kItemMarginY = 8;
+const int kIconWidth = 38;
+const int kIconMarginX = 6;
+const int kEditProfileButtonMarginX = 8;
+
+inline int Round(double x) {
+ return static_cast<int>(x + 0.5);
+}
+
+gfx::Rect GetCenteredAndScaledRect(int src_width, int src_height,
+ int dst_x, int dst_y,
+ int dst_width, int dst_height) {
+ int scaled_width;
+ int scaled_height;
+ if (src_width > src_height) {
+ scaled_width = std::min(src_width, dst_width);
+ float scale = static_cast<float>(scaled_width) /
+ static_cast<float>(src_width);
+ scaled_height = Round(src_height * scale);
+ } else {
+ scaled_height = std::min(src_height, dst_height);
+ float scale = static_cast<float>(scaled_height) /
+ static_cast<float>(src_height);
+ scaled_width = Round(src_width * scale);
+ }
+ int x = dst_x + (dst_width - scaled_width) / 2;
+ int y = dst_y + (dst_height - scaled_height) / 2;
+ return gfx::Rect(x, y, scaled_width, scaled_height);
+}
+
+class ProfileItemView : public views::CustomButton {
+ public:
+ ProfileItemView(const AvatarMenuModel::Item& item,
+ views::ButtonListener* listener)
+ : views::CustomButton(listener),
+ item_(item) {
+ }
+
+ virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE {
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+
+ // Draw the profile icon on the left.
+ SkBitmap profile_icon = item_.icon;
+ gfx::Rect profile_icon_rect = GetCenteredAndScaledRect(
+ profile_icon.width(), profile_icon.height(),
+ 0, 0, kIconWidth, height());
+ canvas->DrawBitmapInt(profile_icon, 0, 0, profile_icon.width(),
+ profile_icon.height(), profile_icon_rect.x(),
+ profile_icon_rect.y(), profile_icon_rect.width(),
+ profile_icon_rect.height(), false);
+
+ // If this profile is selected then draw a check mark on the bottom right
+ // of the profile icon.
+ if (item_.active) {
+ SkBitmap check_icon = rb.GetImageNamed(IDR_PROFILE_SELECTED);
+ int y = profile_icon_rect.bottom() - check_icon.height();
+ int x = profile_icon_rect.right() - check_icon.width() + 2;
+ canvas->DrawBitmapInt(check_icon, 0, 0, check_icon.width(),
+ check_icon.height(), x, y, check_icon.width(),
+ check_icon.height(), false);
+ }
+
+ // Draw the profile name to the right of the profile icon.
+ int name_x = profile_icon_rect.right() + kIconMarginX;
+ canvas->DrawStringInt(item_.name, rb.GetFont(ResourceBundle::BaseFont),
+ GetNameColor(), name_x, 0, width() - name_x,
+ height());
+ }
+
+ virtual gfx::Size GetPreferredSize() OVERRIDE {
+ gfx::Font font = ResourceBundle::GetSharedInstance().GetFont(
+ ResourceBundle::BaseFont);
+ int title_width = font.GetStringWidth(item_.name);
+ return gfx::Size(kIconWidth + kIconMarginX + title_width, kItemHeight);
+ }
+
+ private:
+ SkColor GetNameColor() {
+ bool normal = state() != views::CustomButton::BS_PUSHED &&
+ state() != views::CustomButton::BS_HOT;
+ if (item_.active)
+ return normal ? SkColorSetRGB(30, 30, 30) : SkColorSetRGB(0, 0, 0);
+ return normal ? SkColorSetRGB(128, 128, 128) : SkColorSetRGB(64, 64, 64);
+ }
+
+ AvatarMenuModel::Item item_;
+};
+
+} // namespace
+
+class EditProfileButton : public views::ImageButton {
+ public:
+ EditProfileButton(size_t profile_index, views::ButtonListener* listener)
+ : views::ImageButton(listener),
+ profile_index_(profile_index) {
+ }
+
+ size_t profile_index() {
+ return profile_index_;
+ }
+
+ private:
+ size_t profile_index_;
+};
+
+AvatarMenuBubbleView::AvatarMenuBubbleView(Browser* browser)
+ : add_profile_link_(NULL),
+ browser_(browser),
+ edit_profile_button_(NULL) {
+ avatar_menu_model_.reset(new AvatarMenuModel(
+ &g_browser_process->profile_manager()->GetProfileInfoCache(),
+ this, browser_));
+}
+
+AvatarMenuBubbleView::~AvatarMenuBubbleView() {
+}
+
+gfx::Size AvatarMenuBubbleView::GetPreferredSize() {
+ int max_width = 0;
+ int total_height = 0;
+ for (size_t i = 0; i < item_views_.size(); ++i) {
+ gfx::Size size = item_views_[i]->GetPreferredSize();
+ if (i == edit_profile_button_->profile_index()) {
+ size.set_width(size.width() +
+ edit_profile_button_->GetPreferredSize().width() +
+ kEditProfileButtonMarginX);
+ }
+
+ max_width = std::max(max_width, size.width());
+ total_height += size.height() + kItemMarginY;
+ }
+
+ gfx::Size add_profile_size = add_profile_link_->GetPreferredSize();
+ max_width = std::max(max_width,
+ add_profile_size.width() + kIconWidth + kIconMarginX);
+ total_height += add_profile_link_->GetPreferredSize().height();
+
+ int total_width = std::min(std::max(max_width, kBubbleViewMinWidth),
+ kBubbleViewMaxWidth);
+ return gfx::Size(total_width, total_height);
+}
+
+void AvatarMenuBubbleView::Layout() {
+ int y = 0;
+ for (size_t i = 0; i < item_views_.size(); ++i) {
+ views::CustomButton* item_view = item_views_[i];
+ int item_height = item_view->GetPreferredSize().height();
+ int item_width = width();
+
+ if (i == edit_profile_button_->profile_index()) {
+ gfx::Size edit_size = edit_profile_button_->GetPreferredSize();
+ edit_profile_button_->SetBounds(width() - edit_size.width(), y,
+ edit_size.width(), item_height);
+ item_width -= edit_size.width() + kEditProfileButtonMarginX;
+ }
+
+ item_view->SetBounds(0, y, item_width, item_height);
+ y += item_height + kItemMarginY;
+ }
+
+ add_profile_link_->SetBounds(kIconWidth + kIconMarginX, y, width(),
+ add_profile_link_->GetPreferredSize().height());
+}
+
+void AvatarMenuBubbleView::ButtonPressed(views::Button* sender,
+ const views::Event& event) {
+ if (sender == edit_profile_button_) {
+ avatar_menu_model_->EditProfile(edit_profile_button_->profile_index());
+ } else {
+ for (size_t i = 0; i < item_views_.size(); ++i) {
+ if (sender == item_views_[i]) {
+ avatar_menu_model_->SwitchToProfile(i);
+ break;
+ }
+ }
+ }
+}
+
+void AvatarMenuBubbleView::LinkClicked(views::Link* source, int event_flags) {
+ DCHECK_EQ(source, add_profile_link_);
+ avatar_menu_model_->AddNewProfile();
+}
+
+void AvatarMenuBubbleView::BubbleClosing(Bubble* bubble,
+ bool closed_by_escape) {
+}
+
+bool AvatarMenuBubbleView::CloseOnEscape() {
+ return true;
+}
+
+bool AvatarMenuBubbleView::FadeInOnShow() {
+ return false;
+}
+
+void AvatarMenuBubbleView::OnAvatarMenuModelChanged(
+ AvatarMenuModel* avatar_menu_model) {
+ // Unset all our child view references and call RemoveAllChildViews() which
+ // will actually delete them.
+ add_profile_link_ = NULL;
+ edit_profile_button_ = NULL;
+ item_views_.clear();
+ RemoveAllChildViews(true);
+
+ for (size_t i = 0; i < avatar_menu_model->GetNumberOfItems(); ++i) {
+ const AvatarMenuModel::Item& item = avatar_menu_model->GetItemAt(i);
+ ProfileItemView* item_view = new ProfileItemView(item, this);
+ item_view->SetAccessibleName(l10n_util::GetStringFUTF16(
+ IDS_PROFILES_SWITCH_TO_PROFILE_ACCESSIBLE_NAME, item.name));
+ AddChildView(item_view);
+ item_views_.push_back(item_view);
+
+ if (item.active) {
+ DCHECK(!edit_profile_button_);
+ edit_profile_button_ = new EditProfileButton(i, this);
+ edit_profile_button_->SetAccessibleName(l10n_util::GetStringFUTF16(
+ IDS_PROFILES_CUSTOMIZE_PROFILE_ACCESSIBLE_NAME, item.name));
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+ edit_profile_button_->SetImage(views::CustomButton::BS_NORMAL,
+ rb.GetImageNamed(IDR_PROFILE_EDIT));
+ edit_profile_button_->SetImage(views::CustomButton::BS_HOT,
+ rb.GetImageNamed(IDR_PROFILE_EDIT_HOVER));
+ edit_profile_button_->SetImage(views::CustomButton::BS_PUSHED,
+ rb.GetImageNamed(IDR_PROFILE_EDIT_PRESSED));
+ edit_profile_button_->SetImageAlignment(views::ImageButton::ALIGN_CENTER,
+ views::ImageButton::ALIGN_MIDDLE);
+ AddChildView(edit_profile_button_);
+ }
+ }
+
+ add_profile_link_ = new views::Link(UTF16ToWide(
+ l10n_util::GetStringUTF16(IDS_PROFILES_CREATE_NEW_PROFILE_LINK)));
+ add_profile_link_->SetHorizontalAlignment(views::Label::ALIGN_LEFT);
+ add_profile_link_->SetNormalColor(SkColorSetRGB(0, 0x79, 0xda));
+ AddChildView(add_profile_link_);
+
+ PreferredSizeChanged();
+}
diff --git a/chrome/browser/ui/views/avatar_menu_bubble_view.h b/chrome/browser/ui/views/avatar_menu_bubble_view.h
new file mode 100644
index 0000000..752098c
--- /dev/null
+++ b/chrome/browser/ui/views/avatar_menu_bubble_view.h
@@ -0,0 +1,62 @@
+// Copyright (c) 2011 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 CHROME_BROWSER_UI_VIEWS_AVATAR_MENU_BUBBLE_VIEW_H_
+#define CHROME_BROWSER_UI_VIEWS_AVATAR_MENU_BUBBLE_VIEW_H_
+#pragma once
+
+#include <vector>
+
+#include "chrome/browser/profiles/avatar_menu_model_observer.h"
+#include "chrome/browser/ui/views/bubble/bubble.h"
+#include "views/controls/button/custom_button.h"
+#include "views/controls/link.h"
+#include "views/controls/link_listener.h"
+
+class AvatarMenuModel;
+class Browser;
+class EditProfileButton;
+
+// This bubble view is displayed when the user clicks on the avatar button.
+// It displays a list of profiles and allows users to switch between profiles.
+class AvatarMenuBubbleView : public views::View,
+ public views::ButtonListener,
+ public views::LinkListener,
+ public BubbleDelegate,
+ public AvatarMenuModelObserver {
+ public:
+ explicit AvatarMenuBubbleView(Browser* browser);
+ ~AvatarMenuBubbleView();
+
+ // views::View implementation.
+ virtual gfx::Size GetPreferredSize() OVERRIDE;
+ virtual void Layout() OVERRIDE;
+
+ // views::ButtonListener implementation.
+ virtual void ButtonPressed(views::Button* sender,
+ const views::Event& event) OVERRIDE;
+
+ // views::LinkListener implementation.
+ virtual void LinkClicked(views::Link* source, int event_flags) OVERRIDE;
+
+ // BubbleDelegate implementation.
+ virtual void BubbleClosing(Bubble* bubble, bool closed_by_escape) OVERRIDE;
+ virtual bool CloseOnEscape() OVERRIDE;
+ virtual bool FadeInOnShow() OVERRIDE;
+
+ // AvatarMenuModelObserver implementation.
+ virtual void OnAvatarMenuModelChanged(
+ AvatarMenuModel* avatar_menu_model) OVERRIDE;
+
+ private:
+ views::Link* add_profile_link_;
+ scoped_ptr<AvatarMenuModel> avatar_menu_model_;
+ Browser* browser_;
+ EditProfileButton* edit_profile_button_;
+ std::vector<views::CustomButton*> item_views_;
+
+ DISALLOW_COPY_AND_ASSIGN(AvatarMenuBubbleView);
+};
+
+#endif // CHROME_BROWSER_UI_VIEWS_AVATAR_MENU_BUBBLE_VIEW_H_
diff --git a/chrome/browser/ui/views/avatar_menu_button.cc b/chrome/browser/ui/views/avatar_menu_button.cc
index efe86a8..7b99ee0 100644
--- a/chrome/browser/ui/views/avatar_menu_button.cc
+++ b/chrome/browser/ui/views/avatar_menu_button.cc
@@ -6,7 +6,8 @@
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/profile_menu_model.h"
-#include "chrome/browser/ui/views/avatar_menu.h"
+#include "chrome/browser/ui/views/avatar_menu_bubble_view.h"
+#include "chrome/browser/ui/views/frame/browser_view.h"
#include "ui/gfx/canvas_skia.h"
#include "views/widget/widget.h"
@@ -62,9 +63,16 @@ void AvatarMenuButton::RunMenu(views::View* source, const gfx::Point& pt) {
if (!has_menu_)
return;
- menu_model_.reset(new ProfileMenuModel(browser_));
- // The avatar menu will automatically delete itself when done.
- AvatarMenu* avatar_menu =
- new AvatarMenu(menu_model_.get(), browser_->profile());
- avatar_menu->RunMenu(this);
+ BrowserView* browser_view = BrowserView::GetBrowserViewForNativeWindow(
+ browser_->window()->GetNativeHandle());
+
+ gfx::Point origin;
+ views::View::ConvertPointToScreen(this, &origin);
+ gfx::Rect bounds(0, 0, width(), height());
+ bounds.set_origin(origin);
+
+ AvatarMenuBubbleView* bubble_view = new AvatarMenuBubbleView(browser_);
+ // Bubble::Show() takes ownership of the view.
+ Bubble::Show(browser_view->GetWidget(), bounds, BubbleBorder::TOP_LEFT,
+ bubble_view, bubble_view);
}