Chromium Code Reviews| 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 // 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 |
| OLD | NEW |