diff options
Diffstat (limited to 'ui/views')
89 files changed, 6764 insertions, 83 deletions
diff --git a/ui/views/accessibility/native_view_accessibility_win.h b/ui/views/accessibility/native_view_accessibility_win.h index e017050..51254d9 100644 --- a/ui/views/accessibility/native_view_accessibility_win.h +++ b/ui/views/accessibility/native_view_accessibility_win.h @@ -15,7 +15,7 @@ #include "third_party/iaccessible2/ia2_api_all.h" #include "ui/base/accessibility/accessible_view_state.h" #include "ui/views/controls/native/native_view_host.h" -#include "views/view.h" +#include "ui/views/view.h" namespace ui { enum TextBoundaryDirection; diff --git a/ui/views/animation/bounds_animator.cc b/ui/views/animation/bounds_animator.cc index d656d0a..2e4b0c1 100644 --- a/ui/views/animation/bounds_animator.cc +++ b/ui/views/animation/bounds_animator.cc @@ -7,7 +7,7 @@ #include "base/memory/scoped_ptr.h" #include "ui/base/animation/animation_container.h" #include "ui/base/animation/slide_animation.h" -#include "views/view.h" +#include "ui/views/view.h" // Duration in milliseconds for animations. static const int kAnimationDuration = 200; diff --git a/ui/views/animation/bounds_animator_unittest.cc b/ui/views/animation/bounds_animator_unittest.cc index d030ba1..bf1f08c 100644 --- a/ui/views/animation/bounds_animator_unittest.cc +++ b/ui/views/animation/bounds_animator_unittest.cc @@ -6,7 +6,7 @@ #include "ui/base/animation/slide_animation.h" #include "ui/base/animation/test_animation_delegate.h" #include "ui/views/animation/bounds_animator.h" -#include "views/view.h" +#include "ui/views/view.h" using views::BoundsAnimator; using ui::Animation; diff --git a/ui/views/bubble/border_contents_view.h b/ui/views/bubble/border_contents_view.h index e30ba19..2e86662 100644 --- a/ui/views/bubble/border_contents_view.h +++ b/ui/views/bubble/border_contents_view.h @@ -8,7 +8,7 @@ #include "third_party/skia/include/core/SkColor.h" #include "ui/views/bubble/bubble_border.h" -#include "views/view.h" +#include "ui/views/view.h" namespace views { diff --git a/ui/views/controls/button/button.h b/ui/views/controls/button/button.h index 41aba33..7cba9d1 100644 --- a/ui/views/controls/button/button.h +++ b/ui/views/controls/button/button.h @@ -6,7 +6,7 @@ #define UI_VIEWS_CONTROLS_BUTTON_BUTTON_H_ #pragma once -#include "views/view.h" +#include "ui/views/view.h" namespace views { diff --git a/ui/views/controls/combobox/combobox.h b/ui/views/controls/combobox/combobox.h index dbd62db..a6f7e35 100644 --- a/ui/views/controls/combobox/combobox.h +++ b/ui/views/controls/combobox/combobox.h @@ -10,7 +10,7 @@ #include "ui/gfx/native_widget_types.h" #include "ui/views/controls/combobox/native_combobox_wrapper.h" -#include "views/view.h" +#include "ui/views/view.h" namespace ui { class ComboboxModel; diff --git a/ui/views/controls/combobox/native_combobox_views.h b/ui/views/controls/combobox/native_combobox_views.h index 784539e..61c70a3 100644 --- a/ui/views/controls/combobox/native_combobox_views.h +++ b/ui/views/controls/combobox/native_combobox_views.h @@ -8,7 +8,7 @@ #include "ui/views/controls/combobox/native_combobox_wrapper.h" #include "ui/views/controls/menu/menu_delegate.h" -#include "views/view.h" +#include "ui/views/view.h" namespace gfx { class Canvas; diff --git a/ui/views/controls/focusable_border.h b/ui/views/controls/focusable_border.h index 098809b..903bb61 100644 --- a/ui/views/controls/focusable_border.h +++ b/ui/views/controls/focusable_border.h @@ -8,8 +8,8 @@ #include "base/basictypes.h" #include "base/compiler_specific.h" +#include "ui/views/view.h" #include "views/border.h" -#include "views/view.h" namespace gfx { class Canvas; diff --git a/ui/views/controls/image_view.h b/ui/views/controls/image_view.h index 1149c8d..8827d4e 100644 --- a/ui/views/controls/image_view.h +++ b/ui/views/controls/image_view.h @@ -7,7 +7,7 @@ #pragma once #include "third_party/skia/include/core/SkBitmap.h" -#include "views/view.h" +#include "ui/views/view.h" namespace gfx { class Canvas; diff --git a/ui/views/controls/label.h b/ui/views/controls/label.h index 4f0d6c2..e2c15cc 100644 --- a/ui/views/controls/label.h +++ b/ui/views/controls/label.h @@ -14,7 +14,7 @@ #include "googleurl/src/gurl.h" #include "third_party/skia/include/core/SkColor.h" #include "ui/gfx/font.h" -#include "views/view.h" +#include "ui/views/view.h" namespace views { diff --git a/ui/views/controls/menu/menu_item_view.h b/ui/views/controls/menu/menu_item_view.h index 28481e8..91da31b 100644 --- a/ui/views/controls/menu/menu_item_view.h +++ b/ui/views/controls/menu/menu_item_view.h @@ -14,7 +14,7 @@ #include "base/string16.h" #include "build/build_config.h" #include "third_party/skia/include/core/SkBitmap.h" -#include "views/view.h" +#include "ui/views/view.h" #if defined(OS_WIN) #include <windows.h> diff --git a/ui/views/controls/menu/menu_scroll_view_container.h b/ui/views/controls/menu/menu_scroll_view_container.h index a19b73a..234c8f1 100644 --- a/ui/views/controls/menu/menu_scroll_view_container.h +++ b/ui/views/controls/menu/menu_scroll_view_container.h @@ -6,7 +6,7 @@ #define UI_VIEWS_CONTROLS_MENU_MENU_SCROLL_VIEW_CONTAINER_H_ #pragma once -#include "views/view.h" +#include "ui/views/view.h" namespace views { diff --git a/ui/views/controls/menu/menu_separator.h b/ui/views/controls/menu/menu_separator.h index 4e968a3..d404ddb 100644 --- a/ui/views/controls/menu/menu_separator.h +++ b/ui/views/controls/menu/menu_separator.h @@ -7,7 +7,7 @@ #pragma once #include "base/compiler_specific.h" -#include "views/view.h" +#include "ui/views/view.h" namespace views { diff --git a/ui/views/controls/menu/submenu_view.h b/ui/views/controls/menu/submenu_view.h index 693a538..d4d0497 100644 --- a/ui/views/controls/menu/submenu_view.h +++ b/ui/views/controls/menu/submenu_view.h @@ -10,7 +10,7 @@ #include "base/compiler_specific.h" #include "ui/views/controls/menu/menu_delegate.h" -#include "views/view.h" +#include "ui/views/view.h" namespace views { diff --git a/ui/views/controls/message_box_view.h b/ui/views/controls/message_box_view.h index 64e1f27..cd81e40 100644 --- a/ui/views/controls/message_box_view.h +++ b/ui/views/controls/message_box_view.h @@ -9,7 +9,7 @@ #include <string> #include "base/string16.h" -#include "views/view.h" +#include "ui/views/view.h" namespace views { diff --git a/ui/views/controls/native/native_view_host.h b/ui/views/controls/native/native_view_host.h index 674222b..52575f0 100644 --- a/ui/views/controls/native/native_view_host.h +++ b/ui/views/controls/native/native_view_host.h @@ -9,7 +9,7 @@ #include <string> #include "ui/gfx/native_widget_types.h" -#include "views/view.h" +#include "ui/views/view.h" namespace views { diff --git a/ui/views/controls/native_control.h b/ui/views/controls/native_control.h index ba419c1..f439d3f 100644 --- a/ui/views/controls/native_control.h +++ b/ui/views/controls/native_control.h @@ -9,7 +9,7 @@ #include <windows.h> #include "ui/base/keycodes/keyboard_codes.h" -#include "views/view.h" +#include "ui/views/view.h" namespace views { diff --git a/ui/views/controls/progress_bar.h b/ui/views/controls/progress_bar.h index 02674de..10ce757 100644 --- a/ui/views/controls/progress_bar.h +++ b/ui/views/controls/progress_bar.h @@ -8,7 +8,7 @@ #include "base/basictypes.h" #include "base/compiler_specific.h" -#include "views/view.h" +#include "ui/views/view.h" namespace views { diff --git a/ui/views/controls/resize_area.h b/ui/views/controls/resize_area.h index 1cebd9d..57c65ca 100644 --- a/ui/views/controls/resize_area.h +++ b/ui/views/controls/resize_area.h @@ -8,7 +8,7 @@ #include <string> -#include "views/view.h" +#include "ui/views/view.h" namespace views { diff --git a/ui/views/controls/scrollbar/base_scroll_bar_thumb.h b/ui/views/controls/scrollbar/base_scroll_bar_thumb.h index f015478..c7daf93 100644 --- a/ui/views/controls/scrollbar/base_scroll_bar_thumb.h +++ b/ui/views/controls/scrollbar/base_scroll_bar_thumb.h @@ -9,7 +9,7 @@ #include "ui/gfx/size.h" #include "ui/views/controls/button/custom_button.h" #include "ui/views/controls/scrollbar/scroll_bar.h" -#include "views/view.h" +#include "ui/views/view.h" namespace gfx { class Canvas; diff --git a/ui/views/controls/scrollbar/native_scroll_bar.h b/ui/views/controls/scrollbar/native_scroll_bar.h index 73892fc..c5a9776 100644 --- a/ui/views/controls/scrollbar/native_scroll_bar.h +++ b/ui/views/controls/scrollbar/native_scroll_bar.h @@ -11,7 +11,7 @@ #include "base/compiler_specific.h" #include "base/gtest_prod_util.h" #include "ui/views/controls/scrollbar/scroll_bar.h" -#include "views/view.h" +#include "ui/views/view.h" namespace views { diff --git a/ui/views/controls/scrollbar/native_scroll_bar_views.h b/ui/views/controls/scrollbar/native_scroll_bar_views.h index 6e26487d..136338c 100644 --- a/ui/views/controls/scrollbar/native_scroll_bar_views.h +++ b/ui/views/controls/scrollbar/native_scroll_bar_views.h @@ -12,7 +12,7 @@ #include "ui/views/controls/button/button.h" #include "ui/views/controls/scrollbar/base_scroll_bar.h" #include "ui/views/controls/scrollbar/native_scroll_bar_wrapper.h" -#include "views/view.h" +#include "ui/views/view.h" namespace gfx { class Canvas; diff --git a/ui/views/controls/scrollbar/scroll_bar.h b/ui/views/controls/scrollbar/scroll_bar.h index 6e83ffa..39263d6 100644 --- a/ui/views/controls/scrollbar/scroll_bar.h +++ b/ui/views/controls/scrollbar/scroll_bar.h @@ -8,7 +8,7 @@ #include "base/basictypes.h" #include "base/compiler_specific.h" -#include "views/view.h" +#include "ui/views/view.h" #include "views/views_export.h" namespace views { diff --git a/ui/views/controls/separator.h b/ui/views/controls/separator.h index f47d238..44df432 100644 --- a/ui/views/controls/separator.h +++ b/ui/views/controls/separator.h @@ -8,7 +8,7 @@ #include <string> -#include "views/view.h" +#include "ui/views/view.h" namespace views { diff --git a/ui/views/controls/single_split_view.h b/ui/views/controls/single_split_view.h index c3f2bc3..6c3cd97 100644 --- a/ui/views/controls/single_split_view.h +++ b/ui/views/controls/single_split_view.h @@ -7,7 +7,7 @@ #pragma once #include "base/gtest_prod_util.h" -#include "views/view.h" +#include "ui/views/view.h" namespace views { diff --git a/ui/views/controls/tabbed_pane/native_tabbed_pane_views.h b/ui/views/controls/tabbed_pane/native_tabbed_pane_views.h index 87a9291..5ab456a 100644 --- a/ui/views/controls/tabbed_pane/native_tabbed_pane_views.h +++ b/ui/views/controls/tabbed_pane/native_tabbed_pane_views.h @@ -9,7 +9,7 @@ #include <vector> #include "ui/views/controls/tabbed_pane/native_tabbed_pane_wrapper.h" -#include "views/view.h" +#include "ui/views/view.h" namespace views { diff --git a/ui/views/controls/tabbed_pane/tabbed_pane.h b/ui/views/controls/tabbed_pane/tabbed_pane.h index 97a9d63..4150662 100644 --- a/ui/views/controls/tabbed_pane/tabbed_pane.h +++ b/ui/views/controls/tabbed_pane/tabbed_pane.h @@ -9,7 +9,7 @@ #include "base/basictypes.h" #include "base/compiler_specific.h" #include "base/string16.h" -#include "views/view.h" +#include "ui/views/view.h" namespace views { diff --git a/ui/views/controls/table/table_view2.h b/ui/views/controls/table/table_view2.h index d4f4e86..5ba5c1b 100644 --- a/ui/views/controls/table/table_view2.h +++ b/ui/views/controls/table/table_view2.h @@ -16,7 +16,7 @@ #include "ui/gfx/canvas.h" #include "ui/views/controls/table/native_table_wrapper.h" #include "ui/views/controls/table/table_view.h" -#include "views/view.h" +#include "ui/views/view.h" namespace ui { struct TableColumn; diff --git a/ui/views/controls/textfield/native_textfield_views.h b/ui/views/controls/textfield/native_textfield_views.h index a9cfbb8..8ff590c 100644 --- a/ui/views/controls/textfield/native_textfield_views.h +++ b/ui/views/controls/textfield/native_textfield_views.h @@ -16,8 +16,8 @@ #include "ui/views/controls/textfield/textfield_views_model.h" #include "ui/views/drag_controller.h" #include "ui/views/touchui/touch_selection_controller.h" +#include "ui/views/view.h" #include "views/border.h" -#include "views/view.h" namespace base { class Time; diff --git a/ui/views/controls/textfield/textfield.h b/ui/views/controls/textfield/textfield.h index abdd590..5ff542d 100644 --- a/ui/views/controls/textfield/textfield.h +++ b/ui/views/controls/textfield/textfield.h @@ -19,7 +19,7 @@ #include "ui/gfx/font.h" #include "ui/gfx/native_widget_types.h" #include "ui/views/controls/textfield/native_textfield_wrapper.h" -#include "views/view.h" +#include "ui/views/view.h" #if !defined(OS_LINUX) #include "base/logging.h" diff --git a/ui/views/controls/throbber.h b/ui/views/controls/throbber.h index e40828f..28ead31 100644 --- a/ui/views/controls/throbber.h +++ b/ui/views/controls/throbber.h @@ -10,7 +10,7 @@ #include "base/compiler_specific.h" #include "base/time.h" #include "base/timer.h" -#include "views/view.h" +#include "ui/views/view.h" class SkBitmap; diff --git a/ui/views/debug_utils.cc b/ui/views/debug_utils.cc index 23a75d2..9298122 100644 --- a/ui/views/debug_utils.cc +++ b/ui/views/debug_utils.cc @@ -6,7 +6,7 @@ #include "base/logging.h" #include "base/utf_string_conversions.h" -#include "views/view.h" +#include "ui/views/view.h" #ifndef NDEBUG #include <iostream> diff --git a/ui/views/events/event.cc b/ui/views/events/event.cc index 1fd17ec..5f2d320 100644 --- a/ui/views/events/event.cc +++ b/ui/views/events/event.cc @@ -6,8 +6,8 @@ #include "base/logging.h" #include "ui/base/keycodes/keyboard_code_conversion.h" +#include "ui/views/view.h" #include "ui/views/widget/root_view.h" -#include "views/view.h" namespace views { diff --git a/ui/views/examples/button_example.cc b/ui/views/examples/button_example.cc index ca15c36..6a16864 100644 --- a/ui/views/examples/button_example.cc +++ b/ui/views/examples/button_example.cc @@ -9,7 +9,7 @@ #include "ui/base/resource/resource_bundle.h" #include "ui/views/controls/button/checkbox.h" #include "ui/views/layout/fill_layout.h" -#include "views/view.h" +#include "ui/views/view.h" namespace views { namespace examples { diff --git a/ui/views/examples/example_base.cc b/ui/views/examples/example_base.cc index f86278e..1336c0c 100644 --- a/ui/views/examples/example_base.cc +++ b/ui/views/examples/example_base.cc @@ -9,7 +9,7 @@ #include "base/compiler_specific.h" #include "base/stringprintf.h" #include "ui/views/examples/examples_window.h" -#include "views/view.h" +#include "ui/views/view.h" namespace views { namespace examples { diff --git a/ui/views/examples/link_example.cc b/ui/views/examples/link_example.cc index 183badc..4882aaa 100644 --- a/ui/views/examples/link_example.cc +++ b/ui/views/examples/link_example.cc @@ -7,7 +7,7 @@ #include "base/utf_string_conversions.h" #include "ui/views/controls/link.h" #include "ui/views/layout/fill_layout.h" -#include "views/view.h" +#include "ui/views/view.h" namespace views { namespace examples { diff --git a/ui/views/examples/menu_example.cc b/ui/views/examples/menu_example.cc index db27e07..50640c3 100644 --- a/ui/views/examples/menu_example.cc +++ b/ui/views/examples/menu_example.cc @@ -13,7 +13,7 @@ #include "ui/views/controls/menu/menu_2.h" #include "ui/views/controls/menu/view_menu_delegate.h" #include "ui/views/layout/fill_layout.h" -#include "views/view.h" +#include "ui/views/view.h" namespace views { namespace examples { diff --git a/ui/views/examples/message_box_example.cc b/ui/views/examples/message_box_example.cc index 942f22b..3bb6eb7 100644 --- a/ui/views/examples/message_box_example.cc +++ b/ui/views/examples/message_box_example.cc @@ -7,7 +7,7 @@ #include "base/utf_string_conversions.h" #include "ui/views/controls/message_box_view.h" #include "ui/views/layout/grid_layout.h" -#include "views/view.h" +#include "ui/views/view.h" namespace views { namespace examples { diff --git a/ui/views/examples/native_widget_views_example.cc b/ui/views/examples/native_widget_views_example.cc index c42bc72..783ca4a 100644 --- a/ui/views/examples/native_widget_views_example.cc +++ b/ui/views/examples/native_widget_views_example.cc @@ -9,9 +9,9 @@ #include "ui/views/controls/button/text_button.h" #include "ui/views/examples/example_base.h" #include "ui/views/test/test_views_delegate.h" +#include "ui/views/view.h" #include "ui/views/widget/native_widget_views.h" #include "ui/views/widget/widget.h" -#include "views/view.h" namespace examples { diff --git a/ui/views/examples/progress_bar_example.cc b/ui/views/examples/progress_bar_example.cc index f355007..3a93bc5 100644 --- a/ui/views/examples/progress_bar_example.cc +++ b/ui/views/examples/progress_bar_example.cc @@ -8,7 +8,7 @@ #include "ui/views/controls/button/text_button.h" #include "ui/views/controls/progress_bar.h" #include "ui/views/layout/grid_layout.h" -#include "views/view.h" +#include "ui/views/view.h" namespace { diff --git a/ui/views/examples/radio_button_example.cc b/ui/views/examples/radio_button_example.cc index 315793a..42a6ea9 100644 --- a/ui/views/examples/radio_button_example.cc +++ b/ui/views/examples/radio_button_example.cc @@ -8,7 +8,7 @@ #include "base/utf_string_conversions.h" #include "ui/views/controls/button/text_button.h" #include "ui/views/layout/grid_layout.h" -#include "views/view.h" +#include "ui/views/view.h" namespace views { namespace examples { diff --git a/ui/views/examples/scroll_view_example.cc b/ui/views/examples/scroll_view_example.cc index 5247640..a0f293f 100644 --- a/ui/views/examples/scroll_view_example.cc +++ b/ui/views/examples/scroll_view_example.cc @@ -8,7 +8,7 @@ #include "base/utf_string_conversions.h" #include "ui/views/controls/button/radio_button.h" #include "ui/views/layout/grid_layout.h" -#include "views/view.h" +#include "ui/views/view.h" namespace views { namespace examples { diff --git a/ui/views/examples/text_example.cc b/ui/views/examples/text_example.cc index d51b1d5..c167d00 100644 --- a/ui/views/examples/text_example.cc +++ b/ui/views/examples/text_example.cc @@ -13,7 +13,7 @@ #include "ui/views/controls/label.h" #include "ui/views/examples/example_combobox_model.h" #include "ui/views/layout/grid_layout.h" -#include "views/view.h" +#include "ui/views/view.h" namespace { diff --git a/ui/views/examples/textfield_example.cc b/ui/views/examples/textfield_example.cc index 780cd81..7b4986c 100644 --- a/ui/views/examples/textfield_example.cc +++ b/ui/views/examples/textfield_example.cc @@ -10,7 +10,7 @@ #include "ui/views/controls/label.h" #include "ui/views/controls/textfield/textfield.h" #include "ui/views/layout/grid_layout.h" -#include "views/view.h" +#include "ui/views/view.h" namespace views { namespace examples { diff --git a/ui/views/examples/throbber_example.cc b/ui/views/examples/throbber_example.cc index a3af5cf..913d079 100644 --- a/ui/views/examples/throbber_example.cc +++ b/ui/views/examples/throbber_example.cc @@ -6,7 +6,7 @@ #include "ui/views/controls/throbber.h" #include "ui/views/layout/fill_layout.h" -#include "views/view.h" +#include "ui/views/view.h" namespace views { namespace examples { diff --git a/ui/views/examples/widget_example.cc b/ui/views/examples/widget_example.cc index 76ccb49..11d6504 100644 --- a/ui/views/examples/widget_example.cc +++ b/ui/views/examples/widget_example.cc @@ -8,8 +8,8 @@ #include "ui/views/controls/button/text_button.h" #include "ui/views/layout/box_layout.h" #include "ui/views/layout/layout_manager.h" +#include "ui/views/view.h" #include "ui/views/widget/widget.h" -#include "views/view.h" namespace views { namespace examples { diff --git a/ui/views/focus/accelerator_handler_gtk_unittest.cc b/ui/views/focus/accelerator_handler_gtk_unittest.cc index cdf24e1..d4db6f5 100644 --- a/ui/views/focus/accelerator_handler_gtk_unittest.cc +++ b/ui/views/focus/accelerator_handler_gtk_unittest.cc @@ -9,9 +9,9 @@ #include "ui/gfx/rect.h" #include "ui/views/focus/accelerator_handler.h" #include "ui/views/focus/focus_manager.h" +#include "ui/views/view.h" #include "ui/views/widget/widget.h" #include "ui/views/widget/widget_delegate.h" -#include "views/view.h" namespace views { diff --git a/ui/views/focus/external_focus_tracker.cc b/ui/views/focus/external_focus_tracker.cc index 7e58bc6..cd14496 100644 --- a/ui/views/focus/external_focus_tracker.cc +++ b/ui/views/focus/external_focus_tracker.cc @@ -6,7 +6,7 @@ #include "base/logging.h" #include "ui/views/focus/view_storage.h" -#include "views/view.h" +#include "ui/views/view.h" namespace views { diff --git a/ui/views/focus/focus_manager.cc b/ui/views/focus/focus_manager.cc index 61567bc..ca2a55f 100644 --- a/ui/views/focus/focus_manager.cc +++ b/ui/views/focus/focus_manager.cc @@ -15,9 +15,9 @@ #include "ui/views/focus/focus_search.h" #include "ui/views/focus/view_storage.h" #include "ui/views/focus/widget_focus_manager.h" +#include "ui/views/view.h" #include "ui/views/widget/root_view.h" #include "ui/views/widget/widget.h" -#include "views/view.h" namespace views { diff --git a/ui/views/focus/focus_search.cc b/ui/views/focus/focus_search.cc index e1f3c7b..56ec02f 100644 --- a/ui/views/focus/focus_search.cc +++ b/ui/views/focus/focus_search.cc @@ -5,7 +5,7 @@ #include "base/logging.h" #include "ui/views/focus/focus_manager.h" #include "ui/views/focus/focus_search.h" -#include "views/view.h" +#include "ui/views/view.h" namespace views { diff --git a/ui/views/focus/focus_search.h b/ui/views/focus/focus_search.h index 9fe37a0..50ba6f8 100644 --- a/ui/views/focus/focus_search.h +++ b/ui/views/focus/focus_search.h @@ -6,7 +6,7 @@ #define UI_VIEWS_FOCUS_FOCUS_SEARCH_H_ #pragma once -#include "views/view.h" +#include "ui/views/view.h" namespace views { diff --git a/ui/views/focus/view_storage.h b/ui/views/focus/view_storage.h index 7d748e2..80ea032 100644 --- a/ui/views/focus/view_storage.h +++ b/ui/views/focus/view_storage.h @@ -7,7 +7,7 @@ #pragma once #include "base/memory/singleton.h" -#include "views/view.h" +#include "ui/views/view.h" // This class is a simple storage place for storing/retrieving views. It is // used for example in the FocusManager to store/restore focused views when the diff --git a/ui/views/ime/input_method_base.cc b/ui/views/ime/input_method_base.cc index cb9573c..14d2efd 100644 --- a/ui/views/ime/input_method_base.cc +++ b/ui/views/ime/input_method_base.cc @@ -5,8 +5,8 @@ #include "ui/base/ime/text_input_client.h" #include "ui/views/ime/input_method_base.h" #include "ui/views/ime/text_input_type_tracker.h" +#include "ui/views/view.h" #include "ui/views/widget/widget.h" -#include "views/view.h" #include "base/logging.h" diff --git a/ui/views/ime/input_method_gtk.h b/ui/views/ime/input_method_gtk.h index 670669f..ddb9f240 100644 --- a/ui/views/ime/input_method_gtk.h +++ b/ui/views/ime/input_method_gtk.h @@ -14,7 +14,7 @@ #include "ui/base/ime/composition_text.h" #include "ui/base/ime/text_input_client.h" #include "ui/views/ime/input_method_base.h" -#include "views/view.h" +#include "ui/views/view.h" namespace views { diff --git a/ui/views/ime/input_method_ibus.h b/ui/views/ime/input_method_ibus.h index c4b516b..8af9c11 100644 --- a/ui/views/ime/input_method_ibus.h +++ b/ui/views/ime/input_method_ibus.h @@ -19,7 +19,7 @@ #include "ui/base/ime/text_input_client.h" #include "ui/views/events/event.h" #include "ui/views/ime/input_method_base.h" -#include "views/view.h" +#include "ui/views/view.h" // Forward declarations, so that we don't need to include ibus.h in this file. typedef struct _GAsyncResult GAsyncResult; diff --git a/ui/views/ime/input_method_win.h b/ui/views/ime/input_method_win.h index 5a34673..2be706c 100644 --- a/ui/views/ime/input_method_win.h +++ b/ui/views/ime/input_method_win.h @@ -14,8 +14,8 @@ #include "base/compiler_specific.h" #include "ui/base/win/ime_input.h" #include "ui/views/ime/input_method_base.h" +#include "ui/views/view.h" #include "ui/views/widget/widget.h" -#include "views/view.h" namespace views { diff --git a/ui/views/ime/mock_input_method.h b/ui/views/ime/mock_input_method.h index 5384661..1fe432e 100644 --- a/ui/views/ime/mock_input_method.h +++ b/ui/views/ime/mock_input_method.h @@ -12,7 +12,7 @@ #include "base/compiler_specific.h" #include "ui/base/ime/composition_text.h" #include "ui/views/ime/input_method_base.h" -#include "views/view.h" +#include "ui/views/view.h" namespace views { diff --git a/ui/views/layout/box_layout.cc b/ui/views/layout/box_layout.cc index 7471b4c..332322f 100644 --- a/ui/views/layout/box_layout.cc +++ b/ui/views/layout/box_layout.cc @@ -6,7 +6,7 @@ #include "ui/gfx/insets.h" #include "ui/gfx/rect.h" -#include "views/view.h" +#include "ui/views/view.h" namespace views { diff --git a/ui/views/layout/box_layout_unittest.cc b/ui/views/layout/box_layout_unittest.cc index 0430c0c..dcf3227 100644 --- a/ui/views/layout/box_layout_unittest.cc +++ b/ui/views/layout/box_layout_unittest.cc @@ -4,7 +4,7 @@ #include "testing/gtest/include/gtest/gtest.h" #include "ui/views/layout/box_layout.h" -#include "views/view.h" +#include "ui/views/view.h" class StaticSizedView : public views::View { public: diff --git a/ui/views/layout/fill_layout.h b/ui/views/layout/fill_layout.h index b7d4c41..59a3f5d 100644 --- a/ui/views/layout/fill_layout.h +++ b/ui/views/layout/fill_layout.h @@ -8,7 +8,7 @@ #include "base/compiler_specific.h" #include "ui/views/layout/layout_manager.h" -#include "views/view.h" +#include "ui/views/view.h" namespace views { diff --git a/ui/views/layout/grid_layout.cc b/ui/views/layout/grid_layout.cc index 7444b8b..8fb1005 100644 --- a/ui/views/layout/grid_layout.cc +++ b/ui/views/layout/grid_layout.cc @@ -10,7 +10,7 @@ #include "base/stl_util.h" #include "ui/gfx/insets.h" #include "ui/views/layout/layout_constants.h" -#include "views/view.h" +#include "ui/views/view.h" namespace views { diff --git a/ui/views/layout/grid_layout.h b/ui/views/layout/grid_layout.h index a57e16d..70e9502 100644 --- a/ui/views/layout/grid_layout.h +++ b/ui/views/layout/grid_layout.h @@ -11,7 +11,7 @@ #include "base/compiler_specific.h" #include "ui/views/layout/layout_manager.h" -#include "views/view.h" +#include "ui/views/view.h" namespace gfx { class Insets; diff --git a/ui/views/layout/grid_layout_unittest.cc b/ui/views/layout/grid_layout_unittest.cc index d7911c6..4c29cc6 100644 --- a/ui/views/layout/grid_layout_unittest.cc +++ b/ui/views/layout/grid_layout_unittest.cc @@ -4,7 +4,7 @@ #include "testing/gtest/include/gtest/gtest.h" #include "ui/views/layout/grid_layout.h" -#include "views/view.h" +#include "ui/views/view.h" using views::ColumnSet; using views::GridLayout; diff --git a/ui/views/layout/layout_manager.cc b/ui/views/layout/layout_manager.cc index f2b4e8a..188cc6f1 100644 --- a/ui/views/layout/layout_manager.cc +++ b/ui/views/layout/layout_manager.cc @@ -4,7 +4,7 @@ #include "ui/views/layout/layout_manager.h" -#include "views/view.h" +#include "ui/views/view.h" namespace views { diff --git a/ui/views/mouse_watcher.cc b/ui/views/mouse_watcher.cc index 555af23..1115901 100644 --- a/ui/views/mouse_watcher.cc +++ b/ui/views/mouse_watcher.cc @@ -11,8 +11,8 @@ #include "base/message_loop.h" #include "ui/base/events.h" #include "ui/gfx/screen.h" +#include "ui/views/view.h" #include "ui/views/widget/widget.h" -#include "views/view.h" namespace views { diff --git a/ui/views/touchui/gesture_manager.cc b/ui/views/touchui/gesture_manager.cc index 8aba2a31..4dd6b0a 100644 --- a/ui/views/touchui/gesture_manager.cc +++ b/ui/views/touchui/gesture_manager.cc @@ -10,8 +10,8 @@ #include "base/logging.h" #include "ui/views/events/event.h" +#include "ui/views/view.h" #include "ui/views/widget/widget.h" -#include "views/view.h" namespace views { diff --git a/ui/views/touchui/gesture_manager.h b/ui/views/touchui/gesture_manager.h index 50e266c..e5ec4b5 100644 --- a/ui/views/touchui/gesture_manager.h +++ b/ui/views/touchui/gesture_manager.h @@ -7,7 +7,7 @@ #pragma once #include "base/memory/singleton.h" -#include "views/view.h" +#include "ui/views/view.h" namespace ui { enum TouchStatus; diff --git a/ui/views/touchui/touch_selection_controller.h b/ui/views/touchui/touch_selection_controller.h index c117dc2..5318c9d 100644 --- a/ui/views/touchui/touch_selection_controller.h +++ b/ui/views/touchui/touch_selection_controller.h @@ -8,7 +8,7 @@ #include "ui/base/models/simple_menu_model.h" #include "ui/gfx/point.h" -#include "views/view.h" +#include "ui/views/view.h" namespace views { diff --git a/ui/views/touchui/touch_selection_controller_impl.h b/ui/views/touchui/touch_selection_controller_impl.h index 494482e..f0d5a5d 100644 --- a/ui/views/touchui/touch_selection_controller_impl.h +++ b/ui/views/touchui/touch_selection_controller_impl.h @@ -9,7 +9,7 @@ #include "base/timer.h" #include "ui/gfx/point.h" #include "ui/views/touchui/touch_selection_controller.h" -#include "views/view.h" +#include "ui/views/view.h" #include "views/views_export.h" namespace views { diff --git a/ui/views/view.cc b/ui/views/view.cc new file mode 100644 index 0000000..09d3cbe --- /dev/null +++ b/ui/views/view.cc @@ -0,0 +1,2061 @@ +// 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/view.h" + +#include <algorithm> + +#include "base/debug/trace_event.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/message_loop.h" +#include "base/stringprintf.h" +#include "base/utf_string_conversions.h" +#include "third_party/skia/include/core/SkRect.h" +#include "ui/base/accessibility/accessibility_types.h" +#include "ui/base/dragdrop/drag_drop_types.h" +#include "ui/gfx/canvas_skia.h" +#include "ui/gfx/compositor/compositor.h" +#include "ui/gfx/compositor/layer.h" +#include "ui/gfx/compositor/layer_animator.h" +#include "ui/gfx/interpolated_transform.h" +#include "ui/gfx/path.h" +#include "ui/gfx/point3.h" +#include "ui/gfx/transform.h" +#include "ui/views/context_menu_controller.h" +#include "ui/views/drag_controller.h" +#include "ui/views/layout/layout_manager.h" +#include "ui/views/widget/native_widget_private.h" +#include "ui/views/widget/root_view.h" +#include "ui/views/widget/tooltip_manager.h" +#include "ui/views/widget/widget.h" +#include "views/background.h" +#include "views/views_delegate.h" + +#if defined(OS_WIN) +#include "base/win/scoped_gdi_object.h" +#include "ui/views/accessibility/native_view_accessibility_win.h" +#endif +#if defined(TOOLKIT_USES_GTK) +#include "ui/base/gtk/scoped_handle_gtk.h" +#endif + +namespace { + +// Whether to use accelerated compositing when necessary (e.g. when a view has a +// transformation). +#if defined(VIEWS_COMPOSITOR) +bool use_acceleration_when_possible = true; +#else +bool use_acceleration_when_possible = false; +#endif + +// Saves the drawing state, and restores the state when going out of scope. +class ScopedCanvas { + public: + explicit ScopedCanvas(gfx::Canvas* canvas) : canvas_(canvas) { + if (canvas_) + canvas_->Save(); + } + ~ScopedCanvas() { + if (canvas_) + canvas_->Restore(); + } + void SetCanvas(gfx::Canvas* canvas) { + if (canvas_) + canvas_->Restore(); + canvas_ = canvas; + canvas_->Save(); + } + + private: + gfx::Canvas* canvas_; + + DISALLOW_COPY_AND_ASSIGN(ScopedCanvas); +}; + +// Returns the top view in |view|'s hierarchy. +const views::View* GetHierarchyRoot(const views::View* view) { + const views::View* root = view; + while (root && root->parent()) + root = root->parent(); + return root; +} + +} // namespace + +namespace views { + +// static +ViewsDelegate* ViewsDelegate::views_delegate = NULL; + +// static +const char View::kViewClassName[] = "views/View"; + +//////////////////////////////////////////////////////////////////////////////// +// View, public: + +// TO BE MOVED ----------------------------------------------------------------- + +void View::SetHotTracked(bool flag) { +} + +bool View::IsHotTracked() const { + return false; +} + +// Creation and lifetime ------------------------------------------------------- + +View::View() + : parent_owned_(true), + id_(0), + group_(-1), + parent_(NULL), + visible_(true), + enabled_(true), + painting_enabled_(true), + registered_for_visible_bounds_notification_(false), + clip_x_(0.0), + clip_y_(0.0), + needs_layout_(true), + flip_canvas_on_paint_for_rtl_ui_(false), + paint_to_layer_(false), + accelerator_registration_delayed_(false), + accelerator_focus_manager_(NULL), + registered_accelerator_count_(0), + next_focusable_view_(NULL), + previous_focusable_view_(NULL), + focusable_(false), + accessibility_focusable_(false), + context_menu_controller_(NULL), + drag_controller_(NULL) { +} + +View::~View() { + if (parent_) + parent_->RemoveChildView(this); + + for (Views::const_iterator i(children_.begin()); i != children_.end(); ++i) { + (*i)->parent_ = NULL; + if ((*i)->parent_owned()) + delete *i; + } + +#if defined(OS_WIN) + if (native_view_accessibility_win_.get()) + native_view_accessibility_win_->set_view(NULL); +#endif +} + +// Tree operations ------------------------------------------------------------- + +const Widget* View::GetWidget() const { + // The root view holds a reference to this view hierarchy's Widget. + return parent_ ? parent_->GetWidget() : NULL; +} + +Widget* View::GetWidget() { + return const_cast<Widget*>(const_cast<const View*>(this)->GetWidget()); +} + +void View::AddChildView(View* view) { + AddChildViewAt(view, child_count()); +} + +void View::AddChildViewAt(View* view, int index) { + CHECK_NE(view, this) << "You cannot add a view as its own child"; + + // If |view| has a parent, remove it from its parent. + View* parent = view->parent_; + if (parent) + parent->RemoveChildView(view); + + // Sets the prev/next focus views. + InitFocusSiblings(view, index); + + // Let's insert the view. + view->parent_ = this; + children_.insert(children_.begin() + index, view); + + for (View* v = this; v; v = v->parent_) + v->ViewHierarchyChangedImpl(false, true, this, view); + + view->PropagateAddNotifications(this, view); + UpdateTooltip(); + if (GetWidget()) + RegisterChildrenForVisibleBoundsNotification(view); + + if (layout_manager_.get()) + layout_manager_->ViewAdded(this, view); + + if (use_acceleration_when_possible) + ReorderLayers(); + + // Make sure the visibility of the child layers are correct. + // If any of the parent View is hidden, then the layers of the subtree + // rooted at |this| should be hidden. Otherwise, all the child layers should + // inherit the visibility of the owner View. + UpdateLayerVisibility(); +} + +void View::ReorderChildView(View* view, int index) { + DCHECK_EQ(view->parent_, this); + if (index < 0) + index = child_count() - 1; + else if (index >= child_count()) + return; + if (children_[index] == view) + return; + + const Views::iterator i(std::find(children_.begin(), children_.end(), view)); + DCHECK(i != children_.end()); + children_.erase(i); + + // Unlink the view first + View* next_focusable = view->next_focusable_view_; + View* prev_focusable = view->previous_focusable_view_; + if (prev_focusable) + prev_focusable->next_focusable_view_ = next_focusable; + if (next_focusable) + next_focusable->previous_focusable_view_ = prev_focusable; + + // Add it in the specified index now. + InitFocusSiblings(view, index); + children_.insert(children_.begin() + index, view); + + if (use_acceleration_when_possible) + ReorderLayers(); +} + +void View::RemoveChildView(View* view) { + DoRemoveChildView(view, true, true, false); +} + +void View::RemoveAllChildViews(bool delete_children) { + while (!children_.empty()) + DoRemoveChildView(children_.front(), false, false, delete_children); + UpdateTooltip(); +} + +bool View::Contains(const View* view) const { + for (const View* v = view; v; v = v->parent_) { + if (v == this) + return true; + } + return false; +} + +int View::GetIndexOf(const View* view) const { + Views::const_iterator i(std::find(children_.begin(), children_.end(), view)); + return i != children_.end() ? static_cast<int>(i - children_.begin()) : -1; +} + +// Size and disposition -------------------------------------------------------- + +void View::SetBounds(int x, int y, int width, int height) { + SetBoundsRect(gfx::Rect(x, y, std::max(0, width), std::max(0, height))); +} + +void View::SetBoundsRect(const gfx::Rect& bounds) { + if (bounds == bounds_) { + if (needs_layout_) { + needs_layout_ = false; + Layout(); + SchedulePaint(); + } + return; + } + + if (IsVisible()) { + // Paint where the view is currently. + SchedulePaintBoundsChanged( + bounds_.size() == bounds.size() ? SCHEDULE_PAINT_SIZE_SAME : + SCHEDULE_PAINT_SIZE_CHANGED); + } + + gfx::Rect prev = bounds_; + bounds_ = bounds; + BoundsChanged(prev); +} + +void View::SetSize(const gfx::Size& size) { + SetBounds(x(), y(), size.width(), size.height()); +} + +void View::SetPosition(const gfx::Point& position) { + SetBounds(position.x(), position.y(), width(), height()); +} + +void View::SetX(int x) { + SetBounds(x, y(), width(), height()); +} + +void View::SetY(int y) { + SetBounds(x(), y, width(), height()); +} + +gfx::Rect View::GetContentsBounds() const { + gfx::Rect contents_bounds(GetLocalBounds()); + if (border_.get()) { + gfx::Insets insets; + border_->GetInsets(&insets); + contents_bounds.Inset(insets); + } + return contents_bounds; +} + +gfx::Rect View::GetLocalBounds() const { + return gfx::Rect(gfx::Point(), size()); +} + +gfx::Insets View::GetInsets() const { + gfx::Insets insets; + if (border_.get()) + border_->GetInsets(&insets); + return insets; +} + +gfx::Rect View::GetVisibleBounds() const { + if (!IsVisibleInRootView()) + return gfx::Rect(); + gfx::Rect vis_bounds(0, 0, width(), height()); + gfx::Rect ancestor_bounds; + const View* view = this; + ui::Transform transform; + + while (view != NULL && !vis_bounds.IsEmpty()) { + transform.ConcatTransform(view->GetTransform()); + transform.ConcatTranslate(static_cast<float>(view->GetMirroredX()), + static_cast<float>(view->y())); + + vis_bounds = view->ConvertRectToParent(vis_bounds); + const View* ancestor = view->parent_; + if (ancestor != NULL) { + ancestor_bounds.SetRect(0, 0, ancestor->width(), ancestor->height()); + vis_bounds = vis_bounds.Intersect(ancestor_bounds); + } else if (!view->GetWidget()) { + // If the view has no Widget, we're not visible. Return an empty rect. + return gfx::Rect(); + } + view = ancestor; + } + if (vis_bounds.IsEmpty()) + return vis_bounds; + // Convert back to this views coordinate system. + transform.TransformRectReverse(&vis_bounds); + return vis_bounds; +} + +gfx::Rect View::GetScreenBounds() const { + gfx::Point origin; + View::ConvertPointToScreen(this, &origin); + return gfx::Rect(origin, size()); +} + +gfx::Size View::GetPreferredSize() { + if (layout_manager_.get()) + return layout_manager_->GetPreferredSize(this); + return gfx::Size(); +} + +int View::GetBaseline() const { + return -1; +} + +void View::SizeToPreferredSize() { + gfx::Size prefsize = GetPreferredSize(); + if ((prefsize.width() != width()) || (prefsize.height() != height())) + SetBounds(x(), y(), prefsize.width(), prefsize.height()); +} + +gfx::Size View::GetMinimumSize() { + return GetPreferredSize(); +} + +int View::GetHeightForWidth(int w) { + if (layout_manager_.get()) + return layout_manager_->GetPreferredHeightForWidth(this, w); + return GetPreferredSize().height(); +} + +void View::SetVisible(bool visible) { + if (visible != visible_) { + // If the View is currently visible, schedule paint to refresh parent. + // TODO(beng): not sure we should be doing this if we have a layer. + if (visible_) + SchedulePaint(); + + visible_ = visible; + + // This notifies all sub-views recursively. + PropagateVisibilityNotifications(this, visible_); + UpdateLayerVisibility(); + + // If we are newly visible, schedule paint. + if (visible_) + SchedulePaint(); + } +} + +bool View::IsVisible() const { + return visible_; +} + +bool View::IsVisibleInRootView() const { + return IsVisible() && parent_ ? parent_->IsVisibleInRootView() : false; +} + +void View::SetEnabled(bool enabled) { + if (enabled != enabled_) { + enabled_ = enabled; + OnEnabledChanged(); + } +} + +bool View::IsEnabled() const { + return enabled_; +} + +void View::OnEnabledChanged() { + SchedulePaint(); +} + +// Transformations ------------------------------------------------------------- + +const ui::Transform& View::GetTransform() const { + static const ui::Transform* no_op = new ui::Transform; + return layer() ? layer()->transform() : *no_op; +} + +void View::SetTransform(const ui::Transform& transform) { + if (!transform.HasChange()) { + if (layer()) { + layer()->SetTransform(transform); + if (!paint_to_layer_) + DestroyLayer(); + } else { + // Nothing. + } + } else { + if (!layer()) + CreateLayer(); + layer()->SetTransform(transform); + layer()->ScheduleDraw(); + } +} + +void View::SetPaintToLayer(bool paint_to_layer) { + paint_to_layer_ = paint_to_layer; + if (paint_to_layer_ && !layer()) { + CreateLayer(); + } else if (!paint_to_layer_ && layer()) { + DestroyLayer(); + } +} + +// RTL positioning ------------------------------------------------------------- + +gfx::Rect View::GetMirroredBounds() const { + gfx::Rect bounds(bounds_); + bounds.set_x(GetMirroredX()); + return bounds; +} + +gfx::Point View::GetMirroredPosition() const { + return gfx::Point(GetMirroredX(), y()); +} + +int View::GetMirroredX() const { + return parent_ ? parent_->GetMirroredXForRect(bounds_) : x(); +} + +int View::GetMirroredXForRect(const gfx::Rect& bounds) const { + return base::i18n::IsRTL() ? + (width() - bounds.x() - bounds.width()) : bounds.x(); +} + +int View::GetMirroredXInView(int x) const { + return base::i18n::IsRTL() ? width() - x : x; +} + +int View::GetMirroredXWithWidthInView(int x, int w) const { + return base::i18n::IsRTL() ? width() - x - w : x; +} + +// Layout ---------------------------------------------------------------------- + +void View::Layout() { + needs_layout_ = false; + + // If we have a layout manager, let it handle the layout for us. + if (layout_manager_.get()) + layout_manager_->Layout(this); + + // Make sure to propagate the Layout() call to any children that haven't + // received it yet through the layout manager and need to be laid out. This + // is needed for the case when the child requires a layout but its bounds + // weren't changed by the layout manager. If there is no layout manager, we + // just propagate the Layout() call down the hierarchy, so whoever receives + // the call can take appropriate action. + for (int i = 0, count = child_count(); i < count; ++i) { + View* child = child_at(i); + if (child->needs_layout_ || !layout_manager_.get()) { + child->needs_layout_ = false; + child->Layout(); + } + } +} + +void View::InvalidateLayout() { + // Always invalidate up. This is needed to handle the case of us already being + // valid, but not our parent. + needs_layout_ = true; + if (parent_) + parent_->InvalidateLayout(); +} + +LayoutManager* View::GetLayoutManager() const { + return layout_manager_.get(); +} + +void View::SetLayoutManager(LayoutManager* layout_manager) { + if (layout_manager_.get()) + layout_manager_->Uninstalled(this); + + layout_manager_.reset(layout_manager); + if (layout_manager_.get()) + layout_manager_->Installed(this); +} + +// Attributes ------------------------------------------------------------------ + +std::string View::GetClassName() const { + return kViewClassName; +} + +View* View::GetAncestorWithClassName(const std::string& name) { + for (View* view = this; view; view = view->parent_) { + if (view->GetClassName() == name) + return view; + } + return NULL; +} + +const View* View::GetViewByID(int id) const { + if (id == id_) + return const_cast<View*>(this); + + for (int i = 0, count = child_count(); i < count; ++i) { + const View* view = child_at(i)->GetViewByID(id); + if (view) + return view; + } + return NULL; +} + +View* View::GetViewByID(int id) { + return const_cast<View*>(const_cast<const View*>(this)->GetViewByID(id)); +} + +void View::SetGroup(int gid) { + // Don't change the group id once it's set. + DCHECK(group_ == -1 || group_ == gid); + group_ = gid; +} + +int View::GetGroup() const { + return group_; +} + +bool View::IsGroupFocusTraversable() const { + return true; +} + +void View::GetViewsInGroup(int group, Views* views) { + if (group_ == group) + views->push_back(this); + + for (int i = 0, count = child_count(); i < count; ++i) + child_at(i)->GetViewsInGroup(group, views); +} + +View* View::GetSelectedViewForGroup(int group) { + Views views; + GetWidget()->GetRootView()->GetViewsInGroup(group, &views); + return views.empty() ? NULL : views[0]; +} + +// Coordinate conversion ------------------------------------------------------- + +// static +void View::ConvertPointToView(const View* source, + const View* target, + gfx::Point* point) { + if (source == target) + return; + + // |source| can be NULL. + const View* root = GetHierarchyRoot(target); + if (source) { + CHECK_EQ(GetHierarchyRoot(source), root); + + if (source != root) + source->ConvertPointForAncestor(root, point); + } + + if (target != root) + target->ConvertPointFromAncestor(root, point); + + // API defines NULL |source| as returning the point in screen coordinates. + if (!source) { + *point = point->Subtract( + root->GetWidget()->GetClientAreaScreenBounds().origin()); + } +} + +// static +void View::ConvertPointToWidget(const View* src, gfx::Point* p) { + DCHECK(src); + DCHECK(p); + + src->ConvertPointForAncestor(NULL, p); +} + +// static +void View::ConvertPointFromWidget(const View* dest, gfx::Point* p) { + DCHECK(dest); + DCHECK(p); + + dest->ConvertPointFromAncestor(NULL, p); +} + +// static +void View::ConvertPointToScreen(const View* src, gfx::Point* p) { + DCHECK(src); + DCHECK(p); + + // If the view is not connected to a tree, there's nothing we can do. + const Widget* widget = src->GetWidget(); + if (widget) { + ConvertPointToWidget(src, p); + gfx::Rect r = widget->GetClientAreaScreenBounds(); + p->SetPoint(p->x() + r.x(), p->y() + r.y()); + } +} + +gfx::Rect View::ConvertRectToParent(const gfx::Rect& rect) const { + gfx::Rect x_rect = rect; + GetTransform().TransformRect(&x_rect); + x_rect.Offset(GetMirroredPosition()); + return x_rect; +} + +gfx::Rect View::ConvertRectToWidget(const gfx::Rect& rect) const { + gfx::Rect x_rect = rect; + for (const View* v = this; v; v = v->parent_) + x_rect = v->ConvertRectToParent(x_rect); + return x_rect; +} + +// Painting -------------------------------------------------------------------- + +void View::SchedulePaint() { + SchedulePaintInRect(GetLocalBounds()); +} + +void View::SchedulePaintInRect(const gfx::Rect& rect) { + if (!IsVisible() || !painting_enabled_) + return; + + if (layer()) { + layer()->SchedulePaint(rect); + } else if (parent_) { + // Translate the requested paint rect to the parent's coordinate system + // then pass this notification up to the parent. + parent_->SchedulePaintInRect(ConvertRectToParent(rect)); + } +} + +void View::Paint(gfx::Canvas* canvas) { + TRACE_EVENT0("views", "View::Paint"); + + ScopedCanvas scoped_canvas(canvas); + + // Paint this View and its children, setting the clip rect to the bounds + // of this View and translating the origin to the local bounds' top left + // point. + // + // Note that the X (or left) position we pass to ClipRectInt takes into + // consideration whether or not the view uses a right-to-left layout so that + // we paint our view in its mirrored position if need be. + if (!canvas->ClipRect(gfx::Rect(GetMirroredX(), y(), + width() - static_cast<int>(clip_x_), + height() - static_cast<int>(clip_y_)))) { + return; + } + // Non-empty clip, translate the graphics such that 0,0 corresponds to + // where this view is located (related to its parent). + canvas->Translate(GetMirroredPosition()); + canvas->Transform(GetTransform()); + + PaintCommon(canvas); +} + +ThemeProvider* View::GetThemeProvider() const { + const Widget* widget = GetWidget(); + return widget ? widget->GetThemeProvider() : NULL; +} + +// Accelerated Painting -------------------------------------------------------- + +// static +void View::set_use_acceleration_when_possible(bool use) { + use_acceleration_when_possible = use; +} + +// static +bool View::get_use_acceleration_when_possible() { + return use_acceleration_when_possible; +} + +// Input ----------------------------------------------------------------------- + +View* View::GetEventHandlerForPoint(const gfx::Point& point) { + // Walk the child Views recursively looking for the View that most + // tightly encloses the specified point. + for (int i = child_count() - 1; i >= 0; --i) { + View* child = child_at(i); + if (!child->IsVisible()) + continue; + + gfx::Point point_in_child_coords(point); + View::ConvertPointToView(this, child, &point_in_child_coords); + if (child->HitTest(point_in_child_coords)) + return child->GetEventHandlerForPoint(point_in_child_coords); + } + return this; +} + +gfx::NativeCursor View::GetCursor(const MouseEvent& event) { +#if defined(OS_WIN) && !defined(USE_AURA) + static HCURSOR arrow = LoadCursor(NULL, IDC_ARROW); + return arrow; +#else + return gfx::kNullCursor; +#endif +} + +bool View::HitTest(const gfx::Point& l) const { + if (GetLocalBounds().Contains(l)) { + if (HasHitTestMask()) { + gfx::Path mask; + GetHitTestMask(&mask); +#if defined(USE_AURA) + // TODO: should we use this every where? + SkRegion clip_region; + clip_region.setRect(0, 0, width(), height()); + SkRegion mask_region; + return mask_region.setPath(mask, clip_region) && + mask_region.contains(l.x(), l.y()); +#elif defined(OS_WIN) + base::win::ScopedRegion rgn(mask.CreateNativeRegion()); + return !!PtInRegion(rgn, l.x(), l.y()); +#elif defined(TOOLKIT_USES_GTK) + ui::ScopedRegion rgn(mask.CreateNativeRegion()); + return gdk_region_point_in(rgn.Get(), l.x(), l.y()); +#endif + } + // No mask, but inside our bounds. + return true; + } + // Outside our bounds. + return false; +} + +bool View::OnMousePressed(const MouseEvent& event) { + return false; +} + +bool View::OnMouseDragged(const MouseEvent& event) { + return false; +} + +void View::OnMouseReleased(const MouseEvent& event) { +} + +void View::OnMouseCaptureLost() { +} + +void View::OnMouseMoved(const MouseEvent& event) { +} + +void View::OnMouseEntered(const MouseEvent& event) { +} + +void View::OnMouseExited(const MouseEvent& event) { +} + +ui::TouchStatus View::OnTouchEvent(const TouchEvent& event) { + DVLOG(1) << "visited the OnTouchEvent"; + return ui::TOUCH_STATUS_UNKNOWN; +} + +void View::SetMouseHandler(View *new_mouse_handler) { + // It is valid for new_mouse_handler to be NULL + if (parent_) + parent_->SetMouseHandler(new_mouse_handler); +} + +bool View::OnKeyPressed(const KeyEvent& event) { + return false; +} + +bool View::OnKeyReleased(const KeyEvent& event) { + return false; +} + +bool View::OnMouseWheel(const MouseWheelEvent& event) { + return false; +} + +ui::TextInputClient* View::GetTextInputClient() { + return NULL; +} + +InputMethod* View::GetInputMethod() { + Widget* widget = GetWidget(); + return widget ? widget->GetInputMethod() : NULL; +} + +// Accelerators ---------------------------------------------------------------- + +void View::AddAccelerator(const ui::Accelerator& accelerator) { + if (!accelerators_.get()) + accelerators_.reset(new std::vector<ui::Accelerator>()); + + DCHECK(std::find(accelerators_->begin(), accelerators_->end(), accelerator) == + accelerators_->end()) + << "Registering the same accelerator multiple times"; + + accelerators_->push_back(accelerator); + RegisterPendingAccelerators(); +} + +void View::RemoveAccelerator(const ui::Accelerator& accelerator) { + if (!accelerators_.get()) { + NOTREACHED() << "Removing non-existing accelerator"; + return; + } + + std::vector<ui::Accelerator>::iterator i( + std::find(accelerators_->begin(), accelerators_->end(), accelerator)); + if (i == accelerators_->end()) { + NOTREACHED() << "Removing non-existing accelerator"; + return; + } + + size_t index = i - accelerators_->begin(); + accelerators_->erase(i); + if (index >= registered_accelerator_count_) { + // The accelerator is not registered to FocusManager. + return; + } + --registered_accelerator_count_; + + // Providing we are attached to a Widget and registered with a focus manager, + // we should de-register from that focus manager now. + if (GetWidget() && accelerator_focus_manager_) + accelerator_focus_manager_->UnregisterAccelerator(accelerator, this); +} + +void View::ResetAccelerators() { + if (accelerators_.get()) + UnregisterAccelerators(false); +} + +bool View::AcceleratorPressed(const ui::Accelerator& accelerator) { + return false; +} + +// Focus ----------------------------------------------------------------------- + +bool View::HasFocus() const { + const FocusManager* focus_manager = GetFocusManager(); + return focus_manager && (focus_manager->GetFocusedView() == this); +} + +View* View::GetNextFocusableView() { + return next_focusable_view_; +} + +const View* View::GetNextFocusableView() const { + return next_focusable_view_; +} + +View* View::GetPreviousFocusableView() { + return previous_focusable_view_; +} + +void View::SetNextFocusableView(View* view) { + view->previous_focusable_view_ = this; + next_focusable_view_ = view; +} + +bool View::IsFocusableInRootView() const { + return IsFocusable() && IsVisibleInRootView(); +} + +bool View::IsAccessibilityFocusableInRootView() const { + return (focusable_ || accessibility_focusable_) && IsEnabled() && + IsVisibleInRootView(); +} + +FocusManager* View::GetFocusManager() { + Widget* widget = GetWidget(); + return widget ? widget->GetFocusManager() : NULL; +} + +const FocusManager* View::GetFocusManager() const { + const Widget* widget = GetWidget(); + return widget ? widget->GetFocusManager() : NULL; +} + +void View::RequestFocus() { + FocusManager* focus_manager = GetFocusManager(); + if (focus_manager && IsFocusableInRootView()) + focus_manager->SetFocusedView(this); +} + +bool View::SkipDefaultKeyEventProcessing(const KeyEvent& event) { + return false; +} + +FocusTraversable* View::GetFocusTraversable() { + return NULL; +} + +FocusTraversable* View::GetPaneFocusTraversable() { + return NULL; +} + +// Tooltips -------------------------------------------------------------------- + +bool View::GetTooltipText(const gfx::Point& p, string16* tooltip) const { + return false; +} + +bool View::GetTooltipTextOrigin(const gfx::Point& p, gfx::Point* loc) const { + return false; +} + +// Context menus --------------------------------------------------------------- + +void View::ShowContextMenu(const gfx::Point& p, bool is_mouse_gesture) { + if (!context_menu_controller_) + return; + + context_menu_controller_->ShowContextMenuForView(this, p, is_mouse_gesture); +} + +// Drag and drop --------------------------------------------------------------- + +bool View::GetDropFormats( + int* formats, + std::set<OSExchangeData::CustomFormat>* custom_formats) { + return false; +} + +bool View::AreDropTypesRequired() { + return false; +} + +bool View::CanDrop(const OSExchangeData& data) { + // TODO(sky): when I finish up migration, this should default to true. + return false; +} + +void View::OnDragEntered(const DropTargetEvent& event) { +} + +int View::OnDragUpdated(const DropTargetEvent& event) { + return ui::DragDropTypes::DRAG_NONE; +} + +void View::OnDragExited() { +} + +int View::OnPerformDrop(const DropTargetEvent& event) { + return ui::DragDropTypes::DRAG_NONE; +} + +void View::OnDragDone() { +} + +// static +bool View::ExceededDragThreshold(int delta_x, int delta_y) { + return (abs(delta_x) > GetHorizontalDragThreshold() || + abs(delta_y) > GetVerticalDragThreshold()); +} + +// Scrolling ------------------------------------------------------------------- + +void View::ScrollRectToVisible(const gfx::Rect& rect) { + // We must take RTL UI mirroring into account when adjusting the position of + // the region. + if (parent_) { + gfx::Rect scroll_rect(rect); + scroll_rect.Offset(GetMirroredX(), y()); + parent_->ScrollRectToVisible(scroll_rect); + } +} + +int View::GetPageScrollIncrement(ScrollView* scroll_view, + bool is_horizontal, bool is_positive) { + return 0; +} + +int View::GetLineScrollIncrement(ScrollView* scroll_view, + bool is_horizontal, bool is_positive) { + return 0; +} + +//////////////////////////////////////////////////////////////////////////////// +// View, protected: + +// Size and disposition -------------------------------------------------------- + +void View::OnBoundsChanged(const gfx::Rect& previous_bounds) { +} + +void View::PreferredSizeChanged() { + InvalidateLayout(); + if (parent_) + parent_->ChildPreferredSizeChanged(this); +} + +bool View::NeedsNotificationWhenVisibleBoundsChange() const { + return false; +} + +void View::OnVisibleBoundsChanged() { +} + +// Tree operations ------------------------------------------------------------- + +void View::ViewHierarchyChanged(bool is_add, View* parent, View* child) { +} + +void View::VisibilityChanged(View* starting_from, bool is_visible) { +} + +void View::NativeViewHierarchyChanged(bool attached, + gfx::NativeView native_view, + internal::RootView* root_view) { + FocusManager* focus_manager = GetFocusManager(); + if (!accelerator_registration_delayed_ && + accelerator_focus_manager_ && + accelerator_focus_manager_ != focus_manager) { + UnregisterAccelerators(true); + accelerator_registration_delayed_ = true; + } + if (accelerator_registration_delayed_ && attached) { + if (focus_manager) { + RegisterPendingAccelerators(); + accelerator_registration_delayed_ = false; + } + } +} + +// Painting -------------------------------------------------------------------- + +void View::PaintChildren(gfx::Canvas* canvas) { + TRACE_EVENT0("views", "View::PaintChildren"); + for (int i = 0, count = child_count(); i < count; ++i) + if (!child_at(i)->layer()) + child_at(i)->Paint(canvas); +} + +void View::OnPaint(gfx::Canvas* canvas) { + TRACE_EVENT0("views", "View::OnPaint"); + OnPaintBackground(canvas); + OnPaintFocusBorder(canvas); + OnPaintBorder(canvas); +} + +void View::OnPaintBackground(gfx::Canvas* canvas) { + if (background_.get()) { + TRACE_EVENT2("views", "View::OnPaintBackground", + "width", canvas->GetSkCanvas()->getDevice()->width(), + "height", canvas->GetSkCanvas()->getDevice()->height()); + background_->Paint(canvas, this); + } +} + +void View::OnPaintBorder(gfx::Canvas* canvas) { + if (border_.get()) { + TRACE_EVENT2("views", "View::OnPaintBorder", + "width", canvas->GetSkCanvas()->getDevice()->width(), + "height", canvas->GetSkCanvas()->getDevice()->height()); + border_->Paint(*this, canvas); + } +} + +void View::OnPaintFocusBorder(gfx::Canvas* canvas) { + if ((IsFocusable() || IsAccessibilityFocusableInRootView()) && HasFocus()) { + TRACE_EVENT2("views", "views::OnPaintFocusBorder", + "width", canvas->GetSkCanvas()->getDevice()->width(), + "height", canvas->GetSkCanvas()->getDevice()->height()); + canvas->DrawFocusRect(GetLocalBounds()); + } +} + +// Accelerated Painting -------------------------------------------------------- + +void View::SetFillsBoundsOpaquely(bool fills_bounds_opaquely) { + // This method should not have the side-effect of creating the layer. + if (layer()) + layer()->SetFillsBoundsOpaquely(fills_bounds_opaquely); +} + +bool View::SetExternalTexture(ui::Texture* texture) { + DCHECK(texture); + SetPaintToLayer(true); + + layer()->SetExternalTexture(texture); + + // Child views must not paint into the external texture. So make sure each + // child view has its own layer to paint into. + for (Views::iterator i = children_.begin(); i != children_.end(); ++i) + (*i)->SetPaintToLayer(true); + + SchedulePaintInRect(GetLocalBounds()); + return true; +} + +void View::CalculateOffsetToAncestorWithLayer(gfx::Point* offset, + ui::Layer** layer_parent) { + if (layer()) { + if (layer_parent) + *layer_parent = layer(); + return; + } + if (!parent_) + return; + + offset->Offset(x(), y()); + parent_->CalculateOffsetToAncestorWithLayer(offset, layer_parent); +} + +void View::MoveLayerToParent(ui::Layer* parent_layer, + const gfx::Point& point) { + gfx::Point local_point(point); + if (parent_layer != layer()) + local_point.Offset(x(), y()); + if (layer() && parent_layer != layer()) { + parent_layer->Add(layer()); + layer()->SetBounds(gfx::Rect(local_point.x(), local_point.y(), + width(), height())); + } else { + for (int i = 0, count = child_count(); i < count; ++i) + child_at(i)->MoveLayerToParent(parent_layer, local_point); + } +} + +void View::UpdateLayerVisibility() { + if (!use_acceleration_when_possible) + return; + bool visible = IsVisible(); + for (const View* v = parent_; visible && v && !v->layer(); v = v->parent_) + visible = v->IsVisible(); + + UpdateChildLayerVisibility(visible); +} + +void View::UpdateChildLayerVisibility(bool ancestor_visible) { + if (layer()) { + layer()->SetVisible(ancestor_visible && IsVisible()); + } else { + for (int i = 0, count = child_count(); i < count; ++i) + child_at(i)->UpdateChildLayerVisibility(ancestor_visible && IsVisible()); + } +} + +void View::UpdateChildLayerBounds(const gfx::Point& offset) { + if (layer()) { + layer()->SetBounds(gfx::Rect(offset.x(), offset.y(), width(), height())); + } else { + for (int i = 0, count = child_count(); i < count; ++i) { + gfx::Point new_offset(offset.x() + child_at(i)->x(), + offset.y() + child_at(i)->y()); + child_at(i)->UpdateChildLayerBounds(new_offset); + } + } +} + +void View::OnPaintLayer(gfx::Canvas* canvas) { + if (!layer() || !layer()->fills_bounds_opaquely()) + canvas->GetSkCanvas()->drawColor(SK_ColorBLACK, SkXfermode::kClear_Mode); + PaintCommon(canvas); +} + +void View::ReorderLayers() { + View* v = this; + while (v && !v->layer()) + v = v->parent(); + + // Forward to widget in case we're in a NativeWidgetView. + if (!v) { + if (GetWidget()) + GetWidget()->ReorderLayers(); + } else { + for (Views::const_iterator i(v->children_.begin()); + i != v->children_.end(); + ++i) + (*i)->ReorderChildLayers(v->layer()); + } +} + +void View::ReorderChildLayers(ui::Layer* parent_layer) { + if (layer()) { + DCHECK_EQ(parent_layer, layer()->parent()); + parent_layer->StackAtTop(layer()); + } else { + for (Views::const_iterator i(children_.begin()); i != children_.end(); ++i) + (*i)->ReorderChildLayers(parent_layer); + } +} + +// Input ----------------------------------------------------------------------- + +bool View::HasHitTestMask() const { + return false; +} + +void View::GetHitTestMask(gfx::Path* mask) const { + DCHECK(mask); +} + +// Focus ----------------------------------------------------------------------- + +bool View::IsFocusable() const { + return focusable_ && IsEnabled() && IsVisible(); +} + +void View::OnFocus() { + // TODO(beng): Investigate whether it's possible for us to move this to + // Focus(). + // By default, we clear the native focus. This ensures that no visible native + // view as the focus and that we still receive keyboard inputs. + FocusManager* focus_manager = GetFocusManager(); + if (focus_manager) + focus_manager->ClearNativeFocus(); + + // TODO(beng): Investigate whether it's possible for us to move this to + // Focus(). + // Notify assistive technologies of the focus change. + GetWidget()->NotifyAccessibilityEvent( + this, ui::AccessibilityTypes::EVENT_FOCUS, true); +} + +void View::OnBlur() { +} + +void View::Focus() { + SchedulePaint(); + OnFocus(); +} + +void View::Blur() { + SchedulePaint(); + OnBlur(); +} + +// Tooltips -------------------------------------------------------------------- + +void View::TooltipTextChanged() { + Widget* widget = GetWidget(); + // TooltipManager may be null if there is a problem creating it. + if (widget && widget->native_widget_private()->GetTooltipManager()) { + widget->native_widget_private()->GetTooltipManager()-> + TooltipTextChanged(this); + } +} + +// Context menus --------------------------------------------------------------- + +gfx::Point View::GetKeyboardContextMenuLocation() { + gfx::Rect vis_bounds = GetVisibleBounds(); + gfx::Point screen_point(vis_bounds.x() + vis_bounds.width() / 2, + vis_bounds.y() + vis_bounds.height() / 2); + ConvertPointToScreen(this, &screen_point); + return screen_point; +} + +// Drag and drop --------------------------------------------------------------- + +int View::GetDragOperations(const gfx::Point& press_pt) { + return drag_controller_ ? + drag_controller_->GetDragOperationsForView(this, press_pt) : + ui::DragDropTypes::DRAG_NONE; +} + +void View::WriteDragData(const gfx::Point& press_pt, OSExchangeData* data) { + DCHECK(drag_controller_); + drag_controller_->WriteDragDataForView(this, press_pt, data); +} + +bool View::InDrag() { + Widget* widget = GetWidget(); + return widget ? widget->dragged_view() == this : false; +} + +// Debugging ------------------------------------------------------------------- + +#if !defined(NDEBUG) + +std::string View::PrintViewGraph(bool first) { + return DoPrintViewGraph(first, this); +} + +std::string View::DoPrintViewGraph(bool first, View* view_with_children) { + // 64-bit pointer = 16 bytes of hex + "0x" + '\0' = 19. + const size_t kMaxPointerStringLength = 19; + + std::string result; + + if (first) + result.append("digraph {\n"); + + // Node characteristics. + char p[kMaxPointerStringLength]; + + size_t baseNameIndex = GetClassName().find_last_of('/'); + if (baseNameIndex == std::string::npos) + baseNameIndex = 0; + else + baseNameIndex++; + + char bounds_buffer[512]; + + // Information about current node. + base::snprintf(p, arraysize(bounds_buffer), "%p", view_with_children); + result.append(" N"); + result.append(p+2); + result.append(" [label=\""); + + result.append(GetClassName().substr(baseNameIndex).c_str()); + + base::snprintf(bounds_buffer, + arraysize(bounds_buffer), + "\\n bounds: (%d, %d), (%dx%d)", + this->bounds().x(), + this->bounds().y(), + this->bounds().width(), + this->bounds().height()); + result.append(bounds_buffer); + + if (layer() && !layer()->hole_rect().IsEmpty()) { + base::snprintf(bounds_buffer, + arraysize(bounds_buffer), + "\\n hole bounds: (%d, %d), (%dx%d)", + layer()->hole_rect().x(), + layer()->hole_rect().y(), + layer()->hole_rect().width(), + layer()->hole_rect().height()); + result.append(bounds_buffer); + } + + if (GetTransform().HasChange()) { + gfx::Point translation; + float rotation; + gfx::Point3f scale; + if (ui::InterpolatedTransform::FactorTRS(GetTransform(), + &translation, + &rotation, + &scale)) { + if (translation != gfx::Point(0, 0)) { + base::snprintf(bounds_buffer, + arraysize(bounds_buffer), + "\\n translation: (%d, %d)", + translation.x(), + translation.y()); + result.append(bounds_buffer); + } + + if (fabs(rotation) > 1e-5) { + base::snprintf(bounds_buffer, + arraysize(bounds_buffer), + "\\n rotation: %3.2f", rotation); + result.append(bounds_buffer); + } + + if (scale.AsPoint() != gfx::Point(0, 0)) { + base::snprintf(bounds_buffer, + arraysize(bounds_buffer), + "\\n scale: (%2.4f, %2.4f)", + scale.x(), + scale.y()); + result.append(bounds_buffer); + } + } + } + + result.append("\""); + if (!parent_) + result.append(", shape=box"); + if (layer()) { + if (layer()->texture()) + result.append(", color=green"); + else + result.append(", color=red"); + + if (layer()->fills_bounds_opaquely()) + result.append(", style=filled"); + } + result.append("]\n"); + + // Link to parent. + if (parent_) { + char pp[kMaxPointerStringLength]; + + base::snprintf(pp, kMaxPointerStringLength, "%p", parent_); + result.append(" N"); + result.append(pp+2); + result.append(" -> N"); + result.append(p+2); + result.append("\n"); + } + + // Children. + for (int i = 0, count = view_with_children->child_count(); i < count; ++i) + result.append(view_with_children->child_at(i)->PrintViewGraph(false)); + + if (first) + result.append("}\n"); + + return result; +} + +#endif + +//////////////////////////////////////////////////////////////////////////////// +// View, private: + +// DropInfo -------------------------------------------------------------------- + +void View::DragInfo::Reset() { + possible_drag = false; + start_pt = gfx::Point(); +} + +void View::DragInfo::PossibleDrag(const gfx::Point& p) { + possible_drag = true; + start_pt = p; +} + +// Painting -------------------------------------------------------------------- + +void View::SchedulePaintBoundsChanged(SchedulePaintType type) { + // If we have a layer and the View's size did not change, we do not need to + // schedule any paints since the layer will be redrawn at its new location + // during the next Draw() cycle in the compositor. + if (!layer() || type == SCHEDULE_PAINT_SIZE_CHANGED) { + // Otherwise, if the size changes or we don't have a layer then we need to + // use SchedulePaint to invalidate the area occupied by the View. + SchedulePaint(); + } else if (parent_ && type == SCHEDULE_PAINT_SIZE_SAME) { + // The compositor doesn't Draw() until something on screen changes, so + // if our position changes but nothing is being animated on screen, then + // tell the compositor to redraw the scene. We know layer() exists due to + // the above if clause. + layer()->ScheduleDraw(); + } +} + +void View::PaintCommon(gfx::Canvas* canvas) { + if (!IsVisible() || !painting_enabled_) + return; + + { + // If the View we are about to paint requested the canvas to be flipped, we + // should change the transform appropriately. + // The canvas mirroring is undone once the View is done painting so that we + // don't pass the canvas with the mirrored transform to Views that didn't + // request the canvas to be flipped. + ScopedCanvas scoped(canvas); + if (FlipCanvasOnPaintForRTLUI()) { + canvas->Translate(gfx::Point(width(), 0)); + canvas->Scale(-1, 1); + } + + OnPaint(canvas); + } + + PaintChildren(canvas); +} + +// Tree operations ------------------------------------------------------------- + +void View::DoRemoveChildView(View* view, + bool update_focus_cycle, + bool update_tool_tip, + bool delete_removed_view) { + DCHECK(view); + const Views::iterator i(std::find(children_.begin(), children_.end(), view)); + scoped_ptr<View> view_to_be_deleted; + if (i != children_.end()) { + if (update_focus_cycle) { + // Let's remove the view from the focus traversal. + View* next_focusable = view->next_focusable_view_; + View* prev_focusable = view->previous_focusable_view_; + if (prev_focusable) + prev_focusable->next_focusable_view_ = next_focusable; + if (next_focusable) + next_focusable->previous_focusable_view_ = prev_focusable; + } + + if (GetWidget()) + UnregisterChildrenForVisibleBoundsNotification(view); + view->PropagateRemoveNotifications(this); + view->parent_ = NULL; + view->UpdateLayerVisibility(); + + if (delete_removed_view && view->parent_owned()) + view_to_be_deleted.reset(view); + + children_.erase(i); + } + + if (update_tool_tip) + UpdateTooltip(); + + if (layout_manager_.get()) + layout_manager_->ViewRemoved(this, view); +} + +void View::PropagateRemoveNotifications(View* parent) { + for (int i = 0, count = child_count(); i < count; ++i) + child_at(i)->PropagateRemoveNotifications(parent); + + for (View* v = this; v; v = v->parent_) + v->ViewHierarchyChangedImpl(true, false, parent, this); +} + +void View::PropagateAddNotifications(View* parent, View* child) { + for (int i = 0, count = child_count(); i < count; ++i) + child_at(i)->PropagateAddNotifications(parent, child); + ViewHierarchyChangedImpl(true, true, parent, child); +} + +void View::PropagateNativeViewHierarchyChanged(bool attached, + gfx::NativeView native_view, + internal::RootView* root_view) { + for (int i = 0, count = child_count(); i < count; ++i) + child_at(i)->PropagateNativeViewHierarchyChanged(attached, + native_view, + root_view); + NativeViewHierarchyChanged(attached, native_view, root_view); +} + +void View::ViewHierarchyChangedImpl(bool register_accelerators, + bool is_add, + View* parent, + View* child) { + if (register_accelerators) { + if (is_add) { + // If you get this registration, you are part of a subtree that has been + // added to the view hierarchy. + if (GetFocusManager()) { + RegisterPendingAccelerators(); + } else { + // Delay accelerator registration until visible as we do not have + // focus manager until then. + accelerator_registration_delayed_ = true; + } + } else { + if (child == this) + UnregisterAccelerators(true); + } + } + + if (is_add && layer() && !layer()->parent()) { + UpdateParentLayer(); + } else if (!is_add && child == this) { + // Make sure the layers beloning to the subtree rooted at |child| get + // removed from layers that do not belong in the same subtree. + OrphanLayers(); + } + + ViewHierarchyChanged(is_add, parent, child); + parent->needs_layout_ = true; +} + +// Size and disposition -------------------------------------------------------- + +void View::PropagateVisibilityNotifications(View* start, bool is_visible) { + for (int i = 0, count = child_count(); i < count; ++i) + child_at(i)->PropagateVisibilityNotifications(start, is_visible); + VisibilityChangedImpl(start, is_visible); +} + +void View::VisibilityChangedImpl(View* starting_from, bool is_visible) { + if (is_visible) + RegisterPendingAccelerators(); + else + UnregisterAccelerators(true); + VisibilityChanged(starting_from, is_visible); +} + +void View::BoundsChanged(const gfx::Rect& previous_bounds) { + if (IsVisible()) { + // Paint the new bounds. + SchedulePaintBoundsChanged( + bounds_.size() == previous_bounds.size() ? SCHEDULE_PAINT_SIZE_SAME : + SCHEDULE_PAINT_SIZE_CHANGED); + } + + if (use_acceleration_when_possible) { + if (layer()) { + if (parent_) { + gfx::Point offset; + parent_->CalculateOffsetToAncestorWithLayer(&offset, NULL); + offset.Offset(x(), y()); + layer()->SetBounds(gfx::Rect(offset, size())); + } else { + layer()->SetBounds(bounds_); + } + // TODO(beng): this seems redundant with the SchedulePaint at the top of + // this function. explore collapsing. + if (previous_bounds.size() != bounds_.size() && + !layer()->layer_updated_externally()) { + // If our bounds have changed then we need to update the complete + // texture. + layer()->SchedulePaint(GetLocalBounds()); + } + } else { + // If our bounds have changed, then any descendant layer bounds may + // have changed. Update them accordingly. + gfx::Point offset; + CalculateOffsetToAncestorWithLayer(&offset, NULL); + UpdateChildLayerBounds(offset); + } + } + + OnBoundsChanged(previous_bounds); + + if (previous_bounds.size() != size()) { + needs_layout_ = false; + Layout(); + } + + if (NeedsNotificationWhenVisibleBoundsChange()) + OnVisibleBoundsChanged(); + + // Notify interested Views that visible bounds within the root view may have + // changed. + if (descendants_to_notify_.get()) { + for (Views::iterator i(descendants_to_notify_->begin()); + i != descendants_to_notify_->end(); ++i) { + (*i)->OnVisibleBoundsChanged(); + } + } +} + +// static +void View::RegisterChildrenForVisibleBoundsNotification(View* view) { + if (view->NeedsNotificationWhenVisibleBoundsChange()) + view->RegisterForVisibleBoundsNotification(); + for (int i = 0; i < view->child_count(); ++i) + RegisterChildrenForVisibleBoundsNotification(view->child_at(i)); +} + +// static +void View::UnregisterChildrenForVisibleBoundsNotification(View* view) { + if (view->NeedsNotificationWhenVisibleBoundsChange()) + view->UnregisterForVisibleBoundsNotification(); + for (int i = 0; i < view->child_count(); ++i) + UnregisterChildrenForVisibleBoundsNotification(view->child_at(i)); +} + +void View::RegisterForVisibleBoundsNotification() { + if (registered_for_visible_bounds_notification_) + return; + + registered_for_visible_bounds_notification_ = true; + for (View* ancestor = parent_; ancestor; ancestor = ancestor->parent_) + ancestor->AddDescendantToNotify(this); +} + +void View::UnregisterForVisibleBoundsNotification() { + if (!registered_for_visible_bounds_notification_) + return; + + registered_for_visible_bounds_notification_ = false; + for (View* ancestor = parent_; ancestor; ancestor = ancestor->parent_) + ancestor->RemoveDescendantToNotify(this); +} + +void View::AddDescendantToNotify(View* view) { + DCHECK(view); + if (!descendants_to_notify_.get()) + descendants_to_notify_.reset(new Views); + descendants_to_notify_->push_back(view); +} + +void View::RemoveDescendantToNotify(View* view) { + DCHECK(view && descendants_to_notify_.get()); + Views::iterator i(std::find( + descendants_to_notify_->begin(), descendants_to_notify_->end(), view)); + DCHECK(i != descendants_to_notify_->end()); + descendants_to_notify_->erase(i); + if (descendants_to_notify_->empty()) + descendants_to_notify_.reset(); +} + +// Transformations ------------------------------------------------------------- + +bool View::GetTransformRelativeTo(const View* ancestor, + ui::Transform* transform) const { + const View* p = this; + + while (p && p != ancestor) { + transform->ConcatTransform(p->GetTransform()); + transform->ConcatTranslate(static_cast<float>(p->GetMirroredX()), + static_cast<float>(p->y())); + + p = p->parent_; + } + + return p == ancestor; +} + +// Coordinate conversion ------------------------------------------------------- + +bool View::ConvertPointForAncestor(const View* ancestor, + gfx::Point* point) const { + ui::Transform trans; + // TODO(sad): Have some way of caching the transformation results. + bool result = GetTransformRelativeTo(ancestor, &trans); + gfx::Point3f p(*point); + trans.TransformPoint(p); + *point = p.AsPoint(); + return result; +} + +bool View::ConvertPointFromAncestor(const View* ancestor, + gfx::Point* point) const { + ui::Transform trans; + bool result = GetTransformRelativeTo(ancestor, &trans); + gfx::Point3f p(*point); + trans.TransformPointReverse(p); + *point = p.AsPoint(); + return result; +} + +// Accelerated painting -------------------------------------------------------- + +void View::CreateLayer() { + // A new layer is being created for the view. So all the layers of the + // sub-tree can inherit the visibility of the corresponding view. + for (int i = 0, count = child_count(); i < count; ++i) + child_at(i)->UpdateChildLayerVisibility(true); + + layer_.reset(new ui::Layer()); + layer_->set_delegate(this); +#if !defined(NDEBUG) + layer_->set_name(GetClassName()); +#endif + + UpdateParentLayers(); + UpdateLayerVisibility(); + + // The new layer needs to be ordered in the layer tree according + // to the view tree. Children of this layer were added in order + // in UpdateParentLayers(). + if (parent()) + parent()->ReorderLayers(); +} + +void View::UpdateParentLayers() { + // Attach all top-level un-parented layers. + if (layer() && !layer()->parent()) { + UpdateParentLayer(); + } else { + for (int i = 0, count = child_count(); i < count; ++i) + child_at(i)->UpdateParentLayers(); + } +} + +void View::UpdateParentLayer() { + if (!layer()) + return; + + ui::Layer* parent_layer = NULL; + gfx::Point offset(x(), y()); + + // TODO(sad): The NULL check here for parent_ essentially is to check if this + // is the RootView. Instead of doing this, this function should be made + // virtual and overridden from the RootView. + if (parent_) + parent_->CalculateOffsetToAncestorWithLayer(&offset, &parent_layer); + else if (!parent_ && GetWidget()) + GetWidget()->CalculateOffsetToAncestorWithLayer(&offset, &parent_layer); + + ReparentLayer(offset, parent_layer); +} + +void View::OrphanLayers() { + if (layer()) { + if (layer()->parent()) + layer()->parent()->Remove(layer()); + + // The layer belonging to this View has already been orphaned. It is not + // necessary to orphan the child layers. + return; + } + for (int i = 0, count = child_count(); i < count; ++i) + child_at(i)->OrphanLayers(); +} + +void View::ReparentLayer(const gfx::Point& offset, ui::Layer* parent_layer) { + layer_->SetBounds(gfx::Rect(offset.x(), offset.y(), width(), height())); + DCHECK_NE(layer(), parent_layer); + if (parent_layer) + parent_layer->Add(layer()); + layer_->SchedulePaint(GetLocalBounds()); + MoveLayerToParent(layer(), gfx::Point()); +} + +void View::DestroyLayer() { + ui::Layer* new_parent = layer()->parent(); + std::vector<ui::Layer*> children = layer()->children(); + for (size_t i = 0; i < children.size(); ++i) { + layer()->Remove(children[i]); + if (new_parent) + new_parent->Add(children[i]); + } + + layer_.reset(); + + if (new_parent) + ReorderLayers(); + + gfx::Point offset; + CalculateOffsetToAncestorWithLayer(&offset, NULL); + UpdateChildLayerBounds(offset); + + SchedulePaint(); +} + +// Input ----------------------------------------------------------------------- + +bool View::ProcessMousePressed(const MouseEvent& event, DragInfo* drag_info) { + const bool enabled = IsEnabled(); + int drag_operations = + (enabled && event.IsOnlyLeftMouseButton() && HitTest(event.location())) ? + GetDragOperations(event.location()) : 0; + ContextMenuController* context_menu_controller = event.IsRightMouseButton() ? + context_menu_controller_ : 0; + + const bool result = OnMousePressed(event); + // WARNING: we may have been deleted, don't use any View variables. + + if (!enabled) + return result; + + if (drag_operations != ui::DragDropTypes::DRAG_NONE) { + drag_info->PossibleDrag(event.location()); + return true; + } + return !!context_menu_controller || result; +} + +bool View::ProcessMouseDragged(const MouseEvent& event, DragInfo* drag_info) { + // Copy the field, that way if we're deleted after drag and drop no harm is + // done. + ContextMenuController* context_menu_controller = context_menu_controller_; + const bool possible_drag = drag_info->possible_drag; + if (possible_drag && ExceededDragThreshold( + drag_info->start_pt.x() - event.x(), + drag_info->start_pt.y() - event.y())) { + if (!drag_controller_ || + drag_controller_->CanStartDragForView( + this, drag_info->start_pt, event.location())) + DoDrag(event, drag_info->start_pt); + } else { + if (OnMouseDragged(event)) + return true; + // Fall through to return value based on context menu controller. + } + // WARNING: we may have been deleted. + return (context_menu_controller != NULL) || possible_drag; +} + +void View::ProcessMouseReleased(const MouseEvent& event) { + if (context_menu_controller_ && event.IsOnlyRightMouseButton()) { + // Assume that if there is a context menu controller we won't be deleted + // from mouse released. + gfx::Point location(event.location()); + OnMouseReleased(event); + if (HitTest(location)) { + ConvertPointToScreen(this, &location); + ShowContextMenu(location, true); + } + } else { + OnMouseReleased(event); + } + // WARNING: we may have been deleted. +} + +ui::TouchStatus View::ProcessTouchEvent(const TouchEvent& event) { + // TODO(rjkroege): Implement a grab scheme similar to as as is found in + // MousePressed. + return OnTouchEvent(event); +} + +// Accelerators ---------------------------------------------------------------- + +void View::RegisterPendingAccelerators() { + if (!accelerators_.get() || + registered_accelerator_count_ == accelerators_->size()) { + // No accelerators are waiting for registration. + return; + } + + if (!GetWidget()) { + // The view is not yet attached to a widget, defer registration until then. + return; + } + + accelerator_focus_manager_ = GetFocusManager(); + if (!accelerator_focus_manager_) { + // Some crash reports seem to show that we may get cases where we have no + // focus manager (see bug #1291225). This should never be the case, just + // making sure we don't crash. + + // TODO(jcampan): This fails for a view under NativeWidgetGtk with + // TYPE_CHILD. (see http://crbug.com/21335) reenable + // NOTREACHED assertion and verify accelerators works as + // expected. +#if defined(OS_WIN) + NOTREACHED(); +#endif + return; + } + // Only register accelerators if we are visible. + if (!IsVisibleInRootView() || !GetWidget()->IsVisible()) + return; + for (std::vector<ui::Accelerator>::const_iterator i( + accelerators_->begin() + registered_accelerator_count_); + i != accelerators_->end(); ++i) { + accelerator_focus_manager_->RegisterAccelerator(*i, this); + } + registered_accelerator_count_ = accelerators_->size(); +} + +void View::UnregisterAccelerators(bool leave_data_intact) { + if (!accelerators_.get()) + return; + + if (GetWidget()) { + if (accelerator_focus_manager_) { + // We may not have a FocusManager if the window containing us is being + // closed, in which case the FocusManager is being deleted so there is + // nothing to unregister. + accelerator_focus_manager_->UnregisterAccelerators(this); + accelerator_focus_manager_ = NULL; + } + if (!leave_data_intact) { + accelerators_->clear(); + accelerators_.reset(); + } + registered_accelerator_count_ = 0; + } +} + +// Focus ----------------------------------------------------------------------- + +void View::InitFocusSiblings(View* v, int index) { + int count = child_count(); + + if (count == 0) { + v->next_focusable_view_ = NULL; + v->previous_focusable_view_ = NULL; + } else { + if (index == count) { + // We are inserting at the end, but the end of the child list may not be + // the last focusable element. Let's try to find an element with no next + // focusable element to link to. + View* last_focusable_view = NULL; + for (Views::iterator i(children_.begin()); i != children_.end(); ++i) { + if (!(*i)->next_focusable_view_) { + last_focusable_view = *i; + break; + } + } + if (last_focusable_view == NULL) { + // Hum... there is a cycle in the focus list. Let's just insert ourself + // after the last child. + View* prev = children_[index - 1]; + v->previous_focusable_view_ = prev; + v->next_focusable_view_ = prev->next_focusable_view_; + prev->next_focusable_view_->previous_focusable_view_ = v; + prev->next_focusable_view_ = v; + } else { + last_focusable_view->next_focusable_view_ = v; + v->next_focusable_view_ = NULL; + v->previous_focusable_view_ = last_focusable_view; + } + } else { + View* prev = children_[index]->GetPreviousFocusableView(); + v->previous_focusable_view_ = prev; + v->next_focusable_view_ = children_[index]; + if (prev) + prev->next_focusable_view_ = v; + children_[index]->previous_focusable_view_ = v; + } + } +} + +// System events --------------------------------------------------------------- + +void View::PropagateThemeChanged() { + for (int i = child_count() - 1; i >= 0; --i) + child_at(i)->PropagateThemeChanged(); + OnThemeChanged(); +} + +void View::PropagateLocaleChanged() { + for (int i = child_count() - 1; i >= 0; --i) + child_at(i)->PropagateLocaleChanged(); + OnLocaleChanged(); +} + +// Tooltips -------------------------------------------------------------------- + +void View::UpdateTooltip() { + Widget* widget = GetWidget(); + // TODO(beng): The TooltipManager NULL check can be removed when we + // consolidate Init() methods and make views_unittests Init() all + // Widgets that it uses. + if (widget && widget->native_widget_private()->GetTooltipManager()) + widget->native_widget_private()->GetTooltipManager()->UpdateTooltip(); +} + +// Drag and drop --------------------------------------------------------------- + +void View::DoDrag(const MouseEvent& event, const gfx::Point& press_pt) { + int drag_operations = GetDragOperations(press_pt); + if (drag_operations == ui::DragDropTypes::DRAG_NONE) + return; + + OSExchangeData data; + WriteDragData(press_pt, &data); + + // Message the RootView to do the drag and drop. That way if we're removed + // the RootView can detect it and avoid calling us back. + GetWidget()->RunShellDrag(this, data, drag_operations); +} + +} // namespace views diff --git a/ui/views/view.h b/ui/views/view.h new file mode 100644 index 0000000..6e8eb28 --- /dev/null +++ b/ui/views/view.h @@ -0,0 +1,1440 @@ +// 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_VIEW_H_ +#define UI_VIEWS_VIEW_H_ +#pragma once + +#include <algorithm> +#include <map> +#include <set> +#include <string> +#include <vector> + +#include "base/compiler_specific.h" +#include "base/i18n/rtl.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "build/build_config.h" +#include "ui/base/accelerators/accelerator.h" +#include "ui/base/dragdrop/os_exchange_data.h" +#include "ui/gfx/compositor/layer_delegate.h" +#include "ui/gfx/native_widget_types.h" +#include "ui/gfx/rect.h" +#include "ui/views/events/event.h" +#include "views/background.h" +#include "views/border.h" + +#if defined(OS_WIN) +#include "base/win/scoped_comptr.h" +#endif + +using ui::OSExchangeData; + +namespace gfx { +class Canvas; +class Insets; +class Path; +} + +namespace ui { +struct AccessibleViewState; +class Compositor; +class Layer; +class TextInputClient; +class Texture; +class ThemeProvider; +class Transform; +enum TouchStatus; +} + +#if defined(OS_WIN) +class __declspec(uuid("26f5641a-246d-457b-a96d-07f3fae6acf2")) +NativeViewAccessibilityWin; +#endif + +namespace views { + +class Background; +class Border; +class ContextMenuController; +class DragController; +class FocusManager; +class FocusTraversable; +class InputMethod; +class LayoutManager; +class ScrollView; +class Widget; + +namespace internal { +class NativeWidgetView; +class RootView; +} + +///////////////////////////////////////////////////////////////////////////// +// +// View class +// +// A View is a rectangle within the views View hierarchy. It is the base +// class for all Views. +// +// A View is a container of other Views (there is no such thing as a Leaf +// View - makes code simpler, reduces type conversion headaches, design +// mistakes etc) +// +// The View contains basic properties for sizing (bounds), layout (flex, +// orientation, etc), painting of children and event dispatch. +// +// The View also uses a simple Box Layout Manager similar to XUL's +// SprocketLayout system. Alternative Layout Managers implementing the +// LayoutManager interface can be used to lay out children if required. +// +// It is up to the subclass to implement Painting and storage of subclass - +// specific properties and functionality. +// +// Unless otherwise documented, views is not thread safe and should only be +// accessed from the main thread. +// +///////////////////////////////////////////////////////////////////////////// +class VIEWS_EXPORT View : public ui::LayerDelegate, + public ui::AcceleratorTarget { + public: + typedef std::vector<View*> Views; + + // TO BE MOVED --------------------------------------------------------------- + // TODO(beng): These methods are to be moved to other files/classes. + + // TODO(beng): delete + // Set whether this view is hottracked. A disabled view cannot be hottracked. + // If flag differs from the current value, SchedulePaint is invoked. + virtual void SetHotTracked(bool flag); + + // TODO(beng): delete + // Returns whether the view is hot-tracked. + virtual bool IsHotTracked() const; + + // Creation and lifetime ----------------------------------------------------- + + View(); + virtual ~View(); + + // By default a View is owned by its parent unless specified otherwise here. + bool parent_owned() const { return parent_owned_; } + void set_parent_owned(bool parent_owned) { parent_owned_ = parent_owned; } + + // Tree operations ----------------------------------------------------------- + + // Get the Widget that hosts this View, if any. + virtual const Widget* GetWidget() const; + virtual Widget* GetWidget(); + + // Adds |view| as a child of this view, optionally at |index|. + void AddChildView(View* view); + void AddChildViewAt(View* view, int index); + + // Moves |view| to the specified |index|. A negative value for |index| moves + // the view at the end. + void ReorderChildView(View* view, int index); + + // Removes |view| from this view. The view's parent will change to NULL. + void RemoveChildView(View* view); + + // Removes all the children from this view. If |delete_children| is true, + // the views are deleted, unless marked as not parent owned. + void RemoveAllChildViews(bool delete_children); + + // STL-style accessors. + Views::const_iterator children_begin() { return children_.begin(); } + Views::const_iterator children_end() { return children_.end(); } + Views::const_reverse_iterator children_rbegin() { return children_.rbegin(); } + Views::const_reverse_iterator children_rend() { return children_.rend(); } + int child_count() const { return static_cast<int>(children_.size()); } + bool has_children() const { return !children_.empty(); } + + // Returns the child view at |index|. + const View* child_at(int index) const { + DCHECK_GE(index, 0); + DCHECK_LT(index, child_count()); + return children_[index]; + } + View* child_at(int index) { + return const_cast<View*>(const_cast<const View*>(this)->child_at(index)); + } + + // Returns the parent view. + const View* parent() const { return parent_; } + View* parent() { return parent_; } + + // Returns true if |view| is contained within this View's hierarchy, even as + // an indirect descendant. Will return true if child is also this view. + bool Contains(const View* view) const; + + // Returns the index of |view|, or -1 if |view| is not a child of this view. + int GetIndexOf(const View* view) const; + + // Size and disposition ------------------------------------------------------ + // Methods for obtaining and modifying the position and size of the view. + // Position is in the coordinate system of the view's parent. + // Position is NOT flipped for RTL. See "RTL positioning" for RTL-sensitive + // position accessors. + // Transformations are not applied on the size/position. For example, if + // bounds is (0, 0, 100, 100) and it is scaled by 0.5 along the X axis, the + // width will still be 100 (although when painted, it will be 50x50, painted + // at location (0, 0)). + + void SetBounds(int x, int y, int width, int height); + void SetBoundsRect(const gfx::Rect& bounds); + void SetSize(const gfx::Size& size); + void SetPosition(const gfx::Point& position); + void SetX(int x); + void SetY(int y); + + // No transformation is applied on the size or the locations. + const gfx::Rect& bounds() const { return bounds_; } + int x() const { return bounds_.x(); } + int y() const { return bounds_.y(); } + int width() const { return bounds_.width(); } + int height() const { return bounds_.height(); } + const gfx::Size& size() const { return bounds_.size(); } + + // Returns the bounds of the content area of the view, i.e. the rectangle + // enclosed by the view's border. + gfx::Rect GetContentsBounds() const; + + // Returns the bounds of the view in its own coordinates (i.e. position is + // 0, 0). + gfx::Rect GetLocalBounds() const; + + // Returns the insets of the current border. If there is no border an empty + // insets is returned. + virtual gfx::Insets GetInsets() const; + + // Returns the visible bounds of the receiver in the receivers coordinate + // system. + // + // When traversing the View hierarchy in order to compute the bounds, the + // function takes into account the mirroring setting and transformation for + // each View and therefore it will return the mirrored and transformed version + // of the visible bounds if need be. + gfx::Rect GetVisibleBounds() const; + + // Return the bounds of the View in screen coordinate system. + gfx::Rect GetScreenBounds() const; + + // Returns the baseline of this view, or -1 if this view has no baseline. The + // return value is relative to the preferred height. + virtual int GetBaseline() const; + + // Get the size the View would like to be, if enough space were available. + virtual gfx::Size GetPreferredSize(); + + // Convenience method that sizes this view to its preferred size. + void SizeToPreferredSize(); + + // Gets the minimum size of the view. View's implementation invokes + // GetPreferredSize. + virtual gfx::Size GetMinimumSize(); + + // Return the height necessary to display this view with the provided width. + // View's implementation returns the value from getPreferredSize.cy. + // Override if your View's preferred height depends upon the width (such + // as with Labels). + virtual int GetHeightForWidth(int w); + + // Set whether the receiving view is visible. Painting is scheduled as needed + virtual void SetVisible(bool visible); + + // Return whether a view is visible + virtual bool IsVisible() const; + + // Return whether a view and its ancestors are visible. Returns true if the + // path from this view to the root view is visible. + virtual bool IsVisibleInRootView() const; + + // Set whether this view is enabled. A disabled view does not receive keyboard + // or mouse inputs. If flag differs from the current value, SchedulePaint is + // invoked. + void SetEnabled(bool enabled); + + // Returns whether the view is enabled. + virtual bool IsEnabled() const; + + // This indicates that the view completely fills its bounds in an opaque + // color. This doesn't affect compositing but is a hint to the compositor to + // optimize painting. + // Note that this method does not implicitly create a layer if one does not + // already exist for the View, but is a no-op in that case. + void SetFillsBoundsOpaquely(bool fills_bounds_opaquely); + + // Transformations ----------------------------------------------------------- + + // Methods for setting transformations for a view (e.g. rotation, scaling). + + const ui::Transform& GetTransform() const; + + // Clipping parameters. Clipping happens from the right and/or bottom. The + // clipping amount is in parent's coordinate system, as in, if the view is + // rotated, then the clipping will be applied after the rotation (and other + // transformations, if any). + void set_clip_x(float x) { clip_x_ = x; } + void set_clip_y(float y) { clip_y_ = y; } + void set_clip(float x, float y) { clip_x_ = x; clip_y_ = y; } + + // Sets the transform to the supplied transform. + void SetTransform(const ui::Transform& transform); + + // Sets whether this view paints to a layer. A view paints to a layer if + // either of the following are true: + // . the view has a non-identity transform. + // . SetPaintToLayer(true) has been invoked. + // View creates the Layer only when it exists in a Widget with a non-NULL + // Compositor. + void SetPaintToLayer(bool paint_to_layer); + + const ui::Layer* layer() const { return layer_.get(); } + ui::Layer* layer() { return layer_.get(); } + + // RTL positioning ----------------------------------------------------------- + + // Methods for accessing the bounds and position of the view, relative to its + // parent. The position returned is mirrored if the parent view is using a RTL + // layout. + // + // NOTE: in the vast majority of the cases, the mirroring implementation is + // transparent to the View subclasses and therefore you should use the + // bounds() accessor instead. + gfx::Rect GetMirroredBounds() const; + gfx::Point GetMirroredPosition() const; + int GetMirroredX() const; + + // Given a rectangle specified in this View's coordinate system, the function + // computes the 'left' value for the mirrored rectangle within this View. If + // the View's UI layout is not right-to-left, then bounds.x() is returned. + // + // UI mirroring is transparent to most View subclasses and therefore there is + // no need to call this routine from anywhere within your subclass + // implementation. + int GetMirroredXForRect(const gfx::Rect& rect) const; + + // Given the X coordinate of a point inside the View, this function returns + // the mirrored X coordinate of the point if the View's UI layout is + // right-to-left. If the layout is left-to-right, the same X coordinate is + // returned. + // + // Following are a few examples of the values returned by this function for + // a View with the bounds {0, 0, 100, 100} and a right-to-left layout: + // + // GetMirroredXCoordinateInView(0) -> 100 + // GetMirroredXCoordinateInView(20) -> 80 + // GetMirroredXCoordinateInView(99) -> 1 + int GetMirroredXInView(int x) const; + + // Given a X coordinate and a width inside the View, this function returns + // the mirrored X coordinate if the View's UI layout is right-to-left. If the + // layout is left-to-right, the same X coordinate is returned. + // + // Following are a few examples of the values returned by this function for + // a View with the bounds {0, 0, 100, 100} and a right-to-left layout: + // + // GetMirroredXCoordinateInView(0, 10) -> 90 + // GetMirroredXCoordinateInView(20, 20) -> 60 + int GetMirroredXWithWidthInView(int x, int w) const; + + // Layout -------------------------------------------------------------------- + + // Lay out the child Views (set their bounds based on sizing heuristics + // specific to the current Layout Manager) + virtual void Layout(); + + // TODO(beng): I think we should remove this. + // Mark this view and all parents to require a relayout. This ensures the + // next call to Layout() will propagate to this view, even if the bounds of + // parent views do not change. + void InvalidateLayout(); + + // Gets/Sets the Layout Manager used by this view to size and place its + // children. + // The LayoutManager is owned by the View and is deleted when the view is + // deleted, or when a new LayoutManager is installed. + LayoutManager* GetLayoutManager() const; + void SetLayoutManager(LayoutManager* layout); + + // Attributes ---------------------------------------------------------------- + + // The view class name. + static const char kViewClassName[]; + + // Return the receiving view's class name. A view class is a string which + // uniquely identifies the view class. It is intended to be used as a way to + // find out during run time if a view can be safely casted to a specific view + // subclass. The default implementation returns kViewClassName. + virtual std::string GetClassName() const; + + // Returns the first ancestor, starting at this, whose class name is |name|. + // Returns null if no ancestor has the class name |name|. + View* GetAncestorWithClassName(const std::string& name); + + // Recursively descends the view tree starting at this view, and returns + // the first child that it encounters that has the given ID. + // Returns NULL if no matching child view is found. + virtual const View* GetViewByID(int id) const; + virtual View* GetViewByID(int id); + + // Gets and sets the ID for this view. ID should be unique within the subtree + // that you intend to search for it. 0 is the default ID for views. + int id() const { return id_; } + void set_id(int id) { id_ = id; } + + // A group id is used to tag views which are part of the same logical group. + // Focus can be moved between views with the same group using the arrow keys. + // Groups are currently used to implement radio button mutual exclusion. + // The group id is immutable once it's set. + void SetGroup(int gid); + // Returns the group id of the view, or -1 if the id is not set yet. + int GetGroup() const; + + // If this returns true, the views from the same group can each be focused + // when moving focus with the Tab/Shift-Tab key. If this returns false, + // only the selected view from the group (obtained with + // GetSelectedViewForGroup()) is focused. + virtual bool IsGroupFocusTraversable() const; + + // Fills |views| with all the available views which belong to the provided + // |group|. + void GetViewsInGroup(int group, Views* views); + + // Returns the View that is currently selected in |group|. + // The default implementation simply returns the first View found for that + // group. + virtual View* GetSelectedViewForGroup(int group); + + // Coordinate conversion ----------------------------------------------------- + + // Note that the utility coordinate conversions functions always operate on + // the mirrored position of the child Views if the parent View uses a + // right-to-left UI layout. + + // Convert a point from the coordinate system of one View to another. + // + // |source| and |target| must be in the same widget, but doesn't need to be in + // the same view hierarchy. + // |source| can be NULL in which case it means the screen coordinate system. + static void ConvertPointToView(const View* source, + const View* target, + gfx::Point* point); + + // Convert a point from a View's coordinate system to that of its Widget. + static void ConvertPointToWidget(const View* src, gfx::Point* point); + + // Convert a point from the coordinate system of a View's Widget to that + // View's coordinate system. + static void ConvertPointFromWidget(const View* dest, gfx::Point* p); + + // Convert a point from a View's coordinate system to that of the screen. + static void ConvertPointToScreen(const View* src, gfx::Point* point); + + // Applies transformation on the rectangle, which is in the view's coordinate + // system, to convert it into the parent's coordinate system. + gfx::Rect ConvertRectToParent(const gfx::Rect& rect) const; + + // Converts a rectangle from this views coordinate system to its widget + // coordinate system. + gfx::Rect ConvertRectToWidget(const gfx::Rect& rect) const; + + // Painting ------------------------------------------------------------------ + + // Mark all or part of the View's bounds as dirty (needing repaint). + // |r| is in the View's coordinates. + // Rectangle |r| should be in the view's coordinate system. The + // transformations are applied to it to convert it into the parent coordinate + // system before propagating SchedulePaint up the view hierarchy. + // TODO(beng): Make protected. + virtual void SchedulePaint(); + virtual void SchedulePaintInRect(const gfx::Rect& r); + + // Called by the framework to paint a View. Performs translation and clipping + // for View coordinates and language direction as required, allows the View + // to paint itself via the various OnPaint*() event handlers and then paints + // the hierarchy beneath it. + virtual void Paint(gfx::Canvas* canvas); + + // The background object is owned by this object and may be NULL. + void set_background(Background* b) { background_.reset(b); } + const Background* background() const { return background_.get(); } + Background* background() { return background_.get(); } + + // The border object is owned by this object and may be NULL. + void set_border(Border* b) { border_.reset(b); } + const Border* border() const { return border_.get(); } + Border* border() { return border_.get(); } + + // Get the theme provider from the parent widget. + virtual ui::ThemeProvider* GetThemeProvider() const; + + // RTL painting -------------------------------------------------------------- + + // This method determines whether the gfx::Canvas object passed to + // View::Paint() needs to be transformed such that anything drawn on the + // canvas object during View::Paint() is flipped horizontally. + // + // By default, this function returns false (which is the initial value of + // |flip_canvas_on_paint_for_rtl_ui_|). View subclasses that need to paint on + // a flipped gfx::Canvas when the UI layout is right-to-left need to call + // EnableCanvasFlippingForRTLUI(). + bool FlipCanvasOnPaintForRTLUI() const { + return flip_canvas_on_paint_for_rtl_ui_ ? base::i18n::IsRTL() : false; + } + + // Enables or disables flipping of the gfx::Canvas during View::Paint(). + // Note that if canvas flipping is enabled, the canvas will be flipped only + // if the UI layout is right-to-left; that is, the canvas will be flipped + // only if base::i18n::IsRTL() returns true. + // + // Enabling canvas flipping is useful for leaf views that draw a bitmap that + // needs to be flipped horizontally when the UI layout is right-to-left + // (views::Button, for example). This method is helpful for such classes + // because their drawing logic stays the same and they can become agnostic to + // the UI directionality. + void EnableCanvasFlippingForRTLUI(bool enable) { + flip_canvas_on_paint_for_rtl_ui_ = enable; + } + + // Accelerated painting ------------------------------------------------------ + + // Enable/Disable accelerated compositing. + static void set_use_acceleration_when_possible(bool use); + static bool get_use_acceleration_when_possible(); + + // Input --------------------------------------------------------------------- + // The points (and mouse locations) in the following functions are in the + // view's coordinates, except for a RootView. + + // Returns the deepest visible descendant that contains the specified point. + virtual View* GetEventHandlerForPoint(const gfx::Point& point); + + // Return the cursor that should be used for this view or the default cursor. + // The event location is in the receiver's coordinate system. The caller is + // responsible for managing the lifetime of the returned object, though that + // lifetime may vary from platform to platform. On Windows, the cursor is a + // shared resource, but Gtk destroys the returned cursor after setting it. + virtual gfx::NativeCursor GetCursor(const MouseEvent& event); + + // Convenience to test whether a point is within this view's bounds + virtual bool HitTest(const gfx::Point& l) const; + + // This method is invoked when the user clicks on this view. + // The provided event is in the receiver's coordinate system. + // + // Return true if you processed the event and want to receive subsequent + // MouseDraggged and MouseReleased events. This also stops the event from + // bubbling. If you return false, the event will bubble through parent + // views. + // + // If you remove yourself from the tree while processing this, event bubbling + // stops as if you returned true, but you will not receive future events. + // The return value is ignored in this case. + // + // Default implementation returns true if a ContextMenuController has been + // set, false otherwise. Override as needed. + // + virtual bool OnMousePressed(const MouseEvent& event); + + // This method is invoked when the user clicked on this control. + // and is still moving the mouse with a button pressed. + // The provided event is in the receiver's coordinate system. + // + // Return true if you processed the event and want to receive + // subsequent MouseDragged and MouseReleased events. + // + // Default implementation returns true if a ContextMenuController has been + // set, false otherwise. Override as needed. + // + virtual bool OnMouseDragged(const MouseEvent& event); + + // This method is invoked when the user releases the mouse + // button. The event is in the receiver's coordinate system. + // + // Default implementation notifies the ContextMenuController is appropriate. + // Subclasses that wish to honor the ContextMenuController should invoke + // super. + virtual void OnMouseReleased(const MouseEvent& event); + + // This method is invoked when the mouse press/drag was canceled by a + // system/user gesture. + virtual void OnMouseCaptureLost(); + + // This method is invoked when the mouse is above this control + // The event is in the receiver's coordinate system. + // + // Default implementation does nothing. Override as needed. + virtual void OnMouseMoved(const MouseEvent& event); + + // This method is invoked when the mouse enters this control. + // + // Default implementation does nothing. Override as needed. + virtual void OnMouseEntered(const MouseEvent& event); + + // This method is invoked when the mouse exits this control + // The provided event location is always (0, 0) + // Default implementation does nothing. Override as needed. + virtual void OnMouseExited(const MouseEvent& event); + + // This method is invoked for each touch event. Default implementation + // does nothing. Override as needed. + virtual ui::TouchStatus OnTouchEvent(const TouchEvent& event); + + // Set the MouseHandler for a drag session. + // + // A drag session is a stream of mouse events starting + // with a MousePressed event, followed by several MouseDragged + // events and finishing with a MouseReleased event. + // + // This method should be only invoked while processing a + // MouseDragged or MousePressed event. + // + // All further mouse dragged and mouse up events will be sent + // the MouseHandler, even if it is reparented to another window. + // + // The MouseHandler is automatically cleared when the control + // comes back from processing the MouseReleased event. + // + // Note: if the mouse handler is no longer connected to a + // view hierarchy, events won't be sent. + // + virtual void SetMouseHandler(View* new_mouse_handler); + + // Invoked when a key is pressed or released. + // Subclasser should return true if the event has been processed and false + // otherwise. If the event has not been processed, the parent will be given a + // chance. + virtual bool OnKeyPressed(const KeyEvent& event); + virtual bool OnKeyReleased(const KeyEvent& event); + + // Invoked when the user uses the mousewheel. Implementors should return true + // if the event has been processed and false otherwise. This message is sent + // if the view is focused. If the event has not been processed, the parent + // will be given a chance. + virtual bool OnMouseWheel(const MouseWheelEvent& event); + + // Returns the View's TextInputClient instance or NULL if the View doesn't + // support text input. + virtual ui::TextInputClient* GetTextInputClient(); + + // Convenience method to retrieve the InputMethod associated with the + // Widget that contains this view. Returns NULL if this view is not part of a + // view hierarchy with a Widget. + virtual InputMethod* GetInputMethod(); + + // Accelerators -------------------------------------------------------------- + + // Sets a keyboard accelerator for that view. When the user presses the + // accelerator key combination, the AcceleratorPressed method is invoked. + // Note that you can set multiple accelerators for a view by invoking this + // method several times. + virtual void AddAccelerator(const ui::Accelerator& accelerator); + + // Removes the specified accelerator for this view. + virtual void RemoveAccelerator(const ui::Accelerator& accelerator); + + // Removes all the keyboard accelerators for this view. + virtual void ResetAccelerators(); + + // Overridden from AcceleratorTarget: + virtual bool AcceleratorPressed(const ui::Accelerator& accelerator) OVERRIDE; + + // Focus --------------------------------------------------------------------- + + // Returns whether this view currently has the focus. + virtual bool HasFocus() const; + + // Returns the view that should be selected next when pressing Tab. + View* GetNextFocusableView(); + const View* GetNextFocusableView() const; + + // Returns the view that should be selected next when pressing Shift-Tab. + View* GetPreviousFocusableView(); + + // Sets the component that should be selected next when pressing Tab, and + // makes the current view the precedent view of the specified one. + // Note that by default views are linked in the order they have been added to + // their container. Use this method if you want to modify the order. + // IMPORTANT NOTE: loops in the focus hierarchy are not supported. + void SetNextFocusableView(View* view); + + // Sets whether this view can accept the focus. + // Note that this is false by default so that a view used as a container does + // not get the focus. + void set_focusable(bool focusable) { focusable_ = focusable; } + + // Returns true if the view is focusable (IsFocusable) and visible in the root + // view. See also IsFocusable. + bool IsFocusableInRootView() const; + + // Return whether this view is focusable when the user requires full keyboard + // access, even though it may not be normally focusable. + bool IsAccessibilityFocusableInRootView() const; + + // Set whether this view can be made focusable if the user requires + // full keyboard access, even though it's not normally focusable. + // Note that this is false by default. + void set_accessibility_focusable(bool accessibility_focusable) { + accessibility_focusable_ = accessibility_focusable; + } + + // Convenience method to retrieve the FocusManager associated with the + // Widget that contains this view. This can return NULL if this view is not + // part of a view hierarchy with a Widget. + virtual FocusManager* GetFocusManager(); + virtual const FocusManager* GetFocusManager() const; + + // Request the keyboard focus. The receiving view will become the + // focused view. + virtual void RequestFocus(); + + // Invoked when a view is about to be requested for focus due to the focus + // traversal. Reverse is this request was generated going backward + // (Shift-Tab). + virtual void AboutToRequestFocusFromTabTraversal(bool reverse) { } + + // Invoked when a key is pressed before the key event is processed (and + // potentially eaten) by the focus manager for tab traversal, accelerators and + // other focus related actions. + // The default implementation returns false, ensuring that tab traversal and + // accelerators processing is performed. + // Subclasses should return true if they want to process the key event and not + // have it processed as an accelerator (if any) or as a tab traversal (if the + // key event is for the TAB key). In that case, OnKeyPressed will + // subsequently be invoked for that event. + virtual bool SkipDefaultKeyEventProcessing(const KeyEvent& event); + + // Subclasses that contain traversable children that are not directly + // accessible through the children hierarchy should return the associated + // FocusTraversable for the focus traversal to work properly. + virtual FocusTraversable* GetFocusTraversable(); + + // Subclasses that can act as a "pane" must implement their own + // FocusTraversable to keep the focus trapped within the pane. + // If this method returns an object, any view that's a direct or + // indirect child of this view will always use this FocusTraversable + // rather than the one from the widget. + virtual FocusTraversable* GetPaneFocusTraversable(); + + // Tooltips ------------------------------------------------------------------ + + // Gets the tooltip for this View. If the View does not have a tooltip, + // return false. If the View does have a tooltip, copy the tooltip into + // the supplied string and return true. + // Any time the tooltip text that a View is displaying changes, it must + // invoke TooltipTextChanged. + // |p| provides the coordinates of the mouse (relative to this view). + virtual bool GetTooltipText(const gfx::Point& p, string16* tooltip) const; + + // Returns the location (relative to this View) for the text on the tooltip + // to display. If false is returned (the default), the tooltip is placed at + // a default position. + virtual bool GetTooltipTextOrigin(const gfx::Point& p, gfx::Point* loc) const; + + // Context menus ------------------------------------------------------------- + + // Sets the ContextMenuController. Setting this to non-null makes the View + // process mouse events. + ContextMenuController* context_menu_controller() { + return context_menu_controller_; + } + void set_context_menu_controller(ContextMenuController* menu_controller) { + context_menu_controller_ = menu_controller; + } + + // Provides default implementation for context menu handling. The default + // implementation calls the ShowContextMenu of the current + // ContextMenuController (if it is not NULL). Overridden in subclassed views + // to provide right-click menu display triggerd by the keyboard (i.e. for the + // Chrome toolbar Back and Forward buttons). No source needs to be specified, + // as it is always equal to the current View. + virtual void ShowContextMenu(const gfx::Point& p, + bool is_mouse_gesture); + + // Drag and drop ------------------------------------------------------------- + + DragController* drag_controller() { return drag_controller_; } + void set_drag_controller(DragController* drag_controller) { + drag_controller_ = drag_controller; + } + + // During a drag and drop session when the mouse moves the view under the + // mouse is queried for the drop types it supports by way of the + // GetDropFormats methods. If the view returns true and the drag site can + // provide data in one of the formats, the view is asked if the drop data + // is required before any other drop events are sent. Once the + // data is available the view is asked if it supports the drop (by way of + // the CanDrop method). If a view returns true from CanDrop, + // OnDragEntered is sent to the view when the mouse first enters the view, + // as the mouse moves around within the view OnDragUpdated is invoked. + // If the user releases the mouse over the view and OnDragUpdated returns a + // valid drop, then OnPerformDrop is invoked. If the mouse moves outside the + // view or over another view that wants the drag, OnDragExited is invoked. + // + // Similar to mouse events, the deepest view under the mouse is first checked + // if it supports the drop (Drop). If the deepest view under + // the mouse does not support the drop, the ancestors are walked until one + // is found that supports the drop. + + // Override and return the set of formats that can be dropped on this view. + // |formats| is a bitmask of the formats defined bye OSExchangeData::Format. + // The default implementation returns false, which means the view doesn't + // support dropping. + virtual bool GetDropFormats( + int* formats, + std::set<OSExchangeData::CustomFormat>* custom_formats); + + // Override and return true if the data must be available before any drop + // methods should be invoked. The default is false. + virtual bool AreDropTypesRequired(); + + // A view that supports drag and drop must override this and return true if + // data contains a type that may be dropped on this view. + virtual bool CanDrop(const OSExchangeData& data); + + // OnDragEntered is invoked when the mouse enters this view during a drag and + // drop session and CanDrop returns true. This is immediately + // followed by an invocation of OnDragUpdated, and eventually one of + // OnDragExited or OnPerformDrop. + virtual void OnDragEntered(const DropTargetEvent& event); + + // Invoked during a drag and drop session while the mouse is over the view. + // This should return a bitmask of the DragDropTypes::DragOperation supported + // based on the location of the event. Return 0 to indicate the drop should + // not be accepted. + virtual int OnDragUpdated(const DropTargetEvent& event); + + // Invoked during a drag and drop session when the mouse exits the views, or + // when the drag session was canceled and the mouse was over the view. + virtual void OnDragExited(); + + // Invoked during a drag and drop session when OnDragUpdated returns a valid + // operation and the user release the mouse. + virtual int OnPerformDrop(const DropTargetEvent& event); + + // Invoked from DoDrag after the drag completes. This implementation does + // nothing, and is intended for subclasses to do cleanup. + virtual void OnDragDone(); + + // Returns true if the mouse was dragged enough to start a drag operation. + // delta_x and y are the distance the mouse was dragged. + static bool ExceededDragThreshold(int delta_x, int delta_y); + + // Accessibility ------------------------------------------------------------- + + // Modifies |state| to reflect the current accessible state of this view. + virtual void GetAccessibleState(ui::AccessibleViewState* state) { } + + // Returns an instance of the native accessibility interface for this view. + virtual gfx::NativeViewAccessible GetNativeViewAccessible(); + + // Scrolling ----------------------------------------------------------------- + // TODO(beng): Figure out if this can live somewhere other than View, i.e. + // closer to ScrollView. + + // Scrolls the specified region, in this View's coordinate system, to be + // visible. View's implementation passes the call onto the parent View (after + // adjusting the coordinates). It is up to views that only show a portion of + // the child view, such as Viewport, to override appropriately. + virtual void ScrollRectToVisible(const gfx::Rect& rect); + + // The following methods are used by ScrollView to determine the amount + // to scroll relative to the visible bounds of the view. For example, a + // return value of 10 indicates the scrollview should scroll 10 pixels in + // the appropriate direction. + // + // Each method takes the following parameters: + // + // is_horizontal: if true, scrolling is along the horizontal axis, otherwise + // the vertical axis. + // is_positive: if true, scrolling is by a positive amount. Along the + // vertical axis scrolling by a positive amount equates to + // scrolling down. + // + // The return value should always be positive and gives the number of pixels + // to scroll. ScrollView interprets a return value of 0 (or negative) + // to scroll by a default amount. + // + // See VariableRowHeightScrollHelper and FixedRowHeightScrollHelper for + // implementations of common cases. + virtual int GetPageScrollIncrement(ScrollView* scroll_view, + bool is_horizontal, bool is_positive); + virtual int GetLineScrollIncrement(ScrollView* scroll_view, + bool is_horizontal, bool is_positive); + + protected: + // Size and disposition ------------------------------------------------------ + + // Override to be notified when the bounds of the view have changed. + virtual void OnBoundsChanged(const gfx::Rect& previous_bounds); + + // Called when the preferred size of a child view changed. This gives the + // parent an opportunity to do a fresh layout if that makes sense. + virtual void ChildPreferredSizeChanged(View* child) {} + + // Invalidates the layout and calls ChildPreferredSizeChanged on the parent + // if there is one. Be sure to call View::PreferredSizeChanged when + // overriding such that the layout is properly invalidated. + virtual void PreferredSizeChanged(); + + // Override returning true when the view needs to be notified when its visible + // bounds relative to the root view may have changed. Only used by + // NativeViewHost. + virtual bool NeedsNotificationWhenVisibleBoundsChange() const; + + // Notification that this View's visible bounds relative to the root view may + // have changed. The visible bounds are the region of the View not clipped by + // its ancestors. This is used for clipping NativeViewHost. + virtual void OnVisibleBoundsChanged(); + + // Override to be notified when the enabled state of this View has + // changed. The default implementation calls SchedulePaint() on this View. + virtual void OnEnabledChanged(); + + // Tree operations ----------------------------------------------------------- + + // This method is invoked when the tree changes. + // + // When a view is removed, it is invoked for all children and grand + // children. For each of these views, a notification is sent to the + // view and all parents. + // + // When a view is added, a notification is sent to the view, all its + // parents, and all its children (and grand children) + // + // Default implementation does nothing. Override to perform operations + // required when a view is added or removed from a view hierarchy + // + // parent is the new or old parent. Child is the view being added or + // removed. + // + virtual void ViewHierarchyChanged(bool is_add, View* parent, View* child); + + // When SetVisible() changes the visibility of a view, this method is + // invoked for that view as well as all the children recursively. + virtual void VisibilityChanged(View* starting_from, bool is_visible); + + // Called when the native view hierarchy changed. + // |attached| is true if that view has been attached to a new NativeView + // hierarchy, false if it has been detached. + // |native_view| is the NativeView this view was attached/detached from, and + // |root_view| is the root view associated with the NativeView. + // Views created without a native view parent don't have a focus manager. + // When this function is called they could do the processing that requires + // it - like registering accelerators, for example. + virtual void NativeViewHierarchyChanged(bool attached, + gfx::NativeView native_view, + internal::RootView* root_view); + + // Painting ------------------------------------------------------------------ + + // Responsible for calling Paint() on child Views. Override to control the + // order child Views are painted. + virtual void PaintChildren(gfx::Canvas* canvas); + + // Override to provide rendering in any part of the View's bounds. Typically + // this is the "contents" of the view. If you override this method you will + // have to call the subsequent OnPaint*() methods manually. + virtual void OnPaint(gfx::Canvas* canvas); + + // Override to paint a background before any content is drawn. Typically this + // is done if you are satisfied with a default OnPaint handler but wish to + // supply a different background. + virtual void OnPaintBackground(gfx::Canvas* canvas); + + // Override to paint a border not specified by SetBorder(). + virtual void OnPaintBorder(gfx::Canvas* canvas); + + // Override to paint a focus border (usually a dotted rectangle) around + // relevant contents. + virtual void OnPaintFocusBorder(gfx::Canvas* canvas); + + // Accelerated painting ------------------------------------------------------ + + // This creates a layer for the view, if one does not exist. It then + // passes the texture to a layer associated with the view. While an external + // texture is set, the view will not update the layer contents. + // + // |texture| cannot be NULL. + // + // Returns false if it cannot create a layer to which to assign the texture. + bool SetExternalTexture(ui::Texture* texture); + + // Returns the offset from this view to the nearest ancestor with a layer. + // If |ancestor| is non-NULL it is set to the nearest ancestor with a layer. + virtual void CalculateOffsetToAncestorWithLayer( + gfx::Point* offset, + ui::Layer** layer_parent); + + // If this view has a layer, the layer is reparented to |parent_layer| and its + // bounds is set based on |point|. If this view does not have a layer, then + // recurses through all children. This is used when adding a layer to an + // existing view to make sure all descendants that have layers are parented to + // the right layer. + virtual void MoveLayerToParent(ui::Layer* parent_layer, + const gfx::Point& point); + + // Called to update the bounds of any child layers within this View's + // hierarchy when something happens to the hierarchy. + virtual void UpdateChildLayerBounds(const gfx::Point& offset); + + // Overridden from ui::LayerDelegate: + virtual void OnPaintLayer(gfx::Canvas* canvas) OVERRIDE; + + // Finds the layer that this view paints to (it may belong to an ancestor + // view), then reorders the immediate children of that layer to match the + // order of the view tree. + virtual void ReorderLayers(); + + // This reorders the immediate children of |*parent_layer| to match the + // order of the view tree. + virtual void ReorderChildLayers(ui::Layer* parent_layer); + + // Input --------------------------------------------------------------------- + + // Called by HitTest to see if this View has a custom hit test mask. If the + // return value is true, GetHitTestMask will be called to obtain the mask. + // Default value is false, in which case the View will hit-test against its + // bounds. + virtual bool HasHitTestMask() const; + + // Called by HitTest to retrieve a mask for hit-testing against. Subclasses + // override to provide custom shaped hit test regions. + virtual void GetHitTestMask(gfx::Path* mask) const; + + // Focus --------------------------------------------------------------------- + + // Returns whether this view can accept focus. + // A view can accept focus if it's enabled, focusable and visible. + // This method is intended for views to use when calculating preferred size. + // The FocusManager and other places use IsFocusableInRootView. + virtual bool IsFocusable() const; + + // Override to be notified when focus has changed either to or from this View. + virtual void OnFocus(); + virtual void OnBlur(); + + // Handle view focus/blur events for this view. + void Focus(); + void Blur(); + + // System events ------------------------------------------------------------- + + // Called when the UI theme has changed, overriding allows individual Views to + // do special cleanup and processing (such as dropping resource caches). + // To dispatch a theme changed notification, call Widget::ThemeChanged(). + virtual void OnThemeChanged() {} + + // Called when the locale has changed, overriding allows individual Views to + // update locale-dependent strings. + // To dispatch a locale changed notification, call Widget::LocaleChanged(). + virtual void OnLocaleChanged() {} + + // Tooltips ------------------------------------------------------------------ + + // Views must invoke this when the tooltip text they are to display changes. + void TooltipTextChanged(); + + // Context menus ------------------------------------------------------------- + + // Returns the location, in screen coordinates, to show the context menu at + // when the context menu is shown from the keyboard. This implementation + // returns the middle of the visible region of this view. + // + // This method is invoked when the context menu is shown by way of the + // keyboard. + virtual gfx::Point GetKeyboardContextMenuLocation(); + + // Drag and drop ------------------------------------------------------------- + + // These are cover methods that invoke the method of the same name on + // the DragController. Subclasses may wish to override rather than install + // a DragController. + // See DragController for a description of these methods. + virtual int GetDragOperations(const gfx::Point& press_pt); + virtual void WriteDragData(const gfx::Point& press_pt, OSExchangeData* data); + + // Returns whether we're in the middle of a drag session that was initiated + // by us. + bool InDrag(); + + // Returns how much the mouse needs to move in one direction to start a + // drag. These methods cache in a platform-appropriate way. These values are + // used by the public static method ExceededDragThreshold(). + static int GetHorizontalDragThreshold(); + static int GetVerticalDragThreshold(); + + // Debugging ----------------------------------------------------------------- + +#if !defined(NDEBUG) + // Returns string containing a graph of the views hierarchy in graphViz DOT + // language (http://graphviz.org/). Can be called within debugger and save + // to a file to compile/view. + // Note: Assumes initial call made with first = true. + virtual std::string PrintViewGraph(bool first); + + // Some classes may own an object which contains the children to displayed in + // the views hierarchy. The above function gives the class the flexibility to + // decide which object should be used to obtain the children, but this + // function makes the decision explicit. + std::string DoPrintViewGraph(bool first, View* view_with_children); +#endif + + private: + friend class internal::NativeWidgetView; + friend class internal::RootView; + friend class FocusManager; + friend class ViewStorage; + friend class Widget; + friend class PaintLock; + + // Used to track a drag. RootView passes this into + // ProcessMousePressed/Dragged. + struct DragInfo { + // Sets possible_drag to false and start_x/y to 0. This is invoked by + // RootView prior to invoke ProcessMousePressed. + void Reset(); + + // Sets possible_drag to true and start_pt to the specified point. + // This is invoked by the target view if it detects the press may generate + // a drag. + void PossibleDrag(const gfx::Point& p); + + // Whether the press may generate a drag. + bool possible_drag; + + // Coordinates of the mouse press. + gfx::Point start_pt; + }; + + // Painting ----------------------------------------------------------------- + + enum SchedulePaintType { + // Indicates the size is the same (only the origin changed). + SCHEDULE_PAINT_SIZE_SAME, + + // Indicates the size changed (and possibly the origin). + SCHEDULE_PAINT_SIZE_CHANGED + }; + + // Invoked before and after the bounds change to schedule painting the old and + // new bounds. + void SchedulePaintBoundsChanged(SchedulePaintType type); + + // Common Paint() code shared by accelerated and non-accelerated code paths to + // invoke OnPaint() on the View. + void PaintCommon(gfx::Canvas* canvas); + + // Tree operations ----------------------------------------------------------- + + // Removes |view| from the hierarchy tree. If |update_focus_cycle| is true, + // the next and previous focusable views of views pointing to this view are + // updated. If |update_tool_tip| is true, the tooltip is updated. If + // |delete_removed_view| is true, the view is also deleted (if it is parent + // owned). + void DoRemoveChildView(View* view, + bool update_focus_cycle, + bool update_tool_tip, + bool delete_removed_view); + + // Call ViewHierarchyChanged for all child views on all parents + void PropagateRemoveNotifications(View* parent); + + // Call ViewHierarchyChanged for all children + void PropagateAddNotifications(View* parent, View* child); + + // Propagates NativeViewHierarchyChanged() notification through all the + // children. + void PropagateNativeViewHierarchyChanged(bool attached, + gfx::NativeView native_view, + internal::RootView* root_view); + + // Takes care of registering/unregistering accelerators if + // |register_accelerators| true and calls ViewHierarchyChanged(). + void ViewHierarchyChangedImpl(bool register_accelerators, + bool is_add, + View* parent, + View* child); + + // Size and disposition ------------------------------------------------------ + + // Call VisibilityChanged() recursively for all children. + void PropagateVisibilityNotifications(View* from, bool is_visible); + + // Registers/unregisters accelerators as necessary and calls + // VisibilityChanged(). + void VisibilityChangedImpl(View* starting_from, bool is_visible); + + // Responsible for propagating bounds change notifications to relevant + // views. + void BoundsChanged(const gfx::Rect& previous_bounds); + + // Visible bounds notification registration. + // When a view is added to a hierarchy, it and all its children are asked if + // they need to be registered for "visible bounds within root" notifications + // (see comment on OnVisibleBoundsChanged()). If they do, they are registered + // with every ancestor between them and the root of the hierarchy. + static void RegisterChildrenForVisibleBoundsNotification(View* view); + static void UnregisterChildrenForVisibleBoundsNotification(View* view); + void RegisterForVisibleBoundsNotification(); + void UnregisterForVisibleBoundsNotification(); + + // Adds/removes view to the list of descendants that are notified any time + // this views location and possibly size are changed. + void AddDescendantToNotify(View* view); + void RemoveDescendantToNotify(View* view); + + // Transformations ----------------------------------------------------------- + + // Returns in |transform| the transform to get from coordinates of |ancestor| + // to this. Returns true if |ancestor| is found. If |ancestor| is not found, + // or NULL, |transform| is set to convert from root view coordinates to this. + bool GetTransformRelativeTo(const View* ancestor, + ui::Transform* transform) const; + + // Coordinate conversion ----------------------------------------------------- + + // Convert a point in the view's coordinate to an ancestor view's coordinate + // system using necessary transformations. Returns whether the point was + // successfully converted to the ancestor's coordinate system. + bool ConvertPointForAncestor(const View* ancestor, gfx::Point* point) const; + + // Convert a point in the ancestor's coordinate system to the view's + // coordinate system using necessary transformations. Returns whether the + // point was successfully from the ancestor's coordinate system to the view's + // coordinate system. + bool ConvertPointFromAncestor(const View* ancestor, gfx::Point* point) const; + + // Accelerated painting ------------------------------------------------------ + + // Disables painting during time critical operations. Used by PaintLock. + // TODO(vollick) Ideally, the widget would not dispatch paints into the + // hierarchy during time critical operations and this would not be needed. + void set_painting_enabled(bool enabled) { painting_enabled_ = enabled; } + + // Creates the layer and related fields for this view. + void CreateLayer(); + + // Parents all un-parented layers within this view's hierarchy to this view's + // layer. + void UpdateParentLayers(); + + // Updates the view's layer's parent. Called when a view is added to a view + // hierarchy, responsible for parenting the view's layer to the enclosing + // layer in the hierarchy. + void UpdateParentLayer(); + + // Parents this view's layer to |parent_layer|, and sets its bounds and other + // properties in accordance to |offset|, the view's offset from the + // |parent_layer|. + void ReparentLayer(const gfx::Point& offset, ui::Layer* parent_layer); + + // Called to update the layer visibility. The layer will be visible if the + // View itself, and all its parent Views are visible. This also updates + // visibility of the child layers. + void UpdateLayerVisibility(); + void UpdateChildLayerVisibility(bool visible); + + // Orphans the layers in this subtree that are parented to layers outside of + // this subtree. + void OrphanLayers(); + + // Destroys the layer associated with this view, and reparents any descendants + // to the destroyed layer's parent. + void DestroyLayer(); + + // Input --------------------------------------------------------------------- + + // RootView invokes these. These in turn invoke the appropriate OnMouseXXX + // method. If a drag is detected, DoDrag is invoked. + bool ProcessMousePressed(const MouseEvent& event, DragInfo* drop_info); + bool ProcessMouseDragged(const MouseEvent& event, DragInfo* drop_info); + void ProcessMouseReleased(const MouseEvent& event); + + // RootView will invoke this with incoming TouchEvents. Returns the + // the result of OnTouchEvent. + ui::TouchStatus ProcessTouchEvent(const TouchEvent& event); + + // Accelerators -------------------------------------------------------------- + + // Registers this view's keyboard accelerators that are not registered to + // FocusManager yet, if possible. + void RegisterPendingAccelerators(); + + // Unregisters all the keyboard accelerators associated with this view. + // |leave_data_intact| if true does not remove data from accelerators_ array, + // so it could be re-registered with other focus manager + void UnregisterAccelerators(bool leave_data_intact); + + // Focus --------------------------------------------------------------------- + + // Initialize the previous/next focusable views of the specified view relative + // to the view at the specified index. + void InitFocusSiblings(View* view, int index); + + // System events ------------------------------------------------------------- + + // Used to propagate theme changed notifications from the root view to all + // views in the hierarchy. + virtual void PropagateThemeChanged(); + + // Used to propagate locale changed notifications from the root view to all + // views in the hierarchy. + virtual void PropagateLocaleChanged(); + + // Tooltips ------------------------------------------------------------------ + + // Propagates UpdateTooltip() to the TooltipManager for the Widget. + // This must be invoked any time the View hierarchy changes in such a way + // the view under the mouse differs. For example, if the bounds of a View is + // changed, this is invoked. Similarly, as Views are added/removed, this + // is invoked. + void UpdateTooltip(); + + // Drag and drop ------------------------------------------------------------- + + // Starts a drag and drop operation originating from this view. This invokes + // WriteDragData to write the data and GetDragOperations to determine the + // supported drag operations. When done, OnDragDone is invoked. + void DoDrag(const MouseEvent& event, const gfx::Point& press_pt); + + ////////////////////////////////////////////////////////////////////////////// + + // Creation and lifetime ----------------------------------------------------- + + // True if the hierarchy (i.e. the parent View) is responsible for deleting + // this View. Default is true. + bool parent_owned_; + + // Attributes ---------------------------------------------------------------- + + // The id of this View. Used to find this View. + int id_; + + // The group of this view. Some view subclasses use this id to find other + // views of the same group. For example radio button uses this information + // to find other radio buttons. + int group_; + + // Tree operations ----------------------------------------------------------- + + // This view's parent. + View* parent_; + + // This view's children. + Views children_; + + // Size and disposition ------------------------------------------------------ + + // This View's bounds in the parent coordinate system. + gfx::Rect bounds_; + + // Whether this view is visible. + bool visible_; + + // Whether this view is enabled. + bool enabled_; + + // Whether this view is painting. + bool painting_enabled_; + + // Whether or not RegisterViewForVisibleBoundsNotification on the RootView + // has been invoked. + bool registered_for_visible_bounds_notification_; + + // List of descendants wanting notification when their visible bounds change. + scoped_ptr<Views> descendants_to_notify_; + + // Transformations ----------------------------------------------------------- + + // Clipping parameters. skia transformation matrix does not give us clipping. + // So we do it ourselves. + float clip_x_; + float clip_y_; + + // Layout -------------------------------------------------------------------- + + // Whether the view needs to be laid out. + bool needs_layout_; + + // The View's LayoutManager defines the sizing heuristics applied to child + // Views. The default is absolute positioning according to bounds_. + scoped_ptr<LayoutManager> layout_manager_; + + // Painting ------------------------------------------------------------------ + + // Background + scoped_ptr<Background> background_; + + // Border. + scoped_ptr<Border> border_; + + // RTL painting -------------------------------------------------------------- + + // Indicates whether or not the gfx::Canvas object passed to View::Paint() + // is going to be flipped horizontally (using the appropriate transform) on + // right-to-left locales for this View. + bool flip_canvas_on_paint_for_rtl_ui_; + + // Accelerated painting ------------------------------------------------------ + + bool paint_to_layer_; + scoped_ptr<ui::Layer> layer_; + + // Accelerators -------------------------------------------------------------- + + // true if when we were added to hierarchy we were without focus manager + // attempt addition when ancestor chain changed. + bool accelerator_registration_delayed_; + + // Focus manager accelerators registered on. + FocusManager* accelerator_focus_manager_; + + // The list of accelerators. List elements in the range + // [0, registered_accelerator_count_) are already registered to FocusManager, + // and the rest are not yet. + scoped_ptr<std::vector<ui::Accelerator> > accelerators_; + size_t registered_accelerator_count_; + + // Focus --------------------------------------------------------------------- + + // Next view to be focused when the Tab key is pressed. + View* next_focusable_view_; + + // Next view to be focused when the Shift-Tab key combination is pressed. + View* previous_focusable_view_; + + // Whether this view can be focused. + bool focusable_; + + // Whether this view is focusable if the user requires full keyboard access, + // even though it may not be normally focusable. + bool accessibility_focusable_; + + // Context menus ------------------------------------------------------------- + + // The menu controller. + ContextMenuController* context_menu_controller_; + + // Drag and drop ------------------------------------------------------------- + + DragController* drag_controller_; + + // Accessibility ------------------------------------------------------------- + + // The Windows-specific accessibility implementation for this view. +#if defined(OS_WIN) + base::win::ScopedComPtr<NativeViewAccessibilityWin> + native_view_accessibility_win_; +#endif + + DISALLOW_COPY_AND_ASSIGN(View); +}; + +} // namespace views + +#endif // UI_VIEWS_VIEW_H_ diff --git a/ui/views/view_aura.cc b/ui/views/view_aura.cc new file mode 100644 index 0000000..6045b91 --- /dev/null +++ b/ui/views/view_aura.cc @@ -0,0 +1,37 @@ +// 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/view.h" + +namespace { + +// Default horizontal drag threshold in pixels. +// Same as what gtk uses. +const int kDefaultHorizontalDragThreshold = 8; + +// Default vertical drag threshold in pixels. +// Same as what gtk uses. +const int kDefaultVerticalDragThreshold = 8; + +} // namespace + +namespace views { + +gfx::NativeViewAccessible View::GetNativeViewAccessible() { + return NULL; +} + +int View::GetHorizontalDragThreshold() { + // TODO(jennyz): This value may need to be adjusted for different platforms + // and for different display density. + return kDefaultHorizontalDragThreshold; +} + +int View::GetVerticalDragThreshold() { + // TODO(jennyz): This value may need to be adjusted for different platforms + // and for different display density. + return kDefaultVerticalDragThreshold; +} + +} // namespace views diff --git a/ui/views/view_gtk.cc b/ui/views/view_gtk.cc new file mode 100644 index 0000000..459cd31 --- /dev/null +++ b/ui/views/view_gtk.cc @@ -0,0 +1,39 @@ +// 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/view.h" + +#include <gtk/gtk.h> + +#include "base/logging.h" + +namespace views { + +gfx::NativeViewAccessible View::GetNativeViewAccessible() { + NOTIMPLEMENTED(); + return NULL; +} + +int View::GetHorizontalDragThreshold() { + static bool determined_threshold = false; + static int drag_threshold = 8; + if (determined_threshold) + return drag_threshold; + determined_threshold = true; + GtkSettings* settings = gtk_settings_get_default(); + if (!settings) + return drag_threshold; + int value = 0; + g_object_get(G_OBJECT(settings), "gtk-dnd-drag-threshold", &value, NULL); + if (value) + drag_threshold = value; + return drag_threshold; +} + +int View::GetVerticalDragThreshold() { + // Vertical and horizontal drag threshold are the same in Gtk. + return GetHorizontalDragThreshold(); +} + +} // namespace views diff --git a/ui/views/view_unittest.cc b/ui/views/view_unittest.cc new file mode 100644 index 0000000..e64fae6 --- /dev/null +++ b/ui/views/view_unittest.cc @@ -0,0 +1,3068 @@ +// 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 <map> + +#include "base/memory/scoped_ptr.h" +#include "base/rand_util.h" +#include "base/string_util.h" +#include "base/utf_string_conversions.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "ui/base/accelerators/accelerator.h" +#include "ui/base/clipboard/clipboard.h" +#include "ui/base/keycodes/keyboard_codes.h" +#include "ui/base/models/simple_menu_model.h" +#include "ui/gfx/canvas_skia.h" +#include "ui/gfx/compositor/compositor.h" +#include "ui/gfx/compositor/layer.h" +#include "ui/gfx/compositor/layer_animator.h" +#include "ui/gfx/compositor/test/test_compositor.h" +#include "ui/gfx/compositor/test/test_texture.h" +#include "ui/gfx/path.h" +#include "ui/gfx/transform.h" +#include "ui/views/controls/button/button_dropdown.h" +#include "ui/views/controls/button/checkbox.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/events/event.h" +#include "ui/views/focus/accelerator_handler.h" +#include "ui/views/focus/view_storage.h" +#include "ui/views/test/views_test_base.h" +#include "ui/views/touchui/gesture_manager.h" +#include "ui/views/view.h" +#include "ui/views/widget/native_widget.h" +#include "ui/views/widget/root_view.h" +#include "ui/views/window/dialog_delegate.h" +#include "views/background.h" +#include "views/views_delegate.h" + +#if defined(OS_WIN) +#include "ui/views/test/test_views_delegate.h" +#endif +#if defined(USE_AURA) +#include "ui/aura/desktop.h" +#endif + +using ::testing::_; + +namespace { + +// Returns true if |ancestor| is an ancestor of |layer|. +bool LayerIsAncestor(const ui::Layer* ancestor, const ui::Layer* layer) { + while (layer && layer != ancestor) + layer = layer->parent(); + return layer == ancestor; +} + +// Convenience functions for walking a View tree. +const views::View* FirstView(const views::View* view) { + const views::View* v = view; + while (v->has_children()) + v = v->child_at(0); + return v; +} + +const views::View* NextView(const views::View* view) { + const views::View* v = view; + const views::View* parent = v->parent(); + if (!parent) + return NULL; + int next = parent->GetIndexOf(v) + 1; + if (next != parent->child_count()) + return FirstView(parent->child_at(next)); + return parent; +} + +// Convenience functions for walking a Layer tree. +const ui::Layer* FirstLayer(const ui::Layer* layer) { + const ui::Layer* l = layer; + while (l->children().size() > 0) + l = l->children()[0]; + return l; +} + +const ui::Layer* NextLayer(const ui::Layer* layer) { + const ui::Layer* parent = layer->parent(); + if (!parent) + return NULL; + const std::vector<ui::Layer*> children = parent->children(); + size_t index; + for (index = 0; index < children.size(); index++) { + if (children[index] == layer) + break; + } + size_t next = index + 1; + if (next < children.size()) + return FirstLayer(children[next]); + return parent; +} + +// Given the root nodes of a View tree and a Layer tree, makes sure the two +// trees are in sync. +bool ViewAndLayerTreeAreConsistent(const views::View* view, + const ui::Layer* layer) { + const views::View* v = FirstView(view); + const ui::Layer* l = FirstLayer(layer); + while (v && l) { + // Find the view with a layer. + while (v && !v->layer()) + v = NextView(v); + EXPECT_TRUE(v); + if (!v) + return false; + + // Check if the View tree and the Layer tree are in sync. + EXPECT_EQ(l, v->layer()); + if (v->layer() != l) + return false; + + // Check if the visibility states of the View and the Layer are in sync. + EXPECT_EQ(l->IsDrawn(), v->IsVisibleInRootView()); + if (v->IsVisibleInRootView() != l->IsDrawn()) { + for (const views::View* vv = v; vv; vv = vv->parent()) + LOG(ERROR) << "V: " << vv << " " << vv->IsVisible() << " " + << vv->IsVisibleInRootView() << " " << vv->layer(); + for (const ui::Layer* ll = l; ll; ll = ll->parent()) + LOG(ERROR) << "L: " << ll << " " << ll->IsDrawn(); + return false; + } + + // Check if the size of the View and the Layer are in sync. + EXPECT_EQ(l->bounds(), v->bounds()); + if (v->bounds() != l->bounds()) + return false; + + if (v == view || l == layer) + return v == view && l == layer; + + v = NextView(v); + l = NextLayer(l); + } + + return false; +} + +// Constructs a View tree with the specified depth. +void ConstructTree(views::View* view, int depth) { + if (depth == 0) + return; + int count = base::RandInt(1, 5); + for (int i = 0; i < count; i++) { + views::View* v = new views::View; + view->AddChildView(v); + if (base::RandDouble() > 0.5) + v->SetPaintToLayer(true); + if (base::RandDouble() < 0.2) + v->SetVisible(false); + + ConstructTree(v, depth - 1); + } +} + +void ScrambleTree(views::View* view) { + int count = view->child_count(); + if (count == 0) + return; + for (int i = 0; i < count; i++) { + ScrambleTree(view->child_at(i)); + } + + if (count > 1) { + int a = base::RandInt(0, count - 1); + int b = base::RandInt(0, count - 1); + + views::View* view_a = view->child_at(a); + views::View* view_b = view->child_at(b); + view->ReorderChildView(view_a, b); + view->ReorderChildView(view_b, a); + } + + if (!view->layer() && base::RandDouble() < 0.1) + view->SetPaintToLayer(true); + + if (base::RandDouble() < 0.1) + view->SetVisible(!view->IsVisible()); +} + +} + +namespace views { + +typedef ViewsTestBase ViewTest; + +// A derived class for testing purpose. +class TestView : public View { + public: + TestView() : View(), in_touch_sequence_(false) {} + virtual ~TestView() {} + + // Reset all test state + void Reset() { + did_change_bounds_ = false; + last_mouse_event_type_ = 0; + location_.SetPoint(0, 0); + last_touch_event_type_ = 0; + last_touch_event_was_handled_ = false; + last_clip_.setEmpty(); + accelerator_count_map_.clear(); + } + + virtual void OnBoundsChanged(const gfx::Rect& previous_bounds) OVERRIDE; + virtual bool OnMousePressed(const MouseEvent& event) OVERRIDE; + virtual bool OnMouseDragged(const MouseEvent& event) OVERRIDE; + virtual void OnMouseReleased(const MouseEvent& event) OVERRIDE; + virtual ui::TouchStatus OnTouchEvent(const TouchEvent& event) OVERRIDE; + virtual void Paint(gfx::Canvas* canvas) OVERRIDE; + virtual void SchedulePaintInRect(const gfx::Rect& rect) OVERRIDE; + virtual bool AcceleratorPressed(const ui::Accelerator& accelerator) OVERRIDE; + + // OnBoundsChanged. + bool did_change_bounds_; + gfx::Rect new_bounds_; + + // MouseEvent. + int last_mouse_event_type_; + gfx::Point location_; + + // Painting. + std::vector<gfx::Rect> scheduled_paint_rects_; + + // TouchEvent. + int last_touch_event_type_; + bool last_touch_event_was_handled_; + bool in_touch_sequence_; + + // Painting. + SkRect last_clip_; + + // Accelerators. + std::map<ui::Accelerator, int> accelerator_count_map_; +}; + +// Mock instance of the GestureManager for testing. +class MockGestureManager : public GestureManager { + public: + // Reset all test state. + void Reset() { + last_touch_event_ = 0; + last_view_ = NULL; + previously_handled_flag_ = false; + dispatched_synthetic_event_ = false; + } + + bool ProcessTouchEventForGesture(const TouchEvent& event, + View* source, + ui::TouchStatus status); + MockGestureManager(); + + bool previously_handled_flag_; + int last_touch_event_; + View *last_view_; + bool dispatched_synthetic_event_; + + DISALLOW_COPY_AND_ASSIGN(MockGestureManager); +}; + +// A view subclass that ignores all touch events for testing purposes. +class TestViewIgnoreTouch : public TestView { + public: + TestViewIgnoreTouch() : TestView() {} + virtual ~TestViewIgnoreTouch() {} + + private: + virtual ui::TouchStatus OnTouchEvent(const TouchEvent& event) OVERRIDE; +}; + +//////////////////////////////////////////////////////////////////////////////// +// OnBoundsChanged +//////////////////////////////////////////////////////////////////////////////// + +void TestView::OnBoundsChanged(const gfx::Rect& previous_bounds) { + did_change_bounds_ = true; + new_bounds_ = bounds(); +} + +TEST_F(ViewTest, OnBoundsChanged) { + TestView v; + + gfx::Rect prev_rect(0, 0, 200, 200); + gfx::Rect new_rect(100, 100, 250, 250); + + v.SetBoundsRect(prev_rect); + v.Reset(); + v.SetBoundsRect(new_rect); + + EXPECT_EQ(v.did_change_bounds_, true); + EXPECT_EQ(v.new_bounds_, new_rect); + EXPECT_EQ(v.bounds(), new_rect); +} + +//////////////////////////////////////////////////////////////////////////////// +// MouseEvent +//////////////////////////////////////////////////////////////////////////////// + +bool TestView::OnMousePressed(const MouseEvent& event) { + last_mouse_event_type_ = event.type(); + location_.SetPoint(event.x(), event.y()); + return true; +} + +bool TestView::OnMouseDragged(const MouseEvent& event) { + last_mouse_event_type_ = event.type(); + location_.SetPoint(event.x(), event.y()); + return true; +} + +void TestView::OnMouseReleased(const MouseEvent& event) { + last_mouse_event_type_ = event.type(); + location_.SetPoint(event.x(), event.y()); +} + +TEST_F(ViewTest, MouseEvent) { + TestView* v1 = new TestView(); + v1->SetBounds(0, 0, 300, 300); + + TestView* v2 = new TestView(); + v2->SetBounds(100, 100, 100, 100); + + scoped_ptr<Widget> widget(new Widget); + Widget::InitParams params(Widget::InitParams::TYPE_POPUP); + params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + params.bounds = gfx::Rect(50, 50, 650, 650); + widget->Init(params); + View* root = widget->GetRootView(); + + root->AddChildView(v1); + v1->AddChildView(v2); + + v1->Reset(); + v2->Reset(); + + MouseEvent pressed(ui::ET_MOUSE_PRESSED, + 110, + 120, + ui::EF_LEFT_BUTTON_DOWN); + root->OnMousePressed(pressed); + EXPECT_EQ(v2->last_mouse_event_type_, ui::ET_MOUSE_PRESSED); + EXPECT_EQ(v2->location_.x(), 10); + EXPECT_EQ(v2->location_.y(), 20); + // Make sure v1 did not receive the event + EXPECT_EQ(v1->last_mouse_event_type_, 0); + + // Drag event out of bounds. Should still go to v2 + v1->Reset(); + v2->Reset(); + MouseEvent dragged(ui::ET_MOUSE_DRAGGED, + 50, + 40, + ui::EF_LEFT_BUTTON_DOWN); + root->OnMouseDragged(dragged); + EXPECT_EQ(v2->last_mouse_event_type_, ui::ET_MOUSE_DRAGGED); + EXPECT_EQ(v2->location_.x(), -50); + EXPECT_EQ(v2->location_.y(), -60); + // Make sure v1 did not receive the event + EXPECT_EQ(v1->last_mouse_event_type_, 0); + + // Releasted event out of bounds. Should still go to v2 + v1->Reset(); + v2->Reset(); + MouseEvent released(ui::ET_MOUSE_RELEASED, 0, 0, 0); + root->OnMouseDragged(released); + EXPECT_EQ(v2->last_mouse_event_type_, ui::ET_MOUSE_RELEASED); + EXPECT_EQ(v2->location_.x(), -100); + EXPECT_EQ(v2->location_.y(), -100); + // Make sure v1 did not receive the event + EXPECT_EQ(v1->last_mouse_event_type_, 0); + + widget->CloseNow(); +} + +//////////////////////////////////////////////////////////////////////////////// +// TouchEvent +//////////////////////////////////////////////////////////////////////////////// +bool MockGestureManager::ProcessTouchEventForGesture( + const TouchEvent& event, + View* source, + ui::TouchStatus status) { + if (status != ui::TOUCH_STATUS_UNKNOWN) { + dispatched_synthetic_event_ = false; + return false; + } + last_touch_event_ = event.type(); + last_view_ = source; + previously_handled_flag_ = status != ui::TOUCH_STATUS_UNKNOWN; + dispatched_synthetic_event_ = true; + return true; +} + +MockGestureManager::MockGestureManager() { +} + +ui::TouchStatus TestView::OnTouchEvent(const TouchEvent& event) { + last_touch_event_type_ = event.type(); + location_.SetPoint(event.x(), event.y()); + if (!in_touch_sequence_) { + if (event.type() == ui::ET_TOUCH_PRESSED) { + in_touch_sequence_ = true; + return ui::TOUCH_STATUS_START; + } + } else { + if (event.type() == ui::ET_TOUCH_RELEASED) { + in_touch_sequence_ = false; + return ui::TOUCH_STATUS_END; + } + return ui::TOUCH_STATUS_CONTINUE; + } + return last_touch_event_was_handled_ ? ui::TOUCH_STATUS_CONTINUE : + ui::TOUCH_STATUS_UNKNOWN; +} + +ui::TouchStatus TestViewIgnoreTouch::OnTouchEvent(const TouchEvent& event) { + return ui::TOUCH_STATUS_UNKNOWN; +} + +TEST_F(ViewTest, TouchEvent) { + MockGestureManager gm; + + TestView* v1 = new TestView(); + v1->SetBounds(0, 0, 300, 300); + + TestView* v2 = new TestView(); + v2->SetBounds(100, 100, 100, 100); + + TestView* v3 = new TestViewIgnoreTouch(); + v3->SetBounds(0, 0, 100, 100); + + scoped_ptr<Widget> widget(new Widget()); + Widget::InitParams params(Widget::InitParams::TYPE_POPUP); + params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + params.bounds = gfx::Rect(50, 50, 650, 650); + widget->Init(params); + View* root = widget->GetRootView(); + + root->AddChildView(v1); + static_cast<internal::RootView*>(root)->SetGestureManagerForTesting(&gm); + v1->AddChildView(v2); + v2->AddChildView(v3); + + // |v3| completely obscures |v2|, but all the touch events on |v3| should + // reach |v2| because |v3| doesn't process any touch events. + + // Make sure if none of the views handle the touch event, the gesture manager + // does. + v1->Reset(); + v2->Reset(); + gm.Reset(); + + TouchEvent unhandled(ui::ET_TOUCH_MOVED, + 400, + 400, + 0, /* no flags */ + 0, /* first finger touch */ + 1.0, 0.0, 1.0, 0.0); + root->OnTouchEvent(unhandled); + + EXPECT_EQ(v1->last_touch_event_type_, 0); + EXPECT_EQ(v2->last_touch_event_type_, 0); + + EXPECT_EQ(gm.previously_handled_flag_, false); + EXPECT_EQ(gm.last_touch_event_, ui::ET_TOUCH_MOVED); + EXPECT_EQ(gm.last_view_, root); + EXPECT_EQ(gm.dispatched_synthetic_event_, true); + + // Test press, drag, release touch sequence. + v1->Reset(); + v2->Reset(); + gm.Reset(); + + TouchEvent pressed(ui::ET_TOUCH_PRESSED, + 110, + 120, + 0, /* no flags */ + 0, /* first finger touch */ + 1.0, 0.0, 1.0, 0.0); + v2->last_touch_event_was_handled_ = true; + root->OnTouchEvent(pressed); + + EXPECT_EQ(v2->last_touch_event_type_, ui::ET_TOUCH_PRESSED); + EXPECT_EQ(v2->location_.x(), 10); + EXPECT_EQ(v2->location_.y(), 20); + // Make sure v1 did not receive the event + EXPECT_EQ(v1->last_touch_event_type_, 0); + + // Since v2 handled the touch-event, the gesture manager should not handle it. + EXPECT_EQ(gm.last_touch_event_, 0); + EXPECT_EQ(NULL, gm.last_view_); + EXPECT_EQ(gm.previously_handled_flag_, false); + + // Drag event out of bounds. Should still go to v2 + v1->Reset(); + v2->Reset(); + TouchEvent dragged(ui::ET_TOUCH_MOVED, + 50, + 40, + 0, /* no flags */ + 0, /* first finger touch */ + 1.0, 0.0, 1.0, 0.0); + + root->OnTouchEvent(dragged); + EXPECT_EQ(v2->last_touch_event_type_, ui::ET_TOUCH_MOVED); + EXPECT_EQ(v2->location_.x(), -50); + EXPECT_EQ(v2->location_.y(), -60); + // Make sure v1 did not receive the event + EXPECT_EQ(v1->last_touch_event_type_, 0); + + EXPECT_EQ(gm.last_touch_event_, 0); + EXPECT_EQ(NULL, gm.last_view_); + EXPECT_EQ(gm.previously_handled_flag_, false); + + // Released event out of bounds. Should still go to v2 + v1->Reset(); + v2->Reset(); + TouchEvent released(ui::ET_TOUCH_RELEASED, 0, 0, 0, 0 /* first finger */, + 1.0, 0.0, 1.0, 0.0); + v2->last_touch_event_was_handled_ = true; + root->OnTouchEvent(released); + EXPECT_EQ(v2->last_touch_event_type_, ui::ET_TOUCH_RELEASED); + EXPECT_EQ(v2->location_.x(), -100); + EXPECT_EQ(v2->location_.y(), -100); + // Make sure v1 did not receive the event + EXPECT_EQ(v1->last_touch_event_type_, 0); + + EXPECT_EQ(gm.last_touch_event_, 0); + EXPECT_EQ(NULL, gm.last_view_); + EXPECT_EQ(gm.previously_handled_flag_, false); + + widget->CloseNow(); +} + +//////////////////////////////////////////////////////////////////////////////// +// Painting +//////////////////////////////////////////////////////////////////////////////// + +void TestView::Paint(gfx::Canvas* canvas) { + canvas->GetSkCanvas()->getClipBounds(&last_clip_); +} + +void TestView::SchedulePaintInRect(const gfx::Rect& rect) { + scheduled_paint_rects_.push_back(rect); + View::SchedulePaintInRect(rect); +} + +void CheckRect(const SkRect& check_rect, const SkRect& target_rect) { + EXPECT_EQ(target_rect.fLeft, check_rect.fLeft); + EXPECT_EQ(target_rect.fRight, check_rect.fRight); + EXPECT_EQ(target_rect.fTop, check_rect.fTop); + EXPECT_EQ(target_rect.fBottom, check_rect.fBottom); +} + +/* This test is disabled because it is flakey on some systems. +TEST_F(ViewTest, DISABLED_Painting) { + // Determine if InvalidateRect generates an empty paint rectangle. + EmptyWindow paint_window(CRect(50, 50, 650, 650)); + paint_window.RedrawWindow(CRect(0, 0, 600, 600), NULL, + RDW_UPDATENOW | RDW_INVALIDATE | RDW_ALLCHILDREN); + bool empty_paint = paint_window.empty_paint(); + + NativeWidgetWin window; + window.set_delete_on_destroy(false); + window.set_window_style(WS_OVERLAPPEDWINDOW); + window.Init(NULL, gfx::Rect(50, 50, 650, 650), NULL); + View* root = window.GetRootView(); + + TestView* v1 = new TestView(); + v1->SetBounds(0, 0, 650, 650); + root->AddChildView(v1); + + TestView* v2 = new TestView(); + v2->SetBounds(10, 10, 80, 80); + v1->AddChildView(v2); + + TestView* v3 = new TestView(); + v3->SetBounds(10, 10, 60, 60); + v2->AddChildView(v3); + + TestView* v4 = new TestView(); + v4->SetBounds(10, 200, 100, 100); + v1->AddChildView(v4); + + // Make sure to paint current rects + PaintRootView(root, empty_paint); + + + v1->Reset(); + v2->Reset(); + v3->Reset(); + v4->Reset(); + v3->SchedulePaintInRect(gfx::Rect(10, 10, 10, 10)); + PaintRootView(root, empty_paint); + + SkRect tmp_rect; + + tmp_rect.set(SkIntToScalar(10), + SkIntToScalar(10), + SkIntToScalar(20), + SkIntToScalar(20)); + CheckRect(v3->last_clip_, tmp_rect); + + tmp_rect.set(SkIntToScalar(20), + SkIntToScalar(20), + SkIntToScalar(30), + SkIntToScalar(30)); + CheckRect(v2->last_clip_, tmp_rect); + + tmp_rect.set(SkIntToScalar(30), + SkIntToScalar(30), + SkIntToScalar(40), + SkIntToScalar(40)); + CheckRect(v1->last_clip_, tmp_rect); + + // Make sure v4 was not painted + tmp_rect.setEmpty(); + CheckRect(v4->last_clip_, tmp_rect); + + window.DestroyWindow(); +} +*/ + +#if defined(OS_WIN) +TEST_F(ViewTest, RemoveNotification) { +#else +// TODO(beng): stopped working with widget hierarchy split, +// http://crbug.com/82364 +TEST_F(ViewTest, DISABLED_RemoveNotification) { +#endif + ViewStorage* vs = ViewStorage::GetInstance(); + Widget* widget = new Widget; + widget->Init(Widget::InitParams(Widget::InitParams::TYPE_POPUP)); + View* root_view = widget->GetRootView(); + + View* v1 = new View; + int s1 = vs->CreateStorageID(); + vs->StoreView(s1, v1); + root_view->AddChildView(v1); + View* v11 = new View; + int s11 = vs->CreateStorageID(); + vs->StoreView(s11, v11); + v1->AddChildView(v11); + View* v111 = new View; + int s111 = vs->CreateStorageID(); + vs->StoreView(s111, v111); + v11->AddChildView(v111); + View* v112 = new View; + int s112 = vs->CreateStorageID(); + vs->StoreView(s112, v112); + v11->AddChildView(v112); + View* v113 = new View; + int s113 = vs->CreateStorageID(); + vs->StoreView(s113, v113); + v11->AddChildView(v113); + View* v1131 = new View; + int s1131 = vs->CreateStorageID(); + vs->StoreView(s1131, v1131); + v113->AddChildView(v1131); + View* v12 = new View; + int s12 = vs->CreateStorageID(); + vs->StoreView(s12, v12); + v1->AddChildView(v12); + + View* v2 = new View; + int s2 = vs->CreateStorageID(); + vs->StoreView(s2, v2); + root_view->AddChildView(v2); + View* v21 = new View; + int s21 = vs->CreateStorageID(); + vs->StoreView(s21, v21); + v2->AddChildView(v21); + View* v211 = new View; + int s211 = vs->CreateStorageID(); + vs->StoreView(s211, v211); + v21->AddChildView(v211); + + size_t stored_views = vs->view_count(); + + // Try removing a leaf view. + v21->RemoveChildView(v211); + EXPECT_EQ(stored_views - 1, vs->view_count()); + EXPECT_EQ(NULL, vs->RetrieveView(s211)); + delete v211; // We won't use this one anymore. + + // Now try removing a view with a hierarchy of depth 1. + v11->RemoveChildView(v113); + EXPECT_EQ(stored_views - 3, vs->view_count()); + EXPECT_EQ(NULL, vs->RetrieveView(s113)); + EXPECT_EQ(NULL, vs->RetrieveView(s1131)); + delete v113; // We won't use this one anymore. + + // Now remove even more. + root_view->RemoveChildView(v1); + EXPECT_EQ(NULL, vs->RetrieveView(s1)); + EXPECT_EQ(NULL, vs->RetrieveView(s11)); + EXPECT_EQ(NULL, vs->RetrieveView(s12)); + EXPECT_EQ(NULL, vs->RetrieveView(s111)); + EXPECT_EQ(NULL, vs->RetrieveView(s112)); + + // Put v1 back for more tests. + root_view->AddChildView(v1); + vs->StoreView(s1, v1); + + // Synchronously closing the window deletes the view hierarchy, which should + // remove all its views from ViewStorage. + widget->CloseNow(); + EXPECT_EQ(stored_views - 10, vs->view_count()); + EXPECT_EQ(NULL, vs->RetrieveView(s1)); + EXPECT_EQ(NULL, vs->RetrieveView(s12)); + EXPECT_EQ(NULL, vs->RetrieveView(s11)); + EXPECT_EQ(NULL, vs->RetrieveView(s12)); + EXPECT_EQ(NULL, vs->RetrieveView(s21)); + EXPECT_EQ(NULL, vs->RetrieveView(s111)); + EXPECT_EQ(NULL, vs->RetrieveView(s112)); +} + +namespace { +class HitTestView : public View { + public: + explicit HitTestView(bool has_hittest_mask) + : has_hittest_mask_(has_hittest_mask) { + } + virtual ~HitTestView() {} + + protected: + // Overridden from View: + virtual bool HasHitTestMask() const { + return has_hittest_mask_; + } + virtual void GetHitTestMask(gfx::Path* mask) const { + DCHECK(has_hittest_mask_); + DCHECK(mask); + + SkScalar w = SkIntToScalar(width()); + SkScalar h = SkIntToScalar(height()); + + // Create a triangular mask within the bounds of this View. + mask->moveTo(w / 2, 0); + mask->lineTo(w, h); + mask->lineTo(0, h); + mask->close(); + } + + private: + bool has_hittest_mask_; + + DISALLOW_COPY_AND_ASSIGN(HitTestView); +}; + +gfx::Point ConvertPointToView(View* view, const gfx::Point& p) { + gfx::Point tmp(p); + View::ConvertPointToView(view->GetWidget()->GetRootView(), view, &tmp); + return tmp; +} + +void RotateCounterclockwise(ui::Transform& transform) { + transform.matrix().set3x3(0, -1, 0, + 1, 0, 0, + 0, 0, 1); +} + +void RotateClockwise(ui::Transform& transform) { + transform.matrix().set3x3( 0, 1, 0, + -1, 0, 0, + 0, 0, 1); +} + +} // namespace + +TEST_F(ViewTest, HitTestMasks) { + Widget* widget = new Widget; + widget->Init(Widget::InitParams(Widget::InitParams::TYPE_POPUP)); + View* root_view = widget->GetRootView(); + root_view->SetBounds(0, 0, 500, 500); + + gfx::Rect v1_bounds = gfx::Rect(0, 0, 100, 100); + HitTestView* v1 = new HitTestView(false); + v1->SetBoundsRect(v1_bounds); + root_view->AddChildView(v1); + + gfx::Rect v2_bounds = gfx::Rect(105, 0, 100, 100); + HitTestView* v2 = new HitTestView(true); + v2->SetBoundsRect(v2_bounds); + root_view->AddChildView(v2); + + gfx::Point v1_centerpoint = v1_bounds.CenterPoint(); + gfx::Point v2_centerpoint = v2_bounds.CenterPoint(); + gfx::Point v1_origin = v1_bounds.origin(); + gfx::Point v2_origin = v2_bounds.origin(); + + // Test HitTest + EXPECT_TRUE(v1->HitTest(ConvertPointToView(v1, v1_centerpoint))); + EXPECT_TRUE(v2->HitTest(ConvertPointToView(v2, v2_centerpoint))); + + EXPECT_TRUE(v1->HitTest(ConvertPointToView(v1, v1_origin))); + EXPECT_FALSE(v2->HitTest(ConvertPointToView(v2, v2_origin))); + + // Test GetEventHandlerForPoint + EXPECT_EQ(v1, root_view->GetEventHandlerForPoint(v1_centerpoint)); + EXPECT_EQ(v2, root_view->GetEventHandlerForPoint(v2_centerpoint)); + EXPECT_EQ(v1, root_view->GetEventHandlerForPoint(v1_origin)); + EXPECT_EQ(root_view, root_view->GetEventHandlerForPoint(v2_origin)); + + widget->CloseNow(); +} + +TEST_F(ViewTest, Textfield) { + const string16 kText = ASCIIToUTF16("Reality is that which, when you stop " + "believing it, doesn't go away."); + const string16 kExtraText = ASCIIToUTF16("Pretty deep, Philip!"); + const string16 kEmptyString; + + ui::Clipboard clipboard; + + Widget* widget = new Widget; + Widget::InitParams params(Widget::InitParams::TYPE_POPUP); + params.bounds = gfx::Rect(0, 0, 100, 100); + widget->Init(params); + View* root_view = widget->GetRootView(); + + Textfield* textfield = new Textfield(); + root_view->AddChildView(textfield); + + // Test setting, appending text. + textfield->SetText(kText); + EXPECT_EQ(kText, textfield->text()); + textfield->AppendText(kExtraText); + EXPECT_EQ(kText + kExtraText, textfield->text()); + textfield->SetText(string16()); + EXPECT_EQ(kEmptyString, textfield->text()); + + // Test selection related methods. + textfield->SetText(kText); + EXPECT_EQ(kEmptyString, textfield->GetSelectedText()); + textfield->SelectAll(); + EXPECT_EQ(kText, textfield->text()); + textfield->ClearSelection(); + EXPECT_EQ(kEmptyString, textfield->GetSelectedText()); + + widget->CloseNow(); +} + +#if defined(OS_WIN) && !defined(USE_AURA) + +// Tests that the Textfield view respond appropiately to cut/copy/paste. +TEST_F(ViewTest, TextfieldCutCopyPaste) { + const string16 kNormalText = ASCIIToUTF16("Normal"); + const string16 kReadOnlyText = ASCIIToUTF16("Read only"); + const string16 kPasswordText = ASCIIToUTF16("Password! ** Secret stuff **"); + + ui::Clipboard clipboard; + + Widget* widget = new Widget; + Widget::InitParams params(Widget::InitParams::TYPE_POPUP); + params.bounds = gfx::Rect(0, 0, 100, 100); + widget->Init(params); + View* root_view = widget->GetRootView(); + + Textfield* normal = new Textfield(); + Textfield* read_only = new Textfield(); + read_only->SetReadOnly(true); + Textfield* password = new Textfield(Textfield::STYLE_PASSWORD); + + root_view->AddChildView(normal); + root_view->AddChildView(read_only); + root_view->AddChildView(password); + + normal->SetText(kNormalText); + read_only->SetText(kReadOnlyText); + password->SetText(kPasswordText); + + // + // Test cut. + // + ASSERT_TRUE(normal->GetTestingHandle()); + normal->SelectAll(); + ::SendMessage(normal->GetTestingHandle(), WM_CUT, 0, 0); + + string16 result; + clipboard.ReadText(ui::Clipboard::BUFFER_STANDARD, &result); + EXPECT_EQ(kNormalText, result); + normal->SetText(kNormalText); // Let's revert to the original content. + + ASSERT_TRUE(read_only->GetTestingHandle()); + read_only->SelectAll(); + ::SendMessage(read_only->GetTestingHandle(), WM_CUT, 0, 0); + result.clear(); + clipboard.ReadText(ui::Clipboard::BUFFER_STANDARD, &result); + // Cut should have failed, so the clipboard content should not have changed. + EXPECT_EQ(kNormalText, result); + + ASSERT_TRUE(password->GetTestingHandle()); + password->SelectAll(); + ::SendMessage(password->GetTestingHandle(), WM_CUT, 0, 0); + result.clear(); + clipboard.ReadText(ui::Clipboard::BUFFER_STANDARD, &result); + // Cut should have failed, so the clipboard content should not have changed. + EXPECT_EQ(kNormalText, result); + + // + // Test copy. + // + + // Let's start with read_only as the clipboard already contains the content + // of normal. + read_only->SelectAll(); + ::SendMessage(read_only->GetTestingHandle(), WM_COPY, 0, 0); + result.clear(); + clipboard.ReadText(ui::Clipboard::BUFFER_STANDARD, &result); + EXPECT_EQ(kReadOnlyText, result); + + normal->SelectAll(); + ::SendMessage(normal->GetTestingHandle(), WM_COPY, 0, 0); + result.clear(); + clipboard.ReadText(ui::Clipboard::BUFFER_STANDARD, &result); + EXPECT_EQ(kNormalText, result); + + password->SelectAll(); + ::SendMessage(password->GetTestingHandle(), WM_COPY, 0, 0); + result.clear(); + clipboard.ReadText(ui::Clipboard::BUFFER_STANDARD, &result); + // We don't let you copy from a password field, clipboard should not have + // changed. + EXPECT_EQ(kNormalText, result); + + // + // Test Paste. + // + // Note that we use GetWindowText instead of Textfield::GetText below as the + // text in the Textfield class is synced to the text of the HWND on + // WM_KEYDOWN messages that we are not simulating here. + + // Attempting to copy kNormalText in a read-only text-field should fail. + read_only->SelectAll(); + ::SendMessage(read_only->GetTestingHandle(), WM_KEYDOWN, 0, 0); + wchar_t buffer[1024] = { 0 }; + ::GetWindowText(read_only->GetTestingHandle(), buffer, 1024); + EXPECT_EQ(kReadOnlyText, string16(buffer)); + + password->SelectAll(); + ::SendMessage(password->GetTestingHandle(), WM_PASTE, 0, 0); + ::GetWindowText(password->GetTestingHandle(), buffer, 1024); + EXPECT_EQ(kNormalText, string16(buffer)); + + // Copy from read_only so the string we are pasting is not the same as the + // current one. + read_only->SelectAll(); + ::SendMessage(read_only->GetTestingHandle(), WM_COPY, 0, 0); + normal->SelectAll(); + ::SendMessage(normal->GetTestingHandle(), WM_PASTE, 0, 0); + ::GetWindowText(normal->GetTestingHandle(), buffer, 1024); + EXPECT_EQ(kReadOnlyText, string16(buffer)); + widget->CloseNow(); +} +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Accelerators +//////////////////////////////////////////////////////////////////////////////// +bool TestView::AcceleratorPressed(const ui::Accelerator& accelerator) { + accelerator_count_map_[accelerator]++; + return true; +} + +#if defined(OS_WIN) && !defined(USE_AURA) +TEST_F(ViewTest, ActivateAccelerator) { + // Register a keyboard accelerator before the view is added to a window. + ui::Accelerator return_accelerator(ui::VKEY_RETURN, false, false, false); + TestView* view = new TestView(); + view->Reset(); + view->AddAccelerator(return_accelerator); + EXPECT_EQ(view->accelerator_count_map_[return_accelerator], 0); + + // Create a window and add the view as its child. + scoped_ptr<Widget> widget(new Widget); + Widget::InitParams params(Widget::InitParams::TYPE_POPUP); + params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + params.bounds = gfx::Rect(0, 0, 100, 100); + widget->Init(params); + View* root = widget->GetRootView(); + root->AddChildView(view); + widget->Show(); + + // Get the focus manager. + FocusManager* focus_manager = widget->GetFocusManager(); + ASSERT_TRUE(focus_manager); + + // Hit the return key and see if it takes effect. + EXPECT_TRUE(focus_manager->ProcessAccelerator(return_accelerator)); + EXPECT_EQ(view->accelerator_count_map_[return_accelerator], 1); + + // Hit the escape key. Nothing should happen. + ui::Accelerator escape_accelerator(ui::VKEY_ESCAPE, false, false, false); + EXPECT_FALSE(focus_manager->ProcessAccelerator(escape_accelerator)); + EXPECT_EQ(view->accelerator_count_map_[return_accelerator], 1); + EXPECT_EQ(view->accelerator_count_map_[escape_accelerator], 0); + + // Now register the escape key and hit it again. + view->AddAccelerator(escape_accelerator); + EXPECT_TRUE(focus_manager->ProcessAccelerator(escape_accelerator)); + EXPECT_EQ(view->accelerator_count_map_[return_accelerator], 1); + EXPECT_EQ(view->accelerator_count_map_[escape_accelerator], 1); + + // Remove the return key accelerator. + view->RemoveAccelerator(return_accelerator); + EXPECT_FALSE(focus_manager->ProcessAccelerator(return_accelerator)); + EXPECT_EQ(view->accelerator_count_map_[return_accelerator], 1); + EXPECT_EQ(view->accelerator_count_map_[escape_accelerator], 1); + + // Add it again. Hit the return key and the escape key. + view->AddAccelerator(return_accelerator); + EXPECT_TRUE(focus_manager->ProcessAccelerator(return_accelerator)); + EXPECT_EQ(view->accelerator_count_map_[return_accelerator], 2); + EXPECT_EQ(view->accelerator_count_map_[escape_accelerator], 1); + EXPECT_TRUE(focus_manager->ProcessAccelerator(escape_accelerator)); + EXPECT_EQ(view->accelerator_count_map_[return_accelerator], 2); + EXPECT_EQ(view->accelerator_count_map_[escape_accelerator], 2); + + // Remove all the accelerators. + view->ResetAccelerators(); + EXPECT_FALSE(focus_manager->ProcessAccelerator(return_accelerator)); + EXPECT_EQ(view->accelerator_count_map_[return_accelerator], 2); + EXPECT_EQ(view->accelerator_count_map_[escape_accelerator], 2); + EXPECT_FALSE(focus_manager->ProcessAccelerator(escape_accelerator)); + EXPECT_EQ(view->accelerator_count_map_[return_accelerator], 2); + EXPECT_EQ(view->accelerator_count_map_[escape_accelerator], 2); + + widget->CloseNow(); +} +#endif + +#if defined(OS_WIN) && !defined(USE_AURA) +TEST_F(ViewTest, HiddenViewWithAccelerator) { + ui::Accelerator return_accelerator(ui::VKEY_RETURN, false, false, false); + TestView* view = new TestView(); + view->Reset(); + view->AddAccelerator(return_accelerator); + EXPECT_EQ(view->accelerator_count_map_[return_accelerator], 0); + + scoped_ptr<Widget> widget(new Widget); + Widget::InitParams params(Widget::InitParams::TYPE_POPUP); + params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + params.bounds = gfx::Rect(0, 0, 100, 100); + widget->Init(params); + View* root = widget->GetRootView(); + root->AddChildView(view); + widget->Show(); + + FocusManager* focus_manager = widget->GetFocusManager(); + ASSERT_TRUE(focus_manager); + + view->SetVisible(false); + EXPECT_EQ(NULL, + focus_manager->GetCurrentTargetForAccelerator(return_accelerator)); + + view->SetVisible(true); + EXPECT_EQ(view, + focus_manager->GetCurrentTargetForAccelerator(return_accelerator)); + + widget->CloseNow(); +} +#endif + +#if defined(OS_WIN) && !defined(USE_AURA) +TEST_F(ViewTest, ViewInHiddenWidgetWithAccelerator) { + ui::Accelerator return_accelerator(ui::VKEY_RETURN, false, false, false); + TestView* view = new TestView(); + view->Reset(); + view->AddAccelerator(return_accelerator); + EXPECT_EQ(view->accelerator_count_map_[return_accelerator], 0); + + scoped_ptr<Widget> widget(new Widget); + Widget::InitParams params(Widget::InitParams::TYPE_POPUP); + params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + params.bounds = gfx::Rect(0, 0, 100, 100); + widget->Init(params); + View* root = widget->GetRootView(); + root->AddChildView(view); + + FocusManager* focus_manager = widget->GetFocusManager(); + ASSERT_TRUE(focus_manager); + + EXPECT_EQ(NULL, + focus_manager->GetCurrentTargetForAccelerator(return_accelerator)); + + widget->Show(); + EXPECT_EQ(view, + focus_manager->GetCurrentTargetForAccelerator(return_accelerator)); + + widget->Hide(); + EXPECT_EQ(NULL, + focus_manager->GetCurrentTargetForAccelerator(return_accelerator)); + + widget->CloseNow(); +} +#endif + +#if defined(OS_WIN) && !defined(USE_AURA) +//////////////////////////////////////////////////////////////////////////////// +// Mouse-wheel message rerouting +//////////////////////////////////////////////////////////////////////////////// +class ScrollableTestView : public View { + public: + ScrollableTestView() { } + + virtual gfx::Size GetPreferredSize() { + return gfx::Size(100, 10000); + } + + virtual void Layout() { + SizeToPreferredSize(); + } +}; + +class TestViewWithControls : public View { + public: + TestViewWithControls() { + text_field_ = new Textfield(); + AddChildView(text_field_); + } + + Textfield* text_field_; +}; + +class SimpleWidgetDelegate : public WidgetDelegate { + public: + explicit SimpleWidgetDelegate(View* contents) : contents_(contents) { } + + virtual void DeleteDelegate() { delete this; } + + virtual View* GetContentsView() { return contents_; } + + virtual Widget* GetWidget() { return contents_->GetWidget(); } + virtual const Widget* GetWidget() const { return contents_->GetWidget(); } + + private: + View* contents_; +}; + +// Tests that the mouse-wheel messages are correctly rerouted to the window +// under the mouse. +// TODO(jcampan): http://crbug.com/10572 Disabled as it fails on the Vista build +// bot. +// Note that this fails for a variety of reasons: +// - focused view is apparently reset across window activations and never +// properly restored +// - this test depends on you not having any other window visible open under the +// area that it opens the test windows. --beng +TEST_F(ViewTest, DISABLED_RerouteMouseWheelTest) { + TestViewWithControls* view_with_controls = new TestViewWithControls(); + Widget* window1 = Widget::CreateWindowWithBounds( + new SimpleWidgetDelegate(view_with_controls), + gfx::Rect(0, 0, 100, 100)); + window1->Show(); + ScrollView* scroll_view = new ScrollView(); + scroll_view->SetContents(new ScrollableTestView()); + Widget* window2 = Widget::CreateWindowWithBounds( + new SimpleWidgetDelegate(scroll_view), + gfx::Rect(200, 200, 100, 100)); + window2->Show(); + EXPECT_EQ(0, scroll_view->GetVisibleRect().y()); + + // Make the window1 active, as this is what it would be in real-world. + window1->Activate(); + + // Let's send a mouse-wheel message to the different controls and check that + // it is rerouted to the window under the mouse (effectively scrolling the + // scroll-view). + + // First to the Window's HWND. + ::SendMessage(view_with_controls->GetWidget()->GetNativeView(), + WM_MOUSEWHEEL, MAKEWPARAM(0, -20), MAKELPARAM(250, 250)); + EXPECT_EQ(20, scroll_view->GetVisibleRect().y()); + + // Then the text-field. + ::SendMessage(view_with_controls->text_field_->GetTestingHandle(), + WM_MOUSEWHEEL, MAKEWPARAM(0, -20), MAKELPARAM(250, 250)); + EXPECT_EQ(80, scroll_view->GetVisibleRect().y()); + + // Ensure we don't scroll when the mouse is not over that window. + ::SendMessage(view_with_controls->text_field_->GetTestingHandle(), + WM_MOUSEWHEEL, MAKEWPARAM(0, -20), MAKELPARAM(50, 50)); + EXPECT_EQ(80, scroll_view->GetVisibleRect().y()); + + window1->CloseNow(); + window2->CloseNow(); +} +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Dialogs' default button +//////////////////////////////////////////////////////////////////////////////// + +class MockMenuModel : public ui::MenuModel { + public: + MOCK_CONST_METHOD0(HasIcons, bool()); + MOCK_CONST_METHOD1(GetFirstItemIndex, int(gfx::NativeMenu native_menu)); + MOCK_CONST_METHOD0(GetItemCount, int()); + MOCK_CONST_METHOD1(GetTypeAt, ItemType(int index)); + MOCK_CONST_METHOD1(GetCommandIdAt, int(int index)); + MOCK_CONST_METHOD1(GetLabelAt, string16(int index)); + MOCK_CONST_METHOD1(IsItemDynamicAt, bool(int index)); + MOCK_CONST_METHOD1(GetLabelFontAt, const gfx::Font* (int index)); + MOCK_CONST_METHOD2(GetAcceleratorAt, bool(int index, + ui::Accelerator* accelerator)); + MOCK_CONST_METHOD1(IsItemCheckedAt, bool(int index)); + MOCK_CONST_METHOD1(GetGroupIdAt, int(int index)); + MOCK_METHOD2(GetIconAt, bool(int index, SkBitmap* icon)); + MOCK_CONST_METHOD1(GetButtonMenuItemAt, ui::ButtonMenuItemModel*(int index)); + MOCK_CONST_METHOD1(IsEnabledAt, bool(int index)); + MOCK_CONST_METHOD1(IsVisibleAt, bool(int index)); + MOCK_CONST_METHOD1(GetSubmenuModelAt, MenuModel*(int index)); + MOCK_METHOD1(HighlightChangedTo, void(int index)); + MOCK_METHOD1(ActivatedAt, void(int index)); + MOCK_METHOD2(ActivatedAt, void(int index, int disposition)); + MOCK_METHOD0(MenuWillShow, void()); + MOCK_METHOD0(MenuClosed, void()); + MOCK_METHOD1(SetMenuModelDelegate, void(ui::MenuModelDelegate* delegate)); + MOCK_METHOD3(GetModelAndIndexForCommandId, bool(int command_id, + MenuModel** model, int* index)); +}; + +class TestDialog : public DialogDelegate, public ButtonListener { + public: + explicit TestDialog(MockMenuModel* mock_menu_model) + : contents_(NULL), + button1_(NULL), + button2_(NULL), + checkbox_(NULL), + button_drop_(NULL), + last_pressed_button_(NULL), + mock_menu_model_(mock_menu_model), + canceled_(false), + oked_(false), + closeable_(false), + widget_(NULL) { + } + + void TearDown() { + // Now we can close safely. + closeable_ = true; + widget_->Close(); + widget_ = NULL; + // delegate has to be alive while shutting down. + MessageLoop::current()->DeleteSoon(FROM_HERE, this); + } + + // DialogDelegate implementation: + virtual int GetDefaultDialogButton() const OVERRIDE { + return ui::DIALOG_BUTTON_OK; + } + + virtual View* GetContentsView() OVERRIDE { + if (!contents_) { + contents_ = new View; + button1_ = new NativeTextButton(this, ASCIIToUTF16("Button1")); + button2_ = new NativeTextButton(this, ASCIIToUTF16("Button2")); + checkbox_ = new Checkbox(ASCIIToUTF16("My checkbox")); + button_drop_ = new ButtonDropDown(this, mock_menu_model_); + contents_->AddChildView(button1_); + contents_->AddChildView(button2_); + contents_->AddChildView(checkbox_); + contents_->AddChildView(button_drop_); + } + return contents_; + } + + // Prevent the dialog from really closing (so we can click the OK/Cancel + // buttons to our heart's content). + virtual bool Cancel() OVERRIDE { + canceled_ = true; + return closeable_; + } + virtual bool Accept() OVERRIDE { + oked_ = true; + return closeable_; + } + + virtual Widget* GetWidget() OVERRIDE { + return widget_; + } + virtual const Widget* GetWidget() const OVERRIDE { + return widget_; + } + + // ButtonListener implementation. + virtual void ButtonPressed(Button* sender, const Event& event) OVERRIDE { + last_pressed_button_ = sender; + } + + void ResetStates() { + oked_ = false; + canceled_ = false; + last_pressed_button_ = NULL; + } + + // Set up expectations for methods that are called when an (empty) menu is + // shown from a drop down button. + void ExpectShowDropMenu() { + if (mock_menu_model_) { + EXPECT_CALL(*mock_menu_model_, HasIcons()); + EXPECT_CALL(*mock_menu_model_, GetFirstItemIndex(_)); + EXPECT_CALL(*mock_menu_model_, GetItemCount()); + EXPECT_CALL(*mock_menu_model_, MenuClosed()); + } + } + + View* contents_; + NativeTextButton* button1_; + NativeTextButton* button2_; + Checkbox* checkbox_; + ButtonDropDown* button_drop_; + Button* last_pressed_button_; + MockMenuModel* mock_menu_model_; + + bool canceled_; + bool oked_; + bool closeable_; + Widget* widget_; +}; + +class DefaultButtonTest : public ViewTest { + public: + enum ButtonID { + OK, + CANCEL, + BUTTON1, + BUTTON2 + }; + + DefaultButtonTest() + : focus_manager_(NULL), + test_dialog_(NULL), + client_view_(NULL), + ok_button_(NULL), + cancel_button_(NULL) { + } + + virtual void SetUp() OVERRIDE { + ViewTest::SetUp(); + test_dialog_ = new TestDialog(NULL); + Widget* window = + Widget::CreateWindowWithBounds(test_dialog_, gfx::Rect(0, 0, 100, 100)); + test_dialog_->widget_ = window; + window->Show(); + focus_manager_ = test_dialog_->contents_->GetFocusManager(); + ASSERT_TRUE(focus_manager_ != NULL); + client_view_ = + static_cast<DialogClientView*>(window->client_view()); + ok_button_ = client_view_->ok_button(); + cancel_button_ = client_view_->cancel_button(); + } + + virtual void TearDown() OVERRIDE { + test_dialog_->TearDown(); + ViewTest::TearDown(); + } + + void SimulatePressingEnterAndCheckDefaultButton(ButtonID button_id) { + KeyEvent event(ui::ET_KEY_PRESSED, ui::VKEY_RETURN, 0); + focus_manager_->OnKeyEvent(event); + switch (button_id) { + case OK: + EXPECT_TRUE(test_dialog_->oked_); + EXPECT_FALSE(test_dialog_->canceled_); + EXPECT_FALSE(test_dialog_->last_pressed_button_); + break; + case CANCEL: + EXPECT_FALSE(test_dialog_->oked_); + EXPECT_TRUE(test_dialog_->canceled_); + EXPECT_FALSE(test_dialog_->last_pressed_button_); + break; + case BUTTON1: + EXPECT_FALSE(test_dialog_->oked_); + EXPECT_FALSE(test_dialog_->canceled_); + EXPECT_TRUE(test_dialog_->last_pressed_button_ == + test_dialog_->button1_); + break; + case BUTTON2: + EXPECT_FALSE(test_dialog_->oked_); + EXPECT_FALSE(test_dialog_->canceled_); + EXPECT_TRUE(test_dialog_->last_pressed_button_ == + test_dialog_->button2_); + break; + } + test_dialog_->ResetStates(); + } + + FocusManager* focus_manager_; + TestDialog* test_dialog_; + DialogClientView* client_view_; + NativeTextButton* ok_button_; + NativeTextButton* cancel_button_; +}; + +TEST_F(DefaultButtonTest, DialogDefaultButtonTest) { + // Window has just been shown, we expect the default button specified in the + // DialogDelegate. + EXPECT_TRUE(ok_button_->is_default()); + + // Simulate pressing enter, that should trigger the OK button. + SimulatePressingEnterAndCheckDefaultButton(OK); + + // Simulate focusing another button, it should become the default button. + client_view_->OnWillChangeFocus(ok_button_, test_dialog_->button1_); + EXPECT_FALSE(ok_button_->is_default()); + EXPECT_TRUE(test_dialog_->button1_->is_default()); + // Simulate pressing enter, that should trigger button1. + SimulatePressingEnterAndCheckDefaultButton(BUTTON1); + + // Now select something that is not a button, the OK should become the default + // button again. + client_view_->OnWillChangeFocus(test_dialog_->button1_, + test_dialog_->checkbox_); + EXPECT_TRUE(ok_button_->is_default()); + EXPECT_FALSE(test_dialog_->button1_->is_default()); + SimulatePressingEnterAndCheckDefaultButton(OK); + + // Select yet another button. + client_view_->OnWillChangeFocus(test_dialog_->checkbox_, + test_dialog_->button2_); + EXPECT_FALSE(ok_button_->is_default()); + EXPECT_FALSE(test_dialog_->button1_->is_default()); + EXPECT_TRUE(test_dialog_->button2_->is_default()); + SimulatePressingEnterAndCheckDefaultButton(BUTTON2); + + // Focus nothing. + client_view_->OnWillChangeFocus(test_dialog_->button2_, NULL); + EXPECT_TRUE(ok_button_->is_default()); + EXPECT_FALSE(test_dialog_->button1_->is_default()); + EXPECT_FALSE(test_dialog_->button2_->is_default()); + SimulatePressingEnterAndCheckDefaultButton(OK); + + // Focus the cancel button. + client_view_->OnWillChangeFocus(NULL, cancel_button_); + EXPECT_FALSE(ok_button_->is_default()); + EXPECT_TRUE(cancel_button_->is_default()); + EXPECT_FALSE(test_dialog_->button1_->is_default()); + EXPECT_FALSE(test_dialog_->button2_->is_default()); + SimulatePressingEnterAndCheckDefaultButton(CANCEL); +} + +class ButtonDropDownTest : public ViewTest { + public: + ButtonDropDownTest() + : test_dialog_(NULL), + button_as_view_(NULL) { + } + + virtual void SetUp() OVERRIDE { + ViewTest::SetUp(); + test_dialog_ = new TestDialog(&mock_menu_model_); + Widget* window = + Widget::CreateWindowWithBounds(test_dialog_, gfx::Rect(0, 0, 100, 100)); + test_dialog_->widget_ = window; + window->Show(); + test_dialog_->button_drop_->SetBounds(0, 0, 100, 100); + // We have to cast the button back into a View in order to invoke it's + // OnMouseReleased method. + button_as_view_ = static_cast<View*>(test_dialog_->button_drop_); + } + + virtual void TearDown() OVERRIDE { + test_dialog_->TearDown(); + ViewTest::TearDown(); + } + + TestDialog* test_dialog_; + MockMenuModel mock_menu_model_; + // This is owned by test_dialog_. + View* button_as_view_; + + private: + DISALLOW_COPY_AND_ASSIGN(ButtonDropDownTest); +}; + +// Ensure that regular clicks on the drop down button still work. (i.e. - the +// click events are processed and the listener gets the click) +TEST_F(ButtonDropDownTest, RegularClickTest) { + MouseEvent press_event(ui::ET_MOUSE_PRESSED, 1, 1, ui::EF_LEFT_BUTTON_DOWN); + MouseEvent release_event(ui::ET_MOUSE_RELEASED, 1, 1, + ui::EF_LEFT_BUTTON_DOWN); + button_as_view_->OnMousePressed(press_event); + button_as_view_->OnMouseReleased(release_event); + EXPECT_EQ(test_dialog_->last_pressed_button_, test_dialog_->button_drop_); +} + +//////////////////////////////////////////////////////////////////////////////// +// View hierarchy / Visibility changes +//////////////////////////////////////////////////////////////////////////////// +/* +TEST_F(ViewTest, ChangeVisibility) { +#if defined(OS_LINUX) + // Make CRITICAL messages fatal + // TODO(oshima): we probably should enable this for entire tests on linux. + g_log_set_always_fatal(G_LOG_LEVEL_CRITICAL); +#endif + scoped_ptr<Widget> window(CreateWidget()); + window->Init(NULL, gfx::Rect(0, 0, 500, 300)); + View* root_view = window->GetRootView(); + NativeTextButton* native = new NativeTextButton(NULL, ASCIIToUTF16("Native")); + + root_view->SetContentsView(native); + native->SetVisible(true); + + root_view->RemoveChildView(native); + native->SetVisible(false); + // Change visibility to true with no widget. + native->SetVisible(true); + + root_view->SetContentsView(native); + native->SetVisible(true); +} +*/ + +//////////////////////////////////////////////////////////////////////////////// +// Native view hierachy +//////////////////////////////////////////////////////////////////////////////// +class TestNativeViewHierarchy : public View { + public: + TestNativeViewHierarchy() { + } + + virtual void NativeViewHierarchyChanged(bool attached, + gfx::NativeView native_view, + internal::RootView* root_view) { + NotificationInfo info; + info.attached = attached; + info.native_view = native_view; + info.root_view = root_view; + notifications_.push_back(info); + }; + struct NotificationInfo { + bool attached; + gfx::NativeView native_view; + internal::RootView* root_view; + }; + static const size_t kTotalViews = 2; + std::vector<NotificationInfo> notifications_; +}; + +class TestChangeNativeViewHierarchy { + public: + explicit TestChangeNativeViewHierarchy(ViewTest *view_test) { + view_test_ = view_test; + native_host_ = new NativeViewHost(); + host_ = new Widget; + Widget::InitParams params(Widget::InitParams::TYPE_POPUP); + params.bounds = gfx::Rect(0, 0, 500, 300); + host_->Init(params); + host_->GetRootView()->AddChildView(native_host_); + for (size_t i = 0; i < TestNativeViewHierarchy::kTotalViews; ++i) { + windows_[i] = new Widget; + Widget::InitParams params(Widget::InitParams::TYPE_CONTROL); + params.parent = host_->GetNativeView(); + params.bounds = gfx::Rect(0, 0, 500, 300); + windows_[i]->Init(params); + root_views_[i] = windows_[i]->GetRootView(); + test_views_[i] = new TestNativeViewHierarchy; + root_views_[i]->AddChildView(test_views_[i]); + } + } + + ~TestChangeNativeViewHierarchy() { + for (size_t i = 0; i < TestNativeViewHierarchy::kTotalViews; ++i) { + windows_[i]->Close(); + } + host_->Close(); + // Will close and self-delete widgets - no need to manually delete them. + view_test_->RunPendingMessages(); + } + + void CheckEnumeratingNativeWidgets() { + if (!host_->GetTopLevelWidget()) + return; + Widget::Widgets widgets; + Widget::GetAllChildWidgets(host_->GetNativeView(), &widgets); + EXPECT_EQ(TestNativeViewHierarchy::kTotalViews + 1, widgets.size()); + // Unfortunately there is no guarantee the sequence of views here so always + // go through all of them. + for (Widget::Widgets::iterator i = widgets.begin(); + i != widgets.end(); ++i) { + View* root_view = (*i)->GetRootView(); + if (host_->GetRootView() == root_view) + continue; + size_t j; + for (j = 0; j < TestNativeViewHierarchy::kTotalViews; ++j) + if (root_views_[j] == root_view) + break; + // EXPECT_LT/GT/GE() fails to compile with class-defined constants + // with gcc, with error + // "error: undefined reference to 'TestNativeViewHierarchy::kTotalViews'" + // so I forced to use EXPECT_TRUE() instead. + EXPECT_TRUE(TestNativeViewHierarchy::kTotalViews > j); + } + } + + void CheckChangingHierarhy() { + size_t i; + for (i = 0; i < TestNativeViewHierarchy::kTotalViews; ++i) { + // TODO(georgey): use actual hierarchy changes to send notifications. + static_cast<internal::RootView*>(root_views_[i])-> + NotifyNativeViewHierarchyChanged(false, host_->GetNativeView()); + static_cast<internal::RootView*>(root_views_[i])-> + NotifyNativeViewHierarchyChanged(true, host_->GetNativeView()); + } + for (i = 0; i < TestNativeViewHierarchy::kTotalViews; ++i) { + ASSERT_EQ(static_cast<size_t>(2), test_views_[i]->notifications_.size()); + EXPECT_FALSE(test_views_[i]->notifications_[0].attached); + EXPECT_EQ(host_->GetNativeView(), + test_views_[i]->notifications_[0].native_view); + EXPECT_EQ(root_views_[i], test_views_[i]->notifications_[0].root_view); + EXPECT_TRUE(test_views_[i]->notifications_[1].attached); + EXPECT_EQ(host_->GetNativeView(), + test_views_[i]->notifications_[1].native_view); + EXPECT_EQ(root_views_[i], test_views_[i]->notifications_[1].root_view); + } + } + + NativeViewHost* native_host_; + Widget* host_; + Widget* windows_[TestNativeViewHierarchy::kTotalViews]; + View* root_views_[TestNativeViewHierarchy::kTotalViews]; + TestNativeViewHierarchy* test_views_[TestNativeViewHierarchy::kTotalViews]; + ViewTest* view_test_; +}; + +TEST_F(ViewTest, ChangeNativeViewHierarchyFindRoots) { + // TODO(georgey): Fix the test for Linux +#if defined(OS_WIN) + TestChangeNativeViewHierarchy test(this); + test.CheckEnumeratingNativeWidgets(); +#endif +} + +TEST_F(ViewTest, ChangeNativeViewHierarchyChangeHierarchy) { + // TODO(georgey): Fix the test for Linux +#if defined(OS_WIN) + TestChangeNativeViewHierarchy test(this); + test.CheckChangingHierarhy(); +#endif +} + +//////////////////////////////////////////////////////////////////////////////// +// Transformations +//////////////////////////////////////////////////////////////////////////////// + +class TransformPaintView : public TestView { + public: + TransformPaintView() {} + virtual ~TransformPaintView() {} + + void ClearScheduledPaintRect() { + scheduled_paint_rect_ = gfx::Rect(); + } + + gfx::Rect scheduled_paint_rect() const { return scheduled_paint_rect_; } + + // Overridden from View: + virtual void SchedulePaintInRect(const gfx::Rect& rect) { + gfx::Rect xrect = ConvertRectToParent(rect); + scheduled_paint_rect_ = scheduled_paint_rect_.Union(xrect); + } + + private: + gfx::Rect scheduled_paint_rect_; + + DISALLOW_COPY_AND_ASSIGN(TransformPaintView); +}; + +TEST_F(ViewTest, TransformPaint) { + TransformPaintView* v1 = new TransformPaintView(); + v1->SetBounds(0, 0, 500, 300); + + TestView* v2 = new TestView(); + v2->SetBounds(100, 100, 200, 100); + + Widget* widget = new Widget; + Widget::InitParams params(Widget::InitParams::TYPE_POPUP); + params.bounds = gfx::Rect(50, 50, 650, 650); + widget->Init(params); + widget->Show(); + View* root = widget->GetRootView(); + + root->AddChildView(v1); + v1->AddChildView(v2); + + // At this moment, |v2| occupies (100, 100) to (300, 200) in |root|. + v1->ClearScheduledPaintRect(); + v2->SchedulePaint(); + + EXPECT_EQ(gfx::Rect(100, 100, 200, 100), v1->scheduled_paint_rect()); + + // Rotate |v1| counter-clockwise. + ui::Transform transform; + RotateCounterclockwise(transform); + transform.SetTranslateY(500.0f); + v1->SetTransform(transform); + + // |v2| now occupies (100, 200) to (200, 400) in |root|. + + v1->ClearScheduledPaintRect(); + v2->SchedulePaint(); + + EXPECT_EQ(gfx::Rect(100, 200, 100, 200), v1->scheduled_paint_rect()); + + widget->CloseNow(); +} + +TEST_F(ViewTest, TransformEvent) { + TestView* v1 = new TestView(); + v1->SetBounds(0, 0, 500, 300); + + TestView* v2 = new TestView(); + v2->SetBounds(100, 100, 200, 100); + + Widget* widget = new Widget; + Widget::InitParams params(Widget::InitParams::TYPE_POPUP); + params.bounds = gfx::Rect(50, 50, 650, 650); + widget->Init(params); + View* root = widget->GetRootView(); + + root->AddChildView(v1); + v1->AddChildView(v2); + + // At this moment, |v2| occupies (100, 100) to (300, 200) in |root|. + + // Rotate |v1| counter-clockwise. + ui::Transform transform(v1->GetTransform()); + RotateCounterclockwise(transform); + transform.SetTranslateY(500.0f); + v1->SetTransform(transform); + + // |v2| now occupies (100, 200) to (200, 400) in |root|. + v1->Reset(); + v2->Reset(); + + MouseEvent pressed(ui::ET_MOUSE_PRESSED, + 110, 210, + ui::EF_LEFT_BUTTON_DOWN); + root->OnMousePressed(pressed); + EXPECT_EQ(0, v1->last_mouse_event_type_); + EXPECT_EQ(ui::ET_MOUSE_PRESSED, v2->last_mouse_event_type_); + EXPECT_EQ(190, v2->location_.x()); + EXPECT_EQ(10, v2->location_.y()); + + MouseEvent released(ui::ET_MOUSE_RELEASED, 0, 0, 0); + root->OnMouseReleased(released); + + // Now rotate |v2| inside |v1| clockwise. + transform = v2->GetTransform(); + RotateClockwise(transform); + transform.SetTranslateX(100.0f); + v2->SetTransform(transform); + + // Now, |v2| occupies (100, 100) to (200, 300) in |v1|, and (100, 300) to + // (300, 400) in |root|. + + v1->Reset(); + v2->Reset(); + + MouseEvent p2(ui::ET_MOUSE_PRESSED, + 110, 320, + ui::EF_LEFT_BUTTON_DOWN); + root->OnMousePressed(p2); + EXPECT_EQ(0, v1->last_mouse_event_type_); + EXPECT_EQ(ui::ET_MOUSE_PRESSED, v2->last_mouse_event_type_); + EXPECT_EQ(10, v2->location_.x()); + EXPECT_EQ(20, v2->location_.y()); + + root->OnMouseReleased(released); + + v1->SetTransform(ui::Transform()); + v2->SetTransform(ui::Transform()); + + TestView* v3 = new TestView(); + v3->SetBounds(10, 10, 20, 30); + v2->AddChildView(v3); + + // Rotate |v3| clockwise with respect to |v2|. + transform = v1->GetTransform(); + RotateClockwise(transform); + transform.SetTranslateX(30.0f); + v3->SetTransform(transform); + + // Scale |v2| with respect to |v1| along both axis. + transform = v2->GetTransform(); + transform.SetScale(0.8f, 0.5f); + v2->SetTransform(transform); + + // |v3| occupies (108, 105) to (132, 115) in |root|. + + v1->Reset(); + v2->Reset(); + v3->Reset(); + + MouseEvent p3(ui::ET_MOUSE_PRESSED, + 112, 110, + ui::EF_LEFT_BUTTON_DOWN); + root->OnMousePressed(p3); + + EXPECT_EQ(ui::ET_MOUSE_PRESSED, v3->last_mouse_event_type_); + EXPECT_EQ(10, v3->location_.x()); + EXPECT_EQ(25, v3->location_.y()); + + root->OnMouseReleased(released); + + v1->SetTransform(ui::Transform()); + v2->SetTransform(ui::Transform()); + v3->SetTransform(ui::Transform()); + + v1->Reset(); + v2->Reset(); + v3->Reset(); + + // Rotate |v3| clockwise with respect to |v2|, and scale it along both axis. + transform = v3->GetTransform(); + RotateClockwise(transform); + transform.SetTranslateX(30.0f); + // Rotation sets some scaling transformation. Using SetScale would overwrite + // that and pollute the rotation. So combine the scaling with the existing + // transforamtion. + transform.ConcatScale(0.8f, 0.5f); + v3->SetTransform(transform); + + // Translate |v2| with respect to |v1|. + transform = v2->GetTransform(); + transform.SetTranslate(10, 10); + v2->SetTransform(transform); + + // |v3| now occupies (120, 120) to (144, 130) in |root|. + + MouseEvent p4(ui::ET_MOUSE_PRESSED, + 124, 125, + ui::EF_LEFT_BUTTON_DOWN); + root->OnMousePressed(p4); + + EXPECT_EQ(ui::ET_MOUSE_PRESSED, v3->last_mouse_event_type_); + EXPECT_EQ(10, v3->location_.x()); + EXPECT_EQ(25, v3->location_.y()); + + root->OnMouseReleased(released); + + widget->CloseNow(); +} + +TEST_F(ViewTest, TransformVisibleBound) { + gfx::Rect viewport_bounds(0, 0, 100, 100); + + scoped_ptr<Widget> widget(new Widget); + Widget::InitParams params(Widget::InitParams::TYPE_POPUP); + params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + params.bounds = viewport_bounds; + widget->Init(params); + widget->GetRootView()->SetBoundsRect(viewport_bounds); + + View* viewport = new View; + widget->SetContentsView(viewport); + View* contents = new View; + viewport->AddChildView(contents); + viewport->SetBoundsRect(viewport_bounds); + contents->SetBounds(0, 0, 100, 200); + + View* child = new View; + contents->AddChildView(child); + child->SetBounds(10, 90, 50, 50); + EXPECT_EQ(gfx::Rect(0, 0, 50, 10), child->GetVisibleBounds()); + + // Rotate |child| counter-clockwise + ui::Transform transform; + RotateCounterclockwise(transform); + transform.SetTranslateY(50.0f); + child->SetTransform(transform); + EXPECT_EQ(gfx::Rect(40, 0, 10, 50), child->GetVisibleBounds()); + + widget->CloseNow(); +} + +//////////////////////////////////////////////////////////////////////////////// +// OnVisibleBoundsChanged() + +class VisibleBoundsView : public View { + public: + VisibleBoundsView() : received_notification_(false) {} + virtual ~VisibleBoundsView() {} + + bool received_notification() const { return received_notification_; } + void set_received_notification(bool received) { + received_notification_ = received; + } + + private: + // Overridden from View: + virtual bool NeedsNotificationWhenVisibleBoundsChange() const { + return true; + } + virtual void OnVisibleBoundsChanged() { + received_notification_ = true; + } + + bool received_notification_; + + DISALLOW_COPY_AND_ASSIGN(VisibleBoundsView); +}; + +TEST_F(ViewTest, OnVisibleBoundsChanged) { + gfx::Rect viewport_bounds(0, 0, 100, 100); + + scoped_ptr<Widget> widget(new Widget); + Widget::InitParams params(Widget::InitParams::TYPE_POPUP); + params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + params.bounds = viewport_bounds; + widget->Init(params); + widget->GetRootView()->SetBoundsRect(viewport_bounds); + + View* viewport = new View; + widget->SetContentsView(viewport); + View* contents = new View; + viewport->AddChildView(contents); + viewport->SetBoundsRect(viewport_bounds); + contents->SetBounds(0, 0, 100, 200); + + // Create a view that cares about visible bounds notifications, and position + // it just outside the visible bounds of the viewport. + VisibleBoundsView* child = new VisibleBoundsView; + contents->AddChildView(child); + child->SetBounds(10, 110, 50, 50); + + // The child bound should be fully clipped. + EXPECT_TRUE(child->GetVisibleBounds().IsEmpty()); + + // Now scroll the contents, but not enough to make the child visible. + contents->SetY(contents->y() - 1); + + // We should have received the notification since the visible bounds may have + // changed (even though they didn't). + EXPECT_TRUE(child->received_notification()); + EXPECT_TRUE(child->GetVisibleBounds().IsEmpty()); + child->set_received_notification(false); + + // Now scroll the contents, this time by enough to make the child visible by + // one pixel. + contents->SetY(contents->y() - 10); + EXPECT_TRUE(child->received_notification()); + EXPECT_EQ(1, child->GetVisibleBounds().height()); + child->set_received_notification(false); + + widget->CloseNow(); +} + +//////////////////////////////////////////////////////////////////////////////// +// BoundsChanged() + +TEST_F(ViewTest, SetBoundsPaint) { + TestView top_view; + TestView* child_view = new TestView; + + top_view.SetBounds(0, 0, 100, 100); + top_view.scheduled_paint_rects_.clear(); + child_view->SetBounds(10, 10, 20, 20); + top_view.AddChildView(child_view); + + top_view.scheduled_paint_rects_.clear(); + child_view->SetBounds(30, 30, 20, 20); + EXPECT_EQ(2U, top_view.scheduled_paint_rects_.size()); + + // There should be 2 rects, spanning from (10, 10) to (50, 50). + gfx::Rect paint_rect = + top_view.scheduled_paint_rects_[0].Union( + top_view.scheduled_paint_rects_[1]); + EXPECT_EQ(gfx::Rect(10, 10, 40, 40), paint_rect); +} + +// Tests conversion methods with a transform. +TEST_F(ViewTest, ConvertPointToViewWithTransform) { + TestView top_view; + TestView* child = new TestView; + TestView* child_child = new TestView; + + top_view.AddChildView(child); + child->AddChildView(child_child); + + top_view.SetBounds(0, 0, 1000, 1000); + + child->SetBounds(7, 19, 500, 500); + ui::Transform transform; + transform.SetScale(3.0f, 4.0f); + child->SetTransform(transform); + + child_child->SetBounds(17, 13, 100, 100); + transform = ui::Transform(); + transform.SetScale(5.0f, 7.0f); + child_child->SetTransform(transform); + + // Sanity check to make sure basic transforms act as expected. + { + ui::Transform transform; + transform.ConcatTranslate(1, 1); + transform.ConcatScale(100, 55); + transform.ConcatTranslate(110, -110); + + // convert to a 3x3 matrix. + const SkMatrix& matrix = transform.matrix(); + + EXPECT_EQ(210, matrix.getTranslateX()); + EXPECT_EQ(-55, matrix.getTranslateY()); + EXPECT_EQ(100, matrix.getScaleX()); + EXPECT_EQ(55, matrix.getScaleY()); + EXPECT_EQ(0, matrix.getSkewX()); + EXPECT_EQ(0, matrix.getSkewY()); + } + + { + ui::Transform transform; + transform.SetTranslate(1, 1); + ui::Transform t2; + t2.SetScale(100, 55); + ui::Transform t3; + t3.SetTranslate(110, -110); + transform.ConcatTransform(t2); + transform.ConcatTransform(t3); + + // convert to a 3x3 matrix + const SkMatrix& matrix = transform.matrix(); + + EXPECT_EQ(210, matrix.getTranslateX()); + EXPECT_EQ(-55, matrix.getTranslateY()); + EXPECT_EQ(100, matrix.getScaleX()); + EXPECT_EQ(55, matrix.getScaleY()); + EXPECT_EQ(0, matrix.getSkewX()); + EXPECT_EQ(0, matrix.getSkewY()); + } + + // Conversions from child->top and top->child. + { + gfx::Point point(5, 5); + View::ConvertPointToView(child, &top_view, &point); + EXPECT_EQ(22, point.x()); + EXPECT_EQ(39, point.y()); + + point.SetPoint(22, 39); + View::ConvertPointToView(&top_view, child, &point); + EXPECT_EQ(5, point.x()); + EXPECT_EQ(5, point.y()); + } + + // Conversions from child_child->top and top->child_child. + { + gfx::Point point(5, 5); + View::ConvertPointToView(child_child, &top_view, &point); + EXPECT_EQ(133, point.x()); + EXPECT_EQ(211, point.y()); + + point.SetPoint(133, 211); + View::ConvertPointToView(&top_view, child_child, &point); + EXPECT_EQ(5, point.x()); + EXPECT_EQ(5, point.y()); + } + + // Conversions from child_child->child and child->child_child + { + gfx::Point point(5, 5); + View::ConvertPointToView(child_child, child, &point); + EXPECT_EQ(42, point.x()); + EXPECT_EQ(48, point.y()); + + point.SetPoint(42, 48); + View::ConvertPointToView(child, child_child, &point); + EXPECT_EQ(5, point.x()); + EXPECT_EQ(5, point.y()); + } + + // Conversions from top_view to child with a value that should be negative. + // This ensures we don't round up with negative numbers. + { + gfx::Point point(6, 18); + View::ConvertPointToView(&top_view, child, &point); + EXPECT_EQ(-1, point.x()); + EXPECT_EQ(-1, point.y()); + } +} + +// Tests conversion methods for rectangles. +TEST_F(ViewTest, ConvertRectWithTransform) { + scoped_ptr<Widget> widget(new Widget); + Widget::InitParams params(Widget::InitParams::TYPE_POPUP); + params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + params.bounds = gfx::Rect(50, 50, 650, 650); + widget->Init(params); + View* root = widget->GetRootView(); + + TestView* v1 = new TestView; + TestView* v2 = new TestView; + root->AddChildView(v1); + v1->AddChildView(v2); + + v1->SetBounds(10, 10, 500, 500); + v2->SetBounds(20, 20, 100, 200); + + // |v2| now occupies (30, 30) to (130, 230) in |widget| + gfx::Rect rect(5, 5, 15, 40); + EXPECT_EQ(gfx::Rect(25, 25, 15, 40), v2->ConvertRectToParent(rect)); + EXPECT_EQ(gfx::Rect(35, 35, 15, 40), v2->ConvertRectToWidget(rect)); + + // Rotate |v2| + ui::Transform t2; + RotateCounterclockwise(t2); + t2.SetTranslateY(100.0f); + v2->SetTransform(t2); + + // |v2| now occupies (30, 30) to (230, 130) in |widget| + EXPECT_EQ(gfx::Rect(25, 100, 40, 15), v2->ConvertRectToParent(rect)); + EXPECT_EQ(gfx::Rect(35, 110, 40, 15), v2->ConvertRectToWidget(rect)); + + // Scale down |v1| + ui::Transform t1; + t1.SetScale(0.5, 0.5); + v1->SetTransform(t1); + + // The rectangle should remain the same for |v1|. + EXPECT_EQ(gfx::Rect(25, 100, 40, 15), v2->ConvertRectToParent(rect)); + + // |v2| now occupies (20, 20) to (120, 70) in |widget| + // There are some rounding of floating values here. These values may change if + // floating operations are improved/changed. + EXPECT_EQ(gfx::Rect(22, 60, 20, 7), v2->ConvertRectToWidget(rect)); + + widget->CloseNow(); +} + +class ObserverView : public View { + public: + ObserverView(); + virtual ~ObserverView(); + + void ResetTestState(); + + bool child_added() const { return child_added_; } + bool child_removed() const { return child_removed_; } + const View* parent_view() const { return parent_view_; } + const View* child_view() const { return child_view_; } + + private: + // View: + virtual void ViewHierarchyChanged(bool is_add, + View* parent, + View* child) OVERRIDE; + + bool child_added_; + bool child_removed_; + View* parent_view_; + View* child_view_; + + DISALLOW_COPY_AND_ASSIGN(ObserverView); +}; + +ObserverView::ObserverView() + : child_added_(false), + child_removed_(false), + parent_view_(NULL), + child_view_(NULL) { +} + +ObserverView::~ObserverView() {} + +void ObserverView::ResetTestState() { + child_added_ = false; + child_removed_ = false; + parent_view_ = NULL; + child_view_ = NULL; +} + +void ObserverView::ViewHierarchyChanged(bool is_add, + View* parent, + View* child) { + if (is_add) + child_added_ = true; + else + child_removed_ = true; + + parent_view_ = parent; + child_view_ = child; +} + +// Verifies that the ViewHierarchyChanged() notification is sent correctly when +// a child view is added or removed to all the views in the hierarchy (up and +// down). +// The tree looks like this: +// v1 +// +-- v2 +// +-- v3 +TEST_F(ViewTest, ViewHierarchyChanged) { + ObserverView v1; + + ObserverView* v3 = new ObserverView(); + + // Add |v3| to |v2|. + scoped_ptr<ObserverView> v2(new ObserverView()); + v2->AddChildView(v3); + + // Make sure both |v2| and |v3| receive the ViewHierarchyChanged() + // notification. + EXPECT_TRUE(v2->child_added()); + EXPECT_FALSE(v2->child_removed()); + EXPECT_EQ(v2.get(), v2->parent_view()); + EXPECT_EQ(v3, v2->child_view()); + + EXPECT_TRUE(v3->child_added()); + EXPECT_FALSE(v3->child_removed()); + EXPECT_EQ(v2.get(), v3->parent_view()); + EXPECT_EQ(v3, v3->child_view()); + + // Reset everything to the initial state. + v2->ResetTestState(); + v3->ResetTestState(); + + // Add |v2| to v1. + v1.AddChildView(v2.get()); + + // Verifies that |v2| is the child view *added* and the parent view is |v1|. + // Make sure all the views (v1, v2, v3) received _that_ information. + EXPECT_TRUE(v1.child_added()); + EXPECT_FALSE(v1.child_removed()); + EXPECT_EQ(&v1, v1.parent_view()); + EXPECT_EQ(v2.get(), v1.child_view()); + + EXPECT_TRUE(v2->child_added()); + EXPECT_FALSE(v2->child_removed()); + EXPECT_EQ(&v1, v2->parent_view()); + EXPECT_EQ(v2.get(), v2->child_view()); + + EXPECT_TRUE(v3->child_added()); + EXPECT_FALSE(v3->child_removed()); + EXPECT_EQ(&v1, v3->parent_view()); + EXPECT_EQ(v2.get(), v3->child_view()); + + // Reset everything to the initial state. + v1.ResetTestState(); + v2->ResetTestState(); + v3->ResetTestState(); + + // Remove |v2| from |v1|. + v1.RemoveChildView(v2.get()); + + // Verifies that |v2| is the child view *removed* and the parent view is |v1|. + // Make sure all the views (v1, v2, v3) received _that_ information. + EXPECT_FALSE(v1.child_added()); + EXPECT_TRUE(v1.child_removed()); + EXPECT_EQ(&v1, v1.parent_view()); + EXPECT_EQ(v2.get(), v1.child_view()); + + EXPECT_FALSE(v2->child_added()); + EXPECT_TRUE(v2->child_removed()); + EXPECT_EQ(&v1, v2->parent_view()); + EXPECT_EQ(v2.get(), v2->child_view()); + + EXPECT_FALSE(v3->child_added()); + EXPECT_TRUE(v3->child_removed()); + EXPECT_EQ(&v1, v3->parent_view()); + EXPECT_EQ(v3, v3->child_view()); +} + +// Verifies if the child views added under the root are all deleted when calling +// RemoveAllChildViews. +// The tree looks like this: +// root +// +-- child1 +// +-- foo +// +-- bar0 +// +-- bar1 +// +-- bar2 +// +-- child2 +// +-- child3 +TEST_F(ViewTest, RemoveAllChildViews) { + View root; + + View* child1 = new View; + root.AddChildView(child1); + + for (int i = 0; i < 2; ++i) + root.AddChildView(new View); + + View* foo = new View; + child1->AddChildView(foo); + + // Add some nodes to |foo|. + for (int i = 0; i < 3; ++i) + foo->AddChildView(new View); + + EXPECT_EQ(3, root.child_count()); + EXPECT_EQ(1, child1->child_count()); + EXPECT_EQ(3, foo->child_count()); + + // Now remove all child views from root. + root.RemoveAllChildViews(true); + + EXPECT_EQ(0, root.child_count()); + EXPECT_FALSE(root.has_children()); +} + +TEST_F(ViewTest, Contains) { + View v1; + View* v2 = new View; + View* v3 = new View; + + v1.AddChildView(v2); + v2->AddChildView(v3); + + EXPECT_FALSE(v1.Contains(NULL)); + EXPECT_TRUE(v1.Contains(&v1)); + EXPECT_TRUE(v1.Contains(v2)); + EXPECT_TRUE(v1.Contains(v3)); + + EXPECT_FALSE(v2->Contains(NULL)); + EXPECT_TRUE(v2->Contains(v2)); + EXPECT_FALSE(v2->Contains(&v1)); + EXPECT_TRUE(v2->Contains(v3)); + + EXPECT_FALSE(v3->Contains(NULL)); + EXPECT_TRUE(v3->Contains(v3)); + EXPECT_FALSE(v3->Contains(&v1)); + EXPECT_FALSE(v3->Contains(v2)); +} + +// Verifies if GetIndexOf() returns the correct index for the specified child +// view. +// The tree looks like this: +// root +// +-- child1 +// +-- foo1 +// +-- child2 +TEST_F(ViewTest, GetIndexOf) { + View root; + + View* child1 = new View; + root.AddChildView(child1); + + View* child2 = new View; + root.AddChildView(child2); + + View* foo1 = new View; + child1->AddChildView(foo1); + + EXPECT_EQ(-1, root.GetIndexOf(NULL)); + EXPECT_EQ(-1, root.GetIndexOf(&root)); + EXPECT_EQ(0, root.GetIndexOf(child1)); + EXPECT_EQ(1, root.GetIndexOf(child2)); + EXPECT_EQ(-1, root.GetIndexOf(foo1)); + + EXPECT_EQ(-1, child1->GetIndexOf(NULL)); + EXPECT_EQ(-1, child1->GetIndexOf(&root)); + EXPECT_EQ(-1, child1->GetIndexOf(child1)); + EXPECT_EQ(-1, child1->GetIndexOf(child2)); + EXPECT_EQ(0, child1->GetIndexOf(foo1)); + + EXPECT_EQ(-1, child2->GetIndexOf(NULL)); + EXPECT_EQ(-1, child2->GetIndexOf(&root)); + EXPECT_EQ(-1, child2->GetIndexOf(child2)); + EXPECT_EQ(-1, child2->GetIndexOf(child1)); + EXPECT_EQ(-1, child2->GetIndexOf(foo1)); +} + +// Verifies that the child views can be reordered correctly. +TEST_F(ViewTest, ReorderChildren) { + View root; + + View* child = new View(); + root.AddChildView(child); + + View* foo1 = new View(); + child->AddChildView(foo1); + View* foo2 = new View(); + child->AddChildView(foo2); + View* foo3 = new View(); + child->AddChildView(foo3); + foo1->set_focusable(true); + foo2->set_focusable(true); + foo3->set_focusable(true); + + ASSERT_EQ(0, child->GetIndexOf(foo1)); + ASSERT_EQ(1, child->GetIndexOf(foo2)); + ASSERT_EQ(2, child->GetIndexOf(foo3)); + ASSERT_EQ(foo2, foo1->GetNextFocusableView()); + ASSERT_EQ(foo3, foo2->GetNextFocusableView()); + ASSERT_EQ(NULL, foo3->GetNextFocusableView()); + + // Move |foo2| at the end. + child->ReorderChildView(foo2, -1); + ASSERT_EQ(0, child->GetIndexOf(foo1)); + ASSERT_EQ(1, child->GetIndexOf(foo3)); + ASSERT_EQ(2, child->GetIndexOf(foo2)); + ASSERT_EQ(foo3, foo1->GetNextFocusableView()); + ASSERT_EQ(foo2, foo3->GetNextFocusableView()); + ASSERT_EQ(NULL, foo2->GetNextFocusableView()); + + // Move |foo1| at the end. + child->ReorderChildView(foo1, -1); + ASSERT_EQ(0, child->GetIndexOf(foo3)); + ASSERT_EQ(1, child->GetIndexOf(foo2)); + ASSERT_EQ(2, child->GetIndexOf(foo1)); + ASSERT_EQ(NULL, foo1->GetNextFocusableView()); + ASSERT_EQ(foo2, foo1->GetPreviousFocusableView()); + ASSERT_EQ(foo2, foo3->GetNextFocusableView()); + ASSERT_EQ(foo1, foo2->GetNextFocusableView()); + + // Move |foo2| to the front. + child->ReorderChildView(foo2, 0); + ASSERT_EQ(0, child->GetIndexOf(foo2)); + ASSERT_EQ(1, child->GetIndexOf(foo3)); + ASSERT_EQ(2, child->GetIndexOf(foo1)); + ASSERT_EQ(NULL, foo1->GetNextFocusableView()); + ASSERT_EQ(foo3, foo1->GetPreviousFocusableView()); + ASSERT_EQ(foo3, foo2->GetNextFocusableView()); + ASSERT_EQ(foo1, foo3->GetNextFocusableView()); +} + +// Verifies that GetViewByID returns the correctly child view from the specified +// ID. +// The tree looks like this: +// v1 +// +-- v2 +// +-- v3 +// +-- v4 +TEST_F(ViewTest, GetViewByID) { + View v1; + const int kV1ID = 1; + v1.set_id(kV1ID); + + View v2; + const int kV2ID = 2; + v2.set_id(kV2ID); + + View v3; + const int kV3ID = 3; + v3.set_id(kV3ID); + + View v4; + const int kV4ID = 4; + v4.set_id(kV4ID); + + const int kV5ID = 5; + + v1.AddChildView(&v2); + v2.AddChildView(&v3); + v2.AddChildView(&v4); + + EXPECT_EQ(&v1, v1.GetViewByID(kV1ID)); + EXPECT_EQ(&v2, v1.GetViewByID(kV2ID)); + EXPECT_EQ(&v4, v1.GetViewByID(kV4ID)); + + EXPECT_EQ(NULL, v1.GetViewByID(kV5ID)); // No V5 exists. + EXPECT_EQ(NULL, v2.GetViewByID(kV1ID)); // It can get only from child views. + + const int kGroup = 1; + v3.SetGroup(kGroup); + v4.SetGroup(kGroup); + + View::Views views; + v1.GetViewsInGroup(kGroup, &views); + EXPECT_EQ(2U, views.size()); + + View::Views::const_iterator i(std::find(views.begin(), views.end(), &v3)); + EXPECT_NE(views.end(), i); + + i = std::find(views.begin(), views.end(), &v4); + EXPECT_NE(views.end(), i); +} + +//////////////////////////////////////////////////////////////////////////////// +// Layers +//////////////////////////////////////////////////////////////////////////////// + +#if defined(VIEWS_COMPOSITOR) + +namespace { + +// Test implementation of LayerAnimator. +class TestLayerAnimator : public ui::LayerAnimator { + public: + TestLayerAnimator(); + + const gfx::Rect& last_bounds() const { return last_bounds_; } + + // LayerAnimator. + virtual void SetBounds(const gfx::Rect& bounds) OVERRIDE; + + private: + gfx::Rect last_bounds_; + + DISALLOW_COPY_AND_ASSIGN(TestLayerAnimator); +}; + +TestLayerAnimator::TestLayerAnimator() + : ui::LayerAnimator(base::TimeDelta::FromMilliseconds(0)) { +} + +void TestLayerAnimator::SetBounds(const gfx::Rect& bounds) { + last_bounds_ = bounds; +} + +} // namespace + +class ViewLayerTest : public ViewsTestBase { + public: + ViewLayerTest() : widget_(NULL), old_use_acceleration_(false) {} + + virtual ~ViewLayerTest() { + } + + // Returns the Layer used by the RootView. + ui::Layer* GetRootLayer() { +#if defined(USE_AURA) + ui::Layer* root_layer = NULL; + gfx::Point origin; + widget()->CalculateOffsetToAncestorWithLayer(&origin, &root_layer); + return root_layer; +#else + return widget()->GetRootView()->layer(); +#endif + } + + virtual void SetUp() OVERRIDE { + ViewTest::SetUp(); + old_use_acceleration_ = View::get_use_acceleration_when_possible(); + View::set_use_acceleration_when_possible(true); + + ui::TestTexture::reset_live_count(); + + widget_ = new Widget; + Widget::InitParams params(Widget::InitParams::TYPE_POPUP); + params.bounds = gfx::Rect(50, 50, 200, 200); + widget_->Init(params); + widget_->Show(); + widget_->GetRootView()->SetBounds(0, 0, 200, 200); + } + + virtual void TearDown() OVERRIDE { + View::set_use_acceleration_when_possible(old_use_acceleration_); + widget_->CloseNow(); + Widget::SetPureViews(false); + ViewsTestBase::TearDown(); + } + + Widget* widget() { return widget_; } + + private: + Widget* widget_; + bool old_use_acceleration_; +}; + +#if !defined(USE_AURA) +// This test assumes a particular layer hierarchy that isn't valid for aura. +// Ensures the RootView has a layer and its set up correctly. +TEST_F(ViewLayerTest, RootState) { + ui::Layer* layer = widget()->GetRootView()->layer(); + ASSERT_TRUE(layer); + EXPECT_FALSE(layer->parent()); + EXPECT_EQ(0u, layer->children().size()); + EXPECT_FALSE(layer->transform().HasChange()); + EXPECT_EQ(widget()->GetRootView()->bounds(), layer->bounds()); + EXPECT_TRUE(layer->GetCompositor() != NULL); +} + +// Verifies that the complete bounds of a texture are updated if the texture +// needs to be refreshed and paint with a clip is invoked. +// This test invokes OnNativeWidgetPaintAccelerated, which is not used by aura. +TEST_F(ViewLayerTest, PaintAll) { + View* view = widget()->GetRootView(); + ui::Layer* layer = GetRootLayer(); + view->SetBounds(0, 0, 200, 200); + widget()->OnNativeWidgetPaintAccelerated(gfx::Rect(0, 0, 1, 1)); + ASSERT_TRUE(layer != NULL); + const ui::TestTexture* texture = + static_cast<const ui::TestTexture*>(layer->texture()); + ASSERT_TRUE(texture != NULL); + EXPECT_EQ(view->GetLocalBounds(), texture->bounds_of_last_paint()); +} +#endif + +TEST_F(ViewLayerTest, LayerToggling) { + // Because we lazily create textures the calls to DrawTree are necessary to + // ensure we trigger creation of textures. +#if defined(USE_AURA) + ui::Layer* root_layer = NULL; + gfx::Point origin; + widget()->CalculateOffsetToAncestorWithLayer(&origin, &root_layer); +#else + ui::Layer* root_layer = widget()->GetRootView()->layer(); +#endif + View* content_view = new View; + widget()->SetContentsView(content_view); + +#if !defined(USE_WEBKIT_COMPOSITOR) + // TODO(piman): with the webkit compositor, we don't create Textures on + // Layers. We're not supposed to be calling Layer::DrawTree. This test needs + // refactoring to fully work in that case. + root_layer->DrawTree(); + ui::TestTexture::reset_live_count(); +#endif + + // Create v1, give it a bounds and verify everything is set up correctly. + View* v1 = new View; + v1->SetPaintToLayer(true); +#if !defined(USE_WEBKIT_COMPOSITOR) + root_layer->DrawTree(); + EXPECT_EQ(0, ui::TestTexture::live_count()); +#endif + EXPECT_TRUE(v1->layer() != NULL); + v1->SetBounds(20, 30, 140, 150); + content_view->AddChildView(v1); +#if !defined(USE_WEBKIT_COMPOSITOR) + root_layer->DrawTree(); + EXPECT_EQ(1, ui::TestTexture::live_count()); +#endif + ASSERT_TRUE(v1->layer() != NULL); + EXPECT_EQ(root_layer, v1->layer()->parent()); + EXPECT_EQ(gfx::Rect(20, 30, 140, 150), v1->layer()->bounds()); + + // Create v2 as a child of v1 and do basic assertion testing. + View* v2 = new View; + v1->AddChildView(v2); + EXPECT_TRUE(v2->layer() == NULL); + v2->SetBounds(10, 20, 30, 40); + v2->SetPaintToLayer(true); +#if !defined(USE_WEBKIT_COMPOSITOR) + root_layer->DrawTree(); + EXPECT_EQ(2, ui::TestTexture::live_count()); +#endif + ASSERT_TRUE(v2->layer() != NULL); + EXPECT_EQ(v1->layer(), v2->layer()->parent()); + EXPECT_EQ(gfx::Rect(10, 20, 30, 40), v2->layer()->bounds()); + + // Turn off v1s layer. v2 should still have a layer but its parent should have + // changed. + v1->SetPaintToLayer(false); +#if !defined(USE_WEBKIT_COMPOSITOR) + root_layer->DrawTree(); + EXPECT_EQ(1, ui::TestTexture::live_count()); +#endif + EXPECT_TRUE(v1->layer() == NULL); + EXPECT_TRUE(v2->layer() != NULL); + EXPECT_EQ(root_layer, v2->layer()->parent()); + ASSERT_EQ(1u, root_layer->children().size()); + EXPECT_EQ(root_layer->children()[0], v2->layer()); + // The bounds of the layer should have changed to be relative to the root view + // now. + EXPECT_EQ(gfx::Rect(30, 50, 30, 40), v2->layer()->bounds()); + + // Make v1 have a layer again and verify v2s layer is wired up correctly. + ui::Transform transform; + transform.SetScale(2.0f, 2.0f); + v1->SetTransform(transform); +#if !defined(USE_WEBKIT_COMPOSITOR) + root_layer->DrawTree(); + EXPECT_EQ(2, ui::TestTexture::live_count()); +#endif + EXPECT_TRUE(v1->layer() != NULL); + EXPECT_TRUE(v2->layer() != NULL); + EXPECT_EQ(root_layer, v1->layer()->parent()); + EXPECT_EQ(v1->layer(), v2->layer()->parent()); + ASSERT_EQ(1u, root_layer->children().size()); + EXPECT_EQ(root_layer->children()[0], v1->layer()); + ASSERT_EQ(1u, v1->layer()->children().size()); + EXPECT_EQ(v1->layer()->children()[0], v2->layer()); + EXPECT_EQ(gfx::Rect(10, 20, 30, 40), v2->layer()->bounds()); +} + +// Verifies turning on a layer wires up children correctly. +TEST_F(ViewLayerTest, NestedLayerToggling) { + View* content_view = new View; + widget()->SetContentsView(content_view); + + // Create v1, give it a bounds and verify everything is set up correctly. + View* v1 = new View; + content_view->AddChildView(v1); + v1->SetBounds(20, 30, 140, 150); + + View* v2 = new View; + v1->AddChildView(v2); + + View* v3 = new View; + v3->SetPaintToLayer(true); + v2->AddChildView(v3); + ASSERT_TRUE(v3->layer() != NULL); + + // At this point we have v1-v2-v3. v3 has a layer, v1 and v2 don't. + + v1->SetPaintToLayer(true); + EXPECT_EQ(v1->layer(), v3->layer()->parent()); +} + +TEST_F(ViewLayerTest, LayerAnimator) { + View* content_view = new View; + widget()->SetContentsView(content_view); + + View* v1 = new View; + content_view->AddChildView(v1); + v1->SetPaintToLayer(true); + EXPECT_TRUE(v1->layer() != NULL); + + TestLayerAnimator* animator = new TestLayerAnimator(); + v1->layer()->SetAnimator(animator); + + gfx::Rect bounds(1, 2, 3, 4); + v1->SetBoundsRect(bounds); + EXPECT_EQ(bounds, animator->last_bounds()); + // TestLayerAnimator doesn't update the layer. + EXPECT_NE(bounds, v1->layer()->bounds()); +} + +// Verifies the bounds of a layer are updated if the bounds of ancestor that +// doesn't have a layer change. +TEST_F(ViewLayerTest, BoundsChangeWithLayer) { + View* content_view = new View; + widget()->SetContentsView(content_view); + + View* v1 = new View; + content_view->AddChildView(v1); + v1->SetBounds(20, 30, 140, 150); + + View* v2 = new View; + v2->SetBounds(10, 11, 40, 50); + v1->AddChildView(v2); + v2->SetPaintToLayer(true); + ASSERT_TRUE(v2->layer() != NULL); + EXPECT_EQ(gfx::Rect(30, 41, 40, 50), v2->layer()->bounds()); + + v1->SetPosition(gfx::Point(25, 36)); + EXPECT_EQ(gfx::Rect(35, 47, 40, 50), v2->layer()->bounds()); + + v2->SetPosition(gfx::Point(11, 12)); + EXPECT_EQ(gfx::Rect(36, 48, 40, 50), v2->layer()->bounds()); + + // Bounds of the layer should change even if the view is not invisible. + v1->SetVisible(false); + v1->SetPosition(gfx::Point(20, 30)); + EXPECT_EQ(gfx::Rect(31, 42, 40, 50), v2->layer()->bounds()); + + v2->SetVisible(false); + v2->SetBounds(10, 11, 20, 30); + EXPECT_EQ(gfx::Rect(30, 41, 20, 30), v2->layer()->bounds()); +} + +// Makes sure a transform persists after toggling the visibility. +TEST_F(ViewLayerTest, ToggleVisibilityWithTransform) { + View* view = new View; + ui::Transform transform; + transform.SetScale(2.0f, 2.0f); + view->SetTransform(transform); + widget()->SetContentsView(view); + EXPECT_EQ(2.0f, view->GetTransform().matrix().get(0, 0)); + + view->SetVisible(false); + EXPECT_EQ(2.0f, view->GetTransform().matrix().get(0, 0)); + + view->SetVisible(true); + EXPECT_EQ(2.0f, view->GetTransform().matrix().get(0, 0)); +} + +// Verifies a transform persists after removing/adding a view with a transform. +TEST_F(ViewLayerTest, ResetTransformOnLayerAfterAdd) { + View* view = new View; + ui::Transform transform; + transform.SetScale(2.0f, 2.0f); + view->SetTransform(transform); + widget()->SetContentsView(view); + EXPECT_EQ(2.0f, view->GetTransform().matrix().get(0, 0)); + ASSERT_TRUE(view->layer() != NULL); + EXPECT_EQ(2.0f, view->layer()->transform().matrix().get(0, 0)); + + View* parent = view->parent(); + parent->RemoveChildView(view); + parent->AddChildView(view); + + EXPECT_EQ(2.0f, view->GetTransform().matrix().get(0, 0)); + ASSERT_TRUE(view->layer() != NULL); + EXPECT_EQ(2.0f, view->layer()->transform().matrix().get(0, 0)); +} + +// Makes sure that layer visibility is correct after toggling View visibility. +TEST_F(ViewLayerTest, ToggleVisibilityWithLayer) { + View* content_view = new View; + widget()->SetContentsView(content_view); + + // The view isn't attached to a widget or a parent view yet. But it should + // still have a layer, but the layer should not be attached to the root + // layer. + View* v1 = new View; + v1->SetPaintToLayer(true); + EXPECT_TRUE(v1->layer()); + EXPECT_FALSE(LayerIsAncestor(widget()->GetCompositor()->root_layer(), + v1->layer())); + + // Once the view is attached to a widget, its layer should be attached to the + // root layer and visible. + content_view->AddChildView(v1); + EXPECT_TRUE(LayerIsAncestor(widget()->GetCompositor()->root_layer(), + v1->layer())); + EXPECT_TRUE(v1->layer()->IsDrawn()); + + v1->SetVisible(false); + EXPECT_FALSE(v1->layer()->IsDrawn()); + + v1->SetVisible(true); + EXPECT_TRUE(v1->layer()->IsDrawn()); + + widget()->Hide(); + EXPECT_FALSE(v1->layer()->IsDrawn()); + + widget()->Show(); + EXPECT_TRUE(v1->layer()->IsDrawn()); +} + +// Test that a hole in a layer is correctly created regardless of whether +// the opacity attribute is set before or after the layer is created. +TEST_F(ViewLayerTest, ToggleOpacityWithLayer) { + View* content_view = new View; + widget()->SetContentsView(content_view); + + View* parent_view = new View; + content_view->AddChildView(parent_view); + parent_view->SetPaintToLayer(true); + parent_view->SetBounds(0, 0, 400, 400); + + View* child_view = new View; + child_view->SetBounds(50, 50, 100, 100); + parent_view->AddChildView(child_view); + + widget()->GetCompositor()->Draw(false); + + ASSERT_TRUE(child_view->layer() == NULL); + child_view->SetPaintToLayer(true); + child_view->SetFillsBoundsOpaquely(true); + widget()->GetCompositor()->Draw(false); + ASSERT_TRUE(child_view->layer()); + EXPECT_EQ( + gfx::Rect(50, 50, 100, 100), parent_view->layer()->hole_rect()); + + child_view->SetFillsBoundsOpaquely(false); + widget()->GetCompositor()->Draw(false); + EXPECT_TRUE(parent_view->layer()->hole_rect().IsEmpty()); +} + +// Test that a hole in a layer always corresponds to the bounds of opaque +// layers. +TEST_F(ViewLayerTest, MultipleOpaqueLayers) { + View* content_view = new View; + widget()->SetContentsView(content_view); + + View* parent_view = new View; + parent_view->SetPaintToLayer(true); + parent_view->SetBounds(0, 0, 400, 400); + content_view->AddChildView(parent_view); + + View* child_view1 = new View; + child_view1->SetPaintToLayer(true); + child_view1->SetFillsBoundsOpaquely(true); + child_view1->SetBounds(50, 50, 100, 100); + parent_view->AddChildView(child_view1); + + View* child_view2 = new View; + child_view2->SetPaintToLayer(true); + child_view2->SetFillsBoundsOpaquely(false); + child_view2->SetBounds(150, 150, 200, 200); + parent_view->AddChildView(child_view2); + + widget()->GetCompositor()->Draw(false); + + // Only child_view1 is opaque + EXPECT_EQ( + gfx::Rect(50, 50, 100, 100), parent_view->layer()->hole_rect()); + + // Both child views are opaque + child_view2->SetFillsBoundsOpaquely(true); + widget()->GetCompositor()->Draw(false); + EXPECT_TRUE( + gfx::Rect(50, 50, 100, 100) == parent_view->layer()->hole_rect() || + gfx::Rect(150, 150, 200, 200) == parent_view->layer()->hole_rect()); + + // Only child_view2 is opaque + delete child_view1; + EXPECT_EQ( + gfx::Rect(150, 150, 200, 200), parent_view->layer()->hole_rect()); +} + +// Makes sure that opacity of layer persists after toggling visibilty. +TEST_F(ViewLayerTest, ToggleVisibilityWithOpaqueLayer) { + View* content_view = new View; + widget()->SetContentsView(content_view); + + View* parent_view = new View; + parent_view->SetPaintToLayer(true); + parent_view->SetBounds(0, 0, 400, 400); + content_view->AddChildView(parent_view); + + parent_view->SetPaintToLayer(true); + parent_view->SetBounds(0, 0, 400, 400); + + View* child_view = new View; + child_view->SetBounds(50, 50, 100, 100); + child_view->SetPaintToLayer(true); + child_view->SetFillsBoundsOpaquely(true); + parent_view->AddChildView(child_view); + widget()->GetCompositor()->Draw(false); + EXPECT_EQ( + gfx::Rect(50, 50, 100, 100), parent_view->layer()->hole_rect()); + + child_view->SetVisible(false); + widget()->GetCompositor()->Draw(false); + EXPECT_TRUE(parent_view->layer()->hole_rect().IsEmpty()); + + child_view->SetVisible(true); + widget()->GetCompositor()->Draw(false); + EXPECT_EQ( + gfx::Rect(50, 50, 100, 100), parent_view->layer()->hole_rect()); +} + +// Tests that the layers in the subtree are orphaned after a View is removed +// from the parent. +TEST_F(ViewLayerTest, OrphanLayerAfterViewRemove) { + View* content_view = new View; + widget()->SetContentsView(content_view); + + View* v1 = new View; + content_view->AddChildView(v1); + + View* v2 = new View; + v1->AddChildView(v2); + v2->SetPaintToLayer(true); + EXPECT_TRUE(LayerIsAncestor(widget()->GetCompositor()->root_layer(), + v2->layer())); + EXPECT_TRUE(v2->layer()->IsDrawn()); + + content_view->RemoveChildView(v1); + EXPECT_FALSE(LayerIsAncestor(widget()->GetCompositor()->root_layer(), + v2->layer())); + + // Reparent |v2|. + content_view->AddChildView(v2); + EXPECT_TRUE(LayerIsAncestor(widget()->GetCompositor()->root_layer(), + v2->layer())); + EXPECT_TRUE(v2->layer()->IsDrawn()); +} + +class PaintTrackingView : public View { + public: + PaintTrackingView() : painted_(false) { + } + + bool painted() const { return painted_; } + void set_painted(bool value) { painted_ = value; } + + virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE { + painted_ = true; + } + + private: + bool painted_; + + DISALLOW_COPY_AND_ASSIGN(PaintTrackingView); +}; + +#if !defined(USE_WEBKIT_COMPOSITOR) +// TODO(piman): this test relies on the way the non-webkit compositor works. +// Layer::DrawTree should not be called with the webkit compositor. In the +// WebKit case, it needs to go through the "real" compositor (not the test one) +// to do the paints on the layer/views. + +// Makes sure child views with layers aren't painted when paint starts at an +// ancestor. +TEST_F(ViewLayerTest, DontPaintChildrenWithLayers) { + PaintTrackingView* content_view = new PaintTrackingView; + widget()->SetContentsView(content_view); + content_view->SetPaintToLayer(true); + GetRootLayer()->DrawTree(); + GetRootLayer()->SchedulePaint(gfx::Rect(0, 0, 10, 10)); + content_view->set_painted(false); + // content_view no longer has a dirty rect. Paint from the root and make sure + // PaintTrackingView isn't painted. + GetRootLayer()->DrawTree(); + EXPECT_FALSE(content_view->painted()); + + // Make content_view have a dirty rect, paint the layers and make sure + // PaintTrackingView is painted. + content_view->layer()->SchedulePaint(gfx::Rect(0, 0, 10, 10)); + GetRootLayer()->DrawTree(); + EXPECT_TRUE(content_view->painted()); +} +#endif + +// Tests that the visibility of child layers are updated correctly when a View's +// visibility changes. +TEST_F(ViewLayerTest, VisibilityChildLayers) { + View* v1 = new View; + v1->SetPaintToLayer(true); + widget()->SetContentsView(v1); + + View* v2 = new View; + v1->AddChildView(v2); + + View* v3 = new View; + v2->AddChildView(v3); + v3->SetVisible(false); + + View* v4 = new View; + v4->SetPaintToLayer(true); + v3->AddChildView(v4); + + EXPECT_TRUE(v1->layer()->IsDrawn()); + EXPECT_FALSE(v4->layer()->IsDrawn()); + + v2->SetVisible(false); + EXPECT_TRUE(v1->layer()->IsDrawn()); + EXPECT_FALSE(v4->layer()->IsDrawn()); + + v2->SetVisible(true); + EXPECT_TRUE(v1->layer()->IsDrawn()); + EXPECT_FALSE(v4->layer()->IsDrawn()); + + v2->SetVisible(false); + EXPECT_TRUE(v1->layer()->IsDrawn()); + EXPECT_FALSE(v4->layer()->IsDrawn()); + EXPECT_TRUE(ViewAndLayerTreeAreConsistent(v1, v1->layer())); + + v3->SetVisible(true); + EXPECT_TRUE(v1->layer()->IsDrawn()); + EXPECT_FALSE(v4->layer()->IsDrawn()); + EXPECT_TRUE(ViewAndLayerTreeAreConsistent(v1, v1->layer())); + + // Reparent |v3| to |v1|. + v1->AddChildView(v3); + EXPECT_TRUE(v1->layer()->IsDrawn()); + EXPECT_TRUE(v4->layer()->IsDrawn()); + EXPECT_TRUE(ViewAndLayerTreeAreConsistent(v1, v1->layer())); +} + +// This test creates a random View tree, and then randomly reorders child views, +// reparents views etc. Unrelated changes can appear to break this test. So +// marking this as FLAKY. +TEST_F(ViewLayerTest, FLAKY_ViewLayerTreesInSync) { + View* content = new View; + content->SetPaintToLayer(true); + widget()->SetContentsView(content); + widget()->Show(); + + ConstructTree(content, 5); + EXPECT_TRUE(ViewAndLayerTreeAreConsistent(content, content->layer())); + + ScrambleTree(content); + EXPECT_TRUE(ViewAndLayerTreeAreConsistent(content, content->layer())); + + ScrambleTree(content); + EXPECT_TRUE(ViewAndLayerTreeAreConsistent(content, content->layer())); + + ScrambleTree(content); + EXPECT_TRUE(ViewAndLayerTreeAreConsistent(content, content->layer())); +} + +#endif // VIEWS_COMPOSITOR + +} // namespace views diff --git a/ui/views/view_win.cc b/ui/views/view_win.cc new file mode 100644 index 0000000..472fbcd --- /dev/null +++ b/ui/views/view_win.cc @@ -0,0 +1,36 @@ +// 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/view.h" + +// Necessary to define oleacc GUID's. +#include <windows.h> +#include <initguid.h> +#include <oleacc.h> + +#include "ui/views/accessibility/native_view_accessibility_win.h" + +namespace views { + +gfx::NativeViewAccessible View::GetNativeViewAccessible() { + if (!native_view_accessibility_win_.get()) + native_view_accessibility_win_ = NativeViewAccessibilityWin::Create(this); + return native_view_accessibility_win_.get(); +} + +int View::GetHorizontalDragThreshold() { + static int threshold = -1; + if (threshold == -1) + threshold = GetSystemMetrics(SM_CXDRAG) / 2; + return threshold; +} + +int View::GetVerticalDragThreshold() { + static int threshold = -1; + if (threshold == -1) + threshold = GetSystemMetrics(SM_CYDRAG) / 2; + return threshold; +} + +} // namespace views diff --git a/ui/views/widget/drop_helper.cc b/ui/views/widget/drop_helper.cc index a351090..5b70260 100644 --- a/ui/views/widget/drop_helper.cc +++ b/ui/views/widget/drop_helper.cc @@ -5,8 +5,8 @@ #include "ui/views/widget/drop_helper.h" #include "ui/base/dragdrop/drag_drop_types.h" +#include "ui/views/view.h" #include "ui/views/widget/widget.h" -#include "views/view.h" namespace views { diff --git a/ui/views/widget/native_widget_test_utils_aura.cc b/ui/views/widget/native_widget_test_utils_aura.cc index 4b0ecec..af7cf59 100644 --- a/ui/views/widget/native_widget_test_utils_aura.cc +++ b/ui/views/widget/native_widget_test_utils_aura.cc @@ -4,9 +4,9 @@ #include "ui/views/widget/native_widget_test_utils.h" +#include "ui/views/view.h" #include "ui/views/widget/native_widget_private.h" #include "ui/views/widget/widget.h" -#include "views/view.h" namespace views { namespace internal { diff --git a/ui/views/widget/native_widget_test_utils_gtk.cc b/ui/views/widget/native_widget_test_utils_gtk.cc index 60c2050..3f3fc76 100644 --- a/ui/views/widget/native_widget_test_utils_gtk.cc +++ b/ui/views/widget/native_widget_test_utils_gtk.cc @@ -4,9 +4,9 @@ #include "ui/views/widget/native_widget_test_utils.h" +#include "ui/views/view.h" #include "ui/views/widget/native_widget_private.h" #include "ui/views/widget/widget.h" -#include "views/view.h" namespace views { namespace internal { diff --git a/ui/views/widget/native_widget_test_utils_win.cc b/ui/views/widget/native_widget_test_utils_win.cc index 4b0ecec..af7cf59 100644 --- a/ui/views/widget/native_widget_test_utils_win.cc +++ b/ui/views/widget/native_widget_test_utils_win.cc @@ -4,9 +4,9 @@ #include "ui/views/widget/native_widget_test_utils.h" +#include "ui/views/view.h" #include "ui/views/widget/native_widget_private.h" #include "ui/views/widget/widget.h" -#include "views/view.h" namespace views { namespace internal { diff --git a/ui/views/widget/native_widget_unittest.cc b/ui/views/widget/native_widget_unittest.cc index 7e9b4de..808649a 100644 --- a/ui/views/widget/native_widget_unittest.cc +++ b/ui/views/widget/native_widget_unittest.cc @@ -5,10 +5,10 @@ #include "testing/gtest/include/gtest/gtest.h" #include "ui/views/controls/native/native_view_host.h" #include "ui/views/test/views_test_base.h" +#include "ui/views/view.h" #include "ui/views/widget/native_widget_private.h" #include "ui/views/widget/native_widget_test_utils.h" #include "ui/views/widget/widget.h" -#include "views/view.h" namespace views { diff --git a/ui/views/widget/root_view.h b/ui/views/widget/root_view.h index d5df829..0158f7f 100644 --- a/ui/views/widget/root_view.h +++ b/ui/views/widget/root_view.h @@ -11,7 +11,7 @@ #include "base/memory/ref_counted.h" #include "ui/views/focus/focus_manager.h" #include "ui/views/focus/focus_search.h" -#include "views/view.h" +#include "ui/views/view.h" namespace ui { enum TouchStatus; diff --git a/ui/views/widget/tooltip_manager_gtk.cc b/ui/views/widget/tooltip_manager_gtk.cc index b33e5ad..8114128 100644 --- a/ui/views/widget/tooltip_manager_gtk.cc +++ b/ui/views/widget/tooltip_manager_gtk.cc @@ -9,8 +9,8 @@ #include "ui/gfx/font.h" #include "ui/gfx/screen.h" #include "ui/views/focus/focus_manager.h" +#include "ui/views/view.h" #include "ui/views/widget/native_widget_gtk.h" -#include "views/view.h" // WARNING: this implementation is good for a start, but it doesn't give us // control of tooltip positioning both on mouse events and when showing from diff --git a/ui/views/widget/tooltip_manager_views.cc b/ui/views/widget/tooltip_manager_views.cc index fa3cb63..264dbc3 100644 --- a/ui/views/widget/tooltip_manager_views.cc +++ b/ui/views/widget/tooltip_manager_views.cc @@ -28,10 +28,10 @@ #include "ui/gfx/screen.h" #include "ui/views/events/event.h" #include "ui/views/focus/focus_manager.h" +#include "ui/views/view.h" #include "ui/views/widget/native_widget.h" #include "views/background.h" #include "views/border.h" -#include "views/view.h" namespace { SkColor kTooltipBackground = 0xFF7F7F00; diff --git a/ui/views/widget/tooltip_manager_views.h b/ui/views/widget/tooltip_manager_views.h index e844ddf..1a6ef18 100644 --- a/ui/views/widget/tooltip_manager_views.h +++ b/ui/views/widget/tooltip_manager_views.h @@ -9,10 +9,10 @@ #include "base/message_loop.h" #include "base/timer.h" #include "ui/views/controls/label.h" +#include "ui/views/view.h" #include "ui/views/widget/native_widget.h" #include "ui/views/widget/tooltip_manager.h" #include "ui/views/widget/widget_delegate.h" -#include "views/view.h" #if defined(USE_X11) typedef union _XEvent XEvent; diff --git a/ui/views/widget/tooltip_manager_win.cc b/ui/views/widget/tooltip_manager_win.cc index fedb700..f03d858 100644 --- a/ui/views/widget/tooltip_manager_win.cc +++ b/ui/views/widget/tooltip_manager_win.cc @@ -17,9 +17,9 @@ #include "ui/base/win/hwnd_util.h" #include "ui/gfx/font.h" #include "ui/gfx/screen.h" +#include "ui/views/view.h" #include "ui/views/widget/monitor_win.h" #include "ui/views/widget/widget.h" -#include "views/view.h" namespace views { diff --git a/ui/views/widget/widget_delegate.cc b/ui/views/widget/widget_delegate.cc index 94ebcf1..84af2343 100644 --- a/ui/views/widget/widget_delegate.cc +++ b/ui/views/widget/widget_delegate.cc @@ -7,9 +7,9 @@ #include "base/utf_string_conversions.h" #include "third_party/skia/include/core/SkBitmap.h" #include "ui/views/bubble/bubble_delegate.h" +#include "ui/views/view.h" #include "ui/views/widget/widget.h" #include "ui/views/window/client_view.h" -#include "views/view.h" #include "views/views_delegate.h" namespace views { diff --git a/ui/views/widget/widget_delegate.h b/ui/views/widget/widget_delegate.h index b771d33..58a2c72 100644 --- a/ui/views/widget/widget_delegate.h +++ b/ui/views/widget/widget_delegate.h @@ -11,7 +11,7 @@ #include "base/memory/scoped_ptr.h" #include "ui/base/accessibility/accessibility_types.h" #include "ui/base/ui_base_types.h" -#include "views/view.h" +#include "ui/views/view.h" class SkBitmap; diff --git a/ui/views/window/client_view.h b/ui/views/window/client_view.h index 4b809f2..46da6e2 100644 --- a/ui/views/window/client_view.h +++ b/ui/views/window/client_view.h @@ -6,7 +6,7 @@ #define UI_VIEWS_WINDOW_CLIENT_VIEW_H_ #pragma once -#include "views/view.h" +#include "ui/views/view.h" namespace views { diff --git a/ui/views/window/non_client_view.h b/ui/views/window/non_client_view.h index fc97d69..eaea2bc 100644 --- a/ui/views/window/non_client_view.h +++ b/ui/views/window/non_client_view.h @@ -6,8 +6,8 @@ #define UI_VIEWS_WINDOW_NON_CLIENT_VIEW_H_ #pragma once +#include "ui/views/view.h" #include "ui/views/window/client_view.h" -#include "views/view.h" namespace gfx { class Path; |