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

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: Add basic tests 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, Style style)
346 : model_(model), 340 : model_(model),
347 style_(STYLE_NORMAL), 341 style_(style),
348 listener_(NULL), 342 listener_(NULL),
349 selected_index_(model_->GetDefaultIndex()), 343 selected_index_(model_->GetDefaultIndex()),
350 invalid_(false), 344 invalid_(false),
351 menu_model_adapter_(new ComboboxMenuModelAdapter(this, model)), 345 menu_model_adapter_(new ComboboxMenuModelAdapter(this, model)),
352 text_button_(new TransparentButton(this)), 346 text_button_(new TransparentButton(this)),
353 arrow_button_(new TransparentButton(this)), 347 arrow_button_(new TransparentButton(this)),
354 weak_ptr_factory_(this) { 348 weak_ptr_factory_(this) {
349 if (style_ == STYLE_ACTION)
350 selected_index_ = 0;
351
355 ModelChanged(); 352 ModelChanged();
356 SetFocusable(true); 353 SetFocusable(true);
357 UpdateBorder(); 354 UpdateBorder();
355 arrow_image_ = PlatformStyle::CreateComboboxArrow(enabled(), style);
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++) {
(...skipping 23 matching lines...) Expand all
393 DCHECK(selector_.get() != GetInputMethod()->GetTextInputClient()); 392 DCHECK(selector_.get() != GetInputMethod()->GetTextInputClient());
394 } 393 }
395 } 394 }
396 395
397 // static 396 // static
398 const gfx::FontList& Combobox::GetFontList() { 397 const gfx::FontList& Combobox::GetFontList() {
399 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); 398 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
400 return rb.GetFontListWithDelta(ui::kLabelFontSizeDelta); 399 return rb.GetFontListWithDelta(ui::kLabelFontSizeDelta);
401 } 400 }
402 401
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() { 402 void Combobox::ModelChanged() {
417 // If the selection is no longer valid (or the model is empty), restore the 403 // If the selection is no longer valid (or the model is empty), restore the
418 // default index. 404 // default index.
419 if (selected_index_ >= model_->GetItemCount() || 405 if (selected_index_ >= model_->GetItemCount() ||
420 model_->GetItemCount() == 0 || 406 model_->GetItemCount() == 0 ||
421 model_->IsItemSeparatorAt(selected_index_)) { 407 model_->IsItemSeparatorAt(selected_index_)) {
422 selected_index_ = model_->GetDefaultIndex(); 408 selected_index_ = model_->GetDefaultIndex();
423 } 409 }
424 410
425 content_size_ = GetContentSize(); 411 content_size_ = GetContentSize();
(...skipping 28 matching lines...) Expand all
454 void Combobox::SetInvalid(bool invalid) { 440 void Combobox::SetInvalid(bool invalid) {
455 if (invalid == invalid_) 441 if (invalid == invalid_)
456 return; 442 return;
457 443
458 invalid_ = invalid; 444 invalid_ = invalid;
459 445
460 UpdateBorder(); 446 UpdateBorder();
461 SchedulePaint(); 447 SchedulePaint();
462 } 448 }
463 449
464 int Combobox::GetArrowButtonWidth() const {
465 return GetDisclosureArrowLeftPadding() +
466 ArrowSize().width() +
467 GetDisclosureArrowRightPadding();
468 }
469
470 void Combobox::Layout() { 450 void Combobox::Layout() {
471 PrefixDelegate::Layout(); 451 PrefixDelegate::Layout();
472 452
473 gfx::Insets insets = GetInsets(); 453 gfx::Insets insets = GetInsets();
474 int text_button_width = 0; 454 int text_button_width = 0;
475 int arrow_button_width = 0; 455 int arrow_button_width = 0;
476 456
477 switch (style_) { 457 switch (style_) {
478 case STYLE_NORMAL: { 458 case STYLE_NORMAL: {
479 arrow_button_width = width(); 459 arrow_button_width = width();
480 break; 460 break;
481 } 461 }
482 case STYLE_ACTION: { 462 case STYLE_ACTION: {
483 arrow_button_width = GetDisclosureArrowLeftPadding() + 463 arrow_button_width = GetShoulderWidth();
484 ArrowSize().width() +
485 GetDisclosureArrowRightPadding();
486 text_button_width = width() - arrow_button_width; 464 text_button_width = width() - arrow_button_width;
487 break; 465 break;
488 } 466 }
489 } 467 }
490 468
491 int arrow_button_x = std::max(0, text_button_width); 469 int arrow_button_x = std::max(0, text_button_width);
492 text_button_->SetBounds(0, 0, std::max(0, text_button_width), height()); 470 text_button_->SetBounds(0, 0, std::max(0, text_button_width), height());
493 arrow_button_->SetBounds(arrow_button_x, 0, arrow_button_width, height()); 471 arrow_button_->SetBounds(arrow_button_x, 0, arrow_button_width, height());
494 } 472 }
495 473
474 void Combobox::OnEnabledChanged() {
475 PrefixDelegate::OnEnabledChanged();
476 arrow_image_ = PlatformStyle::CreateComboboxArrow(enabled(), style_);
477 }
478
496 int Combobox::GetRowCount() { 479 int Combobox::GetRowCount() {
497 return model()->GetItemCount(); 480 return model()->GetItemCount();
498 } 481 }
499 482
500 int Combobox::GetSelectedRow() { 483 int Combobox::GetSelectedRow() {
501 return selected_index_; 484 return selected_index_;
502 } 485 }
503 486
504 void Combobox::SetSelectedRow(int row) { 487 void Combobox::SetSelectedRow(int row) {
505 int prev_index = selected_index_; 488 int prev_index = selected_index_;
(...skipping 12 matching lines...) Expand all
518 501
519 gfx::Size Combobox::GetPreferredSize() const { 502 gfx::Size Combobox::GetPreferredSize() const {
520 // The preferred size will drive the local bounds which in turn is used to set 503 // The preferred size will drive the local bounds which in turn is used to set
521 // the minimum width for the dropdown list. 504 // the minimum width for the dropdown list.
522 gfx::Insets insets = GetInsets(); 505 gfx::Insets insets = GetInsets();
523 insets += gfx::Insets(Textfield::kTextPadding, 506 insets += gfx::Insets(Textfield::kTextPadding,
524 Textfield::kTextPadding, 507 Textfield::kTextPadding,
525 Textfield::kTextPadding, 508 Textfield::kTextPadding,
526 Textfield::kTextPadding); 509 Textfield::kTextPadding);
527 int total_width = std::max(kMinComboboxWidth, content_size_.width()) + 510 int total_width = std::max(kMinComboboxWidth, content_size_.width()) +
528 insets.width() + GetDisclosureArrowLeftPadding() + 511 insets.width() + GetShoulderWidth();
529 ArrowSize().width() + GetDisclosureArrowRightPadding();
530 return gfx::Size(total_width, content_size_.height() + insets.height()); 512 return gfx::Size(total_width, content_size_.height() + insets.height());
531 } 513 }
532 514
533 const char* Combobox::GetClassName() const { 515 const char* Combobox::GetClassName() const {
534 return kViewClassName; 516 return kViewClassName;
535 } 517 }
536 518
537 bool Combobox::SkipDefaultKeyEventProcessing(const ui::KeyEvent& e) { 519 bool Combobox::SkipDefaultKeyEventProcessing(const ui::KeyEvent& e) {
538 // Escape should close the drop down list when it is active, not host UI. 520 // Escape should close the drop down list when it is active, not host UI.
539 if (e.key_code() != ui::VKEY_ESCAPE || 521 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( 708 SkColor text_color = GetNativeTheme()->GetSystemColor(
727 enabled() ? ui::NativeTheme::kColorId_LabelEnabledColor : 709 enabled() ? ui::NativeTheme::kColorId_LabelEnabledColor :
728 ui::NativeTheme::kColorId_LabelDisabledColor); 710 ui::NativeTheme::kColorId_LabelDisabledColor);
729 711
730 DCHECK_GE(selected_index_, 0); 712 DCHECK_GE(selected_index_, 0);
731 DCHECK_LT(selected_index_, model()->GetItemCount()); 713 DCHECK_LT(selected_index_, model()->GetItemCount());
732 if (selected_index_ < 0 || selected_index_ > model()->GetItemCount()) 714 if (selected_index_ < 0 || selected_index_ > model()->GetItemCount())
733 selected_index_ = 0; 715 selected_index_ = 0;
734 base::string16 text = model()->GetItemAt(selected_index_); 716 base::string16 text = model()->GetItemAt(selected_index_);
735 717
736 gfx::Size arrow_size = ArrowSize(); 718 int disclosure_arrow_offset = width() - GetShoulderWidth();
737 int disclosure_arrow_offset = width() - arrow_size.width() -
738 GetDisclosureArrowLeftPadding() - GetDisclosureArrowRightPadding();
739 719
740 const gfx::FontList& font_list = Combobox::GetFontList(); 720 const gfx::FontList& font_list = Combobox::GetFontList();
741 int text_width = gfx::GetStringWidth(text, font_list); 721 int text_width = gfx::GetStringWidth(text, font_list);
742 if ((text_width + insets.width()) > disclosure_arrow_offset) 722 if ((text_width + insets.width()) > disclosure_arrow_offset)
743 text_width = disclosure_arrow_offset - insets.width(); 723 text_width = disclosure_arrow_offset - insets.width();
744 724
745 gfx::Rect text_bounds(x, y, text_width, text_height); 725 gfx::Rect text_bounds(x, y, text_width, text_height);
746 AdjustBoundsForRTLUI(&text_bounds); 726 AdjustBoundsForRTLUI(&text_bounds);
747 canvas->DrawStringRect(text, font_list, text_color, text_bounds); 727 canvas->DrawStringRect(text, font_list, text_color, text_bounds);
748 728
749 int arrow_x = disclosure_arrow_offset + GetDisclosureArrowLeftPadding(); 729 gfx::Rect arrow_bounds(disclosure_arrow_offset, 0, GetShoulderWidth(),
750 gfx::Rect arrow_bounds(arrow_x, 730 height());
751 height() / 2 - arrow_size.height() / 2, 731 arrow_bounds.ClampToCenteredSize(ArrowSize());
752 arrow_size.width(),
753 arrow_size.height());
754 AdjustBoundsForRTLUI(&arrow_bounds); 732 AdjustBoundsForRTLUI(&arrow_bounds);
755 733
756 gfx::ImageSkia arrow_image = PlatformStyle::CreateComboboxArrow( 734 canvas->DrawImageInt(arrow_image_, arrow_bounds.x(), arrow_bounds.y());
757 enabled(), style_);
758 canvas->DrawImageInt(arrow_image, arrow_bounds.x(), arrow_bounds.y());
759 } 735 }
760 736
761 void Combobox::PaintButtons(gfx::Canvas* canvas) { 737 void Combobox::PaintButtons(gfx::Canvas* canvas) {
762 DCHECK(style_ == STYLE_ACTION); 738 DCHECK(style_ == STYLE_ACTION);
763 739
764 gfx::ScopedCanvas scoped_canvas(canvas); 740 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 741
770 bool focused = HasFocus(); 742 bool focused = HasFocus();
771 const std::vector<const gfx::ImageSkia*>& arrow_button_images = 743 const std::vector<const gfx::ImageSkia*>& arrow_button_images =
772 menu_button_images_[focused][ 744 menu_button_images_[focused][
773 arrow_button_->state() == Button::STATE_HOVERED ? 745 arrow_button_->state() == Button::STATE_HOVERED ?
774 Button::STATE_NORMAL : arrow_button_->state()]; 746 Button::STATE_NORMAL : arrow_button_->state()];
775 747
776 int text_button_hover_alpha = 748 int text_button_hover_alpha =
777 text_button_->state() == Button::STATE_PRESSED ? 0 : 749 text_button_->state() == Button::STATE_PRESSED ? 0 :
778 static_cast<int>(static_cast<TransparentButton*>(text_button_)-> 750 static_cast<int>(static_cast<TransparentButton*>(text_button_)->
(...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after
866 838
867 // This combobox may be deleted by the listener. 839 // This combobox may be deleted by the listener.
868 base::WeakPtr<Combobox> weak_ptr = weak_ptr_factory_.GetWeakPtr(); 840 base::WeakPtr<Combobox> weak_ptr = weak_ptr_factory_.GetWeakPtr();
869 if (listener_) 841 if (listener_)
870 listener_->OnPerformAction(this); 842 listener_->OnPerformAction(this);
871 843
872 if (weak_ptr && style_ == STYLE_ACTION) 844 if (weak_ptr && style_ == STYLE_ACTION)
873 selected_index_ = 0; 845 selected_index_ = 0;
874 } 846 }
875 847
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 { 848 gfx::Size Combobox::ArrowSize() const {
899 return PlatformStyle::CreateComboboxArrow(enabled(), style_).size(); 849 return arrow_image_.size();
900 } 850 }
901 851
902 gfx::Size Combobox::GetContentSize() const { 852 gfx::Size Combobox::GetContentSize() const {
903 const gfx::FontList& font_list = GetFontList(); 853 const gfx::FontList& font_list = GetFontList();
904 854
905 int width = 0; 855 int width = 0;
906 for (int i = 0; i < model()->GetItemCount(); ++i) { 856 for (int i = 0; i < model()->GetItemCount(); ++i) {
907 if (model_->IsItemSeparatorAt(i)) 857 if (model_->IsItemSeparatorAt(i))
908 continue; 858 continue;
909 859
910 if (style_ != STYLE_ACTION || i == selected_index_) { 860 if (style_ != STYLE_ACTION || i == selected_index_) {
911 width = std::max( 861 width = std::max(
912 width, 862 width,
913 gfx::GetStringWidth(menu_model_adapter_->GetLabelAt(i), font_list)); 863 gfx::GetStringWidth(menu_model_adapter_->GetLabelAt(i), font_list));
914 } 864 }
915 } 865 }
916 return gfx::Size(width, font_list.GetHeight()); 866 return gfx::Size(width, font_list.GetHeight());
917 } 867 }
918 868
919 PrefixSelector* Combobox::GetPrefixSelector() { 869 PrefixSelector* Combobox::GetPrefixSelector() {
920 if (!selector_) 870 if (!selector_)
921 selector_.reset(new PrefixSelector(this)); 871 selector_.reset(new PrefixSelector(this));
922 return selector_.get(); 872 return selector_.get();
923 } 873 }
924 874
875 int Combobox::GetShoulderWidth() const {
876 const int kNormalPadding = 7;
877 const int kActionPadding = 11;
878 int padding = style_ == STYLE_NORMAL ? kNormalPadding : kActionPadding;
879 return ArrowSize().width() + 2 * padding;
880 }
881
925 } // namespace views 882 } // namespace views
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698