diff options
author | saintlou@chromium.org <saintlou@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-04-29 22:59:15 +0000 |
---|---|---|
committer | saintlou@chromium.org <saintlou@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-04-29 22:59:15 +0000 |
commit | abe390e388afe636b55096036785ab3801d95029 (patch) | |
tree | 626beb3df4db2d8dc8dff10843edcad42b396c4e /views | |
parent | 09efa3d1622cf315dee71154904a52407470c810 (diff) | |
download | chromium_src-abe390e388afe636b55096036785ab3801d95029.zip chromium_src-abe390e388afe636b55096036785ab3801d95029.tar.gz chromium_src-abe390e388afe636b55096036785ab3801d95029.tar.bz2 |
Initial implementation of pure Views combobox (aka dropdown list). The following known missing items will be added in a separate CL
+ RTL handling
+ possible vertical centering of menu (UX)
+ Handle Key events for accessibility
+ Factor the focused border also present in textfield
+ look of the double arrow (now drawn in skia)
BUG=none
TEST=none
Review URL: http://codereview.chromium.org/6880224
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@83617 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'views')
-rw-r--r-- | views/controls/combobox/combobox.h | 4 | ||||
-rw-r--r-- | views/controls/combobox/native_combobox_gtk.cc | 5 | ||||
-rw-r--r-- | views/controls/combobox/native_combobox_views.cc | 388 | ||||
-rw-r--r-- | views/controls/combobox/native_combobox_views.h | 136 | ||||
-rw-r--r-- | views/views.gyp | 2 |
5 files changed, 532 insertions, 3 deletions
diff --git a/views/controls/combobox/combobox.h b/views/controls/combobox/combobox.h index c50fadd..693eb4f 100644 --- a/views/controls/combobox/combobox.h +++ b/views/controls/combobox/combobox.h @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -19,7 +19,7 @@ namespace views { class NativeComboboxWrapper; -// A non-editable combo-box. +// A non-editable combo-box (aka a drop-down list) class Combobox : public View { public: // The combobox's class name. diff --git a/views/controls/combobox/native_combobox_gtk.cc b/views/controls/combobox/native_combobox_gtk.cc index 00552d8..01b9762 100644 --- a/views/controls/combobox/native_combobox_gtk.cc +++ b/views/controls/combobox/native_combobox_gtk.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -12,6 +12,7 @@ #include "base/utf_string_conversions.h" #include "ui/base/models/combobox_model.h" #include "views/controls/combobox/combobox.h" +#include "views/controls/combobox/native_combobox_views.h" #include "views/views_delegate.h" using ui::ComboboxModel; // TODO(beng): remove @@ -211,6 +212,8 @@ void NativeComboboxGtk::CallMenuMoveCurrent( // static NativeComboboxWrapper* NativeComboboxWrapper::CreateWrapper( Combobox* combobox) { + if (NativeComboboxViews::IsComboboxViewsEnabled()) + return new NativeComboboxViews(combobox); return new NativeComboboxGtk(combobox); } diff --git a/views/controls/combobox/native_combobox_views.cc b/views/controls/combobox/native_combobox_views.cc new file mode 100644 index 0000000..492b1a7 --- /dev/null +++ b/views/controls/combobox/native_combobox_views.cc @@ -0,0 +1,388 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "views/controls/combobox/native_combobox_views.h" + +#include <algorithm> + +#include "base/command_line.h" +#include "ui/base/models/combobox_model.h" +#include "ui/base/resource/resource_bundle.h" +#include "ui/gfx/canvas.h" +#include "ui/gfx/canvas_skia.h" +#include "ui/gfx/font.h" +#include "ui/gfx/path.h" +#include "views/background.h" +#include "views/border.h" +#include "views/controls/menu/menu_2.h" +#include "views/controls/combobox/combobox.h" + +#if defined(OS_LINUX) +#include "ui/gfx/gtk_util.h" +#endif + +namespace { + +// A global flag to switch the Combobox wrapper to NativeComboboxViews. +bool combobox_view_enabled = false; + +// Define the size of the insets +const int kTopInsetSize = 4; +const int kLeftInsetSize = 4; +const int kBottomInsetSize = 4; +const int kRightInsetSize = 4; + +// Limit how small a combobox can be. +const int kMinComboboxWidth = 148; + +// Size of the combobox arrow +const int kComboboxArrowSize = 9; +const int kComboboxArrowOffset = 7; +const int kComboboxArrowMargin = 12; + +// Color settings for text, border, backgrounds and cursor. +// These are tentative, and should be derived from theme, system +// settings and current settings. +const SkColor kSelectedTextColor = SK_ColorWHITE; +const SkColor kReadonlyTextColor = SK_ColorDKGRAY; +const SkColor kFocusedSelectionColor = SK_ColorBLUE; +const SkColor kUnfocusedSelectionColor = SK_ColorLTGRAY; +const SkColor kFocusedBorderColor = SK_ColorCYAN; +const SkColor kDefaultBorderColor = SK_ColorGRAY; +const SkColor kCursorColor = SK_ColorBLACK; + +// A switch to enable NativeTextfieldViews; +const char kEnableComboboxViewsSwitch[] = "enable-combobox-views"; +} // namespace + +namespace views { + +const char NativeComboboxViews::kViewClassName[] = + "views/NativeComboboxViews"; + +NativeComboboxViews::NativeComboboxViews(Combobox* parent) + : combobox_(parent), + text_border_(new ComboboxBorder()), + dropdown_open_(false), + selected_item_(-1), + content_width_(0), + content_height_(0) { + set_border(text_border_); +} + +NativeComboboxViews::~NativeComboboxViews() { +} + +//////////////////////////////////////////////////////////////////////////////// +// NativeComboboxViews, View overrides: + +bool NativeComboboxViews::OnMousePressed(const views::MouseEvent& e) { + combobox_->RequestFocus(); + if (e.IsLeftMouseButton()) { + UpdateFromModel(); + ShowDropDownMenu(); + } + + return true; +} + +bool NativeComboboxViews::OnMouseDragged(const views::MouseEvent& event) { + return true; +} + +bool NativeComboboxViews::OnKeyPressed(const views::KeyEvent& event) { + // OnKeyPressed/OnKeyReleased/OnFocus/OnBlur will never be invoked on + // NativeComboboxViews as it will never gain focus. + NOTREACHED(); + return false; +} + +bool NativeComboboxViews::OnKeyReleased(const views::KeyEvent& event) { + NOTREACHED(); + return false; +} + +void NativeComboboxViews::OnPaint(gfx::Canvas* canvas) { + text_border_->set_has_focus(combobox_->HasFocus()); + OnPaintBackground(canvas); + PaintText(canvas); + OnPaintBorder(canvas); +} + +void NativeComboboxViews::OnFocus() { + NOTREACHED(); +} + +void NativeComboboxViews::OnBlur() { + NOTREACHED(); +} + +///////////////////////////////////////////////////////////////// +// NativeComboboxViews, NativeComboboxWrapper overrides: + +void NativeComboboxViews::UpdateFromModel() { + int max_width = 0; + const gfx::Font &font = GetFont(); + + // retrieve the items from the model and add them to our own menu + dropdown_list_model_.reset(new ui::SimpleMenuModel(this)); + int num_items = combobox_->model()->GetItemCount(); + for (int i = 0; i < num_items; ++i) { + // TODO(saintlou): figure out RTL issues + string16 text = combobox_->model()->GetItemAt(i); + dropdown_list_model_->AddItem(i, text); + max_width = std::max(max_width, font.GetStringWidth(text)); + } + + dropdown_list_menu_.reset(new Menu2(dropdown_list_model_.get())); + content_width_ = max_width; + content_height_ = font.GetFontSize(); +} + +void NativeComboboxViews::UpdateSelectedItem() { + selected_item_ = combobox_->selected_item(); +} + +void NativeComboboxViews::UpdateEnabled() { + SetEnabled(combobox_->IsEnabled()); +} + +int NativeComboboxViews::GetSelectedItem() const { + return selected_item_; +} + +bool NativeComboboxViews::IsDropdownOpen() const { + return dropdown_open_; +} + +gfx::Size NativeComboboxViews::GetPreferredSize() { + if (content_width_ == 0) + UpdateFromModel(); + + // TODO(saintlou) the preferred size will drive the local bounds + // which in turn is used to set the minimum width for the dropdown + gfx::Insets insets = GetInsets(); + return gfx::Size(std::min(kMinComboboxWidth, + content_width_ + 2 * (insets.width())), + content_height_ + 2 * (insets.height())); +} + +View* NativeComboboxViews::GetView() { + return this; +} + +void NativeComboboxViews::SetFocus() { + text_border_->set_has_focus(true); +} + +gfx::NativeView NativeComboboxViews::GetTestingHandle() const { + NOTREACHED(); + return NULL; +} + +///////////////////////////////////////////////////////////////// +// NativeComboboxViews, ui::SimpleMenuModel::Delegate overrides: + +bool NativeComboboxViews::IsCommandIdChecked(int command_id) const { + return true; +} + +bool NativeComboboxViews::IsCommandIdEnabled(int command_id) const { + return true; +} + +bool NativeComboboxViews::GetAcceleratorForCommandId(int command_id, + ui::Accelerator* accelerator) { + return false; +} + +void NativeComboboxViews::ExecuteCommand(int command_id) { + if (command_id >= 0 && command_id < combobox_->model()->GetItemCount()) { + selected_item_ = command_id; + combobox_->SelectionChanged(); + } + else + NOTREACHED() << "unknown command: " << command_id; +} + + +// static +bool NativeComboboxViews::IsComboboxViewsEnabled() { +#if defined(TOUCH_UI) + return true; +#else + return combobox_view_enabled || + CommandLine::ForCurrentProcess()->HasSwitch( + kEnableComboboxViewsSwitch); +#endif +} + +// static +void NativeComboboxViews::SetEnableComboboxViews(bool enabled) { + combobox_view_enabled = enabled; +} + +///////////////////////////////////////////////////////////////// +// NativeComboboxViews private methods: + +const gfx::Font& NativeComboboxViews::GetFont() const { + ResourceBundle& rb = ResourceBundle::GetSharedInstance(); + #if 0 // For now the width calculation is off so stick with !defined(TOUCH_UI) + return rb.GetFont(ResourceBundle::LargeFont); + #else + return rb.GetFont(ResourceBundle::BaseFont); + #endif +} + +// tip_x and tip_y are the coordinates of the tip of an arrow head which is +// drawn as an isoscele triangle +// shift_x and shift_y are offset from tip_x and tip_y to specify the relative +// positions of the 2 equal angles (ie not the angle at the tip of the arrow) +// Note: the width of the base (the side opposite the tip) is 2 * shift_x +void NativeComboboxViews::DrawArrow(gfx::Canvas* canvas, + int tip_x, + int tip_y, + int shift_x, + int shift_y) const { + SkPaint paint; + paint.setStyle(SkPaint::kStrokeAndFill_Style); + paint.setColor(kCursorColor); + paint.setAntiAlias(true); + gfx::Path path; + path.incReserve(4); + path.moveTo(SkIntToScalar(tip_x), SkIntToScalar(tip_y)); + path.lineTo(SkIntToScalar(tip_x + shift_x), SkIntToScalar(tip_y + shift_y)); + path.lineTo(SkIntToScalar(tip_x - shift_x), SkIntToScalar(tip_y + shift_y)); + path.close(); + canvas->AsCanvasSkia()->drawPath(path, paint); +} + + +void NativeComboboxViews::PaintText(gfx::Canvas* canvas) { + gfx::Insets insets = GetInsets(); + + canvas->Save(); + canvas->ClipRectInt(insets.left(), insets.top(), + width() - insets.width(), height() - insets.height()); + + int x = insets.left(); + int y = insets.top(); + int text_height = height() - insets.height(); + SkColor text_color = kCursorColor; + + int index = GetSelectedItem(); + if (index < 0 || index > combobox_->model()->GetItemCount()) + index = 0; + string16 text = combobox_->model()->GetItemAt(index); + + const gfx::Font &font = GetFont(); + int width = font.GetStringWidth(text); + + canvas->DrawStringInt(text, font, text_color, x, y, width, text_height); + + // draw the double arrow + gfx::Rect lb = GetLocalBounds(); + DrawArrow(canvas, + lb.width() - (kComboboxArrowSize / 2) - kComboboxArrowOffset, + lb.height() / 2 - kComboboxArrowSize, + kComboboxArrowSize / 2, + kComboboxArrowSize - 2); + DrawArrow(canvas, + lb.width() - (kComboboxArrowSize / 2) - kComboboxArrowOffset, + lb.height() / 2 + kComboboxArrowSize, + -kComboboxArrowSize / 2, + -(kComboboxArrowSize - 2)); + + // draw the margin + canvas->DrawLineInt(kDefaultBorderColor, + lb.width() - kComboboxArrowSize - kComboboxArrowMargin, + kTopInsetSize, + lb.width() - kComboboxArrowSize - kComboboxArrowMargin, + lb.height() - kBottomInsetSize); + + canvas->Restore(); +} + +void NativeComboboxViews::ShowDropDownMenu() { + if (dropdown_list_model_.get()) { + gfx::Rect lb = GetLocalBounds(); + + // Both the menu position and the menu anchor type change if the UI layout + // is right-to-left. + gfx::Point menu_position(lb.origin()); + + if (base::i18n::IsRTL()) + menu_position.Offset(lb.width() - 1, 0); + + View::ConvertPointToScreen(this, &menu_position); + + if (menu_position.x() < 0) + menu_position.set_x(0); + + // Open the menu + dropdown_list_menu_.reset(new Menu2(dropdown_list_model_.get())); + dropdown_list_menu_->SetMinimumWidth(lb.width()); + dropdown_open_ = true; + dropdown_list_menu_->RunMenuAt(menu_position, Menu2::ALIGN_TOPLEFT); + dropdown_open_ = false; + + // Need to explicitly clear mouse handler so that events get sent + // properly after the menu finishes running. If we don't do this, then + // the first click to other parts of the UI is eaten. + SetMouseHandler(NULL); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// ComboboxBorder +// +/////////////////////////////////////////////////////////////////////////////// + +NativeComboboxViews::ComboboxBorder::ComboboxBorder() + : has_focus_(false), + insets_(kTopInsetSize, kLeftInsetSize, kBottomInsetSize, kRightInsetSize) +{ +} + +void NativeComboboxViews::ComboboxBorder::Paint( + const View& view, gfx::Canvas* canvas) const { + SkRect rect; + rect.set(SkIntToScalar(0), SkIntToScalar(0), + SkIntToScalar(view.width()), SkIntToScalar(view.height())); + SkScalar corners[8] = { + // top-left + SkIntToScalar(insets_.left()), + SkIntToScalar(insets_.top()), + // top-right + SkIntToScalar(insets_.right()), + SkIntToScalar(insets_.top()), + // bottom-right + SkIntToScalar(insets_.right()), + SkIntToScalar(insets_.bottom()), + // bottom-left + SkIntToScalar(insets_.left()), + SkIntToScalar(insets_.bottom()), + }; + SkPath path; + path.addRoundRect(rect, corners); + SkPaint paint; + paint.setStyle(SkPaint::kStroke_Style); + paint.setFlags(SkPaint::kAntiAlias_Flag); + paint.setColor(has_focus_ ? kFocusedBorderColor : kDefaultBorderColor); + paint.setStrokeWidth(SkIntToScalar(has_focus_ ? 2 : 1)); + canvas->AsCanvasSkia()->drawPath(path, paint); +} + +void NativeComboboxViews::ComboboxBorder::GetInsets(gfx::Insets* insets) const +{ + *insets = insets_; +} + +void NativeComboboxViews::ComboboxBorder::SetInsets(int top, int left, + int bottom, int right) { + insets_.Set(top, left, bottom, right); +} + +} // namespace views diff --git a/views/controls/combobox/native_combobox_views.h b/views/controls/combobox/native_combobox_views.h new file mode 100644 index 0000000..f74adc4 --- /dev/null +++ b/views/controls/combobox/native_combobox_views.h @@ -0,0 +1,136 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef VIEWS_CONTROLS_COMBOBOX_NATIVE_COMBOBOX_VIEWS_H_ +#define VIEWS_CONTROLS_COMBOBOX_NATIVE_COMBOBOX_VIEWS_H_ +#pragma once + +#include "ui/base/models/simple_menu_model.h" +#include "views/controls/combobox/native_combobox_wrapper.h" +#include "views/view.h" + +namespace gfx { +class Canvas; +class Font; +} + +namespace views { + +class KeyEvent; +class Menu2; + +// A views/skia only implementation of NativeComboboxWrapper. +// No platform specific code is used. +class NativeComboboxViews : public views::View, + public NativeComboboxWrapper, + public ui::SimpleMenuModel::Delegate { + public: + explicit NativeComboboxViews(Combobox* parent); + virtual ~NativeComboboxViews(); + + // views::View overrides: + virtual bool OnMousePressed(const views::MouseEvent& event) OVERRIDE; + virtual bool OnMouseDragged(const views::MouseEvent& event) OVERRIDE; + virtual bool OnKeyPressed(const views::KeyEvent& event) OVERRIDE; + virtual bool OnKeyReleased(const views::KeyEvent& event) OVERRIDE; + virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; + virtual void OnFocus() OVERRIDE; + virtual void OnBlur() OVERRIDE; + + // NativeComboboxWrapper overrides: + virtual void UpdateFromModel() OVERRIDE; + virtual void UpdateSelectedItem() OVERRIDE; + virtual void UpdateEnabled() OVERRIDE; + virtual int GetSelectedItem() const OVERRIDE; + virtual bool IsDropdownOpen() const OVERRIDE; + virtual gfx::Size GetPreferredSize() OVERRIDE; + virtual View* GetView() OVERRIDE; + virtual void SetFocus() OVERRIDE; + virtual gfx::NativeView GetTestingHandle() const OVERRIDE; + + // ui::SimpleMenuModel::Delegate overrides + virtual bool IsCommandIdChecked(int command_id) const OVERRIDE; + virtual bool IsCommandIdEnabled(int command_id) const OVERRIDE; + virtual bool GetAcceleratorForCommandId( + int command_id, + ui::Accelerator* accelerator) OVERRIDE; + virtual void ExecuteCommand(int command_id) OVERRIDE; + + // class name of internal + static const char kViewClassName[]; + + // Returns true when + // 1) built with GYP_DEFINES="touchui=1" + // 2) enabled by SetEnableComboboxViews(true) + // 3) enabled by the command line flag "--enable-combobox-view") + static bool IsComboboxViewsEnabled(); + // Enable/Disable NativeComboboxViews implementation for Combobox. + static void SetEnableComboboxViews(bool enabled); + + private: + + // A Border class to draw focus border for the text field. + // TODO(saintlou): refactor with NativeTextfieldViews + class ComboboxBorder : public Border { + public: + ComboboxBorder(); + + // Border implementation. + virtual void Paint(const View& view, gfx::Canvas* canvas) const OVERRIDE; + virtual void GetInsets(gfx::Insets* insets) const OVERRIDE; + + // Sets the insets of the border. + void SetInsets(int top, int left, int bottom, int right); + + // Sets the focus state. + void set_has_focus(bool has_focus) { + has_focus_ = has_focus; + } + + private: + bool has_focus_; + gfx::Insets insets_; + + DISALLOW_COPY_AND_ASSIGN(ComboboxBorder); + }; + + // Returns the Combobox's font. + const gfx::Font& GetFont() const; + + // Draw an arrow + void DrawArrow(gfx::Canvas* canvas, + int tip_x, int tip_y, int shift_x, int shift_y) const; + + // Draw the selected value of the drop down list + void PaintText(gfx::Canvas* canvas); + + // Show the drop down list + void ShowDropDownMenu(); + + // The parent combobox, the owner of this object. + Combobox* combobox_; + + // The reference to the border class. The object is owned by View::border_. + ComboboxBorder* text_border_; + + // Context menu and its content list for the combobox. + scoped_ptr<ui::SimpleMenuModel> dropdown_list_model_; + scoped_ptr<Menu2> dropdown_list_menu_; + + // Is the drop down list showing + bool dropdown_open_; + + // Index in the model of the selected item: -1 => none + int selected_item_; + + // The maximum dimensions of the content in the dropdown + int content_width_; + int content_height_; + + DISALLOW_COPY_AND_ASSIGN(NativeComboboxViews); +}; + +} // namespace views + +#endif // VIEWS_CONTROLS_COMBOBOX_NATIVE_COMBOBOX_VIEWS_H_ diff --git a/views/views.gyp b/views/views.gyp index dfa848d..64cffe1 100644 --- a/views/views.gyp +++ b/views/views.gyp @@ -111,6 +111,8 @@ 'controls/combobox/combobox.h', 'controls/combobox/native_combobox_gtk.cc', 'controls/combobox/native_combobox_gtk.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', |