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

Side by Side Diff: ui/views/controls/combobox/combobox.cc

Issue 1904753002: MenuButton: support Mac look & feel (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: cache arrow images Created 4 years, 7 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 unified diff | Download patch
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "ui/views/controls/combobox/combobox.h" 5 #include "ui/views/controls/combobox/combobox.h"
6 6
7 #include <stddef.h> 7 #include <stddef.h>
8 8
9 #include <utility> 9 #include <utility>
10 10
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
47 namespace { 47 namespace {
48 48
49 // Menu border widths 49 // Menu border widths
50 const int kMenuBorderWidthLeft = 1; 50 const int kMenuBorderWidthLeft = 1;
51 const int kMenuBorderWidthTop = 1; 51 const int kMenuBorderWidthTop = 1;
52 const int kMenuBorderWidthRight = 1; 52 const int kMenuBorderWidthRight = 1;
53 53
54 // Limit how small a combobox can be. 54 // Limit how small a combobox can be.
55 const int kMinComboboxWidth = 25; 55 const int kMinComboboxWidth = 25;
56 56
57 // Size of the combobox arrow margins
58 const int kDisclosureArrowLeftPadding = 7;
59 const int kDisclosureArrowRightPadding = 7;
60 const int kDisclosureArrowButtonLeftPadding = 11;
61 const int kDisclosureArrowButtonRightPadding = 12;
62
63 // Define the id of the first item in the menu (since it needs to be > 0) 57 // Define the id of the first item in the menu (since it needs to be > 0)
64 const int kFirstMenuItemId = 1000; 58 const int kFirstMenuItemId = 1000;
65 59
66 // Used to indicate that no item is currently selected by the user. 60 // Used to indicate that no item is currently selected by the user.
67 const int kNoSelection = -1; 61 const int kNoSelection = -1;
68 62
69 const int kBodyButtonImages[] = IMAGE_GRID(IDR_COMBOBOX_BUTTON); 63 const int kBodyButtonImages[] = IMAGE_GRID(IDR_COMBOBOX_BUTTON);
70 const int kHoveredBodyButtonImages[] = IMAGE_GRID(IDR_COMBOBOX_BUTTON_H); 64 const int kHoveredBodyButtonImages[] = IMAGE_GRID(IDR_COMBOBOX_BUTTON_H);
71 const int kPressedBodyButtonImages[] = IMAGE_GRID(IDR_COMBOBOX_BUTTON_P); 65 const int kPressedBodyButtonImages[] = IMAGE_GRID(IDR_COMBOBOX_BUTTON_P);
72 const int kFocusedBodyButtonImages[] = IMAGE_GRID(IDR_COMBOBOX_BUTTON_F); 66 const int kFocusedBodyButtonImages[] = IMAGE_GRID(IDR_COMBOBOX_BUTTON_F);
(...skipping 262 matching lines...) Expand 10 before | Expand all | Expand 10 after
335 329
336 Combobox* owner_; // Weak. Owns this. 330 Combobox* owner_; // Weak. Owns this.
337 ui::ComboboxModel* model_; // Weak. 331 ui::ComboboxModel* model_; // Weak.
338 332
339 DISALLOW_COPY_AND_ASSIGN(ComboboxMenuModelAdapter); 333 DISALLOW_COPY_AND_ASSIGN(ComboboxMenuModelAdapter);
340 }; 334 };
341 335
342 //////////////////////////////////////////////////////////////////////////////// 336 ////////////////////////////////////////////////////////////////////////////////
343 // Combobox, public: 337 // Combobox, public:
344 338
345 Combobox::Combobox(ui::ComboboxModel* model) 339 Combobox::Combobox(ui::ComboboxModel* model) : Combobox(model, STYLE_NORMAL) {}
340
341 Combobox::Combobox(ui::ComboboxModel* model, Style style)
346 : model_(model), 342 : model_(model),
347 style_(STYLE_NORMAL), 343 style_(style),
348 listener_(NULL), 344 listener_(NULL),
349 selected_index_(model_->GetDefaultIndex()), 345 selected_index_(model_->GetDefaultIndex()),
350 invalid_(false), 346 invalid_(false),
351 menu_model_adapter_(new ComboboxMenuModelAdapter(this, model)), 347 menu_model_adapter_(new ComboboxMenuModelAdapter(this, model)),
352 text_button_(new TransparentButton(this)), 348 text_button_(new TransparentButton(this)),
353 arrow_button_(new TransparentButton(this)), 349 arrow_button_(new TransparentButton(this)),
350 enabled_arrow_image_(PlatformStyle::CreateComboboxArrow(true, style)),
351 disabled_arrow_image_(PlatformStyle::CreateComboboxArrow(false, style)),
354 weak_ptr_factory_(this) { 352 weak_ptr_factory_(this) {
355 ModelChanged(); 353 ModelChanged();
356 SetFocusable(true); 354 SetFocusable(true);
357 UpdateBorder(); 355 UpdateBorder();
358 // set_background() takes ownership but takes a raw pointer. 356 // set_background() takes ownership but takes a raw pointer.
359 std::unique_ptr<Background> b = PlatformStyle::CreateComboboxBackground(); 357 std::unique_ptr<Background> b =
358 PlatformStyle::CreateComboboxBackground(GetShoulderWidth());
360 set_background(b.release()); 359 set_background(b.release());
361 360
362 // Initialize the button images. 361 // Initialize the button images.
363 Button::ButtonState button_states[] = { 362 Button::ButtonState button_states[] = {
364 Button::STATE_DISABLED, 363 Button::STATE_DISABLED,
365 Button::STATE_NORMAL, 364 Button::STATE_NORMAL,
366 Button::STATE_HOVERED, 365 Button::STATE_HOVERED,
367 Button::STATE_PRESSED, 366 Button::STATE_PRESSED,
368 }; 367 };
369 for (int i = 0; i < 2; i++) { 368 for (int i = 0; i < 2; i++) {
370 for (size_t state_index = 0; state_index < arraysize(button_states); 369 for (size_t state_index = 0; state_index < arraysize(button_states);
371 state_index++) { 370 state_index++) {
372 Button::ButtonState state = button_states[state_index]; 371 Button::ButtonState state = button_states[state_index];
373 size_t num; 372 size_t num;
374 bool focused = !!i; 373 bool focused = !!i;
375 const int* ids = GetBodyButtonImageIds(focused, state, &num); 374 const int* ids = GetBodyButtonImageIds(focused, state, &num);
376 body_button_painters_[focused][state].reset( 375 body_button_painters_[focused][state].reset(
377 Painter::CreateImageGridPainter(ids)); 376 Painter::CreateImageGridPainter(ids));
378 menu_button_images_[focused][state] = GetMenuButtonImages(focused, state); 377 menu_button_images_[focused][state] = GetMenuButtonImages(focused, state);
379 } 378 }
380 } 379 }
381 380
382 text_button_->SetVisible(true); 381 text_button_->SetVisible(true);
383 arrow_button_->SetVisible(true); 382 arrow_button_->SetVisible(true);
384 text_button_->SetFocusable(false); 383 text_button_->SetFocusable(false);
385 arrow_button_->SetFocusable(false); 384 arrow_button_->SetFocusable(false);
386 AddChildView(text_button_); 385 AddChildView(text_button_);
387 AddChildView(arrow_button_); 386 AddChildView(arrow_button_);
387
388 if (style_ == STYLE_ACTION)
tapted 2016/04/26 05:05:57 Move this to the top of the constructor. Then the
Elly Fong-Jones 2016/04/26 17:41:25 Done.
389 selected_index_ = 0;
390
391 content_size_ = GetContentSize();
392 PreferredSizeChanged();
388 } 393 }
389 394
390 Combobox::~Combobox() { 395 Combobox::~Combobox() {
391 if (GetInputMethod() && selector_.get()) { 396 if (GetInputMethod() && selector_.get()) {
392 // Combobox should have been blurred before destroy. 397 // Combobox should have been blurred before destroy.
393 DCHECK(selector_.get() != GetInputMethod()->GetTextInputClient()); 398 DCHECK(selector_.get() != GetInputMethod()->GetTextInputClient());
394 } 399 }
395 } 400 }
396 401
397 // static 402 // static
398 const gfx::FontList& Combobox::GetFontList() { 403 const gfx::FontList& Combobox::GetFontList() {
399 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); 404 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
400 return rb.GetFontListWithDelta(ui::kLabelFontSizeDelta); 405 return rb.GetFontListWithDelta(ui::kLabelFontSizeDelta);
401 } 406 }
402 407
403 void Combobox::SetStyle(Style style) {
404 if (style_ == style)
405 return;
406
407 style_ = style;
408 if (style_ == STYLE_ACTION)
409 selected_index_ = 0;
410
411 UpdateBorder();
412 content_size_ = GetContentSize();
413 PreferredSizeChanged();
414 }
415
416 void Combobox::ModelChanged() { 408 void Combobox::ModelChanged() {
417 // If the selection is no longer valid (or the model is empty), restore the 409 // If the selection is no longer valid (or the model is empty), restore the
418 // default index. 410 // default index.
419 if (selected_index_ >= model_->GetItemCount() || 411 if (selected_index_ >= model_->GetItemCount() ||
420 model_->GetItemCount() == 0 || 412 model_->GetItemCount() == 0 ||
421 model_->IsItemSeparatorAt(selected_index_)) { 413 model_->IsItemSeparatorAt(selected_index_)) {
422 selected_index_ = model_->GetDefaultIndex(); 414 selected_index_ = model_->GetDefaultIndex();
423 } 415 }
424 416
425 content_size_ = GetContentSize(); 417 content_size_ = GetContentSize();
(...skipping 28 matching lines...) Expand all
454 void Combobox::SetInvalid(bool invalid) { 446 void Combobox::SetInvalid(bool invalid) {
455 if (invalid == invalid_) 447 if (invalid == invalid_)
456 return; 448 return;
457 449
458 invalid_ = invalid; 450 invalid_ = invalid;
459 451
460 UpdateBorder(); 452 UpdateBorder();
461 SchedulePaint(); 453 SchedulePaint();
462 } 454 }
463 455
464 int Combobox::GetArrowButtonWidth() const { 456 int Combobox::GetShoulderWidth() const {
465 return GetDisclosureArrowLeftPadding() + 457 const int kNormalPadding = 7;
466 ArrowSize().width() + 458 const int kActionPadding = 11;
467 GetDisclosureArrowRightPadding(); 459 int padding = style_ == STYLE_NORMAL ? kNormalPadding : kActionPadding;
460 return ArrowSize().width() + 2 * padding;
468 } 461 }
469 462
470 void Combobox::Layout() { 463 void Combobox::Layout() {
471 PrefixDelegate::Layout(); 464 PrefixDelegate::Layout();
472 465
473 gfx::Insets insets = GetInsets(); 466 gfx::Insets insets = GetInsets();
474 int text_button_width = 0; 467 int text_button_width = 0;
475 int arrow_button_width = 0; 468 int arrow_button_width = 0;
476 469
477 switch (style_) { 470 switch (style_) {
478 case STYLE_NORMAL: { 471 case STYLE_NORMAL: {
479 arrow_button_width = width(); 472 arrow_button_width = width();
480 break; 473 break;
481 } 474 }
482 case STYLE_ACTION: { 475 case STYLE_ACTION: {
483 arrow_button_width = GetDisclosureArrowLeftPadding() + 476 arrow_button_width = GetShoulderWidth();
484 ArrowSize().width() +
485 GetDisclosureArrowRightPadding();
486 text_button_width = width() - arrow_button_width; 477 text_button_width = width() - arrow_button_width;
487 break; 478 break;
488 } 479 }
489 } 480 }
490 481
491 int arrow_button_x = std::max(0, text_button_width); 482 int arrow_button_x = std::max(0, text_button_width);
492 text_button_->SetBounds(0, 0, std::max(0, text_button_width), height()); 483 text_button_->SetBounds(0, 0, std::max(0, text_button_width), height());
493 arrow_button_->SetBounds(arrow_button_x, 0, arrow_button_width, height()); 484 arrow_button_->SetBounds(arrow_button_x, 0, arrow_button_width, height());
494 } 485 }
495 486
(...skipping 22 matching lines...) Expand all
518 509
519 gfx::Size Combobox::GetPreferredSize() const { 510 gfx::Size Combobox::GetPreferredSize() const {
520 // The preferred size will drive the local bounds which in turn is used to set 511 // The preferred size will drive the local bounds which in turn is used to set
521 // the minimum width for the dropdown list. 512 // the minimum width for the dropdown list.
522 gfx::Insets insets = GetInsets(); 513 gfx::Insets insets = GetInsets();
523 insets += gfx::Insets(Textfield::kTextPadding, 514 insets += gfx::Insets(Textfield::kTextPadding,
524 Textfield::kTextPadding, 515 Textfield::kTextPadding,
525 Textfield::kTextPadding, 516 Textfield::kTextPadding,
526 Textfield::kTextPadding); 517 Textfield::kTextPadding);
527 int total_width = std::max(kMinComboboxWidth, content_size_.width()) + 518 int total_width = std::max(kMinComboboxWidth, content_size_.width()) +
528 insets.width() + GetDisclosureArrowLeftPadding() + 519 insets.width() + GetShoulderWidth();
529 ArrowSize().width() + GetDisclosureArrowRightPadding();
530 return gfx::Size(total_width, content_size_.height() + insets.height()); 520 return gfx::Size(total_width, content_size_.height() + insets.height());
531 } 521 }
532 522
533 const char* Combobox::GetClassName() const { 523 const char* Combobox::GetClassName() const {
534 return kViewClassName; 524 return kViewClassName;
535 } 525 }
536 526
537 bool Combobox::SkipDefaultKeyEventProcessing(const ui::KeyEvent& e) { 527 bool Combobox::SkipDefaultKeyEventProcessing(const ui::KeyEvent& e) {
538 // Escape should close the drop down list when it is active, not host UI. 528 // Escape should close the drop down list when it is active, not host UI.
539 if (e.key_code() != ui::VKEY_ESCAPE || 529 if (e.key_code() != ui::VKEY_ESCAPE ||
(...skipping 186 matching lines...) Expand 10 before | Expand all | Expand 10 after
726 SkColor text_color = GetNativeTheme()->GetSystemColor( 716 SkColor text_color = GetNativeTheme()->GetSystemColor(
727 enabled() ? ui::NativeTheme::kColorId_LabelEnabledColor : 717 enabled() ? ui::NativeTheme::kColorId_LabelEnabledColor :
728 ui::NativeTheme::kColorId_LabelDisabledColor); 718 ui::NativeTheme::kColorId_LabelDisabledColor);
729 719
730 DCHECK_GE(selected_index_, 0); 720 DCHECK_GE(selected_index_, 0);
731 DCHECK_LT(selected_index_, model()->GetItemCount()); 721 DCHECK_LT(selected_index_, model()->GetItemCount());
732 if (selected_index_ < 0 || selected_index_ > model()->GetItemCount()) 722 if (selected_index_ < 0 || selected_index_ > model()->GetItemCount())
733 selected_index_ = 0; 723 selected_index_ = 0;
734 base::string16 text = model()->GetItemAt(selected_index_); 724 base::string16 text = model()->GetItemAt(selected_index_);
735 725
736 gfx::Size arrow_size = ArrowSize(); 726 int disclosure_arrow_offset = width() - GetShoulderWidth();
737 int disclosure_arrow_offset = width() - arrow_size.width() -
738 GetDisclosureArrowLeftPadding() - GetDisclosureArrowRightPadding();
739 727
740 const gfx::FontList& font_list = Combobox::GetFontList(); 728 const gfx::FontList& font_list = Combobox::GetFontList();
741 int text_width = gfx::GetStringWidth(text, font_list); 729 int text_width = gfx::GetStringWidth(text, font_list);
742 if ((text_width + insets.width()) > disclosure_arrow_offset) 730 if ((text_width + insets.width()) > disclosure_arrow_offset)
743 text_width = disclosure_arrow_offset - insets.width(); 731 text_width = disclosure_arrow_offset - insets.width();
744 732
745 gfx::Rect text_bounds(x, y, text_width, text_height); 733 gfx::Rect text_bounds(x, y, text_width, text_height);
746 AdjustBoundsForRTLUI(&text_bounds); 734 AdjustBoundsForRTLUI(&text_bounds);
747 canvas->DrawStringRect(text, font_list, text_color, text_bounds); 735 canvas->DrawStringRect(text, font_list, text_color, text_bounds);
748 736
749 int arrow_x = disclosure_arrow_offset + GetDisclosureArrowLeftPadding(); 737 gfx::ImageSkia arrow_image =
750 gfx::Rect arrow_bounds(arrow_x, 738 enabled() ? enabled_arrow_image_ : disabled_arrow_image_;
751 height() / 2 - arrow_size.height() / 2, 739 gfx::Rect arrow_bounds(disclosure_arrow_offset, 0, GetShoulderWidth(),
752 arrow_size.width(), 740 height());
753 arrow_size.height()); 741 arrow_bounds.ClampToCenteredSize(ArrowSize());
754 AdjustBoundsForRTLUI(&arrow_bounds); 742 AdjustBoundsForRTLUI(&arrow_bounds);
755 743
756 gfx::ImageSkia arrow_image = PlatformStyle::CreateComboboxArrow(
757 enabled(), style_);
758 canvas->DrawImageInt(arrow_image, arrow_bounds.x(), arrow_bounds.y()); 744 canvas->DrawImageInt(arrow_image, arrow_bounds.x(), arrow_bounds.y());
759 } 745 }
760 746
761 void Combobox::PaintButtons(gfx::Canvas* canvas) { 747 void Combobox::PaintButtons(gfx::Canvas* canvas) {
762 DCHECK(style_ == STYLE_ACTION); 748 DCHECK(style_ == STYLE_ACTION);
763 749
764 gfx::ScopedCanvas scoped_canvas(canvas); 750 gfx::ScopedRTLFlipCanvas scoped_canvas(canvas, bounds());
765 if (base::i18n::IsRTL()) {
766 canvas->Translate(gfx::Vector2d(width(), 0));
767 canvas->Scale(-1, 1);
768 }
769 751
770 bool focused = HasFocus(); 752 bool focused = HasFocus();
771 const std::vector<const gfx::ImageSkia*>& arrow_button_images = 753 const std::vector<const gfx::ImageSkia*>& arrow_button_images =
772 menu_button_images_[focused][ 754 menu_button_images_[focused][
773 arrow_button_->state() == Button::STATE_HOVERED ? 755 arrow_button_->state() == Button::STATE_HOVERED ?
774 Button::STATE_NORMAL : arrow_button_->state()]; 756 Button::STATE_NORMAL : arrow_button_->state()];
775 757
776 int text_button_hover_alpha = 758 int text_button_hover_alpha =
777 text_button_->state() == Button::STATE_PRESSED ? 0 : 759 text_button_->state() == Button::STATE_PRESSED ? 0 :
778 static_cast<int>(static_cast<TransparentButton*>(text_button_)-> 760 static_cast<int>(static_cast<TransparentButton*>(text_button_)->
(...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after
866 848
867 // This combobox may be deleted by the listener. 849 // This combobox may be deleted by the listener.
868 base::WeakPtr<Combobox> weak_ptr = weak_ptr_factory_.GetWeakPtr(); 850 base::WeakPtr<Combobox> weak_ptr = weak_ptr_factory_.GetWeakPtr();
869 if (listener_) 851 if (listener_)
870 listener_->OnPerformAction(this); 852 listener_->OnPerformAction(this);
871 853
872 if (weak_ptr && style_ == STYLE_ACTION) 854 if (weak_ptr && style_ == STYLE_ACTION)
873 selected_index_ = 0; 855 selected_index_ = 0;
874 } 856 }
875 857
876 int Combobox::GetDisclosureArrowLeftPadding() const {
877 switch (style_) {
878 case STYLE_NORMAL:
879 return kDisclosureArrowLeftPadding;
880 case STYLE_ACTION:
881 return kDisclosureArrowButtonLeftPadding;
882 }
883 NOTREACHED();
884 return 0;
885 }
886
887 int Combobox::GetDisclosureArrowRightPadding() const {
888 switch (style_) {
889 case STYLE_NORMAL:
890 return kDisclosureArrowRightPadding;
891 case STYLE_ACTION:
892 return kDisclosureArrowButtonRightPadding;
893 }
894 NOTREACHED();
895 return 0;
896 }
897
898 gfx::Size Combobox::ArrowSize() const { 858 gfx::Size Combobox::ArrowSize() const {
899 return PlatformStyle::CreateComboboxArrow(enabled(), style_).size(); 859 return enabled() ? enabled_arrow_image_.size() : disabled_arrow_image_.size();
tapted 2016/04/26 05:05:57 same here - this should DCHECK that the sizes are
Elly Fong-Jones 2016/04/26 17:41:25 Done.
900 } 860 }
901 861
902 gfx::Size Combobox::GetContentSize() const { 862 gfx::Size Combobox::GetContentSize() const {
903 const gfx::FontList& font_list = GetFontList(); 863 const gfx::FontList& font_list = GetFontList();
904 864
905 int width = 0; 865 int width = 0;
906 for (int i = 0; i < model()->GetItemCount(); ++i) { 866 for (int i = 0; i < model()->GetItemCount(); ++i) {
907 if (model_->IsItemSeparatorAt(i)) 867 if (model_->IsItemSeparatorAt(i))
908 continue; 868 continue;
909 869
910 if (style_ != STYLE_ACTION || i == selected_index_) { 870 if (style_ != STYLE_ACTION || i == selected_index_) {
911 width = std::max( 871 width = std::max(
912 width, 872 width,
913 gfx::GetStringWidth(menu_model_adapter_->GetLabelAt(i), font_list)); 873 gfx::GetStringWidth(menu_model_adapter_->GetLabelAt(i), font_list));
914 } 874 }
915 } 875 }
916 return gfx::Size(width, font_list.GetHeight()); 876 return gfx::Size(width, font_list.GetHeight());
917 } 877 }
918 878
919 PrefixSelector* Combobox::GetPrefixSelector() { 879 PrefixSelector* Combobox::GetPrefixSelector() {
920 if (!selector_) 880 if (!selector_)
921 selector_.reset(new PrefixSelector(this)); 881 selector_.reset(new PrefixSelector(this));
922 return selector_.get(); 882 return selector_.get();
923 } 883 }
924 884
925 } // namespace views 885 } // namespace views
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698