1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "ui/views/controls/button/text_button.h" 6 7 #include <algorithm> 8 9 #include "base/logging.h" 10 #include "grit/ui_resources.h" 11 #include "ui/base/resource/resource_bundle.h" 12 #include "ui/gfx/animation/throb_animation.h" 13 #include "ui/gfx/canvas.h" 14 #include "ui/gfx/image/image.h" 15 #include "ui/views/controls/button/button.h" 16 #include "ui/views/painter.h" 17 #include "ui/views/widget/widget.h" 18 19 #if defined(OS_WIN) 20 #include "skia/ext/skia_utils_win.h" 21 #include "ui/gfx/platform_font_win.h" 22 #include "ui/native_theme/native_theme_win.h" 23 #endif 24 25 namespace views { 26 27 namespace { 28 29 // Default space between the icon and text. 30 const int kDefaultIconTextSpacing = 5; 31 32 // Preferred padding between text and edge. 33 const int kPreferredPaddingHorizontal = 6; 34 const int kPreferredPaddingVertical = 5; 35 36 // Preferred padding between text and edge for NativeTheme border. 37 const int kPreferredNativeThemePaddingHorizontal = 12; 38 const int kPreferredNativeThemePaddingVertical = 5; 39 40 // By default the focus rect is drawn at the border of the view. For a button, 41 // we inset the focus rect by 3 pixels so that it doesn't draw on top of the 42 // button's border. This roughly matches how the Windows native focus rect for 43 // buttons looks. A subclass that draws a button with different padding may need 44 // to provide a different focus painter and do something different. 45 const int kFocusRectInset = 3; 46 47 // How long the hover fade animation should last. 48 const int kHoverAnimationDurationMs = 170; 49 50 #if defined(OS_WIN) 51 // These sizes are from http://msdn.microsoft.com/en-us/library/aa511279.aspx 52 const int kMinWidthDLUs = 50; 53 const int kMinHeightDLUs = 14; 54 #endif 55 56 // The default hot and pushed button image IDs; normal has none by default. 57 const int kHotImages[] = IMAGE_GRID(IDR_TEXTBUTTON_HOVER); 58 const int kPushedImages[] = IMAGE_GRID(IDR_TEXTBUTTON_PRESSED); 59 60 } // namespace 61 62 // static 63 const char TextButtonBase::kViewClassName[] = "TextButtonBase"; 64 // static 65 const char TextButton::kViewClassName[] = "TextButton"; 66 67 68 // TextButtonBorder ----------------------------------------------------------- 69 70 TextButtonBorder::TextButtonBorder() { 71 } 72 73 TextButtonBorder::~TextButtonBorder() { 74 } 75 76 void TextButtonBorder::Paint(const View& view, gfx::Canvas* canvas) { 77 } 78 79 gfx::Insets TextButtonBorder::GetInsets() const { 80 return insets_; 81 } 82 83 gfx::Size TextButtonBorder::GetMinimumSize() const { 84 return gfx::Size(); 85 } 86 87 void TextButtonBorder::SetInsets(const gfx::Insets& insets) { 88 insets_ = insets; 89 } 90 91 92 // TextButtonDefaultBorder ---------------------------------------------------- 93 94 TextButtonDefaultBorder::TextButtonDefaultBorder() 95 : vertical_padding_(kPreferredPaddingVertical) { 96 set_hot_painter(Painter::CreateImageGridPainter(kHotImages)); 97 set_pushed_painter(Painter::CreateImageGridPainter(kPushedImages)); 98 SetInsets(gfx::Insets(vertical_padding_, kPreferredPaddingHorizontal, 99 vertical_padding_, kPreferredPaddingHorizontal)); 100 } 101 102 TextButtonDefaultBorder::~TextButtonDefaultBorder() { 103 } 104 105 void TextButtonDefaultBorder::Paint(const View& view, gfx::Canvas* canvas) { 106 const TextButton* button = static_cast<const TextButton*>(&view); 107 int state = button->state(); 108 bool animating = button->GetAnimation()->is_animating(); 109 110 Painter* painter = normal_painter_.get(); 111 // Use the hot painter when we're hovered. Also use the hot painter when we're 112 // STATE_NORMAL and |animating| so that we show throb animations started from 113 // CustomButton::StartThrobbing which should start throbbing the button 114 // regardless of whether it is hovered. 115 if (button->show_multiple_icon_states() && 116 ((state == TextButton::STATE_HOVERED) || 117 (state == TextButton::STATE_PRESSED) || 118 ((state == TextButton::STATE_NORMAL) && animating))) { 119 painter = (state == TextButton::STATE_PRESSED) ? 120 pushed_painter_.get() : hot_painter_.get(); 121 } 122 if (painter) { 123 if (animating) { 124 // TODO(pkasting): Really this should crossfade between states so it could 125 // handle the case of having a non-NULL |normal_painter_|. 126 canvas->SaveLayerAlpha(static_cast<uint8>( 127 button->GetAnimation()->CurrentValueBetween(0, 255))); 128 painter->Paint(canvas, view.size()); 129 canvas->Restore(); 130 } else { 131 painter->Paint(canvas, view.size()); 132 } 133 } 134 } 135 136 gfx::Size TextButtonDefaultBorder::GetMinimumSize() const { 137 gfx::Size size; 138 if (normal_painter_) 139 size.SetToMax(normal_painter_->GetMinimumSize()); 140 if (hot_painter_) 141 size.SetToMax(hot_painter_->GetMinimumSize()); 142 if (pushed_painter_) 143 size.SetToMax(pushed_painter_->GetMinimumSize()); 144 return size; 145 } 146 147 148 // TextButtonNativeThemeBorder ------------------------------------------------ 149 150 TextButtonNativeThemeBorder::TextButtonNativeThemeBorder( 151 NativeThemeDelegate* delegate) 152 : delegate_(delegate) { 153 SetInsets(gfx::Insets(kPreferredNativeThemePaddingVertical, 154 kPreferredNativeThemePaddingHorizontal, 155 kPreferredNativeThemePaddingVertical, 156 kPreferredNativeThemePaddingHorizontal)); 157 } 158 159 TextButtonNativeThemeBorder::~TextButtonNativeThemeBorder() { 160 } 161 162 void TextButtonNativeThemeBorder::Paint(const View& view, gfx::Canvas* canvas) { 163 const ui::NativeTheme* theme = view.GetNativeTheme(); 164 const TextButtonBase* tb = static_cast<const TextButton*>(&view); 165 ui::NativeTheme::Part part = delegate_->GetThemePart(); 166 gfx::Rect rect(delegate_->GetThemePaintRect()); 167 168 if (tb->show_multiple_icon_states() && 169 delegate_->GetThemeAnimation() != NULL && 170 delegate_->GetThemeAnimation()->is_animating()) { 171 // Paint background state. 172 ui::NativeTheme::ExtraParams prev_extra; 173 ui::NativeTheme::State prev_state = 174 delegate_->GetBackgroundThemeState(&prev_extra); 175 theme->Paint(canvas->sk_canvas(), part, prev_state, rect, prev_extra); 176 177 // Composite foreground state above it. 178 ui::NativeTheme::ExtraParams extra; 179 ui::NativeTheme::State state = delegate_->GetForegroundThemeState(&extra); 180 int alpha = delegate_->GetThemeAnimation()->CurrentValueBetween(0, 255); 181 canvas->SaveLayerAlpha(static_cast<uint8>(alpha)); 182 theme->Paint(canvas->sk_canvas(), part, state, rect, extra); 183 canvas->Restore(); 184 } else { 185 ui::NativeTheme::ExtraParams extra; 186 ui::NativeTheme::State state = delegate_->GetThemeState(&extra); 187 theme->Paint(canvas->sk_canvas(), part, state, rect, extra); 188 } 189 } 190 191 192 // TextButtonBase ------------------------------------------------------------- 193 194 TextButtonBase::TextButtonBase(ButtonListener* listener, 195 const base::string16& text) 196 : CustomButton(listener), 197 alignment_(ALIGN_LEFT), 198 min_width_(0), 199 min_height_(0), 200 max_width_(0), 201 show_multiple_icon_states_(true), 202 is_default_(false), 203 multi_line_(false), 204 use_enabled_color_from_theme_(true), 205 use_disabled_color_from_theme_(true), 206 use_highlight_color_from_theme_(true), 207 use_hover_color_from_theme_(true), 208 focus_painter_(Painter::CreateDashedFocusPainter()) { 209 SetText(text); 210 SetAnimationDuration(kHoverAnimationDurationMs); 211 } 212 213 TextButtonBase::~TextButtonBase() { 214 } 215 216 void TextButtonBase::SetIsDefault(bool is_default) { 217 if (is_default == is_default_) 218 return; 219 is_default_ = is_default; 220 if (is_default_) 221 AddAccelerator(ui::Accelerator(ui::VKEY_RETURN, ui::EF_NONE)); 222 else 223 RemoveAccelerator(ui::Accelerator(ui::VKEY_RETURN, ui::EF_NONE)); 224 SchedulePaint(); 225 } 226 227 void TextButtonBase::SetText(const base::string16& text) { 228 if (text == text_) 229 return; 230 text_ = text; 231 SetAccessibleName(text); 232 UpdateTextSize(); 233 } 234 235 void TextButtonBase::SetFontList(const gfx::FontList& font_list) { 236 font_list_ = font_list; 237 UpdateTextSize(); 238 } 239 240 void TextButtonBase::SetEnabledColor(SkColor color) { 241 color_enabled_ = color; 242 use_enabled_color_from_theme_ = false; 243 UpdateColor(); 244 } 245 246 void TextButtonBase::SetDisabledColor(SkColor color) { 247 color_disabled_ = color; 248 use_disabled_color_from_theme_ = false; 249 UpdateColor(); 250 } 251 252 void TextButtonBase::SetHighlightColor(SkColor color) { 253 color_highlight_ = color; 254 use_highlight_color_from_theme_ = false; 255 } 256 257 void TextButtonBase::SetHoverColor(SkColor color) { 258 color_hover_ = color; 259 use_hover_color_from_theme_ = false; 260 } 261 262 void TextButtonBase::ClearMaxTextSize() { 263 max_text_size_ = text_size_; 264 } 265 266 void TextButtonBase::SetShowMultipleIconStates(bool show_multiple_icon_states) { 267 show_multiple_icon_states_ = show_multiple_icon_states; 268 } 269 270 void TextButtonBase::SetMultiLine(bool multi_line) { 271 if (multi_line != multi_line_) { 272 multi_line_ = multi_line; 273 max_text_size_.SetSize(0, 0); 274 UpdateTextSize(); 275 SchedulePaint(); 276 } 277 } 278 279 gfx::Size TextButtonBase::GetPreferredSize() const { 280 gfx::Insets insets = GetInsets(); 281 282 // Use the max size to set the button boundaries. 283 // In multiline mode max size can be undefined while 284 // width() is 0, so max it out with current text size. 285 gfx::Size prefsize(std::max(max_text_size_.width(), 286 text_size_.width()) + insets.width(), 287 std::max(max_text_size_.height(), 288 text_size_.height()) + insets.height()); 289 290 if (max_width_ > 0) 291 prefsize.set_width(std::min(max_width_, prefsize.width())); 292 293 prefsize.set_width(std::max(prefsize.width(), min_width_)); 294 prefsize.set_height(std::max(prefsize.height(), min_height_)); 295 296 return prefsize; 297 } 298 299 int TextButtonBase::GetHeightForWidth(int w) const { 300 if (!multi_line_) 301 return View::GetHeightForWidth(w); 302 303 if (max_width_ > 0) 304 w = std::min(max_width_, w); 305 306 gfx::Size text_size; 307 CalculateTextSize(&text_size, w); 308 int height = text_size.height() + GetInsets().height(); 309 310 return std::max(height, min_height_); 311 } 312 313 void TextButtonBase::OnPaint(gfx::Canvas* canvas) { 314 PaintButton(canvas, PB_NORMAL); 315 } 316 317 void TextButtonBase::OnBoundsChanged(const gfx::Rect& previous_bounds) { 318 if (multi_line_) 319 UpdateTextSize(); 320 } 321 322 const gfx::Animation* TextButtonBase::GetAnimation() const { 323 return hover_animation_.get(); 324 } 325 326 void TextButtonBase::UpdateColor() { 327 color_ = enabled() ? color_enabled_ : color_disabled_; 328 } 329 330 void TextButtonBase::UpdateTextSize() { 331 int text_width = width(); 332 // If width is defined, use GetTextBounds.width() for maximum text width, 333 // as it will take size of checkbox/radiobutton into account. 334 if (text_width != 0) { 335 gfx::Rect text_bounds = GetTextBounds(); 336 text_width = text_bounds.width(); 337 } 338 CalculateTextSize(&text_size_, text_width); 339 // Before layout width() is 0, and multiline text will be treated as one line. 340 // Do not store max_text_size in this case. UpdateTextSize will be called 341 // again once width() changes. 342 if (!multi_line_ || text_width != 0) { 343 max_text_size_.SetSize(std::max(max_text_size_.width(), text_size_.width()), 344 std::max(max_text_size_.height(), 345 text_size_.height())); 346 PreferredSizeChanged(); 347 } 348 } 349 350 void TextButtonBase::CalculateTextSize(gfx::Size* text_size, 351 int max_width) const { 352 int h = font_list_.GetHeight(); 353 int w = multi_line_ ? max_width : 0; 354 int flags = ComputeCanvasStringFlags(); 355 if (!multi_line_) 356 flags |= gfx::Canvas::NO_ELLIPSIS; 357 358 gfx::Canvas::SizeStringInt(text_, font_list_, &w, &h, 0, flags); 359 text_size->SetSize(w, h); 360 } 361 362 void TextButtonBase::OnPaintText(gfx::Canvas* canvas, PaintButtonMode mode) { 363 gfx::Rect text_bounds(GetTextBounds()); 364 if (text_bounds.width() > 0) { 365 // Because the text button can (at times) draw multiple elements on the 366 // canvas, we can not mirror the button by simply flipping the canvas as 367 // doing this will mirror the text itself. Flipping the canvas will also 368 // make the icons look wrong because icons are almost always represented as 369 // direction-insensitive images and such images should never be flipped 370 // horizontally. 371 // 372 // Due to the above, we must perform the flipping manually for RTL UIs. 373 text_bounds.set_x(GetMirroredXForRect(text_bounds)); 374 375 SkColor text_color = (show_multiple_icon_states_ && 376 (state() == STATE_HOVERED || state() == STATE_PRESSED)) ? 377 color_hover_ : color_; 378 379 int draw_string_flags = gfx::Canvas::DefaultCanvasTextAlignment() | 380 ComputeCanvasStringFlags(); 381 382 if (mode == PB_FOR_DRAG) { 383 // Disable sub-pixel rendering as background is transparent. 384 draw_string_flags |= gfx::Canvas::NO_SUBPIXEL_RENDERING; 385 canvas->DrawStringRectWithHalo(text_, font_list_, 386 SK_ColorBLACK, SK_ColorWHITE, 387 text_bounds, draw_string_flags); 388 } else { 389 canvas->DrawStringRectWithFlags(text_, font_list_, text_color, 390 text_bounds, draw_string_flags); 391 } 392 } 393 } 394 395 int TextButtonBase::ComputeCanvasStringFlags() const { 396 if (!multi_line_) 397 return 0; 398 399 int flags = gfx::Canvas::MULTI_LINE; 400 switch (alignment_) { 401 case ALIGN_LEFT: 402 flags |= gfx::Canvas::TEXT_ALIGN_LEFT; 403 break; 404 case ALIGN_RIGHT: 405 flags |= gfx::Canvas::TEXT_ALIGN_RIGHT; 406 break; 407 case ALIGN_CENTER: 408 flags |= gfx::Canvas::TEXT_ALIGN_CENTER; 409 break; 410 } 411 return flags; 412 } 413 414 void TextButtonBase::OnFocus() { 415 View::OnFocus(); 416 if (focus_painter_) 417 SchedulePaint(); 418 } 419 420 void TextButtonBase::OnBlur() { 421 View::OnBlur(); 422 if (focus_painter_) 423 SchedulePaint(); 424 } 425 426 void TextButtonBase::GetExtraParams( 427 ui::NativeTheme::ExtraParams* params) const { 428 params->button.checked = false; 429 params->button.indeterminate = false; 430 params->button.is_default = false; 431 params->button.is_focused = false; 432 params->button.has_border = false; 433 params->button.classic_state = 0; 434 params->button.background_color = 435 GetNativeTheme()->GetSystemColor( 436 ui::NativeTheme::kColorId_ButtonBackgroundColor); 437 } 438 439 gfx::Rect TextButtonBase::GetContentBounds(int extra_width) const { 440 gfx::Insets insets = GetInsets(); 441 int available_width = width() - insets.width(); 442 int content_width = text_size_.width() + extra_width; 443 int content_x = 0; 444 switch(alignment_) { 445 case ALIGN_LEFT: 446 content_x = insets.left(); 447 break; 448 case ALIGN_RIGHT: 449 content_x = width() - insets.right() - content_width; 450 if (content_x < insets.left()) 451 content_x = insets.left(); 452 break; 453 case ALIGN_CENTER: 454 content_x = insets.left() + std::max(0, 455 (available_width - content_width) / 2); 456 break; 457 } 458 content_width = std::min(content_width, 459 width() - insets.right() - content_x); 460 int available_height = height() - insets.height(); 461 int content_y = (available_height - text_size_.height()) / 2 + insets.top(); 462 463 gfx::Rect bounds(content_x, content_y, content_width, text_size_.height()); 464 return bounds; 465 } 466 467 gfx::Rect TextButtonBase::GetTextBounds() const { 468 return GetContentBounds(0); 469 } 470 471 void TextButtonBase::SetFocusPainter(scoped_ptr<Painter> focus_painter) { 472 focus_painter_ = focus_painter.Pass(); 473 } 474 475 void TextButtonBase::PaintButton(gfx::Canvas* canvas, PaintButtonMode mode) { 476 if (mode == PB_NORMAL) { 477 OnPaintBackground(canvas); 478 OnPaintBorder(canvas); 479 Painter::PaintFocusPainter(this, canvas, focus_painter_.get()); 480 } 481 482 OnPaintText(canvas, mode); 483 } 484 485 gfx::Size TextButtonBase::GetMinimumSize() const { 486 return max_text_size_; 487 } 488 489 void TextButtonBase::OnEnabledChanged() { 490 // We should always call UpdateColor() since the state of the button might be 491 // changed by other functions like CustomButton::SetState(). 492 UpdateColor(); 493 CustomButton::OnEnabledChanged(); 494 } 495 496 const char* TextButtonBase::GetClassName() const { 497 return kViewClassName; 498 } 499 500 void TextButtonBase::OnNativeThemeChanged(const ui::NativeTheme* theme) { 501 if (use_enabled_color_from_theme_) { 502 color_enabled_ = theme->GetSystemColor( 503 ui::NativeTheme::kColorId_ButtonEnabledColor); 504 } 505 if (use_disabled_color_from_theme_) { 506 color_disabled_ = theme->GetSystemColor( 507 ui::NativeTheme::kColorId_ButtonDisabledColor); 508 } 509 if (use_highlight_color_from_theme_) { 510 color_highlight_ = theme->GetSystemColor( 511 ui::NativeTheme::kColorId_ButtonHighlightColor); 512 } 513 if (use_hover_color_from_theme_) { 514 color_hover_ = theme->GetSystemColor( 515 ui::NativeTheme::kColorId_ButtonHoverColor); 516 } 517 UpdateColor(); 518 } 519 520 gfx::Rect TextButtonBase::GetThemePaintRect() const { 521 return GetLocalBounds(); 522 } 523 524 ui::NativeTheme::State TextButtonBase::GetThemeState( 525 ui::NativeTheme::ExtraParams* params) const { 526 GetExtraParams(params); 527 switch(state()) { 528 case STATE_DISABLED: 529 return ui::NativeTheme::kDisabled; 530 case STATE_NORMAL: 531 return ui::NativeTheme::kNormal; 532 case STATE_HOVERED: 533 return ui::NativeTheme::kHovered; 534 case STATE_PRESSED: 535 return ui::NativeTheme::kPressed; 536 default: 537 NOTREACHED() << "Unknown state: " << state(); 538 return ui::NativeTheme::kNormal; 539 } 540 } 541 542 const gfx::Animation* TextButtonBase::GetThemeAnimation() const { 543 #if defined(OS_WIN) 544 if (GetNativeTheme() == ui::NativeThemeWin::instance()) { 545 return ui::NativeThemeWin::instance()->IsThemingActive() ? 546 hover_animation_.get() : NULL; 547 } 548 #endif 549 return hover_animation_.get(); 550 } 551 552 ui::NativeTheme::State TextButtonBase::GetBackgroundThemeState( 553 ui::NativeTheme::ExtraParams* params) const { 554 GetExtraParams(params); 555 return ui::NativeTheme::kNormal; 556 } 557 558 ui::NativeTheme::State TextButtonBase::GetForegroundThemeState( 559 ui::NativeTheme::ExtraParams* params) const { 560 GetExtraParams(params); 561 return ui::NativeTheme::kHovered; 562 } 563 564 565 // TextButton ----------------------------------------------------------------- 566 567 TextButton::TextButton(ButtonListener* listener, const base::string16& text) 568 : TextButtonBase(listener, text), 569 icon_placement_(ICON_ON_LEFT), 570 has_hover_icon_(false), 571 has_pushed_icon_(false), 572 icon_text_spacing_(kDefaultIconTextSpacing), 573 ignore_minimum_size_(true), 574 full_justification_(false) { 575 SetBorder(scoped_ptr<Border>(new TextButtonDefaultBorder)); 576 SetFocusPainter(Painter::CreateDashedFocusPainterWithInsets( 577 gfx::Insets(kFocusRectInset, kFocusRectInset, 578 kFocusRectInset, kFocusRectInset))); 579 } 580 581 TextButton::~TextButton() { 582 } 583 584 void TextButton::SetIcon(const gfx::ImageSkia& icon) { 585 icon_ = icon; 586 SchedulePaint(); 587 } 588 589 void TextButton::SetHoverIcon(const gfx::ImageSkia& icon) { 590 icon_hover_ = icon; 591 has_hover_icon_ = true; 592 SchedulePaint(); 593 } 594 595 void TextButton::SetPushedIcon(const gfx::ImageSkia& icon) { 596 icon_pushed_ = icon; 597 has_pushed_icon_ = true; 598 SchedulePaint(); 599 } 600 601 gfx::Size TextButton::GetPreferredSize() const { 602 gfx::Size prefsize(TextButtonBase::GetPreferredSize()); 603 prefsize.Enlarge(icon_.width(), 0); 604 prefsize.set_height(std::max(prefsize.height(), icon_.height())); 605 606 // Use the max size to set the button boundaries. 607 if (icon_.width() > 0 && !text_.empty()) 608 prefsize.Enlarge(icon_text_spacing_, 0); 609 610 if (max_width_ > 0) 611 prefsize.set_width(std::min(max_width_, prefsize.width())); 612 613 #if defined(OS_WIN) 614 // Clamp the size returned to at least the minimum size. 615 if (!ignore_minimum_size_) { 616 gfx::PlatformFontWin* platform_font = static_cast<gfx::PlatformFontWin*>( 617 font_list_.GetPrimaryFont().platform_font()); 618 prefsize.set_width(std::max( 619 prefsize.width(), 620 platform_font->horizontal_dlus_to_pixels(kMinWidthDLUs))); 621 prefsize.set_height(std::max( 622 prefsize.height(), 623 platform_font->vertical_dlus_to_pixels(kMinHeightDLUs))); 624 } 625 #endif 626 627 prefsize.set_width(std::max(prefsize.width(), min_width_)); 628 prefsize.set_height(std::max(prefsize.height(), min_height_)); 629 630 return prefsize; 631 } 632 633 void TextButton::PaintButton(gfx::Canvas* canvas, PaintButtonMode mode) { 634 if (full_justification_ && icon_placement_ == ICON_ON_LEFT) 635 set_alignment(ALIGN_RIGHT); 636 637 TextButtonBase::PaintButton(canvas, mode); 638 OnPaintIcon(canvas, mode); 639 } 640 641 void TextButton::OnPaintIcon(gfx::Canvas* canvas, PaintButtonMode mode) { 642 const gfx::ImageSkia& icon = GetImageToPaint(); 643 644 if (icon.width() > 0) { 645 gfx::Rect text_bounds = GetTextBounds(); 646 int icon_x = 0; 647 int spacing = text_.empty() ? 0 : icon_text_spacing_; 648 gfx::Insets insets = GetInsets(); 649 switch (icon_placement_) { 650 case ICON_ON_LEFT: 651 icon_x = full_justification_ ? insets.left() 652 : text_bounds.x() - icon.width() - spacing; 653 break; 654 case ICON_ON_RIGHT: 655 icon_x = full_justification_ ? width() - insets.right() - icon.width() 656 : text_bounds.right() + spacing; 657 break; 658 case ICON_CENTERED: 659 DCHECK(text_.empty()); 660 icon_x = (width() - insets.width() - icon.width()) / 2 + insets.left(); 661 break; 662 default: 663 NOTREACHED(); 664 break; 665 } 666 667 int available_height = height() - insets.height(); 668 int icon_y = (available_height - icon.height()) / 2 + insets.top(); 669 670 // Mirroring the icon position if necessary. 671 gfx::Rect icon_bounds(icon_x, icon_y, icon.width(), icon.height()); 672 icon_bounds.set_x(GetMirroredXForRect(icon_bounds)); 673 canvas->DrawImageInt(icon, icon_bounds.x(), icon_bounds.y()); 674 } 675 } 676 677 void TextButton::set_ignore_minimum_size(bool ignore_minimum_size) { 678 ignore_minimum_size_ = ignore_minimum_size; 679 } 680 681 void TextButton::set_full_justification(bool full_justification) { 682 full_justification_ = full_justification; 683 } 684 685 const char* TextButton::GetClassName() const { 686 return kViewClassName; 687 } 688 689 ui::NativeTheme::Part TextButton::GetThemePart() const { 690 return ui::NativeTheme::kPushButton; 691 } 692 693 void TextButton::GetExtraParams(ui::NativeTheme::ExtraParams* params) const { 694 TextButtonBase::GetExtraParams(params); 695 params->button.is_default = is_default_; 696 } 697 698 gfx::Rect TextButton::GetTextBounds() const { 699 int extra_width = 0; 700 701 const gfx::ImageSkia& icon = GetImageToPaint(); 702 if (icon.width() > 0) 703 extra_width = icon.width() + (text_.empty() ? 0 : icon_text_spacing_); 704 705 gfx::Rect bounds(GetContentBounds(extra_width)); 706 707 if (extra_width > 0) { 708 // Make sure the icon is always fully visible. 709 if (icon_placement_ == ICON_ON_LEFT) { 710 bounds.Inset(extra_width, 0, 0, 0); 711 } else if (icon_placement_ == ICON_ON_RIGHT) { 712 bounds.Inset(0, 0, extra_width, 0); 713 } 714 } 715 716 return bounds; 717 } 718 719 const gfx::ImageSkia& TextButton::GetImageToPaint() const { 720 if (show_multiple_icon_states_) { 721 if (has_hover_icon_ && (state() == STATE_HOVERED)) 722 return icon_hover_; 723 if (has_pushed_icon_ && (state() == STATE_PRESSED)) 724 return icon_pushed_; 725 } 726 return icon_; 727 } 728 729 } // namespace views 730