// 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/chromeos/login/user_view.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/chromeos/login/helper.h"
#include "chrome/browser/chromeos/login/rounded_rect_painter.h"
#include "chrome/browser/chromeos/login/rounded_view.h"
#include "chrome/browser/chromeos/view_ids.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/canvas_skia.h"
#include "ui/gfx/gtk_util.h"
#include "ui/gfx/rect.h"
#include "views/background.h"
#include "views/controls/button/text_button.h"
#include "views/controls/image_view.h"
#include "views/controls/label.h"
#include "views/controls/link.h"
#include "views/painter.h"
namespace {
// Background color and corner radius of the login status label and
// signout button.
const SkColor kSignoutBackgroundColor = 0xFF007700;
const int kSignoutBackgroundCornerRadius = 4;
// Horiz/Vert insets for Signout view.
const int kSignoutViewHorizontalInsets = 10;
const int kSignoutViewVerticalInsets = 5;
const int kMinControlHeight = 16;
// Padding between remove button and top right image corner.
const int kRemoveButtonPadding = 3;
// Draws green-ish background for signout view with
// rounded corners at the bottom.
class SignoutBackgroundPainter : public views::Painter {
virtual void Paint(int w, int h, gfx::Canvas* canvas) {
SkRect rect = {0, 0, w, h};
SkPath path;
path.addRoundRect(rect,
kSignoutBackgroundCornerRadius, kSignoutBackgroundCornerRadius);
SkPaint paint;
paint.setStyle(SkPaint::kFill_Style);
paint.setFlags(SkPaint::kAntiAlias_Flag);
paint.setColor(kSignoutBackgroundColor);
canvas->AsCanvasSkia()->drawPath(path, paint);
}
};
} // namespace
namespace chromeos {
using login::kBackgroundColor;
using login::kTextColor;
using login::kUserImageSize;
// The view that shows the Sign out button below the user's image.
class SignoutView : public views::View {
public:
explicit SignoutView(views::LinkController* link_controller) {
ResourceBundle& rb = ResourceBundle::GetSharedInstance();
const gfx::Font& font = rb.GetFont(ResourceBundle::SmallFont);
active_user_label_ = new views::Label(
UTF16ToWide(l10n_util::GetStringUTF16(IDS_SCREEN_LOCK_ACTIVE_USER)));
active_user_label_->SetFont(font);
active_user_label_->SetColor(kTextColor);
signout_link_ = new views::Link(
UTF16ToWide(l10n_util::GetStringUTF16(IDS_SCREEN_LOCK_SIGN_OUT)));
signout_link_->SetController(link_controller);
signout_link_->SetFont(font);
signout_link_->SetColor(kTextColor);
signout_link_->SetFocusable(true);
signout_link_->SetHighlightedColor(kTextColor);
signout_link_->SetDisabledColor(kTextColor);
signout_link_->SetNormalColor(kTextColor);
signout_link_->SetID(VIEW_ID_SCREEN_LOCKER_SIGNOUT_LINK);
AddChildView(active_user_label_);
AddChildView(signout_link_);
set_background(views::Background::CreateBackgroundPainter(
true, new SignoutBackgroundPainter()));
}
// views::View overrides.
virtual void Layout() {
gfx::Size label = active_user_label_->GetPreferredSize();
gfx::Size button = signout_link_->GetPreferredSize();
active_user_label_->SetBounds(
kSignoutViewHorizontalInsets, (height() - label.height()) / 2,
label.width(), label.height());
signout_link_->SetBounds(
width() - button.width() - kSignoutViewHorizontalInsets,
(height() - button.height()) / 2,
button.width(), button.height());
}
virtual gfx::Size GetPreferredSize() {
gfx::Size label = active_user_label_->GetPreferredSize();
gfx::Size button = signout_link_->GetPreferredSize();
int width =
label.width() + button.width() + 2 * kSignoutViewHorizontalInsets;
int height =
std::max(kMinControlHeight, std::max(label.height(), button.height())) +
kSignoutViewVerticalInsets * 2;
return gfx::Size(width, height);
}
views::Link* signout_link() { return signout_link_; }
private:
views::Label* active_user_label_;
views::Link* signout_link_;
DISALLOW_COPY_AND_ASSIGN(SignoutView);
};
class RemoveButton : public views::TextButton {
public:
RemoveButton(views::ButtonListener* listener,
const SkBitmap& icon,
const std::wstring& text,
const gfx::Point& top_right)
: views::TextButton(listener, std::wstring()),
icon_(icon),
text_(text),
top_right_(top_right),
was_first_click_(false) {
SetEnabledColor(SK_ColorWHITE);
SetDisabledColor(SK_ColorWHITE);
SetHighlightColor(SK_ColorWHITE);
SetHoverColor(SK_ColorWHITE);
SetIcon(icon_);
UpdatePosition();
}
protected:
// Overridden from View:
virtual void OnMouseExited(const views::MouseEvent& event) {
SetIcon(icon_);
views::TextButton::SetText(std::wstring());
ClearMaxTextSize();
set_background(NULL);
set_border(new views::TextButtonBorder);
UpdatePosition();
views::TextButton::OnMouseExited(event);
was_first_click_ = false;
}
void NotifyClick(const views::Event& event) {
if (!was_first_click_) {
// On first click transform image to "remove" label.
SetIcon(SkBitmap());
views::TextButton::SetText(text_);
const SkColor kStrokeColor = SK_ColorWHITE;
const SkColor kButtonColor = 0xFFE94949;
const int kStrokeWidth = 1;
const int kVerticalPadding = 4;
const int kHorizontalPadding = 8;
const int kCornerRadius = 4;
set_background(
CreateRoundedBackground(
kCornerRadius, kStrokeWidth, kButtonColor, kStrokeColor));
set_border(
views::Border::CreateEmptyBorder(kVerticalPadding,
kHorizontalPadding,
kVerticalPadding,
kHorizontalPadding));
UpdatePosition();
was_first_click_ = true;
} else {
// On second click propagate to base class to fire ButtonPressed.
views::TextButton::NotifyClick(event);
}
}
void SetText(const std::wstring& text) {
text_ = text;
}
private:
// Update button position and schedule paint event for the view and parent.
void UpdatePosition() {
gfx::Size size = GetPreferredSize();
gfx::Point origin = top_right_;
origin.Offset(-size.width(), 0);
SetBoundsRect(gfx::Rect(origin, size));
if (parent())
parent()->SchedulePaint();
}
SkBitmap icon_;
std::wstring text_;
gfx::Point top_right_;
bool was_first_click_;
DISALLOW_COPY_AND_ASSIGN(RemoveButton);
};
class PodImageView : public views::ImageView {
public:
explicit PodImageView(const UserView::Delegate* delegate)
: delegate_(delegate) { }
void SetImage(const SkBitmap& image, const SkBitmap& image_hot) {
image_ = image;
image_hot_ = image_hot;
views::ImageView::SetImage(image_);
}
protected:
// Overridden from View:
virtual void OnMouseEntered(const views::MouseEvent& event) {
views::ImageView::SetImage(image_hot_);
}
virtual void OnMouseExited(const views::MouseEvent& event) {
views::ImageView::SetImage(image_);
}
gfx::NativeCursor GetCursorForPoint(
ui::EventType event_type,
const gfx::Point& p) {
return (delegate_->IsUserSelected()) ? NULL : gfx::GetCursor(GDK_HAND2);
}
private:
const UserView::Delegate* delegate_;
SkBitmap image_;
SkBitmap image_hot_;
DISALLOW_COPY_AND_ASSIGN(PodImageView);
};
UserView::UserView(Delegate* delegate, bool is_login, bool need_background)
: delegate_(delegate),
signout_view_(NULL),
image_view_(NULL),
remove_button_(NULL) {
DCHECK(delegate);
if (!is_login)
signout_view_ = new SignoutView(this);
if (need_background)
image_view_ = new RoundedView<PodImageView>(delegate);
else
image_view_ = new PodImageView(delegate);
Init(need_background);
}
void UserView::Init(bool need_background) {
if (need_background) {
image_view_->set_background(
views::Background::CreateSolidBackground(kBackgroundColor));
}
// UserView's layout never changes, so let's layout once here.
image_view_->SetBounds(0, 0, kUserImageSize, kUserImageSize);
AddChildView(image_view_);
if (signout_view_) {
signout_view_->SetBounds(0, kUserImageSize, kUserImageSize,
signout_view_->GetPreferredSize().height());
AddChildView(signout_view_);
}
ResourceBundle& rb = ResourceBundle::GetSharedInstance();
remove_button_ = new RemoveButton(
this,
*rb.GetBitmapNamed(IDR_CLOSE_BAR_H),
UTF16ToWide(l10n_util::GetStringUTF16(IDS_LOGIN_REMOVE)),
gfx::Point(kUserImageSize - kRemoveButtonPadding, kRemoveButtonPadding));
remove_button_->SetVisible(false);
AddChildView(remove_button_);
}
void UserView::SetImage(const SkBitmap& image, const SkBitmap& image_hot) {
int desired_size = std::min(image.width(), image.height());
// Desired size is not preserved if it's greater than 75% of kUserImageSize.
if (desired_size * 4 > 3 * kUserImageSize)
desired_size = kUserImageSize;
image_view_->SetImageSize(gfx::Size(desired_size, desired_size));
image_view_->SetImage(image, image_hot);
}
void UserView::SetTooltipText(const std::wstring& text) {
DCHECK(image_view_);
image_view_->SetTooltipText(text);
}
gfx::Size UserView::GetPreferredSize() {
return gfx::Size(
kUserImageSize,
kUserImageSize +
(signout_view_ ? signout_view_->GetPreferredSize().height() : 0));
}
void UserView::SetSignoutEnabled(bool enabled) {
DCHECK(signout_view_);
signout_view_->signout_link()->SetEnabled(enabled);
// Relayout because active and inactive link has different preferred size.
Layout();
}
void UserView::LinkActivated(views::Link* source, int event_flags) {
DCHECK(delegate_);
DCHECK(signout_view_);
if (signout_view_->signout_link() == source)
delegate_->OnSignout();
}
void UserView::SetRemoveButtonVisible(bool flag) {
remove_button_->SetVisible(flag);
}
void UserView::ButtonPressed(views::Button* sender, const views::Event& event) {
DCHECK(delegate_);
if (remove_button_ == sender)
delegate_->OnRemoveUser();
}
void UserView::OnLocaleChanged() {
remove_button_->SetText(
UTF16ToWide(l10n_util::GetStringUTF16(IDS_LOGIN_REMOVE)));
delegate_->OnLocaleChanged();
}
} // namespace chromeos