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.
5 #include "ui/views/controls/combobox/combobox.h"
8 #include "base/logging.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "ui/accessibility/ax_view_state.h"
11 #include "ui/base/ime/input_method.h"
12 #include "ui/base/models/combobox_model.h"
13 #include "ui/base/resource/resource_bundle.h"
14 #include "ui/events/event.h"
15 #include "ui/events/keycodes/keyboard_codes.h"
16 #include "ui/gfx/animation/throb_animation.h"
17 #include "ui/gfx/canvas.h"
18 #include "ui/gfx/image/image.h"
19 #include "ui/gfx/scoped_canvas.h"
20 #include "ui/gfx/text_utils.h"
21 #include "ui/native_theme/common_theme.h"
22 #include "ui/native_theme/native_theme.h"
23 #include "ui/resources/grit/ui_resources.h"
24 #include "ui/views/background.h"
25 #include "ui/views/color_constants.h"
26 #include "ui/views/controls/button/custom_button.h"
27 #include "ui/views/controls/button/label_button.h"
28 #include "ui/views/controls/combobox/combobox_listener.h"
29 #include "ui/views/controls/focusable_border.h"
30 #include "ui/views/controls/menu/menu_item_view.h"
31 #include "ui/views/controls/menu/menu_runner.h"
32 #include "ui/views/controls/menu/menu_runner_handler.h"
33 #include "ui/views/controls/menu/submenu_view.h"
34 #include "ui/views/controls/prefix_selector.h"
35 #include "ui/views/controls/textfield/textfield.h"
36 #include "ui/views/mouse_constants.h"
37 #include "ui/views/painter.h"
38 #include "ui/views/resources/grit/views_resources.h"
39 #include "ui/views/widget/widget.h"
46 const int kMenuBorderWidthLeft
= 1;
47 const int kMenuBorderWidthTop
= 1;
48 const int kMenuBorderWidthRight
= 1;
50 // Limit how small a combobox can be.
51 const int kMinComboboxWidth
= 25;
53 // Size of the combobox arrow margins
54 const int kDisclosureArrowLeftPadding
= 7;
55 const int kDisclosureArrowRightPadding
= 7;
56 const int kDisclosureArrowButtonLeftPadding
= 11;
57 const int kDisclosureArrowButtonRightPadding
= 12;
59 // Define the id of the first item in the menu (since it needs to be > 0)
60 const int kFirstMenuItemId
= 1000;
62 // Used to indicate that no item is currently selected by the user.
63 const int kNoSelection
= -1;
65 const int kBodyButtonImages
[] = IMAGE_GRID(IDR_COMBOBOX_BUTTON
);
66 const int kHoveredBodyButtonImages
[] = IMAGE_GRID(IDR_COMBOBOX_BUTTON_H
);
67 const int kPressedBodyButtonImages
[] = IMAGE_GRID(IDR_COMBOBOX_BUTTON_P
);
68 const int kFocusedBodyButtonImages
[] = IMAGE_GRID(IDR_COMBOBOX_BUTTON_F
);
69 const int kFocusedHoveredBodyButtonImages
[] =
70 IMAGE_GRID(IDR_COMBOBOX_BUTTON_F_H
);
71 const int kFocusedPressedBodyButtonImages
[] =
72 IMAGE_GRID(IDR_COMBOBOX_BUTTON_F_P
);
74 #define MENU_IMAGE_GRID(x) { \
75 x ## _MENU_TOP, x ## _MENU_CENTER, x ## _MENU_BOTTOM, }
77 const int kMenuButtonImages
[] = MENU_IMAGE_GRID(IDR_COMBOBOX_BUTTON
);
78 const int kHoveredMenuButtonImages
[] = MENU_IMAGE_GRID(IDR_COMBOBOX_BUTTON_H
);
79 const int kPressedMenuButtonImages
[] = MENU_IMAGE_GRID(IDR_COMBOBOX_BUTTON_P
);
80 const int kFocusedMenuButtonImages
[] = MENU_IMAGE_GRID(IDR_COMBOBOX_BUTTON_F
);
81 const int kFocusedHoveredMenuButtonImages
[] =
82 MENU_IMAGE_GRID(IDR_COMBOBOX_BUTTON_F_H
);
83 const int kFocusedPressedMenuButtonImages
[] =
84 MENU_IMAGE_GRID(IDR_COMBOBOX_BUTTON_F_P
);
86 #undef MENU_IMAGE_GRID
88 // The transparent button which holds a button state but is not rendered.
89 class TransparentButton
: public CustomButton
{
91 TransparentButton(ButtonListener
* listener
)
92 : CustomButton(listener
) {
93 SetAnimationDuration(LabelButton::kHoverAnimationDurationMs
);
95 ~TransparentButton() override
{}
97 bool OnMousePressed(const ui::MouseEvent
& mouse_event
) override
{
98 parent()->RequestFocus();
102 double GetAnimationValue() const {
103 return hover_animation_
->GetCurrentValue();
107 DISALLOW_COPY_AND_ASSIGN(TransparentButton
);
110 // Returns the next or previous valid index (depending on |increment|'s value).
111 // Skips separator or disabled indices. Returns -1 if there is no valid adjacent
113 int GetAdjacentIndex(ui::ComboboxModel
* model
, int increment
, int index
) {
114 DCHECK(increment
== -1 || increment
== 1);
117 while (index
>= 0 && index
< model
->GetItemCount()) {
118 if (!model
->IsItemSeparatorAt(index
) || !model
->IsItemEnabledAt(index
))
125 // Returns the image resource ids of an array for the body button.
127 // TODO(hajimehoshi): This function should return the images for the 'disabled'
128 // status. (crbug/270052)
129 const int* GetBodyButtonImageIds(bool focused
,
130 Button::ButtonState state
,
135 case Button::STATE_DISABLED
:
136 return focused
? kFocusedBodyButtonImages
: kBodyButtonImages
;
137 case Button::STATE_NORMAL
:
138 return focused
? kFocusedBodyButtonImages
: kBodyButtonImages
;
139 case Button::STATE_HOVERED
:
141 kFocusedHoveredBodyButtonImages
: kHoveredBodyButtonImages
;
142 case Button::STATE_PRESSED
:
144 kFocusedPressedBodyButtonImages
: kPressedBodyButtonImages
;
151 // Returns the image resource ids of an array for the menu button.
152 const int* GetMenuButtonImageIds(bool focused
,
153 Button::ButtonState state
,
158 case Button::STATE_DISABLED
:
159 return focused
? kFocusedMenuButtonImages
: kMenuButtonImages
;
160 case Button::STATE_NORMAL
:
161 return focused
? kFocusedMenuButtonImages
: kMenuButtonImages
;
162 case Button::STATE_HOVERED
:
164 kFocusedHoveredMenuButtonImages
: kHoveredMenuButtonImages
;
165 case Button::STATE_PRESSED
:
167 kFocusedPressedMenuButtonImages
: kPressedMenuButtonImages
;
174 // Returns the images for the menu buttons.
175 std::vector
<const gfx::ImageSkia
*> GetMenuButtonImages(
177 Button::ButtonState state
) {
180 ids
= GetMenuButtonImageIds(focused
, state
, &num_ids
);
181 std::vector
<const gfx::ImageSkia
*> images
;
182 images
.reserve(num_ids
);
183 ui::ResourceBundle
& rb
= ui::ResourceBundle::GetSharedInstance();
184 for (size_t i
= 0; i
< num_ids
; i
++)
185 images
.push_back(rb
.GetImageSkiaNamed(ids
[i
]));
189 // Paints three images in a column at the given location. The center image is
190 // stretched so as to fit the given height.
191 void PaintImagesVertically(gfx::Canvas
* canvas
,
192 const gfx::ImageSkia
& top_image
,
193 const gfx::ImageSkia
& center_image
,
194 const gfx::ImageSkia
& bottom_image
,
195 int x
, int y
, int width
, int height
) {
196 canvas
->DrawImageInt(top_image
,
197 0, 0, top_image
.width(), top_image
.height(),
198 x
, y
, width
, top_image
.height(), false);
199 y
+= top_image
.height();
200 int center_height
= height
- top_image
.height() - bottom_image
.height();
201 canvas
->DrawImageInt(center_image
,
202 0, 0, center_image
.width(), center_image
.height(),
203 x
, y
, width
, center_height
, false);
205 canvas
->DrawImageInt(bottom_image
,
206 0, 0, bottom_image
.width(), bottom_image
.height(),
207 x
, y
, width
, bottom_image
.height(), false);
210 // Paints the arrow button.
211 void PaintArrowButton(
213 const std::vector
<const gfx::ImageSkia
*>& arrow_button_images
,
215 PaintImagesVertically(canvas
,
216 *arrow_button_images
[0],
217 *arrow_button_images
[1],
218 *arrow_button_images
[2],
219 x
, 0, arrow_button_images
[0]->width(), height
);
225 const char Combobox::kViewClassName
[] = "views/Combobox";
227 ////////////////////////////////////////////////////////////////////////////////
230 Combobox::Combobox(ui::ComboboxModel
* model
)
232 style_(STYLE_NORMAL
),
234 selected_index_(model_
->GetDefaultIndex()),
237 dropdown_open_(false),
238 text_button_(new TransparentButton(this)),
239 arrow_button_(new TransparentButton(this)),
240 weak_ptr_factory_(this) {
241 model_
->AddObserver(this);
246 // Initialize the button images.
247 Button::ButtonState button_states
[] = {
248 Button::STATE_DISABLED
,
249 Button::STATE_NORMAL
,
250 Button::STATE_HOVERED
,
251 Button::STATE_PRESSED
,
253 for (int i
= 0; i
< 2; i
++) {
254 for (size_t state_index
= 0; state_index
< arraysize(button_states
);
256 Button::ButtonState state
= button_states
[state_index
];
259 const int* ids
= GetBodyButtonImageIds(focused
, state
, &num
);
260 body_button_painters_
[focused
][state
].reset(
261 Painter::CreateImageGridPainter(ids
));
262 menu_button_images_
[focused
][state
] = GetMenuButtonImages(focused
, state
);
266 text_button_
->SetVisible(true);
267 arrow_button_
->SetVisible(true);
268 text_button_
->SetFocusable(false);
269 arrow_button_
->SetFocusable(false);
270 AddChildView(text_button_
);
271 AddChildView(arrow_button_
);
274 Combobox::~Combobox() {
275 model_
->RemoveObserver(this);
277 if (GetInputMethod() && selector_
.get()) {
278 // Combobox should have been blurred before destroy.
279 DCHECK(selector_
.get() != GetInputMethod()->GetTextInputClient());
284 const gfx::FontList
& Combobox::GetFontList() {
285 ui::ResourceBundle
& rb
= ui::ResourceBundle::GetSharedInstance();
286 return rb
.GetFontList(ui::ResourceBundle::BaseFont
);
289 void Combobox::SetStyle(Style style
) {
294 if (style_
== STYLE_ACTION
)
299 PreferredSizeChanged();
302 void Combobox::ModelChanged() {
303 selected_index_
= std::min(0, model_
->GetItemCount());
305 PreferredSizeChanged();
308 void Combobox::SetSelectedIndex(int index
) {
309 if (style_
== STYLE_ACTION
)
312 selected_index_
= index
;
316 bool Combobox::SelectValue(const base::string16
& value
) {
317 if (style_
== STYLE_ACTION
)
320 for (int i
= 0; i
< model()->GetItemCount(); ++i
) {
321 if (value
== model()->GetItemAt(i
)) {
329 void Combobox::SetAccessibleName(const base::string16
& name
) {
330 accessible_name_
= name
;
333 void Combobox::SetInvalid(bool invalid
) {
334 if (invalid
== invalid_
)
343 void Combobox::Layout() {
344 PrefixDelegate::Layout();
346 gfx::Insets insets
= GetInsets();
347 int text_button_width
= 0;
348 int arrow_button_width
= 0;
352 arrow_button_width
= width();
356 arrow_button_width
= GetDisclosureArrowLeftPadding() +
357 ArrowSize().width() +
358 GetDisclosureArrowRightPadding();
359 text_button_width
= width() - arrow_button_width
;
364 int arrow_button_x
= std::max(0, text_button_width
);
365 text_button_
->SetBounds(0, 0, std::max(0, text_button_width
), height());
366 arrow_button_
->SetBounds(arrow_button_x
, 0, arrow_button_width
, height());
369 bool Combobox::IsItemChecked(int id
) const {
373 bool Combobox::IsCommandEnabled(int id
) const {
374 return model()->IsItemEnabledAt(MenuCommandToIndex(id
));
377 void Combobox::ExecuteCommand(int id
) {
378 selected_index_
= MenuCommandToIndex(id
);
382 bool Combobox::GetAccelerator(int id
, ui::Accelerator
* accel
) const {
386 int Combobox::GetRowCount() {
387 return model()->GetItemCount();
390 int Combobox::GetSelectedRow() {
391 return selected_index_
;
394 void Combobox::SetSelectedRow(int row
) {
395 int prev_index
= selected_index_
;
396 SetSelectedIndex(row
);
397 if (selected_index_
!= prev_index
)
401 base::string16
Combobox::GetTextForRow(int row
) {
402 return model()->IsItemSeparatorAt(row
) ? base::string16() :
403 model()->GetItemAt(row
);
406 ////////////////////////////////////////////////////////////////////////////////
407 // Combobox, View overrides:
409 gfx::Size
Combobox::GetPreferredSize() const {
410 // The preferred size will drive the local bounds which in turn is used to set
411 // the minimum width for the dropdown list.
412 gfx::Insets insets
= GetInsets();
413 insets
+= gfx::Insets(Textfield::kTextPadding
,
414 Textfield::kTextPadding
,
415 Textfield::kTextPadding
,
416 Textfield::kTextPadding
);
417 int total_width
= std::max(kMinComboboxWidth
, content_size_
.width()) +
418 insets
.width() + GetDisclosureArrowLeftPadding() +
419 ArrowSize().width() + GetDisclosureArrowRightPadding();
420 return gfx::Size(total_width
, content_size_
.height() + insets
.height());
423 const char* Combobox::GetClassName() const {
424 return kViewClassName
;
427 bool Combobox::SkipDefaultKeyEventProcessing(const ui::KeyEvent
& e
) {
428 // Escape should close the drop down list when it is active, not host UI.
429 if (e
.key_code() != ui::VKEY_ESCAPE
||
430 e
.IsShiftDown() || e
.IsControlDown() || e
.IsAltDown()) {
433 return dropdown_open_
;
436 bool Combobox::OnKeyPressed(const ui::KeyEvent
& e
) {
437 // TODO(oshima): handle IME.
438 DCHECK_EQ(e
.type(), ui::ET_KEY_PRESSED
);
440 DCHECK_GE(selected_index_
, 0);
441 DCHECK_LT(selected_index_
, model()->GetItemCount());
442 if (selected_index_
< 0 || selected_index_
> model()->GetItemCount())
445 bool show_menu
= false;
446 int new_index
= kNoSelection
;
447 switch (e
.key_code()) {
448 // Show the menu on F4 without modifiers.
450 if (e
.IsAltDown() || e
.IsAltGrDown() || e
.IsControlDown())
455 // Move to the next item if any, or show the menu on Alt+Down like Windows.
460 new_index
= GetAdjacentIndex(model(), 1, selected_index_
);
463 // Move to the end of the list.
465 case ui::VKEY_NEXT
: // Page down.
466 new_index
= GetAdjacentIndex(model(), -1, model()->GetItemCount());
469 // Move to the beginning of the list.
471 case ui::VKEY_PRIOR
: // Page up.
472 new_index
= GetAdjacentIndex(model(), 1, -1);
475 // Move to the previous item if any.
477 new_index
= GetAdjacentIndex(model(), -1, selected_index_
);
480 // Click the button only when the button style mode.
482 if (style_
== STYLE_ACTION
) {
483 // When pressing space, the click event will be raised after the key is
485 text_button_
->SetState(Button::STATE_PRESSED
);
491 // Click the button only when the button style mode.
492 case ui::VKEY_RETURN
:
493 if (style_
!= STYLE_ACTION
)
504 ShowDropDownMenu(ui::MENU_SOURCE_KEYBOARD
);
505 } else if (new_index
!= selected_index_
&& new_index
!= kNoSelection
&&
506 style_
!= STYLE_ACTION
) {
507 DCHECK(!model()->IsItemSeparatorAt(new_index
));
508 selected_index_
= new_index
;
515 bool Combobox::OnKeyReleased(const ui::KeyEvent
& e
) {
516 if (style_
!= STYLE_ACTION
)
517 return false; // crbug.com/127520
519 if (e
.key_code() == ui::VKEY_SPACE
&& style_
== STYLE_ACTION
)
525 void Combobox::OnPaint(gfx::Canvas
* canvas
) {
528 OnPaintBackground(canvas
);
530 OnPaintBorder(canvas
);
534 PaintButtons(canvas
);
541 void Combobox::OnFocus() {
542 if (GetInputMethod())
543 GetInputMethod()->SetFocusedTextInputClient(GetPrefixSelector());
546 // Border renders differently when focused.
550 void Combobox::OnBlur() {
551 if (GetInputMethod())
552 GetInputMethod()->DetachTextInputClient(GetPrefixSelector());
555 selector_
->OnViewBlur();
556 // Border renders differently when focused.
560 void Combobox::GetAccessibleState(ui::AXViewState
* state
) {
561 state
->role
= ui::AX_ROLE_COMBO_BOX
;
562 state
->name
= accessible_name_
;
563 state
->value
= model_
->GetItemAt(selected_index_
);
564 state
->index
= selected_index_
;
565 state
->count
= model_
->GetItemCount();
568 void Combobox::OnComboboxModelChanged(ui::ComboboxModel
* model
) {
569 DCHECK_EQ(model
, model_
);
573 void Combobox::ButtonPressed(Button
* sender
, const ui::Event
& event
) {
579 if (sender
== text_button_
) {
582 DCHECK_EQ(arrow_button_
, sender
);
583 // TODO(hajimehoshi): Fix the problem that the arrow button blinks when
584 // cliking this while the dropdown menu is opened.
585 const base::TimeDelta delta
= base::Time::Now() - closed_time_
;
586 if (delta
.InMilliseconds() <= kMinimumMsBetweenButtonClicks
)
589 ui::MenuSourceType source_type
= ui::MENU_SOURCE_MOUSE
;
590 if (event
.IsKeyEvent())
591 source_type
= ui::MENU_SOURCE_KEYBOARD
;
592 else if (event
.IsGestureEvent() || event
.IsTouchEvent())
593 source_type
= ui::MENU_SOURCE_TOUCH
;
594 ShowDropDownMenu(source_type
);
598 void Combobox::UpdateFromModel() {
599 const gfx::FontList
& font_list
= Combobox::GetFontList();
601 menu_
= new MenuItemView(this);
602 // MenuRunner owns |menu_|.
603 dropdown_list_menu_runner_
.reset(new MenuRunner(menu_
, MenuRunner::COMBOBOX
));
605 int num_items
= model()->GetItemCount();
607 bool text_item_appended
= false;
608 for (int i
= 0; i
< num_items
; ++i
) {
609 // When STYLE_ACTION is used, the first item and the following separators
610 // are not added to the dropdown menu. It is assumed that the first item is
611 // always selected and rendered on the top of the action button.
612 if (model()->IsItemSeparatorAt(i
)) {
613 if (text_item_appended
|| style_
!= STYLE_ACTION
)
614 menu_
->AppendSeparator();
618 base::string16 text
= model()->GetItemAt(i
);
620 // Inserting the Unicode formatting characters if necessary so that the
621 // text is displayed correctly in right-to-left UIs.
622 base::i18n::AdjustStringForLocaleDirection(&text
);
624 if (style_
!= STYLE_ACTION
|| i
> 0) {
625 menu_
->AppendMenuItem(i
+ kFirstMenuItemId
, text
, MenuItemView::NORMAL
);
626 text_item_appended
= true;
629 if (style_
!= STYLE_ACTION
|| i
== selected_index_
)
630 width
= std::max(width
, gfx::GetStringWidth(text
, font_list
));
633 content_size_
.SetSize(width
, font_list
.GetHeight());
636 void Combobox::UpdateBorder() {
637 scoped_ptr
<FocusableBorder
> border(new FocusableBorder());
638 if (style_
== STYLE_ACTION
)
639 border
->SetInsets(5, 10, 5, 10);
641 border
->SetColor(kWarningColor
);
642 SetBorder(border
.Pass());
645 void Combobox::AdjustBoundsForRTLUI(gfx::Rect
* rect
) const {
646 rect
->set_x(GetMirroredXForRect(*rect
));
649 void Combobox::PaintText(gfx::Canvas
* canvas
) {
650 gfx::Insets insets
= GetInsets();
651 insets
+= gfx::Insets(0, Textfield::kTextPadding
, 0, Textfield::kTextPadding
);
653 gfx::ScopedCanvas
scoped_canvas(canvas
);
654 canvas
->ClipRect(GetContentsBounds());
656 int x
= insets
.left();
657 int y
= insets
.top();
658 int text_height
= height() - insets
.height();
659 SkColor text_color
= GetNativeTheme()->GetSystemColor(
660 ui::NativeTheme::kColorId_LabelEnabledColor
);
662 DCHECK_GE(selected_index_
, 0);
663 DCHECK_LT(selected_index_
, model()->GetItemCount());
664 if (selected_index_
< 0 || selected_index_
> model()->GetItemCount())
666 base::string16 text
= model()->GetItemAt(selected_index_
);
668 gfx::Size arrow_size
= ArrowSize();
669 int disclosure_arrow_offset
= width() - arrow_size
.width() -
670 GetDisclosureArrowLeftPadding() - GetDisclosureArrowRightPadding();
672 const gfx::FontList
& font_list
= Combobox::GetFontList();
673 int text_width
= gfx::GetStringWidth(text
, font_list
);
674 if ((text_width
+ insets
.width()) > disclosure_arrow_offset
)
675 text_width
= disclosure_arrow_offset
- insets
.width();
677 gfx::Rect
text_bounds(x
, y
, text_width
, text_height
);
678 AdjustBoundsForRTLUI(&text_bounds
);
679 canvas
->DrawStringRect(text
, font_list
, text_color
, text_bounds
);
681 int arrow_x
= disclosure_arrow_offset
+ GetDisclosureArrowLeftPadding();
682 gfx::Rect
arrow_bounds(arrow_x
,
683 height() / 2 - arrow_size
.height() / 2,
685 arrow_size
.height());
686 AdjustBoundsForRTLUI(&arrow_bounds
);
688 // TODO(estade): hack alert! Remove this direct call into CommonTheme. For now
689 // STYLE_ACTION isn't properly themed so we have to override the NativeTheme
690 // behavior. See crbug.com/384071
691 if (style_
== STYLE_ACTION
) {
692 ui::CommonThemePaintComboboxArrow(canvas
->sk_canvas(), arrow_bounds
);
694 ui::NativeTheme::ExtraParams ignored
;
695 GetNativeTheme()->Paint(canvas
->sk_canvas(),
696 ui::NativeTheme::kComboboxArrow
,
697 ui::NativeTheme::kNormal
,
703 void Combobox::PaintButtons(gfx::Canvas
* canvas
) {
704 DCHECK(style_
== STYLE_ACTION
);
706 gfx::ScopedCanvas
scoped_canvas(canvas
);
707 if (base::i18n::IsRTL()) {
708 canvas
->Translate(gfx::Vector2d(width(), 0));
709 canvas
->Scale(-1, 1);
712 bool focused
= HasFocus();
713 const std::vector
<const gfx::ImageSkia
*>& arrow_button_images
=
714 menu_button_images_
[focused
][
715 arrow_button_
->state() == Button::STATE_HOVERED
?
716 Button::STATE_NORMAL
: arrow_button_
->state()];
718 int text_button_hover_alpha
=
719 text_button_
->state() == Button::STATE_PRESSED
? 0 :
720 static_cast<int>(static_cast<TransparentButton
*>(text_button_
)->
721 GetAnimationValue() * 255);
722 if (text_button_hover_alpha
< 255) {
723 canvas
->SaveLayerAlpha(255 - text_button_hover_alpha
);
724 Painter
* text_button_painter
=
725 body_button_painters_
[focused
][
726 text_button_
->state() == Button::STATE_HOVERED
?
727 Button::STATE_NORMAL
: text_button_
->state()].get();
728 Painter::PaintPainterAt(canvas
, text_button_painter
,
729 gfx::Rect(0, 0, text_button_
->width(), height()));
732 if (0 < text_button_hover_alpha
) {
733 canvas
->SaveLayerAlpha(text_button_hover_alpha
);
734 Painter
* text_button_hovered_painter
=
735 body_button_painters_
[focused
][Button::STATE_HOVERED
].get();
736 Painter::PaintPainterAt(canvas
, text_button_hovered_painter
,
737 gfx::Rect(0, 0, text_button_
->width(), height()));
741 int arrow_button_hover_alpha
=
742 arrow_button_
->state() == Button::STATE_PRESSED
? 0 :
743 static_cast<int>(static_cast<TransparentButton
*>(arrow_button_
)->
744 GetAnimationValue() * 255);
745 if (arrow_button_hover_alpha
< 255) {
746 canvas
->SaveLayerAlpha(255 - arrow_button_hover_alpha
);
747 PaintArrowButton(canvas
, arrow_button_images
, arrow_button_
->x(), height());
750 if (0 < arrow_button_hover_alpha
) {
751 canvas
->SaveLayerAlpha(arrow_button_hover_alpha
);
752 const std::vector
<const gfx::ImageSkia
*>& arrow_button_hovered_images
=
753 menu_button_images_
[focused
][Button::STATE_HOVERED
];
754 PaintArrowButton(canvas
, arrow_button_hovered_images
,
755 arrow_button_
->x(), height());
760 void Combobox::ShowDropDownMenu(ui::MenuSourceType source_type
) {
761 if (!dropdown_list_menu_runner_
.get())
764 // Extend the menu to the width of the combobox.
765 SubmenuView
* submenu
= menu_
->CreateSubmenu();
766 submenu
->set_minimum_preferred_width(
767 size().width() - (kMenuBorderWidthLeft
+ kMenuBorderWidthRight
));
769 gfx::Rect lb
= GetLocalBounds();
770 gfx::Point
menu_position(lb
.origin());
772 if (style_
== STYLE_NORMAL
) {
773 // Inset the menu's requested position so the border of the menu lines up
774 // with the border of the combobox.
775 menu_position
.set_x(menu_position
.x() + kMenuBorderWidthLeft
);
776 menu_position
.set_y(menu_position
.y() + kMenuBorderWidthTop
);
778 lb
.set_width(lb
.width() - (kMenuBorderWidthLeft
+ kMenuBorderWidthRight
));
780 View::ConvertPointToScreen(this, &menu_position
);
781 if (menu_position
.x() < 0)
782 menu_position
.set_x(0);
784 gfx::Rect
bounds(menu_position
, lb
.size());
786 Button::ButtonState original_state
= Button::STATE_NORMAL
;
788 original_state
= arrow_button_
->state();
789 arrow_button_
->SetState(Button::STATE_PRESSED
);
791 dropdown_open_
= true;
792 MenuAnchorPosition anchor_position
=
793 style_
== STYLE_ACTION
? MENU_ANCHOR_TOPRIGHT
: MENU_ANCHOR_TOPLEFT
;
794 if (dropdown_list_menu_runner_
->RunMenuAt(
795 GetWidget(), NULL
, bounds
, anchor_position
, source_type
) ==
796 MenuRunner::MENU_DELETED
) {
799 dropdown_open_
= false;
801 arrow_button_
->SetState(original_state
);
802 closed_time_
= base::Time::Now();
804 // Need to explicitly clear mouse handler so that events get sent
805 // properly after the menu finishes running. If we don't do this, then
806 // the first click to other parts of the UI is eaten.
807 SetMouseHandler(NULL
);
810 void Combobox::OnPerformAction() {
811 NotifyAccessibilityEvent(ui::AX_EVENT_VALUE_CHANGED
, false);
814 // This combobox may be deleted by the listener.
815 base::WeakPtr
<Combobox
> weak_ptr
= weak_ptr_factory_
.GetWeakPtr();
817 listener_
->OnPerformAction(this);
819 if (weak_ptr
&& style_
== STYLE_ACTION
)
823 int Combobox::MenuCommandToIndex(int menu_command_id
) const {
824 // (note that the id received is offset by kFirstMenuItemId)
825 // Revert menu ID offset to map back to combobox model.
826 int index
= menu_command_id
- kFirstMenuItemId
;
827 DCHECK_LT(index
, model()->GetItemCount());
831 int Combobox::GetDisclosureArrowLeftPadding() const {
834 return kDisclosureArrowLeftPadding
;
836 return kDisclosureArrowButtonLeftPadding
;
842 int Combobox::GetDisclosureArrowRightPadding() const {
845 return kDisclosureArrowRightPadding
;
847 return kDisclosureArrowButtonRightPadding
;
853 gfx::Size
Combobox::ArrowSize() const {
854 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
855 // TODO(estade): hack alert! This should always use GetNativeTheme(). For now
856 // STYLE_ACTION isn't properly themed so we have to override the NativeTheme
857 // behavior. See crbug.com/384071
858 const ui::NativeTheme
* native_theme_for_arrow
= style_
== STYLE_ACTION
?
859 ui::NativeTheme::instance() :
862 const ui::NativeTheme
* native_theme_for_arrow
= GetNativeTheme();
865 ui::NativeTheme::ExtraParams ignored
;
866 return native_theme_for_arrow
->GetPartSize(ui::NativeTheme::kComboboxArrow
,
867 ui::NativeTheme::kNormal
,
871 PrefixSelector
* Combobox::GetPrefixSelector() {
873 selector_
.reset(new PrefixSelector(this));
874 return selector_
.get();