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

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

Issue 1310903004: Use a ui::MenuModel in views::Combobox (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Split out Cocoa changes to http://crrev.com/1321023005 Created 5 years, 3 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 "base/bind.h"
8 #include "base/logging.h" 7 #include "base/logging.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "ui/accessibility/ax_view_state.h" 8 #include "ui/accessibility/ax_view_state.h"
11 #include "ui/base/ime/input_method.h" 9 #include "ui/base/ime/input_method.h"
12 #include "ui/base/models/combobox_model.h" 10 #include "ui/base/models/combobox_model.h"
11 #include "ui/base/models/combobox_model_observer.h"
13 #include "ui/base/resource/resource_bundle.h" 12 #include "ui/base/resource/resource_bundle.h"
14 #include "ui/events/event.h" 13 #include "ui/events/event.h"
15 #include "ui/events/keycodes/keyboard_codes.h"
16 #include "ui/gfx/animation/throb_animation.h" 14 #include "ui/gfx/animation/throb_animation.h"
17 #include "ui/gfx/canvas.h" 15 #include "ui/gfx/canvas.h"
18 #include "ui/gfx/image/image.h"
19 #include "ui/gfx/scoped_canvas.h" 16 #include "ui/gfx/scoped_canvas.h"
20 #include "ui/gfx/text_utils.h" 17 #include "ui/gfx/text_utils.h"
21 #include "ui/native_theme/common_theme.h" 18 #include "ui/native_theme/common_theme.h"
22 #include "ui/native_theme/native_theme.h" 19 #include "ui/native_theme/native_theme.h"
23 #include "ui/resources/grit/ui_resources.h" 20 #include "ui/resources/grit/ui_resources.h"
24 #include "ui/views/background.h"
25 #include "ui/views/color_constants.h" 21 #include "ui/views/color_constants.h"
26 #include "ui/views/controls/button/custom_button.h" 22 #include "ui/views/controls/button/custom_button.h"
27 #include "ui/views/controls/button/label_button.h" 23 #include "ui/views/controls/button/label_button.h"
28 #include "ui/views/controls/combobox/combobox_listener.h" 24 #include "ui/views/controls/combobox/combobox_listener.h"
29 #include "ui/views/controls/focusable_border.h" 25 #include "ui/views/controls/focusable_border.h"
30 #include "ui/views/controls/menu/menu_item_view.h" 26 #include "ui/views/controls/menu/menu_config.h"
31 #include "ui/views/controls/menu/menu_runner.h" 27 #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" 28 #include "ui/views/controls/prefix_selector.h"
35 #include "ui/views/controls/textfield/textfield.h" 29 #include "ui/views/controls/textfield/textfield.h"
36 #include "ui/views/mouse_constants.h" 30 #include "ui/views/mouse_constants.h"
37 #include "ui/views/painter.h" 31 #include "ui/views/painter.h"
38 #include "ui/views/resources/grit/views_resources.h" 32 #include "ui/views/resources/grit/views_resources.h"
39 #include "ui/views/widget/widget.h" 33 #include "ui/views/widget/widget.h"
40 34
41 namespace views { 35 namespace views {
42 36
43 namespace { 37 namespace {
(...skipping 173 matching lines...) Expand 10 before | Expand all | Expand 10 after
217 *arrow_button_images[1], 211 *arrow_button_images[1],
218 *arrow_button_images[2], 212 *arrow_button_images[2],
219 x, 0, arrow_button_images[0]->width(), height); 213 x, 0, arrow_button_images[0]->width(), height);
220 } 214 }
221 215
222 } // namespace 216 } // namespace
223 217
224 // static 218 // static
225 const char Combobox::kViewClassName[] = "views/Combobox"; 219 const char Combobox::kViewClassName[] = "views/Combobox";
226 220
221 // Adapts a ui::ComboboxModel to a ui::MenuModel.
222 class Combobox::ComboboxMenuModelAdapter : public ui::MenuModel,
223 public ui::ComboboxModelObserver {
224 public:
225 ComboboxMenuModelAdapter(Combobox* owner, ui::ComboboxModel* model)
226 : owner_(owner), model_(model) {
227 model_->AddObserver(this);
228 }
229
230 ~ComboboxMenuModelAdapter() override { model_->RemoveObserver(this); }
231
232 // Finds the size of the largest menu label or, for STYLE_ACTION, the size of
233 // the selected label.
234 gfx::Size GetContentSize() const {
235 const gfx::FontList& font_list = GetFontList();
236 const int num_items = GetItemCount();
sky 2015/09/03 17:52:27 move into for loop.
tapted 2015/09/04 00:46:54 Done.
237
238 int width = 0;
239 for (int i = 0; i < num_items; ++i) {
240 if (model_->IsItemSeparatorAt(i))
241 continue;
242
243 if (owner_->style_ != STYLE_ACTION || i == owner_->selected_index_)
244 width = std::max(width, gfx::GetStringWidth(GetLabelAt(i), font_list));
245 }
246 return gfx::Size(width, font_list.GetHeight());
247 }
248
249 private:
250 bool UseCheckmarks() const {
251 return owner_->style_ != STYLE_ACTION &&
252 MenuConfig::instance(owner_->GetNativeTheme())
253 .check_selected_combobox_item;
254 }
255
256 // Overridden from MenuModel:
257 bool HasIcons() const override { return false; }
258
259 int GetItemCount() const override { return model_->GetItemCount(); }
260
261 ItemType GetTypeAt(int index) const override {
262 if (model_->IsItemSeparatorAt(index)) {
263 // In action menus, disallow <item>, <separator>, ... since that would put
264 // a separator at the top of the menu.
265 DCHECK(index != 1 || owner_->style_ != STYLE_ACTION);
266 return TYPE_SEPARATOR;
267 }
268 return UseCheckmarks() ? TYPE_CHECK : TYPE_COMMAND;
269 }
270
271 ui::MenuSeparatorType GetSeparatorTypeAt(int index) const override {
272 return ui::NORMAL_SEPARATOR;
273 }
274
275 int GetCommandIdAt(int index) const override {
276 return index + kFirstMenuItemId;
277 }
278
279 base::string16 GetLabelAt(int index) const override {
280 // Inserting the Unicode formatting characters if necessary so that the
281 // text is displayed correctly in right-to-left UIs.
282 base::string16 text = model_->GetItemAt(index);
283 base::i18n::AdjustStringForLocaleDirection(&text);
284 return text;
285 }
286
287 bool IsItemDynamicAt(int index) const override { return true; }
288
289 const gfx::FontList* GetLabelFontListAt(int index) const override {
290 return &GetFontList();
291 }
292
293 bool GetAcceleratorAt(int index,
294 ui::Accelerator* accelerator) const override {
295 return false;
296 }
297
298 bool IsItemCheckedAt(int index) const override {
299 return UseCheckmarks() && index == owner_->selected_index_;
300 }
301
302 int GetGroupIdAt(int index) const override { return -1; }
303
304 bool GetIconAt(int index, gfx::Image* icon) override { return false; }
305
306 ui::ButtonMenuItemModel* GetButtonMenuItemAt(int index) const override {
307 return nullptr;
308 }
309
310 bool IsEnabledAt(int index) const override {
311 return model_->IsItemEnabledAt(index);
312 }
313
314 bool IsVisibleAt(int index) const override {
315 // When STYLE_ACTION is used, the first item is not added to the menu. It is
316 // assumed that the first item is always selected and rendered on the top of
317 // the action button.
318 return index > 0 || owner_->style_ != STYLE_ACTION;
319 }
320
321 void HighlightChangedTo(int index) override {}
322
323 void ActivatedAt(int index) override {
324 owner_->selected_index_ = index;
325 owner_->OnPerformAction();
326 }
327
328 void ActivatedAt(int index, int event_flags) override { ActivatedAt(index); }
329
330 MenuModel* GetSubmenuModelAt(int index) const override { return nullptr; }
331
332 void SetMenuModelDelegate(
333 ui::MenuModelDelegate* menu_model_delegate) override {}
334
335 ui::MenuModelDelegate* GetMenuModelDelegate() const override {
336 return nullptr;
337 }
338
339 // Overridden from ComboboxModelObserver:
340 void OnComboboxModelChanged(ui::ComboboxModel* model) override {
341 owner_->ModelChanged();
342 }
343
344 Combobox* owner_; // Weak. Owns this.
345 ui::ComboboxModel* model_; // Weak.
346
347 DISALLOW_COPY_AND_ASSIGN(ComboboxMenuModelAdapter);
348 };
349
227 //////////////////////////////////////////////////////////////////////////////// 350 ////////////////////////////////////////////////////////////////////////////////
228 // Combobox, public: 351 // Combobox, public:
229 352
230 Combobox::Combobox(ui::ComboboxModel* model) 353 Combobox::Combobox(ui::ComboboxModel* model)
231 : model_(model), 354 : model_(model),
232 style_(STYLE_NORMAL), 355 style_(STYLE_NORMAL),
233 listener_(NULL), 356 listener_(NULL),
234 selected_index_(model_->GetDefaultIndex()), 357 selected_index_(model_->GetDefaultIndex()),
235 invalid_(false), 358 invalid_(false),
236 menu_(NULL), 359 menu_model_adapter_(new ComboboxMenuModelAdapter(this, model)),
237 dropdown_open_(false),
238 text_button_(new TransparentButton(this)), 360 text_button_(new TransparentButton(this)),
239 arrow_button_(new TransparentButton(this)), 361 arrow_button_(new TransparentButton(this)),
240 weak_ptr_factory_(this) { 362 weak_ptr_factory_(this) {
241 model_->AddObserver(this); 363 ModelChanged();
242 UpdateFromModel();
243 SetFocusable(true); 364 SetFocusable(true);
244 UpdateBorder(); 365 UpdateBorder();
245 366
246 // Initialize the button images. 367 // Initialize the button images.
247 Button::ButtonState button_states[] = { 368 Button::ButtonState button_states[] = {
248 Button::STATE_DISABLED, 369 Button::STATE_DISABLED,
249 Button::STATE_NORMAL, 370 Button::STATE_NORMAL,
250 Button::STATE_HOVERED, 371 Button::STATE_HOVERED,
251 Button::STATE_PRESSED, 372 Button::STATE_PRESSED,
252 }; 373 };
(...skipping 12 matching lines...) Expand all
265 386
266 text_button_->SetVisible(true); 387 text_button_->SetVisible(true);
267 arrow_button_->SetVisible(true); 388 arrow_button_->SetVisible(true);
268 text_button_->SetFocusable(false); 389 text_button_->SetFocusable(false);
269 arrow_button_->SetFocusable(false); 390 arrow_button_->SetFocusable(false);
270 AddChildView(text_button_); 391 AddChildView(text_button_);
271 AddChildView(arrow_button_); 392 AddChildView(arrow_button_);
272 } 393 }
273 394
274 Combobox::~Combobox() { 395 Combobox::~Combobox() {
275 model_->RemoveObserver(this);
276
277 if (GetInputMethod() && selector_.get()) { 396 if (GetInputMethod() && selector_.get()) {
278 // Combobox should have been blurred before destroy. 397 // Combobox should have been blurred before destroy.
279 DCHECK(selector_.get() != GetInputMethod()->GetTextInputClient()); 398 DCHECK(selector_.get() != GetInputMethod()->GetTextInputClient());
280 } 399 }
281 } 400 }
282 401
283 // static 402 // static
284 const gfx::FontList& Combobox::GetFontList() { 403 const gfx::FontList& Combobox::GetFontList() {
285 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); 404 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
286 return rb.GetFontList(ui::ResourceBundle::BaseFont); 405 return rb.GetFontList(ui::ResourceBundle::BaseFont);
287 } 406 }
288 407
289 void Combobox::SetStyle(Style style) { 408 void Combobox::SetStyle(Style style) {
290 if (style_ == style) 409 if (style_ == style)
291 return; 410 return;
292 411
293 style_ = style; 412 style_ = style;
294 if (style_ == STYLE_ACTION) 413 if (style_ == STYLE_ACTION)
295 selected_index_ = 0; 414 selected_index_ = 0;
296 415
297 UpdateBorder(); 416 UpdateBorder();
298 UpdateFromModel(); 417 content_size_ = menu_model_adapter_->GetContentSize();
299 PreferredSizeChanged(); 418 PreferredSizeChanged();
300 } 419 }
301 420
302 void Combobox::ModelChanged() { 421 void Combobox::ModelChanged() {
303 selected_index_ = std::min(0, model_->GetItemCount()); 422 selected_index_ = std::min(model_->GetDefaultIndex(), model_->GetItemCount());
tapted 2015/09/03 12:52:26 The old code did `index_ = std::min(0, itemcount)`
sky 2015/09/03 17:52:27 I suspect the old code really wanted something lik
tapted 2015/09/04 00:46:54 Ah, yeah using GetItemCount() would only make sens
sky 2015/09/04 14:34:17 I think GetDefaultIndex() is used during construct
tapted 2015/09/07 00:37:27 Sorry - misunderstood your comment. Changed this t
304 UpdateFromModel(); 423 content_size_ = menu_model_adapter_->GetContentSize();
305 PreferredSizeChanged(); 424 PreferredSizeChanged();
306 } 425 }
307 426
308 void Combobox::SetSelectedIndex(int index) { 427 void Combobox::SetSelectedIndex(int index) {
309 if (style_ == STYLE_ACTION) 428 if (style_ == STYLE_ACTION)
310 return; 429 return;
311 430
312 selected_index_ = index; 431 selected_index_ = index;
313 SchedulePaint(); 432 SchedulePaint();
314 } 433 }
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
359 text_button_width = width() - arrow_button_width; 478 text_button_width = width() - arrow_button_width;
360 break; 479 break;
361 } 480 }
362 } 481 }
363 482
364 int arrow_button_x = std::max(0, text_button_width); 483 int arrow_button_x = std::max(0, text_button_width);
365 text_button_->SetBounds(0, 0, std::max(0, text_button_width), height()); 484 text_button_->SetBounds(0, 0, std::max(0, text_button_width), height());
366 arrow_button_->SetBounds(arrow_button_x, 0, arrow_button_width, height()); 485 arrow_button_->SetBounds(arrow_button_x, 0, arrow_button_width, height());
367 } 486 }
368 487
369 bool Combobox::IsItemChecked(int id) const {
370 return false;
371 }
372
373 bool Combobox::IsCommandEnabled(int id) const {
374 return model()->IsItemEnabledAt(MenuCommandToIndex(id));
375 }
376
377 void Combobox::ExecuteCommand(int id) {
378 selected_index_ = MenuCommandToIndex(id);
379 OnPerformAction();
380 }
381
382 bool Combobox::GetAccelerator(int id, ui::Accelerator* accel) const {
383 return false;
384 }
385
386 int Combobox::GetRowCount() { 488 int Combobox::GetRowCount() {
387 return model()->GetItemCount(); 489 return model()->GetItemCount();
388 } 490 }
389 491
390 int Combobox::GetSelectedRow() { 492 int Combobox::GetSelectedRow() {
391 return selected_index_; 493 return selected_index_;
392 } 494 }
393 495
394 void Combobox::SetSelectedRow(int row) { 496 void Combobox::SetSelectedRow(int row) {
395 int prev_index = selected_index_; 497 int prev_index = selected_index_;
(...skipping 12 matching lines...) Expand all
408 510
409 gfx::Size Combobox::GetPreferredSize() const { 511 gfx::Size Combobox::GetPreferredSize() const {
410 // The preferred size will drive the local bounds which in turn is used to set 512 // The preferred size will drive the local bounds which in turn is used to set
411 // the minimum width for the dropdown list. 513 // the minimum width for the dropdown list.
412 gfx::Insets insets = GetInsets(); 514 gfx::Insets insets = GetInsets();
413 insets += gfx::Insets(Textfield::kTextPadding, 515 insets += gfx::Insets(Textfield::kTextPadding,
414 Textfield::kTextPadding, 516 Textfield::kTextPadding,
415 Textfield::kTextPadding, 517 Textfield::kTextPadding,
416 Textfield::kTextPadding); 518 Textfield::kTextPadding);
417 int total_width = std::max(kMinComboboxWidth, content_size_.width()) + 519 int total_width = std::max(kMinComboboxWidth, content_size_.width()) +
418 insets.width() + GetDisclosureArrowLeftPadding() + 520 insets.width() + GetDisclosureArrowLeftPadding() +
419 ArrowSize().width() + GetDisclosureArrowRightPadding(); 521 ArrowSize().width() + GetDisclosureArrowRightPadding();
420 return gfx::Size(total_width, content_size_.height() + insets.height()); 522 return gfx::Size(total_width, content_size_.height() + insets.height());
421 } 523 }
422 524
423 const char* Combobox::GetClassName() const { 525 const char* Combobox::GetClassName() const {
424 return kViewClassName; 526 return kViewClassName;
425 } 527 }
426 528
427 bool Combobox::SkipDefaultKeyEventProcessing(const ui::KeyEvent& e) { 529 bool Combobox::SkipDefaultKeyEventProcessing(const ui::KeyEvent& e) {
428 // Escape should close the drop down list when it is active, not host UI. 530 // Escape should close the drop down list when it is active, not host UI.
429 if (e.key_code() != ui::VKEY_ESCAPE || 531 if (e.key_code() != ui::VKEY_ESCAPE ||
430 e.IsShiftDown() || e.IsControlDown() || e.IsAltDown()) { 532 e.IsShiftDown() || e.IsControlDown() || e.IsAltDown()) {
431 return false; 533 return false;
432 } 534 }
433 return dropdown_open_; 535 return menu_runner_;
434 } 536 }
435 537
436 bool Combobox::OnKeyPressed(const ui::KeyEvent& e) { 538 bool Combobox::OnKeyPressed(const ui::KeyEvent& e) {
437 // TODO(oshima): handle IME. 539 // TODO(oshima): handle IME.
438 DCHECK_EQ(e.type(), ui::ET_KEY_PRESSED); 540 DCHECK_EQ(e.type(), ui::ET_KEY_PRESSED);
439 541
440 DCHECK_GE(selected_index_, 0); 542 DCHECK_GE(selected_index_, 0);
441 DCHECK_LT(selected_index_, model()->GetItemCount()); 543 DCHECK_LT(selected_index_, model()->GetItemCount());
442 if (selected_index_ < 0 || selected_index_ > model()->GetItemCount()) 544 if (selected_index_ < 0 || selected_index_ > model()->GetItemCount())
443 selected_index_ = 0; 545 selected_index_ = 0;
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
493 if (style_ != STYLE_ACTION) 595 if (style_ != STYLE_ACTION)
494 return false; 596 return false;
495 OnPerformAction(); 597 OnPerformAction();
496 break; 598 break;
497 599
498 default: 600 default:
499 return false; 601 return false;
500 } 602 }
501 603
502 if (show_menu) { 604 if (show_menu) {
503 UpdateFromModel();
504 ShowDropDownMenu(ui::MENU_SOURCE_KEYBOARD); 605 ShowDropDownMenu(ui::MENU_SOURCE_KEYBOARD);
505 } else if (new_index != selected_index_ && new_index != kNoSelection && 606 } else if (new_index != selected_index_ && new_index != kNoSelection &&
506 style_ != STYLE_ACTION) { 607 style_ != STYLE_ACTION) {
507 DCHECK(!model()->IsItemSeparatorAt(new_index)); 608 DCHECK(!model()->IsItemSeparatorAt(new_index));
508 selected_index_ = new_index; 609 selected_index_ = new_index;
509 OnPerformAction(); 610 OnPerformAction();
510 } 611 }
511 612
512 return true; 613 return true;
513 } 614 }
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
558 } 659 }
559 660
560 void Combobox::GetAccessibleState(ui::AXViewState* state) { 661 void Combobox::GetAccessibleState(ui::AXViewState* state) {
561 state->role = ui::AX_ROLE_COMBO_BOX; 662 state->role = ui::AX_ROLE_COMBO_BOX;
562 state->name = accessible_name_; 663 state->name = accessible_name_;
563 state->value = model_->GetItemAt(selected_index_); 664 state->value = model_->GetItemAt(selected_index_);
564 state->index = selected_index_; 665 state->index = selected_index_;
565 state->count = model_->GetItemCount(); 666 state->count = model_->GetItemCount();
566 } 667 }
567 668
568 void Combobox::OnComboboxModelChanged(ui::ComboboxModel* model) {
569 DCHECK_EQ(model, model_);
570 ModelChanged();
571 }
572
573 void Combobox::ButtonPressed(Button* sender, const ui::Event& event) { 669 void Combobox::ButtonPressed(Button* sender, const ui::Event& event) {
574 if (!enabled()) 670 if (!enabled())
575 return; 671 return;
576 672
577 RequestFocus(); 673 RequestFocus();
578 674
579 if (sender == text_button_) { 675 if (sender == text_button_) {
580 OnPerformAction(); 676 OnPerformAction();
581 } else { 677 } else {
582 DCHECK_EQ(arrow_button_, sender); 678 DCHECK_EQ(arrow_button_, sender);
583 // TODO(hajimehoshi): Fix the problem that the arrow button blinks when 679 // TODO(hajimehoshi): Fix the problem that the arrow button blinks when
584 // cliking this while the dropdown menu is opened. 680 // cliking this while the dropdown menu is opened.
585 const base::TimeDelta delta = base::Time::Now() - closed_time_; 681 const base::TimeDelta delta = base::Time::Now() - closed_time_;
586 if (delta.InMilliseconds() <= kMinimumMsBetweenButtonClicks) 682 if (delta.InMilliseconds() <= kMinimumMsBetweenButtonClicks)
587 return; 683 return;
588 684
589 ui::MenuSourceType source_type = ui::MENU_SOURCE_MOUSE; 685 ui::MenuSourceType source_type = ui::MENU_SOURCE_MOUSE;
590 if (event.IsKeyEvent()) 686 if (event.IsKeyEvent())
591 source_type = ui::MENU_SOURCE_KEYBOARD; 687 source_type = ui::MENU_SOURCE_KEYBOARD;
592 else if (event.IsGestureEvent() || event.IsTouchEvent()) 688 else if (event.IsGestureEvent() || event.IsTouchEvent())
593 source_type = ui::MENU_SOURCE_TOUCH; 689 source_type = ui::MENU_SOURCE_TOUCH;
594 ShowDropDownMenu(source_type); 690 ShowDropDownMenu(source_type);
595 } 691 }
596 } 692 }
597 693
598 void Combobox::UpdateFromModel() {
599 const gfx::FontList& font_list = Combobox::GetFontList();
600
601 menu_ = new MenuItemView(this);
602 // MenuRunner owns |menu_|.
603 dropdown_list_menu_runner_.reset(new MenuRunner(menu_, MenuRunner::COMBOBOX));
604
605 int num_items = model()->GetItemCount();
606 int width = 0;
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();
615 continue;
616 }
617
618 base::string16 text = model()->GetItemAt(i);
619
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);
623
624 if (style_ != STYLE_ACTION || i > 0) {
625 menu_->AppendMenuItem(i + kFirstMenuItemId, text, MenuItemView::NORMAL);
626 text_item_appended = true;
627 }
628
629 if (style_ != STYLE_ACTION || i == selected_index_)
630 width = std::max(width, gfx::GetStringWidth(text, font_list));
631 }
632
633 content_size_.SetSize(width, font_list.GetHeight());
634 }
635
636 void Combobox::UpdateBorder() { 694 void Combobox::UpdateBorder() {
637 scoped_ptr<FocusableBorder> border(new FocusableBorder()); 695 scoped_ptr<FocusableBorder> border(new FocusableBorder());
638 if (style_ == STYLE_ACTION) 696 if (style_ == STYLE_ACTION)
639 border->SetInsets(5, 10, 5, 10); 697 border->SetInsets(5, 10, 5, 10);
640 if (invalid_) 698 if (invalid_)
641 border->SetColor(kWarningColor); 699 border->SetColor(kWarningColor);
642 SetBorder(border.Pass()); 700 SetBorder(border.Pass());
643 } 701 }
644 702
645 void Combobox::AdjustBoundsForRTLUI(gfx::Rect* rect) const { 703 void Combobox::AdjustBoundsForRTLUI(gfx::Rect* rect) const {
(...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after
751 canvas->SaveLayerAlpha(arrow_button_hover_alpha); 809 canvas->SaveLayerAlpha(arrow_button_hover_alpha);
752 const std::vector<const gfx::ImageSkia*>& arrow_button_hovered_images = 810 const std::vector<const gfx::ImageSkia*>& arrow_button_hovered_images =
753 menu_button_images_[focused][Button::STATE_HOVERED]; 811 menu_button_images_[focused][Button::STATE_HOVERED];
754 PaintArrowButton(canvas, arrow_button_hovered_images, 812 PaintArrowButton(canvas, arrow_button_hovered_images,
755 arrow_button_->x(), height()); 813 arrow_button_->x(), height());
756 canvas->Restore(); 814 canvas->Restore();
757 } 815 }
758 } 816 }
759 817
760 void Combobox::ShowDropDownMenu(ui::MenuSourceType source_type) { 818 void Combobox::ShowDropDownMenu(ui::MenuSourceType source_type) {
761 if (!dropdown_list_menu_runner_.get())
762 UpdateFromModel();
763
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));
768
769 gfx::Rect lb = GetLocalBounds(); 819 gfx::Rect lb = GetLocalBounds();
770 gfx::Point menu_position(lb.origin()); 820 gfx::Point menu_position(lb.origin());
771 821
772 if (style_ == STYLE_NORMAL) { 822 if (style_ == STYLE_NORMAL) {
773 // Inset the menu's requested position so the border of the menu lines up 823 // Inset the menu's requested position so the border of the menu lines up
774 // with the border of the combobox. 824 // with the border of the combobox.
775 menu_position.set_x(menu_position.x() + kMenuBorderWidthLeft); 825 menu_position.set_x(menu_position.x() + kMenuBorderWidthLeft);
776 menu_position.set_y(menu_position.y() + kMenuBorderWidthTop); 826 menu_position.set_y(menu_position.y() + kMenuBorderWidthTop);
777 } 827 }
778 lb.set_width(lb.width() - (kMenuBorderWidthLeft + kMenuBorderWidthRight)); 828 lb.set_width(lb.width() - (kMenuBorderWidthLeft + kMenuBorderWidthRight));
779 829
780 View::ConvertPointToScreen(this, &menu_position); 830 View::ConvertPointToScreen(this, &menu_position);
781 if (menu_position.x() < 0) 831 if (menu_position.x() < 0)
782 menu_position.set_x(0); 832 menu_position.set_x(0);
783 833
784 gfx::Rect bounds(menu_position, lb.size()); 834 gfx::Rect bounds(menu_position, lb.size());
785 835
786 Button::ButtonState original_state = Button::STATE_NORMAL; 836 Button::ButtonState original_state = Button::STATE_NORMAL;
787 if (arrow_button_) { 837 if (arrow_button_) {
788 original_state = arrow_button_->state(); 838 original_state = arrow_button_->state();
789 arrow_button_->SetState(Button::STATE_PRESSED); 839 arrow_button_->SetState(Button::STATE_PRESSED);
790 } 840 }
791 dropdown_open_ = true;
792 MenuAnchorPosition anchor_position = 841 MenuAnchorPosition anchor_position =
793 style_ == STYLE_ACTION ? MENU_ANCHOR_TOPRIGHT : MENU_ANCHOR_TOPLEFT; 842 style_ == STYLE_ACTION ? MENU_ANCHOR_TOPRIGHT : MENU_ANCHOR_TOPLEFT;
794 if (dropdown_list_menu_runner_->RunMenuAt( 843
795 GetWidget(), NULL, bounds, anchor_position, source_type) == 844 // Allow |menu_runner_| to be set by the testing API.
796 MenuRunner::MENU_DELETED) { 845 if (!menu_runner_) {
sky 2015/09/03 17:52:27 This makes me nervous, in particular what happens
tapted 2015/09/04 00:46:54 Done.
846 menu_runner_.reset(
847 new MenuRunner(menu_model_adapter_.get(), MenuRunner::COMBOBOX));
848 }
849 if (menu_runner_->RunMenuAt(GetWidget(), nullptr, bounds, anchor_position,
850 source_type) == MenuRunner::MENU_DELETED) {
797 return; 851 return;
798 } 852 }
799 dropdown_open_ = false; 853 menu_runner_.reset();
800 if (arrow_button_) 854 if (arrow_button_)
801 arrow_button_->SetState(original_state); 855 arrow_button_->SetState(original_state);
802 closed_time_ = base::Time::Now(); 856 closed_time_ = base::Time::Now();
803 857
804 // Need to explicitly clear mouse handler so that events get sent 858 // 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 859 // 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. 860 // the first click to other parts of the UI is eaten.
807 SetMouseHandler(NULL); 861 SetMouseHandler(NULL);
808 } 862 }
809 863
810 void Combobox::OnPerformAction() { 864 void Combobox::OnPerformAction() {
811 NotifyAccessibilityEvent(ui::AX_EVENT_VALUE_CHANGED, false); 865 NotifyAccessibilityEvent(ui::AX_EVENT_VALUE_CHANGED, false);
812 SchedulePaint(); 866 SchedulePaint();
813 867
814 // This combobox may be deleted by the listener. 868 // This combobox may be deleted by the listener.
815 base::WeakPtr<Combobox> weak_ptr = weak_ptr_factory_.GetWeakPtr(); 869 base::WeakPtr<Combobox> weak_ptr = weak_ptr_factory_.GetWeakPtr();
816 if (listener_) 870 if (listener_)
817 listener_->OnPerformAction(this); 871 listener_->OnPerformAction(this);
818 872
819 if (weak_ptr && style_ == STYLE_ACTION) 873 if (weak_ptr && style_ == STYLE_ACTION)
820 selected_index_ = 0; 874 selected_index_ = 0;
821 } 875 }
822 876
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());
828 return index;
829 }
830
831 int Combobox::GetDisclosureArrowLeftPadding() const { 877 int Combobox::GetDisclosureArrowLeftPadding() const {
832 switch (style_) { 878 switch (style_) {
833 case STYLE_NORMAL: 879 case STYLE_NORMAL:
834 return kDisclosureArrowLeftPadding; 880 return kDisclosureArrowLeftPadding;
835 case STYLE_ACTION: 881 case STYLE_ACTION:
836 return kDisclosureArrowButtonLeftPadding; 882 return kDisclosureArrowButtonLeftPadding;
837 } 883 }
838 NOTREACHED(); 884 NOTREACHED();
839 return 0; 885 return 0;
840 } 886 }
(...skipping 26 matching lines...) Expand all
867 ui::NativeTheme::kNormal, 913 ui::NativeTheme::kNormal,
868 ignored); 914 ignored);
869 } 915 }
870 916
871 PrefixSelector* Combobox::GetPrefixSelector() { 917 PrefixSelector* Combobox::GetPrefixSelector() {
872 if (!selector_) 918 if (!selector_)
873 selector_.reset(new PrefixSelector(this)); 919 selector_.reset(new PrefixSelector(this));
874 return selector_.get(); 920 return selector_.get();
875 } 921 }
876 922
923 ui::MenuModel* Combobox::GetMenuModelForTest() {
924 return menu_model_adapter_.get();
925 }
926
877 } // namespace views 927 } // namespace views
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698