Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(8371)

Unified Diff: chrome/browser/ui/views/avatar_menu_bubble_view.cc

Issue 8070021: Update look of profile menu (Views) (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: address review comments Created 9 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « chrome/browser/ui/views/avatar_menu_bubble_view.h ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: chrome/browser/ui/views/avatar_menu_bubble_view.cc
diff --git a/chrome/browser/ui/views/avatar_menu_bubble_view.cc b/chrome/browser/ui/views/avatar_menu_bubble_view.cc
index 57deb8f723857ea5add7f7f0e71e49f292e5ca6e..2b7df41437ad1ed4d79d052c442dc1fa0433da86 100644
--- a/chrome/browser/ui/views/avatar_menu_bubble_view.cc
+++ b/chrome/browser/ui/views/avatar_menu_bubble_view.cc
@@ -15,19 +15,20 @@
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/gfx/canvas.h"
+#include "ui/gfx/canvas_skia.h"
#include "ui/gfx/font.h"
#include "ui/gfx/image/image.h"
#include "views/controls/button/image_button.h"
+#include "views/controls/image_view.h"
+#include "views/controls/label.h"
namespace {
-const int kBubbleViewMinWidth = 175;
-const int kBubbleViewMaxWidth = 800;
-const int kItemHeight = 32;
-const int kItemMarginY = 8;
+const int kItemHeight = 44;
+const int kItemMarginY = 4;
const int kIconWidth = 38;
const int kIconMarginX = 6;
-const int kEditProfileButtonMarginX = 8;
+const int kSeparatorPaddingY = 5;
inline int Round(double x) {
return static_cast<int>(x + 0.5);
@@ -54,85 +55,203 @@ gfx::Rect GetCenteredAndScaledRect(int src_width, int src_height,
return gfx::Rect(x, y, scaled_width, scaled_height);
}
-class ProfileItemView : public views::CustomButton {
+// Delegate to callback when the highlight state of a control changes.
+class HighlightDelegate {
public:
- ProfileItemView(const AvatarMenuModel::Item& item,
- views::ButtonListener* listener)
- : views::CustomButton(listener),
- item_(item) {
+ virtual void OnHighlightStateChanged() = 0;
+};
+
+// A custom Link control that forwards highlight state changes. We need to do
+// this to make sure that the ProfileItemView looks highlighted even when
+// the mouse is over this link.
+class EditProfileLink : public views::Link {
+ public:
+ explicit EditProfileLink(const string16& title,
+ HighlightDelegate* delegate)
+ : views::Link(UTF16ToWideHack(title)),
+ delegate_(delegate),
+ state_(views::CustomButton::BS_NORMAL) {
}
- virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE {
- ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+ virtual void OnMouseEntered(const views::MouseEvent& event) OVERRIDE {
+ views::Link::OnMouseEntered(event);
+ state_ = views::CustomButton::BS_HOT;
+ delegate_->OnHighlightStateChanged();
+ }
+
+ virtual void OnMouseExited(const views::MouseEvent& event) OVERRIDE {
+ views::Link::OnMouseExited(event);
+ state_ = views::CustomButton::BS_NORMAL;
+ delegate_->OnHighlightStateChanged();
+ }
+
+ views::CustomButton::ButtonState state() { return state_; }
+
+ private:
+ HighlightDelegate* delegate_;
+ views::CustomButton::ButtonState state_;
+};
+
+// A custom image view that ignores mouse events so that the parent can receive
+// them them instead.
+class ProfileImageView : public views::ImageView {
+ public:
+ virtual bool HitTest(const gfx::Point& l) const OVERRIDE {
+ return false;
+ }
+};
- // Draw the profile icon on the left.
+// Control that shows information about a single profile.
+class ProfileItemView : public views::CustomButton,
+ public HighlightDelegate {
+ public:
+ ProfileItemView(const AvatarMenuModel::Item& item,
+ views::ButtonListener* switch_profile_listener,
+ views::LinkListener* edit_profile_listener)
+ : views::CustomButton(switch_profile_listener),
+ item_(item) {
+ image_view_ = new ProfileImageView();
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);
+ SkBitmap badged_icon = GetBadgedIcon(profile_icon);
+ image_view_->SetImage(&badged_icon);
+ } else {
+ image_view_->SetImage(&profile_icon);
}
+ AddChildView(image_view_);
- // 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());
+ // Add a label to show the profile name.
+ name_label_ = new views::Label(UTF16ToWideHack(item_.name));
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+ gfx::Font base_font = rb.GetFont(ResourceBundle::BaseFont);
+ int style = item_.active ? gfx::Font::BOLD : 0;
+ const int kNameFontDelta = 1;
+ name_label_->SetFont(base_font.DeriveFont(kNameFontDelta, style));
+ name_label_->SetHorizontalAlignment(views::Label::ALIGN_LEFT);
+ AddChildView(name_label_);
+
+ // Add a label to show the sync state.
+ sync_state_label_ = new views::Label(UTF16ToWideHack(item_.sync_state));
+ const int kStateFontDelta = -1;
+ sync_state_label_->SetFont(base_font.DeriveFont(kStateFontDelta));
+ sync_state_label_->SetHorizontalAlignment(views::Label::ALIGN_LEFT);
+ sync_state_label_->SetEnabled(false);
+ AddChildView(sync_state_label_);
+
+ // Add an edit profile link.
+ edit_link_ = new EditProfileLink(
+ l10n_util::GetStringUTF16(IDS_PROFILES_EDIT_PROFILE_LINK), this);
+ edit_link_->set_listener(edit_profile_listener);
+ edit_link_->SetHorizontalAlignment(views::Label::ALIGN_LEFT);
+ edit_link_->SetNormalColor(SkColorSetRGB(0, 0x79, 0xda));
+ AddChildView(edit_link_);
+
+ OnHighlightStateChanged();
}
- 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);
+ virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE {
+ if (IsHighlighted()) {
+ canvas->FillRectInt(SkColorSetRGB(0xe3, 0xed, 0xf6), 0, 0,
+ width(), height());
+ }
}
- 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);
+ virtual gfx::Size GetPreferredSize() OVERRIDE {
+ int width = std::max(name_label_->GetPreferredSize().width(),
+ sync_state_label_->GetPreferredSize().width());
+ width = std::max(edit_link_->GetPreferredSize().width(),
+ width);
+ return gfx::Size(kIconWidth + kIconMarginX + width, kItemHeight);
}
- AvatarMenuModel::Item item_;
-};
+ virtual void Layout() OVERRIDE {
+ // Profile icon.
+ const SkBitmap& icon = image_view_->GetImage();
+ gfx::Rect icon_rect = GetCenteredAndScaledRect(
+ icon.width(), icon.height(), 0, 0, kIconWidth, height());
+ image_view_->SetBoundsRect(icon_rect);
+
+ int label_x = icon_rect.right() + kIconMarginX;
+ int max_label_width = width() - label_x;
+ gfx::Size name_size = name_label_->GetPreferredSize();
+ name_size.set_width(std::min(name_size.width(), max_label_width));
+ gfx::Size state_size = sync_state_label_->GetPreferredSize();
+ state_size.set_width(std::min(state_size.width(), max_label_width));
+ gfx::Size edit_size = edit_link_->GetPreferredSize();
+ edit_size.set_width(std::min(edit_size.width(), max_label_width));
+
+ const int kNameStatePaddingY = 2;
+ int labels_height = name_size.height() + kNameStatePaddingY +
+ std::max(state_size.height(), edit_size.height());
+ int y = (height() - labels_height) / 2;
+ name_label_->SetBounds(label_x, y, name_size.width(), name_size.height());
+
+ int bottom = y + labels_height;
+ sync_state_label_->SetBounds(label_x, bottom - state_size.height(),
+ state_size.width(), state_size.height());
+ // The edit link overlaps the sync state label.
+ edit_link_->SetBounds(label_x, bottom - edit_size.height(),
+ edit_size.width(), edit_size.height());
+ }
-} // namespace
+ virtual void OnHighlightStateChanged() OVERRIDE {
+ bool show_edit = IsHighlighted() && item_.active;
+ sync_state_label_->SetVisible(!show_edit);
+ edit_link_->SetVisible(show_edit);
+ SchedulePaint();
+ }
-class EditProfileButton : public views::ImageButton {
- public:
- EditProfileButton(size_t profile_index, views::ButtonListener* listener)
- : views::ImageButton(listener),
- profile_index_(profile_index) {
+ virtual void OnMouseEntered(const views::MouseEvent& event) OVERRIDE {
+ views::CustomButton::OnMouseEntered(event);
+ OnHighlightStateChanged();
}
- size_t profile_index() {
- return profile_index_;
+ virtual void OnMouseExited(const views::MouseEvent& event) OVERRIDE {
+ views::CustomButton::OnMouseExited(event);
+ OnHighlightStateChanged();
}
+ EditProfileLink* edit_link() { return edit_link_; }
+ const AvatarMenuModel::Item& item() { return item_; }
+
private:
- size_t profile_index_;
+ bool IsHighlighted() {
+ return state() == views::CustomButton::BS_PUSHED ||
+ state() == views::CustomButton::BS_HOT ||
+ edit_link_->state() == views::CustomButton::BS_PUSHED ||
+ edit_link_->state() == views::CustomButton::BS_HOT;
+ }
+
+ SkBitmap GetBadgedIcon(const SkBitmap& icon) {
+ gfx::Rect icon_rect = GetCenteredAndScaledRect(
+ icon.width(), icon.height(), 0, 0, kIconWidth, kItemHeight);
+
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+ SkBitmap badge = rb.GetImageNamed(IDR_PROFILE_SELECTED);
+ const float kBadgeOverlapRatioX = 1.0f / 5.0f;
+ int width = icon_rect.width() + badge.width() * kBadgeOverlapRatioX;
+ const float kBadgeOverlapRatioY = 1.0f / 3.0f;
+ int height = icon_rect.height() + badge.height() * kBadgeOverlapRatioY;
+
+ gfx::CanvasSkia canvas(width, height, false);
+ canvas.DrawBitmapInt(icon, 0, 0, icon.width(), icon.height(), 0, 0,
+ icon_rect.width(), icon_rect.height(), true);
+ canvas.DrawBitmapInt(badge, width - badge.width(), height - badge.height());
+ return canvas.ExtractBitmap();
+ }
+
+ EditProfileLink* edit_link_;
+ views::ImageView* image_view_;
+ AvatarMenuModel::Item item_;
+ views::Label* name_label_;
+ views::Label* sync_state_label_;
};
+} // namespace
+
AvatarMenuBubbleView::AvatarMenuBubbleView(Browser* browser)
: add_profile_link_(NULL),
- browser_(browser),
- edit_profile_button_(NULL) {
+ browser_(browser) {
avatar_menu_model_.reset(new AvatarMenuModel(
&g_browser_process->profile_manager()->GetProfileInfoCache(),
this, browser_));
@@ -148,21 +267,20 @@ gfx::Size AvatarMenuBubbleView::GetPreferredSize() {
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;
}
+ total_height += kSeparatorPaddingY * 2 +
+ separator_->GetPreferredSize().height();
+
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();
+ const int kBubbleViewMaxWidth = 800;
+ const int kBubbleViewMinWidth = 175;
int total_width = std::min(std::max(max_width, kBubbleViewMinWidth),
kBubbleViewMaxWidth);
return gfx::Size(total_width, total_height);
@@ -174,39 +292,45 @@ void AvatarMenuBubbleView::Layout() {
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;
}
+ y += kSeparatorPaddingY;
+ int separator_height = separator_->GetPreferredSize().height();
+ separator_->SetBounds(0, y, width(), separator_height);
+ y += kSeparatorPaddingY + separator_height;
+
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]) {
+ for (size_t i = 0; i < item_views_.size(); ++i) {
+ ProfileItemView* item_view = static_cast<ProfileItemView*>(item_views_[i]);
+ if (sender == item_view) {
+ // Clicking on the active profile shouldn't do anything.
+ if (!item_view->item().active)
avatar_menu_model_->SwitchToProfile(i);
- break;
- }
+ break;
}
}
}
void AvatarMenuBubbleView::LinkClicked(views::Link* source, int event_flags) {
- DCHECK_EQ(source, add_profile_link_);
- avatar_menu_model_->AddNewProfile();
+ if (source == add_profile_link_) {
+ avatar_menu_model_->AddNewProfile();
+ return;
+ }
+
+ for (size_t i = 0; i < item_views_.size(); ++i) {
+ ProfileItemView* item_view = static_cast<ProfileItemView*>(item_views_[i]);
+ if (source == item_view->edit_link()) {
+ avatar_menu_model_->EditProfile(i);
+ return;
+ }
+ }
}
void AvatarMenuBubbleView::BubbleClosing(Bubble* bubble,
@@ -226,36 +350,21 @@ void AvatarMenuBubbleView::OnAvatarMenuModelChanged(
// 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);
+ ProfileItemView* item_view = new ProfileItemView(item, this, 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_);
- }
}
+ separator_ = new views::Separator();
+ AddChildView(separator_);
+
add_profile_link_ = new views::Link(UTF16ToWide(
l10n_util::GetStringUTF16(IDS_PROFILES_CREATE_NEW_PROFILE_LINK)));
add_profile_link_->set_listener(this);
« no previous file with comments | « chrome/browser/ui/views/avatar_menu_bubble_view.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698