summaryrefslogtreecommitdiffstats
path: root/ui
diff options
context:
space:
mode:
authortfarina@chromium.org <tfarina@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-11-24 02:18:05 +0000
committertfarina@chromium.org <tfarina@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-11-24 02:18:05 +0000
commit89539dec77e9c1625d6616c4060b474e0ebd46bd (patch)
tree29acfb1a6c654d790f7ca181d6669da8a3db0277 /ui
parentbf4d7d1f03aadbe74fcf2d5a555d645e02854ab6 (diff)
downloadchromium_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')
-rw-r--r--ui/views/controls/combobox/combobox.cc143
-rw-r--r--ui/views/controls/combobox/combobox.h102
-rw-r--r--ui/views/controls/combobox/combobox_listener.h28
-rw-r--r--ui/views/controls/combobox/native_combobox_gtk.cc228
-rw-r--r--ui/views/controls/combobox/native_combobox_gtk.h64
-rw-r--r--ui/views/controls/combobox/native_combobox_views.cc386
-rw-r--r--ui/views/controls/combobox/native_combobox_views.h104
-rw-r--r--ui/views/controls/combobox/native_combobox_views_unittest.cc166
-rw-r--r--ui/views/controls/combobox/native_combobox_win.cc215
-rw-r--r--ui/views/controls/combobox/native_combobox_win.h58
-rw-r--r--ui/views/controls/combobox/native_combobox_wrapper.h71
-rw-r--r--ui/views/examples/combobox_example.cc2
-rw-r--r--ui/views/examples/combobox_example.h2
-rw-r--r--ui/views/examples/native_theme_button_example.cc2
-rw-r--r--ui/views/examples/native_theme_button_example.h2
-rw-r--r--ui/views/examples/text_example.cc2
-rw-r--r--ui/views/examples/text_example.h2
-rw-r--r--ui/views/focus/focus_traversal_unittest.cc2
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"