diff options
author | tfarina@chromium.org <tfarina@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-11-29 20:25:29 +0000 |
---|---|---|
committer | tfarina@chromium.org <tfarina@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-11-29 20:25:29 +0000 |
commit | 8fc1b372c1f74f06296a0eebee0d40dbd623332f (patch) | |
tree | dde0e7d9c71dd2e461c0860e9ebe61e4a9ea385f /ui | |
parent | de797cf7fd9f68c59b636a26f1ade2e01357d8e9 (diff) | |
download | chromium_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')
54 files changed, 4049 insertions, 26 deletions
diff --git a/ui/aura_shell/drag_image_view.h b/ui/aura_shell/drag_image_view.h index ecc665c..6f92ea3 100644 --- a/ui/aura_shell/drag_image_view.h +++ b/ui/aura_shell/drag_image_view.h @@ -6,7 +6,7 @@ #define UI_AURA_SHELL_DRAG_IMAGE_VIEW_H_ #pragma once -#include "views/controls/image_view.h" +#include "ui/views/controls/image_view.h" namespace views { class Widget; 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") |