diff options
author | msw@chromium.org <msw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-09-14 04:53:08 +0000 |
---|---|---|
committer | msw@chromium.org <msw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-09-14 04:53:08 +0000 |
commit | f6445da986734610bee921979d10c76cea0f7e23 (patch) | |
tree | 0a81f747585e0512c8732018b514b2e39b6a99e1 | |
parent | 992558368941b95d0c8f08b7d3887bd4ac2098a9 (diff) | |
download | chromium_src-f6445da986734610bee921979d10c76cea0f7e23.zip chromium_src-f6445da986734610bee921979d10c76cea0f7e23.tar.gz chromium_src-f6445da986734610bee921979d10c76cea0f7e23.tar.bz2 |
Remove NativeComboboxWin; consolidate Views Combobox code.
The Views Combobox is used by default on Win M30 (stable).
This removes the deprecated native Windows control impl.
The NativeComboboxViews impl is promoted to Combobox.
Perform additional cleanup (remove wrapper, etc.).
BUG=175843
TEST=Views Comboboxes (e.g. in the bookmark bubble) work as expected.
R=sky@chromium.org
Review URL: https://chromiumcodereview.appspot.com/22889018
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@223213 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | ui/views/controls/combobox/combobox.cc | 392 | ||||
-rw-r--r-- | ui/views/controls/combobox/combobox.h | 97 | ||||
-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.cc | 448 | ||||
-rw-r--r-- | ui/views/controls/combobox/native_combobox_views.h | 115 | ||||
-rw-r--r-- | ui/views/controls/combobox/native_combobox_win.cc | 218 | ||||
-rw-r--r-- | ui/views/controls/combobox/native_combobox_win.h | 58 | ||||
-rw-r--r-- | ui/views/controls/combobox/native_combobox_wrapper.h | 76 | ||||
-rw-r--r-- | ui/views/views.gyp | 7 | ||||
-rw-r--r-- | ui/views/widget/widget.h | 2 |
10 files changed, 404 insertions, 1058 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_ diff --git a/ui/views/views.gyp b/ui/views/views.gyp index df51058..a471825 100644 --- a/ui/views/views.gyp +++ b/ui/views/views.gyp @@ -95,11 +95,6 @@ 'controls/combobox/combobox.cc', 'controls/combobox/combobox.h', 'controls/combobox/combobox_listener.h', - 'controls/combobox/native_combobox_views.cc', - 'controls/combobox/native_combobox_views.h', - 'controls/combobox/native_combobox_win.cc', - 'controls/combobox/native_combobox_win.h', - 'controls/combobox/native_combobox_wrapper.h', 'controls/focusable_border.cc', 'controls/focusable_border.h', 'controls/glow_hover_controller.cc', @@ -718,7 +713,7 @@ 'controls/button/custom_button_unittest.cc', 'controls/button/image_button_unittest.cc', 'controls/button/label_button_unittest.cc', - 'controls/combobox/native_combobox_views_unittest.cc', + 'controls/combobox/combobox_unittest.cc', 'controls/label_unittest.cc', 'controls/menu/menu_model_adapter_unittest.cc', 'controls/native/native_view_host_aura_unittest.cc', diff --git a/ui/views/widget/widget.h b/ui/views/widget/widget.h index e9ca9b0a..1600f94 100644 --- a/ui/views/widget/widget.h +++ b/ui/views/widget/widget.h @@ -746,8 +746,8 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate, void DestroyRootView(); private: + friend class ComboboxTest; friend class NativeTextfieldViewsTest; - friend class NativeComboboxViewsTest; // Sets the value of |disable_inactive_rendering_|. If the value changes, // both the NonClientView and WidgetDelegate are notified. |