summaryrefslogtreecommitdiffstats
path: root/ui/views/controls/combobox
diff options
context:
space:
mode:
Diffstat (limited to 'ui/views/controls/combobox')
-rw-r--r--ui/views/controls/combobox/combobox.cc392
-rw-r--r--ui/views/controls/combobox/combobox.h97
-rw-r--r--ui/views/controls/combobox/combobox_unittest.cc (renamed from ui/views/controls/combobox/native_combobox_views_unittest.cc)49
-rw-r--r--ui/views/controls/combobox/native_combobox_views.cc448
-rw-r--r--ui/views/controls/combobox/native_combobox_views.h115
-rw-r--r--ui/views/controls/combobox/native_combobox_win.cc218
-rw-r--r--ui/views/controls/combobox/native_combobox_win.h58
-rw-r--r--ui/views/controls/combobox/native_combobox_wrapper.h76
8 files changed, 402 insertions, 1051 deletions
diff --git a/ui/views/controls/combobox/combobox.cc b/ui/views/controls/combobox/combobox.cc
index 34f4848..86aef66 100644
--- a/ui/views/controls/combobox/combobox.cc
+++ b/ui/views/controls/combobox/combobox.cc
@@ -6,32 +6,101 @@
#include "base/logging.h"
#include "base/strings/utf_string_conversions.h"
+#include "grit/ui_resources.h"
#include "ui/base/accessibility/accessible_view_state.h"
#include "ui/base/events/event.h"
#include "ui/base/keycodes/keyboard_codes.h"
#include "ui/base/models/combobox_model.h"
#include "ui/base/resource/resource_bundle.h"
+#include "ui/gfx/canvas.h"
+#include "ui/native_theme/native_theme.h"
+#include "ui/views/color_constants.h"
#include "ui/views/controls/combobox/combobox_listener.h"
-#include "ui/views/controls/native/native_view_host.h"
+#include "ui/views/controls/focusable_border.h"
+#include "ui/views/controls/menu/menu_runner.h"
+#include "ui/views/controls/menu/submenu_view.h"
#include "ui/views/controls/prefix_selector.h"
#include "ui/views/ime/input_method.h"
+#include "ui/views/mouse_constants.h"
#include "ui/views/widget/widget.h"
namespace views {
+namespace {
+
+// Menu border widths
+const int kMenuBorderWidthLeft = 1;
+const int kMenuBorderWidthTop = 1;
+const int kMenuBorderWidthRight = 1;
+const int kMenuBorderWidthBottom = 2;
+
+// Limit how small a combobox can be.
+const int kMinComboboxWidth = 25;
+
+// Size of the combobox arrow margins
+const int kDisclosureArrowLeftPadding = 7;
+const int kDisclosureArrowRightPadding = 7;
+
+// Define the id of the first item in the menu (since it needs to be > 0)
+const int kFirstMenuItemId = 1000;
+
+const SkColor kInvalidTextColor = SK_ColorWHITE;
+
+// Used to indicate that no item is currently selected by the user.
+const int kNoSelection = -1;
+
+// The background to use for invalid comboboxes.
+class InvalidBackground : public Background {
+ public:
+ InvalidBackground() {}
+ virtual ~InvalidBackground() {}
+
+ // Overridden from Background:
+ virtual void Paint(gfx::Canvas* canvas, View* view) const OVERRIDE {
+ gfx::Rect bounds(view->GetLocalBounds());
+ // Inset by 2 to leave 1 empty pixel between background and border.
+ bounds.Inset(2, 2, 2, 2);
+ canvas->FillRect(bounds, kWarningColor);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(InvalidBackground);
+};
+
+// Returns the next or previous valid index (depending on |increment|'s value).
+// Skips separator indices. Returns -1 if there is no valid adjacent index.
+int GetAdjacentIndex(ui::ComboboxModel* model, int increment, int index) {
+ DCHECK(increment == -1 || increment == 1);
+
+ index += increment;
+ while (index >= 0 && index < model->GetItemCount()) {
+ if (!model->IsItemSeparatorAt(index))
+ return index;
+ index += increment;
+ }
+ return kNoSelection;
+}
+
+} // namespace
+
// static
-const char Combobox::kViewClassName[] = "Combobox";
+const char Combobox::kViewClassName[] = "views/Combobox";
////////////////////////////////////////////////////////////////////////////////
// Combobox, public:
Combobox::Combobox(ui::ComboboxModel* model)
- : native_wrapper_(NULL),
- model_(model),
+ : model_(model),
listener_(NULL),
selected_index_(model_->GetDefaultIndex()),
- invalid_(false) {
+ invalid_(false),
+ text_border_(new FocusableBorder()),
+ disclosure_arrow_(ui::ResourceBundle::GetSharedInstance().GetImageNamed(
+ IDR_MENU_DROPARROW).ToImageSkia()),
+ dropdown_open_(false) {
+ UpdateFromModel();
set_focusable(true);
+ set_border(text_border_);
}
Combobox::~Combobox() {
@@ -45,22 +114,13 @@ const gfx::Font& Combobox::GetFont() {
void Combobox::ModelChanged() {
selected_index_ = std::min(0, model_->GetItemCount());
- if (native_wrapper_)
- native_wrapper_->UpdateFromModel();
+ UpdateFromModel();
PreferredSizeChanged();
}
void Combobox::SetSelectedIndex(int index) {
selected_index_ = index;
- if (native_wrapper_)
- native_wrapper_->UpdateSelectedIndex();
-}
-
-void Combobox::SelectionChanged() {
- selected_index_ = native_wrapper_->GetSelectedIndex();
- if (listener_)
- listener_->OnSelectedIndexChanged(this);
- NotifyAccessibilityEvent(ui::AccessibilityTypes::EVENT_VALUE_CHANGED, false);
+ SchedulePaint();
}
void Combobox::SetAccessibleName(const string16& name) {
@@ -69,8 +129,13 @@ void Combobox::SetAccessibleName(const string16& name) {
void Combobox::SetInvalid(bool invalid) {
invalid_ = invalid;
- if (native_wrapper_)
- native_wrapper_->ValidityStateChanged();
+ if (invalid) {
+ text_border_->SetColor(kWarningColor);
+ set_background(new InvalidBackground());
+ } else {
+ text_border_->UseDefaultColor();
+ set_background(NULL);
+ }
}
ui::TextInputClient* Combobox::GetTextInputClient() {
@@ -79,6 +144,28 @@ ui::TextInputClient* Combobox::GetTextInputClient() {
return selector_.get();
}
+
+bool Combobox::IsItemChecked(int id) const {
+ return false;
+}
+
+bool Combobox::IsCommandEnabled(int id) const {
+ return true;
+}
+
+void Combobox::ExecuteCommand(int id) {
+ // (note that the id received is offset by kFirstMenuItemId)
+ // Revert menu ID offset to map back to combobox model.
+ id -= kFirstMenuItemId;
+ DCHECK_LT(id, model()->GetItemCount());
+ selected_index_ = id;
+ OnSelectionChanged();
+}
+
+bool Combobox::GetAccelerator(int id, ui::Accelerator* accel) {
+ return false;
+}
+
int Combobox::GetRowCount() {
return model()->GetItemCount();
}
@@ -99,65 +186,137 @@ string16 Combobox::GetTextForRow(int row) {
// Combobox, View overrides:
gfx::Size Combobox::GetPreferredSize() {
- if (native_wrapper_)
- return native_wrapper_->GetPreferredSize();
- return gfx::Size();
-}
+ if (content_size_.IsEmpty())
+ UpdateFromModel();
-void Combobox::Layout() {
- if (native_wrapper_) {
- native_wrapper_->GetView()->SetBounds(0, 0, width(), height());
- native_wrapper_->GetView()->Layout();
- }
+ // The preferred size will drive the local bounds which in turn is used to set
+ // the minimum width for the dropdown list.
+ gfx::Insets insets = GetInsets();
+ int total_width = std::max(kMinComboboxWidth, content_size_.width()) +
+ insets.width() + kDisclosureArrowLeftPadding +
+ disclosure_arrow_->width() + kDisclosureArrowRightPadding;
+
+ return gfx::Size(total_width, content_size_.height() + insets.height());
}
-void Combobox::OnEnabledChanged() {
- View::OnEnabledChanged();
- if (native_wrapper_)
- native_wrapper_->UpdateEnabled();
+const char* Combobox::GetClassName() const {
+ return kViewClassName;
}
-// VKEY_ESCAPE should be handled by this view when the drop down list is active.
-// In other words, the list should be closed instead of the dialog.
bool Combobox::SkipDefaultKeyEventProcessing(const ui::KeyEvent& e) {
+ // Escape should close the drop down list when it is active, not host UI.
if (e.key_code() != ui::VKEY_ESCAPE ||
e.IsShiftDown() || e.IsControlDown() || e.IsAltDown()) {
return false;
}
- return native_wrapper_ && native_wrapper_->IsDropdownOpen();
+ return dropdown_open_;
}
-void Combobox::OnPaintFocusBorder(gfx::Canvas* canvas) {
- if (NativeViewHost::kRenderNativeControlFocus)
- View::OnPaintFocusBorder(canvas);
+bool Combobox::OnMousePressed(const ui::MouseEvent& mouse_event) {
+ RequestFocus();
+ const base::TimeDelta delta = base::Time::Now() - closed_time_;
+ if (mouse_event.IsLeftMouseButton() &&
+ (delta.InMilliseconds() > kMinimumMsBetweenButtonClicks)) {
+ UpdateFromModel();
+ ShowDropDownMenu(ui::MENU_SOURCE_MOUSE);
+ }
+
+ return true;
+}
+
+bool Combobox::OnMouseDragged(const ui::MouseEvent& mouse_event) {
+ return true;
}
bool Combobox::OnKeyPressed(const ui::KeyEvent& e) {
- return native_wrapper_ && native_wrapper_->HandleKeyPressed(e);
+ // TODO(oshima): handle IME.
+ DCHECK_EQ(e.type(), ui::ET_KEY_PRESSED);
+
+ DCHECK_GE(selected_index_, 0);
+ DCHECK_LT(selected_index_, model()->GetItemCount());
+ if (selected_index_ < 0 || selected_index_ > model()->GetItemCount())
+ selected_index_ = 0;
+
+ bool show_menu = false;
+ int new_index = kNoSelection;
+ switch (e.key_code()) {
+ // Show the menu on Space.
+ case ui::VKEY_SPACE:
+ show_menu = true;
+ break;
+
+ // Show the menu on Alt+Down (like Windows) or move to the next item if any.
+ case ui::VKEY_DOWN:
+ if (e.IsAltDown())
+ show_menu = true;
+ else
+ new_index = GetAdjacentIndex(model(), 1, selected_index_);
+ break;
+
+ // Move to the end of the list.
+ case ui::VKEY_END:
+ case ui::VKEY_NEXT: // Page down.
+ new_index = GetAdjacentIndex(model(), -1, model()->GetItemCount());
+ break;
+
+ // Move to the beginning of the list.
+ case ui::VKEY_HOME:
+ case ui::VKEY_PRIOR: // Page up.
+ new_index = GetAdjacentIndex(model(), 1, -1);
+ break;
+
+ // Move to the previous item if any.
+ case ui::VKEY_UP:
+ new_index = GetAdjacentIndex(model(), -1, selected_index_);
+ break;
+
+ default:
+ return false;
+ }
+
+ if (show_menu) {
+ UpdateFromModel();
+ ShowDropDownMenu(ui::MENU_SOURCE_KEYBOARD);
+ } else if (new_index != selected_index_ && new_index != kNoSelection) {
+ DCHECK(!model()->IsItemSeparatorAt(new_index));
+ selected_index_ = new_index;
+ OnSelectionChanged();
+ }
+
+ return true;
}
bool Combobox::OnKeyReleased(const ui::KeyEvent& e) {
- return native_wrapper_ && native_wrapper_->HandleKeyReleased(e);
+ return false; // crbug.com/127520
+}
+
+void Combobox::OnGestureEvent(ui::GestureEvent* gesture) {
+ if (gesture->type() == ui::ET_GESTURE_TAP) {
+ UpdateFromModel();
+ ShowDropDownMenu(ui::MENU_SOURCE_TOUCH);
+ gesture->StopPropagation();
+ return;
+ }
+ View::OnGestureEvent(gesture);
+}
+
+void Combobox::OnPaint(gfx::Canvas* canvas) {
+ OnPaintBackground(canvas);
+ PaintText(canvas);
+ OnPaintBorder(canvas);
}
void Combobox::OnFocus() {
GetInputMethod()->OnFocus();
- // Forward the focus to the wrapper.
- if (native_wrapper_) {
- native_wrapper_->SetFocus();
- NotifyAccessibilityEvent(ui::AccessibilityTypes::EVENT_FOCUS, true);
- } else {
- View::OnFocus(); // Will focus the RootView window (so we still get
- // keyboard messages).
- }
+ text_border_->set_has_focus(true);
+ View::OnFocus();
}
void Combobox::OnBlur() {
GetInputMethod()->OnBlur();
if (selector_)
selector_->OnViewBlur();
- if (native_wrapper_)
- native_wrapper_->HandleBlur();
+ text_border_->set_has_focus(false);
}
void Combobox::GetAccessibleState(ui::AccessibleViewState* state) {
@@ -168,24 +327,129 @@ void Combobox::GetAccessibleState(ui::AccessibleViewState* state) {
state->count = model_->GetItemCount();
}
-void Combobox::ViewHierarchyChanged(
- const ViewHierarchyChangedDetails& details) {
- if (details.is_add && !native_wrapper_ && GetWidget()) {
- // The native wrapper's lifetime will be managed by the view hierarchy after
- // we call AddChildView.
- native_wrapper_ = NativeComboboxWrapper::CreateWrapper(this);
- AddChildView(native_wrapper_->GetView());
- // The underlying native widget may not be created until the wrapper is
- // parented. For this reason the wrapper is only updated after adding its
- // view.
- native_wrapper_->UpdateFromModel();
- native_wrapper_->UpdateSelectedIndex();
- native_wrapper_->UpdateEnabled();
+void Combobox::UpdateFromModel() {
+ int max_width = 0;
+ const gfx::Font& font = Combobox::GetFont();
+
+ MenuItemView* menu = new MenuItemView(this);
+ // MenuRunner owns |menu|.
+ dropdown_list_menu_runner_.reset(new MenuRunner(menu));
+
+ int num_items = model()->GetItemCount();
+ for (int i = 0; i < num_items; ++i) {
+ if (model()->IsItemSeparatorAt(i)) {
+ menu->AppendSeparator();
+ continue;
+ }
+
+ string16 text = 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_size_.SetSize(max_width, font.GetHeight());
}
-const char* Combobox::GetClassName() const {
- return kViewClassName;
+void Combobox::AdjustBoundsForRTLUI(gfx::Rect* rect) const {
+ rect->set_x(GetMirroredXForRect(*rect));
+}
+
+void Combobox::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 = invalid() ? kInvalidTextColor :
+ GetNativeTheme()->GetSystemColor(
+ ui::NativeTheme::kColorId_LabelEnabledColor);
+
+ DCHECK_GE(selected_index_, 0);
+ DCHECK_LT(selected_index_, model()->GetItemCount());
+ if (selected_index_ < 0 || selected_index_ > model()->GetItemCount())
+ selected_index_ = 0;
+ string16 text = model()->GetItemAt(selected_index_);
+
+ int disclosure_arrow_offset = width() - disclosure_arrow_->width()
+ - kDisclosureArrowLeftPadding - kDisclosureArrowRightPadding;
+
+ const gfx::Font& font = Combobox::GetFont();
+ int text_width = font.GetStringWidth(text);
+ if ((text_width + insets.width()) > disclosure_arrow_offset)
+ text_width = disclosure_arrow_offset - insets.width();
+
+ gfx::Rect text_bounds(x, y, text_width, text_height);
+ AdjustBoundsForRTLUI(&text_bounds);
+ canvas->DrawStringInt(text, font, text_color, text_bounds);
+
+ gfx::Rect arrow_bounds(disclosure_arrow_offset + kDisclosureArrowLeftPadding,
+ height() / 2 - disclosure_arrow_->height() / 2,
+ disclosure_arrow_->width(),
+ disclosure_arrow_->height());
+ AdjustBoundsForRTLUI(&arrow_bounds);
+
+ SkPaint paint;
+ // This makes the arrow subtractive.
+ if (invalid())
+ paint.setXfermodeMode(SkXfermode::kDstOut_Mode);
+ canvas->DrawImageInt(*disclosure_arrow_, arrow_bounds.x(), arrow_bounds.y(),
+ paint);
+
+ canvas->Restore();
+}
+
+void Combobox::ShowDropDownMenu(ui::MenuSourceType source_type) {
+ if (!dropdown_list_menu_runner_.get())
+ UpdateFromModel();
+
+ // Extend the menu to the width of the combobox.
+ MenuItemView* menu = dropdown_list_menu_runner_->GetMenu();
+ SubmenuView* submenu = menu->CreateSubmenu();
+ submenu->set_minimum_preferred_width(size().width() -
+ (kMenuBorderWidthLeft + kMenuBorderWidthRight));
+
+ gfx::Rect lb = GetLocalBounds();
+ gfx::Point menu_position(lb.origin());
+
+ // Inset the menu's requested position so the border of the menu lines up
+ // with the border of the combobox.
+ menu_position.set_x(menu_position.x() + kMenuBorderWidthLeft);
+ menu_position.set_y(menu_position.y() + kMenuBorderWidthTop);
+ lb.set_width(lb.width() - (kMenuBorderWidthLeft + kMenuBorderWidthRight));
+
+ 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(
+ GetWidget(), NULL, bounds, MenuItemView::TOPLEFT, source_type, 0) ==
+ MenuRunner::MENU_DELETED)
+ return;
+ dropdown_open_ = false;
+ closed_time_ = base::Time::Now();
+
+ // 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);
+}
+
+void Combobox::OnSelectionChanged() {
+ if (listener_)
+ listener_->OnSelectedIndexChanged(this);
+ NotifyAccessibilityEvent(ui::AccessibilityTypes::EVENT_VALUE_CHANGED, false);
+ SchedulePaint();
}
} // namespace views
diff --git a/ui/views/controls/combobox/combobox.h b/ui/views/controls/combobox/combobox.h
index e71604e..09188e8 100644
--- a/ui/views/controls/combobox/combobox.h
+++ b/ui/views/controls/combobox/combobox.h
@@ -7,10 +7,10 @@
#include <string>
+#include "base/time/time.h"
#include "ui/gfx/native_widget_types.h"
-#include "ui/views/controls/combobox/native_combobox_wrapper.h"
+#include "ui/views/controls/menu/menu_delegate.h"
#include "ui/views/controls/prefix_delegate.h"
-#include "ui/views/view.h"
namespace gfx {
class Font;
@@ -23,10 +23,12 @@ class ComboboxModel;
namespace views {
class ComboboxListener;
+class FocusableBorder;
+class MenuRunner;
class PrefixSelector;
-// A non-editable combobox (aka a drop-down list).
-class VIEWS_EXPORT Combobox : public PrefixDelegate {
+// A non-editable combobox (aka a drop-down list or selector).
+class VIEWS_EXPORT Combobox : public MenuDelegate, public PrefixDelegate {
public:
// The combobox's class name.
static const char kViewClassName[];
@@ -38,9 +40,7 @@ class VIEWS_EXPORT Combobox : public PrefixDelegate {
static const gfx::Font& GetFont();
// Sets the listener which will be called when a selection has been made.
- void set_listener(ComboboxListener* listener) {
- listener_ = listener;
- }
+ void set_listener(ComboboxListener* listener) { listener_ = listener; }
// Informs the combobox that its model changed.
void ModelChanged();
@@ -49,66 +49,67 @@ class VIEWS_EXPORT Combobox : public PrefixDelegate {
int selected_index() const { return selected_index_; }
void SetSelectedIndex(int index);
- // Called when the combobox's selection is changed by the user.
- void SelectionChanged();
-
ui::ComboboxModel* model() const { return model_; }
// Set the accessible name of the combobox.
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_;
- }
-
- // Visually marks the combobox as having an invalid value selected. The caller
- // is responsible for flipping it back to valid if the selection changes.
+ // Visually marks the combobox as having an invalid value selected.
+ // When invalid, it paints with white text on a red background.
+ // Callers are responsible for restoring validity with selection changes.
void SetInvalid(bool invalid);
-
- bool invalid() const {
- return invalid_;
- }
+ bool invalid() const { return invalid_; }
// Overridden from View:
virtual gfx::Size GetPreferredSize() OVERRIDE;
- virtual void Layout() OVERRIDE;
- virtual void OnEnabledChanged() OVERRIDE;
+ virtual const char* GetClassName() const OVERRIDE;
virtual bool SkipDefaultKeyEventProcessing(const ui::KeyEvent& e) OVERRIDE;
- virtual void OnPaintFocusBorder(gfx::Canvas* canvas) OVERRIDE;
+ virtual bool OnMousePressed(const ui::MouseEvent& mouse_event) OVERRIDE;
+ virtual bool OnMouseDragged(const ui::MouseEvent& mouse_event) OVERRIDE;
virtual bool OnKeyPressed(const ui::KeyEvent& e) OVERRIDE;
virtual bool OnKeyReleased(const ui::KeyEvent& e) OVERRIDE;
+ virtual void OnGestureEvent(ui::GestureEvent* gesture) OVERRIDE;
+ virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
virtual void OnFocus() OVERRIDE;
virtual void OnBlur() OVERRIDE;
virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE;
virtual ui::TextInputClient* GetTextInputClient() OVERRIDE;
+ // Overridden from MenuDelegate:
+ 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;
+
// Overridden from PrefixDelegate:
virtual int GetRowCount() OVERRIDE;
virtual int GetSelectedRow() OVERRIDE;
virtual void SetSelectedRow(int row) OVERRIDE;
virtual string16 GetTextForRow(int row) OVERRIDE;
- protected:
- // Overridden from View:
- virtual void ViewHierarchyChanged(
- const ViewHierarchyChangedDetails& details) OVERRIDE;
- virtual const char* GetClassName() const OVERRIDE;
+ private:
+ // Updates the combobox's content from its model.
+ void UpdateFromModel();
- // The object that actually implements the native combobox.
- NativeComboboxWrapper* native_wrapper_;
+ // Given bounds within our View, this helper mirrors the bounds if necessary.
+ void AdjustBoundsForRTLUI(gfx::Rect* rect) const;
+
+ // Draw the selected value of the drop down list
+ void PaintText(gfx::Canvas* canvas);
+
+ // Show the drop down list
+ void ShowDropDownMenu(ui::MenuSourceType source_type);
+
+ // Called when the selection is changed by the user.
+ void OnSelectionChanged();
- private:
// Our model. Not owned.
ui::ComboboxModel* model_;
// Our listener. Not owned. Notified when the selected index change.
ComboboxListener* listener_;
- // The current selected index.
+ // The current selected index; -1 and means no selection.
int selected_index_;
// True when the selection is visually denoted as invalid.
@@ -117,8 +118,32 @@ class VIEWS_EXPORT Combobox : public PrefixDelegate {
// The accessible name of this combobox.
string16 accessible_name_;
+ // A helper used to select entries by keyboard input.
scoped_ptr<PrefixSelector> selector_;
+ // The reference to the border class. The object is owned by View::border_.
+ FocusableBorder* text_border_;
+
+ // The disclosure arrow next to the currently selected item from the list.
+ const gfx::ImageSkia* disclosure_arrow_;
+
+ // Responsible for showing the context menu.
+ scoped_ptr<MenuRunner> dropdown_list_menu_runner_;
+
+ // Is the drop down list showing
+ bool dropdown_open_;
+
+ // Like MenuButton, we use a time object in order to keep track of when the
+ // combobox was closed. The time is used for simulating menu behavior; that
+ // is, if the menu is shown and the button is pressed, we need to close the
+ // menu. There is no clean way to get the second click event because the
+ // menu is displayed using a modal loop and, unlike regular menus in Windows,
+ // the button is not part of the displayed menu.
+ base::Time closed_time_;
+
+ // The maximum dimensions of the content in the dropdown
+ gfx::Size content_size_;
+
DISALLOW_COPY_AND_ASSIGN(Combobox);
};
diff --git a/ui/views/controls/combobox/native_combobox_views_unittest.cc b/ui/views/controls/combobox/combobox_unittest.cc
index 821db8a..80bac01 100644
--- a/ui/views/controls/combobox/native_combobox_views_unittest.cc
+++ b/ui/views/controls/combobox/combobox_unittest.cc
@@ -1,8 +1,8 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2013 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 "ui/views/controls/combobox/combobox.h"
#include <set>
@@ -10,10 +10,8 @@
#include "ui/base/events/event.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/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 {
@@ -85,18 +83,9 @@ class TestComboboxModel : public ui::ComboboxModel {
namespace views {
-class NativeComboboxViewsTest : public ViewsTestBase {
+class ComboboxTest : public ViewsTestBase {
public:
- NativeComboboxViewsTest()
- : widget_(NULL),
- combobox_(NULL),
- combobox_view_(NULL),
- input_method_(NULL) {}
-
- // ::testing::Test:
- virtual void SetUp() OVERRIDE {
- ViewsTestBase::SetUp();
- }
+ ComboboxTest() : widget_(NULL), combobox_(NULL), input_method_(NULL) {}
virtual void TearDown() OVERRIDE {
if (widget_)
@@ -119,10 +108,6 @@ class NativeComboboxViewsTest : public ViewsTestBase {
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_);
@@ -148,9 +133,6 @@ class NativeComboboxViewsTest : public ViewsTestBase {
// |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 the model, hence it needs to be scoped.
scoped_ptr<TestComboboxModel> model_;
@@ -158,7 +140,7 @@ class NativeComboboxViewsTest : public ViewsTestBase {
MockInputMethod* input_method_;
};
-TEST_F(NativeComboboxViewsTest, KeyTest) {
+TEST_F(ComboboxTest, KeyTest) {
InitCombobox();
SendKeyEvent(ui::VKEY_END);
EXPECT_EQ(combobox_->selected_index() + 1, model_->GetItemCount());
@@ -181,13 +163,12 @@ TEST_F(NativeComboboxViewsTest, KeyTest) {
// Check that if a combobox is disabled before it has a native wrapper, then the
// native wrapper inherits the disabled state when it gets created.
-TEST_F(NativeComboboxViewsTest, DisabilityTest) {
+TEST_F(ComboboxTest, DisabilityTest) {
model_.reset(new TestComboboxModel());
ASSERT_FALSE(combobox_);
combobox_ = new TestCombobox(model_.get());
combobox_->SetEnabled(false);
- ASSERT_FALSE(combobox_->GetNativeWrapperForTesting());
widget_ = new Widget;
Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
@@ -196,16 +177,12 @@ TEST_F(NativeComboboxViewsTest, DisabilityTest) {
View* container = new View();
widget_->SetContentsView(container);
container->AddChildView(combobox_);
-
- combobox_view_ = static_cast<NativeComboboxViews*>(
- combobox_->GetNativeWrapperForTesting());
- ASSERT_TRUE(combobox_view_);
- ASSERT_FALSE(combobox_view_->enabled());
+ EXPECT_FALSE(combobox_->enabled());
}
// Verifies that we don't select a separator line in combobox when navigating
// through keyboard.
-TEST_F(NativeComboboxViewsTest, SkipSeparatorSimple) {
+TEST_F(ComboboxTest, SkipSeparatorSimple) {
InitCombobox();
std::set<int> separators;
separators.insert(2);
@@ -227,7 +204,7 @@ TEST_F(NativeComboboxViewsTest, SkipSeparatorSimple) {
// Verifies that we never select the separator that is in the beginning of the
// combobox list when navigating through keyboard.
-TEST_F(NativeComboboxViewsTest, SkipSeparatorBeginning) {
+TEST_F(ComboboxTest, SkipSeparatorBeginning) {
InitCombobox();
std::set<int> separators;
separators.insert(0);
@@ -249,7 +226,7 @@ TEST_F(NativeComboboxViewsTest, SkipSeparatorBeginning) {
// Verifies that we never select the separator that is in the end of the
// combobox list when navigating through keyboard.
-TEST_F(NativeComboboxViewsTest, SkipSeparatorEnd) {
+TEST_F(ComboboxTest, SkipSeparatorEnd) {
InitCombobox();
std::set<int> separators;
separators.insert(model_->GetItemCount() - 1);
@@ -266,7 +243,7 @@ TEST_F(NativeComboboxViewsTest, SkipSeparatorEnd) {
// Verifies that we never select any of the adjacent separators (multiple
// consecutive) that appear in the beginning of the combobox list when
// navigating through keyboard.
-TEST_F(NativeComboboxViewsTest, SkipMultipleSeparatorsAtBeginning) {
+TEST_F(ComboboxTest, SkipMultipleSeparatorsAtBeginning) {
InitCombobox();
std::set<int> separators;
separators.insert(0);
@@ -291,7 +268,7 @@ TEST_F(NativeComboboxViewsTest, SkipMultipleSeparatorsAtBeginning) {
// Verifies that we never select any of the adjacent separators (multiple
// consecutive) that appear in the middle of the combobox list when navigating
// through keyboard.
-TEST_F(NativeComboboxViewsTest, SkipMultipleAdjacentSeparatorsAtMiddle) {
+TEST_F(ComboboxTest, SkipMultipleAdjacentSeparatorsAtMiddle) {
InitCombobox();
std::set<int> separators;
separators.insert(4);
@@ -308,7 +285,7 @@ TEST_F(NativeComboboxViewsTest, SkipMultipleAdjacentSeparatorsAtMiddle) {
// Verifies that we never select any of the adjacent separators (multiple
// consecutive) that appear in the end of the combobox list when navigating
// through keyboard.
-TEST_F(NativeComboboxViewsTest, SkipMultipleSeparatorsAtEnd) {
+TEST_F(ComboboxTest, SkipMultipleSeparatorsAtEnd) {
InitCombobox();
std::set<int> separators;
separators.insert(7);
diff --git a/ui/views/controls/combobox/native_combobox_views.cc b/ui/views/controls/combobox/native_combobox_views.cc
deleted file mode 100644
index f9d3f00..0000000
--- a/ui/views/controls/combobox/native_combobox_views.cc
+++ /dev/null
@@ -1,448 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ui/views/controls/combobox/native_combobox_views.h"
-
-#include <algorithm>
-
-#include "grit/ui_resources.h"
-#include "ui/base/events/event.h"
-#include "ui/base/keycodes/keyboard_codes.h"
-#include "ui/base/models/combobox_model.h"
-#include "ui/base/resource/resource_bundle.h"
-#include "ui/gfx/canvas.h"
-#include "ui/gfx/font.h"
-#include "ui/gfx/image/image.h"
-#include "ui/gfx/path.h"
-#include "ui/native_theme/native_theme.h"
-#include "ui/views/background.h"
-#include "ui/views/border.h"
-#include "ui/views/color_constants.h"
-#include "ui/views/controls/combobox/combobox.h"
-#include "ui/views/controls/focusable_border.h"
-#include "ui/views/controls/menu/menu_runner.h"
-#include "ui/views/controls/menu/submenu_view.h"
-#include "ui/views/mouse_constants.h"
-#include "ui/views/widget/root_view.h"
-#include "ui/views/widget/widget.h"
-
-namespace views {
-
-namespace {
-
-// Define the size of the insets.
-const int kTopInsetSize = 4;
-const int kLeftInsetSize = 4;
-const int kBottomInsetSize = 4;
-const int kRightInsetSize = 4;
-
-// Menu border widths
-const int kMenuBorderWidthLeft = 1;
-const int kMenuBorderWidthTop = 1;
-const int kMenuBorderWidthRight = 1;
-const int kMenuBorderWidthBottom = 2;
-
-// Limit how small a combobox can be.
-const int kMinComboboxWidth = 25;
-
-// Size of the combobox arrow margins
-const int kDisclosureArrowLeftPadding = 7;
-const int kDisclosureArrowRightPadding = 7;
-
-// Define the id of the first item in the menu (since it needs to be > 0)
-const int kFirstMenuItemId = 1000;
-
-const SkColor kInvalidTextColor = SK_ColorWHITE;
-
-// Used to indicate that no item is currently selected by the user.
-const int kNoSelection = -1;
-
-// The background to use for invalid comboboxes.
-class InvalidBackground : public Background {
- public:
- InvalidBackground() {}
- virtual ~InvalidBackground() {}
-
- // Overridden from Background:
- virtual void Paint(gfx::Canvas* canvas, View* view) const OVERRIDE {
- gfx::Rect bounds(view->GetLocalBounds());
- // Inset by 2 to leave 1 empty pixel between background and border.
- bounds.Inset(2, 2, 2, 2);
- canvas->FillRect(bounds, kWarningColor);
- }
-
- private:
- DISALLOW_COPY_AND_ASSIGN(InvalidBackground);
-};
-
-// Returns the next or previous valid index (depending on |increment|'s value).
-// Skips separator indices. Returns -1 if there is no valid adjacent index.
-int GetAdjacentIndex(Combobox* combobox, int increment, int index) {
- DCHECK(increment == -1 || increment == 1);
-
- index += increment;
- while (index >= 0 && index < combobox->model()->GetItemCount()) {
- if (!combobox->model()->IsItemSeparatorAt(index))
- return index;
- index += increment;
- }
- return kNoSelection;
-}
-
-} // namespace
-
-const char NativeComboboxViews::kViewClassName[] = "views/NativeComboboxViews";
-
-NativeComboboxViews::NativeComboboxViews(Combobox* combobox)
- : combobox_(combobox),
- text_border_(new FocusableBorder()),
- disclosure_arrow_(ui::ResourceBundle::GetSharedInstance().GetImageNamed(
- IDR_MENU_DROPARROW).ToImageSkia()),
- dropdown_open_(false),
- selected_index_(kNoSelection),
- content_width_(0),
- content_height_(0) {
- set_border(text_border_);
-}
-
-NativeComboboxViews::~NativeComboboxViews() {
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// NativeComboboxViews, View overrides:
-
-bool NativeComboboxViews::OnMousePressed(const ui::MouseEvent& mouse_event) {
- combobox_->RequestFocus();
- const base::TimeDelta delta = base::Time::Now() - closed_time_;
- if (mouse_event.IsLeftMouseButton() &&
- delta.InMilliseconds() > kMinimumMsBetweenButtonClicks) {
- UpdateFromModel();
- ShowDropDownMenu(ui::MENU_SOURCE_MOUSE);
- }
-
- return true;
-}
-
-bool NativeComboboxViews::OnMouseDragged(const ui::MouseEvent& mouse_event) {
- return true;
-}
-
-bool NativeComboboxViews::OnKeyPressed(const ui::KeyEvent& key_event) {
- // TODO(oshima): handle IME.
- DCHECK_EQ(key_event.type(), ui::ET_KEY_PRESSED);
-
- bool show_menu = false;
- int new_index = kNoSelection;
- switch (key_event.key_code()) {
- // Show the menu on Space.
- case ui::VKEY_SPACE:
- show_menu = true;
- break;
-
- // Show the menu on Alt+Down (like Windows) or move to the next item if any.
- case ui::VKEY_DOWN:
- if (key_event.IsAltDown())
- show_menu = true;
- else
- new_index = GetAdjacentIndex(combobox_, 1, selected_index_);
- break;
-
- // Move to the end of the list.
- case ui::VKEY_END:
- case ui::VKEY_NEXT: // Page down.
- new_index =
- GetAdjacentIndex(combobox_, -1, combobox_->model()->GetItemCount());
- break;
-
- // Move to the beginning of the list.
- case ui::VKEY_HOME:
- case ui::VKEY_PRIOR: // Page up.
- new_index = GetAdjacentIndex(combobox_, 1, -1);
- break;
-
- // Move to the previous item if any.
- case ui::VKEY_UP:
- new_index = GetAdjacentIndex(combobox_, -1, selected_index_);
- break;
-
- default:
- return false;
- }
-
- if (show_menu) {
- UpdateFromModel();
- ShowDropDownMenu(ui::MENU_SOURCE_KEYBOARD);
- } else if (new_index != selected_index_ && new_index != kNoSelection) {
- DCHECK(!combobox_->model()->IsItemSeparatorAt(new_index));
- selected_index_ = new_index;
- combobox_->SelectionChanged();
- SchedulePaint();
- }
-
- return true;
-}
-
-bool NativeComboboxViews::OnKeyReleased(const ui::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, ui::EventHandler overrides:
-
-void NativeComboboxViews::OnGestureEvent(ui::GestureEvent* gesture) {
- if (gesture->type() == ui::ET_GESTURE_TAP) {
- UpdateFromModel();
- ShowDropDownMenu(ui::MENU_SOURCE_TOUCH);
- gesture->StopPropagation();
- return;
- }
- View::OnGestureEvent(gesture);
-}
-
-/////////////////////////////////////////////////////////////////
-// NativeComboboxViews, NativeComboboxWrapper overrides:
-
-void NativeComboboxViews::UpdateFromModel() {
- int max_width = 0;
- const gfx::Font& font = Combobox::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) {
- if (combobox_->model()->IsItemSeparatorAt(i)) {
- menu->AppendSeparator();
- continue;
- }
-
- 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.GetHeight();
-}
-
-void NativeComboboxViews::UpdateSelectedIndex() {
- selected_index_ = combobox_->selected_index();
- SchedulePaint();
-}
-
-void NativeComboboxViews::UpdateEnabled() {
- SetEnabled(combobox_->enabled());
-}
-
-int NativeComboboxViews::GetSelectedIndex() const {
- return selected_index_;
-}
-
-bool NativeComboboxViews::IsDropdownOpen() const {
- return dropdown_open_;
-}
-
-gfx::Size NativeComboboxViews::GetPreferredSize() {
- if (content_width_ == 0)
- UpdateFromModel();
-
- // The preferred size will drive the local bounds which in turn is used to set
- // the minimum width for the dropdown list.
- gfx::Insets insets = GetInsets();
- int total_width = std::max(kMinComboboxWidth, content_width_) +
- insets.width() + kDisclosureArrowLeftPadding +
- disclosure_arrow_->width() + kDisclosureArrowRightPadding;
-
- return gfx::Size(total_width, content_height_ + insets.height());
-}
-
-View* NativeComboboxViews::GetView() {
- return this;
-}
-
-void NativeComboboxViews::SetFocus() {
- text_border_->set_has_focus(true);
-}
-
-void NativeComboboxViews::ValidityStateChanged() {
- if (combobox_->invalid()) {
- text_border_->SetColor(kWarningColor);
- set_background(new InvalidBackground());
- } else {
- text_border_->UseDefaultColor();
- set_background(NULL);
- }
- SchedulePaint();
-}
-
-bool NativeComboboxViews::HandleKeyPressed(const ui::KeyEvent& e) {
- return OnKeyPressed(e);
-}
-
-bool NativeComboboxViews::HandleKeyReleased(const ui::KeyEvent& e) {
- return false; // crbug.com/127520
-}
-
-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_index_ = id;
- combobox_->SelectionChanged();
- SchedulePaint();
-}
-
-bool NativeComboboxViews::GetAccelerator(int id, ui::Accelerator* accel) {
- return false;
-}
-
-/////////////////////////////////////////////////////////////////
-// NativeComboboxViews private methods:
-
-void NativeComboboxViews::AdjustBoundsForRTLUI(gfx::Rect* rect) const {
- rect->set_x(GetMirroredXForRect(*rect));
-}
-
-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 = combobox_->invalid() ? kInvalidTextColor :
- GetNativeTheme()->GetSystemColor(
- ui::NativeTheme::kColorId_LabelEnabledColor);
-
- int index = GetSelectedIndex();
- if (index < 0 || index > combobox_->model()->GetItemCount())
- index = 0;
- string16 text = combobox_->model()->GetItemAt(index);
-
- int disclosure_arrow_offset = width() - disclosure_arrow_->width()
- - kDisclosureArrowLeftPadding - kDisclosureArrowRightPadding;
-
- const gfx::Font& font = Combobox::GetFont();
- int text_width = font.GetStringWidth(text);
- if ((text_width + insets.width()) > disclosure_arrow_offset)
- text_width = disclosure_arrow_offset - insets.width();
-
- gfx::Rect text_bounds(x, y, text_width, text_height);
- AdjustBoundsForRTLUI(&text_bounds);
- canvas->DrawStringInt(text, font, text_color, text_bounds);
-
- gfx::Rect arrow_bounds(disclosure_arrow_offset + kDisclosureArrowLeftPadding,
- height() / 2 - disclosure_arrow_->height() / 2,
- disclosure_arrow_->width(),
- disclosure_arrow_->height());
- AdjustBoundsForRTLUI(&arrow_bounds);
-
- SkPaint paint;
- // This makes the arrow subtractive.
- if (combobox_->invalid())
- paint.setXfermodeMode(SkXfermode::kDstOut_Mode);
- canvas->DrawImageInt(*disclosure_arrow_, arrow_bounds.x(), arrow_bounds.y(),
- paint);
-
- canvas->Restore();
-}
-
-void NativeComboboxViews::ShowDropDownMenu(ui::MenuSourceType source_type) {
- if (!dropdown_list_menu_runner_.get())
- UpdateFromModel();
-
- // Extend the menu to the width of the combobox.
- MenuItemView* menu = dropdown_list_menu_runner_->GetMenu();
- SubmenuView* submenu = menu->CreateSubmenu();
- submenu->set_minimum_preferred_width(size().width() -
- (kMenuBorderWidthLeft + kMenuBorderWidthRight));
-
- gfx::Rect lb = GetLocalBounds();
- gfx::Point menu_position(lb.origin());
-
- // Inset the menu's requested position so the border of the menu lines up
- // with the border of the combobox.
- menu_position.set_x(menu_position.x() + kMenuBorderWidthLeft);
- menu_position.set_y(menu_position.y() + kMenuBorderWidthTop);
- lb.set_width(lb.width() - (kMenuBorderWidthLeft + kMenuBorderWidthRight));
-
- 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(
- GetWidget(), NULL, bounds, MenuItemView::TOPLEFT, source_type, 0) ==
- MenuRunner::MENU_DELETED)
- return;
- dropdown_open_ = false;
- closed_time_ = base::Time::Now();
-
- // 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
deleted file mode 100644
index bdf7033..0000000
--- a/ui/views/controls/combobox/native_combobox_views.h
+++ /dev/null
@@ -1,115 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef UI_VIEWS_CONTROLS_COMBOBOX_NATIVE_COMBOBOX_VIEWS_H_
-#define UI_VIEWS_CONTROLS_COMBOBOX_NATIVE_COMBOBOX_VIEWS_H_
-
-#include "base/time/time.h"
-#include "ui/views/controls/combobox/native_combobox_wrapper.h"
-#include "ui/views/controls/menu/menu_delegate.h"
-#include "ui/views/view.h"
-
-namespace gfx {
-class Canvas;
-class Font;
-}
-
-namespace views {
-
-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:
- static const char kViewClassName[];
-
- explicit NativeComboboxViews(Combobox* combobox);
- virtual ~NativeComboboxViews();
-
- // views::View overrides:
- virtual bool OnMousePressed(const ui::MouseEvent& mouse_event) OVERRIDE;
- virtual bool OnMouseDragged(const ui::MouseEvent& mouse_event) OVERRIDE;
- virtual bool OnKeyPressed(const ui::KeyEvent& key_event) OVERRIDE;
- virtual bool OnKeyReleased(const ui::KeyEvent& key_event) OVERRIDE;
- virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
- virtual void OnFocus() OVERRIDE;
- virtual void OnBlur() OVERRIDE;
-
- // ui::EventHandler overrides:
- virtual void OnGestureEvent(ui::GestureEvent* gesture) OVERRIDE;
-
- // NativeComboboxWrapper overrides:
- virtual void UpdateFromModel() OVERRIDE;
- virtual void UpdateSelectedIndex() OVERRIDE;
- virtual void UpdateEnabled() OVERRIDE;
- virtual int GetSelectedIndex() const OVERRIDE;
- virtual bool IsDropdownOpen() const OVERRIDE;
- virtual gfx::Size GetPreferredSize() OVERRIDE;
- virtual View* GetView() OVERRIDE;
- virtual void SetFocus() OVERRIDE;
- virtual void ValidityStateChanged() OVERRIDE;
- virtual bool HandleKeyPressed(const ui::KeyEvent& event) OVERRIDE;
- virtual bool HandleKeyReleased(const ui::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;
-
- private:
- // Given bounds within our View, this helper routine mirrors the bounds if
- // necessary.
- void AdjustBoundsForRTLUI(gfx::Rect* rect) const;
-
- // Draw the selected value of the drop down list
- void PaintText(gfx::Canvas* canvas);
-
- // Show the drop down list
- void ShowDropDownMenu(ui::MenuSourceType source_type);
-
- // 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_;
-
- // The disclosure arrow next to the currently selected item from the list.
- const gfx::ImageSkia* disclosure_arrow_;
-
- // Responsible for showing the context menu.
- scoped_ptr<MenuRunner> dropdown_list_menu_runner_;
-
- // Is the drop down list showing
- bool dropdown_open_;
-
- // Like MenuButton, we use a time object in order to keep track of when the
- // combobox was closed. The time is used for simulating menu behavior; that
- // is, if the menu is shown and the button is pressed, we need to close the
- // menu. There is no clean way to get the second click event because the
- // menu is displayed using a modal loop and, unlike regular menus in Windows,
- // the button is not part of the displayed menu.
- base::Time closed_time_;
-
- // The selected index in the model. The default value is -1, which means no
- // selection.
- int selected_index_;
-
- // 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_win.cc b/ui/views/controls/combobox/native_combobox_win.cc
deleted file mode 100644
index a810d85..0000000
--- a/ui/views/controls/combobox/native_combobox_win.cc
+++ /dev/null
@@ -1,218 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ui/views/controls/combobox/native_combobox_win.h"
-
-#include "base/i18n/rtl.h"
-#include "base/strings/utf_string_conversions.h"
-#include "ui/base/events/event.h"
-#include "ui/base/models/combobox_model.h"
-#include "ui/base/resource/resource_bundle.h"
-#include "ui/gfx/font.h"
-#include "ui/gfx/win/hwnd_util.h"
-#include "ui/native_theme/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 {
-
-// Limit how small a combobox can be.
-const int kMinComboboxWidth = 148;
-
-// Add a couple extra pixels to the widths of comboboxes and combobox
-// dropdowns so that text isn't too crowded.
-const int kComboboxExtraPaddingX = 6;
-
-} // namespace
-
-namespace views {
-
-////////////////////////////////////////////////////////////////////////////////
-// 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);
- const gfx::Font& font = Combobox::GetFont();
- 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_index(), 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::UpdateSelectedIndex() {
- // 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 combobox
- // and therefore we can not assume that the string we get from
- // ui::ComboboxModel can be safely searched for and selected (which is what
- // CB_SELECTSTRING does).
- SendMessage(native_view(), CB_SETCURSEL, combobox_->selected_index(), 0);
-}
-
-void NativeComboboxWin::UpdateEnabled() {
- SetEnabled(combobox_->enabled());
-}
-
-int NativeComboboxWin::GetSelectedIndex() const {
- LRESULT selected_index = SendMessage(native_view(), CB_GETCURSEL, 0, 0);
- return selected_index != CB_ERR ? selected_index : -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 = ui::NativeThemeWin::instance()->GetThemeBorderSize(
- ui::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();
-}
-
-void NativeComboboxWin::ValidityStateChanged() {
- // TODO(estade): implement.
-}
-
-bool NativeComboboxWin::HandleKeyPressed(const ui::KeyEvent& event) {
- return false;
-}
-
-bool NativeComboboxWin::HandleKeyReleased(const ui::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);
- gfx::CheckWindowCreated(control_hwnd);
- NativeControlCreated(control_hwnd);
-}
-
-void NativeComboboxWin::NativeControlCreated(HWND native_control) {
- NativeControlWin::NativeControlCreated(native_control);
-
- UpdateFont();
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// NativeComboboxWin, private:
-
-void NativeComboboxWin::UpdateFont() {
- HFONT font = Combobox::GetFont().GetNativeFont();
- SendMessage(native_view(), WM_SETFONT, reinterpret_cast<WPARAM>(font), FALSE);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// NativeComboboxWrapper, public:
-
-// static
-NativeComboboxWrapper* NativeComboboxWrapper::CreateWrapper(
- Combobox* combobox) {
- return new NativeComboboxViews(combobox);
-}
-
-} // namespace views
diff --git a/ui/views/controls/combobox/native_combobox_win.h b/ui/views/controls/combobox/native_combobox_win.h
deleted file mode 100644
index 5349460..0000000
--- a/ui/views/controls/combobox/native_combobox_win.h
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef UI_VIEWS_CONTROLS_COMBOBOX_NATIVE_COMBOBOX_WIN_H_
-#define UI_VIEWS_CONTROLS_COMBOBOX_NATIVE_COMBOBOX_WIN_H_
-
-#include "ui/views/controls/combobox/native_combobox_wrapper.h"
-#include "ui/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 UpdateSelectedIndex() OVERRIDE;
- virtual void UpdateEnabled() OVERRIDE;
- virtual int GetSelectedIndex() const OVERRIDE;
- virtual bool IsDropdownOpen() const OVERRIDE;
- virtual gfx::Size GetPreferredSize() OVERRIDE;
- virtual View* GetView() OVERRIDE;
- virtual void SetFocus() OVERRIDE;
- virtual void ValidityStateChanged() OVERRIDE;
- virtual bool HandleKeyPressed(const ui::KeyEvent& event) OVERRIDE;
- virtual bool HandleKeyReleased(const ui::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
deleted file mode 100644
index 539812f..0000000
--- a/ui/views/controls/combobox/native_combobox_wrapper.h
+++ /dev/null
@@ -1,76 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef UI_VIEWS_CONTROLS_COMBOBOX_NATIVE_COMBOBOX_WRAPPER_H_
-#define UI_VIEWS_CONTROLS_COMBOBOX_NATIVE_COMBOBOX_WRAPPER_H_
-
-#include "ui/gfx/native_widget_types.h"
-#include "ui/views/views_export.h"
-
-namespace gfx {
-class Size;
-}
-
-namespace ui {
-class KeyEvent;
-}
-
-namespace views {
-
-class Combobox;
-class View;
-
-class VIEWS_EXPORT NativeComboboxWrapper {
- public:
- // Updates the combobox's content from its model.
- virtual void UpdateFromModel() = 0;
-
- // Updates the selected index from the associated combobox.
- virtual void UpdateSelectedIndex() = 0;
-
- // Updates the enabled state of the combobox from the associated view.
- virtual void UpdateEnabled() = 0;
-
- // Returns the selected index.
- virtual int GetSelectedIndex() 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 view that hosts the native control.
- virtual View* GetView() = 0;
-
- // Sets the focus to the button.
- virtual void SetFocus() = 0;
-
- // Called when the combobox flipped validity. When invalid, it paints
- // with white text on a red background.
- virtual void ValidityStateChanged() = 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 ui::KeyEvent& e) = 0;
- virtual bool HandleKeyReleased(const ui::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_