summaryrefslogtreecommitdiffstats
path: root/views
diff options
context:
space:
mode:
authorsaintlou@chromium.org <saintlou@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-04-29 22:59:15 +0000
committersaintlou@chromium.org <saintlou@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-04-29 22:59:15 +0000
commitabe390e388afe636b55096036785ab3801d95029 (patch)
tree626beb3df4db2d8dc8dff10843edcad42b396c4e /views
parent09efa3d1622cf315dee71154904a52407470c810 (diff)
downloadchromium_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.h4
-rw-r--r--views/controls/combobox/native_combobox_gtk.cc5
-rw-r--r--views/controls/combobox/native_combobox_views.cc388
-rw-r--r--views/controls/combobox/native_combobox_views.h136
-rw-r--r--views/views.gyp2
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',