#include "ui/views/controls/button/label_button.h"
+#include "base/lazy_instance.h"
#include "base/logging.h"
-#include "grit/ui_resources.h"
-#include "ui/base/resource/resource_bundle.h"
#include "ui/gfx/animation/throb_animation.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/font_list.h"
namespace {
-// The spacing between the icon and text.
+// The default spacing between the icon and text.
const int kSpacing = 5;
#if !(defined(OS_LINUX) && !defined(OS_CHROMEOS))
const SkColor kStyleButtonShadowColor = SK_ColorWHITE;
#endif
+const gfx::FontList& GetDefaultNormalFontList() {
+ static base::LazyInstance<gfx::FontList>::Leaky font_list =
+ LAZY_INSTANCE_INITIALIZER;
+ return font_list.Get();
+}
+
+const gfx::FontList& GetDefaultBoldFontList() {
+ static base::LazyInstance<gfx::FontList>::Leaky font_list =
+ LAZY_INSTANCE_INITIALIZER;
+ if ((font_list.Get().GetFontStyle() & gfx::Font::BOLD) == 0) {
+ font_list.Get() = font_list.Get().
+ DeriveWithStyle(font_list.Get().GetFontStyle() | gfx::Font::BOLD);
+ DCHECK_NE(font_list.Get().GetFontStyle() & gfx::Font::BOLD, 0);
+ }
+ return font_list.Get();
+}
+
} // namespace
namespace views {
: CustomButton(listener),
image_(new ImageView()),
label_(new Label()),
+ cached_normal_font_list_(GetDefaultNormalFontList()),
+ cached_bold_font_list_(GetDefaultBoldFontList()),
button_state_images_(),
button_state_colors_(),
explicitly_set_colors_(),
is_default_(false),
- style_(STYLE_TEXTBUTTON) {
+ style_(STYLE_TEXTBUTTON),
+ border_is_themed_border_(true),
+ image_label_spacing_(kSpacing) {
SetAnimationDuration(kHoverAnimationDurationMs);
SetText(text);
- SetFontList(gfx::FontList());
AddChildView(image_);
image_->set_interactive(false);
AddChildView(label_);
+ label_->SetFontList(cached_normal_font_list_);
label_->SetAutoColorReadabilityEnabled(false);
label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
explicitly_set_colors_[for_state] = true;
}
+void LabelButton::SetTextShadows(const gfx::ShadowValues& shadows) {
+ label_->SetShadows(shadows);
+}
+
+void LabelButton::SetTextSubpixelRenderingEnabled(bool enabled) {
+ label_->SetSubpixelRenderingEnabled(enabled);
+}
+
bool LabelButton::GetTextMultiLine() const {
- return label_->is_multi_line();
+ return label_->multi_line();
}
void LabelButton::SetTextMultiLine(bool text_multi_line) {
cached_bold_font_list_ : cached_normal_font_list_);
}
-void LabelButton::SetElideBehavior(Label::ElideBehavior elide_behavior) {
+void LabelButton::SetElideBehavior(gfx::ElideBehavior elide_behavior) {
label_->SetElideBehavior(elide_behavior);
}
gfx::HorizontalAlignment LabelButton::GetHorizontalAlignment() const {
- return label_->horizontal_alignment();
+ return label_->GetHorizontalAlignment();
}
void LabelButton::SetHorizontalAlignment(gfx::HorizontalAlignment alignment) {
InvalidateLayout();
}
+void LabelButton::SetMinSize(const gfx::Size& min_size) {
+ min_size_ = min_size;
+ ResetCachedPreferredSize();
+}
+
+void LabelButton::SetMaxSize(const gfx::Size& max_size) {
+ max_size_ = max_size;
+ ResetCachedPreferredSize();
+}
+
void LabelButton::SetIsDefault(bool is_default) {
if (is_default == is_default_)
return;
SetFocusable(true);
}
if (style == STYLE_BUTTON)
- set_min_size(gfx::Size(70, 33));
-
- ResetColorsFromNativeTheme();
-
- UpdateThemedBorder(scoped_ptr<Border>(new LabelButtonBorder(style_)));
+ SetMinSize(gfx::Size(70, 33));
+ OnNativeThemeChanged(GetNativeTheme());
+ ResetCachedPreferredSize();
+}
- // Invalidate the layout to pickup the new insets from the border.
+void LabelButton::SetImageLabelSpacing(int spacing) {
+ if (spacing == image_label_spacing_)
+ return;
+ image_label_spacing_ = spacing;
+ ResetCachedPreferredSize();
InvalidateLayout();
}
focus_painter_ = focus_painter.Pass();
}
-gfx::Size LabelButton::GetPreferredSize() {
+gfx::Size LabelButton::GetPreferredSize() const {
+ if (cached_preferred_size_valid_)
+ return cached_preferred_size_;
+
// Use a temporary label copy for sizing to avoid calculation side-effects.
Label label(GetText(), cached_normal_font_list_);
+ label.SetShadows(label_->shadows());
label.SetMultiLine(GetTextMultiLine());
if (style() == STYLE_BUTTON) {
label.SetFontList(cached_normal_font_list_);
}
- // Resize multi-line labels given the current limited available width.
- const gfx::Size image_size(image_->GetPreferredSize());
- const int image_width = image_size.width();
- if (GetTextMultiLine() && (width() > image_width + kSpacing))
- label.SizeToFit(width() - image_width - (image_width > 0 ? kSpacing : 0));
-
// Calculate the required size.
+ const gfx::Size image_size(image_->GetPreferredSize());
gfx::Size size(label.GetPreferredSize());
- if (image_width > 0 && size.width() > 0)
- size.Enlarge(kSpacing, 0);
+ if (image_size.width() > 0 && size.width() > 0)
+ size.Enlarge(image_label_spacing_, 0);
size.SetToMax(gfx::Size(0, image_size.height()));
const gfx::Insets insets(GetInsets());
size.Enlarge(image_size.width() + insets.width(), insets.height());
size.set_width(std::min(max_size_.width(), size.width()));
if (max_size_.height() > 0)
size.set_height(std::min(max_size_.height(), size.height()));
- return size;
+
+ // Cache this computed size, as recomputing it is an expensive operation.
+ cached_preferred_size_valid_ = true;
+ cached_preferred_size_ = size;
+ return cached_preferred_size_;
+}
+
+int LabelButton::GetHeightForWidth(int w) const {
+ w -= GetInsets().width();
+ const gfx::Size image_size(image_->GetPreferredSize());
+ w -= image_size.width();
+ if (image_size.width() > 0 && !GetText().empty())
+ w -= image_label_spacing_;
+
+ int height = std::max(image_size.height(), label_->GetHeightForWidth(w));
+ if (border())
+ height = std::max(height, border()->GetMinimumSize().height());
+
+ height = std::max(height, min_size_.height());
+ if (max_size_.height() > 0)
+ height = std::min(height, max_size_.height());
+ return height;
}
void LabelButton::Layout() {
adjusted_alignment = (adjusted_alignment == gfx::ALIGN_LEFT) ?
gfx::ALIGN_RIGHT : gfx::ALIGN_LEFT;
- gfx::Rect child_area(GetLocalBounds());
+ gfx::Rect child_area(GetChildAreaBounds());
child_area.Inset(GetInsets());
gfx::Size image_size(image_->GetPreferredSize());
// The label takes any remaining width after sizing the image, unless both
// views are centered. In that case, using the tighter preferred label width
// avoids wasted space within the label that would look like awkward padding.
- gfx::Size label_size(child_area.size());
+ // Labels can paint over the full button height, including the border height.
+ gfx::Size label_size(child_area.width(), height());
if (!image_size.IsEmpty() && !label_size.IsEmpty()) {
- label_size.set_width(
- std::max(child_area.width() - image_size.width() - kSpacing, 0));
+ label_size.set_width(std::max(child_area.width() -
+ image_size.width() - image_label_spacing_, 0));
if (adjusted_alignment == gfx::ALIGN_CENTER) {
// Ensure multi-line labels paired with images use their available width.
- if (GetTextMultiLine())
- label_->SizeToFit(label_size.width());
label_size.set_width(
std::min(label_size.width(), label_->GetPreferredSize().width()));
}
gfx::Point image_origin(child_area.origin());
image_origin.Offset(0, (child_area.height() - image_size.height()) / 2);
if (adjusted_alignment == gfx::ALIGN_CENTER) {
+ const int spacing = (image_size.width() > 0 && label_size.width() > 0) ?
+ image_label_spacing_ : 0;
const int total_width = image_size.width() + label_size.width() +
- ((image_size.width() > 0 && label_size.width() > 0) ? kSpacing : 0);
+ spacing;
image_origin.Offset((child_area.width() - total_width) / 2, 0);
} else if (adjusted_alignment == gfx::ALIGN_RIGHT) {
image_origin.Offset(child_area.width() - image_size.width(), 0);
}
- gfx::Point label_origin(child_area.origin());
- if (!image_size.IsEmpty() &&adjusted_alignment != gfx::ALIGN_RIGHT)
- label_origin.set_x(image_origin.x() + image_size.width() + kSpacing);
+ gfx::Point label_origin(child_area.x(), 0);
+ if (!image_size.IsEmpty() && adjusted_alignment != gfx::ALIGN_RIGHT) {
+ label_origin.set_x(image_origin.x() + image_size.width() +
+ image_label_spacing_);
+ }
image_->SetBoundsRect(gfx::Rect(image_origin, image_size));
label_->SetBoundsRect(gfx::Rect(label_origin, label_size));
return kViewClassName;
}
+scoped_ptr<LabelButtonBorder> LabelButton::CreateDefaultBorder() const {
+ return scoped_ptr<LabelButtonBorder>(new LabelButtonBorder(style_));
+}
+
+void LabelButton::SetBorder(scoped_ptr<Border> border) {
+ border_is_themed_border_ = false;
+ View::SetBorder(border.Pass());
+ ResetCachedPreferredSize();
+}
+
+gfx::Rect LabelButton::GetChildAreaBounds() {
+ return GetLocalBounds();
+}
+
void LabelButton::OnPaint(gfx::Canvas* canvas) {
View::OnPaint(canvas);
Painter::PaintFocusPainter(this, canvas, focus_painter_.get());
label_->SetBackgroundColor(SK_ColorBLACK);
label_->set_background(Background::CreateSolidBackground(SK_ColorBLACK));
label_->SetAutoColorReadabilityEnabled(true);
- label_->ClearEmbellishing();
+ label_->SetShadows(gfx::ShadowValues());
} else if (style() == STYLE_BUTTON) {
// TODO(erg): This is disabled on desktop linux because of the binary asset
// confusion. These details should either be pushed into ui::NativeThemeWin
label_->SetBackgroundColor(theme->GetSystemColor(
ui::NativeTheme::kColorId_ButtonBackgroundColor));
label_->SetAutoColorReadabilityEnabled(false);
- label_->SetShadowColors(kStyleButtonShadowColor, kStyleButtonShadowColor);
- label_->SetShadowOffset(0, 1);
+ label_->SetShadows(gfx::ShadowValues(
+ 1, gfx::ShadowValue(gfx::Point(0, 1), 0, kStyleButtonShadowColor)));
#endif
label_->set_background(NULL);
} else {
void LabelButton::UpdateImage() {
image_->SetImage(GetImage(state()));
+ ResetCachedPreferredSize();
}
-void LabelButton::UpdateThemedBorder(scoped_ptr<Border> label_button_border) {
+void LabelButton::UpdateThemedBorder() {
+ // Don't override borders set by others.
+ if (!border_is_themed_border_)
+ return;
+
+ scoped_ptr<LabelButtonBorder> label_button_border = CreateDefaultBorder();
+
#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
views::LinuxUI* linux_ui = views::LinuxUI::instance();
if (linux_ui) {
- SetBorder(linux_ui->CreateNativeBorder(this, label_button_border.Pass()));
+ SetBorder(linux_ui->CreateNativeBorder(
+ this, label_button_border.Pass()));
} else
#endif
{
SetBorder(label_button_border.Pass());
}
+
+ border_is_themed_border_ = true;
}
void LabelButton::StateChanged() {
}
void LabelButton::ChildPreferredSizeChanged(View* child) {
+ ResetCachedPreferredSize();
PreferredSizeChanged();
}
void LabelButton::OnNativeThemeChanged(const ui::NativeTheme* theme) {
ResetColorsFromNativeTheme();
+ UpdateThemedBorder();
+ // Invalidate the layout to pickup the new insets from the border.
+ InvalidateLayout();
}
ui::NativeTheme::Part LabelButton::GetThemePart() const {
return ui::NativeTheme::kHovered;
}
+void LabelButton::ResetCachedPreferredSize() {
+ cached_preferred_size_valid_ = false;
+ cached_preferred_size_= gfx::Size();
+}
+
} // namespace views