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

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: +curlies 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
« no previous file with comments | « ui/views/controls/combobox/combobox.h ('k') | ui/views/controls/combobox/combobox_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 private:
233 bool UseCheckmarks() const {
234 return owner_->style_ != STYLE_ACTION &&
235 MenuConfig::instance(owner_->GetNativeTheme())
236 .check_selected_combobox_item;
237 }
238
239 // Overridden from MenuModel:
240 bool HasIcons() const override { return false; }
241
242 int GetItemCount() const override { return model_->GetItemCount(); }
243
244 ItemType GetTypeAt(int index) const override {
245 if (model_->IsItemSeparatorAt(index)) {
246 // In action menus, disallow <item>, <separator>, ... since that would put
247 // a separator at the top of the menu.
248 DCHECK(index != 1 || owner_->style_ != STYLE_ACTION);
249 return TYPE_SEPARATOR;
250 }
251 return UseCheckmarks() ? TYPE_CHECK : TYPE_COMMAND;
252 }
253
254 ui::MenuSeparatorType GetSeparatorTypeAt(int index) const override {
255 return ui::NORMAL_SEPARATOR;
256 }
257
258 int GetCommandIdAt(int index) const override {
259 return index + kFirstMenuItemId;
260 }
261
262 base::string16 GetLabelAt(int index) const override {
263 // Inserting the Unicode formatting characters if necessary so that the
264 // text is displayed correctly in right-to-left UIs.
265 base::string16 text = model_->GetItemAt(index);
266 base::i18n::AdjustStringForLocaleDirection(&text);
267 return text;
268 }
269
270 bool IsItemDynamicAt(int index) const override { return true; }
271
272 const gfx::FontList* GetLabelFontListAt(int index) const override {
273 return &GetFontList();
274 }
275
276 bool GetAcceleratorAt(int index,
277 ui::Accelerator* accelerator) const override {
278 return false;
279 }
280
281 bool IsItemCheckedAt(int index) const override {
282 return UseCheckmarks() && index == owner_->selected_index_;
283 }
284
285 int GetGroupIdAt(int index) const override { return -1; }
286
287 bool GetIconAt(int index, gfx::Image* icon) override { return false; }
288
289 ui::ButtonMenuItemModel* GetButtonMenuItemAt(int index) const override {
290 return nullptr;
291 }
292
293 bool IsEnabledAt(int index) const override {
294 return model_->IsItemEnabledAt(index);
295 }
296
297 bool IsVisibleAt(int index) const override {
298 // When STYLE_ACTION is used, the first item is not added to the menu. It is
299 // assumed that the first item is always selected and rendered on the top of
300 // the action button.
301 return index > 0 || owner_->style_ != STYLE_ACTION;
302 }
303
304 void HighlightChangedTo(int index) override {}
305
306 void ActivatedAt(int index) override {
307 owner_->selected_index_ = index;
308 owner_->OnPerformAction();
309 }
310
311 void ActivatedAt(int index, int event_flags) override { ActivatedAt(index); }
312
313 MenuModel* GetSubmenuModelAt(int index) const override { return nullptr; }
314
315 void SetMenuModelDelegate(
316 ui::MenuModelDelegate* menu_model_delegate) override {}
317
318 ui::MenuModelDelegate* GetMenuModelDelegate() const override {
319 return nullptr;
320 }
321
322 // Overridden from ComboboxModelObserver:
323 void OnComboboxModelChanged(ui::ComboboxModel* model) override {
324 owner_->ModelChanged();
325 }
326
327 Combobox* owner_; // Weak. Owns this.
328 ui::ComboboxModel* model_; // Weak.
329
330 DISALLOW_COPY_AND_ASSIGN(ComboboxMenuModelAdapter);
331 };
332
227 //////////////////////////////////////////////////////////////////////////////// 333 ////////////////////////////////////////////////////////////////////////////////
228 // Combobox, public: 334 // Combobox, public:
229 335
230 Combobox::Combobox(ui::ComboboxModel* model) 336 Combobox::Combobox(ui::ComboboxModel* model)
231 : model_(model), 337 : model_(model),
232 style_(STYLE_NORMAL), 338 style_(STYLE_NORMAL),
233 listener_(NULL), 339 listener_(NULL),
234 selected_index_(model_->GetDefaultIndex()), 340 selected_index_(model_->GetDefaultIndex()),
235 invalid_(false), 341 invalid_(false),
236 menu_(NULL), 342 menu_model_adapter_(new ComboboxMenuModelAdapter(this, model)),
237 dropdown_open_(false),
238 text_button_(new TransparentButton(this)), 343 text_button_(new TransparentButton(this)),
239 arrow_button_(new TransparentButton(this)), 344 arrow_button_(new TransparentButton(this)),
240 weak_ptr_factory_(this) { 345 weak_ptr_factory_(this) {
241 model_->AddObserver(this); 346 ModelChanged();
242 UpdateFromModel();
243 SetFocusable(true); 347 SetFocusable(true);
244 UpdateBorder(); 348 UpdateBorder();
245 349
246 // Initialize the button images. 350 // Initialize the button images.
247 Button::ButtonState button_states[] = { 351 Button::ButtonState button_states[] = {
248 Button::STATE_DISABLED, 352 Button::STATE_DISABLED,
249 Button::STATE_NORMAL, 353 Button::STATE_NORMAL,
250 Button::STATE_HOVERED, 354 Button::STATE_HOVERED,
251 Button::STATE_PRESSED, 355 Button::STATE_PRESSED,
252 }; 356 };
(...skipping 12 matching lines...) Expand all
265 369
266 text_button_->SetVisible(true); 370 text_button_->SetVisible(true);
267 arrow_button_->SetVisible(true); 371 arrow_button_->SetVisible(true);
268 text_button_->SetFocusable(false); 372 text_button_->SetFocusable(false);
269 arrow_button_->SetFocusable(false); 373 arrow_button_->SetFocusable(false);
270 AddChildView(text_button_); 374 AddChildView(text_button_);
271 AddChildView(arrow_button_); 375 AddChildView(arrow_button_);
272 } 376 }
273 377
274 Combobox::~Combobox() { 378 Combobox::~Combobox() {
275 model_->RemoveObserver(this);
276
277 if (GetInputMethod() && selector_.get()) { 379 if (GetInputMethod() && selector_.get()) {
278 // Combobox should have been blurred before destroy. 380 // Combobox should have been blurred before destroy.
279 DCHECK(selector_.get() != GetInputMethod()->GetTextInputClient()); 381 DCHECK(selector_.get() != GetInputMethod()->GetTextInputClient());
280 } 382 }
281 } 383 }
282 384
283 // static 385 // static
284 const gfx::FontList& Combobox::GetFontList() { 386 const gfx::FontList& Combobox::GetFontList() {
285 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); 387 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
286 return rb.GetFontList(ui::ResourceBundle::BaseFont); 388 return rb.GetFontList(ui::ResourceBundle::BaseFont);
287 } 389 }
288 390
289 void Combobox::SetStyle(Style style) { 391 void Combobox::SetStyle(Style style) {
290 if (style_ == style) 392 if (style_ == style)
291 return; 393 return;
292 394
293 style_ = style; 395 style_ = style;
294 if (style_ == STYLE_ACTION) 396 if (style_ == STYLE_ACTION)
295 selected_index_ = 0; 397 selected_index_ = 0;
296 398
297 UpdateBorder(); 399 UpdateBorder();
298 UpdateFromModel(); 400 content_size_ = GetContentSize();
299 PreferredSizeChanged(); 401 PreferredSizeChanged();
300 } 402 }
301 403
302 void Combobox::ModelChanged() { 404 void Combobox::ModelChanged() {
303 selected_index_ = std::min(0, model_->GetItemCount()); 405 // If the selection is no longer valid (or the model is empty), restore the
304 UpdateFromModel(); 406 // default index.
407 if (selected_index_ >= model_->GetItemCount() ||
408 model_->GetItemCount() == 0 ||
409 model_->IsItemSeparatorAt(selected_index_)) {
410 selected_index_ = model_->GetDefaultIndex();
411 }
412
413 content_size_ = GetContentSize();
305 PreferredSizeChanged(); 414 PreferredSizeChanged();
306 } 415 }
307 416
308 void Combobox::SetSelectedIndex(int index) { 417 void Combobox::SetSelectedIndex(int index) {
309 if (style_ == STYLE_ACTION) 418 if (style_ == STYLE_ACTION)
310 return; 419 return;
311 420
312 selected_index_ = index; 421 selected_index_ = index;
313 SchedulePaint(); 422 SchedulePaint();
314 } 423 }
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
359 text_button_width = width() - arrow_button_width; 468 text_button_width = width() - arrow_button_width;
360 break; 469 break;
361 } 470 }
362 } 471 }
363 472
364 int arrow_button_x = std::max(0, text_button_width); 473 int arrow_button_x = std::max(0, text_button_width);
365 text_button_->SetBounds(0, 0, std::max(0, text_button_width), height()); 474 text_button_->SetBounds(0, 0, std::max(0, text_button_width), height());
366 arrow_button_->SetBounds(arrow_button_x, 0, arrow_button_width, height()); 475 arrow_button_->SetBounds(arrow_button_x, 0, arrow_button_width, height());
367 } 476 }
368 477
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() { 478 int Combobox::GetRowCount() {
387 return model()->GetItemCount(); 479 return model()->GetItemCount();
388 } 480 }
389 481
390 int Combobox::GetSelectedRow() { 482 int Combobox::GetSelectedRow() {
391 return selected_index_; 483 return selected_index_;
392 } 484 }
393 485
394 void Combobox::SetSelectedRow(int row) { 486 void Combobox::SetSelectedRow(int row) {
395 int prev_index = selected_index_; 487 int prev_index = selected_index_;
(...skipping 12 matching lines...) Expand all
408 500
409 gfx::Size Combobox::GetPreferredSize() const { 501 gfx::Size Combobox::GetPreferredSize() const {
410 // The preferred size will drive the local bounds which in turn is used to set 502 // The preferred size will drive the local bounds which in turn is used to set
411 // the minimum width for the dropdown list. 503 // the minimum width for the dropdown list.
412 gfx::Insets insets = GetInsets(); 504 gfx::Insets insets = GetInsets();
413 insets += gfx::Insets(Textfield::kTextPadding, 505 insets += gfx::Insets(Textfield::kTextPadding,
414 Textfield::kTextPadding, 506 Textfield::kTextPadding,
415 Textfield::kTextPadding, 507 Textfield::kTextPadding,
416 Textfield::kTextPadding); 508 Textfield::kTextPadding);
417 int total_width = std::max(kMinComboboxWidth, content_size_.width()) + 509 int total_width = std::max(kMinComboboxWidth, content_size_.width()) +
418 insets.width() + GetDisclosureArrowLeftPadding() + 510 insets.width() + GetDisclosureArrowLeftPadding() +
419 ArrowSize().width() + GetDisclosureArrowRightPadding(); 511 ArrowSize().width() + GetDisclosureArrowRightPadding();
420 return gfx::Size(total_width, content_size_.height() + insets.height()); 512 return gfx::Size(total_width, content_size_.height() + insets.height());
421 } 513 }
422 514
423 const char* Combobox::GetClassName() const { 515 const char* Combobox::GetClassName() const {
424 return kViewClassName; 516 return kViewClassName;
425 } 517 }
426 518
427 bool Combobox::SkipDefaultKeyEventProcessing(const ui::KeyEvent& e) { 519 bool Combobox::SkipDefaultKeyEventProcessing(const ui::KeyEvent& e) {
428 // Escape should close the drop down list when it is active, not host UI. 520 // Escape should close the drop down list when it is active, not host UI.
429 if (e.key_code() != ui::VKEY_ESCAPE || 521 if (e.key_code() != ui::VKEY_ESCAPE ||
430 e.IsShiftDown() || e.IsControlDown() || e.IsAltDown()) { 522 e.IsShiftDown() || e.IsControlDown() || e.IsAltDown()) {
431 return false; 523 return false;
432 } 524 }
433 return dropdown_open_; 525 return menu_runner_;
434 } 526 }
435 527
436 bool Combobox::OnKeyPressed(const ui::KeyEvent& e) { 528 bool Combobox::OnKeyPressed(const ui::KeyEvent& e) {
437 // TODO(oshima): handle IME. 529 // TODO(oshima): handle IME.
438 DCHECK_EQ(e.type(), ui::ET_KEY_PRESSED); 530 DCHECK_EQ(e.type(), ui::ET_KEY_PRESSED);
439 531
440 DCHECK_GE(selected_index_, 0); 532 DCHECK_GE(selected_index_, 0);
441 DCHECK_LT(selected_index_, model()->GetItemCount()); 533 DCHECK_LT(selected_index_, model()->GetItemCount());
442 if (selected_index_ < 0 || selected_index_ > model()->GetItemCount()) 534 if (selected_index_ < 0 || selected_index_ > model()->GetItemCount())
443 selected_index_ = 0; 535 selected_index_ = 0;
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
493 if (style_ != STYLE_ACTION) 585 if (style_ != STYLE_ACTION)
494 return false; 586 return false;
495 OnPerformAction(); 587 OnPerformAction();
496 break; 588 break;
497 589
498 default: 590 default:
499 return false; 591 return false;
500 } 592 }
501 593
502 if (show_menu) { 594 if (show_menu) {
503 UpdateFromModel();
504 ShowDropDownMenu(ui::MENU_SOURCE_KEYBOARD); 595 ShowDropDownMenu(ui::MENU_SOURCE_KEYBOARD);
505 } else if (new_index != selected_index_ && new_index != kNoSelection && 596 } else if (new_index != selected_index_ && new_index != kNoSelection &&
506 style_ != STYLE_ACTION) { 597 style_ != STYLE_ACTION) {
507 DCHECK(!model()->IsItemSeparatorAt(new_index)); 598 DCHECK(!model()->IsItemSeparatorAt(new_index));
508 selected_index_ = new_index; 599 selected_index_ = new_index;
509 OnPerformAction(); 600 OnPerformAction();
510 } 601 }
511 602
512 return true; 603 return true;
513 } 604 }
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
558 } 649 }
559 650
560 void Combobox::GetAccessibleState(ui::AXViewState* state) { 651 void Combobox::GetAccessibleState(ui::AXViewState* state) {
561 state->role = ui::AX_ROLE_COMBO_BOX; 652 state->role = ui::AX_ROLE_COMBO_BOX;
562 state->name = accessible_name_; 653 state->name = accessible_name_;
563 state->value = model_->GetItemAt(selected_index_); 654 state->value = model_->GetItemAt(selected_index_);
564 state->index = selected_index_; 655 state->index = selected_index_;
565 state->count = model_->GetItemCount(); 656 state->count = model_->GetItemCount();
566 } 657 }
567 658
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) { 659 void Combobox::ButtonPressed(Button* sender, const ui::Event& event) {
574 if (!enabled()) 660 if (!enabled())
575 return; 661 return;
576 662
577 RequestFocus(); 663 RequestFocus();
578 664
579 if (sender == text_button_) { 665 if (sender == text_button_) {
580 OnPerformAction(); 666 OnPerformAction();
581 } else { 667 } else {
582 DCHECK_EQ(arrow_button_, sender); 668 DCHECK_EQ(arrow_button_, sender);
583 // TODO(hajimehoshi): Fix the problem that the arrow button blinks when 669 // TODO(hajimehoshi): Fix the problem that the arrow button blinks when
584 // cliking this while the dropdown menu is opened. 670 // cliking this while the dropdown menu is opened.
585 const base::TimeDelta delta = base::Time::Now() - closed_time_; 671 const base::TimeDelta delta = base::Time::Now() - closed_time_;
586 if (delta.InMilliseconds() <= kMinimumMsBetweenButtonClicks) 672 if (delta.InMilliseconds() <= kMinimumMsBetweenButtonClicks)
587 return; 673 return;
588 674
589 ui::MenuSourceType source_type = ui::MENU_SOURCE_MOUSE; 675 ui::MenuSourceType source_type = ui::MENU_SOURCE_MOUSE;
590 if (event.IsKeyEvent()) 676 if (event.IsKeyEvent())
591 source_type = ui::MENU_SOURCE_KEYBOARD; 677 source_type = ui::MENU_SOURCE_KEYBOARD;
592 else if (event.IsGestureEvent() || event.IsTouchEvent()) 678 else if (event.IsGestureEvent() || event.IsTouchEvent())
593 source_type = ui::MENU_SOURCE_TOUCH; 679 source_type = ui::MENU_SOURCE_TOUCH;
594 ShowDropDownMenu(source_type); 680 ShowDropDownMenu(source_type);
595 } 681 }
596 } 682 }
597 683
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() { 684 void Combobox::UpdateBorder() {
637 scoped_ptr<FocusableBorder> border(new FocusableBorder()); 685 scoped_ptr<FocusableBorder> border(new FocusableBorder());
638 if (style_ == STYLE_ACTION) 686 if (style_ == STYLE_ACTION)
639 border->SetInsets(5, 10, 5, 10); 687 border->SetInsets(5, 10, 5, 10);
640 if (invalid_) 688 if (invalid_)
641 border->SetColor(kWarningColor); 689 border->SetColor(kWarningColor);
642 SetBorder(border.Pass()); 690 SetBorder(border.Pass());
643 } 691 }
644 692
645 void Combobox::AdjustBoundsForRTLUI(gfx::Rect* rect) const { 693 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); 799 canvas->SaveLayerAlpha(arrow_button_hover_alpha);
752 const std::vector<const gfx::ImageSkia*>& arrow_button_hovered_images = 800 const std::vector<const gfx::ImageSkia*>& arrow_button_hovered_images =
753 menu_button_images_[focused][Button::STATE_HOVERED]; 801 menu_button_images_[focused][Button::STATE_HOVERED];
754 PaintArrowButton(canvas, arrow_button_hovered_images, 802 PaintArrowButton(canvas, arrow_button_hovered_images,
755 arrow_button_->x(), height()); 803 arrow_button_->x(), height());
756 canvas->Restore(); 804 canvas->Restore();
757 } 805 }
758 } 806 }
759 807
760 void Combobox::ShowDropDownMenu(ui::MenuSourceType source_type) { 808 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(); 809 gfx::Rect lb = GetLocalBounds();
770 gfx::Point menu_position(lb.origin()); 810 gfx::Point menu_position(lb.origin());
771 811
772 if (style_ == STYLE_NORMAL) { 812 if (style_ == STYLE_NORMAL) {
773 // Inset the menu's requested position so the border of the menu lines up 813 // Inset the menu's requested position so the border of the menu lines up
774 // with the border of the combobox. 814 // with the border of the combobox.
775 menu_position.set_x(menu_position.x() + kMenuBorderWidthLeft); 815 menu_position.set_x(menu_position.x() + kMenuBorderWidthLeft);
776 menu_position.set_y(menu_position.y() + kMenuBorderWidthTop); 816 menu_position.set_y(menu_position.y() + kMenuBorderWidthTop);
777 } 817 }
778 lb.set_width(lb.width() - (kMenuBorderWidthLeft + kMenuBorderWidthRight)); 818 lb.set_width(lb.width() - (kMenuBorderWidthLeft + kMenuBorderWidthRight));
779 819
780 View::ConvertPointToScreen(this, &menu_position); 820 View::ConvertPointToScreen(this, &menu_position);
781 if (menu_position.x() < 0) 821 if (menu_position.x() < 0)
782 menu_position.set_x(0); 822 menu_position.set_x(0);
783 823
784 gfx::Rect bounds(menu_position, lb.size()); 824 gfx::Rect bounds(menu_position, lb.size());
785 825
786 Button::ButtonState original_state = Button::STATE_NORMAL; 826 Button::ButtonState original_state = Button::STATE_NORMAL;
787 if (arrow_button_) { 827 if (arrow_button_) {
788 original_state = arrow_button_->state(); 828 original_state = arrow_button_->state();
789 arrow_button_->SetState(Button::STATE_PRESSED); 829 arrow_button_->SetState(Button::STATE_PRESSED);
790 } 830 }
791 dropdown_open_ = true;
792 MenuAnchorPosition anchor_position = 831 MenuAnchorPosition anchor_position =
793 style_ == STYLE_ACTION ? MENU_ANCHOR_TOPRIGHT : MENU_ANCHOR_TOPLEFT; 832 style_ == STYLE_ACTION ? MENU_ANCHOR_TOPRIGHT : MENU_ANCHOR_TOPLEFT;
794 if (dropdown_list_menu_runner_->RunMenuAt( 833
795 GetWidget(), NULL, bounds, anchor_position, source_type) == 834 // Allow |menu_runner_| to be set by the testing API, but if this method is
796 MenuRunner::MENU_DELETED) { 835 // ever invoked recursively, ensure the old menu is closed.
836 if (!menu_runner_ || menu_runner_->IsRunning()) {
837 menu_runner_.reset(
838 new MenuRunner(menu_model_adapter_.get(), MenuRunner::COMBOBOX));
839 }
840 if (menu_runner_->RunMenuAt(GetWidget(), nullptr, bounds, anchor_position,
841 source_type) == MenuRunner::MENU_DELETED) {
797 return; 842 return;
798 } 843 }
799 dropdown_open_ = false; 844 menu_runner_.reset();
800 if (arrow_button_) 845 if (arrow_button_)
801 arrow_button_->SetState(original_state); 846 arrow_button_->SetState(original_state);
802 closed_time_ = base::Time::Now(); 847 closed_time_ = base::Time::Now();
803 848
804 // Need to explicitly clear mouse handler so that events get sent 849 // 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 850 // 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. 851 // the first click to other parts of the UI is eaten.
807 SetMouseHandler(NULL); 852 SetMouseHandler(NULL);
808 } 853 }
809 854
810 void Combobox::OnPerformAction() { 855 void Combobox::OnPerformAction() {
811 NotifyAccessibilityEvent(ui::AX_EVENT_VALUE_CHANGED, false); 856 NotifyAccessibilityEvent(ui::AX_EVENT_VALUE_CHANGED, false);
812 SchedulePaint(); 857 SchedulePaint();
813 858
814 // This combobox may be deleted by the listener. 859 // This combobox may be deleted by the listener.
815 base::WeakPtr<Combobox> weak_ptr = weak_ptr_factory_.GetWeakPtr(); 860 base::WeakPtr<Combobox> weak_ptr = weak_ptr_factory_.GetWeakPtr();
816 if (listener_) 861 if (listener_)
817 listener_->OnPerformAction(this); 862 listener_->OnPerformAction(this);
818 863
819 if (weak_ptr && style_ == STYLE_ACTION) 864 if (weak_ptr && style_ == STYLE_ACTION)
820 selected_index_ = 0; 865 selected_index_ = 0;
821 } 866 }
822 867
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 { 868 int Combobox::GetDisclosureArrowLeftPadding() const {
832 switch (style_) { 869 switch (style_) {
833 case STYLE_NORMAL: 870 case STYLE_NORMAL:
834 return kDisclosureArrowLeftPadding; 871 return kDisclosureArrowLeftPadding;
835 case STYLE_ACTION: 872 case STYLE_ACTION:
836 return kDisclosureArrowButtonLeftPadding; 873 return kDisclosureArrowButtonLeftPadding;
837 } 874 }
838 NOTREACHED(); 875 NOTREACHED();
839 return 0; 876 return 0;
840 } 877 }
(...skipping 20 matching lines...) Expand all
861 #else 898 #else
862 const ui::NativeTheme* native_theme_for_arrow = GetNativeTheme(); 899 const ui::NativeTheme* native_theme_for_arrow = GetNativeTheme();
863 #endif 900 #endif
864 901
865 ui::NativeTheme::ExtraParams ignored; 902 ui::NativeTheme::ExtraParams ignored;
866 return native_theme_for_arrow->GetPartSize(ui::NativeTheme::kComboboxArrow, 903 return native_theme_for_arrow->GetPartSize(ui::NativeTheme::kComboboxArrow,
867 ui::NativeTheme::kNormal, 904 ui::NativeTheme::kNormal,
868 ignored); 905 ignored);
869 } 906 }
870 907
908 gfx::Size Combobox::GetContentSize() const {
909 const gfx::FontList& font_list = GetFontList();
910
911 int width = 0;
912 for (int i = 0; i < model()->GetItemCount(); ++i) {
913 if (model_->IsItemSeparatorAt(i))
914 continue;
915
916 if (style_ != STYLE_ACTION || i == selected_index_) {
917 width = std::max(
918 width,
919 gfx::GetStringWidth(menu_model_adapter_->GetLabelAt(i), font_list));
920 }
921 }
922 return gfx::Size(width, font_list.GetHeight());
923 }
924
871 PrefixSelector* Combobox::GetPrefixSelector() { 925 PrefixSelector* Combobox::GetPrefixSelector() {
872 if (!selector_) 926 if (!selector_)
873 selector_.reset(new PrefixSelector(this)); 927 selector_.reset(new PrefixSelector(this));
874 return selector_.get(); 928 return selector_.get();
875 } 929 }
876 930
877 } // namespace views 931 } // namespace views
OLDNEW
« no previous file with comments | « ui/views/controls/combobox/combobox.h ('k') | ui/views/controls/combobox/combobox_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698