diff options
Diffstat (limited to 'ui/views/controls/combobox')
-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 |
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_ |