OLD | NEW |
---|---|
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 Loading... | |
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 Loading... | |
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 || model_->IsItemSeparatorAt(selected_index_)) | |
409 selected_index_ = model_->GetDefaultIndex(); | |
sky
2015/09/08 16:23:12
nit: {}
tapted
2015/09/09 00:30:57
Done.
| |
410 | |
411 content_size_ = GetContentSize(); | |
305 PreferredSizeChanged(); | 412 PreferredSizeChanged(); |
306 } | 413 } |
307 | 414 |
308 void Combobox::SetSelectedIndex(int index) { | 415 void Combobox::SetSelectedIndex(int index) { |
309 if (style_ == STYLE_ACTION) | 416 if (style_ == STYLE_ACTION) |
310 return; | 417 return; |
311 | 418 |
312 selected_index_ = index; | 419 selected_index_ = index; |
313 SchedulePaint(); | 420 SchedulePaint(); |
314 } | 421 } |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
359 text_button_width = width() - arrow_button_width; | 466 text_button_width = width() - arrow_button_width; |
360 break; | 467 break; |
361 } | 468 } |
362 } | 469 } |
363 | 470 |
364 int arrow_button_x = std::max(0, text_button_width); | 471 int arrow_button_x = std::max(0, text_button_width); |
365 text_button_->SetBounds(0, 0, std::max(0, text_button_width), height()); | 472 text_button_->SetBounds(0, 0, std::max(0, text_button_width), height()); |
366 arrow_button_->SetBounds(arrow_button_x, 0, arrow_button_width, height()); | 473 arrow_button_->SetBounds(arrow_button_x, 0, arrow_button_width, height()); |
367 } | 474 } |
368 | 475 |
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() { | 476 int Combobox::GetRowCount() { |
387 return model()->GetItemCount(); | 477 return model()->GetItemCount(); |
388 } | 478 } |
389 | 479 |
390 int Combobox::GetSelectedRow() { | 480 int Combobox::GetSelectedRow() { |
391 return selected_index_; | 481 return selected_index_; |
392 } | 482 } |
393 | 483 |
394 void Combobox::SetSelectedRow(int row) { | 484 void Combobox::SetSelectedRow(int row) { |
395 int prev_index = selected_index_; | 485 int prev_index = selected_index_; |
(...skipping 12 matching lines...) Expand all Loading... | |
408 | 498 |
409 gfx::Size Combobox::GetPreferredSize() const { | 499 gfx::Size Combobox::GetPreferredSize() const { |
410 // The preferred size will drive the local bounds which in turn is used to set | 500 // The preferred size will drive the local bounds which in turn is used to set |
411 // the minimum width for the dropdown list. | 501 // the minimum width for the dropdown list. |
412 gfx::Insets insets = GetInsets(); | 502 gfx::Insets insets = GetInsets(); |
413 insets += gfx::Insets(Textfield::kTextPadding, | 503 insets += gfx::Insets(Textfield::kTextPadding, |
414 Textfield::kTextPadding, | 504 Textfield::kTextPadding, |
415 Textfield::kTextPadding, | 505 Textfield::kTextPadding, |
416 Textfield::kTextPadding); | 506 Textfield::kTextPadding); |
417 int total_width = std::max(kMinComboboxWidth, content_size_.width()) + | 507 int total_width = std::max(kMinComboboxWidth, content_size_.width()) + |
418 insets.width() + GetDisclosureArrowLeftPadding() + | 508 insets.width() + GetDisclosureArrowLeftPadding() + |
419 ArrowSize().width() + GetDisclosureArrowRightPadding(); | 509 ArrowSize().width() + GetDisclosureArrowRightPadding(); |
420 return gfx::Size(total_width, content_size_.height() + insets.height()); | 510 return gfx::Size(total_width, content_size_.height() + insets.height()); |
421 } | 511 } |
422 | 512 |
423 const char* Combobox::GetClassName() const { | 513 const char* Combobox::GetClassName() const { |
424 return kViewClassName; | 514 return kViewClassName; |
425 } | 515 } |
426 | 516 |
427 bool Combobox::SkipDefaultKeyEventProcessing(const ui::KeyEvent& e) { | 517 bool Combobox::SkipDefaultKeyEventProcessing(const ui::KeyEvent& e) { |
428 // Escape should close the drop down list when it is active, not host UI. | 518 // Escape should close the drop down list when it is active, not host UI. |
429 if (e.key_code() != ui::VKEY_ESCAPE || | 519 if (e.key_code() != ui::VKEY_ESCAPE || |
430 e.IsShiftDown() || e.IsControlDown() || e.IsAltDown()) { | 520 e.IsShiftDown() || e.IsControlDown() || e.IsAltDown()) { |
431 return false; | 521 return false; |
432 } | 522 } |
433 return dropdown_open_; | 523 return menu_runner_; |
434 } | 524 } |
435 | 525 |
436 bool Combobox::OnKeyPressed(const ui::KeyEvent& e) { | 526 bool Combobox::OnKeyPressed(const ui::KeyEvent& e) { |
437 // TODO(oshima): handle IME. | 527 // TODO(oshima): handle IME. |
438 DCHECK_EQ(e.type(), ui::ET_KEY_PRESSED); | 528 DCHECK_EQ(e.type(), ui::ET_KEY_PRESSED); |
439 | 529 |
440 DCHECK_GE(selected_index_, 0); | 530 DCHECK_GE(selected_index_, 0); |
441 DCHECK_LT(selected_index_, model()->GetItemCount()); | 531 DCHECK_LT(selected_index_, model()->GetItemCount()); |
442 if (selected_index_ < 0 || selected_index_ > model()->GetItemCount()) | 532 if (selected_index_ < 0 || selected_index_ > model()->GetItemCount()) |
443 selected_index_ = 0; | 533 selected_index_ = 0; |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
493 if (style_ != STYLE_ACTION) | 583 if (style_ != STYLE_ACTION) |
494 return false; | 584 return false; |
495 OnPerformAction(); | 585 OnPerformAction(); |
496 break; | 586 break; |
497 | 587 |
498 default: | 588 default: |
499 return false; | 589 return false; |
500 } | 590 } |
501 | 591 |
502 if (show_menu) { | 592 if (show_menu) { |
503 UpdateFromModel(); | |
504 ShowDropDownMenu(ui::MENU_SOURCE_KEYBOARD); | 593 ShowDropDownMenu(ui::MENU_SOURCE_KEYBOARD); |
505 } else if (new_index != selected_index_ && new_index != kNoSelection && | 594 } else if (new_index != selected_index_ && new_index != kNoSelection && |
506 style_ != STYLE_ACTION) { | 595 style_ != STYLE_ACTION) { |
507 DCHECK(!model()->IsItemSeparatorAt(new_index)); | 596 DCHECK(!model()->IsItemSeparatorAt(new_index)); |
508 selected_index_ = new_index; | 597 selected_index_ = new_index; |
509 OnPerformAction(); | 598 OnPerformAction(); |
510 } | 599 } |
511 | 600 |
512 return true; | 601 return true; |
513 } | 602 } |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
558 } | 647 } |
559 | 648 |
560 void Combobox::GetAccessibleState(ui::AXViewState* state) { | 649 void Combobox::GetAccessibleState(ui::AXViewState* state) { |
561 state->role = ui::AX_ROLE_COMBO_BOX; | 650 state->role = ui::AX_ROLE_COMBO_BOX; |
562 state->name = accessible_name_; | 651 state->name = accessible_name_; |
563 state->value = model_->GetItemAt(selected_index_); | 652 state->value = model_->GetItemAt(selected_index_); |
564 state->index = selected_index_; | 653 state->index = selected_index_; |
565 state->count = model_->GetItemCount(); | 654 state->count = model_->GetItemCount(); |
566 } | 655 } |
567 | 656 |
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) { | 657 void Combobox::ButtonPressed(Button* sender, const ui::Event& event) { |
574 if (!enabled()) | 658 if (!enabled()) |
575 return; | 659 return; |
576 | 660 |
577 RequestFocus(); | 661 RequestFocus(); |
578 | 662 |
579 if (sender == text_button_) { | 663 if (sender == text_button_) { |
580 OnPerformAction(); | 664 OnPerformAction(); |
581 } else { | 665 } else { |
582 DCHECK_EQ(arrow_button_, sender); | 666 DCHECK_EQ(arrow_button_, sender); |
583 // TODO(hajimehoshi): Fix the problem that the arrow button blinks when | 667 // TODO(hajimehoshi): Fix the problem that the arrow button blinks when |
584 // cliking this while the dropdown menu is opened. | 668 // cliking this while the dropdown menu is opened. |
585 const base::TimeDelta delta = base::Time::Now() - closed_time_; | 669 const base::TimeDelta delta = base::Time::Now() - closed_time_; |
586 if (delta.InMilliseconds() <= kMinimumMsBetweenButtonClicks) | 670 if (delta.InMilliseconds() <= kMinimumMsBetweenButtonClicks) |
587 return; | 671 return; |
588 | 672 |
589 ui::MenuSourceType source_type = ui::MENU_SOURCE_MOUSE; | 673 ui::MenuSourceType source_type = ui::MENU_SOURCE_MOUSE; |
590 if (event.IsKeyEvent()) | 674 if (event.IsKeyEvent()) |
591 source_type = ui::MENU_SOURCE_KEYBOARD; | 675 source_type = ui::MENU_SOURCE_KEYBOARD; |
592 else if (event.IsGestureEvent() || event.IsTouchEvent()) | 676 else if (event.IsGestureEvent() || event.IsTouchEvent()) |
593 source_type = ui::MENU_SOURCE_TOUCH; | 677 source_type = ui::MENU_SOURCE_TOUCH; |
594 ShowDropDownMenu(source_type); | 678 ShowDropDownMenu(source_type); |
595 } | 679 } |
596 } | 680 } |
597 | 681 |
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() { | 682 void Combobox::UpdateBorder() { |
637 scoped_ptr<FocusableBorder> border(new FocusableBorder()); | 683 scoped_ptr<FocusableBorder> border(new FocusableBorder()); |
638 if (style_ == STYLE_ACTION) | 684 if (style_ == STYLE_ACTION) |
639 border->SetInsets(5, 10, 5, 10); | 685 border->SetInsets(5, 10, 5, 10); |
640 if (invalid_) | 686 if (invalid_) |
641 border->SetColor(kWarningColor); | 687 border->SetColor(kWarningColor); |
642 SetBorder(border.Pass()); | 688 SetBorder(border.Pass()); |
643 } | 689 } |
644 | 690 |
645 void Combobox::AdjustBoundsForRTLUI(gfx::Rect* rect) const { | 691 void Combobox::AdjustBoundsForRTLUI(gfx::Rect* rect) const { |
(...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
751 canvas->SaveLayerAlpha(arrow_button_hover_alpha); | 797 canvas->SaveLayerAlpha(arrow_button_hover_alpha); |
752 const std::vector<const gfx::ImageSkia*>& arrow_button_hovered_images = | 798 const std::vector<const gfx::ImageSkia*>& arrow_button_hovered_images = |
753 menu_button_images_[focused][Button::STATE_HOVERED]; | 799 menu_button_images_[focused][Button::STATE_HOVERED]; |
754 PaintArrowButton(canvas, arrow_button_hovered_images, | 800 PaintArrowButton(canvas, arrow_button_hovered_images, |
755 arrow_button_->x(), height()); | 801 arrow_button_->x(), height()); |
756 canvas->Restore(); | 802 canvas->Restore(); |
757 } | 803 } |
758 } | 804 } |
759 | 805 |
760 void Combobox::ShowDropDownMenu(ui::MenuSourceType source_type) { | 806 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(); | 807 gfx::Rect lb = GetLocalBounds(); |
770 gfx::Point menu_position(lb.origin()); | 808 gfx::Point menu_position(lb.origin()); |
771 | 809 |
772 if (style_ == STYLE_NORMAL) { | 810 if (style_ == STYLE_NORMAL) { |
773 // Inset the menu's requested position so the border of the menu lines up | 811 // Inset the menu's requested position so the border of the menu lines up |
774 // with the border of the combobox. | 812 // with the border of the combobox. |
775 menu_position.set_x(menu_position.x() + kMenuBorderWidthLeft); | 813 menu_position.set_x(menu_position.x() + kMenuBorderWidthLeft); |
776 menu_position.set_y(menu_position.y() + kMenuBorderWidthTop); | 814 menu_position.set_y(menu_position.y() + kMenuBorderWidthTop); |
777 } | 815 } |
778 lb.set_width(lb.width() - (kMenuBorderWidthLeft + kMenuBorderWidthRight)); | 816 lb.set_width(lb.width() - (kMenuBorderWidthLeft + kMenuBorderWidthRight)); |
779 | 817 |
780 View::ConvertPointToScreen(this, &menu_position); | 818 View::ConvertPointToScreen(this, &menu_position); |
781 if (menu_position.x() < 0) | 819 if (menu_position.x() < 0) |
782 menu_position.set_x(0); | 820 menu_position.set_x(0); |
783 | 821 |
784 gfx::Rect bounds(menu_position, lb.size()); | 822 gfx::Rect bounds(menu_position, lb.size()); |
785 | 823 |
786 Button::ButtonState original_state = Button::STATE_NORMAL; | 824 Button::ButtonState original_state = Button::STATE_NORMAL; |
787 if (arrow_button_) { | 825 if (arrow_button_) { |
788 original_state = arrow_button_->state(); | 826 original_state = arrow_button_->state(); |
789 arrow_button_->SetState(Button::STATE_PRESSED); | 827 arrow_button_->SetState(Button::STATE_PRESSED); |
790 } | 828 } |
791 dropdown_open_ = true; | |
792 MenuAnchorPosition anchor_position = | 829 MenuAnchorPosition anchor_position = |
793 style_ == STYLE_ACTION ? MENU_ANCHOR_TOPRIGHT : MENU_ANCHOR_TOPLEFT; | 830 style_ == STYLE_ACTION ? MENU_ANCHOR_TOPRIGHT : MENU_ANCHOR_TOPLEFT; |
794 if (dropdown_list_menu_runner_->RunMenuAt( | 831 |
795 GetWidget(), NULL, bounds, anchor_position, source_type) == | 832 // Allow |menu_runner_| to be set by the testing API, but if this method is |
796 MenuRunner::MENU_DELETED) { | 833 // ever invoked recursively, ensure the old menu is closed. |
834 if (!menu_runner_ || menu_runner_->IsRunning()) { | |
835 menu_runner_.reset( | |
836 new MenuRunner(menu_model_adapter_.get(), MenuRunner::COMBOBOX)); | |
837 } | |
838 if (menu_runner_->RunMenuAt(GetWidget(), nullptr, bounds, anchor_position, | |
839 source_type) == MenuRunner::MENU_DELETED) { | |
797 return; | 840 return; |
798 } | 841 } |
799 dropdown_open_ = false; | 842 menu_runner_.reset(); |
800 if (arrow_button_) | 843 if (arrow_button_) |
801 arrow_button_->SetState(original_state); | 844 arrow_button_->SetState(original_state); |
802 closed_time_ = base::Time::Now(); | 845 closed_time_ = base::Time::Now(); |
803 | 846 |
804 // Need to explicitly clear mouse handler so that events get sent | 847 // 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 | 848 // 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. | 849 // the first click to other parts of the UI is eaten. |
807 SetMouseHandler(NULL); | 850 SetMouseHandler(NULL); |
808 } | 851 } |
809 | 852 |
810 void Combobox::OnPerformAction() { | 853 void Combobox::OnPerformAction() { |
811 NotifyAccessibilityEvent(ui::AX_EVENT_VALUE_CHANGED, false); | 854 NotifyAccessibilityEvent(ui::AX_EVENT_VALUE_CHANGED, false); |
812 SchedulePaint(); | 855 SchedulePaint(); |
813 | 856 |
814 // This combobox may be deleted by the listener. | 857 // This combobox may be deleted by the listener. |
815 base::WeakPtr<Combobox> weak_ptr = weak_ptr_factory_.GetWeakPtr(); | 858 base::WeakPtr<Combobox> weak_ptr = weak_ptr_factory_.GetWeakPtr(); |
816 if (listener_) | 859 if (listener_) |
817 listener_->OnPerformAction(this); | 860 listener_->OnPerformAction(this); |
818 | 861 |
819 if (weak_ptr && style_ == STYLE_ACTION) | 862 if (weak_ptr && style_ == STYLE_ACTION) |
820 selected_index_ = 0; | 863 selected_index_ = 0; |
821 } | 864 } |
822 | 865 |
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 { | 866 int Combobox::GetDisclosureArrowLeftPadding() const { |
832 switch (style_) { | 867 switch (style_) { |
833 case STYLE_NORMAL: | 868 case STYLE_NORMAL: |
834 return kDisclosureArrowLeftPadding; | 869 return kDisclosureArrowLeftPadding; |
835 case STYLE_ACTION: | 870 case STYLE_ACTION: |
836 return kDisclosureArrowButtonLeftPadding; | 871 return kDisclosureArrowButtonLeftPadding; |
837 } | 872 } |
838 NOTREACHED(); | 873 NOTREACHED(); |
839 return 0; | 874 return 0; |
840 } | 875 } |
(...skipping 20 matching lines...) Expand all Loading... | |
861 #else | 896 #else |
862 const ui::NativeTheme* native_theme_for_arrow = GetNativeTheme(); | 897 const ui::NativeTheme* native_theme_for_arrow = GetNativeTheme(); |
863 #endif | 898 #endif |
864 | 899 |
865 ui::NativeTheme::ExtraParams ignored; | 900 ui::NativeTheme::ExtraParams ignored; |
866 return native_theme_for_arrow->GetPartSize(ui::NativeTheme::kComboboxArrow, | 901 return native_theme_for_arrow->GetPartSize(ui::NativeTheme::kComboboxArrow, |
867 ui::NativeTheme::kNormal, | 902 ui::NativeTheme::kNormal, |
868 ignored); | 903 ignored); |
869 } | 904 } |
870 | 905 |
906 gfx::Size Combobox::GetContentSize() const { | |
907 const gfx::FontList& font_list = GetFontList(); | |
908 | |
909 int width = 0; | |
910 for (int i = 0; i < model()->GetItemCount(); ++i) { | |
911 if (model_->IsItemSeparatorAt(i)) | |
912 continue; | |
913 | |
914 if (style_ != STYLE_ACTION || i == selected_index_) { | |
915 width = std::max( | |
916 width, | |
917 gfx::GetStringWidth(menu_model_adapter_->GetLabelAt(i), font_list)); | |
918 } | |
919 } | |
920 return gfx::Size(width, font_list.GetHeight()); | |
921 } | |
922 | |
871 PrefixSelector* Combobox::GetPrefixSelector() { | 923 PrefixSelector* Combobox::GetPrefixSelector() { |
872 if (!selector_) | 924 if (!selector_) |
873 selector_.reset(new PrefixSelector(this)); | 925 selector_.reset(new PrefixSelector(this)); |
874 return selector_.get(); | 926 return selector_.get(); |
875 } | 927 } |
876 | 928 |
877 } // namespace views | 929 } // namespace views |
OLD | NEW |