diff options
author | tfarina@chromium.org <tfarina@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-11-24 02:18:05 +0000 |
---|---|---|
committer | tfarina@chromium.org <tfarina@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-11-24 02:18:05 +0000 |
commit | 89539dec77e9c1625d6616c4060b474e0ebd46bd (patch) | |
tree | 29acfb1a6c654d790f7ca181d6669da8a3db0277 /ui | |
parent | bf4d7d1f03aadbe74fcf2d5a555d645e02854ab6 (diff) | |
download | chromium_src-89539dec77e9c1625d6616c4060b474e0ebd46bd.zip chromium_src-89539dec77e9c1625d6616c4060b474e0ebd46bd.tar.gz chromium_src-89539dec77e9c1625d6616c4060b474e0ebd46bd.tar.bz2 |
views: Move combobox directory to ui/views/controls/.
BUG=104039
R=ben@chromium.org
Review URL: http://codereview.chromium.org/8682011
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@111478 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ui')
18 files changed, 1572 insertions, 7 deletions
diff --git a/ui/views/controls/combobox/combobox.cc b/ui/views/controls/combobox/combobox.cc new file mode 100644 index 0000000..24c122b --- /dev/null +++ b/ui/views/controls/combobox/combobox.cc @@ -0,0 +1,143 @@ +// Copyright (c) 2011 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/utf_string_conversions.h" +#include "ui/base/accessibility/accessible_view_state.h" +#include "ui/base/keycodes/keyboard_codes.h" +#include "ui/base/models/combobox_model.h" +#include "ui/views/controls/combobox/combobox_listener.h" +#include "ui/views/widget/widget.h" +#include "views/controls/native/native_view_host.h" + +namespace views { + +// static +const char Combobox::kViewClassName[] = "views/Combobox"; + +//////////////////////////////////////////////////////////////////////////////// +// Combobox, public: + +Combobox::Combobox(ui::ComboboxModel* model) + : native_wrapper_(NULL), + model_(model), + listener_(NULL), + selected_item_(0) { + set_focusable(true); +} + +Combobox::~Combobox() { +} + +void Combobox::ModelChanged() { + selected_item_ = std::min(0, model_->GetItemCount()); + if (native_wrapper_) + native_wrapper_->UpdateFromModel(); + PreferredSizeChanged(); +} + +void Combobox::SetSelectedItem(int index) { + selected_item_ = index; + if (native_wrapper_) + native_wrapper_->UpdateSelectedItem(); +} + +void Combobox::SelectionChanged() { + int prev_selected_item = selected_item_; + selected_item_ = native_wrapper_->GetSelectedItem(); + if (listener_) + listener_->ItemChanged(this, prev_selected_item, selected_item_); + if (GetWidget()) { + GetWidget()->NotifyAccessibilityEvent( + this, ui::AccessibilityTypes::EVENT_VALUE_CHANGED, false); + } +} + +void Combobox::SetAccessibleName(const string16& name) { + accessible_name_ = name; +} + +//////////////////////////////////////////////////////////////////////////////// +// 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 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 views::KeyEvent& e) { + return native_wrapper_ && native_wrapper_->HandleKeyPressed(e); +} + +bool Combobox::OnKeyReleased(const views::KeyEvent& e) { + return native_wrapper_ && native_wrapper_->HandleKeyReleased(e); +} + +void Combobox::OnFocus() { + // Forward the focus to the wrapper. + if (native_wrapper_) + native_wrapper_->SetFocus(); + else + View::OnFocus(); // Will focus the RootView window (so we still get + // keyboard messages). +} + +void Combobox::OnBlur() { + 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_item_); + state->index = selected_item(); + state->count = model()->GetItemCount(); +} + +void Combobox::ViewHierarchyChanged(bool is_add, View* parent, View* child) { + if (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()); + } +} + +std::string Combobox::GetClassName() const { + return kViewClassName; +} + +} // namespace views diff --git a/ui/views/controls/combobox/combobox.h b/ui/views/controls/combobox/combobox.h new file mode 100644 index 0000000..dbd62db --- /dev/null +++ b/ui/views/controls/combobox/combobox.h @@ -0,0 +1,102 @@ +// Copyright (c) 2011 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. + +#ifndef UI_VIEWS_CONTROLS_COMBOBOX_COMBOBOX_H_ +#define UI_VIEWS_CONTROLS_COMBOBOX_COMBOBOX_H_ +#pragma once + +#include <string> + +#include "ui/gfx/native_widget_types.h" +#include "ui/views/controls/combobox/native_combobox_wrapper.h" +#include "views/view.h" + +namespace ui { +class ComboboxModel; +} + +namespace views { + +class ComboboxListener; + +// A non-editable combo-box (aka a drop-down list) +class VIEWS_EXPORT Combobox : public View { + public: + // The combobox's class name. + static const char kViewClassName[]; + + // |model| is not owned by the combo box. + explicit Combobox(ui::ComboboxModel* model); + virtual ~Combobox(); + + // Register |listener| for item change events. + void set_listener(ComboboxListener* listener) { + listener_ = listener; + } + + // Inform the combo box that its model changed. + void ModelChanged(); + + // Gets/Sets the selected item. + int selected_item() const { return selected_item_; } + void SetSelectedItem(int index); + + // Called when the combo box's selection is changed by the user. + void SelectionChanged(); + + // Accessor for |model_|. + ui::ComboboxModel* model() const { return model_; } + + // Set the accessible name of the combo box. + void SetAccessibleName(const string16& name); + + // Provided only for testing: + gfx::NativeView GetTestingHandle() const { + return native_wrapper_ ? native_wrapper_->GetTestingHandle() : NULL; + } + NativeComboboxWrapper* GetNativeWrapperForTesting() const { + return native_wrapper_; + } + + // Overridden from View: + virtual gfx::Size GetPreferredSize() OVERRIDE; + virtual void Layout() OVERRIDE; + virtual void OnEnabledChanged() OVERRIDE; + virtual bool SkipDefaultKeyEventProcessing(const KeyEvent& e) OVERRIDE; + virtual void OnPaintFocusBorder(gfx::Canvas* canvas) OVERRIDE; + virtual bool OnKeyPressed(const views::KeyEvent& e) OVERRIDE; + virtual bool OnKeyReleased(const views::KeyEvent& e) OVERRIDE; + virtual void OnFocus() OVERRIDE; + virtual void OnBlur() OVERRIDE; + virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE; + + protected: + // Overridden from View: + virtual void ViewHierarchyChanged(bool is_add, + View* parent, + View* child) OVERRIDE; + virtual std::string GetClassName() const OVERRIDE; + + // The object that actually implements the native combobox. + NativeComboboxWrapper* native_wrapper_; + + private: + // Our model. + ui::ComboboxModel* model_; + + // The combobox's listener. Notified when the selected item change. + ComboboxListener* listener_; + + // The current selection. + int selected_item_; + + // The accessible name of the text field. + string16 accessible_name_; + + DISALLOW_COPY_AND_ASSIGN(Combobox); +}; + +} // namespace views + +#endif // UI_VIEWS_CONTROLS_COMBOBOX_COMBOBOX_H_ diff --git a/ui/views/controls/combobox/combobox_listener.h b/ui/views/controls/combobox/combobox_listener.h new file mode 100644 index 0000000..cd0f48a --- /dev/null +++ b/ui/views/controls/combobox/combobox_listener.h @@ -0,0 +1,28 @@ +// Copyright (c) 2011 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. + +#ifndef UI_VIEWS_CONTROLS_COMBOBOX_COMBOBOX_LISTENER_H_ +#define UI_VIEWS_CONTROLS_COMBOBOX_COMBOBOX_LISTENER_H_ +#pragma once + +namespace views { + +class Combobox; + +// An interface implemented by an object to let it know that the selected item +// of a combobox changed. +class ComboboxListener { + public: + // This is invoked once the selected item changed. + virtual void ItemChanged(Combobox* combo_box, + int prev_index, + int new_index) = 0; + + protected: + virtual ~ComboboxListener() {} +}; + +} // namespace views + +#endif // UI_VIEWS_CONTROLS_COMBOBOX_COMBOBOX_LISTENER_H_ diff --git a/ui/views/controls/combobox/native_combobox_gtk.cc b/ui/views/controls/combobox/native_combobox_gtk.cc new file mode 100644 index 0000000..a09ad57 --- /dev/null +++ b/ui/views/controls/combobox/native_combobox_gtk.cc @@ -0,0 +1,228 @@ +// Copyright (c) 2011 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/native_combobox_gtk.h" + +#include <gtk/gtk.h> + +#include <algorithm> + +#include "base/message_loop.h" +#include "base/utf_string_conversions.h" +#include "ui/base/models/combobox_model.h" +#include "ui/views/controls/combobox/combobox.h" +#include "ui/views/controls/combobox/native_combobox_views.h" +#include "ui/views/widget/widget.h" +#include "views/views_delegate.h" + +namespace views { + +//////////////////////////////////////////////////////////////////////////////// +// NativeComboboxGtk, public: + +NativeComboboxGtk::NativeComboboxGtk(Combobox* combobox) + : combobox_(combobox), + menu_(NULL) { + set_focus_view(combobox); +} + +NativeComboboxGtk::~NativeComboboxGtk() { +} + +//////////////////////////////////////////////////////////////////////////////// +// NativeComboboxGtk, NativeComboboxWrapper implementation: + +void NativeComboboxGtk::UpdateFromModel() { + if (!native_view()) + return; + + preferred_size_ = gfx::Size(); + + GtkListStore* store = + GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(native_view()))); + ui::ComboboxModel* model = combobox_->model(); + int count = model->GetItemCount(); + gtk_list_store_clear(store); + GtkTreeIter iter; + while (count-- > 0) { + gtk_list_store_prepend(store, &iter); + gtk_list_store_set(store, &iter, + 0, UTF16ToUTF8(model->GetItemAt(count)).c_str(), + -1); + } +} + +void NativeComboboxGtk::UpdateSelectedItem() { + if (!native_view()) + return; + gtk_combo_box_set_active( + GTK_COMBO_BOX(native_view()), combobox_->selected_item()); +} + +void NativeComboboxGtk::UpdateEnabled() { + SetEnabled(combobox_->IsEnabled()); +} + +int NativeComboboxGtk::GetSelectedItem() const { + if (!native_view()) + return 0; + return gtk_combo_box_get_active(GTK_COMBO_BOX(native_view())); +} + +bool NativeComboboxGtk::IsDropdownOpen() const { + if (!native_view()) + return false; + gboolean popup_shown; + g_object_get(G_OBJECT(native_view()), "popup-shown", &popup_shown, NULL); + return popup_shown; +} + +gfx::Size NativeComboboxGtk::GetPreferredSize() { + if (!native_view()) + return gfx::Size(); + + if (preferred_size_.IsEmpty()) { + GtkRequisition size_request = { 0, 0 }; + gtk_widget_size_request(native_view(), &size_request); + // TODO(oshima|scott): we may not need ::max to 29. revisit this. + preferred_size_.SetSize(size_request.width, + std::max(size_request.height, 29)); + } + return preferred_size_; +} + +View* NativeComboboxGtk::GetView() { + return this; +} + +void NativeComboboxGtk::SetFocus() { + OnFocus(); +} + +bool NativeComboboxGtk::HandleKeyPressed(const views::KeyEvent& event) { + return false; +} + +bool NativeComboboxGtk::HandleKeyReleased(const views::KeyEvent& event) { + return false; +} + +void NativeComboboxGtk::HandleFocus() { +} + +void NativeComboboxGtk::HandleBlur() { +} + +gfx::NativeView NativeComboboxGtk::GetTestingHandle() const { + return native_view(); +} + + +//////////////////////////////////////////////////////////////////////////////// +// NativeComboboxGtk, NativeControlGtk overrides: +void NativeComboboxGtk::CreateNativeControl() { + GtkListStore* store = gtk_list_store_new(1, G_TYPE_STRING); + GtkWidget* widget = gtk_combo_box_new_with_model(GTK_TREE_MODEL(store)); + g_object_unref(G_OBJECT(store)); + + GtkCellRenderer* cell = gtk_cell_renderer_text_new(); + gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(widget), cell, TRUE); + gtk_cell_layout_set_attributes( + GTK_CELL_LAYOUT(widget), cell, "text", 0, NULL); + g_signal_connect(widget, "changed", + G_CALLBACK(CallChangedThunk), this); + g_signal_connect_after(widget, "popup", + G_CALLBACK(CallPopUpThunk), this); + + // Get the menu associated with the combo box and listen to events on it. + GList* menu_list = gtk_menu_get_for_attach_widget(widget); + if (menu_list) { + menu_ = reinterpret_cast<GtkMenu*>(menu_list->data); + g_signal_connect_after(menu_, "move-current", + G_CALLBACK(CallMenuMoveCurrentThunk), this); + } + + NativeControlCreated(widget); +} + +void NativeComboboxGtk::NativeControlCreated(GtkWidget* native_control) { + NativeControlGtk::NativeControlCreated(native_control); + // Set the initial state of the combobox. + UpdateFromModel(); + UpdateEnabled(); + UpdateSelectedItem(); +} + +//////////////////////////////////////////////////////////////////////////////// +// NativeComboboxGtk, private: +void NativeComboboxGtk::SelectionChanged() { + combobox_->SelectionChanged(); +} + +void NativeComboboxGtk::FocusedMenuItemChanged() { + DCHECK(menu_); + GtkWidget* menu_item = GTK_MENU_SHELL(menu_)->active_menu_item; + if (!menu_item) + return; + + // Figure out the item index and total number of items. + GList* items = gtk_container_get_children(GTK_CONTAINER(menu_)); + guint count = g_list_length(items); + int index = g_list_index(items, static_cast<gconstpointer>(menu_item)); + + // Get the menu item's label. + std::string name; + GList* children = gtk_container_get_children(GTK_CONTAINER(menu_item)); + for (GList* l = g_list_first(children); l != NULL; l = g_list_next(l)) { + GtkWidget* child = static_cast<GtkWidget*>(l->data); + if (GTK_IS_CELL_VIEW(child)) { + GtkCellView* cell_view = GTK_CELL_VIEW(child); + GtkTreePath* path = gtk_cell_view_get_displayed_row(cell_view); + GtkTreeModel* model = NULL; + model = gtk_cell_view_get_model(cell_view); + GtkTreeIter iter; + if (model && gtk_tree_model_get_iter(model, &iter, path)) { + GValue value = { 0 }; + gtk_tree_model_get_value(model, &iter, 0, &value); + name = g_value_get_string(&value); + break; + } + } + } + + if (ViewsDelegate::views_delegate) { + ViewsDelegate::views_delegate->NotifyMenuItemFocused(string16(), + UTF8ToUTF16(name), + index, + count, + false); + } +} + +void NativeComboboxGtk::CallChanged(GtkWidget* widget) { + SelectionChanged(); +} + +gboolean NativeComboboxGtk::CallPopUp(GtkWidget* widget) { + FocusedMenuItemChanged(); + return false; +} + +void NativeComboboxGtk::CallMenuMoveCurrent( + GtkWidget* menu, GtkMenuDirectionType focus_direction) { + FocusedMenuItemChanged(); +} + +//////////////////////////////////////////////////////////////////////////////// +// NativeComboboxWrapper, public: + +// static +NativeComboboxWrapper* NativeComboboxWrapper::CreateWrapper( + Combobox* combobox) { + if (Widget::IsPureViews()) + return new NativeComboboxViews(combobox); + return new NativeComboboxGtk(combobox); +} + +} // namespace views diff --git a/ui/views/controls/combobox/native_combobox_gtk.h b/ui/views/controls/combobox/native_combobox_gtk.h new file mode 100644 index 0000000..9466dcd --- /dev/null +++ b/ui/views/controls/combobox/native_combobox_gtk.h @@ -0,0 +1,64 @@ +// Copyright (c) 2011 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. + +#ifndef UI_VIEWS_CONTROLS_COMBOBOX_NATIVE_COMBOBOX_GTK_H_ +#define UI_VIEWS_CONTROLS_COMBOBOX_NATIVE_COMBOBOX_GTK_H_ +#pragma once + +#include "ui/base/gtk/gtk_signal.h" +#include "ui/views/controls/combobox/native_combobox_wrapper.h" +#include "views/controls/native_control_gtk.h" + +namespace views { + +class NativeComboboxGtk : public NativeControlGtk, + public NativeComboboxWrapper { + public: + explicit NativeComboboxGtk(Combobox* combobox); + virtual ~NativeComboboxGtk(); + + // Overridden from NativeComboboxWrapper: + virtual void UpdateFromModel() OVERRIDE; + virtual void UpdateSelectedItem() OVERRIDE; + virtual void UpdateEnabled() OVERRIDE; + virtual int GetSelectedItem() const OVERRIDE; + virtual bool IsDropdownOpen() const OVERRIDE; + virtual gfx::Size GetPreferredSize() OVERRIDE; + virtual View* GetView() OVERRIDE; + virtual void SetFocus() OVERRIDE; + virtual bool HandleKeyPressed(const views::KeyEvent& event) OVERRIDE; + virtual bool HandleKeyReleased(const views::KeyEvent& event) OVERRIDE; + virtual void HandleFocus() OVERRIDE; + virtual void HandleBlur() OVERRIDE; + virtual gfx::NativeView GetTestingHandle() const OVERRIDE; + + protected: + // Overridden from NativeControlGtk: + virtual void CreateNativeControl() OVERRIDE; + virtual void NativeControlCreated(GtkWidget* widget) OVERRIDE; + + private: + void SelectionChanged(); + void FocusedMenuItemChanged(); + + CHROMEGTK_CALLBACK_0(NativeComboboxGtk, void, CallChanged); + CHROMEGTK_CALLBACK_0(NativeComboboxGtk, gboolean, CallPopUp); + CHROMEGTK_CALLBACK_1(NativeComboboxGtk, void, CallMenuMoveCurrent, + GtkMenuDirectionType); + + // The combobox we are bound to. + Combobox* combobox_; + + // The combo box's pop-up menu. + GtkMenu* menu_; + + // The preferred size from the last size_request. + gfx::Size preferred_size_; + + DISALLOW_COPY_AND_ASSIGN(NativeComboboxGtk); +}; + +} // namespace views + +#endif // UI_VIEWS_CONTROLS_COMBOBOX_NATIVE_COMBOBOX_GTK_H_ diff --git a/ui/views/controls/combobox/native_combobox_views.cc b/ui/views/controls/combobox/native_combobox_views.cc new file mode 100644 index 0000000..1e8e535 --- /dev/null +++ b/ui/views/controls/combobox/native_combobox_views.cc @@ -0,0 +1,386 @@ +// Copyright (c) 2011 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/native_combobox_views.h" + +#include <algorithm> + +#include "base/command_line.h" +#include "base/utf_string_conversions.h" +#include "ui/base/keycodes/keyboard_codes.h" +#include "ui/base/models/combobox_model.h" +#include "ui/base/resource/resource_bundle.h" +#include "ui/gfx/canvas.h" +#include "ui/gfx/canvas_skia.h" +#include "ui/gfx/font.h" +#include "ui/gfx/path.h" +#include "ui/views/controls/combobox/combobox.h" +#include "ui/views/widget/root_view.h" +#include "ui/views/widget/widget.h" +#include "views/background.h" +#include "views/border.h" +#include "views/controls/focusable_border.h" +#include "views/controls/menu/menu_runner.h" +#include "views/controls/menu/submenu_view.h" + +#if defined(OS_LINUX) +#include "ui/gfx/gtk_util.h" +#endif + +namespace { + +// Define the size of the insets. +const int kTopInsetSize = 4; +const int kLeftInsetSize = 4; +const int kBottomInsetSize = 4; +const int kRightInsetSize = 4; + +// Limit how small a combobox can be. +const int kMinComboboxWidth = 148; + +// Size of the combobox arrow. +const int kComboboxArrowSize = 9; +const int kComboboxArrowOffset = 7; +const int kComboboxArrowMargin = 12; + +// Color settings for text and border. +// These are tentative, and should be derived from theme, system +// settings and current settings. +const SkColor kDefaultBorderColor = SK_ColorGRAY; +const SkColor kTextColor = SK_ColorBLACK; + +// Define the id of the first item in the menu (since it needs to be > 0) +const int kFirstMenuItemId = 1000; + +} // namespace + +namespace views { + +const char NativeComboboxViews::kViewClassName[] = + "views/NativeComboboxViews"; + +NativeComboboxViews::NativeComboboxViews(Combobox* parent) + : combobox_(parent), + text_border_(new FocusableBorder()), + dropdown_open_(false), + selected_item_(-1), + content_width_(0), + content_height_(0) { + set_border(text_border_); +} + +NativeComboboxViews::~NativeComboboxViews() { +} + +//////////////////////////////////////////////////////////////////////////////// +// NativeComboboxViews, View overrides: + +bool NativeComboboxViews::OnMousePressed(const views::MouseEvent& mouse_event) { + combobox_->RequestFocus(); + if (mouse_event.IsLeftMouseButton()) { + UpdateFromModel(); + ShowDropDownMenu(); + } + + return true; +} + +bool NativeComboboxViews::OnMouseDragged(const views::MouseEvent& mouse_event) { + return true; +} + +bool NativeComboboxViews::OnKeyPressed(const views::KeyEvent& key_event) { + // TODO(oshima): handle IME. + DCHECK(key_event.type() == ui::ET_KEY_PRESSED); + + // Check if we are in the default state (-1) and set to first item. + if(selected_item_ == -1) + selected_item_ = 0; + + int new_item = selected_item_; + switch(key_event.key_code()){ + + // move to the next element if any + case ui::VKEY_DOWN: + if (new_item < (combobox_->model()->GetItemCount() - 1)) + new_item++; + break; + + // move to the end of the list + case ui::VKEY_END: + case ui::VKEY_NEXT: + new_item = combobox_->model()->GetItemCount() - 1; + break; + + // move to the top of the list + case ui::VKEY_HOME: + case ui::VKEY_PRIOR: + new_item = 0; + break; + + // move to the previous element if any + case ui::VKEY_UP: + if (new_item > 0) + new_item--; + break; + + default: + return false; + + } + + if(new_item != selected_item_) { + selected_item_ = new_item; + combobox_->SelectionChanged(); + SchedulePaint(); + } + + return true; +} + +bool NativeComboboxViews::OnKeyReleased(const views::KeyEvent& key_event) { + return true; +} + +void NativeComboboxViews::OnPaint(gfx::Canvas* canvas) { + text_border_->set_has_focus(combobox_->HasFocus()); + OnPaintBackground(canvas); + PaintText(canvas); + OnPaintBorder(canvas); +} + +void NativeComboboxViews::OnFocus() { + NOTREACHED(); +} + +void NativeComboboxViews::OnBlur() { + NOTREACHED(); +} + +///////////////////////////////////////////////////////////////// +// NativeComboboxViews, NativeComboboxWrapper overrides: + +void NativeComboboxViews::UpdateFromModel() { + int max_width = 0; + const gfx::Font &font = GetFont(); + + MenuItemView* menu = new MenuItemView(this); + // MenuRunner owns |menu|. + dropdown_list_menu_runner_.reset(new MenuRunner(menu)); + + int num_items = combobox_->model()->GetItemCount(); + for (int i = 0; i < num_items; ++i) { + string16 text = combobox_->model()->GetItemAt(i); + + // Inserting the Unicode formatting characters if necessary so that the + // text is displayed correctly in right-to-left UIs. + base::i18n::AdjustStringForLocaleDirection(&text); + + menu->AppendMenuItem(i + kFirstMenuItemId, text, MenuItemView::NORMAL); + max_width = std::max(max_width, font.GetStringWidth(text)); + } + + content_width_ = max_width; + content_height_ = font.GetFontSize(); +} + +void NativeComboboxViews::UpdateSelectedItem() { + selected_item_ = combobox_->selected_item(); +} + +void NativeComboboxViews::UpdateEnabled() { + SetEnabled(combobox_->IsEnabled()); +} + +int NativeComboboxViews::GetSelectedItem() const { + return selected_item_; +} + +bool NativeComboboxViews::IsDropdownOpen() const { + return dropdown_open_; +} + +gfx::Size NativeComboboxViews::GetPreferredSize() { + if (content_width_ == 0) + UpdateFromModel(); + + // TODO(saintlou) the preferred size will drive the local bounds + // which in turn is used to set the minimum width for the dropdown + gfx::Insets insets = GetInsets(); + return gfx::Size(std::min(kMinComboboxWidth, + content_width_ + 2 * (insets.width())), + content_height_ + 2 * (insets.height())); +} + +View* NativeComboboxViews::GetView() { + return this; +} + +void NativeComboboxViews::SetFocus() { + text_border_->set_has_focus(true); +} + +bool NativeComboboxViews::HandleKeyPressed(const KeyEvent& e) { + return OnKeyPressed(e); +} + +bool NativeComboboxViews::HandleKeyReleased(const KeyEvent& e) { + return true; +} + +void NativeComboboxViews::HandleFocus() { + SchedulePaint(); +} + +void NativeComboboxViews::HandleBlur() { +} + +gfx::NativeView NativeComboboxViews::GetTestingHandle() const { + NOTREACHED(); + return NULL; +} + +///////////////////////////////////////////////////////////////// +// NativeComboboxViews, views::MenuDelegate overrides: +// (note that the id received is offset by kFirstMenuItemId) + +bool NativeComboboxViews::IsItemChecked(int id) const { + return false; +} + +bool NativeComboboxViews::IsCommandEnabled(int id) const { + return true; +} + +void NativeComboboxViews::ExecuteCommand(int id) { + // revert menu offset to map back to combobox model + id -= kFirstMenuItemId; + DCHECK_LT(id, combobox_->model()->GetItemCount()); + selected_item_ = id; + combobox_->SelectionChanged(); + SchedulePaint(); +} + +bool NativeComboboxViews::GetAccelerator(int id, ui::Accelerator* accel) { + return false; +} + +///////////////////////////////////////////////////////////////// +// NativeComboboxViews private methods: + +const gfx::Font& NativeComboboxViews::GetFont() const { + ResourceBundle& rb = ResourceBundle::GetSharedInstance(); + return rb.GetFont(ResourceBundle::BaseFont); +} + +// tip_x and tip_y are the coordinates of the tip of an arrow head which is +// drawn as an isoscele triangle +// shift_x and shift_y are offset from tip_x and tip_y to specify the relative +// positions of the 2 equal angles (ie not the angle at the tip of the arrow) +// Note: the width of the base (the side opposite the tip) is 2 * shift_x +void NativeComboboxViews::DrawArrow(gfx::Canvas* canvas, + int tip_x, + int tip_y, + int shift_x, + int shift_y) const { + SkPaint paint; + paint.setStyle(SkPaint::kStrokeAndFill_Style); + paint.setColor(kTextColor); + paint.setAntiAlias(true); + gfx::Path path; + path.incReserve(4); + path.moveTo(SkIntToScalar(tip_x), SkIntToScalar(tip_y)); + path.lineTo(SkIntToScalar(tip_x + shift_x), SkIntToScalar(tip_y + shift_y)); + path.lineTo(SkIntToScalar(tip_x - shift_x), SkIntToScalar(tip_y + shift_y)); + path.close(); + canvas->GetSkCanvas()->drawPath(path, paint); +} + + +void NativeComboboxViews::PaintText(gfx::Canvas* canvas) { + gfx::Insets insets = GetInsets(); + + canvas->Save(); + canvas->ClipRect(GetContentsBounds()); + + int x = insets.left(); + int y = insets.top(); + int text_height = height() - insets.height(); + SkColor text_color = kTextColor; + + int index = GetSelectedItem(); + if (index < 0 || index > combobox_->model()->GetItemCount()) + index = 0; + string16 text = combobox_->model()->GetItemAt(index); + + const gfx::Font& font = GetFont(); + int width = font.GetStringWidth(text); + + canvas->DrawStringInt(text, font, text_color, x, y, width, text_height); + + // draw the double arrow + gfx::Rect lb = GetLocalBounds(); + DrawArrow(canvas, + lb.width() - (kComboboxArrowSize / 2) - kComboboxArrowOffset, + lb.height() / 2 - kComboboxArrowSize, + kComboboxArrowSize / 2, + kComboboxArrowSize - 2); + DrawArrow(canvas, + lb.width() - (kComboboxArrowSize / 2) - kComboboxArrowOffset, + lb.height() / 2 + kComboboxArrowSize, + -kComboboxArrowSize / 2, + -(kComboboxArrowSize - 2)); + + // draw the margin + canvas->DrawLineInt(kDefaultBorderColor, + lb.width() - kComboboxArrowSize - kComboboxArrowMargin, + kTopInsetSize, + lb.width() - kComboboxArrowSize - kComboboxArrowMargin, + lb.height() - kBottomInsetSize); + + canvas->Restore(); +} + +void NativeComboboxViews::ShowDropDownMenu() { + + if (!dropdown_list_menu_runner_.get()) + UpdateFromModel(); + + // Extend the menu to the width of the combobox. + SubmenuView* submenu = dropdown_list_menu_runner_->GetMenu()->CreateSubmenu(); + submenu->set_minimum_preferred_width(size().width()); + + gfx::Rect lb = GetLocalBounds(); + gfx::Point menu_position(lb.origin()); + View::ConvertPointToScreen(this, &menu_position); + if (menu_position.x() < 0) + menu_position.set_x(0); + + gfx::Rect bounds(menu_position, lb.size()); + + dropdown_open_ = true; + if (dropdown_list_menu_runner_->RunMenuAt( + NULL, NULL, bounds, MenuItemView::TOPLEFT, + MenuRunner::HAS_MNEMONICS) == MenuRunner::MENU_DELETED) + return; + dropdown_open_ = false; + + // Need to explicitly clear mouse handler so that events get sent + // properly after the menu finishes running. If we don't do this, then + // the first click to other parts of the UI is eaten. + SetMouseHandler(NULL); +} + +//////////////////////////////////////////////////////////////////////////////// +// NativeComboboxWrapper, public: + +#if defined(USE_AURA) +// static +NativeComboboxWrapper* NativeComboboxWrapper::CreateWrapper( + Combobox* combobox) { + return new NativeComboboxViews(combobox); +} +#endif + +} // namespace views diff --git a/ui/views/controls/combobox/native_combobox_views.h b/ui/views/controls/combobox/native_combobox_views.h new file mode 100644 index 0000000..571b70b --- /dev/null +++ b/ui/views/controls/combobox/native_combobox_views.h @@ -0,0 +1,104 @@ +// Copyright (c) 2011 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. + +#ifndef UI_VIEWS_CONTROLS_COMBOBOX_NATIVE_COMBOBOX_VIEWS_H_ +#define UI_VIEWS_CONTROLS_COMBOBOX_NATIVE_COMBOBOX_VIEWS_H_ +#pragma once + +#include "ui/views/controls/combobox/native_combobox_wrapper.h" +#include "views/controls/menu/menu_delegate.h" +#include "views/view.h" + +namespace gfx { +class Canvas; +class Font; +} + +namespace views { + +class KeyEvent; +class FocusableBorder; +class MenuRunner; + +// A views/skia only implementation of NativeComboboxWrapper. +// No platform specific code is used. +class NativeComboboxViews : public views::View, + public NativeComboboxWrapper, + public views::MenuDelegate { + public: + explicit NativeComboboxViews(Combobox* parent); + virtual ~NativeComboboxViews(); + + // views::View overrides: + virtual bool OnMousePressed(const views::MouseEvent& mouse_event) OVERRIDE; + virtual bool OnMouseDragged(const views::MouseEvent& mouse_event) OVERRIDE; + virtual bool OnKeyPressed(const views::KeyEvent& key_event) OVERRIDE; + virtual bool OnKeyReleased(const views::KeyEvent& key_event) OVERRIDE; + virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; + virtual void OnFocus() OVERRIDE; + virtual void OnBlur() OVERRIDE; + + // NativeComboboxWrapper overrides: + virtual void UpdateFromModel() OVERRIDE; + virtual void UpdateSelectedItem() OVERRIDE; + virtual void UpdateEnabled() OVERRIDE; + virtual int GetSelectedItem() const OVERRIDE; + virtual bool IsDropdownOpen() const OVERRIDE; + virtual gfx::Size GetPreferredSize() OVERRIDE; + virtual View* GetView() OVERRIDE; + virtual void SetFocus() OVERRIDE; + virtual bool HandleKeyPressed(const views::KeyEvent& event) OVERRIDE; + virtual bool HandleKeyReleased(const views::KeyEvent& event) OVERRIDE; + virtual void HandleFocus() OVERRIDE; + virtual void HandleBlur() OVERRIDE; + virtual gfx::NativeView GetTestingHandle() const OVERRIDE; + + // MenuDelegate overrides: + virtual bool IsItemChecked(int id) const OVERRIDE; + virtual bool IsCommandEnabled(int id) const OVERRIDE; + virtual void ExecuteCommand(int id) OVERRIDE; + virtual bool GetAccelerator(int id, ui::Accelerator* accelerator) OVERRIDE; + + // class name of internal + static const char kViewClassName[]; + + private: + // Returns the Combobox's font. + const gfx::Font& GetFont() const; + + // Draw an arrow + void DrawArrow(gfx::Canvas* canvas, + int tip_x, int tip_y, int shift_x, int shift_y) const; + + // Draw the selected value of the drop down list + void PaintText(gfx::Canvas* canvas); + + // Show the drop down list + void ShowDropDownMenu(); + + // The parent combobox, the owner of this object. + Combobox* combobox_; + + // The reference to the border class. The object is owned by View::border_. + FocusableBorder* text_border_; + + // Responsible for showing the context menu. + scoped_ptr<MenuRunner> dropdown_list_menu_runner_; + + // Is the drop down list showing + bool dropdown_open_; + + // Index in the model of the selected item: -1 => none + int selected_item_; + + // The maximum dimensions of the content in the dropdown + int content_width_; + int content_height_; + + DISALLOW_COPY_AND_ASSIGN(NativeComboboxViews); +}; + +} // namespace views + +#endif // UI_VIEWS_CONTROLS_COMBOBOX_NATIVE_COMBOBOX_VIEWS_H_ diff --git a/ui/views/controls/combobox/native_combobox_views_unittest.cc b/ui/views/controls/combobox/native_combobox_views_unittest.cc new file mode 100644 index 0000000..d11a8d2 --- /dev/null +++ b/ui/views/controls/combobox/native_combobox_views_unittest.cc @@ -0,0 +1,166 @@ +// Copyright (c) 2011 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 "base/utf_string_conversions.h" +#include "ui/base/keycodes/keyboard_codes.h" +#include "ui/base/models/combobox_model.h" +#include "ui/views/controls/combobox/combobox.h" +#include "ui/views/controls/combobox/native_combobox_views.h" +#include "ui/views/ime/mock_input_method.h" +#include "ui/views/test/views_test_base.h" +#include "ui/views/widget/native_widget_private.h" +#include "ui/views/widget/widget.h" + +namespace { + +// A wrapper of Combobox to intercept the result of OnKeyPressed() and +// OnKeyReleased() methods. +class TestCombobox : public views::Combobox { + public: + TestCombobox(ui::ComboboxModel* model) + : Combobox(model), + key_handled_(false), + key_received_(false) { + } + + virtual bool OnKeyPressed(const views::KeyEvent& e) OVERRIDE { + key_received_ = true; + key_handled_ = views::Combobox::OnKeyPressed(e); + return key_handled_; + } + + virtual bool OnKeyReleased(const views::KeyEvent& e) OVERRIDE { + key_received_ = true; + key_handled_ = views::Combobox::OnKeyReleased(e); + return key_handled_; + } + + bool key_handled() const { return key_handled_; } + bool key_received() const { return key_received_; } + + void clear() { + key_received_ = key_handled_ = false; + } + + private: + bool key_handled_; + bool key_received_; + + DISALLOW_COPY_AND_ASSIGN(TestCombobox); +}; + +// A concrete class is needed to test the combobox +class TestComboboxModel : public ui::ComboboxModel { + public: + TestComboboxModel() {} + virtual ~TestComboboxModel() {} + virtual int GetItemCount() { + return 4; + } + virtual string16 GetItemAt(int index) { + EXPECT_GE(index, 0); + EXPECT_LT(index, GetItemCount()); + return string16(); + } + private: + DISALLOW_COPY_AND_ASSIGN(TestComboboxModel); +}; + +} // namespace + +namespace views { + +class NativeComboboxViewsTest : public ViewsTestBase { + public: + NativeComboboxViewsTest() + : widget_(NULL), + combobox_(NULL), + combobox_view_(NULL), + model_(NULL), + input_method_(NULL) { + } + + // ::testing::Test: + virtual void SetUp() { + ViewsTestBase::SetUp(); + Widget::SetPureViews(true); + } + + virtual void TearDown() { + Widget::SetPureViews(false); + if (widget_) + widget_->Close(); + ViewsTestBase::TearDown(); + } + + void InitCombobox() { + model_.reset(new TestComboboxModel()); + + ASSERT_FALSE(combobox_); + combobox_ = new TestCombobox(model_.get()); + combobox_->set_id(1); + + widget_ = new Widget; + Widget::InitParams params(Widget::InitParams::TYPE_POPUP); + params.bounds = gfx::Rect(100, 100, 100, 100); + widget_->Init(params); + View* container = new View(); + widget_->SetContentsView(container); + container->AddChildView(combobox_); + + combobox_view_ = static_cast<NativeComboboxViews*>( + combobox_->GetNativeWrapperForTesting()); + ASSERT_TRUE(combobox_view_); + + input_method_ = new MockInputMethod(); + widget_->ReplaceInputMethod(input_method_); + + // Assumes the Widget is always focused. + input_method_->OnFocus(); + + combobox_->RequestFocus(); + } + + protected: + void SendKeyEvent(ui::KeyboardCode key_code) { + KeyEvent event(ui::ET_KEY_PRESSED, key_code, 0); + input_method_->DispatchKeyEvent(event); + } + + View* GetFocusedView() { + return widget_->GetFocusManager()->GetFocusedView(); + } + + // We need widget to populate wrapper class. + Widget* widget_; + + // combobox_ will be allocated InitCombobox() and then owned by widget_. + TestCombobox* combobox_; + + // combobox_view_ is the pointer to the pure Views interface of combobox_. + NativeComboboxViews* combobox_view_; + + // Combobox does not take ownership of model_, which needs to be scoped. + scoped_ptr<ui::ComboboxModel> model_; + + // For testing input method related behaviors. + MockInputMethod* input_method_; +}; + +TEST_F(NativeComboboxViewsTest, KeyTest) { + InitCombobox(); + SendKeyEvent(ui::VKEY_END); + EXPECT_EQ(combobox_->selected_item() + 1, model_->GetItemCount()); + SendKeyEvent(ui::VKEY_HOME); + EXPECT_EQ(combobox_->selected_item(), 0); + SendKeyEvent(ui::VKEY_DOWN); + SendKeyEvent(ui::VKEY_DOWN); + EXPECT_EQ(combobox_->selected_item(), 2); + SendKeyEvent(ui::VKEY_RIGHT); + EXPECT_EQ(combobox_->selected_item(), 2); + SendKeyEvent(ui::VKEY_LEFT); + EXPECT_EQ(combobox_->selected_item(), 2); +} + +} // namespace views diff --git a/ui/views/controls/combobox/native_combobox_win.cc b/ui/views/controls/combobox/native_combobox_win.cc new file mode 100644 index 0000000..97cd440 --- /dev/null +++ b/ui/views/controls/combobox/native_combobox_win.cc @@ -0,0 +1,215 @@ +// Copyright (c) 2011 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/native_combobox_win.h" + +#include "base/i18n/rtl.h" +#include "base/utf_string_conversions.h" +#include "ui/base/models/combobox_model.h" +#include "ui/base/resource/resource_bundle.h" +#include "ui/base/win/hwnd_util.h" +#include "ui/gfx/font.h" +#include "ui/gfx/native_theme_win.h" +#include "ui/views/controls/combobox/combobox.h" +#include "ui/views/controls/combobox/native_combobox_views.h" +#include "ui/views/widget/widget.h" + +namespace views { + +// Limit how small a combobox can be. +static const int kMinComboboxWidth = 148; + +// Add a couple extra pixels to the widths of comboboxes and combobox +// dropdowns so that text isn't too crowded. +static const int kComboboxExtraPaddingX = 6; + +//////////////////////////////////////////////////////////////////////////////// +// NativeComboboxWin, public: + +NativeComboboxWin::NativeComboboxWin(Combobox* combobox) + : combobox_(combobox), + content_width_(0) { + // Associates the actual HWND with the combobox so it is the one considered as + // having the focus (not the wrapper) when the HWND is focused directly (with + // a click for example). + set_focus_view(combobox); +} + +NativeComboboxWin::~NativeComboboxWin() { +} + +//////////////////////////////////////////////////////////////////////////////// +// NativeComboboxWin, NativeComboboxWrapper implementation: + +void NativeComboboxWin::UpdateFromModel() { + SendMessage(native_view(), CB_RESETCONTENT, 0, 0); + gfx::Font font = ResourceBundle::GetSharedInstance().GetFont( + ResourceBundle::BaseFont); + int max_width = 0; + int num_items = combobox_->model()->GetItemCount(); + for (int i = 0; i < num_items; ++i) { + string16 text = combobox_->model()->GetItemAt(i); + + // Inserting the Unicode formatting characters if necessary so that the + // text is displayed correctly in right-to-left UIs. + base::i18n::AdjustStringForLocaleDirection(&text); + + SendMessage(native_view(), CB_ADDSTRING, 0, + reinterpret_cast<LPARAM>(UTF16ToWide(text).c_str())); + max_width = std::max(max_width, font.GetStringWidth(text)); + } + content_width_ = max_width; + + if (num_items > 0) { + SendMessage(native_view(), CB_SETCURSEL, combobox_->selected_item(), 0); + + // Set the width for the drop down while accounting for the scrollbar and + // borders. + if (num_items > ComboBox_GetMinVisible(native_view())) + max_width += GetSystemMetrics(SM_CXVSCROLL); + // SM_CXEDGE would not be correct here, since the dropdown is flat, not 3D. + int kComboboxDropdownBorderSize = 1; + max_width += 2 * kComboboxDropdownBorderSize + kComboboxExtraPaddingX; + SendMessage(native_view(), CB_SETDROPPEDWIDTH, max_width, 0); + } +} + +void NativeComboboxWin::UpdateSelectedItem() { + // Note that we use CB_SETCURSEL and not CB_SELECTSTRING because on RTL + // locales the strings we get from our ComboBox::Model might be augmented + // with Unicode directionality marks before we insert them into the combo box + // and therefore we can not assume that the string we get from + // ComboBox::Model can be safely searched for and selected (which is what + // CB_SELECTSTRING does). + SendMessage(native_view(), CB_SETCURSEL, combobox_->selected_item(), 0); +} + +void NativeComboboxWin::UpdateEnabled() { + SetEnabled(combobox_->IsEnabled()); +} + +int NativeComboboxWin::GetSelectedItem() const { + LRESULT selected_item = SendMessage(native_view(), CB_GETCURSEL, 0, 0); + return selected_item != CB_ERR ? selected_item : -1; +} + +bool NativeComboboxWin::IsDropdownOpen() const { + return SendMessage(native_view(), CB_GETDROPPEDSTATE, 0, 0) != 0; +} + +gfx::Size NativeComboboxWin::GetPreferredSize() { + COMBOBOXINFO cbi = { 0 }; + cbi.cbSize = sizeof(cbi); + // Note: Don't use CB_GETCOMBOBOXINFO since that crashes on WOW64 systems + // when you have a global message hook installed. + GetComboBoxInfo(native_view(), &cbi); + gfx::Rect rect_item(cbi.rcItem); + gfx::Rect rect_button(cbi.rcButton); + gfx::Size border = gfx::NativeThemeWin::instance()->GetThemeBorderSize( + gfx::NativeThemeWin::MENULIST); + + // The padding value of '3' is the xy offset from the corner of the control + // to the corner of rcItem. It does not seem to be queryable from the theme. + // It is consistent on all versions of Windows from 2K to Vista, and is + // invariant with respect to the combobox border size. We could conceivably + // get this number from rect_item.x, but it seems fragile to depend on + // position here, inside of the layout code. + const int kItemOffset = 3; + int item_to_button_distance = std::max(kItemOffset - border.width(), 0); + + // The cx computation can be read as measuring from left to right. + int pref_width = std::max(kItemOffset + content_width_ + + kComboboxExtraPaddingX + + item_to_button_distance + rect_button.width() + + border.width(), kMinComboboxWidth); + // The two arguments to ::max below should be typically be equal. + int pref_height = std::max(rect_item.height() + 2 * kItemOffset, + rect_button.height() + 2 * border.height()); + return gfx::Size(pref_width, pref_height); +} + +View* NativeComboboxWin::GetView() { + return this; +} + +void NativeComboboxWin::SetFocus() { + OnFocus(); +} + +bool NativeComboboxWin::HandleKeyPressed(const views::KeyEvent& event) { + return false; +} + +bool NativeComboboxWin::HandleKeyReleased(const views::KeyEvent& event) { + return false; +} + +void NativeComboboxWin::HandleFocus() { +} + +void NativeComboboxWin::HandleBlur() { +} + +gfx::NativeView NativeComboboxWin::GetTestingHandle() const { + return native_view(); +} + +//////////////////////////////////////////////////////////////////////////////// +// NativeComboboxWin, NativeControlWin overrides: + +bool NativeComboboxWin::ProcessMessage(UINT message, + WPARAM w_param, + LPARAM l_param, + LRESULT* result) { + if (message == WM_COMMAND && HIWORD(w_param) == CBN_SELCHANGE) { + combobox_->SelectionChanged(); + *result = 0; + return true; + } + return NativeControlWin::ProcessMessage(message, w_param, l_param, result); +} + +void NativeComboboxWin::CreateNativeControl() { + // It's ok to add WS_VSCROLL. The scrollbar will show up only when necessary + // as long as we don't use CBS_DISABLENOSCROLL. + // See http://msdn.microsoft.com/en-us/library/7h63bxbe(VS.80).aspx + DWORD flags = WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | + CBS_DROPDOWNLIST | WS_VSCROLL; + HWND control_hwnd = ::CreateWindowEx(GetAdditionalExStyle(), L"COMBOBOX", L"", + flags, 0, 0, width(), height(), + GetWidget()->GetNativeView(), NULL, NULL, + NULL); + ui::CheckWindowCreated(control_hwnd); + NativeControlCreated(control_hwnd); +} + +void NativeComboboxWin::NativeControlCreated(HWND native_control) { + NativeControlWin::NativeControlCreated(native_control); + + UpdateFont(); + UpdateFromModel(); + UpdateSelectedItem(); +} + +//////////////////////////////////////////////////////////////////////////////// +// NativeComboboxWin, private: + +void NativeComboboxWin::UpdateFont() { + HFONT font = ResourceBundle::GetSharedInstance(). + GetFont(ResourceBundle::BaseFont).GetNativeFont(); + SendMessage(native_view(), WM_SETFONT, reinterpret_cast<WPARAM>(font), FALSE); +} + +//////////////////////////////////////////////////////////////////////////////// +// NativeComboboxWrapper, public: + +// static +NativeComboboxWrapper* NativeComboboxWrapper::CreateWrapper( + Combobox* combobox) { + if (Widget::IsPureViews()) + return new NativeComboboxViews(combobox); + return new NativeComboboxWin(combobox); +} + +} // namespace views diff --git a/ui/views/controls/combobox/native_combobox_win.h b/ui/views/controls/combobox/native_combobox_win.h new file mode 100644 index 0000000..ee22cee --- /dev/null +++ b/ui/views/controls/combobox/native_combobox_win.h @@ -0,0 +1,58 @@ +// Copyright (c) 2011 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. + +#ifndef UI_VIEWS_CONTROLS_COMBOBOX_NATIVE_COMBOBOX_WIN_H_ +#define UI_VIEWS_CONTROLS_COMBOBOX_NATIVE_COMBOBOX_WIN_H_ +#pragma once + +#include "ui/views/controls/combobox/native_combobox_wrapper.h" +#include "views/controls/native_control_win.h" + +namespace views { + +class NativeComboboxWin : public NativeControlWin, + public NativeComboboxWrapper { + public: + explicit NativeComboboxWin(Combobox* combobox); + virtual ~NativeComboboxWin(); + + // Overridden from NativeComboboxWrapper: + virtual void UpdateFromModel() OVERRIDE; + virtual void UpdateSelectedItem() OVERRIDE; + virtual void UpdateEnabled() OVERRIDE; + virtual int GetSelectedItem() const OVERRIDE; + virtual bool IsDropdownOpen() const OVERRIDE; + virtual gfx::Size GetPreferredSize() OVERRIDE; + virtual View* GetView() OVERRIDE; + virtual void SetFocus() OVERRIDE; + virtual bool HandleKeyPressed(const views::KeyEvent& event) OVERRIDE; + virtual bool HandleKeyReleased(const views::KeyEvent& event) OVERRIDE; + virtual void HandleFocus() OVERRIDE; + virtual void HandleBlur() OVERRIDE; + virtual gfx::NativeView GetTestingHandle() const OVERRIDE; + + protected: + // Overridden from NativeControlWin: + virtual bool ProcessMessage(UINT message, + WPARAM w_param, + LPARAM l_param, + LRESULT* result) OVERRIDE; + virtual void CreateNativeControl() OVERRIDE; + virtual void NativeControlCreated(HWND native_control) OVERRIDE; + + private: + void UpdateFont(); + + // The combobox we are bound to. + Combobox* combobox_; + + // The min width, in pixels, for the text content. + int content_width_; + + DISALLOW_COPY_AND_ASSIGN(NativeComboboxWin); +}; + +} // namespace views + +#endif // UI_VIEWS_CONTROLS_COMBOBOX_NATIVE_COMBOBOX_WIN_H_ diff --git a/ui/views/controls/combobox/native_combobox_wrapper.h b/ui/views/controls/combobox/native_combobox_wrapper.h new file mode 100644 index 0000000..959871e --- /dev/null +++ b/ui/views/controls/combobox/native_combobox_wrapper.h @@ -0,0 +1,71 @@ +// Copyright (c) 2011 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. + +#ifndef UI_VIEWS_CONTROLS_COMBOBOX_NATIVE_COMBOBOX_WRAPPER_H_ +#define UI_VIEWS_CONTROLS_COMBOBOX_NATIVE_COMBOBOX_WRAPPER_H_ +#pragma once + +#include "ui/gfx/native_widget_types.h" +#include "views/views_export.h" + +namespace gfx{ +class Size; +} + +namespace views { + +class Combobox; +class KeyEvent; +class View; + +class VIEWS_EXPORT NativeComboboxWrapper { + public: + // Updates the combobox's content from its model. + virtual void UpdateFromModel() = 0; + + // Updates the displayed selected item from the associated Combobox. + virtual void UpdateSelectedItem() = 0; + + // Updates the enabled state of the combobox from the associated view. + virtual void UpdateEnabled() = 0; + + // Gets the selected index. + virtual int GetSelectedItem() const = 0; + + // Returns true if the Combobox dropdown is open. + virtual bool IsDropdownOpen() const = 0; + + // Returns the preferred size of the combobox. + virtual gfx::Size GetPreferredSize() = 0; + + // Retrieves the views::View that hosts the native control. + virtual View* GetView() = 0; + + // Sets the focus to the button. + virtual void SetFocus() = 0; + + // Invoked when a key is pressed/release on Combobox. Subclasser + // should return true if the event has been processed and false + // otherwise. + // See also View::OnKeyPressed/OnKeyReleased. + virtual bool HandleKeyPressed(const views::KeyEvent& e) = 0; + virtual bool HandleKeyReleased(const views::KeyEvent& e) = 0; + + // Invoked when focus is being moved from or to the Combobox. + // See also View::OnFocus/OnBlur. + virtual void HandleFocus() = 0; + virtual void HandleBlur() = 0; + + // Returns a handle to the underlying native view for testing. + virtual gfx::NativeView GetTestingHandle() const = 0; + + static NativeComboboxWrapper* CreateWrapper(Combobox* combobox); + + protected: + virtual ~NativeComboboxWrapper() {} +}; + +} // namespace views + +#endif // UI_VIEWS_CONTROLS_COMBOBOX_NATIVE_COMBOBOX_WRAPPER_H_ diff --git a/ui/views/examples/combobox_example.cc b/ui/views/examples/combobox_example.cc index e035743..b271ebe 100644 --- a/ui/views/examples/combobox_example.cc +++ b/ui/views/examples/combobox_example.cc @@ -7,8 +7,8 @@ #include "base/stringprintf.h" #include "base/utf_string_conversions.h" #include "ui/base/models/combobox_model.h" +#include "ui/views/controls/combobox/combobox.h" #include "ui/views/layout/fill_layout.h" -#include "views/controls/combobox/combobox.h" namespace { diff --git a/ui/views/examples/combobox_example.h b/ui/views/examples/combobox_example.h index 56aba1c..7b3d15a 100644 --- a/ui/views/examples/combobox_example.h +++ b/ui/views/examples/combobox_example.h @@ -8,8 +8,8 @@ #include "base/basictypes.h" #include "base/compiler_specific.h" +#include "ui/views/controls/combobox/combobox_listener.h" #include "ui/views/examples/example_base.h" -#include "views/controls/combobox/combobox_listener.h" namespace examples { diff --git a/ui/views/examples/native_theme_button_example.cc b/ui/views/examples/native_theme_button_example.cc index cdd3f2a..ded8483 100644 --- a/ui/views/examples/native_theme_button_example.cc +++ b/ui/views/examples/native_theme_button_example.cc @@ -12,9 +12,9 @@ #include "ui/base/animation/throb_animation.h" #include "ui/base/models/combobox_model.h" #include "ui/gfx/canvas.h" +#include "ui/views/controls/combobox/combobox.h" #include "ui/views/examples/example_combobox_model.h" #include "ui/views/layout/grid_layout.h" -#include "views/controls/combobox/combobox.h" #include "views/controls/label.h" #include "views/native_theme_painter.h" diff --git a/ui/views/examples/native_theme_button_example.h b/ui/views/examples/native_theme_button_example.h index 8b59fec..7a116f1 100644 --- a/ui/views/examples/native_theme_button_example.h +++ b/ui/views/examples/native_theme_button_example.h @@ -8,9 +8,9 @@ #include "base/basictypes.h" #include "ui/gfx/native_theme.h" +#include "ui/views/controls/combobox/combobox_listener.h" #include "ui/views/examples/example_base.h" #include "views/controls/button/custom_button.h" -#include "views/controls/combobox/combobox_listener.h" #include "views/native_theme_delegate.h" #include "views/native_theme_painter.h" diff --git a/ui/views/examples/text_example.cc b/ui/views/examples/text_example.cc index 415337a..cac7e1f 100644 --- a/ui/views/examples/text_example.cc +++ b/ui/views/examples/text_example.cc @@ -8,10 +8,10 @@ #include "ui/base/resource/resource_bundle.h" #include "ui/gfx/canvas.h" #include "ui/gfx/canvas_skia.h" +#include "ui/views/controls/combobox/combobox.h" #include "ui/views/examples/example_combobox_model.h" #include "ui/views/layout/grid_layout.h" #include "views/controls/button/checkbox.h" -#include "views/controls/combobox/combobox.h" #include "views/controls/label.h" #include "views/view.h" diff --git a/ui/views/examples/text_example.h b/ui/views/examples/text_example.h index e99fb1a..e9e096f 100644 --- a/ui/views/examples/text_example.h +++ b/ui/views/examples/text_example.h @@ -8,9 +8,9 @@ #include "base/basictypes.h" #include "base/compiler_specific.h" +#include "ui/views/controls/combobox/combobox_listener.h" #include "ui/views/examples/example_base.h" #include "views/controls/button/button.h" -#include "views/controls/combobox/combobox_listener.h" namespace views { class Checkbox; diff --git a/ui/views/focus/focus_traversal_unittest.cc b/ui/views/focus/focus_traversal_unittest.cc index 87bfc26..e886a95 100644 --- a/ui/views/focus/focus_traversal_unittest.cc +++ b/ui/views/focus/focus_traversal_unittest.cc @@ -7,13 +7,13 @@ #include "base/string_number_conversions.h" #include "base/utf_string_conversions.h" #include "ui/base/models/combobox_model.h" +#include "ui/views/controls/combobox/combobox.h" #include "ui/views/focus/focus_manager_test.h" #include "ui/views/widget/root_view.h" #include "ui/views/widget/widget.h" #include "views/controls/button/checkbox.h" #include "views/controls/button/radio_button.h" #include "views/controls/button/text_button.h" -#include "views/controls/combobox/combobox.h" #include "views/controls/label.h" #include "views/controls/link.h" #include "views/controls/native/native_view_host.h" |