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

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

Issue 59383003: Add the button style for combobox (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: sky's review (3) Created 7 years 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 "base/logging.h" 7 #include "base/logging.h"
8 #include "base/strings/utf_string_conversions.h" 8 #include "base/strings/utf_string_conversions.h"
9 #include "grit/ui_resources.h" 9 #include "grit/ui_resources.h"
10 #include "ui/base/accessibility/accessible_view_state.h" 10 #include "ui/base/accessibility/accessible_view_state.h"
11 #include "ui/base/models/combobox_model.h" 11 #include "ui/base/models/combobox_model.h"
12 #include "ui/base/resource/resource_bundle.h" 12 #include "ui/base/resource/resource_bundle.h"
13 #include "ui/events/event.h" 13 #include "ui/events/event.h"
14 #include "ui/events/keycodes/keyboard_codes.h" 14 #include "ui/events/keycodes/keyboard_codes.h"
15 #include "ui/gfx/animation/throb_animation.h"
15 #include "ui/gfx/canvas.h" 16 #include "ui/gfx/canvas.h"
17 #include "ui/gfx/image/image.h"
18 #include "ui/gfx/scoped_canvas.h"
16 #include "ui/native_theme/native_theme.h" 19 #include "ui/native_theme/native_theme.h"
17 #include "ui/views/color_constants.h" 20 #include "ui/views/color_constants.h"
21 #include "ui/views/controls/button/custom_button.h"
22 #include "ui/views/controls/button/label_button.h"
18 #include "ui/views/controls/combobox/combobox_listener.h" 23 #include "ui/views/controls/combobox/combobox_listener.h"
19 #include "ui/views/controls/focusable_border.h" 24 #include "ui/views/controls/focusable_border.h"
20 #include "ui/views/controls/menu/menu_runner.h" 25 #include "ui/views/controls/menu/menu_runner.h"
21 #include "ui/views/controls/menu/submenu_view.h" 26 #include "ui/views/controls/menu/submenu_view.h"
22 #include "ui/views/controls/prefix_selector.h" 27 #include "ui/views/controls/prefix_selector.h"
23 #include "ui/views/ime/input_method.h" 28 #include "ui/views/ime/input_method.h"
24 #include "ui/views/mouse_constants.h" 29 #include "ui/views/mouse_constants.h"
30 #include "ui/views/painter.h"
25 #include "ui/views/widget/widget.h" 31 #include "ui/views/widget/widget.h"
26 32
27 namespace views { 33 namespace views {
28 34
29 namespace { 35 namespace {
30 36
31 // Menu border widths 37 // Menu border widths
32 const int kMenuBorderWidthLeft = 1; 38 const int kMenuBorderWidthLeft = 1;
33 const int kMenuBorderWidthTop = 1; 39 const int kMenuBorderWidthTop = 1;
34 const int kMenuBorderWidthRight = 1; 40 const int kMenuBorderWidthRight = 1;
35 41
36 // Limit how small a combobox can be. 42 // Limit how small a combobox can be.
37 const int kMinComboboxWidth = 25; 43 const int kMinComboboxWidth = 25;
38 44
39 // Size of the combobox arrow margins 45 // Size of the combobox arrow margins
40 const int kDisclosureArrowLeftPadding = 7; 46 const int kDisclosureArrowLeftPadding = 7;
41 const int kDisclosureArrowRightPadding = 7; 47 const int kDisclosureArrowRightPadding = 7;
48 const int kDisclosureArrowButtonLeftPadding = 11;
49 const int kDisclosureArrowButtonRightPadding = 12;
42 50
43 // Define the id of the first item in the menu (since it needs to be > 0) 51 // Define the id of the first item in the menu (since it needs to be > 0)
44 const int kFirstMenuItemId = 1000; 52 const int kFirstMenuItemId = 1000;
45 53
46 const SkColor kInvalidTextColor = SK_ColorWHITE; 54 const SkColor kInvalidTextColor = SK_ColorWHITE;
47 55
48 // Used to indicate that no item is currently selected by the user. 56 // Used to indicate that no item is currently selected by the user.
49 const int kNoSelection = -1; 57 const int kNoSelection = -1;
50 58
59 const int kBodyButtonImages[] = IMAGE_GRID(IDR_COMBOBOX_BUTTON);
60 const int kHoveredBodyButtonImages[] = IMAGE_GRID(IDR_COMBOBOX_BUTTON_H);
61 const int kPressedBodyButtonImages[] = IMAGE_GRID(IDR_COMBOBOX_BUTTON_P);
62
63 #define MENU_IMAGE_GRID(x) { \
64 x ## _MENU_TOP, x ## _MENU_CENTER, x ## _MENU_BOTTOM, }
65
66 const int kMenuButtonImages[] = MENU_IMAGE_GRID(IDR_COMBOBOX_BUTTON);
67 const int kHoveredMenuButtonImages[] = MENU_IMAGE_GRID(IDR_COMBOBOX_BUTTON_H);
68 const int kPressedMenuButtonImages[] = MENU_IMAGE_GRID(IDR_COMBOBOX_BUTTON_P);
69
70 #undef MENU_IMAGE_GRID
71
51 // The background to use for invalid comboboxes. 72 // The background to use for invalid comboboxes.
52 class InvalidBackground : public Background { 73 class InvalidBackground : public Background {
53 public: 74 public:
54 InvalidBackground() {} 75 InvalidBackground() {}
55 virtual ~InvalidBackground() {} 76 virtual ~InvalidBackground() {}
56 77
57 // Overridden from Background: 78 // Overridden from Background:
58 virtual void Paint(gfx::Canvas* canvas, View* view) const OVERRIDE { 79 virtual void Paint(gfx::Canvas* canvas, View* view) const OVERRIDE {
59 gfx::Rect bounds(view->GetLocalBounds()); 80 gfx::Rect bounds(view->GetLocalBounds());
60 // Inset by 2 to leave 1 empty pixel between background and border. 81 // Inset by 2 to leave 1 empty pixel between background and border.
61 bounds.Inset(2, 2, 2, 2); 82 bounds.Inset(2, 2, 2, 2);
62 canvas->FillRect(bounds, kWarningColor); 83 canvas->FillRect(bounds, kWarningColor);
63 } 84 }
64 85
65 private: 86 private:
66 DISALLOW_COPY_AND_ASSIGN(InvalidBackground); 87 DISALLOW_COPY_AND_ASSIGN(InvalidBackground);
67 }; 88 };
68 89
90 // The transparent button which holds a button state but is not rendered.
91 class TransparentButton : public CustomButton {
92 public:
93 TransparentButton(ButtonListener* listener)
94 : CustomButton(listener) {
95 SetAnimationDuration(LabelButton::kHoverAnimationDurationMs);
96 }
97 virtual ~TransparentButton() {}
98
99 // CustomButton:
100 virtual void StateChanged() OVERRIDE {
101 parent()->SchedulePaintInRect(bounds());
102 }
103
104 // gfx::AnimationDelegate:
105 virtual void AnimationProgressed(const gfx::Animation* animation) OVERRIDE {
106 parent()->SchedulePaintInRect(bounds());
107 }
108
109 double GetAnimationValue() const {
110 return hover_animation_->GetCurrentValue();
111 }
112
113 private:
114 DISALLOW_COPY_AND_ASSIGN(TransparentButton);
115 };
116
69 // Returns the next or previous valid index (depending on |increment|'s value). 117 // Returns the next or previous valid index (depending on |increment|'s value).
70 // Skips separator or disabled indices. Returns -1 if there is no valid adjacent 118 // Skips separator or disabled indices. Returns -1 if there is no valid adjacent
71 // index. 119 // index.
72 int GetAdjacentIndex(ui::ComboboxModel* model, int increment, int index) { 120 int GetAdjacentIndex(ui::ComboboxModel* model, int increment, int index) {
73 DCHECK(increment == -1 || increment == 1); 121 DCHECK(increment == -1 || increment == 1);
74 122
75 index += increment; 123 index += increment;
76 while (index >= 0 && index < model->GetItemCount()) { 124 while (index >= 0 && index < model->GetItemCount()) {
77 if (!model->IsItemSeparatorAt(index) || !model->IsItemEnabledAt(index)) 125 if (!model->IsItemSeparatorAt(index) || !model->IsItemEnabledAt(index))
78 return index; 126 return index;
79 index += increment; 127 index += increment;
80 } 128 }
81 return kNoSelection; 129 return kNoSelection;
82 } 130 }
83 131
132 // Returns the image resource ids of an array for the body button.
133 //
134 // TODO(hajimehoshi): This function should return the images for the 'disabled'
135 // status. (crbug/270052)
136 //
137 // TODO(hajimehoshi): Currently, |focused| is ignored. This should return the
138 // images for the 'focused' status. (crbug/270052)
139 const int* GetBodyButtonImageIds(bool focused,
140 Button::ButtonState state,
141 size_t* num) {
142 DCHECK(num);
143 *num = 9;
144 switch (state) {
145 case Button::STATE_DISABLED:
146 return kBodyButtonImages;
147 case Button::STATE_NORMAL:
148 return kBodyButtonImages;
149 case Button::STATE_HOVERED:
150 return kHoveredBodyButtonImages;
151 case Button::STATE_PRESSED:
152 return kPressedBodyButtonImages;
153 default:
154 NOTREACHED();
155 }
156 return NULL;
157 }
158
159 // Returns the image resource ids of an array for the menu button.
160 const int* GetMenuButtonImageIds(bool focused,
161 Button::ButtonState state,
162 size_t* num) {
163 DCHECK(num);
164 *num = 3;
165 switch (state) {
166 case Button::STATE_DISABLED:
167 return kMenuButtonImages;
168 case Button::STATE_NORMAL:
169 return kMenuButtonImages;
170 case Button::STATE_HOVERED:
171 return kHoveredMenuButtonImages;
172 case Button::STATE_PRESSED:
173 return kPressedMenuButtonImages;
174 default:
175 NOTREACHED();
176 }
177 return NULL;
178 }
179
180 // Returns the images for the menu buttons.
181 std::vector<const gfx::ImageSkia*> GetMenuButtonImages(
182 bool focused,
183 Button::ButtonState state) {
184 const int* ids;
185 size_t num_ids;
186 ids = GetMenuButtonImageIds(focused, state, &num_ids);
187 std::vector<const gfx::ImageSkia*> images;
188 images.reserve(num_ids);
189 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
190 for (size_t i = 0; i < num_ids; i++)
191 images.push_back(rb.GetImageSkiaNamed(ids[i]));
192 return images;
193 }
194
195 // Paints three images in a column at the given location. The center image is
196 // stretched so as to fit the given height.
197 void PaintImagesVertically(gfx::Canvas* canvas,
198 const gfx::ImageSkia& top_image,
199 const gfx::ImageSkia& center_image,
200 const gfx::ImageSkia& bottom_image,
201 int x, int y, int width, int height) {
202 canvas->DrawImageInt(top_image,
203 0, 0, top_image.width(), top_image.height(),
204 x, y, width, top_image.height(), false);
205 y += top_image.height();
206 int center_height = height - top_image.height() - bottom_image.height();
207 canvas->DrawImageInt(center_image,
208 0, 0, center_image.width(), center_image.height(),
209 x, y, width, center_height, false);
210 y += center_height;
211 canvas->DrawImageInt(bottom_image,
212 0, 0, bottom_image.width(), bottom_image.height(),
213 x, y, width, bottom_image.height(), false);
214 }
215
216 // Paints the arrow button.
217 void PaintArrowButton(
218 gfx::Canvas* canvas,
219 const std::vector<const gfx::ImageSkia*>& arrow_button_images,
220 int x, int height) {
221 PaintImagesVertically(canvas,
222 *arrow_button_images[0],
223 *arrow_button_images[1],
224 *arrow_button_images[2],
225 x, 0, arrow_button_images[0]->width(), height);
226 }
227
84 } // namespace 228 } // namespace
85 229
86 // static 230 // static
87 const char Combobox::kViewClassName[] = "views/Combobox"; 231 const char Combobox::kViewClassName[] = "views/Combobox";
88 232
89 //////////////////////////////////////////////////////////////////////////////// 233 ////////////////////////////////////////////////////////////////////////////////
90 // Combobox, public: 234 // Combobox, public:
91 235
92 Combobox::Combobox(ui::ComboboxModel* model) 236 Combobox::Combobox(ui::ComboboxModel* model)
93 : model_(model), 237 : model_(model),
238 style_(STYLE_SHOW_DROP_DOWN_ON_CLICK),
94 listener_(NULL), 239 listener_(NULL),
95 selected_index_(model_->GetDefaultIndex()), 240 selected_index_(model_->GetDefaultIndex()),
96 invalid_(false), 241 invalid_(false),
97 text_border_(new FocusableBorder()), 242 text_border_(new FocusableBorder()),
98 disclosure_arrow_(ui::ResourceBundle::GetSharedInstance().GetImageNamed( 243 disclosure_arrow_(ui::ResourceBundle::GetSharedInstance().GetImageNamed(
99 IDR_MENU_DROPARROW).ToImageSkia()), 244 IDR_MENU_DROPARROW).ToImageSkia()),
100 dropdown_open_(false) { 245 dropdown_open_(false),
246 text_button_(new TransparentButton(this)),
247 arrow_button_(new TransparentButton(this)) {
101 model_->AddObserver(this); 248 model_->AddObserver(this);
102 UpdateFromModel(); 249 UpdateFromModel();
103 set_focusable(true); 250 set_focusable(true);
104 // |text_border_| draws focus. 251 // |text_border_| draws focus.
105 set_focus_border(NULL); 252 set_focus_border(NULL);
106 set_border(text_border_); 253 set_border(text_border_);
254
255 // Initialize the button images.
256 Button::ButtonState button_states[] = {
257 Button::STATE_DISABLED,
258 Button::STATE_NORMAL,
259 Button::STATE_HOVERED,
260 Button::STATE_PRESSED,
261 };
262 for (int focused = 0; focused < 2; focused++) {
263 for (size_t state_index = 0; state_index < arraysize(button_states);
264 state_index++) {
265 Button::ButtonState state = button_states[state_index];
266 size_t num;
267 const int* ids = GetBodyButtonImageIds(focused, state, &num);
268 body_button_painters_[focused][state].reset(
269 Painter::CreateImageGridPainter(ids));
270 menu_button_images_[focused][state] = GetMenuButtonImages(focused, state);
271 }
272 }
273
274 text_button_->SetVisible(true);
275 arrow_button_->SetVisible(true);
276 text_button_->set_focusable(false);
277 arrow_button_->set_focusable(false);
278 AddChildView(text_button_);
279 AddChildView(arrow_button_);
107 } 280 }
108 281
109 Combobox::~Combobox() { 282 Combobox::~Combobox() {
110 model_->RemoveObserver(this); 283 model_->RemoveObserver(this);
111 } 284 }
112 285
113 // static 286 // static
114 const gfx::Font& Combobox::GetFont() { 287 const gfx::Font& Combobox::GetFont() {
115 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); 288 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
116 return rb.GetFont(ui::ResourceBundle::BaseFont); 289 return rb.GetFont(ui::ResourceBundle::BaseFont);
117 } 290 }
118 291
292 void Combobox::SetStyle(Style style) {
293 if (style_ == style)
294 return;
295
296 style_ = style;
297 switch (style) {
298 case STYLE_SHOW_DROP_DOWN_ON_CLICK: {
299 // Recreate a FocusableBorder to reset the insets of the border.
300 // set_border with a new border will delete the existing border.
301 text_border_ = new FocusableBorder();
302 set_border(text_border_);
303 break;
304 }
305 case STYLE_NOTIFY_ON_CLICK: {
306 text_border_->SetInsets(8, 13, 8, 13);
sky 2013/12/05 16:46:32 What I'm saying is you should get rid of text_bord
hajimehoshi 2013/12/06 12:27:04 Done. (Added UpdateBorder)
307 break;
308 }
309 }
310
311 PreferredSizeChanged();
312 }
313
119 void Combobox::ModelChanged() { 314 void Combobox::ModelChanged() {
120 selected_index_ = std::min(0, model_->GetItemCount()); 315 selected_index_ = std::min(0, model_->GetItemCount());
121 UpdateFromModel(); 316 UpdateFromModel();
122 PreferredSizeChanged(); 317 PreferredSizeChanged();
123 } 318 }
124 319
125 void Combobox::SetSelectedIndex(int index) { 320 void Combobox::SetSelectedIndex(int index) {
126 selected_index_ = index; 321 selected_index_ = index;
127 SchedulePaint(); 322 SchedulePaint();
128 } 323 }
(...skipping 26 matching lines...) Expand all
155 } 350 }
156 SchedulePaint(); 351 SchedulePaint();
157 } 352 }
158 353
159 ui::TextInputClient* Combobox::GetTextInputClient() { 354 ui::TextInputClient* Combobox::GetTextInputClient() {
160 if (!selector_) 355 if (!selector_)
161 selector_.reset(new PrefixSelector(this)); 356 selector_.reset(new PrefixSelector(this));
162 return selector_.get(); 357 return selector_.get();
163 } 358 }
164 359
360 void Combobox::Layout() {
361 PrefixDelegate::Layout();
362
363 gfx::Insets insets = GetInsets();
364 int height = content_size_.height() + insets.height();
sky 2013/12/05 16:46:32 Shouldn't you use height() here rather than calcul
hajimehoshi 2013/12/06 12:27:04 height() was enough. Done.
365 int text_button_width = 0;
366 int arrow_button_width = 0;
367
368 switch (style_) {
369 case STYLE_SHOW_DROP_DOWN_ON_CLICK: {
370 arrow_button_width = width();
371 break;
372 }
373 case STYLE_NOTIFY_ON_CLICK: {
374 arrow_button_width = GetDisclosureArrowLeftPadding() +
375 disclosure_arrow_->width() + GetDisclosureArrowRightPadding();
376 text_button_width = width() - arrow_button_width;
377 break;
378 }
379 }
380
381 int arrow_button_x = text_button_width;
382 text_button_->SetBounds(0, 0, text_button_width, height);
sky 2013/12/05 16:46:32 std::max(0, text_button_width)
hajimehoshi 2013/12/06 12:27:04 Done.
383 arrow_button_->SetBounds(arrow_button_x, 0, arrow_button_width, height);
384 }
165 385
166 bool Combobox::IsItemChecked(int id) const { 386 bool Combobox::IsItemChecked(int id) const {
167 return false; 387 return false;
168 } 388 }
169 389
170 bool Combobox::IsCommandEnabled(int id) const { 390 bool Combobox::IsCommandEnabled(int id) const {
171 return model()->IsItemEnabledAt(MenuCommandToIndex(id)); 391 return model()->IsItemEnabledAt(MenuCommandToIndex(id));
172 } 392 }
173 393
174 void Combobox::ExecuteCommand(int id) { 394 void Combobox::ExecuteCommand(int id) {
(...skipping 25 matching lines...) Expand all
200 // Combobox, View overrides: 420 // Combobox, View overrides:
201 421
202 gfx::Size Combobox::GetPreferredSize() { 422 gfx::Size Combobox::GetPreferredSize() {
203 if (content_size_.IsEmpty()) 423 if (content_size_.IsEmpty())
204 UpdateFromModel(); 424 UpdateFromModel();
205 425
206 // The preferred size will drive the local bounds which in turn is used to set 426 // The preferred size will drive the local bounds which in turn is used to set
207 // the minimum width for the dropdown list. 427 // the minimum width for the dropdown list.
208 gfx::Insets insets = GetInsets(); 428 gfx::Insets insets = GetInsets();
209 int total_width = std::max(kMinComboboxWidth, content_size_.width()) + 429 int total_width = std::max(kMinComboboxWidth, content_size_.width()) +
210 insets.width() + kDisclosureArrowLeftPadding + 430 insets.width() + GetDisclosureArrowLeftPadding() +
211 disclosure_arrow_->width() + kDisclosureArrowRightPadding; 431 disclosure_arrow_->width() + GetDisclosureArrowRightPadding();
212
213 return gfx::Size(total_width, content_size_.height() + insets.height()); 432 return gfx::Size(total_width, content_size_.height() + insets.height());
214 } 433 }
215 434
216 const char* Combobox::GetClassName() const { 435 const char* Combobox::GetClassName() const {
217 return kViewClassName; 436 return kViewClassName;
218 } 437 }
219 438
220 bool Combobox::SkipDefaultKeyEventProcessing(const ui::KeyEvent& e) { 439 bool Combobox::SkipDefaultKeyEventProcessing(const ui::KeyEvent& e) {
221 // Escape should close the drop down list when it is active, not host UI. 440 // Escape should close the drop down list when it is active, not host UI.
222 if (e.key_code() != ui::VKEY_ESCAPE || 441 if (e.key_code() != ui::VKEY_ESCAPE ||
223 e.IsShiftDown() || e.IsControlDown() || e.IsAltDown()) { 442 e.IsShiftDown() || e.IsControlDown() || e.IsAltDown()) {
224 return false; 443 return false;
225 } 444 }
226 return dropdown_open_; 445 return dropdown_open_;
227 } 446 }
228 447
229 bool Combobox::OnMousePressed(const ui::MouseEvent& mouse_event) {
230 RequestFocus();
231 const base::TimeDelta delta = base::Time::Now() - closed_time_;
232 if (mouse_event.IsLeftMouseButton() &&
233 (delta.InMilliseconds() > kMinimumMsBetweenButtonClicks)) {
234 UpdateFromModel();
235 ShowDropDownMenu(ui::MENU_SOURCE_MOUSE);
236 }
237
238 return true;
239 }
240
241 bool Combobox::OnMouseDragged(const ui::MouseEvent& mouse_event) {
242 return true;
243 }
244
245 bool Combobox::OnKeyPressed(const ui::KeyEvent& e) { 448 bool Combobox::OnKeyPressed(const ui::KeyEvent& e) {
246 // TODO(oshima): handle IME. 449 // TODO(oshima): handle IME.
247 DCHECK_EQ(e.type(), ui::ET_KEY_PRESSED); 450 DCHECK_EQ(e.type(), ui::ET_KEY_PRESSED);
248 451
249 DCHECK_GE(selected_index_, 0); 452 DCHECK_GE(selected_index_, 0);
250 DCHECK_LT(selected_index_, model()->GetItemCount()); 453 DCHECK_LT(selected_index_, model()->GetItemCount());
251 if (selected_index_ < 0 || selected_index_ > model()->GetItemCount()) 454 if (selected_index_ < 0 || selected_index_ > model()->GetItemCount())
252 selected_index_ = 0; 455 selected_index_ = 0;
253 456
254 bool show_menu = false; 457 bool show_menu = false;
(...skipping 24 matching lines...) Expand all
279 case ui::VKEY_HOME: 482 case ui::VKEY_HOME:
280 case ui::VKEY_PRIOR: // Page up. 483 case ui::VKEY_PRIOR: // Page up.
281 new_index = GetAdjacentIndex(model(), 1, -1); 484 new_index = GetAdjacentIndex(model(), 1, -1);
282 break; 485 break;
283 486
284 // Move to the previous item if any. 487 // Move to the previous item if any.
285 case ui::VKEY_UP: 488 case ui::VKEY_UP:
286 new_index = GetAdjacentIndex(model(), -1, selected_index_); 489 new_index = GetAdjacentIndex(model(), -1, selected_index_);
287 break; 490 break;
288 491
492 // Click the button only when the button style mode.
493 case ui::VKEY_SPACE:
494 if (style_ == STYLE_NOTIFY_ON_CLICK) {
495 // When pressing space, the click event will be raised after the key is
496 // released.
497 text_button_->SetState(Button::STATE_PRESSED);
498 }
499 break;
500
501 // Click the button only when the button style mode.
502 case ui::VKEY_RETURN:
503 HandleClickEvent();
504 break;
505
289 default: 506 default:
290 return false; 507 return false;
291 } 508 }
292 509
293 if (show_menu) { 510 if (show_menu) {
294 UpdateFromModel(); 511 UpdateFromModel();
295 ShowDropDownMenu(ui::MENU_SOURCE_KEYBOARD); 512 ShowDropDownMenu(ui::MENU_SOURCE_KEYBOARD);
296 } else if (new_index != selected_index_ && new_index != kNoSelection) { 513 } else if (new_index != selected_index_ && new_index != kNoSelection) {
297 DCHECK(!model()->IsItemSeparatorAt(new_index)); 514 DCHECK(!model()->IsItemSeparatorAt(new_index));
298 selected_index_ = new_index; 515 selected_index_ = new_index;
299 OnSelectionChanged(); 516 OnSelectionChanged();
300 } 517 }
301 518
302 return true; 519 return true;
303 } 520 }
304 521
305 bool Combobox::OnKeyReleased(const ui::KeyEvent& e) { 522 bool Combobox::OnKeyReleased(const ui::KeyEvent& e) {
306 return false; // crbug.com/127520 523 if (style_ != STYLE_NOTIFY_ON_CLICK)
307 } 524 return false; // crbug.com/127520
308 525
309 void Combobox::OnGestureEvent(ui::GestureEvent* gesture) { 526 if (e.key_code() == ui::VKEY_SPACE)
310 if (gesture->type() == ui::ET_GESTURE_TAP) { 527 HandleClickEvent();
311 UpdateFromModel(); 528
312 ShowDropDownMenu(ui::MENU_SOURCE_TOUCH); 529 return false;
313 gesture->StopPropagation();
314 return;
315 }
316 View::OnGestureEvent(gesture);
317 } 530 }
318 531
319 void Combobox::OnPaint(gfx::Canvas* canvas) { 532 void Combobox::OnPaint(gfx::Canvas* canvas) {
320 OnPaintBackground(canvas); 533 switch (style_) {
321 PaintText(canvas); 534 case STYLE_SHOW_DROP_DOWN_ON_CLICK: {
322 OnPaintBorder(canvas); 535 OnPaintBackground(canvas);
536 PaintText(canvas);
537 OnPaintBorder(canvas);
538 break;
539 }
540 case STYLE_NOTIFY_ON_CLICK: {
541 PaintButtons(canvas);
542 PaintText(canvas);
543 break;
544 }
545 }
323 } 546 }
324 547
325 void Combobox::OnFocus() { 548 void Combobox::OnFocus() {
326 GetInputMethod()->OnFocus(); 549 GetInputMethod()->OnFocus();
327 View::OnFocus(); 550 View::OnFocus();
328 // Border renders differently when focused. 551 // Border renders differently when focused.
329 SchedulePaint(); 552 SchedulePaint();
330 } 553 }
331 554
332 void Combobox::OnBlur() { 555 void Combobox::OnBlur() {
333 GetInputMethod()->OnBlur(); 556 GetInputMethod()->OnBlur();
334 if (selector_) 557 if (selector_)
335 selector_->OnViewBlur(); 558 selector_->OnViewBlur();
336 // Border renders differently when focused. 559 // Border renders differently when focused.
337 SchedulePaint(); 560 SchedulePaint();
338 } 561 }
339 562
340 void Combobox::GetAccessibleState(ui::AccessibleViewState* state) { 563 void Combobox::GetAccessibleState(ui::AccessibleViewState* state) {
341 state->role = ui::AccessibilityTypes::ROLE_COMBOBOX; 564 state->role = ui::AccessibilityTypes::ROLE_COMBOBOX;
342 state->name = accessible_name_; 565 state->name = accessible_name_;
343 state->value = model_->GetItemAt(selected_index_); 566 state->value = model_->GetItemAt(selected_index_);
344 state->index = selected_index_; 567 state->index = selected_index_;
345 state->count = model_->GetItemCount(); 568 state->count = model_->GetItemCount();
346 } 569 }
347 570
348 void Combobox::OnModelChanged() { 571 void Combobox::OnModelChanged() {
349 ModelChanged(); 572 ModelChanged();
350 } 573 }
351 574
575 void Combobox::ButtonPressed(Button* sender, const ui::Event& event) {
576 RequestFocus();
577
578 if (sender == text_button_) {
579 HandleClickEvent();
580 } else {
581 DCHECK_EQ(arrow_button_, sender);
582 // TODO(hajimehoshi): Fix the problem that the arrow button blinks when
583 // cliking this while the dropdown menu is opened.
584 const base::TimeDelta delta = base::Time::Now() - closed_time_;
585 if (delta.InMilliseconds() <= kMinimumMsBetweenButtonClicks)
586 return;
587
588 ui::MenuSourceType source_type = ui::MENU_SOURCE_MOUSE;
589 if (event.IsKeyEvent())
590 source_type = ui::MENU_SOURCE_KEYBOARD;
591 else if (event.IsGestureEvent() || event.IsTouchEvent())
592 source_type = ui::MENU_SOURCE_TOUCH;
593 ShowDropDownMenu(source_type);
594 }
595 }
596
352 void Combobox::UpdateFromModel() { 597 void Combobox::UpdateFromModel() {
353 int max_width = 0; 598 int max_width = 0;
354 const gfx::Font& font = Combobox::GetFont(); 599 const gfx::Font& font = Combobox::GetFont();
355 600
356 MenuItemView* menu = new MenuItemView(this); 601 MenuItemView* menu = new MenuItemView(this);
357 // MenuRunner owns |menu|. 602 // MenuRunner owns |menu|.
358 dropdown_list_menu_runner_.reset(new MenuRunner(menu)); 603 dropdown_list_menu_runner_.reset(new MenuRunner(menu));
359 604
360 int num_items = model()->GetItemCount(); 605 int num_items = model()->GetItemCount();
361 for (int i = 0; i < num_items; ++i) { 606 for (int i = 0; i < num_items; ++i) {
(...skipping 15 matching lines...) Expand all
377 content_size_.SetSize(max_width, font.GetHeight()); 622 content_size_.SetSize(max_width, font.GetHeight());
378 } 623 }
379 624
380 void Combobox::AdjustBoundsForRTLUI(gfx::Rect* rect) const { 625 void Combobox::AdjustBoundsForRTLUI(gfx::Rect* rect) const {
381 rect->set_x(GetMirroredXForRect(*rect)); 626 rect->set_x(GetMirroredXForRect(*rect));
382 } 627 }
383 628
384 void Combobox::PaintText(gfx::Canvas* canvas) { 629 void Combobox::PaintText(gfx::Canvas* canvas) {
385 gfx::Insets insets = GetInsets(); 630 gfx::Insets insets = GetInsets();
386 631
387 canvas->Save(); 632 gfx::ScopedCanvas scoped_canvas(canvas);
388 canvas->ClipRect(GetContentsBounds()); 633 canvas->ClipRect(GetContentsBounds());
389 634
390 int x = insets.left(); 635 int x = insets.left();
391 int y = insets.top(); 636 int y = insets.top();
392 int text_height = height() - insets.height(); 637 int text_height = height() - insets.height();
393 SkColor text_color = invalid() ? kInvalidTextColor : 638 SkColor text_color = invalid() ? kInvalidTextColor :
394 GetNativeTheme()->GetSystemColor( 639 GetNativeTheme()->GetSystemColor(
395 ui::NativeTheme::kColorId_LabelEnabledColor); 640 ui::NativeTheme::kColorId_LabelEnabledColor);
396 641
397 DCHECK_GE(selected_index_, 0); 642 DCHECK_GE(selected_index_, 0);
398 DCHECK_LT(selected_index_, model()->GetItemCount()); 643 DCHECK_LT(selected_index_, model()->GetItemCount());
399 if (selected_index_ < 0 || selected_index_ > model()->GetItemCount()) 644 if (selected_index_ < 0 || selected_index_ > model()->GetItemCount())
400 selected_index_ = 0; 645 selected_index_ = 0;
401 string16 text = model()->GetItemAt(selected_index_); 646 string16 text = model()->GetItemAt(selected_index_);
402 647
403 int disclosure_arrow_offset = width() - disclosure_arrow_->width() 648 int disclosure_arrow_offset = width() - disclosure_arrow_->width() -
404 - kDisclosureArrowLeftPadding - kDisclosureArrowRightPadding; 649 GetDisclosureArrowLeftPadding() - GetDisclosureArrowRightPadding();
405 650
406 const gfx::Font& font = Combobox::GetFont(); 651 const gfx::Font& font = Combobox::GetFont();
407 int text_width = font.GetStringWidth(text); 652 int text_width = font.GetStringWidth(text);
408 if ((text_width + insets.width()) > disclosure_arrow_offset) 653 if ((text_width + insets.width()) > disclosure_arrow_offset)
409 text_width = disclosure_arrow_offset - insets.width(); 654 text_width = disclosure_arrow_offset - insets.width();
410 655
411 gfx::Rect text_bounds(x, y, text_width, text_height); 656 gfx::Rect text_bounds(x, y, text_width, text_height);
412 AdjustBoundsForRTLUI(&text_bounds); 657 AdjustBoundsForRTLUI(&text_bounds);
413 canvas->DrawStringInt(text, font, text_color, text_bounds); 658 canvas->DrawStringInt(text, font, text_color, text_bounds);
414 659
415 gfx::Rect arrow_bounds(disclosure_arrow_offset + kDisclosureArrowLeftPadding, 660 int arrow_x = disclosure_arrow_offset + GetDisclosureArrowLeftPadding();
661 gfx::Rect arrow_bounds(arrow_x,
416 height() / 2 - disclosure_arrow_->height() / 2, 662 height() / 2 - disclosure_arrow_->height() / 2,
417 disclosure_arrow_->width(), 663 disclosure_arrow_->width(),
418 disclosure_arrow_->height()); 664 disclosure_arrow_->height());
419 AdjustBoundsForRTLUI(&arrow_bounds); 665 AdjustBoundsForRTLUI(&arrow_bounds);
420 666
421 SkPaint paint; 667 SkPaint paint;
422 // This makes the arrow subtractive. 668 // This makes the arrow subtractive.
423 if (invalid()) 669 if (invalid())
424 paint.setXfermodeMode(SkXfermode::kDstOut_Mode); 670 paint.setXfermodeMode(SkXfermode::kDstOut_Mode);
425 canvas->DrawImageInt(*disclosure_arrow_, arrow_bounds.x(), arrow_bounds.y(), 671 canvas->DrawImageInt(*disclosure_arrow_, arrow_bounds.x(), arrow_bounds.y(),
426 paint); 672 paint);
673 }
427 674
428 canvas->Restore(); 675 void Combobox::PaintButtons(gfx::Canvas* canvas) {
676 DCHECK(style_ == STYLE_NOTIFY_ON_CLICK);
677
678 gfx::ScopedCanvas scoped_canvas(canvas);
679 if (base::i18n::IsRTL()) {
680 canvas->Translate(gfx::Vector2d(width(), 0));
681 canvas->Scale(-1, 1);
682 }
683
684 bool focused = HasFocus();
685 const std::vector<const gfx::ImageSkia*>& arrow_button_images =
686 menu_button_images_[focused][
687 arrow_button_->state() == Button::STATE_HOVERED ?
688 Button::STATE_NORMAL : arrow_button_->state()];
689
690 int text_button_width = width() - arrow_button_images[0]->width();
sky 2013/12/05 16:46:32 Is there a reason you need this code here rather t
hajimehoshi 2013/12/06 12:27:04 Oh, I just didn't realize this... Thanks. I remove
691 int text_button_hover_alpha =
692 text_button_->state() == Button::STATE_PRESSED ? 0 :
693 static_cast<int>(static_cast<TransparentButton*>(text_button_)->
694 GetAnimationValue() * 255);
695 if (text_button_hover_alpha < 255) {
696 canvas->SaveLayerAlpha(255 - text_button_hover_alpha);
697 Painter* text_button_painter =
698 body_button_painters_[focused][
699 text_button_->state() == Button::STATE_HOVERED ?
700 Button::STATE_NORMAL : text_button_->state()].get();
701 Painter::PaintPainterAt(canvas, text_button_painter,
702 gfx::Rect(0, 0, text_button_width, height()));
703 canvas->Restore();
704 }
705 if (0 < text_button_hover_alpha) {
706 canvas->SaveLayerAlpha(text_button_hover_alpha);
707 Painter* text_button_hovered_painter =
708 body_button_painters_[focused][Button::STATE_HOVERED].get();
709 Painter::PaintPainterAt(canvas, text_button_hovered_painter,
710 gfx::Rect(0, 0, text_button_width, height()));
711 canvas->Restore();
712 }
713
714 int arrow_button_x = text_button_width;
715 int arrow_button_hover_alpha =
716 arrow_button_->state() == Button::STATE_PRESSED ? 0 :
717 static_cast<int>(static_cast<TransparentButton*>(arrow_button_)->
718 GetAnimationValue() * 255);
719 if (arrow_button_hover_alpha < 255) {
720 canvas->SaveLayerAlpha(255 - arrow_button_hover_alpha);
721 PaintArrowButton(canvas, arrow_button_images, arrow_button_x, height());
722 canvas->Restore();
723 }
724 if (0 < arrow_button_hover_alpha) {
725 canvas->SaveLayerAlpha(arrow_button_hover_alpha);
726 const std::vector<const gfx::ImageSkia*>& arrow_button_hovered_images =
727 menu_button_images_[focused][Button::STATE_HOVERED];
728 PaintArrowButton(canvas, arrow_button_hovered_images,
729 arrow_button_x, height());
730 canvas->Restore();
731 }
429 } 732 }
430 733
431 void Combobox::ShowDropDownMenu(ui::MenuSourceType source_type) { 734 void Combobox::ShowDropDownMenu(ui::MenuSourceType source_type) {
432 if (!dropdown_list_menu_runner_.get()) 735 if (!dropdown_list_menu_runner_.get())
433 UpdateFromModel(); 736 UpdateFromModel();
434 737
435 // Extend the menu to the width of the combobox. 738 // Extend the menu to the width of the combobox.
436 MenuItemView* menu = dropdown_list_menu_runner_->GetMenu(); 739 MenuItemView* menu = dropdown_list_menu_runner_->GetMenu();
437 SubmenuView* submenu = menu->CreateSubmenu(); 740 SubmenuView* submenu = menu->CreateSubmenu();
438 submenu->set_minimum_preferred_width(size().width() - 741 submenu->set_minimum_preferred_width(
439 (kMenuBorderWidthLeft + kMenuBorderWidthRight)); 742 size().width() - (kMenuBorderWidthLeft + kMenuBorderWidthRight));
440 743
441 gfx::Rect lb = GetLocalBounds(); 744 gfx::Rect lb = GetLocalBounds();
442 gfx::Point menu_position(lb.origin()); 745 gfx::Point menu_position(lb.origin());
443 746
444 // Inset the menu's requested position so the border of the menu lines up 747 // Inset the menu's requested position so the border of the menu lines up
445 // with the border of the combobox. 748 // with the border of the combobox.
446 menu_position.set_x(menu_position.x() + kMenuBorderWidthLeft); 749 menu_position.set_x(menu_position.x() + kMenuBorderWidthLeft);
447 menu_position.set_y(menu_position.y() + kMenuBorderWidthTop); 750 menu_position.set_y(menu_position.y() + kMenuBorderWidthTop);
448 lb.set_width(lb.width() - (kMenuBorderWidthLeft + kMenuBorderWidthRight)); 751 lb.set_width(lb.width() - (kMenuBorderWidthLeft + kMenuBorderWidthRight));
449 752
450 View::ConvertPointToScreen(this, &menu_position); 753 View::ConvertPointToScreen(this, &menu_position);
451 if (menu_position.x() < 0) 754 if (menu_position.x() < 0)
452 menu_position.set_x(0); 755 menu_position.set_x(0);
453 756
454 gfx::Rect bounds(menu_position, lb.size()); 757 gfx::Rect bounds(menu_position, lb.size());
455 758
759 Button::ButtonState original_state;
760 if (arrow_button_) {
761 original_state = arrow_button_->state();
762 arrow_button_->SetState(Button::STATE_PRESSED);
763 }
456 dropdown_open_ = true; 764 dropdown_open_ = true;
457 if (dropdown_list_menu_runner_->RunMenuAt(GetWidget(), NULL, bounds, 765 if (dropdown_list_menu_runner_->RunMenuAt(GetWidget(), NULL, bounds,
458 MenuItemView::TOPLEFT, source_type, MenuRunner::COMBOBOX) == 766 MenuItemView::TOPLEFT, source_type, MenuRunner::COMBOBOX) ==
459 MenuRunner::MENU_DELETED) 767 MenuRunner::MENU_DELETED) {
460 return; 768 return;
769 }
461 dropdown_open_ = false; 770 dropdown_open_ = false;
771 if (arrow_button_)
772 arrow_button_->SetState(original_state);
462 closed_time_ = base::Time::Now(); 773 closed_time_ = base::Time::Now();
463 774
464 // Need to explicitly clear mouse handler so that events get sent 775 // Need to explicitly clear mouse handler so that events get sent
465 // properly after the menu finishes running. If we don't do this, then 776 // properly after the menu finishes running. If we don't do this, then
466 // the first click to other parts of the UI is eaten. 777 // the first click to other parts of the UI is eaten.
467 SetMouseHandler(NULL); 778 SetMouseHandler(NULL);
468 } 779 }
469 780
470 void Combobox::OnSelectionChanged() { 781 void Combobox::OnSelectionChanged() {
471 if (listener_) 782 if (listener_)
472 listener_->OnSelectedIndexChanged(this); 783 listener_->OnSelectedIndexChanged(this);
473 NotifyAccessibilityEvent(ui::AccessibilityTypes::EVENT_VALUE_CHANGED, false); 784 NotifyAccessibilityEvent(ui::AccessibilityTypes::EVENT_VALUE_CHANGED, false);
474 SchedulePaint(); 785 SchedulePaint();
475 } 786 }
476 787
477 int Combobox::MenuCommandToIndex(int menu_command_id) const { 788 int Combobox::MenuCommandToIndex(int menu_command_id) const {
478 // (note that the id received is offset by kFirstMenuItemId) 789 // (note that the id received is offset by kFirstMenuItemId)
479 // Revert menu ID offset to map back to combobox model. 790 // Revert menu ID offset to map back to combobox model.
480 int index = menu_command_id - kFirstMenuItemId; 791 int index = menu_command_id - kFirstMenuItemId;
481 DCHECK_LT(index, model()->GetItemCount()); 792 DCHECK_LT(index, model()->GetItemCount());
482 return index; 793 return index;
483 } 794 }
484 795
796 int Combobox::GetDisclosureArrowLeftPadding() const {
797 switch (style_) {
798 case STYLE_SHOW_DROP_DOWN_ON_CLICK:
799 return kDisclosureArrowLeftPadding;
800 case STYLE_NOTIFY_ON_CLICK:
801 return kDisclosureArrowButtonLeftPadding;
802 }
803 NOTREACHED();
804 return 0;
805 }
806
807 int Combobox::GetDisclosureArrowRightPadding() const {
808 switch (style_) {
809 case STYLE_SHOW_DROP_DOWN_ON_CLICK:
810 return kDisclosureArrowRightPadding;
811 case STYLE_NOTIFY_ON_CLICK:
812 return kDisclosureArrowButtonRightPadding;
813 }
814 NOTREACHED();
815 return 0;
816 }
817
818 void Combobox::HandleClickEvent() {
819 if (style_ != STYLE_NOTIFY_ON_CLICK)
820 return;
821
822 if (listener_)
823 listener_->OnComboboxTextButtonClicked(this);
824 }
825
485 } // namespace views 826 } // namespace views
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698