| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ui/views/controls/combobox/combobox.h" |
| |
| #include "base/logging.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "ui/base/accessibility/accessible_view_state.h" |
| #include "ui/base/events/event.h" |
| #include "ui/base/keycodes/keyboard_codes.h" |
| #include "ui/base/models/combobox_model.h" |
| #include "ui/base/resource/resource_bundle.h" |
| #include "ui/views/controls/combobox/combobox_listener.h" |
| #include "ui/views/controls/native/native_view_host.h" |
| #include "ui/views/controls/prefix_selector.h" |
| #include "ui/views/ime/input_method.h" |
| #include "ui/views/widget/widget.h" |
| |
| namespace views { |
| |
| // static |
| const char Combobox::kViewClassName[] = "Combobox"; |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // Combobox, public: |
| |
| Combobox::Combobox(ui::ComboboxModel* model) |
| : native_wrapper_(NULL), |
| model_(model), |
| listener_(NULL), |
| selected_index_(model_->GetDefaultIndex()), |
| invalid_(false) { |
| set_focusable(true); |
| } |
| |
| Combobox::~Combobox() { |
| } |
| |
| // static |
| const gfx::Font& Combobox::GetFont() { |
| ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); |
| return rb.GetFont(ui::ResourceBundle::BaseFont); |
| } |
| |
| void Combobox::ModelChanged() { |
| selected_index_ = std::min(0, model_->GetItemCount()); |
| if (native_wrapper_) |
| native_wrapper_->UpdateFromModel(); |
| PreferredSizeChanged(); |
| } |
| |
| void Combobox::SetSelectedIndex(int index) { |
| selected_index_ = index; |
| if (native_wrapper_) |
| native_wrapper_->UpdateSelectedIndex(); |
| } |
| |
| void Combobox::SelectionChanged() { |
| selected_index_ = native_wrapper_->GetSelectedIndex(); |
| if (listener_) |
| listener_->OnSelectedIndexChanged(this); |
| NotifyAccessibilityEvent(ui::AccessibilityTypes::EVENT_VALUE_CHANGED, false); |
| } |
| |
| void Combobox::SetAccessibleName(const string16& name) { |
| accessible_name_ = name; |
| } |
| |
| void Combobox::SetInvalid(bool invalid) { |
| invalid_ = invalid; |
| if (native_wrapper_) |
| native_wrapper_->ValidityStateChanged(); |
| } |
| |
| ui::TextInputClient* Combobox::GetTextInputClient() { |
| if (!selector_) |
| selector_.reset(new PrefixSelector(this)); |
| return selector_.get(); |
| } |
| |
| int Combobox::GetRowCount() { |
| return model()->GetItemCount(); |
| } |
| |
| int Combobox::GetSelectedRow() { |
| return selected_index_; |
| } |
| |
| void Combobox::SetSelectedRow(int row) { |
| SetSelectedIndex(row); |
| } |
| |
| string16 Combobox::GetTextForRow(int row) { |
| return model()->GetItemAt(row); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // Combobox, View overrides: |
| |
| gfx::Size Combobox::GetPreferredSize() { |
| if (native_wrapper_) |
| return native_wrapper_->GetPreferredSize(); |
| return gfx::Size(); |
| } |
| |
| void Combobox::Layout() { |
| if (native_wrapper_) { |
| native_wrapper_->GetView()->SetBounds(0, 0, width(), height()); |
| native_wrapper_->GetView()->Layout(); |
| } |
| } |
| |
| void Combobox::OnEnabledChanged() { |
| View::OnEnabledChanged(); |
| if (native_wrapper_) |
| native_wrapper_->UpdateEnabled(); |
| } |
| |
| // VKEY_ESCAPE should be handled by this view when the drop down list is active. |
| // In other words, the list should be closed instead of the dialog. |
| bool Combobox::SkipDefaultKeyEventProcessing(const ui::KeyEvent& e) { |
| if (e.key_code() != ui::VKEY_ESCAPE || |
| e.IsShiftDown() || e.IsControlDown() || e.IsAltDown()) { |
| return false; |
| } |
| return native_wrapper_ && native_wrapper_->IsDropdownOpen(); |
| } |
| |
| void Combobox::OnPaintFocusBorder(gfx::Canvas* canvas) { |
| if (NativeViewHost::kRenderNativeControlFocus) |
| View::OnPaintFocusBorder(canvas); |
| } |
| |
| bool Combobox::OnKeyPressed(const ui::KeyEvent& e) { |
| return native_wrapper_ && native_wrapper_->HandleKeyPressed(e); |
| } |
| |
| bool Combobox::OnKeyReleased(const ui::KeyEvent& e) { |
| return native_wrapper_ && native_wrapper_->HandleKeyReleased(e); |
| } |
| |
| void Combobox::OnFocus() { |
| GetInputMethod()->OnFocus(); |
| // Forward the focus to the wrapper. |
| if (native_wrapper_) { |
| native_wrapper_->SetFocus(); |
| NotifyAccessibilityEvent(ui::AccessibilityTypes::EVENT_FOCUS, true); |
| } else { |
| View::OnFocus(); // Will focus the RootView window (so we still get |
| // keyboard messages). |
| } |
| } |
| |
| void Combobox::OnBlur() { |
| GetInputMethod()->OnBlur(); |
| if (selector_) |
| selector_->OnViewBlur(); |
| if (native_wrapper_) |
| native_wrapper_->HandleBlur(); |
| } |
| |
| void Combobox::GetAccessibleState(ui::AccessibleViewState* state) { |
| state->role = ui::AccessibilityTypes::ROLE_COMBOBOX; |
| state->name = accessible_name_; |
| state->value = model_->GetItemAt(selected_index_); |
| state->index = selected_index_; |
| state->count = model_->GetItemCount(); |
| } |
| |
| void Combobox::ViewHierarchyChanged( |
| const ViewHierarchyChangedDetails& details) { |
| if (details.is_add && !native_wrapper_ && GetWidget()) { |
| // The native wrapper's lifetime will be managed by the view hierarchy after |
| // we call AddChildView. |
| native_wrapper_ = NativeComboboxWrapper::CreateWrapper(this); |
| AddChildView(native_wrapper_->GetView()); |
| // The underlying native widget may not be created until the wrapper is |
| // parented. For this reason the wrapper is only updated after adding its |
| // view. |
| native_wrapper_->UpdateFromModel(); |
| native_wrapper_->UpdateSelectedIndex(); |
| native_wrapper_->UpdateEnabled(); |
| } |
| } |
| |
| const char* Combobox::GetClassName() const { |
| return kViewClassName; |
| } |
| |
| } // namespace views |