summaryrefslogtreecommitdiffstats
path: root/ui/views
diff options
context:
space:
mode:
authortfarina@chromium.org <tfarina@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-11-29 20:25:29 +0000
committertfarina@chromium.org <tfarina@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-11-29 20:25:29 +0000
commit8fc1b372c1f74f06296a0eebee0d40dbd623332f (patch)
treedde0e7d9c71dd2e461c0860e9ebe61e4a9ea385f /ui/views
parentde797cf7fd9f68c59b636a26f1ade2e01357d8e9 (diff)
downloadchromium_src-8fc1b372c1f74f06296a0eebee0d40dbd623332f.zip
chromium_src-8fc1b372c1f74f06296a0eebee0d40dbd623332f.tar.gz
chromium_src-8fc1b372c1f74f06296a0eebee0d40dbd623332f.tar.bz2
views: Move the remaining files to ui/views/controls/.
BUG=104039 R=ben@chromium.org TBR=stevenjb@chromium.org Review URL: http://codereview.chromium.org/8687031 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@112014 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ui/views')
-rw-r--r--ui/views/controls/combobox/native_combobox_gtk.h2
-rw-r--r--ui/views/controls/combobox/native_combobox_views.cc2
-rw-r--r--ui/views/controls/combobox/native_combobox_win.h2
-rw-r--r--ui/views/controls/focusable_border.cc73
-rw-r--r--ui/views/controls/focusable_border.h47
-rw-r--r--ui/views/controls/image_view.cc174
-rw-r--r--ui/views/controls/image_view.h112
-rw-r--r--ui/views/controls/message_box_view.cc227
-rw-r--r--ui/views/controls/message_box_view.h99
-rw-r--r--ui/views/controls/native_control.cc392
-rw-r--r--ui/views/controls/native_control.h126
-rw-r--r--ui/views/controls/native_control_gtk.cc93
-rw-r--r--ui/views/controls/native_control_gtk.h52
-rw-r--r--ui/views/controls/native_control_win.cc226
-rw-r--r--ui/views/controls/native_control_win.h101
-rw-r--r--ui/views/controls/progress_bar.cc319
-rw-r--r--ui/views/controls/progress_bar.h65
-rw-r--r--ui/views/controls/progress_bar_unittest.cc35
-rw-r--r--ui/views/controls/resize_area.cc94
-rw-r--r--ui/views/controls/resize_area.h54
-rw-r--r--ui/views/controls/resize_area_delegate.h29
-rw-r--r--ui/views/controls/scroll_view.cc501
-rw-r--r--ui/views/controls/scroll_view.h207
-rw-r--r--ui/views/controls/scrollbar/base_scroll_bar.cc2
-rw-r--r--ui/views/controls/scrollbar/bitmap_scroll_bar.cc2
-rw-r--r--ui/views/controls/scrollbar/native_scroll_bar_gtk.h2
-rw-r--r--ui/views/controls/scrollbar/native_scroll_bar_views.cc2
-rw-r--r--ui/views/controls/scrollbar/native_scroll_bar_win.h2
-rw-r--r--ui/views/controls/separator.cc47
-rw-r--r--ui/views/controls/separator.h38
-rw-r--r--ui/views/controls/single_split_view.cc263
-rw-r--r--ui/views/controls/single_split_view.h134
-rw-r--r--ui/views/controls/single_split_view_listener.h29
-rw-r--r--ui/views/controls/single_split_view_unittest.cc180
-rw-r--r--ui/views/controls/tabbed_pane/native_tabbed_pane_gtk.h2
-rw-r--r--ui/views/controls/tabbed_pane/native_tabbed_pane_win.h2
-rw-r--r--ui/views/controls/table/native_table_gtk.h2
-rw-r--r--ui/views/controls/table/native_table_win.h2
-rw-r--r--ui/views/controls/table/table_view.h2
-rw-r--r--ui/views/controls/textfield/native_textfield_gtk.h2
-rw-r--r--ui/views/controls/textfield/native_textfield_views.cc2
-rw-r--r--ui/views/controls/throbber.cc178
-rw-r--r--ui/views/controls/throbber.h128
-rw-r--r--ui/views/controls/tree/tree_view.h2
-rw-r--r--ui/views/examples/double_split_view_example.cc2
-rw-r--r--ui/views/examples/message_box_example.cc2
-rw-r--r--ui/views/examples/progress_bar_example.cc2
-rw-r--r--ui/views/examples/scroll_view_example.h2
-rw-r--r--ui/views/examples/single_split_view_example.cc2
-rw-r--r--ui/views/examples/single_split_view_example.h2
-rw-r--r--ui/views/examples/throbber_example.cc2
-rw-r--r--ui/views/focus/focus_traversal_unittest.cc2
-rw-r--r--ui/views/widget/native_widget_win.cc2
53 files changed, 4048 insertions, 25 deletions
diff --git a/ui/views/controls/combobox/native_combobox_gtk.h b/ui/views/controls/combobox/native_combobox_gtk.h
index 9466dcd..7b08410 100644
--- a/ui/views/controls/combobox/native_combobox_gtk.h
+++ b/ui/views/controls/combobox/native_combobox_gtk.h
@@ -8,7 +8,7 @@
#include "ui/base/gtk/gtk_signal.h"
#include "ui/views/controls/combobox/native_combobox_wrapper.h"
-#include "views/controls/native_control_gtk.h"
+#include "ui/views/controls/native_control_gtk.h"
namespace views {
diff --git a/ui/views/controls/combobox/native_combobox_views.cc b/ui/views/controls/combobox/native_combobox_views.cc
index 4c3bc63..4311b8f 100644
--- a/ui/views/controls/combobox/native_combobox_views.cc
+++ b/ui/views/controls/combobox/native_combobox_views.cc
@@ -16,13 +16,13 @@
#include "ui/gfx/font.h"
#include "ui/gfx/path.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/widget/root_view.h"
#include "ui/views/widget/widget.h"
#include "views/background.h"
#include "views/border.h"
-#include "views/controls/focusable_border.h"
#if defined(OS_LINUX)
#include "ui/gfx/gtk_util.h"
diff --git a/ui/views/controls/combobox/native_combobox_win.h b/ui/views/controls/combobox/native_combobox_win.h
index ee22cee..e71e3fe 100644
--- a/ui/views/controls/combobox/native_combobox_win.h
+++ b/ui/views/controls/combobox/native_combobox_win.h
@@ -7,7 +7,7 @@
#pragma once
#include "ui/views/controls/combobox/native_combobox_wrapper.h"
-#include "views/controls/native_control_win.h"
+#include "ui/views/controls/native_control_win.h"
namespace views {
diff --git a/ui/views/controls/focusable_border.cc b/ui/views/controls/focusable_border.cc
new file mode 100644
index 0000000..c90ee67
--- /dev/null
+++ b/ui/views/controls/focusable_border.cc
@@ -0,0 +1,73 @@
+// 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 "ui/views/controls/focusable_border.h"
+
+#include "ui/gfx/canvas.h"
+#include "ui/gfx/canvas_skia.h"
+#include "ui/gfx/insets.h"
+
+namespace {
+
+// Define the size of the insets
+const int kTopInsetSize = 4;
+const int kLeftInsetSize = 4;
+const int kBottomInsetSize = 4;
+const int kRightInsetSize = 4;
+
+// Color settings for border.
+// These are tentative, and should be derived from theme, system
+// settings and current settings.
+const SkColor kFocusedBorderColor = SK_ColorCYAN;
+const SkColor kDefaultBorderColor = SK_ColorGRAY;
+
+} // namespace
+
+namespace views {
+
+FocusableBorder::FocusableBorder()
+ : has_focus_(false),
+ insets_(kTopInsetSize, kLeftInsetSize,
+ kBottomInsetSize, kRightInsetSize) {
+}
+
+void FocusableBorder::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);
+ // TODO(oshima): Copy what WebKit does for focused border.
+ paint.setColor(has_focus_ ? kFocusedBorderColor : kDefaultBorderColor);
+ paint.setStrokeWidth(SkIntToScalar(has_focus_ ? 2 : 1));
+
+ canvas->GetSkCanvas()->drawPath(path, paint);
+}
+
+void FocusableBorder::GetInsets(gfx::Insets* insets) const {
+ *insets = insets_;
+}
+
+void FocusableBorder::SetInsets(int top, int left, int bottom, int right) {
+ insets_.Set(top, left, bottom, right);
+}
+
+} // namespace views
diff --git a/ui/views/controls/focusable_border.h b/ui/views/controls/focusable_border.h
new file mode 100644
index 0000000..098809b
--- /dev/null
+++ b/ui/views/controls/focusable_border.h
@@ -0,0 +1,47 @@
+// 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 UI_VIEWS_CONTROLS_FOCUSABLE_BORDER_H_
+#define UI_VIEWS_CONTROLS_FOCUSABLE_BORDER_H_
+#pragma once
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "views/border.h"
+#include "views/view.h"
+
+namespace gfx {
+class Canvas;
+class Insets;
+}
+
+namespace views {
+
+// A Border class to draw a focused border around a field (e.g textfield).
+class FocusableBorder : public Border {
+ public:
+ FocusableBorder();
+
+ // 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;
+ }
+
+ // Overridden from Border:
+ virtual void Paint(const View& view, gfx::Canvas* canvas) const OVERRIDE;
+ virtual void GetInsets(gfx::Insets* insets) const OVERRIDE;
+
+ private:
+ bool has_focus_;
+ gfx::Insets insets_;
+
+ DISALLOW_COPY_AND_ASSIGN(FocusableBorder);
+};
+
+} // namespace views
+
+#endif // UI_VIEWS_CONTROLS_FOCUSABLE_BORDER_H_
diff --git a/ui/views/controls/image_view.cc b/ui/views/controls/image_view.cc
new file mode 100644
index 0000000..92c1513
--- /dev/null
+++ b/ui/views/controls/image_view.cc
@@ -0,0 +1,174 @@
+// 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 "ui/views/controls/image_view.h"
+
+#include "base/logging.h"
+#include "base/utf_string_conversions.h"
+#include "third_party/skia/include/core/SkPaint.h"
+#include "ui/base/accessibility/accessible_view_state.h"
+#include "ui/gfx/canvas.h"
+#include "ui/gfx/insets.h"
+
+namespace views {
+
+ImageView::ImageView()
+ : image_size_set_(false),
+ horiz_alignment_(CENTER),
+ vert_alignment_(CENTER) {
+}
+
+ImageView::~ImageView() {
+}
+
+void ImageView::SetImage(const SkBitmap& bm) {
+ image_ = bm;
+ PreferredSizeChanged();
+ SchedulePaint();
+}
+
+void ImageView::SetImage(const SkBitmap* bm) {
+ if (bm) {
+ SetImage(*bm);
+ } else {
+ SkBitmap t;
+ SetImage(t);
+ }
+}
+
+const SkBitmap& ImageView::GetImage() {
+ return image_;
+}
+
+void ImageView::SetImageSize(const gfx::Size& image_size) {
+ image_size_set_ = true;
+ image_size_ = image_size;
+ PreferredSizeChanged();
+}
+
+bool ImageView::GetImageSize(gfx::Size* image_size) {
+ DCHECK(image_size);
+ if (image_size_set_)
+ *image_size = image_size_;
+ return image_size_set_;
+}
+
+gfx::Rect ImageView::GetImageBounds() const {
+ gfx::Size image_size(image_size_set_ ?
+ image_size_ : gfx::Size(image_.width(), image_.height()));
+ return gfx::Rect(ComputeImageOrigin(image_size), image_size);
+}
+
+void ImageView::ResetImageSize() {
+ image_size_set_ = false;
+}
+
+gfx::Size ImageView::GetPreferredSize() {
+ gfx::Insets insets = GetInsets();
+ if (image_size_set_) {
+ gfx::Size image_size;
+ GetImageSize(&image_size);
+ image_size.Enlarge(insets.width(), insets.height());
+ return image_size;
+ }
+ return gfx::Size(image_.width() + insets.width(),
+ image_.height() + insets.height());
+}
+
+gfx::Point ImageView::ComputeImageOrigin(const gfx::Size& image_size) const {
+ gfx::Insets insets = GetInsets();
+
+ int x;
+ // In order to properly handle alignment of images in RTL locales, we need
+ // to flip the meaning of trailing and leading. For example, if the
+ // horizontal alignment is set to trailing, then we'll use left alignment for
+ // the image instead of right alignment if the UI layout is RTL.
+ Alignment actual_horiz_alignment = horiz_alignment_;
+ if (base::i18n::IsRTL() && (horiz_alignment_ != CENTER))
+ actual_horiz_alignment = (horiz_alignment_ == LEADING) ? TRAILING : LEADING;
+ switch (actual_horiz_alignment) {
+ case LEADING: x = insets.left(); break;
+ case TRAILING: x = width() - insets.right() - image_size.width(); break;
+ case CENTER: x = (width() - image_size.width()) / 2; break;
+ default: NOTREACHED(); x = 0; break;
+ }
+
+ int y;
+ switch (vert_alignment_) {
+ case LEADING: y = insets.top(); break;
+ case TRAILING: y = height() - insets.bottom() - image_size.height(); break;
+ case CENTER: y = (height() - image_size.height()) / 2; break;
+ default: NOTREACHED(); y = 0; break;
+ }
+
+ return gfx::Point(x, y);
+}
+
+void ImageView::OnPaint(gfx::Canvas* canvas) {
+ View::OnPaint(canvas);
+
+ if (image_.empty())
+ return;
+
+ gfx::Rect image_bounds(GetImageBounds());
+ if (image_bounds.IsEmpty())
+ return;
+
+ if (image_bounds.size() != gfx::Size(image_.width(), image_.height())) {
+ // Resize case
+ image_.buildMipMap(false);
+ SkPaint paint;
+ paint.setFilterBitmap(true);
+ canvas->DrawBitmapInt(image_, 0, 0, image_.width(), image_.height(),
+ image_bounds.x(), image_bounds.y(), image_bounds.width(),
+ image_bounds.height(), true, paint);
+ } else {
+ canvas->DrawBitmapInt(image_, image_bounds.x(), image_bounds.y());
+ }
+}
+
+void ImageView::GetAccessibleState(ui::AccessibleViewState* state) {
+ state->role = ui::AccessibilityTypes::ROLE_GRAPHIC;
+ state->name = tooltip_text_;
+}
+
+void ImageView::SetHorizontalAlignment(Alignment ha) {
+ if (ha != horiz_alignment_) {
+ horiz_alignment_ = ha;
+ SchedulePaint();
+ }
+}
+
+ImageView::Alignment ImageView::GetHorizontalAlignment() const {
+ return horiz_alignment_;
+}
+
+void ImageView::SetVerticalAlignment(Alignment va) {
+ if (va != vert_alignment_) {
+ vert_alignment_ = va;
+ SchedulePaint();
+ }
+}
+
+ImageView::Alignment ImageView::GetVerticalAlignment() const {
+ return vert_alignment_;
+}
+
+void ImageView::SetTooltipText(const string16& tooltip) {
+ tooltip_text_ = tooltip;
+}
+
+string16 ImageView::GetTooltipText() const {
+ return tooltip_text_;
+}
+
+bool ImageView::GetTooltipText(const gfx::Point& p, string16* tooltip) const {
+ if (tooltip_text_.empty())
+ return false;
+
+ *tooltip = GetTooltipText();
+ return true;
+}
+
+} // namespace views
diff --git a/ui/views/controls/image_view.h b/ui/views/controls/image_view.h
new file mode 100644
index 0000000..1149c8d
--- /dev/null
+++ b/ui/views/controls/image_view.h
@@ -0,0 +1,112 @@
+// 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 UI_VIEWS_CONTROLS_IMAGE_VIEW_H_
+#define UI_VIEWS_CONTROLS_IMAGE_VIEW_H_
+#pragma once
+
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "views/view.h"
+
+namespace gfx {
+class Canvas;
+}
+
+namespace views {
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// ImageView class.
+//
+// An ImageView can display an image from an SkBitmap. If a size is provided,
+// the ImageView will resize the provided image to fit if it is too big or will
+// center the image if smaller. Otherwise, the preferred size matches the
+// provided image size.
+//
+/////////////////////////////////////////////////////////////////////////////
+class VIEWS_EXPORT ImageView : public View {
+ public:
+ enum Alignment {
+ LEADING = 0,
+ CENTER,
+ TRAILING
+ };
+
+ ImageView();
+ virtual ~ImageView();
+
+ // Set the bitmap that should be displayed.
+ void SetImage(const SkBitmap& bm);
+
+ // Set the bitmap that should be displayed from a pointer. Reset the image
+ // if the pointer is NULL. The pointer contents is copied in the receiver's
+ // bitmap.
+ void SetImage(const SkBitmap* bm);
+
+ // Returns the bitmap currently displayed or NULL of none is currently set.
+ // The returned bitmap is still owned by the ImageView.
+ const SkBitmap& GetImage();
+
+ // Set the desired image size for the receiving ImageView.
+ void SetImageSize(const gfx::Size& image_size);
+
+ // Return the preferred size for the receiving view. Returns false if the
+ // preferred size is not defined, which means that the view uses the image
+ // size.
+ bool GetImageSize(gfx::Size* image_size);
+
+ // Returns the actual bounds of the visible image inside the view.
+ gfx::Rect GetImageBounds() const;
+
+ // Reset the image size to the current image dimensions.
+ void ResetImageSize();
+
+ // Set / Get the horizontal alignment.
+ void SetHorizontalAlignment(Alignment ha);
+ Alignment GetHorizontalAlignment() const;
+
+ // Set / Get the vertical alignment.
+ void SetVerticalAlignment(Alignment va);
+ Alignment GetVerticalAlignment() const;
+
+ // Set / Get the tooltip text.
+ void SetTooltipText(const string16& tooltip);
+ string16 GetTooltipText() const;
+
+ // Overriden from View
+ virtual gfx::Size GetPreferredSize() OVERRIDE;
+ virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
+ virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE;
+ virtual bool GetTooltipText(const gfx::Point& p,
+ string16* tooltip) const OVERRIDE;
+
+ private:
+ // Compute the image origin given the desired size and the receiver alignment
+ // properties.
+ gfx::Point ComputeImageOrigin(const gfx::Size& image_size) const;
+
+ // Whether the image size is set.
+ bool image_size_set_;
+
+ // The actual image size.
+ gfx::Size image_size_;
+
+ // The underlying bitmap.
+ SkBitmap image_;
+
+ // Horizontal alignment.
+ Alignment horiz_alignment_;
+
+ // Vertical alignment.
+ Alignment vert_alignment_;
+
+ // The current tooltip text.
+ string16 tooltip_text_;
+
+ DISALLOW_COPY_AND_ASSIGN(ImageView);
+};
+
+} // namespace views
+
+#endif // UI_VIEWS_CONTROLS_IMAGE_VIEW_H_
diff --git a/ui/views/controls/message_box_view.cc b/ui/views/controls/message_box_view.cc
new file mode 100644
index 0000000..b9564fe
--- /dev/null
+++ b/ui/views/controls/message_box_view.cc
@@ -0,0 +1,227 @@
+// 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 "ui/views/controls/message_box_view.h"
+
+#include "base/i18n/rtl.h"
+#include "base/message_loop.h"
+#include "base/utf_string_conversions.h"
+#include "ui/base/accessibility/accessible_view_state.h"
+#include "ui/base/clipboard/clipboard.h"
+#include "ui/base/clipboard/scoped_clipboard_writer.h"
+#include "ui/base/message_box_flags.h"
+#include "ui/views/controls/button/checkbox.h"
+#include "ui/views/controls/image_view.h"
+#include "ui/views/controls/label.h"
+#include "ui/views/controls/textfield/textfield.h"
+#include "ui/views/layout/grid_layout.h"
+#include "ui/views/layout/layout_constants.h"
+#include "ui/views/widget/widget.h"
+#include "ui/views/window/client_view.h"
+#include "views/views_delegate.h"
+
+const int kDefaultMessageWidth = 320;
+
+namespace views {
+
+///////////////////////////////////////////////////////////////////////////////
+// MessageBoxView, public:
+
+MessageBoxView::MessageBoxView(int dialog_flags,
+ const string16& message,
+ const string16& default_prompt,
+ int message_width)
+ : message_label_(new Label(message)),
+ prompt_field_(NULL),
+ icon_(NULL),
+ checkbox_(NULL),
+ message_width_(message_width) {
+ Init(dialog_flags, default_prompt);
+}
+
+MessageBoxView::MessageBoxView(int dialog_flags,
+ const string16& message,
+ const string16& default_prompt)
+ : message_label_(new Label(message)),
+ prompt_field_(NULL),
+ icon_(NULL),
+ checkbox_(NULL),
+ message_width_(kDefaultMessageWidth) {
+ Init(dialog_flags, default_prompt);
+}
+
+MessageBoxView::~MessageBoxView() {}
+
+string16 MessageBoxView::GetInputText() {
+ return prompt_field_ ? prompt_field_->text() : string16();
+}
+
+bool MessageBoxView::IsCheckBoxSelected() {
+ return checkbox_ ? checkbox_->checked() : false;
+}
+
+void MessageBoxView::SetIcon(const SkBitmap& icon) {
+ if (!icon_)
+ icon_ = new ImageView();
+ icon_->SetImage(icon);
+ icon_->SetBounds(0, 0, icon.width(), icon.height());
+ ResetLayoutManager();
+}
+
+void MessageBoxView::SetCheckBoxLabel(const string16& label) {
+ if (!checkbox_)
+ checkbox_ = new Checkbox(label);
+ else
+ checkbox_->SetText(label);
+ ResetLayoutManager();
+}
+
+void MessageBoxView::SetCheckBoxSelected(bool selected) {
+ if (!checkbox_)
+ return;
+ checkbox_->SetChecked(selected);
+}
+
+void MessageBoxView::GetAccessibleState(ui::AccessibleViewState* state) {
+ state->role = ui::AccessibilityTypes::ROLE_ALERT;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// MessageBoxView, View overrides:
+
+void MessageBoxView::ViewHierarchyChanged(bool is_add,
+ View* parent,
+ View* child) {
+ if (child == this && is_add) {
+ if (prompt_field_)
+ prompt_field_->SelectAll();
+
+ GetWidget()->NotifyAccessibilityEvent(
+ this, ui::AccessibilityTypes::EVENT_ALERT, true);
+ }
+}
+
+bool MessageBoxView::AcceleratorPressed(const ui::Accelerator& accelerator) {
+ // We only accepts Ctrl-C.
+ DCHECK(accelerator.key_code() == 'C' && accelerator.IsCtrlDown());
+
+ // We must not intercept Ctrl-C when we have a text box and it's focused.
+ if (prompt_field_ && prompt_field_->HasFocus())
+ return false;
+
+ if (!ViewsDelegate::views_delegate)
+ return false;
+
+ ui::Clipboard* clipboard = ViewsDelegate::views_delegate->GetClipboard();
+ if (!clipboard)
+ return false;
+
+ ui::ScopedClipboardWriter scw(clipboard);
+ scw.WriteText(message_label_->GetText());
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// MessageBoxView, private:
+
+void MessageBoxView::Init(int dialog_flags,
+ const string16& default_prompt) {
+ message_label_->SetMultiLine(true);
+ message_label_->SetAllowCharacterBreak(true);
+ if (dialog_flags & ui::MessageBoxFlags::kAutoDetectAlignment) {
+ // Determine the alignment and directionality based on the first character
+ // with strong directionality.
+ base::i18n::TextDirection direction =
+ base::i18n::GetFirstStrongCharacterDirection(
+ message_label_->GetText());
+ Label::Alignment alignment;
+ if (direction == base::i18n::RIGHT_TO_LEFT)
+ alignment = Label::ALIGN_RIGHT;
+ else
+ alignment = Label::ALIGN_LEFT;
+ // In addition, we should set the RTL alignment mode as
+ // AUTO_DETECT_ALIGNMENT so that the alignment will not be flipped around
+ // in RTL locales.
+ message_label_->set_rtl_alignment_mode(Label::AUTO_DETECT_ALIGNMENT);
+ message_label_->SetHorizontalAlignment(alignment);
+ } else {
+ message_label_->SetHorizontalAlignment(Label::ALIGN_LEFT);
+ }
+
+ if (dialog_flags & ui::MessageBoxFlags::kFlagHasPromptField) {
+ prompt_field_ = new Textfield;
+ prompt_field_->SetText(default_prompt);
+ }
+
+ ResetLayoutManager();
+}
+
+void MessageBoxView::ResetLayoutManager() {
+ // Initialize the Grid Layout Manager used for this dialog box.
+ GridLayout* layout = GridLayout::CreatePanel(this);
+ SetLayoutManager(layout);
+
+ gfx::Size icon_size;
+ if (icon_)
+ icon_size = icon_->GetPreferredSize();
+
+ // Add the column set for the message displayed at the top of the dialog box.
+ // And an icon, if one has been set.
+ const int message_column_view_set_id = 0;
+ ColumnSet* column_set = layout->AddColumnSet(message_column_view_set_id);
+ if (icon_) {
+ column_set->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0,
+ GridLayout::FIXED, icon_size.width(),
+ icon_size.height());
+ column_set->AddPaddingColumn(0, kUnrelatedControlHorizontalSpacing);
+ }
+ column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1,
+ GridLayout::FIXED, message_width_, 0);
+
+ // Column set for prompt Textfield, if one has been set.
+ const int textfield_column_view_set_id = 1;
+ if (prompt_field_) {
+ column_set = layout->AddColumnSet(textfield_column_view_set_id);
+ if (icon_) {
+ column_set->AddPaddingColumn(
+ 0, icon_size.width() + kUnrelatedControlHorizontalSpacing);
+ }
+ column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1,
+ GridLayout::USE_PREF, 0, 0);
+ }
+
+ // Column set for checkbox, if one has been set.
+ const int checkbox_column_view_set_id = 2;
+ if (checkbox_) {
+ column_set = layout->AddColumnSet(checkbox_column_view_set_id);
+ if (icon_) {
+ column_set->AddPaddingColumn(
+ 0, icon_size.width() + kUnrelatedControlHorizontalSpacing);
+ }
+ column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1,
+ GridLayout::USE_PREF, 0, 0);
+ }
+
+ layout->StartRow(0, message_column_view_set_id);
+ if (icon_)
+ layout->AddView(icon_);
+
+ layout->AddView(message_label_);
+
+ if (prompt_field_) {
+ layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
+ layout->StartRow(0, textfield_column_view_set_id);
+ layout->AddView(prompt_field_);
+ }
+
+ if (checkbox_) {
+ layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
+ layout->StartRow(0, checkbox_column_view_set_id);
+ layout->AddView(checkbox_);
+ }
+
+ layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
+}
+
+} // namespace views
diff --git a/ui/views/controls/message_box_view.h b/ui/views/controls/message_box_view.h
new file mode 100644
index 0000000..64e1f27
--- /dev/null
+++ b/ui/views/controls/message_box_view.h
@@ -0,0 +1,99 @@
+// 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 UI_VIEWS_CONTROLS_MESSAGE_BOX_VIEW_H_
+#define UI_VIEWS_CONTROLS_MESSAGE_BOX_VIEW_H_
+#pragma once
+
+#include <string>
+
+#include "base/string16.h"
+#include "views/view.h"
+
+namespace views {
+
+class Checkbox;
+class ImageView;
+class Label;
+class Textfield;
+
+// This class displays the contents of a message box. It is intended for use
+// within a constrained window, and has options for a message, prompt, OK
+// and Cancel buttons.
+class VIEWS_EXPORT MessageBoxView : public View {
+ public:
+ MessageBoxView(int dialog_flags,
+ const string16& message,
+ const string16& default_prompt,
+ int message_width);
+
+ MessageBoxView(int dialog_flags,
+ const string16& message,
+ const string16& default_prompt);
+
+ virtual ~MessageBoxView();
+
+ // Returns the text box.
+ views::Textfield* text_box() { return prompt_field_; }
+
+ // Returns user entered data in the prompt field.
+ string16 GetInputText();
+
+ // Returns true if a checkbox is selected, false otherwise. (And false if
+ // the message box has no checkbox.)
+ bool IsCheckBoxSelected();
+
+ // Adds |icon| to the upper left of the message box or replaces the current
+ // icon. To start out, the message box has no icon.
+ void SetIcon(const SkBitmap& icon);
+
+ // Adds a checkbox with the specified label to the message box if this is the
+ // first call. Otherwise, it changes the label of the current checkbox. To
+ // start, the message box has no checkbox until this function is called.
+ void SetCheckBoxLabel(const string16& label);
+
+ // Sets the state of the check-box.
+ void SetCheckBoxSelected(bool selected);
+
+ // View:
+ virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE;
+
+ protected:
+ // View:
+ virtual void ViewHierarchyChanged(bool is_add,
+ views::View* parent,
+ views::View* child) OVERRIDE;
+ // Handles Ctrl-C and writes the message in the system clipboard.
+ virtual bool AcceleratorPressed(const ui::Accelerator& accelerator) OVERRIDE;
+
+ private:
+ // Sets up the layout manager and initializes the prompt field. This should
+ // only be called once, from the constructor.
+ void Init(int dialog_flags, const string16& default_prompt);
+
+ // Sets up the layout manager based on currently initialized views. Should be
+ // called when a view is initialized or changed.
+ void ResetLayoutManager();
+
+ // Message for the message box.
+ Label* message_label_;
+
+ // Input text field for the message box.
+ Textfield* prompt_field_;
+
+ // Icon displayed in the upper left corner of the message box.
+ ImageView* icon_;
+
+ // Checkbox for the message box.
+ Checkbox* checkbox_;
+
+ // Maximum width of the message label.
+ int message_width_;
+
+ DISALLOW_COPY_AND_ASSIGN(MessageBoxView);
+};
+
+} // namespace views
+
+#endif // UI_VIEWS_CONTROLS_MESSAGE_BOX_VIEW_H_
diff --git a/ui/views/controls/native_control.cc b/ui/views/controls/native_control.cc
new file mode 100644
index 0000000..7edbda6
--- /dev/null
+++ b/ui/views/controls/native_control.cc
@@ -0,0 +1,392 @@
+// 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 "ui/views/controls/native_control.h"
+
+#include <atlbase.h>
+#include <atlapp.h>
+#include <atlcrack.h>
+#include <atlframe.h>
+#include <atlmisc.h>
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "ui/base/accessibility/accessibility_types.h"
+#include "ui/base/keycodes/keyboard_code_conversion_win.h"
+#include "ui/base/keycodes/keyboard_codes.h"
+#include "ui/base/l10n/l10n_util_win.h"
+#include "ui/base/view_prop.h"
+#include "ui/base/win/hwnd_util.h"
+#include "ui/views/controls/native/native_view_host.h"
+#include "ui/views/focus/focus_manager.h"
+#include "ui/views/widget/widget.h"
+#include "views/background.h"
+#include "views/border.h"
+
+using ui::ViewProp;
+
+namespace views {
+
+// Maps to the NativeControl.
+static const char* const kNativeControlKey = "__NATIVE_CONTROL__";
+
+class NativeControlContainer : public CWindowImpl<NativeControlContainer,
+ CWindow,
+ CWinTraits<WS_CHILD | WS_CLIPSIBLINGS |
+ WS_CLIPCHILDREN>> {
+ public:
+ explicit NativeControlContainer(NativeControl* parent)
+ : parent_(parent),
+ control_(NULL),
+ original_handler_(NULL) {
+ }
+
+ void Init() {
+ Create(parent_->GetWidget()->GetNativeView());
+ ::ShowWindow(m_hWnd, SW_SHOW);
+ }
+
+ virtual ~NativeControlContainer() {
+ }
+
+ // NOTE: If you add a new message, be sure and verify parent_ is valid before
+ // calling into parent_.
+ DECLARE_FRAME_WND_CLASS(L"ChromeViewsNativeControlContainer", NULL);
+ BEGIN_MSG_MAP(NativeControlContainer);
+ MSG_WM_CREATE(OnCreate);
+ MSG_WM_ERASEBKGND(OnEraseBkgnd);
+ MSG_WM_PAINT(OnPaint);
+ MSG_WM_SIZE(OnSize);
+ MSG_WM_NOTIFY(OnNotify);
+ MSG_WM_COMMAND(OnCommand);
+ MSG_WM_DESTROY(OnDestroy);
+ MSG_WM_CONTEXTMENU(OnContextMenu);
+ MSG_WM_CTLCOLORBTN(OnCtlColorBtn);
+ MSG_WM_CTLCOLORSTATIC(OnCtlColorStatic)
+ END_MSG_MAP();
+
+ HWND GetControl() {
+ return control_;
+ }
+
+ // Called when the parent is getting deleted. This control stays around until
+ // it gets the OnFinalMessage call.
+ void ResetParent() {
+ parent_ = NULL;
+ }
+
+ void OnFinalMessage(HWND hwnd) {
+ if (parent_)
+ parent_->NativeControlDestroyed();
+ delete this;
+ }
+
+ private:
+ friend class NativeControl;
+
+ LRESULT OnCreate(LPCREATESTRUCT create_struct) {
+ control_ = parent_->CreateNativeControl(m_hWnd);
+
+ // We subclass the control hwnd so we get the WM_KEYDOWN messages.
+ original_handler_ = ui::SetWindowProc(
+ control_, &NativeControl::NativeControlWndProc);
+ prop_.reset(new ViewProp(control_, kNativeControlKey , parent_));
+
+ ::ShowWindow(control_, SW_SHOW);
+ return 1;
+ }
+
+ LRESULT OnEraseBkgnd(HDC dc) {
+ return 1;
+ }
+
+ void OnPaint(HDC ignore) {
+ PAINTSTRUCT ps;
+ HDC dc = ::BeginPaint(*this, &ps);
+ ::EndPaint(*this, &ps);
+ }
+
+ void OnSize(int type, const CSize& sz) {
+ ::MoveWindow(control_, 0, 0, sz.cx, sz.cy, TRUE);
+ }
+
+ LRESULT OnCommand(UINT code, int id, HWND source) {
+ return parent_ ? parent_->OnCommand(code, id, source) : 0;
+ }
+
+ LRESULT OnNotify(int w_param, LPNMHDR l_param) {
+ if (parent_)
+ return parent_->OnNotify(w_param, l_param);
+ else
+ return 0;
+ }
+
+ void OnDestroy() {
+ if (parent_)
+ parent_->OnDestroy();
+ }
+
+ void OnContextMenu(HWND window, const POINT& location) {
+ if (parent_)
+ parent_->OnContextMenu(location);
+ }
+
+ // We need to find an ancestor with a non-null background, and
+ // ask it for a (solid color) brush that approximates
+ // the background. The caller will use this when drawing
+ // the native control as a background color, particularly
+ // for radiobuttons and XP style pushbuttons.
+ LRESULT OnCtlColor(UINT msg, HDC dc, HWND control) {
+ const View *ancestor = parent_;
+ while (ancestor) {
+ const Background *background = ancestor->background();
+ if (background) {
+ HBRUSH brush = background->GetNativeControlBrush();
+ if (brush)
+ return reinterpret_cast<LRESULT>(brush);
+ }
+ ancestor = ancestor->parent();
+ }
+
+ // COLOR_BTNFACE is the default for dialog box backgrounds.
+ return reinterpret_cast<LRESULT>(GetSysColorBrush(COLOR_BTNFACE));
+ }
+
+ LRESULT OnCtlColorBtn(HDC dc, HWND control) {
+ return OnCtlColor(WM_CTLCOLORBTN, dc, control);
+ }
+
+ LRESULT OnCtlColorStatic(HDC dc, HWND control) {
+ return OnCtlColor(WM_CTLCOLORSTATIC, dc, control);
+ }
+
+ NativeControl* parent_;
+ HWND control_;
+
+ // Message handler that was set before we reset it.
+ WNDPROC original_handler_;
+
+ scoped_ptr<ViewProp> prop_;
+
+ DISALLOW_COPY_AND_ASSIGN(NativeControlContainer);
+};
+
+NativeControl::NativeControl() : hwnd_view_(NULL),
+ container_(NULL),
+ fixed_width_(-1),
+ horizontal_alignment_(CENTER),
+ fixed_height_(-1),
+ vertical_alignment_(CENTER) {
+ set_focusable(true);
+}
+
+NativeControl::~NativeControl() {
+ if (container_) {
+ container_->ResetParent();
+ ::DestroyWindow(*container_);
+ }
+}
+
+void NativeControl::ValidateNativeControl() {
+ if (hwnd_view_ == NULL) {
+ hwnd_view_ = new NativeViewHost;
+ AddChildView(hwnd_view_);
+ }
+
+ if (!container_ && IsVisible()) {
+ container_ = new NativeControlContainer(this);
+ container_->Init();
+ hwnd_view_->Attach(*container_);
+ if (!IsEnabled())
+ EnableWindow(GetNativeControlHWND(), IsEnabled());
+
+ // This message ensures that the focus border is shown.
+ ::SendMessage(container_->GetControl(),
+ WM_CHANGEUISTATE,
+ MAKELPARAM(UIS_CLEAR, UISF_HIDEFOCUS),
+ 0);
+ }
+}
+
+void NativeControl::ViewHierarchyChanged(bool is_add, View *parent,
+ View *child) {
+ if (is_add && parent != this && !container_ && GetWidget()) {
+ ValidateNativeControl();
+ Layout();
+ }
+}
+
+void NativeControl::Layout() {
+ if (!container_ && GetWidget())
+ ValidateNativeControl();
+
+ if (hwnd_view_) {
+ gfx::Rect lb = GetLocalBounds();
+
+ int x = lb.x();
+ int y = lb.y();
+ int width = lb.width();
+ int height = lb.height();
+ if (fixed_width_ > 0) {
+ width = std::min(fixed_width_, width);
+ switch (horizontal_alignment_) {
+ case LEADING:
+ // Nothing to do.
+ break;
+ case CENTER:
+ x += (lb.width() - width) / 2;
+ break;
+ case TRAILING:
+ x = x + lb.width() - width;
+ break;
+ default:
+ NOTREACHED();
+ }
+ }
+
+ if (fixed_height_ > 0) {
+ height = std::min(fixed_height_, height);
+ switch (vertical_alignment_) {
+ case LEADING:
+ // Nothing to do.
+ break;
+ case CENTER:
+ y += (lb.height() - height) / 2;
+ break;
+ case TRAILING:
+ y = y + lb.height() - height;
+ break;
+ default:
+ NOTREACHED();
+ }
+ }
+
+ hwnd_view_->SetBounds(x, y, width, height);
+ }
+}
+
+void NativeControl::OnContextMenu(const POINT& location) {
+ if (!context_menu_controller())
+ return;
+
+ if (location.x == -1 && location.y == -1)
+ ShowContextMenu(GetKeyboardContextMenuLocation(), false);
+ else
+ ShowContextMenu(gfx::Point(location), true);
+}
+
+void NativeControl::OnFocus() {
+ if (container_) {
+ DCHECK(container_->GetControl());
+ ::SetFocus(container_->GetControl());
+ if (GetWidget()) {
+ GetWidget()->NotifyAccessibilityEvent(
+ this, ui::AccessibilityTypes::EVENT_FOCUS, false);
+ }
+ }
+}
+
+HWND NativeControl::GetNativeControlHWND() {
+ if (container_)
+ return container_->GetControl();
+ else
+ return NULL;
+}
+
+void NativeControl::NativeControlDestroyed() {
+ if (hwnd_view_)
+ hwnd_view_->Detach();
+ container_ = NULL;
+}
+
+void NativeControl::SetVisible(bool f) {
+ if (f != IsVisible()) {
+ View::SetVisible(f);
+ if (!f && container_) {
+ ::DestroyWindow(*container_);
+ } else if (f && !container_) {
+ ValidateNativeControl();
+ }
+ }
+}
+
+void NativeControl::OnEnabledChanged() {
+ View::OnEnabledChanged();
+ if (GetNativeControlHWND())
+ EnableWindow(GetNativeControlHWND(), IsEnabled());
+}
+
+void NativeControl::OnPaint(gfx::Canvas* canvas) {
+}
+
+void NativeControl::VisibilityChanged(View* starting_from, bool is_visible) {
+ SetVisible(is_visible);
+}
+
+void NativeControl::SetFixedWidth(int width, Alignment alignment) {
+ DCHECK_GT(width, 0);
+ fixed_width_ = width;
+ horizontal_alignment_ = alignment;
+}
+
+void NativeControl::SetFixedHeight(int height, Alignment alignment) {
+ DCHECK_GT(height, 0);
+ fixed_height_ = height;
+ vertical_alignment_ = alignment;
+}
+
+DWORD NativeControl::GetAdditionalExStyle() const {
+ // If the UI for the view is mirrored, we should make sure we add the
+ // extended window style for a right-to-left layout so the subclass creates
+ // a mirrored HWND for the underlying control.
+ DWORD ex_style = 0;
+ if (base::i18n::IsRTL())
+ ex_style |= l10n_util::GetExtendedStyles();
+
+ return ex_style;
+}
+
+DWORD NativeControl::GetAdditionalRTLStyle() const {
+ // If the UI for the view is mirrored, we should make sure we add the
+ // extended window style for a right-to-left layout so the subclass creates
+ // a mirrored HWND for the underlying control.
+ DWORD ex_style = 0;
+ if (base::i18n::IsRTL())
+ ex_style |= l10n_util::GetExtendedTooltipStyles();
+
+ return ex_style;
+}
+
+// static
+LRESULT CALLBACK NativeControl::NativeControlWndProc(HWND window,
+ UINT message,
+ WPARAM w_param,
+ LPARAM l_param) {
+ NativeControl* native_control = static_cast<NativeControl*>(
+ ViewProp::GetValue(window, kNativeControlKey));
+ DCHECK(native_control);
+ WNDPROC original_handler = native_control->container_->original_handler_;
+ DCHECK(original_handler);
+
+ if (message == WM_KEYDOWN &&
+ native_control->OnKeyDown(ui::KeyboardCodeForWindowsKeyCode(w_param))) {
+ return 0;
+ } else if (message == WM_SETFOCUS) {
+ // Let the focus manager know that the focus changed.
+ FocusManager* focus_manager = native_control->GetFocusManager();
+ if (focus_manager) {
+ focus_manager->SetFocusedView(native_control);
+ } else {
+ NOTREACHED();
+ }
+ } else if (message == WM_DESTROY) {
+ ui::SetWindowProc(window, reinterpret_cast<WNDPROC>(original_handler));
+ native_control->container_->prop_.reset();
+ }
+
+ return CallWindowProc(reinterpret_cast<WNDPROC>(original_handler), window,
+ message, w_param, l_param);
+}
+
+} // namespace views
diff --git a/ui/views/controls/native_control.h b/ui/views/controls/native_control.h
new file mode 100644
index 0000000..ba419c1
--- /dev/null
+++ b/ui/views/controls/native_control.h
@@ -0,0 +1,126 @@
+// 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 UI_VIEWS_CONTROLS_NATIVE_CONTROL_H_
+#define UI_VIEWS_CONTROLS_NATIVE_CONTROL_H_
+#pragma once
+
+#include <windows.h>
+
+#include "ui/base/keycodes/keyboard_codes.h"
+#include "views/view.h"
+
+namespace views {
+
+class NativeViewHost;
+class NativeControlContainer;
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// NativeControl is an abstract view that is used to implement views wrapping
+// native controls. Subclasses can simply implement CreateNativeControl() to
+// wrap a new kind of control
+//
+////////////////////////////////////////////////////////////////////////////////
+class VIEWS_EXPORT NativeControl : public View {
+ public:
+ enum Alignment {
+ LEADING = 0,
+ CENTER,
+ TRAILING };
+
+ NativeControl();
+ virtual ~NativeControl();
+
+ virtual void ViewHierarchyChanged(bool is_add, View *parent, View *child);
+ virtual void Layout();
+
+ // Overridden to properly set the native control state.
+ virtual void SetVisible(bool f);
+ virtual void OnEnabledChanged();
+
+ // Overridden to do nothing.
+ virtual void OnPaint(gfx::Canvas* canvas);
+
+ protected:
+ friend class NativeControlContainer;
+
+ // Overridden by sub-classes to create the windows control which is wrapped
+ virtual HWND CreateNativeControl(HWND parent_container) = 0;
+
+ // Invoked when the native control sends a WM_NOTIFY message to its parent
+ virtual LRESULT OnNotify(int w_param, LPNMHDR l_param) = 0;
+
+ // Invoked when the native control sends a WM_COMMAND message to its parent
+ virtual LRESULT OnCommand(UINT code, int id, HWND source) { return 0; }
+
+ // Invoked when the appropriate gesture for a context menu is issued.
+ virtual void OnContextMenu(const POINT& location);
+
+ // Overridden so to set the native focus to the native control.
+ virtual void OnFocus();
+
+ // Invoked when the native control sends a WM_DESTORY message to its parent.
+ virtual void OnDestroy() { }
+
+ // Return the native control
+ virtual HWND GetNativeControlHWND();
+
+ // Invoked by the native windows control when it has been destroyed. This is
+ // invoked AFTER WM_DESTORY has been sent. Any window commands send to the
+ // HWND will most likely fail.
+ void NativeControlDestroyed();
+
+ // Overridden so that the control properly reflects parent's visibility.
+ virtual void VisibilityChanged(View* starting_from, bool is_visible);
+
+ // Controls that have fixed sizes should call these methods to specify the
+ // actual size and how they should be aligned within their parent.
+ void SetFixedWidth(int width, Alignment alignment);
+ void SetFixedHeight(int height, Alignment alignment);
+
+ // Invoked when a key is pressed on the control.
+ // Should return true if the key message was processed, false otherwise.
+ virtual bool OnKeyDown(ui::KeyboardCode virtual_key_code) { return false; }
+
+ // Returns additional extended style flags. When subclasses call
+ // CreateWindowEx in order to create the underlying control, they must OR the
+ // ExStyle parameter with the value returned by this function.
+ //
+ // We currently use this method in order to add flags such as WS_EX_LAYOUTRTL
+ // to the HWND for views with right-to-left UI layout.
+ DWORD GetAdditionalExStyle() const;
+
+ // TODO(xji): we use the following temporary function as we transition the
+ // various native controls to use the right set of RTL flags. This function
+ // will go away (and be replaced by GetAdditionalExStyle()) once all the
+ // controls are properly transitioned.
+ DWORD GetAdditionalRTLStyle() const;
+
+ // This variable is protected to provide subclassers direct access. However
+ // subclassers should always check for NULL since this variable is only
+ // initialized in ValidateNativeControl().
+ NativeViewHost* hwnd_view_;
+
+ // Fixed size information. -1 for a size means no fixed size.
+ int fixed_width_;
+ Alignment horizontal_alignment_;
+ int fixed_height_;
+ Alignment vertical_alignment_;
+
+ private:
+
+ void ValidateNativeControl();
+
+ static LRESULT CALLBACK NativeControlWndProc(HWND window, UINT message,
+ WPARAM w_param, LPARAM l_param);
+
+ NativeControlContainer* container_;
+
+ DISALLOW_COPY_AND_ASSIGN(NativeControl);
+};
+
+} // namespace views
+
+#endif // UI_VIEWS_CONTROLS_NATIVE_CONTROL_H_
diff --git a/ui/views/controls/native_control_gtk.cc b/ui/views/controls/native_control_gtk.cc
new file mode 100644
index 0000000..417e8a8
--- /dev/null
+++ b/ui/views/controls/native_control_gtk.cc
@@ -0,0 +1,93 @@
+// 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 "ui/views/controls/native_control_gtk.h"
+
+#include <gtk/gtk.h>
+
+#include "base/logging.h"
+#include "ui/base/accessibility/accessibility_types.h"
+#include "ui/views/focus/focus_manager.h"
+#include "ui/views/widget/widget.h"
+
+namespace views {
+
+NativeControlGtk::NativeControlGtk() {
+}
+
+NativeControlGtk::~NativeControlGtk() {
+ if (native_view())
+ gtk_widget_destroy(native_view());
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// NativeControlGtk, View overrides:
+
+void NativeControlGtk::OnEnabledChanged() {
+ View::OnEnabledChanged();
+ if (native_view())
+ gtk_widget_set_sensitive(native_view(), IsEnabled());
+}
+
+void NativeControlGtk::ViewHierarchyChanged(bool is_add, View* parent,
+ View* child) {
+ // Call the base class to hide the view if we're being removed.
+ NativeViewHost::ViewHierarchyChanged(is_add, parent, child);
+
+ if (!is_add && child == this && native_view()) {
+ Detach();
+ } else if (is_add && GetWidget() && !native_view()) {
+ // Create the widget when we're added to a valid Widget. Many
+ // controls need a parent widget to function properly.
+ CreateNativeControl();
+ }
+}
+
+void NativeControlGtk::VisibilityChanged(View* starting_from, bool is_visible) {
+ if (!native_view()) {
+ if (GetWidget())
+ CreateNativeControl();
+ } else {
+ // The view becomes visible after native control is created.
+ // Layout now.
+ Layout();
+ }
+}
+
+void NativeControlGtk::OnFocus() {
+ DCHECK(native_view());
+ gtk_widget_grab_focus(native_view());
+ GetWidget()->NotifyAccessibilityEvent(
+ parent(), ui::AccessibilityTypes::EVENT_FOCUS, true);
+}
+
+void NativeControlGtk::NativeControlCreated(GtkWidget* native_control) {
+ Attach(native_control);
+
+ // Update the newly created GtkWidget with any resident enabled state.
+ gtk_widget_set_sensitive(native_view(), IsEnabled());
+
+ // Listen for focus change event to update the FocusManager focused view.
+ g_signal_connect(native_control, "focus-in-event",
+ G_CALLBACK(CallFocusIn), this);
+}
+
+// static
+gboolean NativeControlGtk::CallFocusIn(GtkWidget* gtk_widget,
+ GdkEventFocus* event,
+ NativeControlGtk* control) {
+ Widget* widget = Widget::GetTopLevelWidgetForNativeView(gtk_widget);
+ FocusManager* focus_manager = widget ? widget->GetFocusManager() : NULL;
+ if (!focus_manager) {
+ // TODO(jcampan): http://crbug.com/21378 Reenable this NOTREACHED() when the
+ // options page is only based on views.
+ // NOTREACHED();
+ NOTIMPLEMENTED();
+ return false;
+ }
+ focus_manager->SetFocusedView(control->focus_view());
+ return false;
+}
+
+} // namespace views
diff --git a/ui/views/controls/native_control_gtk.h b/ui/views/controls/native_control_gtk.h
new file mode 100644
index 0000000..a72c943
--- /dev/null
+++ b/ui/views/controls/native_control_gtk.h
@@ -0,0 +1,52 @@
+// 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 UI_VIEWS_CONTROLS_NATIVE_CONTROL_GTK_H_
+#define UI_VIEWS_CONTROLS_NATIVE_CONTROL_GTK_H_
+#pragma once
+
+#include <gtk/gtk.h>
+
+#include "base/compiler_specific.h"
+#include "ui/views/controls/native/native_view_host.h"
+
+namespace views {
+
+// A View that hosts a native control.
+class NativeControlGtk : public NativeViewHost {
+ public:
+ NativeControlGtk();
+ virtual ~NativeControlGtk();
+
+ // Overridden from View:
+ virtual void OnEnabledChanged() OVERRIDE;
+
+ protected:
+ virtual void ViewHierarchyChanged(bool is_add,
+ View *parent,
+ View *child) OVERRIDE;
+ virtual void VisibilityChanged(View* starting_from, bool is_visible) OVERRIDE;
+ virtual void OnFocus() OVERRIDE;
+
+ // Called when the NativeControlGtk is attached to a View hierarchy with a
+ // valid Widget. The NativeControlGtk should use this opportunity to create
+ // its associated GtkWidget.
+ virtual void CreateNativeControl() = 0;
+
+ // MUST be called by the subclass implementation of |CreateNativeControl|
+ // immediately after creating the control GtkWidget, otherwise it won't be
+ // attached to the GtkView and will be effectively orphaned.
+ virtual void NativeControlCreated(GtkWidget* widget);
+
+ private:
+ static gboolean CallFocusIn(GtkWidget* gtk_widget,
+ GdkEventFocus* event,
+ NativeControlGtk* button);
+
+ DISALLOW_COPY_AND_ASSIGN(NativeControlGtk);
+};
+
+} // namespace views
+
+#endif // UI_VIEWS_CONTROLS_NATIVE_CONTROL_GTK_H_
diff --git a/ui/views/controls/native_control_win.cc b/ui/views/controls/native_control_win.cc
new file mode 100644
index 0000000..2b71964
--- /dev/null
+++ b/ui/views/controls/native_control_win.cc
@@ -0,0 +1,226 @@
+// 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 "ui/views/controls/native_control_win.h"
+
+#include <windowsx.h>
+
+#include "base/logging.h"
+#include "ui/base/accessibility/accessibility_types.h"
+#include "ui/base/l10n/l10n_util_win.h"
+#include "ui/base/view_prop.h"
+#include "ui/base/win/hwnd_util.h"
+#include "ui/views/controls/combobox/combobox.h"
+#include "ui/views/focus/focus_manager.h"
+#include "ui/views/widget/widget.h"
+
+using ui::ViewProp;
+
+const char kNativeControlWinKey[] = "__NATIVE_CONTROL_WIN__";
+
+namespace views {
+
+////////////////////////////////////////////////////////////////////////////////
+// NativeControlWin, public:
+
+NativeControlWin::NativeControlWin() {
+}
+
+NativeControlWin::~NativeControlWin() {
+ HWND hwnd = native_view();
+ if (hwnd) {
+ // Destroy the hwnd if it still exists. Otherwise we won't have shut things
+ // down correctly, leading to leaking and crashing if another message
+ // comes in for the hwnd.
+ Detach();
+ DestroyWindow(hwnd);
+ }
+}
+
+bool NativeControlWin::ProcessMessage(UINT message,
+ WPARAM w_param,
+ LPARAM l_param,
+ LRESULT* result) {
+ switch (message) {
+ case WM_CONTEXTMENU:
+ ShowContextMenu(gfx::Point(GET_X_LPARAM(l_param), GET_Y_LPARAM(l_param)));
+ *result = 0;
+ return true;
+ case WM_CTLCOLORBTN:
+ case WM_CTLCOLORSTATIC:
+ *result = GetControlColor(message, reinterpret_cast<HDC>(w_param),
+ native_view());
+ return true;
+ }
+ return false;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// NativeControlWin, View overrides:
+
+void NativeControlWin::OnEnabledChanged() {
+ View::OnEnabledChanged();
+ if (native_view())
+ EnableWindow(native_view(), IsEnabled());
+}
+
+void NativeControlWin::ViewHierarchyChanged(bool is_add, View* parent,
+ View* child) {
+ // Call the base class to hide the view if we're being removed.
+ NativeViewHost::ViewHierarchyChanged(is_add, parent, child);
+
+ // Create the HWND when we're added to a valid Widget. Many controls need a
+ // parent HWND to function properly.
+ if (is_add && GetWidget() && !native_view())
+ CreateNativeControl();
+}
+
+void NativeControlWin::VisibilityChanged(View* starting_from, bool is_visible) {
+ // We might get called due to visibility changes at any point in the
+ // hierarchy, lets check whether we are really visible or not.
+ bool visible = IsVisibleInRootView();
+ if (!visible && native_view()) {
+ // We destroy the child control HWND when we become invisible because of the
+ // performance cost of maintaining many HWNDs.
+ HWND hwnd = native_view();
+ Detach();
+ DestroyWindow(hwnd);
+ } else if (visible && !native_view()) {
+ if (GetWidget())
+ CreateNativeControl();
+ }
+ if (visible) {
+ // The view becomes visible after native control is created.
+ // Layout now.
+ Layout();
+ }
+}
+
+void NativeControlWin::OnFocus() {
+ DCHECK(native_view());
+ SetFocus(native_view());
+
+ // Since we are being wrapped by a view, accessibility should receive
+ // the super class as the focused view.
+ View* parent_view = parent();
+
+ // Due to some controls not behaving as expected without having
+ // a native win32 control, we don't always send a native (MSAA)
+ // focus notification.
+ bool send_native_event =
+ parent_view->GetClassName() != Combobox::kViewClassName &&
+ parent_view->HasFocus();
+
+ // Send the accessibility focus notification.
+ if (parent_view->GetWidget()) {
+ parent_view->GetWidget()->NotifyAccessibilityEvent(
+ parent_view, ui::AccessibilityTypes::EVENT_FOCUS, send_native_event);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// NativeControlWin, protected:
+
+void NativeControlWin::ShowContextMenu(const gfx::Point& location) {
+ if (!context_menu_controller())
+ return;
+
+ if (location.x() == -1 && location.y() == -1)
+ View::ShowContextMenu(GetKeyboardContextMenuLocation(), false);
+ else
+ View::ShowContextMenu(location, true);
+}
+
+void NativeControlWin::NativeControlCreated(HWND native_control) {
+ // Associate this object with the control's HWND so that NativeWidgetWin can
+ // find this object when it receives messages from it.
+ props_.push_back(new ViewProp(native_control, kNativeControlWinKey, this));
+ props_.push_back(ChildWindowMessageProcessor::Register(native_control, this));
+
+ // Subclass so we get WM_KEYDOWN and WM_SETFOCUS messages.
+ original_wndproc_ = ui::SetWindowProc(
+ native_control, &NativeControlWin::NativeControlWndProc);
+
+ Attach(native_control);
+ // native_view() is now valid.
+
+ // Update the newly created HWND with any resident enabled state.
+ EnableWindow(native_view(), IsEnabled());
+
+ // This message ensures that the focus border is shown.
+ SendMessage(native_view(), WM_CHANGEUISTATE,
+ MAKEWPARAM(UIS_CLEAR, UISF_HIDEFOCUS), 0);
+}
+
+DWORD NativeControlWin::GetAdditionalExStyle() const {
+ // If the UI for the view is mirrored, we should make sure we add the
+ // extended window style for a right-to-left layout so the subclass creates
+ // a mirrored HWND for the underlying control.
+ DWORD ex_style = 0;
+ if (base::i18n::IsRTL())
+ ex_style |= l10n_util::GetExtendedStyles();
+
+ return ex_style;
+}
+
+DWORD NativeControlWin::GetAdditionalRTLStyle() const {
+ // If the UI for the view is mirrored, we should make sure we add the
+ // extended window style for a right-to-left layout so the subclass creates
+ // a mirrored HWND for the underlying control.
+ DWORD ex_style = 0;
+ if (base::i18n::IsRTL())
+ ex_style |= l10n_util::GetExtendedTooltipStyles();
+
+ return ex_style;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// NativeControlWin, private:
+
+LRESULT NativeControlWin::GetControlColor(UINT message, HDC dc, HWND sender) {
+ View *ancestor = this;
+ while (ancestor) {
+ const Background* background = ancestor->background();
+ if (background) {
+ HBRUSH brush = background->GetNativeControlBrush();
+ if (brush)
+ return reinterpret_cast<LRESULT>(brush);
+ }
+ ancestor = ancestor->parent();
+ }
+
+ // COLOR_BTNFACE is the default for dialog box backgrounds.
+ return reinterpret_cast<LRESULT>(GetSysColorBrush(COLOR_BTNFACE));
+}
+
+// static
+LRESULT NativeControlWin::NativeControlWndProc(HWND window,
+ UINT message,
+ WPARAM w_param,
+ LPARAM l_param) {
+ NativeControlWin* native_control = reinterpret_cast<NativeControlWin*>(
+ ViewProp::GetValue(window, kNativeControlWinKey));
+ DCHECK(native_control);
+
+ if (message == WM_KEYDOWN &&
+ native_control->OnKeyDown(static_cast<int>(w_param))) {
+ return 0;
+ } else if (message == WM_SETFOCUS) {
+ // Let the focus manager know that the focus changed.
+ FocusManager* focus_manager = native_control->GetFocusManager();
+ if (focus_manager) {
+ focus_manager->SetFocusedView(native_control->focus_view());
+ } else {
+ NOTREACHED();
+ }
+ } else if (message == WM_DESTROY) {
+ native_control->props_.reset();
+ ui::SetWindowProc(window, native_control->original_wndproc_);
+ }
+
+ return CallWindowProc(native_control->original_wndproc_, window, message,
+ w_param, l_param);
+}
+
+} // namespace views
diff --git a/ui/views/controls/native_control_win.h b/ui/views/controls/native_control_win.h
new file mode 100644
index 0000000..f8a002b
--- /dev/null
+++ b/ui/views/controls/native_control_win.h
@@ -0,0 +1,101 @@
+// 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 UI_VIEWS_CONTROLS_NATIVE_CONTROL_WIN_H_
+#define UI_VIEWS_CONTROLS_NATIVE_CONTROL_WIN_H_
+#pragma once
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_vector.h"
+#include "ui/views/controls/native/native_view_host.h"
+#include "ui/views/widget/child_window_message_processor.h"
+
+namespace ui {
+class ViewProp;
+}
+
+namespace views {
+
+// A View that hosts a native Windows control.
+class NativeControlWin : public ChildWindowMessageProcessor,
+ public NativeViewHost {
+ public:
+ NativeControlWin();
+ virtual ~NativeControlWin();
+
+ // Called by our subclassed window procedure when a WM_KEYDOWN message is
+ // received by the HWND created by an object derived from NativeControlWin.
+ // Returns true if the key was processed, false otherwise.
+ virtual bool OnKeyDown(int vkey) { return false; }
+
+ // Overridden from ChildWindowMessageProcessor:
+ virtual bool ProcessMessage(UINT message,
+ WPARAM w_param,
+ LPARAM l_param,
+ LRESULT* result) OVERRIDE;
+
+ // Overridden from View:
+ virtual void OnEnabledChanged() OVERRIDE;
+
+ protected:
+ virtual void ViewHierarchyChanged(bool is_add,
+ View* parent,
+ View* child) OVERRIDE;
+ virtual void VisibilityChanged(View* starting_from, bool is_visible) OVERRIDE;
+ virtual void OnFocus() OVERRIDE;
+
+ // Called by the containing NativeWidgetWin when a WM_CONTEXTMENU message is
+ // received from the HWND created by an object derived from NativeControlWin.
+ virtual void ShowContextMenu(const gfx::Point& location);
+
+ // Called when the NativeControlWin is attached to a View hierarchy with a
+ // valid Widget. The NativeControlWin should use this opportunity to create
+ // its associated HWND.
+ virtual void CreateNativeControl() = 0;
+
+ // MUST be called by the subclass implementation of |CreateNativeControl|
+ // immediately after creating the control HWND, otherwise it won't be attached
+ // to the NativeViewHost and will be effectively orphaned.
+ virtual void NativeControlCreated(HWND native_control);
+
+ // Returns additional extended style flags. When subclasses call
+ // CreateWindowEx in order to create the underlying control, they must OR the
+ // ExStyle parameter with the value returned by this function.
+ //
+ // We currently use this method in order to add flags such as WS_EX_LAYOUTRTL
+ // to the HWND for views with right-to-left UI layout.
+ DWORD GetAdditionalExStyle() const;
+
+ // TODO(xji): we use the following temporary function as we transition the
+ // various native controls to use the right set of RTL flags. This function
+ // will go away (and be replaced by GetAdditionalExStyle()) once all the
+ // controls are properly transitioned.
+ DWORD GetAdditionalRTLStyle() const;
+
+ private:
+ typedef ScopedVector<ui::ViewProp> ViewProps;
+
+ // Called by the containing NativeWidgetWin when a message of type
+ // WM_CTLCOLORBTN or WM_CTLCOLORSTATIC is sent from the HWND created by an
+ // object derived from NativeControlWin.
+ LRESULT GetControlColor(UINT message, HDC dc, HWND sender);
+
+ // Our subclass window procedure for the attached control.
+ static LRESULT CALLBACK NativeControlWndProc(HWND window,
+ UINT message,
+ WPARAM w_param,
+ LPARAM l_param);
+
+ // The window procedure before we subclassed.
+ WNDPROC original_wndproc_;
+
+ ViewProps props_;
+
+ DISALLOW_COPY_AND_ASSIGN(NativeControlWin);
+};
+
+} // namespace views
+
+#endif // UI_VIEWS_CONTROLS_NATIVE_CONTROL_WIN_H_
diff --git a/ui/views/controls/progress_bar.cc b/ui/views/controls/progress_bar.cc
new file mode 100644
index 0000000..74943bb
--- /dev/null
+++ b/ui/views/controls/progress_bar.cc
@@ -0,0 +1,319 @@
+// 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 "ui/views/controls/progress_bar.h"
+
+#include <algorithm>
+#include <string>
+
+#include "base/logging.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "third_party/skia/include/effects/SkBlurMaskFilter.h"
+#include "third_party/skia/include/effects/SkGradientShader.h"
+#include "ui/base/accessibility/accessible_view_state.h"
+#include "ui/gfx/canvas_skia.h"
+#include "ui/gfx/color_utils.h"
+#include "ui/gfx/font.h"
+#include "ui/gfx/insets.h"
+#include "views/background.h"
+#include "views/border.h"
+#include "views/painter.h"
+
+namespace {
+
+// Corner radius for the progress bar's border.
+const int kCornerRadius = 3;
+
+// Progress bar's border width
+const int kBorderWidth = 1;
+
+void AddRoundRectPathWithPadding(int x, int y,
+ int w, int h,
+ int corner_radius,
+ SkScalar padding,
+ SkPath* path) {
+ DCHECK(path);
+ if (path == NULL)
+ return;
+ SkRect rect;
+ rect.set(
+ SkIntToScalar(x) + padding, SkIntToScalar(y) + padding,
+ SkIntToScalar(x + w) - padding, SkIntToScalar(y + h) - padding);
+ path->addRoundRect(
+ rect,
+ SkIntToScalar(corner_radius) - padding,
+ SkIntToScalar(corner_radius) - padding);
+}
+
+void AddRoundRectPath(int x, int y,
+ int w, int h,
+ int corner_radius,
+ SkPath* path) {
+ static const SkScalar half = SkIntToScalar(1) / 2;
+ AddRoundRectPathWithPadding(x, y, w, h, corner_radius, half, path);
+}
+
+void FillRoundRect(gfx::Canvas* canvas,
+ int x, int y,
+ int w, int h,
+ int corner_radius,
+ const SkColor colors[],
+ const SkScalar points[],
+ int count,
+ bool gradient_horizontal) {
+ SkPath path;
+ AddRoundRectPath(x, y, w, h, corner_radius, &path);
+ SkPaint paint;
+ paint.setStyle(SkPaint::kFill_Style);
+ paint.setFlags(SkPaint::kAntiAlias_Flag);
+
+ SkPoint p[2];
+ p[0].set(SkIntToScalar(x), SkIntToScalar(y));
+ if (gradient_horizontal) {
+ p[1].set(SkIntToScalar(x + w), SkIntToScalar(y));
+ } else {
+ p[1].set(SkIntToScalar(x), SkIntToScalar(y + h));
+ }
+ SkShader* s = SkGradientShader::CreateLinear(
+ p, colors, points, count, SkShader::kClamp_TileMode, NULL);
+ paint.setShader(s);
+ // Need to unref shader, otherwise never deleted.
+ s->unref();
+
+ canvas->GetSkCanvas()->drawPath(path, paint);
+}
+
+void FillRoundRect(gfx::Canvas* canvas,
+ int x, int y,
+ int w, int h,
+ int corner_radius,
+ SkColor gradient_start_color,
+ SkColor gradient_end_color,
+ bool gradient_horizontal) {
+ if (gradient_start_color != gradient_end_color) {
+ SkColor colors[2] = { gradient_start_color, gradient_end_color };
+ FillRoundRect(canvas, x, y, w, h, corner_radius,
+ colors, NULL, 2, gradient_horizontal);
+ } else {
+ SkPath path;
+ AddRoundRectPath(x, y, w, h, corner_radius, &path);
+ SkPaint paint;
+ paint.setStyle(SkPaint::kFill_Style);
+ paint.setFlags(SkPaint::kAntiAlias_Flag);
+ paint.setColor(gradient_start_color);
+ canvas->GetSkCanvas()->drawPath(path, paint);
+ }
+}
+
+void StrokeRoundRect(gfx::Canvas* canvas,
+ int x, int y,
+ int w, int h,
+ int corner_radius,
+ SkColor stroke_color,
+ int stroke_width) {
+ SkPath path;
+ AddRoundRectPath(x, y, w, h, corner_radius, &path);
+ SkPaint paint;
+ paint.setShader(NULL);
+ paint.setColor(stroke_color);
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setFlags(SkPaint::kAntiAlias_Flag);
+ paint.setStrokeWidth(SkIntToScalar(stroke_width));
+ canvas->GetSkCanvas()->drawPath(path, paint);
+}
+
+} // namespace
+
+namespace views {
+
+// static
+const char ProgressBar::kViewClassName[] = "views/ProgressBar";
+
+ProgressBar::ProgressBar()
+ : min_display_value_(0.0),
+ max_display_value_(1.0),
+ current_value_(0.0) {
+}
+
+ProgressBar::~ProgressBar() {
+}
+
+void ProgressBar::SetDisplayRange(double min_display_value,
+ double max_display_value) {
+ if (min_display_value != min_display_value_ ||
+ max_display_value != max_display_value_) {
+ DCHECK(min_display_value < max_display_value);
+ min_display_value_ = min_display_value;
+ max_display_value_ = max_display_value;
+ SchedulePaint();
+ }
+}
+
+void ProgressBar::SetValue(double value) {
+ if (value != current_value_) {
+ current_value_ = value;
+ SchedulePaint();
+ }
+}
+
+void ProgressBar::SetTooltipText(const string16& tooltip_text) {
+ tooltip_text_ = tooltip_text;
+}
+
+bool ProgressBar::GetTooltipText(const gfx::Point& p, string16* tooltip) const {
+ DCHECK(tooltip);
+ if (tooltip == NULL)
+ return false;
+ tooltip->assign(tooltip_text_);
+ return !tooltip_text_.empty();
+}
+
+void ProgressBar::GetAccessibleState(ui::AccessibleViewState* state) {
+ state->role = ui::AccessibilityTypes::ROLE_PROGRESSBAR;
+ state->state = ui::AccessibilityTypes::STATE_READONLY;
+}
+
+gfx::Size ProgressBar::GetPreferredSize() {
+ return gfx::Size(100, 16);
+}
+
+std::string ProgressBar::GetClassName() const {
+ return kViewClassName;
+}
+
+void ProgressBar::OnPaint(gfx::Canvas* canvas) {
+ const double capped_value = std::min(
+ std::max(current_value_, min_display_value_), max_display_value_);
+ const double capped_fraction =
+ (capped_value - min_display_value_) /
+ (max_display_value_ - min_display_value_);
+ const int progress_width = static_cast<int>(width() * capped_fraction + 0.5);
+
+#if defined(OS_CHROMEOS)
+ const SkColor background_colors[] = {
+ SkColorSetRGB(0xBB, 0xBB, 0xBB),
+ SkColorSetRGB(0xE7, 0xE7, 0xE7),
+ SkColorSetRGB(0xFE, 0xFE, 0xFE)
+ };
+
+ const SkScalar background_points[] = {
+ SkDoubleToScalar(0),
+ SkDoubleToScalar(0.1),
+ SkDoubleToScalar(1)
+ };
+ const SkColor background_border_color = SkColorSetRGB(0xA1, 0xA1, 0xA1);
+
+ // Draw background.
+ FillRoundRect(canvas,
+ 0, 0, width(), height(),
+ kCornerRadius,
+ background_colors,
+ background_points,
+ arraysize(background_colors),
+ false);
+ StrokeRoundRect(canvas,
+ 0, 0,
+ width(), height(),
+ kCornerRadius,
+ background_border_color,
+ kBorderWidth);
+
+ if (progress_width > 1) {
+ const bool enabled = IsEnabled();
+
+ const SkColor bar_color_start = enabled ?
+ SkColorSetRGB(100, 116, 147) :
+ SkColorSetRGB(229, 232, 237);
+ const SkColor bar_color_end = enabled ?
+ SkColorSetRGB(65, 73, 87) :
+ SkColorSetRGB(224, 225, 227);
+
+ const SkColor bar_outer_color = enabled ?
+ SkColorSetRGB(0x4A, 0x4A, 0x4A) :
+ SkColorSetARGB(0x80, 0x4A, 0x4A, 0x4A);
+
+ const SkColor bar_inner_border_color =
+ SkColorSetARGB(0x3F, 0xFF, 0xFF, 0xFF); // 0.25 white
+ const SkColor bar_inner_shadow_color =
+ SkColorSetARGB(0x54, 0xFF, 0xFF, 0xFF); // 0.33 white
+
+ // Draw bar background
+ FillRoundRect(canvas,
+ 0, 0, progress_width, height(),
+ kCornerRadius,
+ bar_color_start,
+ bar_color_end,
+ false);
+
+ // Draw inner stroke and shadow if wide enough.
+ if (progress_width > 2 * kBorderWidth) {
+ canvas->GetSkCanvas()->save();
+
+ SkPath inner_path;
+ AddRoundRectPathWithPadding(
+ 0, 0, progress_width, height(),
+ kCornerRadius,
+ SkIntToScalar(kBorderWidth),
+ &inner_path);
+ canvas->GetSkCanvas()->clipPath(inner_path);
+
+ // Draw bar inner stroke
+ StrokeRoundRect(canvas,
+ kBorderWidth, kBorderWidth,
+ progress_width - 2 * kBorderWidth,
+ height() - 2 * kBorderWidth,
+ kCornerRadius - kBorderWidth,
+ bar_inner_border_color,
+ kBorderWidth);
+
+ // Draw bar inner shadow
+ StrokeRoundRect(canvas,
+ 0, kBorderWidth, progress_width, height(),
+ kCornerRadius,
+ bar_inner_shadow_color,
+ kBorderWidth);
+
+ canvas->GetSkCanvas()->restore();
+ }
+
+ // Draw bar stroke
+ StrokeRoundRect(canvas,
+ 0, 0, progress_width, height(),
+ kCornerRadius,
+ bar_outer_color,
+ kBorderWidth);
+ }
+#else
+ SkColor bar_color_start = SkColorSetRGB(81, 138, 223);
+ SkColor bar_color_end = SkColorSetRGB(51, 103, 205);
+ SkColor background_color_start = SkColorSetRGB(212, 212, 212);
+ SkColor background_color_end = SkColorSetRGB(252, 252, 252);
+ SkColor border_color = SkColorSetRGB(144, 144, 144);
+
+ FillRoundRect(canvas,
+ 0, 0, width(), height(),
+ kCornerRadius,
+ background_color_start,
+ background_color_end,
+ false);
+ if (progress_width > 1) {
+ FillRoundRect(canvas,
+ 0, 0,
+ progress_width, height(),
+ kCornerRadius,
+ bar_color_start,
+ bar_color_end,
+ false);
+ }
+ StrokeRoundRect(canvas,
+ 0, 0,
+ width(), height(),
+ kCornerRadius,
+ border_color,
+ kBorderWidth);
+#endif
+}
+
+} // namespace views
diff --git a/ui/views/controls/progress_bar.h b/ui/views/controls/progress_bar.h
new file mode 100644
index 0000000..02674de
--- /dev/null
+++ b/ui/views/controls/progress_bar.h
@@ -0,0 +1,65 @@
+// 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 UI_VIEWS_CONTROLS_PROGRESS_BAR_H_
+#define UI_VIEWS_CONTROLS_PROGRESS_BAR_H_
+#pragma once
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "views/view.h"
+
+namespace views {
+
+// Progress bar is a control that indicates progress visually.
+class VIEWS_EXPORT ProgressBar : public View {
+ public:
+ // The value range defaults to [0.0, 1.0].
+ ProgressBar();
+ virtual ~ProgressBar();
+
+ double current_value() const { return current_value_; }
+
+ // Sets the inclusive range of values to be displayed. Values outside of the
+ // range will be capped when displayed.
+ void SetDisplayRange(double min_display_value, double max_display_value);
+
+ // Sets the current value. Values outside of the range [min_display_value_,
+ // max_display_value_] will be stored unmodified and capped for display.
+ void SetValue(double value);
+
+ // Sets the tooltip text. Default behavior for a progress bar is to show no
+ // tooltip on mouse hover. Calling this lets you set a custom tooltip. To
+ // revert to default behavior, call this with an empty string.
+ void SetTooltipText(const string16& tooltip_text);
+
+ // Overridden from View:
+ virtual bool GetTooltipText(const gfx::Point& p,
+ string16* tooltip) const OVERRIDE;
+ virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE;
+
+ private:
+ static const char kViewClassName[];
+
+ // Overridden from View:
+ virtual gfx::Size GetPreferredSize() OVERRIDE;
+ virtual std::string GetClassName() const OVERRIDE;
+ virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
+
+ // Inclusive range used when displaying values.
+ double min_display_value_;
+ double max_display_value_;
+
+ // Current value. May be outside of [min_display_value_, max_display_value_].
+ double current_value_;
+
+ // Tooltip text.
+ string16 tooltip_text_;
+
+ DISALLOW_COPY_AND_ASSIGN(ProgressBar);
+};
+
+} // namespace views
+
+#endif // UI_VIEWS_CONTROLS_PROGRESS_BAR_H_
diff --git a/ui/views/controls/progress_bar_unittest.cc b/ui/views/controls/progress_bar_unittest.cc
new file mode 100644
index 0000000..71cdb20
--- /dev/null
+++ b/ui/views/controls/progress_bar_unittest.cc
@@ -0,0 +1,35 @@
+// 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 "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/accessibility/accessible_view_state.h"
+#include "ui/views/controls/progress_bar.h"
+
+namespace views {
+
+TEST(ProgressBarTest, TooltipTextProperty) {
+ ProgressBar bar;
+ string16 tooltip = ASCIIToUTF16("Some text");
+ EXPECT_FALSE(bar.GetTooltipText(gfx::Point(), &tooltip));
+ EXPECT_EQ(string16(), tooltip);
+ string16 tooltip_text = ASCIIToUTF16("My progress");
+ bar.SetTooltipText(tooltip_text);
+ EXPECT_TRUE(bar.GetTooltipText(gfx::Point(), &tooltip));
+ EXPECT_EQ(tooltip_text, tooltip);
+}
+
+TEST(ProgressBarTest, Accessibility) {
+ ProgressBar bar;
+ bar.SetValue(62);
+
+ ui::AccessibleViewState state;
+ bar.GetAccessibleState(&state);
+ EXPECT_EQ(ui::AccessibilityTypes::ROLE_PROGRESSBAR, state.role);
+ EXPECT_EQ(string16(), state.name);
+ EXPECT_TRUE(ui::AccessibilityTypes::STATE_READONLY & state.state);
+}
+
+} // namespace views
diff --git a/ui/views/controls/resize_area.cc b/ui/views/controls/resize_area.cc
new file mode 100644
index 0000000..832ebec
--- /dev/null
+++ b/ui/views/controls/resize_area.cc
@@ -0,0 +1,94 @@
+// 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 "ui/views/controls/resize_area.h"
+
+#include "base/logging.h"
+#include "ui/base/accessibility/accessible_view_state.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/views/controls/resize_area_delegate.h"
+
+#if defined(OS_LINUX)
+#include "ui/gfx/gtk_util.h"
+#endif
+
+#if defined(USE_AURA)
+#include "ui/aura/cursor.h"
+#endif
+
+namespace views {
+
+const char ResizeArea::kViewClassName[] = "views/ResizeArea";
+
+////////////////////////////////////////////////////////////////////////////////
+// ResizeArea
+
+ResizeArea::ResizeArea(ResizeAreaDelegate* delegate)
+ : delegate_(delegate),
+ initial_position_(0) {
+}
+
+ResizeArea::~ResizeArea() {
+}
+
+std::string ResizeArea::GetClassName() const {
+ return kViewClassName;
+}
+
+gfx::NativeCursor ResizeArea::GetCursor(const MouseEvent& event) {
+ if (!IsEnabled())
+ return gfx::kNullCursor;
+#if defined(USE_AURA)
+ return aura::kCursorEastWestResize;
+#elif defined(OS_WIN)
+ static HCURSOR g_resize_cursor = LoadCursor(NULL, IDC_SIZEWE);
+ return g_resize_cursor;
+#elif defined(OS_LINUX)
+ return gfx::GetCursor(GDK_SB_H_DOUBLE_ARROW);
+#endif
+}
+
+bool ResizeArea::OnMousePressed(const views::MouseEvent& event) {
+ if (!event.IsOnlyLeftMouseButton())
+ return false;
+
+ // The resize area obviously will move once you start dragging so we need to
+ // convert coordinates to screen coordinates so that we don't lose our
+ // bearings.
+ gfx::Point point(event.x(), 0);
+ View::ConvertPointToScreen(this, &point);
+ initial_position_ = point.x();
+
+ return true;
+}
+
+bool ResizeArea::OnMouseDragged(const views::MouseEvent& event) {
+ if (!event.IsLeftMouseButton())
+ return false;
+
+ ReportResizeAmount(event.x(), false);
+ return true;
+}
+
+void ResizeArea::OnMouseReleased(const views::MouseEvent& event) {
+ ReportResizeAmount(event.x(), true);
+}
+
+void ResizeArea::OnMouseCaptureLost() {
+ ReportResizeAmount(initial_position_, true);
+}
+
+void ResizeArea::GetAccessibleState(ui::AccessibleViewState* state) {
+ state->role = ui::AccessibilityTypes::ROLE_SEPARATOR;
+}
+
+void ResizeArea::ReportResizeAmount(int resize_amount, bool last_update) {
+ gfx::Point point(resize_amount, 0);
+ View::ConvertPointToScreen(this, &point);
+ resize_amount = point.x() - initial_position_;
+ delegate_->OnResize(base::i18n::IsRTL() ? -resize_amount : resize_amount,
+ last_update);
+}
+
+} // namespace views
diff --git a/ui/views/controls/resize_area.h b/ui/views/controls/resize_area.h
new file mode 100644
index 0000000..1cebd9d
--- /dev/null
+++ b/ui/views/controls/resize_area.h
@@ -0,0 +1,54 @@
+// 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 UI_VIEWS_CONTROLS_RESIZE_AREA_H_
+#define UI_VIEWS_CONTROLS_RESIZE_AREA_H_
+#pragma once
+
+#include <string>
+
+#include "views/view.h"
+
+namespace views {
+
+class ResizeAreaDelegate;
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// An invisible area that acts like a horizontal resizer.
+//
+////////////////////////////////////////////////////////////////////////////////
+class VIEWS_EXPORT ResizeArea : public View {
+ public:
+ static const char kViewClassName[];
+
+ explicit ResizeArea(ResizeAreaDelegate* delegate);
+ virtual ~ResizeArea();
+
+ // Overridden from views::View:
+ virtual std::string GetClassName() const OVERRIDE;
+ virtual gfx::NativeCursor GetCursor(const views::MouseEvent& event) OVERRIDE;
+ virtual bool OnMousePressed(const views::MouseEvent& event) OVERRIDE;
+ virtual bool OnMouseDragged(const views::MouseEvent& event) OVERRIDE;
+ virtual void OnMouseReleased(const views::MouseEvent& event) OVERRIDE;
+ virtual void OnMouseCaptureLost() OVERRIDE;
+ virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE;
+
+ private:
+ // Report the amount the user resized by to the delegate, accounting for
+ // directionality.
+ void ReportResizeAmount(int resize_amount, bool last_update);
+
+ // The delegate to notify when we have updates.
+ ResizeAreaDelegate* delegate_;
+
+ // The mouse position at start (in screen coordinates).
+ int initial_position_;
+
+ DISALLOW_COPY_AND_ASSIGN(ResizeArea);
+};
+
+} // namespace views
+
+#endif // UI_VIEWS_CONTROLS_RESIZE_AREA_H_
diff --git a/ui/views/controls/resize_area_delegate.h b/ui/views/controls/resize_area_delegate.h
new file mode 100644
index 0000000..ad8b3bf
--- /dev/null
+++ b/ui/views/controls/resize_area_delegate.h
@@ -0,0 +1,29 @@
+// 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 UI_VIEWS_CONTROLS_RESIZE_AREA_DELEGATE_H_
+#define UI_VIEWS_CONTROLS_RESIZE_AREA_DELEGATE_H_
+#pragma once
+
+namespace views {
+
+// An interface implemented by objects that want to be notified about the resize
+// event.
+class ResizeAreaDelegate {
+ public:
+ // OnResize is sent when resizing is detected. |resize_amount| specifies the
+ // number of pixels that the user wants to resize by, and can be negative or
+ // positive (depending on direction of dragging and flips according to
+ // locale directionality: dragging to the left in LTR locales gives negative
+ // |resize_amount| but positive amount for RTL). |done_resizing| is true if
+ // the user has released the mouse.
+ virtual void OnResize(int resize_amount, bool done_resizing) = 0;
+
+ protected:
+ virtual ~ResizeAreaDelegate() {}
+};
+
+} // namespace views
+
+#endif // UI_VIEWS_CONTROLS_RESIZE_AREA_DELEGATE_H_
diff --git a/ui/views/controls/scroll_view.cc b/ui/views/controls/scroll_view.cc
new file mode 100644
index 0000000..8d67d28
--- /dev/null
+++ b/ui/views/controls/scroll_view.cc
@@ -0,0 +1,501 @@
+// 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 "ui/views/controls/scroll_view.h"
+
+#include "base/logging.h"
+#include "ui/views/controls/scrollbar/native_scroll_bar.h"
+#include "ui/views/widget/root_view.h"
+
+namespace views {
+
+const char* const ScrollView::kViewClassName = "views/ScrollView";
+
+// Viewport contains the contents View of the ScrollView.
+class Viewport : public View {
+ public:
+ Viewport() {}
+ virtual ~Viewport() {}
+
+ virtual std::string GetClassName() const {
+ return "views/Viewport";
+ }
+
+ virtual void ScrollRectToVisible(const gfx::Rect& rect) {
+ if (!has_children() || !parent())
+ return;
+
+ View* contents = child_at(0);
+ gfx::Rect scroll_rect(rect);
+ scroll_rect.Offset(-contents->x(), -contents->y());
+ static_cast<ScrollView*>(parent())->ScrollContentsRegionToBeVisible(
+ scroll_rect);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Viewport);
+};
+
+
+ScrollView::ScrollView() {
+ Init(new NativeScrollBar(true), new NativeScrollBar(false), NULL);
+}
+
+ScrollView::ScrollView(ScrollBar* horizontal_scrollbar,
+ ScrollBar* vertical_scrollbar,
+ View* resize_corner) {
+ Init(horizontal_scrollbar, vertical_scrollbar, resize_corner);
+}
+
+ScrollView::~ScrollView() {
+ // If scrollbars are currently not used, delete them
+ if (!horiz_sb_->parent())
+ delete horiz_sb_;
+
+ if (!vert_sb_->parent())
+ delete vert_sb_;
+
+ if (resize_corner_ && !resize_corner_->parent())
+ delete resize_corner_;
+}
+
+void ScrollView::SetContents(View* a_view) {
+ if (contents_ && contents_ != a_view) {
+ viewport_->RemoveChildView(contents_);
+ delete contents_;
+ contents_ = NULL;
+ }
+
+ if (a_view) {
+ contents_ = a_view;
+ viewport_->AddChildView(contents_);
+ }
+
+ Layout();
+}
+
+View* ScrollView::GetContents() const {
+ return contents_;
+}
+
+void ScrollView::Init(ScrollBar* horizontal_scrollbar,
+ ScrollBar* vertical_scrollbar,
+ View* resize_corner) {
+ DCHECK(horizontal_scrollbar && vertical_scrollbar);
+
+ contents_ = NULL;
+ horiz_sb_ = horizontal_scrollbar;
+ vert_sb_ = vertical_scrollbar;
+ resize_corner_ = resize_corner;
+
+ viewport_ = new Viewport();
+ AddChildView(viewport_);
+
+ // Don't add the scrollbars as children until we discover we need them
+ // (ShowOrHideScrollBar).
+ horiz_sb_->SetVisible(false);
+ horiz_sb_->set_controller(this);
+ vert_sb_->SetVisible(false);
+ vert_sb_->set_controller(this);
+ if (resize_corner_)
+ resize_corner_->SetVisible(false);
+}
+
+// Make sure that a single scrollbar is created and visible as needed
+void ScrollView::SetControlVisibility(View* control, bool should_show) {
+ if (!control)
+ return;
+ if (should_show) {
+ if (!control->IsVisible()) {
+ AddChildView(control);
+ control->SetVisible(true);
+ }
+ } else {
+ RemoveChildView(control);
+ control->SetVisible(false);
+ }
+}
+
+void ScrollView::ComputeScrollBarsVisibility(const gfx::Size& vp_size,
+ const gfx::Size& content_size,
+ bool* horiz_is_shown,
+ bool* vert_is_shown) const {
+ // Try to fit both ways first, then try vertical bar only, then horizontal
+ // bar only, then defaults to both shown.
+ if (content_size.width() <= vp_size.width() &&
+ content_size.height() <= vp_size.height()) {
+ *horiz_is_shown = false;
+ *vert_is_shown = false;
+ } else if (content_size.width() <= vp_size.width() - GetScrollBarWidth()) {
+ *horiz_is_shown = false;
+ *vert_is_shown = true;
+ } else if (content_size.height() <= vp_size.height() - GetScrollBarHeight()) {
+ *horiz_is_shown = true;
+ *vert_is_shown = false;
+ } else {
+ *horiz_is_shown = true;
+ *vert_is_shown = true;
+ }
+}
+
+void ScrollView::Layout() {
+ // Most views will want to auto-fit the available space. Most of them want to
+ // use the all available width (without overflowing) and only overflow in
+ // height. Examples are HistoryView, MostVisitedView, DownloadTabView, etc.
+ // Other views want to fit in both ways. An example is PrintView. To make both
+ // happy, assume a vertical scrollbar but no horizontal scrollbar. To
+ // override this default behavior, the inner view has to calculate the
+ // available space, used ComputeScrollBarsVisibility() to use the same
+ // calculation that is done here and sets its bound to fit within.
+ gfx::Rect viewport_bounds = GetLocalBounds();
+ // Realign it to 0 so it can be used as-is for SetBounds().
+ viewport_bounds.set_origin(gfx::Point(0, 0));
+ // viewport_size is the total client space available.
+ gfx::Size viewport_size = viewport_bounds.size();
+ if (viewport_bounds.IsEmpty()) {
+ // There's nothing to layout.
+ return;
+ }
+
+ // Assumes a vertical scrollbar since most the current views are designed for
+ // this.
+ int horiz_sb_height = GetScrollBarHeight();
+ int vert_sb_width = GetScrollBarWidth();
+ viewport_bounds.set_width(viewport_bounds.width() - vert_sb_width);
+ // Update the bounds right now so the inner views can fit in it.
+ viewport_->SetBoundsRect(viewport_bounds);
+
+ // Give contents_ a chance to update its bounds if it depends on the
+ // viewport.
+ if (contents_)
+ contents_->Layout();
+
+ bool should_layout_contents = false;
+ bool horiz_sb_required = false;
+ bool vert_sb_required = false;
+ if (contents_) {
+ gfx::Size content_size = contents_->size();
+ ComputeScrollBarsVisibility(viewport_size,
+ content_size,
+ &horiz_sb_required,
+ &vert_sb_required);
+ }
+ bool resize_corner_required = resize_corner_ && horiz_sb_required &&
+ vert_sb_required;
+ // Take action.
+ SetControlVisibility(horiz_sb_, horiz_sb_required);
+ SetControlVisibility(vert_sb_, vert_sb_required);
+ SetControlVisibility(resize_corner_, resize_corner_required);
+
+ // Non-default.
+ if (horiz_sb_required) {
+ viewport_bounds.set_height(
+ std::max(0, viewport_bounds.height() - horiz_sb_height));
+ should_layout_contents = true;
+ }
+ // Default.
+ if (!vert_sb_required) {
+ viewport_bounds.set_width(viewport_bounds.width() + vert_sb_width);
+ should_layout_contents = true;
+ }
+
+ if (horiz_sb_required) {
+ horiz_sb_->SetBounds(0,
+ viewport_bounds.bottom(),
+ viewport_bounds.right(),
+ horiz_sb_height);
+ }
+ if (vert_sb_required) {
+ vert_sb_->SetBounds(viewport_bounds.right(),
+ 0,
+ vert_sb_width,
+ viewport_bounds.bottom());
+ }
+ if (resize_corner_required) {
+ // Show the resize corner.
+ resize_corner_->SetBounds(viewport_bounds.right(),
+ viewport_bounds.bottom(),
+ vert_sb_width,
+ horiz_sb_height);
+ }
+
+ // Update to the real client size with the visible scrollbars.
+ viewport_->SetBoundsRect(viewport_bounds);
+ if (should_layout_contents && contents_)
+ contents_->Layout();
+
+ CheckScrollBounds();
+ SchedulePaint();
+ UpdateScrollBarPositions();
+}
+
+int ScrollView::CheckScrollBounds(int viewport_size,
+ int content_size,
+ int current_pos) {
+ int max = std::max(content_size - viewport_size, 0);
+ if (current_pos < 0)
+ current_pos = 0;
+ else if (current_pos > max)
+ current_pos = max;
+ return current_pos;
+}
+
+void ScrollView::CheckScrollBounds() {
+ if (contents_) {
+ int x, y;
+
+ x = CheckScrollBounds(viewport_->width(),
+ contents_->width(),
+ -contents_->x());
+ y = CheckScrollBounds(viewport_->height(),
+ contents_->height(),
+ -contents_->y());
+
+ // This is no op if bounds are the same
+ contents_->SetBounds(-x, -y, contents_->width(), contents_->height());
+ }
+}
+
+gfx::Rect ScrollView::GetVisibleRect() const {
+ if (!contents_)
+ return gfx::Rect();
+
+ const int x =
+ (horiz_sb_ && horiz_sb_->IsVisible()) ? horiz_sb_->GetPosition() : 0;
+ const int y =
+ (vert_sb_ && vert_sb_->IsVisible()) ? vert_sb_->GetPosition() : 0;
+ return gfx::Rect(x, y, viewport_->width(), viewport_->height());
+}
+
+void ScrollView::ScrollContentsRegionToBeVisible(const gfx::Rect& rect) {
+ if (!contents_ || ((!horiz_sb_ || !horiz_sb_->IsVisible()) &&
+ (!vert_sb_ || !vert_sb_->IsVisible()))) {
+ return;
+ }
+
+ // Figure out the maximums for this scroll view.
+ const int contents_max_x =
+ std::max(viewport_->width(), contents_->width());
+ const int contents_max_y =
+ std::max(viewport_->height(), contents_->height());
+
+ // Make sure x and y are within the bounds of [0,contents_max_*].
+ int x = std::max(0, std::min(contents_max_x, rect.x()));
+ int y = std::max(0, std::min(contents_max_y, rect.y()));
+
+ // Figure out how far and down the rectangle will go taking width
+ // and height into account. This will be "clipped" by the viewport.
+ const int max_x = std::min(contents_max_x,
+ x + std::min(rect.width(), viewport_->width()));
+ const int max_y = std::min(contents_max_y,
+ y + std::min(rect.height(), viewport_->height()));
+
+ // See if the rect is already visible. Note the width is (max_x - x)
+ // and the height is (max_y - y) to take into account the clipping of
+ // either viewport or the content size.
+ const gfx::Rect vis_rect = GetVisibleRect();
+ if (vis_rect.Contains(gfx::Rect(x, y, max_x - x, max_y - y)))
+ return;
+
+ // Shift contents_'s X and Y so that the region is visible. If we
+ // need to shift up or left from where we currently are then we need
+ // to get it so that the content appears in the upper/left
+ // corner. This is done by setting the offset to -X or -Y. For down
+ // or right shifts we need to make sure it appears in the
+ // lower/right corner. This is calculated by taking max_x or max_y
+ // and scaling it back by the size of the viewport.
+ const int new_x =
+ (vis_rect.x() > x) ? x : std::max(0, max_x - viewport_->width());
+ const int new_y =
+ (vis_rect.y() > y) ? y : std::max(0, max_y - viewport_->height());
+
+ contents_->SetX(-new_x);
+ contents_->SetY(-new_y);
+ UpdateScrollBarPositions();
+}
+
+void ScrollView::UpdateScrollBarPositions() {
+ if (!contents_) {
+ return;
+ }
+
+ if (horiz_sb_->IsVisible()) {
+ int vw = viewport_->width();
+ int cw = contents_->width();
+ int origin = contents_->x();
+ horiz_sb_->Update(vw, cw, -origin);
+ }
+ if (vert_sb_->IsVisible()) {
+ int vh = viewport_->height();
+ int ch = contents_->height();
+ int origin = contents_->y();
+ vert_sb_->Update(vh, ch, -origin);
+ }
+}
+
+// TODO(ACW): We should really use ScrollWindowEx as needed
+void ScrollView::ScrollToPosition(ScrollBar* source, int position) {
+ if (!contents_)
+ return;
+
+ if (source == horiz_sb_ && horiz_sb_->IsVisible()) {
+ int vw = viewport_->width();
+ int cw = contents_->width();
+ int origin = contents_->x();
+ if (-origin != position) {
+ int max_pos = std::max(0, cw - vw);
+ if (position < 0)
+ position = 0;
+ else if (position > max_pos)
+ position = max_pos;
+ contents_->SetX(-position);
+ contents_->SchedulePaintInRect(contents_->GetVisibleBounds());
+ }
+ } else if (source == vert_sb_ && vert_sb_->IsVisible()) {
+ int vh = viewport_->height();
+ int ch = contents_->height();
+ int origin = contents_->y();
+ if (-origin != position) {
+ int max_pos = std::max(0, ch - vh);
+ if (position < 0)
+ position = 0;
+ else if (position > max_pos)
+ position = max_pos;
+ contents_->SetY(-position);
+ contents_->SchedulePaintInRect(contents_->GetVisibleBounds());
+ }
+ }
+}
+
+int ScrollView::GetScrollIncrement(ScrollBar* source, bool is_page,
+ bool is_positive) {
+ bool is_horizontal = source->IsHorizontal();
+ int amount = 0;
+ View* view = GetContents();
+ if (view) {
+ if (is_page)
+ amount = view->GetPageScrollIncrement(this, is_horizontal, is_positive);
+ else
+ amount = view->GetLineScrollIncrement(this, is_horizontal, is_positive);
+ if (amount > 0)
+ return amount;
+ }
+ // No view, or the view didn't return a valid amount.
+ if (is_page)
+ return is_horizontal ? viewport_->width() : viewport_->height();
+ return is_horizontal ? viewport_->width() / 5 : viewport_->height() / 5;
+}
+
+bool ScrollView::OnKeyPressed(const KeyEvent& event) {
+ bool processed = false;
+
+ // Give vertical scrollbar priority
+ if (vert_sb_->IsVisible()) {
+ processed = vert_sb_->OnKeyPressed(event);
+ }
+
+ if (!processed && horiz_sb_->IsVisible()) {
+ processed = horiz_sb_->OnKeyPressed(event);
+ }
+ return processed;
+}
+
+bool ScrollView::OnMouseWheel(const MouseWheelEvent& e) {
+ bool processed = false;
+ // Give vertical scrollbar priority
+ if (vert_sb_->IsVisible()) {
+ processed = vert_sb_->OnMouseWheel(e);
+ }
+ if (!processed && horiz_sb_->IsVisible()) {
+ processed = horiz_sb_->OnMouseWheel(e);
+ }
+ return processed;
+}
+
+std::string ScrollView::GetClassName() const {
+ return kViewClassName;
+}
+
+int ScrollView::GetScrollBarWidth() const {
+ return vert_sb_->GetLayoutSize();
+}
+
+int ScrollView::GetScrollBarHeight() const {
+ return horiz_sb_->GetLayoutSize();
+}
+
+// VariableRowHeightScrollHelper ----------------------------------------------
+
+VariableRowHeightScrollHelper::VariableRowHeightScrollHelper(
+ Controller* controller) : controller_(controller) {
+}
+
+VariableRowHeightScrollHelper::~VariableRowHeightScrollHelper() {
+}
+
+int VariableRowHeightScrollHelper::GetPageScrollIncrement(
+ ScrollView* scroll_view, bool is_horizontal, bool is_positive) {
+ if (is_horizontal)
+ return 0;
+ // y coordinate is most likely negative.
+ int y = abs(scroll_view->GetContents()->y());
+ int vis_height = scroll_view->GetContents()->parent()->height();
+ if (is_positive) {
+ // Align the bottom most row to the top of the view.
+ int bottom = std::min(scroll_view->GetContents()->height() - 1,
+ y + vis_height);
+ RowInfo bottom_row_info = GetRowInfo(bottom);
+ // If 0, ScrollView will provide a default value.
+ return std::max(0, bottom_row_info.origin - y);
+ } else {
+ // Align the row on the previous page to to the top of the view.
+ int last_page_y = y - vis_height;
+ RowInfo last_page_info = GetRowInfo(std::max(0, last_page_y));
+ if (last_page_y != last_page_info.origin)
+ return std::max(0, y - last_page_info.origin - last_page_info.height);
+ return std::max(0, y - last_page_info.origin);
+ }
+}
+
+int VariableRowHeightScrollHelper::GetLineScrollIncrement(
+ ScrollView* scroll_view, bool is_horizontal, bool is_positive) {
+ if (is_horizontal)
+ return 0;
+ // y coordinate is most likely negative.
+ int y = abs(scroll_view->GetContents()->y());
+ RowInfo row = GetRowInfo(y);
+ if (is_positive) {
+ return row.height - (y - row.origin);
+ } else if (y == row.origin) {
+ row = GetRowInfo(std::max(0, row.origin - 1));
+ return y - row.origin;
+ } else {
+ return y - row.origin;
+ }
+}
+
+VariableRowHeightScrollHelper::RowInfo
+ VariableRowHeightScrollHelper::GetRowInfo(int y) {
+ return controller_->GetRowInfo(y);
+}
+
+// FixedRowHeightScrollHelper -----------------------------------------------
+
+FixedRowHeightScrollHelper::FixedRowHeightScrollHelper(int top_margin,
+ int row_height)
+ : VariableRowHeightScrollHelper(NULL),
+ top_margin_(top_margin),
+ row_height_(row_height) {
+ DCHECK_GT(row_height, 0);
+}
+
+VariableRowHeightScrollHelper::RowInfo
+ FixedRowHeightScrollHelper::GetRowInfo(int y) {
+ if (y < top_margin_)
+ return RowInfo(0, top_margin_);
+ return RowInfo((y - top_margin_) / row_height_ * row_height_ + top_margin_,
+ row_height_);
+}
+
+} // namespace views
diff --git a/ui/views/controls/scroll_view.h b/ui/views/controls/scroll_view.h
new file mode 100644
index 0000000..a7f84fd
--- /dev/null
+++ b/ui/views/controls/scroll_view.h
@@ -0,0 +1,207 @@
+// 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 UI_VIEWS_CONTROLS_SCROLL_VIEW_H_
+#define UI_VIEWS_CONTROLS_SCROLL_VIEW_H_
+#pragma once
+
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "ui/views/controls/scrollbar/scroll_bar.h"
+
+namespace views {
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// ScrollView class
+//
+// A ScrollView is used to make any View scrollable. The view is added to
+// a viewport which takes care of clipping.
+//
+// In this current implementation both horizontal and vertical scrollbars are
+// added as needed.
+//
+// The scrollview supports keyboard UI and mousewheel.
+//
+/////////////////////////////////////////////////////////////////////////////
+
+class VIEWS_EXPORT ScrollView : public View, public ScrollBarController {
+ public:
+ static const char* const kViewClassName;
+
+ ScrollView();
+ // Initialize with specific views. resize_corner is optional.
+ ScrollView(ScrollBar* horizontal_scrollbar,
+ ScrollBar* vertical_scrollbar,
+ View* resize_corner);
+ virtual ~ScrollView();
+
+ // Set the contents. Any previous contents will be deleted. The contents
+ // is the view that needs to scroll.
+ void SetContents(View* a_view);
+ View* GetContents() const;
+
+ // Overridden to layout the viewport and scrollbars.
+ virtual void Layout() OVERRIDE;
+
+ // Returns the visible region of the content View.
+ gfx::Rect GetVisibleRect() const;
+
+ // Scrolls the minimum amount necessary to make the specified rectangle
+ // visible, in the coordinates of the contents view. The specified rectangle
+ // is constrained by the bounds of the contents view. This has no effect if
+ // the contents have not been set.
+ //
+ // Client code should use ScrollRectToVisible, which invokes this
+ // appropriately.
+ void ScrollContentsRegionToBeVisible(const gfx::Rect& rect);
+
+ // ScrollBarController.
+ // NOTE: this is intended to be invoked by the ScrollBar, and NOT general
+ // client code.
+ // See also ScrollRectToVisible.
+ virtual void ScrollToPosition(ScrollBar* source, int position) OVERRIDE;
+
+ // Returns the amount to scroll relative to the visible bounds. This invokes
+ // either GetPageScrollIncrement or GetLineScrollIncrement to determine the
+ // amount to scroll. If the view returns 0 (or a negative value) a default
+ // value is used.
+ virtual int GetScrollIncrement(ScrollBar* source,
+ bool is_page,
+ bool is_positive) OVERRIDE;
+
+ // Keyboard events
+ virtual bool OnKeyPressed(const KeyEvent& event) OVERRIDE;
+ virtual bool OnMouseWheel(const MouseWheelEvent& e) OVERRIDE;
+
+ virtual std::string GetClassName() const OVERRIDE;
+
+ // Retrieves the vertical scrollbar width.
+ int GetScrollBarWidth() const;
+
+ // Retrieves the horizontal scrollbar height.
+ int GetScrollBarHeight() const;
+
+ // Computes the visibility of both scrollbars, taking in account the view port
+ // and content sizes.
+ void ComputeScrollBarsVisibility(const gfx::Size& viewport_size,
+ const gfx::Size& content_size,
+ bool* horiz_is_shown,
+ bool* vert_is_shown) const;
+
+ ScrollBar* horizontal_scroll_bar() const { return horiz_sb_; }
+
+ ScrollBar* vertical_scroll_bar() const { return vert_sb_; }
+
+ private:
+ // Initialize the ScrollView. resize_corner is optional.
+ void Init(ScrollBar* horizontal_scrollbar,
+ ScrollBar* vertical_scrollbar,
+ View* resize_corner);
+
+ // Shows or hides the scrollbar/resize_corner based on the value of
+ // |should_show|.
+ void SetControlVisibility(View* control, bool should_show);
+
+ // Update the scrollbars positions given viewport and content sizes.
+ void UpdateScrollBarPositions();
+
+ // Make sure the content is not scrolled out of bounds
+ void CheckScrollBounds();
+
+ // Make sure the content is not scrolled out of bounds in one dimension
+ int CheckScrollBounds(int viewport_size, int content_size, int current_pos);
+
+ // The clipping viewport. Content is added to that view.
+ View* viewport_;
+
+ // The current contents
+ View* contents_;
+
+ // Horizontal scrollbar.
+ ScrollBar* horiz_sb_;
+
+ // Vertical scrollbar.
+ ScrollBar* vert_sb_;
+
+ // Resize corner.
+ View* resize_corner_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScrollView);
+};
+
+// VariableRowHeightScrollHelper is intended for views that contain rows of
+// varying height. To use a VariableRowHeightScrollHelper create one supplying
+// a Controller and delegate GetPageScrollIncrement and GetLineScrollIncrement
+// to the helper. VariableRowHeightScrollHelper calls back to the
+// Controller to determine row boundaries.
+class VariableRowHeightScrollHelper {
+ public:
+ // The origin and height of a row.
+ struct RowInfo {
+ RowInfo(int origin, int height) : origin(origin), height(height) {}
+
+ // Origin of the row.
+ int origin;
+
+ // Height of the row.
+ int height;
+ };
+
+ // Used to determine row boundaries.
+ class Controller {
+ public:
+ // Returns the origin and size of the row at the specified location.
+ virtual VariableRowHeightScrollHelper::RowInfo GetRowInfo(int y) = 0;
+ };
+
+ // Creates a new VariableRowHeightScrollHelper. Controller is
+ // NOT deleted by this VariableRowHeightScrollHelper.
+ explicit VariableRowHeightScrollHelper(Controller* controller);
+ virtual ~VariableRowHeightScrollHelper();
+
+ // Delegate the View methods of the same name to these. The scroll amount is
+ // determined by querying the Controller for the appropriate row to scroll
+ // to.
+ int GetPageScrollIncrement(ScrollView* scroll_view,
+ bool is_horizontal, bool is_positive);
+ int GetLineScrollIncrement(ScrollView* scroll_view,
+ bool is_horizontal, bool is_positive);
+
+ protected:
+ // Returns the row information for the row at the specified location. This
+ // calls through to the method of the same name on the controller.
+ virtual RowInfo GetRowInfo(int y);
+
+ private:
+ Controller* controller_;
+
+ DISALLOW_COPY_AND_ASSIGN(VariableRowHeightScrollHelper);
+};
+
+// FixedRowHeightScrollHelper is intended for views that contain fixed height
+// height rows. To use a FixedRowHeightScrollHelper delegate
+// GetPageScrollIncrement and GetLineScrollIncrement to it.
+class FixedRowHeightScrollHelper : public VariableRowHeightScrollHelper {
+ public:
+ // Creates a FixedRowHeightScrollHelper. top_margin gives the distance from
+ // the top of the view to the first row, and may be 0. row_height gives the
+ // height of each row.
+ FixedRowHeightScrollHelper(int top_margin, int row_height);
+
+ protected:
+ // Calculates the bounds of the row from the top margin and row height.
+ virtual RowInfo GetRowInfo(int y) OVERRIDE;
+
+ private:
+ int top_margin_;
+ int row_height_;
+
+ DISALLOW_COPY_AND_ASSIGN(FixedRowHeightScrollHelper);
+};
+
+} // namespace views
+
+#endif // UI_VIEWS_CONTROLS_SCROLL_VIEW_H_
diff --git a/ui/views/controls/scrollbar/base_scroll_bar.cc b/ui/views/controls/scrollbar/base_scroll_bar.cc
index 3afb238..54d4b44 100644
--- a/ui/views/controls/scrollbar/base_scroll_bar.cc
+++ b/ui/views/controls/scrollbar/base_scroll_bar.cc
@@ -18,9 +18,9 @@
#include "ui/gfx/canvas.h"
#include "ui/views/controls/menu/menu_item_view.h"
#include "ui/views/controls/menu/menu_runner.h"
+#include "ui/views/controls/scroll_view.h"
#include "ui/views/controls/scrollbar/base_scroll_bar_thumb.h"
#include "ui/views/widget/widget.h"
-#include "views/controls/scroll_view.h"
#if defined(OS_LINUX)
#include "ui/gfx/screen.h"
diff --git a/ui/views/controls/scrollbar/bitmap_scroll_bar.cc b/ui/views/controls/scrollbar/bitmap_scroll_bar.cc
index 6aa44d5..47c8f13 100644
--- a/ui/views/controls/scrollbar/bitmap_scroll_bar.cc
+++ b/ui/views/controls/scrollbar/bitmap_scroll_bar.cc
@@ -18,9 +18,9 @@
#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/canvas.h"
#include "ui/views/controls/menu/menu.h"
+#include "ui/views/controls/scroll_view.h"
#include "ui/views/controls/scrollbar/base_scroll_bar_thumb.h"
#include "ui/views/widget/widget.h"
-#include "views/controls/scroll_view.h"
#if defined(OS_LINUX)
#include "views/screen.h"
diff --git a/ui/views/controls/scrollbar/native_scroll_bar_gtk.h b/ui/views/controls/scrollbar/native_scroll_bar_gtk.h
index 909adfe..e9a5ed5 100644
--- a/ui/views/controls/scrollbar/native_scroll_bar_gtk.h
+++ b/ui/views/controls/scrollbar/native_scroll_bar_gtk.h
@@ -7,8 +7,8 @@
#pragma once
#include "base/compiler_specific.h"
+#include "ui/views/controls/native_control_gtk.h"
#include "ui/views/controls/scrollbar/native_scroll_bar_wrapper.h"
-#include "views/controls/native_control_gtk.h"
namespace views {
diff --git a/ui/views/controls/scrollbar/native_scroll_bar_views.cc b/ui/views/controls/scrollbar/native_scroll_bar_views.cc
index 29c5f6f..fba93d09 100644
--- a/ui/views/controls/scrollbar/native_scroll_bar_views.cc
+++ b/ui/views/controls/scrollbar/native_scroll_bar_views.cc
@@ -10,11 +10,11 @@
#include "ui/gfx/canvas_skia.h"
#include "ui/gfx/path.h"
#include "ui/views/controls/button/custom_button.h"
+#include "ui/views/controls/focusable_border.h"
#include "ui/views/controls/scrollbar/base_scroll_bar_button.h"
#include "ui/views/controls/scrollbar/base_scroll_bar_thumb.h"
#include "ui/views/controls/scrollbar/native_scroll_bar.h"
#include "ui/views/controls/scrollbar/scroll_bar.h"
-#include "views/controls/focusable_border.h"
namespace views {
diff --git a/ui/views/controls/scrollbar/native_scroll_bar_win.h b/ui/views/controls/scrollbar/native_scroll_bar_win.h
index 33e369e..1f7ec36 100644
--- a/ui/views/controls/scrollbar/native_scroll_bar_win.h
+++ b/ui/views/controls/scrollbar/native_scroll_bar_win.h
@@ -6,8 +6,8 @@
#define UI_VIEWS_CONTROLS_SCROLLBAR_NATIVE_SCROLL_BAR_WIN_H_
#pragma once
+#include "ui/views/controls/native_control_win.h"
#include "ui/views/controls/scrollbar/native_scroll_bar_wrapper.h"
-#include "views/controls/native_control_win.h"
namespace views {
diff --git a/ui/views/controls/separator.cc b/ui/views/controls/separator.cc
new file mode 100644
index 0000000..2842068
--- /dev/null
+++ b/ui/views/controls/separator.cc
@@ -0,0 +1,47 @@
+// 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 "ui/views/controls/separator.h"
+
+#include "ui/base/accessibility/accessible_view_state.h"
+#include "ui/gfx/canvas.h"
+
+namespace views {
+
+// static
+const char Separator::kViewClassName[] = "views/Separator";
+
+// The separator height in pixels.
+const int kSeparatorHeight = 1;
+
+// Default color of the separator.
+const SkColor kDefaultColor = SkColorSetARGB(255, 233, 233, 233);
+
+Separator::Separator() {
+ set_focusable(false);
+}
+
+Separator::~Separator() {
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Separator, View overrides:
+
+gfx::Size Separator::GetPreferredSize() {
+ return gfx::Size(width(), kSeparatorHeight);
+}
+
+void Separator::GetAccessibleState(ui::AccessibleViewState* state) {
+ state->role = ui::AccessibilityTypes::ROLE_SEPARATOR;
+}
+
+void Separator::Paint(gfx::Canvas* canvas) {
+ canvas->FillRect(kDefaultColor, bounds());
+}
+
+std::string Separator::GetClassName() const {
+ return kViewClassName;
+}
+
+} // namespace views
diff --git a/ui/views/controls/separator.h b/ui/views/controls/separator.h
new file mode 100644
index 0000000..f47d238
--- /dev/null
+++ b/ui/views/controls/separator.h
@@ -0,0 +1,38 @@
+// 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 UI_VIEWS_CONTROLS_SEPARATOR_H_
+#define UI_VIEWS_CONTROLS_SEPARATOR_H_
+#pragma once
+
+#include <string>
+
+#include "views/view.h"
+
+namespace views {
+
+// The Separator class is a view that shows a line used to visually separate
+// other views. The current implementation is only horizontal.
+
+class VIEWS_EXPORT Separator : public View {
+ public:
+ // The separator's class name.
+ static const char kViewClassName[];
+
+ Separator();
+ virtual ~Separator();
+
+ // Overridden from View:
+ virtual gfx::Size GetPreferredSize() OVERRIDE;
+ virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE;
+ virtual void Paint(gfx::Canvas* canvas) OVERRIDE;
+ virtual std::string GetClassName() const OVERRIDE;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Separator);
+};
+
+} // namespace views
+
+#endif // UI_VIEWS_CONTROLS_SEPARATOR_H_
diff --git a/ui/views/controls/single_split_view.cc b/ui/views/controls/single_split_view.cc
new file mode 100644
index 0000000..333c388
--- /dev/null
+++ b/ui/views/controls/single_split_view.cc
@@ -0,0 +1,263 @@
+// 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 "ui/views/controls/single_split_view.h"
+
+#if defined(TOOLKIT_USES_GTK)
+#include <gdk/gdk.h>
+#endif
+
+#include "skia/ext/skia_utils_win.h"
+#include "ui/base/accessibility/accessible_view_state.h"
+#include "ui/gfx/canvas.h"
+#include "ui/views/controls/single_split_view_listener.h"
+#include "views/background.h"
+
+#if defined(TOOLKIT_USES_GTK)
+#include "ui/gfx/gtk_util.h"
+#endif
+
+#if defined(USE_AURA)
+#include "ui/aura/cursor.h"
+#endif
+
+namespace views {
+
+// static
+const char SingleSplitView::kViewClassName[] =
+ "ui/views/controls/SingleSplitView";
+
+// Size of the divider in pixels.
+static const int kDividerSize = 4;
+
+SingleSplitView::SingleSplitView(View* leading,
+ View* trailing,
+ Orientation orientation,
+ SingleSplitViewListener* listener)
+ : is_horizontal_(orientation == HORIZONTAL_SPLIT),
+ divider_offset_(-1),
+ resize_leading_on_bounds_change_(true),
+ listener_(listener) {
+ AddChildView(leading);
+ AddChildView(trailing);
+#if defined(OS_WIN)
+ set_background(
+ views::Background::CreateSolidBackground(
+ skia::COLORREFToSkColor(GetSysColor(COLOR_3DFACE))));
+#endif
+}
+
+void SingleSplitView::Layout() {
+ gfx::Rect leading_bounds;
+ gfx::Rect trailing_bounds;
+ CalculateChildrenBounds(bounds(), &leading_bounds, &trailing_bounds);
+
+ if (has_children()) {
+ if (child_at(0)->IsVisible())
+ child_at(0)->SetBoundsRect(leading_bounds);
+ if (child_count() > 1) {
+ if (child_at(1)->IsVisible())
+ child_at(1)->SetBoundsRect(trailing_bounds);
+ }
+ }
+
+ SchedulePaint();
+
+ // Invoke super's implementation so that the children are layed out.
+ View::Layout();
+}
+
+std::string SingleSplitView::GetClassName() const {
+ return kViewClassName;
+}
+
+void SingleSplitView::GetAccessibleState(ui::AccessibleViewState* state) {
+ state->role = ui::AccessibilityTypes::ROLE_GROUPING;
+ state->name = accessible_name_;
+}
+
+gfx::Size SingleSplitView::GetPreferredSize() {
+ int width = 0;
+ int height = 0;
+ for (int i = 0; i < 2 && i < child_count(); ++i) {
+ View* view = child_at(i);
+ gfx::Size pref = view->GetPreferredSize();
+ if (is_horizontal_) {
+ width += pref.width();
+ height = std::max(height, pref.height());
+ } else {
+ width = std::max(width, pref.width());
+ height += pref.height();
+ }
+ }
+ if (is_horizontal_)
+ width += kDividerSize;
+ else
+ height += kDividerSize;
+ return gfx::Size(width, height);
+}
+
+gfx::NativeCursor SingleSplitView::GetCursor(const MouseEvent& event) {
+ if (!IsPointInDivider(event.location()))
+ return gfx::kNullCursor;
+#if defined(USE_AURA)
+ return is_horizontal_ ?
+ aura::kCursorEastWestResize : aura::kCursorNorthSouthResize;
+#elif defined(OS_WIN)
+ static HCURSOR we_resize_cursor = LoadCursor(NULL, IDC_SIZEWE);
+ static HCURSOR ns_resize_cursor = LoadCursor(NULL, IDC_SIZENS);
+ return is_horizontal_ ? we_resize_cursor : ns_resize_cursor;
+#elif defined(TOOLKIT_USES_GTK)
+ return gfx::GetCursor(is_horizontal_ ? GDK_SB_H_DOUBLE_ARROW :
+ GDK_SB_V_DOUBLE_ARROW);
+#endif
+}
+
+void SingleSplitView::CalculateChildrenBounds(
+ const gfx::Rect& bounds,
+ gfx::Rect* leading_bounds,
+ gfx::Rect* trailing_bounds) const {
+ bool is_leading_visible = has_children() && child_at(0)->IsVisible();
+ bool is_trailing_visible = child_count() > 1 && child_at(1)->IsVisible();
+
+ if (!is_leading_visible && !is_trailing_visible) {
+ *leading_bounds = gfx::Rect();
+ *trailing_bounds = gfx::Rect();
+ return;
+ }
+
+ int divider_at;
+
+ if (!is_trailing_visible) {
+ divider_at = GetPrimaryAxisSize(bounds.width(), bounds.height());
+ } else if (!is_leading_visible) {
+ divider_at = 0;
+ } else {
+ divider_at =
+ CalculateDividerOffset(divider_offset_, this->bounds(), bounds);
+ divider_at = NormalizeDividerOffset(divider_at, bounds);
+ }
+
+ int divider_size =
+ !is_leading_visible || !is_trailing_visible ? 0 : kDividerSize;
+
+ if (is_horizontal_) {
+ *leading_bounds = gfx::Rect(0, 0, divider_at, bounds.height());
+ *trailing_bounds =
+ gfx::Rect(divider_at + divider_size, 0,
+ std::max(0, bounds.width() - divider_at - divider_size),
+ bounds.height());
+ } else {
+ *leading_bounds = gfx::Rect(0, 0, bounds.width(), divider_at);
+ *trailing_bounds =
+ gfx::Rect(0, divider_at + divider_size, bounds.width(),
+ std::max(0, bounds.height() - divider_at - divider_size));
+ }
+}
+
+void SingleSplitView::SetAccessibleName(const string16& name) {
+ accessible_name_ = name;
+}
+
+bool SingleSplitView::OnMousePressed(const MouseEvent& event) {
+ if (!IsPointInDivider(event.location()))
+ return false;
+ drag_info_.initial_mouse_offset = GetPrimaryAxisSize(event.x(), event.y());
+ drag_info_.initial_divider_offset =
+ NormalizeDividerOffset(divider_offset_, bounds());
+ return true;
+}
+
+bool SingleSplitView::OnMouseDragged(const MouseEvent& event) {
+ if (child_count() < 2)
+ return false;
+
+ int delta_offset = GetPrimaryAxisSize(event.x(), event.y()) -
+ drag_info_.initial_mouse_offset;
+ if (is_horizontal_ && base::i18n::IsRTL())
+ delta_offset *= -1;
+ // Honor the minimum size when resizing.
+ gfx::Size min = child_at(0)->GetMinimumSize();
+ int new_size = std::max(GetPrimaryAxisSize(min.width(), min.height()),
+ drag_info_.initial_divider_offset + delta_offset);
+
+ // And don't let the view get bigger than our width.
+ new_size = std::min(GetPrimaryAxisSize() - kDividerSize, new_size);
+
+ if (new_size != divider_offset_) {
+ set_divider_offset(new_size);
+ if (!listener_ || listener_->SplitHandleMoved(this))
+ Layout();
+ }
+ return true;
+}
+
+void SingleSplitView::OnMouseCaptureLost() {
+ if (child_count() < 2)
+ return;
+
+ if (drag_info_.initial_divider_offset != divider_offset_) {
+ set_divider_offset(drag_info_.initial_divider_offset);
+ if (!listener_ || listener_->SplitHandleMoved(this))
+ Layout();
+ }
+}
+
+void SingleSplitView::OnBoundsChanged(const gfx::Rect& previous_bounds) {
+ divider_offset_ = CalculateDividerOffset(divider_offset_, previous_bounds,
+ bounds());
+}
+
+bool SingleSplitView::IsPointInDivider(const gfx::Point& p) {
+ if (child_count() < 2)
+ return false;
+
+ if (!child_at(0)->IsVisible() || !child_at(1)->IsVisible())
+ return false;
+
+ int divider_relative_offset;
+ if (is_horizontal_) {
+ divider_relative_offset =
+ p.x() - child_at(base::i18n::IsRTL() ? 1 : 0)->width();
+ } else {
+ divider_relative_offset = p.y() - child_at(0)->height();
+ }
+ return (divider_relative_offset >= 0 &&
+ divider_relative_offset < kDividerSize);
+}
+
+int SingleSplitView::CalculateDividerOffset(
+ int divider_offset,
+ const gfx::Rect& previous_bounds,
+ const gfx::Rect& new_bounds) const {
+ if (resize_leading_on_bounds_change_ && divider_offset != -1) {
+ // We do not update divider_offset on minimize (to zero) and on restore
+ // (to largest value). As a result we get back to the original value upon
+ // window restore.
+ bool is_minimize_or_restore =
+ previous_bounds.height() == 0 || new_bounds.height() == 0;
+ if (!is_minimize_or_restore) {
+ if (is_horizontal_)
+ divider_offset += new_bounds.width() - previous_bounds.width();
+ else
+ divider_offset += new_bounds.height() - previous_bounds.height();
+
+ if (divider_offset < 0)
+ divider_offset = kDividerSize;
+ }
+ }
+ return divider_offset;
+}
+
+int SingleSplitView::NormalizeDividerOffset(int divider_offset,
+ const gfx::Rect& bounds) const {
+ int primary_axis_size = GetPrimaryAxisSize(bounds.width(), bounds.height());
+ if (divider_offset < 0)
+ // primary_axis_size may < kDividerSize during initial layout.
+ return std::max(0, (primary_axis_size - kDividerSize) / 2);
+ return std::min(divider_offset,
+ std::max(primary_axis_size - kDividerSize, 0));
+}
+
+} // namespace views
diff --git a/ui/views/controls/single_split_view.h b/ui/views/controls/single_split_view.h
new file mode 100644
index 0000000..c3f2bc3
--- /dev/null
+++ b/ui/views/controls/single_split_view.h
@@ -0,0 +1,134 @@
+// 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 UI_VIEWS_CONTROLS_SINGLE_SPLIT_VIEW_H_
+#define UI_VIEWS_CONTROLS_SINGLE_SPLIT_VIEW_H_
+#pragma once
+
+#include "base/gtest_prod_util.h"
+#include "views/view.h"
+
+namespace views {
+
+class SingleSplitViewListener;
+
+// SingleSplitView lays out two views next to each other, either horizontally
+// or vertically. A splitter exists between the two views that the user can
+// drag around to resize the views.
+// SingleSplitViewListener's SplitHandleMoved notification helps to monitor user
+// initiated layout changes.
+class VIEWS_EXPORT SingleSplitView : public View {
+ public:
+ enum Orientation {
+ HORIZONTAL_SPLIT,
+ VERTICAL_SPLIT
+ };
+
+ static const char kViewClassName[];
+
+ SingleSplitView(View* leading,
+ View* trailing,
+ Orientation orientation,
+ SingleSplitViewListener* listener);
+
+ virtual void Layout() OVERRIDE;
+ virtual std::string GetClassName() const OVERRIDE;
+
+ virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE;
+
+ // SingleSplitView's preferred size is the sum of the preferred widths
+ // and the max of the heights.
+ virtual gfx::Size GetPreferredSize() OVERRIDE;
+
+ // Overriden to return a resize cursor when over the divider.
+ virtual gfx::NativeCursor GetCursor(const MouseEvent& event) OVERRIDE;
+
+ Orientation orientation() const {
+ return is_horizontal_ ? HORIZONTAL_SPLIT : VERTICAL_SPLIT;
+ }
+
+ void set_divider_offset(int divider_offset) {
+ divider_offset_ = divider_offset;
+ }
+ int divider_offset() const { return divider_offset_; }
+
+ // Sets whether the leading component is resized when the split views size
+ // changes. The default is true. A value of false results in the trailing
+ // component resizing on a bounds change.
+ void set_resize_leading_on_bounds_change(bool resize) {
+ resize_leading_on_bounds_change_ = resize;
+ }
+
+ // Calculates ideal leading and trailing view bounds according to the given
+ // split view |bounds|, current divider offset and children visiblity.
+ // Does not change children view bounds.
+ void CalculateChildrenBounds(const gfx::Rect& bounds,
+ gfx::Rect* leading_bounds,
+ gfx::Rect* trailing_bounds) const;
+
+ void SetAccessibleName(const string16& name);
+
+ protected:
+ // View overrides.
+ virtual bool OnMousePressed(const MouseEvent& event) OVERRIDE;
+ virtual bool OnMouseDragged(const MouseEvent& event) OVERRIDE;
+ virtual void OnMouseCaptureLost() OVERRIDE;
+ virtual void OnBoundsChanged(const gfx::Rect& previous_bounds) OVERRIDE;
+
+ private:
+ // This test calls OnMouse* functions.
+ FRIEND_TEST_ALL_PREFIXES(SingleSplitViewTest, MouseDrag);
+
+ // Returns true if |x| or |y| is over the divider.
+ bool IsPointInDivider(const gfx::Point& p);
+
+ // Calculates the new |divider_offset| based on the changes of split view
+ // bounds.
+ int CalculateDividerOffset(int divider_offset,
+ const gfx::Rect& previous_bounds,
+ const gfx::Rect& new_bounds) const;
+
+ // Returns divider offset within primary axis size range for given split
+ // view |bounds|.
+ int NormalizeDividerOffset(int divider_offset, const gfx::Rect& bounds) const;
+
+ // Returns width in case of horizontal split and height otherwise.
+ int GetPrimaryAxisSize() const {
+ return GetPrimaryAxisSize(width(), height());
+ }
+
+ int GetPrimaryAxisSize(int h, int v) const {
+ return is_horizontal_ ? h : v;
+ }
+
+ // Used to track drag info.
+ struct DragInfo {
+ // The initial coordinate of the mouse when the user started the drag.
+ int initial_mouse_offset;
+ // The initial position of the divider when the user started the drag.
+ int initial_divider_offset;
+ };
+
+ DragInfo drag_info_;
+
+ // Orientation of the split view.
+ bool is_horizontal_;
+
+ // Position of the divider.
+ int divider_offset_;
+
+ bool resize_leading_on_bounds_change_;
+
+ // Listener to notify about user initiated handle movements. Not owned.
+ SingleSplitViewListener* listener_;
+
+ // The accessible name of this view.
+ string16 accessible_name_;
+
+ DISALLOW_COPY_AND_ASSIGN(SingleSplitView);
+};
+
+} // namespace views
+
+#endif // UI_VIEWS_CONTROLS_SINGLE_SPLIT_VIEW_H_
diff --git a/ui/views/controls/single_split_view_listener.h b/ui/views/controls/single_split_view_listener.h
new file mode 100644
index 0000000..580ac82
--- /dev/null
+++ b/ui/views/controls/single_split_view_listener.h
@@ -0,0 +1,29 @@
+// 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 UI_VIEWS_CONTROLS_SINGLE_SPLIT_VIEW_LISTENER_H_
+#define UI_VIEWS_CONTROLS_SINGLE_SPLIT_VIEW_LISTENER_H_
+#pragma once
+
+namespace views {
+
+class SingleSplitView;
+
+// An interface implemented by objects that want to be notified when the
+// splitter moves.
+class SingleSplitViewListener {
+ public:
+ // Invoked when split handle is moved by the user. |sender|'s divider_offset
+ // is already set to the new value, but Layout has not happened yet.
+ // Returns false if the layout has been handled by the listener, returns
+ // true if |sender| should do it by itself.
+ virtual bool SplitHandleMoved(SingleSplitView* sender) = 0;
+
+ protected:
+ virtual ~SingleSplitViewListener() {}
+};
+
+} // namespace views
+
+#endif // UI_VIEWS_CONTROLS_SINGLE_SPLIT_VIEW_LISTENER_H_
diff --git a/ui/views/controls/single_split_view_unittest.cc b/ui/views/controls/single_split_view_unittest.cc
new file mode 100644
index 0000000..891a877
--- /dev/null
+++ b/ui/views/controls/single_split_view_unittest.cc
@@ -0,0 +1,180 @@
+// 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 "base/logging.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/views/controls/single_split_view.h"
+#include "ui/views/controls/single_split_view_listener.h"
+
+using ::testing::_;
+using ::testing::Return;
+
+namespace {
+
+static void VerifySplitViewLayout(const views::SingleSplitView& split) {
+ ASSERT_EQ(2, split.child_count());
+
+ const views::View* leading = split.child_at(0);
+ const views::View* trailing = split.child_at(1);
+
+ if (split.bounds().IsEmpty()) {
+ EXPECT_TRUE(leading->bounds().IsEmpty());
+ EXPECT_TRUE(trailing->bounds().IsEmpty());
+ return;
+ }
+
+ EXPECT_FALSE(leading->bounds().IsEmpty());
+ EXPECT_FALSE(trailing->bounds().IsEmpty());
+ EXPECT_FALSE(leading->bounds().Intersects(trailing->bounds()));
+
+ if (split.orientation() == views::SingleSplitView::HORIZONTAL_SPLIT) {
+ EXPECT_EQ(leading->bounds().height(), split.bounds().height());
+ EXPECT_EQ(trailing->bounds().height(), split.bounds().height());
+ EXPECT_LT(leading->bounds().width() + trailing->bounds().width(),
+ split.bounds().width());
+ } else if (split.orientation() == views::SingleSplitView::VERTICAL_SPLIT) {
+ EXPECT_EQ(leading->bounds().width(), split.bounds().width());
+ EXPECT_EQ(trailing->bounds().width(), split.bounds().width());
+ EXPECT_LT(leading->bounds().height() + trailing->bounds().height(),
+ split.bounds().height());
+ } else {
+ NOTREACHED();
+ }
+}
+
+class MockObserver : public views::SingleSplitViewListener {
+ public:
+ MOCK_METHOD1(SplitHandleMoved, bool(views::SingleSplitView*));
+};
+
+} // namespace
+
+namespace views {
+
+TEST(SingleSplitViewTest, Resize) {
+ // Test cases to iterate through for horizontal and vertical split views.
+ struct TestCase {
+ // Split view resize policy for this test case.
+ bool resize_leading_on_bounds_change;
+ // Split view size to set.
+ int primary_axis_size;
+ int secondary_axis_size;
+ // Expected divider offset.
+ int divider_offset;
+ } test_cases[] = {
+ // The initial split size is 100x100, divider at 33.
+ { true, 100, 100, 33 },
+ // Grow the split view, leading view should grow.
+ { true, 1000, 100, 933 },
+ // Shrink the split view, leading view should shrink.
+ { true, 200, 100, 133 },
+ // Minimize the split view, divider should not move.
+ { true, 0, 0, 133 },
+ // Restore the split view, divider should not move.
+ { false, 500, 100, 133 },
+ // Resize the split view by secondary axis, divider should not move.
+ { false, 500, 600, 133 }
+ };
+
+ SingleSplitView::Orientation orientations[] = {
+ SingleSplitView::HORIZONTAL_SPLIT,
+ SingleSplitView::VERTICAL_SPLIT
+ };
+
+ for (size_t orientation = 0; orientation < arraysize(orientations);
+ ++orientation) {
+ // Create a split view.
+ SingleSplitView split(
+ new View(), new View(), orientations[orientation], NULL);
+
+ // Set initial size and divider offset.
+ EXPECT_EQ(test_cases[0].primary_axis_size,
+ test_cases[0].secondary_axis_size);
+ split.SetBounds(0, 0, test_cases[0].primary_axis_size,
+ test_cases[0].secondary_axis_size);
+ split.set_divider_offset(test_cases[0].divider_offset);
+ split.Layout();
+
+ // Run all test cases.
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_cases); ++i) {
+ split.set_resize_leading_on_bounds_change(
+ test_cases[i].resize_leading_on_bounds_change);
+ if (split.orientation() == SingleSplitView::HORIZONTAL_SPLIT) {
+ split.SetBounds(0, 0, test_cases[i].primary_axis_size,
+ test_cases[i].secondary_axis_size);
+ } else {
+ split.SetBounds(0, 0, test_cases[i].secondary_axis_size,
+ test_cases[i].primary_axis_size);
+ }
+
+ EXPECT_EQ(test_cases[i].divider_offset, split.divider_offset());
+ VerifySplitViewLayout(split);
+ }
+
+ // Special cases, one of the child views is hidden.
+ split.child_at(0)->SetVisible(false);
+ split.Layout();
+
+ EXPECT_EQ(split.size(), split.child_at(1)->size());
+
+ split.child_at(0)->SetVisible(true);
+ split.child_at(1)->SetVisible(false);
+ split.Layout();
+
+ EXPECT_EQ(split.size(), split.child_at(0)->size());
+ }
+}
+
+TEST(SingleSplitViewTest, MouseDrag) {
+ MockObserver observer;
+ SingleSplitView split(
+ new View(), new View(), SingleSplitView::VERTICAL_SPLIT, &observer);
+
+ ON_CALL(observer, SplitHandleMoved(_))
+ .WillByDefault(Return(true));
+ // SplitHandleMoved is called for two mouse moves and one mouse capture loss.
+ EXPECT_CALL(observer, SplitHandleMoved(_))
+ .Times(3);
+
+ split.SetBounds(0, 0, 10, 100);
+ const int kInitialDividerOffset = 33;
+ const int kMouseOffset = 2; // Mouse offset in the divider.
+ const int kMouseMoveDelta = 7;
+ split.set_divider_offset(kInitialDividerOffset);
+ split.Layout();
+
+ // Drag divider to the right, in 2 steps.
+ MouseEvent mouse_pressed(
+ ui::ET_MOUSE_PRESSED, 7, kInitialDividerOffset + kMouseOffset, 0);
+ ASSERT_TRUE(split.OnMousePressed(mouse_pressed));
+ EXPECT_EQ(kInitialDividerOffset, split.divider_offset());
+
+ MouseEvent mouse_dragged_1(
+ ui::ET_MOUSE_DRAGGED, 5,
+ kInitialDividerOffset + kMouseOffset + kMouseMoveDelta, 0);
+ ASSERT_TRUE(split.OnMouseDragged(mouse_dragged_1));
+ EXPECT_EQ(kInitialDividerOffset + kMouseMoveDelta, split.divider_offset());
+
+ MouseEvent mouse_dragged_2(
+ ui::ET_MOUSE_DRAGGED, 6,
+ kInitialDividerOffset + kMouseOffset + kMouseMoveDelta * 2, 0);
+ ASSERT_TRUE(split.OnMouseDragged(mouse_dragged_2));
+ EXPECT_EQ(kInitialDividerOffset + kMouseMoveDelta * 2,
+ split.divider_offset());
+
+ MouseEvent mouse_released(
+ ui::ET_MOUSE_RELEASED, 7,
+ kInitialDividerOffset + kMouseOffset + kMouseMoveDelta * 2, 0);
+ split.OnMouseReleased(mouse_released);
+ EXPECT_EQ(kInitialDividerOffset + kMouseMoveDelta * 2,
+ split.divider_offset());
+
+ // Expect intial offset after a system/user gesture cancels the drag.
+ // This shouldn't occur after mouse release, but it's sufficient for testing.
+ split.OnMouseCaptureLost();
+ EXPECT_EQ(kInitialDividerOffset, split.divider_offset());
+}
+
+} // namespace views
diff --git a/ui/views/controls/tabbed_pane/native_tabbed_pane_gtk.h b/ui/views/controls/tabbed_pane/native_tabbed_pane_gtk.h
index c37af6c..b937cb2 100644
--- a/ui/views/controls/tabbed_pane/native_tabbed_pane_gtk.h
+++ b/ui/views/controls/tabbed_pane/native_tabbed_pane_gtk.h
@@ -8,8 +8,8 @@
#include "base/basictypes.h"
#include "base/compiler_specific.h"
+#include "ui/views/controls/native_control_gtk.h"
#include "ui/views/controls/tabbed_pane/native_tabbed_pane_wrapper.h"
-#include "views/controls/native_control_gtk.h"
namespace views {
diff --git a/ui/views/controls/tabbed_pane/native_tabbed_pane_win.h b/ui/views/controls/tabbed_pane/native_tabbed_pane_win.h
index 8935c1a..6532438 100644
--- a/ui/views/controls/tabbed_pane/native_tabbed_pane_win.h
+++ b/ui/views/controls/tabbed_pane/native_tabbed_pane_win.h
@@ -8,8 +8,8 @@
#include <vector>
+#include "ui/views/controls/native_control_win.h"
#include "ui/views/controls/tabbed_pane/native_tabbed_pane_wrapper.h"
-#include "views/controls/native_control_win.h"
namespace views {
diff --git a/ui/views/controls/table/native_table_gtk.h b/ui/views/controls/table/native_table_gtk.h
index 3ebb51f..df63faf 100644
--- a/ui/views/controls/table/native_table_gtk.h
+++ b/ui/views/controls/table/native_table_gtk.h
@@ -9,8 +9,8 @@
#include "base/compiler_specific.h"
#include "ui/base/gtk/gtk_signal.h"
#include "ui/base/models/table_model.h"
+#include "ui/views/controls/native_control_gtk.h"
#include "ui/views/controls/table/native_table_wrapper.h"
-#include "views/controls/native_control_gtk.h"
namespace views {
diff --git a/ui/views/controls/table/native_table_win.h b/ui/views/controls/table/native_table_win.h
index 810eb94..432b30c 100644
--- a/ui/views/controls/table/native_table_win.h
+++ b/ui/views/controls/table/native_table_win.h
@@ -9,8 +9,8 @@
#include <windows.h>
#include "ui/base/models/table_model.h"
+#include "ui/views/controls/native_control_win.h"
#include "ui/views/controls/table/native_table_wrapper.h"
-#include "views/controls/native_control_win.h"
typedef struct tagNMLVCUSTOMDRAW NMLVCUSTOMDRAW;
diff --git a/ui/views/controls/table/table_view.h b/ui/views/controls/table/table_view.h
index e226f10..78a0f55 100644
--- a/ui/views/controls/table/table_view.h
+++ b/ui/views/controls/table/table_view.h
@@ -21,7 +21,7 @@
#include <windows.h>
// TODO(port): remove the ifdef when native_control.h is ported.
-#include "views/controls/native_control.h"
+#include "ui/views/controls/native_control.h"
typedef struct tagNMLVCUSTOMDRAW NMLVCUSTOMDRAW;
#endif // defined(OS_WIN)
diff --git a/ui/views/controls/textfield/native_textfield_gtk.h b/ui/views/controls/textfield/native_textfield_gtk.h
index ea3151c..42a9680 100644
--- a/ui/views/controls/textfield/native_textfield_gtk.h
+++ b/ui/views/controls/textfield/native_textfield_gtk.h
@@ -10,8 +10,8 @@
#include "base/string16.h"
#include "ui/base/gtk/gtk_signal.h"
+#include "ui/views/controls/native_control_gtk.h"
#include "ui/views/controls/textfield/native_textfield_wrapper.h"
-#include "views/controls/native_control_gtk.h"
namespace gfx {
class SelectionModel;
diff --git a/ui/views/controls/textfield/native_textfield_views.cc b/ui/views/controls/textfield/native_textfield_views.cc
index 3e6a5f8..d5d0b56 100644
--- a/ui/views/controls/textfield/native_textfield_views.cc
+++ b/ui/views/controls/textfield/native_textfield_views.cc
@@ -18,6 +18,7 @@
#include "ui/gfx/canvas.h"
#include "ui/gfx/insets.h"
#include "ui/gfx/render_text.h"
+#include "ui/views/controls/focusable_border.h"
#include "ui/views/controls/menu/menu_item_view.h"
#include "ui/views/controls/menu/menu_model_adapter.h"
#include "ui/views/controls/menu/menu_runner.h"
@@ -29,7 +30,6 @@
#include "ui/views/widget/widget.h"
#include "views/background.h"
#include "views/border.h"
-#include "views/controls/focusable_border.h"
#include "views/metrics.h"
#include "views/views_delegate.h"
diff --git a/ui/views/controls/throbber.cc b/ui/views/controls/throbber.cc
new file mode 100644
index 0000000..d323c00
--- /dev/null
+++ b/ui/views/controls/throbber.cc
@@ -0,0 +1,178 @@
+// 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 "ui/views/controls/throbber.h"
+
+#include "base/time.h"
+#include "grit/ui_resources.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/gfx/canvas.h"
+
+using base::Time;
+using base::TimeDelta;
+
+namespace views {
+
+Throbber::Throbber(int frame_time_ms,
+ bool paint_while_stopped)
+ : running_(false),
+ paint_while_stopped_(paint_while_stopped),
+ frames_(NULL),
+ frame_time_(TimeDelta::FromMilliseconds(frame_time_ms)) {
+ SetFrames(ResourceBundle::GetSharedInstance().GetBitmapNamed(IDR_THROBBER));
+}
+
+Throbber::~Throbber() {
+ Stop();
+}
+
+void Throbber::Start() {
+ if (running_)
+ return;
+
+ start_time_ = Time::Now();
+
+ timer_.Start(FROM_HERE, frame_time_ - TimeDelta::FromMilliseconds(10),
+ this, &Throbber::Run);
+
+ running_ = true;
+
+ SchedulePaint(); // paint right away
+}
+
+void Throbber::Stop() {
+ if (!running_)
+ return;
+
+ timer_.Stop();
+
+ running_ = false;
+ SchedulePaint(); // Important if we're not painting while stopped
+}
+
+void Throbber::SetFrames(SkBitmap* frames) {
+ frames_ = frames;
+ DCHECK(frames_->width() > 0 && frames_->height() > 0);
+ DCHECK(frames_->width() % frames_->height() == 0);
+ frame_count_ = frames_->width() / frames_->height();
+ PreferredSizeChanged();
+}
+
+void Throbber::Run() {
+ DCHECK(running_);
+
+ SchedulePaint();
+}
+
+gfx::Size Throbber::GetPreferredSize() {
+ return gfx::Size(frames_->height(), frames_->height());
+}
+
+void Throbber::OnPaint(gfx::Canvas* canvas) {
+ if (!running_ && !paint_while_stopped_)
+ return;
+
+ const TimeDelta elapsed_time = Time::Now() - start_time_;
+ const int current_frame =
+ static_cast<int>(elapsed_time / frame_time_) % frame_count_;
+
+ int image_size = frames_->height();
+ int image_offset = current_frame * image_size;
+ canvas->DrawBitmapInt(*frames_,
+ image_offset, 0, image_size, image_size,
+ 0, 0, image_size, image_size,
+ false);
+}
+
+
+
+// Smoothed throbber ---------------------------------------------------------
+
+
+// Delay after work starts before starting throbber, in milliseconds.
+static const int kStartDelay = 200;
+
+// Delay after work stops before stopping, in milliseconds.
+static const int kStopDelay = 50;
+
+
+SmoothedThrobber::SmoothedThrobber(int frame_time_ms)
+ : Throbber(frame_time_ms, /* paint_while_stopped= */ false),
+ start_delay_ms_(kStartDelay),
+ stop_delay_ms_(kStopDelay) {
+}
+
+SmoothedThrobber::~SmoothedThrobber() {}
+
+void SmoothedThrobber::Start() {
+ stop_timer_.Stop();
+
+ if (!running_ && !start_timer_.IsRunning()) {
+ start_timer_.Start(FROM_HERE, TimeDelta::FromMilliseconds(start_delay_ms_),
+ this, &SmoothedThrobber::StartDelayOver);
+ }
+}
+
+void SmoothedThrobber::StartDelayOver() {
+ Throbber::Start();
+}
+
+void SmoothedThrobber::Stop() {
+ if (!running_)
+ start_timer_.Stop();
+
+ stop_timer_.Stop();
+ stop_timer_.Start(FROM_HERE, TimeDelta::FromMilliseconds(stop_delay_ms_),
+ this, &SmoothedThrobber::StopDelayOver);
+}
+
+void SmoothedThrobber::StopDelayOver() {
+ Throbber::Stop();
+}
+
+// Checkmark throbber ---------------------------------------------------------
+
+CheckmarkThrobber::CheckmarkThrobber()
+ : Throbber(kFrameTimeMs, false),
+ checked_(false) {
+ InitClass();
+}
+
+void CheckmarkThrobber::SetChecked(bool checked) {
+ bool changed = checked != checked_;
+ if (changed) {
+ checked_ = checked;
+ SchedulePaint();
+ }
+}
+
+void CheckmarkThrobber::OnPaint(gfx::Canvas* canvas) {
+ if (running_) {
+ // Let the throbber throb...
+ Throbber::OnPaint(canvas);
+ return;
+ }
+ // Otherwise we paint our tick mark or nothing depending on our state.
+ if (checked_) {
+ int checkmark_x = (width() - checkmark_->width()) / 2;
+ int checkmark_y = (height() - checkmark_->height()) / 2;
+ canvas->DrawBitmapInt(*checkmark_, checkmark_x, checkmark_y);
+ }
+}
+
+// static
+void CheckmarkThrobber::InitClass() {
+ static bool initialized = false;
+ if (!initialized) {
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+ checkmark_ = rb.GetBitmapNamed(IDR_CHECKMARK);
+ initialized = true;
+ }
+}
+
+// static
+SkBitmap* CheckmarkThrobber::checkmark_ = NULL;
+
+} // namespace views
diff --git a/ui/views/controls/throbber.h b/ui/views/controls/throbber.h
new file mode 100644
index 0000000..e40828f
--- /dev/null
+++ b/ui/views/controls/throbber.h
@@ -0,0 +1,128 @@
+// 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 UI_VIEWS_CONTROLS_THROBBER_H_
+#define UI_VIEWS_CONTROLS_THROBBER_H_
+#pragma once
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/time.h"
+#include "base/timer.h"
+#include "views/view.h"
+
+class SkBitmap;
+
+namespace views {
+
+// Throbbers display an animation, usually used as a status indicator.
+
+class VIEWS_EXPORT Throbber : public View {
+ public:
+ // |frame_time_ms| is the amount of time that should elapse between frames
+ // (in milliseconds)
+ // If |paint_while_stopped| is false, this view will be invisible when not
+ // running.
+ Throbber(int frame_time_ms, bool paint_while_stopped);
+ Throbber(int frame_time_ms, bool paint_while_stopped, SkBitmap* frames);
+ virtual ~Throbber();
+
+ // Start and stop the throbber animation
+ virtual void Start();
+ virtual void Stop();
+
+ // Set custom throbber frames. Otherwise IDR_THROBBER is loaded.
+ void SetFrames(SkBitmap* frames);
+
+ // overridden from View
+ virtual gfx::Size GetPreferredSize() OVERRIDE;
+ virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
+
+ protected:
+ // Specifies whether the throbber is currently animating or not
+ bool running_;
+
+ private:
+ void Run();
+
+ bool paint_while_stopped_;
+ int frame_count_; // How many frames we have.
+ base::Time start_time_; // Time when Start was called.
+ SkBitmap* frames_; // Frames bitmaps.
+ base::TimeDelta frame_time_; // How long one frame is displayed.
+ base::RepeatingTimer<Throbber> timer_; // Used to schedule Run calls.
+
+ DISALLOW_COPY_AND_ASSIGN(Throbber);
+};
+
+// A SmoothedThrobber is a throbber that is representing potentially short
+// and nonoverlapping bursts of work. SmoothedThrobber ignores small
+// pauses in the work stops and starts, and only starts its throbber after
+// a small amount of work time has passed.
+class VIEWS_EXPORT SmoothedThrobber : public Throbber {
+ public:
+ SmoothedThrobber(int frame_delay_ms);
+ SmoothedThrobber(int frame_delay_ms, SkBitmap* frames);
+ virtual ~SmoothedThrobber();
+
+ virtual void Start() OVERRIDE;
+ virtual void Stop() OVERRIDE;
+
+ void set_start_delay_ms(int value) { start_delay_ms_ = value; }
+ void set_stop_delay_ms(int value) { stop_delay_ms_ = value; }
+
+ private:
+ // Called when the startup-delay timer fires
+ // This function starts the actual throbbing.
+ void StartDelayOver();
+
+ // Called when the shutdown-delay timer fires.
+ // This function stops the actual throbbing.
+ void StopDelayOver();
+
+ // Delay after work starts before starting throbber, in milliseconds.
+ int start_delay_ms_;
+
+ // Delay after work stops before stopping, in milliseconds.
+ int stop_delay_ms_;
+
+ base::OneShotTimer<SmoothedThrobber> start_timer_;
+ base::OneShotTimer<SmoothedThrobber> stop_timer_;
+
+ DISALLOW_COPY_AND_ASSIGN(SmoothedThrobber);
+};
+
+// A CheckmarkThrobber is a special variant of throbber that has three states:
+// 1. not yet completed (which paints nothing)
+// 2. working (which paints the throbber animation)
+// 3. completed (which paints a checkmark)
+//
+class VIEWS_EXPORT CheckmarkThrobber : public Throbber {
+ public:
+ CheckmarkThrobber();
+
+ // If checked is true, the throbber stops spinning and displays a checkmark.
+ // If checked is false, the throbber stops spinning and displays nothing.
+ void SetChecked(bool checked);
+
+ // Overridden from Throbber:
+ virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
+
+ private:
+ static const int kFrameTimeMs = 30;
+
+ static void InitClass();
+
+ // Whether or not we should display a checkmark.
+ bool checked_;
+
+ // The checkmark image.
+ static SkBitmap* checkmark_;
+
+ DISALLOW_COPY_AND_ASSIGN(CheckmarkThrobber);
+};
+
+} // namespace views
+
+#endif // UI_VIEWS_CONTROLS_THROBBER_H_
diff --git a/ui/views/controls/tree/tree_view.h b/ui/views/controls/tree/tree_view.h
index 9b1fd75..035d3f6 100644
--- a/ui/views/controls/tree/tree_view.h
+++ b/ui/views/controls/tree/tree_view.h
@@ -15,7 +15,7 @@
#include "base/compiler_specific.h"
#include "ui/base/keycodes/keyboard_codes.h"
#include "ui/base/models/tree_model.h"
-#include "views/controls/native_control.h"
+#include "ui/views/controls/native_control.h"
namespace views {
diff --git a/ui/views/examples/double_split_view_example.cc b/ui/views/examples/double_split_view_example.cc
index 516c191..4620f72 100644
--- a/ui/views/examples/double_split_view_example.cc
+++ b/ui/views/examples/double_split_view_example.cc
@@ -4,8 +4,8 @@
#include "ui/views/examples/double_split_view_example.h"
+#include "ui/views/controls/single_split_view.h"
#include "ui/views/layout/grid_layout.h"
-#include "views/controls/single_split_view.h"
namespace {
diff --git a/ui/views/examples/message_box_example.cc b/ui/views/examples/message_box_example.cc
index e1b1542..942f22b 100644
--- a/ui/views/examples/message_box_example.cc
+++ b/ui/views/examples/message_box_example.cc
@@ -5,8 +5,8 @@
#include "ui/views/examples/message_box_example.h"
#include "base/utf_string_conversions.h"
+#include "ui/views/controls/message_box_view.h"
#include "ui/views/layout/grid_layout.h"
-#include "views/controls/message_box_view.h"
#include "views/view.h"
namespace views {
diff --git a/ui/views/examples/progress_bar_example.cc b/ui/views/examples/progress_bar_example.cc
index e28cad3..f355007 100644
--- a/ui/views/examples/progress_bar_example.cc
+++ b/ui/views/examples/progress_bar_example.cc
@@ -6,8 +6,8 @@
#include "base/utf_string_conversions.h"
#include "ui/views/controls/button/text_button.h"
+#include "ui/views/controls/progress_bar.h"
#include "ui/views/layout/grid_layout.h"
-#include "views/controls/progress_bar.h"
#include "views/view.h"
namespace {
diff --git a/ui/views/examples/scroll_view_example.h b/ui/views/examples/scroll_view_example.h
index 62de4e8..d18eac9 100644
--- a/ui/views/examples/scroll_view_example.h
+++ b/ui/views/examples/scroll_view_example.h
@@ -11,8 +11,8 @@
#include "base/basictypes.h"
#include "base/compiler_specific.h"
#include "ui/views/controls/button/text_button.h"
+#include "ui/views/controls/scroll_view.h"
#include "ui/views/examples/example_base.h"
-#include "views/controls/scroll_view.h"
namespace views {
namespace examples {
diff --git a/ui/views/examples/single_split_view_example.cc b/ui/views/examples/single_split_view_example.cc
index 6b703e3..70e4b19 100644
--- a/ui/views/examples/single_split_view_example.cc
+++ b/ui/views/examples/single_split_view_example.cc
@@ -4,8 +4,8 @@
#include "ui/views/examples/single_split_view_example.h"
+#include "ui/views/controls/single_split_view.h"
#include "ui/views/layout/grid_layout.h"
-#include "views/controls/single_split_view.h"
namespace views {
namespace examples {
diff --git a/ui/views/examples/single_split_view_example.h b/ui/views/examples/single_split_view_example.h
index 9c9d919..c3a95be 100644
--- a/ui/views/examples/single_split_view_example.h
+++ b/ui/views/examples/single_split_view_example.h
@@ -8,8 +8,8 @@
#include "base/basictypes.h"
#include "base/compiler_specific.h"
+#include "ui/views/controls/single_split_view_listener.h"
#include "ui/views/examples/example_base.h"
-#include "views/controls/single_split_view_listener.h"
namespace views {
namespace examples {
diff --git a/ui/views/examples/throbber_example.cc b/ui/views/examples/throbber_example.cc
index f25152a0..a3af5cf 100644
--- a/ui/views/examples/throbber_example.cc
+++ b/ui/views/examples/throbber_example.cc
@@ -4,8 +4,8 @@
#include "ui/views/examples/throbber_example.h"
+#include "ui/views/controls/throbber.h"
#include "ui/views/layout/fill_layout.h"
-#include "views/controls/throbber.h"
#include "views/view.h"
namespace views {
diff --git a/ui/views/focus/focus_traversal_unittest.cc b/ui/views/focus/focus_traversal_unittest.cc
index 3605015..fc58df8 100644
--- a/ui/views/focus/focus_traversal_unittest.cc
+++ b/ui/views/focus/focus_traversal_unittest.cc
@@ -14,11 +14,11 @@
#include "ui/views/controls/label.h"
#include "ui/views/controls/link.h"
#include "ui/views/controls/native/native_view_host.h"
+#include "ui/views/controls/scroll_view.h"
#include "ui/views/controls/textfield/textfield.h"
#include "ui/views/focus/focus_manager_test.h"
#include "ui/views/widget/root_view.h"
#include "ui/views/widget/widget.h"
-#include "views/controls/scroll_view.h"
#if !defined(USE_AURA)
#include "ui/views/controls/tabbed_pane/tabbed_pane.h"
diff --git a/ui/views/widget/native_widget_win.cc b/ui/views/widget/native_widget_win.cc
index ce5ad17..f328ecb 100644
--- a/ui/views/widget/native_widget_win.cc
+++ b/ui/views/widget/native_widget_win.cc
@@ -33,6 +33,7 @@
#include "ui/gfx/path.h"
#include "ui/gfx/screen.h"
#include "ui/views/accessibility/native_view_accessibility_win.h"
+#include "ui/views/controls/native_control_win.h"
#include "ui/views/controls/textfield/native_textfield_views.h"
#include "ui/views/focus/accelerator_handler.h"
#include "ui/views/focus/view_storage.h"
@@ -45,7 +46,6 @@
#include "ui/views/widget/root_view.h"
#include "ui/views/widget/widget_delegate.h"
#include "ui/views/window/native_frame_view.h"
-#include "views/controls/native_control_win.h"
#include "views/views_delegate.h"
#pragma comment(lib, "dwmapi.lib")