blob: 5c694b4e3079361b5e471a97e58af3ffa9aaaf4d [file] [log] [blame]
// Copyright (c) 2006-2008 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/views/text_button.h"
#include "chrome/app/theme/theme_resources.h"
#include "chrome/common/gfx/chrome_canvas.h"
#include "chrome/common/resource_bundle.h"
#include "chrome/common/throb_animation.h"
#include "chrome/common/win_util.h"
#include "chrome/views/button.h"
#include "chrome/views/event.h"
#include "chrome/views/view_menu_delegate.h"
#include "chrome/views/view_container.h"
#include "generated_resources.h"
namespace ChromeViews {
// Padding between the icon and text.
static const int kIconTextPadding = 5;
// Preferred padding between text and edge
static const int kPreferredPaddingHorizontal = 6;
static const int kPreferredPaddingVertical = 5;
static const SkColor kEnabledColor = SkColorSetRGB(6, 45, 117);
static const SkColor kHighlightColor = SkColorSetARGB(200, 255, 255, 255);
static const SkColor kDisabledColor = SkColorSetRGB(161, 161, 146);
// How long the hover fade animation should last.
static const int kHoverAnimationDurationMs = 170;
////////////////////////////////////////////////////////////////////////////////
//
// TextButtonBorder - constructors, destructors, initialization
//
////////////////////////////////////////////////////////////////////////////////
TextButtonBorder::TextButtonBorder() {
ResourceBundle& rb = ResourceBundle::GetSharedInstance();
hot_set_.top_left = rb.GetBitmapNamed(IDR_TEXTBUTTON_TOP_LEFT_H);
hot_set_.top = rb.GetBitmapNamed(IDR_TEXTBUTTON_TOP_H);
hot_set_.top_right = rb.GetBitmapNamed(IDR_TEXTBUTTON_TOP_RIGHT_H);
hot_set_.left = rb.GetBitmapNamed(IDR_TEXTBUTTON_LEFT_H);
hot_set_.center = rb.GetBitmapNamed(IDR_TEXTBUTTON_CENTER_H);
hot_set_.right = rb.GetBitmapNamed(IDR_TEXTBUTTON_RIGHT_H);
hot_set_.bottom_left = rb.GetBitmapNamed(IDR_TEXTBUTTON_BOTTOM_LEFT_H);
hot_set_.bottom = rb.GetBitmapNamed(IDR_TEXTBUTTON_BOTTOM_H);
hot_set_.bottom_right = rb.GetBitmapNamed(IDR_TEXTBUTTON_BOTTOM_RIGHT_H);
pushed_set_.top_left = rb.GetBitmapNamed(IDR_TEXTBUTTON_TOP_LEFT_P);
pushed_set_.top = rb.GetBitmapNamed(IDR_TEXTBUTTON_TOP_P);
pushed_set_.top_right = rb.GetBitmapNamed(IDR_TEXTBUTTON_TOP_RIGHT_P);
pushed_set_.left = rb.GetBitmapNamed(IDR_TEXTBUTTON_LEFT_P);
pushed_set_.center = rb.GetBitmapNamed(IDR_TEXTBUTTON_CENTER_P);
pushed_set_.right = rb.GetBitmapNamed(IDR_TEXTBUTTON_RIGHT_P);
pushed_set_.bottom_left = rb.GetBitmapNamed(IDR_TEXTBUTTON_BOTTOM_LEFT_P);
pushed_set_.bottom = rb.GetBitmapNamed(IDR_TEXTBUTTON_BOTTOM_P);
pushed_set_.bottom_right = rb.GetBitmapNamed(IDR_TEXTBUTTON_BOTTOM_RIGHT_P);
}
TextButtonBorder::~TextButtonBorder() {
}
////////////////////////////////////////////////////////////////////////////////
//
// TextButtonBackground - painting
//
////////////////////////////////////////////////////////////////////////////////
void TextButtonBorder::Paint(const View& view, ChromeCanvas* canvas) const {
const TextButton* mb = static_cast<const TextButton*>(&view);
int state = mb->GetState();
// TextButton takes care of deciding when to call Paint.
const MBBImageSet* set = &hot_set_;
if (state == TextButton::BS_PUSHED)
set = &pushed_set_;
if (set) {
CRect bounds;
view.GetBounds(&bounds);
// Draw the top left image
canvas->DrawBitmapInt(*set->top_left, 0, 0);
// Tile the top image
canvas->TileImageInt(*set->top,
set->top_left->width(), 0,
bounds.Width() - set->top_right->width() - set->top_left->width(),
set->top->height());
// Draw the top right image
canvas->DrawBitmapInt(*set->top_right,
bounds.Width() - set->top_right->width(), 0);
// Tile the left image
canvas->TileImageInt(*set->left,
0, set->top_left->height(),
set->top_left->width(),
bounds.Height() - set->top->height() - set->bottom_left->height());
// Tile the center image
canvas->TileImageInt(*set->center,
set->left->width(), set->top->height(),
bounds.Width() - set->right->width() - set->left->width(),
bounds.Height() - set->bottom->height() - set->top->height());
// Tile the right image
canvas->TileImageInt(*set->right,
bounds.Width() - set->right->width(), set->top_right->height(),
bounds.Width(), bounds.Height() - set->bottom_right->height() - set->top_right->height());
// Draw the bottom left image
canvas->DrawBitmapInt(*set->bottom_left, 0, bounds.Height() - set->bottom_left->height());
// Tile the bottom image
canvas->TileImageInt(*set->bottom,
set->bottom_left->width(), bounds.Height() - set->bottom->height(),
bounds.Width() - set->bottom_right->width() - set->bottom_left->width(),
set->bottom->height());
// Draw the bottom right image
canvas->DrawBitmapInt(*set->bottom_right,
bounds.Width() - set->bottom_right->width(),
bounds.Height() - set->bottom_right->height());
} else {
// Do nothing
}
}
void TextButtonBorder::GetInsets(gfx::Insets* insets) const {
insets->Set(kPreferredPaddingVertical, kPreferredPaddingHorizontal,
kPreferredPaddingVertical, kPreferredPaddingHorizontal);
}
////////////////////////////////////////////////////////////////////////////////
//
// TextButton - Implementation
//
////////////////////////////////////////////////////////////////////////////////
TextButton::TextButton(const std::wstring& text)
: max_text_size_(CSize(0, 0)),
font_(ResourceBundle::GetSharedInstance().GetFont(
ResourceBundle::BaseFont)),
color_(kEnabledColor),
BaseButton(),
max_width_(0),
alignment_(ALIGN_LEFT) {
SetText(text);
SetBorder(new TextButtonBorder);
SetAnimationDuration(kHoverAnimationDurationMs);
}
TextButton::~TextButton() {
}
void TextButton::GetPreferredSize(CSize *result) {
gfx::Insets insets = GetInsets();
// Use the max size to set the button boundaries.
result->cx = max_text_size_.cx + icon_.width() + insets.width();
result->cy = std::max(static_cast<int>(max_text_size_.cy), icon_.height()) +
insets.height();
if (icon_.width() > 0 && !text_.empty())
result->cx += kIconTextPadding;
if (max_width_ > 0)
result->cx = std::min(max_width_, static_cast<int>(result->cx));
}
void TextButton::GetMinimumSize(CSize *result) {
result->cx = max_text_size_.cx;
result->cy = max_text_size_.cy;
}
bool TextButton::OnMousePressed(const ChromeViews::MouseEvent& e) {
return true;
}
void TextButton::SetText(const std::wstring& text) {
text_ = text;
// Update our new current and max text size
text_size_.cx = font_.GetStringWidth(text_);
text_size_.cy = font_.height();
max_text_size_.cx = std::max(max_text_size_.cx, text_size_.cx);
max_text_size_.cy = std::max(max_text_size_.cy, text_size_.cy);
}
void TextButton::SetIcon(const SkBitmap& icon) {
icon_ = icon;
}
void TextButton::ClearMaxTextSize() {
max_text_size_ = text_size_;
}
void TextButton::Paint(ChromeCanvas* canvas) {
Paint(canvas, false);
}
void TextButton::Paint(ChromeCanvas* canvas, bool for_drag) {
if (!for_drag) {
PaintBackground(canvas);
if (hover_animation_->IsAnimating()) {
// Draw the hover bitmap into an offscreen buffer, then blend it
// back into the current canvas.
canvas->saveLayerAlpha(NULL,
static_cast<int>(hover_animation_->GetCurrentValue() * 255),
SkCanvas::kARGB_NoClipLayer_SaveFlag);
canvas->drawARGB(0, 255, 255, 255, SkPorterDuff::kClear_Mode);
PaintBorder(canvas);
canvas->restore();
} else if (state_ == BS_HOT || state_ == BS_PUSHED) {
PaintBorder(canvas);
}
PaintFocusBorder(canvas);
}
gfx::Insets insets = GetInsets();
int available_width = GetWidth() - insets.width();
int available_height = GetHeight() - insets.height();
// Use the actual text (not max) size to properly center the text.
int content_width = text_size_.cx;
if (icon_.width() > 0) {
content_width += icon_.width();
if (!text_.empty())
content_width += kIconTextPadding;
}
// Place the icon along the left edge.
int icon_x;
if (alignment_ == ALIGN_LEFT) {
icon_x = insets.left();
} else if (alignment_ == ALIGN_RIGHT) {
icon_x = available_width - content_width;
} else {
icon_x =
std::max(0, (available_width - content_width) / 2) + insets.left();
}
int text_x = icon_x;
if (icon_.width() > 0)
text_x += icon_.width() + kIconTextPadding;
const int text_width = std::min(static_cast<int>(text_size_.cx),
GetWidth() - insets.right() - text_x);
int text_y = (available_height - text_size_.cy) / 2 + insets.top();
if (text_width > 0) {
// Because the text button can (at times) draw multiple elements on the
// canvas, we can not mirror the button by simply flipping the canvas as
// doing this will mirror the text itself. Flipping the canvas will also
// make the icons look wrong because icons are almost always represented as
// direction insentisive bitmaps and such bitmaps should never be flipped
// horizontally.
//
// Due to the above, we must perform the flipping manually for RTL UIs.
gfx::Rect text_bounds(text_x, text_y, text_width, text_size_.cy);
text_bounds.set_x(MirroredLeftPointForRect(text_bounds));
// Draw bevel highlight
canvas->DrawStringInt(text_,
font_,
kHighlightColor,
text_bounds.x() + 1,
text_bounds.y() + 1,
text_bounds.width(),
text_bounds.height());
canvas->DrawStringInt(text_,
font_,
color_,
text_bounds.x(),
text_bounds.y(),
text_bounds.width(),
text_bounds.height());
}
if (icon_.width() > 0) {
int icon_y = (available_height - icon_.height()) / 2 + insets.top();
// Mirroring the icon position if necessary.
gfx::Rect icon_bounds(icon_x, icon_y, icon_.width(), icon_.height());
icon_bounds.set_x(MirroredLeftPointForRect(icon_bounds));
canvas->DrawBitmapInt(icon_, icon_bounds.x(), icon_bounds.y());
}
}
void TextButton::SetEnabled(bool enabled) {
if (enabled == IsEnabled())
return;
BaseButton::SetEnabled(enabled);
color_ = enabled ? kEnabledColor : kDisabledColor;
SchedulePaint();
}
} // namespace ChromeViews