diff options
Diffstat (limited to 'chromium/ui/views/controls/combobox/combobox.cc')
-rw-r--r-- | chromium/ui/views/controls/combobox/combobox.cc | 244 |
1 files changed, 142 insertions, 102 deletions
diff --git a/chromium/ui/views/controls/combobox/combobox.cc b/chromium/ui/views/controls/combobox/combobox.cc index 7feb5ffd0a7..b2a456d4906 100644 --- a/chromium/ui/views/controls/combobox/combobox.cc +++ b/chromium/ui/views/controls/combobox/combobox.cc @@ -4,10 +4,12 @@ #include "ui/views/controls/combobox/combobox.h" +#include "base/bind.h" #include "base/logging.h" +#include "base/message_loop/message_loop_proxy.h" #include "base/strings/utf_string_conversions.h" #include "grit/ui_resources.h" -#include "ui/base/accessibility/accessible_view_state.h" +#include "ui/accessibility/ax_view_state.h" #include "ui/base/models/combobox_model.h" #include "ui/base/resource/resource_bundle.h" #include "ui/events/event.h" @@ -16,6 +18,8 @@ #include "ui/gfx/canvas.h" #include "ui/gfx/image/image.h" #include "ui/gfx/scoped_canvas.h" +#include "ui/gfx/text_utils.h" +#include "ui/native_theme/common_theme.h" #include "ui/native_theme/native_theme.h" #include "ui/views/background.h" #include "ui/views/color_constants.h" @@ -23,6 +27,7 @@ #include "ui/views/controls/button/label_button.h" #include "ui/views/controls/combobox/combobox_listener.h" #include "ui/views/controls/focusable_border.h" +#include "ui/views/controls/menu/menu_item_view.h" #include "ui/views/controls/menu/menu_runner.h" #include "ui/views/controls/menu/menu_runner_handler.h" #include "ui/views/controls/menu/submenu_view.h" @@ -53,8 +58,6 @@ const int kDisclosureArrowButtonRightPadding = 12; // Define the id of the first item in the menu (since it needs to be > 0) const int kFirstMenuItemId = 1000; -const SkColor kInvalidTextColor = SK_ColorWHITE; - // Used to indicate that no item is currently selected by the user. const int kNoSelection = -1; @@ -81,24 +84,6 @@ const int kFocusedPressedMenuButtonImages[] = #undef MENU_IMAGE_GRID -// The background to use for invalid comboboxes. -class InvalidBackground : public Background { - public: - InvalidBackground() {} - virtual ~InvalidBackground() {} - - // Overridden from Background: - virtual void Paint(gfx::Canvas* canvas, View* view) const OVERRIDE { - gfx::Rect bounds(view->GetLocalBounds()); - // Inset by 2 to leave 1 empty pixel between background and border. - bounds.Inset(2, 2, 2, 2); - canvas->FillRect(bounds, kWarningColor); - } - - private: - DISALLOW_COPY_AND_ASSIGN(InvalidBackground); -}; - // The transparent button which holds a button state but is not rendered. class TransparentButton : public CustomButton { public: @@ -108,6 +93,11 @@ class TransparentButton : public CustomButton { } virtual ~TransparentButton() {} + virtual bool OnMousePressed(const ui::MouseEvent& mouse_event) OVERRIDE { + parent()->RequestFocus(); + return true; + } + double GetAnimationValue() const { return hover_animation_->GetCurrentValue(); } @@ -238,15 +228,14 @@ const char Combobox::kViewClassName[] = "views/Combobox"; Combobox::Combobox(ui::ComboboxModel* model) : model_(model), - style_(STYLE_SHOW_DROP_DOWN_ON_CLICK), + style_(STYLE_NORMAL), listener_(NULL), selected_index_(model_->GetDefaultIndex()), invalid_(false), - disclosure_arrow_(ui::ResourceBundle::GetSharedInstance().GetImageNamed( - IDR_MENU_DROPARROW).ToImageSkia()), dropdown_open_(false), text_button_(new TransparentButton(this)), - arrow_button_(new TransparentButton(this)) { + arrow_button_(new TransparentButton(this)), + weak_ptr_factory_(this) { model_->AddObserver(this); UpdateFromModel(); SetFocusable(true); @@ -285,9 +274,9 @@ Combobox::~Combobox() { } // static -const gfx::Font& Combobox::GetFont() { +const gfx::FontList& Combobox::GetFontList() { ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); - return rb.GetFont(ui::ResourceBundle::BaseFont); + return rb.GetFontList(ui::ResourceBundle::BaseFont); } void Combobox::SetStyle(Style style) { @@ -295,8 +284,11 @@ void Combobox::SetStyle(Style style) { return; style_ = style; + if (style_ == STYLE_ACTION) + selected_index_ = 0; UpdateBorder(); + UpdateFromModel(); PreferredSizeChanged(); } @@ -307,11 +299,17 @@ void Combobox::ModelChanged() { } void Combobox::SetSelectedIndex(int index) { + if (style_ == STYLE_ACTION) + return; + selected_index_ = index; SchedulePaint(); } bool Combobox::SelectValue(const base::string16& value) { + if (style_ == STYLE_ACTION) + return false; + for (int i = 0; i < model()->GetItemCount(); ++i) { if (value == model()->GetItemAt(i)) { SetSelectedIndex(i); @@ -321,7 +319,7 @@ bool Combobox::SelectValue(const base::string16& value) { return false; } -void Combobox::SetAccessibleName(const string16& name) { +void Combobox::SetAccessibleName(const base::string16& name) { accessible_name_ = name; } @@ -331,7 +329,6 @@ void Combobox::SetInvalid(bool invalid) { invalid_ = invalid; - set_background(invalid_ ? new InvalidBackground() : NULL); UpdateBorder(); SchedulePaint(); } @@ -350,13 +347,14 @@ void Combobox::Layout() { int arrow_button_width = 0; switch (style_) { - case STYLE_SHOW_DROP_DOWN_ON_CLICK: { + case STYLE_NORMAL: { arrow_button_width = width(); break; } - case STYLE_NOTIFY_ON_CLICK: { + case STYLE_ACTION: { arrow_button_width = GetDisclosureArrowLeftPadding() + - disclosure_arrow_->width() + GetDisclosureArrowRightPadding(); + ArrowSize().width() + + GetDisclosureArrowRightPadding(); text_button_width = width() - arrow_button_width; break; } @@ -377,10 +375,10 @@ bool Combobox::IsCommandEnabled(int id) const { void Combobox::ExecuteCommand(int id) { selected_index_ = MenuCommandToIndex(id); - OnSelectionChanged(); + OnPerformAction(); } -bool Combobox::GetAccelerator(int id, ui::Accelerator* accel) { +bool Combobox::GetAccelerator(int id, ui::Accelerator* accel) const { return false; } @@ -393,26 +391,27 @@ int Combobox::GetSelectedRow() { } void Combobox::SetSelectedRow(int row) { + int prev_index = selected_index_; SetSelectedIndex(row); + if (selected_index_ != prev_index) + OnPerformAction(); } -string16 Combobox::GetTextForRow(int row) { - return model()->IsItemSeparatorAt(row) ? string16() : model()->GetItemAt(row); +base::string16 Combobox::GetTextForRow(int row) { + return model()->IsItemSeparatorAt(row) ? base::string16() : + model()->GetItemAt(row); } //////////////////////////////////////////////////////////////////////////////// // Combobox, View overrides: -gfx::Size Combobox::GetPreferredSize() { - if (content_size_.IsEmpty()) - UpdateFromModel(); - +gfx::Size Combobox::GetPreferredSize() const { // The preferred size will drive the local bounds which in turn is used to set // the minimum width for the dropdown list. gfx::Insets insets = GetInsets(); int total_width = std::max(kMinComboboxWidth, content_size_.width()) + insets.width() + GetDisclosureArrowLeftPadding() + - disclosure_arrow_->width() + GetDisclosureArrowRightPadding(); + ArrowSize().width() + GetDisclosureArrowRightPadding(); return gfx::Size(total_width, content_size_.height() + insets.height()); } @@ -475,7 +474,7 @@ bool Combobox::OnKeyPressed(const ui::KeyEvent& e) { // Click the button only when the button style mode. case ui::VKEY_SPACE: - if (style_ == STYLE_NOTIFY_ON_CLICK) { + if (style_ == STYLE_ACTION) { // When pressing space, the click event will be raised after the key is // released. text_button_->SetState(Button::STATE_PRESSED); @@ -486,9 +485,9 @@ bool Combobox::OnKeyPressed(const ui::KeyEvent& e) { // Click the button only when the button style mode. case ui::VKEY_RETURN: - if (style_ != STYLE_NOTIFY_ON_CLICK) + if (style_ != STYLE_ACTION) return false; - HandleClickEvent(); + OnPerformAction(); break; default: @@ -498,34 +497,35 @@ bool Combobox::OnKeyPressed(const ui::KeyEvent& e) { if (show_menu) { UpdateFromModel(); ShowDropDownMenu(ui::MENU_SOURCE_KEYBOARD); - } else if (new_index != selected_index_ && new_index != kNoSelection) { + } else if (new_index != selected_index_ && new_index != kNoSelection && + style_ != STYLE_ACTION) { DCHECK(!model()->IsItemSeparatorAt(new_index)); selected_index_ = new_index; - OnSelectionChanged(); + OnPerformAction(); } return true; } bool Combobox::OnKeyReleased(const ui::KeyEvent& e) { - if (style_ != STYLE_NOTIFY_ON_CLICK) + if (style_ != STYLE_ACTION) return false; // crbug.com/127520 - if (e.key_code() == ui::VKEY_SPACE) - HandleClickEvent(); + if (e.key_code() == ui::VKEY_SPACE && style_ == STYLE_ACTION) + OnPerformAction(); return false; } void Combobox::OnPaint(gfx::Canvas* canvas) { switch (style_) { - case STYLE_SHOW_DROP_DOWN_ON_CLICK: { + case STYLE_NORMAL: { OnPaintBackground(canvas); PaintText(canvas); OnPaintBorder(canvas); break; } - case STYLE_NOTIFY_ON_CLICK: { + case STYLE_ACTION: { PaintButtons(canvas); PaintText(canvas); break; @@ -548,23 +548,27 @@ void Combobox::OnBlur() { SchedulePaint(); } -void Combobox::GetAccessibleState(ui::AccessibleViewState* state) { - state->role = ui::AccessibilityTypes::ROLE_COMBOBOX; +void Combobox::GetAccessibleState(ui::AXViewState* state) { + state->role = ui::AX_ROLE_COMBO_BOX; state->name = accessible_name_; state->value = model_->GetItemAt(selected_index_); state->index = selected_index_; state->count = model_->GetItemCount(); } -void Combobox::OnModelChanged() { +void Combobox::OnComboboxModelChanged(ui::ComboboxModel* model) { + DCHECK_EQ(model, model_); ModelChanged(); } void Combobox::ButtonPressed(Button* sender, const ui::Event& event) { + if (!enabled()) + return; + RequestFocus(); if (sender == text_button_) { - HandleClickEvent(); + OnPerformAction(); } else { DCHECK_EQ(arrow_button_, sender); // TODO(hajimehoshi): Fix the problem that the arrow button blinks when @@ -583,40 +587,50 @@ void Combobox::ButtonPressed(Button* sender, const ui::Event& event) { } void Combobox::UpdateFromModel() { - int max_width = 0; - const gfx::Font& font = Combobox::GetFont(); + const gfx::FontList& font_list = Combobox::GetFontList(); MenuItemView* menu = new MenuItemView(this); // MenuRunner owns |menu|. dropdown_list_menu_runner_.reset(new MenuRunner(menu)); int num_items = model()->GetItemCount(); + int width = 0; + bool text_item_appended = false; for (int i = 0; i < num_items; ++i) { + // When STYLE_ACTION is used, the first item and the following separators + // are not added to the dropdown menu. It is assumed that the first item is + // always selected and rendered on the top of the action button. if (model()->IsItemSeparatorAt(i)) { - menu->AppendSeparator(); + if (text_item_appended || style_ != STYLE_ACTION) + menu->AppendSeparator(); continue; } - string16 text = model()->GetItemAt(i); + base::string16 text = model()->GetItemAt(i); // Inserting the Unicode formatting characters if necessary so that the // text is displayed correctly in right-to-left UIs. base::i18n::AdjustStringForLocaleDirection(&text); - menu->AppendMenuItem(i + kFirstMenuItemId, text, MenuItemView::NORMAL); - max_width = std::max(max_width, font.GetStringWidth(text)); + if (style_ != STYLE_ACTION || i > 0) { + menu->AppendMenuItem(i + kFirstMenuItemId, text, MenuItemView::NORMAL); + text_item_appended = true; + } + + if (style_ != STYLE_ACTION || i == selected_index_) + width = std::max(width, gfx::GetStringWidth(text, font_list)); } - content_size_.SetSize(max_width, font.GetHeight()); + content_size_.SetSize(width, font_list.GetHeight()); } void Combobox::UpdateBorder() { - FocusableBorder* border = new FocusableBorder(); - if (style_ == STYLE_NOTIFY_ON_CLICK) + scoped_ptr<FocusableBorder> border(new FocusableBorder()); + if (style_ == STYLE_ACTION) border->SetInsets(8, 13, 8, 13); if (invalid_) border->SetColor(kWarningColor); - set_border(border); + SetBorder(border.PassAs<Border>()); } void Combobox::AdjustBoundsForRTLUI(gfx::Rect* rect) const { @@ -632,45 +646,52 @@ void Combobox::PaintText(gfx::Canvas* canvas) { int x = insets.left(); int y = insets.top(); int text_height = height() - insets.height(); - SkColor text_color = invalid() ? kInvalidTextColor : - GetNativeTheme()->GetSystemColor( - ui::NativeTheme::kColorId_LabelEnabledColor); + SkColor text_color = GetNativeTheme()->GetSystemColor( + ui::NativeTheme::kColorId_LabelEnabledColor); DCHECK_GE(selected_index_, 0); DCHECK_LT(selected_index_, model()->GetItemCount()); if (selected_index_ < 0 || selected_index_ > model()->GetItemCount()) selected_index_ = 0; - string16 text = model()->GetItemAt(selected_index_); + base::string16 text = model()->GetItemAt(selected_index_); - int disclosure_arrow_offset = width() - disclosure_arrow_->width() - + gfx::Size arrow_size = ArrowSize(); + int disclosure_arrow_offset = width() - arrow_size.width() - GetDisclosureArrowLeftPadding() - GetDisclosureArrowRightPadding(); - const gfx::Font& font = Combobox::GetFont(); - int text_width = font.GetStringWidth(text); + const gfx::FontList& font_list = Combobox::GetFontList(); + int text_width = gfx::GetStringWidth(text, font_list); if ((text_width + insets.width()) > disclosure_arrow_offset) text_width = disclosure_arrow_offset - insets.width(); gfx::Rect text_bounds(x, y, text_width, text_height); AdjustBoundsForRTLUI(&text_bounds); - canvas->DrawStringInt(text, font, text_color, text_bounds); + canvas->DrawStringRect(text, font_list, text_color, text_bounds); int arrow_x = disclosure_arrow_offset + GetDisclosureArrowLeftPadding(); gfx::Rect arrow_bounds(arrow_x, - height() / 2 - disclosure_arrow_->height() / 2, - disclosure_arrow_->width(), - disclosure_arrow_->height()); + height() / 2 - arrow_size.height() / 2, + arrow_size.width(), + arrow_size.height()); AdjustBoundsForRTLUI(&arrow_bounds); - SkPaint paint; - // This makes the arrow subtractive. - if (invalid()) - paint.setXfermodeMode(SkXfermode::kDstOut_Mode); - canvas->DrawImageInt(*disclosure_arrow_, arrow_bounds.x(), arrow_bounds.y(), - paint); + // TODO(estade): hack alert! Remove this direct call into CommonTheme. For now + // STYLE_ACTION isn't properly themed so we have to override the NativeTheme + // behavior. See crbug.com/384071 + if (style_ == STYLE_ACTION) { + ui::CommonThemePaintComboboxArrow(canvas->sk_canvas(), arrow_bounds); + } else { + ui::NativeTheme::ExtraParams ignored; + GetNativeTheme()->Paint(canvas->sk_canvas(), + ui::NativeTheme::kComboboxArrow, + ui::NativeTheme::kNormal, + arrow_bounds, + ignored); + } } void Combobox::PaintButtons(gfx::Canvas* canvas) { - DCHECK(style_ == STYLE_NOTIFY_ON_CLICK); + DCHECK(style_ == STYLE_ACTION); gfx::ScopedCanvas scoped_canvas(canvas); if (base::i18n::IsRTL()) { @@ -739,10 +760,12 @@ void Combobox::ShowDropDownMenu(ui::MenuSourceType source_type) { gfx::Rect lb = GetLocalBounds(); gfx::Point menu_position(lb.origin()); - // Inset the menu's requested position so the border of the menu lines up - // with the border of the combobox. - menu_position.set_x(menu_position.x() + kMenuBorderWidthLeft); - menu_position.set_y(menu_position.y() + kMenuBorderWidthTop); + if (style_ == STYLE_NORMAL) { + // Inset the menu's requested position so the border of the menu lines up + // with the border of the combobox. + menu_position.set_x(menu_position.x() + kMenuBorderWidthLeft); + menu_position.set_y(menu_position.y() + kMenuBorderWidthTop); + } lb.set_width(lb.width() - (kMenuBorderWidthLeft + kMenuBorderWidthRight)); View::ConvertPointToScreen(this, &menu_position); @@ -757,8 +780,10 @@ void Combobox::ShowDropDownMenu(ui::MenuSourceType source_type) { arrow_button_->SetState(Button::STATE_PRESSED); } dropdown_open_ = true; + MenuAnchorPosition anchor_position = + style_ == STYLE_ACTION ? MENU_ANCHOR_TOPRIGHT : MENU_ANCHOR_TOPLEFT; if (dropdown_list_menu_runner_->RunMenuAt(GetWidget(), NULL, bounds, - MenuItemView::TOPLEFT, source_type, + anchor_position, source_type, MenuRunner::COMBOBOX) == MenuRunner::MENU_DELETED) { return; @@ -774,12 +799,17 @@ void Combobox::ShowDropDownMenu(ui::MenuSourceType source_type) { SetMouseHandler(NULL); } -void Combobox::OnSelectionChanged() { - NotifyAccessibilityEvent(ui::AccessibilityTypes::EVENT_VALUE_CHANGED, false); +void Combobox::OnPerformAction() { + NotifyAccessibilityEvent(ui::AX_EVENT_VALUE_CHANGED, false); SchedulePaint(); + + // This combobox may be deleted by the listener. + base::WeakPtr<Combobox> weak_ptr = weak_ptr_factory_.GetWeakPtr(); if (listener_) - listener_->OnSelectedIndexChanged(this); - // |this| may now be deleted. + listener_->OnPerformAction(this); + + if (weak_ptr && style_ == STYLE_ACTION) + selected_index_ = 0; } int Combobox::MenuCommandToIndex(int menu_command_id) const { @@ -792,9 +822,9 @@ int Combobox::MenuCommandToIndex(int menu_command_id) const { int Combobox::GetDisclosureArrowLeftPadding() const { switch (style_) { - case STYLE_SHOW_DROP_DOWN_ON_CLICK: + case STYLE_NORMAL: return kDisclosureArrowLeftPadding; - case STYLE_NOTIFY_ON_CLICK: + case STYLE_ACTION: return kDisclosureArrowButtonLeftPadding; } NOTREACHED(); @@ -803,21 +833,31 @@ int Combobox::GetDisclosureArrowLeftPadding() const { int Combobox::GetDisclosureArrowRightPadding() const { switch (style_) { - case STYLE_SHOW_DROP_DOWN_ON_CLICK: + case STYLE_NORMAL: return kDisclosureArrowRightPadding; - case STYLE_NOTIFY_ON_CLICK: + case STYLE_ACTION: return kDisclosureArrowButtonRightPadding; } NOTREACHED(); return 0; } -void Combobox::HandleClickEvent() { - if (style_ != STYLE_NOTIFY_ON_CLICK) - return; - - if (listener_) - listener_->OnComboboxTextButtonClicked(this); +gfx::Size Combobox::ArrowSize() const { +#if defined(OS_LINUX) && !defined(OS_CHROMEOS) + // TODO(estade): hack alert! This should always use GetNativeTheme(). For now + // STYLE_ACTION isn't properly themed so we have to override the NativeTheme + // behavior. See crbug.com/384071 + const ui::NativeTheme* native_theme_for_arrow = style_ == STYLE_ACTION ? + ui::NativeTheme::instance() : + GetNativeTheme(); +#else + const ui::NativeTheme* native_theme_for_arrow = GetNativeTheme(); +#endif + + ui::NativeTheme::ExtraParams ignored; + return native_theme_for_arrow->GetPartSize(ui::NativeTheme::kComboboxArrow, + ui::NativeTheme::kNormal, + ignored); } } // namespace views |